* Copyright 2013, Paweł Dziepak, pdziepak@quarnos.org.
* Distributed under the terms of the MIT License.
*/
#include <util/AutoLock.h>
#include "scheduler_common.h"
#include "scheduler_cpu.h"
#include "scheduler_modes.h"
#include "scheduler_profiler.h"
#include "scheduler_thread.h"
using namespace Scheduler;
const bigtime_t kCacheExpire = 100000;
static void
switch_to_mode()
{
}
static void
set_cpu_enabled(int32 , bool )
{
}
static bool
has_cache_expired(const ThreadData* threadData)
{
SCHEDULER_ENTER_FUNCTION();
if (threadData->WentSleepActive() == 0)
return false;
CoreEntry* core = threadData->Core();
bigtime_t activeTime = core->GetActiveTime();
return activeTime - threadData->WentSleepActive() > kCacheExpire;
}
static CoreEntry*
choose_core(const ThreadData* threadData)
{
SCHEDULER_ENTER_FUNCTION();
PackageEntry* package = gIdlePackageList.Last();
if (package == NULL) {
package = PackageEntry::GetMostIdlePackage();
}
int32 index = 0;
CPUSet mask = threadData->GetCPUMask();
const bool useMask = !mask.IsEmpty();
CoreEntry* core = NULL;
if (package != NULL) {
do {
core = package->GetIdleCore(index++);
} while (useMask && core != NULL && !core->CPUMask().Matches(mask));
}
if (core == NULL) {
ReadSpinLocker coreLocker(gCoreHeapsLock);
index = 0;
do {
core = gCoreLoadHeap.PeekMinimum(index++);
} while (useMask && core != NULL && !core->CPUMask().Matches(mask));
if (core == NULL) {
index = 0;
do {
core = gCoreHighLoadHeap.PeekMinimum(index++);
} while (useMask && core != NULL && !core->CPUMask().Matches(mask));
}
}
ASSERT(core != NULL);
return core;
}
static CoreEntry*
rebalance(const ThreadData* threadData)
{
SCHEDULER_ENTER_FUNCTION();
CoreEntry* core = threadData->Core();
ASSERT(core != NULL);
ReadSpinLocker coreLocker(gCoreHeapsLock);
CPUSet mask = threadData->GetCPUMask();
const bool useMask = !mask.IsEmpty();
int32 index = 0;
CoreEntry* other;
do {
other = gCoreLoadHeap.PeekMinimum(index++);
if (other != NULL && (useMask && other->CPUMask().IsEmpty()))
panic("other->CPUMask().IsEmpty()\n");
} while (useMask && other != NULL && !other->CPUMask().Matches(mask));
if (other == NULL) {
index = 0;
do {
other = gCoreHighLoadHeap.PeekMinimum(index++);
} while (useMask && other != NULL && !other->CPUMask().Matches(mask));
}
coreLocker.Unlock();
ASSERT(other != NULL);
int32 coreLoad = core->GetLoad();
int32 otherLoad = other->GetLoad();
if (other == core || otherLoad + kLoadDifference >= coreLoad)
return core;
int32 difference = coreLoad - otherLoad - kLoadDifference;
ASSERT(difference > 0);
int32 threadLoad = threadData->GetLoad() / core->CPUCount();
return difference >= threadLoad ? other : core;
}
static void
rebalance_irqs(bool idle)
{
SCHEDULER_ENTER_FUNCTION();
if (idle)
return;
cpu_ent* cpu = get_cpu_struct();
SpinLocker locker(cpu->irqs_lock);
irq_assignment* chosen = NULL;
irq_assignment* irq = (irq_assignment*)list_get_first_item(&cpu->irqs);
int32 totalLoad = 0;
while (irq != NULL) {
if (chosen == NULL || chosen->load < irq->load)
chosen = irq;
totalLoad += irq->load;
irq = (irq_assignment*)list_get_next_item(&cpu->irqs, irq);
}
locker.Unlock();
if (chosen == NULL || totalLoad < kLowLoad)
return;
ReadSpinLocker coreLocker(gCoreHeapsLock);
CoreEntry* other = gCoreLoadHeap.PeekMinimum();
if (other == NULL)
other = gCoreHighLoadHeap.PeekMinimum();
coreLocker.Unlock();
int32 newCPU = other->CPUHeap()->PeekRoot()->ID();
ASSERT(other != NULL);
CoreEntry* core = CoreEntry::GetCore(cpu->cpu_num);
if (other == core)
return;
if (other->GetLoad() + kLoadDifference >= core->GetLoad())
return;
assign_io_interrupt_to_cpu(chosen->irq, newCPU);
}
scheduler_mode_operations gSchedulerLowLatencyMode = {
"low latency",
1000,
100,
{ 2, 5 },
5000,
switch_to_mode,
set_cpu_enabled,
has_cache_expired,
choose_core,
rebalance,
rebalance_irqs,
};