/* * 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); }