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 * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
23 * Copyright 2019 Joyent, Inc.
24 */
25
26 /*
27 * Topology Plugin Modules
28 *
29 * Topology plugin modules are shared libraries that are dlopen'd and
30 * used to enumerate resources in the system and export per-node method
31 * operations.
32 *
33 * They are loaded by our builtin scheme-specific plugins, other modules or
34 * by processing a topo map XML file to enumerate and create nodes for
35 * resources that are present in the system. They may also export a set of
36 * topology node specific methods that can be invoked directly via
37 * topo_method_invoke() or indirectly via the
38 * topo_prop_get* family of functions to access dynamic property data.
39 *
40 * Module Plugin API
41 *
42 * Enumerators must provide entry points for initialization and clean-up
43 * (_topo_init() and _topo_fini()). In their _topo_init() function, an
44 * enumerator should register (topo_mod_register()) its enumeration callback
45 * and allocate resources required for a subsequent call to the callback.
46 * Optionally, methods may also be registered with topo_method_register().
47 *
48 * In its enumeration callback routine, the module should search for resources
49 * within its realm of responsibility and create any node ranges,
50 * topo_node_range_create() and nodes, topo_node_bind(). The Enumerator
51 * module is handed a node to which it may begin attaching additional
52 * topology nodes. The enumerator may only access those nodes within its
53 * current scope of operation: the node passed into its enumeration op and
54 * any nodes it creates during enumeration. If the enumerator requires walker-
55 * style access to these nodes, it must use
56 * topo_mod_walk_init()/topo_walk_step()/topo_walk_fini().
57 *
58 * If additional helper modules need to be loaded to complete the enumeration
59 * the module may do so by calling topo_mod_load(). Enumeration may then
60 * continue with the module handing off enumeration to its helper module
61 * by calling topo_mod_enumerate(). Similarly, a module may call
62 * topo_mod_enummap() to kick-off enumeration according to a given XML
63 * topology map file. A module *may* not cause re-entrance to itself
64 * via either of these interfaces. If re-entry is detected an error
65 * will be returned (ETOPO_ENUM_RECURS).
66 *
67 * If the module registers a release callback, it will be called on a node
68 * by node basis during topo_snap_rele(). Any private node data may be
69 * deallocated or methods unregistered at that time. Global module data
70 * should be cleaned up before or at the time that the module _topo_fini
71 * entry point is called.
72 *
73 * Module entry points and method invocations are guaranteed to be
74 * single-threaded for a given snapshot handle. Applications may have
75 * more than one topology snapshot open at a time. This means that the
76 * module operations and methods may be called for different module handles
77 * (topo_mod_t) asynchronously. The enumerator should not use static or
78 * global data structures that may become inconsistent in this situation.
79 * Method operations may be re-entrant if the module invokes one of its own
80 * methods directly or via dynamic property access. Caution should be
81 * exercised with method operations to insure that data remains consistent
82 * within the module and that deadlocks can not occur.
83 */
84
85 #include <pthread.h>
86 #include <assert.h>
87 #include <errno.h>
88 #include <dirent.h>
89 #include <limits.h>
90 #include <alloca.h>
91 #include <unistd.h>
92 #include <stdio.h>
93 #include <ctype.h>
94 #include <pcidb.h>
95 #include <sys/param.h>
96 #include <sys/utsname.h>
97 #include <sys/smbios.h>
98 #include <sys/fm/protocol.h>
99 #include <sys/types.h>
100 #include <sys/stat.h>
101 #include <fcntl.h>
102
103 #include <topo_alloc.h>
104 #include <topo_error.h>
105 #include <topo_file.h>
106 #include <topo_fmri.h>
107 #include <topo_module.h>
108 #include <topo_method.h>
109 #include <topo_string.h>
110 #include <topo_subr.h>
111 #include <topo_tree.h>
112
113 #define PLUGIN_PATH "plugins"
114 #define PLUGIN_PATH_LEN MAXNAMELEN + 5
115
116 topo_mod_t *
topo_mod_load(topo_mod_t * pmod,const char * name,topo_version_t version)117 topo_mod_load(topo_mod_t *pmod, const char *name,
118 topo_version_t version)
119 {
120 char *path;
121 char file[PLUGIN_PATH_LEN];
122 topo_mod_t *mod = NULL;
123 topo_hdl_t *thp;
124
125 thp = pmod->tm_hdl;
126
127 /*
128 * Already loaded, topo_mod_lookup will bump the ref count
129 */
130 if ((mod = topo_mod_lookup(thp, name, 1)) != NULL) {
131 if (mod->tm_info->tmi_version != version) {
132 topo_mod_rele(mod);
133 (void) topo_mod_seterrno(pmod, ETOPO_MOD_VER);
134 return (NULL);
135 }
136 return (mod);
137 }
138
139 (void) snprintf(file, PLUGIN_PATH_LEN, "%s/%s.so",
140 PLUGIN_PATH, name);
141 path = topo_search_path(pmod, thp->th_rootdir, (const char *)file);
142 if (path == NULL ||
143 (mod = topo_modhash_load(thp, name, path, &topo_rtld_ops, version))
144 == NULL) { /* returned with mod held */
145 topo_mod_strfree(pmod, path);
146 (void) topo_mod_seterrno(pmod, topo_hdl_errno(thp) ?
147 topo_hdl_errno(thp) : ETOPO_MOD_NOENT);
148 return (NULL);
149 }
150
151 topo_mod_strfree(pmod, path);
152
153 return (mod);
154 }
155
156 void
topo_mod_unload(topo_mod_t * mod)157 topo_mod_unload(topo_mod_t *mod)
158 {
159 topo_mod_rele(mod);
160 }
161
162 static int
set_register_error(topo_mod_t * mod,int err)163 set_register_error(topo_mod_t *mod, int err)
164 {
165 if (mod->tm_info != NULL)
166 topo_mod_unregister(mod);
167
168 topo_dprintf(mod->tm_hdl, TOPO_DBG_ERR,
169 "module registration failed for %s: %s\n",
170 mod->tm_name, topo_strerror(err));
171
172 return (topo_mod_seterrno(mod, err));
173 }
174
175 int
topo_mod_register(topo_mod_t * mod,const topo_modinfo_t * mip,topo_version_t version)176 topo_mod_register(topo_mod_t *mod, const topo_modinfo_t *mip,
177 topo_version_t version)
178 {
179
180 assert(!(mod->tm_flags & TOPO_MOD_FINI ||
181 mod->tm_flags & TOPO_MOD_REG));
182
183 if (version != TOPO_VERSION)
184 return (set_register_error(mod, EMOD_VER_ABI));
185
186 if ((mod->tm_info = topo_mod_zalloc(mod, sizeof (topo_imodinfo_t)))
187 == NULL)
188 return (set_register_error(mod, EMOD_NOMEM));
189 if ((mod->tm_info->tmi_ops = topo_mod_alloc(mod,
190 sizeof (topo_modops_t))) == NULL)
191 return (set_register_error(mod, EMOD_NOMEM));
192
193 mod->tm_info->tmi_desc = topo_mod_strdup(mod, mip->tmi_desc);
194 if (mod->tm_info->tmi_desc == NULL)
195 return (set_register_error(mod, EMOD_NOMEM));
196
197 mod->tm_info->tmi_scheme = topo_mod_strdup(mod, mip->tmi_scheme);
198 if (mod->tm_info->tmi_scheme == NULL)
199 return (set_register_error(mod, EMOD_NOMEM));
200
201
202 mod->tm_info->tmi_version = (topo_version_t)mip->tmi_version;
203 mod->tm_info->tmi_ops->tmo_enum = mip->tmi_ops->tmo_enum;
204 mod->tm_info->tmi_ops->tmo_release = mip->tmi_ops->tmo_release;
205
206 mod->tm_flags |= TOPO_MOD_REG;
207
208 topo_dprintf(mod->tm_hdl, TOPO_DBG_MODSVC,
209 "registration succeeded for %s\n", mod->tm_name);
210
211 return (0);
212 }
213
214 void
topo_mod_unregister(topo_mod_t * mod)215 topo_mod_unregister(topo_mod_t *mod)
216 {
217 if (mod->tm_info == NULL)
218 return;
219
220 assert(!(mod->tm_flags & TOPO_MOD_FINI));
221
222 mod->tm_flags &= ~TOPO_MOD_REG;
223
224 if (mod->tm_info == NULL)
225 return;
226
227 if (mod->tm_info->tmi_ops != NULL)
228 topo_mod_free(mod, mod->tm_info->tmi_ops,
229 sizeof (topo_modops_t));
230 if (mod->tm_info->tmi_desc != NULL)
231 topo_mod_strfree(mod, mod->tm_info->tmi_desc);
232 if (mod->tm_info->tmi_scheme != NULL)
233 topo_mod_strfree(mod, mod->tm_info->tmi_scheme);
234
235 topo_mod_free(mod, mod->tm_info, sizeof (topo_imodinfo_t));
236
237 mod->tm_info = NULL;
238 }
239
240 int
topo_mod_enumerate(topo_mod_t * mod,tnode_t * node,const char * enum_name,const char * name,topo_instance_t min,topo_instance_t max,void * data)241 topo_mod_enumerate(topo_mod_t *mod, tnode_t *node, const char *enum_name,
242 const char *name, topo_instance_t min, topo_instance_t max, void *data)
243 {
244 int err = 0;
245 topo_mod_t *enum_mod;
246
247 assert(mod->tm_flags & TOPO_MOD_REG);
248
249 if ((enum_mod = topo_mod_lookup(mod->tm_hdl, enum_name, 0)) == NULL)
250 return (topo_mod_seterrno(mod, EMOD_MOD_NOENT));
251
252 topo_node_hold(node);
253
254 topo_dprintf(mod->tm_hdl, TOPO_DBG_MODSVC, "module %s enumerating "
255 "node %s=%d\n", (char *)mod->tm_name, (char *)node->tn_name,
256 node->tn_instance);
257
258 topo_mod_enter(enum_mod);
259 err = enum_mod->tm_info->tmi_ops->tmo_enum(enum_mod, node, name, min,
260 max, enum_mod->tm_priv, data);
261 topo_mod_exit(enum_mod);
262
263 if (err != 0) {
264 (void) topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM);
265
266 topo_dprintf(mod->tm_hdl, TOPO_DBG_ERR,
267 "module %s failed enumeration for "
268 " node %s=%d\n", (char *)mod->tm_name,
269 (char *)node->tn_name, node->tn_instance);
270
271 topo_node_rele(node);
272 return (-1);
273 }
274
275 topo_node_rele(node);
276
277 return (0);
278 }
279
280 int
topo_mod_enummap(topo_mod_t * mod,tnode_t * node,const char * name,const char * scheme)281 topo_mod_enummap(topo_mod_t *mod, tnode_t *node, const char *name,
282 const char *scheme)
283 {
284 return (topo_file_load(mod, node, (char *)name, (char *)scheme, 0));
285 }
286
287 static nvlist_t *
set_fmri_err(topo_mod_t * mod,int err)288 set_fmri_err(topo_mod_t *mod, int err)
289 {
290 (void) topo_mod_seterrno(mod, err);
291 return (NULL);
292 }
293
294 nvlist_t *
topo_mod_hcfmri(topo_mod_t * mod,tnode_t * pnode,int version,const char * name,topo_instance_t inst,nvlist_t * hc_specific,nvlist_t * auth,const char * part,const char * rev,const char * serial)295 topo_mod_hcfmri(topo_mod_t *mod, tnode_t *pnode, int version, const char *name,
296 topo_instance_t inst, nvlist_t *hc_specific, nvlist_t *auth,
297 const char *part, const char *rev, const char *serial)
298 {
299 int err;
300 nvlist_t *pfmri = NULL, *fmri = NULL, *args = NULL;
301 nvlist_t *nfp = NULL;
302 char *lpart, *lrev, *lserial;
303
304 if (version != FM_HC_SCHEME_VERSION)
305 return (set_fmri_err(mod, EMOD_FMRI_VERSION));
306
307 /*
308 * Do we have any args to pass?
309 */
310 if (pnode != NULL || auth != NULL || part != NULL || rev != NULL ||
311 serial != NULL || hc_specific != NULL) {
312 if (topo_mod_nvalloc(mod, &args, NV_UNIQUE_NAME) != 0)
313 return (set_fmri_err(mod, EMOD_FMRI_NVL));
314 }
315
316 if (pnode != NULL) {
317 if (topo_node_resource(pnode, &pfmri, &err) < 0) {
318 nvlist_free(args);
319 return (set_fmri_err(mod, EMOD_NVL_INVAL));
320 }
321
322 if (nvlist_add_nvlist(args, TOPO_METH_FMRI_ARG_PARENT,
323 pfmri) != 0) {
324 nvlist_free(pfmri);
325 nvlist_free(args);
326 return (set_fmri_err(mod, EMOD_FMRI_NVL));
327 }
328 nvlist_free(pfmri);
329 }
330
331 /*
332 * Add optional payload
333 */
334 if (auth != NULL)
335 (void) nvlist_add_nvlist(args, TOPO_METH_FMRI_ARG_AUTH, auth);
336 if (part != NULL) {
337 lpart = topo_cleanup_auth_str(mod->tm_hdl, part);
338 if (lpart != NULL) {
339 (void) nvlist_add_string(args, TOPO_METH_FMRI_ARG_PART,
340 lpart);
341 topo_hdl_free(mod->tm_hdl, lpart, strlen(lpart) + 1);
342 } else {
343 (void) nvlist_add_string(args, TOPO_METH_FMRI_ARG_PART,
344 "");
345 }
346 }
347 if (rev != NULL) {
348 lrev = topo_cleanup_auth_str(mod->tm_hdl, rev);
349 if (lrev != NULL) {
350 (void) nvlist_add_string(args, TOPO_METH_FMRI_ARG_REV,
351 lrev);
352 topo_hdl_free(mod->tm_hdl, lrev, strlen(lrev) + 1);
353 } else {
354 (void) nvlist_add_string(args, TOPO_METH_FMRI_ARG_REV,
355 "");
356 }
357 }
358 if (serial != NULL) {
359 lserial = topo_cleanup_auth_str(mod->tm_hdl, serial);
360 if (lserial != NULL) {
361 (void) nvlist_add_string(args, TOPO_METH_FMRI_ARG_SER,
362 lserial);
363 topo_hdl_free(mod->tm_hdl, lserial,
364 strlen(lserial) + 1);
365 } else {
366 (void) nvlist_add_string(args, TOPO_METH_FMRI_ARG_SER,
367 "");
368 }
369 }
370 if (hc_specific != NULL)
371 (void) nvlist_add_nvlist(args, TOPO_METH_FMRI_ARG_HCS,
372 hc_specific);
373
374 if ((fmri = topo_fmri_create(mod->tm_hdl, FM_FMRI_SCHEME_HC, name, inst,
375 args, &err)) == NULL) {
376 nvlist_free(args);
377 return (set_fmri_err(mod, err));
378 }
379
380 nvlist_free(args);
381
382 (void) topo_mod_nvdup(mod, fmri, &nfp);
383 nvlist_free(fmri);
384
385 return (nfp);
386 }
387
388 nvlist_t *
topo_mod_devfmri(topo_mod_t * mod,int version,const char * dev_path,const char * devid)389 topo_mod_devfmri(topo_mod_t *mod, int version, const char *dev_path,
390 const char *devid)
391 {
392 int err;
393 nvlist_t *fmri, *args;
394 nvlist_t *nfp = NULL;
395
396 if (version != FM_DEV_SCHEME_VERSION)
397 return (set_fmri_err(mod, EMOD_FMRI_VERSION));
398
399 if (topo_mod_nvalloc(mod, &args, NV_UNIQUE_NAME) != 0)
400 return (set_fmri_err(mod, EMOD_FMRI_NVL));
401
402 if (nvlist_add_string(args, FM_FMRI_DEV_PATH, dev_path) != 0) {
403 nvlist_free(args);
404 return (set_fmri_err(mod, EMOD_FMRI_NVL));
405 }
406
407 (void) nvlist_add_string(args, FM_FMRI_DEV_ID, devid);
408
409 if ((fmri = topo_fmri_create(mod->tm_hdl, FM_FMRI_SCHEME_DEV,
410 FM_FMRI_SCHEME_DEV, 0, args, &err)) == NULL) {
411 nvlist_free(args);
412 return (set_fmri_err(mod, err));
413 }
414
415 nvlist_free(args);
416
417 (void) topo_mod_nvdup(mod, fmri, &nfp);
418 nvlist_free(fmri);
419
420 return (nfp);
421 }
422
423 nvlist_t *
topo_mod_cpufmri(topo_mod_t * mod,int version,uint32_t cpu_id,uint8_t cpumask,const char * serial)424 topo_mod_cpufmri(topo_mod_t *mod, int version, uint32_t cpu_id, uint8_t cpumask,
425 const char *serial)
426 {
427 int err;
428 nvlist_t *fmri = NULL, *args = NULL;
429 nvlist_t *nfp = NULL;
430
431 if (version != FM_CPU_SCHEME_VERSION)
432 return (set_fmri_err(mod, EMOD_FMRI_VERSION));
433
434 if (topo_mod_nvalloc(mod, &args, NV_UNIQUE_NAME) != 0)
435 return (set_fmri_err(mod, EMOD_FMRI_NVL));
436
437 if (nvlist_add_uint32(args, FM_FMRI_CPU_ID, cpu_id) != 0) {
438 nvlist_free(args);
439 return (set_fmri_err(mod, EMOD_FMRI_NVL));
440 }
441
442 /*
443 * Add optional payload
444 */
445 (void) nvlist_add_uint8(args, FM_FMRI_CPU_MASK, cpumask);
446 (void) nvlist_add_string(args, FM_FMRI_CPU_SERIAL_ID, serial);
447
448 if ((fmri = topo_fmri_create(mod->tm_hdl, FM_FMRI_SCHEME_CPU,
449 FM_FMRI_SCHEME_CPU, 0, args, &err)) == NULL) {
450 nvlist_free(args);
451 return (set_fmri_err(mod, err));
452 }
453
454 nvlist_free(args);
455
456 (void) topo_mod_nvdup(mod, fmri, &nfp);
457 nvlist_free(fmri);
458
459 return (nfp);
460 }
461
462 nvlist_t *
topo_mod_memfmri(topo_mod_t * mod,int version,uint64_t pa,uint64_t offset,const char * unum,int flags)463 topo_mod_memfmri(topo_mod_t *mod, int version, uint64_t pa, uint64_t offset,
464 const char *unum, int flags)
465 {
466 int err;
467 nvlist_t *args = NULL, *fmri = NULL;
468 nvlist_t *nfp = NULL;
469
470 if (version != FM_MEM_SCHEME_VERSION)
471 return (set_fmri_err(mod, EMOD_FMRI_VERSION));
472
473 if (topo_mod_nvalloc(mod, &args, NV_UNIQUE_NAME) != 0)
474 return (set_fmri_err(mod, EMOD_FMRI_NVL));
475
476 err = nvlist_add_string(args, FM_FMRI_MEM_UNUM, unum);
477 if (flags & TOPO_MEMFMRI_PA)
478 err |= nvlist_add_uint64(args, FM_FMRI_MEM_PHYSADDR, pa);
479 if (flags & TOPO_MEMFMRI_OFFSET)
480 err |= nvlist_add_uint64(args, FM_FMRI_MEM_OFFSET, offset);
481
482 if (err != 0) {
483 nvlist_free(args);
484 return (set_fmri_err(mod, EMOD_FMRI_NVL));
485 }
486
487 if ((fmri = topo_fmri_create(mod->tm_hdl, FM_FMRI_SCHEME_MEM,
488 FM_FMRI_SCHEME_MEM, 0, args, &err)) == NULL) {
489 nvlist_free(args);
490 return (set_fmri_err(mod, err));
491 }
492
493 nvlist_free(args);
494
495 (void) topo_mod_nvdup(mod, fmri, &nfp);
496 nvlist_free(fmri);
497
498 return (nfp);
499
500 }
501
502 nvlist_t *
topo_mod_pkgfmri(topo_mod_t * mod,int version,const char * path)503 topo_mod_pkgfmri(topo_mod_t *mod, int version, const char *path)
504 {
505 int err;
506 nvlist_t *fmri = NULL, *args = NULL;
507 nvlist_t *nfp = NULL;
508
509 if (version != FM_PKG_SCHEME_VERSION)
510 return (set_fmri_err(mod, EMOD_FMRI_VERSION));
511
512 if (topo_mod_nvalloc(mod, &args, NV_UNIQUE_NAME) != 0)
513 return (set_fmri_err(mod, EMOD_FMRI_NVL));
514
515 if (nvlist_add_string(args, "path", path) != 0) {
516 nvlist_free(args);
517 return (set_fmri_err(mod, EMOD_FMRI_NVL));
518 }
519
520 if ((fmri = topo_fmri_create(mod->tm_hdl, FM_FMRI_SCHEME_PKG,
521 FM_FMRI_SCHEME_PKG, 0, args, &err)) == NULL) {
522 nvlist_free(args);
523 return (set_fmri_err(mod, err));
524 }
525
526 nvlist_free(args);
527
528 (void) topo_mod_nvdup(mod, fmri, &nfp);
529 nvlist_free(fmri);
530
531 return (nfp);
532 }
533
534 nvlist_t *
topo_mod_modfmri(topo_mod_t * mod,int version,const char * driver)535 topo_mod_modfmri(topo_mod_t *mod, int version, const char *driver)
536 {
537 int err;
538 nvlist_t *fmri = NULL, *args = NULL;
539 nvlist_t *nfp = NULL;
540
541 if (version != FM_MOD_SCHEME_VERSION)
542 return (set_fmri_err(mod, EMOD_FMRI_VERSION));
543
544 if (topo_mod_nvalloc(mod, &args, NV_UNIQUE_NAME) != 0)
545 return (set_fmri_err(mod, EMOD_FMRI_NVL));
546
547 if (nvlist_add_string(args, "DRIVER", driver) != 0) {
548 nvlist_free(args);
549 return (set_fmri_err(mod, EMOD_FMRI_NVL));
550 }
551
552 if ((fmri = topo_fmri_create(mod->tm_hdl, FM_FMRI_SCHEME_MOD,
553 FM_FMRI_SCHEME_MOD, 0, args, &err)) == NULL) {
554 nvlist_free(args);
555 return (set_fmri_err(mod, err));
556 }
557
558 nvlist_free(args);
559
560 (void) topo_mod_nvdup(mod, fmri, &nfp);
561 nvlist_free(fmri);
562
563 return (nfp);
564 }
565
566 #define _SWFMRI_ADD_STRING(nvl, name, val) \
567 ((val) ? (nvlist_add_string(nvl, name, val) != 0) : 0)
568
569 nvlist_t *
topo_mod_swfmri(topo_mod_t * mod,int version,char * obj_path,char * obj_root,nvlist_t * obj_pkg,char * site_token,char * site_module,char * site_file,char * site_func,int64_t site_line,char * ctxt_origin,char * ctxt_execname,int64_t ctxt_pid,char * ctxt_zone,int64_t ctxt_ctid,char ** ctxt_stack,uint_t ctxt_stackdepth)570 topo_mod_swfmri(topo_mod_t *mod, int version,
571 char *obj_path, char *obj_root, nvlist_t *obj_pkg,
572 char *site_token, char *site_module, char *site_file, char *site_func,
573 int64_t site_line, char *ctxt_origin, char *ctxt_execname,
574 int64_t ctxt_pid, char *ctxt_zone, int64_t ctxt_ctid,
575 char **ctxt_stack, uint_t ctxt_stackdepth)
576 {
577 nvlist_t *fmri, *args;
578 nvlist_t *nfp = NULL;
579 int err;
580
581 if (version != FM_SW_SCHEME_VERSION)
582 return (set_fmri_err(mod, EMOD_FMRI_VERSION));
583
584 if (topo_mod_nvalloc(mod, &args, NV_UNIQUE_NAME) != 0)
585 return (set_fmri_err(mod, EMOD_FMRI_NVL));
586
587 err = 0;
588 err |= _SWFMRI_ADD_STRING(args, "obj_path", obj_path);
589 err |= _SWFMRI_ADD_STRING(args, "obj_root", obj_root);
590 if (obj_pkg)
591 err |= nvlist_add_nvlist(args, "obj_pkg", obj_pkg);
592
593 err |= _SWFMRI_ADD_STRING(args, "site_token", site_token);
594 err |= _SWFMRI_ADD_STRING(args, "site_module", site_module);
595 err |= _SWFMRI_ADD_STRING(args, "site_file", site_file);
596 err |= _SWFMRI_ADD_STRING(args, "site_func", site_func);
597 if (site_line != -1)
598 err |= nvlist_add_int64(args, "site_line", site_line);
599
600 err |= _SWFMRI_ADD_STRING(args, "ctxt_origin", ctxt_origin);
601 err |= _SWFMRI_ADD_STRING(args, "ctxt_execname", ctxt_execname);
602 if (ctxt_pid != -1)
603 err |= nvlist_add_int64(args, "ctxt_pid", ctxt_pid);
604 err |= _SWFMRI_ADD_STRING(args, "ctxt_zone", ctxt_zone);
605 if (ctxt_ctid != -1)
606 err |= nvlist_add_int64(args, "ctxt_ctid", ctxt_ctid);
607 if (ctxt_stack != NULL && ctxt_stackdepth != 0)
608 err |= nvlist_add_string_array(args, "stack", ctxt_stack,
609 ctxt_stackdepth);
610
611 if (err) {
612 nvlist_free(args);
613 return (set_fmri_err(mod, EMOD_FMRI_NVL));
614 }
615
616 if ((fmri = topo_fmri_create(mod->tm_hdl, FM_FMRI_SCHEME_SW,
617 FM_FMRI_SCHEME_SW, 0, args, &err)) == NULL) {
618 nvlist_free(args);
619 return (set_fmri_err(mod, err));
620 }
621
622 nvlist_free(args);
623
624 (void) topo_mod_nvdup(mod, fmri, &nfp);
625 nvlist_free(fmri);
626
627 return (nfp);
628 }
629
630 int
topo_mod_str2nvl(topo_mod_t * mod,const char * fmristr,nvlist_t ** fmri)631 topo_mod_str2nvl(topo_mod_t *mod, const char *fmristr, nvlist_t **fmri)
632 {
633 int err;
634 nvlist_t *np = NULL;
635
636 if (topo_fmri_str2nvl(mod->tm_hdl, fmristr, &np, &err) < 0)
637 return (topo_mod_seterrno(mod, err));
638
639 if (topo_mod_nvdup(mod, np, fmri) < 0) {
640 nvlist_free(np);
641 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
642 }
643
644 nvlist_free(np);
645
646 return (0);
647 }
648
649 int
topo_mod_nvl2str(topo_mod_t * mod,nvlist_t * fmri,char ** fmristr)650 topo_mod_nvl2str(topo_mod_t *mod, nvlist_t *fmri, char **fmristr)
651 {
652 int err;
653 char *sp;
654
655 if (topo_fmri_nvl2str(mod->tm_hdl, fmri, &sp, &err) < 0)
656 return (topo_mod_seterrno(mod, err));
657
658 if ((*fmristr = topo_mod_strdup(mod, sp)) == NULL) {
659 topo_hdl_strfree(mod->tm_hdl, sp);
660 return (topo_mod_seterrno(mod, EMOD_NOMEM));
661 }
662
663 topo_hdl_strfree(mod->tm_hdl, sp);
664
665 return (0);
666 }
667
668 void *
topo_mod_getspecific(topo_mod_t * mod)669 topo_mod_getspecific(topo_mod_t *mod)
670 {
671 return (mod->tm_priv);
672 }
673
674 void
topo_mod_setspecific(topo_mod_t * mod,void * data)675 topo_mod_setspecific(topo_mod_t *mod, void *data)
676 {
677 mod->tm_priv = data;
678 }
679
680 void
topo_mod_setdebug(topo_mod_t * mod)681 topo_mod_setdebug(topo_mod_t *mod)
682 {
683 mod->tm_debug = 1;
684 }
685
686 ipmi_handle_t *
topo_mod_ipmi_hold(topo_mod_t * mod)687 topo_mod_ipmi_hold(topo_mod_t *mod)
688 {
689 topo_hdl_t *thp = mod->tm_hdl;
690 int err;
691 char *errmsg;
692
693 (void) pthread_mutex_lock(&thp->th_ipmi_lock);
694 if (thp->th_ipmi == NULL) {
695 if ((thp->th_ipmi = ipmi_open(&err, &errmsg, IPMI_TRANSPORT_BMC,
696 NULL)) == NULL) {
697 topo_dprintf(mod->tm_hdl, TOPO_DBG_ERR,
698 "ipmi_open() failed: %s (ipmi errno=%d)", errmsg,
699 err);
700 (void) pthread_mutex_unlock(&thp->th_ipmi_lock);
701 }
702 }
703
704
705 return (thp->th_ipmi);
706 }
707
708 void
topo_mod_ipmi_rele(topo_mod_t * mod)709 topo_mod_ipmi_rele(topo_mod_t *mod)
710 {
711 topo_hdl_t *thp = mod->tm_hdl;
712
713 (void) pthread_mutex_unlock(&thp->th_ipmi_lock);
714 }
715
716 di_node_t
topo_mod_devinfo(topo_mod_t * mod)717 topo_mod_devinfo(topo_mod_t *mod)
718 {
719 return (topo_hdl_devinfo(mod->tm_hdl));
720 }
721
722 smbios_hdl_t *
topo_mod_smbios(topo_mod_t * mod)723 topo_mod_smbios(topo_mod_t *mod)
724 {
725 topo_hdl_t *thp = mod->tm_hdl;
726
727 if (thp->th_smbios == NULL)
728 thp->th_smbios = smbios_open(NULL, SMB_VERSION, 0, NULL);
729
730 return (thp->th_smbios);
731 }
732
733 di_prom_handle_t
topo_mod_prominfo(topo_mod_t * mod)734 topo_mod_prominfo(topo_mod_t *mod)
735 {
736 return (topo_hdl_prominfo(mod->tm_hdl));
737 }
738
739 pcidb_hdl_t *
topo_mod_pcidb(topo_mod_t * mod)740 topo_mod_pcidb(topo_mod_t *mod)
741 {
742 topo_hdl_t *thp = mod->tm_hdl;
743
744 if (thp->th_pcidb == NULL)
745 thp->th_pcidb = pcidb_open(PCIDB_VERSION);
746
747 return (thp->th_pcidb);
748 }
749
750 void
topo_mod_clrdebug(topo_mod_t * mod)751 topo_mod_clrdebug(topo_mod_t *mod)
752 {
753 mod->tm_debug = 0;
754 }
755
756 /*PRINTFLIKE2*/
757 void
topo_mod_dprintf(topo_mod_t * mod,const char * format,...)758 topo_mod_dprintf(topo_mod_t *mod, const char *format, ...)
759 {
760 topo_hdl_t *thp = mod->tm_hdl;
761 va_list alist;
762
763 if (mod->tm_debug == 0 || !(thp->th_debug & TOPO_DBG_MOD))
764 return;
765
766 va_start(alist, format);
767 topo_vdprintf(mod->tm_hdl, (const char *)mod->tm_name, format, alist);
768 va_end(alist);
769 }
770
771 char *
topo_mod_product(topo_mod_t * mod)772 topo_mod_product(topo_mod_t *mod)
773 {
774 return (topo_mod_strdup(mod, mod->tm_hdl->th_product));
775 }
776
777 static char *
topo_mod_server(topo_mod_t * mod)778 topo_mod_server(topo_mod_t *mod)
779 {
780 static struct utsname uts;
781
782 (void) uname(&uts);
783 return (topo_mod_strdup(mod, uts.nodename));
784 }
785
786 static char *
topo_mod_psn(topo_mod_t * mod)787 topo_mod_psn(topo_mod_t *mod)
788 {
789 smbios_hdl_t *shp;
790 const char *psn;
791
792 if ((shp = topo_mod_smbios(mod)) == NULL ||
793 (psn = smbios_psn(shp)) == NULL)
794 return (NULL);
795
796 return (topo_cleanup_auth_str(mod->tm_hdl, psn));
797 }
798
799 static char *
topo_mod_csn(topo_mod_t * mod)800 topo_mod_csn(topo_mod_t *mod)
801 {
802 char csn[MAXNAMELEN];
803 smbios_hdl_t *shp;
804 di_prom_handle_t promh = DI_PROM_HANDLE_NIL;
805 di_node_t rooth = DI_NODE_NIL;
806 const char *bufp;
807
808 if ((shp = topo_mod_smbios(mod)) != NULL) {
809 bufp = smbios_csn(shp);
810 if (bufp != NULL)
811 (void) strlcpy(csn, bufp, MAXNAMELEN);
812 else
813 return (NULL);
814 } else if ((rooth = topo_mod_devinfo(mod)) != DI_NODE_NIL &&
815 (promh = topo_mod_prominfo(mod)) != DI_PROM_HANDLE_NIL) {
816 if (di_prom_prop_lookup_bytes(promh, rooth, "chassis-sn",
817 (unsigned char **)&bufp) != -1) {
818 (void) strlcpy(csn, bufp, MAXNAMELEN);
819 } else {
820 return (NULL);
821 }
822 } else {
823 return (NULL);
824 }
825
826 return (topo_cleanup_auth_str(mod->tm_hdl, csn));
827 }
828
829 nvlist_t *
topo_mod_auth(topo_mod_t * mod,tnode_t * pnode)830 topo_mod_auth(topo_mod_t *mod, tnode_t *pnode)
831 {
832 int err;
833 char *prod = NULL;
834 char *csn = NULL;
835 char *psn = NULL;
836 char *server = NULL;
837 nvlist_t *auth;
838
839 if ((err = topo_mod_nvalloc(mod, &auth, NV_UNIQUE_NAME)) != 0) {
840 (void) topo_mod_seterrno(mod, EMOD_FMRI_NVL);
841 return (NULL);
842 }
843
844 (void) topo_prop_get_string(pnode, FM_FMRI_AUTHORITY,
845 FM_FMRI_AUTH_PRODUCT, &prod, &err);
846 (void) topo_prop_get_string(pnode, FM_FMRI_AUTHORITY,
847 FM_FMRI_AUTH_PRODUCT_SN, &psn, &err);
848 (void) topo_prop_get_string(pnode, FM_FMRI_AUTHORITY,
849 FM_FMRI_AUTH_CHASSIS, &csn, &err);
850 (void) topo_prop_get_string(pnode, FM_FMRI_AUTHORITY,
851 FM_FMRI_AUTH_SERVER, &server, &err);
852
853 /*
854 * Let's do this the hard way
855 */
856 if (prod == NULL)
857 prod = topo_mod_product(mod);
858 if (csn == NULL)
859 csn = topo_mod_csn(mod);
860 if (psn == NULL)
861 psn = topo_mod_psn(mod);
862 if (server == NULL) {
863 server = topo_mod_server(mod);
864 }
865
866 /*
867 * No luck, return NULL
868 */
869 if (!prod && !server && !csn && !psn) {
870 nvlist_free(auth);
871 return (NULL);
872 }
873
874 err = 0;
875 if (prod != NULL) {
876 err |= nvlist_add_string(auth, FM_FMRI_AUTH_PRODUCT, prod);
877 topo_mod_strfree(mod, prod);
878 }
879 if (psn != NULL) {
880 err |= nvlist_add_string(auth, FM_FMRI_AUTH_PRODUCT_SN, psn);
881 topo_mod_strfree(mod, psn);
882 }
883 if (server != NULL) {
884 err |= nvlist_add_string(auth, FM_FMRI_AUTH_SERVER, server);
885 topo_mod_strfree(mod, server);
886 }
887 if (csn != NULL) {
888 err |= nvlist_add_string(auth, FM_FMRI_AUTH_CHASSIS, csn);
889 topo_mod_strfree(mod, csn);
890 }
891
892 if (err != 0) {
893 nvlist_free(auth);
894 (void) topo_mod_seterrno(mod, EMOD_NVL_INVAL);
895 return (NULL);
896 }
897
898 return (auth);
899 }
900
901 topo_walk_t *
topo_mod_walk_init(topo_mod_t * mod,tnode_t * node,topo_mod_walk_cb_t cb_f,void * pdata,int * errp)902 topo_mod_walk_init(topo_mod_t *mod, tnode_t *node, topo_mod_walk_cb_t cb_f,
903 void *pdata, int *errp)
904 {
905 topo_walk_t *wp;
906 topo_hdl_t *thp = mod->tm_hdl;
907
908 if ((wp = topo_node_walk_init(thp, mod, node, (int (*)())cb_f, pdata,
909 errp)) == NULL)
910 return (NULL);
911
912 return (wp);
913 }
914
915 char *
topo_mod_clean_str(topo_mod_t * mod,const char * str)916 topo_mod_clean_str(topo_mod_t *mod, const char *str)
917 {
918 if (str == NULL)
919 return (NULL);
920
921 return (topo_cleanup_auth_str(mod->tm_hdl, str));
922 }
923
924 int
topo_mod_file_search(topo_mod_t * mod,const char * file,int oflags)925 topo_mod_file_search(topo_mod_t *mod, const char *file, int oflags)
926 {
927 int ret;
928 char *path;
929 topo_hdl_t *thp = mod->tm_hdl;
930
931 path = topo_search_path(mod, thp->th_rootdir, file);
932 if (path == NULL) {
933 return (-1);
934 }
935
936 ret = open(path, oflags);
937 topo_mod_strfree(mod, path);
938 return (ret);
939 }
940
941 /*ARGSUSED*/
942 int
topo_mod_hc_occupied(topo_mod_t * mod,tnode_t * node,topo_version_t version,nvlist_t * in,nvlist_t ** out)943 topo_mod_hc_occupied(topo_mod_t *mod, tnode_t *node, topo_version_t version,
944 nvlist_t *in, nvlist_t **out)
945 {
946 nvlist_t *nvl = NULL;
947 tnode_t *cnp;
948 boolean_t is_occupied = B_FALSE;
949
950 if (version > TOPO_METH_OCCUPIED_VERSION)
951 return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW));
952
953 /*
954 * Iterate though the child nodes. If there are no non-facility
955 * node children then it is unoccupied.
956 */
957 for (cnp = topo_child_first(node); cnp != NULL;
958 cnp = topo_child_next(node, cnp)) {
959 if (topo_node_flags(cnp) != TOPO_NODE_FACILITY)
960 is_occupied = B_TRUE;
961 }
962
963 if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 ||
964 nvlist_add_boolean_value(nvl, TOPO_METH_OCCUPIED_RET,
965 is_occupied) != 0) {
966 topo_mod_dprintf(mod, "Failed to allocate 'out' nvlist\n");
967 nvlist_free(nvl);
968 return (topo_mod_seterrno(mod, EMOD_NOMEM));
969 }
970 *out = nvl;
971
972 return (0);
973 }
974
975 /*
976 * Convenience routine for creating a UFM slot node. This routine assumes
977 * that the caller has already created the containing range via a call to
978 * topo_node_range_create().
979 */
980 tnode_t *
topo_mod_create_ufm_slot(topo_mod_t * mod,tnode_t * ufmnode,topo_ufm_slot_info_t * slotinfo)981 topo_mod_create_ufm_slot(topo_mod_t *mod, tnode_t *ufmnode,
982 topo_ufm_slot_info_t *slotinfo)
983 {
984 nvlist_t *auth = NULL, *fmri = NULL;
985 tnode_t *slotnode;
986 topo_pgroup_info_t pgi;
987 int err, rc;
988
989 if (slotinfo == NULL || slotinfo->usi_version == NULL ||
990 slotinfo->usi_mode == 0) {
991 topo_mod_dprintf(mod, "invalid slot info");
992 (void) topo_mod_seterrno(mod, ETOPO_MOD_INVAL);
993 return (NULL);
994 }
995 if ((auth = topo_mod_auth(mod, ufmnode)) == NULL) {
996 topo_mod_dprintf(mod, "topo_mod_auth() failed: %s",
997 topo_mod_errmsg(mod));
998 /* errno set */
999 return (NULL);
1000 }
1001
1002 if ((fmri = topo_mod_hcfmri(mod, ufmnode, FM_HC_SCHEME_VERSION,
1003 SLOT, slotinfo->usi_slotid, NULL, auth, NULL, NULL, NULL)) ==
1004 NULL) {
1005 nvlist_free(auth);
1006 topo_mod_dprintf(mod, "topo_mod_hcfmri() failed: %s",
1007 topo_mod_errmsg(mod));
1008 /* errno set */
1009 return (NULL);
1010 }
1011
1012 if ((slotnode = topo_node_bind(mod, ufmnode, SLOT,
1013 slotinfo->usi_slotid, fmri)) == NULL) {
1014 nvlist_free(auth);
1015 nvlist_free(fmri);
1016 topo_mod_dprintf(mod, "topo_node_bind() failed: %s",
1017 topo_mod_errmsg(mod));
1018 /* errno set */
1019 return (NULL);
1020 }
1021
1022 /* Create authority and system pgroups */
1023 topo_pgroup_hcset(slotnode, auth);
1024 nvlist_free(auth);
1025 nvlist_free(fmri);
1026
1027 /* Just inherit the parent's FRU */
1028 if (topo_node_fru_set(slotnode, NULL, 0, &err) != 0) {
1029 topo_mod_dprintf(mod, "failed to set FRU on %s: %s", UFM,
1030 topo_strerror(err));
1031 (void) topo_mod_seterrno(mod, err);
1032 goto slotfail;
1033 }
1034
1035 pgi.tpi_name = TOPO_PGROUP_SLOT;
1036 pgi.tpi_namestab = TOPO_STABILITY_PRIVATE;
1037 pgi.tpi_datastab = TOPO_STABILITY_PRIVATE;
1038 pgi.tpi_version = TOPO_VERSION;
1039 rc = topo_pgroup_create(slotnode, &pgi, &err);
1040
1041 if (rc == 0)
1042 rc += topo_prop_set_uint32(slotnode, TOPO_PGROUP_SLOT,
1043 TOPO_PROP_SLOT_TYPE, TOPO_PROP_IMMUTABLE,
1044 TOPO_SLOT_TYPE_UFM, &err);
1045
1046 pgi.tpi_name = TOPO_PGROUP_UFM_SLOT;
1047
1048 if (rc == 0)
1049 rc += topo_pgroup_create(slotnode, &pgi, &err);
1050
1051 if (rc == 0) {
1052 rc += topo_prop_set_uint32(slotnode, TOPO_PGROUP_UFM_SLOT,
1053 TOPO_PROP_UFM_SLOT_MODE, TOPO_PROP_IMMUTABLE,
1054 slotinfo->usi_mode, &err);
1055 }
1056
1057 if (rc == 0) {
1058 rc += topo_prop_set_uint32(slotnode, TOPO_PGROUP_UFM_SLOT,
1059 TOPO_PROP_UFM_SLOT_ACTIVE, TOPO_PROP_IMMUTABLE,
1060 (uint32_t)slotinfo->usi_active, &err);
1061 }
1062
1063 if (rc == 0) {
1064 rc += topo_prop_set_string(slotnode, TOPO_PGROUP_UFM_SLOT,
1065 TOPO_PROP_UFM_SLOT_VERSION, TOPO_PROP_IMMUTABLE,
1066 slotinfo->usi_version, &err);
1067 }
1068
1069 if (rc == 0 && slotinfo->usi_extra != NULL) {
1070 nvpair_t *elem = NULL;
1071 char *pname, *pval;
1072
1073 while ((elem = nvlist_next_nvpair(slotinfo->usi_extra,
1074 elem)) != NULL) {
1075 if (nvpair_type(elem) != DATA_TYPE_STRING)
1076 continue;
1077
1078 pname = nvpair_name(elem);
1079 if ((rc -= nvpair_value_string(elem, &pval)) != 0)
1080 break;
1081
1082 rc += topo_prop_set_string(slotnode,
1083 TOPO_PGROUP_UFM_SLOT, pname, TOPO_PROP_IMMUTABLE,
1084 pval, &err);
1085
1086 if (rc != 0)
1087 break;
1088 }
1089 }
1090
1091 if (rc != 0) {
1092 topo_mod_dprintf(mod, "error setting properties on %s node",
1093 SLOT);
1094 (void) topo_mod_seterrno(mod, err);
1095 goto slotfail;
1096 }
1097 return (slotnode);
1098
1099 slotfail:
1100 topo_node_unbind(slotnode);
1101 return (NULL);
1102 }
1103
1104 /*
1105 * This is a convenience routine to allow enumerator modules to easily create
1106 * the necessary UFM node layout for the most common case, which will be a
1107 * single UFM with a single slot. This routine assumes that the caller has
1108 * already created the containing range via a call to topo_node_range_create().
1109 *
1110 * For more complex scenarios (like multiple slots per UFM), callers can set
1111 * the slotinfo param to NULL. In this case the ufm node will get created, but
1112 * it will skip creating the slot node - allowing the module to manually call
1113 * topo_mod_create_ufm_slot() to create custom UFM slots.
1114 */
1115 tnode_t *
topo_mod_create_ufm(topo_mod_t * mod,tnode_t * parent,const char * descr,topo_ufm_slot_info_t * slotinfo)1116 topo_mod_create_ufm(topo_mod_t *mod, tnode_t *parent, const char *descr,
1117 topo_ufm_slot_info_t *slotinfo)
1118 {
1119 nvlist_t *auth = NULL, *fmri = NULL;
1120 tnode_t *ufmnode, *slotnode;
1121 topo_pgroup_info_t pgi;
1122 int err, rc;
1123
1124 if ((auth = topo_mod_auth(mod, parent)) == NULL) {
1125 topo_mod_dprintf(mod, "topo_mod_auth() failed: %s",
1126 topo_mod_errmsg(mod));
1127 /* errno set */
1128 return (NULL);
1129 }
1130
1131 if ((fmri = topo_mod_hcfmri(mod, parent, FM_HC_SCHEME_VERSION,
1132 UFM, 0, NULL, auth, NULL, NULL, NULL)) ==
1133 NULL) {
1134 nvlist_free(auth);
1135 topo_mod_dprintf(mod, "topo_mod_hcfmri() failed: %s",
1136 topo_mod_errmsg(mod));
1137 /* errno set */
1138 return (NULL);
1139 }
1140
1141 if ((ufmnode = topo_node_bind(mod, parent, UFM, 0, fmri)) == NULL) {
1142 nvlist_free(auth);
1143 nvlist_free(fmri);
1144 topo_mod_dprintf(mod, "topo_node_bind() failed: %s",
1145 topo_mod_errmsg(mod));
1146 /* errno set */
1147 return (NULL);
1148 }
1149
1150 /* Create authority and system pgroups */
1151 topo_pgroup_hcset(ufmnode, auth);
1152 nvlist_free(auth);
1153 nvlist_free(fmri);
1154
1155 /* Just inherit the parent's FRU */
1156 if (topo_node_fru_set(ufmnode, NULL, 0, &err) != 0) {
1157 topo_mod_dprintf(mod, "failed to set FRU on %s: %s", UFM,
1158 topo_strerror(err));
1159 (void) topo_mod_seterrno(mod, err);
1160 goto ufmfail;
1161 }
1162
1163 pgi.tpi_name = TOPO_PGROUP_UFM;
1164 pgi.tpi_namestab = TOPO_STABILITY_PRIVATE;
1165 pgi.tpi_datastab = TOPO_STABILITY_PRIVATE;
1166 pgi.tpi_version = TOPO_VERSION;
1167 rc = topo_pgroup_create(ufmnode, &pgi, &err);
1168
1169 if (rc == 0)
1170 rc += topo_prop_set_string(ufmnode, TOPO_PGROUP_UFM,
1171 TOPO_PROP_UFM_DESCR, TOPO_PROP_IMMUTABLE, descr, &err);
1172
1173 if (rc != 0) {
1174 topo_mod_dprintf(mod, "error setting properties on %s node",
1175 UFM);
1176 (void) topo_mod_seterrno(mod, err);
1177 goto ufmfail;
1178 }
1179
1180 if (slotinfo != NULL) {
1181 if (topo_node_range_create(mod, ufmnode, SLOT, 0, 0) < 0) {
1182 topo_mod_dprintf(mod, "error creating %s range", SLOT);
1183 goto ufmfail;
1184 }
1185 slotnode = topo_mod_create_ufm_slot(mod, ufmnode, slotinfo);
1186
1187 if (slotnode == NULL)
1188 goto ufmfail;
1189 }
1190 return (ufmnode);
1191
1192 ufmfail:
1193 topo_node_unbind(ufmnode);
1194 return (NULL);
1195 }
1196