The Linux Kernel Mailing List
 help / color / mirror / Atom feed
* [PATCH 0/2] Restore and fix SCHED_WARN_ON()
@ 2025-10-28 16:50 Bart Van Assche
  2025-10-28 16:50 ` [PATCH 1/2] Revert "sched/debug: Change SCHED_WARN_ON() to WARN_ON_ONCE()" Bart Van Assche
  2025-10-28 16:50 ` [PATCH 2/2] Fix SCHED_WARN_ON() Bart Van Assche
  0 siblings, 2 replies; 5+ messages in thread
From: Bart Van Assche @ 2025-10-28 16:50 UTC (permalink / raw)
  To: Ingo Molnar; +Cc: linux-kernel, Peter Zijlstra, Bart Van Assche

Hi Ingo,

Using WARN_ON_ONCE() or any other code that calls console_lock() from the
process scheduler may trigger lock inversion. This patch series fixes this
by restoring the SCHED_WARN_ON() macro and by changing its implementation.

Please consider this patch series for the next merge window.

Thanks,

Bart.

Bart Van Assche (2):
  Revert "sched/debug: Change SCHED_WARN_ON() to WARN_ON_ONCE()"
  Fix SCHED_WARN_ON()

 kernel/sched/core.c       | 22 ++++++++--------
 kernel/sched/core_sched.c |  2 +-
 kernel/sched/deadline.c   | 12 ++++-----
 kernel/sched/ext.c        |  2 +-
 kernel/sched/fair.c       | 54 +++++++++++++++++++--------------------
 kernel/sched/rt.c         |  2 +-
 kernel/sched/sched.h      | 26 +++++++++++++++----
 kernel/sched/stats.h      |  2 +-
 8 files changed, 69 insertions(+), 53 deletions(-)


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH 1/2] Revert "sched/debug: Change SCHED_WARN_ON() to WARN_ON_ONCE()"
  2025-10-28 16:50 [PATCH 0/2] Restore and fix SCHED_WARN_ON() Bart Van Assche
@ 2025-10-28 16:50 ` Bart Van Assche
  2025-10-28 16:50 ` [PATCH 2/2] Fix SCHED_WARN_ON() Bart Van Assche
  1 sibling, 0 replies; 5+ messages in thread
From: Bart Van Assche @ 2025-10-28 16:50 UTC (permalink / raw)
  To: Ingo Molnar
  Cc: linux-kernel, Peter Zijlstra, Bart Van Assche, Juri Lelli,
	Vincent Guittot

Prepare for modifying the SCHED_WARN_ON() implementation.

This reverts commit f7d2728cc032a23fccb5ecde69793a38eb30ba5c.

Signed-off-by: Bart Van Assche <bvanassche@acm.org>
---
 kernel/sched/core.c       | 22 ++++++++--------
 kernel/sched/core_sched.c |  2 +-
 kernel/sched/deadline.c   | 12 ++++-----
 kernel/sched/ext.c        |  2 +-
 kernel/sched/fair.c       | 54 +++++++++++++++++++--------------------
 kernel/sched/rt.c         |  2 +-
 kernel/sched/sched.h      | 12 +++++----
 kernel/sched/stats.h      |  2 +-
 8 files changed, 55 insertions(+), 53 deletions(-)

diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 67b5f2faab36..90da6857a252 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -839,7 +839,7 @@ void update_rq_clock(struct rq *rq)
 		return;
 
 	if (sched_feat(WARN_DOUBLE_CLOCK))
-		WARN_ON_ONCE(rq->clock_update_flags & RQCF_UPDATED);
+		SCHED_WARN_ON(rq->clock_update_flags & RQCF_UPDATED);
 	rq->clock_update_flags |= RQCF_UPDATED;
 
 	clock = sched_clock_cpu(cpu_of(rq));
