| /* 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[]) |
| { |
| char **whitelist = &argv[1]; |
| int whitelist_len = argc - 1; |
| |
| RUN_TEST_SUITE(utests, num_utests, whitelist, whitelist_len); |
| } |