kernel - unwind kthread_create() mplock
[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  * Copyright (c) 2005 David Gwynne <dlg@openbsd.org>
5  * Copyright (c) 2006 Constantine A. Murenin <cnst+openbsd@bugmail.mojo.ru>
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19
20 #include <sys/param.h>
21 #include <sys/systm.h>
22 #include <sys/kernel.h>
23 #include <sys/malloc.h>
24 #include <sys/kthread.h>
25 #include <sys/queue.h>
26 #include <sys/types.h>
27 #include <sys/time.h>
28 #include <sys/lock.h>
29
30 #include <sys/sysctl.h>
31 #include <sys/sensors.h>
32
33 #include <sys/mplock2.h>
34
35 int                     sensordev_count = 0;
36 SLIST_HEAD(, ksensordev) sensordev_list = SLIST_HEAD_INITIALIZER(sensordev_list);
37
38 struct ksensordev       *sensordev_get(int);
39 struct ksensor          *sensor_find(struct ksensordev *, enum sensor_type, int);
40
41 struct sensor_task {
42         void                            *arg;
43         void                            (*func)(void *);
44
45         int                             period;
46         time_t                          nextrun;
47         volatile int                    running;
48         TAILQ_ENTRY(sensor_task)        entry;
49 };
50
51 void    sensor_task_thread(void *);
52 void    sensor_task_schedule(struct sensor_task *);
53
54 TAILQ_HEAD(, sensor_task) tasklist = TAILQ_HEAD_INITIALIZER(tasklist);
55
56 #ifndef NOSYSCTL8HACK
57 void    sensor_sysctl8magic_install(struct ksensordev *);
58 void    sensor_sysctl8magic_deinstall(struct ksensordev *);
59 #endif
60
61 void
62 sensordev_install(struct ksensordev *sensdev)
63 {
64         struct ksensordev *v, *nv;
65
66         /* mtx_lock(&Giant); */
67         if (sensordev_count == 0) {
68                 sensdev->num = 0;
69                 SLIST_INSERT_HEAD(&sensordev_list, sensdev, list);
70         } else {
71                 for (v = SLIST_FIRST(&sensordev_list);
72                     (nv = SLIST_NEXT(v, list)) != NULL; v = nv)
73                         if (nv->num - v->num > 1)
74                                 break;
75                 sensdev->num = v->num + 1;
76                 SLIST_INSERT_AFTER(v, sensdev, list);
77         }
78         sensordev_count++;
79         /* mtx_unlock(&Giant); */
80
81 #ifndef NOSYSCTL8HACK
82         sensor_sysctl8magic_install(sensdev);
83 #endif
84 }
85
86 void
87 sensor_attach(struct ksensordev *sensdev, struct ksensor *sens)
88 {
89         struct ksensor *v, *nv;
90         struct ksensors_head *sh;
91         int i;
92
93         /* mtx_lock(&Giant); */
94         sh = &sensdev->sensors_list;
95         if (sensdev->sensors_count == 0) {
96                 for (i = 0; i < SENSOR_MAX_TYPES; i++)
97                         sensdev->maxnumt[i] = 0;
98                 sens->numt = 0;
99                 SLIST_INSERT_HEAD(sh, sens, list);
100         } else {
101                 for (v = SLIST_FIRST(sh);
102                     (nv = SLIST_NEXT(v, list)) != NULL; v = nv)
103                         if (v->type == sens->type && (v->type != nv->type || 
104                             (v->type == nv->type && nv->numt - v->numt > 1)))
105                                 break;
106                 /* sensors of the same type go after each other */
107                 if (v->type == sens->type)
108                         sens->numt = v->numt + 1;
109                 else
110                         sens->numt = 0;
111                 SLIST_INSERT_AFTER(v, sens, list);
112         }
113         /* we only increment maxnumt[] if the sensor was added
114          * to the last position of sensors of this type
115          */
116         if (sensdev->maxnumt[sens->type] == sens->numt)
117                 sensdev->maxnumt[sens->type]++;
118         sensdev->sensors_count++;
119         /* mtx_unlock(&Giant); */
120 }
121
122 void
123 sensordev_deinstall(struct ksensordev *sensdev)
124 {
125         /* mtx_lock(&Giant); */
126         sensordev_count--;
127         SLIST_REMOVE(&sensordev_list, sensdev, ksensordev, list);
128         /* mtx_unlock(&Giant); */
129
130 #ifndef NOSYSCTL8HACK
131         sensor_sysctl8magic_deinstall(sensdev);
132 #endif
133 }
134
135 void
136 sensor_detach(struct ksensordev *sensdev, struct ksensor *sens)
137 {
138         struct ksensors_head *sh;
139
140         /* mtx_lock(&Giant); */
141         sh = &sensdev->sensors_list;
142         sensdev->sensors_count--;
143         SLIST_REMOVE(sh, sens, ksensor, list);
144         /* we only decrement maxnumt[] if this is the tail 
145          * sensor of this type
146          */
147         if (sens->numt == sensdev->maxnumt[sens->type] - 1)
148                 sensdev->maxnumt[sens->type]--;
149         /* mtx_unlock(&Giant); */
150 }
151
152 struct ksensordev *
153 sensordev_get(int num)
154 {
155         struct ksensordev *sd;
156
157         SLIST_FOREACH(sd, &sensordev_list, list)
158                 if (sd->num == num)
159                         return (sd);
160
161         return (NULL);
162 }
163
164 struct ksensor *
165 sensor_find(struct ksensordev *sensdev, enum sensor_type type, int numt)
166 {
167         struct ksensor *s;
168         struct ksensors_head *sh;
169
170         sh = &sensdev->sensors_list;
171         SLIST_FOREACH(s, sh, list)
172                 if (s->type == type && s->numt == numt)
173                         return (s);
174
175         return (NULL);
176 }
177
178 int
179 sensor_task_register(void *arg, void (*func)(void *), int period)
180 {
181         struct sensor_task      *st;
182         int                      create_thread = 0;
183
184         st = kmalloc(sizeof(struct sensor_task), M_DEVBUF, M_NOWAIT);
185         if (st == NULL)
186                 return (1);
187
188         st->arg = arg;
189         st->func = func;
190         st->period = period;
191
192         st->running = 1;
193
194         if (TAILQ_EMPTY(&tasklist))
195                 create_thread = 1;
196
197         st->nextrun = 0;
198         TAILQ_INSERT_HEAD(&tasklist, st, entry);
199
200         if (create_thread)
201                 if (kthread_create(sensor_task_thread, NULL, NULL,
202                     "sensors") != 0)
203                         panic("sensors kthread");
204         
205         wakeup(&tasklist);
206
207         return (0);
208 }
209
210 void
211 sensor_task_unregister(void *arg)
212 {
213         struct sensor_task      *st;
214
215         TAILQ_FOREACH(st, &tasklist, entry)
216                 if (st->arg == arg)
217                         st->running = 0;
218 }
219
220 void
221 sensor_task_thread(void *arg)
222 {
223         struct sensor_task      *st, *nst;
224         time_t                  now;
225
226         get_mplock();
227
228         while (!TAILQ_EMPTY(&tasklist)) {
229                 while ((nst = TAILQ_FIRST(&tasklist))->nextrun >
230                     (now = time_second))
231                         tsleep(&tasklist, 0, "timeout",
232                                (nst->nextrun - now) * hz);
233
234                 while ((st = nst) != NULL) {
235                         nst = TAILQ_NEXT(st, entry);
236
237                         if (st->nextrun > now)
238                                 break;
239
240                         /* take it out while we work on it */
241                         TAILQ_REMOVE(&tasklist, st, entry);
242
243                         if (!st->running) {
244                                 kfree(st, M_DEVBUF);
245                                 continue;
246                         }
247
248                         /* run the task */
249                         st->func(st->arg);
250                         /* stick it back in the tasklist */
251                         sensor_task_schedule(st);
252                 }
253         }
254
255         rel_mplock();
256 }
257
258 void
259 sensor_task_schedule(struct sensor_task *st)
260 {
261         struct sensor_task      *cst;
262
263         st->nextrun = time_second + st->period;
264
265         TAILQ_FOREACH(cst, &tasklist, entry) {
266                 if (cst->nextrun > st->nextrun) {
267                         TAILQ_INSERT_BEFORE(cst, st, entry);
268                         return;
269                 }
270         }
271
272         /* must be an empty list, or at the end of the list */
273         TAILQ_INSERT_TAIL(&tasklist, st, entry);
274 }
275
276 /*
277  * sysctl glue code
278  */
279 int sysctl_handle_sensordev(SYSCTL_HANDLER_ARGS);
280 int sysctl_handle_sensor(SYSCTL_HANDLER_ARGS);
281 int sysctl_sensors_handler(SYSCTL_HANDLER_ARGS);
282
283 #ifndef NOSYSCTL8HACK
284
285 SYSCTL_NODE(_hw, OID_AUTO, sensors, CTLFLAG_RD, NULL,
286     "Hardware Sensors sysctl internal magic");
287 SYSCTL_NODE(_hw, HW_SENSORS, _sensors, CTLFLAG_RD, sysctl_sensors_handler,
288     "Hardware Sensors XP MIB interface");
289
290 #else /* NOSYSCTL8HACK */
291
292 SYSCTL_NODE(_hw, HW_SENSORS, sensors, CTLFLAG_RD, sysctl_sensors_handler,
293     "Hardware Sensors");
294 int sensors_debug = 1;
295 SYSCTL_INT(_hw_sensors, OID_AUTO, debug, CTLFLAG_RD, &sensors_debug, 0, "sensors debug");
296
297 #endif /* !NOSYSCTL8HACK */
298
299
300 #ifndef NOSYSCTL8HACK
301
302 /*
303  * XXX:
304  * FreeBSD's sysctl(9) .oid_handler functionality is not accustomed
305  * for the CTLTYPE_NODE handler to handle the undocumented sysctl
306  * magic calls.  As soon as such functionality is developed, 
307  * sysctl_sensors_handler() should be converted to handle all such
308  * calls, and these sysctl_add_oid(9) calls should be removed 
309  * "with a big axe".  This whole sysctl_add_oid(9) business is solely
310  * to please sysctl(8).
311  */
312
313 void
314 sensor_sysctl8magic_install(struct ksensordev *sensdev)
315 {
316         struct sysctl_oid_list *ol;     
317         struct sysctl_ctx_list *cl = &sensdev->clist;
318         struct ksensor *s;
319         struct ksensors_head *sh = &sensdev->sensors_list;
320
321         sysctl_ctx_init(cl);
322         ol = SYSCTL_CHILDREN(SYSCTL_ADD_NODE(cl, (&SYSCTL_NODE_CHILDREN(_hw,
323             sensors)), sensdev->num, sensdev->xname, CTLFLAG_RD, NULL, ""));
324         SLIST_FOREACH(s, sh, list) {
325                 char n[32];
326
327                 ksnprintf(n, sizeof(n), "%s%d", sensor_type_s[s->type], s->numt);
328                 SYSCTL_ADD_PROC(cl, ol, OID_AUTO, n, CTLTYPE_STRUCT |
329                     CTLFLAG_RD, s, 0, sysctl_handle_sensor, "S,sensor", "");
330         }
331 }
332
333 void
334 sensor_sysctl8magic_deinstall(struct ksensordev *sensdev)
335 {
336         struct sysctl_ctx_list *cl = &sensdev->clist;
337
338         sysctl_ctx_free(cl);
339 }
340
341 #endif /* !NOSYSCTL8HACK */
342
343
344 int
345 sysctl_handle_sensordev(SYSCTL_HANDLER_ARGS)
346 {
347         struct ksensordev *ksd = arg1;
348         struct sensordev *usd;
349         int error;
350
351         if (req->newptr)
352                 return (EPERM);
353
354         /* Grab a copy, to clear the kernel pointers */
355         usd = kmalloc(sizeof(*usd), M_TEMP, M_WAITOK | M_ZERO);
356         usd->num = ksd->num;
357         strlcpy(usd->xname, ksd->xname, sizeof(usd->xname));
358         memcpy(usd->maxnumt, ksd->maxnumt, sizeof(usd->maxnumt));
359         usd->sensors_count = ksd->sensors_count;
360
361         error = SYSCTL_OUT(req, usd, sizeof(struct sensordev));
362
363         kfree(usd, M_TEMP);
364         return (error);
365
366 }
367
368 int
369 sysctl_handle_sensor(SYSCTL_HANDLER_ARGS)
370 {
371         struct ksensor *ks = arg1;
372         struct sensor *us;
373         int error;
374
375         if (req->newptr)
376                 return (EPERM);
377
378         /* Grab a copy, to clear the kernel pointers */
379         us = kmalloc(sizeof(*us), M_TEMP, M_WAITOK | M_ZERO);
380         memcpy(us->desc, ks->desc, sizeof(ks->desc));
381         us->tv = ks->tv;
382         us->value = ks->value;
383         us->type = ks->type;
384         us->status = ks->status;
385         us->numt = ks->numt;
386         us->flags = ks->flags;
387
388         error = SYSCTL_OUT(req, us, sizeof(struct sensor));
389
390         kfree(us, M_TEMP);
391         return (error);
392 }
393
394 int
395 sysctl_sensors_handler(SYSCTL_HANDLER_ARGS)
396 {
397         int *name = arg1;
398         u_int namelen = arg2;
399         struct ksensordev *ksd;
400         struct ksensor *ks;
401         int dev, numt;
402         enum sensor_type type;
403
404         if (namelen != 1 && namelen != 3)
405                 return (ENOTDIR);
406
407         dev = name[0];
408         if ((ksd = sensordev_get(dev)) == NULL)
409                 return (ENOENT);
410
411         if (namelen == 1)
412                 return (sysctl_handle_sensordev(NULL, ksd, 0, req));
413
414         type = name[1];
415         numt = name[2];
416
417         if ((ks = sensor_find(ksd, type, numt)) == NULL)
418                 return (ENOENT);
419         return (sysctl_handle_sensor(NULL, ks, 0, req));
420 }