2 * Copyright (c) 1998-2014 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
29 #define IOKIT_ENABLE_SHARED_PTR
32 #include <IOKit/IOInterruptEventSource.h>
33 #include <IOKit/IOKitDebug.h>
34 #include <IOKit/IOLib.h>
35 #include <IOKit/IOService.h>
36 #include <IOKit/IOInterrupts.h>
37 #include <IOKit/IOTimeStamp.h>
38 #include <IOKit/IOWorkLoop.h>
39 #include <IOKit/IOInterruptAccountingPrivate.h>
40 #include <libkern/Block_private.h>
44 #define IOStatisticsInitializeCounter() \
46 IOStatistics::setCounterType(IOEventSource::reserved->counter, kIOStatisticsInterruptEventSourceCounter); \
49 #define IOStatisticsCheckForWork() \
51 IOStatistics::countInterruptCheckForWork(IOEventSource::reserved->counter); \
54 #define IOStatisticsInterrupt() \
56 IOStatistics::countInterrupt(IOEventSource::reserved->counter); \
61 #define IOStatisticsInitializeCounter()
62 #define IOStatisticsCheckForWork()
63 #define IOStatisticsInterrupt()
67 #define super IOEventSource
69 OSDefineMetaClassAndStructors(IOInterruptEventSource
, IOEventSource
)
70 OSMetaClassDefineReservedUnused(IOInterruptEventSource
, 0);
71 OSMetaClassDefineReservedUnused(IOInterruptEventSource
, 1);
72 OSMetaClassDefineReservedUnused(IOInterruptEventSource
, 2);
73 OSMetaClassDefineReservedUnused(IOInterruptEventSource
, 3);
74 OSMetaClassDefineReservedUnused(IOInterruptEventSource
, 4);
75 OSMetaClassDefineReservedUnused(IOInterruptEventSource
, 5);
76 OSMetaClassDefineReservedUnused(IOInterruptEventSource
, 6);
77 OSMetaClassDefineReservedUnused(IOInterruptEventSource
, 7);
80 IOInterruptEventSource::init(OSObject
*inOwner
,
82 IOService
*inProvider
,
87 if (!super::init(inOwner
, (IOEventSourceAction
) inAction
)) {
91 reserved
= IONew(ExpansionData
, 1);
97 bzero(reserved
, sizeof(ExpansionData
));
99 provider
= inProvider
;
100 producerCount
= consumerCount
= 0;
101 autoDisable
= explicitDisable
= false;
102 intIndex
= ~inIntIndex
;
104 // Assumes inOwner holds a reference(retain) on the provider
106 if (IA_ANY_STATISTICS_ENABLED
) {
108 * We only treat this as an "interrupt" if it has a provider; if it does,
109 * set up the objects necessary to track interrupt statistics. Interrupt
110 * event sources without providers are most likely being used as simple
111 * event source in order to poke at workloops and kick off work.
113 * We also avoid try to avoid interrupt accounting overhead if none of
114 * the statistics are enabled.
116 reserved
->statistics
= IONew(IOInterruptAccountingData
, 1);
118 if (!reserved
->statistics
) {
120 * We rely on the free() routine to clean up after us if init fails
126 bzero(reserved
->statistics
, sizeof(IOInterruptAccountingData
));
128 reserved
->statistics
->owner
= this;
131 res
= (kIOReturnSuccess
== registerInterruptHandler(inProvider
, inIntIndex
));
134 intIndex
= inIntIndex
;
138 IOStatisticsInitializeCounter();
144 IOInterruptEventSource::registerInterruptHandler(IOService
*inProvider
,
149 IOInterruptAction intHandler
;
151 ret
= inProvider
->getInterruptType(inIntIndex
, &intType
);
152 if (kIOReturnSuccess
!= ret
) {
156 autoDisable
= (intType
== kIOInterruptTypeLevel
);
158 intHandler
= OSMemberFunctionCast(IOInterruptAction
,
159 this, &IOInterruptEventSource::disableInterruptOccurred
);
161 intHandler
= OSMemberFunctionCast(IOInterruptAction
,
162 this, &IOInterruptEventSource::normalInterruptOccurred
);
165 ret
= provider
->registerInterrupt(inIntIndex
, this, intHandler
);
168 * Add statistics to the provider. The setWorkLoop convention should ensure
169 * that we always go down the unregister path before we register (outside of
170 * init()), so we don't have to worry that we will invoke addInterruptStatistics
173 if ((ret
== kIOReturnSuccess
) && (reserved
->statistics
)) {
175 * Stash the normal index value, for the sake of debugging.
177 reserved
->statistics
->interruptIndex
= inIntIndex
;
180 * We need to hook the interrupt information up to the provider so that it
181 * can find the statistics for this interrupt when desired. The provider is
182 * responsible for maintaining the reporter for a particular interrupt, and
183 * needs a handle on the statistics so that it can request that the reporter
184 * be updated as needed. Errors are considered "soft" for the moment (it
185 * will either panic, or fail in a way such that we can still service the
188 provider
->addInterruptStatistics(reserved
->statistics
, inIntIndex
);
191 * Add the statistics object to the global list of statistics objects; this
192 * is an aid to debugging (we can trivially find statistics for all eligible
193 * interrupts, and dump them; potentially helpful if the system is wedged
194 * due to interrupt activity).
196 interruptAccountingDataAddToList(reserved
->statistics
);
203 IOInterruptEventSource::unregisterInterruptHandler(IOService
*inProvider
,
206 if (reserved
->statistics
) {
207 interruptAccountingDataRemoveFromList(reserved
->statistics
);
208 provider
->removeInterruptStatistics(reserved
->statistics
->interruptIndex
);
211 provider
->unregisterInterrupt(inIntIndex
);
215 OSSharedPtr
<IOInterruptEventSource
>
216 IOInterruptEventSource::interruptEventSource(OSObject
*inOwner
,
218 IOService
*inProvider
,
221 OSSharedPtr
<IOInterruptEventSource
> me
= OSMakeShared
<IOInterruptEventSource
>();
223 if (me
&& !me
->init(inOwner
, inAction
, inProvider
, inIntIndex
)) {
230 OSSharedPtr
<IOInterruptEventSource
>
231 IOInterruptEventSource::interruptEventSource(OSObject
*inOwner
,
232 IOService
*inProvider
,
234 ActionBlock inAction
)
236 OSSharedPtr
<IOInterruptEventSource
> ies
;
237 ies
= IOInterruptEventSource::interruptEventSource(inOwner
, (Action
) NULL
, inProvider
, inIntIndex
);
239 ies
->setActionBlock((IOEventSource::ActionBlock
) inAction
);
246 IOInterruptEventSource::free()
248 if (provider
&& intIndex
>= 0) {
249 unregisterInterruptHandler(provider
, intIndex
);
253 if (reserved
->statistics
) {
254 IODelete(reserved
->statistics
, IOInterruptAccountingData
, 1);
257 IODelete(reserved
, ExpansionData
, 1);
264 IOInterruptEventSource::enable()
266 if (provider
&& intIndex
>= 0) {
267 provider
->enableInterrupt(intIndex
);
268 explicitDisable
= false;
274 IOInterruptEventSource::disable()
276 if (provider
&& intIndex
>= 0) {
277 provider
->disableInterrupt(intIndex
);
278 explicitDisable
= true;
284 IOInterruptEventSource::setWorkLoop(IOWorkLoop
*inWorkLoop
)
287 super::setWorkLoop(inWorkLoop
);
294 * It isn't necessarily safe to wait until free() to unregister the interrupt;
295 * our provider may disappear.
297 unregisterInterruptHandler(provider
, intIndex
);
298 intIndex
= ~intIndex
;
300 } else if ((intIndex
< 0) && (kIOReturnSuccess
== registerInterruptHandler(provider
, ~intIndex
))) {
301 intIndex
= ~intIndex
;
306 super::setWorkLoop(inWorkLoop
);
311 IOInterruptEventSource::getProvider() const
317 IOInterruptEventSource::getIntIndex() const
323 IOInterruptEventSource::getAutoDisable() const
329 IOInterruptEventSource::checkForWork()
331 uint64_t startSystemTime
= 0;
332 uint64_t endSystemTime
= 0;
333 uint64_t startCPUTime
= 0;
334 uint64_t endCPUTime
= 0;
335 unsigned int cacheProdCount
= producerCount
;
336 int numInts
= cacheProdCount
- consumerCount
;
337 IOEventSource::Action intAction
= action
;
338 ActionBlock intActionBlock
= (ActionBlock
) actionBlock
;
340 bool trace
= (gIOKitTrace
& kIOTraceIntEventSource
) ? true : false;
342 if (kActionBlock
& flags
) {
343 address
= ptrauth_nop_cast(void *, _Block_get_invoke_fn((struct Block_layout
*)intActionBlock
));
345 address
= ptrauth_nop_cast(void *, intAction
);
348 IOStatisticsCheckForWork();
352 IOTimeStampStartConstant(IODBG_INTES(IOINTES_ACTION
),
353 VM_KERNEL_ADDRHIDE(address
),
354 VM_KERNEL_ADDRHIDE(owner
),
355 VM_KERNEL_ADDRHIDE(this), VM_KERNEL_ADDRHIDE(workLoop
));
358 if (reserved
->statistics
) {
359 if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelSystemTimeIndex
)) {
360 startSystemTime
= mach_absolute_time();
363 if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelCPUTimeIndex
)) {
364 startCPUTime
= thread_get_runtime_self();
369 if (kActionBlock
& flags
) {
370 (intActionBlock
)(this, numInts
);
372 ((IOInterruptEventAction
)intAction
)(owner
, this, numInts
);
375 if (reserved
->statistics
) {
376 if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelCountIndex
)) {
377 IA_ADD_VALUE(&reserved
->statistics
->interruptStatistics
[kInterruptAccountingSecondLevelCountIndex
], 1);
380 if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelCPUTimeIndex
)) {
381 endCPUTime
= thread_get_runtime_self();
382 IA_ADD_VALUE(&reserved
->statistics
->interruptStatistics
[kInterruptAccountingSecondLevelCPUTimeIndex
], endCPUTime
- startCPUTime
);
385 if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelSystemTimeIndex
)) {
386 endSystemTime
= mach_absolute_time();
387 IA_ADD_VALUE(&reserved
->statistics
->interruptStatistics
[kInterruptAccountingSecondLevelSystemTimeIndex
], endSystemTime
- startSystemTime
);
392 IOTimeStampEndConstant(IODBG_INTES(IOINTES_ACTION
),
393 VM_KERNEL_ADDRHIDE(address
),
394 VM_KERNEL_ADDRHIDE(owner
),
395 VM_KERNEL_ADDRHIDE(this), VM_KERNEL_ADDRHIDE(workLoop
));
398 consumerCount
= cacheProdCount
;
399 if (autoDisable
&& !explicitDisable
) {
402 } else if (numInts
< 0) {
404 IOTimeStampStartConstant(IODBG_INTES(IOINTES_ACTION
),
405 VM_KERNEL_ADDRHIDE(address
),
406 VM_KERNEL_ADDRHIDE(owner
),
407 VM_KERNEL_ADDRHIDE(this), VM_KERNEL_ADDRHIDE(workLoop
));
410 if (reserved
->statistics
) {
411 if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelSystemTimeIndex
)) {
412 startSystemTime
= mach_absolute_time();
415 if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelCPUTimeIndex
)) {
416 startCPUTime
= thread_get_runtime_self();
421 if (kActionBlock
& flags
) {
422 (intActionBlock
)(this, numInts
);
424 ((IOInterruptEventAction
)intAction
)(owner
, this, numInts
);
427 if (reserved
->statistics
) {
428 if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelCountIndex
)) {
429 IA_ADD_VALUE(&reserved
->statistics
->interruptStatistics
[kInterruptAccountingSecondLevelCountIndex
], 1);
432 if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelCPUTimeIndex
)) {
433 endCPUTime
= thread_get_runtime_self();
434 IA_ADD_VALUE(&reserved
->statistics
->interruptStatistics
[kInterruptAccountingSecondLevelCPUTimeIndex
], endCPUTime
- startCPUTime
);
437 if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelSystemTimeIndex
)) {
438 endSystemTime
= mach_absolute_time();
439 IA_ADD_VALUE(&reserved
->statistics
->interruptStatistics
[kInterruptAccountingSecondLevelSystemTimeIndex
], endSystemTime
- startSystemTime
);
444 IOTimeStampEndConstant(IODBG_INTES(IOINTES_ACTION
),
445 VM_KERNEL_ADDRHIDE(address
),
446 VM_KERNEL_ADDRHIDE(owner
),
447 VM_KERNEL_ADDRHIDE(this), VM_KERNEL_ADDRHIDE(workLoop
));
450 consumerCount
= cacheProdCount
;
451 if (autoDisable
&& !explicitDisable
) {
460 IOInterruptEventSource::normalInterruptOccurred
461 (void */
*refcon*/
, IOService */
*prov*/
, int /*source*/)
463 bool trace
= (gIOKitTrace
& kIOTraceIntEventSource
) ? true : false;
465 IOStatisticsInterrupt();
469 IOTimeStampStartConstant(IODBG_INTES(IOINTES_SEMA
), VM_KERNEL_ADDRHIDE(this), VM_KERNEL_ADDRHIDE(owner
));
472 if (reserved
->statistics
) {
473 if (reserved
->statistics
->enablePrimaryTimestamp
) {
474 reserved
->statistics
->primaryTimestamp
= mach_absolute_time();
476 if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingFirstLevelCountIndex
)) {
477 IA_ADD_VALUE(&reserved
->statistics
->interruptStatistics
[kInterruptAccountingFirstLevelCountIndex
], 1);
481 signalWorkAvailable();
484 IOTimeStampEndConstant(IODBG_INTES(IOINTES_SEMA
), VM_KERNEL_ADDRHIDE(this), VM_KERNEL_ADDRHIDE(owner
));
489 IOInterruptEventSource::disableInterruptOccurred
490 (void */
*refcon*/
, IOService
*prov
, int source
)
492 bool trace
= (gIOKitTrace
& kIOTraceIntEventSource
) ? true : false;
494 prov
->disableInterrupt(source
); /* disable the interrupt */
496 IOStatisticsInterrupt();
500 IOTimeStampStartConstant(IODBG_INTES(IOINTES_SEMA
), VM_KERNEL_ADDRHIDE(this), VM_KERNEL_ADDRHIDE(owner
));
503 if (reserved
->statistics
) {
504 if (reserved
->statistics
->enablePrimaryTimestamp
) {
505 reserved
->statistics
->primaryTimestamp
= mach_absolute_time();
507 if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingFirstLevelCountIndex
)) {
508 IA_ADD_VALUE(&reserved
->statistics
->interruptStatistics
[kInterruptAccountingFirstLevelCountIndex
], 1);
512 signalWorkAvailable();
515 IOTimeStampEndConstant(IODBG_INTES(IOINTES_SEMA
), VM_KERNEL_ADDRHIDE(this), VM_KERNEL_ADDRHIDE(owner
));
520 IOInterruptEventSource::interruptOccurred
521 (void *_refcon
, IOService
*prov
, int source
)
523 if (autoDisable
&& prov
) {
524 disableInterruptOccurred(_refcon
, prov
, source
);
526 normalInterruptOccurred(_refcon
, prov
, source
);
531 IOInterruptEventSource::warmCPU
534 return ml_interrupt_prewarm(abstime
);
538 IOInterruptEventSource::enablePrimaryInterruptTimestamp(bool enable
)
540 if (reserved
->statistics
) {
541 reserved
->statistics
->enablePrimaryTimestamp
= enable
;
546 IOInterruptEventSource::getPrimaryInterruptTimestamp()
548 if (reserved
->statistics
&& reserved
->statistics
->enablePrimaryTimestamp
) {
549 return reserved
->statistics
->primaryTimestamp
;