[PATCH] btt: seek additions
[blktrace.git] / btt / trace.c
1 /*
2  * blktrace output analysis: generate a timeline & gather statistics
3  *
4  * Copyright (C) 2006 Alan D. Brunelle <Alan.Brunelle@hp.com>
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  */
21 #include "globals.h"
22
23 static inline void release_iop(struct io *iop)
24 {
25         if (iop->pdu) free(iop->pdu);
26         IO_FREE(iop);
27 }
28
29 struct io *dip_find_exact(struct list_head *head, struct io *iop_in)
30 {
31         struct io *iop;
32         struct list_head *p;
33
34         __list_for_each(p, head) {
35                 iop = list_entry(p, struct io, dev_head);
36                 if (is_bit(iop_in, iop))
37                         return iop;
38         }
39         return NULL;
40 }
41
42 struct io *dip_find_in(struct list_head *head, struct io *iop_in)
43 {
44         struct io *iop;
45         struct list_head *p;
46
47         __list_for_each(p, head) {
48                 iop = list_entry(p, struct io, dev_head);
49                 if (in_bit(iop, iop_in))
50                         return iop;
51         }
52         return NULL;
53 }
54
55 struct io *dip_find_start(struct list_head *head, __u64 sector)
56 {
57         struct io *iop;
58         struct list_head *p;
59
60         __list_for_each(p, head) {
61                 iop = list_entry(p, struct io, dev_head);
62                 if (BIT_START(iop) == sector)
63                         return iop;
64         }
65         return NULL;
66 }
67
68 struct io *dip_find_end(struct list_head *head, __u64 sector)
69 {
70         struct io *iop;
71         struct list_head *p;
72
73         __list_for_each(p, head) {
74                 iop = list_entry(p, struct io, dev_head);
75                 if (BIT_END(iop) == sector)
76                         return iop;
77         }
78         return NULL;
79 }
80
81 struct io *dip_find_in_sec(struct list_head *head, __u64 sector)
82 {
83         struct io *iop;
84         struct list_head *p;
85
86         __list_for_each(p, head) {
87                 iop = list_entry(p, struct io, dev_head);
88                 if (BIT_START(iop) <= sector && sector <= BIT_END(iop))
89                         return iop;
90         }
91         return NULL;
92 }
93
94 struct io *dip_find_qa(struct list_head *head, struct blk_io_trace *t)
95 {
96         struct io *iop;
97         struct list_head *p;
98
99         __list_for_each(p, head) {
100                 iop = list_entry(p, struct io, dev_head);
101                 if (iop->t.cpu == t->cpu && iop->t.sequence == (t->sequence-1))
102                         return iop;
103         }
104         return NULL;
105 }
106
107 void dip_add_ms(struct list_head *head, struct io *d_iop)
108 {
109         struct io *m_iop;
110         struct list_head *p;
111         struct io_list *iolp;
112
113         __list_for_each(p, head) {
114                 m_iop = list_entry(p, struct io, dev_head);
115                 if (in_bit(m_iop, d_iop)) {
116                         iolp = malloc(sizeof(*iolp));
117                         io_link(&iolp->iop, m_iop);
118                         list_add_tail(&iolp->head, &d_iop->u.d.d_im_head);
119                 }
120         }
121 }
122
123 void dip_add_qs(struct list_head *head, struct io *i_iop)
124 {
125         struct io *q_iop;
126         struct list_head *p;
127         struct io_list *iolp;
128
129         __list_for_each(p, head) {
130                 q_iop = list_entry(p, struct io, dev_head);
131                 if (in_bit(q_iop, i_iop)) {
132                         iolp = malloc(sizeof(*iolp));
133                         io_link(&iolp->iop, q_iop);
134                         list_add_tail(&iolp->head, &i_iop->u.i.i_qs_head);
135                 }
136         }
137 }
138
139 void handle_queue(struct io *iop)
140 {
141         struct io *tmp;
142
143         io_setup(iop, IOP_Q);
144
145         update_lq(&last_q, &all_avgs.q2q, iop->t.time);
146         update_qregion(&all_regions, iop->t.time);
147         dip_update_q(iop->dip, iop);
148         pip_update_q(iop);
149
150         tmp = dip_find_exact(dip_get_head(iop->dip, IOP_A), iop);
151         if (tmp) {
152                 iop->u.q.qp_type = Q_A;
153                 io_link(&iop->u.q.qp.q_a, tmp);
154         }
155         else
156                 iop->u.q.qp_type = Q_NONE;
157
158 #if defined(LVM_REMAP_WORKAROUND)
159         if (is_lvm) {
160                 tmp = dip_find_qa(dip_get_head(iop->dip, IOP_A), &iop->t);
161                 if (tmp) {
162                         iop->u.q.qp_type = Q_A;
163                         io_link(&iop->u.q.qp.q_a, tmp);
164                 }
165         }
166 #endif
167 }
168
169 void handle_merge(struct io *iop)
170 {
171         struct io *q_iop;
172
173         io_setup(iop, IOP_M);
174
175         q_iop = dip_find_exact(dip_get_head(iop->dip, IOP_Q), iop);
176         if (q_iop)
177                 io_link(&iop->u.m.m_q, q_iop);
178 }
179
180 void handle_insert(struct io *iop)
181 {
182         struct io_list *iolp = malloc(sizeof(*iolp));
183
184         io_setup(iop, IOP_I);
185         INIT_LIST_HEAD(&iop->u.i.i_qs_head);
186         dip_add_qs(dip_get_head(iop->dip, IOP_Q), iop);
187 }
188
189 void handle_complete(struct io *iop)
190 {
191         struct io *d_iop;
192
193         io_setup(iop, IOP_C);
194         update_blks(iop);
195         update_cregion(&all_regions, iop->t.time);
196         update_cregion(&iop->dip->regions, iop->t.time);
197         if (iop->pip)
198                 update_cregion(&iop->pip->regions, iop->t.time);
199
200         d_iop = dip_find_exact(dip_get_head(iop->dip, IOP_D), iop);
201         if (d_iop) {
202                 io_link(&iop->u.c.c_d, d_iop);
203
204                 add_cy(iop);
205         }
206         else
207                 io_free(iop);
208 }
209
210 void handle_issue(struct io *iop)
211 {
212         struct io *i_iop;
213         struct io_list *iolp = malloc(sizeof(*iolp));
214
215         io_setup(iop, IOP_D);
216         iop->dip->n_ds++;
217
218         INIT_LIST_HEAD(&iop->u.d.d_im_head);
219         i_iop = dip_find_in(dip_get_head(iop->dip, IOP_I), iop);
220         if (i_iop) {
221                 io_link(&iolp->iop, i_iop);
222                 list_add_tail(&iolp->head, &iop->u.d.d_im_head);
223         }
224
225         dip_add_ms(dip_get_head(iop->dip, IOP_M), iop);
226         seeki_add(iop->dip->seek_handle, iop);
227 }
228
229 void handle_split(struct io *iop)
230 {
231         struct io *q_iop;
232
233         pending_xs++;
234         io_setup(iop, IOP_X);
235
236         q_iop = dip_find_exact(dip_get_head(iop->dip, IOP_Q), iop);
237         if (q_iop)
238                 io_link(&iop->u.x.x_q, q_iop);
239 }
240
241 #if 0
242 void __x_add_c(struct io *y_iop, struct io *x_iop,
243                struct blk_io_trace_split_end *rp, int which)
244 {
245         __u32 dev;
246         __u64 sector;
247         struct d_info *dip;
248         struct io **y_cp, *q_iop, *c_iop;
249
250         if (which == 1) {
251                 y_cp = &y_iop->u.y.y_c1;
252                 dev = be32_to_cpu(rp->dev1);
253                 sector = be64_to_cpu(rp->sector1);
254         }
255         else {
256                 y_cp = &y_iop->u.y.y_c2;
257                 dev = be32_to_cpu(rp->dev2);
258                 sector = be64_to_cpu(rp->sector2);
259         }
260
261         dip = __dip_find(dev);
262         ASSERT(dip != NULL);
263
264         q_iop = dip_find_end(dip_get_head(dip, IOP_Q), sector);
265         if (q_iop) {
266                 q_iop->u.q.qp_type = Q_X;
267                 io_link(&q_iop->u.q.qp.q_x, x_iop);
268         }
269
270         c_iop = dip_find_in_sec(dip_get_head(dip, IOP_C), sector);
271         if (c_iop)
272                 io_link(y_cp, c_iop);
273 }
274
275 void handle_split_end(struct io *iop)
276 {
277         struct io *x_iop;
278         struct blk_io_trace_split_end *rp = iop->pdu;
279
280         pending_xs--;
281         io_setup(iop, IOP_Y);
282
283         x_iop = dip_find_exact(dip_get_head(iop->dip, IOP_X), iop);
284         if (x_iop) {
285                 __x_add_c(iop, x_iop, rp, 1);
286                 __x_add_c(iop, x_iop, rp, 2);
287
288                 rem_c(iop->u.y.y_c1);
289                 rem_c(iop->u.y.y_c2);
290
291                 add_cy(iop);
292         }
293         else
294                 release_iop(iop);
295 }
296 #endif
297
298 void handle_remap(struct io *iop)
299 {
300         struct io *q_iop;
301         struct blk_io_trace_remap *rp = iop->pdu;
302         __u32 dev = be32_to_cpu(rp->device);
303         __u64 sector = be64_to_cpu(rp->sector);
304
305         q_iop = dip_find_in_sec(dip_get_head_dev(dev, IOP_Q), sector);
306         if (q_iop) {
307                 io_setup(iop, IOP_A);
308
309 #if defined(LVM_REMAP_WORKAROUND)
310                 if (is_lvm) {
311                         sector = iop->t.sector;
312                         iop->t.sector = be64_to_cpu(rp->sector);
313                 }
314 #endif
315                 io_link(&iop->u.a.a_q, q_iop);
316         }
317         else
318                 release_iop(iop);
319 }
320
321 void extract_i(struct io *i_iop)
322 {
323         struct io_list *iolp;
324         struct list_head *p, *q;
325
326         ASSERT(i_iop != NULL && i_iop->type == IOP_I);
327         list_for_each_safe(p, q, &i_iop->u.i.i_qs_head) {
328                 iolp = list_entry(p, struct io_list, head);
329                 LIST_DEL(&iolp->head);
330
331                 ASSERT(iolp->iop->type == IOP_Q);
332                 (void)__io_put(iolp->iop);
333
334                 free(iolp);
335         }
336 }
337
338 /*
339  * Careful surgery
340  * (1) Need to remove D & its I & M's
341  * (2) Need to leave I's Q and M's Q's -- *no* io_put (__io_put instead)
342  */
343 void handle_requeue(struct io *iop)
344 {
345         struct io *d_iop;
346         struct io_list *iolp;
347         struct list_head *p, *q;
348
349         d_iop = dip_find_start(dip_get_head_dev(iop->t.device, IOP_D),
350                                iop->t.sector);
351         if (d_iop) {
352                 list_for_each_safe(p, q, &d_iop->u.d.d_im_head) {
353                         iolp = list_entry(p, struct io_list, head);
354                         LIST_DEL(&iolp->head);
355
356                         if (iolp->iop->type == IOP_M)
357                                 (void)__io_put(iolp->iop->u.m.m_q);
358                         else
359                                 extract_i(iolp->iop);
360
361                         iolp->iop->users = 0;
362                         io_free(iolp->iop);
363                         free(iolp);
364                 }
365
366                 d_iop->users = 0;
367                 io_free(d_iop);
368         }
369
370         release_iop(iop);
371
372 }
373
374 void __add_trace(struct io *iop)
375 {
376         n_traces++;
377
378         if (verbose && (n_traces % 10000) == 0) {
379                 printf("%10lu t, %10lu m %1lu f\r",
380                        n_traces, n_io_allocs, n_io_frees);
381                 fflush(stdout);
382                 if ((n_traces % 1000000) == 0) printf("\n");
383         }
384
385         switch (iop->t.action & 0xffff) {
386         case __BLK_TA_QUEUE:            handle_queue(iop);      break;
387         case __BLK_TA_BACKMERGE:        handle_merge(iop);      break;
388         case __BLK_TA_FRONTMERGE:       handle_merge(iop);      break;
389         case __BLK_TA_ISSUE:            handle_issue(iop);      break;
390         case __BLK_TA_COMPLETE:         handle_complete(iop);   break;
391         case __BLK_TA_INSERT:           handle_insert(iop);     break;
392         case __BLK_TA_SPLIT:            handle_split(iop);      break;
393 #if 0
394         case __BLK_TA_SPLIT_END:        handle_split_end(iop);  break;
395 #endif
396         case __BLK_TA_REMAP:            handle_remap(iop);      break;
397         case __BLK_TA_REQUEUE:          handle_requeue(iop);    break;
398         }
399 }
400
401 void add_trace(struct io *iop)
402 {
403         if (iop->t.action & BLK_TC_ACT(BLK_TC_NOTIFY)) {
404                 char *slash = strchr(iop->pdu, '/');
405
406                 if (slash)
407                         *slash = '\0';
408                 add_process(iop->t.pid, iop->pdu);
409                 release_iop(iop);
410         }
411         else if (iop->t.action & BLK_TC_ACT(BLK_TC_PC))
412                 release_iop(iop);
413         else
414                 __add_trace(iop);
415 }