xref: /xnu-11215/iokit/Kernel/IOWorkLoop.cpp (revision 3ca3bd55)
1 /*
2  * Copyright (c) 1998-2007 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 Copyright (c) 1998 Apple Computer, Inc.  All rights reserved.
30 
31 HISTORY
32     1998-7-13	Godfrey van der Linden(gvdl)
33         Created.
34 */
35 
36 #include <pexpert/pexpert.h>
37 #include <IOKit/IOWorkLoop.h>
38 #include <IOKit/IOEventSource.h>
39 #include <IOKit/IOInterruptEventSource.h>
40 #include <IOKit/IOCommandGate.h>
41 #include <IOKit/IOTimeStamp.h>
42 #include <libkern/OSDebug.h>
43 
44 #define super OSObject
45 
46 OSDefineMetaClassAndStructors(IOWorkLoop, OSObject);
47 
48 // Block of unused functions intended for future use
49 #if __LP64__
50 OSMetaClassDefineReservedUnused(IOWorkLoop, 0);
51 OSMetaClassDefineReservedUnused(IOWorkLoop, 1);
52 OSMetaClassDefineReservedUnused(IOWorkLoop, 2);
53 #else
54 OSMetaClassDefineReservedUsed(IOWorkLoop, 0);
55 OSMetaClassDefineReservedUsed(IOWorkLoop, 1);
56 OSMetaClassDefineReservedUsed(IOWorkLoop, 2);
57 #endif
58 OSMetaClassDefineReservedUnused(IOWorkLoop, 3);
59 OSMetaClassDefineReservedUnused(IOWorkLoop, 4);
60 OSMetaClassDefineReservedUnused(IOWorkLoop, 5);
61 OSMetaClassDefineReservedUnused(IOWorkLoop, 6);
62 OSMetaClassDefineReservedUnused(IOWorkLoop, 7);
63 
64 enum IOWorkLoopState { kLoopRestart = 0x1, kLoopTerminate = 0x2 };
65 #ifdef __ppc__
66 static inline void SETP(void *addr, unsigned int flag)
67     { unsigned int *num = (unsigned int *) addr; *num |= flag; }
68 static inline void CLRP(void *addr, unsigned int flag)
69     { unsigned int *num = (unsigned int *) addr; *num &= ~flag; }
70 static inline bool ISSETP(void *addr, unsigned int flag)
71     { unsigned int *num = (unsigned int *) addr; return (*num & flag) != 0; }
72 #else
73 static inline void SETP(void *addr, unsigned int flag)
74     { unsigned char *num = (unsigned char *) addr; *num |= flag; }
75 static inline void CLRP(void *addr, unsigned int flag)
76     { unsigned char *num = (unsigned char *) addr; *num &= ~flag; }
77 static inline bool ISSETP(void *addr, unsigned int flag)
78     { unsigned char *num = (unsigned char *) addr; return (*num & flag) != 0; }
79 #endif
80 
81 #define fFlags loopRestart
82 
83 
84 bool IOWorkLoop::init()
85 {
86     // The super init and gateLock allocation MUST be done first
87     if ( !super::init() )
88         return false;
89 
90     if ( gateLock == NULL ) {
91         if ( !( gateLock = IORecursiveLockAlloc()) )
92             return false;
93     }
94 
95     if ( workToDoLock == NULL ) {
96         if ( !(workToDoLock = IOSimpleLockAlloc()) )
97             return false;
98         IOSimpleLockInit(workToDoLock);
99         workToDo = false;
100     }
101 
102     if ( controlG == NULL ) {
103         controlG = IOCommandGate::commandGate(
104             this,
105             OSMemberFunctionCast(
106                 IOCommandGate::Action,
107                 this,
108                 &IOWorkLoop::_maintRequest));
109 
110         if ( !controlG )
111             return false;
112         // Point the controlGate at the workLoop.  Usually addEventSource
113         // does this automatically.  The problem is in this case addEventSource
114         // uses the control gate and it has to be bootstrapped.
115         controlG->setWorkLoop(this);
116         if (addEventSource(controlG) != kIOReturnSuccess)
117             return false;
118     }
119 
120     if ( workThread == NULL ) {
121         thread_continue_t cptr = OSMemberFunctionCast(
122             thread_continue_t,
123             this,
124             &IOWorkLoop::threadMain);
125         if (KERN_SUCCESS != kernel_thread_start(cptr, this, &workThread))
126             return false;
127     }
128 
129     return true;
130 }
131 
132 IOWorkLoop *
133 IOWorkLoop::workLoop()
134 {
135     return IOWorkLoop::workLoopWithOptions(0);
136 }
137 
138 IOWorkLoop *
139 IOWorkLoop::workLoopWithOptions(IOOptionBits options)
140 {
141     IOWorkLoop *me = new IOWorkLoop;
142 
143     if (me && options) {
144 	me->reserved = IONew(ExpansionData, 1);
145 	if (!me->reserved) {
146 	    me->release();
147 	    return 0;
148 	}
149 	me->reserved->options = options;
150     }
151 
152     if (me && !me->init()) {
153         me->release();
154         return 0;
155     }
156 
157     return me;
158 }
159 
160 // Free is called twice:
161 // First when the atomic retainCount transitions from 1 -> 0
162 // Secondly when the work loop itself is commiting hari kari
163 // Hence the each leg of the free must be single threaded.
164 void IOWorkLoop::free()
165 {
166     if (workThread) {
167 	IOInterruptState is;
168 
169 	// If we are here then we must be trying to shut down this work loop
170 	// in this case disable all of the event source, mark the loop
171 	// as terminating and wakeup the work thread itself and return
172 	// Note: we hold the gate across the entire operation mainly for the
173 	// benefit of our event sources so we can disable them cleanly.
174 	closeGate();
175 
176 	disableAllEventSources();
177 
178         is = IOSimpleLockLockDisableInterrupt(workToDoLock);
179 	SETP(&fFlags, kLoopTerminate);
180         thread_wakeup_one((void *) &workToDo);
181         IOSimpleLockUnlockEnableInterrupt(workToDoLock, is);
182 
183 	openGate();
184     }
185     else /* !workThread */ {
186         IOEventSource *event, *next;
187 
188         for (event = eventChain; event; event = next) {
189             next = event->getNext();
190             event->setWorkLoop(0);
191             event->setNext(0);
192             event->release();
193         }
194         eventChain = 0;
195 
196 	// Either we have a partial initialization to clean up
197 	// or the workThread itself is performing hari-kari.
198 	// Either way clean up all of our resources and return.
199 
200 	if (controlG) {
201 	    controlG->release();
202 	    controlG = 0;
203 	}
204 
205 	if (workToDoLock) {
206 	    IOSimpleLockFree(workToDoLock);
207 	    workToDoLock = 0;
208 	}
209 
210 	if (gateLock) {
211 	    IORecursiveLockFree(gateLock);
212 	    gateLock = 0;
213 	}
214 	if (reserved) {
215 	    IODelete(reserved, ExpansionData, 1);
216 	    reserved = 0;
217 	}
218 
219 	super::free();
220     }
221 }
222 
223 IOReturn IOWorkLoop::addEventSource(IOEventSource *newEvent)
224 {
225     return controlG->runCommand((void *) mAddEvent, (void *) newEvent);
226 }
227 
228 IOReturn IOWorkLoop::removeEventSource(IOEventSource *toRemove)
229 {
230     return controlG->runCommand((void *) mRemoveEvent, (void *) toRemove);
231 }
232 
233 void IOWorkLoop::enableAllEventSources() const
234 {
235     IOEventSource *event;
236 
237     for (event = eventChain; event; event = event->getNext())
238         event->enable();
239 }
240 
241 void IOWorkLoop::disableAllEventSources() const
242 {
243     IOEventSource *event;
244 
245     for (event = eventChain; event; event = event->getNext())
246         if (event != controlG)	// Don't disable the control gate
247             event->disable();
248 }
249 
250 void IOWorkLoop::enableAllInterrupts() const
251 {
252     IOEventSource *event;
253 
254     for (event = eventChain; event; event = event->getNext())
255         if (OSDynamicCast(IOInterruptEventSource, event))
256             event->enable();
257 }
258 
259 void IOWorkLoop::disableAllInterrupts() const
260 {
261     IOEventSource *event;
262 
263     for (event = eventChain; event; event = event->getNext())
264         if (OSDynamicCast(IOInterruptEventSource, event))
265             event->disable();
266 }
267 
268 #if KDEBUG
269 #define IOTimeClientS()							\
270 do {									\
271     IOTimeStampStart(IODBG_WORKLOOP(IOWL_CLIENT),			\
272                      (unsigned int) this, (unsigned int) event);	\
273 } while(0)
274 
275 #define IOTimeClientE()							\
276 do {									\
277     IOTimeStampEnd(IODBG_WORKLOOP(IOWL_CLIENT),				\
278                    (unsigned int) this, (unsigned int) event);		\
279 } while(0)
280 
281 #define IOTimeWorkS()							\
282 do {									\
283     IOTimeStampStart(IODBG_WORKLOOP(IOWL_WORK),	(unsigned int) this);	\
284 } while(0)
285 
286 #define IOTimeWorkE()							\
287 do {									\
288     IOTimeStampEnd(IODBG_WORKLOOP(IOWL_WORK),(unsigned int) this);	\
289 } while(0)
290 
291 #else /* !KDEBUG */
292 
293 #define IOTimeClientS()
294 #define IOTimeClientE()
295 #define IOTimeWorkS()
296 #define IOTimeWorkE()
297 
298 #endif /* KDEBUG */
299 
300 /* virtual */ bool IOWorkLoop::runEventSources()
301 {
302     bool res = false;
303     closeGate();
304     if (ISSETP(&fFlags, kLoopTerminate))
305 	goto abort;
306 
307     IOTimeWorkS();
308     bool more;
309     do {
310 	CLRP(&fFlags, kLoopRestart);
311 	more = false;
312 	IOInterruptState is = IOSimpleLockLockDisableInterrupt(workToDoLock);
313 	workToDo = false;
314 	IOSimpleLockUnlockEnableInterrupt(workToDoLock, is);
315 	for (IOEventSource *evnt = eventChain; evnt; evnt = evnt->getNext()) {
316 
317 	    IOTimeClientS();
318 	    more |= evnt->checkForWork();
319 	    IOTimeClientE();
320 
321 	    if (ISSETP(&fFlags, kLoopTerminate))
322 		goto abort;
323 	    else if (fFlags & kLoopRestart) {
324 		more = true;
325 		break;
326 	    }
327 	}
328     } while (more);
329 
330     res = true;
331     IOTimeWorkE();
332 
333 abort:
334     openGate();
335     return res;
336 }
337 
338 /* virtual */ void IOWorkLoop::threadMain()
339 {
340 restartThread:
341     do {
342 	if ( !runEventSources() )
343 	    goto exitThread;
344 
345 	IOInterruptState is = IOSimpleLockLockDisableInterrupt(workToDoLock);
346         if ( !ISSETP(&fFlags, kLoopTerminate) && !workToDo) {
347 	    assert_wait((void *) &workToDo, false);
348 	    IOSimpleLockUnlockEnableInterrupt(workToDoLock, is);
349 	    thread_continue_t cptr = NULL;
350 	    if (!reserved || !(kPreciousStack & reserved->options))
351 		cptr = OSMemberFunctionCast(
352 			thread_continue_t, this, &IOWorkLoop::threadMain);
353 	    thread_block_parameter(cptr, this);
354 	    goto restartThread;
355 	    /* NOTREACHED */
356 	}
357 
358 	// At this point we either have work to do or we need
359 	// to commit suicide.  But no matter
360 	// Clear the simple lock and retore the interrupt state
361 	IOSimpleLockUnlockEnableInterrupt(workToDoLock, is);
362     } while(workToDo);
363 
364 exitThread:
365 	thread_t thread = workThread;
366     workThread = 0;	// Say we don't have a loop and free ourselves
367     free();
368 
369 	thread_deallocate(thread);
370     (void) thread_terminate(thread);
371 }
372 
373 IOThread IOWorkLoop::getThread() const
374 {
375     return workThread;
376 }
377 
378 bool IOWorkLoop::onThread() const
379 {
380     return (IOThreadSelf() == workThread);
381 }
382 
383 bool IOWorkLoop::inGate() const
384 {
385     return IORecursiveLockHaveLock(gateLock);
386 }
387 
388 // Internal APIs used by event sources to control the thread
389 void IOWorkLoop::signalWorkAvailable()
390 {
391     if (workToDoLock) {
392         IOInterruptState is = IOSimpleLockLockDisableInterrupt(workToDoLock);
393         workToDo = true;
394         thread_wakeup_one((void *) &workToDo);
395         IOSimpleLockUnlockEnableInterrupt(workToDoLock, is);
396     }
397 }
398 
399 void IOWorkLoop::openGate()
400 {
401     IORecursiveLockUnlock(gateLock);
402 }
403 
404 void IOWorkLoop::closeGate()
405 {
406     IORecursiveLockLock(gateLock);
407 }
408 
409 bool IOWorkLoop::tryCloseGate()
410 {
411     return IORecursiveLockTryLock(gateLock) != 0;
412 }
413 
414 int IOWorkLoop::sleepGate(void *event, UInt32 interuptibleType)
415 {
416     return IORecursiveLockSleep(gateLock, event, interuptibleType);
417 }
418 
419 int IOWorkLoop::sleepGate(void *event, AbsoluteTime deadline, UInt32 interuptibleType)
420 {
421     return IORecursiveLockSleepDeadline(gateLock, event, deadline, interuptibleType);
422 }
423 
424 void IOWorkLoop::wakeupGate(void *event, bool oneThread)
425 {
426     IORecursiveLockWakeup(gateLock, event, oneThread);
427 }
428 
429 IOReturn IOWorkLoop::runAction(Action inAction, OSObject *target,
430                                   void *arg0, void *arg1,
431                                   void *arg2, void *arg3)
432 {
433     IOReturn res;
434 
435     // closeGate is recursive so don't worry if we already hold the lock.
436     closeGate();
437     res = (*inAction)(target, arg0, arg1, arg2, arg3);
438     openGate();
439 
440     return res;
441 }
442 
443 IOReturn IOWorkLoop::_maintRequest(void *inC, void *inD, void *, void *)
444 {
445     maintCommandEnum command = (maintCommandEnum) (uintptr_t) inC;
446     IOEventSource *inEvent = (IOEventSource *) inD;
447     IOReturn res = kIOReturnSuccess;
448 
449     switch (command)
450     {
451     case mAddEvent:
452         if (!inEvent->getWorkLoop()) {
453             SETP(&fFlags, kLoopRestart);
454 
455             inEvent->retain();
456             inEvent->setWorkLoop(this);
457             inEvent->setNext(0);
458 
459             if (!eventChain)
460                 eventChain = inEvent;
461             else {
462                 IOEventSource *event, *next;
463 
464                 for (event = eventChain; (next = event->getNext()); event = next)
465                     ;
466                 event->setNext(inEvent);
467             }
468         }
469         break;
470 
471     case mRemoveEvent:
472         if (inEvent->getWorkLoop()) {
473             if (eventChain == inEvent)
474                 eventChain = inEvent->getNext();
475             else {
476                 IOEventSource *event, *next;
477 
478                 event = eventChain;
479                 while ((next = event->getNext()) && next != inEvent)
480                     event = next;
481 
482                 if (!next) {
483                     res = kIOReturnBadArgument;
484                     break;
485                 }
486                 event->setNext(inEvent->getNext());
487             }
488 
489             inEvent->setWorkLoop(0);
490             inEvent->setNext(0);
491             inEvent->release();
492             SETP(&fFlags, kLoopRestart);
493         }
494         break;
495 
496     default:
497         return kIOReturnUnsupported;
498     }
499 
500     return res;
501 }
502