From c7fe21b01956e5c630dd32c6c2518dbc8e08a9cc Mon Sep 17 00:00:00 2001 From: Thomas Richter Date: Thu, 23 Dec 2021 13:02:14 +0100 Subject: [PATCH] libcpumf: Create library libcpumf for CPU Measurement functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a function to detect the existence of the CPU Measurement Sampling Facility on a system. Add a function which extracts the CPU Measurement Sampling Facility characteristics on a system, such as - minimum sample speed - maximum sample speed - current CPU speed - basic sample size in bytes - diagnostic sample size in bytes Add a function to detect the existence of the CPU Measurement Sampling Facility on a system and return the current sampling buffer management characteristics, such as: - minimum supported sampling buffer size - maximum supported sampling buffer size Add a function to detect the existence of the CPU Measurement Counting Facility on a system. Add a function which extracts the CPU Measurement Counting Facility characteristics on a system, such as - counter first version number - counter second version number - counter set authorization level Add a function the returm the PMU type number of a CPU Measurement Facility device driver. These numbers may vary between boots. Signed-off-by: Thomas Richter Acked-by: Sumanth Korikkar Signed-off-by: Jan Höppner --- Makefile | 2 +- include/lib/libcpumf.h | 106 +++++++++++++++++++++++++++ libcpumf/Makefile | 19 +++++ libcpumf/libcpumf_cpuset.c | 75 +++++++++++++++++++ libcpumf/libcpumf_example.c | 76 +++++++++++++++++++ libcpumf/libcpumf_pmutype.c | 33 +++++++++ libcpumf/libcpumf_support.c | 141 ++++++++++++++++++++++++++++++++++++ 7 files changed, 451 insertions(+), 1 deletion(-) create mode 100644 include/lib/libcpumf.h create mode 100644 libcpumf/Makefile create mode 100644 libcpumf/libcpumf_cpuset.c create mode 100644 libcpumf/libcpumf_example.c create mode 100644 libcpumf/libcpumf_pmutype.c create mode 100644 libcpumf/libcpumf_support.c diff --git a/Makefile b/Makefile index 73acb3c3..31c40659 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,7 @@ include common.mak # BASELIB_DIRS = libutil libseckey LIB_DIRS = libvtoc libzds libdasd libvmdump libccw libvmcp libekmfweb \ - libkmipclient + libkmipclient libcpumf TOOL_DIRS = zipl zdump fdasd dasdfmt dasdview tunedasd \ tape390 osasnmpd qetharp ip_watcher qethconf scripts zconf \ vmconvert vmcp man mon_tools dasdinfo vmur cpuplugd ipl_tools \ diff --git a/include/lib/libcpumf.h b/include/lib/libcpumf.h new file mode 100644 index 00000000..06c34650 --- /dev/null +++ b/include/lib/libcpumf.h @@ -0,0 +1,106 @@ +/* Copyright IBM Corp. 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. + */ + +#ifndef LIBCPUMF_H +#define LIBCPUMF_H + +#include +#include + +#define S390_CPUMF_CF "/sys/devices/cpum_cf/" +#define S390_CPUMF_CFDIAG "/sys/devices/cpum_cf_diag/" +#define S390_CPUMF_SF "/sys/devices/cpum_sf/" +#define S390_CPUS_POSSIBLE "/sys/devices/system/cpu/possible" +#define S390_CPUS_ONLINE "/sys/devices/system/cpu/online" +#define S390_CPUMSF_BUFFERSZ "/sys/module/kernel/parameters/cpum_sfb_size" + +/** + * Read out the PMU type from a given file. + * + * Return the PMU type number assigned to this PMU by the kernel. This is + * a non zero number. + * If the PMU does not exist return -1 and set errno. + * + * @param[in] dirname Name of the event directory in sysfs + */ +int libcpumf_pmutype(const char *dirname); + +/** + * Read out the CPU list from a given file name, for example from files + * /sys/devices/system/cpu/online or /sys/devices/system/cpu/possible. + * + * Return the cpu_set_t created from parsing the CPU list in the second + * parameter. + * Return code of zero indicates proper conversion and -1 indicates an + * error. + * + * @param[in] buffer Comma separated string of a CPU list + * @param[in] filename Name of a sysfs CPU list file name + * @param[out] mask Converted buffer into cpu_set_t mask structure + */ +int libcpumf_cpuset(const char *buffer, cpu_set_t *mask); +int libcpumf_cpuset_fn(const char *filename, cpu_set_t *mask); + +/** + * Read CPU Measurement Counting Facility hardware information + * + * Return true if CPU Measurement Counter facility information has been + * retrieved and is valid. + * + * Return false if the information could not be extracted from the file. + * + * @param[out] cfvn Contains CPUMF counter first version number + * @param[out] csvn Contains CPUMF counter second version number + * @param[out] auth Contains CPUMF counter set authorization level + */ +bool libcpumf_cpumcf_info(int *cfvn, int *csvn, int *auth); + +/** + * Return true if CPU Measurement Counter Facility is available. + */ +bool libcpumf_have_cpumcf(void); + +/** + * Read CPU Measurement Sampling Facility hardware information + * + * Read all necessary information from /sysfs file /proc/service_levels + * to return CPU Measurement Counter Sampling facility information + * characteristics. + * Return true on success and false when the data can not be retrieved. + * + * @param[out] min Minimum supported sampling interval + * @param[out] max Maximum supported sampling interval + * @param[out] speed Current CPU speed, number of CPU cylces per + * microsecond + * @param[out] basic_sz Basic sample size in bytes + * @param[out] diag_sz Diagnostic sample size in bytes + */ +bool libcpumf_cpumsf_info(unsigned long *min, unsigned long *max, + unsigned long *speed, int *basic_sz, int *diag_sz); + +/** + * Return true if CPU Measurement Sampling Facility is available. + */ +bool libcpumf_have_cpumsf(void); + +/** + * Return true if CPU Measurement Sampling Facility buffer sizes are + * available. + */ +bool libcpumf_have_sfb(void); + +/** + * Read CPU Measurement Sampling Facility supported sampling buffer sizes. + * + * Return the minimum and maximum CPU Measurement sampling facitity buffer + * sizes supported. + * Return true on success and false otherwise. + * + * @param[out] min Minimum supported sampling buffer size + * @param[out] max Maximum supported sampling buffer size + */ +bool libcpumf_sfb_info(unsigned long *min, unsigned long *max); +#endif diff --git a/libcpumf/Makefile b/libcpumf/Makefile new file mode 100644 index 00000000..b7f27f48 --- /dev/null +++ b/libcpumf/Makefile @@ -0,0 +1,19 @@ +include ../common.mak + +lib = libcpumf.a + +examples = libcpumf_example + +all: $(lib) +examples: $(lib) $(examples) + +objects = libcpumf_pmutype.o libcpumf_cpuset.o libcpumf_support.o + +$(lib): $(objects) + +install: all + +libcpumf_example: libcpumf_example.o $(lib) + +clean: + rm -f *.o $(lib) $(examples) diff --git a/libcpumf/libcpumf_cpuset.c b/libcpumf/libcpumf_cpuset.c new file mode 100644 index 00000000..ae8b244f --- /dev/null +++ b/libcpumf/libcpumf_cpuset.c @@ -0,0 +1,75 @@ +/* Copyright IBM Corp. 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 +#include +#include +#include +#include + +#include "lib/libcpumf.h" + +int libcpumf_cpuset(const char *parm, cpu_set_t *mask) +{ + char *cp, *buffer = strdup(parm); + int to, from, rc; + + if (!buffer) /* Errno set to ENOMEM */ + return -1; + /* Check for invalid characters, such as 11.12 instead 11-12 + * but allow blanks and newline. Newline is appended + * when the string is taken from sysfs files, for example + * /sys/devices/system/cpu/online + */ + if (strspn(buffer, "0123456789-,\n ") != strlen(buffer)) { + errno = EINVAL; + return -1; + } + CPU_ZERO(mask); + for (; (cp = strtok(buffer, ",")); buffer = NULL) { + char *dash = strchr(cp, '-'); /* Range character? */ + bool is_ok; + + if (dash) { + rc = sscanf(cp, "%d-%d", &from, &to); + is_ok = rc == 2; + } else { + rc = sscanf(cp, "%d", &to); + from = to; + is_ok = rc == 1; + } + if (!is_ok) { + errno = ERANGE; + rc = -1; + goto out; + } + for (; from <= to; ++from) + CPU_SET(from, mask); + } + rc = 0; +out: + free(buffer); + return rc; +} + +int libcpumf_cpuset_fn(const char *filename, cpu_set_t *mask) +{ + char *txt = NULL; + ssize_t ret = -1; + size_t len = 0; + FILE *fp; + + fp = fopen(filename, "r"); + if (!fp) + return ret; + /* Read out file, one line expected */ + ret = getline(&txt, &len, fp); + fclose(fp); + if (ret > 0) + ret = libcpumf_cpuset(txt, mask); + free(txt); + return ret; +} diff --git a/libcpumf/libcpumf_example.c b/libcpumf/libcpumf_example.c new file mode 100644 index 00000000..122cce69 --- /dev/null +++ b/libcpumf/libcpumf_example.c @@ -0,0 +1,76 @@ +/* Copyright IBM Corp. 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 +#include +#include + +#include "lib/libcpumf.h" + +int main(void) +{ + unsigned long min, max, speed, sfb_min, sfb_max; + int rc, pmu, cfvn, csvn, auth; + cpu_set_t set; + + pmu = libcpumf_pmutype(S390_CPUMF_CF); + if (pmu >= 0) + printf("PMU %stype %d\n", S390_CPUMF_CF, pmu); + else + printf("PMU %stype error %d\n", S390_CPUMF_CF, errno); + pmu = libcpumf_pmutype(S390_CPUMF_SF); + if (pmu >= 0) + printf("PMU %stype %d\n", S390_CPUMF_SF, pmu); + else + printf("PMU %stype error %d\n", S390_CPUMF_SF, errno); + pmu = libcpumf_pmutype(S390_CPUMF_CFDIAG); + if (pmu >= 0) + printf("PMU %stype %d\n", S390_CPUMF_CFDIAG, pmu); + else + printf("PMU %stype error %d\n", S390_CPUMF_CFDIAG, errno); + + rc = libcpumf_cpuset_fn(S390_CPUS_ONLINE, &set); + if (rc == 0) { + puts("Online CPUs:"); + for (int i = 0; i < CPU_SETSIZE; ++i) + if (CPU_ISSET(i, &set)) + printf("%d ", i); + putchar('\n'); + } + rc = libcpumf_cpuset("0-7,9,11-12 ,15", &set); + if (rc == 0) { + puts("String CPUs:"); + for (int i = 0; i < CPU_SETSIZE; ++i) + if (CPU_ISSET(i, &set)) + printf("%d ", i); + putchar('\n'); + } else { + printf("libcpumf_cpuset input invalid %d\n", errno); + } + + printf("CPUMCF support %d\n", libcpumf_have_cpumcf()); + rc = libcpumf_cpumcf_info(&cfvn, &csvn, &auth); + printf("libcpumf_cpumcf_info %d", rc); + if (rc) + printf(" cfvn %d csvn %d authorization %#x", cfvn, csvn, auth); + putchar('\n'); + + printf("CPUMSF support %d\n", libcpumf_have_cpumsf()); + rc = libcpumf_cpumsf_info(&min, &max, &speed, &cfvn, &csvn); + printf("libcpumf_cpumsf_info %d", rc); + if (rc) + printf(" min %ld max %ld speed %#lx basic %d diag %d", min, + max, speed, cfvn, csvn); + putchar('\n'); + + printf("CPUMSF have sfb %d\n", libcpumf_have_sfb()); + rc = libcpumf_sfb_info(&sfb_min, &sfb_max); + printf("libcpumf_sfb_info %d", rc); + if (rc) + printf(" sfb_min %lu sfb_max %lu", sfb_min, sfb_max); + putchar('\n'); + return EXIT_SUCCESS; +} diff --git a/libcpumf/libcpumf_pmutype.c b/libcpumf/libcpumf_pmutype.c new file mode 100644 index 00000000..e89e3ebd --- /dev/null +++ b/libcpumf/libcpumf_pmutype.c @@ -0,0 +1,33 @@ +/* Copyright IBM Corp. 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 +#include +#include + +#include "lib/libcpumf.h" + +int libcpumf_pmutype(const char *dirname) +{ + FILE *file; + char *fn; + int ret; + + ret = asprintf(&fn, "%s/type", dirname); + if (ret == -1) /* No memory, errno set */ + return ret; + file = fopen(fn, "r"); + free(fn); + ret = -1; /* Errno set on file open error */ + if (file) { + /* Read out a single number from that file */ + if (fscanf(file, "%u", &ret) != 1) + /* Unexpected format error, set errno */ + errno = -ERANGE; + fclose(file); + } + return ret; +} diff --git a/libcpumf/libcpumf_support.c b/libcpumf/libcpumf_support.c new file mode 100644 index 00000000..c9ba6972 --- /dev/null +++ b/libcpumf/libcpumf_support.c @@ -0,0 +1,141 @@ +/* Copyright IBM Corp. 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 +#include +#include +#include +#include +#include +#include + +#include "lib/libcpumf.h" + +#define SERVICELEVEL "/proc/service_levels" + +bool libcpumf_cpumcf_info(int *cfvn, int *csvn, int *auth) +{ + char *linep = NULL; + bool rc = false; + size_t line_sz; + ssize_t nbytes; + FILE *fp; + + fp = fopen(SERVICELEVEL, "r"); + if (!fp) + err(EXIT_FAILURE, SERVICELEVEL); + + while ((nbytes = getline(&linep, &line_sz, fp)) != EOF) { + if (!strncmp(linep, "CPU-MF: Counter facility:", 25)) { + int cnt = sscanf(linep, "CPU-MF: Counter facility:" + " version=%d.%d authorization=%x", + cfvn, csvn, auth); + if (cnt != 3) { + warnx("Can not parse line %s", linep); + goto out; + } + rc = true; + break; + } + } +out: + fclose(fp); + free(linep); + + return rc; +} + +bool libcpumf_have_cpumcf(void) +{ + int cfvn, csvn, auth; + + return libcpumf_cpumcf_info(&cfvn, &csvn, &auth); +} + +bool libcpumf_cpumsf_info(unsigned long *min, unsigned long *max, + unsigned long *speed, int *basic_sz, int *diag_sz) +{ + char *linep = NULL; + bool rc = true; + size_t line_sz; + ssize_t nbytes; + int hit = 0; + FILE *fp; + + fp = fopen(SERVICELEVEL, "r"); + if (!fp) + err(EXIT_FAILURE, SERVICELEVEL); + + while ((nbytes = getline(&linep, &line_sz, fp)) != EOF) { + int ok; + + if (!strncmp(linep, "CPU-MF: Sampling facility: min", 30)) { + ok = sscanf(linep, "CPU-MF: Sampling facility:" + " min_rate=%ld max_rate=%ld cpu_speed=%ld", + min, max, speed); + if (ok != 3) { + warnx("Can not parse line %s", linep); + goto out; + } + hit += 1; + } + if (!strncmp(linep, "CPU-MF: Sampling facility: mode=basic", 37)) { + ok = sscanf(linep, "CPU-MF: Sampling facility:" + " mode=basic sample_size=%u", basic_sz); + if (ok != 1) { + warnx("Can not parse line %s", linep); + goto out; + } + hit += 1; + } + if (!strncmp(linep, "CPU-MF: Sampling facility: mode=diag", 36)) { + ok = sscanf(linep, "CPU-MF: Sampling facility:" + " mode=diagnostic sample_size=%u", + diag_sz); + if (ok != 1) { + warnx("Can not parse line %s", linep); + goto out; + } + hit += 1; + } + } +out: + fclose(fp); + free(linep); + if (hit != 3) + rc = false; + + return rc; +} + +bool libcpumf_have_cpumsf(void) +{ + unsigned long a, b, c; + int basic_sz, diag_sz; + + return libcpumf_cpumsf_info(&a, &b, &c, &basic_sz, &diag_sz); +} + +bool libcpumf_have_sfb(void) +{ + unsigned long a, b; + + return libcpumf_sfb_info(&a, &b); +} + +bool libcpumf_sfb_info(unsigned long *min, unsigned long *max) +{ + int rc = false; + FILE *fp; + + fp = fopen(S390_CPUMSF_BUFFERSZ, "r"); + if (!fp) + err(EXIT_FAILURE, "%s", S390_CPUMSF_BUFFERSZ); + if (fscanf(fp, "%lu,%lu", min, max) == 2) + rc = true; + fclose(fp); + return rc; +}