// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2025 Ant Group * Author: Tiwei Bie * * Based on the previous implementation in TT mode * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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);