8#include "pedigree/kernel/Atomic.h"
9#include "pedigree/kernel/Log.h"
10#include "pedigree/kernel/Subsystem.h"
11#include "pedigree/kernel/machine/IrqHandler.h"
12#include "pedigree/kernel/machine/IrqManager.h"
13#include "pedigree/kernel/machine/Machine.h"
14#include "pedigree/kernel/machine/SchedulerTimer.h"
15#include "pedigree/kernel/machine/SchedulerTimerDispatchCleanup.h"
16#include "pedigree/kernel/machine/SchedulerTimerHandler.h"
17#include "pedigree/kernel/process/Event.h"
18#include "pedigree/kernel/process/Process.h"
19#include "pedigree/kernel/process/Scheduler.h"
20#include "pedigree/kernel/process/Thread.h"
21#include "pedigree/kernel/processor/Processor.h"
22#include "pedigree/kernel/processor/ProcessorInformation.h"
23#include "pedigree/kernel/processor/VirtualAddressSpace.h"
24#include "pedigree/kernel/time/Time.h"
29#include "system/kernel/machine/hosted/IrqManager.h"
30#include "system/kernel/machine/hosted/SchedulerTimer.h"
31#include "system/kernel/machine/hosted/Timer.h"
33#if !PEDIGREE_HOSTED_CORE_SMOKE
34extern "C" int hostedSchedulerExitUserProbe(
void* parameter);
35extern "C" void hostedSchedulerExitUserProbeTimedOut(
void* parameter);
39 ".globl hostedSchedulerExitUserProbe\n"
40 ".type hostedSchedulerExitUserProbe,@function\n"
41 "hostedSchedulerExitUserProbe:\n"
70 "movq $0xa00,0(%rsp)\n"
77 "call hostedSetKernelFs@PLT\n"
78 "movq 24(%r12),%rsp\n"
81 "call hostedSchedulerExitUserProbeTimedOut@PLT\n"
83 ".size hostedSchedulerExitUserProbe,.-hostedSchedulerExitUserProbe\n");
87extern bool runHostedAccountingRegressions();
90struct TlsResetContext {
97 interruptsInitiallyEnabled(0),
101 interruptsRestored(0),
117TlsResetContext* g_TlsResetContext =
nullptr;
119void observeTlsReset(
Thread* thread, Thread::TlsResetPhase phase, uintptr_t base) {
120 TlsResetContext* context = __atomic_load_n(&g_TlsResetContext, __ATOMIC_ACQUIRE);
125 context->hookCalls += 1;
128 context->failures += 1;
131 if (phase == Thread::TlsResetBeforeClear && !base) {
132 context->beforeClearCalls += 1;
133 }
else if (phase == Thread::TlsResetCleared && !base) {
134 context->clearedCalls += 1;
135 }
else if (phase == Thread::TlsResetRemapped && base) {
136 context->remappedCalls += 1;
137 context->remappedBase = base;
139 context->failures += 1;
148int resetTlsBaseThread(
void* parameter) {
149 TlsResetContext* context =
reinterpret_cast<TlsResetContext*
>(parameter);
152 context->interruptsInitiallyEnabled = interruptsWereEnabled ? 1 : 0;
153 const uintptr_t initialBase = current->
getTlsBase();
155 reinterpret_cast<void*
>(initialBase));
158 process->resetUserReservations();
167 const uintptr_t tlsBase = context->remappedBase;
170 context->returned = 1;
174bool tlsResetAtomicRemap() {
176 TlsResetContext context;
177 Thread* target =
new Thread(process, resetTlsBaseThread, &context,
nullptr,
true,
true,
true);
178 target->setName(
"hosted TLS reset atomicity probe");
180 __atomic_store_n(&g_TlsResetContext, &context, __ATOMIC_RELEASE);
181 Thread::setTlsResetHookForHostedTest(target, observeTlsReset);
182 const bool started = target->
start();
184 Thread::setTlsResetHookForHostedTest(
nullptr,
nullptr);
185 __atomic_store_n(&g_TlsResetContext,
static_cast<TlsResetContext*
>(
nullptr), __ATOMIC_RELEASE);
192 const bool passed = started && joined && context.interruptsInitiallyEnabled &&
193 context.initialBaseValid && context.hookCalls == 3 &&
194 context.beforeClearCalls == 1 && context.clearedCalls == 1 &&
195 context.remappedCalls == 1 && !context.failures && context.mapped &&
196 context.interruptsRestored && context.returned;
199 "HOSTED-WAIT-TEST: FAIL thread-tls-reset-interrupt-atomicity: "
200 "TLS reset escaped its non-preemptible mapping window");
202 NOTICE(
"HOSTED-WAIT-TEST: PASS thread-tls-reset-interrupt-atomicity");
207struct ContextSwitchContext {
208 explicit ContextSwitchContext(
Thread* driver)
213 restoreBoundaries(0),
228ContextSwitchContext* g_ContextSwitchContext =
nullptr;
230struct SchedulerTimerContext {
231 SchedulerTimerContext() : calls(0), failures(0) {}
237SchedulerTimerContext* g_SchedulerTimerContext =
nullptr;
239constexpr int DeferredTimerExitCode = 73;
241#if !PEDIGREE_HOSTED_CORE_SMOKE
242struct SchedulerExitContext;
244struct SchedulerExitUserProbeState {
247 SchedulerExitContext* context;
248 uintptr_t kernelStackTop;
251static_assert(__builtin_offsetof(SchedulerExitUserProbeState, ready) == 0 &&
252 __builtin_offsetof(SchedulerExitUserProbeState, timedOut) == 8 &&
253 __builtin_offsetof(SchedulerExitUserProbeState, kernelStackTop) == 24,
254 "hosted scheduler user probe assembly layout changed");
256struct SchedulerExitContext {
257 SchedulerExitContext()
258 : user{0, 0, this, 0},
272 SchedulerExitUserProbeState user;
275 uintptr_t userStackBase;
276 uintptr_t userStackTop;
287SchedulerExitContext* g_SchedulerExitContext =
nullptr;
289void schedulerExitEventHandler(
size_t) {
290 SchedulerExitContext* context = __atomic_load_n(&g_SchedulerExitContext, __ATOMIC_ACQUIRE);
296 current->getHostedSignalDepth() ||
298 context->failures += 1;
300 context->eventCalls += 1;
304class SchedulerExitEvent :
public Event {
306 SchedulerExitEvent() :
Event(reinterpret_cast<uintptr_t>(&schedulerExitEventHandler), false) {}
317class SchedulerExitSubsystem :
public Subsystem {
319 explicit SchedulerExitSubsystem(SchedulerExitContext& context)
322 void exit(
int code, ExitCause cause)
override {
324 if (code != DeferredTimerExitCode || current != m_Context.target || m_Context.eventCalls != 1 ||
326 current->getHostedSignalDepth() ||
328 m_Context.failures += 1;
330 m_Context.exitCalls += 1;
355 SchedulerExitContext& m_Context;
358class SchedulerExitProcess :
public Process {
360 SchedulerExitProcess(SchedulerExitContext& context,
Process* parent)
361 :
Process(DeferredPublication(), parent) {
362 setSubsystem(
new SchedulerExitSubsystem(context));
363 description() +=
"hosted scheduler return-tail exit probe";
367 ~SchedulerExitProcess()
override {
372void queueExitEventFromSchedulerTick(uint64_t delta, InterruptState& state) {
373 SchedulerExitContext* context = __atomic_load_n(&g_SchedulerExitContext, __ATOMIC_ACQUIRE);
378 context->hookCalls += 1;
379 if (!__atomic_load_n(&context->user.ready, __ATOMIC_ACQUIRE)) {
382 if (current != context->target) {
385 context->targetHookCalls += 1;
387 state.getStackPointer() < context->userStackBase ||
388 state.getStackPointer() > context->userStackTop) {
391 context->userHookCalls += 1;
392 if (!context->queued.compareAndSwap(0, 1)) {
396 const uint64_t interval = 100 * Time::Multiplier::Millisecond;
399 state.getStackPointer() < context->userStackBase ||
400 state.getStackPointer() > context->userStackTop ||
402 state.getInterruptNumber() != SIGUSR2 ||
403 state.getInterruptSource() != HostedSchedulerTimer::sourceForTest()) {
404 context->failures += 1;
409 if (!current->
sendEvent(context->event)) {
410 context->failures += 1;
412 context->tickCalls += 1;
415bool schedulerTimerExitDeferral() {
416 constexpr const char* Test =
"scheduler-timer-exit-return-tail";
417 SchedulerExitContext context;
418 SchedulerExitEvent event;
419 context.event = &event;
420 const bool timerSlowed = HostedTimer::setSignalIntervalForTest(4 * Time::Multiplier::Second);
422 SchedulerExitProcess* process =
423 driver ?
new SchedulerExitProcess(context, driver->
getParent()) : nullptr;
428 context.userStackBase =
reinterpret_cast<uintptr_t
>(userStack->getBase());
429 context.userStackTop =
reinterpret_cast<uintptr_t
>(userStack->getTop());
430 target =
new Thread(process, hostedSchedulerExitUserProbe, &context.user, userStack->getTop(),
432 context.target = target;
433 context.user.kernelStackTop =
reinterpret_cast<uintptr_t
>(target->
getKernelStack());
434 target->setName(
"hosted scheduler exit return-tail probe");
439 __atomic_store_n(&g_SchedulerExitContext, &context, __ATOMIC_RELEASE);
440 HostedSchedulerTimer::setHardContextHookForTest(queueExitEventFromSchedulerTick);
443 const bool started = target && target->
start();
444 bool reapable =
false;
445 timespec startedAt = {};
447 clock_gettime(CLOCK_MONOTONIC, &startedAt);
448 while (started && !reapable) {
449 reapable = target->isReapableForHostedTest();
454 clock_gettime(CLOCK_MONOTONIC, &now);
455 if (now.tv_sec - startedAt.tv_sec >= 6) {
459 if (started && !reapable) {
460 ERROR(
"HOSTED-WAIT-TEST: DETAIL "
461 << Test <<
": ready=" << context.user.ready <<
" timed-out=" << context.user.timedOut
462 <<
" hooks=" << context.hookCalls.value() <<
" target-hooks="
463 << context.targetHookCalls.value() <<
" user-hooks=" << context.userHookCalls.value()
464 <<
" queued=" << context.queued.value() <<
" event-calls=" << context.eventCalls.value()
465 <<
" exit-calls=" << context.exitCalls.value() <<
" target-status="
467 <<
" target-signal-depth=" << target->getHostedSignalDepth()
468 <<
" timer-admissions=" << HostedSchedulerTimer::activeDispatchesForTest());
469 FATAL(
"Hosted scheduler exit probe made no bounded progress");
472 if (target && !started) {
477 HostedSchedulerTimer::setHardContextHookForTest(
nullptr);
478 __atomic_store_n(&g_SchedulerExitContext,
static_cast<SchedulerExitContext*
>(
nullptr),
480 const bool timerRestored = HostedTimer::setSignalIntervalForTest(Time::Multiplier::Millisecond);
483 event.waitForDeliveries();
491 const bool passed = timerSlowed && timerRestored && started && joined && context.user.ready &&
492 !context.user.timedOut && context.tickCalls == 1 && context.queued == 1 &&
493 context.eventCalls == 1 && context.exitCalls == 1 && !context.failures;
495 ERROR(
"HOSTED-WAIT-TEST: FAIL " << Test
496 <<
": process exit ran before the "
497 "IRQ return-to-user tail");
500 "HOSTED-WAIT-TEST: PASS "
501 "scheduler-timer-exit-return-tail");
507struct HostedSignalSwitchContext {
508 explicit HostedSignalSwitchContext(
Thread* driver)
532HostedSignalSwitchContext* g_HostedSignalSwitchContext =
nullptr;
534void observeHostedAutodisarmTick(uint64_t delta, InterruptState& state) {
535 HostedSignalSwitchContext* context =
536 __atomic_load_n(&g_HostedSignalSwitchContext, __ATOMIC_ACQUIRE);
537 if (!context || !context->armed) {
542 if (current == context->driver) {
543 context->driverTicks += 1;
546 if (current != context->target || !context->computing ||
547 !context->targetTicks.compareAndSwap(0, 1)) {
551 const uint64_t interval = 100 * Time::Multiplier::Millisecond;
552 size_t failureMask = 0;
553 failureMask |= delta < interval ? 1 : 0;
554 failureMask |= (delta % interval) ? 2 : 0;
555 failureMask |= !state.kernelMode() ? 4 : 0;
556 failureMask |= !current->getHostedSignalDepth() ? 8 : 0;
557 failureMask |= Processor::hostedSignalFrameDepthForTest() < 2 ? 16 : 0;
561 failureMask |= state.getInterruptNumber() != SIGUSR2 ? 256 : 0;
562 failureMask |= state.getInterruptSource() != HostedSchedulerTimer::sourceForTest() ? 512 : 0;
565 context->failureMask |= failureMask;
566 context->failures += 1;
570int hostedSignalSwitchTarget(
void* parameter) {
571 HostedSignalSwitchContext* context =
reinterpret_cast<HostedSignalSwitchContext*
>(parameter);
572 context->waiting = 1;
573 while (!context->armed) {
581 sigprocmask(0,
nullptr, &mask);
583 size_t failureMask = 0;
584 failureMask |= !current ? 1024 : 0;
585 failureMask |= current && current->getHostedSignalDepth() ? 2048 : 0;
586 failureMask |= !Processor::hostedSignalFrameDepthForTest() ? 4096 : 0;
588 failureMask |= sigismember(&mask, SIGUSR1) ? 16384 : 0;
589 failureMask |= sigismember(&mask, SIGUSR2) ? 32768 : 0;
592 context->failureMask |= failureMask;
593 context->failures += 1;
596 context->computing = 1;
597 timespec startedAt = {};
599 clock_gettime(CLOCK_MONOTONIC, &startedAt);
600 while (!context->targetTicks) {
602 clock_gettime(CLOCK_MONOTONIC, &now);
603 if (now.tv_sec - startedAt.tv_sec >= 3) {
604 context->failureMask |= 65536;
605 context->failures += 1;
613bool hostedSignalMaskSpansContextSwitch() {
614 constexpr const char* Test =
"hosted-signal-autodisarm-preemption";
617 &context,
nullptr,
false,
true,
true);
618 target->setName(
"hosted signal-mask context-switch probe");
619 context.target = target;
620 const bool started = target->
start();
622 constexpr size_t Attempts = 10000;
623 for (
size_t attempt = 0; started && !context.waiting && attempt < Attempts; ++attempt) {
629 __atomic_store_n(&g_HostedSignalSwitchContext, &context, __ATOMIC_RELEASE);
630 HostedSchedulerTimer::setHardContextHookForTest(observeHostedAutodisarmTick);
633 const Time::Timestamp deadline = Time::getTicks() + (3 * Time::Multiplier::Second);
634 while (!context.ran && Time::getTicks() < deadline) {
639 HostedSchedulerTimer::setHardContextHookForTest(
nullptr);
640 __atomic_store_n(&g_HostedSignalSwitchContext,
static_cast<HostedSignalSwitchContext*
>(
nullptr),
646 for (
size_t attempt = 0; started && !context.ran && attempt < Attempts; ++attempt) {
655 const bool passed = started && context.waiting && context.computing && context.driverTicks &&
656 context.targetTicks == 1 && context.ran && joined && !context.failures;
658 ERROR(
"HOSTED-WAIT-TEST: FAIL "
659 << Test <<
": s=" << started <<
" w=" <<
static_cast<size_t>(context.waiting)
660 <<
" c=" <<
static_cast<size_t>(context.computing)
661 <<
" d=" <<
static_cast<size_t>(context.driverTicks) <<
" t="
662 <<
static_cast<size_t>(context.targetTicks) <<
" r=" <<
static_cast<size_t>(context.ran)
663 <<
" j=" << joined <<
" f=" <<
static_cast<size_t>(context.failures)
664 <<
" m=" << context.failureMask.value());
667 "HOSTED-WAIT-TEST: PASS "
668 "hosted-signal-autodisarm-preemption");
673void observeSchedulerTimerHardContext(uint64_t delta, InterruptState& state) {
674 SchedulerTimerContext* context = __atomic_load_n(&g_SchedulerTimerContext, __ATOMIC_ACQUIRE);
680 const uint64_t interval = 100 * Time::Multiplier::Millisecond;
681 if (delta < interval || (delta % interval) || !current || !current->getHostedSignalDepth() ||
682 !Processor::onHostedExecutionThread() ||
685 state.getInterruptNumber() != SIGUSR2 ||
686 state.getInterruptSource() != HostedSchedulerTimer::sourceForTest()) {
687 context->failures += 1;
692bool schedulerTimerHardContext() {
693 constexpr const char* Test =
"hosted-scheduler-timer-hard-context";
694 SchedulerTimerContext context;
695 const bool directRoute = HostedSchedulerTimer::directRoutePublishedForTest();
699 __atomic_store_n(&g_SchedulerTimerContext, &context, __ATOMIC_RELEASE);
700 HostedSchedulerTimer::setHardContextHookForTest(observeSchedulerTimerHardContext);
703 const Time::Timestamp deadline = Time::getTicks() + (2 * Time::Multiplier::Second);
704 while (!context.calls && Time::getTicks() < deadline) {
709 HostedSchedulerTimer::setHardContextHookForTest(
nullptr);
710 __atomic_store_n(&g_SchedulerTimerContext,
static_cast<SchedulerTimerContext*
>(
nullptr),
714 const bool passed = directRoute && context.calls && !context.failures;
716 ERROR(
"HOSTED-WAIT-TEST: FAIL " << Test
717 <<
": the scheduler callback did not "
718 "use its dedicated controller route");
721 "HOSTED-WAIT-TEST: PASS "
722 "hosted-scheduler-timer-hard-context");
729 ConflictingSchedulerTimerHandler() : calls(0) {}
731 void timer(uint64_t, InterruptState&)
override {
741 : m_Timer(timer), calls(0), removalSucceeded(0), continuedAfterRemoval(0), wrongContext(0) {}
743 void timer(uint64_t, InterruptState&)
override {
748 if (m_Timer && m_Timer->removeHandler(
this)) {
749 removalSucceeded = 1;
753 continuedAfterRemoval = 1;
763bool schedulerTimerAbandonedAdmissionCleanup() {
764 constexpr const char* Test =
"hosted-scheduler-timer-abandoned-admission-cleanup";
766 ConflictingSchedulerTimerHandler handler;
769 const size_t initialLevel = current ? current->
getStateLevel() : 0;
770 const bool published = current && slot.
publish(owner, &handler);
771 SchedulerState* previous = published ? current->
pushState() :
nullptr;
772 const bool pushed = previous && current->
getStateLevel() == initialLevel + 1;
774 bool admitted =
false;
775 bool counted =
false;
776 bool abandoned =
false;
796 const bool removed = abandoned && slot.
unpublish(owner, &handler);
797 const bool republished = removed && slot.
publish(owner, &handler);
798 bool readmitted =
false;
805 const bool finallyRemoved = republished && drained && slot.
unpublish(owner, &handler);
807 const bool passed = published && pushed && admitted && counted && abandoned && removed &&
808 republished && readmitted && drained && finallyRemoved;
810 ERROR(
"HOSTED-WAIT-TEST: FAIL " << Test
811 <<
": an abandoned scheduler frame "
812 "stranded its callback admission");
815 "HOSTED-WAIT-TEST: PASS "
816 "hosted-scheduler-timer-abandoned-admission-cleanup");
821bool schedulerTimerSingleOwner() {
822 constexpr const char* Test =
"hosted-scheduler-timer-single-owner";
825 ConflictingSchedulerTimerHandler conflicting;
827 const bool ownerPublished = owner !=
nullptr;
828 const bool nullRegistrationRejected = timer && !timer->
registerHandler(
nullptr);
829 const bool duplicateRejected = timer && !timer->
registerHandler(owner);
830 const bool conflictRejected = timer && !timer->
registerHandler(&conflicting);
831 const bool nullRemovalRejected = timer && !timer->
removeHandler(
nullptr);
832 const bool wrongOwnerRejected = timer && !timer->
removeHandler(&conflicting);
833 const bool ownerPreserved = HostedSchedulerTimer::publishedHandlerForTest() == owner;
835 const bool passed = timer && ownerPublished && nullRegistrationRejected && duplicateRejected &&
836 conflictRejected && nullRemovalRejected && wrongOwnerRejected &&
837 ownerPreserved && !conflicting.calls;
839 ERROR(
"HOSTED-WAIT-TEST: FAIL " << Test
840 <<
": handler ownership was replaced "
841 "or removed by a non-owner");
844 "HOSTED-WAIT-TEST: PASS "
845 "hosted-scheduler-timer-single-owner");
850bool schedulerTimerSelfRemovalRejected() {
851 constexpr const char* Test =
"hosted-scheduler-timer-self-removal-rejected";
854 SelfRemovingSchedulerTimerHandler probe(timer);
857 constexpr size_t RemovalAttemptLimit = 256;
858 while (timer && handler && attempts < RemovalAttemptLimit) {
871 size_t ownerRemovalAttempts = 0;
872 const bool ownerRemoved = removeWithRetry(owner, ownerRemovalAttempts);
873 const bool probeRegistered = ownerRemoved && timer->
registerHandler(&probe);
874 const Time::Timestamp deadline = Time::getTicks() + (2 * Time::Multiplier::Second);
875 while (probeRegistered && !probe.calls && Time::getTicks() < deadline) {
879 size_t probeRemovalAttempts = 0;
880 const bool probeRemoved =
881 probeRegistered && !probe.removalSucceeded && removeWithRetry(&probe, probeRemovalAttempts);
882 if (probeRegistered && !probeRemoved && !probe.removalSucceeded) {
883 FATAL(
"Hosted scheduler-timer regression could not retire its stack probe");
886 const bool probeQuiesced = !probeRegistered || probeRemoved || probe.removalSucceeded;
887 const bool ownerRestored = ownerRemoved && probeQuiesced && timer->
registerHandler(owner);
888 if (ownerRemoved && !ownerRestored) {
889 FATAL(
"Hosted scheduler-timer regression could not restore the real owner");
891 const bool passed = ownerRemoved && probeRegistered && probe.calls.value() >= 1 &&
892 !probe.removalSucceeded && probe.continuedAfterRemoval == 1 &&
893 !probe.wrongContext && probeRemoved && ownerRestored &&
894 HostedSchedulerTimer::publishedHandlerForTest() == owner;
896 ERROR(
"HOSTED-WAIT-TEST: FAIL " << Test <<
": owner-removed=" << ownerRemoved <<
" registered="
897 << probeRegistered <<
" calls=" << probe.calls.value());
898 ERROR(
"HOSTED-WAIT-TEST: DETAIL "
899 << Test <<
": callback-remove=" << probe.removalSucceeded.value()
900 <<
" continued=" << probe.continuedAfterRemoval.value()
901 <<
" wrong-context=" << probe.wrongContext.value() <<
" probe-removed=" << probeRemoved);
902 ERROR(
"HOSTED-WAIT-TEST: DETAIL "
903 << Test <<
": owner-restored=" << ownerRestored
904 <<
" published-owner=" << (HostedSchedulerTimer::publishedHandlerForTest() == owner)
905 <<
" owner-remove-attempts=" << ownerRemovalAttempts
906 <<
" probe-remove-attempts=" << probeRemovalAttempts
907 <<
" active-dispatches=" << HostedSchedulerTimer::activeDispatchesForTest());
910 "HOSTED-WAIT-TEST: PASS "
911 "hosted-scheduler-timer-self-removal-rejected");
919 return HardIrqDisposition::Handled;
923bool schedulerRouteIsDedicated() {
924 constexpr const char* Test =
"hosted-scheduler-route-dedicated";
925 IrqManager* manager = Machine::instance().getIrqManager();
927 RejectedHostedDeviceHandler deviceHandler;
928 const irq_id_t rejected =
932 manager && handler && !rejected && HostedIrqManager::schedulerIrqHandlerForTest(1) == handler;
934 ERROR(
"HOSTED-WAIT-TEST: FAIL " << Test
935 <<
": hosted admitted a device handler "
936 "on its dedicated scheduler line");
939 "HOSTED-WAIT-TEST: PASS "
940 "hosted-scheduler-route-dedicated");
945void observeQueuedSchedulerTick(uint64_t, InterruptState&) {
946 ContextSwitchContext* context = __atomic_load_n(&g_ContextSwitchContext, __ATOMIC_ACQUIRE);
951 const size_t phase = context->phase;
952 if (phase && phase < 4) {
953 context->tickCalls += 1;
956 context->failures += 1;
962void contextSwitchHook(ProcessorBase::HostedContextSwitchStage stage) {
963 ContextSwitchContext* context = __atomic_load_n(&g_ContextSwitchContext, __ATOMIC_ACQUIRE);
969 case ProcessorBase::HostedContextSwitchStage::SwitchStateReturnedMasked:
970 if (!context->phase.compareAndSwap(0, 1)) {
973 context->switchReturns += 1;
975 context->failures += 1;
978 case ProcessorBase::HostedContextSwitchStage::SchedulerBookkeepingComplete:
979 if (context->phase ==
static_cast<size_t>(1)) {
980 context->bookkeepingCalls += 1;
982 context->failures += 1;
986 case ProcessorBase::HostedContextSwitchStage::SchedulerRestoringInterrupts:
987 if (context->phase ==
static_cast<size_t>(2)) {
988 context->restoreBoundaries += 1;
990 context->failures += 1;
997int contextSwitchTarget(
void* parameter) {
998 ContextSwitchContext* context =
reinterpret_cast<ContextSwitchContext*
>(parameter);
999 context->targetCalls += 1;
1003bool check(
bool condition,
const char* detail) {
1008 ERROR(
"HOSTED-WAIT-TEST: FAIL context-switch-interrupt-restore: " << detail);
1013#if !PEDIGREE_HOSTED_CORE_SMOKE
1014extern "C" void hostedSchedulerExitUserProbeTimedOut(
void* parameter) {
1015 SchedulerExitUserProbeState* state =
reinterpret_cast<SchedulerExitUserProbeState*
>(parameter);
1016 if (state && state->context) {
1017 state->context->failures += 1;
1023bool runHostedSchedulerRegressions() {
1024 if (!tlsResetAtomicRemap()) {
1028 if (!hostedSignalMaskSpansContextSwitch() || !schedulerTimerSingleOwner() ||
1029 !schedulerTimerAbandonedAdmissionCleanup() || !schedulerTimerSelfRemovalRejected() ||
1030 !schedulerRouteIsDedicated() || !schedulerTimerHardContext()) {
1034#if !PEDIGREE_HOSTED_CORE_SMOKE
1037 if (!schedulerTimerExitDeferral()) {
1042 if (!runHostedAccountingRegressions()) {
1047 ContextSwitchContext context(driver);
1048 const bool directRoutePreserved = HostedSchedulerTimer::directRoutePublishedForTest();
1051 &context,
nullptr,
false,
true,
true);
1052 target->setName(
"hosted context-switch IRQ target");
1054 __atomic_store_n(&g_ContextSwitchContext, &context, __ATOMIC_RELEASE);
1055 Processor::setHostedContextSwitchHook(contextSwitchHook);
1056 HostedSchedulerTimer::setHardContextHookForTest(observeQueuedSchedulerTick);
1057 const bool started = target->
start();
1059 constexpr size_t Attempts = 10000;
1060 bool completed =
false;
1061 for (
size_t attempt = 0; started && attempt < Attempts; ++attempt) {
1062 if (context.phase ==
static_cast<size_t>(4) && target->isReapableForHostedTest()) {
1069 HostedSchedulerTimer::setHardContextHookForTest(
nullptr);
1070 Processor::setHostedContextSwitchHook(
nullptr);
1071 __atomic_store_n(&g_ContextSwitchContext,
static_cast<ContextSwitchContext*
>(
nullptr),
1074 const bool targetJoined = target->isReapableForHostedTest() && target->
joinForCompletion();
1075 const bool directRouteAfterRemoval = HostedSchedulerTimer::directRoutePublishedForTest();
1078 check(directRoutePreserved && started && completed && targetJoined &&
1079 directRouteAfterRemoval && context.switchReturns == 1 &&
1080 context.bookkeepingCalls == 1 && context.restoreBoundaries == 1 &&
1081 context.tickCalls == 1 && context.targetCalls == 1 && context.failures == 0,
1082 "the queued scheduler IRQ escaped the masked post-switch boundary");
1084 NOTICE(
"HOSTED-WAIT-TEST: PASS context-switch-interrupt-restore");
virtual size_t getNumber()=0
virtual size_t serialize(uint8_t *pBuffer)=0
virtual HardIrqDisposition irq(irq_id_t number, InterruptState &state)=0
virtual irq_id_t registerHardIsaIrqHandler(uint8_t irq, HardIrqHandler *handler, const IrqPolicy &policy)=0
virtual SchedulerTimer * getSchedulerTimer()=0
VirtualAddressSpace * getAddressSpace()
LargeStaticString & description()
void prepareForDestruction()
static bool getInterrupts()
static ProcessorInformation & information()
static bool inDeviceHardIrq()
static ExecutionContext executionContext()
static void setInterrupts(bool bEnable)
bool beginDispatch(size_t owner, DispatchGuard &guard)
bool publish(size_t owner, SchedulerTimerHandler *handler)
size_t activeDispatches() const
bool unpublish(size_t owner, SchedulerTimerHandler *handler)
virtual void timer(uint64_t delta, InterruptState &state)=0
virtual bool registerHandler(SchedulerTimerHandler *handler)=0
virtual bool removeHandler(SchedulerTimerHandler *handler)=0
static Scheduler & instance()
virtual bool invoke(const char *name, Vector< String > &argv, Vector< String > &env)=0
virtual bool kill(KillReason killReason, Thread *pThread=0)=0
CpuTimeMode currentTimeAccountingMode() const
static void threadExited() NORETURN
Process * getParent() const
void popState(bool clean=true)
SchedulerState * pushState()
bool sendEvent(Event *pEvent)
size_t getStateLevel() const
void deferProcessExit(int code)
void abandonCurrentState(bool clean=false)
A vector / dynamic array.
virtual void freeStack(Stack *pStack)=0
virtual Stack * allocateStack()=0
virtual bool isMapped(void *virtualAddress)=0
virtual void revertToKernelAddressSpace()=0