Blob Blame History Raw
From dd043f2fbd5329ff45685ed2009163022f6a6ea3 Mon Sep 17 00:00:00 2001
From: Alexander Mikhalitsyn <alexander.mikhalitsyn@virtuozzo.com>
Date: Tue, 21 Dec 2021 23:40:01 +0300
Subject: [PATCH 06/10] zdtm: add simple test for rseq C/R

Signed-off-by: Alexander Mikhalitsyn <alexander.mikhalitsyn@virtuozzo.com>
---
 test/zdtm/static/Makefile  |   1 +
 test/zdtm/static/rseq.c    | 190 +++++++++++++++++++++++++++++++++++++
 test/zdtm/static/rseq.desc |   1 +
 3 files changed, 192 insertions(+)
 create mode 100644 test/zdtm/static/rseq.c
 create mode 100644 test/zdtm/static/rseq.desc

diff --git a/test/zdtm/static/Makefile b/test/zdtm/static/Makefile
index 4a21978b5..5a457606c 100644
--- a/test/zdtm/static/Makefile
+++ b/test/zdtm/static/Makefile
@@ -61,6 +61,7 @@ TST_NOFILE	:=				\
 		pthread02			\
 		pthread_timers			\
 		pthread_timers_h		\
+		rseq				\
 		vdso00				\
 		vdso01				\
 		vdso02				\
diff --git a/test/zdtm/static/rseq.c b/test/zdtm/static/rseq.c
new file mode 100644
index 000000000..f2fccb8f4
--- /dev/null
+++ b/test/zdtm/static/rseq.c
@@ -0,0 +1,190 @@
+/*
+ * test for rseq() syscall
+ * See also https://www.efficios.com/blog/2019/02/08/linux-restartable-sequences/
+ * https://github.com/torvalds/linux/commit/d7822b1e24f2df5df98c76f0e94a5416349ff759
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <string.h>
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <syscall.h>
+
+#include "zdtmtst.h"
+
+#if defined(__x86_64__)
+
+const char *test_doc = "Check that rseq() basic C/R works";
+const char *test_author = "Alexander Mikhalitsyn <alexander.mikhalitsyn@virtuozzo.com>";
+/* parts of code borrowed from https://www.efficios.com/blog/2019/02/08/linux-restartable-sequences/ */
+
+/* some useful definitions from kernel uapi */
+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))));
+
+#ifndef __NR_rseq
+#define __NR_rseq 334
+#endif
+/* EOF */
+
+static __thread volatile struct rseq __rseq_abi;
+
+#define RSEQ_SIG 0x53053053
+#define TESTRSEQ 1
+#ifdef TESTRSEQ
+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;
+	rc = sys_rseq(&__rseq_abi, sizeof(struct rseq), 0, RSEQ_SIG);
+	if (rc) {
+		fail("Failed to register rseq");
+		exit(1);
+	}
+}
+
+static void unregister_thread(void)
+{
+	int rc;
+	rc = sys_rseq(&__rseq_abi, sizeof(struct rseq), RSEQ_FLAG_UNREGISTER, RSEQ_SIG);
+	if (rc) {
+		fail("Failed to unregister rseq");
+		exit(1);
+	}
+}
+
+static void check_thread(void)
+{
+	int rc;
+	rc = sys_rseq(&__rseq_abi, 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)
+{
+	/* 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_ip, abort_ip */
+		".quad 1f, 2f, 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 */
+		"2:\n\t"
+		".pushsection __rseq_failure, \"ax\"\n\t"
+		/* Disassembler-friendly signature: nopl <sig>(%rip). */
+		".byte 0x0f, 0x1f, 0x05\n\t"
+		".long 0x53053053\n\t"	/* RSEQ_FLAGS */
+		"4:\n\t"
+		"jmp abort\n\t"
+		".popsection\n\t"
+		: /* gcc asm goto does not allow outputs */
+	       	: [cpu_id]              "r" (cpu),
+		[current_cpu_id]      "m" (__rseq_abi.cpu_id),
+		[rseq_cs]             "m" (__rseq_abi.rseq_cs),
+		/* final store input */
+		[v]                   "m" (*v),
+		[count]               "er" (count)
+		: "memory", "cc", "rax"
+		: abort
+	);
+	/* clang-format on */
+
+	return 0;
+abort:
+	return -1;
+}
+#endif
+int main(int argc, char *argv[])
+{
+#ifdef TESTRSEQ
+	int cpu, ret;
+#endif
+	intptr_t *cpu_data;
+	long nr_cpus = sysconf(_SC_NPROCESSORS_ONLN);
+
+	test_init(argc, argv);
+
+	cpu_data = calloc(nr_cpus, sizeof(*cpu_data));
+	if (!cpu_data) {
+		fail("calloc");
+		exit(EXIT_FAILURE);
+	}
+	register_thread();
+
+	test_msg("Ready for C/R\n");
+
+	test_daemon();
+	test_waitsig();
+#ifdef TESTRSEQ
+	check_thread();
+
+	cpu = RSEQ_ACCESS_ONCE(__rseq_abi.cpu_id_start);
+	ret = rseq_addv(&cpu_data[cpu], 2, cpu);
+	if (ret)
+		fail("Failed to increment per-cpu counter");
+	else
+		test_msg("cpu_data[%d] == %ld\n", cpu, (long int)cpu_data[cpu]);
+
+	/* TODO: remove this :) */
+	(void)cpu;
+	(void)ret;
+	(void)rseq_addv;
+	//unregister_thread();
+	(void)unregister_thread;
+	(void)register_thread;
+#endif
+	test_msg("cpu, cpu_data %d %ld\n", cpu, (long int)cpu_data[cpu]);
+	if (cpu_data[cpu] == 2)
+		pass();
+	else
+		fail();
+	//fail();
+	//unregister_thread();
+	//(void)unregister_thread;
+
+	return 0;
+}
+
+#else
+
+int main(int argc, char *argv[])
+{
+	test_init(argc, argv);
+	skip("Unsupported arch");
+	return 0;
+}
+
+#endif
\ No newline at end of file
diff --git a/test/zdtm/static/rseq.desc b/test/zdtm/static/rseq.desc
new file mode 100644
index 000000000..0324fa39c
--- /dev/null
+++ b/test/zdtm/static/rseq.desc
@@ -0,0 +1 @@
+{'flavor': 'h', 'arch': 'x86_64', 'feature': 'get_rseq_conf'}
-- 
2.34.1