Merge branch 'fix-m' into add-P
[blktrace.git] / blkiomon.c
CommitLineData
cc19ddd6
MP
1/*
2 * I/O monitor based on block queue trace data
3 *
4 * Copyright IBM Corp. 2008
5 *
6 * Author(s): Martin Peschke <mp3@de.ibm.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 */
22
23#include <sys/types.h>
24#include <sys/stat.h>
25#include <fcntl.h>
26#include <unistd.h>
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30#include <signal.h>
31#include <getopt.h>
32#include <errno.h>
33#include <locale.h>
34#include <libgen.h>
35#include <sys/msg.h>
36#include <pthread.h>
37#include <time.h>
38
39#include "blktrace.h"
40#include "rbtree.h"
41#include "jhash.h"
42#include "blkiomon.h"
43
44struct trace {
45 struct blk_io_trace bit;
46 struct rb_node node;
47 struct trace *next;
48 long sequence;
49};
50
51struct rb_search {
52 struct rb_node **node_ptr;
53 struct rb_node *parent;
54};
55
56struct dstat_msg {
57 long mtype;
58 struct blkiomon_stat stat;
59};
60
61struct dstat {
62 struct dstat_msg msg;
63 struct rb_node node;
64 struct dstat *next;
65};
66
67struct output {
68 char *fn;
69 FILE *fp;
70 char *buf;
71 int pipe;
72};
73
74static char blkiomon_version[] = "0.2";
75
76static FILE *ifp;
77static int interval = -1;
78
79static struct trace *vacant_traces_list = NULL;
80static int vacant_traces = 0;
cc19ddd6
MP
81
82#define TRACE_HASH_SIZE 128
83struct trace *thash[TRACE_HASH_SIZE] = {};
84
85static struct dstat *vacant_dstats_list = NULL;
86static struct rb_root dstat_tree[2] = { RB_ROOT, RB_ROOT };
87static struct dstat *dstat_list[2] = {};
88static int dstat_curr = 0;
89
3bc3451b 90static struct output drvdata, human, binary, debug;
cc19ddd6
MP
91
92static char *msg_q_name = NULL;
93static int msg_q_id = -1, msg_q = -1;
94static long msg_id = -1;
95
96static pthread_t interval_thread;
97static pthread_mutex_t dstat_mutex = PTHREAD_MUTEX_INITIALIZER;
98
99int data_is_native = -1;
100
101static int up = 1;
102
103/* debugging */
104static long leftover = 0, driverdata = 0, match = 0, mismatch = 0, sequence = 0;
105
106static void dump_bit(struct trace *t, const char *descr)
107{
108 struct blk_io_trace *bit = &t->bit;
109
110 if (!debug.fn)
111 return;
112
113 fprintf(debug.fp, "--- %s ---\n", descr);
114 fprintf(debug.fp, "magic %16d\n", bit->magic);
115 fprintf(debug.fp, "sequence %16d\n", bit->sequence);
116 fprintf(debug.fp, "time %16ld\n", (unsigned long)bit->time);
117 fprintf(debug.fp, "sector %16ld\n", (unsigned long)bit->sector);
118 fprintf(debug.fp, "bytes %16d\n", bit->bytes);
119 fprintf(debug.fp, "action %16x\n", bit->action);
120 fprintf(debug.fp, "pid %16d\n", bit->pid);
121 fprintf(debug.fp, "device %16d\n", bit->device);
122 fprintf(debug.fp, "cpu %16d\n", bit->cpu);
123 fprintf(debug.fp, "error %16d\n", bit->error);
124 fprintf(debug.fp, "pdu_len %16d\n", bit->pdu_len);
125
126 fprintf(debug.fp, "order %16ld\n", t->sequence);
127}
128
129static void dump_bits(struct trace *t1, struct trace *t2, const char *descr)
130{
131 struct blk_io_trace *bit1 = &t1->bit;
132 struct blk_io_trace *bit2 = &t2->bit;
133
134 if (!debug.fn)
135 return;
136
137 fprintf(debug.fp, "--- %s ---\n", descr);
138 fprintf(debug.fp, "magic %16d %16d\n", bit1->magic, bit2->magic);
139 fprintf(debug.fp, "sequence %16d %16d\n",
140 bit1->sequence, bit2->sequence);
141 fprintf(debug.fp, "time %16ld %16ld\n",
142 (unsigned long)bit1->time, (unsigned long)bit2->time);
143 fprintf(debug.fp, "sector %16ld %16ld\n",
144 (unsigned long)bit1->sector, (unsigned long)bit2->sector);
145 fprintf(debug.fp, "bytes %16d %16d\n", bit1->bytes, bit2->bytes);
146 fprintf(debug.fp, "action %16x %16x\n", bit1->action, bit2->action);
147 fprintf(debug.fp, "pid %16d %16d\n", bit1->pid, bit2->pid);
148 fprintf(debug.fp, "device %16d %16d\n", bit1->device, bit2->device);
149 fprintf(debug.fp, "cpu %16d %16d\n", bit1->cpu, bit2->cpu);
150 fprintf(debug.fp, "error %16d %16d\n", bit1->error, bit2->error);
151 fprintf(debug.fp, "pdu_len %16d %16d\n", bit1->pdu_len, bit2->pdu_len);
152
153 fprintf(debug.fp, "order %16ld %16ld\n", t1->sequence, t2->sequence);
154}
155
156static struct dstat *blkiomon_alloc_dstat(void)
157{
158 struct dstat *dstat;
159
160 if (vacant_dstats_list) {
161 dstat = vacant_dstats_list;
162 vacant_dstats_list = dstat->next;
163 } else
164 dstat = malloc(sizeof(*dstat));
165 if (!dstat) {
9b26a3a8
MP
166 fprintf(stderr,
167 "blkiomon: could not allocate device statistic");
cc19ddd6
MP
168 return NULL;
169 }
170
171 memset(dstat, 0, sizeof(*dstat));
172 return dstat;
173}
174
175static struct dstat *blkiomon_find_dstat(struct rb_search *search, __u32 device)
176{
177 struct rb_node **p = &(dstat_tree[dstat_curr].rb_node);
178 struct rb_node *parent = NULL;
179 struct dstat *dstat;
180
181 while (*p) {
182 parent = *p;
183
184 dstat = rb_entry(parent, struct dstat, node);
185
186 if (dstat->msg.stat.device < device)
187 p = &(*p)->rb_left;
188 else if (dstat->msg.stat.device > device)
189 p = &(*p)->rb_right;
190 else
191 return dstat;
192 }
193 search->node_ptr = p;
194 search->parent = parent;
195 return NULL;
196}
197
198static struct dstat *blkiomon_get_dstat(__u32 device)
199{
200 struct dstat *dstat;
201 struct rb_search search;
202
203 pthread_mutex_lock(&dstat_mutex);
204
205 dstat = blkiomon_find_dstat(&search, device);
206 if (dstat)
207 goto out;
208
209 dstat = blkiomon_alloc_dstat();
210 if (!dstat)
211 goto out;
212
213 dstat->msg.stat.device = device;
29ec9a38
MP
214 dstat->msg.stat.size_r.min = -1ULL;
215 dstat->msg.stat.size_w.min = -1ULL;
216 dstat->msg.stat.d2c_r.min = -1ULL;
217 dstat->msg.stat.d2c_w.min = -1ULL;
cc19ddd6
MP
218
219 rb_link_node(&dstat->node, search.parent, search.node_ptr);
220 rb_insert_color(&dstat->node, &dstat_tree[dstat_curr]);
221
222 dstat->next = dstat_list[dstat_curr];
223 dstat_list[dstat_curr] = dstat;
224
225out:
226 pthread_mutex_unlock(&dstat_mutex);
227 return dstat;
228}
229
230static int blkiomon_output_msg_q(struct dstat *dstat)
231{
232 if (!msg_q_name)
233 return 0;
234
235 dstat->msg.mtype = msg_id;
236 return msgsnd(msg_q, &dstat->msg, sizeof(struct blkiomon_stat), 0);
237}
238
239static int blkiomon_output_binary(struct dstat *dstat)
240{
241 struct blkiomon_stat *p = &dstat->msg.stat;
242
243 if (!binary.fn)
244 return 0;
245
246 if (fwrite(p, sizeof(*p), 1, binary.fp) != 1)
247 goto failed;
248 if (binary.pipe && fflush(binary.fp))
249 goto failed;
250 return 0;
251
252failed:
253 fprintf(stderr, "blkiomon: could not write to %s\n", binary.fn);
254 fclose(binary.fp);
255 binary.fn = NULL;
256 return 1;
257}
258
259static struct dstat *blkiomon_output(struct dstat *head, struct timespec *ts)
260{
261 struct dstat *dstat, *tail = NULL;
262
263 for (dstat = head; dstat; dstat = dstat->next) {
264 dstat->msg.stat.time = ts->tv_sec;
265 blkiomon_stat_print(human.fp, &dstat->msg.stat);
266 blkiomon_stat_to_be(&dstat->msg.stat);
267 blkiomon_output_binary(dstat);
268 blkiomon_output_msg_q(dstat);
269 tail = dstat;
270 }
271 return tail;
272}
273
274static void *blkiomon_interval(void *data)
275{
276 struct timespec wake, r;
277 struct dstat *head, *tail;
278 int finished;
279
280 clock_gettime(CLOCK_REALTIME, &wake);
281
282 while (1) {
283 wake.tv_sec += interval;
284 if (clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &wake, &r)) {
9b26a3a8 285 fprintf(stderr, "blkiomon: interrupted sleep");
cc19ddd6
MP
286 continue;
287 }
288
289 /* grab tree and make data gatherer build up another tree */
290 pthread_mutex_lock(&dstat_mutex);
291 finished = dstat_curr;
292 dstat_curr = dstat_curr ? 0 : 1;
293 pthread_mutex_unlock(&dstat_mutex);
294
295 head = dstat_list[finished];
296 if (!head)
297 continue;
298 dstat_list[finished] = NULL;
299 dstat_tree[finished] = RB_ROOT;
300 tail = blkiomon_output(head, &wake);
301
302 pthread_mutex_lock(&dstat_mutex);
303 tail->next = vacant_dstats_list;
304 vacant_dstats_list = head;
305 pthread_mutex_unlock(&dstat_mutex);
306 }
307 return data;
308}
309
310#define BLK_DATADIR(a) (((a) >> BLK_TC_SHIFT) & (BLK_TC_READ | BLK_TC_WRITE))
311
312static int blkiomon_account(struct blk_io_trace *bit_d,
313 struct blk_io_trace *bit_c)
314{
315 struct dstat *dstat;
316 struct blkiomon_stat *p;
317 __u64 d2c = (bit_c->time - bit_d->time) / 1000; /* ns -> us */
318 __u32 size = bit_d->bytes;
53cab5f5 319 __u64 thrput = size * 1000 / d2c;
cc19ddd6
MP
320
321 dstat = blkiomon_get_dstat(bit_d->device);
322 if (!dstat)
323 return 1;
324 p = &dstat->msg.stat;
325
29ec9a38 326 if (BLK_DATADIR(bit_c->action) & BLK_TC_READ) {
53cab5f5 327 minmax_account(&p->thrput_r, thrput);
29ec9a38
MP
328 minmax_account(&p->size_r, size);
329 minmax_account(&p->d2c_r, d2c);
330 } else if (BLK_DATADIR(bit_c->action) & BLK_TC_WRITE) {
53cab5f5 331 minmax_account(&p->thrput_w, thrput);
29ec9a38
MP
332 minmax_account(&p->size_w, size);
333 minmax_account(&p->d2c_w, d2c);
334 } else
cc19ddd6
MP
335 p->bidir++;
336
337 histlog2_account(p->size_hist, size, &size_hist);
338 histlog2_account(p->d2c_hist, d2c, &d2c_hist);
cc19ddd6
MP
339 return 0;
340}
341
342static struct trace *blkiomon_alloc_trace(void)
343{
344 struct trace *t = vacant_traces_list;
345 if (t) {
346 vacant_traces_list = t->next;
347 vacant_traces--;
348 } else
349 t = malloc(sizeof(*t));
350 memset(t, 0, sizeof(*t));
351 return t;
352}
353
354static void blkiomon_free_trace(struct trace *t)
355{
356 if (vacant_traces < 256) {
357 t->next = vacant_traces_list;
358 vacant_traces_list = t;
359 vacant_traces++;
360 } else
361 free(t);
362}
363
364static int action(int a)
365{
366 int bits = BLK_TC_WRITE | BLK_TC_READ | BLK_TC_FS | BLK_TC_PC;
367 return a & (BLK_TC_ACT(bits));
368}
369
370static void blkiomon_store_trace(struct trace *t)
371{
372 int i = t->bit.sector % TRACE_HASH_SIZE;
373
374 t->next = thash[i];
375 thash[i] = t;
376}
377
378static struct trace *blkiomon_fetch_trace(struct blk_io_trace *bit)
379{
380 int i = bit->sector % TRACE_HASH_SIZE;
381 struct trace *t, *prev = NULL;
382
383 for (t = thash[i]; t; t = t->next) {
384 if (t->bit.device == bit->device &&
385 t->bit.sector == bit->sector &&
386 action(t->bit.action) == action(bit->action)) {
387 if (prev)
388 prev->next = t->next;
389 else
390 thash[i] = t->next;
391 return t;
392 }
393 prev = t;
394 }
395 return NULL;
396}
397
398static struct trace *blkiomon_do_trace(struct trace *t)
399{
400 struct trace *t_stored, *t_old, *t_young;
401
402 /* store trace if there is no match yet */
403 t_stored = blkiomon_fetch_trace(&t->bit);
404 if (!t_stored) {
405 blkiomon_store_trace(t);
406 return blkiomon_alloc_trace();
407 }
408
409 /* figure out older trace and younger trace */
410 if (t_stored->bit.time < t->bit.time) {
411 t_old = t_stored;
412 t_young = t;
413 } else {
414 t_old = t;
415 t_young = t_stored;
416 }
417
418 /* we need an older D trace and a younger C trace */
419 if (t_old->bit.action & BLK_TC_ACT(BLK_TC_ISSUE) &&
420 t_young->bit.action & BLK_TC_ACT(BLK_TC_COMPLETE)) {
421 /* matching D and C traces - update statistics */
422 match++;
423 blkiomon_account(&t_old->bit, &t_young->bit);
424 blkiomon_free_trace(t_stored);
425 return t;
426 }
427
428 /* no matching D and C traces - keep more recent trace */
429 dump_bits(t_old, t_young, "mismatch");
430 mismatch++;
431 blkiomon_store_trace(t_young);
432 return t_old;
433}
434
3bc3451b
MP
435static int blkiomon_dump_drvdata(struct blk_io_trace *bit, void *pdu_buf)
436{
437 if (!drvdata.fn)
438 return 0;
439
440 if (fwrite(bit, sizeof(*bit), 1, drvdata.fp) != 1)
441 goto failed;
442 if (fwrite(pdu_buf, bit->pdu_len, 1, drvdata.fp) != 1)
443 goto failed;
444 if (drvdata.pipe && fflush(drvdata.fp))
445 goto failed;
446 return 0;
447
448failed:
449 fprintf(stderr, "blkiomon: could not write to %s\n", drvdata.fn);
450 fclose(drvdata.fp);
451 drvdata.fn = NULL;
452 return 1;
453}
454
cc19ddd6
MP
455static int blkiomon_do_fifo(void)
456{
457 struct trace *t;
458 struct blk_io_trace *bit;
459 void *pdu_buf = NULL;
460
461 t = blkiomon_alloc_trace();
462 if (!t)
463 return 1;
464 bit = &t->bit;
465
466 while (up) {
467 if (fread(bit, sizeof(*bit), 1, ifp) != 1) {
468 if (!feof(ifp))
469 fprintf(stderr,
470 "blkiomon: could not read trace");
471 break;
472 }
473 if (ferror(ifp)) {
474 clearerr(ifp);
9b26a3a8 475 fprintf(stderr, "blkiomon: error while reading trace");
cc19ddd6
MP
476 break;
477 }
478
9b26a3a8
MP
479 if (data_is_native == -1 && check_data_endianness(bit->magic)) {
480 fprintf(stderr, "blkiomon: endianess problem\n");
cc19ddd6 481 break;
9b26a3a8 482 }
cc19ddd6
MP
483
484 /* endianess */
485 trace_to_cpu(bit);
486 if (verify_trace(bit)) {
9b26a3a8 487 fprintf(stderr, "blkiomon: bad trace\n");
cc19ddd6
MP
488 break;
489 }
490
491 /* read additional trace payload */
492 if (bit->pdu_len) {
493 pdu_buf = realloc(pdu_buf, bit->pdu_len);
494 if (fread(pdu_buf, bit->pdu_len, 1, ifp) != 1) {
495 clearerr(ifp);
9b26a3a8 496 fprintf(stderr, "blkiomon: could not read payload\n");
cc19ddd6
MP
497 break;
498 }
499 }
500
501 t->sequence = sequence++;
502
3bc3451b
MP
503 /* forward low-level device driver trace to other tool */
504 if (bit->action & BLK_TC_ACT(BLK_TC_DRV_DATA)) {
505 driverdata++;
9b26a3a8
MP
506 if (blkiomon_dump_drvdata(bit, pdu_buf)) {
507 fprintf(stderr, "blkiomon: could not send trace\n");
3bc3451b 508 break;
9b26a3a8 509 }
3bc3451b
MP
510 continue;
511 }
512
cc19ddd6
MP
513 if (!(bit->action & BLK_TC_ACT(BLK_TC_ISSUE | BLK_TC_COMPLETE)))
514 continue;
515
516 /* try to find matching trace and update statistics */
517 t = blkiomon_do_trace(t);
9b26a3a8
MP
518 if (!t) {
519 fprintf(stderr, "blkiomon: could not alloc trace\n");
cc19ddd6 520 break;
9b26a3a8 521 }
cc19ddd6
MP
522 bit = &t->bit;
523 /* t and bit will be recycled for next incoming trace */
524 }
525 blkiomon_free_trace(t);
526 free(pdu_buf);
527 return 0;
528}
529
530static int blkiomon_open_output(struct output *out)
531{
532 int mode, vbuf_size;
533
534 if (!out->fn)
535 return 0;
536
537 if (!strcmp(out->fn, "-")) {
538 out->fp = fdopen(STDOUT_FILENO, "w");
539 mode = _IOLBF;
540 vbuf_size = 4096;
541 out->pipe = 1;
542 } else {
543 out->fp = fopen(out->fn, "w");
544 mode = _IOFBF;
545 vbuf_size = 128 * 1024;
546 out->pipe = 0;
547 }
548 if (!out->fp)
549 goto failed;
550 out->buf = malloc(128 * 1024);
551 if (setvbuf(out->fp, out->buf, mode, vbuf_size))
552 goto failed;
553 return 0;
554
555failed:
556 fprintf(stderr, "blkiomon: could not write to %s\n", out->fn);
557 out->fn = NULL;
558 free(out->buf);
559 return 1;
560}
561
562static int blkiomon_open_msg_q(void)
563{
564 key_t key;
565
566 if (!msg_q_name)
567 return 0;
568 if (!msg_q_id || msg_id <= 0)
569 return 1;
570 key = ftok(msg_q_name, msg_q_id);
571 if (key == -1)
572 return 1;
573 while (up) {
574 msg_q = msgget(key, S_IRWXU);
575 if (msg_q >= 0)
576 break;
577 }
578 return (msg_q >= 0 ? 0 : -1);
579}
580
581static void blkiomon_debug(void)
582{
4bd8e3ba 583 int i;
cc19ddd6
MP
584 struct trace *t;
585
586 if (!debug.fn)
587 return;
588
4bd8e3ba
MP
589 for (i = 0; i < TRACE_HASH_SIZE; i++)
590 for (t = thash[i]; t; t = t->next) {
591 dump_bit(t, "leftover");
592 leftover++;
593 }
594
cc19ddd6
MP
595 fprintf(debug.fp, "%ld leftover, %ld match, %ld mismatch, "
596 "%ld driverdata, %ld overall\n",
597 leftover, match, mismatch, driverdata, sequence);
598}
599
3bc3451b 600#define S_OPTS "b:d:D:h:I:Q:q:m:V"
cc19ddd6
MP
601
602static char usage_str[] = "\n\nblkiomon " \
603 "-I <interval> | --interval=<interval>\n" \
604 "[ -h <file> | --human-readable=<file> ]\n" \
605 "[ -b <file> | --binary=<file> ]\n" \
606 "[ -D <file> | --debug=<file> ]\n" \
607 "[ -Q <path name> | --msg-queue-name=<path name>]\n" \
608 "[ -q <msg queue id> | --msg-queue-id=<msg queue id>]\n" \
609 "[ -m <msg id> | --msg-id=<msg id>]\n" \
610 "[ -V | --version ]\n\n" \
611 "\t-I Sample interval.\n" \
612 "\t-h Human-readable output file.\n" \
613 "\t-b Binary output file.\n" \
3bc3451b 614 "\t-d Output file for data emitted by low level device driver.\n" \
cc19ddd6
MP
615 "\t-D Output file for debugging data.\n" \
616 "\t-Qqm Output to message queue using given ID for messages.\n" \
617 "\t-V Print program version.\n\n";
618
619static struct option l_opts[] = {
620 {
621 .name = "human-readable",
622 .has_arg = required_argument,
623 .flag = NULL,
624 .val = 'h'
625 },
626 {
627 .name = "binary",
628 .has_arg = required_argument,
629 .flag = NULL,
630 .val = 'b'
631 },
3bc3451b
MP
632 {
633 .name = "dump-lldd",
634 .has_arg = required_argument,
635 .flag = NULL,
636 .val = 'd'
637 },
cc19ddd6
MP
638 {
639 .name = "debug",
640 .has_arg = required_argument,
641 .flag = NULL,
642 .val = 'D'
643 },
644 {
645 .name = "interval",
646 .has_arg = required_argument,
647 .flag = NULL,
648 .val = 'I'
649 },
650 {
651 .name = "msg-queue",
652 .has_arg = required_argument,
653 .flag = NULL,
654 .val = 'Q'
655 },
656 {
657 .name = "msg-queue-id",
658 .has_arg = required_argument,
659 .flag = NULL,
660 .val = 'q'
661 },
662 {
663 .name = "msg-id",
664 .has_arg = required_argument,
665 .flag = NULL,
666 .val = 'm'
667 },
668 {
669 .name = "version",
670 .has_arg = no_argument,
671 .flag = NULL,
672 .val = 'V'
673 },
674 {
675 .name = NULL,
676 }
677};
678
679static void blkiomon_signal(int signal)
680{
681 fprintf(stderr, "blkiomon: terminated by signal\n");
682 up = signal & 0;
683}
684
685int main(int argc, char *argv[])
686{
687 int c;
688
689 signal(SIGALRM, blkiomon_signal);
690 signal(SIGINT, blkiomon_signal);
691 signal(SIGTERM, blkiomon_signal);
692 signal(SIGQUIT, blkiomon_signal);
693
694 while ((c = getopt_long(argc, argv, S_OPTS, l_opts, NULL)) != -1) {
695 switch (c) {
696 case 'h':
697 human.fn = optarg;
698 break;
699 case 'b':
700 binary.fn = optarg;
701 break;
3bc3451b
MP
702 case 'd':
703 drvdata.fn = optarg;
704 break;
cc19ddd6
MP
705 case 'D':
706 debug.fn = optarg;
707 break;
708 case 'I':
709 interval = atoi(optarg);
710 break;
711 case 'Q':
712 msg_q_name = optarg;
713 break;
714 case 'q':
715 msg_q_id = atoi(optarg);
716 break;
717 case 'm':
718 msg_id = atoi(optarg);
719 break;
720 case 'V':
721 printf("%s version %s\n", argv[0], blkiomon_version);
722 return 0;
723 default:
724 fprintf(stderr, "Usage: %s", usage_str);
725 return 1;
726 }
727 }
728
729 if (interval <= 0) {
730 fprintf(stderr, "Usage: %s", usage_str);
731 return 1;
732 }
733
734 ifp = fdopen(STDIN_FILENO, "r");
735 if (!ifp) {
736 perror("blkiomon: could not open stdin for reading");
737 return 1;
738 }
739
740 if (blkiomon_open_output(&human))
741 return 1;
742 if (blkiomon_open_output(&binary))
743 return 1;
3bc3451b
MP
744 if (blkiomon_open_output(&drvdata))
745 return 1;
cc19ddd6
MP
746 if (blkiomon_open_output(&debug))
747 return 1;
748 if (blkiomon_open_msg_q())
749 return 1;
750
751 if (pthread_create(&interval_thread, NULL, blkiomon_interval, NULL)) {
9b26a3a8 752 fprintf(stderr, "blkiomon: could not create thread");
cc19ddd6
MP
753 return 1;
754 }
755
756 blkiomon_do_fifo();
757
758 blkiomon_debug();
759 return 0;
760}