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; +}