Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e4629194 authored by Max Filippov's avatar Max Filippov
Browse files

xtensa: make fake NMI configurable



Do not always use fake NMI when safe, provide Kconfig option instead.
Print a warning if fake NMI is chosen in unsafe configuration, but allow
it, because it may work if the user knows that interrupts with
priorities at or above PMM IRQ are not used. Add a check to NMI handler
that BUGs if any of these IRQs fire.

Signed-off-by: default avatarMax Filippov <jcmvbkbc@gmail.com>
parent afd2ff9b
Loading
Loading
Loading
Loading
+16 −0
Original line number Diff line number Diff line
@@ -139,6 +139,22 @@ config XTENSA_VARIANT_HAVE_PERF_EVENTS

	  If unsure, say N.

config XTENSA_FAKE_NMI
	bool "Treat PMM IRQ as NMI"
	depends on XTENSA_VARIANT_HAVE_PERF_EVENTS
	default n
	help
	  If PMM IRQ is the only IRQ at EXCM level it is safe to
	  treat it as NMI, which improves accuracy of profiling.

	  If there are other interrupts at or above PMM IRQ priority level
	  but not above the EXCM level, PMM IRQ still may be treated as NMI,
	  but only if these IRQs are not used. There will be a build warning
	  saying that this is not safe, and a bugcheck if one of these IRQs
	  actually fire.

	  If unsure, say N.

config XTENSA_UNALIGNED_USER
	bool "Unaligned memory access in use space"
	help
+5 −7
Original line number Diff line number Diff line
@@ -78,22 +78,20 @@
#define XTENSA_INTLEVEL_MASK(level) _XTENSA_INTLEVEL_MASK(level)
#define _XTENSA_INTLEVEL_MASK(level) (XCHAL_INTLEVEL##level##_MASK)

#define IS_POW2(v) (((v) & ((v) - 1)) == 0)
#define XTENSA_INTLEVEL_ANDBELOW_MASK(l) _XTENSA_INTLEVEL_ANDBELOW_MASK(l)
#define _XTENSA_INTLEVEL_ANDBELOW_MASK(l) (XCHAL_INTLEVEL##l##_ANDBELOW_MASK)

#define PROFILING_INTLEVEL XTENSA_INT_LEVEL(XCHAL_PROFILING_INTERRUPT)

/* LOCKLEVEL defines the interrupt level that masks all
 * general-purpose interrupts.
 */
#if defined(CONFIG_XTENSA_VARIANT_HAVE_PERF_EVENTS) && \
	defined(XCHAL_PROFILING_INTERRUPT) && \
	PROFILING_INTLEVEL == XCHAL_EXCM_LEVEL && \
	XCHAL_EXCM_LEVEL > 1 && \
	IS_POW2(XTENSA_INTLEVEL_MASK(PROFILING_INTLEVEL))
#define LOCKLEVEL (XCHAL_EXCM_LEVEL - 1)
#if defined(CONFIG_XTENSA_FAKE_NMI) && defined(XCHAL_PROFILING_INTERRUPT)
#define LOCKLEVEL (PROFILING_INTLEVEL - 1)
#else
#define LOCKLEVEL XCHAL_EXCM_LEVEL
#endif

#define TOPLEVEL XCHAL_EXCM_LEVEL
#define XTENSA_FAKE_NMI (LOCKLEVEL < TOPLEVEL)

+27 −0
Original line number Diff line number Diff line
@@ -205,6 +205,32 @@ extern void do_IRQ(int, struct pt_regs *);

#if XTENSA_FAKE_NMI

#define IS_POW2(v) (((v) & ((v) - 1)) == 0)

#if !(PROFILING_INTLEVEL == XCHAL_EXCM_LEVEL && \
      IS_POW2(XTENSA_INTLEVEL_MASK(PROFILING_INTLEVEL)))
#warning "Fake NMI is requested for PMM, but there are other IRQs at or above its level."
#warning "Fake NMI will be used, but there will be a bugcheck if one of those IRQs fire."

static inline void check_valid_nmi(void)
{
	unsigned intread = get_sr(interrupt);
	unsigned intenable = get_sr(intenable);

	BUG_ON(intread & intenable &
	       ~(XTENSA_INTLEVEL_ANDBELOW_MASK(PROFILING_INTLEVEL) ^
		 XTENSA_INTLEVEL_MASK(PROFILING_INTLEVEL) ^
		 BIT(XCHAL_PROFILING_INTERRUPT)));
}

#else

static inline void check_valid_nmi(void)
{
}

#endif

irqreturn_t xtensa_pmu_irq_handler(int irq, void *dev_id);

DEFINE_PER_CPU(unsigned long, nmi_count);
@@ -219,6 +245,7 @@ void do_nmi(struct pt_regs *regs)
	old_regs = set_irq_regs(regs);
	nmi_enter();
	++*this_cpu_ptr(&nmi_count);
	check_valid_nmi();
	xtensa_pmu_irq_handler(0, NULL);
	nmi_exit();
	set_irq_regs(old_regs);