debug: Add _ONCE() logic to report_bug()
authorPeter Zijlstra <peterz@infradead.org>
Sat, 25 Feb 2017 07:56:53 +0000 (08:56 +0100)
committerIngo Molnar <mingo@kernel.org>
Thu, 30 Mar 2017 07:37:20 +0000 (09:37 +0200)
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>
arch/arm64/include/asm/bug.h
arch/parisc/include/asm/bug.h
arch/powerpc/include/asm/bug.h
arch/s390/include/asm/bug.h
arch/sh/include/asm/bug.h
arch/x86/include/asm/bug.h
include/asm-generic/bug.h
include/asm-generic/vmlinux.lds.h
include/linux/bug.h
lib/bug.c

index 561190d1588136b1f7bc207ebfd04251694ea552..a9be1072933c0d1213afd9a5fed65de5d554f055 100644 (file)
@@ -55,7 +55,7 @@ _BUGVERBOSE_LOCATION(__FILE__, __LINE__)              \
        unreachable();                          \
 } while (0)
 
-#define __WARN_TAINT(taint) _BUG_FLAGS(BUGFLAG_TAINT(taint))
+#define __WARN_FLAGS(flags) _BUG_FLAGS(BUGFLAG_WARNING|(flags))
 
 #endif /* ! CONFIG_GENERIC_BUG */
 
index 62a33338549c18d6bb32d0a08c7e0728808767fe..d2742273a685df2d243a14fd19a7b47b5a1b94a7 100644 (file)
@@ -46,7 +46,7 @@
 #endif
 
 #ifdef CONFIG_DEBUG_BUGVERBOSE
-#define __WARN_TAINT(taint)                                            \
+#define __WARN_FLAGS(flags)                                            \
        do {                                                            \
                asm volatile("\n"                                       \
                             "1:\t" PARISC_BUG_BREAK_ASM "\n"           \
                             "\t.org 2b+%c3\n"                          \
                             "\t.popsection"                            \
                             : : "i" (__FILE__), "i" (__LINE__),        \
-                            "i" (BUGFLAG_TAINT(taint)),                \
+                            "i" (BUGFLAG_WARNING|(flags)),             \
                             "i" (sizeof(struct bug_entry)) );          \
        } while(0)
 #else
-#define __WARN_TAINT(taint)                                            \
+#define __WARN_FLAGS(flags)                                            \
        do {                                                            \
                asm volatile("\n"                                       \
                             "1:\t" PARISC_BUG_BREAK_ASM "\n"           \
@@ -69,7 +69,7 @@
                             "\t.short %c0\n"                           \
                             "\t.org 2b+%c1\n"                          \
                             "\t.popsection"                            \
-                            : : "i" (BUGFLAG_TAINT(taint)),            \
+                            : : "i" (BUGFLAG_WARNING|(flags)),         \
                             "i" (sizeof(struct bug_entry)) );          \
        } while(0)
 #endif
index 3a39283333c3cdbba5139f1c97f83d7f36815ed9..f2c562a0a427dda2c774acb1e13c7eda8514e21e 100644 (file)
        }                                                       \
 } while (0)
 
-#define __WARN_TAINT(taint) do {                               \
+#define __WARN_FLAGS(flags) do {                               \
        __asm__ __volatile__(                                   \
                "1:     twi 31,0,0\n"                           \
                _EMIT_BUG_ENTRY                                 \
                : : "i" (__FILE__), "i" (__LINE__),             \
-                 "i" (BUGFLAG_TAINT(taint)),                   \
+                 "i" (BUGFLAG_WARNING|(flags)),                \
                  "i" (sizeof(struct bug_entry)));              \
 } while (0)
 
index bf90d1fd97a59cbd4141f8180be49e7b0cc265f7..1bbd9dbfe4e0a2baac12f5a83ac4538bc8a6ca8f 100644 (file)
@@ -46,8 +46,8 @@
        unreachable();                                  \
 } while (0)
 
-#define __WARN_TAINT(taint) do {                       \
-       __EMIT_BUG(BUGFLAG_TAINT(taint));               \
+#define __WARN_FLAGS(flags) do {                       \
+       __EMIT_BUG(BUGFLAG_WARNING|(flags));            \
 } while (0)
 
 #define WARN_ON(x) ({                                  \
index dcf278075429fcb2774cbfaeb586a7dc43bf10ab..1b77f068be2b1d027ae460ce0e6919a84223a1f1 100644 (file)
@@ -50,7 +50,7 @@ do {                                                  \
                   "i" (sizeof(struct bug_entry)));     \
 } while (0)
 
