kernel: smp: introduce k_smp_cpu_start

This renames z_smp_cpu_start() to k_smp_cpu_start().
This effectively promotes z_smp_cpu_start() into a public API
which allows out of tree usage. Since this is a new API,
we can afford to change it signature, where it allows
an additional initialization steps to be done before a newly
powered up CPU starts participating in scheduling threads
to run.

Signed-off-by: Daniel Leung <daniel.leung@intel.com>
This commit is contained in:
Daniel Leung 2023-11-06 14:29:35 -08:00 committed by Anas Nashif
commit 89b231e7e2
6 changed files with 78 additions and 14 deletions

View file

@ -18,6 +18,5 @@ void z_smp_thread_swap(void);
void z_init_cpu(int id);
void z_sched_ipi(void);
void z_smp_start_cpu(int id);
#endif

View file

@ -0,0 +1,35 @@
/*
* Copyright (c) 2023 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_INCLUDE_KERNEL_SMP_H_
#define ZEPHYR_INCLUDE_KERNEL_SMP_H_
typedef void (*smp_init_fn)(void *arg);
/**
* @brief Start a CPU.
*
* This routine is used to manually start the CPU specified
* by @a id. It may be called to restart a CPU that had been
* stopped or powered down, as well as some other scenario.
* After the CPU has finished initialization, the CPU will be
* ready to participate in thread scheduling and execution.
*
* @note This function must not be used on currently running
* CPU. The target CPU must be in off state, or in
* certain architectural state(s) where the CPU is
* permitted to go through the power up process.
* Detection of such state(s) must be provided by
* the platform layers.
*
* @param id ID of target CPU.
* @param fn Function to be called before letting scheduler
* run.
* @param arg Argument to @a fn.
*/
void k_smp_cpu_start(int id, smp_init_fn fn, void *arg);
#endif /* ZEPHYR_INCLUDE_KERNEL_SMP_H_ */

View file

@ -4,6 +4,7 @@
#include <zephyr/kernel.h>
#include <zephyr/kernel_structs.h>
#include <zephyr/kernel/smp.h>
#include <zephyr/spinlock.h>
#include <kswap.h>
#include <kernel_internal.h>
@ -12,6 +13,23 @@ static atomic_t global_lock;
static atomic_t cpu_start_flag;
static atomic_t ready_flag;
/**
* Struct holding the function to be called before handing off
* to schedule and its argument.
*/
static struct cpu_start_cb {
/**
* Function to be called before handing off to scheduler.
* Can be NULL.
*/
smp_init_fn fn;
/** Argument to @ref cpu_start_fn.fn. */
void *arg;
} cpu_start_fn;
static struct k_spinlock cpu_start_lock;
unsigned int z_smp_global_lock(void)
{
unsigned int key = arch_irq_lock();
@ -80,35 +98,48 @@ void z_smp_thread_swap(void)
static inline FUNC_NORETURN void smp_init_top(void *arg)
{
struct k_thread dummy_thread;
struct cpu_start_cb *csc = arg;
(void)atomic_set(&ready_flag, 1);
wait_for_start_signal(arg);
wait_for_start_signal(&cpu_start_flag);
z_dummy_thread_init(&dummy_thread);
#ifdef CONFIG_SYS_CLOCK_EXISTS
smp_timer_init();
#endif
/* Do additional initialization steps if needed. */
if ((csc != NULL) && (csc->fn != NULL)) {
csc->fn(csc->arg);
}
z_swap_unlocked();
CODE_UNREACHABLE; /* LCOV_EXCL_LINE */
}
static void start_cpu(int id, atomic_t *start_flag)
static void start_cpu(int id, struct cpu_start_cb *csc)
{
z_init_cpu(id);
(void)atomic_clear(&ready_flag);
arch_start_cpu(id, z_interrupt_stacks[id], CONFIG_ISR_STACK_SIZE,
smp_init_top, start_flag);
smp_init_top, csc);
while (!atomic_get(&ready_flag)) {
local_delay();
}
}
void z_smp_start_cpu(int id)
void k_smp_cpu_start(int id, smp_init_fn fn, void *arg)
{
k_spinlock_key_t key = k_spin_lock(&cpu_start_lock);
cpu_start_fn.fn = fn;
cpu_start_fn.arg = arg;
(void)atomic_set(&cpu_start_flag, 1); /* async, don't care */
start_cpu(id, &cpu_start_flag);
start_cpu(id, &cpu_start_fn);
k_spin_unlock(&cpu_start_lock, key);
}
void z_smp_init(void)
@ -118,7 +149,7 @@ void z_smp_init(void)
unsigned int num_cpus = arch_num_cpus();
for (int i = 1; i < num_cpus; i++) {
start_cpu(i, &cpu_start_flag);
start_cpu(i, NULL);
}
(void)atomic_set(&cpu_start_flag, 1);
}

View file

@ -3,6 +3,7 @@
*/
#include <stdlib.h>
#include <zephyr/kernel.h>
#include <zephyr/kernel/smp.h>
#include <zephyr/ztest.h>
#include <zephyr/cache.h>
@ -188,7 +189,7 @@ static void halt_and_restart(int cpu)
k_msleep(50);
}
z_smp_start_cpu(cpu);
k_smp_cpu_start(cpu, NULL, NULL);
/* Startup can be slow */
k_msleep(50);

View file

@ -3,6 +3,7 @@
*/
#include <zephyr/kernel.h>
#include <zephyr/kernel/smp.h>
#include <zephyr/ztest.h>
#include "tests.h"
@ -25,8 +26,6 @@ volatile bool mp_flag;
struct k_thread cpu_thr;
K_THREAD_STACK_DEFINE(thr_stack, STACKSZ);
extern void z_smp_start_cpu(int id);
static void thread_fn(void *a, void *b, void *c)
{
int cpuid = (int) a;
@ -62,7 +61,7 @@ ZTEST(intel_adsp_boot, test_1st_smp_boot_delay)
zassert_false(mp_flag, "cpu %d must not be running yet", i);
/* Start the second CPU */
z_smp_start_cpu(i);
k_smp_cpu_start(i, NULL, NULL);
/* Verify the thread ran */
k_busy_wait(CPU_START_DELAY);

View file

@ -3,6 +3,7 @@
*/
#include <zephyr/kernel.h>
#include <zephyr/kernel/smp.h>
#include <zephyr/ztest.h>
/* Experimentally 10ms is enough time to get the second CPU to run on
@ -25,8 +26,6 @@ volatile bool mp_flag;
struct k_thread cpu1_thr;
K_THREAD_STACK_DEFINE(thr_stack, STACKSZ);
extern void z_smp_start_cpu(int id);
static void thread_fn(void *a, void *b, void *c)
{
mp_flag = true;
@ -47,7 +46,7 @@ ZTEST(smp_boot_delay, test_smp_boot_delay)
zassert_false(mp_flag, "CPU1 must not be running yet");
/* Start the second CPU */
z_smp_start_cpu(1);
k_smp_cpu_start(1, NULL, NULL);
/* Verify the thread ran */
k_busy_wait(CPU_START_DELAY);