]> 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 53a99baa9e2929cf8bbd53730d732cb16cc22de6..265b2f7860725434aa52bc57b0b58e176486e637 100644 (file)
@@ -1,33 +1,46 @@
 /*
- * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 1998-2000, 2009-2010 Apple Inc. All rights reserved.
  *
- * @APPLE_LICENSE_HEADER_START@
- * 
- * The contents of this file constitute Original Code as defined in and
- * are subject to the Apple Public Source License Version 1.1 (the
- * "License").  You may not use this file except in compliance with the
- * License.  Please obtain a copy of the License at
- * http://www.apple.com/publicsource and read it before using this file.
- * 
- * This Original Code and all software distributed under the License are
- * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * @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
+ * compliance with the License. The rights granted to you under the License
+ * may not be used to create, or enable the creation or redistribution of,
+ * 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,
  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
- * License for the specific language governing rights and limitations
- * under the License.
- * 
- * @APPLE_LICENSE_HEADER_END@
+ * 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>
+
 #include <IOKit/IOCommandGate.h>
 #include <IOKit/IOWorkLoop.h>
 #include <IOKit/IOReturn.h>
 #include <IOKit/IOTimeStamp.h>
+#include <IOKit/IOKitDebug.h>
 
 #define super IOEventSource
 
 OSDefineMetaClassAndStructors(IOCommandGate, IOEventSource)
+#if __LP64__
 OSMetaClassDefineReservedUnused(IOCommandGate, 0);
+#else
+OSMetaClassDefineReservedUsed(IOCommandGate, 0);
+#endif
 OSMetaClassDefineReservedUnused(IOCommandGate, 1);
 OSMetaClassDefineReservedUnused(IOCommandGate, 2);
 OSMetaClassDefineReservedUnused(IOCommandGate, 3);
@@ -36,132 +49,285 @@ OSMetaClassDefineReservedUnused(IOCommandGate, 5);
 OSMetaClassDefineReservedUnused(IOCommandGate, 6);
 OSMetaClassDefineReservedUnused(IOCommandGate, 7);
 
-bool IOCommandGate::checkForWork() { return false; }
+#if IOKITSTATS
+
+#define IOStatisticsInitializeCounter() \
+do { \
+       IOStatistics::setCounterType(IOEventSource::reserved->counter, kIOStatisticsCommandGateCounter); \
+} while (0)
+
+#define IOStatisticsActionCall() \
+do { \
+       IOStatistics::countCommandGateActionCall(IOEventSource::reserved->counter); \
+} while (0)
+
+#else
 
-bool IOCommandGate::init(OSObject *inOwner, Action inAction = 0)
+#define IOStatisticsInitializeCounter()
+#define IOStatisticsActionCall()
+
+#endif /* IOKITSTATS */
+
+bool
+IOCommandGate::init(OSObject *inOwner, Action inAction)
 {
-    return super::init(inOwner, (IOEventSource::Action) inAction);
+       bool res = super::init(inOwner, (IOEventSource::Action) inAction);
+       if (res) {
+               IOStatisticsInitializeCounter();
+       }
+
+       return res;
 }
 
 IOCommandGate *
-IOCommandGate::commandGate(OSObject *inOwner, Action inAction = 0)
+IOCommandGate::commandGate(OSObject *inOwner, Action inAction)
 {
-    IOCommandGate *me = new IOCommandGate;
+       IOCommandGate *me = new IOCommandGate;
 
-    if (me && !me->init(inOwner, inAction)) {
-        me->free();
-        return 0;
-    }
+       if (me && !me->init(inOwner, inAction)) {
+               me->release();
+               return 0;
+       }
 
-    return me;
+       return me;
 }
 
-IOReturn IOCommandGate::runCommand(void *arg0 = 0, void *arg1 = 0,
-                                   void *arg2 = 0, void *arg3 = 0)
+/* virtual */ void
+IOCommandGate::disable()
 {
-    IOReturn res;
+       if (workLoop && !workLoop->inGate()) {
+               OSReportWithBacktrace("IOCommandGate::disable() called when not gated");
+       }
 
-    if (!enabled)
-        return kIOReturnNotPermitted;
+       super::disable();
+}
 
