mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Claudiu Beznea <claudiu.beznea@tuxon.dev>
To: Manivannan Sadhasivam <mani@kernel.org>
Cc: Claudiu Beznea <claudiu.beznea+renesas@tuxon.dev>,
	lpieralisi@kernel.org, kwilczynski@kernel.org, robh@kernel.org,
	bhelgaas@google.com, p.zabel@pengutronix.de,
	linux-pci@vger.kernel.org, linux-renesas-soc@vger.kernel.org,
	linux-kernel@vger.kernel.org,
	John Madieu <john.madieu.xa@bp.renesas.com>,
	Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
Subject: Re: [PATCH v3 7/7] PCI: rzg3s-host: Re-enumerate the bus on PCIe link-state changes
Date: Tue, 22 Sep 2026 10:50:24 +0300	[thread overview]
Message-ID: <004c9d3b-6f9b-4bd7-85a3-4acdd5d95924@tuxon.dev> (raw)
In-Reply-To: <n7cafny3y6rr63tx6rkt5umxl3f4rsxpu2ojze7luudzzr4flb@egj7dru2bnhq>



On 9/21/26 19:49, Manivannan Sadhasivam wrote:
> On Fri, Sep 11, 2026 at 03:14:25PM +0300, claudiu beznea wrote:
>> Hi, Mani,
>>
>> On 9/10/26 17:38, Manivannan Sadhasivam wrote:
>>> On Fri, Aug 14, 2026 at 05:13:12PM +0300, Claudiu Beznea wrote:
>>>> From: John Madieu <john.madieu.xa@bp.renesas.com>
>>>>
>>>> The RZ/G3{E, S}, RZ/V2{H(P), N} PCIe controllers does not expose the
>>>> standard PCIe Slot Capability registers, so the generic pciehp driver
>>>> cannot be used. The only link-state signal the hardware provides is the
>>>> DL_UpDown bit in the PEIS0 event status register, which is raised on every
>>>> Data Link layer up/down transition.
>>>>
>>>> Enable DL_UpDown in PEIE0 and hook up an interrupt handler so the driver
>>>> can react to link-state changes: a device that trains after boot gets
>>>> enumerated, and a device that disappears on link loss is removed. This
>>>> provides hotplug-like behavior without the PCI hotplug core, which is
>>>> unavailable for the reason above.
>>>>
>>>> On a DL_UpDown event the handler acks the W1C status bit and schedules a
>>>> worker that inspects PCSTAT1.DL_DOWN_STS:
>>>>
>>>>     - link up: re-run max link speed negotiation, wait for the link to
>>>>       settle and pci_rescan_bus() the root bus;
>>>>     - link down: reset the root port, walk the bus in reverse and
>>>>       pci_stop_and_remove_bus_device() each child.
>>>>
>>>> Both paths take pci_lock_rescan_remove() to serialize against the PCI
>>>> core.
>>>>
>>>> While enumeration succeeds without resetting the root port, performing
>>>> reads and writes to an NVMe endpoint after a link down/link up cycle
>>>> results in failures on some devices. Address this by implementing
>>>> pci_host_bridge::reset_root_port() for the RZ/G3S PCIe driver.
>>>>
>>>> The implementation of pci_host_bridge::reset_root_port() masks all
>>>> enabled interrupts and synchronizes them before resetting the controller
>>>> to prevent asynchronous events from interfering with the reset operation.
>>>>
>>>> After the controller is reset, all previously masked interrupts are
>>>> restored.
>>>>
>>>> Since rzg3s_pcie_host_stop() or rzg3s_pcie_host_start() can fail
>>>> during a root port reset, introduce struct rzg3s_pcie_host::state to
>>>> track the host controller state. The interrupt handlers and register
>>>> access paths consult this state to avoid accessing the controller after a
>>>> failed reset. This was implemented to be able to re-use the
>>>> rzg3s_pcie_host_stop()/rzg3s_pcie_host_start() as is (since they call
>>>> functions which can sleep).
>>>>
>>>> The introduced states are START, STOP, PROCESS, and PORT_RESET. The initial
>>>> state is STOP. After the controller is initialized, the state is switched
>>>> to START. Any API exposed through struct pci_ops switches the controller
>>>> to the PROCESS state, as do the interrupt handlers.
>>>>
>>>> rzg3s_pcie_host_reset_root_port() switches the state to PORT_RESET to
>>>> prevent any controller access while the Root Port reset is in progress.
>>>>
>>>> A controller left in a broken state (STOP) after a failed root port reset
>>>> can recover after a system suspend/resume cycle, since
>>>> rzg3s_pcie_host_start() is invoked again during resume.
>>>>
>>>> Link events are processed only after the controller has been fully
>>>> initialized.
>>>>
>>>> While at it, make probe tolerant of an absent device. Previously, if the
>>>> link failed to come up during rzg3s_pcie_host_init(), probe tore the
>>>> controller back down and failed. Distinguish this case with -ENODEV,
>>>> leave the controller and refclk running, and let the link-up path
>>>> enumerate the device once it appears.
>>>>
>>>
>>> Please split the DL_UpDown addition and reset_root_port() into separate patches.
>>
>> If splitting, it would mean the DL_UpDown patch will be broken (at least for
>> RZ/G3S SoC). Would that be OK with you?
>>
> 
> It will not be broken, but just that the RP won't be reset through platform
> specific reset hook. So it is OK to have the successive commit add that
> functionality, provided it is mentioned in the commit message.

