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_UNSLIDE(intAction
), (uintptr_t) owner
, (uintptr_t) this, (uintptr_t) workLoop
);
313 if (reserved
->statistics
) {
314 if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelSystemTimeIndex
)) {
315 startSystemTime
= mach_absolute_time();
318 if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelCPUTimeIndex
)) {
319 startCPUTime
= thread_get_runtime_self();
324 (*intAction
)(owner
, this, numInts
);
326 if (reserved
->statistics
) {
327 if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelCountIndex
)) {
328 IA_ADD_VALUE(&reserved
->statistics
->interruptStatistics
[kInterruptAccountingSecondLevelCountIndex
], 1);
331 if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelCPUTimeIndex
)) {
332 endCPUTime
= thread_get_runtime_self();
333 IA_ADD_VALUE(&reserved
->statistics
->interruptStatistics
[kInterruptAccountingSecondLevelCPUTimeIndex
], endCPUTime
- startCPUTime
);
336 if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelSystemTimeIndex
)) {
337 endSystemTime
= mach_absolute_time();
338 IA_ADD_VALUE(&reserved
->statistics
->interruptStatistics
[kInterruptAccountingSecondLevelSystemTimeIndex
], endSystemTime
- startSystemTime
);
343 IOTimeStampEndConstant(IODBG_INTES(IOINTES_ACTION
),
344 VM_KERNEL_UNSLIDE(intAction
), (uintptr_t) owner
, (uintptr_t) this, (uintptr_t) workLoop
);
346 consumerCount
= cacheProdCount
;
347 if (autoDisable
&& !explicitDisable
)
351 else if ( numInts
< 0 )
354 IOTimeStampStartConstant(IODBG_INTES(IOINTES_ACTION
),
355 VM_KERNEL_UNSLIDE(intAction
), (uintptr_t) owner
, (uintptr_t) this, (uintptr_t) workLoop
);
357 if (reserved
->statistics
) {
358 if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelSystemTimeIndex
)) {
359 startSystemTime
= mach_absolute_time();
362 if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelCPUTimeIndex
)) {
363 startCPUTime
= thread_get_runtime_self();
368 (*intAction
)(owner
, this, -numInts
);
370 if (reserved
->statistics
) {
371 if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelCountIndex
)) {
372 IA_ADD_VALUE(&reserved
->statistics
->interruptStatistics
[kInterruptAccountingSecondLevelCountIndex
], 1);
375 if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelCPUTimeIndex
)) {
376 endCPUTime
= thread_get_runtime_self();
377 IA_ADD_VALUE(&reserved
->statistics
->interruptStatistics
[kInterruptAccountingSecondLevelCPUTimeIndex
], endCPUTime
- startCPUTime
);
380 if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelSystemTimeIndex
)) {
381 endSystemTime
= mach_absolute_time();
382 IA_ADD_VALUE(&reserved
->statistics
->interruptStatistics
[kInterruptAccountingSecondLevelSystemTimeIndex
], endSystemTime
- startSystemTime
);
387 IOTimeStampEndConstant(IODBG_INTES(IOINTES_ACTION
),
388 VM_KERNEL_UNSLIDE(intAction
), (uintptr_t) owner
, (uintptr_t) this, (uintptr_t) workLoop
);
390 consumerCount
= cacheProdCount
;
391 if (autoDisable
&& !explicitDisable
)
398 void IOInterruptEventSource::normalInterruptOccurred
399 (void */
*refcon*/
, IOService */
*prov*/
, int /*source*/)
401 bool trace
= (gIOKitTrace
& kIOTraceIntEventSource
) ? true : false;
403 IOStatisticsInterrupt();
407 IOTimeStampStartConstant(IODBG_INTES(IOINTES_SEMA
), (uintptr_t) this, (uintptr_t) owner
);
409 if (reserved
->statistics
) {
410 if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingFirstLevelCountIndex
)) {
411 IA_ADD_VALUE(&reserved
->statistics
->interruptStatistics
[kInterruptAccountingFirstLevelCountIndex
], 1);
415 signalWorkAvailable();
418 IOTimeStampEndConstant(IODBG_INTES(IOINTES_SEMA
), (uintptr_t) this, (uintptr_t) owner
);
421 void IOInterruptEventSource::disableInterruptOccurred
422 (void */
*refcon*/
, IOService
*prov
, int source
)
424 bool trace
= (gIOKitTrace
& kIOTraceIntEventSource
) ? true : false;
426 prov
->disableInterrupt(source
); /* disable the interrupt */
428 IOStatisticsInterrupt();
432 IOTimeStampStartConstant(IODBG_INTES(IOINTES_SEMA
), (uintptr_t) this, (uintptr_t) owner
);
434 if (reserved
->statistics
) {
435 if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingFirstLevelCountIndex
)) {
436 IA_ADD_VALUE(&reserved
->statistics
->interruptStatistics
[kInterruptAccountingFirstLevelCountIndex
], 1);
440 signalWorkAvailable();
443 IOTimeStampEndConstant(IODBG_INTES(IOINTES_SEMA
), (uintptr_t) this, (uintptr_t) owner
);
446 void IOInterruptEventSource::interruptOccurred
447 (void *refcon
, IOService
*prov
, int source
)
449 if (autoDisable
&& prov
)
450 disableInterruptOccurred(refcon
, prov
, source
);
452 normalInterruptOccurred(refcon
, prov
, source
);
455 IOReturn
IOInterruptEventSource::warmCPU
459 return ml_interrupt_prewarm(abstime
);