1 /* 2 * Copyright 2002-2011, Haiku. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Copyright 2001, Travis Geiselbrecht. All rights reserved. 6 * Distributed under the terms of the NewOS License. 7 */ 8 9 10 /*! Policy info for timers */ 11 12 13 #include <timer.h> 14 15 #include <OS.h> 16 17 #include <arch/timer.h> 18 #include <boot/kernel_args.h> 19 #include <cpu.h> 20 #include <debug.h> 21 #include <elf.h> 22 #include <real_time_clock.h> 23 #include <smp.h> 24 #include <thread.h> 25 #include <util/AutoLock.h> 26 27 28 struct per_cpu_timer_data { 29 spinlock lock; 30 timer* volatile events; 31 timer* volatile current_event; 32 int32 current_event_in_progress; 33 bigtime_t real_time_offset; 34 }; 35 36 static per_cpu_timer_data sPerCPU[SMP_MAX_CPUS]; 37 38 39 //#define TRACE_TIMER 40 #ifdef TRACE_TIMER 41 # define TRACE(x) dprintf x 42 #else 43 # define TRACE(x) ; 44 #endif 45 46 47 /*! Sets the hardware timer to the given absolute time. 48 49 \param scheduleTime The absolute system time for the timer expiration. 50 \param now The current system time. 51 */ 52 static void 53 set_hardware_timer(bigtime_t scheduleTime, bigtime_t now) 54 { 55 arch_timer_set_hardware_timer(scheduleTime > now ? scheduleTime - now : 0); 56 } 57 58 59 /*! Sets the hardware timer to the given absolute time. 60 61 \param scheduleTime The absolute system time for the timer expiration. 62 */ 63 static inline void 64 set_hardware_timer(bigtime_t scheduleTime) 65 { 66 set_hardware_timer(scheduleTime, system_time()); 67 } 68 69 70 /*! NOTE: expects interrupts to be off */ 71 static void 72 add_event_to_list(timer* event, timer* volatile* list) 73 { 74 timer* next; 75 timer* last = NULL; 76 77 // stick it in the event list 78 for (next = *list; next; last = next, next = (timer*)next->next) { 79 if ((bigtime_t)next->schedule_time >= (bigtime_t)event->schedule_time) 80 break; 81 } 82 83 if (last != NULL) { 84 event->next = last->next; 85 last->next = event; 86 } else { 87 event->next = next; 88 *list = event; 89 } 90 } 91 92 93 static void 94 per_cpu_real_time_clock_changed(void*, int cpu) 95 { 96 per_cpu_timer_data& cpuData = sPerCPU[cpu]; 97 SpinLocker cpuDataLocker(cpuData.lock); 98 99 bigtime_t realTimeOffset = rtc_boot_time(); 100 if (realTimeOffset == cpuData.real_time_offset) 101 return; 102 103 // The real time offset has changed. We need to update all affected 104 // timers. First find and dequeue them. 105 bigtime_t timeDiff = cpuData.real_time_offset - realTimeOffset; 106 cpuData.real_time_offset = realTimeOffset; 107 108 timer* affectedTimers = NULL; 109 timer* volatile* it = &cpuData.events; 110 timer* firstEvent = *it; 111 while (timer* event = *it) { 112 // check whether it's an absolute real-time timer 113 uint32 flags = event->flags; 114 if ((flags & ~B_TIMER_FLAGS) != B_ONE_SHOT_ABSOLUTE_TIMER 115 || (flags & B_TIMER_REAL_TIME_BASE) == 0) { 116 it = &event->next; 117 continue; 118 } 119 120 // Yep, remove the timer from the queue and add it to the 121 // affectedTimers list. 122 *it = event->next; 123 event->next = affectedTimers; 124 affectedTimers = event; 125 } 126 127 // update and requeue the affected timers 128 bool firstEventChanged = cpuData.events != firstEvent; 129 firstEvent = cpuData.events; 130 131 while (affectedTimers != NULL) { 132 timer* event = affectedTimers; 133 affectedTimers = event->next; 134 135 bigtime_t oldTime = event->schedule_time; 136 event->schedule_time += timeDiff; 137 138 // handle over-/underflows 139 if (timeDiff >= 0) { 140 if (event->schedule_time < oldTime) 141 event->schedule_time = B_INFINITE_TIMEOUT; 142 } else { 143 if (event->schedule_time < 0) 144 event->schedule_time = 0; 145 } 146 147 add_event_to_list(event, &cpuData.events); 148 } 149 150 firstEventChanged |= cpuData.events != firstEvent; 151 152 // If the first event has changed, reset the hardware timer. 153 if (firstEventChanged) 154 set_hardware_timer(cpuData.events->schedule_time); 155 } 156 157 158 // #pragma mark - debugging 159 160 161 static int 162 dump_timers(int argc, char** argv) 163 { 164 int32 cpuCount = smp_get_num_cpus(); 165 for (int32 i = 0; i < cpuCount; i++) { 166 kprintf("CPU %" B_PRId32 ":\n", i); 167 168 if (sPerCPU[i].events == NULL) { 169 kprintf(" no timers scheduled\n"); 170 continue; 171 } 172 173 for (timer* event = sPerCPU[i].events; event != NULL; 174 event = event->next) { 175 kprintf(" [%9lld] %p: ", (long long)event->schedule_time, event); 176 if ((event->flags & ~B_TIMER_FLAGS) == B_PERIODIC_TIMER) 177 kprintf("periodic %9lld, ", (long long)event->period); 178 else 179 kprintf("one shot, "); 180 181 kprintf("flags: %#x, user data: %p, callback: %p ", 182 event->flags, event->user_data, event->hook); 183 184 // look up and print the hook function symbol 185 const char* symbol; 186 const char* imageName; 187 bool exactMatch; 188 189 status_t error = elf_debug_lookup_symbol_address( 190 (addr_t)event->hook, NULL, &symbol, &imageName, &exactMatch); 191 if (error == B_OK && exactMatch) { 192 if (const char* slash = strchr(imageName, '/')) 193 imageName = slash + 1; 194 195 kprintf(" %s:%s", imageName, symbol); 196 } 197 198 kprintf("\n"); 199 } 200 } 201 202 kprintf("current time: %lld\n", (long long)system_time()); 203 204 return 0; 205 } 206 207 208 // #pragma mark - kernel-private 209 210 211 status_t 212 timer_init(kernel_args* args) 213 { 214 TRACE(("timer_init: entry\n")); 215 216 if (arch_init_timer(args) != B_OK) 217 panic("arch_init_timer() failed"); 218 219 add_debugger_command_etc("timers", &dump_timers, "List all timers", 220 "\n" 221 "Prints a list of all scheduled timers.\n", 0); 222 223 return B_OK; 224 } 225 226 227 void 228 timer_init_post_rtc(void) 229 { 230 bigtime_t realTimeOffset = rtc_boot_time(); 231 232 int32 cpuCount = smp_get_num_cpus(); 233 for (int32 i = 0; i < cpuCount; i++) 234 sPerCPU[i].real_time_offset = realTimeOffset; 235 } 236 237 238 void 239 timer_real_time_clock_changed() 240 { 241 call_all_cpus(&per_cpu_real_time_clock_changed, NULL); 242 } 243 244 245 int32 246 timer_interrupt() 247 { 248 timer* event; 249 spinlock* spinlock; 250 per_cpu_timer_data& cpuData = sPerCPU[smp_get_current_cpu()]; 251 int32 rc = B_HANDLED_INTERRUPT; 252 253 TRACE(("timer_interrupt: time %lld, cpu %ld\n", system_time(), 254 smp_get_current_cpu())); 255 256 spinlock = &cpuData.lock; 257 258 acquire_spinlock(spinlock); 259 260 event = cpuData.events; 261 while (event != NULL && ((bigtime_t)event->schedule_time < system_time())) { 262 // this event needs to happen 263 int mode = event->flags; 264 265 cpuData.events = (timer*)event->next; 266 cpuData.current_event = event; 267 atomic_set(&cpuData.current_event_in_progress, 1); 268 269 release_spinlock(spinlock); 270 271 TRACE(("timer_interrupt: calling hook %p for event %p\n", event->hook, 272 event)); 273 274 // call the callback 275 // note: if the event is not periodic, it is ok 276 // to delete the event structure inside the callback 277 if (event->hook) 278 rc = event->hook(event); 279 280 atomic_set(&cpuData.current_event_in_progress, 0); 281 282 acquire_spinlock(spinlock); 283 284 if ((mode & ~B_TIMER_FLAGS) == B_PERIODIC_TIMER 285 && cpuData.current_event != NULL) { 286 // we need to adjust it and add it back to the list 287 event->schedule_time += event->period; 288 289 // If the new schedule time is a full interval or more in the past, 290 // skip ticks. 291 bigtime_t now = system_time(); 292 if (now >= event->schedule_time + event->period) { 293 // pick the closest tick in the past 294 event->schedule_time = now 295 - (now - event->schedule_time) % event->period; 296 } 297 298 add_event_to_list(event, &cpuData.events); 299 } 300 301 cpuData.current_event = NULL; 302 303 event = cpuData.events; 304 } 305 306 // setup the next hardware timer 307 if (cpuData.events != NULL) 308 set_hardware_timer(cpuData.events->schedule_time); 309 310 release_spinlock(spinlock); 311 312 return rc; 313 } 314 315 316 // #pragma mark - public API 317 318 319 status_t 320 add_timer(timer* event, timer_hook hook, bigtime_t period, int32 flags) 321 { 322 bigtime_t currentTime = system_time(); 323 cpu_status state; 324 325 if (event == NULL || hook == NULL || period < 0) 326 return B_BAD_VALUE; 327 328 TRACE(("add_timer: event %p\n", event)); 329 330 // compute the schedule time 331 bigtime_t scheduleTime; 332 if ((flags & B_TIMER_USE_TIMER_STRUCT_TIMES) != 0) { 333 scheduleTime = event->schedule_time; 334 period = event->period; 335 } else { 336 scheduleTime = period; 337 if ((flags & ~B_TIMER_FLAGS) != B_ONE_SHOT_ABSOLUTE_TIMER) 338 scheduleTime += currentTime; 339 event->schedule_time = (int64)scheduleTime; 340 event->period = period; 341 } 342 343 event->hook = hook; 344 event->flags = flags; 345 346 state = disable_interrupts(); 347 int currentCPU = smp_get_current_cpu(); 348 per_cpu_timer_data& cpuData = sPerCPU[currentCPU]; 349 acquire_spinlock(&cpuData.lock); 350 351 // If the timer is an absolute real-time base timer, convert the schedule 352 // time to system time. 353 if ((flags & ~B_TIMER_FLAGS) == B_ONE_SHOT_ABSOLUTE_TIMER 354 && (flags & B_TIMER_REAL_TIME_BASE) != 0) { 355 if (event->schedule_time > cpuData.real_time_offset) 356 event->schedule_time -= cpuData.real_time_offset; 357 else 358 event->schedule_time = 0; 359 } 360 361 add_event_to_list(event, &cpuData.events); 362 event->cpu = currentCPU; 363 364 // if we were stuck at the head of the list, set the hardware timer 365 if (event == cpuData.events) 366 set_hardware_timer(scheduleTime, currentTime); 367 368 release_spinlock(&cpuData.lock); 369 restore_interrupts(state); 370 371 return B_OK; 372 } 373 374 375 bool 376 cancel_timer(timer* event) 377 { 378 TRACE(("cancel_timer: event %p\n", event)); 379 380 InterruptsLocker _; 381 382 // lock the right CPU spinlock 383 int cpu = event->cpu; 384 SpinLocker spinLocker; 385 while (true) { 386 if (cpu >= SMP_MAX_CPUS) 387 return false; 388 389 spinLocker.SetTo(sPerCPU[cpu].lock, false); 390 if (cpu == event->cpu) 391 break; 392 393 // cpu field changed while we were trying to lock 394 spinLocker.Unlock(); 395 cpu = event->cpu; 396 } 397 398 per_cpu_timer_data& cpuData = sPerCPU[cpu]; 399 400 if (event != cpuData.current_event) { 401 // The timer hook is not yet being executed. 402 timer* current = cpuData.events; 403 timer* last = NULL; 404 405 while (current != NULL) { 406 if (current == event) { 407 // we found it 408 if (last == NULL) 409 cpuData.events = current->next; 410 else 411 last->next = current->next; 412 current->next = NULL; 413 // break out of the whole thing 414 break; 415 } 416 last = current; 417 current = current->next; 418 } 419 420 // If not found, we assume this was a one-shot timer and has already 421 // fired. 422 if (current == NULL) 423 return true; 424 425 // invalidate CPU field 426 event->cpu = 0xffff; 427 428 // If on the current CPU, also reset the hardware timer. 429 if (cpu == smp_get_current_cpu()) { 430 if (cpuData.events == NULL) 431 arch_timer_clear_hardware_timer(); 432 else 433 set_hardware_timer(cpuData.events->schedule_time); 434 } 435 436 return false; 437 } 438 439 // The timer hook is currently being executed. We clear the current 440 // event so that timer_interrupt() will not reschedule periodic timers. 441 cpuData.current_event = NULL; 442 443 // Unless this is a kernel-private timer that also requires the scheduler 444 // lock to be held while calling the event hook, we'll have to wait 445 // for the hook to complete. When called from the timer hook we don't 446 // wait either, of course. 447 if (cpu != smp_get_current_cpu()) { 448 spinLocker.Unlock(); 449 450 while (atomic_get(&cpuData.current_event_in_progress) == 1) 451 cpu_wait(&cpuData.current_event_in_progress, 0); 452 } 453 454 return true; 455 } 456 457 458 void 459 spin(bigtime_t microseconds) 460 { 461 bigtime_t time = system_time(); 462 463 while ((system_time() - time) < microseconds) 464 cpu_pause(); 465 } 466