Skip to content

Commit

Permalink
Merge branch 'for-next/smt-control' into for-next/core
Browse files Browse the repository at this point in the history
* for-next/smt-control:
  : Support SMT control on arm64
  arm64: Kconfig: Enable HOTPLUG_SMT
  arm64: topology: Support SMT control on ACPI based system
  arch_topology: Support SMT control for OF based system
  cpu/SMT: Provide a default topology_is_primary_thread()
  • Loading branch information
Catalin Marinas committed Mar 25, 2025
2 parents 8cc14fd + eed4583 commit 8ae9e2d
Show file tree
Hide file tree
Showing 6 changed files with 98 additions and 1 deletion.
1 change: 1 addition & 0 deletions arch/arm64/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,7 @@ config ARM64
select HAVE_KRETPROBES
select HAVE_GENERIC_VDSO
select HOTPLUG_CORE_SYNC_DEAD if HOTPLUG_CPU
select HOTPLUG_SMT if HOTPLUG_CPU
select IRQ_DOMAIN
select IRQ_FORCED_THREADING
select KASAN_VMALLOC if KASAN
Expand Down
54 changes: 54 additions & 0 deletions arch/arm64/kernel/topology.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@
#include <linux/arch_topology.h>
#include <linux/cacheinfo.h>
#include <linux/cpufreq.h>
#include <linux/cpu_smt.h>
#include <linux/init.h>
#include <linux/percpu.h>
#include <linux/sched/isolation.h>
#include <linux/xarray.h>

#include <asm/cpu.h>
#include <asm/cputype.h>
Expand All @@ -38,17 +40,28 @@ static bool __init acpi_cpu_is_threaded(int cpu)
return !!is_threaded;
}

struct cpu_smt_info {
unsigned int thread_num;
int core_id;
};

/*
* Propagate the topology information of the processor_topology_node tree to the
* cpu_topology array.
*/
int __init parse_acpi_topology(void)
{
unsigned int max_smt_thread_num = 1;
struct cpu_smt_info *entry;
struct xarray hetero_cpu;
unsigned long hetero_id;
int cpu, topology_id;

if (acpi_disabled)
return 0;

xa_init(&hetero_cpu);

for_each_possible_cpu(cpu) {
topology_id = find_acpi_cpu_topology(cpu, 0);
if (topology_id < 0)
Expand All @@ -58,6 +71,34 @@ int __init parse_acpi_topology(void)
cpu_topology[cpu].thread_id = topology_id;
topology_id = find_acpi_cpu_topology(cpu, 1);
cpu_topology[cpu].core_id = topology_id;

/*
* In the PPTT, CPUs below a node with the 'identical
* implementation' flag have the same number of threads.
* Count the number of threads for only one CPU (i.e.
* one core_id) among those with the same hetero_id.
* See the comment of find_acpi_cpu_topology_hetero_id()
* for more details.
*
* One entry is created for each node having:
* - the 'identical implementation' flag
* - its parent not having the flag
*/
hetero_id = find_acpi_cpu_topology_hetero_id(cpu);
entry = xa_load(&hetero_cpu, hetero_id);
if (!entry) {
entry = kzalloc(sizeof(*entry), GFP_KERNEL);
WARN_ON_ONCE(!entry);

if (entry) {
entry->core_id = topology_id;
entry->thread_num = 1;
xa_store(&hetero_cpu, hetero_id,
entry, GFP_KERNEL);
}
} else if (entry->core_id == topology_id) {
entry->thread_num++;
}
} else {
cpu_topology[cpu].thread_id = -1;
cpu_topology[cpu].core_id = topology_id;
Expand All @@ -68,6 +109,19 @@ int __init parse_acpi_topology(void)
cpu_topology[cpu].package_id = topology_id;
}

/*
* This is a short loop since the number of XArray elements is the
* number of heterogeneous CPU clusters. On a homogeneous system
* there's only one entry in the XArray.
*/
xa_for_each(&hetero_cpu, hetero_id, entry) {
max_smt_thread_num = max(max_smt_thread_num, entry->thread_num);
xa_erase(&hetero_cpu, hetero_id);
kfree(entry);
}

cpu_smt_set_num_threads(max_smt_thread_num, max_smt_thread_num);
xa_destroy(&hetero_cpu);
return 0;
}
#endif
Expand Down
1 change: 1 addition & 0 deletions arch/powerpc/include/asm/topology.h
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ static inline bool topology_is_primary_thread(unsigned int cpu)
{
return cpu == cpu_first_thread_sibling(cpu);
}
#define topology_is_primary_thread topology_is_primary_thread

