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);
75 bool IOInterruptEventSource::init(OSObject
*inOwner
,
77 IOService
*inProvider
,
82 if ( !super::init(inOwner
, (IOEventSourceAction
) inAction
) )
85 reserved
= IONew(ExpansionData
, 1);
91 bzero(reserved
, sizeof(ExpansionData
));
93 provider
= inProvider
;
94 producerCount
= consumerCount
= 0;
95 autoDisable
= explicitDisable
= false;
96 intIndex
= ~inIntIndex
;
98 // Assumes inOwner holds a reference(retain) on the provider
100 if (IA_ANY_STATISTICS_ENABLED
) {
102 * We only treat this as an "interrupt" if it has a provider; if it does,
103 * set up the objects necessary to track interrupt statistics. Interrupt
104 * event sources without providers are most likely being used as simple
105 * event source in order to poke at workloops and kick off work.
107 * We also avoid try to avoid interrupt accounting overhead if none of
108 * the statistics are enabled.
110 reserved
->statistics
= IONew(IOInterruptAccountingData
, 1);
112 if (!reserved
->statistics
) {
114 * We rely on the free() routine to clean up after us if init fails
120 bzero(reserved
->statistics
, sizeof(IOInterruptAccountingData
));
122 reserved
->statistics
->owner
= this;
125 res
= (kIOReturnSuccess
== registerInterruptHandler(inProvider
, inIntIndex
));
128 intIndex
= inIntIndex
;
132 IOStatisticsInitializeCounter();
137 IOReturn
IOInterruptEventSource::registerInterruptHandler(IOService
*inProvider
,
142 IOInterruptAction intHandler
;
144 ret
= inProvider
->getInterruptType(inIntIndex
, &intType
);
145 if (kIOReturnSuccess
!= ret
)
148 autoDisable
= (intType
== kIOInterruptTypeLevel
);
150 intHandler
= OSMemberFunctionCast(IOInterruptAction
,
151 this, &IOInterruptEventSource::disableInterruptOccurred
);
154 intHandler
= OSMemberFunctionCast(IOInterruptAction
,
155 this, &IOInterruptEventSource::normalInterruptOccurred
);
157 ret
= provider
->registerInterrupt(inIntIndex
, this, intHandler
);
160 * Add statistics to the provider. The setWorkLoop convention should ensure
161 * that we always go down the unregister path before we register (outside of
162 * init()), so we don't have to worry that we will invoke addInterruptStatistics
165 if ((ret
== kIOReturnSuccess
) && (reserved
->statistics
)) {
167 * Stash the normal index value, for the sake of debugging.
169 reserved
->statistics
->interruptIndex
= inIntIndex
;
172 * We need to hook the interrupt information up to the provider so that it
173 * can find the statistics for this interrupt when desired. The provider is
174 * responsible for maintaining the reporter for a particular interrupt, and
175 * needs a handle on the statistics so that it can request that the reporter
176 * be updated as needed. Errors are considered "soft" for the moment (it
177 * will either panic, or fail in a way such that we can still service the
180 provider
->addInterruptStatistics(reserved
->statistics
, inIntIndex
);
183 * Add the statistics object to the global list of statistics objects; this
184 * is an aid to debugging (we can trivially find statistics for all eligible
185 * interrupts, and dump them; potentially helpful if the system is wedged
186 * due to interrupt activity).
188 interruptAccountingDataAddToList(reserved
->statistics
);
195 IOInterruptEventSource::unregisterInterruptHandler(IOService
*inProvider
,
198 if (reserved
->statistics
) {
199 interruptAccountingDataRemoveFromList(reserved
->statistics
);
200 provider
->removeInterruptStatistics(reserved
->statistics
->interruptIndex
);
203 provider
->unregisterInterrupt(inIntIndex
);
207 IOInterruptEventSource
*
208 IOInterruptEventSource::interruptEventSource(OSObject
*inOwner
,
210 IOService
*inProvider
,
213 IOInterruptEventSource
*me
= new IOInterruptEventSource
;
215 if (me
&& !me
->init(inOwner
, inAction
, inProvider
, inIntIndex
)) {
223 void IOInterruptEventSource::free()
225 if (provider
&& intIndex
>= 0)
226 unregisterInterruptHandler(provider
, intIndex
);
229 if (reserved
->statistics
) {
230 IODelete(reserved
->statistics
, IOInterruptAccountingData
, 1);
233 IODelete(reserved
, ExpansionData
, 1);
239 void IOInterruptEventSource::enable()
241 if (provider
&& intIndex
>= 0) {
242 provider
->enableInterrupt(intIndex
);
243 explicitDisable
= false;
248 void IOInterruptEventSource::disable()
250 if (provider
&& intIndex
>= 0) {
251 provider
->disableInterrupt(intIndex
);
252 explicitDisable
= true;
257 void IOInterruptEventSource::setWorkLoop(IOWorkLoop
*inWorkLoop
)
259 if (inWorkLoop
) super::setWorkLoop(inWorkLoop
);
265 * It isn't necessarily safe to wait until free() to unregister the interrupt;
266 * our provider may disappear.
268 unregisterInterruptHandler(provider
, intIndex
);
269 intIndex
= ~intIndex
;
271 } else if ((intIndex
< 0) && (kIOReturnSuccess
== registerInterruptHandler(provider
, ~intIndex
))) {
272 intIndex
= ~intIndex
;
276 if (!inWorkLoop
) super::setWorkLoop(inWorkLoop
);
279 const IOService
*IOInterruptEventSource::getProvider() const
284 int IOInterruptEventSource::getIntIndex() const
289 bool IOInterruptEventSource::getAutoDisable() const
294 bool IOInterruptEventSource::checkForWork()
296 uint64_t startSystemTime
= 0;
297 uint64_t endSystemTime
= 0;
298 uint64_t startCPUTime
= 0;
299 uint64_t endCPUTime
= 0;
300 unsigned int cacheProdCount
= producerCount
;
301 int numInts
= cacheProdCount
- consumerCount
;
302 IOInterruptEventAction intAction
= (IOInterruptEventAction
) action
;
303 bool trace
= (gIOKitTrace
& kIOTraceIntEventSource
) ? true : false;
305 IOStatisticsCheckForWork();
310 IOTimeStampStartConstant(IODBG_INTES(IOINTES_ACTION
),
311 VM_KERNEL_ADDRHIDE(intAction
), VM_KERNEL_ADDRHIDE(owner
),
312 VM_KERNEL_ADDRHIDE(this), VM_KERNEL_ADDRHIDE(workLoop
));
314 if (reserved
->statistics
) {
315 if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelSystemTimeIndex
)) {
316 startSystemTime
= mach_absolute_time();
319 if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelCPUTimeIndex
)) {
320 startCPUTime
= thread_get_runtime_self();
325 (*intAction
)(owner
, this, numInts
);
327 if (reserved
->statistics
) {
328 if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelCountIndex
)) {
329 IA_ADD_VALUE(&reserved
->statistics
->interruptStatistics
[kInterruptAccountingSecondLevelCountIndex
], 1);
332 if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelCPUTimeIndex
)) {
333 endCPUTime
= thread_get_runtime_self();
334 IA_ADD_VALUE(&reserved
->statistics
->interruptStatistics
[kInterruptAccountingSecondLevelCPUTimeIndex
], endCPUTime
- startCPUTime
);
337 if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelSystemTimeIndex
)) {
338 endSystemTime
= mach_absolute_time();
339 IA_ADD_VALUE(&reserved
->statistics
->interruptStatistics
[kInterruptAccountingSecondLevelSystemTimeIndex
], endSystemTime
- startSystemTime
);
344 IOTimeStampEndConstant(IODBG_INTES(IOINTES_ACTION
),
345 VM_KERNEL_ADDRHIDE(intAction
), VM_KERNEL_ADDRHIDE(owner
),
346 VM_KERNEL_ADDRHIDE(this), VM_KERNEL_ADDRHIDE(workLoop
));
348 consumerCount
= cacheProdCount
;
349 if (autoDisable
&& !explicitDisable
)
353 else if ( numInts
< 0 )
356 IOTimeStampStartConstant(IODBG_INTES(IOINTES_ACTION
),
357 VM_KERNEL_ADDRHIDE(intAction
), VM_KERNEL_ADDRHIDE(owner
),
358 VM_KERNEL_ADDRHIDE(this), VM_KERNEL_ADDRHIDE(workLoop
));
360 if (reserved
->statistics
) {
361 if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelSystemTimeIndex
)) {
362 startSystemTime
= mach_absolute_time();
365 if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelCPUTimeIndex
)) {
366 startCPUTime
= thread_get_runtime_self();
371 (*intAction
)(owner
, this, -numInts
);
373 if (reserved
->statistics
) {
374 if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelCountIndex
)) {
375 IA_ADD_VALUE(&reserved
->statistics
->interruptStatistics
[kInterruptAccountingSecondLevelCountIndex
], 1);
378 if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelCPUTimeIndex
)) {
379 endCPUTime
= thread_get_runtime_self();
380 IA_ADD_VALUE(&reserved
->statistics
->interruptStatistics
[kInterruptAccountingSecondLevelCPUTimeIndex
], endCPUTime
- startCPUTime
);
383 if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelSystemTimeIndex
)) {
384 endSystemTime
= mach_absolute_time();
385 IA_ADD_VALUE(&reserved
->statistics
->interruptStatistics
[kInterruptAccountingSecondLevelSystemTimeIndex
], endSystemTime
- startSystemTime
);
390 IOTimeStampEndConstant(IODBG_INTES(IOINTES_ACTION
),
391 VM_KERNEL_ADDRHIDE(intAction
), VM_KERNEL_ADDRHIDE(owner
),
392 VM_KERNEL_ADDRHIDE(this), VM_KERNEL_ADDRHIDE(workLoop
));
394 consumerCount
= cacheProdCount
;
395 if (autoDisable
&& !explicitDisable
)
402 void IOInterruptEventSource::normalInterruptOccurred
403 (void */
*refcon*/
, IOService */
*prov*/
, int /*source*/)
405 bool trace
= (gIOKitTrace
& kIOTraceIntEventSource
) ? true : false;
407 IOStatisticsInterrupt();
411 IOTimeStampStartConstant(IODBG_INTES(IOINTES_SEMA
), VM_KERNEL_ADDRHIDE(this), VM_KERNEL_ADDRHIDE(owner
));
413 if (reserved
->statistics
) {
414 if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingFirstLevelCountIndex
)) {
415 IA_ADD_VALUE(&reserved
->statistics
->interruptStatistics
[kInterruptAccountingFirstLevelCountIndex
], 1);
419 signalWorkAvailable();
422 IOTimeStampEndConstant(IODBG_INTES(IOINTES_SEMA
), VM_KERNEL_ADDRHIDE(this), VM_KERNEL_ADDRHIDE(owner
));
425 void IOInterruptEventSource::disableInterruptOccurred
426 (void */
*refcon*/
, IOService
*prov
, int source
)
428 bool trace
= (gIOKitTrace
& kIOTraceIntEventSource
) ? true : false;
430 prov
->disableInterrupt(source
); /* disable the interrupt */
432 IOStatisticsInterrupt();
436 IOTimeStampStartConstant(IODBG_INTES(IOINTES_SEMA
), VM_KERNEL_ADDRHIDE(this), VM_KERNEL_ADDRHIDE(owner
));
438 if (reserved
->statistics
) {
439 if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingFirstLevelCountIndex
)) {
440 IA_ADD_VALUE(&reserved
->statistics
->interruptStatistics
[kInterruptAccountingFirstLevelCountIndex
], 1);
444 signalWorkAvailable();
447 IOTimeStampEndConstant(IODBG_INTES(IOINTES_SEMA
), VM_KERNEL_ADDRHIDE(this), VM_KERNEL_ADDRHIDE(owner
));
450 void IOInterruptEventSource::interruptOccurred
451 (void *refcon
, IOService
*prov
, int source
)
453 if (autoDisable
&& prov
)
454 disableInterruptOccurred(refcon
, prov
, source
);
456 normalInterruptOccurred(refcon
, prov
, source
);
459 IOReturn
IOInterruptEventSource::warmCPU
463 return ml_interrupt_prewarm(abstime
);