]> git.saurik.com Git - apple/xnu.git/blobdiff - iokit/Kernel/IOService.cpp
xnu-4570.71.2.tar.gz
[apple/xnu.git] / iokit / Kernel / IOService.cpp
index 40055c5c9c5b43ce3963ca5c03838abe79273172..5d4be71a60f65b42fbd9da7efd34bebdf7a02393 100644 (file)
@@ -190,6 +190,7 @@ static IOLock *                     gIOServiceBusyLock;
 bool                           gCPUsRunning;
 
 static thread_t                        gIOTerminateThread;
+static thread_t                        gIOTerminateWorkerThread;
 static UInt32                  gIOTerminateWork;
 static OSArray *               gIOTerminatePhase2List;
 static OSArray *               gIOStopList;
@@ -448,6 +449,11 @@ void IOService::initialize( void )
     gIOStopProviderList    = OSArray::withCapacity( 16 );
     gIOFinalizeList       = OSArray::withCapacity( 16 );
     assert( gIOTerminatePhase2List && gIOStopList && gIOStopProviderList && gIOFinalizeList );
+
+    // worker thread that is responsible for terminating / cleaning up threads
+    kernel_thread_start(&terminateThread, NULL, &gIOTerminateWorkerThread);
+    assert(gIOTerminateWorkerThread);
+    thread_set_thread_name(gIOTerminateWorkerThread, "IOServiceTerminateThread");
 }
 
 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
@@ -460,6 +466,11 @@ const char *getCpuDelayBusStallHolderName(void) {
     return sCPULatencyHolderName[kCpuDelayBusStall];
 }
 
+const char *getCpuInterruptDelayHolderName(void);
+const char *getCpuInterruptDelayHolderName(void) {
+    return sCPULatencyHolderName[kCpuDelayInterrupt];
+}
+
 }
 #endif
 
@@ -2221,13 +2232,27 @@ void IOService::setTerminateDefer(IOService * provider, bool defer)
     }
 }
 
+// Must call this while holding gJobsLock
+void IOService::waitToBecomeTerminateThread(void)
+{
+    IOLockAssert(gJobsLock, kIOLockAssertOwned);
+    bool wait;
+    do {
+        wait = (gIOTerminateThread != THREAD_NULL);
+        if (wait) {
+            IOLockSleep(gJobsLock, &gIOTerminateThread, THREAD_UNINT);
+        }
+    } while (wait);
+    gIOTerminateThread = current_thread();
+}
+
 // call with lockForArbitration
 void IOService::scheduleTerminatePhase2( IOOptionBits options )
 {
     AbsoluteTime       deadline;
     uint64_t           regID1;
     int                        waitResult = THREAD_AWAKENED;
-    bool               wait, haveDeadline = false;
+    bool               wait = false, haveDeadline = false;
 
     if (!(__state[0] & kIOServiceInactiveState)) return;
 
@@ -2249,15 +2274,7 @@ void IOService::scheduleTerminatePhase2( IOOptionBits options )
     if( (options & kIOServiceSynchronous)
         && (current_thread() != gIOTerminateThread)) {
 
-        do {
-            wait = (gIOTerminateThread != 0);
-            if( wait) {
-                // wait to become the terminate thread
-                IOLockSleep( gJobsLock, &gIOTerminateThread, THREAD_UNINT);
-            }
-        } while( wait );
-
-        gIOTerminateThread = current_thread();
+        waitToBecomeTerminateThread();
         gIOTerminatePhase2List->setObject( this );
         gIOTerminateWork++;
 
@@ -2266,16 +2283,20 @@ void IOService::scheduleTerminatePhase2( IOOptionBits options )
                terminateWorker( options );
             wait = (0 != (__state[1] & kIOServiceBusyStateMask));
             if( wait) {
-                // wait for the victim to go non-busy
+                /* wait for the victim to go non-busy */
                 if( !haveDeadline) {
                     clock_interval_to_deadline( 15, kSecondScale, &deadline );
                     haveDeadline = true;
                 }
+                /* let others do work while we wait */
+                gIOTerminateThread = 0;
+                IOLockWakeup( gJobsLock, (event_t) &gIOTerminateThread, /* one-thread */ false);
                 waitResult = IOLockSleepDeadline( gJobsLock, &gIOTerminateWork,
                                                   deadline, THREAD_UNINT );
-                if( waitResult == THREAD_TIMED_OUT) {
-                    IOLog("%s[0x%qx]::terminate(kIOServiceSynchronous) timeout\n", getName(), getRegistryEntryID());
-               }
+                if (__improbable(waitResult == THREAD_TIMED_OUT)) {
+                    panic("%s[0x%qx]::terminate(kIOServiceSynchronous) timeout\n", getName(), getRegistryEntryID());
+                }
+                waitToBecomeTerminateThread();
             }
         } while(gIOTerminateWork || (wait && (waitResult != THREAD_TIMED_OUT)));
 
@@ -2287,11 +2308,9 @@ void IOService::scheduleTerminatePhase2( IOOptionBits options )
 
         gIOTerminatePhase2List->setObject( this );
         if( 0 == gIOTerminateWork++) {
-           if( !gIOTerminateThread)
-               kernel_thread_start(&terminateThread, (void *)(uintptr_t) options, &gIOTerminateThread);
-           else
-               IOLockWakeup(gJobsLock, (event_t) &gIOTerminateWork, /* one-thread */ false );
-       }
+            assert(gIOTerminateWorkerThread);
+            IOLockWakeup(gJobsLock, (event_t)&gIOTerminateWork, /* one-thread */ false );
+        }
     }
 
     IOLockUnlock( gJobsLock );
