diff options
-rw-r--r-- | lib/Makefile.sources | 2 | ||||
-rw-r--r-- | lib/igt_taints.c | 104 | ||||
-rw-r--r-- | lib/igt_taints.h | 19 | ||||
-rw-r--r-- | lib/meson.build | 1 | ||||
-rw-r--r-- | runner/executor.c | 73 |
5 files changed, 140 insertions, 59 deletions
diff --git a/lib/Makefile.sources b/lib/Makefile.sources index 67b38645..7102f95e 100644 --- a/lib/Makefile.sources +++ b/lib/Makefile.sources @@ -64,6 +64,8 @@ lib_source_list = \ igt_sysfs.h \ igt_sysrq.c \ igt_sysrq.h \ + igt_taints.c \ + igt_taints.h \ igt_thread.c \ igt_thread.h \ igt_x86.h \ diff --git a/lib/igt_taints.c b/lib/igt_taints.c new file mode 100644 index 00000000..6f5b827c --- /dev/null +++ b/lib/igt_taints.c @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright © 2021 Intel Corporation + */ + +#include <stdio.h> + +#include "igt_taints.h" + +/* see Linux's include/linux/kernel.h */ +static const struct { + int bit; + int bad; + const char *explanation; +} abort_taints[] = { + { 5, 1, "TAINT_BAD_PAGE: Bad page reference or an unexpected page flags." }, + { 7, 1, "TAINT_DIE: Kernel has died - BUG/OOPS." }, + { 9, 1, "TAINT_WARN: WARN_ON has happened." }, + { -1 } +}; + +/** + * igt_explain_taints: + * @taints: mask of taints requiring an explanation [inout] + * + * Inspects the mask and looks up the first reason corresponding to a set + * bit in the mask. It returns the reason as a string constant, and removes + * the bit from the mask. If the mask is empty, or we have no known reason + * matching the mask, NULL is returned. + * + * This may be used in a loop to extract all known reasons for why the + * kernel is tainted: + * + * while (reason = igt_explain_taints(&taints)) + * igt_info("%s", reason); + * + * Returns the first reason corresponding to a taint bit. + */ +const char *igt_explain_taints(unsigned long *taints) +{ + for (typeof(*abort_taints) *taint = abort_taints; + taint->bit >= 0; + taint++) { + if (*taints & (1ul << taint->bit)) { + *taints &= ~(1ul << taint->bit); + return taint->explanation; + } + } + + return NULL; +} + +/** + * igt_bad_taints: + * + * Returns the mask of kernel taints that IGT considers fatal. + * Such as TAINT_WARN set when the kernel oopses. + */ +unsigned long igt_bad_taints(void) +{ + static unsigned long bad_taints; + + if (!bad_taints) { + for (typeof(*abort_taints) *taint = abort_taints; + taint->bit >= 0; + taint++) { + if (taint->bad) + bad_taints |= 1ul << taint->bit; + } + } + + return bad_taints; +} + +/** + * igt_kernel_tainted: + * @taints: bitmask of kernel taints [out] + * + * Reads the bitmask of kernel taints from "/proc/sys/kernel/tainted", + * see linux/kernel.h for the full set of flags. These are set whenever + * the kernel encounters an exceptional condition that may impair functionality. + * The kernel only sets the taint once, and so once a "fatal" condition has + * been encountered, it is generally not advisable to continue testing, as at + * least all future taint reporting will be lost. + * + * igt_kernel_tainted() returns the set of _all_ taints reported via @taints, + * and also the set of _fatal_ taints as its return value. + * + * Returns a mask of fatal taints; 0 if untainted. + */ +unsigned long igt_kernel_tainted(unsigned long *taints) +{ + FILE *f; + + *taints = 0; + + f = fopen("/proc/sys/kernel/tainted", "r"); + if (f) { + fscanf(f, "%lu", taints); + fclose(f); + } + + return is_tainted(*taints); +} diff --git a/lib/igt_taints.h b/lib/igt_taints.h new file mode 100644 index 00000000..be4195c5 --- /dev/null +++ b/lib/igt_taints.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright © 2021 Intel Corporation + */ + +#ifndef __IGT_TAINTS_H__ +#define __IGT_TAINTS_H__ + +unsigned long igt_kernel_tainted(unsigned long *taints); +const char *igt_explain_taints(unsigned long *taints); + +unsigned long igt_bad_taints(void); + +static inline unsigned long is_tainted(unsigned long taints) +{ + return taints & igt_bad_taints(); +} + +#endif /* __IGT_TAINTS_H__ */ diff --git a/lib/meson.build b/lib/meson.build index 540facb2..3abc42cb 100644 --- a/lib/meson.build +++ b/lib/meson.build @@ -27,6 +27,7 @@ lib_sources = [ 'igt_syncobj.c', 'igt_sysfs.c', 'igt_sysrq.c', + 'igt_taints.c', 'igt_thread.c', 'igt_vec.c', 'igt_vgem.c', diff --git a/runner/executor.c b/runner/executor.c index faf272d8..93db8bb3 100644 --- a/runner/executor.c +++ b/runner/executor.c @@ -24,6 +24,7 @@ #include "igt_aux.h" #include "igt_core.h" +#include "igt_taints.h" #include "executor.h" #include "output_strings.h" @@ -307,70 +308,23 @@ static char *handle_lockdep(void) return NULL; } -/* see Linux's include/linux/kernel.h */ -static const struct { - unsigned long bit; - const char *explanation; -} abort_taints[] = { - {(1 << 5), "TAINT_BAD_PAGE: Bad page reference or an unexpected page flags."}, - {(1 << 7), "TAINT_DIE: Kernel has died - BUG/OOPS."}, - {(1 << 9), "TAINT_WARN: WARN_ON has happened."}, - {0, 0}}; - -static unsigned long bad_taints(void) -{ - static unsigned long __bad_taints; - - if (!__bad_taints) { - for (typeof(*abort_taints) *taint = abort_taints; - taint->bit; - taint++) - __bad_taints |= taint->bit; - } - - return __bad_taints; -} - -static unsigned long is_tainted(unsigned long taints) -{ - return taints & bad_taints(); -} - -static unsigned long tainted(unsigned long *taints) -{ - FILE *f; - - *taints = 0; - - f = fopen("/proc/sys/kernel/tainted", "r"); - if (f) { - fscanf(f, "%lu", taints); - fclose(f); - } - - return is_tainted(*taints); -} - static char *handle_taint(void) { - unsigned long taints; + unsigned long taints, bad; + char *explain; char *reason; - if (!tainted(&taints)) + bad = igt_kernel_tainted(&taints); + if (!bad) return NULL; - asprintf(&reason, "Kernel badly tainted (%#lx) (check dmesg for details):\n", - taints); - - for (typeof(*abort_taints) *taint = abort_taints; taint->bit; taint++) { - if (taint->bit & taints) { - char *old_reason = reason; - asprintf(&reason, "%s\t(%#lx) %s\n", - old_reason, - taint->bit, - taint->explanation); - free(old_reason); - } + asprintf(&reason, "Kernel badly tainted (%#lx, %#lx) (check dmesg for details):\n", + taints, bad); + + while ((explain = igt_explain_taints(&bad))) { + char *old_reason = reason; + asprintf(&reason, "%s\t%s\n", old_reason, explain); + free(old_reason); } return reason; @@ -1142,7 +1096,8 @@ static int monitor_output(pid_t child, sigfd = -1; /* we are dying, no signal handling for now */ } - timeout_reason = need_to_timeout(settings, killed, tainted(&taints), + timeout_reason = need_to_timeout(settings, killed, + igt_kernel_tainted(&taints), igt_time_elapsed(&time_last_activity, &time_now), igt_time_elapsed(&time_last_subtest, &time_now), igt_time_elapsed(&time_killed, &time_now), |