#include "lib/getrusage.h"
#include "idletime.h"
#include "err.h"
+#include "lib/tp.h"
+
+static pthread_t helper_thread;
+static pthread_mutex_t helper_lock;
+pthread_cond_t helper_cond;
+int helper_do_stat = 0;
-static pthread_t disk_util_thread;
-static struct fio_mutex *disk_thread_mutex;
static struct fio_mutex *startup_mutex;
-static struct fio_mutex *writeout_mutex;
static struct flist_head *cgroup_list;
static char *cgroup_mnt;
static int exit_value;
int shm_id = 0;
int temp_stall_ts;
unsigned long done_secs = 0;
-volatile int disk_util_exit = 0;
+volatile int helper_exit = 0;
#define PAGE_ALIGN(buf) \
(char *) (((uintptr_t) (buf) + page_mask) & ~page_mask)
fio_server_got_signal(sig);
else {
log_info("\nfio: terminating on signal %d\n", sig);
- fflush(stdout);
+ log_info_flush();
exit_value = 128;
}
ratemin);
return 1;
} else {
- rate = ((bytes - td->rate_bytes[ddir]) * 1000) / spent;
+ if (spent)
+ rate = ((bytes - td->rate_bytes[ddir]) * 1000) / spent;
+ else
+ rate = 0;
+
if (rate < ratemin ||
bytes < td->rate_bytes[ddir]) {
log_err("%s: min rate %u not met, got"
td->o.name, rate_iops);
return 1;
} else {
- rate = ((iops - td->rate_blocks[ddir]) * 1000) / spent;
+ if (spent)
+ rate = ((iops - td->rate_blocks[ddir]) * 1000) / spent;
+ else
+ rate = 0;
+
if (rate < rate_iops_min ||
iops < td->rate_blocks[ddir]) {
log_err("%s: min iops rate %u not met,"
* fill_device option is set.
*/
td_clear_error(td);
- td->terminate = 1;
+ fio_mark_td_terminate(td);
return 1;
} else {
/*
if (runtime_exceeded(td, &td->tv_cache)) {
__update_tv_cache(td);
if (runtime_exceeded(td, &td->tv_cache)) {
- td->terminate = 1;
+ fio_mark_td_terminate(td);
break;
}
}
static int io_bytes_exceeded(struct thread_data *td)
{
- unsigned long long bytes;
+ unsigned long long bytes, limit;
if (td_rw(td))
bytes = td->this_io_bytes[DDIR_READ] + td->this_io_bytes[DDIR_WRITE];
else
bytes = td->this_io_bytes[DDIR_TRIM];
- return bytes >= td->o.size || exceeds_number_ios(td);
+ if (td->o.io_limit)
+ limit = td->o.io_limit;
+ else
+ limit = td->o.size;
+
+ return bytes >= limit || exceeds_number_ios(td);
}
/*
if (runtime_exceeded(td, &td->tv_cache)) {
__update_tv_cache(td);
if (runtime_exceeded(td, &td->tv_cache)) {
- td->terminate = 1;
+ fio_mark_td_terminate(td);
break;
}
}
case FIO_Q_COMPLETED:
if (io_u->error) {
ret = -io_u->error;
+ unlog_io_piece(td, io_u);
clear_io_u(td, io_u);
} else if (io_u->resid) {
int bytes = io_u->xfer_buflen - io_u->resid;
struct fio_file *f = io_u->file;
bytes_issued += bytes;
+
+ trim_io_piece(td, io_u);
+
/*
* zero read, fail
*/
if (!bytes) {
+ unlog_io_piece(td, io_u);
td_verror(td, EIO, "full resid");
put_io_u(td, io_u);
break;
bytes_issued += io_u->xfer_buflen;
break;
case FIO_Q_BUSY:
+ unlog_io_piece(td, io_u);
requeue_io_u(td, &io_u);
ret2 = td_io_commit(td);
if (ret2 < 0)
if (td->o.fill_device && td->error == ENOSPC) {
td->error = 0;
- td->terminate = 1;
+ fio_mark_td_terminate(td);
}
if (!td->error) {
struct fio_file *f;
/*
* Read back and check that the selected scheduler is now the default.
*/
- ret = fread(tmp, 1, sizeof(tmp), f);
+ ret = fread(tmp, sizeof(tmp), 1, f);
if (ferror(f) || ret < 0) {
td_verror(td, errno, "fread");
fclose(f);
return 1;
}
+ tmp[sizeof(tmp) - 1] = '\0';
+
sprintf(tmp2, "[%s]", td->o.ioscheduler);
if (!strstr(tmp, tmp2)) {
static int keep_running(struct thread_data *td)
{
+ unsigned long long limit;
+
if (td->done)
return 0;
if (td->o.time_based)
if (exceeds_number_ios(td))
return 0;
- if (td->o.size != -1ULL && ddir_rw_sum(td->io_bytes) < td->o.size) {
+ if (td->o.io_limit)
+ limit = td->o.io_limit;
+ else
+ limit = td->o.size;
+
+ if (limit != -1ULL && ddir_rw_sum(td->io_bytes) < limit) {
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);
+ diff = limit - ddir_rw_sum(td->io_bytes);
if (diff < td_max_bs(td))
return 0;
} else
td->pid = gettid();
- /*
- * fio_time_init() may not have been called yet if running as a server
- */
- fio_time_init();
-
fio_local_clock_init(o->use_thread);
dprint(FD_PROCESS, "jobs pid=%d started\n", (int) td->pid);
#ifdef CONFIG_LIBNUMA
/* numa node setup */
if (o->numa_cpumask_set || o->numa_memmask_set) {
+ struct bitmask *mask;
int ret;
if (numa_available() < 0) {
}
if (o->numa_cpumask_set) {
- ret = numa_run_on_node_mask(o->numa_cpunodesmask);
+ mask = numa_parse_nodestring(o->numa_cpunodes);
+ ret = numa_run_on_node_mask(mask);
+ numa_free_nodemask(mask);
if (ret == -1) {
td_verror(td, errno, \
"numa_run_on_node_mask failed\n");
if (o->numa_memmask_set) {
+ mask = NULL;
+ if (o->numa_memnodes)
+ mask = numa_parse_nodestring(o->numa_memnodes);
+
switch (o->numa_mem_mode) {
case MPOL_INTERLEAVE:
- numa_set_interleave_mask(o->numa_memnodesmask);
+ numa_set_interleave_mask(mask);
break;
case MPOL_BIND:
- numa_set_membind(o->numa_memnodesmask);
+ numa_set_membind(mask);
break;
case MPOL_LOCAL:
numa_set_localalloc();
break;
}
+ if (mask)
+ numa_free_nodemask(mask);
+
}
}
#endif
goto err;
}
+ if (td->flags & TD_F_COMPRESS_LOG)
+ tp_init(&td->tp_data);
+
fio_verify_init(td);
fio_gettime(&td->epoch, NULL);
fio_unpin_memory(td);
- fio_mutex_down(writeout_mutex);
- finalize_logs(td);
- if (td->bw_log) {
- if (o->bw_log_file) {
- finish_log_named(td, td->bw_log,
- o->bw_log_file, "bw");
- } else
- finish_log(td, td->bw_log, "bw");
- }
- if (td->lat_log) {
- if (o->lat_log_file) {
- finish_log_named(td, td->lat_log,
- o->lat_log_file, "lat");
- } else
- finish_log(td, td->lat_log, "lat");
- }
- if (td->slat_log) {
- if (o->lat_log_file) {
- finish_log_named(td, td->slat_log,
- o->lat_log_file, "slat");
- } else
- finish_log(td, td->slat_log, "slat");
- }
- if (td->clat_log) {
- if (o->lat_log_file) {
- finish_log_named(td, td->clat_log,
- o->lat_log_file, "clat");
- } else
- finish_log(td, td->clat_log, "clat");
- }
- if (td->iops_log) {
- if (o->iops_log_file) {
- finish_log_named(td, td->iops_log,
- o->iops_log_file, "iops");
- } else
- finish_log(td, td->iops_log, "iops");
- }
+ fio_writeout_logs(td);
+
+ if (td->flags & TD_F_COMPRESS_LOG)
+ tp_exit(&td->tp_data);
- fio_mutex_up(writeout_mutex);
if (o->exec_postrun)
exec_string(o, o->exec_postrun, (const char *)"postrun");
if (o->write_iolog_file)
write_iolog_close(td);
- fio_mutex_remove(td->rusage_sem);
- td->rusage_sem = NULL;
-
fio_mutex_remove(td->mutex);
td->mutex = NULL;
td_set_runstate(td, TD_EXITED);
+
+ /*
+ * Do this last after setting our runstate to exited, so we
+ * know that the stat thread is signaled.
+ */
+ check_update_rusage(td);
+
return (void *) (uintptr_t) td->error;
}
struct thread_data *td;
void *data, *ret;
-#ifndef __hpux
+#if !defined(__hpux) && !defined(CONFIG_NO_SHM)
data = shmat(shmid, NULL, 0);
if (data == (void *) -1) {
int __err = errno;
return (int) (uintptr_t) ret;
}
+static void dump_td_info(struct thread_data *td)
+{
+ log_err("fio: job '%s' hasn't exited in %lu seconds, it appears to "
+ "be stuck. Doing forceful exit of this job.\n", td->o.name,
+ (unsigned long) time_since_now(&td->terminate_time));
+}
+
/*
* Run over the job map and reap the threads that have exited, if any.
*/
}
}
+ /*
+ * If the job is stuck, do a forceful timeout of it and
+ * move on.
+ */
+ if (td->terminate &&
+ time_since_now(&td->terminate_time) >= FIO_REAP_TIMEOUT) {
+ dump_td_info(td);
+ td_set_runstate(td, TD_REAPED);
+ goto reaped;
+ }
+
/*
* thread is not dead, continue
*/
nr_process > 1 ? "es" : "");
}
log_info("\n");
- fflush(stdout);
+ log_info_flush();
}
todo = thread_number;
update_io_ticks();
}
-void wait_for_disk_thread_exit(void)
+static void wait_for_helper_thread_exit(void)
{
- fio_mutex_down(disk_thread_mutex);
+ void *ret;
+
+ helper_exit = 1;
+ pthread_cond_signal(&helper_cond);
+ pthread_join(helper_thread, &ret);
}
static void free_disk_util(void)
{
- disk_util_start_exit();
- wait_for_disk_thread_exit();
disk_util_prune_entries();
+
+ pthread_cond_destroy(&helper_cond);
}
-static void *disk_thread_main(void *data)
+static void *helper_thread_main(void *data)
{
int ret = 0;
fio_mutex_up(startup_mutex);
- while (threads && !ret) {
- usleep(DISK_UTIL_MSEC * 1000);
- if (!threads)
- break;
+ while (!ret) {
+ uint64_t sec = DISK_UTIL_MSEC / 1000;
+ uint64_t nsec = (DISK_UTIL_MSEC % 1000) * 1000000;
+ struct timespec ts;
+ struct timeval tv;
+
+ gettimeofday(&tv, NULL);
+ ts.tv_sec = tv.tv_sec + sec;
+ ts.tv_nsec = (tv.tv_usec * 1000) + nsec;
+ if (ts.tv_nsec > 1000000000ULL) {
+ ts.tv_nsec -= 1000000000ULL;
+ ts.tv_sec++;
+ }
+
+ pthread_cond_timedwait(&helper_cond, &helper_lock, &ts);
+
ret = update_io_ticks();
+ if (helper_do_stat) {
+ helper_do_stat = 0;
+ __show_running_run_stats();
+ }
+
if (!is_backend)
print_thread_status();
}
- fio_mutex_up(disk_thread_mutex);
return NULL;
}
-static int create_disk_util_thread(void)
+static int create_helper_thread(void)
{
int ret;
setup_disk_util();
- disk_thread_mutex = fio_mutex_init(FIO_MUTEX_LOCKED);
+ pthread_cond_init(&helper_cond, NULL);
+ pthread_mutex_init(&helper_lock, NULL);
- ret = pthread_create(&disk_util_thread, NULL, disk_thread_main, NULL);
+ ret = pthread_create(&helper_thread, NULL, helper_thread_main, NULL);
if (ret) {
- fio_mutex_remove(disk_thread_mutex);
- log_err("Can't create disk util thread: %s\n", strerror(ret));
- return 1;
- }
-
- ret = pthread_detach(disk_util_thread);
- if (ret) {
- fio_mutex_remove(disk_thread_mutex);
- log_err("Can't detatch disk util thread: %s\n", strerror(ret));
+ log_err("Can't create helper thread: %s\n", strerror(ret));
return 1;
}
return 0;
if (write_bw_log) {
- 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);
+ struct log_params p = {
+ .log_type = IO_LOG_TYPE_BW,
+ };
+
+ setup_log(&agg_io_log[DDIR_READ], &p, "agg-read_bw.log");
+ setup_log(&agg_io_log[DDIR_WRITE], &p, "agg-write_bw.log");
+ setup_log(&agg_io_log[DDIR_TRIM], &p, "agg-trim_bw.log");
}
startup_mutex = fio_mutex_init(FIO_MUTEX_LOCKED);
if (startup_mutex == NULL)
return 1;
- writeout_mutex = fio_mutex_init(FIO_MUTEX_UNLOCKED);
- if (writeout_mutex == NULL)
- return 1;
set_genesis_time();
stat_init();
- create_disk_util_thread();
+ create_helper_thread();
cgroup_list = smalloc(sizeof(*cgroup_list));
INIT_FLIST_HEAD(cgroup_list);
run_threads();
+ wait_for_helper_thread_exit();
+
if (!fio_abort) {
- show_run_stats();
+ __show_run_stats();
if (write_bw_log) {
- __finish_log(agg_io_log[DDIR_READ], "agg-read_bw.log");
- __finish_log(agg_io_log[DDIR_WRITE],
- "agg-write_bw.log");
- __finish_log(agg_io_log[DDIR_TRIM],
- "agg-write_bw.log");
+ int i;
+
+ for (i = 0; i < DDIR_RWDIR_CNT; i++) {
+ struct io_log *log = agg_io_log[i];
+
+ flush_log(log);
+ free_log(log);
+ }
}
}
- for_each_td(td, i)
+ for_each_td(td, i) {
fio_options_free(td);
+ fio_mutex_remove(td->rusage_sem);
+ td->rusage_sem = NULL;
+ }
free_disk_util();
cgroup_kill(cgroup_list);
sfree(cgroup_mnt);
fio_mutex_remove(startup_mutex);
- fio_mutex_remove(writeout_mutex);
- fio_mutex_remove(disk_thread_mutex);
stat_exit();
return exit_value;
}