forked from mirrors/linux
		
	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
					
				
					 10 changed files with 52 additions and 25 deletions
				
			
		|  | @ -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 a new issue
	
	 Peter Zijlstra
						Peter Zijlstra