Files
s390-tools/zconf/zcrypt/zcryptstats.c
Szabina Korbai a5af5bcf70 zcrypt: Implement zsh and bash autocompletion
Add generation of shell autocompletion scripts
to chzcrypt, lszcrypt and zcryptstats.

Acked-by: Steffen Eiden <seiden@linux.ibm.com>
Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Szabina Korbai <szkorbai@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-04-30 08:51:04 +02:00

2336 lines
54 KiB
C

/*
* zcryptstats - Show usage statistics of IBM Crypto Express adapters
*
* Copyright IBM Corp. 2019, 2022
*
* 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 <ctype.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <locale.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <signal.h>
#include <time.h>
#include <asm/chsc.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/utsname.h>
#include "lib/util_base.h"
#include "lib/util_file.h"
#include "lib/util_libc.h"
#include "lib/util_opt.h"
#include "lib/util_path.h"
#include "lib/util_prg.h"
#include "lib/util_rec.h"
#include "lib/util_scandir.h"
#include "lib/zt_common.h"
#include "zcryptstats_cli.h"
#ifndef offsetof
#define offsetof(type, member) ((size_t) &((type *)0)->member)
#endif
#ifndef offsetofend
#define offsetofend(type, member) \
(offsetof(type, member) + sizeof(((type *)0)->member))
#endif
#define SYSFS_DEVICES_AP_PATH "devices/ap"
#define SYSFS_DEVICES_CARD "devices/ap/card%02x"
#define SYSFS_DEVICES_APQN "devices/ap/card%02x/%02x.%04x"
#define SYSFS_DEVICES_CARD_ONLINE "devices/ap/card%02x/online"
#define SYSFS_DEVICES_APQN_ONLINE "devices/ap/card%02x/%02x.%04x/online"
#define CHSC_DEVICE "/dev/chsc"
#define NUM_CARDS_OLD 64
#define NUM_CARDS 256
#define NUM_DOMAINS 256
#define MASK_WORD_BITS (sizeof(uint32_t) * 8)
#define MASK_WORD_NO(n) ((n) / MASK_WORD_BITS)
#define MASK_BIT_IN_WORD(n) ((n) % MASK_WORD_BITS)
#define MASK_BIT(n) (0x80000000 >> MASK_BIT_IN_WORD(n))
struct chsc_apdn {
uint8_t ap_index;
uint8_t domain_index;
} __packed;
struct chsc_scdmd_request {
struct chsc_header header;
struct chsc_apdn first_drid;
struct chsc_apdn last_drid;
uint32_t s:1;
uint32_t reserved1:31;
uint32_t reserved2;
uint32_t apsm[8];
uint32_t dsm[8];
} __packed;
struct chsc_scdmd_response {
struct chsc_header header;
uint32_t reserved1;
uint16_t p:1;
uint16_t reserved2:15;
struct chsc_apdn crid;
uint32_t reserved3;
} __packed;
struct chsc_scdmd_area {
struct chsc_scdmd_request request;
struct chsc_scdmd_response response;
uint8_t response_data[CHSC_SIZE - sizeof(struct chsc_scdmd_request) -
sizeof(struct chsc_scdmd_response)];
} __packed;
struct chsc_scmd_request {
struct chsc_header header;
uint8_t reserved1;
uint8_t zeros1:6;
uint8_t one:1;
uint8_t zero:1;
uint8_t fcs;
uint8_t lcs;
uint32_t reserved2;
uint32_t reserved3;
} __packed;
struct chsc_scmd_response {
struct chsc_header header;
uint32_t reserved1;
uint32_t p:1;
uint32_t reserved2:31;
uint32_t reserved3;
} __packed;
struct chsc_scmd_area {
struct chsc_scmd_request request;
struct chsc_scmd_response response;
uint8_t response_data[CHSC_SIZE - sizeof(struct chsc_scmd_request) -
sizeof(struct chsc_scmd_response)];
} __packed;
struct chsc_cmb_header {
uint8_t reserved1;
uint8_t ct; /* AP_DEVICE_TYPE_xxx values */
uint8_t format;
uint8_t ax;
float s;
uint32_t v;
uint8_t dx;
uint8_t mt;
uint16_t l4;
} __packed;
struct chsc_cmb_entry {
u64 t;
u64 c;
} __packed;
struct chsc_cmb_area {
struct chsc_cmb_header header;
struct chsc_cmb_entry entries[32];
} __packed;
#define CRYPTO_TYPE_PCICC 3
#define CRYPTO_TYPE_PCICA 4
#define CRYPTO_TYPE_PCIXCC 5
#define CRYPTO_TYPE_CEX2A 6
#define CRYPTO_TYPE_CEX2C 7
#define CRYPTO_TYPE_CEX3A 8
#define CRYPTO_TYPE_CEX3C 9
#define CRYPTO_TYPE_CEX4S 10
#define CRYPTO_TYPE_CEX5S 11
#define CRYPTO_TYPE_CEX6S 12
#define CRYPTO_TYPE_CEX7S 13
#define CRYPTO_TYPE_CEX8S 14
#define CRYPTO_TYPE_TOLERATION CRYPTO_TYPE_CEX8S
struct crypto_counter {
const char *name;
bool is_totals;
};
struct crypto_mode {
const char *name;
char indicatior_char;
unsigned int num_counters;
const struct crypto_counter *counters;
};
struct crypto_type {
const char *name;
unsigned int num_modes;
const struct crypto_mode *modes;
};
#define NUM_COPROC_COUNTERS 2
static const struct crypto_counter counter_coproc[NUM_COPROC_COUNTERS] = {
{ .name = "All", .is_totals = true },
{ .name = "RSA Key-gen" },
};
#define NUM_ACCEL_COUNTERS 6
static const struct crypto_counter counter_accel[NUM_ACCEL_COUNTERS] = {
{ .name = "RSA 1024 ME" },
{ .name = "RSA 2048 ME" },
{ .name = "RSA 1024 CRT" },
{ .name = "RSA 2048 CRT" },
{ .name = "RSA 4096 ME" },
{ .name = "RSA 4096 CTR" },
};
#define NUM_EP11_COUNTERS 5
static const struct crypto_counter counter_ep11[NUM_EP11_COUNTERS] = {
{ .name = "Asym. Slow" },
{ .name = "Asym. Fast" },
{ .name = "Symm. Partial" },
{ .name = "Symm. Complete" },
{ .name = "Asym. Key-gen" },
};
#define NUM_PCICA_COUNTERS 20
static const struct crypto_counter counter_pcica[NUM_PCICA_COUNTERS] = {
{ .name = "RSA 1024 ME (E0)" },
{ .name = "RSA 2048 ME (E0)" },
{ .name = "RSA 1024 CRT (E0)" },
{ .name = "RSA 2048 CRT (E0)" },
{ .name = "RSA 1024 ME (E1)" },
{ .name = "RSA 2048 ME (E1)" },
{ .name = "RSA 1024 CRT (E1)" },
{ .name = "RSA 2048 CRT (E1)" },
{ .name = "RSA 1024 ME (E2)" },
{ .name = "RSA 2048 ME (E2)" },
{ .name = "RSA 1024 CRT (E2)" },
{ .name = "RSA 2048 CRT (E2)" },
{ .name = "RSA 1024 ME (E3)" },
{ .name = "RSA 2048 ME (E3)" },
{ .name = "RSA 1024 CRT (E3)" },
{ .name = "RSA 2048 CRT (E3)" },
{ .name = "RSA 1024 ME (E4)" },
{ .name = "RSA 2048 ME (E4)" },
{ .name = "RSA 1024 CRT (E4)" },
{ .name = "RSA 2048 CRT (E4)" },
};
#define NUM_COPROC_MODES 1
static const struct crypto_mode mode_coproc[1] = {
{ .num_counters = NUM_COPROC_COUNTERS,
.counters = counter_coproc},
};
#define NUM_ACCEL_MODES 1
static const struct crypto_mode mode_accel[1] = {
{ .num_counters = NUM_ACCEL_COUNTERS,
.counters = counter_accel },
};
#define NUM_PCICA_MODES 1
static const struct crypto_mode mode_pcica[1] = {
{ .num_counters = NUM_PCICA_COUNTERS,
.counters = counter_pcica },
};
#define NUM_CEX45678_MODES 11
static const struct crypto_mode mode_cex45678[NUM_CEX45678_MODES] = {
{ 0 },
{ 0 },
{ 0 },
{ 0 },
{ 0 },
{ 0 },
{ 0 },
{ 0 },
{ .name = "Accelerator", .indicatior_char = 'A',
.num_counters = NUM_ACCEL_COUNTERS,
.counters = counter_accel },
{ .name = "CCA co-processor", .indicatior_char = 'C',
.num_counters = NUM_COPROC_COUNTERS,
.counters = counter_coproc},
{ .name = "EP11 co-processor", .indicatior_char = 'P',
.num_counters = NUM_EP11_COUNTERS,
.counters = counter_ep11 },
};
#define NUM_CRYPTO_TYPES 15
static const struct crypto_type crypto_types[NUM_CRYPTO_TYPES] = {
{ 0 },
{ 0 },
{ 0 },
{ .name = "PCICC", .num_modes = NUM_COPROC_MODES,
.modes = mode_coproc },
{ .name = "PCICA", .num_modes = NUM_PCICA_MODES,
.modes = mode_pcica },
{ .name = "PCIXCC", .num_modes = NUM_COPROC_MODES,
.modes = mode_coproc},
{ .name = "CEX2A", .num_modes = NUM_ACCEL_MODES,
.modes = mode_accel },
{ .name = "CEX2C", .num_modes = NUM_COPROC_MODES,
.modes = mode_coproc },
{ .name = "CEX3A", .num_modes = NUM_ACCEL_MODES,
.modes = mode_accel },
{ .name = "CEX3C", .num_modes = NUM_COPROC_MODES,
.modes = mode_coproc },
{ .name = "CEX4", .num_modes = NUM_CEX45678_MODES,
.modes = mode_cex45678 },
{ .name = "CEX5", .num_modes = NUM_CEX45678_MODES,
.modes = mode_cex45678 },
{ .name = "CEX6", .num_modes = NUM_CEX45678_MODES,
.modes = mode_cex45678 },
{ .name = "CEX7", .num_modes = NUM_CEX45678_MODES,
.modes = mode_cex45678 },
{ .name = "CEX8", .num_modes = NUM_CEX45678_MODES,
.modes = mode_cex45678 },
};
struct type_mapping {
uint8_t from_type;
uint8_t from_mode;
uint8_t to_type;
uint8_t to_mode;
struct type_mapping *next;
};
struct device_selection {
int card;
int domain; /* -1 if not specified */
struct device_selection *next;
};
struct interval_data {
bool current_valid;
bool previous_valid;
struct chsc_cmb_area current;
struct chsc_cmb_area previous;
};
struct card_data {
struct interval_data data;
struct interval_data *domains[NUM_DOMAINS];
};
struct interval_values {
u64 count;
double rate;
double utilization;
double duration;
};
struct print_func {
int (*print_initialize)(void);
int (*print_terminate)(void);
int (*print_header)(void);
int (*print_footer)(void);
int (*print_interval_header)(unsigned long interval_count,
const char *timestamp);
int (*print_interval_footer)(void);
int (*print_device_header)(bool is_apqn, uint8_t card, uint8_t domain,
const char *type, const char *timestamp);
int (*print_device_footer)(void);
int (*print_counter_data)(bool is_apqn, uint8_t card, uint8_t domain,
const char *type, const char *timestamp,
const char *name,
struct interval_values *vals);
int (*print_counter_separator)(void);
};
#define pr_call(func) g.print_funcs->func == NULL ? 0 : g.print_funcs->func
static int default_print_initialize(void);
static int default_print_terminate(void);
static int default_print_header(void);
static int default_print_footer(void);
static int default_print_interval_header(unsigned long interval_count,
const char *timestamp);
static int default_print_device_header(bool is_apqn, uint8_t card,
uint8_t domain, const char *type,
const char *timestamp);
static int default_print_device_footer(void);
static int default_print_counter_data(bool is_apqn, uint8_t card,
uint8_t domain, const char *type,
const char *timestamp, const char *name,
struct interval_values *vals);
static int default_print_counter_separator(void);
static const struct print_func default_print = {
.print_initialize = default_print_initialize,
.print_terminate = default_print_terminate,
.print_header = default_print_header,
.print_footer = default_print_footer,
.print_interval_header = default_print_interval_header,
.print_device_header = default_print_device_header,
.print_device_footer = default_print_device_footer,
.print_counter_data = default_print_counter_data,
.print_counter_separator = default_print_counter_separator,
};
static int json_print_initialize(void);
static int json_print_header(void);
static int json_print_footer(void);
static int json_print_interval_header(unsigned long interval_count,
const char *timestamp);
static int json_print_interval_footer(void);
static int json_print_device_header(bool is_apqn, uint8_t card,
uint8_t domain, const char *type,
const char *timestamp);
static int json_print_device_footer(void);
static int json_print_counter_data(bool is_apqn, uint8_t card, uint8_t domain,
const char *type, const char *timestamp,
const char *name,
struct interval_values *vals);
static const struct print_func json_print = {
.print_initialize = json_print_initialize,
.print_header = json_print_header,
.print_footer = json_print_footer,
.print_interval_header = json_print_interval_header,
.print_interval_footer = json_print_interval_footer,
.print_device_header = json_print_device_header,
.print_device_footer = json_print_device_footer,
.print_counter_data = json_print_counter_data,
};
static int table_print_initialize(void);
static int table_print_terminate(void);
static int table_print_header(void);
static int table_print_interval_footer(void);
static int table_print_counter_data(bool is_apqn, uint8_t card, uint8_t domain,
const char *type, const char *timestamp,
const char *name,
struct interval_values *vals);
static const struct print_func table_print = {
.print_initialize = table_print_initialize,
.print_terminate = table_print_terminate,
.print_header = table_print_header,
.print_interval_footer = table_print_interval_footer,
.print_counter_data = table_print_counter_data,
};
static int csv_print_initialize(void);
static int csv_print_terminate(void);
static int csv_print_header(void);
static int csv_print_counter_data(bool is_apqn, uint8_t card, uint8_t domain,
const char *type, const char *timestamp,
const char *name,
struct interval_values *vals);
static const struct print_func csv_print = {
.print_initialize = csv_print_initialize,
.print_terminate = csv_print_terminate,
.print_header = csv_print_header,
.print_counter_data = csv_print_counter_data,
};
/*
* Program configuration
*/
static const struct util_prg prg = {
.desc = "Display usage statistics of IBM Crypto Express adapters",
.args = "[DEVICE_IDS]",
.copyright_vec = {
{
.owner = "IBM Corp.",
.pub_first = 2019,
.pub_last = 2019,
},
UTIL_PRG_COPYRIGHT_END
}
};
/*
* Global variables for program options
*/
static struct zcryptstats_globals {
long interval;
unsigned long count;
bool no_totals;
bool only_totals;
bool no_apqn;
char *map_type;
char **device_ids;
bool all;
bool only_online;
bool verbose;
int chsc_fd;
uint8_t max_card_used;
uint32_t card_mask[8];
uint8_t min_card;
uint8_t max_card;
uint32_t domain_mask[8];
uint8_t min_domain;
uint8_t max_domain;
struct device_selection *dev_selection;
struct type_mapping *type_mapping;
struct card_data *cards[NUM_CARDS];
const struct print_func *print_funcs;
struct util_rec *device_rec;
struct util_rec *counter_rec;
bool first_device;
bool first_counter;
} g = {
.interval = 10,
.chsc_fd = -1,
.print_funcs = &default_print,
};
static volatile bool quit;
#define pr_verbose(fmt...) do { \
if (g.verbose) \
warnx(fmt); \
} while (0)
/*
* Describe adapter ids
*/
static void print_adapter_id_help(void)
{
printf("\n");
printf("DEVICE_IDS\n");
util_print_indented(" List of cryptographic device IDs separated by "
"blanks for which statistics are displayed. "
"DEVICE_ID can either be a card device ID "
"('<card-id>') or a queue device ID (<card-id>."
"<domain-id>'). To filter all devices by domain, "
"provide '.<domain-id>'. If no IDs are given, "
"statistics are displayed for all available "
"devices.", 2);
printf("\n");
printf("EXAMPLE:\n");
util_print_indented(" Display statistics for all cryptographic "
"devices with card ID '02.", 2);
printf(" # zcryptstats 02\n");
printf("\n");
util_print_indented(" Display statistics for cryptographic devices "
"with card ID '02' and domain ID '0005'.", 2);
printf(" # zcryptstats 02.0005\n");
printf("\n");
}
static struct type_mapping *find_type_mapping(uint8_t from_type,
uint8_t from_mode)
{
struct type_mapping *map = g.type_mapping;
while (map != NULL) {
if (map->from_type == from_type && map->from_mode == from_mode)
return map;
map = map->next;
}
return NULL;
}
/*
* Get the name of the card for a crypto type and mode.
* Note: This function might return the address of a static string variable.
* It is only valid until this function is called again.
*/
static const char *get_card_name(uint8_t type, uint8_t mode)
{
const struct crypto_type *ct;
const struct crypto_mode *m;
static char temp_name[250];
struct type_mapping *map;
map = find_type_mapping(type, mode);
if (map != NULL) {
type = map->to_type;
mode = map->to_mode;
} else if (type >= NUM_CRYPTO_TYPES) {
type = CRYPTO_TYPE_TOLERATION;
}
if (type >= NUM_CRYPTO_TYPES)
return "UNKNOWN ADAPTER TYPE";
ct = &crypto_types[type];
if (ct->name == NULL || ct->modes == NULL || ct->num_modes == 0)
return "UNKNOWN ADAPTER TYPE";
if (mode >= ct->num_modes)
return ct->name;
m = &ct->modes[mode];
snprintf(temp_name, sizeof(temp_name) - 1, "%s%c (%s)", ct->name,
m->indicatior_char, m->name != NULL ? m->name : "");
return temp_name;
}
/*
* Get the name of a counter for a crypto type, mode and index.
* Note: This function might return the address of a static string variable.
* It is only valid until this function is called again.
*/
static const char *get_counter_name(uint8_t type, uint8_t mode, uint8_t index)
{
const struct crypto_type *ct;
const struct crypto_mode *m;
static char temp_name[250];
struct type_mapping *map;
map = find_type_mapping(type, mode);
if (map != NULL) {
type = map->to_type;
mode = map->to_mode;
} else if (type >= NUM_CRYPTO_TYPES) {
type = CRYPTO_TYPE_TOLERATION;
}
if (type >= NUM_CRYPTO_TYPES)
goto generic;
ct = &crypto_types[type];
if (ct->name == NULL || ct->modes == NULL || ct->num_modes == 0)
goto generic;
if (mode >= ct->num_modes)
goto generic;
m = &ct->modes[mode];
if (m->counters == NULL || m->num_counters == 0)
goto generic;
if (index >= m->num_counters)
goto generic;
return m->counters[index].name;
generic:
snprintf(temp_name, sizeof(temp_name) - 1, "COUNTER %u", index);
return temp_name;
}
/*
* Returns true if a counter for a crypto type, mode and index represents the
* total number of operations.
*/
static bool is_counter_totals(uint8_t type, uint8_t mode, uint8_t index)
{
const struct crypto_type *ct;
const struct crypto_mode *m;
struct type_mapping *map;
map = find_type_mapping(type, mode);
if (map != NULL) {
type = map->to_type;
mode = map->to_mode;
} else if (type >= NUM_CRYPTO_TYPES) {
type = CRYPTO_TYPE_TOLERATION;
}
if (type >= NUM_CRYPTO_TYPES)
return false;
ct = &crypto_types[type];
if (ct->name == NULL || ct->modes == NULL || ct->num_modes == 0)
return false;
if (mode >= ct->num_modes)
return false;
m = &ct->modes[mode];
if (m->counters == NULL || m->num_counters == 0)
return false;
if (index >= m->num_counters)
return false;
return m->counters[index].is_totals;
}
/*
* Returns true if the card is available
*/
static bool is_card_available(uint8_t card)
{
char *path;
bool ret;
path = util_path_sysfs(SYSFS_DEVICES_CARD, card);
ret = util_path_is_dir(path);
free(path);
return ret;
}
/*
* Returns true if the APQN is available
*/
static bool is_apqn_available(uint8_t card, uint8_t domain)
{
char *path;
bool ret;
path = util_path_sysfs(SYSFS_DEVICES_APQN, card, card, domain);
ret = util_path_is_dir(path);
free(path);
return ret;
}
/*
* Returns true if the card is online
*/
static bool is_card_online(uint8_t card)
{
unsigned long online;
char *path;
int rc;
path = util_path_sysfs(SYSFS_DEVICES_CARD_ONLINE, card);
rc = util_file_read_ul(&online, 10, path);
free(path);
return rc == 0 && online != 0;
}
/*
* Returns true if the APQN is online
*/
static bool is_apqn_online(uint8_t card, uint8_t domain)
{
unsigned long online;
char *path;
int rc;
path = util_path_sysfs(SYSFS_DEVICES_APQN_ONLINE, card, card, domain);
rc = util_file_read_ul(&online, 10, path);
free(path);
if (rc != 0)
return false;
return online != 0;
}
/*
* Updates the APQNs data with data for the current interval.
*/
static void update_apqn_data(uint8_t card, uint8_t domain,
struct chsc_cmb_area *cmb, size_t cmb_len)
{
struct card_data *cd = g.cards[card];
struct interval_data *dd;
if (cd == NULL) {
cd = util_malloc(sizeof(struct card_data));
memset(cd, 0, sizeof(struct card_data));
g.cards[card] = cd;
pr_verbose("Card %02x added", card);
}
dd = cd->domains[domain];
if (dd == NULL) {
dd = util_malloc(sizeof(struct interval_data));
memset(dd, 0, sizeof(struct interval_data));
cd->domains[domain] = dd;
pr_verbose("APQN %02x.%04x added", card, domain);
} else {
if (!dd->current_valid) {
dd->previous = dd->current;
dd->previous_valid = true;
}
}
memset(&dd->current, 0, sizeof(struct chsc_cmb_area));
memcpy(&dd->current, cmb, cmb_len);
dd->current_valid = true;
}
/*
* Updates the card's data with data for the current interval.
*/
static void update_card_data(uint8_t card, struct chsc_cmb_area *cmb,
size_t cmb_len)
{
struct card_data *cd = g.cards[card];
if (cd == NULL) {
cd = util_malloc(sizeof(struct card_data));
memset(cd, 0, sizeof(struct card_data));
g.cards[card] = cd;
pr_verbose("Card %02x added", card);
} else {
if (!cd->data.current_valid) {
cd->data.previous = cd->data.current;
cd->data.previous_valid = true;
}
};
memset(&cd->data.current, 0, sizeof(struct chsc_cmb_area));
memcpy(&cd->data.current, cmb, cmb_len);
cd->data.current_valid = true;
}
/*
* Frees the interval data of a card
*/
static void free_card_data(struct card_data *cd)
{
struct interval_data *dd;
int domain;
if (cd == NULL)
return;
for (domain = 0; domain < NUM_DOMAINS; domain++) {
dd = cd->domains[domain];
if (dd == NULL)
continue;
free(dd);
cd->domains[domain] = NULL;
}
free(cd);
}
/*
* Frees the interval data
*/
static void free_interval_data(void)
{
struct card_data *cd;
int card;
for (card = 0; card < NUM_CARDS; card++) {
cd = g.cards[card];
if (cd == NULL)
continue;
free_card_data(cd);
g.cards[card] = NULL;
}
}
/*
* Returns the highest card index used by the system.
* If there is a card with an index > 0x3f, then the returned number is 0xff,
* else 0x3f is returned.
*/
static int get_max_card_index(uint8_t *max_index)
{
struct dirent **dev_vec = NULL;
int i, count, card, rc = 0;
char *path;
path = util_path_sysfs(SYSFS_DEVICES_AP_PATH);
if (!util_path_is_dir(path)) {
warnx("Crypto device driver is not available");
rc = -ENODEV;
goto out;
}
count = util_scandir(&dev_vec, NULL, path, "card[0-9a-fA-F]+");
if (count < 1) {
warnx("No crypto card devices found");
rc = -ENODEV;
goto out;
}
*max_index = NUM_CARDS_OLD - 1;
for (i = 0; i < count; i++) {
if (sscanf(dev_vec[i]->d_name, "card%x", &card) != 1)
continue;
if (card >= NUM_CARDS_OLD)
*max_index = NUM_CARDS - 1;
}
pr_verbose("Max card index used: %u", *max_index);
out:
free(path);
if (dev_vec != NULL)
free(dev_vec);
return rc;
}
/*
* Returns the size of the CMB. The size is either contained in l4 field
* of the cmb, or a fix length dependent on the crypto type, if l4 is zero.
*/
static size_t get_cmb_length(struct chsc_cmb_area *cmb)
{
size_t len = cmb->header.l4;
if (len != 0)
return len;
switch (cmb->header.ct) {
case CRYPTO_TYPE_PCICC:
case CRYPTO_TYPE_PCIXCC:
case CRYPTO_TYPE_CEX2C:
case CRYPTO_TYPE_CEX3C:
return 64;
case CRYPTO_TYPE_PCICA:
return 336;
case CRYPTO_TYPE_CEX2A:
case CRYPTO_TYPE_CEX3A:
return 80;
default:
warnx("Zero length value in CMB");
return 0;
}
}
/*
* Return true if the device is in the device selection list and mask
*/
static bool filter_device(uint8_t card, uint8_t domain, bool is_apqn)
{
struct device_selection *dev;
bool found;
/* Check for selection mask */
if ((g.card_mask[MASK_WORD_NO(card)] &
MASK_BIT(card)) == 0) {
pr_verbose("Skipping card %02x (mask)", card);
return false;
}
if (is_apqn) {
if ((g.domain_mask[MASK_WORD_NO(domain)] &
MASK_BIT(domain)) == 0) {
pr_verbose("Skipping APQN %02x.%04x (mask)", card,
domain);
return false;
}
}
/* Check for device selection list */
if (g.dev_selection != NULL) {
dev = g.dev_selection;
found = false;
while (dev != NULL) {
if (is_apqn == false) {
/* Its a card */
if (card == (dev->card >= 0 ? dev->card :
card)) {
found = true;
break;
}
} else {
/* Its an APQN */
if (card == (dev->card >= 0 ? dev->card :
card) &&
domain == (dev->domain >= 0 ? dev->domain :
domain)) {
found = true;
break;
}
}
dev = dev->next;
}
if (!found) {
if (is_apqn)
pr_verbose("Skipping APQN %02x.%04x "
"(selection)", card, domain);
else
pr_verbose("Skipping card %02x (selection)",
card);
return false;
}
}
if (g.all)
return true;
/* Check if card/APQN is available in the system (SYSFS) */
if (!is_card_available(card)) {
pr_verbose("Skipping card %02x (not available)", card);
return false;
}
if (is_apqn && !is_apqn_available(card, domain)) {
pr_verbose("Skipping APQN %02x.%04x (not available)",
card, domain);
return false;
}
if (g.only_online) {
/* Check if card/APQN is online */
if (!is_card_online(card)) {
pr_verbose("Skipping card %02x (not online)", card);
return false;
}
if (is_apqn && !is_apqn_online(card, domain)) {
pr_verbose("Skipping APQN %02x.%04x (not online)",
card, domain);
return false;
}
}
return true;
}
/*
* Process a crypto measurement block.
* Passes back the actual length of the CMB processed and its card number.
* Returns -ENODEV when the CMB is skipped.
*/
static int process_cmb(struct chsc_cmb_area *cmb, size_t size, size_t *length,
uint8_t *card)
{
size_t len;
len = get_cmb_length(cmb);
if (len == 0)
return -EINVAL;
if (len > size) {
warnx("Length value in CMB exceeds size of CMB");
return -EINVAL;
}
if (card != NULL)
*card = cmb->header.ax;
if (length != NULL)
*length = len;
if (filter_device(cmb->header.ax, cmb->header.dx,
cmb->header.format == 1) == false)
return -ENODEV;
if (cmb->header.format == 1)
update_apqn_data(cmb->header.ax, cmb->header.dx, cmb, len);
else
update_card_data(cmb->header.ax, cmb, len);
return 0;
}
/*
* Translate the CHSC response code to an error (0 or negative errno)
*/
static int chsc_error_from_response(int response)
{
if (response != 0x0001)
pr_verbose("CHSC Response code: %04x", response);
switch (response) {
case 0x0001:
return 0;
case 0x0002:
return -EOPNOTSUPP;
case 0x0003:
case 0x0006:
case 0x0007:
case 0x0008:
case 0x000a:
case 0x0103:
case 0x0104:
return -EINVAL;
case 0x0004:
return -EOPNOTSUPP;
case 0x000b:
return -EBUSY;
case 0x0102:
return -ENOMEM;
case 0x0105:
return -EACCES;
case 0x0100:
case 0x0107:
return -ENODEV;
default:
return -EIO;
}
}
/*
* Process the APQN measurement data and extract the CMBs
*/
static int process_apqn_measurement_data(struct chsc_scdmd_area *scdmd_area)
{
size_t size = scdmd_area->response.header.length -
sizeof(struct chsc_scdmd_response);
size_t len, ofs = 0;
int rc;
while (ofs < size) {
rc = process_cmb((struct chsc_cmb_area *)
&scdmd_area->response_data[ofs],
size - ofs, &len, NULL);
if (rc != 0 && rc != -ENODEV)
return rc;
ofs += len;
}
return 0;
}
/*
* Get Crypto Measurement data on the APQN level
*/
static int get_apqn_measurement_data(uint8_t card)
{
struct chsc_scdmd_area scdmd_area;
int rc;
memset(&scdmd_area, 0, sizeof(scdmd_area));
do {
scdmd_area.request.header.code = 0x102d;
scdmd_area.request.header.length =
sizeof(struct chsc_scdmd_request);
if (scdmd_area.response.p) {
scdmd_area.request.first_drid =
scdmd_area.response.crid;
} else {
scdmd_area.request.first_drid.ap_index = card;
scdmd_area.request.first_drid.domain_index =
g.min_domain;
}
scdmd_area.request.last_drid.ap_index = card;
scdmd_area.request.last_drid.domain_index = g.max_domain;
scdmd_area.request.s = 1;
scdmd_area.request.apsm[MASK_WORD_NO(card)] |= MASK_BIT(card);
memcpy(scdmd_area.request.dsm, g.domain_mask,
sizeof(scdmd_area.request.dsm));
rc = ioctl(g.chsc_fd, CHSC_START_SYNC, &scdmd_area);
if (rc != 0) {
rc = -errno;
warnx("Failed to get APQN measurement data for card "
"%02x: %s", card, strerror(errno));
break;
}
rc = chsc_error_from_response(scdmd_area.response.header.code);
if (rc != 0) {
if (rc != -EOPNOTSUPP && rc != -ENODEV) {
warnx("Failed to get APQN crypto measurement "
"data for card %02x: %s", card,
strerror(-rc));
} else {
pr_verbose("Failed to get APQN crypto "
"measurement data for card %02x: %s",
card, strerror(-rc));
/*
* ignore return code other than -EOPNOTSUPP
* and -ENODEV
*/
rc = 0;
}
break;
}
rc = process_apqn_measurement_data(&scdmd_area);
if (rc != 0)
break;
} while (scdmd_area.response.p);
return rc;
}
/*
* Process the card measurement data and extract the CMBs
*/
static int process_card_measurement_data(struct chsc_scmd_area *scmd_area,
uint8_t *last_card)
{
size_t size = scmd_area->response.header.length -
sizeof(struct chsc_scmd_response);
size_t len, ofs = 0;
int rc;
while (ofs < size) {
rc = process_cmb((struct chsc_cmb_area *)
&scmd_area->response_data[ofs],
size - ofs, &len, last_card);
if (rc != 0 && rc != -ENODEV)
return rc;
ofs += len;
if (rc == -ENODEV)
continue;
if (!g.no_apqn) {
rc = get_apqn_measurement_data(*last_card);
if (rc != 0)
return rc;
}
}
return 0;
}
/*
* Get Crypto Measurement data on the card level
*/
static int get_card_measurement_data(void)
{
struct chsc_scmd_area scmd_area;
uint8_t last_card = 0;
int rc;
memset(&scmd_area, 0, sizeof(scmd_area));
do {
scmd_area.request.header.code = 0x102e;
scmd_area.request.header.length =
sizeof(struct chsc_scmd_request);
scmd_area.request.one = 1;
scmd_area.request.fcs = g.min_card;
scmd_area.request.lcs = g.max_card;
rc = ioctl(g.chsc_fd, CHSC_START_SYNC, &scmd_area);
if (rc != 0) {
rc = -errno;
warnx("Failed to get card measurement data: %s",
strerror(errno));
break;
}
rc = chsc_error_from_response(scmd_area.response.header.code);
if (rc != 0) {
warnx("Failed to get card crypto measurement data: %s",
strerror(-rc));
break;
}
rc = process_card_measurement_data(&scmd_area, &last_card);
if (rc != 0)
break;
if (scmd_area.response.p)
scmd_area.request.fcs = last_card + 1;
} while (scmd_area.response.p && last_card < g.max_card);
return rc;
}
/*
* Signal handler for SIGALRM
*/
static void alarm_handler(int UNUSED(sig))
{
if (!quit)
alarm(g.interval);
}
/*
* Signal handler for SIGINT and SIGTERM
*/
static void int_handler(int UNUSED(sig))
{
quit = true;
raise(SIGALRM);
}
/*
* Calculates the time difference between tv1 and tv2 in seconds
*/
static float time_diff(struct timeval *tv1, struct timeval *tv2)
{
struct timeval tv_diff;
tv_diff.tv_sec = tv2->tv_sec - tv1->tv_sec;
tv_diff.tv_usec = tv2->tv_usec - tv1->tv_usec;
if (tv_diff.tv_usec < 0) {
tv_diff.tv_sec--;
tv_diff.tv_usec += 1000000;
}
return (float)tv_diff.tv_sec + (float)(0.000001 * tv_diff.tv_usec);
}
/*
* Initialize the default print format
*/
static int default_print_initialize(void)
{
g.device_rec = util_rec_new_wide("-");
util_rec_def(g.device_rec, "device", UTIL_REC_ALIGN_LEFT, 7,
"DEVICE");
util_rec_def(g.device_rec, "kind", UTIL_REC_ALIGN_LEFT, 5, "");
util_rec_def(g.device_rec, "type", UTIL_REC_ALIGN_LEFT, 33,
"TYPE");
util_rec_def(g.device_rec, "time", UTIL_REC_ALIGN_LEFT, 20,
"TIMESTAMP");
g.counter_rec = util_rec_new_wide("-");
util_rec_def(g.counter_rec, "name", UTIL_REC_ALIGN_LEFT, 18,
"COUNTER");
util_rec_def(g.counter_rec, "ops", UTIL_REC_ALIGN_RIGHT, 10,
"OPS");
util_rec_def(g.counter_rec, "rate", UTIL_REC_ALIGN_RIGHT, 12,
"RATE");
util_rec_def(g.counter_rec, "utilization", UTIL_REC_ALIGN_RIGHT,
12, "UTILIZATION");
util_rec_def(g.counter_rec, "duration", UTIL_REC_ALIGN_RIGHT,
15, "AVG.DURATION");
return 0;
}
/*
* Terminate the default print format
*/
static int default_print_terminate(void)
{
util_rec_free(g.counter_rec);
util_rec_free(g.device_rec);
return 0;
}
/*
* Print the header lines for the default print format
*/
static int default_print_header(void)
{
char timestamp[64];
struct utsname un;
struct tm *tm;
time_t t;
time(&t);
tm = localtime(&t);
strftime(timestamp, sizeof(timestamp), "%x", tm);
if (uname(&un) != 0)
return -errno;
printf("%s %s (%s) \t%s\t%s\n\n", un.sysname, un.release,
un.nodename, timestamp, un.machine);
return 0;
}
/*
* Print the footer lines for the default print format
*/
static int default_print_footer(void)
{
printf("\n");
return 0;
}
/*
* Print the interval header lines for the default print format
*/
static int default_print_interval_header(unsigned long interval_count,
const char *timestamp)
{
printf("*****************************************************"
"***************\n");
printf("TIME: %s\t\tINTERVAL: %lu\n\n", timestamp, interval_count);
return 0;
}
/*
* Prints the separator lines in front of a device for the default print format
*/
static int default_print_device_header(bool is_apqn, uint8_t card,
uint8_t domain, const char *type,
const char *timestamp)
{
if (is_apqn)
util_rec_set(g.device_rec, "device", "%02x.%04x", card,
domain);
else
util_rec_set(g.device_rec, "device", "%02x", card);
util_rec_set(g.device_rec, "kind", "%s",
is_apqn ? "APQN" : "CARD");
util_rec_set(g.device_rec, "type", "%s", type);
util_rec_set(g.device_rec, "time", "%s", timestamp);
util_rec_print_hdr(g.device_rec);
util_rec_print(g.device_rec);
printf("\n");
util_rec_set_indent(g.counter_rec, is_apqn ? 8 : 4);
util_rec_print_hdr(g.counter_rec);
return 0;
}
/*
* Prints the separator lines after a device for the default print format
*/
static int default_print_device_footer(void)
{
printf("\n");
return 0;
}
/**
* Prints the counter data for the default print format
*/
static int default_print_counter_data(bool UNUSED(is_apqn),
uint8_t UNUSED(card),
uint8_t UNUSED(domain),
const char *UNUSED(type),
const char *UNUSED(timestamp),
const char *name,
struct interval_values *vals)
{
util_rec_set(g.counter_rec, "name", "%s", name);
util_rec_set(g.counter_rec, "ops", "%llu", vals->count);
util_rec_set(g.counter_rec, "rate", "%.2f", vals->rate);
util_rec_set(g.counter_rec, "utilization", "%.2f %%",
vals->utilization * 100);
if (vals->duration >= 1)
util_rec_set(g.counter_rec, "duration", "%.3f sec ",
vals->duration);
else if (vals->duration >= 0.001)
util_rec_set(g.counter_rec, "duration", "%.3f msec",
vals->duration * 1000);
else
util_rec_set(g.counter_rec, "duration", "%.3f usec",
vals->duration * 1000000);
util_rec_print(g.counter_rec);
return 0;
}
/*
* Prints a separator between the counter lines and the totals line for the
* default print format
*/
static int default_print_counter_separator(void)
{
util_rec_print_separator(g.counter_rec);
return 0;
}
/*
* Initialize the JSON print format
*/
static int json_print_initialize(void)
{
/* Use a decimal point to make JSON code compliant with RFC7159 */
setlocale(LC_NUMERIC, "C");
return 0;
}
/*
* Print the header lines for the JSON print format
*/
static int json_print_header(void)
{
char timestamp[64];
struct utsname un;
struct tm *tm;
time_t t;
time(&t);
tm = localtime(&t);
strftime(timestamp, sizeof(timestamp), "%x", tm);
if (uname(&un) != 0)
return -errno;
printf("{\"zcryptstats\": {\n");
printf("\t\"host\": {\n");
printf("\t\t\"nodename\": \"%s\",\n", un.nodename);
printf("\t\t\"sysname\": \"%s\",\n", un.sysname);
printf("\t\t\"release\": \"%s\",\n", un.release);
printf("\t\t\"machine\": \"%s\",\n", un.machine);
printf("\t\t\"date\": \"%s\",\n", timestamp);
printf("\t\t\"statistics\": [\n");
return 0;
}
/*
* Print the footer lines for the JSON print format
*/
static int json_print_footer(void)
{
printf("\n\t\t]\n");
printf("\t}\n");
printf("}}\n");
return 0;
}
/*
* Print the interval header lines for the JSON print format
*/
static int json_print_interval_header(unsigned long interval_count,
const char *timestamp)
{
if (interval_count > 1)
printf(",\n");
printf("\t\t\t{\n");
printf("\t\t\t\t\"interval\": %lu, \"timestamp\": \"%s\","
" \"devices\": [\n", interval_count, timestamp);
return 0;
}
/*
* Print the interval footer lines for the JSON print format
*/
static int json_print_interval_footer(void)
{
if (!g.first_device)
printf("\n");
printf("\t\t\t\t]\n");
printf("\t\t\t}");
return 0;
}
/*
* Prints the separator lines in front of a device for the JSON print format
*/
static int json_print_device_header(bool is_apqn, uint8_t card,
uint8_t domain, const char *type,
const char *UNUSED(timestamp))
{
if (!g.first_device)
printf(",\n");
printf("\t\t\t\t\t{");
if (is_apqn)
printf("\"device\": \"%02x.%04x\"", card,
domain);
else
printf("\"device\": \"%02x\"", card);
printf(", \"type\": \"%s\",\n", type);
printf("\t\t\t\t\t \"counters\": [\n");
return 0;
}
/*
* Prints the separator lines after a device for the JSON print format
*/
static int json_print_device_footer(void)
{
if (!g.first_counter)
printf("\n");
printf("\t\t\t\t\t ]}");
return 0;
}
/**
* Prints the counter data for the JSON print format
*/
static int json_print_counter_data(bool UNUSED(is_apqn),
uint8_t UNUSED(card),
uint8_t UNUSED(domain),
const char *UNUSED(type),
const char *UNUSED(timestamp),
const char *name,
struct interval_values *vals)
{
if (!g.first_counter)
printf(",\n");
printf("\t\t\t\t\t\t{\"counter\": \"%s\", \"ops\": %llu, "
"\"rate\": %.2f, \"utilization\": %.2f, \"duration\": %.9f}",
name, vals->count, vals->rate, vals->utilization * 100,
vals->duration);
return 0;
}
static int table_print_initialize(void)
{
g.counter_rec = util_rec_new_wide("-");
util_rec_def(g.counter_rec, "time", UTIL_REC_ALIGN_LEFT, 20,
"TIMESTAMP");
util_rec_def(g.counter_rec, "device", UTIL_REC_ALIGN_LEFT, 7,
"DEVICE");
util_rec_def(g.counter_rec, "ops", UTIL_REC_ALIGN_RIGHT, 10,
"OPS");
util_rec_def(g.counter_rec, "rate", UTIL_REC_ALIGN_RIGHT, 12,
"RATE");
util_rec_def(g.counter_rec, "utilization", UTIL_REC_ALIGN_RIGHT,
12, "UTILIZATION");
util_rec_def(g.counter_rec, "duration", UTIL_REC_ALIGN_RIGHT,
15, "AVG.DURATION");
return 0;
}
static int table_print_terminate(void)
{
util_rec_free(g.counter_rec);
return 0;
}
static int table_print_header(void)
{
int rc;
rc = default_print_header();
if (rc != 0)
return rc;
util_rec_print_hdr(g.counter_rec);
return 0;
}
static int table_print_interval_footer(void)
{
util_rec_print_separator(g.counter_rec);
return 0;
}
static int table_print_counter_data(bool is_apqn, uint8_t card, uint8_t domain,
const char *UNUSED(type),
const char *timestamp,
const char *UNUSED(name),
struct interval_values *vals)
{
if (is_apqn)
util_rec_set(g.counter_rec, "device", "%02x.%04x", card,
domain);
else
util_rec_set(g.counter_rec, "device", "%02x", card);
util_rec_set(g.counter_rec, "time", "%s", timestamp);
util_rec_set(g.counter_rec, "ops", "%llu", vals->count);
util_rec_set(g.counter_rec, "rate", "%.2f", vals->rate);
util_rec_set(g.counter_rec, "utilization", "%.2f %%",
vals->utilization * 100);
if (vals->duration >= 1)
util_rec_set(g.counter_rec, "duration", "%.3f sec ",
vals->duration);
else if (vals->duration >= 0.001)
util_rec_set(g.counter_rec, "duration", "%.3f msec",
vals->duration * 1000);
else
util_rec_set(g.counter_rec, "duration", "%.3f usec",
vals->duration * 1000000);
util_rec_print(g.counter_rec);
return 0;
}
static int csv_print_initialize(void)
{
/* Use a decimal point to not conflict with the colon separator char */
setlocale(LC_NUMERIC, "C");
g.counter_rec = util_rec_new_csv(",");
util_rec_def(g.counter_rec, "time", UTIL_REC_ALIGN_LEFT, 20,
"TIMESTAMP");
util_rec_def(g.counter_rec, "device", UTIL_REC_ALIGN_LEFT, 7,
"DEVICE");
util_rec_def(g.counter_rec, "ops", UTIL_REC_ALIGN_RIGHT, 10,
"OPS");
util_rec_def(g.counter_rec, "rate", UTIL_REC_ALIGN_RIGHT, 12,
"RATE");
util_rec_def(g.counter_rec, "utilization", UTIL_REC_ALIGN_RIGHT,
12, "UTILIZATION");
util_rec_def(g.counter_rec, "duration", UTIL_REC_ALIGN_RIGHT,
15, "AVG.DURATION");
return 0;
}
static int csv_print_terminate(void)
{
util_rec_free(g.counter_rec);
return 0;
}
static int csv_print_header(void)
{
util_rec_print_hdr(g.counter_rec);
return 0;
}
static int csv_print_counter_data(bool is_apqn, uint8_t card, uint8_t domain,
const char *UNUSED(type),
const char *timestamp,
const char *UNUSED(name),
struct interval_values *vals)
{
if (is_apqn)
util_rec_set(g.counter_rec, "device", "%02x.%04x", card,
domain);
else
util_rec_set(g.counter_rec, "device", "%02x", card);
util_rec_set(g.counter_rec, "time", "%s", timestamp);
util_rec_set(g.counter_rec, "ops", "%llu", vals->count);
util_rec_set(g.counter_rec, "rate", "%.2f", vals->rate);
util_rec_set(g.counter_rec, "utilization", "%.2f %%",
vals->utilization * 100);
util_rec_set(g.counter_rec, "duration", "%.9f", vals->duration);
util_rec_print(g.counter_rec);
return 0;
}
/*
* Calculates number of ops, utilization, duration and rate of an
* interval from the timer values, scale and interval time.
*/
static void calc_interval_values(struct chsc_cmb_entry *current,
struct chsc_cmb_entry *previous,
float scale,
float interval_time,
struct interval_values *result)
{
u64 tdiff;
tdiff = current->t - previous->t;
result->count = current->c - previous->c;
result->utilization = (double)(tdiff) * scale / interval_time;
if (result->count > 0)
result->duration = (double)(tdiff) * scale / result->count;
else
result->duration = 0;
result->rate = (double)result->count / interval_time;
}
/*
* Print the measurement data of an interval
*/
static int print_interval_data(struct interval_data *data,
const char *timestamp, float interval_time)
{
struct chsc_cmb_entry total_current;
struct chsc_cmb_entry total_previous;
struct interval_values vals;
const char *type, *counter;
uint32_t mask = 0x80000000;
bool totals_found = false;
size_t len;
int i, rc;
len = get_cmb_length(&data->current);
type = get_card_name(data->current.header.ct, data->current.header.mt);
rc = pr_call(print_device_header)(data->current.header.format == 1,
data->current.header.ax,
data->current.header.dx, type,
timestamp);
if (rc != 0)
return rc;
memset(&total_current, 0, sizeof(total_current));
memset(&total_previous, 0, sizeof(total_previous));
g.first_counter = true;
for (i = 0; i < 32 &&
offsetofend(struct chsc_cmb_area, entries[i]) <= len; i++) {
if (data->current.header.v & mask) {
if (is_counter_totals(data->current.header.ct,
data->current.header.mt, i)) {
total_current.t = data->current.entries[i].t;
total_current.c = data->current.entries[i].c;
total_previous.t = data->previous.entries[i].t;
total_previous.c = data->previous.entries[i].c;
totals_found = true;
} else if (!totals_found) {
total_current.t += data->current.entries[i].t;
total_current.c += data->current.entries[i].c;
total_previous.t += data->previous.entries[i].t;
total_previous.c += data->previous.entries[i].c;
}
if (g.only_totals)
continue;
calc_interval_values(&data->current.entries[i],
&data->previous.entries[i],
data->current.header.s,
interval_time,
&vals);
counter = get_counter_name(data->current.header.ct,
data->current.header.mt, i);
rc = pr_call(print_counter_data)(
data->current.header.format == 1,
data->current.header.ax,
data->current.header.dx, type,
timestamp, counter, &vals);
if (rc != 0)
break;
g.first_counter = false;
}
mask >>= 1;
}
if (!g.no_totals) {
rc = pr_call(print_counter_separator)();
if (rc != 0)
return rc;
calc_interval_values(&total_current, &total_previous,
data->current.header.s, interval_time,
&vals);
rc = pr_call(print_counter_data)(
data->current.header.format == 1,
data->current.header.ax,
data->current.header.dx, type,
timestamp, "Total", &vals);
if (rc != 0)
return rc;
}
rc = pr_call(print_device_footer)();
if (rc != 0)
return rc;
return 0;
}
/*
* Print the measured data
*/
static int print_measurement_data(unsigned long interval_count,
float interval_time, const char *timestamp)
{
bool header_printed = false;
struct interval_data *dd;
struct card_data *cd;
int card, domain, rc;
g.first_device = true;
for (card = 0; card < NUM_CARDS; card++) {
cd = g.cards[card];
if (cd == NULL)
continue;
if (!cd->data.current_valid) {
/* Not update in last interval -> free it */
free_card_data(cd);
g.cards[card] = NULL;
pr_verbose("Card %02x removed", card);
continue;
}
if (cd->data.previous_valid) {
if (memcmp(&cd->data.current.header,
&cd->data.previous.header,
sizeof(struct chsc_cmb_header)) != 0) {
free_card_data(cd);
g.cards[card] = NULL;
pr_verbose("CMB header mismatch, card %02x "
"removed", card);
continue;
}
if (!header_printed) {
rc = pr_call(print_interval_header)(
interval_count, timestamp);
if (rc != 0)
return rc;
header_printed = true;
}
rc = print_interval_data(&cd->data, timestamp,
interval_time);
if (rc != 0)
return rc;
g.first_device = false;
}
cd->data.current_valid = false;
for (domain = 0; domain < NUM_DOMAINS; domain++) {
dd = cd->domains[domain];
if (dd == NULL)
continue;
if (!dd->current_valid) {
/* Not update in last interval -> free it */
free(dd);
cd->domains[domain] = NULL;
pr_verbose("APQN %02x.%04x removed", card,
domain);
}
if (dd->previous_valid) {
if (memcmp(&dd->current.header,
&dd->previous.header,
sizeof(struct chsc_cmb_header))) {
free(dd);
cd->domains[domain] = NULL;
pr_verbose("CMB header mismatch, APQN "
"%02x.%04x removed", card,
domain);
continue;
}
rc = print_interval_data(dd, timestamp,
interval_time);
if (rc != 0)
return rc;
}
dd->current_valid = false;
}
}
if (header_printed) {
rc = pr_call(print_interval_footer)();
if (rc != 0)
return rc;
} else if (interval_count > 0) {
pr_verbose("No data was reported in this interval");
warnx("Failed to get card crypto measurement data: %s",
strerror(ENODEV));
return -ENODEV;
}
return 0;
}
/*
* Perform the measurement in intervals
*/
static int perform_measurement(void)
{
struct timeval tv_current, tv_previous;
struct sigaction alrm_act, int_act;
unsigned long interval_count = 0;
float interval_time;
char timestamp[64];
struct tm *tm;
int rc;
/* Set a handler for SIGINT/SIGTERM */
memset(&int_act, 0, sizeof(int_act));
int_act.sa_handler = int_handler;
sigaction(SIGINT, &int_act, NULL);
sigaction(SIGTERM, &int_act, NULL);
/* Set a handler for SIGALRM */
memset(&alrm_act, 0, sizeof(alrm_act));
alrm_act.sa_handler = alarm_handler;
sigaction(SIGALRM, &alrm_act, NULL);
rc = pr_call(print_initialize)();
if (rc != 0)
return rc;
rc = pr_call(print_header)();
if (rc != 0)
return 0;
alarm(g.interval);
memset(&tv_current, 0, sizeof(tv_current));
while (!quit) {
pr_verbose("Interval %lu", interval_count);
tv_previous = tv_current;
rc = gettimeofday(&tv_current, NULL);
if (rc != 0)
break;
tm = localtime(&tv_current.tv_sec);
if (tm == NULL)
break;
strftime(timestamp, sizeof(timestamp), "%x %X", tm);
interval_time = time_diff(&tv_previous, &tv_current);
rc = get_card_measurement_data();
if (rc != 0)
break;
rc = print_measurement_data(interval_count, interval_time,
timestamp);
if (rc != 0)
break;
if (g.count > 0 && interval_count >= g.count) {
pr_verbose("Interval limit reached");
break;
}
interval_count++;
if (quit)
break;
pause();
}
if (quit)
pr_verbose("Measurement stopped by user");
alarm(0);
memset(&alrm_act, 0, sizeof(alrm_act));
alrm_act.sa_handler = SIG_DFL;
sigaction(SIGALRM, &alrm_act, NULL);
rc = pr_call(print_footer)();
if (rc != 0)
return 0;
rc = pr_call(print_terminate)();
if (rc != 0)
return rc;
return 0;
}
/*
* Parse the type mapping specification:
* TYPE:MODE=TYPE:MODE[,TYPE:MODE=TYPE:MODE[,...]]
*/
static int parse_type_mapping(char *mapping)
{
unsigned int from_type, to_type, from_mode, to_mode;
struct type_mapping *map;
char *tok;
tok = strtok(mapping, ",");
while (tok != NULL) {
if (sscanf(tok, "%u:%u=%u:%u", &from_type, &from_mode,
&to_type, &to_mode) != 4) {
warnx("Invalid type mapping: %s", tok);
return -EINVAL;
}
pr_verbose("from_type: %u from_mode: %u to_type: %u "
"to_mode: %u", from_type, from_mode, to_type,
to_mode);
if (from_type < NUM_CRYPTO_TYPES &&
crypto_types[from_type].name != NULL &&
from_mode < crypto_types[from_type].num_modes &&
crypto_types[from_type].modes[from_mode].counters != NULL) {
warnx("Cannot map a known type/mode to another "
"type/mode: %s", tok);
return -EINVAL;
}
if (to_type >= NUM_CRYPTO_TYPES ||
crypto_types[to_type].name == NULL ||
to_mode >= crypto_types[to_type].num_modes ||
crypto_types[to_type].modes[to_mode].counters == NULL) {
warnx("Cannot map a type/mode to an unknown "
"type/mode: %s", tok);
return -EINVAL;
}
map = util_malloc(sizeof(struct type_mapping));
map->from_type = from_type;
map->from_mode = from_mode;
map->to_type = to_type;
map->to_mode = to_mode;
map->next = g.type_mapping;
g.type_mapping = map;
tok = strtok(NULL, ",");
}
return 0;
}
static void free_type_mapping(void)
{
struct type_mapping *map = g.type_mapping;
struct type_mapping *next;
while (map != NULL) {
next = map->next;
free(map);
map = next;
}
}
static int add_device_selection(const char *device_id)
{
int card = -1, domain = -1;
struct device_selection *dev;
pr_verbose("device_id: '%s'", device_id);
/* check for 'card[.domain]' specification */
if (sscanf(device_id, "%x.%x", &card, &domain) >= 1) {
pr_verbose("card: %d domain: %d", card, domain);
if (card < 0 || card > g.max_card_used) {
warnx("Invalid card specified: %s", device_id);
return -EINVAL;
}
g.card_mask[MASK_WORD_NO(card)] |= MASK_BIT(card);
g.min_card = MIN(g.min_card, card);
g.max_card = MAX(g.max_card, card);
if (domain >= 0) {
if (domain > NUM_DOMAINS) {
warnx("Invalid domain specified: %s",
device_id);
return -EINVAL;
}
g.domain_mask[MASK_WORD_NO(domain)] |=
MASK_BIT(domain);
g.min_domain = MIN(g.max_domain, domain);
g.max_domain = MAX(g.max_domain, domain);
} else {
memset(g.domain_mask, 0xff, sizeof(g.domain_mask));
g.min_domain = 0;
g.max_domain = NUM_DOMAINS - 1;
}
dev = util_malloc(sizeof(struct device_selection));
dev->card = card;
dev->domain = domain;
dev->next = g.dev_selection;
g.dev_selection = dev;
return 0;
}
/* check for '.domain' specification */
if (device_id[0] == '.' &&
sscanf(device_id + 1, "%x", &domain) == 1) {
pr_verbose("domain: %d", domain);
if (domain < 0 || domain > NUM_DOMAINS) {
warnx("Invalid domain specified: %s", device_id);
return -EINVAL;
}
g.domain_mask[MASK_WORD_NO(domain)] |= MASK_BIT(domain);
g.min_domain = MIN(g.max_domain, domain);
g.max_domain = MAX(g.max_domain, domain);
memset(g.card_mask, 0xff, sizeof(g.card_mask));
g.min_card = 0;
g.max_card = g.max_card_used;
dev = util_malloc(sizeof(struct device_selection));
dev->card = -1;
dev->domain = domain;
dev->next = g.dev_selection;
g.dev_selection = dev;
return 0;
}
warnx("Invalid device ID specified: %s", device_id);
return -EINVAL;
}
/*
* Frees the device selection list
*/
static void free_device_selection(void)
{
struct device_selection *dev = g.dev_selection;
struct device_selection *next;
while (dev != NULL) {
next = dev->next;
free(dev);
dev = next;
}
}
/*
* Build the AP and domain selection mask from the specified device ID's.
*/
static int parse_device_selection(void)
{
int i, rc;
g.min_card = NUM_CARDS - 1;
g.max_card = 0;
g.min_domain = NUM_DOMAINS - 1;
g.max_domain = 0;
for (i = 0; g.device_ids[i] != NULL; i++) {
rc = add_device_selection(g.device_ids[i]);
if (rc != 0)
return rc;
}
if (i == 0) {
/* No device-IDs specified */
memset(g.card_mask, 0xff, sizeof(g.card_mask));
g.min_card = 0;
g.max_card = g.max_card_used;
memset(g.domain_mask, 0xff, sizeof(g.domain_mask));
g.min_domain = 0;
g.max_domain = NUM_DOMAINS - 1;
}
pr_verbose("Min card: %u, max card: %u", g.min_card, g.max_card);
pr_verbose("Min domain: %u, max domain: %u", g.min_domain,
g.max_domain);
return 0;
}
/*
* Entry point
*/
int main(int argc, char *argv[])
{
char *endp;
int c, rc;
util_prg_init(&prg);
util_opt_init(zcryptstats_opt_vec, NULL);
while (1) {
c = util_opt_getopt_long(argc, argv);
if (c == -1)
break;
switch (c) {
case 'i':
g.interval = strtoll(optarg, &endp, 0);
if (*optarg == '\0' || *endp != '\0' ||
g.interval <= 0 ||
(g.interval == LLONG_MAX && errno == ERANGE)) {
warnx("Invalid value for '--interval'|"
"'-i': '%s'", optarg);
util_prg_print_parse_error();
return EXIT_FAILURE;
}
break;
case 'c':
g.count = strtoull(optarg, &endp, 0);
if (*optarg == '\0' || *endp != '\0' || g.count == 0 ||
(g.count == LLONG_MAX && errno == ERANGE)) {
warnx("Invalid value for '--count'|"
"'-c': '%s'", optarg);
util_prg_print_parse_error();
return EXIT_FAILURE;
}
break;
case 'o':
if (strcasecmp(optarg, "JSON") == 0) {
g.print_funcs = &json_print;
} else if (strcasecmp(optarg, "TABLE") == 0) {
g.only_totals = true;
g.print_funcs = &table_print;
} else if (strcasecmp(optarg, "CSV") == 0) {
g.only_totals = true;
g.print_funcs = &csv_print;
} else {
warnx("Invalid value for '--output'|"
"'-o': '%s'", optarg);
util_prg_print_parse_error();
return EXIT_FAILURE;
}
break;
case 't':
g.no_totals = true;
break;
case 'T':
g.only_totals = true;
break;
case 'a':
g.no_apqn = true;
break;
case 'M':
g.map_type = optarg;
break;
case 'A':
g.all = true;
break;
case 'O':
g.only_online = true;
break;
case 'V':
g.verbose = true;
break;
case 'h':
util_prg_print_help();
util_opt_print_help();
print_adapter_id_help();
return EXIT_SUCCESS;
case 'v':
util_prg_print_version();
return EXIT_SUCCESS;
default:
util_opt_print_parse_error(c, argv);
return EXIT_FAILURE;
}
}
/* remaining positional args are device IDs */
g.device_ids = &argv[optind];
if (g.only_totals && g.no_totals) {
warnx("Either --no-totals or --only-totals can be specified, "
"but not both");
return EXIT_FAILURE;
}
if (g.only_online && g.all) {
warnx("Either --only-online or --all can be specified, "
"but not both");
return EXIT_FAILURE;
}
pr_verbose("Interval: %ld Count: %ld", g.interval, g.count);
rc = get_max_card_index(&g.max_card_used);
if (rc != 0) {
rc = EXIT_FAILURE;
goto out;
}
rc = parse_device_selection();
if (rc != 0) {
rc = EXIT_FAILURE;
goto out;
}
if (g.map_type != NULL) {
rc = parse_type_mapping(g.map_type);
if (rc != 0) {
rc = EXIT_FAILURE;
goto out;
}
}
g.chsc_fd = open(CHSC_DEVICE, O_RDWR);
if (g.chsc_fd < 0) {
rc = errno;
warnx("File '%s:' %s", CHSC_DEVICE, strerror(errno));
if (rc == ENOENT)
warnx("You might have to load kernel module 'chsc_sch' "
"using 'modprobe chsc_sch'");
return EXIT_FAILURE;
}
pr_verbose("Device '%s' has been opened successfully", CHSC_DEVICE);
/* Don't buffer data if redirected to a pipe */
setbuf(stdout, NULL);
rc = perform_measurement();
if (rc != 0) {
rc = EXIT_FAILURE;
goto out;
}
out:
if (g.chsc_fd >= 0)
close(g.chsc_fd);
free_device_selection();
free_type_mapping();
free_interval_data();
return rc;
}