1 /*
2  * Copyright (c) 1998-2014 Apple Inc. All rights reserved.
3  *
4  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5  *
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.
14  *
15  * Please obtain a copy of the License at
16  * http://www.opensource.apple.com/apsl/ and read it before using this file.
17  *
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.
25  *
26  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27  */
28 
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>
37 
38 #if IOKITSTATS
39 
40 #define IOStatisticsInitializeCounter() \
41 do { \
42 	IOStatistics::setCounterType(IOEventSource::reserved->counter, kIOStatisticsInterruptEventSourceCounter); \
43 } while (0)
44 
45 #define IOStatisticsCheckForWork() \
46 do { \
47 	IOStatistics::countInterruptCheckForWork(IOEventSource::reserved->counter); \
48 } while (0)
49 
50 #define IOStatisticsInterrupt() \
51 do { \
52 	IOStatistics::countInterrupt(IOEventSource::reserved->counter); \
53 } while (0)
54 
55 #else
56 
57 #define IOStatisticsInitializeCounter()
58 #define IOStatisticsCheckForWork()
59 #define IOStatisticsInterrupt()
60 
61 #endif // IOKITSTATS
62 
63 #define super IOEventSource
64 
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);
74 
75 bool IOInterruptEventSource::init(OSObject *inOwner,
76 				  Action inAction,
77 				  IOService *inProvider,
78 				  int inIntIndex)
79 {
80     bool res = true;
81 
82     if ( !super::init(inOwner, (IOEventSourceAction) inAction) )
83         return false;
84 
85     reserved = IONew(ExpansionData, 1);
86 
87     if (!reserved) {
88         return false;
89     }
90 
91     bzero(reserved, sizeof(ExpansionData));
92 
93     provider = inProvider;
94     producerCount = consumerCount = 0;
95     autoDisable = explicitDisable = false;
96     intIndex = ~inIntIndex;
97 
98     // Assumes inOwner holds a reference(retain) on the provider
99     if (inProvider) {
100         if (IA_ANY_STATISTICS_ENABLED) {
101             /*
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.
106              *
107              * We also avoid try to avoid interrupt accounting overhead if none of
108              * the statistics are enabled.
109              */
110             reserved->statistics = IONew(IOInterruptAccountingData, 1);
111 
112             if (!reserved->statistics) {
113                 /*
114                  * We rely on the free() routine to clean up after us if init fails
115                  * midway.
116                  */
117                 return false;
118             }
119 
120             bzero(reserved->statistics, sizeof(IOInterruptAccountingData));
121 
122             reserved->statistics->owner = this;
123         }
124 
125         res = (kIOReturnSuccess == registerInterruptHandler(inProvider, inIntIndex));
126 
127 	if (res) {
128 	    intIndex = inIntIndex;
129         }
130     }
131 
132     IOStatisticsInitializeCounter();
133 
134     return res;
135 }
136 
137 IOReturn IOInterruptEventSource::registerInterruptHandler(IOService *inProvider,
138 				  int inIntIndex)
139 {
140     IOReturn ret;
141     int intType;
142     IOInterruptAction intHandler;
143 
144     ret = inProvider->getInterruptType(inIntIndex, &intType);
145     if (kIOReturnSuccess != ret)
146 	return (ret);
147 
148     autoDisable = (intType == kIOInterruptTypeLevel);
149     if (autoDisable) {
150 	intHandler = OSMemberFunctionCast(IOInterruptAction,
151 	    this, &IOInterruptEventSource::disableInterruptOccurred);
152     }
153     else
154 	intHandler = OSMemberFunctionCast(IOInterruptAction,
155 	    this, &IOInterruptEventSource::normalInterruptOccurred);
156 
157     ret = provider->registerInterrupt(inIntIndex, this, intHandler);
158 
159     /*
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
163      * erroneously.
164      */
165     if ((ret == kIOReturnSuccess) && (reserved->statistics)) {
166         /*
167          * Stash the normal index value, for the sake of debugging.
168          */
169         reserved->statistics->interruptIndex = inIntIndex;
170 
171         /*
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
178          * interrupt).
179          */
180         provider->addInterruptStatistics(reserved->statistics, inIntIndex);
181 
182         /*
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).
187          */
188         interruptAccountingDataAddToList(reserved->statistics);
189     }
190 
191     return (ret);
192 }
193 
194 void
195 IOInterruptEventSource::unregisterInterruptHandler(IOService *inProvider,
196 				  int inIntIndex)
197 {
198     if (reserved->statistics) {
199         interruptAccountingDataRemoveFromList(reserved->statistics);
200         provider->removeInterruptStatistics(reserved->statistics->interruptIndex);
201     }
202 
203     provider->unregisterInterrupt(inIntIndex);
204 }
205 
206 
207 IOInterruptEventSource *
208 IOInterruptEventSource::interruptEventSource(OSObject *inOwner,
209 					     Action inAction,
210 					     IOService *inProvider,
211 					     int inIntIndex)
212 {
213     IOInterruptEventSource *me = new IOInterruptEventSource;
214 
215     if (me && !me->init(inOwner, inAction, inProvider, inIntIndex)) {
216         me->release();
217         return 0;
218     }
219 
220     return me;
221 }
222 
223 void IOInterruptEventSource::free()
224 {
225     if (provider && intIndex >= 0)
226         unregisterInterruptHandler(provider, intIndex);
227 
228     if (reserved) {
229         if (reserved->statistics) {
230             IODelete(reserved->statistics, IOInterruptAccountingData, 1);
231         }
232 
233         IODelete(reserved, ExpansionData, 1);
234     }
235 
236     super::free();
237 }
238 
239 void IOInterruptEventSource::enable()
240 {
241     if (provider && intIndex >= 0) {
242         provider->enableInterrupt(intIndex);
243         explicitDisable = false;
244         enabled = true;
245     }
246 }
247 
248 void IOInterruptEventSource::disable()
249 {
250     if (provider && intIndex >= 0) {
251         provider->disableInterrupt(intIndex);
252         explicitDisable = true;
253         enabled = false;
254     }
255 }
256 
257 void IOInterruptEventSource::setWorkLoop(IOWorkLoop *inWorkLoop)
258 {
259     if (inWorkLoop) super::setWorkLoop(inWorkLoop);
260 
261     if (provider) {
262 	if (!inWorkLoop) {
263 	    if (intIndex >= 0) {
264                 /*
265                  * It isn't necessarily safe to wait until free() to unregister the interrupt;
266                  * our provider may disappear.
267                  */
268                 unregisterInterruptHandler(provider, intIndex);
269 		intIndex = ~intIndex;
270 	    }
271 	} else if ((intIndex < 0) && (kIOReturnSuccess == registerInterruptHandler(provider, ~intIndex))) {
272 	    intIndex = ~intIndex;
273 	}
274     }
275 
276     if (!inWorkLoop) super::setWorkLoop(inWorkLoop);
277 }
278 
279 const IOService *IOInterruptEventSource::getProvider() const
280 {
281     return provider;
282 }
283 
284 int IOInterruptEventSource::getIntIndex() const
285 {
286     return intIndex;
287 }
288 
289 bool IOInterruptEventSource::getAutoDisable() const
290 {
291     return autoDisable;
292 }
293 
294 bool IOInterruptEventSource::checkForWork()
295 {
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;
304 
305     IOStatisticsCheckForWork();
306 
307 	if ( numInts > 0 )
308 	{
309 		if (trace)
310 			IOTimeStampStartConstant(IODBG_INTES(IOINTES_ACTION),
311 						 VM_KERNEL_ADDRHIDE(intAction), VM_KERNEL_ADDRHIDE(owner),
312 						 VM_KERNEL_ADDRHIDE(this), VM_KERNEL_ADDRHIDE(workLoop));
313 
314 		if (reserved->statistics) {
315 			if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelSystemTimeIndex)) {
316 				startSystemTime = mach_absolute_time();
317 			}
318 
319 			if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelCPUTimeIndex)) {
320 				startCPUTime = thread_get_runtime_self();
321 			}
322 		}
323 
324 		// Call the handler
325 		(*intAction)(owner, this, numInts);
326 
327 		if (reserved->statistics) {
328 			if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelCountIndex)) {
329 				IA_ADD_VALUE(&reserved->statistics->interruptStatistics[kInterruptAccountingSecondLevelCountIndex], 1);
330 			}
331 
332 			if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelCPUTimeIndex)) {
333 				endCPUTime = thread_get_runtime_self();
334 				IA_ADD_VALUE(&reserved->statistics->interruptStatistics[kInterruptAccountingSecondLevelCPUTimeIndex], endCPUTime - startCPUTime);
335 			}
336 
337 			if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelSystemTimeIndex)) {
338 				endSystemTime = mach_absolute_time();
339 				IA_ADD_VALUE(&reserved->statistics->interruptStatistics[kInterruptAccountingSecondLevelSystemTimeIndex], endSystemTime - startSystemTime);
340 			}
341 		}
342 
343 		if (trace)
344 			IOTimeStampEndConstant(IODBG_INTES(IOINTES_ACTION),
345 						VM_KERNEL_ADDRHIDE(intAction), VM_KERNEL_ADDRHIDE(owner),
346 						VM_KERNEL_ADDRHIDE(this), VM_KERNEL_ADDRHIDE(workLoop));
347 
348 		consumerCount = cacheProdCount;
349 		if (autoDisable && !explicitDisable)
350 			enable();
351 	}
352 
353 	else if ( numInts < 0 )
354 	{
355 		if (trace)
356 			IOTimeStampStartConstant(IODBG_INTES(IOINTES_ACTION),
357 						VM_KERNEL_ADDRHIDE(intAction), VM_KERNEL_ADDRHIDE(owner),
358 						VM_KERNEL_ADDRHIDE(this), VM_KERNEL_ADDRHIDE(workLoop));
359 
360 		if (reserved->statistics) {
361 			if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelSystemTimeIndex)) {
362 				startSystemTime = mach_absolute_time();
363 			}
364 
365 			if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelCPUTimeIndex)) {
366 				startCPUTime = thread_get_runtime_self();
367 			}
368 		}
369 
370 		// Call the handler
371 		(*intAction)(owner, this, -numInts);
372 
373 		if (reserved->statistics) {
374 			if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelCountIndex)) {
375 				IA_ADD_VALUE(&reserved->statistics->interruptStatistics[kInterruptAccountingSecondLevelCountIndex], 1);
376 			}
377 
378 			if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelCPUTimeIndex)) {
379 				endCPUTime = thread_get_runtime_self();
380 				IA_ADD_VALUE(&reserved->statistics->interruptStatistics[kInterruptAccountingSecondLevelCPUTimeIndex], endCPUTime - startCPUTime);
381 			}
382 
383 			if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelSystemTimeIndex)) {
384 				endSystemTime = mach_absolute_time();
385 				IA_ADD_VALUE(&reserved->statistics->interruptStatistics[kInterruptAccountingSecondLevelSystemTimeIndex], endSystemTime - startSystemTime);
386 			}
387 		}
388 
389 		if (trace)
390 			IOTimeStampEndConstant(IODBG_INTES(IOINTES_ACTION),
391 							VM_KERNEL_ADDRHIDE(intAction), VM_KERNEL_ADDRHIDE(owner),
392 							VM_KERNEL_ADDRHIDE(this), VM_KERNEL_ADDRHIDE(workLoop));
393 
394 		consumerCount = cacheProdCount;
395 		if (autoDisable && !explicitDisable)
396 			enable();
397 	}
398 
399     return false;
400 }
401 
402 void IOInterruptEventSource::normalInterruptOccurred
403     (void */*refcon*/, IOService */*prov*/, int /*source*/)
404 {
405 	bool trace = (gIOKitTrace & kIOTraceIntEventSource) ? true : false;
406 
407     IOStatisticsInterrupt();
408     producerCount++;
409 
410 	if (trace)
411 	    IOTimeStampStartConstant(IODBG_INTES(IOINTES_SEMA), VM_KERNEL_ADDRHIDE(this), VM_KERNEL_ADDRHIDE(owner));
412 
413     if (reserved->statistics) {
414         if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingFirstLevelCountIndex)) {
415             IA_ADD_VALUE(&reserved->statistics->interruptStatistics[kInterruptAccountingFirstLevelCountIndex], 1);
416         }
417     }
418 
419     signalWorkAvailable();
420 
421 	if (trace)
422 	    IOTimeStampEndConstant(IODBG_INTES(IOINTES_SEMA), VM_KERNEL_ADDRHIDE(this), VM_KERNEL_ADDRHIDE(owner));
423 }
424 
425 void IOInterruptEventSource::disableInterruptOccurred
426     (void */*refcon*/, IOService *prov, int source)
427 {
428 	bool trace = (gIOKitTrace & kIOTraceIntEventSource) ? true : false;
429 
430     prov->disableInterrupt(source);	/* disable the interrupt */
431 
432     IOStatisticsInterrupt();
433     producerCount++;
434 
435 	if (trace)
436 	    IOTimeStampStartConstant(IODBG_INTES(IOINTES_SEMA), VM_KERNEL_ADDRHIDE(this), VM_KERNEL_ADDRHIDE(owner));
437 
438     if (reserved->statistics) {
439         if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingFirstLevelCountIndex)) {
440             IA_ADD_VALUE(&reserved->statistics->interruptStatistics[kInterruptAccountingFirstLevelCountIndex], 1);
441         }
442     }
443 
444     signalWorkAvailable();
445 
446 	if (trace)
447 	    IOTimeStampEndConstant(IODBG_INTES(IOINTES_SEMA), VM_KERNEL_ADDRHIDE(this), VM_KERNEL_ADDRHIDE(owner));
448 }
449 
450 void IOInterruptEventSource::interruptOccurred
451     (void *refcon, IOService *prov, int source)
452 {
453     if (autoDisable && prov)
454         disableInterruptOccurred(refcon, prov, source);
455     else
456         normalInterruptOccurred(refcon, prov, source);
457 }
458 
459 IOReturn IOInterruptEventSource::warmCPU
460     (uint64_t abstime)
461 {
462 
463 	return ml_interrupt_prewarm(abstime);
464 }
465