Skip to content

Commit 05484e0

Browse files
msrasmussenIngo Molnar
authored andcommitted
sched/topology: Add SD_ASYM_CPUCAPACITY flag detection
The SD_ASYM_CPUCAPACITY sched_domain flag is supposed to mark the sched_domain in the hierarchy where all CPU capacities are visible for any CPU's point of view on asymmetric CPU capacity systems. The scheduler can then take to take capacity asymmetry into account when balancing at this level. It also serves as an indicator for how wide task placement heuristics have to search to consider all available CPU capacities as asymmetric systems might often appear symmetric at smallest level(s) of the sched_domain hierarchy. The flag has been around for while but so far only been set by out-of-tree code in Android kernels. One solution is to let each architecture provide the flag through a custom sched_domain topology array and associated mask and flag functions. However, SD_ASYM_CPUCAPACITY is special in the sense that it depends on the capacity and presence of all CPUs in the system, i.e. when hotplugging all CPUs out except those with one particular CPU capacity the flag should disappear even if the sched_domains don't collapse. Similarly, the flag is affected by cpusets where load-balancing is turned off. Detecting when the flags should be set therefore depends not only on topology information but also the cpuset configuration and hotplug state. The arch code doesn't have easy access to the cpuset configuration. Instead, this patch implements the flag detection in generic code where cpusets and hotplug state is already taken care of. All the arch is responsible for is to implement arch_scale_cpu_capacity() and force a full rebuild of the sched_domain hierarchy if capacities are updated, e.g. later in the boot process when cpufreq has initialized. Signed-off-by: Morten Rasmussen <morten.rasmussen@arm.com> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: dietmar.eggemann@arm.com Cc: valentin.schneider@arm.com Cc: vincent.guittot@linaro.org Link: http://lkml.kernel.org/r/1532093554-30504-2-git-send-email-morten.rasmussen@arm.com [ Fixed 'CPU' capitalization. ] Signed-off-by: Ingo Molnar <mingo@kernel.org>
1 parent 882a78a commit 05484e0

File tree

2 files changed

+78
-9
lines changed

2 files changed

+78
-9
lines changed

include/linux/sched/topology.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,10 @@
2323
#define SD_BALANCE_FORK 0x0008 /* Balance on fork, clone */
2424
#define SD_BALANCE_WAKE 0x0010 /* Balance on wakeup */
2525
#define SD_WAKE_AFFINE 0x0020 /* Wake task to waking CPU */
26-
#define SD_ASYM_CPUCAPACITY 0x0040 /* Groups have different max cpu capacities */
27-
#define SD_SHARE_CPUCAPACITY 0x0080 /* Domain members share cpu capacity */
26+
#define SD_ASYM_CPUCAPACITY 0x0040 /* Domain members have different CPU capacities */
27+
#define SD_SHARE_CPUCAPACITY 0x0080 /* Domain members share CPU capacity */
2828
#define SD_SHARE_POWERDOMAIN 0x0100 /* Domain members share power domain */
29-
#define SD_SHARE_PKG_RESOURCES 0x0200 /* Domain members share cpu pkg resources */
29+
#define SD_SHARE_PKG_RESOURCES 0x0200 /* Domain members share CPU pkg resources */
3030
#define SD_SERIALIZE 0x0400 /* Only a single load balancing instance */
3131
#define SD_ASYM_PACKING 0x0800 /* Place busy groups earlier in the domain */
3232
#define SD_PREFER_SIBLING 0x1000 /* Prefer to place tasks in a sibling domain */

kernel/sched/topology.c

Lines changed: 75 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1061,7 +1061,6 @@ static struct cpumask ***sched_domains_numa_masks;
10611061
* SD_SHARE_PKG_RESOURCES - describes shared caches
10621062
* SD_NUMA - describes NUMA topologies
10631063
* SD_SHARE_POWERDOMAIN - describes shared power domain
1064-
* SD_ASYM_CPUCAPACITY - describes mixed capacity topologies
10651064
*
10661065
* Odd one out, which beside describing the topology has a quirk also
10671066
* prescribes the desired behaviour that goes along with it:
@@ -1073,13 +1072,12 @@ static struct cpumask ***sched_domains_numa_masks;
10731072
SD_SHARE_PKG_RESOURCES | \
10741073
SD_NUMA | \
10751074
SD_ASYM_PACKING | \
1076-
SD_ASYM_CPUCAPACITY | \
10771075
SD_SHARE_POWERDOMAIN)
10781076

10791077
static struct sched_domain *
10801078
sd_init(struct sched_domain_topology_level *tl,
10811079
const struct cpumask *cpu_map,
1082-
struct sched_domain *child, int cpu)
1080+
struct sched_domain *child, int dflags, int cpu)
10831081
{
10841082
struct sd_data *sdd = &tl->data;
10851083
struct sched_domain *sd = *per_cpu_ptr(sdd->sd, cpu);
@@ -1100,6 +1098,9 @@ sd_init(struct sched_domain_topology_level *tl,
11001098
"wrong sd_flags in topology description\n"))
11011099
sd_flags &= ~TOPOLOGY_SD_FLAGS;
11021100

