/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (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
* or http://www.opensolaris.org/os/licensing.
* 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 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* platform independent module to manage nodes under frutree
*/
/*
* This file has the frutree initialization code:
* 1) parse the config file to create all locations in the chassis
* 2) probe each location to find fru and probe the fru recursively to
* create locations, port nodes
* 3) handle hotswap picl events (dr_ap_state_change, dr_req)
* - update the frutree
* - send out picl-state-change, picl-condition-events
* 4) Monitor the port nodes state and condition
*/
#include <stdlib.h>
#include <sys/param.h>
#include <strings.h>
#include <string.h>
#include <limits.h>
#include <syslog.h>
#include <pthread.h>
#include <thread.h>
#include <libintl.h>
#include <sys/systeminfo.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <dirent.h>
#include <fcntl.h>
#include <ctype.h>
#include <time.h>
#include <poll.h>
#include <assert.h>
#include <libnvpair.h>
#include <alloca.h>
#include <stdarg.h>
#include <config_admin.h>
#include <libdevinfo.h>
#include <synch.h>
#include <sys/time.h>
#include <picl.h>
#include <picltree.h>
#include <picldefs.h>
#include <picld_pluginutil.h>
#include <libfru.h>
#include <sys/sysevent/dr.h>
#include <ptree_impl.h>
#include "piclfrutree.h"
#pragma init(piclfrutree_register)
/*
* following values are tunables that can be changed using
* environment variables
*/
int frutree_debug = NONE; /* debug switch */
static int frutree_poll_timeout = 5; /* polling time to monitor ports */
static int frutree_drwait_time = 10; /* wait time for dr operation */
#define PICL_PROP_CONF_FILE "conf_name"
#define PICL_ADMINLOCK_DISABLED "disabled"
#define PICL_ADMINLOCK_ENABLED "enabled"
#define HASH_TABLE_SIZE (64)
#define BUF_SIZE 25
#define HASH_INDEX(s, x) ((int)((x) & ((s) - 1)))
#define FRUDATA_PTR(_X) ((frutree_frunode_t *)(((hashdata_t *)(_X))->data))
#define LOCDATA_PTR(_X) ((frutree_locnode_t *)(((hashdata_t *)(_X))->data))
#define PORTDATA_PTR(_X) ((frutree_portnode_t *)(((hashdata_t *)(_X))->data))
/* Hash table structure */
typedef struct frutree_hash_elm {
picl_nodehdl_t hdl;
void *nodep;
struct frutree_hash_elm *nextp;
} frutree_hashelm_t;
typedef struct {
int hash_size;
frutree_hashelm_t **tbl;
} frutree_hash_t;
typedef struct {
frutree_datatype_t type;
void *data;
} hashdata_t;
typedef int (*callback_t)(picl_nodehdl_t, void *);
typedef enum {
INIT_FRU = 0x0,
CREATE_DEVICES_ENTRIES,
CONFIGURE_FRU,
UNCONFIGURE_FRU,
CPU_OFFLINE,
CPU_ONLINE,
HANDLE_CONFIGURE,
HANDLE_UNCONFIGURE,
HANDLE_INSERT,
HANDLE_REMOVE,
HANDLE_LOCSTATE_CHANGE,
POST_COND_EVENT,
POST_EVENTS
} action_t;
typedef struct {
action_t action;
void *data;
} frutree_dr_arg_t;
typedef struct event_queue {
frutree_dr_arg_t arg;
struct event_queue *next;
}ev_queue_t;
typedef struct {
char node_name[PICL_PROPNAMELEN_MAX];
picl_nodehdl_t retnodeh;
} frutree_callback_data_t;
typedef struct remove_list {
picl_nodehdl_t nodeh;
struct remove_list *next;
} delete_list_t;
typedef struct {
frutree_frunode_t *frup;
delete_list_t *first;
} frutree_init_callback_arg_t;
boolean_t frutree_connects_initiated = B_FALSE;
static ev_queue_t *queue_head = NULL;
static ev_queue_t *queue_tail = NULL;
static pthread_mutex_t ev_mutex;
static pthread_cond_t ev_cond;
static frutree_hash_t node_hash_table = {0, NULL};
static picl_nodehdl_t chassish = 0;
static picl_nodehdl_t frutreeh = 0;
static picl_nodehdl_t rooth = 0;
static picl_nodehdl_t platformh = 0;
static boolean_t post_picl_events = B_FALSE;
static int piclevent_pending = 0;
static char conf_file[MAXPATHLEN];
static char sys_name[SYS_NMLN];
static mutex_t piclevent_mutex = DEFAULTMUTEX;
static cond_t piclevent_completed_cv = DEFAULTCV;
static rwlock_t hash_lock;
static pthread_t tid;
static void *dr_thread(void *);
static pthread_t init_threadID;
static pthread_t monitor_tid;
static pthread_mutex_t monitor_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t monitor_cv = PTHREAD_COND_INITIALIZER;
static int fini_called = 0;
static void *monitor_node_status(void *);
static ev_queue_t *remove_from_queue(void);
static picl_errno_t handle_chassis_configure(frutree_frunode_t *frup);
/*
* location states.
*/
static char *loc_state[] = {
PICLEVENTARGVAL_UNKNOWN,
PICLEVENTARGVAL_EMPTY,
PICLEVENTARGVAL_CONNECTED,
PICLEVENTARGVAL_DISCONNECTED,
PICLEVENTARGVAL_CONNECTING,
PICLEVENTARGVAL_DISCONNECTING,
NULL
};
/*
* fru states.
*/
static char *fru_state[] = {
PICLEVENTARGVAL_UNKNOWN,
PICLEVENTARGVAL_CONFIGURED,
PICLEVENTARGVAL_UNCONFIGURED,
PICLEVENTARGVAL_CONFIGURING,
PICLEVENTARGVAL_UNCONFIGURING,
NULL
};
/*
* fru condition.
*/
static char *fru_cond[] = {
PICLEVENTARGVAL_UNKNOWN,
PICLEVENTARGVAL_FAILED,
PICLEVENTARGVAL_FAILING,
PICLEVENTARGVAL_OK,
PICLEVENTARGVAL_TESTING,
NULL
};
/*
* port states.
*/
static char *port_state[] = {
PICLEVENTARGVAL_DOWN,
PICLEVENTARGVAL_UP,
PICLEVENTARGVAL_UNKNOWN,
NULL
};
/*
* port condition.
*/
static char *port_cond[] = {
PICLEVENTARGVAL_OK,
PICLEVENTARGVAL_FAILING,
PICLEVENTARGVAL_FAILED,
PICLEVENTARGVAL_TESTING,
PICLEVENTARGVAL_UNKNOWN,
NULL
};
/* mapping between libcfgadm error codes to picl error codes */
static const int cfg2picl_errmap[][2] = {
{CFGA_OK, PICL_SUCCESS},
{CFGA_NACK, PICL_NORESPONSE},
{CFGA_NOTSUPP, PICL_NOTSUPPORTED},
{CFGA_OPNOTSUPP, PICL_NOTSUPPORTED},
{CFGA_PRIV, PICL_FAILURE},
{CFGA_BUSY, PICL_TREEBUSY},
{CFGA_SYSTEM_BUSY, PICL_TREEBUSY},
{CFGA_DATA_ERROR, PICL_FAILURE},
{CFGA_LIB_ERROR, PICL_FAILURE},
{CFGA_NO_LIB, PICL_FAILURE},
{CFGA_INSUFFICENT_CONDITION, PICL_FAILURE},
{CFGA_INVAL, PICL_INVALIDARG},
{CFGA_ERROR, PICL_FAILURE},
{CFGA_APID_NOEXIST, PICL_NODENOTFOUND},
{CFGA_ATTR_INVAL, PICL_INVALIDARG}
};
/* local functions */
static void piclfrutree_register(void);
static void piclfrutree_init(void);
static void piclfrutree_fini(void);
static void * init_thread(void *);
static void frutree_wd_evhandler(const char *, const void *, size_t, void *);
static void frutree_dr_apstate_change_evhandler(const char *, const void *,
size_t, void *);
static void frutree_dr_req_evhandler(const char *, const void *,
size_t, void *);
static void frutree_cpu_state_change_evhandler(const char *, const void *,
size_t, void *);
static void init_queue(void);
static void frutree_get_env();
static picl_errno_t hash_init(void);
static picl_errno_t hash_remove_entry(picl_nodehdl_t);
static picl_errno_t hash_lookup_entry(picl_nodehdl_t, void **);
static void hash_destroy();
static picl_errno_t initialize_frutree();
static picl_errno_t update_loc_state(frutree_locnode_t *, boolean_t *);
static int is_autoconfig_enabled(char *);
static picl_errno_t do_action(picl_nodehdl_t, int action, void *);
static picl_errno_t probe_fru(frutree_frunode_t *, boolean_t);
static picl_errno_t handle_fru_unconfigure(frutree_frunode_t *);
static picl_errno_t update_loc_state(frutree_locnode_t *, boolean_t *);
static picl_errno_t update_fru_state(frutree_frunode_t *, boolean_t *);
static picl_errno_t update_port_state(frutree_portnode_t *, boolean_t);
static picl_errno_t configure_fru(frutree_frunode_t *, cfga_flags_t);
static picl_errno_t post_piclevent(const char *, char *, char *,
picl_nodehdl_t, frutree_wait_t);
static picl_errno_t fru_init(frutree_frunode_t *);
/* External functions */
extern boolean_t is_fru_present_under_location(frutree_locnode_t *);
extern int kstat_port_state(frutree_port_type_t, char *, int);
extern int kstat_port_cond(frutree_port_type_t, char *, int);
extern picl_errno_t probe_libdevinfo(frutree_frunode_t *,
frutree_device_args_t **, boolean_t);
extern picl_errno_t get_scsislot_name(char *, char *, char *);
extern picl_errno_t probe_for_scsi_frus(frutree_frunode_t *);
extern picl_errno_t get_fru_path(char *, frutree_frunode_t *);
extern picl_errno_t scsi_info_init();
extern void scsi_info_fini();
extern picl_errno_t get_port_info(frutree_portnode_t *);
extern char *strtok_r(char *s1, const char *s2, char **lasts);
/* Plugin initialization */
static picld_plugin_reg_t frutree_reg_info = {
PICLD_PLUGIN_VERSION_1,
PICLD_PLUGIN_CRITICAL,
"SUNW_piclfrutree",
piclfrutree_init,
piclfrutree_fini
};
/* ptree entry points */
static void
piclfrutree_register(void)
{
FRUTREE_DEBUG0(FRUTREE_INIT, "piclfrutree register");
(void) picld_plugin_register(&frutree_reg_info);
}
static void
piclfrutree_init(void)
{
FRUTREE_DEBUG0(FRUTREE_INIT, "piclfrutree_init begin");
(void) rwlock_init(&hash_lock, USYNC_THREAD, NULL);
fini_called = 0;
/* read the environment variables */
frutree_get_env();
if (sysinfo(SI_PLATFORM, sys_name, sizeof (sys_name)) == -1) {
return;
}
if (hash_init() != PICL_SUCCESS) {
return;
}
if (initialize_frutree() != PICL_SUCCESS) {
return;
}
/* initialize the event queue */
(void) init_queue();
(void) pthread_cond_init(&ev_cond, NULL);
(void) pthread_mutex_init(&ev_mutex, NULL);
if (pthread_create(&tid, NULL, &dr_thread, NULL) != 0) {
return;
}
/* register for picl events */
if (ptree_register_handler(PICLEVENT_DR_AP_STATE_CHANGE,
frutree_dr_apstate_change_evhandler, NULL) !=
PICL_SUCCESS) {
return;
}
if (ptree_register_handler(PICLEVENT_DR_REQ,
frutree_dr_req_evhandler, NULL) != PICL_SUCCESS) {
return;
}
if (ptree_register_handler(PICLEVENT_CPU_STATE_CHANGE,
frutree_cpu_state_change_evhandler, NULL) !=
PICL_SUCCESS) {
return;
}
if (ptree_register_handler(PICLEVENT_STATE_CHANGE,
frutree_wd_evhandler, NULL) != PICL_SUCCESS) {
return;
}
FRUTREE_DEBUG0(FRUTREE_INIT, "piclfrutree_init end");
}
static void
piclfrutree_fini(void)
{
ev_queue_t *event = NULL;
void *exitval;
FRUTREE_DEBUG0(EVENTS, "piclfrutree_fini begin");
fini_called = 1;
/* unregister event handlers */
(void) ptree_unregister_handler(PICLEVENT_DR_AP_STATE_CHANGE,
frutree_dr_apstate_change_evhandler, NULL);
(void) ptree_unregister_handler(PICLEVENT_DR_REQ,
frutree_dr_req_evhandler, NULL);
(void) ptree_unregister_handler(PICLEVENT_CPU_STATE_CHANGE,
frutree_cpu_state_change_evhandler, NULL);
(void) ptree_unregister_handler(PICLEVENT_STATE_CHANGE,
frutree_wd_evhandler, NULL);
/* flush the event queue */
(void) pthread_mutex_lock(&ev_mutex);
event = remove_from_queue();
while (event) {
free(event);
event = remove_from_queue();
}
queue_head = queue_tail = NULL;
(void) pthread_cond_broadcast(&ev_cond);
(void) pthread_mutex_unlock(&ev_mutex);
(void) pthread_cancel(tid);
(void) pthread_join(tid, &exitval);
(void) pthread_cancel(monitor_tid);
(void) pthread_join(monitor_tid, &exitval);
(void) pthread_cancel(init_threadID);
(void) pthread_join(init_threadID, &exitval);
hash_destroy();
(void) ptree_delete_node(frutreeh);
(void) ptree_destroy_node(frutreeh);
frutree_connects_initiated = B_FALSE;
chassish = frutreeh = rooth = platformh = 0;
post_picl_events = B_FALSE;
piclevent_pending = 0;
FRUTREE_DEBUG0(EVENTS, "piclfrutree_fini end");
}
/* read the ENVIRONMENT variables and initialize tunables */
static void
frutree_get_env()
{
char *val;
int intval = 0;
/* read frutree debug flag value */
if (val = getenv(FRUTREE_DEBUG)) {
errno = 0;
intval = strtol(val, (char **)NULL, 0);
if (errno == 0) {
frutree_debug = intval;
FRUTREE_DEBUG1(PRINT_ALL, "SUNW_frutree:debug = %x",
frutree_debug);
}
}
/* read poll timeout value */
if (val = getenv(FRUTREE_POLL_TIMEOUT)) {
errno = 0;
intval = strtol(val, (char **)NULL, 0);
if (errno == 0) {
frutree_poll_timeout = intval;
}
}
/* read drwait time value */
if (val = getenv(FRUTREE_DRWAIT)) {
errno = 0;
intval = strtol(val, (char **)NULL, 0);
if (errno == 0) {
frutree_drwait_time = intval;
}
}
}
/*
* callback function for ptree_walk_tree_class to get the
* node handle of node
* matches a node with same class and name
*/
static int
frutree_get_nodehdl(picl_nodehdl_t nodeh, void *c_args)
{
picl_errno_t rc;
char name[PICL_PROPNAMELEN_MAX];
frutree_callback_data_t *fru_arg;
if (c_args == NULL)
return (PICL_INVALIDARG);
fru_arg = (frutree_callback_data_t *)c_args;
if ((rc = ptree_get_propval_by_name(nodeh, PICL_PROP_NAME, name,
sizeof (name))) != PICL_SUCCESS) {
return (rc);
}
if (strcmp(fru_arg->node_name, name) == 0) {
fru_arg->retnodeh = nodeh;
return (PICL_WALK_TERMINATE);
}
return (PICL_WALK_CONTINUE);
}
/* queue implementation (used to queue hotswap events) */
static void
init_queue(void)
{
queue_head = NULL;
queue_tail = NULL;
}
/* add an event to the queue */
static int
add_to_queue(frutree_dr_arg_t dr_data)
{
ev_queue_t *new_event;
new_event = (ev_queue_t *)malloc(sizeof (ev_queue_t));
if (new_event == NULL)
return (PICL_NOSPACE);
new_event->arg.action = dr_data.action;
new_event->arg.data = dr_data.data;
new_event->next = NULL;
if (queue_head == NULL) {
queue_head = new_event;
} else {
queue_tail->next = new_event;
}
queue_tail = new_event;
return (PICL_SUCCESS);
}
static ev_queue_t *
remove_from_queue(void)
{
ev_queue_t *event = NULL;
if (queue_head == NULL)
return (NULL);
event = queue_head;
queue_head = queue_head->next;
if (queue_head == NULL)
queue_tail = NULL;
return (event);
}
/*
* event handler for watchdog expiry event (picl-state-change) event on
* watchdog-timer node
*/
/* ARGSUSED */
static void
frutree_wd_evhandler(const char *ename, const void *earg, size_t size,
void *cookie)
{
nvlist_t *nvlp;
char *wd_state = NULL;
picl_errno_t rc;
picl_nodehdl_t wd_nodehdl;
char value[PICL_PROPNAMELEN_MAX];
frutree_callback_data_t fru_arg;
if (ename == NULL)
return;
if (strncmp(ename, PICLEVENT_STATE_CHANGE,
strlen(PICLEVENT_STATE_CHANGE))) {
return;
}
if (nvlist_unpack((char *)earg, size, &nvlp, NULL)) {
return;
}
if (nvlist_lookup_uint64(nvlp, PICLEVENTARG_NODEHANDLE,
&wd_nodehdl) == -1) {
nvlist_free(nvlp);
return;
}
if (nvlist_lookup_string(nvlp, PICLEVENTARG_STATE,
&wd_state) != 0) {
nvlist_free(nvlp);
return;
}
if ((rc = ptree_get_propval_by_name(wd_nodehdl,
PICL_PROP_CLASSNAME, value, sizeof (value))) != PICL_SUCCESS) {
nvlist_free(nvlp);
return;
}
/* if the event is not of watchdog-timer, return */
if (strcmp(value, PICL_CLASS_WATCHDOG_TIMER) != 0) {
nvlist_free(nvlp);
return;
}
FRUTREE_DEBUG1(EVENTS, "frutree:Received WD event(%s)", wd_state);
/* frutree plugin handles only watchdog expiry events */
if (strcmp(wd_state, PICL_PROPVAL_WD_STATE_EXPIRED) != 0) {
nvlist_free(nvlp);
return;
}
if ((rc = ptree_get_propval_by_name(wd_nodehdl,
PICL_PROP_WATCHDOG_ACTION, value, sizeof (value))) !=
PICL_SUCCESS) {
nvlist_free(nvlp);
return;
}
/* if action is none, dont do anything */
if (strcmp(value, PICL_PROPVAL_WD_ACTION_NONE) == 0) {
nvlist_free(nvlp);
return;
}
/* find the CPU nodehdl */
(void) strncpy(fru_arg.node_name, SANIBEL_PICLNODE_CPU,
sizeof (fru_arg.node_name));
fru_arg.retnodeh = 0;
if ((rc = ptree_walk_tree_by_class(chassish, PICL_CLASS_FRU,
&fru_arg, frutree_get_nodehdl)) != PICL_SUCCESS) {
nvlist_free(nvlp);
return;
}
if (fru_arg.retnodeh == NULL) {
nvlist_free(nvlp);
return;
}
if ((rc = post_piclevent(PICLEVENT_CONDITION_CHANGE,
PICLEVENTARGVAL_FAILED, NULL, fru_arg.retnodeh,
NO_WAIT)) != PICL_SUCCESS) {
FRUTREE_DEBUG3(EVENTS, PTREE_POST_PICLEVENT_ERR,
SANIBEL_PICLNODE_CPU, PICLEVENT_CONDITION_CHANGE, rc);
}
nvlist_free(nvlp);
}
/*
* event handler for dr_ap_state_change event
* - determine the event type and queue it in dr_queue to handle it
*/
/* ARGSUSED */
static void
frutree_dr_apstate_change_evhandler(const char *ename, const void *earg,
size_t size, void *cookie)
{
nvlist_t *nvlp;
char *name = NULL;
char *ap_id = NULL;
char *hint = NULL;
picl_nodehdl_t nodeh, childh;
hashdata_t *hashptr = NULL;
frutree_dr_arg_t dr_arg;
frutree_frunode_t *frup = NULL;
frutree_locnode_t *locp = NULL;
frutree_callback_data_t fru_arg;
boolean_t state_changed = B_FALSE;
if (ename == NULL)
return;
if (strncmp(ename, PICLEVENT_DR_AP_STATE_CHANGE,
strlen(PICLEVENT_DR_AP_STATE_CHANGE)) != 0) {
return;
}
if (nvlist_unpack((char *)earg, size, &nvlp, NULL)) {
return;
}
if (nvlist_lookup_string(nvlp, PICLEVENTARG_AP_ID, &ap_id) == -1) {
nvlist_free(nvlp);
return;
}
if (nvlist_lookup_string(nvlp, PICLEVENTARG_HINT, &hint) == -1) {
nvlist_free(nvlp);
return;
}
/* check for empty strings */
if (!ap_id || !hint) {
FRUTREE_DEBUG0(EVENTS, "Empty hint/ap_id");
nvlist_free(nvlp);
return;
}
/* get the location name */
name = strrchr(ap_id, ':');
if (name == NULL) {
name = ap_id;
} else {
name++;
}
/* find the loc object */
(void) strncpy(fru_arg.node_name, name, sizeof (fru_arg.node_name));
fru_arg.retnodeh = 0;
if (ptree_walk_tree_by_class(chassish, PICL_CLASS_LOCATION,
&fru_arg, frutree_get_nodehdl) != PICL_SUCCESS) {
nvlist_free(nvlp);
return;
}
if (fru_arg.retnodeh == NULL) {
nvlist_free(nvlp);
return;
}
nodeh = fru_arg.retnodeh;
if (hash_lookup_entry(nodeh, (void **)&hashptr) != PICL_SUCCESS) {
nvlist_free(nvlp);
return;
}
locp = LOCDATA_PTR(hashptr);
if (locp == NULL) {
nvlist_free(nvlp);
return;
}
if (strcmp(hint, DR_HINT_INSERT) == 0) {
dr_arg.action = HANDLE_INSERT;
dr_arg.data = locp;
(void) pthread_mutex_lock(&ev_mutex);
if (add_to_queue(dr_arg) != PICL_SUCCESS) {
(void) pthread_mutex_unlock(&ev_mutex);
nvlist_free(nvlp);
return;
}
(void) pthread_cond_signal(&ev_cond);
(void) pthread_mutex_unlock(&ev_mutex);
nvlist_free(nvlp);
return;
}
if (strcmp(hint, DR_HINT_REMOVE) == 0) {
dr_arg.action = HANDLE_REMOVE;
dr_arg.data = locp;
(void) pthread_mutex_lock(&ev_mutex);
if (add_to_queue(dr_arg) != PICL_SUCCESS) {
(void) pthread_mutex_unlock(&ev_mutex);
nvlist_free(nvlp);
return;
}
(void) pthread_cond_signal(&ev_cond);
(void) pthread_mutex_unlock(&ev_mutex);
nvlist_free(nvlp);
return;
}
if (strcmp(hint, DR_RESERVED_ATTR) != 0) { /* unknown event */
nvlist_free(nvlp);
return;
}
/* handle DR_RESERVED_ATTR HINT */
/* check if this is a fru event */
if (ptree_get_propval_by_name(locp->locnodeh, PICL_PROP_CHILD,
&childh, sizeof (childh)) == PICL_SUCCESS) {
/* get the child fru information */
if (hash_lookup_entry(childh, (void **)&hashptr) ==
PICL_SUCCESS) {
frup = FRUDATA_PTR(hashptr);
}
}
if (frup == NULL) {
nvlist_free(nvlp);
return;
}
(void) pthread_mutex_lock(&frup->mutex);
if (frup->dr_in_progress) {
/* dr in progress, neglect the event */
(void) pthread_mutex_unlock(&frup->mutex);
nvlist_free(nvlp);
return;
}
(void) pthread_mutex_unlock(&frup->mutex);
if (update_fru_state(frup, &state_changed) != PICL_SUCCESS) {
nvlist_free(nvlp);
return;
}
if (state_changed) {
(void) pthread_mutex_lock(&frup->mutex);
/* figure out if this is config/unconfig operation */
if (frup->state == FRU_STATE_CONFIGURED) {
dr_arg.action = HANDLE_CONFIGURE;
dr_arg.data = frup;
} else if (frup->state == FRU_STATE_UNCONFIGURED) {
dr_arg.action = HANDLE_UNCONFIGURE;
dr_arg.data = frup;
}
(void) pthread_mutex_unlock(&frup->mutex);
(void) pthread_mutex_lock(&ev_mutex);
if (add_to_queue(dr_arg) != PICL_SUCCESS) {
(void) pthread_mutex_unlock(&ev_mutex);
nvlist_free(nvlp);
return;
}
(void) pthread_cond_signal(&ev_cond);
(void) pthread_mutex_unlock(&ev_mutex);
nvlist_free(nvlp);
return;
}
/* check if this event is related to location */
(void) pthread_mutex_lock(&locp->mutex);
if (locp->dr_in_progress) {
/* dr in progress, neglect the event */
(void) pthread_mutex_unlock(&locp->mutex);
nvlist_free(nvlp);
return;
}
(void) pthread_mutex_unlock(&locp->mutex);
if (update_loc_state(locp, &state_changed) != PICL_SUCCESS) {
nvlist_free(nvlp);
return;
}
if (state_changed) { /* location state has changed */
dr_arg.action = HANDLE_LOCSTATE_CHANGE;
dr_arg.data = locp;
(void) pthread_mutex_lock(&ev_mutex);
if (add_to_queue(dr_arg) != PICL_SUCCESS) {
(void) pthread_mutex_unlock(&ev_mutex);
nvlist_free(nvlp);
return;
}
(void) pthread_cond_signal(&ev_cond);
(void) pthread_mutex_unlock(&ev_mutex);
nvlist_free(nvlp);
return;
}
/* duplicate event */
nvlist_free(nvlp);
}
/*
* Event handler for dr_req event
*/
/* ARGSUSED */
static void
frutree_dr_req_evhandler(const char *ename, const void *earg, size_t size,
void *cookie)
{
nvlist_t *nvlp;
char *name = NULL;
char *ap_id = NULL;
char *dr_req = NULL;
picl_nodehdl_t nodeh;
frutree_dr_arg_t dr_arg;
hashdata_t *hashptr = NULL;
frutree_frunode_t *frup = NULL;
frutree_callback_data_t fru_arg;
if (ename == NULL)
return;
if (strncmp(ename, PICLEVENT_DR_REQ, strlen(PICLEVENT_DR_REQ)) != 0) {
return;
}
if (nvlist_unpack((char *)earg, size, &nvlp, NULL)) {
return;
}
if (nvlist_lookup_string(nvlp, PICLEVENTARG_AP_ID, &ap_id) == -1) {
nvlist_free(nvlp);
return;
}
if (nvlist_lookup_string(nvlp, PICLEVENTARG_DR_REQ_TYPE,
&dr_req) == -1) {
nvlist_free(nvlp);
return;
}
if (!ap_id || !dr_req) {
FRUTREE_DEBUG0(EVENTS, "Empty dr_req/ap_id");
nvlist_free(nvlp);
return;
}
/* get the location name */
name = strrchr(ap_id, ':');
if (name == NULL) {
name = ap_id;
} else {
name++;
}
if (name == NULL) {
nvlist_free(nvlp);
return;
}
FRUTREE_DEBUG2(EVENTS, "DR_REQ:%s on %s", dr_req, name);
(void) strncpy(fru_arg.node_name, name, sizeof (fru_arg.node_name));
fru_arg.retnodeh = 0;
if (ptree_walk_tree_by_class(frutreeh, PICL_CLASS_FRU,
&fru_arg, frutree_get_nodehdl) != PICL_SUCCESS) {
nvlist_free(nvlp);
return;
}
if (fru_arg.retnodeh == NULL) {
nvlist_free(nvlp);
return;
}
nodeh = fru_arg.retnodeh;
/* find the fru object */
if (hash_lookup_entry(nodeh, (void **)&hashptr) != PICL_SUCCESS) {
nvlist_free(nvlp);
return;
}
frup = FRUDATA_PTR(hashptr);
if (frup == NULL) {
nvlist_free(nvlp);
return;
}
if (strcmp(dr_req, DR_REQ_INCOMING_RES) == 0) {
dr_arg.action = CONFIGURE_FRU;
dr_arg.data = frup;
} else if (strcmp(dr_req, DR_REQ_OUTGOING_RES) == 0) {
dr_arg.action = UNCONFIGURE_FRU;
dr_arg.data = frup;
} else {
nvlist_free(nvlp);
return;
}
(void) pthread_mutex_lock(&ev_mutex);
if (add_to_queue(dr_arg) != PICL_SUCCESS) {
(void) pthread_mutex_unlock(&ev_mutex);
nvlist_free(nvlp);
return;
}
(void) pthread_cond_signal(&ev_cond);
(void) pthread_mutex_unlock(&ev_mutex);
nvlist_free(nvlp);
}
/*
* Event handler for cpu_state_change event
*/
/* ARGSUSED */
static void
frutree_cpu_state_change_evhandler(const char *ename, const void *earg,
size_t size, void *cookie)
{
char *hint = NULL;
nvlist_t *nvlp;
frutree_frunode_t *frup = NULL;
hashdata_t *hashptr = NULL;
picl_nodehdl_t nodeh;
frutree_dr_arg_t dr_arg;
if (ename == NULL)
return;
if (strncmp(ename, PICLEVENT_CPU_STATE_CHANGE,
strlen(PICLEVENT_CPU_STATE_CHANGE)) != 0) {
return;
}
if (nvlist_unpack((char *)earg, size, &nvlp, NULL)) {
return;
}
if (nvlist_lookup_uint64(nvlp, PICLEVENTARG_NODEHANDLE, &nodeh) == -1) {
nvlist_free(nvlp);
return;
}
if (nvlist_lookup_string(nvlp, PICLEVENTARG_CPU_EV_TYPE, &hint) == -1) {
nvlist_free(nvlp);
return;
}
if (hash_lookup_entry(nodeh, (void **)&hashptr) != PICL_SUCCESS) {
nvlist_free(nvlp);
return;
}
frup = FRUDATA_PTR(hashptr);
if (frup == NULL) {
nvlist_free(nvlp);
return;
}
if (strcmp(hint, PICLEVENTARGVAL_OFFLINE) == 0) {
dr_arg.action = CPU_OFFLINE;
dr_arg.data = frup;
} else if (strcmp(hint, PICLEVENTARGVAL_ONLINE) == 0) {
dr_arg.action = CPU_ONLINE;
dr_arg.data = frup;
} else {
nvlist_free(nvlp);
return;
}
(void) pthread_mutex_lock(&ev_mutex);
if (add_to_queue(dr_arg) != PICL_SUCCESS) {
(void) pthread_mutex_unlock(&ev_mutex);
nvlist_free(nvlp);
return;
}
(void) pthread_cond_signal(&ev_cond);
(void) pthread_mutex_unlock(&ev_mutex);
nvlist_free(nvlp);
}
static void
attach_driver(char *driver)
{
char cmd[BUF_SIZE];
cmd[0] = '\0';
(void) snprintf(cmd, sizeof (cmd), "%s %s",
DEVFSADM_CMD, driver);
(void) pclose(popen(cmd, "r"));
}
/*
* Find the node in platform tree with given devfs-path.
* ptree_find_node is getting a node with devfs-path /pci@1f,0/pci@1,1
* when we want to find node with /pci@1f,0/pci@1. The fix
* is required in libpicltree. For now use ptree_walk_tree_by_class
* to find the node.
*/
static int
find_ref_parent(picl_nodehdl_t nodeh, void *c_args)
{
picl_prophdl_t proph;
ptree_propinfo_t propinfo;
void *vbuf;
frutree_callback_data_t *fru_arg;
if (c_args == NULL)
return (PICL_INVALIDARG);
fru_arg = (frutree_callback_data_t *)c_args;
if (ptree_get_prop_by_name(nodeh, PICL_PROP_DEVFS_PATH,
&proph) != PICL_SUCCESS) {
return (PICL_WALK_CONTINUE);
}
if (ptree_get_propinfo(proph, &propinfo) != PICL_SUCCESS) {
return (PICL_WALK_CONTINUE);
}
vbuf = alloca(propinfo.piclinfo.size);
if (vbuf == NULL)
return (PICL_WALK_CONTINUE);
if (ptree_get_propval(proph, vbuf,
propinfo.piclinfo.size) != PICL_SUCCESS) {
return (PICL_WALK_CONTINUE);
}
/* compare the devfs_path */
if (strcmp(fru_arg->node_name, (char *)vbuf) == 0) {
fru_arg->retnodeh = nodeh;
return (PICL_WALK_TERMINATE);
}
return (PICL_WALK_CONTINUE);
}
/*
* Find the reference node in /platform tree
* return : 0 - if node is not found
*/
static picl_nodehdl_t
get_reference_handle(picl_nodehdl_t nodeh)
{
picl_prophdl_t proph;
ptree_propinfo_t propinfo;
void *vbuf;
picl_errno_t rc = PICL_SUCCESS;
char devfs_path[PICL_PROPNAMELEN_MAX];
char value[PICL_PROPNAMELEN_MAX];
char class[PICL_PROPNAMELEN_MAX];
frutree_callback_data_t fru_arg;
picl_nodehdl_t refhdl = 0, ref_parent = 0, nodehdl = 0;
/*
* for fru node, get the devfspath and bus-addr of
* its parent.
*/
if (ptree_get_propval_by_name(nodeh, PICL_PROP_CLASSNAME,
class, sizeof (class)) != PICL_SUCCESS) {
return (0);
}
if (strcmp(class, PICL_CLASS_FRU) == 0) {
if (ptree_get_propval_by_name(nodeh, PICL_PROP_PARENT,
&nodehdl, sizeof (nodehdl)) != PICL_SUCCESS) {
return (0);
}
} else if (strcmp(class, PICL_CLASS_PORT) == 0) {
nodehdl = nodeh;
} else {
return (0);
}
if (ptree_get_propval_by_name(nodehdl, PICL_PROP_DEVFS_PATH,
devfs_path, sizeof (devfs_path)) != PICL_SUCCESS) {
return (0);
}
if (ptree_get_propval_by_name(nodehdl, PICL_PROP_BUS_ADDR,
value, sizeof (value)) != PICL_SUCCESS) {
return (0);
}
/* find the node with same devfs-path */
(void) strncpy(fru_arg.node_name, devfs_path,
sizeof (fru_arg.node_name));
fru_arg.retnodeh = 0;
if (ptree_walk_tree_by_class(platformh, NULL,
(void *)&fru_arg, find_ref_parent) != PICL_SUCCESS) {
return (0);
}
if (fru_arg.retnodeh == NULL)
return (0);
ref_parent = fru_arg.retnodeh;
/* traverse thru childeren and find the reference node */
rc = ptree_get_propval_by_name(ref_parent, PICL_PROP_CHILD,
&refhdl, sizeof (picl_nodehdl_t));
while (rc == PICL_SUCCESS) {
nodehdl = refhdl;
rc = ptree_get_propval_by_name(refhdl, PICL_PROP_PEER,
&refhdl, sizeof (picl_nodehdl_t));
/*
* compare the bus_addr or Unit address
* format of bus_addr can be either (1,3 or 0x6)
*/
if (ptree_get_prop_by_name(nodehdl, PICL_PROP_BUS_ADDR,
&proph) != PICL_SUCCESS) {
if (ptree_get_prop_by_name(nodehdl,
PICL_PROP_UNIT_ADDRESS, &proph) !=
PICL_SUCCESS) {
continue;
}
}
if (ptree_get_propinfo(proph, &propinfo) != PICL_SUCCESS) {
continue;
}
vbuf = alloca(propinfo.piclinfo.size);
if (vbuf == NULL)
continue;
if (ptree_get_propval(proph, vbuf,
propinfo.piclinfo.size) != PICL_SUCCESS) {
continue;
}
if (strchr((char *)vbuf, ',') != NULL) {
if (strcmp(value, (char *)vbuf) == 0) {
return (nodehdl);
}
} else {
if (strtoul((char *)vbuf, NULL, 16) ==
strtoul(value, NULL, 16)) {
return (nodehdl);
}
}
}
return (0);
}
/* Hash Table Management */
static void
free_data(frutree_datatype_t type, hashdata_t *datap)
{
frutree_frunode_t *frup = NULL;
frutree_locnode_t *locp = NULL;
frutree_portnode_t *portp = NULL;
if (datap == NULL) {
return;
}
switch (type) {
case FRU_TYPE:
frup = (frutree_frunode_t *)datap->data;
free(frup->name);
(void) pthread_mutex_destroy(&frup->mutex);
(void) pthread_cond_destroy(&frup->cond_cv);
(void) pthread_cond_destroy(&frup->busy_cond_cv);
free(frup);
break;
case LOC_TYPE:
locp = (frutree_locnode_t *)datap->data;
free(locp->name);
(void) pthread_mutex_destroy(&locp->mutex);
(void) pthread_cond_destroy(&locp->cond_cv);
free(locp);
break;
case PORT_TYPE:
portp = (frutree_portnode_t *)datap->data;
free(portp->name);
free(portp);
break;
}
free(datap);
}
/*
* Initialize the hash table
*/
static picl_errno_t
hash_init(void)
{
int i;
FRUTREE_DEBUG0(HASHTABLE, "hash_init begin");
node_hash_table.tbl = (frutree_hashelm_t **)malloc(
sizeof (frutree_hashelm_t *) * HASH_TABLE_SIZE);
if (node_hash_table.tbl == NULL) {
return (PICL_NOSPACE);
}
/* initialize each entry in hashtable */
node_hash_table.hash_size = HASH_TABLE_SIZE;
for (i = 0; i < node_hash_table.hash_size; ++i) {
node_hash_table.tbl[i] = NULL;
}
return (PICL_SUCCESS);
}
/*
* Destroy the hash table
*/
static void
hash_destroy(void)
{
int i;
frutree_hashelm_t *el;
hashdata_t *datap = NULL;
(void) rw_wrlock(&hash_lock);
if (node_hash_table.tbl == NULL) {
(void) rw_unlock(&hash_lock);
return;
}
/* loop thru each linked list in the table and free */
for (i = 0; i < node_hash_table.hash_size; ++i) {
while (node_hash_table.tbl[i] != NULL) {
el = node_hash_table.tbl[i];
node_hash_table.tbl[i] = el->nextp;
datap = (hashdata_t *)el->nodep;
free_data(datap->type, datap);
el->nodep = NULL;
free(el);
el = NULL;
}
}
free(node_hash_table.tbl);
(void) rw_unlock(&hash_lock);
}
/*
* Add an entry to the hash table
*/
static picl_errno_t
hash_add_entry(picl_nodehdl_t hdl, void *nodep)
{
int indx;
frutree_hashelm_t *el;
FRUTREE_DEBUG0(HASHTABLE, "hash_add_entry : begin");
(void) rw_wrlock(&hash_lock);
if (node_hash_table.tbl == NULL) {
(void) rw_unlock(&hash_lock);
return (PICL_NOTINITIALIZED);
}
el = (frutree_hashelm_t *)malloc(sizeof (frutree_hashelm_t));
if (el == NULL) {
(void) rw_unlock(&hash_lock);
return (PICL_NOSPACE);
}
el->hdl = hdl;
el->nodep = nodep;
el->nextp = NULL;
if (frutree_debug & HASHTABLE) {
picl_nodehdl_t nodeid;
nodeid = hdl;
cvt_ptree2picl(&nodeid);
FRUTREE_DEBUG1(HASHTABLE, "added node: %llx", nodeid);
}
indx = HASH_INDEX(node_hash_table.hash_size, hdl);
if (node_hash_table.tbl[indx] == NULL) {
/* first element for this index */
node_hash_table.tbl[indx] = el;
(void) rw_unlock(&hash_lock);
return (PICL_SUCCESS);
}
el->nextp = node_hash_table.tbl[indx];
node_hash_table.tbl[indx] = el;
(void) rw_unlock(&hash_lock);
return (PICL_SUCCESS);
}
/*
* Remove a hash entry from the table
*/
static picl_errno_t
hash_remove_entry(picl_nodehdl_t hdl)
{
int i;
hashdata_t *datap = NULL;
frutree_hashelm_t *prev, *cur;
(void) rw_wrlock(&hash_lock);
if (node_hash_table.tbl == NULL) {
(void) rw_unlock(&hash_lock);
return (PICL_NOTINITIALIZED);
}
i = HASH_INDEX(node_hash_table.hash_size, hdl);
/* check that the hash chain is not empty */
if (node_hash_table.tbl[i] == NULL) {
(void) rw_wrlock(&hash_lock);
return (PICL_NODENOTFOUND);
}
/* search hash chain for entry to be removed */
prev = NULL;
cur = node_hash_table.tbl[i];
while (cur) {
if (cur->hdl == hdl) {
if (prev == NULL) { /* 1st elem in hash chain */
node_hash_table.tbl[i] = cur->nextp;
} else {
prev->nextp = cur->nextp;
}
datap = (hashdata_t *)cur->nodep;
free_data(datap->type, datap);
cur->nodep = NULL;
free(cur);
cur = NULL;
if (frutree_debug & HASHTABLE) {
picl_nodehdl_t nodeid;
nodeid = hdl;
cvt_ptree2picl(&nodeid);
FRUTREE_DEBUG1(HASHTABLE, "removed node: %llx",
nodeid);
}
(void) rw_unlock(&hash_lock);
return (PICL_SUCCESS);
}
prev = cur;
cur = cur->nextp;
}
/* entry was not found */
(void) rw_unlock(&hash_lock);
return (PICL_NODENOTFOUND);
}
/*
* Lookup a handle in the table
*/
static picl_errno_t
hash_lookup_entry(picl_nodehdl_t hdl, void **nodepp)
{
int i;
frutree_hashelm_t *el;
FRUTREE_DEBUG1(HASHTABLE, "hash_lookup begin: %llx", hdl);
(void) rw_rdlock(&hash_lock);
if (node_hash_table.tbl == NULL) {
(void) rw_unlock(&hash_lock);
return (PICL_NOTINITIALIZED);
}
if (nodepp == NULL) {
(void) rw_unlock(&hash_lock);
return (PICL_INVALIDHANDLE);
}
i = HASH_INDEX(node_hash_table.hash_size, hdl);
if (node_hash_table.tbl[i] == NULL) {
(void) rw_unlock(&hash_lock);
return (PICL_NODENOTFOUND);
}
el = node_hash_table.tbl[i];
while (el) {
if (el->hdl == hdl) {
*nodepp = el->nodep;
(void) rw_unlock(&hash_lock);
return (PICL_SUCCESS);
}
el = el->nextp;
}
(void) rw_unlock(&hash_lock);
return (PICL_NODENOTFOUND);
}
/* create and initialize data structure for a loc node */
static picl_errno_t
make_loc_data(char *full_name, hashdata_t **hashptr)
{
char *name_copy;
frutree_locnode_t *locp;
hashdata_t *datap = NULL;
datap = (hashdata_t *)malloc(sizeof (hashdata_t));
if (datap == NULL) {
return (PICL_NOSPACE);
}
datap->type = LOC_TYPE;
/* allocate the data */
locp = (frutree_locnode_t *)malloc(sizeof (frutree_locnode_t));
if (locp == NULL) {
free(datap);
return (PICL_NOSPACE);
}
/* make a copy of the name */
name_copy = strdup(full_name);
if (name_copy == NULL) {
free(locp);
free(datap);
return (PICL_NOSPACE);
}
/* initialize the data */
locp->name = name_copy;
locp->locnodeh = 0;
locp->state = LOC_STATE_UNKNOWN;
locp->prev_state = LOC_STATE_UNKNOWN;
locp->cpu_node = B_FALSE;
locp->autoconfig_enabled = B_FALSE;
locp->state_mgr = UNKNOWN;
locp->dr_in_progress = B_FALSE;
(void) pthread_mutex_init(&locp->mutex, NULL);
(void) pthread_cond_init(&locp->cond_cv, NULL);
datap->data = locp;
*hashptr = datap;
return (PICL_SUCCESS);
}
/* create and initialize data structure for a fru node */
static picl_errno_t
make_fru_data(char *full_name, hashdata_t **hashptr)
{
char *name_copy;
frutree_frunode_t *frup;
hashdata_t *datap = NULL;
datap = (hashdata_t *)malloc(sizeof (hashdata_t));
if (datap == NULL) {
return (PICL_NOSPACE);
}
datap->type = FRU_TYPE;
/* allocate the data */
frup = (frutree_frunode_t *)malloc(sizeof (frutree_frunode_t));
if (frup == NULL) {
free(datap);
return (PICL_NOSPACE);
}
/* make a copy of the name */
name_copy = strdup(full_name);
if (name_copy == NULL) {
free(frup);
free(datap);
return (PICL_NOSPACE);
}
/* initialize the data */
frup->name = name_copy;
frup->frunodeh = 0;
frup->state = FRU_STATE_UNCONFIGURED;
frup->prev_state = FRU_STATE_UNKNOWN;
frup->cond = FRU_COND_UNKNOWN;
frup->prev_cond = FRU_COND_UNKNOWN;
frup->cpu_node = B_FALSE;
frup->autoconfig_enabled = B_FALSE;
frup->dr_in_progress = B_FALSE;
frup->busy = B_FALSE;
frup->state_mgr = UNKNOWN;
frup->fru_path[0] = '\0';
(void) pthread_mutex_init(&frup->mutex, NULL);
(void) pthread_cond_init(&frup->cond_cv, NULL);
(void) pthread_cond_init(&frup->busy_cond_cv, NULL);
datap->data = frup;
*hashptr = datap;
return (PICL_SUCCESS);
}
/* create and initialize data structure for a port node */
static picl_errno_t
make_port_data(char *full_name, hashdata_t **hashptr)
{
char *name_copy;
frutree_portnode_t *portp;
hashdata_t *datap = NULL;
datap = (hashdata_t *)malloc(sizeof (hashdata_t));
if (datap == NULL) {
return (PICL_NOSPACE);
}
datap->type = PORT_TYPE;
/* allocate the data */
portp = (frutree_portnode_t *)malloc(sizeof (frutree_portnode_t));
if (portp == NULL) {
free(datap);
return (PICL_NOSPACE);
}
/* make a copy of the name */
name_copy = strdup(full_name);
if (name_copy == NULL) {
free(portp);
free(datap);
return (PICL_NOSPACE);
}
/* initialize the data */
portp->name = name_copy;
portp->portnodeh = 0;
portp->state = PORT_STATE_UNKNOWN;
portp->cond = PORT_COND_UNKNOWN;
datap->data = portp;
*hashptr = datap;
return (PICL_SUCCESS);
}
/*
* utility routine to create table entries
*/
static picl_errno_t
create_table_entry(picl_prophdl_t tblhdl, picl_nodehdl_t refhdl, char *class)
{
picl_errno_t rc;
ptree_propinfo_t propinfo;
picl_prophdl_t prophdl[2];
char buf[PICL_CLASSNAMELEN_MAX];
/* first column is class */
if ((rc = ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION,
PICL_PTYPE_CHARSTRING, PICL_READ, PICL_CLASSNAMELEN_MAX,
PICL_PROP_CLASS, NULLREAD,
NULLWRITE)) != PICL_SUCCESS) {
return (rc);
}
if ((rc = ptree_create_prop(&propinfo, class,
&prophdl[0])) != PICL_SUCCESS) {
return (rc);
}
/* second column is reference property */
(void) snprintf(buf, sizeof (buf), "_%s_", class);
if ((rc = ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION,
PICL_PTYPE_REFERENCE, PICL_READ,
sizeof (picl_nodehdl_t), buf, NULLREAD,
NULLWRITE)) != PICL_SUCCESS) {
return (rc);
}
if ((rc = ptree_create_prop(&propinfo, &refhdl,
&prophdl[1])) != PICL_SUCCESS) {
return (rc);
}
/* add row to table */
if ((rc = ptree_add_row_to_table(tblhdl, 2, prophdl)) != PICL_SUCCESS) {
return (rc);
}
return (PICL_SUCCESS);
}
/*
* Utility routine to create picl property
*/
static picl_errno_t
create_property(int ptype, int pmode, size_t psize, char *pname,
int (*readfn)(ptree_rarg_t *, void *),
int (*writefn)(ptree_warg_t *, const void *),
picl_nodehdl_t nodeh, picl_prophdl_t *prophp, void *vbuf)
{
picl_errno_t rc;
ptree_propinfo_t propinfo;
picl_prophdl_t proph;
if (pname == NULL || vbuf == NULL) {
return (PICL_FAILURE);
}
if (ptype == PICL_PTYPE_TABLE) {
if ((rc = ptree_create_table((picl_prophdl_t *)vbuf))
!= PICL_SUCCESS) {
return (rc);
}
}
if ((rc = ptree_get_prop_by_name(nodeh, pname, &proph)) ==
PICL_SUCCESS) { /* property already exists */
return (rc);
}
rc = ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION,
ptype, pmode, psize, pname, readfn, writefn);
if (rc != PICL_SUCCESS) {
return (rc);
}
rc = ptree_create_and_add_prop(nodeh, &propinfo, vbuf, prophp);
if (rc != PICL_SUCCESS) {
return (rc);
}
return (PICL_SUCCESS);
}
/*
* create frutree node, chassis node
*/
static picl_errno_t
initialize_frutree()
{
int rc = PICL_SUCCESS;
hashdata_t *datap = NULL;
frutree_frunode_t *frup = NULL;
uint64_t ap_status_time;
FRUTREE_DEBUG0(FRUTREE_INIT, "initialize_frutree begin");
/* Get the root of the PICL tree */
if ((rc = ptree_get_root(&rooth)) != PICL_SUCCESS) {
return (rc);
}
FRUTREE_DEBUG1(FRUTREE_INIT, "roothdl = %llx", rooth);
/* create /frutree node */
if ((rc = ptree_create_and_add_node(rooth, PICL_NODE_FRUTREE,
PICL_CLASS_PICL, &frutreeh)) != PICL_SUCCESS) {
return (rc);
}
FRUTREE_DEBUG1(FRUTREE_INIT, "frutreeh = %llx", frutreeh);
/* create chassis node */
if ((rc = ptree_create_node(PICL_NODE_CHASSIS, PICL_CLASS_FRU,
&chassish)) != PICL_SUCCESS) {
return (rc);
}
FRUTREE_DEBUG1(FRUTREE_INIT, "chassish = %llx", chassish);
/* Allocate fru data */
if ((rc = make_fru_data(PICL_NODE_CHASSIS, &datap)) !=
PICL_SUCCESS) {
(void) ptree_destroy_node(chassish);
return (rc);
}
/* initialise chassis handle and parent handle */
frup = FRUDATA_PTR(datap);
frup->frunodeh = chassish;
/* Add the chassis node to the tree */
if ((rc = ptree_add_node(frutreeh, chassish)) != PICL_SUCCESS) {
free_data(datap->type, datap);
(void) ptree_destroy_node(chassish);
return (rc);
}
/* create chassis state property */
if ((rc = create_property(PICL_PTYPE_CHARSTRING,
PICL_READ, PICL_PROPNAMELEN_MAX, PICL_PROP_STATE,
NULLREAD, NULLWRITE, chassish, (picl_prophdl_t *)NULL,
PICLEVENTARGVAL_UNCONFIGURED)) != PICL_SUCCESS) {
free_data(datap->type, datap);
(void) ptree_delete_node(chassish);
(void) ptree_destroy_node(chassish);
return (rc);
}
ap_status_time = (uint64_t)(time(NULL));
if ((rc = create_property(PICL_PTYPE_TIMESTAMP, PICL_READ,
sizeof (ap_status_time), PICL_PROP_STATUS_TIME,
NULLREAD, NULLWRITE, chassish,
NULL, &ap_status_time)) != PICL_SUCCESS) {
free_data(datap->type, datap);
(void) ptree_delete_node(chassish);
(void) ptree_destroy_node(chassish);
return (rc);
}
/* save chassis info in hashtable */
if ((rc = hash_add_entry(chassish,
(void *)datap)) != PICL_SUCCESS) {
free_data(datap->type, datap);
(void) ptree_delete_node(chassish);
(void) ptree_destroy_node(chassish);
return (rc);
}
return (PICL_SUCCESS);
}
/*
* Read the temporary property created by platform specific
* plugin to get the config file name.
*/
static picl_errno_t
get_configuration_file()
{
picl_errno_t rc;
picl_prophdl_t proph;
char file_name[PICL_PROPNAMELEN_MAX];
if ((rc = ptree_get_prop_by_name(chassish,
PICL_PROP_CONF_FILE, &proph)) != PICL_SUCCESS) {
return (rc);
}
if ((rc = ptree_get_propval(proph, file_name,
sizeof (file_name))) != PICL_SUCCESS) {
return (rc);
}
(void) snprintf(conf_file, sizeof (conf_file),
PICLD_PLAT_PLUGIN_DIRF"%s", sys_name, file_name);
/* delete the tmp prop created by platform specific plugin */
(void) ptree_delete_prop(proph);
(void) ptree_destroy_prop(proph);
FRUTREE_DEBUG1(EVENTS, "Using %s conf file", conf_file);
return (PICL_SUCCESS);
}
/*
* Read the cfgadm data and get the latest information
*/
static picl_errno_t
get_cfgadm_state(cfga_list_data_t *data, char *ap_id)
{
int nlist;
cfga_err_t ap_list_err;
cfga_list_data_t *list = NULL;
char * const *p = &ap_id;
if (data == NULL || ap_id == NULL) {
return (PICL_INVALIDARG);
}
ap_list_err = config_list_ext(1, p, &list, &nlist, NULL,
NULL, NULL, 0);
if (ap_list_err != CFGA_OK) {
free(list);
return (cfg2picl_errmap[ap_list_err][1]);
}
(void) memcpy(data, list, sizeof (cfga_list_data_t));
free(list);
return (PICL_SUCCESS);
}
/*
* syncup with cfgadm data and read latest location state information
*/
static picl_errno_t
update_loc_state(frutree_locnode_t *locp, boolean_t *updated)
{
int i = 0;
cfga_list_data_t *list = NULL;
picl_errno_t rc, rc1;
char valbuf[PICL_PROPNAMELEN_MAX];
char slot_type[PICL_PROPNAMELEN_MAX];
uint64_t ap_status_time;
*updated = B_FALSE;
if (locp->state_mgr == PLUGIN_PVT) {
if ((rc = ptree_get_propval_by_name(locp->locnodeh,
PICL_PROP_STATE, (void *)valbuf,
PICL_PROPNAMELEN_MAX)) != PICL_SUCCESS) {
return (rc);
}
/* if there is a change in state, update the internal value */
if (strcmp(loc_state[locp->state], valbuf) != 0) {
ap_status_time = (uint64_t)(time(NULL));
if ((rc = ptree_update_propval_by_name(locp->locnodeh,
PICL_PROP_STATUS_TIME, (void *)&ap_status_time,
sizeof (ap_status_time))) != PICL_SUCCESS) {
FRUTREE_DEBUG3(EVENTS, PTREE_UPDATE_PROP_ERR,
PICL_PROP_STATUS_TIME, locp->name, rc);
}
*updated = B_TRUE;
locp->prev_state = locp->state;
for (i = 0; (loc_state[i] != NULL); i++) {
if (strcmp(loc_state[i], valbuf) == 0) {
locp->state = i;
return (PICL_SUCCESS);
}
}
}
return (PICL_SUCCESS);
} else if (locp->state_mgr == STATIC_LOC) {
return (PICL_SUCCESS);
}
/* get the info from the libcfgadm interface */
list = (cfga_list_data_t *)malloc(sizeof (cfga_list_data_t));
if (list == NULL) {
return (PICL_NOSPACE);
}
if ((rc = get_cfgadm_state(list, locp->name)) != PICL_SUCCESS) {
if ((rc1 = ptree_get_propval_by_name(locp->locnodeh,
PICL_PROP_SLOT_TYPE, slot_type,
sizeof (slot_type))) != PICL_SUCCESS) {
free(list);
return (rc1);
}
if (strcmp(slot_type, SANIBEL_SCSI_SLOT) != 0 &&
strcmp(slot_type, SANIBEL_IDE_SLOT) != 0) {
free(list);
return (rc);
}
/* this is a scsi location */
if (rc != PICL_NODENOTFOUND) {
free(list);
return (rc);
}
/*
* for scsi locations, if node is not found,
* consider location state as empty
*/
(void) pthread_mutex_lock(&locp->mutex);
if (locp->state != LOC_STATE_EMPTY) {
*updated = B_TRUE;
locp->prev_state = locp->state;
locp->state = LOC_STATE_EMPTY;
ap_status_time = (uint64_t)(time(NULL));
if ((rc = ptree_update_propval_by_name(locp->locnodeh,
PICL_PROP_STATUS_TIME, (void *)&ap_status_time,
sizeof (ap_status_time))) != PICL_SUCCESS) {
FRUTREE_DEBUG3(EVENTS, PTREE_UPDATE_PROP_ERR,
PICL_PROP_STATUS_TIME, locp->name, rc);
}
}
(void) pthread_mutex_unlock(&locp->mutex);
free(list);
return (PICL_SUCCESS);
}
(void) pthread_mutex_lock(&locp->mutex);
switch (list->ap_r_state) {
case CFGA_STAT_CONNECTED:
if (locp->state != LOC_STATE_CONNECTED) {
*updated = B_TRUE;
locp->prev_state = locp->state;
locp->state = LOC_STATE_CONNECTED;
}
break;
case CFGA_STAT_DISCONNECTED:
if (locp->state != LOC_STATE_DISCONNECTED) {
*updated = B_TRUE;
locp->prev_state = locp->state;
locp->state = LOC_STATE_DISCONNECTED;
}
break;
case CFGA_STAT_EMPTY:
if (locp->state != LOC_STATE_EMPTY) {
*updated = B_TRUE;
locp->prev_state = locp->state;
locp->state = LOC_STATE_EMPTY;
}
break;
default:
if (locp->state != LOC_STATE_UNKNOWN) {
*updated = B_TRUE;
locp->prev_state = locp->state;
locp->state = LOC_STATE_UNKNOWN;
}
}
if (*updated == B_TRUE) {
ap_status_time = (uint64_t)(time(NULL));
if ((rc = ptree_update_propval_by_name(locp->locnodeh,
PICL_PROP_STATUS_TIME, (void *)&ap_status_time,
sizeof (ap_status_time))) != PICL_SUCCESS) {
FRUTREE_DEBUG3(EVENTS, PTREE_UPDATE_PROP_ERR,
PICL_PROP_STATUS_TIME, locp->name, rc);
}
}
/* update the autoconfig flag */
switch (is_autoconfig_enabled(locp->name)) {
case 1:
locp->autoconfig_enabled = B_TRUE;
break;
case 0:
default:
locp->autoconfig_enabled = B_FALSE;
break;
}
(void) pthread_mutex_unlock(&locp->mutex);
free(list);
return (PICL_SUCCESS);
}
/*
* volatile callback function to return the state value for a location
*/
static int
get_loc_state(ptree_rarg_t *rarg, void *buf)
{
picl_errno_t rc;
frutree_dr_arg_t dr_arg;
hashdata_t *hashptr = NULL;
frutree_locnode_t *locp = NULL;
boolean_t state_change = B_FALSE;
if (buf == NULL) {
return (PICL_INVALIDARG);
}
if ((rc = hash_lookup_entry(rarg->nodeh, (void **)&hashptr)) !=
PICL_SUCCESS) {
return (rc);
}
locp = LOCDATA_PTR(hashptr);
if (locp == NULL) {
return (PICL_FAILURE);
}
(void) pthread_mutex_lock(&locp->mutex);
if (locp->dr_in_progress == B_TRUE) {
/* return the cached value */
(void) strncpy((char *)buf, loc_state[locp->state],
PICL_PROPNAMELEN_MAX);
(void) pthread_mutex_unlock(&locp->mutex);
return (PICL_SUCCESS);
}
(void) pthread_mutex_unlock(&locp->mutex);
if ((rc = update_loc_state(locp, &state_change)) != PICL_SUCCESS) {
FRUTREE_DEBUG2(EVENTS, GET_LOC_STATE_ERR, locp->name, rc);
/* return the cached value */
(void) strncpy((char *)buf, loc_state[locp->state],
PICL_PROPNAMELEN_MAX);
return (rc);
}
/* if there is a state change, handle the event */
if (state_change) {
(void) pthread_mutex_lock(&locp->mutex);
if (locp->state == LOC_STATE_EMPTY) { /* card removed */
dr_arg.action = HANDLE_REMOVE;
} else if (locp->prev_state == LOC_STATE_EMPTY) {
dr_arg.action = HANDLE_INSERT; /* card inserted */
} else {
/* loc state changed */
dr_arg.action = HANDLE_LOCSTATE_CHANGE;
}
(void) pthread_mutex_unlock(&locp->mutex);
dr_arg.data = locp;
(void) pthread_mutex_lock(&ev_mutex);
if ((rc = add_to_queue(dr_arg)) != PICL_SUCCESS) {
(void) pthread_mutex_unlock(&ev_mutex);
FRUTREE_DEBUG3(EVENTS, EVENT_NOT_HANDLED,
"dr_ap_state_change", locp->name, rc);
} else {
(void) pthread_cond_signal(&ev_cond);
(void) pthread_mutex_unlock(&ev_mutex);
}
}
(void) strncpy((char *)buf, loc_state[locp->state],
PICL_PROPNAMELEN_MAX);
return (PICL_SUCCESS);
}
/*
* syncup with cfgadm data and read latest fru state information
*/
static picl_errno_t
update_fru_state(frutree_frunode_t *frup, boolean_t *updated)
{
int i;
picl_errno_t rc;
picl_nodehdl_t loch;
uint64_t ap_status_time;
hashdata_t *hashptr = NULL;
cfga_list_data_t *list = NULL;
frutree_locnode_t *locp = NULL;
char valbuf[PICL_PROPNAMELEN_MAX];
*updated = B_FALSE;
if (frup->state_mgr == PLUGIN_PVT) {
if ((rc = ptree_get_propval_by_name(frup->frunodeh,
PICL_PROP_STATE, (void *)valbuf,
PICL_PROPNAMELEN_MAX)) != PICL_SUCCESS) {
return (rc);
}
/* if there is a change in state, update the internal value */
if (strcmp(fru_state[frup->state], valbuf) != 0) {
*updated = B_TRUE;
frup->prev_state = frup->state;
ap_status_time = (uint64_t)(time(NULL));
if ((rc = ptree_update_propval_by_name(frup->frunodeh,
PICL_PROP_STATUS_TIME, (void *)&ap_status_time,
sizeof (ap_status_time))) != PICL_SUCCESS) {
if (rc == PICL_PROPNOTFOUND) {
(void) create_property(
PICL_PTYPE_TIMESTAMP, PICL_READ,
sizeof (ap_status_time),
PICL_PROP_STATUS_TIME,
NULLREAD, NULLWRITE,
frup->frunodeh,
NULL, &ap_status_time);
} else {
FRUTREE_DEBUG3(EVENTS,
PTREE_UPDATE_PROP_ERR,
PICL_PROP_STATUS_TIME,
frup->name, rc);
}
}
for (i = 0; (fru_state[i] != NULL); i++) {
if (strcmp(fru_state[i], valbuf) == 0) {
frup->state = i;
return (PICL_SUCCESS);
}
}
}
return (PICL_SUCCESS);
} else if (frup->state_mgr == STATIC_LOC) {
frup->state = FRU_STATE_CONFIGURED;
return (PICL_SUCCESS);
}
if ((rc = ptree_get_propval_by_name(frup->frunodeh, PICL_PROP_PARENT,
&loch, sizeof (loch))) != PICL_SUCCESS) {
return (rc);
}
if ((rc = hash_lookup_entry(loch, (void **)&hashptr)) !=
PICL_SUCCESS) {
return (rc);
}
locp = LOCDATA_PTR(hashptr);
if (locp == NULL) {
return (PICL_FAILURE);
}
list = (cfga_list_data_t *)malloc(sizeof (cfga_list_data_t));
if (list == NULL) {
return (PICL_NOSPACE);
}
if ((rc = get_cfgadm_state(list, locp->name)) != PICL_SUCCESS) {
free(list);
return (rc);
}
(void) pthread_mutex_lock(&frup->mutex);
switch (list->ap_o_state) {
case CFGA_STAT_CONFIGURED:
if (frup->state != FRU_STATE_CONFIGURED) {
*updated = B_TRUE;
frup->prev_state = frup->state;
frup->state = FRU_STATE_CONFIGURED;
}
break;
case CFGA_STAT_UNCONFIGURED:
if (frup->state != FRU_STATE_UNCONFIGURED) {
*updated = B_TRUE;
frup->prev_state = frup->state;
frup->state = FRU_STATE_UNCONFIGURED;
}
break;
default:
if (frup->state != FRU_STATE_UNKNOWN) {
*updated = B_TRUE;
frup->prev_state = frup->state;
frup->state = FRU_STATE_UNKNOWN;
}
break;
}
/* update the fru_type property */
if (list->ap_type) {
if ((rc = ptree_update_propval_by_name(frup->frunodeh,
PICL_PROP_FRU_TYPE, list->ap_type,
sizeof (list->ap_type))) != PICL_SUCCESS) {
FRUTREE_DEBUG3(EVENTS, PTREE_UPDATE_PROP_ERR,
PICL_PROP_FRU_TYPE, frup->name, rc);
}
}
if (*updated == B_TRUE) {
ap_status_time = (uint64_t)(time(NULL));
if ((rc = ptree_update_propval_by_name(frup->frunodeh,
PICL_PROP_STATUS_TIME, (void *)&ap_status_time,
sizeof (ap_status_time))) != PICL_SUCCESS) {
FRUTREE_DEBUG3(EVENTS, PTREE_UPDATE_PROP_ERR,
PICL_PROP_STATUS_TIME, frup->name, rc);
}
}
(void) pthread_mutex_unlock(&frup->mutex);
free(list);
return (PICL_SUCCESS);
}
/*
* syncup with cfgadm data and read latest fru condition information
*/
static picl_errno_t
update_fru_condition(frutree_frunode_t *frup, boolean_t *updated)
{
int i = 0;
picl_errno_t rc;
picl_nodehdl_t loch;
uint64_t ap_cond_time;
hashdata_t *hashptr = NULL;
cfga_list_data_t *list = NULL;
frutree_locnode_t *locp = NULL;
char valbuf[PICL_PROPNAMELEN_MAX];
*updated = B_FALSE;
if (frup->state_mgr == PLUGIN_PVT) {
if ((rc = ptree_get_propval_by_name(frup->frunodeh,
PICL_PROP_CONDITION, (void *)valbuf,
PICL_PROPNAMELEN_MAX)) != PICL_SUCCESS) {
return (rc);
}
/*
* if there is a change in condition, update the
* internal value
*/
if (strcmp(fru_cond[frup->cond], valbuf) != 0) {
*updated = B_TRUE;
ap_cond_time = (uint64_t)(time(NULL));
if ((rc = ptree_update_propval_by_name(frup->frunodeh,
PICL_PROP_CONDITION_TIME, (void *)&ap_cond_time,
sizeof (ap_cond_time))) != PICL_SUCCESS) {
FRUTREE_DEBUG3(EVENTS, PTREE_UPDATE_PROP_ERR,
PICL_PROP_CONDITION_TIME, frup->name,
rc);
}
frup->prev_cond = frup->cond;
for (i = 0; (fru_cond[i] != NULL); i++) {
if (strcmp(fru_cond[i], valbuf) == 0) {
frup->cond = i;
return (PICL_SUCCESS);
}
}
}
return (PICL_SUCCESS);
} else if (frup->state_mgr == STATIC_LOC) {
frup->cond = FRU_COND_OK;
return (PICL_SUCCESS);
}
if ((rc = ptree_get_propval_by_name(frup->frunodeh, PICL_PROP_PARENT,
&loch, sizeof (loch))) != PICL_SUCCESS) {
return (rc);
}
if ((rc = hash_lookup_entry(loch, (void **)&hashptr)) !=
PICL_SUCCESS) {
return (rc);
}
locp = LOCDATA_PTR(hashptr);
if (locp == NULL) {
return (PICL_FAILURE);
}
list = (cfga_list_data_t *)malloc(sizeof (cfga_list_data_t));
if (list == NULL) {
return (PICL_NOSPACE);
}
if ((rc = get_cfgadm_state(list, locp->name)) != PICL_SUCCESS) {
free(list);
return (rc);
}
switch (list->ap_cond) {
case CFGA_COND_OK:
if (frup->cond != FRU_COND_OK) {
*updated = B_TRUE;
frup->prev_cond = frup->cond;
frup->cond = FRU_COND_OK;
}
break;
case CFGA_COND_FAILING:
if (frup->cond != FRU_COND_FAILING) {
*updated = B_TRUE;
frup->prev_cond = frup->cond;
frup->cond = FRU_COND_FAILING;
}
break;
case CFGA_COND_FAILED:
case CFGA_COND_UNUSABLE:
if (frup->cond != FRU_COND_FAILED) {
*updated = B_TRUE;
frup->prev_cond = frup->cond;
frup->cond = FRU_COND_FAILED;
}
break;
default:
if (frup->cond != FRU_COND_UNKNOWN) {
*updated = B_TRUE;
frup->prev_cond = frup->cond;
frup->cond = FRU_COND_UNKNOWN;
}
}
if (*updated == B_TRUE) {
ap_cond_time = (uint64_t)(time(NULL));
if ((rc = ptree_update_propval_by_name(frup->frunodeh,
PICL_PROP_CONDITION_TIME, (void *)&ap_cond_time,
sizeof (ap_cond_time))) != PICL_SUCCESS) {
FRUTREE_DEBUG3(EVENTS, PTREE_UPDATE_PROP_ERR,
PICL_PROP_CONDITION_TIME, frup->name, rc);
}
}
free(list);
return (PICL_SUCCESS);
}
/*
* Volatile callback function to read fru state
*/
static int
get_fru_state(ptree_rarg_t *rarg, void *buf)
{
picl_errno_t rc;
hashdata_t *hashptr = NULL;
frutree_frunode_t *frup = NULL;
boolean_t state_change = B_FALSE;
frutree_dr_arg_t dr_arg;
if (buf == NULL) {
return (PICL_INVALIDARG);
}
if ((rc = hash_lookup_entry(rarg->nodeh, (void **)&hashptr)) !=
PICL_SUCCESS) {
return (rc);
}
frup = FRUDATA_PTR(hashptr);
if (frup == NULL) {
return (PICL_FAILURE);
}
/* return the cached value, if dr is in progress */
(void) pthread_mutex_lock(&frup->mutex);
if (frup->dr_in_progress) {
(void) pthread_mutex_unlock(&frup->mutex);
(void) strncpy((char *)buf, fru_state[frup->state],
PICL_PROPNAMELEN_MAX);
return (PICL_SUCCESS);
}
(void) pthread_mutex_unlock(&frup->mutex);
if ((rc = update_fru_state(frup, &state_change)) != PICL_SUCCESS) {
FRUTREE_DEBUG2(EVENTS, GET_FRU_STATE_ERR, frup->name, rc);
/* return the cached value */
(void) strncpy((char *)buf, fru_state[frup->state],
PICL_PROPNAMELEN_MAX);
return (rc);
}
/* if there is a state change, handle the event */
if (state_change) {
(void) pthread_mutex_lock(&frup->mutex);
/* figure out if this is config/unconfig operation */
if (frup->state == FRU_STATE_CONFIGURED) {
dr_arg.action = HANDLE_CONFIGURE;
dr_arg.data = frup;
} else if (frup->state == FRU_STATE_UNCONFIGURED) {
dr_arg.action = HANDLE_UNCONFIGURE;
dr_arg.data = frup;
}
(void) pthread_mutex_unlock(&frup->mutex);
(void) pthread_mutex_lock(&ev_mutex);
if ((rc = add_to_queue(dr_arg)) != PICL_SUCCESS) {
(void) pthread_mutex_unlock(&ev_mutex);
FRUTREE_DEBUG3(EVENTS, EVENT_NOT_HANDLED,
"dr_ap_state_chage", frup->name, rc);
} else {
(void) pthread_cond_signal(&ev_cond);
(void) pthread_mutex_unlock(&ev_mutex);
}
}
(void) strncpy((char *)buf, fru_state[frup->state],
PICL_PROPNAMELEN_MAX);
return (PICL_SUCCESS);
}
/*
* Volatile callback function to read fru condition
*/
static int
get_fru_condition(ptree_rarg_t *rarg, void *buf)
{
picl_errno_t rc;
frutree_dr_arg_t dr_arg;
hashdata_t *hashptr = NULL;
frutree_frunode_t *frup = NULL;
boolean_t cond_changed = B_FALSE;
if (buf == NULL) {
return (PICL_INVALIDARG);
}
if ((rc = hash_lookup_entry(rarg->nodeh, (void **)&hashptr)) !=
PICL_SUCCESS) {
return (rc);
}
frup = FRUDATA_PTR(hashptr);
if (frup == NULL) {
return (PICL_FAILURE);
}
/* return the cached value, if dr is in progress */
(void) pthread_mutex_lock(&frup->mutex);
if (frup->dr_in_progress) {
(void) pthread_mutex_unlock(&frup->mutex);
(void) strncpy((char *)buf, fru_cond[frup->cond],
PICL_PROPNAMELEN_MAX);
return (PICL_SUCCESS);
}
(void) pthread_mutex_unlock(&frup->mutex);
if ((rc = update_fru_condition(frup, &cond_changed)) != PICL_SUCCESS) {
FRUTREE_DEBUG2(EVENTS, GET_FRU_COND_ERR, frup->name, rc);
/* return the cached value */
(void) strncpy((char *)buf, fru_cond[frup->cond],
PICL_PROPNAMELEN_MAX);
return (rc);
}
if (cond_changed) {
dr_arg.action = POST_COND_EVENT;
dr_arg.data = frup;
(void) pthread_mutex_lock(&ev_mutex);
if ((rc = add_to_queue(dr_arg)) != PICL_SUCCESS) {
(void) pthread_mutex_unlock(&ev_mutex);
FRUTREE_DEBUG3(EVENTS, EVENT_NOT_HANDLED,
"condition event", frup->name, rc);
} else {
(void) pthread_cond_signal(&ev_cond);
(void) pthread_mutex_unlock(&ev_mutex);
}
}
/* if there is a condition change, post picl event */
(void) strncpy((char *)buf, fru_cond[frup->cond],
PICL_PROPNAMELEN_MAX);
return (PICL_SUCCESS);
}
static void
free_cache(frutree_cache_t *cachep)
{
frutree_cache_t *tmp = NULL;
if (cachep == NULL)
return;
while (cachep != NULL) {
tmp = cachep;
cachep = cachep->next;
free(tmp);
}
}
/*
* traverse the /platform tree in PICL tree to create logical devices table
*/
static picl_errno_t
probe_platform_tree(frutree_frunode_t *frup, frutree_device_args_t **devp)
{
picl_errno_t rc;
picl_nodehdl_t refhdl = 0;
char class[PICL_CLASSNAMELEN_MAX];
frutree_device_args_t *device = NULL;
picl_prophdl_t tblprophdl;
picl_prophdl_t dev_tblhdl, env_tblhdl = 0;
if (devp == NULL) {
return (PICL_FAILURE);
}
device = *(frutree_device_args_t **)devp;
if (device == NULL) {
return (PICL_FAILURE);
}
/* traverse thru platform tree and add entries to Devices table */
if ((refhdl = get_reference_handle(frup->frunodeh)) == 0) {
return (PICL_NODENOTFOUND);
}
/* create Devices table property */
if ((rc = create_property(PICL_PTYPE_TABLE, PICL_READ,
sizeof (picl_prophdl_t), PICL_PROP_DEVICES, NULLREAD,
NULLWRITE, frup->frunodeh, &tblprophdl, &dev_tblhdl)) !=
PICL_SUCCESS) {
return (rc);
}
if ((rc = ptree_get_propval_by_name(refhdl, PICL_PROP_CLASSNAME,
class, sizeof (class))) != PICL_SUCCESS) {
return (rc);
}
if ((rc = create_table_entry(dev_tblhdl, refhdl, class)) !=
PICL_SUCCESS) {
return (rc);
}
/* create Environment devices table property */
if ((rc = create_property(PICL_PTYPE_TABLE, PICL_READ,
sizeof (picl_prophdl_t), PICL_PROP_ENV, NULLREAD,
NULLWRITE, frup->frunodeh, &tblprophdl, &env_tblhdl)) !=
PICL_SUCCESS) {
return (rc);
}
device->nodeh = refhdl;
device->device_tblhdl = dev_tblhdl;
device->env_tblhdl = env_tblhdl;
device->first = NULL;
device->last = NULL;
device->create_cache = B_FALSE;
/* probe using platform tree info */
if ((rc = do_action(refhdl, CREATE_DEVICES_ENTRIES,
device)) != PICL_SUCCESS) {
free_cache(device->first);
return (rc);
}
return (PICL_SUCCESS);
}
/*
* create temp conf file to pass it to picld util lib to create
* nodes under the fru
*/
static picl_errno_t
create_fru_children(frutree_frunode_t *frup, frutree_device_args_t device)
{
int fd;
picl_errno_t rc;
char conffile[MAXPATHLEN];
char dir[MAXPATHLEN];
struct stat file_stat;
char version[BUF_SIZE];
frutree_cache_t *cachep = NULL;
cachep = device.first;
if (cachep == NULL) {
return (PICL_SUCCESS);
}
/* create the configuration file for the fru */
(void) snprintf(dir, MAXPATHLEN, "%s%s", TEMP_DIR, frup->name);
bzero(&file_stat, sizeof (file_stat));
if (stat(conffile, &file_stat) == -1) {
if (mkdir(conffile, 0755) == -1) {
return (PICL_FAILURE);
}
}
(void) snprintf(conffile, MAXPATHLEN, "%s/%s", dir, PROBE_FILE);
if ((fd = open(conffile, O_WRONLY|O_CREAT|O_TRUNC, 0644)) == -1) {
(void) rmdir(dir);
return (PICL_FAILURE);
}
(void) snprintf(version, sizeof (version), "VERSION %d.0",
PTREE_PROPINFO_VERSION);
if (write(fd, version, strlen(version)) != strlen(version)) {
(void) remove(conffile);
(void) rmdir(dir);
(void) close(fd);
return (PICL_FAILURE);
}
/* traverse thru each cache entry and append to conf file */
while (cachep != NULL) {
if (write(fd, cachep->buf, strlen(cachep->buf))
!= strlen(cachep->buf)) {
(void) close(fd);
(void) remove(conffile);
(void) rmdir(dir);
return (PICL_FAILURE);
}
cachep = cachep->next;
}
(void) close(fd);
/* create child nodes for fru using the conffile created */
if ((rc = picld_pluginutil_parse_config_file(frup->frunodeh,
conffile)) != PICL_SUCCESS) {
(void) remove(conffile);
(void) rmdir(dir);
return (rc);
}
(void) remove(conffile);
(void) rmdir(dir);
if ((rc = fru_init(frup)) != PICL_SUCCESS) {
return (rc);
}
return (PICL_SUCCESS);
}
/*
* probes libdevinfo and create the port nodes under a fru
* probes for any scsi devices under a fru
*/
static picl_errno_t
probe_fru(frutree_frunode_t *frup, boolean_t load_drivers)
{
picl_errno_t rc;
picl_nodehdl_t child, loch;
char slot_type[PICL_PROPNAMELEN_MAX];
char devfs_path[PICL_PROPNAMELEN_MAX];
char probe_path[PICL_PROPNAMELEN_MAX];
frutree_device_args_t *device = NULL;
if (frup == NULL) {
return (PICL_FAILURE);
}
FRUTREE_DEBUG1(EVENTS, "probing :%s", frup->name);
if ((rc = ptree_get_propval_by_name(frup->frunodeh, PICL_PROP_PARENT,
&loch, sizeof (loch))) != PICL_SUCCESS) {
return (rc);
}
bzero(devfs_path, PICL_PROPNAMELEN_MAX);
bzero(probe_path, PICL_PROPNAMELEN_MAX);
if ((rc = ptree_get_propval_by_name(loch, PICL_PROP_DEVFS_PATH,
devfs_path, sizeof (devfs_path))) == PICL_SUCCESS) {
device = (frutree_device_args_t *)malloc(
sizeof (frutree_device_args_t));
if (device == NULL) {
return (PICL_NOSPACE);
}
device->first = NULL;
device->last = NULL;
(void) probe_platform_tree(frup, &device);
free_cache(device->first);
free(device);
}
/*
* if parent has NULL probe-path, skip probing this fru
* probe only child locations (if present).
* if probe-path is not present use devfs-path as path for
* probing the fru.
*/
rc = ptree_get_propval_by_name(loch, PICL_PROP_PROBE_PATH,
probe_path, sizeof (probe_path));
if (rc != PICL_SUCCESS) {
if (!devfs_path[0]) { /* devfspath is also not present */
return (PICL_SUCCESS); /* nothing to probe */
} else {
/* use devfs-path as path for probing */
if ((rc = get_fru_path(devfs_path, frup)) !=
PICL_SUCCESS) {
return (rc);
}
}
} else {
/* NULL path, skip probing this fru */
if (strlen(probe_path) == 0) {
rc = fru_init(frup); /* probe its children */
return (rc);
} else {
/* valid probe-path */
if ((rc = get_fru_path(probe_path, frup)) !=
PICL_SUCCESS) {
return (rc);
}
}
}
/* children present already, no need to probe libdevinfo */
rc = ptree_get_propval_by_name(frup->frunodeh, PICL_PROP_CHILD,
&child, sizeof (picl_nodehdl_t));
if (rc == PICL_SUCCESS) { /* child present */
if ((rc = fru_init(frup)) != PICL_SUCCESS) {
return (rc);
}
/* now create the scsi nodes for this fru */
if ((rc = probe_for_scsi_frus(frup)) != PICL_SUCCESS) {
return (rc);
}
return (PICL_SUCCESS);
}
if (ptree_get_propval_by_name(frup->frunodeh, PICL_PROP_PARENT,
&loch, sizeof (loch)) != PICL_SUCCESS) {
return (rc);
}
if ((rc = ptree_get_propval_by_name(loch, PICL_PROP_SLOT_TYPE,
slot_type, sizeof (slot_type))) != PICL_SUCCESS) {
return (rc);
}
/* no need to probe further for scsi frus */
if (strcmp(slot_type, SANIBEL_SCSI_SLOT) == 0 ||
strcmp(slot_type, SANIBEL_IDE_SLOT) == 0) {
return (PICL_SUCCESS);
}
device = (frutree_device_args_t *)malloc(
sizeof (frutree_device_args_t));
if (device == NULL) {
return (PICL_NOSPACE);
}
device->first = NULL;
device->last = NULL;
if ((rc = probe_libdevinfo(frup, &device, load_drivers)) !=
PICL_SUCCESS) {
free_cache(device->first);
free(device);
return (rc);
}
if (device->first != NULL) {
if ((rc = create_fru_children(frup, *device)) != PICL_SUCCESS) {
free_cache(device->first);
free(device);
return (rc);
}
}
free_cache(device->first);
free(device);
/* now create the scsi nodes for this fru */
if ((rc = probe_for_scsi_frus(frup)) != PICL_SUCCESS) {
return (rc);
}
return (PICL_SUCCESS);
}
/*
* callback function for ptree_walk_tree_by_class,
* used to update hashtable during DR_HINT_REMOVE event
*/
/*ARGSUSED*/
static int
frutree_update_hash(picl_nodehdl_t nodeh, void *c_args)
{
picl_errno_t rc = 0;
if ((rc = hash_remove_entry(nodeh)) != PICL_SUCCESS) {
return (rc);
}
return (PICL_WALK_CONTINUE);
}
/*
* routine to handle DR_HINT_REMOVE
*/
static picl_errno_t
handle_fru_remove(frutree_frunode_t *frup)
{
picl_errno_t rc = PICL_SUCCESS;
if (frup == NULL) {
return (PICL_FAILURE);
}
if ((rc = ptree_walk_tree_by_class(frup->frunodeh,
NULL, NULL, frutree_update_hash)) != PICL_SUCCESS) {
return (rc);
}
(void) ptree_delete_node(frup->frunodeh);
(void) ptree_destroy_node(frup->frunodeh);
if ((rc = hash_remove_entry(frup->frunodeh)) !=
PICL_SUCCESS) {
return (rc);
}
return (PICL_SUCCESS);
}
/* remove State and Condition props for all the nodes under fru */
/*ARGSUSED*/
static int
frutree_handle_unconfigure(picl_nodehdl_t nodeh, void *c_args)
{
picl_errno_t rc = 0;
picl_prophdl_t proph;
char class[PICL_PROPNAMELEN_MAX];
if (ptree_get_prop_by_name(nodeh, PICL_PROP_STATE,
&proph) == PICL_SUCCESS) {
(void) ptree_delete_prop(proph);
(void) ptree_destroy_prop(proph);
}
if (ptree_get_prop_by_name(nodeh, PICL_PROP_STATUS_TIME,
&proph) == PICL_SUCCESS) {
(void) ptree_delete_prop(proph);
(void) ptree_destroy_prop(proph);
}
if ((rc = ptree_get_propval_by_name(nodeh, PICL_PROP_CLASSNAME,
class, sizeof (class))) != PICL_SUCCESS) {
return (rc);
}
if (strcmp(class, PICL_CLASS_PORT) == 0) {
if (ptree_get_prop_by_name(nodeh, PICL_PROP_CONDITION,
&proph) == PICL_SUCCESS) {
(void) ptree_delete_prop(proph);
(void) ptree_destroy_prop(proph);
}
if (ptree_get_prop_by_name(nodeh, PICL_PROP_CONDITION_TIME,
&proph) == PICL_SUCCESS) {
(void) ptree_delete_prop(proph);
(void) ptree_destroy_prop(proph);
}
/* delete devices table */
if (ptree_get_prop_by_name(nodeh, PICL_PROP_DEVICES,
&proph) == PICL_SUCCESS) {
(void) ptree_delete_prop(proph);
(void) ptree_destroy_prop(proph);
}
}
return (PICL_WALK_CONTINUE);
}
/*
* traverse thru each node fru node and do cleanup
*/
static picl_errno_t
handle_fru_unconfigure(frutree_frunode_t *frup)
{
picl_errno_t rc = 0, retval = 0;
picl_prophdl_t proph;
picl_nodehdl_t childh, peerh, nodeh;
hashdata_t *hashptr = NULL;
frutree_frunode_t *child_frup = NULL;
char class[PICL_PROPNAMELEN_MAX];
if (frup == NULL) {
return (PICL_FAILURE);
}
/* delete devices table */
if (ptree_get_prop_by_name(frup->frunodeh, PICL_PROP_DEVICES,
&proph) == PICL_SUCCESS) {
(void) ptree_delete_prop(proph);
(void) ptree_destroy_prop(proph);
}
/* delete Environment devices table */
if (ptree_get_prop_by_name(frup->frunodeh, PICL_PROP_ENV,
&proph) == PICL_SUCCESS) {
(void) ptree_delete_prop(proph);
(void) ptree_destroy_prop(proph);
}
if ((rc = ptree_walk_tree_by_class(frup->frunodeh,
NULL, NULL, frutree_handle_unconfigure)) != PICL_SUCCESS) {
return (rc);
}
/* remove all the fru nodes under the child locations */
retval = ptree_get_propval_by_name(frup->frunodeh, PICL_PROP_CHILD,
&peerh, sizeof (peerh));
while (retval == PICL_SUCCESS) {
nodeh = peerh;
retval = ptree_get_propval_by_name(nodeh, PICL_PROP_PEER,
&peerh, sizeof (peerh));
if ((rc = ptree_get_propval_by_name(nodeh, PICL_PROP_CLASSNAME,
class, sizeof (class))) != PICL_SUCCESS) {
return (rc);
}
if (strcmp(class, PICL_CLASS_PORT) == 0) {
continue;
}
/* if the child location has fru, delete the fru */
if (ptree_get_propval_by_name(nodeh, PICL_PROP_CHILD,
&childh, sizeof (childh)) != PICL_SUCCESS) {
continue;
}
/* child is present under the location */
if ((rc = hash_lookup_entry(childh, (void **)&hashptr)) !=
PICL_SUCCESS) {
return (rc);
}
child_frup = FRUDATA_PTR(hashptr);
(void) handle_fru_remove(child_frup);
}
return (PICL_SUCCESS);
}
/*
* create the properties under the fru
*/
static picl_errno_t
create_fru_props(frutree_frunode_t *frup)
{
picl_errno_t rc;
uint64_t ap_status_time = 0;
boolean_t state_change;
/* create state props */
if ((rc = create_property(PICL_PTYPE_CHARSTRING,
PICL_READ + PICL_VOLATILE, PICL_PROPNAMELEN_MAX,
PICL_PROP_STATE, get_fru_state, NULLWRITE,
frup->frunodeh, NULL, fru_state[frup->state])) !=
PICL_SUCCESS) {
FRUTREE_DEBUG3(EVENTS, PTREE_CREATE_PROP_FAILED,
PICL_PROP_STATE, frup->name, rc);
}
ap_status_time = (uint64_t)(time(NULL));
if ((rc = create_property(PICL_PTYPE_TIMESTAMP, PICL_READ,
sizeof (ap_status_time), PICL_PROP_STATUS_TIME,
NULLREAD, NULLWRITE, frup->frunodeh,
NULL, &ap_status_time)) != PICL_SUCCESS) {
FRUTREE_DEBUG3(EVENTS, PTREE_CREATE_PROP_FAILED,
PICL_PROP_STATUS_TIME, frup->name, rc);
}
if ((rc = update_fru_state(frup, &state_change)) != PICL_SUCCESS) {
FRUTREE_DEBUG2(EVENTS, GET_FRU_STATE_ERR, frup->name, rc);
return (rc);
}
/* create condition props */
if ((rc = create_property(PICL_PTYPE_CHARSTRING,
PICL_READ + PICL_VOLATILE, PICL_PROPNAMELEN_MAX,
PICL_PROP_CONDITION, get_fru_condition, NULLWRITE,
frup->frunodeh, NULL, fru_cond[frup->cond])) !=
PICL_SUCCESS) {
FRUTREE_DEBUG3(EVENTS, PTREE_CREATE_PROP_FAILED,
PICL_PROP_CONDITION, frup->name, rc);
}
if ((rc = create_property(PICL_PTYPE_TIMESTAMP, PICL_READ,
sizeof (ap_status_time), PICL_PROP_CONDITION_TIME,
NULLREAD, NULLWRITE, frup->frunodeh, NULL,
&ap_status_time)) != PICL_SUCCESS) {
FRUTREE_DEBUG3(EVENTS, PTREE_CREATE_PROP_FAILED,
PICL_PROP_CONDITION_TIME, frup->name, rc);
}
if ((rc = update_fru_condition(frup, &state_change)) != PICL_SUCCESS) {
FRUTREE_DEBUG2(EVENTS, GET_FRU_COND_ERR, frup->name, rc);
return (rc);
}
/* create admin lock prop */
if ((rc = create_property(PICL_PTYPE_CHARSTRING,
PICL_READ + PICL_WRITE, PICL_PROPNAMELEN_MAX,
PICL_PROP_ADMIN_LOCK, NULLREAD, NULLWRITE,
frup->frunodeh, NULL, PICL_ADMINLOCK_DISABLED)) !=
PICL_SUCCESS) {
FRUTREE_DEBUG3(EVENTS, PTREE_CREATE_PROP_FAILED,
PICL_PROP_ADMIN_LOCK, frup->name, rc);
}
return (rc);
}
/*
* calls libcfgadm API to do a connect on a location
*/
static picl_errno_t
connect_fru(frutree_locnode_t *locp)
{
picl_errno_t rc;
cfga_err_t ap_list_err;
cfga_flags_t flags = 0;
boolean_t state_change;
uint64_t ap_status_time;
hrtime_t start;
hrtime_t end;
if (locp == NULL) {
return (PICL_FAILURE);
}
if ((rc = post_piclevent(PICLEVENT_STATE_CHANGE,
PICLEVENTARGVAL_CONNECTING, loc_state[locp->state],
locp->locnodeh, WAIT)) != PICL_SUCCESS) {
FRUTREE_DEBUG3(EVENTS, PTREE_POST_PICLEVENT_ERR,
locp->name, PICLEVENT_STATE_CHANGE, rc);
}
(void) pthread_mutex_lock(&locp->mutex);
locp->dr_in_progress = B_TRUE;
(void) pthread_mutex_unlock(&locp->mutex);
if (frutree_debug & PERF_DATA) {
start = gethrtime();
}
ap_list_err = config_change_state(CFGA_CMD_CONNECT, 1, &(locp->name),
NULL, NULL, NULL, NULL, flags);
if (frutree_debug & PERF_DATA) {
end = gethrtime();
FRUTREE_DEBUG2(PERF_DATA, "time for connect on %s: %lld nsec",
locp->name, (end - start));
}
if (ap_list_err != CFGA_OK) {
(void) pthread_mutex_lock(&locp->mutex);
locp->dr_in_progress = B_FALSE;
(void) pthread_mutex_unlock(&locp->mutex);
/* release mutex before updating state */
(void) update_loc_state(locp, &state_change);
if ((rc = post_piclevent(PICLEVENT_STATE_CHANGE,
loc_state[locp->state], PICLEVENTARGVAL_CONNECTING,
locp->locnodeh, WAIT)) != PICL_SUCCESS) {
FRUTREE_DEBUG3(EVENTS, PTREE_POST_PICLEVENT_ERR,
locp->name, PICLEVENT_STATE_CHANGE, rc);
}
if (locp->state == LOC_STATE_CONNECTED) {
/* wakeup threads sleeping on this condition */
(void) pthread_mutex_lock(&locp->mutex);
(void) pthread_cond_broadcast(&locp->cond_cv);
(void) pthread_mutex_unlock(&locp->mutex);
return (PICL_SUCCESS);
}
return (cfg2picl_errmap[ap_list_err][1]);
}
(void) pthread_mutex_lock(&locp->mutex);
locp->dr_in_progress = B_FALSE;
locp->prev_state = LOC_STATE_DISCONNECTED;
locp->state = LOC_STATE_CONNECTED;
ap_status_time = (uint64_t)(time(NULL));
if ((rc = ptree_update_propval_by_name(locp->locnodeh,
PICL_PROP_STATUS_TIME, (void *)&ap_status_time,
sizeof (ap_status_time))) != PICL_SUCCESS) {
FRUTREE_DEBUG3(EVENTS, PTREE_UPDATE_PROP_ERR,
PICL_PROP_STATUS_TIME, locp->name, rc);
}
/* wakeup threads sleeping on this condition */
(void) pthread_cond_broadcast(&locp->cond_cv);
(void) pthread_mutex_unlock(&locp->mutex);
if ((rc = post_piclevent(PICLEVENT_STATE_CHANGE,
PICLEVENTARGVAL_CONNECTED, PICLEVENTARGVAL_CONNECTING,
locp->locnodeh, WAIT)) != PICL_SUCCESS) {
FRUTREE_DEBUG3(EVENTS, PTREE_POST_PICLEVENT_ERR,
locp->name, PICLEVENT_STATE_CHANGE, rc);
}
return (PICL_SUCCESS);
}
/*
* calls libcfgadm API to do a disconnect on a location
*/
static picl_errno_t
disconnect_fru(frutree_locnode_t *locp)
{
picl_errno_t rc;
picl_nodehdl_t childh;
hashdata_t *hashptr = NULL;
timestruc_t to;
struct timeval tp;
hrtime_t start, end;
cfga_err_t ap_list_err;
cfga_flags_t flags = 0;
boolean_t state_change;
uint64_t ap_status_time;
frutree_frunode_t *frup = NULL;
if (locp == NULL) {
return (PICL_FAILURE);
}
(void) pthread_mutex_lock(&locp->mutex);
if (locp->state == LOC_STATE_DISCONNECTED) {
(void) pthread_mutex_unlock(&locp->mutex);
return (PICL_SUCCESS);
}
(void) pthread_mutex_unlock(&locp->mutex);
/* get the child fru information */
if (ptree_get_propval_by_name(locp->locnodeh, PICL_PROP_CHILD,
&childh, sizeof (childh)) == PICL_SUCCESS) {
if (hash_lookup_entry(childh, (void **)&hashptr) ==
PICL_SUCCESS) {
frup = FRUDATA_PTR(hashptr);
}
}
if (frup == NULL) {
return (PICL_SUCCESS);
}
(void) pthread_mutex_lock(&frup->mutex);
(void) gettimeofday(&tp, NULL);
to.tv_sec = tp.tv_sec + frutree_drwait_time;
to.tv_nsec = tp.tv_usec * 1000;
if (frup->state != FRU_STATE_UNCONFIGURED) {
(void) pthread_cond_timedwait(&frup->cond_cv,
&frup->mutex, &to);
}
if (frup->state != FRU_STATE_UNCONFIGURED) {
FRUTREE_DEBUG1(LOG_ERR, "SUNW_frutree:Disconnect operation on"
" %s failed", locp->name);
(void) pthread_mutex_unlock(&frup->mutex);
return (PICL_FAILURE);
}
(void) pthread_mutex_unlock(&frup->mutex);
if ((rc = post_piclevent(PICLEVENT_STATE_CHANGE,
PICLEVENTARGVAL_DISCONNECTING, loc_state[locp->state],
locp->locnodeh, WAIT)) != PICL_SUCCESS) {
FRUTREE_DEBUG3(EVENTS, PTREE_POST_PICLEVENT_ERR,
locp->name, PICLEVENT_STATE_CHANGE, rc);
}
(void) pthread_mutex_lock(&locp->mutex);
locp->dr_in_progress = B_TRUE;
(void) pthread_mutex_unlock(&locp->mutex);
if (frutree_debug & PERF_DATA) {
start = gethrtime();
}
ap_list_err = config_change_state(CFGA_CMD_DISCONNECT, 1, &(locp->name),
NULL, NULL, NULL, NULL, flags);
if (frutree_debug & PERF_DATA) {
end = gethrtime();
FRUTREE_DEBUG2(PERF_DATA, "time for disconnect on %s: %lld ns",
locp->name, (end - start));
}
if (ap_list_err != CFGA_OK) {
(void) pthread_mutex_lock(&locp->mutex);
locp->dr_in_progress = B_FALSE;
(void) pthread_mutex_unlock(&locp->mutex);
/* release mutex before updating state */
(void) update_loc_state(locp, &state_change);
if ((rc = post_piclevent(PICLEVENT_STATE_CHANGE,
loc_state[locp->state], PICLEVENTARGVAL_DISCONNECTING,
locp->locnodeh, WAIT)) != PICL_SUCCESS) {
FRUTREE_DEBUG3(EVENTS, PTREE_POST_PICLEVENT_ERR,
locp->name, PICLEVENT_STATE_CHANGE, rc);
}
(void) pthread_mutex_lock(&locp->mutex);
if (locp->state == LOC_STATE_DISCONNECTED) {
(void) pthread_mutex_unlock(&locp->mutex);
return (PICL_SUCCESS);
}
(void) pthread_mutex_unlock(&locp->mutex);
return (cfg2picl_errmap[ap_list_err][1]);
}
(void) pthread_mutex_lock(&locp->mutex);
locp->dr_in_progress = B_FALSE;
locp->prev_state = LOC_STATE_CONNECTED;
locp->state = LOC_STATE_DISCONNECTED;
ap_status_time = (uint64_t)(time(NULL));
if ((rc = ptree_update_propval_by_name(locp->locnodeh,
PICL_PROP_STATUS_TIME, (void *)&ap_status_time,
sizeof (ap_status_time))) != PICL_SUCCESS) {
FRUTREE_DEBUG3(EVENTS, PTREE_UPDATE_PROP_ERR,
PICL_PROP_STATUS_TIME, locp->name, rc);
}
(void) pthread_mutex_unlock(&locp->mutex);
if ((rc = post_piclevent(PICLEVENT_STATE_CHANGE,
PICLEVENTARGVAL_DISCONNECTED, PICLEVENTARGVAL_DISCONNECTING,
locp->locnodeh, WAIT)) != PICL_SUCCESS) {
FRUTREE_DEBUG3(EVENTS, PTREE_POST_PICLEVENT_ERR,
locp->name, PICLEVENT_STATE_CHANGE, rc);
}
return (PICL_SUCCESS);
}
/*
* Handle DR_INCOMING_RES event
*/
static void
handle_fru_configure(frutree_frunode_t *frup)
{
picl_errno_t rc;
boolean_t cond_changed;
if (frup == NULL)
return;
if ((rc = probe_fru(frup, B_FALSE)) != PICL_SUCCESS) {
FRUTREE_DEBUG2(EVENTS, PROBE_FRU_ERR, frup->name, rc);
}
/* update the fru condition */
(void) update_fru_condition(frup, &cond_changed);
if (cond_changed) {
if ((rc = post_piclevent(PICLEVENT_CONDITION_CHANGE,
fru_cond[frup->cond], fru_cond[frup->prev_cond],
frup->frunodeh, WAIT)) != PICL_SUCCESS) {
FRUTREE_DEBUG3(EVENTS, PTREE_POST_PICLEVENT_ERR,
frup->name, PICLEVENT_CONDITION_CHANGE, rc);
}
}
if ((rc = post_piclevent(PICLEVENT_STATE_CHANGE,
fru_state[frup->state], fru_state[frup->prev_state],
frup->frunodeh, WAIT)) != PICL_SUCCESS) {
FRUTREE_DEBUG3(EVENTS, PTREE_POST_PICLEVENT_ERR,
frup->name, PICLEVENT_STATE_CHANGE, rc);
}
}
/*
* call libcfgadm API to configure a fru
* (Handle DR_INCOMING_RES event)
*/
static picl_errno_t
configure_fru(frutree_frunode_t *frup, cfga_flags_t flags)
{
picl_errno_t rc;
picl_nodehdl_t parenth;
timestruc_t to;
struct timeval tp;
hrtime_t start, end;
cfga_err_t ap_list_err;
uint64_t ap_status_time;
hashdata_t *hashptr = NULL;
frutree_locnode_t *locp = NULL;
boolean_t state_change, cond_changed;
if (frup == NULL) {
return (PICL_FAILURE);
}
(void) pthread_mutex_lock(&frup->mutex);
if (frup->state == FRU_STATE_CONFIGURED) {
(void) pthread_mutex_unlock(&frup->mutex);
ap_list_err = config_change_state(CFGA_CMD_CONFIGURE, 1,
&(frup->name), NULL, NULL, NULL, NULL, flags);
return (PICL_SUCCESS);
}
(void) pthread_mutex_unlock(&frup->mutex);
if ((rc = ptree_get_propval_by_name(frup->frunodeh, PICL_PROP_PARENT,
&parenth, sizeof (parenth))) != PICL_SUCCESS) {
return (rc);
}
if ((rc = hash_lookup_entry(parenth, (void **)&hashptr)) !=
PICL_SUCCESS) {
return (rc);
}
locp = LOCDATA_PTR(hashptr);
if (locp == NULL) {
return (PICL_FAILURE);
}
(void) pthread_mutex_lock(&locp->mutex);
(void) gettimeofday(&tp, NULL);
to.tv_sec = tp.tv_sec + frutree_drwait_time;
to.tv_nsec = tp.tv_usec * 1000;
/* wait for sometime for location to get connected */
if (locp->state != LOC_STATE_CONNECTED) {
(void) pthread_cond_timedwait(&locp->cond_cv,
&locp->mutex, &to);
}
if (locp->state != LOC_STATE_CONNECTED) { /* give up */
FRUTREE_DEBUG1(EVENTS, "SUNW_frutree:Configure operation on"
" %s failed as loc is not connected", locp->name);
(void) pthread_mutex_unlock(&locp->mutex);
return (PICL_FAILURE);
}
(void) pthread_mutex_unlock(&locp->mutex);
if ((rc = post_piclevent(PICLEVENT_STATE_CHANGE,
PICLEVENTARGVAL_CONFIGURING, fru_state[frup->state],
frup->frunodeh, WAIT)) != PICL_SUCCESS) {
FRUTREE_DEBUG3(EVENTS, PTREE_POST_PICLEVENT_ERR,
frup->name, PICLEVENT_STATE_CHANGE, rc);
}
(void) pthread_mutex_lock(&frup->mutex);
frup->dr_in_progress = B_TRUE;
(void) pthread_mutex_unlock(&frup->mutex);
if (frutree_debug & PERF_DATA) {
start = gethrtime();
}
ap_list_err = config_change_state(CFGA_CMD_CONFIGURE, 1,
&(frup->name), NULL, NULL, NULL, NULL, flags);
if (frutree_debug & PERF_DATA) {
end = gethrtime();
FRUTREE_DEBUG2(PERF_DATA, "time for configure on %s: %lld nsec",
frup->name, (end - start));
}
if (ap_list_err != CFGA_OK) {
(void) pthread_mutex_lock(&frup->mutex);
frup->dr_in_progress = B_FALSE;
(void) pthread_mutex_unlock(&frup->mutex);
/* release mutex before updating state */
(void) update_fru_state(frup, &state_change);
if ((rc = post_piclevent(PICLEVENT_STATE_CHANGE,
fru_state[frup->state], PICLEVENTARGVAL_CONFIGURING,
frup->frunodeh, WAIT)) != PICL_SUCCESS) {
FRUTREE_DEBUG3(EVENTS, PTREE_POST_PICLEVENT_ERR,
frup->name, PICLEVENT_STATE_CHANGE, rc);
}
/* update the fru condition */
(void) update_fru_condition(frup, &state_change);
if (state_change) {
if ((rc = post_piclevent(PICLEVENT_CONDITION_CHANGE,
fru_cond[frup->cond], fru_cond[frup->prev_cond],
frup->frunodeh, WAIT)) != PICL_SUCCESS) {
FRUTREE_DEBUG3(EVENTS, PTREE_POST_PICLEVENT_ERR,
frup->name, PICLEVENT_CONDITION_CHANGE,
rc);
}
}
return (cfg2picl_errmap[ap_list_err][1]);
}
(void) pthread_mutex_lock(&frup->mutex);
frup->dr_in_progress = B_FALSE;
frup->prev_state = FRU_STATE_UNCONFIGURED;
frup->state = FRU_STATE_CONFIGURED;
ap_status_time = (uint64_t)(time(NULL));
if ((rc = ptree_update_propval_by_name(frup->frunodeh,
PICL_PROP_STATUS_TIME, (void *)&ap_status_time,
sizeof (ap_status_time))) != PICL_SUCCESS) {
FRUTREE_DEBUG3(EVENTS, PTREE_UPDATE_PROP_ERR,
PICL_PROP_STATUS_TIME, frup->name, rc);
}
(void) pthread_mutex_unlock(&frup->mutex);
if ((rc = probe_fru(frup, B_FALSE)) != PICL_SUCCESS) {
FRUTREE_DEBUG2(FRUTREE_INIT, PROBE_FRU_ERR, frup->name, rc);
}
/* update the fru condition */
(void) update_fru_condition(frup, &cond_changed);
if (cond_changed) {
if ((rc = post_piclevent(PICLEVENT_CONDITION_CHANGE,
fru_cond[frup->cond], fru_cond[frup->prev_cond],
frup->frunodeh, WAIT)) != PICL_SUCCESS) {
FRUTREE_DEBUG3(EVENTS, PTREE_POST_PICLEVENT_ERR,
frup->name, PICLEVENT_CONDITION_CHANGE, rc);
}
}
/* send the state change event */
if ((rc = post_piclevent(PICLEVENT_STATE_CHANGE,
fru_state[frup->state], PICLEVENTARGVAL_CONFIGURING,
frup->frunodeh, WAIT)) != PICL_SUCCESS) {
FRUTREE_DEBUG3(EVENTS, PTREE_POST_PICLEVENT_ERR,
frup->name, PICLEVENT_STATE_CHANGE, rc);
}
return (PICL_SUCCESS);
}
/*
* Handle DR_OUTGOING_RES event
* (call libcfgadm API to unconfigure a fru)
*/
static picl_errno_t
unconfigure_fru(frutree_frunode_t *frup, cfga_flags_t flags)
{
picl_errno_t rc;
cfga_err_t ap_list_err;
boolean_t state_change;
uint64_t ap_status_time;
hrtime_t start;
hrtime_t end;
if (frup == NULL) {
return (PICL_FAILURE);
}
(void) pthread_mutex_lock(&frup->mutex);
if (frup->state == FRU_STATE_UNCONFIGURED) {
(void) pthread_mutex_unlock(&frup->mutex);
return (PICL_SUCCESS);
}
(void) pthread_mutex_unlock(&frup->mutex);
if ((rc = post_piclevent(PICLEVENT_STATE_CHANGE,
PICLEVENTARGVAL_UNCONFIGURING, fru_state[frup->state],
frup->frunodeh, WAIT)) != PICL_SUCCESS) {
FRUTREE_DEBUG3(EVENTS, PTREE_POST_PICLEVENT_ERR,
frup->name, PICLEVENT_STATE_CHANGE, rc);
}
(void) pthread_mutex_lock(&frup->mutex);
while (frup->busy == B_TRUE) {
(void) pthread_cond_wait(&frup->busy_cond_cv,
&frup->mutex);
}
frup->dr_in_progress = B_TRUE;
(void) pthread_mutex_unlock(&frup->mutex);
if (frutree_debug & PERF_DATA) {
start = gethrtime();
}
ap_list_err = config_change_state(CFGA_CMD_UNCONFIGURE, 1,
&(frup->name), NULL, NULL, NULL, NULL, flags);
if (frutree_debug & PERF_DATA) {
end = gethrtime();
FRUTREE_DEBUG2(PERF_DATA, "time for unconfigure on %s: %lld ns",
frup->name, (end - start));
}
if (ap_list_err != CFGA_OK) {
/*
* call configure again (workaround for
* ENUM# to get generated for next attempt)
*/
config_change_state(CFGA_CMD_CONFIGURE, 1,
&(frup->name), NULL, NULL, NULL, NULL, flags);
(void) pthread_mutex_lock(&frup->mutex);
frup->dr_in_progress = B_FALSE;
(void) pthread_mutex_unlock(&frup->mutex);
/* release mutex before updating state */
(void) update_fru_condition(frup, &state_change);
if (state_change) {
if ((rc = post_piclevent(PICLEVENT_CONDITION_CHANGE,
fru_cond[frup->cond], fru_cond[frup->prev_cond],
frup->frunodeh, WAIT)) != PICL_SUCCESS) {
FRUTREE_DEBUG3(EVENTS, PTREE_POST_PICLEVENT_ERR,
frup->name, PICLEVENT_CONDITION_CHANGE,
rc);
}
}
(void) update_fru_state(frup, &state_change);
if ((rc = post_piclevent(PICLEVENT_STATE_CHANGE,
fru_state[frup->state], PICLEVENTARGVAL_UNCONFIGURING,
frup->frunodeh, WAIT)) != PICL_SUCCESS) {
FRUTREE_DEBUG3(EVENTS, PTREE_POST_PICLEVENT_ERR,
frup->name, PICLEVENT_STATE_CHANGE, rc);
}
return (cfg2picl_errmap[ap_list_err][1]);
}
(void) pthread_mutex_lock(&frup->mutex);
frup->dr_in_progress = B_FALSE;
frup->prev_state = FRU_STATE_CONFIGURED;
frup->state = FRU_STATE_UNCONFIGURED;
ap_status_time = (uint64_t)(time(NULL));
if ((rc = ptree_update_propval_by_name(frup->frunodeh,
PICL_PROP_STATUS_TIME, (void *)&ap_status_time,
sizeof (ap_status_time))) != PICL_SUCCESS) {
FRUTREE_DEBUG3(EVENTS, PTREE_UPDATE_PROP_ERR,
PICL_PROP_STATUS_TIME, frup->name, rc);
}
/* wakeup threads sleeping on this condition */
(void) pthread_cond_broadcast(&frup->cond_cv);
(void) pthread_mutex_unlock(&frup->mutex);
/* update the fru condition */
if ((rc = update_fru_condition(frup, &state_change)) != PICL_SUCCESS) {
FRUTREE_DEBUG2(EVENTS, GET_FRU_STATE_ERR,
frup->name, rc);
}
if (state_change) {
if ((rc = post_piclevent(PICLEVENT_CONDITION_CHANGE,
fru_cond[frup->cond], fru_cond[frup->prev_cond],
frup->frunodeh, WAIT)) != PICL_SUCCESS) {
FRUTREE_DEBUG3(EVENTS, PTREE_POST_PICLEVENT_ERR,
frup->name, PICLEVENT_CONDITION_CHANGE, rc);
}
}
if ((rc = post_piclevent(PICLEVENT_STATE_CHANGE,
PICLEVENTARGVAL_UNCONFIGURED, PICLEVENTARGVAL_UNCONFIGURING,
frup->frunodeh, WAIT)) != PICL_SUCCESS) {
FRUTREE_DEBUG3(EVENTS, PTREE_POST_PICLEVENT_ERR,
frup->name, PICLEVENT_STATE_CHANGE, rc);
}
return (PICL_SUCCESS);
}
/* creates fru nodes with basic properties and sends out intializing events */
static int
create_fru_node(frutree_locnode_t *locp, frutree_frunode_t **child_frupp)
{
picl_errno_t rc;
hashdata_t *fru_data = NULL;
frutree_frunode_t *frup = NULL;
picl_nodehdl_t fruh, child;
char slot_type[PICL_PROPNAMELEN_MAX];
char fru_name[PICL_PROPNAMELEN_MAX];
char apid_type[PICL_PROPNAMELEN_MAX];
boolean_t fru_present = B_FALSE;
boolean_t state_changed = B_FALSE;
if (locp->state == LOC_STATE_EMPTY) {
return (PICL_SUCCESS);
}
/* check if fru is present or not */
rc = ptree_get_propval_by_name(locp->locnodeh, PICL_PROP_CHILD,
&child, sizeof (picl_nodehdl_t));
if (rc == PICL_SUCCESS) {
fru_present = B_TRUE;
fruh = child;
(void) ptree_get_propval_by_name(child, PICL_PROP_NAME,
fru_name, sizeof (fru_name));
}
/* create fru node */
if (fru_present == B_FALSE) {
(void) strncpy(fru_name, locp->name, sizeof (fru_name));
if ((rc = ptree_create_node(fru_name, PICL_CLASS_FRU,
&fruh)) != PICL_SUCCESS) {
return (rc);
}
}
/* initialize internal data structures */
if ((rc = make_fru_data(fru_name, &fru_data)) != PICL_SUCCESS) {
return (rc);
}
frup = FRUDATA_PTR(fru_data);
frup->frunodeh = fruh;
frup->cpu_node = locp->cpu_node;
frup->state_mgr = locp->state_mgr;
*child_frupp = frup;
if ((rc = hash_add_entry(fruh, (void *)(fru_data))) != PICL_SUCCESS) {
(void) ptree_destroy_node(fruh);
free_data(FRU_TYPE, (fru_data));
return (rc);
}
if (locp->state_mgr == STATIC_LOC) {
if ((rc = ptree_get_propval_by_name(locp->locnodeh,
PICL_PROP_SLOT_TYPE, slot_type,
sizeof (slot_type))) == PICL_SUCCESS) {
(void) strncpy(apid_type, slot_type,
sizeof (apid_type));
}
}
/* create fru type property */
if ((rc = create_property(PICL_PTYPE_CHARSTRING, PICL_READ,
PICL_PROPNAMELEN_MAX, PICL_PROP_FRU_TYPE, NULLREAD,
NULLWRITE, fruh, NULL, apid_type)) !=
PICL_SUCCESS) {
FRUTREE_DEBUG3(FRUTREE_INIT, PTREE_CREATE_PROP_FAILED,
PICL_PROP_FRU_TYPE, frup->name, rc);
}
if (fru_present == B_FALSE) {
if ((rc = ptree_add_node(locp->locnodeh, fruh)) !=
PICL_SUCCESS) {
(void) ptree_destroy_node(fruh);
(void) hash_remove_entry(fruh);
return (rc);
}
}
if (locp->state_mgr == PLUGIN_PVT) {
(void) update_fru_state(frup, &state_changed);
return (PICL_SUCCESS);
}
if ((rc = create_fru_props(frup)) != PICL_SUCCESS) {
return (rc);
}
return (PICL_SUCCESS);
}
static picl_errno_t
add_node2cache(picl_nodehdl_t nodeh, char *class, frutree_cache_t **cacheptr)
{
int instance;
picl_errno_t rc;
char driver[PICL_PROPNAMELEN_MAX];
char bus_addr[PICL_PROPNAMELEN_MAX];
char devfs_path[PICL_PROPNAMELEN_MAX];
char node_name[PICL_PROPNAMELEN_MAX];
char port_type[PICL_PROPNAMELEN_MAX];
char label[PICL_PROPNAMELEN_MAX];
frutree_cache_t *cachep = NULL;
if (strcmp(class, SANIBEL_NETWORK_PORT) == 0) {
(void) strncpy(label, SANIBEL_NETWORK_LABEL, sizeof (label));
(void) strncpy(node_name, PICL_CLASS_PORT, sizeof (node_name));
(void) strncpy(port_type, SANIBEL_NETWORK_PORT,
sizeof (port_type));
} else if (strcmp(class, SANIBEL_SERIAL_PORT) == 0) {
(void) strncpy(label, SANIBEL_SERIAL_PORT, sizeof (label));
(void) strncpy(node_name, PICL_CLASS_PORT, sizeof (node_name));
(void) strncpy(port_type, SANIBEL_SERIAL_PORT,
sizeof (port_type));
} else if (strcmp(class, SANIBEL_PARALLEL_PORT) == 0) {
(void) strncpy(label, SANIBEL_PARALLEL_PORT, sizeof (label));
(void) strncpy(node_name, PICL_CLASS_PORT, sizeof (node_name));
(void) strncpy(port_type, SANIBEL_PARALLEL_PORT,
sizeof (port_type));
} else {
return (PICL_FAILURE);
}
if ((rc = ptree_get_propval_by_name(nodeh, PICL_PROP_INSTANCE,
&instance, sizeof (instance))) != PICL_SUCCESS) {
return (rc);
}
/* load the driver */
if (instance < 0) {
attach_driver(driver);
}
if ((rc = ptree_get_propval_by_name(nodeh, PICL_PROP_DEVFS_PATH,
devfs_path, sizeof (devfs_path))) != PICL_SUCCESS) {
return (rc);
}
/* get either bus address or unit address */
if ((rc = ptree_get_propval_by_name(nodeh, PICL_PROP_BUS_ADDR, bus_addr,
sizeof (bus_addr))) != PICL_SUCCESS) {
if ((rc = ptree_get_propval_by_name(nodeh,
PICL_PROP_UNIT_ADDRESS, bus_addr,
sizeof (bus_addr))) != PICL_SUCCESS) {
return (rc);
}
}
if ((rc = ptree_get_propval_by_name(nodeh, PICL_PROP_DRIVER_NAME,
driver, sizeof (driver))) != PICL_SUCCESS) {
return (rc);
}
cachep = (frutree_cache_t *)malloc(sizeof (frutree_cache_t));
if (NULL == cachep) {
return (PICL_NOSPACE);
}
cachep->buf[0] = '\0';
/* update the cache buffer in PICL configuration format */
(void) snprintf(cachep->buf, sizeof (cachep->buf),
"\n%s %s%d %s\n"
"\t%s %s %s %s 0 \"%s %d\"\n"
"\t%s %s %s %s 0 \"%s\"\n"
"\t%s %s %s %s 1 %d\n"
"\t%s %s %s %s 0 \"%s\"\n"
"\t%s %s %s %s 0 \"%s\"\n"
"%s\n",
"NODE", driver, instance, node_name,
"PROP", PICL_PROP_LABEL, "string", "r", label, instance,
"PROP", PICL_PROP_BUS_ADDR, "string", "r", bus_addr,
"PROP", PICL_PROP_GEO_ADDR, "uint", "r", instance,
"PROP", PICL_PROP_PORT_TYPE, "string", "r", port_type,
"PROP", PICL_PROP_DEVFS_PATH, "string", "r", devfs_path,
"ENDNODE");
*cacheptr = cachep;
return (PICL_SUCCESS);
}
/* ARGSUSED */
static int
create_device_entries(picl_nodehdl_t nodeh, void *c_args)
{
char class[PICL_CLASSNAMELEN_MAX];
char name[PICL_PROPNAMELEN_MAX];
frutree_device_args_t *device = NULL;
frutree_cache_t *cachep = NULL;
if (c_args == NULL) { /* need not create cache */
return (PICL_INVALIDARG);
}
device = (frutree_device_args_t *)c_args;
if (ptree_get_propval_by_name(nodeh, PICL_PROP_CLASSNAME,
class, sizeof (class)) != PICL_SUCCESS) {
return (PICL_WALK_CONTINUE);
}
/* add reference handle to Devices table */
(void) create_table_entry(device->device_tblhdl, nodeh, class);
/* add to Environment Devices table */
if (strcmp(class, PICL_CLASS_TEMPERATURE_SENSOR) == 0) {
if (device->env_tblhdl) {
(void) create_table_entry(device->env_tblhdl, nodeh,
class);
}
}
if (device->create_cache != B_TRUE) { /* dont create cache */
return (PICL_WALK_CONTINUE);
}
/* compare the classname and create the cache entry for the child */
if (ptree_get_propval_by_name(nodeh, PICL_PROP_NAME, name,
sizeof (name)) != PICL_SUCCESS) {
return (PICL_WALK_CONTINUE);
}
if (strcmp(name, SANIBEL_PICLNODE_PARALLEL) == 0) {
(void) strncpy(class, SANIBEL_PARALLEL_PORT, sizeof (class));
}
if (add_node2cache(nodeh, class, &cachep) != PICL_SUCCESS) {
return (PICL_WALK_CONTINUE);
}
/* add cache to the linked list */
if (cachep != NULL) {
cachep->next = NULL;
if (device->first == NULL) { /* 1st node */
device->first = cachep;
device->last = NULL;
} else if (device->last != NULL) { /* last node */
device->last->next = cachep;
device->last = cachep;
} else { /* 2nd node */
device->first->next = cachep;
device->last = cachep;
}
}
return (PICL_WALK_CONTINUE);
}
/*
* determine the state manager for this node
*/
static picl_errno_t
get_loc_type(frutree_locnode_t *locp)
{
picl_errno_t rc;
cfga_list_data_t *list = NULL;
char valbuf[PICL_PROPNAMELEN_MAX];
char slot_type[PICL_PROPNAMELEN_MAX];
if (locp->state_mgr != UNKNOWN)
return (PICL_SUCCESS);
rc = ptree_get_propval_by_name(locp->locnodeh, PICL_PROP_STATE,
(void *)valbuf, PICL_PROPNAMELEN_MAX);
if (rc == PICL_SUCCESS) { /* managed by platform specific plugin */
locp->state_mgr = PLUGIN_PVT;
return (PICL_SUCCESS);
}
/* get the info from the libcfgadm interface */
list = (cfga_list_data_t *)malloc(sizeof (cfga_list_data_t));
if (list == NULL) {
return (PICL_NOSPACE);
}
if ((rc = get_cfgadm_state(list, locp->name)) == PICL_SUCCESS) {
locp->state_mgr = CFGADM_AP;
} else {
if ((rc = ptree_get_propval_by_name(locp->locnodeh,
PICL_PROP_SLOT_TYPE, slot_type,
sizeof (slot_type))) != PICL_SUCCESS) {
free(list);
return (rc);
}
if (strcmp(slot_type, SANIBEL_SCSI_SLOT) == 0 ||
strcmp(slot_type, SANIBEL_IDE_SLOT) == 0) {
/*
* for scsi locations, if cfgadm ap is
* not present, then consider it as device
* not present
*/
locp->state_mgr = CFGADM_AP;
} else {
/*
* devices like PMC card doesnt showup in cfgadm
*/
locp->state_mgr = STATIC_LOC;
}
}
free(list);
return (PICL_SUCCESS);
}
/*
* Initialize the location node.(create all the props)
*/
static picl_errno_t
location_init(frutree_locnode_t *locp)
{
picl_errno_t rc;
boolean_t state_change;
uint64_t ap_status_time = 0;
char valbuf[PICL_PROPNAMELEN_MAX];
/* check if it is a CPU location node or not */
if (ptree_get_propval_by_name(locp->locnodeh, PICL_PROP_NAME,
(void *)valbuf, PICL_PROPNAMELEN_MAX) == PICL_SUCCESS) {
if (strncmp(valbuf, SANIBEL_PICLNODE_CPU,
strlen(SANIBEL_PICLNODE_CPU)) == 0) {
locp->cpu_node = B_TRUE;
}
}
/*
* Algorithm:
* if "State" prop is already created (node is managed by other plugin)
* does nothing
* else if cfgadm ap is found
* creates State prop and intializes it
* else
* find the nodes using libdevinfo under a given path
* at given geoaddr
* if node is found
* mark node state a connected
* else
* mark node state a empty
*/
(void) get_loc_type(locp);
if (locp->state_mgr == PLUGIN_PVT) {
(void) update_loc_state(locp, &state_change);
return (PICL_SUCCESS);
}
if (locp->state_mgr == STATIC_LOC) {
/*
* in case of scsi locations,, loc state will be connected
* no need to check again if the fru is present using libdevinfo
*/
if (locp->state != LOC_STATE_CONNECTED) {
if (is_fru_present_under_location(locp) == B_TRUE) {
locp->state = LOC_STATE_CONNECTED;
} else {
locp->state = LOC_STATE_EMPTY;
}
}
}
/* create state property */
if ((rc = create_property(PICL_PTYPE_CHARSTRING,
PICL_READ + PICL_VOLATILE, PICL_PROPNAMELEN_MAX,
PICL_PROP_STATE, get_loc_state, NULLWRITE, locp->locnodeh,
NULL, loc_state[locp->state])) != PICL_SUCCESS) {
FRUTREE_DEBUG3(FRUTREE_INIT, PTREE_CREATE_PROP_FAILED,
PICL_PROP_STATE, locp->name, rc);
return (rc);
}
ap_status_time = (uint64_t)(time(NULL));
/* create location StatusTime prop. */
if ((rc = create_property(PICL_PTYPE_TIMESTAMP, PICL_READ,
sizeof (uint64_t), PICL_PROP_STATUS_TIME, NULLREAD,
NULLWRITE, locp->locnodeh, NULL, &ap_status_time)) !=
PICL_SUCCESS) {
FRUTREE_DEBUG3(FRUTREE_INIT, PTREE_CREATE_PROP_FAILED,
PICL_PROP_STATUS_TIME, locp->name, rc);
return (rc);
}
if ((rc = update_loc_state(locp, &state_change)) != PICL_SUCCESS) {
FRUTREE_DEBUG2(FRUTREE_INIT, GET_LOC_STATE_ERR, locp->name, rc);
return (rc);
}
return (PICL_SUCCESS);
}
static frutree_port_type_t
frutree_get_port_type(frutree_portnode_t *portp)
{
char device_type[PICL_PROPNAMELEN_MAX];
frutree_port_type_t port_type = UNKNOWN_PORT;
if (portp == NULL) {
return (port_type);
}
if (ptree_get_propval_by_name(portp->portnodeh,
PICL_PROP_PORT_TYPE, device_type,
sizeof (device_type)) == PICL_SUCCESS) {
if (strcmp(device_type, SANIBEL_NETWORK_PORT) == 0) {
port_type = NETWORK_PORT;
} else if (strcmp(device_type,
SANIBEL_SERIAL_PORT) == 0) {
port_type = SERIAL_PORT;
} else if (strcmp(device_type,
SANIBEL_PARALLEL_PORT) == 0) {
port_type = PARALLEL_PORT;
}
}
return (port_type);
}
/* volatile callback function to get port condition */
static int
get_port_condition(ptree_rarg_t *rarg, void *buf)
{
picl_errno_t rc;
hashdata_t *hashptr = NULL;
frutree_portnode_t *portp = NULL;
frutree_port_type_t port_type;
if (buf == NULL) {
return (PICL_INVALIDARG);
}
if ((rc = hash_lookup_entry(rarg->nodeh, (void **)&hashptr)) !=
PICL_SUCCESS) {
return (rc);
}
portp = PORTDATA_PTR(hashptr);
if (portp == NULL) {
return (PICL_FAILURE);
}
port_type = frutree_get_port_type(portp);
if (port_type == UNKNOWN_PORT) {
portp->cond = PORT_COND_UNKNOWN;
(void) strncpy((char *)buf, port_cond[portp->cond],
PICL_PROPNAMELEN_MAX);
return (PICL_SUCCESS);
}
if ((rc = update_port_state(portp, B_TRUE)) != PICL_SUCCESS) {
return (rc);
}
(void) strncpy((char *)buf, port_cond[portp->cond],
PICL_PROPNAMELEN_MAX);
return (PICL_SUCCESS);
}
/* volatile callback function to get port state */
static int
get_port_state(ptree_rarg_t *rarg, void *buf)
{
picl_errno_t rc;
hashdata_t *hashptr = NULL;
frutree_portnode_t *portp = NULL;
frutree_port_type_t port_type;
if (buf == NULL) {
return (PICL_INVALIDARG);
}
if ((rc = hash_lookup_entry(rarg->nodeh, (void **)&hashptr)) !=
PICL_SUCCESS) {
return (rc);
}
portp = PORTDATA_PTR(hashptr);
if (portp == NULL) {
return (PICL_FAILURE);
}
port_type = frutree_get_port_type(portp);
if (port_type == UNKNOWN_PORT) {
portp->state = PORT_STATE_UNKNOWN;
(void) strncpy((char *)buf, port_state[portp->state],
PICL_PROPNAMELEN_MAX);
return (PICL_SUCCESS);
}
if ((rc = update_port_state(portp, B_TRUE)) != PICL_SUCCESS) {
return (rc);
}
(void) strncpy((char *)buf, port_state[portp->state],
PICL_PROPNAMELEN_MAX);
return (PICL_SUCCESS);
}
/*
* Creates State and Condition property for a port node
*/
static picl_errno_t
port_init(frutree_portnode_t *portp)
{
picl_prophdl_t proph;
ptree_propinfo_t propinfo;
void *vbuf;
picl_errno_t rc;
uint64_t status_time;
picl_nodehdl_t refhdl;
frutree_device_args_t device;
picl_prophdl_t tblprophdl, tblhdl;
char class[PICL_PROPNAMELEN_MAX];
if (portp == NULL) {
return (PICL_FAILURE);
}
refhdl = get_reference_handle(portp->portnodeh);
/* traverse thru platform tree and add entries to Devices table */
if (refhdl != 0) {
/* create Devices table property */
if ((rc = create_property(PICL_PTYPE_TABLE, PICL_READ,
sizeof (picl_prophdl_t), PICL_PROP_DEVICES,
NULLREAD, NULLWRITE, portp->portnodeh, &tblprophdl,
&tblhdl)) != PICL_SUCCESS) {
return (rc);
}
/* walk down the subtree and populate Devices */
if ((rc = ptree_get_propval_by_name(refhdl,
PICL_PROP_CLASSNAME, class,
sizeof (class))) != PICL_SUCCESS) {
return (rc);
}
if ((rc = create_table_entry(tblhdl, refhdl, class)) !=
PICL_SUCCESS) {
return (rc);
}
device.nodeh = refhdl;
device.device_tblhdl = tblhdl;
device.first = NULL;
device.last = NULL;
device.create_cache = B_FALSE;
if ((rc = do_action(refhdl, CREATE_DEVICES_ENTRIES,
(void *)&device)) != PICL_SUCCESS) {
return (rc);
}
if ((rc = ptree_get_prop_by_name(refhdl, PICL_PROP_INSTANCE,
&proph)) != PICL_SUCCESS) {
return (rc);
}
if ((rc = ptree_get_propinfo(proph, &propinfo)) !=
PICL_SUCCESS) {
return (rc);
}
vbuf = alloca(propinfo.piclinfo.size);
if (vbuf == NULL)
return (PICL_NOSPACE);
if ((rc = ptree_get_propval(proph, vbuf,
propinfo.piclinfo.size)) != PICL_SUCCESS) {
return (rc);
}
portp->instance = *(int *)vbuf;
if ((rc = ptree_get_prop_by_name(refhdl,
PICL_PROP_DRIVER_NAME, &proph)) != PICL_SUCCESS) {
return (rc);
}
if ((rc = ptree_get_propinfo(proph, &propinfo)) !=
PICL_SUCCESS) {
return (rc);
}
vbuf = alloca(propinfo.piclinfo.size);
if (vbuf == NULL)
return (PICL_NOSPACE);
if ((rc = ptree_get_propval(proph, vbuf,
propinfo.piclinfo.size)) != PICL_SUCCESS) {
return (rc);
}
(void) strncpy(portp->driver, (char *)vbuf,
sizeof (portp->driver));
} else {
/* this node is created using libdevinfo or conf file */
if ((rc = get_port_info(portp)) != PICL_SUCCESS) {
return (rc);
}
}
/* create state and condition properties */
if ((rc = create_property(PICL_PTYPE_CHARSTRING,
PICL_READ | PICL_VOLATILE, PICL_PROPNAMELEN_MAX,
PICL_PROP_STATE, get_port_state, NULLWRITE, portp->portnodeh,
NULL, port_state[portp->state])) != PICL_SUCCESS) {
return (rc);
}
status_time = (uint64_t)(time(NULL));
if ((rc = create_property(PICL_PTYPE_TIMESTAMP, PICL_READ,
sizeof (uint64_t), PICL_PROP_STATUS_TIME, NULLREAD,
NULLWRITE, portp->portnodeh, NULL, &status_time)) !=
PICL_SUCCESS) {
return (rc);
}
if ((rc = create_property(PICL_PTYPE_CHARSTRING,
PICL_READ | PICL_VOLATILE, PICL_PROPNAMELEN_MAX,
PICL_PROP_CONDITION, get_port_condition, NULLWRITE,
portp->portnodeh, NULL, port_cond[portp->cond])) !=
PICL_SUCCESS) {
return (rc);
}
if ((rc = create_property(PICL_PTYPE_TIMESTAMP, PICL_READ,
sizeof (uint64_t), PICL_PROP_CONDITION_TIME, NULLREAD,
NULLWRITE, portp->portnodeh, NULL, &status_time)) !=
PICL_SUCCESS) {
return (rc);
}
(void) update_port_state(portp, B_FALSE);
return (PICL_SUCCESS);
}
/*
* This routine dynamically determines the scsi name (using libcfgadm)
* that corresponds to the node specified in configuration file
*/
static picl_errno_t
init_scsi_slot(frutree_frunode_t *frup, frutree_locnode_t **ptr2locp,
boolean_t *node_name_changed)
{
picl_errno_t rc;
char devfs_path[PICL_PROPNAMELEN_MAX];
char bus_addr[PICL_PROPNAMELEN_MAX];
char label[PICL_PROPNAMELEN_MAX];
char name[MAXPATHLEN];
uint8_t geo_addr = 0;
frutree_locnode_t *locp = NULL, *new_locp = NULL;
hashdata_t *hashptr = NULL;
picl_nodehdl_t nodeh;
if (ptr2locp == NULL) {
return (PICL_INVALIDARG);
}
locp = (frutree_locnode_t *)*ptr2locp;
*node_name_changed = B_FALSE;
if (locp == NULL) {
return (PICL_FAILURE);
}
if ((rc = ptree_get_propval_by_name(locp->locnodeh,
PICL_PROP_DEVFS_PATH, devfs_path,
sizeof (devfs_path))) != PICL_SUCCESS) {
return (rc);
}
if ((rc = ptree_get_propval_by_name(locp->locnodeh,
PICL_PROP_BUS_ADDR, bus_addr,
sizeof (bus_addr))) != PICL_SUCCESS) {
return (rc);
}
/* find the dynamic ap_id from libcfgadm */
if ((rc = get_scsislot_name(devfs_path, bus_addr,
name)) != PICL_SUCCESS) {
/* if rc is NODENOTFOUND, then slot is empty */
if (rc != PICL_NODENOTFOUND) {
return (rc);
} else {
return (PICL_SUCCESS);
}
}
/* node name is same, so dont change anything */
if (strcmp(name, locp->name) == 0) {
return (PICL_SUCCESS);
}
if ((rc = ptree_get_propval_by_name(locp->locnodeh,
PICL_PROP_GEO_ADDR, &geo_addr,
sizeof (geo_addr))) != PICL_SUCCESS) {
geo_addr = 0;
}
if ((rc = ptree_get_propval_by_name(locp->locnodeh,
PICL_PROP_LABEL, label,
sizeof (label))) != PICL_SUCCESS) {
return (rc);
}
/* Now recreate the node with new name */
if ((rc = ptree_create_node(name, PICL_CLASS_LOCATION,
&nodeh)) != PICL_SUCCESS) {
return (rc);
}
/* add all the properties now */
(void) create_property(PICL_PTYPE_CHARSTRING, PICL_READ,
PICL_PROPNAMELEN_MAX, PICL_PROP_SLOT_TYPE, NULLREAD,
NULLWRITE, nodeh, (picl_prophdl_t *)NULL,
SANIBEL_SCSI_SLOT);
(void) create_property(PICL_PTYPE_CHARSTRING, PICL_READ,
PICL_PROPNAMELEN_MAX, PICL_PROP_LABEL, NULLREAD,
NULLWRITE, nodeh, (picl_prophdl_t *)NULL,
label);
(void) create_property(PICL_PTYPE_CHARSTRING, PICL_READ,
PICL_PROPNAMELEN_MAX, PICL_PROP_BUS_ADDR, NULLREAD,
NULLWRITE, nodeh, (picl_prophdl_t *)NULL,
bus_addr);
(void) create_property(PICL_PTYPE_UNSIGNED_INT, PICL_READ,
sizeof (uint8_t), PICL_PROP_GEO_ADDR, NULLREAD,
NULLWRITE, nodeh, (picl_prophdl_t *)NULL,
&geo_addr);
(void) create_property(PICL_PTYPE_CHARSTRING, PICL_READ,
PICL_PROPNAMELEN_MAX, PICL_PROP_DEVFS_PATH, NULLREAD,
NULLWRITE, nodeh, (picl_prophdl_t *)NULL,
devfs_path);
(void) ptree_add_node(frup->frunodeh, nodeh);
if ((rc = make_loc_data(name, &hashptr)) != PICL_SUCCESS) {
return (rc);
}
/* save data in hash table */
if ((rc = hash_add_entry(nodeh, (void *)hashptr)) != PICL_SUCCESS) {
free_data(hashptr->type, hashptr);
return (rc);
}
new_locp = LOCDATA_PTR(hashptr);
new_locp->locnodeh = nodeh;
*ptr2locp = new_locp;
*node_name_changed = B_TRUE;
return (PICL_SUCCESS);
}
/*
* find the child nodes under a fru and initialize them
*/
static int
frutree_initialize_children(picl_nodehdl_t childh, void *c_args)
{
picl_errno_t rc;
picl_nodehdl_t parenth;
boolean_t node_changed = B_FALSE;
hashdata_t *datap = NULL;
char name[PICL_PROPNAMELEN_MAX];
char class[PICL_PROPNAMELEN_MAX];
frutree_frunode_t *frup = NULL;
frutree_init_callback_arg_t *arg;
if (c_args == NULL) {
return (PICL_INVALIDARG);
}
arg = (frutree_init_callback_arg_t *)c_args;
frup = arg->frup;
if ((rc = ptree_get_propval_by_name(childh, PICL_PROP_PARENT,
&parenth, sizeof (parenth))) != PICL_SUCCESS) {
return (rc);
}
if (parenth != frup->frunodeh)
return (PICL_WALK_CONTINUE);
if ((rc = ptree_get_propval_by_name(childh, PICL_PROP_CLASSNAME, class,
sizeof (class))) != PICL_SUCCESS) {
return (rc);
}
if ((rc = ptree_get_propval_by_name(childh, PICL_PROP_NAME, name,
sizeof (name))) != PICL_SUCCESS) {
return (rc);
}
if (strcmp(class, PICL_CLASS_LOCATION) == 0) {
char slot_type[PICL_PROPNAMELEN_MAX];
frutree_locnode_t *locp = NULL;
frutree_frunode_t *child_frup = NULL;
/* initialize internal data structure */
if ((rc = make_loc_data(name, &datap)) != PICL_SUCCESS) {
return (PICL_WALK_CONTINUE);
}
locp = LOCDATA_PTR(datap);
locp->locnodeh = childh;
/* save data in hash table */
(void) hash_add_entry(childh, (void *)datap);
if ((rc = ptree_get_propval_by_name(locp->locnodeh,
PICL_PROP_SLOT_TYPE, slot_type,
sizeof (slot_type))) != PICL_SUCCESS) {
FRUTREE_DEBUG3(FRUTREE_INIT, PTREE_GET_PROPVAL_ERR,
PICL_PROP_SLOT_TYPE, locp->name, rc);
return (PICL_WALK_CONTINUE);
} else {
if (strcmp(slot_type, SANIBEL_SCSI_SLOT) == 0 ||
strcmp(slot_type, SANIBEL_IDE_SLOT) == 0) {
/*
* this rountine finds the valid cfgadm
* ap_id name for a given node and
* creates a new node with that name.
* If the node name is changed, the present
* node must be added to the list of nodes
* to be deleted from tree after ptree walk.
*/
(void) init_scsi_slot(frup, &locp,
&node_changed);
if (node_changed) {
delete_list_t *nodep = NULL;
/*
* add this node to list of nodes
* to be removed
*/
nodep = (delete_list_t *)malloc(
sizeof (delete_list_t));
if (nodep == NULL) {
return (PICL_NOSPACE);
}
nodep->nodeh = childh;
nodep->next = NULL;
if (arg->first == NULL) {
arg->first = nodep;
} else { /* add 2 front */
nodep->next = arg->first;
arg->first = nodep;
}
}
}
}
if ((rc = location_init(locp)) != PICL_SUCCESS) {
return (PICL_WALK_CONTINUE);
}
/* if location is empty, done */
if (locp->state == LOC_STATE_EMPTY ||
locp->state == LOC_STATE_UNKNOWN) {
return (PICL_WALK_CONTINUE);
}
/* create the fru node and initialize it */
if ((rc = create_fru_node(locp, &child_frup)) !=
PICL_SUCCESS) {
return (PICL_WALK_CONTINUE);
}
/*
* if fru is already configured, create the
* subtree under the child fru
*/
if (child_frup->state == FRU_STATE_CONFIGURED) {
/* initialize the fru_path */
if ((rc = probe_fru(child_frup, B_TRUE)) !=
PICL_SUCCESS) {
FRUTREE_DEBUG2(EVENTS, PROBE_FRU_ERR,
child_frup->name, rc);
}
}
} else if (strcmp(class, PICL_CLASS_PORT) == 0) {
frutree_portnode_t *portp = NULL;
if ((rc = make_port_data(name, &datap)) != PICL_SUCCESS) {
return (PICL_WALK_CONTINUE);
}
(void) hash_add_entry(childh, (void *)datap);
portp = PORTDATA_PTR(datap);
portp->portnodeh = childh;
(void) port_init(portp);
}
return (PICL_WALK_CONTINUE);
}
/* traverse thru all locations under fru and initiate connects */
static int
initiate_connects(picl_nodehdl_t nodeh, void *args)
{
picl_errno_t rc;
hashdata_t *hashptr = NULL;
picl_nodehdl_t parenth;
frutree_frunode_t *frup = NULL;
frutree_locnode_t *locp = NULL;
if (args == NULL) {
return (PICL_INVALIDARG);
}
frup = (frutree_frunode_t *)args;
if ((rc = ptree_get_propval_by_name(nodeh, PICL_PROP_PARENT,
&parenth, sizeof (parenth))) != PICL_SUCCESS) {
return (rc);
}
if (parenth != frup->frunodeh)
return (PICL_WALK_CONTINUE);
if ((rc = hash_lookup_entry(nodeh, (void **)&hashptr)) !=
PICL_SUCCESS) {
return (PICL_WALK_CONTINUE);
}
locp = LOCDATA_PTR(hashptr);
if (locp->state == LOC_STATE_EMPTY ||
locp->state == LOC_STATE_UNKNOWN ||
locp->state == LOC_STATE_CONNECTED) {
return (PICL_WALK_CONTINUE);
}
/* if loc is not connected, do a connect operation */
if (locp->autoconfig_enabled) {
if ((rc = connect_fru(locp)) != PICL_SUCCESS) {
FRUTREE_DEBUG2(EVENTS, CONNECT_FAILED_ERR,
locp->name, rc);
}
}
return (PICL_WALK_CONTINUE);
}
/*
* Initializes the subtree under a FRU
*/
static picl_errno_t
fru_init(frutree_frunode_t *frup)
{
picl_errno_t rc;
delete_list_t *tmp = NULL, *curr = NULL;
frutree_init_callback_arg_t arg;
if (frup == NULL) {
return (PICL_INVALIDARG);
}
arg.frup = frup;
arg.first = NULL;
/*
* this routine creates internal data structures for
* all the children under this fru and initializes them
*/
if ((rc = do_action(frup->frunodeh, INIT_FRU,
(void *)&arg)) != PICL_SUCCESS) {
return (rc);
}
/* traverse thru delete_nodes_list and delete the nodes from tree */
curr = arg.first;
while (curr) {
tmp = curr;
(void) ptree_delete_node(tmp->nodeh);
(void) ptree_destroy_node(tmp->nodeh);
(void) hash_remove_entry(tmp->nodeh);
free(tmp);
curr = curr->next;
}
/*
* dont post events during intialization (for other FRUs)
* chassis intialization will take care of posting events
* for complete frutree
*/
if ((frup->frunodeh == chassish) ||
(post_picl_events == B_TRUE)) {
if ((rc = do_action(frup->frunodeh, POST_EVENTS, NULL)) !=
PICL_SUCCESS) {
FRUTREE_DEBUG1(LOG_ERR, "SUNW_frutree:Error in "
"posting picl events(error=%d)", rc);
}
}
if (frup->frunodeh == chassish) {
post_picl_events = B_TRUE;
frutree_connects_initiated = B_TRUE;
}
/* initiate connects */
if ((rc = ptree_walk_tree_by_class(frup->frunodeh, PICL_CLASS_LOCATION,
(void *)frup, initiate_connects)) != PICL_SUCCESS) {
return (rc);
}
return (PICL_SUCCESS);
}
/*ARGSUSED*/
static int
post_events(picl_nodehdl_t childh, void *c_args)
{
int rc;
hashdata_t *hashptr = NULL;
frutree_frunode_t *frup = NULL;
frutree_locnode_t *locp = NULL;
frutree_portnode_t *portp = NULL;
char classval[PICL_CLASSNAMELEN_MAX];
if ((rc = ptree_get_propval_by_name(childh, PICL_PROP_CLASSNAME,
classval, sizeof (classval))) != PICL_SUCCESS) {
return (PICL_WALK_CONTINUE);
}
if ((rc = hash_lookup_entry(childh, (void **)&hashptr)) !=
PICL_SUCCESS) {
return (PICL_WALK_CONTINUE);
}
if (strcmp(classval, PICL_CLASS_LOCATION) == 0) {
locp = LOCDATA_PTR(hashptr);
if ((rc = post_piclevent(PICLEVENT_STATE_CHANGE,
loc_state[locp->state], loc_state[locp->prev_state],
childh, WAIT)) != PICL_SUCCESS) {
FRUTREE_DEBUG3(EVENTS, PTREE_POST_PICLEVENT_ERR,
locp->name, PICLEVENT_STATE_CHANGE, rc);
}
return (PICL_WALK_CONTINUE);
}
if (strcmp(classval, PICL_CLASS_FRU) == 0) {
frup = FRUDATA_PTR(hashptr);
if ((rc = post_piclevent(PICLEVENT_STATE_CHANGE,
fru_state[frup->state], fru_state[frup->prev_state],
childh, WAIT)) != PICL_SUCCESS) {
FRUTREE_DEBUG3(EVENTS, PTREE_POST_PICLEVENT_ERR,
frup->name, PICLEVENT_STATE_CHANGE, rc);
}
if ((rc = post_piclevent(PICLEVENT_CONDITION_CHANGE,
fru_cond[frup->cond], fru_cond[frup->prev_cond],
frup->frunodeh, WAIT)) != PICL_SUCCESS) {
FRUTREE_DEBUG3(EVENTS, PTREE_POST_PICLEVENT_ERR,
frup->name, PICLEVENT_CONDITION_CHANGE, rc);
}
return (PICL_WALK_CONTINUE);
}
if (strcmp(classval, PICL_CLASS_PORT) == 0) {
portp = PORTDATA_PTR(hashptr);
if ((rc = post_piclevent(PICLEVENT_STATE_CHANGE,
port_state[portp->state], NULL,
portp->portnodeh, WAIT)) != PICL_SUCCESS) {
FRUTREE_DEBUG3(EVENTS, PTREE_POST_PICLEVENT_ERR,
portp->name, PICLEVENT_STATE_CHANGE, rc);
}
if ((rc = post_piclevent(PICLEVENT_CONDITION_CHANGE,
port_cond[portp->cond], NULL,
portp->portnodeh, WAIT)) != PICL_SUCCESS) {
FRUTREE_DEBUG3(EVENTS, PTREE_POST_PICLEVENT_ERR,
portp->name, PICLEVENT_CONDITION_CHANGE, rc);
}
return (PICL_WALK_CONTINUE);
}
return (PICL_WALK_CONTINUE);
}
/*
* This function is a utility function that calls the
* appropriate call back function for the all the nodes under
* the specified root node.
* future additions can be done by defining new action and callback.
*/
static picl_errno_t
do_action(picl_nodehdl_t root, int action, void *cargs)
{
int rc;
callback_t func_ptr;
char *class = NULL;
switch (action) {
case INIT_FRU:
func_ptr = frutree_initialize_children;
class = NULL;
break;
case CREATE_DEVICES_ENTRIES:
func_ptr = create_device_entries;
class = NULL;
break;
case POST_EVENTS:
func_ptr = post_events;
class = NULL;
break;
default:
return (PICL_INVALIDARG);
}
if ((rc = ptree_walk_tree_by_class(root, class, cargs,
func_ptr)) != PICL_SUCCESS) {
return (rc);
}
return (PICL_SUCCESS);
}
static picl_errno_t
frutree_update_chassis_state(frutree_frustate_t state,
frutree_frustate_t prev_state)
{
uint64_t ap_status_time;
picl_errno_t rc = 0;
char present_state[PICL_PROPNAMELEN_MAX];
(void) strncpy(present_state, fru_state[state], sizeof (present_state));
(void) ptree_update_propval_by_name(chassish,
PICL_PROP_STATE, present_state, sizeof (present_state));
ap_status_time = (uint64_t)(time(NULL));
if ((rc = ptree_update_propval_by_name(chassish,
PICL_PROP_STATUS_TIME, (void *)&ap_status_time,
sizeof (ap_status_time))) != PICL_SUCCESS) {
FRUTREE_DEBUG3(EVENTS, PTREE_UPDATE_PROP_ERR,
PICL_PROP_STATUS_TIME, PICL_NODE_CHASSIS, rc);
}
if ((rc = post_piclevent(PICLEVENT_STATE_CHANGE,
fru_state[state], fru_state[prev_state],
chassish, WAIT)) != PICL_SUCCESS) {
FRUTREE_DEBUG3(EVENTS, PTREE_POST_PICLEVENT_ERR,
PICL_NODE_CHASSIS, PICLEVENT_STATE_CHANGE, rc);
}
return (PICL_SUCCESS);
}
static picl_errno_t
frutree_init()
{
picl_errno_t rc;
frutree_frunode_t *frup = NULL;
hashdata_t *hashptr = NULL;
if ((rc = ptree_get_node_by_path(PLATFORM_PATH, &platformh)) !=
PICL_SUCCESS) {
return (rc);
}
if ((rc = hash_lookup_entry(chassish, (void **)&hashptr)) !=
PICL_SUCCESS) {
return (rc);
}
frup = FRUDATA_PTR(hashptr);
/* create the nodes in conf file under chassis node */
if ((rc = picld_pluginutil_parse_config_file(chassish,
conf_file)) != PICL_SUCCESS) {
/* update chassis state to unconfigured */
(void) frutree_update_chassis_state(
FRU_STATE_UNCONFIGURED, FRU_STATE_UNKNOWN);
return (rc);
}
/* update chassis state to configuring */
(void) frutree_update_chassis_state(
FRU_STATE_CONFIGURING, FRU_STATE_UNCONFIGURED);
if (scsi_info_init() != PICL_SUCCESS) {
/* update chassis state to unconfigured */
(void) frutree_update_chassis_state(
FRU_STATE_UNCONFIGURED, FRU_STATE_CONFIGURING);
return (PICL_FAILURE);
}
/* traverse thru all the nodes under chassis, initialize them */
if ((rc = fru_init(frup)) != PICL_SUCCESS) {
/* update chassis state to unconfigured */
(void) frutree_update_chassis_state(
FRU_STATE_UNCONFIGURED, FRU_STATE_CONFIGURING);
scsi_info_fini();
return (rc);
}
/* free the memory used during initialization */
scsi_info_fini();
/* start node monitoring thread */
if (pthread_create(&monitor_tid, NULL, monitor_node_status,
NULL) != 0) {
FRUTREE_DEBUG0(EVENTS, "SUNW_frutree:Error in creating node"
" monitoring thread");
}
(void) pthread_mutex_lock(&frup->mutex);
frup->state = FRU_STATE_CONFIGURED;
(void) pthread_mutex_unlock(&frup->mutex);
/* update chassis state to configured */
(void) frutree_update_chassis_state(
FRU_STATE_CONFIGURED, FRU_STATE_CONFIGURING);
return (PICL_SUCCESS);
}
/* ARGSUSED */
static void *
init_thread(void *arg)
{
picl_errno_t rc;
FRUTREE_DEBUG0(FRUTREE_INIT, "init_thread begin");
(void) pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
(void) pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
if (get_configuration_file() != PICL_SUCCESS) {
return (NULL);
}
FRUTREE_DEBUG1(FRUTREE_INIT, "conf_file = %s", conf_file);
if ((rc = frutree_init()) != PICL_SUCCESS) {
FRUTREE_DEBUG1(FRUTREE_INIT, "frutree_init failed, error = %d",
rc);
}
FRUTREE_DEBUG0(FRUTREE_INIT, "init_thread end");
return (NULL);
}
/* ARGSUSED */
static void
event_completion_handler(char *ename, void *earg, size_t size)
{
if (frutree_debug & EV_COMPLETION) {
char name[PICL_PROPNAMELEN_MAX];
nvlist_t *nvlp;
char *value = NULL;
char *arg = NULL;
picl_nodehdl_t fruhdl;
time_t current_time;
if (strncmp(ename, PICLEVENT_STATE_CHANGE,
strlen(PICLEVENT_STATE_CHANGE)) == 0) {
arg = PICLEVENTARG_STATE;
} else if (strncmp(ename, PICLEVENT_CONDITION_CHANGE,
strlen(PICLEVENT_CONDITION_CHANGE)) == 0) {
arg = PICLEVENTARG_CONDITION;
}
(void) nvlist_unpack((char *)earg, size, &nvlp, NULL);
(void) nvlist_lookup_uint64(nvlp, PICLEVENTARG_NODEHANDLE,
&fruhdl);
if (arg != NULL)
(void) nvlist_lookup_string(nvlp, arg, &value);
(void) ptree_get_propval_by_name(fruhdl, PICL_PROP_NAME,
(void *)name, sizeof (name));
current_time = (uint64_t)(time(NULL));
if (value != NULL) {
FRUTREE_DEBUG4(EV_COMPLETION, "ev_completed[%s]%s(%s) "
"on %s", ctime(&current_time), ename, value, name);
}
nvlist_free(nvlp);
}
(void) mutex_lock(&piclevent_mutex);
piclevent_pending = 0;
(void) cond_broadcast(&piclevent_completed_cv);
(void) mutex_unlock(&piclevent_mutex);
free(earg);
free(ename);
}
picl_errno_t
post_piclevent(const char *event, char *val1,
char *val2, picl_nodehdl_t nodeh, frutree_wait_t wait)
{
nvlist_t *nvl;
size_t nvl_size;
char *pack_buf = NULL;
char *ename = NULL;
char *arg = NULL;
picl_errno_t rc;
timestruc_t to;
struct timeval tp;
if (event == NULL || val1 == NULL) {
return (PICL_INVALIDARG);
}
if (nvlist_alloc(&nvl, NV_UNIQUE_NAME_TYPE, NULL)) {
return (PICL_FAILURE);
}
if (nvlist_add_uint64(nvl, PICLEVENTARG_NODEHANDLE, nodeh)) {
nvlist_free(nvl);
return (PICL_FAILURE);
}
if ((ename = strdup(event)) == NULL) {
nvlist_free(nvl);
return (PICL_NOSPACE);
}
if (strncmp(ename, PICLEVENT_STATE_CHANGE,
strlen(PICLEVENT_STATE_CHANGE)) == 0) {
arg = PICLEVENTARG_STATE;
} else if (strncmp(ename, PICLEVENT_CONDITION_CHANGE,
strlen(PICLEVENT_CONDITION_CHANGE)) == 0) {
arg = PICLEVENTARG_CONDITION;
} else {
free(ename);
nvlist_free(nvl);
return (PICL_INVALIDARG);
}
if (nvlist_add_string(nvl, arg, val1)) {
free(ename);
nvlist_free(nvl);
return (PICL_FAILURE);
}
if (strncmp(ename, PICLEVENT_CONDITION_CHANGE,
strlen(PICLEVENT_CONDITION_CHANGE)) == 0) {
if (nvlist_pack(nvl, &pack_buf, &nvl_size, NV_ENCODE_NATIVE,
NULL)) {
free(ename);
nvlist_free(nvl);
return (PICL_FAILURE);
}
} else { /* state change event */
if (val2 != NULL) {
/* if there is a last state, add it to nvlist */
if (nvlist_add_string(nvl,
PICLEVENTARG_LAST_STATE, val2)) {
free(ename);
nvlist_free(nvl);
return (PICL_FAILURE);
}
}
}
if (nvlist_pack(nvl, &pack_buf, &nvl_size, NV_ENCODE_NATIVE, NULL)) {
free(ename);
nvlist_free(nvl);
return (PICL_FAILURE);
}
(void) mutex_lock(&piclevent_mutex);
while (piclevent_pending) {
(void) cond_wait(&piclevent_completed_cv,
&piclevent_mutex);
}
piclevent_pending = 1;
(void) mutex_unlock(&piclevent_mutex);
if ((rc = ptree_post_event(ename, pack_buf, nvl_size,
event_completion_handler)) != PICL_SUCCESS) {
free(ename);
free(pack_buf);
nvlist_free(nvl);
(void) mutex_lock(&piclevent_mutex);
piclevent_pending = 0;
(void) mutex_unlock(&piclevent_mutex);
return (rc);
}
if (frutree_debug) {
char name[PICL_PROPNAMELEN_MAX];
(void) ptree_get_propval_by_name(nodeh, PICL_PROP_NAME,
name, sizeof (name));
if (val2 != NULL) {
FRUTREE_DEBUG4(EVENTS, "%s(%s -> %s) on %s", ename,
val2, val1, name);
} else {
FRUTREE_DEBUG3(EVENTS, "%s(%s) on %s", ename,
val1, name);
}
}
if (wait) { /* wait for the event to be handled */
(void) mutex_lock(&piclevent_mutex);
while (piclevent_pending) {
(void) gettimeofday(&tp, NULL);
to.tv_sec = tp.tv_sec + 1;
to.tv_nsec = tp.tv_usec * 1000;
(void) cond_timedwait(&piclevent_completed_cv,
&piclevent_mutex, &to);
}
(void) mutex_unlock(&piclevent_mutex);
}
nvlist_free(nvl);
return (PICL_SUCCESS);
}
/*
* return values
* -1 : error
* 0 : not enabled
* 1 : enabled
*/
/* ARGSUSED */
static int
is_autoconfig_enabled(char *loc_name)
{
return (1);
}
static picl_errno_t
update_loc_type(frutree_locnode_t *locp)
{
cfga_list_data_t *list = NULL;
/* get the info from the libcfgadm interface */
list = (cfga_list_data_t *)malloc(sizeof (cfga_list_data_t));
if (list == NULL) {
return (PICL_NOSPACE);
}
if (get_cfgadm_state(list, locp->name) == PICL_SUCCESS) {
locp->state_mgr = CFGADM_AP;
free(list);
return (PICL_SUCCESS);
}
free(list);
return (PICL_NODENOTFOUND);
}
/*
* handles DR_INCOMING_RES on chassis node
* (refresh piclfrutree tree)
*/
static int
reconfigure_chassis(picl_nodehdl_t nodeh, void *args)
{
picl_errno_t rc;
hashdata_t *hashptr = NULL;
picl_nodehdl_t parenth, childh;
frutree_frunode_t *frup = NULL, *child_frup = NULL;
frutree_locnode_t *locp = NULL;
boolean_t state_changed = B_FALSE;
boolean_t cond_changed = B_FALSE;
frutree_dr_arg_t dr_arg;
if (args == NULL) {
return (PICL_INVALIDARG);
}
frup = (frutree_frunode_t *)args;
if ((rc = ptree_get_propval_by_name(nodeh, PICL_PROP_PARENT,
&parenth, sizeof (parenth))) != PICL_SUCCESS) {
return (rc);
}
if (parenth != frup->frunodeh)
return (PICL_WALK_CONTINUE);
if ((rc = hash_lookup_entry(nodeh, (void **)&hashptr)) !=
PICL_SUCCESS) {
return (PICL_WALK_CONTINUE);
}
locp = LOCDATA_PTR(hashptr);
/* if the location has child fru, get its information */
if (ptree_get_propval_by_name(nodeh, PICL_PROP_CHILD,
&childh, sizeof (childh)) == PICL_SUCCESS) {
/* get the child fru information */
if (hash_lookup_entry(childh, (void **)&hashptr) ==
PICL_SUCCESS) {
child_frup = FRUDATA_PTR(hashptr);
}
}
/* for each location, update the state */
if (locp->state_mgr == STATIC_LOC) {
/* check if cfgadm ap_id is present */
rc = update_loc_type(locp);
if (rc == PICL_SUCCESS) {
if (child_frup) {
child_frup->state_mgr = locp->state_mgr;
(void) update_fru_state(child_frup,
&state_changed);
}
}
}
state_changed = B_FALSE;
(void) update_loc_state(locp, &state_changed);
if (state_changed) {
switch (locp->state) {
case LOC_STATE_CONNECTED:
case LOC_STATE_DISCONNECTED:
if (locp->prev_state == LOC_STATE_EMPTY ||
locp->prev_state == LOC_STATE_UNKNOWN) {
/* handle fru insertion */
dr_arg.action = HANDLE_INSERT;
} else {
/* handle loc state change */
dr_arg.action = HANDLE_LOCSTATE_CHANGE;
}
break;
case LOC_STATE_EMPTY:
/* handle fru removal */
if (locp->prev_state == LOC_STATE_UNKNOWN) {
/* post piclevent to update led */
dr_arg.action = HANDLE_LOCSTATE_CHANGE;
} else {
/* disconnected fru is removed */
dr_arg.action = HANDLE_REMOVE;
}
break;
default:
return (PICL_WALK_CONTINUE);
} /* end of switch */
dr_arg.data = locp;
(void) pthread_mutex_lock(&ev_mutex);
if ((rc = add_to_queue(dr_arg)) != PICL_SUCCESS) {
(void) pthread_mutex_unlock(&ev_mutex);
return (PICL_WALK_CONTINUE);
}
(void) pthread_cond_signal(&ev_cond);
(void) pthread_mutex_unlock(&ev_mutex);
return (PICL_WALK_CONTINUE);
} else {
/* connect the disconnect locations */
if (locp->state == LOC_STATE_DISCONNECTED &&
locp->autoconfig_enabled == B_TRUE) {
if ((rc = connect_fru(locp)) != PICL_SUCCESS) {
FRUTREE_DEBUG2(EVENTS, CONNECT_FAILED_ERR,
locp->name, rc);
}
return (PICL_WALK_CONTINUE);
}
}
/* post picl event for child fru */
if (child_frup == NULL) {
return (PICL_WALK_CONTINUE);
}
/* update the state */
(void) update_fru_state(child_frup, &state_changed);
if (state_changed) {
if ((rc = post_piclevent(PICLEVENT_STATE_CHANGE,
fru_state[child_frup->state],
fru_state[child_frup->prev_state],
child_frup->frunodeh, WAIT)) != PICL_SUCCESS) {
FRUTREE_DEBUG3(EVENTS, PTREE_POST_PICLEVENT_ERR,
child_frup->name, PICLEVENT_STATE_CHANGE, rc);
}
}
/* update the condition */
(void) update_fru_condition(child_frup, &cond_changed);
if (cond_changed) {
if ((rc = post_piclevent(PICLEVENT_CONDITION_CHANGE,
fru_cond[child_frup->cond],
fru_cond[child_frup->prev_cond],
child_frup->frunodeh, WAIT)) != PICL_SUCCESS) {
FRUTREE_DEBUG3(EVENTS, PTREE_POST_PICLEVENT_ERR,
child_frup->name, PICLEVENT_CONDITION_CHANGE,
rc);
}
}
return (PICL_WALK_CONTINUE);
}
static picl_errno_t
handle_chassis_configure(frutree_frunode_t *frup)
{
picl_errno_t rc;
if (frup == NULL) {
return (PICL_INVALIDARG);
}
(void) pthread_mutex_lock(&frup->mutex);
FRUTREE_DEBUG1(EVENTS, "DR_INCOMING_RES on %s", frup->name);
if (frup->state == FRU_STATE_UNCONFIGURED) {
frup->state = FRU_STATE_CONFIGURING;
(void) pthread_mutex_unlock(&frup->mutex);
/* initial probe/initialization */
/* create a thread to do the initialization */
if (pthread_create(&init_threadID, NULL, &init_thread,
NULL) != 0) {
return (PICL_FAILURE);
}
return (PICL_SUCCESS);
}
(void) pthread_mutex_unlock(&frup->mutex);
/*
* 1. update the state of all the nodes in chassis
* 2. handle all the state changes accordingly
*/
if ((rc = ptree_walk_tree_by_class(chassish, PICL_CLASS_LOCATION,
(void *)frup, reconfigure_chassis)) != PICL_SUCCESS) {
return (rc);
}
return (PICL_SUCCESS);
}
static picl_errno_t
handle_chassis_unconfigure(frutree_frunode_t *frup)
{
picl_errno_t rc;
if (frup->state == FRU_STATE_UNCONFIGURED) {
return (PICL_SUCCESS);
}
/* do any cleanups here */
if ((rc = post_piclevent(PICLEVENT_STATE_CHANGE,
PICLEVENTARGVAL_UNCONFIGURING, PICLEVENTARGVAL_CONFIGURED,
chassish, WAIT)) != PICL_SUCCESS) {
FRUTREE_DEBUG3(EVENTS, PTREE_POST_PICLEVENT_ERR,
PICL_NODE_CHASSIS, PICLEVENT_STATE_CHANGE, rc);
}
if ((rc = ptree_update_propval_by_name(chassish,
PICL_PROP_STATE, PICLEVENTARGVAL_UNCONFIGURED,
PICL_PROPNAMELEN_MAX)) != PICL_SUCCESS) {
FRUTREE_DEBUG3(EVENTS, PTREE_UPDATE_PROP_ERR,
PICL_PROP_STATE, PICL_NODE_CHASSIS, rc);
}
frup->prev_state = FRU_STATE_CONFIGURED;
frup->state = FRU_STATE_UNCONFIGURED;
(void) handle_fru_unconfigure(frup);
if ((rc = post_piclevent(PICLEVENT_STATE_CHANGE,
PICLEVENTARGVAL_UNCONFIGURED, PICLEVENTARGVAL_UNCONFIGURING,
chassish, WAIT)) != PICL_SUCCESS) {
FRUTREE_DEBUG3(EVENTS, PTREE_POST_PICLEVENT_ERR,
PICL_NODE_CHASSIS, PICLEVENT_STATE_CHANGE, rc);
}
return (PICL_SUCCESS);
}
static picl_errno_t
configuration_fn(frutree_dr_arg_t *dr_arg)
{
picl_errno_t rc;
picl_nodehdl_t parenth;
cfga_flags_t flags = 0;
frutree_frunode_t *frup = NULL;
frutree_locnode_t *locp = NULL;
hashdata_t *hashptr = NULL;
boolean_t state_changed = B_FALSE;
if (dr_arg == NULL)
return (PICL_FAILURE);
frup = (frutree_frunode_t *)dr_arg->data;
if (frup == NULL) {
free(dr_arg);
return (PICL_FAILURE);
}
if (frup->frunodeh == chassish) {
rc = handle_chassis_configure(frup);
free(dr_arg);
return (rc);
}
if ((rc = ptree_get_propval_by_name(frup->frunodeh, PICL_PROP_PARENT,
&parenth, sizeof (parenth))) != PICL_SUCCESS) {
free(dr_arg);
return (rc);
}
if ((rc = hash_lookup_entry(parenth, (void **)&hashptr)) !=
PICL_SUCCESS) {
free(dr_arg);
return (rc);
}
locp = LOCDATA_PTR(hashptr);
/*
* update the location state also, as this could be
* user initiated connect operation
*/
(void) update_loc_state(locp, &state_changed);
if (state_changed)
if ((rc = post_piclevent(PICLEVENT_STATE_CHANGE,
loc_state[locp->state], loc_state[locp->prev_state],
locp->locnodeh, WAIT)) != PICL_SUCCESS) {
FRUTREE_DEBUG3(EVENTS, PTREE_POST_PICLEVENT_ERR,
locp->name, PICLEVENT_STATE_CHANGE, rc);
}
switch (dr_arg->action) {
case CPU_ONLINE:
flags |= CFGA_FLAG_FORCE;
FRUTREE_DEBUG1(EVENTS, "CPU online on %s", frup->name);
if (locp->state != LOC_STATE_CONNECTED) {
if (locp->autoconfig_enabled) {
if ((rc = connect_fru(locp)) != PICL_SUCCESS) {
FRUTREE_DEBUG2(EVENTS,
CONNECT_FAILED_ERR,
locp->name, rc);
}
}
break;
} /*FALLTHRU*/
/* do configure now */
case CONFIGURE_FRU: /* dr_incoming_res */
FRUTREE_DEBUG1(EVENTS, "DR_INCOMING_RES on %s", frup->name);
if ((rc = configure_fru(frup, flags)) != PICL_SUCCESS) {
FRUTREE_DEBUG2(EVENTS, CONFIGURE_FAILED_ERR,
frup->name, rc);
break;
}
}
free(dr_arg);
return (PICL_SUCCESS);
}
/* handles all dr related events */
static picl_errno_t
handle_dr_event(frutree_dr_arg_t *dr_arg)
{
picl_errno_t rc;
picl_nodehdl_t loch, childh;
hashdata_t *hashptr = NULL;
cfga_flags_t flags = 0;
frutree_dr_arg_t *arg = NULL;
frutree_dr_arg_t fru_dr_arg;
frutree_locnode_t *locp = NULL;
frutree_frunode_t *frup = NULL, *child_frup = NULL;
boolean_t state_changed = B_FALSE, cond_changed = B_FALSE;
switch (dr_arg->action) {
case CPU_ONLINE:
case CONFIGURE_FRU:
frup = (frutree_frunode_t *)dr_arg->data;
arg = (frutree_dr_arg_t *)malloc(sizeof (frutree_dr_arg_t));
if (arg == NULL) {
FRUTREE_DEBUG2(EVENTS, CONFIGURE_FAILED_ERR,
frup->name, PICL_NOSPACE);
return (NULL);
}
arg->action = dr_arg->action;
arg->data = dr_arg->data;
(void) configuration_fn((void *)arg);
break;
case CPU_OFFLINE:
flags |= CFGA_FLAG_FORCE;
frup = (frutree_frunode_t *)dr_arg->data;
if (frup == NULL) {
break;
}
FRUTREE_DEBUG1(EVENTS, "CPU_OFFLINE on %s", frup->name);
if ((rc = unconfigure_fru(frup, flags)) != PICL_SUCCESS) {
FRUTREE_DEBUG2(EVENTS, UNCONFIG_FAILED_ERR, frup->name, rc);
break;
}
if ((rc = handle_fru_unconfigure(frup)) != PICL_SUCCESS) {
FRUTREE_DEBUG3(EVENTS, EVENT_NOT_HANDLED, PICLEVENT_DR_REQ,
frup->name, rc);
}
break;
case UNCONFIGURE_FRU: /* dr_outgoing_res */
frup = (frutree_frunode_t *)dr_arg->data;
if (frup == NULL) {
break;
}
FRUTREE_DEBUG1(EVENTS, "DR_OUTGOING_RES on %s", frup->name);
if (frup->frunodeh == chassish) {
(void) handle_chassis_unconfigure(frup);
break;
}
if ((rc = unconfigure_fru(frup, flags)) != PICL_SUCCESS) {
FRUTREE_DEBUG2(EVENTS, UNCONFIG_FAILED_ERR, frup->name, rc);
break;
}
if ((rc = handle_fru_unconfigure(frup)) != PICL_SUCCESS) {
FRUTREE_DEBUG3(EVENTS, EVENT_NOT_HANDLED,
PICLEVENT_DR_REQ, frup->name, rc);
}
if (ptree_get_propval_by_name(frup->frunodeh, PICL_PROP_PARENT,
&loch, sizeof (loch)) != PICL_SUCCESS) {
break;
}
if ((rc = hash_lookup_entry(loch, (void **)&hashptr)) !=
PICL_SUCCESS) {
break;
}
locp = LOCDATA_PTR(hashptr);
/* check the autoconfig flag */
if (locp->autoconfig_enabled == B_FALSE) {
break;
}
if ((rc = disconnect_fru(locp)) != PICL_SUCCESS) {
FRUTREE_DEBUG2(EVENTS, "SUNW_frutree:Disconnect on %s "
"failed(error=%d)", locp->name, rc);
}
break;
case HANDLE_CONFIGURE: /* basic hotswap operation */
frup = (frutree_frunode_t *)dr_arg->data;
if (frup == NULL) {
break;
}
FRUTREE_DEBUG1(EVENTS, "HANDLE CONFIGURE on %s", frup->name);
handle_fru_configure(frup);
break;
case HANDLE_UNCONFIGURE: /* basic hotswap operation */
/* cleanup the internal data structures */
frup = (frutree_frunode_t *)dr_arg->data;
if (frup == NULL) {
break;
}
FRUTREE_DEBUG1(EVENTS, "HANDLE UNCONFIGURE on %s", frup->name);
if ((rc = post_piclevent(PICLEVENT_STATE_CHANGE,
fru_state[frup->state], fru_state[frup->prev_state],
frup->frunodeh, WAIT)) != PICL_SUCCESS) {
FRUTREE_DEBUG3(EVENTS, PTREE_POST_PICLEVENT_ERR,
frup->name, PICLEVENT_STATE_CHANGE, rc);
}
/* update the fru condition */
(void) update_fru_condition(frup, &state_changed);
if (state_changed) {
if ((rc = post_piclevent(PICLEVENT_CONDITION_CHANGE,
fru_cond[frup->cond], fru_cond[frup->prev_cond],
frup->frunodeh, WAIT)) != PICL_SUCCESS) {
FRUTREE_DEBUG3(EVENTS, PTREE_POST_PICLEVENT_ERR,
frup->name, PICLEVENT_CONDITION_CHANGE, rc);
}
}
if ((rc = handle_fru_unconfigure(frup)) != PICL_SUCCESS) {
FRUTREE_DEBUG3(EVENTS, EVENT_NOT_HANDLED,
PICLEVENT_DR_AP_STATE_CHANGE, frup->name, rc);
}
break;
case HANDLE_LOCSTATE_CHANGE: /* basic hotswap operation */
/* posts state change events of location */
locp = (frutree_locnode_t *)dr_arg->data;
if (locp == NULL) {
break;
}
FRUTREE_DEBUG1(EVENTS, "HANDLE LOC STATE CHANGE on %s", locp->name);
if ((rc = post_piclevent(PICLEVENT_STATE_CHANGE,
loc_state[locp->state], loc_state[locp->prev_state],
locp->locnodeh, WAIT)) != PICL_SUCCESS) {
FRUTREE_DEBUG3(EVENTS, PTREE_POST_PICLEVENT_ERR,
locp->name, PICLEVENT_STATE_CHANGE, rc);
}
/* wakeup threads sleeping on this condition */
(void) pthread_mutex_lock(&locp->mutex);
if (locp->state == LOC_STATE_CONNECTED) {
(void) pthread_cond_broadcast(&locp->cond_cv);
}
(void) pthread_mutex_unlock(&locp->mutex);
/* if the location has child fru, get its information */
if (ptree_get_propval_by_name(locp->locnodeh, PICL_PROP_CHILD,
&childh, sizeof (childh)) == PICL_SUCCESS) {
/* get the child fru information */
if (hash_lookup_entry(childh, (void **)&hashptr) ==
PICL_SUCCESS) {
child_frup = FRUDATA_PTR(hashptr);
}
}
/* update the child fru state and handle any state changes */
if (child_frup == NULL) {
break;
}
if ((rc = update_fru_state(child_frup, &state_changed)) !=
PICL_SUCCESS) {
FRUTREE_DEBUG2(EVENTS, GET_FRU_STATE_ERR, child_frup->name, rc);
break;
}
if (state_changed == B_FALSE) {
/*
* if there is no change in state, check for condition
* changes.
* if there is a state change, handling state change
* will take care of condition changes also.
*/
(void) update_fru_condition(child_frup, &cond_changed);
if (cond_changed == B_FALSE) {
break;
}
if ((rc = post_piclevent(PICLEVENT_CONDITION_CHANGE,
fru_cond[child_frup->cond],
fru_cond[child_frup->prev_cond],
child_frup->frunodeh, WAIT)) != PICL_SUCCESS) {
FRUTREE_DEBUG3(EVENTS, PTREE_POST_PICLEVENT_ERR,
child_frup->name,
PICLEVENT_CONDITION_CHANGE, rc);
}
break;
}
/* add to queue to handle the fru state change */
(void) pthread_mutex_lock(&child_frup->mutex);
/* figure out if this is config/unconfig operation */
if (child_frup->state == FRU_STATE_CONFIGURED) {
fru_dr_arg.action = HANDLE_CONFIGURE;
fru_dr_arg.data = child_frup;
} else if (child_frup->state == FRU_STATE_UNCONFIGURED) {
fru_dr_arg.action = HANDLE_UNCONFIGURE;
fru_dr_arg.data = child_frup;
}
(void) pthread_mutex_unlock(&child_frup->mutex);
(void) pthread_mutex_lock(&ev_mutex);
if ((rc = add_to_queue(fru_dr_arg)) != PICL_SUCCESS) {
(void) pthread_mutex_unlock(&ev_mutex);
break;
}
(void) pthread_cond_signal(&ev_cond);
(void) pthread_mutex_unlock(&ev_mutex);
break;
case HANDLE_INSERT: /* dr_apstate_change (HINT_INSERT) */
locp = (frutree_locnode_t *)dr_arg->data;
if (locp == NULL) {
break;
}
FRUTREE_DEBUG1(EVENTS, "HANDLE INSERT on %s", locp->name);
/* if the location has child fru, get its information */
if (ptree_get_propval_by_name(locp->locnodeh, PICL_PROP_CHILD,
&childh, sizeof (childh)) == PICL_SUCCESS) {
/* get the child fru information */
if (hash_lookup_entry(childh, (void **)&hashptr) ==
PICL_SUCCESS) {
child_frup = FRUDATA_PTR(hashptr);
}
}
if (child_frup) {
/*
* if previous state is not empty, it could be a
* hint insert to retry connects
*/
(void) update_loc_state(locp, &state_changed);
if (state_changed) {
if ((rc = post_piclevent(PICLEVENT_STATE_CHANGE,
loc_state[locp->state],
loc_state[locp->prev_state], locp->locnodeh,
WAIT)) != PICL_SUCCESS) {
FRUTREE_DEBUG3(EVENTS, PTREE_POST_PICLEVENT_ERR,
locp->name, PICLEVENT_STATE_CHANGE, rc);
}
}
(void) update_fru_condition(child_frup, &cond_changed);
if (cond_changed == B_TRUE) {
if ((rc = post_piclevent(PICLEVENT_CONDITION_CHANGE,
fru_cond[child_frup->cond],
fru_cond[child_frup->prev_cond],
child_frup->frunodeh, WAIT)) != PICL_SUCCESS) {
FRUTREE_DEBUG3(EVENTS,
PTREE_POST_PICLEVENT_ERR,
child_frup->name,
PICLEVENT_CONDITION_CHANGE, rc);
}
}
if (!locp->autoconfig_enabled) {
break;
}
if (locp->state != LOC_STATE_CONNECTED) {
if ((rc = connect_fru(locp)) != PICL_SUCCESS) {
FRUTREE_DEBUG2(EVENTS, CONNECT_FAILED_ERR,
locp->name, rc);
}
}
break;
}
(void) update_loc_state(locp, &state_changed);
if ((rc = create_fru_node(locp, &child_frup)) != PICL_SUCCESS) {
FRUTREE_DEBUG3(EVENTS, EVENT_NOT_HANDLED,
PICLEVENT_DR_AP_STATE_CHANGE, locp->name, rc);
break;
}
if ((rc = post_piclevent(PICLEVENT_STATE_CHANGE,
loc_state[locp->state], loc_state[locp->prev_state],
locp->locnodeh, WAIT)) != PICL_SUCCESS) {
FRUTREE_DEBUG3(EVENTS, PTREE_POST_PICLEVENT_ERR,
locp->name, PICLEVENT_STATE_CHANGE, rc);
}
if (locp->autoconfig_enabled) {
if ((rc = connect_fru(locp)) != PICL_SUCCESS) {
FRUTREE_DEBUG2(EVENTS, CONNECT_FAILED_ERR,
locp->name, rc);
}
}
break;
case HANDLE_REMOVE: /* dr_apstate_change (HINT_REMOVE) */
locp = (frutree_locnode_t *)dr_arg->data;
if (locp == NULL) {
break;
}
FRUTREE_DEBUG1(EVENTS, "HANDLE REMOVE on %s", locp->name);
if (locp->state == LOC_STATE_EMPTY) {
break; /* discard the spurious event */
}
(void) update_loc_state(locp, &state_changed);
/* if the location has child fru, get its information */
if (ptree_get_propval_by_name(locp->locnodeh, PICL_PROP_CHILD,
&childh, sizeof (childh)) == PICL_SUCCESS) {
/* get the child fru information */
if (hash_lookup_entry(childh, (void **)&hashptr) ==
PICL_SUCCESS) {
frup = FRUDATA_PTR(hashptr);
}
}
if (frup == NULL) {
break;
}
/*
* frutree need to post this event before handling the
* fru remove, so that other plugins (like frudata) can
* do the cleanup
*/
if ((rc = post_piclevent(PICLEVENT_STATE_CHANGE,
loc_state[locp->state], loc_state[locp->prev_state],
locp->locnodeh, WAIT)) != PICL_SUCCESS) {
FRUTREE_DEBUG3(EVENTS, PTREE_POST_PICLEVENT_ERR,
locp->name, PICLEVENT_STATE_CHANGE, rc);
}
if ((rc = handle_fru_remove(frup)) != PICL_SUCCESS) {
FRUTREE_DEBUG2(EVENTS, "SUNW_frutree:Error in handling"
"removal of fru under %s(error=%d)", locp->name, rc);
}
break;
case POST_COND_EVENT:
frup = (frutree_frunode_t *)dr_arg->data;
if (frup == NULL) {
break;
}
if ((rc = post_piclevent(PICLEVENT_CONDITION_CHANGE,
fru_cond[frup->cond], fru_cond[frup->prev_cond],
frup->frunodeh, WAIT)) != PICL_SUCCESS) {
FRUTREE_DEBUG3(EVENTS, PTREE_POST_PICLEVENT_ERR,
frup->name, PICLEVENT_CONDITION_CHANGE, rc);
}
default:
break;
}
return (PICL_SUCCESS);
}
/*ARGSUSED*/
static void*
dr_thread(void * arg)
{
ev_queue_t *event = NULL;
(void) pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
(void) pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
for (;;) {
if (fini_called)
break;
(void) pthread_mutex_lock(&ev_mutex);
while (queue_head == NULL) {
(void) pthread_cond_wait(&ev_cond, &ev_mutex);
}
event = remove_from_queue();
(void) pthread_mutex_unlock(&ev_mutex);
while (event) {
(void) handle_dr_event(&event->arg);
free(event);
event = NULL;
(void) pthread_mutex_lock(&ev_mutex);
event = remove_from_queue();
(void) pthread_mutex_unlock(&ev_mutex);
}
}
return (NULL);
}
static picl_errno_t
update_port_state(frutree_portnode_t *portp, boolean_t post_ev)
{
int state, cond;
picl_errno_t rc;
uint64_t ap_status_time;
boolean_t state_changed = B_FALSE;
boolean_t cond_changed = B_FALSE;
frutree_port_type_t port_type;
if (portp == NULL) {
return (PICL_INVALIDARG);
}
port_type = frutree_get_port_type(portp);
if (port_type == UNKNOWN_PORT) {
return (PICL_SUCCESS);
}
state = kstat_port_state(port_type, portp->driver,
portp->instance);
cond = kstat_port_cond(port_type, portp->driver,
portp->instance);
switch (state) {
case 0:
/* DOWN */
if (portp->state != PORT_STATE_DOWN) {
portp->state = PORT_STATE_DOWN;
state_changed = B_TRUE;
}
break;
case 1:
/* UP */
if (portp->state != PORT_STATE_UP) {
portp->state = PORT_STATE_UP;
state_changed = B_TRUE;
}
break;
default:
/* UNKNOWN */
if (portp->state != PORT_STATE_UNKNOWN) {
portp->state = PORT_STATE_UNKNOWN;
state_changed = B_TRUE;
}
}
if (post_ev && state_changed) {
ap_status_time = (uint64_t)(time(NULL));
if ((rc = ptree_update_propval_by_name(portp->portnodeh,
PICL_PROP_STATUS_TIME, &ap_status_time,
sizeof (uint64_t))) != PICL_SUCCESS) {
FRUTREE_DEBUG3(EVENTS, PTREE_UPDATE_PROP_ERR,
PICL_PROP_STATUS_TIME, portp->name, rc);
}
if ((rc = post_piclevent(PICLEVENT_STATE_CHANGE,
port_state[portp->state], NULL,
portp->portnodeh, WAIT)) != PICL_SUCCESS) {
FRUTREE_DEBUG3(EVENTS, PTREE_POST_PICLEVENT_ERR,
portp->name, PICLEVENT_STATE_CHANGE, rc);
}
}
switch (cond) {
case 0:
if (portp->cond != PORT_COND_OK) {
portp->cond = PORT_COND_OK;
cond_changed = B_TRUE;
}
break;
case 1:
if (portp->cond != PORT_COND_FAILING) {
portp->cond = PORT_COND_FAILING;
cond_changed = B_TRUE;
}
break;
case 2:
if (portp->cond != PORT_COND_FAILED) {
portp->cond = PORT_COND_FAILED;
cond_changed = B_TRUE;
}
break;
case 3:
if (portp->cond != PORT_COND_TESTING) {
portp->cond = PORT_COND_TESTING;
cond_changed = B_TRUE;
}
break;
default:
if (portp->cond != PORT_COND_UNKNOWN) {
portp->cond = PORT_COND_UNKNOWN;
cond_changed = B_TRUE;
}
}
if (post_ev && cond_changed) {
ap_status_time = (uint64_t)(time(NULL));
if ((rc = ptree_update_propval_by_name(portp->portnodeh,
PICL_PROP_CONDITION_TIME, &ap_status_time,
sizeof (uint64_t))) != PICL_SUCCESS) {
FRUTREE_DEBUG3(EVENTS, PTREE_UPDATE_PROP_ERR,
PICL_PROP_CONDITION_TIME, portp->name, rc);
}
if ((rc = post_piclevent(PICLEVENT_CONDITION_CHANGE,
port_cond[portp->cond], NULL,
portp->portnodeh, WAIT)) != PICL_SUCCESS) {
FRUTREE_DEBUG3(EVENTS, PTREE_POST_PICLEVENT_ERR,
portp->name, PICLEVENT_CONDITION_CHANGE, rc);
}
}
return (PICL_SUCCESS);
}
/*
* monitor port nodes and scsi nodes under a fru
*/
static int
monitor_nodes_under_fru(picl_nodehdl_t nodeh, void *c_args)
{
picl_errno_t rc;
picl_nodehdl_t parenth;
hashdata_t *hashptr = NULL;
boolean_t state_changed;
frutree_portnode_t *portp = NULL;
frutree_locnode_t *locp = NULL;
frutree_frunode_t *frup = NULL;
char class[PICL_PROPNAMELEN_MAX];
char slot_type[PICL_PROPNAMELEN_MAX];
if (c_args == NULL) {
return (PICL_INVALIDARG);
}
frup = (frutree_frunode_t *)c_args;
if (ptree_get_propval_by_name(nodeh, PICL_PROP_PARENT,
&parenth, sizeof (parenth)) != PICL_SUCCESS) {
return (PICL_WALK_CONTINUE);
}
if (parenth != frup->frunodeh)
return (PICL_WALK_CONTINUE);
if ((rc = ptree_get_propval_by_name(nodeh, PICL_PROP_CLASSNAME, class,
sizeof (class))) != PICL_SUCCESS) {
return (PICL_WALK_CONTINUE);
}
if ((rc = hash_lookup_entry(nodeh, (void **)&hashptr)) !=
PICL_SUCCESS) {
return (PICL_WALK_CONTINUE);
}
if (strcmp(class, PICL_CLASS_LOCATION) == 0) {
locp = LOCDATA_PTR(hashptr);
if (ptree_get_propval_by_name(locp->locnodeh,
PICL_PROP_SLOT_TYPE, slot_type,
sizeof (slot_type)) != PICL_SUCCESS) {
return (PICL_WALK_CONTINUE);
}
if (strcmp(slot_type, SANIBEL_SCSI_SLOT) == 0 ||
strcmp(slot_type, SANIBEL_IDE_SLOT) == 0) {
return (PICL_WALK_CONTINUE);
}
(void) update_loc_state(locp, &state_changed);
if (state_changed) {
if ((rc = post_piclevent(PICLEVENT_STATE_CHANGE,
loc_state[locp->state],
loc_state[locp->prev_state],
locp->locnodeh, WAIT)) != PICL_SUCCESS) {
FRUTREE_DEBUG3(EVENTS, PTREE_POST_PICLEVENT_ERR,
locp->name, PICLEVENT_STATE_CHANGE, rc);
}
}
} else if (strcmp(class, PICL_CLASS_PORT) == 0) {
portp = PORTDATA_PTR(hashptr);
(void) update_port_state(portp, B_TRUE);
}
return (PICL_WALK_CONTINUE);
}
/* This routine monitors only port node, scsi nodes */
/* ARGSUSED */
static int
monitor_fru(picl_nodehdl_t nodeh, void *c_args)
{
picl_errno_t rc;
picl_nodehdl_t loch;
hashdata_t *hashptr = NULL;
frutree_frunode_t *frup = NULL;
boolean_t state_changed, cond_changed;
char slot_type[PICL_PROPNAMELEN_MAX];
if (hash_lookup_entry(nodeh, (void **)&hashptr) !=
PICL_SUCCESS) {
return (PICL_WALK_CONTINUE);
}
frup = FRUDATA_PTR(hashptr);
(void) pthread_mutex_lock(&frup->mutex);
if (frup->dr_in_progress) {
(void) pthread_mutex_unlock(&frup->mutex);
return (PICL_WALK_CONTINUE);
}
frup->busy = B_TRUE;
(void) pthread_mutex_unlock(&frup->mutex);
/* get the parent information to determine if it is scsi slot or not */
if (ptree_get_propval_by_name(nodeh, PICL_PROP_PARENT,
&loch, sizeof (loch)) != PICL_SUCCESS) {
return (PICL_WALK_CONTINUE);
}
if (ptree_get_propval_by_name(loch, PICL_PROP_SLOT_TYPE, slot_type,
sizeof (slot_type)) != PICL_SUCCESS) {
return (PICL_WALK_CONTINUE);
}
if (strcmp(slot_type, SANIBEL_SCSI_SLOT) == 0 ||
strcmp(slot_type, SANIBEL_IDE_SLOT) == 0) {
/* scsi fru */
(void) update_fru_state(frup, &state_changed);
(void) update_fru_condition(frup, &cond_changed);
if (state_changed) {
if ((rc = post_piclevent(PICLEVENT_STATE_CHANGE,
fru_state[frup->state],
fru_state[frup->prev_state],
frup->frunodeh, WAIT)) != PICL_SUCCESS) {
FRUTREE_DEBUG3(EVENTS, PTREE_POST_PICLEVENT_ERR,
frup->name, PICLEVENT_STATE_CHANGE, rc);
}
}
if (cond_changed) {
if ((rc = post_piclevent(PICLEVENT_CONDITION_CHANGE,
fru_cond[frup->cond], fru_cond[frup->prev_cond],
frup->frunodeh, WAIT)) != PICL_SUCCESS) {
FRUTREE_DEBUG3(EVENTS, PTREE_POST_PICLEVENT_ERR,
frup->name, PICLEVENT_CONDITION_CHANGE,
rc);
}
}
(void) pthread_mutex_lock(&frup->mutex);
frup->busy = B_FALSE;
(void) pthread_cond_signal(&frup->busy_cond_cv);
(void) pthread_mutex_unlock(&frup->mutex);
return (PICL_WALK_CONTINUE);
}
if (frup->state != FRU_STATE_CONFIGURED) {
(void) pthread_mutex_lock(&frup->mutex);
frup->busy = B_FALSE;
(void) pthread_cond_signal(&frup->busy_cond_cv);
(void) pthread_mutex_unlock(&frup->mutex);
return (PICL_WALK_CONTINUE);
}
(void) ptree_walk_tree_by_class(chassish,
NULL, (void *)frup, monitor_nodes_under_fru);
(void) pthread_mutex_lock(&frup->mutex);
frup->busy = B_FALSE;
(void) pthread_cond_signal(&frup->busy_cond_cv);
(void) pthread_mutex_unlock(&frup->mutex);
return (PICL_WALK_CONTINUE);
}
/* ARGSUSED */
static void *
monitor_node_status(void *arg)
{
int err;
timestruc_t to;
struct timeval tp;
(void) pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
(void) pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
FRUTREE_DEBUG0(EVENTS, "Monitoring for port status started");
do
{
(void) pthread_mutex_lock(&monitor_mutex);
(void) gettimeofday(&tp, NULL);
to.tv_sec = tp.tv_sec + frutree_poll_timeout;
to.tv_nsec = tp.tv_usec * 1000;
err = pthread_cond_timedwait(&monitor_cv, &monitor_mutex, &to);
(void) pthread_mutex_unlock(&monitor_mutex);
if (err == ETIMEDOUT) { /* woke up from sleep */
(void) ptree_walk_tree_by_class(chassish,
PICL_CLASS_FRU, (void *)NULL, monitor_fru);
}
} while (fini_called == 0);
return (NULL);
}
picl_errno_t
create_children(frutree_frunode_t *frup, char *scsi_loc, char *bus_addr,
int slot_no, char *slot_type, boolean_t is_cfgadm_ap)
{
int i = 0;
picl_errno_t rc;
picl_nodehdl_t nodeh;
uint8_t geo_addr = 0;
hashdata_t *datap = NULL;
frutree_locnode_t *locp = NULL;
hashdata_t *hashptr = NULL;
char fru_type[PICL_PROPNAMELEN_MAX];
frutree_frunode_t *child_frup = NULL;
frutree_callback_data_t fru_arg;
if (frup == NULL || scsi_loc == NULL || slot_type == NULL) {
return (PICL_FAILURE);
}
/* check if the location is already created */
(void) strncpy(fru_arg.node_name, scsi_loc,
sizeof (fru_arg.node_name));
fru_arg.retnodeh = 0;
if ((rc = ptree_walk_tree_by_class(chassish, PICL_CLASS_LOCATION,
&fru_arg, frutree_get_nodehdl)) == PICL_SUCCESS) {
if (fru_arg.retnodeh != 0) { /* node is already present */
return (PICL_SUCCESS);
}
}
/* create the location node and all its properties */
if ((rc = ptree_create_node(scsi_loc, PICL_CLASS_LOCATION,
&nodeh)) != PICL_SUCCESS) {
return (rc);
}
if ((rc = create_property(PICL_PTYPE_CHARSTRING, PICL_READ,
PICL_PROPNAMELEN_MAX, PICL_PROP_SLOT_TYPE, NULLREAD,
NULLWRITE, nodeh, NULL, slot_type)) !=
PICL_SUCCESS) {
FRUTREE_DEBUG3(FRUTREE_INIT, PTREE_CREATE_PROP_FAILED,
PICL_PROP_SLOT_TYPE, scsi_loc, rc);
}
if ((rc = create_property(PICL_PTYPE_CHARSTRING, PICL_READ,
PICL_PROPNAMELEN_MAX, PICL_PROP_LABEL, NULLREAD,
NULLWRITE, nodeh, NULL, bus_addr)) != PICL_SUCCESS) {
FRUTREE_DEBUG3(FRUTREE_INIT, PTREE_CREATE_PROP_FAILED,
PICL_PROP_LABEL, scsi_loc, rc);
}
if ((rc = create_property(PICL_PTYPE_CHARSTRING, PICL_READ,
PICL_PROPNAMELEN_MAX, PICL_PROP_BUS_ADDR, NULLREAD,
NULLWRITE, nodeh, NULL, bus_addr)) != PICL_SUCCESS) {
FRUTREE_DEBUG3(FRUTREE_INIT, PTREE_CREATE_PROP_FAILED,
PICL_PROP_BUS_ADDR, scsi_loc, rc);
}
geo_addr = slot_no;
if ((rc = create_property(PICL_PTYPE_UNSIGNED_INT, PICL_READ,
sizeof (uint8_t), PICL_PROP_GEO_ADDR, NULLREAD,
NULLWRITE, nodeh, (picl_prophdl_t *)NULL,
&geo_addr)) != PICL_SUCCESS) {
FRUTREE_DEBUG3(FRUTREE_INIT, PTREE_CREATE_PROP_FAILED,
PICL_PROP_GEO_ADDR, scsi_loc, rc);
}
if ((rc = create_property(PICL_PTYPE_CHARSTRING, PICL_READ,
PICL_PROPNAMELEN_MAX, PICL_PROP_DEVFS_PATH, NULLREAD,
NULLWRITE, nodeh, NULL, frup->fru_path)) !=
PICL_SUCCESS) {
FRUTREE_DEBUG3(FRUTREE_INIT, PTREE_CREATE_PROP_FAILED,
PICL_PROP_DEVFS_PATH, scsi_loc, rc);
}
if ((rc = ptree_add_node(frup->frunodeh, nodeh)) != PICL_SUCCESS) {
(void) ptree_destroy_node(nodeh);
return (rc);
}
/* save the node in hashtable */
if ((rc = make_loc_data(scsi_loc, &datap)) != PICL_SUCCESS) {
return (rc);
}
locp = LOCDATA_PTR(datap);
locp->locnodeh = nodeh;
/* save data in hash table */
(void) hash_add_entry(nodeh, (void *)datap);
if ((rc = hash_lookup_entry(nodeh, (void **)&hashptr)) !=
PICL_SUCCESS) {
return (rc);
}
locp = LOCDATA_PTR(hashptr);
if (is_cfgadm_ap != B_TRUE) { /* device found in libdevinfo */
locp->state_mgr = STATIC_LOC;
locp->state = LOC_STATE_CONNECTED;
}
if ((rc = location_init(locp)) != PICL_SUCCESS) {
return (rc);
}
/* if location is empty, done */
if (locp->state == LOC_STATE_EMPTY) {
if ((rc = post_piclevent(PICLEVENT_STATE_CHANGE,
PICLEVENTARGVAL_EMPTY, NULL,
locp->locnodeh, WAIT)) != PICL_SUCCESS) {
FRUTREE_DEBUG3(EVENTS, PTREE_POST_PICLEVENT_ERR,
locp->name, PICLEVENT_STATE_CHANGE, rc);
}
return (PICL_SUCCESS);
}
/* create the fru node and initilize it */
if ((rc = create_fru_node(locp, &child_frup)) != PICL_SUCCESS) {
return (rc);
}
/* post picl event on location (frudata is consumer for these events) */
if ((rc = post_piclevent(PICLEVENT_STATE_CHANGE,
loc_state[locp->state], PICLEVENTARGVAL_EMPTY,
locp->locnodeh, WAIT)) != PICL_SUCCESS) {
FRUTREE_DEBUG3(EVENTS, PTREE_POST_PICLEVENT_ERR,
locp->name, PICLEVENT_STATE_CHANGE, rc);
}
if (child_frup->state_mgr == STATIC_LOC) {
/* derive the fru_type from name */
while (i < strlen(scsi_loc)) {
if (isdigit(scsi_loc[i])) {
(void) strncpy(fru_type, scsi_loc, i);
fru_type[i] = '\0';
break;
}
++i;
}
if ((rc = ptree_update_propval_by_name(child_frup->frunodeh,
PICL_PROP_FRU_TYPE, fru_type, sizeof (fru_type))) !=
PICL_SUCCESS) {
FRUTREE_DEBUG3(EVENTS, PTREE_UPDATE_PROP_ERR,
PICL_PROP_FRU_TYPE, child_frup->name, rc);
}
}
/* post picl state change event on fru state */
if ((rc = post_piclevent(PICLEVENT_STATE_CHANGE,
fru_state[child_frup->state], PICLEVENTARGVAL_UNKNOWN,
child_frup->frunodeh, WAIT)) != PICL_SUCCESS) {
FRUTREE_DEBUG3(EVENTS, PTREE_POST_PICLEVENT_ERR,
frup->name, PICLEVENT_STATE_CHANGE, rc);
}
/* for scsi FRUs we need not probe further */
return (PICL_SUCCESS);
}
/*
* recursive search in the subtree
*/
/*ARGSUSED*/
boolean_t
is_location_present_in_subtree(frutree_frunode_t *frup, const char *name,
const char *path)
{
frutree_callback_data_t fru_arg;
(void) strncpy(fru_arg.node_name, name,
sizeof (fru_arg.node_name));
fru_arg.retnodeh = 0;
if (ptree_walk_tree_by_class(frup->frunodeh, PICL_CLASS_LOCATION,
&fru_arg, frutree_get_nodehdl) == PICL_SUCCESS) {
if (fru_arg.retnodeh != 0) { /* node is already present */
return (B_TRUE);
}
}
return (B_FALSE);
}