Ticket #1069: scheduler.diff
File scheduler.diff, 15.5 KB (added by , 13 years ago) |
---|
-
src/system/kernel/arch/x86/arch_cpu.cpp
28 28 #include <arch_system_info.h> 29 29 #include <arch/x86/selector.h> 30 30 #include <boot/kernel_args.h> 31 #include <arch/x86/apic.h> 31 32 32 33 #include "interrupts.h" 33 34 #include "paging/X86PagingStructures.h" … … 504 505 #endif // DUMP_FEATURE_STRING 505 506 506 507 508 int32 509 round_to_pwr_of_2(int32 in) 510 { 511 if (in > 64) 512 return 128; 513 else if (in > 32) 514 return 64; 515 else if (in > 16) 516 return 32; 517 else if (in > 8) 518 return 16; 519 else if (in > 4) 520 return 8; 521 else if (in > 2) 522 return 4; 523 else if (in > 1) 524 return 2; 525 else 526 return in; 527 } 528 529 507 530 static int 508 531 detect_cpu(int currentCPU) 509 532 { … … 519 542 cpu->arch.feature[FEATURE_EXT_AMD] = 0; 520 543 cpu->arch.model_name[0] = 0; 521 544 545 // initialize the topology data 546 // TODO: should this be negative since 0s are valid? 547 cpu->cpu_num_in_core = 0; 548 cpu->core_num = 0; 549 cpu->package_num = 0; 550 cpu->numa_num = 0; 551 522 552 // print some fun data 523 553 get_current_cpuid(&cpuid, 0); 524 554 … … 608 638 cpu->arch.feature[FEATURE_EXT_AMD] = cpuid.regs.edx; // edx 609 639 } 610 640 641 // determine processor topology for scheduler 642 int32 logcpubits = 0; 643 int32 corebits = 0; 644 if (cpu->arch.feature[FEATURE_COMMON] & IA32_FEATURE_HTT) { 645 // Has HT 646 cpu->has_ht = true; 647 get_current_cpuid(&cpuid, 0x00000001); 648 int32 count = (cpuid.regs.ebx >> 16) & 255; // EBX[23:16] 649 if (cpu->arch.vendor == VENDOR_INTEL) { 650 // retrieve topology data for Intel chipsets 651 get_current_cpuid_ex(&cpuid, 0x0000000B, 0); 652 // check if leaf 0x0000000B exists 653 if ((cpuid.regs.ebx & ((1<<15)-1)) != 0) { // EBX[15:0] 654 dprintf("******* Intel Processor - Leaf 0x0000000B Exists!\n"); 655 logcpubits = cpuid.regs.eax & ((1<<4)-1); // EAX[4:0] 656 get_current_cpuid_ex(&cpuid, 0x0000000B, 1); 657 corebits = (cpuid.regs.eax & ((1<<4)-1)) - logcpubits; 658 } else { 659 // retrieve topology data without using leaf 0x0000000B 660 dprintf("******* Intel Processor - Leaf 0x0000000B Does NOT Exist!\n"); 661 // NOTE: There are some cases where leaf 0x00000004 isn't 662 // supported... I don't know how to test for that yet (Duggan) 663 get_current_cpuid(&cpuid, 0x00000004); 664 int32 temp = cpuid.regs.eax >> 26; // EAX[31:26] 665 logcpubits = round_to_pwr_of_2(temp); 666 temp = temp >> logcpubits; 667 corebits = round_to_pwr_of_2(temp); 668 } 669 } else if (cpu->arch.vendor == VENDOR_AMD) { 670 // retrieve topology data for AMD chipsets 671 // NOTE: There are some cases where 0x80000008 isn't supported... 672 // I have no clue how to test that right now (Duggan) 673 dprintf("******* AMD Processor - Has Hyperthreading!\n"); 674 get_current_cpuid(&cpuid, 0x80000008); 675 int32 temp = (cpuid.regs.ecx >> 12) & 15; // ECX[15:12] 676 if (temp != 0) 677 corebits = temp; 678 else 679 corebits = round_to_pwr_of_2(cpuid.regs.ecx & 255); 680 logcpubits = round_to_pwr_of_2(count >> corebits); 681 } else { 682 // no clue how to retrieve information for non-Intel/AMD vendors 683 dprintf("******* Cannot determine topology for non-Intel/AMD OEM!\n"); 684 cpu->has_ht = false; 685 } 686 687 // Now determine the topology 688 if (cpu->has_ht) { 689 cpu->cpu_num_in_core = apic_local_id() & ((1 << logcpubits) - 1); 690 cpu->core_num = (apic_local_id() >> logcpubits) & 691 ((1 << corebits) -1); 692 cpu->package_num = apic_local_id() & 693 ~((1 << (logcpubits + corebits)) -1); 694 } 695 } else { 696 // No HT 697 dprintf("******* No Hyperthreading!\n"); 698 // set flag in cpu_ent to say there's no HT 699 cpu->has_ht = false; 700 } 701 611 702 #if DUMP_FEATURE_STRING 612 703 dump_feature_string(currentCPU, cpu); 613 704 #endif -
src/system/kernel/arch/x86/cpuid.S
28 28 ret 29 29 FUNCTION_END(get_current_cpuid) 30 30 31 /* void get_current_cpuid_ex(cpuid_info *info, uint32 eaxRegister, 32 uint32 ecxRegister = 0) */ 33 FUNCTION(get_current_cpuid_ex): 34 pushl %ebx 35 pushl %edi 36 movl 12(%esp),%edi /* first arg points to the cpuid_info structure */ 37 movl 16(%esp),%eax /* second arg sets up eax */ 38 movl 20(%esp),%ecx /* third arg sets up ecx */ 39 cpuid 40 movl %eax,0(%edi) /* copy the regs into the cpuid_info structure */ 41 movl %ebx,4(%edi) 42 movl %edx,8(%edi) 43 movl %ecx,12(%edi) 44 popl %edi 45 popl %ebx 46 xorl %eax, %eax /* return B_OK */ 47 ret 48 FUNCTION_END(get_current_cpuid_ex) 31 49 50 32 51 /* unsigned int get_eflags(void) */ 33 52 FUNCTION(get_eflags): 34 53 pushfl -
src/system/kernel/team.cpp
447 447 user_data_size = 0; 448 448 free_user_threads = NULL; 449 449 450 // new team has no soft affinity 451 preferred_cpu = -1; 452 450 453 supplementary_groups = NULL; 451 454 supplementary_group_count = 0; 452 455 -
src/system/kernel/scheduler/scheduler.cpp
68 68 cpuCount != 1 ? "s" : ""); 69 69 70 70 if (cpuCount > 1) { 71 #if 071 #if 1 72 72 dprintf("scheduler_init: using affine scheduler\n"); 73 73 scheduler_affine_init(); 74 74 #else -
src/system/kernel/scheduler/scheduler_affine.cpp
1 1 /* 2 * Copyright 2011, James Dewey Taylor, james.dewey.taylor@gmail.com 2 3 * Copyright 2009, Rene Gollent, rene@gollent.com. 3 4 * Copyright 2008-2011, Ingo Weinhold, ingo_weinhold@gmx.de. 4 5 * Copyright 2002-2010, Axel Dörfler, axeld@pinc-software.de. … … 37 38 # define TRACE(x) ; 38 39 #endif 39 40 41 // Helper macros 42 #define RunQueue(x) sRunQueue[sCPUMap[x]] 43 #define RunQueueSize(x) sRunQueueSize[sCPUMap[x]] 44 40 45 // The run queues. Holds the threads ready to run ordered by priority. 41 46 // One queue per schedulable target (CPU, core, etc.). 42 47 // TODO: consolidate this such that HT/SMT entities on the same physical core 43 48 // share a queue, once we have the necessary API for retrieving the topology 44 49 // information 50 static int32 sCPUMap[B_MAX_CPU_COUNT]; 45 51 static Thread* sRunQueue[B_MAX_CPU_COUNT]; 46 52 static int32 sRunQueueSize[B_MAX_CPU_COUNT]; 47 53 static Thread* sIdleThreads; … … 108 114 Thread *thread = NULL; 109 115 110 116 for (int32 i = 0; i < smp_get_num_cpus(); i++) { 111 thread = sRunQueue[i];117 thread = RunQueue(i); 112 118 kprintf("Run queue for cpu %ld (%ld threads)\n", i, 113 sRunQueueSize[i]);114 if ( sRunQueueSize[i]> 0) {119 RunQueueSize(i)); 120 if (RunQueueSize(i) > 0) { 115 121 kprintf("thread id priority avg. quantum name\n"); 116 122 while (thread) { 117 123 kprintf("%p %-7ld %-8ld %-12ld %s\n", thread, thread->id, … … 126 132 } 127 133 128 134 135 void 136 display_topology() 137 { 138 dprintf_no_syslog("Processor Topology Data\n"); 139 dprintf_no_syslog("Num\tCPU\tNumOnCore\tCore\tPackage\n"); 140 for (int32 i = 0; i < smp_get_num_cpus(); i++) { 141 dprintf_no_syslog("%ld\t%d\t%d\t%d\t%d\n", 142 i, gCPU[i].cpu_num, gCPU[i].cpu_num_in_core, gCPU[i].core_num, 143 gCPU[i].package_num); 144 } 145 } 146 147 129 148 /*! Returns the most idle CPU based on the active time counters. 130 149 Note: thread lock must be held when entering this function 131 150 */ … … 136 155 for (int32 i = 0; i < smp_get_num_cpus(); i++) { 137 156 if (gCPU[i].disabled) 138 157 continue; 139 if (targetCPU < 0 || sRunQueueSize[i] < sRunQueueSize[targetCPU])158 if (targetCPU < 0 || RunQueueSize(i) < RunQueueSize(targetCPU)) 140 159 targetCPU = i; 141 160 } 142 161 … … 153 172 int32 targetCPU = -1; 154 173 if (thread->pinned_to_cpu > 0) 155 174 targetCPU = thread->previous_cpu->cpu_num; 156 else if (thread->previous_cpu == NULL || thread->previous_cpu->disabled) 157 targetCPU = affine_get_most_idle_cpu(); 158 else 175 else if (thread->previous_cpu == NULL || thread->previous_cpu->disabled) { 176 if (thread->team->preferred_cpu < 0) 177 thread->team->preferred_cpu = affine_get_most_idle_cpu(); 178 targetCPU = thread->team->preferred_cpu; 179 } else 159 180 targetCPU = thread->previous_cpu->cpu_num; 160 181 161 182 thread->state = thread->next_state = B_THREAD_READY; … … 165 186 sIdleThreads = thread; 166 187 } else { 167 188 Thread *curr, *prev; 168 for (curr = sRunQueue[targetCPU], prev = NULL; curr189 for (curr = RunQueue(targetCPU), prev = NULL; curr 169 190 && curr->priority >= thread->next_priority; 170 191 curr = curr->queue_next) { 171 192 if (prev) 172 193 prev = prev->queue_next; 173 194 else 174 prev = sRunQueue[targetCPU];195 prev = RunQueue(targetCPU); 175 196 } 176 197 177 198 T(EnqueueThread(thread, prev, curr)); 178 sRunQueueSize[targetCPU]++;199 RunQueueSize(targetCPU)++; 179 200 thread->queue_next = curr; 180 201 if (prev) 181 202 prev->queue_next = thread; 182 203 else 183 sRunQueue[targetCPU]= thread;204 RunQueue(targetCPU) = thread; 184 205 185 206 thread->scheduler_data->fLastQueue = targetCPU; 186 207 } … … 213 234 resultThread = prevThread->queue_next; 214 235 prevThread->queue_next = resultThread->queue_next; 215 236 } else { 216 resultThread = sRunQueue[currentCPU];217 sRunQueue[currentCPU]= resultThread->queue_next;237 resultThread = RunQueue(currentCPU); 238 RunQueue(currentCPU) = resultThread->queue_next; 218 239 } 219 sRunQueueSize[currentCPU]--;240 RunQueueSize(currentCPU)--; 220 241 resultThread->scheduler_data->fLastQueue = -1; 221 242 222 243 return resultThread; … … 239 260 int32 targetCPU = -1; 240 261 for (int32 i = 0; i < smp_get_num_cpus(); i++) { 241 262 // skip CPUs that have either no or only one thread 242 if (i == currentCPU || sRunQueueSize[i]< 2)263 if (i == currentCPU || RunQueueSize(i) < 2) 243 264 continue; 244 265 245 266 // out of the CPUs with threads available to steal, 246 267 // pick whichever one is generally the most CPU bound. 247 268 if (targetCPU < 0 248 || sRunQueue[i]->priority > sRunQueue[targetCPU]->priority249 || ( sRunQueue[i]->priority == sRunQueue[targetCPU]->priority250 && sRunQueueSize[i] > sRunQueueSize[targetCPU]))269 || RunQueue(i)->priority > RunQueue(targetCPU)->priority 270 || (RunQueue(i)->priority == RunQueue(targetCPU)->priority 271 && RunQueueSize(i) > RunQueueSize(targetCPU))) 251 272 targetCPU = i; 252 273 } 253 274 254 275 if (targetCPU < 0) 255 276 return NULL; 256 277 257 Thread* nextThread = sRunQueue[targetCPU];278 Thread* nextThread = RunQueue(targetCPU); 258 279 Thread* prevThread = NULL; 259 280 260 281 while (nextThread != NULL) { … … 302 323 Thread *item = NULL, *prev = NULL; 303 324 targetCPU = thread->scheduler_data->fLastQueue; 304 325 305 for (item = sRunQueue[targetCPU], prev = NULL; item && item != thread;326 for (item = RunQueue(targetCPU), prev = NULL; item && item != thread; 306 327 item = item->queue_next) { 307 328 if (prev) 308 329 prev = prev->queue_next; … … 373 394 374 395 Thread *nextThread, *prevThread; 375 396 376 TRACE(("reschedule(): cpu %ld, cur_thread = %ld\n", currentCPU, oldThread->id)); 397 TRACE(("reschedule(): cpu %ld, cur_thread = %ld\n", currentCPU, 398 oldThread->id)); 377 399 378 400 oldThread->state = oldThread->next_state; 379 401 switch (oldThread->next_state) { 380 402 case B_THREAD_RUNNING: 381 403 case B_THREAD_READY: 382 TRACE(("enqueueing thread %ld into run q. pri = %ld\n", oldThread->id, oldThread->priority)); 404 TRACE(("enqueueing thread %ld into run q. pri = %ld\n", 405 oldThread->id, oldThread->priority)); 383 406 affine_enqueue_in_run_queue(oldThread); 384 407 break; 385 408 case B_THREAD_SUSPENDED: … … 388 411 case THREAD_STATE_FREE_ON_RESCHED: 389 412 break; 390 413 default: 391 TRACE(("not enqueueing thread %ld into run q. next_state = %ld\n", oldThread->id, oldThread->next_state)); 414 TRACE(("not enqueueing thread %ld into run q. next_state = %ld\n", 415 oldThread->id, oldThread->next_state)); 392 416 break; 393 417 } 394 418 395 nextThread = sRunQueue[currentCPU];419 nextThread = RunQueue(currentCPU); 396 420 prevThread = NULL; 397 421 398 if ( sRunQueue[currentCPU]!= NULL) {422 if (RunQueue(currentCPU) != NULL) { 399 423 TRACE(("dequeueing next thread from cpu %ld\n", currentCPU)); 400 424 // select next thread from the run queue 401 425 while (nextThread->queue_next) { … … 487 511 cancel_timer(quantumTimer); 488 512 oldThread->cpu->preempted = 0; 489 513 490 // we do not adjust the quantum for the idle thread as it is going to be491 // preempted most of the time and would likely get the longer quantum492 // over time, indeed we use a smaller quantum to avoid running idle too493 // long514 // we do not adjust the quantum for the idle thread as it is going to 515 // be preempted most of the time and would likely get the longer 516 // quantum over time, indeed we use a smaller quantum to avoid running 517 // idle too long 494 518 bigtime_t quantum = kMinThreadQuantum; 495 519 // give CPU-bound background threads a larger quantum size 496 520 // to minimize unnecessary context switches if the system is idle … … 574 598 memset(sRunQueueSize, 0, sizeof(sRunQueueSize)); 575 599 add_debugger_command_etc("run_queue", &dump_run_queue, 576 600 "List threads in run queue", "\nLists threads in run queue", 0); 601 // TODO: get topology info to initialize sCPUMap 602 // we're assuming a homogenous topology for now 603 // also we're just worried about HT not various levels of cache sharing 604 if (gCPU[0].has_ht) { 605 int maxcorenum = 0; 606 for (int i = 0; i < smp_get_num_cpus(); i++) { 607 if (gCPU[i].core_num > maxcorenum) 608 maxcorenum = gCPU[i].core_num; 609 } 610 for (int i = 0; i < smp_get_num_cpus(); i++) { 611 sCPUMap[i] = (maxcorenum + 1) * gCPU[i].package_num + 612 gCPU[i].core_num; 613 } 614 } else { 615 for (int i = 0; i < B_MAX_CPU_COUNT ; i++) { 616 sCPUMap[i] = i; 617 } 618 } 619 #if 1 620 display_topology(); 621 #endif 577 622 } -
headers/private/kernel/arch/x86/arch_system_info.h
13 13 #endif 14 14 15 15 status_t get_current_cpuid(cpuid_info *info, uint32 eax); 16 status_t get_current_cpuid_ex(cpuid_info *info, uint32 eax, uint32 ecx = 0); 16 17 uint32 get_eflags(void); 17 18 void set_eflags(uint32 value); 18 19 -
headers/private/kernel/cpu.h
34 34 /* CPU local data structure */ 35 35 36 36 typedef struct cpu_ent { 37 // the logical cpu id 37 38 int cpu_num; 38 39 40 // the physical location of the logical cpu 41 int cpu_num_in_core; 42 int core_num; 43 int package_num; 44 int numa_num; 45 46 bool has_ht; 47 39 48 // thread.c: used to force a reschedule at quantum expiration time 40 49 int preempted; 41 50 timer quantum_timer; -
headers/private/kernel/thread_types.h
236 236 struct list dead_threads; 237 237 int dead_threads_count; 238 238 239 int32 preferred_cpu; // soft affinity for the team (can be 240 // overridden by setting a thread's hard 241 // affinity 242 239 243 // protected by the team's fLock 240 244 team_dead_children dead_children; 241 245 team_job_control_children stopped_children; … … 429 433 struct cpu_ent *previous_cpu; // protected by scheduler lock 430 434 int32 pinned_to_cpu; // only accessed by this thread or in the 431 435 // scheduler, when thread is not running 436 bool affinity_hard; // I think this is what pinned_to_cpu is 437 // supposed to be but I'm not sure (Duggan) 432 438 433 439 sigset_t sig_block_mask; // protected by scheduler lock, 434 440 // only modified by the thread itself