Historical speck list archives
 help / color / mirror / Atom feed
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

      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).