Ticket #231: diff
File diff, 11.4 KB (added by , 15 years ago) |
---|
-
kernel/arch/ia32/include/atomic.h
=== modified file 'kernel/arch/ia32/include/atomic.h'
100 100 101 101 static inline atomic_count_t test_and_set(atomic_t *val) 102 102 { 103 atomic_count_t v = 1;103 atomic_count_t v = 0xf00dba11UL; 104 104 105 105 asm volatile ( 106 106 "xchgl %[v], %[count]\n" -
kernel/generic/include/cpu.h
=== modified file 'kernel/generic/include/cpu.h'
37 37 38 38 #include <mm/tlb.h> 39 39 #include <synch/spinlock.h> 40 #include <synch/condvar.h> 40 41 #include <proc/scheduler.h> 41 42 #include <arch/cpu.h> 42 43 #include <arch/context.h> … … 59 60 runq_t rq[RQ_COUNT]; 60 61 volatile size_t needs_relink; 61 62 63 #ifdef CONFIG_SMP 64 condvar_t kcpulb_cv; 65 #endif 66 62 67 SPINLOCK_DECLARE(timeoutlock); 63 68 link_t timeout_active_head; 64 69 -
kernel/generic/include/synch/spinlock.h
=== modified file 'kernel/generic/include/synch/spinlock.h'
41 41 #include <atomic.h> 42 42 #include <debug.h> 43 43 44 #define LOCK_LOCKED 0xf00dba11UL 45 #define LOCK_UNLOCKED 0xca11b00fUL 46 #define LOCK_MAGIC1 0xdeadca1fUL 47 #define LOCK_MAGIC2 0x0becedadUL 48 44 49 #ifdef CONFIG_SMP 45 50 46 51 typedef struct { 52 unative_t magic1; 47 53 atomic_t val; 54 unative_t magic2; 55 56 atomic_t cnt; 48 57 49 58 #ifdef CONFIG_DEBUG_SPINLOCK 50 59 const char *name; … … 67 76 #define SPINLOCK_INITIALIZE_NAME(lock_name, desc_name) \ 68 77 spinlock_t lock_name = { \ 69 78 .name = desc_name, \ 70 .val = { 0 } \ 79 .magic1 = LOCK_MAGIC1, \ 80 .magic2 = LOCK_MAGIC2, \ 81 .val = { LOCK_UNLOCKED }, \ 82 .cnt = { 0 } \ 71 83 } 72 84 73 85 #define SPINLOCK_STATIC_INITIALIZE_NAME(lock_name, desc_name) \ 74 86 static spinlock_t lock_name = { \ 75 87 .name = desc_name, \ 76 .val = { 0 } \ 88 .magic1 = LOCK_MAGIC1, \ 89 .magic2 = LOCK_MAGIC2, \ 90 .val = { LOCK_UNLOCKED }, \ 91 .cnt = { 0 } \ 77 92 } 78 93 79 94 #define spinlock_lock(lock) spinlock_lock_debug(lock) … … 103 118 extern void spinlock_initialize(spinlock_t *lock, const char *name); 104 119 extern int spinlock_trylock(spinlock_t *lock); 105 120 extern void spinlock_lock_debug(spinlock_t *lock); 106 107 /** Unlock spinlock 108 * 109 * Unlock spinlock. 110 * 111 * @param sl Pointer to spinlock_t structure. 112 */ 113 static inline void spinlock_unlock(spinlock_t *lock) 114 { 115 ASSERT(atomic_get(&lock->val) != 0); 116 117 /* 118 * Prevent critical section code from bleeding out this way down. 119 */ 120 CS_LEAVE_BARRIER(); 121 122 atomic_set(&lock->val, 0); 123 preemption_enable(); 124 } 121 extern void spinlock_unlock(spinlock_t *lock); 125 122 126 123 #ifdef CONFIG_DEBUG_SPINLOCK 127 124 -
kernel/generic/src/cpu/cpu.c
=== modified file 'kernel/generic/src/cpu/cpu.c'
58 58 void cpu_init(void) { 59 59 unsigned int i, j; 60 60 61 #ifdef CONFIG_SMP62 61 if (config.cpu_active == 1) { 63 #endif /* CONFIG_SMP */64 62 cpus = (cpu_t *) malloc(sizeof(cpu_t) * config.cpu_count, 65 63 FRAME_ATOMIC); 66 64 if (!cpus) 67 65 panic("Cannot allocate CPU structures."); 68 66 69 /* initialize everything */67 /* Initialize everything */ 70 68 memsetb(cpus, sizeof(cpu_t) * config.cpu_count, 0); 71 69 72 70 for (i = 0; i < config.cpu_count; i++) { 73 cpus[i].stack = (uint8_t *) frame_alloc(STACK_FRAMES, FRAME_KA | FRAME_ATOMIC); 71 cpus[i].stack = (uint8_t *) frame_alloc(STACK_FRAMES, 72 FRAME_KA | FRAME_ATOMIC); 74 73 75 74 cpus[i].id = i; 76 75 77 76 spinlock_initialize(&cpus[i].lock, "cpu_t.lock"); 77 #ifdef CONFIG_SMP 78 condvar_initialize(&cpus[i].kcpulb_cv); 79 #endif 78 80 79 81 for (j = 0; j < RQ_COUNT; j++) { 80 spinlock_initialize(&cpus[i].rq[j].lock, "rq_t.lock"); 82 spinlock_initialize(&cpus[i].rq[j].lock, 83 "rq_t.lock"); 81 84 list_initialize(&cpus[i].rq[j].rq_head); 82 85 } 83 86 } 84 85 #ifdef CONFIG_SMP86 87 } 87 #endif /* CONFIG_SMP */88 88 89 89 CPU = &cpus[config.cpu_active - 1]; 90 90 -
kernel/generic/src/proc/scheduler.c
=== modified file 'kernel/generic/src/proc/scheduler.c'
189 189 interrupts_enable(); 190 190 191 191 if (atomic_get(&CPU->nrdy) == 0) { 192 #ifdef CONFIG_SMP 193 condvar_signal(&CPU->kcpulb_cv); 194 #endif 195 192 196 /* 193 197 * For there was nothing to run, the CPU goes to sleep 194 198 * until a hardware interrupt or an IPI comes. … … 530 534 } 531 535 532 536 #ifdef CONFIG_SMP 537 538 static int get_cpu_balance(atomic_count_t *); 539 540 /** 541 * Calculate the number of threads that will be migrated/stolen from 542 * other CPU's based on the actual situation. 543 * 544 * @param average[out] Pointer to memory where the average number of threads 545 * per CPU will be stored. 546 * @return The number of threads that should be migrated by the current CPU. 547 */ 548 int get_cpu_balance(atomic_count_t *average) 549 { 550 *average = atomic_get(&nrdy) / config.cpu_active + 1; 551 return *average - atomic_get(&CPU->nrdy); 552 } 553 533 554 /** Load balancing thread 534 555 * 535 556 * SMP load balancing thread, supervising thread supplies … … 547 568 int j; 548 569 int k = 0; 549 570 ipl_t ipl; 571 mutex_t dummy_lock; 572 uint32_t backoff_us = 0; 550 573 551 574 /* 552 575 * Detach kcpulb as nobody will call thread_join_timeout() on it. 553 576 */ 554 577 thread_detach(THREAD); 555 578 579 /* 580 * The dummy lock is only needed to indulge the condvar_wait() API, 581 * but other than that, it is not really necessary. We simply initialize 582 * it and keep it locked. 583 */ 584 mutex_initialize(&dummy_lock, MUTEX_PASSIVE); 585 mutex_lock(&dummy_lock); 586 556 587 loop: 557 /* 558 * Work in 1s intervals. 559 */ 560 thread_sleep(1); 561 562 not_satisfied: 563 /* 564 * Calculate the number of threads that will be migrated/stolen from 565 * other CPU's. Note that situation can have changed between two 566 * passes. Each time get the most up to date counts. 567 */ 568 average = atomic_get(&nrdy) / config.cpu_active + 1; 569 count = average - atomic_get(&CPU->nrdy); 570 571 if (count <= 0) 572 goto satisfied; 588 if (backoff_us) 589 thread_usleep(backoff_us * 100000); 590 591 /* 592 * Wait until our CPU wants to steal some threads or until a 1s 593 * interval elapses. 594 */ 595 while ((count = get_cpu_balance(&average)) <= 0) 596 condvar_wait_timeout(&CPU->kcpulb_cv, &dummy_lock, 1000000); 597 598 if (backoff_us < 1000000) 599 backoff_us <<= 5; 573 600 574 601 /* 575 602 * Searching least priority queues on all CPU's first and most priority … … 656 683 657 684 interrupts_restore(ipl); 658 685 659 if (--count == 0) 660 goto satisfied; 686 if (--count == 0) { 687 backoff_us >>= 1; 688 goto loop; 689 } 661 690 662 691 /* 663 692 * We are not satisfied yet, focus on another … … 676 705 * Be a little bit light-weight and let migrated threads run. 677 706 */ 678 707 scheduler(); 679 } else { 680 /* 681 * We failed to migrate a single thread. 682 * Give up this turn. 683 */ 684 goto loop; 708 backoff_us >>= 1; 685 709 } 686 710 687 goto not_satisfied;688 689 satisfied:690 711 goto loop; 691 712 } 692 713 -
kernel/generic/src/synch/spinlock.c
=== modified file 'kernel/generic/src/synch/spinlock.c'
43 43 #include <print.h> 44 44 #include <debug.h> 45 45 #include <symtab.h> 46 #include <cpu.h> 47 #include <proc/thread.h> 48 #include <proc/task.h> 49 #include <arch/cycle.h> 46 50 47 51 #ifdef CONFIG_SMP 48 52 … … 53 57 */ 54 58 void spinlock_initialize(spinlock_t *lock, const char *name) 55 59 { 56 atomic_set(&lock->val, 0); 60 atomic_set(&lock->val, LOCK_UNLOCKED); 61 atomic_set(&lock->cnt, 0); 57 62 #ifdef CONFIG_DEBUG_SPINLOCK 63 lock->magic1 = LOCK_MAGIC1; 64 lock->magic2 = LOCK_MAGIC2; 58 65 lock->name = name; 59 66 #endif 60 67 } 61 68 62 69 #ifdef CONFIG_DEBUG_SPINLOCK 63 70 71 static const char *le_str[] = { 72 "invalid", 73 "spinlock_lock", 74 "spinlock_trylock", 75 "spinlock_unlock" 76 }; 77 78 typedef enum { 79 LE_INVALID = 0, 80 LE_LOCK, 81 LE_TRYLOCK, 82 LE_UNLOCK 83 } lock_event_type_t; 84 85 typedef struct { 86 lock_event_type_t type; 87 spinlock_t *lock; 88 cpu_t *cpu; 89 thread_t *thread; 90 unative_t pc; 91 uint64_t cycle; 92 atomic_count_t cnt; 93 } lock_event_t; 94 95 #define LE_EVENTS 8000 96 97 static lock_event_t lock_events[LE_EVENTS]; 98 static atomic_t ne; 99 100 static int lock_tracing = 1; 101 102 void lock_event_record(lock_event_type_t type, spinlock_t *l, unative_t pc); 103 void lock_event_record(lock_event_type_t type, spinlock_t *l, unative_t pc) 104 { 105 if (!lock_tracing) 106 return; 107 if (l->name[0] == '*') 108 return; 109 110 int i = atomic_postinc(&ne) % LE_EVENTS; 111 112 lock_events[i].type = type; 113 lock_events[i].lock = l; 114 lock_events[i].cpu = CPU; 115 lock_events[i].thread = THREAD; 116 lock_events[i].pc = pc; 117 lock_events[i].cycle = get_cycle(); 118 lock_events[i].cnt = atomic_get(&l->cnt); 119 } 120 121 void lock_event_print(lock_event_t *le); 122 void lock_event_print(lock_event_t *le) 123 { 124 125 printf("cycle=%p ", le->cycle); 126 if (le->type > LE_UNLOCK) 127 le->type = 0; 128 printf("%s(%s/%p) ", le_str[le->type], le->lock->name, le->lock); 129 if (le->cpu) 130 printf("cpu%d, ", le->cpu->id); 131 else 132 printf("nocpu, "); 133 printf("pc=%p ", le->pc); 134 printf("cnt=%ld ", le->cnt); 135 printf("thread=%p\n", le->thread); 136 } 137 138 void lock_event_show(spinlock_t *l); 139 void lock_event_show(spinlock_t *l) 140 { 141 int i, j = 0; 142 uint64_t min = 0xffffffffffffffffULL; 143 144 for (i = 0; i < LE_EVENTS; i++) { 145 if (lock_events[i].cycle < min) { 146 min = lock_events[i].cycle; 147 j = i; 148 } 149 } 150 151 for (i = j; i < LE_EVENTS + j; i++) { 152 lock_event_t *e = &lock_events[i % LE_EVENTS]; 153 154 if (e->lock == l) 155 lock_event_print(e); 156 } 157 } 158 64 159 /** Lock spinlock 65 160 * 66 161 * Lock spinlock. … … 75 170 size_t i = 0; 76 171 bool deadlock_reported = false; 77 172 173 ASSERT(lock->magic1 == LOCK_MAGIC1); 174 ASSERT(lock->magic2 == LOCK_MAGIC2); 175 78 176 preemption_disable(); 79 while (test_and_set(&lock->val) ) {177 while (test_and_set(&lock->val) != LOCK_UNLOCKED) { 80 178 /* 81 179 * We need to be careful about particular locks 82 180 * which are directly used to report deadlocks … … 110 208 } 111 209 } 112 210 211 atomic_inc(&lock->cnt); 212 if (atomic_get(&lock->cnt) != 1) { 213 lock_tracing = 0; 214 lock_event_show(lock); 215 panic("not alone in critical section"); 216 } 217 113 218 if (deadlock_reported) 114 219 printf("cpu%u: not deadlocked\n", CPU->id); 115 220 221 222 lock_event_record(LE_LOCK, lock, CALLER); 223 116 224 /* 117 225 * Prevent critical section code from bleeding out this way up. 118 226 */ 119 227 CS_ENTER_BARRIER(); 228 if (atomic_get(&lock->cnt) != 1) { 229 lock_tracing = 0; 230 lock_event_show(lock); 231 panic("not alone in critical section"); 232 } 120 233 } 121 234 122 235 #endif … … 135 248 int spinlock_trylock(spinlock_t *lock) 136 249 { 137 250 preemption_disable(); 138 int rc = !test_and_set(&lock->val); 251 atomic_count_t ret = test_and_set(&lock->val); 252 253 ASSERT(lock->magic1 == LOCK_MAGIC1); 254 ASSERT(lock->magic2 == LOCK_MAGIC2); 255 ASSERT(ret == LOCK_LOCKED || ret == LOCK_UNLOCKED); 139 256 140 257 /* 141 258 * Prevent critical section code from bleeding out this way up. 142 259 */ 143 260 CS_ENTER_BARRIER(); 144 261 145 if ( !rc)262 if (ret == LOCK_LOCKED) 146 263 preemption_enable(); 147 148 return rc; 149 } 150 264 else { 265 if (atomic_postinc(&lock->cnt) != 0) 266 panic("This lock is locked!"); 267 lock_event_record(LE_TRYLOCK, lock, CALLER); 268 } 269 270 return ret == LOCK_UNLOCKED; 271 } 272 273 void spinlock_unlock(spinlock_t *lock) 274 { 275 ASSERT(lock->magic1 == LOCK_MAGIC1); 276 ASSERT(lock->magic2 == LOCK_MAGIC2); 277 278 lock_event_record(LE_UNLOCK, lock, CALLER); 279 if (atomic_get(&lock->val) != LOCK_LOCKED) { 280 lock_tracing = 0; 281 lock_event_show(lock); 282 panic("unlocking non-locked lock"); 283 } 284 285 /* 286 * Prevent critical section code from bleeding out this way down. 287 */ 288 CS_LEAVE_BARRIER(); 289 290 atomic_dec(&lock->cnt); 291 atomic_set(&lock->val, LOCK_UNLOCKED); 292 preemption_enable(); 293 } 151 294 #endif 152 295 153 296 /** @}