]>
git.saurik.com Git - apple/hfs.git/blob - tests/cases/test-throttled-io.c
1 #include <TargetConditionals.h>
8 #include <sys/resource.h>
9 #include <CommonCrypto/CommonDigest.h>
16 #include <sys/errno.h>
17 #include <libkern/OSAtomic.h>
20 #include <sys/mount.h>
22 #include "hfs-tests.h"
23 #include "test-utils.h"
24 #include "disk-image.h"
26 #define TEST_PATH "/tmp/throttled-io.sparseimage"
27 #define MOUNT_POINT "/tmp/throttled_io"
31 static disk_image_t
*gDI
;
32 static char *gFile1
, *gFile2
, *gFile3
;
35 static const size_t gBuf_size
= 64 * 1024 * 1024;
37 static pid_t bg_io_pid
= 0;
38 static size_t BG_IOSIZE
= (4U << 10); // BG-IO buffer size 4KiB
39 static off_t BG_MAX_FILESIZE
= (1ULL << 30); // Max BG-IO File-size 1GiB
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
48 errno_t
background_io_worker(void)
55 // Open the file at path `gFile1`, create a new file if one does not
58 fd
= open(gFile1
, O_RDWR
|O_TRUNC
);
59 if (fd
== -1 && errno
== ENOENT
) {
61 fd
= creat(gFile1
, 0666);
63 fprintf(stderr
, "Failed to create file: %s\n",
68 fd
= open(gFile1
, O_RDWR
);
72 // Return errno if we could not open the file.
75 fprintf(stderr
, "Failed to open file: %s\n", gFile1
);
80 // Allocate the write buffer on-stack such that we don't have to free
83 buffer
= alloca(BG_IOSIZE
);
86 (void)memset(buffer
, -1, BG_IOSIZE
);
92 written
= pwrite(fd
, buffer
, BG_IOSIZE
, offset
);
98 if (offset
>= BG_MAX_FILESIZE
) {
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.
108 if (!(offset
% (off_t
)(BG_IOSIZE
* 1024 * 32))) {
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
119 if (getppid() == 1) {
125 // Should not come here.
131 // Start a continious background-io process, if successful the pid of the
132 // background-io is cached in `bg_io_pid'.
134 static void start_background_io_process(void)
142 assert_fail("Failed to spawn background-io "
143 "process, error %d, %s.\n", errno
,
148 child_ret
= background_io_worker();
152 bg_io_pid
= child_pid
;
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.
161 static void kill_background_io_process(void)
169 kill(bg_io_pid
, SIGKILL
);
171 child_pid
= waitpid(bg_io_pid
, &child_status
, WUNTRACED
);
172 } while (child_pid
== -1 && errno
== EINTR
);
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
,
180 if (WIFEXITED(child_status
)) {
183 error
= WEXITSTATUS(child_status
);
185 assert_fail("background-io exited with error %d, "
186 "%s.\n", error
, strerror(error
));
193 static int run_test1(void)
199 // Kick off another process to ensure we get throttled.
201 start_background_io_process();
204 // Cache the set IO policy of this process.
206 orig_io_policy
= getiopolicy_np(IOPOL_TYPE_DISK
, IOPOL_SCOPE_PROCESS
);
207 assert_with_errno(orig_io_policy
!= -1);
210 // Set new IO policy for this process.
212 assert_no_err(setiopolicy_np(IOPOL_TYPE_DISK
, IOPOL_SCOPE_PROCESS
,
215 assert_with_errno((fd
= open("/dev/random", O_RDONLY
)) >= 0);
216 assert_with_errno((fd2
= open(gFile2
, O_RDWR
| O_CREAT
| O_TRUNC
,
219 assert_no_err(fcntl(fd2
, F_SINGLE_WRITER
, 1));
220 assert_no_err(fcntl(fd2
, F_NOCACHE
, 1));
222 gBuf
= valloc(gBuf_size
);
226 ssize_t res
= check_io(read(fd
, gBuf
, gBuf_size
), gBuf_size
);
228 CC_SHA1_Update(&ctx
, gBuf
, (CC_LONG
)res
);
230 res
= check_io(write(fd2
, gBuf
, res
), res
);
232 bzero(gBuf
, gBuf_size
);
237 lseek(fd2
, 0, SEEK_SET
);
239 res
= check_io(read(fd2
, gBuf
, gBuf_size
), gBuf_size
);
241 CC_SHA1_Update(&ctx2
, gBuf
, (CC_LONG
)res
);
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
);
247 assert(!memcmp(digest1
, digest2
, CC_SHA1_DIGEST_LENGTH
));
249 assert_no_err (close(fd
));
250 assert_no_err (close(fd2
));
253 // Kill the background IO process.
255 kill_background_io_process();
258 // Restore the orig. IO policy.
260 assert_no_err(setiopolicy_np(IOPOL_TYPE_DISK
, IOPOL_SCOPE_PROCESS
,
265 static volatile uint64_t written
;
266 static volatile bool done
;
268 static void test2_thread(void)
270 int fd
= open(gFile3
, O_RDONLY
);
273 void *b
= gBuf
+ gBuf_size
/ 2;
274 uLong seq
= crc32(0, Z_NULL
, 0);
281 res
= check_io(pread(fd
, b
, gBuf_size
/ 2, offset
), -1);
282 } while (res
== 0 && !done
);
284 assert (res
% 4 == 0);
288 for (uLong
*p
= b
; res
; ++p
, res
-= sizeof(uLong
)) {
289 seq
= crc32(Z_NULL
, (void *)&seq
, 4);
293 if (offset
< written
)
298 assert_no_err (close(fd
));
301 static int run_test2(void)
307 // Kick off another process to ensure we get throttled.
309 start_background_io_process();
312 // Cache the set IO policy of this process.
314 orig_io_policy
= getiopolicy_np(IOPOL_TYPE_DISK
, IOPOL_SCOPE_PROCESS
);
315 assert_with_errno(orig_io_policy
!= -1);
318 // Set new IO policy for this process.
320 assert_no_err(setiopolicy_np(IOPOL_TYPE_DISK
, IOPOL_SCOPE_PROCESS
,
323 fd
= open(gFile3
, O_RDWR
| O_CREAT
| O_TRUNC
, 0666);
326 assert_no_err(fcntl(fd
, F_SINGLE_WRITER
, 1));
327 assert_no_err(fcntl(fd
, F_NOCACHE
, 1));
330 pthread_create(&thread
, NULL
, (void *(*)(void *))test2_thread
, NULL
);
331 uLong seq
= crc32(0, Z_NULL
, 0);
333 for (int i
= 0; i
< 4; ++i
) {
335 for (unsigned i
= 0; i
< gBuf_size
/ 2 / sizeof(uLong
); ++i
) {
336 seq
= crc32(Z_NULL
, (void *)&seq
, 4);
340 ssize_t res
= check_io(write(fd
, gBuf
, gBuf_size
/ 2), gBuf_size
/ 2);
349 pthread_join(thread
, NULL
);
351 assert_no_err (close(fd
));
354 // Kill the background IO process.
356 kill_background_io_process();
359 // Restore the orig. IO policy.
361 assert_no_err(setiopolicy_np(IOPOL_TYPE_DISK
, IOPOL_SCOPE_PROCESS
,
367 static bool clean_up(void)
369 kill_background_io_process();
382 int run_throttled_io(__unused test_ctx_t
*ctx
)
385 gDI
= disk_image_create(TEST_PATH
, &(disk_image_opts_t
){
387 .mount_point
= MOUNT_POINT
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
);
394 test_cleanup(^ bool {
398 int res
= run_test1();
409 #endif // !TARGET_OS_BRIDGE