xref: /haiku/src/system/kernel/smp.cpp (revision 46d4471af7fad4e52cfbd09174598cf5318aceed)
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 was 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 #	if DEBUG_SPINLOCKS
366 					panic("acquire_spinlock(): Failed to acquire spinlock %p "
367 						"for a long time (last caller: %p, value: %" B_PRIx32
368 						")", lock, find_lock_caller(lock), lock->lock);
369 #	else
370 					panic("acquire_spinlock(): Failed to acquire spinlock %p "
371 						"for a long time (value: %" B_PRIx32 ")", lock,
372 						lock->lock);
373 #	endif
374 					count = 0;
375 				}
376 
377 				process_all_pending_ici(currentCPU);
378 				cpu_wait(&lock->lock, 0);
379 			}
380 			if (atomic_get_and_set(&lock->lock, 1) == 0)
381 				break;
382 		}
383 
384 #	if DEBUG_SPINLOCKS
385 		push_lock_caller(arch_debug_get_caller(), lock);
386 #	endif
387 #endif
388 	} else {
389 #if DEBUG_SPINLOCKS
390 		int32 oldValue = atomic_get_and_set(&lock->lock, 1);
391 		if (oldValue != 0) {
392 			panic("acquire_spinlock: attempt to acquire lock %p twice on "
393 				"non-SMP system (last caller: %p, value %" B_PRIx32 ")", lock,
394 				find_lock_caller(lock), oldValue);
395 		}
396 
397 		push_lock_caller(arch_debug_get_caller(), lock);
398 #endif
399 	}
400 #if DEBUG_SPINLOCK_LATENCIES
401 	push_latency(lock);
402 #endif
403 }
404 
405 
406 static void
407 acquire_spinlock_nocheck(spinlock *lock)
408 {
409 #if DEBUG_SPINLOCKS
410 	if (are_interrupts_enabled()) {
411 		panic("acquire_spinlock_nocheck: attempt to acquire lock %p with "
412 			"interrupts enabled", lock);
413 	}
414 #endif
415 
416 	if (sNumCPUs > 1) {
417 #if B_DEBUG_SPINLOCK_CONTENTION
418 		while (atomic_add(&lock->lock, 1) != 0) {
419 		}
420 #else
421 		while (1) {
422 			uint32 count = 0;
423 			while (lock->lock != 0) {
424 				if (++count == SPINLOCK_DEADLOCK_COUNT_NO_CHECK) {
425 #	if DEBUG_SPINLOCKS
426 					panic("acquire_spinlock_nocheck(): Failed to acquire "
427 						"spinlock %p for a long time (last caller: %p, value: %"
428 						B_PRIx32 ")", lock, find_lock_caller(lock), lock->lock);
429 #	else
430 					panic("acquire_spinlock_nocheck(): Failed to acquire "
431 						"spinlock %p for a long time (value: %" B_PRIx32 ")",
432 						lock, lock->lock);
433 #	endif
434 					count = 0;
435 				}
436 
437 				cpu_wait(&lock->lock, 0);
438 			}
439 
440 			if (atomic_get_and_set(&lock->lock, 1) == 0)
441 				break;
442 		}
443 
444 #	if DEBUG_SPINLOCKS
445 		push_lock_caller(arch_debug_get_caller(), lock);
446 #	endif
447 #endif
448 	} else {
449 #if DEBUG_SPINLOCKS
450 		int32 oldValue = atomic_get_and_set(&lock->lock, 1);
451 		if (oldValue != 0) {
452 			panic("acquire_spinlock_nocheck: attempt to acquire lock %p twice "
453 				"on non-SMP system (last caller: %p, value %" B_PRIx32 ")",
454 				lock, find_lock_caller(lock), oldValue);
455 		}
456 
457 		push_lock_caller(arch_debug_get_caller(), lock);
458 #endif
459 	}
460 }
461 
462 
463 /*!	Equivalent to acquire_spinlock(), save for currentCPU parameter. */
464 static void
465 acquire_spinlock_cpu(int32 currentCPU, spinlock *lock)
466 {
467 #if DEBUG_SPINLOCKS
468 	if (are_interrupts_enabled()) {
469 		panic("acquire_spinlock_cpu: attempt to acquire lock %p with "
470 			"interrupts enabled", lock);
471 	}
472 #endif
473 
474 	if (sNumCPUs > 1) {
475 #if B_DEBUG_SPINLOCK_CONTENTION
476 		while (atomic_add(&lock->lock, 1) != 0)
477 			process_all_pending_ici(currentCPU);
478 #else
479 		while (1) {
480 			uint32 count = 0;
481 			while (lock->lock != 0) {
482 				if (++count == SPINLOCK_DEADLOCK_COUNT) {
483 #	if DEBUG_SPINLOCKS
484 					panic("acquire_spinlock_cpu(): Failed to acquire spinlock "
485 						"%p for a long time (last caller: %p, value: %" B_PRIx32
486 						")", lock, find_lock_caller(lock), lock->lock);
487 #	else
488 					panic("acquire_spinlock_cpu(): Failed to acquire spinlock "
489 						"%p for a long time (value: %" B_PRIx32 ")", lock,
490 						lock->lock);
491 #	endif
492 					count = 0;
493 				}
494 
495 				process_all_pending_ici(currentCPU);
496 				cpu_wait(&lock->lock, 0);
497 			}
498 			if (atomic_get_and_set(&lock->lock, 1) == 0)
499 				break;
500 		}
501 
502 #	if DEBUG_SPINLOCKS
503 		push_lock_caller(arch_debug_get_caller(), lock);
504 #	endif
505 #endif
506 	} else {
507 #if DEBUG_SPINLOCKS
508 		int32 oldValue = atomic_get_and_set(&lock->lock, 1);
509 		if (oldValue != 0) {
510 			panic("acquire_spinlock_cpu(): attempt to acquire lock %p twice on "
511 				"non-SMP system (last caller: %p, value %" B_PRIx32 ")", lock,
512 				find_lock_caller(lock), oldValue);
513 		}
514 
515 		push_lock_caller(arch_debug_get_caller(), lock);
516 #endif
517 	}
518 }
519 
520 
521 void
522 release_spinlock(spinlock *lock)
523 {
524 #if DEBUG_SPINLOCK_LATENCIES
525 	test_latency(lock);
526 #endif
527 
528 	if (sNumCPUs > 1) {
529 		if (are_interrupts_enabled())
530 			panic("release_spinlock: attempt to release lock %p with "
531 				"interrupts enabled\n", lock);
532 #if B_DEBUG_SPINLOCK_CONTENTION
533 		{
534 			int32 count = atomic_and(&lock->lock, 0) - 1;
535 			if (count < 0) {
536 				panic("release_spinlock: lock %p was already released\n", lock);
537 			} else {
538 				// add to the total count -- deal with carry manually
539 				if ((uint32)atomic_add(&lock->count_low, count) + count
540 						< (uint32)count) {
541 					atomic_add(&lock->count_high, 1);
542 				}
543 			}
544 		}
545 #elif DEBUG_SPINLOCKS
546 		if (atomic_get_and_set(&lock->lock, 0) != 1)
547 			panic("release_spinlock: lock %p was already released\n", lock);
548 #else
549 		atomic_set(&lock->lock, 0);
550 #endif
551 	} else {
552 #if DEBUG_SPINLOCKS
553 		if (are_interrupts_enabled()) {
554 			panic("release_spinlock: attempt to release lock %p with "
555 				"interrupts enabled\n", lock);
556 		}
557 		if (atomic_get_and_set(&lock->lock, 0) != 1)
558 			panic("release_spinlock: lock %p was already released\n", lock);
559 #endif
560 #if DEBUG_SPINLOCK_LATENCIES
561 		test_latency(lock);
562 #endif
563 	}
564 }
565 
566 
567 bool
568 try_acquire_write_spinlock(rw_spinlock* lock)
569 {
570 #if DEBUG_SPINLOCKS
571 	if (are_interrupts_enabled()) {
572 		panic("try_acquire_write_spinlock: attempt to acquire lock %p with "
573 			"interrupts enabled", lock);
574 	}
575 
576 	if (sNumCPUs < 2 && lock->lock != 0) {
577 		panic("try_acquire_write_spinlock(): attempt to acquire lock %p twice "
578 			"on non-SMP system", lock);
579 	}
580 #endif
581 
582 	return atomic_test_and_set(&lock->lock, 1u << 31, 0) == 0;
583 }
584 
585 
586 void
587 acquire_write_spinlock(rw_spinlock* lock)
588 {
589 #if DEBUG_SPINLOCKS
590 	if (are_interrupts_enabled()) {
591 		panic("acquire_write_spinlock: attempt to acquire lock %p with "
592 			"interrupts enabled", lock);
593 	}
594 #endif
595 
596 	uint32 count = 0;
597 	int currentCPU = smp_get_current_cpu();
598 	while (true) {
599 		if (try_acquire_write_spinlock(lock))
600 			break;
601 
602 		while (lock->lock != 0) {
603 			if (++count == SPINLOCK_DEADLOCK_COUNT) {
604 				panic("acquire_write_spinlock(): Failed to acquire spinlock %p "
605 					"for a long time!", lock);
606 				count = 0;
607 			}
608 
609 			process_all_pending_ici(currentCPU);
610 			cpu_wait(&lock->lock, 0);
611 		}
612 	}
613 }
614 
615 
616 void
617 release_write_spinlock(rw_spinlock* lock)
618 {
619 #if DEBUG_SPINLOCKS
620 	uint32 previous = atomic_get_and_set(&lock->lock, 0);
621 	if ((previous & 1u << 31) == 0) {
622 		panic("release_write_spinlock: lock %p was already released (value: "
623 			"%#" B_PRIx32 ")\n", lock, previous);
624 	}
625 #else
626 	atomic_set(&lock->lock, 0);
627 #endif
628 }
629 
630 
631 bool
632 try_acquire_read_spinlock(rw_spinlock* lock)
633 {
634 #if DEBUG_SPINLOCKS
635 	if (are_interrupts_enabled()) {
636 		panic("try_acquire_read_spinlock: attempt to acquire lock %p with "
637 			"interrupts enabled", lock);
638 	}
639 
640 	if (sNumCPUs < 2 && lock->lock != 0) {
641 		panic("try_acquire_read_spinlock(): attempt to acquire lock %p twice "
642 			"on non-SMP system", lock);
643 	}
644 #endif
645 
646 	uint32 previous = atomic_add(&lock->lock, 1);
647 	return (previous & (1u << 31)) == 0;
648 }
649 
650 
651 void
652 acquire_read_spinlock(rw_spinlock* lock)
653 {
654 #if DEBUG_SPINLOCKS
655 	if (are_interrupts_enabled()) {
656 		panic("acquire_read_spinlock: attempt to acquire lock %p with "
657 			"interrupts enabled", lock);
658 	}
659 #endif
660 
661 	uint32 count = 0;
662 	int currentCPU = smp_get_current_cpu();
663 	while (1) {
664 		if (try_acquire_read_spinlock(lock))
665 			break;
666 
667 		while ((lock->lock & (1u << 31)) != 0) {
668 			if (++count == SPINLOCK_DEADLOCK_COUNT) {
669 				panic("acquire_read_spinlock(): Failed to acquire spinlock %p "
670 					"for a long time!", lock);
671 				count = 0;
672 			}
673 
674 			process_all_pending_ici(currentCPU);
675 			cpu_wait(&lock->lock, 0);
676 		}
677 	}
678 }
679 
680 
681 void
682 release_read_spinlock(rw_spinlock* lock)
683 {
684 #if DEBUG_SPINLOCKS
685 	uint32 previous = atomic_add(&lock->lock, -1);
686 	if ((previous & 1u << 31) != 0) {
687 		panic("release_read_spinlock: lock %p was already released (value:"
688 			" %#" B_PRIx32 ")\n", lock, previous);
689 	}
690 #else
691 	atomic_add(&lock->lock, -1);
692 #endif
693 
694 }
695 
696 
697 bool
698 try_acquire_write_seqlock(seqlock* lock) {
699 	bool succeed = try_acquire_spinlock(&lock->lock);
700 	if (succeed)
701 		atomic_add((int32*)&lock->count, 1);
702 	return succeed;
703 }
704 
705 
706 void
707 acquire_write_seqlock(seqlock* lock) {
708 	acquire_spinlock(&lock->lock);
709 	atomic_add((int32*)&lock->count, 1);
710 }
711 
712 
713 void
714 release_write_seqlock(seqlock* lock) {
715 	atomic_add((int32*)&lock->count, 1);
716 	release_spinlock(&lock->lock);
717 }
718 
719 
720 uint32
721 acquire_read_seqlock(seqlock* lock) {
722 	return atomic_get((int32*)&lock->count);
723 }
724 
725 
726 bool
727 release_read_seqlock(seqlock* lock, uint32 count) {
728 	memory_read_barrier();
729 
730 	uint32 current = *(volatile int32*)&lock->count;
731 
732 	if (count % 2 == 1 || current != count) {
733 		cpu_pause();
734 		return false;
735 	}
736 
737 	return true;
738 }
739 
740 
741 /*!	Finds a free message and gets it.
742 	NOTE: has side effect of disabling interrupts
743 	return value is the former interrupt state
744 */
745 static cpu_status
746 find_free_message(struct smp_msg** msg)
747 {
748 	cpu_status state;
749 
750 	TRACE("find_free_message: entry\n");
751 
752 retry:
753 	while (sFreeMessageCount <= 0)
754 		cpu_pause();
755 
756 	state = disable_interrupts();
757 	acquire_spinlock(&sFreeMessageSpinlock);
758 
759 	if (sFreeMessageCount <= 0) {
760 		// someone grabbed one while we were getting the lock,
761 		// go back to waiting for it
762 		release_spinlock(&sFreeMessageSpinlock);
763 		restore_interrupts(state);
764 		goto retry;
765 	}
766 
767 	*msg = sFreeMessages;
768 	sFreeMessages = (*msg)->next;
769 	sFreeMessageCount--;
770 
771 	release_spinlock(&sFreeMessageSpinlock);
772 
773 	TRACE("find_free_message: returning msg %p\n", *msg);
774 
775 	return state;
776 }
777 
778 
779 /*!	Similar to find_free_message(), but expects the interrupts to be disabled
780 	already.
781 */
782 static void
783 find_free_message_interrupts_disabled(int32 currentCPU,
784 	struct smp_msg** _message)
785 {
786 	TRACE("find_free_message_interrupts_disabled: entry\n");
787 
788 	acquire_spinlock_cpu(currentCPU, &sFreeMessageSpinlock);
789 	while (sFreeMessageCount <= 0) {
790 		release_spinlock(&sFreeMessageSpinlock);
791 		process_all_pending_ici(currentCPU);
792 		cpu_pause();
793 		acquire_spinlock_cpu(currentCPU, &sFreeMessageSpinlock);
794 	}
795 
796 	*_message = sFreeMessages;
797 	sFreeMessages = (*_message)->next;
798 	sFreeMessageCount--;
799 
800 	release_spinlock(&sFreeMessageSpinlock);
801 
802 	TRACE("find_free_message_interrupts_disabled: returning msg %p\n",
803 		*_message);
804 }
805 
806 
807 static void
808 return_free_message(struct smp_msg* msg)
809 {
810 	TRACE("return_free_message: returning msg %p\n", msg);
811 
812 	acquire_spinlock_nocheck(&sFreeMessageSpinlock);
813 	msg->next = sFreeMessages;
814 	sFreeMessages = msg;
815 	sFreeMessageCount++;
816 	release_spinlock(&sFreeMessageSpinlock);
817 }
818 
819 
820 static struct smp_msg*
821 check_for_message(int currentCPU, mailbox_source& sourceMailbox)
822 {
823 	if (!sICIEnabled)
824 		return NULL;
825 
826 	struct smp_msg* msg = atomic_pointer_get(&sCPUMessages[currentCPU]);
827 	if (msg != NULL) {
828 		do {
829 			cpu_pause();
830 			msg = atomic_pointer_get(&sCPUMessages[currentCPU]);
831 			ASSERT(msg != NULL);
832 		} while (atomic_pointer_test_and_set(&sCPUMessages[currentCPU],
833 				msg->next, msg) != msg);
834 
835 		TRACE(" cpu %d: found msg %p in cpu mailbox\n", currentCPU, msg);
836 		sourceMailbox = MAILBOX_LOCAL;
837 	} else if (atomic_get(&get_cpu_struct()->ici_counter)
838 		!= atomic_get(&sBroadcastMessageCounter)) {
839 
840 		// try getting one from the broadcast mailbox
841 		acquire_spinlock_nocheck(&sBroadcastMessageSpinlock);
842 
843 		msg = sBroadcastMessages;
844 		while (msg != NULL) {
845 			if (!msg->proc_bitmap.GetBit(currentCPU)) {
846 				// we have handled this one already
847 				msg = msg->next;
848 				continue;
849 			}
850 
851 			// mark it so we wont try to process this one again
852 			msg->proc_bitmap.ClearBitAtomic(currentCPU);
853 			atomic_add(&gCPU[currentCPU].ici_counter, 1);
854 
855 			sourceMailbox = MAILBOX_BCAST;
856 			break;
857 		}
858 		release_spinlock(&sBroadcastMessageSpinlock);
859 
860 		if (msg != NULL) {
861 			TRACE(" cpu %d: found msg %p in broadcast mailbox\n", currentCPU,
862 				msg);
863 		}
864 	}
865 	return msg;
866 }
867 
868 
869 static void
870 finish_message_processing(int currentCPU, struct smp_msg* msg,
871 	mailbox_source sourceMailbox)
872 {
873 	if (atomic_add(&msg->ref_count, -1) != 1)
874 		return;
875 
876 	// we were the last one to decrement the ref_count
877 	// it's our job to remove it from the list & possibly clean it up
878 
879 	// clean up the message
880 	if (sourceMailbox == MAILBOX_BCAST)
881 		acquire_spinlock_nocheck(&sBroadcastMessageSpinlock);
882 
883 	TRACE("cleaning up message %p\n", msg);
884 
885 	if (sourceMailbox != MAILBOX_BCAST) {
886 		// local mailbox -- the message has already been removed in
887 		// check_for_message()
888 	} else if (msg == sBroadcastMessages) {
889 		sBroadcastMessages = msg->next;
890 	} else {
891 		// we need to walk to find the message in the list.
892 		// we can't use any data found when previously walking through
893 		// the list, since the list may have changed. But, we are guaranteed
894 		// to at least have msg in it.
895 		struct smp_msg* last = NULL;
896 		struct smp_msg* msg1;
897 
898 		msg1 = sBroadcastMessages;
899 		while (msg1 != NULL && msg1 != msg) {
900 			last = msg1;
901 			msg1 = msg1->next;
902 		}
903 
904 		// by definition, last must be something
905 		if (msg1 == msg && last != NULL)
906 			last->next = msg->next;
907 		else
908 			panic("last == NULL or msg != msg1");
909 	}
910 
911 	if (sourceMailbox == MAILBOX_BCAST)
912 		release_spinlock(&sBroadcastMessageSpinlock);
913 
914 	if ((msg->flags & SMP_MSG_FLAG_FREE_ARG) != 0 && msg->data_ptr != NULL)
915 		free(msg->data_ptr);
916 
917 	if ((msg->flags & SMP_MSG_FLAG_SYNC) != 0) {
918 		atomic_set(&msg->done, 1);
919 		// the caller cpu should now free the message
920 	} else {
921 		// in the !SYNC case, we get to free the message
922 		return_free_message(msg);
923 	}
924 }
925 
926 
927 static status_t
928 process_pending_ici(int32 currentCPU)
929 {
930 	mailbox_source sourceMailbox;
931 	struct smp_msg* msg = check_for_message(currentCPU, sourceMailbox);
932 	if (msg == NULL)
933 		return B_ENTRY_NOT_FOUND;
934 
935 	TRACE("  cpu %ld message = %ld\n", currentCPU, msg->message);
936 
937 	bool haltCPU = false;
938 
939 	switch (msg->message) {
940 		case SMP_MSG_INVALIDATE_PAGE_RANGE:
941 			arch_cpu_invalidate_TLB_range(msg->data, msg->data2);
942 			break;
943 		case SMP_MSG_INVALIDATE_PAGE_LIST:
944 			arch_cpu_invalidate_TLB_list((addr_t*)msg->data, (int)msg->data2);
945 			break;
946 		case SMP_MSG_USER_INVALIDATE_PAGES:
947 			arch_cpu_user_TLB_invalidate();
948 			break;
949 		case SMP_MSG_GLOBAL_INVALIDATE_PAGES:
950 			arch_cpu_global_TLB_invalidate();
951 			break;
952 		case SMP_MSG_CPU_HALT:
953 			haltCPU = true;
954 			break;
955 		case SMP_MSG_CALL_FUNCTION:
956 		{
957 			smp_call_func func = (smp_call_func)msg->data_ptr;
958 			func(msg->data, currentCPU, msg->data2, msg->data3);
959 			break;
960 		}
961 		case SMP_MSG_RESCHEDULE:
962 			scheduler_reschedule_ici();
963 			break;
964 
965 		default:
966 			dprintf("smp_intercpu_int_handler: got unknown message %" B_PRId32 "\n",
967 				msg->message);
968 			break;
969 	}
970 
971 	// finish dealing with this message, possibly removing it from the list
972 	finish_message_processing(currentCPU, msg, sourceMailbox);
973 
974 	// special case for the halt message
975 	if (haltCPU)
976 		debug_trap_cpu_in_kdl(currentCPU, false);
977 
978 	return B_OK;
979 }
980 
981 
982 #if B_DEBUG_SPINLOCK_CONTENTION
983 
984 
985 static uint64
986 get_spinlock_counter(spinlock* lock)
987 {
988 	uint32 high;
989 	uint32 low;
990 	do {
991 		high = (uint32)atomic_get(&lock->count_high);
992 		low = (uint32)atomic_get(&lock->count_low);
993 	} while (high != atomic_get(&lock->count_high));
994 
995 	return ((uint64)high << 32) | low;
996 }
997 
998 
999 static status_t
1000 spinlock_contention_syscall(const char* subsystem, uint32 function,
1001 	void* buffer, size_t bufferSize)
1002 {
1003 	spinlock_contention_info info;
1004 
1005 	if (function != GET_SPINLOCK_CONTENTION_INFO)
1006 		return B_BAD_VALUE;
1007 
1008 	if (bufferSize < sizeof(spinlock_contention_info))
1009 		return B_BAD_VALUE;
1010 
1011 	info.thread_spinlock_counter = get_spinlock_counter(&gThreadSpinlock);
1012 	info.team_spinlock_counter = get_spinlock_counter(&gTeamSpinlock);
1013 
1014 	if (!IS_USER_ADDRESS(buffer)
1015 		|| user_memcpy(buffer, &info, sizeof(info)) != B_OK) {
1016 		return B_BAD_ADDRESS;
1017 	}
1018 
1019 	return B_OK;
1020 }
1021 
1022 
1023 #endif	// B_DEBUG_SPINLOCK_CONTENTION
1024 
1025 
1026 static void
1027 process_early_cpu_call(int32 cpu)
1028 {
1029 	sEarlyCPUCallFunction(sEarlyCPUCallCookie, cpu);
1030 	sEarlyCPUCallSet.ClearBitAtomic(cpu);
1031 	atomic_add(&sEarlyCPUCallCount, 1);
1032 }
1033 
1034 
1035 static void
1036 call_all_cpus_early(void (*function)(void*, int), void* cookie)
1037 {
1038 	if (sNumCPUs > 1) {
1039 		sEarlyCPUCallFunction = function;
1040 		sEarlyCPUCallCookie = cookie;
1041 
1042 		atomic_set(&sEarlyCPUCallCount, 1);
1043 		sEarlyCPUCallSet.SetAll();
1044 		sEarlyCPUCallSet.ClearBit(0);
1045 
1046 		// wait for all CPUs to finish
1047 		while (sEarlyCPUCallCount < sNumCPUs)
1048 			cpu_wait(&sEarlyCPUCallCount, sNumCPUs);
1049 	}
1050 
1051 	function(cookie, 0);
1052 }
1053 
1054 
1055 //	#pragma mark -
1056 
1057 
1058 int
1059 smp_intercpu_int_handler(int32 cpu)
1060 {
1061 	TRACE("smp_intercpu_int_handler: entry on cpu %ld\n", cpu);
1062 
1063 	process_all_pending_ici(cpu);
1064 
1065 	TRACE("smp_intercpu_int_handler: done on cpu %ld\n", cpu);
1066 
1067 	return B_HANDLED_INTERRUPT;
1068 }
1069 
1070 
1071 void
1072 smp_send_ici(int32 targetCPU, int32 message, addr_t data, addr_t data2,
1073 	addr_t data3, void* dataPointer, uint32 flags)
1074 {
1075 	struct smp_msg *msg;
1076 
1077 	TRACE("smp_send_ici: target 0x%lx, mess 0x%lx, data 0x%lx, data2 0x%lx, "
1078 		"data3 0x%lx, ptr %p, flags 0x%lx\n", targetCPU, message, data, data2,
1079 		data3, dataPointer, flags);
1080 
1081 	if (sICIEnabled) {
1082 		int state;
1083 		int currentCPU;
1084 
1085 		// find_free_message leaves interrupts disabled
1086 		state = find_free_message(&msg);
1087 
1088 		currentCPU = smp_get_current_cpu();
1089 		if (targetCPU == currentCPU) {
1090 			return_free_message(msg);
1091 			restore_interrupts(state);
1092 			return; // nope, cant do that
1093 		}
1094 
1095 		// set up the message
1096 		msg->message = message;
1097 		msg->data = data;
1098 		msg->data2 = data2;
1099 		msg->data3 = data3;
1100 		msg->data_ptr = dataPointer;
1101 		msg->ref_count = 1;
1102 		msg->flags = flags;
1103 		msg->done = 0;
1104 
1105 		// stick it in the appropriate cpu's mailbox
1106 		struct smp_msg* next;
1107 		do {
1108 			cpu_pause();
1109 			next = atomic_pointer_get(&sCPUMessages[targetCPU]);
1110 			msg->next = next;
1111 		} while (atomic_pointer_test_and_set(&sCPUMessages[targetCPU], msg,
1112 				next) != next);
1113 
1114 		arch_smp_send_ici(targetCPU);
1115 
1116 		if ((flags & SMP_MSG_FLAG_SYNC) != 0) {
1117 			// wait for the other cpu to finish processing it
1118 			// the interrupt handler will ref count it to <0
1119 			// if the message is sync after it has removed it from the mailbox
1120 			while (msg->done == 0) {
1121 				process_all_pending_ici(currentCPU);
1122 				cpu_wait(&msg->done, 1);
1123 			}
1124 			// for SYNC messages, it's our responsibility to put it
1125 			// back into the free list
1126 			return_free_message(msg);
1127 		}
1128 
1129 		restore_interrupts(state);
1130 	}
1131 }
1132 
1133 
1134 void
1135 smp_send_multicast_ici(CPUSet& cpuMask, int32 message, addr_t data,
1136 	addr_t data2, addr_t data3, void *dataPointer, uint32 flags)
1137 {
1138 	if (!sICIEnabled)
1139 		return;
1140 
1141 	int currentCPU = smp_get_current_cpu();
1142 
1143 	// find_free_message leaves interrupts disabled
1144 	struct smp_msg *msg;
1145 	int state = find_free_message(&msg);
1146 
1147 	msg->proc_bitmap = cpuMask;
1148 	msg->proc_bitmap.ClearBit(currentCPU);
1149 
1150 	int32 targetCPUs = 0;
1151 	for (int32 i = 0; i < sNumCPUs; i++) {
1152 		if (msg->proc_bitmap.GetBit(i))
1153 			targetCPUs++;
1154 	}
1155 
1156 	if (targetCPUs == 0) {
1157 		panic("smp_send_multicast_ici(): 0 CPU mask");
1158 		return;
1159 	}
1160 
1161 	msg->message = message;
1162 	msg->data = data;
1163 	msg->data2 = data2;
1164 	msg->data3 = data3;
1165 	msg->data_ptr = dataPointer;
1166 	msg->ref_count = targetCPUs;
1167 	msg->flags = flags;
1168 	msg->done = 0;
1169 
1170 	bool broadcast = targetCPUs == sNumCPUs - 1;
1171 
1172 	// stick it in the broadcast mailbox
1173 	acquire_spinlock_nocheck(&sBroadcastMessageSpinlock);
1174 	msg->next = sBroadcastMessages;
1175 	sBroadcastMessages = msg;
1176 	release_spinlock(&sBroadcastMessageSpinlock);
1177 
1178 	atomic_add(&sBroadcastMessageCounter, 1);
1179 	for (int32 i = 0; i < sNumCPUs; i++) {
1180 		if (!cpuMask.GetBit(i))
1181 			atomic_add(&gCPU[i].ici_counter, 1);
1182 	}
1183 
1184 	if (broadcast)
1185 		arch_smp_send_broadcast_ici();
1186 	else
1187 		arch_smp_send_multicast_ici(cpuMask);
1188 
1189 	if ((flags & SMP_MSG_FLAG_SYNC) != 0) {
1190 		// wait for the other cpus to finish processing it
1191 		// the interrupt handler will ref count it to <0
1192 		// if the message is sync after it has removed it from the mailbox
1193 		while (msg->done == 0) {
1194 			process_all_pending_ici(currentCPU);
1195 			cpu_wait(&msg->done, 1);
1196 		}
1197 
1198 		// for SYNC messages, it's our responsibility to put it
1199 		// back into the free list
1200 		return_free_message(msg);
1201 	}
1202 
1203 	restore_interrupts(state);
1204 }
1205 
1206 
1207 void
1208 smp_send_broadcast_ici(int32 message, addr_t data, addr_t data2, addr_t data3,
1209 	void *dataPointer, uint32 flags)
1210 {
1211 	struct smp_msg *msg;
1212 
1213 	TRACE("smp_send_broadcast_ici: cpu %ld mess 0x%lx, data 0x%lx, data2 "
1214 		"0x%lx, data3 0x%lx, ptr %p, flags 0x%lx\n", smp_get_current_cpu(),
1215 		message, data, data2, data3, dataPointer, flags);
1216 
1217 	if (sICIEnabled) {
1218 		int state;
1219 		int currentCPU;
1220 
1221 		// find_free_message leaves interrupts disabled
1222 		state = find_free_message(&msg);
1223 
1224 		currentCPU = smp_get_current_cpu();
1225 
1226 		msg->message = message;
1227 		msg->data = data;
1228 		msg->data2 = data2;
1229 		msg->data3 = data3;
1230 		msg->data_ptr = dataPointer;
1231 		msg->ref_count = sNumCPUs - 1;
1232 		msg->flags = flags;
1233 		msg->proc_bitmap.SetAll();
1234 		msg->proc_bitmap.ClearBit(currentCPU);
1235 		msg->done = 0;
1236 
1237 		TRACE("smp_send_broadcast_ici%d: inserting msg %p into broadcast "
1238 			"mbox\n", currentCPU, msg);
1239 
1240 		// stick it in the appropriate cpu's mailbox
1241 		acquire_spinlock_nocheck(&sBroadcastMessageSpinlock);
1242 		msg->next = sBroadcastMessages;
1243 		sBroadcastMessages = msg;
1244 		release_spinlock(&sBroadcastMessageSpinlock);
1245 
1246 		atomic_add(&sBroadcastMessageCounter, 1);
1247 		atomic_add(&gCPU[currentCPU].ici_counter, 1);
1248 
1249 		arch_smp_send_broadcast_ici();
1250 
1251 		TRACE("smp_send_broadcast_ici: sent interrupt\n");
1252 
1253 		if ((flags & SMP_MSG_FLAG_SYNC) != 0) {
1254 			// wait for the other cpus to finish processing it
1255 			// the interrupt handler will ref count it to <0
1256 			// if the message is sync after it has removed it from the mailbox
1257 			TRACE("smp_send_broadcast_ici: waiting for ack\n");
1258 
1259 			while (msg->done == 0) {
1260 				process_all_pending_ici(currentCPU);
1261 				cpu_wait(&msg->done, 1);
1262 			}
1263 
1264 			TRACE("smp_send_broadcast_ici: returning message to free list\n");
1265 
1266 			// for SYNC messages, it's our responsibility to put it
1267 			// back into the free list
1268 			return_free_message(msg);
1269 		}
1270 
1271 		restore_interrupts(state);
1272 	}
1273 
1274 	TRACE("smp_send_broadcast_ici: done\n");
1275 }
1276 
1277 
1278 void
1279 smp_send_broadcast_ici_interrupts_disabled(int32 currentCPU, int32 message,
1280 	addr_t data, addr_t data2, addr_t data3, void *dataPointer, uint32 flags)
1281 {
1282 	if (!sICIEnabled)
1283 		return;
1284 
1285 	TRACE("smp_send_broadcast_ici_interrupts_disabled: cpu %ld mess 0x%lx, "
1286 		"data 0x%lx, data2 0x%lx, data3 0x%lx, ptr %p, flags 0x%lx\n",
1287 		currentCPU, message, data, data2, data3, dataPointer, flags);
1288 
1289 	struct smp_msg *msg;
1290 	find_free_message_interrupts_disabled(currentCPU, &msg);
1291 
1292 	msg->message = message;
1293 	msg->data = data;
1294 	msg->data2 = data2;
1295 	msg->data3 = data3;
1296 	msg->data_ptr = dataPointer;
1297 	msg->ref_count = sNumCPUs - 1;
1298 	msg->flags = flags;
1299 	msg->proc_bitmap.SetAll();
1300 	msg->proc_bitmap.ClearBit(currentCPU);
1301 	msg->done = 0;
1302 
1303 	TRACE("smp_send_broadcast_ici_interrupts_disabled %ld: inserting msg %p "
1304 		"into broadcast mbox\n", currentCPU, msg);
1305 
1306 	// stick it in the appropriate cpu's mailbox
1307 	acquire_spinlock_nocheck(&sBroadcastMessageSpinlock);
1308 	msg->next = sBroadcastMessages;
1309 	sBroadcastMessages = msg;
1310 	release_spinlock(&sBroadcastMessageSpinlock);
1311 
1312 	atomic_add(&sBroadcastMessageCounter, 1);
1313 	atomic_add(&gCPU[currentCPU].ici_counter, 1);
1314 
1315 	arch_smp_send_broadcast_ici();
1316 
1317 	TRACE("smp_send_broadcast_ici_interrupts_disabled %ld: sent interrupt\n",
1318 		currentCPU);
1319 
1320 	if ((flags & SMP_MSG_FLAG_SYNC) != 0) {
1321 		// wait for the other cpus to finish processing it
1322 		// the interrupt handler will ref count it to <0
1323 		// if the message is sync after it has removed it from the mailbox
1324 		TRACE("smp_send_broadcast_ici_interrupts_disabled %ld: waiting for "
1325 			"ack\n", currentCPU);
1326 
1327 		while (msg->done == 0) {
1328 			process_all_pending_ici(currentCPU);
1329 			cpu_wait(&msg->done, 1);
1330 		}
1331 
1332 		TRACE("smp_send_broadcast_ici_interrupts_disabled %ld: returning "
1333 			"message to free list\n", currentCPU);
1334 
1335 		// for SYNC messages, it's our responsibility to put it
1336 		// back into the free list
1337 		return_free_message(msg);
1338 	}
1339 
1340 	TRACE("smp_send_broadcast_ici_interrupts_disabled: done\n");
1341 }
1342 
1343 
1344 /*!	Spin on non-boot CPUs until smp_wake_up_non_boot_cpus() has been called.
1345 
1346 	\param cpu The index of the calling CPU.
1347 	\param rendezVous A rendez-vous variable to make sure that the boot CPU
1348 		does not return before all other CPUs have started waiting.
1349 	\return \c true on the boot CPU, \c false otherwise.
1350 */
1351 bool
1352 smp_trap_non_boot_cpus(int32 cpu, uint32* rendezVous)
1353 {
1354 	if (cpu == 0) {
1355 		smp_cpu_rendezvous(rendezVous);
1356 		return true;
1357 	}
1358 
1359 	smp_cpu_rendezvous(rendezVous);
1360 
1361 	while (sBootCPUSpin == 0) {
1362 		if (sEarlyCPUCallSet.GetBit(cpu))
1363 			process_early_cpu_call(cpu);
1364 
1365 		cpu_pause();
1366 	}
1367 
1368 	return false;
1369 }
1370 
1371 
1372 void
1373 smp_wake_up_non_boot_cpus()
1374 {
1375 	// ICIs were previously being ignored
1376 	if (sNumCPUs > 1)
1377 		sICIEnabled = true;
1378 
1379 	// resume non boot CPUs
1380 	atomic_set(&sBootCPUSpin, 1);
1381 }
1382 
1383 
1384 /*!	Spin until all CPUs have reached the rendez-vous point.
1385 
1386 	The rendez-vous variable \c *var must have been initialized to 0 before the
1387 	function is called. The variable will be non-null when the function returns.
1388 
1389 	Note that when the function returns on one CPU, it only means that all CPU
1390 	have already entered the function. It does not mean that the variable can
1391 	already be reset. Only when all CPUs have returned (which would have to be
1392 	ensured via another rendez-vous) the variable can be reset.
1393 */
1394 void
1395 smp_cpu_rendezvous(uint32* var)
1396 {
1397 	atomic_add((int32*)var, 1);
1398 
1399 	while (*var < (uint32)sNumCPUs)
1400 		cpu_wait((int32*)var, sNumCPUs);
1401 }
1402 
1403 
1404 status_t
1405 smp_init(kernel_args* args)
1406 {
1407 	TRACE("smp_init: entry\n");
1408 
1409 #if DEBUG_SPINLOCK_LATENCIES
1410 	sEnableLatencyCheck
1411 		= !get_safemode_boolean(B_SAFEMODE_DISABLE_LATENCY_CHECK, false);
1412 #endif
1413 
1414 #if DEBUG_SPINLOCKS
1415 	add_debugger_command_etc("spinlock", &dump_spinlock,
1416 		"Dump info on a spinlock",
1417 		"\n"
1418 		"Dumps info on a spinlock.\n", 0);
1419 #endif
1420 	add_debugger_command_etc("ici", &dump_ici_messages,
1421 		"Dump info on pending ICI messages",
1422 		"\n"
1423 		"Dumps info on pending ICI messages.\n", 0);
1424 	add_debugger_command_etc("ici_message", &dump_ici_message,
1425 		"Dump info on an ICI message",
1426 		"\n"
1427 		"Dumps info on an ICI message.\n", 0);
1428 
1429 	if (args->num_cpus > 1) {
1430 		sFreeMessages = NULL;
1431 		sFreeMessageCount = 0;
1432 		for (int i = 0; i < MSG_POOL_SIZE; i++) {
1433 			struct smp_msg* msg
1434 				= (struct smp_msg*)malloc(sizeof(struct smp_msg));
1435 			if (msg == NULL) {
1436 				panic("error creating smp mailboxes\n");
1437 				return B_ERROR;
1438 			}
1439 			memset(msg, 0, sizeof(struct smp_msg));
1440 			msg->next = sFreeMessages;
1441 			sFreeMessages = msg;
1442 			sFreeMessageCount++;
1443 		}
1444 		sNumCPUs = args->num_cpus;
1445 	}
1446 	TRACE("smp_init: calling arch_smp_init\n");
1447 
1448 	return arch_smp_init(args);
1449 }
1450 
1451 
1452 status_t
1453 smp_per_cpu_init(kernel_args* args, int32 cpu)
1454 {
1455 	return arch_smp_per_cpu_init(args, cpu);
1456 }
1457 
1458 
1459 status_t
1460 smp_init_post_generic_syscalls(void)
1461 {
1462 #if B_DEBUG_SPINLOCK_CONTENTION
1463 	return register_generic_syscall(SPINLOCK_CONTENTION,
1464 		&spinlock_contention_syscall, 0, 0);
1465 #else
1466 	return B_OK;
1467 #endif
1468 }
1469 
1470 
1471 void
1472 smp_set_num_cpus(int32 numCPUs)
1473 {
1474 	sNumCPUs = numCPUs;
1475 }
1476 
1477 
1478 int32
1479 smp_get_num_cpus()
1480 {
1481 	return sNumCPUs;
1482 }
1483 
1484 
1485 int32
1486 smp_get_current_cpu(void)
1487 {
1488 	return thread_get_current_thread()->cpu->cpu_num;
1489 }
1490 
1491 
1492 // #pragma mark - public exported functions
1493 
1494 
1495 void
1496 call_all_cpus(void (*func)(void*, int), void* cookie)
1497 {
1498 	cpu_status state = disable_interrupts();
1499 
1500 	// if inter-CPU communication is not yet enabled, use the early mechanism
1501 	if (!sICIEnabled) {
1502 		call_all_cpus_early(func, cookie);
1503 		restore_interrupts(state);
1504 		return;
1505 	}
1506 
1507 	if (smp_get_num_cpus() > 1) {
1508 		smp_send_broadcast_ici(SMP_MSG_CALL_FUNCTION, (addr_t)cookie,
1509 			0, 0, (void*)func, SMP_MSG_FLAG_ASYNC);
1510 	}
1511 
1512 	// we need to call this function ourselves as well
1513 	func(cookie, smp_get_current_cpu());
1514 
1515 	restore_interrupts(state);
1516 }
1517 
1518 
1519 void
1520 call_all_cpus_sync(void (*func)(void*, int), void* cookie)
1521 {
1522 	cpu_status state = disable_interrupts();
1523 
1524 	// if inter-CPU communication is not yet enabled, use the early mechanism
1525 	if (!sICIEnabled) {
1526 		call_all_cpus_early(func, cookie);
1527 		restore_interrupts(state);
1528 		return;
1529 	}
1530 
1531 	if (smp_get_num_cpus() > 1) {
1532 		smp_send_broadcast_ici(SMP_MSG_CALL_FUNCTION, (addr_t)cookie,
1533 			0, 0, (void*)func, SMP_MSG_FLAG_SYNC);
1534 	}
1535 
1536 	// we need to call this function ourselves as well
1537 	func(cookie, smp_get_current_cpu());
1538 
1539 	restore_interrupts(state);
1540 }
1541 
1542 
1543 #undef memory_read_barrier
1544 #undef memory_write_barrier
1545 
1546 
1547 void
1548 memory_read_barrier()
1549 {
1550 	memory_read_barrier_inline();
1551 }
1552 
1553 
1554 void
1555 memory_write_barrier()
1556 {
1557 	memory_write_barrier_inline();
1558 }
1559 
1560