Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
linux
Commits
015b2693
Commit
015b2693
authored
Aug 30, 2019
by
Paul Walmsley
Browse files
Options
Browse Files
Download
Plain Diff
Merge tag 'common/for-v5.4-rc1/cpu-topology' into for-v5.4-rc1-branch
parents
a256f2e3
f51edcec
Changes
12
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
514 additions
and
486 deletions
+514
-486
Documentation/devicetree/bindings/cpu/cpu-topology.txt
Documentation/devicetree/bindings/cpu/cpu-topology.txt
+167
-89
MAINTAINERS
MAINTAINERS
+7
-0
arch/arm/include/asm/topology.h
arch/arm/include/asm/topology.h
+0
-20
arch/arm/kernel/topology.c
arch/arm/kernel/topology.c
+6
-54
arch/arm64/include/asm/topology.h
arch/arm64/include/asm/topology.h
+0
-23
arch/arm64/kernel/topology.c
arch/arm64/kernel/topology.c
+4
-299
arch/riscv/Kconfig
arch/riscv/Kconfig
+1
-0
arch/riscv/kernel/smpboot.c
arch/riscv/kernel/smpboot.c
+3
-0
drivers/base/Kconfig
drivers/base/Kconfig
+1
-1
drivers/base/arch_topology.c
drivers/base/arch_topology.c
+298
-0
include/linux/arch_topology.h
include/linux/arch_topology.h
+26
-0
include/linux/topology.h
include/linux/topology.h
+1
-0
No files found.
Documentation/devicetree/bindings/
arm/
topology.txt
→
Documentation/devicetree/bindings/
cpu/cpu-
topology.txt
View file @
015b2693
===========================================
ARM
topology binding description
CPU
topology binding description
===========================================
===========================================
1 - Introduction
===========================================
In a
n ARM
system, the hierarchy of CPUs is defined through three entities that
In a
SMP
system, the hierarchy of CPUs is defined through three entities that
are used to describe the layout of physical CPUs in the system:
- socket
- cluster
- core
- thread
The cpu nodes (bindings defined in [1]) represent the devices that
correspond to physical CPUs and are to be mapped to the hierarchy levels.
The bottom hierarchy level sits at core or thread level depending on whether
symmetric multi-threading (SMT) is supported or not.
...
...
@@ -24,33 +22,31 @@ threads existing in the system and map to the hierarchy level "thread" above.
In systems where SMT is not supported "cpu" nodes represent all cores present
in the system and map to the hierarchy level "core" above.
ARM
topology bindings allow one to associate cpu nodes with hierarchical groups
CPU
topology bindings allow one to associate cpu nodes with hierarchical groups
corresponding to the system hierarchy; syntactically they are defined as device
tree nodes.
The remainder of this document provides the topology bindings for ARM, based
on the Devicetree Specification, available from:
Currently, only ARM/RISC-V intend to use this cpu topology binding but it may be
used for any other architecture as well.
https://www.devicetree.org/specifications/
The cpu nodes, as per bindings defined in [4], represent the devices that
correspond to physical CPUs and are to be mapped to the hierarchy levels.
If not stated otherwise, whenever a reference to a cpu node phandle is made its
value must point to a cpu node compliant with the cpu node bindings as
documented in [1].
A topology description containing phandles to cpu nodes that are not compliant
with bindings standardized in [
1
] is therefore considered invalid.
with bindings standardized in [
4
] is therefore considered invalid.
===========================================
2 - cpu-map node
===========================================
The ARM CPU topology is defined within the cpu-map node, which is a direct
The ARM
/RISC-V
CPU topology is defined within the cpu-map node, which is a direct
child of the cpus node and provides a container where the actual topology
nodes are listed.
- cpu-map node
Usage: Optional - On
ARM
SMP systems provide CPUs topology to the OS.
ARM u
niprocessor systems do not require a topology
Usage: Optional - On SMP systems provide CPUs topology to the OS.
U
niprocessor systems do not require a topology
description and therefore should not define a
cpu-map node.
...
...
@@ -63,21 +59,23 @@ nodes are listed.
The cpu-map node's child nodes can be:
- one or more cluster nodes
- one or more cluster nodes or
- one or more socket nodes in a multi-socket system
Any other configuration is considered invalid.
The cpu-map node can only contain
three
types of child nodes:
The cpu-map node can only contain
4
types of child nodes:
- socket node
- cluster node
- core node
- thread node
whose bindings are described in paragraph 3.
The nodes describing the CPU topology (
cluster/core/thread) can only
be defined within the cpu-map node and every core/thread in the system
must be defined within the topology. Any other configuration is
The nodes describing the CPU topology (
socket/cluster/core/thread) can
only be defined within the cpu-map node and every core/thread in the
system
must be defined within the topology. Any other configuration is
invalid and therefore must be ignored.
===========================================
...
...
@@ -85,26 +83,44 @@ invalid and therefore must be ignored.
===========================================
cpu-map child nodes must follow a naming convention where the node name
must be "
clusterN", "coreN", "threadN" depending on the node type (i
e
cluster/core/thread) (where N = {0, 1, ...} is the node number; nodes which
are siblings within a single common parent node must be given a unique and
must be "
socketN", "clusterN", "coreN", "threadN" depending on the node typ
e
(ie socket/cluster/core/thread) (where N = {0, 1, ...} is the node number; nodes
which
are siblings within a single common parent node must be given a unique and
sequential N value, starting from 0).
cpu-map child nodes which do not share a common parent node can have the same
name (ie same number N as other cpu-map child nodes at different device tree
levels) since name uniqueness will be guaranteed by the device tree hierarchy.
===========================================
3 - cluster/core/thread node bindings
3 -
socket/
cluster/core/thread node bindings
===========================================
Bindings for cluster/cpu/thread nodes are defined as follows:
Bindings for socket/cluster/cpu/thread nodes are defined as follows:
- socket node
Description: must be declared within a cpu-map node, one node
per physical socket in the system. A system can
contain single or multiple physical socket.
The association of sockets and NUMA nodes is beyond
the scope of this bindings, please refer [2] for
NUMA bindings.
This node is optional for a single socket system.
The socket node name must be "socketN" as described in 2.1 above.
A socket node can not be a leaf node.
A socket node's child nodes must be one or more cluster nodes.
Any other configuration is considered invalid.
- cluster node
Description: must be declared within a cpu-map node, one node
per cluster. A system can contain several layers of
clustering
and cluster nodes can be contained in parent
cluster nodes.
clustering
within a single physical socket and cluster
nodes can be contained in parent
cluster nodes.
The cluster node name must be "clusterN" as described in 2.1 above.
A cluster node can not be a leaf node.
...
...
@@ -164,90 +180,93 @@ Bindings for cluster/cpu/thread nodes are defined as follows:
4 - Example dts
===========================================
Example 1 (ARM 64-bit, 16-cpu system, two clusters of clusters):
Example 1 (ARM 64-bit, 16-cpu system, two clusters of clusters in a single
physical socket):
cpus {
#size-cells = <0>;
#address-cells = <2>;
cpu-map {
cluster
0 {
socket
0 {
cluster0 {
core0 {
thread0 {
cpu = <&CPU0>;
cluster0 {
core0 {
thread0 {
cpu = <&CPU0>;
};
thread1 {
cpu = <&CPU1>;
};
};
thread1 {
cpu = <&CPU1>;
};
};
core1 {
thread0 {
cpu = <&CPU2>;
};
thread1 {
cpu = <&CPU3>;
core1 {
thread0 {
cpu = <&CPU2>;
};
thread1 {
cpu = <&CPU3>;
};
};
};
};
cluster1 {
core0 {
thread0 {
cpu = <&CPU4>;
};
thread1 {
cpu = <&CPU5>;
cluster1 {
core0 {
thread0 {
cpu = <&CPU4>;
};
thread1 {
cpu = <&CPU5>;
};
};
};
core1 {
thread0 {
cpu = <&CPU6>;
};
thread1 {
cpu = <&CPU7>;
};
};
};
};
cluster1 {
cluster0 {
core0 {
thread0 {
cpu = <&CPU8>;
};
thread1 {
cpu = <&CPU9>;
};
};
core1 {
thread0 {
cpu = <&CPU10>;
};
thread1 {
cpu = <&CPU11>;
core1 {
thread0 {
cpu = <&CPU6>;
};
thread1 {
cpu = <&CPU7>;
};
};
};
};
cluster1 {
core0 {
thread0 {
cpu = <&CPU12>;
cluster0 {
core0 {
thread0 {
cpu = <&CPU8>;
};
thread1 {
cpu = <&CPU9>;
};
};
thread1 {
cpu = <&CPU13>;
core1 {
thread0 {
cpu = <&CPU10>;
};
thread1 {
cpu = <&CPU11>;
};
};
};
core1 {
thread0 {
cpu = <&CPU14>;
cluster1 {
core0 {
thread0 {
cpu = <&CPU12>;
};
thread1 {
cpu = <&CPU13>;
};
};
thread1 {
cpu = <&CPU15>;
core1 {
thread0 {
cpu = <&CPU14>;
};
thread1 {
cpu = <&CPU15>;
};
};
};
};
...
...
@@ -470,6 +489,65 @@ cpus {
};
};
Example 3: HiFive Unleashed (RISC-V 64 bit, 4 core system)
{
#address-cells = <2>;
#size-cells = <2>;
compatible = "sifive,fu540g", "sifive,fu500";
model = "sifive,hifive-unleashed-a00";
...
cpus {
#address-cells = <1>;
#size-cells = <0>;
cpu-map {
socket0 {
cluster0 {
core0 {
cpu = <&CPU1>;
};
core1 {
cpu = <&CPU2>;
};
core2 {
cpu0 = <&CPU2>;
};
core3 {
cpu0 = <&CPU3>;
};
};
};
};
CPU1: cpu@1 {
device_type = "cpu";
compatible = "sifive,rocket0", "riscv";
reg = <0x1>;
}
CPU2: cpu@2 {
device_type = "cpu";
compatible = "sifive,rocket0", "riscv";
reg = <0x2>;
}
CPU3: cpu@3 {
device_type = "cpu";
compatible = "sifive,rocket0", "riscv";
reg = <0x3>;
}
CPU4: cpu@4 {
device_type = "cpu";
compatible = "sifive,rocket0", "riscv";
reg = <0x4>;
}
}
};
===============================================================================
[1] ARM Linux kernel documentation
Documentation/devicetree/bindings/arm/cpus.yaml
[2] Devicetree NUMA binding description
Documentation/devicetree/bindings/numa.txt
[3] RISC-V Linux kernel documentation
Documentation/devicetree/bindings/riscv/cpus.txt
[4] https://www.devicetree.org/specifications/
MAINTAINERS
View file @
015b2693
...
...
@@ -6732,6 +6732,13 @@ W: https://linuxtv.org
S: Maintained
F: drivers/media/radio/radio-gemtek*
GENERIC ARCHITECTURE TOPOLOGY
M: Sudeep Holla <sudeep.holla@arm.com>
L: linux-kernel@vger.kernel.org
S: Maintained
F: drivers/base/arch_topology.c
F: include/linux/arch_topology.h
GENERIC GPIO I2C DRIVER
M: Wolfram Sang <wsa+renesas@sang-engineering.com>
S: Supported
...
...
arch/arm/include/asm/topology.h
View file @
015b2693
...
...
@@ -5,26 +5,6 @@
#ifdef CONFIG_ARM_CPU_TOPOLOGY
#include <linux/cpumask.h>
struct
cputopo_arm
{
int
thread_id
;
int
core_id
;
int
socket_id
;
cpumask_t
thread_sibling
;
cpumask_t
core_sibling
;
};
extern
struct
cputopo_arm
cpu_topology
[
NR_CPUS
];
#define topology_physical_package_id(cpu) (cpu_topology[cpu].socket_id)
#define topology_core_id(cpu) (cpu_topology[cpu].core_id)
#define topology_core_cpumask(cpu) (&cpu_topology[cpu].core_sibling)
#define topology_sibling_cpumask(cpu) (&cpu_topology[cpu].thread_sibling)
void
init_cpu_topology
(
void
);
void
store_cpu_topology
(
unsigned
int
cpuid
);
const
struct
cpumask
*
cpu_coregroup_mask
(
int
cpu
);
#include <linux/arch_topology.h>
/* Replace task scheduler's default frequency-invariant accounting */
...
...
arch/arm/kernel/topology.c
View file @
015b2693
...
...
@@ -177,17 +177,6 @@ static inline void parse_dt_topology(void) {}
static
inline
void
update_cpu_capacity
(
unsigned
int
cpuid
)
{}
#endif
/*
* cpu topology table
*/
struct
cputopo_arm
cpu_topology
[
NR_CPUS
];
EXPORT_SYMBOL_GPL
(
cpu_topology
);
const
struct
cpumask
*
cpu_coregroup_mask
(
int
cpu
)
{
return
&
cpu_topology
[
cpu
].
core_sibling
;
}
/*
* The current assumption is that we can power gate each core independently.
* This will be superseded by DT binding once available.
...
...
@@ -197,32 +186,6 @@ const struct cpumask *cpu_corepower_mask(int cpu)
return
&
cpu_topology
[
cpu
].
thread_sibling
;
}
static
void
update_siblings_masks
(
unsigned
int
cpuid
)
{
struct
cputopo_arm
*
cpu_topo
,
*
cpuid_topo
=
&
cpu_topology
[
cpuid
];
int
cpu
;
/* update core and thread sibling masks */
for_each_possible_cpu
(
cpu
)
{
cpu_topo
=
&
cpu_topology
[
cpu
];
if
(
cpuid_topo
->
socket_id
!=
cpu_topo
->
socket_id
)
continue
;
cpumask_set_cpu
(
cpuid
,
&
cpu_topo
->
core_sibling
);
if
(
cpu
!=
cpuid
)
cpumask_set_cpu
(
cpu
,
&
cpuid_topo
->
core_sibling
);
if
(
cpuid_topo
->
core_id
!=
cpu_topo
->
core_id
)
continue
;
cpumask_set_cpu
(
cpuid
,
&
cpu_topo
->
thread_sibling
);
if
(
cpu
!=
cpuid
)
cpumask_set_cpu
(
cpu
,
&
cpuid_topo
->
thread_sibling
);
}
smp_wmb
();
}
/*
* store_cpu_topology is called at boot when only one cpu is running
* and with the mutex cpu_hotplug.lock locked, when several cpus have booted,
...
...
@@ -230,7 +193,7 @@ static void update_siblings_masks(unsigned int cpuid)
*/
void
store_cpu_topology
(
unsigned
int
cpuid
)
{
struct
cpu
topo_arm
*
cpuid_topo
=
&
cpu_topology
[
cpuid
];
struct
cpu
_topology
*
cpuid_topo
=
&
cpu_topology
[
cpuid
];
unsigned
int
mpidr
;
/* If the cpu topology has been already set, just return */
...
...
@@ -250,12 +213,12 @@ void store_cpu_topology(unsigned int cpuid)
/* core performance interdependency */
cpuid_topo
->
thread_id
=
MPIDR_AFFINITY_LEVEL
(
mpidr
,
0
);
cpuid_topo
->
core_id
=
MPIDR_AFFINITY_LEVEL
(
mpidr
,
1
);
cpuid_topo
->
socket
_id
=
MPIDR_AFFINITY_LEVEL
(
mpidr
,
2
);
cpuid_topo
->
package
_id
=
MPIDR_AFFINITY_LEVEL
(
mpidr
,
2
);
}
else
{
/* largely independent cores */
cpuid_topo
->
thread_id
=
-
1
;
cpuid_topo
->
core_id
=
MPIDR_AFFINITY_LEVEL
(
mpidr
,
0
);
cpuid_topo
->
socket
_id
=
MPIDR_AFFINITY_LEVEL
(
mpidr
,
1
);
cpuid_topo
->
package
_id
=
MPIDR_AFFINITY_LEVEL
(
mpidr
,
1
);
}
}
else
{
/*
...
...
@@ -265,7 +228,7 @@ void store_cpu_topology(unsigned int cpuid)
*/
cpuid_topo
->
thread_id
=
-
1
;
cpuid_topo
->
core_id
=
0
;
cpuid_topo
->
socket
_id
=
-
1
;
cpuid_topo
->
package
_id
=
-
1
;
}
update_siblings_masks
(
cpuid
);
...
...
@@ -275,7 +238,7 @@ void store_cpu_topology(unsigned int cpuid)
pr_info
(
"CPU%u: thread %d, cpu %d, socket %d, mpidr %x
\n
"
,
cpuid
,
cpu_topology
[
cpuid
].
thread_id
,
cpu_topology
[
cpuid
].
core_id
,
cpu_topology
[
cpuid
].
socket
_id
,
mpidr
);
cpu_topology
[
cpuid
].
package
_id
,
mpidr
);
}
static
inline
int
cpu_corepower_flags
(
void
)
...
...
@@ -298,18 +261,7 @@ static struct sched_domain_topology_level arm_topology[] = {
*/
void
__init
init_cpu_topology
(
void
)
{
unsigned
int
cpu
;
/* init core mask and capacity */
for_each_possible_cpu
(
cpu
)
{
struct
cputopo_arm
*
cpu_topo
=
&
(
cpu_topology
[
cpu
]);
cpu_topo
->
thread_id
=
-
1
;
cpu_topo
->
core_id
=
-
1
;
cpu_topo
->
socket_id
=
-
1
;
cpumask_clear
(
&
cpu_topo
->
core_sibling
);
cpumask_clear
(
&
cpu_topo
->
thread_sibling
);
}
reset_cpu_topology
();
smp_wmb
();
parse_dt_topology
();
...
...
arch/arm64/include/asm/topology.h
View file @
015b2693
...
...
@@ -4,29 +4,6 @@
#include <linux/cpumask.h>
struct
cpu_topology
{
int
thread_id
;
int
core_id
;
int
package_id
;
int
llc_id
;
cpumask_t
thread_sibling
;
cpumask_t
core_sibling
;
cpumask_t
llc_sibling
;
};
extern
struct
cpu_topology
cpu_topology
[
NR_CPUS
];
#define topology_physical_package_id(cpu) (cpu_topology[cpu].package_id)
#define topology_core_id(cpu) (cpu_topology[cpu].core_id)
#define topology_core_cpumask(cpu) (&cpu_topology[cpu].core_sibling)
#define topology_sibling_cpumask(cpu) (&cpu_topology[cpu].thread_sibling)
#define topology_llc_cpumask(cpu) (&cpu_topology[cpu].llc_sibling)
void
init_cpu_topology
(
void
);
void
store_cpu_topology
(
unsigned
int
cpuid
);
void
remove_cpu_topology
(
unsigned
int
cpuid
);
const
struct
cpumask
*
cpu_coregroup_mask
(
int
cpu
);
#ifdef CONFIG_NUMA
struct
pci_bus
;
...
...
arch/arm64/kernel/topology.c
View file @
015b2693
...
...
@@ -14,250 +14,13 @@
#include <linux/acpi.h>
#include <linux/arch_topology.h>
#include <linux/cacheinfo.h>
#include <linux/cpu.h>
#include <linux/cpumask.h>
#include <linux/init.h>
#include <linux/percpu.h>
#include <linux/node.h>
#include <linux/nodemask.h>
#include <linux/of.h>
#include <linux/sched.h>
#include <linux/sched/topology.h>
#include <linux/slab.h>
#include <linux/smp.h>
#include <linux/string.h>
#include <asm/cpu.h>
#include <asm/cputype.h>
#include <asm/topology.h>
static
int
__init
get_cpu_for_node
(
struct
device_node
*
node
)
{
struct
device_node
*
cpu_node
;
int
cpu
;
cpu_node
=
of_parse_phandle
(
node
,
"cpu"
,
0
);
if
(
!
cpu_node
)
return
-
1
;
cpu
=
of_cpu_node_to_id
(
cpu_node
);
if
(
cpu
>=
0
)
topology_parse_cpu_capacity
(
cpu_node
,
cpu
);
else
pr_crit
(
"Unable to find CPU node for %pOF
\n
"
,
cpu_node
);
of_node_put
(
cpu_node
);
return
cpu
;
}
static
int
__init
parse_core
(
struct
device_node
*
core
,
int
package_id
,
int
core_id
)
{
char
name
[
10
];
bool
leaf
=
true
;
int
i
=
0
;
int
cpu
;
struct
device_node
*
t
;
do
{
snprintf
(
name
,
sizeof
(
name
),
"thread%d"
,
i
);
t
=
of_get_child_by_name
(
core
,
name
);
if
(
t
)
{
leaf
=
false
;
cpu
=
get_cpu_for_node
(
t
);
if
(
cpu
>=
0
)
{
cpu_topology
[
cpu
].
package_id
=
package_id
;
cpu_topology
[
cpu
].
core_id
=
core_id
;
cpu_topology
[
cpu
].
thread_id
=
i
;
}
else
{
pr_err
(
"%pOF: Can't get CPU for thread
\n
"
,
t
);
of_node_put
(
t
);
return
-
EINVAL
;
}
of_node_put
(
t
);
}
i
++
;
}
while
(
t
);
cpu
=
get_cpu_for_node
(
core
);
if
(
cpu
>=
0
)
{
if
(
!
leaf
)
{
pr_err
(
"%pOF: Core has both threads and CPU
\n
"
,
core
);
return
-
EINVAL
;
}
cpu_topology
[
cpu
].
package_id
=
package_id
;
cpu_topology
[
cpu
].
core_id
=
core_id
;
}
else
if
(
leaf
)
{
pr_err
(
"%pOF: Can't get CPU for leaf core
\n
"
,
core
);
return
-
EINVAL
;
}
return
0
;
}
static
int
__init
parse_cluster
(
struct
device_node
*
cluster
,
int
depth
)
{
char
name
[
10
];
bool
leaf
=
true
;
bool
has_cores
=
false
;
struct
device_node
*
c
;
static
int
package_id
__initdata
;
int
core_id
=
0
;
int
i
,
ret
;
/*
* First check for child clusters; we currently ignore any
* information about the nesting of clusters and present the
* scheduler with a flat list of them.
*/
i
=
0
;
do
{
snprintf
(
name
,
sizeof
(
name
),
"cluster%d"
,
i
);
c
=
of_get_child_by_name
(
cluster
,
name
);
if
(
c
)
{
leaf
=
false
;
ret
=
parse_cluster
(
c
,
depth
+
1
);
of_node_put
(
c
);
if
(
ret
!=
0
)
return
ret
;
}
i
++
;
}
while
(
c
);
/* Now check for cores */
i
=
0
;
do
{
snprintf
(
name
,
sizeof
(
name
),
"core%d"
,
i
);
c
=
of_get_child_by_name
(
cluster
,
name
);
if
(
c
)
{
has_cores
=
true
;
if
(
depth
==
0
)
{
pr_err
(
"%pOF: cpu-map children should be clusters
\n
"
,
c
);
of_node_put
(
c
);
return
-
EINVAL
;
}
if
(
leaf
)
{
ret
=
parse_core
(
c
,
package_id
,
core_id
++
);
}
else
{
pr_err
(
"%pOF: Non-leaf cluster with core %s
\n
"
,
cluster
,
name
);
ret
=
-
EINVAL
;
}
of_node_put
(
c
);
if
(
ret
!=
0
)
return
ret
;
}
i
++
;
}
while
(
c
);
if
(
leaf
&&
!
has_cores
)
pr_warn
(
"%pOF: empty cluster
\n
"
,
cluster
);
if
(
leaf
)
package_id
++
;
return
0
;
}
static
int
__init
parse_dt_topology
(
void
)
{
struct
device_node
*
cn
,
*
map
;
int
ret
=
0
;
int
cpu
;
cn
=
of_find_node_by_path
(
"/cpus"
);
if
(
!
cn
)
{
pr_err
(
"No CPU information found in DT
\n
"
);
return
0
;
}
/*
* When topology is provided cpu-map is essentially a root
* cluster with restricted subnodes.
*/
map
=
of_get_child_by_name
(
cn
,
"cpu-map"
);
if
(
!
map
)
goto
out
;
ret
=
parse_cluster
(
map
,
0
);
if
(
ret
!=
0
)
goto
out_map
;
topology_normalize_cpu_scale
();
/*
* Check that all cores are in the topology; the SMP code will
* only mark cores described in the DT as possible.
*/
for_each_possible_cpu
(
cpu
)
if
(
cpu_topology
[
cpu
].
package_id
==
-
1
)
ret
=
-
EINVAL
;
out_map:
of_node_put
(
map
);
out:
of_node_put
(
cn
);
return
ret
;
}
/*
* cpu topology table
*/
struct
cpu_topology
cpu_topology
[
NR_CPUS
];
EXPORT_SYMBOL_GPL
(
cpu_topology
);
const
struct
cpumask
*
cpu_coregroup_mask
(
int
cpu
)
{
const
cpumask_t
*
core_mask
=
cpumask_of_node
(
cpu_to_node
(
cpu
));
/* Find the smaller of NUMA, core or LLC siblings */
if
(
cpumask_subset
(
&
cpu_topology
[
cpu
].
core_sibling
,
core_mask
))
{
/* not numa in package, lets use the package siblings */
core_mask
=
&
cpu_topology
[
cpu
].
core_sibling
;
}
if
(
cpu_topology
[
cpu
].
llc_id
!=
-
1
)
{
if
(
cpumask_subset
(
&
cpu_topology
[
cpu
].
llc_sibling
,
core_mask
))
core_mask
=
&
cpu_topology
[
cpu
].
llc_sibling
;
}
return
core_mask
;
}
static
void
update_siblings_masks
(
unsigned
int
cpuid
)
{
struct
cpu_topology
*
cpu_topo
,
*
cpuid_topo
=
&
cpu_topology
[
cpuid
];
int
cpu
;
/* update core and thread sibling masks */
for_each_online_cpu
(
cpu
)
{
cpu_topo
=
&
cpu_topology
[
cpu
];
if
(
cpuid_topo
->
llc_id
==
cpu_topo
->
llc_id
)
{
cpumask_set_cpu
(
cpu
,
&
cpuid_topo
->
llc_sibling
);
cpumask_set_cpu
(
cpuid
,
&
cpu_topo
->
llc_sibling
);
}
if
(
cpuid_topo
->
package_id
!=
cpu_topo
->
package_id
)
continue
;
cpumask_set_cpu
(
cpuid
,
&
cpu_topo
->
core_sibling
);
cpumask_set_cpu
(
cpu
,
&
cpuid_topo
->
core_sibling
);
if
(
cpuid_topo
->
core_id
!=
cpu_topo
->
core_id
)
continue
;
cpumask_set_cpu
(
cpuid
,
&
cpu_topo
->
thread_sibling
);
cpumask_set_cpu
(
cpu
,
&
cpuid_topo
->
thread_sibling
);
}
}
void
store_cpu_topology
(
unsigned
int
cpuid
)
{
struct
cpu_topology
*
cpuid_topo
=
&
cpu_topology
[
cpuid
];
...
...
@@ -296,59 +59,19 @@ void store_cpu_topology(unsigned int cpuid)
update_siblings_masks
(
cpuid
);
}
static
void
clear_cpu_topology
(
int
cpu
)
{
struct
cpu_topology
*
cpu_topo
=
&
cpu_topology
[
cpu
];
cpumask_clear
(
&
cpu_topo
->
llc_sibling
);
cpumask_set_cpu
(
cpu
,
&
cpu_topo
->
llc_sibling
);
cpumask_clear
(
&
cpu_topo
->
core_sibling
);
cpumask_set_cpu
(
cpu
,
&
cpu_topo
->
core_sibling
);
cpumask_clear
(
&
cpu_topo
->
thread_sibling
);
cpumask_set_cpu
(
cpu
,
&
cpu_topo
->
thread_sibling
);
}
static
void
__init
reset_cpu_topology
(
void
)
{
unsigned
int
cpu
;
for_each_possible_cpu
(
cpu
)
{
struct
cpu_topology
*
cpu_topo
=
&
cpu_topology
[
cpu
];
cpu_topo
->
thread_id
=
-
1
;
cpu_topo
->
core_id
=
0
;
cpu_topo
->
package_id
=
-
1
;
cpu_topo
->
llc_id
=
-
1
;
clear_cpu_topology
(
cpu
);
}
}
void
remove_cpu_topology
(
unsigned
int
cpu
)
{
int
sibling
;
for_each_cpu
(
sibling
,
topology_core_cpumask
(
cpu
))
cpumask_clear_cpu
(
cpu
,
topology_core_cpumask
(
sibling
));
for_each_cpu
(
sibling
,
topology_sibling_cpumask
(
cpu
))
cpumask_clear_cpu
(
cpu
,
topology_sibling_cpumask
(
sibling
));
for_each_cpu
(
sibling
,
topology_llc_cpumask
(
cpu
))
cpumask_clear_cpu
(
cpu
,
topology_llc_cpumask
(
sibling
));
clear_cpu_topology
(
cpu
);
}
#ifdef CONFIG_ACPI
/*
* Propagate the topology information of the processor_topology_node tree to the
* cpu_topology array.
*/
static
int
__init
parse_acpi_topology
(
void
)
int
__init
parse_acpi_topology
(
void
)
{
bool
is_threaded
;
int
cpu
,
topology_id
;
if
(
acpi_disabled
)
return
0
;
is_threaded
=
read_cpuid_mpidr
()
&
MPIDR_MT_BITMASK
;
for_each_possible_cpu
(
cpu
)
{
...
...
@@ -384,24 +107,6 @@ static int __init parse_acpi_topology(void)
return
0
;
}
#else
static
inline
int
__init
parse_acpi_topology
(
void
)
{
return
-
EINVAL
;
}
#endif
void
__init
init_cpu_topology
(
void
)
{
reset_cpu_topology
();
/*
* Discard anything that was parsed if we hit an error so we
* don't use partial information.
*/
if
(
!
acpi_disabled
&&
parse_acpi_topology
())
reset_cpu_topology
();
else
if
(
of_have_populated_dt
()
&&
parse_dt_topology
())
reset_cpu_topology
();
}
arch/riscv/Kconfig
View file @
015b2693
...
...
@@ -48,6 +48,7 @@ config RISCV
select PCI_MSI if PCI
select RISCV_TIMER
select GENERIC_IRQ_MULTI_HANDLER
select GENERIC_ARCH_TOPOLOGY if SMP
select ARCH_HAS_PTE_SPECIAL
select ARCH_HAS_MMIOWB
select HAVE_EBPF_JIT if 64BIT
...
...
arch/riscv/kernel/smpboot.c
View file @
015b2693
...
...
@@ -8,6 +8,7 @@
* Copyright (C) 2017 SiFive
*/
#include <linux/arch_topology.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
...
...
@@ -35,6 +36,7 @@ static DECLARE_COMPLETION(cpu_running);
void
__init
smp_prepare_boot_cpu
(
void
)
{
init_cpu_topology
();
}
void
__init
smp_prepare_cpus
(
unsigned
int
max_cpus
)
...
...
@@ -138,6 +140,7 @@ asmlinkage void __init smp_callin(void)
trap_init
();
notify_cpu_starting
(
smp_processor_id
());
update_siblings_masks
(
smp_processor_id
());
set_cpu_online
(
smp_processor_id
(),
1
);
/*
* Remote TLB flushes are ignored while the CPU is offline, so emit
...
...
drivers/base/Kconfig
View file @
015b2693
...
...
@@ -202,7 +202,7 @@ config GENERIC_ARCH_TOPOLOGY
help
Enable support for architectures common topology code: e.g., parsing
CPU capacity information from DT, usage of such information for
appropriate scaling, sysfs interface for
chang
ing capacity values at
appropriate scaling, sysfs interface for
read
ing capacity values at
runtime.
endmenu
drivers/base/arch_topology.c
View file @
015b2693
...
...
@@ -15,6 +15,11 @@
#include <linux/string.h>
#include <linux/sched/topology.h>
#include <linux/cpuset.h>
#include <linux/cpumask.h>
#include <linux/init.h>
#include <linux/percpu.h>
#include <linux/sched.h>
#include <linux/smp.h>
DEFINE_PER_CPU
(
unsigned
long
,
freq_scale
)
=
SCHED_CAPACITY_SCALE
;
...
...
@@ -241,3 +246,296 @@ static void parsing_done_workfn(struct work_struct *work)
#else
core_initcall
(
free_raw_capacity
);
#endif
#if defined(CONFIG_ARM64) || defined(CONFIG_RISCV)
static
int
__init
get_cpu_for_node
(
struct
device_node
*
node
)
{
struct
device_node
*
cpu_node
;
int
cpu
;
cpu_node
=
of_parse_phandle
(
node
,
"cpu"
,
0
);
if
(
!
cpu_node
)
return
-
1
;
cpu
=
of_cpu_node_to_id
(
cpu_node
);
if
(
cpu
>=
0
)
topology_parse_cpu_capacity
(
cpu_node
,
cpu
);
else
pr_crit
(
"Unable to find CPU node for %pOF
\n
"
,
cpu_node
);
of_node_put
(
cpu_node
);
return
cpu
;
}
static
int
__init
parse_core
(
struct
device_node
*
core
,
int
package_id
,
int
core_id
)
{
char
name
[
10
];
bool
leaf
=
true
;
int
i
=
0
;
int
cpu
;
struct
device_node
*
t
;
do
{
snprintf
(
name
,
sizeof
(
name
),
"thread%d"
,
i
);
t
=
of_get_child_by_name
(
core
,
name
);
if
(
t
)
{
leaf
=
false
;
cpu
=
get_cpu_for_node
(
t
);
if
(
cpu
>=
0
)
{
cpu_topology
[
cpu
].
package_id
=
package_id
;
cpu_topology
[
cpu
].
core_id
=
core_id
;
cpu_topology
[
cpu
].
thread_id
=
i
;
}
else
{
pr_err
(
"%pOF: Can't get CPU for thread
\n
"
,
t
);
of_node_put
(
t
);
return
-
EINVAL
;
}
of_node_put
(
t
);
}
i
++
;
}
while
(
t
);
cpu
=
get_cpu_for_node
(
core
);
if
(
cpu
>=
0
)
{
if
(
!
leaf
)
{
pr_err
(
"%pOF: Core has both threads and CPU
\n
"
,
core
);
return
-
EINVAL
;
}
cpu_topology
[
cpu
].
package_id
=
package_id
;
cpu_topology
[
cpu
].
core_id
=
core_id
;
}
else
if
(
leaf
)
{
pr_err
(
"%pOF: Can't get CPU for leaf core
\n
"
,
core
);
return
-
EINVAL
;
}
return
0
;
}
static
int
__init
parse_cluster
(
struct
device_node
*
cluster
,
int
depth
)
{
char
name
[
10
];
bool
leaf
=
true
;
bool
has_cores
=
false
;
struct
device_node
*
c
;
static
int
package_id
__initdata
;
int
core_id
=
0
;
int
i
,
ret
;
/*
* First check for child clusters; we currently ignore any
* information about the nesting of clusters and present the
* scheduler with a flat list of them.
*/
i
=
0
;
do
{
snprintf
(
name
,
sizeof
(
name
),
"cluster%d"
,
i
);
c
=
of_get_child_by_name
(
cluster
,
name
);
if
(
c
)
{
leaf
=
false
;
ret
=
parse_cluster
(
c
,
depth
+
1
);
of_node_put
(
c
);
if
(
ret
!=
0
)
return
ret
;
}
i
++
;
}
while
(
c
);
/* Now check for cores */
i
=
0
;
do
{
snprintf
(
name
,
sizeof
(
name
),
"core%d"
,
i
);
c
=
of_get_child_by_name
(
cluster
,
name
);
if
(
c
)
{
has_cores
=
true
;
if
(
depth
==
0
)
{
pr_err
(
"%pOF: cpu-map children should be clusters
\n
"
,
c
);
of_node_put
(
c
);
return
-
EINVAL
;
}
if
(
leaf
)
{
ret
=
parse_core
(
c
,
package_id
,
core_id
++
);
}
else
{
pr_err
(
"%pOF: Non-leaf cluster with core %s
\n
"
,
cluster
,
name
);
ret
=
-
EINVAL
;
}
of_node_put
(
c
);
if
(
ret
!=
0
)
return
ret
;
}
i
++
;
}
while
(
c
);
if
(
leaf
&&
!
has_cores
)
pr_warn
(
"%pOF: empty cluster
\n
"
,
cluster
);
if
(
leaf
)
package_id
++
;
return
0
;
}
static
int
__init
parse_dt_topology
(
void
)
{
struct
device_node
*
cn
,
*
map
;
int
ret
=
0
;
int
cpu
;
cn
=
of_find_node_by_path
(
"/cpus"
);
if
(
!
cn
)
{
pr_err
(
"No CPU information found in DT
\n
"
);
return
0
;
}
/*
* When topology is provided cpu-map is essentially a root
* cluster with restricted subnodes.
*/
map
=
of_get_child_by_name
(
cn
,
"cpu-map"
);
if
(
!
map
)
goto
out
;
ret
=
parse_cluster
(
map
,
0
);
if
(
ret
!=
0
)
goto
out_map
;
topology_normalize_cpu_scale
();
/*
* Check that all cores are in the topology; the SMP code will
* only mark cores described in the DT as possible.
*/
for_each_possible_cpu
(
cpu
)
if
(
cpu_topology
[
cpu
].
package_id
==
-
1
)
ret
=
-
EINVAL
;
out_map:
of_node_put
(
map
);
out:
of_node_put
(
cn
);
return
ret
;
}
#endif
/*
* cpu topology table
*/
struct
cpu_topology
cpu_topology
[
NR_CPUS
];
EXPORT_SYMBOL_GPL
(
cpu_topology
);
const
struct
cpumask
*
cpu_coregroup_mask
(
int
cpu
)
{
const
cpumask_t
*
core_mask
=
cpumask_of_node
(
cpu_to_node
(
cpu
));
/* Find the smaller of NUMA, core or LLC siblings */
if
(
cpumask_subset
(
&
cpu_topology
[
cpu
].
core_sibling
,
core_mask
))
{
/* not numa in package, lets use the package siblings */
core_mask
=
&
cpu_topology
[
cpu
].
core_sibling
;
}
if
(
cpu_topology
[
cpu
].
llc_id
!=
-
1
)
{
if
(
cpumask_subset
(
&
cpu_topology
[
cpu
].
llc_sibling
,
core_mask
))
core_mask
=
&
cpu_topology
[
cpu
].
llc_sibling
;
}
return
core_mask
;
}
void
update_siblings_masks
(
unsigned
int
cpuid
)
{
struct
cpu_topology
*
cpu_topo
,
*
cpuid_topo
=
&
cpu_topology
[
cpuid
];
int
cpu
;
/* update core and thread sibling masks */
for_each_online_cpu
(
cpu
)
{
cpu_topo
=
&
cpu_topology
[
cpu
];
if
(
cpuid_topo
->
llc_id
==
cpu_topo
->
llc_id
)
{
cpumask_set_cpu
(
cpu
,
&
cpuid_topo
->
llc_sibling
);
cpumask_set_cpu
(
cpuid
,
&
cpu_topo
->
llc_sibling
);
}
if
(
cpuid_topo
->
package_id
!=
cpu_topo
->
package_id
)
continue
;
cpumask_set_cpu
(
cpuid
,
&
cpu_topo
->
core_sibling
);
cpumask_set_cpu
(
cpu
,
&
cpuid_topo
->
core_sibling
);
if
(
cpuid_topo
->
core_id
!=
cpu_topo
->
core_id
)
continue
;
cpumask_set_cpu
(
cpuid
,
&
cpu_topo
->
thread_sibling
);
cpumask_set_cpu
(
cpu
,
&
cpuid_topo
->
thread_sibling
);
}
}
static
void
clear_cpu_topology
(
int
cpu
)
{
struct
cpu_topology
*
cpu_topo
=
&
cpu_topology
[
cpu
];
cpumask_clear
(
&
cpu_topo
->
llc_sibling
);
cpumask_set_cpu
(
cpu
,
&
cpu_topo
->
llc_sibling
);
cpumask_clear
(
&
cpu_topo
->
core_sibling
);
cpumask_set_cpu
(
cpu
,
&
cpu_topo
->
core_sibling
);
cpumask_clear
(
&
cpu_topo
->
thread_sibling
);
cpumask_set_cpu
(
cpu
,
&
cpu_topo
->
thread_sibling
);
}
void
__init
reset_cpu_topology
(
void
)
{
unsigned
int
cpu
;
for_each_possible_cpu
(
cpu
)
{
struct
cpu_topology
*
cpu_topo
=
&
cpu_topology
[
cpu
];
cpu_topo
->
thread_id
=
-
1
;
cpu_topo
->
core_id
=
-
1
;
cpu_topo
->
package_id
=
-
1
;
cpu_topo
->
llc_id
=
-
1
;
clear_cpu_topology
(
cpu
);
}
}
void
remove_cpu_topology
(
unsigned
int
cpu
)
{
int
sibling
;
for_each_cpu
(
sibling
,
topology_core_cpumask
(
cpu
))
cpumask_clear_cpu
(
cpu
,
topology_core_cpumask
(
sibling
));
for_each_cpu
(
sibling
,
topology_sibling_cpumask
(
cpu
))
cpumask_clear_cpu
(
cpu
,
topology_sibling_cpumask
(
sibling
));
for_each_cpu
(
sibling
,
topology_llc_cpumask
(
cpu
))
cpumask_clear_cpu
(
cpu
,
topology_llc_cpumask
(
sibling
));
clear_cpu_topology
(
cpu
);
}
__weak
int
__init
parse_acpi_topology
(
void
)
{
return
0
;
}
#if defined(CONFIG_ARM64) || defined(CONFIG_RISCV)
void
__init
init_cpu_topology
(
void
)
{
reset_cpu_topology
();
/*
* Discard anything that was parsed if we hit an error so we
* don't use partial information.
*/
if
(
parse_acpi_topology
())
reset_cpu_topology
();
else
if
(
of_have_populated_dt
()
&&
parse_dt_topology
())
reset_cpu_topology
();
}
#endif
include/linux/arch_topology.h
View file @
015b2693
...
...
@@ -33,4 +33,30 @@ unsigned long topology_get_freq_scale(int cpu)
return
per_cpu
(
freq_scale
,
cpu
);
}
struct
cpu_topology
{
int
thread_id
;
int
core_id
;
int
package_id
;
int
llc_id
;
cpumask_t
thread_sibling
;
cpumask_t
core_sibling
;
cpumask_t
llc_sibling
;
};
#ifdef CONFIG_GENERIC_ARCH_TOPOLOGY
extern
struct
cpu_topology
cpu_topology
[
NR_CPUS
];
#define topology_physical_package_id(cpu) (cpu_topology[cpu].package_id)
#define topology_core_id(cpu) (cpu_topology[cpu].core_id)
#define topology_core_cpumask(cpu) (&cpu_topology[cpu].core_sibling)
#define topology_sibling_cpumask(cpu) (&cpu_topology[cpu].thread_sibling)
#define topology_llc_cpumask(cpu) (&cpu_topology[cpu].llc_sibling)
void
init_cpu_topology
(
void
);
void
store_cpu_topology
(
unsigned
int
cpuid
);
const
struct
cpumask
*
cpu_coregroup_mask
(
int
cpu
);
void
update_siblings_masks
(
unsigned
int
cpu
);
void
remove_cpu_topology
(
unsigned
int
cpuid
);
void
reset_cpu_topology
(
void
);
#endif
#endif
/* _LINUX_ARCH_TOPOLOGY_H_ */
include/linux/topology.h
View file @
015b2693
...
...
@@ -27,6 +27,7 @@
#ifndef _LINUX_TOPOLOGY_H
#define _LINUX_TOPOLOGY_H
#include <linux/arch_topology.h>
#include <linux/cpumask.h>
#include <linux/bitops.h>
#include <linux/mmzone.h>
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment