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 IOInterruptEventSource *
224 IOInterruptEventSource::interruptEventSource(OSObject *inOwner,
225 					     IOService *inProvider,
226 					     int inIntIndex,
227 					     ActionBlock inAction)
228 {
229     IOInterruptEventSource * ies;
230     ies = IOInterruptEventSource::interruptEventSource(inOwner, (Action) NULL, inProvider, inIntIndex);
231     if (ies) ies->setActionBlock((IOEventSource::ActionBlock) inAction);
232 
233     return ies;
234 }
235 
236 void IOInterruptEventSource::free()
237 {
238     if (provider && intIndex >= 0)
239         unregisterInterruptHandler(provider, intIndex);
240 
241     if (reserved) {
242         if (reserved->statistics) {
243             IODelete(reserved->statistics, IOInterruptAccountingData, 1);
244         }
245 
246         IODelete(reserved, ExpansionData, 1);
247     }
248 
249     super::free();
250 }
251 
252 void IOInterruptEventSource::enable()
253 {
254     if (provider && intIndex >= 0) {
255         provider->enableInterrupt(intIndex);
256         explicitDisable = false;
257         enabled = true;
258     }
259 }
260 
261 void IOInterruptEventSource::disable()
262 {
263     if (provider && intIndex >= 0) {
264         provider->disableInterrupt(intIndex);
265         explicitDisable = true;
266         enabled = false;
267     }
268 }
269 
270 void IOInterruptEventSource::setWorkLoop(IOWorkLoop *inWorkLoop)
271 {
272     if (inWorkLoop) super::setWorkLoop(inWorkLoop);
273 
274     if (provider) {
275 	if (!inWorkLoop) {
276 	    if (intIndex >= 0) {
277                 /*
278                  * It isn't necessarily safe to wait until free() to unregister the interrupt;
279                  * our provider may disappear.
280                  */
281                 unregisterInterruptHandler(provider, intIndex);
282 		intIndex = ~intIndex;
283 	    }
284 	} else if ((intIndex < 0) && (kIOReturnSuccess == registerInterruptHandler(provider, ~intIndex))) {
285 	    intIndex = ~intIndex;
286 	}
287     }
288 
289     if (!inWorkLoop) super::setWorkLoop(inWorkLoop);
290 }
291 
292 const IOService *IOInterruptEventSource::getProvider() const
293 {
294     return provider;
295 }
296 
297 int IOInterruptEventSource::getIntIndex() const
298 {
299     return intIndex;
300 }
301 
302 bool IOInterruptEventSource::getAutoDisable() const
303 {
304     return autoDisable;
305 }
306 
307 bool IOInterruptEventSource::checkForWork()
308 {
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;
318 
319     IOStatisticsCheckForWork();
320 
321 	if ( numInts > 0 )
322 	{
323 		if (trace)
324 			IOTimeStampStartConstant(IODBG_INTES(IOINTES_ACTION),
325 						 VM_KERNEL_ADDRHIDE(intAction), VM_KERNEL_ADDRHIDE(owner),
326 						 VM_KERNEL_ADDRHIDE(this), VM_KERNEL_ADDRHIDE(workLoop));
327 
328 		if (reserved->statistics) {
329 			if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelSystemTimeIndex)) {
330 				startSystemTime = mach_absolute_time();
331 			}
332 
333 			if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelCPUTimeIndex)) {
334 				startCPUTime = thread_get_runtime_self();
335 			}
336 		}
337 
338 		// Call the handler
339 		if (kActionBlock & flags) (intActionBlock)(this, numInts);
340 		else                      (*intAction)(owner, this, numInts);
341 
342 		if (reserved->statistics) {
343 			if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelCountIndex)) {
344 				IA_ADD_VALUE(&reserved->statistics->interruptStatistics[kInterruptAccountingSecondLevelCountIndex], 1);
345 			}
346 
347 			if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelCPUTimeIndex)) {
348 				endCPUTime = thread_get_runtime_self();
349 				IA_ADD_VALUE(&reserved->statistics->interruptStatistics[kInterruptAccountingSecondLevelCPUTimeIndex], endCPUTime - startCPUTime);
350 			}
351 
352 			if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelSystemTimeIndex)) {
353 				endSystemTime = mach_absolute_time();
354 				IA_ADD_VALUE(&reserved->statistics->interruptStatistics[kInterruptAccountingSecondLevelSystemTimeIndex], endSystemTime - startSystemTime);
355 			}
356 		}
357 
358 		if (trace)
359 			IOTimeStampEndConstant(IODBG_INTES(IOINTES_ACTION),
360 						VM_KERNEL_ADDRHIDE(intAction), VM_KERNEL_ADDRHIDE(owner),
361 						VM_KERNEL_ADDRHIDE(this), VM_KERNEL_ADDRHIDE(workLoop));
362 
363 		consumerCount = cacheProdCount;
364 		if (autoDisable && !explicitDisable)
365 			enable();
366 	}
367 
368 	else if ( numInts < 0 )
369 	{
370 		if (trace)
371 			IOTimeStampStartConstant(IODBG_INTES(IOINTES_ACTION),
372 						VM_KERNEL_ADDRHIDE(intAction), VM_KERNEL_ADDRHIDE(owner),
373 						VM_KERNEL_ADDRHIDE(this), VM_KERNEL_ADDRHIDE(workLoop));
374 
375 		if (reserved->statistics) {
376 			if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelSystemTimeIndex)) {
377 				startSystemTime = mach_absolute_time();
378 			}
379 
380 			if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelCPUTimeIndex)) {
381 				startCPUTime = thread_get_runtime_self();
382 			}
383 		}
384 
385 		// Call the handler
386 		if (kActionBlock & flags) (intActionBlock)(this, numInts);
387 		else                      (*intAction)(owner, this, numInts);
388 
389 		if (reserved->statistics) {
390 			if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelCountIndex)) {
391 				IA_ADD_VALUE(&reserved->statistics->interruptStatistics[kInterruptAccountingSecondLevelCountIndex], 1);
392 			}
393 
394 			if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelCPUTimeIndex)) {
395 				endCPUTime = thread_get_runtime_self();
396 				IA_ADD_VALUE(&reserved->statistics->interruptStatistics[kInterruptAccountingSecondLevelCPUTimeIndex], endCPUTime - startCPUTime);
397 			}
398 
399 			if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingSecondLevelSystemTimeIndex)) {
400 				endSystemTime = mach_absolute_time();
401 				IA_ADD_VALUE(&reserved->statistics->interruptStatistics[kInterruptAccountingSecondLevelSystemTimeIndex], endSystemTime - startSystemTime);
402 			}
403 		}
404 
405 		if (trace)
406 			IOTimeStampEndConstant(IODBG_INTES(IOINTES_ACTION),
407 							VM_KERNEL_ADDRHIDE(intAction), VM_KERNEL_ADDRHIDE(owner),
408 							VM_KERNEL_ADDRHIDE(this), VM_KERNEL_ADDRHIDE(workLoop));
409 
410 		consumerCount = cacheProdCount;
411 		if (autoDisable && !explicitDisable)
412 			enable();
413 	}
414 
415     return false;
416 }
417 
418 void IOInterruptEventSource::normalInterruptOccurred
419     (void */*refcon*/, IOService */*prov*/, int /*source*/)
420 {
421 	bool trace = (gIOKitTrace & kIOTraceIntEventSource) ? true : false;
422 
423     IOStatisticsInterrupt();
424     producerCount++;
425 
426 	if (trace)
427 	    IOTimeStampStartConstant(IODBG_INTES(IOINTES_SEMA), VM_KERNEL_ADDRHIDE(this), VM_KERNEL_ADDRHIDE(owner));
428 
429     if (reserved->statistics) {
430         if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingFirstLevelCountIndex)) {
431             IA_ADD_VALUE(&reserved->statistics->interruptStatistics[kInterruptAccountingFirstLevelCountIndex], 1);
432         }
433     }
434 
435     signalWorkAvailable();
436 
437 	if (trace)
438 	    IOTimeStampEndConstant(IODBG_INTES(IOINTES_SEMA), VM_KERNEL_ADDRHIDE(this), VM_KERNEL_ADDRHIDE(owner));
439 }
440 
441 void IOInterruptEventSource::disableInterruptOccurred
442     (void */*refcon*/, IOService *prov, int source)
443 {
444 	bool trace = (gIOKitTrace & kIOTraceIntEventSource) ? true : false;
445 
446     prov->disableInterrupt(source);	/* disable the interrupt */
447 
448     IOStatisticsInterrupt();
449     producerCount++;
450 
451 	if (trace)
452 	    IOTimeStampStartConstant(IODBG_INTES(IOINTES_SEMA), VM_KERNEL_ADDRHIDE(this), VM_KERNEL_ADDRHIDE(owner));
453 
454     if (reserved->statistics) {
455         if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingFirstLevelCountIndex)) {
456             IA_ADD_VALUE(&reserved->statistics->interruptStatistics[kInterruptAccountingFirstLevelCountIndex], 1);
457         }
458     }
459 
460     signalWorkAvailable();
461 
462 	if (trace)
463 	    IOTimeStampEndConstant(IODBG_INTES(IOINTES_SEMA), VM_KERNEL_ADDRHIDE(this), VM_KERNEL_ADDRHIDE(owner));
464 }
465 
466 void IOInterruptEventSource::interruptOccurred
467     (void *refcon, IOService *prov, int source)
468 {
469     if (autoDisable && prov)
470         disableInterruptOccurred(refcon, prov, source);
471     else
472         normalInterruptOccurred(refcon, prov, source);
473 }
474 
475 IOReturn IOInterruptEventSource::warmCPU
476     (uint64_t abstime)
477 {
478 
479 	return ml_interrupt_prewarm(abstime);
480 }
481