1130803Smarcel/*
2130803Smarcel * Copyright 2013, Pawe�� Dziepak, pdziepak@quarnos.org.
3130803Smarcel * Distributed under the terms of the MIT License.
4130803Smarcel */
5130803Smarcel
6130803Smarcel
7130803Smarcel#include <util/AutoLock.h>
8130803Smarcel
9130803Smarcel#include "scheduler_common.h"
10130803Smarcel#include "scheduler_cpu.h"
11130803Smarcel#include "scheduler_modes.h"
12130803Smarcel#include "scheduler_profiler.h"
13130803Smarcel#include "scheduler_thread.h"
14130803Smarcel
15130803Smarcel
16130803Smarcelusing namespace Scheduler;
17130803Smarcel
18130803Smarcel
19130803Smarcelconst bigtime_t kCacheExpire = 100000;
20130803Smarcel
21130803Smarcel
22130803Smarcelstatic void
23130803Smarcelswitch_to_mode()
24130803Smarcel{
25130803Smarcel}
26130803Smarcel
27130803Smarcel
28130803Smarcelstatic void
29130803Smarcelset_cpu_enabled(int32 /* cpu */, bool /* enabled */)
30130803Smarcel{
31130803Smarcel}
32130803Smarcel
33130803Smarcel
34130803Smarcelstatic bool
35130803Smarcelhas_cache_expired(const ThreadData* threadData)
36130803Smarcel{
37130803Smarcel	SCHEDULER_ENTER_FUNCTION();
38130803Smarcel	if (threadData->WentSleepActive() == 0)
39130803Smarcel		return false;
40130803Smarcel	CoreEntry* core = threadData->Core();
41130803Smarcel	bigtime_t activeTime = core->GetActiveTime();
42130803Smarcel	return activeTime - threadData->WentSleepActive() > kCacheExpire;
43130803Smarcel}
44130803Smarcel
45130803Smarcel
46130803Smarcelstatic CoreEntry*
47130803Smarcelchoose_core(const ThreadData* /* threadData */)
48130803Smarcel{
49130803Smarcel	SCHEDULER_ENTER_FUNCTION();
50130803Smarcel
51130803Smarcel	// wake new package
52130803Smarcel	PackageEntry* package = gIdlePackageList.Last();
53130803Smarcel	if (package == NULL) {
54130803Smarcel		// wake new core
55130803Smarcel		package = PackageEntry::GetMostIdlePackage();
56130803Smarcel	}
57130803Smarcel
58130803Smarcel	CoreEntry* core = NULL;
59130803Smarcel	if (package != NULL)
60130803Smarcel		core = package->GetIdleCore();
61130803Smarcel
62130803Smarcel	if (core == NULL) {
63130803Smarcel		ReadSpinLocker coreLocker(gCoreHeapsLock);
64130803Smarcel		// no idle cores, use least occupied core
65130803Smarcel		core = gCoreLoadHeap.PeekMinimum();
66130803Smarcel		if (core == NULL)
67130803Smarcel			core = gCoreHighLoadHeap.PeekMinimum();
68130803Smarcel	}
69130803Smarcel
70130803Smarcel	ASSERT(core != NULL);
71130803Smarcel	return core;
72130803Smarcel}
73130803Smarcel
74130803Smarcel
75130803Smarcelstatic CoreEntry*
76130803Smarcelrebalance(const ThreadData* threadData)
77130803Smarcel{
78130803Smarcel	SCHEDULER_ENTER_FUNCTION();
79130803Smarcel
80130803Smarcel	CoreEntry* core = threadData->Core();
81130803Smarcel	ASSERT(core != NULL);
82130803Smarcel
83130803Smarcel	// Get the least loaded core.
84130803Smarcel	ReadSpinLocker coreLocker(gCoreHeapsLock);
85130803Smarcel	CoreEntry* other = gCoreLoadHeap.PeekMinimum();
86130803Smarcel	if (other == NULL)
87130803Smarcel		other = gCoreHighLoadHeap.PeekMinimum();
88130803Smarcel	coreLocker.Unlock();
89130803Smarcel	ASSERT(other != NULL);
90130803Smarcel
91130803Smarcel	// Check if the least loaded core is significantly less loaded than
92130803Smarcel	// the current one.
93130803Smarcel	int32 coreLoad = core->GetLoad();
94130803Smarcel	int32 otherLoad = other->GetLoad();
95130803Smarcel	if (other == core || otherLoad + kLoadDifference >= coreLoad)
96130803Smarcel		return core;
97130803Smarcel
98130803Smarcel	// Check whether migrating the current thread would result in both core
99130803Smarcel	// loads become closer to the average.
100130803Smarcel	int32 difference = coreLoad - otherLoad - kLoadDifference;
101130803Smarcel	ASSERT(difference > 0);
102130803Smarcel
103130803Smarcel	int32 threadLoad = threadData->GetLoad() / core->CPUCount();
104130803Smarcel	return difference >= threadLoad ? other : core;
105130803Smarcel}
106130803Smarcel
107130803Smarcel
108130803Smarcelstatic void
109130803Smarcelrebalance_irqs(bool idle)
110130803Smarcel{
111130803Smarcel	SCHEDULER_ENTER_FUNCTION();
112130803Smarcel
113130803Smarcel	if (idle)
114130803Smarcel		return;
115130803Smarcel
116130803Smarcel	cpu_ent* cpu = get_cpu_struct();
117130803Smarcel	SpinLocker locker(cpu->irqs_lock);
118130803Smarcel
119130803Smarcel	irq_assignment* chosen = NULL;
120130803Smarcel	irq_assignment* irq = (irq_assignment*)list_get_first_item(&cpu->irqs);
121130803Smarcel
122130803Smarcel	int32 totalLoad = 0;
123130803Smarcel	while (irq != NULL) {
124130803Smarcel		if (chosen == NULL || chosen->load < irq->load)
125130803Smarcel			chosen = irq;
126130803Smarcel		totalLoad += irq->load;
127130803Smarcel		irq = (irq_assignment*)list_get_next_item(&cpu->irqs, irq);
128130803Smarcel	}
129130803Smarcel
130130803Smarcel	locker.Unlock();
131130803Smarcel
132130803Smarcel	if (chosen == NULL || totalLoad < kLowLoad)
133130803Smarcel		return;
134130803Smarcel
135130803Smarcel	ReadSpinLocker coreLocker(gCoreHeapsLock);
136130803Smarcel	CoreEntry* other = gCoreLoadHeap.PeekMinimum();
137130803Smarcel	if (other == NULL)
138130803Smarcel		other = gCoreHighLoadHeap.PeekMinimum();
139130803Smarcel	coreLocker.Unlock();
140130803Smarcel
141130803Smarcel	int32 newCPU = other->CPUHeap()->PeekRoot()->ID();
142130803Smarcel
143130803Smarcel	ASSERT(other != NULL);
144130803Smarcel
145130803Smarcel	CoreEntry* core = CoreEntry::GetCore(cpu->cpu_num);
146130803Smarcel	if (other == core)
147130803Smarcel		return;
148130803Smarcel	if (other->GetLoad() + kLoadDifference >= core->GetLoad())
149130803Smarcel		return;
150130803Smarcel
151130803Smarcel	assign_io_interrupt_to_cpu(chosen->irq, newCPU);
152130803Smarcel}
153130803Smarcel
154130803Smarcel
155130803Smarcelscheduler_mode_operations gSchedulerLowLatencyMode = {
156130803Smarcel	"low latency",
157130803Smarcel
158130803Smarcel	1000,
159130803Smarcel	100,
160130803Smarcel	{ 2, 5 },
161130803Smarcel
162130803Smarcel	5000,
163130803Smarcel
164130803Smarcel	switch_to_mode,
165130803Smarcel	set_cpu_enabled,
166130803Smarcel	has_cache_expired,
167130803Smarcel	choose_core,
168130803Smarcel	rebalance,
169130803Smarcel	rebalance_irqs,
170130803Smarcel};
171130803Smarcel
172130803Smarcel