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 OSMetaClassDefineReservedUnused(IOTimerEventSource, 0); 51 OSMetaClassDefineReservedUnused(IOTimerEventSource, 1); 52 OSMetaClassDefineReservedUnused(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 void IOTimerEventSource::timeout(void *self) 99 { 100 IOTimerEventSource *me = (IOTimerEventSource *) self; 101 102 IOStatisticsTimeout(); 103 104 if (me->enabled && me->action) 105 { 106 IOWorkLoop * 107 wl = me->workLoop; 108 if (wl) 109 { 110 Action doit; 111 wl->closeGate(); 112 IOStatisticsCloseGate(); 113 doit = (Action) me->action; 114 if (doit && me->enabled && AbsoluteTime_to_scalar(&me->abstime)) 115 { 116 bool trace = (gIOKitTrace & kIOTraceTimers) ? true : false; 117 118 if (trace) 119 IOTimeStampStartConstant(IODBG_TIMES(IOTIMES_ACTION), 120 VM_KERNEL_UNSLIDE(doit), (uintptr_t) me->owner); 121 122 (*doit)(me->owner, me); 123 #if CONFIG_DTRACE 124 DTRACE_TMR3(iotescallout__expire, Action, doit, OSObject, me->owner, void, me->workLoop); 125 #endif 126 127 if (trace) 128 IOTimeStampEndConstant(IODBG_TIMES(IOTIMES_ACTION), 129 VM_KERNEL_UNSLIDE(doit), (uintptr_t) me->owner); 130 } 131 IOStatisticsOpenGate(); 132 wl->openGate(); 133 } 134 } 135 } 136 137 void IOTimerEventSource::timeoutAndRelease(void * self, void * c) 138 { 139 IOTimerEventSource *me = (IOTimerEventSource *) self; 140 /* The second parameter (a pointer) gets abused to carry an SInt32, so on LP64, "count" 141 must be cast to "long" before, in order to tell GCC we're not truncating a pointer. */ 142 SInt32 count = (SInt32) (long) c; 143 144 IOStatisticsTimeout(); 145 146 if (me->enabled && me->action) 147 { 148 IOWorkLoop * 149 wl = me->reserved->workLoop; 150 if (wl) 151 { 152 Action doit; 153 wl->closeGate(); 154 IOStatisticsCloseGate(); 155 doit = (Action) me->action; 156 if (doit && (me->reserved->calloutGeneration == count)) 157 { 158 bool trace = (gIOKitTrace & kIOTraceTimers) ? true : false; 159 160 if (trace) 161 IOTimeStampStartConstant(IODBG_TIMES(IOTIMES_ACTION), 162 VM_KERNEL_UNSLIDE(doit), (uintptr_t) me->owner); 163 164 (*doit)(me->owner, me); 165 #if CONFIG_DTRACE 166 DTRACE_TMR3(iotescallout__expire, Action, doit, OSObject, me->owner, void, me->workLoop); 167 #endif 168 169 if (trace) 170 IOTimeStampEndConstant(IODBG_TIMES(IOTIMES_ACTION), 171 VM_KERNEL_UNSLIDE(doit), (uintptr_t) me->owner); 172 } 173 IOStatisticsOpenGate(); 174 wl->openGate(); 175 } 176 } 177 178 me->reserved->workLoop->release(); 179 me->release(); 180 } 181 182 void IOTimerEventSource::setTimeoutFunc() 183 { 184 // reserved != 0 means IOTimerEventSource::timeoutAndRelease is being used, 185 // not a subclassed implementation 186 reserved = IONew(ExpansionData, 1); 187 calloutEntry = (void *) thread_call_allocate((thread_call_func_t) &IOTimerEventSource::timeoutAndRelease, 188 (thread_call_param_t) this); 189 } 190 191 bool IOTimerEventSource::init(OSObject *inOwner, Action inAction) 192 { 193 if (!super::init(inOwner, (IOEventSource::Action) inAction) ) 194 return false; 195 196 setTimeoutFunc(); 197 if (!calloutEntry) 198 return false; 199 200 IOStatisticsInitializeCounter(); 201 202 return true; 203 } 204 205 IOTimerEventSource * 206 IOTimerEventSource::timerEventSource(OSObject *inOwner, Action inAction) 207 { 208 IOTimerEventSource *me = new IOTimerEventSource; 209 210 if (me && !me->init(inOwner, inAction)) { 211 me->release(); 212 return 0; 213 } 214 215 return me; 216 } 217 218 void IOTimerEventSource::free() 219 { 220 if (calloutEntry) { 221 cancelTimeout(); 222 thread_call_free((thread_call_t) calloutEntry); 223 } 224 225 if (reserved) 226 IODelete(reserved, ExpansionData, 1); 227 228 super::free(); 229 } 230 231 void IOTimerEventSource::cancelTimeout() 232 { 233 if (reserved) 234 reserved->calloutGeneration++; 235 bool active = thread_call_cancel((thread_call_t) calloutEntry); 236 AbsoluteTime_to_scalar(&abstime) = 0; 237 if (active && reserved) 238 { 239 release(); 240 workLoop->release(); 241 } 242 } 243 244 void IOTimerEventSource::enable() 245 { 246 super::enable(); 247 if (kIOReturnSuccess != wakeAtTime(abstime)) 248 super::disable(); // Problem re-scheduling timeout ignore enable 249 } 250 251 void IOTimerEventSource::disable() 252 { 253 if (reserved) 254 reserved->calloutGeneration++; 255 bool active = thread_call_cancel((thread_call_t) calloutEntry); 256 super::disable(); 257 if (active && reserved) 258 { 259 release(); 260 workLoop->release(); 261 } 262 } 263 264 IOReturn IOTimerEventSource::setTimeoutTicks(UInt32 ticks) 265 { 266 return setTimeout(ticks, kTickScale); 267 } 268 269 IOReturn IOTimerEventSource::setTimeoutMS(UInt32 ms) 270 { 271 return setTimeout(ms, kMillisecondScale); 272 } 273 274 IOReturn IOTimerEventSource::setTimeoutUS(UInt32 us) 275 { 276 return setTimeout(us, kMicrosecondScale); 277 } 278 279 IOReturn IOTimerEventSource::setTimeout(UInt32 interval, UInt32 scale_factor) 280 { 281 AbsoluteTime end; 282 283 clock_interval_to_deadline(interval, scale_factor, &end); 284 return wakeAtTime(end); 285 } 286 287 #if !defined(__LP64__) 288 IOReturn IOTimerEventSource::setTimeout(mach_timespec_t interval) 289 { 290 AbsoluteTime end, nsecs; 291 292 clock_interval_to_absolutetime_interval 293 (interval.tv_nsec, kNanosecondScale, &nsecs); 294 clock_interval_to_deadline 295 (interval.tv_sec, NSEC_PER_SEC, &end); 296 ADD_ABSOLUTETIME(&end, &nsecs); 297 298 return wakeAtTime(end); 299 } 300 #endif 301 302 IOReturn IOTimerEventSource::setTimeout(AbsoluteTime interval) 303 { 304 AbsoluteTime end; 305 306 clock_get_uptime(&end); 307 ADD_ABSOLUTETIME(&end, &interval); 308 309 return wakeAtTime(end); 310 } 311 312 IOReturn IOTimerEventSource::wakeAtTimeTicks(UInt32 ticks) 313 { 314 return wakeAtTime(ticks, kTickScale); 315 } 316 317 IOReturn IOTimerEventSource::wakeAtTimeMS(UInt32 ms) 318 { 319 return wakeAtTime(ms, kMillisecondScale); 320 } 321 322 IOReturn IOTimerEventSource::wakeAtTimeUS(UInt32 us) 323 { 324 return wakeAtTime(us, kMicrosecondScale); 325 } 326 327 IOReturn IOTimerEventSource::wakeAtTime(UInt32 inAbstime, UInt32 scale_factor) 328 { 329 AbsoluteTime end; 330 clock_interval_to_absolutetime_interval(inAbstime, scale_factor, &end); 331 332 return wakeAtTime(end); 333 } 334 335 #if !defined(__LP64__) 336 IOReturn IOTimerEventSource::wakeAtTime(mach_timespec_t inAbstime) 337 { 338 AbsoluteTime end, nsecs; 339 340 clock_interval_to_absolutetime_interval 341 (inAbstime.tv_nsec, kNanosecondScale, &nsecs); 342 clock_interval_to_absolutetime_interval 343 (inAbstime.tv_sec, kSecondScale, &end); 344 ADD_ABSOLUTETIME(&end, &nsecs); 345 346 return wakeAtTime(end); 347 } 348 #endif 349 350 void IOTimerEventSource::setWorkLoop(IOWorkLoop *inWorkLoop) 351 { 352 super::setWorkLoop(inWorkLoop); 353 if ( enabled && AbsoluteTime_to_scalar(&abstime) && workLoop ) 354 wakeAtTime(abstime); 355 } 356 357 IOReturn IOTimerEventSource::wakeAtTime(AbsoluteTime inAbstime) 358 { 359 if (!action) 360 return kIOReturnNoResources; 361 362 abstime = inAbstime; 363 if ( enabled && AbsoluteTime_to_scalar(&inAbstime) && AbsoluteTime_to_scalar(&abstime) && workLoop ) 364 { 365 if (reserved) 366 { 367 retain(); 368 workLoop->retain(); 369 reserved->workLoop = workLoop; 370 reserved->calloutGeneration++; 371 if (thread_call_enter1_delayed((thread_call_t) calloutEntry, 372 (void *)(uintptr_t) reserved->calloutGeneration, inAbstime)) 373 { 374 release(); 375 workLoop->release(); 376 } 377 } 378 else 379 thread_call_enter_delayed((thread_call_t) calloutEntry, inAbstime); 380 } 381 382 return kIOReturnSuccess; 383 } 384