]> git.saurik.com Git - apple/hfs.git/blame - tests/cases/test-throttled-io.c
hfs-556.100.11.tar.gz
[apple/hfs.git] / tests / cases / test-throttled-io.c
CommitLineData
de8ee011
A
1#include <TargetConditionals.h>
2
3#if !TARGET_OS_BRIDGE
4
558d2836
A
5#include <sys/fcntl.h>
6#include <unistd.h>
7#include <stdlib.h>
8#include <sys/resource.h>
9#include <CommonCrypto/CommonDigest.h>
10#include <stdbool.h>
11#include <stdio.h>
12#include <spawn.h>
13#include <string.h>
14#include <signal.h>
15#include <stdarg.h>
16#include <sys/errno.h>
17#include <libkern/OSAtomic.h>
18#include <zlib.h>
19#include <pthread.h>
de8ee011 20#include <sys/mount.h>
558d2836
A
21
22#include "hfs-tests.h"
23#include "test-utils.h"
24#include "disk-image.h"
25
de8ee011
A
26#define TEST_PATH "/tmp/throttled-io.sparseimage"
27#define MOUNT_POINT "/tmp/throttled_io"
28
558d2836
A
29TEST(throttled_io)
30
7adaf79d
A
31static disk_image_t *gDI;
32static char *gFile1, *gFile2, *gFile3;
558d2836 33
7adaf79d
A
34static void *gBuf;
35static const size_t gBuf_size = 64 * 1024 * 1024;
558d2836 36
de8ee011
A
37static pid_t bg_io_pid = 0;
38static size_t BG_IOSIZE = (4U << 10); // BG-IO buffer size 4KiB
39static off_t BG_MAX_FILESIZE = (1ULL << 30); // Max BG-IO File-size 1GiB
40
41//
42// A worker function called from the background-io child process. First it
43// attempts to open file at path `gFile1` ( a new file is created if one does
44// does not exist). If the file is opened successfully, the file is written
45// continiously, wrapping around when write offset is greater or equal to
46// `BG_MAX_FILESIZE`.
47//
48errno_t background_io_worker(void)
558d2836 49{
de8ee011
A
50 int fd;
51 off_t offset;
52 char *buffer;
53
54 //
55 // Open the file at path `gFile1`, create a new file if one does not
56 // exists.
57 //
58 fd = open(gFile1, O_RDWR|O_TRUNC);
59 if (fd == -1 && errno == ENOENT) {
60
61 fd = creat(gFile1, 0666);
62 if (fd == -1) {
63 fprintf(stderr, "Failed to create file: %s\n",
64 gFile1);
65 return errno;
66 }
67 close(fd);
68 fd = open(gFile1, O_RDWR);
69 }
70
71 //
72 // Return errno if we could not open the file.
73 //
74 if (fd == -1) {
75 fprintf(stderr, "Failed to open file: %s\n", gFile1);
76 return errno;
77 }
78
79 //
80 // Allocate the write buffer on-stack such that we don't have to free
81 // it explicitly.
82 //
83 buffer = alloca(BG_IOSIZE);
84 if (!buffer)
85 return ENOMEM;
86 (void)memset(buffer, -1, BG_IOSIZE);
87
88 offset = 0;
89 while (true) {
90 ssize_t written;
91
92 written = pwrite(fd, buffer, BG_IOSIZE, offset);
93 if (written == -1) {
94 return errno;
95 }
96
97 offset += written;
98 if (offset >= BG_MAX_FILESIZE) {
99 offset = 0;
100 }
101
102 //
103 // Voluntarily relinquish cpu to allow the throttled process to
104 // schedule after every 128 MiB of write, else the test can
105 // take very long time and timeout. Sleep half a second after
106 // we have written 128 MiB.
107 //
108 if (!(offset % (off_t)(BG_IOSIZE * 1024 * 32))) {
109 usleep(500000);
110 }
111
112 //
113 // Just in case the test times-out for some reason and parent
114 // terminates without killing this background-io process, let
115 // us poll this child process's parent. If the parent process
116 // has died then to ensure cleanup we return from this child
117 // process.
118 //
119 if (getppid() == 1) {
120 return ETIMEDOUT;
121 }
122 }
123
124 //
125 // Should not come here.
126 //
127 return 0;
7adaf79d
A
128}
129
de8ee011
A
130//
131// Start a continious background-io process, if successful the pid of the
132// background-io is cached in `bg_io_pid'.
133//
134static void start_background_io_process(void)
7adaf79d 135{
de8ee011
A
136 pid_t child_pid;
137
138 child_pid = fork();
139 switch(child_pid) {
140
141 case -1:
142 assert_fail("Failed to spawn background-io "
143 "process, error %d, %s.\n", errno,
144 strerror(errno));
145 case 0: {
146 int child_ret;
147
148 child_ret = background_io_worker();
149 _exit(child_ret);
150 }
151 default:
152 bg_io_pid = child_pid;
7adaf79d
A
153 }
154}
155
de8ee011
A
156//
157// Kill the background-io process if it was started. The background-io process
158// should perform IO continuously and should not exit normally. If the
159// background-io exited normally, the error is reported.
160//
161static void kill_background_io_process(void)
7adaf79d 162{
de8ee011
A
163 int child_status;
164 pid_t child_pid;
7adaf79d 165
de8ee011
A
166 if (!bg_io_pid)
167 return;
558d2836 168
de8ee011
A
169 kill(bg_io_pid, SIGKILL);
170 do {
171 child_pid = waitpid(bg_io_pid, &child_status, WUNTRACED);
172 } while (child_pid == -1 && errno == EINTR);
173
174 if (child_pid == -1 && errno != ECHILD) {
175 assert_fail("Failed to wait for child pid: %ld, error %d, "
176 "%s.\n", (long)bg_io_pid, errno,
177 strerror(errno));
178 }
179
180 if (WIFEXITED(child_status)) {
181 int error;
182
183 error = WEXITSTATUS(child_status);
184 if (error) {
185 assert_fail("background-io exited with error %d, "
186 "%s.\n", error, strerror(error));
187 }
188 }
189
190 bg_io_pid = 0;
191}
558d2836 192
de8ee011
A
193static int run_test1(void)
194{
558d2836 195 int fd, fd2;
de8ee011
A
196 int orig_io_policy;
197
198 //
199 // Kick off another process to ensure we get throttled.
200 //
201 start_background_io_process();
202
203 //
204 // Cache the set IO policy of this process.
205 //
206 orig_io_policy = getiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_PROCESS);
207 assert_with_errno(orig_io_policy != -1);
208
209 //
210 // Set new IO policy for this process.
211 //
212 assert_no_err(setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_PROCESS,
213 IOPOL_THROTTLE));
214
558d2836 215 assert_with_errno((fd = open("/dev/random", O_RDONLY)) >= 0);
de8ee011
A
216 assert_with_errno((fd2 = open(gFile2, O_RDWR | O_CREAT | O_TRUNC,
217 0666)) >= 0);
558d2836
A
218
219 assert_no_err(fcntl(fd2, F_SINGLE_WRITER, 1));
220 assert_no_err(fcntl(fd2, F_NOCACHE, 1));
221
7adaf79d 222 gBuf = valloc(gBuf_size);
558d2836
A
223 CC_SHA1_CTX ctx;
224 CC_SHA1_Init(&ctx);
225
7adaf79d 226 ssize_t res = check_io(read(fd, gBuf, gBuf_size), gBuf_size);
558d2836 227
7adaf79d 228 CC_SHA1_Update(&ctx, gBuf, (CC_LONG)res);
558d2836 229
7adaf79d 230 res = check_io(write(fd2, gBuf, res), res);
558d2836 231
7adaf79d 232 bzero(gBuf, gBuf_size);
558d2836
A
233
234 CC_SHA1_CTX ctx2;
235 CC_SHA1_Init(&ctx2);
236
237 lseek(fd2, 0, SEEK_SET);
238
7adaf79d 239 res = check_io(read(fd2, gBuf, gBuf_size), gBuf_size);
558d2836 240
7adaf79d 241 CC_SHA1_Update(&ctx2, gBuf, (CC_LONG)res);
558d2836
A
242
243 uint8_t digest1[CC_SHA1_DIGEST_LENGTH], digest2[CC_SHA1_DIGEST_LENGTH];
244 CC_SHA1_Final(digest1, &ctx);
245 CC_SHA1_Final(digest2, &ctx2);
246
247 assert(!memcmp(digest1, digest2, CC_SHA1_DIGEST_LENGTH));
248
7adaf79d
A
249 assert_no_err (close(fd));
250 assert_no_err (close(fd2));
7adaf79d 251
de8ee011
A
252 //
253 // Kill the background IO process.
254 //
255 kill_background_io_process();
256
257 //
258 // Restore the orig. IO policy.
259 //
260 assert_no_err(setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_PROCESS,
261 orig_io_policy));
558d2836
A
262 return 0;
263}
264
265static volatile uint64_t written;
266static volatile bool done;
267
268static void test2_thread(void)
269{
7adaf79d 270 int fd = open(gFile3, O_RDONLY);
558d2836
A
271 assert(fd >= 0);
272
7adaf79d 273 void *b = gBuf + gBuf_size / 2;
558d2836
A
274 uLong seq = crc32(0, Z_NULL, 0);
275 uint32_t offset = 0;
276
277 do {
278 ssize_t res;
279
280 do {
7adaf79d 281 res = check_io(pread(fd, b, gBuf_size / 2, offset), -1);
558d2836
A
282 } while (res == 0 && !done);
283
284 assert (res % 4 == 0);
285
286 offset += res;
287
288 for (uLong *p = b; res; ++p, res -= sizeof(uLong)) {
289 seq = crc32(Z_NULL, (void *)&seq, 4);
290 assert(*p == seq);
291 }
292
293 if (offset < written)
294 continue;
295 OSMemoryBarrier();
296 } while (!done);
7adaf79d
A
297
298 assert_no_err (close(fd));
558d2836
A
299}
300
301static int run_test2(void)
302{
de8ee011
A
303 int fd;
304 int orig_io_policy;
305
306 //
307 // Kick off another process to ensure we get throttled.
308 //
309 start_background_io_process();
310
311 //
312 // Cache the set IO policy of this process.
313 //
314 orig_io_policy = getiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_PROCESS);
315 assert_with_errno(orig_io_policy != -1);
316
317 //
318 // Set new IO policy for this process.
319 //
320 assert_no_err(setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_PROCESS,
321 IOPOL_THROTTLE));
322
323 fd = open(gFile3, O_RDWR | O_CREAT | O_TRUNC, 0666);
558d2836
A
324 assert(fd >= 0);
325
326 assert_no_err(fcntl(fd, F_SINGLE_WRITER, 1));
327 assert_no_err(fcntl(fd, F_NOCACHE, 1));
328
329 pthread_t thread;
330 pthread_create(&thread, NULL, (void *(*)(void *))test2_thread, NULL);
331 uLong seq = crc32(0, Z_NULL, 0);
332
333 for (int i = 0; i < 4; ++i) {
7adaf79d
A
334 uLong *p = gBuf;
335 for (unsigned i = 0; i < gBuf_size / 2 / sizeof(uLong); ++i) {
558d2836
A
336 seq = crc32(Z_NULL, (void *)&seq, 4);
337 p[i] = seq;
338 }
339
7adaf79d 340 ssize_t res = check_io(write(fd, gBuf, gBuf_size / 2), gBuf_size / 2);
558d2836
A
341
342 written += res;
343 }
344
345 OSMemoryBarrier();
346
347 done = true;
348
349 pthread_join(thread, NULL);
350
7adaf79d 351 assert_no_err (close(fd));
de8ee011
A
352
353 //
354 // Kill the background IO process.
355 //
356 kill_background_io_process();
357
358 //
359 // Restore the orig. IO policy.
360 //
361 assert_no_err(setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_PROCESS,
362 orig_io_policy));
7adaf79d 363
558d2836
A
364 return 0;
365}
366
367static bool clean_up(void)
368{
de8ee011 369 kill_background_io_process();
558d2836 370
7adaf79d
A
371 unlink(gFile1);
372 unlink(gFile2);
373 unlink(gFile3);
374
375 free(gFile1);
376 free(gFile2);
377 free(gFile3);
558d2836
A
378
379 return true;
380}
381
382int run_throttled_io(__unused test_ctx_t *ctx)
383{
de8ee011
A
384
385 gDI = disk_image_create(TEST_PATH, &(disk_image_opts_t){
386 .size = 8 GB,
387 .mount_point = MOUNT_POINT
388 });
7adaf79d
A
389
390 asprintf(&gFile1, "%s/throttled_io.1", gDI->mount_point);
391 asprintf(&gFile2, "%s/throttled_io.2", gDI->mount_point);
392 asprintf(&gFile3, "%s/throttled_io.3", gDI->mount_point);
393
558d2836
A
394 test_cleanup(^ bool {
395 return clean_up();
396 });
ec99dd30 397
558d2836
A
398 int res = run_test1();
399 if (res)
7adaf79d 400 return res;
ec99dd30 401
558d2836
A
402 res = run_test2();
403 if (res)
7adaf79d 404 return res;
558d2836
A
405
406 return res;
407}
de8ee011
A
408
409#endif // !TARGET_OS_BRIDGE