[CRIU] [PATCH 2/3] s390/zdtm: Add s390x_reg_check test case

Alice Frosi alice at linux.vnet.ibm.com
Thu Aug 10 19:04:51 MSK 2017


From: Michael Holzheu <holzheu at linux.vnet.ibm.com>

This test can be used to verify FP and VX registers on s390:

- Verify that "criu restore" sets the correct register sets
  from "criu dump":
  $ zdtmp.py run -t zdtm/static/s390x_regs_check

- Verify that dumpee continues running with correct registers after
  parasite injection:
  $ zdtm.py run --norst -t zdtm/static/s390x_regs_check
  $ zdtm.py run --norst --pre 2 -t zdtm/static/s390x_regs_check
  $ zdtm.py run --check-only -t zdtm/static/s390x_regs_check

Reviewed-by: Alice Frosi <alice at linux.vnet.ibm.com>
Signed-off-by: Michael Holzheu <holzheu at linux.vnet.ibm.com>
---
 test/zdtm/static/Makefile              |   6 +
 test/zdtm/static/s390x_regs_check.c    | 494 +++++++++++++++++++++++++++++++++
 test/zdtm/static/s390x_regs_check.desc |   1 +
 3 files changed, 501 insertions(+)
 create mode 100644 test/zdtm/static/s390x_regs_check.c
 create mode 100644 test/zdtm/static/s390x_regs_check.desc

diff --git a/test/zdtm/static/Makefile b/test/zdtm/static/Makefile
index dbde9c4..13336ac 100644
--- a/test/zdtm/static/Makefile
+++ b/test/zdtm/static/Makefile
@@ -199,6 +199,10 @@ ifneq ($(COMPAT_TEST),y)
 endif
 endif
 
+ifeq ($(SRCARCH),s390)
+        TST_NOFILE += s390x_regs_check
+endif
+
 TST_FILE	=				\
 		maps06				\
 		write_read00			\
@@ -484,6 +488,8 @@ pidns03:		LDFLAGS += -pthread
 
 shm-unaligned:		CFLAGS += -DZDTM_SHM_UNALIGNED
 
+s390x_regs_check:	LDFLAGS += -pthread
+
 $(LIB):	force
 	$(Q) $(MAKE) -C $(LIBDIR)
 
diff --git a/test/zdtm/static/s390x_regs_check.c b/test/zdtm/static/s390x_regs_check.c
new file mode 100644
index 0000000..a92679a
--- /dev/null
+++ b/test/zdtm/static/s390x_regs_check.c
@@ -0,0 +1,494 @@
+#include <linux/elf.h>
+#include <pthread.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/ptrace.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Check that FP and VX registers do not change";
+const char *test_author = "Michael Holzheu <holzheu at linux.vnet.ibm.com>";
+
+/*
+ * This test case executes the following procedure:
+ *
+ * 1) Set registers to defined values
+ * The main process creates one child process and within that process
+ * NR_THREADS threads. Then the main process uses ptrace(SETREGS) to
+ * set the registers in the child process and in all threads.
+ *
+ * 2) Detach from child and threads
+ * Do this in order to allow criu to use ptrace for dumping.
+ *
+ * 3) Issue criu commands
+ * Useful tests are: dump, dump --check-only, dump --leave-running
+ *
+ * 4) Check registers
+ * Use ptrace(GETREGS) and compare with original values from step 1.
+ *
+ * This test can be used for two purposes:
+ *
+ * - Verify that "criu restore" sets the correct register sets
+ *   from "criu dump":
+ *   $ zdtmp.py run -t zdtm/static/s390x_regs_check
+ *
+ * - Verify that dumpee continues running with correct registers after
+ *   parasite injection:
+ *   $ zdtmp.py run --norst -t zdtm/static/s390x_regs_check
+ *   $ zdtmp.py run --norst --pre 2 -t zdtm/static/s390x_regs_check
+ *   $ zdtmp.py run --check-only -t zdtm/static/s390x_regs_check
+ */
+#define NR_THREADS 2
+#define NR_THREADS_ALL (NR_THREADS + 1)
+
+static pid_t thread_pids[NR_THREADS_ALL];
+static int pipefd[2];
+
+/*
+ * Generic structure to define a register set and test data
+ */
+struct reg_set {
+	const char *name;	/* Name of regset */
+	int nr;			/* Number of regset */
+	void *data;		/* Test data */
+	int len;		/* Number of bytes of test data */
+	bool optional;		/* Not all kernels/machines have this reg set */
+	bool available;		/* Current kernel/machine has this reg set */
+};
+
+/*
+ * s390 floating point registers
+ */
+struct prfpreg {
+	uint32_t	fpc;
+	uint64_t	fprs[16];
+};
+
+struct prfpreg prfpreg_data = {
+	.fpc = 0,
+	.fprs = {
+		0x0000000000000000,
+		0x1111111111111110,
+		0x2222222222222220,
+		0x3333333333333330,
+		0x4444444444444440,
+		0x5555555555555550,
+		0x6666666666666660,
+		0x7777777777777770,
+		0x8888888888888880,
+		0x9999999999999990,
+		0xaaaaaaaaaaaaaaa0,
+		0xbbbbbbbbbbbbbbb0,
+		0xccccccccccccccc0,
+		0xddddddddddddddd0,
+		0xeeeeeeeeeeeeeee0,
+		0xfffffffffffffff0,
+	}
+};
+
+struct reg_set reg_set_prfpreg = {
+	.name		= "PRFPREG",
+	.nr		= NT_PRFPREG,
+	.data		= &prfpreg_data,
+	.len		= sizeof(prfpreg_data),
+	.optional	= false,
+};
+
+/*
+ * s390 vector VXRS_LOW registers
+ */
+
+#define NT_S390_VXRS_LOW	0x309
+
+struct vxrs_low {
+	uint64_t	regs[16];
+};
+
+struct vxrs_low vxrs_low_data = {
+	.regs = {
+		0x0000000000000001,
+		0x1111111111111111,
+		0x2222222222222221,
+		0x3333333333333331,
+		0x4444444444444441,
+		0x5555555555555551,
+		0x6666666666666661,
+		0x7777777777777771,
+		0x8888888888888881,
+		0x9999999999999991,
+		0xaaaaaaaaaaaaaaa1,
+		0xbbbbbbbbbbbbbbb1,
+		0xccccccccccccccc1,
+		0xddddddddddddddd1,
+		0xeeeeeeeeeeeeeee1,
+		0xfffffffffffffff1,
+	}
+};
+
+struct reg_set reg_set_vxrs_low = {
+	.name		= "VXRS_LOW",
+	.nr		= NT_S390_VXRS_LOW,
+	.data		= &vxrs_low_data,
+	.len		= sizeof(vxrs_low_data),
+	.optional	= true,
+};
+
+/*
+ * s390 vector VXRS_HIGH registers
+ */
+
+#define NT_S390_VXRS_HIGH	0x30a
+
+struct vxrs_high {
+	uint64_t	regs[32];
+};
+
+struct vxrs_high vxrs_high_data = {
+	.regs = {
+		0x0000000000000002, 0x0000000000000002,
+		0x1111111111111112, 0x1111111111111112,
+		0x2222222222222222, 0x2222222222222222,
+		0x3333333333333332, 0x3333333333333332,
+		0x4444444444444442, 0x4444444444444442,
+		0x5555555555555552, 0x5555555555555552,
+		0x6666666666666662, 0x6666666666666662,
+		0x7777777777777772, 0x7777777777777772,
+		0x8888888888888882, 0x8888888888888882,
+		0x9999999999999992, 0x9999999999999992,
+		0xaaaaaaaaaaaaaaa2, 0xaaaaaaaaaaaaaaa2,
+		0xbbbbbbbbbbbbbbb2, 0xbbbbbbbbbbbbbbb2,
+		0xccccccccccccccc2, 0xccccccccccccccc2,
+		0xddddddddddddddd2, 0xddddddddddddddd2,
+		0xeeeeeeeeeeeeeee2, 0xeeeeeeeeeeeeeee2,
+		0xfffffffffffffff2, 0xfffffffffffffff2,
+	}
+};
+
+struct reg_set reg_set_vxrs_high = {
+	.name		= "VXRS_HIGH",
+	.nr		= NT_S390_VXRS_HIGH,
+	.data		= &vxrs_high_data,
+	.len		= sizeof(vxrs_high_data),
+	.optional	= true,
+};
+
+/*
+ * Vector with all regsets
+ */
+struct reg_set *reg_set_vec[] = {
+	&reg_set_prfpreg,
+	&reg_set_vxrs_low,
+	&reg_set_vxrs_high,
+	NULL,
+};
+
+/*
+ * Print hexdump for buffer with variable group parameter
+ */
+void util_hexdump_grp(const char *tag, const void *data, int grp,
+		      int count, int indent)
+{
+	const char *buf = data;
+	char str[1024], *ptr;
+	int i, first = 1;
+
+	for (i = 0; i < count; i++) {
+		if (first) {
+			ptr = str;
+			ptr += sprintf(ptr, "%*s", indent, " ");
+			if (tag)
+				ptr += sprintf(ptr, "%s: ", tag);
+			ptr += sprintf(ptr, "%08x: ", i);
+			first = 0;
+		}
+		ptr += sprintf(ptr, "%02x", buf[i]);
+		if (i % 16 == 15 || i + 1 == count) {
+			test_msg("%s\n", str);
+			first = 1;
+		} else if (i % grp == grp - 1) {
+			ptr += sprintf(ptr, " ");
+		}
+	}
+}
+
+/*
+ * Print hexdump for buffer with fix grp parameter
+ */
+void util_hexdump(const char *tag, const void *data, int count)
+{
+	util_hexdump_grp(tag, data, sizeof(long), count, 0);
+}
+
+/*
+ * Set regset for pid
+ */
+static int set_regset(pid_t pid, struct reg_set *reg_set)
+{
+	struct iovec iov;
+
+	iov.iov_base = reg_set->data;
+	iov.iov_len = reg_set->len;
+
+	if (ptrace(PTRACE_SETREGSET, pid, reg_set->nr, iov) == 0) {
+		test_msg(" REGSET: %12s -> DONE\n", reg_set->name);
+		reg_set->available = true;
+		return 0;
+	}
+	if (reg_set->optional) {
+		switch (errno) {
+		case ENODEV:
+			test_msg(" REGSET: %12s -> not supported by machine\n",
+				 reg_set->name);
+			return 0;
+		case EINVAL:
+			test_msg(" REGSET: %12s -> not supported by kernel\n",
+				 reg_set->name);
+			return 0;
+		default:
+			break;
+		}
+	}
+	pr_perror("PTRACE_SETREGSET for %s failed for pid %d",
+		  reg_set->name, pid);
+	return -1;
+}
+
+/*
+ * Apply all regsets
+ */
+static int set_regset_all(pid_t pid)
+{
+	int i;
+
+	for (i = 0; reg_set_vec[i] != NULL; i++) {
+		if (set_regset(pid, reg_set_vec[i]))
+			return -1;
+	}
+	return 0;
+}
+
+/*
+ * Check if regset for pid has changed
+ */
+static int check_regset(pid_t pid, struct reg_set *reg_set)
+{
+	struct iovec iov;
+	char *data;
+
+	if (!reg_set->available)
+		return 0;
+	data = calloc(reg_set->len, 1);
+	if (!data)
+		return -1;
+
+	iov.iov_base = data;
+	iov.iov_len = reg_set->len;
+
+	if (ptrace(PTRACE_GETREGSET, pid, reg_set->nr, iov) != 0) {
+		pr_perror("PTRACE_SETREGSET for %s failed for pid %d",
+			  reg_set->name, pid);
+		free(data);
+		return -1;
+	}
+	if (memcmp(data, reg_set->data, reg_set->len) != 0) {
+		test_msg("RegSet %s changed for pid=%d\n", reg_set->name, pid);
+		test_msg("Original values:\n");
+		util_hexdump(reg_set->name, reg_set->data, reg_set->len);
+		test_msg("New values:\n");
+		util_hexdump(reg_set->name, data, reg_set->len);
+		free(data);
+		return -1;
+	}
+	free(data);
+	return 0;
+}
+
+/*
+ * Check all regsets
+ */
+static int check_regset_all(pid_t pid)
+{
+	int i;
+
+	for (i = 0; reg_set_vec[i] != NULL; i++) {
+		if (check_regset(pid, reg_set_vec[i]))
+			return -1;
+	}
+	return 0;
+}
+
+/*
+ * Send error to father
+ */
+static void send_error(void)
+{
+	int val = 0;
+
+	if (write(pipefd[1], &val, sizeof(val)) == -1)
+		pr_perror("write failed");
+}
+
+/*
+ * Write tid to pipe and then loop without changing registers
+ */
+static inline void send_tid_and_loop(int fd)
+{
+	int tid = syscall(__NR_gettid);
+
+	asm volatile(
+		     "lgr	2,%0\n"	/* Arg 1: fd */
+		     "la	3,%1\n" /* Arg 2: &tid */
+		     "lghi	4,4\n"  /* Arg 3: sizeof(int) */
+		     "svc	4\n"	/* __NR_write SVC: */
+		     /* After SVC no more registers are changed */
+		     "0:	j 0b\n" /* Loop here */
+		     : : "d" (fd), "Q" (tid) : "2", "3", "4");
+}
+
+/*
+ * Function for threads
+ */
+static void *thread_func(void *fd)
+{
+	send_tid_and_loop(pipefd[1]);
+	return NULL;
+}
+
+/*
+ * Function executed by the child
+ */
+static void child_func(void)
+{
+	pthread_t thread;
+	int i;
+
+	/* Close read end of pipe */
+	close(pipefd[0]);
+	/* Create threads and send TID */
+	for (i = 0; i < NR_THREADS; i++) {
+		if (pthread_create(&thread, NULL, thread_func, NULL) != 0) {
+			pr_perror("Error create thread: %d", i);
+			send_error();
+		}
+	}
+	/* Send tid and wait until get killed */
+	send_tid_and_loop(pipefd[1]);
+}
+
+/*
+ * Attach to a thread
+ */
+static int ptrace_attach(pid_t pid)
+{
+	if (ptrace(PTRACE_ATTACH, pid, 0, 0) == 0) {
+		wait(NULL);
+		return 0;
+	}
+	pr_perror("Attach to thread %d failed", pid);
+	return -1;
+}
+
+/*
+ * Detach from a thread
+ */
+static int ptrace_detach(pid_t pid)
+{
+	if (ptrace(PTRACE_DETACH, pid, 0, 0) == 0)
+		return 0;
+	pr_perror("Detach from thread %d failed", pid);
+	return -1;
+}
+
+/*
+ * Create child with threads and verify that registers are not corrupted
+ */
+int main(int argc, char *argv[])
+{
+	bool failed = false;
+	pid_t child, pid;
+	int i;
+
+	test_init(argc, argv);
+
+	test_msg("------------- START 1 PROCESS + %d THREADS ---------------\n",
+	       NR_THREADS);
+	if (pipe(pipefd) == -1) {
+		perror("pipe failed");
+		exit(EXIT_FAILURE);
+	}
+	child = fork();
+
+	if (child == 0)
+		child_func();
+
+	/* Parent */
+	for (i = 0; i < NR_THREADS_ALL; i++) {
+		if (read(pipefd[0], &pid, sizeof(pid_t)) == -1) {
+			perror("Read from pipe failed");
+			failed = true;
+			goto kill_all_threads;
+		}
+		if (pid == 0) {
+			pr_err("Not all threads are started\n");
+			failed = true;
+			goto kill_all_threads;
+		}
+		test_msg("STARTED: pid = %d\n", pid);
+		thread_pids[i] = pid;
+	}
+
+	/* Close write end */
+	close(pipefd[1]);
+	test_msg("---------------------- SET REGISTERS --------------------\n");
+	for (i = 0; i < NR_THREADS_ALL; i++) {
+		pid = thread_pids[i];
+		test_msg("SET: pid = %d\n", pid);
+		ptrace_attach(pid);
+		set_regset_all(pid);
+		ptrace_detach(pid);
+	}
+
+	test_daemon();
+	test_waitsig();
+
+	test_msg("-------------------- CHECK REGISTERS --------------------\n");
+	for (i = 0; i < NR_THREADS_ALL; i++) {
+		pid = thread_pids[i];
+		test_msg("CHECK: pid = %d:\n", pid);
+		ptrace_attach(pid);
+		if (check_regset_all(pid) == 0) {
+			test_msg(" -> OK\n");
+		} else {
+			test_msg(" -> FAIL\n");
+			failed = true;
+		}
+		ptrace_detach(pid);
+	}
+	test_msg("----------------------- CLEANUP  ------------------------\n");
+
+kill_all_threads:
+	for (i = 0; i < NR_THREADS_ALL; i++) {
+		pid = thread_pids[i];
+		if (pid == 0)
+			continue;
+		test_msg("KILL: pid = %d\n", pid);
+		kill(pid, SIGTERM);
+	}
+
+	if (failed) {
+		fail("Registers changed");
+		return 1;
+	}
+	pass();
+	return 0;
+}
diff --git a/test/zdtm/static/s390x_regs_check.desc b/test/zdtm/static/s390x_regs_check.desc
new file mode 100644
index 0000000..8621263
--- /dev/null
+++ b/test/zdtm/static/s390x_regs_check.desc
@@ -0,0 +1 @@
+{'arch': 's390x'}
-- 
2.9.3



More information about the CRIU mailing list