From f76aa4ade354649e3291b5e7274c368740b05417 Mon Sep 17 00:00:00 2001 From: bb-cat Date: Wed, 2 Mar 2022 15:05:34 +0800 Subject: [PATCH 14/16] zdtm: add rseq transition test for amd64 Signed-off-by: Alexander Mikhalitsyn --- test/zdtm/transition/Makefile | 1 + test/zdtm/transition/rseq01.c | 208 +++++++++++++++++++ test/zdtm/transition/rseq01.desc | 1 + 3 files changed, 210 insertions(+) create mode 100644 test/zdtm/transition/rseq01.c create mode 100644 test/zdtm/transition/rseq01.desc diff --git a/test/zdtm/transition/Makefile b/test/zdtm/transition/Makefile index 9388157..fae4e27 100644 --- a/test/zdtm/transition/Makefile +++ b/test/zdtm/transition/Makefile @@ -23,6 +23,7 @@ TST_NOFILE = \ lazy-thp \ pid_reuse \ pidfd_store_sk \ + rseq01 \ TST_FILE = \ diff --git a/test/zdtm/transition/rseq01.c b/test/zdtm/transition/rseq01.c new file mode 100644 index 0000000..5fac5a6 --- /dev/null +++ b/test/zdtm/transition/rseq01.c @@ -0,0 +1,208 @@ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "zdtmtst.h" + +#ifdef __has_include +# if __has_include ("sys/rseq.h") +# include +# endif +#endif + +#if defined(__x86_64__) + +#if defined(__x86_64__) && defined(RSEQ_SIG) +static inline void *thread_pointer(void) +{ + void *result; + asm("mov %%fs:0, %0" : "=r"(result)); + return result; +} + +static inline void unregister_old_rseq(void) +{ + /* unregister rseq */ + syscall(__NR_rseq, (void *)((char *)thread_pointer() + __rseq_offset), __rseq_size, 1, RSEQ_SIG); +} +#else +static inline void unregister_old_rseq(void) +{ +} +#endif + +const char *test_doc = "rseq() transition test"; +const char *test_author = "Alexander Mikhalitsyn "; + +/* parts of code borrowed from https://www.efficios.com/blog/2019/02/08/linux-restartable-sequences/ */ + +/* some useful definitions from kernel uapi */ +#ifndef RSEQ_SIG + +enum rseq_flags { + RSEQ_FLAG_UNREGISTER = (1 << 0), +}; + +struct rseq { + uint32_t cpu_id_start; + uint32_t cpu_id; + uint64_t rseq_cs; + uint32_t flags; +} __attribute__((aligned(4 * sizeof(uint64_t)))); + +#define RSEQ_SIG 0x53053053 + +#endif + +#ifndef __NR_rseq +#define __NR_rseq 334 +#endif +/* EOF */ + +static volatile struct rseq *rseq_ptr; +static __thread volatile struct rseq __rseq_abi; + +static int sys_rseq(volatile struct rseq *rseq_abi, uint32_t rseq_len, int flags, uint32_t sig) +{ + return syscall(__NR_rseq, rseq_abi, rseq_len, flags, sig); +} + +static void register_thread(void) +{ + int rc; + unregister_old_rseq(); + rc = sys_rseq(rseq_ptr, sizeof(struct rseq), 0, RSEQ_SIG); + if (rc) { + fail("Failed to register rseq"); + exit(1); + } +} + +static void check_thread(void) +{ + int rc; + rc = sys_rseq(rseq_ptr, sizeof(struct rseq), 0, RSEQ_SIG); + if (!(rc && errno == EBUSY)) { + fail("Failed to check rseq %d", rc); + exit(1); + } +} + +#define RSEQ_ACCESS_ONCE(x) (*(__volatile__ __typeof__(x) *)&(x)) + +static int rseq_addv(intptr_t *v, intptr_t count, int cpu) +{ + double a = 10000000000000000.0; + double b = -1; + /*test_msg("enter %f %f\n", a, b);*/ + + /* clang-format off */ + __asm__ __volatile__ goto( + ".pushsection __rseq_table, \"aw\"\n\t" + ".balign 32\n\t" + "cs_obj:\n\t" + /* version, flags */ + ".long 0, 0\n\t" + /* start_ip, post_commit_offset, abort_ip */ + ".quad 1f, (2f-1f), 4f\n\t" + ".popsection\n\t" + "1:\n\t" + "leaq cs_obj(%%rip), %%rax\n\t" + "movq %%rax, %[rseq_cs]\n\t" + "cmpl %[cpu_id], %[current_cpu_id]\n\t" + "jnz 4f\n\t" + "addq %[count], %[v]\n\t" /* final store */ + "mov $10000000, %%rcx\n\t" + "fldl %[x]\n\t" /* we have st clobbered */ + "5:\n\t" + "fsqrt\n\t" /* heavy instruction */ + "dec %%rcx\n\t" + "jnz 5b\n\t" + "fstpl %[y]\n\t" + "2:\n\t" + ".pushsection __rseq_failure, \"ax\"\n\t" + /* Disassembler-friendly signature: nopl (%rip). */ + ".byte 0x0f, 0xb9, 0x3d\n\t" + ".long 0x53053053\n\t" /* RSEQ_FLAGS */ + "4:\n\t" + /*"fstpl %[y]\n\t"*/ + "jmp %l[abort]\n\t" + /*"jmp 1b\n\t"*/ + ".popsection\n\t" + : /* gcc asm goto does not allow outputs */ + : [cpu_id] "r" (cpu), + [current_cpu_id] "m" (rseq_ptr->cpu_id), + [rseq_cs] "m" (rseq_ptr->rseq_cs), + /* final store input */ + [v] "m" (*v), + [count] "er" (count), + [x] "m" (a), + [y] "m" (b) + : "memory", "cc", "rax", "rcx", "st" + : abort + ); + /* clang-format on */ + /*test_msg("exit %f %f\n", a, b);*/ + return 0; +abort: + /*test_msg("abort %f %f\n", a, b);*/ + return -1; +} + +int main(int argc, char *argv[]) +{ + int cpu = 0; + int ret; + intptr_t *cpu_data; + long nr_cpus; + + rseq_ptr = &__rseq_abi; + memset((void *)rseq_ptr, 0, sizeof(struct rseq)); + + test_init(argc, argv); + nr_cpus = sysconf(_SC_NPROCESSORS_ONLN); + + cpu_data = calloc(nr_cpus, sizeof(*cpu_data)); + if (!cpu_data) { + fail("calloc"); + exit(EXIT_FAILURE); + } + register_thread(); + + test_daemon(); + + while (test_go()) { + cpu = RSEQ_ACCESS_ONCE(rseq_ptr->cpu_id_start); + ret = rseq_addv(&cpu_data[cpu], 2, cpu); + if (ret) + fail("Failed to increment per-cpu counter"); + } + + test_waitsig(); + + check_thread(); + pass(); + + return 0; +} + +#else + +int main(int argc, char *argv[]) +{ + test_init(argc, argv); + skip("Unsupported arch"); + return 0; +} + +#endif diff --git a/test/zdtm/transition/rseq01.desc b/test/zdtm/transition/rseq01.desc new file mode 100644 index 0000000..0324fa3 --- /dev/null +++ b/test/zdtm/transition/rseq01.desc @@ -0,0 +1 @@ +{'flavor': 'h', 'arch': 'x86_64', 'feature': 'get_rseq_conf'} -- 2.30.0