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 #define super IOEventSource 49 OSDefineMetaClassAndStructors(IOTimerEventSource, IOEventSource) 50 OSMetaClassDefineReservedUsed(IOTimerEventSource, 0); 51 OSMetaClassDefineReservedUsed(IOTimerEventSource, 1); 52 OSMetaClassDefineReservedUsed(IOTimerEventSource, 2); 53 OSMetaClassDefineReservedUnused(IOTimerEventSource, 3); 54 OSMetaClassDefineReservedUnused(IOTimerEventSource, 4); 55 OSMetaClassDefineReservedUnused(IOTimerEventSource, 5); 56 OSMetaClassDefineReservedUnused(IOTimerEventSource, 6); 57 OSMetaClassDefineReservedUnused(IOTimerEventSource, 7); 58 59 #if IOKITSTATS 60 61 #define IOStatisticsInitializeCounter() \ 62 do { \ 63 IOStatistics::setCounterType(IOEventSource::reserved->counter, kIOStatisticsTimerEventSourceCounter); \ 64 } while (0) 65 66 #define IOStatisticsOpenGate() \ 67 do { \ 68 IOStatistics::countOpenGate(me->IOEventSource::reserved->counter); \ 69 } while (0) 70 71 #define IOStatisticsCloseGate() \ 72 do { \ 73 IOStatistics::countCloseGate(me->IOEventSource::reserved->counter); \ 74 } while (0) 75 76 #define IOStatisticsTimeout() \ 77 do { \ 78 IOStatistics::countTimerTimeout(me->IOEventSource::reserved->counter); \ 79 } while (0) 80 81 #else 82 83 #define IOStatisticsInitializeCounter() 84 #define IOStatisticsOpenGate() 85 #define IOStatisticsCloseGate() 86 #define IOStatisticsTimeout() 87 88 #endif /* IOKITSTATS */ 89 90 // 91 // reserved != 0 means IOTimerEventSource::timeoutAndRelease is being used, 92 // not a subclassed implementation. 93 // 94 95 // Timeout handler function. This function is called by the kernel when 96 // the timeout interval expires. 97 // 98 99 static __inline__ void 100 InvokeAction(IOTimerEventSource::Action action, IOTimerEventSource * ts, 101 OSObject * owner, IOWorkLoop * workLoop) 102 { 103 bool trace = (gIOKitTrace & kIOTraceTimers) ? true : false; 104 105 if (trace) 106 IOTimeStampStartConstant(IODBG_TIMES(IOTIMES_ACTION), 107 VM_KERNEL_ADDRHIDE(action), VM_KERNEL_ADDRHIDE(owner)); 108 109 (*action)(owner, ts); 110 111 #if CONFIG_DTRACE 112 DTRACE_TMR3(iotescallout__expire, Action, action, OSObject, owner, void, workLoop); 113 #endif 114 115 if (trace) 116 IOTimeStampEndConstant(IODBG_TIMES(IOTIMES_ACTION), 117 VM_KERNEL_UNSLIDE(action), VM_KERNEL_ADDRHIDE(owner)); 118 } 119 120 void IOTimerEventSource::timeout(void *self) 121 { 122 IOTimerEventSource *me = (IOTimerEventSource *) self; 123 124 IOStatisticsTimeout(); 125 126 if (me->enabled && me->action) 127 { 128 IOWorkLoop * 129 wl = me->workLoop; 130 if (wl) 131 { 132 Action doit; 133 wl->closeGate(); 134 IOStatisticsCloseGate(); 135 doit = (Action) me->action; 136 if (doit && me->enabled && AbsoluteTime_to_scalar(&me->abstime)) 137 { 138 InvokeAction(doit, me, me->owner, me->workLoop); 139 } 140 IOStatisticsOpenGate(); 141 wl->openGate(); 142 } 143 } 144 } 145 146 void IOTimerEventSource::timeoutAndRelease(void * self, void * c) 147 { 148 IOTimerEventSource *me = (IOTimerEventSource *) self; 149 /* The second parameter (a pointer) gets abused to carry an SInt32, so on LP64, "count" 150 must be cast to "long" before, in order to tell GCC we're not truncating a pointer. */ 151 SInt32 count = (SInt32) (long) c; 152 153 IOStatisticsTimeout(); 154 155 if (me->enabled && me->action) 156 { 157 IOWorkLoop * 158 wl = me->reserved->workLoop; 159 if (wl) 160 { 161 Action doit; 162 wl->closeGate(); 163 IOStatisticsCloseGate(); 164 doit = (Action) me->action; 165 if (doit && (me->reserved->calloutGeneration == count)) 166 { 167 InvokeAction(doit, me, me->owner, me->workLoop); 168 } 169 IOStatisticsOpenGate(); 170 wl->openGate(); 171 } 172 } 173 174 me->reserved->workLoop->release(); 175 me->release(); 176 } 177 178 // -- work loop delivery 179 180 bool IOTimerEventSource::checkForWork() 181 { 182 Action doit; 183 184 if (reserved 185 && (reserved->calloutGenerationSignaled == reserved->calloutGeneration) 186 && enabled && (doit = (Action) action)) 187 { 188 reserved->calloutGenerationSignaled = ~reserved->calloutGeneration; 189 InvokeAction(doit, this, owner, workLoop); 190 } 191 192 return false; 193 } 194 195 void IOTimerEventSource::timeoutSignaled(void * self, void * c) 196 { 197 IOTimerEventSource *me = (IOTimerEventSource *) self; 198 199 me->reserved->calloutGenerationSignaled = (SInt32)(long) c; 200 if (me->enabled) me->signalWorkAvailable(); 201 } 202 203 // -- 204 205 void IOTimerEventSource::setTimeoutFunc() 206 { 207 thread_call_priority_t pri; 208 uint32_t options; 209 210 if (reserved) panic("setTimeoutFunc already %p, %p", this, reserved); 211 212 // reserved != 0 means IOTimerEventSource::timeoutAndRelease is being used, 213 // not a subclassed implementation 214 reserved = IONew(ExpansionData, 1); 215 reserved->calloutGenerationSignaled = ~reserved->calloutGeneration; 216 options = abstime; 217 abstime = 0; 218 219 thread_call_options_t tcoptions = 0; 220 thread_call_func_t func = NULL; 221 222 switch (kIOTimerEventSourceOptionsPriorityMask & options) 223 { 224 case kIOTimerEventSourceOptionsPriorityHigh: 225 pri = THREAD_CALL_PRIORITY_HIGH; 226 func = &IOTimerEventSource::timeoutAndRelease; 227 break; 228 229 case kIOTimerEventSourceOptionsPriorityKernel: 230 pri = THREAD_CALL_PRIORITY_KERNEL; 231 func = &IOTimerEventSource::timeoutAndRelease; 232 break; 233 234 case kIOTimerEventSourceOptionsPriorityKernelHigh: 235 pri = THREAD_CALL_PRIORITY_KERNEL_HIGH; 236 func = &IOTimerEventSource::timeoutAndRelease; 237 break; 238 239 case kIOTimerEventSourceOptionsPriorityUser: 240 pri = THREAD_CALL_PRIORITY_USER; 241 func = &IOTimerEventSource::timeoutAndRelease; 242 break; 243 244 case kIOTimerEventSourceOptionsPriorityLow: 245 pri = THREAD_CALL_PRIORITY_LOW; 246 func = &IOTimerEventSource::timeoutAndRelease; 247 break; 248 249 case kIOTimerEventSourceOptionsPriorityWorkLoop: 250 pri = THREAD_CALL_PRIORITY_KERNEL; 251 tcoptions |= THREAD_CALL_OPTIONS_SIGNAL; 252 if (kIOTimerEventSourceOptionsAllowReenter & options) break; 253 func = &IOTimerEventSource::timeoutSignaled; 254 break; 255 256 default: 257 break; 258 } 259 260 assertf(func, "IOTimerEventSource options 0x%x", options); 261 if (!func) return; // init will fail 262 263 if (THREAD_CALL_OPTIONS_SIGNAL & tcoptions) flags |= kActive; 264 else flags |= kPassive; 265 266 if (!(kIOTimerEventSourceOptionsAllowReenter & options)) tcoptions |= THREAD_CALL_OPTIONS_ONCE; 267 268 calloutEntry = (void *) thread_call_allocate_with_options(func, 269 (thread_call_param_t) this, pri, tcoptions); 270 assert(calloutEntry); 271 } 272 273 bool IOTimerEventSource::init(OSObject *inOwner, Action inAction) 274 { 275 if (!super::init(inOwner, (IOEventSource::Action) inAction) ) 276 return false; 277 278 setTimeoutFunc(); 279 if (!calloutEntry) 280 return false; 281 282 IOStatisticsInitializeCounter(); 283 284 return true; 285 } 286 287 bool IOTimerEventSource::init(uint32_t options, OSObject *inOwner, Action inAction) 288 { 289 abstime = options; 290 return (init(inOwner, inAction)); 291 } 292 293 IOTimerEventSource * 294 IOTimerEventSource::timerEventSource(uint32_t inOptions, OSObject *inOwner, Action inAction) 295 { 296 IOTimerEventSource *me = new IOTimerEventSource; 297 298 if (me && !me->init(inOptions, inOwner, inAction)) { 299 me->release(); 300 return 0; 301 } 302 303 return me; 304 } 305 306 #define _thread_call_cancel(tc) ((kActive & flags) ? thread_call_cancel_wait((tc)) : thread_call_cancel((tc))) 307 308 IOTimerEventSource * 309 IOTimerEventSource::timerEventSource(OSObject *inOwner, Action inAction) 310 { 311 return (IOTimerEventSource::timerEventSource( 312 kIOTimerEventSourceOptionsPriorityKernelHigh, 313 inOwner, inAction)); 314 } 315 316 void IOTimerEventSource::free() 317 { 318 if (calloutEntry) { 319 __assert_only bool freed; 320 321 cancelTimeout(); 322 323 freed = thread_call_free((thread_call_t) calloutEntry); 324 assert(freed); 325 } 326 327 if (reserved) 328 IODelete(reserved, ExpansionData, 1); 329 330 super::free(); 331 } 332 333 void IOTimerEventSource::cancelTimeout() 334 { 335 if (reserved) 336 reserved->calloutGeneration++; 337 bool active = _thread_call_cancel((thread_call_t) calloutEntry); 338 AbsoluteTime_to_scalar(&abstime) = 0; 339 if (active && reserved && (kPassive & flags)) 340 { 341 release(); 342 workLoop->release(); 343 } 344 } 345 346 void IOTimerEventSource::enable() 347 { 348 super::enable(); 349 if (kIOReturnSuccess != wakeAtTime(abstime)) 350 super::disable(); // Problem re-scheduling timeout ignore enable 351 } 352 353 void IOTimerEventSource::disable() 354 { 355 if (reserved) 356 reserved->calloutGeneration++; 357 bool active = _thread_call_cancel((thread_call_t) calloutEntry); 358 super::disable(); 359 if (active && reserved && (kPassive & flags)) 360 { 361 release(); 362 workLoop->release(); 363 } 364 } 365 366 IOReturn IOTimerEventSource::setTimeoutTicks(UInt32 ticks) 367 { 368 return setTimeout(ticks, kTickScale); 369 } 370 371 IOReturn IOTimerEventSource::setTimeoutMS(UInt32 ms) 372 { 373 return setTimeout(ms, kMillisecondScale); 374 } 375 376 IOReturn IOTimerEventSource::setTimeoutUS(UInt32 us) 377 { 378 return setTimeout(us, kMicrosecondScale); 379 } 380 381 IOReturn IOTimerEventSource::setTimeout(UInt32 interval, UInt32 scale_factor) 382 { 383 AbsoluteTime end; 384 385 clock_interval_to_deadline(interval, scale_factor, &end); 386 return wakeAtTime(end); 387 } 388 389 #if !defined(__LP64__) 390 IOReturn IOTimerEventSource::setTimeout(mach_timespec_t interval) 391 { 392 AbsoluteTime end, nsecs; 393 394 clock_interval_to_absolutetime_interval 395 (interval.tv_nsec, kNanosecondScale, &nsecs); 396 clock_interval_to_deadline 397 (interval.tv_sec, NSEC_PER_SEC, &end); 398 ADD_ABSOLUTETIME(&end, &nsecs); 399 400 return wakeAtTime(end); 401 } 402 #endif 403 404 IOReturn IOTimerEventSource::setTimeout(AbsoluteTime interval) 405 { 406 AbsoluteTime end; 407 clock_absolutetime_interval_to_deadline(interval, &end); 408 return wakeAtTime(end); 409 } 410 411 IOReturn IOTimerEventSource::setTimeout(uint32_t options, 412 AbsoluteTime abstime, AbsoluteTime leeway) 413 { 414 AbsoluteTime end; 415 clock_continuoustime_interval_to_deadline(abstime, &end); 416 return wakeAtTime(options, end, leeway); 417 418 } 419 420 IOReturn IOTimerEventSource::wakeAtTimeTicks(UInt32 ticks) 421 { 422 return wakeAtTime(ticks, kTickScale); 423 } 424 425 IOReturn IOTimerEventSource::wakeAtTimeMS(UInt32 ms) 426 { 427 return wakeAtTime(ms, kMillisecondScale); 428 } 429 430 IOReturn IOTimerEventSource::wakeAtTimeUS(UInt32 us) 431 { 432 return wakeAtTime(us, kMicrosecondScale); 433 } 434 435 IOReturn IOTimerEventSource::wakeAtTime(UInt32 inAbstime, UInt32 scale_factor) 436 { 437 AbsoluteTime end; 438 clock_interval_to_absolutetime_interval(inAbstime, scale_factor, &end); 439 440 return wakeAtTime(end); 441 } 442 443 #if !defined(__LP64__) 444 IOReturn IOTimerEventSource::wakeAtTime(mach_timespec_t inAbstime) 445 { 446 AbsoluteTime end, nsecs; 447 448 clock_interval_to_absolutetime_interval 449 (inAbstime.tv_nsec, kNanosecondScale, &nsecs); 450 clock_interval_to_absolutetime_interval 451 (inAbstime.tv_sec, kSecondScale, &end); 452 ADD_ABSOLUTETIME(&end, &nsecs); 453 454 return wakeAtTime(end); 455 } 456 #endif 457 458 void IOTimerEventSource::setWorkLoop(IOWorkLoop *inWorkLoop) 459 { 460 super::setWorkLoop(inWorkLoop); 461 if ( enabled && AbsoluteTime_to_scalar(&abstime) && workLoop ) 462 wakeAtTime(abstime); 463 } 464 465 IOReturn IOTimerEventSource::wakeAtTime(AbsoluteTime inAbstime) 466 { 467 return wakeAtTime(0, inAbstime, 0); 468 } 469 470 IOReturn IOTimerEventSource::wakeAtTime(uint32_t options, AbsoluteTime inAbstime, AbsoluteTime leeway) 471 { 472 if (!action) 473 return kIOReturnNoResources; 474 475 abstime = inAbstime; 476 if ( enabled && AbsoluteTime_to_scalar(&inAbstime) && AbsoluteTime_to_scalar(&abstime) && workLoop ) 477 { 478 uint32_t tcoptions = 0; 479 480 if (kIOTimeOptionsWithLeeway & options) tcoptions |= THREAD_CALL_DELAY_LEEWAY; 481 if (kIOTimeOptionsContinuous & options) tcoptions |= THREAD_CALL_CONTINUOUS; 482 483 if (reserved) 484 { 485 if (kPassive & flags) 486 { 487 retain(); 488 workLoop->retain(); 489 } 490 reserved->workLoop = workLoop; 491 reserved->calloutGeneration++; 492 if (thread_call_enter_delayed_with_leeway((thread_call_t) calloutEntry, 493 (void *)(uintptr_t) reserved->calloutGeneration, inAbstime, leeway, tcoptions) 494 && (kPassive & flags)) 495 { 496 release(); 497 workLoop->release(); 498 } 499 } 500 else 501 { 502 thread_call_enter_delayed_with_leeway((thread_call_t) calloutEntry, 503 NULL, inAbstime, leeway, tcoptions); 504 } 505 } 506 507 return kIOReturnSuccess; 508 } 509