debug: Add _ONCE() logic to report_bug()
Josh suggested moving the _ONCE logic inside the trap handler, using a bit in the bug_entry::flags field, avoiding the need for the extra variable. Sadly this only works for WARN_ON_ONCE(), since the others have printk() statements prior to triggering the trap. Still, this saves a fair amount of text and some data: text data filename 10682460 4530992 defconfig-build/vmlinux.orig 10665111 4530096 defconfig-build/vmlinux.patched Suggested-by: Josh Poimboeuf <jpoimboe@redhat.com> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Cc: Andy Lutomirski <luto@kernel.org> Cc: Arnd Bergmann <arnd@arndb.de> Cc: Borislav Petkov <bp@alien8.de> Cc: Brian Gerst <brgerst@gmail.com> Cc: Denys Vlasenko <dvlasenk@redhat.com> Cc: H. Peter Anvin <hpa@zytor.com> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Thomas Gleixner <tglx@linutronix.de> Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
parent
70579a86e3
commit
19d436268d
|
@ -55,7 +55,7 @@ _BUGVERBOSE_LOCATION(__FILE__, __LINE__) \
|
||||||
unreachable(); \
|
unreachable(); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define __WARN_TAINT(taint) _BUG_FLAGS(BUGFLAG_TAINT(taint))
|
#define __WARN_FLAGS(flags) _BUG_FLAGS(BUGFLAG_WARNING|(flags))
|
||||||
|
|
||||||
#endif /* ! CONFIG_GENERIC_BUG */
|
#endif /* ! CONFIG_GENERIC_BUG */
|
||||||
|
|
||||||
|
|
|
@ -46,7 +46,7 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_DEBUG_BUGVERBOSE
|
#ifdef CONFIG_DEBUG_BUGVERBOSE
|
||||||
#define __WARN_TAINT(taint) \
|
#define __WARN_FLAGS(flags) \
|
||||||
do { \
|
do { \
|
||||||
asm volatile("\n" \
|
asm volatile("\n" \
|
||||||
"1:\t" PARISC_BUG_BREAK_ASM "\n" \
|
"1:\t" PARISC_BUG_BREAK_ASM "\n" \
|
||||||
|
@ -56,11 +56,11 @@
|
||||||
"\t.org 2b+%c3\n" \
|
"\t.org 2b+%c3\n" \
|
||||||
"\t.popsection" \
|
"\t.popsection" \
|
||||||
: : "i" (__FILE__), "i" (__LINE__), \
|
: : "i" (__FILE__), "i" (__LINE__), \
|
||||||
"i" (BUGFLAG_TAINT(taint)), \
|
"i" (BUGFLAG_WARNING|(flags)), \
|
||||||
"i" (sizeof(struct bug_entry)) ); \
|
"i" (sizeof(struct bug_entry)) ); \
|
||||||
} while(0)
|
} while(0)
|
||||||
#else
|
#else
|
||||||
#define __WARN_TAINT(taint) \
|
#define __WARN_FLAGS(flags) \
|
||||||
do { \
|
do { \
|
||||||
asm volatile("\n" \
|
asm volatile("\n" \
|
||||||
"1:\t" PARISC_BUG_BREAK_ASM "\n" \
|
"1:\t" PARISC_BUG_BREAK_ASM "\n" \
|
||||||
|
@ -69,7 +69,7 @@
|
||||||
"\t.short %c0\n" \
|
"\t.short %c0\n" \
|
||||||
"\t.org 2b+%c1\n" \
|
"\t.org 2b+%c1\n" \
|
||||||
"\t.popsection" \
|
"\t.popsection" \
|
||||||
: : "i" (BUGFLAG_TAINT(taint)), \
|
: : "i" (BUGFLAG_WARNING|(flags)), \
|
||||||
"i" (sizeof(struct bug_entry)) ); \
|
"i" (sizeof(struct bug_entry)) ); \
|
||||||
} while(0)
|
} while(0)
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -85,12 +85,12 @@
|
||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define __WARN_TAINT(taint) do { \
|
#define __WARN_FLAGS(flags) do { \
|
||||||
__asm__ __volatile__( \
|
__asm__ __volatile__( \
|
||||||
"1: twi 31,0,0\n" \
|
"1: twi 31,0,0\n" \
|
||||||
_EMIT_BUG_ENTRY \
|
_EMIT_BUG_ENTRY \
|
||||||
: : "i" (__FILE__), "i" (__LINE__), \
|
: : "i" (__FILE__), "i" (__LINE__), \
|
||||||
"i" (BUGFLAG_TAINT(taint)), \
|
"i" (BUGFLAG_WARNING|(flags)), \
|
||||||
"i" (sizeof(struct bug_entry))); \
|
"i" (sizeof(struct bug_entry))); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
|
|
@ -46,8 +46,8 @@
|
||||||
unreachable(); \
|
unreachable(); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define __WARN_TAINT(taint) do { \
|
#define __WARN_FLAGS(flags) do { \
|
||||||
__EMIT_BUG(BUGFLAG_TAINT(taint)); \
|
__EMIT_BUG(BUGFLAG_WARNING|(flags)); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define WARN_ON(x) ({ \
|
#define WARN_ON(x) ({ \
|
||||||
|
|
|
@ -50,7 +50,7 @@ do { \
|
||||||
"i" (sizeof(struct bug_entry))); \
|
"i" (sizeof(struct bug_entry))); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define __WARN_TAINT(taint) \
|
#define __WARN_FLAGS(flags) \
|
||||||
do { \
|
do { \
|
||||||
__asm__ __volatile__ ( \
|
__asm__ __volatile__ ( \
|
||||||
"1:\t.short %O0\n" \
|
"1:\t.short %O0\n" \
|
||||||
|
@ -59,7 +59,7 @@ do { \
|
||||||
: "n" (TRAPA_BUG_OPCODE), \
|
: "n" (TRAPA_BUG_OPCODE), \
|
||||||
"i" (__FILE__), \
|
"i" (__FILE__), \
|
||||||
"i" (__LINE__), \
|
"i" (__LINE__), \
|
||||||
"i" (BUGFLAG_TAINT(taint)), \
|
"i" (BUGFLAG_WARNING|(flags)), \
|
||||||
"i" (sizeof(struct bug_entry))); \
|
"i" (sizeof(struct bug_entry))); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
|
|
@ -76,7 +76,7 @@ do { \
|
||||||
unreachable(); \
|
unreachable(); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define __WARN_TAINT(taint) _BUG_FLAGS(ASM_UD0, BUGFLAG_TAINT(taint))
|
#define __WARN_FLAGS(flags) _BUG_FLAGS(ASM_UD0, BUGFLAG_WARNING|(flags))
|
||||||
|
|
||||||
#include <asm-generic/bug.h>
|
#include <asm-generic/bug.h>
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
|
|
||||||
#ifdef CONFIG_GENERIC_BUG
|
#ifdef CONFIG_GENERIC_BUG
|
||||||
#define BUGFLAG_WARNING (1 << 0)
|
#define BUGFLAG_WARNING (1 << 0)
|
||||||
|
#define BUGFLAG_ONCE (1 << 1)
|
||||||
|
#define BUGFLAG_DONE (1 << 2)
|
||||||
#define BUGFLAG_TAINT(taint) (BUGFLAG_WARNING | ((taint) << 8))
|
#define BUGFLAG_TAINT(taint) (BUGFLAG_WARNING | ((taint) << 8))
|
||||||
#define BUG_GET_TAINT(bug) ((bug)->flags >> 8)
|
#define BUG_GET_TAINT(bug) ((bug)->flags >> 8)
|
||||||
#endif
|
#endif
|
||||||
|
@ -55,6 +57,18 @@ struct bug_entry {
|
||||||
#define BUG_ON(condition) do { if (unlikely(condition)) BUG(); } while (0)
|
#define BUG_ON(condition) do { if (unlikely(condition)) BUG(); } while (0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef __WARN_FLAGS
|
||||||
|
#define __WARN_TAINT(taint) __WARN_FLAGS(BUGFLAG_TAINT(taint))
|
||||||
|
#define __WARN_ONCE_TAINT(taint) __WARN_FLAGS(BUGFLAG_ONCE|BUGFLAG_TAINT(taint))
|
||||||
|
|
||||||
|
#define WARN_ON_ONCE(condition) ({ \
|
||||||
|
int __ret_warn_on = !!(condition); \
|
||||||
|
if (unlikely(__ret_warn_on)) \
|
||||||
|
__WARN_ONCE_TAINT(TAINT_WARN); \
|
||||||
|
unlikely(__ret_warn_on); \
|
||||||
|
})
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* WARN(), WARN_ON(), WARN_ON_ONCE, and so on can be used to report
|
* WARN(), WARN_ON(), WARN_ON_ONCE, and so on can be used to report
|
||||||
* significant issues that need prompt attention if they should ever
|
* significant issues that need prompt attention if they should ever
|
||||||
|
@ -97,7 +111,7 @@ void __warn(const char *file, int line, void *caller, unsigned taint,
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef WARN
|
#ifndef WARN
|
||||||
#define WARN(condition, format...) ({ \
|
#define WARN(condition, format...) ({ \
|
||||||
int __ret_warn_on = !!(condition); \
|
int __ret_warn_on = !!(condition); \
|
||||||
if (unlikely(__ret_warn_on)) \
|
if (unlikely(__ret_warn_on)) \
|
||||||
__WARN_printf(format); \
|
__WARN_printf(format); \
|
||||||
|
@ -112,6 +126,7 @@ void __warn(const char *file, int line, void *caller, unsigned taint,
|
||||||
unlikely(__ret_warn_on); \
|
unlikely(__ret_warn_on); \
|
||||||
})
|
})
|
||||||
|
|
||||||
|
#ifndef WARN_ON_ONCE
|
||||||
#define WARN_ON_ONCE(condition) ({ \
|
#define WARN_ON_ONCE(condition) ({ \
|
||||||
static bool __section(.data.unlikely) __warned; \
|
static bool __section(.data.unlikely) __warned; \
|
||||||
int __ret_warn_once = !!(condition); \
|
int __ret_warn_once = !!(condition); \
|
||||||
|
@ -122,6 +137,7 @@ void __warn(const char *file, int line, void *caller, unsigned taint,
|
||||||
} \
|
} \
|
||||||
unlikely(__ret_warn_once); \
|
unlikely(__ret_warn_once); \
|
||||||
})
|
})
|
||||||
|
#endif
|
||||||
|
|
||||||
#define WARN_ONCE(condition, format...) ({ \
|
#define WARN_ONCE(condition, format...) ({ \
|
||||||
static bool __section(.data.unlikely) __warned; \
|
static bool __section(.data.unlikely) __warned; \
|
||||||
|
|
|
@ -286,8 +286,6 @@
|
||||||
*(.rodata1) \
|
*(.rodata1) \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
BUG_TABLE \
|
|
||||||
\
|
|
||||||
/* PCI quirks */ \
|
/* PCI quirks */ \
|
||||||
.pci_fixup : AT(ADDR(.pci_fixup) - LOAD_OFFSET) { \
|
.pci_fixup : AT(ADDR(.pci_fixup) - LOAD_OFFSET) { \
|
||||||
VMLINUX_SYMBOL(__start_pci_fixups_early) = .; \
|
VMLINUX_SYMBOL(__start_pci_fixups_early) = .; \
|
||||||
|
@ -855,7 +853,8 @@
|
||||||
READ_MOSTLY_DATA(cacheline) \
|
READ_MOSTLY_DATA(cacheline) \
|
||||||
DATA_DATA \
|
DATA_DATA \
|
||||||
CONSTRUCTORS \
|
CONSTRUCTORS \
|
||||||
}
|
} \
|
||||||
|
BUG_TABLE
|
||||||
|
|
||||||
#define INIT_TEXT_SECTION(inittext_align) \
|
#define INIT_TEXT_SECTION(inittext_align) \
|
||||||
. = ALIGN(inittext_align); \
|
. = ALIGN(inittext_align); \
|
||||||
|
|
|
@ -105,7 +105,7 @@ static inline int is_warning_bug(const struct bug_entry *bug)
|
||||||
return bug->flags & BUGFLAG_WARNING;
|
return bug->flags & BUGFLAG_WARNING;
|
||||||
}
|
}
|
||||||
|
|
||||||
const struct bug_entry *find_bug(unsigned long bugaddr);
|
struct bug_entry *find_bug(unsigned long bugaddr);
|
||||||
|
|
||||||
enum bug_trap_type report_bug(unsigned long bug_addr, struct pt_regs *regs);
|
enum bug_trap_type report_bug(unsigned long bug_addr, struct pt_regs *regs);
|
||||||
|
|
||||||
|
|
28
lib/bug.c
28
lib/bug.c
|
@ -47,7 +47,7 @@
|
||||||
#include <linux/sched.h>
|
#include <linux/sched.h>
|
||||||
#include <linux/rculist.h>
|
#include <linux/rculist.h>
|
||||||
|
|
||||||
extern const struct bug_entry __start___bug_table[], __stop___bug_table[];
|
extern struct bug_entry __start___bug_table[], __stop___bug_table[];
|
||||||
|
|
||||||
static inline unsigned long bug_addr(const struct bug_entry *bug)
|
static inline unsigned long bug_addr(const struct bug_entry *bug)
|
||||||
{
|
{
|
||||||
|
@ -62,10 +62,10 @@ static inline unsigned long bug_addr(const struct bug_entry *bug)
|
||||||
/* Updates are protected by module mutex */
|
/* Updates are protected by module mutex */
|
||||||
static LIST_HEAD(module_bug_list);
|
static LIST_HEAD(module_bug_list);
|
||||||
|
|
||||||
static const struct bug_entry *module_find_bug(unsigned long bugaddr)
|
static struct bug_entry *module_find_bug(unsigned long bugaddr)
|
||||||
{
|
{
|
||||||
struct module *mod;
|
struct module *mod;
|
||||||
const struct bug_entry *bug = NULL;
|
struct bug_entry *bug = NULL;
|
||||||
|
|
||||||
rcu_read_lock_sched();
|
rcu_read_lock_sched();
|
||||||
list_for_each_entry_rcu(mod, &module_bug_list, bug_list) {
|
list_for_each_entry_rcu(mod, &module_bug_list, bug_list) {
|
||||||
|
@ -122,15 +122,15 @@ void module_bug_cleanup(struct module *mod)
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
static inline const struct bug_entry *module_find_bug(unsigned long bugaddr)
|
static inline struct bug_entry *module_find_bug(unsigned long bugaddr)
|
||||||
{
|
{
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
const struct bug_entry *find_bug(unsigned long bugaddr)
|
struct bug_entry *find_bug(unsigned long bugaddr)
|
||||||
{
|
{
|
||||||
const struct bug_entry *bug;
|
struct bug_entry *bug;
|
||||||
|
|
||||||
for (bug = __start___bug_table; bug < __stop___bug_table; ++bug)
|
for (bug = __start___bug_table; bug < __stop___bug_table; ++bug)
|
||||||
if (bugaddr == bug_addr(bug))
|
if (bugaddr == bug_addr(bug))
|
||||||
|
@ -141,9 +141,9 @@ const struct bug_entry *find_bug(unsigned long bugaddr)
|
||||||
|
|
||||||
enum bug_trap_type report_bug(unsigned long bugaddr, struct pt_regs *regs)
|
enum bug_trap_type report_bug(unsigned long bugaddr, struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
const struct bug_entry *bug;
|
struct bug_entry *bug;
|
||||||
const char *file;
|
const char *file;
|
||||||
unsigned line, warning;
|
unsigned line, warning, once, done;
|
||||||
|
|
||||||
if (!is_valid_bugaddr(bugaddr))
|
if (!is_valid_bugaddr(bugaddr))
|
||||||
return BUG_TRAP_TYPE_NONE;
|
return BUG_TRAP_TYPE_NONE;
|
||||||
|
@ -164,6 +164,18 @@ enum bug_trap_type report_bug(unsigned long bugaddr, struct pt_regs *regs)
|
||||||
line = bug->line;
|
line = bug->line;
|
||||||
#endif
|
#endif
|
||||||
warning = (bug->flags & BUGFLAG_WARNING) != 0;
|
warning = (bug->flags & BUGFLAG_WARNING) != 0;
|
||||||
|
once = (bug->flags & BUGFLAG_ONCE) != 0;
|
||||||
|
done = (bug->flags & BUGFLAG_DONE) != 0;
|
||||||
|
|
||||||
|
if (warning && once) {
|
||||||
|
if (done)
|
||||||
|
return BUG_TRAP_TYPE_WARN;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Since this is the only store, concurrency is not an issue.
|
||||||
|
*/
|
||||||
|
bug->flags |= BUGFLAG_DONE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (warning) {
|
if (warning) {
|
||||||
|
|
Loading…
Reference in New Issue