Home | History | Annotate | Download | only in btt
      1 /*
      2  * blktrace output analysis: generate a timeline & gather statistics
      3  *
      4  * Copyright (C) 2006 Alan D. Brunelle <Alan.Brunelle (at) 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>
     22 #include "globals.h"
     23 
     24 #define N_DEV_HASH	128
     25 #define DEV_HASH(dev)	((MAJOR(dev) ^ MINOR(dev)) & (N_DEV_HASH - 1))
     26 struct list_head	dev_heads[N_DEV_HASH];
     27 
     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 
     55 void init_dev_heads(void)
     56 {
     57 	int i;
     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 {
     64 	struct d_info *dip;
     65 	struct list_head *p;
     66 
     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 
     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);
     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);
     87 	bno_dump_free(dip->bno_dump_handle);
     88 	unplug_hist_free(dip->up_hist_handle);
     89 	if (output_all_data)
     90 		q2d_free(dip->q2d_priv);
     91 	if (dip->pit_fp)
     92 		fclose(dip->pit_fp);
     93 	free(dip);
     94 }
     95 
     96 void dip_exit(void)
     97 {
     98 	struct list_head *p, *q;
     99 
    100 	list_for_each_safe(p, q, &all_devs) {
    101 		struct d_info *dip = list_entry(p, struct d_info, all_head);
    102 		__dip_exit(dip);
    103 	}
    104 }
    105 
    106 static inline char *mkhandle(char *str, __u32 device, char *post)
    107 {
    108 	int mjr = device >> MINORBITS;
    109 	int mnr = device & ((1 << MINORBITS) - 1);
    110 
    111 	sprintf(str, "%03d,%03d%s", mjr, mnr, post);
    112 	return str;
    113 }
    114 
    115 static inline FILE *open_pit(char *str)
    116 {
    117 	FILE *fp = my_fopen(str, "w");
    118 
    119 	if (fp == NULL)
    120 		perror(str);
    121 
    122 	return fp;
    123 }
    124 
    125 struct d_info *dip_alloc(__u32 device, struct io *iop)
    126 {
    127 	struct d_info *dip = __dip_find(device);
    128 
    129 	if (dip == NULL) {
    130 		char str[256];
    131 
    132 		dip = malloc(sizeof(struct d_info));
    133 		memset(dip, 0, sizeof(*dip));
    134 		dip->heads = dip_rb_mkhds();
    135 		region_init(&dip->regions);
    136 		dip->device = device;
    137 		dip->last_q = (__u64)-1;
    138 		dip->devmap = dev_map_find(device);
    139 		dip->bno_dump_handle = bno_dump_alloc(device);
    140 		dip->up_hist_handle = unplug_hist_alloc(device);
    141 		dip->seek_handle = seeki_alloc(mkhandle(str, device, "_d2d"));
    142 		dip->q2q_handle = seeki_alloc(mkhandle(str, device, "_q2q"));
    143 		dip->aqd_handle = aqd_alloc(mkhandle(str, device, "_aqd"));
    144 		dip->q2d_plat_handle =
    145 				plat_alloc(mkhandle(str, device, "_q2d_plat"));
    146 		dip->q2c_plat_handle =
    147 				plat_alloc(mkhandle(str, device, "_q2c_plat"));
    148 		dip->d2c_plat_handle =
    149 				plat_alloc(mkhandle(str, device, "_d2c_plat"));
    150 		latency_alloc(dip);
    151 		list_add_tail(&dip->hash_head, &dev_heads[DEV_HASH(device)]);
    152 		list_add_tail(&dip->all_head, &all_devs);
    153 		dip->start_time = BIT_TIME(iop->t.time);
    154 		dip->pre_culling = 1;
    155 		if (output_all_data)
    156 			dip->q2d_priv = q2d_alloc();
    157 		n_devs++;
    158 		if (per_io_trees)
    159 			dip->pit_fp = open_pit(mkhandle(per_io_trees,
    160 							  device, "_pit.dat"));
    161 	}
    162 
    163 	if (dip->pre_culling) {
    164 		if (iop->type == IOP_Q || iop->type == IOP_A)
    165 			dip->pre_culling = 0;
    166 		else
    167 			return NULL;
    168 	}
    169 
    170 	iop->linked = dip_rb_ins(dip, iop);
    171 	dip->end_time = BIT_TIME(iop->t.time);
    172 
    173 	return dip;
    174 }
    175 
    176 void iop_rem_dip(struct io *iop)
    177 {
    178 	if (iop->linked) {
    179 		dip_rb_rem(iop);
    180 		iop->linked = 0;
    181 	}
    182 }
    183 
    184 void dip_foreach(struct io *iop, enum iop_type type,
    185 		 void (*fnc)(struct io *iop, struct io *this), int rm_after)
    186 {
    187 	if (rm_after) {
    188 		LIST_HEAD(head);
    189 		struct io *this;
    190 		struct list_head *p, *q;
    191 
    192 		dip_rb_fe(iop->dip, type, iop, fnc, &head);
    193 		list_for_each_safe(p, q, &head) {
    194 			this = list_entry(p, struct io, f_head);
    195 			list_del(&this->f_head);
    196 			io_release(this);
    197 		}
    198 	} else
    199 		dip_rb_fe(iop->dip, type, iop, fnc, NULL);
    200 }
    201 
    202 void dip_foreach_list(struct io *iop, enum iop_type type, struct list_head *hd)
    203 {
    204 	dip_rb_fe(iop->dip, type, iop, NULL, hd);
    205 }
    206 
    207 struct io *dip_find_sec(struct d_info *dip, enum iop_type type, __u64 sec)
    208 {
    209 	return dip_rb_find_sec(dip, type, sec);
    210 }
    211 
    212 void dip_foreach_out(void (*func)(struct d_info *, void *), void *arg)
    213 {
    214 	if (devices == NULL) {
    215 		struct list_head *p;
    216 		__list_for_each(p, &all_devs)
    217 			func(list_entry(p, struct d_info, all_head), arg);
    218 	} else {
    219 		int i;
    220 		struct d_info *dip;
    221 		unsigned int mjr, mnr;
    222 		char *p = devices;
    223 
    224 		while (p && ((i = sscanf(p, "%u,%u", &mjr, &mnr)) == 2)) {
    225 			dip = __dip_find((__u32)((mjr << MINORBITS) | mnr));
    226 			func(dip, arg);
    227 			p = strchr(p, ';');
    228 			if (p) p++;
    229 		}
    230 	}
    231 }
    232 
    233 void dip_plug(__u32 dev, double cur_time)
    234 {
    235 	struct d_info *dip = __dip_find(dev);
    236 
    237 	if (dip && !dip->is_plugged) {
    238 		dip->is_plugged = 1;
    239 		dip->last_plug = cur_time;
    240 	}
    241 }
    242 
    243 static inline void unplug(struct d_info *dip, double cur_time)
    244 {
    245 	dip->is_plugged = 0;
    246 	dip->plugged_time += (cur_time - dip->last_plug);
    247 }
    248 
    249 void dip_unplug(__u32 dev, double cur_time, __u64 nios_up)
    250 {
    251 	struct d_info *dip = __dip_find(dev);
    252 
    253 	if (dip && dip->is_plugged) {
    254 		dip->nplugs++;
    255 		dip->nios_up += nios_up;
    256 		unplug(dip, cur_time);
    257 	}
    258 }
    259 
    260 void dip_unplug_tm(__u32 dev, double cur_time, __u64 nios_up)
    261 {
    262 	struct d_info *dip = __dip_find(dev);
    263 
    264 	if (dip && dip->is_plugged) {
    265 		dip->nios_upt += nios_up;
    266 		dip->nplugs_t++;
    267 		unplug(dip, cur_time);
    268 	}
    269 }
    270 
    271 void dip_cleanup(void)
    272 {
    273 	struct list_head *p, *q;
    274 
    275 	list_for_each_safe(p, q, &all_devs) {
    276 		struct d_info *dip = list_entry(p, struct d_info, all_head);
    277 
    278 		if (dip->n_qs == 0 && dip->n_ds == 0)
    279 			__dip_exit(dip);
    280 	}
    281 }
    282