From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from shelob.surriel.com (shelob.surriel.com [96.67.55.147]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 461193090DE for ; Thu, 11 Jun 2026 02:14:52 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=96.67.55.147 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781144098; cv=none; b=iTRY/8NqC95WF/cGVpzi8EYwwVl+MEZyo/8OsRDVeDWZSh6AxWkX2FKtsDCr8YWUddFjCePAG8KfABKUPWb1nntau1mnuT6gmnm/P9hKMwxJ2YwXz5XZx/nXu8X7F1FtW5Otii/NrPXo37/KBTzzwu4mF46bEZmnanzrcIbPjFA= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781144098; c=relaxed/simple; bh=pZ2OMyX9kAbU+OP7QFlfNxHrEn13ZDwahR6MX4iDr4U=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=XRUzT9VUxH0mG+qSumhCUiqov4XWJl+98gTbKnYOmWSxgl3eLa6IDKo0mftRIqAHvDG1NZ+2kjO7aXLCk5R8EDYxAPftq9bsxqzWgZB/WHz00LmXrA9NNYbDE5P0Yi14tTxdciR//o7qVn8ZjKSabKS3OXoqWnFCTdlMDeEVPL0= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=surriel.com; spf=pass smtp.mailfrom=surriel.com; dkim=pass (2048-bit key) header.d=surriel.com header.i=@surriel.com header.b=dgPdeYQx; arc=none smtp.client-ip=96.67.55.147 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=surriel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=surriel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=surriel.com header.i=@surriel.com header.b="dgPdeYQx" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=surriel.com ; s=mail; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To: Message-ID:Date:Subject:Cc:To:From:Sender:Reply-To:Content-Type:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=cIKmPe8koayPfts5BlDOg/bnnQqndk0UCEnsMcgaVX8=; b=dgPdeYQxTBtByaXhaQXmJFrux2 lJuMAmr73a0qrZOItdVq8oaNk5FN3+hN+9UCe7dLsc3uFbDW+k3B/VYqf6Lyh9Aq8Bib330+v+U1+ se1PSP+NB0EdDSWnx4FEBgLWjd2G0USxW5xyB5BD0iyNpaHmW8Mil+cJ7RT4jHGrpXCPdWJUB/aIO gyeMAnImzj9Oth+TlaXw1A0WuctMO3VpU4ESFnEkilU5MjzQwuXfPGfMDXnhZ9XaHf0mzShKW+HyG CtYaNXNv8XERSqDHeiV/nlliVk8aPfqkaSe/zSRgInslR/K1sOm3G1IThispgBLGKPp8zGV2Xw5eE KsR/241w==; Received: from fangorn.home.surriel.com ([10.0.13.7]) by shelob.surriel.com with esmtpsa (TLS1.2) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.97.1) (envelope-from ) id 1wXUwJ-000000003Zo-0Axa; Wed, 10 Jun 2026 22:14:39 -0400 From: Rik van Riel To: linux-kernel@vger.kernel.org Cc: kernel-team@meta.com, mingo@redhat.com, peterz@infradead.org, juri.lelli@redhat.com, vincent.guittot@linaro.org, dietmar.eggemann@arm.com, vschneid@redhat.com, Rik van Riel Subject: [PATCH 1/9] sched: add SCHED_WARN_ON()/SCHED_WARN_ON_ONCE()/SCHED_WARN()/SCHED_WARN_ONCE() Date: Wed, 10 Jun 2026 22:14:08 -0400 Message-ID: <20260611021416.910555-2-riel@surriel.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260611021416.910555-1-riel@surriel.com> References: <20260611021416.910555-1-riel@surriel.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit WARN_ON(), WARN_ON_ONCE(), WARN() and WARN_ONCE() emit at KERN_WARNING. With a legacy (or boot) console registered, vprintk_emit() takes the synchronous "legacy_direct" path: console_trylock_spinning() + console_unlock() -> up(&console_sem) -> wake_up_process() -> try_to_wake_up(), which acquires the woken task's ->pi_lock and its rq->lock. If the WARN fires while the current CPU already holds an rq->lock or a ->pi_lock -- the case for almost every WARN in the scheduling-class hot paths -- this re-enters the scheduler and can deadlock (recursively on rq->lock, or via the pi_lock/rq->lock ordering). The nbcon and klogd wakeups are deferred via irq_work and are safe; only the legacy console path is synchronous. Plain WARN*() emit at KERN_WARNING rather than LOGLEVEL_SCHED, so they do not get the scheduler-safe deferral that printk_deferred() does. Add SCHED_WARN_ON()/SCHED_WARN_ON_ONCE() (and the SCHED_WARN()/ SCHED_WARN_ONCE() forms that carry a format message), which behave exactly like their WARN*() counterparts but bracket the report in a printk_deferred section, so the console output is handed to irq_work instead of being emitted synchronously. The bracket is entered only on the (cold) firing path, so the hot path cost is unchanged: just the condition test. printk_deferred_enter()/exit() toggle a per-CPU counter and must be balanced on one CPU, so the caller must have preemption disabled. That is always true while an rq->lock or ->pi_lock (both raw_spinlock_t, which disable preemption) is held. A lockdep_assert_preemption_disabled() guards against misuse from preemptible context and compiles away without CONFIG_PROVE_LOCKING. WARN_ON()/WARN_ON_ONCE() do not stringify their condition, so passing the already-evaluated result as a constant produces identical console output; the condition is evaluated exactly once and its boolean result is returned, preserving the semantics for callers that test it. No conversions are done here; this only adds the macros. Signed-off-by: Rik van Riel Assisted-by: Claude:claude-opus-4-8 --- kernel/sched/sched.h | 52 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index c7c2dea65edd..60739ccfc32f 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -44,6 +44,7 @@ #include #include #include +#include #include #include #include @@ -99,6 +100,57 @@ struct cpuidle_state; #define TASK_ON_RQ_QUEUED 1 #define TASK_ON_RQ_MIGRATING 2 +/* + * SCHED_WARN_ON() / SCHED_WARN_ON_ONCE() / SCHED_WARN() / SCHED_WARN_ONCE(): + * WARN_ON() / WARN_ON_ONCE() / WARN() / WARN_ONCE() variants that are safe to + * call while holding an rq->lock or a task's ->pi_lock. + * + * A plain WARN emits at KERN_WARNING. With a legacy console registered, the + * printk takes the synchronous path console_unlock() -> up(&console_sem) -> + * wake_up_process() -> try_to_wake_up(), which grabs ->pi_lock and rq->lock -- + * and so deadlocks if such a lock is already held by the WARNing context. + * + * Bracket the report in a printk_deferred section so the console output is + * handed to irq_work instead. This is done only on the (cold) firing path, so + * the hot path keeps just the condition test. printk_deferred_enter()/exit() + * toggle a per-CPU counter and must be balanced on one CPU; the caller must + * therefore have preemption disabled, which is always true while an rq/pi + * raw_spinlock is held. The lockdep assert catches misuse from preemptible + * context and compiles away without CONFIG_PROVE_LOCKING. + */ +#define __SCHED_WARN_DEFERRED(__warn, x) \ +({ \ + int __ret = !!(x); \ + \ + lockdep_assert_preemption_disabled(); \ + if (unlikely(__ret)) { \ + printk_deferred_enter(); \ + __warn(1); \ + printk_deferred_exit(); \ + } \ + __ret; \ +}) + +#define SCHED_WARN_ON(x) __SCHED_WARN_DEFERRED(WARN_ON, x) +#define SCHED_WARN_ON_ONCE(x) __SCHED_WARN_DEFERRED(WARN_ON_ONCE, x) + +/* As above, for the WARN()/WARN_ONCE() forms that carry a format message. */ +#define __SCHED_WARN_FMT_DEFERRED(__warn, x, fmt...) \ +({ \ + int __ret = !!(x); \ + \ + lockdep_assert_preemption_disabled(); \ + if (unlikely(__ret)) { \ + printk_deferred_enter(); \ + __warn(1, fmt); \ + printk_deferred_exit(); \ + } \ + __ret; \ +}) + +#define SCHED_WARN(x, fmt...) __SCHED_WARN_FMT_DEFERRED(WARN, x, fmt) +#define SCHED_WARN_ONCE(x, fmt...) __SCHED_WARN_FMT_DEFERRED(WARN_ONCE, x, fmt) + extern __read_mostly int scheduler_running; extern unsigned long calc_load_update; -- 2.53.0-Meta