/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Implementation of ri_init routine for obtaining mapping
* of system board attachment points to physical devices and to
* the Reconfiguration Coordination Manager (RCM) client usage
* of these devices.
*/
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <kstat.h>
#include <sys/sbd_ioctl.h>
#include "rsrc_info_impl.h"
/*
* Occupant types exported by cfgadm sbd plugin via
* config_admin(3CFGADM).
*/
/*
* RCM abstract resource names.
*/
/*
* define to allow io_cm_info to return NODE is NULL to ri_init,
* in order to skip over nodes w/unattached drivers
*/
/*
* This code is CMP aware as it parses the
* cfgadm info field for individual cpuids.
*/
typedef struct {
int nlist;
} apd_t;
typedef struct {
long pagesize;
long syspages;
long sysmb;
} mem_stat_t;
typedef struct {
typedef struct {
char **rlist;
int nrlist;
int ncpus;
int ndevs;
} rcmd_t;
typedef struct {
const char *rsrc;
const char *info;
} usage_t;
/* Lookup table entry for matching IO devices to RCM resource usage */
typedef struct {
int n_usage;
typedef struct {
int n_entries;
int n_slots;
typedef struct {
int err;
char *pathbuf;
static int dyn_ap_ids(char *, cfga_list_data_t **, int *);
rcmd_t *);
static int ident_leaf(di_node_t);
static int mk_drv_inst(di_node_t, char [], char *);
static int devinfo_node_walk(di_node_t, void *);
static int devinfo_devlink_walk(di_devlink_t, void *);
static int rcm_ignore(char *, char *);
static int state2query(int);
static ri_dev_t *ri_dev_alloc(void);
static ri_dev_t *io_dev_alloc(char *);
static ri_client_t *ri_client_alloc(char *, char *);
static void apd_tbl_free(apd_t [], int);
static char *pstate2str(int);
static int ecache_info_init(ecache_info_t *);
static int find_cpu_nodes(di_node_t, void *);
static int table_compare_names(const void *, const void *);
static int table_compare_indices(const void *, const void *);
static void empty_table(lookup_table_t *);
#ifdef DEBUG
#endif /* DEBUG */
static struct {
char *type;
} cm_ctl[] = {
};
/*
* Table of known info string prefixes for RCM modules that do not
* represent actual resource usage, but instead provide name translations
* or sequencing within the RCM namespace. Since RCM provides no way to
* filter these out, we must maintain this hack.
*/
static char *rcm_info_filter[] = {
"Network interface", /* Network naming module */
};
/*
* Allocate snapshot handle.
*/
int
{
int i, j;
int, rcmd_t *);
int cm_info_rv;
return (RI_INVAL);
if (flags & ~RI_REQ_MASK)
return (RI_NOTSUP);
rv = RI_FAILURE;
goto out;
}
/*
* Create mapping of boards to components.
*/
goto out;
}
}
#ifdef DEBUG
#endif /* DEBUG */
rv = RI_FAILURE;
goto out;
}
/*
* Best effort attempt to read cpu ecache sizes from
* in i_cpu_cm_info().
*/
rv = RI_FAILURE;
goto out;
}
/*
* Add component info based on occupant type. Note all
* passes through the apd table skip over the first
* cfgadm_list_data entry, which is the static system board
* attachment point.
*/
continue;
}
if ((cm_info =
if (cm_info_rv != 0) {
/*
* If we cannot obtain info for the ap,
* skip it and do not fail the entire
* operation. This case occurs when the
* driver for a device is not attached:
* di_init() returns failed back to
* io_cm_info().
*/
if (cm_info_rv == RI_NODE_NIL)
continue;
else {
rv = RI_FAILURE;
goto out;
}
}
}
}
}
rv = RI_FAILURE;
out:
if (rv == RI_SUCCESS)
else
return (rv);
}
/*
* Map static board attachment point to dynamic attachment points (components).
*/
static int
{
char *errstr;
return (-1);
}
return (0);
}
/*
* Initialize rcm handle, memory stats. Cache query result if necessary.
*/
static int
{
int rv = 0;
return (-1);
}
return (-1);
}
if (flags & RI_INCLUDE_QUERY)
return (rv);
}
static void
{
char **cpp;
}
}
typedef struct {
} di_arg_t;
/*
* The ecache sizes for individual cpus are read from the
* from the cfgadm_sbd cpu attachment point ecache info,
* which may be a sum of multiple cores for CMP.
*/
static int
{
rv = -1;
goto done;
}
rv = -1;
goto done;
}
rv = -1;
goto done;
}
rv = -1;
goto done;
}
find_cpu_nodes) != 0) {
rv = -1;
}
done:
if (root != DI_NODE_NIL)
if (ph != DI_PROM_HANDLE_NIL)
return (rv);
}
/*
* Libdevinfo node walk callback for reading ecache size
* properties for cpu device nodes. Subtrees not containing
* cpu nodes are filtered out.
*/
static int
{
char *name;
int walk_child = 0;
if (node == DI_NODE_NIL) {
return (DI_WALK_TERMINATE);
}
return (DI_WALK_CONTINUE);
}
return (DI_WALK_PRUNECHILD);
}
/*
* CMP nodes will be the parent of cpu nodes. On some platforms,
* cpu nodes will be under the ssm node. In either case,
* continue searching this subtree.
*/
return (DI_WALK_CONTINUE);
}
}
/*
* Ecache size property name differs with processor implementation.
* Panther has both L2 and L3, so check for L3 first to differentiate
* from Jaguar, which has only L2.
*/
/*
* On some platforms the cache property is in the core
* node while the cpuid is in the child cpu node. It may
* be needed while processing this node or a child node.
*/
walk_child = 1;
}
if (ec->ecache_curr != 0) {
}
}
}
/*
* Given a di_node_t, call the appropriate int property lookup routine.
* Note: This lookup fails if the int property has multiple value entries.
*/
static int
{
int rv;
}
/*
* For offline queries, RCM must be given a list of all resources
* so modules can have access to the full scope of the operation.
* The rcm_get_info calls are made individually in order to map the
* returned rcm_info_t's to physical devices. The rcm_request_offline
* result is cached so the query state can be looked up as we process
* the rcm_get_info calls. This routine also tallies up the amount of
* memory going away and creates a list of cpu ids to be used
* later for rcm_request_capacity_change.
*/
static int
{
int i, j;
#ifdef DEBUG
char **cpp;
#endif /* DEBUG */
/*
* Initial pass to size cpu and resource name arrays needed to
* interface with RCM. Attachment point ids for CMP can represent
* multiple cpus (and resource names). Instead of parsing the
* cfgadm info field here, use the worse case that all component
* attachment points are CMP.
*/
continue;
}
}
}
/* account for trailing NULL in rlist */
return (-1);
}
/*
* Second pass to fill in the RCM resource and cpu lists.
*/
continue;
}
if ((cm_rcm_qpass =
return (-1);
}
}
}
return (0);
/*
* Cache query result. Since we are only interested in the
* set of RCM clients processed and not their request status,
* the return value is irrelevant.
*/
#ifdef DEBUG
}
#endif /* DEBUG */
return (0);
}
static int
{
-1 : 0);
}
/*
* RCM capacity change request for cpus.
*/
static int
{
int i, j, k;
int rv = 0;
/* get all cpus in the system */
return (-1);
rv = -1;
goto out;
}
rv = -1;
goto out;
}
/*
* Construct the new cpu list.
*/
for (i = 0, j = 0; i < sysncpus; i++) {
break;
}
}
}
}
sysncpus) != 0 ||
newncpus) != 0) {
rv = -1;
goto out;
}
#ifdef DEBUG
for (i = 0; i < sysncpus; i++) {
}
for (i = 0; i < newncpus; i++) {
}
#endif /* DEBUG */
out:
return (rv);
}
static int
{
int i;
return (-1);
}
return (-1);
}
(void) kstat_close(kc);
return (-1);
}
}
}
(void) kstat_close(kc);
return (0);
}
/*
* RCM capacity change request for memory.
*/
static int
{
long newpages;
int rv = 0;
return (-1);
}
return (-1);
}
"page_size=%d, old_pages=%d, new_pages=%d\n",
return (rv);
}
static int
{
int i;
return (cm_ctl[i].cm_rcm_qpass);
}
}
return (NULL);
}
/*
* Save cpu ids and RCM abstract resource names.
* Cpu ids will be used for the capacity change request.
* Resource names will be used for the offline query.
*/
static int
{
&ecache) == 3);
return (-1);
}
}
return (0);
}
/*
* No RCM resource names for individual memory units, so
* just add to offline query page count.
*/
static int
{
char *cp;
return (-1);
}
return (0);
}
/*
* Add physical I/O bus name to RCM resource list.
*/
static int
{
char *rsrcname;
return (-1);
}
return (-1);
}
return (0);
}
static int
int, rcmd_t *)
{
int i;
}
}
return (NULL);
}
/*
* Create cpu handle, adding properties exported by sbd plugin and
* RCM client usage.
*/
/* ARGSUSED */
static int
{
return (-1);
}
/* parse cpuids */
break;
}
}
return (rv);
}
static int
{
int ecache_mb = 0;
int ecache_kb = 0;
/*
* Could have been unconfigured in the interim, so cannot
* count on processor_info recognizing it.
*/
return (-1);
}
/*
* Assume the ecache_info table has the right e-cache size for
* this CPU. Use the value found in cfgadm (ecache_cfga) if not.
*/
}
if (ecache_mb == 0) {
}
return (-1);
}
/*
* Report cache size in kilobyte units if available. This info is
* added to support processors with cache sizes that are non-integer
* megabyte multiples.
*/
if (ecache_kb != 0) {
ecache_kb) != 0) {
return (-1);
}
}
&rcm_info) != RCM_SUCCESS) {
return (-1);
}
return (0);
}
/*
* Create memory handle, adding properties exported by sbd plugin.
* No RCM tuples to be saved unless RCM is modified to export names
* for individual memory units.
*/
/* ARGSUSED */
static int
{
char *cp;
char *cpval;
int len;
&size_kb) != 2) {
goto err_fmt;
}
goto err_fmt;
}
goto err_fmt;
}
if (len >= CFGA_AP_LOG_ID_LEN) {
goto err_fmt;
}
}
goto err_fmt;
}
}
goto err_fmt;
}
if (len >= CFGA_AP_LOG_ID_LEN) {
goto err_fmt;
}
}
goto err_fmt;
}
}
return (-1);
/*
* Convert memory sizes to MB (truncate).
*/
return (-1);
}
if (target[0] != '\0' &&
return (-1);
}
if (source[0] != '\0' &&
return (-1);
}
/*
* XXX - move this property to attachment point hdl?
*/
return (-1);
}
return (0);
return (-1);
}
/*
* Initiate a libdevinfo walk on the IO bus path.
* XXX - investigate performance using two threads here: one thread to do the
* libdevinfo snapshot and treewalk; and one thread to get RCM usage info
*/
static int
{
int i;
int j;
int k;
int set_size;
int retval = 0;
int n_usage;
/* Extract devfs path from cfgadm information */
return (-1);
}
/* Initialize empty device lookup table */
devicetable.n_entries = 0;
devicetable.n_slots = 0;
/* Get libdevinfo snapshot */
goto end;
}
/*
* Map in devlinks database.
* XXX - This could be moved to ri_init() for better performance.
*/
retval = -1;
goto end;
}
/* Initialize argument for devinfo treewalk */
/* Use libdevinfo treewalk to build device lookup table */
devinfo_node_walk) != 0) {
retval = -1;
goto end;
}
retval = -1;
goto end;
}
/* Call RCM to gather usage information */
RCM_SUCCESS) {
retval = -1;
goto end;
}
/* Sort the device table by name (proper order for lookups) */
/* Perform mappings of RCM usage segments to device table entries */
lastdeventry = NULL;
continue;
retval = -1;
goto end;
}
} else {
retval = -1;
goto end;
}
}
}
/* Re-sort the device table by index number (original treewalk order) */
/*
* Use the mapped usage and the device table to construct ri_dev_t's.
* Construct one for each set of entries in the device table with
* matching di_node_t's, if: 1) it has mapped RCM usage, or 2) it is
* a leaf node and the caller has requested that unmanaged nodes be
* included in the output.
*/
i = 0;
while (i < devicetable.n_entries) {
/* Count how many usage records are mapped to this node's set */
n_usage = 0;
set_size = 0;
set_size += 1;
}
/*
* If there's no usage, then the node is unmanaged. Skip this
* set of devicetable entries unless the node is a leaf node
* and the caller has requested information on unmanaged leaves.
*/
if ((n_usage == 0) &&
i += set_size;
continue;
}
/*
* The checks above determined that this node is going in.
* ri_dev_t for this node.
*/
retval = -1;
break;
}
retval = -1;
break;
}
/* Now add all the RCM usage records (if any) to the ri_dev_t */
for (j = i; j < (i + set_size); j++) {
/* Create new ri_client_t for basic usage */
"ri_client_alloc failed\n"));
retval = -1;
goto end;
}
/* Add extra query usage to the ri_client_t */
if ((flags & RI_INCLUDE_QUERY) &&
"add_query_state failed\n"));
retval = -1;
goto end;
}
/* Link new ri_client_t to ri_dev_t */
if (io->rcm_clients) {
} else {
}
}
}
/* Link the ri_dev_t into the return value */
/* Advance to the next node set */
i += set_size;
}
end:
if (root != DI_NODE_NIL)
return (retval);
}
static int
{
}
/* ARGSUSED */
static int
{
char *drv;
int inst;
devfs_path));
return (-1);
}
devfs_path));
return (-1);
}
return (0);
}
/*
* Libdevinfo walker.
*
* During the tree walk of the attached IO devices, for each node
* and all of its associated minors, the following actions are performed:
* - The /devices path of the physical device node or minor
* is stored in a lookup table along with a reference to the
* libdevinfo node it represents via add_lookup_entry().
* - The device links associated with each device are also
* stored in the same lookup table along with a reference to
* the libdevinfo node it represents via the minor walk callback.
*
*/
static int
{
char *devfs_path;
#ifdef DEBUG
char *drv;
#endif /* DEBUG */
if (node == DI_NODE_NIL) {
return (DI_WALK_TERMINATE);
}
/* Use the provided path buffer to create full /devices path */
#ifdef DEBUG
di_instance(node)));
#endif
/* Free the devfs_path */
/* Add an entry to the lookup table for this physical device */
return (DI_WALK_TERMINATE);
}
/* Check if this node has minors */
/* Walk this node's minors */
devinfo_minor_walk) != 0) {
return (DI_WALK_TERMINATE);
}
}
}
return (DI_WALK_CONTINUE);
}
/*
* Use di_devlink_walk to find the /dev link from /devices path for this minor
*/
static int
{
char *name;
char *devfs_path;
#ifdef DEBUG
}
#endif /* DEBUG */
/* Terminate the walk when the device node changes */
return (DI_WALK_TERMINATE);
}
/* Construct full /devices path for this minor */
return (DI_WALK_CONTINUE);
}
/* Add lookup entry for this minor node */
return (DI_WALK_TERMINATE);
}
/*
* Walk the associated device links.
* Note that di_devlink_walk() doesn't want "/devices" in its paths.
* Also note that di_devlink_walk() will fail if there are no device
* links, which is fine; so ignore if it fails. Only check for
* internal failures during such a walk.
*/
return (DI_WALK_TERMINATE);
}
return (DI_WALK_CONTINUE);
}
static int
{
const char *linkpath;
/* Get the devlink's path */
return (DI_WALK_TERMINATE);
}
/* Add lookup entry for this devlink */
return (DI_WALK_TERMINATE);
}
return (DI_WALK_CONTINUE);
}
/*
* Map rcm_info_t's to ri_client_t's, filtering out "uninteresting" (hack)
* RCM clients. The number of "interesting" ri_client_t's is returned
* in cnt if passed non-NULL.
*/
static int
{
return (0);
*cnt = 0;
continue;
}
continue;
return (-1);
return (-1);
}
++*cnt;
/*
* Link in
*/
*client_list = client;
continue;
}
}
}
return (0);
}
/*
* Currently only filtering out based on known info string prefixes.
*/
/* ARGSUSED */
static int
{
char **cpp;
return (0);
}
}
return (-1);
}
/*
* If this tuple was cached in the offline query pass, add the
* query state and error string to the ri_client_t.
*/
static int
const char *info)
{
}
RI_QUERY_ERR, errstr) != 0)) {
return (-1);
}
return (0);
}
static int
{
int query;
switch (rcm_state) {
case RCM_STATE_OFFLINE_QUERY:
case RCM_STATE_SUSPEND_QUERY:
query = RI_QUERY_OK;
break;
break;
default:
break;
}
return (query);
}
static void
{
return;
}
}
}
/*
* The cpu list is ordered on cpuid since CMP cpuids will not necessarily
* be discovered in sequence.
*/
static void
{
}
}
/*
* Linear lookup. Should convert to hash tab.
*/
static rcm_info_tuple_t *
{
return (NULL);
}
continue;
}
return (tuple);
}
}
return (NULL);
}
/*
* Create and link attachment point handle.
*/
static ri_ap_t *
{
return (NULL);
}
return (NULL);
}
} else {
}
}
return (ap);
}
static ri_dev_t *
ri_dev_alloc(void)
{
}
return (dev);
}
static ri_dev_t *
{
return (NULL);
drv_inst) != 0) {
return (NULL);
}
return (io);
}
static ri_client_t *
{
return (NULL);
}
return (NULL);
}
return (NULL);
}
return (client);
}
static void
{
int i;
}
static char *
{
char *state;
switch (pi_state) {
case P_OFFLINE:
state = PS_OFFLINE;
break;
case P_ONLINE:
break;
case P_FAULTED:
state = PS_FAULTED;
break;
case P_POWEROFF:
state = PS_POWEROFF;
break;
case P_NOINTR:
break;
case P_SPARE:
break;
default:
state = "unknown";
break;
}
return (state);
}
#ifdef DEBUG
static void
{
int i, j;
j++, cfga_ldata++) {
"apd_tbl[%d].cfga_list_data[%d].ap_log_id=%s\n",
i, j, cfga_ldata->ap_log_id));
}
}
}
#endif /* DEBUG */
/*
* The lookup table is a simple array that is grown in chunks
* to optimize memory allocation.
* Indices are assigned to each array entry in-order so that
* the original device tree ordering can be discerned at a later time.
*
* add_lookup_entry is called from the libdevinfo tree traversal callbacks:
* 1) devinfo_node_walk - physical device path for each node in
* the devinfo tree via di_walk_node(), lookup entry name is
* /devices/[di_devfs_path]
* 2) devinfo_minor_walk - physical device path plus minor name for
* each minor associated with a node via di_walk_minor(), lookup entry
* name is /devices/[di_devfs_path:di_minor_name]
* 3) devinfo_devlink_walk - for each minor's /dev link from its /devices
* path via di_devlink_walk(), lookup entry name is di_devlink_path()
*/
static int
{
/* Grow the lookup table by USAGE_ALLOC_SIZE slots if necessary */
sizeof (lookup_entry_t);
return (-1);
}
}
/* Add this name to the next slot */
return (-1);
}
return (0);
}
/*
* lookup table entry names are full pathname strings, all start with /
*/
static int
table_compare_names(const void *a, const void *b)
{
}
/*
* Compare two indices and return -1 for less, 1 for greater, 0 for equal
*/
static int
table_compare_indices(const void *a, const void *b)
{
return (-1);
return (1);
return (0);
}
/*
* Given a RCM resource name, find the matching entry in the IO device table
*/
static lookup_entry_t *
{
sizeof (lookup_entry_t), table_compare_names);
#ifdef DEBUG
}
#endif /* DEBUG */
return (entry);
}
/*
* Add RCM usage to the given device table entry.
* Returns -1 on realloc failure.
*/
static int
{
const char *info;
return (0);
return (0);
return (-1);
}
return (0);
}
static void
{
int i;
if (table) {
/*
* Note: the strings pointed to from within
* usage were freed already by rcm_free_info
*/
}
}
}