The signal handler safety changes to the helper thread have resulted in
inconsistent status-interval intervals. Consider the following:
$ ./fio-canonical/fio --name=test --rw=randwrite --ioengine=libaio --direct=1 --runtime=180 --time_based --filename=/dev/fioa --output=write-canonical.out --minimal --status-interval=1
$ cut -d ';' -f 50 < write-canonical.out | awk 'NR>1{print $1-p} {p=$1}' | sort -n | tail
1002
1002
1002
1002
1002
1042
1046
1251
1252
1252
Several of the status-interval output lines are ~1250ms apart.
This patch moves code for triggering the status-interval output from the main
fio process to the helper thread. The resulting intervals are much closer to
the desired 1000ms.
$ ./fio/fio --name=test --rw=randwrite --ioengine=libaio --direct=1 --runtime=180 --time_based --filename=/dev/fioa --minimal --status-interval=1 --output=write-test.out
$ cut -d ';' -f 50 < write-test.out | awk 'NR>1{print $1-p} {p=$1}' | sort -n | tail
1001
1001
1001
1001
1001
1001
1001
1001
1001
1001
Reported-by: <nate.rivers@wdc.com>
Fixes:
31eca641ad91 ("Fix a potential deadlock in helper_do_stat()")
Signed-off-by: Vincent Fu <vincent.fu@wdc.com>
static void *helper_thread_main(void *data)
{
struct helper_data *hd = data;
static void *helper_thread_main(void *data)
{
struct helper_data *hd = data;
- unsigned int msec_to_next_event, next_log, next_ss = STEADYSTATE_MSEC;
- struct timespec ts, last_du, last_ss;
+ unsigned int msec_to_next_event, next_log, next_si;
+ unsigned int next_ss = STEADYSTATE_MSEC;
+ struct timespec ts, last_du, last_ss, last_si;
char action;
int ret = 0;
char action;
int ret = 0;
#endif
memcpy(&last_du, &ts, sizeof(ts));
memcpy(&last_ss, &ts, sizeof(ts));
#endif
memcpy(&last_du, &ts, sizeof(ts));
memcpy(&last_ss, &ts, sizeof(ts));
+ memcpy(&last_si, &ts, sizeof(ts));
fio_sem_up(hd->startup_sem);
msec_to_next_event = DISK_UTIL_MSEC;
while (!ret && !hd->exit) {
fio_sem_up(hd->startup_sem);
msec_to_next_event = DISK_UTIL_MSEC;
while (!ret && !hd->exit) {
- uint64_t since_du, since_ss = 0;
+ uint64_t since_du, since_si, since_ss = 0;
struct timeval timeout = {
.tv_sec = msec_to_next_event / 1000,
.tv_usec = (msec_to_next_event % 1000) * 1000,
struct timeval timeout = {
.tv_sec = msec_to_next_event / 1000,
.tv_usec = (msec_to_next_event % 1000) * 1000,
if (action == A_DO_STAT)
__show_running_run_stats();
if (action == A_DO_STAT)
__show_running_run_stats();
+ if (status_interval) {
+ since_si = mtime_since(&last_si, &ts);
+ if (since_si >= status_interval || status_interval - since_si < 10) {
+ __show_running_run_stats();
+ timespec_add_msec(&last_si, since_si);
+ if (since_si > status_interval)
+ next_si = status_interval - (since_si - status_interval);
+ else
+ next_si = status_interval;
+ } else
+ next_si = status_interval - since_si;
+ msec_to_next_event = min(next_si, msec_to_next_event);
+ }
+
next_log = calc_log_samples();
if (!next_log)
next_log = DISK_UTIL_MSEC;
next_log = calc_log_samples();
if (!next_log)
next_log = DISK_UTIL_MSEC;
-static bool status_interval_init;
-static struct timespec status_time;
static bool status_file_disabled;
#define FIO_STATUS_FILE "fio-dump-status"
static bool status_file_disabled;
#define FIO_STATUS_FILE "fio-dump-status"
void check_for_running_stats(void)
{
void check_for_running_stats(void)
{
- if (status_interval) {
- if (!status_interval_init) {
- fio_gettime(&status_time, NULL);
- status_interval_init = true;
- } else if (mtime_since_now(&status_time) >= status_interval) {
- show_running_run_stats();
- fio_gettime(&status_time, NULL);
- return;
- }
- }
if (check_status_file()) {
show_running_run_stats();
return;
if (check_status_file()) {
show_running_run_stats();
return;