IOInterruptEventSource.cpp [plain text]
#include <IOKit/IOInterruptEventSource.h>
#include <IOKit/IOKitDebug.h>
#include <IOKit/IOLib.h>
#include <IOKit/IOService.h>
#include <IOKit/IOInterrupts.h>
#include <IOKit/IOTimeStamp.h>
#include <IOKit/IOWorkLoop.h>
#include <IOKit/IOInterruptAccountingPrivate.h>
#if IOKITSTATS
#define IOStatisticsInitializeCounter() \
do { \
IOStatistics::setCounterType(IOEventSource::reserved->counter, kIOStatisticsInterruptEventSourceCounter); \
} while (0)
#define IOStatisticsCheckForWork() \
do { \
IOStatistics::countInterruptCheckForWork(IOEventSource::reserved->counter); \
} while (0)
#define IOStatisticsInterrupt() \
do { \
IOStatistics::countInterrupt(IOEventSource::reserved->counter); \
} while (0)
#else
#define IOStatisticsInitializeCounter()
#define IOStatisticsCheckForWork()
#define IOStatisticsInterrupt()
#endif // IOKITSTATS
#define super IOEventSource
OSDefineMetaClassAndStructors(IOInterruptEventSource, IOEventSource)
OSMetaClassDefineReservedUnused(IOInterruptEventSource, 0);
OSMetaClassDefineReservedUnused(IOInterruptEventSource, 1);
OSMetaClassDefineReservedUnused(IOInterruptEventSource, 2);
OSMetaClassDefineReservedUnused(IOInterruptEventSource, 3);
OSMetaClassDefineReservedUnused(IOInterruptEventSource, 4);
OSMetaClassDefineReservedUnused(IOInterruptEventSource, 5);
OSMetaClassDefineReservedUnused(IOInterruptEventSource, 6);
OSMetaClassDefineReservedUnused(IOInterruptEventSource, 7);
bool IOInterruptEventSource::init(OSObject *inOwner,
Action inAction,
IOService *inProvider,
int inIntIndex)
{
bool res = true;
if ( !super::init(inOwner, (IOEventSourceAction) inAction) )
return false;
reserved = IONew(ExpansionData, 1);
if (!reserved) {
return false;
}
bzero(reserved, sizeof(ExpansionData));
provider = inProvider;
producerCount = consumerCount = 0;
autoDisable = explicitDisable = false;
intIndex = ~inIntIndex;
if (inProvider) {
if (IA_ANY_STATISTICS_ENABLED) {
reserved->statistics = IONew(IOInterruptAccountingData, 1);
if (!reserved->statistics) {
return false;
}
bzero(reserved->statistics, sizeof(IOInterruptAccountingData));
reserved->statistics->owner = this;
}
res = (kIOReturnSuccess == registerInterruptHandler(inProvider, inIntIndex));
if (res) {
intIndex = inIntIndex;
}
}
IOStatisticsInitializeCounter();
return res;
}
IOReturn IOInterruptEventSource::registerInterruptHandler(IOService *inProvider,
int inIntIndex)
{
IOReturn ret;
int intType;
IOInterruptAction intHandler;
ret = inProvider->getInterruptType(inIntIndex, &intType);
if (kIOReturnSuccess != ret)
return (ret);
autoDisable = (intType == kIOInterruptTypeLevel);
if (autoDisable) {
intHandler = OSMemberFunctionCast(IOInterruptAction,
this, &IOInterruptEventSource::disableInterruptOccurred);
}
else
intHandler = OSMemberFunctionCast(IOInterruptAction,
this, &IOInterruptEventSource::normalInterruptOccurred);
ret = provider->registerInterrupt(inIntIndex, this, intHandler);
if ((ret == kIOReturnSuccess) && (reserved->statistics)) {
reserved->statistics->interruptIndex = inIntIndex;
provider->addInterruptStatistics(reserved->statistics, inIntIndex);
interruptAccountingDataAddToList(reserved->statistics);
}
return (ret);
}
void
IOInterruptEventSource::unregisterInterruptHandler(IOService *inProvider,
int inIntIndex)
{
if (reserved->statistics) {
interruptAccountingDataRemoveFromList(reserved->statistics);
provider->removeInterruptStatistics(reserved->statistics->interruptIndex);
}
provider->unregisterInterrupt(inIntIndex);
}
IOInterruptEventSource *
IOInterruptEventSource::interruptEventSource(OSObject *inOwner,
Action inAction,
IOService *inProvider,
int inIntIndex)
{
IOInterruptEventSource *me = new IOInterruptEventSource;
if (me && !me->init(inOwner, inAction, inProvider, inIntIndex)) {
me->release();
return 0;
}
return me;
}
void IOInterruptEventSource::free()
{
if (provider && intIndex >= 0)
unregisterInterruptHandler(provider, intIndex);
if (reserved) {
if (reserved->statistics) {
IODelete(reserved->statistics, IOInterruptAccountingData, 1);
}
IODelete(reserved, ExpansionData, 1);
}
super::free();
}
void IOInterruptEventSource::enable()
{
if (provider && intIndex >= 0) {
provider->enableInterrupt(intIndex);
explicitDisable = false;
enabled = true;
}
}
void IOInterruptEventSource::disable()
{
if (provider && intIndex >= 0) {
provider->disableInterrupt(intIndex);
explicitDisable = true;
enabled = false;
}
}
void IOInterruptEventSource::setWorkLoop(IOWorkLoop *inWorkLoop)
{
if (inWorkLoop) super::setWorkLoop(inWorkLoop);
if (provider) {
if (!inWorkLoop) {
if (intIndex >= 0) {
unregisterInterruptHandler(provider, intIndex);
intIndex = ~intIndex;
}
} else if ((intIndex < 0) && (kIOReturnSuccess == registerInterruptHandler(provider, ~intIndex))) {
intIndex = ~intIndex;
}
}
if (!inWorkLoop) super::setWorkLoop(inWorkLoop);
}
const IOService *IOInterruptEventSource::getProvider() const
{
return provider;
}
int IOInterruptEventSource::getIntIndex() const
{
return intIndex;
}
bool IOInterruptEventSource::getAutoDisable() const
{
return autoDisable;
}
bool IOInterruptEventSource::checkForWork()
{
uint64_t startSystemTime = 0;
uint64_t endSystemTime = 0;
uint64_t startCPUTime = 0;
uint64_t endCPUTime = 0;
unsigned int cacheProdCount = producerCount;
int numInts = cacheProdCount - consumerCount;
IOInterruptEventAction intAction = (IOInterruptEventAction) action;
bool trace = (gIOKitTrace & kIOTraceIntEventSource) ? true : false;
IOStatisticsCheckForWork();
if ( numInts > 0 )
{
if (trace)
IOTimeStampStartConstant(IODBG_INTES(IOINTES_ACTION),
VM_KERNEL_ADDRHIDE(intAction), VM_KERNEL_ADDRHIDE(owner),
VM_KERNEL_ADDRHIDE(this), VM_KERNEL_ADDRHIDE(workLoop));
if (reserved->statistics) {
if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelSystemTimeIndex)) {
startSystemTime = mach_absolute_time();
}
if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelCPUTimeIndex)) {
startCPUTime = thread_get_runtime_self();
}
}
(*intAction)(owner, this, numInts);
if (reserved->statistics) {
if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelCountIndex)) {
IA_ADD_VALUE(&reserved->statistics->interruptStatistics[kInterruptAccountingSecondLevelCountIndex], 1);
}
if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelCPUTimeIndex)) {
endCPUTime = thread_get_runtime_self();
IA_ADD_VALUE(&reserved->statistics->interruptStatistics[kInterruptAccountingSecondLevelCPUTimeIndex], endCPUTime - startCPUTime);
}
if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelSystemTimeIndex)) {
endSystemTime = mach_absolute_time();
IA_ADD_VALUE(&reserved->statistics->interruptStatistics[kInterruptAccountingSecondLevelSystemTimeIndex], endSystemTime - startSystemTime);
}
}
if (trace)
IOTimeStampEndConstant(IODBG_INTES(IOINTES_ACTION),
VM_KERNEL_ADDRHIDE(intAction), VM_KERNEL_ADDRHIDE(owner),
VM_KERNEL_ADDRHIDE(this), VM_KERNEL_ADDRHIDE(workLoop));
consumerCount = cacheProdCount;
if (autoDisable && !explicitDisable)
enable();
}
else if ( numInts < 0 )
{
if (trace)
IOTimeStampStartConstant(IODBG_INTES(IOINTES_ACTION),
VM_KERNEL_ADDRHIDE(intAction), VM_KERNEL_ADDRHIDE(owner),
VM_KERNEL_ADDRHIDE(this), VM_KERNEL_ADDRHIDE(workLoop));
if (reserved->statistics) {
if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelSystemTimeIndex)) {
startSystemTime = mach_absolute_time();
}
if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelCPUTimeIndex)) {
startCPUTime = thread_get_runtime_self();
}
}
(*intAction)(owner, this, -numInts);
if (reserved->statistics) {
if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelCountIndex)) {
IA_ADD_VALUE(&reserved->statistics->interruptStatistics[kInterruptAccountingSecondLevelCountIndex], 1);
}
if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelCPUTimeIndex)) {
endCPUTime = thread_get_runtime_self();
IA_ADD_VALUE(&reserved->statistics->interruptStatistics[kInterruptAccountingSecondLevelCPUTimeIndex], endCPUTime - startCPUTime);
}
if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelSystemTimeIndex)) {
endSystemTime = mach_absolute_time();
IA_ADD_VALUE(&reserved->statistics->interruptStatistics[kInterruptAccountingSecondLevelSystemTimeIndex], endSystemTime - startSystemTime);
}
}
if (trace)
IOTimeStampEndConstant(IODBG_INTES(IOINTES_ACTION),
VM_KERNEL_ADDRHIDE(intAction), VM_KERNEL_ADDRHIDE(owner),
VM_KERNEL_ADDRHIDE(this), VM_KERNEL_ADDRHIDE(workLoop));
consumerCount = cacheProdCount;
if (autoDisable && !explicitDisable)
enable();
}
return false;
}
void IOInterruptEventSource::normalInterruptOccurred
(void *, IOService *, int )
{
bool trace = (gIOKitTrace & kIOTraceIntEventSource) ? true : false;
IOStatisticsInterrupt();
producerCount++;
if (trace)
IOTimeStampStartConstant(IODBG_INTES(IOINTES_SEMA), VM_KERNEL_ADDRHIDE(this), VM_KERNEL_ADDRHIDE(owner));
if (reserved->statistics) {
if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingFirstLevelCountIndex)) {
IA_ADD_VALUE(&reserved->statistics->interruptStatistics[kInterruptAccountingFirstLevelCountIndex], 1);
}
}
signalWorkAvailable();
if (trace)
IOTimeStampEndConstant(IODBG_INTES(IOINTES_SEMA), VM_KERNEL_ADDRHIDE(this), VM_KERNEL_ADDRHIDE(owner));
}
void IOInterruptEventSource::disableInterruptOccurred
(void *, IOService *prov, int source)
{
bool trace = (gIOKitTrace & kIOTraceIntEventSource) ? true : false;
prov->disableInterrupt(source);
IOStatisticsInterrupt();
producerCount++;
if (trace)
IOTimeStampStartConstant(IODBG_INTES(IOINTES_SEMA), VM_KERNEL_ADDRHIDE(this), VM_KERNEL_ADDRHIDE(owner));
if (reserved->statistics) {
if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingFirstLevelCountIndex)) {
IA_ADD_VALUE(&reserved->statistics->interruptStatistics[kInterruptAccountingFirstLevelCountIndex], 1);
}
}
signalWorkAvailable();
if (trace)
IOTimeStampEndConstant(IODBG_INTES(IOINTES_SEMA), VM_KERNEL_ADDRHIDE(this), VM_KERNEL_ADDRHIDE(owner));
}
void IOInterruptEventSource::interruptOccurred
(void *refcon, IOService *prov, int source)
{
if (autoDisable && prov)
disableInterruptOccurred(refcon, prov, source);
else
normalInterruptOccurred(refcon, prov, source);
}
IOReturn IOInterruptEventSource::warmCPU
(uint64_t abstime)
{
return ml_interrupt_prewarm(abstime);
}