From ca2e17833fa81026f0dfcf35b6b01a5f9f6c0806 Mon Sep 17 00:00:00 2001 Message-Id: In-Reply-To: References: From: Kamalesh Babulal Date: Tue, 21 Apr 2020 07:05:00 -0500 Subject: [PATCH V4 01/14] common/cpu_info_helpers: library to capture CPU information To: powerpc-utils-devel@googlegroups.com Cc: Tyrel Datwyler , Nathan Lynch , Naveen N . Rao , Gautham R . Shenoy , Vasant Hegde ppc64_cpu tool does a lots of heavy lifting in finding the information about the underlying CPU topology and various other details. These details might be required by other tools, probing the underlying platform for similar details. Moving such functions to a common library, allows sharing across ppc64_cpu and others tools, instead of multiple implementation of functions capturing same information. Signed-off-by: Kamalesh Babulal --- Makefile.am | 4 +- src/common/cpu_info_helpers.c | 290 ++++++++++++++++++++++++++++++++++ src/common/cpu_info_helpers.h | 46 ++++++ src/ppc64_cpu.c | 227 +------------------------- 4 files changed, 346 insertions(+), 221 deletions(-) create mode 100644 src/common/cpu_info_helpers.c create mode 100644 src/common/cpu_info_helpers.h diff --git a/Makefile.am b/Makefile.am index a272008..1f0b4f6 100644 --- a/Makefile.am +++ b/Makefile.am @@ -63,6 +63,8 @@ pseries_platform_SOURCES = src/common/pseries_platform.c src/common/pseries_plat librtas_error_SOURCES = src/common/librtas_error.c src/common/librtas_error.h +cpu_info_helpers_SOURCES = src/common/cpu_info_helpers.c src/common/cpu_info_helpers.h + src_nvram_SOURCES = src/nvram.c src/nvram.h $(pseries_platform_SOURCES) src_nvram_LDADD = -lz @LIBDL@ @@ -70,7 +72,7 @@ src_lsprop_SOURCES = src/lsprop.c $(pseries_platform_SOURCES) src_lparstat_SOURCES = src/lparstat.c src/lparstat.h $(pseries_platform_SOURCES) -src_ppc64_cpu_SOURCES = src/ppc64_cpu.c $(pseries_platform_SOURCES) +src_ppc64_cpu_SOURCES = src/ppc64_cpu.c $(pseries_platform_SOURCES) $(cpu_info_helpers_SOURCES) src_ppc64_cpu_LDADD = -lpthread diff --git a/src/common/cpu_info_helpers.c b/src/common/cpu_info_helpers.c new file mode 100644 index 0000000..0bb1dfe --- /dev/null +++ b/src/common/cpu_info_helpers.c @@ -0,0 +1,290 @@ +/** + * @file cpu_info_helpers.c + * @brief Common routines to capture cpu information + * + * Copyright (c) 2007, 2020 International Business Machines + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * @author Anton Blanchard + * @author Kamalesh Babulal + */ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cpu_info_helpers.h" + +int get_attribute(char *path, const char *fmt, int *value) +{ + FILE *fp; + int rc; + + rc = access(path, F_OK); + if (rc) + return -1; + + fp = fopen(path, "r"); + if (!fp) + return -1; + + rc = fscanf(fp, fmt, value); + fclose(fp); + + if (rc == EOF) + return -1; + + return 0; +} + +static int test_sysattr(char *attribute, int perms, int threads_in_system) +{ + char path[SYSFS_PATH_MAX]; + int i; + + for (i = 0; i < threads_in_system; i++) { + sprintf(path, SYSFS_CPUDIR"/%s", i, attribute); + if (access(path, F_OK)) + continue; + + if (access(path, perms)) + return 0; + } + + return 1; +} + +int __sysattr_is_readable(char *attribute, int threads_in_system) +{ + return test_sysattr(attribute, R_OK, threads_in_system); +} + +int __sysattr_is_writeable(char *attribute, int threads_in_system) +{ + return test_sysattr(attribute, W_OK, threads_in_system); +} + +int cpu_online(int thread) +{ + char path[SYSFS_PATH_MAX]; + int rc, online; + + sprintf(path, SYSFS_CPUDIR"/online", thread); + rc = get_attribute(path, "%d", &online); + + /* This attribute does not exist in kernels without hotplug enabled */ + if (rc && errno == ENOENT) + return 1; + + if (rc || !online) + return 0; + + return 1; +} + +int is_subcore_capable(void) +{ + return access(SYSFS_SUBCORES, F_OK) == 0; +} + +int num_subcores(void) +{ + int rc, subcores; + + rc = get_attribute(SYSFS_SUBCORES, "%d", &subcores); + if (rc) + return -1; + return subcores; +} + +int get_cpu_info(int *_threads_per_cpu, int *_cpus_in_system, + int *_threads_in_system) +{ + DIR *d; + struct dirent *de; + int first_cpu = 1; + int rc; + int subcores; + int threads_in_system; + int threads_per_cpu = 0; + int cpus_in_system = 0; + + d = opendir("/proc/device-tree/cpus"); + if (!d) + return -1; + + while ((de = readdir(d)) != NULL) { + if (!strncmp(de->d_name, "PowerPC", 7)) { + if (first_cpu) { + struct stat sbuf; + char path[128]; + + snprintf(path, sizeof(path), INTSERV_PATH, de->d_name); + rc = stat(path, &sbuf); + if (!rc) + threads_per_cpu = sbuf.st_size / 4; + + first_cpu = 0; + } + + cpus_in_system++; + } + } + + closedir(d); + threads_in_system = cpus_in_system * threads_per_cpu; + + subcores = num_subcores(); + if (is_subcore_capable() && subcores > 0) { + threads_per_cpu /= subcores; + cpus_in_system *= subcores; + } + + *_threads_per_cpu = threads_per_cpu; + *_threads_in_system = threads_in_system; + *_cpus_in_system = cpus_in_system; + + return 0; +} + +int __is_smt_capable(int threads_in_system) +{ + struct stat sb; + char path[SYSFS_PATH_MAX]; + int i; + + for (i = 0; i < threads_in_system; i++) { + sprintf(path, SYSFS_CPUDIR"/smt_snooze_delay", i); + if (stat(path, &sb)) + continue; + return 1; + } + + return 0; +} + +int __get_one_smt_state(int core, int threads_per_cpu) +{ + int primary_thread = core * threads_per_cpu; + int smt_state = 0; + int i; + + if (!__sysattr_is_readable("online", threads_per_cpu)) { + perror("Cannot retrieve smt state"); + return -2; + } + + for (i = 0; i < threads_per_cpu; i++) { + smt_state += cpu_online(primary_thread + i); + } + + return smt_state; +} + +static void print_cpu_list(const cpu_set_t *cpuset, int cpuset_size, + int cpus_in_system) +{ + int core; + const char *comma = ""; + + for (core = 0; core < cpus_in_system; core++) { + int begin = core; + if (CPU_ISSET_S(core, cpuset_size, cpuset)) { + while (CPU_ISSET_S(core+1, cpuset_size, cpuset)) + core++; + + if (core > begin) + printf("%s%d-%d", comma, begin, core); + else + printf("%s%d", comma, core); + comma = ","; + } + } +} + +int __do_smt(bool numeric, int cpus_in_system, int threads_per_cpu, + bool print_smt_state) +{ + int thread, c, smt_state = 0; + cpu_set_t **cpu_states = NULL; + int cpu_state_size = CPU_ALLOC_SIZE(cpus_in_system); + int start_cpu = 0, stop_cpu = cpus_in_system; + int rc = 0; + + cpu_states = (cpu_set_t **)calloc(threads_per_cpu, sizeof(cpu_set_t)); + if (!cpu_states) + return -ENOMEM; + + for (thread = 0; thread < threads_per_cpu; thread++) { + cpu_states[thread] = CPU_ALLOC(cpus_in_system); + CPU_ZERO_S(cpu_state_size, cpu_states[thread]); + } + + for (c = start_cpu; c < stop_cpu; c++) { + int threads_online = __get_one_smt_state(c, threads_per_cpu); + + if (threads_online < 0) { + rc = threads_online; + goto cleanup_get_smt; + } + if (threads_online) + CPU_SET_S(c, cpu_state_size, + cpu_states[threads_online - 1]); + } + + for (thread = 0; thread < threads_per_cpu; thread++) { + if (CPU_COUNT_S(cpu_state_size, cpu_states[thread])) { + if (smt_state == 0) + smt_state = thread + 1; + else if (smt_state > 0) + smt_state = -1; /* mix of SMT modes */ + } + } + + if (!print_smt_state) + return smt_state; + + if (smt_state == 1) { + if (numeric) + printf("SMT=1\n"); + else + printf("SMT is off\n"); + } else if (smt_state == -1) { + for (thread = 0; thread < threads_per_cpu; thread++) { + if (CPU_COUNT_S(cpu_state_size, + cpu_states[thread])) { + printf("SMT=%d: ", thread + 1); + print_cpu_list(cpu_states[thread], + cpu_state_size, cpus_in_system); + printf("\n"); + } + } + } else { + printf("SMT=%d\n", smt_state); + } + +cleanup_get_smt: + for (thread = 0; thread < threads_per_cpu; thread++) + CPU_FREE(cpu_states[thread]); + + return rc; +} diff --git a/src/common/cpu_info_helpers.h b/src/common/cpu_info_helpers.h new file mode 100644 index 0000000..8f09d79 --- /dev/null +++ b/src/common/cpu_info_helpers.h @@ -0,0 +1,46 @@ +/** + * @file cpu_info_helpers.h + * @brief Header of common routines to capture cpu information + * + * Copyright (c) 2007, 2020 International Business Machines + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * @author Anton Blanchard + * @author Kamalesh Babulal + */ +#ifndef _CPU_INFO_HELPERS_H +#define _CPU_INFO_HELPERS_H + +#define SYSFS_CPUDIR "/sys/devices/system/cpu/cpu%d" +#define SYSFS_SUBCORES "/sys/devices/system/cpu/subcores_per_core" +#define INTSERV_PATH "/proc/device-tree/cpus/%s/ibm,ppc-interrupt-server#s" + +#define SYSFS_PATH_MAX 128 + +extern int __sysattr_is_readable(char *attribute, int threads_in_system); +extern int __sysattr_is_writeable(char *attribute, int threads_in_system); +extern int cpu_online(int thread); +extern int is_subcore_capable(void); +extern int num_subcores(void); +extern int get_attribute(char *path, const char *fmt, int *value); +extern int get_cpu_info(int *threads_per_cpu, int *cpus_in_system, + int *threads_in_system); +extern int __is_smt_capable(int threads_in_system); +extern int __get_one_smt_state(int core, int threads_per_cpu); +extern int __do_smt(bool numeric, int cpus_in_system, int threads_per_cpu, + bool print_smt_state); + +#endif /* CPU_INFO_H */ diff --git a/src/ppc64_cpu.c b/src/ppc64_cpu.c index 6d02235..cacbf1a 100644 --- a/src/ppc64_cpu.c +++ b/src/ppc64_cpu.c @@ -47,15 +47,12 @@ #endif #include +#include "cpu_info_helpers.h" #define PPC64_CPU_VERSION "1.2" -#define SYSFS_CPUDIR "/sys/devices/system/cpu/cpu%d" -#define SYSFS_SUBCORES "/sys/devices/system/cpu/subcores_per_core" #define DSCR_DEFAULT_PATH "/sys/devices/system/cpu/dscr_default" -#define INTSERV_PATH "/proc/device-tree/cpus/%s/ibm,ppc-interrupt-server#s" -#define SYSFS_PATH_MAX 128 #define MAX_NR_CPUS 1024 #define DIAGNOSTICS_RUN_MODE 42 #define CPU_OFFLINE -1 @@ -80,54 +77,14 @@ static int threads_in_system = 0; static int do_info(void); -static int test_sysattr(char *attribute, int perms) -{ - char path[SYSFS_PATH_MAX]; - int i; - - for (i = 0; i < threads_in_system; i++) { - sprintf(path, SYSFS_CPUDIR"/%s", i, attribute); - if (access(path, F_OK)) - continue; - - if (access(path, perms)) - return 0; - } - - return 1; -} - static int sysattr_is_readable(char *attribute) { - return test_sysattr(attribute, R_OK); + return __sysattr_is_readable(attribute, threads_in_system); } static int sysattr_is_writeable(char *attribute) { - return test_sysattr(attribute, W_OK); -} - -static int get_attribute(char *path, const char *fmt, int *value) -{ - FILE *fp; - int rc; - - rc = access(path, F_OK); - if (rc) - return -1; - - - fp = fopen(path, "r"); - if (!fp) - return -1; - - rc = fscanf(fp, fmt, value); - fclose(fp); - - if (rc == EOF) - return -1; - - return 0; + return __sysattr_is_writeable(attribute, threads_in_system); } static int set_attribute(const char *path, const char *fmt, int value) @@ -156,24 +113,6 @@ close: return rc; } -static int cpu_online(int thread) -{ - char path[SYSFS_PATH_MAX]; - int rc, online; - - sprintf(path, SYSFS_CPUDIR"/online", thread); - rc = get_attribute(path, "%d", &online); - - /* This attribute does not exist in kernels without hotplug enabled */ - if (rc && errno == ENOENT) - return 1; - - if (rc || !online) - return 0; - - return 1; -} - static int get_system_attribute(char *attribute, const char *fmt, int *value, int *inconsistent) { @@ -316,93 +255,14 @@ static int offline_thread(const char *path) return set_attribute(path, "%d", 0); } -static int is_subcore_capable(void) -{ - return access(SYSFS_SUBCORES, F_OK) == 0; -} - -static int num_subcores(void) -{ - int rc, subcores; - rc = get_attribute(SYSFS_SUBCORES, "%d", &subcores); - if (rc) - return -1; - return subcores; -} - -static int get_cpu_info(void) -{ - DIR *d; - struct dirent *de; - int first_cpu = 1; - int rc; - int subcores; - - d = opendir("/proc/device-tree/cpus"); - if (!d) - return -1; - - while ((de = readdir(d)) != NULL) { - if (!strncmp(de->d_name, "PowerPC", 7)) { - if (first_cpu) { - struct stat sbuf; - char path[128]; - - sprintf(path, INTSERV_PATH, de->d_name); - rc = stat(path, &sbuf); - if (!rc) - threads_per_cpu = sbuf.st_size / 4; - - first_cpu = 0; - } - - cpus_in_system++; - } - } - - closedir(d); - threads_in_system = cpus_in_system * threads_per_cpu; - - subcores = num_subcores(); - if (is_subcore_capable() && subcores > 0) { - threads_per_cpu /= subcores; - cpus_in_system *= subcores; - } - return 0; -} - static int is_smt_capable(void) { - struct stat sb; - char path[SYSFS_PATH_MAX]; - int i; - - for (i = 0; i < threads_in_system; i++) { - sprintf(path, SYSFS_CPUDIR"/smt_snooze_delay", i); - if (stat(path, &sb)) - continue; - return 1; - } - - return 0; + return __is_smt_capable(threads_in_system); } static int get_one_smt_state(int core) { - int primary_thread = core * threads_per_cpu; - int smt_state = 0; - int i; - - if (!sysattr_is_readable("online")) { - perror("Cannot retrieve smt state"); - return -2; - } - - for (i = 0; i < threads_per_cpu; i++) { - smt_state += cpu_online(primary_thread + i); - } - - return smt_state; + return __get_one_smt_state(core, threads_per_cpu); } static int get_smt_state(void) @@ -513,26 +373,6 @@ static int is_dscr_capable(void) return 0; } -void print_cpu_list(const cpu_set_t *cpuset, int cpuset_size) -{ - int core; - const char *comma = ""; - - for (core = 0; core < cpus_in_system; core++) { - int begin = core; - if (CPU_ISSET_S(core, cpuset_size, cpuset)) { - while (CPU_ISSET_S(core+1, cpuset_size, cpuset)) - core++; - - if (core > begin) - printf("%s%d-%d", comma, begin, core); - else - printf("%s%d", comma, core); - comma = ","; - } - } -} - static int do_smt(char *state, bool numeric) { int rc = 0; @@ -547,60 +387,7 @@ static int do_smt(char *state, bool numeric) } if (!state) { - int thread, c; - cpu_set_t *cpu_states[threads_per_cpu]; - int cpu_state_size = CPU_ALLOC_SIZE(cpus_in_system); - int start_cpu = 0, stop_cpu = cpus_in_system; - - for (thread = 0; thread < threads_per_cpu; thread++) { - cpu_states[thread] = CPU_ALLOC(cpus_in_system); - CPU_ZERO_S(cpu_state_size, cpu_states[thread]); - } - - for (c = start_cpu; c < stop_cpu; c++) { - int threads_online = get_one_smt_state(c); - - if (threads_online < 0) { - rc = threads_online; - goto cleanup_get_smt; - } - if (threads_online) - CPU_SET_S(c, cpu_state_size, - cpu_states[threads_online - 1]); - } - - for (thread = 0; thread < threads_per_cpu; thread++) { - if (CPU_COUNT_S(cpu_state_size, cpu_states[thread])) { - if (smt_state == 0) - smt_state = thread + 1; - else if (smt_state > 0) - smt_state = -1; /* mix of SMT modes */ - } - } - - if (smt_state == 1) { - if (numeric) - printf("SMT=1\n"); - else - printf("SMT is off\n"); - } else if (smt_state == -1) { - for (thread = 0; thread < threads_per_cpu; thread++) { - if (CPU_COUNT_S(cpu_state_size, - cpu_states[thread])) { - printf("SMT=%d: ", thread + 1); - print_cpu_list(cpu_states[thread], - cpu_state_size); - printf("\n"); - } - } - } else { - printf("SMT=%d\n", smt_state); - } - -cleanup_get_smt: - for (thread = 0; thread < threads_per_cpu; thread++) - CPU_FREE(cpu_states[thread]); - + rc = __do_smt(numeric, cpus_in_system, threads_per_cpu, true); } else { if (!strcmp(state, "on")) smt_state = threads_per_cpu; @@ -1510,7 +1297,7 @@ int main(int argc, char *argv[]) return 0; } - rc = get_cpu_info(); + rc = get_cpu_info(&threads_per_cpu, &cpus_in_system, &threads_in_system); if (rc) { printf("Could not determine system cpu/thread information.\n"); return rc; -- 2.25.3