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
52OSDefineMetaClassAndStructors(IOTimerEventSource, IOEventSource)
53OSMetaClassDefineReservedUsed(IOTimerEventSource, 0);
54OSMetaClassDefineReservedUsed(IOTimerEventSource, 1);
55OSMetaClassDefineReservedUsed(IOTimerEventSource, 2);
56OSMetaClassDefineReservedUnused(IOTimerEventSource, 3);
57OSMetaClassDefineReservedUnused(IOTimerEventSource, 4);
58OSMetaClassDefineReservedUnused(IOTimerEventSource, 5);
59OSMetaClassDefineReservedUnused(IOTimerEventSource, 6);
60OSMetaClassDefineReservedUnused(IOTimerEventSource, 7);
61
62#if IOKITSTATS
63
64#define IOStatisticsInitializeCounter() \
65do { \
66 IOStatistics::setCounterType(IOEventSource::reserved->counter, kIOStatisticsTimerEventSourceCounter); \
67} while (0)
68
69#define IOStatisticsOpenGate() \
70do { \
71 IOStatistics::countOpenGate(me->IOEventSource::reserved->counter); \
72} while (0)
73
74#define IOStatisticsCloseGate() \
75do { \
76 IOStatistics::countCloseGate(me->IOEventSource::reserved->counter); \
77} while (0)
78
79#define IOStatisticsTimeout() \
80do { \
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
103IOTimerEventSource::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
124void 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
150void 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
184bool 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
199void 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
209void 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
277bool 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
291bool IOTimerEventSource::init(uint32_t options, OSObject *inOwner, Action inAction)
292{
293 abstime = options;
294 return (init(inOwner, inAction));
295}
296
297IOTimerEventSource *
298IOTimerEventSource::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
310IOTimerEventSource *
311IOTimerEventSource::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
322IOTimerEventSource *
323IOTimerEventSource::timerEventSource(OSObject *inOwner, Action inAction)
324{
325 return (IOTimerEventSource::timerEventSource(
326 kIOTimerEventSourceOptionsPriorityKernelHigh,
327 inOwner, inAction));
328}
329
330void 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
347void 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
360void IOTimerEventSource::enable()
361{
362 super::enable();
363 if (kIOReturnSuccess != wakeAtTime(abstime))
364 super::disable(); // Problem re-scheduling timeout ignore enable
365}
366
367void 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
380IOReturn IOTimerEventSource::setTimeoutTicks(UInt32 ticks)
381{
382 return setTimeout(ticks, kTickScale);
383}
384
385IOReturn IOTimerEventSource::setTimeoutMS(UInt32 ms)
386{
387 return setTimeout(ms, kMillisecondScale);
388}
389
390IOReturn IOTimerEventSource::setTimeoutUS(UInt32 us)
391{
392 return setTimeout(us, kMicrosecondScale);
393}
394
395IOReturn 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__)
404IOReturn 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
418IOReturn IOTimerEventSource::setTimeout(AbsoluteTime interval)
419{
420 AbsoluteTime end;
421 clock_absolutetime_interval_to_deadline(interval, &end);
422 return wakeAtTime(end);
423}
424
425IOReturn 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
437IOReturn IOTimerEventSource::wakeAtTimeTicks(UInt32 ticks)
438{
439 return wakeAtTime(ticks, kTickScale);
440}
441
442IOReturn IOTimerEventSource::wakeAtTimeMS(UInt32 ms)
443{
444 return wakeAtTime(ms, kMillisecondScale);
445}
446
447IOReturn IOTimerEventSource::wakeAtTimeUS(UInt32 us)
448{
449 return wakeAtTime(us, kMicrosecondScale);
450}
451
452IOReturn 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__)
461IOReturn 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
475void IOTimerEventSource::setWorkLoop(IOWorkLoop *inWorkLoop)
476{
477 super::setWorkLoop(inWorkLoop);
478 if ( enabled && AbsoluteTime_to_scalar(&abstime) && workLoop )
479 wakeAtTime(abstime);
480}
481
482IOReturn IOTimerEventSource::wakeAtTime(AbsoluteTime inAbstime)
483{
484 return wakeAtTime(0, inAbstime, 0);
485}
486
487IOReturn 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