-    if (!action)
-        return kIOReturnNoResources;
+/* virtual */ void
+IOCommandGate::enable()
+{
+       if (workLoop) {
+               closeGate();
+               super::enable();
+               wakeupGate(&enabled, /* oneThread */ false); // Unblock sleeping threads
+               openGate();
+       }
+}
 
-    // closeGate is recursive so don't worry if we already hold the lock.
-    IOTimeStampConstant(IODBG_CMDQ(IOCMDQ_ACTION),
-                       (unsigned int) action, (unsigned int) owner);
+/* virtual */ void
+IOCommandGate::free()
+{
+       if (workLoop) {
+               setWorkLoop(0);
+       }
+       super::free();
+}
 
-    closeGate();
-    res = (*(Action) action)(owner, arg0, arg1, arg2, arg3);
-    openGate();
+enum{
+       kSleepersRemoved     = 0x00000001,
+       kSleepersWaitEnabled = 0x00000002,
+       kSleepersActions     = 0x00000100,
+       kSleepersActionsMask = 0xffffff00,
+};
 
-    return res;
+/* virtual */ void
+IOCommandGate::setWorkLoop(IOWorkLoop *inWorkLoop)
+{
+       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;
+       }
+
+       super::setWorkLoop(inWorkLoop);
 }
 
-IOReturn IOCommandGate::runAction(Action inAction,
-                                  void *arg0 = 0, void *arg1 = 0,
-                                  void *arg2 = 0, void *arg3 = 0)
+IOReturn
+IOCommandGate::runCommand(void *arg0, void *arg1,
+    void *arg2, void *arg3)
 {
-    IOReturn res;
-
-    if (!enabled)
-        return kIOReturnNotPermitted;
-
-    if (!inAction)
-        return kIOReturnBadArgument;
+       return runAction((Action) action, arg0, arg1, arg2, arg3);
+}
 
-    IOTimeStampConstant(IODBG_CMDQ(IOCMDQ_ACTION),
-                       (unsigned int) inAction, (unsigned int) owner);
+IOReturn
+IOCommandGate::attemptCommand(void *arg0, void *arg1,
+    void *arg2, void *arg3)
+{
+       return attemptAction((Action) action, arg0, arg1, arg2, arg3);
+}
 
-    // closeGate is recursive so don't worry if we already hold the lock.
-    closeGate();
-    res = (*inAction)(owner, arg0, arg1, arg2, arg3);
-    openGate();
 
-    return res;
+static IOReturn
+IOCommandGateActionToBlock(OSObject *owner,
+    void *arg0, void *arg1,
+    void *arg2, void *arg3)
+{
+       return ((IOEventSource::ActionBlock) arg0)();
 }
 
-IOReturn IOCommandGate::attemptCommand(void *arg0 = 0, void *arg1 = 0,
-                                       void *arg2 = 0, void *arg3 = 0)
+IOReturn
+IOCommandGate::runActionBlock(ActionBlock action)
 {
-    IOReturn res;
+       return runAction(&IOCommandGateActionToBlock, action);
+}
 
-    if (!enabled)
-        return kIOReturnNotPermitted;
+IOReturn
+IOCommandGate::runAction(Action inAction,
+    void *arg0, void *arg1,
+    void *arg2, void *arg3)
+{
+       IOWorkLoop * wl;
+       uintptr_t  * sleepersP;
+
+       if (!inAction) {
+               return kIOReturnBadArgument;
+       }
+       if (!(wl = workLoop)) {
+               return kIOReturnNotReady;
+       }
+
+       // 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;
+                       }
+               }
+       }
+
+       bool trace = (gIOKitTrace & kIOTraceCommandGates) ? true : false;
+
+       if (trace) {
+               IOTimeStampStartConstant(IODBG_CMDQ(IOCMDQ_ACTION),
+                   VM_KERNEL_ADDRHIDE(inAction), VM_KERNEL_ADDRHIDE(owner));
+       }
+
+       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_ADDRHIDE(inAction), VM_KERNEL_ADDRHIDE(owner));
+       }
+
+       if (kSleepersRemoved == ((kSleepersActionsMask | kSleepersRemoved) & *sleepersP)) {
+               // no actions outstanding
+               *sleepersP &= ~kSleepersRemoved;
+               super::setWorkLoop(0);
+       }
+
+       wl->openGate();
+
+       return res;
+}
 
