Skip to content

Commit

Permalink
s390/dump: rework CPU register dump code
Browse files Browse the repository at this point in the history
To collect the CPU registers of the crashed system allocated a single
page with memblock_alloc_base and use it as a copy buffer. Replace the
stop-and-store-status sigp with a store-status-at-address sigp in
smp_save_dump_cpus() and smp_store_status(). In both cases the target
CPU is already stopped and store-status-at-address avoids the detour
via the absolute zero page.

For kexec simplify s390_reset_system and call store_status() before
the prefix register of the boot CPU has been set to zero. Use STPX
to store the prefix register and remove dump_prefix_page.

Acked-by: Michael Holzheu <holzheu@linux.vnet.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
  • Loading branch information
Martin Schwidefsky committed Nov 27, 2015
1 parent f08b841 commit 1a36a39
Showing 12 changed files with 158 additions and 145 deletions.
10 changes: 1 addition & 9 deletions arch/s390/include/asm/fpu/internal.h
Original file line number Diff line number Diff line change
@@ -12,21 +12,13 @@
#include <asm/ctl_reg.h>
#include <asm/fpu/types.h>

static inline void save_vx_regs_safe(__vector128 *vxrs)
static inline void save_vx_regs(__vector128 *vxrs)
{
unsigned long cr0, flags;

flags = arch_local_irq_save();
__ctl_store(cr0, 0, 0);
__ctl_set_bit(0, 17);
__ctl_set_bit(0, 18);
asm volatile(
" la 1,%0\n"
" .word 0xe70f,0x1000,0x003e\n" /* vstm 0,15,0(1) */
" .word 0xe70f,0x1100,0x0c3e\n" /* vstm 16,31,256(1) */
: "=Q" (*(struct vx_array *) vxrs) : : "1");
__ctl_load(cr0, 0, 0);
arch_local_irq_restore(flags);
}

