Skip to content

Commit 997acaf

Browse files
Mark RutlandPeter Zijlstra
Mark Rutland
authored and
Peter Zijlstra
committed
lockdep: report broken irq restoration
We generally expect local_irq_save() and local_irq_restore() to be paired and sanely nested, and so local_irq_restore() expects to be called with irqs disabled. Thus, within local_irq_restore() we only trace irq flag changes when unmasking irqs. This means that a sequence such as: | local_irq_disable(); | local_irq_save(flags); | local_irq_enable(); | local_irq_restore(flags); ... is liable to break things, as the local_irq_restore() would mask irqs without tracing this change. Similar problems may exist for architectures whose arch_irq_restore() function depends on being called with irqs disabled. We don't consider such sequences to be a good idea, so let's define those as forbidden, and add tooling to detect such broken cases. This patch adds debug code to WARN() when raw_local_irq_restore() is called with irqs enabled. As raw_local_irq_restore() is expected to pair with raw_local_irq_save(), it should never be called with irqs enabled. To avoid the possibility of circular header dependencies between irqflags.h and bug.h, the warning is handled in a separate C file. The new code is all conditional on a new CONFIG_DEBUG_IRQFLAGS symbol which is independent of CONFIG_TRACE_IRQFLAGS. As noted above such cases will confuse lockdep, so CONFIG_DEBUG_LOCKDEP now selects CONFIG_DEBUG_IRQFLAGS. Signed-off-by: Mark Rutland <mark.rutland@arm.com> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Link: https://lkml.kernel.org/r/20210111153707.10071-1-mark.rutland@arm.com
1 parent 2f0df49 commit 997acaf

File tree

4 files changed

+32
-0
lines changed

4 files changed

+32
-0
lines changed

include/linux/irqflags.h

+12
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,17 @@ do { \
149149
# define start_critical_timings() do { } while (0)
150150
#endif
151151

152+
#ifdef CONFIG_DEBUG_IRQFLAGS
153+
extern void warn_bogus_irq_restore(void);
154+
#define raw_check_bogus_irq_restore() \
155+
do { \
156+
if (unlikely(!arch_irqs_disabled())) \
157+
warn_bogus_irq_restore(); \
158+
} while (0)
159+
#else
160+
#define raw_check_bogus_irq_restore() do { } while (0)
161+
#endif
162+
152163
/*
153164
* Wrap the arch provided IRQ routines to provide appropriate checks.
154165
*/
@@ -162,6 +173,7 @@ do { \
162173
#define raw_local_irq_restore(flags) \
163174
do { \
164175
typecheck(unsigned long, flags); \
176+
raw_check_bogus_irq_restore(); \
165177
arch_local_irq_restore(flags); \
166178
} while (0)
167179
#define raw_local_save_flags(flags) \

kernel/locking/Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ CFLAGS_REMOVE_mutex-debug.o = $(CC_FLAGS_FTRACE)
1515
CFLAGS_REMOVE_rtmutex-debug.o = $(CC_FLAGS_FTRACE)
1616
endif
1717

18+
obj-$(CONFIG_DEBUG_IRQFLAGS) += irqflag-debug.o
1819
obj-$(CONFIG_DEBUG_MUTEXES) += mutex-debug.o
1920
obj-$(CONFIG_LOCKDEP) += lockdep.o
2021
ifeq ($(CONFIG_PROC_FS),y)

kernel/locking/irqflag-debug.c

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
3+
#include <linux/bug.h>
4+
#include <linux/export.h>
5+
#include <linux/irqflags.h>
6+
7+
void warn_bogus_irq_restore(void)
8+
{
9+
WARN_ONCE(1, "raw_local_irq_restore() called with IRQs enabled\n");
10+
}
11+
EXPORT_SYMBOL(warn_bogus_irq_restore);

lib/Kconfig.debug

+8
Original file line numberDiff line numberDiff line change
@@ -1343,6 +1343,7 @@ config LOCKDEP_SMALL
13431343
config DEBUG_LOCKDEP
13441344
bool "Lock dependency engine debugging"
13451345
depends on DEBUG_KERNEL && LOCKDEP
1346+
select DEBUG_IRQFLAGS
13461347
help
13471348
If you say Y here, the lock dependency engine will do
13481349
additional runtime checks to debug itself, at the price
@@ -1431,6 +1432,13 @@ config TRACE_IRQFLAGS_NMI
14311432
depends on TRACE_IRQFLAGS
14321433
depends on TRACE_IRQFLAGS_NMI_SUPPORT
14331434

1435+
config DEBUG_IRQFLAGS
1436+
bool "Debug IRQ flag manipulation"
1437+
help
1438+
Enables checks for potentially unsafe enabling or disabling of
1439+
interrupts, such as calling raw_local_irq_restore() when interrupts
1440+
are enabled.
1441+
14341442
config STACKTRACE
14351443
bool "Stack backtrace support"
14361444
depends on STACKTRACE_SUPPORT

0 commit comments

Comments
 (0)