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