[PATCH 3/9] sched/fair: defer WARN console output under rq->lock

From: Rik van Riel

Date: Wed Jun 10 2026 - 22:24:58 EST


Convert the WARN*() calls that run under rq->lock or ->pi_lock to the
SCHED_WARN*() variants, so their console output is deferred to irq_work
instead of being emitted synchronously (which can deadlock via
console_unlock() -> up(&console_sem) -> try_to_wake_up() while the lock
is held).

This should prevent a deadlock if these warnings fire with a legacy
or boot console configured.

Signed-off-by: Rik van Riel <riel@xxxxxxxxxxx>
Assisted-by: Claude:claude-opus-4-8
---
kernel/sched/fair.c | 66 ++++++++++++++++++++++-----------------------
1 file changed, 33 insertions(+), 33 deletions(-)

diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index 49b48c5f5746..0cddbbe2e6a5 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -404,7 +404,7 @@ static inline void list_del_leaf_cfs_rq(struct cfs_rq *cfs_rq)

static inline void assert_list_leaf_cfs_rq(struct rq *rq)
{
- WARN_ON_ONCE(rq->tmp_alone_branch != &rq->leaf_cfs_rq_list);
+ SCHED_WARN_ON_ONCE(rq->tmp_alone_branch != &rq->leaf_cfs_rq_list);
}

/* Iterate through all leaf cfs_rq's on a runqueue */
@@ -689,7 +689,7 @@ __sum_w_vruntime_add(struct cfs_rq *cfs_rq, struct sched_entity *se)
s64 w_vruntime, key = entity_key(cfs_rq, se);

w_vruntime = key * weight;
- WARN_ON_ONCE((w_vruntime >> 63) != (w_vruntime >> 62));
+ SCHED_WARN_ON_ONCE((w_vruntime >> 63) != (w_vruntime >> 62));

cfs_rq->sum_w_vruntime += w_vruntime;
cfs_rq->sum_weight += weight;
@@ -861,7 +861,7 @@ bool update_entity_lag(struct cfs_rq *cfs_rq, struct sched_entity *se)
u64 avruntime = avg_vruntime(cfs_rq);
s64 vlag = entity_lag(cfs_rq, se, avruntime);

- WARN_ON_ONCE(!se->on_rq);
+ SCHED_WARN_ON_ONCE(!se->on_rq);

if (se->sched_delayed) {
/* previous vlag < 0 otherwise se would not be delayed */
@@ -1153,7 +1153,7 @@ static struct sched_entity *pick_eevdf(struct cfs_rq *cfs_rq, bool protect)
if (sched_feat(PICK_BUDDY) && protect &&
cfs_rq->next && entity_eligible(cfs_rq, cfs_rq->next)) {
/* ->next will never be delayed */
- WARN_ON_ONCE(cfs_rq->next->sched_delayed);
+ SCHED_WARN_ON_ONCE(cfs_rq->next->sched_delayed);
return cfs_rq->next;
}

@@ -4918,7 +4918,7 @@ static inline bool load_avg_is_decayed(struct sched_avg *sa)
* Make sure that rounding and/or propagation of PELT values never
* break this.
*/
- WARN_ON_ONCE(sa->load_avg ||
+ SCHED_WARN_ON_ONCE(sa->load_avg ||
sa->util_avg ||
sa->runnable_avg);

@@ -6065,7 +6065,7 @@ place_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int flags)

weight = avg_vruntime_weight(cfs_rq, se->load.weight);
lag *= load + weight;
- if (WARN_ON_ONCE(!load))
+ if (SCHED_WARN_ON_ONCE(!load))
load = 1;
lag = div64_long(lag, load);

@@ -6258,7 +6258,7 @@ dequeue_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int flags)
clear_buddies(cfs_rq, se);

if (flags & DEQUEUE_DELAYED) {
- WARN_ON_ONCE(!se->sched_delayed);
+ SCHED_WARN_ON_ONCE(!se->sched_delayed);
} else {
bool delay = sleep;
/*
@@ -6268,7 +6268,7 @@ dequeue_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int flags)
if (flags & (DEQUEUE_SPECIAL | DEQUEUE_THROTTLE))
delay = false;

- WARN_ON_ONCE(delay && se->sched_delayed);
+ SCHED_WARN_ON_ONCE(delay && se->sched_delayed);

if (sched_feat(DELAY_DEQUEUE) && delay &&
!entity_eligible(cfs_rq, se)) {
@@ -6360,7 +6360,7 @@ set_next_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, bool first)
}

update_stats_curr_start(cfs_rq, se);
- WARN_ON_ONCE(cfs_rq->curr);
+ SCHED_WARN_ON_ONCE(cfs_rq->curr);
cfs_rq->curr = se;

/*
@@ -6422,7 +6422,7 @@ static void put_prev_entity(struct cfs_rq *cfs_rq, struct sched_entity *prev)
/* in !on_rq case, update occurred at dequeue */
update_load_avg(cfs_rq, prev, 0);
}
- WARN_ON_ONCE(cfs_rq->curr != prev);
+ SCHED_WARN_ON_ONCE(cfs_rq->curr != prev);
cfs_rq->curr = NULL;
}

