[mp] restructure the sequence of how cpus are brought up

- Move a bit of the shared logic of secondary bootstrapping into a new
  function, lk_secondary_cpu_entry_early() which sets the current cpu
  pointer before calling the first half of the secondary LK_INIT
  routines.
- Create the per cpu idle threads on the main cpu instead of the
  secondary as they come up.
- Tweak all of the SMP capable architectures to use this new path.
- Move the top level mp routines into a separate file top/mp.c
- A bit more correctly ifdef out more SMP code.
This commit is contained in:
Travis Geiselbrecht
2025-10-08 05:00:31 +00:00
parent e47183725d
commit e4d65228b5
15 changed files with 141 additions and 85 deletions

View File

@@ -158,8 +158,11 @@ typedef struct thread {
void thread_init_early(void);
void thread_init(void);
void thread_become_idle(void) __NO_RETURN;
#if WITH_SMP
void thread_secondary_cpu_init_early(void);
void thread_secondary_cpu_entry(void) __NO_RETURN;
void thread_create_secondary_cpu_idle_thread(uint cpu);
#endif
void thread_set_name(const char *name);
void thread_set_priority(int priority);
thread_t *thread_create(const char *name, thread_start_routine entry, void *arg, int priority, size_t stack_size);

View File

@@ -859,7 +859,7 @@ void thread_become_idle(void) {
#if WITH_SMP
char name[16];
snprintf(name, sizeof(name), "idle %d", arch_curr_cpu_num());
snprintf(name, sizeof(name), "idle %u", arch_curr_cpu_num());
thread_set_name(name);
#else
thread_set_name("idle");
@@ -880,21 +880,27 @@ void thread_become_idle(void) {
idle_thread_routine();
}
/* create an idle thread for the cpu we're on, and start scheduling */
#if WITH_SMP
// Get this secondary cpu onto thread context
void thread_secondary_cpu_init_early(void) {
DEBUG_ASSERT(arch_ints_disabled());
/* construct an idle thread to cover our cpu */
// Look up our idle thread and mark it as current on this cpu
uint cpu = arch_curr_cpu_num();
thread_t *t = idle_thread(cpu);
DEBUG_ASSERT(t && t->state == THREAD_RUNNING && t->flags & THREAD_FLAG_IDLE);
set_current_thread(t);
}
// Construct a secondary's cpu idle thread such that it appears to already be running
void thread_create_secondary_cpu_idle_thread(uint cpu) {
thread_t *t = idle_thread(cpu);
char name[16];
snprintf(name, sizeof(name), "idle %u", cpu);
init_thread_struct(t, name);
thread_set_pinned_cpu(t, cpu);
/* half construct this thread, since we're already running */
// Half construct this thread as if it were running at max priority, since the secondary cpu will
// start executing this thread when it starts.
t->priority = HIGHEST_PRIORITY;
t->state = THREAD_RUNNING;
t->flags = THREAD_FLAG_DETACHED | THREAD_FLAG_IDLE;
@@ -903,27 +909,32 @@ void thread_secondary_cpu_init_early(void) {
wait_queue_init(&t->retcode_wait_queue);
THREAD_LOCK(state);
list_add_head(&thread_list, &t->thread_list_node);
set_current_thread(t);
THREAD_UNLOCK(state);
}
// We should be on the idle thread for the secondary cpu, drop to idle priority and
// start properly scheduling by enabling interrupts and yielding to the scheduler.
void thread_secondary_cpu_entry(void) {
uint cpu = arch_curr_cpu_num();
thread_t *t = get_current_thread();
DEBUG_ASSERT(t && t->state == THREAD_RUNNING && t->flags & THREAD_FLAG_IDLE);
t->priority = IDLE_PRIORITY;
// Mark the local cpu as active and idle
mp_set_curr_cpu_active(true);
mp_set_cpu_idle(cpu);
/* enable interrupts and start the scheduler on this cpu */
// Enable interrupts and start the scheduler on this cpu
arch_enable_ints();
thread_yield();
// Fall through to the idle thread routine
idle_thread_routine();
}
#endif // WITH_SMP
static const char *thread_state_to_str(enum thread_state state) {
switch (state) {