761 lines · 17.2 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-or-later/* * * Copyright (C) 2000, 2001 Kanoj Sarcar * Copyright (C) 2000, 2001 Ralf Baechle * Copyright (C) 2000, 2001 Silicon Graphics, Inc. * Copyright (C) 2000, 2001, 2003 Broadcom Corporation */#include <linux/cache.h>#include <linux/delay.h>#include <linux/init.h>#include <linux/interrupt.h>#include <linux/profile.h>#include <linux/smp.h>#include <linux/spinlock.h>#include <linux/threads.h>#include <linux/export.h>#include <linux/time.h>#include <linux/timex.h>#include <linux/sched/mm.h>#include <linux/cpumask.h>#include <linux/cpu.h>#include <linux/rcupdate.h>#include <linux/err.h>#include <linux/ftrace.h>#include <linux/irqdomain.h>#include <linux/of.h>#include <linux/of_irq.h> #include <linux/atomic.h>#include <asm/cpu.h>#include <asm/ginvt.h>#include <asm/processor.h>#include <asm/idle.h>#include <asm/r4k-timer.h>#include <asm/mips-cps.h>#include <asm/mmu_context.h>#include <asm/time.h>#include <asm/setup.h>#include <asm/maar.h> int __cpu_number_map[CONFIG_MIPS_NR_CPU_NR_MAP];   /* Map physical to logical */EXPORT_SYMBOL(__cpu_number_map); int __cpu_logical_map[NR_CPUS];		/* Map logical to physical */EXPORT_SYMBOL(__cpu_logical_map); /* Number of TCs (or siblings in Intel speak) per CPU core */int smp_num_siblings = 1;EXPORT_SYMBOL(smp_num_siblings); /* representing the TCs (or siblings in Intel speak) of each logical CPU */cpumask_t cpu_sibling_map[NR_CPUS] __read_mostly;EXPORT_SYMBOL(cpu_sibling_map); /* representing the core map of multi-core chips of each logical CPU */cpumask_t cpu_core_map[NR_CPUS] __read_mostly;EXPORT_SYMBOL(cpu_core_map); #ifndef CONFIG_HOTPLUG_PARALLELstatic DECLARE_COMPLETION(cpu_starting);static DECLARE_COMPLETION(cpu_running);#endif /* * A logical cpu mask containing only one VPE per core to * reduce the number of IPIs on large MT systems. */cpumask_t cpu_foreign_map[NR_CPUS] __read_mostly;EXPORT_SYMBOL(cpu_foreign_map); /* representing cpus for which sibling maps can be computed */static cpumask_t cpu_sibling_setup_map; /* representing cpus for which core maps can be computed */static cpumask_t cpu_core_setup_map; cpumask_t cpu_coherent_mask; struct cpumask __cpu_primary_thread_mask __read_mostly; unsigned int smp_max_threads __initdata = UINT_MAX; static int __init early_nosmt(char *s){	smp_max_threads = 1;	return 0;}early_param("nosmt", early_nosmt); static int __init early_smt(char *s){	get_option(&s, &smp_max_threads);	/* Ensure at least one thread is available */	smp_max_threads = clamp_val(smp_max_threads, 1U, UINT_MAX);	return 0;}early_param("smt", early_smt); #ifdef CONFIG_GENERIC_IRQ_IPIstatic struct irq_desc *call_desc;static struct irq_desc *sched_desc;#endif static inline void set_cpu_sibling_map(int cpu){	int i; 	cpumask_set_cpu(cpu, &cpu_sibling_setup_map); 	if (smp_num_siblings > 1) {		for_each_cpu(i, &cpu_sibling_setup_map) {			if (cpus_are_siblings(cpu, i)) {				cpumask_set_cpu(i, &cpu_sibling_map[cpu]);				cpumask_set_cpu(cpu, &cpu_sibling_map[i]);			}		}	} else		cpumask_set_cpu(cpu, &cpu_sibling_map[cpu]);} static inline void set_cpu_core_map(int cpu){	int i; 	cpumask_set_cpu(cpu, &cpu_core_setup_map); 	for_each_cpu(i, &cpu_core_setup_map) {		if (cpu_data[cpu].package == cpu_data[i].package) {			cpumask_set_cpu(i, &cpu_core_map[cpu]);			cpumask_set_cpu(cpu, &cpu_core_map[i]);		}	}} /* * Calculate a new cpu_foreign_map mask whenever a * new cpu appears or disappears. */void calculate_cpu_foreign_map(void){	int i, k, core_present;	cpumask_t temp_foreign_map; 	/* Re-calculate the mask */	cpumask_clear(&temp_foreign_map);	for_each_online_cpu(i) {		core_present = 0;		for_each_cpu(k, &temp_foreign_map)			if (cpus_are_siblings(i, k))				core_present = 1;		if (!core_present)			cpumask_set_cpu(i, &temp_foreign_map);	} 	for_each_online_cpu(i)		cpumask_andnot(&cpu_foreign_map[i],			       &temp_foreign_map, &cpu_sibling_map[i]);} const struct plat_smp_ops *mp_ops;EXPORT_SYMBOL(mp_ops); void register_smp_ops(const struct plat_smp_ops *ops){	if (mp_ops)		printk(KERN_WARNING "Overriding previously set SMP ops\n"); 	mp_ops = ops;} #ifdef CONFIG_GENERIC_IRQ_IPIvoid mips_smp_send_ipi_single(int cpu, unsigned int action){	mips_smp_send_ipi_mask(cpumask_of(cpu), action);} void mips_smp_send_ipi_mask(const struct cpumask *mask, unsigned int action){	unsigned long flags;	unsigned int core;	int cpu; 	local_irq_save(flags); 	switch (action) {	case SMP_CALL_FUNCTION:		__ipi_send_mask(call_desc, mask);		break; 	case SMP_RESCHEDULE_YOURSELF:		__ipi_send_mask(sched_desc, mask);		break; 	default:		BUG();	} 	if (mips_cpc_present()) {		for_each_cpu(cpu, mask) {			if (cpus_are_siblings(cpu, smp_processor_id()))				continue; 			core = cpu_core(&cpu_data[cpu]); 			while (!cpumask_test_cpu(cpu, &cpu_coherent_mask)) {				mips_cm_lock_other_cpu(cpu, CM_GCR_Cx_OTHER_BLOCK_LOCAL);				mips_cpc_lock_other(core);				write_cpc_co_cmd(CPC_Cx_CMD_PWRUP);				mips_cpc_unlock_other();				mips_cm_unlock_other();			}		}	} 	local_irq_restore(flags);}  static irqreturn_t ipi_resched_interrupt(int irq, void *dev_id){	scheduler_ipi(); 	return IRQ_HANDLED;} static irqreturn_t ipi_call_interrupt(int irq, void *dev_id){	generic_smp_call_function_interrupt(); 	return IRQ_HANDLED;} static void smp_ipi_init_one(unsigned int virq, const char *name,			     irq_handler_t handler){	int ret; 	irq_set_handler(virq, handle_percpu_irq);	ret = request_irq(virq, handler, IRQF_PERCPU, name, NULL);	BUG_ON(ret);} static unsigned int call_virq, sched_virq; int mips_smp_ipi_allocate(const struct cpumask *mask){	int virq;	struct irq_domain *ipidomain;	struct device_node *node; 	node = of_irq_find_parent(of_root);	ipidomain = irq_find_matching_host(node, DOMAIN_BUS_IPI); 	/*	 * Some platforms have half DT setup. So if we found irq node but	 * didn't find an ipidomain, try to search for one that is not in the	 * DT.	 */	if (node && !ipidomain)		ipidomain = irq_find_matching_host(NULL, DOMAIN_BUS_IPI); 	/*	 * There are systems which use IPI IRQ domains, but only have one	 * registered when some runtime condition is met. For example a Malta	 * kernel may include support for GIC & CPU interrupt controller IPI	 * IRQ domains, but if run on a system with no GIC & no MT ASE then	 * neither will be supported or registered.	 *	 * We only have a problem if we're actually using multiple CPUs so fail	 * loudly if that is the case. Otherwise simply return, skipping IPI	 * setup, if we're running with only a single CPU.	 */	if (!ipidomain) {		BUG_ON(num_present_cpus() > 1);		return 0;	} 	virq = irq_reserve_ipi(ipidomain, mask);	BUG_ON(!virq);	if (!call_virq)		call_virq = virq; 	virq = irq_reserve_ipi(ipidomain, mask);	BUG_ON(!virq);	if (!sched_virq)		sched_virq = virq; 	if (irq_domain_is_ipi_per_cpu(ipidomain)) {		int cpu; 		for_each_cpu(cpu, mask) {			smp_ipi_init_one(call_virq + cpu, "IPI call",					 ipi_call_interrupt);			smp_ipi_init_one(sched_virq + cpu, "IPI resched",					 ipi_resched_interrupt);		}	} else {		smp_ipi_init_one(call_virq, "IPI call", ipi_call_interrupt);		smp_ipi_init_one(sched_virq, "IPI resched",				 ipi_resched_interrupt);	} 	return 0;} int mips_smp_ipi_free(const struct cpumask *mask){	struct irq_domain *ipidomain;	struct device_node *node; 	node = of_irq_find_parent(of_root);	ipidomain = irq_find_matching_host(node, DOMAIN_BUS_IPI); 	/*	 * Some platforms have half DT setup. So if we found irq node but	 * didn't find an ipidomain, try to search for one that is not in the	 * DT.	 */	if (node && !ipidomain)		ipidomain = irq_find_matching_host(NULL, DOMAIN_BUS_IPI); 	BUG_ON(!ipidomain); 	if (irq_domain_is_ipi_per_cpu(ipidomain)) {		int cpu; 		for_each_cpu(cpu, mask) {			free_irq(call_virq + cpu, NULL);			free_irq(sched_virq + cpu, NULL);		}	}	irq_destroy_ipi(call_virq, mask);	irq_destroy_ipi(sched_virq, mask);	return 0;}  static int __init mips_smp_ipi_init(void){	if (num_possible_cpus() == 1)		return 0; 	mips_smp_ipi_allocate(cpu_possible_mask); 	call_desc = irq_to_desc(call_virq);	sched_desc = irq_to_desc(sched_virq); 	return 0;}early_initcall(mips_smp_ipi_init);#endif /* * First C code run on the secondary CPUs after being started up by * the master. */asmlinkage void start_secondary(void){	unsigned int cpu = raw_smp_processor_id(); 	cpu_probe();	per_cpu_trap_init(false);	rcutree_report_cpu_starting(cpu);	mips_clockevent_init();	mp_ops->init_secondary();	cpu_report();	maar_init(); 	/*	 * XXX parity protection should be folded in here when it's converted	 * to an option instead of something based on .cputype	 */ #ifdef CONFIG_HOTPLUG_PARALLEL	cpuhp_ap_sync_alive();#endif	calibrate_delay();	cpu_data[cpu].udelay_val = loops_per_jiffy; 	set_cpu_sibling_map(cpu);	set_cpu_core_map(cpu); 	cpumask_set_cpu(cpu, &cpu_coherent_mask);	notify_cpu_starting(cpu); #ifndef CONFIG_HOTPLUG_PARALLEL	/* Notify boot CPU that we're starting & ready to sync counters */	complete(&cpu_starting);#endif 	synchronise_count_slave(cpu); 	/* The CPU is running and counters synchronised, now mark it online */	set_cpu_online(cpu, true); 	calculate_cpu_foreign_map(); #ifndef CONFIG_HOTPLUG_PARALLEL	/*	 * Notify boot CPU that we're up & online and it can safely return	 * from __cpu_up	 */	complete(&cpu_running);#endif 	/*	 * irq will be enabled in ->smp_finish(), enabling it too early	 * is dangerous.	 */	WARN_ON_ONCE(!irqs_disabled());	mp_ops->smp_finish(); 	cpu_startup_entry(CPUHP_AP_ONLINE_IDLE);} static void stop_this_cpu(void *dummy){	/*	 * Remove this CPU:	 */ 	set_cpu_online(smp_processor_id(), false);	calculate_cpu_foreign_map();	local_irq_disable();	rcutree_report_cpu_dead();	while (1);} void smp_send_stop(void){	smp_call_function(stop_this_cpu, NULL, 0);} void __init smp_cpus_done(unsigned int max_cpus){} /* called from main before smp_init() */void __init smp_prepare_cpus(unsigned int max_cpus){	init_new_context(current, &init_mm);	current_thread_info()->cpu = 0;	mp_ops->prepare_cpus(max_cpus);	set_cpu_sibling_map(0);	set_cpu_core_map(0);	calculate_cpu_foreign_map();#ifndef CONFIG_HOTPLUG_CPU	init_cpu_present(cpu_possible_mask);#endif	cpumask_copy(&cpu_coherent_mask, cpu_possible_mask);} /* preload SMP state for boot cpu */void __init smp_prepare_boot_cpu(void){	if (mp_ops->prepare_boot_cpu)		mp_ops->prepare_boot_cpu();	set_cpu_possible(0, true);	set_cpu_online(0, true);} #ifdef CONFIG_HOTPLUG_PARALLELint arch_cpuhp_kick_ap_alive(unsigned int cpu, struct task_struct *tidle){	return mp_ops->boot_secondary(cpu, tidle);}#elseint __cpu_up(unsigned int cpu, struct task_struct *tidle){	int err; 	err = mp_ops->boot_secondary(cpu, tidle);	if (err)		return err; 	/* Wait for CPU to start and be ready to sync counters */	if (!wait_for_completion_timeout(&cpu_starting,					 msecs_to_jiffies(1000))) {		pr_crit("CPU%u: failed to start\n", cpu);		return -EIO;	} 	/* Wait for CPU to finish startup & mark itself online before return */	wait_for_completion(&cpu_running);	return 0;}#endif #ifdef CONFIG_PROFILING/* Not really SMP stuff ... */int setup_profiling_timer(unsigned int multiplier){	return 0;}#endif static void flush_tlb_all_ipi(void *info){	local_flush_tlb_all();

Lines 1–500 of 761.