From: Peter Zijlstra <peterz@infradead.org>
To: mingo@kernel.org
Cc: peterz@infradead.org, juri.lelli@redhat.com,
vincent.guittot@linaro.org, dietmar.eggemann@arm.com,
rostedt@goodmis.org, bsegall@google.com, mgorman@suse.de,
vschneid@redhat.com, kprateek.nayak@amd.com,
linux-kernel@vger.kernel.org, tj@kernel.org,
ziqianlu@bytedance.com
Subject: [PATCH v2 05/12] sched/core: Abstract/fix rq_clock_start_loop_update()
Date: Sat, 07 Nov 2026 12:59:18 +0100 [thread overview]
Message-ID: <20261107120207.242460109@infradead.org> (raw)
In-Reply-To: 20261107115913.261163891@infradead.org
The existing rq_clock_start_loop_update() / rq_lock_stop_loop_update() almost
do what we need rq_drop_lock() / rq_repin_lock() to do. They freeze time by
setting ACT_SKIP.
Rename them rq_clock_freeze() / rq_clock_thaw() and have them operate on a new
bit RQCF_FREEZE. This is such that schedule() can safely nest inside
rq_clock_freeze() as is required for rq_drop_lock().
Sanitize the rq->clock_update_flags handling while adding the new bit --
notably do away with the full shift left which makes a mess of things.
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
---
kernel/sched/core.c | 4 +--
kernel/sched/fair.c | 15 +++++++-----
kernel/sched/sched.h | 63 +++++++++++++++++++++++++++++----------------------
3 files changed, 48 insertions(+), 34 deletions(-)
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -7185,9 +7185,9 @@ static void __sched notrace __schedule(i
hrtick_schedule_enter(rq);
/* Promote REQ to ACT */
- rq->clock_update_flags <<= 1;
+ rq->clock_update_flags |= (rq->clock_update_flags & RQCF_REQ_SKIP) << 1;
update_rq_clock(rq);
- rq->clock_update_flags = RQCF_UPDATED;
+ rq->clock_update_flags &= (RQCF_FREEZE|RQCF_UPDATED);
switch_count = &prev->nivcsw;
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -5445,6 +5445,7 @@ static inline void clear_tg_load_avg(str
static void __maybe_unused clear_tg_offline_cfs_rqs(struct rq *rq)
{
struct task_group *tg;
+ unsigned int cuf;
lockdep_assert_rq_held(rq);
@@ -5453,7 +5454,7 @@ static void __maybe_unused clear_tg_offl
* set_rq_offline(), so we should skip updating
* the rq clock again in unthrottle_cfs_rq().
*/
- rq_clock_start_loop_update(rq);
+ cuf = rq_clock_freeze(rq);
guard(rcu)();
@@ -5463,7 +5464,7 @@ static void __maybe_unused clear_tg_offl
clear_tg_load_avg(cfs_rq);
}
- rq_clock_stop_loop_update(rq);
+ rq_clock_thaw(rq, cuf);
}
/*
@@ -7331,6 +7332,7 @@ static void __cfsb_csd_unthrottle(void *
{
struct cfs_rq *cursor, *tmp;
struct rq *rq = arg;
+ unsigned int cuf;
guard(rq_lock)(rq);
@@ -7340,7 +7342,7 @@ static void __cfsb_csd_unthrottle(void *
* Do it once and skip the potential next ones.
*/
update_rq_clock(rq);
- rq_clock_start_loop_update(rq);
+ cuf = rq_clock_freeze(rq);
/*
* Since we hold rq lock we're safe from concurrent manipulation of
@@ -7359,7 +7361,7 @@ static void __cfsb_csd_unthrottle(void *
unthrottle_cfs_rq(cursor);
}
- rq_clock_stop_loop_update(rq);
+ rq_clock_thaw(rq, cuf);
}
static inline void __unthrottle_cfs_rq_async(struct cfs_rq *cfs_rq)
@@ -7865,6 +7867,7 @@ static void __maybe_unused update_runtim
static void __maybe_unused unthrottle_offline_cfs_rqs(struct rq *rq)
{
struct task_group *tg;
+ unsigned int cuf;
lockdep_assert_rq_held(rq);
@@ -7877,7 +7880,7 @@ static void __maybe_unused unthrottle_of
* set_rq_offline(), so we should skip updating
* the rq clock again in unthrottle_cfs_rq().
*/
- rq_clock_start_loop_update(rq);
+ cuf = rq_clock_freeze(rq);
guard(rcu)();
@@ -7904,7 +7907,7 @@ static void __maybe_unused unthrottle_of
unthrottle_cfs_rq(cfs_rq);
}
- rq_clock_stop_loop_update(rq);
+ rq_clock_thaw(rq, cuf);
}
bool cfs_task_bw_constrained(struct task_struct *p)
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -1790,24 +1790,33 @@ extern void update_rq_clock(struct rq *r
* %RQCF_ACT_SKIP - is set from inside of __schedule() when skipping is
* in effect and calls to update_rq_clock() are being ignored.
*
+ * %RQCF_FREEZE - set from rq_clock_freeze(), much like ACT_SKIP it
+ * makes update_rq_clock() a noop.
+ *
* %RQCF_UPDATED - is a debug flag that indicates whether a call has been
* made to update_rq_clock() since the last time rq::lock was pinned.
*
- * If inside of __schedule(), clock_update_flags will have been
- * shifted left (a left shift is a cheap operation for the fast path
- * to promote %RQCF_REQ_SKIP to %RQCF_ACT_SKIP), so you must use,
- *
- * if (rq-clock_update_flags >= RQCF_UPDATED)
- *
- * to check if %RQCF_UPDATED is set. It'll never be shifted more than
- * one position though, because the next rq_unpin_lock() will shift it
- * back.
+ *
+ * yield():
+ * will generally result in REQ_SKIP.
+ *
+ * update_rq_clock():
+ * sets UPDATED, will not modify rq->clock when NOP_MASK
+ *
+ * schedule():
+ * will promote REQ_SKIP to ACT_SKIP before its update_rq_clock() to ensure
+ * the yield() and schedule() appear to happen instantaneous.
+ *
+ * rq_clock_freeze()/rq_clock_thaw():
+ * set FREEZE to make 'nested' update_rq_clock() calls NOP and avoid
+ * time being moved forward. Also used in rq_drop_lock()/rq_repin_lock().
*/
#define RQCF_REQ_SKIP 0x01
#define RQCF_ACT_SKIP 0x02
-#define RQCF_UPDATED 0x04
+#define RQCF_FREEZE 0x04
+#define RQCF_UPDATED 0x08
-#define RQCF_NOP_MASK (RQCF_ACT_SKIP | RQCF_UPDATED)
+#define RQCF_NOP_MASK (RQCF_ACT_SKIP | RQCF_FREEZE | RQCF_UPDATED)
static inline void assert_clock_updated(struct rq *rq)
{
@@ -1815,7 +1824,7 @@ static inline void assert_clock_updated(
* The only reason for not seeing a clock update since the
* last rq_pin_lock() is if we're currently skipping updates.
*/
- WARN_ON_ONCE(rq->clock_update_flags < RQCF_ACT_SKIP);
+ WARN_ON_ONCE(!(rq->clock_update_flags & RQCF_UPDATED));
}
static inline u64 rq_clock(struct rq *rq)
@@ -1851,25 +1860,27 @@ static inline void rq_clock_cancel_skipu
}
/*
- * During cpu offlining and rq wide unthrottling, we can trigger
- * an update_rq_clock() for several cfs and rt runqueues (Typically
- * when using list_for_each_entry_*)
- * rq_clock_start_loop_update() can be called after updating the clock
- * once and before iterating over the list to prevent multiple update.
- * After the iterative traversal, we need to call rq_clock_stop_loop_update()
- * to clear RQCF_ACT_SKIP of rq->clock_update_flags.
+ * FREEZE rq_clock. This is used in a number of places to
+ * ensure things appears to happen at the same time.
+ *
+ * Eg. during cpu offlining and rq wide unthrottling, we can trigger an
+ * update_rq_clock() for several cfs and rt runqueues (Typically when using
+ * list_for_each_entry_*)
*/
-static inline void rq_clock_start_loop_update(struct rq *rq)
+static inline unsigned int rq_clock_freeze(struct rq *rq)
{
+ unsigned int flags = rq->clock_update_flags;
lockdep_assert_rq_held(rq);
- WARN_ON_ONCE(rq->clock_update_flags & RQCF_ACT_SKIP);
- rq->clock_update_flags |= RQCF_ACT_SKIP;
+ assert_clock_updated(rq);
+ rq->clock_update_flags |= RQCF_FREEZE;
+ return flags;
}
-static inline void rq_clock_stop_loop_update(struct rq *rq)
+static inline void rq_clock_thaw(struct rq *rq, bool flags)
{
lockdep_assert_rq_held(rq);
- rq->clock_update_flags &= ~RQCF_ACT_SKIP;
+ rq->clock_update_flags &= ~RQCF_FREEZE;
+ rq->clock_update_flags |= RQCF_UPDATED | (flags & RQCF_FREEZE);
}
struct rq_flags {
@@ -1938,14 +1949,14 @@ static inline void rq_pin_lock(struct rq
{
rf->cookie = lockdep_pin_lock(__rq_lockp(rq));
- rq->clock_update_flags &= (RQCF_REQ_SKIP|RQCF_ACT_SKIP);
+ rq->clock_update_flags &= ~RQCF_UPDATED;
rf->clock_update_flags = 0;
assert_balance_callbacks_empty(rq);
}
static inline void rq_unpin_lock(struct rq *rq, struct rq_flags *rf)
{
- if (rq->clock_update_flags > RQCF_ACT_SKIP)
+ if (rq->clock_update_flags & RQCF_UPDATED)
rf->clock_update_flags = RQCF_UPDATED;
scx_rq_clock_invalidate(rq);
next prev parent reply other threads:[~2026-11-07 12:03 UTC|newest]
Thread overview: 42+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-07 11:59 [PATCH v2 00/12] sched: core-sched fixes and balancing Peter Zijlstra
2026-11-07 11:59 ` [PATCH v2 01/12] sched/core: Fix pick_next_task() self recursion Peter Zijlstra
2026-11-11 0:30 ` Tejun Heo
2026-11-11 10:45 ` Peter Zijlstra
2026-11-07 11:59 ` [PATCH v2 02/12] sched/core: Introduce rq_drop_lock() Peter Zijlstra
2026-11-08 9:25 ` Kayra Cizmeci
2026-11-11 10:46 ` Peter Zijlstra
2026-11-07 11:59 ` [PATCH v2 03/12] sched/core: Decompose update_rq_clock() Peter Zijlstra
2026-11-07 11:59 ` [PATCH v2 04/12] sched/core: Make __update_rq_clock() skip updates on UPDATED Peter Zijlstra
2026-11-11 1:11 ` Tejun Heo
2026-11-11 10:28 ` Peter Zijlstra
2026-11-11 12:25 ` Peter Zijlstra
2026-11-11 17:08 ` Tejun Heo
2026-11-07 11:59 ` Peter Zijlstra [this message]
2026-11-08 22:26 ` [PATCH v2 05/12] sched/core: Abstract/fix rq_clock_start_loop_update() Kayra Cizmeci
2026-11-11 9:10 ` K Prateek Nayak
2026-11-11 9:51 ` Kayra Cizmeci
2026-11-11 16:39 ` Peter Zijlstra
2026-11-07 11:59 ` [PATCH v2 06/12] sched/core: New rq_{pin,drop,repin,unpin}_lock() semantics Peter Zijlstra
2026-11-11 10:41 ` Tejun Heo
2026-11-07 11:59 ` [PATCH v2 07/12] sched/core: Simplify/fix time updates Peter Zijlstra
2026-11-07 11:59 ` [PATCH v2 08/12] sched/core: Allow newidle for core-sched Peter Zijlstra
2026-11-07 11:59 ` [PATCH v2 09/12] sched/rt: Add early exit on balance path Peter Zijlstra
2026-11-07 11:59 ` [PATCH v2 10/12] sched/fair: Reflow pick_task_fair() / newidle Peter Zijlstra
2026-11-11 11:02 ` Tejun Heo
2026-11-11 12:31 ` Peter Zijlstra
2026-11-07 11:59 ` [PATCH v2 11/12] sched/fair: Push sched_balance_newidle() unlock down Peter Zijlstra
2026-11-07 11:59 ` [PATCH v2 12/12] sched: Remove sched_class::balance() Peter Zijlstra
2026-11-11 11:18 ` Tejun Heo
2026-11-11 13:05 ` Peter Zijlstra
2026-11-11 13:11 ` Peter Zijlstra
2026-11-11 14:57 ` Kayra Cizmeci
2026-11-11 15:47 ` Peter Zijlstra
2026-11-11 16:15 ` Peter Zijlstra
2026-11-11 18:53 ` Kayra Cizmeci
2026-11-12 6:46 ` K Prateek Nayak
2026-11-12 9:33 ` Peter Zijlstra
2026-11-12 10:17 ` K Prateek Nayak
2026-11-12 10:34 ` Peter Zijlstra
2026-11-12 13:17 ` Andrea Righi
2026-11-11 11:46 ` [PATCH v2 00/12] sched: core-sched fixes and balancing Tejun Heo
2026-11-11 13:57 ` Peter Zijlstra
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=20261107120207.242460109@infradead.org \
--to=peterz@infradead.org \
--cc=bsegall@google.com \
--cc=dietmar.eggemann@arm.com \
--cc=juri.lelli@redhat.com \
--cc=kprateek.nayak@amd.com \
--cc=linux-kernel@vger.kernel.org \
--cc=mgorman@suse.de \
--cc=mingo@kernel.org \
--cc=rostedt@goodmis.org \
--cc=tj@kernel.org \
--cc=vincent.guittot@linaro.org \
--cc=vschneid@redhat.com \
--cc=ziqianlu@bytedance.com \
/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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.