]> git.saurik.com Git - apple/hfs.git/blob - tests/cases/test-uncached-io.c
5193693d0e5e18ad9098ffbad10d3501368c207a
[apple/hfs.git] / tests / cases / test-uncached-io.c
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 }