blob: e2f64577d362dc43e38e8d12559264b0c0e572d1 [file] [log] [blame]
// Copyright 2016 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <assert.h>
#include <errno.h>
#include <lib/zircon-internal/xorshiftrand.h>
#include "util.h"
#define FAIL -1
#define BUSY 0
#define DONE 1
#define FBUFSIZE 65536
static_assert(FBUFSIZE == ((FBUFSIZE / sizeof(uint64_t)) * sizeof(uint64_t)),
"FBUFSIZE not multiple of uint64_t");
typedef struct worker worker_t;
struct worker {
worker_t* next;
int (*work)(worker_t* w);
rand64_t rdata;
rand32_t rops;
int fd;
int status;
uint32_t flags;
uint32_t size;
uint32_t pos;
union {
uint8_t u8[FBUFSIZE];
uint64_t u64[FBUFSIZE / sizeof(uint64_t)];
};
char name[256];
};
static worker_t* all_workers;
bool worker_new(const char* where, const char* fn,
int (*work)(worker_t* w), uint32_t size, uint32_t flags);
int worker_writer(worker_t* w);
static bool init_environment();
#define F_RAND_IOSIZE 1
#define KB(n) ((n)*1024)
#define MB(n) ((n)*1024 * 1024)
struct {
int (*work)(worker_t*);
const char* name;
uint32_t size;
uint32_t flags;
} WORK[] = {
{ worker_writer, "file0000", KB(512), F_RAND_IOSIZE, },
{ worker_writer, "file0001", MB(10), F_RAND_IOSIZE, },
{ worker_writer, "file0002", KB(512), F_RAND_IOSIZE, },
{ worker_writer, "file0003", KB(512), F_RAND_IOSIZE, },
{ worker_writer, "file0004", KB(512), 0, },
{ worker_writer, "file0005", MB(20), 0, },
{ worker_writer, "file0006", KB(512), 0, },
{ worker_writer, "file0007", KB(512), 0, },
};
int worker_rw(worker_t* w, bool do_read) {
if (w->pos == w->size) {
return DONE;
}
// offset into buffer
uint32_t off = w->pos % FBUFSIZE;
// fill our content buffer if it's empty
if (off == 0) {
for (unsigned n = 0; n < (FBUFSIZE / sizeof(uint64_t)); n++) {
w->u64[n] = rand64(&w->rdata);
}
}
// data in buffer available to write
uint32_t xfer = FBUFSIZE - off;
// do not exceed our desired size
if (xfer > (w->size - w->pos)) {
xfer = w->size - w->pos;
}
if ((w->flags & F_RAND_IOSIZE) && (xfer > 3000)) {
xfer = 3000 + (rand32(&w->rops) % (xfer - 3000));
}
ssize_t r;
if (do_read) {
uint8_t buffer[FBUFSIZE];
if ((r = emu_read(w->fd, buffer, xfer)) < 0) {
fprintf(stderr, "worker('%s') read failed @%u: %d\n",
w->name, w->pos, errno);
return FAIL;
}
if (memcmp(buffer, w->u8 + off, r)) {
fprintf(stderr, "worker('%s) verify failed @%u\n",
w->name, w->pos);
return FAIL;
}
} else {
if ((r = emu_write(w->fd, w->u8 + off, xfer)) < 0) {
fprintf(stderr, "worker('%s') write failed @%u: %d\n",
w->name, w->pos, errno);
return FAIL;
}
}
// advance
w->pos += r;
return BUSY;
}
int worker_verify(worker_t* w) {
int r = worker_rw(w, true);
if (r == DONE) {
emu_close(w->fd);
}
return r;
}
int worker_writer(worker_t* w) {
int r = worker_rw(w, false);
if (r == DONE) {
if (emu_lseek(w->fd, 0, SEEK_SET) != 0) {
fprintf(stderr, "worker('%s') seek failed: %s\n",
w->name, strerror(errno));
return FAIL;
}
// start at 0 and reset our data generator seed
srand64(&w->rdata, w->name);
w->pos = 0;
w->work = worker_verify;
return BUSY;
}
return r;
}
bool worker_new(const char* where, const char* fn,
int (*work)(worker_t* w), uint32_t size, uint32_t flags) {
worker_t* w = (worker_t*)calloc(1, sizeof(worker_t));
ASSERT_NE(w, nullptr);
snprintf(w->name, sizeof(w->name), "%s%s", where, fn);
srand64(&w->rdata, w->name);
srand32(&w->rops, w->name);
w->size = size;
w->work = work;
w->flags = flags;
if ((w->fd = emu_open(w->name, O_RDWR | O_CREAT | O_EXCL, 0644)) < 0) {
fprintf(stderr, "worker('%s') cannot create file\n", w->name);
free(w);
return false;
}
if (all_workers) {
w->next = all_workers;
}
all_workers = w;
return true;
}
int do_work() {
uint32_t busy_count = 0;
for (worker_t* w = all_workers; w != nullptr; w = w->next) {
if (w->status == BUSY) {
busy_count++;
if ((w->status = w->work(w)) == FAIL) {
return FAIL;
}
if (w->status == DONE) {
fprintf(stderr, "worker('%s') finished\n", w->name);
}
}
}
return busy_count ? BUSY : DONE;
}
bool do_all_work() {
BEGIN_HELPER;
for (;;) {
int r = do_work();
ASSERT_NE(r, FAIL);
if (r == DONE) {
break;
}
ASSERT_EQ(run_fsck(), 0);
}
END_HELPER;
}
static bool init_environment() {
all_workers = nullptr;
// assemble workers
const char* where = "::";
for (unsigned n = 0; n < fbl::count_of(WORK); n++) {
ASSERT_TRUE(worker_new(where, WORK[n].name, WORK[n].work,
WORK[n].size, WORK[n].flags));
}
return true;
}
bool TestWorkSingleThread(void) {
BEGIN_TEST;
ASSERT_TRUE(init_environment());
ASSERT_TRUE(do_all_work());
worker_t* w = all_workers;
worker_t* next;
while (w != NULL) {
next = w->next;
free(w);
w = next;
}
END_TEST;
}
RUN_MINFS_TESTS(rw_workers_test,
RUN_TEST_MEDIUM(TestWorkSingleThread)
)