@@ -2299,18 +2318,23 @@ void IOService::scheduleTerminatePhase2( IOOptionBits options )
     release();
 }
 
+__attribute__((__noreturn__))
 void IOService::terminateThread( void * arg, wait_result_t waitResult )
 {
-    IOLockLock( gJobsLock );
-
-    while (gIOTerminateWork)
-       terminateWorker( (uintptr_t) arg );
+    // IOLockSleep re-acquires the lock on wakeup, so we only need to do this once
+    IOLockLock(gJobsLock);
+    while (true) {
+        if (gIOTerminateThread != gIOTerminateWorkerThread) {
+            waitToBecomeTerminateThread();
+        }
 
-    thread_deallocate(gIOTerminateThread);
-    gIOTerminateThread = 0;
-    IOLockWakeup( gJobsLock, (event_t) &gIOTerminateThread, /* one-thread */ false);
+        while (gIOTerminateWork)
+            terminateWorker( (uintptr_t)arg );
 
-    IOLockUnlock( gJobsLock );
+        gIOTerminateThread = 0;
+        IOLockWakeup( gJobsLock, (event_t) &gIOTerminateThread, /* one-thread */ false);
+        IOLockSleep(gJobsLock, &gIOTerminateWork, THREAD_UNINT);
+    }
 }
 
 void IOService::scheduleStop( IOService * provider )
@@ -2331,10 +2355,8 @@ void IOService::scheduleStop( IOService * provider )
     gIOStopProviderList->tailQ( provider );
 
     if( 0 == gIOTerminateWork++) {
-        if( !gIOTerminateThread)
-           kernel_thread_start(&terminateThread, (void *) 0, &gIOTerminateThread);
-        else
-            IOLockWakeup(gJobsLock, (event_t) &gIOTerminateWork, /* one-thread */ false );
+        assert(gIOTerminateWorkerThread);
+        IOLockWakeup(gJobsLock, (event_t)&gIOTerminateWork, /* one-thread */ false );
     }
 
     IOLockUnlock( gJobsLock );
