The Pedigree Project 0.1
Scheduler.cc
1/*
2 * Copyright (c) 2008-2014, Pedigree Developers
3 *
4 * Please see the CONTRIB file in the root of the source tree for a full
5 * list of contributors.
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20#include <config.h>
21
22#if THREADS
23
24#include "pedigree/kernel/LockGuard.h"
25#include "pedigree/kernel/Log.h"
26#include "pedigree/kernel/Metrics.h"
27#include "pedigree/kernel/process/PerProcessorScheduler.h"
28#include "pedigree/kernel/process/Process.h"
29#include "pedigree/kernel/process/ProcessorThreadAllocator.h"
30#include "pedigree/kernel/process/RoundRobinCoreAllocator.h"
31#include "pedigree/kernel/process/Scheduler.h"
32#include "pedigree/kernel/processor/Processor.h"
33#include "pedigree/kernel/processor/ProcessorInformation.h"
34#include "pedigree/kernel/utilities/Iterator.h"
35#include "pedigree/kernel/utilities/Vector.h"
36#include "pedigree/kernel/utilities/assert.h"
37#include "pedigree/kernel/utilities/utility.h"
38
40
41#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
42namespace {
43Scheduler::GenericThreadStatusHook g_GenericThreadStatusHook = nullptr;
44}
45#endif
46
47// Scheduler can be used at times where it is not yet safe to do the useful
48// "safer" Spinlock deadlock detection.
49#define SCHEDULER_HAS_SAFE_SPINLOCKS true
50
51// Do we allow recursing in the Scheduler lock? Note that the lock surrounds
52// memory operations (editing a List<T>), so if e.g. VirtualAddressSpace depends
53// on Scheduler, you need to recurse.
54#define SCHEDULER_HAS_RECURSIVE_SPINLOCKS true
55
56Scheduler::ProcessLease::ProcessLease() : m_pProcess(nullptr), m_TerminationDeferral(false) {}
57
58Scheduler::ProcessLease::ProcessLease(Process* process)
59 : m_pProcess(process), m_TerminationDeferral(process != nullptr) {}
60
61Scheduler::ProcessLease::ProcessLease(ProcessLease&& other) noexcept
62 : m_pProcess(other.m_pProcess),
63 m_TerminationDeferral(pedigree_std::move(other.m_TerminationDeferral)) {
64 other.m_pProcess = nullptr;
65}
66
67Scheduler::ProcessLease::~ProcessLease() {
68 reset();
69}
70
71Scheduler::ProcessLease& Scheduler::ProcessLease::operator=(ProcessLease&& other) noexcept {
72 if (this != &other) {
73 if (other.m_pProcess) {
74 m_TerminationDeferral = pedigree_std::move(other.m_TerminationDeferral);
75
76 Process* previous = m_pProcess;
77 m_pProcess = other.m_pProcess;
78 other.m_pProcess = nullptr;
79 if (previous) {
81 }
82 } else {
83 reset();
84 }
85 }
86 return *this;
87}
88
89void Scheduler::ProcessLease::reset() {
90 Process* process = m_pProcess;
91 m_pProcess = nullptr;
92 if (process) {
94 }
95 m_TerminationDeferral = TerminationDeferral(false);
96}
97
98Scheduler::Scheduler()
99 : m_ActivityLock(),
100 m_LoadAverage(),
101 m_UserNanoseconds(0),
102 m_KernelNanoseconds(0),
103 m_IdleNanoseconds(0),
104 m_Processes(),
105 m_NextPid(0),
106 m_PTMap(),
107 m_TPMap(),
110 m_SchedulerLock(false),
112
113bool Scheduler::initialise(Process* pKernelProcess) {
114 if (Processor::getCount() > CpuAffinityMask::MaximumCpus)
115 FATAL("Processor topology exceeds the affinity mask capacity.");
117 ProcessorThreadAllocator::instance().setAlgorithm(pRoundRobin);
118
119 m_pKernelProcess = pKernelProcess;
120
122
123 m_pBspScheduler = &Processor::information().getScheduler();
124 procList.pushBack(m_pBspScheduler);
125
126 size_t i = 0;
128 it != Processor::m_ProcessorInformation.end(); it++, i += 2) {
129 auto thisScheduler = &((*it)->getScheduler());
130 if (thisScheduler != m_pBspScheduler) {
131 procList.pushBack(thisScheduler);
132 }
133 }
134
135 pRoundRobin->initialise(procList);
136
137 return true;
138}
139
141 m_SchedulerLock.acquire(SCHEDULER_HAS_RECURSIVE_SPINLOCKS, SCHEDULER_HAS_SAFE_SPINLOCKS);
142 m_TPMap.insert(pThread, &PPSched);
143 pThread->setScheduler(&PPSched);
144 if (!pThread->m_Placement.migratable) {
145 pThread->m_Placement.allowed = CpuAffinityMask();
146 const size_t cpu = PPSched.logicalCpu();
147 pThread->m_Placement.allowed.set(cpu == ~size_t(0) ? Processor::index() : cpu);
148 }
150}
151
153 m_SchedulerLock.acquire(SCHEDULER_HAS_RECURSIVE_SPINLOCKS, SCHEDULER_HAS_SAFE_SPINLOCKS);
154 PerProcessorScheduler* pPpSched = m_TPMap.lookup(pThread);
155 if (pPpSched) {
156 pPpSched->removeThread(pThread);
157 m_TPMap.remove(pThread);
158 }
160}
161
163 m_SchedulerLock.acquire(SCHEDULER_HAS_RECURSIVE_SPINLOCKS, SCHEDULER_HAS_SAFE_SPINLOCKS);
164 PerProcessorScheduler* pPpSched = m_TPMap.lookup(pThread);
166 return pPpSched != 0;
167}
168
169bool Scheduler::hasActiveSyscallLocked(Service_t service) const {
170 for (auto it = m_TPMap.begin(); it != m_TPMap.end(); ++it) {
171 if (__atomic_load_n(&it.key()->m_ActiveSyscalls[service], __ATOMIC_SEQ_CST)) {
172 return true;
173 }
174 }
175 return false;
176}
177
179 const size_t id = (m_NextPid += 1) - 1;
180 // Linux robust owner words reserve the upper two bits. IDs are never
181 // recycled, so exhaustion cannot silently alias an older owner.
182 assert(id < 0x3FFFFFFF);
183 return id;
184}
185
187 process->endExternalLease();
188}
189
191 // Reserve outside m_SchedulerLock. A worker generation is snapshotted
192 // before this call, so a Process added after the count can only publish a
193 // later generation and will be covered by the next pass.
195
196 m_SchedulerLock.acquire(SCHEDULER_HAS_RECURSIVE_SPINLOCKS, SCHEDULER_HAS_SAFE_SPINLOCKS);
198 it != m_Processes.end() && processes.count() < processes.size(); ++it) {
199 Process* process = *it;
200 if (process->beginExternalLease()) {
201 processes.pushBack(process);
202 }
203 }
205
206 for (Vector<Process*>::Iterator it = processes.begin(); it != processes.end(); ++it) {
207 Process* process = *it;
209 process->endExternalLease();
210 }
211}
212
214 m_SchedulerLock.acquire(SCHEDULER_HAS_RECURSIVE_SPINLOCKS, SCHEDULER_HAS_SAFE_SPINLOCKS);
215 m_Processes.pushBack(pProcess);
217}
218
220 bool removed = false;
221 m_SchedulerLock.acquire(SCHEDULER_HAS_RECURSIVE_SPINLOCKS, SCHEDULER_HAS_SAFE_SPINLOCKS);
222 pProcess->closeExternalLeaseAdmission();
223 for (List<Process*>::Iterator it = m_Processes.begin(); it != m_Processes.end(); it++) {
224 if (*it == pProcess) {
225 m_Processes.erase(it);
226 removed = true;
227 break;
228 }
229 }
231 if (removed) {
232 m_ProcessRemovalWaiters.wakeAll(WaitQueue::WakeReason::Signalled, WaitQueue::Channel(pProcess));
233 }
234}
235
237 Metrics::increment(Metrics::Counter::Yield);
238 Processor::information().getScheduler().schedule();
239}
240
241CpuAffinityMask Scheduler::onlineAffinity() {
242 CpuAffinityMask mask;
243 const size_t count = Processor::getCount();
244 assert(count <= CpuAffinityMask::MaximumCpus);
245 for (size_t cpu = 0; cpu < count; ++cpu)
246 mask.set(cpu);
247 return mask;
248}
249
250size_t Scheduler::affinityBytes() {
251 return ((Processor::getCount() + CpuAffinityMask::WordBits - 1) / CpuAffinityMask::WordBits) *
252 sizeof(unsigned long);
253}
254
255PerProcessorScheduler* Scheduler::schedulerForCpu(size_t cpu) {
256 ProcessorInformation* information = Processor::informationAt(cpu);
257 return information ? &information->getScheduler() : nullptr;
258}
259
260void Scheduler::rebindThread(Thread* thread, PerProcessorScheduler& scheduler) {
262 assert(m_TPMap.lookup(thread));
263 m_TPMap.insert(thread, &scheduler);
264 thread->setScheduler(&scheduler);
265 thread->setCpuId(scheduler.m_PhysicalCpu);
266}
267
269 m_SchedulerLock.acquire(SCHEDULER_HAS_RECURSIVE_SPINLOCKS, SCHEDULER_HAS_SAFE_SPINLOCKS);
270 size_t result = m_Processes.count();
272 return result;
273}
274
276 m_SchedulerLock.acquire(SCHEDULER_HAS_RECURSIVE_SPINLOCKS, SCHEDULER_HAS_SAFE_SPINLOCKS);
277 if (n >= m_Processes.count()) {
279 lease.reset();
280 return false;
281 }
282
283 size_t i = 0;
284 Process* pResult = 0;
285 for (List<Process*>::Iterator it = m_Processes.begin(); it != m_Processes.end(); it++) {
286 if (i == n) {
287 pResult = *it;
288 break;
289 }
290 i++;
291 }
292
293 if (pResult && !pResult->beginExternalLease()) {
294 pResult = nullptr;
295 }
297 lease = ProcessLease(pResult);
298 return pResult != nullptr;
299}
300
302 m_SchedulerLock.acquire(SCHEDULER_HAS_RECURSIVE_SPINLOCKS, SCHEDULER_HAS_SAFE_SPINLOCKS);
303 Process* pResult = nullptr;
304 for (List<Process*>::Iterator it = m_Processes.begin(); it != m_Processes.end(); ++it) {
305 Process* candidate = *it;
306 if (candidate->getType() == type && candidate->beginExternalLease()) {
307 pResult = candidate;
308 break;
309 }
310 }
312
313 lease = ProcessLease(pResult);
314 return pResult != nullptr;
315}
316
318 if (!expected) {
319 lease.reset();
320 return false;
321 }
322
323 m_SchedulerLock.acquire(SCHEDULER_HAS_RECURSIVE_SPINLOCKS, SCHEDULER_HAS_SAFE_SPINLOCKS);
324 Process* pResult = nullptr;
325 for (List<Process*>::Iterator it = m_Processes.begin(); it != m_Processes.end(); ++it) {
326 if (*it == expected) {
327 pResult = *it;
328 break;
329 }
330 }
331
332 if (pResult && !pResult->beginExternalLease()) {
333 pResult = nullptr;
334 }
336 lease = ProcessLease(pResult);
337 return pResult != nullptr;
338}
339
341 m_SchedulerLock.acquire(SCHEDULER_HAS_RECURSIVE_SPINLOCKS, SCHEDULER_HAS_SAFE_SPINLOCKS);
342 Process* pResult = nullptr;
343 for (List<Process*>::Iterator it = m_Processes.begin(); it != m_Processes.end(); ++it) {
344 Process* candidate = *it;
345 if (candidate->getId() == id) {
346 pResult = candidate;
347 break;
348 }
349 }
350
351 if (pResult && !pResult->beginExternalLease()) {
352 pResult = nullptr;
353 }
355 lease = ProcessLease(pResult);
356 return pResult != nullptr;
357}
358
359bool Scheduler::acquireProcessByUserspaceId(ProcessLease& lease, size_t id,
360 const UserspacePidNamespace* space) {
361 auto* current = Processor::information().getCurrentThread();
362 auto owner = current && current->getParent()->pidNamespace()
363 ? current->getParent()->pidNamespace()
364 : Process::rootPidNamespace();
365 if (!space) {
366 space = owner.get();
367 }
368 m_SchedulerLock.acquire(SCHEDULER_HAS_RECURSIVE_SPINLOCKS, SCHEDULER_HAS_SAFE_SPINLOCKS);
369 Process* pResult = nullptr;
370 for (List<Process*>::Iterator it = m_Processes.begin(); it != m_Processes.end(); ++it) {
371 Process* candidate = *it;
372 if (id && candidate->getUserspaceId(space) == id) {
373 pResult = candidate;
374 break;
375 }
376 }
377
378 if (pResult && !pResult->beginExternalLease()) {
379 pResult = nullptr;
380 }
382 lease = ProcessLease(pResult);
383 return pResult != nullptr;
384}
385
386bool Scheduler::acquireNextProcess(ProcessLease& lease, size_t afterId) {
387 lease.reset();
388 m_SchedulerLock.acquire(SCHEDULER_HAS_RECURSIVE_SPINLOCKS, SCHEDULER_HAS_SAFE_SPINLOCKS);
389 Process* result = nullptr;
390 while (true) {
391 for (Process* candidate : m_Processes) {
392 if (candidate->getId() > afterId && (!result || candidate->getId() < result->getId())) {
393 result = candidate;
394 }
395 }
396 if (!result || result->beginExternalLease()) {
397 break;
398 }
399 afterId = result->getId();
400 result = nullptr;
401 }
403 lease = ProcessLease(result);
404 return result != nullptr;
405}
406
407bool Scheduler::acquireThreadByUserspaceId(Process::ThreadLease& lease, size_t id,
408 const UserspacePidNamespace* space) {
409 lease.reset();
410 auto* current = Processor::information().getCurrentThread();
411 auto owner = current && current->getParent()->pidNamespace()
412 ? current->getParent()->pidNamespace()
413 : Process::rootPidNamespace();
414 if (!space) {
415 space = owner.get();
416 }
417 size_t after = 0;
418 ProcessLease process;
419 while (acquireNextProcess(process, after)) {
420 after = process->getId();
421 if (process->getUserspaceId(space) && process->acquireThreadByUserspaceId(lease, id, space)) {
422 return true;
423 }
424 }
425 return false;
426}
427
429 lease.reset();
430 size_t afterId = 0;
431 while (afterId < id) {
432 Process* candidate = nullptr;
433 m_SchedulerLock.acquire(SCHEDULER_HAS_RECURSIVE_SPINLOCKS, SCHEDULER_HAS_SAFE_SPINLOCKS);
434 for (List<Process*>::Iterator it = m_Processes.begin(); it != m_Processes.end(); ++it) {
435 Process* process = *it;
436 const size_t processId = process->getId();
437 if (processId > afterId && processId <= id &&
438 (!candidate || processId < candidate->getId())) {
439 candidate = process;
440 }
441 }
442 if (candidate) {
443 afterId = candidate->getId();
444 if (!candidate->beginExternalLease()) {
445 candidate = nullptr;
446 }
447 } else {
448 afterId = id;
449 }
451
452 // A numeric cursor cannot skip a surviving process when a preceding
453 // process disappears. Its lease bridges the two independent locks.
454 ProcessLease process(candidate);
455 if (process && process->acquireThreadByTaskId(lease, id)) {
456 return true;
457 }
458 }
459 return false;
460}
461
463 TerminationDeferral terminationDeferral;
464 while (true) {
465 auto guard = m_ProcessRemovalWaiters.acquire();
466 bool present = false;
467 m_SchedulerLock.acquire(SCHEDULER_HAS_RECURSIVE_SPINLOCKS, SCHEDULER_HAS_SAFE_SPINLOCKS);
468 for (List<Process*>::Iterator it = m_Processes.begin(); it != m_Processes.end(); ++it) {
469 if (*it == expected) {
470 present = true;
471 break;
472 }
473 }
475 if (!present) {
476 return;
477 }
478
479 const WaitQueue::WakeReason reason = guard.waitForCompletion(
480 WaitQueue::Channel(expected), Thread::ProcessWait, reinterpret_cast<uintptr_t>(expected));
481 (void)reason;
482 }
483}
484
486 m_SchedulerLock.acquire(SCHEDULER_HAS_RECURSIVE_SPINLOCKS, SCHEDULER_HAS_SAFE_SPINLOCKS);
487 size_t childIndex = 0;
488 Process* pResult = 0;
489 for (List<Process*>::Iterator it = m_Processes.begin(); it != m_Processes.end(); ++it) {
490 Process* pProcess = *it;
491 if (pProcess && pProcess->getParent() == pParent) {
492 if (childIndex == n) {
493 pResult = pProcess;
494 break;
495 }
496 ++childIndex;
497 }
498 }
500 return pResult;
501}
502
503void Scheduler::threadStatusChanged(Thread* pThread) {
504#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
505 GenericThreadStatusHook hook = __atomic_load_n(&g_GenericThreadStatusHook, __ATOMIC_ACQUIRE);
506 if (hook) {
507 hook(pThread);
508 }
509#endif
510 PerProcessorScheduler* pSched = pThread->getScheduler();
511 assert(pSched);
512
513 pSched->threadStatusChanged(pThread);
514}
515
516#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
517void Scheduler::setGenericThreadStatusHook(GenericThreadStatusHook hook) {
518 __atomic_store_n(&g_GenericThreadStatusHook, hook, __ATOMIC_RELEASE);
519}
520#endif
521
522#endif
Definition List.h:61
Iterator begin()
Definition List.h:122
::Iterator< T, node_t > Iterator
Definition List.h:67
Iterator end()
Definition List.h:132
size_t getUserspaceId() const
Definition Process.h:504
void endExternalLease()
Definition Process.cc:1210
size_t getId()
Definition Process.h:499
bool beginExternalLease()
Definition Process.cc:1200
void closeExternalLeaseAdmission()
Definition Process.cc:1273
Process * getParent()
Definition Process.h:620
void drainDeferredTimeAccounting()
Definition Process.cc:925
ProcessType
Definition Process.h:296
static ProcessorInformation & information()
static size_t getCount()
static Vector< ProcessorInformation * > m_ProcessorInformation
Definition Processor.h:512
static ProcessorInformation * informationAt(size_t cpu)
Definition Processor.cc:39
static size_t index()
void setAlgorithm(ThreadToCoreAllocationAlgorithm *pAlgorithm)
Sets the algorithm to use for allocating threads to cores.
This class manages how processes and threads are scheduled across processors.
Definition Scheduler.h:50
MUST_USE_RESULT bool acquireFirstProcessOfType(ProcessLease &lease, Process::ProcessType type)
Definition Scheduler.cc:301
static Scheduler m_Instance
Definition Scheduler.h:241
List< Process *, 0 > m_Processes
Definition Scheduler.h:244
bool hasActiveSyscallLocked(Service_t service) const
Definition Scheduler.cc:169
Process * getChildProcess(Process *pParent, size_t n)
Definition Scheduler.cc:485
void drainDeferredTimeAccounting()
Definition Scheduler.cc:190
void releaseProcessLease(Process *process)
Definition Scheduler.cc:186
MUST_USE_RESULT bool acquireProcessById(ProcessLease &lease, size_t id)
Definition Scheduler.cc:340
void removeThread(Thread *pThread)
Definition Scheduler.cc:152
Spinlock m_SchedulerLock
Definition Scheduler.h:268
static Scheduler & instance()
Definition Scheduler.h:96
size_t reserveProcessId()
Definition Scheduler.cc:178
size_t getNumProcesses()
Definition Scheduler.cc:268
bool initialise(Process *pKernelProcess)
Definition Scheduler.cc:113
MUST_USE_RESULT bool acquireProcess(ProcessLease &lease, size_t n)
Definition Scheduler.cc:275
Atomic< size_t > m_NextPid
Definition Scheduler.h:247
void waitUntilProcessRemoved(Process *expected)
Definition Scheduler.cc:462
bool threadInSchedule(Thread *pThread)
Definition Scheduler.cc:162
MUST_USE_RESULT bool acquireThreadByTaskId(Process::ThreadLease &lease, size_t id)
Definition Scheduler.cc:428
void addThread(Thread *pThread, PerProcessorScheduler &PPSched)
Definition Scheduler.cc:140
Tree< PerProcessorScheduler *, List< Thread * > * > m_PTMap
Definition Scheduler.h:250
Tree< Thread *, PerProcessorScheduler * > m_TPMap
Definition Scheduler.h:253
WaitQueue m_ProcessRemovalWaiters
Definition Scheduler.h:272
Process * m_pKernelProcess
Definition Scheduler.h:256
void addProcess(Process *pProcess)
Definition Scheduler.cc:213
void yield()
Definition Scheduler.cc:236
PerProcessorScheduler * m_pBspScheduler
Definition Scheduler.h:265
void removeProcess(Process *pProcess)
Definition Scheduler.cc:219
void release()
Definition Spinlock.cc:168
bool acquire(bool recurse=false, bool safe=true)
Definition Spinlock.cc:36
void setScheduler(class PerProcessorScheduler *pScheduler)
Definition Thread.cc:3531
void setCpuId(size_t id)
Definition Thread.h:870
class PerProcessorScheduler * getScheduler() const
Definition Thread.h:929
Iterator begin()
Definition Tree.h:402
void remove(const K &key)
Definition Tree.h:301
E lookup(const K &key) const
Definition Tree.h:193
void insert(const K &key, const E &value)
Definition Tree.h:149
Iterator end()
Definition Tree.h:427
A vector / dynamic array.
Definition Vector.h:33
Iterator end()
Definition Vector.h:172
Iterator begin()
Definition Vector.h:162
Iterator erase(Iterator &Iter)
Definition List.h:352
size_t count() const
Definition List.h:212
void pushBack(const T &value)
Definition List.h:216
void pushBack(const T &value)
Definition Vector.h:275
size_t size() const
Definition Vector.h:265
size_t count() const
Definition Vector.h:270