xref: /haiku/src/system/kernel/scheduler/low_latency.cpp (revision 18027fff34af4a666c1e62254b462cbaeae1859e)
1 /*
2  * Copyright 2013, Paweł Dziepak, pdziepak@quarnos.org.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include <util/AutoLock.h>
8 
9 #include "scheduler_common.h"
10 #include "scheduler_cpu.h"
11 #include "scheduler_modes.h"
12 #include "scheduler_profiler.h"
13 #include "scheduler_thread.h"
14 
15 
16 using namespace Scheduler;
17 
18 
19 const bigtime_t kCacheExpire = 100000;
20 
21 
22 static void
23 switch_to_mode()
24 {
25 }
26 
27 
28 static void
29 set_cpu_enabled(int32 /* cpu */, bool /* enabled */)
30 {
31 }
32 
33 
34 static bool
35 has_cache_expired(const ThreadData* threadData)
36 {
37 	SCHEDULER_ENTER_FUNCTION();
38 	if (threadData->WentSleepActive() == 0)
39 		return false;
40 	CoreEntry* core = threadData->Core();
41 	bigtime_t activeTime = core->GetActiveTime();
42 	return activeTime - threadData->WentSleepActive() > kCacheExpire;
43 }
44 
45 
46 static CoreEntry*
47 choose_core(const ThreadData* /* threadData */)
48 {
49 	SCHEDULER_ENTER_FUNCTION();
50 
51 	// wake new package
52 	PackageEntry* package = gIdlePackageList.Last();
53 	if (package == NULL) {
54 		// wake new core
55 		package = PackageEntry::GetMostIdlePackage();
56 	}
57 
58 	CoreEntry* core = NULL;
59 	if (package != NULL)
60 		core = package->GetIdleCore();
61 
62 	if (core == NULL) {
63 		ReadSpinLocker coreLocker(gCoreHeapsLock);
64 		// no idle cores, use least occupied core
65 		core = gCoreLoadHeap.PeekMinimum();
66 		if (core == NULL)
67 			core = gCoreHighLoadHeap.PeekMinimum();
68 	}
69 
70 	ASSERT(core != NULL);
71 	return core;
72 }
73 
74 
75 static bool
76 should_rebalance(const ThreadData* threadData)
77 {
78 	SCHEDULER_ENTER_FUNCTION();
79 
80 	int32 coreLoad = threadData->Core()->GetLoad();
81 	int32 threadLoad = threadData->GetLoad() / threadData->Core()->CPUCount();
82 
83 	// If the thread produces more than 50% of the load, leave it here. In
84 	// such situation it is better to move other threads away.
85 	if (threadLoad >= coreLoad / 2)
86 		return false;
87 
88 	// Get the least loaded core.
89 	ReadSpinLocker coreLocker(gCoreHeapsLock);
90 	CoreEntry* other = gCoreLoadHeap.PeekMinimum();
91 	if (other == NULL)
92 		other = gCoreHighLoadHeap.PeekMinimum();
93 	coreLocker.Unlock();
94 	ASSERT(other != NULL);
95 
96 	if (other == threadData->Core())
97 		return false;
98 
99 	// If there are idle cores give them some work unless that will cause
100 	// the current core to become idle.
101 	int32 coreNewLoad = coreLoad - threadLoad;
102 	if (other->GetLoad() == 0 && coreNewLoad != 0)
103 		return true;
104 
105 	// Attempt to keep load balanced.
106 	int32 otherNewLoad = other->GetLoad() + threadLoad;
107 	return coreNewLoad - otherNewLoad >= kLoadDifference;
108 }
109 
110 
111 static void
112 rebalance_irqs(bool idle)
113 {
114 	SCHEDULER_ENTER_FUNCTION();
115 
116 	if (idle)
117 		return;
118 
119 	cpu_ent* cpu = get_cpu_struct();
120 	SpinLocker locker(cpu->irqs_lock);
121 
122 	irq_assignment* chosen = NULL;
123 	irq_assignment* irq = (irq_assignment*)list_get_first_item(&cpu->irqs);
124 
125 	int32 totalLoad = 0;
126 	while (irq != NULL) {
127 		if (chosen == NULL || chosen->load < irq->load)
128 			chosen = irq;
129 		totalLoad += irq->load;
130 		irq = (irq_assignment*)list_get_next_item(&cpu->irqs, irq);
131 	}
132 
133 	locker.Unlock();
134 
135 	if (chosen == NULL || totalLoad < kLowLoad)
136 		return;
137 
138 	ReadSpinLocker coreLocker(gCoreHeapsLock);
139 	CoreEntry* other = gCoreLoadHeap.PeekMinimum();
140 	if (other == NULL)
141 		other = gCoreHighLoadHeap.PeekMinimum();
142 	coreLocker.Unlock();
143 
144 	int32 newCPU = other->CPUHeap()->PeekRoot()->ID();
145 
146 	ASSERT(other != NULL);
147 
148 	CoreEntry* core = CoreEntry::GetCore(cpu->cpu_num);
149 	if (other == core)
150 		return;
151 	if (other->GetLoad() + kLoadDifference >= core->GetLoad())
152 		return;
153 
154 	assign_io_interrupt_to_cpu(chosen->irq, newCPU);
155 }
156 
157 
158 scheduler_mode_operations gSchedulerLowLatencyMode = {
159 	"low latency",
160 
161 	1000,
162 	100,
163 	{ 2, 5 },
164 
165 	5000,
166 
167 	switch_to_mode,
168 	set_cpu_enabled,
169 	has_cache_expired,
170 	choose_core,
171 	should_rebalance,
172 	rebalance_irqs,
173 };
174 
175