12#include "pedigree/kernel/LockGuard.h"
13#include "pedigree/kernel/Log.h"
14#include "pedigree/kernel/process/PerProcessorScheduler.h"
15#include "pedigree/kernel/process/Process.h"
16#include "pedigree/kernel/process/Scheduler.h"
17#include "pedigree/kernel/process/SchedulingAlgorithm.h"
18#include "pedigree/kernel/process/TerminationDeferral.h"
19#include "pedigree/kernel/process/Thread.h"
20#include "pedigree/kernel/process/Uninterruptible.h"
21#include "pedigree/kernel/processor/Processor.h"
22#include "pedigree/kernel/processor/ProcessorInformation.h"
24#if X86_COMMON && MULTIPROCESSOR
25#include <machine/mach_pc/LocalApic.h>
26#include <machine/mach_pc/Pc.h>
29#if PEDIGREE_AFFINITY_TESTS
31Thread* g_AffinityCommitTarget =
nullptr;
32Thread::AffinityCommitHook g_AffinityCommitHook =
nullptr;
35void Thread::setAffinityCommitHookForTest(
Thread* target, AffinityCommitHook hook) {
36 __atomic_store_n(&g_AffinityCommitTarget, target, __ATOMIC_RELEASE);
37 __atomic_store_n(&g_AffinityCommitHook, hook, __ATOMIC_RELEASE);
43 placement.allowed = Scheduler::onlineAffinity();
44 placement.migratable =
true;
50 creator.snapshotPlacement(placement);
51 return placement.migratable ? placement : initialUser();
56 m_Placement = *placement;
57 m_Placement.allowed.intersect(Scheduler::onlineAffinity());
58 if (m_Placement.allowed.empty())
59 FATAL(
"Thread construction has no allowed online processor.");
69 placement = m_Placement;
76 m_LegacyUserCallbackPins == ~
size_t(0) ||
77 !m_Placement.allowed.contains(owner->logicalCpu()) ||
78 (m_AffinityPending && !m_RequestedAffinity.contains(owner->logicalCpu())))
80 ++m_LegacyUserCallbackPins;
84void Thread::unpinLegacyUserCallbacks() {
86 assert(m_LegacyUserCallbackPins);
87 --m_LegacyUserCallbackPins;
92 effective.intersect(Scheduler::onlineAffinity());
94 if (effective.empty())
95 return AffinityResult::Invalid;
99 bool rejected =
false;
100 bool parentPinned =
false;
101 bool threadPinned =
false;
103 auto progress = m_AffinityWaiters.acquire();
107 return AffinityResult::Terminal;
108 if (!m_Placement.migratable)
109 return AffinityResult::Pinned;
110 if (m_AffinityPending) {
111 generation = m_AffinityGeneration;
112 return AffinityResult::Busy;
116 if (m_LegacyUserCallbackPins && !effective.contains(owner->logicalCpu()))
117 return AffinityResult::Unsupported;
118 if (m_AffinityGeneration == ~uint64_t(0))
119 return AffinityResult::Invalid;
121 if (!m_AffinityWorkQueued) {
123 return AffinityResult::Terminal;
126 rejected = !threadPinned;
129 generation = ++m_AffinityGeneration;
130 m_RequestedAffinity = effective;
131 m_AffinityPending =
true;
132 __atomic_store_n(&m_AffinityReturnPending,
static_cast<size_t>(1), __ATOMIC_RELEASE);
133 if (!m_AffinityWorkQueued) {
134 m_AffinityWorkQueued =
true;
135 if (!owner->enqueueAffinity(
this)) {
136 m_AffinityWorkQueued =
false;
137 m_AffinityPending =
false;
138 __atomic_store_n(&m_AffinityReturnPending,
static_cast<size_t>(0), __ATOMIC_RELEASE);
150 return AffinityResult::Terminal;
153 return AffinityResult::Success;
158 return AffinityResult::Invalid;
161 auto progress = m_AffinityWaiters.acquire();
164 if (generation > m_AffinityGeneration)
165 return AffinityResult::Invalid;
166 if (generation <= m_AffinityCompleted)
167 return AffinityResult::Success;
168 if (!m_AffinityPending)
169 return AffinityResult::Terminal;
171 const auto reason = progress.waitForCompletion(
WaitQueue::Channel(
this), Thread::ProcessWait,
172 reinterpret_cast<uintptr_t
>(
this));
178 if (!affinityWorkPending()) {
182 return AffinityResult::Success;
188 FATAL(
"Affinity gate entered for a non-current Thread.");
194 bool rejected =
false;
195 bool parentPinned =
false;
196 bool threadPinned =
false;
197 bool finished =
false;
198 AffinityResult result = AffinityResult::Success;
202 auto progress = m_AffinityWaiters.acquire();
205 if (!m_AffinityGatePending) {
209 result = AffinityResult::Terminal;
211 }
else if (m_Placement.allowed.contains(owner->logicalCpu())) {
215 assert(!m_LegacyUserCallbackPins);
216 if (!finished && !m_AffinityWorkQueued) {
220 rejected = !threadPinned;
222 if (!finished && !rejected) {
223 m_AffinityGatePending =
true;
224 __atomic_store_n(&m_AffinityReturnPending,
static_cast<size_t>(1), __ATOMIC_RELEASE);
225 if (!m_AffinityWorkQueued) {
226 m_AffinityWorkQueued =
true;
227 if (!owner->enqueueAffinity(
this)) {
228 m_AffinityWorkQueued =
false;
229 m_AffinityGatePending =
false;
238 if (!finished && !rejected) {
247 reinterpret_cast<uintptr_t
>(
this),
true,
false);
261 return AffinityResult::Terminal;
267void Thread::publishReadyNotification() {
269 assert(m_ReadyPublicationPending);
270 __atomic_store_n(&m_ReadyPublicationPending,
false, __ATOMIC_RELEASE);
274 owner->m_pSchedulingAlgorithm->threadStatusChanged(
this);
278bool PerProcessorScheduler::enqueueAffinity(
Thread* thread,
bool accepted) {
280 if (!m_AffinityAdmissionOpen && !accepted)
282 assert(!thread->m_AffinityNext);
284 m_AffinityTail->m_AffinityNext = thread;
286 m_AffinityHead = thread;
287 m_AffinityTail = thread;
288 m_AffinityRequests += 1;
293void PerProcessorScheduler::prompt() {
295#if X86_COMMON && MULTIPROCESSOR
299 const uint8_t apicId = information->localApicId();
301 const bool submitted = Pc::instance().getLocalApic().interProcessorInterrupt(
302 apicId, IPI_RESCHEDULE_VECTOR, LocalApic::deliveryModeFixed,
true,
false);
308void PerProcessorScheduler::drainAffinityRequests() {
312 const size_t batch = m_AffinityRequests.value();
313 for (
size_t i = 0; i < batch; ++i) {
317 thread = m_AffinityHead;
320 m_AffinityHead = thread->m_AffinityNext;
322 m_AffinityTail =
nullptr;
323 thread->m_AffinityNext =
nullptr;
325#if PEDIGREE_AFFINITY_TESTS
326 const auto hook = __atomic_load_n(&g_AffinityCommitHook, __ATOMIC_ACQUIRE);
327 if (hook && thread == __atomic_load_n(&g_AffinityCommitTarget, __ATOMIC_ACQUIRE))
333 auto progress = thread->m_AffinityWaiters.acquire();
336 assert(thread->m_AffinityWorkQueued && thread->
getScheduler() ==
this);
338 const bool terminal = m_StopTimeAccountingWorker.value() || thread->
m_bShutdown ||
340 thread->
m_Status == Thread::AwaitingJoin ||
342 if (!terminal && !thread->m_HasSchedulerContext) {
346 assert(thread->
m_Status != Thread::Running);
347 if (thread->m_AffinityPending) {
348 assert(!thread->m_LegacyUserCallbackPins ||
349 thread->m_RequestedAffinity.contains(m_LogicalCpu));
350 thread->m_Placement.allowed = thread->m_RequestedAffinity;
351 thread->m_AffinityCompleted = thread->m_AffinityGeneration;
353 if (thread->m_AffinityGatePending) {
354 if (!thread->m_Placement.allowed.contains(m_LogicalCpu)) {
355 assert(!thread->m_LegacyUserCallbackPins);
356 for (
size_t cpu = 0; cpu < CpuAffinityMask::MaximumCpus; ++cpu) {
357 if (thread->m_Placement.allowed.contains(cpu)) {
358 destination = Scheduler::schedulerForCpu(cpu);
369 thread->m_AffinityPending =
false;
370 thread->m_AffinityGatePending =
false;
371 thread->m_AffinityWorkQueued =
false;
372 __atomic_store_n(&thread->m_AffinityReturnPending,
static_cast<size_t>(0),
379 m_AffinityRequests -= 1;
381 const bool queued = enqueueAffinity(thread,
true);
384 destination->prompt();
SchedulingAlgorithm * m_pSchedulingAlgorithm
void ringIrqWorkDoorbell()
bool beginExternalLease()
static ProcessorInformation & information()
static ProcessorInformation * informationAt(size_t cpu)
static void setInterrupts(bool bEnable)
static Scheduler & instance()
virtual void removeThread(Thread *pThread)=0
virtual void threadStatusChanged(Thread *pThread)=0
@ Continue
No unwind necessary, carry on as normal.
bool tryPinLegacyUserCallbacks()
AffinityResult waitAffinity(uint64_t generation)
void snapshotPlacementLocked(ThreadPlacement &placement) const
UnwindType getUnwindState()
class PerProcessorScheduler * getScheduler() const
AffinityResult requestAffinity(const CpuAffinityMask &mask, uint64_t &generation)
bool beginExternalLease()
AffinityResult completeAffinityAtSafePoint(bool *waited=nullptr)