os_cpc.c
2026/7/19大约 3 分钟附录源码附录
os_cpc.c
路径: kernel\source\os_cpc.c
/**
***********************************************************************************************************************
* Copyright (c) 2022, China Mobile Communications Group Co.,Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* @file os_cpc.c
*
* @brief cross processor call.
*
* @revision
* Date Author Notes
* 2022-01-12 OneOS Team First version
***********************************************************************************************************************
*/
#include <oneos_config.h>
#include <os_types.h>
#include <os_task.h>
#include <os_spinlock.h>
#include <os_errno.h>
#include <os_stddef.h>
#include "os_kernel_internal.h"
#include <arch_barrier.h>
#include <arch_atomic.h>
#include <arch_misc.h>
#include <arch_interrupt.h>
#include <os_cpc.h>
/* clang-format off */
const os_ubase_t MASK_BITS[33] =
{
0x00,
0x01,
0x03,
0x07,
0x0F,
0x1F,
0x3F,
0x7F,
0xFF,
0x01FF,
0x03FF,
0x07FF,
0x0FFF,
0x1FFF,
0x3FFF,
0x7FFF,
0xFFFF,
0x01FFFF,
0x03FFFF,
0x07FFFF,
0x0FFFFF,
0x1FFFFF,
0x3FFFFF,
0x7FFFFF,
0xFFFFFF,
0x01FFFFFF,
0x03FFFFFF,
0x07FFFFFF,
0x0FFFFFFF,
0x1FFFFFFF,
0x3FFFFFFF,
0x7FFFFFFF,
0xFFFFFFFFU
};
/* clang-format on */
#ifdef OS_USING_SMP
os_spinlock_t g_cpc_lock; /* cpc resource lock */
os_cpc_func_t g_cpc_func; /* cpc call function */
void *g_cpc_arg; /* arg of CPC call function */
int32_t g_cpc_flag; /* flag of CPC call function */
os_atomic_t g_cpc_ret; /* return value of CPC call function */
os_atomic_t g_cpc_resp_flag; /* response flag.
Before the calling core sends the IPI,
the bit corresponding to the CPU number is seted.
When the called core responds to the calling core,
the bit corresponding to the CPU number is cleared.*/
static void os_cpc_ipi_handle(int vector, void *param)
{
os_cpc_func_t func;
void *arg;
int32_t flag;
OS_UNREFERENCE(vector);
OS_UNREFERENCE(param);
func = g_cpc_func;
arg = g_cpc_arg;
flag = g_cpc_flag;
if (OS_NULL == func)
{
os_atomic_clear_bit(&g_cpc_resp_flag, os_cpu_id_get());
return;
}
switch (flag)
{
case OS_CPC_SYNC:
if (func(arg) == OS_FAILURE)
{
os_atomic_set(&g_cpc_ret, OS_FAILURE);
}
smp_wmb();
os_atomic_clear_bit(&g_cpc_resp_flag, os_cpu_id_get());
break;
case OS_CPC_SYNC_MANUAL:
func(arg);
break;
case OS_CPC_ASYNC:
default:
os_atomic_clear_bit(&g_cpc_resp_flag, os_cpu_id_get());
func(arg);
break;
}
}
/**
***********************************************************************************************************************
* @brief This function initializes the cross processor call module.
*
* @param[in] None
*
* @return None
***********************************************************************************************************************
*/
os_err_t os_cpc_init(void)
{
os_spin_lock_init(&g_cpc_lock);
g_cpc_func = OS_NULL;
g_cpc_arg = OS_NULL;
os_atomic_set(&g_cpc_ret, OS_SUCCESS);
os_atomic_set(&g_cpc_resp_flag, 0);
os_hw_ipi_handler_install(OS_SMP_IPI_CPC, os_cpc_ipi_handle);
os_hw_interrupt_umask(OS_SMP_IPI_CPC);
return OS_SUCCESS;
}
OS_INIT_CALL(os_cpc_init, OS_INIT_LEVEL_COMPONENT, OS_INIT_SUBLEVEL_LOW);
/**
***********************************************************************************************************************
* @brief The current CPU causes some other CPU to generate an interrupt
* and execute the function specified by os_cpc_call.
*
* @details This interface is used when the calling core needs other cores to execute specified functions.
* The calling core implements the above requirements by sending an ipi to the called core and
* specifying the code interface to be executed by the interrupt handling function.
* The function parameter flag specifies three synchronization mechanisms:OS_CPC_SYNC、OS_CPC_ASYNC、
* OS_CPC_SYNC_MANUAL.
*
* @attention When the synchronization flag is OS_CPC_SYNC,
* the return value of the function is the return value of the callback function.
* When the synchronization flag is OS_CPC_ASYNC,
* the callback function is not executed, and the return value of the function is always OS_SUCCESS.
* When the synchronization flag is OS_CPC_SYNC_MANUAL,
you need to manually call os_cpc_sync to respond.
The return value of the function is also specified by os_cpc_sync
*
* @param[in] cpu called core number.
* @param[in] func called core interrupt callback function.
* @param[in] arg Argument of the called core interrupt callback function.
* @param[in] flag Synchronization Mechanism flag.
*
* @return The called core interrupt callback function execution return value.
* @retval OS_SUCCESS The called core interrupt callback function execution return OS_SUCCESS.
* @retval OS_FAILURE The called core interrupt callback function execution return OS_FAILURE.
***********************************************************************************************************************
*/
os_err_t os_cpc_call(int32_t cpu, os_cpc_func_t func, void *arg, int32_t flag)
{
os_ubase_t irq_save;
os_err_t ret;
if (cpu >= OS_SMP_MAX_CPUS)
{
return OS_INVAL;
}
if (OS_NULL == g_os_current_task[cpu])
{
return OS_FAILURE;
}
if (cpu == os_cpu_id_get())
{
return OS_FAILURE;
}
os_spin_lock_irq_rt(&g_cpc_lock, &irq_save);
os_atomic_set(&g_cpc_resp_flag, 1U << cpu);
g_cpc_func = func;
g_cpc_arg = arg;
g_cpc_flag = flag;
os_atomic_set(&g_cpc_ret, OS_SUCCESS);
os_hw_ipi_send(OS_SMP_IPI_CPC, 1U << cpu);
while (os_atomic_read(&g_cpc_resp_flag) != 0)
{
;
}
ret = os_atomic_read(&g_cpc_ret);
os_spin_unlock_irq(&g_cpc_lock, irq_save);
return ret;
}
/**
***********************************************************************************************************************
* @brief The mechanism of this function is basically the same as os_cpc_call,
* except that multiple cores are called.
*
* @attention When any core returns OS_FAILURE, this function returns OS_FAILURE.
*
* @param[in] cpuset A set of called core.
* @param[in] func called core interrupt callback function.
* @param[in] arg Argument of the called core interrupt callback function.
* @param[in] flag Synchronization Mechanism flag.
*
* @return The called core interrupt callback function execution return value.
* @retval OS_SUCCESS The called core interrupt callback function execution return OS_SUCCESS.
* @retval OS_FAILURE The called core interrupt callback function execution return OS_SUCCESS.
***********************************************************************************************************************
*/
os_err_t os_cpc_broadcast(uint32_t cpuset, os_cpc_func_t func, void *arg, int32_t flag)
{
uint32_t i;
int32_t cpu;
uint32_t target_cpuset;
os_ubase_t irq_save;
os_err_t ret;
cpu = os_cpu_id_get();
if (0 == cpuset)
{
return OS_INVAL;
}
target_cpuset = cpuset;
target_cpuset &= MASK_BITS[OS_SMP_MAX_CPUS];
if (target_cpuset != cpuset)
{
return OS_INVAL;
}
OS_BIT_CLR(target_cpuset, cpu);
for (i = 0; i < OS_SMP_MAX_CPUS; i++)
{
if (OS_NULL == g_os_current_task[i])
{
OS_BIT_CLR(target_cpuset, i);
}
}
if (0 == target_cpuset)
{
return OS_INVAL;
}
os_spin_lock_irq_rt(&g_cpc_lock, &irq_save);
os_atomic_set(&g_cpc_resp_flag, target_cpuset);
g_cpc_func = func;
g_cpc_arg = arg;
g_cpc_flag = flag;
os_atomic_set(&g_cpc_ret, OS_SUCCESS);
os_hw_ipi_send(OS_SMP_IPI_CPC, target_cpuset);
while (os_atomic_read(&g_cpc_resp_flag) != 0)
{
;
}
ret = os_atomic_read(&g_cpc_ret);
os_spin_unlock_irq(&g_cpc_lock, irq_save);
return ret;
}
/**
***********************************************************************************************************************
* @brief This function is used to respond to the OS_CPC_SYNC_MANUAL synchronization mechanism.
*
* @param[in] ret Synchronous response return value.
*
* @return None
***********************************************************************************************************************
*/
void os_cpc_sync(os_err_t ret)
{
if (OS_FAILURE == ret)
{
os_atomic_set(&g_cpc_ret, OS_FAILURE);
}
smp_wmb();
os_atomic_clear_bit(&g_cpc_resp_flag, os_cpu_id_get());
}
#endif /* OS_USING_SMP */