Add CONFIG_IRQFLAGS_DEBUG checks for local_irq_disable() when irqs are already disabled, and local_irq_enable() when they are enabled. This could help catch risky or unbalanced irq manipulation. Signed-off-by: Nicholas Piggin <npiggin@xxxxxxxxx> --- include/linux/irqflags.h | 26 ++++++++++++++++++++++++-- kernel/locking/irqflag-debug.c | 14 ++++++++++++++ 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/include/linux/irqflags.h b/include/linux/irqflags.h index 5ec0fa71399e..82f54cda2c20 100644 --- a/include/linux/irqflags.h +++ b/include/linux/irqflags.h @@ -157,6 +157,18 @@ do { \ #endif #ifdef CONFIG_DEBUG_IRQFLAGS +extern void warn_bogus_irq_disable(void); +#define raw_check_bogus_irq_disable() \ + do { \ + if (unlikely(arch_irqs_disabled())) \ + warn_bogus_irq_disable(); \ + } while (0) +extern void warn_bogus_irq_enable(void); +#define raw_check_bogus_irq_enable() \ + do { \ + if (unlikely(!arch_irqs_disabled())) \ + warn_bogus_irq_enable(); \ + } while (0) extern void warn_bogus_irq_restore(void); #define raw_check_bogus_irq_restore() \ do { \ @@ -164,14 +176,24 @@ extern void warn_bogus_irq_restore(void); warn_bogus_irq_restore(); \ } while (0) #else +#define raw_check_bogus_irq_disable() do { } while (0) +#define raw_check_bogus_irq_enable() do { } while (0) #define raw_check_bogus_irq_restore() do { } while (0) #endif /* * Wrap the arch provided IRQ routines to provide appropriate checks. */ -#define raw_local_irq_disable() arch_local_irq_disable() -#define raw_local_irq_enable() arch_local_irq_enable() +#define raw_local_irq_disable() \ + do { \ + raw_check_bogus_irq_disable(); \ + arch_local_irq_disable(); \ + } while (0) +#define raw_local_irq_enable() \ + do { \ + raw_check_bogus_irq_enable(); \ + arch_local_irq_enable(); \ + } while (0) #define raw_local_irq_save(flags) \ do { \ typecheck(unsigned long, flags); \ diff --git a/kernel/locking/irqflag-debug.c b/kernel/locking/irqflag-debug.c index 810b50344d35..20e5e1b9a86f 100644 --- a/kernel/locking/irqflag-debug.c +++ b/kernel/locking/irqflag-debug.c @@ -4,6 +4,20 @@ #include <linux/export.h> #include <linux/irqflags.h> +noinstr void warn_bogus_irq_disable(void) +{ + instrumentation_begin(); + WARN_ONCE(1, "raw_local_irq_disable() called with IRQs disabled\n"); + instrumentation_end(); +} +EXPORT_SYMBOL(warn_bogus_irq_disable); +noinstr void warn_bogus_irq_enable(void) +{ + instrumentation_begin(); + WARN_ONCE(1, "raw_local_irq_enable() called with IRQs enabled\n"); + instrumentation_end(); +} +EXPORT_SYMBOL(warn_bogus_irq_enable); noinstr void warn_bogus_irq_restore(void) { instrumentation_begin(); -- 2.40.1