#include "server.h"
#include "lib/getrusage.h"
#include "idletime.h"
+#include "err.h"
static pthread_t disk_util_thread;
static struct fio_mutex *disk_thread_mutex;
break;
while ((io_u = get_io_u(td)) != NULL) {
+ if (IS_ERR(io_u)) {
+ io_u = NULL;
+ ret = FIO_Q_BUSY;
+ goto reap;
+ }
+
/*
* We are only interested in the places where
* we wrote or trimmed IOs. Turn those into
* completed io_u's first. Note that we can get BUSY even
* without IO queued, if the system is resource starved.
*/
+reap:
full = queue_full(td) || (ret == FIO_Q_BUSY && td->cur_depth);
if (full || !td->o.iodepth_batch_complete) {
min_events = min(td->o.iodepth_batch_complete,
break;
io_u = get_io_u(td);
- if (!io_u) {
+ if (IS_ERR_OR_NULL(io_u)) {
+ int err = PTR_ERR(io_u);
+
+ io_u = NULL;
+ if (err == -EBUSY) {
+ ret = FIO_Q_BUSY;
+ goto reap;
+ }
if (td->o.latency_target)
goto reap;
break;
if (diff < td_max_bs(td))
return 0;
+ if (fio_files_done(td))
+ return 0;
+
return 1;
}
--- /dev/null
+#ifndef FIO_ERR_H
+#define FIO_ERR_H
+
+/*
+ * Kernel pointers have redundant information, so we can use a
+ * scheme where we can return either an error code or a dentry
+ * pointer with the same return value.
+ *
+ * This should be a per-architecture thing, to allow different
+ * error and pointer decisions.
+ */
+#define MAX_ERRNO 4095
+
+#define IS_ERR_VALUE(x) ((x) >= (unsigned long)-MAX_ERRNO)
+
+static inline void *ERR_PTR(long error)
+{
+ return (void *) error;
+}
+
+static inline long PTR_ERR(const void *ptr)
+{
+ return (long) ptr;
+}
+
+static inline long IS_ERR(const void *ptr)
+{
+ return IS_ERR_VALUE((unsigned long)ptr);
+}
+
+static inline long IS_ERR_OR_NULL(const void *ptr)
+{
+ return !ptr || IS_ERR_VALUE((unsigned long)ptr);
+}
+
+static inline int PTR_ERR_OR_ZERO(const void *ptr)
+{
+ if (IS_ERR(ptr))
+ return PTR_ERR(ptr);
+ else
+ return 0;
+}
+
+#endif
extern int get_fileno(struct thread_data *, const char *);
extern void free_release_files(struct thread_data *);
void fio_file_reset(struct thread_data *, struct fio_file *);
+int fio_files_done(struct thread_data *);
#endif
unsigned long long total_size, extend_size;
struct thread_options *o = &td->o;
struct fio_file *f;
- unsigned int i;
+ unsigned int i, nr_fs_extra = 0;
int err = 0, need_extend;
int old_state;
+ const unsigned int bs = td_min_bs(td);
+ uint64_t fs = 0;
dprint(FD_FILE, "setup files\n");
goto err_out;
}
+ /*
+ * Calculate per-file size and potential extra size for the
+ * first files, if needed.
+ */
+ if (!o->file_size_low) {
+ uint64_t all_fs;
+
+ fs = o->size / o->nr_files;
+ all_fs = fs * o->nr_files;
+
+ if (all_fs < o->size)
+ nr_fs_extra = (o->size - all_fs) / bs;
+ }
+
/*
* now file sizes are known, so we can set ->io_size. if size= is
* not given, ->io_size is just equal to ->real_file_size. if size
if (!o->file_size_low) {
/*
* no file size range given, file size is equal to
- * total size divided by number of files. if that is
- * zero, set it to the real file size.
+ * total size divided by number of files. If that is
+ * zero, set it to the real file size. If the size
+ * doesn't divide nicely with the min blocksize,
+ * make the first files bigger.
*/
- f->io_size = o->size / o->nr_files;
+ f->io_size = fs;
+ if (nr_fs_extra) {
+ nr_fs_extra--;
+ f->io_size += bs;
+ }
+
if (!f->io_size)
f->io_size = f->real_file_size - f->file_offset;
} else if (f->real_file_size < o->file_size_low ||
if (td->o.random_generator == FIO_RAND_GEN_LFSR)
lfsr_reset(&f->lfsr, td->rand_seeds[FIO_RAND_BLOCK_OFF]);
}
+
+int fio_files_done(struct thread_data *td)
+{
+ struct fio_file *f;
+ unsigned int i;
+
+ for_each_file(td, f, i)
+ if (!fio_file_done(f))
+ return 0;
+
+ return 1;
+}
#include "trim.h"
#include "lib/rand.h"
#include "lib/axmap.h"
+#include "err.h"
struct io_completion_data {
int nr; /* input */
if (!fio_file_open(f)) {
int err;
+ if (td->nr_open_files >= td->o.open_files)
+ return ERR_PTR(-EBUSY);
+
err = td_io_open_file(td, f);
if (err)
continue;
if (!fio_file_open(f)) {
int err;
+ if (td->nr_open_files >= td->o.open_files)
+ return ERR_PTR(-EBUSY);
+
err = td_io_open_file(td, f);
if (err) {
dprint(FD_FILE, "error %d on open of %s\n",
else
f = get_next_file_rand(td, FIO_FILE_open, FIO_FILE_closing);
+ if (IS_ERR(f))
+ return f;
+
td->file_service_file = f;
td->file_service_left = td->file_service_nr - 1;
out:
return __get_next_file(td);
}
-static int set_io_u_file(struct thread_data *td, struct io_u *io_u)
+static long set_io_u_file(struct thread_data *td, struct io_u *io_u)
{
struct fio_file *f;
do {
f = get_next_file(td);
- if (!f)
- return 1;
+ if (IS_ERR_OR_NULL(f))
+ return PTR_ERR(f);
io_u->file = f;
get_file(f);
struct fio_file *f;
struct io_u *io_u;
int do_scramble = 0;
+ long ret = 0;
io_u = __get_io_u(td);
if (!io_u) {
if (read_iolog_get(td, io_u))
goto err_put;
} else if (set_io_u_file(td, io_u)) {
+ ret = -EBUSY;
dprint(FD_IO, "io_u %p, setting file failed\n", io_u);
goto err_put;
}
f = io_u->file;
+ if (!f) {
+ dprint(FD_IO, "io_u %p, setting file failed\n", io_u);
+ goto err_put;
+ }
+
assert(fio_file_open(f));
if (ddir_rw(io_u->ddir)) {
err_put:
dprint(FD_IO, "get_io_u failed\n");
put_io_u(td, io_u);
- return NULL;
+ return ERR_PTR(ret);
}
void io_u_log_error(struct thread_data *td, struct io_u *io_u)