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: 12+ 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-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 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®