Files
s390-tools/ziomon/ziomon_util.c
Ajaykumar Rajappa daad3bf0e7 ziomon: Send MQ poll status messages to stdout
Move MQ poll status messages from stderr to stdout since they are
normal progress indications rather than warnings/errors.

Signed-off-by: Ajaykumar Rajappa <ajaykr@linux.ibm.com>
Reviewed-by: M Nikhil <nikh1092@linux.ibm.com>
Reviewed-by: Nihar Panda <niharp@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-03-11 11:25:18 +01:00

1398 lines
34 KiB
C

/*
* FCP adapter trace utility
*
* Utilization data collector for zfcp adapters
*
* Copyright IBM Corp. 2008, 2017
*
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#include <assert.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <limits.h>
#include <limits.h>
#include <math.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <time.h>
#include <time.h>
#include <unistd.h>
#include "lib/zt_common.h"
#include "ziomon_util.h"
#ifdef WITH_MAIN
const char *toolname = "ziomon_util";
#else
extern const char *toolname;
#endif
struct overall_result_wrp {
long mtype; /* must be long due to msg-passing
interface */
struct utilization_data o_res;
};
struct ioerr_wrp {
long mtype;
struct ioerr_data data;
};
__u32 get_result_sz(struct utilization_data *res)
{
return (sizeof(struct utilization_data)
+ res->num_adapters * sizeof(struct adapter_utilization));
}
__u32 get_ioerr_data_sz(struct ioerr_data *wrp)
{
return (sizeof(struct ioerr_data)
+ wrp->num_luns * sizeof(struct ioerr_cnt));
}
void print_utilization_result(struct utilization_data *res)
{
int i;
struct adapter_utilization *a_res;
struct utilization_stats *u_res;
time_t t;
t = res->timestamp;
printf("timestamp : %s", ctime(&t));
printf("num adapters : %d\n", res->num_adapters);
for (i = 0; i < res->num_adapters; ++i) {
a_res = &res->adapt_utils[i];
u_res = &a_res->stats;
if (i != 0)
printf("\n");
printf("utilization of host adapter %d\n", a_res->adapter_no);
printf("\tnum samples: %llu\n", (unsigned long long)u_res->count);
if (u_res->count > 0) {
printf("\tqueue full incidents: %d\n", u_res->queue_full);
printf("\taverage queue utilization: %.1lf slots\n",
u_res->queue_util_integral / (double)u_res->queue_util_interval);
printf("\tadapter:\n");
print_abbrev_stat(&u_res->adapter, u_res->count);
printf("\tbus:\n");
print_abbrev_stat(&u_res->bus, u_res->count);
printf("\tprocessor:\n");
print_abbrev_stat(&u_res->cpu, u_res->count);
}
}
}
static void swap_overall_result(struct utilization_data *res)
{
int i;
struct adapter_utilization *a_res;
struct utilization_stats *u_res;
swap_64(res->timestamp);
for (i = 0; i < res->num_adapters; ++i) {
a_res = &res->adapt_utils[i];
swap_32(a_res->adapter_no);
swap_16(a_res->valid);
u_res = &a_res->stats;
swap_64(u_res->count);
swap_32(u_res->queue_full);
swap_64(u_res->queue_util_integral);
swap_64(u_res->queue_util_interval);
swap_abbrev_stat(&u_res->adapter);
swap_abbrev_stat(&u_res->bus);
swap_abbrev_stat(&u_res->cpu);
}
}
void conv_overall_result_to_BE(struct utilization_data *res)
{
swap_overall_result(res);
swap_16(res->num_adapters);
}
void conv_overall_result_from_BE(struct utilization_data *res)
{
swap_16(res->num_adapters);
swap_overall_result(res);
}
int compare_hctl_idents(const struct hctl_ident *src,
const struct hctl_ident *tgt)
{
if (src->host == tgt->host) {
if (src->channel == tgt->channel) {
if (src->target == tgt->target)
return (src->lun - tgt->lun);
else
return (src->target - tgt->target);
}
else
return (src->channel - tgt->channel);
}
else
return (src->host - tgt->host);
}
void aggregate_utilization_data(const struct utilization_data *src,
struct utilization_data *tgt)
{
int i;
assert(tgt->timestamp = src->timestamp);
tgt->timestamp = src->timestamp;
if (src->num_adapters != tgt->num_adapters)
fprintf(stderr, "%s: Warning: inconsistent"
" number of adapters: %d %d\n", toolname,
src->num_adapters, tgt->num_adapters);
for (i = 0; i < src->num_adapters; ++i)
aggregate_adapter_result(&src->adapt_utils[i], &tgt->adapt_utils[i]);
return;
}
void aggregate_adapter_result(const struct adapter_utilization *src,
struct adapter_utilization *tgt)
{
if (src->valid) {
/* in case we aggregate over different adapters,
we clearly indicate that this is not valid anymore */
if (src->adapter_no != tgt->adapter_no)
tgt->adapter_no = -1;
tgt->stats.queue_full += src->stats.queue_full;
tgt->stats.queue_util_integral +=
src->stats.queue_util_integral;
tgt->stats.queue_util_interval +=
src->stats.queue_util_interval;
tgt->stats.count += src->stats.count;
aggregate_abbrev_stat(&src->stats.adapter,
&tgt->stats.adapter);
aggregate_abbrev_stat(&src->stats.bus,
&tgt->stats.bus);
aggregate_abbrev_stat(&src->stats.cpu,
&tgt->stats.cpu);
}
}
void print_ioerr_data(struct ioerr_data *data)
{
__u64 i;
struct ioerr_cnt *cnt;
time_t t;
t = data->timestamp;
fprintf(stdout, "timestamp : %s", ctime(&t));
fprintf(stdout, "num luns : %lld\n", (long long)data->num_luns);
for (i = 0; i < data->num_luns; ++i) {
cnt = &data->ioerrors[i];
fprintf(stdout, "\t%d:%d:%d:%d: %lld I/O errors\n",
cnt->identifier.host, cnt->identifier.channel,
cnt->identifier.target, cnt->identifier.lun,
(long long)cnt->num_ioerr);
}
}
static void swap_ioerr_data(struct ioerr_data *data)
{
__u64 i;
struct ioerr_cnt *cnt;
swap_64(data->timestamp);
for (i = 0; i < data->num_luns; ++i) {
cnt = &data->ioerrors[i];
swap_32(cnt->identifier.host);
swap_32(cnt->identifier.channel);
swap_32(cnt->identifier.target);
swap_32(cnt->identifier.lun);
swap_64(cnt->num_ioerr);
}
}
void conv_ioerr_data_to_BE(struct ioerr_data *data)
{
swap_ioerr_data(data);
swap_64(data->num_luns);
}
void conv_ioerr_data_from_BE(struct ioerr_data *data)
{
swap_64(data->num_luns);
swap_ioerr_data(data);
}
void aggregate_ioerr_cnt(const struct ioerr_cnt *src, struct ioerr_cnt *tgt)
{
/* in case we aggregate over different devices,
we clearly indicate that this is not valid anymore */
if (compare_hctl_idents(&src->identifier, &tgt->identifier) != 0)
memset(&tgt->identifier, 0xff, sizeof(struct hctl_ident));
tgt->num_ioerr += src->num_ioerr;
}
void aggregate_ioerr_data(const struct ioerr_data *src, struct ioerr_data *tgt)
{
__u64 i;
assert(tgt->timestamp <= src->timestamp);
tgt->timestamp = src->timestamp;
for (i=0; i<src->num_luns; ++i)
aggregate_ioerr_cnt(&src->ioerrors[i], &tgt->ioerrors[i]);
}
#ifdef WITH_MAIN
#define SAMPLE_INTERVAL_DFT 2
#define SAMPLE_INTERVAL_DFT_STR "2"
static int keep_running;
int verbose=0;
struct util_data {
int queue_full; /* # queue full instances in frame */
int queue_full_prev; /* previous absolut value
of queue_full */
__u64 queue_util_integral;
__u64 queue_util_interval;
__u64 queue_util_prev; /* previous absolut value
of queue_util_integral */
struct timeval queue_util_timestamp;
struct abbrev_stat adapter;
struct abbrev_stat bus;
struct abbrev_stat cpu;
long count;
};
struct adapter_data {
int host_nr;
char *path;
char *q_full_path;
int status; /* 0 if good != 0 in case of failure */
struct util_data data;
};
struct adapters {
int num_adapters;
struct adapter_data adapters[0];
};
#define MAX_HOST_PATH_LEN 42
#define MAX_LUN_PATH_LEN strlen("/sys/bus/scsi/devices/4000000000:4000000000:4000000000:4000000000/ioerr_cnt")
struct options {
int num_hosts; /* total number of hosts */
int num_hosts_a; /* number of allocated host & lun structures */
long *host_nr; /* array if host numbers. to trace */
char **host_path; /* array of paths to utilization files */
int num_luns; /* number of luns */
char **luns; /* array of luns to monitor */
__u32 *luns_prev; /* array of previous values of luns */
long duration; /* overall duration in seconds */
long s_duration; /* ssample duration in seconds */
long i_duration; /* interval duration in seconds */
char *msg_q_path;
int msg_q_id;
int msg_q; /* msg q handle */
long msg_id; /* msg id to use in msg q */
long msg_id_ioerr; /* msg id to use in msg q for ioerr messages*/
};
static void init_opts(struct options *opts)
{
opts->i_duration = -1;
opts->duration = -1;
opts->s_duration = SAMPLE_INTERVAL_DFT;
opts->num_hosts = 0;
opts->num_hosts_a = 0;
opts->host_nr = NULL;
opts->host_path = NULL;
opts->num_luns = 0;
opts->luns = NULL;
opts->luns_prev = NULL;
opts->msg_q_path = NULL;
opts->msg_q_id = -1;
opts->msg_q = -1;
opts->msg_id = LONG_MIN;
opts->msg_id_ioerr = LONG_MIN;
}
static void deinit_opts(struct options *opts)
{
int i;
free(opts->host_nr);
opts->host_nr = NULL;
if (opts->host_path) {
for (i = 0; i < opts->num_hosts_a; ++i)
free(opts->host_path[i]);
free(opts->host_path);
opts->host_path = NULL;
}
for (i=0; i<opts->num_hosts_a; ++i)
free(opts->luns[i]);
opts->num_hosts_a = 0;
opts->msg_q = -1;
free(opts->luns);
free(opts->luns_prev);
}
static void init_util_data(struct util_data *data)
{
init_abbrev_stat(&data->adapter);
init_abbrev_stat(&data->bus);
init_abbrev_stat(&data->cpu);
data->count = 0;
}
static int init_ioerr_cnt(struct ioerr_cnt *cnt, char *str)
{
cnt->num_ioerr = 0;
return (sscanf(str, "%d:%d:%d:%d", &cnt->identifier.host,
&cnt->identifier.channel, &cnt->identifier.target,
&cnt->identifier.lun) == 5);
}
static int init_host_opts(struct options *opts, int num_hosts)
{
int i;
free(opts->host_nr);
if (opts->host_path) {
for (i = 0; i < opts->num_hosts_a; ++i)
free(opts->host_path[i]);
free(opts->host_path);
}
opts->host_nr = calloc(num_hosts, sizeof(long));
if (!opts->host_nr) {
fprintf(stderr, "%s: calloc opts->host_nr: %s\n",
toolname, strerror(errno));
return -1;
}
opts->host_path = malloc(num_hosts * sizeof(char *));
if (!opts->host_path) {
fprintf(stderr, "%s: malloc opts->host_path: %s\n",
toolname, strerror(errno));
return -1;
}
for (i = 0; i < num_hosts; ++i) {
opts->host_path[i] = malloc(sizeof(char) * MAX_HOST_PATH_LEN + 1);
if (!opts->host_path[i]) {
fprintf(stderr, "%s: malloc opts->host_path[]: %s\n",
toolname, strerror(errno));
return -1;
}
}
free(opts->luns);
opts->luns = calloc(num_hosts, sizeof(char *));
free(opts->luns_prev);
opts->luns_prev = calloc(num_hosts, sizeof(__u32));
if (!opts->luns || !opts->luns_prev) {
fprintf(stderr, "%s: malloc opts->luns: %s\n",
toolname, strerror(errno));
return -1;
}
for (i = 0; i < num_hosts; ++i) {
opts->luns[i] = malloc(sizeof(char) * MAX_LUN_PATH_LEN + 1);
if (!opts->host_path[i]) {
fprintf(stderr, "%s: malloc opts->luns[]: %s\n",
toolname, strerror(errno));
return -1;
}
}
opts->num_hosts_a = num_hosts;
return 0;
}
#define LINE_LEN 255
static int read_attribute(char *path, char *line, int *status)
{
int fd;
int rc = 0;
fd = open(path, O_RDONLY);
if (fd < 0) {
rc = -1; /* adapter gone */
goto out;
}
rc = read(fd, line, LINE_LEN);
if (rc < 0) {
rc = -2; /* I/O error */
goto out;
}
rc = 0;
close(fd);
out:
if (status)
*status = rc;
return rc;
}
static int poll_utilization(struct adapters *all_adapters)
{
char line[LINE_LEN];
int cpu, bus, adapter;
int rc = 0, grc = 0;
struct adapter_data *adpt;
struct util_data *u_data;
int i;
for (i = 0; i < all_adapters->num_adapters; ++i) {
adpt = &all_adapters->adapters[i];
u_data = &adpt->data;
/* read utilization attribute */
if (read_attribute(adpt->path, line, &adpt->status)) {
grc++;
continue;
}
rc = sscanf(line, "%d %d %d", &cpu, &bus, &adapter);
if (rc != 3) {
fprintf(stderr, "%s: Warning:"
" Could not parse %s: %s\n", toolname,
line, strerror(errno));
adpt->status = 3;
continue;
}
update_abbrev_stat(&u_data->adapter, adapter);
update_abbrev_stat(&u_data->bus, bus);
update_abbrev_stat(&u_data->cpu, cpu);
verbose_msg("data read for adapter %d: adapter=%d, bus=%d,"
" cpu=%d\n",
adpt->host_nr, adapter, bus, cpu);
u_data->count++;
}
return grc;
}
static int calc_overflow(__u64 old_val, __u64 new_val)
{
double ofl_val = 0;
int i = 0;
assert(old_val > new_val);
while (ofl_val < old_val) {
ofl_val = pow(2, i);
++i;
}
return (new_val + (int)(ofl_val - old_val));
}
static int poll_queue_full(int init, struct adapters *all_adapters)
{
char line[LINE_LEN];
int rc = 0;
struct adapter_data *adpt;
struct util_data *u_data;
int queue_full_tmp;
long long unsigned int queue_util_tmp;
int i;
struct timeval tmp, cur_time;
for (i = 0; i < all_adapters->num_adapters; ++i) {
adpt = &all_adapters->adapters[i];
u_data = &adpt->data;
/* read queue_full attribute */
if (read_attribute(adpt->q_full_path, line, &adpt->status))
continue;
rc = sscanf(line, "%d %Lu", &queue_full_tmp, &queue_util_tmp);
if (rc == 1) {
fprintf(stderr, "%s: Only one value in"
" %s, your kernel level is probably too old.\n",
toolname, adpt->q_full_path);
return -1;
}
if (rc != 2) {
fprintf(stderr, "%s: Warning:"
" Could not parse %s: %s\n",
toolname, line, strerror(errno));
adpt->status = 6;
continue;
}
gettimeofday(&cur_time, NULL);
if (!init) {
if (queue_full_tmp < u_data->queue_full_prev)
u_data->queue_full =
calc_overflow(u_data->queue_full_prev,
queue_full_tmp);
else
u_data->queue_full = queue_full_tmp
- u_data->queue_full_prev;
if (queue_util_tmp < u_data->queue_util_prev)
u_data->queue_util_integral =
calc_overflow(u_data->queue_util_prev,
queue_util_tmp);
else
u_data->queue_util_integral = queue_util_tmp
- u_data->queue_util_prev;
timersub(&cur_time, &u_data->queue_util_timestamp, &tmp);
u_data->queue_util_interval = tmp.tv_sec * 1000000
+ tmp.tv_usec;
}
u_data->queue_full_prev = queue_full_tmp;
u_data->queue_util_prev = queue_util_tmp;
u_data->queue_util_timestamp = cur_time;
}
return 0;
}
static int poll_ioerr_cnt(int init, struct ioerr_data *data,
struct options *opts)
{
char line[LINE_LEN];
int rc = 0, grc = 0;
int i;
__u32 tmp;
if (!init)
data->timestamp = time(NULL);
for (i=0; i<opts->num_luns; ++i) {
/* read ioerr_cnt attribute */
if (read_attribute(opts->luns[i], line, NULL)) {
fprintf(stderr, "%s: Warning: Could not read %s\n",
toolname, opts->luns[i]);
grc++;
continue;
}
rc = sscanf(line, "%i", &tmp);
if (rc != 1) {
fprintf(stderr, "%s: Warning:"
" Could not parse ioerr line %s: %s\n",
toolname, line, strerror(errno));
grc++;
continue;
}
if (!init) {
if (tmp < opts->luns_prev[i])
data->ioerrors[i].num_ioerr = calc_overflow(
opts->luns_prev[i], tmp);
else
data->ioerrors[i].num_ioerr = tmp
- opts->luns_prev[i];
verbose_msg("data read for i/o err %s: ioerr_cnt=%d\n",
opts->luns[i], data->ioerrors[i].num_ioerr);
}
opts->luns_prev[i] = tmp;
}
return grc;
}
static void reinit_adapters(struct adapters *adptrs)
{
int i;
for (i = 0; i < adptrs->num_adapters; ++i)
init_util_data(&adptrs->adapters[i].data);
}
static int init_adapters(struct adapters *all_adapters, struct options *opts)
{
struct adapter_data *adapter;
struct stat buf;
int i, rc = 0;
if (opts)
all_adapters->num_adapters = opts->num_hosts;
for (i = 0; i < all_adapters->num_adapters; ++i) {
adapter = &all_adapters->adapters[i];
if (opts) {
adapter->host_nr = opts->host_nr[i];
adapter->path = malloc(sizeof(char)*MAX_HOST_PATH_LEN);
sprintf(adapter->path, "%s/utilization",
opts->host_path[i]);
if (stat(adapter->path, &buf)) {
fprintf(stderr, "%s: Path does not exist: %s - correct kernel version?\n",
toolname, adapter->path);
rc++;
}
adapter->q_full_path =
malloc(sizeof(char)*MAX_HOST_PATH_LEN);
sprintf(adapter->q_full_path, "%s/queue_full",
opts->host_path[i]);
if (stat(adapter->q_full_path, &buf)) {
fprintf(stderr, "%s: Path does not exist: %s - correct kernel version?\n",
toolname, adapter->q_full_path);
rc++;
}
adapter->status = 0;
}
init_util_data(&all_adapters->adapters[i].data);
}
if (poll_queue_full(1, all_adapters))
return -1;
return rc;
}
static void deinit_adapters(struct adapters *all_adapters)
{
int i;
for (i = 0; i < all_adapters->num_adapters; ++i) {
free(all_adapters->adapters[i].path);
free(all_adapters->adapters[i].q_full_path);
}
}
/**
* set up structures for the lun result message and replace lun identifiers
* with complete path to respective ioerr_cnts.
*/
static int init_ioerr_wrp(struct ioerr_wrp **wrp, struct options *opts)
{
int i;
struct stat buf;
char tmp[255];
*wrp = malloc(sizeof(struct ioerr_wrp)
+ opts->num_luns*sizeof(struct ioerr_cnt));
if (!*wrp) {
fprintf(stderr, "%s: Memory allocation failed\n", toolname);
return -1;
}
(*wrp)->mtype = opts->msg_id_ioerr;
(*wrp)->data.num_luns = opts->num_luns;
for (i=0; i<opts->num_luns; ++i) {
if (init_ioerr_cnt(&(*wrp)->data.ioerrors[i], opts->luns[i])) {
fprintf(stderr, "%s: Could not parse %s\n",
toolname, opts->luns[i]);
return -2;
}
strcpy(tmp, opts->luns[i]);
sprintf(opts->luns[i], "/sys/bus/scsi/devices/%s/ioerr_cnt", tmp);
if (stat(opts->luns[i], &buf)) {
fprintf(stderr, "%s: Could not open %s: %s\n",
toolname, opts->luns[i], strerror(errno));
return -3;
}
}
if (poll_ioerr_cnt(1, NULL, opts)) {
fprintf(stderr, "%s: Could not read initial values of ioerr"
" attributes.\n", toolname);
return -1;
}
return 0;
}
static void init_result_wrp(struct overall_result_wrp **res, int num_adapters,
struct options *opts)
{
*res = malloc(sizeof(struct overall_result_wrp) + (num_adapters * sizeof(struct adapter_utilization)));
(*res)->o_res.num_adapters = num_adapters;
(*res)->mtype = opts->msg_id;
}
static void deinit_result_wrp(struct overall_result_wrp **res)
{
free(*res);
res = NULL;
}
static void print_version(void)
{
fprintf(stdout, "%s: ziomon utilization monitor, version %s\n"
"Copyright IBM Corp. 2008, 2017\n",
toolname, RELEASE_STRING);
}
static int get_argument_long(long *param, char opt)
{
char *p;
if (!optarg) {
fprintf(stderr, "%s: Argument missing"
" to option %c\n", toolname, opt);
return -1;
}
*param = strtol(optarg, &p, 0);
if (errno) {
fprintf(stderr, "%s: Unrecognized"
" parameter to option %c: %s\n", toolname, opt,
strerror(errno));
return -2;
}
if (*p != '\0') {
fprintf(stderr, "%s: Unrecognized"
" parameter to option %c\n", toolname, opt);
return -3;
}
return 0;
}
static int hostdir_filter(const struct dirent *dir)
{
return !strncmp(dir->d_name, "host", 4);
}
static int check_host_param(long *host_nr, char *host_path)
{
struct stat buf;
char *tmp;
if (*host_nr > 9999) {
fprintf(stderr, "%s: Host number out"
" of range\n", toolname);
return -1;
}
sprintf(host_path, "/sys/class/scsi_host/host%ld", *host_nr);
if (stat(host_path, &buf)) {
fprintf(stderr, "%s: Cannot access %s:"
" %s\n", toolname, host_path, strerror(errno));
return -1;
}
tmp = malloc(strlen(host_path) + strlen("/queue_full") + 1);
sprintf(tmp, "%s/queue_full", host_path);
if (stat(tmp, &buf)) {
fprintf(stderr, "%s: Cannot access %s."
" Your installed kernel is probably too old. Please"
" check that your kernel matches the level in the"
" documentation.\n", toolname, tmp);
free(tmp);
return -1;
}
free(tmp);
verbose_msg("host path : %s\n", host_path);
return 0;
}
static int find_all_hosts(struct options *opts)
{
char *h_path = "/sys/class/scsi_host";
struct dirent **namelist;
int num_dirents;
int i, k;
int rc = 0;
verbose_msg("no host adapter(s) specified, scanning...\n");
num_dirents = scandir(h_path, &namelist, hostdir_filter, alphasort);
if (num_dirents <= 0) {
fprintf(stderr, "%s: No host adapter(s) found\n", toolname);
rc = -3;
goto out;
}
if (init_host_opts(opts, num_dirents))
return -1;
opts->num_hosts = num_dirents;
for (i = 0; i < num_dirents; ++i) {
k = sscanf(namelist[i]->d_name, "host%ld", &opts->host_nr[i]);
if (k != 1) {
fprintf(stderr, "%s: Internal error while scanning"
" host adapter no\n", toolname);
rc = -4;
} else
check_host_param(&opts->host_nr[i],
opts->host_path[i]);
}
out:
for (i = 0; i < num_dirents; i++)
free(namelist[i]);
free(namelist);
return rc;
}
static int host_adapter_compare(const void *a, const void *b)
{
return (*(long *)a > *(long *)b);
}
static int setup_msg_q(struct options *opts)
{
key_t util_q;
int wait=0;
if (opts->msg_id <= 0) {
fprintf(stderr, "%s: Invalid or missing msg"
" id for utilization messages\n", toolname);
return -1;
}
if (opts->msg_id_ioerr <= 0) {
fprintf(stderr, "%s: Invalid or missing msg"
" id for I/O error messages\n", toolname);
return -1;
}
if (opts->msg_id == opts->msg_id_ioerr) {
fprintf(stderr, "%s: Message IDs for"
" I/O error count and utilization messages must be"
" different\n", toolname);
return -1;
}
util_q = ftok(opts->msg_q_path, opts->msg_q_id);
verbose_msg("message queue key is %d\n", util_q);
while (keep_running) {
opts->msg_q = msgget(util_q, S_IRWXU);
if (opts->msg_q >= 0) {
if (wait)
fprintf(stdout, "%s: Message queue is up!\n", toolname);
break;
}
if (!wait) {
wait = 1;
fprintf(stdout, "%s: Message queue not up yet, waiting...\n", toolname);
}
usleep(200000);
}
verbose_msg("message queue id is %d\n", opts->msg_q);
if (opts->msg_q_path) {
verbose_msg("message queue path : %s\n", opts->msg_q_path);
verbose_msg("message queue id : %d\n", opts->msg_q_id);
verbose_msg("message id : %ld\n", opts->msg_id);
verbose_msg("message id : %ld\n", opts->msg_id);
}
return (opts->msg_q >= 0 ? 0 : -1);
}
static void generate_result(struct utilization_data *ures,
struct adapters *all_adapters)
{
struct adapter_data *a_data;
struct util_data *u_data;
struct adapter_utilization *a_res;
struct utilization_stats *u_res;
int i;
ures->num_adapters = all_adapters->num_adapters;
ures->timestamp = time(NULL);
for (i = 0; i < all_adapters->num_adapters; ++i) {
a_data = &all_adapters->adapters[i];
a_res = &ures->adapt_utils[i];
a_res->adapter_no = a_data->host_nr;
u_data = &a_data->data;
a_res->valid = u_data->count;
if (a_res->valid) {
u_res = &a_res->stats;
u_res->queue_full = u_data->queue_full;
u_res->queue_util_integral = u_data->queue_util_integral;
u_res->queue_util_interval = u_data->queue_util_interval;
u_res->count = u_data->count;
copy_abbrev_stat(&u_res->adapter, &u_data->adapter);
copy_abbrev_stat(&u_res->bus, &u_data->bus);
copy_abbrev_stat(&u_res->cpu, &u_data->cpu);
}
}
}
static const char help_text[] =
"Usage: ziomon_util [-h] [-v] [-V] [-i n] [-s n] "
"[-Q <msgq_path> -q <msgq_id>\n"
" -m <msg_id>] -d n -a <n> -l <lun>\n"
"\n"
"Start the monitor for the host adapter utilization.\n"
"Example: ziomon_util -d 60 -i 4 -a 0\n"
"\n"
"-h, --help Print usage information and exit.\n"
"-v, --version Print version information and exit.\n"
"-V, --verbose Be verbose.\n"
"-s, --sample-length Duration between each sample in seconds.\n"
" Defaults to "SAMPLE_INTERVAL_DFT_STR" seconds.\n"
"-i, --interval-length Aggregate samples over this duration (in seconds).\n"
" Defaults to 'duration'.\n"
"-d, --duration Overall duration in seconds.\n"
"-a, --adapter Host adapter no. to watch. Specify each host"
" adapter\n"
" separately.\n"
"-l, --lun watch I/O error count of LUN. Specify each LUN\n"
" separately in h:b:t:l format.\n"
"-Q, --msg-queue-name Specify the message queue path name.\n"
"-q, --msg-queue-id Specify the message queue id.\n"
"-m, --msg-id Specify the message id to use.\n"
"-L, --msg-id-ioerr Specify the message id for I/O error count"
" messages.\n";
static void print_help(void)
{
fprintf(stdout, "%s", help_text);
}
static int parse_params(int argc, char **argv, struct options *opts)
{
int c;
int index, i;
static struct option long_options[] = {
{ "version", no_argument, NULL, 'v'},
{ "help", no_argument, NULL, 'h'},
{ "verbose", no_argument, NULL, 'V'},
{ "msg-queue-name", required_argument, NULL, 'Q'},
{ "msg-queue-id", required_argument, NULL, 'q'},
{ "msg-id", required_argument, NULL, 'm'},
{ "msg-id-ioerr", required_argument, NULL, 'L'},
{ "sample-length", required_argument, NULL, 's'},
{ "interval-length",required_argument, NULL, 'i'},
{ "duration", required_argument, NULL, 'd'},
{ "adapter", required_argument, NULL, 'a'},
{ "lun", required_argument, NULL, 'l'},
{ NULL, 0, NULL, 0 }
};
if (argc <= 1) {
print_help();
return -1;
}
/* this is too much, but argc/2 is a reliable upper boundary
and saves us the trouble of figuring out how many host
adapters were specified up front */
init_host_opts(opts, argc/2);
while ((c = getopt_long(argc, argv, "L:l:m:Q:q:a:s:d:i:vhV", long_options,
&index)) != EOF) {
switch (c) {
case 'V':
verbose = 1;
break;
case 'a':
if (get_argument_long(&opts->host_nr[opts->num_hosts], c))
return -1;
(opts->num_hosts)++;
break;
case 'l':
if (!optarg) {
fprintf(stderr, "%s: Argument missing to"
" option '-l'\n", toolname);
return -1;
}
strcpy(opts->luns[opts->num_luns], optarg);
(opts->num_luns)++;
break;
case 'L':
if (get_argument_long(&opts->msg_id_ioerr, c))
return -1;
break;
case 'i':
if (get_argument_long(&opts->i_duration, c))
return -1;
break;
case 's':
if (get_argument_long(&opts->s_duration, c))
return -1;
break;
case 'd':
if (get_argument_long(&opts->duration, c))
return -1;
break;
case 'm':
if (get_argument_long(&opts->msg_id, c))
return -1;
break;
case 'Q':
if (!optarg) {
fprintf(stderr, "%s: Argument missing to"
" option '-Q'\n", toolname);
return -1;
}
opts->msg_q_path = optarg;
break;
case 'q':
if (!optarg) {
fprintf(stderr, "%s: Argument missing to"
" option '-Q'\n", toolname);
return -1;
}
opts->msg_q_id = atoi(optarg);
if (opts->msg_q_id < 0) {
fprintf(stderr, "%s: Parameter to option '-q'"
" must be greater than 0\n", toolname);
return -1;
}
break;
case 'v':
print_version();
return 1;
case 'h':
print_help();
return 1;
default:
fprintf(stderr, "%s: Try '%s --help' for more"
" information.\n", toolname, toolname);
return -1;
}
}
if (opts->duration < 0) {
fprintf(stderr, "%s: No duration specified\n", toolname);
return -1;
}
if (opts->i_duration < 0)
opts->i_duration = opts->duration;
if (opts->i_duration < opts->s_duration) {
fprintf(stderr, "%s: Sample duration"
" must be at least the interval duration\n", toolname);
return -1;
}
if (opts->duration < opts->i_duration) {
fprintf(stderr, "%s: Overall duration"
" must be at least the interval duration\n", toolname);
return -1;
}
if (opts->s_duration && opts->i_duration % opts->s_duration) {
fprintf(stderr, "%s: Sample duration"
" must be a multiple of sample duration\n", toolname);
return -1;
}
if (opts->i_duration && opts->duration % opts->i_duration) {
fprintf(stderr, "%s: overall duration"
" must be a multiple of section duration\n", toolname);
return -1;
}
if (opts->num_hosts > 0) {
qsort(opts->host_nr, opts->num_hosts, sizeof(long),
host_adapter_compare);
for (c = 0; c < opts->num_hosts; ++c) {
if (check_host_param(&opts->host_nr[c],
opts->host_path[c]))
return -1;
}
} else {
if (find_all_hosts(opts))
return -1;
}
if (opts->msg_q_path || opts->msg_q_id >= 0
|| opts->msg_id != LONG_MIN) {
if (!opts->msg_q_path || opts->msg_q_id < 0
|| opts->msg_id == LONG_MIN) {
fprintf(stderr, "%s: Make sure to"
" specify all required arguments for message"
" queue.\n", toolname);
return -1;
}
}
verbose_msg("num adapters : %d\n", opts->num_hosts);
verbose_msg("num luns : %d\n", opts->num_luns);
for (i=0; i<opts->num_luns; ++i)
verbose_msg("lun : %s\n", opts->luns[i]);
verbose_msg("overall duration : %lds\n", opts->duration);
verbose_msg("interval duration : %lds\n", opts->i_duration);
verbose_msg("sample duration : %lds\n", opts->s_duration);
return 0;
}
static void send_message(int msg_q, void *data, size_t data_sz)
{
if (msgsnd(msg_q, data, data_sz, 0) < 0) {
/* somehow we don't get this signal if queue is shut down
though we should... */
if (errno == EIDRM) {
keep_running = 0;
verbose_msg("msgqueue removed, shutting down...\n");
} else {
fprintf(stderr, "%s: Failed to send"
" message: %s\n", toolname, strerror(errno));
verbose_msg("msgsnd() returned error code %d\n",
errno);
}
}
}
static int has_ioerrs(struct ioerr_data *ioerr)
{
__u64 i;
for (i = 0; i < ioerr->num_luns; ++i) {
if (ioerr->ioerrors[i].num_ioerr != 0)
return 1;
}
return 0;
}
static int has_non_null_stats(struct abbrev_stat *var)
{
return !(var->max == 0 && var->min == 0);
}
static int has_adapter_traffic(struct utilization_data *res)
{
int i;
struct adapter_utilization *a_res;
for (i=0; i<res->num_adapters; ++i) {
a_res = &res->adapt_utils[i];
if (!a_res->valid)
return 1;
if (a_res->stats.queue_full != 0)
return 1;
/* makes sure that we don't send messages when essentially no
data is processed. Reading the 'utilization' sysfs attribute
causes traffic, hence we would always send a message without
this threshold! */
if (a_res->stats.queue_util_integral
/ (double)a_res->stats.queue_util_interval >= 0.05)
return 1;
if (has_non_null_stats(&a_res->stats.adapter)
|| has_non_null_stats(&a_res->stats.bus)
|| has_non_null_stats(&a_res->stats.cpu))
return 1;
}
return 0;
}
static void print_to_msg_q(struct overall_result_wrp *res_wrp,
struct ioerr_wrp *ioerr,
struct options *opts,
int force)
{
size_t msg_size;
if (has_adapter_traffic(&res_wrp->o_res) || force) {
msg_size = get_result_sz(&res_wrp->o_res);
if (verbose)
print_utilization_result(&res_wrp->o_res);
verbose_msg("write utilization result to msg q %d (msg-type: %ld, msg-size: %d)\n",
opts->msg_q, res_wrp->mtype, (unsigned int)msg_size);
conv_overall_result_to_BE(&res_wrp->o_res);
send_message(opts->msg_q, res_wrp, msg_size);
}
if (has_ioerrs(&ioerr->data) || force) {
msg_size = get_ioerr_data_sz(&ioerr->data);
if (verbose)
print_ioerr_data(&ioerr->data);
verbose_msg("write ioerr result to msg q %d (msg-type: %ld, msg-size: %d)\n",
opts->msg_q, ioerr->mtype, (unsigned int)msg_size);
conv_ioerr_data_to_BE(&ioerr->data);
send_message(opts->msg_q, ioerr, msg_size);
}
}
static void void_handler(int sig)
{
verbose_msg("interrupted by signal %u\n", sig);
keep_running = 0;
}
static void setup_signals(void)
{
signal(SIGALRM, void_handler);
signal(SIGINT, void_handler);
signal(SIGTERM, void_handler);
signal(SIGQUIT, void_handler);
}
static void sleep_until(struct timeval *end)
{
struct timeval tmp;
do {
gettimeofday(&tmp, NULL);
if (timercmp(&tmp, end, >=))
break;
timersub(end, &tmp, &tmp);
verbose_msg("sleep for %ld msec\n", tmp.tv_sec * 1000000
+ tmp.tv_usec);
usleep(tmp.tv_sec * 1000000 + tmp.tv_usec);
} while (keep_running);
}
/*
* Params:
* -h : host nr.
* -t : duration
*/
int main(int argc, char **argv)
{
struct options opts;
struct adapters *all_adapters = NULL;
struct overall_result_wrp *result_wrp = NULL;
struct timeval sample_end;
struct timeval interval_end;
struct timeval duration_end;
struct timeval first_interval;
struct ioerr_wrp *ioerr = NULL;
int rc = 0;
int parse_params_rc;
verbose = 0;
keep_running = 1;
setup_signals();
init_opts(&opts);
parse_params_rc = parse_params(argc, argv, &opts);
if (parse_params_rc < 0) {
rc = -1;
goto out2;
} else if (parse_params_rc > 0) {
rc = 0;
goto out2;
}
if (opts.msg_q_path && setup_msg_q(&opts)) {
rc = -2;
goto out2;
}
all_adapters = malloc(sizeof(struct adapters)
+ opts.num_hosts * sizeof(struct adapter_data));
if (init_adapters(all_adapters, &opts)) {
rc = -8;
goto out2;
}
init_result_wrp(&result_wrp, all_adapters->num_adapters, &opts);
if (init_ioerr_wrp(&ioerr, &opts)) {
rc = -3;
goto out;
}
gettimeofday(&sample_end, NULL);
timerclear(&interval_end);
timerclear(&duration_end);
timerclear(&first_interval);
timeradd(&interval_end, &sample_end, &interval_end);
timeradd(&first_interval, &sample_end, &first_interval);
first_interval.tv_sec += opts.i_duration;
timeradd(&duration_end, &sample_end, &duration_end);
duration_end.tv_sec += opts.duration;
do {
interval_end.tv_sec += opts.i_duration;
reinit_adapters(all_adapters);
do {
sample_end.tv_sec += opts.s_duration;
sleep_until(&sample_end);
poll_utilization(all_adapters);
if (timercmp(&sample_end, &interval_end, >=)) {
/* final sample in interval */
if (poll_queue_full(0, all_adapters)) {
rc = -3;
goto out;
}
if (poll_ioerr_cnt(0, &ioerr->data, &opts)) {
rc = -7;
goto out;
}
}
} while (keep_running
&& timercmp(&sample_end, &interval_end, <));
if (!keep_running)
break; /* only publish results after a full cycle */
generate_result(&result_wrp->o_res, all_adapters);
if (opts.msg_q >= 0)
/* Always print the first and the last message */
print_to_msg_q(result_wrp, ioerr, &opts,
(timercmp(&interval_end, &first_interval, ==)
|| timercmp(&interval_end, &duration_end, >=)));
else {
print_utilization_result(&result_wrp->o_res);
print_ioerr_data(&ioerr->data);
}
/* we only have to sleep in case d_interval is not
a multiple of i_interval */
sleep_until(&interval_end);
} while (keep_running && timercmp(&interval_end, &duration_end, <));
if (!keep_running)
verbose_msg("signal received, ending...\n");
out:
deinit_adapters(all_adapters);
deinit_result_wrp(&result_wrp);
out2:
deinit_opts(&opts);
free(all_adapters);
free(ioerr);
return rc;
}
#endif