version 1.1.1.1, 2008/06/03 10:38:46 |
version 1.1.1.1.2.1, 2008/08/13 17:12:32 |
|
|
#include <kmem.h> |
#include <kmem.h> |
#include <exception.h> |
#include <exception.h> |
|
|
static volatile u_long lbolt; /* ticks elapsed since bootup */ |
static volatile u_long lbolt; /* ticks elapsed since bootup */ |
static struct event timer_event; /* event to wakeup timer thread */ |
|
static struct event delay_event; /* event for the delay thread */ |
|
static struct list timer_list; /* list of active timers */ |
|
static struct list expire_list; /* list of expired timers */ |
|
static void (*tick_hook)(int); /* hook routine for timer tick */ |
|
|
|
|
static struct event timer_event; /* event to wakeup a timer thread */ |
|
static struct event delay_event; /* event for the thread delay */ |
|
static struct list timer_list; /* list of active timers */ |
|
static struct list expire_list; /* list of expired timers */ |
|
|
|
static void (*volatile tick_hook)(int); /* hook routine for timer tick */ |
|
|
/* |
/* |
* Macro to get a timer element for the next timer expiration. |
* Macro to get a timer element for the next expiration. |
*/ |
*/ |
#define timer_next() \ |
#define timer_next() \ |
(list_entry(list_first(&timer_list), struct timer, link)) |
(list_entry(list_first(&timer_list), struct timer, link)) |
|
|
/* |
/* |
* Helper routine to get remaining ticks for the expiration time. |
* Get remaining ticks to the expiration time. |
* Return 0 if time already passed. |
* Return 0 if time already passed. |
*/ |
*/ |
static u_long |
static u_long |
|
|
} |
} |
|
|
/* |
/* |
* Add a timer element to the timer list, in the proper place. |
* Add a timer element to the timer list in the proper place. |
* Requires interrupts to be disabled by the caller. |
* Requires interrupts to be disabled by the caller. |
*/ |
*/ |
static void |
static void |
|
|
tmr->expire = lbolt + ticks; |
tmr->expire = lbolt + ticks; |
|
|
/* |
/* |
* We sort the timer list by time. So, we can quickly |
* We sort the timer list by time. So, we can |
* get the next expiration time from the head element |
* quickly get the next expiration time from |
* of the timer list. |
* the head element of the timer list. |
*/ |
*/ |
head = &timer_list; |
head = &timer_list; |
for (n = list_first(head); n != head; n = list_next(n)) { |
for (n = list_first(head); n != head; n = list_next(n)) { |
|
|
} |
} |
|
|
/* |
/* |
* Execute a function after a specified length of time. |
* Schedule a callout function to run after a specified |
* A device driver can call timer_callout() or timer_stop() |
* length of time. A device driver can call |
* from ISR at interrupt level. |
* timer_callout()/timer_stop() from ISR at interrupt level. |
*/ |
*/ |
void |
void |
timer_callout(struct timer *tmr, void (*func)(void *), |
timer_callout(struct timer *tmr, u_long msec, |
void *arg, u_long msec) |
void (*func)(void *), void *arg) |
{ |
{ |
u_long ticks; |
u_long ticks; |
|
|
|
|
ticks = 1; |
ticks = 1; |
|
|
irq_lock(); |
irq_lock(); |
|
|
/* |
|
* Stop timer if running |
|
*/ |
|
if (tmr->active) |
if (tmr->active) |
list_remove(&tmr->link); |
list_remove(&tmr->link); |
/* |
|
* Program timer |
|
*/ |
|
tmr->func = func; |
tmr->func = func; |
tmr->arg = arg; |
tmr->arg = arg; |
tmr->active = 1; |
tmr->active = 1; |
tmr->interval = 0; |
tmr->interval = 0; |
timer_add(tmr, ticks); |
timer_add(tmr, ticks); |
|
|
irq_unlock(); |
irq_unlock(); |
} |
} |
|
|
|
|
|
|
/* |
/* |
* timer_delay - delay thread execution. |
* timer_delay - delay thread execution. |
|
* |
* The caller thread is blocked for the specified time. |
* The caller thread is blocked for the specified time. |
* Returns 0 on success, or the remaining time (msec) on failure. |
* Returns 0 on success, or the remaining time (msec) on failure. |
* This service is not available at interrupt level. |
* This service is not available at interrupt level. |
|
|
|
|
/* |
/* |
* timer_sleep - sleep system call. |
* timer_sleep - sleep system call. |
* @delay: delay time in milli-second |
|
* @remain: remaining time returned if the sleep is interrupted. |
|
* |
* |
* Stop execution of current thread for the indicated amount of time. |
* Stop execution of the current thread for the indicated amount |
* Returns EINTR if sleep is canceled by some reasons. |
* of time. If the sleep is interrupted, the remaining time |
|
* is set in "remain". Returns EINTR if sleep is canceled by |
|
* some reasons. |
*/ |
*/ |
int |
int |
timer_sleep(u_long delay, u_long *remain) |
timer_sleep(u_long msec, u_long *remain) |
{ |
{ |
u_long msec; |
u_long left; |
int err = 0; |
int err = 0; |
|
|
msec = timer_delay(delay); |
left = timer_delay(msec); |
|
|
if (remain != NULL) |
if (remain != NULL) |
err = umem_copyout(&msec, remain, sizeof(u_long)); |
err = umem_copyout(&left, remain, sizeof(left)); |
if (err == 0 && msec > 0) |
if (err == 0 && left > 0) |
err = EINTR; |
err = EINTR; |
return err; |
return err; |
} |
} |
|
|
* Send an alarm exception to the target task. |
* Send an alarm exception to the target task. |
*/ |
*/ |
static void |
static void |
alarm_expire(void *task) |
alarm_expire(void *arg) |
{ |
{ |
|
|
exception_post((task_t)task, SIGALRM); |
exception_post((task_t)arg, SIGALRM); |
} |
} |
|
|
/* |
/* |
* timer_alarm - schedule an alarm exception. |
* timer_alarm - alarm system call. |
* @delay: delay time in milli-second. If delay is 0, stop timer. |
|
* @remain: remaining time of the previous alarm request. |
|
* |
* |
* SIGALRM is sent to the caller task when specified delay time |
* SIGALRM exception is sent to the caller task when specified |
* is passed. |
* delay time is passed. If passed time is 0, stop the current |
|
* running timer. |
*/ |
*/ |
int |
int |
timer_alarm(u_long delay, u_long *remain) |
timer_alarm(u_long msec, u_long *remain) |
{ |
{ |
struct timer *tmr; |
struct timer *tmr; |
u_long msec = 0; |
task_t self = cur_task(); |
|
u_long left = 0; |
int err = 0; |
int err = 0; |
|
|
irq_lock(); |
irq_lock(); |
tmr = &cur_task()->alarm; |
tmr = &self->alarm; |
if (tmr->active) { |
if (tmr->active) { |
/* |
/* |
* Save the remaining time before we update |
* Save the remaining time to return |
* the timer value. |
* before we update the timer value. |
*/ |
*/ |
msec = tick_to_msec(time_remain(tmr->expire)); |
left = tick_to_msec(time_remain(tmr->expire)); |
} |
} |
if (delay == 0) { |
if (msec == 0) { |
timer_stop(tmr); |
timer_stop(tmr); |
} else { |
} else { |
timer_callout(tmr, alarm_expire, cur_task(), delay); |
timer_callout(tmr, msec, &alarm_expire, self); |
} |
} |
irq_unlock(); |
irq_unlock(); |
|
|
if (remain != NULL) |
if (remain != NULL) |
err = umem_copyout(&msec, remain, sizeof(u_long)); |
err = umem_copyout(&left, remain, sizeof(left)); |
return err; |
return err; |
} |
} |
|
|
/* |
/* |
* timer_periodic - set periodic timer for the specified thread. |
* timer_periodic - set periodic timer for the specified thread. |
* @th: thread to set timer. |
|
* @start: first time to wakeup. set 0 to stop timer. |
|
* @period: time interval to wakeup. This must be non-zero. |
|
* (The unit of start/period is milli-seconds.) |
|
* |
* |
* The periodic thread will wait the timer period by calling |
* The periodic thread will wait the timer period by calling |
* timer_waitperiod(). |
* timer_waitperiod(). The unit of start/period is milli-seconds. |
*/ |
*/ |
int |
int |
timer_periodic(thread_t th, u_long start, u_long period) |
timer_periodic(thread_t th, u_long start, u_long period) |
|
|
} else { |
} else { |
if (tmr == NULL) { |
if (tmr == NULL) { |
/* |
/* |
* Allocate a timer element at first call. We |
* Allocate a timer element at first call. |
* don't put this data in the thread structure |
* We don't put this data in the thread |
* because only a few threads will use the |
* structure because only a few threads |
* periodic timer function. |
* will use the periodic timer function. |
*/ |
*/ |
tmr = kmem_alloc(sizeof(struct timer)); |
tmr = kmem_alloc(sizeof(tmr)); |
if (tmr == NULL) { |
if (tmr == NULL) { |
sched_unlock(); |
sched_unlock(); |
return ENOMEM; |
return ENOMEM; |
|
|
/* |
/* |
* timer_waitperiod - wait next period of the periodic timer. |
* timer_waitperiod - wait next period of the periodic timer. |
* |
* |
* Since this routine can exit by any exceptions, the control may |
* Since this routine can exit by any exceptions, the control |
* return at non-period time. So, the caller must retry immediately |
* may return at non-period time. So, the caller must retry |
* if the error status is EINTR. This will be automatically done |
* immediately if the error status is EINTR. This will be |
* by the library stub routine. |
* automatically done by the library stub routine. |
*/ |
*/ |
int |
int |
timer_waitperiod(void) |
timer_waitperiod(void) |
|
|
|
|
if (time_before(lbolt, tmr->expire)) { |
if (time_before(lbolt, tmr->expire)) { |
/* |
/* |
* Sleep until timer_tick() routine wakes us up. |
* Sleep until timer_tick() routine |
|
* wakes us up. |
*/ |
*/ |
rc = sched_sleep(&tmr->event); |
rc = sched_sleep(&tmr->event); |
if (rc != SLP_SUCCESS) |
if (rc != SLP_SUCCESS) |
|
|
void |
void |
timer_cleanup(thread_t th) |
timer_cleanup(thread_t th) |
{ |
{ |
|
|
if (th->periodic != NULL) { |
if (th->periodic != NULL) { |
timer_stop(th->periodic); |
timer_stop(th->periodic); |
kmem_free(th->periodic); |
kmem_free(th->periodic); |
} |
} |
} |
} |
|
|
|
/* |
|
* Install a timer hook routine. |
|
* We allow only one hook routine in system. |
|
*/ |
int |
int |
timer_hook(void (*func)(int)) |
timer_hook(void (*func)(int)) |
{ |
{ |
|
|
/* |
/* |
* Timer thread. |
* Timer thread. |
* |
* |
* Handle all expired timers. Each callout routine is called |
* Handle all expired timers. Each callout routine is |
* with scheduler locked and interrupts enabled. |
* called with scheduler locked and interrupts enabled. |
*/ |
*/ |
static void |
static void |
timer_thread(u_long unused) |
timer_thread(void *arg) |
{ |
{ |
struct timer *tmr; |
struct timer *tmr; |
|
|
for (;;) { |
for (;;) { |
|
|
/* Wait until next timer expiration. */ |
/* Wait until next timer expiration. */ |
sched_sleep(&timer_event); |
sched_sleep(&timer_event); |
|
|
|
|
/* |
/* |
* Timer tick handler |
* Timer tick handler |
* |
* |
* timer_tick() is called straight from the real time clock interrupt. |
* timer_tick() is called straight from the real time |
* All interrupts are still disabled at the entry of this routine. |
* clock interrupt. All interrupts are still disabled |
|
* at the entry of this routine. |
*/ |
*/ |
void |
void |
timer_tick(void) |
timer_tick(void) |
|
|
u_long ticks; |
u_long ticks; |
int idle, wakeup = 0; |
int idle, wakeup = 0; |
|
|
/* Bump time in ticks. */ |
/* |
|
* Bump time in ticks. |
|
* Note that it is allowed to wrap. |
|
*/ |
lbolt++; |
lbolt++; |
|
|
/* |
/* |
* Handle all of the timer elements that have expired. |
* Handle all of the timer elements that have expired. |
*/ |
*/ |
while (!list_empty(&timer_list) && |
while (!list_empty(&timer_list)) { |
time_after_eq(lbolt, timer_next()->expire)) { |
|
/* |
/* |
|
* Check timer expiration. |
|
*/ |
|
tmr = timer_next(); |
|
if (time_before(lbolt, tmr->expire)) |
|
break; |
|
/* |
* Remove an expired timer from the list and wakup |
* Remove an expired timer from the list and wakup |
* the appropriate thread. If it is periodic timer, |
* the appropriate thread. If it is periodic timer, |
* reprogram the next expiration time. Otherwize, |
* reprogram the next expiration time. Otherwise, |
* it is moved to the expired list. |
* it is moved to the expired list. |
*/ |
*/ |
tmr = timer_next(); |
|
list_remove(&tmr->link); |
list_remove(&tmr->link); |
if (tmr->interval != 0) { |
if (tmr->interval != 0) { |
/* |
/* |
|
|
sched_tick(); |
sched_tick(); |
|
|
/* |
/* |
* Call a hook routine for power management or profiling work. |
* Call a hook routine for power management |
|
* or profiling work. |
*/ |
*/ |
if (tick_hook != NULL) { |
if (tick_hook != NULL) { |
idle = (cur_thread->prio == PRIO_IDLE) ? 1 : 0; |
idle = (cur_thread->prio == PRIO_IDLE) ? 1 : 0; |
tick_hook(idle); |
(*tick_hook)(idle); |
} |
} |
} |
} |
|
|
|
|
info->hz = HZ; |
info->hz = HZ; |
} |
} |
|
|
#if defined(DEBUG) && defined(CONFIG_KDUMP) |
|
void |
|
timer_dump(void) |
|
{ |
|
struct timer *tmr; |
|
list_t head, n; |
|
|
|
printk("Timer dump:\n"); |
|
printk("lbolt=%d\n", lbolt); |
|
|
|
head = &timer_list; |
|
for (n = list_first(head); n != head; n = list_next(n)) { |
|
tmr = list_entry(n, struct timer, link); |
|
printk("timer=%x func=%x arg=%x expire=%d\n", (int)tmr, |
|
(int)tmr->func, (int)tmr->arg, (int)tmr->expire); |
|
} |
|
} |
|
#endif |
|
|
|
/* |
/* |
* Initialize the timer facility, called at system startup time. |
* Initialize the timer facility, called at system startup time. |
*/ |
*/ |
void |
void |
timer_init(void) |
timer_init(void) |
{ |
{ |
|
thread_t th; |
|
|
list_init(&timer_list); |
list_init(&timer_list); |
list_init(&expire_list); |
list_init(&expire_list); |
event_init(&timer_event, "timer"); |
event_init(&timer_event, "timer"); |
event_init(&delay_event, "delay"); |
event_init(&delay_event, "delay"); |
|
|
/* |
/* Start timer thread */ |
* Start timer thread |
th = kthread_create(&timer_thread, NULL, PRIO_TIMER); |
*/ |
if (th == NULL) |
if (kernel_thread(PRIO_TIMER, timer_thread, 0) == NULL) |
|
panic("timer_init"); |
panic("timer_init"); |
} |
} |