Files
s390-tools/zconf/chp/chpstat/cmg1.c
Peter Oberparleiter cb77faeae7 chpstat: add tool to display channel-path statistics
Add a new tool named chpstat that can be used to view channel-path
statistics such as utilization and I/O throughput, and to query and
control the status of the channel-path statistics function.

Note: Channel-path statistics are only available on systems running in
      an LPAR or DPM partition.

When run without further options, data for all channel-paths is
displayed repeatedly with a 5 second delay in table format.

Example output:

      CHANNEL-PATH       UTILIZATION(%)   READ(B/s)  WRITE(B/s)
  ID TYP CMG SHR SPEED  PART TOTAL  BUS  PART TOTAL  PART TOTAL
  1d  25   2   1     -  7.16  7.50 7.50  129M  129M  0.00  161K
  21  1b   2   1   32G  0.00  0.00 0.00  0.00  0.00  0.00  0.00
  34  1b   2   1   32G  0.00  0.00 0.00  0.00  0.00  0.00  0.00
  61  25   2   1     -  0.00  0.01 0.00  0.00 2.00K  0.00  307K
  63  25   2   1     -  0.00  0.01 0.00  0.00  0.00  0.00  381K
  bd  11   2   1   10G     -     -    - 529.8 532.1 616.3 616.3

Reviewed-by: Vineeth Vijayan <vneethv@linux.ibm.com>
Signed-off-by: Peter Oberparleiter <oberpar@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2024-05-27 16:52:27 +02:00

144 lines
3.5 KiB
C

/*
* Support for CMG 1 channel-path statistics
*
* Copyright IBM Corp. 2024
*
* 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 "cmg.h"
#include "cmg_helper.h"
#include "column.h"
#include "key.h"
#include "lib/util_base.h"
#include "lib/zt_common.h"
/* Macros to convert generic cmg_data_t into CMG-specific ones. */
#define get_cue(d, x) ((struct cue1_t *)&((d)->x.cue))
#define get_metrics(d) ((struct metrics1_t *)&((d)->metrics))
/* CMG 1 format Channel-Utilization-Entry. */
struct cue1_t {
u8 cuiv;
u32 timestamp:24;
u32 channel_path_busy_time_cpc;
u32 channel_path_busy_time;
} __packed;
STATIC_ASSERT(sizeof(struct cue1_t) <= sizeof(cue_t));
/* Metrics based on CMG 1 format CUEs. */
struct metrics1_t {
double interval;
double util_total;
double util_part;
};
STATIC_ASSERT(sizeof(struct metrics1_t) <= sizeof(metrics_t));
/* IDs of columns that should be shown by default in table output. */
static const enum column_id_t default_column_ids[] = {
COL_CHPID,
COL_TYPE,
COL_CMG,
COL_SHARED,
COL_SPEED,
COL_UTIL_PART,
COL_UTIL_TOTAL,
/* End of list. */
COL_END
};
static void pr_cue(struct cmg_pair_t **a, unsigned int *n,
struct cmg_data_t *data)
{
struct cue1_t *cue = get_cue(data, util_b);
pr_u32(a, n, cue, timestamp);
pr_cue_u32(a, n, cue, channel_path_busy_time_cpc);
pr_cue_u32(a, n, cue, channel_path_busy_time);
}
static void pr_metrics(struct cmg_pair_t **a, unsigned int *n,
struct cmg_data_t *data)
{
struct metrics1_t *metrics = get_metrics(data);
pr_metric(a, n, metrics, interval, CMG_NUMBER, COL_INTERVAL);
pr_metric(a, n, metrics, util_total, CMG_PERCENT, COL_UTIL_TOTAL);
pr_metric(a, n, metrics, util_part, CMG_PERCENT, COL_UTIL_PART);
}
static struct cmg_pair_t *get_values(struct cmg_data_t *data, int groups)
{
struct cmg_pair_t *array = NULL;
unsigned int num = 0;
if (groups & KEY_GRP_UTIL)
pr_cue(&array, &num, data);
if (groups & KEY_GRP_METRICS)
pr_metrics(&array, &num, data);
/* Add terminating null-element. */
util_expand_array(&array, &num);
array[num - 1].key = NULL;
return array;
}
/* Initialize metrics in @m. */
static void init_metrics(struct metrics1_t *m)
{
m->interval = METRICS_INIT;
m->util_total = METRICS_INIT;
m->util_part = METRICS_INIT;
}
/* Recalculate metrics in @data. */
static void update_metrics(struct cmg_data_t *data)
{
struct metrics1_t *m = get_metrics(data);
struct cue1_t *a = get_cue(data, util_a);
struct cue1_t *b = get_cue(data, util_b);
u32 ticks, delta;
init_metrics(m);
ticks = get_delta(a->timestamp, b->timestamp, CUE_TS_WIDTH);
if (ticks == 0)
return;
/* interval = t2 - t1 */
m->interval = tick_to_s(ticks);
/* util_total = 100.0 * ticks_busy_cpc / ticks_total */
if (cue_valid2(a, b, channel_path_busy_time_cpc)) {
delta = field_delta(channel_path_busy_time_cpc, a, b);
m->util_total = 100.0 * delta / ticks;
}
/* util_part = 100.0 * ticks_busy / ticks_total */
if (cue_valid2(a, b, channel_path_busy_time)) {
delta = field_delta(channel_path_busy_time, a, b);
m->util_part = 100.0 * delta / ticks;
}
}
/* Object defining this CMG. */
static struct cmg_t cmg1 = {
.cmg = 1,
.selected = false,
.found = 0,
.has_cmcb = false,
.default_column_ids = default_column_ids,
.get_values = &get_values,
.update_metrics = &update_metrics,
};
/* Add to CMG registry. */
static void __attribute__((constructor)) cmg1_ctr(void)
{
cmg_add(&cmg1);
}