From: Yuri Andriaccio <yurand2000@gmail.com>
To: "Ingo Molnar" <mingo@redhat.com>,
"Peter Zijlstra" <peterz@infradead.org>,
"Juri Lelli" <juri.lelli@redhat.com>,
"Vincent Guittot" <vincent.guittot@linaro.org>,
"Dietmar Eggemann" <dietmar.eggemann@arm.com>,
"Steven Rostedt" <rostedt@goodmis.org>,
"Ben Segall" <bsegall@google.com>, "Mel Gorman" <mgorman@suse.de>,
"Valentin Schneider" <vschneid@redhat.com>,
"K Prateek Nayak" <kprateek.nayak@amd.com>,
"Tejun Heo" <tj@kernel.org>,
"Johannes Weiner" <hannes@cmpxchg.org>,
"Michal Koutný" <mkoutny@suse.com>
Cc: cgroups@vger.kernel.org, linux-kernel@vger.kernel.org,
Luca Abeni <luca.abeni@santannapisa.it>,
Yuri Andriaccio <yuri.andriaccio@santannapisa.it>
Subject: [RFC PATCH v7 27/29] sched/rt: Integrate cpuset and cpu controllers
Date: Mon, 9 Nov 2026 17:36:13 +0100 [thread overview]
Message-ID: <20261109163616.195264-28-yurand2000@gmail.com> (raw)
In-Reply-To: <20261109163616.195264-1-yurand2000@gmail.com>
Integrate the cpuset and cpu controllers:
- cpuset cannot be changed if the HCBS cgroup is active (non-max)
or any of the group's children is active (cpuset_can_change function).
- introduce cpuset_effective_cpus to read the cpuset's effective mask.
- update struct dl_bandwidth to store the effective mask of the cgroup.
- remove the dl_internal_runtime in dl_bandwidth as this value is now
per-cpu/server and equal to dl_se->dl_runtime.
- update schedulability checks to account for cpumasks.
- update cpu.rt.effective.local interface to show per-cpu internal
values.
Signed-off-by: Yuri Andriaccio <yurand2000@gmail.com>
---
include/linux/cpuset.h | 2 +
kernel/cgroup/cpuset.c | 31 +++++
kernel/sched/core.c | 22 +++-
kernel/sched/deadline.c | 13 ++-
kernel/sched/rt.c | 249 ++++++++++++++++++++++++++--------------
kernel/sched/sched.h | 9 +-
6 files changed, 237 insertions(+), 89 deletions(-)
diff --git a/include/linux/cpuset.h b/include/linux/cpuset.h
index 914983387e95..6487bdbbdeb3 100644
--- a/include/linux/cpuset.h
+++ b/include/linux/cpuset.h
@@ -81,6 +81,8 @@ extern void cpuset_cpus_allowed_locked(struct task_struct *p, struct cpumask *ma
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 void cpuset_effective_cpus(struct cgroup *cgroup, struct cpumask *mask);
+extern int cpuset_can_change(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);
diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index 3219f927c35e..c7fd878154c6 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -2572,6 +2572,10 @@ static int update_cpumask(struct cpuset *cs, struct cpuset *trialcs,
if (cpumask_equal(cs->cpus_allowed, trialcs->cpus_allowed))
return 0;
+ /* Disallow change if subtree has any allocated rt-cgroup */
+ if (!cpuset_can_change(cs->css.cgroup))
+ return -EINVAL;
+
compute_trialcs_excpus(trialcs, cs);
trialcs->prs_err = PERR_NONE;
@@ -2632,6 +2636,10 @@ static int update_exclusive_cpumask(struct cpuset *cs, struct cpuset *trialcs,
if (cpumask_equal(cs->exclusive_cpus, trialcs->exclusive_cpus))
return 0;
+ /* Disallow change if subtree has any allocated rt-cgroup */
+ if (!cpuset_can_change(cs->css.cgroup))
+ return -EINVAL;
+
/*
* Reject the change if there is exclusive CPUs conflict with
* the siblings.
@@ -3585,6 +3593,10 @@ static ssize_t cpuset_partition_write(struct kernfs_open_file *of, char *buf,
else
return -EINVAL;
+ /* Disallow change if subtree has any allocated rt-cgroup */
+ if (!cpuset_can_change(cs->css.cgroup))
+ return -EINVAL;
+
mutex_lock(&cpuset_top_mutex);
cpus_read_lock();
wait_attach_done_lock();
@@ -4354,6 +4366,25 @@ int cpuset_num_cpus(struct cgroup *cgrp)
return nr;
}
+void cpuset_effective_cpus(struct cgroup *cgrp, struct cpumask *mask)
+{
+ struct cpuset *cs;
+
+ if (!is_in_v2_mode()) {
+ cpumask_copy(mask, cpu_online_mask);
+ return;
+ }
+
+ guard(rcu)();
+ cs = css_cs(cgroup_e_css(cgrp, &cpuset_cgrp_subsys));
+ if (!cs) {
+ cpumask_copy(mask, cpu_online_mask);
+ return;
+ }
+
+ cpumask_copy(mask, cs->effective_cpus);
+}
+
void __init cpuset_init_current_mems_allowed(void)
{
nodes_setall(current->mems_allowed);
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index e4e50202896d..59bd9599c509 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -8970,6 +8970,8 @@ void __init sched_init(void)
init_defrootdomain();
#ifdef CONFIG_RT_GROUP_SCHED
+ BUG_ON(!alloc_dl_bandwidth(&root_task_group.dl_bandwidth, GFP_NOWAIT));
+
init_dl_bandwidth(&root_task_group.dl_bandwidth,
global_rt_period(), 0, &root_task_group);
#endif /* CONFIG_RT_GROUP_SCHED */
@@ -10426,6 +10428,19 @@ static void __maybe_unused cpu_period_quota_print(struct seq_file *sf,
seq_printf(sf, " %ld\n", period);
}
+static void __maybe_unused cpu_period_quota_single_print(struct seq_file *sf, int cpu,
+ long period, long quota)
+{
+ seq_printf(sf, "%d ", cpu);
+
+ if (quota < 0)
+ seq_puts(sf, "runtime=max");
+ else
+ seq_printf(sf, "runtime=%ld", quota);
+
+ seq_printf(sf, " period=%ld\n", period);
+}
+
/* caller should put the current value in *@periodp before calling */
static int __maybe_unused cpu_period_quota_parse(char *buf, u64 *period_us_p,
u64 *quota_us_p)
@@ -10486,9 +10501,12 @@ static int cpu_rt_internal_show(struct seq_file *sf, void *v)
{
struct task_group *tg = css_tg(seq_css(sf));
u64 period_us, runtime_us;
+ int cpu;
- tg_rt_internal_bandwidth(tg, &period_us, &runtime_us);
- cpu_period_quota_print(sf, period_us, runtime_us);
+ for_each_possible_cpu(cpu) {
+ tg_rt_internal_bandwidth(tg, cpu, &period_us, &runtime_us);
+ cpu_period_quota_single_print(sf, cpu, period_us, runtime_us);
+ }
return 0;
}
diff --git a/kernel/sched/deadline.c b/kernel/sched/deadline.c
index 6220d793ae91..0282258db3e9 100644
--- a/kernel/sched/deadline.c
+++ b/kernel/sched/deadline.c
@@ -592,14 +592,25 @@ static inline int is_leftmost(struct sched_dl_entity *dl_se, struct dl_rq *dl_rq
static void init_dl_rq_bw_ratio(struct dl_rq *dl_rq);
+struct dl_bandwidth *alloc_dl_bandwidth(struct dl_bandwidth *dl_b, gfp_t gfp_flags) {
+ if (!alloc_cpumask_var(&dl_b->active_mask, gfp_flags))
+ return NULL;
+
+ return dl_b;
+}
+
+void free_dl_bandwidth(struct dl_bandwidth *dl_b) {
+ free_cpumask_var(dl_b->active_mask);
+}
+
void init_dl_bandwidth(struct dl_bandwidth *dl_b, u64 period, u64 runtime,
struct task_group *active_context)
{
raw_spin_lock_init(&dl_b->dl_runtime_lock);
dl_b->dl_period = period;
dl_b->dl_runtime = runtime;
- dl_b->dl_internal_runtime = 0;
dl_b->active_context = active_context;
+ cpumask_clear(dl_b->active_mask);
}
diff --git a/kernel/sched/rt.c b/kernel/sched/rt.c
index fffb6ec30e68..37e4b24183ab 100644
--- a/kernel/sched/rt.c
+++ b/kernel/sched/rt.c
@@ -172,6 +172,7 @@ void free_rt_sched_group(struct task_group *tg)
kfree(tg->rt_rq);
kfree(tg->dl_se);
+ free_dl_bandwidth(&tg->dl_bandwidth);
}
static inline void __rt_rq_free(struct rt_rq **rt_rq)
@@ -206,6 +207,7 @@ static int __alloc_rt_sched_group_data(struct task_group *tg) {
struct rt_rq **tg_rt_rq __free(rt_rq_free) = NULL;
struct sched_dl_entity **tg_dl_se __free(dl_se_free) = NULL;
struct sched_dl_entity *dl_se __free(kfree) = NULL;
+ struct dl_bandwidth *dl_b __free(dl_bandwidth_free) = NULL;
struct rq *s_rq __free(kfree) = NULL;
int i;
@@ -218,6 +220,10 @@ static int __alloc_rt_sched_group_data(struct task_group *tg) {
if (!tg_dl_se)
return 0;
+ dl_b = alloc_dl_bandwidth(&tg->dl_bandwidth, GFP_KERNEL);
+ if(!dl_b)
+ return 0;
+
for_each_possible_cpu(i) {
s_rq = kzalloc_node(sizeof(struct rq),
GFP_KERNEL, cpu_to_node(i));
@@ -235,6 +241,7 @@ static int __alloc_rt_sched_group_data(struct task_group *tg) {
tg->rt_rq = no_free_ptr(tg_rt_rq);
tg->dl_se = no_free_ptr(tg_dl_se);
+ retain_and_null_ptr(dl_b);
return 1;
}
@@ -1416,7 +1423,8 @@ static int find_lowest_rt_rq(struct task_struct *task)
* different capacities of the CPUs when searching for
* the lowest_mask.
*/
- if (dl_se->dl_throttled || !rt_task_fits_capacity(task, cpu))
+ if (!dl_se->dl_runtime || dl_se->dl_throttled ||
+ !rt_task_fits_capacity(task, cpu))
continue;
if (prio >= lowest_prio) {
@@ -1567,7 +1575,8 @@ static struct rt_rq *find_lock_lowest_rt_rq(struct task_struct *task, struct rt_
* check the task migration disable flag here too.
*/
if (unlikely(is_migration_disabled(task) ||
- (dl_group && lowest_dl_se->dl_throttled) ||
+ (dl_group && lowest_dl_se->dl_throttled &&
+ !lowest_dl_se->dl_runtime) ||
!cpumask_test_cpu(lowest_rq->cpu, &task->cpus_mask) ||
task != pick_next_pushable_task(rt_rq))) {
@@ -2418,114 +2427,132 @@ static int tg_subtree_has_rt_tasks(struct task_group *tg) {
dl_bandwidth_read(tg)->active_context);
}
+static inline u64 tg_cpu_period(const struct dl_bandwidth *dl_b, int cpu) {
+ return cpumask_test_cpu(cpu, dl_b->active_mask) ? dl_b->dl_period : 0;
+}
+
+static inline u64 tg_cpu_runtime(const struct dl_bandwidth *dl_b, int cpu) {
+ return cpumask_test_cpu(cpu, dl_b->active_mask) ? dl_b->dl_runtime : 0;
+}
+
struct tg_update_data {
struct task_group *tg;
- u64 rt_period;
- u64 rt_runtime;
+ struct dl_bandwidth dl_b;
};
struct tg_compute_children_bw_data {
struct tg_update_data update;
struct task_group *active_context;
- u64 period;
- u64 runtime_sum;
+ const struct cpumask *mask;
+ u64 *rt_runtime_sum;
};
static int __tg_compute_children_runtime(struct task_group *tg, void *data) {
struct tg_compute_children_bw_data *d = data;
const struct task_group *parent_active_context;
- const struct dl_bandwidth *dl_b_parent, *dl_b = dl_bandwidth_read(tg);
- u64 period, runtime;
+ const struct dl_bandwidth *reference_dl_b, *dl_b_parent, *dl_b;
+ u64 reference_period, period, runtime;
+ int cpu;
/* Skip the current task group from the sum. */
if (tg == d->active_context)
return 0;
- period = dl_b->dl_period;
- runtime = dl_b->dl_runtime;
+ reference_dl_b = dl_bandwidth_read(d->active_context);
+ dl_b = dl_bandwidth_read(tg);
if (tg == d->update.tg) {
- period = d->update.rt_period;
- runtime = d->update.rt_runtime;
+ dl_b = &d->update.dl_b;
}
- if (runtime == RUNTIME_INF || period == 0)
+
+ /* Skip the task group if runtime = `max` */
+ if (dl_b->dl_runtime == RUNTIME_INF)
return 0;
+ /* Skip the task group if it's parent active context is not current */
dl_b_parent = dl_bandwidth_read(tg->parent);
parent_active_context = dl_b_parent->active_context;
if (tg->parent == d->update.tg) {
if (dl_b_parent->dl_runtime != RUNTIME_INF &&
- d->update.rt_runtime == RUNTIME_INF)
+ d->update.dl_b.dl_runtime == RUNTIME_INF)
parent_active_context = dl_bandwidth_read(tg->parent->parent)->active_context;
- else if (d->update.rt_runtime != RUNTIME_INF)
+ else if (d->update.dl_b.dl_runtime != RUNTIME_INF)
parent_active_context = tg->parent;
}
if (parent_active_context != d->active_context)
return 0;
- d->runtime_sum += mul_u64_u64_div_u64(runtime, d->period, period);
+ for_each_cpu(cpu, d->mask) {
+ reference_period = tg_cpu_period(reference_dl_b, cpu);
+ period = tg_cpu_period(dl_b, cpu);
+ runtime = tg_cpu_runtime(dl_b, cpu);
+
+ if (period)
+ d->rt_runtime_sum[cpu] += mul_u64_u64_div_u64(runtime, reference_period, period);
+ }
+
return 0;
}
-static u64
-tg_compute_children_runtime(struct task_group *tg,
- u64 normalization_period,
- struct tg_update_data *data)
+static void tg_compute_children_runtime(struct task_group *tg,
+ struct tg_update_data *data,
+ const struct cpumask *mask,
+ u64 *sum_runtime)
{
+ int cpu;
struct tg_compute_children_bw_data sum_data = {
.active_context = tg,
- .period = normalization_period,
- .runtime_sum = 0,
+ .rt_runtime_sum = sum_runtime,
+ .mask = mask,
.update = *data,
};
+ for_each_cpu(cpu, mask)
+ sum_runtime[cpu] = 0;
+
lockdep_assert(rcu_read_lock_held());
walk_tg_tree_from(tg, __tg_compute_children_runtime, tg_nop, &sum_data);
- return sum_data.runtime_sum;
}
struct rt_schedulable_data {
struct tg_update_data update;
- u64 rt_runtime_remainder;
+ u64 *children_runtime;
+ u64 *rt_runtime_remainder;
};
static int __tg_rt_schedulable(struct task_group *tg, void *data)
{
struct rt_schedulable_data *d = data;
const struct dl_bandwidth *dl_b;
- u64 total, sum = 0;
- u64 period, runtime;
+ u64 total, runtime;
+ int cpu;
dl_b = dl_bandwidth_read(tg);
- period = dl_b->dl_period;
- runtime = dl_b->dl_runtime;
-
if (tg == d->update.tg) {
- period = d->update.rt_period;
- runtime = d->update.rt_runtime;
+ dl_b = &d->update.dl_b;
}
/*
* "max" groups are always schedulable, as they defer their access
* control to their first non-max parent.
*/
- if (runtime == RUNTIME_INF)
+ if (dl_b->dl_runtime == RUNTIME_INF)
return 0;
/*
* Cannot have more runtime than the period.
*/
- if (runtime > period)
+ if (dl_b->dl_runtime > dl_b->dl_period)
return -EINVAL;
/*
* Ensure we don't starve existing RT tasks if runtime turns zero.
*/
- if (dl_bandwidth_enabled() && !runtime && tg != &root_task_group &&
+ if (dl_bandwidth_enabled() && !dl_b->dl_runtime && tg != &root_task_group &&
tg_subtree_has_rt_tasks(tg))
return -EBUSY;
- total = to_ratio(period, runtime);
+ total = to_ratio(dl_b->dl_period, dl_b->dl_runtime);
/*
* Nobody can have more than the global setting allows.
@@ -2542,16 +2569,18 @@ static int __tg_rt_schedulable(struct task_group *tg, void *data)
* The sum of our children's runtime, plus our own bw, should not
* exceed our own max.
*/
- total = runtime;
- sum = tg_compute_children_runtime(tg, period, &d->update);
- if (sum > total)
- return -EINVAL;
+ tg_compute_children_runtime(tg, &d->update, cpu_possible_mask, d->children_runtime);
+ for_each_possible_cpu(cpu) {
+ runtime = tg_cpu_runtime(dl_b, cpu);
+ if (d->children_runtime[cpu] > runtime)
+ return -EINVAL;
- /*
- * Compute remaining runtime
- */
- if (tg == d->update.tg)
- d->rt_runtime_remainder = total - sum;
+ /*
+ * Compute remaining runtime
+ */
+ if (tg == d->update.tg)
+ d->rt_runtime_remainder[cpu] = runtime - d->children_runtime[cpu];
+ }
return 0;
}
@@ -2559,15 +2588,25 @@ static int __tg_rt_schedulable(struct task_group *tg, void *data)
static int tg_rt_schedulable(struct tg_update_data *data, u64 *remainder_runtime)
{
int err;
+ u64 *children_runtime __free(kfree) = NULL;
+ u64 *rt_runtime_remainder __free(kfree) = NULL;
struct rt_schedulable_data d = {
- .update = (struct tg_update_data) {
- .tg = data->tg,
- .rt_period = data->rt_period,
- .rt_runtime = data->rt_runtime,
- },
- .rt_runtime_remainder = 0,
+ .update = *data,
+ .children_runtime = NULL,
+ .rt_runtime_remainder = NULL,
};
+ children_runtime = kcalloc(nr_cpu_ids, sizeof(u64), GFP_NOWAIT);
+ if (!children_runtime)
+ return -ENOMEM;
+
+ rt_runtime_remainder = kcalloc(nr_cpu_ids, sizeof(u64), GFP_NOWAIT);
+ if (!rt_runtime_remainder)
+ return -ENOMEM;
+
+ d.children_runtime = children_runtime;
+ d.rt_runtime_remainder = rt_runtime_remainder;
+
/*
* Walk the cgroup tree and check schedulability constraints.
*/
@@ -2576,7 +2615,7 @@ static int tg_rt_schedulable(struct tg_update_data *data, u64 *remainder_runtime
if (err)
return err;
- *remainder_runtime = d.rt_runtime_remainder;
+ memcpy(remainder_runtime, rt_runtime_remainder, array_size(nr_cpu_ids, sizeof(u64)));
return 0;
}
@@ -2626,16 +2665,25 @@ int tg_rt_bandwidth(struct task_group *tg,
return 0;
}
-int tg_rt_internal_bandwidth(struct task_group *tg,
+int tg_rt_internal_bandwidth(struct task_group *tg, int cpu,
u64 *rt_period_us, u64 *rt_runtime_us)
{
const struct dl_bandwidth *dl_b;
+ cpumask_var_t mask __free(free_cpumask_var) = CPUMASK_VAR_NULL;
+
+ if (!alloc_cpumask_var(&mask, GFP_NOWAIT))
+ return 0;
+
+ cpuset_effective_cpus(tg->css.cgroup, mask);
guard(raw_spinlock_irq)(dl_bw_lock_of_tg(tg));
dl_b = dl_bandwidth_read(tg);
- *rt_runtime_us = dl_b->dl_internal_runtime;
- *rt_runtime_us = DIV64_U64_ROUND_CLOSEST(*rt_runtime_us, NSEC_PER_USEC);
+ *rt_runtime_us = -1;
+ if (dl_b->dl_runtime != RUNTIME_INF || !cpumask_test_cpu(cpu, mask)) {
+ *rt_runtime_us = tg->dl_se[cpu]->dl_runtime;
+ *rt_runtime_us = DIV64_U64_ROUND_CLOSEST(*rt_runtime_us, NSEC_PER_USEC);
+ }
*rt_period_us = div64_u64(dl_b->dl_period, NSEC_PER_USEC);
@@ -2646,11 +2694,13 @@ int tg_set_rt_bandwidth(struct task_group *tg,
u64 rt_period_us, u64 rt_runtime_us)
{
struct tg_update_data update;
+ struct dl_bandwidth *__update_dl_b __free(dl_bandwidth_free) = NULL;
struct task_group *parent_ctx;
- struct dl_bandwidth *dl_b;
+ struct dl_bandwidth *dl_b_write;
+ const struct dl_bandwidth *dl_b;
u64 rt_period, rt_runtime, old_rt_runtime;
- u64 rt_actual_runtime = 0;
- u64 runtime, children_runtime;
+ u64 *rt_actual_runtime __free(kfree) = NULL;
+ u64 period, runtime;
struct sched_attr attr;
int err, i;
@@ -2692,14 +2742,9 @@ int tg_set_rt_bandwidth(struct task_group *tg,
if (rt_runtime != RUNTIME_INF && !__checkparam_dl(&attr, true))
return -EINVAL;
- update = (struct tg_update_data) {
- .tg = tg,
- .rt_period = rt_period,
- .rt_runtime = rt_runtime,
- };
-
guard(mutex)(&rt_constraints_mutex);
- old_rt_runtime = dl_bandwidth_read(tg)->dl_runtime;
+ dl_b = dl_bandwidth_read(tg);
+ old_rt_runtime = dl_b->dl_runtime;
/*
* Disallow changing from/to "max" and a HCBS reservation if the group
@@ -2713,15 +2758,28 @@ int tg_set_rt_bandwidth(struct task_group *tg,
tg_subtree_has_rt_tasks(tg))
return -EINVAL;
- err = tg_rt_schedulable(&update, &rt_actual_runtime);
+ rt_actual_runtime = kcalloc(nr_cpu_ids, sizeof(u64), GFP_NOWAIT);
+ if (!rt_actual_runtime)
+ return -ENOMEM;
+
+ __update_dl_b = alloc_dl_bandwidth(&update.dl_b, GFP_NOWAIT);
+ if(!__update_dl_b)
+ return -ENOMEM;
+
+ init_dl_bandwidth(&update.dl_b, rt_period, rt_runtime, dl_b->active_context);
+ update.tg = tg;
+
+ cpuset_effective_cpus(tg->css.cgroup, update.dl_b.active_mask);
+
+ err = tg_rt_schedulable(&update, rt_actual_runtime);
if (err)
return err;
scoped_guard(raw_spinlock_irq, dl_bw_lock_of_tg(tg)) {
- dl_b = dl_bandwidth_write(tg);
- dl_b->dl_period = rt_period;
- dl_b->dl_runtime = rt_runtime;
- dl_b->dl_internal_runtime = rt_actual_runtime;
+ dl_b_write = dl_bandwidth_write(tg);
+ dl_b_write->dl_period = rt_period;
+ dl_b_write->dl_runtime = rt_runtime;
+ cpumask_copy(dl_b_write->active_mask, update.dl_b.active_mask);
}
if (tg == &root_task_group)
@@ -2737,15 +2795,13 @@ int tg_set_rt_bandwidth(struct task_group *tg,
(rt_runtime != RUNTIME_INF && old_rt_runtime == RUNTIME_INF))
{
if (rt_runtime == RUNTIME_INF)
- tg_update_active_context(tg, dl_b->active_context, parent_ctx);
+ tg_update_active_context(tg, update.dl_b.active_context, parent_ctx);
else
- tg_update_active_context(tg, dl_b->active_context, tg);
-
+ tg_update_active_context(tg, update.dl_b.active_context, tg);
}
- WARN_ON(rt_runtime == RUNTIME_INF && rt_actual_runtime != 0);
for_each_possible_cpu(i) {
- dl_init_tg(tg->dl_se[i], rt_actual_runtime, rt_period);
+ dl_init_tg(tg->dl_se[i], rt_actual_runtime[i], tg_cpu_period(dl_b, i));
}
/*
@@ -2754,24 +2810,49 @@ int tg_set_rt_bandwidth(struct task_group *tg,
if (parent_ctx == &root_task_group)
return 0;
- scoped_guard(raw_spinlock_irq, dl_bw_lock_of_tg(parent_ctx)) {
- dl_b = dl_bandwidth_write(parent_ctx);
+ dl_b = dl_bandwidth_read(parent_ctx);
- runtime = dl_b->dl_runtime;
- children_runtime = tg_compute_children_runtime(parent_ctx, dl_b->dl_period, &update);
+ tg_compute_children_runtime(parent_ctx, &update, update.dl_b.active_mask, rt_actual_runtime);
+ for_each_cpu(i, update.dl_b.active_mask) {
+ period = tg_cpu_period(dl_b, i);
+ runtime = tg_cpu_runtime(dl_b, i) - rt_actual_runtime[i];
- rt_period = dl_b->dl_period;
- rt_actual_runtime = runtime - children_runtime;
- dl_b->dl_internal_runtime = rt_actual_runtime;
+ dl_init_tg(parent_ctx->dl_se[i], runtime, period);
}
- for_each_possible_cpu(i) {
- dl_init_tg(parent_ctx->dl_se[i], rt_actual_runtime, rt_period);
- }
+ return 0;
+}
+
+static int __tg_subtree_is_max(struct task_group *tg, void *data) {
+ const struct dl_bandwidth *dl_b;
+
+ guard(raw_spinlock_irq)(dl_bw_lock_of_tg(tg));
+ dl_b = dl_bandwidth_read(tg);
+ if (dl_b->dl_runtime != RUNTIME_INF)
+ return 1;
return 0;
}
+int cpuset_can_change(struct cgroup *cgroup) {
+ struct task_group *tg;
+ int err;
+
+ if (!rt_group_sched_enabled())
+ return 1;
+
+ guard(rcu)();
+ tg = css_tg(cgroup_e_css(cgroup, &cpu_cgrp_subsys));
+ if (!tg)
+ return 0;
+
+ err = walk_tg_tree_from(tg, __tg_subtree_is_max, tg_nop, NULL);
+ if (err)
+ return 0;
+
+ return 1;
+}
+
int sched_rt_can_attach(struct task_group *tg)
{
struct task_group *ctx;
diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
index 323befba5609..9410d1909729 100644
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -316,8 +316,8 @@ struct rt_prio_array {
struct dl_bandwidth {
raw_spinlock_t dl_runtime_lock;
u64 dl_runtime;
- u64 dl_internal_runtime;
u64 dl_period;
+ cpumask_var_t active_mask;
struct task_group *active_context;
};
@@ -586,7 +586,7 @@ extern bool cfs_task_bw_constrained(struct task_struct *p);
extern int tg_rt_bandwidth(struct task_group *tg,
u64 *rt_period_us, u64 *rt_runtime_us);
-extern int tg_rt_internal_bandwidth(struct task_group *tg,
+extern int tg_rt_internal_bandwidth(struct task_group *tg, int cpu,
u64 *rt_period_us, u64 *rt_runtime_us);
extern int tg_set_rt_bandwidth(struct task_group *tg,
u64 rt_period_us, u64 rt_runtime_us);
@@ -3054,6 +3054,11 @@ extern void resched_curr(struct rq *rq);
extern void resched_curr_lazy(struct rq *rq);
extern void resched_cpu(int cpu);
+extern struct dl_bandwidth *alloc_dl_bandwidth(struct dl_bandwidth *dl_b, gfp_t gfp_flags);
+extern void free_dl_bandwidth(struct dl_bandwidth *dl_b);
+
+DEFINE_FREE(dl_bandwidth_free, struct dl_bandwidth *, if (_T) free_dl_bandwidth(_T))
+
extern void init_dl_bandwidth(struct dl_bandwidth *dl_b, u64 period, u64 runtime,
struct task_group *active_context);
extern void init_dl_entity(struct sched_dl_entity *dl_se);
--
2.56.0
next prev parent reply other threads:[~2026-11-09 16:37 UTC|newest]
Thread overview: 30+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-09 16:35 [RFC PATCH v7 00/29] Hierarchical Constant Bandwidth Server Yuri Andriaccio
2026-11-09 16:35 ` [RFC PATCH v7 01/29] sched/deadline: Introduce dl_freq_invariant flag for fair/ext servers Yuri Andriaccio
2026-11-09 16:35 ` [RFC PATCH v7 02/29] sched/deadline: Do not access dl_se->rq directly Yuri Andriaccio
2026-11-09 16:35 ` [RFC PATCH v7 03/29] sched/deadline: Distinguish between dl_rq and my_q Yuri Andriaccio
2026-11-09 16:35 ` [RFC PATCH v7 04/29] sched/rt: Pass an rt_rq instead of an rq where needed Yuri Andriaccio
2026-11-09 16:35 ` [RFC PATCH v7 05/29] sched/rt: Move functions from rt.c to sched.h Yuri Andriaccio
2026-11-09 16:35 ` [RFC PATCH v7 06/29] sched/rt: Disable RT_GROUP_SCHED Yuri Andriaccio
2026-11-09 16:35 ` [RFC PATCH v7 07/29] sched/rt: Remove unnecessary runqueue pointer in struct rt_rq Yuri Andriaccio
2026-11-09 16:35 ` [RFC PATCH v7 08/29] sched/rt: Introduce HCBS specific structs in task_group Yuri Andriaccio
2026-11-09 16:35 ` [RFC PATCH v7 09/29] sched/core: Initialize HCBS specific structures Yuri Andriaccio
2026-11-09 16:35 ` [RFC PATCH v7 10/29] sched/deadline: Add dl_init_tg Yuri Andriaccio
2026-11-09 16:35 ` [RFC PATCH v7 11/29] sched/rt: Add {alloc/unregister/free}_rt_sched_group Yuri Andriaccio
2026-11-09 16:35 ` [RFC PATCH v7 12/29] sched/deadline: Account rt-cgroups bandwidth in deadline tasks schedulability tests Yuri Andriaccio
2026-11-09 16:35 ` [RFC PATCH v7 13/29] sched/rt: Implement dl-server operations for rt-cgroups Yuri Andriaccio
2026-11-09 16:36 ` [RFC PATCH v7 14/29] sched/rt: Update task event callbacks for HCBS scheduling Yuri Andriaccio
2026-11-09 16:36 ` [RFC PATCH v7 15/29] sched/rt: Remove support for cgroups-v1 Yuri Andriaccio
2026-11-09 16:36 ` [RFC PATCH v7 16/29] sched/rt: Update rt-cgroup schedulability checks Yuri Andriaccio
2026-11-09 16:36 ` [RFC PATCH v7 17/29] sched/rt: Update task's RT runqueue when switching scheduling class Yuri Andriaccio
2026-11-09 16:36 ` [RFC PATCH v7 18/29] sched/rt: Remove old RT_GROUP_SCHED data structures Yuri Andriaccio
2026-11-09 16:36 ` [RFC PATCH v7 19/29] sched/rt: Add HCBS migration code to related functions Yuri Andriaccio
2026-11-09 16:36 ` [RFC PATCH v7 20/29] sched/rt: Hook HCBS migration functions Yuri Andriaccio
2026-11-09 16:36 ` [RFC PATCH v7 21/29] sched/core: Execute enqueued balance callbacks when changing allowed CPUs Yuri Andriaccio
2026-11-09 16:36 ` [RFC PATCH v7 22/29] sched/rt: Try pull task on empty server pick Yuri Andriaccio
2026-11-09 16:36 ` [RFC PATCH v7 23/29] sched/core: Execute enqueued balance callbacks after migrate_disable_switch Yuri Andriaccio
2026-11-09 16:36 ` [RFC PATCH v7 24/29] sched/syscalls: Allow unpriviledged tasks to set RT priority inside cgroups Yuri Andriaccio
2026-11-09 16:36 ` [RFC PATCH v7 25/29] sched/rt: Enable HRTICK_DL for HCBS servers Yuri Andriaccio
2026-11-09 16:36 ` [RFC PATCH v7 26/29] Documentation: Update documentation for real-time cgroups Yuri Andriaccio
2026-11-09 16:36 ` Yuri Andriaccio [this message]
2026-11-09 16:36 ` [RFC PATCH v7 28/29] sched/rt: Prevent CPU offline operation on active rt-cgroups Yuri Andriaccio
2026-11-09 16:36 ` [RFC PATCH v7 29/29] sched/rt: Introduce multi-cpu support Yuri Andriaccio
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261109163616.195264-28-yurand2000@gmail.com \
--to=yurand2000@gmail.com \
--cc=bsegall@google.com \
--cc=cgroups@vger.kernel.org \
--cc=dietmar.eggemann@arm.com \
--cc=hannes@cmpxchg.org \
--cc=juri.lelli@redhat.com \
--cc=kprateek.nayak@amd.com \
--cc=linux-kernel@vger.kernel.org \
--cc=luca.abeni@santannapisa.it \
--cc=mgorman@suse.de \
--cc=mingo@redhat.com \
--cc=mkoutny@suse.com \
--cc=peterz@infradead.org \
--cc=rostedt@goodmis.org \
--cc=tj@kernel.org \
--cc=vincent.guittot@linaro.org \
--cc=vschneid@redhat.com \
--cc=yuri.andriaccio@santannapisa.it \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®