]> git.saurik.com Git - apple/xnu.git/blob - iokit/Kernel/IOWorkLoop.cpp
09135d172fb390838b270f640383d76276020ff8
[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 workToDo = more = false;
305 for (IOEventSource *evnt = eventChain; evnt; evnt = evnt->getNext()) {
306
307 IOTimeClientS();
308 more |= evnt->checkForWork();
309 IOTimeClientE();
310
311 if (ISSETP(&fFlags, kLoopTerminate))
312 goto abort;
313 else if (fFlags & kLoopRestart) {
314 more = true;
315 break;
316 }
317 }
318 } while (more);
319
320 res = true;
321 IOTimeWorkE();
322
323 abort:
324 openGate();
325 return res;
326 }
327
328 /* virtual */ void IOWorkLoop::threadMain()
329 {
330 restartThread:
331 do {
332 if ( !runEventSources() )
333 goto exitThread;
334
335 IOInterruptState is = IOSimpleLockLockDisableInterrupt(workToDoLock);
336 if ( !ISSETP(&fFlags, kLoopTerminate) && !workToDo) {
337 assert_wait((void *) &workToDo, false);
338 IOSimpleLockUnlockEnableInterrupt(workToDoLock, is);
339 thread_continue_t cptr = NULL;
340 if (!reserved || !(kPreciousStack & reserved->options))
341 cptr = OSMemberFunctionCast(
342 thread_continue_t, this, &IOWorkLoop::threadMain);
343 thread_block_parameter(cptr, this);
344 goto restartThread;
345 /* NOTREACHED */
346 }
347
348 // At this point we either have work to do or we need
349 // to commit suicide. But no matter
350 // Clear the simple lock and retore the interrupt state
351 IOSimpleLockUnlockEnableInterrupt(workToDoLock, is);
352 } while(workToDo);
353
354 exitThread:
355 workThread = 0; // Say we don't have a loop and free ourselves
356 free();
357 IOExitThread();
358 }
359
360 IOThread IOWorkLoop::getThread() const
361 {
362 return workThread;
363 }
364
365 bool IOWorkLoop::onThread() const
366 {
367 return (IOThreadSelf() == workThread);
368 }
369
370 bool IOWorkLoop::inGate() const
371 {
372 return IORecursiveLockHaveLock(gateLock);
373 }
374
375 // Internal APIs used by event sources to control the thread
376 void IOWorkLoop::signalWorkAvailable()
377 {
378 if (workToDoLock) {
379 IOInterruptState is = IOSimpleLockLockDisableInterrupt(workToDoLock);
380 workToDo = true;
381 thread_wakeup_one((void *) &workToDo);
382 IOSimpleLockUnlockEnableInterrupt(workToDoLock, is);
383 }
384 }
385
386 void IOWorkLoop::openGate()
387 {
388 IORecursiveLockUnlock(gateLock);
389 }
390
391 void IOWorkLoop::closeGate()
392 {
393 IORecursiveLockLock(gateLock);
394 }
395
396 bool IOWorkLoop::tryCloseGate()
397 {
398 return IORecursiveLockTryLock(gateLock) != 0;
399 }
400
401 int IOWorkLoop::sleepGate(void *event, UInt32 interuptibleType)
402 {
403 return IORecursiveLockSleep(gateLock, event, interuptibleType);
404 }
405
406 void IOWorkLoop::wakeupGate(void *event, bool oneThread)
407 {
408 IORecursiveLockWakeup(gateLock, event, oneThread);
409 }
410
411 IOReturn IOWorkLoop::runAction(Action inAction, OSObject *target,
412 void *arg0, void *arg1,
413 void *arg2, void *arg3)
414 {
415 IOReturn res;
416
417 // closeGate is recursive so don't worry if we already hold the lock.
418 closeGate();
419 res = (*inAction)(target, arg0, arg1, arg2, arg3);
420 openGate();
421
422 return res;
423 }
424
425 IOReturn IOWorkLoop::_maintRequest(void *inC, void *inD, void *, void *)
426 {
427 maintCommandEnum command = (maintCommandEnum) (vm_address_t) inC;
428 IOEventSource *inEvent = (IOEventSource *) inD;
429 IOReturn res = kIOReturnSuccess;
430
431 switch (command)
432 {
433 case mAddEvent:
434 if (!inEvent->getWorkLoop()) {
435 SETP(&fFlags, kLoopRestart);
436
437 inEvent->retain();
438 inEvent->setWorkLoop(this);
439 inEvent->setNext(0);
440
441 if (!eventChain)
442 eventChain = inEvent;
443 else {
444 IOEventSource *event, *next;
445
446 for (event = eventChain; (next = event->getNext()); event = next)
447 ;
448 event->setNext(inEvent);
449 }
450 }
451 break;
452
453 case mRemoveEvent:
454 if (inEvent->getWorkLoop()) {
455 if (eventChain == inEvent)
456 eventChain = inEvent->getNext();
457 else {
458 IOEventSource *event, *next;
459
460 event = eventChain;
461 while ((next = event->getNext()) && next != inEvent)
462 event = next;
463
464 if (!next) {
465 res = kIOReturnBadArgument;
466 break;
467 }
468 event->setNext(inEvent->getNext());
469 }
470
471 inEvent->setWorkLoop(0);
472 inEvent->setNext(0);
473 inEvent->release();
474 SETP(&fFlags, kLoopRestart);
475 }
476 break;
477
478 default:
479 return kIOReturnUnsupported;
480 }
481
482 return res;
483 }