* [PATCH v9 0/2] hung_task: Improve warning budget handling and task reporting
@ 2026-08-14 13:57 Aaron Tomlin
2026-08-14 13:57 ` [PATCH v9 1/2] hung_task: Reset warning budget when problem gets resolved Aaron Tomlin
2026-08-14 13:57 ` [PATCH v9 2/2] hung_task: Log summary line when warning budget is exhausted Aaron Tomlin
0 siblings, 2 replies; 12+ messages in thread
From: Aaron Tomlin @ 2026-08-14 13:57 UTC (permalink / raw)
To: akpm, lance.yang, mhiramat, pmladek
Cc: linux-kernel, david.laight.linux, atomlin, neelx, sean, chjohnst,
steve, mproche, nick.lange
The hung_task watchdog detects tasks stuck in TASK_UNINTERRUPTIBLE (D)
state for longer than CONFIG_DEFAULT_HUNG_TASK_TIMEOUT seconds. To prevent
log spam during system spikes, sysctl_hung_task_warnings enforces a budget
on the number of logged warnings.
However, the current implementation has two major limitations:
1. Permanent exhaustion of warning budget
sysctl_hung_task_warnings is decremented directly when printing
warnings. Once this budget hits zero, no further warnings are
reported until an administrator manually updates the sysctl value or
reboots the system. Consequently, a single temporary hang episode
permanently blinds the kernel watchdog to any subsequent hung tasks
after system recovery.
2. Total log suppression when budget is exhausted
Once the warning budget reaches zero, hung_task_info() completely
suppresses all output, including the basic single-line alert. While
suppressing verbose stack dumps and lock debugging is desirable to
prevent dmesg flooding, hiding basic task alerts leaves
administrators entirely unaware that tasks are hanging.
This patch series resolves both limitations by decoupling the configured
warning limit from the active runtime budget, automatically resetting the
budget upon system recovery or sysctl updates, and emitting a single
aggregate summary line when hung tasks are detected under an exhausted
warning budget.
Patch 1 separates the configured sysctl hung_task_warnings from the runtime
budget. Introduces an atomic flag so khungtaskd locklessly resets the
runtime budget when a scan finds zero hung tasks or when userspace writes
to the sysctl.
Patch 2 prevents dmesg flooding during system-wide hangs by keeping
per-task stack dumps budgeted, but provides ongoing visibility by logging a
single aggregate summary line at the end of each scan iteration when the
warning budget is exhausted.
Changes since v8:
- Resolved a concurrent sysctl write race condition on
hung_task_warnings_printed by making khungtaskd the sole owner of
runtime budget updates. Introduced an atomic flag set via sysctl write
and picked up locklessly by khungtaskd at the start of a scan iteration
(Lance Yang)
- Restored per-task header logging back inside the warning budget check in
hung_task_info() to prevent dmesg ring buffer saturation and console
lock contention during mass hung task events (Lance Yang)
- Added a single aggregate scan summary line printed at the end of each
watchdog scan when hung tasks are detected and the warning budget is
exhausted (Lance Yang)
- Linked to v8: https://lore.kernel.org/lkml/20260804202050.262427-1-atomlin@atomlin.com/
Changes since v7:
- Consolidated the commit message of each patch (Lance Yang)
- Linked to v7: https://lore.kernel.org/lkml/20260804155406.254810-1-atomlin@atomlin.com/
Changes since v6:
- Restructured the series in a new direction. Introduced an internal
counter (hung_task_warnings_printed) decoupled from
sysctl_hung_task_warnings. The warning budget automatically resets to
the configured limit once a check interval completes with zero hung
tasks detected, or when modified via sysctl (Petr Mladek)
- Ensured the single-line blocked hung task message is always printed even
after the warning budget reaches zero. Now budget enforcement is
restricted solely to suppressing verbose diagnostics (Petr Mladek)
- Refined the description of sysctl hung_task_warnings (Lance Yang)
- Removed field hung_task_reported from struct task_struct
- Removed the CONFIG_DETECT_HUNG_TASK_BLOCKER integration and
hung_task_blockers[] used to track memory addresses of blocker locks
across check intervals
- Removed the skip_show_task logic and dmesg log suppression messages
- Removed tracking variables (warnings_decremented and
hung_task_has_active) and the dmesg recovery notice printed when
clearing the blocker array
- Linked to v6: https://lore.kernel.org/lkml/20260719161305.428947-1-atomlin@atomlin.com/
Changes since v5:
- Skipped hung_task_info() and sys_info() for tasks already reported in
previous rounds to avoid log spam
- Linked to v5: https://lore.kernel.org/lkml/20260712202100.123934-1-atomlin@atomlin.com/
Changes since v4:
- Replaced the stack-local hashmap implementation with a persistent
array (Petr Mladek)
- Persistently track blocker addresses across scan intervals. Suppress
warning reports and keep the sysctl_hung_task_warnings budget intact
if the blocker is already tracked in the array (Petr Mladek)
- Reset the warnings budget and clear the blocker array when the hang
resolves (Petr Mladek)
- Output a recovery message to the kernel ring buffer upon hang
resolution
- Linked to v4: https://lore.kernel.org/lkml/20260627205733.90983-1-atomlin@atomlin.com/
Changes since v3:
- Deduct from the global budget if printing a full stack trace
- Pivoted from heuristic Wait Channel hashing to deterministic
blocker address hashing via CONFIG_DETECT_HUNG_TASK_BLOCKER
- Replaced the hung_task_reported bit-field with a standalone u8 byte.
Move hung_task_reported into an existing structural alignment hole
within task_struct following blocked_lock, resulting in zero overall
memory footprint increase and optimal cacheline grouping
- Linked to v3: https://lore.kernel.org/lkml/20260621213756.43225-1-atomlin@atomlin.com/
Changes since v2:
- Replaced the per-round cache flush with a task_struct bit-field for
persistent cross-scan tracking, mitigating delayed budget exhaustion
- Abandoned exact-stack hashing in favour of Wait Channel hashing
- Transitioned from jhash() to hash_long() to optimise single-pointer
hashing, and relocated the hash map to the local stack
- Linked to v2: https://lore.kernel.org/lkml/20260620013559.1537893-1-atomlin@atomlin.com/
Changes since v1:
- Preserve "INFO:" headers for all hung tasks; suppress only the stack
dumps for duplicates (Masami Hiramatsu)
- Print a clear notification when a trace is explicitly suppressed
- Add #ifdef CONFIG_STACKTRACE guards to prevent Kconfig build errors
- Optimise overhead by unwinding the stack only if a warning is
actually going to be printed
- Linked to v1: https://lore.kernel.org/lkml/20260617184841.1447955-1-atomlin@atomlin.com/
Aaron Tomlin (2):
hung_task: Reset warning budget when problem gets resolved
hung_task: Log summary line when warning budget is exhausted
Documentation/admin-guide/sysctl/kernel.rst | 5 ++-
kernel/hung_task.c | 46 +++++++++++++++++----
2 files changed, 40 insertions(+), 11 deletions(-)
--
2.55.0
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v9 1/2] hung_task: Reset warning budget when problem gets resolved
2026-08-14 13:57 [PATCH v9 0/2] hung_task: Improve warning budget handling and task reporting Aaron Tomlin
@ 2026-08-14 13:57 ` Aaron Tomlin
2026-08-14 15:58 ` Lance Yang
2026-08-26 11:17 ` Petr Mladek
2026-08-14 13:57 ` [PATCH v9 2/2] hung_task: Log summary line when warning budget is exhausted Aaron Tomlin
1 sibling, 2 replies; 12+ messages in thread
From: Aaron Tomlin @ 2026-08-14 13:57 UTC (permalink / raw)
To: akpm, lance.yang, mhiramat, pmladek
Cc: linux-kernel, david.laight.linux, atomlin, neelx, sean, chjohnst,
steve, mproche, nick.lange
The sysctl hung_task_warnings currently holds both the configured warning
limit and the remaining budget. Each detailed report decrements the
sysctl, so once it reaches zero, the configured limit is lost and cannot
be restored automatically.
Keep sysctl hung_task_warnings unchanged and track the remaining budget
in hung_task_warnings_printed. Reset the runtime budget via an atomic flag
when a watchdog check sees no hung tasks or when userspace writes a new
sysctl value.
Suggested-by: Petr Mladek <pmladek@suse.com>
Suggested-by: Lance Yang <lance.yang@linux.dev>
Tested-by: Lance Yang <lance.yang@linux.dev>
Signed-off-by: Aaron Tomlin <atomlin@atomlin.com>
---
Documentation/admin-guide/sysctl/kernel.rst | 5 +--
kernel/hung_task.c | 40 ++++++++++++++++-----
2 files changed, 35 insertions(+), 10 deletions(-)
diff --git a/Documentation/admin-guide/sysctl/kernel.rst b/Documentation/admin-guide/sysctl/kernel.rst
index c6994e55d141..b0f8e55efc1a 100644
--- a/Documentation/admin-guide/sysctl/kernel.rst
+++ b/Documentation/admin-guide/sysctl/kernel.rst
@@ -459,8 +459,9 @@ hung_task_warnings
==================
The maximum number of warnings to report. During a check interval
-if a hung task is detected, this value is decreased by 1.
-When this value reaches 0, no more warnings will be reported.
+if a hung task is detected, the internal warning budget is decreased by 1.
+When this budget reaches 0, no more detailed warnings will be reported. The
+warning budget is reset to the configured limit when no hung task is found.
This file shows up if ``CONFIG_DETECT_HUNG_TASK`` is enabled.
-1: report an infinite number of warnings.
diff --git a/kernel/hung_task.c b/kernel/hung_task.c
index 6fcc94ce4ca9..53499fbead83 100644
--- a/kernel/hung_task.c
+++ b/kernel/hung_task.c
@@ -59,6 +59,9 @@ static unsigned long __read_mostly sysctl_hung_task_check_interval_secs;
static int __read_mostly sysctl_hung_task_warnings = 10;
+static int hung_task_warnings_printed = 10;
+static atomic_t reset_hung_task_warnings = ATOMIC_INIT(0);
+
static int __read_mostly did_panic;
static bool hung_task_call_panic;
@@ -245,11 +248,11 @@ static void hung_task_info(struct task_struct *t, unsigned long timeout,
/*
* The given task did not get scheduled for more than
* CONFIG_DEFAULT_HUNG_TASK_TIMEOUT. Therefore, complain
- * accordingly
+ * accordingly with full details if the budget is not exhausted.
*/
- if (sysctl_hung_task_warnings || hung_task_call_panic) {
- if (sysctl_hung_task_warnings > 0)
- sysctl_hung_task_warnings--;
+ if (hung_task_warnings_printed || hung_task_call_panic) {
+ if (hung_task_warnings_printed > 0)
+ hung_task_warnings_printed--;
pr_err("INFO: task %s:%d blocked%s for more than %ld seconds.\n",
t->comm, t->pid, t->in_iowait ? " in I/O wait" : "",
(jiffies - t->last_switch_time) / HZ);
@@ -264,7 +267,7 @@ static void hung_task_info(struct task_struct *t, unsigned long timeout,
sched_show_task(t);
debug_show_blocker(t, timeout);
- if (!sysctl_hung_task_warnings)
+ if (!hung_task_warnings_printed)
pr_info("Future hung task reports are suppressed, see sysctl kernel.hung_task_warnings\n");
}
@@ -304,7 +307,7 @@ static void check_hung_uninterruptible_tasks(unsigned long timeout)
unsigned long last_break = jiffies;
struct task_struct *g, *t;
unsigned long this_round_count;
- int need_warning = sysctl_hung_task_warnings;
+ int need_warning;
unsigned long si_mask = hung_task_si_mask;
/*
@@ -314,6 +317,11 @@ static void check_hung_uninterruptible_tasks(unsigned long timeout)
if (test_taint(TAINT_DIE) || did_panic)
return;
+ if (atomic_xchg(&reset_hung_task_warnings, 0))
+ hung_task_warnings_printed =
+ READ_ONCE(sysctl_hung_task_warnings);
+ need_warning = hung_task_warnings_printed;
+
this_round_count = 0;
rcu_read_lock();
for_each_process_thread(g, t) {
@@ -340,8 +348,11 @@ static void check_hung_uninterruptible_tasks(unsigned long timeout)
unlock:
rcu_read_unlock();
- if (!this_round_count)
+ if (!this_round_count) {
+ hung_task_warnings_printed =
+ READ_ONCE(sysctl_hung_task_warnings);
return;
+ }
if (need_warning || hung_task_call_panic) {
si_mask |= SYS_INFO_LOCKS;
@@ -425,6 +436,19 @@ static int proc_dohung_task_timeout_secs(const struct ctl_table *table, int writ
return ret;
}
+static int proc_dohung_task_warnings(const struct ctl_table *table, int write,
+ void *buffer,
+ size_t *lenp, loff_t *ppos)
+{
+ int ret;
+
+ ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
+ if (!ret && write)
+ atomic_set_release(&reset_hung_task_warnings, 1);
+
+ return ret;
+}
+
/*
* This is needed for proc_doulongvec_minmax of sysctl_hung_task_timeout_secs
* and hung_task_check_interval_secs
@@ -480,7 +504,7 @@ static const struct ctl_table hung_task_sysctls[] = {
.data = &sysctl_hung_task_warnings,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = proc_dointvec_minmax,
+ .proc_handler = proc_dohung_task_warnings,
.extra1 = SYSCTL_NEG_ONE,
},
{
--
2.55.0
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v9 2/2] hung_task: Log summary line when warning budget is exhausted
2026-08-14 13:57 [PATCH v9 0/2] hung_task: Improve warning budget handling and task reporting Aaron Tomlin
2026-08-14 13:57 ` [PATCH v9 1/2] hung_task: Reset warning budget when problem gets resolved Aaron Tomlin
@ 2026-08-14 13:57 ` Aaron Tomlin
2026-08-14 16:27 ` Lance Yang
1 sibling, 1 reply; 12+ messages in thread
From: Aaron Tomlin @ 2026-08-14 13:57 UTC (permalink / raw)
To: akpm, lance.yang, mhiramat, pmladek
Cc: linux-kernel, david.laight.linux, atomlin, neelx, sean, chjohnst,
steve, mproche, nick.lange
Once the warning budget is exhausted, hung_task_info() stops printing
per-task details. To provide visibility without causing additional log
spam, emit a single aggregate summary line at the end of each watchdog
scan when hung tasks are detected and the warning budget is exhausted.
Keep per-task reports budgeted to avoid flooding dmesg or causing
ring buffer overflows during system-wide hangs.
Suggested-by: Petr Mladek <pmladek@suse.com>
Suggested-by: Lance Yang <lance.yang@linux.dev>
Signed-off-by: Aaron Tomlin <atomlin@atomlin.com>
---
kernel/hung_task.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/kernel/hung_task.c b/kernel/hung_task.c
index 53499fbead83..f5eb75325f3a 100644
--- a/kernel/hung_task.c
+++ b/kernel/hung_task.c
@@ -268,7 +268,7 @@ static void hung_task_info(struct task_struct *t, unsigned long timeout,
debug_show_blocker(t, timeout);
if (!hung_task_warnings_printed)
- pr_info("Future hung task reports are suppressed, see sysctl kernel.hung_task_warnings\n");
+ pr_info("Future hung task reports won't print details about each process, see sysctl kernel.hung_task_warnings\n");
}
touch_nmi_watchdog();
@@ -354,6 +354,10 @@ static void check_hung_uninterruptible_tasks(unsigned long timeout)
return;
}
+ if (!hung_task_warnings_printed && !hung_task_call_panic)
+ pr_info("hung_task: %lu hung tasks detected, warning budget exhausted\n",
+ this_round_count);
+
if (need_warning || hung_task_call_panic) {
si_mask |= SYS_INFO_LOCKS;
--
2.55.0
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v9 1/2] hung_task: Reset warning budget when problem gets resolved
2026-08-14 13:57 ` [PATCH v9 1/2] hung_task: Reset warning budget when problem gets resolved Aaron Tomlin
@ 2026-08-14 15:58 ` Lance Yang
2026-08-14 17:22 ` Aaron Tomlin
2026-08-26 11:17 ` Petr Mladek
1 sibling, 1 reply; 12+ messages in thread
From: Lance Yang @ 2026-08-14 15:58 UTC (permalink / raw)
To: atomlin
Cc: akpm, mhiramat, pmladek, linux-kernel, david.laight.linux, neelx,
sean, chjohnst, steve, mproche, nick.lange, Lance Yang
On Fri, Aug 14, 2026 at 09:57:17AM -0400, Aaron Tomlin wrote:
>The sysctl hung_task_warnings currently holds both the configured warning
>limit and the remaining budget. Each detailed report decrements the
>sysctl, so once it reaches zero, the configured limit is lost and cannot
>be restored automatically.
>
>Keep sysctl hung_task_warnings unchanged and track the remaining budget
>in hung_task_warnings_printed. Reset the runtime budget via an atomic flag
>when a watchdog check sees no hung tasks or when userspace writes a new
>sysctl value.
Only sysctl writes go through reset_hung_task_warnings; no-hung case
reloads budget directly. Also worth spelling out why flag is there:
keeping runtime budget khungtaskd-owned. I'd write it as:
Keep sysctl_hung_task_warnings as the configured warning limit and make
khungtaskd the sole owner of the remaining budget. A check that finds no
hung tasks reloads the budget directly from the configured limit. A
successful sysctl write publishes an atomic reset request, which
khungtaskd consumes at the start of the next check.
No need to resend just for this, though. I think Andrew can fix that up
when applying.
>Suggested-by: Petr Mladek <pmladek@suse.com>
>Suggested-by: Lance Yang <lance.yang@linux.dev>
>Tested-by: Lance Yang <lance.yang@linux.dev>
>Signed-off-by: Aaron Tomlin <atomlin@atomlin.com>
>---
Nothing jumped out at me, thanks!
Reviewed-by: Lance Yang <lance.yang@linux.dev>
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v9 2/2] hung_task: Log summary line when warning budget is exhausted
2026-08-14 13:57 ` [PATCH v9 2/2] hung_task: Log summary line when warning budget is exhausted Aaron Tomlin
@ 2026-08-14 16:27 ` Lance Yang
2026-08-14 17:26 ` Aaron Tomlin
2026-08-26 11:31 ` Petr Mladek
0 siblings, 2 replies; 12+ messages in thread
From: Lance Yang @ 2026-08-14 16:27 UTC (permalink / raw)
To: atomlin
Cc: akpm, mhiramat, pmladek, linux-kernel, david.laight.linux, neelx,
sean, chjohnst, steve, mproche, nick.lange, Lance Yang
On Fri, Aug 14, 2026 at 09:57:18AM -0400, Aaron Tomlin wrote:
>Once the warning budget is exhausted, hung_task_info() stops printing
>per-task details. To provide visibility without causing additional log
>spam, emit a single aggregate summary line at the end of each watchdog
>scan when hung tasks are detected and the warning budget is exhausted.
>
>Keep per-task reports budgeted to avoid flooding dmesg or causing
>ring buffer overflows during system-wide hangs.
Hm... not quite unconditional. hung_task_call_panic still gets through
budget gate, so with hung_task_warnings=1 and hung_task_panic=2, first
task can exhaust budget and second one still gets full details (right
before panic).
I'd write changelog like this:
Once the warning budget is exhausted, hung_task_info() normally stops
printing per-task details. When panic is triggered, full details are
still printed so diagnostics remain available before panic.
To retain visibility without restoring per-task output after budget
exhaustion, emit a single aggregate summary line at the end of each
watchdog scan that detects hung tasks with an exhausted budget. This
keeps non-panic per-task reports budgeted during system-wide hangs.
>
>Suggested-by: Petr Mladek <pmladek@suse.com>
>Suggested-by: Lance Yang <lance.yang@linux.dev>
>Signed-off-by: Aaron Tomlin <atomlin@atomlin.com>
>---
> kernel/hung_task.c | 6 +++++-
> 1 file changed, 5 insertions(+), 1 deletion(-)
>
>diff --git a/kernel/hung_task.c b/kernel/hung_task.c
>index 53499fbead83..f5eb75325f3a 100644
>--- a/kernel/hung_task.c
>+++ b/kernel/hung_task.c
>@@ -268,7 +268,7 @@ static void hung_task_info(struct task_struct *t, unsigned long timeout,
> debug_show_blocker(t, timeout);
>
> if (!hung_task_warnings_printed)
>- pr_info("Future hung task reports are suppressed, see sysctl kernel.hung_task_warnings\n");
>+ pr_info("Future hung task reports won't print details about each process, see sysctl kernel.hung_task_warnings\n");
Same exception here... next task can hit panic threshold and print full
details anyway (and this line can be printed while hung_task_call_panic
is already set).
I'd make condition and message match actual behavior:
"hung_task: further per-task details suppressed until warning budget is
reset or panic is triggered (see sysctl kernel.hung_task_warnings)\n"
No need to resend just for these ... I think Andrew can fix them up when
applying :)
Otherwise, LGTM.
Reviewed-by: Lance Yang <lance.yang@linux.dev>
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v9 1/2] hung_task: Reset warning budget when problem gets resolved
2026-08-14 15:58 ` Lance Yang
@ 2026-08-14 17:22 ` Aaron Tomlin
0 siblings, 0 replies; 12+ messages in thread
From: Aaron Tomlin @ 2026-08-14 17:22 UTC (permalink / raw)
To: Lance Yang
Cc: akpm, mhiramat, pmladek, linux-kernel, david.laight.linux, neelx,
sean, chjohnst, steve, mproche, nick.lange
On Fri, Aug 14, 2026 at 11:58:21PM +0800, Lance Yang wrote:
>
> On Fri, Aug 14, 2026 at 09:57:17AM -0400, Aaron Tomlin wrote:
> >The sysctl hung_task_warnings currently holds both the configured warning
> >limit and the remaining budget. Each detailed report decrements the
> >sysctl, so once it reaches zero, the configured limit is lost and cannot
> >be restored automatically.
> >
> >Keep sysctl hung_task_warnings unchanged and track the remaining budget
> >in hung_task_warnings_printed. Reset the runtime budget via an atomic flag
> >when a watchdog check sees no hung tasks or when userspace writes a new
> >sysctl value.
>
> Only sysctl writes go through reset_hung_task_warnings; no-hung case
> reloads budget directly. Also worth spelling out why flag is there:
> keeping runtime budget khungtaskd-owned. I'd write it as:
>
> Keep sysctl_hung_task_warnings as the configured warning limit and make
> khungtaskd the sole owner of the remaining budget. A check that finds no
> hung tasks reloads the budget directly from the configured limit. A
> successful sysctl write publishes an atomic reset request, which
> khungtaskd consumes at the start of the next check.
>
> No need to resend just for this, though. I think Andrew can fix that up
> when applying.
Hi Lance,
Acknowledged.
> >Suggested-by: Petr Mladek <pmladek@suse.com>
> >Suggested-by: Lance Yang <lance.yang@linux.dev>
> >Tested-by: Lance Yang <lance.yang@linux.dev>
> >Signed-off-by: Aaron Tomlin <atomlin@atomlin.com>
> >---
>
> Nothing jumped out at me, thanks!
>
> Reviewed-by: Lance Yang <lance.yang@linux.dev>
Thank you!
--
Aaron Tomlin
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v9 2/2] hung_task: Log summary line when warning budget is exhausted
2026-08-14 16:27 ` Lance Yang
@ 2026-08-14 17:26 ` Aaron Tomlin
2026-08-26 11:31 ` Petr Mladek
1 sibling, 0 replies; 12+ messages in thread
From: Aaron Tomlin @ 2026-08-14 17:26 UTC (permalink / raw)
To: Lance Yang
Cc: akpm, mhiramat, pmladek, linux-kernel, david.laight.linux, neelx,
sean, chjohnst, steve, mproche, nick.lange
On Sat, Aug 15, 2026 at 12:27:34AM +0800, Lance Yang wrote:
>
> On Fri, Aug 14, 2026 at 09:57:18AM -0400, Aaron Tomlin wrote:
> >Once the warning budget is exhausted, hung_task_info() stops printing
> >per-task details. To provide visibility without causing additional log
> >spam, emit a single aggregate summary line at the end of each watchdog
> >scan when hung tasks are detected and the warning budget is exhausted.
> >
> >Keep per-task reports budgeted to avoid flooding dmesg or causing
> >ring buffer overflows during system-wide hangs.
>
> Hm... not quite unconditional. hung_task_call_panic still gets through
> budget gate, so with hung_task_warnings=1 and hung_task_panic=2, first
> task can exhaust budget and second one still gets full details (right
> before panic).
>
> I'd write changelog like this:
>
> Once the warning budget is exhausted, hung_task_info() normally stops
> printing per-task details. When panic is triggered, full details are
> still printed so diagnostics remain available before panic.
>
> To retain visibility without restoring per-task output after budget
> exhaustion, emit a single aggregate summary line at the end of each
> watchdog scan that detects hung tasks with an exhausted budget. This
> keeps non-panic per-task reports budgeted during system-wide hangs.
Hi Lance,
Acknowledged.
> >Suggested-by: Petr Mladek <pmladek@suse.com>
> >Suggested-by: Lance Yang <lance.yang@linux.dev>
> >Signed-off-by: Aaron Tomlin <atomlin@atomlin.com>
> >---
> > kernel/hung_task.c | 6 +++++-
> > 1 file changed, 5 insertions(+), 1 deletion(-)
> >
> >diff --git a/kernel/hung_task.c b/kernel/hung_task.c
> >index 53499fbead83..f5eb75325f3a 100644
> >--- a/kernel/hung_task.c
> >+++ b/kernel/hung_task.c
> >@@ -268,7 +268,7 @@ static void hung_task_info(struct task_struct *t, unsigned long timeout,
> > debug_show_blocker(t, timeout);
> >
> > if (!hung_task_warnings_printed)
> >- pr_info("Future hung task reports are suppressed, see sysctl kernel.hung_task_warnings\n");
> >+ pr_info("Future hung task reports won't print details about each process, see sysctl kernel.hung_task_warnings\n");
>
> Same exception here... next task can hit panic threshold and print full
> details anyway (and this line can be printed while hung_task_call_panic
> is already set).
>
> I'd make condition and message match actual behavior:
>
> "hung_task: further per-task details suppressed until warning budget is
> reset or panic is triggered (see sysctl kernel.hung_task_warnings)\n"
Agreed.
> No need to resend just for these ... I think Andrew can fix them up when
> applying :)
>
> Otherwise, LGTM.
>
> Reviewed-by: Lance Yang <lance.yang@linux.dev>
Understood. Thank you Lance. Andrew, please let me know if otherwise.
Kind regards,
--
Aaron Tomlin
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v9 1/2] hung_task: Reset warning budget when problem gets resolved
2026-08-14 13:57 ` [PATCH v9 1/2] hung_task: Reset warning budget when problem gets resolved Aaron Tomlin
2026-08-14 15:58 ` Lance Yang
@ 2026-08-26 11:17 ` Petr Mladek
2026-08-27 15:30 ` Lance Yang
1 sibling, 1 reply; 12+ messages in thread
From: Petr Mladek @ 2026-08-26 11:17 UTC (permalink / raw)
To: Aaron Tomlin
Cc: akpm, lance.yang, mhiramat, linux-kernel, david.laight.linux,
neelx, sean, chjohnst, steve, mproche, nick.lange
Hi,
first, I am sorry for so late review. I had vacation, many
things accumulated, ...
On Fri 2026-08-14 09:57:17, Aaron Tomlin wrote:
> The sysctl hung_task_warnings currently holds both the configured warning
> limit and the remaining budget. Each detailed report decrements the
> sysctl, so once it reaches zero, the configured limit is lost and cannot
> be restored automatically.
>
> Keep sysctl hung_task_warnings unchanged and track the remaining budget
> in hung_task_warnings_printed. Reset the runtime budget via an atomic flag
> when a watchdog check sees no hung tasks or when userspace writes a new
> sysctl value.
>
> --- a/kernel/hung_task.c
> +++ b/kernel/hung_task.c
> @@ -59,6 +59,9 @@ static unsigned long __read_mostly sysctl_hung_task_check_interval_secs;
>
> static int __read_mostly sysctl_hung_task_warnings = 10;
>
> +static int hung_task_warnings_printed = 10;
Nit: The name of the variable is a bit misleading in this final
version. It does not longer count the number of printed
messages.
A better name might be "hung_task_warnings_budget" or so.
It would be nice to change it if we need another version.
And I am afraid that we would need it, see below.
If we change it then I would also add comments explaining
the difference between the two values, something like:
<proposal>
/*
* Limit the number of printed hung tasks to prevent printing
* the same or similar backtraces repeatedly.
*/
static int __read_mostly sysctl_hung_task_warnings = 10;
/*
* The number of hung tasks which still can be reported.
* The budget gets restored to the original limit when
* the previous stall is resolved.
*/
static int hung_task_warnings_budget = 10;
</proposal>
> +static atomic_t reset_hung_task_warnings = ATOMIC_INIT(0);
> +
> static int __read_mostly did_panic;
> static bool hung_task_call_panic;
>
> @@ -245,11 +248,11 @@ static void hung_task_info(struct task_struct *t, unsigned long timeout,
> /*
> * The given task did not get scheduled for more than
> * CONFIG_DEFAULT_HUNG_TASK_TIMEOUT. Therefore, complain
> - * accordingly
> + * accordingly with full details if the budget is not exhausted.
> */
> - if (sysctl_hung_task_warnings || hung_task_call_panic) {
> - if (sysctl_hung_task_warnings > 0)
> - sysctl_hung_task_warnings--;
> + if (hung_task_warnings_printed || hung_task_call_panic) {
> + if (hung_task_warnings_printed > 0)
> + hung_task_warnings_printed--;
> pr_err("INFO: task %s:%d blocked%s for more than %ld seconds.\n",
> t->comm, t->pid, t->in_iowait ? " in I/O wait" : "",
> (jiffies - t->last_switch_time) / HZ);
> @@ -264,7 +267,7 @@ static void hung_task_info(struct task_struct *t, unsigned long timeout,
> sched_show_task(t);
> debug_show_blocker(t, timeout);
>
> - if (!sysctl_hung_task_warnings)
> + if (!hung_task_warnings_printed)
> pr_info("Future hung task reports are suppressed, see sysctl kernel.hung_task_warnings\n");
> }
>
> @@ -304,7 +307,7 @@ static void check_hung_uninterruptible_tasks(unsigned long timeout)
> unsigned long last_break = jiffies;
> struct task_struct *g, *t;
> unsigned long this_round_count;
> - int need_warning = sysctl_hung_task_warnings;
> + int need_warning;
> unsigned long si_mask = hung_task_si_mask;
>
> /*
> @@ -314,6 +317,11 @@ static void check_hung_uninterruptible_tasks(unsigned long timeout)
> if (test_taint(TAINT_DIE) || did_panic)
> return;
>
> + if (atomic_xchg(&reset_hung_task_warnings, 0))
I would use here atomic_xchg_acquire(). It serializes the ordering
of reset_hung_task_warnings vs sysctl_hung_task_warnings.
It would make it symetric with the barrier in the sysctl handler.
> + hung_task_warnings_printed =
> + READ_ONCE(sysctl_hung_task_warnings);
This would work only when "sysctl_hung_task_warnings"
is updated using WRITE_ONCE(). But it seems that this
is not the case. My understading is that it is updated by:
+ proc_dointvec_minmax()
+ do_proc_vec()
+ proc_get_long()
+ strtoul_lenient()
which does a plain assigment:
static int strtoul_lenient(const char *cp, char **endp, unsigned int base,
unsigned long *res)
{
[...]
*res = (unsigned long)result;
[...]
}
It can be solved by using temporary variable in proc_dointvec_minmax().
We have a custom proc_dohung_task_warnings() handler anyway.
See below.
> + need_warning = hung_task_warnings_printed;
> +
> this_round_count = 0;
> rcu_read_lock();
> for_each_process_thread(g, t) {
> @@ -340,8 +348,11 @@ static void check_hung_uninterruptible_tasks(unsigned long timeout)
> unlock:
> rcu_read_unlock();
>
> - if (!this_round_count)
> + if (!this_round_count) {
> + hung_task_warnings_printed =
> + READ_ONCE(sysctl_hung_task_warnings);
> return;
> + }
>
> if (need_warning || hung_task_call_panic) {
> si_mask |= SYS_INFO_LOCKS;
> @@ -425,6 +436,19 @@ static int proc_dohung_task_timeout_secs(const struct ctl_table *table, int writ
> return ret;
> }
>
> +static int proc_dohung_task_warnings(const struct ctl_table *table, int write,
> + void *buffer,
> + size_t *lenp, loff_t *ppos)
> +{
> + int ret;
> +
> + ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
> + if (!ret && write)
> + atomic_set_release(&reset_hung_task_warnings, 1);
> +
> + return ret;
> +}
We should use WRITE_ONCE() when updating proc_dohung_task_warnings.
So, we need similar trick with proxy_table like in
proc_dohung_task_detect_count. Something like, on top of this patch:
--- a/kernel/hung_task.c
+++ b/kernel/hung_task.c
@@ -444,13 +444,26 @@ static int proc_dohung_task_warnings(const struct ctl_table *table, int write,
void *buffer,
size_t *lenp, loff_t *ppos)
{
+ struct ctl_table proxy_table;
+ int warnings;
int ret;
- ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
- if (!ret && write)
- atomic_set_release(&reset_hung_task_warnings, 1);
+ proxy_table = *table;
+ proxy_table.data = &warnings;
- return ret;
+ if (SYSCTL_KERN_TO_USER(write))
+ warnings = READ_ONCE(sysctl_hung_task_warnings);
+
+ ret = proc_dointvec_minmax(&proxy_table, write, buffer, lenp, ppos);
+ if (ret < 0)
+ return ret;
+
+ if (SYSCTL_USER_TO_KERN(write)) {
+ WRITE_ONCE(sysctl_hung_task_warnings, warnings);
+ atomic_set_release(&reset_hung_task_warnings, 1);
+ }
+
+ return 0;
}
/*
Otherwise, it looks good to me.
Best Regards,
Petr
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v9 2/2] hung_task: Log summary line when warning budget is exhausted
2026-08-14 16:27 ` Lance Yang
2026-08-14 17:26 ` Aaron Tomlin
@ 2026-08-26 11:31 ` Petr Mladek
1 sibling, 0 replies; 12+ messages in thread
From: Petr Mladek @ 2026-08-26 11:31 UTC (permalink / raw)
To: Lance Yang
Cc: atomlin, akpm, mhiramat, linux-kernel, david.laight.linux, neelx,
sean, chjohnst, steve, mproche, nick.lange
On Sat 2026-08-15 00:27:34, Lance Yang wrote:
>
> On Fri, Aug 14, 2026 at 09:57:18AM -0400, Aaron Tomlin wrote:
> >Once the warning budget is exhausted, hung_task_info() stops printing
> >per-task details. To provide visibility without causing additional log
> >spam, emit a single aggregate summary line at the end of each watchdog
> >scan when hung tasks are detected and the warning budget is exhausted.
> >
> >Keep per-task reports budgeted to avoid flooding dmesg or causing
> >ring buffer overflows during system-wide hangs.
>
> Hm... not quite unconditional. hung_task_call_panic still gets through
> budget gate, so with hung_task_warnings=1 and hung_task_panic=2, first
> task can exhaust budget and second one still gets full details (right
> before panic).
Great catch!
> I'd write changelog like this:
>
> Once the warning budget is exhausted, hung_task_info() normally stops
> printing per-task details. When panic is triggered, full details are
> still printed so diagnostics remain available before panic.
>
> To retain visibility without restoring per-task output after budget
> exhaustion, emit a single aggregate summary line at the end of each
> watchdog scan that detects hung tasks with an exhausted budget. This
> keeps non-panic per-task reports budgeted during system-wide hangs.
Yup, this sounds better.
> >--- a/kernel/hung_task.c
> >+++ b/kernel/hung_task.c
> >@@ -268,7 +268,7 @@ static void hung_task_info(struct task_struct *t, unsigned long timeout,
> > debug_show_blocker(t, timeout);
> >
> > if (!hung_task_warnings_printed)
> >- pr_info("Future hung task reports are suppressed, see sysctl kernel.hung_task_warnings\n");
> >+ pr_info("Future hung task reports won't print details about each process, see sysctl kernel.hung_task_warnings\n");
>
> Same exception here... next task can hit panic threshold and print full
> details anyway (and this line can be printed while hung_task_call_panic
> is already set).
>
> I'd make condition and message match actual behavior:
>
> "hung_task: further per-task details suppressed until warning budget is
> reset or panic is triggered (see sysctl kernel.hung_task_warnings)\n"
Looks better as well.
With the two changes:
Reviewed-by: Petr Mladek <pmladek@suse.com>
One more idea. It might be useful to print a message also
in first clean check after a stall was resolved.
But it might need to add one more variable, e.g.
last_round_count. We could not check
(hung_task_warnings_printed != READ_ONCE(sysctl_hung_task_warnings))
because it might be racy.
It might be done later in a separate patch.
Best Regards,
Petr
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v9 1/2] hung_task: Reset warning budget when problem gets resolved
2026-08-26 11:17 ` Petr Mladek
@ 2026-08-27 15:30 ` Lance Yang
2026-08-28 9:05 ` Petr Mladek
0 siblings, 1 reply; 12+ messages in thread
From: Lance Yang @ 2026-08-27 15:30 UTC (permalink / raw)
To: pmladek, atomlin
Cc: akpm, lance.yang, mhiramat, linux-kernel, david.laight.linux,
neelx, sean, chjohnst, steve, mproche, nick.lange
On Wed, Aug 26, 2026 at 01:17:47PM +0200, Petr Mladek wrote:
[...]
>/*
> * The number of hung tasks which still can be reported.
> * The budget gets restored to the original limit when
> * the previous stall is resolved.
> */
>static int hung_task_warnings_budget = 10;
Yeah, hung_task_warnings_budget is better. Comments too.
></proposal>
>
>> +static atomic_t reset_hung_task_warnings = ATOMIC_INIT(0);
>> +
>> static int __read_mostly did_panic;
>> static bool hung_task_call_panic;
>>
>> @@ -245,11 +248,11 @@ static void hung_task_info(struct task_struct *t, unsigned long timeout,
>> /*
>> * The given task did not get scheduled for more than
>> * CONFIG_DEFAULT_HUNG_TASK_TIMEOUT. Therefore, complain
>> - * accordingly
>> + * accordingly with full details if the budget is not exhausted.
>> */
>> - if (sysctl_hung_task_warnings || hung_task_call_panic) {
>> - if (sysctl_hung_task_warnings > 0)
>> - sysctl_hung_task_warnings--;
>> + if (hung_task_warnings_printed || hung_task_call_panic) {
>> + if (hung_task_warnings_printed > 0)
>> + hung_task_warnings_printed--;
>> pr_err("INFO: task %s:%d blocked%s for more than %ld seconds.\n",
>> t->comm, t->pid, t->in_iowait ? " in I/O wait" : "",
>> (jiffies - t->last_switch_time) / HZ);
>> @@ -264,7 +267,7 @@ static void hung_task_info(struct task_struct *t, unsigned long timeout,
>> sched_show_task(t);
>> debug_show_blocker(t, timeout);
>>
>> - if (!sysctl_hung_task_warnings)
>> + if (!hung_task_warnings_printed)
>> pr_info("Future hung task reports are suppressed, see sysctl kernel.hung_task_warnings\n");
>> }
>>
>> @@ -304,7 +307,7 @@ static void check_hung_uninterruptible_tasks(unsigned long timeout)
>> unsigned long last_break = jiffies;
>> struct task_struct *g, *t;
>> unsigned long this_round_count;
>> - int need_warning = sysctl_hung_task_warnings;
>> + int need_warning;
>> unsigned long si_mask = hung_task_si_mask;
>>
>> /*
>> @@ -314,6 +317,11 @@ static void check_hung_uninterruptible_tasks(unsigned long timeout)
>> if (test_taint(TAINT_DIE) || did_panic)
>> return;
>>
>> + if (atomic_xchg(&reset_hung_task_warnings, 0))
>
>I would use here atomic_xchg_acquire(). It serializes the ordering
>of reset_hung_task_warnings vs sysctl_hung_task_warnings.
>It would make it symetric with the barrier in the sysctl handler.
Yep, _acquire is enough here. Plain atomic_xchg() is already fully
ordered, though, so this looks like making the intent clearer rather
than fixing the ordering :)
The old-value return already makes plain atomic_xchg() fully ordered :)
ORDERING (see memory-barriers.txt)
--------
The rule of thumb:
...
- RMW operations that have a return value are fully ordered;
...
Except of course when a successful operation has an explicit ordering
like:
{}_relaxed: unordered
{}_acquire: the R of the RMW (or atomic_read) is an ACQUIRE
{}_release: the W of the RMW (or atomic_set) is a RELEASE
>
>> + hung_task_warnings_printed =
>> + READ_ONCE(sysctl_hung_task_warnings);
>
>This would work only when "sysctl_hung_task_warnings"
>is updated using WRITE_ONCE(). But it seems that this
>is not the case. My understading is that it is updated by:
Wait, I think proc_dointvec_minmax() already handles this.
The handler publishes the reset only after proc_dointvec_minmax()
succeeds:
static int proc_dohung_task_warnings(const struct ctl_table *table, int write,
void *buffer,
size_t *lenp, loff_t *ppos)
{
int ret;
ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
if (!ret && write)
atomic_set_release(&reset_hung_task_warnings, 1);
return ret;
}
For proc_dointvec_minmax(), the converter is:
int proc_dointvec_minmax(const struct ctl_table *table, int dir,
void *buffer, size_t *lenp, loff_t *ppos)
{
return do_proc_dointvec(table, dir, buffer, lenp, ppos,
do_proc_int_conv_minmax);
}
Here, i is table->data, while lval is local:
static int do_proc_dointvec(const struct ctl_table *table, int dir,
void *buffer, size_t *lenp, loff_t *ppos,
int (*conv)(bool *negp, unsigned long *u_ptr, int *k_ptr,
int dir, const struct ctl_table *table))
{
...
i = (int *) table->data;
vleft = table->maxlen / sizeof(*i);
...
for (; left && vleft--; i++, first=0) {
unsigned long lval;
bool neg;
if (SYSCTL_USER_TO_KERN(dir)) {
proc_skip_spaces(&p, &left);
if (!left)
break;
err = proc_get_long(&p, &left, &lval, &neg,
proc_wspace_sep,
sizeof(proc_wspace_sep), NULL);
if (err)
break;
if (conv(&neg, &lval, i, 1, table)) {
err = -EINVAL;
break;
}
...
*lenp -= left;
out:
*ppos += *lenp;
return err;
}
The min/max callback passes true for k_ptr_range_check:
static int do_proc_int_conv_minmax(bool *negp, unsigned long *u_ptr, int *k_ptr,
int dir, const struct ctl_table *tbl)
{
return proc_int_conv(negp, u_ptr, k_ptr, dir, tbl, true,
sysctl_user_to_kern_int_conv,
sysctl_kern_to_user_int_conv);
}
proc_int_conv() writes i here:
int proc_int_conv(bool *negp, ulong *u_ptr, int *k_ptr, int dir,
const struct ctl_table *tbl, bool k_ptr_range_check,
int (*user_to_kern)(const bool *negp, const ulong *u_ptr, int *k_ptr),
int (*kern_to_user)(bool *negp, ulong *u_ptr, const int *k_ptr))
{
...
if (k_ptr_range_check) {
int tmp_k, ret;
if (!tbl)
return -EINVAL;
ret = user_to_kern(negp, u_ptr, &tmp_k);
if (ret)
return ret;
if ((tbl->extra1 && *(int *)tbl->extra1 > tmp_k) ||
(tbl->extra2 && *(int *)tbl->extra2 < tmp_k))
return -EINVAL;
WRITE_ONCE(*k_ptr, tmp_k);
...
return 0;
}
So table->data already gets WRITE_ONCE() before atomic_set_release(). No
need for proxy_table here, AFAICS. I'd keep the rename and _acquire
change :)
WDYT?
Cheers, Lance
>
> + proc_dointvec_minmax()
> + do_proc_vec()
> + proc_get_long()
> + strtoul_lenient()
>
>which does a plain assigment:
>
>static int strtoul_lenient(const char *cp, char **endp, unsigned int base,
> unsigned long *res)
>{
>[...]
> *res = (unsigned long)result;
>[...]
>}
>
>It can be solved by using temporary variable in proc_dointvec_minmax().
>We have a custom proc_dohung_task_warnings() handler anyway.
>See below.
>
>> + need_warning = hung_task_warnings_printed;
>> +
>> this_round_count = 0;
>> rcu_read_lock();
>> for_each_process_thread(g, t) {
>> @@ -340,8 +348,11 @@ static void check_hung_uninterruptible_tasks(unsigned long timeout)
>> unlock:
>> rcu_read_unlock();
>>
>> - if (!this_round_count)
>> + if (!this_round_count) {
>> + hung_task_warnings_printed =
>> + READ_ONCE(sysctl_hung_task_warnings);
>> return;
>> + }
>>
>> if (need_warning || hung_task_call_panic) {
>> si_mask |= SYS_INFO_LOCKS;
>> @@ -425,6 +436,19 @@ static int proc_dohung_task_timeout_secs(const struct ctl_table *table, int writ
>> return ret;
>> }
>>
>> +static int proc_dohung_task_warnings(const struct ctl_table *table, int write,
>> + void *buffer,
>> + size_t *lenp, loff_t *ppos)
>> +{
>> + int ret;
>> +
>> + ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
>> + if (!ret && write)
>> + atomic_set_release(&reset_hung_task_warnings, 1);
>> +
>> + return ret;
>> +}
>
>We should use WRITE_ONCE() when updating proc_dohung_task_warnings.
>So, we need similar trick with proxy_table like in
>proc_dohung_task_detect_count. Something like, on top of this patch:
>
>--- a/kernel/hung_task.c
>+++ b/kernel/hung_task.c
>@@ -444,13 +444,26 @@ static int proc_dohung_task_warnings(const struct ctl_table *table, int write,
> void *buffer,
> size_t *lenp, loff_t *ppos)
> {
>+ struct ctl_table proxy_table;
>+ int warnings;
> int ret;
>
>- ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
>- if (!ret && write)
>- atomic_set_release(&reset_hung_task_warnings, 1);
>+ proxy_table = *table;
>+ proxy_table.data = &warnings;
>
>- return ret;
>+ if (SYSCTL_KERN_TO_USER(write))
>+ warnings = READ_ONCE(sysctl_hung_task_warnings);
>+
>+ ret = proc_dointvec_minmax(&proxy_table, write, buffer, lenp, ppos);
>+ if (ret < 0)
>+ return ret;
>+
>+ if (SYSCTL_USER_TO_KERN(write)) {
>+ WRITE_ONCE(sysctl_hung_task_warnings, warnings);
>+ atomic_set_release(&reset_hung_task_warnings, 1);
>+ }
>+
>+ return 0;
> }
>
> /*
>
>Otherwise, it looks good to me.
>
>Best Regards,
>Petr
>
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v9 1/2] hung_task: Reset warning budget when problem gets resolved
2026-08-27 15:30 ` Lance Yang
@ 2026-08-28 9:05 ` Petr Mladek
2026-08-28 9:22 ` Lance Yang
0 siblings, 1 reply; 12+ messages in thread
From: Petr Mladek @ 2026-08-28 9:05 UTC (permalink / raw)
To: Lance Yang
Cc: atomlin, akpm, mhiramat, linux-kernel, david.laight.linux, neelx,
sean, chjohnst, steve, mproche, nick.lange
On Thu 2026-08-27 23:30:01, Lance Yang wrote:
> On Wed, Aug 26, 2026 at 01:17:47PM +0200, Petr Mladek wrote:
> >> @@ -314,6 +317,11 @@ static void check_hung_uninterruptible_tasks(unsigned long timeout)
> >> if (test_taint(TAINT_DIE) || did_panic)
> >> return;
> >>
> >> + if (atomic_xchg(&reset_hung_task_warnings, 0))
> >
> >I would use here atomic_xchg_acquire(). It serializes the ordering
> >of reset_hung_task_warnings vs sysctl_hung_task_warnings.
> >It would make it symetric with the barrier in the sysctl handler.
>
> Yep, _acquire is enough here. Plain atomic_xchg() is already fully
> ordered, though, so this looks like making the intent clearer rather
> than fixing the ordering :)
Yes, my intention was to make the ordering more clear and symmetric.
The original code worked because the barrier was even stronger.
> The old-value return already makes plain atomic_xchg() fully ordered :)
>
> ORDERING (see memory-barriers.txt)
> --------
>
> The rule of thumb:
> ...
> - RMW operations that have a return value are fully ordered;
> ...
> Except of course when a successful operation has an explicit ordering
> like:
>
> {}_relaxed: unordered
> {}_acquire: the R of the RMW (or atomic_read) is an ACQUIRE
> {}_release: the W of the RMW (or atomic_set) is a RELEASE
>
> >
> >> + hung_task_warnings_printed =
> >> + READ_ONCE(sysctl_hung_task_warnings);
> >
> >This would work only when "sysctl_hung_task_warnings"
> >is updated using WRITE_ONCE(). But it seems that this
> >is not the case. My understading is that it is updated by:
>
> Wait, I think proc_dointvec_minmax() already handles this.
You are right.
> For proc_dointvec_minmax(), the converter is:
>
> int proc_dointvec_minmax(const struct ctl_table *table, int dir,
> void *buffer, size_t *lenp, loff_t *ppos)
> {
> return do_proc_dointvec(table, dir, buffer, lenp, ppos,
> do_proc_int_conv_minmax);
> }
>
> Here, i is table->data, while lval is local:
I have missed this.
> static int do_proc_dointvec(const struct ctl_table *table, int dir,
> void *buffer, size_t *lenp, loff_t *ppos,
> int (*conv)(bool *negp, unsigned long *u_ptr, int *k_ptr,
> int dir, const struct ctl_table *table))
> {
> ...
> i = (int *) table->data;
> vleft = table->maxlen / sizeof(*i);
> ...
> for (; left && vleft--; i++, first=0) {
> unsigned long lval;
> bool neg;
>
> if (SYSCTL_USER_TO_KERN(dir)) {
> proc_skip_spaces(&p, &left);
>
> if (!left)
> break;
> err = proc_get_long(&p, &left, &lval, &neg,
> proc_wspace_sep,
> sizeof(proc_wspace_sep), NULL);
I have missed that proc_get_long() assigns the value to the local
variable @lval.
> if (err)
> break;
> if (conv(&neg, &lval, i, 1, table)) {
> err = -EINVAL;
> break;
The real asigment to table->data is done here. And I agree that it
goes down to proc_int_conv() which does WRITE_ONCE().
So, we are on the safe side and do _not_ need the proxy table.
Now, I am not sure whether we need v10. It might be worth it.
AFAIK, Andrew has not taken this patchset yet...
I am sorry for complications.
Best Regards,
Petr
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v9 1/2] hung_task: Reset warning budget when problem gets resolved
2026-08-28 9:05 ` Petr Mladek
@ 2026-08-28 9:22 ` Lance Yang
0 siblings, 0 replies; 12+ messages in thread
From: Lance Yang @ 2026-08-28 9:22 UTC (permalink / raw)
To: Petr Mladek
Cc: atomlin, akpm, mhiramat, linux-kernel, david.laight.linux, neelx,
sean, chjohnst, steve, mproche, nick.lange
On 2026/8/28 17:05, Petr Mladek wrote:
> On Thu 2026-08-27 23:30:01, Lance Yang wrote:
>> On Wed, Aug 26, 2026 at 01:17:47PM +0200, Petr Mladek wrote:
>>>> @@ -314,6 +317,11 @@ static void check_hung_uninterruptible_tasks(unsigned long timeout)
>>>> if (test_taint(TAINT_DIE) || did_panic)
>>>> return;
>>>>
>>>> + if (atomic_xchg(&reset_hung_task_warnings, 0))
>>>
>>> I would use here atomic_xchg_acquire(). It serializes the ordering
>>> of reset_hung_task_warnings vs sysctl_hung_task_warnings.
>>> It would make it symetric with the barrier in the sysctl handler.
>>
>> Yep, _acquire is enough here. Plain atomic_xchg() is already fully
>> ordered, though, so this looks like making the intent clearer rather
>> than fixing the ordering :)
>
> Yes, my intention was to make the ordering more clear and symmetric.
Yep, that makes sense. atomic_xchg_acquire() is a better fit here :)
> The original code worked because the barrier was even stronger.
>
>> The old-value return already makes plain atomic_xchg() fully ordered :)
>>
>> ORDERING (see memory-barriers.txt)
>> --------
>>
>> The rule of thumb:
>> ...
>> - RMW operations that have a return value are fully ordered;
>> ...
>> Except of course when a successful operation has an explicit ordering
>> like:
>>
>> {}_relaxed: unordered
>> {}_acquire: the R of the RMW (or atomic_read) is an ACQUIRE
>> {}_release: the W of the RMW (or atomic_set) is a RELEASE
>>
>>>
>>>> + hung_task_warnings_printed =
>>>> + READ_ONCE(sysctl_hung_task_warnings);
>>>
>>> This would work only when "sysctl_hung_task_warnings"
>>> is updated using WRITE_ONCE(). But it seems that this
>>> is not the case. My understading is that it is updated by:
>>
>> Wait, I think proc_dointvec_minmax() already handles this.
>
> You are right.
>
>> For proc_dointvec_minmax(), the converter is:
>>
>> int proc_dointvec_minmax(const struct ctl_table *table, int dir,
>> void *buffer, size_t *lenp, loff_t *ppos)
>> {
>> return do_proc_dointvec(table, dir, buffer, lenp, ppos,
>> do_proc_int_conv_minmax);
>> }
>>
>> Here, i is table->data, while lval is local:
>
> I have missed this.
No worries at all. This was easy to miss in that call chain ...
>
>> static int do_proc_dointvec(const struct ctl_table *table, int dir,
>> void *buffer, size_t *lenp, loff_t *ppos,
>> int (*conv)(bool *negp, unsigned long *u_ptr, int *k_ptr,
>> int dir, const struct ctl_table *table))
>> {
>> ...
>> i = (int *) table->data;
>> vleft = table->maxlen / sizeof(*i);
>> ...
>> for (; left && vleft--; i++, first=0) {
>> unsigned long lval;
>> bool neg;
>>
>> if (SYSCTL_USER_TO_KERN(dir)) {
>> proc_skip_spaces(&p, &left);
>>
>> if (!left)
>> break;
>> err = proc_get_long(&p, &left, &lval, &neg,
>> proc_wspace_sep,
>> sizeof(proc_wspace_sep), NULL);
>
> I have missed that proc_get_long() assigns the value to the local
> variable @lval.
>
>> if (err)
>> break;
>> if (conv(&neg, &lval, i, 1, table)) {
>> err = -EINVAL;
>> break;
>
> The real asigment to table->data is done here. And I agree that it
> goes down to proc_int_conv() which does WRITE_ONCE().
>
> So, we are on the safe side and do _not_ need the proxy table.
Agreed.
>
> Now, I am not sure whether we need v10. It might be worth it.
> AFAIK, Andrew has not taken this patchset yet...
I think we do. Definitely :) And if Andrew hasn't picked it up
yet, even better. There's still time to fold the changes in :)
>
> I am sorry for complications.
No need to apologize at all, Petr. I really appreciate you taking
another careful look!
Cheers, Lance
^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2026-08-28 9:23 UTC | newest]
Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-08-14 13:57 [PATCH v9 0/2] hung_task: Improve warning budget handling and task reporting Aaron Tomlin
2026-08-14 13:57 ` [PATCH v9 1/2] hung_task: Reset warning budget when problem gets resolved Aaron Tomlin
2026-08-14 15:58 ` Lance Yang
2026-08-14 17:22 ` Aaron Tomlin
2026-08-26 11:17 ` Petr Mladek
2026-08-27 15:30 ` Lance Yang
2026-08-28 9:05 ` Petr Mladek
2026-08-28 9:22 ` Lance Yang
2026-08-14 13:57 ` [PATCH v9 2/2] hung_task: Log summary line when warning budget is exhausted Aaron Tomlin
2026-08-14 16:27 ` Lance Yang
2026-08-14 17:26 ` Aaron Tomlin
2026-08-26 11:31 ` Petr Mladek
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®