xref: /haiku/src/system/kernel/smp.cpp (revision 8e8f7748d39f8407894a5ab79f7b4f93bc4f4652)
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