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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * Topology Plugin Modules 31 * 32 * Topology plugin modules are shared libraries that are dlopen'd and 33 * used to enumerate resources in the system. 34 * They are loaded by our builtin scheme-specific plugins or other modules 35 * to enumerate and create nodes for resources that are present in the system. 36 * They may also export a set of resource (node) specific methods that can be 37 * called on node-by-node basis. 38 * 39 * Module Plugin API 40 * 41 * Enumerators must provide entry points for intialization and clean-up 42 * (_topo_init() and _topo_fini()). In their _topo_init() function, an 43 * enumerator should register (topo_mod_register()) its enumeration callback 44 * and allocate resources required for a subsequent call to the callback. 45 * Optionally, methods may also be registered with topo_method_register(). 46 * 47 * In its enumeration callback routine, the module should search for resources 48 * within its realm of resposibility and create any node ranges, 49 * topo_node_range_create() or nodes, topo_node_bind(). The Enumerator 50 * module is handed a node to which it may begin attaching additional 51 * topology nodes. 52 * 53 * If additional helper modules need to be loaded to complete the enumeration 54 * the module may do so by calling topo_mod_load(). Enumeration may then 55 * continue with the module handing off enumeration to its helper module 56 * by calling topo_mod_enumerate(). 57 * 58 * If the module registers a release callback, it will be called on a node 59 * by node basis during topo_snap_rele(). Any private node data may be 60 * deallocated or methods unregistered at that time. Global module data 61 * should be clean-up before or at the time that the module _topo_fini 62 * entry point is called. 63 */ 64 65 #include <pthread.h> 66 #include <assert.h> 67 #include <errno.h> 68 #include <dirent.h> 69 #include <limits.h> 70 #include <alloca.h> 71 #include <unistd.h> 72 #include <stdio.h> 73 74 #include <topo_module.h> 75 #include <topo_alloc.h> 76 #include <topo_string.h> 77 #include <topo_error.h> 78 #include <topo_subr.h> 79 80 topo_mod_t * 81 topo_mod_load(topo_mod_t *pmod, const char *path) 82 { 83 int err = 0; 84 char *p; 85 topo_mod_t *mod = NULL; 86 topo_hdl_t *thp; 87 88 thp = pmod->tm_hdl; 89 90 /* 91 * Already loaded, bump the ref count 92 */ 93 if ((mod = topo_mod_lookup(thp, path)) != NULL) { 94 topo_mod_hold(mod); 95 return (mod); 96 } 97 98 /* 99 * Check for a valid path 100 */ 101 if (access(path, F_OK) != 0) { 102 (void) topo_mod_seterrno(pmod, ETOPO_MOD_NOENT); 103 return (NULL); 104 } 105 106 if ((p = strrchr(path, '.')) != NULL && strcmp(p, ".so") == 0) { 107 if ((mod = topo_modhash_load(thp, path, 108 &topo_rtld_ops)) == NULL) { /* returned with mod held */ 109 (void) topo_mod_seterrno(pmod, err ? err : 110 ETOPO_MOD_NOENT); 111 return (NULL); 112 } 113 } else { 114 (void) topo_mod_seterrno(pmod, err ? err : ETOPO_MOD_NOENT); 115 return (NULL); 116 } 117 118 return (mod); 119 } 120 121 void 122 topo_mod_unload(topo_mod_t *mod) 123 { 124 topo_mod_rele(mod); 125 } 126 127 static int 128 set_register_error(topo_mod_t *mod, int err) 129 { 130 if (mod->tm_info != NULL) 131 topo_mod_unregister(mod); 132 133 topo_dprintf(TOPO_DBG_ERR, "module registration failed for %s: %s\n", 134 mod->tm_name, topo_strerror(err)); 135 136 return (topo_mod_seterrno(mod, err)); 137 } 138 139 int 140 topo_mod_register(topo_mod_t *mod, const topo_modinfo_t *mip, void *priv) 141 { 142 143 assert(!(mod->tm_flags & TOPO_MOD_FINI || 144 mod->tm_flags & TOPO_MOD_REG)); 145 146 if (mod->tm_version > mip->tmi_version) 147 return (set_register_error(mod, ETOPO_VER_OLD)); 148 if (mod->tm_version < mip->tmi_version) 149 return (set_register_error(mod, ETOPO_VER_NEW)); 150 151 if ((mod->tm_info = topo_mod_alloc(mod, sizeof (topo_modinfo_t))) 152 == NULL) 153 return (set_register_error(mod, ETOPO_NOMEM)); 154 155 mod->tm_info->tmi_desc = topo_mod_strdup(mod, mip->tmi_desc); 156 if (mod->tm_info->tmi_desc == NULL) 157 return (set_register_error(mod, ETOPO_NOMEM)); 158 159 mod->tm_info->tmi_version = (topo_version_t)mip->tmi_version; 160 mod->tm_info->tmi_enum = mip->tmi_enum; 161 mod->tm_info->tmi_release = mip->tmi_release; 162 163 mod->tm_flags |= TOPO_MOD_REG; 164 mod->tm_priv = priv; 165 166 if (mod == NULL) { 167 topo_dprintf(TOPO_DBG_MOD, "registration succeeded for %s\n", 168 mod->tm_name); 169 170 return (0); 171 } 172 173 174 topo_dprintf(TOPO_DBG_MOD, "registration succeeded for %s\n", 175 mod->tm_name); 176 177 return (0); 178 } 179 180 void 181 topo_mod_unregister(topo_mod_t *mod) 182 { 183 if (mod->tm_info == NULL) 184 return; 185 186 assert(!(mod->tm_flags & TOPO_MOD_FINI)); 187 188 mod->tm_flags &= ~TOPO_MOD_REG; 189 190 if (mod->tm_info == NULL) 191 return; 192 193 if (mod->tm_info->tmi_desc != NULL) 194 topo_mod_strfree(mod, mod->tm_info->tmi_desc); 195 196 topo_mod_free(mod, mod->tm_info, sizeof (topo_modinfo_t)); 197 198 mod->tm_info = NULL; 199 } 200 201 int 202 topo_mod_enumerate(topo_mod_t *mod, tnode_t *node, const char *enum_name, 203 const char *name, topo_instance_t min, topo_instance_t max) 204 { 205 int err = 0; 206 topo_mod_t *enum_mod; 207 208 assert(mod->tm_flags & TOPO_MOD_REG); 209 210 if ((enum_mod = topo_mod_lookup(mod->tm_hdl, enum_name)) == NULL) 211 return (topo_mod_seterrno(mod, ETOPO_MOD_NOENT)); 212 213 topo_node_hold(node); 214 215 topo_dprintf(TOPO_DBG_MOD, "module %s enumerating node %s=%d\n", 216 (char *)mod->tm_name, (char *)node->tn_name, node->tn_instance); 217 218 topo_mod_enter(enum_mod); 219 err = enum_mod->tm_info->tmi_enum(enum_mod, node, name, min, max, 220 enum_mod->tm_priv); 221 topo_mod_exit(enum_mod); 222 223 if (err != 0) { 224 (void) topo_mod_seterrno(mod, ETOPO_MODULE); 225 226 topo_dprintf(TOPO_DBG_ERR, "module %s failed enumeration for " 227 " node %s=%d\n", (char *)mod->tm_name, 228 (char *)node->tn_name, node->tn_instance); 229 230 topo_node_rele(node); 231 return (-1); 232 } 233 234 topo_node_rele(node); 235 236 return (0); 237 } 238 239 char * 240 topo_mod_rootdir(topo_mod_t *mod) 241 { 242 return (mod->tm_rootdir); 243 } 244 245 topo_hdl_t * 246 topo_mod_handle(topo_mod_t *mod) 247 { 248 return (mod->tm_hdl); 249 } 250 251 void * 252 topo_mod_private(topo_mod_t *mod) 253 { 254 return (mod->tm_priv); 255 } 256 257 void 258 topo_mod_setdebug(topo_mod_t *mod, int mask) 259 { 260 mod->tm_debug |= mask; 261 } 262 263 void 264 topo_mod_clrdebug(topo_mod_t *mod) 265 { 266 mod->tm_debug = 0; 267 } 268 269 /*PRINTFLIKE2*/ 270 void 271 topo_mod_dprintf(topo_mod_t *mod, const char *format, ...) 272 { 273 if (mod->tm_debug & mod->tm_hdl->th_debug) { 274 va_list alist; 275 276 va_start(alist, format); 277 (void) fputs("libtopo DEBUG: ", stderr); 278 (void) vfprintf(stderr, format, alist); 279 va_end(alist); 280 } 281 } 282