X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/5ba3f43ea354af8ad55bea84372a2bc834d8757c..d26ffc64f583ab2d29df48f13518685602bc8832:/iokit/Kernel/IOService.cpp diff --git a/iokit/Kernel/IOService.cpp b/iokit/Kernel/IOService.cpp index 40055c5c9..5d4be71a6 100644 --- a/iokit/Kernel/IOService.cpp +++ b/iokit/Kernel/IOService.cpp @@ -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(¬ifiers); - 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 );