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>
This commit is contained in:
Peter Oberparleiter
2024-02-05 14:40:25 +01:00
committed by Steffen Eiden
parent 38ea8fc3ee
commit cb77faeae7
17 changed files with 4974 additions and 1 deletions

1
.gitignore vendored
View File

@@ -92,6 +92,7 @@ vmcp/vmcp
vmur/vmur
zconf/chp/chchp
zconf/chp/lschp
zconf/chp/chpstat/chpstat
zconf/css/lscss
zconf/qeth/lsqeth
zconf/scm/lsscm

View File

@@ -1,6 +1,7 @@
include ../../common.mak
all: chchp lschp
all: chchp lschp chpstat
$(MAKE) -C chpstat all
libs = $(rootdir)/libutil/libutil.a
@@ -14,8 +15,10 @@ install: all
$(INSTALL) -d -m 755 $(DESTDIR)$(MANDIR)/man8
$(INSTALL) -m 644 -c chchp.8 $(DESTDIR)$(MANDIR)/man8
$(INSTALL) -m 644 -c lschp.8 $(DESTDIR)$(MANDIR)/man8
$(MAKE) -C chpstat install
clean:
rm -f *.o chchp lschp
$(MAKE) -C chpstat clean
.PHONY: all install clean

View File

@@ -0,0 +1,19 @@
include ../../../common.mak
all: chpstat
libs = $(rootdir)/libutil/libutil.a
chpstat: chpstat.o $(libs) column.o key.o cmg.o cmg_helper.o \
cmg1.o cmg2.o cmg3.o
install: all
$(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR)
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 chpstat $(DESTDIR)$(BINDIR)
$(INSTALL) -d -m 755 $(DESTDIR)$(MANDIR)/man8
$(INSTALL) -m 644 -c chpstat.8 $(DESTDIR)$(MANDIR)/man8
clean:
rm -f *.o chpstat
.PHONY: all install clean

739
zconf/chp/chpstat/chpstat.8 Normal file
View File

