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 16using namespace Scheduler; 17 18 19const bigtime_t kCacheExpire = 100000; 20 21 22static void 23switch_to_mode() 24{ 25} 26 27 28static void 29set_cpu_enabled(int32 /* cpu */, bool /* enabled */) 30{ 31} 32 33 34static bool 35has_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 46static CoreEntry* 47choose_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 75static CoreEntry* 76rebalance(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 108static void 109rebalance_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 155scheduler_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