/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Serengeti Environmental Information driver (sgenv)
*
* This driver requests the environmental properties from the SC. These
* request-response transactions are transferred through the SBBC mailbox,
* between the Domain and the SC.
*
* All sensors have the same sort of properties: Low and high limits, warning
* thresholds, last measured value, time of measurement, units (e.g., degrees
* Celsius, volts, etc.), and so on.
*
* Each sensor is named by a unique Tag. The Tag identifies the geographical
* location of the sensor in the Serengeti, and what it is the sensor measures.
*
* Requestable sensor properties are broken into two types: Those which are
* quasi-constant (infrequently change) - e.g., tolerance-defining low and high
* limits; and those which are volatile (typically change) - e.g., the current
* measurement.
*
* Unfortunately, property sets are too large to comprise a single mailbox
* message, so the sets are further subdivided into notionally arbitrary
* collections. NOTE: The SC-mailbox framework now supports fragmented messages
* which could allow us to request the data in larger chunks in the future.
*
* Each collection is fetched by a separate transaction.
*
* Firstly there is a transaction to obtain a list of all collections. Each non-
* zero key in this list is associated whith one of the collections of sensors.
* (This sparse list of keys is then used as an index to obtain all the sensor
* data for each collection).
*
* For each collection, there is one request-reply transaction to obtain a list
* of all sensors in that collection and the limits that apply to each; and a
* separate request-reply transaction to obtain the measurements from the
* sensors in the collection.
*
* The sgenv driver assembles each property set from the constituent
* collections, and caches the assembled property sets into the appropriate
* cache (env_cache, board_cache). The caches are created at startup and are
* updated on receipt of events from the SC. These events (which include DR
* events and ENV events) notify sgenv of configuration changes and
* environmental state changes (such as a sensor state change, Fan speed
* change).
*
* The SC-APP maintains a pseudo-sensor in each collection "measuring" changes
* to the quasi-constants in that collection. By monitoring these pseudo-sensor
* measurements, the kstat driver avoids redundant or speculative re-fetches of
* the quasi-constant properties.
*/
#include <sys/sgevents.h>
#include <sys/sysevent.h>
#include <sys/serengeti.h>
#include <sys/sgfrutypes.h>
#include <sys/sgsbbc_iosram.h>
#include <sys/sgsbbc_mailbox.h>
#include <sys/sbdp_priv.h>
#include <sys/sgenv_impl.h>
/*
* Global Variables - can be patched from Solaris
* ==============================================
*/
/*
* the maximum amount of time this driver is prepared to wait for the mailbox
* to reply before it decides to timeout. The value is initially set in the
* _init() routine to the global Serengeti variable <sbbc_mbox_default_timeout>
* but could be tuned specifically for SGENV after booting up the system.
*/
int sgenv_max_mbox_wait_time = 0;
#ifdef DEBUG
/*
* This variable controls the level of debug output
*/
#endif
/*
* Module Variables
* ================
*/
/*
* Driver entry points
*/
nodev, /* open() */
nodev, /* close() */
nodev, /* strategy() */
nodev, /* print() */
nodev, /* dump() */
nodev, /* read() */
nodev, /* write() */
nodev, /* ioctl() */
nodev, /* devmap() */
nodev, /* mmap() */
ddi_segmap, /* segmap() */
nochpoll, /* poll() */
ddi_prop_op, /* prop_op() */
NULL, /* cb_str */
};
0, /* ref count */
ddi_getinfo_1to1, /* getinfo() */
nulldev, /* identify() */
nulldev, /* probe() */
sgenv_attach, /* attach() */
sgenv_detach, /* detach */
nodev, /* reset */
&sgenv_cb_ops, /* pointer to cb_ops structure */
nulldev, /* power() */
ddi_quiesce_not_needed, /* quiesce() */
};
/*
* Loadable module support.
*/
extern struct mod_ops mod_driverops;
&mod_driverops, /* Type of module. This is a driver */
"Environmental Driver", /* Name of the module */
&sgenv_ops /* pointer to the dev_ops structure */
};
&modldrv,
};
/* Opaque state structure pointer */
static void *sgenv_statep;
/*
* <env_cache> is a cache of all the sensor readings which is persistent
* between kstat reads. It is created at init and gets updated upon receipt
* of events from the SC.
*
* The kstat_update function takes a copy of the non-zero entries in this
* cache and creates a temp buffer called env_cache_snapshot. The
* kstat_snapshot function then bcopies the env_cache_snapshot into the
* kstat buffer. This is done because there is no way to ensure that the
* env_cache won't change between the kstat_update and the kstat_snapshot
* which will cause problems as the update sets the ks_data_size.
*/
/*
* This is set to TRUE the first time env data is stored in the cache
* so that at least from then on, old data can be returned if a call to
* the mailbox fails.
*/
/*
* This lock is needed by the variable-sized kstat which returns
* environmental info. It prevents data-size races with kstat clients.
*/
/*
* The <env_cache> can be accessed asynchronously by the polling function
* and the kstat_read framework. This mutex ensures that access to the data
* is controlled correctly.
*/
/*
* We need to store the last time we asked the SC for environmental information
* so that we do not send too many requests in a short period of time.
*/
/*
* Variables to coordinate between the handlers which are triggered when
* the env cache needs to be updated and the thread which does the work.
*/
static volatile int env_thread_run = 0;
/*
* <board_cache> is a cache of all the board status info and it is persistent
* between kstat reads.
*
* The kstat_update function takes a copy of the non-zero entries in this
* cache and copies them into the board_cache_snapshot buffer. The
* kstat_snapshot function then bcopies the board_cache_snapshot into the
* kstat buffer. This is done because there is no way to ensure that the
* board_cache won't change between the kstat_update and the kstat_snapshot
* which will cause problems as the update sets the ks_data_size.
*/
/*
* This mutex ensures the <board_cache> is not destroyed while the board data
* is being collected.
*/
/*
* This lock is needed by the variable-sized kstat which returns
* board status info. It prevents data-size races with kstat clients.
*/
/*
* This is a count of the number of board readings were stored by
* the kstat_update routine - this is needed by the kstat_snapshot routine.
*/
static int board_count = 0;
static int board_count_snapshot = 0;
/*
* We need to store the last time we asked the SC for board information
* so that we do not send too many requests in a short period of time.
*/
/*
* Variables to coordinate between the handlers which are triggered when
* the board cache needs to be updated and the thread which does the work.
*/
static volatile int board_thread_run = 0;
/*
* Used to keep track of the number of sensors associated with each key.
* The sum of all the values in this array is used to set ks_data_size.
*/
/*
* This variable keeps a count of the number of errors that have occurred
* when we make calls to the mailbox for Env or Board data.
*/
static int sgenv_mbox_error_count = 0;
/*
* mutex which protects the keyswitch interrupt handler.
*/
/*
* mutex which protects the env interrupt handler.
*/
/*
* mutex which protects the DR handler interrupt handler.
*/
/*
* Payloads of the event handlers.
*/
/*
* The following 3 arrays list all possible HPUs, Parts and Device types
*/
/*
* ensure that all possible HPUs exported, as described in the main comment
* in <sys/sensor_tag.h>, are accounted for here.
*/
0, (char *)NULL
};
0, (char *)NULL
};
0, (char *)NULL
};
int
_init(void)
{
int error = 0;
sizeof (sgenv_soft_state_t), 1);
if (error)
return (error);
if (error) {
return (error);
}
/* set the default timeout value */
return (error);
}
int
{
}
int
_fini(void)
{
int error = 0;
if (error)
return (error);
return (error);
}
static int
{
int instance;
int err;
switch (cmd) {
case DDI_ATTACH:
/* allocate a global sgenv_soft_state structure */
if (err != DDI_SUCCESS) {
"structure for inst %d.", instance);
return (DDI_FAILURE);
}
"structure for inst %d.", instance);
return (DDI_FAILURE);
}
if (err != 0) {
/*
* Some of the kstats may have been created before the
* error occurred in sgenv_add_kstats(), so we call
* sgenv_remove_kstats() which removes any kstats
* already created.
*/
return (DDI_FAILURE);
}
/*
* Before we setup the framework to read the data from the SC
* we need to ensure the caches are initialized correctly.
*/
/*
* Add the threads which will update the env and board caches
* and post events to Sysevent Framework in the background
* indicate to the threads that they need to do so.
*/
if (err != DDI_SUCCESS) {
return (DDI_FAILURE);
}
if (err != DDI_SUCCESS) {
(void) sgenv_remove_cache_update_threads();
return (DDI_FAILURE);
}
/*
* Add the handlers which watch for unsolicited messages
* and post event to Sysevent Framework.
*/
if (err != DDI_SUCCESS) {
(void) sgenv_remove_intr_handlers();
(void) sgenv_remove_cache_update_threads();
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
case DDI_RESUME:
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
}
static int
{
int instance;
int err;
switch (cmd) {
case DDI_DETACH:
"structure for inst %d.", instance);
return (DDI_FAILURE);
}
if (err != DDI_SUCCESS) {
}
/*
* Remove the handlers which watch for unsolicited messages
* and post event to Sysevent Framework.
*/
if (err != DDI_SUCCESS) {
}
return (DDI_SUCCESS);
case DDI_SUSPEND:
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
}
static int
{
/*
* Create the 'keyswitch position' named kstat.
*/
/* initialize the named kstat */
/* update the soft state */
} else {
return (-1);
}
/*
* Environmental Information.
*/
"misc", KSTAT_TYPE_RAW, 0,
ksp->ks_data_size = 0;
ksp->ks_snaptime = 0;
/* update the soft state */
} else {
return (-1);
}
/*
* Board Status Information.
*/
"misc", KSTAT_TYPE_RAW, 0,
ksp->ks_data_size = 0;
ksp->ks_snaptime = 0;
/* update the soft state */
} else {
return (-1);
}
return (0);
}
static void
{
}
}
}
}
/*
* This function registers mailbox interrupt handlers to watch for certain
* unsolicited mailbox messages, which indicate that some event has occurred.
*
* Currently only the following events are handled:
* MBOX_EVENT_KEY_SWITCH
* MBOX_EVENT_ENV
* - Thresholds/Limits Exceeded
* - Fan Status changed
*
* ERRORS:
* We return DDI_FAILURE if we fail to register any one of the
* interrupt handlers.
*/
static int
sgenv_add_intr_handlers(void)
{
int err;
/*
* Register an interrupt handler with the sgsbbc driver for the
* MBOX_EVENT_KEY_SWITCH events.
* - The virtual keyswitch has changed, we generate a sysevent.
*/
if (err != 0) {
"handler. Err=%d", err);
return (DDI_FAILURE);
}
/*
* Register an interrupt handler with the sgsbbc driver for the
* MBOX_EVENT_ENV events.
* - Thresholds/Limits Exceeded, we generate a sysevent
* and we update our caches.
*/
if (err != 0) {
"(env) handler. Err=%d", err);
return (DDI_FAILURE);
}
/*
* Register an interrupt handler with the sgsbbc driver for the
* MBOX_EVENT_ENV events.
* - Fan Status changed, we generate a sysevent, and
* we update the env cache only.
*/
if (err != 0) {
"handler. Err=%d", err);
return (DDI_FAILURE);
}
/*
* Register an interrupt handler with the sgsbbc driver for the
* MBOX_EVENT_GENERIC events.
* - DR state change, we update our caches.
*/
if (err != 0) {
"handler. Err=%d", err);
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* This function unregisters the mailbox interrupt handlers.
*
* ERRORS:
* We return DDI_FAILURE if we fail to register any one of the
* interrupt handlers.
*/
static int
{
int err;
if (err != 0) {
"handler. Err=%d", err);
rv = DDI_FAILURE;
}
if (err != 0) {
"handler. Err=%d", err);
rv = DDI_FAILURE;
}
if (err != 0) {
"handler. Err=%d", err);
rv = DDI_FAILURE;
}
if (err != 0) {
"handler. Err=%d", err);
rv = DDI_FAILURE;
}
return (rv);
}
static int
{
DCMN_ERR_S(f, "sgenv_create_cache_update_threads()");
/* Create thread to ensure env_cache is updated */
env_thread_run = 1;
/* Create thread to ensure board_cache is updated */
board_thread_run = 1;
return (DDI_SUCCESS);
}
static int
{
DCMN_ERR_S(f, "sgenv_remove_cache_update_threads()");
/* Cause the env_cache thread to terminate. */
env_thread_run = 0;
/* Cause the board_cache thread to terminate. */
board_thread_run = 0;
return (DDI_SUCCESS);
}
static int
{
switch (rw) {
case KSTAT_WRITE:
/*
* Write not permitted
*/
return (EACCES);
case KSTAT_READ:
/*
* Get the size of the keyswitch IO-SRAM chunk.
* This should be one byte.
*
* If the size is not 1 byte we set the position to UNKNOWN
*
* Otherwise we read the keyswitch position from IO-SRAM.
* Then check that this is a valid keyswitch position.
* If it is not valid then something is corrupt and set
* the position to UNKNOWN.
*/
if (size != 1) {
rv = -1;
} else {
/* Check posn is not corrupt */
switch (posn) {
case SG_KEYSWITCH_POSN_ON:
case SG_KEYSWITCH_POSN_DIAG:
case SG_KEYSWITCH_POSN_SECURE:
/* value read from kstat is OK */
break;
default:
/* value read from kstat is corrupt */
break;
}
}
/* Write position to kstat. */
return (rv);
default:
return (EINVAL);
}
}
static void
sgenv_init_env_cache(void)
{
ASSERT(env_thread_run == 0);
}
/*
* This thread runs in the background and waits for an interrupt handler
* need to update our Env Cache.
*/
static void
sgenv_update_env_cache(void)
{
DCMN_ERR_S(f, "sgenv_update_env_cache()");
while (env_thread_run == 1) {
/*
* We check to see if the update needed flag is set.
* If it is then this means that:
* 1) This is the first time through the while loop
* and we need to initialize the cache.
* 2) An interrupt handler was triggered while we
* we were updating the env cache during the previous
* iteration of the while loop and we need to refresh
* the env data to ensure we are completely up to date.
*
* Otherwise we wait until we get a signal from one of the
* interrupt handlers.
*/
if (env_cache_update_needed) {
} else {
/* Check if we are being asked to terminate */
if (env_thread_run == 0) {
break;
}
}
(void) sgenv_get_env_info_data();
(void) sgenv_check_sensor_thresholds();
if (env_cache_update_needed == B_FALSE)
}
env_thread_run = -1;
thread_exit();
}
/*
* We always return what is in the env_cache. It is up to the SC to ensure
* that the env_cache is current by sending events to us when something
* changes. The cache will then be updated by going to the SC to get the
* new data. That way the kstat_update code can always be sure that it gets
* current data without having to wait while the SC responds (slowly) to our
* request for data.
*
* The way the update and snapshot code works, we cannot be guaranteed that
* someone won't grab the env_cache_lock between the update and snapshot
* calls so we use a temporary snapshot of the env_cache. We cannot hold
* any locks across the calls from the update to the snapshot as we are
* not guaranteed that the snapshot function will be called. So we create
* the snapshot of the env_cache in the update routine and dump this to the
* kstat user buffer in the snapshot routine. (There are error conditions in
* which the snapshot will not be called by the kstat framework so we need
* to handle these appropriately.)
*/
static int
{
DCMN_ERR_S(f, "sgenv_env_info_kstat_update()");
int err = 0;
int key_posn;
switch (rw) {
case KSTAT_WRITE:
/*
* Write not permitted
*/
return (EACCES);
case KSTAT_READ:
/*
* We now need to ensure that there is enough room allocated
* by the kstat framework to return the data via ks_data.
* It is possible there may be no data in the cache but
* we still return zero sized kstats to ensure no client breaks
*/
/*
* If the snapshot still has data (this could be because the
* kstat framework discovered an error and did not call the
* snapshot code which should have freed this buffer) we free
* it here.
*/
if ((env_cache_snapshot != NULL) &&
(env_cache_snapshot_size > 0)) {
"env_cache_snapshot buf", f);
}
/*
* Create a new snapshot buffer based on ks_data_size
*/
/*
* We need to take a fresh snapshot of the env_cache here.
* For each sensor collection, we check to see if there is
* data in the cache (ie. != NULL). If there is, we copy it
* into the snapshot.
*/
if (vol_sensor_count[key_posn] <= 0)
continue;
/*
* <env_cache> entry should have been allocated
* in the kstat_update function already.
*
* If this <env_cache> entry is NULL, then
* it has already been destroyed or cleared
* and the sensor readings have disappeared.
*/
"disappeared", key_posn);
vol_sensor_count[key_posn] = 0;
continue;
}
sizeof (env_sensor_t) *
}
return (err);
default:
return (EINVAL);
}
}
static int
{
DCMN_ERR_S(f, "sgenv_env_info_kstat_snapshot()");
switch (rw) {
case KSTAT_WRITE:
/*
* Write not permitted
*/
return (EACCES);
case KSTAT_READ:
/*
* We have taken a snapshot of the env_cache in the
* update routine so we simply bcopy this into the
* kstat buf. No locks needed here.
*/
if (env_cache_snapshot_size > 0)
/*
* Free the memory used by the snapshot. If for some reason
* the kstat framework does not call this snapshot routine,
* we also have a check in the update routine so the next
* time it is called it checks for this condition and frees
* the snapshot buffer there.
*/
return (0);
default:
return (EINVAL);
}
}
static void
sgenv_init_board_cache(void)
{
int i;
ASSERT(board_thread_run == 0);
/*
* Init all node-ids to be -1.
*/
for (i = 0; i < SG_MAX_BDS; i++)
}
/*
* This thread runs in the background and waits for an interrupt handler
* need to update our Board Cache.
*/
static void
sgenv_update_board_cache(void)
{
DCMN_ERR_S(f, "sgenv_update_board_cache()");
while (board_thread_run == 1) {
/*
* We check to see if the update needed flag is set.
* If it is then this means that:
* 1) This is the first time through the while loop
* and we need to initialize the cache.
* 2) An interrupt handler was triggered while we
* we were updating the cache during the previous
* iteration of the while loop and we need to refresh
* the env data to ensure we are completely up to date.
*
* Otherwise we wait until we get a signal from one of the
* interrupt handlers.
*/
if (board_cache_update_needed) {
} else {
/* Check if we are being asked to terminate */
if (board_thread_run == 0) {
break;
}
}
(void) sgenv_get_board_info_data();
if (board_cache_update_needed == B_FALSE)
}
board_thread_run = -1;
thread_exit();
}
/*
* We always return what is in the board_cache. It is up to the SC to ensure
* that the board_cache is current by sending events to us when something
* changes. The cache will then be updated by going to the SC to get the
* new data. That way the kstat_update code can always be sure that it gets
* current data without having to wait while the SC responds (slowly) to our
* request for data.
*
* The way the update and snapshot code works, we cannot be guaranteed that
* someone won't grab the board_cache_lock between the update and snapshot
* calls so we use a snapshot buffer of the board_cache. We cannot hold
* any locks across the calls from the update to the snapshot as we are
* not guaranteed that the snapshot function will be called. So we create
* the snapshot of the board_cache in the update routine and dump this to the
* kstat user buffer in the snapshot routine. (There are error conditions in
* which the snapshot will not be called by the kstat framework so we need
* to handle these appropriately.)
*/
static int
{
int i;
switch (rw) {
case KSTAT_WRITE:
/*
* Write not permitted
*/
return (EACCES);
case KSTAT_READ:
/*
* The board_cache is created during startup, and so should be
* available before a user can log in and trigger a kstat read,
* but we check just in case.
*/
if (board_cache_updated == FALSE)
return (ENXIO);
/*
* Set <ks_data_size> to the new number of board readings so
* that the snapshot routine can allocate the correctly sized
* kstat.
*/
/*
* We are now guaranteed that that board_cache is not in flux
* (as we have the lock) so we take a copy of the board_cache
* into the board_cache_snapshot so that the snapshot routine
* can copy it from the board_cache_snapshot into the user kstat
* buffer.
*/
for (i = 0; i < SG_MAX_BDS; i++) {
board_cache_snapshot[i] = board_cache[i];
}
return (0);
default:
return (EINVAL);
}
}
static int
{
DCMN_ERR_S(f, "sgenv_board_info_kstat_snapshot()");
int i, num_bds = 0;
switch (rw) {
case KSTAT_WRITE:
/*
* Write not permitted
*/
return (EACCES);
case KSTAT_READ:
if (board_cache_updated == FALSE) {
ksp->ks_data_size = 0;
return (ENOMEM);
}
/*
* Update the snap_time with the last time we got fresh data
* from the SC.
*/
/*
* For each entry in the board_cache_snapshot we check to see
* if the node_id is != NULL before we copy it into
* the kstat buf.
*/
for (i = 0; i < SG_MAX_BDS; i++) {
bdp = &board_cache_snapshot[i];
"cache_snapshot entry[%d], node=%d",
/*
* Need a check to ensure that the buf
* is still within the allocated size.
* We check how many boards are already
* in the user buf before adding one.
*/
num_bds++;
if (num_bds > board_count_snapshot) {
ksp->ks_data_size = 0;
" %d >= %d.",
f, num_bds, board_count_snapshot);
return (EIO);
}
" cache_snapshot entry[%d], node=%d,"
}
}
return (0);
default:
return (EINVAL);
}
}
/*
* This function coordinates reading the env data from the SC.
*
* ERROR:
* If an error occurs while making a call to the mailbox and we have data
* in the cache from a previous call to the SC, we return an error of 0.
* That way the kstat framework will return the old data instead of
* returning an error and an empty kstat.
*/
static int
sgenv_get_env_info_data(void)
{
DCMN_ERR_S(f, "sgenv_get_env_info_data()");
int i;
if (err != 0) {
/*
* If we get an error getting the key values, then we return
* as we cannot proceed any farther. If there is old env data
* in the cache, then we return zero so that the kstat
* framework will export the old data.
*/
if (env_cache_updated == FALSE) {
return (err);
} else {
return (0);
}
}
for (i = 0; i < SGENV_MAX_HPU_KEYS; i++) {
if (vol_sensor_count[i] == 0) {
/* empty collection */
old_key = 0;
} else {
/*
* populated collection:
* (assert size is OK, and 1st sensor is pseudo-sensor)
*/
== SG_INFO_VALUE_OK);
}
/*
* No data is associated with this key position and there was
* no data on the previous read either so we simply continue
* to the next key position.
*/
continue;
}
/*
* We need to grab this lock every time we are going to
* update a HPU. However, a kstat_read can grab
* the env_cache_lock when it wants to get a snapshot of
* the env_cache. This has the affect of stopping the
* active env_cache writer after they have updated the
* active HPU, allowing the kstat_read to get a dump of
* the env_cache, then the env_cache writer can resume
* updating the cache. For performance it is more important
* that the kstat_read completes quickly so we allow the
* kstat_read to interrupt the updating of the env_cache.
* The updating can take anything from a few seconds to
* several minutes to complete.
*/
/*
* If the key just read is zero, then the
* group of sensors have been removed by
* some means and we need to zero out
* the env_cache. (this ensures that data
* belonging to a removed board is not
* returned)
*/
if (key == 0) {
(void) sgenv_clear_env_cache_entry(i);
continue;
}
/*
* Check to see if this key has changed since
* the last read.
*
* If it has changed, we need to update everything.
*
* If it hasn't we simply read the volatiles
* and check to see if the constants have changed.
*/
/*
* If the key is non-zero, then a new HPU has
* been added to the system or it has changed
* somehow and we need to re-read everything.
* (we also need to zero out the env_cache as
* there may be less sensors returned now and
* the old ones may not be overwritten)
*/
/*
* If the <env_cache> has not already been
* allocated for this key position then we
* go ahead and allocate it.
*/
if (err == DDI_FAILURE) {
continue;
}
}
if (err) {
i, old_key, "Constant Data");
if (err != DDI_FAILURE) {
continue;
} else if (env_cache_updated == TRUE) {
return (0);
} else {
return (DDI_FAILURE);
}
}
if (err) {
i, old_key, "Threshold Data");
if (err != DDI_FAILURE) {
continue;
} else if (env_cache_updated == TRUE) {
return (0);
} else {
return (DDI_FAILURE);
}
}
if (err) {
i, old_key, "Volatile Data (fresh)");
if (err != DDI_FAILURE) {
continue;
} else if (env_cache_updated == TRUE) {
return (0);
} else {
return (DDI_FAILURE);
}
}
/*
* As we have successfully got env data for a HPU,
* we ensure <env_cache_updated> is set to TRUE so that
* in the future, if an error occurs during the mailbox
* transfer, we know that there is old data for at
* least one HPU in the <env_cache> which could be
* returned instead of returning an error to the kstat
* framework indicating that we have no data to return.
*/
} else {
/*
* key == old_key
*
* Handle the case when the value of the old key and
* the new key are identical.
*/
/*
* If the keys are identical, then the quasi-constants
* should not have changed (and so don't need updating).
* Similarly for the threshold readings.
*/
/* Update the volatile data */
if (err) {
i, old_key, "Volatile Data (update)");
if (err == DDI_FAILURE) {
return (0);
} else {
continue;
}
}
}
}
return (0);
}
static int
{
/*
* This array keeps track of the valid nodes in a system. A call is
* made to OBP to get the "nodeid" property from all the ssm nodes,
* and for each nodeid found, that position in the array is set to
* TRUE. For a Serengeti only one position in the array will be TRUE.
*/
static fn_t f = "sgenv_get_board_info_data()";
if (first_time) {
first_time = FALSE;
}
continue;
/*
* If we have discovered in a previous call to the SC
* that there is no board in this slot on this type of
* chassis then we don't waste resources asking the SC
* for nonexistent data.
*/
continue;
/*
* We want to avoid the case where an invalid time
* is specified by a user (by patching the
* global variable <sgenv_max_mbox_wait_time>).
*
* Any incorrect values are reset to the default time.
*/
if (sgenv_max_mbox_wait_time <=
max(sbbc_mbox_min_timeout, 0))
/*
* errors from Solaris sgsbbc driver
*/
if (status > SG_MBOX_STATUS_SUCCESS) {
return (rv);
}
/*
* errors from SCAPP
*/
if (status == SG_MBOX_STATUS_ILLEGAL_NODE) {
node_present[node] =
/*
* No point looping through the rest of
* the boards associated with this node.
*/
break;
} else if (status ==
/*
* We clear the bit representing <board>
* in <node> to indicate that this slot
* cannot exist on this chassis.
*/
continue;
} else if (status ==
/*
* We cannot access data for this slot,
* however we may be able to do so in
* the future. We do nothing.
*/
} else {
"Board data for "
if (rv == 0)
continue;
}
}
/*
* Check if the SC returns data for this board.
*/
/*
* If this cache entry used to have data and
* now doesn't we decrement the board_count
* clear the env_cache. The board must have
* been removed.
*/
board_count--;
/*
* clear board_cache entry by
* setting node_id to -1;
*/
"Clearing cache line %d [%p]",
}
} else {
/*
* If this cache entry was previously empty
* and we now have data for it we increment
* the board_count. A new board must have
* been added.
*/
board_count++;
/*
* update the board_cache entry
*/
"Writing data for bd=%d into "
" the board_cache at [%p]",
}
} /* board */
} /* node */
/*
* Indicate that have managed to store valid data in the <board_cache>
* at least once.
*/
if (board_count > 0)
return (rv);
}
static int
{
/*
* We want to avoid the case where an invalid time
* is specified by a user (by patching the
* global variable <sgenv_max_mbox_wait_time>).
*
* Any incorrect values are reset to the default time.
*/
return (rv);
}
static int
{
/*
* Only one of these buffers is ever going to be used in a call
* so to save kernel stack space we use a union.
*/
union {
} buf;
int i; /* loop variable for mbox msg_buf */
if (flag == SG_GET_ENV_CONSTANTS) {
} else if (flag == SG_GET_ENV_VOLATILES) {
} else if (flag == SG_GET_ENV_THRESHOLDS) {
} else {
return (-1);
}
/*
* We want to avoid the case where an invalid time
* is specified by a user (by patching the
* global variable <sgenv_max_mbox_wait_time>).
*
* Any incorrect values are reset to the default time.
*/
/*
* We now check that the data returned is valid.
*/
if (rv != 0) {
/*
* The SBBC driver encountered an error.
*/
return (rv);
} else {
/*
* The SC encountered an error.
*/
switch (*status) {
case SG_MBOX_STATUS_SUCCESS:
/*
* No problems encountered - continue and return the
* new data.
*/
break;
case ETIMEDOUT:
/*
* For some reason the mailbox failed to return data
* and instead timed out so we return ETIMEDOUT
*/
return (ETIMEDOUT);
case ENXIO:
/*
* no sensors associated with this key, this may have
* changed since we read the keys.
*/
return (ENXIO);
default:
/*
* The contents of the mbox message contain corrupt
* data. Flag this as an error to be returned.
*/
return (EINVAL);
}
}
/*
* data returned in the mailbox message into the <env_cache>.
*/
if (flag == SG_GET_ENV_CONSTANTS) {
} else if (flag == SG_GET_ENV_VOLATILES) {
} else if (flag == SG_GET_ENV_THRESHOLDS) {
}
}
if (flag == SG_GET_ENV_VOLATILES)
return (rv);
}
/*
* This function handles any errors received from the mailbox framework while
* getting environmental data.
*
* INPUT PARAMETERS
* err - return value from call to mailbox framework.
* status - message status returned by mailbox framework.
* key - key from previous (if any) reading of env data.
* Needed to see if we have old data in the <env_cache>.
* str - String indicating what type of env request failed.
*
* RETURN VALUES
* rv == DDI_FAILURE - there is no point in continuing processing
* the data, we should exit from the kstat
* framework.
* rv != DDI_FAILURE - error has been handled correctly, continue
* processing the data returned from the SC.
*/
static int
{
switch (err) {
case ENXIO:
/*
* The SC has changed the env data associated with this key
* since we started getting the data. We cannot tell if the
* data has disappeared due to the removal of the board from
* our Domain or just that the data has been updated. We
* simply return the last known data (if possible) and the
* next time we request the env data, the SC will have
* finished processing this board so we will receive the
* correct key values and we can get the correct data.
*/
break;
default:
rv = DDI_FAILURE;
break;
}
/*
* If there was no data in the <env_cache>, we need to clear the data
* just added as the <env_cache> will only be partially filled.
*/
if (key == 0)
return (rv);
}
/*
* If the sensor readings for a particular collection of HPUs become invalid,
* then we clear the cache by freeing up the memory.
*/
static void
{
vol_sensor_count[key_posn] = 0;
}
}
static void
{
/*
* We update the count of errors we have encountered during calls to
* the mailbox framework (unless we will cause a wraparound)
*/
if (sgenv_mbox_error_count < INT_MAX)
#ifdef DEBUG
if ((sgenv_debug & SGENV_DEBUG_MSG) == 0)
return;
switch (err) {
case ENOTSUP:
"support SGENV");
break;
case ETIMEDOUT:
"SGENV request for %s", str);
break;
default:
break;
}
#endif
}
/*
* INPUT PARAMETERS
* key_posn - The position in the env_cache for which we want to
* allocate space for a HPU's env data.
*
* ERROR VALUES
* DDI_FAILURE - We failed to allocate memory for this cache entry.
* There is no point asking the SC for env data for this
* HPU as we will have nowhere to store it.
*/
static int
{
int i; /* used to loop thru each sensor to set the status */
key_posn);
return (DDI_FAILURE);
}
for (i = 0; i < SGENV_MAX_SENSORS_PER_KEY; i++)
return (DDI_SUCCESS);
}
static void
sgenv_destroy_env_cache(void)
{
int i;
for (i = 0; i < SGENV_MAX_HPU_KEYS; i++) {
vol_sensor_count[i] = 0;
}
}
}
static void
{
int i;
/* reinitialize this and recount number of sensors */
ksp->ks_data_size = 0;
for (i = 0; i < SGENV_MAX_HPU_KEYS; i++) {
if (vol_sensor_count[i] <= 0)
continue;
/*
* increment ksp->ks_data_size by the number of
* sensors in the collection <i>.
*/
sizeof (env_sensor_t);
}
}
/*
* This function is triggered by the thread that updates the env_cache.
* It checks for any sensors which have exceeded their limits/thresholds
* and generates sysevents for the sensor values that have changed.
*/
/*ARGSUSED*/
static uint_t
{
DCMN_ERR_S(f, "sgenv_poll_env()");
int i; /* loops through each sensor for each <key> */
if (vol_sensor_count[key] == 0)
continue;
for (i = 0; i < vol_sensor_count[key]; i++) {
if (SG_GET_SENSOR_STATUS(status) ==
continue;
}
/*
* This sensor has changed in status since the last
* time we polled - we need to inform the sysevent
* framework.
*/
/*
* we don't care about the pseudo sensors and
* the Fan Status is notified by a separate
* unsolicited event so we simply get the next
* reading
*/
case SG_SENSOR_TYPE_ENVDB:
case SG_SENSOR_TYPE_COOLING:
continue;
/*
* We have handled all the special cases by now.
*/
default:
(void) sgenv_process_threshold_event(sensor);
break;
}
}
}
return (DDI_SUCCESS);
}
/*
* This function is passed in an array of length SSM_MAX_INSTANCES and
* it searches OBP to for ssm nodes, and for each one if finds, it sets the
* corresponding position in the array to TRUE.
*/
static void
{
rdip = ddi_root_node();
int value;
DDI_PROP_DONTPASS, "nodeid", 0);
/*
* If we get a valid nodeID which has not already
* been found in a previous call to this function,
* then we set all 10 LSB bits to indicate there may
* be a board present in each slot.
*
* It is the job of sgenv_get_board_info_data() to weed
* out the invalid cases when we don't have a
* DS chassis.
*
* NOTE: We make the assumption that a chassis cannot
* be DR'ed out, which is true for a Serengeti.
* By the time WildCat need this functionality Solaris
* will be able to know what kind of a chassis is
* present and there will be no need to try and work
* this out from the msg_status from the mailbox.
*/
if ((value >= 0) &&
(value < SSM_MAX_INSTANCES) &&
}
}
}
}
static void
{
/*
* Save the previous status so we can compare them later
*/
case SG_SENSOR_TYPE_ENVDB:
/*
* We want the status of this sensor to always be OK
* The concept of limits/thresholds do not exist for it.
*/
break;
case SG_SENSOR_TYPE_COOLING:
/*
* Fans have no concept of limits/thresholds, they have a state
* which we store in the <sd_status> field so that we can see
* when this state is changed.
*/
} else {
}
/*
* If this is the first time this fan status has been read,
* then we need to initialize the previous reading to be the
* same as the current reading so that an event is not
* triggered.
*
* [ When the env_cache is being created, the status of the
* sensors is set to SG_SENSOR_STATUS_OK, which is not a
* valid Fan status ].
*/
}
break;
default:
} else {
}
break;
}
}
/*
* This function, when given an integer arg describing a HPU type,
* returns the descriptive string associated with this HPU type.
*/
static const char *
{
else
hpu_list++;
}
return ((char *)NULL);
}
/*
* This function, when given an integer arg describing a sensor part,
* returns the descriptive string associated with this sensor part.
*/
static const char *
{
else
part_list++;
}
return ((char *)NULL);
}
/*
* This function, when given an integer arg describing a sensor type,
* returns the descriptive string associated with this sensor type.
*/
static const char *
{
else
type_list++;
}
return ((char *)NULL);
}
/*
* This function takes a sensor TagID and generates a string describing
* where in the system the sensor is.
*/
static void
{
const char *hpu_str;
const char *part_str;
const char *type_str;
"Sensor: Node=%d, Board=%s%d, Device=%s%d, Type=%s%d: reading has ",
}
/*
* This interrupt handler watches for unsolicited mailbox messages from the SC
* telling it that the Keyswitch Position had changed. It then informs the
* Sysevent Framework of this change.
*/
static uint_t
{
DCMN_ERR_S(f, "sgenv_keyswitch_handler()");
int err;
return (DDI_INTR_CLAIMED);
}
return (DDI_INTR_CLAIMED);
}
return (DDI_INTR_CLAIMED);
}
/*
* Allocate memory for sysevent buffer.
*/
return (DDI_INTR_CLAIMED);
}
/*
* Set the DOMAIN_WHAT_CHANGED attribute.
*/
&se_val, SE_NOSLEEP);
if (err != 0) {
return (DDI_INTR_CLAIMED);
}
/*
* Log this event with sysevent framework.
*/
return (DDI_INTR_CLAIMED);
}
if (err != 0) {
return (DDI_INTR_CLAIMED);
}
/* clean up */
return (DDI_INTR_CLAIMED);
}
/*
* This interrupt handler watches for unsolicited mailbox messages from the SC
* It then informs the Sysevent Framework of this change and updates the
* env_cache.
*/
static uint_t
{
DCMN_ERR_S(f, "sgenv_env_data_handler()");
return (DDI_INTR_CLAIMED);
}
return (DDI_INTR_CLAIMED);
}
/*
* We check the first field of the msg_buf to see if the event_type
* is SC_EVENT_ENV, if it is then we handle the event.
*/
return (DDI_INTR_CLAIMED);
}
/*
* We now need to signal to the env background thread to ask the SC
* for env readings and discover which sensor caused the SC to send
* the ENV event before sending a sysevent to userland.
*/
return (DDI_INTR_CLAIMED);
}
/*
* This interrupt handler watches for unsolicited mailbox messages from the SC
* telling it that the status of a fan has changed. We register a sysevent
* and trigger a softint to update the env cache.
*/
static uint_t
{
DCMN_ERR_S(f, "sgenv_fan_status_handler()");
int err;
return (DDI_INTR_CLAIMED);
}
/*
* We check the first field of the msg_buf to see if the event_type
* is SC_EVENT_FAN
*/
return (DDI_INTR_CLAIMED);
}
/*
* If another type of ENV Event triggered this handler then we simply
* return now.
*/
return (DDI_INTR_CLAIMED);
}
/*
* Allocate memory for sysevent buffer.
*/
f, EC_ENV, ESC_ENV_FAN);
return (DDI_INTR_CLAIMED);
}
/*
* Set the following attributes for this event:
*
* ENV_FRU_ID
* ENV_FRU_RESOURCE_ID
* ENV_FRU_DEVICE
* ENV_FRU_STATE
* ENV_MSG
*
*/
if (err != 0) {
return (DDI_INTR_CLAIMED);
}
&se_val, SE_NOSLEEP);
if (err != 0) {
return (DDI_INTR_CLAIMED);
}
&se_val, SE_NOSLEEP);
if (err != 0) {
return (DDI_INTR_CLAIMED);
}
/*
* Checks the fan to see if it has failed.
*/
case SGENV_FAN_SPEED_OFF:
case SGENV_FAN_SPEED_LOW:
case SGENV_FAN_SPEED_HIGH:
break;
case SGENV_FAN_SPEED_UNKNOWN:
default:
break;
}
&se_val, SE_NOSLEEP);
if (err != 0) {
return (DDI_INTR_CLAIMED);
}
/*
* Create the message to be sent to sysevent.
*/
case SGENV_FAN_SPEED_OFF:
break;
case SGENV_FAN_SPEED_LOW:
break;
case SGENV_FAN_SPEED_HIGH:
break;
case SGENV_FAN_SPEED_UNKNOWN:
default:
break;
}
if (err != 0) {
return (DDI_INTR_CLAIMED);
}
/*
* Log this event with sysevent framework.
*/
return (DDI_INTR_CLAIMED);
}
if (err != 0) {
return (DDI_INTR_CLAIMED);
}
/*
* We now need to signal to the env background thread to ask the SC
* for env readings and discover which sensor caused the SC to send
* the ENV event before sending a sysevent to userland.
*/
return (DDI_INTR_CLAIMED);
}
/*
* This function informs the Sysevent Framework that a temperature, voltage
* that the reading has returned to a safe value having exceeded its
*/
static int
{
DCMN_ERR_S(f, "sgenv_process_threshold_event()");
int err;
/*
* This function handles the case when a temperature reading passes
* so we need to work out which case it is.
*
* if <temp_event_type> is TRUE, then need to handle an event
* of type ESC_ENV_TEMP.
*/
int temp_event_type;
return (DDI_FAILURE);
}
break;
default:
return (DDI_FAILURE);
}
break;
}
/*
* Set the following attributes for this event:
*
* ENV_FRU_ID
* ENV_FRU_RESOURCE_ID
* ENV_FRU_DEVICE
* ENV_FRU_STATE
* ENV_MSG
*
*/
if (err != 0) {
return (DDI_FAILURE);
}
&se_val, SE_NOSLEEP);
if (err != 0) {
return (DDI_FAILURE);
}
&se_val, SE_NOSLEEP);
if (err != 0) {
return (DDI_FAILURE);
}
/*
* We need to find out the status of the reading.
*/
case SG_SENSOR_STATUS_OK:
break;
case SG_SENSOR_STATUS_LO_WARN:
case SG_SENSOR_STATUS_HI_WARN:
break;
default:
break;
}
/*
* Add ENV_FRU_STATE attribute.
*/
&se_val, SE_NOSLEEP);
if (err != 0) {
err);
return (DDI_FAILURE);
}
/*
* Save the sensor TagID as a string so that a meaningful message
* can be passed to as part of the ENV_MSG attribute.
*/
/*
* We need to add a string stating what type of event occurred.
*/
case SG_SENSOR_STATUS_OK:
break;
case SG_SENSOR_STATUS_LO_WARN:
break;
case SG_SENSOR_STATUS_HI_WARN:
break;
break;
break;
default:
break;
}
/*
* Add ENV_MSG attribute.
*/
if (err != 0) {
return (DDI_FAILURE);
}
/*
* Log this event with sysevent framework.
*/
return (DDI_FAILURE);
}
if (err != 0) {
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* This function gets called when sgenv is notified of a DR event.
* We need to update the board and env caches to ensure that they
* now contain the latest system information..
*/
static uint_t
{
DCMN_ERR_S(f, "sgenv_dr_event_handler()");
return (DDI_INTR_CLAIMED);
}
return (DDI_INTR_CLAIMED);
}
/*
* We check the event_details field of the msg_buf to see if
* we need to invalidate the caches
*/
switch (payload->event_details) {
case SG_EVT_BOARD_ABSENT:
case SG_EVT_BOARD_PRESENT:
case SG_EVT_UNASSIGN:
case SG_EVT_ASSIGN:
case SG_EVT_UNAVAILABLE:
case SG_EVT_AVAILABLE:
case SG_EVT_POWER_OFF:
case SG_EVT_POWER_ON:
case SG_EVT_PASSED_TEST:
case SG_EVT_FAILED_TEST:
/*
* We now need to signal to the background threads to poll the
* SC for env readings and board info which may have changed
* as a result of the DR changes. This will cause the
* env_cache and the board_cache to be updated.
*/
break;
default:
break;
}
return (DDI_INTR_CLAIMED);
}
/*
* from the SC. It indicates to the thread responsible for the cache specified
* that it needs to update its data.
*/
static void
{
DCMN_ERR_S(f, "sgenv_indicate_cache_update_needed()");
/*
* If the cache is already being updated, we set a flag to
* inform the thread that it needs to reread the data when
* it is finished as we cannot be sure if the data was read
* before or after the time this handler was triggered.
*
* Otherwise the thread is waiting for us and we signal
* to it to start reading the data.
*/
switch (cache_type) {
case ENV_CACHE:
if (env_cache_updating) {
"updating env cache", f);
} else {
"to env thread", f);
}
break;
case BOARD_CACHE:
if (board_cache_updating) {
"updating board cache", f);
} else {
"to board thread", f);
}
break;
default:
break;
}
}