1 /* 2 * Copyright (c) 1998-2000, 2009-2010 Apple Inc. All rights reserved. 3 * 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. The rights granted to you under the License 10 * may not be used to create, or enable the creation or redistribution of, 11 * unlawful or unlicensed copies of an Apple operating system, or to 12 * circumvent, violate, or enable the circumvention or violation of, any 13 * terms of an Apple operating system software license agreement. 14 * 15 * Please obtain a copy of the License at 16 * http://www.opensource.apple.com/apsl/ and read it before using this file. 17 * 18 * The Original Code and all software distributed under the License are 19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 23 * Please see the License for the specific language governing rights and 24 * limitations under the License. 25 * 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 27 */ 28 29 #include <sys/cdefs.h> 30 31 __BEGIN_DECLS 32 #include <kern/thread_call.h> 33 __END_DECLS 34 35 #include <IOKit/assert.h> 36 #include <IOKit/system.h> 37 38 #include <IOKit/IOLib.h> 39 #include <IOKit/IOTimerEventSource.h> 40 #include <IOKit/IOWorkLoop.h> 41 42 #include <IOKit/IOTimeStamp.h> 43 #include <IOKit/IOKitDebug.h> 44 #if CONFIG_DTRACE 45 #include <mach/sdt.h> 46 #endif 47 48 #include <libkern/Block.h> 49 50 51 #define super IOEventSource 52 OSDefineMetaClassAndStructors(IOTimerEventSource, IOEventSource) 53 OSMetaClassDefineReservedUsed(IOTimerEventSource, 0); 54 OSMetaClassDefineReservedUsed(IOTimerEventSource, 1); 55 OSMetaClassDefineReservedUsed(IOTimerEventSource, 2); 56 OSMetaClassDefineReservedUnused(IOTimerEventSource, 3); 57 OSMetaClassDefineReservedUnused(IOTimerEventSource, 4); 58 OSMetaClassDefineReservedUnused(IOTimerEventSource, 5); 59 OSMetaClassDefineReservedUnused(IOTimerEventSource, 6); 60 OSMetaClassDefineReservedUnused(IOTimerEventSource, 7); 61 62 #if IOKITSTATS 63 64 #define IOStatisticsInitializeCounter() \ 65 do { \ 66 IOStatistics::setCounterType(IOEventSource::reserved->counter, kIOStatisticsTimerEventSourceCounter); \ 67 } while (0) 68 69 #define IOStatisticsOpenGate() \ 70 do { \ 71 IOStatistics::countOpenGate(me->IOEventSource::reserved->counter); \ 72 } while (0) 73 74 #define IOStatisticsCloseGate() \ 75 do { \ 76 IOStatistics::countCloseGate(me->IOEventSource::reserved->counter); \ 77 } while (0) 78 79 #define IOStatisticsTimeout() \ 80 do { \ 81 IOStatistics::countTimerTimeout(me->IOEventSource::reserved->counter); \ 82 } while (0) 83 84 #else 85 86 #define IOStatisticsInitializeCounter() 87 #define IOStatisticsOpenGate() 88 #define IOStatisticsCloseGate() 89 #define IOStatisticsTimeout() 90 91 #endif /* IOKITSTATS */ 92 93 // 94 // reserved != 0 means IOTimerEventSource::timeoutAndRelease is being used, 95 // not a subclassed implementation. 96 // 97 98 // Timeout handler function. This function is called by the kernel when 99 // the timeout interval expires. 100 // 101 102 __inline__ void 103 IOTimerEventSource::invokeAction(IOTimerEventSource::Action action, IOTimerEventSource * ts, 104 OSObject * owner, IOWorkLoop * workLoop) 105 { 106 bool trace = (gIOKitTrace & kIOTraceTimers) ? true : false; 107 108 if (trace) 109 IOTimeStampStartConstant(IODBG_TIMES(IOTIMES_ACTION), 110 VM_KERNEL_ADDRHIDE(action), VM_KERNEL_ADDRHIDE(owner)); 111 112 if (kActionBlock & flags) ((IOTimerEventSource::ActionBlock) actionBlock)(ts); 113 else (*action)(owner, ts); 114 115 #if CONFIG_DTRACE 116 DTRACE_TMR3(iotescallout__expire, Action, action, OSObject, owner, void, workLoop); 117 #endif 118 119 if (trace) 120 IOTimeStampEndConstant(IODBG_TIMES(IOTIMES_ACTION), 121 VM_KERNEL_UNSLIDE(action), VM_KERNEL_ADDRHIDE(owner)); 122 } 123 124 void IOTimerEventSource::timeout(void *self) 125 { 126 IOTimerEventSource *me = (IOTimerEventSource *) self; 127 128 IOStatisticsTimeout(); 129 130 if (me->enabled && me->action) 131 { 132 IOWorkLoop * 133 wl = me->workLoop; 134 if (wl) 135 { 136 Action doit; 137 wl->closeGate(); 138 IOStatisticsCloseGate(); 139 doit = (Action) me->action; 140 if (doit && me->enabled && AbsoluteTime_to_scalar(&me->abstime)) 141 { 142 me->invokeAction(doit, me, me->owner, me->workLoop); 143 } 144 IOStatisticsOpenGate(); 145 wl->openGate(); 146 } 147 } 148 } 149 150 void IOTimerEventSource::timeoutAndRelease(void * self, void * c) 151 { 152 IOTimerEventSource *me = (IOTimerEventSource *) self; 153 /* The second parameter (a pointer) gets abused to carry an SInt32, so on LP64, "count" 154 must be cast to "long" before, in order to tell GCC we're not truncating a pointer. */ 155 SInt32 count = (SInt32) (long) c; 156 157 IOStatisticsTimeout(); 158 159 if (me->enabled && me->action) 160 { 161 IOWorkLoop * 162 wl = me->reserved->workLoop; 163 if (wl) 164 { 165 Action doit; 166 wl->closeGate(); 167 IOStatisticsCloseGate(); 168 doit = (Action) me->action; 169 if (doit && (me->reserved->calloutGeneration == count)) 170 { 171 me->invokeAction(doit, me, me->owner, me->workLoop); 172 } 173 IOStatisticsOpenGate(); 174 wl->openGate(); 175 } 176 } 177 178 me->reserved->workLoop->release(); 179 me->release(); 180 } 181 182 // -- work loop delivery 183 184 bool IOTimerEventSource::checkForWork() 185 { 186 Action doit; 187 188 if (reserved 189 && (reserved->calloutGenerationSignaled == reserved->calloutGeneration) 190 && enabled && (doit = (Action) action)) 191 { 192 reserved->calloutGenerationSignaled = ~reserved->calloutGeneration; 193 invokeAction(doit, this, owner, workLoop); 194 } 195 196 return false; 197 } 198 199 void IOTimerEventSource::timeoutSignaled(void * self, void * c) 200 { 201 IOTimerEventSource *me = (IOTimerEventSource *) self; 202 203 me->reserved->calloutGenerationSignaled = (SInt32)(long) c; 204 if (me->enabled) me->signalWorkAvailable(); 205 } 206 207 // -- 208 209 void IOTimerEventSource::setTimeoutFunc() 210 { 211 thread_call_priority_t pri; 212 uint32_t options; 213 214 if (reserved) panic("setTimeoutFunc already %p, %p", this, reserved); 215 216 // reserved != 0 means IOTimerEventSource::timeoutAndRelease is being used, 217 // not a subclassed implementation 218 reserved = IONew(ExpansionData, 1); 219 reserved->calloutGenerationSignaled = ~reserved->calloutGeneration; 220 options = abstime; 221 abstime = 0; 222 223 thread_call_options_t tcoptions = 0; 224 thread_call_func_t func = NULL; 225 226 switch (kIOTimerEventSourceOptionsPriorityMask & options) 227 { 228 case kIOTimerEventSourceOptionsPriorityHigh: 229 pri = THREAD_CALL_PRIORITY_HIGH; 230 func = &IOTimerEventSource::timeoutAndRelease; 231 break; 232 233 case kIOTimerEventSourceOptionsPriorityKernel: 234 pri = THREAD_CALL_PRIORITY_KERNEL; 235 func = &IOTimerEventSource::timeoutAndRelease; 236 break; 237 238 case kIOTimerEventSourceOptionsPriorityKernelHigh: 239 pri = THREAD_CALL_PRIORITY_KERNEL_HIGH; 240 func = &IOTimerEventSource::timeoutAndRelease; 241 break; 242 243 case kIOTimerEventSourceOptionsPriorityUser: 244 pri = THREAD_CALL_PRIORITY_USER; 245 func = &IOTimerEventSource::timeoutAndRelease; 246 break; 247 248 case kIOTimerEventSourceOptionsPriorityLow: 249 pri = THREAD_CALL_PRIORITY_LOW; 250 func = &IOTimerEventSource::timeoutAndRelease; 251 break; 252 253 case kIOTimerEventSourceOptionsPriorityWorkLoop: 254 pri = THREAD_CALL_PRIORITY_KERNEL; 255 tcoptions |= THREAD_CALL_OPTIONS_SIGNAL; 256 if (kIOTimerEventSourceOptionsAllowReenter & options) break; 257 func = &IOTimerEventSource::timeoutSignaled; 258 break; 259 260 default: 261 break; 262 } 263 264 assertf(func, "IOTimerEventSource options 0x%x", options); 265 if (!func) return; // init will fail 266 267 if (THREAD_CALL_OPTIONS_SIGNAL & tcoptions) flags |= kActive; 268 else flags |= kPassive; 269 270 if (!(kIOTimerEventSourceOptionsAllowReenter & options)) tcoptions |= THREAD_CALL_OPTIONS_ONCE; 271 272 calloutEntry = (void *) thread_call_allocate_with_options(func, 273 (thread_call_param_t) this, pri, tcoptions); 274 assert(calloutEntry); 275 } 276 277 bool IOTimerEventSource::init(OSObject *inOwner, Action inAction) 278 { 279 if (!super::init(inOwner, (IOEventSource::Action) inAction) ) 280 return false; 281 282 setTimeoutFunc(); 283 if (!calloutEntry) 284 return false; 285 286 IOStatisticsInitializeCounter(); 287 288 return true; 289 } 290 291 bool IOTimerEventSource::init(uint32_t options, OSObject *inOwner, Action inAction) 292 { 293 abstime = options; 294 return (init(inOwner, inAction)); 295 } 296 297 IOTimerEventSource * 298 IOTimerEventSource::timerEventSource(uint32_t inOptions, OSObject *inOwner, Action inAction) 299 { 300 IOTimerEventSource *me = new IOTimerEventSource; 301 302 if (me && !me->init(inOptions, inOwner, inAction)) { 303 me->release(); 304 return 0; 305 } 306 307 return me; 308 } 309 310 IOTimerEventSource * 311 IOTimerEventSource::timerEventSource(uint32_t options, OSObject *inOwner, ActionBlock action) 312 { 313 IOTimerEventSource * tes; 314 tes = IOTimerEventSource::timerEventSource(options, inOwner, (Action) NULL); 315 if (tes) tes->setActionBlock((IOEventSource::ActionBlock) action); 316 317 return tes; 318 } 319 320 #define _thread_call_cancel(tc) ((kActive & flags) ? thread_call_cancel_wait((tc)) : thread_call_cancel((tc))) 321 322 IOTimerEventSource * 323 IOTimerEventSource::timerEventSource(OSObject *inOwner, Action inAction) 324 { 325 return (IOTimerEventSource::timerEventSource( 326 kIOTimerEventSourceOptionsPriorityKernelHigh, 327 inOwner, inAction)); 328 } 329 330 void IOTimerEventSource::free() 331 { 332 if (calloutEntry) { 333 __assert_only bool freed; 334 335 cancelTimeout(); 336 337 freed = thread_call_free((thread_call_t) calloutEntry); 338 assert(freed); 339 } 340 341 if (reserved) 342 IODelete(reserved, ExpansionData, 1); 343 344 super::free(); 345 } 346 347 void IOTimerEventSource::cancelTimeout() 348 { 349 if (reserved) 350 reserved->calloutGeneration++; 351 bool active = _thread_call_cancel((thread_call_t) calloutEntry); 352 AbsoluteTime_to_scalar(&abstime) = 0; 353 if (active && reserved && (kPassive & flags)) 354 { 355 release(); 356 workLoop->release(); 357 } 358 } 359 360 void IOTimerEventSource::enable() 361 { 362 super::enable(); 363 if (kIOReturnSuccess != wakeAtTime(abstime)) 364 super::disable(); // Problem re-scheduling timeout ignore enable 365 } 366 367 void IOTimerEventSource::disable() 368 { 369 if (reserved) 370 reserved->calloutGeneration++; 371 bool active = _thread_call_cancel((thread_call_t) calloutEntry); 372 super::disable(); 373 if (active && reserved && (kPassive & flags)) 374 { 375 release(); 376 workLoop->release(); 377 } 378 } 379 380 IOReturn IOTimerEventSource::setTimeoutTicks(UInt32 ticks) 381 { 382 return setTimeout(ticks, kTickScale); 383 } 384 385 IOReturn IOTimerEventSource::setTimeoutMS(UInt32 ms) 386 { 387 return setTimeout(ms, kMillisecondScale); 388 } 389 390 IOReturn IOTimerEventSource::setTimeoutUS(UInt32 us) 391 { 392 return setTimeout(us, kMicrosecondScale); 393 } 394 395 IOReturn IOTimerEventSource::setTimeout(UInt32 interval, UInt32 scale_factor) 396 { 397 AbsoluteTime end; 398 399 clock_interval_to_deadline(interval, scale_factor, &end); 400 return wakeAtTime(end); 401 } 402 403 #if !defined(__LP64__) 404 IOReturn IOTimerEventSource::setTimeout(mach_timespec_t interval) 405 { 406 AbsoluteTime end, nsecs; 407 408 clock_interval_to_absolutetime_interval 409 (interval.tv_nsec, kNanosecondScale, &nsecs); 410 clock_interval_to_deadline 411 (interval.tv_sec, NSEC_PER_SEC, &end); 412 ADD_ABSOLUTETIME(&end, &nsecs); 413 414 return wakeAtTime(end); 415 } 416 #endif 417 418 IOReturn IOTimerEventSource::setTimeout(AbsoluteTime interval) 419 { 420 AbsoluteTime end; 421 clock_absolutetime_interval_to_deadline(interval, &end); 422 return wakeAtTime(end); 423 } 424 425 IOReturn IOTimerEventSource::setTimeout(uint32_t options, 426 AbsoluteTime abstime, AbsoluteTime leeway) 427 { 428 AbsoluteTime end; 429 if (options & kIOTimeOptionsContinuous) 430 clock_continuoustime_interval_to_deadline(abstime, &end); 431 else 432 clock_absolutetime_interval_to_deadline(abstime, &end); 433 434 return wakeAtTime(options, end, leeway); 435 } 436 437 IOReturn IOTimerEventSource::wakeAtTimeTicks(UInt32 ticks) 438 { 439 return wakeAtTime(ticks, kTickScale); 440 } 441 442 IOReturn IOTimerEventSource::wakeAtTimeMS(UInt32 ms) 443 { 444 return wakeAtTime(ms, kMillisecondScale); 445 } 446 447 IOReturn IOTimerEventSource::wakeAtTimeUS(UInt32 us) 448 { 449 return wakeAtTime(us, kMicrosecondScale); 450 } 451 452 IOReturn IOTimerEventSource::wakeAtTime(UInt32 inAbstime, UInt32 scale_factor) 453 { 454 AbsoluteTime end; 455 clock_interval_to_absolutetime_interval(inAbstime, scale_factor, &end); 456 457 return wakeAtTime(end); 458 } 459 460 #if !defined(__LP64__) 461 IOReturn IOTimerEventSource::wakeAtTime(mach_timespec_t inAbstime) 462 { 463 AbsoluteTime end, nsecs; 464 465 clock_interval_to_absolutetime_interval 466 (inAbstime.tv_nsec, kNanosecondScale, &nsecs); 467 clock_interval_to_absolutetime_interval 468 (inAbstime.tv_sec, kSecondScale, &end); 469 ADD_ABSOLUTETIME(&end, &nsecs); 470 471 return wakeAtTime(end); 472 } 473 #endif 474 475 void IOTimerEventSource::setWorkLoop(IOWorkLoop *inWorkLoop) 476 { 477 super::setWorkLoop(inWorkLoop); 478 if ( enabled && AbsoluteTime_to_scalar(&abstime) && workLoop ) 479 wakeAtTime(abstime); 480 } 481 482 IOReturn IOTimerEventSource::wakeAtTime(AbsoluteTime inAbstime) 483 { 484 return wakeAtTime(0, inAbstime, 0); 485 } 486 487 IOReturn IOTimerEventSource::wakeAtTime(uint32_t options, AbsoluteTime inAbstime, AbsoluteTime leeway) 488 { 489 if (!action) 490 return kIOReturnNoResources; 491 492 abstime = inAbstime; 493 if ( enabled && AbsoluteTime_to_scalar(&inAbstime) && AbsoluteTime_to_scalar(&abstime) && workLoop ) 494 { 495 uint32_t tcoptions = 0; 496 497 if (kIOTimeOptionsWithLeeway & options) tcoptions |= THREAD_CALL_DELAY_LEEWAY; 498 if (kIOTimeOptionsContinuous & options) tcoptions |= THREAD_CALL_CONTINUOUS; 499 500 if (reserved) 501 { 502 if (kPassive & flags) 503 { 504 retain(); 505 workLoop->retain(); 506 } 507 reserved->workLoop = workLoop; 508 reserved->calloutGeneration++; 509 if (thread_call_enter_delayed_with_leeway((thread_call_t) calloutEntry, 510 (void *)(uintptr_t) reserved->calloutGeneration, inAbstime, leeway, tcoptions) 511 && (kPassive & flags)) 512 { 513 release(); 514 workLoop->release(); 515 } 516 } 517 else 518 { 519 thread_call_enter_delayed_with_leeway((thread_call_t) calloutEntry, 520 NULL, inAbstime, leeway, tcoptions); 521 } 522 } 523 524 return kIOReturnSuccess; 525 } 526