kernel: Simplify various redundant conditions.
[dragonfly.git] / sys / kern / kern_sensors.c
1 /* $OpenBSD: kern_sensors.c,v 1.19 2007/06/04 18:42:05 deraadt Exp $ */
2
3 /*
4  * (MPSAFE)
5  *
6  * Copyright (c) 2005 David Gwynne <dlg@openbsd.org>
7  * Copyright (c) 2006 Constantine A. Murenin <cnst+openbsd@bugmail.mojo.ru>
8  *
9  * Permission to use, copy, modify, and distribute this software for any
10  * purpose with or without fee is hereby granted, provided that the above
11  * copyright notice and this permission notice appear in all copies.
12  *
13  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
14  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
16  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
19  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20  */
21
22 #include <sys/param.h>
23 #include <sys/systm.h>
24 #include <sys/kernel.h>
25 #include <sys/malloc.h>
26 #include <sys/kthread.h>
27 #include <sys/queue.h>
28 #include <sys/types.h>
29 #include <sys/time.h>
30 #include <sys/spinlock.h>
31 #include <sys/spinlock2.h>
32 #include <sys/lock.h>
33 #include <sys/cpu_topology.h>
34
35 #include <sys/sysctl.h>
36 #include <sys/sensors.h>
37
38 static int              sensordev_idmax;
39 static TAILQ_HEAD(sensordev_list, ksensordev) sensordev_list =
40     TAILQ_HEAD_INITIALIZER(sensordev_list);
41
42 static struct ksensordev *sensordev_get(int);
43 static struct ksensor   *sensor_find(struct ksensordev *, enum sensor_type,
44                             int);
45
46 struct sensor_task {
47         void                            *arg;
48         void                            (*func)(void *);
49
50         int                             period;
51         time_t                          nextrun;        /* time_uptime */
52         int                             running;
53         int                             cpuid;
54         TAILQ_ENTRY(sensor_task)        entry;
55 };
56 TAILQ_HEAD(sensor_tasklist, sensor_task);
57
58 struct sensor_taskthr {
59         struct sensor_tasklist          list;
60         struct lock                     lock;
61 };
62
63 static void             sensor_task_thread(void *);
64 static void             sensor_task_schedule(struct sensor_taskthr *,
65                             struct sensor_task *);
66
67 static void             sensordev_sysctl_install(struct ksensordev *);
68 static void             sensordev_sysctl_deinstall(struct ksensordev *);
69 static void             sensor_sysctl_install(struct ksensordev *,
70                             struct ksensor *);
71 static void             sensor_sysctl_deinstall(struct ksensordev *,
72                             struct ksensor *);
73
74 static struct sensor_taskthr sensor_task_threads[MAXCPU];
75 static int              sensor_task_default_cpu;
76
77 void
78 sensordev_install(struct ksensordev *sensdev)
79 {
80         struct ksensordev *v, *after = NULL;
81         int num = 0;
82
83         SYSCTL_XLOCK();
84
85         TAILQ_FOREACH(v, &sensordev_list, list) {
86                 if (v->num == num) {
87                         ++num;
88                         after = v;
89                 } else if (v->num > num) {
90                         break;
91                 }
92         }
93
94         sensdev->num = num;
95         if (after == NULL) {
96                 KKASSERT(sensdev->num == 0);
97                 TAILQ_INSERT_HEAD(&sensordev_list, sensdev, list);
98         } else {
99                 TAILQ_INSERT_AFTER(&sensordev_list, after, sensdev, list);
100         }
101
102         /* Save max sensor device id */
103         sensordev_idmax = TAILQ_LAST(&sensordev_list, sensordev_list)->num + 1;
104
105         /* Install sysctl node for this sensor device */
106         sensordev_sysctl_install(sensdev);
107
108         SYSCTL_XUNLOCK();
109 }
110
111 void
112 sensor_attach(struct ksensordev *sensdev, struct ksensor *sens)
113 {
114         struct ksensor *v, *nv;
115         struct ksensors_head *sh;
116         int i;
117
118         SYSCTL_XLOCK();
119
120         sh = &sensdev->sensors_list;
121         if (sensdev->sensors_count == 0) {
122                 for (i = 0; i < SENSOR_MAX_TYPES; i++)
123                         sensdev->maxnumt[i] = 0;
124                 sens->numt = 0;
125                 SLIST_INSERT_HEAD(sh, sens, list);
126         } else {
127                 for (v = SLIST_FIRST(sh);
128                     (nv = SLIST_NEXT(v, list)) != NULL; v = nv)
129                         if (v->type == sens->type &&
130                             (v->type != nv->type || nv->numt - v->numt > 1))
131                                 break;
132                 /* sensors of the same type go after each other */
133                 if (v->type == sens->type)
134                         sens->numt = v->numt + 1;
135                 else
136                         sens->numt = 0;
137                 SLIST_INSERT_AFTER(v, sens, list);
138         }
139         /*
140          * We only increment maxnumt[] if the sensor was added
141          * to the last position of sensors of this type
142          */
143         if (sensdev->maxnumt[sens->type] == sens->numt)
144                 sensdev->maxnumt[sens->type]++;
145         sensdev->sensors_count++;
146
147         /* Install sysctl node for this sensor */
148         sensor_sysctl_install(sensdev, sens);
149
150         SYSCTL_XUNLOCK();
151 }
152
153 void
154 sensordev_deinstall(struct ksensordev *sensdev)
155 {
156         struct ksensordev *last;
157
158         SYSCTL_XLOCK();
159
160         TAILQ_REMOVE(&sensordev_list, sensdev, list);
161
162         /* Adjust max sensor device id */
163         last = TAILQ_LAST(&sensordev_list, sensordev_list);
164         if (last != NULL)
165                 sensordev_idmax = last->num + 1;
166         else
167                 sensordev_idmax = 0;
168
169         /*
170          * Deinstall sensor device's sysctl node; this also
171          * removes all attached sensors' sysctl nodes.
172          */
173         sensordev_sysctl_deinstall(sensdev);
174
175         SYSCTL_XUNLOCK();
176 }
177
178 void
179 sensor_detach(struct ksensordev *sensdev, struct ksensor *sens)
180 {
181         struct ksensors_head *sh;
182
183         SYSCTL_XLOCK();
184
185         sh = &sensdev->sensors_list;
186         sensdev->sensors_count--;
187         SLIST_REMOVE(sh, sens, ksensor, list);
188         /*
189          * We only decrement maxnumt[] if this is the tail 
190          * sensor of this type
191          */
192         if (sens->numt == sensdev->maxnumt[sens->type] - 1)
193                 sensdev->maxnumt[sens->type]--;
194
195         /* Deinstall sensor's sysctl node */
196         sensor_sysctl_deinstall(sensdev, sens);
197
198         SYSCTL_XUNLOCK();
199 }
200
201 static struct ksensordev *
202 sensordev_get(int num)
203 {
204         struct ksensordev *sd;
205
206         SYSCTL_ASSERT_LOCKED();
207
208         TAILQ_FOREACH(sd, &sensordev_list, list) {
209                 if (sd->num == num)
210                         return (sd);
211         }
212         return (NULL);
213 }
214
215 static struct ksensor *
216 sensor_find(struct ksensordev *sensdev, enum sensor_type type, int numt)
217 {
218         struct ksensor *s;
219         struct ksensors_head *sh;
220
221         SYSCTL_ASSERT_LOCKED();
222
223         sh = &sensdev->sensors_list;
224         SLIST_FOREACH(s, sh, list) {
225                 if (s->type == type && s->numt == numt)
226                         return (s);
227         }
228         return (NULL);
229 }
230
231 void
232 sensor_task_register(void *arg, void (*func)(void *), int period)
233 {
234         sensor_task_register2(arg, func, period, -1);
235 }
236
237 void
238 sensor_task_unregister(void *arg)
239 {
240         struct sensor_taskthr   *thr;
241         struct sensor_task      *st;
242
243         thr = &sensor_task_threads[sensor_task_default_cpu];
244         lockmgr(&thr->lock, LK_EXCLUSIVE);
245         TAILQ_FOREACH(st, &thr->list, entry)
246                 if (st->arg == arg)
247                         st->running = 0;
248         lockmgr(&thr->lock, LK_RELEASE);
249 }
250
251 void
252 sensor_task_unregister2(struct sensor_task *st)
253 {
254         struct sensor_taskthr *thr;
255
256         KASSERT(st->cpuid >= 0 && st->cpuid < ncpus,
257             ("invalid task cpuid %d", st->cpuid));
258         thr = &sensor_task_threads[st->cpuid];
259
260         /*
261          * Hold the lock then zero-out running, so upon returning
262          * to the caller, the task will no longer run.
263          */
264         lockmgr(&thr->lock, LK_EXCLUSIVE);
265         st->running = 0;
266         lockmgr(&thr->lock, LK_RELEASE);
267 }
268
269 struct sensor_task *
270 sensor_task_register2(void *arg, void (*func)(void *), int period, int cpu)
271 {
272         struct sensor_taskthr   *thr;
273         struct sensor_task      *st;
274
275         if (cpu < 0)
276                 cpu = sensor_task_default_cpu;
277         KASSERT(cpu >= 0 && cpu < ncpus, ("invalid cpuid %d", cpu));
278         thr = &sensor_task_threads[cpu];
279
280         st = kmalloc(sizeof(struct sensor_task), M_DEVBUF, M_WAITOK);
281
282         lockmgr(&thr->lock, LK_EXCLUSIVE);
283         st->arg = arg;
284         st->func = func;
285         st->period = period;
286         st->cpuid = cpu;
287
288         st->running = 1;
289
290         st->nextrun = 0;
291         TAILQ_INSERT_HEAD(&thr->list, st, entry);
292
293         wakeup(&thr->list);
294
295         lockmgr(&thr->lock, LK_RELEASE);
296
297         return st;
298 }
299
300 static void
301 sensor_task_thread(void *xthr)
302 {
303         struct sensor_taskthr   *thr = xthr;
304         struct sensor_task      *st, *nst;
305         time_t                  now;
306
307         lockmgr(&thr->lock, LK_EXCLUSIVE);
308
309         for (;;) {
310                 while (TAILQ_EMPTY(&thr->list))
311                         lksleep(&thr->list, &thr->lock, 0, "waittask", 0);
312
313                 while ((nst = TAILQ_FIRST(&thr->list))->nextrun >
314                     (now = time_uptime)) {
315                         lksleep(&thr->list, &thr->lock, 0,
316                             "timeout", (nst->nextrun - now) * hz);
317                 }
318
319                 while ((st = nst) != NULL) {
320                         nst = TAILQ_NEXT(st, entry);
321
322                         if (st->nextrun > now)
323                                 break;
324
325                         /* take it out while we work on it */
326                         TAILQ_REMOVE(&thr->list, st, entry);
327
328                         if (!st->running) {
329                                 kfree(st, M_DEVBUF);
330                                 continue;
331                         }
332
333                         /* run the task */
334                         st->func(st->arg);
335                         /* stick it back in the tasklist */
336                         sensor_task_schedule(thr, st);
337                 }
338         }
339
340         lockmgr(&thr->lock, LK_RELEASE);
341 }
342
343 static void
344 sensor_task_schedule(struct sensor_taskthr *thr, struct sensor_task *st)
345 {
346         struct sensor_task      *cst;
347
348         KASSERT(lockstatus(&thr->lock, curthread) == LK_EXCLUSIVE,
349             ("sensor task lock is not held"));
350
351         st->nextrun = time_uptime + st->period;
352
353         TAILQ_FOREACH(cst, &thr->list, entry) {
354                 if (cst->nextrun > st->nextrun) {
355                         TAILQ_INSERT_BEFORE(cst, st, entry);
356                         return;
357                 }
358         }
359
360         /* must be an empty list, or at the end of the list */
361         TAILQ_INSERT_TAIL(&thr->list, st, entry);
362 }
363
364 /*
365  * sysctl glue code
366  */
367 static int      sysctl_handle_sensordev(SYSCTL_HANDLER_ARGS);
368 static int      sysctl_handle_sensor(SYSCTL_HANDLER_ARGS);
369 static int      sysctl_sensors_handler(SYSCTL_HANDLER_ARGS);
370
371 SYSCTL_NODE(_hw, OID_AUTO, sensors, CTLFLAG_RD, NULL,
372     "Hardware Sensors sysctl internal magic");
373 SYSCTL_NODE(_hw, HW_SENSORS, _sensors, CTLFLAG_RD, sysctl_sensors_handler,
374     "Hardware Sensors XP MIB interface");
375
376 SYSCTL_INT(_hw_sensors, OID_AUTO, dev_idmax, CTLFLAG_RD,
377     &sensordev_idmax, 0, "Max sensor device id");
378
379 static void
380 sensordev_sysctl_install(struct ksensordev *sensdev)
381 {
382         struct sysctl_ctx_list *cl = &sensdev->clist;
383         struct ksensor *s;
384         struct ksensors_head *sh = &sensdev->sensors_list;
385
386         SYSCTL_ASSERT_LOCKED();
387
388         KASSERT(sensdev->oid == NULL,
389             ("sensor device %s sysctl node already installed", sensdev->xname));
390
391         sysctl_ctx_init(cl);
392         sensdev->oid = SYSCTL_ADD_NODE(cl, SYSCTL_STATIC_CHILDREN(_hw_sensors),
393             sensdev->num, sensdev->xname, CTLFLAG_RD, NULL, "");
394         if (sensdev->oid == NULL) {
395                 kprintf("sensor: add sysctl tree for %s failed\n",
396                     sensdev->xname);
397                 return;
398         }
399
400         /* Install sysctl nodes for sensors attached to this sensor device */
401         SLIST_FOREACH(s, sh, list)
402                 sensor_sysctl_install(sensdev, s);
403 }
404
405 static void
406 sensor_sysctl_install(struct ksensordev *sensdev, struct ksensor *sens)
407 {
408         char n[32];
409
410         SYSCTL_ASSERT_LOCKED();
411
412         if (sensdev->oid == NULL) {
413                 /* Sensor device sysctl node is not installed yet */
414                 return;
415         }
416
417         ksnprintf(n, sizeof(n), "%s%d", sensor_type_s[sens->type], sens->numt);
418         KASSERT(sens->oid == NULL,
419             ("sensor %s:%s sysctl node already installed", sensdev->xname, n));
420
421         sens->oid = SYSCTL_ADD_PROC(&sensdev->clist,
422             SYSCTL_CHILDREN(sensdev->oid), OID_AUTO, n,
423             CTLTYPE_STRUCT | CTLFLAG_RD, sens, 0, sysctl_handle_sensor,
424             "S,sensor", "");
425 }
426
427 static void
428 sensordev_sysctl_deinstall(struct ksensordev *sensdev)
429 {
430         SYSCTL_ASSERT_LOCKED();
431
432         if (sensdev->oid != NULL) {
433                 sysctl_ctx_free(&sensdev->clist);
434                 sensdev->oid = NULL;
435         }
436 }
437
438 static void
439 sensor_sysctl_deinstall(struct ksensordev *sensdev, struct ksensor *sens)
440 {
441         SYSCTL_ASSERT_LOCKED();
442
443         if (sensdev->oid != NULL && sens->oid != NULL) {
444                 sysctl_ctx_entry_del(&sensdev->clist, sens->oid);
445                 sysctl_remove_oid(sens->oid, 1, 0);
446         }
447         sens->oid = NULL;
448 }
449
450 static int
451 sysctl_handle_sensordev(SYSCTL_HANDLER_ARGS)
452 {
453         struct ksensordev *ksd = arg1;
454         struct sensordev *usd;
455         int error;
456
457         if (req->newptr)
458                 return (EPERM);
459
460         /* Grab a copy, to clear the kernel pointers */
461         usd = kmalloc(sizeof(*usd), M_TEMP, M_WAITOK | M_ZERO);
462         usd->num = ksd->num;
463         strlcpy(usd->xname, ksd->xname, sizeof(usd->xname));
464         memcpy(usd->maxnumt, ksd->maxnumt, sizeof(usd->maxnumt));
465         usd->sensors_count = ksd->sensors_count;
466
467         error = SYSCTL_OUT(req, usd, sizeof(struct sensordev));
468
469         kfree(usd, M_TEMP);
470         return (error);
471 }
472
473 static int
474 sysctl_handle_sensor(SYSCTL_HANDLER_ARGS)
475 {
476         struct ksensor *ks = arg1;
477         struct sensor *us;
478         int error;
479
480         if (req->newptr)
481                 return (EPERM);
482
483         /* Grab a copy, to clear the kernel pointers */
484         us = kmalloc(sizeof(*us), M_TEMP, M_WAITOK | M_ZERO);
485         memcpy(us->desc, ks->desc, sizeof(ks->desc));
486         us->tv = ks->tv;
487         us->value = ks->value;
488         us->type = ks->type;
489         us->status = ks->status;
490         us->numt = ks->numt;
491         us->flags = ks->flags;
492
493         error = SYSCTL_OUT(req, us, sizeof(struct sensor));
494
495         kfree(us, M_TEMP);
496         return (error);
497 }
498
499 static int
500 sysctl_sensors_handler(SYSCTL_HANDLER_ARGS)
501 {
502         int *name = arg1;
503         u_int namelen = arg2;
504         struct ksensordev *ksd;
505         struct ksensor *ks;
506         int dev, numt;
507         enum sensor_type type;
508
509         if (namelen != 1 && namelen != 3)
510                 return (ENOTDIR);
511
512         dev = name[0];
513         if ((ksd = sensordev_get(dev)) == NULL)
514                 return (ENOENT);
515
516         if (namelen == 1)
517                 return (sysctl_handle_sensordev(NULL, ksd, 0, req));
518
519         type = name[1];
520         numt = name[2];
521
522         if ((ks = sensor_find(ksd, type, numt)) == NULL)
523                 return (ENOENT);
524         return (sysctl_handle_sensor(NULL, ks, 0, req));
525 }
526
527 static void
528 sensor_sysinit(void *arg __unused)
529 {
530         const cpu_node_t *node;
531         int cpu;
532
533         /*
534          * By default, stick sensor tasks to the cpu belonging to
535          * the first cpu package, since most of the time accessing
536          * sensor devices from the first cpu package will be faster,
537          * e.g. through DMI or DMI2 on Intel CPUs; no QPI will be
538          * generated.
539          */
540         node = get_cpu_node_by_chipid(0);
541         if (node != NULL && node->child_no > 0)
542                 sensor_task_default_cpu = BSRCPUMASK(node->members);
543         else
544                 sensor_task_default_cpu = ncpus - 1;
545         if (bootverbose) {
546                 kprintf("sensors: tasks default to cpu%d\n",
547                     sensor_task_default_cpu);
548         }
549
550         for (cpu = 0; cpu < ncpus; ++cpu) {
551                 struct sensor_taskthr *thr = &sensor_task_threads[cpu];
552                 int error;
553
554                 TAILQ_INIT(&thr->list);
555                 lockinit(&thr->lock, "sensorthr", 0, LK_CANRECURSE);
556
557                 error = kthread_create_cpu(sensor_task_thread, thr, NULL, cpu,
558                     "sensors %d", cpu);
559                 if (error)
560                         panic("sensors kthread on cpu%d failed: %d", cpu, error);
561         }
562 }
563 SYSINIT(sensor, SI_SUB_PRE_DRIVERS, SI_ORDER_ANY, sensor_sysinit, NULL);