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