mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
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>
318 lines
8.8 KiB
C
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);
|
|
}
|