/*
* 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
*/
/*
*/
/*
* Walk the LDOM PRI component nodes and create appropriate topology nodes
*/
#include <stddef.h>
#include <inttypes.h>
#include <strings.h>
#include <string.h>
#include <libuutil.h>
#include <libnvpair.h>
#include <fm/topo_mod.h>
#include "pi_impl.h"
/*
* Allow for custom topo node creation routines based on topo-hc-name.
*/
struct pi_enum_functions_s {
};
struct pi_methods_s {
char *hc_name;
};
extern topo_method_t
/*
* List of custom enumerators for PRI nodes that require them. The most
* common nodes are listed first.
*/
{pi_enum_cpu, STRAND},
{pi_enum_cpu, CPU},
{pi_enum_mem, DIMM},
{pi_enum_cpu, CORE},
{pi_enum_cpu, CHIP},
{pi_enum_niu, NIU},
{pi_enum_bay, BAY},
};
/* List of methods that will be registered in the nodes. */
{pi_chip_methods, CHIP},
{pi_core_methods, CORE},
{pi_strand_methods, CPU},
{pi_mem_methods, DIMM},
};
/*
* In order to create a topology node from a PRI MDE node we need to know the
* topology parent node that should be used. So, after creating a topology
* node from an MDE node, we associate children of the MDE node with the new
* topology node. Thus, when the children are visited we can know the
* appropriate parent topology node to use.
*
* We take advantage of the libtopo threading model here, which guarantees a
* single thread and a single invocation at a time for an enumerator. This
* makes using a file-global safe.
*/
struct pi_walkernode_s {
};
/* The routine called for each node in the PRI while walking the graph */
/*
* Create a sub-range for a given PRI node and associate the given topology
* node with the children.
*/
topo_instance_t, tnode_t **);
/* Routines to handle the list of topology parents and mde_nodes */
static int pi_walkerlist_compare(const void *, const void *, void *);
static int pi_walkerlist_create(topo_mod_t *);
static void pi_walkerlist_destroy(topo_mod_t *);
uint32_t);
int
{
int result;
if (result != 0) {
return (-1);
}
/* Add the builtin functions to the list */
fp++;
}
/* Add the builtin methods to the list */
mp++;
}
if (result != 0) {
return (-1);
}
return (0);
}
void
{
}
/*
* Begin to walk the machine description array starting at the given PRI node.
*/
int
{
int result;
return (-1);
}
mde_node);
/*
* Create a list to store topology nodes and their associated machine
* description index. This allows the code to know the parent of a
* node when creating topology entries.
*/
if (result != 0) {
return (result);
}
/* Create a walker node for the parent of the start node */
if (result != 0) {
return (result);
}
/*
* This is a top-level node. Make sure we call the top level
* enumerator if there is not already a custom enumerator registered.
*/
/*
* There is no enumerator function registered for this
* hc name. Automatically register the top level node
* enumerator function.
*/
if (result != 0) {
"walker could not register enumerator for type "
"%s\n", hc_name);
return (-1);
}
"walker registered pi_enum_top enumerator for type %s\n",
hc_name);
}
/* Walk the machine description list starting at the given node */
pi_walker_node, (void *)pip);
switch (result) {
case 0:
/* Successful completion */
/* DO NOTHING */
break;
case MDE_WALK_ERROR:
/*
* Store that we have a partial enumeration and return
* that we have encountered an error.
*/
result = -1;
break;
default:
/*
* This should not happen. We want to always produce
* as complete a topology as possible, even in the face
* of errors, however, so set an error and continue.
*/
"walker encountered invalid result: %d. "
"Continuing\n", result);
result = 0;
break;
}
/* Destroy the walker list, which is no longer necessary */
return (result);
}
/*
* Visited once for each node in the machine description. Creates a topo
* node for the machine description node and associates it with it's parent,
* by calling an appropriate creation routine for the node type.
*
* Output:
* This routine returns MDE_WALK_NEXT, MDE_WALK_DONE or MDE_WALK_ERROR
* only.
*/
static int
void *private)
{
int result;
/* Make sure we have our private data */
return (MDE_WALK_ERROR);
}
"walker processing node_0x%llx parent node 0x%llx\n",
/* Should we skip this node ? */
if (skip) {
/* Skip this node and continue to the next node */
return (MDE_WALK_NEXT);
}
if (result != 0) {
/*
* No ID available to place this mde node in the topology so
* we cannot create a topology node.
*/
return (MDE_WALK_NEXT);
}
/*
* Find the parent topo node for this machine description node.
*
* If found, the element will also be removed from the list and the
* memory used to keep track of it released. We will only visit an
* MDE node once and so the memory is no longer needed.
*/
/*
* No parent was found or a NULL parent encountered. We
* cannot create a new topology node without a parent (
* even for top level nodes). We associate children of
* this MDE node with a NULL parent to silently skip the
* remainder of this MDE branch.
*/
mde_node);
return (result);
}
/*
* We have the mde node instance and parent information.
* Attempt to create a topology node for this mde node.
*/
&t_node);
/*
* We have failed to create a new topology node based on
* the current MDE node. We set partial enumeration and
* return without associating the children of this MDE
* node with a topology parent. This will propgate the
* creation error down this MDE branch.
*/
return (result);
}
/*
* Associate the new topology node with any children of this mde node.
*/
return (result);
}
static int
{
int result;
char *hc_name;
/*
* A parent topology node is required even for top-level
* nodes.
*/
return (MDE_WALK_NEXT);
}
/*
* Find the topo-hc-name for this node which is used to find
* the specific creation function
*/
/* Cannot get the hc-name */
"failed to find hc-name for node_0x%llx\n", mde_node);
return (MDE_WALK_NEXT);
}
/* Determine the topology node creation routine to use */
faddr = 0;
if (result == 0) {
/*
* A function is registered for this node. Convert the
* address to a pointer to function
*/
}
/*
* Create a topology node for this mde node by calling the identified
* enumeration function
*/
if (result != 0) {
"failed to create topo entry for node_0x%llx type %s\n",
} else {
/*
* Need to check if we need enumerate internal expander
* attached bays. Since there is no MDE(PRI record) for
* an internal expander we are invoking the ses enumerator
* here. It will check if there is any internal enclosure
* and enumerate expander attached disks.
* Note that those nodes will be enumerated as a child of
* of chassis.
*/
" enumerator: %s",
} else {
/*
* min and max instance is handled by
* the ses enumerator.
*/
SES_ENCLOSURE, 0, 0, NULL);
if (result != 0)
"enumerate expander attached "
"internal bays. Continue on.");
}
}
}
return (MDE_WALK_NEXT);
}
/*
* Scan the children of a given MDE node and find all the sets of topo-hc-name
* types and their instance ranges. From this information we create topology
* node ranges on the given parent so that when the children are visited and a
* topology node is created, the range exists and the creation will succeed.
*/
static int
{
int result;
int rc;
int num_arcs;
int arcidx;
char *hc_name;
"walker failed to create node range with a NULL parent\n");
return (MDE_WALK_NEXT);
}
/* Determine how many children the given node has */
if (num_arcs == 0) {
/* This node has no children */
return (MDE_WALK_NEXT);
}
/* Get the indexes for all the child nodes and put them in an array */
return (MDE_WALK_ERROR);
}
/*
* The children of the given node may have multiple types.
* Potentially, each child may have a different type and we need to
* create a topo node range for each one.
*
* We loop through the children and collect the type information for
* each one and associate the child with the given parent topo node.
*/
if (result != 0) {
return (MDE_WALK_ERROR);
}
arcidx = 0;
/* Should this node be skipped? */
/* Skip this node */
continue;
}
/* Get the type of this node */
/* Increment the count of nodes with this type */
if (rc != 0) {
/*
* We have not visited this type yet. Create
* a new range based on this nodes instance
* information.
*/
if (result != 0) {
/*
* This error can only if there was a
* memory failure of some kind. Stop
* the walk or it will just get worse.
*/
(void) topo_mod_seterrno(mod,
return (MDE_WALK_ERROR);
}
/*
* We know the list exists now or the above
* would have failed. Just look it up.
*/
&hc_range);
}
/* Re-calculate the range minimums and maximums */
} else {
(void) topo_mod_seterrno(mod,
return (MDE_WALK_ERROR);
}
hc_name);
}
/*
* Associate this node with the given topo parent even if it
* has no instance. We do this so that later an error with
* the PRI node will be reported instead of an internal
* error about not being able to find the parent of a node
*/
if (rc != 0) {
"could not add node_0x%llx to walker list\n",
}
}
/*
* We have associated all the child nodes with the given topo parent
* in the walker list. Now we need to create topo ranges for each
* set of child types under the parent.
*/
/* Get the type name and count from the list element */
/*
* We have the number of children with this type.
* Create an appropriate range.
*/
"creating instance range %d to %d of type %s\n",
if (rc != 0) {
"failed to created node range %d to %d for "
}
/* Check the next node */
}
return (MDE_WALK_NEXT);
}
static int
{
int result;
if (result != 0) {
return (result);
}
/* Create min and max elements in this list */
return (-1);
}
return (0);
}
/* ARGSUSED */
static int
{
return (1);
}
return (-1);
}
return (0);
}
static int
{
/* Initialize the uutil list structure */
if (walker_pool == NULL) {
return (-1);
}
if (walker_list == NULL) {
walker_pool = NULL;
return (-1);
}
return (0);
}
static void
{
void *wvp;
/* Destroy our list of items */
/*
* First, we empty the list of elements and free each one.
* We do not free the data elements as they are libtopo nodes
* and will be freed by libtopo
*/
}
walker_list = NULL;
walker_pool = NULL;
}
static int
{
return (-1);
}
return (0);
}
/*
* Find the parent topo node for this machine description node.
*
* Nodes are removed from the list as they are found. They are only
* visited once and this eliminates the need for a separate routine
* that walks the list to free elements later.
*/
static int
{
return (-1);
}
/* Remove this element from the list */
return (0);
}