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 | */ | |
21 | #include <stdio.h> | |
63eba147 JA |
22 | #include "globals.h" |
23 | ||
24 | #define N_DEV_HASH 128 | |
25 | #define DEV_HASH(dev) ((MAJOR(dev) ^ MINOR(dev)) & (N_DEV_HASH - 1)) | |
6eb42155 | 26 | struct list_head dev_heads[N_DEV_HASH]; |
63eba147 | 27 | |
69040794 AB |
28 | static inline void *dip_rb_mkhds(void) |
29 | { | |
30 | size_t len = N_IOP_TYPES * sizeof(struct rb_root); | |
31 | return memset(malloc(len), 0, len); | |
32 | } | |
33 | ||
34 | static void __destroy(struct rb_node *n) | |
35 | { | |
36 | if (n) { | |
37 | struct io *iop = rb_entry(n, struct io, rb_node); | |
38 | ||
39 | __destroy(n->rb_left); | |
40 | __destroy(n->rb_right); | |
41 | io_release(iop); | |
42 | } | |
43 | } | |
44 | ||
45 | static void __destroy_heads(struct rb_root *roots) | |
46 | { | |
47 | int i; | |
48 | ||
49 | for (i = 0; i < N_IOP_TYPES; i++) | |
50 | __destroy(roots[i].rb_node); | |
51 | ||
52 | free(roots); | |
53 | } | |
54 | ||
d76c5b81 AB |
55 | #if defined(DEBUG) |
56 | void __dump_rb_node(struct rb_node *n) | |
57 | { | |
58 | struct io *iop = rb_entry(n, struct io, rb_node); | |
59 | ||
60 | dbg_ping(); | |
4c48f14e | 61 | __dump_iop(stdout, iop, 0); |
d76c5b81 AB |
62 | if (n->rb_left) |
63 | __dump_rb_node(n->rb_left); | |
64 | if (n->rb_right) | |
65 | __dump_rb_node(n->rb_right); | |
66 | } | |
67 | ||
68 | void __dump_rb_tree(struct d_info *dip, enum iop_type type) | |
69 | { | |
70 | struct rb_root *roots = dip->heads; | |
71 | struct rb_root *root = &roots[type]; | |
72 | struct rb_node *n = root->rb_node; | |
73 | ||
74 | if (n) { | |
75 | printf("\tIOP_%c\n", type2c(type)); | |
76 | __dump_rb_node(n); | |
77 | } | |
78 | } | |
79 | ||
80 | void dump_rb_trees(void) | |
81 | { | |
82 | int i; | |
83 | enum iop_type type; | |
84 | struct d_info *dip; | |
85 | struct list_head *p; | |
86 | ||
87 | for (i = 0; i < N_DEV_HASH; i++) { | |
88 | __list_for_each(p, &dev_heads[i]) { | |
89 | dip = list_entry(p, struct d_info, hash_head); | |
90 | printf("Trees for %3d,%-3d\n", MAJOR(dip->device), | |
91 | MINOR(dip->device)); | |
4c48f14e AB |
92 | for (type = IOP_Q; type < N_IOP_TYPES; type++) |
93 | __dump_rb_tree(dip, type); | |
d76c5b81 AB |
94 | } |
95 | } | |
96 | } | |
97 | #endif | |
98 | ||
6eb42155 | 99 | void init_dev_heads(void) |
63eba147 JA |
100 | { |
101 | int i; | |
63eba147 JA |
102 | for (i = 0; i < N_DEV_HASH; i++) |
103 | INIT_LIST_HEAD(&dev_heads[i]); | |
104 | } | |
105 | ||
106 | struct d_info *__dip_find(__u32 device) | |
107 | { | |
63eba147 | 108 | struct d_info *dip; |
d76c5b81 | 109 | struct list_head *p; |
63eba147 | 110 | |
63eba147 JA |
111 | __list_for_each(p, &dev_heads[DEV_HASH(device)]) { |
112 | dip = list_entry(p, struct d_info, hash_head); | |
113 | if (device == dip->device) | |
114 | return dip; | |
115 | } | |
116 | ||
117 | return NULL; | |
118 | } | |
119 | ||
69040794 AB |
120 | void dip_exit(void) |
121 | { | |
122 | struct d_info *dip; | |
123 | struct list_head *p, *q; | |
124 | ||
125 | list_for_each_safe(p, q, &all_devs) { | |
126 | dip = list_entry(p, struct d_info, all_head); | |
127 | ||
128 | __destroy_heads(dip->heads); | |
129 | region_exit(&dip->regions); | |
130 | seeki_exit(dip->seek_handle); | |
4c48f14e | 131 | seeki_exit(dip->q2q_handle); |
69040794 | 132 | bno_dump_exit(dip->bno_dump_handle); |
fc16a815 | 133 | unplug_hist_exit(dip->unplug_hist_handle); |
951ea56c AB |
134 | if (output_all_data) |
135 | q2d_release(dip->q2d_priv); | |
69040794 AB |
136 | free(dip); |
137 | } | |
138 | } | |
139 | ||
4c48f14e AB |
140 | static inline char *mkhandle(char *str, __u32 device, char *post) |
141 | { | |
142 | int mjr = device >> MINORBITS; | |
143 | int mnr = device & ((1 << MINORBITS) - 1); | |
144 | ||
145 | sprintf(str, "%03d,%03d%s", mjr, mnr, post); | |
146 | return str; | |
147 | } | |
148 | ||
095181f2 | 149 | struct d_info *dip_add(__u32 device, struct io *iop) |
63eba147 JA |
150 | { |
151 | struct d_info *dip = __dip_find(device); | |
152 | ||
153 | if (dip == NULL) { | |
4c48f14e AB |
154 | char str[256]; |
155 | ||
6eb42155 | 156 | dip = malloc(sizeof(struct d_info)); |
2fa934ce | 157 | memset(dip, 0, sizeof(*dip)); |
6eb42155 | 158 | dip->heads = dip_rb_mkhds(); |
69040794 | 159 | region_init(&dip->regions); |
63eba147 JA |
160 | dip->device = device; |
161 | dip->last_q = (__u64)-1; | |
63eba147 | 162 | dip->map = dev_map_find(device); |
69040794 | 163 | dip->bno_dump_handle = bno_dump_init(device); |
fc16a815 | 164 | dip->unplug_hist_handle = unplug_hist_init(device); |
4c48f14e AB |
165 | dip->seek_handle = seeki_init(mkhandle(str, device, "_d2d")); |
166 | dip->q2q_handle = seeki_init(mkhandle(str, device, "_q2q")); | |
b2ecdd0f | 167 | latency_init(dip); |
63eba147 | 168 | list_add_tail(&dip->hash_head, &dev_heads[DEV_HASH(device)]); |
6eb42155 | 169 | list_add_tail(&dip->all_head, &all_devs); |
b2822cea | 170 | dip->start_time = BIT_TIME(iop->t.time); |
11997716 | 171 | dip->pre_culling = 1; |
951ea56c AB |
172 | if (output_all_data) |
173 | dip->q2d_priv = q2d_init(); | |
63eba147 JA |
174 | n_devs++; |
175 | } | |
176 | ||
11997716 AB |
177 | if (dip->pre_culling) { |
178 | if (iop->type == IOP_Q || iop->type == IOP_A) | |
179 | dip->pre_culling = 0; | |
180 | else | |
181 | return NULL; | |
182 | } | |
183 | ||
095181f2 | 184 | iop->linked = dip_rb_ins(dip, iop); |
b2822cea | 185 | dip->end_time = BIT_TIME(iop->t.time); |
c8b0b334 AB |
186 | |
187 | # if defined(DEBUG) | |
188 | if (iop->linked) | |
189 | rb_tree_size++; | |
190 | # endif | |
191 | ||
63eba147 JA |
192 | return dip; |
193 | } | |
6eb42155 ADB |
194 | |
195 | void dip_rem(struct io *iop) | |
196 | { | |
d76c5b81 AB |
197 | if (iop->linked) { |
198 | dip_rb_rem(iop); | |
199 | iop->linked = 0; | |
200 | } | |
6eb42155 ADB |
201 | } |
202 | ||
203 | void dip_foreach(struct io *iop, enum iop_type type, | |
204 | void (*fnc)(struct io *iop, struct io *this), int rm_after) | |
205 | { | |
206 | if (rm_after) { | |
207 | LIST_HEAD(head); | |
208 | struct io *this; | |
209 | struct list_head *p, *q; | |
210 | ||
211 | dip_rb_fe(iop->dip, type, iop, fnc, &head); | |
212 | list_for_each_safe(p, q, &head) { | |
213 | this = list_entry(p, struct io, f_head); | |
214 | LIST_DEL(&this->f_head); | |
215 | io_release(this); | |
216 | } | |
217 | } | |
218 | else | |
219 | dip_rb_fe(iop->dip, type, iop, fnc, NULL); | |
220 | } | |
221 | ||
095181f2 JA |
222 | void dip_foreach_list(struct io *iop, enum iop_type type, struct list_head *hd) |
223 | { | |
224 | dip_rb_fe(iop->dip, type, iop, NULL, hd); | |
225 | } | |
226 | ||
6eb42155 ADB |
227 | struct io *dip_find_sec(struct d_info *dip, enum iop_type type, __u64 sec) |
228 | { | |
229 | return dip_rb_find_sec(dip, type, sec); | |
230 | } | |
231 | ||
232 | void dip_foreach_out(void (*func)(struct d_info *, void *), void *arg) | |
233 | { | |
234 | if (devices == NULL) { | |
235 | struct list_head *p; | |
236 | __list_for_each(p, &all_devs) | |
237 | func(list_entry(p, struct d_info, all_head), arg); | |
238 | } | |
239 | else { | |
240 | int i; | |
241 | struct d_info *dip; | |
242 | unsigned int mjr, mnr; | |
243 | char *p = devices; | |
244 | ||
245 | while (p && ((i = sscanf(p, "%u,%u", &mjr, &mnr)) == 2)) { | |
246 | dip = __dip_find((__u32)((mjr << MINORBITS) | mnr)); | |
247 | ASSERT(dip); | |
248 | ||
249 | func(dip, arg); | |
250 | ||
251 | p = strchr(p, ';'); | |
252 | if (p) p++; | |
253 | } | |
254 | } | |
255 | } | |
b2822cea AB |
256 | |
257 | void dip_plug(__u32 dev, double cur_time) | |
258 | { | |
259 | struct d_info *dip = __dip_find(dev); | |
260 | ||
261 | if (!dip || dip->is_plugged) return; | |
262 | ||
263 | dip->is_plugged = 1; | |
264 | dip->last_plug = cur_time; | |
265 | } | |
266 | ||
00a47cd1 | 267 | void dip_unplug(__u32 dev, double cur_time) |
b2822cea AB |
268 | { |
269 | struct d_info *dip = __dip_find(dev); | |
270 | ||
00a47cd1 AB |
271 | if (dip && dip->is_plugged) { |
272 | dip->nplugs++; | |
273 | dip->plugged_time += (cur_time - dip->last_plug); | |
274 | dip->is_plugged = 0; | |
275 | } | |
276 | } | |
b2822cea | 277 | |
00a47cd1 AB |
278 | void dip_unplug_tm(__u32 dev) |
279 | { | |
280 | struct d_info *dip = __dip_find(dev); | |
b2822cea | 281 | |
00a47cd1 AB |
282 | if (dip && dip->is_plugged) |
283 | dip->n_timer_unplugs++; | |
b2822cea | 284 | } |