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