Add a test for partial writes
authorBarret Rhoden <brho@cs.berkeley.edu>
Fri, 26 May 2017 18:26:58 +0000 (14:26 -0400)
committerBarret Rhoden <brho@cs.berkeley.edu>
Fri, 26 May 2017 18:26:58 +0000 (14:26 -0400)
This will catch the qio and Inferno rwrite() bugs.

Signed-off-by: Barret Rhoden <brho@cs.berkeley.edu>
user/utest/qio.c [new file with mode: 0644]

diff --git a/user/utest/qio.c b/user/utest/qio.c
new file mode 100644 (file)
index 0000000..c16371f
--- /dev/null
@@ -0,0 +1,87 @@
+/* Copyright (c) 2017 Google, Inc.
+ * Barret Rhoden <brho@cs.berkeley.edu>
+ * See LICENSE for details. */
+
+#define _GNU_SOURCE
+#include <utest/utest.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+
+
+TEST_SUITE("QIO");
+
+/* <--- Begin definition of test cases ---> */
+
+static bool fd_is_writable(int fd)
+{
+       struct stat stat_buf;
+       int ret;
+
+       ret = fstat(fd, &stat_buf);
+       UT_ASSERT_FMT("fstat failed", !ret);
+       return S_WRITABLE(stat_buf.st_mode);
+}
+
+/* This is a little fragile, in that it relies on the kernel's value of
+ * Maxatomic in qio.c. */
+bool test_partial_write_to_full_queue(void)
+{
+       int pipefd[2];
+       ssize_t ret;
+       char *buf;
+       size_t buf_sz = 1ULL << 20;
+
+       buf = malloc(buf_sz);
+       UT_ASSERT_FMT("buf alloc failed", buf);
+       ret = pipe2(pipefd, O_NONBLOCK);
+       UT_ASSERT_FMT("pipe2 failed", ret == 0);
+
+       /* Fill the pipe.  The limit is usually O(10K) */
+       do {
+               ret = write(pipefd[1], buf, 1024);
+       } while (ret > 0);
+       UT_ASSERT_FMT("Didn't get EAGAIN, got %d", errno == EAGAIN, errno);
+
+       /* The way qio works is we accept the last block, and won't accept future
+        * blocks until we drop below the limit.  Let's drain until we get there.
+        * Should be only one or two. */
+       while (!fd_is_writable(pipefd[1])) {
+               ret = read(pipefd[0], buf, 1024);
+               UT_ASSERT_FMT("Failed to read from pipe with data", ret > 0);
+       }
+
+       /* Now we have a little room.  If we send in a very large write, greater
+        * than Maxatomic, we should get a partial write.  If we get -1 back, then
+        * the kernel said it was writable, but we couldn't write.  This happened
+        * once when the kernel would write some, then get EAGAIN and throw -
+        * ignoring the successful initial write. */
+       ret = write(pipefd[1], buf, buf_sz);
+       UT_ASSERT_FMT("write error %d, errno %d", ret > 0, ret, errno);
+       UT_ASSERT_FMT("wrote %d >= %d buf_sz", ret < buf_sz, ret, buf_sz);
+
+       free(buf);
+       close(pipefd[0]);
+       close(pipefd[1]);
+       return TRUE;
+}
+
+
+/* <--- End definition of test cases ---> */
+
+struct utest utests[] = {
+       UTEST_REG(partial_write_to_full_queue),
+};
+int num_utests = sizeof(utests) / sizeof(struct utest);
+
+int main(int argc, char *argv[])
+{
+       // Run test suite passing it all the args as whitelist of what tests to run.
+       char **whitelist = &argv[1];
+       int whitelist_len = argc - 1;
+
+       RUN_TEST_SUITE(utests, num_utests, whitelist, whitelist_len);
+}