Skip to content

Commit

Permalink
s390/smp: enable DAT before CPU restart callback is called
Browse files Browse the repository at this point in the history
[ Upstream commit 915fea0 ]

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: Heiko Carstens <hca@linux.ibm.com>
Signed-off-by: Alexander Gordeev <agordeev@linux.ibm.com>
Signed-off-by: Heiko Carstens <hca@linux.ibm.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
  • Loading branch information
Alexander Gordeev authored and gregkh committed Sep 15, 2021
1 parent 1409e62 commit ab6999a
Show file tree
Hide file tree
Showing 8 changed files with 42 additions and 19 deletions.
3 changes: 2 additions & 1 deletion arch/s390/include/asm/lowcore.h
Expand Up @@ -124,7 +124,8 @@ struct lowcore {
/* Restart function and parameter. */
__u64 restart_fn; /* 0x0370 */
__u64 restart_data; /* 0x0378 */
__u64 restart_source; /* 0x0380 */
__u32 restart_source; /* 0x0380 */
__u32 restart_flags; /* 0x0384 */

/* Address space pointer. */
__u64 kernel_asce; /* 0x0388 */
Expand Down
2 changes: 2 additions & 0 deletions arch/s390/include/asm/processor.h
Expand Up @@ -26,6 +26,8 @@
#define _CIF_MCCK_GUEST BIT(CIF_MCCK_GUEST)
#define _CIF_DEDICATED_CPU BIT(CIF_DEDICATED_CPU)

#define RESTART_FLAG_CTLREGS _AC(1 << 0, U)

#ifndef __ASSEMBLY__

#include <linux/cpumask.h>
Expand Down
1 change: 1 addition & 0 deletions arch/s390/kernel/asm-offsets.c
Expand Up @@ -116,6 +116,7 @@ int main(void)
OFFSET(__LC_RESTART_FN, lowcore, restart_fn);
OFFSET(__LC_RESTART_DATA, lowcore, restart_data);
OFFSET(__LC_RESTART_SOURCE, lowcore, restart_source);
OFFSET(__LC_RESTART_FLAGS, lowcore, restart_flags);
OFFSET(__LC_KERNEL_ASCE, lowcore, kernel_asce);
OFFSET(__LC_USER_ASCE, lowcore, user_asce);
OFFSET(__LC_LPP, lowcore, lpp);
Expand Down
11 changes: 7 additions & 4 deletions arch/s390/kernel/entry.S
Expand Up @@ -624,12 +624,15 @@ ENTRY(mcck_int_handler)
4: j 4b
ENDPROC(mcck_int_handler)

#
# PSW restart interrupt handler
#
ENTRY(restart_int_handler)
ALTERNATIVE "", ".insn s,0xb2800000,_LPP_OFFSET", 40
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
xc STACK_FRAME_OVERHEAD(__PT_SIZE,%r15),STACK_FRAME_OVERHEAD(%r15)
stmg %r0,%r14,STACK_FRAME_OVERHEAD+__PT_R0(%r15)
Expand All @@ -638,7 +641,7 @@ ENTRY(restart_int_handler)
xc 0(STACK_FRAME_OVERHEAD,%r15),0(%r15)
lg %r1,__LC_RESTART_FN # load fn, parm & source cpu
lg %r2,__LC_RESTART_DATA
lg %r3,__LC_RESTART_SOURCE
lgf %r3,__LC_RESTART_SOURCE
ltgr %r3,%r3 # test source cpu address
jm 1f # negative -> skip source stop
0: sigp %r4,%r3,SIGP_SENSE # sigp sense to source cpu
Expand Down
3 changes: 0 additions & 3 deletions arch/s390/kernel/ipl.c
Expand Up @@ -179,8 +179,6 @@ static inline 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);
return __diag308(subcode, addr);
}
Expand Down Expand Up @@ -1843,7 +1841,6 @@ static struct kobj_attribute on_restart_attr = __ATTR_RW(on_restart);

