linux/arch/riscv/kernel/smpboot.c
<<
>>
Prefs
   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#include <asm/alternative.h>
  36
  37#include "head.h"
  38
  39static DECLARE_COMPLETION(cpu_running);
  40
  41void __init smp_prepare_boot_cpu(void)
  42{
  43        init_cpu_topology();
  44#ifdef CONFIG_RISCV_ERRATA_ALTERNATIVE
  45        apply_boot_alternatives();
  46#endif
  47}
  48
  49void __init smp_prepare_cpus(unsigned int max_cpus)
  50{
  51        int cpuid;
  52        int ret;
  53        unsigned int curr_cpuid;
  54
  55        curr_cpuid = smp_processor_id();
  56        numa_store_cpu_info(curr_cpuid);
  57        numa_add_cpu(curr_cpuid);
  58
  59        /* This covers non-smp usecase mandated by "nosmp" option */
  60        if (max_cpus == 0)
  61                return;
  62
  63        for_each_possible_cpu(cpuid) {
  64                if (cpuid == curr_cpuid)
  65                        continue;
  66                if (cpu_ops[cpuid]->cpu_prepare) {
  67                        ret = cpu_ops[cpuid]->cpu_prepare(cpuid);
  68                        if (ret)
  69                                continue;
  70                }
  71                set_cpu_present(cpuid, true);
  72                numa_store_cpu_info(cpuid);
  73        }
  74}
  75
  76void __init setup_smp(void)
  77{
  78        struct device_node *dn;
  79        int hart;
  80        bool found_boot_cpu = false;
  81        int cpuid = 1;
  82
  83        cpu_set_ops(0);
  84
  85        for_each_of_cpu_node(dn) {
  86                hart = riscv_of_processor_hartid(dn);
  87                if (hart < 0)
  88                        continue;
  89
  90                if (hart == cpuid_to_hartid_map(0)) {
  91                        BUG_ON(found_boot_cpu);
  92                        found_boot_cpu = 1;
  93                        early_map_cpu_to_node(0, of_node_to_nid(dn));
  94                        continue;
  95                }
  96                if (cpuid >= NR_CPUS) {
  97                        pr_warn("Invalid cpuid [%d] for hartid [%d]\n",
  98                                cpuid, hart);
  99                        break;
 100                }
 101
 102                cpuid_to_hartid_map(cpuid) = hart;
 103                early_map_cpu_to_node(cpuid, of_node_to_nid(dn));
 104                cpuid++;
 105        }
 106
 107        BUG_ON(!found_boot_cpu);
 108
 109        if (cpuid > nr_cpu_ids)
 110                pr_warn("Total number of cpus [%d] is greater than nr_cpus option value [%d]\n",
 111                        cpuid, nr_cpu_ids);
 112
 113        for (cpuid = 1; cpuid < nr_cpu_ids; cpuid++) {
 114                if (cpuid_to_hartid_map(cpuid) != INVALID_HARTID) {
 115                        cpu_set_ops(cpuid);
 116                        set_cpu_possible(cpuid, true);
 117                }
 118        }
 119}
 120
 121static int start_secondary_cpu(int cpu, struct task_struct *tidle)
 122{
 123        if (cpu_ops[cpu]->cpu_start)
 124                return cpu_ops[cpu]->cpu_start(cpu, tidle);
 125
 126        return -EOPNOTSUPP;
 127}
 128
 129int __cpu_up(unsigned int cpu, struct task_struct *tidle)
 130{
 131        int ret = 0;
 132        tidle->thread_info.cpu = cpu;
 133
 134        ret = start_secondary_cpu(cpu, tidle);
 135        if (!ret) {
 136                wait_for_completion_timeout(&cpu_running,
 137                                            msecs_to_jiffies(1000));
 138
 139                if (!cpu_online(cpu)) {
 140                        pr_crit("CPU%u: failed to come online\n", cpu);
 141                        ret = -EIO;
 142                }
 143        } else {
 144                pr_crit("CPU%u: failed to start\n", cpu);
 145        }
 146
 147        return ret;
 148}
 149
 150void __init smp_cpus_done(unsigned int max_cpus)
 151{
 152}
 153
 154/*
 155 * C entry point for a secondary processor.
 156 */
 157asmlinkage __visible void smp_callin(void)
 158{
 159        struct mm_struct *mm = &init_mm;
 160        unsigned int curr_cpuid = smp_processor_id();
 161
 162        riscv_clear_ipi();
 163
 164        /* All kernel threads share the same mm context.  */
 165        mmgrab(mm);
 166        current->active_mm = mm;
 167
 168        notify_cpu_starting(curr_cpuid);
 169        numa_add_cpu(curr_cpuid);
 170        update_siblings_masks(curr_cpuid);
 171        set_cpu_online(curr_cpuid, 1);
 172
 173        /*
 174         * Remote TLB flushes are ignored while the CPU is offline, so emit
 175         * a local TLB flush right now just in case.
 176         */
 177        local_flush_tlb_all();
 178        complete(&cpu_running);
 179        /*
 180         * Disable preemption before enabling interrupts, so we don't try to
 181         * schedule a CPU that hasn't actually started yet.
 182         */
 183        local_irq_enable();
 184        cpu_startup_entry(CPUHP_AP_ONLINE_IDLE);
 185}
 186