sysmon_envsys_events.c revision 1.2 1 /* $NetBSD: sysmon_envsys_events.c,v 1.2 2007/07/02 11:05:52 xtraeme Exp $ */
2
3 /*-
4 * Copyright (c) 2007 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Juan Romero Pardines.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by Juan Romero Pardines
21 * for the NetBSD Foundation, Inc. and its contributors.
22 * 4. Neither the name of The NetBSD Foundation nor the names of its
23 * contributors may be used to endorse or promote products derived
24 * from this software without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 */
38
39 /*
40 * sysmon_envsys(9) events framework.
41 */
42
43 #include <sys/cdefs.h>
44 __KERNEL_RCSID(0, "$NetBSD: sysmon_envsys_events.c,v 1.2 2007/07/02 11:05:52 xtraeme Exp $");
45
46 #include <sys/param.h>
47 #include <sys/types.h>
48 #include <sys/conf.h>
49 #include <sys/errno.h>
50 #include <sys/kernel.h>
51 #include <sys/systm.h>
52 #include <sys/proc.h>
53 #include <sys/mutex.h>
54 #include <sys/kmem.h>
55 #include <sys/callout.h>
56
57 #include <dev/sysmon/sysmonvar.h>
58 #include <dev/sysmon/sysmon_envsysvar.h>
59
60 struct sme_sensor_state {
61 int type;
62 const char *desc;
63 };
64
65 static const struct sme_sensor_state sme_sensor_drive_state[] = {
66 { ENVSYS_DRIVE_EMPTY, "drive state is unknown" },
67 { ENVSYS_DRIVE_READY, "drive is ready" },
68 { ENVSYS_DRIVE_POWERUP, "drive is powering up" },
69 { ENVSYS_DRIVE_ONLINE, "drive is online" },
70 { ENVSYS_DRIVE_IDLE, "drive is idle" },
71 { ENVSYS_DRIVE_ACTIVE, "drive is active" },
72 { ENVSYS_DRIVE_REBUILD, "drive is rebuilding" },
73 { ENVSYS_DRIVE_POWERDOWN, "drive is powering down" },
74 { ENVSYS_DRIVE_FAIL, "drive failed" },
75 { ENVSYS_DRIVE_PFAIL, "drive degraded" },
76 { -1, "unknown" }
77 };
78
79 static struct workqueue *seewq;
80 static struct callout seeco;
81 static bool sme_events_initialized = false;
82 kmutex_t sme_mtx, sme_event_mtx, sme_event_init_mtx;
83
84 /*
85 * sme_event_register:
86 *
87 * + Registers a sysmon envsys event.
88 * + Creates a new sysmon envsys event.
89 */
90 int
91 sme_event_register(sme_event_t *see)
92 {
93 sme_event_t *lsee;
94 struct penvsys_state *pes_old, *pes_new;
95 int error = 0;
96
97 KASSERT(see != NULL);
98
99 pes_new = &see->pes;
100
101 mutex_enter(&sme_event_mtx);
102 /*
103 * Ensure that we don't add events for the same sensor
104 * and with the same type.
105 */
106 LIST_FOREACH(lsee, &sme_events_list, see_list) {
107 pes_old = &lsee->pes;
108 if (strcmp(pes_old->pes_sensname,
109 pes_new->pes_sensname) == 0) {
110 if (lsee->type == see->type) {
111 DPRINTF(("%s: dev=%s sensor=%s type=%d "
112 "(already exists)\n", __func__,
113 see->pes.pes_dvname,
114 see->pes.pes_sensname, see->type));
115 error = EEXIST;
116 goto out;
117 }
118 }
119 }
120
121 DPRINTF(("%s: dev=%s sensor=%s snum=%d type=%d "
122 "critval=%" PRIu32 "\n", __func__,
123 see->pes.pes_dvname, see->pes.pes_sensname,
124 see->snum, see->type, see->critval));
125
126 LIST_INSERT_HEAD(&sme_events_list, see, see_list);
127 /*
128 * Initialize the events framework if it wasn't initialized
129 * before.
130 */
131 mutex_enter(&sme_event_init_mtx);
132 if (sme_events_initialized == false)
133 error = sme_events_init();
134 mutex_exit(&sme_event_init_mtx);
135
136 out:
137 mutex_exit(&sme_event_mtx);
138 return error;
139 }
140
141 /*
142 * sme_event_unregister:
143 *
144 * + Unregisters a sysmon envsys event.
145 */
146 int
147 sme_event_unregister(const char *sensor, int type)
148 {
149 sme_event_t *see;
150 bool found = false;
151
152 KASSERT(sensor != NULL);
153
154 mutex_enter(&sme_event_mtx);
155 LIST_FOREACH(see, &sme_events_list, see_list) {
156 if (strcmp(see->pes.pes_sensname, sensor) == 0) {
157 if (see->type == type) {
158 found = true;
159 break;
160 }
161 }
162 }
163
164 if (!found) {
165 mutex_exit(&sme_event_mtx);
166 return EINVAL;
167 }
168
169 DPRINTF(("%s: removing dev=%s sensor=%s type=%d\n",
170 __func__, see->pes.pes_dvname, sensor, type));
171 LIST_REMOVE(see, see_list);
172
173 /*
174 * So the events list is empty, we'll do the following:
175 *
176 * - stop the callout.
177 * - destroy the workqueue.
178 */
179 if (LIST_EMPTY(&sme_events_list)) {
180 mutex_exit(&sme_event_mtx);
181
182 mutex_enter(&sme_event_init_mtx);
183 callout_stop(&seeco);
184 workqueue_destroy(seewq);
185 sme_events_initialized = false;
186 DPRINTF(("%s: events framework destroyed\n", __func__));
187 mutex_exit(&sme_event_init_mtx);
188 goto out;
189 }
190
191 mutex_exit(&sme_event_mtx);
192 kmem_free(see, sizeof(*see));
193
194 out:
195 return 0;
196 }
197
198 /*
199 * sme_event_drvadd:
200 *
201 * + Adds a new sysmon envsys event for a driver if a sensor
202 * has set any accepted monitoring flag.
203 */
204 void
205 sme_event_drvadd(void *arg)
206 {
207 sme_event_drv_t *sed_t = arg;
208 int error = 0;
209
210 KASSERT(sed_t != NULL);
211
212 #define SEE_REGEVENT(a, b, c) \
213 do { \
214 if (sed_t->edata->flags & (a)) { \
215 char str[32] = "monitoring-state-"; \
216 \
217 error = sme_event_add(sed_t->sdict, \
218 sed_t->edata, \
219 sed_t->sme->sme_name, \
220 NULL, \
221 0, \
222 (b), \
223 sed_t->powertype); \
224 if (error && error != EEXIST) \
225 printf("%s: failed to add event! " \
226 "error=%d sensor=%s event=%s\n", \
227 __func__, error, sed_t->edata->desc, (c)); \
228 else { \
229 mutex_enter(&sme_mtx); \
230 (void)strlcat(str, (c), sizeof(str)); \
231 prop_dictionary_set_bool(sed_t->sdict, \
232 str, \
233 true); \
234 mutex_exit(&sme_mtx); \
235 } \
236 } \
237 } while (/* CONSTCOND */ 0)
238
239 SEE_REGEVENT(ENVSYS_FMONCRITICAL,
240 PENVSYS_EVENT_CRITICAL,
241 "critical");
242
243 SEE_REGEVENT(ENVSYS_FMONCRITUNDER,
244 PENVSYS_EVENT_CRITUNDER,
245 "critunder");
246
247 SEE_REGEVENT(ENVSYS_FMONCRITOVER,
248 PENVSYS_EVENT_CRITOVER,
249 "critover");
250
251 SEE_REGEVENT(ENVSYS_FMONWARNUNDER,
252 PENVSYS_EVENT_WARNUNDER,
253 "warnunder");
254
255 SEE_REGEVENT(ENVSYS_FMONWARNOVER,
256 PENVSYS_EVENT_WARNOVER,
257 "warnover");
258
259 SEE_REGEVENT(ENVSYS_FMONDRVSTATE,
260 PENVSYS_EVENT_DRIVE_STCHANGED,
261 "drvstchanged");
262
263 /* we are done, free memory now */
264 kmem_free(sed_t, sizeof(*sed_t));
265 }
266
267 /*
268 * sme_event_add:
269 *
270 * + Initializes or updates a sysmon envsys event.
271 */
272 int
273 sme_event_add(prop_dictionary_t sdict, envsys_data_t *edata,
274 const char *drvn, const char *objkey,
275 int32_t critval, int crittype, int powertype)
276 {
277 sme_event_t *see = NULL;
278 prop_object_t obj;
279 int error = 0;
280
281 KASSERT(sdict != NULL || edata != NULL);
282
283 /* critical condition set via userland */
284 if (objkey && critval) {
285 obj = prop_dictionary_get(sdict, objkey);
286 if (obj != NULL) {
287 /*
288 * object is already in dictionary, update
289 * the critical value.
290 */
291 mutex_enter(&sme_event_mtx);
292 LIST_FOREACH(see, &sme_events_list, see_list) {
293 if (strcmp(edata->desc,
294 see->pes.pes_sensname) == 0)
295 if (crittype == see->type)
296 break;
297 }
298 see->critval = critval;
299 mutex_exit(&sme_event_mtx);
300 DPRINTF(("%s: event updated\n", __func__));
301 goto out;
302 }
303 }
304
305 if (LIST_EMPTY(&sme_events_list))
306 goto register_event;
307
308 /* check if the event is already on the list */
309 mutex_enter(&sme_event_mtx);
310 LIST_FOREACH(see, &sme_events_list, see_list) {
311 if (strcmp(edata->desc, see->pes.pes_sensname) == 0)
312 if (crittype == see->type) {
313 mutex_exit(&sme_event_mtx);
314 error = EEXIST;
315 goto out;
316 }
317 }
318 mutex_exit(&sme_event_mtx);
319
320 /*
321 * object is not in dictionary, create a new
322 * sme event and assign required members.
323 */
324 register_event:
325 see = kmem_zalloc(sizeof(*see), KM_SLEEP);
326
327 mutex_enter(&sme_event_mtx);
328 see->critval = critval;
329 see->type = crittype;
330 (void)strlcpy(see->pes.pes_dvname, drvn,
331 sizeof(see->pes.pes_dvname));
332 see->pes.pes_type = powertype;
333 (void)strlcpy(see->pes.pes_sensname, edata->desc,
334 sizeof(see->pes.pes_sensname));
335 see->snum = edata->sensor;
336 mutex_exit(&sme_event_mtx);
337
338 if (sme_event_register(see)) {
339 kmem_free(see, sizeof(*see));
340 return EINVAL;
341 }
342
343 out:
344 /* update the object in the dictionary */
345 if (objkey && critval) {
346 mutex_enter(&sme_event_mtx);
347 SENSOR_UPINT32(sdict, objkey, critval);
348 mutex_exit(&sme_event_mtx);
349 }
350
351 return error;
352 }
353
354 /*
355 * sme_events_init:
356 *
357 * + Initializes the callout and the workqueue to handle
358 * the sysmon envsys events.
359 */
360 int
361 sme_events_init(void)
362 {
363 int error;
364
365 error = workqueue_create(&seewq, "envsysev",
366 sme_events_worker, NULL, 0, IPL_NONE, 0);
367 if (error)
368 goto out;
369
370 callout_init(&seeco);
371 callout_setfunc(&seeco, sme_events_check, NULL);
372 callout_schedule(&seeco, SME_EVTIMO);
373 sme_events_initialized = true;
374 DPRINTF(("%s: events framework initialized\n", __func__));
375
376 out:
377 return error;
378 }
379
380 /*
381 * sme_events_check:
382 *
383 * + Runs the work on each sysmon envsys event in our
384 * workqueue periodically with callout.
385 */
386 void
387 sme_events_check(void *arg)
388 {
389 sme_event_t *see;
390
391 LIST_FOREACH(see, &sme_events_list, see_list) {
392 DPRINTF(("%s: dev=%s sensor=%s type=%d\n",
393 __func__,
394 see->pes.pes_dvname,
395 see->pes.pes_sensname,
396 see->type));
397 workqueue_enqueue(seewq, &see->see_wk);
398 }
399 callout_schedule(&seeco, SME_EVTIMO);
400 }
401
402 /*
403 * sme_events_worker:
404 *
405 * + workqueue thread that checks if there's a critical condition
406 * and sends an event if the condition was triggered.
407 */
408 void
409 sme_events_worker(struct work *wk, void *arg)
410 {
411 const struct sme_sensor_state *esds = sme_sensor_drive_state;
412 sme_event_t *see = (void *)wk;
413 struct sysmon_envsys *sme;
414 envsys_data_t *edata;
415 int d, i, error = 0;
416
417 KASSERT(wk == &see->see_wk);
418
419 /* XXX: NOT YET mutex_enter(&sme_event_mtx); */
420 /*
421 * We have to find the sme device by looking
422 * at the power envsys device name.
423 */
424 LIST_FOREACH(sme, &sysmon_envsys_list, sme_list)
425 if (strcmp(sme->sme_name, see->pes.pes_dvname) == 0)
426 break;
427
428 KASSERT(sme != NULL);
429
430 /* get the sensor number in the sme event */
431 d = see->snum;
432 edata = &sme->sme_sensor_data[d];
433
434 /*
435 * refresh the sensor that was marked with a critical
436 * event.
437 */
438 if ((sme->sme_flags & SME_DISABLE_GTREDATA) == 0) {
439 error = (*sme->sme_gtredata)(sme, edata);
440 if (error) {
441 mutex_exit(&sme_event_mtx);
442 return;
443 }
444 }
445
446 DPRINTF(("%s: desc=%s sensor=%d units=%d value_cur=%d\n",
447 __func__, edata->desc, edata->sensor,
448 edata->units, edata->value_cur));
449
450 #define SME_SENDNORMALEVENT() \
451 do { \
452 if (see->evsent && edata->state == ENVSYS_SVALID) { \
453 see->evsent = false; \
454 sysmon_penvsys_event(&see->pes, PENVSYS_EVENT_NORMAL); \
455 } \
456 } while (/* CONSTCOND */ 0)
457
458 switch (see->type) {
459 /* handle a critical limit event */
460 case PENVSYS_EVENT_CRITICAL:
461 SME_SENDNORMALEVENT();
462 if (!see->evsent && edata->state == ENVSYS_SCRITICAL) {
463 see->evsent = true;
464 sysmon_penvsys_event(&see->pes,
465 PENVSYS_EVENT_CRITICAL);
466 }
467
468 break;
469 /* handle a critical under limit event */
470 case PENVSYS_EVENT_CRITUNDER:
471 SME_SENDNORMALEVENT();
472 if (!see->evsent && edata->state == ENVSYS_SCRITUNDER) {
473 see->evsent = true;
474 sysmon_penvsys_event(&see->pes,
475 PENVSYS_EVENT_CRITUNDER);
476 }
477
478 break;
479 /* handle a critical over limit event */
480 case PENVSYS_EVENT_CRITOVER:
481 SME_SENDNORMALEVENT();
482 if (!see->evsent && edata->state == ENVSYS_SCRITOVER) {
483 see->evsent = true;
484 sysmon_penvsys_event(&see->pes,
485 PENVSYS_EVENT_CRITOVER);
486 }
487
488 break;
489 /* handle a warning under limit event */
490 case PENVSYS_EVENT_WARNUNDER:
491 SME_SENDNORMALEVENT();
492 if (!see->evsent && edata->state == ENVSYS_SWARNUNDER) {
493 see->evsent = true;
494 sysmon_penvsys_event(&see->pes,
495 PENVSYS_EVENT_WARNUNDER);
496 }
497
498 break;
499 /* handle a warning over limit event */
500 case PENVSYS_EVENT_WARNOVER:
501 SME_SENDNORMALEVENT();
502 if (!see->evsent && edata->state == ENVSYS_SWARNOVER) {
503 see->evsent = true;
504 sysmon_penvsys_event(&see->pes,
505 PENVSYS_EVENT_WARNOVER);
506 }
507
508 break;
509 /* handle an user critical capacity */
510 case PENVSYS_EVENT_BATT_USERCAP:
511 if (see->evsent && edata->value_cur > see->critval) {
512 see->evsent = false;
513 sysmon_penvsys_event(&see->pes, PENVSYS_EVENT_NORMAL);
514 }
515
516 if (!see->evsent && edata->value_cur < see->critval) {
517 see->evsent = true;
518 sysmon_penvsys_event(&see->pes,
519 PENVSYS_EVENT_BATT_USERCAP);
520 }
521
522 break;
523 /* handle a max critical event */
524 case PENVSYS_EVENT_USER_CRITMAX:
525 if (see->evsent && edata->value_cur < see->critval) {
526 see->evsent = false;
527 sysmon_penvsys_event(&see->pes, PENVSYS_EVENT_NORMAL);
528 }
529
530 if (!see->evsent && edata->value_cur > see->critval) {
531 see->evsent = true;
532 sysmon_penvsys_event(&see->pes,
533 PENVSYS_EVENT_USER_CRITMAX);
534 }
535
536 break;
537 /* handle a min critical event */
538 case PENVSYS_EVENT_USER_CRITMIN:
539 if (see->evsent && edata->value_cur > see->critval) {
540 see->evsent = false;
541 sysmon_penvsys_event(&see->pes, PENVSYS_EVENT_NORMAL);
542 }
543
544 if (!see->evsent && edata->value_cur < see->critval) {
545 see->evsent = true;
546 sysmon_penvsys_event(&see->pes,
547 PENVSYS_EVENT_USER_CRITMIN);
548 }
549
550 break;
551 /* handle a drive state change event */
552 case PENVSYS_EVENT_DRIVE_STCHANGED:
553 /* the state has not been changed, just ignore the event */
554 if (edata->value_cur == see->evsent)
555 break;
556
557 for (i = 0; esds[i].type != -1; i++)
558 if (esds[i].type == edata->value_cur)
559 break;
560
561 /* copy current state description */
562 (void)strlcpy(see->pes.pes_statedesc, esds[i].desc,
563 sizeof(see->pes.pes_statedesc));
564
565 /* state is ok again... send a normal event */
566 if (see->evsent && edata->value_cur == ENVSYS_DRIVE_ONLINE) {
567 see->evsent = false;
568 sysmon_penvsys_event(&see->pes, PENVSYS_EVENT_NORMAL);
569 }
570
571 /* something bad happened to the drive... send the event */
572 if (see->evsent || edata->value_cur != ENVSYS_DRIVE_ONLINE) {
573 /* save current drive state */
574 see->evsent = edata->value_cur;
575 sysmon_penvsys_event(&see->pes,
576 PENVSYS_EVENT_DRIVE_STCHANGED);
577 }
578
579 break;
580 default:
581 break;
582 }
583 /* XXX: NOT YET mutex_exit(&sme_event_mtx); */
584 }
585