From: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
To: juri.lelli@redhat.com, peterz@infradead.org, mingo@redhat.com,
vincent.guittot@linaro.org
Cc: linux-kernel@vger.kernel.org,
Mathieu Desnoyers <mathieu.desnoyers@efficios.com>,
tglx@kernel.org, will@kernel.org, boqun@kernel.org,
dietmar.eggemann@arm.com, rostedt@goodmis.org,
bsegall@google.com, mgorman@suse.de, vschneid@redhat.com,
kprateek.nayak@amd.com, luca.abeni@santannapisa.it,
mathieu.poirier@linaro.org, longman@redhat.com,
ridong.chen@linux.dev, tj@kernel.org, hannes@cmpxchg.org,
mkoutny@suse.com, shuah@kernel.org, bigeasy@linutronix.de,
clrkwllms@kernel.org, parri.andrea@gmail.com,
m.szyprowski@samsung.com, kayracizmeci@gmail.com,
weihengqiuu@gmail.com, cgroups@vger.kernel.org,
linux-kselftest@vger.kernel.org, linux-rt-devel@lists.linux.dev,
stable@vger.kernel.org
Subject: [PATCH v4 19/26] cgroup/cpuset: Give back the bandwidth an attach did not move
Date: Fri, 6 Nov 2026 20:44:44 -0500 [thread overview]
Message-ID: <20261107014451.1487825-20-mathieu.desnoyers@efficios.com> (raw)
In-Reply-To: <20261107014451.1487825-1-mathieu.desnoyers@efficios.com>
cpuset_can_attach() counts the bandwidth of every migrating deadline
task in sum_migrate_dl_bw, and cpuset_reserve_dl_bw() sets that sum
aside in the destination's root domain. The attach that follows moves
the bandwidth of each task that is still a deadline task, and nothing
answers for the reservation of one that is not: what was set aside for a
task that left SCHED_DEADLINE, or died, since it was counted stays in
the destination's total_bw, which admits that much less until the next
rebuild of the accounting. A task that flips between SCHED_DEADLINE and
SCHED_OTHER every millisecond while it is moved in and out of a one-CPU
partition root 200 times leaves total_bw of the partition at 367000,
681572 or 2^64 - 576716, where it was 52428, its fair server, in 4 VMs
of 10 of v7.4-rc1 with the patches of this series before this one.
Keep the tally of what the attach moves, and give the rest back once it
is through. The tally takes what set_cpus_allowed_dl() moved, written
into the affinity context under the runqueue lock: the root domain that
cpuset_attach_task() reads without that lock can change under it, CPU
hotplug moving the task in dl_task_offline_migration(), and the tally
would count a move that did not happen. With this change, total_bw of
the partition is 52428 after those 200 moves, in 6 VMs of 6.
Found by the randomized case of the selftest added earlier in this
series, which caught a fair server it could no longer lower in a
partition root with no room left.
Fixes: 2ef269ef1ac0 ("cgroup/cpuset: Free DL BW in case can_attach() fails")
Cc: stable@vger.kernel.org
Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
---
include/linux/sched.h | 3 ++-
kernel/cgroup/cpuset-internal.h | 2 ++
kernel/cgroup/cpuset.c | 39 ++++++++++++++++++++++++++-------
kernel/sched/core.c | 11 +++++++---
kernel/sched/deadline.c | 5 ++++-
kernel/sched/sched.h | 2 ++
6 files changed, 49 insertions(+), 13 deletions(-)
diff --git a/include/linux/sched.h b/include/linux/sched.h
index b0c36891415d..bd611dbb57b0 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1980,7 +1980,8 @@ extern void set_cpus_allowed_force(struct task_struct *p, const struct cpumask *
*/
extern int set_cpus_allowed_ptr(struct task_struct *p, const struct cpumask *new_mask);
extern int set_cpus_allowed_bw_reserved(struct task_struct *p,
- const struct cpumask *new_mask);
+ const struct cpumask *new_mask,
+ u64 *moved);
extern int dup_user_cpus_ptr(struct task_struct *dst, struct task_struct *src, int node);
extern void release_user_cpus_ptr(struct task_struct *p);
extern int dl_task_check_affinity(struct task_struct *p, const struct cpumask *mask);
diff --git a/kernel/cgroup/cpuset-internal.h b/kernel/cgroup/cpuset-internal.h
index 153067b164c1..6f3d43d8dbad 100644
--- a/kernel/cgroup/cpuset-internal.h
+++ b/kernel/cgroup/cpuset-internal.h
@@ -170,6 +170,8 @@ struct cpuset {
int nr_migrate_dl_tasks;
/* DL bandwidth that needs destination reservation for this attach. */
u64 sum_migrate_dl_bw;
+ /* Of that bandwidth, what the attach moved: the rest is given back. */
+ u64 sum_moved_dl_bw;
/*
* CPU used for temporary DL bandwidth allocation during attach;
* -1 if no DL bandwidth was allocated in the current attach.
diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index 2ee58d46da0e..f93f5547714a 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -3127,10 +3127,24 @@ static void clear_attach_data(struct llist_head *head, bool cancel)
llist_for_each_entry_safe(cs, next, lnode, attach_node) {
init_llist_node(&cs->attach_node);
if (cs->nr_migrate_dl_tasks) {
- if (cancel && cs->dl_bw_cpu >= 0)
- dl_bw_free(cs->dl_bw_cpu, cs->sum_migrate_dl_bw);
+ /*
+ * Give back the bandwidth the attach did not move:
+ * all of the reservation when it is cancelled, and
+ * what was set aside for the tasks that are no
+ * longer deadline tasks otherwise. Theirs is
+ * released by the class they left, from the root
+ * domain that counts it.
+ */
+ if (cs->dl_bw_cpu >= 0) {
+ u64 bw = cancel ? cs->sum_migrate_dl_bw :
+ cs->sum_migrate_dl_bw - cs->sum_moved_dl_bw;
+
+ if (bw)
+ dl_bw_free(cs->dl_bw_cpu, bw);
+ }
cs->nr_migrate_dl_tasks = 0;
cs->sum_migrate_dl_bw = 0;
+ cs->sum_moved_dl_bw = 0;
cs->dl_bw_cpu = -1;
}
}
@@ -3281,19 +3295,28 @@ static void cpuset_attach_task(struct cpuset *cs, struct task_struct *task)
subpartitions_cpus);
/*
* cpuset_reserve_dl_bw() made room in @cs for the bandwidth of the
- * deadline tasks cpuset_can_attach() counted: say so, so that this
- * task's bandwidth moves there with it.
+ * deadline tasks cpuset_can_attach() counted: offer it, so that this
+ * task's bandwidth moves there with it. A task that became a
+ * deadline task since has nothing set aside, so it moves its
+ * bandwidth only where the rest of the reservation covers it.
*/
- if (dl_task(task) && dl_task_needs_bw_move(task, cs->effective_cpus))
+ if (dl_task(task) && dl_task_needs_bw_move(task, cs->effective_cpus) &&
+ cs->sum_moved_dl_bw + task->dl.dl_bw <= cs->sum_migrate_dl_bw)
reserved = true;
/*
* can_attach beforehand should guarantee that this doesn't
* fail. TODO: have a better way to handle failure here
*/
- if (reserved)
- WARN_ON_ONCE(set_cpus_allowed_bw_reserved(task, cpus_attach));
- else
+ if (reserved) {
+ u64 moved;
+
+ /* The move decides: the tally counts what it moved. */
+ WARN_ON_ONCE(set_cpus_allowed_bw_reserved(task, cpus_attach,
+ &moved));
+ cs->sum_moved_dl_bw += moved;
+ } else {
WARN_ON_ONCE(set_cpus_allowed_ptr(task, cpus_attach));
+ }
if (cpuset_v2() && !attach_ctx.mems_updated)
return;
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 6a2d660ffea1..494a815c6bb0 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -3274,16 +3274,21 @@ EXPORT_SYMBOL_GPL(set_cpus_allowed_ptr);
/*
* The same, for a caller that has made room in the root domain of @new_mask
- * for the deadline bandwidth of @p: that bandwidth moves there with it.
+ * for the deadline bandwidth of @p: that bandwidth moves there with it, and
+ * @moved takes what the move counts there, 0 where it moved none.
*/
-int set_cpus_allowed_bw_reserved(struct task_struct *p, const struct cpumask *new_mask)
+int set_cpus_allowed_bw_reserved(struct task_struct *p,
+ const struct cpumask *new_mask, u64 *moved)
{
struct affinity_context ac = {
.new_mask = new_mask,
.flags = SCA_DL_BW_RESERVED,
};
+ int ret;
- return __set_cpus_allowed_ptr(p, &ac);
+ ret = __set_cpus_allowed_ptr(p, &ac);
+ *moved = ac.dl_bw_moved;
+ return ret;
}
/*
diff --git a/kernel/sched/deadline.c b/kernel/sched/deadline.c
index 1b730e22ee6c..f169cf35e313 100644
--- a/kernel/sched/deadline.c
+++ b/kernel/sched/deadline.c
@@ -3544,7 +3544,9 @@ static void set_cpus_allowed_dl(struct task_struct *p,
* The bandwidth of a task whose move no one made room for stays
* where it is counted, for the next rebuild of the accounting to
* attribute, rather than leaving the root domain it moves to short
- * of what its tasks hold.
+ * of what its tasks hold. @ctx tells the caller what moved: the
+ * record of a task is read here, under its runqueue lock, and the
+ * caller cannot keep a reservation for a move this decides against.
*/
if ((ctx->flags & SCA_DL_BW_RESERVED) &&
dl_task_needs_bw_move(p, ctx->new_mask)) {
@@ -3572,6 +3574,7 @@ static void set_cpus_allowed_dl(struct task_struct *p,
cpu = cpumask_any_and(ctx->new_mask, cpu_active_mask);
p->dl.dl_bw_cpu = cpu < nr_cpu_ids ? (int)cpu : -1;
p->dl.dl_bw_gen = dl_bw_gen;
+ ctx->dl_bw_moved = p->dl.dl_bw;
}
set_cpus_allowed_common(p, ctx);
diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
index 57aadc5b1ba6..10a4750af6f3 100644
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -2662,6 +2662,8 @@ extern const u32 sched_prio_to_wmult[40];
struct affinity_context {
const struct cpumask *new_mask;
struct cpumask *user_mask;
+ /* Out: the deadline bandwidth the move counts in the new root domain. */
+ u64 dl_bw_moved;
unsigned int flags;
};
--
2.47.3
next prev parent reply other threads:[~2026-11-07 1:45 UTC|newest]
Thread overview: 30+ 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 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 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 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 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 ` Mathieu Desnoyers [this message]
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 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
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=20261107014451.1487825-20-mathieu.desnoyers@efficios.com \
--to=mathieu.desnoyers@efficios.com \
--cc=bigeasy@linutronix.de \
--cc=boqun@kernel.org \
--cc=bsegall@google.com \
--cc=cgroups@vger.kernel.org \
--cc=clrkwllms@kernel.org \
--cc=dietmar.eggemann@arm.com \
--cc=hannes@cmpxchg.org \
--cc=juri.lelli@redhat.com \
--cc=kayracizmeci@gmail.com \
--cc=kprateek.nayak@amd.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-kselftest@vger.kernel.org \
--cc=linux-rt-devel@lists.linux.dev \
--cc=longman@redhat.com \
--cc=luca.abeni@santannapisa.it \
--cc=m.szyprowski@samsung.com \
--cc=mathieu.poirier@linaro.org \
--cc=mgorman@suse.de \
--cc=mingo@redhat.com \
--cc=mkoutny@suse.com \
--cc=parri.andrea@gmail.com \
--cc=peterz@infradead.org \
--cc=ridong.chen@linux.dev \
--cc=rostedt@goodmis.org \
--cc=shuah@kernel.org \
--cc=stable@vger.kernel.org \
--cc=tglx@kernel.org \
--cc=tj@kernel.org \
--cc=vincent.guittot@linaro.org \
--cc=vschneid@redhat.com \
--cc=weihengqiuu@gmail.com \
--cc=will@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®