-#define __WARN_TAINT(taint)                            \
+#define __WARN_FLAGS(flags)                            \
 do {                                                   \
        __asm__ __volatile__ (                          \
                "1:\t.short %O0\n"                      \
@@ -59,7 +59,7 @@ do {                                                  \
                 : "n" (TRAPA_BUG_OPCODE),              \
                   "i" (__FILE__),                      \
                   "i" (__LINE__),                      \
-                  "i" (BUGFLAG_TAINT(taint)),          \
+                  "i" (BUGFLAG_WARNING|(flags)),       \
                   "i" (sizeof(struct bug_entry)));     \
 } while (0)
 
index cecf559d00129499da311d3b8c342cf880f541f0..39e702d90cdbdda8df0b42349d9f8d262b08bf4d 100644 (file)
@@ -76,7 +76,7 @@ do {                                                          \
        unreachable();                                          \
 } 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>
 
index 6f96247226a4d2b9c1e9aa56be423ffa0c4d49df..5fe9f39cec44c0115165ed30fdb46a17057e5d1a 100644 (file)
@@ -5,6 +5,8 @@
 
 #ifdef CONFIG_GENERIC_BUG
 #define BUGFLAG_WARNING                (1 << 0)
+#define BUGFLAG_ONCE           (1 << 1)
+#define BUGFLAG_DONE           (1 << 2)
 #define BUGFLAG_TAINT(taint)   (BUGFLAG_WARNING | ((taint) << 8))
 #define BUG_GET_TAINT(bug)     ((bug)->flags >> 8)
 #endif
@@ -55,6 +57,18 @@ struct bug_entry {
 #define BUG_ON(condition) do { if (unlikely(condition)) BUG(); } while (0)
 #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
  * 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
 
 #ifndef WARN
-#define WARN(condition, format...) ({                                          \
+#define WARN(condition, format...) ({                                  \
        int __ret_warn_on = !!(condition);                              \
        if (unlikely(__ret_warn_on))                                    \
                __WARN_printf(format);                                  \
@@ -112,6 +126,7 @@ void __warn(const char *file, int line, void *caller, unsigned taint,
        unlikely(__ret_warn_on);                                        \
 })
 
+#ifndef WARN_ON_ONCE
 #define WARN_ON_ONCE(condition)        ({                              \
        static bool __section(.data.unlikely) __warned;         \
        int __ret_warn_once = !!(condition);                    \
@@ -122,6 +137,7 @@ void __warn(const char *file, int line, void *caller, unsigned taint,
        }                                                       \
        unlikely(__ret_warn_once);                              \
 })
+#endif
 
 #define WARN_ONCE(condition, format...)        ({                      \
        static bool __section(.data.unlikely) __warned;         \
index 0968d13b388591ae02b37f9dda0fe7a498cc7475..51c8dbe8e8d9f594dc7d6bdc545925f48f14a24c 100644 (file)
                *(.rodata1)                                             \
        }                                                               \
                                                                        \
-       BUG_TABLE                                                       \
-                                                                       \
        /* PCI quirks */                                                \
        .pci_fixup        : AT(ADDR(.pci_fixup) - LOAD_OFFSET) {        \
                VMLINUX_SYMBOL(__start_pci_fixups_early) = .;           \
                READ_MOSTLY_DATA(cacheline)                             \
                DATA_DATA                                               \
                CONSTRUCTORS                                            \
-       }
+       }                                                               \
+       BUG_TABLE
 
 #define INIT_TEXT_SECTION(inittext_align)                              \
        . = ALIGN(inittext_align);                                      \
index 5828489309bbd22a255f11064418dc3a6b9366de..687b557fc5eb9fca13f4dadd3643eb769886da67 100644 (file)
@@ -105,7 +105,7 @@ static inline int is_warning_bug(const struct bug_entry *bug)
        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);
 
index 06edbbef062322f12662f95d726b2d83856ac62c..a6a1137d06db75f94d005a9e646e061847be1c93 100644 (file)
--- a/lib/bug.c
+++ b/lib/bug.c
@@ -47,7 +47,7 @@
 #include <linux/sched.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)
 {
@@ -62,10 +62,10 @@ static inline unsigned long bug_addr(const struct bug_entry *bug)
 /* Updates are protected by module mutex */
 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;
-       const struct bug_entry *bug = NULL;
+       struct bug_entry *bug = NULL;
 
        rcu_read_lock_sched();
        list_for_each_entry_rcu(mod, &module_bug_list, bug_list) {
@@ -122,15 +122,15 @@ void module_bug_cleanup(struct module *mod)
 
 #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;
 }
 #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)
                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)
 {
-       const struct bug_entry *bug;
+       struct bug_entry *bug;
        const char *file;
-       unsigned line, warning;
+       unsigned line, warning, once, done;
 
        if (!is_valid_bugaddr(bugaddr))
                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;
 #endif
                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) {