]> git.saurik.com Git - apple/xnu.git/blobdiff - iokit/Kernel/IOCommandGate.cpp
xnu-4903.270.47.tar.gz
[apple/xnu.git] / iokit / Kernel / IOCommandGate.cpp
index 9b19d70eefeb5e7dd63bfb88997f44401ed9c654..265b2f7860725434aa52bc57b0b58e176486e637 100644 (file)
@@ -2,7 +2,7 @@
  * Copyright (c) 1998-2000, 2009-2010 Apple Inc. All rights reserved.
  *
  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
- * 
+ *
  * This file contains Original Code and/or Modifications of Original Code
  * as defined in and that are subject to the Apple Public Source License
  * Version 2.0 (the 'License'). You may not use this file except in
  * unlawful or unlicensed copies of an Apple operating system, or to
  * circumvent, violate, or enable the circumvention or violation of, any
  * terms of an Apple operating system software license agreement.
- * 
+ *
  * Please obtain a copy of the License at
  * http://www.opensource.apple.com/apsl/ and read it before using this file.
- * 
+ *
  * The Original Code and all software distributed under the License are
  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
@@ -22,7 +22,7 @@
  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
  * Please see the License for the specific language governing rights and
  * limitations under the License.
- * 
+ *
  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
  */
 #include <libkern/OSDebug.h>
