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