1 /* 2 * Copyright 2013, Paweł Dziepak, pdziepak@quarnos.org. 3 * Copyright 2008-2009, Ingo Weinhold, ingo_weinhold@gmx.de. 4 * Copyright 2002-2010, Axel Dörfler, axeld@pinc-software.de. 5 * Distributed under the terms of the MIT License. 6 * 7 * Copyright 2001-2002, Travis Geiselbrecht. All rights reserved. 8 * Distributed under the terms of the NewOS License. 9 */ 10 11 12 /*! Functionality for symetrical multi-processors */ 13 14 15 #include <smp.h> 16 17 #include <stdlib.h> 18 #include <string.h> 19 20 #include <arch/atomic.h> 21 #include <arch/cpu.h> 22 #include <arch/debug.h> 23 #include <arch/int.h> 24 #include <arch/smp.h> 25 #include <boot/kernel_args.h> 26 #include <cpu.h> 27 #include <generic_syscall.h> 28 #include <int.h> 29 #include <spinlock_contention.h> 30 #include <thread.h> 31 #include <util/atomic.h> 32 #if DEBUG_SPINLOCK_LATENCIES 33 # include <safemode.h> 34 #endif 35 36 #include "kernel_debug_config.h" 37 38 39 //#define TRACE_SMP 40 #ifdef TRACE_SMP 41 # define TRACE(...) dprintf_no_syslog(__VA_ARGS__) 42 #else 43 # define TRACE(...) (void)0 44 #endif 45 46 47 #undef try_acquire_spinlock 48 #undef acquire_spinlock 49 #undef release_spinlock 50 51 #undef try_acquire_read_spinlock 52 #undef acquire_read_spinlock 53 #undef release_read_spinlock 54 #undef try_acquire_write_spinlock 55 #undef acquire_write_spinlock 56 #undef release_write_spinlock 57 58 #undef try_acquire_write_seqlock 59 #undef acquire_write_seqlock 60 #undef release_write_seqlock 61 #undef acquire_read_seqlock 62 #undef release_read_seqlock 63 64 65 #define MSG_POOL_SIZE (SMP_MAX_CPUS * 4) 66 67 // These macros define the number of unsuccessful iterations in 68 // acquire_spinlock() and acquire_spinlock_nocheck() after which the functions 69 // panic(), assuming a deadlock. 70 #define SPINLOCK_DEADLOCK_COUNT 100000000 71 #define SPINLOCK_DEADLOCK_COUNT_NO_CHECK 2000000000 72 73 74 struct smp_msg { 75 struct smp_msg *next; 76 int32 message; 77 addr_t data; 78 addr_t data2; 79 addr_t data3; 80 void *data_ptr; 81 uint32 flags; 82 int32 ref_count; 83 int32 done; 84 CPUSet proc_bitmap; 85 }; 86 87 enum mailbox_source { 88 MAILBOX_LOCAL, 89 MAILBOX_BCAST, 90 }; 91 92 static int32 sBootCPUSpin = 0; 93 94 static int32 sEarlyCPUCallCount; 95 static CPUSet sEarlyCPUCallSet; 96 static void (*sEarlyCPUCallFunction)(void*, int); 97 void* sEarlyCPUCallCookie; 98 99 static struct smp_msg* sFreeMessages = NULL; 100 static int32 sFreeMessageCount = 0; 101 static spinlock sFreeMessageSpinlock = B_SPINLOCK_INITIALIZER; 102 103 static struct smp_msg* sCPUMessages[SMP_MAX_CPUS] = { NULL, }; 104 105 static struct smp_msg* sBroadcastMessages = NULL; 106 static spinlock sBroadcastMessageSpinlock = B_SPINLOCK_INITIALIZER; 107 static int32 sBroadcastMessageCounter; 108 109 static bool sICIEnabled = false; 110 static int32 sNumCPUs = 1; 111 112 static int32 process_pending_ici(int32 currentCPU); 113 114 115 #if DEBUG_SPINLOCKS 116 #define NUM_LAST_CALLERS 32 117 118 static struct { 119 void *caller; 120 spinlock *lock; 121 } sLastCaller[NUM_LAST_CALLERS]; 122 123 static int32 sLastIndex = 0; 124 // Is incremented atomically. Must be % NUM_LAST_CALLERS before being used 125 // as index into sLastCaller. Note, that it has to be casted to uint32 126 // before applying the modulo operation, since otherwise after overflowing 127 // that would yield negative indices. 128 129 130 static void 131 push_lock_caller(void* caller, spinlock* lock) 132 { 133 int32 index = (uint32)atomic_add(&sLastIndex, 1) % NUM_LAST_CALLERS; 134 135 sLastCaller[index].caller = caller; 136 sLastCaller[index].lock = lock; 137 } 138 139 140 static void* 141 find_lock_caller(spinlock* lock) 142 { 143 int32 lastIndex = (uint32)atomic_get(&sLastIndex) % NUM_LAST_CALLERS; 144 145 for (int32 i = 0; i < NUM_LAST_CALLERS; i++) { 146 int32 index = (NUM_LAST_CALLERS + lastIndex - 1 - i) % NUM_LAST_CALLERS; 147 if (sLastCaller[index].lock == lock) 148 return sLastCaller[index].caller; 149 } 150 151 return NULL; 152 } 153 154 155 int 156 dump_spinlock(int argc, char** argv) 157 { 158 if (argc != 2) { 159 print_debugger_command_usage(argv[0]); 160 return 0; 161 } 162 163 uint64 address; 164 if (!evaluate_debug_expression(argv[1], &address, false)) 165 return 0; 166 167 spinlock* lock = (spinlock*)(addr_t)address; 168 kprintf("spinlock %p:\n", lock); 169 bool locked = B_SPINLOCK_IS_LOCKED(lock); 170 if (locked) { 171 kprintf(" locked from %p\n", find_lock_caller(lock)); 172 } else 173 kprintf(" not locked\n"); 174 175 return 0; 176 } 177 178 179 #endif // DEBUG_SPINLOCKS 180 181 182 #if DEBUG_SPINLOCK_LATENCIES 183 184 185 #define NUM_LATENCY_LOCKS 4 186 #define DEBUG_LATENCY 200 187 188 189 static struct { 190 spinlock *lock; 191 bigtime_t timestamp; 192 } sLatency[SMP_MAX_CPUS][NUM_LATENCY_LOCKS]; 193 194 static int32 sLatencyIndex[SMP_MAX_CPUS]; 195 static bool sEnableLatencyCheck; 196 197 198 static void 199 push_latency(spinlock* lock) 200 { 201 if (!sEnableLatencyCheck) 202 return; 203 204 int32 cpu = smp_get_current_cpu(); 205 int32 index = (++sLatencyIndex[cpu]) % NUM_LATENCY_LOCKS; 206 207 sLatency[cpu][index].lock = lock; 208 sLatency[cpu][index].timestamp = system_time(); 209 } 210 211 212 static void 213 test_latency(spinlock* lock) 214 { 215 if (!sEnableLatencyCheck) 216 return; 217 218 int32 cpu = smp_get_current_cpu(); 219 220 for (int32 i = 0; i < NUM_LATENCY_LOCKS; i++) { 221 if (sLatency[cpu][i].lock == lock) { 222 bigtime_t diff = system_time() - sLatency[cpu][i].timestamp; 223 if (diff > DEBUG_LATENCY && diff < 500000) { 224 panic("spinlock %p were held for %lld usecs (%d allowed)\n", 225 lock, diff, DEBUG_LATENCY); 226 } 227 228 sLatency[cpu][i].lock = NULL; 229 } 230 } 231 } 232 233 234 #endif // DEBUG_SPINLOCK_LATENCIES 235 236 237 int 238 dump_ici_messages(int argc, char** argv) 239 { 240 // count broadcast messages 241 int32 count = 0; 242 int32 doneCount = 0; 243 int32 unreferencedCount = 0; 244 smp_msg* message = sBroadcastMessages; 245 while (message != NULL) { 246 count++; 247 if (message->done == 1) 248 doneCount++; 249 if (message->ref_count <= 0) 250 unreferencedCount++; 251 message = message->next; 252 } 253 254 kprintf("ICI broadcast messages: %" B_PRId32 ", first: %p\n", count, 255 sBroadcastMessages); 256 kprintf(" done: %" B_PRId32 "\n", doneCount); 257 kprintf(" unreferenced: %" B_PRId32 "\n", unreferencedCount); 258 259 // count per-CPU messages 260 for (int32 i = 0; i < sNumCPUs; i++) { 261 count = 0; 262 message = sCPUMessages[i]; 263 while (message != NULL) { 264 count++; 265 message = message->next; 266 } 267 268 kprintf("CPU %" B_PRId32 " messages: %" B_PRId32 ", first: %p\n", i, 269 count, sCPUMessages[i]); 270 } 271 272 return 0; 273 } 274 275 276 int 277 dump_ici_message(int argc, char** argv) 278 { 279 if (argc != 2) { 280 print_debugger_command_usage(argv[0]); 281 return 0; 282 } 283 284 uint64 address; 285 if (!evaluate_debug_expression(argv[1], &address, false)) 286 return 0; 287 288 smp_msg* message = (smp_msg*)(addr_t)address; 289 kprintf("ICI message %p:\n", message); 290 kprintf(" next: %p\n", message->next); 291 kprintf(" message: %" B_PRId32 "\n", message->message); 292 kprintf(" data: 0x%lx\n", message->data); 293 kprintf(" data2: 0x%lx\n", message->data2); 294 kprintf(" data3: 0x%lx\n", message->data3); 295 kprintf(" data_ptr: %p\n", message->data_ptr); 296 kprintf(" flags: %" B_PRIx32 "\n", message->flags); 297 kprintf(" ref_count: %" B_PRIx32 "\n", message->ref_count); 298 kprintf(" done: %s\n", message->done == 1 ? "true" : "false"); 299 300 kprintf(" proc_bitmap: "); 301 for (int32 i = 0; i < sNumCPUs; i++) { 302 if (message->proc_bitmap.GetBit(i)) 303 kprintf("%s%" B_PRId32, i != 0 ? ", " : "", i); 304 } 305 kprintf("\n"); 306 307 return 0; 308 } 309 310 311 static inline void 312 process_all_pending_ici(int32 currentCPU) 313 { 314 while (process_pending_ici(currentCPU) != B_ENTRY_NOT_FOUND) 315 ; 316 } 317 318 319 bool 320 try_acquire_spinlock(spinlock* lock) 321 { 322 #if DEBUG_SPINLOCKS 323 if (are_interrupts_enabled()) { 324 panic("try_acquire_spinlock: attempt to acquire lock %p with " 325 "interrupts enabled", lock); 326 } 327 #endif 328 329 #if B_DEBUG_SPINLOCK_CONTENTION 330 if (atomic_add(&lock->lock, 1) != 0) 331 return false; 332 #else 333 if (atomic_get_and_set((int32*)lock, 1) != 0) 334 return false; 335 336 # if DEBUG_SPINLOCKS 337 push_lock_caller(arch_debug_get_caller(), lock); 338 # endif 339 #endif 340 341 return true; 342 } 343 344 345 void 346 acquire_spinlock(spinlock* lock) 347 { 348 #if DEBUG_SPINLOCKS 349 if (are_interrupts_enabled()) { 350 panic("acquire_spinlock: attempt to acquire lock %p with interrupts " 351 "enabled", lock); 352 } 353 #endif 354 355 if (sNumCPUs > 1) { 356 int currentCPU = smp_get_current_cpu(); 357 #if B_DEBUG_SPINLOCK_CONTENTION 358 while (atomic_add(&lock->lock, 1) != 0) 359 process_all_pending_ici(currentCPU); 360 #else 361 while (1) { 362 uint32 count = 0; 363 while (lock->lock != 0) { 364 if (++count == SPINLOCK_DEADLOCK_COUNT) { 365 panic("acquire_spinlock(): Failed to acquire spinlock %p " 366 "for a long time!", lock); 367 count = 0; 368 } 369 370 process_all_pending_ici(currentCPU); 371 cpu_wait(&lock->lock, 0); 372 } 373 if (atomic_get_and_set(&lock->lock, 1) == 0) 374 break; 375 } 376 377 # if DEBUG_SPINLOCKS 378 push_lock_caller(arch_debug_get_caller(), lock); 379 # endif 380 #endif 381 } else { 382 #if DEBUG_SPINLOCKS 383 int32 oldValue; 384 oldValue = atomic_get_and_set(&lock->lock, 1); 385 if (oldValue != 0) { 386 panic("acquire_spinlock: attempt to acquire lock %p twice on " 387 "non-SMP system (last caller: %p, value %" B_PRId32 ")", lock, 388 find_lock_caller(lock), oldValue); 389 } 390 391 push_lock_caller(arch_debug_get_caller(), lock); 392 #endif 393 } 394 #if DEBUG_SPINLOCK_LATENCIES 395 push_latency(lock); 396 #endif 397 } 398 399 400 static void 401 acquire_spinlock_nocheck(spinlock *lock) 402 { 403 #if DEBUG_SPINLOCKS 404 if (are_interrupts_enabled()) { 405 panic("acquire_spinlock_nocheck: attempt to acquire lock %p with " 406 "interrupts enabled", lock); 407 } 408 #endif 409 410 if (sNumCPUs > 1) { 411 #if B_DEBUG_SPINLOCK_CONTENTION 412 while (atomic_add(&lock->lock, 1) != 0) { 413 } 414 #else 415 while (1) { 416 uint32 count = 0; 417 while (lock->lock != 0) { 418 if (++count == SPINLOCK_DEADLOCK_COUNT_NO_CHECK) { 419 panic("acquire_spinlock(): Failed to acquire spinlock %p " 420 "for a long time!", lock); 421 count = 0; 422 } 423 424 cpu_wait(&lock->lock, 0); 425 } 426 427 if (atomic_get_and_set(&lock->lock, 1) == 0) 428 break; 429 } 430 #endif 431 } else { 432 #if DEBUG_SPINLOCKS 433 if (atomic_get_and_set(&lock->lock, 1) != 0) { 434 panic("acquire_spinlock_nocheck: attempt to acquire lock %p twice " 435 "on non-SMP system\n", lock); 436 } 437 #endif 438 } 439 } 440 441 442 /*! Equivalent to acquire_spinlock(), save for currentCPU parameter. */ 443 static void 444 acquire_spinlock_cpu(int32 currentCPU, spinlock *lock) 445 { 446 #if DEBUG_SPINLOCKS 447 if (are_interrupts_enabled()) { 448 panic("acquire_spinlock_cpu: attempt to acquire lock %p with " 449 "interrupts enabled", lock); 450 } 451 #endif 452 453 if (sNumCPUs > 1) { 454 #if B_DEBUG_SPINLOCK_CONTENTION 455 while (atomic_add(&lock->lock, 1) != 0) 456 process_all_pending_ici(currentCPU); 457 #else 458 while (1) { 459 uint32 count = 0; 460 while (lock->lock != 0) { 461 if (++count == SPINLOCK_DEADLOCK_COUNT) { 462 panic("acquire_spinlock_cpu(): Failed to acquire spinlock " 463 "%p for a long time!", lock); 464 count = 0; 465 } 466 467 process_all_pending_ici(currentCPU); 468 cpu_wait(&lock->lock, 0); 469 } 470 if (atomic_get_and_set(&lock->lock, 1) == 0) 471 break; 472 } 473 474 # if DEBUG_SPINLOCKS 475 push_lock_caller(arch_debug_get_caller(), lock); 476 # endif 477 #endif 478 } else { 479 #if DEBUG_SPINLOCKS 480 int32 oldValue; 481 oldValue = atomic_get_and_set(&lock->lock, 1); 482 if (oldValue != 0) { 483 panic("acquire_spinlock_cpu(): attempt to acquire lock %p twice on " 484 "non-SMP system (last caller: %p, value %" B_PRId32 ")", lock, 485 find_lock_caller(lock), oldValue); 486 } 487 488 push_lock_caller(arch_debug_get_caller(), lock); 489 #endif 490 } 491 } 492 493 494 void 495 release_spinlock(spinlock *lock) 496 { 497 #if DEBUG_SPINLOCK_LATENCIES 498 test_latency(lock); 499 #endif 500 501 if (sNumCPUs > 1) { 502 if (are_interrupts_enabled()) 503 panic("release_spinlock: attempt to release lock %p with " 504 "interrupts enabled\n", lock); 505 #if B_DEBUG_SPINLOCK_CONTENTION 506 { 507 int32 count = atomic_and(&lock->lock, 0) - 1; 508 if (count < 0) { 509 panic("release_spinlock: lock %p was already released\n", lock); 510 } else { 511 // add to the total count -- deal with carry manually 512 if ((uint32)atomic_add(&lock->count_low, count) + count 513 < (uint32)count) { 514 atomic_add(&lock->count_high, 1); 515 } 516 } 517 } 518 #elif DEBUG_SPINLOCKS 519 if (atomic_get_and_set(&lock->lock, 0) != 1) 520 panic("release_spinlock: lock %p was already released\n", lock); 521 #else 522 atomic_set(&lock->lock, 0); 523 #endif 524 } else { 525 #if DEBUG_SPINLOCKS 526 if (are_interrupts_enabled()) { 527 panic("release_spinlock: attempt to release lock %p with " 528 "interrupts enabled\n", lock); 529 } 530 if (atomic_get_and_set(&lock->lock, 0) != 1) 531 panic("release_spinlock: lock %p was already released\n", lock); 532 #endif 533 #if DEBUG_SPINLOCK_LATENCIES 534 test_latency(lock); 535 #endif 536 } 537 } 538 539 540 bool 541 try_acquire_write_spinlock(rw_spinlock* lock) 542 { 543 #if DEBUG_SPINLOCKS 544 if (are_interrupts_enabled()) { 545 panic("try_acquire_write_spinlock: attempt to acquire lock %p with " 546 "interrupts enabled", lock); 547 } 548 549 if (sNumCPUs < 2 && lock->lock != 0) { 550 panic("try_acquire_write_spinlock(): attempt to acquire lock %p twice " 551 "on non-SMP system", lock); 552 } 553 #endif 554 555 return atomic_test_and_set(&lock->lock, 1u << 31, 0) == 0; 556 } 557 558 559 void 560 acquire_write_spinlock(rw_spinlock* lock) 561 { 562 #if DEBUG_SPINLOCKS 563 if (are_interrupts_enabled()) { 564 panic("acquire_write_spinlock: attempt to acquire lock %p with " 565 "interrupts enabled", lock); 566 } 567 #endif 568 569 uint32 count = 0; 570 int currentCPU = smp_get_current_cpu(); 571 while (true) { 572 if (try_acquire_write_spinlock(lock)) 573 break; 574 575 while (lock->lock != 0) { 576 if (++count == SPINLOCK_DEADLOCK_COUNT) { 577 panic("acquire_write_spinlock(): Failed to acquire spinlock %p " 578 "for a long time!", lock); 579 count = 0; 580 } 581 582 process_all_pending_ici(currentCPU); 583 cpu_wait(&lock->lock, 0); 584 } 585 } 586 } 587 588 589 void 590 release_write_spinlock(rw_spinlock* lock) 591 { 592 #if DEBUG_SPINLOCKS 593 uint32 previous = atomic_get_and_set(&lock->lock, 0); 594 if ((previous & 1u << 31) == 0) { 595 panic("release_write_spinlock: lock %p was already released (value: " 596 "%#" B_PRIx32 ")\n", lock, previous); 597 } 598 #else 599 atomic_set(&lock->lock, 0); 600 #endif 601 } 602 603 604 bool 605 try_acquire_read_spinlock(rw_spinlock* lock) 606 { 607 #if DEBUG_SPINLOCKS 608 if (are_interrupts_enabled()) { 609 panic("try_acquire_read_spinlock: attempt to acquire lock %p with " 610 "interrupts enabled", lock); 611 } 612 613 if (sNumCPUs < 2 && lock->lock != 0) { 614 panic("try_acquire_read_spinlock(): attempt to acquire lock %p twice " 615 "on non-SMP system", lock); 616 } 617 #endif 618 619 uint32 previous = atomic_add(&lock->lock, 1); 620 return (previous & (1u << 31)) == 0; 621 } 622 623 624 void 625 acquire_read_spinlock(rw_spinlock* lock) 626 { 627 #if DEBUG_SPINLOCKS 628 if (are_interrupts_enabled()) { 629 panic("acquire_read_spinlock: attempt to acquire lock %p with " 630 "interrupts enabled", lock); 631 } 632 #endif 633 634 uint32 count = 0; 635 int currentCPU = smp_get_current_cpu(); 636 while (1) { 637 if (try_acquire_read_spinlock(lock)) 638 break; 639 640 while ((lock->lock & (1u << 31)) != 0) { 641 if (++count == SPINLOCK_DEADLOCK_COUNT) { 642 panic("acquire_read_spinlock(): Failed to acquire spinlock %p " 643 "for a long time!", lock); 644 count = 0; 645 } 646 647 process_all_pending_ici(currentCPU); 648 cpu_wait(&lock->lock, 0); 649 } 650 } 651 } 652 653 654 void 655 release_read_spinlock(rw_spinlock* lock) 656 { 657 #if DEBUG_SPINLOCKS 658 uint32 previous = atomic_add(&lock->lock, -1); 659 if ((previous & 1u << 31) != 0) { 660 panic("release_read_spinlock: lock %p was already released (value:" 661 " %#" B_PRIx32 ")\n", lock, previous); 662 } 663 #else 664 atomic_add(&lock->lock, -1); 665 #endif 666 667 } 668 669 670 bool 671 try_acquire_write_seqlock(seqlock* lock) { 672 bool succeed = try_acquire_spinlock(&lock->lock); 673 if (succeed) 674 atomic_add((int32*)&lock->count, 1); 675 return succeed; 676 } 677 678 679 void 680 acquire_write_seqlock(seqlock* lock) { 681 acquire_spinlock(&lock->lock); 682 atomic_add((int32*)&lock->count, 1); 683 } 684 685 686 void 687 release_write_seqlock(seqlock* lock) { 688 atomic_add((int32*)&lock->count, 1); 689 release_spinlock(&lock->lock); 690 } 691 692 693 uint32 694 acquire_read_seqlock(seqlock* lock) { 695 return atomic_get((int32*)&lock->count); 696 } 697 698 699 bool 700 release_read_seqlock(seqlock* lock, uint32 count) { 701 memory_read_barrier(); 702 703 uint32 current = *(volatile int32*)&lock->count; 704 705 if (count % 2 == 1 || current != count) { 706 cpu_pause(); 707 return false; 708 } 709 710 return true; 711 } 712 713 714 /*! Finds a free message and gets it. 715 NOTE: has side effect of disabling interrupts 716 return value is the former interrupt state 717 */ 718 static cpu_status 719 find_free_message(struct smp_msg** msg) 720 { 721 cpu_status state; 722 723 TRACE("find_free_message: entry\n"); 724 725 retry: 726 while (sFreeMessageCount <= 0) 727 cpu_pause(); 728 729 state = disable_interrupts(); 730 acquire_spinlock(&sFreeMessageSpinlock); 731 732 if (sFreeMessageCount <= 0) { 733 // someone grabbed one while we were getting the lock, 734 // go back to waiting for it 735 release_spinlock(&sFreeMessageSpinlock); 736 restore_interrupts(state); 737 goto retry; 738 } 739 740 *msg = sFreeMessages; 741 sFreeMessages = (*msg)->next; 742 sFreeMessageCount--; 743 744 release_spinlock(&sFreeMessageSpinlock); 745 746 TRACE("find_free_message: returning msg %p\n", *msg); 747 748 return state; 749 } 750 751 752 /*! Similar to find_free_message(), but expects the interrupts to be disabled 753 already. 754 */ 755 static void 756 find_free_message_interrupts_disabled(int32 currentCPU, 757 struct smp_msg** _message) 758 { 759 TRACE("find_free_message_interrupts_disabled: entry\n"); 760 761 acquire_spinlock_cpu(currentCPU, &sFreeMessageSpinlock); 762 while (sFreeMessageCount <= 0) { 763 release_spinlock(&sFreeMessageSpinlock); 764 process_all_pending_ici(currentCPU); 765 cpu_pause(); 766 acquire_spinlock_cpu(currentCPU, &sFreeMessageSpinlock); 767 } 768 769 *_message = sFreeMessages; 770 sFreeMessages = (*_message)->next; 771 sFreeMessageCount--; 772 773 release_spinlock(&sFreeMessageSpinlock); 774 775 TRACE("find_free_message_interrupts_disabled: returning msg %p\n", 776 *_message); 777 } 778 779 780 static void 781 return_free_message(struct smp_msg* msg) 782 { 783 TRACE("return_free_message: returning msg %p\n", msg); 784 785 acquire_spinlock_nocheck(&sFreeMessageSpinlock); 786 msg->next = sFreeMessages; 787 sFreeMessages = msg; 788 sFreeMessageCount++; 789 release_spinlock(&sFreeMessageSpinlock); 790 } 791 792 793 static struct smp_msg* 794 check_for_message(int currentCPU, mailbox_source& sourceMailbox) 795 { 796 if (!sICIEnabled) 797 return NULL; 798 799 struct smp_msg* msg = atomic_pointer_get(&sCPUMessages[currentCPU]); 800 if (msg != NULL) { 801 do { 802 cpu_pause(); 803 msg = atomic_pointer_get(&sCPUMessages[currentCPU]); 804 ASSERT(msg != NULL); 805 } while (atomic_pointer_test_and_set(&sCPUMessages[currentCPU], 806 msg->next, msg) != msg); 807 808 TRACE(" cpu %d: found msg %p in cpu mailbox\n", currentCPU, msg); 809 sourceMailbox = MAILBOX_LOCAL; 810 } else if (atomic_get(&get_cpu_struct()->ici_counter) 811 != atomic_get(&sBroadcastMessageCounter)) { 812 813 // try getting one from the broadcast mailbox 814 acquire_spinlock_nocheck(&sBroadcastMessageSpinlock); 815 816 msg = sBroadcastMessages; 817 while (msg != NULL) { 818 if (!msg->proc_bitmap.GetBit(currentCPU)) { 819 // we have handled this one already 820 msg = msg->next; 821 continue; 822 } 823 824 // mark it so we wont try to process this one again 825 msg->proc_bitmap.ClearBitAtomic(currentCPU); 826 atomic_add(&gCPU[currentCPU].ici_counter, 1); 827 828 sourceMailbox = MAILBOX_BCAST; 829 break; 830 } 831 release_spinlock(&sBroadcastMessageSpinlock); 832 833 if (msg != NULL) { 834 TRACE(" cpu %d: found msg %p in broadcast mailbox\n", currentCPU, 835 msg); 836 } 837 } 838 return msg; 839 } 840 841 842 static void 843 finish_message_processing(int currentCPU, struct smp_msg* msg, 844 mailbox_source sourceMailbox) 845 { 846 if (atomic_add(&msg->ref_count, -1) != 1) 847 return; 848 849 // we were the last one to decrement the ref_count 850 // it's our job to remove it from the list & possibly clean it up 851 852 // clean up the message 853 if (sourceMailbox == MAILBOX_BCAST) 854 acquire_spinlock_nocheck(&sBroadcastMessageSpinlock); 855 856 TRACE("cleaning up message %p\n", msg); 857 858 if (sourceMailbox != MAILBOX_BCAST) { 859 // local mailbox -- the message has already been removed in 860 // check_for_message() 861 } else if (msg == sBroadcastMessages) { 862 sBroadcastMessages = msg->next; 863 } else { 864 // we need to walk to find the message in the list. 865 // we can't use any data found when previously walking through 866 // the list, since the list may have changed. But, we are guaranteed 867 // to at least have msg in it. 868 struct smp_msg* last = NULL; 869 struct smp_msg* msg1; 870 871 msg1 = sBroadcastMessages; 872 while (msg1 != NULL && msg1 != msg) { 873 last = msg1; 874 msg1 = msg1->next; 875 } 876 877 // by definition, last must be something 878 if (msg1 == msg && last != NULL) 879 last->next = msg->next; 880 else 881 panic("last == NULL or msg != msg1"); 882 } 883 884 if (sourceMailbox == MAILBOX_BCAST) 885 release_spinlock(&sBroadcastMessageSpinlock); 886 887 if ((msg->flags & SMP_MSG_FLAG_FREE_ARG) != 0 && msg->data_ptr != NULL) 888 free(msg->data_ptr); 889 890 if ((msg->flags & SMP_MSG_FLAG_SYNC) != 0) { 891 atomic_set(&msg->done, 1); 892 // the caller cpu should now free the message 893 } else { 894 // in the !SYNC case, we get to free the message 895 return_free_message(msg); 896 } 897 } 898 899 900 static status_t 901 process_pending_ici(int32 currentCPU) 902 { 903 mailbox_source sourceMailbox; 904 struct smp_msg* msg = check_for_message(currentCPU, sourceMailbox); 905 if (msg == NULL) 906 return B_ENTRY_NOT_FOUND; 907 908 TRACE(" cpu %ld message = %ld\n", currentCPU, msg->message); 909 910 bool haltCPU = false; 911 912 switch (msg->message) { 913 case SMP_MSG_INVALIDATE_PAGE_RANGE: 914 arch_cpu_invalidate_TLB_range(msg->data, msg->data2); 915 break; 916 case SMP_MSG_INVALIDATE_PAGE_LIST: 917 arch_cpu_invalidate_TLB_list((addr_t*)msg->data, (int)msg->data2); 918 break; 919 case SMP_MSG_USER_INVALIDATE_PAGES: 920 arch_cpu_user_TLB_invalidate(); 921 break; 922 case SMP_MSG_GLOBAL_INVALIDATE_PAGES: 923 arch_cpu_global_TLB_invalidate(); 924 break; 925 case SMP_MSG_CPU_HALT: 926 haltCPU = true; 927 break; 928 case SMP_MSG_CALL_FUNCTION: 929 { 930 smp_call_func func = (smp_call_func)msg->data_ptr; 931 func(msg->data, currentCPU, msg->data2, msg->data3); 932 break; 933 } 934 case SMP_MSG_RESCHEDULE: 935 scheduler_reschedule_ici(); 936 break; 937 938 default: 939 dprintf("smp_intercpu_int_handler: got unknown message %" B_PRId32 "\n", 940 msg->message); 941 break; 942 } 943 944 // finish dealing with this message, possibly removing it from the list 945 finish_message_processing(currentCPU, msg, sourceMailbox); 946 947 // special case for the halt message 948 if (haltCPU) 949 debug_trap_cpu_in_kdl(currentCPU, false); 950 951 return B_OK; 952 } 953 954 955 #if B_DEBUG_SPINLOCK_CONTENTION 956 957 958 static uint64 959 get_spinlock_counter(spinlock* lock) 960 { 961 uint32 high; 962 uint32 low; 963 do { 964 high = (uint32)atomic_get(&lock->count_high); 965 low = (uint32)atomic_get(&lock->count_low); 966 } while (high != atomic_get(&lock->count_high)); 967 968 return ((uint64)high << 32) | low; 969 } 970 971 972 static status_t 973 spinlock_contention_syscall(const char* subsystem, uint32 function, 974 void* buffer, size_t bufferSize) 975 { 976 spinlock_contention_info info; 977 978 if (function != GET_SPINLOCK_CONTENTION_INFO) 979 return B_BAD_VALUE; 980 981 if (bufferSize < sizeof(spinlock_contention_info)) 982 return B_BAD_VALUE; 983 984 info.thread_spinlock_counter = get_spinlock_counter(&gThreadSpinlock); 985 info.team_spinlock_counter = get_spinlock_counter(&gTeamSpinlock); 986 987 if (!IS_USER_ADDRESS(buffer) 988 || user_memcpy(buffer, &info, sizeof(info)) != B_OK) { 989 return B_BAD_ADDRESS; 990 } 991 992 return B_OK; 993 } 994 995 996 #endif // B_DEBUG_SPINLOCK_CONTENTION 997 998 999 static void 1000 process_early_cpu_call(int32 cpu) 1001 { 1002 sEarlyCPUCallFunction(sEarlyCPUCallCookie, cpu); 1003 sEarlyCPUCallSet.ClearBitAtomic(cpu); 1004 atomic_add(&sEarlyCPUCallCount, 1); 1005 } 1006 1007 1008 static void 1009 call_all_cpus_early(void (*function)(void*, int), void* cookie) 1010 { 1011 if (sNumCPUs > 1) { 1012 sEarlyCPUCallFunction = function; 1013 sEarlyCPUCallCookie = cookie; 1014 1015 atomic_set(&sEarlyCPUCallCount, 1); 1016 sEarlyCPUCallSet.SetAll(); 1017 sEarlyCPUCallSet.ClearBit(0); 1018 1019 // wait for all CPUs to finish 1020 while (sEarlyCPUCallCount < sNumCPUs) 1021 cpu_wait(&sEarlyCPUCallCount, sNumCPUs); 1022 } 1023 1024 function(cookie, 0); 1025 } 1026 1027 1028 // #pragma mark - 1029 1030 1031 int 1032 smp_intercpu_int_handler(int32 cpu) 1033 { 1034 TRACE("smp_intercpu_int_handler: entry on cpu %ld\n", cpu); 1035 1036 process_all_pending_ici(cpu); 1037 1038 TRACE("smp_intercpu_int_handler: done on cpu %ld\n", cpu); 1039 1040 return B_HANDLED_INTERRUPT; 1041 } 1042 1043 1044 void 1045 smp_send_ici(int32 targetCPU, int32 message, addr_t data, addr_t data2, 1046 addr_t data3, void* dataPointer, uint32 flags) 1047 { 1048 struct smp_msg *msg; 1049 1050 TRACE("smp_send_ici: target 0x%lx, mess 0x%lx, data 0x%lx, data2 0x%lx, " 1051 "data3 0x%lx, ptr %p, flags 0x%lx\n", targetCPU, message, data, data2, 1052 data3, dataPointer, flags); 1053 1054 if (sICIEnabled) { 1055 int state; 1056 int currentCPU; 1057 1058 // find_free_message leaves interrupts disabled 1059 state = find_free_message(&msg); 1060 1061 currentCPU = smp_get_current_cpu(); 1062 if (targetCPU == currentCPU) { 1063 return_free_message(msg); 1064 restore_interrupts(state); 1065 return; // nope, cant do that 1066 } 1067 1068 // set up the message 1069 msg->message = message; 1070 msg->data = data; 1071 msg->data2 = data2; 1072 msg->data3 = data3; 1073 msg->data_ptr = dataPointer; 1074 msg->ref_count = 1; 1075 msg->flags = flags; 1076 msg->done = 0; 1077 1078 // stick it in the appropriate cpu's mailbox 1079 struct smp_msg* next; 1080 do { 1081 cpu_pause(); 1082 next = atomic_pointer_get(&sCPUMessages[targetCPU]); 1083 msg->next = next; 1084 } while (atomic_pointer_test_and_set(&sCPUMessages[targetCPU], msg, 1085 next) != next); 1086 1087 arch_smp_send_ici(targetCPU); 1088 1089 if ((flags & SMP_MSG_FLAG_SYNC) != 0) { 1090 // wait for the other cpu to finish processing it 1091 // the interrupt handler will ref count it to <0 1092 // if the message is sync after it has removed it from the mailbox 1093 while (msg->done == 0) { 1094 process_all_pending_ici(currentCPU); 1095 cpu_wait(&msg->done, 1); 1096 } 1097 // for SYNC messages, it's our responsibility to put it 1098 // back into the free list 1099 return_free_message(msg); 1100 } 1101 1102 restore_interrupts(state); 1103 } 1104 } 1105 1106 1107 void 1108 smp_send_multicast_ici(CPUSet& cpuMask, int32 message, addr_t data, 1109 addr_t data2, addr_t data3, void *dataPointer, uint32 flags) 1110 { 1111 if (!sICIEnabled) 1112 return; 1113 1114 int currentCPU = smp_get_current_cpu(); 1115 1116 // find_free_message leaves interrupts disabled 1117 struct smp_msg *msg; 1118 int state = find_free_message(&msg); 1119 1120 msg->proc_bitmap = cpuMask; 1121 msg->proc_bitmap.ClearBit(currentCPU); 1122 1123 int32 targetCPUs = 0; 1124 for (int32 i = 0; i < sNumCPUs; i++) { 1125 if (msg->proc_bitmap.GetBit(i)) 1126 targetCPUs++; 1127 } 1128 1129 if (targetCPUs == 0) { 1130 panic("smp_send_multicast_ici(): 0 CPU mask"); 1131 return; 1132 } 1133 1134 msg->message = message; 1135 msg->data = data; 1136 msg->data2 = data2; 1137 msg->data3 = data3; 1138 msg->data_ptr = dataPointer; 1139 msg->ref_count = targetCPUs; 1140 msg->flags = flags; 1141 msg->done = 0; 1142 1143 bool broadcast = targetCPUs == sNumCPUs - 1; 1144 1145 // stick it in the broadcast mailbox 1146 acquire_spinlock_nocheck(&sBroadcastMessageSpinlock); 1147 msg->next = sBroadcastMessages; 1148 sBroadcastMessages = msg; 1149 release_spinlock(&sBroadcastMessageSpinlock); 1150 1151 atomic_add(&sBroadcastMessageCounter, 1); 1152 for (int32 i = 0; i < sNumCPUs; i++) { 1153 if (!cpuMask.GetBit(i)) 1154 atomic_add(&gCPU[i].ici_counter, 1); 1155 } 1156 1157 if (broadcast) 1158 arch_smp_send_broadcast_ici(); 1159 else 1160 arch_smp_send_multicast_ici(cpuMask); 1161 1162 if ((flags & SMP_MSG_FLAG_SYNC) != 0) { 1163 // wait for the other cpus to finish processing it 1164 // the interrupt handler will ref count it to <0 1165 // if the message is sync after it has removed it from the mailbox 1166 while (msg->done == 0) { 1167 process_all_pending_ici(currentCPU); 1168 cpu_wait(&msg->done, 1); 1169 } 1170 1171 // for SYNC messages, it's our responsibility to put it 1172 // back into the free list 1173 return_free_message(msg); 1174 } 1175 1176 restore_interrupts(state); 1177 } 1178 1179 1180 void 1181 smp_send_broadcast_ici(int32 message, addr_t data, addr_t data2, addr_t data3, 1182 void *dataPointer, uint32 flags) 1183 { 1184 struct smp_msg *msg; 1185 1186 TRACE("smp_send_broadcast_ici: cpu %ld mess 0x%lx, data 0x%lx, data2 " 1187 "0x%lx, data3 0x%lx, ptr %p, flags 0x%lx\n", smp_get_current_cpu(), 1188 message, data, data2, data3, dataPointer, flags); 1189 1190 if (sICIEnabled) { 1191 int state; 1192 int currentCPU; 1193 1194 // find_free_message leaves interrupts disabled 1195 state = find_free_message(&msg); 1196 1197 currentCPU = smp_get_current_cpu(); 1198 1199 msg->message = message; 1200 msg->data = data; 1201 msg->data2 = data2; 1202 msg->data3 = data3; 1203 msg->data_ptr = dataPointer; 1204 msg->ref_count = sNumCPUs - 1; 1205 msg->flags = flags; 1206 msg->proc_bitmap.SetAll(); 1207 msg->proc_bitmap.ClearBit(currentCPU); 1208 msg->done = 0; 1209 1210 TRACE("smp_send_broadcast_ici%d: inserting msg %p into broadcast " 1211 "mbox\n", currentCPU, msg); 1212 1213 // stick it in the appropriate cpu's mailbox 1214 acquire_spinlock_nocheck(&sBroadcastMessageSpinlock); 1215 msg->next = sBroadcastMessages; 1216 sBroadcastMessages = msg; 1217 release_spinlock(&sBroadcastMessageSpinlock); 1218 1219 atomic_add(&sBroadcastMessageCounter, 1); 1220 atomic_add(&gCPU[currentCPU].ici_counter, 1); 1221 1222 arch_smp_send_broadcast_ici(); 1223 1224 TRACE("smp_send_broadcast_ici: sent interrupt\n"); 1225 1226 if ((flags & SMP_MSG_FLAG_SYNC) != 0) { 1227 // wait for the other cpus to finish processing it 1228 // the interrupt handler will ref count it to <0 1229 // if the message is sync after it has removed it from the mailbox 1230 TRACE("smp_send_broadcast_ici: waiting for ack\n"); 1231 1232 while (msg->done == 0) { 1233 process_all_pending_ici(currentCPU); 1234 cpu_wait(&msg->done, 1); 1235 } 1236 1237 TRACE("smp_send_broadcast_ici: returning message to free list\n"); 1238 1239 // for SYNC messages, it's our responsibility to put it 1240 // back into the free list 1241 return_free_message(msg); 1242 } 1243 1244 restore_interrupts(state); 1245 } 1246 1247 TRACE("smp_send_broadcast_ici: done\n"); 1248 } 1249 1250 1251 void 1252 smp_send_broadcast_ici_interrupts_disabled(int32 currentCPU, int32 message, 1253 addr_t data, addr_t data2, addr_t data3, void *dataPointer, uint32 flags) 1254 { 1255 if (!sICIEnabled) 1256 return; 1257 1258 TRACE("smp_send_broadcast_ici_interrupts_disabled: cpu %ld mess 0x%lx, " 1259 "data 0x%lx, data2 0x%lx, data3 0x%lx, ptr %p, flags 0x%lx\n", 1260 currentCPU, message, data, data2, data3, dataPointer, flags); 1261 1262 struct smp_msg *msg; 1263 find_free_message_interrupts_disabled(currentCPU, &msg); 1264 1265 msg->message = message; 1266 msg->data = data; 1267 msg->data2 = data2; 1268 msg->data3 = data3; 1269 msg->data_ptr = dataPointer; 1270 msg->ref_count = sNumCPUs - 1; 1271 msg->flags = flags; 1272 msg->proc_bitmap.SetAll(); 1273 msg->proc_bitmap.ClearBit(currentCPU); 1274 msg->done = 0; 1275 1276 TRACE("smp_send_broadcast_ici_interrupts_disabled %ld: inserting msg %p " 1277 "into broadcast mbox\n", currentCPU, msg); 1278 1279 // stick it in the appropriate cpu's mailbox 1280 acquire_spinlock_nocheck(&sBroadcastMessageSpinlock); 1281 msg->next = sBroadcastMessages; 1282 sBroadcastMessages = msg; 1283 release_spinlock(&sBroadcastMessageSpinlock); 1284 1285 atomic_add(&sBroadcastMessageCounter, 1); 1286 atomic_add(&gCPU[currentCPU].ici_counter, 1); 1287 1288 arch_smp_send_broadcast_ici(); 1289 1290 TRACE("smp_send_broadcast_ici_interrupts_disabled %ld: sent interrupt\n", 1291 currentCPU); 1292 1293 if ((flags & SMP_MSG_FLAG_SYNC) != 0) { 1294 // wait for the other cpus to finish processing it 1295 // the interrupt handler will ref count it to <0 1296 // if the message is sync after it has removed it from the mailbox 1297 TRACE("smp_send_broadcast_ici_interrupts_disabled %ld: waiting for " 1298 "ack\n", currentCPU); 1299 1300 while (msg->done == 0) { 1301 process_all_pending_ici(currentCPU); 1302 cpu_wait(&msg->done, 1); 1303 } 1304 1305 TRACE("smp_send_broadcast_ici_interrupts_disabled %ld: returning " 1306 "message to free list\n", currentCPU); 1307 1308 // for SYNC messages, it's our responsibility to put it 1309 // back into the free list 1310 return_free_message(msg); 1311 } 1312 1313 TRACE("smp_send_broadcast_ici_interrupts_disabled: done\n"); 1314 } 1315 1316 1317 /*! Spin on non-boot CPUs until smp_wake_up_non_boot_cpus() has been called. 1318 1319 \param cpu The index of the calling CPU. 1320 \param rendezVous A rendez-vous variable to make sure that the boot CPU 1321 does not return before all other CPUs have started waiting. 1322 \return \c true on the boot CPU, \c false otherwise. 1323 */ 1324 bool 1325 smp_trap_non_boot_cpus(int32 cpu, uint32* rendezVous) 1326 { 1327 if (cpu == 0) { 1328 smp_cpu_rendezvous(rendezVous); 1329 return true; 1330 } 1331 1332 smp_cpu_rendezvous(rendezVous); 1333 1334 while (sBootCPUSpin == 0) { 1335 if (sEarlyCPUCallSet.GetBit(cpu)) 1336 process_early_cpu_call(cpu); 1337 1338 cpu_pause(); 1339 } 1340 1341 return false; 1342 } 1343 1344 1345 void 1346 smp_wake_up_non_boot_cpus() 1347 { 1348 // ICIs were previously being ignored 1349 if (sNumCPUs > 1) 1350 sICIEnabled = true; 1351 1352 // resume non boot CPUs 1353 atomic_set(&sBootCPUSpin, 1); 1354 } 1355 1356 1357 /*! Spin until all CPUs have reached the rendez-vous point. 1358 1359 The rendez-vous variable \c *var must have been initialized to 0 before the 1360 function is called. The variable will be non-null when the function returns. 1361 1362 Note that when the function returns on one CPU, it only means that all CPU 1363 have already entered the function. It does not mean that the variable can 1364 already be reset. Only when all CPUs have returned (which would have to be 1365 ensured via another rendez-vous) the variable can be reset. 1366 */ 1367 void 1368 smp_cpu_rendezvous(uint32* var) 1369 { 1370 atomic_add((int32*)var, 1); 1371 1372 while (*var < (uint32)sNumCPUs) 1373 cpu_wait((int32*)var, sNumCPUs); 1374 } 1375 1376 1377 status_t 1378 smp_init(kernel_args* args) 1379 { 1380 TRACE("smp_init: entry\n"); 1381 1382 #if DEBUG_SPINLOCK_LATENCIES 1383 sEnableLatencyCheck 1384 = !get_safemode_boolean(B_SAFEMODE_DISABLE_LATENCY_CHECK, false); 1385 #endif 1386 1387 #if DEBUG_SPINLOCKS 1388 add_debugger_command_etc("spinlock", &dump_spinlock, 1389 "Dump info on a spinlock", 1390 "\n" 1391 "Dumps info on a spinlock.\n", 0); 1392 #endif 1393 add_debugger_command_etc("ici", &dump_ici_messages, 1394 "Dump info on pending ICI messages", 1395 "\n" 1396 "Dumps info on pending ICI messages.\n", 0); 1397 add_debugger_command_etc("ici_message", &dump_ici_message, 1398 "Dump info on an ICI message", 1399 "\n" 1400 "Dumps info on an ICI message.\n", 0); 1401 1402 if (args->num_cpus > 1) { 1403 sFreeMessages = NULL; 1404 sFreeMessageCount = 0; 1405 for (int i = 0; i < MSG_POOL_SIZE; i++) { 1406 struct smp_msg* msg 1407 = (struct smp_msg*)malloc(sizeof(struct smp_msg)); 1408 if (msg == NULL) { 1409 panic("error creating smp mailboxes\n"); 1410 return B_ERROR; 1411 } 1412 memset(msg, 0, sizeof(struct smp_msg)); 1413 msg->next = sFreeMessages; 1414 sFreeMessages = msg; 1415 sFreeMessageCount++; 1416 } 1417 sNumCPUs = args->num_cpus; 1418 } 1419 TRACE("smp_init: calling arch_smp_init\n"); 1420 1421 return arch_smp_init(args); 1422 } 1423 1424 1425 status_t 1426 smp_per_cpu_init(kernel_args* args, int32 cpu) 1427 { 1428 return arch_smp_per_cpu_init(args, cpu); 1429 } 1430 1431 1432 status_t 1433 smp_init_post_generic_syscalls(void) 1434 { 1435 #if B_DEBUG_SPINLOCK_CONTENTION 1436 return register_generic_syscall(SPINLOCK_CONTENTION, 1437 &spinlock_contention_syscall, 0, 0); 1438 #else 1439 return B_OK; 1440 #endif 1441 } 1442 1443 1444 void 1445 smp_set_num_cpus(int32 numCPUs) 1446 { 1447 sNumCPUs = numCPUs; 1448 } 1449 1450 1451 int32 1452 smp_get_num_cpus() 1453 { 1454 return sNumCPUs; 1455 } 1456 1457 1458 int32 1459 smp_get_current_cpu(void) 1460 { 1461 return thread_get_current_thread()->cpu->cpu_num; 1462 } 1463 1464 1465 // #pragma mark - public exported functions 1466 1467 1468 void 1469 call_all_cpus(void (*func)(void*, int), void* cookie) 1470 { 1471 cpu_status state = disable_interrupts(); 1472 1473 // if inter-CPU communication is not yet enabled, use the early mechanism 1474 if (!sICIEnabled) { 1475 call_all_cpus_early(func, cookie); 1476 restore_interrupts(state); 1477 return; 1478 } 1479 1480 if (smp_get_num_cpus() > 1) { 1481 smp_send_broadcast_ici(SMP_MSG_CALL_FUNCTION, (addr_t)cookie, 1482 0, 0, (void*)func, SMP_MSG_FLAG_ASYNC); 1483 } 1484 1485 // we need to call this function ourselves as well 1486 func(cookie, smp_get_current_cpu()); 1487 1488 restore_interrupts(state); 1489 } 1490 1491 1492 void 1493 call_all_cpus_sync(void (*func)(void*, int), void* cookie) 1494 { 1495 cpu_status state = disable_interrupts(); 1496 1497 // if inter-CPU communication is not yet enabled, use the early mechanism 1498 if (!sICIEnabled) { 1499 call_all_cpus_early(func, cookie); 1500 restore_interrupts(state); 1501 return; 1502 } 1503 1504 if (smp_get_num_cpus() > 1) { 1505 smp_send_broadcast_ici(SMP_MSG_CALL_FUNCTION, (addr_t)cookie, 1506 0, 0, (void*)func, SMP_MSG_FLAG_SYNC); 1507 } 1508 1509 // we need to call this function ourselves as well 1510 func(cookie, smp_get_current_cpu()); 1511 1512 restore_interrupts(state); 1513 } 1514 1515 1516 #undef memory_read_barrier 1517 #undef memory_write_barrier 1518 1519 1520 void 1521 memory_read_barrier() 1522 { 1523 memory_read_barrier_inline(); 1524 } 1525 1526 1527 void 1528 memory_write_barrier() 1529 { 1530 memory_write_barrier_inline(); 1531 } 1532 1533