/* Copyright IBM Corp. 2023 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ /* List available Processor Assist Instrumentation (PAI) counters. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "lib/util_opt.h" #include "lib/util_prg.h" #include "lib/util_base.h" #include "lib/util_path.h" #include "lib/util_scandir.h" #include "lib/util_libc.h" #include "lib/util_file.h" #include "lib/util_list.h" #include "lib/libcpumf.h" static struct util_opt opt_vec[] = { UTIL_OPT_SECTION("OPTIONS"), { .option = { "numeric", no_argument, NULL, 'n' }, .desc = "Sort PAI counters by counter number" }, { .option = { "type", required_argument, NULL, 't' }, .argument = "TYPE", .desc = "Type of PAI counters to show: crypto, nnpa" }, UTIL_OPT_HELP, UTIL_OPT_VERSION, UTIL_OPT_END }; static const struct util_prg prg = { .desc = "List Processor Assist Information counter sets", .copyright_vec = { { .owner = "IBM Corp.", .pub_first = 2023, .pub_last = 2023, }, UTIL_PRG_COPYRIGHT_END } }; static bool numsort; /* If true sort counter numerically */ #define PAI_PATH "/bus/event_source/devices/%s" enum pai_types { /* Bit mask for supported PAI counters */ pai_type_crypto = 0, /* PAI Crypto Counters */ pai_type_nnpa = 1, /* PAI NNPA Counters */ pai_type_max = 2, /* PAI maximum value, must be last */ }; static int pai_types_show; struct pai_ctrname { /* List of defined counters */ char *name; /* Counter name */ unsigned long nr; /* Counter number */ }; struct pai_node { /* Head for PAI counter sets */ struct util_list_node node; /* Successor in PAI counter set list */ enum pai_types type; /* PAI type */ int pmu; /* Assigned PMU type number */ const char *name; /* Counter set name */ char *name_uc; /* Counter set name upper case */ const char *sysfs_name; /* Counter set name in /sysfs tree */ const char *filter_name; /* Counter set name for scandir filter */ struct pai_ctrname *ctrlist; /* List of counter names & numbers */ size_t ctrsize; /* Total size in bytes of ctrlist */ int ctridx; /* Index of last entry used in ctrlist */ unsigned long base; /* Base number for counter set */ }; static struct util_list pai_list; /* Return base of counter set, this is the first counter of this set. */ static unsigned long pai_type_base(enum pai_types t) { switch (t) { case pai_type_crypto: return 0x1000; case pai_type_nnpa: return 0x1800; case pai_type_max: break; } return 0; } /* Test PAI counter name from command line option. */ static const char *pai_type_name(enum pai_types t) { switch (t) { case pai_type_crypto: return "crypto"; case pai_type_nnpa: return "nnpa"; case pai_type_max: break; } return "unknown"; } /* Convert PAI counter type to sysfs directory name. Only validated * input at this time. */ static const char *pai_type_sysfs(enum pai_types t) { if (t == pai_type_crypto) return "pai_crypto"; return "pai_ext"; } /* Convert PAI counter type to sysfs directory name filter for scandir(). */ static const char *pai_type_filter(enum pai_types t) { if (t == pai_type_nnpa) return "^NNPA"; return "[^.]"; /* Matches anything but . and .. in sysfs */ } /* Sort PAI counter names by assigned counter number. */ static int pai_ctrcmp(const void *p1, const void *p2) { struct pai_ctrname *l = (struct pai_ctrname *)p1; struct pai_ctrname *r = (struct pai_ctrname *)p2; return l->nr > r->nr ? 1 : -1; } /* Convert string to upper case. */ static char *str2uc(const char *s) { char *uc = util_strdup(s), *old_uc = uc; for (; *uc; ++uc) *uc = toupper(*uc); return old_uc; } /* Read counter names and assigned event number from sysfs file tree. * Exit when sysfs directory can not be scanned. */ static void read_counternames(struct pai_node *node) { int i, more = 0, ctr = 0, count = 0; struct dirent **namelist = NULL; char *path, *ctrpath; /* Read counter names and assigned event number. */ path = util_path_sysfs(PAI_PATH "/events", node->sysfs_name); count = util_scandir(&namelist, alphasort, path, node->filter_name); if (count <= 0) errx(EXIT_FAILURE, "Cannot open %s", path); node->ctrsize = count * sizeof(*node->ctrlist); node->ctrlist = util_malloc(node->ctrsize); for (i = 0; i < count && ctr >= 0; i++) { util_asprintf(&ctrpath, "%s/%s", path, namelist[i]->d_name); if (util_file_read_va(ctrpath, "event=%x", &ctr) == 1) { node->ctrlist[node->ctridx].name = util_strdup(namelist[i]->d_name); node->ctrlist[node->ctridx++].nr = ctr; more++; } else { warnx("Cannot parse %s", ctrpath); } free(ctrpath); } util_scandir_free(namelist, count); free(path); if (numsort && more > 1) qsort(node->ctrlist, more, sizeof(*node->ctrlist), pai_ctrcmp); } static void show_painode(void) { struct pai_node *node; int indent = 0; int offset = 0; util_list_iterate(&pai_list, node) { for (int i = 0; i < node->ctridx; ++i) indent = MAX((size_t)indent, strlen(node->ctrlist[i].name)); } printf("RAW %*s NAME %*s DESCRIPTION\n", 3, "", indent - 5, ""); util_list_iterate(&pai_list, node) { for (int i = 0; i < node->ctridx; ++i) { printf("%d:%ld %s", node->pmu, node->ctrlist[i].nr, node->ctrlist[i].name); offset = indent - strlen(node->ctrlist[i].name) + 1; printf("%*s", offset, ""); printf("Counter %ld / PAI %s counter set\n", node->ctrlist[i].nr - node->base, node->name_uc); } } } /* Release all memory allocated at make_painode(). */ static void free_painode(void) { struct pai_node *next, *node; util_list_iterate_safe(&pai_list, node, next) { free(node->name_uc); for (int i = 0; i < node->ctridx; ++i) free(node->ctrlist[i].name); free(node->ctrlist); free(node); } } static void make_painode(enum pai_types t) { struct pai_node *node = util_zalloc(sizeof(*node)); char *path; node->type = t; node->sysfs_name = pai_type_sysfs(t); node->name = pai_type_name(t); node->name_uc = str2uc(node->name); node->filter_name = pai_type_filter(t); node->base = pai_type_base(t); /* Read PMU type number. */ path = util_path_sysfs(PAI_PATH, node->sysfs_name); node->pmu = libcpumf_pmutype(path); if (node->pmu < 0) errx(EXIT_FAILURE, "Cannot open %s", path); free(path); read_counternames(node); util_list_add_tail(&pai_list, node); } static int painode_cmp(void *a, void *b, void *UNUSED(data)) { struct pai_node *n1 = (struct pai_node *)a; struct pai_node *n2 = (struct pai_node *)b; return n1->pmu < n2->pmu ? -1 : 1; } static void sort_painode(void) { util_list_sort(&pai_list, painode_cmp, NULL); } /* Check for hardware support and return false if not available. */ static bool have_support(enum pai_types t) { const char *sysfn = pai_type_sysfs(t); char *path = util_path_sysfs(PAI_PATH, sysfn); bool rc = true; if (!util_path_is_dir(path)) { warnx("No support for PAI %s facility", pai_type_name(t)); rc = false; } free(path); return rc; } /* * Check the argument for option -t. It must be a valid PAI counter set. * Exit when an invalid PAI counter set name has been specified. */ static void check_type_name(const char *type) { bool no_match = true; enum pai_types i; const char *fn; for (i = pai_type_crypto; i < pai_type_max; ++i) { fn = pai_type_name(i); if (!strcasecmp(fn, type)) { pai_types_show |= (1 << i); no_match = false; } } if (no_match) errx(EXIT_FAILURE, "Invalid argument for -t %s", type); } int main(int argc, char **argv) { int ch; util_list_init(&pai_list, struct pai_node, node); util_prg_init(&prg); util_opt_init(opt_vec, NULL); while ((ch = util_opt_getopt_long(argc, argv)) != -1) { switch (ch) { default: util_opt_print_parse_error(ch, argv); return EXIT_FAILURE; case 'h': util_prg_print_help(); util_opt_print_help(); return EXIT_SUCCESS; case 'v': util_prg_print_version(); return EXIT_SUCCESS; case 'n': numsort = true; break; case 't': check_type_name(optarg); break; } } /* Nothing specified, show all PAI counters */ if (!pai_types_show) pai_types_show = (1 << pai_type_crypto) | (1 << pai_type_nnpa); /* Check for hardware support */ for (enum pai_types i = pai_type_crypto; i < pai_type_max; ++i) { if ((pai_types_show & (1 << i))) { if (!have_support(i)) pai_types_show &= ~(1 << i); else make_painode(i); } } sort_painode(); show_painode(); free_painode(); return ch; }