OK, will split it as suggested.

> 
>>>
>>>> Signed-off-by: John Madieu <john.madieu.xa@bp.renesas.com>
>>>> Co-developed-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
>>>> Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
>>>> ---
>>>>
>>>> Changes in v3:
>>>> - added RZG3S_PCI_PEIE0_DL_UPDOWN
>>>> - re-worked the support by implemeting
>>>>     struct pci_host_bridge::reset_root_port()
>>>> - introduced the struct rzg3s_pcie_host::state to:
>>>> -- avoid touching the controller while a reset root port is in progress
>>>> -- and avoid touching the controller in case a reset root port failed
>>>> -- and to be able to re-use the already existing code in the reset
>>>>      root port function
>>>> -- and added CLASS() constructs helpers for it to keep the state handling
>>>>      code simpler
>>>> - updated the patch description to reflect the updates
>>>>
>>>>    drivers/pci/controller/pcie-rzg3s-host.c | 375 +++++++++++++++++++++--
>>>>    1 file changed, 356 insertions(+), 19 deletions(-)
>>>>
>>>> diff --git a/drivers/pci/controller/pcie-rzg3s-host.c b/drivers/pci/controller/pcie-rzg3s-host.c
>>>> index 4765ac1befba..b3531468ef38 100644
>>>> --- a/drivers/pci/controller/pcie-rzg3s-host.c
>>>> +++ b/drivers/pci/controller/pcie-rzg3s-host.c
>>>> @@ -86,6 +86,7 @@
>>>>    #define RZG3S_PCI_MSGRCVIS_MRI			BIT(24)
>>>>    #define RZG3S_PCI_PEIE0				0x200
>>>> +#define RZG3S_PCI_PEIE0_DL_UPDOWN		BIT(9)
>>>>    #define RZG3S_PCI_PEIS0				0x204
>>>>    #define RZG3S_PCI_PEIS0_RX_DLLP_PM_ENTER	BIT(12)
>>>> @@ -310,6 +311,24 @@ struct rzg3s_pcie_port {
>>>>    	u32 device_id;
>>>>    };
>>>> +/**
>>>> + * enum rzg3s_pcie_host_state - RZ/G3S PCIe Host state
>>>> + * @RZG3S_PCIE_HOST_STATE_STOP: Host is stopped (initial state, reached from
>>>> + *                              PORT_RESET, START)
>>>> + * @RZG3S_PCIE_HOST_STATE_START: Host is started (reached from STOP, PROCESS,
>>>> + *                               PORT_RESET)
>>>> + * @RZG3S_PCIE_HOST_STATE_PROCESS: Host is started and is processing requests
>>>> + *                                 (reached from START)
>>>> + * @RZG3S_PCIE_HOST_STATE_PORT_RESET: Host root port is resetting (reached from
>>>> + *                                    START)
>>>> + */
>>>> +enum rzg3s_pcie_host_state {
>>>> +	RZG3S_PCIE_HOST_STATE_STOP,
>>>> +	RZG3S_PCIE_HOST_STATE_START,
>>>> +	RZG3S_PCIE_HOST_STATE_PORT_RESET,
>>>> +	RZG3S_PCIE_HOST_STATE_PROCESS,
>>>> +};
>>>> +
>>>>    /**
>>>>     * struct rzg3s_pcie_host - RZ/G3S PCIe data structure
>>>>     * @axi: base address for AXI registers
>>>> @@ -323,6 +342,8 @@ struct rzg3s_pcie_port {
>>>>     * @msi: MSI data structure
>>>>     * @port: PCIe Root Port
>>>>     * @hw_lock: lock for access to the HW resources
>>>> + * @state: PCIe controller state
>>>> + * @event_irq: PCIe event interrupt for DL_UpDown detection
>>>>     * @intx_irqs: INTx interrupts
>>>>     * @max_link_speed: maximum supported link speed
>>>>     * @controller_id: PCIe controller identifier, used for System Controller access
>>>> @@ -340,6 +361,8 @@ struct rzg3s_pcie_host {
>>>>    	struct rzg3s_pcie_msi msi;
>>>>    	struct rzg3s_pcie_port port;
>>>>    	raw_spinlock_t hw_lock;
>>>> +	atomic_t state;
>>>> +	int event_irq;
>>>>    	int intx_irqs[PCI_NUM_INTX];
>>>>    	int max_link_speed;
>>>>    	enum rzg3s_pcie_controller_id controller_id;
>>>> @@ -348,6 +371,61 @@ struct rzg3s_pcie_host {
>>>>    #define rzg3s_msi_to_host(_msi)	container_of(_msi, struct rzg3s_pcie_host, msi)
>>>> +/**
>>>> + * struct rzg3s_pcie_host_atomic_state - RZ/G3S PCIe state data structure
>>>> + * @state: Atomic state variable to operate on. Should point to
>>>> + *         struct rzg3s_pcie_host::state.
>>>> + * @expect: Expected host state. The host state is not changed if the
>>>> + *          current host state differs from the expected state.
>>>> + * @saved: Saved host state. When the host state is changed, the previous
>>>> + *         state is saved in this variable. This is necessary to restore
>>>> + *         the previous state after the protected section is executed.
>>>> + *
>>>> + * This structure is necessary for state setting and restoration using
>>>> + * CLASS() constructs.
>>>> + */
>>>> +struct rzg3s_pcie_host_atomic_state {
>>>> +	atomic_t *state;
>>>> +	enum rzg3s_pcie_host_state expect;
>>>> +	enum rzg3s_pcie_host_state saved;
>>>> +};
>>>> +
>>>> +static struct rzg3s_pcie_host_atomic_state
>>>> +rzg3s_pcie_host_atomic_state_save(atomic_t *state, int expect, int newval)
>>>> +{
>>>> +	return (struct rzg3s_pcie_host_atomic_state){
>>>> +		.state = state,
>>>> +		.expect = expect,
>>>> +		.saved = atomic_cmpxchg(state, expect, newval),
>>>> +	};
>>>> +}
>>>> +
>>>> +static void
>>>> +rzg3s_pcie_host_atomic_state_restore(struct rzg3s_pcie_host_atomic_state state)
>>>> +{
>>>> +	if (state.saved == state.expect)
>>>> +		atomic_xchg(state.state, state.expect);
>>>> +}
>>>> +
>>>> +DEFINE_CLASS(rzg3s_pcie_host_state_lock,
>>>> +	     struct rzg3s_pcie_host_atomic_state,
>>>> +	     rzg3s_pcie_host_atomic_state_restore(_T),
>>>> +	     rzg3s_pcie_host_atomic_state_save(state, expect, newval),
>>>> +	     atomic_t *state, int expect, int newval)
>>>> +
>>>> +/* Use it to change the state w/o the need to restore it on function exit. */
>>>> +#define RZG3S_PCIE_HOST_STATE_CHANGE(_state, _from, _to) \
>>>> +	CLASS(rzg3s_pcie_host_state_lock, _lock) \
>>>> +	      (_state, RZG3S_PCIE_HOST_STATE_##_from, \
>>>> +		       RZG3S_PCIE_HOST_STATE_##_to) \
>>>> +
>>>> +/*
>>>> + * Use it to check if the state change failed. _from is the initial state.
>>>> + * Use it in conjunction with RZG3S_PCIE_HOST_STATE_CHANGE().
>>>> + */
>>>> +#define RZG3S_PCIE_HOST_STATE_CHANGE_FAILED(_from) \
>>>> +	(_lock.saved != RZG3S_PCIE_HOST_STATE_##_from)
>>>> +
>>>
>>> I don't really see a need for all these state management. None of the other
>>> controller drivers are doing and the serialization provided by the PCI core
>>> using pci_lock should be enough. If you find any issue or race, please share it
>>> here.
>>
>> The problems I see are the following:
>>
>> 1/ In theory, the rzg3s_pcie_host_start() in .reset_root_port() can fail. If
>>     that happens the controller is left with the clocks and resets disabled.
>>     Accessing  it later in this state may lead to sync aborts, and the system
>>     being fully blocked. This is how the HW behaves. Re-enabling clocks and
>>     resets in this situation may lead to unbalanced  reference counters for them.
>>
> 
> Once reset_root_port() fails, the controller is literally dead and the error is
> propagated to the PCI core. So who is going to access it later?

Actually, here I wanted to sustain that we need this to properly decide what to 
do with the clocks and resets on suspend/resume. Could use a bool as suggested 
bellow.

> 
>> 2/ In theory, struct pci_ops::{read, write} ops could be called while the reset
>>     is in progress, if I'm not wrong. That can be avoided by using a common spin
>>     lock but we can't use it in .reset_root_port() since this calls APIs
>>     that could sleep (e.g. clk_prepare_enable()/clk_disable_unprepare())
>>
> 
> AFAICS, 'block_cfg_access' will be set for the entire duration of the
> reset_root_port() invocation and it will prevent config space accessors to race
> with the reset.

I wasn't aware of this, thanks for sharing.

> 
>> 3/ Since at the moment the .suspend() is called the IRQs are enabled, the link
>>     event and the .suspend() can race. This could also be avoided by using
>>     a spin lock but, again, there are APIs on .reset_root_port() that can sleep.
>>
> 
> suspend/resume callbacks are only invoked in the no_irq phase.

I was referring to wakeup IRQs. At the moment we don't handle any wakeup IRQs in 
this driver. So, I think we should be safe dropping the code as you suggested.

> 
>> 4/ To keep sane the reference counted resources, handled in
>>     rzg3s_pcie_host_stop()/rzg3s_pcie_host_start(), the state track would be
>>     needed, for .reset_root_port() failure case and racing, since these functions
>>     are also called in suspend/resume
>>
> 
> You can solve it using a simple bool flag.

OK

> 
> So I still do not see a real need for these complex state management.

OK

Thank you,
Claudiu

      reply	other threads:[~2026-09-22  7:50 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-14 14:13 [PATCH v3 0/7] PCI: rzg3s-host: Add PCIe hotplug support Claudiu Beznea
2026-08-14 14:13 ` [PATCH v3 1/7] PCI: rzg3s-host: Follow hardware manual clock/reset initialization order Claudiu Beznea
2026-08-14 14:13 ` [PATCH v3 2/7] PCI: rzg3s-host: Fix runtime PM handling in the NOIRQ suspend/resume phase Claudiu Beznea
2026-09-10 14:27   ` Manivannan Sadhasivam
2026-08-14 14:13 ` [PATCH v3 3/7] PCI: rzg3s-host: Drop nop instructions Claudiu Beznea
2026-08-14 14:13 ` [PATCH v3 4/7] PCI: rzg3s-host: Move host configuration code together Claudiu Beznea
2026-08-14 14:13 ` [PATCH v3 5/7] PCI: rzg3s-host: Move suspend/resume code into dedicated functions Claudiu Beznea
2026-08-14 14:13 ` [PATCH v3 6/7] PCI: rzg3s-host: Move IRQ domain setup code Claudiu Beznea
2026-08-14 14:13 ` [PATCH v3 7/7] PCI: rzg3s-host: Re-enumerate the bus on PCIe link-state changes Claudiu Beznea
2026-09-10 14:38   ` Manivannan Sadhasivam
2026-09-11 12:14     ` claudiu beznea
2026-09-21 16:49       ` Manivannan Sadhasivam
2026-09-22  7:50         ` Claudiu Beznea [this message]

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=004c9d3b-6f9b-4bd7-85a3-4acdd5d95924@tuxon.dev \
    --to=claudiu.beznea@tuxon.dev \
    --cc=bhelgaas@google.com \
    --cc=claudiu.beznea+renesas@tuxon.dev \
    --cc=claudiu.beznea.uj@bp.renesas.com \
    --cc=john.madieu.xa@bp.renesas.com \
    --cc=kwilczynski@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-pci@vger.kernel.org \
    --cc=linux-renesas-soc@vger.kernel.org \
    --cc=lpieralisi@kernel.org \
    --cc=mani@kernel.org \
    --cc=p.zabel@pengutronix.de \
    --cc=robh@kernel.org \
    /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®