]>
Commit | Line | Data |
---|---|---|
5ba3f43e A |
1 | #ifdef T_NAMESPACE |
2 | #undef T_NAMESPACE | |
3 | #endif | |
4 | #include <darwintest.h> | |
5 | #include <darwintest_utils.h> | |
6 | ||
7 | #include <stdlib.h> | |
8 | #include <unistd.h> | |
9 | #include <fcntl.h> | |
10 | #include <System/sys/fsctl.h> | |
11 | #include <paths.h> | |
12 | ||
13 | static char *mktempdir(void); | |
14 | static char *mktempmount(void); | |
15 | ||
16 | #ifndef TEST_UNENTITLED | |
17 | static int system_legal(const char *command); | |
18 | static char *mkramdisk(void); | |
19 | static uint64_t time_for_read(int fd, const char *expected); | |
20 | static void perf_setup(char **path, int *fd); | |
21 | ||
22 | #define READSIZE 1024L | |
23 | #endif /* !TEST_UNENTITLED */ | |
24 | ||
25 | T_GLOBAL_META( | |
26 | T_META_NAMESPACE("xnu.vfs.dmc"), | |
cb323159 A |
27 | T_META_ASROOT(true), |
28 | T_META_RUN_CONCURRENTLY(true) | |
0a7de745 | 29 | ); |
5ba3f43e A |
30 | |
31 | #pragma mark Entitled Tests | |
32 | ||
33 | #ifndef TEST_UNENTITLED | |
34 | T_DECL(fsctl_get_uninitialized, | |
0a7de745 A |
35 | "Initial fsctl.get should return zeros", |
36 | T_META_ASROOT(false)) | |
5ba3f43e A |
37 | { |
38 | int err; | |
39 | char *mount_path; | |
40 | disk_conditioner_info info = {0}; | |
41 | disk_conditioner_info expected_info = {0}; | |
42 | ||
43 | T_SETUPBEGIN; | |
44 | mount_path = mktempmount(); | |
45 | T_SETUPEND; | |
46 | ||
47 | info.enabled = true; | |
48 | info.is_ssd = true; | |
49 | err = fsctl(mount_path, DISK_CONDITIONER_IOC_GET, &info, 0); | |
50 | T_WITH_ERRNO; | |
51 | T_ASSERT_EQ_INT(0, err, "fsctl(DISK_CONDITIONER_IOC_GET)"); | |
52 | ||
53 | err = memcmp(&info, &expected_info, sizeof(info)); | |
54 | T_ASSERT_EQ_INT(0, err, "initial DMC info is zeroed"); | |
55 | } | |
56 | ||
57 | T_DECL(fsctl_set, | |
0a7de745 | 58 | "fsctl.set should succeed and fsctl.get should verify") |
5ba3f43e A |
59 | { |
60 | int err; | |
61 | char *mount_path; | |
62 | disk_conditioner_info info = {0}; | |
63 | disk_conditioner_info expected_info = {0}; | |
64 | ||
65 | T_SETUPBEGIN; | |
66 | mount_path = mktempmount(); | |
67 | T_SETUPEND; | |
68 | ||
69 | info.enabled = 1; | |
70 | info.access_time_usec = 10; | |
71 | info.read_throughput_mbps = 40; | |
72 | info.write_throughput_mbps = 40; | |
73 | info.is_ssd = 0; | |
d9a64523 A |
74 | info.ioqueue_depth = 8; |
75 | info.maxreadcnt = 8; | |
76 | info.maxwritecnt = 8; | |
77 | info.segreadcnt = 8; | |
78 | info.segwritecnt = 8; | |
5ba3f43e A |
79 | expected_info = info; |
80 | ||
81 | err = fsctl(mount_path, DISK_CONDITIONER_IOC_SET, &info, 0); | |
82 | T_WITH_ERRNO; | |
83 | T_ASSERT_EQ_INT(0, err, "fsctl(DISK_CONDITIONER_IOC_SET)"); | |
84 | ||
85 | err = fsctl(mount_path, DISK_CONDITIONER_IOC_GET, &info, 0); | |
86 | T_WITH_ERRNO; | |
87 | T_ASSERT_EQ_INT(0, err, "fsctl(DISK_CONDITIONER_IOC_GET) after SET"); | |
88 | ||
89 | err = memcmp(&info, &expected_info, sizeof(info)); | |
90 | T_ASSERT_EQ_INT(0, err, "fsctl.get is the info configured by fsctl.set"); | |
91 | } | |
92 | ||
d9a64523 A |
93 | static void |
94 | verify_mount_fallback_values(const char *mount_path, disk_conditioner_info *info) | |
95 | { | |
96 | int err; | |
97 | disk_conditioner_info newinfo = {0}; | |
98 | ||
99 | err = fsctl(mount_path, DISK_CONDITIONER_IOC_SET, info, 0); | |
100 | T_WITH_ERRNO; | |
101 | T_ASSERT_EQ_INT(0, err, "fsctl(DISK_CONDITIONER_IOC_SET)"); | |
102 | ||
103 | err = fsctl(mount_path, DISK_CONDITIONER_IOC_GET, &newinfo, 0); | |
104 | T_WITH_ERRNO; | |
105 | T_ASSERT_EQ_INT(0, err, "fsctl(DISK_CONDITIONER_IOC_GET) after SET"); | |
106 | ||
107 | // without querying the drive for the expected values, the best we can do is | |
108 | // assert that they are not zero (impossible) or less than UINT32_MAX (unlikely) | |
109 | T_ASSERT_GT(newinfo.ioqueue_depth, 0u, "ioqueue_depth is the value from the mount"); | |
110 | T_ASSERT_GT(newinfo.maxreadcnt, 0u, "maxreadcnt is value from the mount"); | |
111 | T_ASSERT_GT(newinfo.maxwritecnt, 0u, "maxwritecnt is value from the mount"); | |
112 | T_ASSERT_GT(newinfo.segreadcnt, 0u, "segreadcnt is value from the mount"); | |
113 | T_ASSERT_GT(newinfo.segwritecnt, 0u, "segwritecnt is value from the mount"); | |
114 | T_ASSERT_LT(newinfo.ioqueue_depth, UINT32_MAX, "ioqueue_depth is the value from the mount"); | |
115 | T_ASSERT_LT(newinfo.maxreadcnt, UINT32_MAX, "maxreadcnt is value from the mount"); | |
116 | T_ASSERT_LT(newinfo.maxwritecnt, UINT32_MAX, "maxwritecnt is value from the mount"); | |
117 | T_ASSERT_LT(newinfo.segreadcnt, UINT32_MAX, "segreadcnt is value from the mount"); | |
118 | T_ASSERT_LT(newinfo.segwritecnt, UINT32_MAX, "segwritecnt is value from the mount"); | |
119 | } | |
120 | ||
121 | T_DECL(fsctl_set_zero, | |
0a7de745 | 122 | "fsctl.set zero values should fall back to original mount settings") |
d9a64523 A |
123 | { |
124 | char *mount_path; | |
125 | disk_conditioner_info info = {0}; | |
126 | ||
127 | T_SETUPBEGIN; | |
128 | mount_path = mktempmount(); | |
129 | ||
130 | info.enabled = 1; | |
131 | /* everything else is 0 */ | |
132 | ||
133 | T_SETUPEND; | |
134 | ||
135 | verify_mount_fallback_values(mount_path, &info); | |
136 | } | |
137 | ||
138 | T_DECL(fsctl_set_out_of_bounds, | |
0a7de745 | 139 | "fsctl.set out-of-bounds values should fall back to original mount settings") |
d9a64523 A |
140 | { |
141 | char *mount_path; | |
142 | disk_conditioner_info info; | |
143 | ||
144 | T_SETUPBEGIN; | |
145 | mount_path = mktempmount(); | |
146 | ||
147 | memset(&info, UINT32_MAX, sizeof(info)); | |
148 | info.enabled = 1; | |
149 | info.access_time_usec = 0; | |
150 | info.read_throughput_mbps = 0; | |
151 | info.write_throughput_mbps = 0; | |
152 | /* everything else is UINT32_MAX */ | |
153 | ||
154 | T_SETUPEND; | |
155 | ||
156 | verify_mount_fallback_values(mount_path, &info); | |
157 | } | |
158 | ||
159 | T_DECL(fsctl_restore_mount_fields, | |
0a7de745 | 160 | "fsctl.set should restore fields on mount_t that it temporarily overrides") |
d9a64523 A |
161 | { |
162 | int err; | |
163 | char *mount_path; | |
164 | disk_conditioner_info info; | |
165 | disk_conditioner_info mount_fields; | |
166 | ||
167 | T_SETUPBEGIN; | |
168 | mount_path = mktempmount(); | |
169 | T_SETUPEND; | |
170 | ||
171 | /* first set out-of-bounds values to retrieve the original mount_t fields */ | |
172 | memset(&info, UINT32_MAX, sizeof(info)); | |
173 | info.enabled = 1; | |
174 | info.access_time_usec = 0; | |
175 | info.read_throughput_mbps = 0; | |
176 | info.write_throughput_mbps = 0; | |
177 | /* everything else is UINT32_MAX */ | |
178 | err = fsctl(mount_path, DISK_CONDITIONER_IOC_SET, &info, 0); | |
179 | T_WITH_ERRNO; | |
180 | T_ASSERT_EQ_INT(0, err, "fsctl(DISK_CONDITIONER_IOC_SET)"); | |
181 | ||
182 | err = fsctl(mount_path, DISK_CONDITIONER_IOC_GET, &mount_fields, 0); | |
183 | T_WITH_ERRNO; | |
184 | T_ASSERT_EQ_INT(0, err, "fsctl(DISK_CONDITIONER_IOC_GET)"); | |
185 | ||
186 | /* now turn off the disk conditioner which should restore fields on the mount_t */ | |
187 | memset(&info, 1, sizeof(info)); | |
188 | info.enabled = 0; | |
189 | err = fsctl(mount_path, DISK_CONDITIONER_IOC_SET, &info, 0); | |
190 | T_WITH_ERRNO; | |
191 | T_ASSERT_EQ_INT(0, err, "fsctl(DISK_CONDITIONER_IOC_SET)"); | |
192 | ||
193 | /* and finally set out-of-bounds values again to retrieve the new mount_t fields which should not have changed */ | |
194 | memset(&info, UINT32_MAX, sizeof(info)); | |
195 | info.enabled = 0; | |
196 | info.access_time_usec = 0; | |
197 | info.read_throughput_mbps = 0; | |
198 | info.write_throughput_mbps = 0; | |
199 | /* everything else is UINT32_MAX */ | |
200 | err = fsctl(mount_path, DISK_CONDITIONER_IOC_SET, &info, 0); | |
201 | T_WITH_ERRNO; | |
202 | T_ASSERT_EQ_INT(0, err, "fsctl(DISK_CONDITIONER_IOC_SET)"); | |
203 | ||
204 | err = fsctl(mount_path, DISK_CONDITIONER_IOC_GET, &info, 0); | |
205 | T_WITH_ERRNO; | |
206 | T_ASSERT_EQ_INT(0, err, "fsctl(DISK_CONDITIONER_IOC_GET)"); | |
207 | ||
208 | T_ASSERT_EQ(info.maxreadcnt, mount_fields.maxreadcnt, "mount_t maxreadcnt restored"); | |
209 | T_ASSERT_EQ(info.maxwritecnt, mount_fields.maxwritecnt, "mount_t maxwritecnt restored"); | |
210 | T_ASSERT_EQ(info.segreadcnt, mount_fields.segreadcnt, "mount_t segreadcnt restored"); | |
211 | T_ASSERT_EQ(info.segwritecnt, mount_fields.segwritecnt, "mount_t segwritecnt restored"); | |
212 | T_ASSERT_EQ(info.ioqueue_depth, mount_fields.ioqueue_depth, "mount_t ioqueue_depth restored"); | |
213 | } | |
214 | ||
5ba3f43e | 215 | T_DECL(fsctl_get_nonroot, |
0a7de745 A |
216 | "fsctl.get should not require root", |
217 | T_META_ASROOT(false)) | |
5ba3f43e A |
218 | { |
219 | int err; | |
220 | char *mount_path; | |
221 | disk_conditioner_info info; | |
222 | ||
223 | T_SETUPBEGIN; | |
224 | // make sure we're not root | |
225 | if (0 == geteuid()) { | |
226 | seteuid(5000); | |
227 | } | |
228 | ||
229 | mount_path = mktempmount(); | |
230 | T_SETUPEND; | |
231 | ||
232 | err = fsctl(mount_path, DISK_CONDITIONER_IOC_GET, &info, 0); | |
233 | T_WITH_ERRNO; | |
234 | T_ASSERT_EQ_INT(0, err, "fsctl.get without root"); | |
235 | } | |
236 | ||
237 | T_DECL(fsctl_set_nonroot, | |
0a7de745 A |
238 | "fsctl.set should require root", |
239 | T_META_ASROOT(false)) | |
5ba3f43e A |
240 | { |
241 | int err; | |
242 | char *mount_path; | |
243 | disk_conditioner_info info = {0}; | |
244 | disk_conditioner_info expected_info = {0}; | |
245 | ||
246 | T_SETUPBEGIN; | |
247 | // make sure we're not root | |
248 | if (0 == geteuid()) { | |
249 | seteuid(5000); | |
250 | } | |
251 | ||
252 | mount_path = mktempmount(); | |
253 | T_SETUPEND; | |
254 | ||
255 | // save original info | |
256 | err = fsctl(mount_path, DISK_CONDITIONER_IOC_GET, &expected_info, 0); | |
257 | T_WITH_ERRNO; | |
258 | T_ASSERT_EQ_INT(0, err, "Get original DMC info"); | |
259 | ||
260 | info.enabled = 1; | |
261 | info.access_time_usec = 10; | |
262 | err = fsctl(mount_path, DISK_CONDITIONER_IOC_SET, &info, 0); | |
263 | T_WITH_ERRNO; | |
264 | T_ASSERT_NE_INT(0, err, "fsctl.set returns error without root"); | |
265 | ||
266 | err = fsctl(mount_path, DISK_CONDITIONER_IOC_GET, &info, 0); | |
267 | T_WITH_ERRNO; | |
268 | T_ASSERT_EQ_INT(0, err, "fsctl.get after nonroot fsctl.set"); | |
269 | ||
270 | err = memcmp(&info, &expected_info, sizeof(info)); | |
271 | T_ASSERT_EQ_INT(0, err, "fsctl.set should not change info without root"); | |
272 | } | |
273 | ||
274 | T_DECL(fsctl_delays, | |
cb323159 A |
275 | "Validate I/O delays when DMC is enabled", |
276 | T_META_RUN_CONCURRENTLY(false)) | |
5ba3f43e A |
277 | { |
278 | char *path; | |
279 | int fd; | |
280 | int err; | |
281 | uint64_t elapsed_nsec, expected_nsec; | |
d9a64523 | 282 | disk_conditioner_info info = {0}; |
5ba3f43e A |
283 | char buf[READSIZE]; |
284 | ||
285 | T_SETUPBEGIN; | |
286 | perf_setup(&path, &fd); | |
287 | memset(buf, 0xFF, sizeof(buf)); | |
288 | T_ASSERT_EQ_LONG((long)sizeof(buf), write(fd, buf, sizeof(buf)), "write random data to temp file"); | |
289 | fcntl(fd, F_FULLFSYNC); | |
290 | T_SETUPEND; | |
291 | ||
292 | expected_nsec = NSEC_PER_SEC / 2; | |
293 | ||
294 | // measure delay before setting parameters (should be none) | |
295 | elapsed_nsec = time_for_read(fd, buf); | |
296 | T_ASSERT_LT_ULLONG(elapsed_nsec, expected_nsec, "DMC disabled read(%ld) from %s is reasonably fast", READSIZE, path); | |
297 | ||
298 | // measure delay after setting parameters | |
299 | info.enabled = 1; | |
300 | info.access_time_usec = expected_nsec / NSEC_PER_USEC; | |
301 | info.read_throughput_mbps = 40; | |
302 | info.write_throughput_mbps = 40; | |
303 | info.is_ssd = 1; // is_ssd will ensure we get constant access_time delays rather than scaled | |
304 | err = fsctl(path, DISK_CONDITIONER_IOC_SET, &info, 0); | |
305 | T_WITH_ERRNO; | |
306 | T_ASSERT_EQ_INT(0, err, "fsctl(DISK_CONDITIONER_IOC_SET) delay"); | |
307 | ||
308 | elapsed_nsec = time_for_read(fd, buf); | |
309 | T_ASSERT_GT_ULLONG(elapsed_nsec, expected_nsec, "DMC enabled read(%ld) from %s is at least the expected delay", READSIZE, path); | |
310 | T_ASSERT_LT_ULLONG(elapsed_nsec, 2 * expected_nsec, "DMC enabled read(%ld) from %s is no more than twice the expected delay", READSIZE, path); | |
311 | ||
312 | // measure delay after resetting parameters (should be none) | |
313 | info.enabled = 0; | |
314 | err = fsctl(path, DISK_CONDITIONER_IOC_SET, &info, 0); | |
315 | T_WITH_ERRNO; | |
316 | T_ASSERT_EQ_INT(0, err, "fsctl(DISK_CONDITIONER_IOC_SET) reset delay"); | |
317 | ||
318 | usleep(USEC_PER_SEC / 2); // might still be other I/O inflight | |
319 | elapsed_nsec = time_for_read(fd, buf); | |
320 | T_ASSERT_LT_ULLONG(elapsed_nsec, expected_nsec, "After disabling DMC read(%ld) from %s is reasonably fast", READSIZE, path); | |
321 | } | |
322 | ||
323 | #else /* TEST_UNENTITLED */ | |
324 | ||
325 | #pragma mark Unentitled Tests | |
326 | ||
327 | T_DECL(fsctl_get_unentitled, | |
0a7de745 | 328 | "fsctl.get should not require entitlement") |
5ba3f43e A |
329 | { |
330 | int err; | |
331 | char *mount_path; | |
332 | disk_conditioner_info info; | |
333 | ||
334 | T_SETUPBEGIN; | |
335 | mount_path = mktempmount(); | |
336 | T_SETUPEND; | |
337 | ||
338 | err = fsctl(mount_path, DISK_CONDITIONER_IOC_GET, &info, 0); | |
339 | T_WITH_ERRNO; | |
340 | T_ASSERT_EQ_INT(0, err, "fsctl.get without entitlement"); | |
341 | } | |
342 | ||
343 | T_DECL(fsctl_set_unentitled, | |
0a7de745 | 344 | "fsctl.set should require entitlement") |
5ba3f43e A |
345 | { |
346 | int err; | |
347 | char *mount_path; | |
348 | disk_conditioner_info info = {0}; | |
349 | disk_conditioner_info expected_info = {0}; | |
350 | ||
351 | T_SETUPBEGIN; | |
352 | mount_path = mktempmount(); | |
353 | T_SETUPEND; | |
354 | ||
355 | // save original info | |
356 | err = fsctl(mount_path, DISK_CONDITIONER_IOC_GET, &expected_info, 0); | |
357 | T_WITH_ERRNO; | |
358 | T_ASSERT_EQ_INT(0, err, "Get original DMC info"); | |
359 | ||
360 | info.enabled = 1; | |
361 | info.access_time_usec = 10; | |
362 | err = fsctl(mount_path, DISK_CONDITIONER_IOC_SET, &info, 0); | |
363 | T_WITH_ERRNO; | |
364 | T_ASSERT_NE_INT(0, err, "fsctl.set returns error without entitlement"); | |
365 | ||
366 | err = fsctl(mount_path, DISK_CONDITIONER_IOC_GET, &info, 0); | |
367 | T_WITH_ERRNO; | |
368 | T_ASSERT_EQ_INT(0, err, "fsctl.get after unentitled fsctl.set"); | |
369 | ||
370 | err = memcmp(&info, &expected_info, sizeof(info)); | |
371 | T_ASSERT_EQ_INT(0, err, "fsctl.set should not change info without entitlement"); | |
372 | } | |
373 | ||
374 | #endif /* TEST_UNENTITLED */ | |
375 | ||
376 | #pragma mark Helpers | |
377 | ||
0a7de745 A |
378 | static char * |
379 | mktempdir(void) | |
380 | { | |
5ba3f43e A |
381 | char *path = malloc(PATH_MAX); |
382 | strcpy(path, "/tmp/dmc.XXXXXXXX"); | |
383 | atexit_b(^{ free(path); }); | |
384 | ||
385 | // create a temporary mount to run the fsctl on | |
386 | T_WITH_ERRNO; | |
387 | T_ASSERT_NOTNULL(mkdtemp(path), "Create temporary directory"); | |
388 | atexit_b(^{ remove(path); }); | |
389 | ||
390 | return path; | |
391 | } | |
392 | ||
393 | /* | |
394 | * Return the path to a temporary mount | |
395 | * with no usable filesystem but still | |
396 | * can be configured by the disk conditioner | |
397 | * | |
398 | * Faster than creating a ram disk to test with | |
399 | * when access to the filesystem is not necessary | |
400 | */ | |
0a7de745 A |
401 | static char * |
402 | mktempmount(void) | |
403 | { | |
5ba3f43e A |
404 | char *mount_path = mktempdir(); |
405 | ||
406 | T_WITH_ERRNO; | |
407 | T_ASSERT_EQ_INT(0, mount("devfs", mount_path, MNT_RDONLY, NULL), "Create temporary devfs mount"); | |
408 | atexit_b(^{ unmount(mount_path, MNT_FORCE); }); | |
409 | ||
410 | return mount_path; | |
411 | } | |
412 | ||
413 | #ifndef TEST_UNENTITLED | |
414 | ||
415 | /* | |
416 | * Wrapper around dt_launch_tool/dt_waitpid | |
417 | * that works like libc:system() | |
418 | */ | |
0a7de745 A |
419 | static int |
420 | system_legal(const char *command) | |
421 | { | |
5ba3f43e A |
422 | pid_t pid = -1; |
423 | int exit_status = 0; | |
424 | const char *argv[] = { | |
425 | _PATH_BSHELL, | |
426 | "-c", | |
427 | command, | |
428 | NULL | |
429 | }; | |
430 | ||
431 | int rc = dt_launch_tool(&pid, (char **)(void *)argv, false, NULL, NULL); | |
432 | if (rc != 0) { | |
433 | return -1; | |
434 | } | |
435 | if (!dt_waitpid(pid, &exit_status, NULL, 30)) { | |
436 | if (exit_status != 0) { | |
437 | return exit_status; | |
438 | } | |
439 | return -1; | |
440 | } | |
441 | ||
442 | return exit_status; | |
443 | } | |
444 | ||
445 | /* | |
446 | * Return the path to a temporary mount | |
447 | * that contains a usable HFS+ filesystem | |
448 | * mounted via a ram disk | |
449 | */ | |
0a7de745 A |
450 | static char * |
451 | mkramdisk(void) | |
452 | { | |
5ba3f43e A |
453 | char cmd[1024]; |
454 | char *mount_path = mktempdir(); | |
455 | char *dev_disk_file = malloc(256); | |
456 | atexit_b(^{ free(dev_disk_file); }); | |
457 | strcpy(dev_disk_file, "/tmp/dmc.ramdisk.XXXXXXXX"); | |
458 | ||
459 | T_WITH_ERRNO; | |
460 | T_ASSERT_NOTNULL(mktemp(dev_disk_file), "Create temporary file to store dev disk for ramdisk"); | |
461 | atexit_b(^{ remove(dev_disk_file); }); | |
462 | ||
463 | // create the RAM disk device | |
464 | snprintf(cmd, sizeof(cmd), "hdik -nomount ram://10000 > %s", dev_disk_file); | |
465 | T_ASSERT_EQ_INT(0, system_legal(cmd), "Create ramdisk"); | |
466 | ||
467 | atexit_b(^{ | |
468 | char eject_cmd[1024]; | |
469 | unmount(mount_path, MNT_FORCE); | |
470 | snprintf(eject_cmd, sizeof(eject_cmd), "hdik -e `cat %s`", dev_disk_file); | |
471 | system_legal(eject_cmd); | |
472 | remove(dev_disk_file); | |
473 | }); | |
474 | ||
475 | // initialize as an HFS volume | |
476 | snprintf(cmd, sizeof(cmd), "newfs_hfs `cat %s`", dev_disk_file); | |
477 | T_ASSERT_EQ_INT(0, system_legal(cmd), "Initialize ramdisk as HFS"); | |
478 | ||
479 | // mount it | |
480 | snprintf(cmd, sizeof(cmd), "mount -t hfs `cat %s` %s", dev_disk_file, mount_path); | |
481 | T_ASSERT_EQ_INT(0, system_legal(cmd), "Mount ramdisk"); | |
482 | ||
483 | return mount_path; | |
484 | } | |
485 | ||
0a7de745 A |
486 | static uint64_t |
487 | time_for_read(int fd, const char *expected) | |
488 | { | |
5ba3f43e A |
489 | int err; |
490 | ssize_t ret; | |
491 | char buf[READSIZE]; | |
492 | uint64_t start, stop; | |
493 | ||
494 | bzero(buf, sizeof(buf)); | |
495 | lseek(fd, 0, SEEK_SET); | |
496 | ||
497 | start = dt_nanoseconds(); | |
498 | ret = read(fd, buf, READSIZE); | |
499 | stop = dt_nanoseconds(); | |
500 | ||
501 | T_ASSERT_GE_LONG(ret, 0L, "read from temporary file"); | |
502 | T_ASSERT_EQ_LONG(ret, READSIZE, "read %ld bytes from temporary file", READSIZE); | |
503 | err = memcmp(buf, expected, sizeof(buf)); | |
504 | T_ASSERT_EQ_INT(0, err, "read expected contents from temporary file"); | |
505 | ||
0a7de745 | 506 | return stop - start; |
5ba3f43e A |
507 | } |
508 | ||
0a7de745 A |
509 | static void |
510 | perf_setup(char **path, int *fd) | |
511 | { | |
5ba3f43e A |
512 | int temp_fd; |
513 | char *temp_path; | |
514 | ||
515 | char *mount_path = mkramdisk(); | |
516 | temp_path = *path = malloc(PATH_MAX); | |
517 | snprintf(temp_path, PATH_MAX, "%s/dmc.XXXXXXXX", mount_path); | |
518 | atexit_b(^{ free(temp_path); }); | |
519 | ||
520 | T_ASSERT_NOTNULL(mktemp(temp_path), "Create temporary file"); | |
521 | atexit_b(^{ remove(temp_path); }); | |
522 | ||
523 | temp_fd = *fd = open(temp_path, O_RDWR | O_CREAT); | |
524 | T_WITH_ERRNO; | |
525 | T_ASSERT_GE_INT(temp_fd, 0, "Open temporary file for read/write"); | |
526 | atexit_b(^{ close(temp_fd); }); | |
527 | fcntl(temp_fd, F_NOCACHE, 1); | |
528 | } | |
529 | #endif /* !TEST_UNENTITLED */ |