All of lore.kernel.org
 help / color / mirror / Atom feed
From: Tomas Glozar <tglozar@redhat.com>
To: Steven Rostedt <rostedt@goodmis.org>, Tomas Glozar <tglozar@redhat.com>
Cc: John Kacur <jkacur@redhat.com>,
	Luis Goncalves <lgoncalv@redhat.com>,
	Crystal Wood <crwood@redhat.com>,
	Costa Shulyupin <costa.shul@redhat.com>,
	Wander Lairson Costa <wander@redhat.com>,
	LKML <linux-kernel@vger.kernel.org>,
	linux-trace-kernel <linux-trace-kernel@vger.kernel.org>
Subject: [PATCH 1/5] rtla: Replace get_nprocs_conf() with sysfs possible cpus
Date: Fri, 14 Aug 2026 15:55:07 +0200	[thread overview]
Message-ID: <20260814135511.2207190-2-tglozar@redhat.com> (raw)
In-Reply-To: <20260814135511.2207190-1-tglozar@redhat.com>

rtla uses get_nprocs_conf(), a standard libc routine, to retrieve the
total number of CPUs for the purpose of processing per-CPU data.

In some configurations, libc might return a different value than what is
seen by the kernel. To cover for this situation, replace the call to
get_nprocs_conf() with a newly added helper, get_possible_cpus().

get_possible_cpus() reads /sys/devices/system/cpu/possible and parses
the CPU list, identically to what libbpf_num_possible_cpus() does.
Additionally, it checks that the possible cpu list is zero-based and has
no holes by computing both the cpu count and the maximum cpu number.
Systems where /sys/.../cpu/possible is unreadable, is not zero-based,
or has holes, now report an error instead of crashing later due to unmet
assumptions.

Note that parse_cpu_set() cannot be used to parse
/sys/devices/system/cpu/possible, because it only supports CPU count of
1024 and lower. Higher CPU counts are not fully supported by rtla;
nr_cpus > 1024 is, though, so it has to be covered by the new
implementation in order to avoid a regression.

A new function, cpu_list_iterate(), is added to utils.c to contain the
cpu list parsing logic, which is now shared between the new function
get_possible_cpus() and the pre-existing parse_cpu_set().

Assisted-by: Claude:claude-opus-4-6
Signed-off-by: Tomas Glozar <tglozar@redhat.com>
---
 tools/tracing/rtla/src/common.c |  16 +++-
 tools/tracing/rtla/src/utils.c  | 165 +++++++++++++++++++++++++++-----
 tools/tracing/rtla/src/utils.h  |   3 +
 3 files changed, 156 insertions(+), 28 deletions(-)

diff --git a/tools/tracing/rtla/src/common.c b/tools/tracing/rtla/src/common.c
index 8c7f5e75b2ec8..20fae1f19cacf 100644
--- a/tools/tracing/rtla/src/common.c
+++ b/tools/tracing/rtla/src/common.c
@@ -6,7 +6,6 @@
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
-#include <sys/sysinfo.h>
 
 #include "common.h"
 
@@ -155,7 +154,20 @@ int run_tool(struct tool_ops *ops, int argc, char *argv[])
 	bool stopped;
 	int retval;
 
-	nr_cpus = get_nprocs_conf();
+	nr_cpus = get_possible_cpus();
+	if (nr_cpus == -1) {
+		err_msg("Could not read number of possible cpus\n");
+		goto out_exit;
+	}
+	if (nr_cpus == -2) {
+		err_msg("Could not parse number of possible cpus\n");
+		goto out_exit;
+	}
+	if (nr_cpus == -3) {
+		err_msg("Unsupported non-contiguous or non-zero-based CPU topology\n");
+		goto out_exit;
+	}
+
 	params = ops->parse_args(argc, argv);
 	if (!params)
 		exit(1);
