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