scoutos / prex-0.9.0 / sys / kern / timer.c @ 03e9c04a
History | View | Annotate | Download (9.38 KB)
1 |
/*-
|
---|---|
2 |
* Copyright (c) 2005-2009, Kohsuke Ohtani
|
3 |
* All rights reserved.
|
4 |
*
|
5 |
* Redistribution and use in source and binary forms, with or without
|
6 |
* modification, are permitted provided that the following conditions
|
7 |
* are met:
|
8 |
* 1. Redistributions of source code must retain the above copyright
|
9 |
* notice, this list of conditions and the following disclaimer.
|
10 |
* 2. Redistributions in binary form must reproduce the above copyright
|
11 |
* notice, this list of conditions and the following disclaimer in the
|
12 |
* documentation and/or other materials provided with the distribution.
|
13 |
* 3. Neither the name of the author nor the names of any co-contributors
|
14 |
* may be used to endorse or promote products derived from this software
|
15 |
* without specific prior written permission.
|
16 |
*
|
17 |
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
18 |
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
19 |
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
20 |
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
21 |
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
22 |
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
23 |
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
24 |
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
25 |
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
26 |
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
27 |
* SUCH DAMAGE.
|
28 |
*/
|
29 |
|
30 |
/*
|
31 |
* timer.c - kernel timer services.
|
32 |
*/
|
33 |
|
34 |
#include <kernel.h> |
35 |
#include <task.h> |
36 |
#include <event.h> |
37 |
#include <sched.h> |
38 |
#include <thread.h> |
39 |
#include <kmem.h> |
40 |
#include <exception.h> |
41 |
#include <timer.h> |
42 |
#include <sys/signal.h> |
43 |
|
44 |
static volatile u_long lbolt; /* ticks elapsed since bootup */ |
45 |
static volatile u_long idle_ticks; /* total ticks for idle */ |
46 |
|
47 |
static struct event timer_event; /* event to wakeup a timer thread */ |
48 |
static struct event delay_event; /* event for the thread delay */ |
49 |
static struct list timer_list; /* list of active timers */ |
50 |
static struct list expire_list; /* list of expired timers */ |
51 |
|
52 |
/*
|
53 |
* Get remaining ticks to the expiration time.
|
54 |
* Return 0 if timer has been expired.
|
55 |
*/
|
56 |
static u_long
|
57 |
time_remain(u_long expire) |
58 |
{ |
59 |
|
60 |
if (time_before(lbolt, expire))
|
61 |
return expire - lbolt;
|
62 |
return 0; |
63 |
} |
64 |
|
65 |
/*
|
66 |
* Activate a timer.
|
67 |
*/
|
68 |
static void |
69 |
timer_add(struct timer *tmr, u_long ticks)
|
70 |
{ |
71 |
list_t head, n; |
72 |
struct timer *t;
|
73 |
|
74 |
if (ticks == 0) |
75 |
ticks++; |
76 |
|
77 |
tmr->expire = lbolt + ticks; |
78 |
tmr->state = TM_ACTIVE; |
79 |
|
80 |
/*
|
81 |
* Insert a timer element into the timer list which
|
82 |
* is sorted by expiration time.
|
83 |
*/
|
84 |
head = &timer_list; |
85 |
for (n = list_first(head); n != head; n = list_next(n)) {
|
86 |
t = list_entry(n, struct timer, link);
|
87 |
if (time_before(tmr->expire, t->expire))
|
88 |
break;
|
89 |
} |
90 |
list_insert(list_prev(n), &tmr->link); |
91 |
} |
92 |
|
93 |
/*
|
94 |
* Stop an active timer.
|
95 |
*/
|
96 |
void
|
97 |
timer_stop(struct timer *tmr)
|
98 |
{ |
99 |
int s;
|
100 |
|
101 |
ASSERT(tmr != NULL);
|
102 |
|
103 |
s = splhigh(); |
104 |
if (tmr->state == TM_ACTIVE) {
|
105 |
list_remove(&tmr->link); |
106 |
tmr->state = TM_STOP; |
107 |
} |
108 |
splx(s); |
109 |
} |
110 |
|
111 |
/*
|
112 |
* Schedule a callout function to run after a specified
|
113 |
* length of time.
|
114 |
*
|
115 |
* Note: A device driver can call timer_callout() or
|
116 |
* timer_stop() from ISR at interrupt level.
|
117 |
*/
|
118 |
void
|
119 |
timer_callout(struct timer *tmr, u_long msec, void (*fn)(void *), void *arg) |
120 |
{ |
121 |
int s;
|
122 |
|
123 |
ASSERT(tmr != NULL);
|
124 |
ASSERT(fn != NULL);
|
125 |
|
126 |
s = splhigh(); |
127 |
|
128 |
if (tmr->state == TM_ACTIVE)
|
129 |
list_remove(&tmr->link); |
130 |
|
131 |
tmr->func = fn; |
132 |
tmr->arg = arg; |
133 |
tmr->interval = 0;
|
134 |
timer_add(tmr, mstohz(msec)); |
135 |
|
136 |
splx(s); |
137 |
} |
138 |
|
139 |
/*
|
140 |
* timer_delay - delay thread execution.
|
141 |
*
|
142 |
* The caller thread is blocked for the specified time.
|
143 |
* Returns 0 on success, or the remaining time (msec) on
|
144 |
* failure.
|
145 |
*/
|
146 |
u_long |
147 |
timer_delay(u_long msec) |
148 |
{ |
149 |
struct timer *tmr;
|
150 |
u_long remain = 0;
|
151 |
int rc;
|
152 |
|
153 |
rc = sched_tsleep(&delay_event, msec); |
154 |
if (rc != SLP_TIMEOUT) {
|
155 |
tmr = &curthread->timeout; |
156 |
remain = hztoms(time_remain(tmr->expire)); |
157 |
} |
158 |
return remain;
|
159 |
} |
160 |
|
161 |
/*
|
162 |
* timer_sleep - sleep system call.
|
163 |
*
|
164 |
* Stop execution of the current thread for the indicated amount
|
165 |
* of time. If the sleep is interrupted, the remaining time is
|
166 |
* set in "remain".
|
167 |
*/
|
168 |
int
|
169 |
timer_sleep(u_long msec, u_long *remain) |
170 |
{ |
171 |
u_long left; |
172 |
|
173 |
left = timer_delay(msec); |
174 |
|
175 |
if (remain != NULL) { |
176 |
if (copyout(&left, remain, sizeof(left))) |
177 |
return EFAULT;
|
178 |
} |
179 |
if (left > 0) |
180 |
return EINTR;
|
181 |
return 0; |
182 |
} |
183 |
|
184 |
/*
|
185 |
* Alarm timer expired:
|
186 |
* Send an alarm exception to the target task.
|
187 |
*/
|
188 |
static void |
189 |
alarm_expire(void *arg)
|
190 |
{ |
191 |
task_t task = (task_t)arg; |
192 |
|
193 |
exception_post(task, SIGALRM); |
194 |
} |
195 |
|
196 |
/*
|
197 |
* timer_alarm - alarm system call.
|
198 |
*
|
199 |
* SIGALRM exception is sent to the caller task when specified
|
200 |
* delay time is passed. If "msec" argument is 0, stop the
|
201 |
* current running timer.
|
202 |
*/
|
203 |
int
|
204 |
timer_alarm(u_long msec, u_long *remain) |
205 |
{ |
206 |
struct timer *tmr;
|
207 |
u_long left = 0;
|
208 |
int s;
|
209 |
|
210 |
s = splhigh(); |
211 |
tmr = &curtask->alarm; |
212 |
|
213 |
/*
|
214 |
* If the timer is active, save the remaining time
|
215 |
* before we update the timer setting.
|
216 |
*/
|
217 |
if (tmr->state == TM_ACTIVE)
|
218 |
left = hztoms(time_remain(tmr->expire)); |
219 |
|
220 |
if (msec == 0) |
221 |
timer_stop(tmr); |
222 |
else
|
223 |
timer_callout(tmr, msec, &alarm_expire, curtask); |
224 |
|
225 |
splx(s); |
226 |
if (remain != NULL) { |
227 |
if (copyout(&left, remain, sizeof(left))) |
228 |
return EFAULT;
|
229 |
} |
230 |
return 0; |
231 |
} |
232 |
|
233 |
/*
|
234 |
* timer_periodic - set periodic timer for the specified thread.
|
235 |
*
|
236 |
* The periodic thread can wait the timer period by calling
|
237 |
* timer_waitperiod(). The unit of start/period is milli-seconds.
|
238 |
*/
|
239 |
int
|
240 |
timer_periodic(thread_t t, u_long start, u_long period) |
241 |
{ |
242 |
struct timer *tmr;
|
243 |
int s;
|
244 |
|
245 |
if (start != 0 && period == 0) |
246 |
return EINVAL;
|
247 |
|
248 |
sched_lock(); |
249 |
if (!thread_valid(t)) {
|
250 |
sched_unlock(); |
251 |
return ESRCH;
|
252 |
} |
253 |
if (t->task != curtask) {
|
254 |
sched_unlock(); |
255 |
return EPERM;
|
256 |
} |
257 |
|
258 |
tmr = t->periodic; |
259 |
if (start == 0) { |
260 |
/*
|
261 |
* Cancel periodic timer.
|
262 |
*/
|
263 |
if (tmr == NULL || tmr->state != TM_ACTIVE) { |
264 |
sched_unlock(); |
265 |
return EINVAL;
|
266 |
} |
267 |
timer_stop(tmr); |
268 |
} else {
|
269 |
if (tmr == NULL) { |
270 |
/*
|
271 |
* Allocate a timer element at first call.
|
272 |
* This is to save the data area in the thread
|
273 |
* structure.
|
274 |
*/
|
275 |
if ((tmr = kmem_alloc(sizeof(tmr))) == NULL) { |
276 |
sched_unlock(); |
277 |
return ENOMEM;
|
278 |
} |
279 |
memset(tmr, 0, sizeof(*tmr)); |
280 |
event_init(&tmr->event, "periodic");
|
281 |
t->periodic = tmr; |
282 |
} |
283 |
/*
|
284 |
* Program an interval timer.
|
285 |
*/
|
286 |
s = splhigh(); |
287 |
tmr->interval = mstohz(period); |
288 |
if (tmr->interval == 0) |
289 |
tmr->interval = 1;
|
290 |
timer_add(tmr, mstohz(start)); |
291 |
splx(s); |
292 |
} |
293 |
sched_unlock(); |
294 |
return 0; |
295 |
} |
296 |
|
297 |
/*
|
298 |
* timer_waitperiod - wait next period of the periodic timer.
|
299 |
*
|
300 |
* If the caller task receives any exception, this system call
|
301 |
* will return before target time. So, the caller must retry
|
302 |
* immediately if the error status is EINTR. This will be
|
303 |
* automatically done by the library stub routine.
|
304 |
*/
|
305 |
int
|
306 |
timer_waitperiod(void)
|
307 |
{ |
308 |
struct timer *tmr;
|
309 |
int rc;
|
310 |
|
311 |
tmr = curthread->periodic; |
312 |
if (tmr == NULL || tmr->state != TM_ACTIVE) |
313 |
return EINVAL;
|
314 |
|
315 |
if (time_before(lbolt, tmr->expire)) {
|
316 |
/*
|
317 |
* Sleep until timer_handler() routine wakes us up.
|
318 |
*/
|
319 |
rc = sched_sleep(&tmr->event); |
320 |
if (rc != SLP_SUCCESS)
|
321 |
return EINTR;
|
322 |
} |
323 |
return 0; |
324 |
} |
325 |
|
326 |
/*
|
327 |
* Untimeout the timers for the thread termination.
|
328 |
*/
|
329 |
void
|
330 |
timer_cancel(thread_t t) |
331 |
{ |
332 |
|
333 |
if (t->periodic != NULL) { |
334 |
timer_stop(t->periodic); |
335 |
kmem_free(t->periodic); |
336 |
t->periodic = NULL;
|
337 |
} |
338 |
} |
339 |
|
340 |
/*
|
341 |
* Timer thread.
|
342 |
*
|
343 |
* Handle all expired timers. Each callout routine is
|
344 |
* called with scheduler locked and interrupts enabled.
|
345 |
*/
|
346 |
static void |
347 |
timer_thread(void *dummy)
|
348 |
{ |
349 |
struct timer *tmr;
|
350 |
|
351 |
splhigh(); |
352 |
|
353 |
for (;;) {
|
354 |
/*
|
355 |
* Wait until next timer expiration.
|
356 |
*/
|
357 |
sched_sleep(&timer_event); |
358 |
|
359 |
while (!list_empty(&expire_list)) {
|
360 |
/*
|
361 |
* callout
|
362 |
*/
|
363 |
tmr = timer_next(&expire_list); |
364 |
list_remove(&tmr->link); |
365 |
tmr->state = TM_STOP; |
366 |
sched_lock(); |
367 |
spl0(); |
368 |
(*tmr->func)(tmr->arg); |
369 |
|
370 |
/*
|
371 |
* Unlock scheduler here in order to give
|
372 |
* chance to higher priority threads to run.
|
373 |
*/
|
374 |
sched_unlock(); |
375 |
splhigh(); |
376 |
} |
377 |
} |
378 |
/* NOTREACHED */
|
379 |
} |
380 |
|
381 |
/*
|
382 |
* Handle clock interrupts.
|
383 |
*
|
384 |
* timer_handler() is called directly from the real time clock
|
385 |
* interrupt. All interrupts are still disabled at the entry
|
386 |
* of this routine.
|
387 |
*/
|
388 |
void
|
389 |
timer_handler(void)
|
390 |
{ |
391 |
struct timer *tmr;
|
392 |
u_long ticks; |
393 |
int wakeup = 0; |
394 |
|
395 |
/*
|
396 |
* Bump time in ticks.
|
397 |
* Note that it is allowed to wrap.
|
398 |
*/
|
399 |
lbolt++; |
400 |
if (curthread->priority == PRI_IDLE)
|
401 |
idle_ticks++; |
402 |
|
403 |
while (!list_empty(&timer_list)) {
|
404 |
/*
|
405 |
* Check timer expiration.
|
406 |
*/
|
407 |
tmr = timer_next(&timer_list); |
408 |
if (time_before(lbolt, tmr->expire))
|
409 |
break;
|
410 |
|
411 |
list_remove(&tmr->link); |
412 |
if (tmr->interval != 0) { |
413 |
/*
|
414 |
* Periodic timer - reprogram timer again.
|
415 |
*/
|
416 |
ticks = time_remain(tmr->expire + tmr->interval); |
417 |
timer_add(tmr, ticks); |
418 |
sched_wakeup(&tmr->event); |
419 |
} else {
|
420 |
/*
|
421 |
* One-shot timer
|
422 |
*/
|
423 |
list_insert(&expire_list, &tmr->link); |
424 |
wakeup = 1;
|
425 |
} |
426 |
} |
427 |
if (wakeup)
|
428 |
sched_wakeup(&timer_event); |
429 |
|
430 |
sched_tick(); |
431 |
} |
432 |
|
433 |
/*
|
434 |
* Return ticks since boot.
|
435 |
*/
|
436 |
u_long |
437 |
timer_ticks(void)
|
438 |
{ |
439 |
|
440 |
return lbolt;
|
441 |
} |
442 |
|
443 |
/*
|
444 |
* Return timer information.
|
445 |
*/
|
446 |
void
|
447 |
timer_info(struct timerinfo *info)
|
448 |
{ |
449 |
|
450 |
info->hz = HZ; |
451 |
info->cputicks = lbolt; |
452 |
info->idleticks = idle_ticks; |
453 |
} |
454 |
|
455 |
/*
|
456 |
* Initialize the timer facility, called at system startup time.
|
457 |
*/
|
458 |
void
|
459 |
timer_init(void)
|
460 |
{ |
461 |
|
462 |
event_init(&timer_event, "timer");
|
463 |
event_init(&delay_event, "delay");
|
464 |
list_init(&timer_list); |
465 |
list_init(&expire_list); |
466 |
|
467 |
if (kthread_create(&timer_thread, NULL, PRI_TIMER) == NULL) |
468 |
panic("timer_init");
|
469 |
} |