diff --git a/tools/tracing/rtla/src/utils.c b/tools/tracing/rtla/src/utils.c
index cb187e7d48d1c..d65de511be9e1 100644
--- a/tools/tracing/rtla/src/utils.c
+++ b/tools/tracing/rtla/src/utils.c
@@ -108,57 +108,170 @@ void get_duration(time_t start_time, char *output, int output_size)
 }
 
 /*
- * parse_cpu_set - parse a cpu_list filling cpu_set_t argument
+ * cpu_list_iterate - parse a cpu list and call a function on each element
  *
- * Receives a cpu list, like 1-3,5 (cpus 1, 2, 3, 5), and then set
- * filling cpu_set_t argument.
+ * If callback returns a non-zero value, the iteration is stopped.
  *
- * Returns 0 on success, 1 otherwise.
+ * Returns the number of cpus in the list (including duplicates) on success,
+ * callback return value on break, and -1 on error.
  */
-int parse_cpu_set(char *cpu_list, cpu_set_t *set)
+int cpu_list_iterate(const char *cpu_list, int (*callback)(int, void *), void *data)
 {
 	const char *p;
-	int end_cpu;
-	int cpu;
-	int i;
-
-	CPU_ZERO(set);
+	int i, cpu, end_cpu, count = 0, retval;
 
-	for (p = cpu_list; *p; ) {
+	for (p = cpu_list; *p && *p != '\n'; ) {
 		cpu = atoi(p);
-		if (cpu < 0 || (!cpu && *p != '0') || cpu >= nr_cpus)
-			goto err;
+		if (cpu < 0 || (!cpu && *p != '0'))
+			return -1;
 
 		while (isdigit(*p))
 			p++;
 		if (*p == '-') {
 			p++;
 			end_cpu = atoi(p);
-			if (end_cpu < cpu || (!end_cpu && *p != '0') || end_cpu >= nr_cpus)
-				goto err;
+			if (end_cpu < cpu || (!end_cpu && *p != '0'))
+				return -1;
 			while (isdigit(*p))
 				p++;
 		} else
 			end_cpu = cpu;
 
-		if (cpu == end_cpu) {
-			debug_msg("cpu_set: adding cpu %d\n", cpu);
-			CPU_SET(cpu, set);
-		} else {
-			for (i = cpu; i <= end_cpu; i++) {
-				debug_msg("cpu_set: adding cpu %d\n", i);
-				CPU_SET(i, set);
-			}
+		for (i = cpu; i <= end_cpu; i++) {
+			retval = callback(i, data);
+			if (retval)
+				return retval;
+			++count;
 		}
 
 		if (*p == ',')
 			p++;
 	}
 
+	return count;
+}
+
+static int max_cpu_callback(int i, void *data)
+{
+	int *max_cpu = data;
+
+	if (i > *max_cpu)
+		*max_cpu = i;
+
+	return 0;
+}
+
+static int tmp_cpu_set_callback(int i, void *data)
+{
+	bool *cpu_set = data;
+
+	cpu_set[i] = true;
+
+	return 0;
+}
+
+/*
+ * get_possible_cpus - get the number of possible CPUs from sysfs
+ *
+ * Parse /sys/devices/system/cpu/possible to determine the number of
+ * possible CPUs. Only contiguous zero-based CPUs lists are accepted.
+ *
+ * Returns the number of possible CPUs, or a negative value on error:
+ * - -1 if the file is unreadable,
+ * - -2 if parsing failed,
+ * - -3 if the cpu list is non-zero-based or non-contiguous.
+ */
+int get_possible_cpus(void)
+{
+	char *str = NULL;
+	size_t len = 0;
+	int nr_cpus = 0, max_cpu = -1, i;
+	FILE *fp;
+	bool *cpu_set;
+
+	fp = fopen("/sys/devices/system/cpu/possible", "r");
+	if (!fp)
+		return -1;
+
+	if (getline(&str, &len, fp) < 1) {
+		/* cpu string should be at least 1 character */
+		if (str)
+			free(str);
+		fclose(fp);
+		return -1;
+	}
+
+	fclose(fp);
+
+	/* get maximum cpu number */
+	if (cpu_list_iterate(str, max_cpu_callback, &max_cpu) < 0) {
+		free(str);
+		return -2;
+	}
+
+	if (max_cpu < 0 || max_cpu == INT_MAX) {
+		/* empty or bogus cpu list */
+		free(str);
+		return -2;
+	}
+
+	/* get max cpu using dynamic array, as nr_cpus might be > 1024 */
+	cpu_set = calloc(max_cpu + 1, sizeof(bool));
+	if (!cpu_set) {
+		free(str);
+		return -2;
+	}
+	if (cpu_list_iterate(str, tmp_cpu_set_callback, cpu_set) < 0) {
+		free(str);
+		free(cpu_set);
+		return -2;
+	}
+	for (i = 0; i <= max_cpu; i++) {
+		if (cpu_set[i])
+			++nr_cpus;
+	}
+	free(cpu_set);
+
+	free(str);
+
+	if (max_cpu >= nr_cpus)
+		/* rtla assumes cpu < nr_cpus for all cpus */
+		return -3;
+
+	return nr_cpus;
+}
+
+static int cpu_set_callback(int i, void *data)
+{
+	cpu_set_t *set = data;
+
+	if (i >= nr_cpus || i >= CPU_SETSIZE)
+		return -1;
+
+	debug_msg("cpu_set: adding cpu %d\n", i);
+	CPU_SET(i, set);
+
+	return 0;
+}
+
+/*
+ * parse_cpu_set - parse a cpu_list filling cpu_set_t argument
+ *
+ * Receives a cpu list, like 1-3,5 (cpus 1, 2, 3, 5), and then set
+ * filling cpu_set_t argument.
+ *
+ * Returns 0 on success, 1 otherwise.
+ */
+int parse_cpu_set(char *cpu_list, cpu_set_t *set)
+{
+	CPU_ZERO(set);
+
+	if (cpu_list_iterate(cpu_list, cpu_set_callback, set) < 0) {
+		debug_msg("Error parsing the cpu set %s\n", cpu_list);
+		return 1;
+	}
+
 	return 0;
-err:
-	debug_msg("Error parsing the cpu set %s\n", cpu_list);
-	return 1;
 }
 
 /*
diff --git a/tools/tracing/rtla/src/utils.h b/tools/tracing/rtla/src/utils.h
index 2ba3333669bb4..c26ba8827947a 100644
--- a/tools/tracing/rtla/src/utils.h
+++ b/tools/tracing/rtla/src/utils.h
@@ -47,6 +47,9 @@ void fatal(const char *fmt, ...);
 long parse_seconds_duration(char *val);
 void get_duration(time_t start_time, char *output, int output_size);
 
+int cpu_list_iterate(const char *cpu_list, int (*callback)(int, void *), void *data);
+int get_possible_cpus(void);
+
 long long get_llong_from_str(char *start);
 
 static inline void
-- 
2.55.0


  reply	other threads:[~2026-08-14 13:55 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-14 13:55 [PATCH 0/5] rtla: Implement more robust nr_cpus handling Tomas Glozar
2026-08-14 13:55 ` Tomas Glozar [this message]
2026-08-14 13:55 ` [PATCH 2/5] rtla: Discard trace entries with cpu >= nr_cpus Tomas Glozar
2026-08-14 14:02   ` sashiko-bot
2026-08-14 13:55 ` [PATCH 3/5] rtla: Abort on nr_cpus mismatch with tracer Tomas Glozar
2026-08-14 13:55 ` [PATCH 4/5] rtla/tests: Add unit test for cpu_list_iterate() Tomas Glozar
2026-08-14 13:55 ` [PATCH 5/5] rtla/tests: Add unit test for get_max_cpu_from_list() Tomas Glozar
2026-08-14 14:30 ` [PATCH 0/5] rtla: Implement more robust nr_cpus handling Steven Rostedt
2026-08-14 17:49   ` Tomas Glozar

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=20260814135511.2207190-2-tglozar@redhat.com \
    --to=tglozar@redhat.com \
    --cc=costa.shul@redhat.com \
    --cc=crwood@redhat.com \
    --cc=jkacur@redhat.com \
    --cc=lgoncalv@redhat.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-trace-kernel@vger.kernel.org \
    --cc=rostedt@goodmis.org \
    --cc=wander@redhat.com \
    /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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.