int pipe;
};
-static char blkiomon_version[] = "0.2";
+static char blkiomon_version[] = "0.3";
static FILE *ifp;
static int interval = -1;
static struct trace *vacant_traces_list = NULL;
static int vacant_traces = 0;
-static struct rb_root trace_tree = RB_ROOT;
#define TRACE_HASH_SIZE 128
struct trace *thash[TRACE_HASH_SIZE] = {};
static struct dstat *dstat_list[2] = {};
static int dstat_curr = 0;
-static struct output human, binary, debug;
+static struct output drvdata, human, binary, debug;
static char *msg_q_name = NULL;
static int msg_q_id = -1, msg_q = -1;
} else
dstat = malloc(sizeof(*dstat));
if (!dstat) {
- perror("blkiomon: could not allocate device statistic");
+ fprintf(stderr,
+ "blkiomon: could not allocate device statistic");
return NULL;
}
- memset(dstat, 0, sizeof(*dstat));
+ blkiomon_stat_init(&dstat->msg.stat);
return dstat;
}
goto out;
dstat->msg.stat.device = device;
- dstat->msg.stat.size_mm.min = -1ULL;
- dstat->msg.stat.d2c_mm.min = -1ULL;
rb_link_node(&dstat->node, search.parent, search.node_ptr);
rb_insert_color(&dstat->node, &dstat_tree[dstat_curr]);
while (1) {
wake.tv_sec += interval;
if (clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &wake, &r)) {
- perror("blkiomon: interrupted sleep");
+ fprintf(stderr, "blkiomon: interrupted sleep");
continue;
}
struct blkiomon_stat *p;
__u64 d2c = (bit_c->time - bit_d->time) / 1000; /* ns -> us */
__u32 size = bit_d->bytes;
+ __u64 thrput = size * 1000 / d2c;
dstat = blkiomon_get_dstat(bit_d->device);
if (!dstat)
return 1;
p = &dstat->msg.stat;
- if (BLK_DATADIR(bit_c->action) & BLK_TC_READ)
- p->read++;
- else if (BLK_DATADIR(bit_c->action) & BLK_TC_WRITE)
- p->write++;
- else
+ if (BLK_DATADIR(bit_c->action) & BLK_TC_READ) {
+ minmax_account(&p->thrput_r, thrput);
+ minmax_account(&p->size_r, size);
+ minmax_account(&p->d2c_r, d2c);
+ } else if (BLK_DATADIR(bit_c->action) & BLK_TC_WRITE) {
+ minmax_account(&p->thrput_w, thrput);
+ minmax_account(&p->size_w, size);
+ minmax_account(&p->d2c_w, d2c);
+ } else
p->bidir++;
histlog2_account(p->size_hist, size, &size_hist);
histlog2_account(p->d2c_hist, d2c, &d2c_hist);
- minmax_account(&p->size_mm, size);
- minmax_account(&p->d2c_mm, d2c);
return 0;
}
return t_old;
}
+static int blkiomon_dump_drvdata(struct blk_io_trace *bit, void *pdu_buf)
+{
+ if (!drvdata.fn)
+ return 0;
+
+ if (fwrite(bit, sizeof(*bit), 1, drvdata.fp) != 1)
+ goto failed;
+ if (fwrite(pdu_buf, bit->pdu_len, 1, drvdata.fp) != 1)
+ goto failed;
+ if (drvdata.pipe && fflush(drvdata.fp))
+ goto failed;
+ return 0;
+
+failed:
+ fprintf(stderr, "blkiomon: could not write to %s\n", drvdata.fn);
+ fclose(drvdata.fp);
+ drvdata.fn = NULL;
+ return 1;
+}
+
static int blkiomon_do_fifo(void)
{
struct trace *t;
}
if (ferror(ifp)) {
clearerr(ifp);
- perror("blkiomon: error while reading trace");
+ fprintf(stderr, "blkiomon: error while reading trace");
break;
}
- if (data_is_native == -1 && check_data_endianness(bit->magic))
+ if (data_is_native == -1 && check_data_endianness(bit->magic)) {
+ fprintf(stderr, "blkiomon: endianess problem\n");
break;
+ }
/* endianess */
trace_to_cpu(bit);
if (verify_trace(bit)) {
- perror("blkiomon: bad trace");
+ fprintf(stderr, "blkiomon: bad trace\n");
break;
}
pdu_buf = realloc(pdu_buf, bit->pdu_len);
if (fread(pdu_buf, bit->pdu_len, 1, ifp) != 1) {
clearerr(ifp);
- perror("blkiomon: could not read payload");
+ fprintf(stderr, "blkiomon: could not read payload\n");
break;
}
}
t->sequence = sequence++;
+ /* forward low-level device driver trace to other tool */
+ if (bit->action & BLK_TC_ACT(BLK_TC_DRV_DATA)) {
+ driverdata++;
+ if (blkiomon_dump_drvdata(bit, pdu_buf)) {
+ fprintf(stderr, "blkiomon: could not send trace\n");
+ break;
+ }
+ continue;
+ }
+
if (!(bit->action & BLK_TC_ACT(BLK_TC_ISSUE | BLK_TC_COMPLETE)))
continue;
/* try to find matching trace and update statistics */
t = blkiomon_do_trace(t);
- if (!t)
+ if (!t) {
+ fprintf(stderr, "blkiomon: could not alloc trace\n");
break;
+ }
bit = &t->bit;
/* t and bit will be recycled for next incoming trace */
}
static void blkiomon_debug(void)
{
- struct rb_node *n;
+ int i;
struct trace *t;
if (!debug.fn)
return;
- for (n = rb_first(&trace_tree); n; n = rb_next(n)) {
- t = rb_entry(n, struct trace, node);
- dump_bit(t, "leftover");
- leftover++;
- }
+ for (i = 0; i < TRACE_HASH_SIZE; i++)
+ for (t = thash[i]; t; t = t->next) {
+ dump_bit(t, "leftover");
+ leftover++;
+ }
+
fprintf(debug.fp, "%ld leftover, %ld match, %ld mismatch, "
"%ld driverdata, %ld overall\n",
leftover, match, mismatch, driverdata, sequence);
}
-#define S_OPTS "b:D:h:I:Q:q:m:V"
+#define S_OPTS "b:d:D:h:I:Q:q:m:V"
static char usage_str[] = "\n\nblkiomon " \
"-I <interval> | --interval=<interval>\n" \
"[ -h <file> | --human-readable=<file> ]\n" \
"[ -b <file> | --binary=<file> ]\n" \
+ "[ -d <file> | --dump-lldd=<file> ]\n" \
"[ -D <file> | --debug=<file> ]\n" \
- "[ -Q <path name> | --msg-queue-name=<path name>]\n" \
+ "[ -Q <path name> | --msg-queue=<path name>]\n" \
"[ -q <msg queue id> | --msg-queue-id=<msg queue id>]\n" \
"[ -m <msg id> | --msg-id=<msg id>]\n" \
"[ -V | --version ]\n\n" \
"\t-I Sample interval.\n" \
"\t-h Human-readable output file.\n" \
"\t-b Binary output file.\n" \
+ "\t-d Output file for data emitted by low level device driver.\n" \
"\t-D Output file for debugging data.\n" \
"\t-Qqm Output to message queue using given ID for messages.\n" \
"\t-V Print program version.\n\n";
.flag = NULL,
.val = 'b'
},
+ {
+ .name = "dump-lldd",
+ .has_arg = required_argument,
+ .flag = NULL,
+ .val = 'd'
+ },
{
.name = "debug",
.has_arg = required_argument,
case 'b':
binary.fn = optarg;
break;
+ case 'd':
+ drvdata.fn = optarg;
+ break;
case 'D':
debug.fn = optarg;
break;
return 1;
if (blkiomon_open_output(&binary))
return 1;
+ if (blkiomon_open_output(&drvdata))
+ return 1;
if (blkiomon_open_output(&debug))
return 1;
if (blkiomon_open_msg_q())
return 1;
if (pthread_create(&interval_thread, NULL, blkiomon_interval, NULL)) {
- perror("blkiomon: could not create thread");
+ fprintf(stderr, "blkiomon: could not create thread");
return 1;
}