Added testsync to help test sync.
Testsync works in conjunction with platform_SyncCrash to
test fsync and msync. The test writes data to a file,
calls a flavor of sync and then the crashes the system.
After the system comes back up, testsync verifies that
the data was saved correctly.
BUG=chromium:239536
TEST=platform_SyncCrash
Change-Id: Ieceba236a00b344ca96fdc5f44c52985e1c51492
Reviewed-on: https://gerrit.chromium.org/gerrit/56412
Tested-by: Paul Taysom <taysom@chromium.org>
Reviewed-by: Luigi Semenzato <semenzato@chromium.org>
Commit-Queue: Paul Taysom <taysom@chromium.org>
diff --git a/file.m/testsync.c b/file.m/testsync.c
new file mode 100644
index 0000000..30e5bc2
--- /dev/null
+++ b/file.m/testsync.c
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/*
+ * These tests verify that sync works correctly.
+ *
+ * Each test is written in two phases. The first sets up the test,
+ * writes the data, calls sync and then crashes the system.
+ *
+ * After the system reboots, the second phase of the test verifies
+ * the results and cleans up.
+ *
+ * Current phases:
+ * writer - writes to the given file, calls fsync, then crashes.
+ * mapper - uses memory mapped file to write data, calls msync,
+ * then crashes.
+ * verifier - verifies that all the data that was synced is there.
+ *
+ * Uses a 64 bit counter for the data.
+ */
+
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <debug.h>
+#include <eprintf.h>
+#include <esys.h>
+#include <puny.h>
+#include <style.h>
+#include <util.h>
+
+enum {
+ CHUNK_SIZE = (1<<13),
+ NUM_U64_PER_CHUNK = CHUNK_SIZE / sizeof(u64)
+};
+
+static char *PhaseName;
+typedef void (*phasefn_t)(void);
+
+struct phase {
+ char *name;
+ phasefn_t fn;
+};
+
+u64 Buf[NUM_U64_PER_CHUNK];
+u64 Value;
+
+static void fill(void)
+{
+ int i;
+
+ for (i = 0; i < NUM_U64_PER_CHUNK; i++)
+ Buf[i] = Value++;
+}
+
+static void verify(void)
+{
+ int i;
+
+ for (i = 0; i < NUM_U64_PER_CHUNK; i++) {
+ if (Buf[i] != Value)
+ fatal("Expected %lld is %lld", Value, Buf[i]);
+ Value++;
+ }
+}
+
+static void writer(void)
+{
+ u64 size = Option.file_size;
+ u64 chunks = size / sizeof(Buf);
+ u64 i;
+ int fd = eopen(Option.file, O_WRONLY | O_CREAT | O_TRUNC);
+
+ Value = 0;
+ for (i = 0; i < chunks; i++) {
+ fill();
+ ewrite(fd, Buf, sizeof(Buf));
+ }
+ efsync(fd);
+ crash();
+}
+
+static void mapper(void)
+{
+ u64 size = Option.file_size;
+ u64 chunks = size / sizeof(Buf);
+ u64 i;
+ u8 *map;
+ u8 *m;
+ int fd = eopen(Option.file, O_RDWR | O_CREAT | O_TRUNC);
+
+ Value = 0;
+ epwrite(fd, "", 1, size - 1);
+ map = emmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ for (m = map, i = 0; i < chunks; i++, m += sizeof(Buf)) {
+ fill();
+ memcpy(m, Buf, sizeof(Buf));
+ }
+ emsync(map, size, MS_SYNC);
+ crash();
+}
+
+static void verifier(void)
+{
+ u64 size = Option.file_size;
+ u64 blocks = size / sizeof(Buf);
+ u64 i;
+ int fd = eopen(Option.file, O_RDONLY);
+
+ Value = 0;
+ for (i = 0; i < blocks; i++) {
+ eread(fd, Buf, sizeof(Buf));
+ verify();
+ }
+ eclose(fd);
+}
+
+static struct phase Phase[] = {
+ { "writer", writer },
+ { "mapper", mapper },
+ { "verifier", verifier }
+};
+
+static phasefn_t find_phase(char *name)
+{
+ int n = sizeof(Phase) / sizeof(struct phase);
+ int i;
+
+ for (i = 0; i < n; i++)
+ if (strcmp(name, Phase[i].name) == 0)
+ return Phase[i].fn;
+ fatal("Phase %s: not found", name);
+ return NULL;
+}
+
+static bool myopt(int c)
+{
+ switch (c) {
+ case 'p':
+ PhaseName = strdup(optarg);
+ break;
+ default:
+ return FALSE;
+ }
+ return TRUE;
+}
+
+void usage (void)
+{
+ pr_usage("-f <file> -p <test phase name>\n"
+ "\tf - file to write");
+}
+
+int main(int argc, char *argv[])
+{
+ phasefn_t fn;
+
+ punyopt(argc, argv, myopt, "p:");
+ fn = find_phase(PhaseName);
+ if (fn)
+ fn();
+ return 0;
+}
diff --git a/include/esys.h b/include/esys.h
new file mode 100644
index 0000000..c655e66
--- /dev/null
+++ b/include/esys.h
@@ -0,0 +1,25 @@
+/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/*
+ * esys wraps all the system calls and calls fatal on errors.
+ */
+
+void eclose(int fd);
+int ecreat(const char *pathname);
+void efsync(int fd);
+void efdatasync(int fd);
+int eopen(const char *pathname, int flags);
+int eread(int fd, void *buf, size_t nbyte);
+int epread(int fd, void *buf, size_t nbyte, off_t offset);
+void esync(void);
+void esyncfs(int fd);
+void eunlink(const char *pathname);
+int ewrite(int fd, const void *buf, size_t nbyte);
+int epwrite(int fd, void *buf, size_t nbyte, off_t offset);
+void *emmap(void *addr, size_t length, int prot, int flags,
+ int fd, off_t offset);
+void emunmap(void *addr, size_t length);
+void emsync(void *addr, size_t length, int flags);
diff --git a/include/twister.h b/include/twister.h
index e2a5d93..21c99c9 100644
--- a/include/twister.h
+++ b/include/twister.h
@@ -57,6 +57,7 @@
#define _TWISTER_H_ 1
#include <style.h>
+#include <stdlib.h>
/*
* Interfaces to Mersenne Twister pseudorandom number generator.
diff --git a/include/util.h b/include/util.h
new file mode 100644
index 0000000..4a4e530
--- /dev/null
+++ b/include/util.h
@@ -0,0 +1,11 @@
+/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/*
+ * Miscellaneous utilities for writing tests.
+ */
+
+void gen_name(char *name, int len);
+void crash(void);
diff --git a/libpuny.b/esys.c b/libpuny.b/esys.c
new file mode 100644
index 0000000..d61558a
--- /dev/null
+++ b/libpuny.b/esys.c
@@ -0,0 +1,132 @@
+/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/*
+ * esys wraps all the system calls and generates fatal errors.
+ * Some arguments are hard coded.
+ */
+
+#define _GNU_SOURCE
+
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <eprintf.h>
+#include <esys.h>
+
+void eclose(int fd)
+{
+ int rc = close(fd);
+ if (rc == -1)
+ fatal("close:");
+}
+
+int ecreat(const char *pathname)
+{
+ int fd = creat(pathname, 0666);
+ if (fd == -1)
+ fatal("creat %s:", pathname);
+ return fd;
+}
+
+void efsync(int fd)
+{
+ int rc = fsync(fd);
+ if (rc == -1)
+ fatal("fsync:");
+}
+
+void efdatasync(int fd)
+{
+ int rc = fdatasync(fd);
+ if (rc == -1)
+ fatal("fdatasync:");
+}
+
+int eopen(const char *pathname, int flags)
+{
+ int fd = open(pathname, flags, 0666);
+ if (fd == -1)
+ fatal("open %s:", pathname);
+ return fd;
+}
+
+int eread(int fd, void *buf, size_t nbyte)
+{
+ ssize_t rc = read(fd, buf, nbyte);
+ if (rc == -1)
+ fatal("read:");
+ return rc;
+}
+
+int epread(int fd, void *buf, size_t nbyte, off_t offset)
+{
+ ssize_t rc = pread(fd, buf, nbyte, offset);
+ if (rc == -1)
+ fatal("pread:");
+ return rc;
+}
+
+void esync(void)
+{
+ sync();
+}
+
+void esyncfs(int fd)
+{
+ syncfs(fd);
+}
+
+void eunlink(const char *pathname)
+{
+ int rc = unlink(pathname);
+ if (rc == -1)
+ fatal("unlink %s:", pathname);
+}
+
+int ewrite(int fildes, const void *buf, size_t nbyte)
+{
+ ssize_t rc = write(fildes, buf, nbyte);
+ if (rc == -1)
+ fatal("write:");
+ return rc;
+}
+
+int epwrite(int fd, void *buf, size_t nbyte, off_t offset)
+{
+ ssize_t rc;
+
+ rc = pwrite(fd, buf, nbyte, offset);
+ if (rc == -1)
+ fatal("pwrite:");
+ return rc;
+}
+
+void *emmap(void *addr, size_t length, int prot, int flags,
+ int fd, off_t offset)
+{
+ void *map = mmap(addr, length, prot, flags, fd, offset);
+ if (map == MAP_FAILED)
+ fatal("mmap:");
+ return map;
+}
+
+void emunmap(void *addr, size_t length)
+{
+ int rc = munmap(addr, length);
+ if (rc == -1)
+ fatal("munmap:");
+}
+
+void emsync(void *addr, size_t length, int flags)
+{
+ int rc = msync(addr, length, flags);
+ if (rc == -1)
+ fatal("msync;");
+}
+
diff --git a/libpuny.b/util.c b/libpuny.b/util.c
new file mode 100644
index 0000000..f80fbe4
--- /dev/null
+++ b/libpuny.b/util.c
@@ -0,0 +1,44 @@
+/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/*
+ * Miscellaneous utilities for writing tests.
+ */
+
+
+#include <fcntl.h>
+
+#include <esys.h>
+#include <twister.h>
+#include <util.h>
+
+/*
+ * gen_name: generate a random file name of the given length.
+ * The len include the null at the end.
+ */
+void gen_name(char *name, int len)
+{
+ static char file_name_char[] = "abcdefghijklmnopqrstuvwxyz"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "_0123456789";
+ char *c = name;
+
+ for (--len; len > 0; len--)
+ *c++ = file_name_char[twister_urand(sizeof(file_name_char) - 1)];
+ *c = '\0';
+}
+
+
+/*
+ * crash crashes the system by causing a panic:
+ * echo panic >/proc/breakme
+ */
+
+void crash(void)
+{
+ static char panic[] = "panic";
+ int fd = eopen("/proc/breakme", O_WRONLY);
+ ewrite(fd, panic, sizeof(panic));
+}