]> git.saurik.com Git - apple/hfs.git/blob - tests/cases/test-throttled-io.c
803c0b721d074ec0ea5dd3d96999b12bb5f05077
[apple/hfs.git] / tests / cases / test-throttled-io.c
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 *di;
24 static char *file1, *file2, *file3;
25
26 static pid_t pid;
27 static void *buf;
28 static const size_t buf_size = 64 * 1024 * 1024;
29
30 static int run_test1(void)
31 {
32 char *of;
33 asprintf(&of, "of=%s", file1);
34
35 // Kick off another process to ensure we get throttled
36 assert_no_err(posix_spawn(&pid, "/bin/dd", NULL, NULL,
37 (char *[]){ "/bin/dd", "if=/dev/random",
38 of, NULL },
39 NULL));
40
41 assert_no_err(setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_PROCESS,
42 IOPOL_THROTTLE));
43
44 int fd, fd2;
45 assert_with_errno((fd = open("/dev/random", O_RDONLY)) >= 0);
46 assert_with_errno((fd2 = open(file2,
47 O_RDWR | O_CREAT | O_TRUNC, 0666)) >= 0);
48
49 assert_no_err(fcntl(fd2, F_SINGLE_WRITER, 1));
50 assert_no_err(fcntl(fd2, F_NOCACHE, 1));
51
52 buf = valloc(buf_size);
53 CC_SHA1_CTX ctx;
54 CC_SHA1_Init(&ctx);
55
56 ssize_t res = check_io(read(fd, buf, buf_size), buf_size);
57
58 CC_SHA1_Update(&ctx, buf, (CC_LONG)res);
59
60 res = check_io(write(fd2, buf, res), res);
61
62 bzero(buf, buf_size);
63
64 CC_SHA1_CTX ctx2;
65 CC_SHA1_Init(&ctx2);
66
67 lseek(fd2, 0, SEEK_SET);
68
69 res = check_io(read(fd2, buf, buf_size), buf_size);
70
71 CC_SHA1_Update(&ctx2, buf, (CC_LONG)res);
72
73 uint8_t digest1[CC_SHA1_DIGEST_LENGTH], digest2[CC_SHA1_DIGEST_LENGTH];
74 CC_SHA1_Final(digest1, &ctx);
75 CC_SHA1_Final(digest2, &ctx2);
76
77 assert(!memcmp(digest1, digest2, CC_SHA1_DIGEST_LENGTH));
78
79 return 0;
80 }
81
82 static volatile uint64_t written;
83 static volatile bool done;
84
85 static void test2_thread(void)
86 {
87 int fd = open(file3, O_RDONLY);
88 assert(fd >= 0);
89
90 void *b = buf + buf_size / 2;
91 uLong seq = crc32(0, Z_NULL, 0);
92 uint32_t offset = 0;
93
94 do {
95 ssize_t res;
96
97 do {
98 res = check_io(pread(fd, b, buf_size / 2, offset), -1);
99 } while (res == 0 && !done);
100
101 assert (res % 4 == 0);
102
103 offset += res;
104
105 for (uLong *p = b; res; ++p, res -= sizeof(uLong)) {
106 seq = crc32(Z_NULL, (void *)&seq, 4);
107 assert(*p == seq);
108 }
109
110 if (offset < written)
111 continue;
112 OSMemoryBarrier();
113 } while (!done);
114 }
115
116 static int run_test2(void)
117 {
118 int fd = open(file3, O_RDWR | O_CREAT | O_TRUNC, 0666);
119 assert(fd >= 0);
120
121 assert_no_err(fcntl(fd, F_SINGLE_WRITER, 1));
122 assert_no_err(fcntl(fd, F_NOCACHE, 1));
123
124 pthread_t thread;
125 pthread_create(&thread, NULL, (void *(*)(void *))test2_thread, NULL);
126 uLong seq = crc32(0, Z_NULL, 0);
127
128 for (int i = 0; i < 4; ++i) {
129 uLong *p = buf;
130 for (unsigned i = 0; i < buf_size / 2 / sizeof(uLong); ++i) {
131 seq = crc32(Z_NULL, (void *)&seq, 4);
132 p[i] = seq;
133 }
134
135 ssize_t res = check_io(write(fd, buf, buf_size / 2), buf_size / 2);
136
137 written += res;
138 }
139
140 OSMemoryBarrier();
141
142 done = true;
143
144 pthread_join(thread, NULL);
145
146 return 0;
147 }
148
149 static bool clean_up(void)
150 {
151 kill(pid, SIGKILL);
152 int stat;
153 wait(&stat);
154
155 unlink(file1);
156 unlink(file2);
157 unlink(file3);
158
159 return true;
160 }
161
162 int run_throttled_io(__unused test_ctx_t *ctx)
163 {
164 test_cleanup(^ bool {
165 return clean_up();
166 });
167
168 di = disk_image_get();
169 asprintf(&file1, "%s/throttled_io.1", di->mount_point);
170 asprintf(&file2, "%s/throttled_io.2", di->mount_point);
171 asprintf(&file3, "%s/throttled_io.3", di->mount_point);
172
173 int res = run_test1();
174 if (res)
175 goto out;
176
177 res = run_test2();
178 if (res)
179 goto out;
180
181 out:
182 free(file1);
183 free(file2);
184 free(file3);
185
186 return res;
187 }