mirror of
https://github.com/linux-msm/laptops-kernel.git
synced 2026-08-13 14:19:53 -07:00
sched/fair: Add cgroup_mode: max
In order to avoid the average CPU fraction avg(F_g_n) becoming tiny '1/N', assume each cgroup is maximally concurrent and distrubute 'N*weight', such that: F_g_n' = N * F_g_n Giving: avg(F_g_n') = N*avg(F_g_n) ~ N * 1/N = 1 And while this sounds like it solves things, remember what that ~ meant. There is the corner case when a cgroup is minimally loaded, eg a single runnable task, therefore limit the CPU fraction to that of a nice -20 task to avoid getting too much load. This last bit is what makes it different from a previous proposal to allow raising cpu.weight to '100 * N', that would not limit the mininal concurrency case and results in a very large F_g_n. And just like F_g_n << 1 is problematic, so is F_g_n >> 1 for the exact same reasons (it would drown the kthreads, but it also risks overflowing the load values). So while this might appear to be a better scheme than the current default scheme, it doesn't really handle less than maximal concurrency nicely -- it clips and introduces artificially large weights. So where the traditional SMP mode works well when nr_tasks << nr_cpus, MAX doesn't work well in that regime and vice-versa. The meaning of "cpu.weight" would be: weight per allowed CPU. Included for completeness (and infrastructure). Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Link: https://patch.msgid.link/20260605124051.589618504%40infradead.org
This commit is contained in:
@@ -80,6 +80,7 @@ extern void lockdep_assert_cpuset_lock_held(void);
|
||||
extern void cpuset_cpus_allowed_locked(struct task_struct *p, struct cpumask *mask);
|
||||
extern void cpuset_cpus_allowed(struct task_struct *p, struct cpumask *mask);
|
||||
extern bool cpuset_cpus_allowed_fallback(struct task_struct *p);
|
||||
extern int cpuset_num_cpus(struct cgroup *cgroup);
|
||||
extern nodemask_t cpuset_mems_allowed(struct task_struct *p);
|
||||
#define cpuset_current_mems_allowed (current->mems_allowed)
|
||||
void cpuset_init_current_mems_allowed(void);
|
||||
@@ -216,6 +217,11 @@ static inline bool cpuset_cpus_allowed_fallback(struct task_struct *p)
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline int cpuset_num_cpus(struct cgroup *cgroup)
|
||||
{
|
||||
return num_online_cpus();
|
||||
}
|
||||
|
||||
static inline nodemask_t cpuset_mems_allowed(struct task_struct *p)
|
||||
{
|
||||
return node_possible_map;
|
||||
|
||||
@@ -4124,6 +4124,28 @@ bool cpuset_cpus_allowed_fallback(struct task_struct *tsk)
|
||||
return changed;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the number of CPUs available for this cgroup.
|
||||
*
|
||||
* This only really works for cgroup-v2 where all the controllers are mounted
|
||||
* in the same hierarchy. If not cgroup-v2 or no cpuset controller is
|
||||
* configured it reverts to num_online_cpus().
|
||||
*/
|
||||
int cpuset_num_cpus(struct cgroup *cgrp)
|
||||
{
|
||||
int nr = num_online_cpus();
|
||||
struct cpuset *cs;
|
||||
|
||||
if (is_in_v2_mode()) {
|
||||
guard(rcu)();
|
||||
cs = css_cs(cgroup_e_css(cgrp, &cpuset_cgrp_subsys));
|
||||
if (cs)
|
||||
nr = cpumask_weight(cs->effective_cpus);
|
||||
}
|
||||
|
||||
return nr;
|
||||
}
|
||||
|
||||
void __init cpuset_init_current_mems_allowed(void)
|
||||
{
|
||||
nodes_setall(current->mems_allowed);
|
||||
|
||||
@@ -640,6 +640,7 @@ static int cgroup_mode = 1;
|
||||
static const char *cgroup_mode_str[] = {
|
||||
"up",
|
||||
"smp",
|
||||
"max",
|
||||
};
|
||||
|
||||
static int sched_cgroup_mode(const char *str)
|
||||
|
||||
+47
-5
@@ -4801,12 +4801,10 @@ static inline int throttled_hierarchy(struct cfs_rq *cfs_rq);
|
||||
*
|
||||
* hence icky!
|
||||
*/
|
||||
static long calc_smp_shares(struct cfs_rq *cfs_rq)
|
||||
static long __calc_smp_shares(struct cfs_rq *cfs_rq, long tg_shares, long shares_max)
|
||||
{
|
||||
long tg_weight, tg_shares, load, shares;
|
||||
struct task_group *tg = cfs_rq->tg;
|
||||
|
||||
tg_shares = READ_ONCE(tg->shares);
|
||||
long tg_weight, load, shares;
|
||||
|
||||
load = max(scale_load_down(cfs_rq->load.weight), cfs_rq->avg.load_avg);
|
||||
|
||||
@@ -4832,7 +4830,48 @@ static long calc_smp_shares(struct cfs_rq *cfs_rq)
|
||||
* case no task is runnable on a CPU MIN_SHARES=2 should be returned
|
||||
* instead of 0.
|
||||
*/
|
||||
return clamp_t(long, shares, MIN_SHARES, tg_shares);
|
||||
return clamp_t(long, shares, MIN_SHARES, shares_max);
|
||||
}
|
||||
|
||||
static int tg_cpus(struct task_group *tg)
|
||||
{
|
||||
int nr = num_online_cpus();
|
||||
|
||||
if (cpusets_enabled()) {
|
||||
struct cgroup *cgrp = tg->css.cgroup;
|
||||
if (cgrp)
|
||||
nr = cpuset_num_cpus(cgrp);
|
||||
}
|
||||
|
||||
return nr;
|
||||
}
|
||||
|
||||
/*
|
||||
* Func: min(fraction(nr_cpus * tg->shares), nice -20)
|
||||
*
|
||||
* Scale tg->shares by the maximal number of CPUs; but clip the max shares at
|
||||
* nice -20, otherwise a single spinner on a 512 CPU machine would result in
|
||||
* 512*NICE_0_LOAD, which is also crazy.
|
||||
*/
|
||||
static long calc_max_shares(struct cfs_rq *cfs_rq)
|
||||
{
|
||||
struct task_group *tg = cfs_rq->tg;
|
||||
int nr = tg_cpus(tg);
|
||||
long tg_shares = READ_ONCE(tg->shares);
|
||||
long max_shares = scale_load(sched_prio_to_weight[0]);
|
||||
return __calc_smp_shares(cfs_rq, tg_shares * nr, max_shares);
|
||||
}
|
||||
|
||||
/*
|
||||
* Func: fraction(tg->shares)
|
||||
*
|
||||
* This infamously results in tiny shares when you have many CPUs.
|
||||
*/
|
||||
static long calc_smp_shares(struct cfs_rq *cfs_rq)
|
||||
{
|
||||
struct task_group *tg = cfs_rq->tg;
|
||||
long tg_shares = READ_ONCE(tg->shares);
|
||||
return __calc_smp_shares(cfs_rq, tg_shares, tg_shares);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -4857,6 +4896,9 @@ void __sched_cgroup_mode_update(int mode)
|
||||
default:
|
||||
func = &calc_smp_shares;
|
||||
break;
|
||||
case 2:
|
||||
func = &calc_max_shares;
|
||||
break;
|
||||
}
|
||||
static_call_update(calc_group_shares, func);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user