@@ -6768,7 +6768,7 @@ static int tg_unthrottle_up(struct task_group *tg, void *data)

cfs_rq->throttled_clock_self = 0;

- if (WARN_ON_ONCE((s64)delta < 0))
+ if (SCHED_WARN_ON_ONCE((s64)delta < 0))
delta = 0;

cfs_rq->throttled_clock_self_time += delta;
@@ -6855,8 +6855,8 @@ static int tg_throttle_down(struct task_group *tg, void *data)
cfs_rq->pelt_clock_throttled = 1;
}

- WARN_ON_ONCE(cfs_rq->throttled_clock_self);
- WARN_ON_ONCE(!list_empty(&cfs_rq->throttled_limbo_list));
+ SCHED_WARN_ON_ONCE(cfs_rq->throttled_clock_self);
+ SCHED_WARN_ON_ONCE(!list_empty(&cfs_rq->throttled_limbo_list));
return 0;
}

@@ -6910,7 +6910,7 @@ static bool throttle_cfs_rq(struct cfs_rq *cfs_rq)
* throttled-list. rq->lock protects completion.
*/
cfs_rq->throttled = 1;
- WARN_ON_ONCE(cfs_rq->throttled_clock);
+ SCHED_WARN_ON_ONCE(cfs_rq->throttled_clock);

/*
* If current hierarchy was throttled, add throttle work to the
@@ -7029,7 +7029,7 @@ static inline void __unthrottle_cfs_rq_async(struct cfs_rq *cfs_rq)
}

/* Already enqueued */
- if (WARN_ON_ONCE(!list_empty(&cfs_rq->throttled_csd_list)))
+ if (SCHED_WARN_ON_ONCE(!list_empty(&cfs_rq->throttled_csd_list)))
return;

