There was a missing case in reschedule(). Also fix a copy/paste error in the power_saving logic. This also slightly optimizes things by having an unset CPU mask turn into a boolean and be processed separately, avoiding loops and masks entirely in that case. Have CPU masks be unset by default for new threads, while at it. Change-Id: Ic5d000a72839448a2d025cfc99de1ed49c841852 Reviewed-on: https://review.haiku-os.org/c/haiku/+/7900 Reviewed-by: waddlesplash <[email protected]>
338 lines
7.8 KiB
C++
338 lines
7.8 KiB
C++
/*
|
|
* Copyright 2013, Paweł Dziepak, [email protected].
|
|
* Distributed under the terms of the MIT License.
|
|
*/
|
|
|
|
#include "scheduler_thread.h"
|
|
|
|
|
|
using namespace Scheduler;
|
|
|
|
|
|
static bigtime_t sQuantumLengths[THREAD_MAX_SET_PRIORITY + 1];
|
|
|
|
const int32 kMaximumQuantumLengthsCount = 20;
|
|
static bigtime_t sMaximumQuantumLengths[kMaximumQuantumLengthsCount];
|
|
|
|
|
|
void
|
|
ThreadData::_InitBase()
|
|
{
|
|
fStolenTime = 0;
|
|
fQuantumStart = 0;
|
|
fLastInterruptTime = 0;
|
|
|
|
fWentSleep = 0;
|
|
fWentSleepActive = 0;
|
|
|
|
fEnqueued = false;
|
|
fReady = false;
|
|
|
|
fPriorityPenalty = 0;
|
|
fAdditionalPenalty = 0;
|
|
|
|
fEffectivePriority = GetPriority();
|
|
fBaseQuantum = sQuantumLengths[GetEffectivePriority()];
|
|
|
|
fTimeUsed = 0;
|
|
|
|
fMeasureAvailableActiveTime = 0;
|
|
fLastMeasureAvailableTime = 0;
|
|
fMeasureAvailableTime = 0;
|
|
}
|
|
|
|
|
|
inline CoreEntry*
|
|
ThreadData::_ChooseCore() const
|
|
{
|
|
SCHEDULER_ENTER_FUNCTION();
|
|
|
|
ASSERT(!gSingleCore);
|
|
return gCurrentMode->choose_core(this);
|
|
}
|
|
|
|
|
|
inline CPUEntry*
|
|
ThreadData::_ChooseCPU(CoreEntry* core, bool& rescheduleNeeded) const
|
|
{
|
|
SCHEDULER_ENTER_FUNCTION();
|
|
|
|
int32 threadPriority = GetEffectivePriority();
|
|
|
|
CPUSet mask = GetCPUMask();
|
|
const bool useMask = !mask.IsEmpty();
|
|
ASSERT(!useMask || mask.Matches(core->CPUMask()));
|
|
|
|
if (fThread->previous_cpu != NULL && !fThread->previous_cpu->disabled
|
|
&& (!useMask || mask.GetBit(fThread->previous_cpu->cpu_num))) {
|
|
CPUEntry* previousCPU
|
|
= CPUEntry::GetCPU(fThread->previous_cpu->cpu_num);
|
|
if (previousCPU->Core() == core) {
|
|
CoreCPUHeapLocker _(core);
|
|
if (CPUPriorityHeap::GetKey(previousCPU) < threadPriority) {
|
|
previousCPU->UpdatePriority(threadPriority);
|
|
rescheduleNeeded = true;
|
|
return previousCPU;
|
|
}
|
|
}
|
|
}
|
|
|
|
CoreCPUHeapLocker _(core);
|
|
int32 index = 0;
|
|
CPUEntry* cpu;
|
|
do {
|
|
cpu = core->CPUHeap()->PeekRoot(index++);
|
|
} while (useMask && cpu != NULL && !mask.GetBit(cpu->ID()));
|
|
ASSERT(cpu != NULL);
|
|
|
|
if (CPUPriorityHeap::GetKey(cpu) < threadPriority) {
|
|
cpu->UpdatePriority(threadPriority);
|
|
rescheduleNeeded = true;
|
|
} else
|
|
rescheduleNeeded = false;
|
|
|
|
return cpu;
|
|
}
|
|
|
|
|
|
ThreadData::ThreadData(Thread* thread)
|
|
:
|
|
fThread(thread)
|
|
{
|
|
}
|
|
|
|
|
|
void
|
|
ThreadData::Init()
|
|
{
|
|
_InitBase();
|
|
fCore = NULL;
|
|
|
|
Thread* currentThread = thread_get_current_thread();
|
|
ThreadData* currentThreadData = currentThread->scheduler_data;
|
|
fNeededLoad = currentThreadData->fNeededLoad;
|
|
|
|
if (!IsRealTime()) {
|
|
fPriorityPenalty = std::min(currentThreadData->fPriorityPenalty,
|
|
std::max(GetPriority() - _GetMinimalPriority(), int32(0)));
|
|
fAdditionalPenalty = currentThreadData->fAdditionalPenalty;
|
|
|
|
_ComputeEffectivePriority();
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
ThreadData::Init(CoreEntry* core)
|
|
{
|
|
_InitBase();
|
|
|
|
fCore = core;
|
|
fReady = true;
|
|
fNeededLoad = 0;
|
|
}
|
|
|
|
|
|
void
|
|
ThreadData::Dump() const
|
|
{
|
|
kprintf("\tpriority_penalty:\t%" B_PRId32 "\n", fPriorityPenalty);
|
|
|
|
int32 priority = GetPriority() - _GetPenalty();
|
|
priority = std::max(priority, int32(1));
|
|
kprintf("\tadditional_penalty:\t%" B_PRId32 " (%" B_PRId32 ")\n",
|
|
fAdditionalPenalty % priority, fAdditionalPenalty);
|
|
kprintf("\teffective_priority:\t%" B_PRId32 "\n", GetEffectivePriority());
|
|
|
|
kprintf("\ttime_used:\t\t%" B_PRId64 " us (quantum: %" B_PRId64 " us)\n",
|
|
fTimeUsed, ComputeQuantum());
|
|
kprintf("\tstolen_time:\t\t%" B_PRId64 " us\n", fStolenTime);
|
|
kprintf("\tquantum_start:\t\t%" B_PRId64 " us\n", fQuantumStart);
|
|
kprintf("\tneeded_load:\t\t%" B_PRId32 "%%\n", fNeededLoad / 10);
|
|
kprintf("\twent_sleep:\t\t%" B_PRId64 "\n", fWentSleep);
|
|
kprintf("\twent_sleep_active:\t%" B_PRId64 "\n", fWentSleepActive);
|
|
kprintf("\tcore:\t\t\t%" B_PRId32 "\n",
|
|
fCore != NULL ? fCore->ID() : -1);
|
|
if (fCore != NULL && HasCacheExpired())
|
|
kprintf("\tcache affinity has expired\n");
|
|
}
|
|
|
|
|
|
bool
|
|
ThreadData::ChooseCoreAndCPU(CoreEntry*& targetCore, CPUEntry*& targetCPU)
|
|
{
|
|
SCHEDULER_ENTER_FUNCTION();
|
|
|
|
bool rescheduleNeeded = false;
|
|
|
|
CPUSet mask = GetCPUMask();
|
|
const bool useMask = !mask.IsEmpty();
|
|
|
|
if (targetCore != NULL && (useMask && !targetCore->CPUMask().Matches(mask)))
|
|
targetCore = NULL;
|
|
if (targetCPU != NULL && (useMask && !mask.GetBit(targetCPU->ID())))
|
|
targetCPU = NULL;
|
|
|
|
if (targetCore == NULL && targetCPU != NULL)
|
|
targetCore = targetCPU->Core();
|
|
else if (targetCore != NULL && targetCPU == NULL)
|
|
targetCPU = _ChooseCPU(targetCore, rescheduleNeeded);
|
|
else if (targetCore == NULL && targetCPU == NULL) {
|
|
targetCore = _ChooseCore();
|
|
ASSERT(!useMask || mask.Matches(targetCore->CPUMask()));
|
|
targetCPU = _ChooseCPU(targetCore, rescheduleNeeded);
|
|
}
|
|
|
|
ASSERT(targetCore != NULL);
|
|
ASSERT(targetCPU != NULL);
|
|
|
|
if (fCore != targetCore) {
|
|
fLoadMeasurementEpoch = targetCore->LoadMeasurementEpoch() - 1;
|
|
if (fReady) {
|
|
if (fCore != NULL)
|
|
fCore->RemoveLoad(fNeededLoad, true);
|
|
targetCore->AddLoad(fNeededLoad, fLoadMeasurementEpoch, true);
|
|
}
|
|
}
|
|
|
|
fCore = targetCore;
|
|
return rescheduleNeeded;
|
|
}
|
|
|
|
|
|
bigtime_t
|
|
ThreadData::ComputeQuantum() const
|
|
{
|
|
SCHEDULER_ENTER_FUNCTION();
|
|
|
|
if (IsRealTime())
|
|
return fBaseQuantum;
|
|
|
|
int32 threadCount = fCore->ThreadCount();
|
|
if (fCore->CPUCount() > 0)
|
|
threadCount /= fCore->CPUCount();
|
|
|
|
bigtime_t quantum = fBaseQuantum;
|
|
if (threadCount < kMaximumQuantumLengthsCount)
|
|
quantum = std::min(sMaximumQuantumLengths[threadCount], quantum);
|
|
return quantum;
|
|
}
|
|
|
|
|
|
void
|
|
ThreadData::UnassignCore(bool running)
|
|
{
|
|
SCHEDULER_ENTER_FUNCTION();
|
|
|
|
ASSERT(fCore != NULL);
|
|
if (running || fThread->state == B_THREAD_READY)
|
|
fReady = false;
|
|
if (!fReady)
|
|
fCore = NULL;
|
|
}
|
|
|
|
|
|
/* static */ void
|
|
ThreadData::ComputeQuantumLengths()
|
|
{
|
|
SCHEDULER_ENTER_FUNCTION();
|
|
|
|
for (int32 priority = 0; priority <= THREAD_MAX_SET_PRIORITY; priority++) {
|
|
const bigtime_t kQuantum0 = gCurrentMode->base_quantum;
|
|
if (priority >= B_URGENT_DISPLAY_PRIORITY) {
|
|
sQuantumLengths[priority] = kQuantum0;
|
|
continue;
|
|
}
|
|
|
|
const bigtime_t kQuantum1
|
|
= kQuantum0 * gCurrentMode->quantum_multipliers[0];
|
|
if (priority > B_NORMAL_PRIORITY) {
|
|
sQuantumLengths[priority] = _ScaleQuantum(kQuantum1, kQuantum0,
|
|
B_URGENT_DISPLAY_PRIORITY, B_NORMAL_PRIORITY, priority);
|
|
continue;
|
|
}
|
|
|
|
const bigtime_t kQuantum2
|
|
= kQuantum0 * gCurrentMode->quantum_multipliers[1];
|
|
sQuantumLengths[priority] = _ScaleQuantum(kQuantum2, kQuantum1,
|
|
B_NORMAL_PRIORITY, B_IDLE_PRIORITY, priority);
|
|
}
|
|
|
|
for (int32 threadCount = 0; threadCount < kMaximumQuantumLengthsCount;
|
|
threadCount++) {
|
|
|
|
bigtime_t quantum = gCurrentMode->maximum_latency;
|
|
if (threadCount != 0)
|
|
quantum /= threadCount;
|
|
quantum = std::max(quantum, gCurrentMode->minimal_quantum);
|
|
sMaximumQuantumLengths[threadCount] = quantum;
|
|
}
|
|
}
|
|
|
|
|
|
inline int32
|
|
ThreadData::_GetPenalty() const
|
|
{
|
|
SCHEDULER_ENTER_FUNCTION();
|
|
return fPriorityPenalty;
|
|
}
|
|
|
|
|
|
void
|
|
ThreadData::_ComputeNeededLoad()
|
|
{
|
|
SCHEDULER_ENTER_FUNCTION();
|
|
ASSERT(!IsIdle());
|
|
|
|
int32 oldLoad = compute_load(fLastMeasureAvailableTime,
|
|
fMeasureAvailableActiveTime, fNeededLoad, fMeasureAvailableTime);
|
|
if (oldLoad < 0 || oldLoad == fNeededLoad)
|
|
return;
|
|
|
|
fCore->ChangeLoad(fNeededLoad - oldLoad);
|
|
}
|
|
|
|
|
|
void
|
|
ThreadData::_ComputeEffectivePriority() const
|
|
{
|
|
SCHEDULER_ENTER_FUNCTION();
|
|
|
|
if (IsIdle())
|
|
fEffectivePriority = B_IDLE_PRIORITY;
|
|
else if (IsRealTime())
|
|
fEffectivePriority = GetPriority();
|
|
else {
|
|
fEffectivePriority = GetPriority();
|
|
fEffectivePriority -= _GetPenalty();
|
|
if (fEffectivePriority > 0)
|
|
fEffectivePriority -= fAdditionalPenalty % fEffectivePriority;
|
|
|
|
ASSERT(fEffectivePriority < B_FIRST_REAL_TIME_PRIORITY);
|
|
ASSERT(fEffectivePriority >= B_LOWEST_ACTIVE_PRIORITY);
|
|
}
|
|
|
|
fBaseQuantum = sQuantumLengths[GetEffectivePriority()];
|
|
}
|
|
|
|
|
|
/* static */ bigtime_t
|
|
ThreadData::_ScaleQuantum(bigtime_t maxQuantum, bigtime_t minQuantum,
|
|
int32 maxPriority, int32 minPriority, int32 priority)
|
|
{
|
|
SCHEDULER_ENTER_FUNCTION();
|
|
|
|
ASSERT(priority <= maxPriority);
|
|
ASSERT(priority >= minPriority);
|
|
|
|
bigtime_t result = (maxQuantum - minQuantum) * (priority - minPriority);
|
|
result /= maxPriority - minPriority;
|
|
return maxQuantum - result;
|
|
}
|
|
|
|
|
|
ThreadProcessing::~ThreadProcessing()
|
|
{
|
|
}
|
|
|