blktrace support: fixups/cleanups/improvements
[fio.git] / blktrace.c
1 /*
2  * blktrace support code for fio
3  */
4 #include <stdio.h>
5 #include <stdlib.h>
6
7 #include "list.h"
8 #include "fio.h"
9 #include "blktrace_api.h"
10
11 #define TRACE_FIFO_SIZE 65536
12
13 /*
14  * fifo refill frontend, to avoid reading data in trace sized bites
15  */
16 static int refill_fifo(struct thread_data *td, struct fifo *fifo, int fd)
17 {
18         char buf[TRACE_FIFO_SIZE];
19         unsigned int total;
20         int ret;
21
22         total = sizeof(buf);
23         if (total > fifo_room(fifo))
24                 total = fifo_room(fifo);
25
26         ret = read(fd, buf, total);
27         if (ret < 0) {
28                 td_verror(td, errno, "read blktrace file");
29                 return -1;
30         }
31
32         if (ret > 0)
33                 ret = fifo_put(fifo, buf, ret);
34
35         return ret;
36 }
37
38 /*
39  * Retrieve 'len' bytes from the fifo, refilling if necessary.
40  */
41 static int trace_fifo_get(struct thread_data *td, struct fifo *fifo, int fd,
42                           void *buf, unsigned int len)
43 {
44         if (fifo_len(fifo) < len) {
45                 int ret = refill_fifo(td, fifo, fd);
46
47                 if (ret < 0)
48                         return ret;
49         }
50
51         return fifo_get(fifo, buf, len);
52 }
53
54 /*
55  * Just discard the pdu by seeking past it.
56  */
57 static int discard_pdu(struct thread_data *td, struct fifo *fifo, int fd,
58                        struct blk_io_trace *t)
59 {
60         if (t->pdu_len == 0)
61                 return 0;
62
63         return trace_fifo_get(td, fifo, fd, NULL, t->pdu_len);
64 }
65
66 /*
67  * Check if this is a blktrace binary data file. We read a single trace
68  * into memory and check for the magic signature.
69  */
70 int is_blktrace(const char *filename)
71 {
72         struct blk_io_trace t;
73         int fd, ret;
74
75         fd = open(filename, O_RDONLY);
76         if (fd < 0) {
77                 perror("open blktrace");
78                 return 0;
79         }
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 /*
99  * Store blk_io_trace data in an ipo for later retrieval.
100  */
101 static void store_ipo(struct thread_data *td, unsigned long long offset,
102                       unsigned int bytes, int rw, unsigned long long ttime)
103 {
104         struct io_piece *ipo = malloc(sizeof(*ipo));
105
106         memset(ipo, 0, sizeof(*ipo));
107         INIT_LIST_HEAD(&ipo->list);
108         /*
109          * the 512 is wrong here, it should be the hardware sector size...
110          */
111         ipo->offset = offset * 512;
112         ipo->len = bytes;
113         ipo->delay = ttime / 1000;
114         if (rw)
115                 ipo->ddir = DDIR_WRITE;
116         else
117                 ipo->ddir = DDIR_READ;
118
119         list_add_tail(&ipo->list, &td->io_log_list);
120 }
121
122 /*
123  * We only care for queue traces, most of the others are side effects
124  * due to internal workings of the block layer.
125  */
126 static void handle_trace(struct thread_data *td, struct blk_io_trace *t,
127                          unsigned long long ttime, unsigned long *ios,
128                          unsigned int *bs)
129 {
130         int rw;
131
132         if ((t->action & 0xffff) != __BLK_TA_QUEUE)
133                 return;
134         if (t->action & BLK_TC_ACT(BLK_TC_PC))
135                 return;
136
137         /*
138          * should not happen, need to look into that...
139          */
140         if (!t->bytes)
141                 return;
142
143         rw = (t->action & BLK_TC_ACT(BLK_TC_WRITE)) != 0;
144
145         if (t->bytes > bs[rw])
146                 bs[rw] = t->bytes;
147
148         ios[rw]++;
149         td->o.size += t->bytes;
150         store_ipo(td, t->sector, t->bytes, rw, ttime);
151 }
152
153 /*
154  * Load a blktrace file by reading all the blk_io_trace entries, and storing
155  * them as io_pieces like the fio text version would do.
156  */
157 int load_blktrace(struct thread_data *td, const char *filename)
158 {
159         unsigned long long ttime, delay;
160         struct blk_io_trace t;
161         unsigned long ios[2];
162         unsigned int cpu;
163         unsigned int rw_bs[2];
164         struct fifo *fifo;
165         int fd;
166
167         fd = open(filename, O_RDONLY);
168         if (fd < 0) {
169                 td_verror(td, errno, "open blktrace file");
170                 return 1;
171         }
172
173         fifo = fifo_alloc(TRACE_FIFO_SIZE);
174
175         td->o.size = 0;
176
177         cpu = 0;
178         ttime = 0;
179         ios[0] = ios[1] = 0;
180         rw_bs[0] = rw_bs[1] = 0;
181         do {
182                 int ret = trace_fifo_get(td, fifo, fd, &t, sizeof(t));
183
184                 if (ret < 0)
185                         goto err;
186                 else if (!ret)
187                         break;
188                 else if (ret < (int) sizeof(t)) {
189                         log_err("fio: short fifo get\n");
190                         break;
191                 }
192
193                 if ((t.magic & 0xffffff00) != BLK_IO_TRACE_MAGIC) {
194                         log_err("fio: bad magic in blktrace data: %x\n", t.magic);
195                         goto err;
196                 }
197                 if ((t.magic & 0xff) != BLK_IO_TRACE_VERSION) {
198                         log_err("fio: bad blktrace version %d\n", t.magic & 0xff);
199                         goto err;
200                 }
201                 ret = discard_pdu(td, fifo, fd, &t);
202                 if (ret < 0) {
203                         td_verror(td, ret, "blktrace lseek");
204                         goto err;
205                 } else if (t.pdu_len != ret) {
206                         log_err("fio: discarded %d of %d\n", ret, t.pdu_len);
207                         goto err;
208                 }
209                 if (!ttime) {
210                         ttime = t.time;
211                         cpu = t.cpu;
212                 }
213                 delay = 0;
214                 if (cpu == t.cpu)
215                         delay = t.time - ttime;
216                 handle_trace(td, &t, delay, ios, rw_bs);
217                 ttime = t.time;
218                 cpu = t.cpu;
219         } while (1);
220
221         fifo_free(fifo);
222         close(fd);
223
224         if (!ios[DDIR_READ] && !ios[DDIR_WRITE]) {
225                 log_err("fio: found no ios in blktrace data\n");
226                 return 1;
227         } else if (ios[DDIR_READ] && !ios[DDIR_READ]) {
228                 td->o.td_ddir = TD_DDIR_READ;
229                 td->o.max_bs[DDIR_READ] = rw_bs[DDIR_READ];
230         } else if (!ios[DDIR_READ] && ios[DDIR_WRITE]) {
231                 td->o.td_ddir = TD_DDIR_WRITE;
232                 td->o.max_bs[DDIR_WRITE] = rw_bs[DDIR_WRITE];
233         } else {
234                 td->o.td_ddir = TD_DDIR_RW;
235                 td->o.max_bs[DDIR_READ] = rw_bs[DDIR_READ];
236                 td->o.max_bs[DDIR_WRITE] = rw_bs[DDIR_WRITE];
237         }
238
239         /*
240          * We need to do direct/raw ios to the device, to avoid getting
241          * read-ahead in our way.
242          */
243         td->o.odirect = 1;
244
245         return 0;
246 err:
247         close(fd);
248         fifo_free(fifo);
249         return 1;
250 }