Commit | Line | Data |
---|---|---|
63eba147 JA |
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 | */ | |
63eba147 | 21 | |
e71e4b78 | 22 | static inline struct range_info *new_cur(__u64 time) |
63eba147 | 23 | { |
6eb42155 | 24 | struct range_info *cur = malloc(sizeof(struct range_info)); |
63eba147 JA |
25 | |
26 | INIT_LIST_HEAD(&cur->head); | |
27 | cur->start = time; | |
28 | return cur; | |
29 | } | |
30 | ||
6eb42155 ADB |
31 | static inline void update_range(struct list_head *head_p, |
32 | struct range_info **cur_p, __u64 time) | |
63eba147 JA |
33 | { |
34 | if (*cur_p == NULL) | |
35 | *cur_p = new_cur(time); | |
36 | else { | |
095181f2 | 37 | __u64 my_delta = (time > (*cur_p)->end) ? time - (*cur_p)->end : 1; |
63eba147 JA |
38 | if (BIT_TIME(my_delta) >= range_delta) { |
39 | list_add_tail(&(*cur_p)->head, head_p); | |
40 | *cur_p = new_cur(time); | |
41 | } | |
42 | } | |
43 | ||
44 | (*cur_p)->end = time; | |
45 | } | |
46 | ||
e71e4b78 | 47 | static inline void init_region(struct region_info *reg) |
63eba147 JA |
48 | { |
49 | INIT_LIST_HEAD(®->qranges); | |
50 | INIT_LIST_HEAD(®->cranges); | |
51 | reg->qr_cur = reg->cr_cur = NULL; | |
52 | } | |
53 | ||
e71e4b78 | 54 | static inline void update_qregion(struct region_info *reg, __u64 time) |
63eba147 JA |
55 | { |
56 | update_range(®->qranges, ®->qr_cur, time); | |
57 | } | |
58 | ||
e71e4b78 | 59 | static inline void update_cregion(struct region_info *reg, __u64 time) |
63eba147 JA |
60 | { |
61 | update_range(®->cranges, ®->cr_cur, time); | |
62 | } | |
63 | ||
e71e4b78 | 64 | static inline void avg_update(struct avg_info *ap, __u64 t) |
63eba147 JA |
65 | { |
66 | if (ap->n++ == 0) | |
67 | ap->min = ap->total = ap->max = t; | |
68 | else { | |
69 | if (t < ap->min) | |
70 | ap->min = t; | |
71 | else if (t > ap->max) | |
72 | ap->max = t; | |
73 | ap->total += t; | |
74 | } | |
75 | } | |
76 | ||
d76c5b81 AB |
77 | static inline void avg_update_n(struct avg_info *ap, __u64 t, int n) |
78 | { | |
79 | if (ap->n == 0) { | |
80 | ap->min = ap->max = t; | |
81 | ap->total = (n * t); | |
82 | } | |
83 | else { | |
84 | if (t < ap->min) | |
85 | ap->min = t; | |
86 | else if (t > ap->max) | |
87 | ap->max = t; | |
88 | ap->total += (n * t); | |
89 | } | |
90 | ||
91 | ap->n += n; | |
92 | } | |
93 | ||
6eb42155 ADB |
94 | static inline void avg_unupdate(struct avg_info *ap, __u64 t) |
95 | { | |
96 | ap->n--; | |
97 | ap->total -= t; | |
98 | } | |
99 | ||
e71e4b78 | 100 | static inline void update_lq(__u64 *last_q, struct avg_info *avg, __u64 time) |
63eba147 JA |
101 | { |
102 | if (*last_q != ((__u64)-1)) | |
095181f2 | 103 | avg_update(avg, (time > *last_q) ? time - *last_q : 1); |
63eba147 JA |
104 | *last_q = time; |
105 | } | |
106 | ||
e71e4b78 | 107 | static inline void dip_update_q(struct d_info *dip, struct io *iop) |
63eba147 JA |
108 | { |
109 | update_lq(&dip->last_q, &dip->avgs.q2q, iop->t.time); | |
110 | update_qregion(&dip->regions, iop->t.time); | |
111 | } | |
112 | ||
6eb42155 | 113 | static inline struct io *io_alloc(void) |
63eba147 | 114 | { |
6eb42155 | 115 | struct io *iop; |
63eba147 | 116 | |
6eb42155 ADB |
117 | if (!list_empty(&free_ios)) { |
118 | iop = list_entry(free_ios.next, struct io, f_head); | |
119 | LIST_DEL(&iop->f_head); | |
120 | } | |
63eba147 | 121 | else |
6eb42155 | 122 | iop = malloc(sizeof(struct io)); |
63eba147 | 123 | |
095181f2 | 124 | memset(iop, 0, sizeof(struct io)); |
d76c5b81 AB |
125 | INIT_LIST_HEAD(&iop->down_list); |
126 | INIT_LIST_HEAD(&iop->up_list); | |
127 | ||
128 | #if defined(DEBUG) | |
129 | iop->f_head.next = LIST_POISON1; | |
130 | iop->c_pending.next = LIST_POISON1; | |
131 | iop->retry.next = LIST_POISON1; | |
132 | #endif | |
095181f2 JA |
133 | |
134 | return iop; | |
63eba147 JA |
135 | } |
136 | ||
6eb42155 | 137 | static inline void io_free(struct io *iop) |
63eba147 | 138 | { |
095181f2 JA |
139 | # if defined(DEBUG) |
140 | memset(iop, 0, sizeof(*iop)); | |
141 | # endif | |
6eb42155 | 142 | list_add_tail(&iop->f_head, &free_ios); |
63eba147 JA |
143 | } |
144 | ||
095181f2 | 145 | static inline int io_setup(struct io *iop, enum iop_type type) |
63eba147 JA |
146 | { |
147 | iop->type = type; | |
095181f2 JA |
148 | iop->dip = dip_add(iop->t.device, iop); |
149 | if (iop->linked) { | |
150 | iop->pip = find_process(iop->t.pid, NULL); | |
095181f2 JA |
151 | iop->bytes_left = iop->t.bytes; |
152 | } | |
153 | ||
154 | return iop->linked; | |
63eba147 JA |
155 | } |
156 | ||
6eb42155 | 157 | static inline void io_release(struct io *iop) |
63eba147 | 158 | { |
d76c5b81 AB |
159 | ASSERT(iop->f_head.next == LIST_POISON1); |
160 | ASSERT(iop->c_pending.next == LIST_POISON1); | |
161 | ASSERT(iop->retry.next == LIST_POISON1); | |
162 | ASSERT(list_empty(&iop->up_list)); | |
163 | ASSERT(list_empty(&iop->down_list)); | |
164 | ||
095181f2 | 165 | if (iop->linked) |
6eb42155 | 166 | dip_rem(iop); |
6eb42155 ADB |
167 | if (iop->pdu) |
168 | free(iop->pdu); | |
d76c5b81 | 169 | |
6eb42155 | 170 | io_free(iop); |
63eba147 JA |
171 | } |
172 | ||
173 | #define UPDATE_AVGS(_avg, _iop, _pip, _time) do { \ | |
174 | avg_update(&all_avgs. _avg , _time); \ | |
175 | avg_update(&_iop->dip->avgs. _avg , _time); \ | |
176 | if (_pip) avg_update(&_pip->avgs. _avg , _time); \ | |
177 | } while (0) | |
178 | ||
d76c5b81 AB |
179 | #define UPDATE_AVGS_N(_avg, _iop, _pip, _time, _n) do { \ |
180 | avg_update_n(&all_avgs. _avg , _time, _n); \ | |
181 | avg_update_n(&_iop->dip->avgs. _avg , _time, _n); \ | |
182 | if (_pip) avg_update_n(&_pip->avgs. _avg , _time,_n); \ | |
183 | } while (0) | |
184 | ||
6eb42155 ADB |
185 | #define UNUPDATE_AVGS(_avg, _iop, _pip, _time) do { \ |
186 | avg_unupdate(&all_avgs. _avg , _time); \ | |
187 | avg_unupdate(&_iop->dip->avgs. _avg , _time); \ | |
188 | if (_pip) avg_unupdate(&_pip->avgs. _avg , _time); \ | |
189 | } while (0) | |
190 | ||
e71e4b78 | 191 | static inline void update_q2c(struct io *iop, __u64 c_time) |
63eba147 | 192 | { |
d76c5b81 AB |
193 | #if defined(DEBUG) |
194 | if (per_io_ofp) fprintf(per_io_ofp, "q2c %13.9f\n", BIT_TIME(c_time)); | |
195 | #endif | |
63eba147 JA |
196 | UPDATE_AVGS(q2c, iop, iop->pip, c_time); |
197 | } | |
198 | ||
e71e4b78 | 199 | static inline void update_q2a(struct io *iop, __u64 a_time) |
63eba147 | 200 | { |
d76c5b81 AB |
201 | #if defined(DEBUG) |
202 | if (per_io_ofp) fprintf(per_io_ofp, "q2a %13.9f\n", BIT_TIME(a_time)); | |
203 | #endif | |
63eba147 JA |
204 | UPDATE_AVGS(q2a, iop, iop->pip, a_time); |
205 | } | |
206 | ||
e71e4b78 | 207 | static inline void update_q2i(struct io *iop, __u64 i_time) |
63eba147 | 208 | { |
d76c5b81 AB |
209 | #if defined(DEBUG) |
210 | if (per_io_ofp) fprintf(per_io_ofp, "q2i %13.9f\n", BIT_TIME(i_time)); | |
211 | #endif | |
63eba147 JA |
212 | UPDATE_AVGS(q2i, iop, iop->pip, i_time); |
213 | } | |
214 | ||
d76c5b81 AB |
215 | static inline void unupdate_q2i(struct io *iop, __u64 i_time) |
216 | { | |
217 | UNUPDATE_AVGS(q2i, iop, iop->pip, i_time); | |
218 | } | |
219 | ||
e71e4b78 | 220 | static inline void update_i2d(struct io *iop, __u64 d_time) |
63eba147 | 221 | { |
d76c5b81 AB |
222 | #if defined(DEBUG) |
223 | if (per_io_ofp) fprintf(per_io_ofp, "i2d %13.9f\n", BIT_TIME(d_time)); | |
224 | #endif | |
63eba147 JA |
225 | UPDATE_AVGS(i2d, iop, iop->pip, d_time); |
226 | } | |
227 | ||
6eb42155 ADB |
228 | static inline void unupdate_i2d(struct io *iop, __u64 d_time) |
229 | { | |
230 | UNUPDATE_AVGS(i2d, iop, iop->pip, d_time); | |
231 | } | |
232 | ||
d76c5b81 | 233 | static inline void update_d2c(struct io *iop, int n, __u64 c_time) |
63eba147 | 234 | { |
512566d4 | 235 | #if defined(DEBUG) |
d76c5b81 | 236 | if (per_io_ofp) fprintf(per_io_ofp, "d2c %13.9f\n", n*BIT_TIME(c_time)); |
512566d4 | 237 | #endif |
d76c5b81 | 238 | UPDATE_AVGS_N(d2c, iop, iop->pip, c_time, n); |
63eba147 JA |
239 | } |
240 | ||
e71e4b78 | 241 | static inline void update_blks(struct io *iop) |
63eba147 JA |
242 | { |
243 | __u64 nblks = iop->t.bytes >> 9; | |
244 | avg_update(&all_avgs.blks, nblks); | |
6eb42155 | 245 | ASSERT(iop->dip != NULL); |
63eba147 JA |
246 | avg_update(&iop->dip->avgs.blks, nblks); |
247 | if (iop->pip) | |
248 | avg_update(&iop->pip->avgs.blks, nblks); | |
249 | } | |
6eb42155 ADB |
250 | |
251 | static inline struct rb_root *__get_root(struct d_info *dip, enum iop_type type) | |
252 | { | |
253 | struct rb_root *roots = dip->heads; | |
254 | return &roots[type]; | |
255 | } | |
256 | ||
257 | static inline void *dip_rb_mkhds(void) | |
258 | { | |
259 | size_t len = N_IOP_TYPES * sizeof(struct rb_root); | |
260 | return memset(malloc(len), 0, len); | |
261 | } | |
262 | ||
095181f2 | 263 | static inline int dip_rb_ins(struct d_info *dip, struct io *iop) |
6eb42155 | 264 | { |
095181f2 | 265 | return rb_insert(__get_root(dip, iop->type), iop); |
6eb42155 ADB |
266 | } |
267 | ||
268 | static inline void dip_rb_rem(struct io *iop) | |
269 | { | |
270 | rb_erase(&iop->rb_node, __get_root(iop->dip, iop->type)); | |
d76c5b81 AB |
271 | #if defined(DEBUG) |
272 | rb_tree_size--; | |
273 | #endif | |
6eb42155 ADB |
274 | } |
275 | ||
276 | static inline void dip_rb_fe(struct d_info *dip, enum iop_type type, | |
277 | struct io *iop, | |
278 | void (*fnc)(struct io *iop, struct io *this), | |
279 | struct list_head *head) | |
280 | { | |
281 | rb_foreach(__get_root(dip, type)->rb_node, iop, fnc, head); | |
282 | } | |
283 | ||
284 | static inline struct io *dip_rb_find_sec(struct d_info *dip, | |
285 | enum iop_type type, __u64 sec) | |
286 | { | |
287 | return rb_find_sec(__get_root(dip, type), sec); | |
288 | } | |
095181f2 | 289 | |
d76c5b81 | 290 | static inline void bump_retry(__u64 now) |
095181f2 | 291 | { |
d76c5b81 AB |
292 | if (!list_empty(&retries)) |
293 | next_retry_check = now + (100 * 1000); // 100 usec | |
294 | else | |
295 | next_retry_check = 0; | |
095181f2 JA |
296 | } |
297 | ||
298 | static inline void add_retry(struct io *iop) | |
299 | { | |
d76c5b81 | 300 | bump_retry(iop->t.time); |
001b2633 JA |
301 | if (!iop->on_retry_list) { |
302 | list_add_tail(&iop->retry, &retries); | |
303 | iop->on_retry_list = 1; | |
304 | } | |
095181f2 JA |
305 | } |
306 | ||
307 | static inline void del_retry(struct io *iop) | |
308 | { | |
001b2633 JA |
309 | if (iop->on_retry_list) { |
310 | LIST_DEL(&iop->retry); | |
311 | iop->on_retry_list = 0; | |
312 | } | |
d76c5b81 | 313 | bump_retry(iop->t.time); |
095181f2 JA |
314 | } |
315 | ||
316 | static inline __u64 tdelta(struct io *iop1, struct io *iop2) | |
317 | { | |
318 | __u64 t1 = iop1->t.time; | |
319 | __u64 t2 = iop2->t.time; | |
320 | return (t1 < t2) ? (t2 - t1) : 1; | |
321 | } | |
d76c5b81 AB |
322 | |
323 | static inline int remapper_dev(__u32 dev) | |
324 | { | |
325 | int mjr = MAJOR(dev); | |
a6c42403 | 326 | return mjr == 9 || mjr == 253 || mjr == 254; |
d76c5b81 AB |
327 | } |
328 | ||
329 | static inline void dump_iop(struct io *iop, int extra_nl) | |
330 | { | |
331 | if (per_io_ofp) | |
332 | __dump_iop(per_io_ofp, iop, extra_nl); | |
333 | } | |
334 | ||
335 | static inline void dump_iop2(struct io *a_iop, struct io *l_iop) | |
336 | { | |
337 | if (per_io_ofp) | |
338 | __dump_iop2(per_io_ofp, a_iop, l_iop); | |
339 | } | |
340 | ||
341 | static inline int type2c(enum iop_type type) | |
342 | { | |
343 | int c; | |
344 | ||
345 | switch (type) { | |
346 | case IOP_Q: c = 'Q'; break; | |
347 | case IOP_X: c = 'X'; break; | |
348 | case IOP_A: c = 'A'; break; | |
349 | case IOP_I: c = 'I'; break; | |
350 | case IOP_M: c = 'M'; break; | |
351 | case IOP_D: c = 'D'; break; | |
352 | case IOP_C: c = 'C'; break; | |
353 | case IOP_R: c = 'R'; break; | |
354 | case IOP_L: c = 'L'; break; | |
355 | default : c = '?'; break; | |
356 | } | |
357 | ||
358 | return c; | |
359 | } | |
360 | ||
361 | static inline void bilink_free(struct bilink *blp) | |
362 | { | |
363 | free(blp); | |
364 | } | |
365 | ||
366 | static inline struct bilink *bilink_alloc(struct io *diop, struct io *uiop) | |
367 | { | |
368 | struct bilink *blp = malloc(sizeof(*blp)); | |
369 | ||
370 | blp->diop = diop; | |
371 | blp->uiop = uiop; | |
372 | ||
373 | return blp; | |
374 | } | |
375 | ||
376 | static inline void bilink(struct io *diop, struct io *uiop) | |
377 | { | |
378 | struct bilink *blp = bilink_alloc(diop, uiop); | |
379 | ||
380 | list_add_tail(&blp->down_head, &diop->up_list); | |
381 | list_add_tail(&blp->up_head, &uiop->down_list); | |
382 | ||
383 | diop->up_len++; | |
384 | uiop->down_len++; | |
385 | } | |
386 | ||
387 | static inline void biunlink(struct bilink *blp) | |
388 | { | |
389 | LIST_DEL(&blp->down_head); | |
390 | LIST_DEL(&blp->up_head); | |
391 | blp->diop->up_len--; | |
392 | blp->uiop->down_len--; | |
393 | bilink_free(blp); | |
394 | } | |
395 | ||
396 | static inline struct io *bilink_first_down(struct io *iop, | |
397 | struct bilink **blp_p) | |
398 | { | |
399 | struct bilink *blp; | |
400 | ||
401 | if (list_empty(&iop->down_list)) | |
402 | return NULL; | |
403 | blp = list_entry(iop->down_list.next, struct bilink, up_head); | |
404 | ||
405 | if (blp_p != NULL) | |
406 | *blp_p = blp; | |
407 | return blp->diop; | |
408 | } | |
409 | ||
410 | static inline struct io *bilink_first_up(struct io *iop, struct bilink **blp_p) | |
411 | { | |
412 | struct bilink *blp; | |
413 | ||
414 | if (list_empty(&iop->up_list)) | |
415 | return NULL; | |
416 | blp = list_entry(iop->up_list.next, struct bilink, down_head); | |
417 | ||
418 | if (blp_p != NULL) | |
419 | *blp_p = blp; | |
a6c42403 | 420 | return blp->uiop; |
d76c5b81 AB |
421 | } |
422 | ||
423 | typedef void (*bilink_func)(struct io *diop, struct io *uiop, void *param); | |
424 | static inline void bilink_for_each_down(bilink_func func, struct io *uiop, | |
425 | void *param, int ul) | |
426 | { | |
427 | struct bilink *blp; | |
428 | struct list_head *p, *q; | |
429 | ||
430 | list_for_each_safe(p, q, &uiop->down_list) { | |
431 | blp = list_entry(p, struct bilink, up_head); | |
432 | func(blp->diop, uiop, param); | |
433 | if (ul) | |
434 | biunlink(blp); | |
435 | } | |
436 | } | |
fa0ab85d AB |
437 | |
438 | static inline int histo_idx(__u64 nbytes) | |
439 | { | |
440 | int idx = (nbytes >> 9) - 1; | |
441 | return min(idx, N_HIST_BKTS-1); | |
442 | } | |
443 | ||
444 | static inline void update_q_histo(__u64 nbytes) | |
445 | { | |
446 | q_histo[histo_idx(nbytes)]++; | |
447 | } | |
448 | ||
449 | static inline void update_d_histo(__u64 nbytes) | |
450 | { | |
451 | d_histo[histo_idx(nbytes)]++; | |
452 | } |