Commit 915fea04 authored by Alexander Gordeev's avatar Alexander Gordeev Committed by Heiko Carstens

s390/smp: enable DAT before CPU restart callback is called

The restart interrupt is triggered whenever a secondary CPU is
brought online, a remote function call dispatched from another
CPU or a manual PSW restart is initiated and causes the system
to kdump. The handling routine is always called with DAT turned
off. It then initializes the stack frame and invokes a callback.

The existing callbacks handle DAT as follows:

  * __do_restart() and __machine_kexec() turn in on upon entry;
  * __ipl_run(), __reipl_run() and __dump_run() do not turn it
    right away, but all of them call diag308() - which turns DAT
    on, but only if kasan is enabled;

In addition to the described complexity all callbacks (and the
functions they call) should avoid kasan instrumentation while
DAT is off.

This update enables DAT in the assembler restart handler and
relieves any callbacks (which are mostly C functions) from
dealing with DAT altogether.

There are four types of CPU restart that initialize control
registers in different ways:

  1. Start of secondary CPU on boot - control registers are
     inherited from the IPL CPU;
  2. Restart of online CPU - control registers of the CPU being
     restarted are kept;
  3. Hotplug of offline CPU - control registers are inherited
     from the starting CPU;
  4. Start of offline CPU triggered by manual PSW restart -
     the control registers are read from the absolute lowcore
     and contain the boot time IPL CPU values updated with all
     follow-up calls of smp_ctl_set_bit() and smp_ctl_clear_bit()
     routines;

