Speed up md5 hash filling/verifying by 20%
[fio.git] / blktrace.c
1 /*
2  * blktrace support code for fio
3  */
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <sys/stat.h>
7 #include <dirent.h>
8
9 #include "list.h"
10 #include "fio.h"
11 #include "blktrace_api.h"
12
13 #define TRACE_FIFO_SIZE 65536
14
15 /*
16  * fifo refill frontend, to avoid reading data in trace sized bites
17  */
18 static int refill_fifo(struct thread_data *td, struct fifo *fifo, int fd)
19 {
20         char buf[TRACE_FIFO_SIZE];
21         unsigned int total;
22         int ret;
23
24         total = sizeof(buf);
25         if (total > fifo_room(fifo))
26                 total = fifo_room(fifo);
27
28         ret = read(fd, buf, total);
29         if (ret < 0) {
30                 td_verror(td, errno, "read blktrace file");
31                 return -1;
32         }
33
34         if (ret > 0)
35                 ret = fifo_put(fifo, buf, ret);
36
37         return ret;
38 }
39
40 /*
41  * Retrieve 'len' bytes from the fifo, refilling if necessary.
42  */
43 static int trace_fifo_get(struct thread_data *td, struct fifo *fifo, int fd,
44                           void *buf, unsigned int len)
45 {
46         if (fifo_len(fifo) < len) {
47                 int ret = refill_fifo(td, fifo, fd);
48
49                 if (ret < 0)
50                         return ret;
51         }
52
53         return fifo_get(fifo, buf, len);
54 }
55
56 /*
57  * Just discard the pdu by seeking past it.
58  */
59 static int discard_pdu(struct thread_data *td, struct fifo *fifo, int fd,
60                        struct blk_io_trace *t)
61 {
62         if (t->pdu_len == 0)
63                 return 0;
64
65         return trace_fifo_get(td, fifo, fd, NULL, t->pdu_len);
66 }
67
68 /*
69  * Check if this is a blktrace binary data file. We read a single trace
70  * into memory and check for the magic signature.
71  */
72 int is_blktrace(const char *filename)
73 {
74         struct blk_io_trace t;
75         int fd, ret;
76
77         fd = open(filename, O_RDONLY);
78         if (fd < 0)
79                 return 0;
80
81         ret = read(fd, &t, sizeof(t));
82         close(fd);
83
84         if (ret < 0) {
85                 perror("read blktrace");
86                 return 0;
87         } else if (ret != sizeof(t)) {
88                 log_err("fio: short read on blktrace file\n");
89                 return 0;
90         }
91
92         if ((t.magic & 0xffffff00) == BLK_IO_TRACE_MAGIC)
93                 return 1;
94
95         return 0;
96 }
97
98 static int lookup_device(char *path, unsigned int maj, unsigned int min)
99 {
100         struct dirent *dir;
101         struct stat st;
102         int found = 0;
103         DIR *D;
104
105         D = opendir(path);
106         if (!D)
107                 return 0;
108
109         while ((dir = readdir(D)) != NULL) {
110                 char full_path[256];
111
112                 if (!strcmp(dir->d_name, ".") || !strcmp(dir->d_name, ".."))
113                         continue;
114
115                 sprintf(full_path, "%s/%s", path, dir->d_name);
116                 if (lstat(full_path, &st) == -1) {
117                         perror("lstat");
118                         break;
119                 }
120
121                 if (S_ISDIR(st.st_mode)) {
122                         found = lookup_device(full_path, maj, min);
123                         if (found) {
124                                 strcpy(path, full_path);
125                                 break;
126                         }
127                 }
128
129                 if (!S_ISBLK(st.st_mode))
130                         continue;
131
132                 if (maj == major(st.st_rdev) && min == minor(st.st_rdev)) {
133                         strcpy(path, full_path);
134                         found = 1;
135                         break;
136                 }
137         }
138
139         closedir(D);
140         return found;
141 }
142
143 #define FMINORBITS      20
144 #define FMINORMASK      ((1U << FMINORBITS) - 1)
145 #define FMAJOR(dev)     ((unsigned int) ((dev) >> FMINORBITS))
146 #define FMINOR(dev)     ((unsigned int) ((dev) & FMINORMASK))
147
148 static void trace_add_file(struct thread_data *td, __u32 device)
149 {
150         static unsigned int last_maj, last_min;
151         unsigned int maj = FMAJOR(device);
152         unsigned int min = FMINOR(device);
153         struct fio_file *f;
154         char dev[256];
155         unsigned int i;
156
157         if (last_maj == maj && last_min == min)
158                 return;
159
160         last_maj = maj;
161         last_min = min;
162
163         /*
164          * check for this file in our list
165          */
166         for_each_file(td, f, i)
167                 if (f->major == maj && f->minor == min)
168                         return;
169
170         strcpy(dev, "/dev");
171         if (lookup_device(dev, maj, min))
172                 add_file(td, dev);
173 }
174
175 /*
176  * Store blk_io_trace data in an ipo for later retrieval.
177  */
178 static void store_ipo(struct thread_data *td, unsigned long long offset,
179                       unsigned int bytes, int rw, unsigned long long ttime)
180 {
181         struct io_piece *ipo = malloc(sizeof(*ipo));
182
183         memset(ipo, 0, sizeof(*ipo));
184         INIT_LIST_HEAD(&ipo->list);
185         /*
186          * the 512 is wrong here, it should be the hardware sector size...
187          */
188         ipo->offset = offset * 512;
189         ipo->len = bytes;
190         ipo->delay = ttime / 1000;
191         if (rw)
192                 ipo->ddir = DDIR_WRITE;
193         else
194                 ipo->ddir = DDIR_READ;
195
196         list_add_tail(&ipo->list, &td->io_log_list);
197 }
198
199 /*
200  * We only care for queue traces, most of the others are side effects
201  * due to internal workings of the block layer.
202  */
203 static void handle_trace(struct thread_data *td, struct blk_io_trace *t,
204                          unsigned long long ttime, unsigned long *ios,
205                          unsigned int *bs)
206 {
207         int rw;
208
209         if ((t->action & 0xffff) != __BLK_TA_QUEUE)
210                 return;
211         if (t->action & BLK_TC_ACT(BLK_TC_PC))
212                 return;
213         if (t->action & BLK_TC_ACT(BLK_TC_NOTIFY))
214                 return;
215
216         trace_add_file(td, t->device);
217
218         rw = (t->action & BLK_TC_ACT(BLK_TC_WRITE)) != 0;
219
220         if (t->bytes > bs[rw])
221                 bs[rw] = t->bytes;
222
223         ios[rw]++;
224         td->o.size += t->bytes;
225         store_ipo(td, t->sector, t->bytes, rw, ttime);
226 }
227
228 /*
229  * Load a blktrace file by reading all the blk_io_trace entries, and storing
230  * them as io_pieces like the fio text version would do.
231  */
232 int load_blktrace(struct thread_data *td, const char *filename)
233 {
234         unsigned long long ttime, delay;
235         struct blk_io_trace t;
236         unsigned long ios[2];
237         unsigned int cpu;
238         unsigned int rw_bs[2];
239         struct fifo *fifo;
240         int fd;
241
242         fd = open(filename, O_RDONLY);
243         if (fd < 0) {
244                 td_verror(td, errno, "open blktrace file");
245                 return 1;
246         }
247
248         fifo = fifo_alloc(TRACE_FIFO_SIZE);
249
250         td->o.size = 0;
251
252         cpu = 0;
253         ttime = 0;
254         ios[0] = ios[1] = 0;
255         rw_bs[0] = rw_bs[1] = 0;
256         do {
257                 int ret = trace_fifo_get(td, fifo, fd, &t, sizeof(t));
258
259                 if (ret < 0)
260                         goto err;
261                 else if (!ret)
262                         break;
263                 else if (ret < (int) sizeof(t)) {
264                         log_err("fio: short fifo get\n");
265                         break;
266                 }
267
268                 if ((t.magic & 0xffffff00) != BLK_IO_TRACE_MAGIC) {
269                         log_err("fio: bad magic in blktrace data: %x\n", t.magic);
270                         goto err;
271                 }
272                 if ((t.magic & 0xff) != BLK_IO_TRACE_VERSION) {
273                         log_err("fio: bad blktrace version %d\n", t.magic & 0xff);
274                         goto err;
275                 }
276                 ret = discard_pdu(td, fifo, fd, &t);
277                 if (ret < 0) {
278                         td_verror(td, ret, "blktrace lseek");
279                         goto err;
280                 } else if (t.pdu_len != ret) {
281                         log_err("fio: discarded %d of %d\n", ret, t.pdu_len);
282                         goto err;
283                 }
284                 if (!ttime) {
285                         ttime = t.time;
286                         cpu = t.cpu;
287                 }
288                 delay = 0;
289                 if (cpu == t.cpu)
290                         delay = t.time - ttime;
291                 handle_trace(td, &t, delay, ios, rw_bs);
292                 ttime = t.time;
293                 cpu = t.cpu;
294         } while (1);
295
296         fifo_free(fifo);
297         close(fd);
298
299         if (!ios[DDIR_READ] && !ios[DDIR_WRITE]) {
300                 log_err("fio: found no ios in blktrace data\n");
301                 return 1;
302         } else if (ios[DDIR_READ] && !ios[DDIR_READ]) {
303                 td->o.td_ddir = TD_DDIR_READ;
304                 td->o.max_bs[DDIR_READ] = rw_bs[DDIR_READ];
305         } else if (!ios[DDIR_READ] && ios[DDIR_WRITE]) {
306                 td->o.td_ddir = TD_DDIR_WRITE;
307                 td->o.max_bs[DDIR_WRITE] = rw_bs[DDIR_WRITE];
308         } else {
309                 td->o.td_ddir = TD_DDIR_RW;
310                 td->o.max_bs[DDIR_READ] = rw_bs[DDIR_READ];
311                 td->o.max_bs[DDIR_WRITE] = rw_bs[DDIR_WRITE];
312         }
313
314         /*
315          * We need to do direct/raw ios to the device, to avoid getting
316          * read-ahead in our way.
317          */
318         td->o.odirect = 1;
319
320         return 0;
321 err:
322         close(fd);
323         fifo_free(fifo);
324         return 1;
325 }