Project

General

Profile

Statistics
| Branch: | Revision:

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
}