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