Message ID | 20180921221705.6478-8-james.morse@arm.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | APEI in_nmi() rework | expand |
On Fri, Sep 21, 2018 at 11:16:54PM +0100, James Morse wrote: > To split up APEIs in_nmi() path, we need the nmi-like callers to always > be in_nmi(). Add a helper to do the work and claim the notification. > > When KVM or the arch code takes an exception that might be a RAS > notification, it asks the APEI firmware-first code whether it wants > to claim the exception. We can then go on to see if (a future) > kernel-first mechanism wants to claim the notification, before > falling through to the existing default behaviour. > > The NOTIFY_SEA code was merged before we had multiple, possibly > interacting, NMI-like notifications and the need to consider kernel > first in the future. Make the 'claiming' behaviour explicit. > > As we're restructuring the APEI code to allow multiple NMI-like > notifications, any notification that might interrupt interrupts-masked > code must always be wrapped in nmi_enter()/nmi_exit(). This allows APEI > to use in_nmi() to use the right fixmap entries. > > We mask SError over this window to prevent an asynchronous RAS error > arriving and tripping 'nmi_enter()'s BUG_ON(in_nmi()). > > Signed-off-by: James Morse <james.morse@arm.com> > Acked-by: Marc Zyngier <marc.zyngier@arm.com> > Tested-by: Tyler Baicar <tbaicar@codeaurora.org> ... > diff --git a/arch/arm64/kernel/acpi.c b/arch/arm64/kernel/acpi.c > index ed46dc188b22..a9b8bba014b5 100644 > --- a/arch/arm64/kernel/acpi.c > +++ b/arch/arm64/kernel/acpi.c > @@ -28,8 +28,10 @@ > #include <linux/smp.h> > #include <linux/serial_core.h> > > +#include <acpi/ghes.h> > #include <asm/cputype.h> > #include <asm/cpu_ops.h> > +#include <asm/daifflags.h> > #include <asm/pgtable.h> > #include <asm/smp_plat.h> > > @@ -257,3 +259,30 @@ pgprot_t __acpi_get_mem_attribute(phys_addr_t addr) > return __pgprot(PROT_NORMAL_NC); > return __pgprot(PROT_DEVICE_nGnRnE); > } > + > +/* > + * Claim Synchronous External Aborts as a firmware first notification. > + * > + * Used by KVM and the arch do_sea handler. > + * @regs may be NULL when called from process context. > + */ > +int apei_claim_sea(struct pt_regs *regs) > +{ > + int err = -ENOENT; > + unsigned long current_flags = arch_local_save_flags(); > + > + if (!IS_ENABLED(CONFIG_ACPI_APEI_SEA)) > + return err; I don't know what side effects arch_local_save_flags() has on ARM but if we return here, it looks to me like useless work.
Hi Boris, On 12/10/2018 11:02, Borislav Petkov wrote: > On Fri, Sep 21, 2018 at 11:16:54PM +0100, James Morse wrote: >> To split up APEIs in_nmi() path, we need the nmi-like callers to always >> be in_nmi(). Add a helper to do the work and claim the notification. >> >> When KVM or the arch code takes an exception that might be a RAS >> notification, it asks the APEI firmware-first code whether it wants >> to claim the exception. We can then go on to see if (a future) >> kernel-first mechanism wants to claim the notification, before >> falling through to the existing default behaviour. >> >> The NOTIFY_SEA code was merged before we had multiple, possibly >> interacting, NMI-like notifications and the need to consider kernel >> first in the future. Make the 'claiming' behaviour explicit. >> >> As we're restructuring the APEI code to allow multiple NMI-like >> notifications, any notification that might interrupt interrupts-masked >> code must always be wrapped in nmi_enter()/nmi_exit(). This allows APEI >> to use in_nmi() to use the right fixmap entries. >> >> We mask SError over this window to prevent an asynchronous RAS error >> arriving and tripping 'nmi_enter()'s BUG_ON(in_nmi()). >> diff --git a/arch/arm64/kernel/acpi.c b/arch/arm64/kernel/acpi.c >> index ed46dc188b22..a9b8bba014b5 100644 >> --- a/arch/arm64/kernel/acpi.c >> +++ b/arch/arm64/kernel/acpi.c >> @@ -257,3 +259,30 @@ pgprot_t __acpi_get_mem_attribute(phys_addr_t addr) >> return __pgprot(PROT_NORMAL_NC); >> return __pgprot(PROT_DEVICE_nGnRnE); >> } >> + >> +/* >> + * Claim Synchronous External Aborts as a firmware first notification. >> + * >> + * Used by KVM and the arch do_sea handler. >> + * @regs may be NULL when called from process context. >> + */ >> +int apei_claim_sea(struct pt_regs *regs) >> +{ >> + int err = -ENOENT; >> + unsigned long current_flags = arch_local_save_flags(); >> + >> + if (!IS_ENABLED(CONFIG_ACPI_APEI_SEA)) >> + return err; > > I don't know what side effects arch_local_save_flags() has on ARM but if It reads the current 'masked' state for IRQs, debug exceptions and 'SError'. > we return here, it looks to me like useless work. Yes. I lazily assume the compiler will rip that out as the value is never used. But in this case it can't, because its wrapped in asm-volatile, so it doesn't know it has no side-effects. I'll move it further down. Thanks! James
diff --git a/arch/arm64/include/asm/acpi.h b/arch/arm64/include/asm/acpi.h index 709208dfdc8b..f722d2d6bf2b 100644 --- a/arch/arm64/include/asm/acpi.h +++ b/arch/arm64/include/asm/acpi.h @@ -18,6 +18,7 @@ #include <asm/cputype.h> #include <asm/io.h> +#include <asm/ptrace.h> #include <asm/smp_plat.h> #include <asm/tlbflush.h> @@ -139,6 +140,9 @@ static inline pgprot_t arch_apei_get_mem_attribute(phys_addr_t addr) { return __acpi_get_mem_attribute(addr); } +int apei_claim_sea(struct pt_regs *regs); +#else +static inline int apei_claim_sea(struct pt_regs *regs) { return -ENOENT; } #endif /* CONFIG_ACPI_APEI */ #ifdef CONFIG_ACPI_NUMA diff --git a/arch/arm64/include/asm/daifflags.h b/arch/arm64/include/asm/daifflags.h index 22e4c83de5a5..cbd753855bf3 100644 --- a/arch/arm64/include/asm/daifflags.h +++ b/arch/arm64/include/asm/daifflags.h @@ -20,6 +20,7 @@ #define DAIF_PROCCTX 0 #define DAIF_PROCCTX_NOIRQ PSR_I_BIT +#define DAIF_ERRCTX (PSR_I_BIT | PSR_A_BIT) /* mask/save/unmask/restore all exceptions, including interrupts. */ static inline void local_daif_mask(void) diff --git a/arch/arm64/include/asm/kvm_ras.h b/arch/arm64/include/asm/kvm_ras.h index 5f72b07b7912..5b56e7e297b1 100644 --- a/arch/arm64/include/asm/kvm_ras.h +++ b/arch/arm64/include/asm/kvm_ras.h @@ -4,8 +4,22 @@ #ifndef __ARM64_KVM_RAS_H__ #define __ARM64_KVM_RAS_H__ +#include <linux/acpi.h> +#include <linux/errno.h> #include <linux/types.h> -int kvm_handle_guest_sea(phys_addr_t addr, unsigned int esr); +#include <asm/acpi.h> + +/* + * Was this synchronous external abort a RAS notification? + * Returns '0' for errors handled by some RAS subsystem, or -ENOENT. + */ +static inline int kvm_handle_guest_sea(phys_addr_t addr, unsigned int esr) +{ + /* apei_claim_sea(NULL) expects to mask interrupts itself */ + lockdep_assert_irqs_enabled(); + + return apei_claim_sea(NULL); +} #endif /* __ARM64_KVM_RAS_H__ */ diff --git a/arch/arm64/kernel/acpi.c b/arch/arm64/kernel/acpi.c index ed46dc188b22..a9b8bba014b5 100644 --- a/arch/arm64/kernel/acpi.c +++ b/arch/arm64/kernel/acpi.c @@ -28,8 +28,10 @@ #include <linux/smp.h> #include <linux/serial_core.h> +#include <acpi/ghes.h> #include <asm/cputype.h> #include <asm/cpu_ops.h> +#include <asm/daifflags.h> #include <asm/pgtable.h> #include <asm/smp_plat.h> @@ -257,3 +259,30 @@ pgprot_t __acpi_get_mem_attribute(phys_addr_t addr) return __pgprot(PROT_NORMAL_NC); return __pgprot(PROT_DEVICE_nGnRnE); } + +/* + * Claim Synchronous External Aborts as a firmware first notification. + * + * Used by KVM and the arch do_sea handler. + * @regs may be NULL when called from process context. + */ +int apei_claim_sea(struct pt_regs *regs) +{ + int err = -ENOENT; + unsigned long current_flags = arch_local_save_flags(); + + if (!IS_ENABLED(CONFIG_ACPI_APEI_SEA)) + return err; + + /* + * SEA can interrupt SError, mask it and describe this as an NMI so + * that APEI defers the handling. + */ + local_daif_restore(DAIF_ERRCTX); + nmi_enter(); + err = ghes_notify_sea(); + nmi_exit(); + local_daif_restore(current_flags); + + return err; +} diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c index 1a30d7a8c9bf..2c38776bb71f 100644 --- a/arch/arm64/mm/fault.c +++ b/arch/arm64/mm/fault.c @@ -18,6 +18,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <linux/acpi.h> #include <linux/extable.h> #include <linux/signal.h> #include <linux/mm.h> @@ -33,6 +34,7 @@ #include <linux/preempt.h> #include <linux/hugetlb.h> +#include <asm/acpi.h> #include <asm/bug.h> #include <asm/cmpxchg.h> #include <asm/cpufeature.h> @@ -45,8 +47,6 @@ #include <asm/tlbflush.h> #include <asm/traps.h> -#include <acpi/ghes.h> - struct fault_info { int (*fn)(unsigned long addr, unsigned int esr, struct pt_regs *regs); @@ -631,19 +631,10 @@ static int do_sea(unsigned long addr, unsigned int esr, struct pt_regs *regs) inf = esr_to_fault_info(esr); /* - * Synchronous aborts may interrupt code which had interrupts masked. - * Before calling out into the wider kernel tell the interested - * subsystems. + * Return value ignored as we rely on signal merging. + * Future patches will make this more robust. */ - if (IS_ENABLED(CONFIG_ACPI_APEI_SEA)) { - if (interrupts_enabled(regs)) - nmi_enter(); - - ghes_notify_sea(); - - if (interrupts_enabled(regs)) - nmi_exit(); - } + apei_claim_sea(regs); clear_siginfo(&info); info.si_signo = inf->sig; @@ -725,11 +716,6 @@ static const struct fault_info fault_info[] = { { do_bad, SIGKILL, SI_KERNEL, "unknown 63" }, }; -int kvm_handle_guest_sea(phys_addr_t addr, unsigned int esr) -{ - return ghes_notify_sea(); -} - asmlinkage void __exception do_mem_abort(unsigned long addr, unsigned int esr, struct pt_regs *regs) {