|
Packit |
c4abd9 |
/*
|
|
Packit |
c4abd9 |
* I/O monitor based on block queue trace data
|
|
Packit |
c4abd9 |
*
|
|
Packit |
c4abd9 |
* Copyright IBM Corp. 2008
|
|
Packit |
c4abd9 |
*
|
|
Packit |
c4abd9 |
* Author(s): Martin Peschke <mp3@de.ibm.com>
|
|
Packit |
c4abd9 |
*
|
|
Packit |
c4abd9 |
* This program is free software; you can redistribute it and/or modify
|
|
Packit |
c4abd9 |
* it under the terms of the GNU General Public License as published by
|
|
Packit |
c4abd9 |
* the Free Software Foundation; either version 2 of the License, or
|
|
Packit |
c4abd9 |
* (at your option) any later version.
|
|
Packit |
c4abd9 |
*
|
|
Packit |
c4abd9 |
* This program is distributed in the hope that it will be useful,
|
|
Packit |
c4abd9 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
c4abd9 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
Packit |
c4abd9 |
* GNU General Public License for more details.
|
|
Packit |
c4abd9 |
*
|
|
Packit |
c4abd9 |
* You should have received a copy of the GNU General Public License
|
|
Packit |
c4abd9 |
* along with this program; if not, write to the Free Software
|
|
Packit |
c4abd9 |
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
Packit |
c4abd9 |
*/
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
#include <sys/types.h>
|
|
Packit |
c4abd9 |
#include <sys/stat.h>
|
|
Packit |
c4abd9 |
#include <fcntl.h>
|
|
Packit |
c4abd9 |
#include <unistd.h>
|
|
Packit |
c4abd9 |
#include <stdio.h>
|
|
Packit |
c4abd9 |
#include <stdlib.h>
|
|
Packit |
c4abd9 |
#include <string.h>
|
|
Packit |
c4abd9 |
#include <signal.h>
|
|
Packit |
c4abd9 |
#include <getopt.h>
|
|
Packit |
c4abd9 |
#include <errno.h>
|
|
Packit |
c4abd9 |
#include <locale.h>
|
|
Packit |
c4abd9 |
#include <libgen.h>
|
|
Packit |
c4abd9 |
#include <sys/msg.h>
|
|
Packit |
c4abd9 |
#include <pthread.h>
|
|
Packit |
c4abd9 |
#include <time.h>
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
#include "blktrace.h"
|
|
Packit |
c4abd9 |
#include "rbtree.h"
|
|
Packit |
c4abd9 |
#include "jhash.h"
|
|
Packit |
c4abd9 |
#include "blkiomon.h"
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
struct trace {
|
|
Packit |
c4abd9 |
struct blk_io_trace bit;
|
|
Packit |
c4abd9 |
struct rb_node node;
|
|
Packit |
c4abd9 |
struct trace *next;
|
|
Packit |
c4abd9 |
long sequence;
|
|
Packit |
c4abd9 |
};
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
struct rb_search {
|
|
Packit |
c4abd9 |
struct rb_node **node_ptr;
|
|
Packit |
c4abd9 |
struct rb_node *parent;
|
|
Packit |
c4abd9 |
};
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
struct dstat_msg {
|
|
Packit |
c4abd9 |
long mtype;
|
|
Packit |
c4abd9 |
struct blkiomon_stat stat;
|
|
Packit |
c4abd9 |
};
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
struct dstat {
|
|
Packit |
c4abd9 |
struct dstat_msg msg;
|
|
Packit |
c4abd9 |
struct rb_node node;
|
|
Packit |
c4abd9 |
struct dstat *next;
|
|
Packit |
c4abd9 |
};
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
struct output {
|
|
Packit |
c4abd9 |
char *fn;
|
|
Packit |
c4abd9 |
FILE *fp;
|
|
Packit |
c4abd9 |
char *buf;
|
|
Packit |
c4abd9 |
int pipe;
|
|
Packit |
c4abd9 |
};
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
static char blkiomon_version[] = "0.3";
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
static FILE *ifp;
|
|
Packit |
c4abd9 |
static int interval = -1;
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
static struct trace *vacant_traces_list = NULL;
|
|
Packit |
c4abd9 |
static int vacant_traces = 0;
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
#define TRACE_HASH_SIZE 128
|
|
Packit |
c4abd9 |
struct trace *thash[TRACE_HASH_SIZE] = {};
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
static struct dstat *vacant_dstats_list = NULL;
|
|
Packit |
c4abd9 |
static struct rb_root dstat_tree[2] = { RB_ROOT, RB_ROOT };
|
|
Packit |
c4abd9 |
static struct dstat *dstat_list[2] = {};
|
|
Packit |
c4abd9 |
static int dstat_curr = 0;
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
static struct output drvdata, human, binary, debug;
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
static char *msg_q_name = NULL;
|
|
Packit |
c4abd9 |
static int msg_q_id = -1, msg_q = -1;
|
|
Packit |
c4abd9 |
static long msg_id = -1;
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
static pthread_t interval_thread;
|
|
Packit |
c4abd9 |
static pthread_mutex_t dstat_mutex = PTHREAD_MUTEX_INITIALIZER;
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
int data_is_native = -1;
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
static int up = 1;
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
/* debugging */
|
|
Packit |
c4abd9 |
static long leftover = 0, driverdata = 0, match = 0, mismatch = 0, sequence = 0;
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
static void dump_bit(struct trace *t, const char *descr)
|
|
Packit |
c4abd9 |
{
|
|
Packit |
c4abd9 |
struct blk_io_trace *bit = &t->bit;
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
if (!debug.fn)
|
|
Packit |
c4abd9 |
return;
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
fprintf(debug.fp, "--- %s ---\n", descr);
|
|
Packit |
c4abd9 |
fprintf(debug.fp, "magic %16d\n", bit->magic);
|
|
Packit |
c4abd9 |
fprintf(debug.fp, "sequence %16d\n", bit->sequence);
|
|
Packit |
c4abd9 |
fprintf(debug.fp, "time %16ld\n", (unsigned long)bit->time);
|
|
Packit |
c4abd9 |
fprintf(debug.fp, "sector %16ld\n", (unsigned long)bit->sector);
|
|
Packit |
c4abd9 |
fprintf(debug.fp, "bytes %16d\n", bit->bytes);
|
|
Packit |
c4abd9 |
fprintf(debug.fp, "action %16x\n", bit->action);
|
|
Packit |
c4abd9 |
fprintf(debug.fp, "pid %16d\n", bit->pid);
|
|
Packit |
c4abd9 |
fprintf(debug.fp, "device %16d\n", bit->device);
|
|
Packit |
c4abd9 |
fprintf(debug.fp, "cpu %16d\n", bit->cpu);
|
|
Packit |
c4abd9 |
fprintf(debug.fp, "error %16d\n", bit->error);
|
|
Packit |
c4abd9 |
fprintf(debug.fp, "pdu_len %16d\n", bit->pdu_len);
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
fprintf(debug.fp, "order %16ld\n", t->sequence);
|
|
Packit |
c4abd9 |
}
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
static void dump_bits(struct trace *t1, struct trace *t2, const char *descr)
|
|
Packit |
c4abd9 |
{
|
|
Packit |
c4abd9 |
struct blk_io_trace *bit1 = &t1->bit;
|
|
Packit |
c4abd9 |
struct blk_io_trace *bit2 = &t2->bit;
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
if (!debug.fn)
|
|
Packit |
c4abd9 |
return;
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
fprintf(debug.fp, "--- %s ---\n", descr);
|
|
Packit |
c4abd9 |
fprintf(debug.fp, "magic %16d %16d\n", bit1->magic, bit2->magic);
|
|
Packit |
c4abd9 |
fprintf(debug.fp, "sequence %16d %16d\n",
|
|
Packit |
c4abd9 |
bit1->sequence, bit2->sequence);
|
|
Packit |
c4abd9 |
fprintf(debug.fp, "time %16ld %16ld\n",
|
|
Packit |
c4abd9 |
(unsigned long)bit1->time, (unsigned long)bit2->time);
|
|
Packit |
c4abd9 |
fprintf(debug.fp, "sector %16ld %16ld\n",
|
|
Packit |
c4abd9 |
(unsigned long)bit1->sector, (unsigned long)bit2->sector);
|
|
Packit |
c4abd9 |
fprintf(debug.fp, "bytes %16d %16d\n", bit1->bytes, bit2->bytes);
|
|
Packit |
c4abd9 |
fprintf(debug.fp, "action %16x %16x\n", bit1->action, bit2->action);
|
|
Packit |
c4abd9 |
fprintf(debug.fp, "pid %16d %16d\n", bit1->pid, bit2->pid);
|
|
Packit |
c4abd9 |
fprintf(debug.fp, "device %16d %16d\n", bit1->device, bit2->device);
|
|
Packit |
c4abd9 |
fprintf(debug.fp, "cpu %16d %16d\n", bit1->cpu, bit2->cpu);
|
|
Packit |
c4abd9 |
fprintf(debug.fp, "error %16d %16d\n", bit1->error, bit2->error);
|
|
Packit |
c4abd9 |
fprintf(debug.fp, "pdu_len %16d %16d\n", bit1->pdu_len, bit2->pdu_len);
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
fprintf(debug.fp, "order %16ld %16ld\n", t1->sequence, t2->sequence);
|
|
Packit |
c4abd9 |
}
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
static struct dstat *blkiomon_alloc_dstat(void)
|
|
Packit |
c4abd9 |
{
|
|
Packit |
c4abd9 |
struct dstat *dstat;
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
if (vacant_dstats_list) {
|
|
Packit |
c4abd9 |
dstat = vacant_dstats_list;
|
|
Packit |
c4abd9 |
vacant_dstats_list = dstat->next;
|
|
Packit |
c4abd9 |
} else
|
|
Packit |
c4abd9 |
dstat = malloc(sizeof(*dstat));
|
|
Packit |
c4abd9 |
if (!dstat) {
|
|
Packit |
c4abd9 |
fprintf(stderr,
|
|
Packit |
c4abd9 |
"blkiomon: could not allocate device statistic");
|
|
Packit |
c4abd9 |
return NULL;
|
|
Packit |
c4abd9 |
}
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
blkiomon_stat_init(&dstat->msg.stat);
|
|
Packit |
c4abd9 |
return dstat;
|
|
Packit |
c4abd9 |
}
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
static struct dstat *blkiomon_find_dstat(struct rb_search *search, __u32 device)
|
|
Packit |
c4abd9 |
{
|
|
Packit |
c4abd9 |
struct rb_node **p = &(dstat_tree[dstat_curr].rb_node);
|
|
Packit |
c4abd9 |
struct rb_node *parent = NULL;
|
|
Packit |
c4abd9 |
struct dstat *dstat;
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
while (*p) {
|
|
Packit |
c4abd9 |
parent = *p;
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
dstat = rb_entry(parent, struct dstat, node);
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
if (dstat->msg.stat.device < device)
|
|
Packit |
c4abd9 |
p = &(*p)->rb_left;
|
|
Packit |
c4abd9 |
else if (dstat->msg.stat.device > device)
|
|
Packit |
c4abd9 |
p = &(*p)->rb_right;
|
|
Packit |
c4abd9 |
else
|
|
Packit |
c4abd9 |
return dstat;
|
|
Packit |
c4abd9 |
}
|
|
Packit |
c4abd9 |
search->node_ptr = p;
|
|
Packit |
c4abd9 |
search->parent = parent;
|
|
Packit |
c4abd9 |
return NULL;
|
|
Packit |
c4abd9 |
}
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
static struct dstat *blkiomon_get_dstat(__u32 device)
|
|
Packit |
c4abd9 |
{
|
|
Packit |
c4abd9 |
struct dstat *dstat;
|
|
Packit |
c4abd9 |
struct rb_search search = { 0, };
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
pthread_mutex_lock(&dstat_mutex);
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
dstat = blkiomon_find_dstat(&search, device);
|
|
Packit |
c4abd9 |
if (dstat)
|
|
Packit |
c4abd9 |
goto out;
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
dstat = blkiomon_alloc_dstat();
|
|
Packit |
c4abd9 |
if (!dstat)
|
|
Packit |
c4abd9 |
goto out;
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
dstat->msg.stat.device = device;
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
rb_link_node(&dstat->node, search.parent, search.node_ptr);
|
|
Packit |
c4abd9 |
rb_insert_color(&dstat->node, &dstat_tree[dstat_curr]);
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
dstat->next = dstat_list[dstat_curr];
|
|
Packit |
c4abd9 |
dstat_list[dstat_curr] = dstat;
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
out:
|
|
Packit |
c4abd9 |
pthread_mutex_unlock(&dstat_mutex);
|
|
Packit |
c4abd9 |
return dstat;
|
|
Packit |
c4abd9 |
}
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
static int blkiomon_output_msg_q(struct dstat *dstat)
|
|
Packit |
c4abd9 |
{
|
|
Packit |
c4abd9 |
if (!msg_q_name)
|
|
Packit |
c4abd9 |
return 0;
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
dstat->msg.mtype = msg_id;
|
|
Packit |
c4abd9 |
return msgsnd(msg_q, &dstat->msg, sizeof(struct blkiomon_stat), 0);
|
|
Packit |
c4abd9 |
}
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
static int blkiomon_output_binary(struct dstat *dstat)
|
|
Packit |
c4abd9 |
{
|
|
Packit |
c4abd9 |
struct blkiomon_stat *p = &dstat->msg.stat;
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
if (!binary.fn)
|
|
Packit |
c4abd9 |
return 0;
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
if (fwrite(p, sizeof(*p), 1, binary.fp) != 1)
|
|
Packit |
c4abd9 |
goto failed;
|
|
Packit |
c4abd9 |
if (binary.pipe && fflush(binary.fp))
|
|
Packit |
c4abd9 |
goto failed;
|
|
Packit |
c4abd9 |
return 0;
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
failed:
|
|
Packit |
c4abd9 |
fprintf(stderr, "blkiomon: could not write to %s\n", binary.fn);
|
|
Packit |
c4abd9 |
fclose(binary.fp);
|
|
Packit |
c4abd9 |
binary.fn = NULL;
|
|
Packit |
c4abd9 |
return 1;
|
|
Packit |
c4abd9 |
}
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
static struct dstat *blkiomon_output(struct dstat *head, struct timespec *ts)
|
|
Packit |
c4abd9 |
{
|
|
Packit |
c4abd9 |
struct dstat *dstat, *tail = NULL;
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
for (dstat = head; dstat; dstat = dstat->next) {
|
|
Packit |
c4abd9 |
dstat->msg.stat.time = ts->tv_sec;
|
|
Packit |
c4abd9 |
blkiomon_stat_print(human.fp, &dstat->msg.stat);
|
|
Packit |
c4abd9 |
blkiomon_stat_to_be(&dstat->msg.stat);
|
|
Packit |
c4abd9 |
blkiomon_output_binary(dstat);
|
|
Packit |
c4abd9 |
blkiomon_output_msg_q(dstat);
|
|
Packit |
c4abd9 |
tail = dstat;
|
|
Packit |
c4abd9 |
}
|
|
Packit |
c4abd9 |
return tail;
|
|
Packit |
c4abd9 |
}
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
static void *blkiomon_interval(void *data)
|
|
Packit |
c4abd9 |
{
|
|
Packit |
c4abd9 |
struct timespec wake, r;
|
|
Packit |
c4abd9 |
struct dstat *head, *tail;
|
|
Packit |
c4abd9 |
int finished;
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
clock_gettime(CLOCK_REALTIME, &wake);
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
while (1) {
|
|
Packit |
c4abd9 |
wake.tv_sec += interval;
|
|
Packit |
c4abd9 |
if (clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &wake, &r)) {
|
|
Packit |
c4abd9 |
fprintf(stderr, "blkiomon: interrupted sleep");
|
|
Packit |
c4abd9 |
continue;
|
|
Packit |
c4abd9 |
}
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
/* grab tree and make data gatherer build up another tree */
|
|
Packit |
c4abd9 |
pthread_mutex_lock(&dstat_mutex);
|
|
Packit |
c4abd9 |
finished = dstat_curr;
|
|
Packit |
c4abd9 |
dstat_curr = dstat_curr ? 0 : 1;
|
|
Packit |
c4abd9 |
pthread_mutex_unlock(&dstat_mutex);
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
head = dstat_list[finished];
|
|
Packit |
c4abd9 |
if (!head)
|
|
Packit |
c4abd9 |
continue;
|
|
Packit |
c4abd9 |
dstat_list[finished] = NULL;
|
|
Packit |
c4abd9 |
dstat_tree[finished] = RB_ROOT;
|
|
Packit |
c4abd9 |
tail = blkiomon_output(head, &wake);
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
pthread_mutex_lock(&dstat_mutex);
|
|
Packit |
c4abd9 |
tail->next = vacant_dstats_list;
|
|
Packit |
c4abd9 |
vacant_dstats_list = head;
|
|
Packit |
c4abd9 |
pthread_mutex_unlock(&dstat_mutex);
|
|
Packit |
c4abd9 |
}
|
|
Packit |
c4abd9 |
return data;
|
|
Packit |
c4abd9 |
}
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
#define BLK_DATADIR(a) (((a) >> BLK_TC_SHIFT) & (BLK_TC_READ | BLK_TC_WRITE))
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
static int blkiomon_account(struct blk_io_trace *bit_d,
|
|
Packit |
c4abd9 |
struct blk_io_trace *bit_c)
|
|
Packit |
c4abd9 |
{
|
|
Packit |
c4abd9 |
struct dstat *dstat;
|
|
Packit |
c4abd9 |
struct blkiomon_stat *p;
|
|
Packit |
c4abd9 |
__u64 d2c = (bit_c->time - bit_d->time) / 1000; /* ns -> us */
|
|
Packit |
c4abd9 |
__u32 size = bit_d->bytes;
|
|
Packit |
c4abd9 |
__u64 thrput = size * 1000 / d2c;
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
dstat = blkiomon_get_dstat(bit_d->device);
|
|
Packit |
c4abd9 |
if (!dstat)
|
|
Packit |
c4abd9 |
return 1;
|
|
Packit |
c4abd9 |
p = &dstat->msg.stat;
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
if (BLK_DATADIR(bit_c->action) & BLK_TC_READ) {
|
|
Packit |
c4abd9 |
minmax_account(&p->thrput_r, thrput);
|
|
Packit |
c4abd9 |
minmax_account(&p->size_r, size);
|
|
Packit |
c4abd9 |
minmax_account(&p->d2c_r, d2c);
|
|
Packit |
c4abd9 |
} else if (BLK_DATADIR(bit_c->action) & BLK_TC_WRITE) {
|
|
Packit |
c4abd9 |
minmax_account(&p->thrput_w, thrput);
|
|
Packit |
c4abd9 |
minmax_account(&p->size_w, size);
|
|
Packit |
c4abd9 |
minmax_account(&p->d2c_w, d2c);
|
|
Packit |
c4abd9 |
} else
|
|
Packit |
c4abd9 |
p->bidir++;
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
histlog2_account(p->size_hist, size, &size_hist);
|
|
Packit |
c4abd9 |
histlog2_account(p->d2c_hist, d2c, &d2c_hist);
|
|
Packit |
c4abd9 |
return 0;
|
|
Packit |
c4abd9 |
}
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
static struct trace *blkiomon_alloc_trace(void)
|
|
Packit |
c4abd9 |
{
|
|
Packit |
c4abd9 |
struct trace *t = vacant_traces_list;
|
|
Packit |
c4abd9 |
if (t) {
|
|
Packit |
c4abd9 |
vacant_traces_list = t->next;
|
|
Packit |
c4abd9 |
vacant_traces--;
|
|
Packit |
c4abd9 |
} else
|
|
Packit |
c4abd9 |
t = malloc(sizeof(*t));
|
|
Packit |
c4abd9 |
memset(t, 0, sizeof(*t));
|
|
Packit |
c4abd9 |
return t;
|
|
Packit |
c4abd9 |
}
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
static void blkiomon_free_trace(struct trace *t)
|
|
Packit |
c4abd9 |
{
|
|
Packit |
c4abd9 |
if (vacant_traces < 256) {
|
|
Packit |
c4abd9 |
t->next = vacant_traces_list;
|
|
Packit |
c4abd9 |
vacant_traces_list = t;
|
|
Packit |
c4abd9 |
vacant_traces++;
|
|
Packit |
c4abd9 |
} else
|
|
Packit |
c4abd9 |
free(t);
|
|
Packit |
c4abd9 |
}
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
static int action(int a)
|
|
Packit |
c4abd9 |
{
|
|
Packit |
c4abd9 |
int bits = BLK_TC_WRITE | BLK_TC_READ | BLK_TC_FS | BLK_TC_PC;
|
|
Packit |
c4abd9 |
return a & (BLK_TC_ACT(bits));
|
|
Packit |
c4abd9 |
}
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
static void blkiomon_store_trace(struct trace *t)
|
|
Packit |
c4abd9 |
{
|
|
Packit |
c4abd9 |
int i = t->bit.sector % TRACE_HASH_SIZE;
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
t->next = thash[i];
|
|
Packit |
c4abd9 |
thash[i] = t;
|
|
Packit |
c4abd9 |
}
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
static struct trace *blkiomon_fetch_trace(struct blk_io_trace *bit)
|
|
Packit |
c4abd9 |
{
|
|
Packit |
c4abd9 |
int i = bit->sector % TRACE_HASH_SIZE;
|
|
Packit |
c4abd9 |
struct trace *t, *prev = NULL;
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
for (t = thash[i]; t; t = t->next) {
|
|
Packit |
c4abd9 |
if (t->bit.device == bit->device &&
|
|
Packit |
c4abd9 |
t->bit.sector == bit->sector &&
|
|
Packit |
c4abd9 |
action(t->bit.action) == action(bit->action)) {
|
|
Packit |
c4abd9 |
if (prev)
|
|
Packit |
c4abd9 |
prev->next = t->next;
|
|
Packit |
c4abd9 |
else
|
|
Packit |
c4abd9 |
thash[i] = t->next;
|
|
Packit |
c4abd9 |
return t;
|
|
Packit |
c4abd9 |
}
|
|
Packit |
c4abd9 |
prev = t;
|
|
Packit |
c4abd9 |
}
|
|
Packit |
c4abd9 |
return NULL;
|
|
Packit |
c4abd9 |
}
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
static struct trace *blkiomon_do_trace(struct trace *t)
|
|
Packit |
c4abd9 |
{
|
|
Packit |
c4abd9 |
struct trace *t_stored, *t_old, *t_young;
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
/* store trace if there is no match yet */
|
|
Packit |
c4abd9 |
t_stored = blkiomon_fetch_trace(&t->bit);
|
|
Packit |
c4abd9 |
if (!t_stored) {
|
|
Packit |
c4abd9 |
blkiomon_store_trace(t);
|
|
Packit |
c4abd9 |
return blkiomon_alloc_trace();
|
|
Packit |
c4abd9 |
}
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
/* figure out older trace and younger trace */
|
|
Packit |
c4abd9 |
if (t_stored->bit.time < t->bit.time) {
|
|
Packit |
c4abd9 |
t_old = t_stored;
|
|
Packit |
c4abd9 |
t_young = t;
|
|
Packit |
c4abd9 |
} else {
|
|
Packit |
c4abd9 |
t_old = t;
|
|
Packit |
c4abd9 |
t_young = t_stored;
|
|
Packit |
c4abd9 |
}
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
/* we need an older D trace and a younger C trace */
|
|
Packit |
c4abd9 |
if (t_old->bit.action & BLK_TC_ACT(BLK_TC_ISSUE) &&
|
|
Packit |
c4abd9 |
t_young->bit.action & BLK_TC_ACT(BLK_TC_COMPLETE)) {
|
|
Packit |
c4abd9 |
/* matching D and C traces - update statistics */
|
|
Packit |
c4abd9 |
match++;
|
|
Packit |
c4abd9 |
blkiomon_account(&t_old->bit, &t_young->bit);
|
|
Packit |
c4abd9 |
blkiomon_free_trace(t_stored);
|
|
Packit |
c4abd9 |
return t;
|
|
Packit |
c4abd9 |
}
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
/* no matching D and C traces - keep more recent trace */
|
|
Packit |
c4abd9 |
dump_bits(t_old, t_young, "mismatch");
|
|
Packit |
c4abd9 |
mismatch++;
|
|
Packit |
c4abd9 |
blkiomon_store_trace(t_young);
|
|
Packit |
c4abd9 |
return t_old;
|
|
Packit |
c4abd9 |
}
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
static int blkiomon_dump_drvdata(struct blk_io_trace *bit, void *pdu_buf)
|
|
Packit |
c4abd9 |
{
|
|
Packit |
c4abd9 |
if (!drvdata.fn)
|
|
Packit |
c4abd9 |
return 0;
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
if (fwrite(bit, sizeof(*bit), 1, drvdata.fp) != 1)
|
|
Packit |
c4abd9 |
goto failed;
|
|
Packit |
c4abd9 |
if (fwrite(pdu_buf, bit->pdu_len, 1, drvdata.fp) != 1)
|
|
Packit |
c4abd9 |
goto failed;
|
|
Packit |
c4abd9 |
if (drvdata.pipe && fflush(drvdata.fp))
|
|
Packit |
c4abd9 |
goto failed;
|
|
Packit |
c4abd9 |
return 0;
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
failed:
|
|
Packit |
c4abd9 |
fprintf(stderr, "blkiomon: could not write to %s\n", drvdata.fn);
|
|
Packit |
c4abd9 |
fclose(drvdata.fp);
|
|
Packit |
c4abd9 |
drvdata.fn = NULL;
|
|
Packit |
c4abd9 |
return 1;
|
|
Packit |
c4abd9 |
}
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
static int blkiomon_do_fifo(void)
|
|
Packit |
c4abd9 |
{
|
|
Packit |
c4abd9 |
struct trace *t;
|
|
Packit |
c4abd9 |
struct blk_io_trace *bit;
|
|
Packit |
c4abd9 |
void *pdu_buf = NULL;
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
t = blkiomon_alloc_trace();
|
|
Packit |
c4abd9 |
if (!t)
|
|
Packit |
c4abd9 |
return 1;
|
|
Packit |
c4abd9 |
bit = &t->bit;
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
while (up) {
|
|
Packit |
c4abd9 |
if (fread(bit, sizeof(*bit), 1, ifp) != 1) {
|
|
Packit |
c4abd9 |
if (!feof(ifp))
|
|
Packit |
c4abd9 |
fprintf(stderr,
|
|
Packit |
c4abd9 |
"blkiomon: could not read trace");
|
|
Packit |
c4abd9 |
break;
|
|
Packit |
c4abd9 |
}
|
|
Packit |
c4abd9 |
if (ferror(ifp)) {
|
|
Packit |
c4abd9 |
clearerr(ifp);
|
|
Packit |
c4abd9 |
fprintf(stderr, "blkiomon: error while reading trace");
|
|
Packit |
c4abd9 |
break;
|
|
Packit |
c4abd9 |
}
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
if (data_is_native == -1 && check_data_endianness(bit->magic)) {
|
|
Packit |
c4abd9 |
fprintf(stderr, "blkiomon: endianess problem\n");
|
|
Packit |
c4abd9 |
break;
|
|
Packit |
c4abd9 |
}
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
/* endianess */
|
|
Packit |
c4abd9 |
trace_to_cpu(bit);
|
|
Packit |
c4abd9 |
if (verify_trace(bit)) {
|
|
Packit |
c4abd9 |
fprintf(stderr, "blkiomon: bad trace\n");
|
|
Packit |
c4abd9 |
break;
|
|
Packit |
c4abd9 |
}
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
/* read additional trace payload */
|
|
Packit |
c4abd9 |
if (bit->pdu_len) {
|
|
Packit |
c4abd9 |
pdu_buf = realloc(pdu_buf, bit->pdu_len);
|
|
Packit |
c4abd9 |
if (fread(pdu_buf, bit->pdu_len, 1, ifp) != 1) {
|
|
Packit |
c4abd9 |
clearerr(ifp);
|
|
Packit |
c4abd9 |
fprintf(stderr, "blkiomon: could not read payload\n");
|
|
Packit |
c4abd9 |
break;
|
|
Packit |
c4abd9 |
}
|
|
Packit |
c4abd9 |
}
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
t->sequence = sequence++;
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
/* forward low-level device driver trace to other tool */
|
|
Packit |
c4abd9 |
if (bit->action & BLK_TC_ACT(BLK_TC_DRV_DATA)) {
|
|
Packit |
c4abd9 |
driverdata++;
|
|
Packit |
c4abd9 |
if (blkiomon_dump_drvdata(bit, pdu_buf)) {
|
|
Packit |
c4abd9 |
fprintf(stderr, "blkiomon: could not send trace\n");
|
|
Packit |
c4abd9 |
break;
|
|
Packit |
c4abd9 |
}
|
|
Packit |
c4abd9 |
continue;
|
|
Packit |
c4abd9 |
}
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
if (!(bit->action & BLK_TC_ACT(BLK_TC_ISSUE | BLK_TC_COMPLETE)))
|
|
Packit |
c4abd9 |
continue;
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
/* try to find matching trace and update statistics */
|
|
Packit |
c4abd9 |
t = blkiomon_do_trace(t);
|
|
Packit |
c4abd9 |
if (!t) {
|
|
Packit |
c4abd9 |
fprintf(stderr, "blkiomon: could not alloc trace\n");
|
|
Packit |
c4abd9 |
break;
|
|
Packit |
c4abd9 |
}
|
|
Packit |
c4abd9 |
bit = &t->bit;
|
|
Packit |
c4abd9 |
/* t and bit will be recycled for next incoming trace */
|
|
Packit |
c4abd9 |
}
|
|
Packit |
c4abd9 |
blkiomon_free_trace(t);
|
|
Packit |
c4abd9 |
free(pdu_buf);
|
|
Packit |
c4abd9 |
return 0;
|
|
Packit |
c4abd9 |
}
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
static int blkiomon_open_output(struct output *out)
|
|
Packit |
c4abd9 |
{
|
|
Packit |
c4abd9 |
int mode, vbuf_size;
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
if (!out->fn)
|
|
Packit |
c4abd9 |
return 0;
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
if (!strcmp(out->fn, "-")) {
|
|
Packit |
c4abd9 |
out->fp = fdopen(STDOUT_FILENO, "w");
|
|
Packit |
c4abd9 |
mode = _IOLBF;
|
|
Packit |
c4abd9 |
vbuf_size = 4096;
|
|
Packit |
c4abd9 |
out->pipe = 1;
|
|
Packit |
c4abd9 |
} else {
|
|
Packit |
c4abd9 |
out->fp = fopen(out->fn, "w");
|
|
Packit |
c4abd9 |
mode = _IOFBF;
|
|
Packit |
c4abd9 |
vbuf_size = 128 * 1024;
|
|
Packit |
c4abd9 |
out->pipe = 0;
|
|
Packit |
c4abd9 |
}
|
|
Packit |
c4abd9 |
if (!out->fp)
|
|
Packit |
c4abd9 |
goto failed;
|
|
Packit |
c4abd9 |
out->buf = malloc(128 * 1024);
|
|
Packit |
c4abd9 |
if (setvbuf(out->fp, out->buf, mode, vbuf_size))
|
|
Packit |
c4abd9 |
goto failed;
|
|
Packit |
c4abd9 |
return 0;
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
failed:
|
|
Packit |
c4abd9 |
fprintf(stderr, "blkiomon: could not write to %s\n", out->fn);
|
|
Packit |
c4abd9 |
out->fn = NULL;
|
|
Packit |
c4abd9 |
free(out->buf);
|
|
Packit |
c4abd9 |
return 1;
|
|
Packit |
c4abd9 |
}
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
static int blkiomon_open_msg_q(void)
|
|
Packit |
c4abd9 |
{
|
|
Packit |
c4abd9 |
key_t key;
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
if (!msg_q_name)
|
|
Packit |
c4abd9 |
return 0;
|
|
Packit |
c4abd9 |
if (!msg_q_id || msg_id <= 0)
|
|
Packit |
c4abd9 |
return 1;
|
|
Packit |
c4abd9 |
key = ftok(msg_q_name, msg_q_id);
|
|
Packit |
c4abd9 |
if (key == -1)
|
|
Packit |
c4abd9 |
return 1;
|
|
Packit |
c4abd9 |
while (up) {
|
|
Packit |
c4abd9 |
msg_q = msgget(key, S_IRWXU);
|
|
Packit |
c4abd9 |
if (msg_q >= 0)
|
|
Packit |
c4abd9 |
break;
|
|
Packit |
c4abd9 |
}
|
|
Packit |
c4abd9 |
return (msg_q >= 0 ? 0 : -1);
|
|
Packit |
c4abd9 |
}
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
static void blkiomon_debug(void)
|
|
Packit |
c4abd9 |
{
|
|
Packit |
c4abd9 |
int i;
|
|
Packit |
c4abd9 |
struct trace *t;
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
if (!debug.fn)
|
|
Packit |
c4abd9 |
return;
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
for (i = 0; i < TRACE_HASH_SIZE; i++)
|
|
Packit |
c4abd9 |
for (t = thash[i]; t; t = t->next) {
|
|
Packit |
c4abd9 |
dump_bit(t, "leftover");
|
|
Packit |
c4abd9 |
leftover++;
|
|
Packit |
c4abd9 |
}
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
fprintf(debug.fp, "%ld leftover, %ld match, %ld mismatch, "
|
|
Packit |
c4abd9 |
"%ld driverdata, %ld overall\n",
|
|
Packit |
c4abd9 |
leftover, match, mismatch, driverdata, sequence);
|
|
Packit |
c4abd9 |
}
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
#define S_OPTS "b:d:D:h:I:Q:q:m:V"
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
static char usage_str[] = "\n\nblkiomon " \
|
|
Packit |
c4abd9 |
"-I <interval> | --interval=<interval>\n" \
|
|
Packit |
c4abd9 |
"[ -h <file> | --human-readable=<file> ]\n" \
|
|
Packit |
c4abd9 |
"[ -b <file> | --binary=<file> ]\n" \
|
|
Packit |
c4abd9 |
"[ -d <file> | --dump-lldd=<file> ]\n" \
|
|
Packit |
c4abd9 |
"[ -D <file> | --debug=<file> ]\n" \
|
|
Packit |
c4abd9 |
"[ -Q <path name> | --msg-queue=<path name>]\n" \
|
|
Packit |
c4abd9 |
"[ -q <msg queue id> | --msg-queue-id=<msg queue id>]\n" \
|
|
Packit |
c4abd9 |
"[ -m <msg id> | --msg-id=<msg id>]\n" \
|
|
Packit |
c4abd9 |
"[ -V | --version ]\n\n" \
|
|
Packit |
c4abd9 |
"\t-I Sample interval.\n" \
|
|
Packit |
c4abd9 |
"\t-h Human-readable output file.\n" \
|
|
Packit |
c4abd9 |
"\t-b Binary output file.\n" \
|
|
Packit |
c4abd9 |
"\t-d Output file for data emitted by low level device driver.\n" \
|
|
Packit |
c4abd9 |
"\t-D Output file for debugging data.\n" \
|
|
Packit |
c4abd9 |
"\t-Qqm Output to message queue using given ID for messages.\n" \
|
|
Packit |
c4abd9 |
"\t-V Print program version.\n\n";
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
static struct option l_opts[] = {
|
|
Packit |
c4abd9 |
{
|
|
Packit |
c4abd9 |
.name = "human-readable",
|
|
Packit |
c4abd9 |
.has_arg = required_argument,
|
|
Packit |
c4abd9 |
.flag = NULL,
|
|
Packit |
c4abd9 |
.val = 'h'
|
|
Packit |
c4abd9 |
},
|
|
Packit |
c4abd9 |
{
|
|
Packit |
c4abd9 |
.name = "binary",
|
|
Packit |
c4abd9 |
.has_arg = required_argument,
|
|
Packit |
c4abd9 |
.flag = NULL,
|
|
Packit |
c4abd9 |
.val = 'b'
|
|
Packit |
c4abd9 |
},
|
|
Packit |
c4abd9 |
{
|
|
Packit |
c4abd9 |
.name = "dump-lldd",
|
|
Packit |
c4abd9 |
.has_arg = required_argument,
|
|
Packit |
c4abd9 |
.flag = NULL,
|
|
Packit |
c4abd9 |
.val = 'd'
|
|
Packit |
c4abd9 |
},
|
|
Packit |
c4abd9 |
{
|
|
Packit |
c4abd9 |
.name = "debug",
|
|
Packit |
c4abd9 |
.has_arg = required_argument,
|
|
Packit |
c4abd9 |
.flag = NULL,
|
|
Packit |
c4abd9 |
.val = 'D'
|
|
Packit |
c4abd9 |
},
|
|
Packit |
c4abd9 |
{
|
|
Packit |
c4abd9 |
.name = "interval",
|
|
Packit |
c4abd9 |
.has_arg = required_argument,
|
|
Packit |
c4abd9 |
.flag = NULL,
|
|
Packit |
c4abd9 |
.val = 'I'
|
|
Packit |
c4abd9 |
},
|
|
Packit |
c4abd9 |
{
|
|
Packit |
c4abd9 |
.name = "msg-queue",
|
|
Packit |
c4abd9 |
.has_arg = required_argument,
|
|
Packit |
c4abd9 |
.flag = NULL,
|
|
Packit |
c4abd9 |
.val = 'Q'
|
|
Packit |
c4abd9 |
},
|
|
Packit |
c4abd9 |
{
|
|
Packit |
c4abd9 |
.name = "msg-queue-id",
|
|
Packit |
c4abd9 |
.has_arg = required_argument,
|
|
Packit |
c4abd9 |
.flag = NULL,
|
|
Packit |
c4abd9 |
.val = 'q'
|
|
Packit |
c4abd9 |
},
|
|
Packit |
c4abd9 |
{
|
|
Packit |
c4abd9 |
.name = "msg-id",
|
|
Packit |
c4abd9 |
.has_arg = required_argument,
|
|
Packit |
c4abd9 |
.flag = NULL,
|
|
Packit |
c4abd9 |
.val = 'm'
|
|
Packit |
c4abd9 |
},
|
|
Packit |
c4abd9 |
{
|
|
Packit |
c4abd9 |
.name = "version",
|
|
Packit |
c4abd9 |
.has_arg = no_argument,
|
|
Packit |
c4abd9 |
.flag = NULL,
|
|
Packit |
c4abd9 |
.val = 'V'
|
|
Packit |
c4abd9 |
},
|
|
Packit |
c4abd9 |
{
|
|
Packit |
c4abd9 |
.name = NULL,
|
|
Packit |
c4abd9 |
}
|
|
Packit |
c4abd9 |
};
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
static void blkiomon_signal(int signal)
|
|
Packit |
c4abd9 |
{
|
|
Packit |
c4abd9 |
fprintf(stderr, "blkiomon: terminated by signal\n");
|
|
Packit |
c4abd9 |
up = signal & 0;
|
|
Packit |
c4abd9 |
}
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
int main(int argc, char *argv[])
|
|
Packit |
c4abd9 |
{
|
|
Packit |
c4abd9 |
int c;
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
signal(SIGALRM, blkiomon_signal);
|
|
Packit |
c4abd9 |
signal(SIGINT, blkiomon_signal);
|
|
Packit |
c4abd9 |
signal(SIGTERM, blkiomon_signal);
|
|
Packit |
c4abd9 |
signal(SIGQUIT, blkiomon_signal);
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
while ((c = getopt_long(argc, argv, S_OPTS, l_opts, NULL)) != -1) {
|
|
Packit |
c4abd9 |
switch (c) {
|
|
Packit |
c4abd9 |
case 'h':
|
|
Packit |
c4abd9 |
human.fn = optarg;
|
|
Packit |
c4abd9 |
break;
|
|
Packit |
c4abd9 |
case 'b':
|
|
Packit |
c4abd9 |
binary.fn = optarg;
|
|
Packit |
c4abd9 |
break;
|
|
Packit |
c4abd9 |
case 'd':
|
|
Packit |
c4abd9 |
drvdata.fn = optarg;
|
|
Packit |
c4abd9 |
break;
|
|
Packit |
c4abd9 |
case 'D':
|
|
Packit |
c4abd9 |
debug.fn = optarg;
|
|
Packit |
c4abd9 |
break;
|
|
Packit |
c4abd9 |
case 'I':
|
|
Packit |
c4abd9 |
interval = atoi(optarg);
|
|
Packit |
c4abd9 |
break;
|
|
Packit |
c4abd9 |
case 'Q':
|
|
Packit |
c4abd9 |
msg_q_name = optarg;
|
|
Packit |
c4abd9 |
break;
|
|
Packit |
c4abd9 |
case 'q':
|
|
Packit |
c4abd9 |
msg_q_id = atoi(optarg);
|
|
Packit |
c4abd9 |
break;
|
|
Packit |
c4abd9 |
case 'm':
|
|
Packit |
c4abd9 |
msg_id = atoi(optarg);
|
|
Packit |
c4abd9 |
break;
|
|
Packit |
c4abd9 |
case 'V':
|
|
Packit |
c4abd9 |
printf("%s version %s\n", argv[0], blkiomon_version);
|
|
Packit |
c4abd9 |
return 0;
|
|
Packit |
c4abd9 |
default:
|
|
Packit |
c4abd9 |
fprintf(stderr, "Usage: %s", usage_str);
|
|
Packit |
c4abd9 |
return 1;
|
|
Packit |
c4abd9 |
}
|
|
Packit |
c4abd9 |
}
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
if (interval <= 0) {
|
|
Packit |
c4abd9 |
fprintf(stderr, "Usage: %s", usage_str);
|
|
Packit |
c4abd9 |
return 1;
|
|
Packit |
c4abd9 |
}
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
ifp = fdopen(STDIN_FILENO, "r");
|
|
Packit |
c4abd9 |
if (!ifp) {
|
|
Packit |
c4abd9 |
perror("blkiomon: could not open stdin for reading");
|
|
Packit |
c4abd9 |
return 1;
|
|
Packit |
c4abd9 |
}
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
if (blkiomon_open_output(&human))
|
|
Packit |
c4abd9 |
return 1;
|
|
Packit |
c4abd9 |
if (blkiomon_open_output(&binary))
|
|
Packit |
c4abd9 |
return 1;
|
|
Packit |
c4abd9 |
if (blkiomon_open_output(&drvdata))
|
|
Packit |
c4abd9 |
return 1;
|
|
Packit |
c4abd9 |
if (blkiomon_open_output(&debug))
|
|
Packit |
c4abd9 |
return 1;
|
|
Packit |
c4abd9 |
if (blkiomon_open_msg_q())
|
|
Packit |
c4abd9 |
return 1;
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
if (pthread_create(&interval_thread, NULL, blkiomon_interval, NULL)) {
|
|
Packit |
c4abd9 |
fprintf(stderr, "blkiomon: could not create thread");
|
|
Packit |
c4abd9 |
return 1;
|
|
Packit |
c4abd9 |
}
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
blkiomon_do_fifo();
|
|
Packit |
c4abd9 |
|
|
Packit |
c4abd9 |
blkiomon_debug();
|
|
Packit |
c4abd9 |
return 0;
|
|
Packit |
c4abd9 |
}
|