cpuset: fix wrong calculation of relax domain level
RFC PATCH 1/1 cpuset: fix wrong calculation of relax domain level
When multiple cpusets are overlapping in their 'cpus' and hence they form a single sched domain, the largest sched_relax_domain_level among those should be used. But when top_cpuset's sched_load_balance is set, its sched_relax_domain_level is used regardless other sub-cpusets'.
There are several proposals to solve this:
1) Travel the cpuset hierarchy to find the largest relax_domain_level in rebuild_sched_domains(). But cpuset avoids hierarchy travelling when top_cpuset.sched_load_balance is set.
2) Remember the largest relax_domain_level when we update a cpuset's sched_load_balance, sched_relax_domain_level and cpus. This should work, but seems a bit tricky and a bit ugly. (As this patch shows)
3) Don't treat this as a bug, but document this behavior.
--- cpuset.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 2 deletions(-)
kernel/cpuset.c +48 -2
@@ -69,6 +69,14 @@ int number_of_cpusets __read_mostly;struct cgroup_subsys cpuset_subsys;struct cpuset ; +/* +* Tracks # of cpusets in each relax domain level. This is to avoid +* travelling the cpuset hierachy in rebuild_sched_domains() +* when top_cpuset.sched_load_balance == 1. +*/ +static unsigned int __cpusets_rd_lv[SD_LV_MAX +1 ]; +static unsigned int *cpusets_rd_lv = __cpusets_rd_lv +1 ; +/* See "Frequency meter" comments, below. */ struct fmeter {
@@ -594,6 +602,14 @@ static void rebuild_sched_domains(void)update_domain_attr (dattr, &top_cpuset); } *doms = top_cpuset.cpus_allowed; + +for (i =SD_LV_MAX -1 ; i >=0 ; i--) { +if (cpusets_rd_lv[i] && dattr) { + dattr->relax_domain_level = i; +break ; + } + } + goto rebuild; }
@@ -807,6 +823,7 @@ static int update_cpumask(struct cpusetstruct cpuset trialcs;int retval;int is_load_balanced; +int cpus_empty_changed;/* top_cpuset.cpus_allowed tracks cpu_online_map; it's read-only */ if (cs == &top_cpuset)
@@ -839,11 +856,20 @@ static int update_cpumask(struct cpusetreturn 0 ; is_load_balanced =is_sched_load_balance (&trialcs); + cpus_empty_changed = (cpus_empty (cs->cpus_allowed) != +cpus_empty (trialcs.cpus_allowed));mutex_lock (&callback_mutex); cs->cpus_allowed = trialcs.cpus_allowed;mutex_unlock (&callback_mutex); +if (is_load_balanced && cpus_empty_changed) { +if (cpus_empty (cs->cpus_allowed)) + cpusets_rd_lv[cs->relax_domain_level]--; +else + cpusets_rd_lv[cs->relax_domain_level]++; + } +/* * Scan tasks in the cpuset, and update the cpumasks of any * that need an update.
@@ -1074,12 +1100,19 @@ int current_cpuset_is_being_rebound(voidstatic int update_relax_domain_level (struct cpuset *cs,s64 val) { +int need_rebuild = (!cpus_empty (cs->cpus_allowed) && +is_sched_load_balance (cs)); +if (val <-1 || val >=SD_LV_MAX )return -EINVAL ;if (val != cs->relax_domain_level) { +if (need_rebuild) { + cpusets_rd_lv[cs->relax_domain_level]--; + cpusets_rd_lv[val]++; + } cs->relax_domain_level = val; -if (!cpus_empty (cs->cpus_allowed) &&is_sched_load_balance (cs)) +if (need_rebuild)rebuild_sched_domains (); }
@@ -1120,8 +1153,13 @@ static int update_flag(cpuset_flagbits_t cs->flags = trialcs.flags;mutex_unlock (&callback_mutex); -if (cpus_nonempty && balance_flag_changed) +if (cpus_nonempty && balance_flag_changed) { +if (is_sched_load_balance (cs)) + cpusets_rd_lv[cs->relax_domain_level]++; +else + cpusets_rd_lv[cs->relax_domain_level]--;rebuild_sched_domains (); + }return 0 ; }
@@ -1856,6 +1894,7 @@ static void scan_for_empty_cpusets(conststruct list_head queue;struct cgroup *cont;nodemask_t oldmems; +cpumask_t oldcpus;INIT_LIST_HEAD (&queue);
@@ -1876,6 +1915,7 @@ static void scan_for_empty_cpusets(constcontinue ; oldmems = cp->mems_allowed; + oldcpus = cp->cpus_allowed;/* Remove offline cpus and mems from this cpuset. */ mutex_lock (&callback_mutex);
@@ -1884,6 +1924,12 @@ static void scan_for_empty_cpusets(const node_states[N_HIGH_MEMORY]);mutex_unlock (&callback_mutex); +if (is_sched_load_balance (cp)) { +if (cpus_empty (cp->cpus_allowed) && + !cpus_empty (oldcpus)) + cpusets_rd_lv[cp->relax_domain_level]--; + } +/* Move tasks from the empty cpuset to a parent */ if (cpus_empty (cp->cpus_allowed) ||nodes_empty (cp->mems_allowed))
Quoted 11 lines · Li Zefan
When multiple cpusets are overlapping in their 'cpus' and hence they form a single sched domain, the largest
sched_relax_domain_levelamong those should be used. But when top_cpuset'ssched_load_balanceis set, itssched_relax_domain_levelis used regardless other sub-cpusets'.There are several proposals to solve this:
1) Travel the cpuset hierarchy to find the largest
relax_domain_levelinrebuild_sched_domains(). But cpuset avoids hierarchy travelling when top_cpuset.sched_load_balance is set.
Quoted 5 lines
2) Remember the largest
relax_domain_levelwhen we update a cpuset'ssched_load_balance,sched_relax_domain_leveland cpus. This should work, but seems a bit tricky and a bit ugly. (As this patch shows)3) Don't treat this as a bug, but document this behavior.
I think 1) is correct way.
There was a special short path for the top_cpuset's case, but now it is disappeared. I think there are no need to treat the top_cpuset as VIP, so 2) is excessive nurturing.
Thanks,
H.Seto
Quoted 16 lines · Hidetoshi Seto
Quoted 15 lines · Li Zefan
When multiple cpusets are overlapping in their 'cpus' and hence they form a single sched domain, the largest
sched_relax_domain_levelamong those should be used. But when top_cpuset'ssched_load_balanceis set, itssched_relax_domain_levelis used regardless other sub-cpusets'.There are several proposals to solve this:
1) Travel the cpuset hierarchy to find the largest
relax_domain_levelinrebuild_sched_domains(). But cpuset avoids hierarchy travelling when top_cpuset.sched_load_balance is set.2) Remember the largest
relax_domain_levelwhen we update a cpuset'ssched_load_balance,sched_relax_domain_leveland cpus. This should work, but seems a bit tricky and a bit ugly. (As this patch shows)
Quoted 8 lines
Quoted 1 line
3) Don't treat this as a bug, but document this behavior.
I think 1) is correct way.
There was a special short path for the top_cpuset's case, but now it is disappeared. I think there are no need to treat the
top_cpusetas VIP, so 2) is excessive nurturing.
If we all agree on this, I'll send a new patch to fix this.
In looking at this, I notice something I should have questioned before.
The include/linux/sched.h code:
struct sched_domain_attr {
int relax_domain_level;
};
#define SD_ATTR_INIT (struct sched_domain_attr) { \
.relax_domain_level = -1, \
} and the associated passing of relax_domain_level's embedded inside a kmalloc'c struct sched_domain_attr 'dattr' seems like excessive obfuscating apparatus to me. Unless someone has short term plans to be adding some other attributes to this sched_domain_attr, I suspect it would make more sense just to pass relax_domain_level's as explicit lvalues, dropping all this attr stuff.
Adding unnecessary abstractions 'for future growth' is usually a bad idea. It impedes current code understanding more than it aids future code growth.
Signature · 3 lines
I won't rest till it's the best ...
Programmer, Linux Scalability
Paul Jackson <[email protected]> 1.940.382.4214Quoted 4 lines · Li Zefan
When multiple cpusets are overlapping in their 'cpus' and hence they form a single sched domain, the largest
sched_relax_domain_levelamong those should be used. But when top_cpuset'ssched_load_balanceis set, itssched_relax_domain_levelis used regardless other sub-cpusets'.
This code has gotten too complicated for my modest brain ;).
Question:
In the case that the top_cpuset's sched_load_balance is -not- set, is there code already present that sets the sched_relax_domain_level in overlapping cpusets to the largest value in any of the overlapping cpusets?
If so, where is that code?
Signature · 3 lines
I won't rest till it's the best ...
Programmer, Linux Scalability
Paul Jackson <[email protected]> 1.940.382.4214Quoted 9 lines · Paul Jackson
Question:
In the case that the top_cpuset's
sched_load_balanceis -not- set, is there code already present that sets thesched_relax_domain_levelin overlapping cpusets to the largest value in any of the overlapping cpusets?If so, where is that code?
My humble answer:
static void rebuild_sched_domains(void)
{
:
while (__kfifo_get(q, (void *)&cp, sizeof(cp))) {
// pick up cpusets with sched_load_balance = 1
}
:
restart:
/* Find the best partition (set of sched domains) */
for (i = 0; i < csn; i++) {
// check overlap and set proper partition number
}
:
for (nslot = 0, i = 0; i < csn; i++) {
:
if (apn == b->pn) {
// make map and attr from all cpusets
// having same partition number
cpus_or(*dp, *dp, b->cpus_allowed);
b->pn = -1;
if (dattr)
update_domain_attr(dattr
+ nslot, b);
}
:
} :
rebuild:
:
done:
:
}
So the codes you searching is near by 'update_domain_attr' above, I guess.
Thanks,
H.Seto
Quoted 1 line · Seto-san
My humble answer:
Thank-you, sir.
Signature · 3 lines
I won't rest till it's the best ...
Programmer, Linux Scalability
Paul Jackson <[email protected]> 1.940.382.4214Quoted 8 lines · Paul Jackson
Quoted 4 lines · Li Zefan
When multiple cpusets are overlapping in their 'cpus' and hence they form a single sched domain, the largest
sched_relax_domain_levelamong those should be used. But when top_cpuset'ssched_load_balanceis set, itssched_relax_domain_levelis used regardless other sub-cpusets'.This code has gotten too complicated for my modest brain ;).
Quoted 9 lines
Question:
In the case that the top_cpuset's
sched_load_balanceis -not- set, is there code already present that sets thesched_relax_domain_levelin overlapping cpusets to the largest value in any of the overlapping cpusets?If so, where is that code?
It was your idea to use the largest sched_load_balance for overlapping cpusets. ;)
Quoted 1 line
It was your idea to use the largest
sched_load_balancefor overlapping cpusets.
Yes ... quite so ;).
Signature · 3 lines
I won't rest till it's the best ...
Programmer, Linux Scalability
Paul Jackson <[email protected]> 1.940.382.4214