162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * SMP related functions 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright IBM Corp. 1999, 2012 662306a36Sopenharmony_ci * Author(s): Denis Joseph Barrow, 762306a36Sopenharmony_ci * Martin Schwidefsky <schwidefsky@de.ibm.com>, 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * based on other smp stuff by 1062306a36Sopenharmony_ci * (c) 1995 Alan Cox, CymruNET Ltd <alan@cymru.net> 1162306a36Sopenharmony_ci * (c) 1998 Ingo Molnar 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci * The code outside of smp.c uses logical cpu numbers, only smp.c does 1462306a36Sopenharmony_ci * the translation of logical to physical cpu ids. All new code that 1562306a36Sopenharmony_ci * operates on physical cpu numbers needs to go into smp.c. 1662306a36Sopenharmony_ci */ 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#define KMSG_COMPONENT "cpu" 1962306a36Sopenharmony_ci#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include <linux/workqueue.h> 2262306a36Sopenharmony_ci#include <linux/memblock.h> 2362306a36Sopenharmony_ci#include <linux/export.h> 2462306a36Sopenharmony_ci#include <linux/init.h> 2562306a36Sopenharmony_ci#include <linux/mm.h> 2662306a36Sopenharmony_ci#include <linux/err.h> 2762306a36Sopenharmony_ci#include <linux/spinlock.h> 2862306a36Sopenharmony_ci#include <linux/kernel_stat.h> 2962306a36Sopenharmony_ci#include <linux/delay.h> 3062306a36Sopenharmony_ci#include <linux/interrupt.h> 3162306a36Sopenharmony_ci#include <linux/irqflags.h> 3262306a36Sopenharmony_ci#include <linux/irq_work.h> 3362306a36Sopenharmony_ci#include <linux/cpu.h> 3462306a36Sopenharmony_ci#include <linux/slab.h> 3562306a36Sopenharmony_ci#include <linux/sched/hotplug.h> 3662306a36Sopenharmony_ci#include <linux/sched/task_stack.h> 3762306a36Sopenharmony_ci#include <linux/crash_dump.h> 3862306a36Sopenharmony_ci#include <linux/kprobes.h> 3962306a36Sopenharmony_ci#include <asm/asm-offsets.h> 4062306a36Sopenharmony_ci#include <asm/pfault.h> 4162306a36Sopenharmony_ci#include <asm/diag.h> 4262306a36Sopenharmony_ci#include <asm/switch_to.h> 4362306a36Sopenharmony_ci#include <asm/facility.h> 4462306a36Sopenharmony_ci#include <asm/ipl.h> 4562306a36Sopenharmony_ci#include <asm/setup.h> 4662306a36Sopenharmony_ci#include <asm/irq.h> 4762306a36Sopenharmony_ci#include <asm/tlbflush.h> 4862306a36Sopenharmony_ci#include <asm/vtimer.h> 4962306a36Sopenharmony_ci#include <asm/abs_lowcore.h> 5062306a36Sopenharmony_ci#include <asm/sclp.h> 5162306a36Sopenharmony_ci#include <asm/debug.h> 5262306a36Sopenharmony_ci#include <asm/os_info.h> 5362306a36Sopenharmony_ci#include <asm/sigp.h> 5462306a36Sopenharmony_ci#include <asm/idle.h> 5562306a36Sopenharmony_ci#include <asm/nmi.h> 5662306a36Sopenharmony_ci#include <asm/stacktrace.h> 5762306a36Sopenharmony_ci#include <asm/topology.h> 5862306a36Sopenharmony_ci#include <asm/vdso.h> 5962306a36Sopenharmony_ci#include <asm/maccess.h> 6062306a36Sopenharmony_ci#include "entry.h" 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cienum { 6362306a36Sopenharmony_ci ec_schedule = 0, 6462306a36Sopenharmony_ci ec_call_function_single, 6562306a36Sopenharmony_ci ec_stop_cpu, 6662306a36Sopenharmony_ci ec_mcck_pending, 6762306a36Sopenharmony_ci ec_irq_work, 6862306a36Sopenharmony_ci}; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cienum { 7162306a36Sopenharmony_ci CPU_STATE_STANDBY, 7262306a36Sopenharmony_ci CPU_STATE_CONFIGURED, 7362306a36Sopenharmony_ci}; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic DEFINE_PER_CPU(struct cpu *, cpu_device); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistruct pcpu { 7862306a36Sopenharmony_ci unsigned long ec_mask; /* bit mask for ec_xxx functions */ 7962306a36Sopenharmony_ci unsigned long ec_clk; /* sigp timestamp for ec_xxx */ 8062306a36Sopenharmony_ci signed char state; /* physical cpu state */ 8162306a36Sopenharmony_ci signed char polarization; /* physical polarization */ 8262306a36Sopenharmony_ci u16 address; /* physical cpu address */ 8362306a36Sopenharmony_ci}; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_cistatic u8 boot_core_type; 8662306a36Sopenharmony_cistatic struct pcpu pcpu_devices[NR_CPUS]; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ciunsigned int smp_cpu_mt_shift; 8962306a36Sopenharmony_ciEXPORT_SYMBOL(smp_cpu_mt_shift); 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ciunsigned int smp_cpu_mtid; 9262306a36Sopenharmony_ciEXPORT_SYMBOL(smp_cpu_mtid); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci#ifdef CONFIG_CRASH_DUMP 9562306a36Sopenharmony_ci__vector128 __initdata boot_cpu_vector_save_area[__NUM_VXRS]; 9662306a36Sopenharmony_ci#endif 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic unsigned int smp_max_threads __initdata = -1U; 9962306a36Sopenharmony_cicpumask_t cpu_setup_mask; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_cistatic int __init early_nosmt(char *s) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci smp_max_threads = 1; 10462306a36Sopenharmony_ci return 0; 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ciearly_param("nosmt", early_nosmt); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistatic int __init early_smt(char *s) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci get_option(&s, &smp_max_threads); 11162306a36Sopenharmony_ci return 0; 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ciearly_param("smt", early_smt); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci/* 11662306a36Sopenharmony_ci * The smp_cpu_state_mutex must be held when changing the state or polarization 11762306a36Sopenharmony_ci * member of a pcpu data structure within the pcpu_devices array. 11862306a36Sopenharmony_ci */ 11962306a36Sopenharmony_ciDEFINE_MUTEX(smp_cpu_state_mutex); 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci/* 12262306a36Sopenharmony_ci * Signal processor helper functions. 12362306a36Sopenharmony_ci */ 12462306a36Sopenharmony_cistatic inline int __pcpu_sigp_relax(u16 addr, u8 order, unsigned long parm) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci int cc; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci while (1) { 12962306a36Sopenharmony_ci cc = __pcpu_sigp(addr, order, parm, NULL); 13062306a36Sopenharmony_ci if (cc != SIGP_CC_BUSY) 13162306a36Sopenharmony_ci return cc; 13262306a36Sopenharmony_ci cpu_relax(); 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cistatic int pcpu_sigp_retry(struct pcpu *pcpu, u8 order, u32 parm) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci int cc, retry; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci for (retry = 0; ; retry++) { 14162306a36Sopenharmony_ci cc = __pcpu_sigp(pcpu->address, order, parm, NULL); 14262306a36Sopenharmony_ci if (cc != SIGP_CC_BUSY) 14362306a36Sopenharmony_ci break; 14462306a36Sopenharmony_ci if (retry >= 3) 14562306a36Sopenharmony_ci udelay(10); 14662306a36Sopenharmony_ci } 14762306a36Sopenharmony_ci return cc; 14862306a36Sopenharmony_ci} 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cistatic inline int pcpu_stopped(struct pcpu *pcpu) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci u32 status; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci if (__pcpu_sigp(pcpu->address, SIGP_SENSE, 15562306a36Sopenharmony_ci 0, &status) != SIGP_CC_STATUS_STORED) 15662306a36Sopenharmony_ci return 0; 15762306a36Sopenharmony_ci return !!(status & (SIGP_STATUS_CHECK_STOP|SIGP_STATUS_STOPPED)); 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cistatic inline int pcpu_running(struct pcpu *pcpu) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci if (__pcpu_sigp(pcpu->address, SIGP_SENSE_RUNNING, 16362306a36Sopenharmony_ci 0, NULL) != SIGP_CC_STATUS_STORED) 16462306a36Sopenharmony_ci return 1; 16562306a36Sopenharmony_ci /* Status stored condition code is equivalent to cpu not running. */ 16662306a36Sopenharmony_ci return 0; 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci/* 17062306a36Sopenharmony_ci * Find struct pcpu by cpu address. 17162306a36Sopenharmony_ci */ 17262306a36Sopenharmony_cistatic struct pcpu *pcpu_find_address(const struct cpumask *mask, u16 address) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci int cpu; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci for_each_cpu(cpu, mask) 17762306a36Sopenharmony_ci if (pcpu_devices[cpu].address == address) 17862306a36Sopenharmony_ci return pcpu_devices + cpu; 17962306a36Sopenharmony_ci return NULL; 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic void pcpu_ec_call(struct pcpu *pcpu, int ec_bit) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci int order; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci if (test_and_set_bit(ec_bit, &pcpu->ec_mask)) 18762306a36Sopenharmony_ci return; 18862306a36Sopenharmony_ci order = pcpu_running(pcpu) ? SIGP_EXTERNAL_CALL : SIGP_EMERGENCY_SIGNAL; 18962306a36Sopenharmony_ci pcpu->ec_clk = get_tod_clock_fast(); 19062306a36Sopenharmony_ci pcpu_sigp_retry(pcpu, order, 0); 19162306a36Sopenharmony_ci} 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_cistatic int pcpu_alloc_lowcore(struct pcpu *pcpu, int cpu) 19462306a36Sopenharmony_ci{ 19562306a36Sopenharmony_ci unsigned long async_stack, nodat_stack, mcck_stack; 19662306a36Sopenharmony_ci struct lowcore *lc; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci lc = (struct lowcore *) __get_free_pages(GFP_KERNEL | GFP_DMA, LC_ORDER); 19962306a36Sopenharmony_ci nodat_stack = __get_free_pages(GFP_KERNEL, THREAD_SIZE_ORDER); 20062306a36Sopenharmony_ci async_stack = stack_alloc(); 20162306a36Sopenharmony_ci mcck_stack = stack_alloc(); 20262306a36Sopenharmony_ci if (!lc || !nodat_stack || !async_stack || !mcck_stack) 20362306a36Sopenharmony_ci goto out; 20462306a36Sopenharmony_ci memcpy(lc, &S390_lowcore, 512); 20562306a36Sopenharmony_ci memset((char *) lc + 512, 0, sizeof(*lc) - 512); 20662306a36Sopenharmony_ci lc->async_stack = async_stack + STACK_INIT_OFFSET; 20762306a36Sopenharmony_ci lc->nodat_stack = nodat_stack + STACK_INIT_OFFSET; 20862306a36Sopenharmony_ci lc->mcck_stack = mcck_stack + STACK_INIT_OFFSET; 20962306a36Sopenharmony_ci lc->cpu_nr = cpu; 21062306a36Sopenharmony_ci lc->spinlock_lockval = arch_spin_lockval(cpu); 21162306a36Sopenharmony_ci lc->spinlock_index = 0; 21262306a36Sopenharmony_ci lc->return_lpswe = gen_lpswe(__LC_RETURN_PSW); 21362306a36Sopenharmony_ci lc->return_mcck_lpswe = gen_lpswe(__LC_RETURN_MCCK_PSW); 21462306a36Sopenharmony_ci lc->preempt_count = PREEMPT_DISABLED; 21562306a36Sopenharmony_ci if (nmi_alloc_mcesa(&lc->mcesad)) 21662306a36Sopenharmony_ci goto out; 21762306a36Sopenharmony_ci if (abs_lowcore_map(cpu, lc, true)) 21862306a36Sopenharmony_ci goto out_mcesa; 21962306a36Sopenharmony_ci lowcore_ptr[cpu] = lc; 22062306a36Sopenharmony_ci pcpu_sigp_retry(pcpu, SIGP_SET_PREFIX, __pa(lc)); 22162306a36Sopenharmony_ci return 0; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ciout_mcesa: 22462306a36Sopenharmony_ci nmi_free_mcesa(&lc->mcesad); 22562306a36Sopenharmony_ciout: 22662306a36Sopenharmony_ci stack_free(mcck_stack); 22762306a36Sopenharmony_ci stack_free(async_stack); 22862306a36Sopenharmony_ci free_pages(nodat_stack, THREAD_SIZE_ORDER); 22962306a36Sopenharmony_ci free_pages((unsigned long) lc, LC_ORDER); 23062306a36Sopenharmony_ci return -ENOMEM; 23162306a36Sopenharmony_ci} 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_cistatic void pcpu_free_lowcore(struct pcpu *pcpu) 23462306a36Sopenharmony_ci{ 23562306a36Sopenharmony_ci unsigned long async_stack, nodat_stack, mcck_stack; 23662306a36Sopenharmony_ci struct lowcore *lc; 23762306a36Sopenharmony_ci int cpu; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci cpu = pcpu - pcpu_devices; 24062306a36Sopenharmony_ci lc = lowcore_ptr[cpu]; 24162306a36Sopenharmony_ci nodat_stack = lc->nodat_stack - STACK_INIT_OFFSET; 24262306a36Sopenharmony_ci async_stack = lc->async_stack - STACK_INIT_OFFSET; 24362306a36Sopenharmony_ci mcck_stack = lc->mcck_stack - STACK_INIT_OFFSET; 24462306a36Sopenharmony_ci pcpu_sigp_retry(pcpu, SIGP_SET_PREFIX, 0); 24562306a36Sopenharmony_ci lowcore_ptr[cpu] = NULL; 24662306a36Sopenharmony_ci abs_lowcore_unmap(cpu); 24762306a36Sopenharmony_ci nmi_free_mcesa(&lc->mcesad); 24862306a36Sopenharmony_ci stack_free(async_stack); 24962306a36Sopenharmony_ci stack_free(mcck_stack); 25062306a36Sopenharmony_ci free_pages(nodat_stack, THREAD_SIZE_ORDER); 25162306a36Sopenharmony_ci free_pages((unsigned long) lc, LC_ORDER); 25262306a36Sopenharmony_ci} 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_cistatic void pcpu_prepare_secondary(struct pcpu *pcpu, int cpu) 25562306a36Sopenharmony_ci{ 25662306a36Sopenharmony_ci struct lowcore *lc, *abs_lc; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci lc = lowcore_ptr[cpu]; 25962306a36Sopenharmony_ci cpumask_set_cpu(cpu, &init_mm.context.cpu_attach_mask); 26062306a36Sopenharmony_ci cpumask_set_cpu(cpu, mm_cpumask(&init_mm)); 26162306a36Sopenharmony_ci lc->cpu_nr = cpu; 26262306a36Sopenharmony_ci lc->restart_flags = RESTART_FLAG_CTLREGS; 26362306a36Sopenharmony_ci lc->spinlock_lockval = arch_spin_lockval(cpu); 26462306a36Sopenharmony_ci lc->spinlock_index = 0; 26562306a36Sopenharmony_ci lc->percpu_offset = __per_cpu_offset[cpu]; 26662306a36Sopenharmony_ci lc->kernel_asce = S390_lowcore.kernel_asce; 26762306a36Sopenharmony_ci lc->user_asce = s390_invalid_asce; 26862306a36Sopenharmony_ci lc->machine_flags = S390_lowcore.machine_flags; 26962306a36Sopenharmony_ci lc->user_timer = lc->system_timer = 27062306a36Sopenharmony_ci lc->steal_timer = lc->avg_steal_timer = 0; 27162306a36Sopenharmony_ci abs_lc = get_abs_lowcore(); 27262306a36Sopenharmony_ci memcpy(lc->cregs_save_area, abs_lc->cregs_save_area, sizeof(lc->cregs_save_area)); 27362306a36Sopenharmony_ci put_abs_lowcore(abs_lc); 27462306a36Sopenharmony_ci lc->cregs_save_area[1] = lc->kernel_asce; 27562306a36Sopenharmony_ci lc->cregs_save_area[7] = lc->user_asce; 27662306a36Sopenharmony_ci save_access_regs((unsigned int *) lc->access_regs_save_area); 27762306a36Sopenharmony_ci arch_spin_lock_setup(cpu); 27862306a36Sopenharmony_ci} 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_cistatic void pcpu_attach_task(struct pcpu *pcpu, struct task_struct *tsk) 28162306a36Sopenharmony_ci{ 28262306a36Sopenharmony_ci struct lowcore *lc; 28362306a36Sopenharmony_ci int cpu; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci cpu = pcpu - pcpu_devices; 28662306a36Sopenharmony_ci lc = lowcore_ptr[cpu]; 28762306a36Sopenharmony_ci lc->kernel_stack = (unsigned long)task_stack_page(tsk) + STACK_INIT_OFFSET; 28862306a36Sopenharmony_ci lc->current_task = (unsigned long)tsk; 28962306a36Sopenharmony_ci lc->lpp = LPP_MAGIC; 29062306a36Sopenharmony_ci lc->current_pid = tsk->pid; 29162306a36Sopenharmony_ci lc->user_timer = tsk->thread.user_timer; 29262306a36Sopenharmony_ci lc->guest_timer = tsk->thread.guest_timer; 29362306a36Sopenharmony_ci lc->system_timer = tsk->thread.system_timer; 29462306a36Sopenharmony_ci lc->hardirq_timer = tsk->thread.hardirq_timer; 29562306a36Sopenharmony_ci lc->softirq_timer = tsk->thread.softirq_timer; 29662306a36Sopenharmony_ci lc->steal_timer = 0; 29762306a36Sopenharmony_ci} 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_cistatic void pcpu_start_fn(struct pcpu *pcpu, void (*func)(void *), void *data) 30062306a36Sopenharmony_ci{ 30162306a36Sopenharmony_ci struct lowcore *lc; 30262306a36Sopenharmony_ci int cpu; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci cpu = pcpu - pcpu_devices; 30562306a36Sopenharmony_ci lc = lowcore_ptr[cpu]; 30662306a36Sopenharmony_ci lc->restart_stack = lc->kernel_stack; 30762306a36Sopenharmony_ci lc->restart_fn = (unsigned long) func; 30862306a36Sopenharmony_ci lc->restart_data = (unsigned long) data; 30962306a36Sopenharmony_ci lc->restart_source = -1U; 31062306a36Sopenharmony_ci pcpu_sigp_retry(pcpu, SIGP_RESTART, 0); 31162306a36Sopenharmony_ci} 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_citypedef void (pcpu_delegate_fn)(void *); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci/* 31662306a36Sopenharmony_ci * Call function via PSW restart on pcpu and stop the current cpu. 31762306a36Sopenharmony_ci */ 31862306a36Sopenharmony_cistatic void __pcpu_delegate(pcpu_delegate_fn *func, void *data) 31962306a36Sopenharmony_ci{ 32062306a36Sopenharmony_ci func(data); /* should not return */ 32162306a36Sopenharmony_ci} 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_cistatic void pcpu_delegate(struct pcpu *pcpu, 32462306a36Sopenharmony_ci pcpu_delegate_fn *func, 32562306a36Sopenharmony_ci void *data, unsigned long stack) 32662306a36Sopenharmony_ci{ 32762306a36Sopenharmony_ci struct lowcore *lc, *abs_lc; 32862306a36Sopenharmony_ci unsigned int source_cpu; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci lc = lowcore_ptr[pcpu - pcpu_devices]; 33162306a36Sopenharmony_ci source_cpu = stap(); 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci if (pcpu->address == source_cpu) { 33462306a36Sopenharmony_ci call_on_stack(2, stack, void, __pcpu_delegate, 33562306a36Sopenharmony_ci pcpu_delegate_fn *, func, void *, data); 33662306a36Sopenharmony_ci } 33762306a36Sopenharmony_ci /* Stop target cpu (if func returns this stops the current cpu). */ 33862306a36Sopenharmony_ci pcpu_sigp_retry(pcpu, SIGP_STOP, 0); 33962306a36Sopenharmony_ci pcpu_sigp_retry(pcpu, SIGP_CPU_RESET, 0); 34062306a36Sopenharmony_ci /* Restart func on the target cpu and stop the current cpu. */ 34162306a36Sopenharmony_ci if (lc) { 34262306a36Sopenharmony_ci lc->restart_stack = stack; 34362306a36Sopenharmony_ci lc->restart_fn = (unsigned long)func; 34462306a36Sopenharmony_ci lc->restart_data = (unsigned long)data; 34562306a36Sopenharmony_ci lc->restart_source = source_cpu; 34662306a36Sopenharmony_ci } else { 34762306a36Sopenharmony_ci abs_lc = get_abs_lowcore(); 34862306a36Sopenharmony_ci abs_lc->restart_stack = stack; 34962306a36Sopenharmony_ci abs_lc->restart_fn = (unsigned long)func; 35062306a36Sopenharmony_ci abs_lc->restart_data = (unsigned long)data; 35162306a36Sopenharmony_ci abs_lc->restart_source = source_cpu; 35262306a36Sopenharmony_ci put_abs_lowcore(abs_lc); 35362306a36Sopenharmony_ci } 35462306a36Sopenharmony_ci asm volatile( 35562306a36Sopenharmony_ci "0: sigp 0,%0,%2 # sigp restart to target cpu\n" 35662306a36Sopenharmony_ci " brc 2,0b # busy, try again\n" 35762306a36Sopenharmony_ci "1: sigp 0,%1,%3 # sigp stop to current cpu\n" 35862306a36Sopenharmony_ci " brc 2,1b # busy, try again\n" 35962306a36Sopenharmony_ci : : "d" (pcpu->address), "d" (source_cpu), 36062306a36Sopenharmony_ci "K" (SIGP_RESTART), "K" (SIGP_STOP) 36162306a36Sopenharmony_ci : "0", "1", "cc"); 36262306a36Sopenharmony_ci for (;;) ; 36362306a36Sopenharmony_ci} 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci/* 36662306a36Sopenharmony_ci * Enable additional logical cpus for multi-threading. 36762306a36Sopenharmony_ci */ 36862306a36Sopenharmony_cistatic int pcpu_set_smt(unsigned int mtid) 36962306a36Sopenharmony_ci{ 37062306a36Sopenharmony_ci int cc; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci if (smp_cpu_mtid == mtid) 37362306a36Sopenharmony_ci return 0; 37462306a36Sopenharmony_ci cc = __pcpu_sigp(0, SIGP_SET_MULTI_THREADING, mtid, NULL); 37562306a36Sopenharmony_ci if (cc == 0) { 37662306a36Sopenharmony_ci smp_cpu_mtid = mtid; 37762306a36Sopenharmony_ci smp_cpu_mt_shift = 0; 37862306a36Sopenharmony_ci while (smp_cpu_mtid >= (1U << smp_cpu_mt_shift)) 37962306a36Sopenharmony_ci smp_cpu_mt_shift++; 38062306a36Sopenharmony_ci pcpu_devices[0].address = stap(); 38162306a36Sopenharmony_ci } 38262306a36Sopenharmony_ci return cc; 38362306a36Sopenharmony_ci} 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci/* 38662306a36Sopenharmony_ci * Call function on an online CPU. 38762306a36Sopenharmony_ci */ 38862306a36Sopenharmony_civoid smp_call_online_cpu(void (*func)(void *), void *data) 38962306a36Sopenharmony_ci{ 39062306a36Sopenharmony_ci struct pcpu *pcpu; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci /* Use the current cpu if it is online. */ 39362306a36Sopenharmony_ci pcpu = pcpu_find_address(cpu_online_mask, stap()); 39462306a36Sopenharmony_ci if (!pcpu) 39562306a36Sopenharmony_ci /* Use the first online cpu. */ 39662306a36Sopenharmony_ci pcpu = pcpu_devices + cpumask_first(cpu_online_mask); 39762306a36Sopenharmony_ci pcpu_delegate(pcpu, func, data, (unsigned long) restart_stack); 39862306a36Sopenharmony_ci} 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci/* 40162306a36Sopenharmony_ci * Call function on the ipl CPU. 40262306a36Sopenharmony_ci */ 40362306a36Sopenharmony_civoid smp_call_ipl_cpu(void (*func)(void *), void *data) 40462306a36Sopenharmony_ci{ 40562306a36Sopenharmony_ci struct lowcore *lc = lowcore_ptr[0]; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci if (pcpu_devices[0].address == stap()) 40862306a36Sopenharmony_ci lc = &S390_lowcore; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci pcpu_delegate(&pcpu_devices[0], func, data, 41162306a36Sopenharmony_ci lc->nodat_stack); 41262306a36Sopenharmony_ci} 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ciint smp_find_processor_id(u16 address) 41562306a36Sopenharmony_ci{ 41662306a36Sopenharmony_ci int cpu; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci for_each_present_cpu(cpu) 41962306a36Sopenharmony_ci if (pcpu_devices[cpu].address == address) 42062306a36Sopenharmony_ci return cpu; 42162306a36Sopenharmony_ci return -1; 42262306a36Sopenharmony_ci} 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_civoid schedule_mcck_handler(void) 42562306a36Sopenharmony_ci{ 42662306a36Sopenharmony_ci pcpu_ec_call(pcpu_devices + smp_processor_id(), ec_mcck_pending); 42762306a36Sopenharmony_ci} 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_cibool notrace arch_vcpu_is_preempted(int cpu) 43062306a36Sopenharmony_ci{ 43162306a36Sopenharmony_ci if (test_cpu_flag_of(CIF_ENABLED_WAIT, cpu)) 43262306a36Sopenharmony_ci return false; 43362306a36Sopenharmony_ci if (pcpu_running(pcpu_devices + cpu)) 43462306a36Sopenharmony_ci return false; 43562306a36Sopenharmony_ci return true; 43662306a36Sopenharmony_ci} 43762306a36Sopenharmony_ciEXPORT_SYMBOL(arch_vcpu_is_preempted); 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_civoid notrace smp_yield_cpu(int cpu) 44062306a36Sopenharmony_ci{ 44162306a36Sopenharmony_ci if (!MACHINE_HAS_DIAG9C) 44262306a36Sopenharmony_ci return; 44362306a36Sopenharmony_ci diag_stat_inc_norecursion(DIAG_STAT_X09C); 44462306a36Sopenharmony_ci asm volatile("diag %0,0,0x9c" 44562306a36Sopenharmony_ci : : "d" (pcpu_devices[cpu].address)); 44662306a36Sopenharmony_ci} 44762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(smp_yield_cpu); 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci/* 45062306a36Sopenharmony_ci * Send cpus emergency shutdown signal. This gives the cpus the 45162306a36Sopenharmony_ci * opportunity to complete outstanding interrupts. 45262306a36Sopenharmony_ci */ 45362306a36Sopenharmony_civoid notrace smp_emergency_stop(void) 45462306a36Sopenharmony_ci{ 45562306a36Sopenharmony_ci static arch_spinlock_t lock = __ARCH_SPIN_LOCK_UNLOCKED; 45662306a36Sopenharmony_ci static cpumask_t cpumask; 45762306a36Sopenharmony_ci u64 end; 45862306a36Sopenharmony_ci int cpu; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci arch_spin_lock(&lock); 46162306a36Sopenharmony_ci cpumask_copy(&cpumask, cpu_online_mask); 46262306a36Sopenharmony_ci cpumask_clear_cpu(smp_processor_id(), &cpumask); 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci end = get_tod_clock() + (1000000UL << 12); 46562306a36Sopenharmony_ci for_each_cpu(cpu, &cpumask) { 46662306a36Sopenharmony_ci struct pcpu *pcpu = pcpu_devices + cpu; 46762306a36Sopenharmony_ci set_bit(ec_stop_cpu, &pcpu->ec_mask); 46862306a36Sopenharmony_ci while (__pcpu_sigp(pcpu->address, SIGP_EMERGENCY_SIGNAL, 46962306a36Sopenharmony_ci 0, NULL) == SIGP_CC_BUSY && 47062306a36Sopenharmony_ci get_tod_clock() < end) 47162306a36Sopenharmony_ci cpu_relax(); 47262306a36Sopenharmony_ci } 47362306a36Sopenharmony_ci while (get_tod_clock() < end) { 47462306a36Sopenharmony_ci for_each_cpu(cpu, &cpumask) 47562306a36Sopenharmony_ci if (pcpu_stopped(pcpu_devices + cpu)) 47662306a36Sopenharmony_ci cpumask_clear_cpu(cpu, &cpumask); 47762306a36Sopenharmony_ci if (cpumask_empty(&cpumask)) 47862306a36Sopenharmony_ci break; 47962306a36Sopenharmony_ci cpu_relax(); 48062306a36Sopenharmony_ci } 48162306a36Sopenharmony_ci arch_spin_unlock(&lock); 48262306a36Sopenharmony_ci} 48362306a36Sopenharmony_ciNOKPROBE_SYMBOL(smp_emergency_stop); 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci/* 48662306a36Sopenharmony_ci * Stop all cpus but the current one. 48762306a36Sopenharmony_ci */ 48862306a36Sopenharmony_civoid smp_send_stop(void) 48962306a36Sopenharmony_ci{ 49062306a36Sopenharmony_ci int cpu; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci /* Disable all interrupts/machine checks */ 49362306a36Sopenharmony_ci __load_psw_mask(PSW_KERNEL_BITS); 49462306a36Sopenharmony_ci trace_hardirqs_off(); 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci debug_set_critical(); 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci if (oops_in_progress) 49962306a36Sopenharmony_ci smp_emergency_stop(); 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci /* stop all processors */ 50262306a36Sopenharmony_ci for_each_online_cpu(cpu) { 50362306a36Sopenharmony_ci if (cpu == smp_processor_id()) 50462306a36Sopenharmony_ci continue; 50562306a36Sopenharmony_ci pcpu_sigp_retry(pcpu_devices + cpu, SIGP_STOP, 0); 50662306a36Sopenharmony_ci while (!pcpu_stopped(pcpu_devices + cpu)) 50762306a36Sopenharmony_ci cpu_relax(); 50862306a36Sopenharmony_ci } 50962306a36Sopenharmony_ci} 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci/* 51262306a36Sopenharmony_ci * This is the main routine where commands issued by other 51362306a36Sopenharmony_ci * cpus are handled. 51462306a36Sopenharmony_ci */ 51562306a36Sopenharmony_cistatic void smp_handle_ext_call(void) 51662306a36Sopenharmony_ci{ 51762306a36Sopenharmony_ci unsigned long bits; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci /* handle bit signal external calls */ 52062306a36Sopenharmony_ci bits = xchg(&pcpu_devices[smp_processor_id()].ec_mask, 0); 52162306a36Sopenharmony_ci if (test_bit(ec_stop_cpu, &bits)) 52262306a36Sopenharmony_ci smp_stop_cpu(); 52362306a36Sopenharmony_ci if (test_bit(ec_schedule, &bits)) 52462306a36Sopenharmony_ci scheduler_ipi(); 52562306a36Sopenharmony_ci if (test_bit(ec_call_function_single, &bits)) 52662306a36Sopenharmony_ci generic_smp_call_function_single_interrupt(); 52762306a36Sopenharmony_ci if (test_bit(ec_mcck_pending, &bits)) 52862306a36Sopenharmony_ci s390_handle_mcck(); 52962306a36Sopenharmony_ci if (test_bit(ec_irq_work, &bits)) 53062306a36Sopenharmony_ci irq_work_run(); 53162306a36Sopenharmony_ci} 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_cistatic void do_ext_call_interrupt(struct ext_code ext_code, 53462306a36Sopenharmony_ci unsigned int param32, unsigned long param64) 53562306a36Sopenharmony_ci{ 53662306a36Sopenharmony_ci inc_irq_stat(ext_code.code == 0x1202 ? IRQEXT_EXC : IRQEXT_EMS); 53762306a36Sopenharmony_ci smp_handle_ext_call(); 53862306a36Sopenharmony_ci} 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_civoid arch_send_call_function_ipi_mask(const struct cpumask *mask) 54162306a36Sopenharmony_ci{ 54262306a36Sopenharmony_ci int cpu; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci for_each_cpu(cpu, mask) 54562306a36Sopenharmony_ci pcpu_ec_call(pcpu_devices + cpu, ec_call_function_single); 54662306a36Sopenharmony_ci} 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_civoid arch_send_call_function_single_ipi(int cpu) 54962306a36Sopenharmony_ci{ 55062306a36Sopenharmony_ci pcpu_ec_call(pcpu_devices + cpu, ec_call_function_single); 55162306a36Sopenharmony_ci} 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci/* 55462306a36Sopenharmony_ci * this function sends a 'reschedule' IPI to another CPU. 55562306a36Sopenharmony_ci * it goes straight through and wastes no time serializing 55662306a36Sopenharmony_ci * anything. Worst case is that we lose a reschedule ... 55762306a36Sopenharmony_ci */ 55862306a36Sopenharmony_civoid arch_smp_send_reschedule(int cpu) 55962306a36Sopenharmony_ci{ 56062306a36Sopenharmony_ci pcpu_ec_call(pcpu_devices + cpu, ec_schedule); 56162306a36Sopenharmony_ci} 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci#ifdef CONFIG_IRQ_WORK 56462306a36Sopenharmony_civoid arch_irq_work_raise(void) 56562306a36Sopenharmony_ci{ 56662306a36Sopenharmony_ci pcpu_ec_call(pcpu_devices + smp_processor_id(), ec_irq_work); 56762306a36Sopenharmony_ci} 56862306a36Sopenharmony_ci#endif 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci/* 57162306a36Sopenharmony_ci * parameter area for the set/clear control bit callbacks 57262306a36Sopenharmony_ci */ 57362306a36Sopenharmony_cistruct ec_creg_mask_parms { 57462306a36Sopenharmony_ci unsigned long orval; 57562306a36Sopenharmony_ci unsigned long andval; 57662306a36Sopenharmony_ci int cr; 57762306a36Sopenharmony_ci}; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci/* 58062306a36Sopenharmony_ci * callback for setting/clearing control bits 58162306a36Sopenharmony_ci */ 58262306a36Sopenharmony_cistatic void smp_ctl_bit_callback(void *info) 58362306a36Sopenharmony_ci{ 58462306a36Sopenharmony_ci struct ec_creg_mask_parms *pp = info; 58562306a36Sopenharmony_ci unsigned long cregs[16]; 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci __ctl_store(cregs, 0, 15); 58862306a36Sopenharmony_ci cregs[pp->cr] = (cregs[pp->cr] & pp->andval) | pp->orval; 58962306a36Sopenharmony_ci __ctl_load(cregs, 0, 15); 59062306a36Sopenharmony_ci} 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_cistatic DEFINE_SPINLOCK(ctl_lock); 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_civoid smp_ctl_set_clear_bit(int cr, int bit, bool set) 59562306a36Sopenharmony_ci{ 59662306a36Sopenharmony_ci struct ec_creg_mask_parms parms = { .cr = cr, }; 59762306a36Sopenharmony_ci struct lowcore *abs_lc; 59862306a36Sopenharmony_ci u64 ctlreg; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci if (set) { 60162306a36Sopenharmony_ci parms.orval = 1UL << bit; 60262306a36Sopenharmony_ci parms.andval = -1UL; 60362306a36Sopenharmony_ci } else { 60462306a36Sopenharmony_ci parms.orval = 0; 60562306a36Sopenharmony_ci parms.andval = ~(1UL << bit); 60662306a36Sopenharmony_ci } 60762306a36Sopenharmony_ci spin_lock(&ctl_lock); 60862306a36Sopenharmony_ci abs_lc = get_abs_lowcore(); 60962306a36Sopenharmony_ci ctlreg = abs_lc->cregs_save_area[cr]; 61062306a36Sopenharmony_ci ctlreg = (ctlreg & parms.andval) | parms.orval; 61162306a36Sopenharmony_ci abs_lc->cregs_save_area[cr] = ctlreg; 61262306a36Sopenharmony_ci put_abs_lowcore(abs_lc); 61362306a36Sopenharmony_ci on_each_cpu(smp_ctl_bit_callback, &parms, 1); 61462306a36Sopenharmony_ci spin_unlock(&ctl_lock); 61562306a36Sopenharmony_ci} 61662306a36Sopenharmony_ciEXPORT_SYMBOL(smp_ctl_set_clear_bit); 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci#ifdef CONFIG_CRASH_DUMP 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ciint smp_store_status(int cpu) 62162306a36Sopenharmony_ci{ 62262306a36Sopenharmony_ci struct lowcore *lc; 62362306a36Sopenharmony_ci struct pcpu *pcpu; 62462306a36Sopenharmony_ci unsigned long pa; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci pcpu = pcpu_devices + cpu; 62762306a36Sopenharmony_ci lc = lowcore_ptr[cpu]; 62862306a36Sopenharmony_ci pa = __pa(&lc->floating_pt_save_area); 62962306a36Sopenharmony_ci if (__pcpu_sigp_relax(pcpu->address, SIGP_STORE_STATUS_AT_ADDRESS, 63062306a36Sopenharmony_ci pa) != SIGP_CC_ORDER_CODE_ACCEPTED) 63162306a36Sopenharmony_ci return -EIO; 63262306a36Sopenharmony_ci if (!MACHINE_HAS_VX && !MACHINE_HAS_GS) 63362306a36Sopenharmony_ci return 0; 63462306a36Sopenharmony_ci pa = lc->mcesad & MCESA_ORIGIN_MASK; 63562306a36Sopenharmony_ci if (MACHINE_HAS_GS) 63662306a36Sopenharmony_ci pa |= lc->mcesad & MCESA_LC_MASK; 63762306a36Sopenharmony_ci if (__pcpu_sigp_relax(pcpu->address, SIGP_STORE_ADDITIONAL_STATUS, 63862306a36Sopenharmony_ci pa) != SIGP_CC_ORDER_CODE_ACCEPTED) 63962306a36Sopenharmony_ci return -EIO; 64062306a36Sopenharmony_ci return 0; 64162306a36Sopenharmony_ci} 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci/* 64462306a36Sopenharmony_ci * Collect CPU state of the previous, crashed system. 64562306a36Sopenharmony_ci * There are four cases: 64662306a36Sopenharmony_ci * 1) standard zfcp/nvme dump 64762306a36Sopenharmony_ci * condition: OLDMEM_BASE == NULL && is_ipl_type_dump() == true 64862306a36Sopenharmony_ci * The state for all CPUs except the boot CPU needs to be collected 64962306a36Sopenharmony_ci * with sigp stop-and-store-status. The boot CPU state is located in 65062306a36Sopenharmony_ci * the absolute lowcore of the memory stored in the HSA. The zcore code 65162306a36Sopenharmony_ci * will copy the boot CPU state from the HSA. 65262306a36Sopenharmony_ci * 2) stand-alone kdump for SCSI/NVMe (zfcp/nvme dump with swapped memory) 65362306a36Sopenharmony_ci * condition: OLDMEM_BASE != NULL && is_ipl_type_dump() == true 65462306a36Sopenharmony_ci * The state for all CPUs except the boot CPU needs to be collected 65562306a36Sopenharmony_ci * with sigp stop-and-store-status. The firmware or the boot-loader 65662306a36Sopenharmony_ci * stored the registers of the boot CPU in the absolute lowcore in the 65762306a36Sopenharmony_ci * memory of the old system. 65862306a36Sopenharmony_ci * 3) kdump and the old kernel did not store the CPU state, 65962306a36Sopenharmony_ci * or stand-alone kdump for DASD 66062306a36Sopenharmony_ci * condition: OLDMEM_BASE != NULL && !is_kdump_kernel() 66162306a36Sopenharmony_ci * The state for all CPUs except the boot CPU needs to be collected 66262306a36Sopenharmony_ci * with sigp stop-and-store-status. The kexec code or the boot-loader 66362306a36Sopenharmony_ci * stored the registers of the boot CPU in the memory of the old system. 66462306a36Sopenharmony_ci * 4) kdump and the old kernel stored the CPU state 66562306a36Sopenharmony_ci * condition: OLDMEM_BASE != NULL && is_kdump_kernel() 66662306a36Sopenharmony_ci * This case does not exist for s390 anymore, setup_arch explicitly 66762306a36Sopenharmony_ci * deactivates the elfcorehdr= kernel parameter 66862306a36Sopenharmony_ci */ 66962306a36Sopenharmony_cistatic bool dump_available(void) 67062306a36Sopenharmony_ci{ 67162306a36Sopenharmony_ci return oldmem_data.start || is_ipl_type_dump(); 67262306a36Sopenharmony_ci} 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_civoid __init smp_save_dump_ipl_cpu(void) 67562306a36Sopenharmony_ci{ 67662306a36Sopenharmony_ci struct save_area *sa; 67762306a36Sopenharmony_ci void *regs; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci if (!dump_available()) 68062306a36Sopenharmony_ci return; 68162306a36Sopenharmony_ci sa = save_area_alloc(true); 68262306a36Sopenharmony_ci regs = memblock_alloc(512, 8); 68362306a36Sopenharmony_ci if (!sa || !regs) 68462306a36Sopenharmony_ci panic("could not allocate memory for boot CPU save area\n"); 68562306a36Sopenharmony_ci copy_oldmem_kernel(regs, __LC_FPREGS_SAVE_AREA, 512); 68662306a36Sopenharmony_ci save_area_add_regs(sa, regs); 68762306a36Sopenharmony_ci memblock_free(regs, 512); 68862306a36Sopenharmony_ci if (MACHINE_HAS_VX) 68962306a36Sopenharmony_ci save_area_add_vxrs(sa, boot_cpu_vector_save_area); 69062306a36Sopenharmony_ci} 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_civoid __init smp_save_dump_secondary_cpus(void) 69362306a36Sopenharmony_ci{ 69462306a36Sopenharmony_ci int addr, boot_cpu_addr, max_cpu_addr; 69562306a36Sopenharmony_ci struct save_area *sa; 69662306a36Sopenharmony_ci void *page; 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci if (!dump_available()) 69962306a36Sopenharmony_ci return; 70062306a36Sopenharmony_ci /* Allocate a page as dumping area for the store status sigps */ 70162306a36Sopenharmony_ci page = memblock_alloc_low(PAGE_SIZE, PAGE_SIZE); 70262306a36Sopenharmony_ci if (!page) 70362306a36Sopenharmony_ci panic("ERROR: Failed to allocate %lx bytes below %lx\n", 70462306a36Sopenharmony_ci PAGE_SIZE, 1UL << 31); 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci /* Set multi-threading state to the previous system. */ 70762306a36Sopenharmony_ci pcpu_set_smt(sclp.mtid_prev); 70862306a36Sopenharmony_ci boot_cpu_addr = stap(); 70962306a36Sopenharmony_ci max_cpu_addr = SCLP_MAX_CORES << sclp.mtid_prev; 71062306a36Sopenharmony_ci for (addr = 0; addr <= max_cpu_addr; addr++) { 71162306a36Sopenharmony_ci if (addr == boot_cpu_addr) 71262306a36Sopenharmony_ci continue; 71362306a36Sopenharmony_ci if (__pcpu_sigp_relax(addr, SIGP_SENSE, 0) == 71462306a36Sopenharmony_ci SIGP_CC_NOT_OPERATIONAL) 71562306a36Sopenharmony_ci continue; 71662306a36Sopenharmony_ci sa = save_area_alloc(false); 71762306a36Sopenharmony_ci if (!sa) 71862306a36Sopenharmony_ci panic("could not allocate memory for save area\n"); 71962306a36Sopenharmony_ci __pcpu_sigp_relax(addr, SIGP_STORE_STATUS_AT_ADDRESS, __pa(page)); 72062306a36Sopenharmony_ci save_area_add_regs(sa, page); 72162306a36Sopenharmony_ci if (MACHINE_HAS_VX) { 72262306a36Sopenharmony_ci __pcpu_sigp_relax(addr, SIGP_STORE_ADDITIONAL_STATUS, __pa(page)); 72362306a36Sopenharmony_ci save_area_add_vxrs(sa, page); 72462306a36Sopenharmony_ci } 72562306a36Sopenharmony_ci } 72662306a36Sopenharmony_ci memblock_free(page, PAGE_SIZE); 72762306a36Sopenharmony_ci diag_amode31_ops.diag308_reset(); 72862306a36Sopenharmony_ci pcpu_set_smt(0); 72962306a36Sopenharmony_ci} 73062306a36Sopenharmony_ci#endif /* CONFIG_CRASH_DUMP */ 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_civoid smp_cpu_set_polarization(int cpu, int val) 73362306a36Sopenharmony_ci{ 73462306a36Sopenharmony_ci pcpu_devices[cpu].polarization = val; 73562306a36Sopenharmony_ci} 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ciint smp_cpu_get_polarization(int cpu) 73862306a36Sopenharmony_ci{ 73962306a36Sopenharmony_ci return pcpu_devices[cpu].polarization; 74062306a36Sopenharmony_ci} 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ciint smp_cpu_get_cpu_address(int cpu) 74362306a36Sopenharmony_ci{ 74462306a36Sopenharmony_ci return pcpu_devices[cpu].address; 74562306a36Sopenharmony_ci} 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_cistatic void __ref smp_get_core_info(struct sclp_core_info *info, int early) 74862306a36Sopenharmony_ci{ 74962306a36Sopenharmony_ci static int use_sigp_detection; 75062306a36Sopenharmony_ci int address; 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci if (use_sigp_detection || sclp_get_core_info(info, early)) { 75362306a36Sopenharmony_ci use_sigp_detection = 1; 75462306a36Sopenharmony_ci for (address = 0; 75562306a36Sopenharmony_ci address < (SCLP_MAX_CORES << smp_cpu_mt_shift); 75662306a36Sopenharmony_ci address += (1U << smp_cpu_mt_shift)) { 75762306a36Sopenharmony_ci if (__pcpu_sigp_relax(address, SIGP_SENSE, 0) == 75862306a36Sopenharmony_ci SIGP_CC_NOT_OPERATIONAL) 75962306a36Sopenharmony_ci continue; 76062306a36Sopenharmony_ci info->core[info->configured].core_id = 76162306a36Sopenharmony_ci address >> smp_cpu_mt_shift; 76262306a36Sopenharmony_ci info->configured++; 76362306a36Sopenharmony_ci } 76462306a36Sopenharmony_ci info->combined = info->configured; 76562306a36Sopenharmony_ci } 76662306a36Sopenharmony_ci} 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_cistatic int smp_add_present_cpu(int cpu); 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_cistatic int smp_add_core(struct sclp_core_entry *core, cpumask_t *avail, 77162306a36Sopenharmony_ci bool configured, bool early) 77262306a36Sopenharmony_ci{ 77362306a36Sopenharmony_ci struct pcpu *pcpu; 77462306a36Sopenharmony_ci int cpu, nr, i; 77562306a36Sopenharmony_ci u16 address; 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci nr = 0; 77862306a36Sopenharmony_ci if (sclp.has_core_type && core->type != boot_core_type) 77962306a36Sopenharmony_ci return nr; 78062306a36Sopenharmony_ci cpu = cpumask_first(avail); 78162306a36Sopenharmony_ci address = core->core_id << smp_cpu_mt_shift; 78262306a36Sopenharmony_ci for (i = 0; (i <= smp_cpu_mtid) && (cpu < nr_cpu_ids); i++) { 78362306a36Sopenharmony_ci if (pcpu_find_address(cpu_present_mask, address + i)) 78462306a36Sopenharmony_ci continue; 78562306a36Sopenharmony_ci pcpu = pcpu_devices + cpu; 78662306a36Sopenharmony_ci pcpu->address = address + i; 78762306a36Sopenharmony_ci if (configured) 78862306a36Sopenharmony_ci pcpu->state = CPU_STATE_CONFIGURED; 78962306a36Sopenharmony_ci else 79062306a36Sopenharmony_ci pcpu->state = CPU_STATE_STANDBY; 79162306a36Sopenharmony_ci smp_cpu_set_polarization(cpu, POLARIZATION_UNKNOWN); 79262306a36Sopenharmony_ci set_cpu_present(cpu, true); 79362306a36Sopenharmony_ci if (!early && smp_add_present_cpu(cpu) != 0) 79462306a36Sopenharmony_ci set_cpu_present(cpu, false); 79562306a36Sopenharmony_ci else 79662306a36Sopenharmony_ci nr++; 79762306a36Sopenharmony_ci cpumask_clear_cpu(cpu, avail); 79862306a36Sopenharmony_ci cpu = cpumask_next(cpu, avail); 79962306a36Sopenharmony_ci } 80062306a36Sopenharmony_ci return nr; 80162306a36Sopenharmony_ci} 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_cistatic int __smp_rescan_cpus(struct sclp_core_info *info, bool early) 80462306a36Sopenharmony_ci{ 80562306a36Sopenharmony_ci struct sclp_core_entry *core; 80662306a36Sopenharmony_ci static cpumask_t avail; 80762306a36Sopenharmony_ci bool configured; 80862306a36Sopenharmony_ci u16 core_id; 80962306a36Sopenharmony_ci int nr, i; 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci cpus_read_lock(); 81262306a36Sopenharmony_ci mutex_lock(&smp_cpu_state_mutex); 81362306a36Sopenharmony_ci nr = 0; 81462306a36Sopenharmony_ci cpumask_xor(&avail, cpu_possible_mask, cpu_present_mask); 81562306a36Sopenharmony_ci /* 81662306a36Sopenharmony_ci * Add IPL core first (which got logical CPU number 0) to make sure 81762306a36Sopenharmony_ci * that all SMT threads get subsequent logical CPU numbers. 81862306a36Sopenharmony_ci */ 81962306a36Sopenharmony_ci if (early) { 82062306a36Sopenharmony_ci core_id = pcpu_devices[0].address >> smp_cpu_mt_shift; 82162306a36Sopenharmony_ci for (i = 0; i < info->configured; i++) { 82262306a36Sopenharmony_ci core = &info->core[i]; 82362306a36Sopenharmony_ci if (core->core_id == core_id) { 82462306a36Sopenharmony_ci nr += smp_add_core(core, &avail, true, early); 82562306a36Sopenharmony_ci break; 82662306a36Sopenharmony_ci } 82762306a36Sopenharmony_ci } 82862306a36Sopenharmony_ci } 82962306a36Sopenharmony_ci for (i = 0; i < info->combined; i++) { 83062306a36Sopenharmony_ci configured = i < info->configured; 83162306a36Sopenharmony_ci nr += smp_add_core(&info->core[i], &avail, configured, early); 83262306a36Sopenharmony_ci } 83362306a36Sopenharmony_ci mutex_unlock(&smp_cpu_state_mutex); 83462306a36Sopenharmony_ci cpus_read_unlock(); 83562306a36Sopenharmony_ci return nr; 83662306a36Sopenharmony_ci} 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_civoid __init smp_detect_cpus(void) 83962306a36Sopenharmony_ci{ 84062306a36Sopenharmony_ci unsigned int cpu, mtid, c_cpus, s_cpus; 84162306a36Sopenharmony_ci struct sclp_core_info *info; 84262306a36Sopenharmony_ci u16 address; 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci /* Get CPU information */ 84562306a36Sopenharmony_ci info = memblock_alloc(sizeof(*info), 8); 84662306a36Sopenharmony_ci if (!info) 84762306a36Sopenharmony_ci panic("%s: Failed to allocate %zu bytes align=0x%x\n", 84862306a36Sopenharmony_ci __func__, sizeof(*info), 8); 84962306a36Sopenharmony_ci smp_get_core_info(info, 1); 85062306a36Sopenharmony_ci /* Find boot CPU type */ 85162306a36Sopenharmony_ci if (sclp.has_core_type) { 85262306a36Sopenharmony_ci address = stap(); 85362306a36Sopenharmony_ci for (cpu = 0; cpu < info->combined; cpu++) 85462306a36Sopenharmony_ci if (info->core[cpu].core_id == address) { 85562306a36Sopenharmony_ci /* The boot cpu dictates the cpu type. */ 85662306a36Sopenharmony_ci boot_core_type = info->core[cpu].type; 85762306a36Sopenharmony_ci break; 85862306a36Sopenharmony_ci } 85962306a36Sopenharmony_ci if (cpu >= info->combined) 86062306a36Sopenharmony_ci panic("Could not find boot CPU type"); 86162306a36Sopenharmony_ci } 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci /* Set multi-threading state for the current system */ 86462306a36Sopenharmony_ci mtid = boot_core_type ? sclp.mtid : sclp.mtid_cp; 86562306a36Sopenharmony_ci mtid = (mtid < smp_max_threads) ? mtid : smp_max_threads - 1; 86662306a36Sopenharmony_ci pcpu_set_smt(mtid); 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci /* Print number of CPUs */ 86962306a36Sopenharmony_ci c_cpus = s_cpus = 0; 87062306a36Sopenharmony_ci for (cpu = 0; cpu < info->combined; cpu++) { 87162306a36Sopenharmony_ci if (sclp.has_core_type && 87262306a36Sopenharmony_ci info->core[cpu].type != boot_core_type) 87362306a36Sopenharmony_ci continue; 87462306a36Sopenharmony_ci if (cpu < info->configured) 87562306a36Sopenharmony_ci c_cpus += smp_cpu_mtid + 1; 87662306a36Sopenharmony_ci else 87762306a36Sopenharmony_ci s_cpus += smp_cpu_mtid + 1; 87862306a36Sopenharmony_ci } 87962306a36Sopenharmony_ci pr_info("%d configured CPUs, %d standby CPUs\n", c_cpus, s_cpus); 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci /* Add CPUs present at boot */ 88262306a36Sopenharmony_ci __smp_rescan_cpus(info, true); 88362306a36Sopenharmony_ci memblock_free(info, sizeof(*info)); 88462306a36Sopenharmony_ci} 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci/* 88762306a36Sopenharmony_ci * Activate a secondary processor. 88862306a36Sopenharmony_ci */ 88962306a36Sopenharmony_cistatic void smp_start_secondary(void *cpuvoid) 89062306a36Sopenharmony_ci{ 89162306a36Sopenharmony_ci int cpu = raw_smp_processor_id(); 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci S390_lowcore.last_update_clock = get_tod_clock(); 89462306a36Sopenharmony_ci S390_lowcore.restart_stack = (unsigned long)restart_stack; 89562306a36Sopenharmony_ci S390_lowcore.restart_fn = (unsigned long)do_restart; 89662306a36Sopenharmony_ci S390_lowcore.restart_data = 0; 89762306a36Sopenharmony_ci S390_lowcore.restart_source = -1U; 89862306a36Sopenharmony_ci S390_lowcore.restart_flags = 0; 89962306a36Sopenharmony_ci restore_access_regs(S390_lowcore.access_regs_save_area); 90062306a36Sopenharmony_ci cpu_init(); 90162306a36Sopenharmony_ci rcu_cpu_starting(cpu); 90262306a36Sopenharmony_ci init_cpu_timer(); 90362306a36Sopenharmony_ci vtime_init(); 90462306a36Sopenharmony_ci vdso_getcpu_init(); 90562306a36Sopenharmony_ci pfault_init(); 90662306a36Sopenharmony_ci cpumask_set_cpu(cpu, &cpu_setup_mask); 90762306a36Sopenharmony_ci update_cpu_masks(); 90862306a36Sopenharmony_ci notify_cpu_starting(cpu); 90962306a36Sopenharmony_ci if (topology_cpu_dedicated(cpu)) 91062306a36Sopenharmony_ci set_cpu_flag(CIF_DEDICATED_CPU); 91162306a36Sopenharmony_ci else 91262306a36Sopenharmony_ci clear_cpu_flag(CIF_DEDICATED_CPU); 91362306a36Sopenharmony_ci set_cpu_online(cpu, true); 91462306a36Sopenharmony_ci inc_irq_stat(CPU_RST); 91562306a36Sopenharmony_ci local_irq_enable(); 91662306a36Sopenharmony_ci cpu_startup_entry(CPUHP_AP_ONLINE_IDLE); 91762306a36Sopenharmony_ci} 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci/* Upping and downing of CPUs */ 92062306a36Sopenharmony_ciint __cpu_up(unsigned int cpu, struct task_struct *tidle) 92162306a36Sopenharmony_ci{ 92262306a36Sopenharmony_ci struct pcpu *pcpu = pcpu_devices + cpu; 92362306a36Sopenharmony_ci int rc; 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci if (pcpu->state != CPU_STATE_CONFIGURED) 92662306a36Sopenharmony_ci return -EIO; 92762306a36Sopenharmony_ci if (pcpu_sigp_retry(pcpu, SIGP_INITIAL_CPU_RESET, 0) != 92862306a36Sopenharmony_ci SIGP_CC_ORDER_CODE_ACCEPTED) 92962306a36Sopenharmony_ci return -EIO; 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci rc = pcpu_alloc_lowcore(pcpu, cpu); 93262306a36Sopenharmony_ci if (rc) 93362306a36Sopenharmony_ci return rc; 93462306a36Sopenharmony_ci /* 93562306a36Sopenharmony_ci * Make sure global control register contents do not change 93662306a36Sopenharmony_ci * until new CPU has initialized control registers. 93762306a36Sopenharmony_ci */ 93862306a36Sopenharmony_ci spin_lock(&ctl_lock); 93962306a36Sopenharmony_ci pcpu_prepare_secondary(pcpu, cpu); 94062306a36Sopenharmony_ci pcpu_attach_task(pcpu, tidle); 94162306a36Sopenharmony_ci pcpu_start_fn(pcpu, smp_start_secondary, NULL); 94262306a36Sopenharmony_ci /* Wait until cpu puts itself in the online & active maps */ 94362306a36Sopenharmony_ci while (!cpu_online(cpu)) 94462306a36Sopenharmony_ci cpu_relax(); 94562306a36Sopenharmony_ci spin_unlock(&ctl_lock); 94662306a36Sopenharmony_ci return 0; 94762306a36Sopenharmony_ci} 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_cistatic unsigned int setup_possible_cpus __initdata; 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_cistatic int __init _setup_possible_cpus(char *s) 95262306a36Sopenharmony_ci{ 95362306a36Sopenharmony_ci get_option(&s, &setup_possible_cpus); 95462306a36Sopenharmony_ci return 0; 95562306a36Sopenharmony_ci} 95662306a36Sopenharmony_ciearly_param("possible_cpus", _setup_possible_cpus); 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ciint __cpu_disable(void) 95962306a36Sopenharmony_ci{ 96062306a36Sopenharmony_ci unsigned long cregs[16]; 96162306a36Sopenharmony_ci int cpu; 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci /* Handle possible pending IPIs */ 96462306a36Sopenharmony_ci smp_handle_ext_call(); 96562306a36Sopenharmony_ci cpu = smp_processor_id(); 96662306a36Sopenharmony_ci set_cpu_online(cpu, false); 96762306a36Sopenharmony_ci cpumask_clear_cpu(cpu, &cpu_setup_mask); 96862306a36Sopenharmony_ci update_cpu_masks(); 96962306a36Sopenharmony_ci /* Disable pseudo page faults on this cpu. */ 97062306a36Sopenharmony_ci pfault_fini(); 97162306a36Sopenharmony_ci /* Disable interrupt sources via control register. */ 97262306a36Sopenharmony_ci __ctl_store(cregs, 0, 15); 97362306a36Sopenharmony_ci cregs[0] &= ~0x0000ee70UL; /* disable all external interrupts */ 97462306a36Sopenharmony_ci cregs[6] &= ~0xff000000UL; /* disable all I/O interrupts */ 97562306a36Sopenharmony_ci cregs[14] &= ~0x1f000000UL; /* disable most machine checks */ 97662306a36Sopenharmony_ci __ctl_load(cregs, 0, 15); 97762306a36Sopenharmony_ci clear_cpu_flag(CIF_NOHZ_DELAY); 97862306a36Sopenharmony_ci return 0; 97962306a36Sopenharmony_ci} 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_civoid __cpu_die(unsigned int cpu) 98262306a36Sopenharmony_ci{ 98362306a36Sopenharmony_ci struct pcpu *pcpu; 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci /* Wait until target cpu is down */ 98662306a36Sopenharmony_ci pcpu = pcpu_devices + cpu; 98762306a36Sopenharmony_ci while (!pcpu_stopped(pcpu)) 98862306a36Sopenharmony_ci cpu_relax(); 98962306a36Sopenharmony_ci pcpu_free_lowcore(pcpu); 99062306a36Sopenharmony_ci cpumask_clear_cpu(cpu, mm_cpumask(&init_mm)); 99162306a36Sopenharmony_ci cpumask_clear_cpu(cpu, &init_mm.context.cpu_attach_mask); 99262306a36Sopenharmony_ci} 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_civoid __noreturn cpu_die(void) 99562306a36Sopenharmony_ci{ 99662306a36Sopenharmony_ci idle_task_exit(); 99762306a36Sopenharmony_ci pcpu_sigp_retry(pcpu_devices + smp_processor_id(), SIGP_STOP, 0); 99862306a36Sopenharmony_ci for (;;) ; 99962306a36Sopenharmony_ci} 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_civoid __init smp_fill_possible_mask(void) 100262306a36Sopenharmony_ci{ 100362306a36Sopenharmony_ci unsigned int possible, sclp_max, cpu; 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_ci sclp_max = max(sclp.mtid, sclp.mtid_cp) + 1; 100662306a36Sopenharmony_ci sclp_max = min(smp_max_threads, sclp_max); 100762306a36Sopenharmony_ci sclp_max = (sclp.max_cores * sclp_max) ?: nr_cpu_ids; 100862306a36Sopenharmony_ci possible = setup_possible_cpus ?: nr_cpu_ids; 100962306a36Sopenharmony_ci possible = min(possible, sclp_max); 101062306a36Sopenharmony_ci for (cpu = 0; cpu < possible && cpu < nr_cpu_ids; cpu++) 101162306a36Sopenharmony_ci set_cpu_possible(cpu, true); 101262306a36Sopenharmony_ci} 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_civoid __init smp_prepare_cpus(unsigned int max_cpus) 101562306a36Sopenharmony_ci{ 101662306a36Sopenharmony_ci /* request the 0x1201 emergency signal external interrupt */ 101762306a36Sopenharmony_ci if (register_external_irq(EXT_IRQ_EMERGENCY_SIG, do_ext_call_interrupt)) 101862306a36Sopenharmony_ci panic("Couldn't request external interrupt 0x1201"); 101962306a36Sopenharmony_ci /* request the 0x1202 external call external interrupt */ 102062306a36Sopenharmony_ci if (register_external_irq(EXT_IRQ_EXTERNAL_CALL, do_ext_call_interrupt)) 102162306a36Sopenharmony_ci panic("Couldn't request external interrupt 0x1202"); 102262306a36Sopenharmony_ci} 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_civoid __init smp_prepare_boot_cpu(void) 102562306a36Sopenharmony_ci{ 102662306a36Sopenharmony_ci struct pcpu *pcpu = pcpu_devices; 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci WARN_ON(!cpu_present(0) || !cpu_online(0)); 102962306a36Sopenharmony_ci pcpu->state = CPU_STATE_CONFIGURED; 103062306a36Sopenharmony_ci S390_lowcore.percpu_offset = __per_cpu_offset[0]; 103162306a36Sopenharmony_ci smp_cpu_set_polarization(0, POLARIZATION_UNKNOWN); 103262306a36Sopenharmony_ci} 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_civoid __init smp_setup_processor_id(void) 103562306a36Sopenharmony_ci{ 103662306a36Sopenharmony_ci pcpu_devices[0].address = stap(); 103762306a36Sopenharmony_ci S390_lowcore.cpu_nr = 0; 103862306a36Sopenharmony_ci S390_lowcore.spinlock_lockval = arch_spin_lockval(0); 103962306a36Sopenharmony_ci S390_lowcore.spinlock_index = 0; 104062306a36Sopenharmony_ci} 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ci/* 104362306a36Sopenharmony_ci * the frequency of the profiling timer can be changed 104462306a36Sopenharmony_ci * by writing a multiplier value into /proc/profile. 104562306a36Sopenharmony_ci * 104662306a36Sopenharmony_ci * usually you want to run this on all CPUs ;) 104762306a36Sopenharmony_ci */ 104862306a36Sopenharmony_ciint setup_profiling_timer(unsigned int multiplier) 104962306a36Sopenharmony_ci{ 105062306a36Sopenharmony_ci return 0; 105162306a36Sopenharmony_ci} 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_cistatic ssize_t cpu_configure_show(struct device *dev, 105462306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 105562306a36Sopenharmony_ci{ 105662306a36Sopenharmony_ci ssize_t count; 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_ci mutex_lock(&smp_cpu_state_mutex); 105962306a36Sopenharmony_ci count = sprintf(buf, "%d\n", pcpu_devices[dev->id].state); 106062306a36Sopenharmony_ci mutex_unlock(&smp_cpu_state_mutex); 106162306a36Sopenharmony_ci return count; 106262306a36Sopenharmony_ci} 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_cistatic ssize_t cpu_configure_store(struct device *dev, 106562306a36Sopenharmony_ci struct device_attribute *attr, 106662306a36Sopenharmony_ci const char *buf, size_t count) 106762306a36Sopenharmony_ci{ 106862306a36Sopenharmony_ci struct pcpu *pcpu; 106962306a36Sopenharmony_ci int cpu, val, rc, i; 107062306a36Sopenharmony_ci char delim; 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_ci if (sscanf(buf, "%d %c", &val, &delim) != 1) 107362306a36Sopenharmony_ci return -EINVAL; 107462306a36Sopenharmony_ci if (val != 0 && val != 1) 107562306a36Sopenharmony_ci return -EINVAL; 107662306a36Sopenharmony_ci cpus_read_lock(); 107762306a36Sopenharmony_ci mutex_lock(&smp_cpu_state_mutex); 107862306a36Sopenharmony_ci rc = -EBUSY; 107962306a36Sopenharmony_ci /* disallow configuration changes of online cpus and cpu 0 */ 108062306a36Sopenharmony_ci cpu = dev->id; 108162306a36Sopenharmony_ci cpu = smp_get_base_cpu(cpu); 108262306a36Sopenharmony_ci if (cpu == 0) 108362306a36Sopenharmony_ci goto out; 108462306a36Sopenharmony_ci for (i = 0; i <= smp_cpu_mtid; i++) 108562306a36Sopenharmony_ci if (cpu_online(cpu + i)) 108662306a36Sopenharmony_ci goto out; 108762306a36Sopenharmony_ci pcpu = pcpu_devices + cpu; 108862306a36Sopenharmony_ci rc = 0; 108962306a36Sopenharmony_ci switch (val) { 109062306a36Sopenharmony_ci case 0: 109162306a36Sopenharmony_ci if (pcpu->state != CPU_STATE_CONFIGURED) 109262306a36Sopenharmony_ci break; 109362306a36Sopenharmony_ci rc = sclp_core_deconfigure(pcpu->address >> smp_cpu_mt_shift); 109462306a36Sopenharmony_ci if (rc) 109562306a36Sopenharmony_ci break; 109662306a36Sopenharmony_ci for (i = 0; i <= smp_cpu_mtid; i++) { 109762306a36Sopenharmony_ci if (cpu + i >= nr_cpu_ids || !cpu_present(cpu + i)) 109862306a36Sopenharmony_ci continue; 109962306a36Sopenharmony_ci pcpu[i].state = CPU_STATE_STANDBY; 110062306a36Sopenharmony_ci smp_cpu_set_polarization(cpu + i, 110162306a36Sopenharmony_ci POLARIZATION_UNKNOWN); 110262306a36Sopenharmony_ci } 110362306a36Sopenharmony_ci topology_expect_change(); 110462306a36Sopenharmony_ci break; 110562306a36Sopenharmony_ci case 1: 110662306a36Sopenharmony_ci if (pcpu->state != CPU_STATE_STANDBY) 110762306a36Sopenharmony_ci break; 110862306a36Sopenharmony_ci rc = sclp_core_configure(pcpu->address >> smp_cpu_mt_shift); 110962306a36Sopenharmony_ci if (rc) 111062306a36Sopenharmony_ci break; 111162306a36Sopenharmony_ci for (i = 0; i <= smp_cpu_mtid; i++) { 111262306a36Sopenharmony_ci if (cpu + i >= nr_cpu_ids || !cpu_present(cpu + i)) 111362306a36Sopenharmony_ci continue; 111462306a36Sopenharmony_ci pcpu[i].state = CPU_STATE_CONFIGURED; 111562306a36Sopenharmony_ci smp_cpu_set_polarization(cpu + i, 111662306a36Sopenharmony_ci POLARIZATION_UNKNOWN); 111762306a36Sopenharmony_ci } 111862306a36Sopenharmony_ci topology_expect_change(); 111962306a36Sopenharmony_ci break; 112062306a36Sopenharmony_ci default: 112162306a36Sopenharmony_ci break; 112262306a36Sopenharmony_ci } 112362306a36Sopenharmony_ciout: 112462306a36Sopenharmony_ci mutex_unlock(&smp_cpu_state_mutex); 112562306a36Sopenharmony_ci cpus_read_unlock(); 112662306a36Sopenharmony_ci return rc ? rc : count; 112762306a36Sopenharmony_ci} 112862306a36Sopenharmony_cistatic DEVICE_ATTR(configure, 0644, cpu_configure_show, cpu_configure_store); 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_cistatic ssize_t show_cpu_address(struct device *dev, 113162306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 113262306a36Sopenharmony_ci{ 113362306a36Sopenharmony_ci return sprintf(buf, "%d\n", pcpu_devices[dev->id].address); 113462306a36Sopenharmony_ci} 113562306a36Sopenharmony_cistatic DEVICE_ATTR(address, 0444, show_cpu_address, NULL); 113662306a36Sopenharmony_ci 113762306a36Sopenharmony_cistatic struct attribute *cpu_common_attrs[] = { 113862306a36Sopenharmony_ci &dev_attr_configure.attr, 113962306a36Sopenharmony_ci &dev_attr_address.attr, 114062306a36Sopenharmony_ci NULL, 114162306a36Sopenharmony_ci}; 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_cistatic struct attribute_group cpu_common_attr_group = { 114462306a36Sopenharmony_ci .attrs = cpu_common_attrs, 114562306a36Sopenharmony_ci}; 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_cistatic struct attribute *cpu_online_attrs[] = { 114862306a36Sopenharmony_ci &dev_attr_idle_count.attr, 114962306a36Sopenharmony_ci &dev_attr_idle_time_us.attr, 115062306a36Sopenharmony_ci NULL, 115162306a36Sopenharmony_ci}; 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_cistatic struct attribute_group cpu_online_attr_group = { 115462306a36Sopenharmony_ci .attrs = cpu_online_attrs, 115562306a36Sopenharmony_ci}; 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_cistatic int smp_cpu_online(unsigned int cpu) 115862306a36Sopenharmony_ci{ 115962306a36Sopenharmony_ci struct device *s = &per_cpu(cpu_device, cpu)->dev; 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_ci return sysfs_create_group(&s->kobj, &cpu_online_attr_group); 116262306a36Sopenharmony_ci} 116362306a36Sopenharmony_ci 116462306a36Sopenharmony_cistatic int smp_cpu_pre_down(unsigned int cpu) 116562306a36Sopenharmony_ci{ 116662306a36Sopenharmony_ci struct device *s = &per_cpu(cpu_device, cpu)->dev; 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_ci sysfs_remove_group(&s->kobj, &cpu_online_attr_group); 116962306a36Sopenharmony_ci return 0; 117062306a36Sopenharmony_ci} 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_cistatic int smp_add_present_cpu(int cpu) 117362306a36Sopenharmony_ci{ 117462306a36Sopenharmony_ci struct device *s; 117562306a36Sopenharmony_ci struct cpu *c; 117662306a36Sopenharmony_ci int rc; 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_ci c = kzalloc(sizeof(*c), GFP_KERNEL); 117962306a36Sopenharmony_ci if (!c) 118062306a36Sopenharmony_ci return -ENOMEM; 118162306a36Sopenharmony_ci per_cpu(cpu_device, cpu) = c; 118262306a36Sopenharmony_ci s = &c->dev; 118362306a36Sopenharmony_ci c->hotpluggable = 1; 118462306a36Sopenharmony_ci rc = register_cpu(c, cpu); 118562306a36Sopenharmony_ci if (rc) 118662306a36Sopenharmony_ci goto out; 118762306a36Sopenharmony_ci rc = sysfs_create_group(&s->kobj, &cpu_common_attr_group); 118862306a36Sopenharmony_ci if (rc) 118962306a36Sopenharmony_ci goto out_cpu; 119062306a36Sopenharmony_ci rc = topology_cpu_init(c); 119162306a36Sopenharmony_ci if (rc) 119262306a36Sopenharmony_ci goto out_topology; 119362306a36Sopenharmony_ci return 0; 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_ciout_topology: 119662306a36Sopenharmony_ci sysfs_remove_group(&s->kobj, &cpu_common_attr_group); 119762306a36Sopenharmony_ciout_cpu: 119862306a36Sopenharmony_ci unregister_cpu(c); 119962306a36Sopenharmony_ciout: 120062306a36Sopenharmony_ci return rc; 120162306a36Sopenharmony_ci} 120262306a36Sopenharmony_ci 120362306a36Sopenharmony_ciint __ref smp_rescan_cpus(void) 120462306a36Sopenharmony_ci{ 120562306a36Sopenharmony_ci struct sclp_core_info *info; 120662306a36Sopenharmony_ci int nr; 120762306a36Sopenharmony_ci 120862306a36Sopenharmony_ci info = kzalloc(sizeof(*info), GFP_KERNEL); 120962306a36Sopenharmony_ci if (!info) 121062306a36Sopenharmony_ci return -ENOMEM; 121162306a36Sopenharmony_ci smp_get_core_info(info, 0); 121262306a36Sopenharmony_ci nr = __smp_rescan_cpus(info, false); 121362306a36Sopenharmony_ci kfree(info); 121462306a36Sopenharmony_ci if (nr) 121562306a36Sopenharmony_ci topology_schedule_update(); 121662306a36Sopenharmony_ci return 0; 121762306a36Sopenharmony_ci} 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_cistatic ssize_t __ref rescan_store(struct device *dev, 122062306a36Sopenharmony_ci struct device_attribute *attr, 122162306a36Sopenharmony_ci const char *buf, 122262306a36Sopenharmony_ci size_t count) 122362306a36Sopenharmony_ci{ 122462306a36Sopenharmony_ci int rc; 122562306a36Sopenharmony_ci 122662306a36Sopenharmony_ci rc = lock_device_hotplug_sysfs(); 122762306a36Sopenharmony_ci if (rc) 122862306a36Sopenharmony_ci return rc; 122962306a36Sopenharmony_ci rc = smp_rescan_cpus(); 123062306a36Sopenharmony_ci unlock_device_hotplug(); 123162306a36Sopenharmony_ci return rc ? rc : count; 123262306a36Sopenharmony_ci} 123362306a36Sopenharmony_cistatic DEVICE_ATTR_WO(rescan); 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_cistatic int __init s390_smp_init(void) 123662306a36Sopenharmony_ci{ 123762306a36Sopenharmony_ci struct device *dev_root; 123862306a36Sopenharmony_ci int cpu, rc = 0; 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_ci dev_root = bus_get_dev_root(&cpu_subsys); 124162306a36Sopenharmony_ci if (dev_root) { 124262306a36Sopenharmony_ci rc = device_create_file(dev_root, &dev_attr_rescan); 124362306a36Sopenharmony_ci put_device(dev_root); 124462306a36Sopenharmony_ci if (rc) 124562306a36Sopenharmony_ci return rc; 124662306a36Sopenharmony_ci } 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_ci for_each_present_cpu(cpu) { 124962306a36Sopenharmony_ci rc = smp_add_present_cpu(cpu); 125062306a36Sopenharmony_ci if (rc) 125162306a36Sopenharmony_ci goto out; 125262306a36Sopenharmony_ci } 125362306a36Sopenharmony_ci 125462306a36Sopenharmony_ci rc = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "s390/smp:online", 125562306a36Sopenharmony_ci smp_cpu_online, smp_cpu_pre_down); 125662306a36Sopenharmony_ci rc = rc <= 0 ? rc : 0; 125762306a36Sopenharmony_ciout: 125862306a36Sopenharmony_ci return rc; 125962306a36Sopenharmony_ci} 126062306a36Sopenharmony_cisubsys_initcall(s390_smp_init); 126162306a36Sopenharmony_ci 126262306a36Sopenharmony_cistatic __always_inline void set_new_lowcore(struct lowcore *lc) 126362306a36Sopenharmony_ci{ 126462306a36Sopenharmony_ci union register_pair dst, src; 126562306a36Sopenharmony_ci u32 pfx; 126662306a36Sopenharmony_ci 126762306a36Sopenharmony_ci src.even = (unsigned long) &S390_lowcore; 126862306a36Sopenharmony_ci src.odd = sizeof(S390_lowcore); 126962306a36Sopenharmony_ci dst.even = (unsigned long) lc; 127062306a36Sopenharmony_ci dst.odd = sizeof(*lc); 127162306a36Sopenharmony_ci pfx = __pa(lc); 127262306a36Sopenharmony_ci 127362306a36Sopenharmony_ci asm volatile( 127462306a36Sopenharmony_ci " mvcl %[dst],%[src]\n" 127562306a36Sopenharmony_ci " spx %[pfx]\n" 127662306a36Sopenharmony_ci : [dst] "+&d" (dst.pair), [src] "+&d" (src.pair) 127762306a36Sopenharmony_ci : [pfx] "Q" (pfx) 127862306a36Sopenharmony_ci : "memory", "cc"); 127962306a36Sopenharmony_ci} 128062306a36Sopenharmony_ci 128162306a36Sopenharmony_ciint __init smp_reinit_ipl_cpu(void) 128262306a36Sopenharmony_ci{ 128362306a36Sopenharmony_ci unsigned long async_stack, nodat_stack, mcck_stack; 128462306a36Sopenharmony_ci struct lowcore *lc, *lc_ipl; 128562306a36Sopenharmony_ci unsigned long flags, cr0; 128662306a36Sopenharmony_ci u64 mcesad; 128762306a36Sopenharmony_ci 128862306a36Sopenharmony_ci lc_ipl = lowcore_ptr[0]; 128962306a36Sopenharmony_ci lc = (struct lowcore *) __get_free_pages(GFP_KERNEL | GFP_DMA, LC_ORDER); 129062306a36Sopenharmony_ci nodat_stack = __get_free_pages(GFP_KERNEL, THREAD_SIZE_ORDER); 129162306a36Sopenharmony_ci async_stack = stack_alloc(); 129262306a36Sopenharmony_ci mcck_stack = stack_alloc(); 129362306a36Sopenharmony_ci if (!lc || !nodat_stack || !async_stack || !mcck_stack || nmi_alloc_mcesa(&mcesad)) 129462306a36Sopenharmony_ci panic("Couldn't allocate memory"); 129562306a36Sopenharmony_ci 129662306a36Sopenharmony_ci local_irq_save(flags); 129762306a36Sopenharmony_ci local_mcck_disable(); 129862306a36Sopenharmony_ci set_new_lowcore(lc); 129962306a36Sopenharmony_ci S390_lowcore.nodat_stack = nodat_stack + STACK_INIT_OFFSET; 130062306a36Sopenharmony_ci S390_lowcore.async_stack = async_stack + STACK_INIT_OFFSET; 130162306a36Sopenharmony_ci S390_lowcore.mcck_stack = mcck_stack + STACK_INIT_OFFSET; 130262306a36Sopenharmony_ci __ctl_store(cr0, 0, 0); 130362306a36Sopenharmony_ci __ctl_clear_bit(0, 28); /* disable lowcore protection */ 130462306a36Sopenharmony_ci S390_lowcore.mcesad = mcesad; 130562306a36Sopenharmony_ci __ctl_load(cr0, 0, 0); 130662306a36Sopenharmony_ci if (abs_lowcore_map(0, lc, false)) 130762306a36Sopenharmony_ci panic("Couldn't remap absolute lowcore"); 130862306a36Sopenharmony_ci lowcore_ptr[0] = lc; 130962306a36Sopenharmony_ci local_mcck_enable(); 131062306a36Sopenharmony_ci local_irq_restore(flags); 131162306a36Sopenharmony_ci 131262306a36Sopenharmony_ci memblock_free_late(__pa(lc_ipl->mcck_stack - STACK_INIT_OFFSET), THREAD_SIZE); 131362306a36Sopenharmony_ci memblock_free_late(__pa(lc_ipl->async_stack - STACK_INIT_OFFSET), THREAD_SIZE); 131462306a36Sopenharmony_ci memblock_free_late(__pa(lc_ipl->nodat_stack - STACK_INIT_OFFSET), THREAD_SIZE); 131562306a36Sopenharmony_ci memblock_free_late(__pa(lc_ipl), sizeof(*lc_ipl)); 131662306a36Sopenharmony_ci return 0; 131762306a36Sopenharmony_ci} 1318