Skip to content

Commit

Permalink
s390/nmi: allocation of the extended save area
Browse files Browse the repository at this point in the history
The machine check extended save area is needed to store the vector
registers and the guarded storage control block when a CPU is
interrupted by a machine check.

Move the slab cache allocation of the full save area to nmi.c,
for early boot use a static __initdata block.

Reviewed-by: Heiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
  • Loading branch information
Martin Schwidefsky committed Oct 19, 2017
1 parent cc65450 commit 6c81511
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 47 deletions.
10 changes: 8 additions & 2 deletions arch/s390/include/asm/nmi.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ union mci {

#define MCESA_ORIGIN_MASK (~0x3ffUL)
#define MCESA_LC_MASK (0xfUL)
#define MCESA_MIN_SIZE (1024)
#define MCESA_MAX_SIZE (2048)

struct mcesa {
u8 vector_save_area[1024];
Expand All @@ -88,8 +90,12 @@ struct mcesa {

struct pt_regs;

extern void s390_handle_mcck(void);
extern void s390_do_machine_check(struct pt_regs *regs);
void nmi_alloc_boot_cpu(struct lowcore *lc);
int nmi_alloc_per_cpu(struct lowcore *lc);
void nmi_free_per_cpu(struct lowcore *lc);

void s390_handle_mcck(void);
void s390_do_machine_check(struct pt_regs *regs);

#endif /* __ASSEMBLY__ */
#endif /* _ASM_S390_NMI_H */
82 changes: 82 additions & 0 deletions arch/s390/kernel/nmi.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/hardirq.h>
#include <linux/log2.h>
#include <linux/kprobes.h>
#include <linux/slab.h>
#include <linux/time.h>
#include <linux/module.h>
#include <linux/sched/signal.h>
Expand All @@ -38,6 +40,86 @@ struct mcck_struct {
};

static DEFINE_PER_CPU(struct mcck_struct, cpu_mcck);
static struct kmem_cache *mcesa_cache;
static unsigned long mcesa_origin_lc;

static inline int nmi_needs_mcesa(void)
{
return MACHINE_HAS_VX || MACHINE_HAS_GS;
}

static inline unsigned long nmi_get_mcesa_size(void)
{
if (MACHINE_HAS_GS)
return MCESA_MAX_SIZE;
return MCESA_MIN_SIZE;
}

/*
* The initial machine check extended save area for the boot CPU.
* It will be replaced by nmi_init() with an allocated structure.
* The structure is required for machine check happening early in
* the boot process.
*/
static struct mcesa boot_mcesa __initdata __aligned(MCESA_MAX_SIZE);

void __init nmi_alloc_boot_cpu(struct lowcore *lc)
{
if (!nmi_needs_mcesa())
return;
lc->mcesad = (unsigned long) &boot_mcesa;
if (MACHINE_HAS_GS)
lc->mcesad |= ilog2(MCESA_MAX_SIZE);
}

static int __init nmi_init(void)
{
unsigned long origin, cr0, size;

if (!nmi_needs_mcesa())
return 0;
size = nmi_get_mcesa_size();
if (size > MCESA_MIN_SIZE)
mcesa_origin_lc = ilog2(size);
/* create slab cache for the machine-check-extended-save-areas */
mcesa_cache = kmem_cache_create("nmi_save_areas", size, size, 0, NULL);
if (!mcesa_cache)
panic("Couldn't create nmi save area cache");
origin = (unsigned long) kmem_cache_alloc(mcesa_cache, GFP_KERNEL);
if (!origin)
panic("Couldn't allocate nmi save area");
/* The pointer is stored with mcesa_bits ORed in */
kmemleak_not_leak((void *) origin);
__ctl_store(cr0, 0, 0);
__ctl_clear_bit(0, 28); /* disable lowcore protection */
/* Replace boot_mcesa on the boot CPU */
S390_lowcore.mcesad = origin | mcesa_origin_lc;
__ctl_load(cr0, 0, 0);
return 0;
}
early_initcall(nmi_init);

int nmi_alloc_per_cpu(struct lowcore *lc)
{
unsigned long origin;

if (!nmi_needs_mcesa())
return 0;
origin = (unsigned long) kmem_cache_alloc(mcesa_cache, GFP_KERNEL);
if (!origin)
return -ENOMEM;
/* The pointer is stored with mcesa_bits ORed in */
kmemleak_not_leak((void *) origin);
lc->mcesad = origin | mcesa_origin_lc;
return 0;
}

void nmi_free_per_cpu(struct lowcore *lc)
{
if (!nmi_needs_mcesa())
return;
kmem_cache_free(mcesa_cache, (void *)(lc->mcesad & MCESA_ORIGIN_MASK));
}

static notrace void s390_handle_damage(void)
{
Expand Down
11 changes: 2 additions & 9 deletions arch/s390/kernel/setup.c
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
#include <asm/mmu_context.h>
#include <asm/cpcmd.h>
#include <asm/lowcore.h>
#include <asm/nmi.h>
#include <asm/irq.h>
#include <asm/page.h>
#include <asm/ptrace.h>
Expand Down Expand Up @@ -340,15 +341,7 @@ static void __init setup_lowcore(void)
lc->stfl_fac_list = S390_lowcore.stfl_fac_list;
memcpy(lc->stfle_fac_list, S390_lowcore.stfle_fac_list,
MAX_FACILITY_BIT/8);
if (MACHINE_HAS_VX || MACHINE_HAS_GS) {
unsigned long bits, size;

bits = MACHINE_HAS_GS ? 11 : 10;
size = 1UL << bits;
lc->mcesad = (__u64) memblock_virt_alloc(size, size);
if (MACHINE_HAS_GS)
lc->mcesad |= bits;
}
nmi_alloc_boot_cpu(lc);
vdso_alloc_boot_cpu(lc);
lc->sync_enter_timer = S390_lowcore.sync_enter_timer;
lc->async_enter_timer = S390_lowcore.async_enter_timer;
Expand Down
43 changes: 7 additions & 36 deletions arch/s390/kernel/smp.c
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,6 @@ struct pcpu {
static u8 boot_core_type;
static struct pcpu pcpu_devices[NR_CPUS];

static struct kmem_cache *pcpu_mcesa_cache;

unsigned int smp_cpu_mt_shift;
EXPORT_SYMBOL(smp_cpu_mt_shift);

Expand Down Expand Up @@ -193,51 +191,39 @@ static void pcpu_ec_call(struct pcpu *pcpu, int ec_bit)
static int pcpu_alloc_lowcore(struct pcpu *pcpu, int cpu)
{
unsigned long async_stack, panic_stack;
unsigned long mcesa_origin, mcesa_bits;
struct lowcore *lc;

mcesa_origin = mcesa_bits = 0;
if (pcpu != &pcpu_devices[0]) {
pcpu->lowcore = (struct lowcore *)
__get_free_pages(GFP_KERNEL | GFP_DMA, LC_ORDER);
async_stack = __get_free_pages(GFP_KERNEL, ASYNC_ORDER);
panic_stack = __get_free_page(GFP_KERNEL);
if (!pcpu->lowcore || !panic_stack || !async_stack)
goto out;
if (MACHINE_HAS_VX || MACHINE_HAS_GS) {
mcesa_origin = (unsigned long)
kmem_cache_alloc(pcpu_mcesa_cache, GFP_KERNEL);
if (!mcesa_origin)
goto out;
/* The pointer is stored with mcesa_bits ORed in */
kmemleak_not_leak((void *) mcesa_origin);
mcesa_bits = MACHINE_HAS_GS ? 11 : 0;
}
} else {
async_stack = pcpu->lowcore->async_stack - ASYNC_FRAME_OFFSET;
panic_stack = pcpu->lowcore->panic_stack - PANIC_FRAME_OFFSET;
mcesa_origin = pcpu->lowcore->mcesad & MCESA_ORIGIN_MASK;
mcesa_bits = pcpu->lowcore->mcesad & MCESA_LC_MASK;
}
lc = pcpu->lowcore;
memcpy(lc, &S390_lowcore, 512);
memset((char *) lc + 512, 0, sizeof(*lc) - 512);
lc->async_stack = async_stack + ASYNC_FRAME_OFFSET;
lc->panic_stack = panic_stack + PANIC_FRAME_OFFSET;
lc->mcesad = mcesa_origin | mcesa_bits;
lc->cpu_nr = cpu;
lc->spinlock_lockval = arch_spin_lockval(cpu);
lc->spinlock_index = 0;
if (vdso_alloc_per_cpu(lc))
if (nmi_alloc_per_cpu(lc))
goto out;
if (vdso_alloc_per_cpu(lc))
goto out_mcesa;
lowcore_ptr[cpu] = lc;
pcpu_sigp_retry(pcpu, SIGP_SET_PREFIX, (u32)(unsigned long) lc);
return 0;

out_mcesa:
nmi_free_per_cpu(lc);
out:
if (pcpu != &pcpu_devices[0]) {
if (mcesa_origin)
kmem_cache_free(pcpu_mcesa_cache,
(void *) mcesa_origin);
free_page(panic_stack);
free_pages(async_stack, ASYNC_ORDER);
free_pages((unsigned long) pcpu->lowcore, LC_ORDER);
Expand All @@ -249,17 +235,12 @@ static int pcpu_alloc_lowcore(struct pcpu *pcpu, int cpu)

static void pcpu_free_lowcore(struct pcpu *pcpu)
{
unsigned long mcesa_origin;

pcpu_sigp_retry(pcpu, SIGP_SET_PREFIX, 0);
lowcore_ptr[pcpu - pcpu_devices] = NULL;
vdso_free_per_cpu(pcpu->lowcore);
nmi_free_per_cpu(pcpu->lowcore);
if (pcpu == &pcpu_devices[0])
return;
if (MACHINE_HAS_VX || MACHINE_HAS_GS) {
mcesa_origin = pcpu->lowcore->mcesad & MCESA_ORIGIN_MASK;
kmem_cache_free(pcpu_mcesa_cache, (void *) mcesa_origin);
}
free_page(pcpu->lowcore->panic_stack-PANIC_FRAME_OFFSET);
free_pages(pcpu->lowcore->async_stack-ASYNC_FRAME_OFFSET, ASYNC_ORDER);
free_pages((unsigned long) pcpu->lowcore, LC_ORDER);
Expand Down Expand Up @@ -936,22 +917,12 @@ void __init smp_fill_possible_mask(void)

void __init smp_prepare_cpus(unsigned int max_cpus)
{
unsigned long size;

/* request the 0x1201 emergency signal external interrupt */
if (register_external_irq(EXT_IRQ_EMERGENCY_SIG, do_ext_call_interrupt))
panic("Couldn't request external interrupt 0x1201");
/* request the 0x1202 external call external interrupt */
if (register_external_irq(EXT_IRQ_EXTERNAL_CALL, do_ext_call_interrupt))
panic("Couldn't request external interrupt 0x1202");
/* create slab cache for the machine-check-extended-save-areas */
if (MACHINE_HAS_VX || MACHINE_HAS_GS) {
size = 1UL << (MACHINE_HAS_GS ? 11 : 10);
pcpu_mcesa_cache = kmem_cache_create("nmi_save_areas",
size, size, 0, NULL);
if (!pcpu_mcesa_cache)
panic("Couldn't create nmi save area cache");
}
}

void __init smp_prepare_boot_cpu(void)
Expand Down

0 comments on commit 6c81511

Please sign in to comment.