#include <assert.h>
#include <string.h>
#include <sys/poll.h>
+#include <sys/types.h>
+#include <sys/stat.h>
#include "../fio.h"
struct io_u **events;
struct pollfd *pfds;
int *fd_flags;
+};
+
+struct binject_file {
unsigned int bs;
+ int minor;
+ int fd;
};
static void binject_buc_init(struct binject_data *bd, struct io_u *io_u)
return 0;
}
+static unsigned int binject_read_commands(struct thread_data *td, void *p,
+ int left, int *err)
+{
+ struct binject_file *bf;
+ struct fio_file *f;
+ int i, ret, events;
+
+one_more:
+ events = 0;
+ for_each_file(td, f, i) {
+ bf = (struct binject_file *) (uintptr_t) f->engine_data;
+ ret = read(bf->fd, p, left * sizeof(struct b_user_cmd));
+ if (ret < 0) {
+ if (errno == EAGAIN)
+ continue;
+ *err = -errno;
+ td_verror(td, errno, "read");
+ break;
+ } else if (ret) {
+ p += ret;
+ events += ret / sizeof(struct b_user_cmd);
+ }
+ }
+
+ if (*err || events)
+ return events;
+
+ usleep(1000);
+ goto one_more;
+}
+
static int fio_binject_getevents(struct thread_data *td, unsigned int min,
unsigned int max, struct timespec fio_unused *t)
{
void *buf = bd->cmds;
unsigned int i, events;
struct fio_file *f;
+ struct binject_file *bf;
/*
* Fill in the file descriptors
*/
for_each_file(td, f, i) {
+ bf = (struct binject_file *) (uintptr_t) f->engine_data;
+
/*
* don't block for min events == 0
*/
- if (!min) {
- bd->fd_flags[i] = fcntl(f->fd, F_GETFL);
- fcntl(f->fd, F_SETFL, bd->fd_flags[i] | O_NONBLOCK);
- }
- bd->pfds[i].fd = f->fd;
+ if (!min)
+ fio_set_fd_nonblocking(bf->fd, "binject");
+
+ bd->pfds[i].fd = bf->fd;
bd->pfds[i].events = POLLIN;
}
while (left) {
- void *p;
-
- do {
- if (!min)
- break;
-
+ while (!min) {
ret = poll(bd->pfds, td->o.nr_files, -1);
if (ret < 0) {
if (!r)
if (pollin_events(bd->pfds, td->o.nr_files))
break;
- } while (1);
+ }
if (r < 0)
break;
-re_read:
- p = buf;
- events = 0;
- for_each_file(td, f, i) {
- ret = read(f->fd, p, left * sizeof(struct b_user_cmd));
- if (ret < 0) {
- if (errno == EAGAIN)
- continue;
- r = -errno;
- td_verror(td, errno, "read");
- break;
- } else if (ret) {
- p += ret;
- events += ret / sizeof(struct b_user_cmd);
- }
- }
+ events = binject_read_commands(td, buf, left, &r);
if (r < 0)
break;
- if (!events) {
- usleep(1000);
- goto re_read;
- }
left -= events;
r += events;
for (i = 0; i < events; i++) {
struct b_user_cmd *buc = (struct b_user_cmd *) buf + i;
- bd->events[ev_index] = (struct io_u *) buc->usr_ptr;
+ bd->events[ev_index] = (struct io_u *) (unsigned long) buc->usr_ptr;
ev_index++;
}
}
if (!min) {
- for_each_file(td, f, i)
- fcntl(f->fd, F_SETFL, bd->fd_flags[i]);
+ for_each_file(td, f, i) {
+ bf = (struct binject_file *) (uintptr_t) f->engine_data;
+ fcntl(bf->fd, F_SETFL, bd->fd_flags[i]);
+ }
}
if (r > 0)
static int fio_binject_doio(struct thread_data *td, struct io_u *io_u)
{
struct b_user_cmd *buc = &io_u->buc;
- struct fio_file *f = io_u->file;
+ struct binject_file *bf = (struct binject_file *) (uintptr_t) io_u->file->engine_data;
int ret;
- ret = write(f->fd, buc, sizeof(*buc));
+ ret = write(bf->fd, buc, sizeof(*buc));
if (ret < 0)
return ret;
{
struct binject_data *bd = td->io_ops->data;
struct b_user_cmd *buc = &io_u->buc;
+ struct binject_file *bf = (struct binject_file *) (uintptr_t) io_u->file->engine_data;
- bd->bs = 512;
-
- if (io_u->xfer_buflen & (bd->bs - 1)) {
+ if (io_u->xfer_buflen & (bf->bs - 1)) {
log_err("read/write not sector aligned\n");
return EINVAL;
}
buc->type = B_TYPE_READ;
} else if (io_u->ddir == DDIR_WRITE) {
binject_buc_init(bd, io_u);
- buc->type = B_TYPE_WRITE;
+ if (io_u->flags & IO_U_F_BARRIER)
+ buc->type = B_TYPE_WRITEBARRIER;
+ else
+ buc->type = B_TYPE_WRITE;
} else if (io_u->ddir == DDIR_TRIM) {
binject_buc_init(bd, io_u);
buc->type = B_TYPE_DISCARD;
return bd->events[event];
}
+static int binject_open_ctl(struct thread_data *td)
+{
+ int fd;
+
+ fd = open("/dev/binject-ctl", O_RDWR);
+ if (fd < 0)
+ td_verror(td, errno, "open binject-ctl");
+
+ return fd;
+}
+
+static void binject_unmap_dev(struct thread_data *td, struct binject_file *bf)
+{
+ struct b_ioctl_cmd bic;
+ int fdb;
+
+ if (bf->fd >= 0) {
+ close(bf->fd);
+ bf->fd = -1;
+ }
+
+ fdb = binject_open_ctl(td);
+ if (fdb < 0)
+ return;
+
+ bic.minor = bf->minor;
+
+ if (ioctl(fdb, B_IOCTL_DEL, &bic) < 0)
+ td_verror(td, errno, "binject dev unmap");
+
+ close(fdb);
+}
+
+static int binject_map_dev(struct thread_data *td, struct binject_file *bf,
+ int fd)
+{
+ struct b_ioctl_cmd bic;
+ char name[80];
+ struct stat sb;
+ int fdb, dev_there, loops;
+
+ fdb = binject_open_ctl(td);
+ if (fdb < 0)
+ return 1;
+
+ bic.fd = fd;
+
+ if (ioctl(fdb, B_IOCTL_ADD, &bic) < 0) {
+ td_verror(td, errno, "binject dev map");
+ close(fdb);
+ return 1;
+ }
+
+ bf->minor = bic.minor;
+
+ sprintf(name, "/dev/binject%u", bf->minor);
+
+ /*
+ * Wait for udev to create the node...
+ */
+ dev_there = loops = 0;
+ do {
+ if (!stat(name, &sb)) {
+ dev_there = 1;
+ break;
+ }
+
+ usleep(10000);
+ } while (++loops < 100);
+
+ close(fdb);
+
+ if (!dev_there) {
+ log_err("fio: timed out waiting for binject dev\n");
+ goto err_unmap;
+ }
+
+ bf->fd = open(name, O_RDWR);
+ if (bf->fd < 0) {
+ td_verror(td, errno, "binject dev open");
+err_unmap:
+ binject_unmap_dev(td, bf);
+ return 1;
+ }
+
+ return 0;
+}
+
+static int fio_binject_close_file(struct thread_data *td, struct fio_file *f)
+{
+ struct binject_file *bf = (struct binject_file *) (uintptr_t) f->engine_data;
+
+ if (bf) {
+ binject_unmap_dev(td, bf);
+ free(bf);
+ f->engine_data = 0;
+ return generic_close_file(td, f);
+ }
+
+ return 0;
+}
+
+static int fio_binject_open_file(struct thread_data *td, struct fio_file *f)
+{
+ struct binject_file *bf;
+ unsigned int bs;
+ int ret;
+
+ ret = generic_open_file(td, f);
+ if (ret)
+ return 1;
+
+ if (f->filetype != FIO_TYPE_BD) {
+ log_err("fio: binject only works with block devices\n");
+ goto err_close;
+ }
+ if (ioctl(f->fd, BLKSSZGET, &bs) < 0) {
+ td_verror(td, errno, "BLKSSZGET");
+ goto err_close;
+ }
+
+ bf = malloc(sizeof(*bf));
+ bf->bs = bs;
+ bf->minor = bf->fd = -1;
+ f->engine_data = (uintptr_t) bf;
+
+ if (binject_map_dev(td, bf, f->fd)) {
+err_close:
+ ret = generic_close_file(td, f);
+ return 1;
+ }
+
+ return 0;
+}
+
static void fio_binject_cleanup(struct thread_data *td)
{
struct binject_data *bd = td->io_ops->data;
.getevents = fio_binject_getevents,
.event = fio_binject_event,
.cleanup = fio_binject_cleanup,
- .open_file = generic_open_file,
- .close_file = generic_close_file,
+ .open_file = fio_binject_open_file,
+ .close_file = fio_binject_close_file,
.get_file_size = generic_get_file_size,
- .flags = FIO_RAWIO,
+ .flags = FIO_RAWIO | FIO_BARRIER | FIO_MEMALIGN,
};
#else /* FIO_HAVE_BINJECT */
*/
static int fio_binject_init(struct thread_data fio_unused *td)
{
- fprintf(stderr, "fio: ioengine binject not available\n");
+ log_err("fio: ioengine binject not available\n");
return 1;
}