[CRIU] [PATCH cr] zdtm: add new test case for testing logic about COW memory

Andrey Vagin avagin at openvz.org
Sun Oct 28 08:57:34 EDT 2012


This test tests the following combinations:

1. parent and child have a cow-ed mapping
2. parent and child have just coinciding mappings

the pages state in both should be in all possible combinations of

 a. write mapped
 b. read mapped
 c. not touched
 d. before fork
 e. after fork

Signed-off-by: Andrey Vagin <avagin at openvz.org>
---
 test/zdtm.sh                   |    1 +
 test/zdtm/lib/datagen.c        |   25 ++++
 test/zdtm/lib/zdtmtst.h        |    4 +
 test/zdtm/live/static/Makefile |    1 +
 test/zdtm/live/static/cow01.c  |  288 ++++++++++++++++++++++++++++++++++++++++
 5 files changed, 319 insertions(+), 0 deletions(-)
 create mode 100644 test/zdtm/live/static/cow01.c

diff --git a/test/zdtm.sh b/test/zdtm.sh
index 094fe96..950e8ef 100644
--- a/test/zdtm.sh
+++ b/test/zdtm.sh
@@ -63,6 +63,7 @@ static/pty00
 static/pty01
 static/pty04
 static/cow00
+static/cow01
 "
 # Duplicate list with ns/ prefix
 TEST_LIST=$TEST_LIST$(echo $TEST_LIST | tr ' ' '\n' | sed 's#^#ns/#')
diff --git a/test/zdtm/lib/datagen.c b/test/zdtm/lib/datagen.c
index 29d0993..fc4be3d 100644
--- a/test/zdtm/lib/datagen.c
+++ b/test/zdtm/lib/datagen.c
@@ -37,6 +37,23 @@ void datagen(uint8_t *buffer, unsigned length, uint32_t *crc)
 	}
 }
 
+void datagen2(uint8_t *buffer, unsigned length, uint32_t *crc)
+{
+	uint32_t rnd = 0;
+	unsigned shift;
+
+	for (shift = 0; length-- > 0; buffer++, shift--, rnd >>= 8) {
+		if (!shift) {
+			shift = 4;
+			rnd = mrand48();
+		}
+
+		*buffer = rnd;
+		if (crc)
+			*crc = crc32_le8(*crc, *buffer);
+	}
+}
+
 int datachk(const uint8_t *buffer, unsigned length, uint32_t *crc)
 {
 	uint32_t read_crc;
@@ -54,3 +71,11 @@ int datachk(const uint8_t *buffer, unsigned length, uint32_t *crc)
 	}
 	return 0;
 }
+
+int datasum(const uint8_t *buffer, unsigned length, uint32_t *crc)
+{
+	for (; length-- > 0; buffer++)
+		*crc = crc32_le8(*crc, *buffer);
+
+	return 0;
+}
diff --git a/test/zdtm/lib/zdtmtst.h b/test/zdtm/lib/zdtmtst.h
index d5b2d39..11b22a0 100644
--- a/test/zdtm/lib/zdtmtst.h
+++ b/test/zdtm/lib/zdtmtst.h
@@ -32,8 +32,12 @@ extern void test_waitsig(void);
 
 /* generate data with crc32 at the end of the buffer */
 extern void datagen(uint8_t *buffer, unsigned length, uint32_t *crc);
+/* generate data without crc32 at the end of the buffer */
+extern void datagen2(uint8_t *buffer, unsigned length, uint32_t *crc);
 /* check the data buffer against its crc32 */
 extern int datachk(const uint8_t *buffer, unsigned length, uint32_t *crc);
+/* calculate crc for the data buffer*/
+extern int datasum(const uint8_t *buffer, unsigned length, uint32_t *crc);
 
 /* streaming helpers */
 extern int set_nonblock(int fd, int on);
diff --git a/test/zdtm/live/static/Makefile b/test/zdtm/live/static/Makefile
index 2b6be83..76e6a62 100644
--- a/test/zdtm/live/static/Makefile
+++ b/test/zdtm/live/static/Makefile
@@ -72,6 +72,7 @@ TST_NOFILE	=				\
 		socket-ext			\
 		unhashed_proc			\
 		cow00				\
+		cow01				\
 		posix_timers			\
 #		jobctl00			\
 
diff --git a/test/zdtm/live/static/cow01.c b/test/zdtm/live/static/cow01.c
new file mode 100644
index 0000000..20a1c62
--- /dev/null
+++ b/test/zdtm/live/static/cow01.c
@@ -0,0 +1,288 @@
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+#include <unistd.h>
+#include <sys/wait.h>
+#include <linux/limits.h>
+#include <sys/user.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc	= "Check that cow memory are restored";
+const char *test_author	= "Andrey Vagin <avagin at parallels.com";
+
+struct test_case {
+	union {
+		struct {
+			uint8_t b_f_write:1;		/* before fork */
+			uint8_t b_f_read:1;
+			uint8_t a_f_write_child:1;	/* after fork */
+			uint8_t a_f_write_parent:1;
+			uint8_t a_f_read_child:1;
+			uint8_t a_f_read_parent:1;
+#define			TEST_CASES (2 << (6))
+		};
+		uint8_t num;
+	};
+
+	uint32_t crc_parent;
+	uint32_t crc_child;
+} cow_test_cases[TEST_CASES], sep_test_cases[TEST_CASES];
+
+uint8_t *addr_sep;
+uint8_t *addr_cow;
+uint32_t zero_crc = ~1;
+
+static int is_cow(void *addr, pid_t p1, pid_t p2)
+{
+	char buf[PATH_MAX];
+	unsigned long pfn = (unsigned long) addr / PAGE_SIZE;
+	uint64_t map1, map2;
+	int fd1, fd2, ret, i;
+
+	snprintf(buf, sizeof(buf), "/proc/%d/pagemap", p1);
+	fd1 = open(buf, O_RDONLY);
+	if (fd1 < 0) {
+		err("Unable to open file %s", buf);
+		return -1;
+	}
+
+	snprintf(buf, sizeof(buf), "/proc/%d/pagemap", p2);
+	fd2 = open(buf, O_RDONLY);
+	if (fd1 < 0) {
+		err("Unable to open file %s", buf);
+		return -1;
+	}
+
+	/*
+	 * A page can be swapped or unswapped,
+	 * so we should do several iterations.
+	 */
+	for (i = 0; i < 10; i++) {
+		lseek(fd1, pfn * sizeof(map1), SEEK_SET);
+		lseek(fd2, pfn * sizeof(map2), SEEK_SET);
+
+		ret = read(fd1, &map1, sizeof(map1));
+		if (ret != sizeof(map1)) {
+			err("Unable to read data");
+			return -1;
+		}
+
+		ret = read(fd2, &map2, sizeof(map2));
+		if (ret != sizeof(map2)) {
+			err("Unable to read data");
+			return -1;
+		}
+
+		if (map1 == map2)
+			break;
+	}
+
+	close(fd1);
+	close(fd2);
+
+	return map1 == map2;
+}
+
+void child_prep(struct test_case *test_cases, uint8_t *addr)
+{
+	int i;
+
+	for (i = 0; i < TEST_CASES; i++) {
+		struct test_case *tc = test_cases + i;
+		if (tc->a_f_write_child) {
+			tc->crc_child = ~1;
+			datagen2(addr + i * PAGE_SIZE, PAGE_SIZE, &tc->crc_child);
+		}
+		if (tc->a_f_read_child) {
+			uint32_t crc = ~1;
+
+			datasum(addr + i * PAGE_SIZE, PAGE_SIZE, &crc);
+		}
+	}
+}
+
+int child_check(struct test_case *test_cases, uint8_t *addr)
+{
+	int i, ret = 0;
+
+	for (i = 0; i < TEST_CASES; i++) {
+		uint32_t crc = ~1;
+
+		datasum(addr + i * PAGE_SIZE, PAGE_SIZE, &crc);
+		if (crc != test_cases[i].crc_child) {
+			fail("%d: %p data mismatch\n", i, addr + i * PAGE_SIZE);
+			ret++;
+		}
+	}
+
+	return ret;
+}
+
+static int child()
+{
+	int ret = 0;
+
+	addr_sep = mmap(addr_sep, PAGE_SIZE * TEST_CASES,
+				PROT_READ | PROT_WRITE,
+				MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
+	if (addr_sep == MAP_FAILED) {
+		err("Can't allocate memory\n");
+		return 1;
+	}
+
+	child_prep(cow_test_cases, addr_cow);
+	child_prep(sep_test_cases, addr_sep);
+
+	test_waitsig();
+
+	ret += child_check(cow_test_cases, addr_cow);
+	ret += child_check(sep_test_cases, addr_sep);
+
+	return ret ? 1: 0;
+}
+
+void parent_before_fork(struct test_case *test_cases, uint8_t *addr)
+{
+	int i;
+
+	for (i = 0; i < TEST_CASES; i++) {
+		struct test_case *tc = test_cases + i;
+		tc->num = i;
+		tc->crc_parent = zero_crc;
+		tc->crc_child = zero_crc;
+		if (tc->b_f_write) {
+			tc->crc_parent = ~1;
+			datagen2(addr + i * PAGE_SIZE, PAGE_SIZE, &tc->crc_parent);
+			if (addr == addr_cow)
+				tc->crc_child = tc->crc_parent;
+		}
+		if (tc->b_f_read) {
+			uint32_t crc = ~1;
+
+			datasum(addr + i * PAGE_SIZE, PAGE_SIZE, &crc);
+		}
+	}
+}
+
+void parent_post_fork(struct test_case *test_cases, uint8_t *addr)
+{
+	int i;
+
+	for (i = 0; i < TEST_CASES; i++) {
+		struct test_case *tc = test_cases + i;
+		if (tc->a_f_write_parent) {
+			tc->crc_parent = ~1;
+			datagen2(addr + i * PAGE_SIZE, PAGE_SIZE, &tc->crc_parent);
+		}
+
+		if (tc->a_f_read_parent) {
+			uint32_t crc = ~1;
+
+			datasum(addr + i * PAGE_SIZE, PAGE_SIZE, &crc);
+		}
+	}
+}
+
+int parent_check(struct test_case *test_cases, uint8_t *addr, pid_t pid)
+{
+	int i, ret = 0;
+
+	for (i = 0; i < TEST_CASES; i++) {
+		uint32_t crc = ~1;
+
+		datasum(addr + i * PAGE_SIZE, PAGE_SIZE, &crc);
+		if (crc != test_cases[i].crc_parent) {
+			fail("%x: %p data mismatch\n", i, addr + i * PAGE_SIZE);
+			ret++;
+		}
+
+		if (addr != addr_cow)
+			continue;
+
+		if (!test_cases[i].a_f_write_child &&
+		    !test_cases[i].a_f_write_parent &&
+		    test_cases[i].b_f_write)
+			if (!is_cow(addr + i * PAGE_SIZE, pid, getpid())) {
+				fail("%x: %p is not COW-ed\n", i, addr + i * PAGE_SIZE);
+				ret++;
+			}
+	}
+
+	return ret;
+}
+
+int main(int argc, char ** argv)
+{
+	uint8_t zero_page[PAGE_SIZE];
+	int status, err = 0;
+	pid_t pid;
+
+	memset(zero_page, 0, sizeof(zero_page));
+
+	datasum(zero_page, sizeof(zero_page), &zero_crc);
+
+	test_init(argc, argv);
+
+	addr_cow = mmap(NULL, PAGE_SIZE * TEST_CASES + 2,
+				PROT_READ | PROT_WRITE,
+				MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+	if (addr_cow == MAP_FAILED) {
+		err("Can't allocate memory\n");
+		return 1;
+	}
+
+	/*
+	 * Guard pages are used for preventing merging with other vma-s.
+	 * In parent cow-ed and coinciding regions can be merged, but
+	 * in child they cannot be, so COW will not be restored. FIXME
+	 */
+	mmap(addr_cow, PAGE_SIZE, PROT_NONE,
+			MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
+	addr_cow += PAGE_SIZE;
+	mmap(addr_cow + PAGE_SIZE * TEST_CASES, PAGE_SIZE, PROT_NONE,
+			MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
+
+	test_msg("cow_addr=%p\n", addr_cow);
+
+	addr_sep = mmap(NULL, PAGE_SIZE * TEST_CASES,
+				PROT_READ | PROT_WRITE,
+				MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+	if (addr_sep == MAP_FAILED) {
+		err("Can't allocate memory\n");
+		return 1;
+	}
+
+	parent_before_fork(cow_test_cases, addr_cow);
+	parent_before_fork(sep_test_cases, addr_sep);
+
+	pid = test_fork();
+	if (pid < 0)
+		return -1;
+
+	if (pid == 0)
+		return child();
+
+	parent_post_fork(cow_test_cases, addr_cow);
+	parent_post_fork(sep_test_cases, addr_sep);
+
+	test_daemon();
+
+	test_waitsig();
+
+	err += parent_check(cow_test_cases, addr_cow, pid);
+	err += parent_check(sep_test_cases, addr_sep, pid);
+
+	kill(pid, SIGTERM);
+	wait(&status);
+
+	if (status)
+		return 1;
+
+	if (err == 0)
+		pass();
+
+	return 0;
+}
-- 
1.7.1



More information about the CRIU mailing list