* Re: [PATCH v3 7/7] sched/eevdf: Move to a single runqueue
@ 2026-10-30 13:03 Chengfeng Lin
2026-10-30 17:01 ` Mike Galbraith
0 siblings, 1 reply; 17+ messages in thread
From: Chengfeng Lin @ 2026-10-30 13:03 UTC (permalink / raw)
To: Vincent Guittot, Mike Galbraith, Peter Zijlstra
Cc: Ingo Molnar, Juri Lelli, linux-kernel
[-- Attachment #1: Type: text/plain, Size: 4452 bytes --]
Hi Vincent, Mike, Peter,
I have additional pipe-handoff throughput results for 333238da9a19
("sched: Update time before requeueing delayed entities"), which Mike
identified in his wakeup-jitter test [1]. With 15 workers, will-it-scale
throughput on v7.3 is 15.69% lower than v7.1.3 in thread mode and 14.82%
lower in process mode. Reverting this commit on v7.2 in a separate controlled
comparison improves 15-worker thread throughput by 13.65%; the 2-worker
control changes by -0.48%.
I found this while running the existing MMTests recipe for will-it-scale's
context_switch1 test [2], not a generated workload.
Each worker exchanges one-byte messages with its own helper over two pipes,
counting two handoffs per round trip. I kept the default CORE affinity,
restricted to the i7-12700KF's eight P-cores, with both logical CPUs per core
available. Helpers inherit their worker's affinity mask; above eight
workers, cores are reused. All tasks ran at SCHED_OTHER/nice 0, with no extra
CPU-bound competitors or custom time slices. The benchmark loop was unchanged.
After each boot, I ran one warmup run followed by nine measured runs for
each condition. Each run excluded about six seconds of built-in warmup,
then measured throughput for 15 seconds. All kernels were built with
GCC 15.2.0 and run with full preemption, the powersave governor,
balance_performance EPP and Turbo enabled.
The v7.1.3 -> v7.3 -> v7.1.3 comparison used the recipe's worker counts,
plus 1/4/8-worker controls. Changes below are relative to the average of
the two baseline means:
Workers Thread mode Process mode
1/2/4/6/8 -0.70%..-4.52% -1.32%..-4.59%
11 -10.54% -9.96%
15 -15.69% -14.82%
20 -14.81% -14.43%
26 -12.69% -13.55%
33 -8.75% -7.33%
39 -7.39% -6.46%
40 -7.19% -6.51%
For example, 15-worker thread throughput is 10,875,243 -> 9,168,152 ->
10,872,945 handoffs/s, about 1.71 million fewer handoffs/s on v7.3.
The within-boot coefficient of variation (CV) was at most 2.01%; the two
baseline means differed by at most 1.39%.
The matched v7.2 revert comparison used the same CORE affinity (handoffs/s):
Thread workers v7.2 A v7.2 reverted v7.2 B
2 1,178,295 1,174,770 1,182,545
15 9,413,892 10,695,877 9,409,225
At 15 workers the gain is about 1.28 million handoffs/s. The second baseline
was 0.05% lower than the first, and the maximum CV was 0.11%.
In separate PMU runs, I counted instructions and handoffs over the same
15-second post-warmup window. At 15 workers, kernel instructions per
handoff rose from 8,338 on v7.1.3 to 10,423 on v7.3 (+25.00%). Reverting
the commit on v7.2 reduced this from 10,239 to 8,355 (-18.40%). Context
switches stayed at about one per handoff in both comparisons, pointing to
more kernel work per handoff rather than more switches.
Separate entry probes over the same 15-second window observed about 1.0
delayed requeue per 100 handoffs at 2 workers and 92.6 at 15 workers on v7.2.
Almost every delayed requeue included the added update_curr() call.
The revert removed that call, but also reduced the 15-worker requeue rate
to 55.3 per 100 handoffs. With 15 workers, this accounting path was taken
much more often; reverting the commit also changed how often requeues
occurred.
CPU-location samples showed the same broad placement pattern across the
compared kernels.
These results point to additional work in the delayed-requeue path.
Updating time is needed for correct lag compensation, so the revert is
diagnostic, not a proposed fix. Can this overhead be reduced while
preserving correct lag accounting?
Reproduction details are attached; measurements and diagnostic summaries
are available at [3].
[1] https://lists.openwall.net/linux-kernel/2026/10/18/311
[2] https://github.com/antonblanchard/will-it-scale/blob/a34a85cc1e9b9b74e94fdd3ecc479019da610e6a/tests/context_switch1.c
[3] https://github.com/lcf0399/linux-regression-evidence/tree/8022fe1fb0256e65f63b4bde530b346027f63059/kernel/sched/sched-fair-delayed-requeue-handoff-333238da
LLMs assisted with experiment orchestration, analysis and drafting.
The workload is upstream will-it-scale; results are bare-metal measurements.
Assisted-by: LLM
Thanks,
Chengfeng
[-- Attachment #2: reproduce-wis.txt --]
[-- Type: text/plain, Size: 9043 bytes --]
Native CORE will-it-scale pipe-handoff reproduction
==================================================
This describes the native CORE measurements in the accompanying report.
Run on a dedicated test host.
CPU numbers describe the measured i7-12700KF; adapt them to the other host's
physical topology, then keep the chosen topology identical across kernels.
Pinned inputs
-------------
will-it-scale: https://github.com/antonblanchard/will-it-scale
a34a85cc1e9b9b74e94fdd3ecc479019da610e6a
MMTests: https://github.com/gormanm/mmtests
638401e07e70d67fa5a1e6295e1315ae0af831b2
Kernel peeled commits (not annotated-tag object IDs):
v7.1.3 199c9959d3a9b53f346c221757fc7ac507fbac50
v7.2 8d3ae59288f1e7d58d76558a6ee96d533bc5019f
v7.3 c03c1a14507dfaf8b856c2608f9ac216021fc222
Diagnostic v7.2 variant: reverse 333238da9a193ffc58792995f3e951e4cb87bfd2.
It removes update_curr(cfs_rq) from requeue_delayed_entity and is NOT a
proposed fix. The intervention was not performed on v7.3.
Build
-----
Dependencies: C compiler, make, patch, hwloc development files, GNU time,
systemd and cgroup-v2 cpuset support. Tested GCC 15.2.0 and hwloc 2.13.0.
Use clean adjacent will-it-scale and mmtests checkouts. In will-it-scale:
git checkout --detach a34a85cc1e9b9b74e94fdd3ecc479019da610e6a
git -C ../mmtests checkout --detach 638401e07e70d67fa5a1e6295e1315ae0af831b2
sed -n '/^==== BEGIN align-alloc.patch ====$/,/^==== END align-alloc.patch ====$/p' \
../mmtests/shellpack_src/src/wis/wis-install | sed '1d;$d' | patch -p1
sed -i 's|/tmp/shared_area|shared_area|g' main.c
make -j2 context_switch1_threads context_switch1_processes \
CFLAGS='-Wall -O2 -g -std=gnu11'
The alignment patch and shared_area relocation are from the pinned MMTests
install recipe. tests/context_switch1.c is unchanged. Other file-path edits
in the recipe affect file-backed tests, not this pipe test.
Our executable SHA-256 values (different build environments can differ):
threads:
6fd5c63e4caec9916cbd3942b1ad037f738d24b1232c18711f96b72bc5f248d6
processes:
e0e9cf0017bdc0e5f276d7625ecfc0f26b1af1593bca1c50aafd9b1aa7497867
Placement and one invocation
----------------------------
Check lscpu -e=CPU,CORE,SOCKET,NODE. On this host CPU0-15 are eight P-cores;
CORE masks are 0,1; 2,3; ...; 14,15. CPU16-19 are E-cores and excluded from
the test group, but all 20 logical CPUs remain online.
Keep both SMT CPUs in each CORE mask. Native hwloc binding is enabled;
do not add -n or -m. Helpers inherit their worker's mask. Above eight
workers, cores are reused in native order. The coordinator retains CPU0-15.
This is not one worker/helper pair forced onto two separate physical cores.
Example for 15 workers in thread mode (replace mode with processes):
Use an ordinary account with sudo for service creation. Keep the checkout
outside /tmp and /var/tmp because PrivateTmp would hide it.
workers=15
mode=threads
binary="$PWD/context_switch1_$mode"
work=$(mktemp -d "$PWD/wis-run.XXXXXX")
sudo systemd-run --quiet --wait --pipe --collect --service-type=exec \
--unit="wis-handoff-$$" \
-p "User=$(id -un)" -p "Group=$(id -gn)" \
-p 'CPUAffinity=0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15' \
-p AllowedCPUs=0-15 -p MemoryMax=2G -p MemorySwapMax=0 \
-p "TasksMax=$((2 * workers + 16))" \
-p RuntimeMaxSec=45 -p TimeoutStopSec=5 -p KillMode=control-group \
-p NoNewPrivileges=yes -p Environment=LC_ALL=C \
-p "WorkingDirectory=$work" -p "ReadWritePaths=$work" \
-p PrivateTmp=yes -p PrivateDevices=yes \
-p ProtectSystem=strict -p ProtectHome=read-only \
-p TemporaryFileSystem=/dev/shm:rw,size=16M,mode=1777 \
-p LimitCORE=0 -p LimitFSIZE=536870912 -p LimitNOFILE=8192 \
-- /usr/bin/time -v -o "$work/time.txt" \
"$binary" -t "$workers" -s 15 > "$work/native.txt"
Separately validate affinity/clone inheritance using strace -f with
sched_setaffinity, clone and clone3 before clean timing. Check the effective
mask after intersection with the cgroup, not merely the requested mask.
At 15 workers, 30 worker/helper tasks use the eight CORE masks; the first
seven masks have four tasks each and the last has two. The coordinator is
additional. In process mode these are processes, not the same thread group.
Do not use strace or perf during timing. Preserve failed/incomplete runs.
Separate actual-placement check (not clean timing): two inherited 199-Hz
CPU-clock records per condition on v7.1.3, v7.3, v7.2 and the diagnostic
revert. In each record, 14/15 worker/helper pairs at 15 workers had all
captured samples on one logical CPU per pair; the remaining pair used the
CPU14/15 core. At 2 workers, both pairs had samples across their core's
SMT CPUs. Thus an allowed CORE mask does not imply the pair actually runs
on separate logical CPUs. The pattern was unchanged across the four kernels.
Pairs were reconstructed from fork events, with independently checked
affinity masks. The sampled window starts six seconds after the last test
thread is created and ends at the first test-thread exit. It approximates
the measured interval, not an exact benchmark marker or a complete schedule;
unsampled migrations and other timing effects cannot be excluded.
Parameters, runtime and sampling
--------------------------------
Native recipe worker list on this host:
2, 6, 11, 15, 20, 26, 33, 39, 40
Also measured separately: 1, 4, 8. Both thread and process modes were tested.
This does not cover every possible MMTests configuration.
Bare metal, i7-12700KF, 32 GiB RAM; GCC 15.2.0, runtime preempt=full.
SCHED_OTHER/nice 0, no custom sched_runtime/slice, no extra CPU-bound spinner.
powersave governor, balance_performance EPP, Turbo enabled at every point.
No CPU bandwidth quota; 2 GiB memory cap and no swap for the test group.
RT budget remains 950000 us per 1000000 us; these are not RT tasks.
The matched v7.2 builds differ only in the diagnostic revert and LOCALVERSION.
Keep other settings matched and restore temporary changes afterward.
No benchmark, kernel build, coverage observer or profiler runs concurrently.
For each condition in each boot: one placement diagnostic, one complete
warmup run, then nine measured runs without instrumentation. Each run has
six native warmup intervals, fifteen
measurement intervals and average:N. The average is the integer floor of
the sum of fifteen measured totals divided by fifteen.
The main metric is aggregate native handoffs/s, two increments per
write/read round trip. It is not a PMU context-switch count. GNU time records
whole-run user/system CPU and elapsed time separately. Runs last about
21 seconds; lower throughput does not mean a correspondingly longer run.
Independent comparisons:
24 conditions: v7.1.3 -> v7.3 -> v7.1.3.
Thread workers 2/15: v7.2 A -> diagnostic revert -> v7.2 B.
Nine runs within one boot are not nine independent boots. Keep comparisons
and boot positions separate; do not pool batches. Compare the middle mean
to the mean of the first/return means. CV uses sample standard deviation;
return drift is return / first - 1.
Per-run measurements and raw-output verification are retained locally and
available on request; no measurement CSV accompanies the initial report.
Separate post-warmup diagnostics
-------------------------------
Counter diagnostics use the same fifteen native intervals as the throughput
denominator, not the whole 21-second invocation. A diagnostic-only preload
reads inherited task-scoped perf descriptors at the original "measurement"
and "average" output boundaries; the benchmark executable and hot loop are
unchanged. Subtract the two readings and divide by the sum of the fifteen
native handoff totals. Record enabled/running time and boundary read times;
these are short sequential snapshots, not a simultaneous stop of all workers.
There were three counter runs per condition at v7.1.3, v7.3, v7.2 and the
matched revert, for 2/15 workers. Kernel instructions/cycles cover the test
task family, not update_curr alone; interrupt work in that context can count.
They are diagnostics, never additional clean-throughput samples.
A different batch used two entry-probe runs per condition on v7.2 and the
revert, with the same native window markers. Count requeue_delayed_entity
entries/returns, update_curr entries, and update_curr inside the requeue,
restricted to the marked benchmark thread group. Check entry/return bounds
and per-object count conservation; the first arguments are sched_entity and
cfs_rq respectively, not a common runqueue identifier. Exact tested symbols
and the presence/absence of the internal update_curr call were checked in
both vmlinux files. Probe overhead changes execution, so no PMU or throughput
values from these instrumented runs are used as performance evidence. The counts
do not reconstruct every lag value, hierarchy or scheduling decision.
^ permalink raw reply [flat|nested] 17+ messages in thread* Re: [PATCH v3 7/7] sched/eevdf: Move to a single runqueue 2026-10-30 13:03 [PATCH v3 7/7] sched/eevdf: Move to a single runqueue Chengfeng Lin @ 2026-10-30 17:01 ` Mike Galbraith 0 siblings, 0 replies; 17+ messages in thread From: Mike Galbraith @ 2026-10-30 17:01 UTC (permalink / raw) To: Chengfeng Lin, Vincent Guittot, Peter Zijlstra Cc: Ingo Molnar, Juri Lelli, linux-kernel On Fri, 2026-10-30 at 21:03 +0800, Chengfeng Lin wrote: > Hi Vincent, Mike, Peter, > > I have additional pipe-handoff throughput results for 333238da9a19 > ("sched: Update time before requeueing delayed entities"), which Mike > identified in his wakeup-jitter test [1]. With 15 workers, will-it-scale > throughput on v7.3 is 15.69% lower than v7.1.3 in thread mode and 14.82% > lower in process mode. Reverting this commit on v7.2 in a separate controlled > comparison improves 15-worker thread throughput by 13.65%; the 2-worker > control changes by -0.48%. After much crawling over trees etc, and way more yacking than intended for a datapoint, I think I know what's up, and it's simple: there's a good reason those "now" updates are event driven. That one was added in the DELAY_DEQUEUE machinery, where it expends and *banks* precious fastpath cycles, shifting bean counting enough to make DELAY_DEQUEUE measurably less effective. It looks completely broken with a 20% util probe, but more measurement shows that it's not dead, just a bit sad. Test probe sent another signal in virgin flat in __dequeue_task(), and I think it's valid for that update_curr() too. It seems to be in keeping with placement strategy, but with flat changes, we just updated the cfs_rq where all the action is, and hierarchy update is right below it.. looks like redundant overhead. -Mike ^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH v3 0/7] sched: Flatten the pick
@ 2026-06-05 12:40 Peter Zijlstra
2026-06-05 12:40 ` [PATCH v3 7/7] sched/eevdf: Move to a single runqueue Peter Zijlstra
0 siblings, 1 reply; 17+ messages in thread
From: Peter Zijlstra @ 2026-06-05 12:40 UTC (permalink / raw)
To: mingo
Cc: longman, chenridong, peterz, juri.lelli, vincent.guittot,
dietmar.eggemann, rostedt, bsegall, mgorman, vschneid, tj,
hannes, mkoutny, cgroups, linux-kernel, jstultz, kprateek.nayak,
qyousef
Hi!
New version, same story [1]. TL;DR:
- Adds new cgroup_mode knob and implements new policies to address the
hierarchy level weight mismatch.
- Builds upon that base to create a flat / single runqueue scheduler where the
cgroup hierarchy is expressed through dynamic weight management.
I'm hoping to be able to merge these patches early in the next cycle (after
7.2-rc1).
Random benchmark:
Game vs 'for ((i=0; i<8; i++)) do nice ./spin.sh; done':
Lutris / GE-Proton10-34 / Steam Runtime 3 (sniper)
Intel Core i7-2600K
AMD Radeon RX 580
Shadows Awakening (GOG)
default slice(*)
FPS min 4.0 29.0
avg 47.5 59.2
max 83.7 83.7
FT min 9.3 10.2
avg 34.0 17.0
max 121.2 30.0
FPS (Frames Per Second)
FT (FrameTime)
[*] Command prefix: 'chrt -o --sched-runtime 100000 0'
Changes since v2:
- merged debug and prep patches
- fixed update_entity_lag() on dequeue (Vincent)
- fixed throttle vs tick (Prateek)
- fixed wakeup_preempt_fair()
- rebased on tip/sched/core
- rewritten cgroup_mode changelogs
- reworked cgroup_mode concur
- added cgroup_mode tasks
- changed default cgroup_mode
[1] - https://lore.kernel.org/r/20260511113104.563854162@infradead.org
Can also be had:
git://git.kernel.org/pub/scm/linux/kernel/git/peterz/queue.git sched/flat
include/linux/cpuset.h | 6
include/linux/sched.h | 1
kernel/cgroup/cpuset.c | 15
kernel/sched/core.c | 5
kernel/sched/debug.c | 89 ++++
kernel/sched/fair.c | 943 ++++++++++++++++++++++++-------------------------
kernel/sched/pelt.c | 6
kernel/sched/sched.h | 30 -
8 files changed, 607 insertions(+), 488 deletions(-)
^ permalink raw reply [flat|nested] 17+ messages in thread* [PATCH v3 7/7] sched/eevdf: Move to a single runqueue 2026-06-05 12:40 [PATCH v3 0/7] sched: Flatten the pick Peter Zijlstra @ 2026-06-05 12:40 ` Peter Zijlstra 2026-06-20 3:54 ` Chen, Yu C 2026-10-16 13:18 ` Jakub Okoński 0 siblings, 2 replies; 17+ messages in thread From: Peter Zijlstra @ 2026-06-05 12:40 UTC (permalink / raw) To: mingo Cc: longman, chenridong, peterz, juri.lelli, vincent.guittot, dietmar.eggemann, rostedt, bsegall, mgorman, vschneid, tj, hannes, mkoutny, cgroups, linux-kernel, jstultz, kprateek.nayak, qyousef Change fair/cgroup to a single runqueue. Infamously fair/cgroup isn't working for a number of people; typically the complaint is latencies and/or overhead. The latency issue is due to the intermediate entries that represent a combination of tasks and thereby obfuscate the runnability of tasks. The approach here is to leave the cgroup hierarchy as is; including the intermediate enqueue/dequeue but move the actual EEVDF runqueue outside. This means things like the shares_weight approximation are fully preserved. That is, given a hierarchy like: R | se--G1 / \ G2--se se--G3 / \ | T1--se se--T2 se--T3 This is fully maintained for load tracking, however the EEVDF parts of cfs_rq/se go unused for the intermediates and are instead connected like: _R_ / | \ T1 T2 T3 Since the effective weight of the entities is determined by the hierarchy, this gets recomputed on enqueue,set_next_task and tick. Notably, the effective weight (se->h_load) is computed from the hierarchical fraction: se->load / cfs_rq->load. Since EEVDF is now exclusively operating on rq->cfs, it needs to consider cfs_rq->h_nr_queued rather than cfs_rq->nr_queued. Similarly, only tasks can get delayed, simplifying some of the cgroup cleanup. One place where additional information was required was set_next_task() / put_prev_task(), where we need to track 'current' both in the hierarchical sense (cfs_rq->h_curr) and in the flat sense (cfs_rq->curr). As a result of only having a single level to pick from, much of the complications in pick_next_task() and preemption go away. Since many of the hierarchical operations are still there, this won't immediately fix the performance issues, but hopefully it will fix some of the latency issues. TODO: split struct cfs_rq / struct sched_entity TODO: try and get rid of h_curr Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> --- include/linux/sched.h | 1 kernel/sched/core.c | 5 kernel/sched/debug.c | 9 kernel/sched/fair.c | 803 +++++++++++++++++++++----------------------------- kernel/sched/pelt.c | 6 kernel/sched/sched.h | 26 - 6 files changed, 375 insertions(+), 475 deletions(-) --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -574,6 +574,7 @@ struct sched_statistics { struct sched_entity { /* For load-balancing: */ struct load_weight load; + struct load_weight h_load; struct rb_node run_node; u64 deadline; u64 min_vruntime; --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -5652,11 +5652,8 @@ EXPORT_PER_CPU_SYMBOL(kernel_cpustat); */ static inline void prefetch_curr_exec_start(struct task_struct *p) { -#ifdef CONFIG_FAIR_GROUP_SCHED - struct sched_entity *curr = p->se.cfs_rq->curr; -#else struct sched_entity *curr = task_rq(p)->cfs.curr; -#endif + prefetch(curr); prefetch(&curr->exec_start); } --- a/kernel/sched/debug.c +++ b/kernel/sched/debug.c @@ -975,10 +975,11 @@ print_task(struct seq_file *m, struct rq else SEQ_printf(m, " %c", task_state_to_char(p)); - SEQ_printf(m, " %15s %5d %9Ld.%06ld %c %9Ld.%06ld %c %9Ld.%06ld %9Ld.%06ld %9Ld %5d ", + SEQ_printf(m, " %15s %5d %10ld %9Ld.%06ld %c %9Ld.%06ld %c %9Ld.%06ld %9Ld.%06ld %9Ld %5d ", p->comm, task_pid_nr(p), + p->se.h_load.weight, SPLIT_NS(p->se.vruntime), - entity_eligible(cfs_rq_of(&p->se), &p->se) ? 'E' : 'N', + entity_eligible(&rq->cfs, &p->se) ? 'E' : 'N', SPLIT_NS(p->se.deadline), p->se.custom_slice ? 'S' : ' ', SPLIT_NS(p->se.slice), @@ -1007,7 +1008,7 @@ static void print_rq(struct seq_file *m, SEQ_printf(m, "\n"); SEQ_printf(m, "runnable tasks:\n"); - SEQ_printf(m, " S task PID vruntime eligible " + SEQ_printf(m, " S task PID weight vruntime eligible " "deadline slice sum-exec switches " "prio wait-time sum-sleep sum-block" #ifdef CONFIG_NUMA_BALANCING @@ -1115,6 +1116,8 @@ void print_cfs_rq(struct seq_file *m, in cfs_rq->tg_load_avg_contrib); SEQ_printf(m, " .%-30s: %ld\n", "tg_load_avg", atomic_long_read(&cfs_rq->tg->load_avg)); + SEQ_printf(m, " .%-30s: %lu\n", "h_load", + cfs_rq->h_load); #endif /* CONFIG_FAIR_GROUP_SCHED */ #ifdef CONFIG_CFS_BANDWIDTH SEQ_printf(m, " .%-30s: %d\n", "throttled", --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -297,8 +297,8 @@ static u64 __calc_delta(u64 delta_exec, */ static inline u64 calc_delta_fair(u64 delta, struct sched_entity *se) { - if (unlikely(se->load.weight != NICE_0_LOAD)) - delta = __calc_delta(delta, NICE_0_LOAD, &se->load); + if (se->h_load.weight != NICE_0_LOAD) + delta = __calc_delta(delta, NICE_0_LOAD, &se->h_load); return delta; } @@ -428,38 +428,6 @@ static inline struct sched_entity *paren return se->parent; } -static void -find_matching_se(struct sched_entity **se, struct sched_entity **pse) -{ - int se_depth, pse_depth; - - /* - * preemption test can be made between sibling entities who are in the - * same cfs_rq i.e who have a common parent. Walk up the hierarchy of - * both tasks until we find their ancestors who are siblings of common - * parent. - */ - - /* First walk up until both entities are at same depth */ - se_depth = (*se)->depth; - pse_depth = (*pse)->depth; - - while (se_depth > pse_depth) { - se_depth--; - *se = parent_entity(*se); - } - - while (pse_depth > se_depth) { - pse_depth--; - *pse = parent_entity(*pse); - } - - while (!is_same_group(*se, *pse)) { - *se = parent_entity(*se); - *pse = parent_entity(*pse); - } -} - static int tg_is_idle(struct task_group *tg) { return tg->idle > 0; @@ -503,11 +471,6 @@ static inline struct sched_entity *paren return NULL; } -static inline void -find_matching_se(struct sched_entity **se, struct sched_entity **pse) -{ -} - static inline int tg_is_idle(struct task_group *tg) { return 0; @@ -686,7 +649,7 @@ static inline unsigned long avg_vruntime static inline void __sum_w_vruntime_add(struct cfs_rq *cfs_rq, struct sched_entity *se) { - unsigned long weight = avg_vruntime_weight(cfs_rq, se->load.weight); + unsigned long weight = avg_vruntime_weight(cfs_rq, se->h_load.weight); s64 w_vruntime, key = entity_key(cfs_rq, se); w_vruntime = key * weight; @@ -703,7 +666,7 @@ sum_w_vruntime_add_paranoid(struct cfs_r s64 key, tmp; again: - weight = avg_vruntime_weight(cfs_rq, se->load.weight); + weight = avg_vruntime_weight(cfs_rq, se->h_load.weight); key = entity_key(cfs_rq, se); if (check_mul_overflow(key, weight, &key)) @@ -749,7 +712,7 @@ sum_w_vruntime_add(struct cfs_rq *cfs_rq static void sum_w_vruntime_sub(struct cfs_rq *cfs_rq, struct sched_entity *se) { - unsigned long weight = avg_vruntime_weight(cfs_rq, se->load.weight); + unsigned long weight = avg_vruntime_weight(cfs_rq, se->h_load.weight); s64 key = entity_key(cfs_rq, se); cfs_rq->sum_w_vruntime -= key * weight; @@ -791,7 +754,7 @@ u64 avg_vruntime(struct cfs_rq *cfs_rq) s64 runtime = cfs_rq->sum_w_vruntime; if (curr) { - unsigned long w = avg_vruntime_weight(cfs_rq, curr->load.weight); + unsigned long w = avg_vruntime_weight(cfs_rq, curr->h_load.weight); runtime += entity_key(cfs_rq, curr) * w; weight += w; @@ -862,8 +825,6 @@ bool update_entity_lag(struct cfs_rq *cf u64 avruntime = avg_vruntime(cfs_rq); s64 vlag = entity_lag(cfs_rq, se, avruntime); - WARN_ON_ONCE(!se->on_rq); - if (se->sched_delayed) { /* previous vlag < 0 otherwise se would not be delayed */ vlag = max(vlag, se->vlag); @@ -899,7 +860,7 @@ static int vruntime_eligible(struct cfs_ long load = cfs_rq->sum_weight; if (curr && curr->on_rq) { - unsigned long weight = avg_vruntime_weight(cfs_rq, curr->load.weight); + unsigned long weight = avg_vruntime_weight(cfs_rq, curr->h_load.weight); avg += entity_key(cfs_rq, curr) * weight; load += weight; @@ -1040,6 +1001,9 @@ RB_DECLARE_CALLBACKS(static, min_vruntim */ static void __enqueue_entity(struct cfs_rq *cfs_rq, struct sched_entity *se) { + WARN_ON_ONCE(&rq_of(cfs_rq)->cfs != cfs_rq); + WARN_ON_ONCE(!entity_is_task(se)); + sum_w_vruntime_add(cfs_rq, se); se->min_vruntime = se->vruntime; se->min_slice = se->slice; @@ -1049,6 +1013,9 @@ static void __enqueue_entity(struct cfs_ static void __dequeue_entity(struct cfs_rq *cfs_rq, struct sched_entity *se) { + WARN_ON_ONCE(&rq_of(cfs_rq)->cfs != cfs_rq); + WARN_ON_ONCE(!entity_is_task(se)); + rb_erase_augmented_cached(&se->run_node, &cfs_rq->tasks_timeline, &min_vruntime_cb); sum_w_vruntime_sub(cfs_rq, se); @@ -1145,7 +1112,7 @@ static struct sched_entity *pick_eevdf(s * We can safely skip eligibility check if there is only one entity * in this cfs_rq, saving some cycles. */ - if (cfs_rq->nr_queued == 1) + if (cfs_rq->h_nr_queued == 1) return curr && curr->on_rq ? curr : se; /* @@ -1395,8 +1362,6 @@ static s64 update_se(struct rq *rq, stru return delta_exec; } -static void set_next_buddy(struct sched_entity *se); - #ifdef CONFIG_SCHED_CACHE /* @@ -1991,7 +1956,7 @@ static void update_curr(struct cfs_rq *c * not necessarily be the actual task running * (rq->curr.se). This is easy to confuse! */ - struct sched_entity *curr = cfs_rq->curr; + struct sched_entity *curr = cfs_rq->h_curr; struct rq *rq = rq_of(cfs_rq); s64 delta_exec; bool resched; @@ -2003,26 +1968,29 @@ static void update_curr(struct cfs_rq *c if (unlikely(delta_exec <= 0)) return; + account_cfs_rq_runtime(cfs_rq, delta_exec); + + if (!entity_is_task(curr)) + return; + + cfs_rq = &rq->cfs; + curr->vruntime += calc_delta_fair(delta_exec, curr); resched = update_deadline(cfs_rq, curr); - if (entity_is_task(curr)) { - /* - * If the fair_server is active, we need to account for the - * fair_server time whether or not the task is running on - * behalf of fair_server or not: - * - If the task is running on behalf of fair_server, we need - * to limit its time based on the assigned runtime. - * - Fair task that runs outside of fair_server should account - * against fair_server such that it can account for this time - * and possibly avoid running this period. - */ - dl_server_update(&rq->fair_server, delta_exec); - } - - account_cfs_rq_runtime(cfs_rq, delta_exec); + /* + * If the fair_server is active, we need to account for the + * fair_server time whether or not the task is running on + * behalf of fair_server or not: + * - If the task is running on behalf of fair_server, we need + * to limit its time based on the assigned runtime. + * - Fair task that runs outside of fair_server should account + * against fair_server such that it can account for this time + * and possibly avoid running this period. + */ + dl_server_update(&rq->fair_server, delta_exec); - if (cfs_rq->nr_queued == 1) + if (cfs_rq->h_nr_queued == 1) return; if (resched || !protect_slice(curr)) { @@ -2033,7 +2001,10 @@ static void update_curr(struct cfs_rq *c static void update_curr_fair(struct rq *rq) { - update_curr(cfs_rq_of(&rq->donor->se)); + struct sched_entity *se = &rq->donor->se; + + for_each_sched_entity(se) + update_curr(cfs_rq_of(se)); } static inline void @@ -2109,7 +2080,7 @@ update_stats_enqueue_fair(struct cfs_rq * Are we enqueueing a waiting task? (for current tasks * a dequeue/enqueue event is a NOP) */ - if (se != cfs_rq->curr) + if (se != cfs_rq->h_curr) update_stats_wait_start_fair(cfs_rq, se); if (flags & ENQUEUE_WAKEUP) @@ -2127,7 +2098,7 @@ update_stats_dequeue_fair(struct cfs_rq * Mark the end of the wait period if dequeueing a * waiting task: */ - if (se != cfs_rq->curr) + if (se != cfs_rq->h_curr) update_stats_wait_end_fair(cfs_rq, se); if ((flags & DEQUEUE_SLEEP) && entity_is_task(se)) { @@ -4468,6 +4439,7 @@ static inline void update_scan_period(st static void account_entity_enqueue(struct cfs_rq *cfs_rq, struct sched_entity *se) { + WARN_ON_ONCE(cfs_rq != cfs_rq_of(se)); update_load_add(&cfs_rq->load, se->load.weight); if (entity_is_task(se)) { struct task_struct *p = task_of(se); @@ -4483,6 +4455,7 @@ account_entity_enqueue(struct cfs_rq *cf static void account_entity_dequeue(struct cfs_rq *cfs_rq, struct sched_entity *se) { + WARN_ON_ONCE(cfs_rq != cfs_rq_of(se)); update_load_sub(&cfs_rq->load, se->load.weight); if (entity_is_task(se)) { struct task_struct *p = task_of(se); @@ -4564,7 +4537,7 @@ dequeue_load_avg(struct cfs_rq *cfs_rq, static void rescale_entity(struct sched_entity *se, unsigned long weight, bool rel_vprot) { - unsigned long old_weight = se->load.weight; + long old_weight = se->h_load.weight; /* * VRUNTIME @@ -4664,16 +4637,17 @@ rescale_entity(struct sched_entity *se, se->vprot = div64_long(se->vprot * old_weight, weight); } -static void reweight_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, - unsigned long weight) +static void reweight_eevdf(struct cfs_rq *cfs_rq, struct sched_entity *se, + unsigned long weight, bool on_rq) { bool curr = cfs_rq->curr == se; bool rel_vprot = false; u64 avruntime = 0; - if (se->on_rq) { - /* commit outstanding execution time */ - update_curr(cfs_rq); + if (se->h_load.weight == weight) + return; + + if (on_rq) { avruntime = avg_vruntime(cfs_rq); se->vlag = entity_lag(cfs_rq, se, avruntime); se->deadline -= avruntime; @@ -4683,46 +4657,90 @@ static void reweight_entity(struct cfs_r rel_vprot = true; } - cfs_rq->nr_queued--; + cfs_rq->h_nr_queued--; if (!curr) __dequeue_entity(cfs_rq, se); - update_load_sub(&cfs_rq->load, se->load.weight); } - dequeue_load_avg(cfs_rq, se); rescale_entity(se, weight, rel_vprot); - update_load_set(&se->load, weight); + update_load_set(&se->h_load, weight); - do { - u32 divider = get_pelt_divider(&se->avg); - se->avg.load_avg = div_u64(se_weight(se) * se->avg.load_sum, divider); - } while (0); - - enqueue_load_avg(cfs_rq, se); - if (se->on_rq) { + if (on_rq) { if (rel_vprot) se->vprot += avruntime; se->deadline += avruntime; se->rel_deadline = 0; se->vruntime = avruntime - se->vlag; - update_load_add(&cfs_rq->load, se->load.weight); if (!curr) __enqueue_entity(cfs_rq, se); - cfs_rq->nr_queued++; + cfs_rq->h_nr_queued++; } } +static void reweight_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, + unsigned long weight) +{ + if (se->load.weight == weight) + return; + + if (se->on_rq) { + WARN_ON_ONCE(cfs_rq != cfs_rq_of(se)); + update_load_sub(&cfs_rq->load, se->load.weight); + } + dequeue_load_avg(cfs_rq, se); + + update_load_set(&se->load, weight); + + do { + u32 divider = get_pelt_divider(&se->avg); + se->avg.load_avg = div_u64(se_weight(se) * se->avg.load_sum, divider); + } while (0); + + enqueue_load_avg(cfs_rq, se); + + if (se->on_rq) + update_load_add(&cfs_rq->load, se->load.weight); +} + +/* + * weight = NICE_0_LOAD; + * for_each_entity_se(se) + * weight = __calc_prop_weight(cfs_rq_of(se), se, weight); + */ +static __always_inline +unsigned long __calc_prop_weight(struct cfs_rq *cfs_rq, struct sched_entity *se, + unsigned long weight) +{ + weight *= se->load.weight; + if (parent_entity(se)) + weight /= cfs_rq->load.weight; + else + weight /= NICE_0_LOAD; + + return max(weight, MIN_SHARES); +} + static void reweight_task_fair(struct rq *rq, struct task_struct *p, const struct load_weight *lw) { struct sched_entity *se = &p->se; - struct cfs_rq *cfs_rq = cfs_rq_of(se); - struct load_weight *load = &se->load; + unsigned long weight = NICE_0_LOAD; - reweight_entity(cfs_rq, se, lw->weight); - load->inv_weight = lw->inv_weight; + if (se->on_rq) + update_curr_fair(rq); + + reweight_entity(cfs_rq_of(se), se, lw->weight); + se->load.inv_weight = lw->inv_weight; + + if (!se->on_rq) + return; + + for_each_sched_entity(se) + weight = __calc_prop_weight(cfs_rq_of(se), se, weight); + + reweight_eevdf(&rq->cfs, &p->se, weight, p->se.on_rq); } static inline int throttled_hierarchy(struct cfs_rq *cfs_rq); @@ -4958,8 +4976,7 @@ static void update_cfs_group(struct sche return; shares = static_call(calc_group_shares)(gcfs_rq); - if (unlikely(se->load.weight != shares)) - reweight_entity(cfs_rq_of(se), se, shares); + reweight_entity(cfs_rq_of(se), se, shares); } #else /* !CONFIG_FAIR_GROUP_SCHED: */ @@ -5077,7 +5094,7 @@ static inline bool cfs_rq_is_decayed(str * differential update where we store the last value we propagated. This in * turn allows skipping updates if the differential is 'small'. * - * Updating tg's load_avg is necessary before update_cfs_share(). + * Updating tg's load_avg is necessary before update_cfs_group(). */ static inline void update_tg_load_avg(struct cfs_rq *cfs_rq) { @@ -5544,7 +5561,7 @@ static void migrate_se_pelt_lag(struct s * The cfs_rq avg is the direct sum of all its entities (blocked and runnable) * avg. The immediate corollary is that all (fair) tasks must be attached. * - * cfs_rq->avg is used for task_h_load() and update_cfs_share() for example. + * cfs_rq->avg is used for task_h_load() and update_cfs_group() for example. * * Return: true if the load decayed or we removed load. * @@ -6082,6 +6099,7 @@ static void place_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int flags) { u64 vslice, vruntime = avg_vruntime(cfs_rq); + unsigned int nr_queued = cfs_rq->h_nr_queued; bool update_zero = false; s64 lag = 0; @@ -6089,6 +6107,9 @@ place_entity(struct cfs_rq *cfs_rq, stru se->slice = sysctl_sched_base_slice; vslice = calc_delta_fair(se->slice, se); + if (flags & ENQUEUE_QUEUED) + nr_queued -= 1; + /* * Due to how V is constructed as the weighted average of entities, * adding tasks with positive lag, or removing tasks with negative lag @@ -6097,7 +6118,7 @@ place_entity(struct cfs_rq *cfs_rq, stru * * EEVDF: placement strategy #1 / #2 */ - if (sched_feat(PLACE_LAG) && cfs_rq->nr_queued && se->vlag) { + if (sched_feat(PLACE_LAG) && nr_queued && se->vlag) { struct sched_entity *curr = cfs_rq->curr; long load, weight; @@ -6157,9 +6178,9 @@ place_entity(struct cfs_rq *cfs_rq, stru */ load = cfs_rq->sum_weight; if (curr && curr->on_rq) - load += avg_vruntime_weight(cfs_rq, curr->load.weight); + load += avg_vruntime_weight(cfs_rq, curr->h_load.weight); - weight = avg_vruntime_weight(cfs_rq, se->load.weight); + weight = avg_vruntime_weight(cfs_rq, se->h_load.weight); lag *= load + weight; if (WARN_ON_ONCE(!load)) load = 1; @@ -6218,22 +6239,8 @@ static void check_enqueue_throttle(struc static inline int cfs_rq_throttled(struct cfs_rq *cfs_rq); static void -requeue_delayed_entity(struct sched_entity *se); - -static void enqueue_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int flags) { - bool curr = cfs_rq->curr == se; - - /* - * If we're the current task, we must renormalise before calling - * update_curr(). - */ - if (curr) - place_entity(cfs_rq, se, flags); - - update_curr(cfs_rq); - /* * When enqueuing a sched_entity, we must: * - Update loads to have both entity and cfs_rq synced with now. @@ -6252,13 +6259,6 @@ enqueue_entity(struct cfs_rq *cfs_rq, st */ update_cfs_group(se); - /* - * XXX now that the entity has been re-weighted, and it's lag adjusted, - * we can place the entity. - */ - if (!curr) - place_entity(cfs_rq, se, flags); - account_entity_enqueue(cfs_rq, se); /* Entity has migrated, no longer consider this task hot */ @@ -6267,8 +6267,6 @@ enqueue_entity(struct cfs_rq *cfs_rq, st check_schedstat_required(); update_stats_enqueue_fair(cfs_rq, se, flags); - if (!curr) - __enqueue_entity(cfs_rq, se); se->on_rq = 1; if (cfs_rq->nr_queued == 1) { @@ -6286,21 +6284,19 @@ enqueue_entity(struct cfs_rq *cfs_rq, st } } -static void __clear_buddies_next(struct sched_entity *se) +static void set_next_buddy(struct cfs_rq *cfs_rq, struct sched_entity *se) { - for_each_sched_entity(se) { - struct cfs_rq *cfs_rq = cfs_rq_of(se); - if (cfs_rq->next != se) - break; - - cfs_rq->next = NULL; - } + if (WARN_ON_ONCE(!se->on_rq || se->sched_delayed)) + return; + if (se_is_idle(se)) + return; + cfs_rq->next = se; } static void clear_buddies(struct cfs_rq *cfs_rq, struct sched_entity *se) { if (cfs_rq->next == se) - __clear_buddies_next(se); + cfs_rq->next = NULL; } static __always_inline void return_cfs_rq_runtime(struct cfs_rq *cfs_rq); @@ -6311,7 +6307,7 @@ static void set_delayed(struct sched_ent /* * Delayed se of cfs_rq have no tasks queued on them. - * Do not adjust h_nr_runnable since dequeue_entities() + * Do not adjust h_nr_runnable since __dequeue_task() * will account it for blocked tasks. */ if (!entity_is_task(se)) @@ -6344,45 +6340,16 @@ static void clear_delayed(struct sched_e } } -static bool +static void dequeue_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int flags) { - bool sleep = flags & DEQUEUE_SLEEP; - int action = 0; - - update_curr(cfs_rq); - clear_buddies(cfs_rq, se); - - if (flags & DEQUEUE_DELAYED) { - WARN_ON_ONCE(!se->sched_delayed); - } else { - bool delay = sleep; - /* - * DELAY_DEQUEUE relies on spurious wakeups, special task - * states must not suffer spurious wakeups, excempt them. - */ - if (flags & (DEQUEUE_SPECIAL | DEQUEUE_THROTTLE)) - delay = false; - - WARN_ON_ONCE(delay && se->sched_delayed); + int action = UPDATE_TG; - if (sched_feat(DELAY_DEQUEUE) && delay && - !entity_eligible(cfs_rq, se)) { - if (entity_is_task(se)) - action |= UPDATE_UTIL_EST; - update_load_avg(cfs_rq, se, action); - update_entity_lag(cfs_rq, se); - set_delayed(se); - return false; - } - } - - action = UPDATE_TG; if (entity_is_task(se)) { if (task_on_rq_migrating(task_of(se))) action |= DO_DETACH; - if (sleep && !(flags & DEQUEUE_DELAYED)) + if ((flags & DEQUEUE_SLEEP) && !(flags & DEQUEUE_DELAYED)) action |= UPDATE_UTIL_EST; } @@ -6400,14 +6367,6 @@ dequeue_entity(struct cfs_rq *cfs_rq, st update_stats_dequeue_fair(cfs_rq, se, flags); - update_entity_lag(cfs_rq, se); - if (sched_feat(PLACE_REL_DEADLINE) && !sleep) { - se->deadline -= se->vruntime; - se->rel_deadline = 1; - } - - if (se != cfs_rq->curr) - __dequeue_entity(cfs_rq, se); se->on_rq = 0; account_entity_dequeue(cfs_rq, se); @@ -6416,9 +6375,6 @@ dequeue_entity(struct cfs_rq *cfs_rq, st update_cfs_group(se); - if (flags & DEQUEUE_DELAYED) - clear_delayed(se); - if (cfs_rq->nr_queued == 0) { update_idle_cfs_rq_clock_pelt(cfs_rq); #ifdef CONFIG_CFS_BANDWIDTH @@ -6431,15 +6387,11 @@ dequeue_entity(struct cfs_rq *cfs_rq, st } #endif } - - return true; } static void -set_next_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, bool first) +set_next_entity(struct cfs_rq *cfs_rq, struct sched_entity *se) { - clear_buddies(cfs_rq, se); - /* 'current' is not kept within the tree. */ if (se->on_rq) { /* @@ -6448,16 +6400,12 @@ set_next_entity(struct cfs_rq *cfs_rq, s * runqueue. */ update_stats_wait_end_fair(cfs_rq, se); - __dequeue_entity(cfs_rq, se); update_load_avg(cfs_rq, se, UPDATE_TG); - - if (first) - set_protect_slice(cfs_rq, se); } update_stats_curr_start(cfs_rq, se); - WARN_ON_ONCE(cfs_rq->curr); - cfs_rq->curr = se; + WARN_ON_ONCE(cfs_rq->h_curr); + cfs_rq->h_curr = se; /* * Track our maximum slice length, if the CPU's load is at @@ -6477,23 +6425,17 @@ set_next_entity(struct cfs_rq *cfs_rq, s se->prev_sum_exec_runtime = se->sum_exec_runtime; } -static int dequeue_entities(struct rq *rq, struct sched_entity *se, int flags); +static bool __dequeue_task(struct rq *rq, struct task_struct *p, int flags); -/* - * Pick the next process, keeping these things in mind, in this order: - * 1) keep things fair between processes/task groups - * 2) pick the "next" process, since someone really wants that to run - * 3) pick the "last" process, for cache locality - * 4) do not run the "skip" process, if something else is available - */ static struct sched_entity * -pick_next_entity(struct rq *rq, struct cfs_rq *cfs_rq, bool protect) +pick_next_entity(struct rq *rq, bool protect) { + struct cfs_rq *cfs_rq = &rq->cfs; struct sched_entity *se; se = pick_eevdf(cfs_rq, protect); if (se->sched_delayed) { - dequeue_entities(rq, se, DEQUEUE_SLEEP | DEQUEUE_DELAYED); + __dequeue_task(rq, task_of(se), DEQUEUE_SLEEP | DEQUEUE_DELAYED); /* * Must not reference @se again, see __block_task(). */ @@ -6513,13 +6455,11 @@ static void put_prev_entity(struct cfs_r if (prev->on_rq) { update_stats_wait_start_fair(cfs_rq, prev); - /* Put 'current' back into the tree. */ - __enqueue_entity(cfs_rq, prev); /* in !on_rq case, update occurred at dequeue */ update_load_avg(cfs_rq, prev, 0); } - WARN_ON_ONCE(cfs_rq->curr != prev); - cfs_rq->curr = NULL; + WARN_ON_ONCE(cfs_rq->h_curr != prev); + cfs_rq->h_curr = NULL; } static void @@ -7074,7 +7014,7 @@ void unthrottle_cfs_rq(struct cfs_rq *cf assert_list_leaf_cfs_rq(rq); /* Determine whether we need to wake up potentially idle CPU: */ - if (rq->curr == rq->idle && rq->cfs.nr_queued) + if (rq->curr == rq->idle && rq->cfs.h_nr_queued) resched_curr(rq); } @@ -7409,7 +7349,7 @@ static void check_enqueue_throttle(struc return; /* an active group must be handled by the update_curr() path */ - if (!cfs_rq->runtime_enabled || cfs_rq->curr) + if (!cfs_rq->runtime_enabled || cfs_rq->h_curr) return; /* ensure the group is not already throttled */ @@ -7781,7 +7721,7 @@ static void hrtick_start_fair(struct rq resched_curr(rq); return; } - delta = (se->load.weight * vdelta) / NICE_0_LOAD; + delta = (se->h_load.weight * vdelta) / NICE_0_LOAD; /* * Correct for instantaneous load of other classes. @@ -7881,10 +7821,8 @@ static int choose_idle_cpu(int cpu, stru } static void -requeue_delayed_entity(struct sched_entity *se) +requeue_delayed_entity(struct cfs_rq *cfs_rq, struct sched_entity *se) { - struct cfs_rq *cfs_rq = cfs_rq_of(se); - /* * se->sched_delayed should imply: se->on_rq == 1. * Because a delayed entity is one that is still on @@ -7894,19 +7832,58 @@ requeue_delayed_entity(struct sched_enti WARN_ON_ONCE(!se->on_rq); if (update_entity_lag(cfs_rq, se)) { - cfs_rq->nr_queued--; + cfs_rq->h_nr_queued--; if (se != cfs_rq->curr) __dequeue_entity(cfs_rq, se); place_entity(cfs_rq, se, 0); if (se != cfs_rq->curr) __enqueue_entity(cfs_rq, se); - cfs_rq->nr_queued++; + cfs_rq->h_nr_queued++; } update_load_avg(cfs_rq, se, 0); clear_delayed(se); } +static unsigned long enqueue_hierarchy(struct task_struct *p, int flags) +{ + unsigned long weight = NICE_0_LOAD; + int task_new = !(flags & ENQUEUE_WAKEUP); + struct sched_entity *se = &p->se; + int h_nr_idle = task_has_idle_policy(p); + int h_nr_runnable = 1; + + if (task_new && se->sched_delayed) + h_nr_runnable = 0; + + for_each_sched_entity(se) { + struct cfs_rq *cfs_rq = cfs_rq_of(se); + + update_curr(cfs_rq); + + if (!se->on_rq) { + enqueue_entity(cfs_rq, se, flags); + } else { + update_load_avg(cfs_rq, se, UPDATE_TG); + se_update_runnable(se); + update_cfs_group(se); + } + + cfs_rq->h_nr_runnable += h_nr_runnable; + cfs_rq->h_nr_queued++; + cfs_rq->h_nr_idle += h_nr_idle; + + if (cfs_rq_is_idle(cfs_rq)) + h_nr_idle = 1; + + weight = __calc_prop_weight(cfs_rq, se, weight); + + flags = ENQUEUE_WAKEUP; + } + + return weight; +} + /* * The enqueue_task method is called before nr_running is * increased. Here we update the fair scheduling stats and @@ -7915,13 +7892,12 @@ requeue_delayed_entity(struct sched_enti static void enqueue_task_fair(struct rq *rq, struct task_struct *p, int flags) { - struct cfs_rq *cfs_rq; - struct sched_entity *se = &p->se; - int h_nr_idle = task_has_idle_policy(p); - int h_nr_runnable = 1; - int task_new = !(flags & ENQUEUE_WAKEUP); int rq_h_nr_queued = rq->cfs.h_nr_queued; - u64 slice = 0; + int task_new = !(flags & ENQUEUE_WAKEUP); + struct sched_entity *se = &p->se; + struct cfs_rq *cfs_rq = &rq->cfs; + unsigned long weight; + bool curr; if (task_is_throttled(p) && enqueue_throttled_task(p)) return; @@ -7933,10 +7909,10 @@ enqueue_task_fair(struct rq *rq, struct * estimated utilization, before we update schedutil. */ if (!p->se.sched_delayed || (flags & ENQUEUE_DELAYED)) - util_est_enqueue(&rq->cfs, p); + util_est_enqueue(cfs_rq, p); if (flags & ENQUEUE_DELAYED) { - requeue_delayed_entity(se); + requeue_delayed_entity(cfs_rq, se); return; } @@ -7948,57 +7924,22 @@ enqueue_task_fair(struct rq *rq, struct if (p->in_iowait) cpufreq_update_util(rq, SCHED_CPUFREQ_IOWAIT); - if (task_new && se->sched_delayed) - h_nr_runnable = 0; - - for_each_sched_entity(se) { - if (se->on_rq) { - if (se->sched_delayed) - requeue_delayed_entity(se); - break; - } - cfs_rq = cfs_rq_of(se); - - /* - * Basically set the slice of group entries to the min_slice of - * their respective cfs_rq. This ensures the group can service - * its entities in the desired time-frame. - */ - if (slice) { - se->slice = slice; - se->custom_slice = 1; - } - enqueue_entity(cfs_rq, se, flags); - slice = cfs_rq_min_slice(cfs_rq); - - cfs_rq->h_nr_runnable += h_nr_runnable; - cfs_rq->h_nr_queued++; - cfs_rq->h_nr_idle += h_nr_idle; - - if (cfs_rq_is_idle(cfs_rq)) - h_nr_idle = 1; - - flags = ENQUEUE_WAKEUP; - } - - for_each_sched_entity(se) { - cfs_rq = cfs_rq_of(se); + /* + * XXX comment on the curr thing + */ + curr = (cfs_rq->curr == se); + if (curr) + place_entity(cfs_rq, se, flags); - update_load_avg(cfs_rq, se, UPDATE_TG); - se_update_runnable(se); - update_cfs_group(se); + if (se->on_rq && se->sched_delayed) + requeue_delayed_entity(cfs_rq, se); - se->slice = slice; - if (se != cfs_rq->curr) - min_vruntime_cb_propagate(&se->run_node, NULL); - slice = cfs_rq_min_slice(cfs_rq); + weight = enqueue_hierarchy(p, flags); - cfs_rq->h_nr_runnable += h_nr_runnable; - cfs_rq->h_nr_queued++; - cfs_rq->h_nr_idle += h_nr_idle; - - if (cfs_rq_is_idle(cfs_rq)) - h_nr_idle = 1; + if (!curr) { + reweight_eevdf(cfs_rq, se, weight, false); + place_entity(cfs_rq, se, flags | ENQUEUE_QUEUED); + __enqueue_entity(cfs_rq, se); } if (!rq_h_nr_queued && rq->cfs.h_nr_queued) @@ -8029,105 +7970,109 @@ enqueue_task_fair(struct rq *rq, struct hrtick_update(rq); } -/* - * Basically dequeue_task_fair(), except it can deal with dequeue_entity() - * failing half-way through and resume the dequeue later. - * - * Returns: - * -1 - dequeue delayed - * 0 - dequeue throttled - * 1 - dequeue complete - */ -static int dequeue_entities(struct rq *rq, struct sched_entity *se, int flags) +static void dequeue_hierarchy(struct task_struct *p, int flags) { - bool was_sched_idle = sched_idle_rq(rq); + struct sched_entity *se = &p->se; bool task_sleep = flags & DEQUEUE_SLEEP; bool task_delayed = flags & DEQUEUE_DELAYED; bool task_throttled = flags & DEQUEUE_THROTTLE; - struct task_struct *p = NULL; - int h_nr_idle = 0; - int h_nr_queued = 0; int h_nr_runnable = 0; - struct cfs_rq *cfs_rq; - u64 slice = 0; + int h_nr_idle = task_has_idle_policy(p); + bool dequeue = true; - if (entity_is_task(se)) { - p = task_of(se); - h_nr_queued = 1; - h_nr_idle = task_has_idle_policy(p); - if (task_sleep || task_delayed || !se->sched_delayed) - h_nr_runnable = 1; - } + if (task_sleep || task_delayed || !se->sched_delayed) + h_nr_runnable = 1; for_each_sched_entity(se) { - cfs_rq = cfs_rq_of(se); + struct cfs_rq *cfs_rq = cfs_rq_of(se); - if (!dequeue_entity(cfs_rq, se, flags)) { - if (p && &p->se == se) - return -1; + update_curr(cfs_rq); - slice = cfs_rq_min_slice(cfs_rq); - break; + if (dequeue) { + dequeue_entity(cfs_rq, se, flags); + /* Don't dequeue parent if it has other entities besides us */ + if (cfs_rq->load.weight) + dequeue = false; + } else { + update_load_avg(cfs_rq, se, UPDATE_TG); + se_update_runnable(se); + update_cfs_group(se); } cfs_rq->h_nr_runnable -= h_nr_runnable; - cfs_rq->h_nr_queued -= h_nr_queued; + cfs_rq->h_nr_queued--; cfs_rq->h_nr_idle -= h_nr_idle; if (cfs_rq_is_idle(cfs_rq)) - h_nr_idle = h_nr_queued; + h_nr_idle = 1; if (throttled_hierarchy(cfs_rq) && task_throttled) record_throttle_clock(cfs_rq); - /* Don't dequeue parent if it has other entities besides us */ - if (cfs_rq->load.weight) { - slice = cfs_rq_min_slice(cfs_rq); - - /* Avoid re-evaluating load for this entity: */ - se = parent_entity(se); - /* - * Bias pick_next to pick a task from this cfs_rq, as - * p is sleeping when it is within its sched_slice. - */ - if (task_sleep && se) - set_next_buddy(se); - break; - } flags |= DEQUEUE_SLEEP; flags &= ~(DEQUEUE_DELAYED | DEQUEUE_SPECIAL); } +} - for_each_sched_entity(se) { - cfs_rq = cfs_rq_of(se); +/* + * The part of dequeue_task_fair() that is needed to dequeue delayed tasks. + * + * Returns: + * true - dequeued + * false - delayed + */ +static bool __dequeue_task(struct rq *rq, struct task_struct *p, int flags) +{ + struct sched_entity *se = &p->se; + struct cfs_rq *cfs_rq = &rq->cfs; + bool was_sched_idle = sched_idle_rq(rq); + bool task_sleep = flags & DEQUEUE_SLEEP; + bool task_delayed = flags & DEQUEUE_DELAYED; - update_load_avg(cfs_rq, se, UPDATE_TG); - se_update_runnable(se); - update_cfs_group(se); + clear_buddies(cfs_rq, se); - se->slice = slice; - if (se != cfs_rq->curr) - min_vruntime_cb_propagate(&se->run_node, NULL); - slice = cfs_rq_min_slice(cfs_rq); + update_curr(cfs_rq_of(se)); + update_entity_lag(cfs_rq, se); - cfs_rq->h_nr_runnable -= h_nr_runnable; - cfs_rq->h_nr_queued -= h_nr_queued; - cfs_rq->h_nr_idle -= h_nr_idle; + if (flags & DEQUEUE_DELAYED) { + WARN_ON_ONCE(!se->sched_delayed); + } else { + bool delay = task_sleep; + /* + * DELAY_DEQUEUE relies on spurious wakeups, special task + * states must not suffer spurious wakeups, excempt them. + */ + if (flags & (DEQUEUE_SPECIAL | DEQUEUE_THROTTLE)) + delay = false; - if (cfs_rq_is_idle(cfs_rq)) - h_nr_idle = h_nr_queued; + WARN_ON_ONCE(delay && se->sched_delayed); - if (throttled_hierarchy(cfs_rq) && task_throttled) - record_throttle_clock(cfs_rq); + if (sched_feat(DELAY_DEQUEUE) && delay && + !entity_eligible(cfs_rq, se)) { + update_load_avg(cfs_rq_of(se), se, UPDATE_UTIL_EST); + set_delayed(se); + return false; + } } - sub_nr_running(rq, h_nr_queued); + dequeue_hierarchy(p, flags); + + if (sched_feat(PLACE_REL_DEADLINE) && !task_sleep) { + se->deadline -= se->vruntime; + se->rel_deadline = 1; + } + if (se != cfs_rq->curr) + __dequeue_entity(cfs_rq, se); + + sub_nr_running(rq, 1); /* balance early to pull high priority tasks */ if (unlikely(!was_sched_idle && sched_idle_rq(rq))) rq->next_balance = jiffies; - if (p && task_delayed) { + if (task_delayed) { + clear_delayed(se); + WARN_ON_ONCE(!task_sleep); WARN_ON_ONCE(p->on_rq != 1); @@ -8139,7 +8084,7 @@ static int dequeue_entities(struct rq *r __block_task(rq, p); } - return 1; + return true; } /* @@ -8157,11 +8102,11 @@ static bool dequeue_task_fair(struct rq if (!p->se.sched_delayed) util_est_dequeue(&rq->cfs, p); - if (dequeue_entities(rq, &p->se, flags) < 0) + if (!__dequeue_task(rq, p, flags)) return false; /* - * Must not reference @p after dequeue_entities(DEQUEUE_DELAYED). + * Must not reference @p after __dequeue_task(DEQUEUE_DELAYED). */ return true; } @@ -9749,19 +9694,6 @@ static void migrate_task_rq_fair(struct static void task_dead_fair(struct task_struct *p) { struct sched_entity *se = &p->se; - - if (se->sched_delayed) { - struct rq_flags rf; - struct rq *rq; - - rq = task_rq_lock(p, &rf); - if (se->sched_delayed) { - update_rq_clock(rq); - dequeue_entities(rq, se, DEQUEUE_SLEEP | DEQUEUE_DELAYED); - } - task_rq_unlock(rq, p, &rf); - } - remove_entity_load_avg(se); } @@ -9795,21 +9727,10 @@ static void set_cpus_allowed_fair(struct set_task_max_allowed_capacity(p); } -static void set_next_buddy(struct sched_entity *se) -{ - for_each_sched_entity(se) { - if (WARN_ON_ONCE(!se->on_rq)) - return; - if (se_is_idle(se)) - return; - cfs_rq_of(se)->next = se; - } -} - enum preempt_wakeup_action { PREEMPT_WAKEUP_NONE, /* No preemption. */ PREEMPT_WAKEUP_SHORT, /* Ignore slice protection. */ - PREEMPT_WAKEUP_PICK, /* Let __pick_eevdf() decide. */ + PREEMPT_WAKEUP_PICK, /* Let pick_eevdf() decide. */ PREEMPT_WAKEUP_RESCHED, /* Force reschedule. */ }; @@ -9826,7 +9747,7 @@ set_preempt_buddy(struct cfs_rq *cfs_rq, if (cfs_rq->next && entity_before(cfs_rq->next, pse)) return false; - set_next_buddy(pse); + set_next_buddy(cfs_rq, pse); return true; } @@ -9879,7 +9800,7 @@ static void wakeup_preempt_fair(struct r enum preempt_wakeup_action preempt_action = PREEMPT_WAKEUP_PICK; struct task_struct *donor = rq->donor; struct sched_entity *nse, *se = &donor->se, *pse = &p->se; - struct cfs_rq *cfs_rq = task_cfs_rq(donor); + struct cfs_rq *cfs_rq = &rq->cfs; int cse_is_idle, pse_is_idle; /* @@ -9916,7 +9837,6 @@ static void wakeup_preempt_fair(struct r if (!sched_feat(WAKEUP_PREEMPTION)) return; - find_matching_se(&se, &pse); WARN_ON_ONCE(!pse); cse_is_idle = se_is_idle(se); @@ -9944,8 +9864,7 @@ static void wakeup_preempt_fair(struct r if (unlikely(!normal_policy(p->policy))) return; - cfs_rq = cfs_rq_of(se); - update_curr(cfs_rq); + update_curr_fair(rq); /* * If @p has a shorter slice than current and @p is eligible, override * current's slice protection in order to allow preemption. @@ -9989,18 +9908,15 @@ static void wakeup_preempt_fair(struct r } pick: - nse = pick_next_entity(rq, cfs_rq, preempt_action != PREEMPT_WAKEUP_SHORT); - /* If @p has become the most eligible task, force preemption */ - if (nse == pse) - goto preempt; - - /* - * Because p is enqueued, nse being null can only mean that we - * dequeued a delayed task. If there are still entities queued in - * cfs, check if the next one will be p. - */ - if (!nse && cfs_rq->nr_queued) - goto pick; + if (cfs_rq->h_nr_queued) { + nse = pick_next_entity(rq, preempt_action != PREEMPT_WAKEUP_SHORT); + if (unlikely(!nse)) + goto pick; + + /* If @p has become the most eligible task, force preemption */ + if (nse == pse) + goto preempt; + } if (sched_feat(RUN_TO_PARITY)) update_protect_slice(cfs_rq, se); @@ -10019,33 +9935,24 @@ static void wakeup_preempt_fair(struct r struct task_struct *pick_task_fair(struct rq *rq, struct rq_flags *rf) __must_hold(__rq_lockp(rq)) { + struct cfs_rq *cfs_rq = &rq->cfs; struct sched_entity *se; - struct cfs_rq *cfs_rq; struct task_struct *p; - bool throttled; int new_tasks; again: - cfs_rq = &rq->cfs; - if (!cfs_rq->nr_queued) + if (!cfs_rq->h_nr_queued) goto idle; - throttled = false; - - do { - /* Might not have done put_prev_entity() */ - if (cfs_rq->curr && cfs_rq->curr->on_rq) - update_curr(cfs_rq); + /* Might not have done put_prev_entity() */ + if (cfs_rq->curr && cfs_rq->curr->on_rq) + update_curr(cfs_rq); - se = pick_next_entity(rq, cfs_rq, true); - if (!se) - goto again; - cfs_rq = group_cfs_rq(se); - } while (cfs_rq); + se = pick_next_entity(rq, true); + if (!se) + goto again; p = task_of(se); - if (unlikely(throttled)) - task_throttle_setup_work(p); return p; idle: @@ -10079,7 +9986,7 @@ void fair_server_init(struct rq *rq) static void put_prev_task_fair(struct rq *rq, struct task_struct *prev, struct task_struct *next) { struct sched_entity *se = &prev->se; - struct cfs_rq *cfs_rq; + struct cfs_rq *cfs_rq = &rq->cfs; struct sched_entity *nse = NULL; #ifdef CONFIG_FAIR_GROUP_SCHED @@ -10089,7 +9996,7 @@ static void put_prev_task_fair(struct rq while (se) { cfs_rq = cfs_rq_of(se); - if (!nse || cfs_rq->curr) + if (!nse || cfs_rq->h_curr) put_prev_entity(cfs_rq, se); #ifdef CONFIG_FAIR_GROUP_SCHED if (nse) { @@ -10108,6 +10015,14 @@ static void put_prev_task_fair(struct rq #endif se = parent_entity(se); } + + /* Put 'current' back into the tree. */ + cfs_rq = &rq->cfs; + se = &prev->se; + WARN_ON_ONCE(cfs_rq->curr != se); + cfs_rq->curr = NULL; + if (se->on_rq) + __enqueue_entity(cfs_rq, se); } /* @@ -10116,8 +10031,8 @@ static void put_prev_task_fair(struct rq static void yield_task_fair(struct rq *rq) { struct task_struct *curr = rq->donor; - struct cfs_rq *cfs_rq = task_cfs_rq(curr); struct sched_entity *se = &curr->se; + struct cfs_rq *cfs_rq = &rq->cfs; /* * Are we the only task in the tree? @@ -10158,11 +10073,11 @@ static bool yield_to_task_fair(struct rq struct sched_entity *se = &p->se; /* !se->on_rq also covers throttled task */ - if (!se->on_rq) + if (!se->on_rq || se->sched_delayed) return false; /* Tell the scheduler that we'd really like se to run next. */ - set_next_buddy(se); + set_next_buddy(&task_rq(p)->cfs, se); yield_task_fair(rq); @@ -10501,15 +10416,10 @@ static inline long migrate_degrades_loca */ static inline int task_is_ineligible_on_dst_cpu(struct task_struct *p, int dest_cpu) { - struct cfs_rq *dst_cfs_rq; + struct cfs_rq *dst_cfs_rq = &cpu_rq(dest_cpu)->cfs; -#ifdef CONFIG_FAIR_GROUP_SCHED - dst_cfs_rq = tg_cfs_rq(task_group(p), dest_cpu); -#else - dst_cfs_rq = &cpu_rq(dest_cpu)->cfs; -#endif - if (sched_feat(PLACE_LAG) && dst_cfs_rq->nr_queued && - !entity_eligible(task_cfs_rq(p), &p->se)) + if (sched_feat(PLACE_LAG) && dst_cfs_rq->h_nr_queued && + !entity_eligible(&task_rq(p)->cfs, &p->se)) return 1; return 0; @@ -11292,7 +11202,7 @@ static void update_cfs_rq_h_load(struct while ((se = READ_ONCE(cfs_rq->h_load_next)) != NULL) { load = cfs_rq->h_load; load = div64_ul(load * se->avg.load_avg, - cfs_rq_load_avg(cfs_rq) + 1); + cfs_rq_load_avg(cfs_rq) + 1); cfs_rq = group_cfs_rq(se); cfs_rq->h_load = load; cfs_rq->last_h_load_update = now; @@ -14684,7 +14594,7 @@ static inline void task_tick_core(struct * MIN_NR_TASKS_DURING_FORCEIDLE - 1 tasks and use that to check * if we need to give up the CPU. */ - if (rq->core->core_forceidle_count && rq->cfs.nr_queued == 1 && + if (rq->core->core_forceidle_count && rq->cfs.h_nr_queued == 1 && __entity_slice_used(&curr->se, MIN_NR_TASKS_DURING_FORCEIDLE)) resched_curr(rq); } @@ -14893,30 +14803,8 @@ bool cfs_prio_less(const struct task_str WARN_ON_ONCE(task_rq(b)->core != rq->core); -#ifdef CONFIG_FAIR_GROUP_SCHED - /* - * Find an se in the hierarchy for tasks a and b, such that the se's - * are immediate siblings. - */ - while (sea->cfs_rq->tg != seb->cfs_rq->tg) { - int sea_depth = sea->depth; - int seb_depth = seb->depth; - - if (sea_depth >= seb_depth) - sea = parent_entity(sea); - if (sea_depth <= seb_depth) - seb = parent_entity(seb); - } - - se_fi_update(sea, rq->core->core_forceidle_seq, in_fi); - se_fi_update(seb, rq->core->core_forceidle_seq, in_fi); - - cfs_rqa = sea->cfs_rq; - cfs_rqb = seb->cfs_rq; -#else /* !CONFIG_FAIR_GROUP_SCHED: */ cfs_rqa = &task_rq(a)->cfs; cfs_rqb = &task_rq(b)->cfs; -#endif /* !CONFIG_FAIR_GROUP_SCHED */ /* * Find delta after normalizing se's vruntime with its cfs_rq's @@ -14955,11 +14843,20 @@ static inline void task_tick_core(struct static void task_tick_fair(struct rq *rq, struct task_struct *curr, int queued) { struct sched_entity *se = &curr->se; - struct cfs_rq *cfs_rq; - for_each_sched_entity(se) { - cfs_rq = cfs_rq_of(se); - entity_tick(cfs_rq, se, queued); + if (se->on_rq) { + unsigned long weight = NICE_0_LOAD; + struct cfs_rq *cfs_rq; + + for_each_sched_entity(se) { + cfs_rq = cfs_rq_of(se); + entity_tick(cfs_rq, se, queued); + + weight = __calc_prop_weight(cfs_rq, se, weight); + } + + se = &curr->se; + reweight_eevdf(cfs_rq, se, weight, se->on_rq); } if (queued) @@ -14999,7 +14896,7 @@ prio_changed_fair(struct rq *rq, struct if (p->prio == oldprio) return; - if (rq->cfs.nr_queued == 1) + if (rq->cfs.h_nr_queued == 1) return; /* @@ -15128,33 +15025,44 @@ static void switched_to_fair(struct rq * } } -/* - * Account for a task changing its policy or group. - * - * This routine is mostly called to set cfs_rq->curr field when a task - * migrates between groups/classes. - */ static void set_next_task_fair(struct rq *rq, struct task_struct *p, bool first) { struct sched_entity *se = &p->se; bool throttled = false; + struct cfs_rq *cfs_rq = &rq->cfs; + unsigned long weight = NICE_0_LOAD; + bool on_rq = se->on_rq; + + clear_buddies(cfs_rq, se); + + if (on_rq) + __dequeue_entity(cfs_rq, se); for_each_sched_entity(se) { - struct cfs_rq *cfs_rq = cfs_rq_of(se); + cfs_rq = cfs_rq_of(se); - if (IS_ENABLED(CONFIG_FAIR_GROUP_SCHED) && - first && cfs_rq->curr) - break; + if (!IS_ENABLED(CONFIG_FAIR_GROUP_SCHED) || + !first || !cfs_rq->h_curr) + set_next_entity(cfs_rq, se); - set_next_entity(cfs_rq, se, first); /* ensure bandwidth has been allocated on our new cfs_rq */ throttled |= account_cfs_rq_runtime(cfs_rq, 0); + + if (on_rq) + weight = __calc_prop_weight(cfs_rq, se, weight); } if (throttled) task_throttle_setup_work(p); se = &p->se; + cfs_rq->curr = se; + + if (on_rq) { + reweight_eevdf(cfs_rq, se, weight, se->on_rq); + if (first) + set_protect_slice(cfs_rq, se); + } if (task_on_rq_queued(p)) { /* @@ -15267,17 +15175,8 @@ void unregister_fair_sched_group(struct struct sched_entity *se = tg_se(tg, cpu); struct rq *rq = cpu_rq(cpu); - if (se) { - if (se->sched_delayed) { - guard(rq_lock_irqsave)(rq); - if (se->sched_delayed) { - update_rq_clock(rq); - dequeue_entities(rq, se, DEQUEUE_SLEEP | DEQUEUE_DELAYED); - } - list_del_leaf_cfs_rq(cfs_rq); - } + if (se) remove_entity_load_avg(se); - } /* * Only empty task groups can be destroyed; so we can speculatively --- a/kernel/sched/pelt.c +++ b/kernel/sched/pelt.c @@ -206,7 +206,7 @@ ___update_load_sum(u64 now, struct sched /* * running is a subset of runnable (weight) so running can't be set if * runnable is clear. But there are some corner cases where the current - * se has been already dequeued but cfs_rq->curr still points to it. + * se has been already dequeued but cfs_rq->h_curr still points to it. * This means that weight will be 0 but not running for a sched_entity * but also for a cfs_rq if the latter becomes idle. As an example, * this happens during sched_balance_newidle() which calls @@ -307,7 +307,7 @@ int __update_load_avg_blocked_se(u64 now int __update_load_avg_se(u64 now, struct cfs_rq *cfs_rq, struct sched_entity *se) { if (___update_load_sum(now, &se->avg, !!se->on_rq, se_runnable(se), - cfs_rq->curr == se)) { + cfs_rq->h_curr == se)) { ___update_load_avg(&se->avg, se_weight(se)); cfs_se_util_change(&se->avg); @@ -323,7 +323,7 @@ int __update_load_avg_cfs_rq(u64 now, st if (___update_load_sum(now, &cfs_rq->avg, scale_load_down(cfs_rq->load.weight), cfs_rq->h_nr_runnable, - cfs_rq->curr != NULL)) { + cfs_rq->h_curr != NULL)) { ___update_load_avg(&cfs_rq->avg, 1); trace_pelt_cfs_tp(cfs_rq); --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -530,21 +530,8 @@ struct task_group { }; -#ifdef CONFIG_GROUP_SCHED_WEIGHT #define ROOT_TASK_GROUP_LOAD NICE_0_LOAD -/* - * A weight of 0 or 1 can cause arithmetics problems. - * A weight of a cfs_rq is the sum of weights of which entities - * are queued on this cfs_rq, so a weight of a entity should not be - * too large, so as the shares value of a task group. - * (The default weight is 1024 - so there's no practical - * limitation from this.) - */ -#define MIN_SHARES (1UL << 1) -#define MAX_SHARES (1UL << 18) -#endif - typedef int (*tg_visitor)(struct task_group *, void *); extern int walk_tg_tree_from(struct task_group *from, @@ -631,6 +618,17 @@ static inline bool cfs_task_bw_constrain #endif /* !CONFIG_CGROUP_SCHED */ +/* + * A weight of 0 or 1 can cause arithmetics problems. + * A weight of a cfs_rq is the sum of weights of which entities + * are queued on this cfs_rq, so a weight of a entity should not be + * too large, so as the shares value of a task group. + * (The default weight is 1024 - so there's no practical + * limitation from this.) + */ +#define MIN_SHARES (1UL << 1) +#define MAX_SHARES (1UL << 18) + extern void unregister_rt_sched_group(struct task_group *tg); extern void free_rt_sched_group(struct task_group *tg); extern int alloc_rt_sched_group(struct task_group *tg, struct task_group *parent); @@ -709,6 +707,7 @@ struct cfs_rq { /* * CFS load tracking */ + struct sched_entity *h_curr; struct sched_avg avg; #ifndef CONFIG_64BIT u64 last_update_time_copy; @@ -2575,6 +2574,7 @@ extern const u32 sched_prio_to_wmult[40 #define ENQUEUE_MIGRATED 0x00040000 #define ENQUEUE_INITIAL 0x00080000 #define ENQUEUE_RQ_SELECTED 0x00100000 +#define ENQUEUE_QUEUED 0x00200000 #define RETRY_TASK ((void *)-1UL) ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v3 7/7] sched/eevdf: Move to a single runqueue 2026-06-05 12:40 ` [PATCH v3 7/7] sched/eevdf: Move to a single runqueue Peter Zijlstra @ 2026-06-20 3:54 ` Chen, Yu C 2026-06-26 11:40 ` Peter Zijlstra 2026-10-16 13:18 ` Jakub Okoński 1 sibling, 1 reply; 17+ messages in thread From: Chen, Yu C @ 2026-06-20 3:54 UTC (permalink / raw) To: Peter Zijlstra Cc: longman, chenridong, juri.lelli, vincent.guittot, dietmar.eggemann, rostedt, bsegall, mgorman, vschneid, tj, hannes, mkoutny, cgroups, linux-kernel, jstultz, kprateek.nayak, qyousef, mingo On 6/5/2026 8:40 PM, Peter Zijlstra wrote: > Change fair/cgroup to a single runqueue. > > Infamously fair/cgroup isn't working for a number of people; typically > the complaint is latencies and/or overhead. The latency issue is due > to the intermediate entries that represent a combination of tasks and > thereby obfuscate the runnability of tasks. > > The approach here is to leave the cgroup hierarchy as is; including > the intermediate enqueue/dequeue but move the actual EEVDF runqueue > outside. This means things like the shares_weight approximation are > fully preserved. > > That is, given a hierarchy like: > > R > | > se--G1 > / \ > G2--se se--G3 > / \ | > T1--se se--T2 se--T3 > > This is fully maintained for load tracking, however the EEVDF parts of > cfs_rq/se go unused for the intermediates and are instead connected > like: > > _R_ > / | \ > T1 T2 T3 > > Since the effective weight of the entities is determined by the > hierarchy, this gets recomputed on enqueue,set_next_task and tick. > > Notably, the effective weight (se->h_load) is computed from the > hierarchical fraction: se->load / cfs_rq->load. > > Since EEVDF is now exclusively operating on rq->cfs, it needs to > consider cfs_rq->h_nr_queued rather than cfs_rq->nr_queued. Similarly, > only tasks can get delayed, simplifying some of the cgroup cleanup. > > One place where additional information was required was > set_next_task() / put_prev_task(), where we need to track 'current' > both in the hierarchical sense (cfs_rq->h_curr) and in the flat sense > (cfs_rq->curr). > > As a result of only having a single level to pick from, much of the > complications in pick_next_task() and preemption go away. > > Since many of the hierarchical operations are still there, this won't > immediately fix the performance issues, but hopefully it will fix some > of the latency issues. > > TODO: split struct cfs_rq / struct sched_entity > TODO: try and get rid of h_curr > > Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> A divide-by-zero crash is observed when running hackbench: [14697.488452] CPU: 112 UID: 0 PID: 124791 Comm: hackbench Not tainted 7.1.0-rc2+ [14697.492627] RIP: 0010:propagate_entity_load_avg+0x35f/0x3e0 [14697.506799] <TASK> [14697.507411] __dequeue_task+0x2b4/0xc70 [14697.508677] dequeue_task_fair+0x36/0x370 [14697.509047] dequeue_task+0x101/0x2f0 [14697.509426] __schedule+0x1b1/0x1a00 [14697.510868] anon_pipe_read+0x3da/0x450 [14697.511400] vfs_read+0x361/0x390 [14697.512053] __x64_sys_read+0x19/0x30 The divide-by-zero happens here: if (scale_load_down(gcfs_rq->load.weight)) { load_sum = div_u64(gcfs_rq->avg.load_sum, scale_load_down(gcfs_rq->load.weight)); } gcfs_rq->load.weight is an insane large value and is truncated to the lower 32 bits by div_u64, which happen to be 0. Using AI for investigation, the cause is a u32 overflow in update_tg_cfs_runnable(), and flat pickup became a victim when using tg_tasks(): u32 new_sum, divider; ... new_sum = se->avg.runnable_avg * divider; <-- boom The following sequence shows how this triggers the crash: propagate_entity_load_avg() update_tg_cfs_runnable() # u32 overflow corrupts runnable_sum __update_load_avg_cfs_rq() ___update_load_avg() # computes insane runnable_avg update_tg_load_avg() # propagates to tg->runnable_avg update_cfs_group() calc_concur_shares() tg_tasks() # long-to-int truncation, negative nr reweight_entity() # corrupted se->load.weight update_load_add() # corrupted cfs_rq->load.weight propagate_entity_load_avg() update_tg_cfs_load() div_u64() # divide-by-zero Fix by widening new_sum from u32 to u64(no need to force tg_tasks() to return unsigned long after this fix) Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Chen Yu <yu.c.chen@intel.com> --- kernel/sched/fair.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index d991ea85873a..99ea51448981 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -5305,7 +5305,8 @@ static inline void update_tg_cfs_runnable(struct cfs_rq *cfs_rq, struct sched_entity *se, struct cfs_rq *gcfs_rq) { long delta_sum, delta_avg = gcfs_rq->avg.runnable_avg - se->avg.runnable_avg; - u32 new_sum, divider; + u64 new_sum; + u32 divider; /* Nothing to update */ if (!delta_avg) @@ -5319,7 +5320,7 @@ update_tg_cfs_runnable(struct cfs_rq *cfs_rq, struct sched_entity *se, struct cf /* Set new sched_entity's runnable */ se->avg.runnable_avg = gcfs_rq->avg.runnable_avg; - new_sum = se->avg.runnable_avg * divider; + new_sum = (u64)se->avg.runnable_avg * divider; delta_sum = (long)new_sum - (long)se->avg.runnable_sum; se->avg.runnable_sum = new_sum; -- 2.45.2 ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v3 7/7] sched/eevdf: Move to a single runqueue 2026-06-20 3:54 ` Chen, Yu C @ 2026-06-26 11:40 ` Peter Zijlstra 2026-06-29 14:02 ` Vincent Guittot 0 siblings, 1 reply; 17+ messages in thread From: Peter Zijlstra @ 2026-06-26 11:40 UTC (permalink / raw) To: Chen, Yu C Cc: longman, chenridong, juri.lelli, vincent.guittot, dietmar.eggemann, rostedt, bsegall, mgorman, vschneid, tj, hannes, mkoutny, cgroups, linux-kernel, jstultz, kprateek.nayak, qyousef, mingo On Sat, Jun 20, 2026 at 11:54:22AM +0800, Chen, Yu C wrote: > A divide-by-zero crash is observed when running hackbench: > > [14697.488452] CPU: 112 UID: 0 PID: 124791 Comm: hackbench Not tainted > 7.1.0-rc2+ > [14697.492627] RIP: 0010:propagate_entity_load_avg+0x35f/0x3e0 > [14697.506799] <TASK> > [14697.507411] __dequeue_task+0x2b4/0xc70 > [14697.508677] dequeue_task_fair+0x36/0x370 > [14697.509047] dequeue_task+0x101/0x2f0 > [14697.509426] __schedule+0x1b1/0x1a00 > [14697.510868] anon_pipe_read+0x3da/0x450 > [14697.511400] vfs_read+0x361/0x390 > [14697.512053] __x64_sys_read+0x19/0x30 > > The divide-by-zero happens here: > > if (scale_load_down(gcfs_rq->load.weight)) { > load_sum = div_u64(gcfs_rq->avg.load_sum, > scale_load_down(gcfs_rq->load.weight)); > } > > gcfs_rq->load.weight is an insane large value and is truncated > to the lower 32 bits by div_u64, which happen to be 0. > > Using AI for investigation, the cause is a u32 overflow in > update_tg_cfs_runnable(), and flat pickup became a victim when using > tg_tasks(): > > u32 new_sum, divider; > ... > new_sum = se->avg.runnable_avg * divider; <-- boom > > The following sequence shows how this triggers the crash: > > propagate_entity_load_avg() > update_tg_cfs_runnable() # u32 overflow corrupts runnable_sum > > __update_load_avg_cfs_rq() > ___update_load_avg() # computes insane runnable_avg > update_tg_load_avg() # propagates to tg->runnable_avg > > update_cfs_group() > calc_concur_shares() > tg_tasks() # long-to-int truncation, negative nr > reweight_entity() # corrupted se->load.weight > update_load_add() # corrupted cfs_rq->load.weight > > propagate_entity_load_avg() > update_tg_cfs_load() > div_u64() # divide-by-zero > > Fix by widening new_sum from u32 to u64(no need to force tg_tasks() > to return unsigned long after this fix) > Assisted-by: Claude:claude-opus-4.6 > Signed-off-by: Chen Yu <yu.c.chen@intel.com> > --- > kernel/sched/fair.c | 5 +++-- > 1 file changed, 3 insertions(+), 2 deletions(-) > > diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c > index d991ea85873a..99ea51448981 100644 > --- a/kernel/sched/fair.c > +++ b/kernel/sched/fair.c > @@ -5305,7 +5305,8 @@ static inline void > update_tg_cfs_runnable(struct cfs_rq *cfs_rq, struct sched_entity *se, struct cfs_rq *gcfs_rq) > { > long delta_sum, delta_avg = gcfs_rq->avg.runnable_avg - se->avg.runnable_avg; > - u32 new_sum, divider; > + u64 new_sum; > + u32 divider; > > /* Nothing to update */ > if (!delta_avg) > @@ -5319,7 +5320,7 @@ update_tg_cfs_runnable(struct cfs_rq *cfs_rq, struct sched_entity *se, struct cf > > /* Set new sched_entity's runnable */ > se->avg.runnable_avg = gcfs_rq->avg.runnable_avg; > - new_sum = se->avg.runnable_avg * divider; > + new_sum = (u64)se->avg.runnable_avg * divider; > delta_sum = (long)new_sum - (long)se->avg.runnable_sum; > se->avg.runnable_sum = new_sum; Hmm, nice one. This makes sense because sched_avg::runnable_sum is a u64 itself, so having the delta be one is only sensible. I do wonder though, this doesn't actually look to be specific to flat, it just managed to trip it somehow. I'll stick this on as a separate fix. ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v3 7/7] sched/eevdf: Move to a single runqueue 2026-06-26 11:40 ` Peter Zijlstra @ 2026-06-29 14:02 ` Vincent Guittot 0 siblings, 0 replies; 17+ messages in thread From: Vincent Guittot @ 2026-06-29 14:02 UTC (permalink / raw) To: Peter Zijlstra Cc: Chen, Yu C, longman, chenridong, juri.lelli, dietmar.eggemann, rostedt, bsegall, mgorman, vschneid, tj, hannes, mkoutny, cgroups, linux-kernel, jstultz, kprateek.nayak, qyousef, mingo On Fri, 26 Jun 2026 at 13:40, Peter Zijlstra <peterz@infradead.org> wrote: > > On Sat, Jun 20, 2026 at 11:54:22AM +0800, Chen, Yu C wrote: > > > A divide-by-zero crash is observed when running hackbench: > > > > [14697.488452] CPU: 112 UID: 0 PID: 124791 Comm: hackbench Not tainted > > 7.1.0-rc2+ > > [14697.492627] RIP: 0010:propagate_entity_load_avg+0x35f/0x3e0 > > [14697.506799] <TASK> > > [14697.507411] __dequeue_task+0x2b4/0xc70 > > [14697.508677] dequeue_task_fair+0x36/0x370 > > [14697.509047] dequeue_task+0x101/0x2f0 > > [14697.509426] __schedule+0x1b1/0x1a00 > > [14697.510868] anon_pipe_read+0x3da/0x450 > > [14697.511400] vfs_read+0x361/0x390 > > [14697.512053] __x64_sys_read+0x19/0x30 > > > > The divide-by-zero happens here: > > > > if (scale_load_down(gcfs_rq->load.weight)) { > > load_sum = div_u64(gcfs_rq->avg.load_sum, > > scale_load_down(gcfs_rq->load.weight)); > > } > > > > gcfs_rq->load.weight is an insane large value and is truncated > > to the lower 32 bits by div_u64, which happen to be 0. > > > > Using AI for investigation, the cause is a u32 overflow in > > update_tg_cfs_runnable(), and flat pickup became a victim when using > > tg_tasks(): > > > > u32 new_sum, divider; > > ... > > new_sum = se->avg.runnable_avg * divider; <-- boom > > > > The following sequence shows how this triggers the crash: > > > > propagate_entity_load_avg() > > update_tg_cfs_runnable() # u32 overflow corrupts runnable_sum > > > > __update_load_avg_cfs_rq() > > ___update_load_avg() # computes insane runnable_avg > > update_tg_load_avg() # propagates to tg->runnable_avg > > > > update_cfs_group() > > calc_concur_shares() > > tg_tasks() # long-to-int truncation, negative nr > > reweight_entity() # corrupted se->load.weight > > update_load_add() # corrupted cfs_rq->load.weight > > > > propagate_entity_load_avg() > > update_tg_cfs_load() > > div_u64() # divide-by-zero > > > > Fix by widening new_sum from u32 to u64(no need to force tg_tasks() > > to return unsigned long after this fix) > > Assisted-by: Claude:claude-opus-4.6 > > Signed-off-by: Chen Yu <yu.c.chen@intel.com> > > --- > > kernel/sched/fair.c | 5 +++-- > > 1 file changed, 3 insertions(+), 2 deletions(-) > > > > diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c > > index d991ea85873a..99ea51448981 100644 > > --- a/kernel/sched/fair.c > > +++ b/kernel/sched/fair.c > > @@ -5305,7 +5305,8 @@ static inline void > > update_tg_cfs_runnable(struct cfs_rq *cfs_rq, struct sched_entity *se, struct cfs_rq *gcfs_rq) > > { > > long delta_sum, delta_avg = gcfs_rq->avg.runnable_avg - se->avg.runnable_avg; > > - u32 new_sum, divider; > > + u64 new_sum; > > + u32 divider; > > > > /* Nothing to update */ > > if (!delta_avg) > > @@ -5319,7 +5320,7 @@ update_tg_cfs_runnable(struct cfs_rq *cfs_rq, struct sched_entity *se, struct cf > > > > /* Set new sched_entity's runnable */ > > se->avg.runnable_avg = gcfs_rq->avg.runnable_avg; > > - new_sum = se->avg.runnable_avg * divider; > > + new_sum = (u64)se->avg.runnable_avg * divider; > > delta_sum = (long)new_sum - (long)se->avg.runnable_sum; > > se->avg.runnable_sum = new_sum; > > Hmm, nice one. This makes sense because sched_avg::runnable_sum is a u64 > itself, so having the delta be one is only sensible. > > I do wonder though, this doesn't actually look to be specific to flat, > it just managed to trip it somehow. I think, it's specific to the new share policies introduced with flat. Before flat we were using: return clamp_t(long, shares, MIN_SHARES, tg_shares); where tg_shares was clamp(shares, scale_load(MIN_SHARES), scale_load(MAX_SHARES)); With flat, we are using: return clamp_t(long, shares, MIN_SHARES, shares_max); where shares_max can be: max(1, atomic_long_read(&tg->runnable_avg) >> SCHED_CAPACITY_SHIFT) * clamp(shares, scale_load(MIN_SHARES), scale_load(MAX_SHARES)) > > I'll stick this on as a separate fix. ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v3 7/7] sched/eevdf: Move to a single runqueue 2026-06-05 12:40 ` [PATCH v3 7/7] sched/eevdf: Move to a single runqueue Peter Zijlstra 2026-06-20 3:54 ` Chen, Yu C @ 2026-10-16 13:18 ` Jakub Okoński 2026-10-18 6:49 ` Mike Galbraith 2026-10-22 2:12 ` Mike Galbraith 1 sibling, 2 replies; 17+ messages in thread From: Jakub Okoński @ 2026-10-16 13:18 UTC (permalink / raw) To: Peter Zijlstra, mingo Cc: longman, chenridong, juri.lelli, vincent.guittot, dietmar.eggemann, rostedt, bsegall, mgorman, vschneid, tj, hannes, mkoutny, cgroups, linux-kernel, jstultz, kprateek.nayak, qyousef Hi Peter, On 6/5/26 14:40, Peter Zijlstra wrote: > Change fair/cgroup to a single runqueue. > > [...] > > --- a/kernel/sched/fair.c > +++ b/kernel/sched/fair.c > @@ -9916,7 +9837,6 @@ static void wakeup_preempt_fair(struct r > if (!sched_feat(WAKEUP_PREEMPTION)) > return; > > - find_matching_se(&se, &pse); > WARN_ON_ONCE(!pse); > > cse_is_idle = se_is_idle(se); I think this changes how cpu.idle affects wakeup preemption. With the single runqueue, find_matching_se() is gone and se_is_idle() reduces to task_has_idle_policy(). Wouldn't this mean that SCHED_NORMAL tasks in a cpu.idle cgroup are no longer treated as idle for wakeup_preempt_fair()? I tested my existing 7.2.9 desktop against 7.3-rc6 with the setup below. cyclictest doesn't reproduce it because it just sleeps and wakes, no CPU burn and therefore its accrued EEVDF lag keeps the latency low? cc -O2 -o wakelat wakelat.c systemd-run --user --collect --unit=spin -p AllowedCPUs=7 -p CPUWeight=idle sh -c 'while :; do :; done' systemd-run --user --collect --wait --pipe --same-dir -p AllowedCPUs=7 ./wakelat 1000 15 200 | mlr --inidx --ifs space --opprint --barred --right label lat \ then stats1 -a count,p50,p90,p99,max -f lat then rename -r '^lat_,' \ then put -q 'emit mapsum({"kernel": system("uname -r")}, $*)' systemctl --user stop spin wakelat wakes on a fixed 1 ms schedule, records how late each wakeup was, then busy-loops for 200 micros (wall clock). +-----------+-------+-----+-----+------+------+ | kernel | count | p50 | p90 | p99 | max | +-----------+-------+-----+-----+------+------+ | 7.2.9 | 14998 | 11 | 11 | 12 | 1869 | | 7.3.0-rc6 | 13853 | 11 | 12 | 2029 | 2116 | +-----------+-------+-----+-----+------+------+ count is out of 15000 periods. rc6 had 1147 wakeups delayed by over 1 ms. wakelat.c: #define _GNU_SOURCE #include <stdio.h> #include <stdlib.h> #include <sys/prctl.h> #include <time.h> static long long now_ns(void) { struct timespec t; clock_gettime(CLOCK_MONOTONIC, &t); return t.tv_sec * 1000000000LL + t.tv_nsec; } int main(int argc, char **argv) { if (argc != 4) return 2; long long interval = atoll(argv[1]) * 1000, dur = atoll(argv[2]) * 1000000000LL; long long work = atoll(argv[3]) * 1000; long max = dur / interval + 1, n = 0; long *lat = malloc(max * sizeof(*lat)); prctl(PR_SET_TIMERSLACK, 1UL); long long start = now_ns(), next = start + interval; while (next - start < dur && n < max) { struct timespec ts = { next / 1000000000LL, next % 1000000000LL }; clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &ts, NULL); long long now = now_ns(); lat[n++] = (now - next) / 1000; while (now_ns() - now < work) ; do next += interval; while (next <= now); } for (long i = 0; i < n; i++) printf("%ld\n", lat[i]); return 0; } Regards, Jakub ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v3 7/7] sched/eevdf: Move to a single runqueue 2026-10-16 13:18 ` Jakub Okoński @ 2026-10-18 6:49 ` Mike Galbraith 2026-10-19 17:01 ` Vincent Guittot 2026-10-30 15:48 ` Chengfeng Lin 2026-10-22 2:12 ` Mike Galbraith 1 sibling, 2 replies; 17+ messages in thread From: Mike Galbraith @ 2026-10-18 6:49 UTC (permalink / raw) To: Jakub Okoński, Peter Zijlstra, mingo Cc: longman, chenridong, juri.lelli, vincent.guittot, dietmar.eggemann, rostedt, bsegall, mgorman, vschneid, tj, hannes, mkoutny, cgroups, linux-kernel, jstultz, kprateek.nayak, qyousef Greetings! On Fri, 2026-10-16 at 15:18 +0200, Jakub Okoński wrote: > Hi Peter, > > On 6/5/26 14:40, Peter Zijlstra wrote: > > > Change fair/cgroup to a single runqueue. > > > > [...] > > > > --- a/kernel/sched/fair.c > > +++ b/kernel/sched/fair.c > > @@ -9916,7 +9837,6 @@ static void wakeup_preempt_fair(struct r > > if (!sched_feat(WAKEUP_PREEMPTION)) > > return; > > > > - find_matching_se(&se, &pse); > > WARN_ON_ONCE(!pse); > > > > cse_is_idle = se_is_idle(se); > > I think this changes how cpu.idle affects wakeup preemption. My box claims this to be unrelated to the jitter you're seeing, as it is present in 7.2.11, with and without the sched-flat set applied. > With the single runqueue, find_matching_se() is gone and se_is_idle() > reduces to task_has_idle_policy(). Wouldn't this mean that SCHED_NORMAL > tasks in a cpu.idle cgroup are no longer treated as idle for > wakeup_preempt_fair()? > > I tested my existing 7.2.9 desktop against 7.3-rc6 with the setup below. > cyclictest doesn't reproduce it because it just sleeps and wakes, no CPU > burn and therefore its accrued EEVDF lag keeps the latency low? > > cc -O2 -o wakelat wakelat.c > systemd-run --user --collect --unit=spin -p AllowedCPUs=7 -p CPUWeight=idle sh -c 'while :; do :; done' > systemd-run --user --collect --wait --pipe --same-dir -p AllowedCPUs=7 ./wakelat 1000 15 200 | > mlr --inidx --ifs space --opprint --barred --right label lat \ > then stats1 -a count,p50,p90,p99,max -f lat then rename -r '^lat_,' \ > then put -q 'emit mapsum({"kernel": system("uname -r")}, $*)' > systemctl --user stop spin > > wakelat wakes on a fixed 1 ms schedule, records how late each wakeup > was, then busy-loops for 200 micros (wall clock). > > +-----------+-------+-----+-----+------+------+ > > kernel | count | p50 | p90 | p99 | max | > +-----------+-------+-----+-----+------+------+ > > 7.2.9 | 14998 | 11 | 11 | 12 | 1869 | > > 7.3.0-rc6 | 13853 | 11 | 12 | 2029 | 2116 | > +-----------+-------+-----+-----+------+------+ > > count is out of 15000 periods. rc6 had 1147 wakeups delayed by over 1 ms. Thanks a bunch for the proggy. I've been looking for a clear signal with various loads, and stuffing wakelat+awk into one of my scripts provided a perfectly clear signal, which I bisected and confirmed. Note: below is box pointing it's little silicon finger at the source of aforementioned jitter, not necessarily at breakage. Reverting the fingered commit removed the jitter from 7.2.11, and force reverting 68e37487810a + 333238da9a19 removed it from master as well. 333238da9a193ffc58792995f3e951e4cb87bfd2 is the first 'bad' commit commit 333238da9a193ffc58792995f3e951e4cb87bfd2 Author: Vincent Guittot <vincent.guittot@linaro.org> Date: Fri Aug 14 15:52:41 2026 +0200 sched: Update time before requeueing delayed entities In order to compute the right lag, it is required to update time to 'now'. Without this, the delayed entity might appear younger than it really is and receive less compensation for having waited. Signed-off-by: Vincent Guittot <vincent.guittot@linaro.org> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> kernel/sched/fair.c | 2 ++ 1 file changed, 2 insertions(+) ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v3 7/7] sched/eevdf: Move to a single runqueue 2026-10-18 6:49 ` Mike Galbraith @ 2026-10-19 17:01 ` Vincent Guittot 2026-10-19 17:35 ` Jakub Okoński 2026-10-30 15:48 ` Chengfeng Lin 1 sibling, 1 reply; 17+ messages in thread From: Vincent Guittot @ 2026-10-19 17:01 UTC (permalink / raw) To: Mike Galbraith, Jakub Okoński Cc: Peter Zijlstra, mingo, longman, chenridong, juri.lelli, dietmar.eggemann, rostedt, bsegall, mgorman, vschneid, tj, hannes, mkoutny, cgroups, linux-kernel, jstultz, kprateek.nayak, qyousef Le dimanche 18 oct. 2026 à 08:49:29 (+0200), Mike Galbraith a écrit : > Greetings! > > On Fri, 2026-10-16 at 15:18 +0200, Jakub Okoński wrote: > > Hi Peter, > > > > On 6/5/26 14:40, Peter Zijlstra wrote: > > > > > Change fair/cgroup to a single runqueue. > > > > > > [...] > > > > > > --- a/kernel/sched/fair.c > > > +++ b/kernel/sched/fair.c > > > @@ -9916,7 +9837,6 @@ static void wakeup_preempt_fair(struct r > > > if (!sched_feat(WAKEUP_PREEMPTION)) > > > return; > > > > > > - find_matching_se(&se, &pse); > > > WARN_ON_ONCE(!pse); > > > > > > cse_is_idle = se_is_idle(se); > > > > I think this changes how cpu.idle affects wakeup preemption. > > My box claims this to be unrelated to the jitter you're seeing, as it > is present in 7.2.11, with and without the sched-flat set applied. Yes, we don't take into account whether the task group is sched_idle Jakub, Mike, Could you try the patch below ? It solves the problem on my setup --- kernel/sched/fair.c | 80 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 68 insertions(+), 12 deletions(-) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 0f2364479f9d..d17e88ad12a5 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -433,6 +433,38 @@ static inline struct sched_entity *parent_entity(const struct sched_entity *se) return se->parent; } +static void +find_matching_se(struct sched_entity **se, struct sched_entity **pse) +{ + int se_depth, pse_depth; + + /* + * preemption test can be made between sibling entities who are in the + * same cfs_rq i.e who have a common parent. Walk up the hierarchy of + * both tasks until we find their ancestors who are siblings of common + * parent. + */ + + /* First walk up until both entities are at same depth */ + se_depth = (*se)->depth; + pse_depth = (*pse)->depth; + + while (se_depth > pse_depth) { + se_depth--; + *se = parent_entity(*se); + } + + while (pse_depth > se_depth) { + pse_depth--; + *pse = parent_entity(*pse); + } + + while (!is_same_group(*se, *pse)) { + *se = parent_entity(*se); + *pse = parent_entity(*pse); + } +} + static int tg_is_idle(struct task_group *tg) { return tg->idle > 0; @@ -476,6 +508,11 @@ static inline struct sched_entity *parent_entity(struct sched_entity *se) return NULL; } +static inline void +find_matching_se(struct sched_entity **se, struct sched_entity **pse) +{ +} + static inline int tg_is_idle(struct task_group *tg) { return 0; @@ -10129,7 +10166,34 @@ preempt_sync(struct rq *rq, int wake_flags, } /* - * Preempt the current task with a newly woken task if needed: + * Preempt an idle entity in favor of a non-idle entity (and don't preempt + * in the inverse case). + */ +static bool wakeup_preempt_sched_idle(struct sched_entity *cse, struct sched_entity *pse) +{ + int cse_is_idle, pse_is_idle; + + find_matching_se(&cse, &pse); + WARN_ON_ONCE(!pse); + + cse_is_idle = se_is_idle(cse); + pse_is_idle = se_is_idle(pse); + + /* + * Preempt an idle entity in favor of a non-idle entity (and don't preempt + * in the inverse case). + */ + if (cse_is_idle && !pse_is_idle) + return PREEMPT_WAKEUP_RESCHED; + + if (cse_is_idle != pse_is_idle) + return PREEMPT_WAKEUP_NONE; + + return PREEMPT_WAKEUP_PICK; +} + +/* + * Preempt the current task with a newly woken task if needed. */ static void wakeup_preempt_fair(struct rq *rq, struct task_struct *p, int wake_flags) { @@ -10174,22 +10238,14 @@ static void wakeup_preempt_fair(struct rq *rq, struct task_struct *p, int wake_f if (!sched_feat(WAKEUP_PREEMPTION)) return; - WARN_ON_ONCE(!pse); - - cse_is_idle = se_is_idle(se); - pse_is_idle = se_is_idle(pse); + preempt_action = wakeup_preempt_sched_idle(se, pse); - nse = se; - /* - * Preempt an idle entity in favor of a non-idle entity (and don't preempt - * in the inverse case). - */ - if (cse_is_idle && !pse_is_idle) + if (preempt_action == PREEMPT_WAKEUP_RESCHED) goto preempt; update_curr_fair(rq); - if (cse_is_idle != pse_is_idle) + if (preempt_action == PREEMPT_WAKEUP_NONE) goto update; /* -- 2.53.0 > > > With the single runqueue, find_matching_se() is gone and se_is_idle() > > reduces to task_has_idle_policy(). Wouldn't this mean that SCHED_NORMAL > > tasks in a cpu.idle cgroup are no longer treated as idle for > > wakeup_preempt_fair()? > > > > > > I tested my existing 7.2.9 desktop against 7.3-rc6 with the setup below. > > cyclictest doesn't reproduce it because it just sleeps and wakes, no CPU > > burn and therefore its accrued EEVDF lag keeps the latency low? > > > > cc -O2 -o wakelat wakelat.c > > systemd-run --user --collect --unit=spin -p AllowedCPUs=7 -p CPUWeight=idle sh -c 'while :; do :; done' > > systemd-run --user --collect --wait --pipe --same-dir -p AllowedCPUs=7 ./wakelat 1000 15 200 | > > mlr --inidx --ifs space --opprint --barred --right label lat \ > > then stats1 -a count,p50,p90,p99,max -f lat then rename -r '^lat_,' \ > > then put -q 'emit mapsum({"kernel": system("uname -r")}, $*)' > > systemctl --user stop spin > > > > wakelat wakes on a fixed 1 ms schedule, records how late each wakeup > > was, then busy-loops for 200 micros (wall clock). > > > > +-----------+-------+-----+-----+------+------+ > > > kernel | count | p50 | p90 | p99 | max | > > +-----------+-------+-----+-----+------+------+ > > > 7.2.9 | 14998 | 11 | 11 | 12 | 1869 | > > > 7.3.0-rc6 | 13853 | 11 | 12 | 2029 | 2116 | > > +-----------+-------+-----+-----+------+------+ > > > > count is out of 15000 periods. rc6 had 1147 wakeups delayed by over 1 ms. > > Thanks a bunch for the proggy. I've been looking for a clear signal > with various loads, and stuffing wakelat+awk into one of my scripts > provided a perfectly clear signal, which I bisected and confirmed. > > Note: below is box pointing it's little silicon finger at the source of > aforementioned jitter, not necessarily at breakage. > > Reverting the fingered commit removed the jitter from 7.2.11, and force > reverting 68e37487810a + 333238da9a19 removed it from master as well. > > 333238da9a193ffc58792995f3e951e4cb87bfd2 is the first 'bad' commit > commit 333238da9a193ffc58792995f3e951e4cb87bfd2 > Author: Vincent Guittot <vincent.guittot@linaro.org> > Date: Fri Aug 14 15:52:41 2026 +0200 > > sched: Update time before requeueing delayed entities > > In order to compute the right lag, it is required to update time to 'now'. > Without this, the delayed entity might appear younger than it really is and > receive less compensation for having waited. > > Signed-off-by: Vincent Guittot <vincent.guittot@linaro.org> > Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> > > kernel/sched/fair.c | 2 ++ > 1 file changed, 2 insertions(+) > ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v3 7/7] sched/eevdf: Move to a single runqueue 2026-10-19 17:01 ` Vincent Guittot @ 2026-10-19 17:35 ` Jakub Okoński 2026-10-19 18:04 ` Vincent Guittot 0 siblings, 1 reply; 17+ messages in thread From: Jakub Okoński @ 2026-10-19 17:35 UTC (permalink / raw) To: Vincent Guittot, Mike Galbraith Cc: Peter Zijlstra, mingo, longman, chenridong, juri.lelli, dietmar.eggemann, rostedt, bsegall, mgorman, vschneid, tj, hannes, mkoutny, cgroups, linux-kernel, jstultz, kprateek.nayak, qyousef Hi Vincent, Your patch fixes my cpu.idle test case. On 10/19/26 19:01, Vincent Guittot wrote: > /* > - * Preempt the current task with a newly woken task if needed: > + * Preempt an idle entity in favor of a non-idle entity (and don't preempt > + * in the inverse case). > + */ > +static bool wakeup_preempt_sched_idle(struct sched_entity *cse, struct sched_entity *pse) This should return enum preempt_wakeup_action. But with that change, on top of 7.3.0, it still regresses from 7.2.9 if you test with a nice 19 spinner in the same cgroup. I think it's incomplete. ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v3 7/7] sched/eevdf: Move to a single runqueue 2026-10-19 17:35 ` Jakub Okoński @ 2026-10-19 18:04 ` Vincent Guittot 2026-10-19 18:24 ` Jakub Okoński 0 siblings, 1 reply; 17+ messages in thread From: Vincent Guittot @ 2026-10-19 18:04 UTC (permalink / raw) To: Jakub Okoński Cc: Mike Galbraith, Peter Zijlstra, mingo, longman, chenridong, juri.lelli, dietmar.eggemann, rostedt, bsegall, mgorman, vschneid, tj, hannes, mkoutny, cgroups, linux-kernel, jstultz, kprateek.nayak, qyousef On Mon, 19 Oct 2026 at 19:36, Jakub Okoński <jakub@okonski.org> wrote: > > Hi Vincent, > > Your patch fixes my cpu.idle test case. > > On 10/19/26 19:01, Vincent Guittot wrote: > > /* > > - * Preempt the current task with a newly woken task if needed: > > + * Preempt an idle entity in favor of a non-idle entity (and don't preempt > > + * in the inverse case). > > + */ > > +static bool wakeup_preempt_sched_idle(struct sched_entity *cse, struct sched_entity *pse) > > This should return enum preempt_wakeup_action. But with that change, on Argh! Yes I moved from a binary to a ternary value but forgot to update the prototype > top of 7.3.0, it still regresses from 7.2.9 if you test with a nice 19 > spinner in the same cgroup. I think it's incomplete. This patch only aims to fix the sched_idle task group case. I will have a closer look at the nice 19 case. Have you checked the different cgroup_mode with nice 19 ? Vincent ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v3 7/7] sched/eevdf: Move to a single runqueue 2026-10-19 18:04 ` Vincent Guittot @ 2026-10-19 18:24 ` Jakub Okoński 2026-10-19 18:26 ` Vincent Guittot 0 siblings, 1 reply; 17+ messages in thread From: Jakub Okoński @ 2026-10-19 18:24 UTC (permalink / raw) To: Vincent Guittot Cc: Mike Galbraith, Peter Zijlstra, mingo, longman, chenridong, juri.lelli, dietmar.eggemann, rostedt, bsegall, mgorman, vschneid, tj, hannes, mkoutny, cgroups, linux-kernel, jstultz, kprateek.nayak, qyousef On 10/19/26 20:04, Vincent Guittot wrote: > On Mon, 19 Oct 2026 at 19:36, Jakub Okoński <jakub@okonski.org> wrote: >> >> top of 7.3.0, it still regresses from 7.2.9 if you test with a nice 19 >> spinner in the same cgroup. I think it's incomplete. > > This patch only aims to fix the sched_idle task group case. I will > have a closer look at the nice 19 case. Have you checked the different > cgroup_mode with nice 19 ? > > Vincent Same result in my nice 19 test for all of up/smp/concur/max/tasks. ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v3 7/7] sched/eevdf: Move to a single runqueue 2026-10-19 18:24 ` Jakub Okoński @ 2026-10-19 18:26 ` Vincent Guittot 2026-10-21 11:55 ` Vincent Guittot 0 siblings, 1 reply; 17+ messages in thread From: Vincent Guittot @ 2026-10-19 18:26 UTC (permalink / raw) To: Jakub Okoński Cc: Mike Galbraith, Peter Zijlstra, mingo, longman, chenridong, juri.lelli, dietmar.eggemann, rostedt, bsegall, mgorman, vschneid, tj, hannes, mkoutny, cgroups, linux-kernel, jstultz, kprateek.nayak, qyousef On Mon, 19 Oct 2026 at 20:24, Jakub Okoński <jakub@okonski.org> wrote: > > On 10/19/26 20:04, Vincent Guittot wrote: > > On Mon, 19 Oct 2026 at 19:36, Jakub Okoński <jakub@okonski.org> wrote: > >> > >> top of 7.3.0, it still regresses from 7.2.9 if you test with a nice 19 > >> spinner in the same cgroup. I think it's incomplete. > > > > This patch only aims to fix the sched_idle task group case. I will > > have a closer look at the nice 19 case. Have you checked the different > > cgroup_mode with nice 19 ? > > > > Vincent > > Same result in my nice 19 test for all of up/smp/concur/max/tasks. Okay Thanks for the tests results ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v3 7/7] sched/eevdf: Move to a single runqueue 2026-10-19 18:26 ` Vincent Guittot @ 2026-10-21 11:55 ` Vincent Guittot 2026-10-22 12:05 ` Jakub Okoński 0 siblings, 1 reply; 17+ messages in thread From: Vincent Guittot @ 2026-10-21 11:55 UTC (permalink / raw) To: Jakub Okoński Cc: Mike Galbraith, Peter Zijlstra, mingo, longman, juri.lelli, dietmar.eggemann, rostedt, bsegall, mgorman, vschneid, tj, hannes, mkoutny, cgroups, linux-kernel, jstultz, kprateek.nayak, qyousef On Mon, 19 Oct 2026 at 20:26, Vincent Guittot <vincent.guittot@linaro.org> wrote: > > On Mon, 19 Oct 2026 at 20:24, Jakub Okoński <jakub@okonski.org> wrote: > > > > On 10/19/26 20:04, Vincent Guittot wrote: > > > On Mon, 19 Oct 2026 at 19:36, Jakub Okoński <jakub@okonski.org> wrote: > > >> > > >> top of 7.3.0, it still regresses from 7.2.9 if you test with a nice 19 > > >> spinner in the same cgroup. I think it's incomplete. > > > > > > This patch only aims to fix the sched_idle task group case. I will > > > have a closer look at the nice 19 case. Have you checked the different > > > cgroup_mode with nice 19 ? > > > > > > Vincent > > > > Same result in my nice 19 test for all of up/smp/concur/max/tasks. > > Okay > Thanks for the tests results I ran more tests on 7.2.9 and 7.3. I confirm that there is a difference between 7.2.9 and 7.3. With flat hierarchy, we change the weight used to compute vruntime, deadline and vlag. The positive vlag elapses differently with the flat hierarchy and slice protection becomes more active. I used rt-app for the short task that wakes up periodically in my test - v7.2.9 Metric | Value (us) ----------------------------------- Total Samples | 16983 Total outlier | 17 Average | 54 Median (P50) | 53 90th Percentile | 53 99th Percentile | 54 99.9th Percentile | 1582 Maximum | 2248 ----------------------------------- - v7.3 Metric | Value (us) ----------------------------------- Total Samples | 16368 Total outlier | 632 Average | 145 Median (P50) | 53 90th Percentile | 53 99th Percentile | 2039 99.9th Percentile | 2132 Maximum | 3367 ----------------------------------- That being said, there is a trend to make the lag to elapse quicker and an app can't expect to maintain its lag forever while sleeping. Instead, the app should set its slice to notify the scheduler that it would like to run 1st as long as fairness is not broken. If I set a slice of 1ms for the short task I have : - v7.2.9 Metric | Value (us) ----------------------------------- Total Samples | 17000 Total outlier | 16 Average | 52 Median (P50) | 53 90th Percentile | 53 99th Percentile | 55 99.9th Percentile | 55 Maximum | 56 ----------------------------------- - v7.3 Metric | Value (us) ----------------------------------- Total Samples | 17000 Total outlier | 11 Average | 52 Median (P50) | 53 90th Percentile | 53 99th Percentile | 54 99.9th Percentile | 55 Maximum | 57 ----------------------------------- Which is the correct way to ask scheduler to make a task to run 1st ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v3 7/7] sched/eevdf: Move to a single runqueue 2026-10-21 11:55 ` Vincent Guittot @ 2026-10-22 12:05 ` Jakub Okoński 0 siblings, 0 replies; 17+ messages in thread From: Jakub Okoński @ 2026-10-22 12:05 UTC (permalink / raw) To: Vincent Guittot Cc: Mike Galbraith, Peter Zijlstra, mingo, longman, juri.lelli, dietmar.eggemann, rostedt, bsegall, mgorman, vschneid, tj, hannes, mkoutny, cgroups, linux-kernel, jstultz, kprateek.nayak, qyousef On 10/21/26 13:55, Vincent Guittot wrote: > Which is the correct way to ask scheduler to make a task to run 1st I totally get that and will try to make the changes in my workloads, starting with https://invent.kde.org/plasma/kwin/-/merge_requests/10192 That said, I hope both the cpu.idle and nice 19 issues get resolved and I'll be watching those conversations. It's too disruptive otherwise! Regards, Jakub ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v3 7/7] sched/eevdf: Move to a single runqueue 2026-10-18 6:49 ` Mike Galbraith 2026-10-19 17:01 ` Vincent Guittot @ 2026-10-30 15:48 ` Chengfeng Lin 1 sibling, 0 replies; 17+ messages in thread From: Chengfeng Lin @ 2026-10-30 15:48 UTC (permalink / raw) To: Vincent Guittot, Mike Galbraith, Peter Zijlstra Cc: Ingo Molnar, Juri Lelli, linux-kernel Hi Vincent, Mike, Peter, Sorry, my earlier report was sent without the reply headers. It contains additional will-it-scale pipe-handoff results for 333238da9a19: https://lists.openwall.net/linux-kernel/2026/10/30/1506 I'm linking it here to keep it connected to Mike's report in this thread. The measurements and reproduction instructions are unchanged. Thanks, Chengfeng ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v3 7/7] sched/eevdf: Move to a single runqueue 2026-10-16 13:18 ` Jakub Okoński 2026-10-18 6:49 ` Mike Galbraith @ 2026-10-22 2:12 ` Mike Galbraith 1 sibling, 0 replies; 17+ messages in thread From: Mike Galbraith @ 2026-10-22 2:12 UTC (permalink / raw) To: Jakub Okoński, Peter Zijlstra, mingo Cc: longman, chenridong, juri.lelli, vincent.guittot, dietmar.eggemann, rostedt, bsegall, mgorman, vschneid, tj, hannes, mkoutny, cgroups, linux-kernel, jstultz, kprateek.nayak, qyousef On Fri, 2026-10-16 at 15:18 +0200, Jakub Okoński wrote: > Hi Peter, > > On 6/5/26 14:40, Peter Zijlstra wrote: > > > Change fair/cgroup to a single runqueue. > > > > [...] > > > > --- a/kernel/sched/fair.c > > +++ b/kernel/sched/fair.c > > @@ -9916,7 +9837,6 @@ static void wakeup_preempt_fair(struct r > > if (!sched_feat(WAKEUP_PREEMPTION)) > > return; > > > > - find_matching_se(&se, &pse); > > WARN_ON_ONCE(!pse); > > > > cse_is_idle = se_is_idle(se); > > I think this changes how cpu.idle affects wakeup preemption. > > With the single runqueue, find_matching_se() is gone and se_is_idle() > reduces to task_has_idle_policy(). Wouldn't this mean that SCHED_NORMAL > tasks in a cpu.idle cgroup are no longer treated as idle for > wakeup_preempt_fair()? Am I perhaps testing wrong? What I do to produce the perf sched data below is stuff pinned hogs in one cgroup, pinned wakelat in another, diddle the hog populated cgroup cpu.idle... and watch wakelat blast straight through the hog between it and the CPU just fine, regardless of cpu.idle state. WRT the data, ignore perf's max below, take awk processed wakelat output as gospel. I can make the perf numbers match nearly perfects by running all players in the background and terminating via a time event but didn't bother here. The important bits are accumulated runtime, ctx switch count, and avg delay for the bottom two lines of each report. Ignore report format, that's a local hacklet, and also ignore the wakelat runtime delta between tested kernels when cpu.idle=0 in the hog group, that's a DELAY_DEQUEUE issue I've verified to be the source of the jitter I can reproduce. BEGIN 7.3.0.g14c89774-master 7.3.0.g14c89774-master cgroupA-hogs-(cpu.idle=0)-vs-groupB-wakelat wakeup latency >1ms: 4568 max: 2.4ms stddev: 554.882 ---------------------------------------------------------------------------------------------------------- Task | Runtime | Count | Avg delay | Max delay | Sum delay | ---------------------------------------------------------------------------------------------------------- cpuhog:1267 | 60.048 s | 40 | 6.186 us | 43.523 us | 247.445 us | cpuhog:1269 | 60.047 s | 102 | 8.867 us | 144.492 us | 904.512 us | cpuhog:1268 | 60.047 s | 126 | 10.899 us | 774.840 us | 1.373 ms | cpuhog:1266 | 60.045 s | 58 | 19.230 us | 531.945 us | 1.115 ms | cpuhog:1270 | 60.044 s | 35 | 4.708 us | 32.297 us | 164.798 us | cpuhog:1264 | 60.033 s | 492 | 27.200 us | 2.383 ms | 13.383 ms | cpuhog:1265 | 60.030 s | 783 | 23.775 us | 83.871 us | 18.617 ms | cpuhog:1271 | 49.519 s | 51947 | 202.691 us | 5.607 ms | 10.529 s | wakelat:1277 | 10.512 s | 52011 | 171.849 us | 10.416 ms | 8.938 s | ---------------------------------------------------------------------------------------------------------- TOTAL: | 480.376 s | 107581 | | 10.416 ms | 19.627 s | ---------------------------------------------------------------------------------------------------------- 7.3.0.g14c89774-master cgroupA-hogs-(cpu.idle=1)-vs-groupB-wakelat wakeup latency >1ms: 0 max: 0.0ms stddev: 0.500 ---------------------------------------------------------------------------------------------------------- Task | Runtime | Count | Avg delay | Max delay | Sum delay | ---------------------------------------------------------------------------------------------------------- cpuhog:1297 | 60.026 s | 43 | 9.577 us | 83.461 us | 411.831 us | cpuhog:1298 | 60.024 s | 27 | 4.094 us | 31.306 us | 110.547 us | cpuhog:1294 | 60.023 s | 60 | 10.620 us | 52.234 us | 637.217 us | cpuhog:1296 | 60.022 s | 131 | 15.621 us | 908.988 us | 2.046 ms | cpuhog:1293 | 60.019 s | 224 | 6.861 us | 593.440 us | 1.537 ms | cpuhog:1292 | 60.006 s | 690 | 28.075 us | 286.517 us | 19.372 ms | cpuhog:1295 | 60.005 s | 26 | 4.119 us | 32.429 us | 107.111 us | cpuhog:1299 | 47.903 s | 60216 | 201.386 us | 5.270 ms | 12.127 s | wakelat:1305 | 12.124 s | 60023 | 1.963 us | 2.156 ms | 117.862 ms | ---------------------------------------------------------------------------------------------------------- TOTAL: | 480.196 s | 122918 | | 5.270 ms | 12.394 s | ---------------------------------------------------------------------------------------------------------- BEGIN 7.2.11-ag-flat+hack Note: this tree has virgin flat applied, and the bisected commit reverted. 7.2.11-ag-flat+hack cgroupA-hogs-(cpu.idle=0)-vs-groupB-wakelat wakeup latency >1ms: 0 max: 0.0ms stddev: 0.438 ---------------------------------------------------------------------------------------------------------- Task | Runtime | Count | Avg delay | Max delay | Sum delay | ---------------------------------------------------------------------------------------------------------- cpuhog:1272 | 60.040 s | 44 | 6.139 us | 40.163 us | 270.136 us | cpuhog:1275 | 60.039 s | 139 | 3.768 us | 185.461 us | 523.864 us | cpuhog:1276 | 60.039 s | 140 | 5.432 us | 28.697 us | 760.493 us | cpuhog:1274 | 60.039 s | 104 | 12.441 us | 814.146 us | 1.294 ms | cpuhog:1270 | 60.037 s | 271 | 13.571 us | 637.071 us | 3.678 ms | cpuhog:1273 | 60.036 s | 51 | 17.658 us | 439.987 us | 900.594 us | cpuhog:1271 | 60.018 s | 636 | 28.960 us | 362.859 us | 18.419 ms | cpuhog:1277 | 47.902 s | 60081 | 202.120 us | 9.812 ms | 12.144 s | wakelat:1283 | 12.122 s | 60016 | 1.905 us | 2.801 ms | 114.368 ms | ---------------------------------------------------------------------------------------------------------- TOTAL: | 480.316 s | 123011 | | 9.812 ms | 12.352 s | ---------------------------------------------------------------------------------------------------------- 7.2.11-ag-flat+hack cgroupA-hogs-(cpu.idle=1)-vs-groupB-wakelat wakeup latency >1ms: 0 max: 0.0ms stddev: 0.455 ---------------------------------------------------------------------------------------------------------- Task | Runtime | Count | Avg delay | Max delay | Sum delay | ---------------------------------------------------------------------------------------------------------- cpuhog:1298 | 60.021 s | 307 | 8.860 us | 272.423 us | 2.720 ms | cpuhog:1301 | 60.020 s | 20 | 3.661 us | 30.994 us | 73.236 us | cpuhog:1303 | 60.020 s | 135 | 2.070 us | 28.715 us | 279.571 us | cpuhog:1302 | 60.019 s | 80 | 14.390 us | 782.611 us | 1.151 ms | cpuhog:1300 | 60.017 s | 46 | 42.518 us | 1.656 ms | 1.956 ms | cpuhog:1299 | 60.005 s | 22 | 6.165 us | 62.913 us | 135.646 us | cpuhog:1304 | 60.001 s | 666 | 27.965 us | 92.954 us | 18.625 ms | cpuhog:1305 | 47.900 s | 60084 | 201.849 us | 4.224 ms | 12.128 s | wakelat:1311 | 12.124 s | 60009 | 1.977 us | 1.440 ms | 118.685 ms | ---------------------------------------------------------------------------------------------------------- TOTAL: | 480.165 s | 122809 | | 4.224 ms | 12.364 s | ---------------------------------------------------------------------------------------------------------- ^ permalink raw reply [flat|nested] 17+ messages in thread
end of thread, other threads:[~2026-10-30 17:01 UTC | newest] Thread overview: 17+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2026-10-30 13:03 [PATCH v3 7/7] sched/eevdf: Move to a single runqueue Chengfeng Lin 2026-10-30 17:01 ` Mike Galbraith -- strict thread matches above, loose matches on Subject: below -- 2026-06-05 12:40 [PATCH v3 0/7] sched: Flatten the pick Peter Zijlstra 2026-06-05 12:40 ` [PATCH v3 7/7] sched/eevdf: Move to a single runqueue Peter Zijlstra 2026-06-20 3:54 ` Chen, Yu C 2026-06-26 11:40 ` Peter Zijlstra 2026-06-29 14:02 ` Vincent Guittot 2026-10-16 13:18 ` Jakub Okoński 2026-10-18 6:49 ` Mike Galbraith 2026-10-19 17:01 ` Vincent Guittot 2026-10-19 17:35 ` Jakub Okoński 2026-10-19 18:04 ` Vincent Guittot 2026-10-19 18:24 ` Jakub Okoński 2026-10-19 18:26 ` Vincent Guittot 2026-10-21 11:55 ` Vincent Guittot 2026-10-22 12:05 ` Jakub Okoński 2026-10-30 15:48 ` Chengfeng Lin 2026-10-22 2:12 ` Mike Galbraith
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®