public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
From: "Troy Mitchell" <troy.mitchell@linux.dev>
To: "Troy Mitchell" <troy.mitchell@linux.dev>,
	"Aurelien Jarno" <aurelien@aurel32.net>
Cc: "Paul Walmsley" <pjw@kernel.org>,
	"Palmer Dabbelt" <palmer@dabbelt.com>,
	"Albert Ou" <aou@eecs.berkeley.edu>,
	"Alexandre Ghiti" <alex@ghiti.fr>,
	<linux-riscv@lists.infradead.org>, <linux-kernel@vger.kernel.org>,
	<troy.mitchell@linux.spacemit.com>
Subject: Re: [PATCH RFC] riscv: disable local interrupts and stop other CPUs before restart
Date: Wed, 11 Mar 2026 17:54:02 +0800	[thread overview]
Message-ID: <DGZV1C1YZJB8.1KOS6S0ICO4AE@linux.dev> (raw)
In-Reply-To: <DGZUXM2Y3VE5.1Z9ZY97DT5XBA@linux.dev>

On Wed Mar 11, 2026 at 5:49 PM CST, Troy Mitchell wrote:
> On Wed Mar 11, 2026 at 2:47 PM CST, Aurelien Jarno wrote:
>> Hi Troy,
>>
>> On 2026-03-11 10:51, Troy Mitchell wrote:
>>> Currently, the RISC-V implementation of machine_restart() directly calls
>>> do_kernel_restart() without disabling local interrupts or stopping other
>>> CPUs. This missing architectural setup causes fatal issues for systems
>>> that rely on external peripherals (e.g., I2C PMICs) to execute the system
>>> restart when CONFIG_PREEMPT_RCU is enabled.
>>> 
>>> When a restart handler relies on the I2C subsystem, the I2C core checks
>>> i2c_in_atomic_xfer_mode() to decide whether to use the sleepable xfer
>>> or the polling atomic_xfer. This check evaluates to true if
>>> (!preemptible() || irqs_disabled()).
>>> 
>>> During do_kernel_restart(), the restart handlers are invoked via
>>> atomic_notifier_call_chain(), which holds an RCU read lock.
>>> The behavior diverges based on the preemption model:
>>> 1. Under CONFIG_PREEMPT_VOLUNTARY or CONFIG_PREEMPT_NONE, rcu_read_lock()
>>>    implicitly disables preemption. preemptible() evaluates to false, and
>>>    the I2C core correctly routes to the atomic, polling transfer path.
>>> 2. Under CONFIG_PREEMPT_RCU, rcu_read_lock() does NOT disable preemption.
>>>    Since machine_restart() left local interrupts enabled, irqs_disabled()
>>>    is false, and preempt_count is 0. Consequently, preemptible() evaluates
>>>    to true.
>>> 
>>> As a result, the I2C core falsely assumes a sleepable context and routes
>>> the transfer to the standard master_xfer path. This inevitably triggers a
>>> schedule() call while holding the RCU read lock, resulting in a fatal splat:
>>> "Voluntary context switch within RCU read-side critical section!" and
>>> a system hang.
>>> 
>>> Align RISC-V with other major architectures (e.g., ARM64) by adding
>>> local_irq_disable() and smp_send_stop() to machine_restart().
>>> - local_irq_disable() guarantees a strict atomic context, forcing sub-
>>>   systems like I2C to always fall back to polling mode.
>>> - smp_send_stop() ensures exclusive hardware access by quiescing other
>>>   CPUs, preventing them from holding bus locks (e.g., I2C spinlocks)
>>>   during the final restart phase.
>>> 
>>> Signed-off-by: Troy Mitchell <troy.mitchell@linux.dev>
>>> ---
>>>  arch/riscv/kernel/reset.c | 5 +++++
>>>  1 file changed, 5 insertions(+)
>>
>> Thanks. I have been debugging that and it matches my analysis.
>>
>>> diff --git a/arch/riscv/kernel/reset.c b/arch/riscv/kernel/reset.c
>>> index 912288572226..7a5dcfdc3674 100644
>>> --- a/arch/riscv/kernel/reset.c
>>> +++ b/arch/riscv/kernel/reset.c
>>> @@ -5,6 +5,7 @@
>>>  
>>>  #include <linux/reboot.h>
>>>  #include <linux/pm.h>
>>> +#include <linux/smp.h>
>>>  
>>>  static void default_power_off(void)
>>>  {
>>> @@ -17,6 +18,10 @@ EXPORT_SYMBOL(pm_power_off);
>>>  
>>>  void machine_restart(char *cmd)
>>>  {
>>> +	/* Disable interrupts first */
>>> +	local_irq_disable();
>>> +	smp_send_stop();
>>> +
>>>  	do_kernel_restart(cmd);
>>>  	while (1);
>>>  }
>>> 
>>
>> I have started to change the power reset driver to call the I2C code 
>> from a workqueue instead of directly from the notifier call back, but 
>> that's just papering over the issue.
> Since the requirements for i2c_in_atomic() weren't being met, I initially
> considered disabling interrupts before the p1 restart code.
>
> However, I didn't feel that was a generic enough solution, so I looked into
> the architecture-level implementation. That's when I realized how bare-bones
> the current RISC-V machine_restart() actually is..
>
FYI, the discussion with Aurelien started here [1].

Link: https://lore.kernel.org/all/DGZM6WUAVPPS.20Y0NIZYI4572@linux.spacemit.com/ [1]


                          - Troy
>>
>> Your approach is much better and 
>> aligns riscv64 with other architectures, which is important as we might 
>> have shared PMIC drivers.
>>
>> Therefore:
>>
>> Tested-by: Aurelien Jarno <aurelien@aurel32.net>
>> Reviewed-by: Aurelien Jarno <aurelien@aurel32.net>
> Thanks. :)
>
>                                     - Troy
>>
>> Regards
>> Aurelien


  reply	other threads:[~2026-03-11  9:55 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-03-11  2:51 [PATCH RFC] riscv: disable local interrupts and stop other CPUs before restart Troy Mitchell
2026-03-11  6:47 ` Aurelien Jarno
2026-03-11  9:49   ` Troy Mitchell
2026-03-11  9:54     ` Troy Mitchell [this message]
2026-03-12  3:05 ` Vivian Wang
2026-03-16  7:23   ` Troy Mitchell
2026-03-16 13:19     ` Samuel Holland
2026-03-17  2:45       ` Troy Mitchell
2026-03-17  4:07         ` Samuel Holland
2026-03-17  7:42           ` Troy Mitchell

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=DGZV1C1YZJB8.1KOS6S0ICO4AE@linux.dev \
    --to=troy.mitchell@linux.dev \
    --cc=alex@ghiti.fr \
    --cc=aou@eecs.berkeley.edu \
    --cc=aurelien@aurel32.net \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-riscv@lists.infradead.org \
    --cc=palmer@dabbelt.com \
    --cc=pjw@kernel.org \
    --cc=troy.mitchell@linux.spacemit.com \
    /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