[PATCH] BTT patch: (2/3) per-IO stream output
authorJens Axboe <jens.axboe@oracle.com>
Fri, 1 Dec 2006 09:50:45 +0000 (10:50 +0100)
committerJens Axboe <jens.axboe@oracle.com>
Fri, 1 Dec 2006 09:50:45 +0000 (10:50 +0100)
Two major updates:

(1) Added in some robustness - can accept out-of-order traces, and can
"orphan" unfinished IO streams.

(2) Added in ability to put IO streams to a file, sending Q-to-C traces
on a per-IO bases.

The additional robustness comes at some expense (performance), and
so I will look into that next. (Perhaps see what those Judy trees buy
us... :-) )

Signed-off-by: Alan D. Brunelle <Alan.Brunelle@hp.com>
Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
18 files changed:
btt/Makefile
btt/README
btt/args.c
btt/bt_timeline.c
btt/devs.c
btt/dip_rb.c
btt/globals.h
btt/inlines.h
btt/iostat.c
btt/list.h
btt/output.c
btt/trace.c
btt/trace_complete.c [new file with mode: 0644]
btt/trace_im.c [new file with mode: 0644]
btt/trace_issue.c [new file with mode: 0644]
btt/trace_queue.c [new file with mode: 0644]
btt/trace_remap.c [new file with mode: 0644]
btt/trace_requeue.c [new file with mode: 0644]

index 830ceda9b60da9eb2ac16da883f41d8f003e00ba..df65723877245cd08f4f287c3cd35fa25f1cd23a 100644 (file)
@@ -1,5 +1,4 @@
 CC     = gcc
-MYFLAGS = -DLVM_REMAP_WORKAROUND
 CFLAGS = -Wall -O2 -W -g
 #CFLAGS        = -Wall -g -W -UDO_INLINE -DDEBUG
 ALL_CFLAGS = $(CFLAGS) -I.. -D_GNU_SOURCE -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64
@@ -7,8 +6,9 @@ PROGS   = btt
 #ELIBS = -lefence
 #PLIBS = -lpthread
 LIBS   = $(PLIBS) $(ELIBS)
-OBJS   = bt_timeline.o args.o output.o proc.o trace.o misc.o devs.o \
-         devmap.o seek.o iostat.o latency.o dip_rb.o rbtree.o
+OBJS   = args.o bt_timeline.o devmap.o devs.o dip_rb.o iostat.o latency.o \
+         misc.o output.o proc.o seek.o trace.o trace_complete.o trace_im.o \
+         trace_issue.o trace_queue.o trace_remap.o trace_requeue.o rbtree.o
 
 all: depend $(PROGS)
 
index 938f9cece3d39ce04c6281b72afbb462a3169c77..c4f353dcb409ab75ebafec978feaeea9e07647f4 100644 (file)
@@ -16,6 +16,7 @@ Usage: btt
        [ -l <output name> | --d2c-latencies=<output name> ]
        [ -M <dev map>     | --dev-maps=<dev map>
        [ -o <output name> | --output-file=<output name> ]
+       [ -p <output name> | --per-io-dump=<output name> ]
        [ -q <output name> | --q2c-latencies=<output name> ]
        [ -s <output name> | --seeks=<output name> ]
        [ -S <interval>    | --iostat-interval=<interval> ]
@@ -50,6 +51,9 @@ name for each device.
 The -M option takes in a file generated by the provided script
 (gen_disk_info.py), and allows for better output of device names.
 
+The -p option will generate a file that contains a list of all IO
+"sequences" - showing the parts of each IO (Q, A, I/M, D, & C).
+
 The -s option instructs btt to output seek data, the argument provided
 is the basis for file names output. There are two files per device,
 read seeks and write seeks.
index c8a79b297d6a31d926f00ece0ce704f8c47a3d3d..0e8849c5a1f20b39e89931bc8077663de4b35ad6 100644 (file)
@@ -27,7 +27,7 @@
 #include <fcntl.h>
 #include "globals.h"
 
-#define S_OPTS "d:D:e:hi:I:l:M:o:q:s:S:Vv"
+#define S_OPTS "d:D:e:hi:I:l:M:o:p:q:s:S:Vv"
 static struct option l_opts[] = {
        {
                .name = "range-delta",
@@ -83,6 +83,12 @@ static struct option l_opts[] = {
                .flag = NULL,
                .val = 'o'
        },
+       {
+               .name = "per-io-dump",
+               .has_arg = required_argument,
+               .flag = NULL,
+               .val = 'p'
+       },
        {
                .name = "q2c-latencies",
                .has_arg = required_argument,
@@ -128,6 +134,7 @@ static char usage_str[] = \
        "[ -l <output name> | --d2c-latencies=<output name> ]\n" \
        "[ -M <dev map>     | --dev-maps=<dev map>\n" \
        "[ -o <output name> | --output-file=<output name> ]\n" \
+       "[ -p <output name> | --per-io-dump=<output name> ]\n" \
        "[ -q <output name> | --q2c-latencies=<output name> ]\n" \
        "[ -s <output name> | --seeks=<output name> ]\n" \
        "[ -S <interval>    | --iostat-interval=<interval> ]\n" \
@@ -174,6 +181,9 @@ void handle_args(int argc, char *argv[])
                case 'o':
                        output_name = strdup(optarg);
                        break;
+               case 'p':
+                       per_io_name = strdup(optarg);
+                       break;
                case 'q':
                        q2c_name = strdup(optarg);
                        break;
@@ -242,4 +252,12 @@ void handle_args(int argc, char *argv[])
                        exit(1);
                }
        }
+
+       if (per_io_name != NULL) {
+               per_io_ofp = fopen(per_io_name, "w");
+               if (per_io_ofp == NULL) {
+                       perror(per_io_name);
+                       exit(1);
+               }
+       }
 }
index 46d8ad170f76fb8b26c12814d6891bb10556f9d6..1c0855f3d813c4121547ccdae694b17a1c610cb8 100644 (file)
@@ -26,9 +26,9 @@
 char bt_timeline_version[] = "0.99";
 
 char *devices, *exes, *input_name, *output_name, *seek_name;
-char *d2c_name, *q2c_name;
+char *d2c_name, *q2c_name, *per_io_name;
 double range_delta = 0.1;
-FILE *ranges_ofp, *avgs_ofp;
+FILE *ranges_ofp, *avgs_ofp, *per_io_ofp;
 int ifd, verbose = 0;
 unsigned long n_traces;
 struct avgs_info all_avgs;
index bc75dae7e936cd29d3351385801c6c41882fc430..d2df095c61a44aaaf87b0def5728d14786506401 100644 (file)
@@ -46,7 +46,7 @@ struct d_info *__dip_find(__u32 device)
        return NULL;
 }
 
