/*
* arch/s390/kernel/smp.c
*
* Copyright IBM Corp. 1999, 2009
* Author(s): Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com),
* Martin Schwidefsky (schwidefsky@de.ibm.com)
* Heiko Carstens (heiko.carstens@de.ibm.com)
*
* based on other smp stuff by
* (c) 1995 Alan Cox, CymruNET Ltd <alan@cymru.net>
* (c) 1998 Ingo Molnar
*
* We work with logical cpu numbering everywhere we can. The only
* functions using the real cpu address (got from STAP) are the sigp
* functions. For all other functions we use the identity mapping.
* That means that cpu_number_map[i] == i for every cpu. cpu_number_map is
* used e.g. to find the idle task belonging to a logical cpu. Every array
* in the kernel is sorted by the logical cpu number and not by the physical
* one which is causing all the confusion with __cpu_logical_map and
* cpu_number_map in other architectures.
*/
#define KMSG_COMPONENT "cpu"
#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
#include <linux/module.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/err.h>
#include <linux/spinlock.h>
#include <linux/kernel_stat.h>
#include <linux/delay.h>
#include <linux/cache.h>
#include <linux/interrupt.h>
#include <linux/irqflags.h>
#include <linux/cpu.h>
#include <linux/timex.h>
#include <linux/bootmem.h>
#include <asm/ipl.h>
#include <asm/setup.h>
#include <asm/sigp.h>
#include <asm/pgalloc.h>
#include <asm/irq.h>
#include <asm/s390_ext.h>
#include <asm/cpcmd.h>
#include <asm/tlbflush.h>
#include <asm/timer.h>
#include <asm/lowcore.h>
#include <asm/sclp.h>
#include <asm/cputime.h>
#include <asm/vdso.h>
#include <asm/cpu.h>
#include "entry.h"
/* logical cpu to cpu address */
int __cpu_logical_map[NR_CPUS];
static struct task_struct *current_set[NR_CPUS];
static u8 smp_cpu_type;
static int smp_use_sigp_detection;
enum s390_cpu_state {
CPU_STATE_STANDBY,
CPU_STATE_CONFIGURED,
};
DEFINE_MUTEX(smp_cpu_state_mutex);
int smp_cpu_polarization[NR_CPUS];
static int smp_cpu_state[NR_CPUS];
static int cpu_management;
static DEFINE_PER_CPU(struct cpu, cpu_devices);
static void smp_ext_bitcall(int, ec_bit_sig);
static int cpu_stopped(int cpu)
{
__u32 status;
switch (signal_processor_ps(&status, 0, cpu, sigp_sense)) {
case sigp_status_stored:
/* Check for stopped and check stop state */
if (status & 0x50)
return 1;
break;
default:
break;
}
return 0;
}
void smp_send_stop(void)
{
int cpu, rc;
/* Disable all interrupts/machine checks */
__load_psw_mask(psw_kernel_bits & ~PSW_MASK_MCHECK);
trace_hardirqs_off();
/* stop all processors */
for_each_online_cpu(cpu) {
if (cpu == smp_processor_id())
continue;
do {
rc = signal_processor(cpu, sigp_stop);
} while (rc == sigp_busy);
while (!cpu_stopped(cpu))
cpu_relax();
}
}
/*
* This is the main routine where commands issued by other
* cpus are handled.
*/
static void do_ext_call_interrupt(__u16 code)
{
unsigned long bits;
/*
* handle bit signal external calls
*
* For the ec_schedule signal we have to do nothing. All the work
* is done automatically when we return from the interrupt.
*/
bits = xchg(&S390_lowcore.ext_call_fast, 0);
if (test_bit(ec_call_function, &bits))
generic_smp_call_function_interrupt();
if (test_bit(ec_call_function_single, &bits))
generic_smp_call_function_single_interrupt();
}
/*
* Send an external call sigp to another cpu and return without waiting
* for its completion.
*/
static void smp_ext_bitcall(int cpu, ec_bit_sig sig)
{
/*
* Set signaling bit in lowcore of target cpu and kick it
*/
set_bit(sig, (unsigned long *) &lowcore_ptr[cpu]->ext_call_fast);
while (signal_processor(cpu, sigp_emergency_signal) == sigp_busy)
udelay(10);
}
void arch_send_call_function_ipi_mask(const struct cpumask *mask)
{
int cpu;
for_each_cpu(cpu, mask)
smp_ext_bitcall(cpu, ec_call_function);
}
void arch_send_call_function_single_ipi(int cpu)
{
smp_ext_bitcall(cpu, ec_call_function_single);
}
#ifndef CONFIG_64BIT
/*
* this function sends a 'purge tlb' signal to another CPU.
*/
static void smp_ptlb_callback(void *info)
{
__tlb_flush_local();
}
void smp_ptlb_all(void)
{
on_each_cpu(smp_ptlb_callback, NULL, 1);
}
EXPORT_SYMBOL(smp_ptlb_all);
#endif /* ! CONFIG_64BIT */
/*
* this function sends a 'reschedule' IPI to another CPU.
* it goes straight through and wastes no time serializing
* anything. Worst case is that we lose a reschedule ...
*/
void smp_send_reschedule(int cpu)
{
smp_ext_bitcall(cpu, ec_schedule);
}
/*
* parameter area for the set/clear control bit callbacks
*/
struct ec_creg_mask_parms {
unsigned long orvals[16];
unsigned long andvals[16];
};
/*
* callback for setting/clearing control bits
*/
static void smp_ctl_bit_callback(void *info)
{
struct ec_creg_mask_parms