selftests/powerpc: New TM signal self test
authorBreno Leitao <leitao@debian.org>
Tue, 8 Jan 2019 11:31:21 +0000 (09:31 -0200)
committerMichael Ellerman <mpe@ellerman.id.au>
Tue, 15 Jan 2019 00:17:09 +0000 (11:17 +1100)
A new self test that forces MSR[TS] to be set without calling any TM
instruction. This test also tries to cause a page fault at a signal
handler, exactly between MSR[TS] set and tm_recheckpoint(), forcing
thread->texasr to be rewritten with TEXASR[FS] = 0, which will cause a BUG
when tm_recheckpoint() is called.

This test is not deterministic, since it is hard to guarantee that the page
access will cause a page fault. In order to force more page faults at
signal context, the signal handler and the ucontext are being mapped into a
MADV_DONTNEED memory chunks.

Tests have shown that the bug could be exposed with few interactions in a
buggy kernel. This test is configured to loop 5000x, having a good chance
to hit the kernel issue in just one run.  This self test takes less than
two seconds to run.

This test uses set/getcontext because the kernel will recheckpoint
zeroed structures, causing the test to segfault, which is undesired because
the test needs to rerun, so, there is a signal handler for SIGSEGV which
will restart the test.

v2: Uses the MADV_DONTNEED memory advice
v3: Fix memcpy and 32-bits compilation
v4: Does not define unused macros

Signed-off-by: Breno Leitao <leitao@debian.org>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
tools/testing/selftests/powerpc/include/reg.h
tools/testing/selftests/powerpc/include/utils.h
tools/testing/selftests/powerpc/tm/.gitignore
tools/testing/selftests/powerpc/tm/Makefile
tools/testing/selftests/powerpc/tm/tm-signal-context-force-tm.c [new file with mode: 0644]

index 52b4710469d23a840813fa5fc5751fd967e436a2..96043b9b9829e996a610af3c0708312ccc8d39be 100644 (file)
 #define TEXASR_TE      0x0000000004000000
 #define TEXASR_ROT     0x0000000002000000
 
+/* MSR register bits */
+#define MSR_TS_S_LG     33              /* Trans Mem state: Suspended */
+
+#define __MASK(X)       (1UL<<(X))
+
+/* macro to check TM MSR bits */
+#define MSR_TS_S        __MASK(MSR_TS_S_LG)   /* Transaction Suspended */
+
 /* Vector Instructions */
 #define VSX_XX1(xs, ra, rb)    (((xs) & 0x1f) << 21 | ((ra) << 16) |  \
                                 ((rb) << 11) | (((xs) >> 5)))
index ae43a614835de78756efdc60db92668575361adf..7636bf45d5d5eb0bab70dbbd7d50d508a40498c2 100644 (file)
@@ -102,8 +102,10 @@ do {                                                               \
 
 #if defined(__powerpc64__)
 #define UCONTEXT_NIA(UC)       (UC)->uc_mcontext.gp_regs[PT_NIP]
+#define UCONTEXT_MSR(UC)       (UC)->uc_mcontext.gp_regs[PT_MSR]
 #elif defined(__powerpc__)
 #define UCONTEXT_NIA(UC)       (UC)->uc_mcontext.uc_regs->gregs[PT_NIP]
+#define UCONTEXT_MSR(UC)       (UC)->uc_mcontext.uc_regs->gregs[PT_MSR]
 #else
 #error implement UCONTEXT_NIA
 #endif
index 208452a93e2ca8c2567a29b9873812b66dc08fac..951fe855f7cd465a03dc1f03b344bbaa0d8f9264 100644 (file)
@@ -11,6 +11,7 @@ tm-signal-context-chk-fpu
 tm-signal-context-chk-gpr
 tm-signal-context-chk-vmx
 tm-signal-context-chk-vsx
+tm-signal-context-force-tm
 tm-signal-sigreturn-nt
 tm-vmx-unavail
 tm-unavailable
index 75a6853591294d3fe07f0cc85029389b567b7698..c0734ed0ef56bb67ed9330ff2f84462759aca97a 100644 (file)
@@ -4,7 +4,8 @@ SIGNAL_CONTEXT_CHK_TESTS := tm-signal-context-chk-gpr tm-signal-context-chk-fpu
 
 TEST_GEN_PROGS := tm-resched-dscr tm-syscall tm-signal-msr-resv tm-signal-stack \
        tm-vmxcopy tm-fork tm-tar tm-tmspr tm-vmx-unavail tm-unavailable tm-trap \
-       $(SIGNAL_CONTEXT_CHK_TESTS) tm-sigreturn tm-signal-sigreturn-nt
+       $(SIGNAL_CONTEXT_CHK_TESTS) tm-sigreturn tm-signal-sigreturn-nt \
+       tm-signal-context-force-tm
 
 top_srcdir = ../../../../..
 include ../../lib.mk
@@ -20,6 +21,7 @@ $(OUTPUT)/tm-vmx-unavail: CFLAGS += -pthread -m64
 $(OUTPUT)/tm-resched-dscr: ../pmu/lib.c
 $(OUTPUT)/tm-unavailable: CFLAGS += -O0 -pthread -m64 -Wno-error=uninitialized -mvsx
 $(OUTPUT)/tm-trap: CFLAGS += -O0 -pthread -m64
+$(OUTPUT)/tm-signal-context-force-tm: CFLAGS += -pthread -m64
 
 SIGNAL_CONTEXT_CHK_TESTS := $(patsubst %,$(OUTPUT)/%,$(SIGNAL_CONTEXT_CHK_TESTS))
 $(SIGNAL_CONTEXT_CHK_TESTS): tm-signal.S
diff --git a/tools/testing/selftests/powerpc/tm/tm-signal-context-force-tm.c b/tools/testing/selftests/powerpc/tm/tm-signal-context-force-tm.c
new file mode 100644 (file)
index 0000000..3171762
--- /dev/null
@@ -0,0 +1,184 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2018, Breno Leitao, Gustavo Romero, IBM Corp.
+ *
+ * This test raises a SIGUSR1 signal, and toggle the MSR[TS]
+ * fields at the signal handler. With MSR[TS] being set, the kernel will
+ * force a recheckpoint, which may cause a segfault when returning to
+ * user space. Since the test needs to re-run, the segfault needs to be
+ * caught and handled.
+ *
+ * In order to continue the test even after a segfault, the context is
+ * saved prior to the signal being raised, and it is restored when there is
+ * a segmentation fault. This happens for COUNT_MAX times.
+ *
+ * This test never fails (as returning EXIT_FAILURE). It either succeeds,
+ * or crash the kernel (on a buggy kernel).
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <ucontext.h>
+#include <unistd.h>
+#include <sys/mman.h>
+
+#include "tm.h"
+#include "utils.h"
+#include "reg.h"
+
+#define COUNT_MAX       5000           /* Number of interactions */
+
+/*
+ * This test only runs on 64 bits system. Unsetting MSR_TS_S to avoid
+ * compilation issue on 32 bits system. There is no side effect, since the
+ * whole test will be skipped if it is not running on 64 bits system.
+ */
+#ifndef __powerpc64__
+#undef  MSR_TS_S
+#define MSR_TS_S       0
+#endif
+
+/* Setting contexts because the test will crash and we want to recover */
+ucontext_t init_context, main_context;
+
+static int count, first_time;
+
+void usr_signal_handler(int signo, siginfo_t *si, void *uc)
+{
+       ucontext_t *ucp = uc;
+       int ret;
+
+       /*
+        * Allocating memory in a signal handler, and never freeing it on
+        * purpose, forcing the heap increase, so, the memory leak is what
+        * we want here.
+        */
+       ucp->uc_link = mmap(NULL, sizeof(ucontext_t),
+                           PROT_READ | PROT_WRITE,
+                           MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
+       if (ucp->uc_link == (void *)-1) {
+               perror("Mmap failed");
+               exit(-1);
+       }
+
+       /* Forcing the page to be allocated in a page fault */
+       ret = madvise(ucp->uc_link, sizeof(ucontext_t), MADV_DONTNEED);
+       if (ret) {
+               perror("madvise failed");
+               exit(-1);
+       }
+
+       memcpy(&ucp->uc_link->uc_mcontext, &ucp->uc_mcontext,
+               sizeof(ucp->uc_mcontext));
+
+       /* Forcing to enable MSR[TM] */
+       UCONTEXT_MSR(ucp) |= MSR_TS_S;
+
+       /*
+        * A fork inside a signal handler seems to be more efficient than a
+        * fork() prior to the signal being raised.
+        */
+       if (fork() == 0) {
+               /*
+                * Both child and parent will return, but, child returns
+                * with count set so it will exit in the next segfault.
+                * Parent will continue to loop.
+                */
+               count = COUNT_MAX;
+       }
+
+       /*
+        * If the change above does not hit the bug, it will cause a
+        * segmentation fault, since the ck structures are NULL.
+        */
+}
+
+void seg_signal_handler(int signo, siginfo_t *si, void *uc)
+{
+       if (count == COUNT_MAX) {
+               /* Return to tm_signal_force_msr() and exit */
+               setcontext(&main_context);
+       }
+
+       count++;
+
+       /* Reexecute the test */
+       setcontext(&init_context);
+}
+
+void tm_trap_test(void)
+{
+       struct sigaction usr_sa, seg_sa;
+       stack_t ss;
+
+       usr_sa.sa_flags = SA_SIGINFO | SA_ONSTACK;
+       usr_sa.sa_sigaction = usr_signal_handler;
+
+       seg_sa.sa_flags = SA_SIGINFO;
+       seg_sa.sa_sigaction = seg_signal_handler;
+
+       /*
+        * Set initial context. Will get back here from
+        * seg_signal_handler()
+        */
+       getcontext(&init_context);
+
+       /* Allocated an alternative signal stack area */
+       ss.ss_sp = mmap(NULL, SIGSTKSZ, PROT_READ | PROT_WRITE,
+                       MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
+       ss.ss_size = SIGSTKSZ;
+       ss.ss_flags = 0;
+
+       if (ss.ss_sp == (void *)-1) {
+               perror("mmap error\n");
+               exit(-1);
+       }
+
+       /* Force the allocation through a page fault */
+       if (madvise(ss.ss_sp, SIGSTKSZ, MADV_DONTNEED)) {
+               perror("madvise\n");
+               exit(-1);
+       }
+
+       /* Setting an alternative stack to generate a page fault when
+        * the signal is raised.
+        */
+       if (sigaltstack(&ss, NULL)) {
+               perror("sigaltstack\n");
+               exit(-1);
+       }
+
+       /* The signal handler will enable MSR_TS */
+       sigaction(SIGUSR1, &usr_sa, NULL);
+       /* If it does not crash, it will segfault, avoid it to retest */
+       sigaction(SIGSEGV, &seg_sa, NULL);
+
+       raise(SIGUSR1);
+}
+
+int tm_signal_context_force_tm(void)
+{
+       SKIP_IF(!have_htm());
+       /*
+        * Skipping if not running on 64 bits system, since I think it is
+        * not possible to set mcontext's [MSR] with TS, due to it being 32
+        * bits.
+        */
+       SKIP_IF(!is_ppc64le());
+
+       /* Will get back here after COUNT_MAX interactions */
+       getcontext(&main_context);
+
+       if (!first_time++)
+               tm_trap_test();
+
+       return EXIT_SUCCESS;
+}
+
+int main(int argc, char **argv)
+{
+       test_harness(tm_signal_context_force_tm, "tm_signal_context_force_tm");
+}