aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGuenter Roeck <linux@roeck-us.net>2024-05-05 17:53:04 -0700
committerGuenter Roeck <linux@roeck-us.net>2024-05-05 17:53:04 -0700
commit4524842997db0c7e6a87fb959162e57ea4355345 (patch)
treed7eee2a0f397707f16cd4ee1714cf10a4d88ef98
parentcca4b0c07b4b4f60e84a974bbe55c17091b65fdc (diff)
parent451ae112c4da24137a3f2dbe1e6a6914b7c39ec4 (diff)
downloadlinux-staging-testing.tar.gz
Merge branch 'kunit-v4' into testingtesting
-rw-r--r--Documentation/dev-tools/kunit/usage.rst30
-rw-r--r--arch/arm64/include/asm/asm-bug.h29
-rw-r--r--arch/arm64/include/asm/bug.h8
-rw-r--r--arch/loongarch/include/asm/bug.h38
-rw-r--r--arch/parisc/include/asm/bug.h29
-rw-r--r--arch/powerpc/include/asm/bug.h37
-rw-r--r--arch/riscv/include/asm/bug.h38
-rw-r--r--arch/s390/include/asm/bug.h17
-rw-r--r--arch/sh/include/asm/bug.h28
-rw-r--r--arch/x86/include/asm/bug.h21
-rw-r--r--drivers/gpu/drm/tests/drm_rect_test.c16
-rw-r--r--include/asm-generic/bug.h16
-rw-r--r--include/kunit/bug.h56
-rw-r--r--include/kunit/test.h1
-rw-r--r--include/linux/bug.h13
-rw-r--r--lib/bug.c51
-rw-r--r--lib/kunit/Kconfig9
-rw-r--r--lib/kunit/Makefile7
-rw-r--r--lib/kunit/backtrace-suppression-test.c104
-rw-r--r--lib/kunit/bug.c42
20 files changed, 518 insertions, 72 deletions
diff --git a/Documentation/dev-tools/kunit/usage.rst b/Documentation/dev-tools/kunit/usage.rst
index 22955d56b3799b..8d3d36d4103d34 100644
--- a/Documentation/dev-tools/kunit/usage.rst
+++ b/Documentation/dev-tools/kunit/usage.rst
@@ -157,6 +157,34 @@ Alternatively, one can take full control over the error message by using
if (some_setup_function())
KUNIT_FAIL(test, "Failed to setup thing for testing");
+Suppressing warning backtraces
+------------------------------
+
+Some unit tests trigger warning backtraces either intentionally or as side
+effect. Such backtraces are normally undesirable since they distract from
+the actual test and may result in the impression that there is a problem.
+
+Such backtraces can be suppressed. To suppress a backtrace in some_function(),
+use the following code.
+
+.. code-block:: c
+
+ static void some_test(struct kunit *test)
+ {
+ DEFINE_SUPPRESSED_WARNING(some_function);
+
+ START_SUPPRESSED_WARNING(some_function);
+ trigger_backtrace();
+ END_SUPPRESSED_WARNING(some_function);
+ }
+
+SUPPRESSED_WARNING_COUNT() returns the number of suppressed backtraces. If the
+suppressed backtrace was triggered on purpose, this can be used to check if
+the backtrace was actually triggered.
+
+.. code-block:: c
+
+ KUNIT_EXPECT_EQ(test, SUPPRESSED_WARNING_COUNT(some_function), 1);
Test Suites
~~~~~~~~~~~
@@ -857,4 +885,4 @@ For example:
dev_managed_string = devm_kstrdup(fake_device, "Hello, World!");
// Everything is cleaned up automatically when the test ends.
- } \ No newline at end of file
+ }
diff --git a/arch/arm64/include/asm/asm-bug.h b/arch/arm64/include/asm/asm-bug.h
index c762038ba40093..c6d22e3cd8405f 100644
--- a/arch/arm64/include/asm/asm-bug.h
+++ b/arch/arm64/include/asm/asm-bug.h
@@ -8,36 +8,45 @@
#include <asm/brk-imm.h>
#ifdef CONFIG_DEBUG_BUGVERBOSE
-#define _BUGVERBOSE_LOCATION(file, line) __BUGVERBOSE_LOCATION(file, line)
-#define __BUGVERBOSE_LOCATION(file, line) \
+
+#ifdef CONFIG_KUNIT_SUPPRESS_BACKTRACE
+# define HAVE_BUG_FUNCTION
+# define __BUG_FUNC_PTR(func) .long func - .;
+#else
+# define __BUG_FUNC_PTR(func)
+#endif
+
+#define _BUGVERBOSE_LOCATION(file, func, line) __BUGVERBOSE_LOCATION(file, func, line)
+#define __BUGVERBOSE_LOCATION(file, func, line) \
.pushsection .rodata.str,"aMS",@progbits,1; \
14472: .string file; \
.popsection; \
\
.long 14472b - .; \
+ __BUG_FUNC_PTR(func) \
.short line;
#else
-#define _BUGVERBOSE_LOCATION(file, line)
+#define _BUGVERBOSE_LOCATION(file, func, line)
#endif
#ifdef CONFIG_GENERIC_BUG
-#define __BUG_ENTRY(flags) \
+#define __BUG_ENTRY(flags, func) \
.pushsection __bug_table,"aw"; \
.align 2; \
14470: .long 14471f - .; \
-_BUGVERBOSE_LOCATION(__FILE__, __LINE__) \
- .short flags; \
+_BUGVERBOSE_LOCATION(__FILE__, func, __LINE__) \
+ .short flags; \
.popsection; \
14471:
#else
-#define __BUG_ENTRY(flags)
+#define __BUG_ENTRY(flags, func)
#endif
-#define ASM_BUG_FLAGS(flags) \
- __BUG_ENTRY(flags) \
+#define ASM_BUG_FLAGS(flags, func) \
+ __BUG_ENTRY(flags, func) \
brk BUG_BRK_IMM
-#define ASM_BUG() ASM_BUG_FLAGS(0)
+#define ASM_BUG() ASM_BUG_FLAGS(0, .)
#endif /* __ASM_ASM_BUG_H */
diff --git a/arch/arm64/include/asm/bug.h b/arch/arm64/include/asm/bug.h
index 28be048db3f63a..044c5e24a17db8 100644
--- a/arch/arm64/include/asm/bug.h
+++ b/arch/arm64/include/asm/bug.h
@@ -11,8 +11,14 @@
#include <asm/asm-bug.h>
+#ifdef HAVE_BUG_FUNCTION
+# define __BUG_FUNC __func__
+#else
+# define __BUG_FUNC NULL
+#endif
+
#define __BUG_FLAGS(flags) \
- asm volatile (__stringify(ASM_BUG_FLAGS(flags)));
+ asm volatile (__stringify(ASM_BUG_FLAGS(flags, %c0)) : : "i" (__BUG_FUNC));
#define BUG() do { \
__BUG_FLAGS(0); \
diff --git a/arch/loongarch/include/asm/bug.h b/arch/loongarch/include/asm/bug.h
index 08388876ade4ce..193f396d81a075 100644
--- a/arch/loongarch/include/asm/bug.h
+++ b/arch/loongarch/include/asm/bug.h
@@ -3,47 +3,63 @@
#define __ASM_BUG_H
#include <asm/break.h>
+#include <kunit/bug.h>
#include <linux/stringify.h>
#ifndef CONFIG_DEBUG_BUGVERBOSE
-#define _BUGVERBOSE_LOCATION(file, line)
+#define _BUGVERBOSE_LOCATION(file, func, line)
#else
-#define __BUGVERBOSE_LOCATION(file, line) \
+#ifdef CONFIG_KUNIT_SUPPRESS_BACKTRACE
+# define HAVE_BUG_FUNCTION
+# define __BUG_FUNC_PTR(func) .long func - .;
+#else
+# define __BUG_FUNC_PTR(func)
+#endif /* CONFIG_KUNIT_SUPPRESS_BACKTRACE */
+
+#define __BUGVERBOSE_LOCATION(file, func, line) \
.pushsection .rodata.str, "aMS", @progbits, 1; \
10002: .string file; \
.popsection; \
\
.long 10002b - .; \
+ __BUG_FUNC_PTR(func) \
.short line;
-#define _BUGVERBOSE_LOCATION(file, line) __BUGVERBOSE_LOCATION(file, line)
+#define _BUGVERBOSE_LOCATION(file, func, line) __BUGVERBOSE_LOCATION(file, func, line)
#endif
#ifndef CONFIG_GENERIC_BUG
-#define __BUG_ENTRY(flags)
+#define __BUG_ENTRY(flags, func)
#else
-#define __BUG_ENTRY(flags) \
+#define __BUG_ENTRY(flags, func) \
.pushsection __bug_table, "aw"; \
.align 2; \
10000: .long 10001f - .; \
- _BUGVERBOSE_LOCATION(__FILE__, __LINE__) \
+ _BUGVERBOSE_LOCATION(__FILE__, func, __LINE__) \
.short flags; \
.popsection; \
10001:
#endif
-#define ASM_BUG_FLAGS(flags) \
- __BUG_ENTRY(flags) \
+#define ASM_BUG_FLAGS(flags, func) \
+ __BUG_ENTRY(flags, func) \
break BRK_BUG
-#define ASM_BUG() ASM_BUG_FLAGS(0)
+#define ASM_BUG() ASM_BUG_FLAGS(0, .)
+
+#ifdef HAVE_BUG_FUNCTION
+# define __BUG_FUNC __func__
+#else
+# define __BUG_FUNC NULL
+#endif
#define __BUG_FLAGS(flags) \
- asm_inline volatile (__stringify(ASM_BUG_FLAGS(flags)));
+ asm_inline volatile (__stringify(ASM_BUG_FLAGS(flags, %0)) : : "i" (__BUG_FUNC));
#define __WARN_FLAGS(flags) \
do { \
instrumentation_begin(); \
- __BUG_FLAGS(BUGFLAG_WARNING|(flags)); \
+ if (!IS_SUPPRESSED_WARNING(__func__)) \
+ __BUG_FLAGS(BUGFLAG_WARNING|(flags)); \
annotate_reachable(); \
instrumentation_end(); \
} while (0)
diff --git a/arch/parisc/include/asm/bug.h b/arch/parisc/include/asm/bug.h
index 833555f74ffa72..b59c3f7380bf24 100644
--- a/arch/parisc/include/asm/bug.h
+++ b/arch/parisc/include/asm/bug.h
@@ -23,8 +23,17 @@
# define __BUG_REL(val) ".word " __stringify(val)
#endif
-
#ifdef CONFIG_DEBUG_BUGVERBOSE
+
+#ifdef CONFIG_KUNIT_SUPPRESS_BACKTRACE
+# define HAVE_BUG_FUNCTION
+# define __BUG_FUNC_PTR __BUG_REL(%c1)
+# define __BUG_FUNC __func__
+#else
+# define __BUG_FUNC_PTR
+# define __BUG_FUNC NULL
+#endif /* CONFIG_KUNIT_SUPPRESS_BACKTRACE */
+
#define BUG() \
do { \
asm volatile("\n" \
@@ -33,10 +42,12 @@
"\t.align 4\n" \
"2:\t" __BUG_REL(1b) "\n" \
"\t" __BUG_REL(%c0) "\n" \
- "\t.short %1, %2\n" \
- "\t.blockz %3-2*4-2*2\n" \
+ "\t" __BUG_FUNC_PTR "\n" \
+ "\t.short %c2, %c3\n" \
+ "\t.blockz %c4-(.-2b)\n" \
"\t.popsection" \
- : : "i" (__FILE__), "i" (__LINE__), \
+ : : "i" (__FILE__), "i" (__BUG_FUNC), \
+ "i" (__LINE__), \
"i" (0), "i" (sizeof(struct bug_entry)) ); \
unreachable(); \
} while(0)
@@ -58,10 +69,12 @@
"\t.align 4\n" \
"2:\t" __BUG_REL(1b) "\n" \
"\t" __BUG_REL(%c0) "\n" \
- "\t.short %1, %2\n" \
- "\t.blockz %3-2*4-2*2\n" \
+ "\t" __BUG_FUNC_PTR "\n" \
+ "\t.short %c2, %3\n" \
+ "\t.blockz %c4-(.-2b)\n" \
"\t.popsection" \
- : : "i" (__FILE__), "i" (__LINE__), \
+ : : "i" (__FILE__), "i" (__BUG_FUNC), \
+ "i" (__LINE__), \
"i" (BUGFLAG_WARNING|(flags)), \
"i" (sizeof(struct bug_entry)) ); \
} while(0)
@@ -74,7 +87,7 @@
"\t.align 4\n" \
"2:\t" __BUG_REL(1b) "\n" \
"\t.short %0\n" \
- "\t.blockz %1-4-2\n" \
+ "\t.blockz %c1-(.-2b)\n" \
"\t.popsection" \
: : "i" (BUGFLAG_WARNING|(flags)), \
"i" (sizeof(struct bug_entry)) ); \
diff --git a/arch/powerpc/include/asm/bug.h b/arch/powerpc/include/asm/bug.h
index 1db485aacbd9b7..5b06745d20aaf7 100644
--- a/arch/powerpc/include/asm/bug.h
+++ b/arch/powerpc/include/asm/bug.h
@@ -14,6 +14,9 @@
.section __bug_table,"aw"
5001: .4byte \addr - .
.4byte 5002f - .
+#ifdef CONFIG_KUNIT_SUPPRESS_BACKTRACE
+ .4byte 0
+#endif
.short \line, \flags
.org 5001b+BUG_ENTRY_SIZE
.previous
@@ -32,30 +35,46 @@
#endif /* verbose */
#else /* !__ASSEMBLY__ */
-/* _EMIT_BUG_ENTRY expects args %0,%1,%2,%3 to be FILE, LINE, flags and
- sizeof(struct bug_entry), respectively */
+/* _EMIT_BUG_ENTRY expects args %0,%1,%2,%3,%4 to be FILE, __func__, LINE, flags
+ and sizeof(struct bug_entry), respectively */
#ifdef CONFIG_DEBUG_BUGVERBOSE
+
+#ifdef CONFIG_KUNIT_SUPPRESS_BACKTRACE
+# define HAVE_BUG_FUNCTION
+# define __BUG_FUNC_PTR " .4byte %1 - .\n"
+#else
+# define __BUG_FUNC_PTR
+#endif /* CONFIG_KUNIT_SUPPRESS_BACKTRACE */
+
#define _EMIT_BUG_ENTRY \
".section __bug_table,\"aw\"\n" \
"2: .4byte 1b - .\n" \
" .4byte %0 - .\n" \
- " .short %1, %2\n" \
- ".org 2b+%3\n" \
+ __BUG_FUNC_PTR \
+ " .short %2, %3\n" \
+ ".org 2b+%4\n" \
".previous\n"
#else
#define _EMIT_BUG_ENTRY \
".section __bug_table,\"aw\"\n" \
"2: .4byte 1b - .\n" \
- " .short %2\n" \
- ".org 2b+%3\n" \
+ " .short %3\n" \
+ ".org 2b+%4\n" \
".previous\n"
#endif
+#ifdef HAVE_BUG_FUNCTION
+# define __BUG_FUNC __func__
+#else
+# define __BUG_FUNC NULL
+#endif
+
#define BUG_ENTRY(insn, flags, ...) \
__asm__ __volatile__( \
"1: " insn "\n" \
_EMIT_BUG_ENTRY \
- : : "i" (__FILE__), "i" (__LINE__), \
+ : : "i" (__FILE__), "i" (__BUG_FUNC), \
+ "i" (__LINE__), \
"i" (flags), \
"i" (sizeof(struct bug_entry)), \
##__VA_ARGS__)
@@ -80,7 +99,7 @@
if (x) \
BUG(); \
} else { \
- BUG_ENTRY(PPC_TLNEI " %4, 0", 0, "r" ((__force long)(x))); \
+ BUG_ENTRY(PPC_TLNEI " %5, 0", 0, "r" ((__force long)(x))); \
} \
} while (0)
@@ -90,7 +109,7 @@
if (__ret_warn_on) \
__WARN(); \
} else { \
- BUG_ENTRY(PPC_TLNEI " %4, 0", \
+ BUG_ENTRY(PPC_TLNEI " %5, 0", \
BUGFLAG_WARNING | BUGFLAG_TAINT(TAINT_WARN), \
"r" (__ret_warn_on)); \
} \
diff --git a/arch/riscv/include/asm/bug.h b/arch/riscv/include/asm/bug.h
index 1aaea81fb1413e..79f360af4ad8c8 100644
--- a/arch/riscv/include/asm/bug.h
+++ b/arch/riscv/include/asm/bug.h
@@ -30,26 +30,39 @@
typedef u32 bug_insn_t;
#ifdef CONFIG_GENERIC_BUG_RELATIVE_POINTERS
-#define __BUG_ENTRY_ADDR RISCV_INT " 1b - ."
-#define __BUG_ENTRY_FILE RISCV_INT " %0 - ."
+#define __BUG_REL(val) RISCV_INT " " __stringify(val) " - ."
#else
-#define __BUG_ENTRY_ADDR RISCV_PTR " 1b"
-#define __BUG_ENTRY_FILE RISCV_PTR " %0"
+#define __BUG_REL(val) RISCV_PTR " " __stringify(val)
#endif
#ifdef CONFIG_DEBUG_BUGVERBOSE
+
+#ifdef CONFIG_KUNIT_SUPPRESS_BACKTRACE
+# define HAVE_BUG_FUNCTION
+# define __BUG_FUNC_PTR __BUG_REL(%1)
+#else
+# define __BUG_FUNC_PTR
+#endif /* CONFIG_KUNIT_SUPPRESS_BACKTRACE */
+
#define __BUG_ENTRY \
- __BUG_ENTRY_ADDR "\n\t" \
- __BUG_ENTRY_FILE "\n\t" \
- RISCV_SHORT " %1\n\t" \
- RISCV_SHORT " %2"
+ __BUG_REL(1b) "\n\t" \
+ __BUG_REL(%0) "\n\t" \
+ __BUG_FUNC_PTR "\n\t" \
+ RISCV_SHORT " %2\n\t" \
+ RISCV_SHORT " %3"
#else
#define __BUG_ENTRY \
- __BUG_ENTRY_ADDR "\n\t" \
- RISCV_SHORT " %2"
+ __BUG_REL(1b) "\n\t" \
+ RISCV_SHORT " %3"
#endif
#ifdef CONFIG_GENERIC_BUG
+#ifdef HAVE_BUG_FUNCTION
+# define __BUG_FUNC __func__
+#else
+# define __BUG_FUNC NULL
+#endif
+
#define __BUG_FLAGS(flags) \
do { \
__asm__ __volatile__ ( \
@@ -58,10 +71,11 @@ do { \
".pushsection __bug_table,\"aw\"\n\t" \
"2:\n\t" \
__BUG_ENTRY "\n\t" \
- ".org 2b + %3\n\t" \
+ ".org 2b + %4\n\t" \
".popsection" \
: \
- : "i" (__FILE__), "i" (__LINE__), \
+ : "i" (__FILE__), "i" (__BUG_FUNC), \
+ "i" (__LINE__), \
"i" (flags), \
"i" (sizeof(struct bug_entry))); \
} while (0)
diff --git a/arch/s390/include/asm/bug.h b/arch/s390/include/asm/bug.h
index c500d45fb465c5..44d4e9f24ae0e8 100644
--- a/arch/s390/include/asm/bug.h
+++ b/arch/s390/include/asm/bug.h
@@ -8,6 +8,15 @@
#ifdef CONFIG_DEBUG_BUGVERBOSE
+#ifdef CONFIG_KUNIT_SUPPRESS_BACKTRACE
+# define HAVE_BUG_FUNCTION
+# define __BUG_FUNC_PTR " .long %0-.\n"
+# define __BUG_FUNC __func__
+#else
+# define __BUG_FUNC_PTR
+# define __BUG_FUNC NULL
+#endif /* CONFIG_KUNIT_SUPPRESS_BACKTRACE */
+
#define __EMIT_BUG(x) do { \
asm_inline volatile( \
"0: mc 0,0\n" \
@@ -17,10 +26,12 @@
".section __bug_table,\"aw\"\n" \
"2: .long 0b-.\n" \
" .long 1b-.\n" \
- " .short %0,%1\n" \
- " .org 2b+%2\n" \
+ __BUG_FUNC_PTR \
+ " .short %1,%2\n" \
+ " .org 2b+%3\n" \
".previous\n" \
- : : "i" (__LINE__), \
+ : : "i" (__BUG_FUNC), \
+ "i" (__LINE__), \
"i" (x), \
"i" (sizeof(struct bug_entry))); \
} while (0)
diff --git a/arch/sh/include/asm/bug.h b/arch/sh/include/asm/bug.h
index 05a485c4fabcfc..bf4947d51d693b 100644
--- a/arch/sh/include/asm/bug.h
+++ b/arch/sh/include/asm/bug.h
@@ -11,6 +11,15 @@
#define HAVE_ARCH_BUG
#define HAVE_ARCH_WARN_ON
+#ifdef CONFIG_DEBUG_BUGVERBOSE
+#ifdef CONFIG_KUNIT_SUPPRESS_BACKTRACE
+# define HAVE_BUG_FUNCTION
+# define __BUG_FUNC_PTR "\t.long %O2\n"
+#else
+# define __BUG_FUNC_PTR
+#endif /* CONFIG_KUNIT_SUPPRESS_BACKTRACE */
+#endif /* CONFIG_DEBUG_BUGVERBOSE */
+
/**
* _EMIT_BUG_ENTRY
* %1 - __FILE__
@@ -24,21 +33,29 @@
* The offending file and line are encoded in the __bug_table section.
*/
#ifdef CONFIG_DEBUG_BUGVERBOSE
+
#define _EMIT_BUG_ENTRY \
"\t.pushsection __bug_table,\"aw\"\n" \
"2:\t.long 1b, %O1\n" \
- "\t.short %O2, %O3\n" \
- "\t.org 2b+%O4\n" \
+ __BUG_FUNC_PTR \
+ "\t.short %O3, %O4\n" \
+ "\t.org 2b+%O5\n" \
"\t.popsection\n"
#else
#define _EMIT_BUG_ENTRY \
"\t.pushsection __bug_table,\"aw\"\n" \
"2:\t.long 1b\n" \
- "\t.short %O3\n" \
- "\t.org 2b+%O4\n" \
+ "\t.short %O4\n" \
+ "\t.org 2b+%O5\n" \
"\t.popsection\n"
#endif
+#ifdef HAVE_BUG_FUNCTION
+# define __BUG_FUNC __func__
+#else
+# define __BUG_FUNC NULL
+#endif
+
#define BUG() \
do { \
__asm__ __volatile__ ( \
@@ -47,6 +64,7 @@ do { \
: \
: "n" (TRAPA_BUG_OPCODE), \
"i" (__FILE__), \
+ "i" (__BUG_FUNC), \
"i" (__LINE__), "i" (0), \
"i" (sizeof(struct bug_entry))); \
unreachable(); \
@@ -60,6 +78,7 @@ do { \
: \
: "n" (TRAPA_BUG_OPCODE), \
"i" (__FILE__), \
+ "i" (__BUG_FUNC), \
"i" (__LINE__), \
"i" (BUGFLAG_WARNING|(flags)), \
"i" (sizeof(struct bug_entry))); \
@@ -85,6 +104,7 @@ do { \
: \
: "n" (TRAPA_BUG_OPCODE), \
"i" (__FILE__), \
+ "i" (__BUG_FUNC), \
"i" (__LINE__), \
"i" (BUGFLAG_UNWINDER), \
"i" (sizeof(struct bug_entry))); \
diff --git a/arch/x86/include/asm/bug.h b/arch/x86/include/asm/bug.h
index a3ec87d198ac83..7698dfa74c98c0 100644
--- a/arch/x86/include/asm/bug.h
+++ b/arch/x86/include/asm/bug.h
@@ -23,18 +23,28 @@
#ifdef CONFIG_DEBUG_BUGVERBOSE
+#ifdef CONFIG_KUNIT_SUPPRESS_BACKTRACE
+# define HAVE_BUG_FUNCTION
+# define __BUG_FUNC_PTR __BUG_REL(%c1)
+# define __BUG_FUNC __func__
+#else
+# define __BUG_FUNC_PTR
+# define __BUG_FUNC NULL
+#endif /* CONFIG_KUNIT_SUPPRESS_BACKTRACE */
+
#define _BUG_FLAGS(ins, flags, extra) \
do { \
asm_inline volatile("1:\t" ins "\n" \
".pushsection __bug_table,\"aw\"\n" \
"2:\t" __BUG_REL(1b) "\t# bug_entry::bug_addr\n" \
"\t" __BUG_REL(%c0) "\t# bug_entry::file\n" \
- "\t.word %c1" "\t# bug_entry::line\n" \
- "\t.word %c2" "\t# bug_entry::flags\n" \
- "\t.org 2b+%c3\n" \
+ "\t" __BUG_FUNC_PTR "\t# bug_entry::function\n" \
+ "\t.word %c2" "\t# bug_entry::line\n" \
+ "\t.word %c3" "\t# bug_entry::flags\n" \
+ "\t.org 2b+%c4\n" \
".popsection\n" \
extra \
- : : "i" (__FILE__), "i" (__LINE__), \
+ : : "i" (__FILE__), "i" (__BUG_FUNC), "i" (__LINE__),\
"i" (flags), \
"i" (sizeof(struct bug_entry))); \
} while (0)
@@ -80,7 +90,8 @@ do { \
do { \
__auto_type __flags = BUGFLAG_WARNING|(flags); \
instrumentation_begin(); \
- _BUG_FLAGS(ASM_UD2, __flags, ASM_REACHABLE); \
+ if (!IS_SUPPRESSED_WARNING(__func__)) \
+ _BUG_FLAGS(ASM_UD2, __flags, ASM_REACHABLE); \
instrumentation_end(); \
} while (0)
diff --git a/drivers/gpu/drm/tests/drm_rect_test.c b/drivers/gpu/drm/tests/drm_rect_test.c
index 76332cd2ead832..66851769ee32f5 100644
--- a/drivers/gpu/drm/tests/drm_rect_test.c
+++ b/drivers/gpu/drm/tests/drm_rect_test.c
@@ -406,22 +406,38 @@ KUNIT_ARRAY_PARAM(drm_rect_scale, drm_rect_scale_cases, drm_rect_scale_case_desc
static void drm_test_rect_calc_hscale(struct kunit *test)
{
+ DEFINE_SUPPRESSED_WARNING(drm_calc_scale);
const struct drm_rect_scale_case *params = test->param_value;
int scaling_factor;
+ /*
+ * drm_rect_calc_hscale() generates a warning backtrace whenever bad
+ * parameters are passed to it. This affects all unit tests with an
+ * error code in expected_scaling_factor.
+ */
+ START_SUPPRESSED_WARNING(drm_calc_scale);
scaling_factor = drm_rect_calc_hscale(&params->src, &params->dst,
params->min_range, params->max_range);
+ END_SUPPRESSED_WARNING(drm_calc_scale);
KUNIT_EXPECT_EQ(test, scaling_factor, params->expected_scaling_factor);
}
static void drm_test_rect_calc_vscale(struct kunit *test)
{
+ DEFINE_SUPPRESSED_WARNING(drm_calc_scale);
const struct drm_rect_scale_case *params = test->param_value;
int scaling_factor;
+ /*
+ * drm_rect_calc_vscale() generates a warning backtrace whenever bad
+ * parameters are passed to it. This affects all unit tests with an
+ * error code in expected_scaling_factor.
+ */
+ START_SUPPRESSED_WARNING(drm_calc_scale);
scaling_factor = drm_rect_calc_vscale(&params->src, &params->dst,
params->min_range, params->max_range);
+ END_SUPPRESSED_WARNING(drm_calc_scale);
KUNIT_EXPECT_EQ(test, scaling_factor, params->expected_scaling_factor);
}
diff --git a/include/asm-generic/bug.h b/include/asm-generic/bug.h
index b7de3a4eade1c2..a51d273fe1e3d1 100644
--- a/include/asm-generic/bug.h
+++ b/include/asm-generic/bug.h
@@ -18,6 +18,7 @@
#endif
#ifndef __ASSEMBLY__
+#include <kunit/bug.h>
#include <linux/panic.h>
#include <linux/printk.h>
@@ -39,8 +40,14 @@ struct bug_entry {
#ifdef CONFIG_DEBUG_BUGVERBOSE
#ifndef CONFIG_GENERIC_BUG_RELATIVE_POINTERS
const char *file;
+#ifdef HAVE_BUG_FUNCTION
+ const char *function;
+#endif
#else
signed int file_disp;
+#ifdef HAVE_BUG_FUNCTION
+ signed int function_disp;
+#endif
#endif
unsigned short line;
#endif
@@ -96,15 +103,18 @@ extern __printf(1, 2) void __warn_printk(const char *fmt, ...);
#define __WARN() __WARN_printf(TAINT_WARN, NULL)
#define __WARN_printf(taint, arg...) do { \
instrumentation_begin(); \
- warn_slowpath_fmt(__FILE__, __LINE__, taint, arg); \
+ if (!IS_SUPPRESSED_WARNING(__func__)) \
+ warn_slowpath_fmt(__FILE__, __LINE__, taint, arg);\
instrumentation_end(); \
} while (0)
#else
#define __WARN() __WARN_FLAGS(BUGFLAG_TAINT(TAINT_WARN))
#define __WARN_printf(taint, arg...) do { \
instrumentation_begin(); \
- __warn_printk(arg); \
- __WARN_FLAGS(BUGFLAG_NO_CUT_HERE | BUGFLAG_TAINT(taint));\
+ if (!IS_SUPPRESSED_WARNING(__func__)) { \
+ __warn_printk(arg); \
+ __WARN_FLAGS(BUGFLAG_NO_CUT_HERE | BUGFLAG_TAINT(taint));\
+ } \
instrumentation_end(); \
} while (0)
#define WARN_ON_ONCE(condition) ({ \
diff --git a/include/kunit/bug.h b/include/kunit/bug.h
new file mode 100644
index 00000000000000..72e9fb23bbd56f
--- /dev/null
+++ b/include/kunit/bug.h
@@ -0,0 +1,56 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * KUnit helpers for backtrace suppression
+ *
+ * Copyright (c) 2024 Guenter Roeck <linux@roeck-us.net>
+ */
+
+#ifndef _KUNIT_BUG_H
+#define _KUNIT_BUG_H
+
+#ifndef __ASSEMBLY__
+
+#include <linux/kconfig.h>
+
+#ifdef CONFIG_KUNIT_SUPPRESS_BACKTRACE
+
+#include <linux/stringify.h>
+#include <linux/types.h>
+
+struct __suppressed_warning {
+ struct list_head node;
+ const char *function;
+ int counter;
+};
+
+void __start_suppress_warning(struct __suppressed_warning *warning);
+void __end_suppress_warning(struct __suppressed_warning *warning);
+bool __is_suppressed_warning(const char *function);
+
+#define DEFINE_SUPPRESSED_WARNING(func) \
+ struct __suppressed_warning __kunit_suppress_##func = \
+ { .function = __stringify(func), .counter = 0 }
+
+#define START_SUPPRESSED_WARNING(func) \
+ __start_suppress_warning(&__kunit_suppress_##func)
+
+#define END_SUPPRESSED_WARNING(func) \
+ __end_suppress_warning(&__kunit_suppress_##func)
+
+#define IS_SUPPRESSED_WARNING(func) \
+ __is_suppressed_warning(func)
+
+#define SUPPRESSED_WARNING_COUNT(func) \
+ (__kunit_suppress_##func.counter)
+
+#else /* CONFIG_KUNIT_SUPPRESS_BACKTRACE */
+
+#define DEFINE_SUPPRESSED_WARNING(func)
+#define START_SUPPRESSED_WARNING(func)
+#define END_SUPPRESSED_WARNING(func)
+#define IS_SUPPRESSED_WARNING(func) (false)
+#define SUPPRESSED_WARNING_COUNT(func) (0)
+
+#endif /* CONFIG_KUNIT_SUPPRESS_BACKTRACE */
+#endif /* __ASSEMBLY__ */
+#endif /* _KUNIT_BUG_H */
diff --git a/include/kunit/test.h b/include/kunit/test.h
index 61637ef323026c..d0c44594d34cb9 100644
--- a/include/kunit/test.h
+++ b/include/kunit/test.h
@@ -10,6 +10,7 @@
#define _KUNIT_TEST_H
#include <kunit/assert.h>
+#include <kunit/bug.h>
#include <kunit/try-catch.h>
#include <linux/args.h>
diff --git a/include/linux/bug.h b/include/linux/bug.h
index 348acf2558f3c1..c668762dc76a5d 100644
--- a/include/linux/bug.h
+++ b/include/linux/bug.h
@@ -36,6 +36,9 @@ static inline int is_warning_bug(const struct bug_entry *bug)
return bug->flags & BUGFLAG_WARNING;
}
+void bug_get_file_function_line(struct bug_entry *bug, const char **file,
+ const char **function, unsigned int *line);
+
void bug_get_file_line(struct bug_entry *bug, const char **file,
unsigned int *line);
@@ -62,6 +65,16 @@ static inline enum bug_trap_type report_bug(unsigned long bug_addr,
}
struct bug_entry;
+static inline void bug_get_file_function_line(struct bug_entry *bug,
+ const char **file,
+ const char **function,
+ unsigned int *line)
+{
+ *file = NULL;
+ *function = NULL;
+ *line = 0;
+}
+
static inline void bug_get_file_line(struct bug_entry *bug, const char **file,
unsigned int *line)
{
diff --git a/lib/bug.c b/lib/bug.c
index e0ff219899902f..aa8bb12b9809ee 100644
--- a/lib/bug.c
+++ b/lib/bug.c
@@ -26,6 +26,14 @@
when CONFIG_DEBUG_BUGVERBOSE is not enabled, so you must generate
the values accordingly.
+ 2a.Optionally implement support for the "function" entry in struct
+ bug_entry. This entry must point to the name of the function triggering
+ the warning or bug trap (normally __func__). This is only needed if
+ both CONFIG_DEBUG_BUGVERBOSE and CONFIG_KUNIT_SUPPRESS_BACKTRACE are
+ enabled and if the architecture wants to implement support for suppressing
+ warning backtraces. The architecture must define HAVE_BUG_FUNCTION if it
+ adds pointers to function names to struct bug_entry.
+
3. Implement the trap
- In the illegal instruction trap handler (typically), verify
that the fault was in kernel mode, and call report_bug()
@@ -127,14 +135,21 @@ static inline struct bug_entry *module_find_bug(unsigned long bugaddr)
}
#endif
-void bug_get_file_line(struct bug_entry *bug, const char **file,
- unsigned int *line)
+void bug_get_file_function_line(struct bug_entry *bug, const char **file,
+ const char **function, unsigned int *line)
{
+ *function = NULL;
#ifdef CONFIG_DEBUG_BUGVERBOSE
#ifdef CONFIG_GENERIC_BUG_RELATIVE_POINTERS
*file = (const char *)&bug->file_disp + bug->file_disp;
+#ifdef HAVE_BUG_FUNCTION
+ *function = (const char *)&bug->function_disp + bug->function_disp;
+#endif
#else
*file = bug->file;
+#ifdef HAVE_BUG_FUNCTION
+ *function = bug->function;
+#endif
#endif
*line = bug->line;
#else
@@ -143,6 +158,13 @@ void bug_get_file_line(struct bug_entry *bug, const char **file,
#endif
}
+void bug_get_file_line(struct bug_entry *bug, const char **file, unsigned int *line)
+{
+ const char *function;
+
+ bug_get_file_function_line(bug, file, &function, line);
+}
+
struct bug_entry *find_bug(unsigned long bugaddr)
{
struct bug_entry *bug;
@@ -157,8 +179,9 @@ struct bug_entry *find_bug(unsigned long bugaddr)
static enum bug_trap_type __report_bug(unsigned long bugaddr, struct pt_regs *regs)
{
struct bug_entry *bug;
- const char *file;
+ const char *file, *function;
unsigned line, warning, once, done;
+ char __maybe_unused sym[KSYM_SYMBOL_LEN];
if (!is_valid_bugaddr(bugaddr))
return BUG_TRAP_TYPE_NONE;
@@ -169,12 +192,32 @@ static enum bug_trap_type __report_bug(unsigned long bugaddr, struct pt_regs *re
disable_trace_on_warning();
- bug_get_file_line(bug, &file, &line);
+ bug_get_file_function_line(bug, &file, &function, &line);
+#if defined(CONFIG_KUNIT_SUPPRESS_BACKTRACE) && defined(CONFIG_KALLSYMS)
+ if (!function) {
+ /*
+ * This will be seen if report_bug is called on an architecture
+ * with no architecture-specific support for suppressing warning
+ * backtraces, if CONFIG_DEBUG_BUGVERBOSE is not enabled, or if
+ * the calling code is from assembler which does not record a
+ * function name. Extracting the function name from the bug
+ * address is less than perfect since compiler optimization may
+ * result in 'bugaddr' pointing to a function which does not
+ * actually trigger the warning, but it is better than no
+ * suppression at all.
+ */
+ sprint_symbol_no_offset(sym, bugaddr);
+ function = sym;
+ }
+#endif /* defined(CONFIG_KUNIT_SUPPRESS_BACKTRACE) && defined(CONFIG_KALLSYMS) */
warning = (bug->flags & BUGFLAG_WARNING) != 0;
once = (bug->flags & BUGFLAG_ONCE) != 0;
done = (bug->flags & BUGFLAG_DONE) != 0;
+ if (warning && IS_SUPPRESSED_WARNING(function))
+ return BUG_TRAP_TYPE_WARN;
+
if (warning && once) {
if (done)
return BUG_TRAP_TYPE_WARN;
diff --git a/lib/kunit/Kconfig b/lib/kunit/Kconfig
index 68a6daec0aef18..b1b899265accd1 100644
--- a/lib/kunit/Kconfig
+++ b/lib/kunit/Kconfig
@@ -15,6 +15,15 @@ menuconfig KUNIT
if KUNIT
+config KUNIT_SUPPRESS_BACKTRACE
+ bool "KUnit - Enable backtrace suppression"
+ default y
+ help
+ Enable backtrace suppression for KUnit. If enabled, backtraces
+ generated intentionally by KUnit tests are suppressed. Disable
+ to reduce kernel image size if image size is more important than
+ suppression of backtraces generated by KUnit tests.
+
config KUNIT_DEBUGFS
bool "KUnit - Enable /sys/kernel/debug/kunit debugfs representation" if !KUNIT_ALL_TESTS
default KUNIT_ALL_TESTS
diff --git a/lib/kunit/Makefile b/lib/kunit/Makefile
index 309659a32a7841..e68399bc5b3fcb 100644
--- a/lib/kunit/Makefile
+++ b/lib/kunit/Makefile
@@ -14,10 +14,15 @@ ifeq ($(CONFIG_KUNIT_DEBUGFS),y)
kunit-objs += debugfs.o
endif
-# KUnit 'hooks' are built-in even when KUnit is built as a module.
+# KUnit 'hooks' and bug handling are built-in even when KUnit is built
+# as a module.
obj-y += hooks.o
+obj-$(CONFIG_KUNIT_SUPPRESS_BACKTRACE) += bug.o
obj-$(CONFIG_KUNIT_TEST) += kunit-test.o
+ifeq ($(CONFIG_KUNIT_SUPPRESS_BACKTRACE),y)
+obj-$(CONFIG_KUNIT_TEST) += backtrace-suppression-test.o
+endif
# string-stream-test compiles built-in only.
ifeq ($(CONFIG_KUNIT_TEST),y)
diff --git a/lib/kunit/backtrace-suppression-test.c b/lib/kunit/backtrace-suppression-test.c
new file mode 100644
index 00000000000000..47c619283802c0
--- /dev/null
+++ b/lib/kunit/backtrace-suppression-test.c
@@ -0,0 +1,104 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * KUnit test for suppressing warning tracebacks
+ *
+ * Copyright (C) 2024, Guenter Roeck
+ * Author: Guenter Roeck <linux@roeck-us.net>
+ */
+
+#include <kunit/test.h>
+#include <linux/bug.h>
+
+static void backtrace_suppression_test_warn_direct(struct kunit *test)
+{
+ DEFINE_SUPPRESSED_WARNING(backtrace_suppression_test_warn_direct);
+
+ START_SUPPRESSED_WARNING(backtrace_suppression_test_warn_direct);
+ WARN(1, "This backtrace should be suppressed");
+ END_SUPPRESSED_WARNING(backtrace_suppression_test_warn_direct);
+
+ KUNIT_EXPECT_EQ(test, SUPPRESSED_WARNING_COUNT(backtrace_suppression_test_warn_direct), 1);
+}
+
+static void trigger_backtrace_warn(void)
+{
+ WARN(1, "This backtrace should be suppressed");
+}
+
+static void backtrace_suppression_test_warn_indirect(struct kunit *test)
+{
+ DEFINE_SUPPRESSED_WARNING(trigger_backtrace_warn);
+
+ START_SUPPRESSED_WARNING(trigger_backtrace_warn);
+ trigger_backtrace_warn();
+ END_SUPPRESSED_WARNING(trigger_backtrace_warn);
+
+ KUNIT_EXPECT_EQ(test, SUPPRESSED_WARNING_COUNT(trigger_backtrace_warn), 1);
+}
+
+static void backtrace_suppression_test_warn_multi(struct kunit *test)
+{
+ DEFINE_SUPPRESSED_WARNING(trigger_backtrace_warn);
+ DEFINE_SUPPRESSED_WARNING(backtrace_suppression_test_warn_multi);
+
+ START_SUPPRESSED_WARNING(backtrace_suppression_test_warn_multi);
+ START_SUPPRESSED_WARNING(trigger_backtrace_warn);
+ WARN(1, "This backtrace should be suppressed");
+ trigger_backtrace_warn();
+ END_SUPPRESSED_WARNING(trigger_backtrace_warn);
+ END_SUPPRESSED_WARNING(backtrace_suppression_test_warn_multi);
+
+ KUNIT_EXPECT_EQ(test, SUPPRESSED_WARNING_COUNT(backtrace_suppression_test_warn_multi), 1);
+ KUNIT_EXPECT_EQ(test, SUPPRESSED_WARNING_COUNT(trigger_backtrace_warn), 1);
+}
+
+static void backtrace_suppression_test_warn_on_direct(struct kunit *test)
+{
+ DEFINE_SUPPRESSED_WARNING(backtrace_suppression_test_warn_on_direct);
+
+ if (!IS_ENABLED(CONFIG_DEBUG_BUGVERBOSE) && !IS_ENABLED(CONFIG_KALLSYMS))
+ kunit_skip(test, "requires CONFIG_DEBUG_BUGVERBOSE or CONFIG_KALLSYMS");
+
+ START_SUPPRESSED_WARNING(backtrace_suppression_test_warn_on_direct);
+ WARN_ON(1);
+ END_SUPPRESSED_WARNING(backtrace_suppression_test_warn_on_direct);
+
+ KUNIT_EXPECT_EQ(test,
+ SUPPRESSED_WARNING_COUNT(backtrace_suppression_test_warn_on_direct), 1);
+}
+
+static void trigger_backtrace_warn_on(void)
+{
+ WARN_ON(1);
+}
+
+static void backtrace_suppression_test_warn_on_indirect(struct kunit *test)
+{
+ DEFINE_SUPPRESSED_WARNING(trigger_backtrace_warn_on);
+
+ if (!IS_ENABLED(CONFIG_DEBUG_BUGVERBOSE))
+ kunit_skip(test, "requires CONFIG_DEBUG_BUGVERBOSE");
+
+ START_SUPPRESSED_WARNING(trigger_backtrace_warn_on);
+ trigger_backtrace_warn_on();
+ END_SUPPRESSED_WARNING(trigger_backtrace_warn_on);
+
+ KUNIT_EXPECT_EQ(test, SUPPRESSED_WARNING_COUNT(trigger_backtrace_warn_on), 1);
+}
+
+static struct kunit_case backtrace_suppression_test_cases[] = {
+ KUNIT_CASE(backtrace_suppression_test_warn_direct),
+ KUNIT_CASE(backtrace_suppression_test_warn_indirect),
+ KUNIT_CASE(backtrace_suppression_test_warn_multi),
+ KUNIT_CASE(backtrace_suppression_test_warn_on_direct),
+ KUNIT_CASE(backtrace_suppression_test_warn_on_indirect),
+ {}
+};
+
+static struct kunit_suite backtrace_suppression_test_suite = {
+ .name = "backtrace-suppression-test",
+ .test_cases = backtrace_suppression_test_cases,
+};
+kunit_test_suites(&backtrace_suppression_test_suite);
+
+MODULE_LICENSE("GPL");
diff --git a/lib/kunit/bug.c b/lib/kunit/bug.c
new file mode 100644
index 00000000000000..13b3d896c11488
--- /dev/null
+++ b/lib/kunit/bug.c
@@ -0,0 +1,42 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * KUnit helpers for backtrace suppression
+ *
+ * Copyright (c) 2024 Guenter Roeck <linux@roeck-us.net>
+ */
+
+#include <kunit/bug.h>
+#include <linux/export.h>
+#include <linux/list.h>
+#include <linux/string.h>
+
+static LIST_HEAD(suppressed_warnings);
+
+void __start_suppress_warning(struct __suppressed_warning *warning)
+{
+ list_add(&warning->node, &suppressed_warnings);
+}
+EXPORT_SYMBOL_GPL(__start_suppress_warning);
+
+void __end_suppress_warning(struct __suppressed_warning *warning)
+{
+ list_del(&warning->node);
+}
+EXPORT_SYMBOL_GPL(__end_suppress_warning);
+
+bool __is_suppressed_warning(const char *function)
+{
+ struct __suppressed_warning *warning;
+
+ if (!function)
+ return false;
+
+ list_for_each_entry(warning, &suppressed_warnings, node) {
+ if (!strcmp(function, warning->function)) {
+ warning->counter++;
+ return true;
+ }
+ }
+ return false;
+}
+EXPORT_SYMBOL_GPL(__is_suppressed_warning);