Linux Audio

Check our new training course

In-person Linux kernel drivers training

Jun 16-20, 2025
Register
Loading...
v6.2
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * SMP initialisation and IPI support
  4 * Based on arch/arm64/kernel/smp.c
  5 *
  6 * Copyright (C) 2012 ARM Ltd.
  7 * Copyright (C) 2015 Regents of the University of California
  8 * Copyright (C) 2017 SiFive
  9 */
 10
 11#include <linux/arch_topology.h>
 12#include <linux/module.h>
 13#include <linux/init.h>
 14#include <linux/kernel.h>
 15#include <linux/mm.h>
 16#include <linux/sched.h>
 17#include <linux/kernel_stat.h>
 18#include <linux/notifier.h>
 19#include <linux/cpu.h>
 20#include <linux/percpu.h>
 21#include <linux/delay.h>
 22#include <linux/err.h>
 23#include <linux/irq.h>
 24#include <linux/of.h>
 25#include <linux/sched/task_stack.h>
 26#include <linux/sched/mm.h>
 27#include <asm/cpu_ops.h>
 28#include <asm/irq.h>
 29#include <asm/mmu_context.h>
 30#include <asm/numa.h>
 31#include <asm/tlbflush.h>
 32#include <asm/sections.h>
 33#include <asm/sbi.h>
 34#include <asm/smp.h>
 35
 36#include "head.h"
 37
 38static DECLARE_COMPLETION(cpu_running);
 39
 40void __init smp_prepare_boot_cpu(void)
 41{
 
 42}
 43
 44void __init smp_prepare_cpus(unsigned int max_cpus)
 45{
 46	int cpuid;
 47	int ret;
 48	unsigned int curr_cpuid;
 49
 50	init_cpu_topology();
 51
 52	curr_cpuid = smp_processor_id();
 53	store_cpu_topology(curr_cpuid);
 54	numa_store_cpu_info(curr_cpuid);
 55	numa_add_cpu(curr_cpuid);
 56
 57	/* This covers non-smp usecase mandated by "nosmp" option */
 58	if (max_cpus == 0)
 59		return;
 60
 61	for_each_possible_cpu(cpuid) {
 62		if (cpuid == curr_cpuid)
 63			continue;
 64		if (cpu_ops[cpuid]->cpu_prepare) {
 65			ret = cpu_ops[cpuid]->cpu_prepare(cpuid);
 66			if (ret)
 67				continue;
 68		}
 69		set_cpu_present(cpuid, true);
 70		numa_store_cpu_info(cpuid);
 71	}
 72}
 73
 74void __init setup_smp(void)
 75{
 76	struct device_node *dn;
 77	unsigned long hart;
 78	bool found_boot_cpu = false;
 79	int cpuid = 1;
 80	int rc;
 81
 82	cpu_set_ops(0);
 83
 84	for_each_of_cpu_node(dn) {
 85		rc = riscv_of_processor_hartid(dn, &hart);
 86		if (rc < 0)
 87			continue;
 88
 89		if (hart == cpuid_to_hartid_map(0)) {
 90			BUG_ON(found_boot_cpu);
 91			found_boot_cpu = 1;
 92			early_map_cpu_to_node(0, of_node_to_nid(dn));
 93			continue;
 94		}
 95		if (cpuid >= NR_CPUS) {
 96			pr_warn("Invalid cpuid [%d] for hartid [%lu]\n",
 97				cpuid, hart);
 98			continue;
 99		}
100
101		cpuid_to_hartid_map(cpuid) = hart;
102		early_map_cpu_to_node(cpuid, of_node_to_nid(dn));
103		cpuid++;
104	}
105
106	BUG_ON(!found_boot_cpu);
107
108	if (cpuid > nr_cpu_ids)
109		pr_warn("Total number of cpus [%d] is greater than nr_cpus option value [%d]\n",
110			cpuid, nr_cpu_ids);
111
112	for (cpuid = 1; cpuid < nr_cpu_ids; cpuid++) {
113		if (cpuid_to_hartid_map(cpuid) != INVALID_HARTID) {
114			cpu_set_ops(cpuid);
115			set_cpu_possible(cpuid, true);
116		}
117	}
118}
119
120static int start_secondary_cpu(int cpu, struct task_struct *tidle)
121{
122	if (cpu_ops[cpu]->cpu_start)
123		return cpu_ops[cpu]->cpu_start(cpu, tidle);
124
125	return -EOPNOTSUPP;
126}
127
128int __cpu_up(unsigned int cpu, struct task_struct *tidle)
129{
130	int ret = 0;
131	tidle->thread_info.cpu = cpu;
132
133	ret = start_secondary_cpu(cpu, tidle);
134	if (!ret) {
135		wait_for_completion_timeout(&cpu_running,
136					    msecs_to_jiffies(1000));
137
138		if (!cpu_online(cpu)) {
139			pr_crit("CPU%u: failed to come online\n", cpu);
140			ret = -EIO;
141		}
142	} else {
143		pr_crit("CPU%u: failed to start\n", cpu);
144	}
145
146	return ret;
147}
148
149void __init smp_cpus_done(unsigned int max_cpus)
150{
151}
152
153/*
154 * C entry point for a secondary processor.
155 */
156asmlinkage __visible void smp_callin(void)
157{
158	struct mm_struct *mm = &init_mm;
159	unsigned int curr_cpuid = smp_processor_id();
160
161	riscv_clear_ipi();
162
163	/* All kernel threads share the same mm context.  */
164	mmgrab(mm);
165	current->active_mm = mm;
166
167	store_cpu_topology(curr_cpuid);
168	notify_cpu_starting(curr_cpuid);
169	numa_add_cpu(curr_cpuid);
170	set_cpu_online(curr_cpuid, 1);
171
172	/*
173	 * Remote TLB flushes are ignored while the CPU is offline, so emit
174	 * a local TLB flush right now just in case.
175	 */
176	local_flush_tlb_all();
177	complete(&cpu_running);
178	/*
179	 * Disable preemption before enabling interrupts, so we don't try to
180	 * schedule a CPU that hasn't actually started yet.
181	 */
 
182	local_irq_enable();
183	cpu_startup_entry(CPUHP_AP_ONLINE_IDLE);
184}
v5.9
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * SMP initialisation and IPI support
  4 * Based on arch/arm64/kernel/smp.c
  5 *
  6 * Copyright (C) 2012 ARM Ltd.
  7 * Copyright (C) 2015 Regents of the University of California
  8 * Copyright (C) 2017 SiFive
  9 */
 10
 11#include <linux/arch_topology.h>
 12#include <linux/module.h>
 13#include <linux/init.h>
 14#include <linux/kernel.h>
 15#include <linux/mm.h>
 16#include <linux/sched.h>
 17#include <linux/kernel_stat.h>
 18#include <linux/notifier.h>
 19#include <linux/cpu.h>
 20#include <linux/percpu.h>
 21#include <linux/delay.h>
 22#include <linux/err.h>
 23#include <linux/irq.h>
 24#include <linux/of.h>
 25#include <linux/sched/task_stack.h>
 26#include <linux/sched/mm.h>
 27#include <asm/cpu_ops.h>
 28#include <asm/irq.h>
 29#include <asm/mmu_context.h>
 
 30#include <asm/tlbflush.h>
 31#include <asm/sections.h>
 32#include <asm/sbi.h>
 33#include <asm/smp.h>
 34
 35#include "head.h"
 36
 37static DECLARE_COMPLETION(cpu_running);
 38
 39void __init smp_prepare_boot_cpu(void)
 40{
 41	init_cpu_topology();
 42}
 43
 44void __init smp_prepare_cpus(unsigned int max_cpus)
 45{
 46	int cpuid;
 47	int ret;
 
 
 
 
 
 
 
 
 48
 49	/* This covers non-smp usecase mandated by "nosmp" option */
 50	if (max_cpus == 0)
 51		return;
 52
 53	for_each_possible_cpu(cpuid) {
 54		if (cpuid == smp_processor_id())
 55			continue;
 56		if (cpu_ops[cpuid]->cpu_prepare) {
 57			ret = cpu_ops[cpuid]->cpu_prepare(cpuid);
 58			if (ret)
 59				continue;
 60		}
 61		set_cpu_present(cpuid, true);
 
 62	}
 63}
 64
 65void __init setup_smp(void)
 66{
 67	struct device_node *dn;
 68	int hart;
 69	bool found_boot_cpu = false;
 70	int cpuid = 1;
 
 71
 72	cpu_set_ops(0);
 73
 74	for_each_of_cpu_node(dn) {
 75		hart = riscv_of_processor_hartid(dn);
 76		if (hart < 0)
 77			continue;
 78
 79		if (hart == cpuid_to_hartid_map(0)) {
 80			BUG_ON(found_boot_cpu);
 81			found_boot_cpu = 1;
 
 82			continue;
 83		}
 84		if (cpuid >= NR_CPUS) {
 85			pr_warn("Invalid cpuid [%d] for hartid [%d]\n",
 86				cpuid, hart);
 87			break;
 88		}
 89
 90		cpuid_to_hartid_map(cpuid) = hart;
 
 91		cpuid++;
 92	}
 93
 94	BUG_ON(!found_boot_cpu);
 95
 96	if (cpuid > nr_cpu_ids)
 97		pr_warn("Total number of cpus [%d] is greater than nr_cpus option value [%d]\n",
 98			cpuid, nr_cpu_ids);
 99
100	for (cpuid = 1; cpuid < nr_cpu_ids; cpuid++) {
101		if (cpuid_to_hartid_map(cpuid) != INVALID_HARTID) {
102			cpu_set_ops(cpuid);
103			set_cpu_possible(cpuid, true);
104		}
105	}
106}
107
108static int start_secondary_cpu(int cpu, struct task_struct *tidle)
109{
110	if (cpu_ops[cpu]->cpu_start)
111		return cpu_ops[cpu]->cpu_start(cpu, tidle);
112
113	return -EOPNOTSUPP;
114}
115
116int __cpu_up(unsigned int cpu, struct task_struct *tidle)
117{
118	int ret = 0;
119	tidle->thread_info.cpu = cpu;
120
121	ret = start_secondary_cpu(cpu, tidle);
122	if (!ret) {
123		wait_for_completion_timeout(&cpu_running,
124					    msecs_to_jiffies(1000));
125
126		if (!cpu_online(cpu)) {
127			pr_crit("CPU%u: failed to come online\n", cpu);
128			ret = -EIO;
129		}
130	} else {
131		pr_crit("CPU%u: failed to start\n", cpu);
132	}
133
134	return ret;
135}
136
137void __init smp_cpus_done(unsigned int max_cpus)
138{
139}
140
141/*
142 * C entry point for a secondary processor.
143 */
144asmlinkage __visible void smp_callin(void)
145{
146	struct mm_struct *mm = &init_mm;
147	unsigned int curr_cpuid = smp_processor_id();
148
149	riscv_clear_ipi();
150
151	/* All kernel threads share the same mm context.  */
152	mmgrab(mm);
153	current->active_mm = mm;
154
 
155	notify_cpu_starting(curr_cpuid);
156	update_siblings_masks(curr_cpuid);
157	set_cpu_online(curr_cpuid, 1);
158
159	/*
160	 * Remote TLB flushes are ignored while the CPU is offline, so emit
161	 * a local TLB flush right now just in case.
162	 */
163	local_flush_tlb_all();
164	complete(&cpu_running);
165	/*
166	 * Disable preemption before enabling interrupts, so we don't try to
167	 * schedule a CPU that hasn't actually started yet.
168	 */
169	preempt_disable();
170	local_irq_enable();
171	cpu_startup_entry(CPUHP_AP_ONLINE_IDLE);
172}