Prex Home / Browse Source - Prex Version: 0.9.0

root/sys/kern/timer.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. time_remain
  2. timer_add
  3. timer_stop
  4. timer_callout
  5. timer_delay
  6. timer_sleep
  7. alarm_expire
  8. timer_alarm
  9. timer_periodic
  10. timer_waitperiod
  11. timer_cancel
  12. timer_thread
  13. timer_handler
  14. timer_ticks
  15. timer_info
  16. timer_init

   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 }

/* [<][>][^][v][top][bottom][index][help] */