/* $OpenBSD: ipifuncs.c,v 1.26 2022/12/05 08:59:28 visa Exp $ */ /* $NetBSD: ipifuncs.c,v 1.40 2008/04/28 20:23:10 martin Exp $ */ /*- * Copyright (c) 1998, 1999, 2000, 2001 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, * NASA Ames Research Center. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include int mips64_ipi_intr(void *); void mips64_ipi_nop(void); void smp_rendezvous_action(void); void mips64_ipi_ddb(void); void mips64_multicast_ipi(unsigned int, unsigned int); struct evcount ipi_count; unsigned int ipi_irq = 0; unsigned int ipi_mailbox[MAXCPUS]; /* Variables needed for SMP rendezvous. */ struct mutex smp_rv_mtx; volatile unsigned long smp_rv_map; void (*volatile smp_rv_action_func)(void *arg); void * volatile smp_rv_func_arg; volatile unsigned int smp_rv_waiters[2]; /* * NOTE: This table must be kept in order with the bit definitions * in . */ typedef void (*ipifunc_t)(void); ipifunc_t ipifuncs[MIPS64_NIPIS] = { mips64_ipi_nop, smp_rendezvous_action, mips64_ipi_ddb }; /* * Initialize IPI state for a CPU. */ void mips64_ipi_init(void) { cpuid_t cpuid = cpu_number(); int error; if (!cpuid) { mtx_init(&smp_rv_mtx, IPL_HIGH); evcount_attach(&ipi_count, "ipi", &ipi_irq); evcount_percpu(&ipi_count); } hw_ipi_intr_clear(cpuid); error = hw_ipi_intr_establish(mips64_ipi_intr, cpuid); if (error) panic("hw_ipi_intr_establish failed:%d", error); } /* * Process IPIs for a CPU. */ int mips64_ipi_intr(void *arg) { unsigned int pending_ipis, bit; unsigned int cpuid = (unsigned int)(unsigned long)arg; KASSERT (cpuid == cpu_number()); /* clear ipi interrupt */ hw_ipi_intr_clear(cpuid); /* get and clear pending ipis */ pending_ipis = atomic_swap_uint(&ipi_mailbox[cpuid], 0); if (pending_ipis > 0) { for (bit = 0; bit < MIPS64_NIPIS; bit++) { if (pending_ipis & (1UL << bit)) { (*ipifuncs[bit])(); evcount_inc(&ipi_count); } } } return 1; } static void do_send_ipi(unsigned int cpuid, unsigned int ipimask) { #ifdef DEBUG struct cpu_info *ci = get_cpu_info(cpuid); if (ci == NULL) panic("mips_send_ipi: bogus cpu_id"); if (!CPU_IS_RUNNING(ci)) panic("mips_send_ipi: CPU %u not running", cpuid); #endif atomic_setbits_int(&ipi_mailbox[cpuid], ipimask); hw_ipi_intr_set(cpuid); } /* * Send an interprocessor interrupt. */ void mips64_send_ipi(unsigned int cpuid, unsigned int ipimask) { /* * Ensure that preceding stores are visible to other CPUs * before sending the IPI. */ membar_producer(); do_send_ipi(cpuid, ipimask); } /* * Send an IPI to all in the list but ourselves. */ void mips64_multicast_ipi(unsigned int cpumask, unsigned int ipimask) { struct cpu_info *ci; CPU_INFO_ITERATOR cii; cpumask &= ~(1 << cpu_number()); CPU_INFO_FOREACH(cii, ci) { if (!(cpumask & (1UL << ci->ci_cpuid)) || !CPU_IS_RUNNING(ci)) continue; do_send_ipi(ci->ci_cpuid, ipimask); } } void mips64_ipi_nop(void) { #ifdef DEBUG printf("mips64_ipi_nop on cpu%lu\n", cpu_number()); #endif } /* * All-CPU rendezvous. CPUs are signalled, all execute the setup function * (if specified), rendezvous, execute the action function (if specified), * rendezvous again, execute the teardown function (if specified), and then * resume. * * Note that the supplied external functions _must_ be reentrant and aware * that they are running in parallel and in an unknown lock context. */ void smp_rendezvous_action(void) { unsigned int cpumask = 1 << cpu_number(); /* Signal readiness and acquire pre-action state. */ atomic_setbits_int(&smp_rv_waiters[0], cpumask); membar_enter_after_atomic(); while (smp_rv_waiters[0] != smp_rv_map) CPU_BUSY_CYCLE(); (*smp_rv_action_func)(smp_rv_func_arg); /* Release post-action state and signal completion. */ membar_exit_before_atomic(); atomic_setbits_int(&smp_rv_waiters[1], cpumask); } void smp_rendezvous_cpus(unsigned long map, void (* action_func)(void *), void *arg) { unsigned int cpumask = 1 << cpu_number(); if (cpumask == map) { (*action_func)(arg); return; } mtx_enter(&smp_rv_mtx); /* set static function pointers */ smp_rv_map = map; smp_rv_action_func = action_func; smp_rv_func_arg = arg; smp_rv_waiters[0] = 0; smp_rv_waiters[1] = 0; /* Release pre-action state before IPI send. */ membar_exit(); /* signal other processors, which will enter the IPI with interrupts off */ mips64_multicast_ipi(map, MIPS64_IPI_RENDEZVOUS); /* Check if the current CPU is in the map */ if (map & cpumask) smp_rendezvous_action(); while (smp_rv_waiters[1] != smp_rv_map) CPU_BUSY_CYCLE(); /* Acquire post-action state after read. */ membar_sync(); smp_rv_action_func = NULL; mtx_leave(&smp_rv_mtx); } void mips64_ipi_ddb(void) { #ifdef DDB db_enter(); #endif }