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