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 #include <IOKit/IOInterruptEventSource.h>
30 #include <IOKit/IOKitDebug.h>
31 #include <IOKit/IOLib.h>
32 #include <IOKit/IOService.h>
33 #include <IOKit/IOInterrupts.h>
34 #include <IOKit/IOTimeStamp.h>
35 #include <IOKit/IOWorkLoop.h>
36 #include <IOKit/IOInterruptAccountingPrivate.h>
40 #define IOStatisticsInitializeCounter() \
42 IOStatistics::setCounterType(IOEventSource::reserved->counter, kIOStatisticsInterruptEventSourceCounter); \
45 #define IOStatisticsCheckForWork() \
47 IOStatistics::countInterruptCheckForWork(IOEventSource::reserved->counter); \
50 #define IOStatisticsInterrupt() \
52 IOStatistics::countInterrupt(IOEventSource::reserved->counter); \
57 #define IOStatisticsInitializeCounter()
58 #define IOStatisticsCheckForWork()
59 #define IOStatisticsInterrupt()
63 #define super IOEventSource
65 OSDefineMetaClassAndStructors(IOInterruptEventSource
, IOEventSource
)
66 OSMetaClassDefineReservedUnused(IOInterruptEventSource
, 0);
67 OSMetaClassDefineReservedUnused(IOInterruptEventSource
, 1);
68 OSMetaClassDefineReservedUnused(IOInterruptEventSource
, 2);
69 OSMetaClassDefineReservedUnused(IOInterruptEventSource
, 3);
70 OSMetaClassDefineReservedUnused(IOInterruptEventSource
, 4);
71 OSMetaClassDefineReservedUnused(IOInterruptEventSource
, 5);
72 OSMetaClassDefineReservedUnused(IOInterruptEventSource
, 6);
73 OSMetaClassDefineReservedUnused(IOInterruptEventSource
, 7);
76 IOInterruptEventSource::init(OSObject
*inOwner
,
78 IOService
*inProvider
,
83 if (!super::init(inOwner
, (IOEventSourceAction
) inAction
)) {
87 reserved
= IONew(ExpansionData
, 1);
93 bzero(reserved
, sizeof(ExpansionData
));
95 provider
= inProvider
;
96 producerCount
= consumerCount
= 0;
97 autoDisable
= explicitDisable
= false;
98 intIndex
= ~inIntIndex
;
100 // Assumes inOwner holds a reference(retain) on the provider
102 if (IA_ANY_STATISTICS_ENABLED
) {
104 * We only treat this as an "interrupt" if it has a provider; if it does,
105 * set up the objects necessary to track interrupt statistics. Interrupt
106 * event sources without providers are most likely being used as simple
107 * event source in order to poke at workloops and kick off work.
109 * We also avoid try to avoid interrupt accounting overhead if none of
110 * the statistics are enabled.
112 reserved
->statistics
= IONew(IOInterruptAccountingData
, 1);
114 if (!reserved
->statistics
) {
116 * We rely on the free() routine to clean up after us if init fails
122 bzero(reserved
->statistics
, sizeof(IOInterruptAccountingData
));
124 reserved
->statistics
->owner
= this;
127 res
= (kIOReturnSuccess
== registerInterruptHandler(inProvider
, inIntIndex
));
130 intIndex
= inIntIndex
;
134 IOStatisticsInitializeCounter();
140 IOInterruptEventSource::registerInterruptHandler(IOService
*inProvider
,
145 IOInterruptAction intHandler
;
147 ret
= inProvider
->getInterruptType(inIntIndex
, &intType
);
148 if (kIOReturnSuccess
!= ret
) {
152 autoDisable
= (intType
== kIOInterruptTypeLevel
);
154 intHandler
= OSMemberFunctionCast(IOInterruptAction
,
155 this, &IOInterruptEventSource::disableInterruptOccurred
);
157 intHandler
= OSMemberFunctionCast(IOInterruptAction
,
158 this, &IOInterruptEventSource::normalInterruptOccurred
);
161 ret
= provider
->registerInterrupt(inIntIndex
, this, intHandler
);
164 * Add statistics to the provider. The setWorkLoop convention should ensure
165 * that we always go down the unregister path before we register (outside of
166 * init()), so we don't have to worry that we will invoke addInterruptStatistics
169 if ((ret
== kIOReturnSuccess
) && (reserved
->statistics
)) {
171 * Stash the normal index value, for the sake of debugging.
173 reserved
->statistics
->interruptIndex
= inIntIndex
;
176 * We need to hook the interrupt information up to the provider so that it
177 * can find the statistics for this interrupt when desired. The provider is
178 * responsible for maintaining the reporter for a particular interrupt, and
179 * needs a handle on the statistics so that it can request that the reporter
180 * be updated as needed. Errors are considered "soft" for the moment (it
181 * will either panic, or fail in a way such that we can still service the
184 provider
->addInterruptStatistics(reserved
->statistics
, inIntIndex
);
187 * Add the statistics object to the global list of statistics objects; this
188 * is an aid to debugging (we can trivially find statistics for all eligible
189 * interrupts, and dump them; potentially helpful if the system is wedged
190 * due to interrupt activity).
192 interruptAccountingDataAddToList(reserved
->statistics
);
199 IOInterruptEventSource::unregisterInterruptHandler(IOService
*inProvider
,
202 if (reserved
->statistics
) {
203 interruptAccountingDataRemoveFromList(reserved
->statistics
);
204 provider
->removeInterruptStatistics(reserved
->statistics
->interruptIndex
);
207 provider
->unregisterInterrupt(inIntIndex
);
211 IOInterruptEventSource
*
212 IOInterruptEventSource::interruptEventSource(OSObject
*inOwner
,
214 IOService
*inProvider
,
217 IOInterruptEventSource
*me
= new IOInterruptEventSource
;
219 if (me
&& !me
->init(inOwner
, inAction
, inProvider
, inIntIndex
)) {
227 IOInterruptEventSource
*
228 IOInterruptEventSource::interruptEventSource(OSObject
*inOwner
,
229 IOService
*inProvider
,
231 ActionBlock inAction
)
233 IOInterruptEventSource
* ies
;
234 ies
= IOInterruptEventSource::interruptEventSource(inOwner
, (Action
) NULL
, inProvider
, inIntIndex
);
236 ies
->setActionBlock((IOEventSource::ActionBlock
) inAction
);
243 IOInterruptEventSource::free()
245 if (provider
&& intIndex
>= 0) {
246 unregisterInterruptHandler(provider
, intIndex
);
250 if (reserved
->statistics
) {
251 IODelete(reserved
->statistics
, IOInterruptAccountingData
, 1);
254 IODelete(reserved
, ExpansionData
, 1);
261 IOInterruptEventSource::enable()
263 if (provider
&& intIndex
>= 0) {
264 provider
->enableInterrupt(intIndex
);
265 explicitDisable
= false;
271 IOInterruptEventSource::disable()
273 if (provider
&& intIndex
>= 0) {
274 provider
->disableInterrupt(intIndex
);
275 explicitDisable
= true;
281 IOInterruptEventSource::setWorkLoop(IOWorkLoop
*inWorkLoop
)
284 super::setWorkLoop(inWorkLoop
);
291 * It isn't necessarily safe to wait until free() to unregister the interrupt;
292 * our provider may disappear.
294 unregisterInterruptHandler(provider
, intIndex
);
295 intIndex
= ~intIndex
;
297 } else if ((intIndex
< 0) && (kIOReturnSuccess
== registerInterruptHandler(provider
, ~intIndex
))) {
298 intIndex
= ~intIndex
;
303 super::setWorkLoop(inWorkLoop
);
308 IOInterruptEventSource::getProvider() const
314 IOInterruptEventSource::getIntIndex() const
320 IOInterruptEventSource::getAutoDisable() const
326 IOInterruptEventSource::checkForWork()
328 uint64_t startSystemTime
= 0;
329 uint64_t endSystemTime
= 0;
330 uint64_t startCPUTime
= 0;
331 uint64_t endCPUTime
= 0;
332 unsigned int cacheProdCount
= producerCount
;
333 int numInts
= cacheProdCount
- consumerCount
;
334 IOInterruptEventAction intAction
= (IOInterruptEventAction
) action
;
335 ActionBlock intActionBlock
= (ActionBlock
) actionBlock
;
336 bool trace
= (gIOKitTrace
& kIOTraceIntEventSource
) ? true : false;
338 IOStatisticsCheckForWork();
342 IOTimeStampStartConstant(IODBG_INTES(IOINTES_ACTION
),
343 VM_KERNEL_ADDRHIDE(intAction
), VM_KERNEL_ADDRHIDE(owner
),
344 VM_KERNEL_ADDRHIDE(this), VM_KERNEL_ADDRHIDE(workLoop
));
347 if (reserved
->statistics
) {
348 if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelSystemTimeIndex
)) {
349 startSystemTime
= mach_absolute_time();
352 if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelCPUTimeIndex
)) {
353 startCPUTime
= thread_get_runtime_self();
358 if (kActionBlock
& flags
) {
359 (intActionBlock
)(this, numInts
);
361 (*intAction
)(owner
, this, numInts
);
364 if (reserved
->statistics
) {
365 if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelCountIndex
)) {
366 IA_ADD_VALUE(&reserved
->statistics
->interruptStatistics
[kInterruptAccountingSecondLevelCountIndex
], 1);
369 if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelCPUTimeIndex
)) {
370 endCPUTime
= thread_get_runtime_self();
371 IA_ADD_VALUE(&reserved
->statistics
->interruptStatistics
[kInterruptAccountingSecondLevelCPUTimeIndex
], endCPUTime
- startCPUTime
);
374 if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelSystemTimeIndex
)) {
375 endSystemTime
= mach_absolute_time();
376 IA_ADD_VALUE(&reserved
->statistics
->interruptStatistics
[kInterruptAccountingSecondLevelSystemTimeIndex
], endSystemTime
- startSystemTime
);
381 IOTimeStampEndConstant(IODBG_INTES(IOINTES_ACTION
),
382 VM_KERNEL_ADDRHIDE(intAction
), VM_KERNEL_ADDRHIDE(owner
),
383 VM_KERNEL_ADDRHIDE(this), VM_KERNEL_ADDRHIDE(workLoop
));
386 consumerCount
= cacheProdCount
;
387 if (autoDisable
&& !explicitDisable
) {
390 } else if (numInts
< 0) {
392 IOTimeStampStartConstant(IODBG_INTES(IOINTES_ACTION
),
393 VM_KERNEL_ADDRHIDE(intAction
), VM_KERNEL_ADDRHIDE(owner
),
394 VM_KERNEL_ADDRHIDE(this), VM_KERNEL_ADDRHIDE(workLoop
));
397 if (reserved
->statistics
) {
398 if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelSystemTimeIndex
)) {
399 startSystemTime
= mach_absolute_time();
402 if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelCPUTimeIndex
)) {
403 startCPUTime
= thread_get_runtime_self();
408 if (kActionBlock
& flags
) {
409 (intActionBlock
)(this, numInts
);
411 (*intAction
)(owner
, this, numInts
);
414 if (reserved
->statistics
) {
415 if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelCountIndex
)) {
416 IA_ADD_VALUE(&reserved
->statistics
->interruptStatistics
[kInterruptAccountingSecondLevelCountIndex
], 1);
419 if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelCPUTimeIndex
)) {
420 endCPUTime
= thread_get_runtime_self();
421 IA_ADD_VALUE(&reserved
->statistics
->interruptStatistics
[kInterruptAccountingSecondLevelCPUTimeIndex
], endCPUTime
- startCPUTime
);
424 if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelSystemTimeIndex
)) {
425 endSystemTime
= mach_absolute_time();
426 IA_ADD_VALUE(&reserved
->statistics
->interruptStatistics
[kInterruptAccountingSecondLevelSystemTimeIndex
], endSystemTime
- startSystemTime
);
431 IOTimeStampEndConstant(IODBG_INTES(IOINTES_ACTION
),
432 VM_KERNEL_ADDRHIDE(intAction
), VM_KERNEL_ADDRHIDE(owner
),
433 VM_KERNEL_ADDRHIDE(this), VM_KERNEL_ADDRHIDE(workLoop
));
436 consumerCount
= cacheProdCount
;
437 if (autoDisable
&& !explicitDisable
) {
446 IOInterruptEventSource::normalInterruptOccurred
447 (void */
*refcon*/
, IOService */
*prov*/
, int /*source*/)
449 bool trace
= (gIOKitTrace
& kIOTraceIntEventSource
) ? true : false;
451 IOStatisticsInterrupt();
455 IOTimeStampStartConstant(IODBG_INTES(IOINTES_SEMA
), VM_KERNEL_ADDRHIDE(this), VM_KERNEL_ADDRHIDE(owner
));
458 if (reserved
->statistics
) {
459 if (reserved
->statistics
->enablePrimaryTimestamp
) {
460 reserved
->statistics
->primaryTimestamp
= mach_absolute_time();
462 if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingFirstLevelCountIndex
)) {
463 IA_ADD_VALUE(&reserved
->statistics
->interruptStatistics
[kInterruptAccountingFirstLevelCountIndex
], 1);
467 signalWorkAvailable();
470 IOTimeStampEndConstant(IODBG_INTES(IOINTES_SEMA
), VM_KERNEL_ADDRHIDE(this), VM_KERNEL_ADDRHIDE(owner
));
475 IOInterruptEventSource::disableInterruptOccurred
476 (void */
*refcon*/
, IOService
*prov
, int source
)
478 bool trace
= (gIOKitTrace
& kIOTraceIntEventSource
) ? true : false;
480 prov
->disableInterrupt(source
); /* disable the interrupt */
482 IOStatisticsInterrupt();
486 IOTimeStampStartConstant(IODBG_INTES(IOINTES_SEMA
), VM_KERNEL_ADDRHIDE(this), VM_KERNEL_ADDRHIDE(owner
));
489 if (reserved
->statistics
) {
490 if (reserved
->statistics
->enablePrimaryTimestamp
) {
491 reserved
->statistics
->primaryTimestamp
= mach_absolute_time();
493 if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingFirstLevelCountIndex
)) {
494 IA_ADD_VALUE(&reserved
->statistics
->interruptStatistics
[kInterruptAccountingFirstLevelCountIndex
], 1);
498 signalWorkAvailable();
501 IOTimeStampEndConstant(IODBG_INTES(IOINTES_SEMA
), VM_KERNEL_ADDRHIDE(this), VM_KERNEL_ADDRHIDE(owner
));
506 IOInterruptEventSource::interruptOccurred
507 (void *_refcon
, IOService
*prov
, int source
)
509 if (autoDisable
&& prov
) {
510 disableInterruptOccurred(_refcon
, prov
, source
);
512 normalInterruptOccurred(_refcon
, prov
, source
);
517 IOInterruptEventSource::warmCPU
520 return ml_interrupt_prewarm(abstime
);
524 IOInterruptEventSource::enablePrimaryInterruptTimestamp(bool enable
)
526 if (reserved
->statistics
) {
527 reserved
->statistics
->enablePrimaryTimestamp
= enable
;
532 IOInterruptEventSource::getPimaryInterruptTimestamp()
534 if (reserved
->statistics
&& reserved
->statistics
->enablePrimaryTimestamp
) {
535 return reserved
->statistics
->primaryTimestamp
;