mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
rtla: Replace get_nprocs_conf() with sysfs possible cpus
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 Link: https://lore.kernel.org/r/20260814135511.2207190-2-tglozar@redhat.com Signed-off-by: Tomas Glozar <tglozar@redhat.com>
This commit is contained in:
parent
c6bafe09bf
commit
b3dc9905d7
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -107,6 +107,153 @@ void get_duration(time_t start_time, char *output, int output_size)
|
|||
tm_info->tm_sec);
|
||||
}
|
||||
|
||||
/*
|
||||
* cpu_list_iterate - parse a cpu list and call a function on each element
|
||||
*
|
||||
* If callback returns a non-zero value, the iteration is stopped.
|
||||
*
|
||||
* Returns the number of cpus in the list (including duplicates) on success,
|
||||
* callback return value on break, and -1 on error.
|
||||
*/
|
||||
int cpu_list_iterate(const char *cpu_list, int (*callback)(int, void *), void *data)
|
||||
{
|
||||
const char *p;
|
||||
int i, cpu, end_cpu, count = 0, retval;
|
||||
|
||||
for (p = cpu_list; *p && *p != '\n'; ) {
|
||||
cpu = atoi(p);
|
||||
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'))
|
||||
return -1;
|
||||
while (isdigit(*p))
|
||||
p++;
|
||||
} else
|
||||
end_cpu = cpu;
|
||||
|
||||
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
|
||||
*
|
||||
|
|
@ -117,48 +264,14 @@ void get_duration(time_t start_time, char *output, int output_size)
|
|||
*/
|
||||
int parse_cpu_set(char *cpu_list, cpu_set_t *set)
|
||||
{
|
||||
const char *p;
|
||||
int end_cpu;
|
||||
int cpu;
|
||||
int i;
|
||||
|
||||
CPU_ZERO(set);
|
||||
|
||||
for (p = cpu_list; *p; ) {
|
||||
cpu = atoi(p);
|
||||
if (cpu < 0 || (!cpu && *p != '0') || cpu >= nr_cpus)
|
||||
goto err;
|
||||
|
||||
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;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
if (*p == ',')
|
||||
p++;
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user