Files
s390-tools/zconf/chp/chpstat/cmg3.c
Peter Oberparleiter 026ecbafea chpstat: Add support for full CMCB
Newer kernels provide the full, unfiltered Channel-Measurements
Characteristics Block (CMCB) via a new sysfs attribute named
"measurement_chars_full".

Add support for reading the full CMCB data if available in preparation
of new tool functions that will make use of this data.

Signed-off-by: Peter Oberparleiter <oberpar@linux.ibm.com>
Reviewed-by: Vineeth Vijayan <vneethv@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-11-29 15:52:24 +01:00

318 lines
8.8 KiB
C

/*
* Support for CMG 3 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"
/* Macros to convert generic cmg_data_t into CMG-specific ones. */
#define get_cmcb(d) ((struct cmcb3_t *)&((d)->cmcb))
#define get_cue(d, x) ((struct cue3_t *)&((d)->x.cue))
#define get_metrics(d) ((struct metrics3_t *)&((d)->metrics))
/* CMG 3 format Channel-Measurement-Characteristics Block (CMCB). */
struct cmcb3_t {
u32 reserved[3];
u32 data_unit_size;
u32 data_unit_size_cpc;
u32 msg_unit_size;
u32 msg_unit_size_cpc;
} __packed;
STATIC_ASSERT(sizeof(struct cmcb3_t) <= sizeof(cmcb_t));
/* CMG 3 format Channel-Utilization-Entry (CUE). */
struct cue3_t {
u8 cuiv;
u32 timestamp:24;
u32 msg_units_sent;
u32 msg_units_sent_cpc;
u32 unsuccessful_attempts_to_send;
u32 unavailable_receive_buffers;
u32 unavailable_receive_buffers_cpc;
u32 data_units_sent;
u32 data_units_sent_cpc;
} __packed;
STATIC_ASSERT(sizeof(struct cue3_t) <= sizeof(cue_t));
/* Metrics based on CMG 3 format CUEs. */
struct metrics3_t {
double interval;
double write_total;
double write_part;
double msg_rate_total;
double msg_rate_part;
double msg_size_total;
double msg_size_part;
double send_fail_part;
double rcv_fail_total;
double rcv_fail_part;
};
STATIC_ASSERT(sizeof(struct metrics3_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_WRITE_PART,
COL_WRITE_TOTAL,
COL_MSG_RATE_PART,
COL_MSG_RATE_TOTAL,
COL_MSG_SIZE_PART,
COL_MSG_SIZE_TOTAL,
COL_SEND_FAIL_PART,
COL_RCV_FAIL_PART,
COL_RCV_FAIL_TOTAL,
/* End of list. */
COL_END
};
static void pr_chars(struct cmg_pair_t **a, unsigned int *n,
struct cmg_data_t *data)
{
struct cmcb3_t *cmcb = get_cmcb(data);
pr_u32(a, n, cmcb, data_unit_size);
pr_u32(a, n, cmcb, data_unit_size_cpc);
pr_u32(a, n, cmcb, msg_unit_size);
pr_u32(a, n, cmcb, msg_unit_size_cpc);
}
static void pr_cue(struct cmg_pair_t **a, unsigned int *n,
struct cmg_data_t *data)
{
struct cue3_t *cue = get_cue(data, util_b);
pr_u32(a, n, cue, timestamp);
pr_cue_u32(a, n, cue, msg_units_sent);
pr_cue_u32(a, n, cue, msg_units_sent_cpc);
pr_cue_u32(a, n, cue, unsuccessful_attempts_to_send);
pr_cue_u32(a, n, cue, unavailable_receive_buffers);
pr_cue_u32(a, n, cue, unavailable_receive_buffers_cpc);
pr_cue_u32(a, n, cue, data_units_sent);
pr_cue_u32(a, n, cue, data_units_sent_cpc);
}
static void pr_metrics(struct cmg_pair_t **a, unsigned int *n,
struct cmg_data_t *data)
{
struct metrics3_t *m = get_metrics(data);
pr_metric(a, n, m, write_part, CMG_BPS, COL_WRITE_PART);
pr_metric(a, n, m, write_total, CMG_BPS, COL_WRITE_TOTAL);
pr_metric(a, n, m, msg_rate_part, CMG_NUMBER, COL_MSG_RATE_PART);
pr_metric(a, n, m, msg_rate_total, CMG_NUMBER, COL_MSG_RATE_TOTAL);
pr_metric(a, n, m, msg_size_part, CMG_NUMBER, COL_MSG_SIZE_PART);
pr_metric(a, n, m, msg_size_total, CMG_NUMBER, COL_MSG_SIZE_TOTAL);
pr_metric(a, n, m, send_fail_part, CMG_NUMBER, COL_SEND_FAIL_PART);
pr_metric(a, n, m, rcv_fail_part, CMG_NUMBER, COL_RCV_FAIL_PART);
pr_metric(a, n, m, rcv_fail_total, CMG_NUMBER, COL_RCV_FAIL_TOTAL);
}
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_CHARS)
pr_chars(&array, &num, data);
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 metrics3_t *m)
{
m->interval = METRICS_INIT;
m->write_total = METRICS_INIT;
m->write_part = METRICS_INIT;
m->msg_rate_total = METRICS_INIT;
m->msg_rate_part = METRICS_INIT;
m->msg_size_total = METRICS_INIT;
m->msg_size_part = METRICS_INIT;
m->send_fail_part = METRICS_INIT;
m->rcv_fail_total = METRICS_INIT;
m->rcv_fail_part = METRICS_INIT;
}
/* Calculate metrics base on CMG 3 CUEs. */
static void calc_metrics(struct cmg_data_t *data, double seconds)
{
u32 data_size, data_size_cpc, msg_size, msg_size_cpc;
struct metrics3_t *m = get_metrics(data);
struct cmcb3_t *cmcb = get_cmcb(data);
struct cue3_t *a = get_cue(data, util_a);
struct cue3_t *b = get_cue(data, util_b);
double delta, delta2;
/* When not valid (reported as 0) some CMCB fields have default values
* or can be derived from related fields. */
data_size = cmcb->data_unit_size ? : 1;
data_size_cpc = cmcb->data_unit_size_cpc ? : data_size;
msg_size = cmcb->msg_unit_size ? : 1;
msg_size_cpc = cmcb->msg_unit_size_cpc ? : msg_size;
/*
* Amount of bytes per second sent in messages by all partitions
*
* write_total = data_units_sent_cpc * data_unit_size_cpc / seconds
*/
if (cue_valid2(a, b, data_units_sent_cpc)) {
delta = field_delta(data_units_sent_cpc, a, b);
m->write_total = delta * data_size_cpc / seconds;
}
/*
* Amount of bytes per second sent in messages by this partition
*
* write_part = data_units_sent * data_unit_size / seconds
*/
if (cue_valid2(a, b, data_units_sent)) {
delta = field_delta(data_units_sent, a, b);
m->write_part = delta * data_size / seconds;
}
/*
* Rate of messages sent per second by all partitions
*
* msg_rate_total = msg_units_sent_cpc * msg_unit_size_cpc / seconds
*/
if (cue_valid2(a, b, msg_units_sent_cpc)) {
delta = field_delta(msg_units_sent_cpc, a, b);
m->msg_rate_total = delta * msg_size_cpc / seconds;
}
/*
* Rate of messages sent per second by this partition
*
* msg_rate_part = msg_units_sent * msg_unit_size / seconds
*/
if (cue_valid2(a, b, msg_units_sent)) {
delta = field_delta(msg_units_sent, a, b);
m->msg_rate_part = delta * msg_size / seconds;
}
/*
* Average size of messages sent by all partitions
*
* msg_size_total = data_units_sent_cpc * data_unit_size_cpc /
* (msg_units_sent_cpc * msg_unit_size_cpc)
*/
if (cue_valid2(a, b, data_units_sent_cpc) &&
cue_valid2(a, b, msg_units_sent_cpc)) {
delta = field_delta(data_units_sent_cpc, a, b);
delta2 = field_delta(msg_units_sent_cpc, a, b);
if (delta2 != 0.0) {
m->msg_size_total = delta * data_size_cpc /
(delta2 * msg_size_cpc);
}
}
/*
* Average size of messages sent by this partition
*
* msg_size_part = data_units_sent * data_unit_size /
* (msg_units_sent * msg_unit_size)
*/
if (cue_valid2(a, b, data_units_sent) &&
cue_valid2(a, b, msg_units_sent)) {
delta = field_delta(data_units_sent, a, b);
delta2 = field_delta(msg_units_sent, a, b);
if (delta2 != 0.0) {
m->msg_size_part = delta * data_size /
(delta2 * msg_size);
}
}
/*
* Number of failed message send attempts per second by this partition
*
* send_fail_part = unsuccessful_attempts_to_send / seconds
*/
if (cue_valid2(a, b, unsuccessful_attempts_to_send)) {
delta = field_delta(unsuccessful_attempts_to_send, a, b);
m->send_fail_part = delta / seconds;
}
/*
* Rate of messages per second that could not be received by all
* partitions due to unavailable receive buffers
*
* rcv_fail_total = unavailable_receive_buffers_cpc / seconds
*/
if (cue_valid2(a, b, unavailable_receive_buffers_cpc)) {
delta = field_delta(unavailable_receive_buffers_cpc, a, b);
m->rcv_fail_total = delta / seconds;
}
/*
* Rate of messages per second that could not be received by this
* partition due to unavailable receive buffers
*
* rcv_fail_part = unavailable_receive_buffers / seconds
*/
if (cue_valid2(a, b, unavailable_receive_buffers)) {
delta = field_delta(unavailable_receive_buffers, a, b);
m->rcv_fail_part = delta / seconds;
}
}
/* Recalculate metrics in @data. */
static void update_metrics(struct cmg_data_t *data)
{
struct metrics3_t *m = get_metrics(data);
struct cue3_t *a = get_cue(data, util_a);
struct cue3_t *b = get_cue(data, util_b);
u32 ticks;
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);
calc_metrics(data, m->interval);
}
/* Object defining this CMG. */
static struct cmg_t cmg3 = {
.cmg = 3,
.selected = false,
.found = 0,
.has_cmcb = true,
.default_column_ids = default_column_ids,
.get_values = &get_values,
.update_metrics = &update_metrics,
};
/* Add to CMG registry. */
static void __attribute__((constructor)) cmg3_ctr(void)
{
cmg_add(&cmg3);
}