From: sashiko-bot@kernel.org
To: "Mathieu Desnoyers" <mathieu.desnoyers@efficios.com>
Cc: linux-rt-devel@lists.linux.dev, tj@kernel.org,
hannes@cmpxchg.org, mkoutny@suse.com,
linux-kernel@vger.kernel.org
Subject: Re: [PATCH v4 16/26] sched/deadline: Record the root domain holding a task's bandwidth
Date: Sat, 07 Nov 2026 02:35:06 +0000 [thread overview]
Message-ID: <sashiko-outbox-202514@kernel.org> (raw)
In-Reply-To: <20261107014451.1487825-17-mathieu.desnoyers@efficios.com>
Thank you for your contribution! Sashiko AI review found 2 potential
issues to consider:
- [Medium] sched/deadline: dl_task_undepart() loses departed state on PI
boost
- [Medium] sched/deadline: stale dl_bw_cpu when dl_bw is zero misroutes
bandwidth
--
commit 750b37778a0ba278821539fc3ca7bca85e0c4a0a
Author: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
sched/deadline: Record the root domain holding a task's bandwidth
This commit records a CPU from the root domain accounting for a deadline
task's bandwidth in sched_dl_entity::dl_bw_cpu so that subsequent moves,
parameter updates, rebuilds, and releases operate on the root domain that
took the bandwidth.
Link: https://lore.kernel.org/all/20261103-b4-sched-dl-bw-fixes-v1-1-dd761fe005b4@redhat.com/
> diff --git a/kernel/sched/deadline.c b/kernel/sched/deadline.c
> index 3f3de4b74a2dc..7a7101e6f7a03 100644
> --- a/kernel/sched/deadline.c
> +++ b/kernel/sched/deadline.c
[ ... ]
> @@ -3537,10 +3575,18 @@ static void set_cpus_allowed_dl(struct task_struct *p,
> bool dl_task_needs_bw_move(struct task_struct *p,
> const struct cpumask *new_mask)
> {
> - if (!dl_task(p))
> + unsigned int cpu;
> +
> + /*
> + * Nothing to move for a special entity, nor for a task boosted into
> + * the class with no bandwidth of its own: neither is counted, as
> + * dl_add_task_root_domain() leaves them out.
> + */
> + if (!dl_task(p) || dl_entity_is_special(&p->dl) || !p->dl.dl_bw)
> return false;
>
> - return !cpumask_intersects(task_rq(p)->rd->span, new_mask);
> + cpu = dl_task_root_domain_cpu(p);
> + return !cpumask_intersects(cpu_rq(cpu)->rd->span, new_mask);
> }
>
> /* Assumes rq->lock is held */
> @@ -3614,8 +3660,9 @@ void dl_add_task_root_domain(struct task_struct *p)
> raw_spin_lock_irqsave(&p->pi_lock, rf.flags);
> /*
> * Nothing to count for a special entity, nor for a task with no
> - * bandwidth, such as an exited one whose bandwidth went with its
> - * parameters, which the walk of the task list finds too.
> + * bandwidth: an exited one whose bandwidth went with its
> + * parameters, which the walk of the task list finds too, and a task
> + * boosted into the class with none of its own, which gets no record.
> */
> if (!dl_task(p) || dl_entity_is_special(&p->dl) || !p->dl.dl_bw) {
> raw_spin_unlock_irqrestore(&p->pi_lock, rf.flags);
> @@ -3639,10 +3686,12 @@ void dl_add_task_root_domain(struct task_struct *p)
> */
> if (p->dl.dl_bw_gen == dl_bw_gen)
> goto unlock;
> - if (p->dl.dl_departed_cpu >= 0) {
> - cpu = p->dl.dl_departed_cpu;
> - if (!cpu_active(cpu))
> + if (!list_empty(&p->dl.dl_departed_node)) {
> + int recorded = p->dl.dl_bw_cpu;
> +
> + if (recorded < 0 || !cpu_active(recorded))
> goto unlock;
> + cpu = recorded;
> } else if (READ_ONCE(p->__state) == TASK_DEAD) {
[ ... ]
> @@ -3942,13 +3992,10 @@ static void switched_to_dl(struct rq *rq, struct task_struct *p)
> /*
> * Boosted back into the class before its 0-lag time (a policy change
> * released that bandwidth in sched_dl_overflow()): the inactive
> - * timer will not remove it, but its leaving the class again. That
> - * forgets the CPU recorded: moved to another root domain meanwhile,
> - * the task then removes the bandwidth from that one, as it did
> - * before departures were recorded.
> + * timer will not remove it, but its leaving the class again, from
> + * the root domain of the CPU recorded, which it keeps.
> */
> - if (p->dl.dl_departed_cpu >= 0)
> - dl_task_undepart(p);
> + dl_task_undepart(p);
[Severity: Medium]
When a task that left SCHED_DEADLINE before its 0-lag time is boosted back
into dl_sched_class via priority inheritance, does calling
dl_task_undepart(p) lose the fact that its bandwidth is departing?
Unlinking p->dl.dl_departed_node while !task_has_dl_policy(p) makes
list_empty(&p->dl.dl_departed_node) true while dl_task(p) and p->dl.dl_bw
are both non-zero. If the task's affinity or cpuset changed to another
root domain after it left SCHED_DEADLINE, a root-domain rebuild in
dl_add_task_root_domain() will skip the !list_empty(&p->dl.dl_departed_node)
branch, charge the departing bandwidth to the new root domain that never
admitted it, and overwrite p->dl.dl_bw_cpu.
For the same reason, dl_task_needs_bw_move() checks !dl_task(p) rather
than !task_has_dl_policy(p), so migrating that PI-boosted task across
exclusive cpusets moves its departing bandwidth into the destination root
domain. If a prior rebuild had already dropped the departing bandwidth,
dl_add_task_root_domain() also puts it back because it does not check
dl_bw_gen == dl_bw_gen - 1 as dl_clear_root_domains() does. And because
rt_mutex_setprio() does not serialize against dl_rebuild_rd_accounting(),
a departed task that is PI-boosted when dl_clear_root_domains() runs and
deboosted before the task-list walk reaches it is missed by both, dropping
its active 0-lag bandwidth from the root domain.
[ ... ]
> @@ -4155,9 +4202,10 @@ int sched_dl_overflow(struct task_struct *p, int policy,
> u64 period = attr->sched_period ?: attr->sched_deadline;
> u64 runtime = attr->sched_runtime;
> u64 new_bw = dl_policy(policy) ? to_ratio(period, runtime) : 0;
> - int cpus, err = -1, cpu = task_cpu(p);
> - struct dl_bw *dl_b = dl_bw_of(cpu);
> + int cpus, err = -1, cpu;
> + struct dl_bw *dl_b;
> unsigned long cap;
> + bool counted;
>
> if (attr->sched_flags & SCHED_FLAG_SUGOV)
> return 0;
> @@ -4166,6 +4214,20 @@ int sched_dl_overflow(struct task_struct *p, int policy,
> if (new_bw == p->dl.dl_bw && task_has_dl_policy(p))
> return 0;
>
> + /*
> + * The bandwidth of a task of the class is counted in the root
> + * domain that took it, which the CPU the task is on does not tell:
> + * moved asleep, it has not run there yet. Change it where it is
> + * counted; a rebuild that dropped it, and a task that enters the
> + * class, have the root domain of that CPU take the new one. The
> + * caller holds cpuset_mutex, or the lock of dl_accounting_lock()
> + * without cpusets, so no rebuild runs meanwhile.
> + */
> + counted = task_has_dl_policy(p) && p->dl.dl_bw_cpu >= 0 &&
> + p->dl.dl_bw_gen == dl_bw_gen;
> + cpu = counted ? p->dl.dl_bw_cpu : task_cpu(p);
> + dl_b = dl_bw_of(cpu);
[ ... ]
> @@ -4209,11 +4273,20 @@ int sched_dl_overflow(struct task_struct *p, int policy,
> * run meanwhile: the caller holds cpuset_mutex, or the lock of
> * dl_accounting_lock() without cpusets.
> */
> - if (!err && dl_policy(policy) && !task_has_dl_policy(p)) {
> - lockdep_assert_cpuset_lock_held();
> - lockdep_assert_dl_accounting_held();
> - if (p->dl.dl_departed_cpu >= 0)
> - dl_task_release_bw(p, -1);
> + if (!err && dl_policy(policy)) {
> + if (!task_has_dl_policy(p)) {
> + lockdep_assert_cpuset_lock_held();
> + lockdep_assert_dl_accounting_held();
> + /*
> + * Whatever bandwidth the task still has counted
> + * somewhere, the new one replaces: on the departed
> + * list, or taken off it by a boost into the class
> + * (switched_to_dl()), the record says where.
> + */
> + if (p->dl.dl_bw_cpu >= 0)
> + dl_task_release_bw(p);
> + }
> + p->dl.dl_bw_cpu = cpu;
> + p->dl.dl_bw_gen = dl_bw_gen;
> }
[Severity: Medium]
Can recording p->dl.dl_bw_cpu when new_bw is 0 cause a later sched_setattr()
to charge the task's bandwidth to a stale root domain?
__checkparam_dl() permits parameters where (runtime << BW_SHIFT) < period
(for example, runtime = 1024 ns and period = 2 s), for which to_ratio()
returns new_bw == 0. sched_dl_overflow() (and dl_task_offline_migration())
still sets p->dl.dl_bw_cpu to a CPU in the current root domain RD0. If
the task is then moved to another exclusive cpuset partition RD1,
dl_task_needs_bw_move() returns false because !p->dl.dl_bw, leaving
p->dl.dl_bw_cpu pointing at RD0. When the task runs in RD1 and calls
sched_setattr() with new_bw > 0, sched_dl_overflow() sees counted == true
and validates and adds new_bw to RD0's dl_bw instead of RD1's, bypassing
admission control in RD1.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261107014451.1487825-1-mathieu.desnoyers@efficios.com?part=16
next prev parent reply other threads:[~2026-11-07 2:35 UTC|newest]
Thread overview: 36+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-07 1:44 [PATCH v4 00/26] sched/deadline: bandwidth accounting fixes and selftests Mathieu Desnoyers
2026-11-07 1:44 ` [PATCH v4 01/26] sched: Fix cpuset_mutex double unlock in __sched_setscheduler() Mathieu Desnoyers
2026-11-07 1:44 ` [PATCH v4 02/26] sched: Refuse SCHED_FLAG_KEEP_PARAMS that would change the policy or the class Mathieu Desnoyers
2026-11-07 1:44 ` [PATCH v4 03/26] sched/deadline: Do not admit bandwidth for parameters that are kept Mathieu Desnoyers
2026-11-07 1:44 ` [PATCH v4 04/26] sched: Let a task boosted by a deadline waiter change its policy Mathieu Desnoyers
2026-11-07 2:13 ` sashiko-bot
2026-11-07 1:44 ` [PATCH v4 05/26] sched/deadline: Do not divide by zero active CPUs of a root domain Mathieu Desnoyers
2026-11-07 1:44 ` [PATCH v4 06/26] sched: Take cpuset_mutex for a deadline policy change only with pi Mathieu Desnoyers
2026-11-07 1:44 ` [PATCH v4 07/26] sched/deadline: Do not lose a throttled task switched out and back Mathieu Desnoyers
2026-11-07 1:44 ` [PATCH v4 08/26] sched/deadline: Take a deadline boost from the waiter's rt_mutex node Mathieu Desnoyers
2026-11-07 2:25 ` sashiko-bot
2026-11-07 1:44 ` [PATCH v4 09/26] sched/deadline: Reset the deadline and the runtime with the parameters Mathieu Desnoyers
2026-11-07 2:18 ` sashiko-bot
2026-11-07 1:44 ` [PATCH v4 10/26] sched/deadline: Release the bandwidth of a task that leaves the class asleep Mathieu Desnoyers
2026-11-07 2:23 ` sashiko-bot
2026-11-07 1:44 ` [PATCH v4 11/26] sched/deadline: Reject SCHED_DEADLINE on exited tasks Mathieu Desnoyers
2026-11-07 1:44 ` [PATCH v4 12/26] sched/deadline: Keep departing bandwidth across a root domain rebuild Mathieu Desnoyers
2026-11-07 1:44 ` [PATCH v4 13/26] sched/deadline: Do not take a waking task's bandwidth off a runqueue it left Mathieu Desnoyers
2026-11-07 1:44 ` [PATCH v4 14/26] sched/deadline: Rebuild the root domains' accounting from the task list Mathieu Desnoyers
2026-11-07 2:22 ` sashiko-bot
2026-11-07 1:44 ` [PATCH v4 15/26] sched/deadline: Leave a dying task out of a rebuild of the accounting Mathieu Desnoyers
2026-11-07 2:10 ` sashiko-bot
2026-11-07 1:44 ` [PATCH v4 16/26] sched/deadline: Record the root domain holding a task's bandwidth Mathieu Desnoyers
2026-11-07 2:35 ` sashiko-bot [this message]
2026-11-07 1:44 ` [PATCH v4 17/26] sched/deadline: Move a task's bandwidth only where room was made Mathieu Desnoyers
2026-11-07 1:44 ` [PATCH v4 18/26] cgroup/cpuset: Wait for an attach before rebuilding the sched domains Mathieu Desnoyers
2026-11-07 1:44 ` [PATCH v4 19/26] cgroup/cpuset: Give back the bandwidth an attach did not move Mathieu Desnoyers
2026-11-07 1:44 ` [PATCH v4 20/26] sched/deadline: Credit a rebuild to the CPU recorded with the task's bandwidth Mathieu Desnoyers
2026-11-07 2:48 ` sashiko-bot
2026-11-07 1:44 ` [PATCH v4 21/26] sched/deadline: Cancel the replenishment timer with the parameters Mathieu Desnoyers
2026-11-07 1:44 ` [PATCH v4 22/26] sched/deadline: Count the bandwidth a boosted task is given Mathieu Desnoyers
2026-11-07 1:44 ` [PATCH v4 23/26] sched/debug: Show the deadline bandwidth of each runqueue Mathieu Desnoyers
2026-11-07 1:44 ` [PATCH v4 24/26] selftests/sched: Add a test of SCHED_DEADLINE policy round trips Mathieu Desnoyers
2026-11-07 1:44 ` [PATCH v4 25/26] selftests/sched: Test SCHED_DEADLINE bandwidth across a root domain rebuild Mathieu Desnoyers
2026-11-07 1:44 ` [PATCH v4 26/26] selftests/sched: Add a test of SCHED_DEADLINE bandwidth accounting Mathieu Desnoyers
2026-11-07 2:25 ` sashiko-bot
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=sashiko-outbox-202514@kernel.org \
--to=sashiko-bot@kernel.org \
--cc=hannes@cmpxchg.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-rt-devel@lists.linux.dev \
--cc=mathieu.desnoyers@efficios.com \
--cc=mkoutny@suse.com \
--cc=sashiko-reviews@lists.linux.dev \
--cc=tj@kernel.org \
/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®