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 IOInterruptEventSource
*
224 IOInterruptEventSource::interruptEventSource(OSObject
*inOwner
,
225 IOService
*inProvider
,
227 ActionBlock inAction
)
229 IOInterruptEventSource
* ies
;
230 ies
= IOInterruptEventSource::interruptEventSource(inOwner
, (Action
) NULL
, inProvider
, inIntIndex
);
231 if (ies
) ies
->setActionBlock((IOEventSource::ActionBlock
) inAction
);
236 void IOInterruptEventSource::free()
238 if (provider
&& intIndex
>= 0)
239 unregisterInterruptHandler(provider
, intIndex
);
242 if (reserved
->statistics
) {
243 IODelete(reserved
->statistics
, IOInterruptAccountingData
, 1);
246 IODelete(reserved
, ExpansionData
, 1);
252 void IOInterruptEventSource::enable()
254 if (provider
&& intIndex
>= 0) {
255 provider
->enableInterrupt(intIndex
);
256 explicitDisable
= false;
261 void IOInterruptEventSource::disable()
263 if (provider
&& intIndex
>= 0) {
264 provider
->disableInterrupt(intIndex
);
265 explicitDisable
= true;
270 void IOInterruptEventSource::setWorkLoop(IOWorkLoop
*inWorkLoop
)
272 if (inWorkLoop
) super::setWorkLoop(inWorkLoop
);
278 * It isn't necessarily safe to wait until free() to unregister the interrupt;
279 * our provider may disappear.
281 unregisterInterruptHandler(provider
, intIndex
);
282 intIndex
= ~intIndex
;
284 } else if ((intIndex
< 0) && (kIOReturnSuccess
== registerInterruptHandler(provider
, ~intIndex
))) {
285 intIndex
= ~intIndex
;
289 if (!inWorkLoop
) super::setWorkLoop(inWorkLoop
);
292 const IOService
*IOInterruptEventSource::getProvider() const
297 int IOInterruptEventSource::getIntIndex() const
302 bool IOInterruptEventSource::getAutoDisable() const
307 bool IOInterruptEventSource::checkForWork()
309 uint64_t startSystemTime
= 0;
310 uint64_t endSystemTime
= 0;
311 uint64_t startCPUTime
= 0;
312 uint64_t endCPUTime
= 0;
313 unsigned int cacheProdCount
= producerCount
;
314 int numInts
= cacheProdCount
- consumerCount
;
315 IOInterruptEventAction intAction
= (IOInterruptEventAction
) action
;
316 ActionBlock intActionBlock
= (ActionBlock
) actionBlock
;
317 bool trace
= (gIOKitTrace
& kIOTraceIntEventSource
) ? true : false;
319 IOStatisticsCheckForWork();
324 IOTimeStampStartConstant(IODBG_INTES(IOINTES_ACTION
),
325 VM_KERNEL_ADDRHIDE(intAction
), VM_KERNEL_ADDRHIDE(owner
),
326 VM_KERNEL_ADDRHIDE(this), VM_KERNEL_ADDRHIDE(workLoop
));
328 if (reserved
->statistics
) {
329 if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelSystemTimeIndex
)) {
330 startSystemTime
= mach_absolute_time();
333 if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelCPUTimeIndex
)) {
334 startCPUTime
= thread_get_runtime_self();
339 if (kActionBlock
& flags
) (intActionBlock
)(this, numInts
);
340 else (*intAction
)(owner
, this, numInts
);
342 if (reserved
->statistics
) {
343 if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelCountIndex
)) {
344 IA_ADD_VALUE(&reserved
->statistics
->interruptStatistics
[kInterruptAccountingSecondLevelCountIndex
], 1);
347 if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelCPUTimeIndex
)) {
348 endCPUTime
= thread_get_runtime_self();
349 IA_ADD_VALUE(&reserved
->statistics
->interruptStatistics
[kInterruptAccountingSecondLevelCPUTimeIndex
], endCPUTime
- startCPUTime
);
352 if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelSystemTimeIndex
)) {
353 endSystemTime
= mach_absolute_time();
354 IA_ADD_VALUE(&reserved
->statistics
->interruptStatistics
[kInterruptAccountingSecondLevelSystemTimeIndex
], endSystemTime
- startSystemTime
);
359 IOTimeStampEndConstant(IODBG_INTES(IOINTES_ACTION
),
360 VM_KERNEL_ADDRHIDE(intAction
), VM_KERNEL_ADDRHIDE(owner
),
361 VM_KERNEL_ADDRHIDE(this), VM_KERNEL_ADDRHIDE(workLoop
));
363 consumerCount
= cacheProdCount
;
364 if (autoDisable
&& !explicitDisable
)
368 else if ( numInts
< 0 )
371 IOTimeStampStartConstant(IODBG_INTES(IOINTES_ACTION
),
372 VM_KERNEL_ADDRHIDE(intAction
), VM_KERNEL_ADDRHIDE(owner
),
373 VM_KERNEL_ADDRHIDE(this), VM_KERNEL_ADDRHIDE(workLoop
));
375 if (reserved
->statistics
) {
376 if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelSystemTimeIndex
)) {
377 startSystemTime
= mach_absolute_time();
380 if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelCPUTimeIndex
)) {
381 startCPUTime
= thread_get_runtime_self();
386 if (kActionBlock
& flags
) (intActionBlock
)(this, numInts
);
387 else (*intAction
)(owner
, this, numInts
);
389 if (reserved
->statistics
) {
390 if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelCountIndex
)) {
391 IA_ADD_VALUE(&reserved
->statistics
->interruptStatistics
[kInterruptAccountingSecondLevelCountIndex
], 1);
394 if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelCPUTimeIndex
)) {
395 endCPUTime
= thread_get_runtime_self();
396 IA_ADD_VALUE(&reserved
->statistics
->interruptStatistics
[kInterruptAccountingSecondLevelCPUTimeIndex
], endCPUTime
- startCPUTime
);
399 if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelSystemTimeIndex
)) {
400 endSystemTime
= mach_absolute_time();
401 IA_ADD_VALUE(&reserved
->statistics
->interruptStatistics
[kInterruptAccountingSecondLevelSystemTimeIndex
], endSystemTime
- startSystemTime
);
406 IOTimeStampEndConstant(IODBG_INTES(IOINTES_ACTION
),
407 VM_KERNEL_ADDRHIDE(intAction
), VM_KERNEL_ADDRHIDE(owner
),
408 VM_KERNEL_ADDRHIDE(this), VM_KERNEL_ADDRHIDE(workLoop
));
410 consumerCount
= cacheProdCount
;
411 if (autoDisable
&& !explicitDisable
)
418 void IOInterruptEventSource::normalInterruptOccurred
419 (void */
*refcon*/
, IOService */
*prov*/
, int /*source*/)
421 bool trace
= (gIOKitTrace
& kIOTraceIntEventSource
) ? true : false;
423 IOStatisticsInterrupt();
427 IOTimeStampStartConstant(IODBG_INTES(IOINTES_SEMA
), VM_KERNEL_ADDRHIDE(this), VM_KERNEL_ADDRHIDE(owner
));
429 if (reserved
->statistics
) {
430 if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingFirstLevelCountIndex
)) {
431 IA_ADD_VALUE(&reserved
->statistics
->interruptStatistics
[kInterruptAccountingFirstLevelCountIndex
], 1);
435 signalWorkAvailable();
438 IOTimeStampEndConstant(IODBG_INTES(IOINTES_SEMA
), VM_KERNEL_ADDRHIDE(this), VM_KERNEL_ADDRHIDE(owner
));
441 void IOInterruptEventSource::disableInterruptOccurred
442 (void */
*refcon*/
, IOService
*prov
, int source
)
444 bool trace
= (gIOKitTrace
& kIOTraceIntEventSource
) ? true : false;
446 prov
->disableInterrupt(source
); /* disable the interrupt */
448 IOStatisticsInterrupt();
452 IOTimeStampStartConstant(IODBG_INTES(IOINTES_SEMA
), VM_KERNEL_ADDRHIDE(this), VM_KERNEL_ADDRHIDE(owner
));
454 if (reserved
->statistics
) {
455 if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingFirstLevelCountIndex
)) {
456 IA_ADD_VALUE(&reserved
->statistics
->interruptStatistics
[kInterruptAccountingFirstLevelCountIndex
], 1);
460 signalWorkAvailable();
463 IOTimeStampEndConstant(IODBG_INTES(IOINTES_SEMA
), VM_KERNEL_ADDRHIDE(this), VM_KERNEL_ADDRHIDE(owner
));
466 void IOInterruptEventSource::interruptOccurred
467 (void *refcon
, IOService
*prov
, int source
)
469 if (autoDisable
&& prov
)
470 disableInterruptOccurred(refcon
, prov
, source
);
472 normalInterruptOccurred(refcon
, prov
, source
);
475 IOReturn
IOInterruptEventSource::warmCPU
479 return ml_interrupt_prewarm(abstime
);