From: Lance Yang <lance.yang@linux.dev>
To: pmladek@suse.com, atomlin@atomlin.com
Cc: akpm@linux-foundation.org, lance.yang@linux.dev,
mhiramat@kernel.org, linux-kernel@vger.kernel.org,
david.laight.linux@gmail.com, neelx@suse.com, sean@ashe.io,
chjohnst@gmail.com, steve@abita.co, mproche@gmail.com,
nick.lange@gmail.com
Subject: Re: [PATCH v9 1/2] hung_task: Reset warning budget when problem gets resolved
Date: Thu, 27 Aug 2026 23:30:01 +0800 [thread overview]
Message-ID: <20260827153001.18515-1-lance.yang@linux.dev> (raw)
In-Reply-To: <ao7LW7-3GjJzKNp0@pathway.suse.cz>
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
>
next prev parent reply other threads:[~2026-08-27 15:30 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
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 [this message]
2026-08-28 9:05 ` Petr Mladek
2026-08-28 9:22 ` Lance Yang
2026-08-29 13:54 ` Aaron Tomlin
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
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260827153001.18515-1-lance.yang@linux.dev \
--to=lance.yang@linux.dev \
--cc=akpm@linux-foundation.org \
--cc=atomlin@atomlin.com \
--cc=chjohnst@gmail.com \
--cc=david.laight.linux@gmail.com \
--cc=linux-kernel@vger.kernel.org \
--cc=mhiramat@kernel.org \
--cc=mproche@gmail.com \
--cc=neelx@suse.com \
--cc=nick.lange@gmail.com \
--cc=pmladek@suse.com \
--cc=sean@ashe.io \
--cc=steve@abita.co \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.