static void __do_restart(void *ignore)
{
__arch_local_irq_stosm(0x04); /* enable DAT */
smp_send_stop();
#ifdef CONFIG_CRASH_DUMP
crash_kexec(NULL);
Expand Down
1 change: 0 additions & 1 deletion arch/s390/kernel/machine_kexec.c
Expand Up @@ -263,7 +263,6 @@ static void __do_machine_kexec(void *data)
*/
static void __machine_kexec(void *data)
{
__arch_local_irq_stosm(0x04); /* enable DAT */
pfault_fini();
tracing_off();
debug_locks_off();
Expand Down
9 changes: 8 additions & 1 deletion arch/s390/kernel/setup.c
Expand Up @@ -421,7 +421,7 @@ static void __init setup_lowcore_dat_off(void)
lc->restart_stack = (unsigned long) restart_stack;
lc->restart_fn = (unsigned long) do_restart;
lc->restart_data = 0;
lc->restart_source = -1UL;
lc->restart_source = -1U;

mcck_stack = (unsigned long)memblock_alloc(THREAD_SIZE, THREAD_SIZE);
if (!mcck_stack)
Expand Down Expand Up @@ -450,12 +450,19 @@ static void __init setup_lowcore_dat_off(void)

static void __init setup_lowcore_dat_on(void)
{
struct lowcore *lc = lowcore_ptr[0];

__ctl_clear_bit(0, 28);
S390_lowcore.external_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.io_new_psw.mask |= PSW_MASK_DAT;
__ctl_store(S390_lowcore.cregs_save_area, 0, 15);
__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 = {
Expand Down
31 changes: 22 additions & 9 deletions arch/s390/kernel/smp.c
Expand Up @@ -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, mm_cpumask(&init_mm));
lc->cpu_nr = cpu;
lc->restart_flags = RESTART_FLAG_CTLREGS;
lc->spinlock_lockval = arch_spin_lockval(cpu);
lc->spinlock_index = 0;
lc->percpu_offset = __per_cpu_offset[cpu];
Expand Down Expand Up @@ -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_fn = (unsigned long) func;
lc->restart_data = (unsigned long) data;
lc->restart_source = -1UL;
lc->restart_source = -1U;
pcpu_sigp_retry(pcpu, SIGP_RESTART, 0);
}

Expand All @@ -311,12 +312,12 @@ static void __pcpu_delegate(pcpu_delegate_fn *func, void *data)
func(data); /* should not return */
}

static void __no_sanitize_address pcpu_delegate(struct pcpu *pcpu,
pcpu_delegate_fn *func,
void *data, unsigned long stack)
static void pcpu_delegate(struct pcpu *pcpu,
pcpu_delegate_fn *func,
void *data, unsigned long stack)
{
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);
if (pcpu->address == source_cpu) {
Expand Down Expand Up @@ -569,13 +570,21 @@ static void smp_ctl_bit_callback(void *info)
__ctl_load(cregs, 0, 15);
}

static DEFINE_SPINLOCK(ctl_lock);
static unsigned long ctlreg;

/*
* Set a bit in a control register of all cpus
*/
void smp_ctl_set_bit(int cr, int bit)
{
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);
}
EXPORT_SYMBOL(smp_ctl_set_bit);
Expand All @@ -587,6 +596,11 @@ void smp_ctl_clear_bit(int cr, int bit)
{
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);
}
EXPORT_SYMBOL(smp_ctl_clear_bit);
Expand Down Expand Up @@ -895,14 +909,13 @@ static void smp_init_secondary(void)
/*
* 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_fn = (unsigned long) do_restart;
S390_lowcore.restart_data = 0;
S390_lowcore.restart_source = -1UL;
__ctl_load(S390_lowcore.cregs_save_area, 0, 15);
__load_psw_mask(PSW_KERNEL_BITS | PSW_MASK_DAT);
S390_lowcore.restart_source = -1U;
S390_lowcore.restart_flags = 0;
call_on_stack_noreturn(smp_init_secondary, S390_lowcore.kernel_stack);
}

Expand Down

0 comments on commit ab6999a

Please sign in to comment.