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.
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.
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]
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
33 #include <libhotplug.h>
34 #include <libhotplug_impl.h>
35 #include <sys/sunddi.h>
36 #include <sys/ddi_hp.h>
37 #include "hotplugd_impl.h"
40 * Define structures for a path-to-usage lookup table.
42 typedef struct info_entry
{
45 struct info_entry
*next
;
50 info_entry_t
*entries
;
54 * Define callback argument used when getting resources.
60 char path
[MAXPATHLEN
];
61 char connection
[MAXPATHLEN
];
62 char dev_path
[MAXPATHLEN
];
66 * Define callback argument used when merging info.
72 char path
[MAXPATHLEN
];
73 char connection
[MAXPATHLEN
];
79 static int merge_rcm_info(hp_node_t root
, rcm_info_t
*info
);
80 static int get_rcm_usage(char **rsrcs
, rcm_info_t
**info_p
);
81 static int build_table(rcm_info_t
*info
, info_table_t
**tablep
,
83 static void free_table(info_table_t
*table
, size_t table_len
);
84 static int resource_callback(hp_node_t node
, void *argp
);
85 static int merge_callback(hp_node_t node
, void *argp
);
86 static int rsrc2path(const char *rsrc
, char *path
);
87 static int compare_info(const void *a
, const void *b
);
92 * Given an information snapshot, get the corresponding
93 * RCM usage information and merge it into the snapshot.
96 copy_usage(hp_node_t root
)
98 rcm_info_t
*info
= NULL
;
102 /* Get resource names */
103 if ((rv
= rcm_resources(root
, &rsrcs
)) != 0) {
104 log_err("Cannot get RCM resources (%s)\n", strerror(rv
));
108 /* Do nothing if no resources */
112 /* Get RCM usage information */
113 if ((rv
= get_rcm_usage(rsrcs
, &info
)) != 0) {
114 log_err("Cannot get RCM information (%s)\n", strerror(rv
));
115 free_rcm_resources(rsrcs
);
119 /* Done with resource names */
120 free_rcm_resources(rsrcs
);
122 /* If there is RCM usage information, merge it in */
124 rv
= merge_rcm_info(root
, info
);
135 * Given the root of a hotplug information snapshot,
136 * construct a list of RCM compatible resource names.
139 rcm_resources(hp_node_t root
, char ***rsrcsp
)
141 resource_cb_arg_t arg
;
143 /* Initialize results */
146 /* Traverse snapshot to get resources */
147 (void) memset(&arg
, 0, sizeof (resource_cb_arg_t
));
148 (void) hp_traverse(root
, &arg
, resource_callback
);
150 /* Check for errors */
151 if (arg
.error
!= 0) {
152 free_rcm_resources(arg
.rsrcs
);
162 * free_rcm_resources()
164 * Free a table of RCM resource names.
167 free_rcm_resources(char **rsrcs
)
172 for (i
= 0; rsrcs
[i
] != NULL
; i
++)
181 * Implement an RCM offline request.
183 * NOTE: errors from RCM will be merged into the snapshot.
186 rcm_offline(char **rsrcs
, uint_t flags
, hp_node_t root
)
188 rcm_handle_t
*handle
;
189 rcm_info_t
*info
= NULL
;
190 uint_t rcm_flags
= 0;
193 dprintf("rcm_offline()\n");
197 rcm_flags
|= RCM_FORCE
;
199 rcm_flags
|= RCM_QUERY
;
201 /* Allocate RCM handle */
202 if (rcm_alloc_handle(NULL
, 0, NULL
, &handle
) != RCM_SUCCESS
) {
203 log_err("Cannot allocate RCM handle (%s)\n", strerror(errno
));
207 /* Request RCM offline */
208 if (rcm_request_offline_list(handle
, rsrcs
, rcm_flags
,
209 &info
) != RCM_SUCCESS
)
212 /* RCM handle is no longer needed */
213 (void) rcm_free_handle(handle
);
216 * Check if RCM returned any information tuples. If so,
217 * then also check if the RCM operation failed, and possibly
218 * merge the RCM info into the caller's hotplug snapshot.
222 (void) merge_rcm_info(root
, info
);
232 * Implement an RCM online notification.
235 rcm_online(char **rsrcs
)
237 rcm_handle_t
*handle
;
238 rcm_info_t
*info
= NULL
;
240 dprintf("rcm_online()\n");
242 if (rcm_alloc_handle(NULL
, 0, NULL
, &handle
) != RCM_SUCCESS
) {
243 log_err("Cannot allocate RCM handle (%s)\n", strerror(errno
));
247 (void) rcm_notify_online_list(handle
, rsrcs
, 0, &info
);
249 (void) rcm_free_handle(handle
);
258 * Implement an RCM remove notification.
261 rcm_remove(char **rsrcs
)
263 rcm_handle_t
*handle
;
264 rcm_info_t
*info
= NULL
;
266 dprintf("rcm_remove()\n");
268 if (rcm_alloc_handle(NULL
, 0, NULL
, &handle
) != RCM_SUCCESS
) {
269 log_err("Cannot allocate RCM handle (%s)\n", strerror(errno
));
273 (void) rcm_notify_remove_list(handle
, rsrcs
, 0, &info
);
275 (void) rcm_free_handle(handle
);
284 * Lookup usage information for a set of resources from RCM.
287 get_rcm_usage(char **rsrcs
, rcm_info_t
**info_p
)
289 rcm_handle_t
*handle
;
290 rcm_info_t
*info
= NULL
;
293 /* No-op if no RCM resources */
297 /* Allocate RCM handle */
298 if (rcm_alloc_handle(NULL
, RCM_NOPID
, NULL
, &handle
) != RCM_SUCCESS
) {
299 log_err("Cannot allocate RCM handle (%s)\n", strerror(errno
));
303 /* Get usage information from RCM */
304 if (rcm_get_info_list(handle
, rsrcs
,
305 RCM_INCLUDE_DEPENDENT
| RCM_INCLUDE_SUBTREE
,
306 &info
) != RCM_SUCCESS
) {
307 log_err("Failed to get RCM information (%s)\n",
312 /* RCM handle is no longer needed */
313 (void) rcm_free_handle(handle
);
322 * Merge RCM information into a hotplug information snapshot.
323 * First a lookup table is built to map lists of RCM usage to
324 * pathnames. Then during a full traversal of the snapshot,
325 * the lookup table is used for each node to find matching
326 * RCM info tuples for each path in the snapshot.
329 merge_rcm_info(hp_node_t root
, rcm_info_t
*info
)
336 /* Build a lookup table, mapping paths to usage information */
337 if ((rv
= build_table(info
, &table
, &table_len
)) != 0) {
338 log_err("Cannot build RCM lookup table (%s)\n", strerror(rv
));
342 /* Stop if no valid entries were inserted in table */
343 if ((table
== NULL
) || (table_len
== 0)) {
344 log_err("Unable to gather RCM usage.\n");
348 /* Initialize callback argument */
349 (void) memset(&arg
, 0, sizeof (merge_cb_arg_t
));
351 arg
.table_len
= table_len
;
353 /* Perform a merge traversal */
354 (void) hp_traverse(root
, (void *)&arg
, merge_callback
);
356 /* Done with the table */
357 free_table(table
, table_len
);
359 /* Check for errors */
360 if (arg
.error
!= 0) {
361 log_err("Cannot merge RCM information (%s)\n", strerror(rv
));
369 * resource_callback()
371 * A callback function for hp_traverse() that builds an RCM
372 * compatible list of resource path names. The array has
373 * been pre-allocated based on results from the related
374 * callback resource_count_callback().
377 resource_callback(hp_node_t node
, void *argp
)
379 resource_cb_arg_t
*arg
= (resource_cb_arg_t
*)argp
;
385 type
= hp_type(node
);
387 /* Prune OFFLINE ports */
388 if ((type
== HP_NODE_PORT
) && HP_IS_OFFLINE(hp_state(node
)))
389 return (HP_WALK_PRUNECHILD
);
391 /* Skip past non-devices */
392 if (type
!= HP_NODE_DEVICE
)
393 return (HP_WALK_CONTINUE
);
395 /* Lookup resource path */
396 if (hp_path(node
, arg
->path
, arg
->connection
) != 0) {
397 log_err("Cannot get RCM resource path.\n");
399 return (HP_WALK_TERMINATE
);
402 /* Insert "/devices" to path name */
403 (void) snprintf(arg
->dev_path
, MAXPATHLEN
, "/devices%s", arg
->path
);
406 * Grow resource array to accomodate new /devices path.
407 * NOTE: include an extra NULL pointer at end of array.
409 new_size
= (arg
->n_rsrcs
+ 2) * sizeof (char *);
410 if (arg
->rsrcs
== NULL
)
411 new_rsrcs
= (char **)malloc(new_size
);
413 new_rsrcs
= (char **)realloc(arg
->rsrcs
, new_size
);
414 if (new_rsrcs
!= NULL
) {
415 arg
->rsrcs
= new_rsrcs
;
417 log_err("Cannot allocate RCM resource array.\n");
419 return (HP_WALK_TERMINATE
);
422 /* Initialize new entries */
423 arg
->rsrcs
[arg
->n_rsrcs
] = strdup(arg
->dev_path
);
424 arg
->rsrcs
[arg
->n_rsrcs
+ 1] = NULL
;
426 /* Check for errors */
427 if (arg
->rsrcs
[arg
->n_rsrcs
] == NULL
) {
428 log_err("Cannot allocate RCM resource path.\n");
430 return (HP_WALK_TERMINATE
);
433 /* Increment resource count */
436 /* Do not visit children */
437 return (HP_WALK_PRUNECHILD
);
443 * A callback function for hp_traverse() that merges RCM information
444 * tuples into an existing hotplug information snapshot. The RCM
445 * information will be turned into HP_NODE_USAGE nodes.
448 merge_callback(hp_node_t node
, void *argp
)
450 merge_cb_arg_t
*arg
= (merge_cb_arg_t
*)argp
;
457 /* Only process device nodes (other nodes cannot have usage) */
458 if (hp_type(node
) != HP_NODE_DEVICE
)
459 return (HP_WALK_CONTINUE
);
461 /* Get path of current node, using buffer provided in 'arg' */
462 if ((rv
= hp_path(node
, arg
->path
, arg
->connection
)) != 0) {
463 log_err("Cannot lookup hotplug path (%s)\n", strerror(rv
));
465 return (HP_WALK_TERMINATE
);
468 /* Check the lookup table for associated usage */
469 lookup
.path
= arg
->path
;
470 if ((slot
= bsearch(&lookup
, arg
->table
, arg
->table_len
,
471 sizeof (info_table_t
), compare_info
)) == NULL
)
472 return (HP_WALK_CONTINUE
);
474 /* Usage information was found. Append HP_NODE_USAGE nodes. */
475 for (entry
= slot
->entries
; entry
!= NULL
; entry
= entry
->next
) {
477 /* Allocate a new usage node */
478 usage
= (hp_node_t
)calloc(1, sizeof (struct hp_node
));
480 log_err("Cannot allocate hotplug usage node.\n");
482 return (HP_WALK_TERMINATE
);
485 /* Initialize the usage node's contents */
486 usage
->hp_type
= HP_NODE_USAGE
;
487 if ((usage
->hp_name
= strdup(entry
->rsrc
)) == NULL
) {
488 log_err("Cannot allocate hotplug usage node name.\n");
491 return (HP_WALK_TERMINATE
);
493 if ((usage
->hp_usage
= strdup(entry
->usage
)) == NULL
) {
494 log_err("Cannot allocate hotplug usage node info.\n");
495 free(usage
->hp_name
);
498 return (HP_WALK_TERMINATE
);
501 /* Link the usage node as a child of the device node */
502 usage
->hp_parent
= node
;
503 usage
->hp_sibling
= node
->hp_child
;
504 node
->hp_child
= usage
;
507 return (HP_WALK_CONTINUE
);
513 * Build a lookup table that will be used to map paths to their
514 * corresponding RCM information tuples.
517 build_table(rcm_info_t
*info
, info_table_t
**tablep
, size_t *table_lenp
)
519 rcm_info_tuple_t
*tuple
;
526 char path
[MAXPATHLEN
];
528 /* Initialize results */
532 /* Count the RCM info tuples to determine the table's size */
534 for (tuple
= NULL
; (tuple
= rcm_info_next(info
, tuple
)) != NULL
; )
537 /* If the table would be empty, then do nothing */
541 /* Allocate the lookup table */
542 table
= (info_table_t
*)calloc(table_len
, sizeof (info_table_t
));
547 * Fill in the lookup table. Fill one slot in the table
548 * for each device path that has a set of associated RCM
549 * information tuples. In some cases multiple tuples will
550 * be joined together within the same slot.
554 for (tuple
= NULL
; (tuple
= rcm_info_next(info
, tuple
)) != NULL
; ) {
557 * Extract RCM resource name and usage description.
559 * NOTE: skip invalid tuples to return as much as possible.
561 if (((rsrc
= rcm_info_rsrc(tuple
)) == NULL
) ||
562 ((usage
= rcm_info_info(tuple
)) == NULL
)) {
563 log_err("RCM returned invalid resource or usage.\n");
568 * Try to convert the RCM resource name to a hotplug path.
569 * If conversion succeeds and this path differs from the
570 * current slot in the table, then initialize the next
573 if ((rsrc2path(rsrc
, path
) == 0) &&
574 ((slot
== NULL
) || (strcmp(slot
->path
, path
) != 0))) {
575 slot
= &table
[table_len
];
576 if ((slot
->path
= strdup(path
)) == NULL
) {
577 log_err("Cannot build info table slot.\n");
578 free_table(table
, table_len
);
584 /* Append current usage to entry list in the current slot */
587 /* Allocate new entry */
588 entry
= (info_entry_t
*)malloc(sizeof (info_entry_t
));
590 log_err("Cannot allocate info table entry.\n");
591 free_table(table
, table_len
);
595 /* Link entry into current slot list */
596 entry
->next
= slot
->entries
;
597 slot
->entries
= entry
;
599 /* Initialize entry values */
600 if (((entry
->rsrc
= strdup(rsrc
)) == NULL
) ||
601 ((entry
->usage
= strdup(usage
)) == NULL
)) {
602 log_err("Cannot build info table entry.\n");
603 free_table(table
, table_len
);
609 /* Check if valid entries were inserted in table */
610 if (table_len
== 0) {
615 /* Sort the lookup table by hotplug path */
616 qsort(table
, table_len
, sizeof (info_table_t
), compare_info
);
620 *table_lenp
= table_len
;
627 * Destroy a lookup table.
630 free_table(info_table_t
*table
, size_t table_len
)
636 for (index
= 0; index
< table_len
; index
++) {
637 free(table
[index
].path
);
638 while (table
[index
].entries
!= NULL
) {
639 entry
= table
[index
].entries
;
640 table
[index
].entries
= entry
->next
;
653 * Convert from an RCM resource name to a hotplug device path.
656 rsrc2path(const char *rsrc
, char *path
)
659 char tmp
[MAXPATHLEN
];
661 /* Only convert /dev and /devices paths */
662 if (strncmp(rsrc
, "/dev", 4) == 0) {
664 /* Follow symbolic links for /dev paths */
665 if (realpath(rsrc
, tmp
) == NULL
) {
666 log_err("Cannot resolve RCM resource (%s)\n",
671 /* Remove the leading "/devices" part */
672 (void) strlcpy(path
, &tmp
[strlen(S_DEVICES
)], MAXPATHLEN
);
674 /* Remove any trailing minor node part */
675 if ((s
= strrchr(path
, ':')) != NULL
)
678 /* Successfully converted */
689 * Compare two slots in the lookup table that maps paths to usage.
691 * NOTE: for use with qsort() and bsearch().
694 compare_info(const void *a
, const void *b
)
696 info_table_t
*slot_a
= (info_table_t
*)a
;
697 info_table_t
*slot_b
= (info_table_t
*)b
;
699 return (strcmp(slot_a
->path
, slot_b
->path
));