* [MODERATED] [PATCH 0/1] SMTCTL 0
@ 2018-06-07 20:54 Andi Kleen
2018-06-07 20:54 ` [MODERATED] [PATCH 1/1] SMTCTL 1 Andi Kleen
0 siblings, 1 reply; 2+ messages in thread
From: Andi Kleen @ 2018-06-07 20:54 UTC (permalink / raw)
To: speck
Here's an alternative proposal for SMT disabling.
I reimplemented my script as a C tool and put it into tools/*
# smtctl --enable
# smtctl --disable
# smtctl --print
Advantages:
- No kernel changes (although it would benefit from a minor one,
see the commit)
- Much easier to use at runtime than opaque sysfs
- Encourages the right use model of only switching SMT state
when it is actually needed and based on a conscious decision,
not unnecessarily or globally.
Andi Kleen (1):
tools/smtctl: Add smtctl to enable/disable SMT at runtime
tools/smtctl/Makefile | 10 ++
tools/smtctl/smtctl.c | 257 ++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 267 insertions(+)
create mode 100644 tools/smtctl/Makefile
create mode 100644 tools/smtctl/smtctl.c
--
2.14.3
^ permalink raw reply [flat|nested] 2+ messages in thread
* [MODERATED] [PATCH 1/1] SMTCTL 1
2018-06-07 20:54 [MODERATED] [PATCH 0/1] SMTCTL 0 Andi Kleen
@ 2018-06-07 20:54 ` Andi Kleen
0 siblings, 0 replies; 2+ messages in thread
From: Andi Kleen @ 2018-06-07 20:54 UTC (permalink / raw)
To: speck
Add a new tool called "smtctl" that allows to enable/disable SMT from
the command line.
It supports --enable, --disable, --print, and a filter --cpus
to only apply enable or disable to a subset of CPUs.
Open issue:
- The filtering only works for online CPUs because the kernel
does not preserve the topology information for offlined CPUs.
This could be fixed in the kernel.
Signed-off-by: Andi Kleen <ak@linux.intel.com>
---
tools/smtctl/Makefile | 10 ++
tools/smtctl/smtctl.c | 257 ++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 267 insertions(+)
create mode 100644 tools/smtctl/Makefile
create mode 100644 tools/smtctl/smtctl.c
diff --git a/tools/smtctl/Makefile b/tools/smtctl/Makefile
new file mode 100644
index 000000000000..6457f5368e32
--- /dev/null
+++ b/tools/smtctl/Makefile
@@ -0,0 +1,10 @@
+PREFIX := /usr/local
+CFLAGS := -g -Wall -O2
+
+all: smtctl
+
+clean:
+ rm -f smtctl smtctl.o
+
+install:
+ cp smtctl ${PREFIX}/bin
diff --git a/tools/smtctl/smtctl.c b/tools/smtctl/smtctl.c
new file mode 100644
index 000000000000..d0100f415e52
--- /dev/null
+++ b/tools/smtctl/smtctl.c
@@ -0,0 +1,257 @@
+/* Enable and disable SMT */
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright 2018 Intel Corporation. Author: Andi Kleen */
+#define _GNU_SOURCE 1
+#include <getopt.h>
+#include <unistd.h>
+#include <glob.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdarg.h>
+
+struct option opts[] = {
+ {"enable", no_argument, NULL, 'e' },
+ {"disable", no_argument, NULL, 'd' },
+ {"print", no_argument, NULL, 'p' },
+ {"cpus", required_argument, NULL, 'c' },
+ {"verbose", no_argument, NULL, 'v' },
+ {}
+};
+
+struct cpu {
+ int core;
+ int pkg;
+ int thread;
+ bool filter;
+ int num_online;
+ int online;
+};
+
+struct cpu *cpus;
+int num_cpus;
+bool verbose;
+
+
+static int match_core(int a, int b)
+{
+ return cpus[a].pkg == cpus[b].pkg && cpus[a].core == cpus[b].core;
+}
+
+int get_cpu(char *p)
+{
+ char *s = strstr(p, "cpu/cpu");
+ if (s)
+ return strtoul(s + 7, NULL, 10);
+ assert(0);
+}
+
+void writefile(char *val, char *fmt, ...)
+{
+ char *fn;
+ va_list ap;
+ va_start(ap, fmt);
+ vasprintf(&fn, fmt, ap);
+ va_end(ap);
+ FILE *f = fopen(fn, "w");
+ if (!f) {
+ fprintf(stderr, "Cannot write %s: %s\n", fn, strerror(errno));
+ return;
+ }
+ fputs(val, f);
+ fclose(f);
+ free(fn);
+}
+
+int read_num(char *fmt, ...)
+{
+ char *fn;
+ va_list ap;
+ va_start(ap, fmt);
+ vasprintf(&fn, fmt, ap);
+ va_end(ap);
+ int res;
+ FILE *f = fopen(fn, "r");
+ if (!f || fscanf(f, "%d", &res) != 1) {
+ fprintf(stderr, "Cannot read %s: %s\n", fn, strerror(errno));
+ return -1;
+ }
+ free(fn);
+ fclose(f);
+ return res;
+}
+
+void read_topology(void)
+{
+ num_cpus = sysconf(_SC_NPROCESSORS_CONF);
+
+ cpus = calloc(num_cpus, sizeof(struct cpu));
+ if (!cpus)
+ exit(ENOMEM);
+ memset(cpus, -1, num_cpus * sizeof(struct cpu));
+
+ glob_t core_ids, packages;
+
+ memset(&core_ids, 0, sizeof(glob_t));
+ memset(&packages, 0, sizeof(glob_t));
+
+ if (glob("/sys/devices/system/cpu/cpu*/topology/core_id",
+ 0, NULL, &core_ids) ||
+ glob("/sys/devices/system/cpu/cpu*/topology/physical_package_id",
+ 0, NULL, &packages)) {
+ fprintf(stderr, "Cannot read cpu topology: %s\n", strerror(errno));
+ exit(1);
+ }
+
+ int i;
+ for (i = 0; i < core_ids.gl_pathc; i++) {
+ int cpu = get_cpu(core_ids.gl_pathv[i]);
+ cpus[cpu].core = read_num(core_ids.gl_pathv[i]);
+ }
+ for (i = 0; i < packages.gl_pathc; i++) {
+ int cpu = get_cpu(packages.gl_pathv[i]);
+ cpus[cpu].pkg = read_num(packages.gl_pathv[i]);
+ }
+
+ globfree(&core_ids);
+ globfree(&packages);
+
+ /* n^2 algorithm, but we assume the number of cpus stays reasonable */
+ for (i = 0; i < num_cpus; i++) {
+ int thread = 0;
+ int j;
+ for (j = 0; j < num_cpus; j++) {
+ if (cpus[i].pkg == -1)
+ continue;
+ if (match_core(i, j))
+ cpus[j].thread = thread++;
+ }
+ cpus[i].filter = true;
+ cpus[i].num_online = 0;
+ }
+}
+
+void apply_filter(char *filter)
+{
+ int i, j;
+ char *s;
+
+ for (i = 0; i < num_cpus; i++)
+ cpus[i].filter = false;
+ while ((s = strtok(filter, ",")) != NULL) {
+ unsigned cpu = strtoul(s, NULL, 0);
+ if (cpu >= num_cpus) {
+ fprintf(stderr, "filter cpu %d out of bounds\n", cpu);
+ continue;
+ }
+ for (j = 0; j < num_cpus; j++)
+ if (match_core(cpu, j))
+ cpus[j].filter = true;
+ filter = NULL;
+ }
+}
+
+void usage(void)
+{
+ fprintf(stderr, "Enable or disable SMT in CPUs\n"
+ "Usage: smtctl [--enable] [--disable] [--filter cpus]\n"
+ "--enable | -e Enable SMT on CPUs\n"
+ "--disable | -d Disable SMT on CPUs\n"
+ "--print | -p Print status\n"
+ "--verbose | -v Be verbose\n"
+ "--cpus | -c cpunum1,... Only affect CPUs in list\n");
+ exit(1);
+}
+
+int main(int ac, char **av)
+{
+ int i;
+ static char *aname[] = { "Enabling", "Disabling" }; /* Match the enum */
+ enum { ENABLE, DISABLE, PRINT, NONE } action = NONE;
+ char *filter = NULL;
+
+ int c;
+ while ((c = getopt_long(ac, av, "edc:v", opts, NULL)) != -1) {
+ switch (c) {
+ case 'e': /* on */
+ action = ENABLE;
+ break;
+
+ case 'd': /* off */
+ action = DISABLE;
+ break;
+
+ case 'p':
+ action = PRINT;
+ break;
+
+ case 'c': /* cpu */
+ filter = optarg;
+ break;
+
+ case 'v':
+ verbose = true;
+ break;
+
+ default:
+ usage();
+ }
+ }
+ if (optind > ac)
+ usage();
+ read_topology();
+ if (filter)
+ apply_filter(filter);
+
+ switch (action) {
+ case ENABLE:
+ case DISABLE:
+ for (i = 0; i < num_cpus; i++) {
+ if (!cpus[i].filter)
+ continue;
+ if (cpus[i].thread == 0)
+ continue;
+ if (verbose)
+ printf("%s cpu %d\n", aname[action], i);
+ writefile(action == ENABLE ? "1" : "0",
+ "/sys/devices/system/cpu/cpu%d/online", i);
+ }
+ break;
+ case PRINT:
+ for (i = 0; i < num_cpus; i++) {
+ if (!cpus[i].filter)
+ continue;
+ cpus[i].online = read_num("/sys/devices/system/cpu/cpu%d/online", i);
+ if (!cpus[i].online)
+ continue;
+ int j;
+ for (j = 0; j < num_cpus; j++)
+ if (match_core(i, j))
+ cpus[i].num_online++;
+ }
+
+ for (i = 0; i < num_cpus; i++) {
+ if (!cpus[i].filter)
+ continue;
+ printf("%3d: ", i);
+ if (cpus[i].num_online == 0)
+ printf("offline");
+ else if (cpus[i].num_online == 1)
+ printf("SMT:off");
+ else
+ printf("SMT:%d", cpus[i].num_online);
+ if (verbose)
+ printf(" package %2d core %2d thread %2d online %d",
+ cpus[i].pkg, cpus[i].core,
+ cpus[i].thread, cpus[i].online);
+ fputc('\n', stdout);
+ }
+ break;
+ default:
+ usage();
+ }
+ return 0;
+}
--
2.14.3
^ permalink raw reply related [flat|nested] 2+ messages in thread
end of thread, other threads:[~2018-06-07 20:54 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2018-06-07 20:54 [MODERATED] [PATCH 0/1] SMTCTL 0 Andi Kleen
2018-06-07 20:54 ` [MODERATED] [PATCH 1/1] SMTCTL 1 Andi Kleen
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox