From ed5f540c9975478ad69c538bb26f70c5fe6b22f4 Mon Sep 17 00:00:00 2001 From: Andrey Vagin Date: Tue, 20 Nov 2012 20:48:36 +0400 Subject: [PATCH] zdtm: add new test case for testing logic about COW memory (v2) 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 v2: * wait until a child are not prepared * check a FILE | PRIVATE mappings Signed-off-by: Andrey Vagin Signed-off-by: Pavel Emelyanov --- 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 | 405 +++++++++++++++++++++++++++++++++ 5 files changed, 436 insertions(+) create mode 100644 test/zdtm/live/static/cow01.c diff --git a/test/zdtm.sh b/test/zdtm.sh index 7842ac5b7..56f1d5391 100644 --- a/test/zdtm.sh +++ b/test/zdtm.sh @@ -70,6 +70,7 @@ static/pty01 static/pty04 static/tty02 static/child_opened_proc +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 29d099334..fc4be3de2 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 800541b2a..6a2156912 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 9769909cf..e36571397 100644 --- a/test/zdtm/live/static/Makefile +++ b/test/zdtm/live/static/Makefile @@ -104,6 +104,7 @@ TST_FILE = \ unlink_fifo \ unlink_fifo_wronly \ file_shared \ + cow01 \ TST_DIR = \ cwd00 \ diff --git a/test/zdtm/live/static/cow01.c b/test/zdtm/live/static/cow01.c new file mode 100644 index 000000000..bc457b44a --- /dev/null +++ b/test/zdtm/live/static/cow01.c @@ -0,0 +1,405 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "zdtmtst.h" + +const char *test_doc = "Check that cow memory are restored"; +const char *test_author = "Andrey Vagin addr; + + for (i = 0; i < TEST_CASES; i++) { + struct test_case *tc = test_cases->tc + 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); + } + } + + return 0; +} + +static int child_check(struct test_cases *test_cases) +{ + int i, ret = 0; + uint8_t *addr = test_cases->addr; + + for (i = 0; i < TEST_CASES; i++) { + uint32_t crc = ~1; + struct test_case *tc = test_cases->tc + i; + + datasum(addr + i * PAGE_SIZE, PAGE_SIZE, &crc); + if (crc != tc->crc_child) { + fail("%d: %p data mismatch\n", i, addr + i * PAGE_SIZE); + ret++; + } + } + + return ret; +} + +static int parent_before_fork(struct test_cases *test_cases) +{ + uint8_t *addr; + int i; + + if (test_cases->init(test_cases)) + return -1; + + addr = test_cases->addr; + + for (i = 0; i < TEST_CASES; i++) { + struct test_case *tc = test_cases->tc + i; + tc->num = i; + + if (tc->b_f_write) { + tc->crc_parent = ~1; + datagen2(addr + i * PAGE_SIZE, PAGE_SIZE, &tc->crc_parent); + if (test_cases != &sep_tcs) + tc->crc_child = tc->crc_parent; + } + if (tc->b_f_read) { + uint32_t crc = ~1; + + datasum(addr + i * PAGE_SIZE, PAGE_SIZE, &crc); + } + } + + return 0; +} + +static int parent_post_fork(struct test_cases *test_cases) +{ + uint8_t *addr = test_cases->addr; + int i; + + for (i = 0; i < TEST_CASES; i++) { + struct test_case *tc = test_cases->tc + 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); + } + } + + return 0; +} + +static int parent_check(struct test_cases *test_cases) +{ + uint8_t *addr = test_cases->addr; + int i, ret = 0; + + for (i = 0; i < TEST_CASES; i++) { + struct test_case *tc = test_cases->tc + i; + uint32_t crc = ~1; + + datasum(addr + i * PAGE_SIZE, PAGE_SIZE, &crc); + if (crc != tc->crc_parent) { + fail("%x: %p data mismatch\n", i, addr + i * PAGE_SIZE); + ret++; + } + + if (test_cases == &sep_tcs) + continue; + + if (!tc->a_f_write_child && + !tc->a_f_write_parent && + tc->b_f_write) + if (!is_cow(addr + i * PAGE_SIZE, child_pid, getpid())) { + fail("%x: %p is not COW-ed\n", i, addr + i * PAGE_SIZE); + ret++; + } + } + + return ret; +} + +static int init_cow(struct test_cases *tcs) +{ + int i; + void *addr; + + addr = mmap(NULL, PAGE_SIZE * (TEST_CASES + 2), + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (addr == 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, PAGE_SIZE, PROT_NONE, + MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0); + addr += PAGE_SIZE; + tcs->addr = addr; + mmap(addr + PAGE_SIZE * TEST_CASES, PAGE_SIZE, PROT_NONE, + MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0); + + test_msg("cow_addr=%p\n", addr); + for (i = 0; i < TEST_CASES; i++) { + struct test_case *tc = tcs->tc + i; + tc->crc_parent = zero_crc; + tc->crc_child = zero_crc; + } + + return 0; +} + +static int init_sep(struct test_cases *tcs) +{ + int i; + + tcs->addr = mmap(NULL, PAGE_SIZE * TEST_CASES, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (tcs->addr == MAP_FAILED) { + err("Can't allocate memory\n"); + return 1; + } + + test_msg("sep_addr=%p\n", tcs->addr); + for (i = 0; i < TEST_CASES; i++) { + struct test_case *tc = tcs->tc + i; + tc->crc_parent = zero_crc; + tc->crc_child = zero_crc; + } + + return 0; +} + +static int init_file(struct test_cases *tcs) +{ + int i, ret, fd; + uint8_t buf[PAGE_SIZE]; + uint32_t crc; + + fd = open(filename, O_TRUNC | O_CREAT | O_RDWR, 0600); + if (fd < 0) { + err("Unable to create a test file"); + return -1; + } + + for (i = 0; i < TEST_CASES; i++) { + struct test_case *tc = tcs->tc + i; + crc = ~1; + datagen2(buf, sizeof(buf), &crc); + ret = write(fd, buf, sizeof(buf)); + if (ret != sizeof(buf)) { + err("Unable to write data in a test file"); + return -1; + } + + tc->crc_parent = crc; + tc->crc_child = crc; + } + + tcs->addr = mmap(NULL, PAGE_SIZE * TEST_CASES, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_FILE, fd, 0); + if (tcs->addr == MAP_FAILED) { + err("Can't allocate memory\n"); + return 1; + } + + test_msg("file_addr=%p\n", tcs->addr); + close(fd); + + return 0; +} + +static int child(task_waiter_t *child_waiter) +{ + int ret = 0; + + sep_tcs.addr = mmap(sep_tcs.addr, PAGE_SIZE * TEST_CASES, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0); + if (sep_tcs.addr == MAP_FAILED) { + err("Can't allocate memory\n"); + return 1; + } + + EXECUTE_ACTION(child_prep); + + task_waiter_complete_current(child_waiter); + + test_waitsig(); + + ret = EXECUTE_ACTION(child_check); + + return ret ? -1: 0; +} + +int main(int argc, char ** argv) +{ + uint8_t zero_page[PAGE_SIZE]; + int status, err = 0; + task_waiter_t child_waiter; + + task_waiter_init(&child_waiter); + + memset(zero_page, 0, sizeof(zero_page)); + + datasum(zero_page, sizeof(zero_page), &zero_crc); + + test_init(argc, argv); + + if (EXECUTE_ACTION(parent_before_fork)) + return 1; + + child_pid = test_fork(); + if (child_pid < 0) + return -1; + + if (child_pid == 0) + return child(&child_waiter); + + task_waiter_wait4(&child_waiter, child_pid); + + EXECUTE_ACTION(parent_post_fork); + + test_daemon(); + + test_waitsig(); + + err = EXECUTE_ACTION(parent_check); + + kill(child_pid, SIGTERM); + wait(&status); + + unlink(filename); + + if (status) + return 1; + + if (err == 0) + pass(); + + return 0; +}