1 /* 2 * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Copyright 2002-2008, Axel Dörfler, axeld@pinc-software.de. 4 * Distributed under the terms of the MIT License. 5 * 6 * Copyright 2001-2002, Travis Geiselbrecht. All rights reserved. 7 * Distributed under the terms of the NewOS License. 8 */ 9 10 /* Functionality for symetrical multi-processors */ 11 12 #include <smp.h> 13 14 #include <stdlib.h> 15 #include <string.h> 16 17 #include <arch/cpu.h> 18 #include <arch/debug.h> 19 #include <arch/int.h> 20 #include <arch/smp.h> 21 #include <cpu.h> 22 #include <generic_syscall.h> 23 #include <int.h> 24 #include <spinlock_contention.h> 25 #include <thread.h> 26 27 #include "kernel_debug_config.h" 28 29 30 //#define TRACE_SMP 31 32 #ifdef TRACE_SMP 33 # define TRACE(x) dprintf x 34 #else 35 # define TRACE(x) ; 36 #endif 37 38 #define MSG_POOL_SIZE (SMP_MAX_CPUS * 4) 39 40 struct smp_msg { 41 struct smp_msg *next; 42 int32 message; 43 uint32 data; 44 uint32 data2; 45 uint32 data3; 46 void *data_ptr; 47 uint32 flags; 48 int32 ref_count; 49 volatile bool done; 50 uint32 proc_bitmap; 51 }; 52 53 #define MAILBOX_LOCAL 1 54 #define MAILBOX_BCAST 2 55 56 static spinlock boot_cpu_spin[SMP_MAX_CPUS] = { }; 57 58 static struct smp_msg *sFreeMessages = NULL; 59 static volatile int sFreeMessageCount = 0; 60 static spinlock sFreeMessageSpinlock = B_SPINLOCK_INITIALIZER; 61 62 static struct smp_msg *sCPUMessages[SMP_MAX_CPUS] = { NULL, }; 63 static spinlock sCPUMessageSpinlock[SMP_MAX_CPUS]; 64 65 static struct smp_msg *sBroadcastMessages = NULL; 66 static spinlock sBroadcastMessageSpinlock = B_SPINLOCK_INITIALIZER; 67 68 static bool sICIEnabled = false; 69 static int32 sNumCPUs = 1; 70 71 static int32 process_pending_ici(int32 currentCPU); 72 73 74 #if DEBUG_SPINLOCKS 75 #define NUM_LAST_CALLERS 32 76 77 static struct { 78 void *caller; 79 spinlock *lock; 80 } sLastCaller[NUM_LAST_CALLERS]; 81 82 static vint32 sLastIndex = 0; 83 // Is incremented atomically. Must be % NUM_LAST_CALLERS before being used 84 // as index into sLastCaller. Note, that it has to be casted to uint32 85 // before applying the modulo operation, since otherwise after overflowing 86 // that would yield negative indices. 87 88 89 static void 90 push_lock_caller(void *caller, spinlock *lock) 91 { 92 int32 index = (uint32)atomic_add(&sLastIndex, 1) % NUM_LAST_CALLERS; 93 94 sLastCaller[index].caller = caller; 95 sLastCaller[index].lock = lock; 96 } 97 98 99 static void * 100 find_lock_caller(spinlock *lock) 101 { 102 int32 lastIndex = (uint32)sLastIndex % NUM_LAST_CALLERS; 103 104 for (int32 i = 0; i < NUM_LAST_CALLERS; i++) { 105 int32 index = (NUM_LAST_CALLERS + lastIndex - 1 - i) % NUM_LAST_CALLERS; 106 if (sLastCaller[index].lock == lock) 107 return sLastCaller[index].caller; 108 } 109 110 return NULL; 111 } 112 113 114 int 115 dump_spinlock(int argc, char** argv) 116 { 117 if (argc != 2) { 118 print_debugger_command_usage(argv[0]); 119 return 0; 120 } 121 122 uint64 address; 123 if (!evaluate_debug_expression(argv[1], &address, false)) 124 return 0; 125 126 spinlock* lock = (spinlock*)(addr_t)address; 127 kprintf("spinlock %p:\n", lock); 128 bool locked = B_SPINLOCK_IS_LOCKED(lock); 129 if (locked) { 130 kprintf(" locked from %p\n", find_lock_caller(lock)); 131 } else 132 kprintf(" not locked\n"); 133 134 return 0; 135 } 136 137 138 #endif // DEBUG_SPINLOCKS 139 140 141 int 142 dump_ici_messages(int argc, char** argv) 143 { 144 // count broadcast messages 145 int32 count = 0; 146 int32 doneCount = 0; 147 int32 unreferencedCount = 0; 148 smp_msg* message = sBroadcastMessages; 149 while (message != NULL) { 150 count++; 151 if (message->done) 152 doneCount++; 153 if (message->ref_count <= 0) 154 unreferencedCount++; 155 message = message->next; 156 } 157 158 kprintf("ICI broadcast messages: %ld, first: %p\n", count, 159 sBroadcastMessages); 160 kprintf(" done: %ld\n", doneCount); 161 kprintf(" unreferenced: %ld\n", unreferencedCount); 162 163 // count per-CPU messages 164 for (int32 i = 0; i < sNumCPUs; i++) { 165 count = 0; 166 message = sCPUMessages[i]; 167 while (message != NULL) { 168 count++; 169 message = message->next; 170 } 171 172 kprintf("CPU %ld messages: %ld, first: %p\n", i, count, 173 sCPUMessages[i]); 174 } 175 176 return 0; 177 } 178 179 180 int 181 dump_ici_message(int argc, char** argv) 182 { 183 if (argc != 2) { 184 print_debugger_command_usage(argv[0]); 185 return 0; 186 } 187 188 uint64 address; 189 if (!evaluate_debug_expression(argv[1], &address, false)) 190 return 0; 191 192 smp_msg* message = (smp_msg*)(addr_t)address; 193 kprintf("ICI message %p:\n", message); 194 kprintf(" next: %p\n", message->next); 195 kprintf(" message: %ld\n", message->message); 196 kprintf(" data: %ld\n", message->data); 197 kprintf(" data2: %ld\n", message->data2); 198 kprintf(" data3: %ld\n", message->data3); 199 kprintf(" data_ptr: %p\n", message->data_ptr); 200 kprintf(" flags: %lx\n", message->flags); 201 kprintf(" ref_count: %lx\n", message->ref_count); 202 kprintf(" done: %s\n", message->done ? "true" : "false"); 203 kprintf(" proc_bitmap: %lx\n", message->proc_bitmap); 204 205 return 0; 206 } 207 208 209 static inline void 210 process_all_pending_ici(int32 currentCPU) 211 { 212 while (process_pending_ici(currentCPU) != B_ENTRY_NOT_FOUND) 213 ; 214 } 215 216 217 void 218 acquire_spinlock(spinlock *lock) 219 { 220 if (sNumCPUs > 1) { 221 int currentCPU = smp_get_current_cpu(); 222 if (are_interrupts_enabled()) 223 panic("acquire_spinlock: attempt to acquire lock %p with interrupts enabled\n", lock); 224 #if B_DEBUG_SPINLOCK_CONTENTION 225 while (atomic_add(&lock->lock, 1) != 0) 226 process_all_pending_ici(currentCPU); 227 #else 228 while (1) { 229 while (*lock != 0) { 230 process_all_pending_ici(currentCPU); 231 PAUSE(); 232 } 233 if (atomic_set((int32 *)lock, 1) == 0) 234 break; 235 } 236 237 #if DEBUG_SPINLOCKS 238 push_lock_caller(arch_debug_get_caller(), lock); 239 #endif 240 #endif 241 } else { 242 #if DEBUG_SPINLOCKS 243 int32 oldValue; 244 if (are_interrupts_enabled()) 245 panic("acquire_spinlock: attempt to acquire lock %p with interrupts enabled\n", lock); 246 oldValue = atomic_set((int32 *)lock, 1); 247 if (oldValue != 0) { 248 panic("acquire_spinlock: attempt to acquire lock %p twice on non-SMP system (last caller: %p, value %ld)\n", 249 lock, find_lock_caller(lock), oldValue); 250 } 251 252 push_lock_caller(arch_debug_get_caller(), lock); 253 #endif 254 } 255 } 256 257 258 static void 259 acquire_spinlock_nocheck(spinlock *lock) 260 { 261 if (sNumCPUs > 1) { 262 #if DEBUG_SPINLOCKS 263 if (are_interrupts_enabled()) 264 panic("acquire_spinlock_nocheck: attempt to acquire lock %p with interrupts enabled\n", lock); 265 #endif 266 #if B_DEBUG_SPINLOCK_CONTENTION 267 while (atomic_add(&lock->lock, 1) != 0) { 268 } 269 #else 270 while (1) { 271 while (*lock != 0) 272 PAUSE(); 273 if (atomic_set((int32 *)lock, 1) == 0) 274 break; 275 } 276 #endif 277 } else { 278 #if DEBUG_SPINLOCKS 279 if (are_interrupts_enabled()) 280 panic("acquire_spinlock_nocheck: attempt to acquire lock %p with interrupts enabled\n", lock); 281 if (atomic_set((int32 *)lock, 1) != 0) 282 panic("acquire_spinlock_nocheck: attempt to acquire lock %p twice on non-SMP system\n", lock); 283 #endif 284 } 285 } 286 287 288 void 289 release_spinlock(spinlock *lock) 290 { 291 if (sNumCPUs > 1) { 292 if (are_interrupts_enabled()) 293 panic("release_spinlock: attempt to release lock %p with interrupts enabled\n", lock); 294 #if B_DEBUG_SPINLOCK_CONTENTION 295 { 296 int32 count = atomic_set(&lock->lock, 0) - 1; 297 if (count < 0) { 298 panic("release_spinlock: lock %p was already released\n", lock); 299 } else { 300 // add to the total count -- deal with carry manually 301 if ((uint32)atomic_add(&lock->count_low, count) + count 302 < (uint32)count) { 303 atomic_add(&lock->count_high, 1); 304 } 305 } 306 } 307 #else 308 if (atomic_set((int32 *)lock, 0) != 1) 309 panic("release_spinlock: lock %p was already released\n", lock); 310 #endif 311 } else { 312 #if DEBUG_SPINLOCKS 313 if (are_interrupts_enabled()) 314 panic("release_spinlock: attempt to release lock %p with interrupts enabled\n", lock); 315 if (atomic_set((int32 *)lock, 0) != 1) 316 panic("release_spinlock: lock %p was already released\n", lock); 317 #endif 318 } 319 } 320 321 322 /** Finds a free message and gets it. 323 * NOTE: has side effect of disabling interrupts 324 * return value is the former interrupt state 325 */ 326 327 static cpu_status 328 find_free_message(struct smp_msg **msg) 329 { 330 cpu_status state; 331 332 TRACE(("find_free_message: entry\n")); 333 334 retry: 335 while (sFreeMessageCount <= 0) { 336 state = disable_interrupts(); 337 process_all_pending_ici(smp_get_current_cpu()); 338 restore_interrupts(state); 339 PAUSE(); 340 } 341 state = disable_interrupts(); 342 acquire_spinlock(&sFreeMessageSpinlock); 343 344 if (sFreeMessageCount <= 0) { 345 // someone grabbed one while we were getting the lock, 346 // go back to waiting for it 347 release_spinlock(&sFreeMessageSpinlock); 348 restore_interrupts(state); 349 goto retry; 350 } 351 352 *msg = sFreeMessages; 353 sFreeMessages = (*msg)->next; 354 sFreeMessageCount--; 355 356 release_spinlock(&sFreeMessageSpinlock); 357 358 TRACE(("find_free_message: returning msg %p\n", *msg)); 359 360 return state; 361 } 362 363 364 static void 365 return_free_message(struct smp_msg *msg) 366 { 367 TRACE(("return_free_message: returning msg %p\n", msg)); 368 369 acquire_spinlock_nocheck(&sFreeMessageSpinlock); 370 msg->next = sFreeMessages; 371 sFreeMessages = msg; 372 sFreeMessageCount++; 373 release_spinlock(&sFreeMessageSpinlock); 374 } 375 376 377 static struct smp_msg * 378 check_for_message(int currentCPU, int *source_mailbox) 379 { 380 struct smp_msg *msg; 381 382 if (!sICIEnabled) 383 return NULL; 384 385 acquire_spinlock_nocheck(&sCPUMessageSpinlock[currentCPU]); 386 msg = sCPUMessages[currentCPU]; 387 if (msg != NULL) { 388 sCPUMessages[currentCPU] = msg->next; 389 release_spinlock(&sCPUMessageSpinlock[currentCPU]); 390 TRACE((" cpu %d: found msg %p in cpu mailbox\n", currentCPU, msg)); 391 *source_mailbox = MAILBOX_LOCAL; 392 } else { 393 // try getting one from the broadcast mailbox 394 395 release_spinlock(&sCPUMessageSpinlock[currentCPU]); 396 acquire_spinlock_nocheck(&sBroadcastMessageSpinlock); 397 398 msg = sBroadcastMessages; 399 while (msg != NULL) { 400 if (CHECK_BIT(msg->proc_bitmap, currentCPU) != 0) { 401 // we have handled this one already 402 msg = msg->next; 403 continue; 404 } 405 406 // mark it so we wont try to process this one again 407 msg->proc_bitmap = SET_BIT(msg->proc_bitmap, currentCPU); 408 *source_mailbox = MAILBOX_BCAST; 409 break; 410 } 411 release_spinlock(&sBroadcastMessageSpinlock); 412 TRACE((" cpu %d: found msg %p in broadcast mailbox\n", currentCPU, msg)); 413 } 414 return msg; 415 } 416 417 418 static void 419 finish_message_processing(int currentCPU, struct smp_msg *msg, int source_mailbox) 420 { 421 int old_refcount; 422 423 old_refcount = atomic_add(&msg->ref_count, -1); 424 if (old_refcount == 1) { 425 // we were the last one to decrement the ref_count 426 // it's our job to remove it from the list & possibly clean it up 427 struct smp_msg **mbox = NULL; 428 spinlock *spinlock = NULL; 429 430 // clean up the message from one of the mailboxes 431 switch (source_mailbox) { 432 case MAILBOX_BCAST: 433 mbox = &sBroadcastMessages; 434 spinlock = &sBroadcastMessageSpinlock; 435 break; 436 case MAILBOX_LOCAL: 437 mbox = &sCPUMessages[currentCPU]; 438 spinlock = &sCPUMessageSpinlock[currentCPU]; 439 break; 440 } 441 442 acquire_spinlock_nocheck(spinlock); 443 444 TRACE(("cleaning up message %p\n", msg)); 445 446 if (source_mailbox != MAILBOX_BCAST) { 447 // local mailbox -- the message has already been removed in 448 // check_for_message() 449 } else if (msg == *mbox) { 450 (*mbox) = msg->next; 451 } else { 452 // we need to walk to find the message in the list. 453 // we can't use any data found when previously walking through 454 // the list, since the list may have changed. But, we are guaranteed 455 // to at least have msg in it. 456 struct smp_msg *last = NULL; 457 struct smp_msg *msg1; 458 459 msg1 = *mbox; 460 while (msg1 != NULL && msg1 != msg) { 461 last = msg1; 462 msg1 = msg1->next; 463 } 464 465 // by definition, last must be something 466 if (msg1 == msg && last != NULL) 467 last->next = msg->next; 468 else 469 panic("last == NULL or msg != msg1"); 470 } 471 472 release_spinlock(spinlock); 473 474 if ((msg->flags & SMP_MSG_FLAG_FREE_ARG) != 0 && msg->data_ptr != NULL) 475 free(msg->data_ptr); 476 477 if (msg->flags & SMP_MSG_FLAG_SYNC) { 478 msg->done = true; 479 // the caller cpu should now free the message 480 } else { 481 // in the !SYNC case, we get to free the message 482 return_free_message(msg); 483 } 484 } 485 } 486 487 488 static int32 489 process_pending_ici(int32 currentCPU) 490 { 491 struct smp_msg *msg; 492 bool haltCPU = false; 493 int sourceMailbox = 0; 494 int retval = B_HANDLED_INTERRUPT; 495 496 msg = check_for_message(currentCPU, &sourceMailbox); 497 if (msg == NULL) 498 return B_ENTRY_NOT_FOUND; 499 500 TRACE((" cpu %ld message = %ld\n", currentCPU, msg->message)); 501 502 switch (msg->message) { 503 case SMP_MSG_INVALIDATE_PAGE_RANGE: 504 arch_cpu_invalidate_TLB_range((addr_t)msg->data, (addr_t)msg->data2); 505 break; 506 case SMP_MSG_INVALIDATE_PAGE_LIST: 507 arch_cpu_invalidate_TLB_list((addr_t *)msg->data, (int)msg->data2); 508 break; 509 case SMP_MSG_USER_INVALIDATE_PAGES: 510 arch_cpu_user_TLB_invalidate(); 511 break; 512 case SMP_MSG_GLOBAL_INVALIDATE_PAGES: 513 arch_cpu_global_TLB_invalidate(); 514 break; 515 case SMP_MSG_CPU_HALT: 516 haltCPU = true; 517 break; 518 case SMP_MSG_CALL_FUNCTION: 519 { 520 smp_call_func func = (smp_call_func)msg->data_ptr; 521 func(msg->data, currentCPU, msg->data2, msg->data3); 522 break; 523 } 524 case SMP_MSG_RESCHEDULE_IF_IDLE: 525 { 526 struct thread* thread = thread_get_current_thread(); 527 if (thread->priority == B_IDLE_PRIORITY) 528 thread->cpu->invoke_scheduler = true; 529 break; 530 } 531 532 default: 533 dprintf("smp_intercpu_int_handler: got unknown message %ld\n", msg->message); 534 } 535 536 // finish dealing with this message, possibly removing it from the list 537 finish_message_processing(currentCPU, msg, sourceMailbox); 538 539 // special case for the halt message 540 if (haltCPU) 541 debug_trap_cpu_in_kdl(false); 542 543 return retval; 544 } 545 546 547 #if B_DEBUG_SPINLOCK_CONTENTION 548 549 static uint64 550 get_spinlock_counter(spinlock* lock) 551 { 552 uint32 high; 553 uint32 low; 554 do { 555 high = (uint32)atomic_get(&lock->count_high); 556 low = (uint32)atomic_get(&lock->count_low); 557 } while (high != atomic_get(&lock->count_high)); 558 559 return ((uint64)high << 32) | low; 560 } 561 562 563 static status_t 564 spinlock_contention_syscall(const char* subsystem, uint32 function, 565 void* buffer, size_t bufferSize) 566 { 567 spinlock_contention_info info; 568 569 if (function != GET_SPINLOCK_CONTENTION_INFO) 570 return B_BAD_VALUE; 571 572 if (bufferSize < sizeof(spinlock_contention_info)) 573 return B_BAD_VALUE; 574 575 info.thread_spinlock_counter = get_spinlock_counter(&gThreadSpinlock); 576 info.team_spinlock_counter = get_spinlock_counter(&gTeamSpinlock); 577 578 if (!IS_USER_ADDRESS(buffer) 579 || user_memcpy(buffer, &info, sizeof(info)) != B_OK) { 580 return B_BAD_ADDRESS; 581 } 582 583 return B_OK; 584 } 585 586 #endif // B_DEBUG_SPINLOCK_CONTENTION 587 588 589 // #pragma mark - 590 591 592 int 593 smp_intercpu_int_handler(void) 594 { 595 int currentCPU = smp_get_current_cpu(); 596 597 TRACE(("smp_intercpu_int_handler: entry on cpu %d\n", currentCPU)); 598 599 process_all_pending_ici(currentCPU); 600 601 TRACE(("smp_intercpu_int_handler: done\n")); 602 603 return B_HANDLED_INTERRUPT; 604 } 605 606 607 void 608 smp_send_ici(int32 targetCPU, int32 message, uint32 data, uint32 data2, uint32 data3, 609 void *data_ptr, uint32 flags) 610 { 611 struct smp_msg *msg; 612 613 TRACE(("smp_send_ici: target 0x%lx, mess 0x%lx, data 0x%lx, data2 0x%lx, data3 0x%lx, ptr %p, flags 0x%lx\n", 614 targetCPU, message, data, data2, data3, data_ptr, flags)); 615 616 if (sICIEnabled) { 617 int state; 618 int currentCPU; 619 620 // find_free_message leaves interrupts disabled 621 state = find_free_message(&msg); 622 623 currentCPU = smp_get_current_cpu(); 624 if (targetCPU == currentCPU) { 625 return_free_message(msg); 626 restore_interrupts(state); 627 return; // nope, cant do that 628 } 629 630 // set up the message 631 msg->message = message; 632 msg->data = data; 633 msg->data2 = data2; 634 msg->data3 = data3; 635 msg->data_ptr = data_ptr; 636 msg->ref_count = 1; 637 msg->flags = flags; 638 msg->done = false; 639 640 // stick it in the appropriate cpu's mailbox 641 acquire_spinlock_nocheck(&sCPUMessageSpinlock[targetCPU]); 642 msg->next = sCPUMessages[targetCPU]; 643 sCPUMessages[targetCPU] = msg; 644 release_spinlock(&sCPUMessageSpinlock[targetCPU]); 645 646 arch_smp_send_ici(targetCPU); 647 648 if (flags & SMP_MSG_FLAG_SYNC) { 649 // wait for the other cpu to finish processing it 650 // the interrupt handler will ref count it to <0 651 // if the message is sync after it has removed it from the mailbox 652 while (msg->done == false) { 653 process_all_pending_ici(currentCPU); 654 PAUSE(); 655 } 656 // for SYNC messages, it's our responsibility to put it 657 // back into the free list 658 return_free_message(msg); 659 } 660 661 restore_interrupts(state); 662 } 663 } 664 665 666 void 667 smp_send_multicast_ici(cpu_mask_t cpuMask, int32 message, uint32 data, 668 uint32 data2, uint32 data3, void *data_ptr, uint32 flags) 669 { 670 if (!sICIEnabled) 671 return; 672 673 int currentCPU = smp_get_current_cpu(); 674 cpuMask &= ~((cpu_mask_t)1 << currentCPU) 675 & (((cpu_mask_t)1 << sNumCPUs) - 1); 676 if (cpuMask == 0) { 677 panic("smp_send_multicast_ici(): 0 CPU mask"); 678 return; 679 } 680 681 // count target CPUs 682 int32 targetCPUs = 0; 683 for (int32 i = 0; i < sNumCPUs; i++) { 684 if ((cpuMask & (cpu_mask_t)1 << i) != 0) 685 targetCPUs++; 686 } 687 688 // find_free_message leaves interrupts disabled 689 struct smp_msg *msg; 690 int state = find_free_message(&msg); 691 692 msg->message = message; 693 msg->data = data; 694 msg->data2 = data2; 695 msg->data3 = data3; 696 msg->data_ptr = data_ptr; 697 msg->ref_count = targetCPUs; 698 msg->flags = flags; 699 msg->proc_bitmap = ~cpuMask; 700 msg->done = false; 701 702 // stick it in the broadcast mailbox 703 acquire_spinlock_nocheck(&sBroadcastMessageSpinlock); 704 msg->next = sBroadcastMessages; 705 sBroadcastMessages = msg; 706 release_spinlock(&sBroadcastMessageSpinlock); 707 708 arch_smp_send_broadcast_ici(); 709 // TODO: Introduce a call that only bothers the target CPUs! 710 711 if (flags & SMP_MSG_FLAG_SYNC) { 712 // wait for the other cpus to finish processing it 713 // the interrupt handler will ref count it to <0 714 // if the message is sync after it has removed it from the mailbox 715 while (msg->done == false) { 716 process_all_pending_ici(currentCPU); 717 PAUSE(); 718 } 719 720 // for SYNC messages, it's our responsibility to put it 721 // back into the free list 722 return_free_message(msg); 723 } 724 725 restore_interrupts(state); 726 } 727 728 729 void 730 smp_send_broadcast_ici(int32 message, uint32 data, uint32 data2, uint32 data3, 731 void *data_ptr, uint32 flags) 732 { 733 struct smp_msg *msg; 734 735 TRACE(("smp_send_broadcast_ici: cpu %ld mess 0x%lx, data 0x%lx, data2 0x%lx, data3 0x%lx, ptr %p, flags 0x%lx\n", 736 smp_get_current_cpu(), message, data, data2, data3, data_ptr, flags)); 737 738 if (sICIEnabled) { 739 int state; 740 int currentCPU; 741 742 // find_free_message leaves interrupts disabled 743 state = find_free_message(&msg); 744 745 currentCPU = smp_get_current_cpu(); 746 747 msg->message = message; 748 msg->data = data; 749 msg->data2 = data2; 750 msg->data3 = data3; 751 msg->data_ptr = data_ptr; 752 msg->ref_count = sNumCPUs - 1; 753 msg->flags = flags; 754 msg->proc_bitmap = SET_BIT(0, currentCPU); 755 msg->done = false; 756 757 TRACE(("smp_send_broadcast_ici%d: inserting msg %p into broadcast mbox\n", 758 currentCPU, msg)); 759 760 // stick it in the appropriate cpu's mailbox 761 acquire_spinlock_nocheck(&sBroadcastMessageSpinlock); 762 msg->next = sBroadcastMessages; 763 sBroadcastMessages = msg; 764 release_spinlock(&sBroadcastMessageSpinlock); 765 766 arch_smp_send_broadcast_ici(); 767 768 TRACE(("smp_send_broadcast_ici: sent interrupt\n")); 769 770 if (flags & SMP_MSG_FLAG_SYNC) { 771 // wait for the other cpus to finish processing it 772 // the interrupt handler will ref count it to <0 773 // if the message is sync after it has removed it from the mailbox 774 TRACE(("smp_send_broadcast_ici: waiting for ack\n")); 775 776 while (msg->done == false) { 777 process_all_pending_ici(currentCPU); 778 PAUSE(); 779 } 780 781 TRACE(("smp_send_broadcast_ici: returning message to free list\n")); 782 783 // for SYNC messages, it's our responsibility to put it 784 // back into the free list 785 return_free_message(msg); 786 } 787 788 restore_interrupts(state); 789 } 790 791 TRACE(("smp_send_broadcast_ici: done\n")); 792 } 793 794 795 bool 796 smp_trap_non_boot_cpus(int32 cpu) 797 { 798 if (cpu > 0) { 799 #if B_DEBUG_SPINLOCK_CONTENTION 800 boot_cpu_spin[cpu].lock = 1; 801 #else 802 boot_cpu_spin[cpu] = 1; 803 #endif 804 acquire_spinlock_nocheck(&boot_cpu_spin[cpu]); 805 return false; 806 } 807 808 return true; 809 } 810 811 812 void 813 smp_wake_up_non_boot_cpus() 814 { 815 int i; 816 817 // ICIs were previously being ignored 818 if (sNumCPUs > 1) 819 sICIEnabled = true; 820 821 // resume non boot CPUs 822 for (i = 1; i < sNumCPUs; i++) { 823 release_spinlock(&boot_cpu_spin[i]); 824 } 825 } 826 827 /* have all cpus spin until all have run */ 828 void 829 smp_cpu_rendezvous(volatile uint32 *var, int current_cpu) 830 { 831 atomic_or((vint32*)var, 1 << current_cpu); 832 833 while (*var != (((uint32)1 << sNumCPUs) - 1)) 834 PAUSE(); 835 } 836 837 status_t 838 smp_init(kernel_args *args) 839 { 840 struct smp_msg *msg; 841 int i; 842 843 TRACE(("smp_init: entry\n")); 844 845 #if DEBUG_SPINLOCKS 846 add_debugger_command_etc("spinlock", &dump_spinlock, 847 "Dump info on a spinlock", 848 "\n" 849 "Dumps info on a spinlock.\n", 0); 850 #endif 851 add_debugger_command_etc("ici", &dump_ici_messages, 852 "Dump info on pending ICI messages", 853 "\n" 854 "Dumps info on pending ICI messages.\n", 0); 855 add_debugger_command_etc("ici_message", &dump_ici_message, 856 "Dump info on an ICI message", 857 "\n" 858 "Dumps info on an ICI message.\n", 0); 859 860 if (args->num_cpus > 1) { 861 sFreeMessages = NULL; 862 sFreeMessageCount = 0; 863 for (i = 0; i < MSG_POOL_SIZE; i++) { 864 msg = (struct smp_msg *)malloc(sizeof(struct smp_msg)); 865 if (msg == NULL) { 866 panic("error creating smp mailboxes\n"); 867 return B_ERROR; 868 } 869 memset(msg, 0, sizeof(struct smp_msg)); 870 msg->next = sFreeMessages; 871 sFreeMessages = msg; 872 sFreeMessageCount++; 873 } 874 sNumCPUs = args->num_cpus; 875 } 876 TRACE(("smp_init: calling arch_smp_init\n")); 877 878 return arch_smp_init(args); 879 } 880 881 882 status_t 883 smp_per_cpu_init(kernel_args *args, int32 cpu) 884 { 885 return arch_smp_per_cpu_init(args, cpu); 886 } 887 888 889 status_t 890 smp_init_post_generic_syscalls(void) 891 { 892 #if B_DEBUG_SPINLOCK_CONTENTION 893 return register_generic_syscall(SPINLOCK_CONTENTION, 894 &spinlock_contention_syscall, 0, 0); 895 #else 896 return B_OK; 897 #endif 898 } 899 900 901 void 902 smp_set_num_cpus(int32 numCPUs) 903 { 904 sNumCPUs = numCPUs; 905 } 906 907 908 int32 909 smp_get_num_cpus() 910 { 911 return sNumCPUs; 912 } 913 914 915 int32 916 smp_get_current_cpu(void) 917 { 918 return thread_get_current_thread()->cpu->cpu_num; 919 } 920 921 922 // #pragma mark - 923 // public exported functions 924 925 926 void 927 call_all_cpus(void (*func)(void *, int), void *cookie) 928 { 929 cpu_status state = disable_interrupts(); 930 931 if (smp_get_num_cpus() > 1) { 932 smp_send_broadcast_ici(SMP_MSG_CALL_FUNCTION, (uint32)cookie, 933 0, 0, (void *)func, SMP_MSG_FLAG_ASYNC); 934 } 935 936 // we need to call this function ourselves as well 937 func(cookie, smp_get_current_cpu()); 938 939 restore_interrupts(state); 940 } 941 942 void 943 call_all_cpus_sync(void (*func)(void *, int), void *cookie) 944 { 945 cpu_status state = disable_interrupts(); 946 947 if (smp_get_num_cpus() > 1) { 948 smp_send_broadcast_ici(SMP_MSG_CALL_FUNCTION, (uint32)cookie, 949 0, 0, (void *)func, SMP_MSG_FLAG_SYNC); 950 } 951 952 // we need to call this function ourselves as well 953 func(cookie, smp_get_current_cpu()); 954 955 restore_interrupts(state); 956 } 957 958 959 void 960 memory_read_barrier(void) 961 { 962 arch_cpu_memory_read_barrier(); 963 } 964 965 966 void 967 memory_write_barrier(void) 968 { 969 arch_cpu_memory_write_barrier(); 970 } 971 972