Fixed plug/unplug logic in btt
[blktrace.git] / btt / devs.c
index fe879425972a5bab41e47a77ea677c4480d71bb8..3578633149c4a55c180c0e35c5d895a7e067b132 100644 (file)
  *
  */
 #include <stdio.h>
-
 #include "globals.h"
 
 #define N_DEV_HASH     128
 #define DEV_HASH(dev)  ((MAJOR(dev) ^ MINOR(dev)) & (N_DEV_HASH - 1))
-struct list_head       *dev_heads = NULL;
+struct list_head       dev_heads[N_DEV_HASH];
+
+static inline void *dip_rb_mkhds(void)
+{
+       size_t len = N_IOP_TYPES * sizeof(struct rb_root);
+       return memset(malloc(len), 0, len);
+}
 
-static void init_dev_heads(void)
+static void __destroy(struct rb_node *n)
+{
+       if (n) {
+               struct io *iop = rb_entry(n, struct io, rb_node);
+
+               __destroy(n->rb_left);
+               __destroy(n->rb_right);
+               io_release(iop);
+       }
+}
+
+static void __destroy_heads(struct rb_root *roots)
 {
        int i;
 
-       dev_heads = zmalloc(N_DEV_HASH * sizeof(struct list_head));
+       for (i = 0; i < N_IOP_TYPES; i++)
+               __destroy(roots[i].rb_node);
+
+       free(roots);
+}
+
+void init_dev_heads(void)
+{
+       int i;
        for (i = 0; i < N_DEV_HASH; i++)
                INIT_LIST_HEAD(&dev_heads[i]);
 }
 
 struct d_info *__dip_find(__u32 device)
 {
-       struct list_head *p;
        struct d_info *dip;
-
-       if (dev_heads == NULL) {
-               init_dev_heads();
-               return NULL;
-       }
+       struct list_head *p;
 
        __list_for_each(p, &dev_heads[DEV_HASH(device)]) {
                dip = list_entry(p, struct d_info, hash_head);
@@ -54,29 +73,209 @@ struct d_info *__dip_find(__u32 device)
        return NULL;
 }
 
-struct d_info *dip_add(__u32 device, struct io *iop)
+void __dip_exit(struct d_info *dip)
+{
+       list_del(&dip->all_head);
+       __destroy_heads(dip->heads);
+       region_exit(&dip->regions);
+       seeki_free(dip->seek_handle);
+       seeki_free(dip->q2q_handle);
+       aqd_free(dip->aqd_handle);
+       plat_free(dip->q2d_plat_handle);
+       plat_free(dip->q2c_plat_handle);
+       plat_free(dip->d2c_plat_handle);
+       bno_dump_free(dip->bno_dump_handle);
+       unplug_hist_free(dip->up_hist_handle);
+       if (output_all_data)
+               q2d_free(dip->q2d_priv);
+       if (dip->pit_fp)
+               fclose(dip->pit_fp);
+       free(dip);
+}
+
+void dip_exit(void)
+{
+       struct list_head *p, *q;
+
+       list_for_each_safe(p, q, &all_devs) {
+               struct d_info *dip = list_entry(p, struct d_info, all_head);
+               __dip_exit(dip);
+       }
+}
+
+static inline char *mkhandle(char *str, __u32 device, char *post)
+{
+       int mjr = device >> MINORBITS;
+       int mnr = device & ((1 << MINORBITS) - 1);
+
+       sprintf(str, "%03d,%03d%s", mjr, mnr, post);
+       return str;
+}
+
+static inline FILE *open_pit(char *str)
+{
+       FILE *fp = my_fopen(str, "w");
+
+       if (fp == NULL)
+               perror(str);
+
+       return fp;
+}
+
+struct d_info *dip_alloc(__u32 device, struct io *iop)
 {
        struct d_info *dip = __dip_find(device);
 
        if (dip == NULL) {
-               int i;
+               char str[256];
 
-               dip = zmalloc(sizeof(*dip));
+               dip = malloc(sizeof(struct d_info));
+               memset(dip, 0, sizeof(*dip));
+               dip->heads = dip_rb_mkhds();
+               region_init(&dip->regions);
                dip->device = device;
                dip->last_q = (__u64)-1;
-               for (i = 0; i < N_IOP_TYPES; i++)
-                       INIT_LIST_HEAD(&dip->iop_heads[i]);
-               init_region(&dip->regions);
-               dip->map = dev_map_find(device);
-
-               if (dev_heads == NULL) init_dev_heads();
+               dip->devmap = dev_map_find(device);
+               dip->bno_dump_handle = bno_dump_alloc(device);
+               dip->up_hist_handle = unplug_hist_alloc(device);
+               dip->seek_handle = seeki_alloc(mkhandle(str, device, "_d2d"));
+               dip->q2q_handle = seeki_alloc(mkhandle(str, device, "_q2q"));
+               dip->aqd_handle = aqd_alloc(mkhandle(str, device, "_aqd"));
+               dip->q2d_plat_handle =
+                               plat_alloc(mkhandle(str, device, "_q2d_plat"));
+               dip->q2c_plat_handle =
+                               plat_alloc(mkhandle(str, device, "_q2c_plat"));
+               dip->d2c_plat_handle =
+                               plat_alloc(mkhandle(str, device, "_d2c_plat"));
+               latency_alloc(dip);
                list_add_tail(&dip->hash_head, &dev_heads[DEV_HASH(device)]);
-
-               list_add_tail(&dip->head, &all_devs);
+               list_add_tail(&dip->all_head, &all_devs);
+               dip->start_time = BIT_TIME(iop->t.time);
+               dip->pre_culling = 1;
+               if (output_all_data)
+                       dip->q2d_priv = q2d_alloc();
                n_devs++;
+               if (per_io_trees)
+                       dip->pit_fp = open_pit(mkhandle(per_io_trees,
+                                                         device, "_pit.dat"));
+       }
+
+       if (dip->pre_culling) {
+               if (iop->type == IOP_Q || iop->type == IOP_A)
+                       dip->pre_culling = 0;
+               else
+                       return NULL;
        }
 
-       list_add(&iop->dev_head, dip_get_head(dip, iop->type));
+       iop->linked = dip_rb_ins(dip, iop);
+       dip->end_time = BIT_TIME(iop->t.time);
 
        return dip;
 }
+
+void iop_rem_dip(struct io *iop)
+{
+       if (iop->linked) {
+               dip_rb_rem(iop);
+               iop->linked = 0;
+       }
+}
+
+void dip_foreach(struct io *iop, enum iop_type type,
+                void (*fnc)(struct io *iop, struct io *this), int rm_after)
+{
+       if (rm_after) {
+               LIST_HEAD(head);
+               struct io *this;
+               struct list_head *p, *q;
+
+               dip_rb_fe(iop->dip, type, iop, fnc, &head);
+               list_for_each_safe(p, q, &head) {
+                       this = list_entry(p, struct io, f_head);
+                       list_del(&this->f_head);
+                       io_release(this);
+               }
+       } else
+               dip_rb_fe(iop->dip, type, iop, fnc, NULL);
+}
+
+void dip_foreach_list(struct io *iop, enum iop_type type, struct list_head *hd)
+{
+       dip_rb_fe(iop->dip, type, iop, NULL, hd);
+}
+
+struct io *dip_find_sec(struct d_info *dip, enum iop_type type, __u64 sec)
+{
+       return dip_rb_find_sec(dip, type, sec);
+}
+
+void dip_foreach_out(void (*func)(struct d_info *, void *), void *arg)
+{
+       if (devices == NULL) {
+               struct list_head *p;
+               __list_for_each(p, &all_devs)
+                       func(list_entry(p, struct d_info, all_head), arg);
+       } else {
+               int i;
+               struct d_info *dip;
+               unsigned int mjr, mnr;
+               char *p = devices;
+
+               while (p && ((i = sscanf(p, "%u,%u", &mjr, &mnr)) == 2)) {
+                       dip = __dip_find((__u32)((mjr << MINORBITS) | mnr));
+                       func(dip, arg);
+                       p = strchr(p, ';');
+                       if (p) p++;
+               }
+       }
+}
+
+void dip_plug(__u32 dev, double cur_time)
+{
+       struct d_info *dip = __dip_find(dev);
+
+       if (dip && !dip->is_plugged) {
+               dip->is_plugged = 1;
+               dip->last_plug = cur_time;
+       }
+}
+
+static inline void unplug(struct d_info *dip, double cur_time)
+{
+       dip->is_plugged = 0;
+       dip->plugged_time += (cur_time - dip->last_plug);
+}
+
+void dip_unplug(__u32 dev, double cur_time, __u64 nios_up)
+{
+       struct d_info *dip = __dip_find(dev);
+
+       if (dip && dip->is_plugged) {
+               dip->nplugs++;
+               dip->nios_up += nios_up;
+               unplug(dip, cur_time);
+       }
+}
+
+void dip_unplug_tm(__u32 dev, double cur_time, __u64 nios_up)
+{
+       struct d_info *dip = __dip_find(dev);
+
+       if (dip && dip->is_plugged) {
+               dip->nios_upt += nios_up;
+               dip->nplugs_t++;
+               unplug(dip, cur_time);
+       }
+}
+
+void dip_cleanup(void)
+{
+       struct list_head *p, *q;
+
+       list_for_each_safe(p, q, &all_devs) {
+               struct d_info *dip = list_entry(p, struct d_info, all_head);
+
+               if (dip->n_qs == 0 && dip->n_ds == 0)
+                       __dip_exit(dip);
+       }
+}