@@ -68,191 +68,266 @@ do { \
 
 #endif /* IOKITSTATS */
 
-bool IOCommandGate::init(OSObject *inOwner, Action inAction)
+bool
+IOCommandGate::init(OSObject *inOwner, Action inAction)
 {
-    bool res = super::init(inOwner, (IOEventSource::Action) inAction);
-    if (res) {
-        IOStatisticsInitializeCounter();
-    }
+       bool res = super::init(inOwner, (IOEventSource::Action) inAction);
+       if (res) {
+               IOStatisticsInitializeCounter();
+       }
 
-    return res;
+       return res;
 }
 
 IOCommandGate *
 IOCommandGate::commandGate(OSObject *inOwner, Action inAction)
 {
-    IOCommandGate *me = new IOCommandGate;
+       IOCommandGate *me = new IOCommandGate;
 
-    if (me && !me->init(inOwner, inAction)) {
-        me->release();
-        return 0;
-    }
+       if (me && !me->init(inOwner, inAction)) {
+               me->release();
+               return 0;
+       }
 
-    return me;
+       return me;
 }
 
-/* virtual */ void IOCommandGate::disable()
+/* virtual */ void
+IOCommandGate::disable()
 {
-    if (workLoop && !workLoop->inGate())
-       OSReportWithBacktrace("IOCommandGate::disable() called when not gated");
+       if (workLoop && !workLoop->inGate()) {
+               OSReportWithBacktrace("IOCommandGate::disable() called when not gated");
+       }
 
-    super::disable();
+       super::disable();
 }
 
-/* virtual */ void IOCommandGate::enable()
+/* virtual */ void
+IOCommandGate::enable()
 {
-    if (workLoop) {
-       closeGate();
-       super::enable();
-       wakeupGate(&enabled, /* oneThread */ false); // Unblock sleeping threads
-       openGate();
-    }
+       if (workLoop) {
+               closeGate();
+               super::enable();
+               wakeupGate(&enabled, /* oneThread */ false); // Unblock sleeping threads
+               openGate();
+       }
 }
 
-/* virtual */ void IOCommandGate::free()
+/* virtual */ void
+IOCommandGate::free()
 {
-    setWorkLoop(0);
-    super::free();
+       if (workLoop) {
+               setWorkLoop(0);
+       }
+       super::free();
 }
 
-/* virtual */ void IOCommandGate::setWorkLoop(IOWorkLoop *inWorkLoop)
+enum{
+       kSleepersRemoved     = 0x00000001,
+       kSleepersWaitEnabled = 0x00000002,
+       kSleepersActions     = 0x00000100,
+       kSleepersActionsMask = 0xffffff00,
+};
+
+/* virtual */ void
+IOCommandGate::setWorkLoop(IOWorkLoop *inWorkLoop)
 {
-    uintptr_t *sleepersP = (uintptr_t *) &reserved;
-    if (!inWorkLoop && workLoop) {             // tearing down
-       closeGate();
-       *sleepersP |= 1;
-       while (*sleepersP >> 1) {
-           thread_wakeup_with_result(&enabled, THREAD_INTERRUPTED);
-           sleepGate(sleepersP, THREAD_UNINT);
+       IOWorkLoop * wl;
+       uintptr_t  * sleepersP = (uintptr_t *) &reserved;
+       bool         defer;
+
+       if (!inWorkLoop && (wl = workLoop)) {           // tearing down
+               wl->closeGate();
+               *sleepersP |= kSleepersRemoved;
+               while (*sleepersP & kSleepersWaitEnabled) {
+                       thread_wakeup_with_result(&enabled, THREAD_INTERRUPTED);
+                       sleepGate(sleepersP, THREAD_UNINT);
+               }
+               *sleepersP &= ~kSleepersWaitEnabled;
+               defer = (0 != (kSleepersActionsMask & *sleepersP));
+               if (!defer) {
+                       super::setWorkLoop(0);
+                       *sleepersP &= ~kSleepersRemoved;
+               }
+               wl->openGate();
+               return;
        }
-       *sleepersP = 0;
-       openGate();
-    }
-    else
 
-    super::setWorkLoop(inWorkLoop);
+       super::setWorkLoop(inWorkLoop);
 }
 
-IOReturn IOCommandGate::runCommand(void *arg0, void *arg1,
-                                   void *arg2, void *arg3)
+IOReturn
+IOCommandGate::runCommand(void *arg0, void *arg1,
+    void *arg2, void *arg3)
 {
-    return runAction((Action) action, arg0, arg1, arg2, arg3);
+       return runAction((Action) action, arg0, arg1, arg2, arg3);
 }
 
-IOReturn IOCommandGate::attemptCommand(void *arg0, void *arg1,
-                                       void *arg2, void *arg3)
+IOReturn
+IOCommandGate::attemptCommand(void *arg0, void *arg1,
+    void *arg2, void *arg3)
 {
-    return attemptAction((Action) action, arg0, arg1, arg2, arg3);
+       return attemptAction((Action) action, arg0, arg1, arg2, arg3);
 }
 
-IOReturn IOCommandGate::runAction(Action inAction,
-                                  void *arg0, void *arg1,
-                                  void *arg2, void *arg3)
+
+static IOReturn
+IOCommandGateActionToBlock(OSObject *owner,
+    void *arg0, void *arg1,
+    void *arg2, void *arg3)
 {
-    if (!inAction)
-        return kIOReturnBadArgument;
+       return ((IOEventSource::ActionBlock) arg0)();
+}
 
-    // closeGate is recursive needn't worry if we already hold the lock.
-    closeGate();
+IOReturn
+IOCommandGate::runActionBlock(ActionBlock action)
+{
+       return runAction(&IOCommandGateActionToBlock, action);
+}
 
-    // If the command gate is disabled and we aren't on the workloop thread
-    // itself then sleep until we get enabled.
-    IOReturn res;
-    if (!workLoop->onThread()) {
-       while (!enabled) {
-           uintptr_t *sleepersP = (uintptr_t *) &reserved;
+IOReturn
+IOCommandGate::runAction(Action inAction,
+    void *arg0, void *arg1,
+    void *arg2, void *arg3)
+{
+       IOWorkLoop * wl;
+       uintptr_t  * sleepersP;
 
-           *sleepersP += 2;
-           IOReturn res = sleepGate(&enabled, THREAD_ABORTSAFE);
-           *sleepersP -= 2;
+       if (!inAction) {
+               return kIOReturnBadArgument;
+       }
+       if (!(wl = workLoop)) {
+               return kIOReturnNotReady;
+       }
 
-           bool wakeupTearDown = (*sleepersP & 1);
-           if (res || wakeupTearDown) {
-               openGate();
+       // closeGate is recursive needn't worry if we already hold the lock.
+       wl->closeGate();
+       sleepersP = (uintptr_t *) &reserved;
+
+       // If the command gate is disabled and we aren't on the workloop thread
+       // itself then sleep until we get enabled.
+       IOReturn res;
+       if (!wl->onThread()) {
+               while (!enabled) {
+                       IOReturn sleepResult = kIOReturnSuccess;
+                       if (workLoop) {
+                               *sleepersP |= kSleepersWaitEnabled;
+                               sleepResult = wl->sleepGate(&enabled, THREAD_INTERRUPTIBLE);
+                               *sleepersP &= ~kSleepersWaitEnabled;
+                       }
+                       bool wakeupTearDown = (!workLoop || (0 != (*sleepersP & kSleepersRemoved)));
+                       if ((kIOReturnSuccess != sleepResult) || wakeupTearDown) {
+                               wl->openGate();
+
+                               if (wakeupTearDown) {
+                                       wl->wakeupGate(sleepersP, false); // No further resources used
+                               }
+                               return kIOReturnAborted;
+                       }
+               }
+       }
 
-                if (wakeupTearDown)
-                    commandWakeup(sleepersP);  // No further resources used
+       bool trace = (gIOKitTrace & kIOTraceCommandGates) ? true : false;
 
-               return kIOReturnAborted;
-           }
+       if (trace) {
+               IOTimeStampStartConstant(IODBG_CMDQ(IOCMDQ_ACTION),
+                   VM_KERNEL_ADDRHIDE(inAction), VM_KERNEL_ADDRHIDE(owner));
        }
-    }
 
-    bool trace = ( gIOKitTrace & kIOTraceCommandGates ) ? true : false;
-       
-       if (trace)
-               IOTimeStampStartConstant(IODBG_CMDQ(IOCMDQ_ACTION),
-                                        VM_KERNEL_UNSLIDE(inAction), (uintptr_t) owner);
-       
-    IOStatisticsActionCall();
-       
-    // Must be gated and on the work loop or enabled
-    res = (*inAction)(owner, arg0, arg1, arg2, arg3);
-       
-       if (trace)
+       IOStatisticsActionCall();
+
+       // Must be gated and on the work loop or enabled
+
+       *sleepersP += kSleepersActions;
+       res = (*inAction)(owner, arg0, arg1, arg2, arg3);
+       *sleepersP -= kSleepersActions;
+
+       if (trace) {
                IOTimeStampEndConstant(IODBG_CMDQ(IOCMDQ_ACTION),
-                                      VM_KERNEL_UNSLIDE(inAction), (uintptr_t) owner);
-    
-    openGate();
-       
-    return res;
+                   VM_KERNEL_ADDRHIDE(inAction), VM_KERNEL_ADDRHIDE(owner));
+       }
+
+       if (kSleepersRemoved == ((kSleepersActionsMask | kSleepersRemoved) & *sleepersP)) {
+               // no actions outstanding
+               *sleepersP &= ~kSleepersRemoved;
+               super::setWorkLoop(0);
+       }
+
+       wl->openGate();
+
+       return res;
 }
 
-IOReturn IOCommandGate::attemptAction(Action inAction,
-                                      void *arg0, void *arg1,
-                                      void *arg2, void *arg3)
+IOReturn
+IOCommandGate::attemptAction(Action inAction,
+    void *arg0, void *arg1,
+    void *arg2, void *arg3)
 {
-    IOReturn res;
-
-    if (!inAction)
-        return kIOReturnBadArgument;
-
-    // Try to close the gate if can't get return immediately.
-    if (!tryCloseGate())
-        return kIOReturnCannotLock;
-
-    // If the command gate is disabled then sleep until we get a wakeup
-    if (!workLoop->onThread() && !enabled)
-        res = kIOReturnNotPermitted;
-    else {
-               
-        bool trace = ( gIOKitTrace & kIOTraceCommandGates ) ? true : false;
-               
-        if (trace)
-            IOTimeStampStartConstant(IODBG_CMDQ(IOCMDQ_ACTION),
-                                    VM_KERNEL_UNSLIDE(inAction), (uintptr_t) owner);
-        
-        IOStatisticsActionCall();
-        
-        res = (*inAction)(owner, arg0, arg1, arg2, arg3);
-               
-        if (trace)
-            IOTimeStampEndConstant(IODBG_CMDQ(IOCMDQ_ACTION),
-                                  VM_KERNEL_UNSLIDE(inAction), (uintptr_t) owner);
-    }
-
-    openGate();
-
-    return res;
+       IOReturn res;
+       IOWorkLoop * wl;
+
+       if (!inAction) {
+               return kIOReturnBadArgument;
+       }
+       if (!(wl = workLoop)) {
+               return kIOReturnNotReady;
+       }
+
+       // Try to close the gate if can't get return immediately.
+       if (!wl->tryCloseGate()) {
+               return kIOReturnCannotLock;
+       }
+
+       // If the command gate is disabled then sleep until we get a wakeup
+       if (!wl->onThread() && !enabled) {
+               res = kIOReturnNotPermitted;
+       } else {
+               bool trace = (gIOKitTrace & kIOTraceCommandGates) ? true : false;
+
+               if (trace) {
+                       IOTimeStampStartConstant(IODBG_CMDQ(IOCMDQ_ACTION),
+                           VM_KERNEL_ADDRHIDE(inAction), VM_KERNEL_ADDRHIDE(owner));
+               }
+
+               IOStatisticsActionCall();
+
+               res = (*inAction)(owner, arg0, arg1, arg2, arg3);
+
+               if (trace) {
+                       IOTimeStampEndConstant(IODBG_CMDQ(IOCMDQ_ACTION),
+                           VM_KERNEL_ADDRHIDE(inAction), VM_KERNEL_ADDRHIDE(owner));
+               }
+       }
+
+       wl->openGate();
+
+       return res;
 }
 
-IOReturn IOCommandGate::commandSleep(void *event, UInt32 interruptible)
+IOReturn
+IOCommandGate::commandSleep(void *event, UInt32 interruptible)
 {
-    if (!workLoop->inGate())
-        return kIOReturnNotPermitted;
+       if (!workLoop->inGate()) {
+               /* The equivalent of 'msleep' while not holding the mutex is invalid */
+               panic("invalid commandSleep while not holding the gate");
+       }
 
-    return sleepGate(event, interruptible);
+       return sleepGate(event, interruptible);
 }
 
-IOReturn IOCommandGate::commandSleep(void *event, AbsoluteTime deadline, UInt32 interruptible)
+IOReturn
+IOCommandGate::commandSleep(void *event, AbsoluteTime deadline, UInt32 interruptible)
 {
-    if (!workLoop->inGate())
-        return kIOReturnNotPermitted;
+       if (!workLoop->inGate()) {
+               /* The equivalent of 'msleep' while not holding the mutex is invalid */
+               panic("invalid commandSleep while not holding the gate");
+       }
 
-    return sleepGate(event, deadline, interruptible);
+       return sleepGate(event, deadline, interruptible);
 }
 
-void IOCommandGate::commandWakeup(void *event, bool oneThread)
+void
+IOCommandGate::commandWakeup(void *event, bool oneThread)
 {
-    wakeupGate(event, oneThread);
+       wakeupGate(event, oneThread);
 }