-struct d_info *dip_add(__u32 device, struct io *iop, int link)
+struct d_info *dip_add(__u32 device, struct io *iop)
 {
        struct d_info *dip = __dip_find(device);
 
@@ -66,9 +66,7 @@ struct d_info *dip_add(__u32 device, struct io *iop, int link)
                n_devs++;
        }
 
-       if (link)
-               dip_rb_ins(dip, iop);
-
+       iop->linked = dip_rb_ins(dip, iop);
        return dip;
 }
 
@@ -96,6 +94,11 @@ void dip_foreach(struct io *iop, enum iop_type type,
                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);
index 6ff09dff70116dd44869c219fa83488984eafb32..cfc607f47f57eb7d824a8f2f7ffce0eec45c1c2e 100644 (file)
@@ -21,7 +21,7 @@
 #include <stdio.h>
 #include "globals.h"
 
-void rb_insert(struct rb_root *root, struct io *iop)
+int rb_insert(struct rb_root *root, struct io *iop)
 {
        struct io *__iop;
        struct rb_node *parent = NULL;
@@ -39,11 +39,12 @@ void rb_insert(struct rb_root *root, struct io *iop)
                else if (s > __s)
                        p = &(*p)->rb_right;
                else
-                       p = &(*p)->rb_right;
+                       return 0;
        }
 
        rb_link_node(&iop->rb_node, parent, p);
        rb_insert_color(&iop->rb_node, root);
+       return 1;
 }
 
 struct io *rb_find_sec(struct rb_root *root, __u64 sec)
index 1a785f360b9275ca56bbc39a168b317920331728..b94b25f4158572d11ef2f2a41f942221854bc80a 100644 (file)
@@ -62,11 +62,12 @@ enum iop_type {
        IOP_Q = 0,
        IOP_X = 1,
        IOP_A = 2,
-       IOP_M = 3,
-       IOP_I = 4,
-       IOP_D = 5,
-       IOP_C = 6,
-       IOP_R = 7,
+       IOP_L = 3,      // Betwen-device linkage
+       IOP_M = 4,
+       IOP_I = 5,
+       IOP_D = 6,
+       IOP_C = 7,
+       IOP_R = 8,
 };
 #define N_IOP_TYPES    (IOP_R + 1)
 
@@ -164,19 +165,23 @@ struct io {
        struct blk_io_trace t;
        void *pdu;
        enum iop_type type;
-       int linked;
+
+       struct list_head down_head, up_head, c_pending, retry;
+       struct list_head down_list, up_list;
+       __u64 bytes_left;
+       int run_ready, linked, self_remap, displayed;
 };
 
 /* bt_timeline.c */
 
 extern char bt_timeline_version[], *devices, *exes, *input_name, *output_name;
-extern char *seek_name, *iostat_name, *d2c_name, *q2c_name;
+extern char *seek_name, *iostat_name, *d2c_name, *q2c_name, *per_io_name;
 extern double range_delta;
-extern FILE *ranges_ofp, *avgs_ofp, *iostat_ofp;
-extern int verbose, ifd;
+extern FILE *ranges_ofp, *avgs_ofp, *iostat_ofp, *per_io_ofp;;
+extern int verbose, ifd, dump_level;
 extern unsigned int n_devs;
 extern unsigned long n_traces;
-extern struct list_head all_devs, all_procs;
+extern struct list_head all_devs, all_procs, retries;
 extern struct avgs_info all_avgs;
 extern __u64 last_q;
 extern struct region_info all_regions;
@@ -193,16 +198,17 @@ struct devmap *dev_map_find(__u32 device);
 
 /* devs.c */
 void init_dev_heads(void);
-struct d_info *dip_add(__u32 device, struct io *iop, int link);
+struct d_info *dip_add(__u32 device, struct io *iop);
 void dip_rem(struct io *iop);
 struct d_info *__dip_find(__u32 device);
+void dip_foreach_list(struct io *iop, enum iop_type type, struct list_head *hd);
 void dip_foreach(struct io *iop, enum iop_type type, 
                 void (*fnc)(struct io *iop, struct io *this), int rm_after);
 struct io *dip_find_sec(struct d_info *dip, enum iop_type type, __u64 sec);
 void dip_foreach_out(void (*func)(struct d_info *, void *), void *arg);
 
 /* dip_rb.c */