static inline bool topology_smt_thread_allowed(unsigned int cpu)
{
Expand Down
2 changes: 1 addition & 1 deletion arch/x86/include/asm/topology.h
Original file line number Diff line number Diff line change
Expand Up @@ -229,11 +229,11 @@ static inline bool topology_is_primary_thread(unsigned int cpu)
{
return cpumask_test_cpu(cpu, cpu_primary_thread_mask);
}
#define topology_is_primary_thread topology_is_primary_thread

#else /* CONFIG_SMP */
static inline int topology_phys_to_logical_pkg(unsigned int pkg) { return 0; }
static inline int topology_max_smt_threads(void) { return 1; }
static inline bool topology_is_primary_thread(unsigned int cpu) { return true; }
static inline unsigned int topology_amd_nodes_per_pkg(void) { return 1; }
#endif /* !CONFIG_SMP */

Expand Down
18 changes: 18 additions & 0 deletions drivers/base/arch_topology.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <linux/cleanup.h>
#include <linux/cpu.h>
#include <linux/cpufreq.h>
#include <linux/cpu_smt.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/slab.h>
Expand Down Expand Up @@ -508,6 +509,10 @@ core_initcall(free_raw_capacity);
#endif

#if defined(CONFIG_ARM64) || defined(CONFIG_RISCV)

/* Used to enable the SMT control */
static unsigned int max_smt_thread_num = 1;

/*
* This function returns the logic cpu number of the node.
* There are basically three kinds of return values:
Expand Down Expand Up @@ -567,6 +572,8 @@ static int __init parse_core(struct device_node *core, int package_id,
i++;
} while (1);

max_smt_thread_num = max_t(unsigned int, max_smt_thread_num, i);

cpu = get_cpu_for_node(core);
if (cpu >= 0) {
if (!leaf) {
Expand Down Expand Up @@ -679,6 +686,17 @@ static int __init parse_socket(struct device_node *socket)
if (!has_socket)
ret = parse_cluster(socket, 0, -1, 0);

/*
* Reset the max_smt_thread_num to 1 on failure. Since on failure
* we need to notify the framework the SMT is not supported, but
* max_smt_thread_num can be initialized to the SMT thread number
* of the cores which are successfully parsed.
*/
if (ret)
max_smt_thread_num = 1;

cpu_smt_set_num_threads(max_smt_thread_num, max_smt_thread_num);

return ret;
}

Expand Down
23 changes: 23 additions & 0 deletions include/linux/topology.h
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,29 @@ static inline const struct cpumask *cpu_smt_mask(int cpu)
}
#endif

#ifndef topology_is_primary_thread

static inline bool topology_is_primary_thread(unsigned int cpu)
{
/*
* When disabling SMT, the primary thread of the SMT will remain
* enabled/active. Architectures that have a special primary thread
* (e.g. x86) need to override this function. Otherwise the first
* thread in the SMT can be made the primary thread.
*
* The sibling cpumask of an offline CPU always contains the CPU
* itself on architectures using the implementation of
* CONFIG_GENERIC_ARCH_TOPOLOGY for building their topology.
* Other architectures not using CONFIG_GENERIC_ARCH_TOPOLOGY for
* building their topology have to check whether to use this default
* implementation or to override it.
*/
return cpu == cpumask_first(topology_sibling_cpumask(cpu));
}
#define topology_is_primary_thread topology_is_primary_thread

#endif

static inline const struct cpumask *cpu_cpu_mask(int cpu)
{
return cpumask_of_node(cpu_to_node(cpu));
Expand Down

0 comments on commit 8ae9e2d

Please sign in to comment.