1
linux/arch/s390/kernel/machine_kexec.c
Cornelia Huck 7e560814de [S390] path grouping and path verifications fixes.
1. Multipath devices for which SetPGID is not supported are not handled well.
   Use NOP ccws for path verification (sans path grouping) when SetPGID is not
   supported.
2. Check for PGIDs already set with SensePGID on _all_ paths (not just the
   first one) and try to find a common one. Moan if no common PGID can be
   found (and use NOP verification). If no PGIDs have been set, use the css
   global PGID (as before). (Rationale: SetPGID will get a command reject if
   the PGID it tries to set does not match the already set PGID.)
3. Immediately before reboot, issue RESET CHANNEL PATH (rcp) on all chpids. This
   will remove the old PGIDs. rcp will generate solicited CRWs which can be
   savely ignored by the machine check handler (all other actions create
   unsolicited CRWs).

Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
2006-07-12 16:40:19 +02:00

108 lines
2.3 KiB
C

/*
* arch/s390/kernel/machine_kexec.c
*
* (C) Copyright IBM Corp. 2005
*
* Author(s): Rolf Adelsberger <adelsberger@de.ibm.com>
*
*/
/*
* s390_machine_kexec.c - handle the transition of Linux booting another kernel
* on the S390 architecture.
*/
#include <linux/device.h>
#include <linux/mm.h>
#include <linux/kexec.h>
#include <linux/delay.h>
#include <asm/cio.h>
#include <asm/setup.h>
#include <asm/pgtable.h>
#include <asm/pgalloc.h>
#include <asm/system.h>
#include <asm/smp.h>
static void kexec_halt_all_cpus(void *);
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)
{
unsigned long 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;
/* Then copy it */
memcpy((void *) reboot_code_buffer, relocate_kernel,
relocate_kernel_len);
return 0;
}
void
machine_kexec_cleanup(struct kimage *image)
{
}
void
machine_shutdown(void)
{
printk(KERN_INFO "kexec: machine_shutdown called\n");
}
NORET_TYPE void
machine_kexec(struct kimage *image)
{
clear_all_subchannels();
cio_reset_channel_paths();
/* Disable lowcore protection */
ctl_clear_bit(0,28);
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)
{
static atomic_t cpuid = ATOMIC_INIT(-1);
int cpu;
struct kimage *image;
relocate_kernel_t data_mover;
#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();
}
image = (struct kimage *) kernel_image;
data_mover = (relocate_kernel_t)
(page_to_pfn(image->control_code_page) << PAGE_SHIFT);
/* Call the moving routine */
(*data_mover) (&image->head, image->start);
}