static inline void convert_vx_to_fp(freg_t *fprs, __vector128 *vxrs)
3 changes: 1 addition & 2 deletions arch/s390/include/asm/ipl.h
Original file line number Diff line number Diff line change
@@ -87,7 +87,6 @@ struct ipl_parameter_block {
* IPL validity flags
*/
extern u32 ipl_flags;
extern u32 dump_prefix_page;

struct dump_save_areas {
struct save_area_ext **areas;
@@ -176,7 +175,7 @@ enum diag308_rc {

extern int diag308(unsigned long subcode, void *addr);
extern void diag308_reset(void);
extern void store_status(void);
extern void store_status(void (*fn)(void *), void *data);
extern void lgr_info_log(void);

#endif /* _ASM_S390_IPL_H */
3 changes: 1 addition & 2 deletions arch/s390/include/asm/reset.h
Original file line number Diff line number Diff line change
@@ -15,6 +15,5 @@ struct reset_call {

extern void register_reset_call(struct reset_call *reset);
extern void unregister_reset_call(struct reset_call *reset);
extern void s390_reset_system(void (*fn_pre)(void),
void (*fn_post)(void *), void *data);
extern void s390_reset_system(void);
#endif /* _ASM_S390_RESET_H */
2 changes: 1 addition & 1 deletion arch/s390/include/asm/smp.h
Original file line number Diff line number Diff line change
@@ -18,6 +18,7 @@
extern struct mutex smp_cpu_state_mutex;
extern unsigned int smp_cpu_mt_shift;
extern unsigned int smp_cpu_mtid;
extern __vector128 __initdata boot_cpu_vector_save_area[__NUM_VXRS];

extern int __cpu_up(unsigned int cpu, struct task_struct *tidle);

@@ -55,7 +56,6 @@ static inline int smp_store_status(int cpu) { return 0; }
static inline int smp_vcpu_scheduled(int cpu) { return 1; }
static inline void smp_yield_cpu(int cpu) { }
static inline void smp_fill_possible_mask(void) { }
static inline void smp_save_dump_cpus(void) { }

#endif /* CONFIG_SMP */

9 changes: 9 additions & 0 deletions arch/s390/kernel/early.c
Original file line number Diff line number Diff line change
@@ -335,6 +335,14 @@ static __init void detect_machine_facilities(void)
}
}

static inline void save_vector_registers(void)
{
#ifdef CONFIG_CRASH_DUMP
if (test_facility(129))
save_vx_regs(boot_cpu_vector_save_area);
#endif
}

static int __init disable_vector_extension(char *str)
{
S390_lowcore.machine_flags &= ~MACHINE_FLAG_VX;
@@ -451,6 +459,7 @@ void __init startup_init(void)
detect_diag9c();
detect_diag44();
detect_machine_facilities();
save_vector_registers();
setup_topology();
sclp_early_detect();
lockdep_on();
17 changes: 1 addition & 16 deletions arch/s390/kernel/ipl.c
Original file line number Diff line number Diff line change
@@ -2039,10 +2039,7 @@ static void do_reset_calls(void)
reset->fn();
}

u32 dump_prefix_page;

void s390_reset_system(void (*fn_pre)(void),
void (*fn_post)(void *), void *data)
void s390_reset_system(void)
{
struct _lowcore *lc;

@@ -2051,9 +2048,6 @@ void s390_reset_system(void (*fn_pre)(void),
/* Stack for interrupt/machine check handler */
lc->panic_stack = S390_lowcore.panic_stack;

/* Save prefix page address for dump case */
dump_prefix_page = (u32)(unsigned long) lc;

/* Disable prefixing */
set_prefix(0);

@@ -2077,14 +2071,5 @@ void s390_reset_system(void (*fn_pre)(void),
S390_lowcore.subchannel_id = 0;
S390_lowcore.subchannel_nr = 0;

/* Store status at absolute zero */
store_status();

/* Call function before reset */
if (fn_pre)
fn_pre();
do_reset_calls();
/* Call function after reset */
if (fn_post)
fn_post(data);
}
104 changes: 61 additions & 43 deletions arch/s390/kernel/machine_kexec.c
Original file line number Diff line number Diff line change
@@ -34,40 +34,6 @@ extern const unsigned long long relocate_kernel_len;

#ifdef CONFIG_CRASH_DUMP

/*
* Initialize CPU ELF notes
*/
static void setup_regs(void)
{
struct save_area *sa, *sa_0;
unsigned long prefix;
int cpu, this_cpu;

/* setup_regs is called with the prefix register = 0 */
sa_0 = (struct save_area *) __LC_FPREGS_SAVE_AREA;

/* Get status of this CPU out of absolute zero */
prefix = (unsigned long) S390_lowcore.prefixreg_save_area;
sa = (struct save_area *)(prefix + __LC_FPREGS_SAVE_AREA);
memcpy(sa, sa_0, sizeof(struct save_area));
if (MACHINE_HAS_VX) {
struct _lowcore *lc = (struct _lowcore *) prefix;
save_vx_regs_safe((void *) lc->vector_save_area_addr);
}

/* Get status of the other CPUs */
this_cpu = smp_find_processor_id(stap());
for_each_online_cpu(cpu) {
if (cpu == this_cpu)
continue;
if (smp_store_status(cpu))
continue;
prefix = (unsigned long) S390_lowcore.prefixreg_save_area;
sa = (struct save_area *)(prefix + __LC_FPREGS_SAVE_AREA);
memcpy(sa, sa_0, sizeof(struct save_area));
}
}

/*
* PM notifier callback for kdump
*/
@@ -99,14 +65,66 @@ static int __init machine_kdump_pm_init(void)
arch_initcall(machine_kdump_pm_init);

/*
* Start kdump: We expect here that a store status has been done on our CPU
* Reset the system, copy boot CPU registers to absolute zero,
* and jump to the kdump image
*/
static void __do_machine_kdump(void *image)
{
int (*start_kdump)(int) = (void *)((struct kimage *) image)->start;
int (*start_kdump)(int);
unsigned long prefix;

/* store_status() saved the prefix register to lowcore */
prefix = (unsigned long) S390_lowcore.prefixreg_save_area;

/* Now do the reset */
s390_reset_system();

/*
* Copy dump CPU store status info to absolute zero.
* This need to be done *after* s390_reset_system set the
* prefix register of this CPU to zero
*/
memcpy((void *) __LC_FPREGS_SAVE_AREA,
(void *)(prefix + __LC_FPREGS_SAVE_AREA), 512);

__load_psw_mask(PSW_MASK_BASE | PSW_DEFAULT_KEY | PSW_MASK_EA | PSW_MASK_BA);
start_kdump = (void *)((struct kimage *) image)->start;
start_kdump(1);

/* Die if start_kdump returns */
disabled_wait((unsigned long) __builtin_return_address(0));
}

/*
* Start kdump: create a LGR log entry, store status of all CPUs and
* branch to __do_machine_kdump.
*/
static noinline void __machine_kdump(void *image)
{
int this_cpu, cpu;

lgr_info_log();
/* Get status of the other CPUs */
this_cpu = smp_find_processor_id(stap());
for_each_online_cpu(cpu) {
if (cpu == this_cpu)
continue;
if (smp_store_status(cpu))
continue;
}
/* Store status of the boot CPU */
if (MACHINE_HAS_VX)
save_vx_regs((void *) &S390_lowcore.vector_save_area);
/*
* To create a good backchain for this CPU in the dump store_status
* is passed the address of a function. The address is saved into
* the PSW save area of the boot CPU and the function is invoked as
* a tail call of store_status. The backchain in the dump will look
* like this:
* restart_int_handler -> __machine_kexec -> __do_machine_kdump
* The call to store_status() will not return.
*/
store_status(__do_machine_kdump, image);
}
#endif

@@ -229,10 +247,14 @@ static void __do_machine_kexec(void *data)
relocate_kernel_t data_mover;
struct kimage *image = data;

s390_reset_system();
data_mover = (relocate_kernel_t) page_to_phys(image->control_code_page);

/* Call the moving routine */
(*data_mover)(&image->head, image->start);

/* Die if kexec returns */
disabled_wait((unsigned long) __builtin_return_address(0));
}

/*
@@ -245,14 +267,10 @@ static void __machine_kexec(void *data)
tracing_off();
debug_locks_off();
#ifdef CONFIG_CRASH_DUMP
if (((struct kimage *) data)->type == KEXEC_TYPE_CRASH) {

lgr_info_log();
s390_reset_system(setup_regs, __do_machine_kdump, data);
} else
if (((struct kimage *) data)->type == KEXEC_TYPE_CRASH)
__machine_kdump(data);
#endif
s390_reset_system(NULL, __do_machine_kexec, data);
disabled_wait((unsigned long) __builtin_return_address(0));
__do_machine_kexec(data);
}

/*
37 changes: 19 additions & 18 deletions arch/s390/kernel/reipl.S
Original file line number Diff line number Diff line change
@@ -9,12 +9,11 @@
#include <asm/sigp.h>

#
# store_status
# Issue "store status" for the current CPU to its prefix page
# and call passed function afterwards
#
# Prerequisites to run this function:
# - Prefix register is set to zero
# - Original prefix register is stored in "dump_prefix_page"
# - Lowcore protection is off
# r2 = Function to be called after store status
# r3 = Parameter for function
#
ENTRY(store_status)
/* Save register one and load save area base */
@@ -53,23 +52,23 @@ ENTRY(store_status)
/* CPU timer */
lghi %r1,__LC_CPU_TIMER_SAVE_AREA
stpt 0(%r1)
/* Saved prefix register */
/* Store prefix register */
lghi %r1,__LC_PREFIX_SAVE_AREA
larl %r2,dump_prefix_page
mvc 0(4,%r1),0(%r2)
stpx 0(%r1)
/* Clock comparator - seven bytes */
lghi %r1,__LC_CLOCK_COMP_SAVE_AREA
larl %r2,.Lclkcmp
stckc 0(%r2)
mvc 1(7,%r1),1(%r2)
larl %r4,.Lclkcmp
stckc 0(%r4)
mvc 1(7,%r1),1(%r4)
/* Program status word */
lghi %r1,__LC_PSW_SAVE_AREA
epsw %r2,%r3
st %r2,0(%r1)
st %r3,4(%r1)
larl %r2,store_status
epsw %r4,%r5
st %r4,0(%r1)
st %r5,4(%r1)
stg %r2,8(%r1)
br %r14
lgr %r1,%r2
lgr %r2,%r3
br %r1

.section .bss
.align 8
@@ -84,9 +83,11 @@ ENTRY(store_status)
ENTRY(do_reipl_asm)
basr %r13,0
.Lpg0: lpswe .Lnewpsw-.Lpg0(%r13)
.Lpg1: brasl %r14,store_status
.Lpg1: lgr %r3,%r2
larl %r2,.Lstatus
brasl %r14,store_status

lctlg %c6,%c6,.Lall-.Lpg0(%r13)
.Lstatus: lctlg %c6,%c6,.Lall-.Lpg0(%r13)
lgr %r1,%r2
mvc __LC_PGM_NEW_PSW(16),.Lpcnew-.Lpg0(%r13)
stsch .Lschib-.Lpg0(%r13)
2 changes: 2 additions & 0 deletions arch/s390/kernel/setup.c
Original file line number Diff line number Diff line change
@@ -865,11 +865,13 @@ void __init setup_arch(char **cmdline_p)

check_initrd();
reserve_crashkernel();
#ifdef CONFIG_CRASH_DUMP
/*
* Be aware that smp_save_dump_cpus() triggers a system reset.
* Therefore CPU and device initialization should be done afterwards.
*/
smp_save_dump_cpus();
#endif

setup_resources();
setup_vmcoreinfo();
Loading

0 comments on commit 1a36a39

Please sign in to comment.