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