@@ -1734,7 +1734,7 @@ static inline void uclamp_rq_dec_id(struct rq *rq, struct task_struct *p,
 
 	bucket = &uc_rq->bucket[uc_se->bucket_id];
 
-	WARN_ON_ONCE(!bucket->tasks);
+	SCHED_WARN_ON(!bucket->tasks);
 	if (likely(bucket->tasks))
 		bucket->tasks--;
 
@@ -1754,7 +1754,7 @@ static inline void uclamp_rq_dec_id(struct rq *rq, struct task_struct *p,
 	 * Defensive programming: this should never happen. If it happens,
 	 * e.g. due to future modification, warn and fix up the expected value.
 	 */
-	WARN_ON_ONCE(bucket->value > rq_clamp);
+	SCHED_WARN_ON(bucket->value > rq_clamp);
 	if (bucket->value >= rq_clamp) {
 		bkt_clamp = uclamp_rq_max_value(rq, clamp_id, uc_se->value);
 		uclamp_rq_set(rq, clamp_id, bkt_clamp);
@@ -2141,7 +2141,7 @@ void activate_task(struct rq *rq, struct task_struct *p, int flags)
 
 void deactivate_task(struct rq *rq, struct task_struct *p, int flags)
 {
-	WARN_ON_ONCE(flags & DEQUEUE_SLEEP);
+	SCHED_WARN_ON(flags & DEQUEUE_SLEEP);
 
 	WRITE_ONCE(p->on_rq, TASK_ON_RQ_MIGRATING);
 	ASSERT_EXCLUSIVE_WRITER(p->on_rq);
@@ -4092,7 +4092,7 @@ int try_to_wake_up(struct task_struct *p, unsigned int state, int wake_flags)
 		 *  - we're serialized against set_special_state() by virtue of
 		 *    it disabling IRQs (this allows not taking ->pi_lock).
 		 */
-		WARN_ON_ONCE(p->se.sched_delayed);
+		SCHED_WARN_ON(p->se.sched_delayed);
 		if (!ttwu_state_match(p, state, &success))
 			goto out;
 
@@ -4380,7 +4380,7 @@ static void __sched_fork(u64 clone_flags, struct task_struct *p)
 	INIT_LIST_HEAD(&p->se.group_node);
 
 	/* A delayed task cannot be in clone(). */
-	WARN_ON_ONCE(p->se.sched_delayed);
+	SCHED_WARN_ON(p->se.sched_delayed);
 
 #ifdef CONFIG_FAIR_GROUP_SCHED
 	p->se.cfs_rq			= NULL;
@@ -5612,7 +5612,7 @@ static void sched_tick_remote(struct work_struct *work)
 			 * we are always sure that there is no proxy (only a
 			 * single task is running).
 			 */
-			WARN_ON_ONCE(rq->curr != rq->donor);
+			SCHED_WARN_ON(rq->curr != rq->donor);
 			update_rq_clock(rq);
 
 			if (!is_idle_task(curr)) {
@@ -5832,7 +5832,7 @@ static inline void schedule_debug(struct task_struct *prev, bool preempt)
 		preempt_count_set(PREEMPT_DISABLED);
 	}
 	rcu_sleep_check();
-	WARN_ON_ONCE(ct_state() == CT_STATE_USER);
+	SCHED_WARN_ON(ct_state() == CT_STATE_USER);
 
 	profile_hit(SCHED_PROFILING, __builtin_return_address(0));
 
@@ -6914,7 +6914,7 @@ static inline void sched_submit_work(struct task_struct *tsk)
 	 * deadlock if the callback attempts to acquire a lock which is
 	 * already acquired.
 	 */
-	WARN_ON_ONCE(current->__state & TASK_RTLOCK_WAIT);
+	SCHED_WARN_ON(current->__state & TASK_RTLOCK_WAIT);
 
 	/*
 	 * If we are going to sleep and we have plugged IO queued,
@@ -9235,7 +9235,7 @@ static void cpu_util_update_eff(struct cgroup_subsys_state *css)
 	unsigned int clamps;
 
 	lockdep_assert_held(&uclamp_mutex);
-	WARN_ON_ONCE(!rcu_read_lock_held());
+	SCHED_WARN_ON(!rcu_read_lock_held());
 
 	css_for_each_descendant_pre(css, top_css) {
 		uc_parent = css_tg(css)->parent
@@ -10641,7 +10641,7 @@ static void task_mm_cid_work(struct callback_head *work)
 	struct mm_struct *mm;
 	int weight, cpu;
 
-	WARN_ON_ONCE(t != container_of(work, struct task_struct, cid_work));
+	SCHED_WARN_ON(t != container_of(work, struct task_struct, cid_work));
 
 	work->next = work;	/* Prevent double-add */
 	if (t->flags & PF_EXITING)
diff --git a/kernel/sched/core_sched.c b/kernel/sched/core_sched.c
index 9ede71ecba7f..2e9037774d97 100644
--- a/kernel/sched/core_sched.c
+++ b/kernel/sched/core_sched.c
@@ -67,7 +67,7 @@ static unsigned long sched_core_update_cookie(struct task_struct *p,
 	 * a cookie until after we've removed it, we must have core scheduling
 	 * enabled here.
 	 */
-	WARN_ON_ONCE((p->core_cookie || cookie) && !sched_core_enabled(rq));
+	SCHED_WARN_ON((p->core_cookie || cookie) && !sched_core_enabled(rq));
 
 	if (sched_core_enqueued(p))
 		sched_core_dequeue(rq, p, DEQUEUE_SAVE);
diff --git a/kernel/sched/deadline.c b/kernel/sched/deadline.c
index 6b8a9286e2fc..05c1c2d16329 100644
--- a/kernel/sched/deadline.c
+++ b/kernel/sched/deadline.c
@@ -223,8 +223,8 @@ void __add_running_bw(u64 dl_bw, struct dl_rq *dl_rq)
 
 	lockdep_assert_rq_held(rq_of_dl_rq(dl_rq));
 	dl_rq->running_bw += dl_bw;
-	WARN_ON_ONCE(dl_rq->running_bw < old); /* overflow */
-	WARN_ON_ONCE(dl_rq->running_bw > dl_rq->this_bw);
+	SCHED_WARN_ON(dl_rq->running_bw < old); /* overflow */
+	SCHED_WARN_ON(dl_rq->running_bw > dl_rq->this_bw);
 	/* kick cpufreq (see the comment in kernel/sched/sched.h). */
 	cpufreq_update_util(rq_of_dl_rq(dl_rq), 0);
 }
@@ -236,7 +236,7 @@ void __sub_running_bw(u64 dl_bw, struct dl_rq *dl_rq)
 
 	lockdep_assert_rq_held(rq_of_dl_rq(dl_rq));
 	dl_rq->running_bw -= dl_bw;
-	WARN_ON_ONCE(dl_rq->running_bw > old); /* underflow */
+	SCHED_WARN_ON(dl_rq->running_bw > old); /* underflow */
 	if (dl_rq->running_bw > old)
 		dl_rq->running_bw = 0;
 	/* kick cpufreq (see the comment in kernel/sched/sched.h). */
@@ -250,7 +250,7 @@ void __add_rq_bw(u64 dl_bw, struct dl_rq *dl_rq)
 
 	lockdep_assert_rq_held(rq_of_dl_rq(dl_rq));
 	dl_rq->this_bw += dl_bw;
-	WARN_ON_ONCE(dl_rq->this_bw < old); /* overflow */
+	SCHED_WARN_ON(dl_rq->this_bw < old); /* overflow */
 }
 
 static inline
@@ -260,10 +260,10 @@ void __sub_rq_bw(u64 dl_bw, struct dl_rq *dl_rq)
 
 	lockdep_assert_rq_held(rq_of_dl_rq(dl_rq));
 	dl_rq->this_bw -= dl_bw;
-	WARN_ON_ONCE(dl_rq->this_bw > old); /* underflow */
+	SCHED_WARN_ON(dl_rq->this_bw > old); /* underflow */
 	if (dl_rq->this_bw > old)
 		dl_rq->this_bw = 0;
-	WARN_ON_ONCE(dl_rq->running_bw > dl_rq->this_bw);
+	SCHED_WARN_ON(dl_rq->running_bw > dl_rq->this_bw);
 }
 
 static inline
diff --git a/kernel/sched/ext.c b/kernel/sched/ext.c
index b063444dc003..4c1fba4dcd61 100644
--- a/kernel/sched/ext.c
+++ b/kernel/sched/ext.c
@@ -1574,7 +1574,7 @@ static bool task_can_run_on_remote_rq(struct scx_sched *sch,
 {
 	int cpu = cpu_of(rq);
 
-	WARN_ON_ONCE(task_cpu(p) == cpu);
+	SCHED_WARN_ON(task_cpu(p) == cpu);
 
 	/*
 	 * If @p has migration disabled, @p->cpus_ptr is updated to contain only
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index 273e2871b59e..ce89554fc3c1 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -397,7 +397,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(rq->tmp_alone_branch != &rq->leaf_cfs_rq_list);
 }
 
 /* Iterate through all leaf cfs_rq's on a runqueue */
@@ -694,7 +694,7 @@ static void update_entity_lag(struct cfs_rq *cfs_rq, struct sched_entity *se)
 {
 	s64 vlag, limit;
 
-	WARN_ON_ONCE(!se->on_rq);
+	SCHED_WARN_ON(!se->on_rq);
 
 	vlag = avg_vruntime(cfs_rq) - se->vruntime;
 	limit = calc_delta_fair(max_t(u64, 2*se->slice, TICK_NSEC), se);
@@ -3299,7 +3299,7 @@ static void task_numa_work(struct callback_head *work)
 	bool vma_pids_skipped;
 	bool vma_pids_forced = false;
 
-	WARN_ON_ONCE(p != container_of(work, struct task_struct, numa_work));
+	SCHED_WARN_ON(p != container_of(work, struct task_struct, numa_work));
 
 	work->next = work;
 	/*
@@ -4007,7 +4007,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(sa->load_avg ||
 		      sa->util_avg ||
 		      sa->runnable_avg);
 
@@ -5376,7 +5376,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(!se->sched_delayed);
 	} else {
 		bool delay = sleep;
 		/*
@@ -5386,7 +5386,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(delay && se->sched_delayed);
 
 		if (sched_feat(DELAY_DEQUEUE) && delay &&
 		    !entity_eligible(cfs_rq, se)) {
@@ -5477,7 +5477,7 @@ set_next_entity(struct cfs_rq *cfs_rq, struct sched_entity *se)
 	}
 
 	update_stats_curr_start(cfs_rq, se);
-	WARN_ON_ONCE(cfs_rq->curr);
+	SCHED_WARN_ON(cfs_rq->curr);
 	cfs_rq->curr = se;
 
 	/*
@@ -5518,7 +5518,7 @@ pick_next_entity(struct rq *rq, struct cfs_rq *cfs_rq)
 	if (sched_feat(PICK_BUDDY) &&
 	    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(cfs_rq->next->sched_delayed);
 		return cfs_rq->next;
 	}
 
@@ -5554,7 +5554,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(cfs_rq->curr != prev);
 	cfs_rq->curr = NULL;
 }
 
@@ -6013,7 +6013,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(cfs_rq->throttled_clock);
 	return true;
 }
 
@@ -6123,7 +6123,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(!list_empty(&cfs_rq->throttled_csd_list)))
 		return;
 
 	first = list_empty(&rq->cfsb_csd_list);
@@ -6136,7 +6136,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(!cfs_rq_throttled(cfs_rq) ||
 	    cfs_rq->runtime_remaining <= 0))
 		return;
 
@@ -6172,7 +6172,7 @@ static bool distribute_cfs_runtime(struct cfs_bandwidth *cfs_b)
 			goto next;
 
 		/* By the above checks, this should never be true */
-		WARN_ON_ONCE(cfs_rq->runtime_remaining > 0);
+		SCHED_WARN_ON(cfs_rq->runtime_remaining > 0);
 
 		raw_spin_lock(&cfs_b->lock);
 		runtime = -cfs_rq->runtime_remaining + 1;
@@ -6193,7 +6193,7 @@ static bool distribute_cfs_runtime(struct cfs_bandwidth *cfs_b)
 				 * We currently only expect to be unthrottling
 				 * a single cfs_rq locally.
 				 */
-				WARN_ON_ONCE(!list_empty(&local_unthrottle));
+				SCHED_WARN_ON(!list_empty(&local_unthrottle));
 				list_add_tail(&cfs_rq->throttled_csd_list,
 					      &local_unthrottle);
 			}
@@ -6218,7 +6218,7 @@ static bool distribute_cfs_runtime(struct cfs_bandwidth *cfs_b)
 
 		rq_unlock_irqrestore(rq, &rf);
 	}
-	WARN_ON_ONCE(!list_empty(&local_unthrottle));
+	SCHED_WARN_ON(!list_empty(&local_unthrottle));
 
 	rcu_read_unlock();
 
@@ -6786,7 +6786,7 @@ static void hrtick_start_fair(struct rq *rq, struct task_struct *p)
 {
 	struct sched_entity *se = &p->se;
 
-	WARN_ON_ONCE(task_rq(p) != rq);
+	SCHED_WARN_ON(task_rq(p) != rq);
 
 	if (rq->cfs.h_nr_queued > 1) {
 		u64 ran = se->sum_exec_runtime - se->prev_sum_exec_runtime;
@@ -6891,8 +6891,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(!se->sched_delayed);
+	SCHED_WARN_ON(!se->on_rq);
 
 	if (sched_feat(DELAY_ZERO)) {
 		update_entity_lag(cfs_rq, se);
@@ -7139,8 +7139,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(!task_sleep);
+		SCHED_WARN_ON(p->on_rq != 1);
 
 		/* Fix-up what dequeue_task_fair() skipped */
 		hrtick_update(rq);
@@ -8718,7 +8718,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(!se->on_rq))
 			return;
 		if (se_is_idle(se))
 			return;
@@ -12488,7 +12488,7 @@ static void set_cpu_sd_state_busy(int cpu)
 
 void nohz_balance_exit_idle(struct rq *rq)
 {
-	WARN_ON_ONCE(rq != this_rq());
+	SCHED_WARN_ON(rq != this_rq());
 
 	if (likely(!rq->nohz_tick_stopped))
 		return;
@@ -12524,7 +12524,7 @@ void nohz_balance_enter_idle(int cpu)
 {
 	struct rq *rq = cpu_rq(cpu);
 
-	WARN_ON_ONCE(cpu != smp_processor_id());
+	SCHED_WARN_ON(cpu != smp_processor_id());
 
 	/* If this CPU is going down, then nothing needs to be done: */
 	if (!cpu_active(cpu))
@@ -12607,7 +12607,7 @@ static void _nohz_idle_balance(struct rq *this_rq, unsigned int flags)
 	int balance_cpu;
 	struct rq *rq;
 
-	WARN_ON_ONCE((flags & NOHZ_KICK_MASK) == NOHZ_BALANCE_KICK);
+	SCHED_WARN_ON((flags & NOHZ_KICK_MASK) == NOHZ_BALANCE_KICK);
 
 	/*
 	 * We assume there will be no idle load after this update and clear
@@ -13053,7 +13053,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(task_rq(b)->core != rq->core);
 
 #ifdef CONFIG_FAIR_GROUP_SCHED
 	/*
@@ -13266,7 +13266,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(p->se.sched_delayed);
 
 	attach_task_cfs_rq(p);
 
@@ -13299,7 +13299,7 @@ static void __set_next_task_fair(struct rq *rq, struct task_struct *p, bool firs
 	if (!first)
 		return;
 
-	WARN_ON_ONCE(se->sched_delayed);
+	SCHED_WARN_ON(se->sched_delayed);
 
 	if (hrtick_enabled_fair(rq))
 		hrtick_start_fair(rq, p);
diff --git a/kernel/sched/rt.c b/kernel/sched/rt.c
index 1fd97f2d7ec6..61c364219735 100644
--- a/kernel/sched/rt.c
+++ b/kernel/sched/rt.c
@@ -1673,7 +1673,7 @@ static struct sched_rt_entity *pick_next_rt_entity(struct rt_rq *rt_rq)
 	BUG_ON(idx >= MAX_RT_PRIO);
 
 	queue = array->queue + idx;
-	if (WARN_ON_ONCE(list_empty(queue)))
+	if (SCHED_WARN_ON(list_empty(queue)))
 		return NULL;
 	next = list_entry(queue->next, struct sched_rt_entity, run_list);
 
diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
index d04e007608a3..e289d364459e 100644
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -91,6 +91,8 @@ struct cpuidle_state;
 #include "cpupri.h"
 #include "cpudeadline.h"
 
+#define SCHED_WARN_ON(x) WARN_ONCE(x, #x)
+
 /* task_struct::on_rq states: */
 #define TASK_ON_RQ_QUEUED	1
 #define TASK_ON_RQ_MIGRATING	2
@@ -1597,7 +1599,7 @@ static inline void update_idle_core(struct rq *rq) { }
 
 static inline struct task_struct *task_of(struct sched_entity *se)
 {
-	WARN_ON_ONCE(!entity_is_task(se));
+	SCHED_WARN_ON(!entity_is_task(se));
 	return container_of(se, struct task_struct, se);
 }
 
@@ -1678,7 +1680,7 @@ static inline void assert_clock_updated(struct rq *rq)
 	 * The only reason for not seeing a clock update since the
 	 * last rq_pin_lock() is if we're currently skipping updates.
 	 */
-	WARN_ON_ONCE(rq->clock_update_flags < RQCF_ACT_SKIP);
+	SCHED_WARN_ON(rq->clock_update_flags < RQCF_ACT_SKIP);
 }
 
 static inline u64 rq_clock(struct rq *rq)
@@ -1725,7 +1727,7 @@ static inline void rq_clock_cancel_skipupdate(struct rq *rq)
 static inline void rq_clock_start_loop_update(struct rq *rq)
 {
 	lockdep_assert_rq_held(rq);
-	WARN_ON_ONCE(rq->clock_update_flags & RQCF_ACT_SKIP);
+	SCHED_WARN_ON(rq->clock_update_flags & RQCF_ACT_SKIP);
 	rq->clock_update_flags |= RQCF_ACT_SKIP;
 }
 
@@ -1796,7 +1798,7 @@ static inline void rq_pin_lock(struct rq *rq, struct rq_flags *rf)
 
 	rq->clock_update_flags &= (RQCF_REQ_SKIP|RQCF_ACT_SKIP);
 	rf->clock_update_flags = 0;
-	WARN_ON_ONCE(rq->balance_callback && rq->balance_callback != &balance_push_callback);
+	SCHED_WARN_ON(rq->balance_callback && rq->balance_callback != &balance_push_callback);
 }
 
 static inline void rq_unpin_lock(struct rq *rq, struct rq_flags *rf)
@@ -2778,7 +2780,7 @@ static inline void idle_set_state(struct rq *rq,
 
 static inline struct cpuidle_state *idle_get_state(struct rq *rq)
 {
-	WARN_ON_ONCE(!rcu_read_lock_held());
+	SCHED_WARN_ON(!rcu_read_lock_held());
 
 	return rq->idle_state;
 }
diff --git a/kernel/sched/stats.h b/kernel/sched/stats.h
index cbf7206b3f9d..0e015cc4f68c 100644
--- a/kernel/sched/stats.h
+++ b/kernel/sched/stats.h
@@ -144,7 +144,7 @@ static inline void psi_enqueue(struct task_struct *p, int flags)
 
 	if (p->se.sched_delayed) {
 		/* CPU migration of "sleeping" task */
-		WARN_ON_ONCE(!(flags & ENQUEUE_MIGRATED));
+		SCHED_WARN_ON(!(flags & ENQUEUE_MIGRATED));
 		if (p->in_memstall)
 			set |= TSK_MEMSTALL;
 		if (p->in_iowait)

^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH 2/2] Fix SCHED_WARN_ON()
  2025-10-28 16:50 [PATCH 0/2] Restore and fix SCHED_WARN_ON() Bart Van Assche
  2025-10-28 16:50 ` [PATCH 1/2] Revert "sched/debug: Change SCHED_WARN_ON() to WARN_ON_ONCE()" Bart Van Assche
@ 2025-10-28 16:50 ` Bart Van Assche
  2025-10-28 18:45   ` Peter Zijlstra
  2025-10-28 18:50   ` Peter Zijlstra
  1 sibling, 2 replies; 5+ messages in thread
From: Bart Van Assche @ 2025-10-28 16:50 UTC (permalink / raw)
  To: Ingo Molnar
  Cc: linux-kernel, Peter Zijlstra, Bart Van Assche, Juri Lelli,
	Vincent Guittot

SCHED_WARN_ON() uses WARN_ONCE() and WARN_ONCE() uses printk(). Using
printk() from certain code paths in the process scheduler is not allowed
because printk() acquires the console_lock and this may cause printk()
to sleep. Sleeping is not allowed from many paths in the process
scheduler. Hence make sure that SCHED_WARN_ON() uses printk_deferred()
instead of printk().

Here is an example of a lockdep complaint fixed by this patch:

======================================================
WARNING: possible circular locking dependency detected
------------------------------------------------------
swapper/4/0 is trying to acquire lock:
ffffffc082179768 (console_owner){-...}-{0:0}, at: console_lock_spinning_enable+0x44/0x80

but task is already holding lock:
ffffff8b33d30a18 (&rq->__lock){-.-.}-{2:2}, at: __schedule+0x188/0x116c

which lock already depends on the new lock.

the existing dependency chain (in reverse order) is:

-> #5 (&rq->__lock){-.-.}-{2:2}:
       _raw_spin_lock_nested+0x50/0x70
       __task_rq_lock+0x74/0x154
       wake_up_new_task+0x110/0x40c
       kernel_clone+0x28c/0x508
       user_mode_thread+0x60/0x8c
       rest_init+0x28/0x1b8
       start_kernel+0x390/0x4b0

-> #4 (&p->pi_lock){-.-.}-{2:2}:
       _raw_spin_lock_irqsave+0x64/0xac
       try_to_wake_up+0x6c/0xa18
       default_wake_function+0x20/0x38
       autoremove_wake_function+0x1c/0x64
       __wake_up+0xac/0xfc
       rpm_suspend+0x484/0x668
       pm_runtime_work+0x8c/0xa8
       process_one_work+0x26c/0x65c
       worker_thread+0x33c/0x498
       kthread+0x110/0x134
       ret_from_fork+0x10/0x20

-> #3 (&dev->power.wait_queue){....}-{2:2}:
       _raw_spin_lock_irqsave+0x64/0xac
       __wake_up+0x34/0xfc
       rpm_resume+0x4e8/0x5c8
       __pm_runtime_resume+0x6c/0xb0
       dt_idle_attach_cpu+0x70/0x9c
       psci_cpuidle_probe+0x270/0x494
       platform_probe+0xa0/0xe0
       really_probe+0x114/0x454
       __driver_probe_device+0xa4/0x160
       driver_probe_device+0x44/0x23c
       __device_attach_driver+0x15c/0x1f4
       bus_for_each_drv+0x10c/0x168
       __device_attach+0xc0/0x1a0
       device_initial_probe+0x14/0x24
       bus_probe_device+0x94/0x120
       device_add+0x298/0x498
       platform_device_add+0x80/0x24c
       platform_device_register_full+0x144/0x154
       psci_idle_init+0x74/0xc4
       do_one_initcall+0x124/0x464
       do_initcall_level+0xa8/0xd4
       do_initcalls+0x74/0xf0
       do_basic_setup+0x1c/0x28
       kernel_init_freeable+0x154/0x204
       kernel_init+0x20/0x1ac

-> #2 (&dev->power.lock){-.-.}-{2:2}:
       _raw_spin_lock_irqsave+0x64/0xac
       __pm_runtime_resume+0x5c/0xb0
       __uart_start+0x4c/0x120
       uart_write+0xe8/0x304
       n_tty_write+0x310/0x4c8
       file_tty_write+0x154/0x32c
       redirected_tty_write+0x94/0xe0
       vfs_write+0x2e8/0x3ac
       ksys_write+0x78/0xe8
       __arm64_sys_write+0x1c/0x2c
       invoke_syscall+0x58/0x10c
       el0_svc_common+0xa8/0xdc
       do_el0_svc+0x1c/0x28
       el0_svc+0x50/0xd4

-> #1 (&port_lock_key){-...}-{2:2}:
       _raw_spin_lock_irqsave+0x64/0xac
       google_serial8250_console_write+0xe0/0x8f0 [google_8250_base]
       univ8250_console_write+0x2c/0x40 [google_8250]
       console_flush_all+0x210/0x420
       console_unlock+0x84/0x12c
       vprintk_emit+0x12c/0x1b8
       vprintk_default+0x44/0x70
       vprintk+0xe4/0x164
       _printk+0x54/0x80
       register_console+0x428/0x524
       serial_core_register_port+0x664/0x8b8
       serial_ctrl_register_port+0x10/0x20
       uart_add_one_port+0x10/0x20
       google_serial8250_register_8250_port+0x834/0xa1c [google_8250]
       dw8250_probe+0x550/0x5f0 [google_8250_dw]
       platform_probe+0xa0/0xe0
       really_probe+0x114/0x454
       __driver_probe_device+0xa4/0x160
       driver_probe_device+0x44/0x23c
       __driver_attach+0x124/0x278
       bus_for_each_dev+0x104/0x160
       driver_attach+0x24/0x34
       bus_add_driver+0x144/0x2d4
       driver_register+0x68/0x104
       __platform_driver_register+0x20/0x30
       do_one_initcall+0x124/0x464
       do_init_module+0x58/0x2f4
       load_module+0x12c8/0x14b0
       __arm64_sys_finit_module+0x238/0x33c
       invoke_syscall+0x58/0x10c
       el0_svc_common+0xa8/0xdc
       do_el0_svc+0x1c/0x28
       el0_svc+0x50/0xd4

-> #0 (console_owner){-...}-{0:0}:
       __lock_acquire+0x150c/0x2e24
       lock_acquire+0x13c/0x2f0
       console_lock_spinning_enable+0x6c/0x80
       console_flush_all+0x1dc/0x420
       console_unlock+0x84/0x12c
       vprintk_emit+0x12c/0x1b8
       vprintk_default+0x44/0x70
       vprintk+0xe4/0x164
       _printk+0x54/0x80
       __warn_printk+0x10c/0x1d4
       sub_running_bw+0x164/0x1a8
       task_non_contending+0x29c/0x4b0
       dequeue_dl_entity+0x1f0/0x260
       pick_task_dl+0x7c/0x1c4
       __schedule+0x554/0x116c
       schedule_idle+0x24/0x48
       do_idle+0x2f0/0x338
       cpu_startup_entry+0x34/0x3c

other info that might help us debug this:

Chain exists of:
  console_owner --> &p->pi_lock --> &rq->__lock

 Possible unsafe locking scenario:

       CPU0                    CPU1
       ----                    ----
  lock(&rq->__lock);
                               lock(&p->pi_lock);
                               lock(&rq->__lock);
  lock(console_owner);

 *** DEADLOCK ***

3 locks held by swapper/4/0:
 #0: ffffff8b33d30a18 (&rq->__lock){-.-.}-{2:2}, at: __schedule+0x188/0x116c
 #1: ffffffc082179790 (console_lock){+.+.}-{0:0}, at: vprintk_emit+0x124/0x1b8
 #2: ffffffc082109238 (console_srcu){....}-{0:0}, at: rcu_try_lock_acquire+0x0/0x40

stack backtrace:
Call trace:
 dump_backtrace+0xfc/0x17c
 show_stack+0x18/0x28
 dump_stack_lvl+0x40/0x104
 dump_stack+0x18/0x3c
 print_circular_bug+0x32c/0x334
 check_noncircular+0x158/0x178
 __lock_acquire+0x150c/0x2e24
 lock_acquire+0x13c/0x2f0
 console_lock_spinning_enable+0x6c/0x80
 console_flush_all+0x1dc/0x420
 console_unlock+0x84/0x12c
 vprintk_emit+0x12c/0x1b8
 vprintk_default+0x44/0x70
 vprintk+0xe4/0x164
 _printk+0x54/0x80
 __warn_printk+0x10c/0x1d4
 sub_running_bw+0x164/0x1a8
 task_non_contending+0x29c/0x4b0
 dequeue_dl_entity+0x1f0/0x260
 pick_task_dl+0x7c/0x1c4
 __schedule+0x554/0x116c
 schedule_idle+0x24/0x48
 do_idle+0x2f0/0x338
 cpu_startup_entry+0x34/0x3c
 secondary_start_kernel+0x138/0x1a8
 __secondary_switched+0xc0/0xc4

Signed-off-by: Bart Van Assche <bvanassche@acm.org>
---
 kernel/sched/sched.h | 16 +++++++++++++++-
 1 file changed, 15 insertions(+), 1 deletion(-)

diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
index e289d364459e..34a9c473104a 100644
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -91,7 +91,21 @@ struct cpuidle_state;
 #include "cpupri.h"
 #include "cpudeadline.h"
 
-#define SCHED_WARN_ON(x) WARN_ONCE(x, #x)
+#define SCHED_WARN_ON(x)				\
+	({						\
+		bool __ret = (x);			\
+							\
+		if (unlikely(__ret)) {			\
+			unsigned long __flags;		\
+							\
+			local_irq_save(__flags);	\
+			printk_deferred_enter();	\
+			WARN_ONCE(__ret, #x);		\
+			printk_deferred_exit();		\
+			local_irq_restore(__flags);	\
+		}					\
+		unlikely(__ret);			\
+	})
 
 /* task_struct::on_rq states: */
 #define TASK_ON_RQ_QUEUED	1

^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH 2/2] Fix SCHED_WARN_ON()
  2025-10-28 16:50 ` [PATCH 2/2] Fix SCHED_WARN_ON() Bart Van Assche
@ 2025-10-28 18:45   ` Peter Zijlstra
  2025-10-28 18:50   ` Peter Zijlstra
  1 sibling, 0 replies; 5+ messages in thread
From: Peter Zijlstra @ 2025-10-28 18:45 UTC (permalink / raw)
  To: Bart Van Assche; +Cc: Ingo Molnar, linux-kernel, Juri Lelli, Vincent Guittot

On Tue, Oct 28, 2025 at 09:50:58AM -0700, Bart Van Assche wrote:
> SCHED_WARN_ON() uses WARN_ONCE() and WARN_ONCE() uses printk(). Using
> printk() from certain code paths in the process scheduler is not allowed
> because printk() acquires the console_lock and this may cause printk()
> to sleep. Sleeping is not allowed from many paths in the process
> scheduler. Hence make sure that SCHED_WARN_ON() uses printk_deferred()
> instead of printk().

Yeah, so no. I hate that deferred thing with a passion. It means your
warning will never make it out if something fatal happens.

The printk() people have been working on fixing this for a long while
and I think they're close. All the crap consoles will get threaded
printing while the useful consoles (serial) will get atomic printing.

Also, you should never hit these WARNs to begin with, and if you do,
that lockdep splat is the least of your problems.

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH 2/2] Fix SCHED_WARN_ON()
  2025-10-28 16:50 ` [PATCH 2/2] Fix SCHED_WARN_ON() Bart Van Assche
  2025-10-28 18:45   ` Peter Zijlstra
@ 2025-10-28 18:50   ` Peter Zijlstra
  1 sibling, 0 replies; 5+ messages in thread
From: Peter Zijlstra @ 2025-10-28 18:50 UTC (permalink / raw)
  To: Bart Van Assche; +Cc: Ingo Molnar, linux-kernel, Juri Lelli, Vincent Guittot

On Tue, Oct 28, 2025 at 09:50:58AM -0700, Bart Van Assche wrote:


> +#define SCHED_WARN_ON(x)				\
> +	({						\
> +		bool __ret = (x);			\
> +							\
> +		if (unlikely(__ret)) {			\
> +			unsigned long __flags;		\
> +							\
> +			local_irq_save(__flags);	\
> +			printk_deferred_enter();	\
> +			WARN_ONCE(__ret, #x);		\
> +			printk_deferred_exit();		\
> +			local_irq_restore(__flags);	\
> +		}					\
> +		unlikely(__ret);			\
> +	})

Also, this will generate absolutely atrocious code.

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2025-10-28 18:50 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-10-28 16:50 [PATCH 0/2] Restore and fix SCHED_WARN_ON() Bart Van Assche
2025-10-28 16:50 ` [PATCH 1/2] Revert "sched/debug: Change SCHED_WARN_ON() to WARN_ON_ONCE()" Bart Van Assche
2025-10-28 16:50 ` [PATCH 2/2] Fix SCHED_WARN_ON() Bart Van Assche
2025-10-28 18:45   ` Peter Zijlstra
2025-10-28 18:50   ` Peter Zijlstra

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox