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 CoreEntry* 76 rebalance(const ThreadData* threadData) 77 { 78 SCHEDULER_ENTER_FUNCTION(); 79 80 CoreEntry* core = threadData->Core(); 81 ASSERT(core != NULL); 82 83 // Get the least loaded core. 84 ReadSpinLocker coreLocker(gCoreHeapsLock); 85 CoreEntry* other = gCoreLoadHeap.PeekMinimum(); 86 if (other == NULL) 87 other = gCoreHighLoadHeap.PeekMinimum(); 88 coreLocker.Unlock(); 89 ASSERT(other != NULL); 90 91 // Check if the least loaded core is significantly less loaded than 92 // the current one. 93 int32 coreLoad = core->GetLoad(); 94 int32 otherLoad = other->GetLoad(); 95 if (other == core || otherLoad + kLoadDifference >= coreLoad) 96 return core; 97 98 // Check whether migrating the current thread would result in both core 99 // loads become closer to the average. 100 int32 difference = coreLoad - otherLoad - kLoadDifference; 101 ASSERT(difference > 0); 102 103 int32 threadLoad = threadData->GetLoad() / core->CPUCount(); 104 return difference >= threadLoad ? other : core; 105 } 106 107 108 static void 109 rebalance_irqs(bool idle) 110 { 111 SCHEDULER_ENTER_FUNCTION(); 112 113 if (idle) 114 return; 115 116 cpu_ent* cpu = get_cpu_struct(); 117 SpinLocker locker(cpu->irqs_lock); 118 119 irq_assignment* chosen = NULL; 120 irq_assignment* irq = (irq_assignment*)list_get_first_item(&cpu->irqs); 121 122 int32 totalLoad = 0; 123 while (irq != NULL) { 124 if (chosen == NULL || chosen->load < irq->load) 125 chosen = irq; 126 totalLoad += irq->load; 127 irq = (irq_assignment*)list_get_next_item(&cpu->irqs, irq); 128 } 129 130 locker.Unlock(); 131 132 if (chosen == NULL || totalLoad < kLowLoad) 133 return; 134 135 ReadSpinLocker coreLocker(gCoreHeapsLock); 136 CoreEntry* other = gCoreLoadHeap.PeekMinimum(); 137 if (other == NULL) 138 other = gCoreHighLoadHeap.PeekMinimum(); 139 coreLocker.Unlock(); 140 141 int32 newCPU = other->CPUHeap()->PeekRoot()->ID(); 142 143 ASSERT(other != NULL); 144 145 CoreEntry* core = CoreEntry::GetCore(cpu->cpu_num); 146 if (other == core) 147 return; 148 if (other->GetLoad() + kLoadDifference >= core->GetLoad()) 149 return; 150 151 assign_io_interrupt_to_cpu(chosen->irq, newCPU); 152 } 153 154 155 scheduler_mode_operations gSchedulerLowLatencyMode = { 156 "low latency", 157 158 1000, 159 100, 160 { 2, 5 }, 161 162 5000, 163 164 switch_to_mode, 165 set_cpu_enabled, 166 has_cache_expired, 167 choose_core, 168 rebalance, 169 rebalance_irqs, 170 }; 171 172