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 | ||
6eb42155 | 55 | void init_dev_heads(void) |
63eba147 JA |
56 | { |
57 | int i; | |
63eba147 JA |
58 | for (i = 0; i < N_DEV_HASH; i++) |
59 | INIT_LIST_HEAD(&dev_heads[i]); | |
60 | } | |
61 | ||
62 | struct d_info *__dip_find(__u32 device) | |
63 | { | |
63eba147 | 64 | struct d_info *dip; |
d76c5b81 | 65 | struct list_head *p; |
63eba147 | 66 | |
63eba147 JA |
67 | __list_for_each(p, &dev_heads[DEV_HASH(device)]) { |
68 | dip = list_entry(p, struct d_info, hash_head); | |
69 | if (device == dip->device) | |
70 | return dip; | |
71 | } | |
72 | ||
73 | return NULL; | |
74 | } | |
75 | ||
52481561 AB |
76 | void __dip_exit(struct d_info *dip) |
77 | { | |
78 | list_del(&dip->all_head); | |
79 | __destroy_heads(dip->heads); | |
80 | region_exit(&dip->regions); | |
c053af42 AB |
81 | seeki_free(dip->seek_handle); |
82 | seeki_free(dip->q2q_handle); | |
83 | aqd_free(dip->aqd_handle); | |
84 | plat_free(dip->q2d_plat_handle); | |
85 | plat_free(dip->q2c_plat_handle); | |
86 | plat_free(dip->d2c_plat_handle); | |
2e37a10e | 87 | p_live_free(dip->p_live_handle); |
c053af42 AB |
88 | bno_dump_free(dip->bno_dump_handle); |
89 | unplug_hist_free(dip->up_hist_handle); | |
dbb8d92d | 90 | rstat_free(dip->rstat_handle); |
52481561 | 91 | if (output_all_data) |
c053af42 | 92 | q2d_free(dip->q2d_priv); |
52481561 AB |
93 | if (dip->pit_fp) |
94 | fclose(dip->pit_fp); | |
95 | free(dip); | |
96 | } | |
97 | ||
69040794 AB |
98 | void dip_exit(void) |
99 | { | |
69040794 AB |
100 | struct list_head *p, *q; |
101 | ||
102 | list_for_each_safe(p, q, &all_devs) { | |
52481561 AB |
103 | struct d_info *dip = list_entry(p, struct d_info, all_head); |
104 | __dip_exit(dip); | |
69040794 AB |
105 | } |
106 | } | |
107 | ||
a155ab98 | 108 | static inline FILE *open_pit(struct d_info *dip) |
4c48f14e | 109 | { |
a155ab98 AB |
110 | FILE *fp; |
111 | char str[256]; | |
4c48f14e | 112 | |
a155ab98 AB |
113 | sprintf(str, "%s_pit.dat", dip->dip_name); |
114 | if ((fp = my_fopen(str, "w")) == NULL) | |
a22df989 AB |
115 | perror(str); |
116 | ||
117 | return fp; | |
118 | } | |
119 | ||
c053af42 | 120 | struct d_info *dip_alloc(__u32 device, struct io *iop) |
63eba147 JA |
121 | { |
122 | struct d_info *dip = __dip_find(device); | |
123 | ||
124 | if (dip == NULL) { | |
6eb42155 | 125 | dip = malloc(sizeof(struct d_info)); |
2fa934ce | 126 | memset(dip, 0, sizeof(*dip)); |
63eba147 | 127 | dip->device = device; |
c053af42 | 128 | dip->devmap = dev_map_find(device); |
a155ab98 AB |
129 | dip->last_q = (__u64)-1; |
130 | dip->heads = dip_rb_mkhds(); | |
131 | region_init(&dip->regions); | |
b2822cea | 132 | dip->start_time = BIT_TIME(iop->t.time); |
11997716 | 133 | dip->pre_culling = 1; |
a155ab98 AB |
134 | |
135 | mkhandle(dip, dip->dip_name, 256); | |
136 | ||
137 | latency_alloc(dip); | |
138 | dip->aqd_handle = aqd_alloc(dip); | |
139 | dip->bno_dump_handle = bno_dump_alloc(dip); | |
140 | dip->up_hist_handle = unplug_hist_alloc(dip); | |
141 | dip->seek_handle = seeki_alloc(dip, "_d2d"); | |
142 | dip->q2q_handle = seeki_alloc(dip, "_q2q"); | |
143 | dip->q2d_plat_handle = plat_alloc(dip, "_q2d"); | |
144 | dip->q2c_plat_handle = plat_alloc(dip, "_q2c"); | |
145 | dip->d2c_plat_handle = plat_alloc(dip, "_d2c"); | |
146 | dip->rstat_handle = rstat_alloc(dip); | |
2e37a10e | 147 | dip->p_live_handle = p_live_alloc(); |
a155ab98 AB |
148 | |
149 | if (per_io_trees) | |
150 | dip->pit_fp = open_pit(dip); | |
151 | ||
951ea56c | 152 | if (output_all_data) |
c053af42 | 153 | dip->q2d_priv = q2d_alloc(); |
a155ab98 AB |
154 | |
155 | list_add_tail(&dip->hash_head, &dev_heads[DEV_HASH(device)]); | |
156 | list_add_tail(&dip->all_head, &all_devs); | |
63eba147 JA |
157 | n_devs++; |
158 | } | |
159 | ||
11997716 AB |
160 | if (dip->pre_culling) { |
161 | if (iop->type == IOP_Q || iop->type == IOP_A) | |
162 | dip->pre_culling = 0; | |
163 | else | |
164 | return NULL; | |
165 | } | |
166 | ||
095181f2 | 167 | iop->linked = dip_rb_ins(dip, iop); |
b2822cea | 168 | dip->end_time = BIT_TIME(iop->t.time); |
c8b0b334 | 169 | |
63eba147 JA |
170 | return dip; |
171 | } | |
6eb42155 | 172 | |
c053af42 | 173 | void iop_rem_dip(struct io *iop) |
6eb42155 | 174 | { |
d76c5b81 AB |
175 | if (iop->linked) { |
176 | dip_rb_rem(iop); | |
177 | iop->linked = 0; | |
178 | } | |
6eb42155 ADB |
179 | } |
180 | ||
8b10aae0 | 181 | void dip_foreach(struct io *iop, enum iop_type type, |
6eb42155 ADB |
182 | void (*fnc)(struct io *iop, struct io *this), int rm_after) |
183 | { | |
184 | if (rm_after) { | |
185 | LIST_HEAD(head); | |
186 | struct io *this; | |
187 | struct list_head *p, *q; | |
188 | ||
189 | dip_rb_fe(iop->dip, type, iop, fnc, &head); | |
190 | list_for_each_safe(p, q, &head) { | |
191 | this = list_entry(p, struct io, f_head); | |
8b10aae0 | 192 | list_del(&this->f_head); |
6eb42155 ADB |
193 | io_release(this); |
194 | } | |
c053af42 | 195 | } else |
6eb42155 ADB |
196 | dip_rb_fe(iop->dip, type, iop, fnc, NULL); |
197 | } | |
198 | ||
095181f2 JA |
199 | void dip_foreach_list(struct io *iop, enum iop_type type, struct list_head *hd) |
200 | { | |
201 | dip_rb_fe(iop->dip, type, iop, NULL, hd); | |
202 | } | |
203 | ||
6eb42155 ADB |
204 | struct io *dip_find_sec(struct d_info *dip, enum iop_type type, __u64 sec) |
205 | { | |
206 | return dip_rb_find_sec(dip, type, sec); | |
207 | } | |
208 | ||
209 | void dip_foreach_out(void (*func)(struct d_info *, void *), void *arg) | |
210 | { | |
211 | if (devices == NULL) { | |
212 | struct list_head *p; | |
213 | __list_for_each(p, &all_devs) | |
214 | func(list_entry(p, struct d_info, all_head), arg); | |
c053af42 | 215 | } else { |
6eb42155 ADB |
216 | int i; |
217 | struct d_info *dip; | |
218 | unsigned int mjr, mnr; | |
219 | char *p = devices; | |
220 | ||
221 | while (p && ((i = sscanf(p, "%u,%u", &mjr, &mnr)) == 2)) { | |
222 | dip = __dip_find((__u32)((mjr << MINORBITS) | mnr)); | |
6eb42155 | 223 | func(dip, arg); |
6eb42155 ADB |
224 | p = strchr(p, ';'); |
225 | if (p) p++; | |
226 | } | |
227 | } | |
228 | } | |
b2822cea AB |
229 | |
230 | void dip_plug(__u32 dev, double cur_time) | |
231 | { | |
232 | struct d_info *dip = __dip_find(dev); | |
233 | ||
bb4a6607 AB |
234 | if (dip && !dip->is_plugged) { |
235 | dip->is_plugged = 1; | |
236 | dip->last_plug = cur_time; | |
237 | } | |
238 | } | |
b2822cea | 239 | |
bb4a6607 AB |
240 | static inline void unplug(struct d_info *dip, double cur_time) |
241 | { | |
242 | dip->is_plugged = 0; | |
243 | dip->plugged_time += (cur_time - dip->last_plug); | |
b2822cea AB |
244 | } |
245 | ||
b70a6642 | 246 | void dip_unplug(__u32 dev, double cur_time, __u64 nios_up) |
b2822cea AB |
247 | { |
248 | struct d_info *dip = __dip_find(dev); | |
249 | ||
00a47cd1 AB |
250 | if (dip && dip->is_plugged) { |
251 | dip->nplugs++; | |
b70a6642 | 252 | dip->nios_up += nios_up; |
bb4a6607 | 253 | unplug(dip, cur_time); |
00a47cd1 AB |
254 | } |
255 | } | |
b2822cea | 256 | |
15d67efc | 257 | void dip_unplug_tm(__u32 dev, double cur_time, __u64 nios_up) |
00a47cd1 AB |
258 | { |
259 | struct d_info *dip = __dip_find(dev); | |
b2822cea | 260 | |
b70a6642 | 261 | if (dip && dip->is_plugged) { |
b70a6642 AB |
262 | dip->nios_upt += nios_up; |
263 | dip->nplugs_t++; | |
bb4a6607 | 264 | unplug(dip, cur_time); |
b70a6642 | 265 | } |
b2822cea | 266 | } |
52481561 AB |
267 | |
268 | void dip_cleanup(void) | |
269 | { | |
270 | struct list_head *p, *q; | |
271 | ||
272 | list_for_each_safe(p, q, &all_devs) { | |
273 | struct d_info *dip = list_entry(p, struct d_info, all_head); | |
274 | ||
275 | if (dip->n_qs == 0 && dip->n_ds == 0) | |
276 | __dip_exit(dip); | |
277 | } | |
278 | } |