Commit 58e2847a authored by Ryan Roberts's avatar Ryan Roberts Committed by Andrew Morton

selftests: line buffer test program's stdout

Patch series "selftests/mm fixes for arm64", v3.

Given my on-going work on large anon folios and contpte mappings, I
decided it would be a good idea to start running mm selftests to help
guard against regressions.  However, it soon became clear that I
couldn't get the suite to run cleanly on arm64 with a vanilla v6.5-rc1
kernel (perhaps I'm just doing it wrong??), so got stuck in a rabbit
hole trying to debug and fix all the issues.  Some were down to
misconfigurations, but I also found a number of issues with the tests
and even a couple of issues with the kernel.


This patch (of 8):

The selftests runner pipes the test program's stdout to tap_prefix.  The
presence of the pipe means that the test program sets its stdout to be
fully buffered (as aposed to line buffered when directly connected to the
terminal).  The block buffering means that there is often content in the
buffer at fork() time, which causes the output to end up duplicated.  This
was causing problems for mm:cow where test results were duplicated 20-30x.

Solve this by using `stdbuf`, when available to force the test program to
use line buffered mode.  This means previously printf'ed results are
flushed out of the program before any fork().

Additionally, explicitly set line buffer mode in ksft_print_header(),
which means that all test programs that use the ksft framework will
benefit even if stdbuf is not present on the system.

[ryan.roberts@arm.com: add setvbuf() to set buffering mode]
  Link: https://lkml.kernel.org/r/20230726070655.2713530-1-ryan.roberts@arm.com
Link: https://lkml.kernel.org/r/20230724082522.1202616-1-ryan.roberts@arm.com
Link: https://lkml.kernel.org/r/20230724082522.1202616-2-ryan.roberts@arm.comSigned-off-by: default avatarRyan Roberts <ryan.roberts@arm.com>
Reviewed-by: default avatarMark Brown <broonie@kernel.org>
Cc: David Hildenbrand <david@redhat.com>
Cc: Florent Revest <revest@chromium.org>
Cc: Jérôme Glisse <jglisse@redhat.com>
Cc: John Hubbard <jhubbard@nvidia.com>
Cc: Peter Xu <peterx@redhat.com>
Cc: Shuah Khan <shuah@kernel.org>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
parent ea09800b
...@@ -21,6 +21,11 @@ ...@@ -21,6 +21,11 @@
#define EOF (-1) #define EOF (-1)
#endif #endif
/* Buffering mode used by setvbuf. */
#define _IOFBF 0 /* Fully buffered. */
#define _IOLBF 1 /* Line buffered. */
#define _IONBF 2 /* No buffering. */
/* just define FILE as a non-empty type. The value of the pointer gives /* just define FILE as a non-empty type. The value of the pointer gives
* the FD: FILE=~fd for fd>=0 or NULL for fd<0. This way positive FILE * the FD: FILE=~fd for fd>=0 or NULL for fd<0. This way positive FILE
* are immediately identified as abnormal entries (i.e. possible copies * are immediately identified as abnormal entries (i.e. possible copies
...@@ -350,6 +355,25 @@ void perror(const char *msg) ...@@ -350,6 +355,25 @@ void perror(const char *msg)
fprintf(stderr, "%s%serrno=%d\n", (msg && *msg) ? msg : "", (msg && *msg) ? ": " : "", errno); fprintf(stderr, "%s%serrno=%d\n", (msg && *msg) ? msg : "", (msg && *msg) ? ": " : "", errno);
} }
static __attribute__((unused))
int setvbuf(FILE *stream, char *buf, int mode, size_t size)
{
/*
* nolibc does not support buffering so this is a nop. Just check mode
* is valid as required by the spec.
*/
switch (mode) {
case _IOFBF:
case _IOLBF:
case _IONBF:
break;
default:
return EOF;
}
return 0;
}
/* make sure to include all global symbols */ /* make sure to include all global symbols */
#include "nolibc.h" #include "nolibc.h"
......
...@@ -113,6 +113,15 @@ static inline int ksft_get_error_cnt(void) { return ksft_cnt.ksft_error; } ...@@ -113,6 +113,15 @@ static inline int ksft_get_error_cnt(void) { return ksft_cnt.ksft_error; }
static inline void ksft_print_header(void) static inline void ksft_print_header(void)
{ {
/*
* Force line buffering; If stdout is not connected to a terminal, it
* will otherwise default to fully buffered, which can cause output
* duplication if there is content in the buffer when fork()ing. If
* there is a crash, line buffering also means the most recent output
* line will be visible.
*/
setvbuf(stdout, NULL, _IOLBF, 0);
if (!(getenv("KSFT_TAP_LEVEL"))) if (!(getenv("KSFT_TAP_LEVEL")))
printf("TAP version 13\n"); printf("TAP version 13\n");
} }
......
...@@ -105,15 +105,18 @@ run_one() ...@@ -105,15 +105,18 @@ run_one()
echo "# Warning: file $TEST is missing!" echo "# Warning: file $TEST is missing!"
echo "not ok $test_num $TEST_HDR_MSG" echo "not ok $test_num $TEST_HDR_MSG"
else else
if [ -x /usr/bin/stdbuf ]; then
stdbuf="/usr/bin/stdbuf --output=L "
fi
eval kselftest_cmd_args="\$${kselftest_cmd_args_ref:-}" eval kselftest_cmd_args="\$${kselftest_cmd_args_ref:-}"
cmd="./$BASENAME_TEST $kselftest_cmd_args" cmd="$stdbuf ./$BASENAME_TEST $kselftest_cmd_args"
if [ ! -x "$TEST" ]; then if [ ! -x "$TEST" ]; then
echo "# Warning: file $TEST is not executable" echo "# Warning: file $TEST is not executable"
if [ $(head -n 1 "$TEST" | cut -c -2) = "#!" ] if [ $(head -n 1 "$TEST" | cut -c -2) = "#!" ]
then then
interpreter=$(head -n 1 "$TEST" | cut -c 3-) interpreter=$(head -n 1 "$TEST" | cut -c 3-)
cmd="$interpreter ./$BASENAME_TEST" cmd="$stdbuf $interpreter ./$BASENAME_TEST"
else else
echo "not ok $test_num $TEST_HDR_MSG" echo "not ok $test_num $TEST_HDR_MSG"
return return
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment