2022-08-31 18:33:29

by Paul E. McKenney

[permalink] [raw]
Subject: [PATCH nolibc 0/18] nolibc updates for v6.1

Hello!

This series provides nolibc updates for v6.1, most notably greatly
improved testing in tools/testing/selftests/nolibc. See "make help"
for details.

1. make argc 32-bit in riscv startup code, courtesy of Willy Tarreau.

2. Optionally flush printk() buffers before powering off.

3. Use the barrier operation specified by cur_ops, courtesy of
Zqiang.

4. fix build warning in sys_mmap() when my_syscall6 is not defined,
courtesy of Willy Tarreau.

5. make sys_mmap() automatically use the right __NR_mmap definition,
courtesy of Willy Tarreau.

6. add basic infrastructure to ease creation of nolibc tests,
courtesy of Willy Tarreau.

7. support a test definition format, courtesy of Willy Tarreau.

8. implement a few tests for various syscalls, courtesy of Willy
Tarreau.

9. add a few tests for some libc functions, courtesy of Willy
Tarreau.

10. exit with poweroff on success when getpid() == 1, courtesy of
Willy Tarreau.

11. on x86, support exiting with isa-debug-exit, courtesy of Willy
Tarreau.

12. recreate and populate /dev and /proc if missing, courtesy of
Willy Tarreau.

13. condition some tests on /proc existence, courtesy of Willy
Tarreau.

14. support glibc as well, courtesy of Willy Tarreau.

15. add a "kernel" target to build the kernel with the initramfs,
courtesy of Willy Tarreau.

16. add a "defconfig" target, courtesy of Willy Tarreau.

17. add a "run" target to start the kernel in QEMU, courtesy of
Willy Tarreau.

18. "sysroot" target installs a local copy of the sysroot, courtesy
of Willy Tarreau.

19. add a "help" target, courtesy of Willy Tarreau.

20. Avoid generated files being committed, courtesy of Fernanda
Ma'rouf.

Thanx, Paul

------------------------------------------------------------------------

b/Documentation/admin-guide/kernel-parameters.txt | 6
b/MAINTAINERS | 1
b/kernel/rcu/rcutorture.c | 4
b/kernel/torture.c | 7
b/tools/include/nolibc/arch-riscv.h | 2
b/tools/include/nolibc/sys.h | 2
b/tools/testing/selftests/nolibc/.gitignore | 4
b/tools/testing/selftests/nolibc/Makefile | 43 ++
b/tools/testing/selftests/nolibc/nolibc-test.c | 395 ++++++++++++++++++++++
tools/include/nolibc/sys.h | 2
tools/testing/selftests/nolibc/Makefile | 98 +++++
tools/testing/selftests/nolibc/nolibc-test.c | 376 ++++++++++++++++++++
12 files changed, 925 insertions(+), 15 deletions(-)


2022-08-31 18:35:25

by Paul E. McKenney

[permalink] [raw]
Subject: [PATCH nolibc 07/18] selftests/nolibc: add a few tests for some libc functions

From: Willy Tarreau <[email protected]>

The test series called "stdlib" covers some libc functions (string,
stdlib etc). By default they are automatically run after "syscall"
but may be requested in argument or in variable NOLIBC_TEST.

Signed-off-by: Willy Tarreau <[email protected]>
Signed-off-by: Paul E. McKenney <[email protected]>
---
tools/testing/selftests/nolibc/nolibc-test.c | 35 ++++++++++++++++++++
1 file changed, 35 insertions(+)

diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/selftests/nolibc/nolibc-test.c
index dc87832912ce4..b928f099431f7 100644
--- a/tools/testing/selftests/nolibc/nolibc-test.c
+++ b/tools/testing/selftests/nolibc/nolibc-test.c
@@ -496,11 +496,46 @@ int run_syscall(int min, int max)
return ret;
}

+int run_stdlib(int min, int max)
+{
+ int test;
+ int tmp;
+ int ret = 0;
+ void *p1, *p2;
+
+ for (test = min; test >= 0 && test <= max; test++) {
+ int llen = 0; // line length
+
+ /* avoid leaving empty lines below, this will insert holes into
+ * test numbers.
+ */
+ switch (test + __LINE__ + 1) {
+ CASE_TEST(getenv_TERM); EXPECT_STRNZ(1, getenv("TERM")); break;
+ CASE_TEST(getenv_blah); EXPECT_STRZR(1, getenv("blah")); break;
+ CASE_TEST(setcmp_blah_blah); EXPECT_EQ(1, strcmp("blah", "blah"), 0); break;
+ CASE_TEST(setcmp_blah_blah2); EXPECT_NE(1, strcmp("blah", "blah2"), 0); break;
+ CASE_TEST(setncmp_blah_blah); EXPECT_EQ(1, strncmp("blah", "blah", 10), 0); break;
+ CASE_TEST(setncmp_blah_blah4); EXPECT_EQ(1, strncmp("blah", "blah4", 4), 0); break;
+ CASE_TEST(setncmp_blah_blah5); EXPECT_NE(1, strncmp("blah", "blah5", 5), 0); break;
+ CASE_TEST(setncmp_blah_blah6); EXPECT_NE(1, strncmp("blah", "blah6", 6), 0); break;
+ CASE_TEST(strchr_foobar_o); EXPECT_STREQ(1, strchr("foobar", 'o'), "oobar"); break;
+ CASE_TEST(strchr_foobar_z); EXPECT_STRZR(1, strchr("foobar", 'z')); break;
+ CASE_TEST(strrchr_foobar_o); EXPECT_STREQ(1, strrchr("foobar", 'o'), "obar"); break;
+ CASE_TEST(strrchr_foobar_z); EXPECT_STRZR(1, strrchr("foobar", 'z')); break;
+ case __LINE__:
+ return ret; /* must be last */
+ /* note: do not set any defaults so as to permit holes above */
+ }
+ }
+ return ret;
+}
+

/* This is the definition of known test names, with their functions */
static struct test test_names[] = {
/* add new tests here */
{ .name = "syscall", .func = run_syscall },
+ { .name = "stdlib", .func = run_stdlib },
{ 0 }
};

--
2.31.1.189.g2e36527f23

2022-08-31 18:35:26

by Paul E. McKenney

[permalink] [raw]
Subject: [PATCH rcu 2/2] rcutorture: Use the barrier operation specified by cur_ops

From: Zqiang <[email protected]>

The rcutorture_oom_notify() function unconditionally invokes
rcu_barrier(), which is OK when the rcutorture.torture_type value is
"rcu", but unhelpful otherwise. The purpose of these barrier calls is to
wait for all outstanding callback-flooding callbacks to be invoked before
cleaning up their data. Using the wrong barrier function therefore
risks arbitrary memory corruption. Thus, this commit changes these
rcu_barrier() calls into cur_ops->cb_barrier() to make things work when
torturing non-vanilla flavors of RCU.

Signed-off-by: Zqiang <[email protected]>
Signed-off-by: Paul E. McKenney <[email protected]>
---
kernel/rcu/rcutorture.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/kernel/rcu/rcutorture.c b/kernel/rcu/rcutorture.c
index d8e1b270a065f..08b7b59d5d05b 100644
--- a/kernel/rcu/rcutorture.c
+++ b/kernel/rcu/rcutorture.c
@@ -2600,12 +2600,12 @@ static int rcutorture_oom_notify(struct notifier_block *self,
for (i = 0; i < fwd_progress; i++)
ncbs += rcu_torture_fwd_prog_cbfree(&rfp[i]);
pr_info("%s: Freed %lu RCU callbacks.\n", __func__, ncbs);
- rcu_barrier();
+ cur_ops->cb_barrier();
ncbs = 0;
for (i = 0; i < fwd_progress; i++)
ncbs += rcu_torture_fwd_prog_cbfree(&rfp[i]);
pr_info("%s: Freed %lu RCU callbacks.\n", __func__, ncbs);
- rcu_barrier();
+ cur_ops->cb_barrier();
ncbs = 0;
for (i = 0; i < fwd_progress; i++)
ncbs += rcu_torture_fwd_prog_cbfree(&rfp[i]);
--
2.31.1.189.g2e36527f23

2022-08-31 18:37:10

by Paul E. McKenney

[permalink] [raw]
Subject: [PATCH nolibc 15/18] selftests/nolibc: add a "run" target to start the kernel in QEMU

From: Willy Tarreau <[email protected]>

The "run" target will build the kernel and start it in QEMU. The
"rerun" target will not have the kernel dependency and will just try
to start QEMU. The QEMU architecture used to start the kernel is
derived from the configured ARCH. This might need to be improved
for archs which include different variants under the same name
(mips vs mipsel, +/-64, riscv32 vs riscv64). This could be tested
for i386, x86, arm, arm64, mips and riscv (the later two reporting
issues on some tests).

It is possible to pass a test specification for nolibc-test in the TEST
variable, which will be passed as-is as NOLIBC_TEST.

On success, the number of successful tests is printed. On failure, failed
lines are individually printed.

Signed-off-by: Willy Tarreau <[email protected]>
Signed-off-by: Paul E. McKenney <[email protected]>
---
tools/testing/selftests/nolibc/Makefile | 33 +++++++++++++++++++++++++
1 file changed, 33 insertions(+)

diff --git a/tools/testing/selftests/nolibc/Makefile b/tools/testing/selftests/nolibc/Makefile
index c104719eae8b8..7c1f5360f454e 100644
--- a/tools/testing/selftests/nolibc/Makefile
+++ b/tools/testing/selftests/nolibc/Makefile
@@ -31,6 +31,27 @@ DEFCONFIG_mips = malta_defconfig
DEFCONFIG_riscv = defconfig
DEFCONFIG = $(DEFCONFIG_$(ARCH))

+# optional tests to run (default = all)
+TEST =
+
+# QEMU_ARCH: arch names used by qemu
+QEMU_ARCH_i386 = i386
+QEMU_ARCH_x86 = x86_64
+QEMU_ARCH_arm64 = aarch64
+QEMU_ARCH_arm = arm
+QEMU_ARCH_mips = mipsel # works with malta_defconfig
+QEMU_ARCH_riscv = riscv64
+QEMU_ARCH = $(QEMU_ARCH_$(ARCH))
+
+# QEMU_ARGS : some arch-specific args to pass to qemu
+QEMU_ARGS_i386 = -M pc -append "console=ttyS0,9600 i8042.noaux panic=-1 $(TEST:%=NOLIBC_TEST=%)"
+QEMU_ARGS_x86 = -M pc -append "console=ttyS0,9600 i8042.noaux panic=-1 $(TEST:%=NOLIBC_TEST=%)"
+QEMU_ARGS_arm64 = -M virt -cpu cortex-a53 -append "panic=-1 $(TEST:%=NOLIBC_TEST=%)"
+QEMU_ARGS_arm = -M virt -append "panic=-1 $(TEST:%=NOLIBC_TEST=%)"
+QEMU_ARGS_mips = -M malta -append "panic=-1 $(TEST:%=NOLIBC_TEST=%)"
+QEMU_ARGS_riscv = -M virt -append "console=ttyS0 panic=-1 $(TEST:%=NOLIBC_TEST=%)"
+QEMU_ARGS = $(QEMU_ARGS_$(ARCH))
+
# OUTPUT is only set when run from the main makefile, otherwise
# it defaults to this nolibc directory.
OUTPUT ?= $(CURDIR)/
@@ -61,8 +82,20 @@ defconfig:
kernel: initramfs
$(Q)$(MAKE) -C $(srctree) ARCH=$(ARCH) CC=$(CC) CROSS_COMPILE=$(CROSS_COMPILE) $(IMAGE_NAME) CONFIG_INITRAMFS_SOURCE=$(CURDIR)/initramfs

+# run the tests after building the kernel
+run: kernel
+ $(Q)qemu-system-$(QEMU_ARCH) -display none -no-reboot -kernel "$(srctree)/$(IMAGE)" -serial stdio $(QEMU_ARGS) > "$(CURDIR)/run.out"
+ $(Q)grep -w FAIL "$(CURDIR)/run.out" && echo "See all results in $(CURDIR)/run.out" || echo "$$(grep -c ^[0-9].*OK $(CURDIR)/run.out) test(s) passed."
+
+# re-run the tests from an existing kernel
+rerun:
+ $(Q)qemu-system-$(QEMU_ARCH) -display none -no-reboot -kernel "$(srctree)/$(IMAGE)" -serial stdio $(QEMU_ARGS) > "$(CURDIR)/run.out"
+ $(Q)grep -w FAIL "$(CURDIR)/run.out" && echo "See all results in $(CURDIR)/run.out" || echo "$$(grep -c ^[0-9].*OK $(CURDIR)/run.out) test(s) passed."
+
clean:
$(call QUIET_CLEAN, nolibc-test)
$(Q)rm -f nolibc-test
$(call QUIET_CLEAN, initramfs)
$(Q)rm -rf initramfs
+ $(call QUIET_CLEAN, run.out)
+ $(Q)rm -rf run.out
--
2.31.1.189.g2e36527f23

2022-08-31 18:39:14

by Paul E. McKenney

[permalink] [raw]
Subject: [PATCH nolibc 16/18] selftests/nolibc: "sysroot" target installs a local copy of the sysroot

From: Willy Tarreau <[email protected]>

It's not convenient to rely on a sysroot built in another directory,
especially when running cross-compilation tests, where one has to
switch back and forth between directories.

Let's make it possible to install the sysroot directly in the test
directory. It's not big and even benefits from being copied by arch
so that it's easier to switch between archs if needed. The new
"sysroot" target does this, it just calls "headers_standalone" from
nolibc to install the sysroot right here.

Signed-off-by: Willy Tarreau <[email protected]>
Signed-off-by: Paul E. McKenney <[email protected]>
---
tools/testing/selftests/nolibc/Makefile | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/tools/testing/selftests/nolibc/Makefile b/tools/testing/selftests/nolibc/Makefile
index 7c1f5360f454e..210f5369fdfc4 100644
--- a/tools/testing/selftests/nolibc/Makefile
+++ b/tools/testing/selftests/nolibc/Makefile
@@ -67,9 +67,16 @@ LDFLAGS := -s

all: nolibc-test

-nolibc-test: nolibc-test.c
+sysroot: sysroot/$(ARCH)/include
+
+sysroot/$(ARCH)/include:
+ $(QUIET_MKDIR)mkdir -p sysroot
+ $(Q)$(MAKE) -C ../../../include/nolibc ARCH=$(ARCH) OUTPUT=$(CURDIR)/sysroot/ headers_standalone
+ $(Q)mv sysroot/sysroot sysroot/$(ARCH)
+
+nolibc-test: nolibc-test.c sysroot/$(ARCH)/include
$(QUIET_CC)$(CC) $(CFLAGS) $(LDFLAGS) -o $@ \
- -nostdlib -static -include ../../../include/nolibc/nolibc.h $^ -lgcc
+ -nostdlib -static -Isysroot/$(ARCH)/include $< -lgcc

initramfs: nolibc-test
$(QUIET_MKDIR)mkdir -p initramfs
@@ -93,6 +100,8 @@ rerun:
$(Q)grep -w FAIL "$(CURDIR)/run.out" && echo "See all results in $(CURDIR)/run.out" || echo "$$(grep -c ^[0-9].*OK $(CURDIR)/run.out) test(s) passed."

clean:
+ $(call QUIET_CLEAN, sysroot)
+ $(Q)rm -rf sysroot
$(call QUIET_CLEAN, nolibc-test)
$(Q)rm -f nolibc-test
$(call QUIET_CLEAN, initramfs)
--
2.31.1.189.g2e36527f23

2022-08-31 19:00:02

by Paul E. McKenney

[permalink] [raw]
Subject: [PATCH nolibc 11/18] selftests/nolibc: condition some tests on /proc existence

From: Willy Tarreau <[email protected]>

If /proc is not available (program run inside a chroot or without
sufficient permissions), it's better to disable the associated tests.
Some will be preserved like the ones which check for a failure to
create some entries there since they're still supposed to fail.

Signed-off-by: Willy Tarreau <[email protected]>
Signed-off-by: Paul E. McKenney <[email protected]>
---
tools/testing/selftests/nolibc/nolibc-test.c | 14 +++++++++-----
1 file changed, 9 insertions(+), 5 deletions(-)

diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/selftests/nolibc/nolibc-test.c
index a697182c87f57..662dea691749c 100644
--- a/tools/testing/selftests/nolibc/nolibc-test.c
+++ b/tools/testing/selftests/nolibc/nolibc-test.c
@@ -413,11 +413,15 @@ int test_getdents64(const char *dir)
int run_syscall(int min, int max)
{
struct stat stat_buf;
+ int proc;
int test;
int tmp;
int ret = 0;
void *p1, *p2;

+ /* <proc> indicates whether or not /proc is mounted */
+ proc = stat("/proc", &stat_buf) == 0;
+
for (test = min; test >= 0 && test <= max; test++) {
int llen = 0; // line length

@@ -438,12 +442,12 @@ int run_syscall(int min, int max)
CASE_TEST(chdir_root); EXPECT_SYSZR(1, chdir("/")); break;
CASE_TEST(chdir_dot); EXPECT_SYSZR(1, chdir(".")); break;
CASE_TEST(chdir_blah); EXPECT_SYSER(1, chdir("/blah"), -1, ENOENT); break;
- CASE_TEST(chmod_net); EXPECT_SYSZR(1, chmod("/proc/self/net", 0555)); break;
- CASE_TEST(chmod_self); EXPECT_SYSER(1, chmod("/proc/self", 0555), -1, EPERM); break;
- CASE_TEST(chown_self); EXPECT_SYSER(1, chown("/proc/self", 0, 0), -1, EPERM); break;
+ CASE_TEST(chmod_net); EXPECT_SYSZR(proc, chmod("/proc/self/net", 0555)); break;
+ CASE_TEST(chmod_self); EXPECT_SYSER(proc, chmod("/proc/self", 0555), -1, EPERM); break;
+ CASE_TEST(chown_self); EXPECT_SYSER(proc, chown("/proc/self", 0, 0), -1, EPERM); break;
CASE_TEST(chroot_root); EXPECT_SYSZR(1, chroot("/")); break;
CASE_TEST(chroot_blah); EXPECT_SYSER(1, chroot("/proc/self/blah"), -1, ENOENT); break;
- CASE_TEST(chroot_exe); EXPECT_SYSER(1, chroot("/proc/self/exe"), -1, ENOTDIR); break;
+ CASE_TEST(chroot_exe); EXPECT_SYSER(proc, chroot("/proc/self/exe"), -1, ENOTDIR); break;
CASE_TEST(close_m1); EXPECT_SYSER(1, close(-1), -1, EBADF); break;
CASE_TEST(close_dup); EXPECT_SYSZR(1, close(dup(0))); break;
CASE_TEST(dup_0); tmp = dup(0); EXPECT_SYSNE(1, tmp, -1); close(tmp); break;
@@ -464,7 +468,7 @@ int run_syscall(int min, int max)
CASE_TEST(link_root1); EXPECT_SYSER(1, link("/", "/"), -1, EEXIST); break;
CASE_TEST(link_blah); EXPECT_SYSER(1, link("/proc/self/blah", "/blah"), -1, ENOENT); break;
CASE_TEST(link_dir); EXPECT_SYSER(1, link("/", "/blah"), -1, EPERM); break;
- CASE_TEST(link_cross); EXPECT_SYSER(1, link("/proc/self/net", "/blah"), -1, EXDEV); break;
+ CASE_TEST(link_cross); EXPECT_SYSER(proc, link("/proc/self/net", "/blah"), -1, EXDEV); break;
CASE_TEST(lseek_m1); EXPECT_SYSER(1, lseek(-1, 0, SEEK_SET), -1, EBADF); break;
CASE_TEST(lseek_0); EXPECT_SYSER(1, lseek(0, 0, SEEK_SET), -1, ESPIPE); break;
CASE_TEST(mkdir_root); EXPECT_SYSER(1, mkdir("/", 0755), -1, EEXIST); break;
--
2.31.1.189.g2e36527f23

2022-08-31 19:00:08

by Paul E. McKenney

[permalink] [raw]
Subject: [PATCH nolibc 13/18] selftests/nolibc: add a "kernel" target to build the kernel with the initramfs

From: Willy Tarreau <[email protected]>

The "kernel" target rebuilds the kernel with the current config for the
selected arch, with an initramfs containing the nolibc-test utility.

Since image names depend on the architecture, the currently supported
ones are referenced and resolved based on the architecture.

Signed-off-by: Willy Tarreau <[email protected]>
Signed-off-by: Paul E. McKenney <[email protected]>
---
tools/testing/selftests/nolibc/Makefile | 13 +++++++++++++
1 file changed, 13 insertions(+)

diff --git a/tools/testing/selftests/nolibc/Makefile b/tools/testing/selftests/nolibc/Makefile
index fd0a670823340..4a2ab0e73ce2e 100644
--- a/tools/testing/selftests/nolibc/Makefile
+++ b/tools/testing/selftests/nolibc/Makefile
@@ -12,6 +12,16 @@ include $(srctree)/scripts/subarch.include
ARCH = $(SUBARCH)
endif

+# kernel image names by architecture
+IMAGE_i386 = arch/x86/boot/bzImage
+IMAGE_x86 = arch/x86/boot/bzImage
+IMAGE_arm64 = arch/arm64/boot/Image
+IMAGE_arm = arch/arm/boot/zImage
+IMAGE_mips = vmlinuz
+IMAGE_riscv = arch/riscv/boot/Image
+IMAGE = $(IMAGE_$(ARCH))
+IMAGE_NAME = $(notdir $(IMAGE))
+
# OUTPUT is only set when run from the main makefile, otherwise
# it defaults to this nolibc directory.
OUTPUT ?= $(CURDIR)/
@@ -36,6 +46,9 @@ initramfs: nolibc-test
$(call QUIET_INSTALL, initramfs/init)
$(Q)cp nolibc-test initramfs/init

+kernel: initramfs
+ $(Q)$(MAKE) -C $(srctree) ARCH=$(ARCH) CC=$(CC) CROSS_COMPILE=$(CROSS_COMPILE) $(IMAGE_NAME) CONFIG_INITRAMFS_SOURCE=$(CURDIR)/initramfs
+
clean:
$(call QUIET_CLEAN, nolibc-test)
$(Q)rm -f nolibc-test
--
2.31.1.189.g2e36527f23

2022-08-31 19:00:09

by Paul E. McKenney

[permalink] [raw]
Subject: [PATCH nolibc 01/18] tools/nolibc: make argc 32-bit in riscv startup code

From: Willy Tarreau <[email protected]>

The "ld a0, 0(sp)" instruction doesn't build on RISCV32 because that
would load a 64-bit value into a 32-bit register. But argc 32-bit,
not 64, so we ought to use "lw" here. Tested on both RISCV32 and
RISCV64.

Cc: Pranith Kumar <[email protected]>
Signed-off-by: Willy Tarreau <[email protected]>
Signed-off-by: Paul E. McKenney <[email protected]>
---
tools/include/nolibc/arch-riscv.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tools/include/nolibc/arch-riscv.h b/tools/include/nolibc/arch-riscv.h
index 95e2b79249257..ba04771cb3a34 100644
--- a/tools/include/nolibc/arch-riscv.h
+++ b/tools/include/nolibc/arch-riscv.h
@@ -190,7 +190,7 @@ __asm__ (".section .text\n"
".option norelax\n"
"lla gp, __global_pointer$\n"
".option pop\n"
- "ld a0, 0(sp)\n" // argc (a0) was in the stack
+ "lw a0, 0(sp)\n" // argc (a0) was in the stack
"add a1, sp, "SZREG"\n" // argv (a1) = sp
"slli a2, a0, "PTRLOG"\n" // envp (a2) = SZREG*argc ...
"add a2, a2, "SZREG"\n" // + SZREG (skip null)
--
2.31.1.189.g2e36527f23

2022-08-31 19:00:51

by Paul E. McKenney

[permalink] [raw]
Subject: [PATCH rcu 1/2] torture: Optionally flush printk() buffers before powering off

The rcutorture test suite produces quite a bit of console output at
the end of a test. This means that the new-in-2022 printk() kthreads
are likely to be in the process of flushing output at the time of the
torture_shutdown() function's call to kernel_power_off(). Normally,
rcutorture relies on printk() to flush any pending output upon shutdown,
the better to detect bugs in this area, for example, the one introduced
by 8e274732115f ("printk: extend console_lock for per-console locking").
However, once such a bug is detected and reported, it is necessary to
test the rest of the system, without noise from the already-reported bug.

This commit therefore adds a torture.printk_shutdown_bug_workaround
kernel parameter, which causes torture_shutdown() to invoke pr_flush(),
and print an informative message on the console, immediately before
invoking kernel_power_off(). When this kernel parameter is not specified,
it is up to printk() to flush its own buffers.

Suggested-by: John Ogness <[email protected]>
Signed-off-by: Paul E. McKenney <[email protected]>
---
Documentation/admin-guide/kernel-parameters.txt | 6 ++++++
kernel/torture.c | 7 +++++++
2 files changed, 13 insertions(+)

diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index d7f30902fda02..ba1b8776aab83 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -6143,6 +6143,12 @@
are running concurrently, especially on systems
with rotating-rust storage.

+ torture.printk_shutdown_bug_workaround= [KNL]
+ Execute pr_flush(1000, true) just before invoking
+ kernel_power_off() to work around any bugs that
+ might prevent printk() from flushing its buffers
+ at shutdown time.
+
torture.verbose_sleep_frequency= [KNL]
Specifies how many verbose printk()s should be
emitted between each sleep. The default of zero
diff --git a/kernel/torture.c b/kernel/torture.c
index 789aeb0e1159c..7cd2016b02076 100644
--- a/kernel/torture.c
+++ b/kernel/torture.c
@@ -48,6 +48,9 @@ module_param(disable_onoff_at_boot, bool, 0444);
static bool ftrace_dump_at_shutdown;
module_param(ftrace_dump_at_shutdown, bool, 0444);

+static bool printk_shutdown_bug_workaround;
+module_param(printk_shutdown_bug_workaround, bool, 0444);
+
static int verbose_sleep_frequency;
module_param(verbose_sleep_frequency, int, 0444);

@@ -651,6 +654,10 @@ static int torture_shutdown(void *arg)
VERBOSE_TOROUT_STRING("No torture_shutdown_hook(), skipping.");
if (ftrace_dump_at_shutdown)
rcu_ftrace_dump(DUMP_ALL);
+ if (printk_shutdown_bug_workaround) {
+ pr_info("%s: Flushing printk() buffers at power-down time.\n", __func__);
+ pr_flush(1000, true);
+ }
kernel_power_off(); /* Shut down the system. */
return 0;
}
--
2.31.1.189.g2e36527f23

2022-08-31 19:00:55

by Paul E. McKenney

[permalink] [raw]
Subject: [PATCH nolibc 08/18] selftests/nolibc: exit with poweroff on success when getpid() == 1

From: Willy Tarreau <[email protected]>

The idea is to ease automated testing under qemu. If the test succeeds
while running as PID 1, indicating the system was booted with init=/test,
let's just power off so that qemu can exit with a successful code. In
other situations it will exit and provoke a panic, which may be caught
for example with CONFIG_PVPANIC.

Signed-off-by: Willy Tarreau <[email protected]>
Signed-off-by: Paul E. McKenney <[email protected]>
---
tools/testing/selftests/nolibc/nolibc-test.c | 14 ++++++++++++++
1 file changed, 14 insertions(+)

diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/selftests/nolibc/nolibc-test.c
index b928f099431f7..291d96bfd7c12 100644
--- a/tools/testing/selftests/nolibc/nolibc-test.c
+++ b/tools/testing/selftests/nolibc/nolibc-test.c
@@ -626,6 +626,20 @@ int main(int argc, char **argv, char **envp)
}

printf("Total number of errors: %d\n", ret);
+
+ if (getpid() == 1) {
+ /* we're running as init, there's no other process on the
+ * system, thus likely started from a VM for a quick check.
+ * Exiting will provoke a kernel panic that may be reported
+ * as an error by Qemu or the hypervisor, while stopping
+ * cleanly will often be reported as a success. This allows
+ * to use the output of this program for bisecting kernels.
+ */
+ printf("Leaving init with final status: %d\n", !!ret);
+ if (ret == 0)
+ reboot(LINUX_REBOOT_CMD_POWER_OFF);
+ }
+
printf("Exiting with status %d\n", !!ret);
return !!ret;
}
--
2.31.1.189.g2e36527f23

2022-08-31 19:01:37

by Paul E. McKenney

[permalink] [raw]
Subject: [PATCH nolibc 17/18] selftests/nolibc: add a "help" target

From: Willy Tarreau <[email protected]>

It presents the supported targets, and becomes the default target to
save the user from having to read the makefile. The "all" target was
placed after it and now points to "run" to do everything since it's
no longer the default one.

Signed-off-by: Willy Tarreau <[email protected]>
Signed-off-by: Paul E. McKenney <[email protected]>
---
tools/testing/selftests/nolibc/Makefile | 27 ++++++++++++++++++++++++-
1 file changed, 26 insertions(+), 1 deletion(-)

diff --git a/tools/testing/selftests/nolibc/Makefile b/tools/testing/selftests/nolibc/Makefile
index 210f5369fdfc4..69ea659caca98 100644
--- a/tools/testing/selftests/nolibc/Makefile
+++ b/tools/testing/selftests/nolibc/Makefile
@@ -65,7 +65,32 @@ endif
CFLAGS ?= -Os -fno-ident -fno-asynchronous-unwind-tables
LDFLAGS := -s

-all: nolibc-test
+help:
+ @echo "Supported targets under selftests/nolibc:"
+ @echo " all call the \"run\" target below"
+ @echo " help this help"
+ @echo " sysroot create the nolibc sysroot here (uses \$$ARCH)"
+ @echo " nolibc-test build the executable (uses \$$CC and \$$CROSS_COMPILE)"
+ @echo " initramfs prepare the initramfs with nolibc-test"
+ @echo " defconfig create a fresh new default config (uses \$$ARCH)"
+ @echo " kernel (re)build the kernel with the initramfs (uses \$$ARCH)"
+ @echo " run runs the kernel in QEMU after building it (uses \$$ARCH, \$$TEST)"
+ @echo " rerun runs a previously prebuilt kernel in QEMU (uses \$$ARCH, \$$TEST)"
+ @echo " clean clean the sysroot, initramfs, build and output files"
+ @echo ""
+ @echo "The output file is \"run.out\". Test ranges may be passed using \$$TEST."
+ @echo ""
+ @echo "Currently using the following variables:"
+ @echo " ARCH = $(ARCH)"
+ @echo " CROSS_COMPILE = $(CROSS_COMPILE)"
+ @echo " CC = $(CC)"
+ @echo " OUTPUT = $(OUTPUT)"
+ @echo " TEST = $(TEST)"
+ @echo " QEMU_ARCH = $(if $(QEMU_ARCH),$(QEMU_ARCH),UNKNOWN_ARCH) [determined from \$$ARCH]"
+ @echo " IMAGE_NAME = $(if $(IMAGE_NAME),$(IMAGE_NAME),UNKNOWN_ARCH) [determined from \$$ARCH]"
+ @echo ""
+
+all: run

sysroot: sysroot/$(ARCH)/include

--
2.31.1.189.g2e36527f23

2022-08-31 19:02:09

by Paul E. McKenney

[permalink] [raw]
Subject: [PATCH nolibc 18/18] selftests/nolibc: Avoid generated files being committed

From: Fernanda Ma'rouf <[email protected]>

After running the nolibc tests, the "git status" is not clean because
the generated files are not ignored. Create a `.gitignore` inside the
selftests/nolibc directory to ignore them.

Cc: Ammar Faizi <[email protected]>
Cc: Fernanda Ma'rouf <[email protected]>
Signed-off-by: Fernanda Ma'rouf <[email protected]>
Signed-off-by: Willy Tarreau <[email protected]>
Signed-off-by: Paul E. McKenney <[email protected]>
---
tools/testing/selftests/nolibc/.gitignore | 4 ++++
1 file changed, 4 insertions(+)
create mode 100644 tools/testing/selftests/nolibc/.gitignore

diff --git a/tools/testing/selftests/nolibc/.gitignore b/tools/testing/selftests/nolibc/.gitignore
new file mode 100644
index 0000000000000..4696df589d68e
--- /dev/null
+++ b/tools/testing/selftests/nolibc/.gitignore
@@ -0,0 +1,4 @@
+/initramfs/
+/nolibc-test
+/run.out
+/sysroot/
--
2.31.1.189.g2e36527f23

2022-08-31 19:03:03

by Paul E. McKenney

[permalink] [raw]
Subject: [PATCH nolibc 12/18] selftests/nolibc: support glibc as well

From: Willy Tarreau <[email protected]>

Adding support for glibc can be useful to distinguish between bugs in
nolibc and bugs in the kernel when a syscall reports an unusual value.

It's not that much work and should not affect the long term
maintainability of the tests. The necessary changes can essentially be
summed up like this:
- set _GNU_SOURCE a the top to access some definitions
- many includes added when we know we don't come from nolibc (missing
the stdio include guard)
- disable gettid() which is not exposed by glibc
- disable gettimeofday's support of bad pointers since these crash
in glibc
- add a simple itoa() for errorname(); strerror() is too verbose (no
way to get short messages). strerrorname_np() was added in modern
glibc (2.32) to do exactly this but that 's too recent to be usable
as the default fallback.
- use the standard ioperm() definition. May be we need to implement
ioperm() in nolibc if that's useful.

Signed-off-by: Willy Tarreau <[email protected]>
Signed-off-by: Paul E. McKenney <[email protected]>
---
tools/testing/selftests/nolibc/nolibc-test.c | 47 +++++++++++++++++++-
1 file changed, 45 insertions(+), 2 deletions(-)

diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/selftests/nolibc/nolibc-test.c
index 662dea691749c..78bced95ac630 100644
--- a/tools/testing/selftests/nolibc/nolibc-test.c
+++ b/tools/testing/selftests/nolibc/nolibc-test.c
@@ -1,17 +1,41 @@
// SPDX-License-Identifier: GPL-2.0

+#define _GNU_SOURCE
+
/* platform-specific include files coming from the compiler */
#include <limits.h>

/* libc-specific include files
- * The program may be built in 2 ways:
+ * The program may be built in 3 ways:
* $(CC) -nostdlib -include /path/to/nolibc.h => NOLIBC already defined
- * $(CC) -nostdlib -I/path/to/nolibc/sysroot
+ * $(CC) -nostdlib -I/path/to/nolibc/sysroot => _NOLIBC_* guards are present
+ * $(CC) with default libc => NOLIBC* never defined
*/
#ifndef NOLIBC
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#ifndef _NOLIBC_STDIO_H
+/* standard libcs need more includes */
+#include <linux/reboot.h>
+#include <sys/io.h>
+#include <sys/ioctl.h>
+#include <sys/mount.h>
+#include <sys/reboot.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/sysmacros.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <sched.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <unistd.h>
+#endif
#endif

/* will be used by nolibc by getenv() */
@@ -23,6 +47,17 @@ struct test {
int (*func)(int min, int max); // handler
};

+#ifndef _NOLIBC_STDLIB_H
+char *itoa(int i)
+{
+ static char buf[12];
+ int ret;
+
+ ret = snprintf(buf, sizeof(buf), "%d", i);
+ return (ret >= 0 && ret < sizeof(buf)) ? buf : "#err";
+}
+#endif
+
#define CASE_ERR(err) \
case err: return #err

@@ -431,7 +466,9 @@ int run_syscall(int min, int max)
switch (test + __LINE__ + 1) {
CASE_TEST(getpid); EXPECT_SYSNE(1, getpid(), -1); break;
CASE_TEST(getppid); EXPECT_SYSNE(1, getppid(), -1); break;
+#ifdef NOLIBC
CASE_TEST(gettid); EXPECT_SYSNE(1, gettid(), -1); break;
+#endif
CASE_TEST(getpgid_self); EXPECT_SYSNE(1, getpgid(0), -1); break;
CASE_TEST(getpgid_bad); EXPECT_SYSER(1, getpgid(-1), -1, ESRCH); break;
CASE_TEST(kill_0); EXPECT_SYSZR(1, kill(getpid(), 0)); break;
@@ -460,9 +497,11 @@ int run_syscall(int min, int max)
CASE_TEST(getdents64_root); EXPECT_SYSNE(1, test_getdents64("/"), -1); break;
CASE_TEST(getdents64_null); EXPECT_SYSER(1, test_getdents64("/dev/null"), -1, ENOTDIR); break;
CASE_TEST(gettimeofday_null); EXPECT_SYSZR(1, gettimeofday(NULL, NULL)); break;
+#ifdef NOLIBC
CASE_TEST(gettimeofday_bad1); EXPECT_SYSER(1, gettimeofday((void *)1, NULL), -1, EFAULT); break;
CASE_TEST(gettimeofday_bad2); EXPECT_SYSER(1, gettimeofday(NULL, (void *)1), -1, EFAULT); break;
CASE_TEST(gettimeofday_bad2); EXPECT_SYSER(1, gettimeofday(NULL, (void *)1), -1, EFAULT); break;
+#endif
CASE_TEST(ioctl_tiocinq); EXPECT_SYSZR(1, ioctl(0, TIOCINQ, &tmp)); break;
CASE_TEST(ioctl_tiocinq); EXPECT_SYSZR(1, ioctl(0, TIOCINQ, &tmp)); break;
CASE_TEST(link_root1); EXPECT_SYSER(1, link("/", "/"), -1, EEXIST); break;
@@ -703,7 +742,11 @@ int main(int argc, char **argv, char **envp)
* exit with status code 2N+1 when N is written to 0x501. We
* hard-code the syscall here as it's arch-dependent.
*/
+#if defined(_NOLIBC_SYS_H)
else if (my_syscall3(__NR_ioperm, 0x501, 1, 1) == 0)
+#else
+ else if (ioperm(0x501, 1, 1) == 0)
+#endif
asm volatile ("outb %%al, %%dx" :: "d"(0x501), "a"(0));
/* if it does nothing, fall back to the regular panic */
#endif
--
2.31.1.189.g2e36527f23

2022-08-31 19:03:53

by Paul E. McKenney

[permalink] [raw]
Subject: [PATCH nolibc 05/18] selftests/nolibc: support a test definition format

From: Willy Tarreau <[email protected]>

It now becomes possible to pass a string either in argv[1] or in the
NOLIBC_TEST environment variable (the former having precedence), to
specify which tests to run. The format is:

testname[:range]*[,testname...]

Where a range is either a single value or the min and max numbers of the
test IDs in a sequence, delimited by a dash. Multiple ranges are possible.
This should provide enough flexibility to focus on certain failing parts
just by playing with the boot command line in a boot loader or in qemu
depending on what is accessible.

Signed-off-by: Willy Tarreau <[email protected]>
Signed-off-by: Paul E. McKenney <[email protected]>
---
tools/testing/selftests/nolibc/nolibc-test.c | 91 ++++++++++++++++++++
1 file changed, 91 insertions(+)

diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/selftests/nolibc/nolibc-test.c
index 6c050d4381fec..49177ea9943cc 100644
--- a/tools/testing/selftests/nolibc/nolibc-test.c
+++ b/tools/testing/selftests/nolibc/nolibc-test.c
@@ -17,6 +17,12 @@
/* will be used by nolibc by getenv() */
char **environ;

+/* definition of a series of tests */
+struct test {
+ const char *name; // test name
+ int (*func)(int min, int max); // handler
+};
+
#define CASE_ERR(err) \
case err: return #err

@@ -376,19 +382,104 @@ static int expect_strne(const char *expr, int llen, const char *cmp)
return ret;
}

+
/* declare tests based on line numbers. There must be exactly one test per line. */
#define CASE_TEST(name) \
case __LINE__: llen += printf("%d %s", test, #name);


+/* This is the definition of known test names, with their functions */
+static struct test test_names[] = {
+ /* add new tests here */
+ { 0 }
+};
+
int main(int argc, char **argv, char **envp)
{
int min = 0;
int max = __INT_MAX__;
int ret = 0;
+ int err;
+ int idx;
+ char *test;

environ = envp;

+ /* the definition of a series of tests comes from either argv[1] or the
+ * "NOLIBC_TEST" environment variable. It's made of a comma-delimited
+ * series of test names and optional ranges:
+ * syscall:5-15[:.*],stdlib:8-10
+ */
+ test = argv[1];
+ if (!test)
+ test = getenv("NOLIBC_TEST");
+
+ if (test) {
+ char *comma, *colon, *dash, *value;
+
+ do {
+ comma = strchr(test, ',');
+ if (comma)
+ *(comma++) = '\0';
+
+ colon = strchr(test, ':');
+ if (colon)
+ *(colon++) = '\0';
+
+ for (idx = 0; test_names[idx].name; idx++) {
+ if (strcmp(test, test_names[idx].name) == 0)
+ break;
+ }
+
+ if (test_names[idx].name) {
+ /* The test was named, it will be called at least
+ * once. We may have an optional range at <colon>
+ * here, which defaults to the full range.
+ */
+ do {
+ min = 0; max = __INT_MAX__;
+ value = colon;
+ if (value && *value) {
+ colon = strchr(value, ':');
+ if (colon)
+ *(colon++) = '\0';
+
+ dash = strchr(value, '-');
+ if (dash)
+ *(dash++) = '\0';
+
+ /* support :val: :min-max: :min-: :-max: */
+ if (*value)
+ min = atoi(value);
+ if (!dash)
+ max = min;
+ else if (*dash)
+ max = atoi(dash);
+
+ value = colon;
+ }
+
+ /* now's time to call the test */
+ printf("Running test '%s'\n", test_names[idx].name);
+ err = test_names[idx].func(min, max);
+ ret += err;
+ printf("Errors during this test: %d\n\n", err);
+ } while (colon && *colon);
+ } else
+ printf("Ignoring unknown test name '%s'\n", test);
+
+ test = comma;
+ } while (test && *test);
+ } else {
+ /* no test mentioned, run everything */
+ for (idx = 0; test_names[idx].name; idx++) {
+ printf("Running test '%s'\n", test_names[idx].name);
+ err = test_names[idx].func(min, max);
+ ret += err;
+ printf("Errors during this test: %d\n\n", err);
+ }
+ }
+
printf("Total number of errors: %d\n", ret);
printf("Exiting with status %d\n", !!ret);
return !!ret;
--
2.31.1.189.g2e36527f23

2022-08-31 19:04:30

by Paul E. McKenney

[permalink] [raw]
Subject: [PATCH nolibc 02/18] tools/nolibc: fix build warning in sys_mmap() when my_syscall6 is not defined

From: Willy Tarreau <[email protected]>

We return -ENOSYS when there's no syscall6() operation, but we must cast
it to void* to avoid a warning.

Signed-off-by: Willy Tarreau <[email protected]>
Signed-off-by: Paul E. McKenney <[email protected]>
---
tools/include/nolibc/sys.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tools/include/nolibc/sys.h b/tools/include/nolibc/sys.h
index 08491070387bc..b8c96878c9ce0 100644
--- a/tools/include/nolibc/sys.h
+++ b/tools/include/nolibc/sys.h
@@ -692,7 +692,7 @@ void *sys_mmap(void *addr, size_t length, int prot, int flags, int fd,
{
#ifndef my_syscall6
/* Function not implemented. */
- return -ENOSYS;
+ return (void *)-ENOSYS;
#else

int n;
--
2.31.1.189.g2e36527f23

2022-08-31 19:05:54

by Paul E. McKenney

[permalink] [raw]
Subject: [PATCH nolibc 10/18] selftests/nolibc: recreate and populate /dev and /proc if missing

From: Willy Tarreau <[email protected]>

Most of the time the program will be run alone in an initramfs. There
is no value in requiring the user to populate /dev and /proc for such
tests, we can do it ourselves, and it participates to the tests at the
same time.

What's done here is that when called as init (getpid()==1) we check
if /dev exists or create it, if /dev/console and /dev/null exists,
otherwise we try to mount a devtmpfs there, and if it fails we fall
back to mknod. The console is reopened if stdout was closed. Finally
/proc is created and mounted if /proc/self cannot be found. This is
sufficient for most tests.

Signed-off-by: Willy Tarreau <[email protected]>
Signed-off-by: Paul E. McKenney <[email protected]>
---
tools/testing/selftests/nolibc/nolibc-test.c | 56 ++++++++++++++++++++
1 file changed, 56 insertions(+)

diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/selftests/nolibc/nolibc-test.c
index eeb254749239c..a697182c87f57 100644
--- a/tools/testing/selftests/nolibc/nolibc-test.c
+++ b/tools/testing/selftests/nolibc/nolibc-test.c
@@ -530,6 +530,54 @@ int run_stdlib(int min, int max)
return ret;
}

+/* prepare what needs to be prepared for pid 1 (stdio, /dev, /proc, etc) */
+int prepare(void)
+{
+ struct stat stat_buf;
+
+ /* It's possible that /dev doesn't even exist or was not mounted, so
+ * we'll try to create it, mount it, or create minimal entries into it.
+ * We want at least /dev/null and /dev/console.
+ */
+ if (stat("/dev/.", &stat_buf) == 0 || mkdir("/dev", 0755) == 0) {
+ if (stat("/dev/console", &stat_buf) != 0 ||
+ stat("/dev/null", &stat_buf) != 0) {
+ /* try devtmpfs first, otherwise fall back to manual creation */
+ if (mount("/dev", "/dev", "devtmpfs", 0, 0) != 0) {
+ mknod("/dev/console", 0600 | S_IFCHR, makedev(5, 1));
+ mknod("/dev/null", 0666 | S_IFCHR, makedev(1, 3));
+ }
+ }
+ }
+
+ /* If no /dev/console was found before calling init, stdio is closed so
+ * we need to reopen it from /dev/console. If it failed above, it will
+ * still fail here and we cannot emit a message anyway.
+ */
+ if (close(dup(1)) == -1) {
+ int fd = open("/dev/console", O_RDWR);
+
+ if (fd >= 0) {
+ if (fd != 0)
+ dup2(fd, 0);
+ if (fd != 1)
+ dup2(fd, 1);
+ if (fd != 2)
+ dup2(fd, 2);
+ if (fd > 2)
+ close(fd);
+ puts("\nSuccessfully reopened /dev/console.");
+ }
+ }
+
+ /* try to mount /proc if not mounted. Silently fail otherwise */
+ if (stat("/proc/.", &stat_buf) == 0 || mkdir("/proc", 0755) == 0) {
+ if (stat("/proc/self", &stat_buf) != 0)
+ mount("/proc", "/proc", "proc", 0, 0);
+ }
+
+ return 0;
+}

/* This is the definition of known test names, with their functions */
static struct test test_names[] = {
@@ -550,6 +598,14 @@ int main(int argc, char **argv, char **envp)

environ = envp;

+ /* when called as init, it's possible that no console was opened, for
+ * example if no /dev file system was provided. We'll check that fd#1
+ * was opened, and if not we'll attempt to create and open /dev/console
+ * and /dev/null that we'll use for later tests.
+ */
+ if (getpid() == 1)
+ prepare();
+
/* the definition of a series of tests comes from either argv[1] or the
* "NOLIBC_TEST" environment variable. It's made of a comma-delimited
* series of test names and optional ranges:
--
2.31.1.189.g2e36527f23

2022-08-31 19:07:29

by Paul E. McKenney

[permalink] [raw]
Subject: [PATCH nolibc 14/18] selftests/nolibc: add a "defconfig" target

From: Willy Tarreau <[email protected]>

While most archs will work fine with "make defconfig", not all will
do, and it's not always easy to remember the most suitable choice to
use for a specific architecture.

This adds a "defconfig" target to the Makefile so that one may easily
run "make -C ... defconfig" and make sure to clean and rebuild a fresh
config. This is *not* used by default because we want to preserve the
user's config by default.

Signed-off-by: Willy Tarreau <[email protected]>
Signed-off-by: Paul E. McKenney <[email protected]>
---
tools/testing/selftests/nolibc/Makefile | 12 ++++++++++++
1 file changed, 12 insertions(+)

diff --git a/tools/testing/selftests/nolibc/Makefile b/tools/testing/selftests/nolibc/Makefile
index 4a2ab0e73ce2e..c104719eae8b8 100644
--- a/tools/testing/selftests/nolibc/Makefile
+++ b/tools/testing/selftests/nolibc/Makefile
@@ -22,6 +22,15 @@ IMAGE_riscv = arch/riscv/boot/Image
IMAGE = $(IMAGE_$(ARCH))
IMAGE_NAME = $(notdir $(IMAGE))

+# default kernel configurations that appear to be usable
+DEFCONFIG_i386 = defconfig
+DEFCONFIG_x86 = defconfig
+DEFCONFIG_arm64 = defconfig
+DEFCONFIG_arm = multi_v7_defconfig
+DEFCONFIG_mips = malta_defconfig
+DEFCONFIG_riscv = defconfig
+DEFCONFIG = $(DEFCONFIG_$(ARCH))
+
# OUTPUT is only set when run from the main makefile, otherwise
# it defaults to this nolibc directory.
OUTPUT ?= $(CURDIR)/
@@ -46,6 +55,9 @@ initramfs: nolibc-test
$(call QUIET_INSTALL, initramfs/init)
$(Q)cp nolibc-test initramfs/init

+defconfig:
+ $(Q)$(MAKE) -C $(srctree) ARCH=$(ARCH) CC=$(CC) CROSS_COMPILE=$(CROSS_COMPILE) mrproper $(DEFCONFIG) prepare
+
kernel: initramfs
$(Q)$(MAKE) -C $(srctree) ARCH=$(ARCH) CC=$(CC) CROSS_COMPILE=$(CROSS_COMPILE) $(IMAGE_NAME) CONFIG_INITRAMFS_SOURCE=$(CURDIR)/initramfs

--
2.31.1.189.g2e36527f23

2022-08-31 19:08:12

by Paul E. McKenney

[permalink] [raw]
Subject: [PATCH nolibc 06/18] selftests/nolibc: implement a few tests for various syscalls

From: Willy Tarreau <[email protected]>

This adds 63 tests covering about 34 syscalls. Both successes and
failures are tested. Two tests fail when run as unprivileged user
(link_dir which returns EACCESS instead of EPERM, and chroot which
returns EPERM). One test (execve("/")) expects to fail on EACCESS,
but needs to have valid arguments otherwise the kernel will log a
message. And a few tests require /proc to be mounted.

The code is not pretty since all tests are one-liners, sometimes
resulting in long lines, especially when using compount statements to
preset a line, but it's convenient and doesn't obfuscate the code,
which is important to understand what failed.

Signed-off-by: Willy Tarreau <[email protected]>
Signed-off-by: Paul E. McKenney <[email protected]>
---
tools/testing/selftests/nolibc/nolibc-test.c | 110 +++++++++++++++++++
1 file changed, 110 insertions(+)

diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/selftests/nolibc/nolibc-test.c
index 49177ea9943cc..dc87832912ce4 100644
--- a/tools/testing/selftests/nolibc/nolibc-test.c
+++ b/tools/testing/selftests/nolibc/nolibc-test.c
@@ -388,9 +388,119 @@ static int expect_strne(const char *expr, int llen, const char *cmp)
case __LINE__: llen += printf("%d %s", test, #name);


+/* used by some syscall tests below */
+int test_getdents64(const char *dir)
+{
+ char buffer[4096];
+ int fd, ret;
+ int err;
+
+ ret = fd = open(dir, O_RDONLY | O_DIRECTORY, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = getdents64(fd, (void *)buffer, sizeof(buffer));
+ err = errno;
+ close(fd);
+
+ errno = err;
+ return ret;
+}
+
+/* Run syscall tests between IDs <min> and <max>.
+ * Return 0 on success, non-zero on failure.
+ */
+int run_syscall(int min, int max)
+{
+ struct stat stat_buf;
+ int test;
+ int tmp;
+ int ret = 0;
+ void *p1, *p2;
+
+ for (test = min; test >= 0 && test <= max; test++) {
+ int llen = 0; // line length
+
+ /* avoid leaving empty lines below, this will insert holes into
+ * test numbers.
+ */
+ switch (test + __LINE__ + 1) {
+ CASE_TEST(getpid); EXPECT_SYSNE(1, getpid(), -1); break;
+ CASE_TEST(getppid); EXPECT_SYSNE(1, getppid(), -1); break;
+ CASE_TEST(gettid); EXPECT_SYSNE(1, gettid(), -1); break;
+ CASE_TEST(getpgid_self); EXPECT_SYSNE(1, getpgid(0), -1); break;
+ CASE_TEST(getpgid_bad); EXPECT_SYSER(1, getpgid(-1), -1, ESRCH); break;
+ CASE_TEST(kill_0); EXPECT_SYSZR(1, kill(getpid(), 0)); break;
+ CASE_TEST(kill_CONT); EXPECT_SYSZR(1, kill(getpid(), 0)); break;
+ CASE_TEST(kill_BADPID); EXPECT_SYSER(1, kill(INT_MAX, 0), -1, ESRCH); break;
+ CASE_TEST(sbrk); if ((p1 = p2 = sbrk(4096)) != (void *)-1) p2 = sbrk(-4096); EXPECT_SYSZR(1, (p2 == (void *)-1) || p2 == p1); break;
+ CASE_TEST(brk); EXPECT_SYSZR(1, brk(sbrk(0))); break;
+ CASE_TEST(chdir_root); EXPECT_SYSZR(1, chdir("/")); break;
+ CASE_TEST(chdir_dot); EXPECT_SYSZR(1, chdir(".")); break;
+ CASE_TEST(chdir_blah); EXPECT_SYSER(1, chdir("/blah"), -1, ENOENT); break;
+ CASE_TEST(chmod_net); EXPECT_SYSZR(1, chmod("/proc/self/net", 0555)); break;
+ CASE_TEST(chmod_self); EXPECT_SYSER(1, chmod("/proc/self", 0555), -1, EPERM); break;
+ CASE_TEST(chown_self); EXPECT_SYSER(1, chown("/proc/self", 0, 0), -1, EPERM); break;
+ CASE_TEST(chroot_root); EXPECT_SYSZR(1, chroot("/")); break;
+ CASE_TEST(chroot_blah); EXPECT_SYSER(1, chroot("/proc/self/blah"), -1, ENOENT); break;
+ CASE_TEST(chroot_exe); EXPECT_SYSER(1, chroot("/proc/self/exe"), -1, ENOTDIR); break;
+ CASE_TEST(close_m1); EXPECT_SYSER(1, close(-1), -1, EBADF); break;
+ CASE_TEST(close_dup); EXPECT_SYSZR(1, close(dup(0))); break;
+ CASE_TEST(dup_0); tmp = dup(0); EXPECT_SYSNE(1, tmp, -1); close(tmp); break;
+ CASE_TEST(dup_m1); tmp = dup(-1); EXPECT_SYSER(1, tmp, -1, EBADF); if (tmp != -1) close(tmp); break;
+ CASE_TEST(dup2_0); tmp = dup2(0, 100); EXPECT_SYSNE(1, tmp, -1); close(tmp); break;
+ CASE_TEST(dup2_m1); tmp = dup2(-1, 100); EXPECT_SYSER(1, tmp, -1, EBADF); if (tmp != -1) close(tmp); break;
+ CASE_TEST(dup3_0); tmp = dup3(0, 100, 0); EXPECT_SYSNE(1, tmp, -1); close(tmp); break;
+ CASE_TEST(dup3_m1); tmp = dup3(-1, 100, 0); EXPECT_SYSER(1, tmp, -1, EBADF); if (tmp != -1) close(tmp); break;
+ CASE_TEST(execve_root); EXPECT_SYSER(1, execve("/", (char*[]){ [0] = "/", [1] = NULL }, NULL), -1, EACCES); break;
+ CASE_TEST(getdents64_root); EXPECT_SYSNE(1, test_getdents64("/"), -1); break;
+ CASE_TEST(getdents64_null); EXPECT_SYSER(1, test_getdents64("/dev/null"), -1, ENOTDIR); break;
+ CASE_TEST(gettimeofday_null); EXPECT_SYSZR(1, gettimeofday(NULL, NULL)); break;
+ CASE_TEST(gettimeofday_bad1); EXPECT_SYSER(1, gettimeofday((void *)1, NULL), -1, EFAULT); break;
+ CASE_TEST(gettimeofday_bad2); EXPECT_SYSER(1, gettimeofday(NULL, (void *)1), -1, EFAULT); break;
+ CASE_TEST(gettimeofday_bad2); EXPECT_SYSER(1, gettimeofday(NULL, (void *)1), -1, EFAULT); break;
+ CASE_TEST(ioctl_tiocinq); EXPECT_SYSZR(1, ioctl(0, TIOCINQ, &tmp)); break;
+ CASE_TEST(ioctl_tiocinq); EXPECT_SYSZR(1, ioctl(0, TIOCINQ, &tmp)); break;
+ CASE_TEST(link_root1); EXPECT_SYSER(1, link("/", "/"), -1, EEXIST); break;
+ CASE_TEST(link_blah); EXPECT_SYSER(1, link("/proc/self/blah", "/blah"), -1, ENOENT); break;
+ CASE_TEST(link_dir); EXPECT_SYSER(1, link("/", "/blah"), -1, EPERM); break;
+ CASE_TEST(link_cross); EXPECT_SYSER(1, link("/proc/self/net", "/blah"), -1, EXDEV); break;
+ CASE_TEST(lseek_m1); EXPECT_SYSER(1, lseek(-1, 0, SEEK_SET), -1, EBADF); break;
+ CASE_TEST(lseek_0); EXPECT_SYSER(1, lseek(0, 0, SEEK_SET), -1, ESPIPE); break;
+ CASE_TEST(mkdir_root); EXPECT_SYSER(1, mkdir("/", 0755), -1, EEXIST); break;
+ CASE_TEST(open_tty); EXPECT_SYSNE(1, tmp = open("/dev/null", 0), -1); if (tmp != -1) close(tmp); break;
+ CASE_TEST(open_blah); EXPECT_SYSER(1, tmp = open("/proc/self/blah", 0), -1, ENOENT); if (tmp != -1) close(tmp); break;
+ CASE_TEST(poll_null); EXPECT_SYSZR(1, poll(NULL, 0, 0)); break;
+ CASE_TEST(poll_stdout); EXPECT_SYSNE(1, ({ struct pollfd fds = { 1, POLLOUT, 0}; poll(&fds, 1, 0); }), -1); break;
+ CASE_TEST(poll_fault); EXPECT_SYSER(1, poll((void *)1, 1, 0), -1, EFAULT); break;
+ CASE_TEST(read_badf); EXPECT_SYSER(1, read(-1, &tmp, 1), -1, EBADF); break;
+ CASE_TEST(sched_yield); EXPECT_SYSZR(1, sched_yield()); break;
+ CASE_TEST(select_null); EXPECT_SYSZR(1, ({ struct timeval tv = { 0 }; select(0, NULL, NULL, NULL, &tv); })); break;
+ CASE_TEST(select_stdout); EXPECT_SYSNE(1, ({ fd_set fds; FD_ZERO(&fds); FD_SET(1, &fds); select(2, NULL, &fds, NULL, NULL); }), -1); break;
+ CASE_TEST(select_fault); EXPECT_SYSER(1, select(1, (void *)1, NULL, NULL, 0), -1, EFAULT); break;
+ CASE_TEST(stat_blah); EXPECT_SYSER(1, stat("/proc/self/blah", &stat_buf), -1, ENOENT); break;
+ CASE_TEST(stat_fault); EXPECT_SYSER(1, stat(NULL, &stat_buf), -1, EFAULT); break;
+ CASE_TEST(symlink_root); EXPECT_SYSER(1, symlink("/", "/"), -1, EEXIST); break;
+ CASE_TEST(unlink_root); EXPECT_SYSER(1, unlink("/"), -1, EISDIR); break;
+ CASE_TEST(unlink_blah); EXPECT_SYSER(1, unlink("/proc/self/blah"), -1, ENOENT); break;
+ CASE_TEST(wait_child); EXPECT_SYSER(1, wait(&tmp), -1, ECHILD); break;
+ CASE_TEST(waitpid_min); EXPECT_SYSER(1, waitpid(INT_MIN, &tmp, WNOHANG), -1, ESRCH); break;
+ CASE_TEST(waitpid_child); EXPECT_SYSER(1, waitpid(getpid(), &tmp, WNOHANG), -1, ECHILD); break;
+ CASE_TEST(write_badf); EXPECT_SYSER(1, write(-1, &tmp, 1), -1, EBADF); break;
+ CASE_TEST(write_zero); EXPECT_SYSZR(1, write(1, &tmp, 0)); break;
+ case __LINE__:
+ return ret; /* must be last */
+ /* note: do not set any defaults so as to permit holes above */
+ }
+ }
+ return ret;
+}
+
+
/* This is the definition of known test names, with their functions */
static struct test test_names[] = {
/* add new tests here */
+ { .name = "syscall", .func = run_syscall },
{ 0 }
};

--
2.31.1.189.g2e36527f23

2022-08-31 19:15:44

by Paul E. McKenney

[permalink] [raw]
Subject: [PATCH nolibc 04/18] selftests/nolibc: add basic infrastructure to ease creation of nolibc tests

From: Willy Tarreau <[email protected]>

This creates a "nolibc" selftest that intends to test various parts of
the nolibc component, both in terms of build and execution for a given
architecture.

The aim is for it to be as simple to run as a kernel build, by just
passing the compiler (for the build) and the ARCH (for kernel and
execution).

It brings a basic squeleton made of a single C file that will ease testing
and error reporting. The code will be arranged so that it remains easy to
add basic tests for syscalls or library calls that may rely on a condition
to be executed, and whose result is compared to a value or to an error
with a specific errno value.

Tests will just use a relative line number in switch/case statements as
an index, saving the user from having to maintain arrays and complicated
functions which can often just be one-liners.

MAINTAINERS was updated.

Signed-off-by: Willy Tarreau <[email protected]>
Signed-off-by: Paul E. McKenney <[email protected]>
---
MAINTAINERS | 1 +
tools/testing/selftests/nolibc/Makefile | 43 ++
tools/testing/selftests/nolibc/nolibc-test.c | 395 +++++++++++++++++++
3 files changed, 439 insertions(+)
create mode 100644 tools/testing/selftests/nolibc/Makefile
create mode 100644 tools/testing/selftests/nolibc/nolibc-test.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 8a5012ba6ff98..89f939ad19963 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -14444,6 +14444,7 @@ M: Willy Tarreau <[email protected]>
S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/wtarreau/nolibc.git
F: tools/include/nolibc/
+F: tools/testing/selftests/nolibc/

NSDEPS
M: Matthias Maennich <[email protected]>
diff --git a/tools/testing/selftests/nolibc/Makefile b/tools/testing/selftests/nolibc/Makefile
new file mode 100644
index 0000000000000..fd0a670823340
--- /dev/null
+++ b/tools/testing/selftests/nolibc/Makefile
@@ -0,0 +1,43 @@
+# SPDX-License-Identifier: GPL-2.0
+# Makefile for nolibc tests
+include ../../../scripts/Makefile.include
+
+# we're in ".../tools/testing/selftests/nolibc"
+ifeq ($(srctree),)
+srctree := $(patsubst %/tools/testing/selftests/,%,$(dir $(CURDIR)))
+endif
+
+ifeq ($(ARCH),)
+include $(srctree)/scripts/subarch.include
+ARCH = $(SUBARCH)
+endif
+
+# OUTPUT is only set when run from the main makefile, otherwise
+# it defaults to this nolibc directory.
+OUTPUT ?= $(CURDIR)/
+
+ifeq ($(V),1)
+Q=
+else
+Q=@
+endif
+
+CFLAGS ?= -Os -fno-ident -fno-asynchronous-unwind-tables
+LDFLAGS := -s
+
+all: nolibc-test
+
+nolibc-test: nolibc-test.c
+ $(QUIET_CC)$(CC) $(CFLAGS) $(LDFLAGS) -o $@ \
+ -nostdlib -static -include ../../../include/nolibc/nolibc.h $^ -lgcc
+
+initramfs: nolibc-test
+ $(QUIET_MKDIR)mkdir -p initramfs
+ $(call QUIET_INSTALL, initramfs/init)
+ $(Q)cp nolibc-test initramfs/init
+
+clean:
+ $(call QUIET_CLEAN, nolibc-test)
+ $(Q)rm -f nolibc-test
+ $(call QUIET_CLEAN, initramfs)
+ $(Q)rm -rf initramfs
diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/selftests/nolibc/nolibc-test.c
new file mode 100644
index 0000000000000..6c050d4381fec
--- /dev/null
+++ b/tools/testing/selftests/nolibc/nolibc-test.c
@@ -0,0 +1,395 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/* platform-specific include files coming from the compiler */
+#include <limits.h>
+
+/* libc-specific include files
+ * The program may be built in 2 ways:
+ * $(CC) -nostdlib -include /path/to/nolibc.h => NOLIBC already defined
+ * $(CC) -nostdlib -I/path/to/nolibc/sysroot
+ */
+#ifndef NOLIBC
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#endif
+
+/* will be used by nolibc by getenv() */
+char **environ;
+
+#define CASE_ERR(err) \
+ case err: return #err
+
+/* returns the error name (e.g. "ENOENT") for common errors, "SUCCESS" for 0,
+ * or the decimal value for less common ones.
+ */
+const char *errorname(int err)
+{
+ switch (err) {
+ case 0: return "SUCCESS";
+ CASE_ERR(EPERM);
+ CASE_ERR(ENOENT);
+ CASE_ERR(ESRCH);
+ CASE_ERR(EINTR);
+ CASE_ERR(EIO);
+ CASE_ERR(ENXIO);
+ CASE_ERR(E2BIG);
+ CASE_ERR(ENOEXEC);
+ CASE_ERR(EBADF);
+ CASE_ERR(ECHILD);
+ CASE_ERR(EAGAIN);
+ CASE_ERR(ENOMEM);
+ CASE_ERR(EACCES);
+ CASE_ERR(EFAULT);
+ CASE_ERR(ENOTBLK);
+ CASE_ERR(EBUSY);
+ CASE_ERR(EEXIST);
+ CASE_ERR(EXDEV);
+ CASE_ERR(ENODEV);
+ CASE_ERR(ENOTDIR);
+ CASE_ERR(EISDIR);
+ CASE_ERR(EINVAL);
+ CASE_ERR(ENFILE);
+ CASE_ERR(EMFILE);
+ CASE_ERR(ENOTTY);
+ CASE_ERR(ETXTBSY);
+ CASE_ERR(EFBIG);
+ CASE_ERR(ENOSPC);
+ CASE_ERR(ESPIPE);
+ CASE_ERR(EROFS);
+ CASE_ERR(EMLINK);
+ CASE_ERR(EPIPE);
+ CASE_ERR(EDOM);
+ CASE_ERR(ERANGE);
+ CASE_ERR(ENOSYS);
+ default:
+ return itoa(err);
+ }
+}
+
+static int pad_spc(int llen, int cnt, const char *fmt, ...)
+{
+ va_list args;
+ int len;
+ int ret;
+
+ for (len = 0; len < cnt - llen; len++)
+ putchar(' ');
+
+ va_start(args, fmt);
+ ret = vfprintf(stdout, fmt, args);
+ va_end(args);
+ return ret < 0 ? ret : ret + len;
+}
+
+/* The tests below are intended to be used by the macroes, which evaluate
+ * expression <expr>, print the status to stdout, and update the "ret"
+ * variable to count failures. The functions themselves return the number
+ * of failures, thus either 0 or 1.
+ */
+
+#define EXPECT_ZR(cond, expr) \
+ do { if (!cond) pad_spc(llen, 40, "[SKIPPED]\n"); else ret += expect_zr(expr, llen); } while (0)
+
+static int expect_zr(int expr, int llen)
+{
+ int ret = !(expr == 0);
+
+ llen += printf(" = %d ", expr);
+ pad_spc(llen, 40, ret ? "[FAIL]\n" : " [OK]\n");
+ return ret;
+}
+
+
+#define EXPECT_NZ(cond, expr, val) \
+ do { if (!cond) pad_spc(llen, 40, "[SKIPPED]\n"); else ret += expect_nz(expr, llen; } while (0)
+
+static int expect_nz(int expr, int llen)
+{
+ int ret = !(expr != 0);
+
+ llen += printf(" = %d ", expr);
+ pad_spc(llen, 40, ret ? "[FAIL]\n" : " [OK]\n");
+ return ret;
+}
+
+
+#define EXPECT_EQ(cond, expr, val) \
+ do { if (!cond) pad_spc(llen, 40, "[SKIPPED]\n"); else ret += expect_eq(expr, llen, val); } while (0)
+
+static int expect_eq(int expr, int llen, int val)
+{
+ int ret = !(expr == val);
+
+ llen += printf(" = %d ", expr);
+ pad_spc(llen, 40, ret ? "[FAIL]\n" : " [OK]\n");
+ return ret;
+}
+
+
+#define EXPECT_NE(cond, expr, val) \
+ do { if (!cond) pad_spc(llen, 40, "[SKIPPED]\n"); else ret += expect_ne(expr, llen, val); } while (0)
+
+static int expect_ne(int expr, int llen, int val)
+{
+ int ret = !(expr != val);
+
+ llen += printf(" = %d ", expr);
+ pad_spc(llen, 40, ret ? "[FAIL]\n" : " [OK]\n");
+ return ret;
+}
+
+
+#define EXPECT_GE(cond, expr, val) \
+ do { if (!cond) pad_spc(llen, 40, "[SKIPPED]\n"); else ret += expect_ge(expr, llen, val); } while (0)
+
+static int expect_ge(int expr, int llen, int val)
+{
+ int ret = !(expr >= val);
+
+ llen += printf(" = %d ", expr);
+ pad_spc(llen, 40, ret ? "[FAIL]\n" : " [OK]\n");
+ return ret;
+}
+
+
+#define EXPECT_GT(cond, expr, val) \
+ do { if (!cond) pad_spc(llen, 40, "[SKIPPED]\n"); else ret += expect_gt(expr, llen, val); } while (0)
+
+static int expect_gt(int expr, int llen, int val)
+{
+ int ret = !(expr > val);
+
+ llen += printf(" = %d ", expr);
+ pad_spc(llen, 40, ret ? "[FAIL]\n" : " [OK]\n");
+ return ret;
+}
+
+
+#define EXPECT_LE(cond, expr, val) \
+ do { if (!cond) pad_spc(llen, 40, "[SKIPPED]\n"); else ret += expect_le(expr, llen, val); } while (0)
+
+static int expect_le(int expr, int llen, int val)
+{
+ int ret = !(expr <= val);
+
+ llen += printf(" = %d ", expr);
+ pad_spc(llen, 40, ret ? "[FAIL]\n" : " [OK]\n");
+ return ret;
+}
+
+
+#define EXPECT_LT(cond, expr, val) \
+ do { if (!cond) pad_spc(llen, 40, "[SKIPPED]\n"); else ret += expect_lt(expr, llen, val); } while (0)
+
+static int expect_lt(int expr, int llen, int val)
+{
+ int ret = !(expr < val);
+
+ llen += printf(" = %d ", expr);
+ pad_spc(llen, 40, ret ? "[FAIL]\n" : " [OK]\n");
+ return ret;
+}
+
+
+#define EXPECT_SYSZR(cond, expr) \
+ do { if (!cond) pad_spc(llen, 40, "[SKIPPED]\n"); else ret += expect_syszr(expr, llen); } while (0)
+
+static int expect_syszr(int expr, int llen)
+{
+ int ret = 0;
+
+ if (expr) {
+ ret = 1;
+ llen += printf(" = %d %s ", expr, errorname(errno));
+ llen += pad_spc(llen, 40, "[FAIL]\n");
+ } else {
+ llen += printf(" = %d ", expr);
+ llen += pad_spc(llen, 40, " [OK]\n");
+ }
+ return ret;
+}
+
+
+#define EXPECT_SYSEQ(cond, expr, val) \
+ do { if (!cond) pad_spc(llen, 40, "[SKIPPED]\n"); else ret += expect_syseq(expr, llen, val); } while (0)
+
+static int expect_syseq(int expr, int llen, int val)
+{
+ int ret = 0;
+
+ if (expr != val) {
+ ret = 1;
+ llen += printf(" = %d %s ", expr, errorname(errno));
+ llen += pad_spc(llen, 40, "[FAIL]\n");
+ } else {
+ llen += printf(" = %d ", expr);
+ llen += pad_spc(llen, 40, " [OK]\n");
+ }
+ return ret;
+}
+
+
+#define EXPECT_SYSNE(cond, expr, val) \
+ do { if (!cond) pad_spc(llen, 40, "[SKIPPED]\n"); else ret += expect_sysne(expr, llen, val); } while (0)
+
+static int expect_sysne(int expr, int llen, int val)
+{
+ int ret = 0;
+
+ if (expr == val) {
+ ret = 1;
+ llen += printf(" = %d %s ", expr, errorname(errno));
+ llen += pad_spc(llen, 40, "[FAIL]\n");
+ } else {
+ llen += printf(" = %d ", expr);
+ llen += pad_spc(llen, 40, " [OK]\n");
+ }
+ return ret;
+}
+
+
+#define EXPECT_SYSER(cond, expr, expret, experr) \
+ do { if (!cond) pad_spc(llen, 40, "[SKIPPED]\n"); else ret += expect_syserr(expr, expret, experr, llen); } while (0)
+
+static int expect_syserr(int expr, int expret, int experr, int llen)
+{
+ int ret = 0;
+ int _errno = errno;
+
+ llen += printf(" = %d %s ", expr, errorname(_errno));
+ if (expr != expret || _errno != experr) {
+ ret = 1;
+ llen += printf(" != (%d %s) ", expret, errorname(experr));
+ llen += pad_spc(llen, 40, "[FAIL]\n");
+ } else {
+ llen += pad_spc(llen, 40, " [OK]\n");
+ }
+ return ret;
+}
+
+
+#define EXPECT_PTRZR(cond, expr) \
+ do { if (!cond) pad_spc(llen, 40, "[SKIPPED]\n"); else ret += expect_ptrzr(expr, llen); } while (0)
+
+static int expect_ptrzr(const void *expr, int llen)
+{
+ int ret = 0;
+
+ llen += printf(" = <%p> ", expr);
+ if (expr) {
+ ret = 1;
+ llen += pad_spc(llen, 40, "[FAIL]\n");
+ } else {
+ llen += pad_spc(llen, 40, " [OK]\n");
+ }
+ return ret;
+}
+
+
+#define EXPECT_PTRNZ(cond, expr) \
+ do { if (!cond) pad_spc(llen, 40, "[SKIPPED]\n"); else ret += expect_ptrnz(expr, llen); } while (0)
+
+static int expect_ptrnz(const void *expr, int llen)
+{
+ int ret = 0;
+
+ llen += printf(" = <%p> ", expr);
+ if (!expr) {
+ ret = 1;
+ llen += pad_spc(llen, 40, "[FAIL]\n");
+ } else {
+ llen += pad_spc(llen, 40, " [OK]\n");
+ }
+ return ret;
+}
+
+
+#define EXPECT_STRZR(cond, expr) \
+ do { if (!cond) pad_spc(llen, 40, "[SKIPPED]\n"); else ret += expect_strzr(expr, llen); } while (0)
+
+static int expect_strzr(const char *expr, int llen)
+{
+ int ret = 0;
+
+ llen += printf(" = <%s> ", expr);
+ if (expr) {
+ ret = 1;
+ llen += pad_spc(llen, 40, "[FAIL]\n");
+ } else {
+ llen += pad_spc(llen, 40, " [OK]\n");
+ }
+ return ret;
+}
+
+
+#define EXPECT_STRNZ(cond, expr) \
+ do { if (!cond) pad_spc(llen, 40, "[SKIPPED]\n"); else ret += expect_strnz(expr, llen); } while (0)
+
+static int expect_strnz(const char *expr, int llen)
+{
+ int ret = 0;
+
+ llen += printf(" = <%s> ", expr);
+ if (!expr) {
+ ret = 1;
+ llen += pad_spc(llen, 40, "[FAIL]\n");
+ } else {
+ llen += pad_spc(llen, 40, " [OK]\n");
+ }
+ return ret;
+}
+
+
+#define EXPECT_STREQ(cond, expr, cmp) \
+ do { if (!cond) pad_spc(llen, 40, "[SKIPPED]\n"); else ret += expect_streq(expr, llen, cmp); } while (0)
+
+static int expect_streq(const char *expr, int llen, const char *cmp)
+{
+ int ret = 0;
+
+ llen += printf(" = <%s> ", expr);
+ if (strcmp(expr, cmp) != 0) {
+ ret = 1;
+ llen += pad_spc(llen, 40, "[FAIL]\n");
+ } else {
+ llen += pad_spc(llen, 40, " [OK]\n");
+ }
+ return ret;
+}
+
+
+#define EXPECT_STRNE(cond, expr, cmp) \
+ do { if (!cond) pad_spc(llen, 40, "[SKIPPED]\n"); else ret += expect_strne(expr, llen, cmp); } while (0)
+
+static int expect_strne(const char *expr, int llen, const char *cmp)
+{
+ int ret = 0;
+
+ llen += printf(" = <%s> ", expr);
+ if (strcmp(expr, cmp) == 0) {
+ ret = 1;
+ llen += pad_spc(llen, 40, "[FAIL]\n");
+ } else {
+ llen += pad_spc(llen, 40, " [OK]\n");
+ }
+ return ret;
+}
+
+/* declare tests based on line numbers. There must be exactly one test per line. */
+#define CASE_TEST(name) \
+ case __LINE__: llen += printf("%d %s", test, #name);
+
+
+int main(int argc, char **argv, char **envp)
+{
+ int min = 0;
+ int max = __INT_MAX__;
+ int ret = 0;
+
+ environ = envp;
+
+ printf("Total number of errors: %d\n", ret);
+ printf("Exiting with status %d\n", !!ret);
+ return !!ret;
+}
--
2.31.1.189.g2e36527f23

2022-08-31 19:20:19

by Paul E. McKenney

[permalink] [raw]
Subject: [PATCH nolibc 09/18] selftests/nolibc: on x86, support exiting with isa-debug-exit

From: Willy Tarreau <[email protected]>

QEMU, when started with "-device isa-debug-exit -no-reboot" will exit
with status code 2N+1 when N is written to 0x501. This is particularly
convenient for automated tests but this is not portable. As such we
only enable this on x86_64 when pid==1. In addition, this requires an
ioperm() call but in order not to have to define arch-specific syscalls
we just perform the syscall by hand there.

Signed-off-by: Willy Tarreau <[email protected]>
Signed-off-by: Paul E. McKenney <[email protected]>
---
tools/testing/selftests/nolibc/nolibc-test.c | 9 +++++++++
1 file changed, 9 insertions(+)

diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/selftests/nolibc/nolibc-test.c
index 291d96bfd7c12..eeb254749239c 100644
--- a/tools/testing/selftests/nolibc/nolibc-test.c
+++ b/tools/testing/selftests/nolibc/nolibc-test.c
@@ -638,6 +638,15 @@ int main(int argc, char **argv, char **envp)
printf("Leaving init with final status: %d\n", !!ret);
if (ret == 0)
reboot(LINUX_REBOOT_CMD_POWER_OFF);
+#if defined(__x86_64__)
+ /* QEMU started with "-device isa-debug-exit -no-reboot" will
+ * exit with status code 2N+1 when N is written to 0x501. We
+ * hard-code the syscall here as it's arch-dependent.
+ */
+ else if (my_syscall3(__NR_ioperm, 0x501, 1, 1) == 0)
+ asm volatile ("outb %%al, %%dx" :: "d"(0x501), "a"(0));
+ /* if it does nothing, fall back to the regular panic */
+#endif
}

printf("Exiting with status %d\n", !!ret);
--
2.31.1.189.g2e36527f23

2022-08-31 19:56:44

by Paul E. McKenney

[permalink] [raw]
Subject: [PATCH nolibc 03/18] tools/nolibc: make sys_mmap() automatically use the right __NR_mmap definition

From: Willy Tarreau <[email protected]>

__NR_mmap2 was used for i386 but it's also needed for other archs such
as RISCV32 or ARM. Let's decide to use it based on the __NR_mmap2
definition as it's not defined on other archs.

Signed-off-by: Willy Tarreau <[email protected]>
Signed-off-by: Paul E. McKenney <[email protected]>
---
tools/include/nolibc/sys.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tools/include/nolibc/sys.h b/tools/include/nolibc/sys.h
index b8c96878c9ce0..ce3ee03aa6794 100644
--- a/tools/include/nolibc/sys.h
+++ b/tools/include/nolibc/sys.h
@@ -697,7 +697,7 @@ void *sys_mmap(void *addr, size_t length, int prot, int flags, int fd,

int n;

-#if defined(__i386__)
+#if defined(__NR_mmap2)
n = __NR_mmap2;
offset >>= 12;
#else
--
2.31.1.189.g2e36527f23

2022-09-01 09:38:29

by John Ogness

[permalink] [raw]
Subject: Re: [PATCH rcu 1/2] torture: Optionally flush printk() buffers before powering off

Added CC the rest of the printk folks since this would be the first use
of pr_flush() outside of the printk subsystem. And it is a controversial
usage, since it is used to work around potential (future) bugs in
printk.

On 2022-08-31, "Paul E. McKenney" <[email protected]> wrote:
> The rcutorture test suite produces quite a bit of console output at
> the end of a test. This means that the new-in-2022 printk() kthreads
> are likely to be in the process of flushing output at the time of the
> torture_shutdown() function's call to kernel_power_off(). Normally,
> rcutorture relies on printk() to flush any pending output upon shutdown,
> the better to detect bugs in this area, for example, the one introduced
> by 8e274732115f ("printk: extend console_lock for per-console locking").
> However, once such a bug is detected and reported, it is necessary to
> test the rest of the system, without noise from the already-reported bug.

Since the printk kthread implementation has been reverted, this is no
longer an issue. When a kthread implementation is re-introduced, this
issue will need to have been considered and handled.

IMHO this patch is premature at this time and hopefully will never
become necessary (in the form of a bug workaround).

> This commit therefore adds a torture.printk_shutdown_bug_workaround
> kernel parameter, which causes torture_shutdown() to invoke pr_flush(),
> and print an informative message on the console, immediately before
> invoking kernel_power_off(). When this kernel parameter is not specified,
> it is up to printk() to flush its own buffers.
>
> Suggested-by: John Ogness <[email protected]>
> Signed-off-by: Paul E. McKenney <[email protected]>
> ---
> Documentation/admin-guide/kernel-parameters.txt | 6 ++++++
> kernel/torture.c | 7 +++++++
> 2 files changed, 13 insertions(+)
>
> diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
> index d7f30902fda02..ba1b8776aab83 100644
> --- a/Documentation/admin-guide/kernel-parameters.txt
> +++ b/Documentation/admin-guide/kernel-parameters.txt
> @@ -6143,6 +6143,12 @@
> are running concurrently, especially on systems
> with rotating-rust storage.
>
> + torture.printk_shutdown_bug_workaround= [KNL]
> + Execute pr_flush(1000, true) just before invoking
> + kernel_power_off() to work around any bugs that
> + might prevent printk() from flushing its buffers
> + at shutdown time.
> +
> torture.verbose_sleep_frequency= [KNL]
> Specifies how many verbose printk()s should be
> emitted between each sleep. The default of zero
> diff --git a/kernel/torture.c b/kernel/torture.c
> index 789aeb0e1159c..7cd2016b02076 100644
> --- a/kernel/torture.c
> +++ b/kernel/torture.c
> @@ -48,6 +48,9 @@ module_param(disable_onoff_at_boot, bool, 0444);
> static bool ftrace_dump_at_shutdown;
> module_param(ftrace_dump_at_shutdown, bool, 0444);
>
> +static bool printk_shutdown_bug_workaround;
> +module_param(printk_shutdown_bug_workaround, bool, 0444);
> +
> static int verbose_sleep_frequency;
> module_param(verbose_sleep_frequency, int, 0444);
>
> @@ -651,6 +654,10 @@ static int torture_shutdown(void *arg)
> VERBOSE_TOROUT_STRING("No torture_shutdown_hook(), skipping.");
> if (ftrace_dump_at_shutdown)
> rcu_ftrace_dump(DUMP_ALL);
> + if (printk_shutdown_bug_workaround) {
> + pr_info("%s: Flushing printk() buffers at power-down time.\n", __func__);
> + pr_flush(1000, true);
> + }
> kernel_power_off(); /* Shut down the system. */
> return 0;
> }
> --
> 2.31.1.189.g2e36527f23

John Ogness

2022-09-01 10:37:08

by Paul E. McKenney

[permalink] [raw]
Subject: Re: [PATCH rcu 1/2] torture: Optionally flush printk() buffers before powering off

On Thu, Sep 01, 2022 at 11:18:20AM +0206, John Ogness wrote:
> Added CC the rest of the printk folks since this would be the first use
> of pr_flush() outside of the printk subsystem. And it is a controversial
> usage, since it is used to work around potential (future) bugs in
> printk.
>
> On 2022-08-31, "Paul E. McKenney" <[email protected]> wrote:
> > The rcutorture test suite produces quite a bit of console output at
> > the end of a test. This means that the new-in-2022 printk() kthreads
> > are likely to be in the process of flushing output at the time of the
> > torture_shutdown() function's call to kernel_power_off(). Normally,
> > rcutorture relies on printk() to flush any pending output upon shutdown,
> > the better to detect bugs in this area, for example, the one introduced
> > by 8e274732115f ("printk: extend console_lock for per-console locking").
> > However, once such a bug is detected and reported, it is necessary to
> > test the rest of the system, without noise from the already-reported bug.
>
> Since the printk kthread implementation has been reverted, this is no
> longer an issue. When a kthread implementation is re-introduced, this
> issue will need to have been considered and handled.
>
> IMHO this patch is premature at this time and hopefully will never
> become necessary (in the form of a bug workaround).

Very well, I will drop it, for the time being, anyway. ;-)

Thanx, Paul

> > This commit therefore adds a torture.printk_shutdown_bug_workaround
> > kernel parameter, which causes torture_shutdown() to invoke pr_flush(),
> > and print an informative message on the console, immediately before
> > invoking kernel_power_off(). When this kernel parameter is not specified,
> > it is up to printk() to flush its own buffers.
> >
> > Suggested-by: John Ogness <[email protected]>
> > Signed-off-by: Paul E. McKenney <[email protected]>
> > ---
> > Documentation/admin-guide/kernel-parameters.txt | 6 ++++++
> > kernel/torture.c | 7 +++++++
> > 2 files changed, 13 insertions(+)
> >
> > diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
> > index d7f30902fda02..ba1b8776aab83 100644
> > --- a/Documentation/admin-guide/kernel-parameters.txt
> > +++ b/Documentation/admin-guide/kernel-parameters.txt
> > @@ -6143,6 +6143,12 @@
> > are running concurrently, especially on systems
> > with rotating-rust storage.
> >
> > + torture.printk_shutdown_bug_workaround= [KNL]
> > + Execute pr_flush(1000, true) just before invoking
> > + kernel_power_off() to work around any bugs that
> > + might prevent printk() from flushing its buffers
> > + at shutdown time.
> > +
> > torture.verbose_sleep_frequency= [KNL]
> > Specifies how many verbose printk()s should be
> > emitted between each sleep. The default of zero
> > diff --git a/kernel/torture.c b/kernel/torture.c
> > index 789aeb0e1159c..7cd2016b02076 100644
> > --- a/kernel/torture.c
> > +++ b/kernel/torture.c
> > @@ -48,6 +48,9 @@ module_param(disable_onoff_at_boot, bool, 0444);
> > static bool ftrace_dump_at_shutdown;
> > module_param(ftrace_dump_at_shutdown, bool, 0444);
> >
> > +static bool printk_shutdown_bug_workaround;
> > +module_param(printk_shutdown_bug_workaround, bool, 0444);
> > +
> > static int verbose_sleep_frequency;
> > module_param(verbose_sleep_frequency, int, 0444);
> >
> > @@ -651,6 +654,10 @@ static int torture_shutdown(void *arg)
> > VERBOSE_TOROUT_STRING("No torture_shutdown_hook(), skipping.");
> > if (ftrace_dump_at_shutdown)
> > rcu_ftrace_dump(DUMP_ALL);
> > + if (printk_shutdown_bug_workaround) {
> > + pr_info("%s: Flushing printk() buffers at power-down time.\n", __func__);
> > + pr_flush(1000, true);
> > + }
> > kernel_power_off(); /* Shut down the system. */
> > return 0;
> > }
> > --
> > 2.31.1.189.g2e36527f23
>
> John Ogness