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 /* 27 * Implementation of ri_init routine for obtaining mapping 28 * of system board attachment points to physical devices and to 29 * the Reconfiguration Coordination Manager (RCM) client usage 30 * of these devices. 31 */ 32 #include <string.h> 33 #include <stdlib.h> 34 #include <unistd.h> 35 #include <kstat.h> 36 #include <sys/param.h> 37 #include <sys/sbd_ioctl.h> 38 #include "rsrc_info_impl.h" 39 40 /* 41 * Occupant types exported by cfgadm sbd plugin via 42 * config_admin(3CFGADM). 43 */ 44 #define SBD_CM_CPU "cpu" 45 #define SBD_CM_MEM "memory" 46 #define SBD_CM_IO "io" 47 48 /* 49 * RCM abstract resource names. 50 */ 51 #define RCM_MEM_ALL "SUNW_memory" 52 #define RCM_CPU_ALL "SUNW_cpu" 53 #define RCM_CPU RCM_CPU_ALL"/cpu" 54 55 #define KBYTE 1024 56 #define MBYTE 1048576 57 #define USAGE_ALLOC_SIZE 128 58 59 /* 60 * define to allow io_cm_info to return NODE is NULL to ri_init, 61 * in order to skip over nodes w/unattached drivers 62 */ 63 #define RI_NODE_NIL 1 64 65 /* 66 * This code is CMP aware as it parses the 67 * cfgadm info field for individual cpuids. 68 */ 69 #define CPUID_SEP "," 70 #define CPU_INFO_FMT "cpuid=%s speed=%d ecache=%d" 71 72 typedef struct { 73 cfga_list_data_t *cfga_list_data; 74 int nlist; 75 } apd_t; 76 77 typedef struct { 78 long pagesize; 79 long syspages; 80 long sysmb; 81 } mem_stat_t; 82 83 #define ms_syspages m_stat.syspages 84 #define ms_pagesize m_stat.pagesize 85 #define ms_sysmb m_stat.sysmb 86 87 typedef int32_t cpuid_t; 88 89 typedef struct { 90 int cpuid_max; /* maximum cpuid value */ 91 int ecache_curr; /* cached during tree walk */ 92 int *ecache_sizes; /* indexed by cpuid */ 93 } ecache_info_t; 94 95 typedef struct { 96 rcm_handle_t *hdl; 97 rcm_info_t *offline_query_info; 98 char **rlist; 99 int nrlist; 100 cpuid_t *cpus; 101 int ncpus; 102 int ndevs; 103 uint_t query_pages; 104 mem_stat_t m_stat; 105 ecache_info_t ecache_info; 106 } rcmd_t; 107 108 typedef struct { 109 const char *rsrc; 110 const char *info; 111 } usage_t; 112 113 /* Lookup table entry for matching IO devices to RCM resource usage */ 114 typedef struct { 115 int index; /* index into the table array */ 116 di_node_t node; /* associated devinfo node */ 117 char *name; /* device full path name */ 118 int n_usage; 119 usage_t *usage; 120 } lookup_entry_t; 121 122 typedef struct { 123 int n_entries; 124 int n_slots; 125 lookup_entry_t *table; 126 } lookup_table_t; 127 128 typedef struct { 129 int err; 130 di_node_t node; 131 char *pathbuf; 132 lookup_table_t *table; 133 di_devlink_handle_t linkhd; 134 } devinfo_arg_t; 135 136 static int dyn_ap_ids(char *, cfga_list_data_t **, int *); 137 static int rcm_init(rcmd_t *, apd_t [], int, int); 138 static void rcm_fini(rcmd_t *); 139 static int rcm_query_init(rcmd_t *, apd_t [], int); 140 static int cap_request(ri_hdl_t *, rcmd_t *); 141 static int syscpus(cpuid_t **, int *); 142 static int cpu_cap_request(ri_hdl_t *, rcmd_t *); 143 static int mem_cap_request(ri_hdl_t *, rcmd_t *); 144 static int (*cm_rcm_qpass_func(cfga_type_t))(cfga_list_data_t *, rcmd_t *); 145 static int cpu_rcm_qpass(cfga_list_data_t *, rcmd_t *); 146 static int mem_rcm_qpass(cfga_list_data_t *, rcmd_t *); 147 static int io_rcm_qpass(cfga_list_data_t *, rcmd_t *); 148 static int (*cm_info_func(cfga_type_t))(ri_ap_t *, cfga_list_data_t *, int, 149 rcmd_t *); 150 static int cpu_cm_info(ri_ap_t *, cfga_list_data_t *, int, rcmd_t *); 151 static int i_cpu_cm_info(processorid_t, int, int, ri_ap_t *, rcmd_t *); 152 static int mem_cm_info(ri_ap_t *, cfga_list_data_t *, int, rcmd_t *); 153 static int io_cm_info(ri_ap_t *, cfga_list_data_t *, int, rcmd_t *); 154 static int ident_leaf(di_node_t); 155 static int mk_drv_inst(di_node_t, char [], char *); 156 static int devinfo_node_walk(di_node_t, void *); 157 static int devinfo_minor_walk(di_node_t, di_minor_t, void *); 158 static int devinfo_devlink_walk(di_devlink_t, void *); 159 static int add_rcm_clients(ri_client_t **, rcmd_t *, rcm_info_t *, int, int *); 160 static int rcm_ignore(char *, char *); 161 static int add_query_state(rcmd_t *, ri_client_t *, const char *, const char *); 162 static int state2query(int); 163 static void dev_list_append(ri_dev_t **, ri_dev_t *); 164 static void dev_list_cpu_insert(ri_dev_t **, ri_dev_t *, processorid_t); 165 static rcm_info_tuple_t *tuple_lookup(rcmd_t *, const char *, const char *); 166 static ri_ap_t *ri_ap_alloc(char *, ri_hdl_t *); 167 static ri_dev_t *ri_dev_alloc(void); 168 static ri_dev_t *io_dev_alloc(char *); 169 static ri_client_t *ri_client_alloc(char *, char *); 170 static void apd_tbl_free(apd_t [], int); 171 static char *pstate2str(int); 172 static int ecache_info_init(ecache_info_t *); 173 static int find_cpu_nodes(di_node_t, void *); 174 static int prop_lookup_int(di_node_t, di_prom_handle_t, char *, int **); 175 static int add_lookup_entry(lookup_table_t *, const char *, di_node_t); 176 static int table_compare_names(const void *, const void *); 177 static int table_compare_indices(const void *, const void *); 178 static lookup_entry_t *lookup(lookup_table_t *table, const char *); 179 static int add_usage(lookup_entry_t *, const char *, rcm_info_tuple_t *); 180 static void empty_table(lookup_table_t *); 181 182 #ifdef DEBUG 183 static void dump_apd_tbl(FILE *, apd_t *, int); 184 #endif /* DEBUG */ 185 186 static struct { 187 char *type; 188 int (*cm_info)(ri_ap_t *, cfga_list_data_t *, int, rcmd_t *); 189 int (*cm_rcm_qpass)(cfga_list_data_t *, rcmd_t *); 190 } cm_ctl[] = { 191 {SBD_CM_CPU, cpu_cm_info, cpu_rcm_qpass}, 192 {SBD_CM_MEM, mem_cm_info, mem_rcm_qpass}, 193 {SBD_CM_IO, io_cm_info, io_rcm_qpass} 194 }; 195 196 /* 197 * Table of known info string prefixes for RCM modules that do not 198 * represent actual resource usage, but instead provide name translations 199 * or sequencing within the RCM namespace. Since RCM provides no way to 200 * filter these out, we must maintain this hack. 201 */ 202 static char *rcm_info_filter[] = { 203 "Network interface", /* Network naming module */ 204 NULL 205 }; 206 207 208 /* 209 * Allocate snapshot handle. 210 */ 211 int 212 ri_init(int n_apids, char **ap_ids, int flags, ri_hdl_t **hdlp) 213 { 214 int i, j; 215 ri_hdl_t *ri_hdl; 216 ri_ap_t *ap_hdl; 217 rcmd_t *rcm = NULL; 218 cfga_list_data_t *cfga_ldata; 219 apd_t *apd, *apd_tbl = NULL; 220 int (*cm_info)(ri_ap_t *, cfga_list_data_t *, 221 int, rcmd_t *); 222 int rv = RI_SUCCESS; 223 int cm_info_rv; 224 225 if (n_apids <= 0 || ap_ids == NULL || hdlp == NULL) 226 return (RI_INVAL); 227 228 if (flags & ~RI_REQ_MASK) 229 return (RI_NOTSUP); 230 231 *hdlp = NULL; 232 if ((ri_hdl = calloc(1, sizeof (*ri_hdl))) == NULL || 233 (rcm = calloc(1, sizeof (*rcm))) == NULL || 234 (apd_tbl = calloc(n_apids, sizeof (*apd_tbl))) == NULL) { 235 dprintf((stderr, "calloc: %s\n", strerror(errno))); 236 rv = RI_FAILURE; 237 goto out; 238 } 239 240 /* 241 * Create mapping of boards to components. 242 */ 243 for (i = 0, apd = apd_tbl; i < n_apids; i++, apd++) { 244 if (dyn_ap_ids(ap_ids[i], &apd->cfga_list_data, 245 &apd->nlist) == -1) { 246 rv = RI_INVAL; 247 goto out; 248 } 249 } 250 #ifdef DEBUG 251 dump_apd_tbl(stderr, apd_tbl, n_apids); 252 #endif /* DEBUG */ 253 254 if (rcm_init(rcm, apd_tbl, n_apids, flags) != 0) { 255 rv = RI_FAILURE; 256 goto out; 257 } 258 259 /* 260 * Best effort attempt to read cpu ecache sizes from 261 * OBP/Solaris device trees. These are later looked up 262 * in i_cpu_cm_info(). 263 */ 264 (void) ecache_info_init(&rcm->ecache_info); 265 266 for (i = 0, apd = apd_tbl; i < n_apids; i++, apd++) { 267 if ((ap_hdl = ri_ap_alloc(ap_ids[i], ri_hdl)) == NULL) { 268 rv = RI_FAILURE; 269 goto out; 270 } 271 272 /* 273 * Add component info based on occupant type. Note all 274 * passes through the apd table skip over the first 275 * cfgadm_list_data entry, which is the static system board 276 * attachment point. 277 */ 278 for (j = 1, cfga_ldata = &apd->cfga_list_data[1]; 279 j < apd->nlist; j++, cfga_ldata++) { 280 if (cfga_ldata->ap_o_state != CFGA_STAT_CONFIGURED) { 281 continue; 282 } 283 284 if ((cm_info = 285 cm_info_func(cfga_ldata->ap_type)) != NULL) { 286 cm_info_rv = 287 (*cm_info)(ap_hdl, cfga_ldata, flags, rcm); 288 if (cm_info_rv != 0) { 289 /* 290 * If we cannot obtain info for the ap, 291 * skip it and do not fail the entire 292 * operation. This case occurs when the 293 * driver for a device is not attached: 294 * di_init() returns failed back to 295 * io_cm_info(). 296 */ 297 if (cm_info_rv == RI_NODE_NIL) 298 continue; 299 else { 300 rv = RI_FAILURE; 301 goto out; 302 } 303 } 304 } 305 } 306 } 307 308 if ((flags & RI_INCLUDE_QUERY) && cap_request(ri_hdl, rcm) != 0) 309 rv = RI_FAILURE; 310 311 out: 312 if (apd_tbl != NULL) 313 apd_tbl_free(apd_tbl, n_apids); 314 if (rcm != NULL) 315 rcm_fini(rcm); 316 317 if (rv == RI_SUCCESS) 318 *hdlp = ri_hdl; 319 else 320 ri_fini(ri_hdl); 321 322 return (rv); 323 } 324 325 /* 326 * Map static board attachment point to dynamic attachment points (components). 327 */ 328 static int 329 dyn_ap_ids(char *ap_id, cfga_list_data_t **ap_id_list, int *nlist) 330 { 331 cfga_err_t cfga_err; 332 char *errstr; 333 char *opts = "parsable"; 334 char *listops = "class=sbd"; 335 336 cfga_err = config_list_ext(1, &ap_id, ap_id_list, nlist, 337 opts, listops, &errstr, CFGA_FLAG_LIST_ALL); 338 if (cfga_err != CFGA_OK) { 339 dprintf((stderr, "config_list_ext: %s\n", 340 config_strerror(cfga_err))); 341 return (-1); 342 } 343 344 return (0); 345 } 346 347 /* 348 * Initialize rcm handle, memory stats. Cache query result if necessary. 349 */ 350 static int 351 rcm_init(rcmd_t *rcm, apd_t apd_tbl[], int napds, int flags) 352 { 353 longlong_t ii; 354 int rv = 0; 355 356 rcm->offline_query_info = NULL; 357 rcm->rlist = NULL; 358 rcm->cpus = NULL; 359 360 if (rcm_alloc_handle(NULL, RCM_NOPID, NULL, &rcm->hdl) != RCM_SUCCESS) { 361 dprintf((stderr, "rcm_alloc_handle (errno=%d)\n", errno)); 362 return (-1); 363 } 364 365 if ((rcm->ms_pagesize = sysconf(_SC_PAGE_SIZE)) == -1 || 366 (rcm->ms_syspages = sysconf(_SC_PHYS_PAGES)) == -1) { 367 dprintf((stderr, "sysconf: %s\n", strerror(errno))); 368 return (-1); 369 } 370 ii = (longlong_t)rcm->ms_pagesize * rcm->ms_syspages; 371 rcm->ms_sysmb = (int)((ii+MBYTE-1) / MBYTE); 372 373 if (flags & RI_INCLUDE_QUERY) 374 rv = rcm_query_init(rcm, apd_tbl, napds); 375 376 return (rv); 377 } 378 379 static void 380 rcm_fini(rcmd_t *rcm) 381 { 382 char **cpp; 383 384 assert(rcm != NULL); 385 386 if (rcm->offline_query_info != NULL) 387 rcm_free_info(rcm->offline_query_info); 388 if (rcm->hdl != NULL) 389 rcm_free_handle(rcm->hdl); 390 391 if (rcm->rlist != NULL) { 392 for (cpp = rcm->rlist; *cpp != NULL; cpp++) 393 s_free(*cpp); 394 free(rcm->rlist); 395 } 396 397 s_free(rcm->cpus); 398 free(rcm); 399 } 400 401 #define NODENAME_CMP "cmp" 402 #define NODENAME_SSM "ssm" 403 #define PROP_CPUID "cpuid" 404 #define PROP_DEVICE_TYPE "device-type" 405 #define PROP_ECACHE_SIZE "ecache-size" 406 #define PROP_L2_CACHE_SIZE "l2-cache-size" 407 #define PROP_L3_CACHE_SIZE "l3-cache-size" 408 409 typedef struct { 410 di_node_t root; 411 di_prom_handle_t ph; 412 ecache_info_t *ecache_info; 413 } di_arg_t; 414 415 /* 416 * The ecache sizes for individual cpus are read from the 417 * OBP/Solaris device trees. This info cannot be derived 418 * from the cfgadm_sbd cpu attachment point ecache info, 419 * which may be a sum of multiple cores for CMP. 420 */ 421 static int 422 ecache_info_init(ecache_info_t *ec) 423 { 424 di_arg_t di_arg; 425 di_prom_handle_t ph = DI_PROM_HANDLE_NIL; 426 di_node_t root = DI_NODE_NIL; 427 int cpuid_max, rv = 0; 428 429 assert(ec != NULL && ec->cpuid_max == 0 && ec->ecache_sizes == NULL); 430 431 if ((cpuid_max = sysconf(_SC_CPUID_MAX)) == -1) { 432 dprintf((stderr, "sysconf fail: %s\n", strerror(errno))); 433 rv = -1; 434 goto done; 435 } 436 437 if ((root = di_init("/", DINFOCPYALL)) == DI_NODE_NIL) { 438 dprintf((stderr, "di_init fail: %s\n", strerror(errno))); 439 rv = -1; 440 goto done; 441 } 442 443 if ((ph = di_prom_init()) == DI_PROM_HANDLE_NIL) { 444 dprintf((stderr, "di_prom_init fail: %s\n", strerror(errno))); 445 rv = -1; 446 goto done; 447 } 448 449 if ((ec->ecache_sizes = calloc(cpuid_max + 1, sizeof (int))) == NULL) { 450 dprintf((stderr, "calloc fail: %s\n", strerror(errno))); 451 rv = -1; 452 goto done; 453 } 454 ec->cpuid_max = cpuid_max; 455 456 dprintf((stderr, "cpuid_max is set to %d\n", ec->cpuid_max)); 457 458 di_arg.ph = ph; 459 di_arg.root = root; 460 di_arg.ecache_info = ec; 461 462 if (di_walk_node(root, DI_WALK_CLDFIRST, (void *)&di_arg, 463 find_cpu_nodes) != 0) { 464 dprintf((stderr, "di_walk_node fail: %s\n", strerror(errno))); 465 rv = -1; 466 } 467 468 done: 469 if (root != DI_NODE_NIL) 470 di_fini(root); 471 if (ph != DI_PROM_HANDLE_NIL) 472 di_prom_fini(ph); 473 474 return (rv); 475 } 476 477 /* 478 * Libdevinfo node walk callback for reading ecache size 479 * properties for cpu device nodes. Subtrees not containing 480 * cpu nodes are filtered out. 481 */ 482 static int 483 find_cpu_nodes(di_node_t node, void *arg) 484 { 485 char *name; 486 int *cpuid, *ecache; 487 di_arg_t *di_arg = (di_arg_t *)arg; 488 ecache_info_t *ec = di_arg->ecache_info; 489 di_prom_handle_t ph = di_arg->ph; 490 int walk_child = 0; 491 492 if (node == DI_NODE_NIL) { 493 return (DI_WALK_TERMINATE); 494 } 495 496 if (node == di_arg->root) { 497 return (DI_WALK_CONTINUE); 498 } 499 500 if (di_nodeid(node) == DI_PSEUDO_NODEID) { 501 return (DI_WALK_PRUNECHILD); 502 } 503 504 name = di_node_name(node); 505 if (name != NULL) { 506 /* 507 * CMP nodes will be the parent of cpu nodes. On some platforms, 508 * cpu nodes will be under the ssm node. In either case, 509 * continue searching this subtree. 510 */ 511 if (strncmp(name, NODENAME_SSM, strlen(NODENAME_SSM)) == 0 || 512 strncmp(name, NODENAME_CMP, strlen(NODENAME_CMP)) == 0) { 513 return (DI_WALK_CONTINUE); 514 } 515 } 516 517 dprintf((stderr, "find_cpu_nodes: node=%p, name=%s, binding_name=%s\n", 518 node, di_node_name(node), di_binding_name(node))); 519 520 /* 521 * Ecache size property name differs with processor implementation. 522 * Panther has both L2 and L3, so check for L3 first to differentiate 523 * from Jaguar, which has only L2. 524 */ 525 if (prop_lookup_int(node, ph, PROP_ECACHE_SIZE, &ecache) == 0 || 526 prop_lookup_int(node, ph, PROP_L3_CACHE_SIZE, &ecache) == 0 || 527 prop_lookup_int(node, ph, PROP_L2_CACHE_SIZE, &ecache) == 0) { 528 /* 529 * On some platforms the cache property is in the core 530 * node while the cpuid is in the child cpu node. It may 531 * be needed while processing this node or a child node. 532 */ 533 ec->ecache_curr = *ecache; 534 walk_child = 1; 535 } 536 537 if (prop_lookup_int(node, ph, PROP_CPUID, &cpuid) == 0) { 538 539 assert(ec != NULL && ec->ecache_sizes != NULL && 540 *cpuid <= ec->cpuid_max); 541 542 if (ec->ecache_curr != 0) { 543 ec->ecache_sizes[*cpuid] = ec->ecache_curr; 544 545 } 546 } 547 548 return (walk_child ? DI_WALK_CONTINUE : DI_WALK_PRUNECHILD); 549 } 550 551 /* 552 * Given a di_node_t, call the appropriate int property lookup routine. 553 * Note: This lookup fails if the int property has multiple value entries. 554 */ 555 static int 556 prop_lookup_int(di_node_t node, di_prom_handle_t ph, char *propname, int **ival) 557 { 558 int rv; 559 560 rv = (di_nodeid(node) == DI_PROM_NODEID) ? 561 di_prom_prop_lookup_ints(ph, node, propname, ival) : 562 di_prop_lookup_ints(DDI_DEV_T_ANY, node, propname, ival); 563 564 return (rv == 1 ? 0 : -1); 565 } 566 567 /* 568 * For offline queries, RCM must be given a list of all resources 569 * so modules can have access to the full scope of the operation. 570 * The rcm_get_info calls are made individually in order to map the 571 * returned rcm_info_t's to physical devices. The rcm_request_offline 572 * result is cached so the query state can be looked up as we process 573 * the rcm_get_info calls. This routine also tallies up the amount of 574 * memory going away and creates a list of cpu ids to be used 575 * later for rcm_request_capacity_change. 576 */ 577 static int 578 rcm_query_init(rcmd_t *rcm, apd_t apd_tbl[], int napds) 579 { 580 apd_t *apd; 581 int i, j; 582 cfga_list_data_t *cfga_ldata; 583 int (*cm_rcm_qpass)(cfga_list_data_t *, rcmd_t *); 584 #ifdef DEBUG 585 char **cpp; 586 #endif /* DEBUG */ 587 588 /* 589 * Initial pass to size cpu and resource name arrays needed to 590 * interface with RCM. Attachment point ids for CMP can represent 591 * multiple cpus (and resource names). Instead of parsing the 592 * cfgadm info field here, use the worse case that all component 593 * attachment points are CMP. 594 */ 595 rcm->ndevs = 0; 596 for (i = 0, apd = apd_tbl; i < napds; i++, apd++) { 597 for (j = 1, cfga_ldata = &apd->cfga_list_data[1]; 598 j < apd->nlist; j++, cfga_ldata++) { 599 if (cfga_ldata->ap_o_state != CFGA_STAT_CONFIGURED) { 600 continue; 601 } 602 rcm->ndevs += SBD_MAX_CORES_PER_CMP; 603 } 604 } 605 606 /* account for trailing NULL in rlist */ 607 if (rcm->ndevs > 0 && 608 ((rcm->cpus = calloc(rcm->ndevs, sizeof (cpuid_t))) == NULL || 609 (rcm->rlist = calloc(rcm->ndevs + 1, sizeof (char *))) == NULL)) { 610 dprintf((stderr, "calloc: %s\n", strerror(errno))); 611 return (-1); 612 } 613 614 /* 615 * Second pass to fill in the RCM resource and cpu lists. 616 */ 617 for (i = 0, apd = apd_tbl; i < napds; i++, apd++) { 618 for (j = 1, cfga_ldata = &apd->cfga_list_data[1]; 619 j < apd->nlist; j++, cfga_ldata++) { 620 if (cfga_ldata->ap_o_state != CFGA_STAT_CONFIGURED) { 621 continue; 622 } 623 if ((cm_rcm_qpass = 624 cm_rcm_qpass_func(cfga_ldata->ap_type)) != NULL && 625 (*cm_rcm_qpass)(cfga_ldata, rcm) != 0) { 626 return (-1); 627 } 628 } 629 } 630 631 if (rcm->nrlist == 0) 632 return (0); 633 634 /* 635 * Cache query result. Since we are only interested in the 636 * set of RCM clients processed and not their request status, 637 * the return value is irrelevant. 638 */ 639 (void) rcm_request_offline_list(rcm->hdl, rcm->rlist, 640 RCM_QUERY|RCM_SCOPE, &rcm->offline_query_info); 641 642 #ifdef DEBUG 643 dprintf((stderr, "RCM rlist: nrlist=%d\n", rcm->nrlist)); 644 for (cpp = rcm->rlist, i = 0; *cpp != NULL; cpp++, i++) { 645 dprintf((stderr, "rlist[%d]=%s\n", i, *cpp)); 646 } 647 #endif /* DEBUG */ 648 649 return (0); 650 } 651 652 static int 653 cap_request(ri_hdl_t *ri_hdl, rcmd_t *rcm) 654 { 655 return (((rcm->ncpus > 0 && cpu_cap_request(ri_hdl, rcm) != 0) || 656 (rcm->query_pages > 0 && mem_cap_request(ri_hdl, rcm) != 0)) ? 657 -1 : 0); 658 } 659 660 /* 661 * RCM capacity change request for cpus. 662 */ 663 static int 664 cpu_cap_request(ri_hdl_t *ri_hdl, rcmd_t *rcm) 665 { 666 cpuid_t *syscpuids, *newcpuids; 667 int sysncpus, newncpus; 668 rcm_info_t *rcm_info = NULL; 669 int i, j, k; 670 nvlist_t *nvl; 671 int rv = 0; 672 673 /* get all cpus in the system */ 674 if (syscpus(&syscpuids, &sysncpus) == -1) 675 return (-1); 676 677 newncpus = sysncpus - rcm->ncpus; 678 if ((newcpuids = calloc(newncpus, sizeof (cpuid_t))) == NULL) { 679 dprintf((stderr, "calloc: %s", strerror(errno))); 680 rv = -1; 681 goto out; 682 } 683 684 if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) { 685 dprintf((stderr, "nvlist_alloc fail\n")); 686 rv = -1; 687 goto out; 688 } 689 690 /* 691 * Construct the new cpu list. 692 */ 693 for (i = 0, j = 0; i < sysncpus; i++) { 694 for (k = 0; k < rcm->ncpus; k++) { 695 if (rcm->cpus[k] == syscpuids[i]) { 696 break; 697 } 698 } 699 if (k == rcm->ncpus) { 700 newcpuids[j++] = syscpuids[i]; 701 } 702 } 703 704 if (nvlist_add_int32(nvl, "old_total", sysncpus) != 0 || 705 nvlist_add_int32(nvl, "new_total", newncpus) != 0 || 706 nvlist_add_int32_array(nvl, "old_cpu_list", syscpuids, 707 sysncpus) != 0 || 708 nvlist_add_int32_array(nvl, "new_cpu_list", newcpuids, 709 newncpus) != 0) { 710 dprintf((stderr, "nvlist_add fail\n")); 711 rv = -1; 712 goto out; 713 } 714 715 #ifdef DEBUG 716 dprintf((stderr, "old_total=%d\n", sysncpus)); 717 for (i = 0; i < sysncpus; i++) { 718 dprintf((stderr, "old_cpu_list[%d]=%d\n", i, syscpuids[i])); 719 } 720 dprintf((stderr, "new_total=%d\n", newncpus)); 721 for (i = 0; i < newncpus; i++) { 722 dprintf((stderr, "new_cpu_list[%d]=%d\n", i, newcpuids[i])); 723 } 724 #endif /* DEBUG */ 725 726 (void) rcm_request_capacity_change(rcm->hdl, RCM_CPU_ALL, 727 RCM_QUERY|RCM_SCOPE, nvl, &rcm_info); 728 729 rv = add_rcm_clients(&ri_hdl->cpu_cap_clients, rcm, rcm_info, 0, NULL); 730 731 out: 732 s_free(syscpuids); 733 s_free(newcpuids); 734 nvlist_free(nvl); 735 if (rcm_info != NULL) 736 rcm_free_info(rcm_info); 737 738 return (rv); 739 } 740 741 static int 742 syscpus(cpuid_t **cpuids, int *ncpus) 743 { 744 kstat_t *ksp; 745 kstat_ctl_t *kc; 746 cpuid_t *cp; 747 int i; 748 749 if ((*ncpus = sysconf(_SC_NPROCESSORS_CONF)) == -1) { 750 dprintf((stderr, "sysconf: %s\n", errno)); 751 return (-1); 752 } 753 754 if ((kc = kstat_open()) == NULL) { 755 dprintf((stderr, "kstat_open fail\n")); 756 return (-1); 757 } 758 759 if ((cp = calloc(*ncpus, sizeof (cpuid_t))) == NULL) { 760 dprintf((stderr, "calloc: %s\n", errno)); 761 (void) kstat_close(kc); 762 return (-1); 763 } 764 765 for (i = 0, ksp = kc->kc_chain; ksp != NULL; ksp = ksp->ks_next) { 766 if (strcmp(ksp->ks_module, "cpu_info") == 0) { 767 cp[i++] = ksp->ks_instance; 768 } 769 } 770 771 (void) kstat_close(kc); 772 *cpuids = cp; 773 774 return (0); 775 } 776 777 /* 778 * RCM capacity change request for memory. 779 */ 780 static int 781 mem_cap_request(ri_hdl_t *ri_hdl, rcmd_t *rcm) 782 { 783 nvlist_t *nvl; 784 rcm_info_t *rcm_info = NULL; 785 long newpages; 786 int rv = 0; 787 788 if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) { 789 dprintf((stderr, "nvlist_alloc fail\n")); 790 return (-1); 791 } 792 793 newpages = rcm->ms_syspages - rcm->query_pages; 794 if (nvlist_add_int32(nvl, "page_size", rcm->ms_pagesize) != 0 || 795 nvlist_add_int32(nvl, "old_pages", rcm->ms_syspages) != 0 || 796 nvlist_add_int32(nvl, "new_pages", newpages) != 0) { 797 dprintf((stderr, "nvlist_add fail\n")); 798 nvlist_free(nvl); 799 return (-1); 800 } 801 802 dprintf((stderr, "memory capacity change req: " 803 "page_size=%d, old_pages=%d, new_pages=%d\n", 804 rcm->ms_pagesize, rcm->ms_syspages, newpages)); 805 806 (void) rcm_request_capacity_change(rcm->hdl, RCM_MEM_ALL, 807 RCM_QUERY|RCM_SCOPE, nvl, &rcm_info); 808 809 rv = add_rcm_clients(&ri_hdl->mem_cap_clients, rcm, rcm_info, 0, NULL); 810 811 nvlist_free(nvl); 812 if (rcm_info != NULL) 813 rcm_free_info(rcm_info); 814 815 return (rv); 816 } 817 818 static int 819 (*cm_rcm_qpass_func(cfga_type_t ap_type))(cfga_list_data_t *, rcmd_t *) 820 { 821 int i; 822 823 for (i = 0; i < sizeof (cm_ctl) / sizeof (cm_ctl[0]); i++) { 824 if (strcmp(cm_ctl[i].type, ap_type) == 0) { 825 return (cm_ctl[i].cm_rcm_qpass); 826 } 827 } 828 return (NULL); 829 } 830 831 /* 832 * Save cpu ids and RCM abstract resource names. 833 * Cpu ids will be used for the capacity change request. 834 * Resource names will be used for the offline query. 835 */ 836 static int 837 cpu_rcm_qpass(cfga_list_data_t *cfga_ldata, rcmd_t *rcm) 838 { 839 processorid_t cpuid; 840 char *cpustr, *lasts, *rsrcname, rbuf[32]; 841 char cbuf[CFGA_INFO_LEN]; 842 int speed, ecache; 843 844 assert(sscanf(cfga_ldata->ap_info, CPU_INFO_FMT, &cbuf, &speed, 845 &ecache) == 3); 846 847 for (cpustr = (char *)strtok_r(cbuf, CPUID_SEP, &lasts); 848 cpustr != NULL; 849 cpustr = (char *)strtok_r(NULL, CPUID_SEP, &lasts)) { 850 cpuid = atoi(cpustr); 851 852 (void) snprintf(rbuf, sizeof (rbuf), "%s%d", RCM_CPU, cpuid); 853 if ((rsrcname = strdup(rbuf)) == NULL) { 854 dprintf((stderr, "strdup fail\n")); 855 return (-1); 856 } 857 assert(rcm->nrlist < rcm->ndevs && rcm->ncpus < rcm->ndevs); 858 rcm->rlist[rcm->nrlist++] = rsrcname; 859 rcm->cpus[rcm->ncpus++] = (cpuid_t)cpuid; 860 861 dprintf((stderr, "cpu_cm_info: cpuid=%d, rsrcname=%s", 862 cpuid, rsrcname)); 863 } 864 865 return (0); 866 } 867 868 /* 869 * No RCM resource names for individual memory units, so 870 * just add to offline query page count. 871 */ 872 static int 873 mem_rcm_qpass(cfga_list_data_t *cfga, rcmd_t *rcm) 874 { 875 char *cp; 876 uint_t kbytes; 877 longlong_t ii; 878 879 if ((cp = strstr(cfga->ap_info, "size")) == NULL || 880 sscanf(cp, "size=%u", &kbytes) != 1) { 881 dprintf((stderr, "unknown sbd info format: %s\n", cp)); 882 return (-1); 883 } 884 885 ii = (longlong_t)kbytes * KBYTE; 886 rcm->query_pages += (uint_t)(ii / rcm->ms_pagesize); 887 888 dprintf((stderr, "%s: npages=%u\n", cfga->ap_log_id, 889 (uint_t)(ii / rcm->ms_pagesize))); 890 891 return (0); 892 } 893 894 /* 895 * Add physical I/O bus name to RCM resource list. 896 */ 897 static int 898 io_rcm_qpass(cfga_list_data_t *cfga, rcmd_t *rcm) 899 { 900 char path[MAXPATHLEN]; 901 char buf[MAXPATHLEN]; 902 char *rsrcname; 903 904 if (sscanf(cfga->ap_info, "device=%s", path) != 1) { 905 dprintf((stderr, "unknown sbd info format: %s\n", 906 cfga->ap_info)); 907 return (-1); 908 } 909 910 (void) snprintf(buf, sizeof (buf), "/devices%s", path); 911 if ((rsrcname = strdup(buf)) == NULL) { 912 dprintf((stderr, "strdup fail\n")); 913 return (-1); 914 } 915 916 assert(rcm->nrlist < rcm->ndevs); 917 rcm->rlist[rcm->nrlist++] = rsrcname; 918 919 return (0); 920 } 921 922 static int 923 (*cm_info_func(cfga_type_t ap_type))(ri_ap_t *, cfga_list_data_t *, 924 int, rcmd_t *) 925 { 926 int i; 927 928 for (i = 0; i < sizeof (cm_ctl) / sizeof (cm_ctl[0]); i++) { 929 if (strcmp(cm_ctl[i].type, ap_type) == 0) { 930 return (cm_ctl[i].cm_info); 931 } 932 } 933 return (NULL); 934 } 935 936 /* 937 * Create cpu handle, adding properties exported by sbd plugin and 938 * RCM client usage. 939 */ 940 /* ARGSUSED */ 941 static int 942 cpu_cm_info(ri_ap_t *ap, cfga_list_data_t *cfga, int flags, rcmd_t *rcm) 943 { 944 processorid_t cpuid; 945 int speed, ecache, rv = 0; 946 char buf[CFGA_INFO_LEN], *cpustr, *lasts; 947 948 if (sscanf(cfga->ap_info, CPU_INFO_FMT, &buf, &speed, &ecache) != 3) { 949 dprintf((stderr, "unknown sbd info format: %s\n", 950 cfga->ap_info)); 951 return (-1); 952 } 953 954 /* parse cpuids */ 955 for (cpustr = (char *)strtok_r(buf, CPUID_SEP, &lasts); 956 cpustr != NULL; 957 cpustr = (char *)strtok_r(NULL, CPUID_SEP, &lasts)) { 958 cpuid = atoi(cpustr); 959 if ((rv = i_cpu_cm_info(cpuid, speed, ecache, ap, rcm)) != 0) { 960 break; 961 } 962 } 963 964 return (rv); 965 } 966 967 static int 968 i_cpu_cm_info(processorid_t cpuid, int speed, int ecache_cfga, ri_ap_t *ap, 969 rcmd_t *rcm) 970 { 971 int ecache_mb = 0; 972 int ecache_kb = 0; 973 char *state, buf[32]; 974 processor_info_t cpu_info; 975 ri_dev_t *cpu = NULL; 976 rcm_info_t *rcm_info = NULL; 977 978 /* 979 * Could have been unconfigured in the interim, so cannot 980 * count on processor_info recognizing it. 981 */ 982 state = (processor_info(cpuid, &cpu_info) == 0) ? 983 pstate2str(cpu_info.pi_state) : "unknown"; 984 985 if ((cpu = ri_dev_alloc()) == NULL) { 986 dprintf((stderr, "ri_dev_alloc failed\n")); 987 return (-1); 988 } 989 990 /* 991 * Assume the ecache_info table has the right e-cache size for 992 * this CPU. Use the value found in cfgadm (ecache_cfga) if not. 993 */ 994 if (rcm->ecache_info.ecache_sizes != NULL) { 995 assert(rcm->ecache_info.cpuid_max != 0 && 996 cpuid <= rcm->ecache_info.cpuid_max); 997 ecache_mb = rcm->ecache_info.ecache_sizes[cpuid] / MBYTE; 998 ecache_kb = rcm->ecache_info.ecache_sizes[cpuid] / KBYTE; 999 } 1000 1001 if (ecache_mb == 0) { 1002 ecache_mb = ecache_cfga; 1003 } 1004 1005 dprintf((stderr, "i_cpu_cm_info: cpu(%d) ecache=%d MB\n", 1006 cpuid, ecache)); 1007 1008 if (nvlist_add_int32(cpu->conf_props, RI_CPU_ID, cpuid) != 0 || 1009 nvlist_add_int32(cpu->conf_props, RI_CPU_SPEED, speed) != 0 || 1010 nvlist_add_int32(cpu->conf_props, RI_CPU_ECACHE, ecache_mb) != 0 || 1011 nvlist_add_string(cpu->conf_props, RI_CPU_STATE, state) != 0) { 1012 dprintf((stderr, "nvlist_add fail\n")); 1013 ri_dev_free(cpu); 1014 return (-1); 1015 } 1016 1017 /* 1018 * Report cache size in kilobyte units if available. This info is 1019 * added to support processors with cache sizes that are non-integer 1020 * megabyte multiples. 1021 */ 1022 if (ecache_kb != 0) { 1023 if (nvlist_add_int32(cpu->conf_props, RI_CPU_ECACHE_KBYTE, 1024 ecache_kb) != 0) { 1025 dprintf((stderr, "nvlist_add fail: %s\n", 1026 RI_CPU_ECACHE_KBYTE)); 1027 ri_dev_free(cpu); 1028 return (-1); 1029 } 1030 } 1031 1032 (void) snprintf(buf, sizeof (buf), "%s%d", RCM_CPU, cpuid); 1033 dprintf((stderr, "rcm_get_info(%s)\n", buf)); 1034 if (rcm_get_info(rcm->hdl, buf, RCM_INCLUDE_DEPENDENT, 1035 &rcm_info) != RCM_SUCCESS) { 1036 dprintf((stderr, "rcm_get_info (errno=%d)\n", errno)); 1037 ri_dev_free(cpu); 1038 if (rcm_info != NULL) 1039 rcm_free_info(rcm_info); 1040 return (-1); 1041 } 1042 1043 dev_list_cpu_insert(&ap->cpus, cpu, cpuid); 1044 1045 return (0); 1046 } 1047 1048 /* 1049 * Create memory handle, adding properties exported by sbd plugin. 1050 * No RCM tuples to be saved unless RCM is modified to export names 1051 * for individual memory units. 1052 */ 1053 /* ARGSUSED */ 1054 static int 1055 mem_cm_info(ri_ap_t *ap, cfga_list_data_t *cfga, int flags, rcmd_t *rcm) 1056 { 1057 ri_dev_t *mem; 1058 char *cp; 1059 char *cpval; 1060 int len; 1061 uint64_t base_addr; /* required */ 1062 int32_t size_kb; /* required */ 1063 int32_t perm_kb = 0; /* optional */ 1064 char target[CFGA_AP_LOG_ID_LEN] = ""; /* optional */ 1065 int32_t del_kb = 0; /* optional */ 1066 int32_t rem_kb = 0; /* optional */ 1067 char source[CFGA_AP_LOG_ID_LEN] = ""; /* optional */ 1068 1069 if (sscanf(cfga->ap_info, "address=0x%llx size=%u", &base_addr, 1070 &size_kb) != 2) { 1071 goto err_fmt; 1072 } 1073 1074 if ((cp = strstr(cfga->ap_info, "permanent")) != NULL && 1075 sscanf(cp, "permanent=%u", &perm_kb) != 1) { 1076 goto err_fmt; 1077 } 1078 1079 if ((cp = strstr(cfga->ap_info, "target")) != NULL) { 1080 if ((cpval = strstr(cp, "=")) == NULL) { 1081 goto err_fmt; 1082 } 1083 for (len = 0; cpval[len] != '\0' && cpval[len] != ' '; len++) { 1084 if (len >= CFGA_AP_LOG_ID_LEN) { 1085 goto err_fmt; 1086 } 1087 } 1088 if (sscanf(cp, "target=%s deleted=%u remaining=%u", &target, 1089 &del_kb, &rem_kb) != 3) { 1090 goto err_fmt; 1091 } 1092 } 1093 1094 if ((cp = strstr(cfga->ap_info, "source")) != NULL) { 1095 if ((cpval = strstr(cp, "=")) == NULL) { 1096 goto err_fmt; 1097 } 1098 for (len = 0; cpval[len] != '\0' && cpval[len] != ' '; len++) { 1099 if (len >= CFGA_AP_LOG_ID_LEN) { 1100 goto err_fmt; 1101 } 1102 } 1103 if (sscanf(cp, "source=%s", &source) != 1) { 1104 goto err_fmt; 1105 } 1106 } 1107 1108 dprintf((stderr, "%s: base=0x%llx, size=%u, permanent=%u\n", 1109 cfga->ap_log_id, base_addr, size_kb, perm_kb)); 1110 1111 if ((mem = ri_dev_alloc()) == NULL) 1112 return (-1); 1113 1114 /* 1115 * Convert memory sizes to MB (truncate). 1116 */ 1117 if (nvlist_add_uint64(mem->conf_props, RI_MEM_ADDR, base_addr) != 0 || 1118 nvlist_add_int32(mem->conf_props, RI_MEM_BRD, size_kb/KBYTE) != 0 || 1119 nvlist_add_int32(mem->conf_props, RI_MEM_PERM, 1120 perm_kb/KBYTE) != 0) { 1121 dprintf((stderr, "nvlist_add failure\n")); 1122 ri_dev_free(mem); 1123 return (-1); 1124 } 1125 1126 if (target[0] != '\0' && 1127 (nvlist_add_string(mem->conf_props, RI_MEM_TARG, target) != 0 || 1128 nvlist_add_int32(mem->conf_props, RI_MEM_DEL, del_kb/KBYTE) != 0 || 1129 nvlist_add_int32(mem->conf_props, RI_MEM_REMAIN, 1130 rem_kb/KBYTE) != 0)) { 1131 dprintf((stderr, "nvlist_add failure\n")); 1132 ri_dev_free(mem); 1133 return (-1); 1134 } 1135 1136 if (source[0] != '\0' && 1137 nvlist_add_string(mem->conf_props, RI_MEM_SRC, source) != 0) { 1138 dprintf((stderr, "nvlist_add failure\n")); 1139 ri_dev_free(mem); 1140 return (-1); 1141 } 1142 1143 /* 1144 * XXX - move this property to attachment point hdl? 1145 */ 1146 if (nvlist_add_int32(mem->conf_props, RI_MEM_DOMAIN, 1147 rcm->ms_sysmb) != 0) { 1148 dprintf((stderr, "nvlist_add failure\n")); 1149 ri_dev_free(mem); 1150 return (-1); 1151 } 1152 1153 dev_list_append(&ap->mems, mem); 1154 return (0); 1155 1156 err_fmt: 1157 dprintf((stderr, "unknown sbd info format: %s\n", cfga->ap_info)); 1158 return (-1); 1159 } 1160 1161 /* 1162 * Initiate a libdevinfo walk on the IO bus path. 1163 * XXX - investigate performance using two threads here: one thread to do the 1164 * libdevinfo snapshot and treewalk; and one thread to get RCM usage info 1165 */ 1166 static int 1167 io_cm_info(ri_ap_t *ap, cfga_list_data_t *cfga, int flags, rcmd_t *rcm) 1168 { 1169 int i; 1170 int j; 1171 int k; 1172 int set_size; 1173 int retval = 0; 1174 int n_usage; 1175 devinfo_arg_t di_arg; 1176 lookup_table_t devicetable; 1177 lookup_entry_t *deventry; 1178 lookup_entry_t *lastdeventry; 1179 ri_dev_t *io = NULL; 1180 ri_client_t *client; 1181 ri_client_t *tmp; 1182 di_devlink_handle_t linkhd = NULL; 1183 di_node_t root = DI_NODE_NIL; 1184 di_node_t node = DI_NODE_NIL; 1185 rcm_info_tuple_t *rcm_tuple; 1186 rcm_info_t *rcm_info = NULL; 1187 const char *rcm_rsrc = NULL; 1188 char drv_inst[MAXPATHLEN]; 1189 char path[MAXPATHLEN]; 1190 char pathbuf[MAXPATHLEN]; 1191 1192 dprintf((stderr, "io_cm_info(%s)\n", cfga->ap_log_id)); 1193 1194 /* Extract devfs path from cfgadm information */ 1195 if (sscanf(cfga->ap_info, "device=%s\n", path) != 1) { 1196 dprintf((stderr, "unknown sbd info format: %s\n", 1197 cfga->ap_info)); 1198 return (-1); 1199 } 1200 1201 /* Initialize empty device lookup table */ 1202 devicetable.n_entries = 0; 1203 devicetable.n_slots = 0; 1204 devicetable.table = NULL; 1205 1206 /* Get libdevinfo snapshot */ 1207 dprintf((stderr, "di_init(%s)\n", path)); 1208 if ((root = di_init(path, DINFOCPYALL)) == DI_NODE_NIL) { 1209 dprintf((stderr, "di_init: %s\n", strerror(errno))); 1210 retval = RI_NODE_NIL; /* tell ri_init to skip this node */ 1211 goto end; 1212 } 1213 1214 /* 1215 * Map in devlinks database. 1216 * XXX - This could be moved to ri_init() for better performance. 1217 */ 1218 dprintf((stderr, "di_devlink_init()\n")); 1219 if ((linkhd = di_devlink_init(NULL, 0)) == NULL) { 1220 dprintf((stderr, "di_devlink_init: %s\n", strerror(errno))); 1221 retval = -1; 1222 goto end; 1223 } 1224 1225 /* Initialize argument for devinfo treewalk */ 1226 di_arg.err = 0; 1227 di_arg.node = DI_NODE_NIL; 1228 di_arg.pathbuf = pathbuf; 1229 di_arg.table = &devicetable; 1230 di_arg.linkhd = linkhd; 1231 1232 /* Use libdevinfo treewalk to build device lookup table */ 1233 if (di_walk_node(root, DI_WALK_CLDFIRST, (void *)&di_arg, 1234 devinfo_node_walk) != 0) { 1235 dprintf((stderr, "di_walk_node: %s\n", strerror(errno))); 1236 retval = -1; 1237 goto end; 1238 } 1239 if (di_arg.err != 0) { 1240 dprintf((stderr, "di_walk_node: device tree walk failed\n")); 1241 retval = -1; 1242 goto end; 1243 } 1244 1245 /* Call RCM to gather usage information */ 1246 (void) snprintf(pathbuf, MAXPATHLEN, "/devices%s", path); 1247 dprintf((stderr, "rcm_get_info(%s)\n", pathbuf)); 1248 if (rcm_get_info(rcm->hdl, pathbuf, 1249 RCM_INCLUDE_SUBTREE|RCM_INCLUDE_DEPENDENT, &rcm_info) != 1250 RCM_SUCCESS) { 1251 dprintf((stderr, "rcm_get_info (errno=%d)\n", errno)); 1252 retval = -1; 1253 goto end; 1254 } 1255 1256 /* Sort the device table by name (proper order for lookups) */ 1257 qsort(devicetable.table, devicetable.n_entries, sizeof (lookup_entry_t), 1258 table_compare_names); 1259 1260 /* Perform mappings of RCM usage segments to device table entries */ 1261 lastdeventry = NULL; 1262 rcm_tuple = NULL; 1263 while ((rcm_tuple = rcm_info_next(rcm_info, rcm_tuple)) != NULL) { 1264 if ((rcm_rsrc = rcm_info_rsrc(rcm_tuple)) == NULL) 1265 continue; 1266 if (deventry = lookup(&devicetable, rcm_rsrc)) { 1267 if (add_usage(deventry, rcm_rsrc, rcm_tuple)) { 1268 retval = -1; 1269 goto end; 1270 } 1271 lastdeventry = deventry; 1272 } else { 1273 if (add_usage(lastdeventry, rcm_rsrc, rcm_tuple)) { 1274 retval = -1; 1275 goto end; 1276 } 1277 } 1278 } 1279 1280 /* Re-sort the device table by index number (original treewalk order) */ 1281 qsort(devicetable.table, devicetable.n_entries, sizeof (lookup_entry_t), 1282 table_compare_indices); 1283 1284 /* 1285 * Use the mapped usage and the device table to construct ri_dev_t's. 1286 * Construct one for each set of entries in the device table with 1287 * matching di_node_t's, if: 1) it has mapped RCM usage, or 2) it is 1288 * a leaf node and the caller has requested that unmanaged nodes be 1289 * included in the output. 1290 */ 1291 i = 0; 1292 while (i < devicetable.n_entries) { 1293 1294 node = devicetable.table[i].node; 1295 1296 /* Count how many usage records are mapped to this node's set */ 1297 n_usage = 0; 1298 set_size = 0; 1299 while (((i + set_size) < devicetable.n_entries) && 1300 (devicetable.table[i + set_size].node == node)) { 1301 n_usage += devicetable.table[i + set_size].n_usage; 1302 set_size += 1; 1303 } 1304 1305 /* 1306 * If there's no usage, then the node is unmanaged. Skip this 1307 * set of devicetable entries unless the node is a leaf node 1308 * and the caller has requested information on unmanaged leaves. 1309 */ 1310 if ((n_usage == 0) && 1311 !((flags & RI_INCLUDE_UNMANAGED) && (ident_leaf(node)))) { 1312 i += set_size; 1313 continue; 1314 } 1315 1316 /* 1317 * The checks above determined that this node is going in. 1318 * So determine its driver/instance name and allocate an 1319 * ri_dev_t for this node. 1320 */ 1321 if (mk_drv_inst(node, drv_inst, devicetable.table[i].name)) { 1322 dprintf((stderr, "mk_drv_inst failed\n")); 1323 retval = -1; 1324 break; 1325 } 1326 if ((io = io_dev_alloc(drv_inst)) == NULL) { 1327 dprintf((stderr, "io_dev_alloc failed\n")); 1328 retval = -1; 1329 break; 1330 } 1331 1332 /* Now add all the RCM usage records (if any) to the ri_dev_t */ 1333 for (j = i; j < (i + set_size); j++) { 1334 for (k = 0; k < devicetable.table[j].n_usage; k++) { 1335 /* Create new ri_client_t for basic usage */ 1336 client = ri_client_alloc( 1337 (char *)devicetable.table[j].usage[k].rsrc, 1338 (char *)devicetable.table[j].usage[k].info); 1339 if (client == NULL) { 1340 dprintf((stderr, 1341 "ri_client_alloc failed\n")); 1342 ri_dev_free(io); 1343 retval = -1; 1344 goto end; 1345 } 1346 1347 /* Add extra query usage to the ri_client_t */ 1348 if ((flags & RI_INCLUDE_QUERY) && 1349 (add_query_state(rcm, client, 1350 devicetable.table[j].usage[k].rsrc, 1351 devicetable.table[j].usage[k].info) != 0)) { 1352 dprintf((stderr, 1353 "add_query_state failed\n")); 1354 ri_dev_free(io); 1355 ri_client_free(client); 1356 retval = -1; 1357 goto end; 1358 } 1359 1360 /* Link new ri_client_t to ri_dev_t */ 1361 if (io->rcm_clients) { 1362 tmp = io->rcm_clients; 1363 while (tmp->next) 1364 tmp = tmp->next; 1365 tmp->next = client; 1366 } else { 1367 io->rcm_clients = client; 1368 } 1369 } 1370 } 1371 1372 /* Link the ri_dev_t into the return value */ 1373 dev_list_append(&ap->ios, io); 1374 1375 /* Advance to the next node set */ 1376 i += set_size; 1377 } 1378 1379 end: 1380 if (rcm_info != NULL) 1381 rcm_free_info(rcm_info); 1382 if (linkhd != NULL) 1383 di_devlink_fini(&linkhd); 1384 if (root != DI_NODE_NIL) 1385 di_fini(root); 1386 empty_table(&devicetable); 1387 1388 dprintf((stderr, "io_cm_info: returning %d\n", retval)); 1389 return (retval); 1390 } 1391 1392 static int 1393 ident_leaf(di_node_t node) 1394 { 1395 di_minor_t minor = DI_MINOR_NIL; 1396 1397 return ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL && 1398 di_child_node(node) == DI_NODE_NIL); 1399 } 1400 1401 /* ARGSUSED */ 1402 static int 1403 mk_drv_inst(di_node_t node, char drv_inst[], char *devfs_path) 1404 { 1405 char *drv; 1406 int inst; 1407 1408 if ((drv = di_driver_name(node)) == NULL) { 1409 dprintf((stderr, "no driver bound to %s\n", 1410 devfs_path)); 1411 return (-1); 1412 } 1413 1414 if ((inst = di_instance(node)) == -1) { 1415 dprintf((stderr, "no instance assigned to %s\n", 1416 devfs_path)); 1417 return (-1); 1418 } 1419 (void) snprintf(drv_inst, MAXPATHLEN, "%s%d", drv, inst); 1420 1421 return (0); 1422 } 1423 1424 /* 1425 * Libdevinfo walker. 1426 * 1427 * During the tree walk of the attached IO devices, for each node 1428 * and all of its associated minors, the following actions are performed: 1429 * - The /devices path of the physical device node or minor 1430 * is stored in a lookup table along with a reference to the 1431 * libdevinfo node it represents via add_lookup_entry(). 1432 * - The device links associated with each device are also 1433 * stored in the same lookup table along with a reference to 1434 * the libdevinfo node it represents via the minor walk callback. 1435 * 1436 */ 1437 static int 1438 devinfo_node_walk(di_node_t node, void *arg) 1439 { 1440 char *devfs_path; 1441 #ifdef DEBUG 1442 char *drv; 1443 #endif /* DEBUG */ 1444 devinfo_arg_t *di_arg = (devinfo_arg_t *)arg; 1445 1446 if (node == DI_NODE_NIL) { 1447 return (DI_WALK_TERMINATE); 1448 } 1449 1450 if (((di_state(node) & DI_DRIVER_DETACHED) == 0) && 1451 ((devfs_path = di_devfs_path(node)) != NULL)) { 1452 1453 /* Use the provided path buffer to create full /devices path */ 1454 (void) snprintf(di_arg->pathbuf, MAXPATHLEN, "/devices%s", 1455 devfs_path); 1456 1457 #ifdef DEBUG 1458 dprintf((stderr, "devinfo_node_walk(%s)\n", di_arg->pathbuf)); 1459 if ((drv = di_driver_name(node)) != NULL) 1460 dprintf((stderr, " driver name %s instance %d\n", drv, 1461 di_instance(node))); 1462 #endif 1463 1464 /* Free the devfs_path */ 1465 di_devfs_path_free(devfs_path); 1466 1467 /* Add an entry to the lookup table for this physical device */ 1468 if (add_lookup_entry(di_arg->table, di_arg->pathbuf, node)) { 1469 dprintf((stderr, "add_lookup_entry: %s\n", 1470 strerror(errno))); 1471 di_arg->err = 1; 1472 return (DI_WALK_TERMINATE); 1473 } 1474 1475 /* Check if this node has minors */ 1476 if ((di_minor_next(node, DI_MINOR_NIL)) != DI_MINOR_NIL) { 1477 /* Walk this node's minors */ 1478 di_arg->node = node; 1479 if (di_walk_minor(node, NULL, DI_CHECK_ALIAS, arg, 1480 devinfo_minor_walk) != 0) { 1481 dprintf((stderr, "di_walk_minor: %s\n", 1482 strerror(errno))); 1483 di_arg->err = 1; 1484 return (DI_WALK_TERMINATE); 1485 } 1486 } 1487 } 1488 1489 return (DI_WALK_CONTINUE); 1490 } 1491 1492 /* 1493 * Use di_devlink_walk to find the /dev link from /devices path for this minor 1494 */ 1495 static int 1496 devinfo_minor_walk(di_node_t node, di_minor_t minor, void *arg) 1497 { 1498 char *name; 1499 char *devfs_path; 1500 devinfo_arg_t *di_arg = (devinfo_arg_t *)arg; 1501 char pathbuf[MAXPATHLEN]; 1502 1503 #ifdef DEBUG 1504 dprintf((stderr, "devinfo_minor_walk(%d) %s\n", minor, 1505 di_arg->pathbuf)); 1506 1507 if ((name = di_minor_name(minor)) != NULL) { 1508 dprintf((stderr, " minor name %s\n", name)); 1509 } 1510 #endif /* DEBUG */ 1511 1512 /* Terminate the walk when the device node changes */ 1513 if (node != di_arg->node) { 1514 return (DI_WALK_TERMINATE); 1515 } 1516 1517 /* Construct full /devices path for this minor */ 1518 if ((name = di_minor_name(minor)) == NULL) { 1519 return (DI_WALK_CONTINUE); 1520 } 1521 (void) snprintf(pathbuf, MAXPATHLEN, "%s:%s", di_arg->pathbuf, name); 1522 1523 /* Add lookup entry for this minor node */ 1524 if (add_lookup_entry(di_arg->table, pathbuf, node)) { 1525 dprintf((stderr, "add_lookup_entry: %s\n", strerror(errno))); 1526 di_arg->err = 1; 1527 return (DI_WALK_TERMINATE); 1528 } 1529 1530 /* 1531 * Walk the associated device links. 1532 * Note that di_devlink_walk() doesn't want "/devices" in its paths. 1533 * Also note that di_devlink_walk() will fail if there are no device 1534 * links, which is fine; so ignore if it fails. Only check for 1535 * internal failures during such a walk. 1536 */ 1537 devfs_path = &pathbuf[strlen("/devices")]; 1538 (void) di_devlink_walk(di_arg->linkhd, NULL, devfs_path, 0, arg, 1539 devinfo_devlink_walk); 1540 if (di_arg->err != 0) { 1541 return (DI_WALK_TERMINATE); 1542 } 1543 1544 return (DI_WALK_CONTINUE); 1545 } 1546 1547 static int 1548 devinfo_devlink_walk(di_devlink_t devlink, void *arg) 1549 { 1550 const char *linkpath; 1551 devinfo_arg_t *di_arg = (devinfo_arg_t *)arg; 1552 1553 /* Get the devlink's path */ 1554 if ((linkpath = di_devlink_path(devlink)) == NULL) { 1555 dprintf((stderr, "di_devlink_path: %s\n", strerror(errno))); 1556 di_arg->err = 1; 1557 return (DI_WALK_TERMINATE); 1558 } 1559 dprintf((stderr, "devinfo_devlink_walk: %s\n", linkpath)); 1560 1561 /* Add lookup entry for this devlink */ 1562 if (add_lookup_entry(di_arg->table, linkpath, di_arg->node)) { 1563 dprintf((stderr, "add_lookup_entry: %s\n", strerror(errno))); 1564 di_arg->err = 1; 1565 return (DI_WALK_TERMINATE); 1566 } 1567 1568 return (DI_WALK_CONTINUE); 1569 } 1570 1571 /* 1572 * Map rcm_info_t's to ri_client_t's, filtering out "uninteresting" (hack) 1573 * RCM clients. The number of "interesting" ri_client_t's is returned 1574 * in cnt if passed non-NULL. 1575 */ 1576 static int 1577 add_rcm_clients(ri_client_t **client_list, rcmd_t *rcm, rcm_info_t *info, 1578 int flags, int *cnt) 1579 { 1580 rcm_info_tuple_t *tuple; 1581 char *rsrc, *usage; 1582 ri_client_t *client, *tmp; 1583 1584 assert(client_list != NULL && rcm != NULL); 1585 1586 if (info == NULL) 1587 return (0); 1588 1589 if (cnt != NULL) 1590 *cnt = 0; 1591 1592 tuple = NULL; 1593 while ((tuple = rcm_info_next(info, tuple)) != NULL) { 1594 if ((rsrc = (char *)rcm_info_rsrc(tuple)) == NULL || 1595 (usage = (char *)rcm_info_info(tuple)) == NULL) { 1596 continue; 1597 } 1598 1599 if (rcm_ignore(rsrc, usage) == 0) 1600 continue; 1601 1602 if ((client = ri_client_alloc(rsrc, usage)) == NULL) 1603 return (-1); 1604 1605 if ((flags & RI_INCLUDE_QUERY) && add_query_state(rcm, client, 1606 rsrc, usage) != 0) { 1607 ri_client_free(client); 1608 return (-1); 1609 } 1610 1611 if (cnt != NULL) 1612 ++*cnt; 1613 1614 /* 1615 * Link in 1616 */ 1617 if ((tmp = *client_list) == NULL) { 1618 *client_list = client; 1619 continue; 1620 } 1621 while (tmp->next != NULL) { 1622 tmp = tmp->next; 1623 } 1624 tmp->next = client; 1625 } 1626 1627 return (0); 1628 } 1629 1630 /* 1631 * Currently only filtering out based on known info string prefixes. 1632 */ 1633 /* ARGSUSED */ 1634 static int 1635 rcm_ignore(char *rsrc, char *infostr) 1636 { 1637 char **cpp; 1638 1639 for (cpp = rcm_info_filter; *cpp != NULL; cpp++) { 1640 if (strncmp(infostr, *cpp, strlen(*cpp)) == 0) { 1641 return (0); 1642 } 1643 } 1644 return (-1); 1645 } 1646 1647 /* 1648 * If this tuple was cached in the offline query pass, add the 1649 * query state and error string to the ri_client_t. 1650 */ 1651 static int 1652 add_query_state(rcmd_t *rcm, ri_client_t *client, const char *rsrc, 1653 const char *info) 1654 { 1655 int qstate = RI_QUERY_UNKNOWN; 1656 char *errstr = NULL; 1657 rcm_info_tuple_t *cached_tuple; 1658 1659 if ((cached_tuple = tuple_lookup(rcm, rsrc, info)) != NULL) { 1660 qstate = state2query(rcm_info_state(cached_tuple)); 1661 errstr = (char *)rcm_info_error(cached_tuple); 1662 } 1663 1664 if (nvlist_add_int32(client->usg_props, RI_QUERY_STATE, qstate) != 0 || 1665 (errstr != NULL && nvlist_add_string(client->usg_props, 1666 RI_QUERY_ERR, errstr) != 0)) { 1667 dprintf((stderr, "nvlist_add fail\n")); 1668 return (-1); 1669 } 1670 1671 return (0); 1672 } 1673 1674 static int 1675 state2query(int rcm_state) 1676 { 1677 int query; 1678 1679 switch (rcm_state) { 1680 case RCM_STATE_OFFLINE_QUERY: 1681 case RCM_STATE_SUSPEND_QUERY: 1682 query = RI_QUERY_OK; 1683 break; 1684 case RCM_STATE_OFFLINE_QUERY_FAIL: 1685 case RCM_STATE_SUSPEND_QUERY_FAIL: 1686 query = RI_QUERY_FAIL; 1687 break; 1688 default: 1689 query = RI_QUERY_UNKNOWN; 1690 break; 1691 } 1692 1693 return (query); 1694 } 1695 1696 static void 1697 dev_list_append(ri_dev_t **head, ri_dev_t *dev) 1698 { 1699 ri_dev_t *tmp; 1700 1701 if ((tmp = *head) == NULL) { 1702 *head = dev; 1703 return; 1704 } 1705 while (tmp->next != NULL) { 1706 tmp = tmp->next; 1707 } 1708 tmp->next = dev; 1709 } 1710 1711 /* 1712 * The cpu list is ordered on cpuid since CMP cpuids will not necessarily 1713 * be discovered in sequence. 1714 */ 1715 static void 1716 dev_list_cpu_insert(ri_dev_t **listp, ri_dev_t *dev, processorid_t newid) 1717 { 1718 ri_dev_t *tmp; 1719 int32_t cpuid; 1720 1721 while ((tmp = *listp) != NULL && 1722 nvlist_lookup_int32(tmp->conf_props, RI_CPU_ID, &cpuid) == 0 && 1723 cpuid < newid) { 1724 listp = &tmp->next; 1725 } 1726 1727 dev->next = tmp; 1728 *listp = dev; 1729 } 1730 1731 /* 1732 * Linear lookup. Should convert to hash tab. 1733 */ 1734 static rcm_info_tuple_t * 1735 tuple_lookup(rcmd_t *rcm, const char *krsrc, const char *kinfo) 1736 { 1737 rcm_info_tuple_t *tuple = NULL; 1738 const char *rsrc, *info; 1739 1740 if ((rcm == NULL) || (krsrc == NULL) || (kinfo == NULL)) { 1741 return (NULL); 1742 } 1743 1744 while ((tuple = rcm_info_next(rcm->offline_query_info, 1745 tuple)) != NULL) { 1746 if ((rsrc = rcm_info_rsrc(tuple)) == NULL || 1747 (info = rcm_info_info(tuple)) == NULL) { 1748 continue; 1749 } 1750 1751 if (strcmp(rsrc, krsrc) == 0 && strcmp(info, kinfo) == 0) { 1752 return (tuple); 1753 } 1754 } 1755 return (NULL); 1756 } 1757 1758 /* 1759 * Create and link attachment point handle. 1760 */ 1761 static ri_ap_t * 1762 ri_ap_alloc(char *ap_id, ri_hdl_t *hdl) 1763 { 1764 ri_ap_t *ap, *tmp; 1765 1766 if ((ap = calloc(1, sizeof (*ap))) == NULL) { 1767 dprintf((stderr, "calloc: %s\n", strerror(errno))); 1768 return (NULL); 1769 } 1770 1771 if (nvlist_alloc(&ap->conf_props, NV_UNIQUE_NAME, 0) != 0 || 1772 nvlist_add_string(ap->conf_props, RI_AP_REQ_ID, ap_id) != 0) { 1773 nvlist_free(ap->conf_props); 1774 free(ap); 1775 return (NULL); 1776 } 1777 1778 if ((tmp = hdl->aps) == NULL) { 1779 hdl->aps = ap; 1780 } else { 1781 while (tmp->next != NULL) { 1782 tmp = tmp->next; 1783 } 1784 tmp->next = ap; 1785 } 1786 1787 return (ap); 1788 } 1789 1790 static ri_dev_t * 1791 ri_dev_alloc(void) 1792 { 1793 ri_dev_t *dev; 1794 1795 if ((dev = calloc(1, sizeof (*dev))) == NULL || 1796 nvlist_alloc(&dev->conf_props, NV_UNIQUE_NAME, 0) != 0) { 1797 s_free(dev); 1798 } 1799 return (dev); 1800 } 1801 1802 static ri_dev_t * 1803 io_dev_alloc(char *drv_inst) 1804 { 1805 ri_dev_t *io; 1806 1807 assert(drv_inst != NULL); 1808 1809 if ((io = ri_dev_alloc()) == NULL) 1810 return (NULL); 1811 1812 if (nvlist_add_string(io->conf_props, RI_IO_DRV_INST, 1813 drv_inst) != 0) { 1814 dprintf((stderr, "nvlist_add_string fail\n")); 1815 ri_dev_free(io); 1816 return (NULL); 1817 } 1818 1819 return (io); 1820 } 1821 1822 static ri_client_t * 1823 ri_client_alloc(char *rsrc, char *usage) 1824 { 1825 ri_client_t *client; 1826 1827 assert(rsrc != NULL && usage != NULL); 1828 1829 if ((client = calloc(1, sizeof (*client))) == NULL) { 1830 dprintf((stderr, "calloc: %s\n", strerror(errno))); 1831 return (NULL); 1832 } 1833 1834 if (nvlist_alloc(&client->usg_props, NV_UNIQUE_NAME, 0) != 0) { 1835 dprintf((stderr, "nvlist_alloc fail\n")); 1836 free(client); 1837 return (NULL); 1838 } 1839 1840 if (nvlist_add_string(client->usg_props, RI_CLIENT_RSRC, rsrc) != 0 || 1841 nvlist_add_string(client->usg_props, RI_CLIENT_USAGE, usage) != 0) { 1842 dprintf((stderr, "nvlist_add_string fail\n")); 1843 ri_client_free(client); 1844 return (NULL); 1845 } 1846 1847 return (client); 1848 } 1849 1850 static void 1851 apd_tbl_free(apd_t apd_tbl[], int napds) 1852 { 1853 int i; 1854 apd_t *apd; 1855 1856 for (i = 0, apd = apd_tbl; i < napds; i++, apd++) 1857 s_free(apd->cfga_list_data); 1858 1859 free(apd_tbl); 1860 } 1861 1862 static char * 1863 pstate2str(int pi_state) 1864 { 1865 char *state; 1866 1867 switch (pi_state) { 1868 case P_OFFLINE: 1869 state = PS_OFFLINE; 1870 break; 1871 case P_ONLINE: 1872 state = PS_ONLINE; 1873 break; 1874 case P_FAULTED: 1875 state = PS_FAULTED; 1876 break; 1877 case P_POWEROFF: 1878 state = PS_POWEROFF; 1879 break; 1880 case P_NOINTR: 1881 state = PS_NOINTR; 1882 break; 1883 case P_SPARE: 1884 state = PS_SPARE; 1885 break; 1886 default: 1887 state = "unknown"; 1888 break; 1889 } 1890 1891 return (state); 1892 } 1893 1894 #ifdef DEBUG 1895 static void 1896 dump_apd_tbl(FILE *fp, apd_t *apds, int n_apds) 1897 { 1898 int i, j; 1899 cfga_list_data_t *cfga_ldata; 1900 1901 for (i = 0; i < n_apds; i++, apds++) { 1902 dprintf((stderr, "apd_tbl[%d].nlist=%d\n", i, apds->nlist)); 1903 for (j = 0, cfga_ldata = apds->cfga_list_data; j < apds->nlist; 1904 j++, cfga_ldata++) { 1905 dprintf((fp, 1906 "apd_tbl[%d].cfga_list_data[%d].ap_log_id=%s\n", 1907 i, j, cfga_ldata->ap_log_id)); 1908 } 1909 } 1910 } 1911 #endif /* DEBUG */ 1912 1913 /* 1914 * The lookup table is a simple array that is grown in chunks 1915 * to optimize memory allocation. 1916 * Indices are assigned to each array entry in-order so that 1917 * the original device tree ordering can be discerned at a later time. 1918 * 1919 * add_lookup_entry is called from the libdevinfo tree traversal callbacks: 1920 * 1) devinfo_node_walk - physical device path for each node in 1921 * the devinfo tree via di_walk_node(), lookup entry name is 1922 * /devices/[di_devfs_path] 1923 * 2) devinfo_minor_walk - physical device path plus minor name for 1924 * each minor associated with a node via di_walk_minor(), lookup entry 1925 * name is /devices/[di_devfs_path:di_minor_name] 1926 * 3) devinfo_devlink_walk - for each minor's /dev link from its /devices 1927 * path via di_devlink_walk(), lookup entry name is di_devlink_path() 1928 */ 1929 static int 1930 add_lookup_entry(lookup_table_t *table, const char *name, di_node_t node) 1931 { 1932 size_t size; 1933 lookup_entry_t *new_table; 1934 1935 1936 /* Grow the lookup table by USAGE_ALLOC_SIZE slots if necessary */ 1937 if (table->n_entries == table->n_slots) { 1938 size = (table->n_slots + USAGE_ALLOC_SIZE) * 1939 sizeof (lookup_entry_t); 1940 new_table = (lookup_entry_t *)realloc(table->table, size); 1941 if (new_table == NULL) { 1942 dprintf((stderr, "add_lookup_entry: alloc failed: %s\n", 1943 strerror(errno))); 1944 errno = ENOMEM; 1945 return (-1); 1946 } 1947 table->table = new_table; 1948 table->n_slots += USAGE_ALLOC_SIZE; 1949 } 1950 1951 dprintf((stderr, "add_lookup_entry[%d]:%s\n", table->n_entries, name)); 1952 1953 /* Add this name to the next slot */ 1954 if ((table->table[table->n_entries].name = strdup(name)) == NULL) { 1955 dprintf((stderr, "add_lookup_entry: strdup failed: %s\n", 1956 strerror(errno))); 1957 errno = ENOMEM; 1958 return (-1); 1959 } 1960 table->table[table->n_entries].index = table->n_entries; 1961 table->table[table->n_entries].node = node; 1962 table->table[table->n_entries].n_usage = 0; 1963 table->table[table->n_entries].usage = NULL; 1964 table->n_entries += 1; 1965 1966 return (0); 1967 } 1968 1969 /* 1970 * lookup table entry names are full pathname strings, all start with / 1971 */ 1972 static int 1973 table_compare_names(const void *a, const void *b) 1974 { 1975 lookup_entry_t *entry1 = (lookup_entry_t *)a; 1976 lookup_entry_t *entry2 = (lookup_entry_t *)b; 1977 1978 return (strcmp(entry1->name, entry2->name)); 1979 } 1980 1981 1982 /* 1983 * Compare two indices and return -1 for less, 1 for greater, 0 for equal 1984 */ 1985 static int 1986 table_compare_indices(const void *a, const void *b) 1987 { 1988 lookup_entry_t *entry1 = (lookup_entry_t *)a; 1989 lookup_entry_t *entry2 = (lookup_entry_t *)b; 1990 1991 if (entry1->index < entry2->index) 1992 return (-1); 1993 if (entry1->index > entry2->index) 1994 return (1); 1995 return (0); 1996 } 1997 1998 /* 1999 * Given a RCM resource name, find the matching entry in the IO device table 2000 */ 2001 static lookup_entry_t * 2002 lookup(lookup_table_t *table, const char *rcm_rsrc) 2003 { 2004 lookup_entry_t *entry; 2005 lookup_entry_t lookup_arg; 2006 2007 dprintf((stderr, "lookup:%s\n", rcm_rsrc)); 2008 lookup_arg.name = (char *)rcm_rsrc; 2009 entry = bsearch(&lookup_arg, table->table, table->n_entries, 2010 sizeof (lookup_entry_t), table_compare_names); 2011 2012 #ifdef DEBUG 2013 if (entry != NULL) { 2014 dprintf((stderr, " found entry:%d\n", entry->index)); 2015 } 2016 #endif /* DEBUG */ 2017 return (entry); 2018 } 2019 2020 /* 2021 * Add RCM usage to the given device table entry. 2022 * Returns -1 on realloc failure. 2023 */ 2024 static int 2025 add_usage(lookup_entry_t *entry, const char *rcm_rsrc, rcm_info_tuple_t *tuple) 2026 { 2027 size_t size; 2028 const char *info; 2029 usage_t *new_usage; 2030 2031 if ((entry == NULL) || 2032 ((info = rcm_info_info(tuple)) == NULL)) 2033 return (0); 2034 2035 if (rcm_ignore((char *)rcm_rsrc, (char *)info) == 0) 2036 return (0); 2037 2038 size = (entry->n_usage + 1) * sizeof (usage_t); 2039 new_usage = (usage_t *)realloc(entry->usage, size); 2040 if (new_usage == NULL) { 2041 dprintf((stderr, "add_usage: alloc failed: %s\n", 2042 strerror(errno))); 2043 return (-1); 2044 } 2045 dprintf((stderr, "add_usage: entry %d rsrc: %s info: %s\n", 2046 entry->index, rcm_rsrc, info)); 2047 2048 entry->usage = new_usage; 2049 entry->usage[entry->n_usage].rsrc = rcm_rsrc; 2050 entry->usage[entry->n_usage].info = info; 2051 entry->n_usage += 1; 2052 return (0); 2053 } 2054 2055 static void 2056 empty_table(lookup_table_t *table) 2057 { 2058 int i; 2059 2060 if (table) { 2061 for (i = 0; i < table->n_entries; i++) { 2062 if (table->table[i].name) 2063 free(table->table[i].name); 2064 /* 2065 * Note: the strings pointed to from within 2066 * usage were freed already by rcm_free_info 2067 */ 2068 if (table->table[i].usage) 2069 free(table->table[i].usage); 2070 } 2071 if (table->table) 2072 free(table->table); 2073 table->table = NULL; 2074 table->n_entries = 0; 2075 table->n_slots = 0; 2076 } 2077 } 2078