From: Andi Kleen <ak@linux.intel.com>
To: speck@linutronix.de
Subject: [MODERATED] [PATCH 1/1] SMTCTL 1
Date: Thu, 7 Jun 2018 13:54:55 -0700 [thread overview]
Message-ID: <6c01a32871067d6ef888fede6371820f88fbbe01.1528404649.git.ak@linux.intel.com> (raw)
In-Reply-To: <cover.1528404649.git.ak@linux.intel.com>
In-Reply-To: <cover.1528404649.git.ak@linux.intel.com>
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
prev parent reply other threads:[~2018-06-07 20:54 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2018-06-07 20:54 [MODERATED] [PATCH 0/1] SMTCTL 0 Andi Kleen
2018-06-07 20:54 ` Andi Kleen [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=6c01a32871067d6ef888fede6371820f88fbbe01.1528404649.git.ak@linux.intel.com \
--to=ak@linux.intel.com \
--cc=speck@linutronix.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).