1101+
/* Apply detected topology flags */
1102+
sd_flags |= dflags;
1103+
11031104
*sd = (struct sched_domain){
11041105
.min_interval = sd_weight,
11051106
.max_interval = 2*sd_weight,
@@ -1604,9 +1605,9 @@ static void __sdt_free(const struct cpumask *cpu_map)
16041605

16051606
static struct sched_domain *build_sched_domain(struct sched_domain_topology_level *tl,
16061607
const struct cpumask *cpu_map, struct sched_domain_attr *attr,
1607-
struct sched_domain *child, int cpu)
1608+
struct sched_domain *child, int dflags, int cpu)
16081609
{
1609-
struct sched_domain *sd = sd_init(tl, cpu_map, child, cpu);
1610+
struct sched_domain *sd = sd_init(tl, cpu_map, child, dflags, cpu);
16101611

16111612
if (child) {
16121613
sd->level = child->level + 1;
@@ -1632,6 +1633,65 @@ static struct sched_domain *build_sched_domain(struct sched_domain_topology_leve
16321633
return sd;
16331634
}
16341635

1636+
/*
1637+
* Find the sched_domain_topology_level where all CPU capacities are visible
1638+
* for all CPUs.
1639+
*/
1640+
static struct sched_domain_topology_level
1641+
*asym_cpu_capacity_level(const struct cpumask *cpu_map)
1642+
{
1643+
int i, j, asym_level = 0;
1644+
bool asym = false;
1645+
struct sched_domain_topology_level *tl, *asym_tl = NULL;
1646+
unsigned long cap;
1647+
1648+
/* Is there any asymmetry? */
1649+
cap = arch_scale_cpu_capacity(NULL, cpumask_first(cpu_map));
1650+
1651+
for_each_cpu(i, cpu_map) {
1652+
if (arch_scale_cpu_capacity(NULL, i) != cap) {
1653+
asym = true;
1654+
break;
1655+
}
1656+
}
1657+
1658+
if (!asym)
1659+
return NULL;
1660+
1661+
/*
1662+
* Examine topology from all CPU's point of views to detect the lowest
1663+
* sched_domain_topology_level where a highest capacity CPU is visible
1664+
* to everyone.
1665+
*/
1666+
for_each_cpu(i, cpu_map) {
1667+
unsigned long max_capacity = arch_scale_cpu_capacity(NULL, i);
1668+
int tl_id = 0;
1669+
1670+
for_each_sd_topology(tl) {
1671+
if (tl_id < asym_level)
1672+
goto next_level;
1673+
1674+
for_each_cpu_and(j, tl->mask(i), cpu_map) {
1675+
unsigned long capacity;
1676+
1677+
capacity = arch_scale_cpu_capacity(NULL, j);
1678+
1679+
if (capacity <= max_capacity)
1680+
continue;
1681+
1682+
max_capacity = capacity;
1683+
asym_level = tl_id;
1684+
asym_tl = tl;
1685+
}
1686+
next_level:
1687+
tl_id++;
1688+
}
1689+
}
1690+
1691+
return asym_tl;
1692+
}
1693+
1694+
16351695
/*
16361696
* Build sched domains for a given set of CPUs and attach the sched domains
16371697
* to the individual CPUs
@@ -1644,18 +1704,27 @@ build_sched_domains(const struct cpumask *cpu_map, struct sched_domain_attr *att
16441704
struct s_data d;
16451705
struct rq *rq = NULL;
16461706
int i, ret = -ENOMEM;
1707+
struct sched_domain_topology_level *tl_asym;
16471708

16481709
alloc_state = __visit_domain_allocation_hell(&d, cpu_map);
16491710
if (alloc_state != sa_rootdomain)
16501711
goto error;
16511712

1713+
tl_asym = asym_cpu_capacity_level(cpu_map);
1714+
16521715
/* Set up domains for CPUs specified by the cpu_map: */
16531716
for_each_cpu(i, cpu_map) {
16541717
struct sched_domain_topology_level *tl;
16551718

16561719
sd = NULL;
16571720
for_each_sd_topology(tl) {
1658-
sd = build_sched_domain(tl, cpu_map, attr, sd, i);
1721+
int dflags = 0;
1722+
1723+
if (tl == tl_asym)
1724+
dflags |= SD_ASYM_CPUCAPACITY;
1725+
1726+
sd = build_sched_domain(tl, cpu_map, attr, sd, dflags, i);
1727+
16591728
if (tl == sched_domain_topology)
16601729
*per_cpu_ptr(d.sd, i) = sd;
16611730
if (tl->flags & SDTL_OVERLAP)

0 commit comments

Comments
 (0)