From: Nathan Scott Date: Mon, 12 Sep 2005 08:06:59 +0000 (+0200) Subject: [PATCH] Add support for tracing multiple devices X-Git-Tag: blktrace-0.99~166 X-Git-Url: https://git.kernel.dk/?a=commitdiff_plain;h=e7c9f3ffe9833eae21b2601d1cecb2e5d5c7f35f;p=blktrace.git [PATCH] Add support for tracing multiple devices This bumps the protocol number to 0x05, as device info was added to the trace structure. --- diff --git a/README b/README index 5d4f5a8..3197161 100644 --- a/README +++ b/README @@ -1,8 +1,9 @@ Block IO Tracing ---------------- -Written by Jens Axboe (initial version and kernel support) and -Alan D. Brunelle (threading and splitup into two seperate programs). +Written by Jens Axboe (initial version and kernel support), +Alan D. Brunelle (threading and splitup into two seperate programs), +Nathan Scott (bug fixes, process names, multiple devices) Requirements diff --git a/blkparse.c b/blkparse.c index c9b79de..01a840e 100644 --- a/blkparse.c +++ b/blkparse.c @@ -36,8 +36,12 @@ #define SECONDS(x) ((unsigned long long)(x) / 1000000000) #define NANO_SECONDS(x) ((unsigned long long)(x) % 1000000000) -static int backwards; -static unsigned long long genesis_time, last_reported_time; +#define MINORBITS 20 +#define MINORMASK ((1U << MINORBITS) - 1) +#define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS)) +#define MINOR(dev) ((unsigned int) ((dev) & MINORMASK)) + +#define min(a, b) ((a) < (b) ? (a) : (b)) struct io_stats { unsigned long qreads, qwrites, creads, cwrites, mreads, mwrites; @@ -56,6 +60,19 @@ struct per_cpu_info { struct io_stats io_stats; }; +struct per_dev_info { + dev_t id; + char *name; + + int backwards; + unsigned long long events; + unsigned long long last_reported_time; + struct io_stats io_stats; + + int ncpus; + struct per_cpu_info *cpus; +}; + struct per_process_info { char name[16]; __u32 pid; @@ -131,6 +148,7 @@ struct trace { struct io_track { struct rb_node rb_node; + dev_t device; __u64 sector; __u32 pid; unsigned long long allocation_time; @@ -139,13 +157,14 @@ struct io_track { unsigned long long completion_time; }; -static int max_cpus; -static struct per_cpu_info *per_cpu_info; +static int ndevices; +static struct per_dev_info *devices; +static char *get_dev_name(struct per_dev_info *, char *, int); -static unsigned long long events; - -static char *dev, *output_name; static FILE *ofp; +static char *output_name; + +static unsigned long long genesis_time; static int per_process_stats; static int track_ios; @@ -153,6 +172,8 @@ static int track_ios; #define RB_BATCH_DEFAULT (1024) static int rb_batch = RB_BATCH_DEFAULT; +static int pipeline; + #define is_done() (*(volatile int *)(&done)) static volatile int done; @@ -205,16 +226,30 @@ static inline int trace_rb_insert(struct trace *t) struct rb_node *parent = NULL; struct trace *__t; + if (genesis_time == 0 || t->bit->time < genesis_time) + genesis_time = t->bit->time; + while (*p) { parent = *p; __t = rb_entry(parent, struct trace, rb_node); - if (t->bit->sequence < __t->bit->sequence) + if (t->bit->time < __t->bit->time) + p = &(*p)->rb_left; + else if (t->bit->time > __t->bit->time) + p = &(*p)->rb_right; + else if (t->bit->device < __t->bit->device) + p = &(*p)->rb_left; + else if (t->bit->device > __t->bit->device) + p = &(*p)->rb_right; + else if (t->bit->sequence < __t->bit->sequence) p = &(*p)->rb_left; else if (t->bit->sequence > __t->bit->sequence) p = &(*p)->rb_right; - else { - fprintf(stderr, "sequence alias!\n"); + else if (t->bit->device == __t->bit->device) { + fprintf(stderr, + "sequence alias (%d) on device %d,%d!\n", + t->bit->sequence, + MAJOR(t->bit->device), MINOR(t->bit->device)); return 1; } } @@ -232,15 +267,22 @@ static inline int track_rb_insert(struct io_track *iot) while (*p) { parent = *p; - + __iot = rb_entry(parent, struct io_track, rb_node); - if (iot->sector < __iot->sector) + if (iot->device < __iot->device) + p = &(*p)->rb_left; + else if (iot->device > __iot->device) + p = &(*p)->rb_right; + else if (iot->sector < __iot->sector) p = &(*p)->rb_left; else if (iot->sector > __iot->sector) p = &(*p)->rb_right; else { - fprintf(stderr, "sequence alias!\n"); + fprintf(stderr, + "sector alias (%llu) on device %d,%d!\n", + iot->sector, + MAJOR(iot->device), MINOR(iot->device)); return 1; } } @@ -250,7 +292,7 @@ static inline int track_rb_insert(struct io_track *iot) return 0; } -static struct io_track *__find_track(__u64 sector) +static struct io_track *__find_track(dev_t device, __u64 sector) { struct rb_node **p = &rb_track_root.rb_node; struct rb_node *parent = NULL; @@ -261,7 +303,11 @@ static struct io_track *__find_track(__u64 sector) __iot = rb_entry(parent, struct io_track, rb_node); - if (sector < __iot->sector) + if (device < __iot->device) + p = &(*p)->rb_left; + else if (device > __iot->device) + p = &(*p)->rb_right; + else if (sector < __iot->sector) p = &(*p)->rb_left; else if (sector > __iot->sector) p = &(*p)->rb_right; @@ -272,14 +318,15 @@ static struct io_track *__find_track(__u64 sector) return NULL; } -static struct io_track *find_track(__u32 pid, __u64 sector) +static struct io_track *find_track(__u32 pid, dev_t device, __u64 sector) { struct io_track *iot; - iot = __find_track(sector); + iot = __find_track(device, sector); if (!iot) { iot = malloc(sizeof(*iot)); iot->pid = pid; + iot->device = device; iot->sector = sector; track_rb_insert(iot); } @@ -296,7 +343,7 @@ static void log_track_merge(struct blk_io_trace *t) if ((t->action & BLK_TC_ACT(BLK_TC_FS)) == 0) return; - iot = __find_track(t->sector - (t->bytes >> 10)); + iot = __find_track(t->device, t->sector - (t->bytes >> 10)); if (!iot) { fprintf(stderr, "Trying to merge on non-existing request\n"); return; @@ -314,7 +361,7 @@ static void log_track_getrq(struct blk_io_trace *t) if (!track_ios) return; - iot = find_track(t->pid, t->sector); + iot = find_track(t->pid, t->device, t->sector); iot->allocation_time = t->time; } @@ -330,7 +377,7 @@ static unsigned long long log_track_queue(struct blk_io_trace *t) if (!track_ios) return -1; - iot = find_track(t->pid, t->sector); + iot = find_track(t->pid, t->device, t->sector); iot->queue_time = t->time; elapsed = iot->queue_time - iot->allocation_time; @@ -358,7 +405,7 @@ static unsigned long long log_track_issue(struct blk_io_trace *t) if ((t->action & BLK_TC_ACT(BLK_TC_FS)) == 0) return -1; - iot = __find_track(t->sector); + iot = __find_track(t->device, t->sector); if (!iot) { fprintf(stderr, "Trying to issue on non-existing request\n"); return -1; @@ -391,7 +438,7 @@ static unsigned long long log_track_complete(struct blk_io_trace *t) if ((t->action & BLK_TC_ACT(BLK_TC_FS)) == 0) return -1; - iot = __find_track(t->sector); + iot = __find_track(t->device, t->sector); if (!iot) { fprintf(stderr, "Trying to dispatch on non-existing request\n"); return -1; @@ -434,49 +481,89 @@ static struct io_stats *find_process_io_stats(__u32 pid, char *name) return &ppi->io_stats; } -static void resize_cpu_info(int cpuid) + +static void resize_cpu_info(struct per_dev_info *pdi, int cpu) { - int new_space, new_max = cpuid + 1; + struct per_cpu_info *cpus = pdi->cpus; + int ncpus = pdi->ncpus; + int new_count = cpu + 1; + int new_space, size; char *new_start; - per_cpu_info = realloc(per_cpu_info, new_max * sizeof(*per_cpu_info)); - if (!per_cpu_info) { - fprintf(stderr, "Cannot allocate CPU info -- %d x %d bytes\n", - new_max, (int) sizeof(*per_cpu_info)); + size = new_count * sizeof(struct per_cpu_info); + cpus = realloc(cpus, size); + if (!cpus) { + char name[20]; + fprintf(stderr, "Out of memory, CPU info for device %s (%d)\n", + get_dev_name(pdi, name, sizeof(name)), size); exit(1); } - new_start = (char *)per_cpu_info + (max_cpus * sizeof(*per_cpu_info)); - new_space = (new_max - max_cpus) * sizeof(*per_cpu_info); + new_start = (char *)cpus + (ncpus * sizeof(struct per_cpu_info)); + new_space = (new_count - ncpus) * sizeof(struct per_cpu_info); memset(new_start, 0, new_space); - max_cpus = new_max; + + pdi->ncpus = new_count; + pdi->cpus = cpus; +} + +static struct per_cpu_info *get_cpu_info(struct per_dev_info *pdi, int cpu) +{ + if (cpu >= pdi->ncpus) + resize_cpu_info(pdi, cpu); + return &pdi->cpus[cpu]; } -static struct per_cpu_info *get_cpu_info(int cpu) + +static int resize_devices(char *name) { - struct per_cpu_info *pci; + int size = (ndevices + 1) * sizeof(struct per_dev_info); - if (cpu >= max_cpus) - resize_cpu_info(cpu); + devices = realloc(devices, size); + if (!devices) { + fprintf(stderr, "Out of memory, device %s (%d)\n", name, size); + return 1; + } + memset(&devices[ndevices], 0, sizeof(struct per_dev_info)); + devices[ndevices].name = name; + ndevices++; + return 0; +} - /* - * ->cpu might already be set, but just set it unconditionally - */ - pci = &per_cpu_info[cpu]; - pci->cpu = cpu; +static struct per_dev_info *get_dev_info(dev_t id, int create) +{ + int i; - return pci; + for (i = 0; i < ndevices; i++) + if (devices[i].id == id) + return &devices[i]; + if (!create) + return NULL; + if (resize_devices(NULL) != 0) + return NULL; + return &devices[ndevices-1]; } -static inline void check_time(struct blk_io_trace *bit) +static char *get_dev_name(struct per_dev_info *pdi, char *buffer, int size) +{ + if (pdi->name) + snprintf(buffer, size, "%s", pdi->name); + else + snprintf(buffer, size, "%d,%d", MAJOR(pdi->id), MINOR(pdi->id)); + return buffer; +} + + +static void check_time(struct per_dev_info *pdi, struct blk_io_trace *bit) { unsigned long long this = bit->time; - unsigned long long last = last_reported_time; + unsigned long long last = pdi->last_reported_time; - backwards = (this < last) ? 'B' : ' '; - last_reported_time = this; + pdi->backwards = (this < last) ? 'B' : ' '; + pdi->last_reported_time = this; } + static inline void __account_m(struct io_stats *ios, struct blk_io_trace *t, int rw) { @@ -599,8 +686,8 @@ static inline char *setup_header(struct per_cpu_info *pci, rwbs[i] = '\0'; - sprintf(hstring, "%2d %8ld %5Lu.%09Lu %5u %c %3s", - pci->cpu, + sprintf(hstring, "%3d,%-3d %2d %8ld %5Lu.%09Lu %5u %c %3s", + MAJOR(t->device), MINOR(t->device), pci->cpu, (unsigned long)t->sequence, SECONDS(t->time), NANO_SECONDS(t->time), t->pid, act, rwbs); @@ -783,7 +870,8 @@ static void dump_trace_fs(struct blk_io_trace *t, struct per_cpu_info *pci) } } -static int dump_trace(struct blk_io_trace *t, struct per_cpu_info *pci) +static int dump_trace(struct blk_io_trace *t, struct per_cpu_info *pci, + struct per_dev_info *pdi) { int ret = 0; @@ -792,7 +880,7 @@ static int dump_trace(struct blk_io_trace *t, struct per_cpu_info *pci) else dump_trace_fs(t, pci); - events++; + pdi->events++; return ret; } @@ -843,53 +931,61 @@ static void show_process_stats(void) fprintf(ofp, "\n"); } -static void show_cpu_stats(void) +static void show_device_and_cpu_stats(void) { - struct per_cpu_info foo, *pci; - struct io_stats *ios; - int i, pci_events = 0; - - memset(&foo, 0, sizeof(foo)); - - for (i = 0; i < max_cpus; i++) { - char cpu[8]; - - pci = &per_cpu_info[i]; - ios = &pci->io_stats; - - if (!pci->nelems) - continue; - - foo.io_stats.qreads += ios->qreads; - foo.io_stats.qwrites += ios->qwrites; - foo.io_stats.creads += ios->creads; - foo.io_stats.cwrites += ios->cwrites; - foo.io_stats.mreads += ios->mreads; - foo.io_stats.mwrites += ios->mwrites; - foo.io_stats.ireads += ios->ireads; - foo.io_stats.iwrites += ios->iwrites; - foo.io_stats.qread_kb += ios->qread_kb; - foo.io_stats.qwrite_kb += ios->qwrite_kb; - foo.io_stats.cread_kb += ios->cread_kb; - foo.io_stats.cwrite_kb += ios->cwrite_kb; - foo.io_stats.iread_kb += ios->iread_kb; - foo.io_stats.iwrite_kb += ios->iwrite_kb; - - snprintf(cpu, sizeof(cpu) - 1, "CPU%d:", i); - dump_io_stats(ios, cpu); - pci_events++; - } + struct per_dev_info *pdi; + struct per_cpu_info *pci; + struct io_stats total, *ios; + int i, j, pci_events; + char line[3 + 8/*cpu*/ + 2 + 32/*dev*/ + 3]; + char name[32]; + + for (pdi = devices, i = 0; i < ndevices; i++, pdi++) { + + memset(&total, 0, sizeof(total)); + pci_events = 0; + + if (i > 0) + fprintf(ofp, "\n"); + + for (pci = pdi->cpus, j = 0; j < pdi->ncpus; j++, pci++) { + if (!pci->nelems) + continue; + + ios = &pci->io_stats; + total.qreads += ios->qreads; + total.qwrites += ios->qwrites; + total.creads += ios->creads; + total.cwrites += ios->cwrites; + total.mreads += ios->mreads; + total.mwrites += ios->mwrites; + total.ireads += ios->ireads; + total.iwrites += ios->iwrites; + total.qread_kb += ios->qread_kb; + total.qwrite_kb += ios->qwrite_kb; + total.cread_kb += ios->cread_kb; + total.cwrite_kb += ios->cwrite_kb; + total.iread_kb += ios->iread_kb; + total.iwrite_kb += ios->iwrite_kb; + + snprintf(line, sizeof(line) - 1, "CPU%d (%s):", + j, get_dev_name(pdi, name, sizeof(name))); + dump_io_stats(ios, line); + pci_events++; + } - if (pci_events > 1) { - fprintf(ofp, "\n"); - dump_io_stats(&foo.io_stats, "Total:"); - } + if (pci_events > 1) { + fprintf(ofp, "\n"); + snprintf(line, sizeof(line) - 1, "Total (%s):", + get_dev_name(pdi, name, sizeof(name))); + dump_io_stats(&total, line); + } - fprintf(ofp, "\nEvents: %'Lu\n", events); + fprintf(ofp, "Events (%s): %'Lu\n", + get_dev_name(pdi, line, sizeof(line)), pdi->events); + } } -#define min(a, b) ((a) < (b) ? (a) : (b)) - static struct blk_io_trace *find_trace(void *p, unsigned long offset, int nr) { unsigned long max_offset = min(offset,nr * sizeof(struct blk_io_trace)); @@ -908,13 +1004,14 @@ static struct blk_io_trace *find_trace(void *p, unsigned long offset, int nr) return NULL; } -static int sort_entries(void *traces, unsigned long offset, int nr) +static int sort_entries(void *traces, unsigned long offset, int nr, + struct per_dev_info *fpdi, struct per_cpu_info *fpci) { + struct per_dev_info *pdi; struct per_cpu_info *pci; struct blk_io_trace *bit; struct trace *t; void *start = traces; - int nelems = 0; while (traces - start <= offset - sizeof(*bit)) { if (!nr) @@ -925,26 +1022,38 @@ static int sort_entries(void *traces, unsigned long offset, int nr) break; t = malloc(sizeof(*t)); + if (!t) { + fprintf(stderr, "Out of memory, seq %d on dev %d,%d\n", + bit->sequence, + MAJOR(bit->device), MINOR(bit->device)); + return -1; + } t->bit = bit; memset(&t->rb_node, 0, sizeof(t->rb_node)); trace_to_cpu(bit); - if (verify_trace(bit)) + if (verify_trace(bit)) { + free(t); break; + } - pci = get_cpu_info(bit->cpu); + pdi = fpdi ? fpdi : get_dev_info(bit->device, 1); + pdi->id = bit->device; + pci = fpci ? fpci : get_cpu_info(pdi, bit->cpu); + pci->cpu = bit->cpu; pci->nelems++; - if (trace_rb_insert(t)) + if (trace_rb_insert(t)) { + free(t); return -1; + } traces += sizeof(*bit) + bit->pdu_len; - nelems++; nr--; } - return nelems; + return 0; } static void free_entries_rb(void) @@ -961,6 +1070,7 @@ static void free_entries_rb(void) static void show_entries_rb(void) { + struct per_dev_info *pdi; struct blk_io_trace *bit; struct rb_node *n; struct trace *t; @@ -974,19 +1084,24 @@ static void show_entries_rb(void) t = rb_entry(n, struct trace, rb_node); bit = t->bit; + pdi = get_dev_info(bit->device, 0); + if (!pdi) { + fprintf(stderr, "Unknown device ID? (%d,%d)\n", + MAJOR(bit->device), MINOR(bit->device)); + break; + } cpu = bit->cpu; - if (cpu > max_cpus) { - fprintf(stderr, "CPU number too large (%d)\n", cpu); + if (cpu > pdi->ncpus) { + fprintf(stderr, "Unknown CPU ID? (%d, device %d,%d)\n", + cpu, MAJOR(bit->device), MINOR(bit->device)); break; } - if (genesis_time == 0) - genesis_time = bit->time; bit->time -= genesis_time; - check_time(bit); + check_time(pdi, bit); - if (dump_trace(bit, &per_cpu_info[cpu])) + if (dump_trace(bit, &pdi->cpus[cpu], pdi)) break; } while ((n = rb_next(n)) != NULL); @@ -1025,40 +1140,57 @@ static int read_data(int fd, void *buffer, int bytes, int block) static int do_file(void) { - int i, nfiles; + struct per_dev_info *pdi; + int i, j, nfiles = 0; - for (i = 0, nfiles = 0;; i++, nfiles++) { - struct per_cpu_info *pci; - struct stat st; - void *tb; + for (pdi = devices, i = 0; i < ndevices; i++, pdi++) { + for (j = 0;; j++, nfiles++) { + struct per_cpu_info *pci; + struct stat st; + void *tb; - pci = get_cpu_info(i); - - snprintf(pci->fname, sizeof(pci->fname)-1,"%s_out.%d", dev, i); - if (stat(pci->fname, &st) < 0) - break; - if (!st.st_size) - continue; + pci = get_cpu_info(pdi, j); + pci->cpu = j; - printf("Processing %s\n", pci->fname); - - tb = malloc(st.st_size); - - pci->fd = open(pci->fname, O_RDONLY); - if (pci->fd < 0) { - perror(pci->fname); - break; + snprintf(pci->fname, sizeof(pci->fname)-1, + "%s_out.%d", pdi->name, j); + if (stat(pci->fname, &st) < 0) + break; + if (!st.st_size) + continue; + + printf("Processing %s\n", pci->fname); + + tb = malloc(st.st_size); + if (!tb) { + fprintf(stderr, "Out of memory, skip file %s\n", + pci->fname); + continue; + } + + pci->fd = open(pci->fname, O_RDONLY); + if (pci->fd < 0) { + perror(pci->fname); + free(tb); + continue; + } + + if (read_data(pci->fd, tb, st.st_size, 1)) { + close(pci->fd); + free(tb); + continue; + } + + if (sort_entries(tb, st.st_size, ~0U, pdi, pci) == -1) { + close(pci->fd); + free(tb); + continue; + } + + printf("Completed %s (CPU%d %d, entries)\n", + pci->fname, j, pci->nelems); + close(pci->fd); } - - if (read_data(pci->fd, tb, st.st_size, 1)) - break; - - if (sort_entries(tb, st.st_size, ~0U) == -1) - break; - - close(pci->fd); - printf("\t%2d %10s %15d\n", i, pci->fname, pci->nelems); - } if (!nfiles) { @@ -1142,7 +1274,7 @@ static int do_stdin(void) if (!events) break; - if (sort_entries(ptr, ~0UL, events) == -1) + if (sort_entries(ptr, ~0UL, events, NULL, NULL) == -1) break; show_entries_rb(); @@ -1169,7 +1301,8 @@ static void handle_sigint(int sig) static void usage(char *prog) { - fprintf(stderr, "Usage: %s -i [-o ][-s]\n", prog); + fprintf(stderr, "Usage: %s [-i ] [-o ] [-s] ...\n", + prog); } int main(int argc, char *argv[]) @@ -1180,7 +1313,10 @@ int main(int argc, char *argv[]) while ((c = getopt_long(argc, argv, S_OPTS, l_opts, NULL)) != -1) { switch (c) { case 'i': - dev = optarg; + if (!strcmp(optarg, "-") && !pipeline) + pipeline = 1; + else if (resize_devices(optarg) != 0) + return 1; break; case 'o': output_name = optarg; @@ -1202,7 +1338,15 @@ int main(int argc, char *argv[]) } } - if (!dev) { + while (optind < argc) { + if (!strcmp(argv[optind], "-") && !pipeline) + pipeline = 1; + else if (resize_devices(argv[optind]) != 0) + return 1; + optind++; + } + + if (!pipeline && !ndevices) { usage(argv[0]); return 1; } @@ -1238,7 +1382,7 @@ int main(int argc, char *argv[]) return 1; } - if (!strcmp(dev, "-")) + if (pipeline) ret = do_stdin(); else ret = do_file(); @@ -1246,7 +1390,7 @@ int main(int argc, char *argv[]) if (per_process_stats) show_process_stats(); - show_cpu_stats(); + show_device_and_cpu_stats(); flush_output(); return ret; diff --git a/blktrace.c b/blktrace.c index 3ff2391..179c50a 100644 --- a/blktrace.c +++ b/blktrace.c @@ -122,22 +122,31 @@ struct thread_information { int ofd; unsigned long events_processed; + struct device_information *device; }; -static char *relay_path; - -#define is_done() (*(volatile int *)(&done)) -static volatile int done; +struct device_information { + int fd; + char *path; + char buts_name[32]; + int trace_started; + struct thread_information *threads; +}; -static int devfd, ncpus; +static int ncpus; static struct thread_information *thread_information; -static char buts_name[32]; -static char *dev; +static int ndevs; +static struct device_information *device_information; + +/* command line option globals */ +static char *relay_path; static char *output_name; static int act_mask = ~0U; -static int trace_started; static int kill_running_trace; +#define is_done() (*(volatile int *)(&done)) +static volatile int done; + static pthread_mutex_t stdout_mutex = PTHREAD_MUTEX_INITIALIZER; static void exit_trace(int status); @@ -153,7 +162,7 @@ static int find_mask_map(char *string) return -1; } -static int start_trace(char *dev) +static int start_trace(struct device_information *dip) { struct blk_user_trace_setup buts; @@ -162,26 +171,35 @@ static int start_trace(char *dev) buts.buf_nr = BUF_NR; buts.act_mask = act_mask; - if (ioctl(devfd, BLKSTARTTRACE, &buts) < 0) { + if (ioctl(dip->fd, BLKSTARTTRACE, &buts) < 0) { perror("BLKSTARTTRACE"); return 1; } - memcpy(buts_name, buts.name, sizeof(buts_name)); - trace_started = 1; + memcpy(dip->buts_name, buts.name, sizeof(dip->buts_name)); + dip->trace_started = 1; return 0; } -static void stop_trace(void) +static void stop_trace(struct device_information *dip) { - if (trace_started || kill_running_trace) { - if (ioctl(devfd, BLKSTOPTRACE) < 0) + if (dip->trace_started || kill_running_trace) { + if (ioctl(dip->fd, BLKSTOPTRACE) < 0) perror("BLKSTOPTRACE"); - - trace_started = 0; + close(dip->fd); + dip->trace_started = 0; } } +static void stop_all_traces(void) +{ + struct device_information *dip; + int i; + + for (dip = device_information, i = 0; i < ndevs; i++, dip++) + stop_trace(dip); +} + static void *extract_data(struct thread_information *tip, char *ofn, int nb) { int ret, bytes_left; @@ -239,8 +257,8 @@ static void *extract(void *arg) exit_trace(1); } - snprintf(tip->fn, sizeof(tip->fn), - "%s/block/%s/trace%d", relay_path, buts_name, tip->cpu); + snprintf(tip->fn, sizeof(tip->fn), "%s/block/%s/trace%d", + relay_path, tip->device->buts_name, tip->cpu); tip->fd = open(tip->fn, O_RDONLY); if (tip->fd < 0) { perror(tip->fn); @@ -309,44 +327,44 @@ static void *extract(void *arg) return NULL; } -static int start_threads(void) +static int start_threads(struct device_information *dip) { struct thread_information *tip; char op[64]; - int i; + int j, pipeline = output_name && !strcmp(output_name, "-"); - ncpus = sysconf(_SC_NPROCESSORS_ONLN); - if (ncpus < 0) { - fprintf(stderr, "sysconf(_SC_NPROCESSORS_ONLN) failed\n"); - return 0; - } - - thread_information = malloc(ncpus * sizeof(struct thread_information)); - for (i = 0, tip = thread_information; i < ncpus; i++, tip++) { + for (tip = dip->threads, j = 0; j < ncpus; j++, tip++) { + tip->cpu = j; + tip->device = dip; tip->fd_lock = NULL; - tip->cpu = i; tip->events_processed = 0; - if (!strcmp(output_name, "-")) { + if (pipeline) { tip->ofd = dup(STDOUT_FILENO); tip->fd_lock = &stdout_mutex; } else { - sprintf(op, "%s_out.%d", output_name, tip->cpu); + if (output_name) + sprintf(op, "%s_%s_out.%d", output_name, + dip->buts_name, tip->cpu); + else + sprintf(op, "%s_out.%d", + dip->buts_name, tip->cpu); tip->ofd = open(op, O_CREAT|O_TRUNC|O_WRONLY, 0644); } if (tip->ofd < 0) { perror(op); - return 0; + return 1; } if (pthread_create(&tip->thread, NULL, extract, tip)) { - perror( "pthread_create"); - return 0; + perror("pthread_create"); + close(tip->ofd); + return 1; } } - return ncpus; + return 0; } static void close_thread(struct thread_information *tip) @@ -358,54 +376,142 @@ static void close_thread(struct thread_information *tip) tip->fd = tip->ofd = -1; } -static void stop_threads(void) +static void stop_threads(struct device_information *dip) { - struct thread_information *tip = thread_information; - int i; - - for (i = 0; i < ncpus; i++, tip++) { - long ret; + struct thread_information *tip; + long ret; + int j; + for (tip = dip->threads, j = 0; j < ncpus; j++, tip++) { if (pthread_join(tip->thread, (void *) &ret)) perror("thread_join"); close_thread(tip); } } -static void stop_tracing(void) +static void stop_all_threads(void) { - struct thread_information *tip = thread_information; + struct device_information *dip; int i; - for (i = 0; i < ncpus; i++, tip++) - close_thread(tip); - stop_trace(); + for (dip = device_information, i = 0; i < ndevs; i++, dip++) + stop_threads(dip); +} + +static void stop_all_tracing(void) +{ + struct device_information *dip; + struct thread_information *tip; + int i, j; + + for (dip = device_information, i = 0; i < ndevs; i++, dip++) { + for (tip = dip->threads, j = 0; j < ncpus; j++, tip++) + close_thread(tip); + stop_trace(dip); + } } static void exit_trace(int status) { - stop_tracing(); + stop_all_tracing(); exit(status); } -static void show_stats(void) +static int resize_devices(char *path) +{ + int size = (ndevs + 1) * sizeof(struct device_information); + + device_information = realloc(device_information, size); + if (!device_information) { + fprintf(stderr, "Out of memory, device %s (%d)\n", path, size); + return 1; + } + device_information[ndevs].path = path; + ndevs++; + return 0; +} + +static int open_devices(void) { + struct device_information *dip; int i; - struct thread_information *tip; - unsigned long events_processed = 0; - if (!strcmp(output_name, "-")) - return; + for (dip = device_information, i = 0; i < ndevs; i++, dip++) { + dip->fd = open(dip->path, O_RDONLY); + if (dip->fd < 0) { + perror(dip->path); + return 1; + } + } + return 0; +} + +static int start_devices(void) +{ + struct device_information *dip; + int i, j, size; + + size = ncpus * sizeof(struct thread_information); + thread_information = malloc(size * ndevs); + if (!thread_information) { + fprintf(stderr, "Out of memory, threads (%d)\n", size * ndevs); + return 1; + } - for (i = 0, tip = thread_information; i < ncpus; i++, tip++) { - printf("CPU%3d: %20ld events\n", - tip->cpu, tip->events_processed); - events_processed += tip->events_processed; + for (dip = device_information, i = 0; i < ndevs; i++, dip++) { + if (start_trace(dip)) { + close(dip->fd); + fprintf(stderr, "Failed to start trace on %s\n", + dip->path); + break; + } + } + if (i != ndevs) { + for (dip = device_information, j = 0; j < i; j++, dip++) + stop_trace(dip); + return 1; + } + + for (dip = device_information, i = 0; i < ndevs; i++, dip++) { + dip->threads = thread_information + (i * ncpus); + if (start_threads(dip)) { + fprintf(stderr, "Failed to start worker threads\n"); + break; + } + } + if (i != ndevs) { + for (dip = device_information, j = 0; j < i; j++, dip++) + stop_threads(dip); + for (dip = device_information, i = 0; i < ndevs; i++, dip++) + stop_trace(dip); + return 1; } - printf("Total: %20ld events\n", events_processed); + return 0; } +static void show_stats(void) +{ + int i, j; + struct device_information *dip; + struct thread_information *tip; + unsigned long long events_processed; + + if (output_name && !strcmp(output_name, "-")) + return; + + for (dip = device_information, i = 0; i < ndevs; i++, dip++) { + printf("Device: %s\n", dip->path); + events_processed = 0; + for (tip = dip->threads, j = 0; j < ncpus; j++, tip++) { + printf(" CPU%3d: %20ld events\n", + tip->cpu, tip->events_processed); + events_processed += tip->events_processed; + } + printf(" Total: %20lld events\n", events_processed); + } +} + static void show_usage(char *program) { fprintf(stderr,"Usage: %s [-d ] " @@ -448,7 +554,8 @@ int main(int argc, char *argv[]) break; case 'd': - dev = optarg; + if (resize_devices(optarg) != 0) + return 1; break; case 'r': @@ -468,10 +575,12 @@ int main(int argc, char *argv[]) } } - while (optind < argc) - dev = argv[optind++]; + while (optind < argc) { + if (resize_devices(argv[optind++]) != 0) + return 1; + } - if (dev == NULL) { + if (ndevs == 0) { show_usage(argv[0]); return 1; } @@ -488,48 +597,37 @@ int main(int argc, char *argv[]) return 1; } - devfd = open(dev, O_RDONLY); - if (devfd < 0) { - perror(dev); + if (open_devices() != 0) return 1; - } if (kill_running_trace) { - stop_trace(); + stop_all_traces(); return 0; } - if (start_trace(dev)) { - close(devfd); - fprintf(stderr, "Failed to start trace on %s\n", dev); - return 1; - } - setlocale(LC_NUMERIC, "en_US"); - if (!output_name) - output_name = buts_name; - - i = start_threads(); - if (!i) { - fprintf(stderr, "Failed to start worker threads\n"); - stop_trace(); + ncpus = sysconf(_SC_NPROCESSORS_ONLN); + if (ncpus < 0) { + fprintf(stderr, "sysconf(_SC_NPROCESSORS_ONLN) failed\n"); return 1; } + if (start_devices() != 0) + return 1; + signal(SIGINT, handle_sigint); signal(SIGHUP, handle_sigint); signal(SIGTERM, handle_sigint); - atexit(stop_tracing); + atexit(stop_all_tracing); while (!is_done()) sleep(1); - stop_threads(); - stop_trace(); + stop_all_threads(); + stop_all_traces(); show_stats(); - close(devfd); return 0; } diff --git a/blktrace.h b/blktrace.h index 8329d0a..9855101 100644 --- a/blktrace.h +++ b/blktrace.h @@ -7,7 +7,7 @@ #include "blktrace_api.h" #define CHECK_MAGIC(t) (((t)->magic & 0xffffff00) == BLK_IO_TRACE_MAGIC) -#define SUPPORTED_VERSION (0x04) +#define SUPPORTED_VERSION (0x05) #if defined(__LITTLE_ENDIAN_BITFIELD) #define be16_to_cpu(x) __bswap_16(x) @@ -54,6 +54,7 @@ static inline void trace_to_be(struct blk_io_trace *t) t->cpu = cpu_to_be32(t->cpu); t->error = cpu_to_be16(t->error); t->pdu_len = cpu_to_be16(t->pdu_len); + t->device = cpu_to_be32(t->device); /* t->comm is a string (endian neutral) */ } @@ -69,6 +70,7 @@ static inline void trace_to_cpu(struct blk_io_trace *t) t->cpu = be32_to_cpu(t->cpu); t->error = be16_to_cpu(t->error); t->pdu_len = be16_to_cpu(t->pdu_len); + t->device = be32_to_cpu(t->device); /* t->comm is a string (endian neutral) */ } diff --git a/blktrace_api.h b/blktrace_api.h index e79e151..2b3a7ba 100644 --- a/blktrace_api.h +++ b/blktrace_api.h @@ -51,7 +51,7 @@ enum { #define BLK_TA_COMPLETE (__BLK_TA_COMPLETE| BLK_TC_ACT(BLK_TC_COMPLETE)) #define BLK_IO_TRACE_MAGIC 0x65617400 -#define BLK_IO_TRACE_VERSION 0x04 +#define BLK_IO_TRACE_VERSION 0x05 /* * The trace itself @@ -67,6 +67,7 @@ struct blk_io_trace { __u32 cpu; /* on what cpu did it happen */ __u16 error; /* completion error */ __u16 pdu_len; /* length of data after this trace */ + __u32 device; /* device identifier (dev_t) */ char comm[16]; /* task command name (TASK_COMM_LEN) */ }; diff --git a/kernel/blk-trace-2.6.13-git-E0 b/kernel/blk-trace-2.6.13-git-E0 deleted file mode 100644 index b199b04..0000000 --- a/kernel/blk-trace-2.6.13-git-E0 +++ /dev/null @@ -1,578 +0,0 @@ -diff --git a/Documentation/fb/vesafb.txt b/Documentation/fb/vesafb.txt -diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig ---- a/drivers/block/Kconfig -+++ b/drivers/block/Kconfig -@@ -419,6 +419,14 @@ config LBD - your machine, or if you want to have a raid or loopback device - bigger than 2TB. Otherwise say N. - -+config BLK_DEV_IO_TRACE -+ bool "Support for tracing block io actions" -+ select RELAYFS_FS -+ help -+ Say Y here, if you want to be able to trace the block layer actions -+ on a given queue. -+ -+ - config CDROM_PKTCDVD - tristate "Packet writing on CD/DVD media" - depends on !UML -diff --git a/drivers/block/Makefile b/drivers/block/Makefile ---- a/drivers/block/Makefile -+++ b/drivers/block/Makefile -@@ -45,3 +45,5 @@ obj-$(CONFIG_VIODASD) += viodasd.o - obj-$(CONFIG_BLK_DEV_SX8) += sx8.o - obj-$(CONFIG_BLK_DEV_UB) += ub.o - -+obj-$(CONFIG_BLK_DEV_IO_TRACE) += blktrace.o -+ -diff --git a/drivers/block/elevator.c b/drivers/block/elevator.c ---- a/drivers/block/elevator.c -+++ b/drivers/block/elevator.c -@@ -34,6 +34,7 @@ - #include - #include - #include -+#include - - #include - -@@ -371,6 +372,9 @@ struct request *elv_next_request(request - int ret; - - while ((rq = __elv_next_request(q)) != NULL) { -+ -+ blk_add_trace_rq(q, rq, BLK_TA_ISSUE); -+ - /* - * just mark as started even if we don't start it, a request - * that has been delayed should not be passed by new incoming -diff --git a/drivers/block/ioctl.c b/drivers/block/ioctl.c ---- a/drivers/block/ioctl.c -+++ b/drivers/block/ioctl.c -@@ -4,6 +4,7 @@ - #include - #include - #include -+#include - #include - - static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg __user *arg) -@@ -188,6 +189,10 @@ static int blkdev_locked_ioctl(struct fi - return put_ulong(arg, bdev->bd_inode->i_size >> 9); - case BLKGETSIZE64: - return put_u64(arg, bdev->bd_inode->i_size); -+ case BLKSTARTTRACE: -+ return blk_start_trace(bdev, (char __user *) arg); -+ case BLKSTOPTRACE: -+ return blk_stop_trace(bdev); - } - return -ENOIOCTLCMD; - } -diff --git a/drivers/block/ll_rw_blk.c b/drivers/block/ll_rw_blk.c ---- a/drivers/block/ll_rw_blk.c -+++ b/drivers/block/ll_rw_blk.c -@@ -29,6 +29,7 @@ - #include - #include - #include -+#include - - /* - * for max sense size -@@ -1624,6 +1625,11 @@ void blk_cleanup_queue(request_queue_t * - if (q->queue_tags) - __blk_queue_free_tags(q); - -+ if (q->blk_trace) { -+ blk_cleanup_trace(q->blk_trace); -+ q->blk_trace = NULL; -+ } -+ - blk_queue_ordered(q, QUEUE_ORDERED_NONE); - - kmem_cache_free(requestq_cachep, q); -@@ -1970,6 +1976,8 @@ rq_starved: - - rq_init(q, rq); - rq->rl = rl; -+ -+ blk_add_trace_generic(q, bio, rw, BLK_TA_GETRQ); - out: - return rq; - } -@@ -1998,6 +2006,8 @@ static struct request *get_request_wait( - if (!rq) { - struct io_context *ioc; - -+ blk_add_trace_generic(q, bio, rw, BLK_TA_SLEEPRQ); -+ - __generic_unplug_device(q); - spin_unlock_irq(q->queue_lock); - io_schedule(); -@@ -2051,6 +2061,8 @@ EXPORT_SYMBOL(blk_get_request); - */ - void blk_requeue_request(request_queue_t *q, struct request *rq) - { -+ blk_add_trace_rq(q, rq, BLK_TA_REQUEUE); -+ - if (blk_rq_tagged(rq)) - blk_queue_end_tag(q, rq); - -@@ -2714,6 +2726,8 @@ static int __make_request(request_queue_ - if (!q->back_merge_fn(q, req, bio)) - break; - -+ blk_add_trace_bio(q, bio, BLK_TA_BACKMERGE); -+ - req->biotail->bi_next = bio; - req->biotail = bio; - req->nr_sectors = req->hard_nr_sectors += nr_sectors; -@@ -2729,6 +2743,8 @@ static int __make_request(request_queue_ - if (!q->front_merge_fn(q, req, bio)) - break; - -+ blk_add_trace_bio(q, bio, BLK_TA_FRONTMERGE); -+ - bio->bi_next = req->bio; - req->bio = bio; - -@@ -2794,6 +2810,8 @@ get_rq: - req->rq_disk = bio->bi_bdev->bd_disk; - req->start_time = jiffies; - -+ blk_add_trace_bio(q, bio, BLK_TA_QUEUE); -+ - spin_lock_irq(q->queue_lock); - if (elv_queue_empty(q)) - blk_plug_device(q); -@@ -3030,6 +3048,10 @@ end_io: - blk_partition_remap(bio); - - ret = q->make_request_fn(q, bio); -+ -+ if (ret) -+ blk_add_trace_bio(q, bio, BLK_TA_QUEUE); -+ - } while (ret); - } - -@@ -3148,6 +3170,8 @@ static int __end_that_request_first(stru - int total_bytes, bio_nbytes, error, next_idx = 0; - struct bio *bio; - -+ blk_add_trace_rq(req->q, req, BLK_TA_COMPLETE); -+ - /* - * extend uptodate bool to allow < 0 value to be direct io error - */ -diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h ---- a/include/linux/blkdev.h -+++ b/include/linux/blkdev.h -@@ -22,6 +22,7 @@ typedef struct request_queue request_que - struct elevator_queue; - typedef struct elevator_queue elevator_t; - struct request_pm_state; -+struct blk_trace; - - #define BLKDEV_MIN_RQ 4 - #define BLKDEV_MAX_RQ 128 /* Default maximum */ -@@ -412,6 +413,8 @@ struct request_queue - */ - struct request *flush_rq; - unsigned char ordered; -+ -+ struct blk_trace *blk_trace; - }; - - enum { -diff --git a/include/linux/fs.h b/include/linux/fs.h ---- a/include/linux/fs.h -+++ b/include/linux/fs.h -@@ -195,6 +195,8 @@ extern int dir_notify_enable; - #define BLKBSZGET _IOR(0x12,112,size_t) - #define BLKBSZSET _IOW(0x12,113,size_t) - #define BLKGETSIZE64 _IOR(0x12,114,size_t) /* return device size in bytes (u64 *arg) */ -+#define BLKSTARTTRACE _IOWR(0x12,115,struct blk_user_trace_setup) -+#define BLKSTOPTRACE _IO(0x12,116) - - #define BMAP_IOCTL 1 /* obsolete - kept for compatibility */ - #define FIBMAP _IO(0x00,1) /* bmap access */ ---- /dev/null 2005-09-03 12:52:15.000000000 +0200 -+++ linux-2.6/drivers/block/blktrace.c 2005-09-08 08:33:20.000000000 +0200 -@@ -0,0 +1,222 @@ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+static DEFINE_PER_CPU(unsigned long long, blk_trace_cpu_offset) = { 0, }; -+ -+void __blk_add_trace(struct blk_trace *bt, sector_t sector, int bytes, -+ int rw, u32 what, int error, int pdu_len, char *pdu_data) -+{ -+ struct blk_io_trace t; -+ unsigned long flags; -+ int cpu; -+ -+ if (rw & (1 << BIO_RW_BARRIER)) -+ what |= BLK_TC_ACT(BLK_TC_BARRIER); -+ if (rw & (1 << BIO_RW_SYNC)) -+ what |= BLK_TC_ACT(BLK_TC_SYNC); -+ -+ if (rw & WRITE) -+ what |= BLK_TC_ACT(BLK_TC_WRITE); -+ else -+ what |= BLK_TC_ACT(BLK_TC_READ); -+ -+ if (((bt->act_mask << BLK_TC_SHIFT) & what) == 0) -+ return; -+ -+ t.magic = BLK_IO_TRACE_MAGIC | BLK_IO_TRACE_VERSION; -+ t.sequence = atomic_add_return(1, &bt->sequence); -+ -+ cpu = get_cpu(); -+ t.cpu = cpu; -+ t.time = sched_clock() - per_cpu(blk_trace_cpu_offset, cpu); -+ put_cpu(); -+ -+ t.sector = sector; -+ t.bytes = bytes; -+ t.action = what; -+ t.error = error; -+ t.pdu_len = pdu_len; -+ -+ t.pid = current->pid; -+ memcpy(t.comm, current->comm, sizeof(t.comm)); -+ -+ local_irq_save(flags); -+ __relay_write(bt->rchan, &t, sizeof(t)); -+ if (pdu_len) -+ __relay_write(bt->rchan, pdu_data, pdu_len); -+ local_irq_restore(flags); -+} -+ -+static struct dentry *blk_tree_root; -+static DECLARE_MUTEX(blk_tree_mutex); -+ -+static inline void blk_remove_root(void) -+{ -+ if (relayfs_remove_dir(blk_tree_root) != -ENOTEMPTY) -+ blk_tree_root = NULL; -+} -+ -+static void blk_remove_tree(struct dentry *dir) -+{ -+ down(&blk_tree_mutex); -+ relayfs_remove_dir(dir); -+ blk_remove_root(); -+ up(&blk_tree_mutex); -+} -+ -+static struct dentry *blk_create_tree(const char *blk_name) -+{ -+ struct dentry *dir = NULL; -+ -+ down(&blk_tree_mutex); -+ -+ if (!blk_tree_root) { -+ blk_tree_root = relayfs_create_dir("block", NULL); -+ if (!blk_tree_root) -+ goto err; -+ } -+ -+ dir = relayfs_create_dir(blk_name, blk_tree_root); -+ if (!dir) -+ blk_remove_root(); -+ -+err: -+ up(&blk_tree_mutex); -+ return dir; -+} -+ -+void blk_cleanup_trace(struct blk_trace *bt) -+{ -+ relay_close(bt->rchan); -+ blk_remove_tree(bt->dir); -+ kfree(bt); -+} -+ -+int blk_stop_trace(struct block_device *bdev) -+{ -+ request_queue_t *q = bdev_get_queue(bdev); -+ struct blk_trace *bt = NULL; -+ int ret = -EINVAL; -+ -+ if (!q) -+ return -ENXIO; -+ -+ down(&bdev->bd_sem); -+ -+ if (q->blk_trace) { -+ bt = q->blk_trace; -+ q->blk_trace = NULL; -+ ret = 0; -+ } -+ -+ up(&bdev->bd_sem); -+ -+ if (bt) -+ blk_cleanup_trace(bt); -+ -+ return ret; -+} -+ -+int blk_start_trace(struct block_device *bdev, char __user *arg) -+{ -+ request_queue_t *q = bdev_get_queue(bdev); -+ struct blk_user_trace_setup buts; -+ struct blk_trace *bt = NULL; -+ struct dentry *dir = NULL; -+ char b[BDEVNAME_SIZE]; -+ int ret; -+ -+ if (!q) -+ return -ENXIO; -+ -+ if (copy_from_user(&buts, arg, sizeof(buts))) -+ return -EFAULT; -+ -+ if (!buts.buf_size || !buts.buf_nr) -+ return -EINVAL; -+ -+ strcpy(buts.name, bdevname(bdev, b)); -+ -+ if (copy_to_user(arg, &buts, sizeof(buts))) -+ return -EFAULT; -+ -+ down(&bdev->bd_sem); -+ ret = -EBUSY; -+ if (q->blk_trace) -+ goto err; -+ -+ ret = -ENOMEM; -+ bt = kmalloc(sizeof(*bt), GFP_KERNEL); -+ if (!bt) -+ goto err; -+ -+ ret = -ENOENT; -+ dir = blk_create_tree(bdevname(bdev, b)); -+ if (!dir) -+ goto err; -+ -+ bt->dir = dir; -+ atomic_set(&bt->sequence, 0); -+ -+ ret = -EIO; -+ bt->rchan = relay_open("trace", dir, buts.buf_size, buts.buf_nr, NULL); -+ if (!bt->rchan) -+ goto err; -+ -+ bt->act_mask = buts.act_mask; -+ if (!bt->act_mask) -+ bt->act_mask = (u16) -1; -+ -+ q->blk_trace = bt; -+ up(&bdev->bd_sem); -+ return 0; -+err: -+ up(&bdev->bd_sem); -+ if (dir) -+ blk_remove_tree(dir); -+ if (bt) -+ kfree(bt); -+ return ret; -+} -+ -+static void blk_trace_check_cpu_time(void *data) -+{ -+ unsigned long long a, b, *t; -+ struct timeval tv; -+ int cpu = get_cpu(); -+ -+ t = &per_cpu(blk_trace_cpu_offset, cpu); -+ -+ a = sched_clock(); -+ do_gettimeofday(&tv); -+ b = sched_clock(); -+ -+ *t = tv.tv_sec * 1000000000 + tv.tv_usec * 1000; -+ *t -= (a + b) / 2; -+ put_cpu(); -+} -+ -+static int blk_trace_calibrate_offsets(void) -+{ -+ unsigned long flags; -+ -+ smp_call_function(blk_trace_check_cpu_time, NULL, 1, 1); -+ local_irq_save(flags); -+ blk_trace_check_cpu_time(NULL); -+ local_irq_restore(flags); -+ -+ return 0; -+} -+ -+static __init int blk_trace_init(void) -+{ -+ return blk_trace_calibrate_offsets(); -+} -+ -+module_init(blk_trace_init); -+ ---- /dev/null 2005-09-03 12:52:15.000000000 +0200 -+++ linux-2.6/include/linux/blktrace.h 2005-09-08 08:33:20.000000000 +0200 -@@ -0,0 +1,150 @@ -+#ifndef BLKTRACE_H -+#define BLKTRACE_H -+ -+#include -+#include -+#include -+ -+/* -+ * Trace categories -+ */ -+enum { -+ BLK_TC_READ = 1 << 0, /* reads */ -+ BLK_TC_WRITE = 1 << 1, /* writes */ -+ BLK_TC_BARRIER = 1 << 2, /* barrier */ -+ BLK_TC_SYNC = 1 << 3, /* barrier */ -+ BLK_TC_QUEUE = 1 << 4, /* queueing/merging */ -+ BLK_TC_REQUEUE = 1 << 5, /* requeueing */ -+ BLK_TC_ISSUE = 1 << 6, /* issue */ -+ BLK_TC_COMPLETE = 1 << 7, /* completions */ -+ BLK_TC_FS = 1 << 8, /* fs requests */ -+ BLK_TC_PC = 1 << 9, /* pc requests */ -+ -+ BLK_TC_END = 1 << 15, /* only 16-bits, reminder */ -+}; -+ -+#define BLK_TC_SHIFT (16) -+#define BLK_TC_ACT(act) ((act) << BLK_TC_SHIFT) -+ -+/* -+ * Basic trace actions -+ */ -+enum { -+ __BLK_TA_QUEUE = 1, /* queued */ -+ __BLK_TA_BACKMERGE, /* back merged to existing rq */ -+ __BLK_TA_FRONTMERGE, /* front merge to existing rq */ -+ __BLK_TA_GETRQ, /* allocated new request */ -+ __BLK_TA_SLEEPRQ, /* sleeping on rq allocation */ -+ __BLK_TA_REQUEUE, /* request requeued */ -+ __BLK_TA_ISSUE, /* sent to driver */ -+ __BLK_TA_COMPLETE, /* completed by driver */ -+}; -+ -+/* -+ * Trace actions in full. Additionally, read or write is masked -+ */ -+#define BLK_TA_QUEUE (__BLK_TA_QUEUE | BLK_TC_ACT(BLK_TC_QUEUE)) -+#define BLK_TA_BACKMERGE (__BLK_TA_BACKMERGE | BLK_TC_ACT(BLK_TC_QUEUE)) -+#define BLK_TA_FRONTMERGE (__BLK_TA_FRONTMERGE | BLK_TC_ACT(BLK_TC_QUEUE)) -+#define BLK_TA_GETRQ (__BLK_TA_GETRQ | BLK_TC_ACT(BLK_TC_QUEUE)) -+#define BLK_TA_SLEEPRQ (__BLK_TA_SLEEPRQ | BLK_TC_ACT(BLK_TC_QUEUE)) -+#define BLK_TA_REQUEUE (__BLK_TA_REQUEUE | BLK_TC_ACT(BLK_TC_REQUEUE)) -+#define BLK_TA_ISSUE (__BLK_TA_ISSUE | BLK_TC_ACT(BLK_TC_ISSUE)) -+#define BLK_TA_COMPLETE (__BLK_TA_COMPLETE| BLK_TC_ACT(BLK_TC_COMPLETE)) -+ -+#define BLK_IO_TRACE_MAGIC 0x65617400 -+#define BLK_IO_TRACE_VERSION 0x04 -+ -+/* -+ * The trace itself -+ */ -+struct blk_io_trace { -+ u32 magic; /* MAGIC << 8 | version */ -+ u32 sequence; /* event number */ -+ u64 time; /* in microseconds */ -+ u64 sector; /* disk offset */ -+ u32 bytes; /* transfer length */ -+ u32 action; /* what happened */ -+ u32 pid; /* who did it */ -+ u32 cpu; /* on what cpu did it happen */ -+ u16 error; /* completion error */ -+ u16 pdu_len; /* length of data after this trace */ -+ char comm[16]; /* task command name (TASK_COMM_LEN) */ -+}; -+ -+struct blk_trace { -+ struct dentry *dir; -+ struct rchan *rchan; -+ atomic_t sequence; -+ u16 act_mask; -+}; -+ -+/* -+ * User setup structure passed with BLKSTARTTRACE -+ */ -+struct blk_user_trace_setup { -+ char name[BDEVNAME_SIZE]; /* output */ -+ u16 act_mask; /* input */ -+ u32 buf_size; /* input */ -+ u32 buf_nr; /* input */ -+}; -+ -+#if defined(CONFIG_BLK_DEV_IO_TRACE) -+extern int blk_start_trace(struct block_device *, char __user *); -+extern int blk_stop_trace(struct block_device *); -+extern void blk_cleanup_trace(struct blk_trace *); -+extern void __blk_add_trace(struct blk_trace *, sector_t, int, int, u32, int, int, char *); -+ -+static inline void blk_add_trace_rq(struct request_queue *q, struct request *rq, -+ u32 what) -+{ -+ struct blk_trace *bt = q->blk_trace; -+ int rw = rq->flags & 0x07; -+ -+ if (likely(!bt)) -+ return; -+ -+ if (blk_pc_request(rq)) { -+ what |= BLK_TC_ACT(BLK_TC_PC); -+ __blk_add_trace(bt, 0, rq->data_len, rw, what, rq->errors, sizeof(rq->cmd), rq->cmd); -+ } else { -+ what |= BLK_TC_ACT(BLK_TC_FS); -+ __blk_add_trace(bt, rq->hard_sector, rq->hard_nr_sectors << 9, rw, what, rq->errors, 0, NULL); -+ } -+} -+ -+static inline void blk_add_trace_bio(struct request_queue *q, struct bio *bio, -+ u32 what) -+{ -+ struct blk_trace *bt = q->blk_trace; -+ -+ if (likely(!bt)) -+ return; -+ -+ __blk_add_trace(bt, bio->bi_sector, bio->bi_size, bio->bi_rw, what, !bio_flagged(bio, BIO_UPTODATE), 0, NULL); -+} -+ -+static inline void blk_add_trace_generic(struct request_queue *q, -+ struct bio *bio, int rw, u32 what) -+{ -+ struct blk_trace *bt = q->blk_trace; -+ -+ if (likely(!bt)) -+ return; -+ -+ if (bio) -+ blk_add_trace_bio(q, bio, what); -+ else -+ __blk_add_trace(bt, 0, 0, rw, what, 0, 0, NULL); -+} -+ -+#else /* !CONFIG_BLK_DEV_IO_TRACE */ -+#define blk_start_trace(bdev, arg) (-EINVAL) -+#define blk_stop_trace(bdev) (-EINVAL) -+#define blk_cleanup_trace(bt) do { } while (0) -+#define blk_add_trace_rq(q, rq, what) do { } while (0) -+#define blk_add_trace_bio(q, rq, what) do { } while (0) -+#define blk_add_trace_generic(q, rq, rw, what) do { } while (0) -+#endif /* CONFIG_BLK_DEV_IO_TRACE */ -+ -+#endif diff --git a/kernel/blk-trace-2.6.13-git-F0 b/kernel/blk-trace-2.6.13-git-F0 new file mode 100644 index 0000000..ec8ec8f --- /dev/null +++ b/kernel/blk-trace-2.6.13-git-F0 @@ -0,0 +1,649 @@ +diff --git a/Documentation/fb/vesafb.txt b/Documentation/fb/vesafb.txt +diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig +--- a/drivers/block/Kconfig ++++ b/drivers/block/Kconfig +@@ -419,6 +419,14 @@ config LBD + your machine, or if you want to have a raid or loopback device + bigger than 2TB. Otherwise say N. + ++config BLK_DEV_IO_TRACE ++ bool "Support for tracing block io actions" ++ select RELAYFS_FS ++ help ++ Say Y here, if you want to be able to trace the block layer actions ++ on a given queue. ++ ++ + config CDROM_PKTCDVD + tristate "Packet writing on CD/DVD media" + depends on !UML +diff --git a/drivers/block/Makefile b/drivers/block/Makefile +--- a/drivers/block/Makefile ++++ b/drivers/block/Makefile +@@ -45,3 +45,5 @@ obj-$(CONFIG_VIODASD) += viodasd.o + obj-$(CONFIG_BLK_DEV_SX8) += sx8.o + obj-$(CONFIG_BLK_DEV_UB) += ub.o + ++obj-$(CONFIG_BLK_DEV_IO_TRACE) += blktrace.o ++ +diff --git a/drivers/block/blktrace.c b/drivers/block/blktrace.c +--- a/drivers/block/blktrace.c ++++ b/drivers/block/blktrace.c +@@ -36,6 +36,7 @@ void __blk_add_trace(struct blk_trace *b + t.time = sched_clock() - per_cpu(blk_trace_cpu_offset, cpu); + put_cpu(); + ++ t.device = bt->dev; + t.sector = sector; + t.bytes = bytes; + t.action = what; +@@ -161,6 +162,7 @@ int blk_start_trace(struct block_device + goto err; + + bt->dir = dir; ++ bt->dev = bdev->bd_dev; + atomic_set(&bt->sequence, 0); + + ret = -EIO; +diff --git a/drivers/block/cfq-iosched.c b/drivers/block/cfq-iosched.c +--- a/drivers/block/cfq-iosched.c ++++ b/drivers/block/cfq-iosched.c +@@ -2260,6 +2260,8 @@ static void cfq_put_cfqd(struct cfq_data + if (!atomic_dec_and_test(&cfqd->ref)) + return; + ++ blk_put_queue(q); ++ + cfq_shutdown_timer_wq(cfqd); + q->elevator->elevator_data = NULL; + +@@ -2316,6 +2318,7 @@ static int cfq_init_queue(request_queue_ + e->elevator_data = cfqd; + + cfqd->queue = q; ++ atomic_inc(&q->refcnt); + + cfqd->max_queued = q->nr_requests / 4; + q->nr_batching = cfq_queued; +diff --git a/drivers/block/elevator.c b/drivers/block/elevator.c +--- a/drivers/block/elevator.c ++++ b/drivers/block/elevator.c +@@ -34,6 +34,7 @@ + #include + #include + #include ++#include + + #include + +@@ -371,6 +372,9 @@ struct request *elv_next_request(request + int ret; + + while ((rq = __elv_next_request(q)) != NULL) { ++ ++ blk_add_trace_rq(q, rq, BLK_TA_ISSUE); ++ + /* + * just mark as started even if we don't start it, a request + * that has been delayed should not be passed by new incoming +diff --git a/drivers/block/ioctl.c b/drivers/block/ioctl.c +--- a/drivers/block/ioctl.c ++++ b/drivers/block/ioctl.c +@@ -4,6 +4,7 @@ + #include + #include + #include ++#include + #include + + static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg __user *arg) +@@ -188,6 +189,10 @@ static int blkdev_locked_ioctl(struct fi + return put_ulong(arg, bdev->bd_inode->i_size >> 9); + case BLKGETSIZE64: + return put_u64(arg, bdev->bd_inode->i_size); ++ case BLKSTARTTRACE: ++ return blk_start_trace(bdev, (char __user *) arg); ++ case BLKSTOPTRACE: ++ return blk_stop_trace(bdev); + } + return -ENOIOCTLCMD; + } +diff --git a/drivers/block/ll_rw_blk.c b/drivers/block/ll_rw_blk.c +--- a/drivers/block/ll_rw_blk.c ++++ b/drivers/block/ll_rw_blk.c +@@ -29,6 +29,7 @@ + #include + #include + #include ++#include + + /* + * for max sense size +@@ -1624,6 +1625,11 @@ void blk_cleanup_queue(request_queue_t * + if (q->queue_tags) + __blk_queue_free_tags(q); + ++ if (q->blk_trace) { ++ blk_cleanup_trace(q->blk_trace); ++ q->blk_trace = NULL; ++ } ++ + blk_queue_ordered(q, QUEUE_ORDERED_NONE); + + kmem_cache_free(requestq_cachep, q); +@@ -1970,6 +1976,8 @@ rq_starved: + + rq_init(q, rq); + rq->rl = rl; ++ ++ blk_add_trace_generic(q, bio, rw, BLK_TA_GETRQ); + out: + return rq; + } +@@ -1998,6 +2006,8 @@ static struct request *get_request_wait( + if (!rq) { + struct io_context *ioc; + ++ blk_add_trace_generic(q, bio, rw, BLK_TA_SLEEPRQ); ++ + __generic_unplug_device(q); + spin_unlock_irq(q->queue_lock); + io_schedule(); +@@ -2051,6 +2061,8 @@ EXPORT_SYMBOL(blk_get_request); + */ + void blk_requeue_request(request_queue_t *q, struct request *rq) + { ++ blk_add_trace_rq(q, rq, BLK_TA_REQUEUE); ++ + if (blk_rq_tagged(rq)) + blk_queue_end_tag(q, rq); + +@@ -2714,6 +2726,8 @@ static int __make_request(request_queue_ + if (!q->back_merge_fn(q, req, bio)) + break; + ++ blk_add_trace_bio(q, bio, BLK_TA_BACKMERGE); ++ + req->biotail->bi_next = bio; + req->biotail = bio; + req->nr_sectors = req->hard_nr_sectors += nr_sectors; +@@ -2729,6 +2743,8 @@ static int __make_request(request_queue_ + if (!q->front_merge_fn(q, req, bio)) + break; + ++ blk_add_trace_bio(q, bio, BLK_TA_FRONTMERGE); ++ + bio->bi_next = req->bio; + req->bio = bio; + +@@ -2794,6 +2810,8 @@ get_rq: + req->rq_disk = bio->bi_bdev->bd_disk; + req->start_time = jiffies; + ++ blk_add_trace_bio(q, bio, BLK_TA_QUEUE); ++ + spin_lock_irq(q->queue_lock); + if (elv_queue_empty(q)) + blk_plug_device(q); +@@ -3030,6 +3048,10 @@ end_io: + blk_partition_remap(bio); + + ret = q->make_request_fn(q, bio); ++ ++ if (ret) ++ blk_add_trace_bio(q, bio, BLK_TA_QUEUE); ++ + } while (ret); + } + +@@ -3148,6 +3170,8 @@ static int __end_that_request_first(stru + int total_bytes, bio_nbytes, error, next_idx = 0; + struct bio *bio; + ++ blk_add_trace_rq(req->q, req, BLK_TA_COMPLETE); ++ + /* + * extend uptodate bool to allow < 0 value to be direct io error + */ +diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h +--- a/include/linux/blkdev.h ++++ b/include/linux/blkdev.h +@@ -22,6 +22,7 @@ typedef struct request_queue request_que + struct elevator_queue; + typedef struct elevator_queue elevator_t; + struct request_pm_state; ++struct blk_trace; + + #define BLKDEV_MIN_RQ 4 + #define BLKDEV_MAX_RQ 128 /* Default maximum */ +@@ -412,6 +413,8 @@ struct request_queue + */ + struct request *flush_rq; + unsigned char ordered; ++ ++ struct blk_trace *blk_trace; + }; + + enum { +diff --git a/include/linux/blktrace.h b/include/linux/blktrace.h +--- a/include/linux/blktrace.h ++++ b/include/linux/blktrace.h +@@ -53,7 +53,7 @@ enum { + #define BLK_TA_COMPLETE (__BLK_TA_COMPLETE| BLK_TC_ACT(BLK_TC_COMPLETE)) + + #define BLK_IO_TRACE_MAGIC 0x65617400 +-#define BLK_IO_TRACE_VERSION 0x04 ++#define BLK_IO_TRACE_VERSION 0x05 + + /* + * The trace itself +@@ -69,6 +69,7 @@ struct blk_io_trace { + u32 cpu; /* on what cpu did it happen */ + u16 error; /* completion error */ + u16 pdu_len; /* length of data after this trace */ ++ u32 device /* device number */ + char comm[16]; /* task command name (TASK_COMM_LEN) */ + }; + +@@ -76,6 +77,7 @@ struct blk_trace { + struct dentry *dir; + struct rchan *rchan; + atomic_t sequence; ++ u32 dev; + u16 act_mask; + }; + +diff --git a/include/linux/fs.h b/include/linux/fs.h +--- a/include/linux/fs.h ++++ b/include/linux/fs.h +@@ -196,6 +196,8 @@ extern int dir_notify_enable; + #define BLKBSZGET _IOR(0x12,112,size_t) + #define BLKBSZSET _IOW(0x12,113,size_t) + #define BLKGETSIZE64 _IOR(0x12,114,size_t) /* return device size in bytes (u64 *arg) */ ++#define BLKSTARTTRACE _IOWR(0x12,115,struct blk_user_trace_setup) ++#define BLKSTOPTRACE _IO(0x12,116) + + #define BMAP_IOCTL 1 /* obsolete - kept for compatibility */ + #define FIBMAP _IO(0x00,1) /* bmap access */ +--- /dev/null 2005-09-03 12:52:15.000000000 +0200 ++++ linux-2.6/drivers/block/blktrace.c 2005-09-12 09:46:27.000000000 +0200 +@@ -0,0 +1,224 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static DEFINE_PER_CPU(unsigned long long, blk_trace_cpu_offset) = { 0, }; ++ ++void __blk_add_trace(struct blk_trace *bt, sector_t sector, int bytes, ++ int rw, u32 what, int error, int pdu_len, char *pdu_data) ++{ ++ struct blk_io_trace t; ++ unsigned long flags; ++ int cpu; ++ ++ if (rw & (1 << BIO_RW_BARRIER)) ++ what |= BLK_TC_ACT(BLK_TC_BARRIER); ++ if (rw & (1 << BIO_RW_SYNC)) ++ what |= BLK_TC_ACT(BLK_TC_SYNC); ++ ++ if (rw & WRITE) ++ what |= BLK_TC_ACT(BLK_TC_WRITE); ++ else ++ what |= BLK_TC_ACT(BLK_TC_READ); ++ ++ if (((bt->act_mask << BLK_TC_SHIFT) & what) == 0) ++ return; ++ ++ t.magic = BLK_IO_TRACE_MAGIC | BLK_IO_TRACE_VERSION; ++ t.sequence = atomic_add_return(1, &bt->sequence); ++ ++ cpu = get_cpu(); ++ t.cpu = cpu; ++ t.time = sched_clock() - per_cpu(blk_trace_cpu_offset, cpu); ++ put_cpu(); ++ ++ t.device = bt->dev; ++ t.sector = sector; ++ t.bytes = bytes; ++ t.action = what; ++ t.error = error; ++ t.pdu_len = pdu_len; ++ ++ t.pid = current->pid; ++ memcpy(t.comm, current->comm, sizeof(t.comm)); ++ ++ local_irq_save(flags); ++ __relay_write(bt->rchan, &t, sizeof(t)); ++ if (pdu_len) ++ __relay_write(bt->rchan, pdu_data, pdu_len); ++ local_irq_restore(flags); ++} ++ ++static struct dentry *blk_tree_root; ++static DECLARE_MUTEX(blk_tree_mutex); ++ ++static inline void blk_remove_root(void) ++{ ++ if (relayfs_remove_dir(blk_tree_root) != -ENOTEMPTY) ++ blk_tree_root = NULL; ++} ++ ++static void blk_remove_tree(struct dentry *dir) ++{ ++ down(&blk_tree_mutex); ++ relayfs_remove_dir(dir); ++ blk_remove_root(); ++ up(&blk_tree_mutex); ++} ++ ++static struct dentry *blk_create_tree(const char *blk_name) ++{ ++ struct dentry *dir = NULL; ++ ++ down(&blk_tree_mutex); ++ ++ if (!blk_tree_root) { ++ blk_tree_root = relayfs_create_dir("block", NULL); ++ if (!blk_tree_root) ++ goto err; ++ } ++ ++ dir = relayfs_create_dir(blk_name, blk_tree_root); ++ if (!dir) ++ blk_remove_root(); ++ ++err: ++ up(&blk_tree_mutex); ++ return dir; ++} ++ ++void blk_cleanup_trace(struct blk_trace *bt) ++{ ++ relay_close(bt->rchan); ++ blk_remove_tree(bt->dir); ++ kfree(bt); ++} ++ ++int blk_stop_trace(struct block_device *bdev) ++{ ++ request_queue_t *q = bdev_get_queue(bdev); ++ struct blk_trace *bt = NULL; ++ int ret = -EINVAL; ++ ++ if (!q) ++ return -ENXIO; ++ ++ down(&bdev->bd_sem); ++ ++ if (q->blk_trace) { ++ bt = q->blk_trace; ++ q->blk_trace = NULL; ++ ret = 0; ++ } ++ ++ up(&bdev->bd_sem); ++ ++ if (bt) ++ blk_cleanup_trace(bt); ++ ++ return ret; ++} ++ ++int blk_start_trace(struct block_device *bdev, char __user *arg) ++{ ++ request_queue_t *q = bdev_get_queue(bdev); ++ struct blk_user_trace_setup buts; ++ struct blk_trace *bt = NULL; ++ struct dentry *dir = NULL; ++ char b[BDEVNAME_SIZE]; ++ int ret; ++ ++ if (!q) ++ return -ENXIO; ++ ++ if (copy_from_user(&buts, arg, sizeof(buts))) ++ return -EFAULT; ++ ++ if (!buts.buf_size || !buts.buf_nr) ++ return -EINVAL; ++ ++ strcpy(buts.name, bdevname(bdev, b)); ++ ++ if (copy_to_user(arg, &buts, sizeof(buts))) ++ return -EFAULT; ++ ++ down(&bdev->bd_sem); ++ ret = -EBUSY; ++ if (q->blk_trace) ++ goto err; ++ ++ ret = -ENOMEM; ++ bt = kmalloc(sizeof(*bt), GFP_KERNEL); ++ if (!bt) ++ goto err; ++ ++ ret = -ENOENT; ++ dir = blk_create_tree(bdevname(bdev, b)); ++ if (!dir) ++ goto err; ++ ++ bt->dir = dir; ++ bt->dev = bdev->bd_dev; ++ atomic_set(&bt->sequence, 0); ++ ++ ret = -EIO; ++ bt->rchan = relay_open("trace", dir, buts.buf_size, buts.buf_nr, NULL); ++ if (!bt->rchan) ++ goto err; ++ ++ bt->act_mask = buts.act_mask; ++ if (!bt->act_mask) ++ bt->act_mask = (u16) -1; ++ ++ q->blk_trace = bt; ++ up(&bdev->bd_sem); ++ return 0; ++err: ++ up(&bdev->bd_sem); ++ if (dir) ++ blk_remove_tree(dir); ++ if (bt) ++ kfree(bt); ++ return ret; ++} ++ ++static void blk_trace_check_cpu_time(void *data) ++{ ++ unsigned long long a, b, *t; ++ struct timeval tv; ++ int cpu = get_cpu(); ++ ++ t = &per_cpu(blk_trace_cpu_offset, cpu); ++ ++ a = sched_clock(); ++ do_gettimeofday(&tv); ++ b = sched_clock(); ++ ++ *t = tv.tv_sec * 1000000000 + tv.tv_usec * 1000; ++ *t -= (a + b) / 2; ++ put_cpu(); ++} ++ ++static int blk_trace_calibrate_offsets(void) ++{ ++ unsigned long flags; ++ ++ smp_call_function(blk_trace_check_cpu_time, NULL, 1, 1); ++ local_irq_save(flags); ++ blk_trace_check_cpu_time(NULL); ++ local_irq_restore(flags); ++ ++ return 0; ++} ++ ++static __init int blk_trace_init(void) ++{ ++ return blk_trace_calibrate_offsets(); ++} ++ ++module_init(blk_trace_init); ++ +--- /dev/null 2005-09-03 12:52:15.000000000 +0200 ++++ linux-2.6/include/linux/blktrace.h 2005-09-12 09:46:53.000000000 +0200 +@@ -0,0 +1,152 @@ ++#ifndef BLKTRACE_H ++#define BLKTRACE_H ++ ++#include ++#include ++#include ++ ++/* ++ * Trace categories ++ */ ++enum { ++ BLK_TC_READ = 1 << 0, /* reads */ ++ BLK_TC_WRITE = 1 << 1, /* writes */ ++ BLK_TC_BARRIER = 1 << 2, /* barrier */ ++ BLK_TC_SYNC = 1 << 3, /* barrier */ ++ BLK_TC_QUEUE = 1 << 4, /* queueing/merging */ ++ BLK_TC_REQUEUE = 1 << 5, /* requeueing */ ++ BLK_TC_ISSUE = 1 << 6, /* issue */ ++ BLK_TC_COMPLETE = 1 << 7, /* completions */ ++ BLK_TC_FS = 1 << 8, /* fs requests */ ++ BLK_TC_PC = 1 << 9, /* pc requests */ ++ ++ BLK_TC_END = 1 << 15, /* only 16-bits, reminder */ ++}; ++ ++#define BLK_TC_SHIFT (16) ++#define BLK_TC_ACT(act) ((act) << BLK_TC_SHIFT) ++ ++/* ++ * Basic trace actions ++ */ ++enum { ++ __BLK_TA_QUEUE = 1, /* queued */ ++ __BLK_TA_BACKMERGE, /* back merged to existing rq */ ++ __BLK_TA_FRONTMERGE, /* front merge to existing rq */ ++ __BLK_TA_GETRQ, /* allocated new request */ ++ __BLK_TA_SLEEPRQ, /* sleeping on rq allocation */ ++ __BLK_TA_REQUEUE, /* request requeued */ ++ __BLK_TA_ISSUE, /* sent to driver */ ++ __BLK_TA_COMPLETE, /* completed by driver */ ++}; ++ ++/* ++ * Trace actions in full. Additionally, read or write is masked ++ */ ++#define BLK_TA_QUEUE (__BLK_TA_QUEUE | BLK_TC_ACT(BLK_TC_QUEUE)) ++#define BLK_TA_BACKMERGE (__BLK_TA_BACKMERGE | BLK_TC_ACT(BLK_TC_QUEUE)) ++#define BLK_TA_FRONTMERGE (__BLK_TA_FRONTMERGE | BLK_TC_ACT(BLK_TC_QUEUE)) ++#define BLK_TA_GETRQ (__BLK_TA_GETRQ | BLK_TC_ACT(BLK_TC_QUEUE)) ++#define BLK_TA_SLEEPRQ (__BLK_TA_SLEEPRQ | BLK_TC_ACT(BLK_TC_QUEUE)) ++#define BLK_TA_REQUEUE (__BLK_TA_REQUEUE | BLK_TC_ACT(BLK_TC_REQUEUE)) ++#define BLK_TA_ISSUE (__BLK_TA_ISSUE | BLK_TC_ACT(BLK_TC_ISSUE)) ++#define BLK_TA_COMPLETE (__BLK_TA_COMPLETE| BLK_TC_ACT(BLK_TC_COMPLETE)) ++ ++#define BLK_IO_TRACE_MAGIC 0x65617400 ++#define BLK_IO_TRACE_VERSION 0x05 ++ ++/* ++ * The trace itself ++ */ ++struct blk_io_trace { ++ u32 magic; /* MAGIC << 8 | version */ ++ u32 sequence; /* event number */ ++ u64 time; /* in microseconds */ ++ u64 sector; /* disk offset */ ++ u32 bytes; /* transfer length */ ++ u32 action; /* what happened */ ++ u32 pid; /* who did it */ ++ u32 cpu; /* on what cpu did it happen */ ++ u16 error; /* completion error */ ++ u16 pdu_len; /* length of data after this trace */ ++ u32 device /* device number */ ++ char comm[16]; /* task command name (TASK_COMM_LEN) */ ++}; ++ ++struct blk_trace { ++ struct dentry *dir; ++ struct rchan *rchan; ++ atomic_t sequence; ++ u32 dev; ++ u16 act_mask; ++}; ++ ++/* ++ * User setup structure passed with BLKSTARTTRACE ++ */ ++struct blk_user_trace_setup { ++ char name[BDEVNAME_SIZE]; /* output */ ++ u16 act_mask; /* input */ ++ u32 buf_size; /* input */ ++ u32 buf_nr; /* input */ ++}; ++ ++#if defined(CONFIG_BLK_DEV_IO_TRACE) ++extern int blk_start_trace(struct block_device *, char __user *); ++extern int blk_stop_trace(struct block_device *); ++extern void blk_cleanup_trace(struct blk_trace *); ++extern void __blk_add_trace(struct blk_trace *, sector_t, int, int, u32, int, int, char *); ++ ++static inline void blk_add_trace_rq(struct request_queue *q, struct request *rq, ++ u32 what) ++{ ++ struct blk_trace *bt = q->blk_trace; ++ int rw = rq->flags & 0x07; ++ ++ if (likely(!bt)) ++ return; ++ ++ if (blk_pc_request(rq)) { ++ what |= BLK_TC_ACT(BLK_TC_PC); ++ __blk_add_trace(bt, 0, rq->data_len, rw, what, rq->errors, sizeof(rq->cmd), rq->cmd); ++ } else { ++ what |= BLK_TC_ACT(BLK_TC_FS); ++ __blk_add_trace(bt, rq->hard_sector, rq->hard_nr_sectors << 9, rw, what, rq->errors, 0, NULL); ++ } ++} ++ ++static inline void blk_add_trace_bio(struct request_queue *q, struct bio *bio, ++ u32 what) ++{ ++ struct blk_trace *bt = q->blk_trace; ++ ++ if (likely(!bt)) ++ return; ++ ++ __blk_add_trace(bt, bio->bi_sector, bio->bi_size, bio->bi_rw, what, !bio_flagged(bio, BIO_UPTODATE), 0, NULL); ++} ++ ++static inline void blk_add_trace_generic(struct request_queue *q, ++ struct bio *bio, int rw, u32 what) ++{ ++ struct blk_trace *bt = q->blk_trace; ++ ++ if (likely(!bt)) ++ return; ++ ++ if (bio) ++ blk_add_trace_bio(q, bio, what); ++ else ++ __blk_add_trace(bt, 0, 0, rw, what, 0, 0, NULL); ++} ++ ++#else /* !CONFIG_BLK_DEV_IO_TRACE */ ++#define blk_start_trace(bdev, arg) (-EINVAL) ++#define blk_stop_trace(bdev) (-EINVAL) ++#define blk_cleanup_trace(bt) do { } while (0) ++#define blk_add_trace_rq(q, rq, what) do { } while (0) ++#define blk_add_trace_bio(q, rq, what) do { } while (0) ++#define blk_add_trace_generic(q, rq, rw, what) do { } while (0) ++#endif /* CONFIG_BLK_DEV_IO_TRACE */ ++ ++#endif