20#include "pedigree/kernel/ActivityDiagnostics.h"
21#include "pedigree/kernel/Atomic.h"
22#include "pedigree/kernel/LockGuard.h"
23#include "pedigree/kernel/Log.h"
24#include "pedigree/kernel/Metrics.h"
25#include "pedigree/kernel/Spinlock.h"
26#include "pedigree/kernel/Subsystem.h"
27#include "pedigree/kernel/debugger/commands/LocksCommand.h"
28#include "pedigree/kernel/machine/Machine.h"
29#include "pedigree/kernel/machine/SchedulerTimer.h"
30#include "pedigree/kernel/machine/Timer.h"
31#include "pedigree/kernel/machine/Trace.h"
32#include "pedigree/kernel/panic.h"
33#include "pedigree/kernel/process/Event.h"
34#include "pedigree/kernel/process/PerProcessorScheduler.h"
35#include "pedigree/kernel/process/Process.h"
36#include "pedigree/kernel/process/RoundRobin.h"
37#include "pedigree/kernel/process/Scheduler.h"
38#include "pedigree/kernel/process/SchedulingAlgorithm.h"
39#include "pedigree/kernel/process/TerminationDeferral.h"
40#include "pedigree/kernel/process/Thread.h"
41#include "pedigree/kernel/process/eventNumbers.h"
42#include "pedigree/kernel/processor/PhysicalMemoryManager.h"
43#include "pedigree/kernel/processor/Processor.h"
44#include "pedigree/kernel/processor/ProcessorInformation.h"
45#include "pedigree/kernel/processor/VirtualAddressSpace.h"
46#include "pedigree/kernel/processor/state.h"
47#include "pedigree/kernel/time/Time.h"
48#include "pedigree/kernel/utilities/utility.h"
50#include "pedigree/kernel/processor/hosted/Processor.h"
52#if X86_COMMON && MULTIPROCESSOR
53#include <machine/mach_pc/LocalApic.h>
54#include <machine/mach_pc/Pc.h>
56#if PEDIGREE_HOSTED_FUNCTION_PROFILE
57#include "pedigree/kernel/processor/hosted/FunctionProfile.h"
60static constexpr bool VerboseScheduler =
false;
70 : m_pSchedulingAlgorithm(0),
71 m_NewThreadDataLock(),
72 m_NewThreadDataCondition(),
74 m_DelayedNewThreadData(),
75 m_NewThreadAdmissionOpen(false),
76 m_StopNewThreadWorker(false),
78 m_TimeAccountingState(),
79 m_DeferredThreadReapStub(),
80 m_DeferredThreadReaps(m_DeferredThreadReapStub),
81 m_nDeferredThreadReaps(0),
82 m_DeferredThreadReapPublicationState(DeferredReapPublicationClosed),
83 m_StopTimeAccountingWorker(0),
84 m_TimeAccountingWorker(),
85 m_TimeAccountingWorkerWaiters(),
86 m_TimeAccountingWorkerWake(),
88 m_ReschedulePending(0),
89 m_RemotePromptPending(0),
92#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
93 m_nDeferredThreadReapCompletions(0),
98PerProcessorScheduler::~PerProcessorScheduler() {
101 stopNewThreadWorker();
105 panic(
"No scheduler timer present.");
108 FATAL(
"Per-processor scheduler lost timer-handler ownership.");
113 stopTimeAccountingWorker();
116void PerProcessorScheduler::startTimeAccountingWorker(
Process* pParent) {
117 if (m_TimeAccountingWorker) {
118 FATAL(
"Per-processor time accounting worker started twice.");
121 m_StopTimeAccountingWorker = 0;
122 Thread*
worker =
new Thread(pParent, timeAccountingWorkerEntry,
this,
nullptr,
false,
true,
true);
123 worker->setName(
"deferred process time accounting");
125 m_TimeAccountingWorker.adopt(
worker);
127 FATAL(
"Time accounting worker could not be started.");
130 while (!m_DeferredThreadReapPublicationState.compareAndSwap(DeferredReapPublicationClosed, 0)) {
135 m_AffinityAdmissionOpen =
true;
139void PerProcessorScheduler::stopTimeAccountingWorker() {
140 if (!m_TimeAccountingWorker) {
145 m_AffinityAdmissionOpen =
false;
151 m_DeferredThreadReapPublicationState |= DeferredReapPublicationClosed;
152 while (m_DeferredThreadReapPublicationState.value() & DeferredReapPublicationCountMask) {
156 m_StopTimeAccountingWorker = 1;
159 m_TimeAccountingWorker.
join();
161 if (m_nDeferredThreadReaps.value() || m_AffinityRequests.value()) {
162 FATAL(
"Deferred Thread reap worker stopped with pending targets.");
166int PerProcessorScheduler::timeAccountingWorkerEntry(
void* instance) {
170int PerProcessorScheduler::runTimeAccountingWorker() {
173 const bool stopping = m_StopTimeAccountingWorker.value() != 0;
174 const bool pending = m_TimeAccountingState.ready() || m_nDeferredThreadReaps.value() ||
175 m_AffinityRequests.value();
176 if (stopping && m_TimeAccountingState.caughtUp() && !m_nDeferredThreadReaps.value() &&
177 !m_AffinityRequests.value()) {
181 auto guard = m_TimeAccountingWorkerWaiters.acquire();
182 const bool stillPending = m_TimeAccountingState.ready() || m_nDeferredThreadReaps.value() ||
183 m_AffinityRequests.value() || m_StopTimeAccountingWorker.value();
185 const WaitQueue::WakeReason reason =
186 guard.wait(
WaitQueue::Channel(), Thread::CondWait,
reinterpret_cast<uintptr_t
>(
this));
192 const size_t target = m_TimeAccountingState.
beginBatch();
195 drainDeferredThreadReaps();
196 drainAffinityRequests();
208void PerProcessorScheduler::publishDeferredThreadReap(
Thread* thread) {
209 const size_t admission = (m_DeferredThreadReapPublicationState += 1);
210 if (admission & DeferredReapPublicationClosed) {
211 m_DeferredThreadReapPublicationState -= 1;
212 FATAL_NOLOCK(
"Deferred Thread reap publication reached a stopped worker.");
216 if (node.thread != thread) {
217 FATAL_NOLOCK(
"Deferred Thread reap node has invalid ownership.");
222 m_nDeferredThreadReaps += 1;
223 m_DeferredThreadReaps.
push(node);
225 m_DeferredThreadReapPublicationState -= 1;
228bool PerProcessorScheduler::drainDeferredThreadReaps() {
230 IntrusiveMpscQueue<DeferredThreadReapNode, &DeferredThreadReapNode::next>::PopResult;
234 const PopResult result = m_DeferredThreadReaps.
pop(node);
235 if (result == PopResult::Empty) {
238 if (result == PopResult::Transient) {
241 if (!node || !node->thread) {
242 FATAL(
"Deferred Thread reap queue returned an invalid target.");
245 Thread* thread = node->thread;
249 FATAL(
"Deferred Thread reap worker is not at an IRQ-enabled WaitableThread boundary.");
254 m_nDeferredThreadReaps -= 1;
255#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
256 m_nDeferredThreadReapCompletions += 1;
268 bool useSyscallState;
271void PerProcessorScheduler::startNewThreadWorker(
Process* pParent) {
273 if (m_NewThreadWorker || m_NewThreadData.
count() || m_DelayedNewThreadData.
count()) {
275 FATAL(
"Per-processor thread add worker started with live state.");
277 m_StopNewThreadWorker =
false;
278 m_NewThreadAdmissionOpen =
true;
282 new Thread(pParent, processorAddThread,
reinterpret_cast<void*
>(
this), 0,
false,
true);
283 pAddThread->setName(
"PerProcessorScheduler thread add worker");
284 m_NewThreadWorker.adopt(pAddThread);
287void PerProcessorScheduler::stopNewThreadWorker() {
289 m_NewThreadAdmissionOpen =
false;
290 m_StopNewThreadWorker =
true;
291 while (m_DelayedNewThreadData.
count()) {
294 const bool pending = m_NewThreadData.
count();
297 if (!m_NewThreadWorker) {
299 FATAL(
"Per-processor thread add queue has no worker.");
305 m_NewThreadWorker.
join();
308 const bool drained = !m_NewThreadData.
count() && !m_DelayedNewThreadData.
count();
311 FATAL(
"Per-processor thread add worker stopped before draining.");
315int PerProcessorScheduler::processorAddThread(
void* instance) {
318 pInstance->m_NewThreadDataLock.
acquire();
319 while (!pInstance->m_NewThreadData.
count()) {
320 if (pInstance->m_StopNewThreadWorker) {
321 pInstance->m_NewThreadDataLock.
release();
325 pInstance->m_NewThreadDataCondition.
waitForCompletion(pInstance->m_NewThreadDataLock);
328 void* p = pInstance->m_NewThreadData.
popFront();
333 pInstance->m_NewThreadDataLock.
release();
334 FATAL(
"instance " << instance <<
" does not match current scheduler in processorAddThread!");
337 Thread* pThread = pData->pThread;
345 const bool runnable =
347 if (retireBeforeStart) {
349 pInstance->m_NewThreadDataLock.
release();
358 deleteThread(pThread);
363 if (pThread->
m_Status != Thread::Created) {
365 pInstance->m_NewThreadDataLock.
release();
367 "Per-processor add worker cannot park an already "
368 "scheduled thread.");
373 const bool stopping = pInstance->m_StopNewThreadWorker;
375 pInstance->m_DelayedNewThreadData.
pushBack(p);
378 pInstance->m_NewThreadDataLock.
release();
390 deleteThread(pThread);
394 pInstance->m_NewThreadDataLock.
release();
396 if (pData->useSyscallState) {
397 pInstance->
addThread(pThread, pData->state);
399 pInstance->
addThread(pThread, pData->pStartFunction, pData->pParam, pData->bUsermode,
413 if (information && &information->getScheduler() ==
this) {
415 m_PhysicalCpu = information->processorId();
421 if (!pThread->m_Placement.migratable) {
423 pThread->m_Placement.allowed.set(m_LogicalCpu);
425 pThread->m_HasSchedulerContext =
true;
435 startNewThreadWorker(pThread->
getParent());
436 startTimeAccountingWorker(pThread->
getParent());
442 panic(
"No scheduler timer present.");
445 FATAL(
"Per-processor scheduler timer handler is already owned.");
447 m_NominalQuantumNs = pTimer->nominalQuantumNs();
449 if (m_OneShotTimer) {
450 if (m_LogicalCpu == 0) {
451 m_NextLoadSampleDeadline = Time::getTicksFast() + LoadAverage::PeriodNanoseconds;
453 updateOneShotTimer();
457void PerProcessorScheduler::programOneShotTimer() {
458 if (!m_OneShotTimer) {
461 uint64_t deadline = m_QuantumDeadline;
462 if (m_NextLoadSampleDeadline && (!deadline || m_NextLoadSampleDeadline < deadline)) {
463 deadline = m_NextLoadSampleDeadline;
465 const uint64_t clockDeadline = m_ClockDeadline.value();
466 if (clockDeadline && (!deadline || clockDeadline < deadline)) {
467 deadline = clockDeadline;
471 if (!
timer->armDeadline(deadline)) {
472 FATAL_NOLOCK(
"Failed to arm the local scheduler deadline.");
480 if (!m_OneShotTimer) {
483 uint64_t previous = m_ClockDeadline.value();
484 while (!m_ClockDeadline.compareAndSwap(previous, deadline)) {
485 previous = m_ClockDeadline.value();
487 if ((!previous && !deadline) || (previous && (!deadline || deadline >= previous))) {
494 programOneShotTimer();
498#if X86_COMMON && MULTIPROCESSOR
501 if (!Pc::instance().getLocalApic().interProcessorInterrupt(
502 information->localApicId(), IPI_RESCHEDULE_VECTOR, LocalApic::deliveryModeFixed,
true,
504 FATAL_NOLOCK(
"Remote clock deadline rearm failed.");
509void PerProcessorScheduler::updateOneShotTimer() {
510 if (!m_OneShotTimer) {
517 const uint64_t now = Time::getTicksFast();
519 now > ~uint64_t(0) - m_NominalQuantumNs ? ~uint64_t(0) : now + m_NominalQuantumNs;
521 m_QuantumDeadline = 0;
523 programOneShotTimer();
527void PerProcessorScheduler::armLocalQuantumIfNeeded() {
534 if (current && current != m_pIdleThread && !m_QuantumDeadline) {
535 const uint64_t now = Time::getTicksFast();
537 now > ~uint64_t(0) - m_NominalQuantumNs ? ~uint64_t(0) : now + m_NominalQuantumNs;
538 programOneShotTimer();
557void PerProcessorScheduler::scheduleWithInterruptState(
Thread::Status nextStatus,
558 bool dispatchEvents,
bool bWasInterrupts) {
559#if PEDIGREE_HOSTED_FUNCTION_PROFILE
560 hostedFunctionProfileInvalidate(HostedProfileInvalidation::Schedule);
563 ActivityDiagnostics::recordScheduleCall();
564 Metrics::increment(Metrics::Counter::Schedule);
567 if (!pCurrentThread) {
568 FATAL(
"Missing a current thread in PerProcessorScheduler::schedule!");
571 bool canServiceWorkerWakeups = bWasInterrupts;
573 canServiceWorkerWakeups &= !pCurrentThread->getHostedSignalDepth();
575 if (canServiceWorkerWakeups && m_IrqWorkDoorbell.compareAndSwap(1, 0)) {
576 serviceWorkerWakeups();
582 bool dispatchEvent =
false;
583 if (nextStatus == Thread::Sleeping) {
584 if (!pCurrentThread->hasActiveWaitUnlocked()) {
585 if (!pCurrentThread->consumeTerminalWaitCancelledBeforeBlockUnlocked()) {
586 FATAL(
"Scheduler refused a sleep without an active WaitQueue.");
600 dispatchEvent = pCurrentThread->hasDeliverableEventsUnlocked();
605 if (!pCurrentThread->activeWaitPendingUnlocked() || dispatchEvent) {
614 Thread* pNextThread = selectNext(pCurrentThread);
615 if (pNextThread == 0) {
616 ActivityDiagnostics::recordSchedulerIdleFallback(
617 pCurrentThread->
m_Status == Thread::Ready,
618 __atomic_load_n(&pCurrentThread->m_ReadyPublicationPending, __ATOMIC_ACQUIRE));
621 if (nextStatus == Thread::Ready && pCurrentThread != m_pIdleThread &&
622 !pCurrentThread->m_ReadyPublicationPending) {
623 pNextThread = pCurrentThread;
624 }
else if (m_pIdleThread == 0) {
633 pNextThread = m_pIdleThread;
634 if (pNextThread != pCurrentThread)
641 if (pNextThread == pCurrentThread) {
642 ActivityDiagnostics::recordSameThreadSelection();
643 Metrics::increment(Metrics::Counter::SameThread);
644 updateOneShotTimer();
645 const bool waitOwnsEventDispatch = pCurrentThread->hasActiveWaitUnlocked();
648 if (dispatchEvents && !waitOwnsEventDispatch) {
655 NOTICE_NOLOCK(
"schedule: " << pCurrentThread <<
" -> " << pNextThread <<
" -- "
656 << pCurrentThread->getName() <<
" -> " << pNextThread->getName());
660 ActivityDiagnostics::recordContextSwitch();
661 Metrics::increment(Metrics::Counter::ContextSwitch);
662 if (pNextThread == m_pIdleThread) {
663 ActivityDiagnostics::recordIdleSelection();
664 Metrics::increment(Metrics::Counter::IdleSelection);
666 if (pCurrentThread != m_pIdleThread)
667 pCurrentThread->setStatusUnlocked(nextStatus);
668 pNextThread->setStatusUnlocked(Thread::Running);
670 updateOneShotTimer();
680 pCurrentThread->
trackTime(CpuTimeMode::Kernel);
693 FATAL(
"Lock checker disallowed this reschedule.");
697 EMIT_IF(SYSTEM_REQUIRES_ATOMIC_CONTEXT_SWITCH) {
700 const bool waitOwnsEventDispatch = pCurrentThread->hasActiveWaitUnlocked();
701#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
702 Processor::notifyHostedContextSwitchStage(
703 ProcessorBase::HostedContextSwitchStage::SchedulerBookkeepingComplete);
704 Processor::notifyHostedContextSwitchStage(
705 ProcessorBase::HostedContextSwitchStage::SchedulerRestoringInterrupts);
708 if (dispatchEvents && !waitOwnsEventDispatch) {
720 const bool waitOwnsEventDispatch = pCurrentThread->hasActiveWaitUnlocked();
724 if (dispatchEvents && !waitOwnsEventDispatch) {
741 checkEventState(userStack, Thread::EventSelection::WithoutExactUserReturn);
749 InterruptState* interruptState,
750 SyscallState* syscallState) {
774 if (selection == Thread::EventSelection::WithoutExactUserReturn) {
779 Event* pEvent = eventDelivery.get();
780 if (!eventDelivery) {
789 FATAL_NOLOCK(
"User-return event interception requires IRQ-enabled entry");
790 Subsystem::UserReturnEventResult intercepted;
794 [](
void* value) {
static_cast<Event::Delivery*
>(value)->reset(); }, &eventDelivery);
796 intercepted = subsystem->userReturnEvent(*pThread, *pEvent, *frame);
799 if (intercepted != Subsystem::UserReturnEventResult::Deliver) {
800 frame->m_Terminal = intercepted == Subsystem::UserReturnEventResult::Terminal;
801 eventDelivery.
reset();
809 if (interruptState || syscallState) {
810 if (!bWasInterrupts) {
811 FATAL_NOLOCK(
"Exact user-return event delivery requires an IRQ-enabled thread boundary.");
818 if (interruptState) {
826 if (result == Event::UserReturnDelivery::NotApplicable) {
831 eventDelivery.
reset();
839 physical_uintptr_t page = 0;
841 bool mappingAvailable =
true;
846 mappingAvailable = va.
isMapped(
reinterpret_cast<void*
>(handlerAddress));
847 if (mappingAvailable) {
848 va.
getMapping(
reinterpret_cast<void*
>(handlerAddress), page, flags);
852 mappingAvailable = va.
isMapped(
reinterpret_cast<void*
>(handlerAddress));
853 if (mappingAvailable) {
854 va.
getMapping(
reinterpret_cast<void*
>(handlerAddress), page, flags);
859 const bool userAddress =
864 ERROR_NOLOCK(
"checkEventState: Handler address "
865 <<
Hex << handlerAddress <<
" does not match its declared "
866 << (userHandler ?
"user" :
"kernel") <<
" privilege.");
871 bool alternateStackCandidate =
false;
872 uintptr_t alternateStackTop = 0;
875 if (alternate.enabled && !alternate.inUse && alternate.base &&
876 alternate.size <= (~
static_cast<uintptr_t
>(0) - alternate.base)) {
877 alternateStackTop = (alternate.base + alternate.size) & ~
static_cast<uintptr_t
>(0xF);
878 alternateStackCandidate =
true;
882 SchedulerState* oldState = pThread->
pushState();
892 if (alternateStackCandidate) {
893 userStack = alternateStackTop;
896 bool usableUserStack =
false;
897 if (userStack >= pageSz) {
898 const uintptr_t stackPage = userStack - pageSz;
901 va.
isMapped(
reinterpret_cast<void*
>(stackPage))) {
902 va.
getMapping(
reinterpret_cast<void*
>(stackPage), page, flags);
907 if (usableUserStack && alternateStackCandidate) {
912 if (!usableUserStack) {
914 if (!stateStack || !va.
isMapped(adjust_pointer(stateStack->getTop(), -pageSz))) {
917 panic(
"checkEventState: no user fallback stack");
918 pThread->setStateUserStack(stateStack);
921 userStack =
reinterpret_cast<uintptr_t
>(stateStack->getTop());
933 void* eventPage =
reinterpret_cast<void*
>(addr + offset);
937 panic(
"checkEventState: Out of memory!");
944 if (!deletableEvent && !userHandler) {
945 eventDelivery.beginDispatch();
947 pEvent->
serialize(
reinterpret_cast<uint8_t*
>(addr));
952 if (deletableEvent) {
953 eventDelivery.
reset();
956 EMIT_IF(!SYSTEM_REQUIRES_ATOMIC_CONTEXT_SWITCH) {
973 callOnStack(
reinterpret_cast<uintptr_t
>(pThread->
getKernelStack()), handlerAddress, addr);
975 void (*fn)(size_t) =
reinterpret_cast<void (*)(
size_t)
>(handlerAddress);
980 eventDelivery.
reset();
984 }
else if (userStack != 0) {
986 eventDelivery.
reset();
988 EMIT_IF(SYSTEM_REQUIRES_ATOMIC_CONTEXT_SWITCH) {
990 handlerAddress, addr);
1011 void* pParam,
bool bUsermode,
void* pStack) {
1015 pData->pThread = pThread;
1016 pData->pStartFunction = pStartFunction;
1017 pData->pParam = pParam;
1018 pData->bUsermode = bUsermode;
1019 pData->pStack = pStack;
1020 pData->useSyscallState =
false;
1024 m_NewThreadDataLock.
acquire();
1025 if (!m_NewThreadAdmissionOpen) {
1026 m_NewThreadDataLock.
release();
1028 panic(
"Thread admitted after its per-processor worker stopped.");
1031 m_NewThreadDataLock.
release();
1033 m_NewThreadDataCondition.
signal();
1053 assert(pThread->m_Placement.allowed.contains(m_LogicalCpu));
1054 pThread->m_HasSchedulerContext =
true;
1056 Metrics::increment(Metrics::Counter::ContextSwitch);
1057 if (pThread == m_pIdleThread) {
1058 Metrics::increment(Metrics::Counter::IdleSelection);
1060 if (pCurrentThread != m_pIdleThread) {
1061 pCurrentThread->setStatusUnlocked(Thread::Ready);
1063 pThread->setStatusUnlocked(Thread::Running);
1065 updateOneShotTimer();
1074 if (pThread->
getLock().interrupts())
1075 bWasInterrupts =
true;
1076 bool bWas = pThread->
getLock().acquired();
1077 pThread->
getLock().unlockForScheduler();
1087 FATAL(
"Lock checker disallowed this reschedule.");
1091 pCurrentThread->
trackTime(CpuTimeMode::Kernel);
1092 pThread->
recordTime(bUsermode ? CpuTimeMode::User : CpuTimeMode::Kernel);
1094 EMIT_IF(SYSTEM_REQUIRES_ATOMIC_CONTEXT_SWITCH) {
1098 reinterpret_cast<uintptr_t
>(pStartFunction),
reinterpret_cast<uintptr_t
>(pStack),
1099 reinterpret_cast<uintptr_t
>(pParam));
1103 reinterpret_cast<uintptr_t
>(pStartFunction),
reinterpret_cast<uintptr_t
>(pStack),
1104 reinterpret_cast<uintptr_t
>(pParam));
1118 reinterpret_cast<uintptr_t
>(pStartFunction),
1119 reinterpret_cast<uintptr_t
>(pStack),
reinterpret_cast<uintptr_t
>(pParam));
1122 reinterpret_cast<uintptr_t
>(pStartFunction),
1123 reinterpret_cast<uintptr_t
>(pStack),
1124 reinterpret_cast<uintptr_t
>(pParam));
1133 pData->pThread = pThread;
1134 pData->pParam =
nullptr;
1135 pData->useSyscallState =
true;
1136 pData->state = state;
1140 m_NewThreadDataLock.
acquire();
1141 if (!m_NewThreadAdmissionOpen) {
1142 m_NewThreadDataLock.
release();
1144 panic(
"Thread admitted after its per-processor worker stopped.");
1147 m_NewThreadDataLock.
release();
1149 m_NewThreadDataCondition.
signal();
1168 assert(pThread->m_Placement.allowed.contains(m_LogicalCpu));
1169 pThread->m_HasSchedulerContext =
true;
1171 Metrics::increment(Metrics::Counter::ContextSwitch);
1172 if (pThread == m_pIdleThread) {
1173 Metrics::increment(Metrics::Counter::IdleSelection);
1176 if (pCurrentThread != m_pIdleThread) {
1177 pCurrentThread->setStatusUnlocked(Thread::Ready);
1179 pThread->setStatusUnlocked(Thread::Running);
1181 updateOneShotTimer();
1190 if (pThread->
getLock().interrupts())
1191 bWasInterrupts =
true;
1192 bool bWas = pThread->
getLock().acquired();
1193 pThread->
getLock().unlockForScheduler();
1201 FATAL(
"Lock checker disallowed this reschedule.");
1207 state.refreshUserTlsBase();
1211 uintptr_t kStack =
reinterpret_cast<uintptr_t
>(pThread->
getKernelStack());
1212 kStack -=
sizeof(SyscallState);
1213 MemoryCopy(
reinterpret_cast<void*
>(kStack),
reinterpret_cast<void*
>(&state),
1214 sizeof(SyscallState));
1217 SyscallState& newState = *
reinterpret_cast<SyscallState*
>(kStack);
1219 pCurrentThread->
trackTime(CpuTimeMode::Kernel);
1224 EMIT_IF(SYSTEM_REQUIRES_ATOMIC_CONTEXT_SWITCH) {
1225 NOTICE(
"restoring (new) syscall state");
1244 FATAL(
"Clean thread exit has no current Thread.");
1249 FATAL(
"Clean thread exit reached a boundary with active deferral scopes.");
1252 for (
size_t level = 0; level < MAX_NESTED_EVENTS; ++level) {
1254 FATAL(
"Clean thread exit reached a boundary with armed cleanup records.");
1257 if (pThread->hasActiveWaitAtAnyLevel()) {
1258 FATAL(
"Clean thread exit reached a boundary with an active WaitQueue record.");
1261 const bool transferToIdle = pThread && pThread->
m_ExitToIdle;
1263 finishCurrentThreadExit(pLock, transferToIdle);
1273 FATAL(
"Stack discard has no current Thread.");
1276 g_StackDiscardCount += 1;
1278 case StackDiscardReason::EmergencyProcessKill:
1279 g_EmergencyProcessKillDiscardCount += 1;
1281 case StackDiscardReason::HostedRegression:
1282 g_HostedRegressionDiscardCount += 1;
1284 case StackDiscardReason::LegacyAbiCall:
1285 g_LegacyAbiDiscardCount += 1;
1295 pThread->retireDeferredScopes(
true);
1299 finishCurrentThreadExit(pLock, transferToIdle);
1303 return g_StackDiscardCount.value();
1308 case StackDiscardReason::EmergencyProcessKill:
1309 return g_EmergencyProcessKillDiscardCount.value();
1310 case StackDiscardReason::HostedRegression:
1311 return g_HostedRegressionDiscardCount.value();
1312 case StackDiscardReason::LegacyAbiCall:
1313 return g_LegacyAbiDiscardCount.value();
1320 if (!pThread || !m_pIdleThread || pThread == m_pIdleThread) {
1321 panic(
"Current thread has no distinct idle shutdown owner!");
1326void PerProcessorScheduler::finishCurrentThreadExit(
Spinlock* pLock,
bool transferToIdle) {
1346 FATAL(
"Lock checker disallowed this reschedule.");
1352 Thread* pNextThread = transferToIdle ? nullptr : owner.selectNext(pThread);
1354 if (transferToIdle && (!owner.m_pIdleThread || owner.m_pIdleThread == pThread)) {
1355 panic(
"Current thread has no distinct idle shutdown owner!");
1358 if (pNextThread == 0 && owner.m_pIdleThread == 0) {
1360 panic(
"Attempting to kill only thread on this processor!");
1361 }
else if (pNextThread == 0) {
1362 pNextThread = owner.m_pIdleThread;
1363 if (pNextThread != pThread)
1367 Metrics::increment(Metrics::Counter::ContextSwitch);
1368 if (pNextThread == owner.m_pIdleThread) {
1369 Metrics::increment(Metrics::Counter::IdleSelection);
1371 pNextThread->setStatusUnlocked(Thread::Running);
1373 owner.updateOneShotTimer();
1381 pThread->
trackTime(CpuTimeMode::Kernel);
1382 pNextThread->
recordTime(CpuTimeMode::Kernel);
1392void PerProcessorScheduler::deleteThread(
Thread* pThread) {
1399 if (!replacement || replacement == pThread) {
1400 FATAL(
"Hosted Thread deletion has no replacement address space");
1418 pThread->
getLock().unlockForScheduler();
1420 bool deleteTarget =
false;
1421 bool completesProcessExit =
false;
1422 bool wakeExitOwner =
false;
1430 FATAL_NOLOCK(
"Thread retirement raced closed Process admission.");
1438 if (wakeExitOwner) {
1440 pProcess->m_ExecWaiters.wakeAll();
1446 FATAL_NOLOCK(
"Thread retirement reached a non-owning scheduler.");
1448 scheduler.publishDeferredThreadReap(pThread);
1451 if (!completesProcessExit) {
1460void PerProcessorScheduler::removeThread(
Thread* pThread) {
1473 pThread->publishReadyNotification();
1476Thread* PerProcessorScheduler::selectNext(
Thread* current) {
1479 if (__atomic_load_n(&m_IdleWakeRequested, __ATOMIC_ACQUIRE)) {
1480 Thread* idle = __atomic_load_n(&m_pIdleThread, __ATOMIC_ACQUIRE);
1482 if (idle == current)
1490 if (candidate->getScheduler() ==
this && candidate->m_Status == Thread::Ready &&
1491 !candidate->m_ReadyPublicationPending)
1493 candidate->m_Lock.release();
1500 if constexpr (PEDIGREE_TIME_ACCOUNTING && PEDIGREE_SAMPLED_TIME_ACCOUNTING) {
1502 if (current && current != m_pIdleThread && delta) {
1506 ActivityDiagnostics::recordSchedulerTimer();
1507 Metrics::increment(Metrics::Counter::Timer);
1508 if (m_OneShotTimer) {
1510 programOneShotTimer();
1513 const uint64_t now = Time::getTicksFast();
1514 if (m_NextLoadSampleDeadline && now >= m_NextLoadSampleDeadline) {
1515 m_NextLoadSampleDeadline = now > ~uint64_t(0) - LoadAverage::PeriodNanoseconds
1517 : now + LoadAverage::PeriodNanoseconds;
1520 if (m_QuantumDeadline && now >= m_QuantumDeadline) {
1521 m_QuantumDeadline = 0;
1522 m_ReschedulePending = 1;
1524 const uint64_t clockDeadline = m_ClockDeadline.value();
1525 if (clockDeadline && now >= clockDeadline && m_ClockDeadline.compareAndSwap(clockDeadline, 0)) {
1528 programOneShotTimer();
1537 m_ReschedulePending = 1;
1538 }
else if (++m_SchedulerTickCounter >= PEDIGREE_SCHEDULER_TICK_DIVISOR) {
1539 m_SchedulerTickCounter = 0;
1540 m_ReschedulePending = 1;
1544void PerProcessorScheduler::threadStatusChanged(
Thread* pThread) {
1545 bool wakeWorker =
false;
1546 bool queueLocked =
false;
1549 if (pThread->
m_Status == Thread::Created) {
1553 m_NewThreadDataLock.
acquire();
1556 if (pThread->
m_Status == Thread::Created) {
1558 it != m_DelayedNewThreadData.
end();) {
1560 if (pData->pThread == pThread) {
1562 it = m_DelayedNewThreadData.
erase(it);
1574 owner->m_pSchedulingAlgorithm->threadStatusChanged(pThread);
1575 if (pThread->
m_Status == Thread::Ready && !pThread->m_ReadyPublicationPending) {
1580 m_NewThreadDataLock.
release();
1584 m_NewThreadDataCondition.
signal();
1587 readyOwner->prompt(
true);
1592 m_IrqWorkDoorbell = 1;
1599 FATAL(
"Scheduler worker wake was registered twice.");
1602 worker.m_pWaiters = &waiters;
1603 worker.m_pNext = m_pWorkerWakeHead;
1604 m_pWorkerWakeHead = &
worker;
1610 while (*link && *link != &
worker) {
1611 link = &(*link)->m_pNext;
1614 FATAL(
"Scheduler worker wake was not registered.");
1617 worker.m_pNext =
nullptr;
1618 worker.m_pWaiters =
nullptr;
1627 m_IrqWorkDoorbell = 1;
1631void PerProcessorScheduler::serviceWorkerWakeups() {
1635 if (!
worker->m_Pending.value() || !
worker->m_pWaiters) {
1639 if (
worker->m_pWaiters->wakeOne()) {
1640 Metrics::increment(Metrics::Counter::WorkerWake);
1641 worker->m_Pending.compareAndSwap(1, 0);
1650 m_IrqWorkDoorbell = 1;
1655 m_TimeAccountingState.
publish();
1660 Metrics::increment(Metrics::Counter::RescheduleService);
1667 if (m_IrqWorkDoorbell.compareAndSwap(1, 0)) {
1668 serviceWorkerWakeups();
1669 m_ReschedulePending.compareAndSwap(1, 0);
1675 Metrics::increment(Metrics::Counter::RescheduleService);
1680 m_RemotePromptPending = 0;
1681 bool pending = m_ReschedulePending.compareAndSwap(1, 0);
1682 if (m_IrqWorkDoorbell.compareAndSwap(1, 0)) {
1683 serviceWorkerWakeups();
1685 m_ReschedulePending.compareAndSwap(1, 0);
1696 "Return-to-user stop work requires an IRQ-enabled thread "
1702 FATAL_NOLOCK(
"Return-to-user stop work escaped Kernel accounting mode");
1714 bool dispatchKernelEvent =
false;
1715 Thread::EventSelection selection = Thread::EventSelection::StoppedProcessKernel;
1716 WaitQueue::WakeReason reason = WaitQueue::WakeReason::Spurious;
1728 if (state == Process::Active) {
1729 selection = Thread::EventSelection::KernelDeliverable;
1730 dispatchKernelEvent = mode == ProcessStopGateMode::DirectUserTransition &&
1731 current->hasDeliverableEventsUnlocked(selection);
1732 if (!dispatchKernelEvent) {
1737 FATAL_NOLOCK(
"A live Thread reached userspace after its Process terminated");
1742 if (state == Process::Suspended) {
1743 selection = Thread::EventSelection::StoppedProcessKernel;
1744 dispatchKernelEvent = current->hasDeliverableEventsUnlocked(selection);
1748 if (!dispatchKernelEvent) {
1752 reason = guard.waitWithoutEventDispatch(
1754 Thread::ProcessWait,
reinterpret_cast<uintptr_t
>(__builtin_return_address(0)));
1758 if (dispatchKernelEvent) {
1764 if (reason == WaitQueue::WakeReason::Terminating ||
1765 reason == WaitQueue::WakeReason::Unwinding) {
1772 UserReturnFrame::Origin origin,
1773 bool diagnosticSample) {
1774 const uint64_t workStart = diagnosticSample ? ActivityDiagnostics::timestamp() : 0;
1775 auto finishWork = [diagnosticSample, workStart](
bool terminal) {
1776 if (diagnosticSample) {
1777 ActivityDiagnostics::recordUserReturnStage(
1778 ActivityDiagnostics::UserReturnStage::InterruptWork,
1779 ActivityDiagnostics::timestamp() - workStart);
1785 return finishWork(
false);
1789 state.setFlags(state.getFlags() | 0x202);
1792#if PEDIGREE_FAST_USER_RETURN
1793 if ((origin == UserReturnFrame::Origin::Syscall ||
1794 origin == UserReturnFrame::Origin::Interrupt) &&
1795 owner->canSkipUserReturnWork()) {
1796 return finishWork(
false);
1802 Subsystem* subsystem = owner->getParent() ? owner->getParent()->getSubsystem() :
nullptr;
1804 const uint64_t checkpointStart = diagnosticSample ? ActivityDiagnostics::timestamp() : 0;
1805 const bool terminal =
1806 subsystem->userReturnCheckpoint(*owner, frame) == Subsystem::UserReturnResult::Terminal;
1807 if (diagnosticSample) {
1808 ActivityDiagnostics::recordUserReturnStage(
1809 ActivityDiagnostics::UserReturnStage::Checkpoint,
1810 ActivityDiagnostics::timestamp() - checkpointStart);
1813 return finishWork(
true);
1819 uint64_t stageStart = diagnosticSample ? ActivityDiagnostics::timestamp() : 0;
1821 if (diagnosticSample) {
1822 ActivityDiagnostics::recordUserReturnStage(ActivityDiagnostics::UserReturnStage::ProcessStop,
1823 ActivityDiagnostics::timestamp() - stageStart);
1826 return finishWork(
true);
1833 return finishWork(
true);
1836#if PEDIGREE_FAST_USER_RETURN
1838 if ((origin == UserReturnFrame::Origin::Syscall ||
1839 origin == UserReturnFrame::Origin::Interrupt) &&
1840 !frame.m_Terminal && owner->canSkipUserReturnWork()) {
1841 return finishWork(
false);
1845 stageStart = diagnosticSample ? ActivityDiagnostics::timestamp() : 0;
1847 if (diagnosticSample) {
1848 ActivityDiagnostics::recordUserReturnStage(ActivityDiagnostics::UserReturnStage::ProcessStop,
1849 ActivityDiagnostics::timestamp() - stageStart);
1852 return finishWork(
true);
1854 stageStart = diagnosticSample ? ActivityDiagnostics::timestamp() : 0;
1856 state.getStackPointer(), Thread::EventSelection::AnyDeliverable, &state,
nullptr);
1857 if (diagnosticSample) {
1858 ActivityDiagnostics::recordUserReturnStage(ActivityDiagnostics::UserReturnStage::Event,
1859 ActivityDiagnostics::timestamp() - stageStart);
1862 stageStart = diagnosticSample ? ActivityDiagnostics::timestamp() : 0;
1865 if (diagnosticSample) {
1866 ActivityDiagnostics::recordUserReturnStage(ActivityDiagnostics::UserReturnStage::ProcessStop,
1867 ActivityDiagnostics::timestamp() - stageStart);
1870 owner->clearUserReturnWorkIfIdle();
1871 return finishWork(terminal);
1875 UserReturnFrame::Origin origin,
1876 bool diagnosticSample) {
1877 const uint64_t workStart = diagnosticSample ? ActivityDiagnostics::timestamp() : 0;
1878 auto finishWork = [diagnosticSample, workStart](
bool terminal) {
1879 if (diagnosticSample) {
1880 ActivityDiagnostics::recordUserReturnStage(ActivityDiagnostics::UserReturnStage::SyscallWork,
1881 ActivityDiagnostics::timestamp() - workStart);
1887 return finishWork(
false);
1891 state.setFlags(state.getFlags() | 0x202);
1894#if PEDIGREE_FAST_USER_RETURN
1895 if (origin == UserReturnFrame::Origin::Syscall && owner->canSkipUserReturnWork())
1896 return finishWork(
false);
1901 Subsystem* subsystem = owner->getParent() ? owner->getParent()->getSubsystem() :
nullptr;
1903 const uint64_t checkpointStart = diagnosticSample ? ActivityDiagnostics::timestamp() : 0;
1904 const bool terminal =
1905 subsystem->userReturnCheckpoint(*owner, frame) == Subsystem::UserReturnResult::Terminal;
1906 if (diagnosticSample) {
1907 ActivityDiagnostics::recordUserReturnStage(
1908 ActivityDiagnostics::UserReturnStage::Checkpoint,
1909 ActivityDiagnostics::timestamp() - checkpointStart);
1912 return finishWork(
true);
1914 uint64_t stageStart = diagnosticSample ? ActivityDiagnostics::timestamp() : 0;
1916 if (diagnosticSample) {
1917 ActivityDiagnostics::recordUserReturnStage(ActivityDiagnostics::UserReturnStage::ProcessStop,
1918 ActivityDiagnostics::timestamp() - stageStart);
1921 return finishWork(
true);
1922 stageStart = diagnosticSample ? ActivityDiagnostics::timestamp() : 0;
1924 state.getStackPointer(), Thread::EventSelection::AnyDeliverable,
nullptr, &state);
1925 if (diagnosticSample) {
1926 ActivityDiagnostics::recordUserReturnStage(ActivityDiagnostics::UserReturnStage::Event,
1927 ActivityDiagnostics::timestamp() - stageStart);
1929 stageStart = diagnosticSample ? ActivityDiagnostics::timestamp() : 0;
1932 if (diagnosticSample) {
1933 ActivityDiagnostics::recordUserReturnStage(ActivityDiagnostics::UserReturnStage::ProcessStop,
1934 ActivityDiagnostics::timestamp() - stageStart);
1937 owner->clearUserReturnWorkIfIdle();
1938 return finishWork(terminal);
1942 bool diagnosticSample) {
1949 uintptr_t faultAddress = 0;
1950 uintptr_t errorCode = 0;
1956 Subsystem* subsystem = process ? process->getSubsystem() :
nullptr;
1957 if (!subsystem || rawType >
static_cast<size_t>(Subsystem::Other)) {
1958 FATAL(
"Deferred userspace exception has no valid owning subsystem");
1963 if (rawType ==
static_cast<size_t>(Subsystem::PageFault) && !state.kernelMode() &&
1965 const uint64_t faultStart = diagnosticSample ? ActivityDiagnostics::timestamp() : 0;
1967 if (diagnosticSample) {
1968 ActivityDiagnostics::recordUserReturnStage(
1969 ActivityDiagnostics::UserReturnStage::DeferredFault,
1970 ActivityDiagnostics::timestamp() - faultStart);
1971 ActivityDiagnostics::recordUserReturnFaultOutcome(handled);
1978 faultAddress, errorCode);
1993 Subsystem* subsystem = process ? process->getSubsystem() :
nullptr;
1995 FATAL(
"Return-to-user process exit has no owning subsystem.");
1998 subsystem->
exit(request.code, request.cause);
1999 FATAL(
"Subsystem::exit returned to a user-return boundary.");
2003#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
2005struct HostedRunnableCurrentContext {
2006 HostedRunnableCurrentContext(
Thread* current,
Thread* idleOwner)
2007 : driver(current), idle(idleOwner), idleWhileRunnable(0) {}
2014HostedRunnableCurrentContext* g_HostedRunnableCurrentContext =
nullptr;
2016void observeHostedRunnableCurrent(ProcessorBase::HostedContextSwitchStage stage) {
2017 auto* context = __atomic_load_n(&g_HostedRunnableCurrentContext, __ATOMIC_ACQUIRE);
2018 if (context && stage == ProcessorBase::HostedContextSwitchStage::SwitchStateReturnedMasked &&
2020 context->driver->getStatus() == Thread::Ready) {
2021 context->idleWhileRunnable += 1;
2025struct HostedNewThreadContext {
2029int hostedNewThreadWorkerEntry(
void* parameter) {
2030 HostedNewThreadContext* context =
reinterpret_cast<HostedNewThreadContext*
>(parameter);
2031 context->calls += 1;
2037bool PerProcessorScheduler::currentIrqWorkDoorbellPendingForTest() {
2041void PerProcessorScheduler::serviceCurrentIrqWorkDoorbellForTest() {
2045bool PerProcessorScheduler::runHostedNewThreadWorkerRegressions() {
2048 "HOSTED-WAIT-TEST: per-processor worker regression requires "
2049 "the active scheduler");
2053 constexpr size_t Attempts = 10000;
2054 auto isParked = [
this](
Thread* target) {
2056 m_NewThreadDataLock.
acquire();
2058 it != m_DelayedNewThreadData.
end(); ++it) {
2060 if (pData->pThread == target) {
2065 m_NewThreadDataLock.
release();
2068 auto waitUntilParked = [&isParked, Attempts](
Thread* target) {
2069 for (
size_t attempt = 0; attempt < Attempts; ++attempt) {
2070 if (isParked(target)) {
2077 auto check = [](
bool condition,
const char*
message) {
2079 ERROR(
"HOSTED-WAIT-TEST: " <<
message);
2089 if (!check(runnableContext.idle && runnableContext.idle != runnableContext.driver,
2090 "runnable-current regression requires an ordinary thread and an idle owner")) {
2093 __atomic_store_n(&g_HostedRunnableCurrentContext, &runnableContext, __ATOMIC_RELEASE);
2094 Processor::setHostedContextSwitchHook(observeHostedRunnableCurrent);
2095 for (
size_t attempt = 0; attempt < 256; ++attempt) {
2098 Processor::setHostedContextSwitchHook(
nullptr);
2099 __atomic_store_n(&g_HostedRunnableCurrentContext,
2100 static_cast<HostedRunnableCurrentContext*
>(
nullptr), __ATOMIC_RELEASE);
2101 const bool runnablePassed =
2104 runnableContext.driver->getStatus() == Thread::Running,
2105 "scheduler entered idle while the yielding thread remained runnable");
2106 passed &= runnablePassed;
2107 if (runnablePassed) {
2108 NOTICE(
"HOSTED-WAIT-TEST: PASS scheduler-runnable-current-keeps-cpu");
2111 HostedNewThreadContext reapContext;
2112 const size_t reapBaseline = m_nDeferredThreadReapCompletions.value();
2113 Thread* reapTarget =
new Thread(kernelProcess, hostedNewThreadWorkerEntry, &reapContext,
nullptr,
2115 reapTarget->setName(
"hosted deferred Thread reap target");
2117 bool reapCompleted =
false;
2118 for (
size_t attempt = 0; attempt < Attempts; ++attempt) {
2119 if (m_nDeferredThreadReapCompletions.value() == reapBaseline + 1) {
2120 reapCompleted =
true;
2125 const bool reapPassed = check(
2126 reapStarted && reapCompleted && reapContext.calls == 1 && !m_nDeferredThreadReaps.value(),
2127 "detached Thread was not destroyed by the ordinary maintenance worker");
2128 passed &= reapPassed;
2130 NOTICE(
"HOSTED-WAIT-TEST: PASS perprocessor-deferred-thread-reap");
2133 HostedNewThreadContext delayedContext;
2134 Thread* delayed =
new Thread(kernelProcess, hostedNewThreadWorkerEntry, &delayedContext,
nullptr,
2136 delayed->setName(
"hosted delayed add-worker target");
2137 const bool delayedParked = waitUntilParked(delayed);
2138 for (
size_t attempt = 0; attempt < 64; ++attempt) {
2141 const bool stayedDormant = delayedParked && isParked(delayed) && delayedContext.calls == 0;
2142 const bool started = delayed->
start();
2144 const bool delayedPassed =
2145 check(stayedDormant && started && delayedJoined && delayedContext.calls == 1,
2146 "delayed add-worker target did not remain parked until its single "
2147 "start publication");
2148 passed &= delayedPassed;
2149 if (delayedPassed) {
2150 NOTICE(
"HOSTED-WAIT-TEST: PASS perprocessor-delayed-start-wake");
2153 HostedNewThreadContext terminatedContext;
2154 Thread* terminated =
new Thread(kernelProcess, hostedNewThreadWorkerEntry, &terminatedContext,
2155 nullptr,
false,
true,
true);
2156 terminated->setName(
"hosted terminated add-worker target");
2157 const bool terminatedParked = waitUntilParked(terminated);
2160 const bool terminatedPassed =
2161 check(terminatedParked && terminatedJoined && terminatedContext.calls == 0,
2162 "terminate-before-start did not retire the parked add-worker target");
2163 passed &= terminatedPassed;
2164 if (terminatedPassed) {
2165 NOTICE(
"HOSTED-WAIT-TEST: PASS perprocessor-terminate-before-start");
2168 HostedNewThreadContext teardownContext;
2169 Thread* teardown =
new Thread(kernelProcess, hostedNewThreadWorkerEntry, &teardownContext,
2170 nullptr,
false,
true,
true);
2171 teardown->setName(
"hosted add-worker teardown target");
2172 const bool teardownParked = waitUntilParked(teardown);
2174 HostedNewThreadContext detachedTeardownContext;
2175 const size_t detachedReapBaseline = m_nDeferredThreadReapCompletions.value();
2176 Thread* detachedTeardown =
new Thread(kernelProcess, hostedNewThreadWorkerEntry,
2177 &detachedTeardownContext,
nullptr,
false,
true,
true);
2178 detachedTeardown->setName(
"hosted detached add-worker teardown target");
2179 const bool detachedTeardownParked = waitUntilParked(detachedTeardown);
2180 const bool detachedTeardownClaimed = detachedTeardown->
detach();
2182 stopNewThreadWorker();
2184 bool detachedTeardownReaped =
false;
2185 for (
size_t attempt = 0; attempt < Attempts; ++attempt) {
2186 if (m_nDeferredThreadReapCompletions.value() == detachedReapBaseline + 1) {
2187 detachedTeardownReaped =
true;
2193 m_NewThreadDataLock.
acquire();
2194 const bool teardownDrained = !m_NewThreadAdmissionOpen && m_StopNewThreadWorker &&
2195 !m_NewThreadData.
count() && !m_DelayedNewThreadData.
count();
2196 m_NewThreadDataLock.
release();
2197 const bool workerJoined = !m_NewThreadWorker;
2202 for (
size_t attempt = 0; attempt < 64; ++attempt) {
2206 const bool teardownPassed = check(
2207 teardownParked && teardownJoined && teardownContext.calls == 0 && detachedTeardownParked &&
2208 detachedTeardownClaimed && detachedTeardownReaped && detachedTeardownContext.calls == 0 &&
2209 !m_nDeferredThreadReaps.value() && teardownDrained && workerJoined,
2210 "owned add worker did not drain and join with pending parked work");
2211 passed &= teardownPassed;
2212 if (teardownPassed) {
2213 NOTICE(
"HOSTED-WAIT-TEST: PASS perprocessor-worker-teardown");
2216 startNewThreadWorker(kernelProcess);
2218 HostedNewThreadContext restartContext;
2219 Thread* restart =
new Thread(kernelProcess, hostedNewThreadWorkerEntry, &restartContext,
nullptr,
2221 restart->setName(
"hosted restarted add-worker target");
2222 const bool restartParked = waitUntilParked(restart);
2223 const bool restartStarted = restart->
start();
2224 bool restartReapable =
false;
2225 for (
size_t attempt = 0; attempt < Attempts; ++attempt) {
2226 if (restart->isReapableForHostedTest()) {
2227 restartReapable =
true;
2233 const bool restartPassed =
2234 check(restartParked && restartStarted && restartJoined && restartContext.calls == 1,
2235 "replacement add worker did not process a fresh delayed admission");
2236 passed &= restartPassed;
2237 if (restartPassed) {
2238 NOTICE(
"HOSTED-WAIT-TEST: PASS perprocessor-worker-restart");
2246 __atomic_store_n(&m_IdleWakeRequested,
true, __ATOMIC_RELEASE);
2250void PerProcessorScheduler::setIdle(
Thread* pThread) {
2251 __atomic_store_n(&m_pIdleThread, pThread, __ATOMIC_RELEASE);
2253 __atomic_store_n(&m_IdleWakeRequested,
false, __ATOMIC_RELEASE);
2259 updateOneShotTimer();
2260 if (__atomic_load_n(&m_IdleWakeRequested, __ATOMIC_ACQUIRE)) {
void waitForCompletion(Mutex &mutex)
void finishBatch(size_t generation)
static uintptr_t getTrampoline()
virtual bool prefersAlternateUserStack() const
virtual bool isDeletable()
uintptr_t getHandlerAddress()
HandlerPrivilege getHandlerPrivilege() const
static size_t getHandlerBufferSize()
bool isValidHandlerMapping(size_t mappingFlags) const
virtual bool isSignalEvent() const
virtual UserReturnDelivery deliverAtUserReturn(InterruptState &)
virtual bool requiresExactUserReturnState() const
static uintptr_t getHandlerBuffer()
virtual size_t getNumber()=0
virtual size_t serialize(uint8_t *pBuffer)=0
MUST_USE_RESULT PopResult pop(Node *&out)
::Iterator< T, node_t > Iterator
bool checkSchedule(size_t nCpu=~0U)
bool lockReleased(const Spinlock *pLock, size_t nCpu=~0U)
virtual SchedulerTimer * getSchedulerTimer()=0
virtual Timer * getTimer()=0
MUST_USE_RESULT bool tryEnter()
void setClockDeadline(uint64_t deadline)
void serviceDeferredSubsystemException(InterruptState &state, bool diagnosticSample=false)
void commitCurrentThreadExit(Spinlock *pLock=0) NORETURN
void timer(uint64_t delta, InterruptState &state)
void schedule(Thread::Status nextStatus=Thread::Ready, bool dispatchEvents=true)
SchedulingAlgorithm * m_pSchedulingAlgorithm
void requestCurrentThreadExitToIdle()
void ringIrqWorkDoorbell()
void publishDeferredTimeAccounting()
void idleUntilInterrupt()
void commitUserReturnTerminalState()
void checkEventState(uintptr_t userStack)
void requestIdleThreadWakeup()
void servicePendingScheduling()
void abandonCurrentThreadStack(StackDiscardReason reason, Spinlock *pLock=0) NORETURN
void eventHandlerReturned() NORETURN
void addThread(Thread *pThread, Thread::ThreadStartFunc pStartFunction, void *pParam, bool bUsermode, void *pStack)
void initialise(Thread *pThread)
MUST_USE_RESULT bool serviceProcessStopAtUserReturn(ProcessStopGateMode mode=ProcessStopGateMode::StopOnly)
void registerWorkerWake(SchedulerWorkerWake &worker, WaitQueue &waiters)
static size_t stackDiscardCount()
void serviceIrqWorkDoorbell()
MUST_USE_RESULT bool serviceUserReturnWork(InterruptState &state, UserReturnFrame::Origin origin=UserReturnFrame::Origin::Interrupt, bool diagnosticSample=false)
void unregisterWorkerWake(SchedulerWorkerWake &worker)
void killCurrentThread(Spinlock *pLock=0) NORETURN
static void deleteThreadThenRestoreState(Thread *pThread, SchedulerState &newState, volatile uintptr_t *pLock=0) NORETURN
void publishReadyFromWait(Thread *pThread)
virtual physical_uintptr_t allocatePage(size_t pageConstraints=0)=0
static PhysicalMemoryManager & instance()
static constexpr size_t getPageSize() PURE
@ Reaped
Terminal wait status is visible; the owner may still be on-stack.
void publishTerminationReapable()
VirtualAddressSpace * getAddressSpace()
OperationBarrier m_DeferredThreadReaps
bool terminatingThreadReapable(Thread *pThread, bool &wakeOwner)
WaitQueue m_TerminationWaiters
static void restoreState(SchedulerState &state, volatile uintptr_t *pLock=0) NORETURN
static void jumpUser(volatile uintptr_t *pLock, uintptr_t address, uintptr_t stack, uintptr_t p1=0, uintptr_t p2=0, uintptr_t p3=0, uintptr_t p4=0) NORETURN
static bool getInterrupts()
static void setTlsBase(uintptr_t newBase)
static ProcessorInformation & information()
static bool saveState(SchedulerState &state)
static bool inDeviceHardIrq()
static void switchAddressSpace(VirtualAddressSpace &AddressSpace)
static bool guardDeviceHardIrqOperation(DeviceHardIrqOperation operation)
static ExecutionContext executionContext()
static void switchState(bool bInterrupts, SchedulerState &a, SchedulerState &b, volatile uintptr_t *pLock=0)
static void jumpKernel(volatile uintptr_t *pLock, uintptr_t address, uintptr_t stack, uintptr_t p1=0, uintptr_t p2=0, uintptr_t p3=0, uintptr_t p4=0) NORETURN
static ProcessorInformation * informationAt(size_t cpu)
static void saveAndJumpKernel(bool bInterrupts, SchedulerState &s, volatile uintptr_t *pLock, uintptr_t address, uintptr_t stack, uintptr_t p1=0, uintptr_t p2=0, uintptr_t p3=0, uintptr_t p4=0)
static void setInterrupts(bool bEnable)
static void saveAndJumpUser(bool bInterrupts, SchedulerState &s, volatile uintptr_t *pLock, uintptr_t address, uintptr_t stack, uintptr_t p1=0, uintptr_t p2=0, uintptr_t p3=0, uintptr_t p4=0)
static void haltUntilInterrupt()
virtual bool registerHandler(SchedulerTimerHandler *handler)=0
virtual bool removeHandler(SchedulerTimerHandler *handler)=0
virtual bool supportsOneShot() const
void drainDeferredTimeAccounting()
static Scheduler & instance()
void requestLoadAverageSample()
virtual void removeThread(Thread *pThread)=0
virtual void addThread(Thread *pThread)=0
virtual bool hasReady()=0
virtual Thread * getNext(Thread *pCurrentThread)=0
bool acquire(size_t n=1, size_t timeoutSecs=0, size_t timeoutUsecs=0)
volatile processor_register_t * deferredReleaseWord()
bool acquire(bool recurse=false, bool safe=true)
void exit(uintptr_t ra=0)
virtual bool resolveUserPageFault(Thread &, InterruptState &, uintptr_t, uintptr_t)
virtual void threadException(Thread *pThread, ExceptionType eType, InterruptState *pState=nullptr, uintptr_t faultAddress=0, uintptr_t errorCode=0)
virtual void exit(int code, ExitCause cause=ExitCause::Normal)=0
void recordTime(CpuTimeMode mode)
void unlinkWaitsForStackDiscard()
void accountTimerTick(Time::Timestamp delta, bool kernelMode)
void setUnwindState(UnwindType ut)
CpuTimeMode currentTimeAccountingMode() const
AlternateSignalStack m_AlternateSignalStack
@ Continue
No unwind necessary, carry on as normal.
@ TerminateThread
Exit only this thread during Process exit.
@ Exit
Exit the owning process at the next safe boundary.
bool interruptWaitUnlocked(WaitQueue::WakeReason reason, PerProcessorScheduler *&readyScheduler)
DeferredScopeRecord * m_pDeferredScopes[MAX_NESTED_EVENTS]
size_t m_EventDeferralDepth
MUST_USE_RESULT Event::Delivery getNextEvent(EventSelection selection=EventSelection::AnyDeliverable)
int(* ThreadStartFunc)(void *)
bool eventsDeferred() const
UnwindType getUnwindState()
DeferredThreadReapNode m_DeferredReapNode
void trackTime(CpuTimeMode mode)
void setScheduler(class PerProcessorScheduler *pScheduler)
Process * getParent() const
void closeExternalLeaseAdmission()
void popState(bool clean=true)
class PerProcessorScheduler * getScheduler() const
DeferredProcessExit takeDeferredProcessExit()
bool isTerminationDeferred() const
SchedulerState * pushState()
bool sendEvent(Event *pEvent)
size_t getStateLevel() const
size_t m_TerminationDeferralDepth
bool takeDeferredSubsystemException(size_t &type, uintptr_t &faultAddress, uintptr_t &errorCode)
void markDeferredUserReturnSignalInterruption()
void abandonCurrentState(bool clean=false)
void transitionTime(CpuTimeMode from, CpuTimeMode to, bool interruptsAlreadyDisabled=false)
virtual void deadlineInterrupt()
static const size_t RuntimeMapping
virtual Stack * allocateStack()=0
virtual bool map(physical_uintptr_t physicalAddress, void *virtualAddress, size_t flags)=0
virtual bool isMapped(void *virtualAddress)=0
virtual uintptr_t getUserStart() const =0
static const size_t KernelMode
virtual bool getMapping(void *virtualAddress, physical_uintptr_t &physicalAddress, size_t &flags)=0
static const size_t Write
virtual uintptr_t getKernelStart() const =0
virtual bool isAddressValid(void *virtualAddress)=0
void EXPORTED_PUBLIC panic(const char *msg) NORETURN
Iterator erase(Iterator &Iter)
void pushBack(const T &value)
bool m_bOwnsAlternateSignalStack