first = list_empty(&rq->cfsb_csd_list);
@@ -7042,7 +7042,7 @@ static void unthrottle_cfs_rq_async(struct cfs_rq *cfs_rq)
{
lockdep_assert_rq_held(rq_of(cfs_rq));

- if (WARN_ON_ONCE(!cfs_rq_throttled(cfs_rq) ||
+ if (SCHED_WARN_ON_ONCE(!cfs_rq_throttled(cfs_rq) ||
cfs_rq->runtime_remaining <= 0))
return;

@@ -7083,7 +7083,7 @@ static bool distribute_cfs_runtime(struct cfs_bandwidth *cfs_b)
}

/* By the above checks, this should never be true */
- WARN_ON_ONCE(cfs_rq->runtime_remaining > 0);
+ SCHED_WARN_ON_ONCE(cfs_rq->runtime_remaining > 0);

scoped_guard(raw_spinlock, &cfs_b->lock) {
runtime = -cfs_rq->runtime_remaining + 1;
@@ -7671,7 +7671,7 @@ static void hrtick_start_fair(struct rq *rq, struct task_struct *p)
u64 vdelta;
u64 delta;

- WARN_ON_ONCE(task_rq(p) != rq);
+ SCHED_WARN_ON_ONCE(task_rq(p) != rq);

if (rq->cfs.h_nr_queued <= 1)
return;
@@ -7794,8 +7794,8 @@ requeue_delayed_entity(struct sched_entity *se)
* Because a delayed entity is one that is still on
* the runqueue competing until elegibility.
*/
- WARN_ON_ONCE(!se->sched_delayed);
- WARN_ON_ONCE(!se->on_rq);
+ SCHED_WARN_ON_ONCE(!se->sched_delayed);
+ SCHED_WARN_ON_ONCE(!se->on_rq);

if (update_entity_lag(cfs_rq, se)) {
cfs_rq->nr_queued--;
@@ -8032,8 +8032,8 @@ static int dequeue_entities(struct rq *rq, struct sched_entity *se, int flags)
rq->next_balance = jiffies;

if (p && task_delayed) {
- WARN_ON_ONCE(!task_sleep);
- WARN_ON_ONCE(p->on_rq != 1);
+ SCHED_WARN_ON_ONCE(!task_sleep);
+ SCHED_WARN_ON_ONCE(p->on_rq != 1);

/*
* Fix-up what block_task() skipped.
@@ -9709,7 +9709,7 @@ static void set_cpus_allowed_fair(struct task_struct *p, struct affinity_context
static void set_next_buddy(struct sched_entity *se)
{
for_each_sched_entity(se) {
- if (WARN_ON_ONCE(!se->on_rq))
+ if (SCHED_WARN_ON_ONCE(!se->on_rq))
return;
if (se_is_idle(se))
return;
@@ -9756,7 +9756,7 @@ preempt_sync(struct rq *rq, int wake_flags,
* WF_SYNC without WF_TTWU is not expected so warn if it happens even
* though it is likely harmless.
*/
- WARN_ON_ONCE(!(wake_flags & WF_TTWU));
+ SCHED_WARN_ON_ONCE(!(wake_flags & WF_TTWU));

threshold = sysctl_sched_migration_cost;
delta = rq_clock_task(rq) - se->exec_start;
@@ -9828,7 +9828,7 @@ static void wakeup_preempt_fair(struct rq *rq, struct task_struct *p, int wake_f
return;

find_matching_se(&se, &pse);
- WARN_ON_ONCE(!pse);
+ SCHED_WARN_ON_ONCE(!pse);

cse_is_idle = se_is_idle(se);
pse_is_idle = se_is_idle(pse);
@@ -10861,8 +10861,8 @@ static void detach_task(struct task_struct *p, struct lb_env *env)
schedstat_inc(p->stats.nr_forced_migrations);
}

- WARN_ON(task_current(env->src_rq, p));
- WARN_ON(task_current_donor(env->src_rq, p));
+ SCHED_WARN_ON(task_current(env->src_rq, p));
+ SCHED_WARN_ON(task_current_donor(env->src_rq, p));

deactivate_task(env->src_rq, p, DEQUEUE_NOCLOCK);
set_task_cpu(p, env->dst_cpu);
@@ -13334,7 +13334,7 @@ static int sched_balance_rq(int this_cpu, struct rq *this_rq,
goto out_balanced;
}

- WARN_ON_ONCE(busiest == env.dst_rq);
+ SCHED_WARN_ON_ONCE(busiest == env.dst_rq);

update_lb_imbalance_stat(&env, sd, idle);

@@ -13651,7 +13651,7 @@ static int active_load_balance_cpu_stop(void *data)
* we need to fix it. Originally reported by
* Bjorn Helgaas on a 128-CPU setup.
*/
- WARN_ON_ONCE(busiest_rq == target_rq);
+ SCHED_WARN_ON_ONCE(busiest_rq == target_rq);

/* Search for an sd spanning us and the target CPU. */
rcu_read_lock();
@@ -14802,7 +14802,7 @@ bool cfs_prio_less(const struct task_struct *a, const struct task_struct *b,
struct cfs_rq *cfs_rqb;
s64 delta;

- WARN_ON_ONCE(task_rq(b)->core != rq->core);
+ SCHED_WARN_ON_ONCE(task_rq(b)->core != rq->core);

#ifdef CONFIG_FAIR_GROUP_SCHED
/*
@@ -15020,7 +15020,7 @@ static void switched_from_fair(struct rq *rq, struct task_struct *p)

static void switched_to_fair(struct rq *rq, struct task_struct *p)
{
- WARN_ON_ONCE(p->se.sched_delayed);
+ SCHED_WARN_ON_ONCE(p->se.sched_delayed);

attach_task_cfs_rq(p);

@@ -15077,7 +15077,7 @@ static void set_next_task_fair(struct rq *rq, struct task_struct *p, bool first)
if (!first)
return;

- WARN_ON_ONCE(se->sched_delayed);
+ SCHED_WARN_ON_ONCE(se->sched_delayed);

if (hrtick_enabled_fair(rq))
hrtick_start_fair(rq, p);
@@ -15311,7 +15311,7 @@ int sched_group_set_idle(struct task_group *tg, long idle)
rq_lock_irqsave(rq, &rf);

grp_cfs_rq->idle = idle;
- if (WARN_ON_ONCE(was_idle == cfs_rq_is_idle(grp_cfs_rq)))
+ if (SCHED_WARN_ON_ONCE(was_idle == cfs_rq_is_idle(grp_cfs_rq)))
goto next_cpu;

idle_task_delta = grp_cfs_rq->h_nr_queued -
--
2.53.0-Meta