From 095181f283c440c7669f2c14aef5e84c80ff433e Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Fri, 1 Dec 2006 10:50:45 +0100 Subject: [PATCH] [PATCH] BTT patch: (2/3) per-IO stream output 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 Signed-off-by: Jens Axboe --- btt/Makefile | 6 +- btt/README | 4 + btt/args.c | 20 ++++- btt/bt_timeline.c | 4 +- btt/devs.c | 11 ++- btt/dip_rb.c | 5 +- btt/globals.h | 69 +++++++++++++--- btt/inlines.h | 79 ++++++++++++++++--- btt/iostat.c | 4 +- btt/list.h | 21 +++++ btt/output.c | 36 ++++----- btt/trace.c | 184 +++++++++++++++++-------------------------- btt/trace_complete.c | 154 ++++++++++++++++++++++++++++++++++++ btt/trace_im.c | 77 ++++++++++++++++++ btt/trace_issue.c | 102 ++++++++++++++++++++++++ btt/trace_queue.c | 92 ++++++++++++++++++++++ btt/trace_remap.c | 125 +++++++++++++++++++++++++++++ btt/trace_requeue.c | 65 +++++++++++++++ 18 files changed, 890 insertions(+), 168 deletions(-) create mode 100644 btt/trace_complete.c create mode 100644 btt/trace_im.c create mode 100644 btt/trace_issue.c create mode 100644 btt/trace_queue.c create mode 100644 btt/trace_remap.c create mode 100644 btt/trace_requeue.c diff --git a/btt/Makefile b/btt/Makefile index 830ceda..df65723 100644 --- a/btt/Makefile +++ b/btt/Makefile @@ -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) diff --git a/btt/README b/btt/README index 938f9ce..c4f353d 100644 --- a/btt/README +++ b/btt/README @@ -16,6 +16,7 @@ Usage: btt [ -l | --d2c-latencies= ] [ -M | --dev-maps= [ -o | --output-file= ] + [ -p | --per-io-dump= ] [ -q | --q2c-latencies= ] [ -s | --seeks= ] [ -S | --iostat-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. diff --git a/btt/args.c b/btt/args.c index c8a79b2..0e8849c 100644 --- a/btt/args.c +++ b/btt/args.c @@ -27,7 +27,7 @@ #include #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 | --d2c-latencies= ]\n" \ "[ -M | --dev-maps=\n" \ "[ -o | --output-file= ]\n" \ + "[ -p | --per-io-dump= ]\n" \ "[ -q | --q2c-latencies= ]\n" \ "[ -s | --seeks= ]\n" \ "[ -S | --iostat-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); + } + } } diff --git a/btt/bt_timeline.c b/btt/bt_timeline.c index 46d8ad1..1c0855f 100644 --- a/btt/bt_timeline.c +++ b/btt/bt_timeline.c @@ -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; diff --git a/btt/devs.c b/btt/devs.c index bc75dae..d2df095 100644 --- a/btt/devs.c +++ b/btt/devs.c @@ -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); diff --git a/btt/dip_rb.c b/btt/dip_rb.c index 6ff09df..cfc607f 100644 --- a/btt/dip_rb.c +++ b/btt/dip_rb.c @@ -21,7 +21,7 @@ #include #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) diff --git a/btt/globals.h b/btt/globals.h index 1a785f3..b94b25f 100644 --- a/btt/globals.h +++ b/btt/globals.h @@ -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" diff --git a/btt/inlines.h b/btt/inlines.h index c8945bf..4a763b5 100644 --- a/btt/inlines.h +++ b/btt/inlines.h @@ -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; +} diff --git a/btt/iostat.c b/btt/iostat.c index f2c54dc..360d972 100644 --- a/btt/iostat.c +++ b/btt/iostat.c @@ -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)); } diff --git a/btt/list.h b/btt/list.h index b165ecf..f28f0fb 100644 --- a/btt/list.h +++ b/btt/list.h @@ -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 diff --git a/btt/output.c b/btt/output.c index 58bd595..db8a086 100644 --- a/btt/output.c +++ b/btt/output.c @@ -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); diff --git a/btt/trace.c b/btt/trace.c index abb1488..4c57b5d 100644 --- a/btt/trace.c +++ b/btt/trace.c @@ -20,134 +20,87 @@ */ #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 index 0000000..d8e7b5a --- /dev/null +++ b/btt/trace_complete.c @@ -0,0 +1,154 @@ +/* + * blktrace output analysis: generate a timeline & gather statistics + * + * Copyright (C) 2006 Alan D. Brunelle + * + * 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 index 0000000..eecd32a --- /dev/null +++ b/btt/trace_im.c @@ -0,0 +1,77 @@ +/* + * blktrace output analysis: generate a timeline & gather statistics + * + * Copyright (C) 2006 Alan D. Brunelle + * + * 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 index 0000000..17bfae0 --- /dev/null +++ b/btt/trace_issue.c @@ -0,0 +1,102 @@ +/* + * blktrace output analysis: generate a timeline & gather statistics + * + * Copyright (C) 2006 Alan D. Brunelle + * + * 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 index 0000000..d32e159 --- /dev/null +++ b/btt/trace_queue.c @@ -0,0 +1,92 @@ +/* + * blktrace output analysis: generate a timeline & gather statistics + * + * Copyright (C) 2006 Alan D. Brunelle + * + * 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 index 0000000..cc7c643 --- /dev/null +++ b/btt/trace_remap.c @@ -0,0 +1,125 @@ +/* + * blktrace output analysis: generate a timeline & gather statistics + * + * Copyright (C) 2006 Alan D. Brunelle + * + * 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 index 0000000..66f3b05 --- /dev/null +++ b/btt/trace_requeue.c @@ -0,0 +1,65 @@ +/* + * blktrace output analysis: generate a timeline & gather statistics + * + * Copyright (C) 2006 Alan D. Brunelle + * + * 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); +} -- 2.25.1