[LTP] [PATCH v3 07/31] testcases: sysfs: Add sys_cpu_topology02

Cyril Hrubis chrubis@suse.cz
Tue Aug 25 13:36:01 CEST 2026


A test for /sys/devices/system/cpu/cpu*/topology/* files.

Signed-off-by: Cyril Hrubis <chrubis@suse.cz>
---
 runtest/sysfs                                 |   1 +
 .../sysfs/devices/system/cpu/.gitignore       |   1 +
 .../devices/system/cpu/sys_cpu_topology02.c   | 133 ++++++++++++++++++
 3 files changed, 135 insertions(+)
 create mode 100644 testcases/kernel/sysfs/devices/system/cpu/sys_cpu_topology02.c

diff --git a/runtest/sysfs b/runtest/sysfs
index 4fc8f1eb2..b1daab68f 100644
--- a/runtest/sysfs
+++ b/runtest/sysfs
@@ -3,3 +3,4 @@ sys_kernel01 sys_kernel01
 sys_clocksource01 sys_clocksource01
 sys_node01 sys_node01
 sys_cpu_topology01 sys_cpu_topology01
+sys_cpu_topology02 sys_cpu_topology02
diff --git a/testcases/kernel/sysfs/devices/system/cpu/.gitignore b/testcases/kernel/sysfs/devices/system/cpu/.gitignore
index 0615aff77..a688a9b50 100644
--- a/testcases/kernel/sysfs/devices/system/cpu/.gitignore
+++ b/testcases/kernel/sysfs/devices/system/cpu/.gitignore
@@ -1 +1,2 @@
 /sys_cpu_topology01
+/sys_cpu_topology02
diff --git a/testcases/kernel/sysfs/devices/system/cpu/sys_cpu_topology02.c b/testcases/kernel/sysfs/devices/system/cpu/sys_cpu_topology02.c
new file mode 100644
index 000000000..7759d4098
--- /dev/null
+++ b/testcases/kernel/sysfs/devices/system/cpu/sys_cpu_topology02.c
@@ -0,0 +1,133 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2026 Cyril Hrubis <chrubis@suse.cz>
+ */
+
+/*\
+ * Sanity checks for the per-CPU topology attributes exported under
+ * /sys/devices/system/cpu/cpuN/topology/.
+ *
+ * The topology masks describe how a CPU relates to the others. They are nested
+ * from the smallest (SMT threads) to the largest (package) group, so for every
+ * online CPU the test verifies that:
+ *
+ * - thread_siblings_list is a subset of core_cpus_list
+ * - core_cpus_list is a subset of package_cpus_list
+ * - package_cpus_list is a subset of the present CPUs, not just the online
+ *   ones: the whole topology/ directory (package_cpus_list included) is
+ *   added/removed per-CPU on hotplug (drivers/base/topology.c registers it
+ *   against a cpuhp state), so it only ever lists CPUs that share the
+ *   package and are also online right now. Comparing against "present"
+ *   instead of "online" avoids relying on that always being reliably kept
+ *   in sync on every architecture, and avoids a spurious failure if a
+ *   sibling CPU is hot-unplugged concurrently with this test running.
+ * - the CPU itself is contained in its own thread_siblings_list
+ * - core_id is in [0, highest possible CPU id], since there can be at most
+ *   as many cores as possible CPUs (every core has at least one thread)
+ * - physical_package_id is in [-1, highest possible CPU id] on physical
+ *   hardware, or just at least -1 under virtualization (see below). -1 is a
+ *   legitimate value here, not just a range extension for safety: per
+ *   include/linux/topology.h, -1 is the architecture-neutral default for
+ *   topology_physical_package_id() (and die_id/cluster_id/book_id/
+ *   drawer_id) on any architecture, or any CPU type within one, that does
+ *   not implement package detection. topology_core_id() does not have this
+ *   documented -1 fallback (its generic default is 0), so core_id is not
+ *   given the same allowance.
+ *
+ *   The upper bound on physical_package_id is dropped under virtualization:
+ *   store_cpu_topology() in drivers/base/arch_topology.c falls back to
+ *   physical_package_id = cpu_to_node(cpu) whenever ACPI/DT topology
+ *   parsing did not already populate it (common on minimal/generic arm64 VM
+ *   firmware without full ACPI PPTT tables). On physical hardware this
+ *   fallback is still bounded by the number of possible CPUs, since NUMA
+ *   node ids are compacted/renumbered from the firmware's proximity domains
+ *   and there can never be more NUMA nodes than CPUs (each node needs at
+ *   least one). But a VM's NUMA node numbering is a hypervisor/firmware
+ *   policy choice entirely unrelated to its own CPU count, e.g. mirroring a
+ *   large host's own (possibly sparse) NUMA node ids under NUMA
+ *   passthrough/vNUMA configuration, so the bound would not be meaningful
+ *   there and is dropped to avoid a false failure.
+ *
+ * All checks skip gracefully with TCONF when a particular attribute is not
+ * present, as the exact set of topology files differs between kernel versions
+ * and architectures.
+ */
+
+#include <stdio.h>
+#include <limits.h>
+#include <unistd.h>
+#include "tst_test.h"
+#include "tst_sysfs_assert.h"
+
+#define SYS_CPU "/sys/devices/system/cpu"
+
+static void check_self_in_threads(int cpu)
+{
+	TST_SYSFS_ASSERT_LIST_CONTAINS(cpu,
+		SYS_CPU "/cpu%d/topology/thread_siblings_list", cpu);
+}
+
+static void check_cpu_topology(int cpu, int poss_max_id, long pkg_max_id)
+{
+	char sub[PATH_MAX], super[PATH_MAX];
+
+	if (access(SYS_CPU, F_OK))
+		return;
+
+	if (!tst_sysfs_exists(SYS_CPU "/cpu%d/topology/core_id", cpu)) {
+		tst_res(TCONF, "cpu%d has no topology directory", cpu);
+		return;
+	}
+
+	tst_res(TINFO, "Checking cpu%d topology", cpu);
+
+	TST_SYSFS_ASSERT_RANGELL(0, poss_max_id,
+			       SYS_CPU "/cpu%d/topology/core_id", cpu);
+
+	TST_SYSFS_ASSERT_RANGELL(-1, pkg_max_id,
+			       SYS_CPU "/cpu%d/topology/physical_package_id",
+			       cpu);
+
+	snprintf(sub, sizeof(sub),
+		 SYS_CPU "/cpu%d/topology/thread_siblings_list", cpu);
+	snprintf(super, sizeof(super),
+		 SYS_CPU "/cpu%d/topology/core_cpus_list", cpu);
+	TST_SYSFS_ASSERT_LIST_SUBSET(sub, super);
+
+	snprintf(sub, sizeof(sub),
+		 SYS_CPU "/cpu%d/topology/core_cpus_list", cpu);
+	snprintf(super, sizeof(super),
+		 SYS_CPU "/cpu%d/topology/package_cpus_list", cpu);
+	TST_SYSFS_ASSERT_LIST_SUBSET(sub, super);
+
+	snprintf(sub, sizeof(sub),
+		 SYS_CPU "/cpu%d/topology/package_cpus_list", cpu);
+	TST_SYSFS_ASSERT_LIST_SUBSET(sub, SYS_CPU "/present");
+
+	check_self_in_threads(cpu);
+}
+
+static void do_test(void)
+{
+	int count, max_id, poss_count, poss_max_id, cpu;
+	long pkg_max_id;
+
+	if (TST_SYSFS_ASSERT_PARSE_LIST(&count, &max_id, SYS_CPU "/online"))
+		return;
+
+	if (TST_SYSFS_ASSERT_PARSE_LIST(&poss_count, &poss_max_id,
+					SYS_CPU "/possible"))
+		return;
+
+	if (tst_is_virt(VIRT_ANY))
+		pkg_max_id = LONG_MAX;
+	else
+		pkg_max_id = poss_max_id;
+
+	for (cpu = 0; cpu <= max_id; cpu++)
+		check_cpu_topology(cpu, poss_max_id, pkg_max_id);
+}
+
+static struct tst_test test = {
+	.test_all = do_test,
+};
-- 
2.51.0



More information about the ltp mailing list