]>
Commit | Line | Data |
---|---|---|
1 | #include <sys/fcntl.h> | |
2 | #include <unistd.h> | |
3 | #include <stdlib.h> | |
4 | #include <sys/resource.h> | |
5 | #include <CommonCrypto/CommonDigest.h> | |
6 | #include <stdbool.h> | |
7 | #include <stdio.h> | |
8 | #include <spawn.h> | |
9 | #include <string.h> | |
10 | #include <signal.h> | |
11 | #include <stdarg.h> | |
12 | #include <sys/errno.h> | |
13 | #include <libkern/OSAtomic.h> | |
14 | #include <zlib.h> | |
15 | #include <pthread.h> | |
16 | ||
17 | #include "hfs-tests.h" | |
18 | #include "test-utils.h" | |
19 | #include "disk-image.h" | |
20 | ||
21 | TEST(throttled_io) | |
22 | ||
23 | static disk_image_t *gDI; | |
24 | static char *gFile1, *gFile2, *gFile3; | |
25 | ||
26 | static pid_t gPID = 0; | |
27 | static void *gBuf; | |
28 | static const size_t gBuf_size = 64 * 1024 * 1024; | |
29 | ||
30 | static void start_background_io(void) | |
31 | { | |
32 | char *of; | |
33 | asprintf(&of, "of=%s", gFile1); | |
34 | ||
35 | assert_no_err(posix_spawn(&gPID, "/bin/dd", NULL, NULL, | |
36 | (char *[]){ "/bin/dd", "if=/dev/random", | |
37 | of, NULL }, | |
38 | NULL)); | |
39 | } | |
40 | ||
41 | static void end_background_io(void) | |
42 | { | |
43 | if ( gPID != 0 ) | |
44 | { | |
45 | kill(gPID, SIGKILL); | |
46 | int stat; | |
47 | wait(&stat); | |
48 | gPID = 0; | |
49 | } | |
50 | } | |
51 | ||
52 | static int run_test1(void) | |
53 | { | |
54 | ||
55 | // Kick off another process to ensure we get throttled | |
56 | start_background_io(); | |
57 | ||
58 | assert_no_err(setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_PROCESS, | |
59 | IOPOL_THROTTLE)); | |
60 | ||
61 | int fd, fd2; | |
62 | assert_with_errno((fd = open("/dev/random", O_RDONLY)) >= 0); | |
63 | assert_with_errno((fd2 = open(gFile2, | |
64 | O_RDWR | O_CREAT | O_TRUNC, 0666)) >= 0); | |
65 | ||
66 | assert_no_err(fcntl(fd2, F_SINGLE_WRITER, 1)); | |
67 | assert_no_err(fcntl(fd2, F_NOCACHE, 1)); | |
68 | ||
69 | gBuf = valloc(gBuf_size); | |
70 | CC_SHA1_CTX ctx; | |
71 | CC_SHA1_Init(&ctx); | |
72 | ||
73 | ssize_t res = check_io(read(fd, gBuf, gBuf_size), gBuf_size); | |
74 | ||
75 | CC_SHA1_Update(&ctx, gBuf, (CC_LONG)res); | |
76 | ||
77 | res = check_io(write(fd2, gBuf, res), res); | |
78 | ||
79 | bzero(gBuf, gBuf_size); | |
80 | ||
81 | CC_SHA1_CTX ctx2; | |
82 | CC_SHA1_Init(&ctx2); | |
83 | ||
84 | lseek(fd2, 0, SEEK_SET); | |
85 | ||
86 | res = check_io(read(fd2, gBuf, gBuf_size), gBuf_size); | |
87 | ||
88 | CC_SHA1_Update(&ctx2, gBuf, (CC_LONG)res); | |
89 | ||
90 | uint8_t digest1[CC_SHA1_DIGEST_LENGTH], digest2[CC_SHA1_DIGEST_LENGTH]; | |
91 | CC_SHA1_Final(digest1, &ctx); | |
92 | CC_SHA1_Final(digest2, &ctx2); | |
93 | ||
94 | assert(!memcmp(digest1, digest2, CC_SHA1_DIGEST_LENGTH)); | |
95 | ||
96 | assert_no_err (close(fd)); | |
97 | assert_no_err (close(fd2)); | |
98 | ||
99 | end_background_io(); | |
100 | ||
101 | return 0; | |
102 | } | |
103 | ||
104 | static volatile uint64_t written; | |
105 | static volatile bool done; | |
106 | ||
107 | static void test2_thread(void) | |
108 | { | |
109 | int fd = open(gFile3, O_RDONLY); | |
110 | assert(fd >= 0); | |
111 | ||
112 | void *b = gBuf + gBuf_size / 2; | |
113 | uLong seq = crc32(0, Z_NULL, 0); | |
114 | uint32_t offset = 0; | |
115 | ||
116 | do { | |
117 | ssize_t res; | |
118 | ||
119 | do { | |
120 | res = check_io(pread(fd, b, gBuf_size / 2, offset), -1); | |
121 | } while (res == 0 && !done); | |
122 | ||
123 | assert (res % 4 == 0); | |
124 | ||
125 | offset += res; | |
126 | ||
127 | for (uLong *p = b; res; ++p, res -= sizeof(uLong)) { | |
128 | seq = crc32(Z_NULL, (void *)&seq, 4); | |
129 | assert(*p == seq); | |
130 | } | |
131 | ||
132 | if (offset < written) | |
133 | continue; | |
134 | OSMemoryBarrier(); | |
135 | } while (!done); | |
136 | ||
137 | assert_no_err (close(fd)); | |
138 | ||
139 | } | |
140 | ||
141 | static int run_test2(void) | |
142 | { | |
143 | start_background_io(); | |
144 | ||
145 | int fd = open(gFile3, O_RDWR | O_CREAT | O_TRUNC, 0666); | |
146 | assert(fd >= 0); | |
147 | ||
148 | assert_no_err(fcntl(fd, F_SINGLE_WRITER, 1)); | |
149 | assert_no_err(fcntl(fd, F_NOCACHE, 1)); | |
150 | ||
151 | pthread_t thread; | |
152 | pthread_create(&thread, NULL, (void *(*)(void *))test2_thread, NULL); | |
153 | uLong seq = crc32(0, Z_NULL, 0); | |
154 | ||
155 | for (int i = 0; i < 4; ++i) { | |
156 | uLong *p = gBuf; | |
157 | for (unsigned i = 0; i < gBuf_size / 2 / sizeof(uLong); ++i) { | |
158 | seq = crc32(Z_NULL, (void *)&seq, 4); | |
159 | p[i] = seq; | |
160 | } | |
161 | ||
162 | ssize_t res = check_io(write(fd, gBuf, gBuf_size / 2), gBuf_size / 2); | |
163 | ||
164 | written += res; | |
165 | } | |
166 | ||
167 | OSMemoryBarrier(); | |
168 | ||
169 | done = true; | |
170 | ||
171 | pthread_join(thread, NULL); | |
172 | ||
173 | assert_no_err (close(fd)); | |
174 | ||
175 | end_background_io(); | |
176 | ||
177 | return 0; | |
178 | } | |
179 | ||
180 | static bool clean_up(void) | |
181 | { | |
182 | end_background_io(); | |
183 | ||
184 | unlink(gFile1); | |
185 | unlink(gFile2); | |
186 | unlink(gFile3); | |
187 | ||
188 | free(gFile1); | |
189 | free(gFile2); | |
190 | free(gFile3); | |
191 | ||
192 | return true; | |
193 | } | |
194 | ||
195 | int run_throttled_io(__unused test_ctx_t *ctx) | |
196 | { | |
197 | gDI = disk_image_get(); | |
198 | ||
199 | asprintf(&gFile1, "%s/throttled_io.1", gDI->mount_point); | |
200 | asprintf(&gFile2, "%s/throttled_io.2", gDI->mount_point); | |
201 | asprintf(&gFile3, "%s/throttled_io.3", gDI->mount_point); | |
202 | ||
203 | test_cleanup(^ bool { | |
204 | return clean_up(); | |
205 | }); | |
206 | ||
207 | int res = run_test1(); | |
208 | if (res) | |
209 | return res; | |
210 | ||
211 | res = run_test2(); | |
212 | if (res) | |
213 | return res; | |
214 | ||
215 | return res; | |
216 | } |