@@ -2355,12 +2377,9 @@ void IOService::scheduleFinalize(bool now)
     {
        IOLockLock( gJobsLock );
        gIOFinalizeList->tailQ(this);
-       if( 0 == gIOTerminateWork++)
-       {
-           if( !gIOTerminateThread)
-               kernel_thread_start(&terminateThread, (void *) 0, &gIOTerminateThread);
-           else
-               IOLockWakeup(gJobsLock, (event_t) &gIOTerminateWork, /* one-thread */ false );
+       if( 0 == gIOTerminateWork++) {
+           assert(gIOTerminateWorkerThread);
+           IOLockWakeup(gJobsLock, (event_t)&gIOTerminateWork, /* one-thread */ false );
        }
        IOLockUnlock( gJobsLock );
     }
@@ -2662,15 +2681,9 @@ void IOService::terminateWorker( IOOptionBits options )
                    notifiers = victim->copyNotifiers(gIOWillTerminateNotification, 0, 0xffffffff);
                    victim->invokeNotifiers(&notifiers);
 
-                    if( 0 == victim->getClient()) {
-
-                        // no clients - will go to finalize
-                       victim->scheduleFinalize(false);
-
-                    } else {
-                        _workLoopAction( (IOWorkLoop::Action) &actionWillTerminate,
+                    _workLoopAction( (IOWorkLoop::Action) &actionWillTerminate,
                                             victim, (void *)(uintptr_t) options, (void *)(uintptr_t) doPhase2List );
-                    }
+
                     didPhase2List->headQ( victim );
                 }
                 victim->release();
@@ -2682,9 +2695,10 @@ void IOService::terminateWorker( IOOptionBits options )
             }
         
             while( (victim = (IOService *) didPhase2List->getObject(0)) ) {
-    
-                if( victim->lockForArbitration( true )) {
+               bool scheduleFinalize = false;
+               if( victim->lockForArbitration( true )) {
                     victim->__state[1] |= kIOServiceTermPhase3State;
+                    scheduleFinalize = (0 == victim->getClient());
                     victim->unlockForArbitration();
                 }
                 _workLoopAction( (IOWorkLoop::Action) &actionDidTerminate,
@@ -2693,6 +2707,8 @@ void IOService::terminateWorker( IOOptionBits options )
                    _workLoopAction( (IOWorkLoop::Action) &actionDidStop,
                                        victim, (void *)(uintptr_t) options, NULL );
                }
+                // no clients - will go to finalize
+                if (scheduleFinalize) victim->scheduleFinalize(false);
                 didPhase2List->removeObject(0);
             }
             IOLockLock( gJobsLock );
@@ -2703,10 +2719,17 @@ void IOService::terminateWorker( IOOptionBits options )
             doPhase3 = false;
             // finalize leaves
             while( (victim = (IOService *) gIOFinalizeList->getObject(0))) {
-    
+                bool sendFinal = false;
                 IOLockUnlock( gJobsLock );
-                _workLoopAction( (IOWorkLoop::Action) &actionFinalize,
+                if (victim->lockForArbitration(true)) {
+                    sendFinal = (0 == (victim->__state[1] & kIOServiceFinalized));
+                    if (sendFinal) victim->__state[1] |= kIOServiceFinalized;
+                    victim->unlockForArbitration();
+                }
+                if (sendFinal) {
+                    _workLoopAction( (IOWorkLoop::Action) &actionFinalize,
                                     victim, (void *)(uintptr_t) options );
+                }
                 IOLockLock( gJobsLock );
                 // hold off free
                 freeList->setObject( victim );
@@ -2735,22 +2758,26 @@ void IOService::terminateWorker( IOOptionBits options )
 
                 } else {
                     // a terminated client is not ready for stop if it has clients, skip it
-                    if( (kIOServiceInactiveState & client->__state[0]) && client->getClient()) {
-                        TLOG("%s[0x%qx]::defer stop(%s[0x%qx])\n", 
-                               client->getName(), regID2, 
-                               client->getClient()->getName(), client->getClient()->getRegistryEntryID());
-                       IOServiceTrace(
-                           IOSERVICE_TERMINATE_STOP_DEFER,
-                           (uintptr_t) regID1, 
-                           (uintptr_t) (regID1 >> 32),
-                           (uintptr_t) regID2, 
-                           (uintptr_t) (regID2 >> 32));
-
-                        idx++;
-                        continue;
-                    }
-        
+                    bool deferStop = (0 != (kIOServiceInactiveState & client->__state[0]));
                     IOLockUnlock( gJobsLock );
+                    if (deferStop && client->lockForArbitration(true)) {
+                        deferStop = (0 == (client->__state[1] & kIOServiceFinalized));
+                        //deferStop = (!deferStop && (0 != client->getClient()));
+                        //deferStop = (0 != client->getClient());
+                        client->unlockForArbitration();
+                        if (deferStop) {
+                            TLOG("%s[0x%qx]::defer stop()\n", client->getName(), regID2);
+                            IOServiceTrace(IOSERVICE_TERMINATE_STOP_DEFER,
+                               (uintptr_t) regID1,
+                               (uintptr_t) (regID1 >> 32),
+                               (uintptr_t) regID2,
+                               (uintptr_t) (regID2 >> 32));
+
+                            idx++;
+                            IOLockLock( gJobsLock );
+                            continue;
+                        }
+                    }
                     _workLoopAction( (IOWorkLoop::Action) &actionStop,
                                      provider, (void *) client );
                     IOLockLock( gJobsLock );
@@ -5086,6 +5113,7 @@ bool IOResources::matchPropertyTable( OSDictionary * table )
     OSString *         str;
     OSSet *            set;
     OSIterator *       iter;
+    OSObject *          obj;
     OSArray *           keys;
     bool               ok = true;
 
@@ -5106,14 +5134,15 @@ bool IOResources::matchPropertyTable( OSDictionary * table )
     }
     else if ((prop = table->getObject(gIOResourceMatchedKey)))
     {
-        keys = (OSArray *) copyProperty(gIOResourceMatchedKey);
+        obj = copyProperty(gIOResourceMatchedKey);
+        keys = OSDynamicCast(OSArray, obj);
         ok = false;
         if (keys)
         {
             // assuming OSSymbol
             ok = ((-1U) != keys->getNextIndexOfObject(prop, 0));
-            keys->release();
         }
+        OSSafeReleaseNULL(obj);
     }
 
     return( ok );