#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/ipc.h>
-#ifndef FIO_NO_HAVE_SHM_H
-#include <sys/shm.h>
-#endif
#include <sys/mman.h>
#include "fio.h"
+#ifndef FIO_NO_HAVE_SHM_H
+#include <sys/shm.h>
+#endif
#include "hash.h"
#include "smalloc.h"
#include "verify.h"
#include "lib/rand.h"
#include "memalign.h"
#include "server.h"
+#include "lib/getrusage.h"
+#include "idletime.h"
static pthread_t disk_util_thread;
static struct fio_mutex *disk_thread_mutex;
static char *cgroup_mnt;
static int exit_value;
static volatile int fio_abort;
+static unsigned int nr_process = 0;
+static unsigned int nr_thread = 0;
struct io_log *agg_io_log[DDIR_RWDIR_CNT];
int groupid = 0;
unsigned int thread_number = 0;
unsigned int stat_number = 0;
-unsigned int nr_process = 0;
-unsigned int nr_thread = 0;
int shm_id = 0;
int temp_stall_ts;
unsigned long done_secs = 0;
}
static int check_min_rate(struct thread_data *td, struct timeval *now,
- unsigned long *bytes_done)
+ uint64_t *bytes_done)
{
int ret = 0;
return 0;
}
+static int fio_file_fsync(struct thread_data *td, struct fio_file *f)
+{
+ int ret;
+
+ if (fio_file_open(f))
+ return fio_io_sync(td, f);
+
+ if (td_io_open_file(td, f))
+ return 1;
+
+ ret = fio_io_sync(td, f);
+ td_io_close_file(td, f);
+ return ret;
+}
+
static inline void __update_tv_cache(struct thread_data *td)
{
fio_gettime(&td->tv_cache, NULL);
return 0;
}
+static void check_update_rusage(struct thread_data *td)
+{
+ if (td->update_rusage) {
+ td->update_rusage = 0;
+ update_rusage_stat(td);
+ fio_mutex_up(td->rusage_sem);
+ }
+}
+
/*
* The main verify engine. Runs over the writes we previously submitted,
* reads the blocks back in, and checks the crc/md5 of the data.
*/
-static void do_verify(struct thread_data *td)
+static void do_verify(struct thread_data *td, uint64_t verify_bytes)
{
+ uint64_t bytes_done[DDIR_RWDIR_CNT] = { 0, 0, 0 };
struct fio_file *f;
struct io_u *io_u;
int ret, min_events;
break;
}
+ check_update_rusage(td);
+
if (td->error)
return;
int ret2, full;
update_tv_cache(td);
+ check_update_rusage(td);
if (runtime_exceeded(td, &td->tv_cache)) {
__update_tv_cache(td);
break;
}
} else {
+ if (ddir_rw_sum(bytes_done) + td->o.rw_min_bs > verify_bytes)
+ break;
+
while ((io_u = get_io_u(td)) != NULL) {
/*
* We are only interested in the places where
requeue_io_u(td, &io_u);
} else {
sync_done:
- ret = io_u_sync_complete(td, io_u, NULL);
+ ret = io_u_sync_complete(td, io_u, bytes_done);
if (ret < 0)
break;
}
* and do the verification on them through
* the callback handler
*/
- if (io_u_queued_complete(td, min_events, NULL) < 0) {
+ if (io_u_queued_complete(td, min_events, bytes_done) < 0) {
ret = -1;
break;
}
break;
}
+ check_update_rusage(td);
+
if (!td->error) {
min_events = td->cur_depth;
/*
* Main IO worker function. It retrieves io_u's to process and queues
* and reaps them, checking for rate and errors along the way.
+ *
+ * Returns number of bytes written and trimmed.
*/
-static void do_io(struct thread_data *td)
+static uint64_t do_io(struct thread_data *td)
{
+ uint64_t bytes_done[DDIR_RWDIR_CNT] = { 0, 0, 0 };
unsigned int i;
int ret = 0;
(!flist_empty(&td->trim_list)) || !io_bytes_exceeded(td) ||
td->o.time_based) {
struct timeval comp_time;
- unsigned long bytes_done[DDIR_RWDIR_CNT] = { 0, 0, 0 };
int min_evts = 0;
struct io_u *io_u;
int ret2, full;
enum fio_ddir ddir;
+ check_update_rusage(td);
+
if (td->terminate || td->done)
break;
}
}
+ check_update_rusage(td);
+
if (td->trim_entries)
log_err("fio: %d trim entries leaked?\n", td->trim_entries);
i = td->cur_depth;
if (i) {
- ret = io_u_queued_complete(td, i, NULL);
+ ret = io_u_queued_complete(td, i, bytes_done);
if (td->o.fill_device && td->error == ENOSPC)
td->error = 0;
}
td_set_runstate(td, TD_FSYNCING);
for_each_file(td, f, i) {
- if (!fio_file_open(f))
+ if (!fio_file_fsync(td, f))
continue;
- fio_io_sync(td, f);
+
+ log_err("fio: end_fsync failed for file %s\n",
+ f->file_name);
}
}
} else
*/
if (!ddir_rw_sum(td->this_io_bytes))
td->done = 1;
+
+ return bytes_done[DDIR_WRITE] + bytes_done[DDIR_TRIM];
}
static void cleanup_io_u(struct thread_data *td)
char *p;
max_units = td->o.iodepth;
- max_bs = max(td->o.max_bs[DDIR_READ], td->o.max_bs[DDIR_WRITE]);
- max_bs = max(td->o.max_bs[DDIR_TRIM], max_bs);
+ max_bs = td_max_bs(td);
min_write = td->o.min_bs[DDIR_WRITE];
td->orig_buffer_size = (unsigned long long) max_bs
* (unsigned long long) max_units;
return 1;
}
- if (ddir_rw_sum(td->io_bytes) < td->o.size)
+ if (td->o.size != -1ULL && ddir_rw_sum(td->io_bytes) < td->o.size) {
+ uint64_t diff;
+
+ /*
+ * If the difference is less than the minimum IO size, we
+ * are done.
+ */
+ diff = td->o.size - ddir_rw_sum(td->io_bytes);
+ if (diff < td_max_bs(td))
+ return 0;
+
return 1;
+ }
return 0;
}
{
unsigned long long elapsed;
struct thread_data *td = data;
+ struct thread_options *o = &td->o;
pthread_condattr_t attr;
int clear_state;
+ int ret;
- if (!td->o.use_thread) {
+ if (!o->use_thread) {
setsid();
td->pid = getpid();
} else
dprint(FD_PROCESS, "jobs pid=%d started\n", (int) td->pid);
+ if (is_backend)
+ fio_server_send_start(td);
+
INIT_FLIST_HEAD(&td->io_u_freelist);
INIT_FLIST_HEAD(&td->io_u_busylist);
INIT_FLIST_HEAD(&td->io_u_requeues);
* eating a file descriptor
*/
fio_mutex_remove(td->mutex);
+ td->mutex = NULL;
/*
* A new gid requires privilege, so we need to do this before setting
* the uid.
*/
- if (td->o.gid != -1U && setgid(td->o.gid)) {
+ if (o->gid != -1U && setgid(o->gid)) {
td_verror(td, errno, "setgid");
goto err;
}
- if (td->o.uid != -1U && setuid(td->o.uid)) {
+ if (o->uid != -1U && setuid(o->uid)) {
td_verror(td, errno, "setuid");
goto err;
}
* If we have a gettimeofday() thread, make sure we exclude that
* thread from this job
*/
- if (td->o.gtod_cpu)
- fio_cpu_clear(&td->o.cpumask, td->o.gtod_cpu);
+ if (o->gtod_cpu)
+ fio_cpu_clear(&o->cpumask, o->gtod_cpu);
/*
* Set affinity first, in case it has an impact on the memory
* allocations.
*/
- if (td->o.cpumask_set && fio_setaffinity(td->pid, td->o.cpumask) == -1) {
- td_verror(td, errno, "cpu_set_affinity");
- goto err;
+ if (o->cpumask_set) {
+ ret = fio_setaffinity(td->pid, o->cpumask);
+ if (ret == -1) {
+ td_verror(td, errno, "cpu_set_affinity");
+ goto err;
+ }
}
#ifdef CONFIG_LIBNUMA
if (init_io_u(td))
goto err;
- if (td->o.verify_async && verify_async_init(td))
+ if (o->verify_async && verify_async_init(td))
goto err;
- if (td->ioprio_set) {
- if (ioprio_set(IOPRIO_WHO_PROCESS, 0, td->ioprio) == -1) {
+ if (o->ioprio) {
+ ret = ioprio_set(IOPRIO_WHO_PROCESS, 0, o->ioprio_class, o->ioprio);
+ if (ret == -1) {
td_verror(td, errno, "ioprio_set");
goto err;
}
goto err;
errno = 0;
- if (nice(td->o.nice) == -1 && errno != 0) {
+ if (nice(o->nice) == -1 && errno != 0) {
td_verror(td, errno, "nice");
goto err;
}
- if (td->o.ioscheduler && switch_ioscheduler(td))
+ if (o->ioscheduler && switch_ioscheduler(td))
goto err;
- if (!td->o.create_serialize && setup_files(td))
+ if (!o->create_serialize && setup_files(td))
goto err;
if (td_io_init(td))
if (init_random_map(td))
goto err;
- if (td->o.exec_prerun) {
- if (exec_string(td->o.exec_prerun))
- goto err;
- }
+ if (o->exec_prerun && exec_string(o->exec_prerun))
+ goto err;
- if (td->o.pre_read) {
+ if (o->pre_read) {
if (pre_read_files(td) < 0)
goto err;
}
- fio_gettime(&td->epoch, NULL);
- getrusage(RUSAGE_SELF, &td->ru_start);
+ fio_verify_init(td);
+ fio_gettime(&td->epoch, NULL);
+ fio_getrusage(&td->ru_start);
clear_state = 0;
while (keep_running(td)) {
+ uint64_t verify_bytes;
+
fio_gettime(&td->start, NULL);
memcpy(&td->bw_sample_time, &td->start, sizeof(td->start));
memcpy(&td->iops_sample_time, &td->start, sizeof(td->start));
prune_io_piece_log(td);
- do_io(td);
+ verify_bytes = do_io(td);
clear_state = 1;
fio_gettime(&td->start, NULL);
- do_verify(td);
+ do_verify(td, verify_bytes);
td->ts.runtime[DDIR_READ] += utime_since_now(&td->start);
td->ts.io_bytes[DDIR_WRITE] = td->io_bytes[DDIR_WRITE];
td->ts.io_bytes[DDIR_TRIM] = td->io_bytes[DDIR_TRIM];
+ fio_unpin_memory(td);
+
fio_mutex_down(writeout_mutex);
if (td->bw_log) {
if (td->o.bw_log_file) {
close_ioengine(td);
cgroup_shutdown(td, &cgroup_mnt);
- if (td->o.cpumask_set) {
- int ret = fio_cpuset_exit(&td->o.cpumask);
+ if (o->cpumask_set) {
+ int ret = fio_cpuset_exit(&o->cpumask);
td_verror(td, ret, "fio_cpuset_exit");
}
if (td->o.write_iolog_file)
write_iolog_close(td);
+ fio_mutex_remove(td->rusage_sem);
+ td->rusage_sem = NULL;
+
td_set_runstate(td, TD_EXITED);
return (void *) (uintptr_t) td->error;
}
unsigned long spent;
unsigned int i, todo, nr_running, m_rate, t_rate, nr_started;
- if (fio_pin_memory())
- return;
-
if (fio_gtod_offload && fio_start_gtod_thread())
return;
+
+ fio_idle_prof_init();
set_sig_handlers();
+ nr_thread = nr_process = 0;
+ for_each_td(td, i) {
+ if (td->o.use_thread)
+ nr_thread++;
+ else
+ nr_process++;
+ }
+
if (output_format == FIO_OUTPUT_NORMAL) {
log_info("Starting ");
if (nr_thread)
}
}
+ /* start idle threads before io threads start to run */
+ fio_idle_prof_start();
+
set_genesis_time();
while (todo) {
init_disk_util(td);
+ td->rusage_sem = fio_mutex_init(FIO_MUTEX_LOCKED);
+ td->update_rusage = 0;
+
/*
* Set state to created. Thread will transition
* to TD_INITIALIZED when it's done setting up.
reap_threads(&nr_running, &t_rate, &m_rate);
- if (todo) {
- if (is_backend)
- fio_server_idle_loop();
- else
- usleep(100000);
- }
+ if (todo)
+ usleep(100000);
}
while (nr_running) {
reap_threads(&nr_running, &t_rate, &m_rate);
-
- if (is_backend)
- fio_server_idle_loop();
- else
- usleep(10000);
+ usleep(10000);
}
+ fio_idle_prof_stop();
+
update_io_ticks();
- fio_unpin_memory();
}
void wait_for_disk_thread_exit(void)
return 0;
if (write_bw_log) {
- setup_log(&agg_io_log[DDIR_READ], 0);
- setup_log(&agg_io_log[DDIR_WRITE], 0);
- setup_log(&agg_io_log[DDIR_TRIM], 0);
+ setup_log(&agg_io_log[DDIR_READ], 0, IO_LOG_TYPE_BW);
+ setup_log(&agg_io_log[DDIR_WRITE], 0, IO_LOG_TYPE_BW);
+ setup_log(&agg_io_log[DDIR_TRIM], 0, IO_LOG_TYPE_BW);
}
startup_mutex = fio_mutex_init(FIO_MUTEX_LOCKED);