Contributors: 14
Author Tokens Token Proportion Commits Commit Proportion
Tiwei Bie 701 82.67% 1 3.23%
Jeff Dike 77 9.08% 11 35.48%
Al Viro 25 2.95% 6 19.35%
Benjamin Berg 18 2.12% 1 3.23%
Paolo 'Blaisorblade' Giarrusso 7 0.83% 2 6.45%
Ingo Molnar 6 0.71% 2 6.45%
Thomas Gleixner 3 0.35% 1 3.23%
Andy Shevchenko 2 0.24% 1 3.23%
Ingo van Lil 2 0.24% 1 3.23%
Alexey Dobriyan 2 0.24% 1 3.23%
Gennady Sharapov 2 0.24% 1 3.23%
Chris Wedgwood 1 0.12% 1 3.23%
Eric W. Biedermann 1 0.12% 1 3.23%
Alex Dewar 1 0.12% 1 3.23%
Total 848 31


// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (C) 2025 Ant Group
 * Author: Tiwei Bie <tiwei.btw@antgroup.com>
 *
 * Based on the previous implementation in TT mode
 * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
 */

#include <linux/sched.h>
#include <linux/sched/task.h>
#include <linux/sched/task_stack.h>
#include <linux/module.h>
#include <linux/processor.h>
#include <linux/threads.h>
#include <linux/cpu.h>
#include <linux/hardirq.h>
#include <linux/smp.h>
#include <linux/smp-internal.h>
#include <init.h>
#include <kern.h>
#include <os.h>
#include <smp.h>

enum {
	UML_IPI_RES = 0,
	UML_IPI_CALL_SINGLE,
	UML_IPI_CALL,
	UML_IPI_STOP,
};

void arch_smp_send_reschedule(int cpu)
{
	os_send_ipi(cpu, UML_IPI_RES);
}

void arch_send_call_function_single_ipi(int cpu)
{
	os_send_ipi(cpu, UML_IPI_CALL_SINGLE);
}

void arch_send_call_function_ipi_mask(const struct cpumask *mask)
{
	int cpu;

	for_each_cpu(cpu, mask)
		os_send_ipi(cpu, UML_IPI_CALL);
}

void smp_send_stop(void)
{
	int cpu, me = smp_processor_id();

	for_each_online_cpu(cpu) {
		if (cpu == me)
			continue;
		os_send_ipi(cpu, UML_IPI_STOP);
	}
}

static void ipi_handler(int vector, struct uml_pt_regs *regs)
{
	struct pt_regs *old_regs = set_irq_regs((struct pt_regs *)regs);
	int cpu = raw_smp_processor_id();

	irq_enter();

	if (current->mm)
		os_alarm_process(current->mm->context.id.pid);

	switch (vector) {
	case UML_IPI_RES:
		inc_irq_stat(irq_resched_count);
		scheduler_ipi();
		break;

	case UML_IPI_CALL_SINGLE:
		inc_irq_stat(irq_call_count);
		generic_smp_call_function_single_interrupt();
		break;

	case UML_IPI_CALL:
		inc_irq_stat(irq_call_count);
		generic_smp_call_function_interrupt();
		break;

	case UML_IPI_STOP:
		set_cpu_online(cpu, false);
		while (1)
			pause();
		break;

	default:
		pr_err("CPU#%d received unknown IPI (vector=%d)!\n", cpu, vector);
		break;
	}

	irq_exit();
	set_irq_regs(old_regs);
}

void uml_ipi_handler(int vector)
{
	struct uml_pt_regs r = { .is_user = 0 };

	preempt_disable();
	ipi_handler(vector, &r);
	preempt_enable();
}

/* AP states used only during CPU startup */
enum {
	UML_CPU_PAUSED = 0,
	UML_CPU_RUNNING,
};

static int cpu_states[NR_CPUS];

static int start_secondary(void *unused)
{
	int err, cpu = raw_smp_processor_id();

	notify_cpu_starting(cpu);
	set_cpu_online(cpu, true);

	err = um_setup_timer();
	if (err)
		panic("CPU#%d failed to setup timer, err = %d", cpu, err);

	local_irq_enable();

	cpu_startup_entry(CPUHP_AP_ONLINE_IDLE);

	return 0;
}

void uml_start_secondary(void *opaque)
{
	int cpu = raw_smp_processor_id();
	struct mm_struct *mm = &init_mm;
	struct task_struct *idle;

	stack_protections((unsigned long) &cpu_irqstacks[cpu]);
	set_sigstack(&cpu_irqstacks[cpu], THREAD_SIZE);

	set_cpu_present(cpu, true);
	os_futex_wait(&cpu_states[cpu], UML_CPU_PAUSED);

	smp_rmb(); /* paired with smp_wmb() in __cpu_up() */

	idle = cpu_tasks[cpu];
	idle->thread_info.cpu = cpu;

	mmgrab(mm);
	idle->active_mm = mm;

	idle->thread.request.thread.proc = start_secondary;
	idle->thread.request.thread.arg = NULL;

	new_thread(task_stack_page(idle), &idle->thread.switch_buf,
		   new_thread_handler);
	os_start_secondary(opaque, &idle->thread.switch_buf);
}

void __init smp_prepare_cpus(unsigned int max_cpus)
{
	int err, cpu, me = smp_processor_id();
	unsigned long deadline;

	os_init_smp();

	for_each_possible_cpu(cpu) {
		if (cpu == me)
			continue;

		pr_debug("Booting processor %d...\n", cpu);
		err = os_start_cpu_thread(cpu);
		if (err) {
			pr_crit("CPU#%d failed to start cpu thread, err = %d",
				cpu, err);
			continue;
		}

		deadline = jiffies + msecs_to_jiffies(1000);
		spin_until_cond(cpu_present(cpu) ||
				time_is_before_jiffies(deadline));

		if (!cpu_present(cpu))
			pr_crit("CPU#%d failed to boot\n", cpu);
	}
}

int __cpu_up(unsigned int cpu, struct task_struct *tidle)
{
	cpu_tasks[cpu] = tidle;
	smp_wmb(); /* paired with smp_rmb() in uml_start_secondary() */
	cpu_states[cpu] = UML_CPU_RUNNING;
	os_futex_wake(&cpu_states[cpu]);
	spin_until_cond(cpu_online(cpu));

	return 0;
}

void __init smp_cpus_done(unsigned int max_cpus)
{
}

/* Set in uml_ncpus_setup */
int uml_ncpus = 1;

void __init prefill_possible_map(void)
{
	int cpu;

	for (cpu = 0; cpu < uml_ncpus; cpu++)
		set_cpu_possible(cpu, true);
	for (; cpu < NR_CPUS; cpu++)
		set_cpu_possible(cpu, false);
}

static int __init uml_ncpus_setup(char *line, int *add)
{
	*add = 0;

	if (kstrtoint(line, 10, &uml_ncpus)) {
		os_warn("%s: Couldn't parse '%s'\n", __func__, line);
		return -1;
	}

	uml_ncpus = clamp(uml_ncpus, 1, NR_CPUS);

	return 0;
}

__uml_setup("ncpus=", uml_ncpus_setup,
"ncpus=<# of desired CPUs>\n"
"    This tells UML how many virtual processors to start. The maximum\n"
"    number of supported virtual processors can be obtained by querying\n"
"    the CONFIG_NR_CPUS option using --showconfig.\n\n"
);

EXPORT_SYMBOL(uml_curr_cpu);