Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 | /* * Copyright (c) 2014 Apple Computer, Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. The rights granted to you under the License * may not be used to create, or enable the creation or redistribution of, * unlawful or unlicensed copies of an Apple operating system, or to * circumvent, violate, or enable the circumvention or violation of, any * terms of an Apple operating system software license agreement. * * Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ #ifndef __IOKIT_IOINTERRUPTACCOUNTING_PRIVATE_H #define __IOKIT_IOINTERRUPTACCOUNTING_PRIVATE_H /* * Header containing interrupt accounting related prototypes/defines that should be kept private to * xnu itself (no userspace, no kexts, no nothing!). */ #include <stdint.h> #include <IOKit/IOInterruptAccounting.h> #include <kern/queue.h> class OSObject; class IOSimpleReporter; /* * A brief overview. Interrupt accounting (as implemented in IOKit) pertains to infrastructure for * gathering information (currently, statistics only) on interrupts, and allowing them to be reported * (either to userspace through IOReporting, or through lldb; lldb macros have yet to be implemented). * * Currently, interrupt accounting consists of of a relationship between an IOService (a nub, which * will contain interrupt specifiers), an IOInterruptEventSource (if we add other interrupt target * abstractions, support could be added for them as well), and objects necessary to support them. An * interrupt is "named" by a tuple of {provider, interrupt index}; no nub should ever have more than * one interrupt registered for a given index, so this tuple should be unique. * * The "additional objects" mentioned above consist of an IOReporter object (lazily allocated and * tied to the nub; once allocated it will live until the nub is freed), and a statistics object * (effectively part of the IOIES in terms of lifecycle). The statistics object is used by the * interrupt codepath itself, and by the nub when it needs to update the reporter; the reporter is * used to report values to userspace. * * As a consequence of the above relationship, we do not track statistics for directly registered * interrupt handlers. We have no guarantees what the handler or the target may be; if you don't * follow the generic IOKit interrupt model, you will not be tracked by interrupt accounting. For * now, this means you must use an IOIES to be eligible for interrupt accounting. We also do not * track IOIES' that do not have providers (this is indicative that it is only being used to drive * workloop activity, and is not actually handling interrupts). */ /* * This is meant to let us set up the set of interrupt statistics we are actually interested in, by * setting a boot-arg. If we want to track a statistic, the bit corresponding to the index for that * statistic should be set in the bitmask. * * There is a bit of a mismatch here, in that our IOReporting channel namespace allows for 256 statistics, * but this bitmask actually limits it to 32. */ extern uint32_t gInterruptAccountingStatisticBitmask; /* * Check the bitmask by statistic index; useful for setting the initial value and conditionalizing code. */ #define IA_GET_ENABLE_BIT(statisticIndex) \ (((uint32_t) 1) << ((uint32_t) statisticIndex)) #define IA_GET_STATISTIC_ENABLED(statisticIndex) \ (IA_GET_ENABLE_BIT(statisticIndex) & gInterruptAccountingStatisticBitmask) /* * Check if any valid statistics are enabled. */ #define IA_ANY_STATISTICS_ENABLED \ ((IA_GET_ENABLE_BIT(kInterruptAccountingInvalidStatisticIndex) - 1) & gInterruptAccountingStatisticBitmask) /* * Actual string names for the statistics we gather. */ #define kInterruptAccountingChannelNameFirstLevelCount (" First Level Interrupt Handler Count") #define kInterruptAccountingChannelNameSecondLevelCount (" Second Level Interrupt Handler Count") #define kInterruptAccountingChannelNameFirstLevelTime (" First Level Interrupt Handler Time (MATUs)") #define kInterruptAccountingChannelNameSecondLevelCPUTime (" Second Level Interrupt Handler CPU Time (MATUs)") #define kInterruptAccountingChannelNameSecondLevelSystemTime ("Second Level Interrupt Handler System Time (MATUs)") #define kInterruptAccountingChannelNameNoThreadWakeups (" Interrupts that did not try to wake a thread") #define kInterruptAccountingChannelNameTotalThreadWakeups (" Sleeping threads woken up by this interrupt") #define kInterruptAccountingChannelNamePackageWakeups (" Package wakeups caused by this interrupt") #define kInterruptAccountingChannelNameCPUWakeups (" CPU wakeups caused by this interrupt") #define kInterruptAccountingChannelNameIdleExits (" Idle exits caused by this interrupt") static const char * const kInterruptAccountingStatisticNameArray[IA_NUM_INTERRUPT_ACCOUNTING_STATISTICS] = { [kInterruptAccountingFirstLevelCountIndex] = kInterruptAccountingChannelNameFirstLevelCount, [kInterruptAccountingSecondLevelCountIndex] = kInterruptAccountingChannelNameSecondLevelCount, [kInterruptAccountingFirstLevelTimeIndex] = kInterruptAccountingChannelNameFirstLevelTime, [kInterruptAccountingSecondLevelCPUTimeIndex] = kInterruptAccountingChannelNameSecondLevelCPUTime, [kInterruptAccountingSecondLevelSystemTimeIndex] = kInterruptAccountingChannelNameSecondLevelSystemTime, [kInterruptAccountingNoThreadWakeupsIndex] = kInterruptAccountingChannelNameNoThreadWakeups, [kInterruptAccountingTotalThreadWakeupsIndex] = kInterruptAccountingChannelNameTotalThreadWakeups, [kInterruptAccountingPackageWakeupsIndex] = kInterruptAccountingChannelNamePackageWakeups, [kInterruptAccountingCPUWakeupsIndex] = kInterruptAccountingChannelNameCPUWakeups, [kInterruptAccountingIdleExitsIndex] = kInterruptAccountingChannelNameIdleExits, }; /* * IOReporting group names. */ static const char * const kInterruptAccountingGroupName = "Interrupt Statistics (by index)"; /* * TODO: Generate the subgroup name strings? */ #define IA_MAX_SUBGROUP_NAME (32) static const char * const kInterruptAccountingSubgroupNames[IA_MAX_SUBGROUP_NAME] = { "0", "1", "2" , "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31"}; /* * As long as we use a lookup table, we may be out of bounds for a valid index. In this case, fall * back on a generic subgroup name that indicates we have screwed up. */ static const char * const kInterruptAccountingGenericSubgroupName = "(Index > 31)"; /* * For updating the statistics in the data structure. We cannot guarantee all of our platforms will be * able to do a 64-bit store in a single transaction. So, for new platforms, call out to the hardware * atomic add routine; it will either be unsupported, or do the right thing. For architectures or * platforms that do support it; just do regular assignment. * * We use this routine instead of a lock because at the moment, there is no way (in the interrupt context) * to reconcile a lock (even a spinlock) with the IOReporting synchonization (as we have no guarantee that * IOReporting will not block on a mutex, which would result in a panic if it held a spinlock). This * means that reported values may have a disparity if we update the reporter values while an interrupt is * being handled. * * Atomic modification should not be strictly required, as a given interrupt should not be dispatched to * two processors at once (and the interrupt should serve to force out stores), and the second level * handler should be synchonized by the work loop it runs on. */ #if __x86_64__ || __arm64 #define IA_ADD_VALUE(target, value) \ (*(target) += (value)) #else #define IA_ADD_VALUE(target, value) \ (OSAddAtomic64((value), (target))) #endif /* * TODO: Should this be an OSObject? Or properly pull in its methods as member functions? */ struct IOInterruptAccountingData { OSObject * owner; /* The owner of the statistics; currently always an IOIES or a subclass of it */ queue_chain_t chain; /* * We have no guarantee that the owner will not temporarily mutate its index value (i.e, in setWorkLoop * for IOIES). To ensure we can properly recalculate our own identity (and our channel IDs for the * reporter), stash the index we set up the reporter with here. * * Note that we should never remap the interrupt (point it to a different specifier). The mutation of * the index value is usually to negate it; I am uncertain of the reason for this at the moment. The * practical impact being that we should never need to update the stashed index value; it should stay * valid for the lifetime of the owner. */ int interruptIndex; /* * As long as we are based on the simple reporter, all our channels will be 64 bits. Align the data * to allow for safe atomic updates (we don't want to cross a cache line on any platform, but for some * it would cause a panic). */ volatile uint64_t interruptStatistics[IA_NUM_INTERRUPT_ACCOUNTING_STATISTICS] __attribute__((aligned(8))); }; /* * Initializes global values/structures related to interrupt accounting. */ void interruptAccountingInit(void); /* * Routines for adding and removing objects from the global queue of IOInterruptAccountingData objects; * the queue exists as a debugging aid (no entities other than these routines should care about the * queue at runtime). */ void interruptAccountingDataAddToList(IOInterruptAccountingData * data); void interruptAccountingDataRemoveFromList(IOInterruptAccountingData * data); /* * Updates reporter with the statistics contained within data. Invoked when IOReporting has been asked * for updated statistics; requiring explicit synchronization of data between the statistic fields and * the reporter helps keep interrupt accounting overhead down. */ void interruptAccountingDataUpdateChannels(IOInterruptAccountingData * data, IOSimpleReporter * reporter); /* * Initializes the statistics in data using the statistics currently held by reporter. Typically invoked * when data is first associated with reporter. The nub that an interrupt is associated with will be * longer lived than the interrupt; as a result, our owner may not be the first to register for a * particular interrupt index with that nub, so we need to inherit the existing statistics (as we describe * statistics in terms of {nub id, index}, not in terms of our owner). */ void interruptAccountingDataInheritChannels(IOInterruptAccountingData * data, IOSimpleReporter * reporter); #endif /* __IOKIT_IOINTERRUPTACCOUNTING_PRIVATE_H */ |