| // 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, ¨_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); |