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 | |