-void rb_insert(struct rb_root *root, struct io *iop);
+int rb_insert(struct rb_root *root, struct io *iop);
 struct io *rb_find_sec(struct rb_root *root, __u64 sec);
 void rb_foreach(struct rb_node *n, struct io *iop, 
                      void (*fnc)(struct io *iop, struct io *this),
@@ -253,6 +259,45 @@ long long seeki_median(void *handle);
 int seeki_mode(void *handle, struct mode *mp);
 
 /* trace.c */
+void dump_iop(FILE *ofp, struct io *to_iop, struct io *from_iop, int indent);
+void release_iops(struct list_head *del_head);
 void add_trace(struct io *iop);
 
+/* trace_complete.c */
+void trace_complete(struct io *c_iop);
+int retry_complete(struct io *c_iop);
+int ready_complete(struct io *c_iop, struct io *top);
+void run_complete(struct io *c_iop);
+
+/* trace_im.c */
+void trace_insert(struct io *i_iop);
+void trace_merge(struct io *m_iop);
+int ready_im(struct io *im_iop, struct io *top);
+void run_im(struct io *im_iop, struct io *top, struct list_head *del_head);
+void run_unim(struct io *im_iop, struct list_head *del_head);
+
+/* trace_issue.c */
+void trace_issue(struct io *d_iop);
+int ready_issue(struct io *d_iop, struct io *top);
+void run_issue(struct io *d_iop, struct io *top, struct list_head *del_head);
+void run_unissue(struct io *d_iop, struct list_head *del_head);
+
+/* trace_queue.c */
+void trace_queue(struct io *q_iop);
+int ready_queue(struct io *q_iop, struct io *top);
+void run_queue(struct io *q_iop, struct io *top, struct list_head *del_head);
+void run_unqueue(struct io *q_iop, struct list_head *del_head);
+
+/* trace_remap.c */
+void trace_remap(struct io *a_iop);
+int ready_remap(struct io *a_iop, struct io *top);
+void run_remap(struct io *a_iop, struct io *top, struct list_head *del_head);
+void run_unremap(struct io *a_iop, struct list_head *del_head);
+
+/* trace_requeue.c */
+void trace_requeue(struct io *r_iop);
+int retry_requeue(struct io *r_iop);
+int ready_requeue(struct io *r_iop, struct io *top);
+void run_requeue(struct io *r_iop);
+
 #include "inlines.h"
index c8945bfc6aab2e7d5bbee874f4c516c85589e894..4a763b5872e5e928c5aead50d3a2cc24e919e5b6 100644 (file)
@@ -34,7 +34,7 @@ static inline void update_range(struct list_head *head_p,
        if (*cur_p == NULL)
                *cur_p = new_cur(time);
        else {
-               __u64 my_delta = time - (*cur_p)->end;
+               __u64 my_delta = (time > (*cur_p)->end) ? time - (*cur_p)->end : 1;
                if (BIT_TIME(my_delta) >= range_delta) {
                        list_add_tail(&(*cur_p)->head, head_p);
                        *cur_p = new_cur(time);
@@ -83,7 +83,7 @@ static inline void avg_unupdate(struct avg_info *ap, __u64 t)
 static inline void update_lq(__u64 *last_q, struct avg_info *avg, __u64 time)
 {
        if (*last_q != ((__u64)-1))
-               avg_update(avg, time - *last_q);
+               avg_update(avg, (time > *last_q) ? time - *last_q : 1);
        *last_q = time;
 }
 
@@ -104,28 +104,37 @@ static inline struct io *io_alloc(void)
        else
                iop = malloc(sizeof(struct io));
 
-       return memset(iop, 0, sizeof(struct io));
+       memset(iop, 0, sizeof(struct io));
+
+       return iop;
 }
 
 static inline void io_free(struct io *iop)
 {
+#      if defined(DEBUG)
+               memset(iop, 0, sizeof(*iop));
+#      endif
        list_add_tail(&iop->f_head, &free_ios);
 }
 
-static inline void io_setup(struct io *iop, enum iop_type type, int link)
+static inline int io_setup(struct io *iop, enum iop_type type)
 {
        iop->type = type;
-       iop->dip = dip_add(iop->t.device, iop, link);
-       iop->pip = find_process(iop->t.pid, NULL);
-       iop->linked = link;
+       iop->dip = dip_add(iop->t.device, iop);
+       if (iop->linked) {
+               iop->pip = find_process(iop->t.pid, NULL);
+               INIT_LIST_HEAD(&iop->down_list);
+               INIT_LIST_HEAD(&iop->up_list);
+               iop->bytes_left = iop->t.bytes;
+       }
+
+       return iop->linked;
 }
 
 static inline void io_release(struct io *iop)
 {
-       if (iop->linked) {
+       if (iop->linked)
                dip_rem(iop);
-               iop->linked = 0;
-       }
        if (iop->pdu) 
                free(iop->pdu);
        io_free(iop);
@@ -195,9 +204,9 @@ static inline void *dip_rb_mkhds(void)
        return memset(malloc(len), 0, len);
 }
 
-static inline void dip_rb_ins(struct d_info *dip, struct io *iop)
+static inline int dip_rb_ins(struct d_info *dip, struct io *iop)
 {
-       rb_insert(__get_root(dip, iop->type), iop);
+       return rb_insert(__get_root(dip, iop->type), iop);
 }
 
 static inline void dip_rb_rem(struct io *iop)
@@ -218,3 +227,49 @@ static inline struct io *dip_rb_find_sec(struct d_info *dip,
 {
        return rb_find_sec(__get_root(dip, type), sec);
 }
+
+static inline struct io *list_first_down(struct io *iop)
+{
+       struct list_head *p = list_first(&iop->down_list);
+       return p ? list_entry(p, struct io, up_head) : NULL;
+}
+
+static inline struct io *list_first_up(struct io *iop)
+{
+       struct list_head *p = list_first(&iop->up_list);
+       return p ? list_entry(p, struct io, down_head) : NULL;
+}
+
+static inline int list_empty_up(struct io *iop)
+{
+       return list_empty(&iop->up_list);
+}
+
+static inline void __link(struct io *down_iop, struct io *up_iop)
+{
+       list_add_tail(&down_iop->up_head, &up_iop->down_list);
+       list_add_tail(&up_iop->down_head, &down_iop->up_list);
+}
+
+static inline void __unlink(struct io *down_iop, struct io *up_iop)
+{
+       LIST_DEL(&down_iop->up_head);
+       LIST_DEL(&up_iop->down_head);
+}
+
+static inline void add_retry(struct io *iop)
+{
+       list_add_tail(&iop->retry, &retries);
+}
+
+static inline void del_retry(struct io *iop)
+{
+       LIST_DEL(&iop->retry);
+}
+
+static inline __u64 tdelta(struct io *iop1, struct io *iop2)
+{
+       __u64 t1 = iop1->t.time;
+       __u64 t2 = iop2->t.time;
+       return (t1 < t2) ? (t2 - t1) : 1;
+}
index f2c54dc6e891af1608abb53cacc159208a261087..360d972236bd58d8eda778a7673d7746860c805d 100644 (file)
@@ -62,7 +62,7 @@ void dump_hdr(void)
 
 void im2d2c_func(struct io *c_iop, struct io *im_iop)
 {
-       ADD_STAT(c_iop->dip, wait, c_iop->t.time - im_iop->t.time);
+       ADD_STAT(c_iop->dip, wait, tdelta(im_iop, c_iop));
 }
 
 void iostat_init(void)
@@ -295,5 +295,5 @@ void iostat_complete(struct io *d_iop, struct io *c_iop)
        update_idle_time(dip, now, 0);
        DEC_STAT(dip, cur_dev);
 
-       ADD_STAT(dip, svctm, c_iop->t.time - d_iop->t.time);
+       ADD_STAT(dip, svctm, tdelta(d_iop, c_iop));
 }
index b165ecf80b8db696064d5775ae57a3c88aca65dc..f28f0fb2050bd831efd6268e49c588258348ea5d 100644 (file)
@@ -145,4 +145,25 @@ static inline int list_empty(const struct list_head *head)
        return head->next == head;
 }
 
+/**
+ * list_first - Returns first entry on list, or NULL if empty
+ * @head: the list
+ */
+static inline struct list_head *list_first(const struct list_head *head)
+{
+       return list_empty(head) ? NULL : head->next;
+}
+
+/**
+ * list_move_tail - delete from one list and add as another's tail
+ * @list: the entry to move
+ * @head: the head that will follow our entry
+ */
+static inline void list_move_tail(struct list_head *list,
+                                 struct list_head *head)
+{
+        __list_del(list->prev, list->next);
+        list_add_tail(list, head);
+}
+
 #endif
index 58bd595aa6f1672ceb86f4a457afbd1e886fd551..db8a086f7ad6dabe57f2e1f9ea6e1066ee71425e 100644 (file)
@@ -46,24 +46,24 @@ void output_section_hdr(FILE *ofp, char *hdr)
 
 void output_hdr(FILE *ofp, char *hdr)
 {
-       fprintf(ofp, "%12s %13s %13s %13s %11s\n",
+       fprintf(ofp, "%15s %13s %13s %13s %11s\n",
                hdr, "MIN", "AVG", "MAX", "N" );
-       fprintf(ofp, "------------ ------------- ------------- ------------- -----------\n");
+       fprintf(ofp, "--------------- ------------- ------------- ------------- -----------\n");
 }
 
 void __output_avg(FILE *ofp, char *hdr, struct avg_info *ap)
 {
        if (ap->n > 0) {
                ap->avg = BIT_TIME(ap->total) / (double)ap->n;
-               fprintf(ofp, "%-12s %13.9f %13.9f %13.9f %11d\n", hdr,
+               fprintf(ofp, "%-15s %13.9f %13.9f %13.9f %11d\n", hdr,
                        BIT_TIME(ap->min), ap->avg, BIT_TIME(ap->max), ap->n);
        }
 }
 
 void output_hdr2(FILE *ofp, char*hdr)
 {
-       fprintf(ofp, "%12s %13s %13s %13s %13s %13s %13s\n", hdr, "Q2Q", "Q2A", "Q2I", "I2D", "D2C", "Q2C");
-       fprintf(ofp, "------------ ------------- ------------- ------------- ------------- ------------- -------------\n");
+       fprintf(ofp, "%15s %13s %13s %13s %13s %13s %13s\n", hdr, "Q2Q", "Q2A", "Q2I", "I2D", "D2C", "Q2C");
+       fprintf(ofp, "--------------- ------------- ------------- ------------- ------------- ------------- -------------\n");
 }
 
 static inline char *avg2string(struct avg_info *ap, char *string)
@@ -81,7 +81,7 @@ void __output_avg2(FILE *ofp, char *hdr, struct avgs_info *ap)
 
        if (ap->q2q.n > 0 || ap->q2a.n > 0 || ap->q2i.n > 0 ||
                        ap->i2d.n > 0 || ap->d2c.n > 0 || ap->q2c.n > 0) {
-               fprintf(ofp, "%-12s %13s %13s %13s %13s %13s %13s\n", hdr,
+               fprintf(ofp, "%-15s %13s %13s %13s %13s %13s %13s\n", hdr,
                        avg2string(&ap->q2q,c1), avg2string(&ap->q2a,c2),
                        avg2string(&ap->q2i,c3), avg2string(&ap->i2d,c4),
                        avg2string(&ap->d2c,c5), avg2string(&ap->q2c,c6));
@@ -95,8 +95,8 @@ void __pip_output_avg2(struct p_info *pip, void *arg)
 
 void __dip_output_avg2(struct d_info *dip, void *arg)
 {
-       char dev_info[12];
-       __output_avg2((FILE *)arg, make_dev_hdr(dev_info, 12, dip), &dip->avgs);
+       char dev_info[15];
+       __output_avg2((FILE *)arg, make_dev_hdr(dev_info, 15, dip), &dip->avgs);
 }
 
 char *make_dev_hdr(char *pad, size_t len, struct d_info *dip)
@@ -119,9 +119,9 @@ void __output_dip_avg(struct d_info *dip, void *arg)
        struct __oda *odap = arg;
        ai_dip_t ap = odap->func(dip);
        if (ap->n > 0) {
-               char dev_info[12];
+               char dev_info[15];
                ap->avg = BIT_TIME(ap->total) / (double)ap->n;
-               __output_avg(odap->ofp, make_dev_hdr(dev_info, 12, dip), ap);
+               __output_avg(odap->ofp, make_dev_hdr(dev_info, 15, dip), ap);
        }
 }
 
@@ -136,7 +136,7 @@ void output_dip_avg(FILE *ofp, char *hdr, ai_dip_t (*func)(struct d_info *))
 void __output_dip_merge_ratio(struct d_info *dip, void *arg)
 {
        double blks_avg;
-       char scratch[12];
+       char scratch[15];
        double ratio, q2c_n = dip->avgs.q2c.n, d2c_n = dip->n_ds;
 
        if (q2c_n > 0.0 && d2c_n > 0.0) {
@@ -144,7 +144,7 @@ void __output_dip_merge_ratio(struct d_info *dip, void *arg)
                blks_avg = (double)dip->avgs.blks.total / d2c_n;
                fprintf((FILE *)arg, 
                        "%10s | %8llu %8llu %7.1lf | %8llu %8llu %8llu %8llu\n",
-                       make_dev_hdr(scratch, 12, dip),
+                       make_dev_hdr(scratch, 15, dip),
                        (unsigned long long)dip->avgs.q2c.n,
                        (unsigned long long)dip->n_ds,
                        ratio,
@@ -206,7 +206,7 @@ char *d2c_v_q2C(struct d_info *dip, char *s)
 
 void __output_dip_prep_ohead(struct d_info *dip, void *arg)
 {
-       char dev_info[12];
+       char dev_info[15];
        char s1[16], s2[16], s3[16];
 
        if ((dip->avgs.q2i.n > 0 && dip->avgs.i2d.n > 0 &&
@@ -216,7 +216,7 @@ void __output_dip_prep_ohead(struct d_info *dip, void *arg)
                CALC_AVG(&dip->avgs.d2c);
 
                fprintf((FILE *)arg, "%10s | %6s %6s %6s\n",
-                       make_dev_hdr(dev_info, 12, dip),
+                       make_dev_hdr(dev_info, 15, dip),
                        q2i_v_q2C(dip, s1), i2d_v_q2C(dip, s2),
                        d2c_v_q2C(dip, s3));
        }
@@ -235,7 +235,7 @@ void __output_dip_seek_info(struct d_info *dip, void *arg)
        double mean;
        int i, nmodes;
        long long nseeks;
-       char dev_info[12];
+       char dev_info[15];
        long long median;
        struct mode m;
        FILE *ofp = arg;
@@ -247,7 +247,7 @@ void __output_dip_seek_info(struct d_info *dip, void *arg)
                nmodes = seeki_mode(dip->seek_handle, &m);
 
                fprintf(ofp, "%10s | %15lld %15.1lf %15lld | %lld(%d)",
-                       make_dev_hdr(dev_info, 12, dip), nseeks, mean, median, 
+                       make_dev_hdr(dev_info, 15, dip), nseeks, mean, median, 
                        nmodes > 0 ? m.modes[0] : 0, m.most_seeks);
                for (i = 1; i < nmodes; i++)
                        fprintf(ofp, " %lld", m.modes[i]);
@@ -277,8 +277,8 @@ void __output_pip_avg(struct p_info *pip, void *arg)
        ai_pip_t ap = opap->func(pip);
 
        if (ap->n > 0) {
-               char proc_name[12];
-               snprintf(proc_name, 12, pip->name);
+               char proc_name[15];
+               snprintf(proc_name, 15, pip->name);
 
                ap->avg = BIT_TIME(ap->total) / (double)ap->n;
                __output_avg(opap->ofp, proc_name, ap);
index abb148857ad11db2405354e051106e3036c52524..4c57b5df202b02e91e530cf8b64f6adc1dd19962 100644 (file)
  */
 #include "globals.h"
 
-void im2d_func(struct io *d_iop, struct io *im_iop)
-{
-       update_i2d(im_iop, d_iop->t.time - im_iop->t.time);
-}
+int dump_level;
+LIST_HEAD(retries);
 
-void q2c_func(struct io *c_iop, struct io *q_iop)
+static inline void dump_dev(FILE *ofp, __u32 dev)
 {
-       __u64 q2c = c_iop->t.time - q_iop->t.time;
-
-       update_q2c(q_iop, q2c);
-       latency_q2c(q_iop->dip, q_iop->t.time, q2c);
+       fprintf(ofp, "%3d,%-3d ", MAJOR(dev), MINOR(dev));
 }
 
-static inline void handle_im(struct io *im_iop)
+static inline void dump_desc(FILE *ofp, struct io *iop)
 {
-       struct io *q_iop;
-
-       q_iop = dip_find_sec(im_iop->dip, IOP_Q, BIT_START(im_iop));
-       if (q_iop)
-               update_q2i(q_iop, im_iop->t.time - q_iop->t.time);
+       fprintf(ofp, "%10llu+%-4u ", (unsigned long long)iop->t.sector, 
+               t_sec(&iop->t));
 }
 
-void handle_queue(struct io *q_iop)
+void dump_iop(FILE *ofp, struct io *to_iop, struct io *from_iop, int indent)
 {
-       io_setup(q_iop, IOP_Q, 1);
-       update_lq(&last_q, &all_avgs.q2q, q_iop->t.time);
-       update_qregion(&all_regions, q_iop->t.time);
-       dip_update_q(q_iop->dip, q_iop);
-       pip_update_q(q_iop);
-}
+       int i, c;
 
-void handle_remap(struct io *a_iop)
-{
-       struct io *q_iop;
-       struct blk_io_trace_remap *rp = a_iop->pdu;
-       struct d_info *dip = __dip_find(be32_to_cpu(rp->device));
-
-       io_setup(a_iop, IOP_A, 0);
-       if (dip) {
-               q_iop = dip_find_sec(dip, IOP_Q, be64_to_cpu(rp->sector));
-               if (q_iop)
-                       update_q2a(q_iop, a_iop->t.time - q_iop->t.time);
-       }
-       io_release(a_iop);
-}
-
-void handle_insert(struct io *i_iop)
-{
-       io_setup(i_iop, IOP_I, 1);
-       iostat_insert(i_iop);
-       handle_im(i_iop);
-}
+       if (!ofp) return;
+       if (to_iop->displayed) return;
 
-void handle_merge(struct io *m_iop)
-{
-       io_setup(m_iop, IOP_M, 1);
-       iostat_merge(m_iop);
-       handle_im(m_iop);
-}
+       fprintf(ofp, "%5d.%09lu ", (int)SECONDS(to_iop->t.time),
+               (unsigned long)NANO_SECONDS(to_iop->t.time));
 
-void handle_issue(struct io *d_iop)
-{
-       io_setup(d_iop, IOP_D, 1);
-       d_iop->dip->n_ds++;
+       for (i = 0; i < ((dump_level * 4) + indent); i++)
+               fprintf(ofp, " ");
 
-       dip_foreach(d_iop, IOP_I, im2d_func, 0);
-       dip_foreach(d_iop, IOP_M, im2d_func, 0);
+       dump_dev(ofp, to_iop->t.device);
 
-       if (seek_name)
-               seeki_add(d_iop->dip->seek_handle, d_iop);
-       iostat_issue(d_iop);
-}
+       switch (to_iop->type) {
+       case IOP_Q: c = 'Q'; break;
+       case IOP_L: c = 'L'; break;
+       case IOP_A: c = 'A'; break;
+       case IOP_I: c = 'I'; break;
+       case IOP_M: c = 'M'; break;
+       case IOP_D: c = 'D'; break;
+       case IOP_C: c = 'C'; break;
+       default   : c = '?'; break;
+       }
 
-void handle_complete(struct io *c_iop)
-{
-       struct io *d_iop;
-
-       io_setup(c_iop, IOP_C, 0);
-       update_blks(c_iop);
-       update_cregion(&all_regions, c_iop->t.time);
-       update_cregion(&c_iop->dip->regions, c_iop->t.time);
-       if (c_iop->pip)
-               update_cregion(&c_iop->pip->regions, c_iop->t.time);
-
-       d_iop = dip_find_sec(c_iop->dip, IOP_D, BIT_START(c_iop));
-       if (d_iop) {
-               __u64 d2c = c_iop->t.time - d_iop->t.time;
-               update_d2c(d_iop, d2c);
-               latency_d2c(d_iop->dip, c_iop->t.time, d2c);
-               iostat_complete(d_iop, c_iop);
-               dip_foreach(d_iop, IOP_I, NULL, 1);
-               dip_foreach(d_iop, IOP_M, NULL, 1);
-               io_release(d_iop);
+       fprintf(ofp, "%c ", c);
+       dump_desc(ofp, to_iop);
+       if (from_iop) {
+               fprintf(ofp, "<- ");
+               dump_dev(ofp, from_iop->t.device);
+               dump_desc(ofp, from_iop);
        }
+               
+       fprintf(ofp, "\n");
 
-       dip_foreach(c_iop, IOP_Q, q2c_func, 1);
-       io_release(c_iop);
+       to_iop->displayed = 1;
 }
 
-void rq_im2d_func(struct io *d_iop, struct io *im_iop)
+void release_iops(struct list_head *del_head)
 {
-       unupdate_i2d(im_iop, d_iop->t.time - im_iop->t.time);
+       struct io *x_iop;
+       struct list_head *p, *q;
+
+       list_for_each_safe(p, q, del_head) {
+               x_iop = list_entry(p, struct io, f_head);
+               LIST_DEL(&x_iop->f_head);
+               io_release(x_iop);
+       }
 }
 
-/*
- * Careful surgery
- * (1) Need to remove D & its I & M's
- * (2) Need to leave I's Q and M's Q's
- * (3) XXX: Need to downward adjust stats, but we don't carry PREVIOUS
- *     XXX: min/maxes?! We'll just adjust what we can, and hope that 
- *     XXX: the min/maxes are "pretty close". (REQUEUEs are rare, right?)
- */
-void handle_requeue(struct io *r_iop)
+static void do_retries(void)
 {
-       struct io *d_iop;
-
-       io_setup(r_iop, IOP_R, 0);
-       d_iop = dip_find_sec(r_iop->dip, IOP_D, BIT_START(r_iop));
-       if (d_iop) {
-               dip_foreach(d_iop, IOP_I, rq_im2d_func, 1);
-               dip_foreach(d_iop, IOP_M, rq_im2d_func, 1);
-               iostat_unissue(d_iop);
-               io_release(d_iop);
+       struct io *iop;
+       struct list_head *p, *q;
+
+       list_for_each_safe(p, q, &retries) {
+               iop = list_entry(p, struct io, retry);
+               // iop could be gone after call...
+               if (iop->type == IOP_C) 
+                       retry_complete(iop);
+               else
+                       retry_requeue(iop);
        }
-       io_release(r_iop);
 }
 
-void __add_trace(struct io *iop)
+static void __add_trace(struct io *iop)
 {
        time_t now = time(NULL);
 
@@ -162,20 +115,27 @@ void __add_trace(struct io *iop)
        }
 
        switch (iop->t.action & 0xffff) {
-       case __BLK_TA_QUEUE:            handle_queue(iop); break;
-       case __BLK_TA_BACKMERGE:        handle_merge(iop); break;
-       case __BLK_TA_FRONTMERGE:       handle_merge(iop); break;
-       case __BLK_TA_ISSUE:            handle_issue(iop); break;
-       case __BLK_TA_COMPLETE:         handle_complete(iop); break;
-       case __BLK_TA_INSERT:           handle_insert(iop); break;
-       case __BLK_TA_REMAP:            handle_remap(iop); break;
-       case __BLK_TA_REQUEUE:          handle_requeue(iop); break;
-       default:                        io_release(iop); break;
+       case __BLK_TA_QUEUE:            trace_queue(iop); break;
+       case __BLK_TA_REMAP:            trace_remap(iop); break;
+       case __BLK_TA_INSERT:           trace_insert(iop); break;
+       case __BLK_TA_BACKMERGE:        trace_merge(iop); break;
+       case __BLK_TA_FRONTMERGE:       trace_merge(iop); break;
+       case __BLK_TA_REQUEUE:          trace_requeue(iop); break;
+       case __BLK_TA_ISSUE:            trace_issue(iop); break;
+       case __BLK_TA_COMPLETE:         trace_complete(iop); break;
+       default:                        
+               io_release(iop); 
+               return;
        }
+
+       if (((iop->t.action & 0xffff) != __BLK_TA_REQUEUE) && 
+                                               !list_empty(&retries))
+               do_retries();
 }
 
 void add_trace(struct io *iop)
 {
+       if (iop->t.time == 15717167961) dbg_ping();
        if (iop->t.action & BLK_TC_ACT(BLK_TC_NOTIFY)) {
                char *slash = strchr(iop->pdu, '/');
 
diff --git a/btt/trace_complete.c b/btt/trace_complete.c
new file mode 100644 (file)
index 0000000..d8e7b5a
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ * blktrace output analysis: generate a timeline & gather statistics
+ *
+ * Copyright (C) 2006 Alan D. Brunelle <Alan.Brunelle@hp.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+#include "globals.h"
+
+LIST_HEAD(pending_cs);
+
+static void gen_c_list(struct io *c_iop, struct list_head *c_head)
+{
+       struct io *iop;
+       struct list_head *p;
+
+       __list_for_each(p, &pending_cs) {
+               iop = list_entry(p, struct io, c_pending);
+               if (iop->t.device == c_iop->t.device)
+                       continue;
+               if (dip_find_sec(iop->dip, IOP_D, BIT_START(iop)) == NULL)
+                       continue;
+
+               __link(iop, c_iop);
+               if (ready_complete(iop, c_iop))
+                       list_add_tail(&iop->f_head, c_head);
+               __unlink(iop, c_iop);
+       }
+}
+
+static void run_comp(struct io *c_iop, struct io *top, struct list_head *rmhd)
+{
+       struct io *d_iop = dip_find_sec(c_iop->dip, IOP_D, BIT_START(c_iop));
+
+       update_blks(c_iop);
+       if (d_iop) {
+               __u64 d2c = tdelta(d_iop, c_iop);
+
+               update_d2c(d_iop, d2c);
+               latency_d2c(d_iop->dip, c_iop->t.time, d2c);
+               iostat_complete(d_iop, c_iop);
+
+               __link(d_iop, c_iop);
+               run_issue(d_iop, top, rmhd);
+               __unlink(d_iop, c_iop);
+       }
+       else {
+               LIST_HEAD(head);
+               struct io *iop;
+               struct list_head *p, *q;
+
+               gen_c_list(c_iop, &head);
+               list_for_each_safe(p, q, &head) {
+                       iop = list_entry(p, struct io, f_head);
+                       LIST_DEL(&iop->f_head);
+
+                       dump_level++;
+                       __link(iop, c_iop);
+                       run_comp(iop, top, rmhd);
+                       __unlink(iop, c_iop);
+                       dump_level--;
+               }
+       }
+       dump_iop(per_io_ofp, c_iop, NULL, 0);
+       LIST_DEL(&c_iop->c_pending);
+       list_add_tail(&c_iop->f_head, rmhd);
+}
+
+static int ready_comp(struct io *c_iop, 
+                               __attribute__((__unused__)) struct io *top)
+{
+       LIST_HEAD(head);
+       struct io *iop;
+       struct list_head *p, *q;
+       __u64 bl = c_iop->bytes_left;
+
+       gen_c_list(c_iop, &head);
+       list_for_each_safe(p, q, &head) {
+               iop = list_entry(p, struct io, f_head);
+               LIST_DEL(&iop->f_head);
+
+               __link(iop, c_iop);
+               if (ready_complete(iop, c_iop))
+                       bl -= iop->bytes_left;
+               __unlink(iop, c_iop);
+       }
+
+       return bl == 0;
+}
+
+void trace_complete(struct io *c_iop)
+{
+       if (!io_setup(c_iop, IOP_C)) {
+               io_release(c_iop);
+               return;
+       }
+
+       list_add_tail(&c_iop->c_pending, &pending_cs);
+       if (ready_complete(c_iop, c_iop)) {
+               dump_level = 0;
+               run_complete(c_iop);
+       }
+       else 
+               add_retry(c_iop);
+}
+
+int retry_complete(struct io *c_iop)
+{
+       if (!ready_complete(c_iop, c_iop))
+               return 0;
+
+       del_retry(c_iop);
+       run_complete(c_iop);
+       return 1;
+}
+
+int ready_complete(struct io *c_iop, struct io *top)
+{
+       struct io *d_iop = dip_find_sec(c_iop->dip, IOP_D, BIT_START(c_iop));
+
+       if (d_iop) {
+               ASSERT(d_iop->t.bytes == c_iop->bytes_left);
+               return ready_issue(d_iop, top);
+       }
+       else 
+               return ready_comp(c_iop, top);
+}
+
+void run_complete(struct io *c_iop)
+{
+       LIST_HEAD(rmhd);
+
+       update_cregion(&all_regions, c_iop->t.time);
+       update_cregion(&c_iop->dip->regions, c_iop->t.time);
+       if (c_iop->pip)
+               update_cregion(&c_iop->pip->regions, c_iop->t.time);
+
+       run_comp(c_iop, c_iop, &rmhd);
+       if (per_io_ofp) fprintf(per_io_ofp, "\n");
+       release_iops(&rmhd);
+}
diff --git a/btt/trace_im.c b/btt/trace_im.c
new file mode 100644 (file)
index 0000000..eecd32a
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * blktrace output analysis: generate a timeline & gather statistics
+ *
+ * Copyright (C) 2006 Alan D. Brunelle <Alan.Brunelle@hp.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+#include "globals.h"
+
+void trace_insert(struct io *i_iop)
+{
+       if (!io_setup(i_iop, IOP_I)) {
+               io_release(i_iop);
+               return;
+       }
+       iostat_insert(i_iop);
+}
+
+void trace_merge(struct io *m_iop)
+{
+       if (!io_setup(m_iop, IOP_M)) {
+               io_release(m_iop);
+               return;
+       }
+       iostat_merge(m_iop);
+}
+
+int ready_im(struct io *im_iop, struct io *top)
+{
+       struct io *q_iop = dip_find_sec(im_iop->dip, IOP_Q, BIT_START(im_iop));
+
+       if (q_iop) {
+               ASSERT(q_iop->bytes_left >= im_iop->bytes_left);
+               return ready_queue(q_iop, top);
+       }
+
+       return 0;
+}
+
+void run_im(struct io *im_iop, struct io *top, struct list_head *del_head)
+{
+       struct io *q_iop = dip_find_sec(im_iop->dip, IOP_Q, BIT_START(im_iop));
+
+       ASSERT(q_iop);
+       update_q2i(q_iop, tdelta(q_iop, im_iop));
+
+       __link(q_iop, im_iop);
+       run_queue(q_iop, top, del_head);
+       __unlink(q_iop, im_iop);
+
+       dump_iop(per_io_ofp, im_iop, NULL, 0);
+       list_add_tail(&im_iop->f_head, del_head);
+}
+
+void run_unim(struct io *im_iop, struct list_head *del_head)
+{
+       struct io *q_iop = dip_find_sec(im_iop->dip, IOP_Q, BIT_START(im_iop));
+
+       __link(q_iop, im_iop);
+       run_unqueue(q_iop, del_head);
+       __unlink(q_iop, im_iop);
+
+       list_add_tail(&im_iop->f_head, del_head);
+}
diff --git a/btt/trace_issue.c b/btt/trace_issue.c
new file mode 100644 (file)
index 0000000..17bfae0
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * blktrace output analysis: generate a timeline & gather statistics
+ *
+ * Copyright (C) 2006 Alan D. Brunelle <Alan.Brunelle@hp.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+#include "globals.h"
+
+void trace_issue(struct io *d_iop)
+{
+       if (!io_setup(d_iop, IOP_D)) {
+               io_release(d_iop);
+               return;
+       }
+
+       if (seek_name)
+               seeki_add(d_iop->dip->seek_handle, d_iop);
+       iostat_issue(d_iop);
+       d_iop->dip->n_ds++;
+}
+
+int ready_issue(struct io *d_iop, struct io *top)
+{
+       LIST_HEAD(head);
+       struct io *im_iop;
+       struct list_head *p, *q;
+       __u64 bl = d_iop->bytes_left;
+
+       dip_foreach_list(d_iop, IOP_I, &head);
+       dip_foreach_list(d_iop, IOP_M, &head);
+       list_for_each_safe(p, q, &head) {
+               im_iop = list_entry(p, struct io, f_head);
+               LIST_DEL(&im_iop->f_head);
+
+               if (!ready_im(im_iop, top))
+                       return 0;
+
+               bl -= im_iop->bytes_left;
+       }
+
+       return bl == 0;
+}
+
+void run_issue(struct io *d_iop, struct io *top, struct list_head *del_head)
+{
+       LIST_HEAD(head);
+       struct list_head *p, *q;
+       struct io *im_iop;
+
+       dip_foreach_list(d_iop, IOP_I, &head);
+       dip_foreach_list(d_iop, IOP_M, &head);
+       list_for_each_safe(p, q, &head) {
+               im_iop = list_entry(p, struct io, f_head);
+               LIST_DEL(&im_iop->f_head);
+
+               update_i2d(im_iop, tdelta(im_iop, d_iop));
+
+               __link(im_iop, d_iop);
+               run_im(im_iop, top, del_head);
+               __unlink(im_iop, d_iop);
+       }
+
+       dump_iop(per_io_ofp, d_iop, NULL, 0);
+       list_add_tail(&d_iop->f_head, del_head);
+}
+
+void run_unissue(struct io *d_iop, struct list_head *del_head)
+{
+       LIST_HEAD(head);
+       struct io *im_iop;
+       struct list_head *p, *q;
+
+       iostat_unissue(d_iop);
+
+       dip_foreach_list(d_iop, IOP_I, &head);
+       dip_foreach_list(d_iop, IOP_M, &head);
+       list_for_each_safe(p, q, &head) {
+               im_iop = list_entry(p, struct io, f_head);
+               LIST_DEL(&im_iop->f_head);
+
+               __link(im_iop, d_iop);
+               unupdate_i2d(im_iop, tdelta(im_iop, d_iop));
+               run_unim(im_iop, del_head);
+               __unlink(im_iop, d_iop);
+       }
+
+       list_add_tail(&d_iop->f_head, del_head);
+}
diff --git a/btt/trace_queue.c b/btt/trace_queue.c
new file mode 100644 (file)
index 0000000..d32e159
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * blktrace output analysis: generate a timeline & gather statistics
+ *
+ * Copyright (C) 2006 Alan D. Brunelle <Alan.Brunelle@hp.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+#include "globals.h"
+
+void trace_queue(struct io *q_iop)
+{
+       if (!io_setup(q_iop, IOP_Q)) {
+               io_release(q_iop);
+               return;
+       }
+
+       update_lq(&last_q, &all_avgs.q2q, q_iop->t.time);
+       update_qregion(&all_regions, q_iop->t.time);
+       dip_update_q(q_iop->dip, q_iop);
+       pip_update_q(q_iop);
+}
+
+int ready_queue(struct io *q_iop, struct io *top)
+{
+       struct io *a_iop = dip_find_sec(q_iop->dip, IOP_A, BIT_START(q_iop));
+
+       if (a_iop) {
+               ASSERT(a_iop->bytes_left == q_iop->bytes_left);
+               return ready_remap(a_iop, top);
+       }
+
+       return q_iop->t.device == top->t.device &&
+              BIT_START(top) <= BIT_START(q_iop) &&
+                                BIT_END(q_iop) <= BIT_END(top);
+}
+
+void run_queue(struct io *q_iop, struct io *top, struct list_head *del_head)
+{
+       struct io *iop;
+       struct io *a_iop = dip_find_sec(q_iop->dip, IOP_A, BIT_START(q_iop));
+
+       if (a_iop) {
+               __link(a_iop, q_iop);
+               run_remap(a_iop, top, del_head);
+               __unlink(a_iop, q_iop);
+       }
+
+       for (iop = q_iop; iop != NULL; iop = list_first_up(iop)) {
+               if (iop->type == IOP_C && iop->t.device == q_iop->t.device) {
+                       __u64 q2c = tdelta(q_iop, iop);
+
+                       update_q2c(q_iop, q2c);
+                       latency_q2c(q_iop->dip, q_iop->t.time, q2c);
+
+                       dump_iop(per_io_ofp, q_iop, NULL, 
+                                (q_iop->t.device == top->t.device) ? -4 : 0);
+
+                       break;
+               }
+       }
+
+       iop = list_first_up(q_iop);
+       q_iop->bytes_left -= iop->bytes_left;
+       if (q_iop->bytes_left == 0)
+               list_add_tail(&q_iop->f_head, del_head);
+}
+
+void run_unqueue(struct io *q_iop, struct list_head *del_head)
+{
+       struct io *a_iop = dip_find_sec(q_iop->dip, IOP_A, BIT_START(q_iop));
+
+       if (a_iop) {
+               __link(a_iop, q_iop);
+               run_unremap(a_iop, del_head);
+               __unlink(a_iop, q_iop);
+       }
+
+       list_add_tail(&q_iop->f_head, del_head);
+}
diff --git a/btt/trace_remap.c b/btt/trace_remap.c
new file mode 100644 (file)
index 0000000..cc7c643
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * blktrace output analysis: generate a timeline & gather statistics
+ *
+ * Copyright (C) 2006 Alan D. Brunelle <Alan.Brunelle@hp.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+#include "globals.h"
+
+void trace_remap(struct io *a_iop)
+{
+       struct io *l_iop;
+       struct blk_io_trace_remap *rp = a_iop->pdu;
+       __u32 remap_dev = be32_to_cpu(rp->device);
+       __u64 remap_sec = be64_to_cpu(rp->sector);
+
+       if (!io_setup(a_iop, IOP_A)) {
+               io_release(a_iop);
+               return;
+       }
+
+       /* 
+        * Create a fake LINK trace
+        */
+       l_iop = io_alloc();
+       memcpy(&l_iop->t, &a_iop->t, sizeof(a_iop->t));
+       l_iop->t.device = remap_dev;
+       l_iop->t.sector = remap_sec;
+
+       if (!io_setup(l_iop, IOP_L)) {
+               io_release(l_iop);
+               io_release(a_iop);
+               return;
+       }
+
+       __link(l_iop, a_iop);
+       l_iop->self_remap = (MAJOR(a_iop->t.device) == MAJOR(remap_dev));
+}
+
+
+int ready_remap(struct io *a_iop, struct io *top)
+{
+       struct io *l_iop = list_first_down(a_iop);
+       struct blk_io_trace_remap *rp = a_iop->pdu;
+       __u64 remap_sec = be64_to_cpu(rp->sector);
+
+       if (l_iop->self_remap) {
+               struct io *a_iop = dip_find_sec(l_iop->dip, IOP_A, remap_sec);
+               if (a_iop)
+                       return ready_remap(a_iop, top);
+       }
+       else {
+               struct io *q_iop = dip_find_sec(l_iop->dip, IOP_Q, remap_sec);
+               if (q_iop)
+                       return ready_queue(q_iop, top);
+       }
+
+       return 0;
+}
+
+void run_remap(struct io *a_iop, struct io *top, struct list_head *del_head)
+{
+       struct io *l_iop = list_first_down(a_iop);
+       struct blk_io_trace_remap *rp = a_iop->pdu;
+       __u64 remap_sec = be64_to_cpu(rp->sector);
+
+       if (l_iop->self_remap) {
+               struct io *a2_iop = dip_find_sec(l_iop->dip, IOP_A, remap_sec);
+               ASSERT(a2_iop);
+               __link(a2_iop, l_iop);
+               run_remap(a2_iop, top, del_head);
+               __unlink(a2_iop, l_iop);
+       }
+       else {
+               struct io *q_iop = dip_find_sec(l_iop->dip, IOP_Q, remap_sec);
+               ASSERT(q_iop);
+               __link(q_iop, l_iop);
+               update_q2a(q_iop, tdelta(q_iop, a_iop));
+               run_queue(q_iop, top, del_head);
+               __unlink(q_iop, l_iop);
+       }
+
+       dump_iop(per_io_ofp, a_iop, l_iop, 0);
+
+       __unlink(l_iop, a_iop);
+       list_add_tail(&l_iop->f_head, del_head);
+       list_add_tail(&a_iop->f_head, del_head);
+}
+
+void run_unremap(struct io *a_iop, struct list_head *del_head)
+{
+       struct io *l_iop = list_first_down(a_iop);
+       struct blk_io_trace_remap *rp = a_iop->pdu;
+       __u64 remap_sec = be64_to_cpu(rp->sector);
+
+       if (l_iop->self_remap) {
+               struct io *a_iop = dip_find_sec(l_iop->dip, IOP_A, remap_sec);
+               __link(a_iop, l_iop);
+               run_unremap(a_iop, del_head);
+               __unlink(a_iop, l_iop);
+       }
+       else {
+               struct io *q_iop = dip_find_sec(l_iop->dip, IOP_Q, remap_sec);
+               __link(q_iop, l_iop);
+               run_unqueue(q_iop, del_head);
+               __unlink(q_iop, l_iop);
+       }
+
+       __unlink(l_iop, a_iop);
+       list_add_tail(&l_iop->f_head, del_head);
+       list_add_tail(&a_iop->f_head, del_head);
+}
diff --git a/btt/trace_requeue.c b/btt/trace_requeue.c
new file mode 100644 (file)
index 0000000..66f3b05
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * blktrace output analysis: generate a timeline & gather statistics
+ *
+ * Copyright (C) 2006 Alan D. Brunelle <Alan.Brunelle@hp.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+#include "globals.h"
+
+void trace_requeue(struct io *r_iop)
+{
+       if (!io_setup(r_iop, IOP_R)) {
+               io_release(r_iop);
+               return;
+       }
+
+       if (ready_requeue(r_iop, r_iop))
+               run_requeue(r_iop);
+       else 
+               add_retry(r_iop);
+}
+
+int retry_requeue(struct io *r_iop)
+{
+       if (!ready_requeue(r_iop, r_iop))
+               return 0;
+
+       del_retry(r_iop);
+       run_requeue(r_iop);
+       return 1;
+}
+
+int ready_requeue(struct io *r_iop, struct io *top)
+{
+       struct io *d_iop = dip_find_sec(r_iop->dip, IOP_D, BIT_START(r_iop));
+       if (d_iop)
+               return ready_issue(d_iop, top);
+       return 0;
+}
+
+void run_requeue(struct io *r_iop)
+{
+       LIST_HEAD(del_head);
+       struct io *d_iop = dip_find_sec(r_iop->dip, IOP_D, BIT_START(r_iop));
+
+       __link(d_iop, r_iop);
+       run_unissue(d_iop, &del_head);
+       __unlink(d_iop, r_iop);
+
+       list_add_tail(&r_iop->f_head, &del_head);
+       release_iops(&del_head);
+}