]>
Commit | Line | Data |
---|---|---|
558d2836 A |
1 | #include <string.h> |
2 | #include <unistd.h> | |
3 | #include <fcntl.h> | |
4 | #include <pthread.h> | |
5 | #include <assert.h> | |
6 | #include <stdlib.h> | |
7 | #include <errno.h> | |
8 | #include <stdio.h> | |
9 | #include <sys/time.h> | |
10 | #include <sys/uio.h> | |
11 | ||
12 | #include "hfs-tests.h" | |
13 | #include "test-utils.h" | |
14 | #include "disk-image.h" | |
15 | ||
16 | TEST(uncached_io) | |
17 | ||
18 | static disk_image_t *di; | |
19 | ||
20 | pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; | |
21 | pthread_cond_t cond = PTHREAD_COND_INITIALIZER; | |
22 | ||
23 | static volatile int state; | |
24 | static volatile bool run_thread; | |
25 | ||
26 | static char *file1, *file2; | |
27 | ||
28 | void *read_thread(__unused void *arg) | |
29 | { | |
30 | int fd = open(file1, O_RDONLY); | |
31 | ||
32 | int x; | |
33 | ||
34 | while (run_thread) { | |
35 | check_io(pread(fd, &x, 4, 0), 4); | |
36 | ||
37 | if (x == state) | |
38 | continue; | |
39 | ||
40 | pthread_mutex_lock(&mutex); | |
41 | state = ~state; | |
42 | pthread_mutex_unlock(&mutex); | |
43 | ||
44 | pthread_cond_broadcast(&cond); | |
45 | } | |
46 | ||
47 | close(fd); | |
48 | ||
49 | return NULL; | |
50 | } | |
51 | ||
52 | int run_uncached_io(__unused test_ctx_t *ctx) | |
53 | { | |
54 | di = disk_image_get(); | |
55 | asprintf(&file1, "%s/test.data", di->mount_point); | |
56 | asprintf(&file2, "%s/test2.data", di->mount_point); | |
57 | ||
58 | unlink(file1); | |
59 | unlink(file2); | |
60 | ||
61 | int fd; | |
62 | assert_with_errno((fd = open(file1, | |
63 | O_RDWR | O_CREAT | O_TRUNC, 0666)) >= 0); | |
64 | ||
65 | assert_no_err(fcntl(fd, F_NOCACHE, 1)); | |
66 | ||
67 | assert_no_err(ftruncate(fd, 4096)); | |
68 | ||
69 | int fd2; | |
70 | assert_with_errno((fd2 = open(file2, | |
71 | O_RDWR | O_CREAT | O_TRUNC, 0666)) >= 0); | |
72 | ||
73 | // Force the test file to be 1 block, and then 4 blocks | |
74 | assert_no_err(ftruncate(fd2, 4096)); | |
75 | assert_no_err(ftruncate(fd, 4096 + 16384)); | |
76 | assert_no_err(ftruncate(fd2, 8192)); | |
77 | ||
78 | char *buffer = malloc(65536); | |
79 | ||
80 | // Set buffer to be 12288 bytes off 16 KB alignment | |
81 | buffer = (char *)(((uintptr_t)(buffer + 4096) & ~16383) + 12288); | |
82 | ||
83 | check_io(pwrite(fd, buffer, 32768, 0), 32768); | |
84 | ||
85 | // Now do the (slightly simpler case: a single transaction) | |
86 | assert_no_err(ftruncate(fd, 0)); | |
87 | check_io(pwrite(fd, buffer, 32768, 0), 32768); | |
88 | ||
89 | // And one more time with a single page | |
90 | assert_no_err(ftruncate(fd, 0)); | |
91 | check_io(pwrite(fd, buffer, 16384, 0), 16384); | |
92 | ||
93 | // Now just two transactions | |
94 | assert_no_err(ftruncate(fd, 4096)); | |
95 | check_io(pwrite(fd, buffer, 32768, 0), 32768); | |
96 | ||
97 | // And another variant of two transactions | |
98 | assert_no_err(ftruncate(fd, 0)); | |
99 | assert_no_err(ftruncate(fd, 4096 + 16384)); | |
100 | assert_no_err(ftruncate(fd2, 0)); | |
101 | assert_no_err(ftruncate(fd2, 4096)); | |
102 | check_io(pwrite(fd, buffer, 32768, 0), 32768); | |
103 | ||
104 | assert_no_err(close(fd)); | |
105 | assert_no_err(unlink(file1)); | |
106 | assert_no_err(unlink(file2)); | |
107 | ||
108 | assert_with_errno((fd = open(file1, | |
109 | O_RDWR | O_CREAT | O_TRUNC, 0666)) >= 0); | |
110 | ||
111 | // The cluster currently only does uncached I/O if 16 KB or higher | |
112 | int size = 16384; | |
113 | ||
114 | assert_no_err(ftruncate(fd, size)); | |
115 | ||
116 | assert_no_err(fcntl(fd, F_NOCACHE, 1)); | |
117 | ||
118 | char *bufs[2]; | |
119 | ||
120 | bufs[0] = (char *)(((uintptr_t)valloc(size + 65536) + 16383) & ~16383) + 4096; | |
121 | bufs[1] = valloc(size); | |
122 | ||
123 | for (int pass = 0; pass < 2; ++pass) { | |
124 | state = 0; | |
125 | ||
126 | bzero(bufs[0], size); | |
127 | memset(bufs[1], 0xff, size); | |
128 | ||
129 | run_thread = true; | |
130 | ||
131 | pthread_t thread; | |
132 | pthread_create(&thread, NULL, read_thread, NULL); | |
133 | ||
134 | struct timeval start; | |
135 | gettimeofday(&start, NULL); | |
136 | ||
137 | for (;;) { | |
138 | int cur_state = state; | |
139 | ||
140 | switch (pass) { | |
141 | case 0: | |
142 | check_io(pwrite(fd, bufs[cur_state ? 0 : 1], size, 0), size); | |
143 | break; | |
144 | ||
145 | case 1:; | |
146 | struct iovec iovs[] = { | |
147 | { bufs[cur_state ? 0 : 1], size / 2 }, | |
148 | { bufs[cur_state ? 0 : 1] + size / 2, size / 2 } | |
149 | }; | |
150 | ||
151 | assert_no_err(lseek(fd, 0, SEEK_SET)); | |
152 | check_io(writev(fd, iovs, 2), size); | |
153 | break; | |
154 | } | |
155 | ||
156 | pthread_mutex_lock(&mutex); | |
157 | while (state == cur_state) { | |
158 | struct timeval tv; | |
159 | ||
160 | gettimeofday(&tv, NULL); | |
161 | ||
162 | struct timespec ts; | |
163 | TIMEVAL_TO_TIMESPEC(&tv, &ts); | |
164 | ||
165 | ++ts.tv_sec; | |
166 | ||
167 | int res = pthread_cond_timedwait(&cond, &mutex, &ts); | |
168 | ||
169 | if (res) { | |
170 | return -1; | |
171 | } | |
172 | } | |
173 | ||
174 | pthread_mutex_unlock(&mutex); | |
175 | ||
176 | // If 10 seconds are up, we're done | |
177 | struct timeval now, elapsed; | |
178 | gettimeofday(&now, NULL); | |
179 | timersub(&now, &start, &elapsed); | |
180 | if (elapsed.tv_sec >= 10) | |
181 | break; | |
182 | } // for (;;) | |
183 | ||
184 | run_thread = false; | |
185 | pthread_join(thread, NULL); | |
186 | } // for (pass...) | |
187 | ||
188 | assert_no_err(unlink(file1)); | |
189 | ||
190 | free(file1); | |
191 | free(file2); | |
192 | ||
193 | return 0; | |
194 | } |