cregit-Linux how code gets into the kernel

Release 4.14 arch/arm64/kernel/smp.c

/*
 * SMP initialisation and IPI support
 * Based on arch/arm/kernel/smp.c
 *
 * Copyright (C) 2012 ARM Ltd.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <linux/acpi.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/spinlock.h>
#include <linux/sched/mm.h>
#include <linux/sched/hotplug.h>
#include <linux/sched/task_stack.h>
#include <linux/interrupt.h>
#include <linux/cache.h>
#include <linux/profile.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/err.h>
#include <linux/cpu.h>
#include <linux/smp.h>
#include <linux/seq_file.h>
#include <linux/irq.h>
#include <linux/percpu.h>
#include <linux/clockchips.h>
#include <linux/completion.h>
#include <linux/of.h>
#include <linux/irq_work.h>
#include <linux/kexec.h>

#include <asm/alternative.h>
#include <asm/atomic.h>
#include <asm/cacheflush.h>
#include <asm/cpu.h>
#include <asm/cputype.h>
#include <asm/cpu_ops.h>
#include <asm/mmu_context.h>
#include <asm/numa.h>
#include <asm/pgtable.h>
#include <asm/pgalloc.h>
#include <asm/processor.h>
#include <asm/smp_plat.h>
#include <asm/sections.h>
#include <asm/tlbflush.h>
#include <asm/ptrace.h>
#include <asm/virt.h>


#define CREATE_TRACE_POINTS
#include <trace/events/ipi.h>

DEFINE_PER_CPU_READ_MOSTLY(int, cpu_number);

EXPORT_PER_CPU_SYMBOL(cpu_number);

/*
 * as from 2.5, kernels no longer have an init_tasks structure
 * so we need some other way of telling a new secondary core
 * where to place its SVC stack
 */

struct secondary_data secondary_data;
/* Number of CPUs which aren't online, but looping in kernel text. */

int cpus_stuck_in_kernel;


enum ipi_msg_type {
	
IPI_RESCHEDULE,
	
IPI_CALL_FUNC,
	
IPI_CPU_STOP,
	
IPI_CPU_CRASH_STOP,
	
IPI_TIMER,
	
IPI_IRQ_WORK,
	
IPI_WAKEUP
};

#ifdef CONFIG_ARM64_VHE

/* Whether the boot CPU is running in HYP mode or not*/

static bool boot_cpu_hyp_mode;


static inline void save_boot_cpu_run_el(void) { boot_cpu_hyp_mode = is_kernel_in_hyp_mode(); }

Contributors

PersonTokensPropCommitsCommitProp
Suzuki K. Poulose14100.00%1100.00%
Total14100.00%1100.00%


static inline bool is_boot_cpu_in_hyp_mode(void) { return boot_cpu_hyp_mode; }

Contributors

PersonTokensPropCommitsCommitProp
Suzuki K. Poulose12100.00%1100.00%
Total12100.00%1100.00%

/* * Verify that a secondary CPU is running the kernel at the same * EL as that of the boot CPU. */
void verify_cpu_run_el(void) { bool in_el2 = is_kernel_in_hyp_mode(); bool boot_cpu_el2 = is_boot_cpu_in_hyp_mode(); if (in_el2 ^ boot_cpu_el2) { pr_crit("CPU%d: mismatched Exception Level(EL%d) with boot CPU(EL%d)\n", smp_processor_id(), in_el2 ? 2 : 1, boot_cpu_el2 ? 2 : 1); cpu_panic_kernel(); } }

Contributors

PersonTokensPropCommitsCommitProp
Suzuki K. Poulose50100.00%1100.00%
Total50100.00%1100.00%

#else
static inline void save_boot_cpu_run_el(void) {}

Contributors

PersonTokensPropCommitsCommitProp
Suzuki K. Poulose8100.00%1100.00%
Total8100.00%1100.00%

#endif #ifdef CONFIG_HOTPLUG_CPU static int op_cpu_kill(unsigned int cpu); #else
static inline int op_cpu_kill(unsigned int cpu) { return -ENOSYS; }

Contributors

PersonTokensPropCommitsCommitProp
Suzuki K. Poulose15100.00%1100.00%
Total15100.00%1100.00%

#endif /* * Boot a secondary CPU, and assign it the specified idle task. * This also gives us the initial stack to use for this CPU. */
static int boot_secondary(unsigned int cpu, struct task_struct *idle) { if (cpu_ops[cpu]->cpu_boot) return cpu_ops[cpu]->cpu_boot(cpu); return -EOPNOTSUPP; }

Contributors

PersonTokensPropCommitsCommitProp
Catalin Marinas2358.97%150.00%
Mark Rutland1641.03%150.00%
Total39100.00%2100.00%

