Initial commit for TRIM/DISCARD support
[fio.git] / gettime.c
CommitLineData
02bcaa8c 1/*
f5cc024a 2 * Clock functions
02bcaa8c 3 */
f5cc024a 4
02bcaa8c 5#include <unistd.h>
c223da83 6#include <math.h>
02bcaa8c
JA
7#include <sys/time.h>
8
9#include "fio.h"
be4ecfdf 10#include "smalloc.h"
02bcaa8c
JA
11
12#include "hash.h"
13
c223da83 14static unsigned long cycles_per_usec;
3e488920 15static struct timeval last_tv;
c223da83 16static unsigned long last_cycles;
3e488920 17static int last_tv_valid;
02bcaa8c 18
be4ecfdf
JA
19static struct timeval *fio_tv;
20int fio_gtod_offload = 0;
21int fio_gtod_cpu = -1;
22
c223da83
JA
23enum fio_cs fio_clock_source = CS_GTOD;
24
02bcaa8c
JA
25#ifdef FIO_DEBUG_TIME
26
27#define HASH_BITS 8
28#define HASH_SIZE (1 << HASH_BITS)
29
01743ee1 30static struct flist_head hash[HASH_SIZE];
02bcaa8c
JA
31static int gtod_inited;
32
33struct gtod_log {
01743ee1 34 struct flist_head list;
02bcaa8c
JA
35 void *caller;
36 unsigned long calls;
37};
38
39static struct gtod_log *find_hash(void *caller)
40{
41 unsigned long h = hash_ptr(caller, HASH_BITS);
01743ee1 42 struct flist_head *entry;
02bcaa8c 43
01743ee1
JA
44 flist_for_each(entry, &hash[h]) {
45 struct gtod_log *log = flist_entry(entry, struct gtod_log,
46 list);
02bcaa8c
JA
47
48 if (log->caller == caller)
49 return log;
50 }
51
52 return NULL;
53}
54
55static struct gtod_log *find_log(void *caller)
56{
57 struct gtod_log *log = find_hash(caller);
58
59 if (!log) {
60 unsigned long h;
61
62 log = malloc(sizeof(*log));
01743ee1 63 INIT_FLIST_HEAD(&log->list);
02bcaa8c
JA
64 log->caller = caller;
65 log->calls = 0;
66
67 h = hash_ptr(caller, HASH_BITS);
01743ee1 68 flist_add_tail(&log->list, &hash[h]);
02bcaa8c
JA
69 }
70
71 return log;
72}
73
74static void gtod_log_caller(void *caller)
75{
76 if (gtod_inited) {
77 struct gtod_log *log = find_log(caller);
78
79 log->calls++;
80 }
81}
82
83static void fio_exit fio_dump_gtod(void)
84{
85 unsigned long total_calls = 0;
86 int i;
87
88 for (i = 0; i < HASH_SIZE; i++) {
01743ee1 89 struct flist_head *entry;
02bcaa8c
JA
90 struct gtod_log *log;
91
01743ee1
JA
92 flist_for_each(entry, &hash[i]) {
93 log = flist_entry(entry, struct gtod_log, list);
02bcaa8c 94
5ec10eaa
JA
95 printf("function %p, calls %lu\n", log->caller,
96 log->calls);
02bcaa8c
JA
97 total_calls += log->calls;
98 }
99 }
100
101 printf("Total %lu gettimeofday\n", total_calls);
102}
103
104static void fio_init gtod_init(void)
105{
106 int i;
107
108 for (i = 0; i < HASH_SIZE; i++)
01743ee1 109 INIT_FLIST_HEAD(&hash[i]);
02bcaa8c
JA
110
111 gtod_inited = 1;
112}
113
114#endif /* FIO_DEBUG_TIME */
115
1e97cce9 116#ifdef FIO_DEBUG_TIME
02bcaa8c 117void fio_gettime(struct timeval *tp, void *caller)
1e97cce9
JA
118#else
119void fio_gettime(struct timeval *tp, void fio_unused *caller)
120#endif
02bcaa8c
JA
121{
122#ifdef FIO_DEBUG_TIME
123 if (!caller)
124 caller = __builtin_return_address(0);
125
126 gtod_log_caller(caller);
02bcaa8c 127#endif
be4ecfdf
JA
128 if (fio_tv) {
129 memcpy(tp, fio_tv, sizeof(*tp));
130 return;
c223da83
JA
131 }
132
133 switch (fio_clock_source) {
134 case CS_GTOD:
02bcaa8c 135 gettimeofday(tp, NULL);
c223da83
JA
136 break;
137 case CS_CGETTIME: {
02bcaa8c
JA
138 struct timespec ts;
139
d481e006 140 if (clock_gettime(CLOCK_REALTIME, &ts) < 0) {
c223da83
JA
141 log_err("fio: clock_gettime fails\n");
142 assert(0);
02bcaa8c
JA
143 }
144
145 tp->tv_sec = ts.tv_sec;
146 tp->tv_usec = ts.tv_nsec / 1000;
c223da83
JA
147 break;
148 }
149#ifdef ARCH_HAVE_CPU_CLOCK
150 case CS_CPUCLOCK: {
151 unsigned long long usecs, t;
152
153 t = get_cpu_clock();
154 if (t < last_cycles) {
155 dprint(FD_TIME, "CPU clock going back in time\n");
156 t = last_cycles;
157 }
158
159 usecs = t / cycles_per_usec;
160 tp->tv_sec = usecs / 1000000;
161 tp->tv_usec = usecs % 1000000;
162 last_cycles = t;
163 break;
164 }
165#endif
166 default:
167 log_err("fio: invalid clock source %d\n", fio_clock_source);
168 break;
02bcaa8c 169 }
3e488920
JA
170
171 /*
172 * If Linux is using the tsc clock on non-synced processors,
173 * sometimes time can appear to drift backwards. Fix that up.
174 */
175 if (last_tv_valid) {
176 if (tp->tv_sec < last_tv.tv_sec)
177 tp->tv_sec = last_tv.tv_sec;
178 else if (last_tv.tv_sec == tp->tv_sec &&
179 tp->tv_usec < last_tv.tv_usec)
180 tp->tv_usec = last_tv.tv_usec;
181 }
182 last_tv_valid = 1;
183 memcpy(&last_tv, tp, sizeof(*tp));
02bcaa8c 184}
be4ecfdf 185
c223da83
JA
186static unsigned long get_cycles_per_usec(void)
187{
188 struct timeval s, e;
189 unsigned long long c_s, c_e;
190
191 gettimeofday(&s, NULL);
192 c_s = get_cpu_clock();
193 do {
194 unsigned long long elapsed;
195
196 gettimeofday(&e, NULL);
197 elapsed = utime_since(&s, &e);
198 if (elapsed >= 10) {
199 c_e = get_cpu_clock();
200 break;
201 }
202 } while (1);
203
204 return c_e - c_s;
205}
206
207void fio_clock_init(void)
208{
209 double delta, mean, S;
210 unsigned long avg, cycles[10];
211 int i, samples;
212
213 last_tv_valid = 0;
214
215 cycles[0] = get_cycles_per_usec();
216 S = delta = mean = 0.0;
217 for (i = 0; i < 10; i++) {
218 cycles[i] = get_cycles_per_usec();
219 delta = cycles[i] - mean;
220 if (delta) {
221 mean += delta / (i + 1.0);
222 S += delta * (cycles[i] - mean);
223 }
224 }
225
226 S = sqrt(S / (10 - 1.0));
227
228 samples = avg = 0;
229 for (i = 0; i < 10; i++) {
230 double this = cycles[i];
231
232 if ((max(this, mean) - min(this, mean)) > S)
233 continue;
234 samples++;
235 avg += this;
236 }
237
238 S /= 10.0;
239 mean /= 10.0;
240
241 for (i = 0; i < 10; i++)
242 dprint(FD_TIME, "cycles[%d]=%lu\n", i, cycles[i] / 10);
243
244 avg /= (samples * 10);
245 dprint(FD_TIME, "avg: %lu\n", avg);
246 dprint(FD_TIME, "mean=%f, S=%f\n", mean, S);
247
248 cycles_per_usec = avg;
249}
250
be4ecfdf
JA
251void fio_gtod_init(void)
252{
253 fio_tv = smalloc(sizeof(struct timeval));
254 assert(fio_tv);
255}
256
257void fio_gtod_update(void)
258{
259 gettimeofday(fio_tv, NULL);
260}