|  | /* 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); | 
|  | } |