Merge branch 'fix-m' into add-P
[blktrace.git] / blkiomon.c
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
44 struct trace {
45         struct blk_io_trace bit;
46         struct rb_node node;
47         struct trace *next;
48         long sequence;
49 };
50
51 struct rb_search {
52         struct rb_node **node_ptr;
53         struct rb_node *parent;
54 };
55
56 struct dstat_msg {
57         long mtype;
58         struct blkiomon_stat stat;
59 };
60
61 struct dstat {
62         struct dstat_msg msg;
63         struct rb_node node;
64         struct dstat *next;
65 };
66
67 struct output {
68         char *fn;
69         FILE *fp;
70         char *buf;
71         int pipe;
72 };
73
74 static char blkiomon_version[] = "0.2";
75
76 static FILE *ifp;
77 static int interval = -1;
78
79 static struct trace *vacant_traces_list = NULL;
80 static int vacant_traces = 0;
81
82 #define TRACE_HASH_SIZE 128
83 struct trace *thash[TRACE_HASH_SIZE] = {};
84
85 static struct dstat *vacant_dstats_list = NULL;
86 static struct rb_root dstat_tree[2] = { RB_ROOT, RB_ROOT };
87 static struct dstat *dstat_list[2] = {};
88 static int dstat_curr = 0;
89
90 static struct output drvdata, human, binary, debug;
91
92 static char *msg_q_name = NULL;
93 static int msg_q_id = -1, msg_q = -1;
94 static long msg_id = -1;
95
96 static pthread_t interval_thread;
97 static pthread_mutex_t dstat_mutex = PTHREAD_MUTEX_INITIALIZER;
98
99 int data_is_native = -1;
100
101 static int up = 1;
102
103 /* debugging */
104 static long leftover = 0, driverdata = 0, match = 0, mismatch = 0, sequence = 0;
105
106 static 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
129 static 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
156 static 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) {
166                 fprintf(stderr,
167                         "blkiomon: could not allocate device statistic");
168                 return NULL;
169         }
170
171         memset(dstat, 0, sizeof(*dstat));
172         return dstat;
173 }
174
175 static 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
198 static 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;
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;
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
225 out:
226         pthread_mutex_unlock(&dstat_mutex);
227         return dstat;
228 }
229
230 static 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
239 static 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
252 failed:
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
259 static 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
274 static 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)) {
285                         fprintf(stderr, "blkiomon: interrupted sleep");
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
312 static 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;
319         __u64 thrput = size * 1000 / d2c;
320
321         dstat = blkiomon_get_dstat(bit_d->device);
322         if (!dstat)
323                 return 1;
324         p = &dstat->msg.stat;
325
326         if (BLK_DATADIR(bit_c->action) & BLK_TC_READ) {
327                 minmax_account(&p->thrput_r, thrput);
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) {
331                 minmax_account(&p->thrput_w, thrput);
332                 minmax_account(&p->size_w, size);
333                 minmax_account(&p->d2c_w, d2c);
334         } else
335                 p->bidir++;
336
337         histlog2_account(p->size_hist, size, &size_hist);
338         histlog2_account(p->d2c_hist, d2c, &d2c_hist);
339         return 0;
340 }
341
342 static 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
354 static 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
364 static 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
370 static 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
378 static 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
398 static 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
435 static 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
448 failed:
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
455 static 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);
475                         fprintf(stderr, "blkiomon: error while reading trace");
476                         break;
477                 }
478
479                 if (data_is_native == -1 && check_data_endianness(bit->magic)) {
480                         fprintf(stderr, "blkiomon: endianess problem\n");
481                         break;
482                 }
483
484                 /* endianess */
485                 trace_to_cpu(bit);
486                 if (verify_trace(bit)) {
487                         fprintf(stderr, "blkiomon: bad trace\n");
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);
496                                 fprintf(stderr, "blkiomon: could not read payload\n");
497                                 break;
498                         }
499                 }
500
501                 t->sequence = sequence++;
502
503                 /* forward low-level device driver trace to other tool */
504                 if (bit->action & BLK_TC_ACT(BLK_TC_DRV_DATA)) {
505                         driverdata++;
506                         if (blkiomon_dump_drvdata(bit, pdu_buf)) {
507                                 fprintf(stderr, "blkiomon: could not send trace\n");
508                                 break;
509                         }
510                         continue;
511                 }
512
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);
518                 if (!t) {
519                         fprintf(stderr, "blkiomon: could not alloc trace\n");
520                         break;
521                 }
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
530 static 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
555 failed:
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
562 static 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
581 static void blkiomon_debug(void)
582 {
583         int i;
584         struct trace *t;
585
586         if (!debug.fn)
587                 return;
588
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
595         fprintf(debug.fp, "%ld leftover, %ld match, %ld mismatch, "
596                 "%ld driverdata, %ld overall\n",
597                 leftover, match, mismatch, driverdata, sequence);
598 }
599
600 #define S_OPTS "b:d:D:h:I:Q:q:m:V"
601
602 static 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" \
614         "\t-d   Output file for data emitted by low level device driver.\n" \
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
619 static 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         },
632         {
633                 .name = "dump-lldd",
634                 .has_arg = required_argument,
635                 .flag = NULL,
636                 .val = 'd'
637         },
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
679 static void blkiomon_signal(int signal)
680 {
681         fprintf(stderr, "blkiomon: terminated by signal\n");
682         up = signal & 0;
683 }
684
685 int 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;
702                 case 'd':
703                         drvdata.fn = optarg;
704                         break;
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;
744         if (blkiomon_open_output(&drvdata))
745                 return 1;
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)) {
752                 fprintf(stderr, "blkiomon: could not create thread");
753                 return 1;
754         }
755
756         blkiomon_do_fifo();
757
758         blkiomon_debug();
759         return 0;
760 }