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 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 /* 26 * Copyright (c) 2018, Joyent, Inc. 27 */ 28 29 #include <pthread.h> 30 #include <assert.h> 31 #include <errno.h> 32 #include <dirent.h> 33 #include <fnmatch.h> 34 #include <limits.h> 35 #include <alloca.h> 36 #include <unistd.h> 37 #include <stdio.h> 38 #include <strings.h> 39 40 #include <topo_mod.h> 41 42 #include <topo_error.h> 43 #include <topo_module.h> 44 #include <topo_subr.h> 45 #include <topo_tree.h> 46 47 topo_imethod_t * 48 topo_method_lookup(tnode_t *node, const char *name) 49 { 50 topo_imethod_t *mp; 51 52 for (mp = topo_list_next(&node->tn_methods); mp != NULL; 53 mp = topo_list_next(mp)) { 54 if (strcmp(name, mp->tim_name) == 0) { 55 topo_node_unlock(node); 56 return (mp); 57 } 58 } 59 60 return (NULL); 61 } 62 63 /* 64 * Simple API to determine if the specified node supports a given topo method 65 * (specified by the method name and version). Returns true if supported, false 66 * otherwise. 67 */ 68 boolean_t 69 topo_method_supported(tnode_t *node, const char *name, topo_version_t vers) 70 { 71 topo_imethod_t *mp; 72 73 topo_node_lock(node); 74 for (mp = topo_list_next(&node->tn_methods); mp != NULL; 75 mp = topo_list_next(mp)) { 76 if ((strcmp(name, mp->tim_name) == 0) && 77 (vers == mp->tim_version)) { 78 topo_node_unlock(node); 79 return (B_TRUE); 80 } 81 } 82 topo_node_unlock(node); 83 return (B_FALSE); 84 } 85 86 static void 87 topo_method_enter(topo_imethod_t *mp) 88 { 89 (void) pthread_mutex_lock(&mp->tim_lock); 90 91 while (mp->tim_busy != 0) 92 (void) pthread_cond_wait(&mp->tim_cv, &mp->tim_lock); 93 94 ++mp->tim_busy; 95 96 (void) pthread_mutex_unlock(&mp->tim_lock); 97 } 98 99 static void 100 topo_method_exit(topo_imethod_t *mp) 101 { 102 (void) pthread_mutex_lock(&mp->tim_lock); 103 --mp->tim_busy; 104 105 assert(mp->tim_busy == 0); 106 107 (void) pthread_cond_broadcast(&mp->tim_cv); 108 (void) pthread_mutex_unlock(&mp->tim_lock); 109 } 110 111 static int 112 set_methregister_error(topo_mod_t *mod, tnode_t *node, topo_imethod_t *mp, 113 int err) 114 { 115 if (mp != NULL) { 116 topo_list_delete(&node->tn_methods, mp); 117 if (mp->tim_name != NULL) 118 topo_mod_strfree(mod, mp->tim_name); 119 if (mp->tim_desc != NULL) 120 topo_mod_strfree(mod, mp->tim_desc); 121 122 topo_mod_free(mod, mp, sizeof (topo_imethod_t)); 123 } 124 125 topo_node_unlock(node); 126 127 topo_dprintf(mod->tm_hdl, TOPO_DBG_ERR, 128 "method registration failed for %s: %s\n", 129 mod->tm_name, topo_strerror(err)); 130 131 return (topo_mod_seterrno(mod, err)); 132 } 133 134 int 135 topo_method_register(topo_mod_t *mod, tnode_t *node, const topo_method_t *mp) 136 { 137 topo_imethod_t *imp; 138 const topo_method_t *meth; 139 140 /* 141 * Initialize module methods 142 */ 143 for (meth = &mp[0]; meth->tm_name != NULL; meth++) { 144 145 topo_node_lock(node); 146 if (topo_method_lookup(node, meth->tm_name) != NULL) { 147 topo_node_unlock(node); 148 continue; 149 } 150 151 if (meth->tm_stability < TOPO_STABILITY_INTERNAL || 152 meth->tm_stability > TOPO_STABILITY_MAX || 153 meth->tm_func == NULL) 154 return (set_methregister_error(mod, node, NULL, 155 ETOPO_METHOD_INVAL)); 156 157 imp = topo_mod_zalloc(mod, sizeof (topo_imethod_t)); 158 if (imp == NULL) 159 return (set_methregister_error(mod, node, imp, 160 ETOPO_METHOD_NOMEM)); 161 162 if ((imp->tim_name = topo_mod_strdup(mod, meth->tm_name)) 163 == NULL) 164 return (set_methregister_error(mod, node, imp, 165 ETOPO_METHOD_NOMEM)); 166 167 if ((imp->tim_desc = topo_mod_strdup(mod, meth->tm_desc)) 168 == NULL) 169 return (set_methregister_error(mod, node, imp, 170 ETOPO_METHOD_NOMEM)); 171 172 173 imp->tim_stability = meth->tm_stability; 174 imp->tim_version = meth->tm_version; 175 imp->tim_func = meth->tm_func; 176 imp->tim_mod = mod; 177 178 topo_list_append(&node->tn_methods, imp); 179 topo_node_unlock(node); 180 181 topo_dprintf(mod->tm_hdl, TOPO_DBG_MODSVC, 182 "registered module %s method " 183 "%s for %s=%d\n", mod->tm_name, imp->tim_name, 184 topo_node_name(node), topo_node_instance(node)); 185 186 } 187 188 return (0); 189 } 190 191 void 192 topo_method_unregister(topo_mod_t *mod, tnode_t *node, const char *name) 193 { 194 topo_imethod_t *mp; 195 196 topo_node_lock(node); 197 for (mp = topo_list_next(&node->tn_methods); mp != NULL; 198 mp = topo_list_next(mp)) { 199 if (strcmp(name, mp->tim_name) == 0) 200 break; 201 } 202 203 if (mp == NULL) { 204 topo_node_unlock(node); 205 return; 206 } 207 208 topo_list_delete(&node->tn_methods, mp); 209 topo_node_unlock(node); 210 211 if (mp->tim_name != NULL) 212 topo_mod_strfree(mod, mp->tim_name); 213 if (mp->tim_desc != NULL) 214 topo_mod_strfree(mod, mp->tim_desc); 215 216 topo_mod_free(mod, mp, sizeof (topo_imethod_t)); 217 } 218 219 void 220 topo_method_unregister_all(topo_mod_t *mod, tnode_t *node) 221 { 222 topo_imethod_t *mp; 223 224 topo_node_lock(node); 225 while ((mp = topo_list_next(&node->tn_methods)) != NULL) { 226 topo_list_delete(&node->tn_methods, mp); 227 if (mp->tim_name != NULL) 228 topo_mod_strfree(mod, mp->tim_name); 229 if (mp->tim_desc != NULL) 230 topo_mod_strfree(mod, mp->tim_desc); 231 topo_mod_free(mod, mp, sizeof (topo_imethod_t)); 232 } 233 topo_node_unlock(node); 234 } 235 236 237 int 238 topo_method_call(tnode_t *node, const char *method, 239 topo_version_t version, nvlist_t *in, nvlist_t **out, int *err) 240 { 241 int rc, save; 242 topo_imethod_t *mp; 243 244 for (mp = topo_list_next(&node->tn_methods); mp != NULL; 245 mp = topo_list_next(mp)) { 246 if (strcmp(method, mp->tim_name) != 0) 247 continue; 248 249 if (version < mp->tim_version) { 250 *err = ETOPO_METHOD_VEROLD; 251 return (-1); 252 } else if (version > mp->tim_version) { 253 *err = ETOPO_METHOD_VERNEW; 254 return (-1); 255 } 256 257 topo_method_enter(mp); 258 save = mp->tim_mod->tm_errno; 259 mp->tim_mod->tm_errno = 0; 260 if ((rc = mp->tim_func(mp->tim_mod, node, version, in, out)) 261 < 0) { 262 if (mp->tim_mod->tm_errno == 0) 263 *err = ETOPO_METHOD_FAIL; 264 else 265 *err = mp->tim_mod->tm_errno; 266 } 267 mp->tim_mod->tm_errno = save; 268 topo_method_exit(mp); 269 270 return (rc); 271 272 } 273 274 *err = ETOPO_METHOD_NOTSUP; 275 return (-1); 276 } 277 278 int 279 topo_method_invoke(tnode_t *node, const char *method, 280 topo_version_t version, nvlist_t *in, nvlist_t **out, int *err) 281 { 282 int rc; 283 284 topo_node_hold(node); 285 rc = topo_method_call(node, method, version, in, out, err); 286 topo_node_rele(node); 287 288 return (rc); 289 } 290 291 struct sensor_errinfo 292 { 293 boolean_t se_predictive; 294 boolean_t se_nonrecov; 295 uint32_t se_src; 296 }; 297 298 static boolean_t 299 topo_sensor_failed(int32_t type, uint32_t state, struct sensor_errinfo *seinfo) 300 { 301 boolean_t failed; 302 303 failed = B_FALSE; 304 /* 305 * Unless the sensor explicitely says otherwise, all failures are 306 * non-recoverable, hard failures, coming from an unknown source. 307 */ 308 seinfo->se_predictive = B_FALSE; 309 seinfo->se_nonrecov = B_TRUE; 310 seinfo->se_src = TOPO_SENSOR_ERRSRC_UNKNOWN; 311 312 switch (type) { 313 case TOPO_SENSOR_TYPE_THRESHOLD_STATE: 314 if (state & (TOPO_SENSOR_STATE_THRESH_LOWER_NONREC | 315 TOPO_SENSOR_STATE_THRESH_UPPER_NONREC)) { 316 failed = B_TRUE; 317 } else if (state & (TOPO_SENSOR_STATE_THRESH_LOWER_CRIT | 318 TOPO_SENSOR_STATE_THRESH_UPPER_CRIT)) { 319 failed = B_TRUE; 320 seinfo->se_nonrecov = B_FALSE; 321 } 322 break; 323 324 case TOPO_SENSOR_TYPE_POWER_SUPPLY: 325 if (state & TOPO_SENSOR_STATE_POWER_SUPPLY_PREDFAIL) { 326 failed = B_TRUE; 327 seinfo->se_predictive = B_TRUE; 328 seinfo->se_src = TOPO_SENSOR_ERRSRC_INTERNAL; 329 } else if (state & TOPO_SENSOR_STATE_POWER_SUPPLY_FAILURE) { 330 failed = B_TRUE; 331 seinfo->se_src = TOPO_SENSOR_ERRSRC_INTERNAL; 332 } else if (state & 333 (TOPO_SENSOR_STATE_POWER_SUPPLY_INPUT_LOST | 334 TOPO_SENSOR_STATE_POWER_SUPPLY_INPUT_RANGE | 335 TOPO_SENSOR_STATE_POWER_SUPPLY_INPUT_RANGE_PRES)) { 336 seinfo->se_src = TOPO_SENSOR_ERRSRC_EXTERNAL; 337 failed = B_TRUE; 338 } 339 break; 340 341 case TOPO_SENSOR_TYPE_GENERIC_FAILURE: 342 if (state & TOPO_SENSOR_STATE_GENERIC_FAIL_NONRECOV) { 343 failed = B_TRUE; 344 } else if (state & TOPO_SENSOR_STATE_GENERIC_FAIL_CRITICAL) { 345 failed = B_TRUE; 346 seinfo->se_nonrecov = B_FALSE; 347 } 348 break; 349 350 case TOPO_SENSOR_TYPE_GENERIC_OK: 351 if (state & TOPO_SENSOR_STATE_GENERIC_OK_DEASSERTED) 352 failed = B_TRUE; 353 break; 354 case TOPO_SENSOR_TYPE_GENERIC_PREDFAIL: 355 if (state & TOPO_SENSOR_STATE_GENERIC_PREDFAIL_ASSERTED) { 356 failed = B_TRUE; 357 seinfo->se_predictive = B_TRUE; 358 } 359 break; 360 } 361 362 return (failed); 363 } 364 365 static boolean_t 366 topo_spoof_apply(topo_hdl_t *thp, tnode_t *node, tnode_t *facnode, 367 nvlist_t *in, uint32_t *state) 368 { 369 nvpair_t *elem = NULL; 370 nvlist_t *spoof, *rsrc = NULL; 371 char *fmrimatch, *fmri, *facmatch; 372 uint32_t spoof_state; 373 int err; 374 375 while ((elem = nvlist_next_nvpair(in, elem)) != NULL) { 376 if (nvpair_value_nvlist(elem, &spoof) != 0) 377 return (B_FALSE); 378 379 if (nvlist_lookup_string(spoof, ST_SPOOF_FMRI, &fmrimatch) != 380 0 || nvlist_lookup_string(spoof, ST_SPOOF_SENSOR, 381 &facmatch) != 0 || nvlist_lookup_uint32(spoof, 382 ST_SPOOF_STATE, &spoof_state) != 0) 383 continue; 384 385 if (topo_node_resource(node, &rsrc, &err) != 0 || 386 topo_fmri_nvl2str(thp, rsrc, &fmri, &err) != 0) { 387 nvlist_free(rsrc); 388 continue; 389 } 390 nvlist_free(rsrc); 391 392 if (fnmatch(fmrimatch, fmri, 0) == 0 && 393 strcmp(facmatch, topo_node_name(facnode)) == 0) { 394 *state = spoof_state; 395 topo_hdl_strfree(thp, fmri); 396 return (B_TRUE); 397 } 398 topo_hdl_strfree(thp, fmri); 399 } 400 return (B_FALSE); 401 } 402 403 /* 404 * Determine whether there are any sensors indicating failure. This function 405 * is used internally to determine whether a given component is usable, as well 406 * by external monitoring software that wants additional information such as 407 * which sensors indicated failure. The return value is an nvlist of nvlists 408 * indexed by sensor name, each entry with the following contents: 409 * 410 * type, state, units, reading 411 * 412 * Identical to sensor node. 413 * 414 * nonrecov 415 * 416 * Boolean value that is set to indicate that the error is 417 * non-recoverable (the unit is out of service). The default is 418 * critical failure, which indicates a fault but the unit is still 419 * operating. 420 * 421 * injected 422 * 423 * Boolean value indicating that the sensor state was injected. 424 */ 425 /*ARGSUSED*/ 426 int 427 topo_method_sensor_failure(topo_mod_t *mod, tnode_t *node, 428 topo_version_t version, nvlist_t *in, nvlist_t **out) 429 { 430 topo_faclist_t faclist, *fp; 431 int err; 432 nvlist_t *nvl, *props, *propval, *tmp; 433 int ret = -1; 434 uint32_t type, state, units; 435 nvpair_t *elem; 436 double reading; 437 char *propname; 438 boolean_t has_reading, is_spoofed = B_FALSE; 439 struct sensor_errinfo seinfo; 440 441 if (topo_node_facility(mod->tm_hdl, node, TOPO_FAC_TYPE_SENSOR, 442 TOPO_FAC_TYPE_ANY, &faclist, &err) != 0) 443 return (topo_mod_seterrno(mod, ETOPO_METHOD_NOTSUP)); 444 445 if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0) 446 goto error; 447 448 for (fp = topo_list_next(&faclist.tf_list); fp != NULL; 449 fp = topo_list_next(fp)) { 450 if (topo_prop_getpgrp(fp->tf_node, TOPO_PGROUP_FACILITY, 451 &props, &err) != 0) { 452 nvlist_free(nvl); 453 goto error; 454 } 455 type = state = units = 0; 456 reading = 0; 457 has_reading = B_FALSE; 458 459 elem = NULL; 460 while ((elem = nvlist_next_nvpair(props, elem)) != NULL) { 461 if (strcmp(nvpair_name(elem), TOPO_PROP_VAL) != 0 || 462 nvpair_type(elem) != DATA_TYPE_NVLIST) 463 continue; 464 465 (void) nvpair_value_nvlist(elem, &propval); 466 if (nvlist_lookup_string(propval, 467 TOPO_PROP_VAL_NAME, &propname) != 0) 468 continue; 469 470 if (strcmp(propname, TOPO_FACILITY_TYPE) == 0) { 471 (void) nvlist_lookup_uint32(propval, 472 TOPO_PROP_VAL_VAL, &type); 473 } else if (strcmp(propname, TOPO_SENSOR_STATE) == 0) { 474 (void) nvlist_lookup_uint32(propval, 475 TOPO_PROP_VAL_VAL, &state); 476 } else if (strcmp(propname, TOPO_SENSOR_UNITS) == 0) { 477 (void) nvlist_lookup_uint32(propval, 478 TOPO_PROP_VAL_VAL, &units); 479 } else if (strcmp(propname, TOPO_SENSOR_READING) == 0) { 480 has_reading = B_TRUE; 481 (void) nvlist_lookup_double(propval, 482 TOPO_PROP_VAL_VAL, &reading); 483 } 484 } 485 486 if (in != NULL) 487 is_spoofed = topo_spoof_apply(mod->tm_hdl, node, 488 fp->tf_node, in, &state); 489 490 if (topo_sensor_failed(type, state, &seinfo)) { 491 tmp = NULL; 492 if (topo_mod_nvalloc(mod, &tmp, NV_UNIQUE_NAME) != 0 || 493 nvlist_add_uint32(tmp, TOPO_FACILITY_TYPE, 494 type) != 0 || 495 nvlist_add_uint32(tmp, TOPO_SENSOR_STATE, 496 state) != 0 || 497 nvlist_add_uint32(tmp, TOPO_SENSOR_UNITS, 498 units) != 0 || 499 nvlist_add_boolean_value(tmp, 500 "nonrecov", seinfo.se_nonrecov) != 0 || 501 nvlist_add_boolean_value(tmp, 502 "predictive", seinfo.se_predictive) != 0 || 503 nvlist_add_uint32(tmp, "source", 504 seinfo.se_src) != 0 || 505 nvlist_add_boolean_value(nvl, "injected", 506 is_spoofed) != 0 || 507 (has_reading && nvlist_add_double(tmp, 508 TOPO_SENSOR_READING, reading) != 0) || 509 nvlist_add_nvlist(nvl, topo_node_name(fp->tf_node), 510 tmp) != 0) { 511 nvlist_free(props); 512 nvlist_free(tmp); 513 nvlist_free(nvl); 514 ret = topo_mod_seterrno(mod, 515 ETOPO_METHOD_NOMEM); 516 goto error; 517 } 518 519 nvlist_free(tmp); 520 } 521 522 nvlist_free(props); 523 is_spoofed = B_FALSE; 524 } 525 526 *out = nvl; 527 ret = 0; 528 error: 529 while ((fp = topo_list_next(&faclist.tf_list)) != NULL) { 530 topo_list_delete(&faclist.tf_list, fp); 531 topo_mod_free(mod, fp, sizeof (topo_faclist_t)); 532 } 533 return (ret); 534 } 535