mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
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:
committed by
Steffen Eiden
parent
38ea8fc3ee
commit
cb77faeae7
1
.gitignore
vendored
1
.gitignore
vendored
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
19
zconf/chp/chpstat/Makefile
Normal file
19
zconf/chp/chpstat/Makefile
Normal 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
739
zconf/chp/chpstat/chpstat.8
Normal 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
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
101
zconf/chp/chpstat/cmg.c
Normal 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
115
zconf/chp/chpstat/cmg.h
Normal 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
143
zconf/chp/chpstat/cmg1.c
Normal 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
343
zconf/chp/chpstat/cmg2.c
Normal 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
316
zconf/chp/chpstat/cmg3.c
Normal 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);
|
||||
}
|
||||
109
zconf/chp/chpstat/cmg_helper.c
Normal file
109
zconf/chp/chpstat/cmg_helper.c
Normal 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;
|
||||
}
|
||||
81
zconf/chp/chpstat/cmg_helper.h
Normal file
81
zconf/chp/chpstat/cmg_helper.h
Normal 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
430
zconf/chp/chpstat/column.c
Normal 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);
|
||||
}
|
||||
84
zconf/chp/chpstat/column.h
Normal file
84
zconf/chp/chpstat/column.h
Normal 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
227
zconf/chp/chpstat/key.c
Normal 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
89
zconf/chp/chpstat/key.h
Normal 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
20
zconf/chp/chpstat/misc.h
Normal 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 */
|
||||
Reference in New Issue
Block a user