Fix a potential deadlock in helper_do_stat()
[fio.git] / helper_thread.c
CommitLineData
4f37732a
BVA
1#ifdef CONFIG_VALGRIND_DEV
2#include <valgrind/drd.h>
3#else
4#define DRD_IGNORE_VAR(x) do { } while (0)
5#endif
6
a39fb9ea
JA
7#include "fio.h"
8#include "smalloc.h"
9#include "helper_thread.h"
16e56d25 10#include "steadystate.h"
ae626d4e 11#include "pshared.h"
a39fb9ea 12
31eca641
BVA
13enum action {
14 A_EXIT = 1,
15 A_RESET = 2,
16 A_DO_STAT = 3,
17};
18
a39fb9ea
JA
19static struct helper_data {
20 volatile int exit;
31eca641 21 int pipe[2]; /* 0: read end; 1: write end. */
a39fb9ea
JA
22 struct sk_out *sk_out;
23 pthread_t thread;
971caeb1 24 struct fio_sem *startup_sem;
a39fb9ea
JA
25} *helper_data;
26
27void helper_thread_destroy(void)
28{
31eca641
BVA
29 close(helper_data->pipe[0]);
30 close(helper_data->pipe[1]);
a39fb9ea
JA
31 sfree(helper_data);
32}
33
31eca641 34static void submit_action(enum action a)
a39fb9ea 35{
31eca641
BVA
36 const uint8_t data = a;
37 int ret;
38
a39fb9ea
JA
39 if (!helper_data)
40 return;
41
31eca641
BVA
42 ret = write(helper_data->pipe[1], &data, sizeof(data));
43 assert(ret == 1);
44}
a39fb9ea 45
31eca641
BVA
46void helper_reset(void)
47{
48 submit_action(A_RESET);
a39fb9ea
JA
49}
50
31eca641
BVA
51/*
52 * May be invoked in signal handler context and hence must only call functions
53 * that are async-signal-safe. See also
54 * https://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_04_03.
55 */
a39fb9ea
JA
56void helper_do_stat(void)
57{
31eca641 58 submit_action(A_DO_STAT);
a39fb9ea
JA
59}
60
61bool helper_should_exit(void)
62{
63 if (!helper_data)
64 return true;
65
66 return helper_data->exit;
67}
68
69void helper_thread_exit(void)
70{
a39fb9ea 71 helper_data->exit = 1;
31eca641
BVA
72 submit_action(A_EXIT);
73 pthread_join(helper_data->thread, NULL);
a39fb9ea
JA
74}
75
76static void *helper_thread_main(void *data)
77{
78 struct helper_data *hd = data;
16e56d25 79 unsigned int msec_to_next_event, next_log, next_ss = STEADYSTATE_MSEC;
8b6a404c 80 struct timespec ts, last_du, last_ss;
31eca641 81 uint8_t action;
a39fb9ea
JA
82 int ret = 0;
83
84 sk_out_assign(hd->sk_out);
85
78b66d32
BVA
86#ifdef CONFIG_PTHREAD_CONDATTR_SETCLOCK
87 clock_gettime(CLOCK_MONOTONIC, &ts);
88#else
89 clock_gettime(CLOCK_REALTIME, &ts);
90#endif
8b6a404c
VF
91 memcpy(&last_du, &ts, sizeof(ts));
92 memcpy(&last_ss, &ts, sizeof(ts));
a39fb9ea 93
971caeb1 94 fio_sem_up(hd->startup_sem);
a39fb9ea
JA
95
96 msec_to_next_event = DISK_UTIL_MSEC;
97 while (!ret && !hd->exit) {
16e56d25 98 uint64_t since_du, since_ss = 0;
31eca641
BVA
99 struct timeval timeout = {
100 .tv_sec = DISK_UTIL_MSEC / 1000,
101 .tv_usec = (DISK_UTIL_MSEC % 1000) * 1000,
102 };
103 fd_set rfds, efds;
a39fb9ea 104
8b6a404c 105 timespec_add_msec(&ts, msec_to_next_event);
a39fb9ea 106
31eca641
BVA
107 if (read(hd->pipe[0], &action, sizeof(action)) < 0) {
108 FD_ZERO(&rfds);
109 FD_SET(hd->pipe[0], &rfds);
110 FD_ZERO(&efds);
111 FD_SET(hd->pipe[0], &efds);
112 select(1, &rfds, NULL, &efds, &timeout);
113 if (read(hd->pipe[0], &action, sizeof(action)) < 0)
114 action = 0;
115 }
a39fb9ea 116
78b66d32
BVA
117#ifdef CONFIG_PTHREAD_CONDATTR_SETCLOCK
118 clock_gettime(CLOCK_MONOTONIC, &ts);
119#else
120 clock_gettime(CLOCK_REALTIME, &ts);
121#endif
a39fb9ea 122
31eca641
BVA
123 if (action == A_RESET) {
124 last_du = ts;
125 last_ss = ts;
a39fb9ea
JA
126 }
127
8b6a404c 128 since_du = mtime_since(&last_du, &ts);
a39fb9ea
JA
129 if (since_du >= DISK_UTIL_MSEC || DISK_UTIL_MSEC - since_du < 10) {
130 ret = update_io_ticks();
8b6a404c 131 timespec_add_msec(&last_du, DISK_UTIL_MSEC);
a39fb9ea
JA
132 msec_to_next_event = DISK_UTIL_MSEC;
133 if (since_du >= DISK_UTIL_MSEC)
134 msec_to_next_event -= (since_du - DISK_UTIL_MSEC);
dd290fb4
VF
135 } else
136 msec_to_next_event = DISK_UTIL_MSEC - since_du;
a39fb9ea 137
31eca641 138 if (action == A_DO_STAT)
a39fb9ea 139 __show_running_run_stats();
a39fb9ea
JA
140
141 next_log = calc_log_samples();
142 if (!next_log)
143 next_log = DISK_UTIL_MSEC;
144
84784e07 145 if (steadystate_enabled) {
8b6a404c 146 since_ss = mtime_since(&last_ss, &ts);
16e56d25
VF
147 if (since_ss >= STEADYSTATE_MSEC || STEADYSTATE_MSEC - since_ss < 10) {
148 steadystate_check();
8b6a404c 149 timespec_add_msec(&last_ss, since_ss);
16e56d25
VF
150 if (since_ss > STEADYSTATE_MSEC)
151 next_ss = STEADYSTATE_MSEC - (since_ss - STEADYSTATE_MSEC);
152 else
153 next_ss = STEADYSTATE_MSEC;
c27cc65f 154 } else
16e56d25
VF
155 next_ss = STEADYSTATE_MSEC - since_ss;
156 }
157
158 msec_to_next_event = min(min(next_log, msec_to_next_event), next_ss);
e569ca6b 159 dprint(FD_HELPERTHREAD, "since_ss: %llu, next_ss: %u, next_log: %u, msec_to_next_event: %u\n", (unsigned long long)since_ss, next_ss, next_log, msec_to_next_event);
a39fb9ea
JA
160
161 if (!is_backend)
162 print_thread_status();
163 }
164
165 fio_writeout_logs(false);
166
167 sk_out_drop();
168 return NULL;
169}
170
971caeb1 171int helper_thread_create(struct fio_sem *startup_sem, struct sk_out *sk_out)
a39fb9ea
JA
172{
173 struct helper_data *hd;
174 int ret;
175
b3090ff4 176 hd = scalloc(1, sizeof(*hd));
a39fb9ea
JA
177
178 setup_disk_util();
16e56d25 179 steadystate_setup();
a39fb9ea
JA
180
181 hd->sk_out = sk_out;
34febb23 182
31eca641
BVA
183#ifdef __linux__
184 ret = pipe2(hd->pipe, O_CLOEXEC);
185#else
186 ret = pipe(hd->pipe);
187#endif
34febb23 188 if (ret)
f9e5b5ee 189 return 1;
34febb23 190
31eca641
BVA
191 ret = fcntl(hd->pipe[0], F_SETFL, O_NONBLOCK);
192 assert(ret >= 0);
193
971caeb1 194 hd->startup_sem = startup_sem;
a39fb9ea 195
4f37732a
BVA
196 DRD_IGNORE_VAR(helper_data);
197
a39fb9ea
JA
198 ret = pthread_create(&hd->thread, NULL, helper_thread_main, hd);
199 if (ret) {
200 log_err("Can't create helper thread: %s\n", strerror(ret));
201 return 1;
202 }
203
204 helper_data = hd;
205
971caeb1
BVA
206 dprint(FD_MUTEX, "wait on startup_sem\n");
207 fio_sem_down(startup_sem);
208 dprint(FD_MUTEX, "done waiting on startup_sem\n");
a39fb9ea
JA
209 return 0;
210}