|
|||
Prex Home / Browse Source - Prex Version: 0.9.0 |
|||
root/sys/kern/timer.c/* [<][>][^][v][top][bottom][index][help] */DEFINITIONSThis source file includes following definitions.
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] */ | |||
Copyright© 2005-2009 Kohsuke Ohtani |