[S390] cpu shutdown rework

Let one master cpu kill all other cpus instead of sending an external
interrupt to all other cpus so they can kill themselves.
Simplifies reipl/shutdown functions a lot.

Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
This commit is contained in:
Heiko Carstens 2006-12-04 15:40:33 +01:00 committed by Martin Schwidefsky
parent 740b5706b9
commit c6b5b847a7
5 changed files with 58 additions and 180 deletions

View file

@ -576,23 +576,6 @@ static struct subsys_attribute dump_type_attr =
static decl_subsys(dump, NULL, NULL);
#ifdef CONFIG_SMP
static void dump_smp_stop_all(void)
{
int cpu;
preempt_disable();
for_each_online_cpu(cpu) {
if (cpu == smp_processor_id())
continue;
while (signal_processor(cpu, sigp_stop) == sigp_busy)
udelay(10);
}
preempt_enable();
}
#else
#define dump_smp_stop_all() do { } while (0)
#endif
/*
* Shutdown actions section
*/
@ -724,13 +707,13 @@ static void do_dump(void)
switch (dump_method) {
case IPL_METHOD_CCW_CIO:
dump_smp_stop_all();
smp_send_stop();
devid.devno = dump_block_ccw->ipl_info.ccw.devno;
devid.ssid = 0;
reipl_ccw_dev(&devid);
break;
case IPL_METHOD_CCW_VM:
dump_smp_stop_all();
smp_send_stop();
sprintf(buf, "STORE STATUS");
__cpcmd(buf, NULL, 0, NULL);
sprintf(buf, "IPL %X", dump_block_ccw->ipl_info.ccw.devno);
@ -1059,9 +1042,6 @@ void s390_reset_system(void)
{
struct _lowcore *lc;
/* Disable all interrupts/machine checks */
__load_psw_mask(PSW_KERNEL_BITS & ~PSW_MASK_MCHECK);
/* Stack for interrupt/machine check handler */
lc = (struct _lowcore *)(unsigned long) store_prefix();
lc->panic_stack = S390_lowcore.panic_stack;

View file

@ -1,15 +1,10 @@
/*
* arch/s390/kernel/machine_kexec.c
*
* (C) Copyright IBM Corp. 2005
* Copyright IBM Corp. 2005,2006
*
* Author(s): Rolf Adelsberger <adelsberger@de.ibm.com>
*
*/
/*
* s390_machine_kexec.c - handle the transition of Linux booting another kernel
* on the S390 architecture.
* Author(s): Rolf Adelsberger,
* Heiko Carstens <heiko.carstens@de.ibm.com>
*/
#include <linux/device.h>
@ -24,81 +19,53 @@
#include <asm/smp.h>
#include <asm/reset.h>
static void kexec_halt_all_cpus(void *);
typedef void (*relocate_kernel_t) (kimage_entry_t *, unsigned long);
typedef void (*relocate_kernel_t)(kimage_entry_t *, unsigned long);
extern const unsigned char relocate_kernel[];
extern const unsigned long long relocate_kernel_len;
int
machine_kexec_prepare(struct kimage *image)
int machine_kexec_prepare(struct kimage *image)
{
unsigned long reboot_code_buffer;
void *reboot_code_buffer;
/* We don't support anything but the default image type for now. */
if (image->type != KEXEC_TYPE_DEFAULT)
return -EINVAL;
/* Get the destination where the assembler code should be copied to.*/
reboot_code_buffer = page_to_pfn(image->control_code_page)<<PAGE_SHIFT;
reboot_code_buffer = (void *) page_to_phys(image->control_code_page);
/* Then copy it */
memcpy((void *) reboot_code_buffer, relocate_kernel,
relocate_kernel_len);
memcpy(reboot_code_buffer, relocate_kernel, relocate_kernel_len);
return 0;
}
void
machine_kexec_cleanup(struct kimage *image)
void machine_kexec_cleanup(struct kimage *image)
{
}
void
machine_shutdown(void)
void machine_shutdown(void)
{
printk(KERN_INFO "kexec: machine_shutdown called\n");
}
NORET_TYPE void
machine_kexec(struct kimage *image)
{
on_each_cpu(kexec_halt_all_cpus, image, 0, 0);
for (;;);
}
extern void pfault_fini(void);
static void
kexec_halt_all_cpus(void *kernel_image)
void machine_kexec(struct kimage *image)
{
static atomic_t cpuid = ATOMIC_INIT(-1);
int cpu;
struct kimage *image;
relocate_kernel_t data_mover;
preempt_disable();
#ifdef CONFIG_PFAULT
if (MACHINE_IS_VM)
pfault_fini();
#endif
if (atomic_cmpxchg(&cpuid, -1, smp_processor_id()) != -1)
signal_processor(smp_processor_id(), sigp_stop);
/* Wait for all other cpus to enter stopped state */
for_each_online_cpu(cpu) {
if (cpu == smp_processor_id())
continue;
while (!smp_cpu_not_running(cpu))
cpu_relax();
}
smp_send_stop();
s390_reset_system();
image = (struct kimage *) kernel_image;
data_mover = (relocate_kernel_t)
(page_to_pfn(image->control_code_page) << PAGE_SHIFT);
data_mover = (relocate_kernel_t) page_to_phys(image->control_code_page);
/* Call the moving routine */
(*data_mover) (&image->head, image->start);
(*data_mover)(&image->head, image->start);
for (;;);
}

View file

@ -230,18 +230,37 @@ static inline void do_store_status(void)
}
}
static inline void do_wait_for_stop(void)
{
int cpu;
/* Wait for all other cpus to enter stopped state */
for_each_online_cpu(cpu) {
if (cpu == smp_processor_id())
continue;
while(!smp_cpu_not_running(cpu))
cpu_relax();
}
}
/*
* this function sends a 'stop' sigp to all other CPUs in the system.
* it goes straight through.
*/
void smp_send_stop(void)
{
/* Disable all interrupts/machine checks */
__load_psw_mask(PSW_KERNEL_BITS & ~PSW_MASK_MCHECK);
/* write magic number to zero page (absolute 0) */
lowcore_ptr[smp_processor_id()]->panic_magic = __PANIC_MAGIC;
/* stop other processors. */
do_send_stop();
/* wait until other processors are stopped */
do_wait_for_stop();
/* store status of other processors. */
do_store_status();
}
@ -250,88 +269,28 @@ void smp_send_stop(void)
* Reboot, halt and power_off routines for SMP.
*/
static void do_machine_restart(void * __unused)
{
int cpu;
static atomic_t cpuid = ATOMIC_INIT(-1);
if (atomic_cmpxchg(&cpuid, -1, smp_processor_id()) != -1)
signal_processor(smp_processor_id(), sigp_stop);
/* Wait for all other cpus to enter stopped state */
for_each_online_cpu(cpu) {
if (cpu == smp_processor_id())
continue;
while(!smp_cpu_not_running(cpu))
cpu_relax();
}
/* Store status of other cpus. */
do_store_status();
/*
* Finally call reipl. Because we waited for all other
* cpus to enter this function we know that they do
* not hold any s390irq-locks (the cpus have been
* interrupted by an external interrupt and s390irq
* locks are always held disabled).
*/
do_reipl();
}
void machine_restart_smp(char * __unused)
{
on_each_cpu(do_machine_restart, NULL, 0, 0);
}
static void do_wait_for_stop(void)
{
unsigned long cr[16];
__ctl_store(cr, 0, 15);
cr[0] &= ~0xffff;
cr[6] = 0;
__ctl_load(cr, 0, 15);
for (;;)
enabled_wait();
}
static void do_machine_halt(void * __unused)
{
static atomic_t cpuid = ATOMIC_INIT(-1);
if (atomic_cmpxchg(&cpuid, -1, smp_processor_id()) == -1) {
smp_send_stop();
if (MACHINE_IS_VM && strlen(vmhalt_cmd) > 0)
cpcmd(vmhalt_cmd, NULL, 0, NULL);
signal_processor(smp_processor_id(),
sigp_stop_and_store_status);
}
do_wait_for_stop();
smp_send_stop();
do_reipl();
}
void machine_halt_smp(void)
{
on_each_cpu(do_machine_halt, NULL, 0, 0);
}
static void do_machine_power_off(void * __unused)
{
static atomic_t cpuid = ATOMIC_INIT(-1);
if (atomic_cmpxchg(&cpuid, -1, smp_processor_id()) == -1) {
smp_send_stop();
if (MACHINE_IS_VM && strlen(vmpoff_cmd) > 0)
cpcmd(vmpoff_cmd, NULL, 0, NULL);
signal_processor(smp_processor_id(),
sigp_stop_and_store_status);
}
do_wait_for_stop();
smp_send_stop();
if (MACHINE_IS_VM && strlen(vmhalt_cmd) > 0)
__cpcmd(vmhalt_cmd, NULL, 0, NULL);
signal_processor(smp_processor_id(), sigp_stop_and_store_status);
for (;;);
}
void machine_power_off_smp(void)
{
on_each_cpu(do_machine_power_off, NULL, 0, 0);
smp_send_stop();
if (MACHINE_IS_VM && strlen(vmpoff_cmd) > 0)
__cpcmd(vmpoff_cmd, NULL, 0, NULL);
signal_processor(smp_processor_id(), sigp_stop_and_store_status);
for (;;);
}
/*
@ -860,4 +819,3 @@ EXPORT_SYMBOL(smp_ctl_clear_bit);
EXPORT_SYMBOL(smp_call_function);
EXPORT_SYMBOL(smp_get_cpu);
EXPORT_SYMBOL(smp_put_cpu);

View file

@ -19,52 +19,17 @@
#include "sclp.h"
#ifdef CONFIG_SMP
/* Signal completion of shutdown process. All CPUs except the first to enter
* this function: go to stopped state. First CPU: wait until all other
* CPUs are in stopped or check stop state. Afterwards, load special PSW
* to indicate completion. */
static void
do_load_quiesce_psw(void * __unused)
{
static atomic_t cpuid = ATOMIC_INIT(-1);
psw_t quiesce_psw;
int cpu;
if (atomic_cmpxchg(&cpuid, -1, smp_processor_id()) != -1)
signal_processor(smp_processor_id(), sigp_stop);
/* Wait for all other cpus to enter stopped state */
for_each_online_cpu(cpu) {
if (cpu == smp_processor_id())
continue;
while(!smp_cpu_not_running(cpu))
cpu_relax();
}
/* Quiesce the last cpu with the special psw */
quiesce_psw.mask = PSW_BASE_BITS | PSW_MASK_WAIT;
quiesce_psw.addr = 0xfff;
__load_psw(quiesce_psw);
}
/* Shutdown handler. Perform shutdown function on all CPUs. */
static void
do_machine_quiesce(void)
{
on_each_cpu(do_load_quiesce_psw, NULL, 0, 0);
}
#else
/* Shutdown handler. Signal completion of shutdown by loading special PSW. */
static void
do_machine_quiesce(void)
{
psw_t quiesce_psw;
smp_send_stop();
quiesce_psw.mask = PSW_BASE_BITS | PSW_MASK_WAIT;
quiesce_psw.addr = 0xfff;
__load_psw(quiesce_psw);
}
#endif
/* Handler for quiesce event. Start shutdown procedure. */
static void

View file

@ -18,6 +18,7 @@
#include <asm/lowcore.h>
#include <asm/sigp.h>
#include <asm/ptrace.h>
/*
s390 specific smp.c headers
@ -101,6 +102,13 @@ smp_call_function_on(void (*func) (void *info), void *info,
func(info);
return 0;
}
static inline void smp_send_stop(void)
{
/* Disable all interrupts/machine checks */
__load_psw_mask(PSW_KERNEL_BITS & ~PSW_MASK_MCHECK);
}
#define smp_cpu_not_running(cpu) 1
#define smp_get_cpu(cpu) ({ 0; })
#define smp_put_cpu(cpu) ({ 0; })