In first three cases contents of the control registers is the
most recent. In the latter case control registers are good
enough to facilitate successful completion of kdump operation.
Suggested-by: default avatarHeiko Carstens <hca@linux.ibm.com>
Signed-off-by: default avatarAlexander Gordeev <agordeev@linux.ibm.com>
Signed-off-by: default avatarHeiko Carstens <hca@linux.ibm.com>
parent e7dc78d3
...@@ -124,7 +124,8 @@ struct lowcore { ...@@ -124,7 +124,8 @@ struct lowcore {
/* Restart function and parameter. */ /* Restart function and parameter. */
__u64 restart_fn; /* 0x0370 */ __u64 restart_fn; /* 0x0370 */
__u64 restart_data; /* 0x0378 */ __u64 restart_data; /* 0x0378 */
__u64 restart_source; /* 0x0380 */ __u32 restart_source; /* 0x0380 */
__u32 restart_flags; /* 0x0384 */
/* Address space pointer. */ /* Address space pointer. */
__u64 kernel_asce; /* 0x0388 */ __u64 kernel_asce; /* 0x0388 */
......
...@@ -26,6 +26,8 @@ ...@@ -26,6 +26,8 @@
#define _CIF_MCCK_GUEST BIT(CIF_MCCK_GUEST) #define _CIF_MCCK_GUEST BIT(CIF_MCCK_GUEST)
#define _CIF_DEDICATED_CPU BIT(CIF_DEDICATED_CPU) #define _CIF_DEDICATED_CPU BIT(CIF_DEDICATED_CPU)
#define RESTART_FLAG_CTLREGS _AC(1 << 0, U)
#ifndef __ASSEMBLY__ #ifndef __ASSEMBLY__
#include <linux/cpumask.h> #include <linux/cpumask.h>
......
...@@ -116,6 +116,7 @@ int main(void) ...@@ -116,6 +116,7 @@ int main(void)
OFFSET(__LC_RESTART_FN, lowcore, restart_fn); OFFSET(__LC_RESTART_FN, lowcore, restart_fn);
OFFSET(__LC_RESTART_DATA, lowcore, restart_data); OFFSET(__LC_RESTART_DATA, lowcore, restart_data);
OFFSET(__LC_RESTART_SOURCE, lowcore, restart_source); OFFSET(__LC_RESTART_SOURCE, lowcore, restart_source);
OFFSET(__LC_RESTART_FLAGS, lowcore, restart_flags);
OFFSET(__LC_KERNEL_ASCE, lowcore, kernel_asce); OFFSET(__LC_KERNEL_ASCE, lowcore, kernel_asce);
OFFSET(__LC_USER_ASCE, lowcore, user_asce); OFFSET(__LC_USER_ASCE, lowcore, user_asce);
OFFSET(__LC_LPP, lowcore, lpp); OFFSET(__LC_LPP, lowcore, lpp);
......
...@@ -624,12 +624,15 @@ ENTRY(mcck_int_handler) ...@@ -624,12 +624,15 @@ ENTRY(mcck_int_handler)
4: j 4b 4: j 4b
ENDPROC(mcck_int_handler) ENDPROC(mcck_int_handler)
#
# PSW restart interrupt handler
#
ENTRY(restart_int_handler) ENTRY(restart_int_handler)
ALTERNATIVE "", ".insn s,0xb2800000,_LPP_OFFSET", 40 ALTERNATIVE "", ".insn s,0xb2800000,_LPP_OFFSET", 40
stg %r15,__LC_SAVE_AREA_RESTART stg %r15,__LC_SAVE_AREA_RESTART
TSTMSK __LC_RESTART_FLAGS,RESTART_FLAG_CTLREGS,4
jz 0f
la %r15,4095
lctlg %c0,%c15,__LC_CREGS_SAVE_AREA-4095(%r15)
0: larl %r15,.Lstosm_tmp
stosm 0(%r15),0x04 # turn dat on, keep irqs off
lg %r15,__LC_RESTART_STACK lg %r15,__LC_RESTART_STACK
xc STACK_FRAME_OVERHEAD(__PT_SIZE,%r15),STACK_FRAME_OVERHEAD(%r15) xc STACK_FRAME_OVERHEAD(__PT_SIZE,%r15),STACK_FRAME_OVERHEAD(%r15)
stmg %r0,%r14,STACK_FRAME_OVERHEAD+__PT_R0(%r15) stmg %r0,%r14,STACK_FRAME_OVERHEAD+__PT_R0(%r15)
...@@ -638,7 +641,7 @@ ENTRY(restart_int_handler) ...@@ -638,7 +641,7 @@ ENTRY(restart_int_handler)
xc 0(STACK_FRAME_OVERHEAD,%r15),0(%r15) xc 0(STACK_FRAME_OVERHEAD,%r15),0(%r15)
lg %r1,__LC_RESTART_FN # load fn, parm & source cpu lg %r1,__LC_RESTART_FN # load fn, parm & source cpu
lg %r2,__LC_RESTART_DATA lg %r2,__LC_RESTART_DATA
lg %r3,__LC_RESTART_SOURCE lgf %r3,__LC_RESTART_SOURCE
ltgr %r3,%r3 # test source cpu address ltgr %r3,%r3 # test source cpu address
jm 1f # negative -> skip source stop jm 1f # negative -> skip source stop
0: sigp %r4,%r3,SIGP_SENSE # sigp sense to source cpu 0: sigp %r4,%r3,SIGP_SENSE # sigp sense to source cpu
......
...@@ -179,8 +179,6 @@ static inline int __diag308(unsigned long subcode, void *addr) ...@@ -179,8 +179,6 @@ static inline int __diag308(unsigned long subcode, void *addr)
int diag308(unsigned long subcode, void *addr) int diag308(unsigned long subcode, void *addr)
{ {
if (IS_ENABLED(CONFIG_KASAN))
__arch_local_irq_stosm(0x04); /* enable DAT */
diag_stat_inc(DIAG_STAT_X308); diag_stat_inc(DIAG_STAT_X308);
return __diag308(subcode, addr); return __diag308(subcode, addr);
} }
...@@ -1843,7 +1841,6 @@ static struct kobj_attribute on_restart_attr = __ATTR_RW(on_restart); ...@@ -1843,7 +1841,6 @@ static struct kobj_attribute on_restart_attr = __ATTR_RW(on_restart);
static void __do_restart(void *ignore) static void __do_restart(void *ignore)
{ {
__arch_local_irq_stosm(0x04); /* enable DAT */
smp_send_stop(); smp_send_stop();
#ifdef CONFIG_CRASH_DUMP #ifdef CONFIG_CRASH_DUMP
crash_kexec(NULL); crash_kexec(NULL);
......
...@@ -263,7 +263,6 @@ static void __do_machine_kexec(void *data) ...@@ -263,7 +263,6 @@ static void __do_machine_kexec(void *data)
*/ */
static void __machine_kexec(void *data) static void __machine_kexec(void *data)
{ {
__arch_local_irq_stosm(0x04); /* enable DAT */
pfault_fini(); pfault_fini();
tracing_off(); tracing_off();
debug_locks_off(); debug_locks_off();
......
...@@ -465,7 +465,7 @@ static void __init setup_lowcore_dat_off(void) ...@@ -465,7 +465,7 @@ static void __init setup_lowcore_dat_off(void)
lc->restart_stack = (unsigned long) restart_stack; lc->restart_stack = (unsigned long) restart_stack;
lc->restart_fn = (unsigned long) do_restart; lc->restart_fn = (unsigned long) do_restart;
lc->restart_data = 0; lc->restart_data = 0;
lc->restart_source = -1UL; lc->restart_source = -1U;
mcck_stack = (unsigned long)memblock_alloc(THREAD_SIZE, THREAD_SIZE); mcck_stack = (unsigned long)memblock_alloc(THREAD_SIZE, THREAD_SIZE);
if (!mcck_stack) if (!mcck_stack)
...@@ -494,12 +494,19 @@ static void __init setup_lowcore_dat_off(void) ...@@ -494,12 +494,19 @@ static void __init setup_lowcore_dat_off(void)
static void __init setup_lowcore_dat_on(void) static void __init setup_lowcore_dat_on(void)
{ {
struct lowcore *lc = lowcore_ptr[0];
__ctl_clear_bit(0, 28); __ctl_clear_bit(0, 28);
S390_lowcore.external_new_psw.mask |= PSW_MASK_DAT; S390_lowcore.external_new_psw.mask |= PSW_MASK_DAT;
S390_lowcore.svc_new_psw.mask |= PSW_MASK_DAT; S390_lowcore.svc_new_psw.mask |= PSW_MASK_DAT;
S390_lowcore.program_new_psw.mask |= PSW_MASK_DAT; S390_lowcore.program_new_psw.mask |= PSW_MASK_DAT;
S390_lowcore.io_new_psw.mask |= PSW_MASK_DAT; S390_lowcore.io_new_psw.mask |= PSW_MASK_DAT;
__ctl_store(S390_lowcore.cregs_save_area, 0, 15);
__ctl_set_bit(0, 28); __ctl_set_bit(0, 28);
mem_assign_absolute(S390_lowcore.restart_flags, RESTART_FLAG_CTLREGS);
mem_assign_absolute(S390_lowcore.program_new_psw, lc->program_new_psw);
memcpy_absolute(&S390_lowcore.cregs_save_area, lc->cregs_save_area,
sizeof(S390_lowcore.cregs_save_area));
} }
static struct resource code_resource = { static struct resource code_resource = {
......
...@@ -252,6 +252,7 @@ static void pcpu_prepare_secondary(struct pcpu *pcpu, int cpu) ...@@ -252,6 +252,7 @@ static void pcpu_prepare_secondary(struct pcpu *pcpu, int cpu)
cpumask_set_cpu(cpu, &init_mm.context.cpu_attach_mask); cpumask_set_cpu(cpu, &init_mm.context.cpu_attach_mask);
cpumask_set_cpu(cpu, mm_cpumask(&init_mm)); cpumask_set_cpu(cpu, mm_cpumask(&init_mm));
lc->cpu_nr = cpu; lc->cpu_nr = cpu;
lc->restart_flags = RESTART_FLAG_CTLREGS;
lc->spinlock_lockval = arch_spin_lockval(cpu); lc->spinlock_lockval = arch_spin_lockval(cpu);
lc->spinlock_index = 0; lc->spinlock_index = 0;
lc->percpu_offset = __per_cpu_offset[cpu]; lc->percpu_offset = __per_cpu_offset[cpu];
...@@ -297,7 +298,7 @@ static void pcpu_start_fn(struct pcpu *pcpu, void (*func)(void *), void *data) ...@@ -297,7 +298,7 @@ static void pcpu_start_fn(struct pcpu *pcpu, void (*func)(void *), void *data)
lc->restart_stack = lc->nodat_stack; lc->restart_stack = lc->nodat_stack;
lc->restart_fn = (unsigned long) func; lc->restart_fn = (unsigned long) func;
lc->restart_data = (unsigned long) data; lc->restart_data = (unsigned long) data;
lc->restart_source = -1UL; lc->restart_source = -1U;
pcpu_sigp_retry(pcpu, SIGP_RESTART, 0); pcpu_sigp_retry(pcpu, SIGP_RESTART, 0);
} }
...@@ -311,12 +312,12 @@ static void __pcpu_delegate(pcpu_delegate_fn *func, void *data) ...@@ -311,12 +312,12 @@ static void __pcpu_delegate(pcpu_delegate_fn *func, void *data)
func(data); /* should not return */ func(data); /* should not return */
} }
static void __no_sanitize_address pcpu_delegate(struct pcpu *pcpu, static void pcpu_delegate(struct pcpu *pcpu,
pcpu_delegate_fn *func, pcpu_delegate_fn *func,
void *data, unsigned long stack) void *data, unsigned long stack)
{ {
struct lowcore *lc = lowcore_ptr[pcpu - pcpu_devices]; struct lowcore *lc = lowcore_ptr[pcpu - pcpu_devices];
unsigned long source_cpu = stap(); unsigned int source_cpu = stap();
__load_psw_mask(PSW_KERNEL_BITS | PSW_MASK_DAT); __load_psw_mask(PSW_KERNEL_BITS | PSW_MASK_DAT);
if (pcpu->address == source_cpu) { if (pcpu->address == source_cpu) {
...@@ -569,6 +570,9 @@ static void smp_ctl_bit_callback(void *info) ...@@ -569,6 +570,9 @@ static void smp_ctl_bit_callback(void *info)
__ctl_load(cregs, 0, 15); __ctl_load(cregs, 0, 15);
} }
static DEFINE_SPINLOCK(ctl_lock);
static unsigned long ctlreg;
/* /*
* Set a bit in a control register of all cpus * Set a bit in a control register of all cpus
*/ */
...@@ -576,6 +580,11 @@ void smp_ctl_set_bit(int cr, int bit) ...@@ -576,6 +580,11 @@ void smp_ctl_set_bit(int cr, int bit)
{ {
struct ec_creg_mask_parms parms = { 1UL << bit, -1UL, cr }; struct ec_creg_mask_parms parms = { 1UL << bit, -1UL, cr };
spin_lock(&ctl_lock);
memcpy_absolute(&ctlreg, &S390_lowcore.cregs_save_area[cr], sizeof(ctlreg));
__set_bit(bit, &ctlreg);
memcpy_absolute(&S390_lowcore.cregs_save_area[cr], &ctlreg, sizeof(ctlreg));
spin_unlock(&ctl_lock);
on_each_cpu(smp_ctl_bit_callback, &parms, 1); on_each_cpu(smp_ctl_bit_callback, &parms, 1);
} }
EXPORT_SYMBOL(smp_ctl_set_bit); EXPORT_SYMBOL(smp_ctl_set_bit);
...@@ -587,6 +596,11 @@ void smp_ctl_clear_bit(int cr, int bit) ...@@ -587,6 +596,11 @@ void smp_ctl_clear_bit(int cr, int bit)
{ {
struct ec_creg_mask_parms parms = { 0, ~(1UL << bit), cr }; struct ec_creg_mask_parms parms = { 0, ~(1UL << bit), cr };
spin_lock(&ctl_lock);
memcpy_absolute(&ctlreg, &S390_lowcore.cregs_save_area[cr], sizeof(ctlreg));
__clear_bit(bit, &ctlreg);
memcpy_absolute(&S390_lowcore.cregs_save_area[cr], &ctlreg, sizeof(ctlreg));
spin_unlock(&ctl_lock);
on_each_cpu(smp_ctl_bit_callback, &parms, 1); on_each_cpu(smp_ctl_bit_callback, &parms, 1);
} }
EXPORT_SYMBOL(smp_ctl_clear_bit); EXPORT_SYMBOL(smp_ctl_clear_bit);
...@@ -895,14 +909,13 @@ static void smp_init_secondary(void) ...@@ -895,14 +909,13 @@ static void smp_init_secondary(void)
/* /*
* Activate a secondary processor. * Activate a secondary processor.
*/ */
static void __no_sanitize_address smp_start_secondary(void *cpuvoid) static void smp_start_secondary(void *cpuvoid)
{ {
S390_lowcore.restart_stack = (unsigned long) restart_stack; S390_lowcore.restart_stack = (unsigned long) restart_stack;
S390_lowcore.restart_fn = (unsigned long) do_restart; S390_lowcore.restart_fn = (unsigned long) do_restart;
S390_lowcore.restart_data = 0; S390_lowcore.restart_data = 0;
S390_lowcore.restart_source = -1UL; S390_lowcore.restart_source = -1U;
__ctl_load(S390_lowcore.cregs_save_area, 0, 15); S390_lowcore.restart_flags = 0;
__load_psw_mask(PSW_KERNEL_BITS | PSW_MASK_DAT);
call_on_stack_noreturn(smp_init_secondary, S390_lowcore.kernel_stack); call_on_stack_noreturn(smp_init_secondary, S390_lowcore.kernel_stack);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment