1*26947304SEvan Yan /* 2*26947304SEvan Yan * CDDL HEADER START 3*26947304SEvan Yan * 4*26947304SEvan Yan * The contents of this file are subject to the terms of the 5*26947304SEvan Yan * Common Development and Distribution License (the "License"). 6*26947304SEvan Yan * You may not use this file except in compliance with the License. 7*26947304SEvan Yan * 8*26947304SEvan Yan * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9*26947304SEvan Yan * or http://www.opensolaris.org/os/licensing. 10*26947304SEvan Yan * See the License for the specific language governing permissions 11*26947304SEvan Yan * and limitations under the License. 12*26947304SEvan Yan * 13*26947304SEvan Yan * When distributing Covered Code, include this CDDL HEADER in each 14*26947304SEvan Yan * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15*26947304SEvan Yan * If applicable, add the following below this CDDL HEADER, with the 16*26947304SEvan Yan * fields enclosed by brackets "[]" replaced with your own identifying 17*26947304SEvan Yan * information: Portions Copyright [yyyy] [name of copyright owner] 18*26947304SEvan Yan * 19*26947304SEvan Yan * CDDL HEADER END 20*26947304SEvan Yan */ 21*26947304SEvan Yan 22*26947304SEvan Yan /* 23*26947304SEvan Yan * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24*26947304SEvan Yan * Use is subject to license terms. 25*26947304SEvan Yan */ 26*26947304SEvan Yan 27*26947304SEvan Yan #include <stdio.h> 28*26947304SEvan Yan #include <stdlib.h> 29*26947304SEvan Yan #include <unistd.h> 30*26947304SEvan Yan #include <errno.h> 31*26947304SEvan Yan #include <string.h> 32*26947304SEvan Yan #include <librcm.h> 33*26947304SEvan Yan #include <libhotplug.h> 34*26947304SEvan Yan #include <libhotplug_impl.h> 35*26947304SEvan Yan #include <sys/sunddi.h> 36*26947304SEvan Yan #include <sys/ddi_hp.h> 37*26947304SEvan Yan #include "hotplugd_impl.h" 38*26947304SEvan Yan 39*26947304SEvan Yan /* 40*26947304SEvan Yan * Define structures for a path-to-usage lookup table. 41*26947304SEvan Yan */ 42*26947304SEvan Yan typedef struct info_entry { 43*26947304SEvan Yan char *rsrc; 44*26947304SEvan Yan char *usage; 45*26947304SEvan Yan struct info_entry *next; 46*26947304SEvan Yan } info_entry_t; 47*26947304SEvan Yan 48*26947304SEvan Yan typedef struct { 49*26947304SEvan Yan char *path; 50*26947304SEvan Yan info_entry_t *entries; 51*26947304SEvan Yan } info_table_t; 52*26947304SEvan Yan 53*26947304SEvan Yan /* 54*26947304SEvan Yan * Define callback argument used when getting resources. 55*26947304SEvan Yan */ 56*26947304SEvan Yan typedef struct { 57*26947304SEvan Yan int error; 58*26947304SEvan Yan int n_rsrcs; 59*26947304SEvan Yan char **rsrcs; 60*26947304SEvan Yan char path[MAXPATHLEN]; 61*26947304SEvan Yan char connection[MAXPATHLEN]; 62*26947304SEvan Yan char dev_path[MAXPATHLEN]; 63*26947304SEvan Yan } resource_cb_arg_t; 64*26947304SEvan Yan 65*26947304SEvan Yan /* 66*26947304SEvan Yan * Define callback argument used when merging info. 67*26947304SEvan Yan */ 68*26947304SEvan Yan typedef struct { 69*26947304SEvan Yan int error; 70*26947304SEvan Yan info_table_t *table; 71*26947304SEvan Yan size_t table_len; 72*26947304SEvan Yan char path[MAXPATHLEN]; 73*26947304SEvan Yan char connection[MAXPATHLEN]; 74*26947304SEvan Yan } merge_cb_arg_t; 75*26947304SEvan Yan 76*26947304SEvan Yan /* 77*26947304SEvan Yan * Local functions. 78*26947304SEvan Yan */ 79*26947304SEvan Yan static int merge_rcm_info(hp_node_t root, rcm_info_t *info); 80*26947304SEvan Yan static int get_rcm_usage(char **rsrcs, rcm_info_t **info_p); 81*26947304SEvan Yan static int build_table(rcm_info_t *info, info_table_t **tablep, 82*26947304SEvan Yan size_t *table_lenp); 83*26947304SEvan Yan static void free_table(info_table_t *table, size_t table_len); 84*26947304SEvan Yan static int resource_callback(hp_node_t node, void *argp); 85*26947304SEvan Yan static int merge_callback(hp_node_t node, void *argp); 86*26947304SEvan Yan static int rsrc2path(const char *rsrc, char *path); 87*26947304SEvan Yan static int compare_info(const void *a, const void *b); 88*26947304SEvan Yan 89*26947304SEvan Yan /* 90*26947304SEvan Yan * copy_usage() 91*26947304SEvan Yan * 92*26947304SEvan Yan * Given an information snapshot, get the corresponding 93*26947304SEvan Yan * RCM usage information and merge it into the snapshot. 94*26947304SEvan Yan */ 95*26947304SEvan Yan int 96*26947304SEvan Yan copy_usage(hp_node_t root) 97*26947304SEvan Yan { 98*26947304SEvan Yan rcm_info_t *info = NULL; 99*26947304SEvan Yan char **rsrcs = NULL; 100*26947304SEvan Yan int rv; 101*26947304SEvan Yan 102*26947304SEvan Yan /* Get resource names */ 103*26947304SEvan Yan if ((rv = rcm_resources(root, &rsrcs)) != 0) { 104*26947304SEvan Yan log_err("Cannot get RCM resources (%s)\n", strerror(rv)); 105*26947304SEvan Yan return (rv); 106*26947304SEvan Yan } 107*26947304SEvan Yan 108*26947304SEvan Yan /* Do nothing if no resources */ 109*26947304SEvan Yan if (rsrcs == NULL) 110*26947304SEvan Yan return (0); 111*26947304SEvan Yan 112*26947304SEvan Yan /* Get RCM usage information */ 113*26947304SEvan Yan if ((rv = get_rcm_usage(rsrcs, &info)) != 0) { 114*26947304SEvan Yan log_err("Cannot get RCM information (%s)\n", strerror(rv)); 115*26947304SEvan Yan free_rcm_resources(rsrcs); 116*26947304SEvan Yan return (rv); 117*26947304SEvan Yan } 118*26947304SEvan Yan 119*26947304SEvan Yan /* Done with resource names */ 120*26947304SEvan Yan free_rcm_resources(rsrcs); 121*26947304SEvan Yan 122*26947304SEvan Yan /* If there is RCM usage information, merge it in */ 123*26947304SEvan Yan if (info != NULL) { 124*26947304SEvan Yan rv = merge_rcm_info(root, info); 125*26947304SEvan Yan rcm_free_info(info); 126*26947304SEvan Yan return (rv); 127*26947304SEvan Yan } 128*26947304SEvan Yan 129*26947304SEvan Yan return (0); 130*26947304SEvan Yan } 131*26947304SEvan Yan 132*26947304SEvan Yan /* 133*26947304SEvan Yan * rcm_resources() 134*26947304SEvan Yan * 135*26947304SEvan Yan * Given the root of a hotplug information snapshot, 136*26947304SEvan Yan * construct a list of RCM compatible resource names. 137*26947304SEvan Yan */ 138*26947304SEvan Yan int 139*26947304SEvan Yan rcm_resources(hp_node_t root, char ***rsrcsp) 140*26947304SEvan Yan { 141*26947304SEvan Yan resource_cb_arg_t arg; 142*26947304SEvan Yan 143*26947304SEvan Yan /* Initialize results */ 144*26947304SEvan Yan *rsrcsp = NULL; 145*26947304SEvan Yan 146*26947304SEvan Yan /* Traverse snapshot to get resources */ 147*26947304SEvan Yan (void) memset(&arg, 0, sizeof (resource_cb_arg_t)); 148*26947304SEvan Yan (void) hp_traverse(root, &arg, resource_callback); 149*26947304SEvan Yan 150*26947304SEvan Yan /* Check for errors */ 151*26947304SEvan Yan if (arg.error != 0) { 152*26947304SEvan Yan free_rcm_resources(arg.rsrcs); 153*26947304SEvan Yan return (arg.error); 154*26947304SEvan Yan } 155*26947304SEvan Yan 156*26947304SEvan Yan /* Success */ 157*26947304SEvan Yan *rsrcsp = arg.rsrcs; 158*26947304SEvan Yan return (0); 159*26947304SEvan Yan } 160*26947304SEvan Yan 161*26947304SEvan Yan /* 162*26947304SEvan Yan * free_rcm_resources() 163*26947304SEvan Yan * 164*26947304SEvan Yan * Free a table of RCM resource names. 165*26947304SEvan Yan */ 166*26947304SEvan Yan void 167*26947304SEvan Yan free_rcm_resources(char **rsrcs) 168*26947304SEvan Yan { 169*26947304SEvan Yan int i; 170*26947304SEvan Yan 171*26947304SEvan Yan if (rsrcs != NULL) { 172*26947304SEvan Yan for (i = 0; rsrcs[i] != NULL; i++) 173*26947304SEvan Yan free(rsrcs[i]); 174*26947304SEvan Yan free(rsrcs); 175*26947304SEvan Yan } 176*26947304SEvan Yan } 177*26947304SEvan Yan 178*26947304SEvan Yan /* 179*26947304SEvan Yan * rcm_offline() 180*26947304SEvan Yan * 181*26947304SEvan Yan * Implement an RCM offline request. 182*26947304SEvan Yan * 183*26947304SEvan Yan * NOTE: errors from RCM will be merged into the snapshot. 184*26947304SEvan Yan */ 185*26947304SEvan Yan int 186*26947304SEvan Yan rcm_offline(char **rsrcs, uint_t flags, hp_node_t root) 187*26947304SEvan Yan { 188*26947304SEvan Yan rcm_handle_t *handle; 189*26947304SEvan Yan rcm_info_t *info = NULL; 190*26947304SEvan Yan uint_t rcm_flags = 0; 191*26947304SEvan Yan int rv = 0; 192*26947304SEvan Yan 193*26947304SEvan Yan dprintf("rcm_offline()\n"); 194*26947304SEvan Yan 195*26947304SEvan Yan /* Set flags */ 196*26947304SEvan Yan if (flags & HPFORCE) 197*26947304SEvan Yan rcm_flags |= RCM_FORCE; 198*26947304SEvan Yan if (flags & HPQUERY) 199*26947304SEvan Yan rcm_flags |= RCM_QUERY; 200*26947304SEvan Yan 201*26947304SEvan Yan /* Allocate RCM handle */ 202*26947304SEvan Yan if (rcm_alloc_handle(NULL, 0, NULL, &handle) != RCM_SUCCESS) { 203*26947304SEvan Yan log_err("Cannot allocate RCM handle (%s)\n", strerror(errno)); 204*26947304SEvan Yan return (EFAULT); 205*26947304SEvan Yan } 206*26947304SEvan Yan 207*26947304SEvan Yan /* Request RCM offline */ 208*26947304SEvan Yan if (rcm_request_offline_list(handle, rsrcs, rcm_flags, 209*26947304SEvan Yan &info) != RCM_SUCCESS) 210*26947304SEvan Yan rv = EBUSY; 211*26947304SEvan Yan 212*26947304SEvan Yan /* RCM handle is no longer needed */ 213*26947304SEvan Yan (void) rcm_free_handle(handle); 214*26947304SEvan Yan 215*26947304SEvan Yan /* 216*26947304SEvan Yan * Check if RCM returned any information tuples. If so, 217*26947304SEvan Yan * then also check if the RCM operation failed, and possibly 218*26947304SEvan Yan * merge the RCM info into the caller's hotplug snapshot. 219*26947304SEvan Yan */ 220*26947304SEvan Yan if (info != NULL) { 221*26947304SEvan Yan if (rv != 0) 222*26947304SEvan Yan (void) merge_rcm_info(root, info); 223*26947304SEvan Yan rcm_free_info(info); 224*26947304SEvan Yan } 225*26947304SEvan Yan 226*26947304SEvan Yan return (rv); 227*26947304SEvan Yan } 228*26947304SEvan Yan 229*26947304SEvan Yan /* 230*26947304SEvan Yan * rcm_online() 231*26947304SEvan Yan * 232*26947304SEvan Yan * Implement an RCM online notification. 233*26947304SEvan Yan */ 234*26947304SEvan Yan void 235*26947304SEvan Yan rcm_online(char **rsrcs) 236*26947304SEvan Yan { 237*26947304SEvan Yan rcm_handle_t *handle; 238*26947304SEvan Yan rcm_info_t *info = NULL; 239*26947304SEvan Yan 240*26947304SEvan Yan dprintf("rcm_online()\n"); 241*26947304SEvan Yan 242*26947304SEvan Yan if (rcm_alloc_handle(NULL, 0, NULL, &handle) != RCM_SUCCESS) { 243*26947304SEvan Yan log_err("Cannot allocate RCM handle (%s)\n", strerror(errno)); 244*26947304SEvan Yan return; 245*26947304SEvan Yan } 246*26947304SEvan Yan 247*26947304SEvan Yan (void) rcm_notify_online_list(handle, rsrcs, 0, &info); 248*26947304SEvan Yan 249*26947304SEvan Yan (void) rcm_free_handle(handle); 250*26947304SEvan Yan 251*26947304SEvan Yan if (info != NULL) 252*26947304SEvan Yan rcm_free_info(info); 253*26947304SEvan Yan } 254*26947304SEvan Yan 255*26947304SEvan Yan /* 256*26947304SEvan Yan * rcm_remove() 257*26947304SEvan Yan * 258*26947304SEvan Yan * Implement an RCM remove notification. 259*26947304SEvan Yan */ 260*26947304SEvan Yan void 261*26947304SEvan Yan rcm_remove(char **rsrcs) 262*26947304SEvan Yan { 263*26947304SEvan Yan rcm_handle_t *handle; 264*26947304SEvan Yan rcm_info_t *info = NULL; 265*26947304SEvan Yan 266*26947304SEvan Yan dprintf("rcm_remove()\n"); 267*26947304SEvan Yan 268*26947304SEvan Yan if (rcm_alloc_handle(NULL, 0, NULL, &handle) != RCM_SUCCESS) { 269*26947304SEvan Yan log_err("Cannot allocate RCM handle (%s)\n", strerror(errno)); 270*26947304SEvan Yan return; 271*26947304SEvan Yan } 272*26947304SEvan Yan 273*26947304SEvan Yan (void) rcm_notify_remove_list(handle, rsrcs, 0, &info); 274*26947304SEvan Yan 275*26947304SEvan Yan (void) rcm_free_handle(handle); 276*26947304SEvan Yan 277*26947304SEvan Yan if (info != NULL) 278*26947304SEvan Yan rcm_free_info(info); 279*26947304SEvan Yan } 280*26947304SEvan Yan 281*26947304SEvan Yan /* 282*26947304SEvan Yan * get_rcm_usage() 283*26947304SEvan Yan * 284*26947304SEvan Yan * Lookup usage information for a set of resources from RCM. 285*26947304SEvan Yan */ 286*26947304SEvan Yan static int 287*26947304SEvan Yan get_rcm_usage(char **rsrcs, rcm_info_t **info_p) 288*26947304SEvan Yan { 289*26947304SEvan Yan rcm_handle_t *handle; 290*26947304SEvan Yan rcm_info_t *info = NULL; 291*26947304SEvan Yan int rv = 0; 292*26947304SEvan Yan 293*26947304SEvan Yan /* No-op if no RCM resources */ 294*26947304SEvan Yan if (rsrcs == NULL) 295*26947304SEvan Yan return (0); 296*26947304SEvan Yan 297*26947304SEvan Yan /* Allocate RCM handle */ 298*26947304SEvan Yan if (rcm_alloc_handle(NULL, RCM_NOPID, NULL, &handle) != RCM_SUCCESS) { 299*26947304SEvan Yan log_err("Cannot allocate RCM handle (%s)\n", strerror(errno)); 300*26947304SEvan Yan return (EFAULT); 301*26947304SEvan Yan } 302*26947304SEvan Yan 303*26947304SEvan Yan /* Get usage information from RCM */ 304*26947304SEvan Yan if (rcm_get_info_list(handle, rsrcs, 305*26947304SEvan Yan RCM_INCLUDE_DEPENDENT | RCM_INCLUDE_SUBTREE, 306*26947304SEvan Yan &info) != RCM_SUCCESS) { 307*26947304SEvan Yan log_err("Failed to get RCM information (%s)\n", 308*26947304SEvan Yan strerror(errno)); 309*26947304SEvan Yan rv = EFAULT; 310*26947304SEvan Yan } 311*26947304SEvan Yan 312*26947304SEvan Yan /* RCM handle is no longer needed */ 313*26947304SEvan Yan (void) rcm_free_handle(handle); 314*26947304SEvan Yan 315*26947304SEvan Yan *info_p = info; 316*26947304SEvan Yan return (rv); 317*26947304SEvan Yan } 318*26947304SEvan Yan 319*26947304SEvan Yan /* 320*26947304SEvan Yan * merge_rcm_info() 321*26947304SEvan Yan * 322*26947304SEvan Yan * Merge RCM information into a hotplug information snapshot. 323*26947304SEvan Yan * First a lookup table is built to map lists of RCM usage to 324*26947304SEvan Yan * pathnames. Then during a full traversal of the snapshot, 325*26947304SEvan Yan * the lookup table is used for each node to find matching 326*26947304SEvan Yan * RCM info tuples for each path in the snapshot. 327*26947304SEvan Yan */ 328*26947304SEvan Yan static int 329*26947304SEvan Yan merge_rcm_info(hp_node_t root, rcm_info_t *info) 330*26947304SEvan Yan { 331*26947304SEvan Yan merge_cb_arg_t arg; 332*26947304SEvan Yan info_table_t *table; 333*26947304SEvan Yan size_t table_len; 334*26947304SEvan Yan int rv; 335*26947304SEvan Yan 336*26947304SEvan Yan /* Build a lookup table, mapping paths to usage information */ 337*26947304SEvan Yan if ((rv = build_table(info, &table, &table_len)) != 0) { 338*26947304SEvan Yan log_err("Cannot build RCM lookup table (%s)\n", strerror(rv)); 339*26947304SEvan Yan return (rv); 340*26947304SEvan Yan } 341*26947304SEvan Yan 342*26947304SEvan Yan /* Stop if no valid entries were inserted in table */ 343*26947304SEvan Yan if ((table == NULL) || (table_len == 0)) { 344*26947304SEvan Yan log_err("Unable to gather RCM usage.\n"); 345*26947304SEvan Yan return (0); 346*26947304SEvan Yan } 347*26947304SEvan Yan 348*26947304SEvan Yan /* Initialize callback argument */ 349*26947304SEvan Yan (void) memset(&arg, 0, sizeof (merge_cb_arg_t)); 350*26947304SEvan Yan arg.table = table; 351*26947304SEvan Yan arg.table_len = table_len; 352*26947304SEvan Yan 353*26947304SEvan Yan /* Perform a merge traversal */ 354*26947304SEvan Yan (void) hp_traverse(root, (void *)&arg, merge_callback); 355*26947304SEvan Yan 356*26947304SEvan Yan /* Done with the table */ 357*26947304SEvan Yan free_table(table, table_len); 358*26947304SEvan Yan 359*26947304SEvan Yan /* Check for errors */ 360*26947304SEvan Yan if (arg.error != 0) { 361*26947304SEvan Yan log_err("Cannot merge RCM information (%s)\n", strerror(rv)); 362*26947304SEvan Yan return (rv); 363*26947304SEvan Yan } 364*26947304SEvan Yan 365*26947304SEvan Yan return (0); 366*26947304SEvan Yan } 367*26947304SEvan Yan 368*26947304SEvan Yan /* 369*26947304SEvan Yan * resource_callback() 370*26947304SEvan Yan * 371*26947304SEvan Yan * A callback function for hp_traverse() that builds an RCM 372*26947304SEvan Yan * compatible list of resource path names. The array has 373*26947304SEvan Yan * been pre-allocated based on results from the related 374*26947304SEvan Yan * callback resource_count_callback(). 375*26947304SEvan Yan */ 376*26947304SEvan Yan static int 377*26947304SEvan Yan resource_callback(hp_node_t node, void *argp) 378*26947304SEvan Yan { 379*26947304SEvan Yan resource_cb_arg_t *arg = (resource_cb_arg_t *)argp; 380*26947304SEvan Yan char **new_rsrcs; 381*26947304SEvan Yan size_t new_size; 382*26947304SEvan Yan int type; 383*26947304SEvan Yan 384*26947304SEvan Yan /* Get node type */ 385*26947304SEvan Yan type = hp_type(node); 386*26947304SEvan Yan 387*26947304SEvan Yan /* Prune OFFLINE ports */ 388*26947304SEvan Yan if ((type == HP_NODE_PORT) && HP_IS_OFFLINE(hp_state(node))) 389*26947304SEvan Yan return (HP_WALK_PRUNECHILD); 390*26947304SEvan Yan 391*26947304SEvan Yan /* Skip past non-devices */ 392*26947304SEvan Yan if (type != HP_NODE_DEVICE) 393*26947304SEvan Yan return (HP_WALK_CONTINUE); 394*26947304SEvan Yan 395*26947304SEvan Yan /* Lookup resource path */ 396*26947304SEvan Yan if (hp_path(node, arg->path, arg->connection) != 0) { 397*26947304SEvan Yan log_err("Cannot get RCM resource path.\n"); 398*26947304SEvan Yan arg->error = EFAULT; 399*26947304SEvan Yan return (HP_WALK_TERMINATE); 400*26947304SEvan Yan } 401*26947304SEvan Yan 402*26947304SEvan Yan /* Insert "/devices" to path name */ 403*26947304SEvan Yan (void) snprintf(arg->dev_path, MAXPATHLEN, "/devices%s", arg->path); 404*26947304SEvan Yan 405*26947304SEvan Yan /* 406*26947304SEvan Yan * Grow resource array to accomodate new /devices path. 407*26947304SEvan Yan * NOTE: include an extra NULL pointer at end of array. 408*26947304SEvan Yan */ 409*26947304SEvan Yan new_size = (arg->n_rsrcs + 2) * sizeof (char *); 410*26947304SEvan Yan if (arg->rsrcs == NULL) 411*26947304SEvan Yan new_rsrcs = (char **)malloc(new_size); 412*26947304SEvan Yan else 413*26947304SEvan Yan new_rsrcs = (char **)realloc(arg->rsrcs, new_size); 414*26947304SEvan Yan if (new_rsrcs != NULL) { 415*26947304SEvan Yan arg->rsrcs = new_rsrcs; 416*26947304SEvan Yan } else { 417*26947304SEvan Yan log_err("Cannot allocate RCM resource array.\n"); 418*26947304SEvan Yan arg->error = ENOMEM; 419*26947304SEvan Yan return (HP_WALK_TERMINATE); 420*26947304SEvan Yan } 421*26947304SEvan Yan 422*26947304SEvan Yan /* Initialize new entries */ 423*26947304SEvan Yan arg->rsrcs[arg->n_rsrcs] = strdup(arg->dev_path); 424*26947304SEvan Yan arg->rsrcs[arg->n_rsrcs + 1] = NULL; 425*26947304SEvan Yan 426*26947304SEvan Yan /* Check for errors */ 427*26947304SEvan Yan if (arg->rsrcs[arg->n_rsrcs] == NULL) { 428*26947304SEvan Yan log_err("Cannot allocate RCM resource path.\n"); 429*26947304SEvan Yan arg->error = ENOMEM; 430*26947304SEvan Yan return (HP_WALK_TERMINATE); 431*26947304SEvan Yan } 432*26947304SEvan Yan 433*26947304SEvan Yan /* Increment resource count */ 434*26947304SEvan Yan arg->n_rsrcs += 1; 435*26947304SEvan Yan 436*26947304SEvan Yan /* Do not visit children */ 437*26947304SEvan Yan return (HP_WALK_PRUNECHILD); 438*26947304SEvan Yan } 439*26947304SEvan Yan 440*26947304SEvan Yan /* 441*26947304SEvan Yan * merge_callback() 442*26947304SEvan Yan * 443*26947304SEvan Yan * A callback function for hp_traverse() that merges RCM information 444*26947304SEvan Yan * tuples into an existing hotplug information snapshot. The RCM 445*26947304SEvan Yan * information will be turned into HP_NODE_USAGE nodes. 446*26947304SEvan Yan */ 447*26947304SEvan Yan static int 448*26947304SEvan Yan merge_callback(hp_node_t node, void *argp) 449*26947304SEvan Yan { 450*26947304SEvan Yan merge_cb_arg_t *arg = (merge_cb_arg_t *)argp; 451*26947304SEvan Yan hp_node_t usage; 452*26947304SEvan Yan info_table_t lookup; 453*26947304SEvan Yan info_table_t *slot; 454*26947304SEvan Yan info_entry_t *entry; 455*26947304SEvan Yan int rv; 456*26947304SEvan Yan 457*26947304SEvan Yan /* Only process device nodes (other nodes cannot have usage) */ 458*26947304SEvan Yan if (hp_type(node) != HP_NODE_DEVICE) 459*26947304SEvan Yan return (HP_WALK_CONTINUE); 460*26947304SEvan Yan 461*26947304SEvan Yan /* Get path of current node, using buffer provided in 'arg' */ 462*26947304SEvan Yan if ((rv = hp_path(node, arg->path, arg->connection)) != 0) { 463*26947304SEvan Yan log_err("Cannot lookup hotplug path (%s)\n", strerror(rv)); 464*26947304SEvan Yan arg->error = rv; 465*26947304SEvan Yan return (HP_WALK_TERMINATE); 466*26947304SEvan Yan } 467*26947304SEvan Yan 468*26947304SEvan Yan /* Check the lookup table for associated usage */ 469*26947304SEvan Yan lookup.path = arg->path; 470*26947304SEvan Yan if ((slot = bsearch(&lookup, arg->table, arg->table_len, 471*26947304SEvan Yan sizeof (info_table_t), compare_info)) == NULL) 472*26947304SEvan Yan return (HP_WALK_CONTINUE); 473*26947304SEvan Yan 474*26947304SEvan Yan /* Usage information was found. Append HP_NODE_USAGE nodes. */ 475*26947304SEvan Yan for (entry = slot->entries; entry != NULL; entry = entry->next) { 476*26947304SEvan Yan 477*26947304SEvan Yan /* Allocate a new usage node */ 478*26947304SEvan Yan usage = (hp_node_t)calloc(1, sizeof (struct hp_node)); 479*26947304SEvan Yan if (usage == NULL) { 480*26947304SEvan Yan log_err("Cannot allocate hotplug usage node.\n"); 481*26947304SEvan Yan arg->error = ENOMEM; 482*26947304SEvan Yan return (HP_WALK_TERMINATE); 483*26947304SEvan Yan } 484*26947304SEvan Yan 485*26947304SEvan Yan /* Initialize the usage node's contents */ 486*26947304SEvan Yan usage->hp_type = HP_NODE_USAGE; 487*26947304SEvan Yan if ((usage->hp_name = strdup(entry->rsrc)) == NULL) { 488*26947304SEvan Yan log_err("Cannot allocate hotplug usage node name.\n"); 489*26947304SEvan Yan free(usage); 490*26947304SEvan Yan arg->error = ENOMEM; 491*26947304SEvan Yan return (HP_WALK_TERMINATE); 492*26947304SEvan Yan } 493*26947304SEvan Yan if ((usage->hp_usage = strdup(entry->usage)) == NULL) { 494*26947304SEvan Yan log_err("Cannot allocate hotplug usage node info.\n"); 495*26947304SEvan Yan free(usage->hp_name); 496*26947304SEvan Yan free(usage); 497*26947304SEvan Yan arg->error = ENOMEM; 498*26947304SEvan Yan return (HP_WALK_TERMINATE); 499*26947304SEvan Yan } 500*26947304SEvan Yan 501*26947304SEvan Yan /* Link the usage node as a child of the device node */ 502*26947304SEvan Yan usage->hp_parent = node; 503*26947304SEvan Yan usage->hp_sibling = node->hp_child; 504*26947304SEvan Yan node->hp_child = usage; 505*26947304SEvan Yan } 506*26947304SEvan Yan 507*26947304SEvan Yan return (HP_WALK_CONTINUE); 508*26947304SEvan Yan } 509*26947304SEvan Yan 510*26947304SEvan Yan /* 511*26947304SEvan Yan * build_table() 512*26947304SEvan Yan * 513*26947304SEvan Yan * Build a lookup table that will be used to map paths to their 514*26947304SEvan Yan * corresponding RCM information tuples. 515*26947304SEvan Yan */ 516*26947304SEvan Yan static int 517*26947304SEvan Yan build_table(rcm_info_t *info, info_table_t **tablep, size_t *table_lenp) 518*26947304SEvan Yan { 519*26947304SEvan Yan rcm_info_tuple_t *tuple; 520*26947304SEvan Yan info_entry_t *entry; 521*26947304SEvan Yan info_table_t *slot; 522*26947304SEvan Yan info_table_t *table; 523*26947304SEvan Yan size_t table_len; 524*26947304SEvan Yan const char *rsrc; 525*26947304SEvan Yan const char *usage; 526*26947304SEvan Yan char path[MAXPATHLEN]; 527*26947304SEvan Yan 528*26947304SEvan Yan /* Initialize results */ 529*26947304SEvan Yan *tablep = NULL; 530*26947304SEvan Yan *table_lenp = 0; 531*26947304SEvan Yan 532*26947304SEvan Yan /* Count the RCM info tuples to determine the table's size */ 533*26947304SEvan Yan table_len = 0; 534*26947304SEvan Yan for (tuple = NULL; (tuple = rcm_info_next(info, tuple)) != NULL; ) 535*26947304SEvan Yan table_len++; 536*26947304SEvan Yan 537*26947304SEvan Yan /* If the table would be empty, then do nothing */ 538*26947304SEvan Yan if (table_len == 0) 539*26947304SEvan Yan return (ENOENT); 540*26947304SEvan Yan 541*26947304SEvan Yan /* Allocate the lookup table */ 542*26947304SEvan Yan table = (info_table_t *)calloc(table_len, sizeof (info_table_t)); 543*26947304SEvan Yan if (table == NULL) 544*26947304SEvan Yan return (ENOMEM); 545*26947304SEvan Yan 546*26947304SEvan Yan /* 547*26947304SEvan Yan * Fill in the lookup table. Fill one slot in the table 548*26947304SEvan Yan * for each device path that has a set of associated RCM 549*26947304SEvan Yan * information tuples. In some cases multiple tuples will 550*26947304SEvan Yan * be joined together within the same slot. 551*26947304SEvan Yan */ 552*26947304SEvan Yan slot = NULL; 553*26947304SEvan Yan table_len = 0; 554*26947304SEvan Yan for (tuple = NULL; (tuple = rcm_info_next(info, tuple)) != NULL; ) { 555*26947304SEvan Yan 556*26947304SEvan Yan /* 557*26947304SEvan Yan * Extract RCM resource name and usage description. 558*26947304SEvan Yan * 559*26947304SEvan Yan * NOTE: skip invalid tuples to return as much as possible. 560*26947304SEvan Yan */ 561*26947304SEvan Yan if (((rsrc = rcm_info_rsrc(tuple)) == NULL) || 562*26947304SEvan Yan ((usage = rcm_info_info(tuple)) == NULL)) { 563*26947304SEvan Yan log_err("RCM returned invalid resource or usage.\n"); 564*26947304SEvan Yan continue; 565*26947304SEvan Yan } 566*26947304SEvan Yan 567*26947304SEvan Yan /* 568*26947304SEvan Yan * Try to convert the RCM resource name to a hotplug path. 569*26947304SEvan Yan * If conversion succeeds and this path differs from the 570*26947304SEvan Yan * current slot in the table, then initialize the next 571*26947304SEvan Yan * slot in the table. 572*26947304SEvan Yan */ 573*26947304SEvan Yan if ((rsrc2path(rsrc, path) == 0) && 574*26947304SEvan Yan ((slot == NULL) || (strcmp(slot->path, path) != 0))) { 575*26947304SEvan Yan slot = &table[table_len]; 576*26947304SEvan Yan if ((slot->path = strdup(path)) == NULL) { 577*26947304SEvan Yan log_err("Cannot build info table slot.\n"); 578*26947304SEvan Yan free_table(table, table_len); 579*26947304SEvan Yan return (ENOMEM); 580*26947304SEvan Yan } 581*26947304SEvan Yan table_len++; 582*26947304SEvan Yan } 583*26947304SEvan Yan 584*26947304SEvan Yan /* Append current usage to entry list in the current slot */ 585*26947304SEvan Yan if (slot != NULL) { 586*26947304SEvan Yan 587*26947304SEvan Yan /* Allocate new entry */ 588*26947304SEvan Yan entry = (info_entry_t *)malloc(sizeof (info_entry_t)); 589*26947304SEvan Yan if (entry == NULL) { 590*26947304SEvan Yan log_err("Cannot allocate info table entry.\n"); 591*26947304SEvan Yan free_table(table, table_len); 592*26947304SEvan Yan return (ENOMEM); 593*26947304SEvan Yan } 594*26947304SEvan Yan 595*26947304SEvan Yan /* Link entry into current slot list */ 596*26947304SEvan Yan entry->next = slot->entries; 597*26947304SEvan Yan slot->entries = entry; 598*26947304SEvan Yan 599*26947304SEvan Yan /* Initialize entry values */ 600*26947304SEvan Yan if (((entry->rsrc = strdup(rsrc)) == NULL) || 601*26947304SEvan Yan ((entry->usage = strdup(usage)) == NULL)) { 602*26947304SEvan Yan log_err("Cannot build info table entry.\n"); 603*26947304SEvan Yan free_table(table, table_len); 604*26947304SEvan Yan return (ENOMEM); 605*26947304SEvan Yan } 606*26947304SEvan Yan } 607*26947304SEvan Yan } 608*26947304SEvan Yan 609*26947304SEvan Yan /* Check if valid entries were inserted in table */ 610*26947304SEvan Yan if (table_len == 0) { 611*26947304SEvan Yan free(table); 612*26947304SEvan Yan return (0); 613*26947304SEvan Yan } 614*26947304SEvan Yan 615*26947304SEvan Yan /* Sort the lookup table by hotplug path */ 616*26947304SEvan Yan qsort(table, table_len, sizeof (info_table_t), compare_info); 617*26947304SEvan Yan 618*26947304SEvan Yan /* Done */ 619*26947304SEvan Yan *tablep = table; 620*26947304SEvan Yan *table_lenp = table_len; 621*26947304SEvan Yan return (0); 622*26947304SEvan Yan } 623*26947304SEvan Yan 624*26947304SEvan Yan /* 625*26947304SEvan Yan * free_table() 626*26947304SEvan Yan * 627*26947304SEvan Yan * Destroy a lookup table. 628*26947304SEvan Yan */ 629*26947304SEvan Yan static void 630*26947304SEvan Yan free_table(info_table_t *table, size_t table_len) 631*26947304SEvan Yan { 632*26947304SEvan Yan info_entry_t *entry; 633*26947304SEvan Yan int index; 634*26947304SEvan Yan 635*26947304SEvan Yan if (table != NULL) { 636*26947304SEvan Yan for (index = 0; index < table_len; index++) { 637*26947304SEvan Yan if (table[index].path != NULL) 638*26947304SEvan Yan free(table[index].path); 639*26947304SEvan Yan while (table[index].entries != NULL) { 640*26947304SEvan Yan entry = table[index].entries; 641*26947304SEvan Yan table[index].entries = entry->next; 642*26947304SEvan Yan if (entry->rsrc != NULL) 643*26947304SEvan Yan free(entry->rsrc); 644*26947304SEvan Yan if (entry->usage != NULL) 645*26947304SEvan Yan free(entry->usage); 646*26947304SEvan Yan free(entry); 647*26947304SEvan Yan } 648*26947304SEvan Yan } 649*26947304SEvan Yan free(table); 650*26947304SEvan Yan } 651*26947304SEvan Yan } 652*26947304SEvan Yan 653*26947304SEvan Yan /* 654*26947304SEvan Yan * rsrc2path() 655*26947304SEvan Yan * 656*26947304SEvan Yan * Convert from an RCM resource name to a hotplug device path. 657*26947304SEvan Yan */ 658*26947304SEvan Yan static int 659*26947304SEvan Yan rsrc2path(const char *rsrc, char *path) 660*26947304SEvan Yan { 661*26947304SEvan Yan char *s; 662*26947304SEvan Yan char tmp[MAXPATHLEN]; 663*26947304SEvan Yan 664*26947304SEvan Yan /* Only convert /dev and /devices paths */ 665*26947304SEvan Yan if (strncmp(rsrc, "/dev", 4) == 0) { 666*26947304SEvan Yan 667*26947304SEvan Yan /* Follow symbolic links for /dev paths */ 668*26947304SEvan Yan if (realpath(rsrc, tmp) == NULL) { 669*26947304SEvan Yan log_err("Cannot resolve RCM resource (%s)\n", 670*26947304SEvan Yan strerror(errno)); 671*26947304SEvan Yan return (-1); 672*26947304SEvan Yan } 673*26947304SEvan Yan 674*26947304SEvan Yan /* Remove the leading "/devices" part */ 675*26947304SEvan Yan (void) strlcpy(path, &tmp[strlen(S_DEVICES)], MAXPATHLEN); 676*26947304SEvan Yan 677*26947304SEvan Yan /* Remove any trailing minor node part */ 678*26947304SEvan Yan if ((s = strrchr(path, ':')) != NULL) 679*26947304SEvan Yan *s = '\0'; 680*26947304SEvan Yan 681*26947304SEvan Yan /* Successfully converted */ 682*26947304SEvan Yan return (0); 683*26947304SEvan Yan } 684*26947304SEvan Yan 685*26947304SEvan Yan /* Not converted */ 686*26947304SEvan Yan return (-1); 687*26947304SEvan Yan } 688*26947304SEvan Yan 689*26947304SEvan Yan /* 690*26947304SEvan Yan * compare_info() 691*26947304SEvan Yan * 692*26947304SEvan Yan * Compare two slots in the lookup table that maps paths to usage. 693*26947304SEvan Yan * 694*26947304SEvan Yan * NOTE: for use with qsort() and bsearch(). 695*26947304SEvan Yan */ 696*26947304SEvan Yan static int 697*26947304SEvan Yan compare_info(const void *a, const void *b) 698*26947304SEvan Yan { 699*26947304SEvan Yan info_table_t *slot_a = (info_table_t *)a; 700*26947304SEvan Yan info_table_t *slot_b = (info_table_t *)b; 701*26947304SEvan Yan 702*26947304SEvan Yan return (strcmp(slot_a->path, slot_b->path)); 703*26947304SEvan Yan } 704