test-throttled-io.c [plain text]
#include <sys/fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/resource.h>
#include <CommonCrypto/CommonDigest.h>
#include <stdbool.h>
#include <stdio.h>
#include <spawn.h>
#include <string.h>
#include <signal.h>
#include <stdarg.h>
#include <sys/errno.h>
#include <libkern/OSAtomic.h>
#include <zlib.h>
#include <pthread.h>
#include "hfs-tests.h"
#include "test-utils.h"
#include "disk-image.h"
TEST(throttled_io)
static disk_image_t *gDI;
static char *gFile1, *gFile2, *gFile3;
static pid_t gPID = 0;
static void *gBuf;
static const size_t gBuf_size = 64 * 1024 * 1024;
static void start_background_io(void)
{
char *of;
asprintf(&of, "of=%s", gFile1);
assert_no_err(posix_spawn(&gPID, "/bin/dd", NULL, NULL,
(char *[]){ "/bin/dd", "if=/dev/random",
of, NULL },
NULL));
}
static void end_background_io(void)
{
if ( gPID != 0 )
{
kill(gPID, SIGKILL);
int stat;
wait(&stat);
gPID = 0;
}
}
static int run_test1(void)
{
start_background_io();
assert_no_err(setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_PROCESS,
IOPOL_THROTTLE));
int fd, fd2;
assert_with_errno((fd = open("/dev/random", O_RDONLY)) >= 0);
assert_with_errno((fd2 = open(gFile2,
O_RDWR | O_CREAT | O_TRUNC, 0666)) >= 0);
assert_no_err(fcntl(fd2, F_SINGLE_WRITER, 1));
assert_no_err(fcntl(fd2, F_NOCACHE, 1));
gBuf = valloc(gBuf_size);
CC_SHA1_CTX ctx;
CC_SHA1_Init(&ctx);
ssize_t res = check_io(read(fd, gBuf, gBuf_size), gBuf_size);
CC_SHA1_Update(&ctx, gBuf, (CC_LONG)res);
res = check_io(write(fd2, gBuf, res), res);
bzero(gBuf, gBuf_size);
CC_SHA1_CTX ctx2;
CC_SHA1_Init(&ctx2);
lseek(fd2, 0, SEEK_SET);
res = check_io(read(fd2, gBuf, gBuf_size), gBuf_size);
CC_SHA1_Update(&ctx2, gBuf, (CC_LONG)res);
uint8_t digest1[CC_SHA1_DIGEST_LENGTH], digest2[CC_SHA1_DIGEST_LENGTH];
CC_SHA1_Final(digest1, &ctx);
CC_SHA1_Final(digest2, &ctx2);
assert(!memcmp(digest1, digest2, CC_SHA1_DIGEST_LENGTH));
assert_no_err (close(fd));
assert_no_err (close(fd2));
end_background_io();
return 0;
}
static volatile uint64_t written;
static volatile bool done;
static void test2_thread(void)
{
int fd = open(gFile3, O_RDONLY);
assert(fd >= 0);
void *b = gBuf + gBuf_size / 2;
uLong seq = crc32(0, Z_NULL, 0);
uint32_t offset = 0;
do {
ssize_t res;
do {
res = check_io(pread(fd, b, gBuf_size / 2, offset), -1);
} while (res == 0 && !done);
assert (res % 4 == 0);
offset += res;
for (uLong *p = b; res; ++p, res -= sizeof(uLong)) {
seq = crc32(Z_NULL, (void *)&seq, 4);
assert(*p == seq);
}
if (offset < written)
continue;
OSMemoryBarrier();
} while (!done);
assert_no_err (close(fd));
}
static int run_test2(void)
{
start_background_io();
int fd = open(gFile3, O_RDWR | O_CREAT | O_TRUNC, 0666);
assert(fd >= 0);
assert_no_err(fcntl(fd, F_SINGLE_WRITER, 1));
assert_no_err(fcntl(fd, F_NOCACHE, 1));
pthread_t thread;
pthread_create(&thread, NULL, (void *(*)(void *))test2_thread, NULL);
uLong seq = crc32(0, Z_NULL, 0);
for (int i = 0; i < 4; ++i) {
uLong *p = gBuf;
for (unsigned i = 0; i < gBuf_size / 2 / sizeof(uLong); ++i) {
seq = crc32(Z_NULL, (void *)&seq, 4);
p[i] = seq;
}
ssize_t res = check_io(write(fd, gBuf, gBuf_size / 2), gBuf_size / 2);
written += res;
}
OSMemoryBarrier();
done = true;
pthread_join(thread, NULL);
assert_no_err (close(fd));
end_background_io();
return 0;
}
static bool clean_up(void)
{
end_background_io();
unlink(gFile1);
unlink(gFile2);
unlink(gFile3);
free(gFile1);
free(gFile2);
free(gFile3);
return true;
}
int run_throttled_io(__unused test_ctx_t *ctx)
{
gDI = disk_image_get();
asprintf(&gFile1, "%s/throttled_io.1", gDI->mount_point);
asprintf(&gFile2, "%s/throttled_io.2", gDI->mount_point);
asprintf(&gFile3, "%s/throttled_io.3", gDI->mount_point);
test_cleanup(^ bool {
return clean_up();
});
int res = run_test1();
if (res)
return res;
res = run_test2();
if (res)
return res;
return res;
}