@@ -0,0 +1,739 @@
.\" Copyright 2024 IBM Corp.
.\" s390-tools is free software; you can redistribute it and/or modify
.\" it under the terms of the MIT license. See LICENSE for details.
.TH chpstat 8 "" s390-tools chpstat
.SH NAME
chpstat - Display channel-path statistics
.SH SYNOPSIS
.B chpstat
.RI [ OPTIONS ]
.RI [ ACTIONS ]
.RI [ CHPIDS ]
.SH DESCRIPTION
Use
.B chpstat
to view channel-path statistics such as utilization and throughput,
and to query and control the status of the channel-path statistics function.
.br
When run without further options, data for all channel-paths is displayed
repeatedly with a 5 second delay in table format. You can limit output to
specific channel-paths by listing the associated CHPIDs on the command line.
.br
Note: Channel-path statistics are only available on systems running in an
LPAR or DPM partition.
.br
.SS "Output options"
Without options,
.B chpstat
displays performance statistics data in table format. Use options
.BR \-\-columns ", " \-\-all " and " \-\-cmg
to change the list of columns to display.
.br
You can use option
.B \-\-format
to select a machine-readable list output format (JSON, key-value pairs, or
comma-separated values), and option
.B \-\-keys
to restrict output to only a specific set of data keys.
.br
Additional options can be used to display the raw source data used for
calculating channel-path statistics:
.br
.IP \(bu 3
.B Characteristics:
Static data describing base characteristics of a channel-path.
.br
Use option
.B \-\-chars
to view characteristics data.
.PP
.IP \(bu 3
.B Utilization:
Raw data about a channel-paths current utilization. This data is updated
regularly at model-dependent intervals that typically last a few seconds
(see key "interval").
Use option
.B \-\-util
to view raw utilization data.
.PP
.IP \(bu 3
.B Metrics:
Performance statistics derived from utilization and characteristics data.
.br
Note: Metrics data is calculated as averages over all utilization update
intervals that fall within the selected chpstat update interval.
.br
Use option
.B \-\-metrics
to view metrics data.
.PP
.SS Authorization
A special authorization setting needs to be enabled for a system to be able
to access channel-path statistics data.
.B Classic mode LPAR
.IP 1. 3
Logon on to the Hardware Management Console (HMC)
.PP
.IP 2. 3
Select the target LPAR
.PP
.IP 3. 3
Start "Customize Activation Profiles" HMC Task
.PP
.IP 4. 3
Enable "Security/Global performance data control" setting
.PP
Note: The LPAR needs to be deactivated/re-activated for the change to
become effective.
.B DPM Partition
.IP 1. 3
Logon on to the Hardware Management Console (HMC)
.PP
.IP 2. 3
Select the target partition
.PP
.IP 3. 3
Start "Partition Details" HMC Task
.PP
.IP 4. 3
Enable "Controls/Access global performance data" setting
.PP
.IP 5. 3
Press "Apply" - the change will be active immediately
.PP
.SS "Channel-Measurement Groups"
A Channel-Measurement Group (CMG) is a number associated with each channel-path
that determines the type of available statistics data for that channel-path.
.SH ACTIONS
.BR \-s ", " \-\-status
.br
.RS
Show channel-path statistics status
Possible status values are:
.IP \(bu 3
.B enabled:
Statistics facility is active
.PP
.IP \(bu 3
.B disabled:
Statistics facility is inactive
.PP
.IP \(bu 3
.B unsupported:
Statistics facility is not supported.
.br
Note that channel-path statistics are only available when running in LPAR or
DPM partition.
.PP
.RE
.BR \-e ", " \-\-enable
.br
.RS
Enable channel-path statistics
.br
After booting Linux, the channel-path statistics facility starts in disabled
state. Use option
.B \-\-enable
to enable it.
.RE
.BR \-d ", " \-\-disable
.br
.RS
Disable channel-path statistics
.RE
.BR \-l ", " \-\-list\-columns
.br
.RS
List available table columns
.br
Use this option to get a list of available table column names and associated
short description. A comma-separated list of column names can be used with
option
.B \-\-columns
to select the columns to display in table output format.
.RE
.BR \-L ", " \-\-list\-keys
.br
.RS
List available data keys
.br
Use this option to get a list of available keys that can be used with option
.B \-\-keys
to select data pairs to display in machine-readable output format.
.RE
.BR \-h ", " \-\-help
.br
.RS
Print usage information, then exit
.RE
.BR \-v ", " \-\-version
.br
.RS
Print version information, then exit
.RE
.SH OPTIONS
.BR \-n ", " \-\-iterations
.I NUM
.br
.RS
Display NUM reports before ending
.br
By default,
.B chpstat
shows output repeatedly until interrupted. Use option
.B \-\-iterations
to specify how many times output should be updated before exiting. A value of
0 indicates an unlimited number of iterations.
.RE
.BR \-i ", " \-\-interval
.I NUM
.br
.RS
Pause NUM seconds between display
.br
Use this option to specify the number of seconds to wait between output updates.
Valid values are between 1 and 2140.
Note: It is recommended to use interval values of at least the model-dependent
statistics update interval (see key "interval").
.br
.RE
.BR \-c ", " \-\-columns
.IR COL ,..
.br
.RS
Select table columns to show in table output format
.br
To get a list of available columns, use option
.BR \-\-list\-columns .
If a channel-path does not provide data for a selected column, the
corresponding table field is set to '\-'.
.RE
.BR \-k ", " \-\-keys
.IR KEY ,..
.br
.RS
Select keys to show in machine-readable output format
.br
Use this option to select the data to show in machine-readable output
format. To get a list of available keys, use option
.BR \-\-list\-keys .
If a channel-path does not provide data for a selected key, the
corresponding value is set to "".
.RE
.BR \-a ", " \-\-all
.br
.RS
Show all table columns and key data
.br
Use this option to select all supported columns and keys for output.
.RE
.BR \-\-scale
.I UNIT
.br
.RS
Scale BPS values by UNIT
Use this option to specify a value by which bytes-per-seconds (BPS) values -
such as read and write throughput - are scaled in table output format. Accepted
values are:
.IP \(bu 3
.B auto:
Scale automatically to fit value into each column. This is the default.
.PP
.IP \(bu 3
.IR number :
Scale by
.I number
.PP
.IP \(bu 3
.B K:
Scale by 1024 (KiB)
.PP
.IP \(bu 3
.B M:
Scale by 1,048,576 (MiB)
.PP
.IP \(bu 3
.B G:
Scale by 1,073,741,824 (GiB)
.PP
.IP \(bu 3
.B T:
Scale by 1,099,511,627,776 (TiB)
.PP
.RE
.BR \-\-cmg
.IR CMG ,..
.br
.RS
Show data for specified CMGs only
.br
Use this option to limit output to CHPIDs with the specified
Channel-Measurement-Groups (CMG). This option also selects table columns
suitable for the specified CMGs.
.RE
.BR \-\-format
.I FORMAT
.br
.RS
Show data in specified FORMAT
Use this option to show output in a machine-readable format.
.I FORMAT
can be either of:
.IP \(bu 3
.B json:
Single JavaScript Object Notation (JSON) data structure
Data for all iterations is formatted as one JSON data structure formatted
in multiple lines to make them more readable by humans.
.br
See section "OUTPUT FORMAT" for more details.
.br
.PP
.IP \(bu 3
.B json\-seq:
Sequence of JSON data structures
Data for each iteration is formatted as separate JSON data structure prefixed
with an ASCII Record Separator character (0x1e) and suffixed with an ASCII Line
Feed character (0x0a) in accordance with RFC7464.
.br
See section "OUTPUT FORMAT" for more details.
.br
.PP
.IP \(bu 3
.B pairs:
Textual key=value pairs
By default, keys have a prefix that makes them unique across one tool
invocation. This prefix can be removed by specifying option
.BR \-\-no\-prefix .
.PP
.IP \(bu 3
.B csv:
Comma-Separated-Value (CSV) list
.br
All values are quoted with double-quotes and separated by commas. The first
line of output contains a list of headings. Subsequent lines each represent
data for one CHPID in one iteration.
.PP
.RE
.BR \-\-chars
.br
.RS
List channel-path measurement characteristics
Use this option to display static data describing base characteristics of a
channel-path. This option implies a machine-readable format.
.RE
.BR \-\-util
.br
.RS
List unprocessed utilization data
Use this option to display raw channel-path utilization data that is updated
regularly by firmware at model-dependent intervals that typically last a few
seconds (see key "interval"). This option implies machine-readable output
format.
.RE
.BR \-\-metrics
.br
.RS
List performance metrics
.br
Use this option to display performance statistics data derived from utilization
and characteristics data. This option implies machine-readable output format.
.br
Note: Metrics data is calculated as averages over all utilization update
intervals that fall within the selected chpstat update interval.
.RE
.BR \-\-no\-ansi
.br
.RS
Do not use ANSI terminal codes in output
.br
When specified, this option suppresses the use of ANSI terminal control
characters in table output format. Such characters are used to clear the
screen, and to invert the colors for table heading display. Use this option
when an output terminal does not support these control characters.
.RE
.BR \-\-no\-prefix
.br
.RS
Hide key prefix in pairs output format
By default, keys that are shown in the "pairs" machine-readable output format
have a prefix that makes them unique across a tool invocation. Use option
.B \-\-no\-prefix
to remove this prefix.
.RE
.SH "OUTPUT FORMAT"
This section contains additional information for some of the supported
output formats.
.SS json
JSON output consists of a top-level object with the following properties
(key-value pairs):
.IP \(bu 3
.BR meta :
Tool meta-data including API level, version, host name, and time of invocation
.PP
.IP \(bu 3
.BR chpstat :
Channel-path statistics data
.PP
Note: For a given API level, the output format is guaranteed to remain
compatible, that is:
.IP \(bu 3
required child-objects are not removed
.PP
.IP \(bu 3
format and contents of existing objects and properties are retained
.PP
.IP \(bu 3
new child-objects and properties may be added
.PP
Channel-path statistics data is stored as an array of iteration objects under
the "chpstat" property in the top-level object.
Each iteration object contains a property named "channel_paths" the value of
which consists of an array of objects representing data for one channel-path
during one iteration. Objects for a single channel-path contain further
child-objects that group related properties together.
.br
The following object properties are required and will always be part of
JSON output:
.IP \(bu 3
For iteration objects: "iteration", "time", "time_epoch", and "channel_paths"
.br
.PP
.IP \(bu 3
For channel-path objects: "chpid", "type", "cmg", "shared"
.br
.PP
All other properties are optional and will be omitted from JSON output if the
associated value is unavailable. If option
.BR --all
is specified, unavailable properties are also listed as either empty strings or
negative values, depending on the value type.
Example JSON output for single iteration and channel-path with all properties:
.br
{
.br
"meta": {
.br
"api_level": 1,
.br
"version": "2.32.0",
.br
"host": "localhost",
.br
"time_epoch": 1714663282,
.br
"time": "2024-05-02 17:21:22+0200"
.br
},
.br
"chpstat": [
.br
{
.br
"iteration": 0,
.br
"time_epoch": 1714663282,
.br
"time": "2024-05-02 17:21:22+0200",
.br
"channel_paths": [
.br
{
.br
"chpid": "0.00",
.br
"type": 0,
.br
"cmg": 0,
.br
"shared": 0,
.br
"speed": "",
.br
"characteristics": {
.br
"max_bus_cycles": 0,
.br
"max_channel_work_units": 0,
.br
"max_write_data_units": 0,
.br
"max_read_data_units": 0,
.br
"data_unit_size": 0,
.br
"data_unit_size_cpc": 0,
.br
"msg_unit_size": 0,
.br
"msg_unit_size_cpc": 0,
.br
},
.br
"utilization": {
.br
"timestamp": 0,
.br
"bus_cycles_cpc": 0,
.br
"channel_work_units_cpc": 0,
.br
"channel_work_units": 0,
.br
"data_units_written_cpc": 0,
.br
"data_units_written": 0,
.br
"data_units_read_cpc": 0,
.br
"data_units_read": 0,
.br
"total_ficon_ops_cpc": 0,
.br
"total_deferred_ficon_ops_cpc": 0,
.br
"sum_ficon_ops_cpc": 0,
.br
"total_hpf_ops_cpc": 0,
.br
"total_deferred_hpf_ops_cpc": 0,
.br
"sum_hpf_ops_cpc": 0,
.br
"channel_path_busy_time_cpc": 0,
.br
"channel_path_busy_time": 0,
.br
"msg_units_sent": 0,
.br
"msg_units_sent_cpc": 0,
.br
"unsuccessful_attempts_to_send": 0,
.br
"unavailable_receive_buffers": 0,
.br
"unavailable_receive_buffers_cpc": 0,
.br
"data_units_sent": 0,
.br
"data_units_sent_cpc": 0,
.br
},
.br
"metrics": {
.br
"interval": 0.0,
.br
"util_total": 0.0,
.br
"util_part": 0.0,
.br
"util_bus": 0.0,
.br
"read_total": 0.0,
.br
"read_part": 0.0,
.br
"write_total": 0.0,
.br
"write_part": 0.0,
.br
"ficon_rate": 0.0,
.br
"ficon_active": 0.0,
.br
"ficon_defer": 0.0,
.br
"hpf_rate": 0.0,
.br
"hpf_active": 0.0,
.br
"hpf_defer": 0.0,
.br
"msg_rate_part": 0.0,
.br
"msg_rate_total": 0.0,
.br
"msg_size_part": 0.0,
.br
"msg_size_total": 0.0,
.br
"send_fail_part": 0.0,
.br
"rcv_fail_part": 0.0,
.br
"rcv_fail_total": 0.0,
.br
}
.br
}
.br
]
.br
}
.br
]
.br
}
.br
.SS json\-seq
The json\-seq output format is a variation of the JSON output format described
above with the following differences:
.IP \(bu 3
Output consists of a sequence of top-level JSON objects, each contained in
single line with no indentation
.br
.IP \(bu 3
Each top-level object is prefixed by an ASCII Record Separator character (0x1e)
and suffixed with an ASCII Line Feed character (0x0a) in accordance with
RFC7464
.br
.PP
.IP \(bu 3
The first object contains tool meta-data properties defined in the previous
section
.br
.PP
.IP \(bu 3
Subsequent objects each represent channel-path statistics data for one iteration
.br
.PP
.SH "EXIT CODES"
.TP
.B 0
Program finished successfully
.PP
.TP
.B 1
Usage error
.PP
.TP
.B 2
A run\-time error occurred
.PP
.SH EXAMPLES
Display current channel-path statistics status in JSON format:
.RS 4
$
.B chpstat \-\-status \-\-format json
.br
.RE
.PP
.
Determine the model-dependent update interval for CHPID 0.f0:
.RS 4
$
.B chpstat \-\-keys interval \-n 1 \-\-no\-prefix 0.f0 \-\-format pairs
.br
.RE
.PP
.
Collect partition write throughput statistics for 1 hour in CSV format:
.RS 4
$
.B chpstat \-n 60 \-i 60 \-\-format csv \-\-key time,chpid,write_part
.br
.SH "SEE ALSO"
.BR lschp "(8), " chchp (8)

2153
zconf/chp/chpstat/chpstat.c Normal file

File diff suppressed because it is too large Load Diff

101
zconf/chp/chpstat/cmg.c Normal file
View File

@@ -0,0 +1,101 @@
/*
* Registry for CMG types
*
* 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 <err.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include "cmg.h"
#include "misc.h"
#include "lib/util_libc.h"
static struct cmg_t **cmgs;
static unsigned int num_cmgs;
/* Register new CMG-object @cmg. */
void cmg_add(struct cmg_t *cmg)
{
unsigned int i;
for (i = 0; i < num_cmgs; i++) {
if (cmgs[i]->cmg == cmg->cmg)
errx(EXIT_RUNTIME, "Internal error: CMG %d registered "
"multiple times", cmg->cmg);
}
util_expand_array(&cmgs, &num_cmgs);
cmgs[num_cmgs - 1] = cmg;
}
void cmg_exit(void)
{
free(cmgs);
}
/* Return CMG-object for @cmg. */
struct cmg_t *cmg_get(int cmg)
{
unsigned int i;
for (i = 0; i < num_cmgs; i++) {
if (cmg == cmgs[i]->cmg)
return cmgs[i];
}
return NULL;
}
struct cmg_t *_cmg_get_by_index(unsigned int i)
{
return (i < num_cmgs) ? cmgs[i] : NULL;
}
void cmg_free_keys(char **keys)
{
int i;
if (!keys)
return;
for (i = 0; keys[i]; i++)
free(keys[i]);
free(keys);
}
void cmg_free_pairs(struct cmg_pair_t *pairs)
{
int i;
if (!pairs)
return;
for (i = 0; pairs[i].key; i++)
free(pairs[i].key);
free(pairs);
}
char **cmg_get_keys(struct cmg_t *cmg, int groups)
{
struct cmg_pair_t *pairs;
struct cmg_data_t data;
char **keys = NULL;
unsigned int i, num = 0;
/* Get key-value pairs for dummy data. */
memset(&data, 0, sizeof(data));
pairs = cmg->get_values(&data, groups);
/* Convert pair array to key array. */
for (i = 0; pairs[i].key; i++)
util_add_array(&keys, &num, util_strdup(pairs[i].key));
util_add_array(&keys, &num, NULL);
cmg_free_pairs(pairs);
return keys;
}

115
zconf/chp/chpstat/cmg.h Normal file
View File

@@ -0,0 +1,115 @@
/*
* Registry for CMG types
*
* 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.
*/
#ifndef CMG_H
#define CMG_H
#include <stdbool.h>
#include "lib/util_base.h"
#include "lib/util_list.h"
#include "column.h"
#define CMCB_SIZE (5 * sizeof(u32))
#define CUE_SIZE (8 * sizeof(u32))
#define EXT_CUE_SIZE (16 * sizeof(u32))
#define METRICS_SIZE (17 * sizeof(double))
/* Bit-width of CUE timestamp field. */
#define CUE_TS_WIDTH 24
typedef union {
struct {
u8 cuiv;
u32 timestamp:24;
} common __packed;
u8 data[CUE_SIZE];
} cue_t;
typedef u8 ext_cue_t[EXT_CUE_SIZE];
typedef u8 cmcb_t[CMCB_SIZE];
typedef u8 metrics_t[METRICS_SIZE];
struct util_t {
cue_t cue;
ext_cue_t ext_cue;
bool extended;
};
/* CMG-specific CHPID data. */
struct cmg_data_t {
cmcb_t cmcb;
struct util_t util_a;
struct util_t util_b;
metrics_t metrics;
};
enum cmg_unit_t {
CMG_NUMBER,
CMG_PERCENT,
CMG_BPS,
};
enum cmg_value_type_t {
CMG_U32,
CMG_U64,
CMG_FLOAT,
};
struct cmg_pair_t {
/* Key. */
char *key;
enum column_id_t col;
/* Value. */
bool valid;
enum cmg_unit_t unit;
enum cmg_value_type_t type;
union {
u32 value_u32;
u64 value_u64;
double value_double;
};
};
struct cmg_t {
int cmg;
/* Flag indicating whether this CMG was selected on the command line. */
bool selected;
/* Counter indicating number of CHPIDs found with this CMG. */
int found;
/* Indicator whether this CMG requires a CMCB. */
const bool has_cmcb;
/* Array of default column IDs to be displayed for this CMG. */
const enum column_id_t *default_column_ids;
/* Return array of key-value pairs. */
struct cmg_pair_t *(*get_values)(struct cmg_data_t *data, int groups);
/* Update the metrics found in @data. */
void (*update_metrics)(struct cmg_data_t *data);
};
/* Iterate over all supported CMG data types. */
#define cmg_for_each(c) \
for (unsigned int __i = 0; ((c) = _cmg_get_by_index(__i)); __i++)
void cmg_add(struct cmg_t *cmg);
struct cmg_t *cmg_get(int cmg);
struct cmg_t *_cmg_get_by_index(unsigned int i);
void cmg_exit(void);
void cmg_free_keys(char **keys);
void cmg_free_pairs(struct cmg_pair_t *pairs);
char **cmg_get_keys(struct cmg_t *cmg, int groups);
#endif /* CMG_H */

143
zconf/chp/chpstat/cmg1.c Normal file
View File

@@ -0,0 +1,143 @@
/*
* 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);
}

343
zconf/chp/chpstat/cmg2.c Normal file
View File

@@ -0,0 +1,343 @@
/*
* Support for CMG 2 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 cmcb2_t *)&((d)->cmcb))
#define get_cue(d, x) ((struct cue2_t *)&((d)->x.cue))
#define get_ext_cue(d, x) ((struct ext_cue2_t *)&((d)->x.ext_cue))
#define get_metrics(d) ((struct metrics2_t *)&((d)->metrics))
/* CMG 2 format Channel-Measurement-Characteristics Block (CMCB). */
struct cmcb2_t {
u32 max_bus_cycles;
u32 max_channel_work_units;
u32 max_write_data_units;
u32 max_read_data_units;
u32 data_unit_size;
} __packed;
STATIC_ASSERT(sizeof(struct cmcb2_t) <= sizeof(cmcb_t));
/* CMG 2 format Channel-Utilization-Entry (CUE). */
struct cue2_t {
u8 cuiv;
u32 timestamp:24;
u32 bus_cycles_cpc;
u32 channel_work_units_cpc;
u32 channel_work_units;
u32 data_units_written_cpc;
u32 data_units_written;
u32 data_units_read_cpc;
u32 data_units_read;
} __packed;
STATIC_ASSERT(sizeof(struct cue2_t) <= sizeof(cue_t));
/* CMG 2 format Extended Channel-Utilization-Entry. */
struct ext_cue2_t {
u32 total_ficon_ops_cpc;
u32 total_deferred_ficon_ops_cpc;
u64 sum_ficon_ops_cpc;
u32 total_hpf_ops_cpc;
u32 total_deferred_hpf_ops_cpc;
u64 sum_hpf_ops_cpc;
} __packed;
STATIC_ASSERT(sizeof(struct ext_cue2_t) <= sizeof(ext_cue_t));
/* Metrics based on CMG 2 format CUEs. */
struct metrics2_t {
double interval;
double util_total;
double util_part;
double util_bus;
double read_total;
double read_part;
double write_total;
double write_part;
/* Extended CUE metrics. */
double ficon_rate;
double ficon_active;
double ficon_defer;
double hpf_rate;
double hpf_active;
double hpf_defer;
};
STATIC_ASSERT(sizeof(struct metrics2_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,
COL_UTIL_BUS,
COL_READ_PART,
COL_READ_TOTAL,
COL_WRITE_PART,
COL_WRITE_TOTAL,
COL_FICON_RATE,
COL_FICON_ACTIVE,
COL_FICON_DEFER,
COL_HPF_RATE,
COL_HPF_ACTIVE,
COL_HPF_DEFER,
/* End of list. */
COL_END
};
static void pr_chars(struct cmg_pair_t **a, unsigned int *n,
struct cmg_data_t *data)
{
struct cmcb2_t *cmcb = get_cmcb(data);
pr_u32(a, n, cmcb, max_bus_cycles);
pr_u32(a, n, cmcb, max_channel_work_units);
pr_u32(a, n, cmcb, max_write_data_units);
pr_u32(a, n, cmcb, max_read_data_units);
pr_u32(a, n, cmcb, data_unit_size);
}
static void pr_cue(struct cmg_pair_t **a, unsigned int *n,
struct cmg_data_t *data)
{
struct cue2_t *cue = get_cue(data, util_b);
pr_u32(a, n, cue, timestamp);
pr_cue_u32(a, n, cue, bus_cycles_cpc);
pr_cue_u32(a, n, cue, channel_work_units_cpc);
pr_cue_u32(a, n, cue, channel_work_units);
pr_cue_u32(a, n, cue, data_units_written_cpc);
pr_cue_u32(a, n, cue, data_units_written);
pr_cue_u32(a, n, cue, data_units_read_cpc);
pr_cue_u32(a, n, cue, data_units_read);
}
static void pr_ext_cue(struct cmg_pair_t **a, unsigned int *n,
struct cmg_data_t *data)
{
struct ext_cue2_t *ext_cue = get_ext_cue(data, util_b);
bool v = data->util_b.extended;
pr_cond_u32(a, n, v, ext_cue, total_ficon_ops_cpc);
pr_cond_u32(a, n, v, ext_cue, total_deferred_ficon_ops_cpc);
pr_cond_u64(a, n, v, ext_cue, sum_ficon_ops_cpc);
pr_cond_u32(a, n, v, ext_cue, total_hpf_ops_cpc);
pr_cond_u32(a, n, v, ext_cue, total_deferred_hpf_ops_cpc);
pr_cond_u64(a, n, v, ext_cue, sum_hpf_ops_cpc);
}
static void pr_metrics(struct cmg_pair_t **a, unsigned int *n,
struct cmg_data_t *data)
{
struct metrics2_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);
pr_metric(a, n, metrics, util_bus, CMG_PERCENT, COL_UTIL_BUS);
pr_metric(a, n, metrics, read_total, CMG_BPS, COL_READ_TOTAL);
pr_metric(a, n, metrics, read_part, CMG_BPS, COL_READ_PART);
pr_metric(a, n, metrics, write_total, CMG_BPS, COL_WRITE_TOTAL);
pr_metric(a, n, metrics, write_part, CMG_BPS, COL_WRITE_PART);
pr_metric(a, n, metrics, ficon_rate, CMG_NUMBER, COL_FICON_RATE);
pr_metric(a, n, metrics, ficon_active, CMG_NUMBER, COL_FICON_ACTIVE);
pr_metric(a, n, metrics, ficon_defer, CMG_NUMBER, COL_FICON_DEFER);
pr_metric(a, n, metrics, hpf_rate, CMG_NUMBER, COL_HPF_RATE);
pr_metric(a, n, metrics, hpf_active, CMG_NUMBER, COL_HPF_ACTIVE);
pr_metric(a, n, metrics, hpf_defer, CMG_NUMBER, COL_HPF_DEFER);
}
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);
pr_ext_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 metrics2_t *m)
{
m->interval = METRICS_INIT;
m->util_total = METRICS_INIT;
m->util_part = METRICS_INIT;
m->util_bus = METRICS_INIT;
m->read_total = METRICS_INIT;
m->read_part = METRICS_INIT;
m->write_total = METRICS_INIT;
m->write_part = METRICS_INIT;
/* Extended CUE metrics. */
m->ficon_rate = METRICS_INIT;
m->ficon_active = METRICS_INIT;
m->ficon_defer = METRICS_INIT;
m->hpf_rate = METRICS_INIT;
m->hpf_active = METRICS_INIT;
m->hpf_defer = METRICS_INIT;
}
/* Calculate metrics base on CMG 2 CUEs. */
static void calc_metrics(struct cmg_data_t *data, double seconds)
{
struct metrics2_t *m = get_metrics(data);
struct cmcb2_t *cmcb = get_cmcb(data);
struct cue2_t *a = get_cue(data, util_a);
struct cue2_t *b = get_cue(data, util_b);
double delta, max;
/* util_total = 100.0 * work_units_cpc / max_work_units */
if (cue_valid2(a, b, channel_work_units_cpc)) {
delta = field_delta(channel_work_units_cpc, a, b);
max = cmcb->max_channel_work_units * seconds;
if (max != 0.0)
m->util_total = 100.0 * delta / max;
}
/* util_part = 100.0 * work_units / max_work_units */
if (cue_valid2(a, b, channel_work_units)) {
delta = field_delta(channel_work_units, a, b);
max = cmcb->max_channel_work_units * seconds;
if (max != 0.0)
m->util_part = 100.0 * delta / max;
}
/* util_bus = 100.0 * bus_cycles_cpc / max_bus_cycles */
if (cue_valid2(a, b, bus_cycles_cpc)) {
delta = field_delta(bus_cycles_cpc, a, b);
max = cmcb->max_bus_cycles * seconds;
if (max != 0.0)
m->util_bus = 100.0 * delta / max;
}
/* read_total = data_units_read_cpc * unit_size / seconds */
if (cue_valid2(a, b, data_units_read_cpc)) {
delta = field_delta(data_units_read_cpc, a, b);
m->read_total = (double)delta * cmcb->data_unit_size / seconds;
}
/* read_part = data_units_read * unit_size / seconds */
if (cue_valid2(a, b, data_units_read)) {
delta = field_delta(data_units_read, a, b);
m->read_part = (double)delta * cmcb->data_unit_size / seconds;
}
/* write_total = data_units_written_cpc * unit_size / seconds */
if (cue_valid2(a, b, data_units_written_cpc)) {
delta = field_delta(data_units_written_cpc, a, b);
m->write_total = (double)delta * cmcb->data_unit_size /
seconds;
}
/* write_part = data_units_written * unit_size / seconds */
if (cue_valid2(a, b, data_units_written)) {
delta = field_delta(data_units_written, a, b);
m->write_part = (double)delta * cmcb->data_unit_size / seconds;
}
}
/* Calculate metrics base on CMG 2 extended CUEs. */
static void calc_ext_metrics(struct cmg_data_t *data, double seconds)
{
struct metrics2_t *m = get_metrics(data);
struct ext_cue2_t *a = get_ext_cue(data, util_a);
struct ext_cue2_t *b = get_ext_cue(data, util_b);
double delta, delta2;
/* These metrics require extended CUEs. */
if (!data->util_a.extended || !data->util_b.extended)
return;
/* ficon_rate = total_ficon_ops_cpc / seconds */
delta = field_delta(total_ficon_ops_cpc, a, b);
m->ficon_rate = delta / seconds;
/* ficon_active = sum_ficon_ops_cpc / total_ficon_ops_cpc */
if (delta != 0.0) {
delta2 = (double)field_delta64(sum_ficon_ops_cpc, a, b);
m->ficon_active = delta2 / delta;
} else {
m->ficon_active = 0.0;
}
/* ficon_defer = total_deferred_ficon_ops_cpc / seconds */
delta = field_delta(total_deferred_ficon_ops_cpc, a, b);
m->ficon_defer = delta / seconds;
/* hpf_rate = total_hpf_ops_cpc / seconds */
delta = field_delta(total_hpf_ops_cpc, a, b);
m->hpf_rate = delta / seconds;
/* hpf_active = sum_hpf_ops_cpc / total_hpf_ops_cpc */
if (delta != 0.0) {
delta2 = (double)field_delta64(sum_hpf_ops_cpc, a, b);
m->hpf_active = delta2 / delta;
} else {
m->hpf_active = 0.0;
}
/* hpf_defer = total_deferred_hpf_ops_cpc / seconds */
delta = field_delta(total_deferred_hpf_ops_cpc, a, b);
m->hpf_defer = delta / seconds;
}
/* Recalculate metrics in @data. */
static void update_metrics(struct cmg_data_t *data)
{
struct metrics2_t *m = get_metrics(data);
struct cue2_t *a = get_cue(data, util_a);
struct cue2_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);
calc_ext_metrics(data, m->interval);
}
/* Object defining this CMG. */
static struct cmg_t cmg2 = {
.cmg = 2,
.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)) cmg2_ctr(void)
{
cmg_add(&cmg2);
}

316
zconf/chp/chpstat/cmg3.c Normal file
View File

@@ -0,0 +1,316 @@
/*
* 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 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);
}

View File

@@ -0,0 +1,109 @@
/*
* Helper functions for implementing CMG types
*
* 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 <limits.h>
#include <stdio.h>
#include <string.h>
#include "cmg_helper.h"
#include "cmg.h"
#include "lib/util_base.h"
#include "lib/util_libc.h"
/* Convert time ticks to seconds. */
double tick_to_s(u32 t)
{
return CM_TICK * t;
}
/* Get 32 bit delta of counters @curr and @last. Take into account that both
* counters might have wrapped. Counter width is @width bits. */
u32 get_delta(u32 last, u32 curr, int width)
{
if (curr >= last)
return curr - last;
/* Counter wrapped - add [last..max] + [0..curr] */
return (u32)((u64)(1ULL << width) - last + curr);
}
/* Get 64 bit delta of counters @curr and @last. Take into account that both
* counters might have wrapped. Counter width is @width bits. */
u64 get_delta64(u64 last, u64 curr, int width)
{
u64 max;
if (curr >= last)
return curr - last;
/* Counter wrapped - add [last..max] + [0..curr] */
max = (width == 64) ? ULLONG_MAX : (1ULL << width) - 1;
return (max - last + curr) + 1;
}
/* Check whether word at @offset is valid according to @cuiv. */
bool _cue_valid(u8 cuiv, int offset)
{
int num;
u8 mask;
num = offset / (int)sizeof(u32);
mask = 0x80 >> num;
return (cuiv & mask) != 0;
}
static struct cmg_pair_t *add_pair(struct cmg_pair_t **array, unsigned int *num,
bool valid, const char *key,
enum column_id_t col, enum cmg_unit_t unit,
enum cmg_value_type_t type)
{
struct cmg_pair_t pair;
memset(&pair, 0, sizeof(pair));
pair.valid = valid;
pair.key = util_strdup(key);
pair.col = col;
pair.unit = unit;
pair.type = type;
util_add_array(array, num, pair);
return &((*array)[*num - 1]);
}
void _pr_u32(struct cmg_pair_t **array, unsigned int *num, bool valid,
const char *key, enum column_id_t col, enum cmg_unit_t unit,
u32 value)
{
struct cmg_pair_t *pair;
pair = add_pair(array, num, valid, key, col, unit, CMG_U32);
pair->value_u32 = value;
}
void _pr_u64(struct cmg_pair_t **array, unsigned int *num, bool valid,
const char *key, enum column_id_t col, enum cmg_unit_t unit,
u64 value)
{
struct cmg_pair_t *pair;
pair = add_pair(array, num, valid, key, col, unit, CMG_U64);
pair->value_u64 = value;
}
void _pr_double(struct cmg_pair_t **array, unsigned int *num, bool valid,
const char *key, enum column_id_t col, enum cmg_unit_t unit,
double value)
{
struct cmg_pair_t *pair;
pair = add_pair(array, num, valid, key, col, unit, CMG_FLOAT);
pair->value_double = value;
}

View File

@@ -0,0 +1,81 @@
/*
* Helper functions for implementing CMG types
*
* 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.
*/
#ifndef CMG_HELPER_H
#define CMG_HELPER_H
#include <stdbool.h>
#include "cmg.h"
#include "column.h"
#include "lib/util_base.h"
/* Duration of channel-path measurement timestamp tick. */
#define CM_TICK ((double)0.000128)
/* Initialization value for metrics fields. */
#define METRICS_INIT -1.0
/* Check whether member @field of struct @s is valid according to s->cuiv. */
#define cue_valid(s, field) \
(_cue_valid((s)->cuiv, offsetof(__typeof__(*s), field)))
/* Check whether member @field of struct @a and @b is valid according to
* a->cuiv and b->cuiv. */
#define cue_valid2(a, b, field) \
((_cue_valid((a)->cuiv, offsetof(__typeof__(*a), field))) && \
(_cue_valid((b)->cuiv, offsetof(__typeof__(*b), field))))
#define field_delta(field, a, b) \
get_delta((a)->field, (b)->field, 32)
#define field_delta64(field, a, b) \
get_delta64((a)->field, (b)->field, 64)
#define pr_u32(a, n, s, field) \
_pr_u32((a), (n), true, STRINGIFY(field), COL_NONE, CMG_NUMBER, \
(s)->field)
#define pr_cond_u32(a, n, v, s, field) \
_pr_u32((a), (n), (v), STRINGIFY(field), COL_NONE, CMG_NUMBER, \
(s)->field)
#define pr_cue_u32(a, n, s, field) \
pr_cond_u32(a, n, cue_valid(s, field), s, field)
#define pr_u64(a, n, s, field) \
_pr_u64((a), (n), true, STRINGIFY(field), COL_NONE, CMG_NUMBER, \
(s)->field)
#define pr_cond_u64(a, n, v, s, field) \
_pr_u64((a), (n), (v), STRINGIFY(field), COL_NONE, CMG_NUMBER, \
(s)->field)
/* pr_metric(array_ptr, num_ptr, struct, field, unit, column_id) */
#define pr_metric(a, n, s, field, u, c) \
_pr_double((a), (n), ((s)->field != METRICS_INIT), STRINGIFY(field), \
c, u, (s)->field)
double tick_to_s(u32 t);
u32 get_delta(u32 last, u32 curr, int width);
u64 get_delta64(u64 last, u64 curr, int width);
bool _cue_valid(u8 cuiv, int offset);
void _pr_u32(struct cmg_pair_t **a, unsigned int *n, bool valid,
const char *key, enum column_id_t col, enum cmg_unit_t unit,
u32 value);
void _pr_u64(struct cmg_pair_t **a, unsigned int *n, bool valid,
const char *key, enum column_id_t col, enum cmg_unit_t unit,
u64 value);
void _pr_double(struct cmg_pair_t **a, unsigned int *n, bool valid,
const char *key, enum column_id_t col, enum cmg_unit_t unit,
double value);
#endif /* CMG_HELPER_H */

430
zconf/chp/chpstat/column.c Normal file
View File

@@ -0,0 +1,430 @@
/*
* Registry for supported table columns
*
* 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 <string.h>
#include <stdbool.h>
#include "column.h"
#include "lib/util_base.h"
#include "lib/util_libc.h"
/*
* Use group header for adjacent columns of the same type to improve
* readability.
*
* Single header: UTIL Group header: UTILIZATION
* PART PART TOTAL
*/
#define HDR1_CHP_SINGLE ""
#define HDR1_CHP_GROUP "CHANNEL-PATH"
#define HDR1_UTIL_SINGLE "UTIL"
#define HDR1_UTIL_GROUP "UTILIZATION(%)"
#define HDR1_READ_SINGLE "READ"
#define HDR1_READ_GROUP "READ"
#define HDR1_WRITE_SINGLE "WRITE"
#define HDR1_WRITE_GROUP "WRITE"
#define HDR1_FICON_SINGLE "FICON"
#define HDR1_FICON_GROUP "FICON-OPS"
#define HDR1_HPF_SINGLE "HPF"
#define HDR1_HPF_GROUP "HPF-OPS"
#define HDR1_MSGR_SINGLE "RATE"
#define HDR1_MSGR_GROUP "MSG-RATE"
#define HDR1_MSGSZ_SINGLE "SIZE"
#define HDR1_MSGSZ_GROUP "MSG-SIZE"
#define HDR1_MSG_RCVF_SINGLE "RFAIL"
#define HDR1_MSG_RCVF_GROUP "RECEIVE-FAIL"
static struct column_t columns[] = {
{
COL_CHPID,
"chpid",
COL_OTHER,
"Channel-path ID",
"ID",
HDR1_CHP_SINGLE,
HDR1_CHP_GROUP,
0,
},
{
COL_TYPE,
"type",
COL_OTHER,
"Channel-path type",
"TYP",
HDR1_CHP_SINGLE,
HDR1_CHP_GROUP,
0,
},
{
COL_CMG,
"cmg",
COL_OTHER,
"Channel-measurement group",
"CMG",
HDR1_CHP_SINGLE,
HDR1_CHP_GROUP,
0,
},
{
COL_SPEED,
"speed",
COL_OTHER,
"Operational speed",
"SPEED",
HDR1_CHP_SINGLE,
HDR1_CHP_GROUP,
0,
},
{
COL_SHARED,
"shared",
COL_OTHER,
"Shared channel-path indicator",
"SHR",
HDR1_CHP_SINGLE,
HDR1_CHP_GROUP,
0,
},
{
COL_INTERVAL,
"interval",
COL_OTHER,
"Cumulated statistics update interval",
"INT",
HDR1_CHP_SINGLE,
HDR1_CHP_GROUP,
0,
},
{
COL_UTIL_PART,
"util_part",
COL_PERCENT,
"Partition channel-path utilization in %",
"PART",
HDR1_UTIL_SINGLE,
HDR1_UTIL_GROUP,
0,
},
{
COL_UTIL_TOTAL,
"util_total",
COL_PERCENT,
"Total channel-path utilization in %",
"TOTAL",
HDR1_UTIL_SINGLE,
HDR1_UTIL_GROUP,
0,
},
{
COL_UTIL_BUS,
"util_bus",
COL_PERCENT,
"Bus utilization in %",
"BUS",
HDR1_UTIL_SINGLE,
HDR1_UTIL_GROUP,
0,
},
{
COL_READ_PART,
"read_part",
COL_BPS,
"Partition read throughput in B/s",
"PART",
HDR1_READ_SINGLE,
HDR1_READ_GROUP,
0,
},
{
COL_READ_TOTAL,
"read_total",
COL_BPS,
"Total read throughput in B/s",
"TOTAL",
HDR1_READ_SINGLE,
HDR1_READ_GROUP,
0,
},
{
COL_WRITE_PART,
"write_part",
COL_BPS,
"Partition write throughput in B/s",
"PART",
HDR1_WRITE_SINGLE,
HDR1_WRITE_GROUP,
0,
},
{
COL_WRITE_TOTAL,
"write_total",
COL_BPS,
"Total write throughput in B/s",
"TOTAL",
HDR1_WRITE_SINGLE,
HDR1_WRITE_GROUP,
0,
},
{
COL_FICON_RATE,
"ficon_rate",
COL_NUMBER,
"FICON operations per second",
"RATE",
HDR1_FICON_SINGLE,
HDR1_FICON_GROUP,
0,
},
{
COL_FICON_ACTIVE,
"ficon_active",
COL_NUMBER,
"Avg. concurrently active FICON operations",
"ACTV",
HDR1_FICON_SINGLE,
HDR1_FICON_GROUP,
0,
},
{
COL_FICON_DEFER,
"ficon_defer",
COL_NUMBER,
"Deferred FICON operations per second",
"DEFER",
HDR1_FICON_SINGLE,
HDR1_FICON_GROUP,
0,
},
{
COL_HPF_RATE,
"hpf_rate",
COL_NUMBER,
"HPF operations per second",
"RATE",
HDR1_HPF_SINGLE,
HDR1_HPF_GROUP,
0,
},
{
COL_HPF_ACTIVE,
"hpf_active",
COL_NUMBER,
"Avg. concurrently active HPF operations",
"ACTV",
HDR1_HPF_SINGLE,
HDR1_HPF_GROUP,
0,
},
{
COL_HPF_DEFER,
"hpf_defer",
COL_NUMBER,
"Deferred HPF operations per second",
"DEFER",
HDR1_HPF_SINGLE,
HDR1_HPF_GROUP,
0,
},
{
COL_MSG_RATE_PART,
"msg_rate_part",
COL_NUMBER,
"Partition message send rate per second",
"PART",
HDR1_MSGR_SINGLE,
HDR1_MSGR_GROUP,
0,
},
{
COL_MSG_RATE_TOTAL,
"msg_rate_total",
COL_NUMBER,
"Total message send rate per second",
"TOTAL",
HDR1_MSGR_SINGLE,
HDR1_MSGR_GROUP,
0,
},
{
COL_MSG_SIZE_PART,
"msg_size_part",
COL_NUMBER,
"Partition avg. send message size",
"PART",
HDR1_MSGSZ_SINGLE,
HDR1_MSGSZ_GROUP,
0,
},
{
COL_MSG_SIZE_TOTAL,
"msg_size_total",
COL_NUMBER,
"Total avg. send message size",
"TOTAL",
HDR1_MSGSZ_SINGLE,
HDR1_MSGSZ_GROUP,
0,
},
{
COL_SEND_FAIL_PART,
"send_fail_part",
COL_NUMBER,
"Partition message send fail rate per second",
"PART",
"SNDFAIL",
"SNDFAIL",
0,
},
{
COL_RCV_FAIL_PART,
"rcv_fail_part",
COL_NUMBER,
"Partition message receive fail rate per second",
"PART",
HDR1_MSG_RCVF_SINGLE,
HDR1_MSG_RCVF_GROUP,
0,
},
{
COL_RCV_FAIL_TOTAL,
"rcv_fail_total",
COL_NUMBER,
"Total message receive fail rate per second",
"TOTAL",
HDR1_MSG_RCVF_SINGLE,
HDR1_MSG_RCVF_GROUP,
0,
},
};
/* Columns selected by default. */
static const int default_columns[] = {
COL_CHPID,
COL_TYPE,
COL_CMG,
COL_SHARED,
COL_SPEED,
COL_UTIL_PART,
COL_UTIL_TOTAL,
COL_UTIL_BUS,
COL_READ_PART,
COL_READ_TOTAL,
COL_WRITE_PART,
COL_WRITE_TOTAL,
/* End of list. */
COL_END
};
static struct column_t **selected_cols;
static unsigned int num_selected_cols;
static bool hdr_updated;
struct column_t *column_get_by_index(unsigned int i, bool selected)
{
if (selected)
return i < num_selected_cols ? selected_cols[i] : NULL;
return i < ARRAY_SIZE(columns) ? &columns[i] : NULL;
}
/*
* Retrieve column object by @name or %NULL if column does not exist.
*/
struct column_t *column_get_by_name(const char *name)
{
struct column_t *col;
column_for_each(col) {
if (strcasecmp(col->name, name) == 0)
return col;
}
return NULL;
}
/*
* Add the specified column to the list of selected columns. Discard duplicate
* selections of the same column.
*/
void column_select(struct column_t *col)
{
struct column_t *c;
/* Silently filter out double selection of columns to prevent problems
* with non-unique util_rec name fields. */
column_for_each_selected(c) {
if (c == col)
return;
}
util_add_array(&selected_cols, &num_selected_cols, col);
}
void column_select_id_list(const int *ids)
{
struct column_t *col;
int i;
for (i = 0; ids[i] != COL_END; i++) {
column_for_each(col) {
if (col->id == ids[i]) {
column_select(col);
break;
}
}
}
}
void column_select_default(void)
{
column_select_id_list(default_columns);
}
void column_select_all(void)
{
struct column_t *col;
column_for_each(col)
column_select(col);
}
void column_update_bps_suffix(bool auto_scale, char suffix_char)
{
struct column_t *col;
char *str;
if (auto_scale)
util_asprintf(&str, "(B/s)");
else if (suffix_char)
util_asprintf(&str, "(%ciB/s)", suffix_char);
else
str = util_strdup("(*)");
column_for_each(col) {
if (col->unit != COL_BPS) {
col->hdr1_group = util_strdup(col->hdr1_group);
continue;
}
util_asprintf(&col->hdr1_group, "%s%s", col->hdr1_group, str);
}
free(str);
hdr_updated = true;
}
void column_exit(void)
{
struct column_t *col;
if (hdr_updated) {
column_for_each(col)
free(col->hdr1_group);
}
free(selected_cols);
}

View File

@@ -0,0 +1,84 @@
/*
* Registry for supported table columns
*
* 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.
*/
#ifndef COLUMN_H
#define COLUMN_H
#include <stdbool.h>
enum column_id_t {
COL_NONE = -1,
COL_CHPID,
COL_TYPE,
COL_CMG,
COL_SHARED,
COL_SPEED,
COL_INTERVAL,
COL_TIMESTAMP,
COL_UTIL_PART,
COL_UTIL_TOTAL,
COL_UTIL_BUS,
COL_READ_PART,
COL_READ_TOTAL,
COL_WRITE_PART,
COL_WRITE_TOTAL,
COL_FICON_RATE,
COL_FICON_ACTIVE,
COL_FICON_DEFER,
COL_HPF_RATE,
COL_HPF_ACTIVE,
COL_HPF_DEFER,
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,
/* Special value indicating no column. */
COL_END
};
enum col_unit_t {
COL_OTHER,
COL_NUMBER,
COL_PERCENT,
COL_BPS,
};
struct column_t {
enum column_id_t id;
const char *name;
enum col_unit_t unit;
const char *desc;
const char *hdr2;
const char *hdr1_single;
char *hdr1_group;
unsigned int width;
};
#define column_for_each(c) \
for (unsigned int __i = 0; ((c) = column_get_by_index(__i, false)); \
__i++)
#define column_for_each_selected(c) \
for (unsigned int __i = 0; ((c) = column_get_by_index(__i, true)); \
__i++)
struct column_t *column_get_by_index(unsigned int i, bool selected);
struct column_t *column_get_by_name(const char *name);
void column_select(struct column_t *col);
void column_select_id_list(const int *ids);
void column_select_default(void);
void column_select_all(void);
void column_update_bps_suffix(bool auto_scale, char suffix_char);
void column_exit(void);
#endif /* COLUMN_H */

227
zconf/chp/chpstat/key.c Normal file
View File

@@ -0,0 +1,227 @@
/*
* Registry for supported data keys
*
* 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 "key.h"
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include "cmg.h"
#include "lib/util_libc.h"
#include "lib/zt_common.h"
static struct key_t *keys;
static unsigned int num_keys;
static struct key_t **selected_keys;
static unsigned int num_selected_keys;
#define GET_CMG_MASK(c) ((u32)(1 << (c)))
#define CMG_ALL_MASK ((u32)0xffffffff)
#define IS_CMG_MASK(c, n) ((c) & GET_CMG_MASK(n))
static char *mask_to_cmg_str(u32 mask)
{
char *str;
int i;
if (mask == CMG_ALL_MASK)
return util_strdup("all");
str = util_strdup("");
for (i = 1; i <= 32; i++) {
if (!IS_CMG_MASK(mask, i))
continue;
if (*str)
util_concatf(&str, ",");
util_concatf(&str, "%d", i);
}
return str;
}
const char *key_group_to_str(enum key_group_t group)
{
switch (group) {
case KEY_GRP_META:
return "meta";
case KEY_GRP_ITERATION:
return "iteration";
case KEY_GRP_CHP:
return "channel_path";
case KEY_GRP_CHARS:
return "characteristics";
case KEY_GRP_UTIL:
return "utilization";
case KEY_GRP_METRICS:
return "metrics";
default:
return "";
}
}
static void add_key(const char *name, enum key_group_t group, bool found,
u32 cmg_mask)
{
struct key_t *key;
key = key_get_by_name(name);
if (!key) {
util_expand_array(&keys, &num_keys);
key = &keys[num_keys - 1];
key->name = util_strdup(name);
key->group = group;
}
key->found |= found;
key->cmg_mask |= cmg_mask;
free(key->cmg_str);
key->cmg_str = mask_to_cmg_str(key->cmg_mask);
}
static void add_generic_keys(void)
{
add_key(KEY_META_API_LEVEL, KEY_GRP_META, true, CMG_ALL_MASK);
add_key(KEY_META_VERSION, KEY_GRP_META, true, CMG_ALL_MASK);
add_key(KEY_META_HOST, KEY_GRP_META, true, CMG_ALL_MASK);
add_key(KEY_META_TIME, KEY_GRP_META, true, CMG_ALL_MASK);
add_key(KEY_META_TIME_EPOCH, KEY_GRP_META, true, CMG_ALL_MASK);
add_key(KEY_ITERATION, KEY_GRP_ITERATION, true, CMG_ALL_MASK);
add_key(KEY_TIME, KEY_GRP_ITERATION, true, CMG_ALL_MASK);
add_key(KEY_TIME_EPOCH, KEY_GRP_ITERATION, true, CMG_ALL_MASK);
add_key(KEY_CHPID, KEY_GRP_CHP, true, CMG_ALL_MASK);
add_key(KEY_TYPE, KEY_GRP_CHP, true, CMG_ALL_MASK);
add_key(KEY_CMG, KEY_GRP_CHP, true, CMG_ALL_MASK);
add_key(KEY_SHARED, KEY_GRP_CHP, true, CMG_ALL_MASK);
add_key(KEY_SPEED, KEY_GRP_CHP, true, CMG_ALL_MASK);
}
static void add_cmg_keys(bool all)
{
enum key_group_t groups[] = { KEY_GRP_UTIL, KEY_GRP_CHARS,
KEY_GRP_METRICS };
unsigned int i, j;
struct cmg_t *cmg;
char **cmg_keys;
cmg_for_each(cmg) {
for (i = 0; i < ARRAY_SIZE(groups); i++) {
cmg_keys = cmg_get_keys(cmg, groups[i]);
for (j = 0; cmg_keys[j]; j++) {
add_key(cmg_keys[j], groups[i],
cmg->found || all,
GET_CMG_MASK(cmg->cmg));
}
cmg_free_keys(cmg_keys);
}
}
}
void key_init(bool all)
{
add_generic_keys();
add_cmg_keys(all);
}
void key_exit(void)
{
unsigned int i;
for (i = 0; i < num_keys; i++) {
free(keys[i].name);
free(keys[i].cmg_str);
}
free(keys);
free(selected_keys);
}
static int cmp_keys(const void *a, const void *b)
{
const struct key_t * const *a_key = a;
const struct key_t * const *b_key = b;
return strcmp((*a_key)->name, (*b_key)->name);
}
void key_sort_selected(void)
{
qsort(selected_keys, num_selected_keys, sizeof(struct key_t *),
cmp_keys);
}
struct key_t *key_get_by_index(unsigned int i, bool selected)
{
if (selected)
return i < num_selected_keys ? selected_keys[i] : NULL;
return i < num_keys ? &keys[i] : NULL;
}
struct key_t *key_get_by_name(const char *name)
{
struct key_t *key;
key_for_each(key) {
if (strcmp(key->name, name) == 0)
return key;
}
return NULL;
}
void key_select(struct key_t *key)
{
struct key_t *k;
/* Prevent duplicates. */
key_for_each_selected(k) {
if (k == key)
return;
}
util_add_array(&selected_keys, &num_selected_keys, key);
}
void key_select_by_groups(int groups, bool found)
{
struct key_t *key;
key_for_each(key) {
if (found && !key->found)
continue;
if (!(groups & (int)key->group))
continue;
key_select(key);
}
}
void key_select_by_cmg(int cmg)
{
struct key_t *key;
key_for_each(key) {
if (IS_CMG_MASK(key->cmg_mask, cmg))
key_select(key);
}
}
void key_select_all(void)
{
struct key_t *key;
key_for_each(key)
key_select(key);
}
int key_get_selected_groups(void)
{
struct key_t *key;
int groups = 0;
key_for_each_selected(key)
groups |= (int)key->group;
return groups;
}

89
zconf/chp/chpstat/key.h Normal file
View File

@@ -0,0 +1,89 @@
/*
* Registry for supported data keys
*
* 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.
*/
#ifndef KEY_H
#define KEY_H
#include <stdbool.h>
#include "lib/zt_common.h"
#define KEY_META_API_LEVEL "meta.api_level"
#define KEY_META_VERSION "meta.version"
#define KEY_META_HOST "meta.host"
#define KEY_META_TIME "meta.time"
#define KEY_META_TIME_EPOCH "meta.time_epoch"
#define KEY_ITERATION "iteration"
#define KEY_TIME "time"
#define KEY_TIME_EPOCH "time_epoch"
#define KEY_CHPID "chpid"
#define KEY_TYPE "type"
#define KEY_CMG "cmg"
#define KEY_SHARED "shared"
#define KEY_SPEED "speed"
/**
* enum util_fmt_t - Key group identifiers.
* @KEY_GRP_META - Tool-related meta data
* @KEY_GRP_ITERATION - Iteration-related data
* @KEY_GRP_CHP - Channel-path related data
* @KEY_GRP_CHARS - Group for channel-patch measurement characteristics data
* @KEY_GRP_UTIL - Group for unprocessed utilization data
* @KEY_GRP_METRICS - Group for performance metrics
*/
enum key_group_t {
KEY_GRP_META = (1 << 0),
KEY_GRP_ITERATION = (1 << 1),
KEY_GRP_CHP = (1 << 2),
KEY_GRP_CHARS = (1 << 3),
KEY_GRP_UTIL = (1 << 4),
KEY_GRP_METRICS = (1 << 5),
};
#define KEY_GRP_ALL (KEY_GRP_META | KEY_GRP_ITERATION | KEY_GRP_CHP | \
KEY_GRP_CHARS | KEY_GRP_UTIL | KEY_GRP_METRICS)
/**
* struct key_t - A single data key
* @name: Key name
* @cmg_mask: List (bitmask) of CMGs for which this key is defined
* @cmg_str: List (text) of CMGs for which this key is defined
* @group: Key group this key belongs to
* @found: Flag indicating whether key is provided by CMGS of selected CHPIDs
*/
struct key_t {
char *name;
u32 cmg_mask;
char *cmg_str;
enum key_group_t group;
bool found;
};
#define key_for_each(c) \
for (unsigned int __i = 0; ((c) = key_get_by_index(__i, false)); \
__i++)
#define key_for_each_selected(c) \
for (unsigned int __i = 0; ((c) = key_get_by_index(__i, true)); \
__i++)
const char *key_group_to_str(enum key_group_t group);
struct key_t *key_get_by_index(unsigned int i, bool selected);
struct key_t *key_get_by_name(const char *name);
void key_select(struct key_t *key);
void key_select_by_groups(int groups, bool found);
void key_select_by_cmg(int cmg);
void key_select_all(void);
void key_sort_selected(void);
int key_get_selected_groups(void);
void key_init(bool all);
void key_exit(void);
struct key_t *_get_key(int i);
#endif /* KEY_H */

20
zconf/chp/chpstat/misc.h Normal file
View File

@@ -0,0 +1,20 @@
/*
* Miscellaneous definitions
*
* 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.
*/
#ifndef MISC_H
#define MISC_H
/* Program exit codes. */
enum exit_code_t {
EXIT_OK = 0, /* Program finished successfully */
EXIT_USAGE = 1, /* Usage error */
EXIT_RUNTIME = 2, /* Run-time error */
};
#endif /* MISC_H */