]>
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 *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 | } |