1// Copyright 2016 The Fuchsia Authors 2// Copyright (c) 2012 Travis Geiselbrecht 3// 4// Use of this source code is governed by a MIT-style 5// license that can be found in the LICENSE file or at 6// https://opensource.org/licenses/MIT 7 8#include "tests.h" 9 10#include <arch/ops.h> 11#include <assert.h> 12#include <err.h> 13#include <fbl/algorithm.h> 14#include <kernel/event.h> 15#include <kernel/mp.h> 16#include <kernel/thread.h> 17#include <lib/unittest/unittest.h> 18#include <stdio.h> 19#include <trace.h> 20#include <zircon/types.h> 21 22#define LOCAL_TRACE 0 23 24#define TEST_RUNS 1000 25 26static void inorder_count_task(void* raw_context) { 27 ASSERT(arch_ints_disabled()); 28 ASSERT(arch_blocking_disallowed()); 29 int* inorder_counter = (int*)raw_context; 30 uint cpu_num = arch_curr_cpu_num(); 31 32 int oldval = atomic_add(inorder_counter, 1); 33 ASSERT(oldval == (int)cpu_num); 34 LTRACEF(" CPU %u checked in\n", cpu_num); 35} 36 37static void counter_task(void* raw_context) { 38 ASSERT(arch_ints_disabled()); 39 ASSERT(arch_blocking_disallowed()); 40 int* counter = (int*)raw_context; 41 atomic_add(counter, 1); 42} 43 44static int deadlock_test_thread(void* arg) { 45 event_t* gate = (event_t*)arg; 46 event_wait(gate); 47 48 int counter = 0; 49 arch_disable_ints(); 50 mp_sync_exec(MP_IPI_TARGET_ALL_BUT_LOCAL, 0, counter_task, &counter); 51 arch_enable_ints(); 52 return 0; 53} 54 55static void deadlock_test(void) { 56 /* Test for a deadlock caused by multiple CPUs broadcasting concurrently */ 57 58 event_t gate = EVENT_INITIAL_VALUE(gate, false, 0); 59 60 thread_t* threads[5] = {0}; 61 for (uint i = 0; i < fbl::count_of(threads); ++i) { 62 threads[i] = thread_create("sync_ipi_deadlock", deadlock_test_thread, &gate, DEFAULT_PRIORITY); 63 if (!threads[i]) { 64 TRACEF(" failed to create thread\n"); 65 goto cleanup; 66 } 67 thread_resume(threads[i]); 68 } 69 70 event_signal(&gate, true); 71 72cleanup: 73 for (uint i = 0; i < fbl::count_of(threads); ++i) { 74 if (threads[i]) { 75 thread_join(threads[i], NULL, ZX_TIME_INFINITE); 76 } 77 } 78 event_destroy(&gate); 79}; 80 81static bool sync_ipi_tests() { 82 BEGIN_TEST; 83 84 uint num_cpus = arch_max_num_cpus(); 85 uint online = mp_get_online_mask(); 86 if (online != (1U << num_cpus) - 1) { 87 printf("Can only run test with all CPUs online\n"); 88 return true; 89 } 90 91 uint runs = TEST_RUNS; 92 93 /* Test that we're actually blocking and only signaling the ones we target */ 94 for (uint i = 0; i < runs; ++i) { 95 LTRACEF("Sequential test\n"); 96 int inorder_counter = 0; 97 for (uint i = 0; i < num_cpus; ++i) { 98 mp_sync_exec(MP_IPI_TARGET_MASK, 1u << i, inorder_count_task, &inorder_counter); 99 LTRACEF(" Finished signaling CPU %u\n", i); 100 } 101 } 102 103 /* Test that we can signal multiple CPUs at the same time */ 104 for (uint i = 0; i < runs; ++i) { 105 LTRACEF("Counter test (%u CPUs)\n", num_cpus); 106 int counter = 0; 107 108 spin_lock_saved_state_t irqstate; 109 arch_interrupt_save(&irqstate, SPIN_LOCK_FLAG_INTERRUPTS); 110 111 mp_sync_exec(MP_IPI_TARGET_ALL_BUT_LOCAL, 0, counter_task, &counter); 112 113 arch_interrupt_restore(irqstate, SPIN_LOCK_FLAG_INTERRUPTS); 114 115 LTRACEF(" Finished signaling all but local (%d)\n", counter); 116 ASSERT((uint)counter == num_cpus - 1); 117 } 118 119 for (uint i = 0; i < runs; ++i) { 120 LTRACEF("Deadlock test\n"); 121 deadlock_test(); 122 LTRACEF("Deadlock test passed\n"); 123 } 124 125 END_TEST; 126} 127 128UNITTEST_START_TESTCASE(sync_ipi_tests) 129UNITTEST("sync_ipi_tests", sync_ipi_tests) 130UNITTEST_END_TESTCASE(sync_ipi_tests, "sync_ipi_tests", "sync_ipi_tests"); 131