[PATCH] First cut at supporting > 1 file per job
This is likely very buggy, a simple test works though.
Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
diff --git a/filesetup.c b/filesetup.c
new file mode 100644
index 0000000..adfe940
--- /dev/null
+++ b/filesetup.c
@@ -0,0 +1,380 @@
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <assert.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+#include "fio.h"
+#include "os.h"
+
+static int create_file(struct thread_data *td, struct fio_file *f)
+{
+ unsigned long long left;
+ unsigned int bs;
+ char *b;
+ int r;
+
+ f->fd = open(f->file_name, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ if (f->fd < 0) {
+ td_verror(td, errno);
+ return 1;
+ }
+
+ if (ftruncate(f->fd, f->file_size) == -1) {
+ td_verror(td, errno);
+ goto err;
+ }
+
+ b = malloc(td->max_bs);
+ memset(b, 0, td->max_bs);
+
+ left = f->file_size;
+ while (left && !td->terminate) {
+ bs = td->max_bs;
+ if (bs > left)
+ bs = left;
+
+ r = write(f->fd, b, bs);
+
+ if (r == (int) bs) {
+ left -= bs;
+ continue;
+ } else {
+ if (r < 0)
+ td_verror(td, errno);
+ else
+ td_verror(td, EIO);
+
+ break;
+ }
+ }
+
+ if (td->terminate)
+ unlink(f->file_name);
+ else if (td->create_fsync)
+ fsync(f->fd);
+
+ free(b);
+ close(f->fd);
+ f->fd = -1;
+ return 0;
+err:
+ close(f->fd);
+ f->fd = -1;
+ return 1;
+}
+
+static int create_files(struct thread_data *td)
+{
+ struct fio_file *f;
+ int i, err;
+
+ /*
+ * unless specifically asked for overwrite, let normal io extend it
+ */
+ if (!td->overwrite) {
+ td->io_size = td->total_file_size;
+ for_each_file(td, f, i)
+ f->file_size = td->total_file_size / td->nr_files;
+
+ return 0;
+ }
+
+ if (!td->total_file_size) {
+ log_err("Need size for create\n");
+ td_verror(td, EINVAL);
+ return 1;
+ }
+
+ temp_stall_ts = 1;
+ fprintf(f_out, "%s: Laying out IO file(s) (%LuMiB)\n",
+ td->name, td->total_file_size >> 20);
+
+ err = 0;
+ for_each_file(td, f, i) {
+ f->file_size = td->total_file_size / td->nr_files;
+ err = create_file(td, f);
+ break;
+
+ td->io_size += f->file_size;
+ }
+
+ temp_stall_ts = 0;
+ return err;
+}
+
+static int file_size(struct thread_data *td, struct fio_file *f)
+{
+ struct stat st;
+
+ if (td->overwrite) {
+ if (fstat(f->fd, &st) == -1) {
+ td_verror(td, errno);
+ return 1;
+ }
+
+ f->real_file_size = st.st_size;
+
+ if (!f->file_size || f->file_size > f->real_file_size)
+ f->file_size = f->real_file_size;
+ }
+
+ f->file_size -= f->file_offset;
+ return 0;
+}
+
+static int bdev_size(struct thread_data *td, struct fio_file *f)
+{
+ unsigned long long bytes;
+ int r;
+
+ r = blockdev_size(f->fd, &bytes);
+ if (r) {
+ td_verror(td, r);
+ return 1;
+ }
+
+ f->real_file_size = bytes;
+
+ /*
+ * no extend possibilities, so limit size to device size if too large
+ */
+ if (!f->file_size || f->file_size > f->real_file_size)
+ f->file_size = f->real_file_size;
+
+ f->file_size -= f->file_offset;
+ return 0;
+}
+
+static int get_file_size(struct thread_data *td, struct fio_file *f)
+{
+ int ret = 0;
+
+ if (td->filetype == FIO_TYPE_FILE)
+ ret = file_size(td, f);
+ else if (td->filetype == FIO_TYPE_BD)
+ ret = bdev_size(td, f);
+ else
+ f->real_file_size = -1;
+
+ if (ret)
+ return ret;
+
+ if (f->file_offset > f->real_file_size) {
+ log_err("%s: offset extends end (%Lu > %Lu)\n", td->name, f->file_offset, f->real_file_size);
+ return 1;
+ }
+
+ td->io_size += f->file_size;
+ return 0;
+}
+
+static int __setup_file_mmap(struct thread_data *td, struct fio_file *f)
+{
+ int flags;
+
+ if (td_rw(td))
+ flags = PROT_READ | PROT_WRITE;
+ else if (td_write(td)) {
+ flags = PROT_WRITE;
+
+ if (td->verify != VERIFY_NONE)
+ flags |= PROT_READ;
+ } else
+ flags = PROT_READ;
+
+ f->mmap = mmap(NULL, f->file_size, flags, MAP_SHARED, f->fd, f->file_offset);
+ if (f->mmap == MAP_FAILED) {
+ f->mmap = NULL;
+ td_verror(td, errno);
+ return 1;
+ }
+
+ if (td->invalidate_cache) {
+ if (madvise(f->mmap, f->file_size, MADV_DONTNEED) < 0) {
+ td_verror(td, errno);
+ return 1;
+ }
+ }
+
+ if (td->sequential) {
+ if (madvise(f->mmap, f->file_size, MADV_SEQUENTIAL) < 0) {
+ td_verror(td, errno);
+ return 1;
+ }
+ } else {
+ if (madvise(f->mmap, f->file_size, MADV_RANDOM) < 0) {
+ td_verror(td, errno);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static int setup_files_mmap(struct thread_data *td)
+{
+ struct fio_file *f;
+ int i, err = 0;
+
+ for_each_file(td, f, i) {
+ err = __setup_file_mmap(td, f);
+ if (err)
+ break;
+ }
+
+ return err;
+}
+
+static int __setup_file_plain(struct thread_data *td, struct fio_file *f)
+{
+ if (td->invalidate_cache) {
+ if (fadvise(f->fd, f->file_offset, f->file_size, POSIX_FADV_DONTNEED) < 0) {
+ td_verror(td, errno);
+ return 1;
+ }
+ }
+
+ if (td->sequential) {
+ if (fadvise(f->fd, f->file_offset, f->file_size, POSIX_FADV_SEQUENTIAL) < 0) {
+ td_verror(td, errno);
+ return 1;
+ }
+ } else {
+ if (fadvise(f->fd, f->file_offset, f->file_size, POSIX_FADV_RANDOM) < 0) {
+ td_verror(td, errno);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static int setup_files_plain(struct thread_data *td)
+{
+ struct fio_file *f;
+ int i, err = 0;
+
+ for_each_file(td, f, i) {
+ err = __setup_file_plain(td, f);
+ if (err)
+ break;
+ }
+
+ return err;
+}
+
+static int setup_file(struct thread_data *td, struct fio_file *f)
+{
+ struct stat st;
+ int flags = 0;
+
+ if (stat(f->file_name, &st) == -1) {
+ if (errno != ENOENT) {
+ td_verror(td, errno);
+ return 1;
+ }
+ if (!td->create_file) {
+ td_verror(td, ENOENT);
+ return 1;
+ }
+ if (create_file(td, f))
+ return 1;
+ } else if (td->filetype == FIO_TYPE_FILE &&
+ st.st_size < (off_t) f->file_size) {
+ if (create_file(td, f))
+ return 1;
+ }
+
+ if (td->odirect)
+ flags |= OS_O_DIRECT;
+
+ if (td_write(td) || td_rw(td)) {
+ if (td->filetype == FIO_TYPE_FILE) {
+ if (!td->overwrite)
+ flags |= O_TRUNC;
+
+ flags |= O_CREAT;
+ }
+ if (td->sync_io)
+ flags |= O_SYNC;
+
+ flags |= O_RDWR;
+
+ f->fd = open(f->file_name, flags, 0600);
+ } else {
+ if (td->filetype == FIO_TYPE_CHAR)
+ flags |= O_RDWR;
+ else
+ flags |= O_RDONLY;
+
+ f->fd = open(f->file_name, flags);
+ }
+
+ if (f->fd == -1) {
+ td_verror(td, errno);
+ return 1;
+ }
+
+ if (get_file_size(td, f))
+ return 1;
+
+ return 0;
+}
+
+int setup_files(struct thread_data *td)
+{
+ struct fio_file *f;
+ int i, err;
+
+ /*
+ * if ioengine defines a setup() method, it's responsible for
+ * setting up everything in the td->files[] area.
+ */
+ if (td->io_ops->setup)
+ return td->io_ops->setup(td);
+
+ if (create_files(td))
+ return 1;
+
+ for_each_file(td, f, i) {
+ err = setup_file(td, f);
+ if (err)
+ break;
+ }
+
+ if (td->io_size == 0) {
+ log_err("%s: no io blocks\n", td->name);
+ td_verror(td, EINVAL);
+ return 1;
+ }
+
+ if (!td->zone_size)
+ td->zone_size = td->io_size;
+
+ td->total_io_size = td->io_size * td->loops;
+
+ if (td->io_ops->flags & FIO_MMAPIO)
+ return setup_files_mmap(td);
+ else
+ return setup_files_plain(td);
+}
+
+void close_files(struct thread_data *td)
+{
+ int i;
+
+ for (i = 0; i < td->nr_files; i++) {
+ struct fio_file *f = &td->files[i];
+
+ if (f->fd != -1) {
+ close(f->fd);
+ f->fd = -1;
+ }
+ if (f->mmap) {
+ munmap(f->mmap, f->file_size);
+ f->mmap = NULL;
+ }
+ }
+}