Skip to content

Commit a43c159

Browse files
maheshsalmpe
authored andcommitted
powerpc/pseries: Flush SLB contents on SLB MCE errors.
On pseries, as of today system crashes if we get a machine check exceptions due to SLB errors. These are soft errors and can be fixed by flushing the SLBs so the kernel can continue to function instead of system crash. We do this in real mode before turning on MMU. Otherwise we would run into nested machine checks. This patch now fetches the rtas error log in real mode and flushes the SLBs on SLB/ERAT errors. Signed-off-by: Mahesh Salgaonkar <[email protected]> Signed-off-by: Michal Suchanek <[email protected]> Reviewed-by: Nicholas Piggin <[email protected]> Signed-off-by: Michael Ellerman <[email protected]>
1 parent 04fce21 commit a43c159

File tree

10 files changed

+213
-6
lines changed

10 files changed

+213
-6
lines changed

arch/powerpc/include/asm/machdep.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ struct machdep_calls {
108108

109109
/* Early exception handlers called in realmode */
110110
int (*hmi_exception_early)(struct pt_regs *regs);
111+
long (*machine_check_early)(struct pt_regs *regs);
111112

112113
/* Called during machine check exception to retrive fixup address. */
113114
bool (*mce_check_early_recovery)(struct pt_regs *regs);

arch/powerpc/include/asm/mce.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,4 +210,7 @@ extern void release_mce_event(void);
210210
extern void machine_check_queue_event(void);
211211
extern void machine_check_print_event_info(struct machine_check_event *evt,
212212
bool user_mode);
213+
#ifdef CONFIG_PPC_BOOK3S_64
214+
void flush_and_reload_slb(void);
215+
#endif /* CONFIG_PPC_BOOK3S_64 */
213216
#endif /* __ASM_PPC64_MCE_H__ */

arch/powerpc/kernel/exceptions-64s.S

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,9 @@ TRAMP_REAL_BEGIN(machine_check_pSeries)
331331
machine_check_fwnmi:
332332
SET_SCRATCH0(r13) /* save r13 */
333333
EXCEPTION_PROLOG_0(PACA_EXMC)
334+
BEGIN_FTR_SECTION
335+
b machine_check_pSeries_early
336+
END_FTR_SECTION_IFCLR(CPU_FTR_HVMODE)
334337
machine_check_pSeries_0:
335338
EXCEPTION_PROLOG_1(PACA_EXMC, KVMTEST_PR, 0x200)
336339
/*
@@ -342,6 +345,103 @@ machine_check_pSeries_0:
342345

343346
TRAMP_KVM_SKIP(PACA_EXMC, 0x200)
344347

348+
TRAMP_REAL_BEGIN(machine_check_pSeries_early)
349+
BEGIN_FTR_SECTION
350+
EXCEPTION_PROLOG_1(PACA_EXMC, NOTEST, 0x200)
351+
mr r10,r1 /* Save r1 */
352+
lhz r11,PACA_IN_MCE(r13)
353+
cmpwi r11,0 /* Are we in nested machine check */
354+
bne 0f /* Yes, we are. */
355+
/* First machine check entry */
356+
ld r1,PACAMCEMERGSP(r13) /* Use MC emergency stack */
357+
0: subi r1,r1,INT_FRAME_SIZE /* alloc stack frame */
358+
addi r11,r11,1 /* increment paca->in_mce */
359+
sth r11,PACA_IN_MCE(r13)
360+
/* Limit nested MCE to level 4 to avoid stack overflow */
361+
cmpwi r11,MAX_MCE_DEPTH
362+
bgt 1f /* Check if we hit limit of 4 */
363+
mfspr r11,SPRN_SRR0 /* Save SRR0 */
364+
mfspr r12,SPRN_SRR1 /* Save SRR1 */
365+
EXCEPTION_PROLOG_COMMON_1()
366+
EXCEPTION_PROLOG_COMMON_2(PACA_EXMC)
367+
EXCEPTION_PROLOG_COMMON_3(0x200)
368+
addi r3,r1,STACK_FRAME_OVERHEAD
369+
BRANCH_LINK_TO_FAR(machine_check_early) /* Function call ABI */
370+
ld r12,_MSR(r1)
371+
andi. r11,r12,MSR_PR /* See if coming from user. */
372+
bne 2f /* continue in V mode if we are. */
373+
374+
/*
375+
* At this point we are not sure about what context we come from.
376+
* We may be in the middle of switching stack. r1 may not be valid.
377+
* Hence stay on emergency stack, call machine_check_exception and
378+
* return from the interrupt.
379+
* But before that, check if this is an un-recoverable exception.
380+
* If yes, then stay on emergency stack and panic.
381+
*/
382+
andi. r11,r12,MSR_RI
383+
beq 1f
384+
385+
/*
386+
* Check if we have successfully handled/recovered from error, if not
387+
* then stay on emergency stack and panic.
388+
*/
389+
cmpdi r3,0 /* see if we handled MCE successfully */
390+
beq 1f /* if !handled then panic */
391+
392+
/* Stay on emergency stack and return from interrupt. */
393+
LOAD_HANDLER(r10,mce_return)
394+
mtspr SPRN_SRR0,r10
395+
ld r10,PACAKMSR(r13)
396+
mtspr SPRN_SRR1,r10
397+
RFI_TO_KERNEL
398+
b .
399+
400+
1: LOAD_HANDLER(r10,unrecover_mce)
401+
mtspr SPRN_SRR0,r10
402+
ld r10,PACAKMSR(r13)
403+
/*
404+
* We are going down. But there are chances that we might get hit by
405+
* another MCE during panic path and we may run into unstable state
406+
* with no way out. Hence, turn ME bit off while going down, so that
407+
* when another MCE is hit during panic path, hypervisor will
408+
* power cycle the lpar, instead of getting into MCE loop.
409+
*/
410+
li r3,MSR_ME
411+
andc r10,r10,r3 /* Turn off MSR_ME */
412+
mtspr SPRN_SRR1,r10
413+
RFI_TO_KERNEL
414+
b .
415+
416+
/* Move original SRR0 and SRR1 into the respective regs */
417+
2: ld r9,_MSR(r1)
418+
mtspr SPRN_SRR1,r9
419+
ld r3,_NIP(r1)
420+
mtspr SPRN_SRR0,r3
421+
ld r9,_CTR(r1)
422+
mtctr r9
423+
ld r9,_XER(r1)
424+
mtxer r9
425+
ld r9,_LINK(r1)
426+
mtlr r9
427+
REST_GPR(0, r1)
428+
REST_8GPRS(2, r1)
429+
REST_GPR(10, r1)
430+
ld r11,_CCR(r1)
431+
mtcr r11
432+
/* Decrement paca->in_mce. */
433+
lhz r12,PACA_IN_MCE(r13)
434+
subi r12,r12,1
435+
sth r12,PACA_IN_MCE(r13)
436+
REST_GPR(11, r1)
437+
REST_2GPRS(12, r1)
438+
/* restore original r1. */
439+
ld r1,GPR1(r1)
440+
SET_SCRATCH0(r13) /* save r13 */
441+
EXCEPTION_PROLOG_0(PACA_EXMC)
442+
b machine_check_pSeries_0
443+
END_FTR_SECTION_IFCLR(CPU_FTR_HVMODE)
444+
345445
EXC_COMMON_BEGIN(machine_check_common)
346446
/*
347447
* Machine check is different because we use a different
@@ -535,6 +635,35 @@ EXC_COMMON_BEGIN(unrecover_mce)
535635
bl unrecoverable_exception
536636
b 1b
537637

638+
EXC_COMMON_BEGIN(mce_return)
639+
/* Invoke machine_check_exception to print MCE event and return. */
640+
addi r3,r1,STACK_FRAME_OVERHEAD
641+
bl machine_check_exception
642+
ld r9,_MSR(r1)
643+
mtspr SPRN_SRR1,r9
644+
ld r3,_NIP(r1)
645+
mtspr SPRN_SRR0,r3
646+
ld r9,_CTR(r1)
647+
mtctr r9
648+
ld r9,_XER(r1)
649+
mtxer r9
650+
ld r9,_LINK(r1)
651+
mtlr r9
652+
REST_GPR(0, r1)
653+
REST_8GPRS(2, r1)
654+
REST_GPR(10, r1)
655+
ld r11,_CCR(r1)
656+
mtcr r11
657+
/* Decrement paca->in_mce. */
658+
lhz r12,PACA_IN_MCE(r13)
659+
subi r12,r12,1
660+
sth r12,PACA_IN_MCE(r13)
661+
REST_GPR(11, r1)
662+
REST_2GPRS(12, r1)
663+
/* restore original r1. */
664+
ld r1,GPR1(r1)
665+
RFI_TO_KERNEL
666+
b .
538667

539668
EXC_REAL(data_access, 0x300, 0x80)
540669
EXC_VIRT(data_access, 0x4300, 0x80, 0x300)

arch/powerpc/kernel/mce.c

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -488,10 +488,11 @@ long machine_check_early(struct pt_regs *regs)
488488
{
489489
long handled = 0;
490490

491-
__this_cpu_inc(irq_stat.mce_exceptions);
492-
493-
if (cur_cpu_spec && cur_cpu_spec->machine_check_early)
494-
handled = cur_cpu_spec->machine_check_early(regs);
491+
/*
492+
* See if platform is capable of handling machine check.
493+
*/
494+
if (ppc_md.machine_check_early)
495+
handled = ppc_md.machine_check_early(regs);
495496
return handled;
496497
}
497498

arch/powerpc/kernel/mce_power.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ static unsigned long addr_to_pfn(struct pt_regs *regs, unsigned long addr)
6060

6161
/* flush SLBs and reload */
6262
#ifdef CONFIG_PPC_BOOK3S_64
63-
static void flush_and_reload_slb(void)
63+
void flush_and_reload_slb(void)
6464
{
6565
/* Invalidate all SLBs */
6666
slb_flush_all_realmode();

arch/powerpc/platforms/powernv/opal.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -578,6 +578,8 @@ int opal_machine_check(struct pt_regs *regs)
578578
{
579579
struct machine_check_event evt;
580580

581+
__this_cpu_inc(irq_stat.mce_exceptions);
582+
581583
if (!get_mce_event(&evt, MCE_EVENT_RELEASE))
582584
return 0;
583585

arch/powerpc/platforms/powernv/setup.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -437,6 +437,16 @@ static unsigned long pnv_get_proc_freq(unsigned int cpu)
437437
return ret_freq;
438438
}
439439

440+
static long pnv_machine_check_early(struct pt_regs *regs)
441+
{
442+
long handled = 0;
443+
444+
if (cur_cpu_spec && cur_cpu_spec->machine_check_early)
445+
handled = cur_cpu_spec->machine_check_early(regs);
446+
447+
return handled;
448+
}
449+
440450
define_machine(powernv) {
441451
.name = "PowerNV",
442452
.probe = pnv_probe,
@@ -448,6 +458,7 @@ define_machine(powernv) {
448458
.machine_shutdown = pnv_shutdown,
449459
.power_save = NULL,
450460
.calibrate_decr = generic_calibrate_decr,
461+
.machine_check_early = pnv_machine_check_early,
451462
#ifdef CONFIG_KEXEC_CORE
452463
.kexec_cpu_down = pnv_kexec_cpu_down,
453464
#endif

arch/powerpc/platforms/pseries/pseries.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ struct pt_regs;
2424

2525
extern int pSeries_system_reset_exception(struct pt_regs *regs);
2626
extern int pSeries_machine_check_exception(struct pt_regs *regs);
27+
extern long pseries_machine_check_realmode(struct pt_regs *regs);
2728

2829
#ifdef CONFIG_SMP
2930
extern void smp_init_pseries(void);

arch/powerpc/platforms/pseries/ras.c

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include <asm/machdep.h>
2828
#include <asm/rtas.h>
2929
#include <asm/firmware.h>
30+
#include <asm/mce.h>
3031

3132
#include "pseries.h"
3233

@@ -522,6 +523,43 @@ int pSeries_system_reset_exception(struct pt_regs *regs)
522523
return 0; /* need to perform reset */
523524
}
524525

526+
static int mce_handle_error(struct rtas_error_log *errp)
527+
{
528+
struct pseries_errorlog *pseries_log;
529+
struct pseries_mc_errorlog *mce_log;
530+
int disposition = rtas_error_disposition(errp);
531+
u8 error_type;
532+
533+
if (!rtas_error_extended(errp))
534+
goto out;
535+
536+
pseries_log = get_pseries_errorlog(errp, PSERIES_ELOG_SECT_ID_MCE);
537+
if (pseries_log == NULL)
538+
goto out;
539+
540+
mce_log = (struct pseries_mc_errorlog *)pseries_log->data;
541+
error_type = mce_log->error_type;
542+
543+
#ifdef CONFIG_PPC_BOOK3S_64
544+
if (disposition == RTAS_DISP_NOT_RECOVERED) {
545+
switch (error_type) {
546+
case MC_ERROR_TYPE_SLB:
547+
case MC_ERROR_TYPE_ERAT:
548+
/* Store the old slb content someplace. */
549+
flush_and_reload_slb();
550+
disposition = RTAS_DISP_FULLY_RECOVERED;
551+
rtas_set_disposition_recovered(errp);
552+
break;
553+
default:
554+
break;
555+
}
556+
}
557+
#endif
558+
559+
out:
560+
return disposition;
561+
}
562+
525563
/*
526564
* Process MCE rtas errlog event.
527565
*/
@@ -598,11 +636,31 @@ int pSeries_machine_check_exception(struct pt_regs *regs)
598636
struct rtas_error_log *errp;
599637

600638
if (fwnmi_active) {
601-
errp = fwnmi_get_errinfo(regs);
602639
fwnmi_release_errinfo();
640+
errp = fwnmi_get_errlog();
603641
if (errp && recover_mce(regs, errp))
604642
return 1;
605643
}
606644

607645
return 0;
608646
}
647+
648+
long pseries_machine_check_realmode(struct pt_regs *regs)
649+
{
650+
struct rtas_error_log *errp;
651+
int disposition;
652+
653+
if (fwnmi_active) {
654+
errp = fwnmi_get_errinfo(regs);
655+
/*
656+
* Call to fwnmi_release_errinfo() in real mode causes kernel
657+
* to panic. Hence we will call it as soon as we go into
658+
* virtual mode.
659+
*/
660+
disposition = mce_handle_error(errp);
661+
if (disposition == RTAS_DISP_FULLY_RECOVERED)
662+
return 1;
663+
}
664+
665+
return 0;
666+
}

arch/powerpc/platforms/pseries/setup.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1017,6 +1017,7 @@ define_machine(pseries) {
10171017
.calibrate_decr = generic_calibrate_decr,
10181018
.progress = rtas_progress,
10191019
.system_reset_exception = pSeries_system_reset_exception,
1020+
.machine_check_early = pseries_machine_check_realmode,
10201021
.machine_check_exception = pSeries_machine_check_exception,
10211022
#ifdef CONFIG_KEXEC_CORE
10221023
.machine_kexec = pSeries_machine_kexec,

0 commit comments

Comments
 (0)