static DECLARE_COMPLETION(cpu_running);
int __cpu_up(unsigned int cpu, struct task_struct *idle) { int ret; long status; /* * We need to tell the secondary core where to find its stack and the * page tables. */ secondary_data.task = idle; secondary_data.stack = task_stack_page(idle) + THREAD_SIZE; update_cpu_boot_status(CPU_MMU_OFF); __flush_dcache_area(&secondary_data, sizeof(secondary_data)); /* * Now bring the CPU into our world. */ ret = boot_secondary(cpu, idle); if (ret == 0) { /* * CPU was successfully started, wait for it to come online or * time out. */ wait_for_completion_timeout(&cpu_running, msecs_to_jiffies(1000)); if (!cpu_online(cpu)) { pr_crit("CPU%u: failed to come online\n", cpu); ret = -EIO; } } else { pr_err("CPU%u: failed to boot: %d\n", cpu, ret); } secondary_data.task = NULL; secondary_data.stack = NULL; status = READ_ONCE(secondary_data.status); if (ret && status) { if (status == CPU_MMU_OFF) status = READ_ONCE(__early_cpu_boot_status); switch (status) { default: pr_err("CPU%u: failed in unknown state : 0x%lx\n", cpu, status); break; case CPU_KILL_ME: if (!op_cpu_kill(cpu)) { pr_crit("CPU%u: died during early boot\n", cpu); break; } /* Fall through */ pr_crit("CPU%u: may not have shut down cleanly\n", cpu); case CPU_STUCK_IN_KERNEL: pr_crit("CPU%u: is stuck in kernel\n", cpu); cpus_stuck_in_kernel++; break; case CPU_PANIC_KERNEL: panic("CPU%u detected unsupported configuration\n", cpu); } } return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Catalin Marinas11248.07%125.00%
Suzuki K. Poulose10846.35%125.00%
Mark Rutland125.15%125.00%
Ard Biesheuvel10.43%125.00%
Total233100.00%4100.00%

/* * This is the secondary CPU boot entry. We're using this CPUs * idle thread stack, but a set of temporary page tables. */
asmlinkage void secondary_start_kernel(void) { struct mm_struct *mm = &init_mm; unsigned int cpu; cpu = task_cpu(current); set_my_cpu_offset(per_cpu_offset(cpu)); /* * All kernel threads share the same mm context; grab a * reference and switch to it. */ mmgrab(mm); current->active_mm = mm; /* * TTBR0 is only used for the identity mapping at this stage. Make it * point to zero page to avoid speculatively fetching new entries. */ cpu_uninstall_idmap(); preempt_disable(); trace_hardirqs_off(); /* * If the system has established the capabilities, make sure * this CPU ticks all of those. If it doesn't, the CPU will * fail to come online. */ check_local_cpu_capabilities(); if (cpu_ops[cpu]->cpu_postboot) cpu_ops[cpu]->cpu_postboot(); /* * Log the CPU info before it is marked online and might get read. */ cpuinfo_store_cpu(); /* * Enable GIC and timers. */ notify_cpu_starting(cpu); store_cpu_topology(cpu); /* * OK, now it's safe to let the boot CPU continue. Wait for * the CPU migration code to notice that the CPU is online * before we continue. */ pr_info("CPU%u: Booted secondary processor [%08x]\n", cpu, read_cpuid_id()); update_cpu_boot_status(CPU_BOOT_SUCCESS); set_cpu_online(cpu, true); complete(&cpu_running); local_irq_enable(); local_async_enable(); /* * OK, it's off to the idle thread for us */ cpu_startup_entry(CPUHP_AP_ONLINE_IDLE); }

Contributors

PersonTokensPropCommitsCommitProp
Catalin Marinas5843.28%315.79%
Mark Rutland3626.87%421.05%
Suzuki K. Poulose1914.18%421.05%
Marc Zyngier64.48%15.26%
Mark Brown42.99%15.26%
Thomas Gleixner42.99%210.53%
Will Deacon32.24%15.26%
Ard Biesheuvel21.49%15.26%
Vegard Nossum10.75%15.26%
David Daney10.75%15.26%
Total134100.00%19100.00%

#ifdef CONFIG_HOTPLUG_CPU
static int op_cpu_disable(unsigned int cpu) { /* * If we don't have a cpu_die method, abort before we reach the point * of no return. CPU0 may not have an cpu_ops, so test for it. */ if (!cpu_ops[cpu] || !cpu_ops[cpu]->cpu_die) return -EOPNOTSUPP; /* * We may need to abort a hot unplug for some other mechanism-specific * reason. */ if (cpu_ops[cpu]->cpu_disable) return cpu_ops[cpu]->cpu_disable(cpu); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Mark Rutland55100.00%1100.00%
Total55100.00%1100.00%

/* * __cpu_disable runs on the processor to be shutdown. */
int __cpu_disable(void) { unsigned int cpu = smp_processor_id(); int ret; ret = op_cpu_disable(cpu); if (ret) return ret; /* * Take this CPU offline. Once we clear this, we can't return, * and we must not schedule until we're ready to give up the cpu. */ set_cpu_online(cpu, false); /* * OK - migrate IRQs away from this CPU */ irq_migrate_all_off_this_cpu(); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Mark Rutland4597.83%150.00%
Yang Yingliang12.17%150.00%
Total46100.00%2100.00%


static int op_cpu_kill(unsigned int cpu) { /* * If we have no means of synchronising with the dying CPU, then assume * that it is really dead. We can only wait for an arbitrary length of * time and hope that it's dead, so let's skip the wait and just hope. */ if (!cpu_ops[cpu]->cpu_kill) return 0; return cpu_ops[cpu]->cpu_kill(cpu); }

Contributors

PersonTokensPropCommitsCommitProp
Ashwin Chaugule3497.14%150.00%
Mark Rutland12.86%150.00%
Total35100.00%2100.00%

/* * called on the thread which is asking for a CPU to be shutdown - * waits until shutdown has completed, or it is timed out. */
void __cpu_die(unsigned int cpu) { int err; if (!cpu_wait_death(cpu, 5)) { pr_crit("CPU%u: cpu didn't die\n", cpu); return; } pr_notice("CPU%u: shutdown\n", cpu); /* * Now that the dying CPU is beyond the point of no return w.r.t. * in-kernel synchronisation, try to get the firwmare to help us to * verify that it has really left the kernel before we consider * clobbering anything it might still be using. */ err = op_cpu_kill(cpu); if (err) pr_warn("CPU%d may not have shut down cleanly: %d\n", cpu, err); }

Contributors

PersonTokensPropCommitsCommitProp
Mark Rutland4575.00%250.00%
Ashwin Chaugule1220.00%125.00%
Paul E. McKenney35.00%125.00%
Total60100.00%4100.00%

/* * Called from the idle thread for the CPU which has been shutdown. * * Note that we disable IRQs here, but do not re-enable them * before returning to the caller. This is also the behaviour * of the other hotplug-cpu capable cores, so presumably coming * out of idle fixes this. */
void cpu_die(void) { unsigned int cpu = smp_processor_id(); idle_task_exit(); local_irq_disable(); /* Tell __cpu_die() that this CPU is now safe to dispose of */ (void)cpu_report_death(); /* * Actually shutdown the CPU. This must never fail. The specific hotplug * mechanism must perform all required cache maintenance to ensure that * no dirty lines are lost in the process of shutting down the CPU. */ cpu_ops[cpu]->cpu_die(cpu); BUG(); }

Contributors

PersonTokensPropCommitsCommitProp
Mark Rutland3687.80%150.00%
Paul E. McKenney512.20%150.00%
Total41100.00%2100.00%

#endif /* * Kill the calling secondary CPU, early in bringup before it is turned * online. */
void cpu_die_early(void) { int cpu = smp_processor_id(); pr_crit("CPU%d: will not boot\n", cpu); /* Mark this CPU absent */ set_cpu_present(cpu, 0); #ifdef CONFIG_HOTPLUG_CPU update_cpu_boot_status(CPU_KILL_ME); /* Check if we can park ourselves */ if (cpu_ops[cpu] && cpu_ops[cpu]->cpu_die) cpu_ops[cpu]->cpu_die(cpu); #endif update_cpu_boot_status(CPU_STUCK_IN_KERNEL); cpu_park_loop(); }

Contributors

PersonTokensPropCommitsCommitProp
Suzuki K. Poulose71100.00%2100.00%
Total71100.00%2100.00%


static void __init hyp_mode_check(void) { if (is_hyp_mode_available()) pr_info("CPU: All CPU(s) started at EL2\n"); else if (is_hyp_mode_mismatched()) WARN_TAINT(1, TAINT_CPU_OUT_OF_SPEC, "CPU: CPUs started in inconsistent modes"); else pr_info("CPU: All CPU(s) started at EL1\n"); }

Contributors

PersonTokensPropCommitsCommitProp
Jonas Rabenstein40100.00%1100.00%
Total40100.00%1100.00%


void __init smp_cpus_done(unsigned int max_cpus) { pr_info("SMP: Total of %d processors activated.\n", num_online_cpus()); setup_cpu_features(); hyp_mode_check(); apply_alternatives_all(); mark_linear_text_alias_ro(); }

Contributors

PersonTokensPropCommitsCommitProp
Catalin Marinas1756.67%116.67%
Jonas Rabenstein413.33%116.67%
Suzuki K. Poulose310.00%116.67%
Ard Biesheuvel310.00%116.67%
Andre Przywara26.67%116.67%
Will Deacon13.33%116.67%
Total30100.00%6100.00%


void __init smp_prepare_boot_cpu(void) { set_my_cpu_offset(per_cpu_offset(smp_processor_id())); /* * Initialise the static keys early as they may be enabled by the * cpufeature code. */ jump_label_init(); cpuinfo_store_boot_cpu(); save_boot_cpu_run_el(); /* * Run the errata work around checks on the boot CPU, once we have * initialised the cpu feature infrastructure from * cpuinfo_store_boot_cpu() above. */ update_cpu_errata_workarounds(); }

Contributors

PersonTokensPropCommitsCommitProp
Will Deacon1135.48%120.00%
Catalin Marinas1032.26%240.00%
Suzuki K. Poulose1032.26%240.00%
Total31100.00%5100.00%


static u64 __init of_get_cpu_mpidr(struct device_node *dn) { const __be32 *cell; u64 hwid; /* * A cpu node with missing "reg" property is * considered invalid to build a cpu_logical_map * entry. */ cell = of_get_property(dn, "reg", NULL); if (!cell) { pr_err("%pOF: missing reg property\n", dn); return INVALID_HWID; } hwid = of_read_number(cell, of_n_addr_cells(dn)); /* * Non affinity bits must be set to 0 in the DT */ if (hwid & ~MPIDR_HWID_BITMASK) { pr_err("%pOF: invalid reg property\n", dn); return INVALID_HWID; } return hwid; }

Contributors

PersonTokensPropCommitsCommitProp
Lorenzo Pieralisi8297.62%150.00%
Rob Herring22.38%150.00%
Total84100.00%2100.00%

/* * Duplicate MPIDRs are a recipe for disaster. Scan all initialized * entries and check for duplicates. If any is found just ignore the * cpu. cpu_logical_map was initialized to INVALID_HWID to avoid * matching valid MPIDR values. */
static bool __init is_mpidr_duplicate(unsigned int cpu, u64 hwid) { unsigned int i; for (i = 1; (i < cpu) && (i < NR_CPUS); i++) if (cpu_logical_map(i) == hwid) return true; return false; }

Contributors

PersonTokensPropCommitsCommitProp
Lorenzo Pieralisi54100.00%1100.00%
Total54100.00%1100.00%

/* * Initialize cpu operations for a logical cpu and * set it in the possible mask on success */
static int __init smp_cpu_setup(int cpu) { if (cpu_read_ops(cpu)) return -ENODEV; if (cpu_ops[cpu]->cpu_init(cpu)) return -ENODEV; set_cpu_possible(cpu, true); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Lorenzo Pieralisi47100.00%1100.00%
Total47100.00%1100.00%

static bool bootcpu_valid __initdata; static unsigned int cpu_count = 1; #ifdef CONFIG_ACPI static struct acpi_madt_generic_interrupt cpu_madt_gicc[NR_CPUS];
struct acpi_madt_generic_interrupt *acpi_cpu_get_madt_gicc(int cpu) { return &cpu_madt_gicc[cpu]; }

Contributors

PersonTokensPropCommitsCommitProp
Mark Rutland17100.00%1100.00%
Total17100.00%1100.00%

/* * acpi_map_gic_cpu_interface - parse processor MADT entry * * Carry out sanity checks on MADT processor entry and initialize * cpu_logical_map on success */
static void __init acpi_map_gic_cpu_interface(struct acpi_madt_generic_interrupt *processor) { u64 hwid = processor->arm_mpidr; if (!(processor->flags & ACPI_MADT_ENABLED)) { pr_debug("skipping disabled CPU entry with 0x%llx MPIDR\n", hwid); return; } if (hwid & ~MPIDR_HWID_BITMASK || hwid == INVALID_HWID) { pr_err("skipping CPU entry with invalid MPIDR 0x%llx\n", hwid); return; } if (is_mpidr_duplicate(cpu_count, hwid)) { pr_err("duplicate CPU MPIDR 0x%llx in MADT\n", hwid); return; } /* Check if GICC structure of boot CPU is available in the MADT */ if (cpu_logical_map(0) == hwid) { if (bootcpu_valid) { pr_err("duplicate boot CPU MPIDR: 0x%llx in MADT\n", hwid); return; } bootcpu_valid = true; cpu_madt_gicc[0] = *processor; early_map_cpu_to_node(0, acpi_numa_get_nid(0, hwid)); return; } if (cpu_count >= NR_CPUS) return; /* map the logical cpu id to cpu MPIDR */ cpu_logical_map(cpu_count) = hwid; cpu_madt_gicc[cpu_count] = *processor; /* * Set-up the ACPI parking protocol cpu entries * while initializing the cpu_logical_map to * avoid parsing MADT entries multiple times for * nothing (ie a valid cpu_logical_map entry should * contain a valid parking protocol data set to * initialize the cpu if the parking protocol is * the only available enable method). */ acpi_set_mailbox_entry(cpu_count, processor); early_map_cpu_to_node(cpu_count, acpi_numa_get_nid(cpu_count, hwid)); cpu_count++; }

Contributors

PersonTokensPropCommitsCommitProp
Lorenzo Pieralisi8950.28%333.33%
Hanjun Guo2916.38%222.22%
Javi Merino2614.69%111.11%
Mark Rutland169.04%111.11%
Catalin Marinas95.08%111.11%
Will Deacon84.52%111.11%
Total177100.00%9100.00%


static int __init acpi_parse_gic_cpu_interface(struct acpi_subtable_header *header, const unsigned long end) { struct acpi_madt_generic_interrupt *processor; processor = (struct acpi_madt_generic_interrupt *)header; if (BAD_MADT_GICC_ENTRY(processor, end)) return -EINVAL; acpi_table_print_madt_entry(header); acpi_map_gic_cpu_interface(processor); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Lorenzo Pieralisi5189.47%133.33%
Javi Merino58.77%133.33%
Al Stone11.75%133.33%
Total57100.00%3100.00%

#else #define acpi_table_parse_madt(...) do { } while (0) #endif /* * Enumerate the possible CPU set from the device tree and build the * cpu logical map array containing MPIDR values related to logical * cpus. Assumes that cpu_logical_map(0) has already been initialized. */
static void __init of_parse_and_init_cpus(void) { struct device_node *dn; for_each_node_by_type(dn, "cpu") { u64 hwid = of_get_cpu_mpidr(dn); if (hwid == INVALID_HWID) goto next; if (is_mpidr_duplicate(cpu_count, hwid)) { pr_err("%pOF: duplicate cpu reg properties in the DT\n", dn); goto next; } /* * The numbering scheme requires that the boot CPU * must be assigned logical id 0. Record it so that * the logical map built from DT is validated and can * be used. */ if (hwid == cpu_logical_map(0)) { if (bootcpu_valid) { pr_err("%pOF: duplicate boot cpu reg property in DT\n", dn); goto next; } bootcpu_valid = true; early_map_cpu_to_node(0, of_node_to_nid(dn)); /* * cpu_logical_map has already been * initialized and the boot cpu doesn't need * the enable-method so continue without * incrementing cpu. */ continue; } if (cpu_count >= NR_CPUS) goto next; pr_debug("cpu logical map 0x%llx\n", hwid); cpu_logical_map(cpu_count) = hwid; early_map_cpu_to_node(cpu_count, of_node_to_nid(dn)); next: cpu_count++; } }

Contributors

PersonTokensPropCommitsCommitProp
Javi Merino5639.44%112.50%
Lorenzo Pieralisi4430.99%112.50%
Catalin Marinas1711.97%112.50%
Zhen Lei107.04%112.50%
Ganapatrao Kulkarni107.04%112.50%
Rob Herring21.41%112.50%
Dmitry Torokhov21.41%112.50%
JiSheng Zhang10.70%112.50%
Total142100.00%8100.00%

/* * Enumerate the possible CPU set from the device tree or ACPI and build the * cpu logical map array containing MPIDR values related to logical * cpus. Assumes that cpu_logical_map(0) has already been initialized. */
void __init smp_init_cpus(void) { int i; if (acpi_disabled) of_parse_and_init_cpus(); else /* * do a walk of MADT to determine how many CPUs * we have including disabled CPUs, and get information * we need for SMP init */ acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_INTERRUPT, acpi_parse_gic_cpu_interface, 0); if (cpu_count > nr_cpu_ids) pr_warn("Number of cores (%d) exceeds configured maximum of %u - clipping\n", cpu_count, nr_cpu_ids); if (!bootcpu_valid) { pr_err("missing boot CPU MPIDR, not enabling secondaries\n"); return; } /* * We need to set the cpu_logical_map entries before enabling * the cpus so that cpu processor description entries (DT cpu nodes * and ACPI MADT entries) can be retrieved by matching the cpu hwid * with entries in cpu_logical_map while initializing the cpus. * If the cpu set-up fails, invalidate the cpu_logical_map entry. */ for (i = 1; i < nr_cpu_ids; i++) { if (cpu_logical_map(i) != INVALID_HWID) { if (smp_cpu_setup(i)) cpu_logical_map(i) = INVALID_HWID; } } }

Contributors

PersonTokensPropCommitsCommitProp
Lorenzo Pieralisi4848.98%233.33%
Javi Merino3636.73%116.67%
Catalin Marinas1010.20%116.67%
Kefeng Wang33.06%116.67%
Alexey Dobriyan11.02%116.67%
Total98100.00%6100.00%


void __init smp_prepare_cpus(unsigned int max_cpus) { int err; unsigned int cpu; unsigned int this_cpu; init_cpu_topology(); this_cpu = smp_processor_id(); store_cpu_topology(this_cpu); numa_store_cpu_info(this_cpu); /* * If UP is mandated by "nosmp" (which implies "maxcpus=0"), don't set * secondary CPUs present. */ if (max_cpus == 0) return; /* * Initialise the present map (which describes the set of CPUs * actually populated at the present time) and release the * secondaries from the bootloader. */ for_each_possible_cpu(cpu) { per_cpu(cpu_number, cpu) = cpu; if (cpu == smp_processor_id()) continue; if (!cpu_ops[cpu]) continue; err = cpu_ops[cpu]->cpu_prepare(cpu); if (err) continue; set_cpu_present(cpu, true); numa_store_cpu_info(cpu); } }

Contributors

PersonTokensPropCommitsCommitProp
Catalin Marinas4339.45%112.50%
David Daney2018.35%112.50%
Marc Zyngier1715.60%112.50%
Mark Rutland1311.93%225.00%
Suzuki K. Poulose98.26%225.00%
Mark Brown76.42%112.50%
Total109100.00%8100.00%

void (*__smp_cross_call)(const struct cpumask *, unsigned int);
void __init set_smp_cross_call(void (*fn)(const struct cpumask *, unsigned int)) { __smp_cross_call = fn; }

Contributors

PersonTokensPropCommitsCommitProp
Nico Pitre1352.00%133.33%
Catalin Marinas832.00%133.33%
Larry Bassel416.00%133.33%
Total25100.00%3100.00%

static const char *ipi_types[NR_IPI] __tracepoint_string = { #define S(x,s) [x] = s S(IPI_RESCHEDULE, "Rescheduling interrupts"), S(IPI_CALL_FUNC, "Function call interrupts"), S(IPI_CPU_STOP, "CPU stop interrupts"), S(IPI_CPU_CRASH_STOP, "CPU stop (for crash dump) interrupts"), S(IPI_TIMER, "Timer broadcast interrupts"), S(IPI_IRQ_WORK, "IRQ work interrupts"), S(IPI_WAKEUP, "CPU wake-up interrupts"), };
static void smp_cross_call(const struct cpumask *target, unsigned int ipinr) { trace_ipi_raise(target, ipi_types[ipinr]); __smp_cross_call(target, ipinr); }

Contributors

PersonTokensPropCommitsCommitProp
Nico Pitre33100.00%1100.00%
Total33100.00%1100.00%


void show_ipi_list(struct seq_file *p, int prec) { unsigned int cpu, i; for (i = 0; i < NR_IPI; i++) { seq_printf(p, "%*s%u:%s", prec - 1, "IPI", i, prec >= 4 ? " " : ""); for_each_online_cpu(cpu) seq_printf(p, "%10u ", __get_irq_stat(cpu, ipi_irqs[i])); seq_printf(p, " %s\n", ipi_types[i]); } }

Contributors

PersonTokensPropCommitsCommitProp
Catalin Marinas8998.89%150.00%
Sudeep KarkadaNagesha11.11%150.00%
Total90100.00%2100.00%


u64 smp_irq_stat_cpu(unsigned int cpu) { u64 sum = 0; int i; for (i = 0; i < NR_IPI; i++) sum += __get_irq_stat(cpu, ipi_irqs[i]); return sum; }

Contributors

PersonTokensPropCommitsCommitProp
Catalin Marinas45100.00%1100.00%
Total45100.00%1100.00%


void arch_send_call_function_ipi_mask(const struct cpumask *mask) { smp_cross_call(mask, IPI_CALL_FUNC); }

Contributors

PersonTokensPropCommitsCommitProp
Nico Pitre18100.00%1100.00%
Total18100.00%1100.00%


void arch_send_call_function_single_ipi(int cpu) { smp_cross_call(cpumask_of(cpu), IPI_CALL_FUNC); }

Contributors

PersonTokensPropCommitsCommitProp
Nico Pitre1794.44%150.00%
Jiang Liu15.56%150.00%
Total18100.00%2100.00%

#ifdef CONFIG_ARM64_ACPI_PARKING_PROTOCOL
void arch_send_wakeup_ipi_mask(const struct cpumask *mask) { smp_cross_call(mask, IPI_WAKEUP); }

Contributors

PersonTokensPropCommitsCommitProp
Lorenzo Pieralisi18100.00%1100.00%
Total18100.00%1100.00%

#endif #ifdef CONFIG_IRQ_WORK
void arch_irq_work_raise(void) { if (__smp_cross_call) smp_cross_call(cpumask_of(smp_processor_id()), IPI_IRQ_WORK); }

Contributors

PersonTokensPropCommitsCommitProp
Nico Pitre22100.00%1100.00%
Total22100.00%1100.00%

#endif /* * ipi_cpu_stop - handle IPI from smp_send_stop() */
static void ipi_cpu_stop(unsigned int cpu) { set_cpu_online(cpu, false); local_irq_disable(); while (1) cpu_relax(); }

Contributors

PersonTokensPropCommitsCommitProp
Catalin Marinas27100.00%1100.00%
Total27100.00%1100.00%

#ifdef CONFIG_KEXEC_CORE static atomic_t waiting_for_crash_ipi = ATOMIC_INIT(0); #endif
static void ipi_cpu_crash_stop(unsigned int cpu, struct pt_regs *regs) { #ifdef CONFIG_KEXEC_CORE crash_save_cpu(regs, cpu); atomic_dec(&waiting_for_crash_ipi); local_irq_disable(); #ifdef CONFIG_HOTPLUG_CPU if (cpu_ops[cpu]->cpu_die) cpu_ops[cpu]->cpu_die(cpu); #endif /* just in case */ cpu_park_loop(); #endif }

Contributors

PersonTokensPropCommitsCommitProp
AKASHI Takahiro64100.00%1100.00%
Total64100.00%1100.00%

/* * Main handler for inter-processor interrupts */
void handle_IPI(int ipinr, struct pt_regs *regs) { unsigned int cpu = smp_processor_id(); struct pt_regs *old_regs = set_irq_regs(regs); if ((unsigned)ipinr < NR_IPI) { trace_ipi_entry_rcuidle(ipi_types[ipinr]); __inc_irq_stat(cpu, ipi_irqs[ipinr]); } switch (ipinr) { case IPI_RESCHEDULE: scheduler_ipi(); break; case IPI_CALL_FUNC: irq_enter(); generic_smp_call_function_interrupt(); irq_exit(); break; case IPI_CPU_STOP: irq_enter(); ipi_cpu_stop(cpu); irq_exit(); break; case IPI_CPU_CRASH_STOP: if (IS_ENABLED(CONFIG_KEXEC_CORE)) { irq_enter(); ipi_cpu_crash_stop(cpu, regs); unreachable(); } break; #ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST case IPI_TIMER: irq_enter(); tick_receive_broadcast(); irq_exit(); break; #endif #ifdef CONFIG_IRQ_WORK case IPI_IRQ_WORK: irq_enter(); irq_work_run(); irq_exit(); break; #endif #ifdef CONFIG_ARM64_ACPI_PARKING_PROTOCOL case IPI_WAKEUP: WARN_ONCE(!acpi_parking_protocol_valid(cpu), "CPU%u: Wake-up IPI outside the ACPI parking protocol\n", cpu); break; #endif default: pr_crit("CPU%u: Unknown IPI message 0x%x\n", cpu, ipinr); break; } if ((unsigned)ipinr < NR_IPI) trace_ipi_exit_rcuidle(ipi_types[ipinr]); set_irq_regs(old_regs); }

Contributors

PersonTokensPropCommitsCommitProp
Catalin Marinas10347.47%114.29%
Lorenzo Pieralisi4018.43%228.57%
Nico Pitre2812.90%114.29%
AKASHI Takahiro2611.98%114.29%
Larry Bassel188.29%114.29%
Stephen Boyd20.92%114.29%
Total217100.00%7100.00%


void smp_send_reschedule(int cpu) { smp_cross_call(cpumask_of(cpu), IPI_RESCHEDULE); }

Contributors

PersonTokensPropCommitsCommitProp
Catalin Marinas18100.00%1100.00%
Total18100.00%1100.00%

#ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST
void tick_broadcast(const struct cpumask *mask) { smp_cross_call(mask, IPI_TIMER); }

Contributors

PersonTokensPropCommitsCommitProp
Lorenzo Pieralisi18100.00%1100.00%
Total18100.00%1100.00%

#endif
void smp_send_stop(void) { unsigned long timeout; if (num_online_cpus() > 1) { cpumask_t mask; cpumask_copy(&mask, cpu_online_mask); cpumask_clear_cpu(smp_processor_id(), &mask); if (system_state <= SYSTEM_RUNNING) pr_crit("SMP: stopping secondary CPUs\n"); smp_cross_call(&mask, IPI_CPU_STOP); } /* Wait up to one second for other CPUs to stop */ timeout = USEC_PER_SEC; while (num_online_cpus() > 1 && timeout--) udelay(1); if (num_online_cpus() > 1) pr_warning("SMP: failed to stop secondary CPUs %*pbl\n", cpumask_pr_args(cpu_online_mask)); }

Contributors

PersonTokensPropCommitsCommitProp
Catalin Marinas7780.21%125.00%
Jan Glauber1616.67%125.00%
Rusty Russell22.08%125.00%
Thomas Gleixner11.04%125.00%
Total96100.00%4100.00%

#ifdef CONFIG_KEXEC_CORE
void crash_smp_send_stop(void) { static int cpus_stopped; cpumask_t mask; unsigned long timeout; /* * This function can be called twice in panic path, but obviously * we execute this only once. */ if (cpus_stopped) return; cpus_stopped = 1; if (num_online_cpus() == 1) return; cpumask_copy(&mask, cpu_online_mask); cpumask_clear_cpu(smp_processor_id(), &mask); atomic_set(&waiting_for_crash_ipi, num_online_cpus() - 1); pr_crit("SMP: stopping secondary CPUs\n"); smp_cross_call(&mask, IPI_CPU_CRASH_STOP); /* Wait up to one second for other CPUs to stop */ timeout = USEC_PER_SEC; while ((atomic_read(&waiting_for_crash_ipi) > 0) && timeout--) udelay(1); if (atomic_read(&waiting_for_crash_ipi) > 0) pr_warning("SMP: failed to stop secondary CPUs %*pbl\n", cpumask_pr_args(&mask)); }

Contributors

PersonTokensPropCommitsCommitProp
AKASHI Takahiro10887.80%150.00%
Hoeun Ryu1512.20%150.00%
Total123100.00%2100.00%


bool smp_crash_stop_failed(void) { return (atomic_read(&waiting_for_crash_ipi) > 0); }

Contributors

PersonTokensPropCommitsCommitProp
AKASHI Takahiro18100.00%1100.00%
Total18100.00%1100.00%

#endif /* * not supported here */
int setup_profiling_timer(unsigned int multiplier) { return -EINVAL; }

Contributors

PersonTokensPropCommitsCommitProp
Catalin Marinas13100.00%1100.00%
Total13100.00%1100.00%


static bool have_cpu_die(void) { #ifdef CONFIG_HOTPLUG_CPU int any_cpu = raw_smp_processor_id(); if (cpu_ops[any_cpu] && cpu_ops[any_cpu]->cpu_die) return true; #endif return false; }

Contributors

PersonTokensPropCommitsCommitProp
James Morse3487.18%150.00%
Mark Salter512.82%150.00%
Total39100.00%2100.00%


bool cpus_are_stuck_in_kernel(void) { bool smp_spin_tables = (num_possible_cpus() > 1 && !have_cpu_die()); return !!cpus_stuck_in_kernel || smp_spin_tables; }

Contributors

PersonTokensPropCommitsCommitProp
James Morse28100.00%1100.00%
Total28100.00%1100.00%


Overall Contributors

PersonTokensPropCommitsCommitProp
Catalin Marinas84128.04%45.41%
Lorenzo Pieralisi55418.47%56.76%
Suzuki K. Poulose35311.77%1013.51%
Mark Rutland32610.87%1013.51%
AKASHI Takahiro2478.24%11.35%
Nico Pitre1464.87%11.35%
Javi Merino1264.20%11.35%
James Morse622.07%11.35%
Jonas Rabenstein471.57%11.35%
Ashwin Chaugule461.53%11.35%
Larry Bassel341.13%11.35%
Hanjun Guo290.97%22.70%
Will Deacon230.77%45.41%
Marc Zyngier230.77%22.70%
David Daney210.70%11.35%
Jan Glauber160.53%11.35%
Hoeun Ryu150.50%11.35%
Ganapatrao Kulkarni130.43%11.35%
Mark Brown110.37%11.35%
Zhen Lei100.33%11.35%
Paul E. McKenney80.27%11.35%
Ingo Molnar70.23%34.05%
Ard Biesheuvel60.20%34.05%
Thomas Gleixner50.17%34.05%
Mark Salter50.17%11.35%
Andre Przywara50.17%11.35%
Rob Herring40.13%11.35%
Kefeng Wang30.10%11.35%
Dmitry Torokhov20.07%11.35%
Rusty Russell20.07%11.35%
Stephen Boyd20.07%11.35%
Jiang Liu10.03%11.35%
Vegard Nossum10.03%11.35%
Alexey Dobriyan10.03%11.35%
Yang Yingliang10.03%11.35%
Sudeep KarkadaNagesha10.03%11.35%
JiSheng Zhang10.03%11.35%
Al Stone10.03%11.35%
Total2999100.00%74100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.