]> git.saurik.com Git - apple/xnu.git/blob - iokit/Kernel/IOWorkLoop.cpp
xnu-1504.9.37.tar.gz
[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
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 }