]> git.saurik.com Git - apple/xnu.git/blame - iokit/Kernel/IOWorkLoop.cpp
xnu-1504.7.4.tar.gz
[apple/xnu.git] / iokit / Kernel / IOWorkLoop.cpp
CommitLineData
1c79356b 1/*
2d21ac55 2 * Copyright (c) 1998-2007 Apple Inc. All rights reserved.
1c79356b 3 *
2d21ac55 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
1c79356b 5 *
2d21ac55
A
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.
8f6c56a5 14 *
2d21ac55
A
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
8f6c56a5
A
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
2d21ac55
A
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.
8f6c56a5 25 *
2d21ac55 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
1c79356b
A
27 */
28/*
29Copyright (c) 1998 Apple Computer, Inc. All rights reserved.
30
31HISTORY
32 1998-7-13 Godfrey van der Linden(gvdl)
33 Created.
34*/
b0d623f7
A
35
36#include <pexpert/pexpert.h>
1c79356b
A
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>
2d21ac55 42#include <libkern/OSDebug.h>
1c79356b
A
43
44#define super OSObject
45
46OSDefineMetaClassAndStructors(IOWorkLoop, OSObject);
47
48// Block of unused functions intended for future use
b0d623f7
A
49#if __LP64__
50OSMetaClassDefineReservedUnused(IOWorkLoop, 0);
51OSMetaClassDefineReservedUnused(IOWorkLoop, 1);
52OSMetaClassDefineReservedUnused(IOWorkLoop, 2);
53#else
0b4e3aa0 54OSMetaClassDefineReservedUsed(IOWorkLoop, 0);
0c530ab8 55OSMetaClassDefineReservedUsed(IOWorkLoop, 1);
b0d623f7
A
56OSMetaClassDefineReservedUsed(IOWorkLoop, 2);
57#endif
1c79356b
A
58OSMetaClassDefineReservedUnused(IOWorkLoop, 3);
59OSMetaClassDefineReservedUnused(IOWorkLoop, 4);
60OSMetaClassDefineReservedUnused(IOWorkLoop, 5);
61OSMetaClassDefineReservedUnused(IOWorkLoop, 6);
62OSMetaClassDefineReservedUnused(IOWorkLoop, 7);
63
64enum IOWorkLoopState { kLoopRestart = 0x1, kLoopTerminate = 0x2 };
2d21ac55 65#ifdef __ppc__
1c79356b
A
66static inline void SETP(void *addr, unsigned int flag)
67 { unsigned int *num = (unsigned int *) addr; *num |= flag; }
68static inline void CLRP(void *addr, unsigned int flag)
69 { unsigned int *num = (unsigned int *) addr; *num &= ~flag; }
70static inline bool ISSETP(void *addr, unsigned int flag)
71 { unsigned int *num = (unsigned int *) addr; return (*num & flag) != 0; }
2d21ac55
A
72#else
73static inline void SETP(void *addr, unsigned int flag)
74 { unsigned char *num = (unsigned char *) addr; *num |= flag; }
75static inline void CLRP(void *addr, unsigned int flag)
76 { unsigned char *num = (unsigned char *) addr; *num &= ~flag; }
77static inline bool ISSETP(void *addr, unsigned int flag)
78 { unsigned char *num = (unsigned char *) addr; return (*num & flag) != 0; }
79#endif
1c79356b
A
80
81#define fFlags loopRestart
82
b0d623f7 83
1c79356b
A
84bool IOWorkLoop::init()
85{
86 // The super init and gateLock allocation MUST be done first
87 if ( !super::init() )
88 return false;
2d21ac55
A
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 }
1c79356b 101
2d21ac55
A
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 }
1c79356b 119
2d21ac55 120 if ( workThread == NULL ) {
b0d623f7
A
121 thread_continue_t cptr = OSMemberFunctionCast(
122 thread_continue_t,
2d21ac55
A
123 this,
124 &IOWorkLoop::threadMain);
b0d623f7 125 if (KERN_SUCCESS != kernel_thread_start(cptr, this, &workThread))
2d21ac55
A
126 return false;
127 }
1c79356b
A
128
129 return true;
130}
131
132IOWorkLoop *
133IOWorkLoop::workLoop()
2d21ac55
A
134{
135 return IOWorkLoop::workLoopWithOptions(0);
136}
137
138IOWorkLoop *
139IOWorkLoop::workLoopWithOptions(IOOptionBits options)
1c79356b
A
140{
141 IOWorkLoop *me = new IOWorkLoop;
142
2d21ac55
A
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
1c79356b 152 if (me && !me->init()) {
55e303ae 153 me->release();
1c79356b
A
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.
164void 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
2d21ac55 170 // in this case disable all of the event source, mark the loop
1c79356b
A
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
2d21ac55
A
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.
1c79356b
A
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 }
2d21ac55
A
214 if (reserved) {
215 IODelete(reserved, ExpansionData, 1);
216 reserved = 0;
217 }
1c79356b
A
218
219 super::free();
220 }
221}
222
223IOReturn IOWorkLoop::addEventSource(IOEventSource *newEvent)
224{
225 return controlG->runCommand((void *) mAddEvent, (void *) newEvent);
226}
227
228IOReturn IOWorkLoop::removeEventSource(IOEventSource *toRemove)
229{
230 return controlG->runCommand((void *) mRemoveEvent, (void *) toRemove);
231}
232
233void IOWorkLoop::enableAllEventSources() const
234{
235 IOEventSource *event;
236
237 for (event = eventChain; event; event = event->getNext())
238 event->enable();
239}
240
241void 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
250void 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
259void 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() \
270do { \
271 IOTimeStampStart(IODBG_WORKLOOP(IOWL_CLIENT), \
272 (unsigned int) this, (unsigned int) event); \
273} while(0)
274
275#define IOTimeClientE() \
276do { \
277 IOTimeStampEnd(IODBG_WORKLOOP(IOWL_CLIENT), \
278 (unsigned int) this, (unsigned int) event); \
279} while(0)
280
281#define IOTimeWorkS() \
282do { \
283 IOTimeStampStart(IODBG_WORKLOOP(IOWL_WORK), (unsigned int) this); \
284} while(0)
285
286#define IOTimeWorkE() \
287do { \
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
0c530ab8 300/* virtual */ bool IOWorkLoop::runEventSources()
1c79356b 301{
0c530ab8
A
302 bool res = false;
303 closeGate();
304 if (ISSETP(&fFlags, kLoopTerminate))
305 goto abort;
21362eb3 306
6601e61a 307 IOTimeWorkS();
0c530ab8
A
308 bool more;
309 do {
310 CLRP(&fFlags, kLoopRestart);
36401178
A
311 more = false;
312 IOInterruptState is = IOSimpleLockLockDisableInterrupt(workToDoLock);
313 workToDo = false;
314 IOSimpleLockUnlockEnableInterrupt(workToDoLock, is);
0c530ab8
A
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);
4452a7af 329
0c530ab8 330 res = true;
6601e61a
A
331 IOTimeWorkE();
332
0c530ab8
A
333abort:
334 openGate();
335 return res;
336}
337
338/* virtual */ void IOWorkLoop::threadMain()
339{
2d21ac55 340restartThread:
0c530ab8
A
341 do {
342 if ( !runEventSources() )
343 goto exitThread;
6601e61a 344
0c530ab8 345 IOInterruptState is = IOSimpleLockLockDisableInterrupt(workToDoLock);
1c79356b
A
346 if ( !ISSETP(&fFlags, kLoopTerminate) && !workToDo) {
347 assert_wait((void *) &workToDo, false);
348 IOSimpleLockUnlockEnableInterrupt(workToDoLock, is);
2d21ac55
A
349 thread_continue_t cptr = NULL;
350 if (!reserved || !(kPreciousStack & reserved->options))
351 cptr = OSMemberFunctionCast(
352 thread_continue_t, this, &IOWorkLoop::threadMain);
0c530ab8 353 thread_block_parameter(cptr, this);
2d21ac55 354 goto restartThread;
1c79356b
A
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);
0c530ab8 362 } while(workToDo);
1c79356b
A
363
364exitThread:
b0d623f7 365 thread_t thread = workThread;
1c79356b
A
366 workThread = 0; // Say we don't have a loop and free ourselves
367 free();
b0d623f7
A
368
369 thread_deallocate(thread);
370 (void) thread_terminate(thread);
1c79356b
A
371}
372
373IOThread IOWorkLoop::getThread() const
374{
375 return workThread;
376}
377
378bool IOWorkLoop::onThread() const
379{
380 return (IOThreadSelf() == workThread);
381}
382
383bool IOWorkLoop::inGate() const
384{
385 return IORecursiveLockHaveLock(gateLock);
386}
387
388// Internal APIs used by event sources to control the thread
389void 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
399void IOWorkLoop::openGate()
400{
401 IORecursiveLockUnlock(gateLock);
402}
403
404void IOWorkLoop::closeGate()
405{
406 IORecursiveLockLock(gateLock);
407}
408
409bool IOWorkLoop::tryCloseGate()
410{
411 return IORecursiveLockTryLock(gateLock) != 0;
412}
413
414int IOWorkLoop::sleepGate(void *event, UInt32 interuptibleType)
415{
416 return IORecursiveLockSleep(gateLock, event, interuptibleType);
417}
418
b0d623f7
A
419int IOWorkLoop::sleepGate(void *event, AbsoluteTime deadline, UInt32 interuptibleType)
420{
421 return IORecursiveLockSleepDeadline(gateLock, event, deadline, interuptibleType);
422}
423
1c79356b
A
424void IOWorkLoop::wakeupGate(void *event, bool oneThread)
425{
426 IORecursiveLockWakeup(gateLock, event, oneThread);
427}
428
0b4e3aa0 429IOReturn IOWorkLoop::runAction(Action inAction, OSObject *target,
55e303ae
A
430 void *arg0, void *arg1,
431 void *arg2, void *arg3)
0b4e3aa0
A
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
1c79356b
A
443IOReturn IOWorkLoop::_maintRequest(void *inC, void *inD, void *, void *)
444{
b0d623f7 445 maintCommandEnum command = (maintCommandEnum) (uintptr_t) inC;
1c79356b
A
446 IOEventSource *inEvent = (IOEventSource *) inD;
447 IOReturn res = kIOReturnSuccess;
448
449 switch (command)
450 {
451 case mAddEvent:
9bccf70c
A
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 }
1c79356b
A
468 }
469 break;
470
471 case mRemoveEvent:
9bccf70c
A
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());
1c79356b 487 }
9bccf70c
A
488
489 inEvent->setWorkLoop(0);
490 inEvent->setNext(0);
491 inEvent->release();
492 SETP(&fFlags, kLoopRestart);
1c79356b 493 }
1c79356b
A
494 break;
495
496 default:
497 return kIOReturnUnsupported;
498 }
499
500 return res;
501}