NetBSD/sys/dev/sysmon/sysmon_envsys_events.c

578 lines
15 KiB
C

/* $NetBSD: sysmon_envsys_events.c,v 1.1 2007/07/01 07:36:52 xtraeme Exp $ */
/*-
* Copyright (c) 2007 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Juan Romero Pardines.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Juan Romero Pardines
* for the NetBSD Foundation, Inc. and its contributors.
* 4. Neither the name of The NetBSD Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* sysmon_envsys(9) events framework.
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: sysmon_envsys_events.c,v 1.1 2007/07/01 07:36:52 xtraeme Exp $");
#include <sys/param.h>
#include <sys/types.h>
#include <sys/conf.h>
#include <sys/errno.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/mutex.h>
#include <sys/kmem.h>
#include <sys/callout.h>
#include <dev/sysmon/sysmonvar.h>
#include <dev/sysmon/sysmon_envsysvar.h>
struct sme_sensor_state {
int type;
const char *desc;
};
static const struct sme_sensor_state sme_sensor_drive_state[] = {
{ ENVSYS_DRIVE_EMPTY, "drive state is unknown" },
{ ENVSYS_DRIVE_READY, "drive is ready" },
{ ENVSYS_DRIVE_POWERUP, "drive is powering up" },
{ ENVSYS_DRIVE_ONLINE, "drive is online" },
{ ENVSYS_DRIVE_IDLE, "drive is idle" },
{ ENVSYS_DRIVE_ACTIVE, "drive is active" },
{ ENVSYS_DRIVE_REBUILD, "drive is rebuilding" },
{ ENVSYS_DRIVE_POWERDOWN, "drive is powering down" },
{ ENVSYS_DRIVE_FAIL, "drive failed" },
{ ENVSYS_DRIVE_PFAIL, "drive degraded" },
{ -1, "unknown" }
};
static struct workqueue *seewq;
static struct callout seeco;
static bool sme_events_initialized = false;
kmutex_t sme_mtx, sme_event_mtx, sme_event_init_mtx;
/*
* sme_event_register:
*
* + Registers a sysmon envsys event.
* + Creates a new sysmon envsys event.
*/
int
sme_event_register(sme_event_t *see)
{
sme_event_t *lsee;
struct penvsys_state *pes_old, *pes_new;
int error = 0;
KASSERT(see != NULL);
pes_new = &see->pes;
mutex_enter(&sme_event_mtx);
/*
* Ensure that we don't add events for the same sensor
* and with the same type.
*/
LIST_FOREACH(lsee, &sme_events_list, see_list) {
pes_old = &lsee->pes;
if (strcmp(pes_old->pes_sensname,
pes_new->pes_sensname) == 0) {
if (lsee->type == see->type) {
DPRINTF(("%s: dev=%s sensor=%s type=%d "
"(already exists)\n", __func__,
see->pes.pes_dvname,
see->pes.pes_sensname, see->type));
error = EEXIST;
goto out;
}
}
}
DPRINTF(("%s: dev=%s sensor=%s snum=%d type=%d "
"critval=%" PRIu32 "\n", __func__,
see->pes.pes_dvname, see->pes.pes_sensname,
see->snum, see->type, see->critval));
LIST_INSERT_HEAD(&sme_events_list, see, see_list);
/*
* Initialize the events framework if it wasn't initialized
* before.
*/
mutex_enter(&sme_event_init_mtx);
if (sme_events_initialized == false)
error = sme_events_init();
mutex_exit(&sme_event_init_mtx);
out:
mutex_exit(&sme_event_mtx);
return error;
}
/*
* sme_event_unregister:
*
* + Unregisters a sysmon envsys event.
*/
int
sme_event_unregister(const char *sensor, int type)
{
sme_event_t *see;
bool found = false;
KASSERT(sensor != NULL);
mutex_enter(&sme_event_mtx);
LIST_FOREACH(see, &sme_events_list, see_list) {
if (strcmp(see->pes.pes_sensname, sensor) == 0) {
if (see->type == type) {
found = true;
break;
}
}
}
if (!found) {
mutex_exit(&sme_event_mtx);
return EINVAL;
}
DPRINTF(("%s: removing dev=%s sensor=%s type=%d\n",
__func__, see->pes.pes_dvname, sensor, type));
LIST_REMOVE(see, see_list);
/*
* So the events list is empty, we'll do the following:
*
* - stop the callout.
* - destroy the workqueue.
*/
if (LIST_EMPTY(&sme_events_list)) {
mutex_exit(&sme_event_mtx);
mutex_enter(&sme_event_init_mtx);
callout_stop(&seeco);
workqueue_destroy(seewq);
sme_events_initialized = false;
DPRINTF(("%s: events framework destroyed\n", __func__));
mutex_exit(&sme_event_init_mtx);
goto out;
}
mutex_exit(&sme_event_mtx);
kmem_free(see, sizeof(*see));
out:
return 0;
}
/*
* sme_event_drvadd:
*
* + Adds a new sysmon envsys event for a driver if a sensor
* has set any accepted monitoring flag.
*/
void
sme_event_drvadd(void *arg)
{
sme_event_drv_t *sed_t = arg;
KASSERT(sed_t != NULL);
#define SEE_REGEVENT(a, b, c) \
do { \
if (sed_t->edata->flags & (a)) { \
char str[32] = "monitoring-state-"; \
\
if (sme_event_add(sed_t->sdict, \
sed_t->edata, \
sed_t->sme->sme_name, \
NULL, \
0, \
(b), \
sed_t->powertype)) \
printf("%s: failed to add event (%s)\n", \
__func__, (c)); \
mutex_enter(&sme_mtx); \
(void)strlcat(str, (c), sizeof(str)); \
prop_dictionary_set_bool(sed_t->sdict, str, true); \
mutex_exit(&sme_mtx); \
} \
} while (/* CONSTCOND */ 0)
SEE_REGEVENT(ENVSYS_FMONCRITICAL,
PENVSYS_EVENT_CRITICAL,
"critical");
SEE_REGEVENT(ENVSYS_FMONCRITUNDER,
PENVSYS_EVENT_CRITUNDER,
"critunder");
SEE_REGEVENT(ENVSYS_FMONCRITOVER,
PENVSYS_EVENT_CRITOVER,
"critover");
SEE_REGEVENT(ENVSYS_FMONWARNUNDER,
PENVSYS_EVENT_WARNUNDER,
"warnunder");
SEE_REGEVENT(ENVSYS_FMONWARNOVER,
PENVSYS_EVENT_WARNOVER,
"warnover");
SEE_REGEVENT(ENVSYS_FMONDRVSTATE,
PENVSYS_EVENT_DRIVE_STCHANGED,
"drvstchanged");
/* we are done, free memory now */
kmem_free(sed_t, sizeof(*sed_t));
}
/*
* sme_event_add:
*
* + Initializes or updates a sysmon envsys event.
*/
int
sme_event_add(prop_dictionary_t sdict, envsys_data_t *edata,
const char *drvn, const char *objkey,
int32_t critval, int crittype, int powertype)
{
sme_event_t *see = NULL;
prop_object_t obj;
int error = 0;
KASSERT(sdict != NULL || edata != NULL);
/* critical condition set via userland */
if (objkey && critval) {
obj = prop_dictionary_get(sdict, objkey);
if (obj != NULL) {
/*
* object is already in dictionary, update
* the critical value.
*/
mutex_enter(&sme_event_mtx);
LIST_FOREACH(see, &sme_events_list, see_list) {
if (strcmp(edata->desc,
see->pes.pes_sensname) == 0)
if (crittype == see->type)
break;
}
see->critval = critval;
mutex_exit(&sme_event_mtx);
DPRINTF(("%s: event updated\n", __func__));
goto out;
}
}
if (LIST_EMPTY(&sme_events_list))
goto register_event;
/* check if the event is already on the list */
mutex_enter(&sme_event_mtx);
LIST_FOREACH(see, &sme_events_list, see_list) {
if (strcmp(edata->desc, see->pes.pes_sensname) == 0)
if (crittype == see->type) {
mutex_exit(&sme_event_mtx);
error = EEXIST;
goto out;
}
}
mutex_exit(&sme_event_mtx);
/*
* object is not in dictionary, create a new
* sme event and assign required members.
*/
register_event:
see = kmem_zalloc(sizeof(*see), KM_SLEEP);
mutex_enter(&sme_event_mtx);
see->critval = critval;
see->type = crittype;
(void)strlcpy(see->pes.pes_dvname, drvn,
sizeof(see->pes.pes_dvname));
see->pes.pes_type = powertype;
(void)strlcpy(see->pes.pes_sensname, edata->desc,
sizeof(see->pes.pes_sensname));
see->snum = edata->sensor;
mutex_exit(&sme_event_mtx);
if (sme_event_register(see)) {
kmem_free(see, sizeof(*see));
return EINVAL;
}
out:
/* update the object in the dictionary */
if (objkey && critval) {
mutex_enter(&sme_event_mtx);
SENSOR_UPINT32(sdict, objkey, critval);
mutex_exit(&sme_event_mtx);
}
return error;
}
/*
* sme_events_init:
*
* + Initializes the callout and the workqueue to handle
* the sysmon envsys events.
*/
int
sme_events_init(void)
{
int error;
error = workqueue_create(&seewq, "envsysev",
sme_events_worker, NULL, 0, IPL_NONE, 0);
if (error)
goto out;
callout_init(&seeco);
callout_setfunc(&seeco, sme_events_check, NULL);
callout_schedule(&seeco, SME_EVTIMO);
sme_events_initialized = true;
DPRINTF(("%s: events framework initialized\n", __func__));
out:
return error;
}
/*
* sme_events_check:
*
* + Runs the work on each sysmon envsys event in our
* workqueue periodically with callout.
*/
void
sme_events_check(void *arg)
{
sme_event_t *see;
LIST_FOREACH(see, &sme_events_list, see_list) {
DPRINTF(("%s: dev=%s sensor=%s type=%d\n",
__func__,
see->pes.pes_dvname,
see->pes.pes_sensname,
see->type));
workqueue_enqueue(seewq, &see->see_wk);
}
callout_schedule(&seeco, SME_EVTIMO);
}
/*
* sme_events_worker:
*
* + workqueue thread that checks if there's a critical condition
* and sends an event if the condition was triggered.
*/
void
sme_events_worker(struct work *wk, void *arg)
{
const struct sme_sensor_state *esds = sme_sensor_drive_state;
sme_event_t *see = (void *)wk;
struct sysmon_envsys *sme;
envsys_data_t *edata;
int d, i, error = 0;
KASSERT(wk == &see->see_wk);
/* XXX: NOT YET mutex_enter(&sme_event_mtx); */
/*
* We have to find the sme device by looking
* at the power envsys device name.
*/
LIST_FOREACH(sme, &sysmon_envsys_list, sme_list)
if (strcmp(sme->sme_name, see->pes.pes_dvname) == 0)
break;
KASSERT(sme != NULL);
/* get the sensor number in the sme event */
d = see->snum;
edata = &sme->sme_sensor_data[d];
/*
* refresh the sensor that was marked with a critical
* event.
*/
if ((sme->sme_flags & SME_DISABLE_GTREDATA) == 0) {
error = (*sme->sme_gtredata)(sme, edata);
if (error) {
mutex_exit(&sme_event_mtx);
return;
}
}
DPRINTF(("%s: desc=%s sensor=%d units=%d value_cur=%d\n",
__func__, edata->desc, edata->sensor,
edata->units, edata->value_cur));
#define SME_SENDNORMALEVENT() \
do { \
if (see->evsent && edata->state == ENVSYS_SVALID) { \
see->evsent = false; \
sysmon_penvsys_event(&see->pes, PENVSYS_EVENT_NORMAL); \
} \
} while (/* CONSTCOND */ 0)
switch (see->type) {
/* handle a critical limit event */
case PENVSYS_EVENT_CRITICAL:
SME_SENDNORMALEVENT();
if (!see->evsent && edata->state == ENVSYS_SCRITICAL) {
see->evsent = true;
sysmon_penvsys_event(&see->pes,
PENVSYS_EVENT_CRITICAL);
}
break;
/* handle a critical under limit event */
case PENVSYS_EVENT_CRITUNDER:
SME_SENDNORMALEVENT();
if (!see->evsent && edata->state == ENVSYS_SCRITUNDER) {
see->evsent = true;
sysmon_penvsys_event(&see->pes,
PENVSYS_EVENT_CRITUNDER);
}
break;
/* handle a critical over limit event */
case PENVSYS_EVENT_CRITOVER:
SME_SENDNORMALEVENT();
if (!see->evsent && edata->state == ENVSYS_SCRITOVER) {
see->evsent = true;
sysmon_penvsys_event(&see->pes,
PENVSYS_EVENT_CRITOVER);
}
break;
/* handle a warning under limit event */
case PENVSYS_EVENT_WARNUNDER:
SME_SENDNORMALEVENT();
if (!see->evsent && edata->state == ENVSYS_SWARNUNDER) {
see->evsent = true;
sysmon_penvsys_event(&see->pes,
PENVSYS_EVENT_WARNUNDER);
}
break;
/* handle a warning over limit event */
case PENVSYS_EVENT_WARNOVER:
SME_SENDNORMALEVENT();
if (!see->evsent && edata->state == ENVSYS_SWARNOVER) {
see->evsent = true;
sysmon_penvsys_event(&see->pes,
PENVSYS_EVENT_WARNOVER);
}
break;
/* handle an user critical capacity */
case PENVSYS_EVENT_BATT_USERCAP:
if (see->evsent && edata->value_cur > see->critval) {
see->evsent = false;
sysmon_penvsys_event(&see->pes, PENVSYS_EVENT_NORMAL);
}
if (!see->evsent && edata->value_cur < see->critval) {
see->evsent = true;
sysmon_penvsys_event(&see->pes,
PENVSYS_EVENT_BATT_USERCAP);
}
break;
/* handle a max critical event */
case PENVSYS_EVENT_USER_CRITMAX:
if (see->evsent && edata->value_cur < see->critval) {
see->evsent = false;
sysmon_penvsys_event(&see->pes, PENVSYS_EVENT_NORMAL);
}
if (!see->evsent && edata->value_cur > see->critval) {
see->evsent = true;
sysmon_penvsys_event(&see->pes,
PENVSYS_EVENT_USER_CRITMAX);
}
break;
/* handle a min critical event */
case PENVSYS_EVENT_USER_CRITMIN:
if (see->evsent && edata->value_cur > see->critval) {
see->evsent = false;
sysmon_penvsys_event(&see->pes, PENVSYS_EVENT_NORMAL);
}
if (!see->evsent && edata->value_cur < see->critval) {
see->evsent = true;
sysmon_penvsys_event(&see->pes,
PENVSYS_EVENT_USER_CRITMIN);
}
break;
/* handle a drive state change event */
case PENVSYS_EVENT_DRIVE_STCHANGED:
/* the state has not been changed, just ignore the event */
if (edata->value_cur == see->evsent)
break;
for (i = 0; esds[i].type != -1; i++)
if (esds[i].type == edata->value_cur)
break;
/* copy current state description */
(void)strlcpy(see->pes.pes_statedesc, esds[i].desc,
sizeof(see->pes.pes_statedesc));
/* state is ok again... send a normal event */
if (see->evsent && edata->value_cur == ENVSYS_DRIVE_ONLINE) {
see->evsent = false;
sysmon_penvsys_event(&see->pes, PENVSYS_EVENT_NORMAL);
}
/* something bad happened to the drive... send the event */
if (see->evsent || edata->value_cur != ENVSYS_DRIVE_ONLINE) {
/* save current drive state */
see->evsent = edata->value_cur;
sysmon_penvsys_event(&see->pes,
PENVSYS_EVENT_DRIVE_STCHANGED);
}
break;
default:
break;
}
/* XXX: NOT YET mutex_exit(&sme_event_mtx); */
}