vitastor/fio_engine.cpp

374 lines
11 KiB
C++
Raw Normal View History

2019-11-25 02:31:48 +03:00
// FIO engine to test Blockstore
//
2019-12-15 15:11:26 +03:00
// Initialize storage for tests:
//
// dd if=/dev/zero of=test_data.bin bs=1024 count=1048576
// dd if=/dev/zero of=test_meta.bin bs=1024 count=256
// dd if=/dev/zero of=test_journal.bin bs=1024 count=4096
//
// Random write:
//
// fio -thread -ioengine=./libfio_blockstore.so -name=test -bs=4k -direct=1 -fsync=16 -iodepth=16 -rw=randwrite \
// -data_device=./test_data.bin -meta_device=./test_meta.bin -journal_device=./test_journal.bin -size=1000M
//
// Linear write:
//
// fio -thread -ioengine=./libfio_blockstore.so -name=test -bs=128k -direct=1 -fsync=32 -iodepth=32 -rw=write \
// -data_device=./test_data.bin -meta_device=./test_meta.bin -journal_device=./test_journal.bin -size=1000M
//
// Random read (run with -iodepth=32 or -iodepth=1):
//
// fio -thread -ioengine=./libfio_blockstore.so -name=test -bs=4k -direct=1 -iodepth=32 -rw=randread \
// -data_device=./test_data.bin -meta_device=./test_meta.bin -journal_device=./test_journal.bin -size=1000M
2019-11-25 02:31:48 +03:00
#include "blockstore.h"
2019-11-26 00:02:54 +03:00
extern "C" {
#define CONFIG_PWRITEV2
#include "fio/fio.h"
#include "fio/optgroup.h"
}
2019-11-25 02:31:48 +03:00
struct bs_data
{
blockstore_t *bs;
2019-11-25 02:31:48 +03:00
ring_loop_t *ringloop;
/* The list of completed io_u structs. */
2019-11-25 22:35:44 +03:00
std::vector<io_u*> completed;
2019-11-27 02:04:46 +03:00
int op_n = 0, inflight = 0;
2019-11-25 02:31:48 +03:00
};
struct bs_options
{
int __pad;
char *data_device = NULL, *meta_device = NULL, *journal_device = NULL, *disable_fsync = NULL, *block_size_order = NULL;
2019-11-25 02:31:48 +03:00
};
static struct fio_option options[] = {
{
.name = "data_device",
.lname = "Data device",
2019-11-26 00:02:54 +03:00
.type = FIO_OPT_STR_STORE,
.off1 = offsetof(struct bs_options, data_device),
.help = "Name of the data device/file",
.category = FIO_OPT_C_ENGINE,
.group = FIO_OPT_G_FILENAME,
},
{
.name = "meta_device",
.lname = "Metadata device",
.type = FIO_OPT_STR_STORE,
.off1 = offsetof(struct bs_options, meta_device),
.help = "Name of the metadata device/file",
.category = FIO_OPT_C_ENGINE,
.group = FIO_OPT_G_FILENAME,
},
{
.name = "journal_device",
.lname = "Journal device",
.type = FIO_OPT_STR_STORE,
.off1 = offsetof(struct bs_options, journal_device),
.help = "Name of the journal device/file",
2019-11-25 02:31:48 +03:00
.category = FIO_OPT_C_ENGINE,
.group = FIO_OPT_G_FILENAME,
},
2019-11-30 23:55:30 +03:00
{
.name = "disable_fsync",
.lname = "Disable fsync",
.type = FIO_OPT_STR_STORE,
.off1 = offsetof(struct bs_options, disable_fsync),
.help = "Disable fsyncs for blockstore (unsafe if your disk has cache)",
.category = FIO_OPT_C_ENGINE,
.group = FIO_OPT_G_FILENAME,
},
{
.name = "block_size_order",
.lname = "Power of 2 for blockstore block size",
.type = FIO_OPT_STR_STORE,
.off1 = offsetof(struct bs_options, block_size_order),
.help = "Set blockstore block size to 2^this value (from 12 to 27)",
.category = FIO_OPT_C_ENGINE,
.group = FIO_OPT_G_FILENAME,
},
2019-11-25 02:31:48 +03:00
{
.name = NULL,
},
};
static int bs_setup(struct thread_data *td)
{
bs_data *bsd;
2019-12-14 20:51:41 +03:00
//fio_file *f;
//int r;
2019-11-25 02:31:48 +03:00
//int64_t size;
2019-11-26 02:18:37 +03:00
bsd = new bs_data;
2019-11-25 02:31:48 +03:00
if (!bsd)
{
td_verror(td, errno, "calloc");
return 1;
}
td->io_ops_data = bsd;
if (!td->files_index)
{
add_file(td, "blockstore", 0, 0);
td->o.nr_files = td->o.nr_files ? : 1;
td->o.open_files++;
}
2019-12-14 20:51:41 +03:00
//f = td->files[0];
2019-11-25 02:31:48 +03:00
//f->real_file_size = size;
return 0;
}
static void bs_cleanup(struct thread_data *td)
{
2019-11-26 00:02:54 +03:00
bs_data *bsd = (bs_data*)td->io_ops_data;
2019-11-25 02:31:48 +03:00
if (bsd)
{
2019-11-28 02:27:17 +03:00
while (1)
{
2019-11-28 02:27:17 +03:00
do
{
bsd->ringloop->loop();
if (bsd->bs->is_safe_to_stop())
goto safe;
} while (bsd->ringloop->get_loop_again());
bsd->ringloop->wait();
}
2019-11-28 02:27:17 +03:00
safe:
delete bsd->bs;
2019-11-28 02:27:17 +03:00
delete bsd->ringloop;
2019-11-26 02:18:37 +03:00
delete bsd;
2019-11-25 02:31:48 +03:00
}
}
/* Connect to the server from each thread. */
static int bs_init(struct thread_data *td)
{
2019-11-26 00:02:54 +03:00
bs_options *o = (bs_options*)td->eo;
bs_data *bsd = (bs_data*)td->io_ops_data;
2019-11-25 02:31:48 +03:00
blockstore_config_t config;
config["journal_device"] = o->journal_device;
config["meta_device"] = o->meta_device;
config["data_device"] = o->data_device;
if (o->block_size_order)
config["block_size_order"] = o->block_size_order;
2019-11-30 23:55:30 +03:00
if (o->disable_fsync)
config["disable_fsync"] = o->disable_fsync;
if (read_only)
config["readonly"] = "true";
2019-11-25 02:31:48 +03:00
bsd->ringloop = new ring_loop_t(512);
bsd->bs = new blockstore_t(config, bsd->ringloop);
2019-11-26 02:18:37 +03:00
while (1)
2019-11-25 02:31:48 +03:00
{
bsd->ringloop->loop();
2019-11-26 02:18:37 +03:00
if (bsd->bs->is_started())
break;
bsd->ringloop->wait();
2019-11-25 02:31:48 +03:00
}
log_info("fio: blockstore initialized\n");
return 0;
}
/* Begin read or write request. */
static enum fio_q_status bs_queue(struct thread_data *td, struct io_u *io)
2019-11-25 02:31:48 +03:00
{
2019-11-26 00:02:54 +03:00
bs_data *bsd = (bs_data*)td->io_ops_data;
int n = bsd->op_n;
2019-11-25 02:31:48 +03:00
fio_ro_check(td, io);
2019-11-25 02:31:48 +03:00
io->engine_data = bsd;
2019-11-25 02:31:48 +03:00
if (io->ddir == DDIR_WRITE || io->ddir == DDIR_READ)
2019-12-10 00:13:57 +03:00
assert(io->xfer_buflen <= bsd->bs->get_block_size());
2019-11-25 02:31:48 +03:00
blockstore_op_t *op = new blockstore_op_t;
op->callback = NULL;
2019-11-25 22:35:44 +03:00
switch (io->ddir)
2019-11-25 22:35:44 +03:00
{
2019-11-25 02:31:48 +03:00
case DDIR_READ:
2019-11-25 22:35:44 +03:00
op->flags = OP_READ;
op->buf = io->xfer_buf;
2019-11-25 22:35:44 +03:00
op->oid = {
.inode = 1,
2019-12-10 00:13:57 +03:00
.stripe = io->offset >> bsd->bs->get_block_order(),
2019-11-25 22:35:44 +03:00
};
2019-12-10 00:13:57 +03:00
op->offset = io->offset % bsd->bs->get_block_size();
op->len = io->xfer_buflen;
op->callback = [io, n](blockstore_op_t *op)
2019-11-25 22:35:44 +03:00
{
io->error = op->retval < 0 ? -op->retval : 0;
bs_data *bsd = (bs_data*)io->engine_data;
2019-12-03 12:09:30 +03:00
bsd->inflight--;
bsd->completed.push_back(io);
2019-12-03 12:09:30 +03:00
#ifdef BLOCKSTORE_DEBUG
printf("--- OP_READ %llx n=%d retval=%d\n", io, n, op->retval);
#endif
2019-11-25 22:35:44 +03:00
delete op;
};
2019-11-25 02:31:48 +03:00
break;
case DDIR_WRITE:
2019-11-25 22:35:44 +03:00
op->flags = OP_WRITE;
op->buf = io->xfer_buf;
2019-11-25 22:35:44 +03:00
op->oid = {
.inode = 1,
2019-12-10 00:13:57 +03:00
.stripe = io->offset >> bsd->bs->get_block_order(),
2019-11-25 22:35:44 +03:00
};
2019-12-10 00:13:57 +03:00
op->offset = io->offset % bsd->bs->get_block_size();
op->len = io->xfer_buflen;
op->callback = [io, n](blockstore_op_t *op)
2019-11-25 22:35:44 +03:00
{
io->error = op->retval < 0 ? -op->retval : 0;
bs_data *bsd = (bs_data*)io->engine_data;
2019-11-27 02:04:46 +03:00
bsd->inflight--;
bsd->completed.push_back(io);
2019-11-27 18:07:08 +03:00
#ifdef BLOCKSTORE_DEBUG
printf("--- OP_WRITE %llx n=%d retval=%d\n", io, n, op->retval);
#endif
2019-11-25 22:35:44 +03:00
delete op;
};
2019-11-25 02:31:48 +03:00
break;
case DDIR_SYNC:
2019-11-25 22:35:44 +03:00
op->flags = OP_SYNC;
op->callback = [io, n](blockstore_op_t *op)
2019-11-25 22:35:44 +03:00
{
bs_data *bsd = (bs_data*)io->engine_data;
auto & unstable_writes = bsd->bs->get_unstable_writes();
if (op->retval >= 0 && unstable_writes.size() > 0)
2019-11-25 22:35:44 +03:00
{
op->flags = OP_STABLE;
op->len = unstable_writes.size();
2019-11-26 00:02:54 +03:00
obj_ver_id *vers = new obj_ver_id[op->len];
op->buf = vers;
2019-11-25 22:35:44 +03:00
int i = 0;
for (auto it = unstable_writes.begin(); it != unstable_writes.end(); it++, i++)
2019-11-25 22:35:44 +03:00
{
2019-11-26 00:02:54 +03:00
vers[i] = {
2019-11-25 22:35:44 +03:00
.oid = it->first,
.version = it->second,
};
}
unstable_writes.clear();
op->callback = [io, n](blockstore_op_t *op)
2019-11-25 22:35:44 +03:00
{
io->error = op->retval < 0 ? -op->retval : 0;
bs_data *bsd = (bs_data*)io->engine_data;
bsd->completed.push_back(io);
2019-11-27 02:04:46 +03:00
bsd->inflight--;
2019-11-26 00:02:54 +03:00
obj_ver_id *vers = (obj_ver_id*)op->buf;
delete[] vers;
2019-11-27 18:07:08 +03:00
#ifdef BLOCKSTORE_DEBUG
printf("--- OP_SYNC %llx n=%d retval=%d\n", io, n, op->retval);
#endif
2019-11-25 22:35:44 +03:00
delete op;
};
bsd->bs->enqueue_op(op);
2019-11-25 22:35:44 +03:00
}
else
{
io->error = op->retval < 0 ? -op->retval : 0;
bsd->completed.push_back(io);
2019-11-27 02:04:46 +03:00
bsd->inflight--;
2019-11-27 18:07:08 +03:00
#ifdef BLOCKSTORE_DEBUG
printf("--- OP_SYNC %llx n=%d retval=%d\n", io, n, op->retval);
#endif
2019-11-25 22:35:44 +03:00
delete op;
}
};
2019-11-25 02:31:48 +03:00
break;
default:
io->error = EINVAL;
2019-11-25 02:31:48 +03:00
return FIO_Q_COMPLETED;
}
2019-11-27 18:07:08 +03:00
#ifdef BLOCKSTORE_DEBUG
2019-12-03 12:09:30 +03:00
printf("+++ %s %llx n=%d\n", op->flags == OP_READ ? "OP_READ" : (op->flags == OP_WRITE ? "OP_WRITE" : "OP_SYNC"), io, n);
2019-11-27 18:07:08 +03:00
#endif
io->error = 0;
2019-11-27 02:04:46 +03:00
bsd->inflight++;
2019-11-25 22:35:44 +03:00
bsd->bs->enqueue_op(op);
bsd->op_n++;
2019-11-25 02:31:48 +03:00
if (io->error != 0)
return FIO_Q_COMPLETED;
2019-11-25 02:31:48 +03:00
return FIO_Q_QUEUED;
}
static int bs_getevents(struct thread_data *td, unsigned int min, unsigned int max, const struct timespec *t)
{
2019-11-26 00:02:54 +03:00
bs_data *bsd = (bs_data*)td->io_ops_data;
2019-11-25 22:35:44 +03:00
// FIXME timeout
2019-11-26 02:18:37 +03:00
while (true)
2019-11-25 22:35:44 +03:00
{
bsd->ringloop->loop();
2019-11-26 02:18:37 +03:00
if (bsd->completed.size() >= min)
break;
bsd->ringloop->wait();
2019-11-25 02:31:48 +03:00
}
2019-11-25 22:35:44 +03:00
return bsd->completed.size();
2019-11-25 02:31:48 +03:00
}
static struct io_u *bs_event(struct thread_data *td, int event)
{
2019-11-26 00:02:54 +03:00
bs_data *bsd = (bs_data*)td->io_ops_data;
2019-11-25 22:35:44 +03:00
if (bsd->completed.size() == 0)
2019-11-25 02:31:48 +03:00
return NULL;
2019-11-25 22:35:44 +03:00
/* FIXME We ignore the event number and assume fio calls us exactly once for [0..nr_events-1] */
struct io_u *ev = bsd->completed.back();
bsd->completed.pop_back();
return ev;
2019-11-25 02:31:48 +03:00
}
static int bs_io_u_init(struct thread_data *td, struct io_u *io)
2019-11-25 02:31:48 +03:00
{
io->engine_data = NULL;
2019-11-25 02:31:48 +03:00
return 0;
}
static void bs_io_u_free(struct thread_data *td, struct io_u *io)
2019-11-25 02:31:48 +03:00
{
}
static int bs_open_file(struct thread_data *td, struct fio_file *f)
{
return 0;
}
static int bs_invalidate(struct thread_data *td, struct fio_file *f)
{
return 0;
}
2019-11-26 00:02:54 +03:00
struct ioengine_ops ioengine = {
2019-11-25 02:31:48 +03:00
.name = "microceph_blockstore",
.version = FIO_IOOPS_VERSION,
.flags = FIO_MEMALIGN | FIO_DISKLESSIO | FIO_NOEXTEND,
.setup = bs_setup,
.init = bs_init,
.queue = bs_queue,
.getevents = bs_getevents,
.event = bs_event,
2019-11-26 00:02:54 +03:00
.cleanup = bs_cleanup,
2019-11-25 02:31:48 +03:00
.open_file = bs_open_file,
.invalidate = bs_invalidate,
2019-11-26 00:02:54 +03:00
.io_u_init = bs_io_u_init,
.io_u_free = bs_io_u_free,
.option_struct_size = sizeof(struct bs_options),
.options = options,
2019-11-25 02:31:48 +03:00
};
static void fio_init fio_bs_register(void)
{
register_ioengine(&ioengine);
}
static void fio_exit fio_bs_unregister(void)
{
unregister_ioengine(&ioengine);
}