-    if (!action)
-        return kIOReturnNoResources;
+IOReturn
+IOCommandGate::attemptAction(Action inAction,
+    void *arg0, void *arg1,
+    void *arg2, void *arg3)
+{
+       IOReturn res;
+       IOWorkLoop * wl;
 
-    // Try to hold the lock if can't get return immediately.
-    if (!tryCloseGate())
-        return kIOReturnCannotLock;
+       if (!inAction) {
+               return kIOReturnBadArgument;
+       }
+       if (!(wl = workLoop)) {
+               return kIOReturnNotReady;
+       }
 
-    // closeGate is recursive so don't worry if we already hold the lock.
-    IOTimeStampConstant(IODBG_CMDQ(IOCMDQ_ACTION),
-                       (unsigned int) action, (unsigned int) owner);
+       // Try to close the gate if can't get return immediately.
+       if (!wl->tryCloseGate()) {
+               return kIOReturnCannotLock;
+       }
 
-    res = (*(Action) action)(owner, arg0, arg1, arg2, arg3);
-    openGate();
+       // 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;
 
-    return res;
-}
+               if (trace) {
+                       IOTimeStampStartConstant(IODBG_CMDQ(IOCMDQ_ACTION),
+                           VM_KERNEL_ADDRHIDE(inAction), VM_KERNEL_ADDRHIDE(owner));
+               }
 
-IOReturn IOCommandGate::attemptAction(Action inAction,
-                                      void *arg0 = 0, void *arg1 = 0,
-                                      void *arg2 = 0, void *arg3 = 0)
-{
-    IOReturn res;
+               IOStatisticsActionCall();
 
-    if (!enabled)
-        return kIOReturnNotPermitted;
+               res = (*inAction)(owner, arg0, arg1, arg2, arg3);
 
-    if (!inAction)
-        return kIOReturnBadArgument;
+               if (trace) {
+                       IOTimeStampEndConstant(IODBG_CMDQ(IOCMDQ_ACTION),
+                           VM_KERNEL_ADDRHIDE(inAction), VM_KERNEL_ADDRHIDE(owner));
+               }
+       }
 
-    // Try to close the gate if can't get return immediately.
-    if (!tryCloseGate())
-        return kIOReturnCannotLock;
+       wl->openGate();
 
-    IOTimeStampConstant(IODBG_CMDQ(IOCMDQ_ACTION),
-                       (unsigned int) inAction, (unsigned int) owner);
+       return res;
+}
 
-    res = (*inAction)(owner, arg0, arg1, arg2, arg3);
-    openGate();
+IOReturn
+IOCommandGate::commandSleep(void *event, UInt32 interruptible)
+{
+       if (!workLoop->inGate()) {
+               /* The equivalent of 'msleep' while not holding the mutex is invalid */
+               panic("invalid commandSleep while not holding the gate");
+       }
 
-    return res;
+       return sleepGate(event, interruptible);
 }
 
-IOReturn IOCommandGate::commandSleep(void *event, UInt32 interruptible)
+IOReturn
+IOCommandGate::commandSleep(void *event, AbsoluteTime deadline, UInt32 interruptible)
 {
-    IOReturn ret;
-
-    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, deadline, interruptible);
 }
 
-void IOCommandGate::commandWakeup(void *event, bool oneThread)
+void
+IOCommandGate::commandWakeup(void *event, bool oneThread)
 {
-    wakeupGate(event, oneThread);
+       wakeupGate(event, oneThread);
 }