From: sashiko-bot@kernel.org
To: "Claudiu Beznea" <claudiu.beznea@kernel.org>
Cc: linux-pci@vger.kernel.org, wsa+renesas@sang-engineering.com
Subject: Re: [PATCH v7 13/14] PCI: rzg3s-host: Re-enumerate the bus on PCIe link-state changes
Date: Mon, 19 Oct 2026 07:30:16 +0000 [thread overview]
Message-ID: <sashiko-outbox-176746@kernel.org> (raw)
In-Reply-To: <20261019065025.2146539-14-claudiu.beznea@kernel.org>
Thank you for your contribution! Sashiko AI review found 7 potential issue(s) to consider:
- [High] pci: rzg3s-host: rzg3s_pcie_remove_devices() marks Root Port disconnected
- [High] pci: rzg3s-host: rzg3s_pcie_pm_notifier() misses hibernation PM events
- [High] PCI: rzg3s-host: race between rzg3s_pcie_probe() and PM notifier callbacks
- [High] PCI: rzg3s-host: deadlock on pm_chain_head.rwsem in rzg3s_pcie_pm_notifier()
- [Medium] pci: rzg3s-host: late PEIS0_DL_UPDOWN clear loses link bounce events
- [Medium] pci: rzg3s-host: rzg3s_pcie_link_event() reads config before link settles
- [Medium] PCI: rzg3s-host: controller registers accessed after resume_noirq() failure
--
commit 1730fab066afd00592ed2f6ea77d04307ed48cf0
Author: John Madieu <john.madieu.xa@bp.renesas.com>
PCI: rzg3s-host: Re-enumerate the bus on PCIe link-state changes
This commit enables the DL_UpDown interrupt on RZ/G3S and related PCIe
controllers to detect link-state transitions and hotplug-like events,
removing or rescanning devices on the bus accordingly. It also adds a PM
notifier to disable link event interrupts across system suspend and force a
bus rescan on resume, and makes probe tolerant of an initially down link.
> diff --git a/drivers/pci/controller/pcie-rzg3s-host.c b/drivers/pci/controller/pcie-rzg3s-host.c
> index 1ced6d83c7c18..a1c9e29f43e5d 100644
> --- a/drivers/pci/controller/pcie-rzg3s-host.c
> +++ b/drivers/pci/controller/pcie-rzg3s-host.c
[ ... ]
> @@ -1094,12 +1108,187 @@ static int rzg3s_pcie_wait_for_link_up(struct rzg3s_pcie_host *host)
[ ... ]
> +static void rzg3s_pcie_remove_devices(struct pci_bus *bus)
> +{
> + struct pci_dev *dev, *tmp;
> +
> + list_for_each_entry_safe_reverse(dev, tmp, &bus->devices, bus_list) {
> + pci_dev_set_disconnected(dev, NULL);
> + if (pci_has_subordinate(dev)) {
> + pci_walk_bus(dev->subordinate, pci_dev_set_disconnected,
> + NULL);
> + }
> + pci_stop_and_remove_bus_device(dev);
> + }
> +}
[Severity: High]
Both callers in rzg3s_pcie_link_event() pass bridge->bus (the root bus) to
rzg3s_pcie_remove_devices(), where dev in &bus->devices is the controller's
own integrated Root Port (0000:00:00.0) rather than an endpoint across the
link.
Should pci_dev_set_disconnected() only be called on dev->subordinate (as
done in rzg3s_pcie_resume_noirq()) rather than on dev itself?
Because pci_read_config_*() and pci_write_config_*() immediately abort with
PCIBIOS_DEVICE_NOT_FOUND when pci_dev_is_disconnected(dev) is true, marking
the Root Port as disconnected before pci_stop_and_remove_bus_device(dev)
causes Root Port teardown (such as pcie_aspm_exit_link_state(),
pcie_pme_remove(), aer_remove(), pci_msi_shutdown(), and
do_pci_disable_device()) to skip clearing hardware state in the Root Port's
local MMIO registers (host->pcie), leaving stale ASPM, PME, AER, MSI, and
PCI_COMMAND settings across subsequent re-enumerations.
> +
> +static void rzg3s_pcie_link_event(struct rzg3s_pcie_host *host, bool bounced)
> +{
> + struct pci_host_bridge *bridge = pci_host_bridge_from_priv(host);
> + struct pci_bus *bus = bridge->bus, *child;
> + u32 val;
> + int ret;
> +
> + pci_lock_rescan_remove();
> +
> + val = readl_relaxed(host->axi + RZG3S_PCI_PCSTAT1);
> + if (val & RZG3S_PCI_PCSTAT1_DL_DOWN_STS) {
> + if (!list_empty(&bus->devices)) {
> + dev_info(host->dev,
> + "PCIe link down, removing devices\n");
> + rzg3s_pcie_remove_devices(bus);
> + }
> + goto unlock;
> + }
> +
> + if (!list_empty(&bus->devices)) {
> + bool replaced = rzg3s_pcie_device_replaced(host);
> +
> + /*
> + * If bounced = false it means link rescanning (after probe
> + * or system resume), no link transition was recorded. The
> + * enumerated devices are intact, so remove them only if the
> + * connected device was replaced while link events were not
> + * delivered.
> + *
> + * If bounced = true it means at least one real link down/up
> + * cycle occurred.
> + */
> + if (!bounced && !replaced)
> + goto unlock;
> +
> + rzg3s_pcie_remove_devices(bus);
> + }
> +
> + dev_info(host->dev, "PCIe link up, rescanning bus\n");
> +
> + ret = rzg3s_pcie_set_max_link_speed(host);
> + if (ret)
> + dev_info(host->dev, "Failed to set max link speed\n");
> +
> + pci_host_common_link_train_delay(host->max_link_speed);
[Severity: Medium]
When bounced is true and !list_empty(&bus->devices), does calling
rzg3s_pcie_device_replaced(host) issue Type 0 configuration reads to the
downstream endpoint before the link has finished settling?
When a real DL_UpDown event occurs (bounced == true), the result of
rzg3s_pcie_device_replaced(host) is not needed because the devices are
removed unconditionally, and rzg3s_pcie_set_max_link_speed() (which polls
for LTSSM L0 and negotiates link speed) and
pci_host_common_link_train_delay() have not been called yet. Should
rzg3s_pcie_device_replaced(host) only be called when !bounced?
[ ... ]
> @@ -1901,6 +2108,44 @@ static void rzv2h_pcie_release_lanes(void *data)
> rzv2h_num_total_lanes -= host->num_lanes;
> }
>
> +static int rzg3s_pcie_pm_notifier(struct notifier_block *nb,
> + unsigned long action, void *data)
> +{
> + struct rzg3s_pcie_host *host = container_of(nb, struct rzg3s_pcie_host,
> + pm_nb);
> +
> + switch (action) {
> + case PM_SUSPEND_PREPARE:
> + /* Disable link up/down interrupts. */
> + if (host->started)
> + writel_relaxed(0, host->axi + RZG3S_PCI_PEIE0);
> + disable_irq(host->event_irq);
> + break;
[Severity: High]
Can calling disable_irq(host->event_irq) inside rzg3s_pcie_pm_notifier()
deadlock on pm_chain_head.rwsem if rzg3s_pcie_event_irq_thread() is
concurrently probing or removing a PCI device whose driver registers or
unregisters a PM notifier?
During system suspend:
suspend_prepare()
pm_notifier_call_chain_robust(PM_SUSPEND_PREPARE, PM_POST_SUSPEND)
blocking_notifier_call_chain_robust(&pm_chain_head, ...)
down_read(&pm_chain_head.rwsem)
rzg3s_pcie_pm_notifier()
disable_irq(host->event_irq)
synchronize_irq(host->event_irq)
If rzg3s_pcie_event_irq_thread() is simultaneously running
rzg3s_pcie_link_event(), it calls pci_bus_add_devices() or
rzg3s_pcie_remove_devices() -> pci_stop_and_remove_bus_device(), which
synchronously runs PCI endpoint driver .probe() or .remove() callbacks.
Several PCI drivers (such as ath11k_pci, wil6210, amdgpu, and xe) call
register_pm_notifier() or unregister_pm_notifier() in those callbacks,
which blocks on down_write(&pm_chain_head.rwsem) while the suspend thread
holds down_read(&pm_chain_head.rwsem) waiting for the IRQ thread to finish.
> +
> + case PM_POST_SUSPEND:
> + /* Enable link up/down interrupts and force link re-scan. */
> + if (host->started) {
> + /*
> + * The link re-train in resume triggers the link up IRQ
> + * if there are connected devices. Clear the DL_UpDown
> + * status and trigger link_rescan to avoid
> + * re-enumerating the already connected devices.
> + */
> + writel_relaxed(RZG3S_PCI_PEIS0_DL_UPDOWN,
> + host->axi + RZG3S_PCI_PEIS0);
> + WRITE_ONCE(host->link_rescan, true);
> + writel_relaxed(RZG3S_PCI_PEIE0_DL_UPDOWN,
> + host->axi + RZG3S_PCI_PEIE0);
> + }
> + enable_irq(host->event_irq);
> + if (host->started)
> + irq_wake_thread(host->event_irq, host);
> + break;
> + }
> +
> + return NOTIFY_DONE;
> +}
[Severity: High]
rzg3s_pcie_pm_ops uses NOIRQ_SYSTEM_SLEEP_PM_OPS(rzg3s_pcie_suspend_noirq,
rzg3s_pcie_resume_noirq), which wires up the freeze_noirq, thaw_noirq,
poweroff_noirq, and restore_noirq callbacks for hibernation. However,
rzg3s_pcie_pm_notifier() only handles PM_SUSPEND_PREPARE and
PM_POST_SUSPEND.
During hibernation, won't omitting PM_HIBERNATION_PREPARE /
PM_RESTORE_PREPARE leave host->event_irq enabled during device freeze and
poweroff, and won't omitting PM_POST_HIBERNATION / PM_POST_RESTORE leave
RZG3S_PCI_PEIE0 cleared to 0 (after rzg3s_pcie_resume_noirq() ->
rzg3s_pcie_host_start() -> rzg3s_pcie_irq_init() resets it) and skip waking
the IRQ thread to rescan the bus?
>
> static int rzg3s_pcie_probe(struct platform_device *pdev)
> {
[ ... ]
> @@ -1999,15 +2244,38 @@ static int rzg3s_pcie_probe(struct platform_device *pdev)
> if (ret)
> goto power_resets_assert;
>
> + host->started = true;
> + host->pm_nb.notifier_call = rzg3s_pcie_pm_notifier;
> + ret = register_pm_notifier(&host->pm_nb);
> + if (ret)
> + goto host_probe_teardown;
> +
> bridge->sysdata = host;
> bridge->ops = &rzg3s_pcie_root_ops;
> bridge->child_ops = &rzg3s_pcie_child_ops;
> ret = pci_host_probe(bridge);
> if (ret)
> - goto host_probe_teardown;
> + goto pm_notifier_unregister;
> +
> + pci_lock_rescan_remove();
> + rzg3s_pcie_cache_connected_dev_id(host);
> + pci_unlock_rescan_remove();
> +
> + /*
> + * The link re-train triggers the link up IRQ if there are connected
> + * devices. Clear DL_UpDown status and trigger link_rescan to avoid
> + * re-enumerating the devices detected while pci_host_probe() and loose
> + * any potential event.
> + */
> + writel_relaxed(RZG3S_PCI_PEIS0_DL_UPDOWN, host->axi + RZG3S_PCI_PEIS0);
> + WRITE_ONCE(host->link_rescan, true);
> + writel_relaxed(RZG3S_PCI_PEIE0_DL_UPDOWN, host->axi + RZG3S_PCI_PEIE0);
> + irq_wake_thread(host->event_irq, host);
>
> return 0;
[Severity: High]
Because rzg3s_pcie_driver sets PROBE_PREFER_ASYNCHRONOUS, and system
suspend runs pm_notifier_call_chain_robust(PM_SUSPEND_PREPARE,
PM_POST_SUSPEND) before dpm_prepare() calls wait_for_device_probe(), can
rzg3s_pcie_probe() race with rzg3s_pcie_pm_notifier()?
1. host->started is set to true and register_pm_notifier(&host->pm_nb) is
called before pci_host_probe(bridge) initializes bridge->bus. If
suspend_prepare() aborts before pci_register_host_bridge() sets
bridge->bus, PM_POST_SUSPEND wakes rzg3s_pcie_event_irq_thread() ->
rzg3s_pcie_link_event(), which dereferences bridge->bus while it is still
NULL.
2. If PM_SUSPEND_PREPARE runs while rzg3s_pcie_probe() is inside
pci_host_probe(), rzg3s_pcie_pm_notifier() clears RZG3S_PCI_PEIE0 and calls
disable_irq(host->event_irq), but rzg3s_pcie_probe() then finishes before
wait_for_device_probe() returns and unconditionally enables
RZG3S_PCI_PEIE0_DL_UPDOWN and calls irq_wake_thread(host->event_irq, host),
waking the IRQ thread during suspend.
3. If PM_SUSPEND_PREPARE runs before register_pm_notifier(&host->pm_nb) is
called in rzg3s_pcie_probe(), PM_POST_SUSPEND will later call
enable_irq(host->event_irq) without a matching disable_irq().
[Severity: Medium]
In addition, clearing RZG3S_PCI_PEIS0_DL_UPDOWN here at the end of
rzg3s_pcie_probe() (after pci_host_probe(bridge)) and in
rzg3s_pcie_pm_notifier() under PM_POST_SUSPEND (after all device resume
callbacks or after an aborted suspend), rather than immediately after link
training in rzg3s_pcie_host_setup(), seems able to lose link bounce events:
If the same endpoint experiences a link down/up bounce during
pci_host_probe(), between rzg3s_pcie_resume_noirq() and PM_POST_SUSPEND, or
during an aborted suspend, clearing RZG3S_PCI_PEIS0_DL_UPDOWN discards the
hardware event so bounced is false. Because the same device is still
attached, rzg3s_pcie_device_replaced() returns false and
rzg3s_pcie_link_event() exits at:
if (!bounced && !replaced)
goto unlock;
leaving the endpoint with unconfigured BARs, PCI_COMMAND, and MSI state (or
at Gen1 speed if link-up raced with rzg3s_pcie_wait_for_link_up() timing
out in rzg3s_pcie_host_setup()). Should RZG3S_PCI_PEIS0_DL_UPDOWN be
cleared in rzg3s_pcie_host_setup() right after link training instead?
[ ... ]
> @@ -2041,8 +2309,25 @@ static int rzg3s_pcie_suspend_noirq(struct device *dev)
> static int rzg3s_pcie_resume_noirq(struct device *dev)
> {
> struct rzg3s_pcie_host *host = dev_get_drvdata(dev);
> + int ret;
>
> - return rzg3s_pcie_host_start(host);
> + ret = rzg3s_pcie_host_start(host);
> + if (ret)
> + return ret;
> +
> + if (rzg3s_pcie_device_replaced(host)) {
> + struct pci_host_bridge *bridge =
> + pci_host_bridge_from_priv(host);
> + struct pci_dev *port __free(pci_dev_put) =
> + pci_get_slot(bridge->bus, PCI_DEVFN(0, 0));
> +
> + if (port && port->subordinate) {
> + pci_walk_bus(port->subordinate,
> + pci_dev_set_disconnected, NULL);
> + }
> + }
> +
> + return 0;
> }
[Severity: Medium]
If rzg3s_pcie_host_start(host) fails in rzg3s_pcie_resume_noirq(), its
error path asserts host->power_resets and clears
RZG3S_SYSC_FUNC_ID_RST_RSM_B, leaving host->started as false.
Although rzg3s_pcie_pm_notifier() checks host->started to avoid touching
controller registers after a failed resume, the PM core still runs
pci_pm_resume_noirq() and pci_pm_resume() on the child PCI devices. Since
rzg3s_pcie_resume_noirq() returns early before marking the devices
disconnected, and neither rzg3s_pcie_root_ops nor rzg3s_pcie_child_ops
checks host->started, won't config-space accesses during child device
resume (and later runtime accesses to the remaining PCI devices) still read
and write host->pcie and host->axi while the controller is held in reset?
Should the devices also be marked disconnected (or config accesses guarded
by host->started) when rzg3s_pcie_host_start() fails?
[ ... ]
完美!现在检查所有规则:
1. Plain text only, no markdown headers, no backticks, no ALL CAPS shouting? Checked.
2. Commit header format: `commit 1730fab066afd00592ed2f6ea77d04307ed48cf0`, `Author: John Madieu <john.madieu.xa@bp.renesas.com>`, one line subject, brief summary (2 sentences), unified diff quoted with `> `. Checked.
3. No line numbers mentioned in comments? Checked!
4. Wrapped at 78 characters for added text? Checked.
5. `[Severity: <level>]` on its own line right above each inline comment block? Checked (VF1: High, VF4: Medium, PVF3: High, VF2: High, PVF1: High, VF3: Medium, PVF2: Medium - all 7 findings included).
6. Ends with a blank line? Yes, I will make sure the output ends with a blank line and contains only the report. Let's output the final text now.commit 1730fab066afd00592ed2f6ea77d04307ed48cf0
Author: John Madieu <john.madieu.xa@bp.renesas.com>
PCI: rzg3s-host: Re-enumerate the bus on PCIe link-state changes
This commit enables the DL_UpDown interrupt on RZ/G3S and related PCIe
controllers to detect link-state transitions and hotplug-like events,
removing or rescanning devices on the bus accordingly. It also adds a PM
notifier to disable link event interrupts across system suspend and force a
bus rescan on resume, and makes probe tolerant of an initially down link.
> diff --git a/drivers/pci/controller/pcie-rzg3s-host.c b/drivers/pci/controller/pcie-rzg3s-host.c
> index 1ced6d83c7c18..a1c9e29f43e5d 100644
> --- a/drivers/pci/controller/pcie-rzg3s-host.c
> +++ b/drivers/pci/controller/pcie-rzg3s-host.c
[ ... ]
> @@ -1094,12 +1108,187 @@ static int rzg3s_pcie_wait_for_link_up(struct rzg3s_pcie_host *host)
[ ... ]
> +static void rzg3s_pcie_remove_devices(struct pci_bus *bus)
> +{
> + struct pci_dev *dev, *tmp;
> +
> + list_for_each_entry_safe_reverse(dev, tmp, &bus->devices, bus_list) {
> + pci_dev_set_disconnected(dev, NULL);
> + if (pci_has_subordinate(dev)) {
> + pci_walk_bus(dev->subordinate, pci_dev_set_disconnected,
> + NULL);
> + }
> + pci_stop_and_remove_bus_device(dev);
> + }
> +}
[Severity: High]
Both callers in rzg3s_pcie_link_event() pass bridge->bus (the root bus) to
rzg3s_pcie_remove_devices(), where dev in &bus->devices is the controller's
own integrated Root Port (0000:00:00.0) rather than an endpoint across the
link.
Should pci_dev_set_disconnected() only be called on dev->subordinate (as
done in rzg3s_pcie_resume_noirq()) rather than on dev itself?
Because pci_read_config_*() and pci_write_config_*() immediately abort with
PCIBIOS_DEVICE_NOT_FOUND when pci_dev_is_disconnected(dev) is true, marking
the Root Port as disconnected before pci_stop_and_remove_bus_device(dev)
causes Root Port teardown (such as pcie_aspm_exit_link_state(),
pcie_pme_remove(), aer_remove(), pci_msi_shutdown(), and
do_pci_disable_device()) to skip clearing hardware state in the Root Port's
local MMIO registers (host->pcie), leaving stale ASPM, PME, AER, MSI, and
PCI_COMMAND settings across subsequent re-enumerations.
> +
> +static void rzg3s_pcie_link_event(struct rzg3s_pcie_host *host, bool bounced)
> +{
> + struct pci_host_bridge *bridge = pci_host_bridge_from_priv(host);
> + struct pci_bus *bus = bridge->bus, *child;
> + u32 val;
> + int ret;
> +
> + pci_lock_rescan_remove();
> +
> + val = readl_relaxed(host->axi + RZG3S_PCI_PCSTAT1);
> + if (val & RZG3S_PCI_PCSTAT1_DL_DOWN_STS) {
> + if (!list_empty(&bus->devices)) {
> + dev_info(host->dev,
> + "PCIe link down, removing devices\n");
> + rzg3s_pcie_remove_devices(bus);
> + }
> + goto unlock;
> + }
> +
> + if (!list_empty(&bus->devices)) {
> + bool replaced = rzg3s_pcie_device_replaced(host);
> +
> + /*
> + * If bounced = false it means link rescanning (after probe
> + * or system resume), no link transition was recorded. The
> + * enumerated devices are intact, so remove them only if the
> + * connected device was replaced while link events were not
> + * delivered.
> + *
> + * If bounced = true it means at least one real link down/up
> + * cycle occurred.
> + */
> + if (!bounced && !replaced)
> + goto unlock;
> +
> + rzg3s_pcie_remove_devices(bus);
> + }
> +
> + dev_info(host->dev, "PCIe link up, rescanning bus\n");
> +
> + ret = rzg3s_pcie_set_max_link_speed(host);
> + if (ret)
> + dev_info(host->dev, "Failed to set max link speed\n");
> +
> + pci_host_common_link_train_delay(host->max_link_speed);
[Severity: Medium]
When bounced is true and !list_empty(&bus->devices), does calling
rzg3s_pcie_device_replaced(host) issue Type 0 configuration reads to the
downstream endpoint before the link has finished settling?
When a real DL_UpDown event occurs (bounced == true), the result of
rzg3s_pcie_device_replaced(host) is not needed because the devices are
removed unconditionally, and rzg3s_pcie_set_max_link_speed() (which polls
for LTSSM L0 and negotiates link speed) and
pci_host_common_link_train_delay() have not been called yet. Should
rzg3s_pcie_device_replaced(host) only be called when !bounced?
[ ... ]
> @@ -1901,6 +2108,44 @@ static void rzv2h_pcie_release_lanes(void *data)
> rzv2h_num_total_lanes -= host->num_lanes;
> }
>
> +static int rzg3s_pcie_pm_notifier(struct notifier_block *nb,
> + unsigned long action, void *data)
> +{
> + struct rzg3s_pcie_host *host = container_of(nb, struct rzg3s_pcie_host,
> + pm_nb);
> +
> + switch (action) {
> + case PM_SUSPEND_PREPARE:
> + /* Disable link up/down interrupts. */
> + if (host->started)
> + writel_relaxed(0, host->axi + RZG3S_PCI_PEIE0);
> + disable_irq(host->event_irq);
> + break;
[Severity: High]
Can calling disable_irq(host->event_irq) inside rzg3s_pcie_pm_notifier()
deadlock on pm_chain_head.rwsem if rzg3s_pcie_event_irq_thread() is
concurrently probing or removing a PCI device whose driver registers or
unregisters a PM notifier?
During system suspend:
suspend_prepare()
pm_notifier_call_chain_robust(PM_SUSPEND_PREPARE, PM_POST_SUSPEND)
blocking_notifier_call_chain_robust(&pm_chain_head, ...)
down_read(&pm_chain_head.rwsem)
rzg3s_pcie_pm_notifier()
disable_irq(host->event_irq)
synchronize_irq(host->event_irq)
If rzg3s_pcie_event_irq_thread() is simultaneously running
rzg3s_pcie_link_event(), it calls pci_bus_add_devices() or
rzg3s_pcie_remove_devices() -> pci_stop_and_remove_bus_device(), which
synchronously runs PCI endpoint driver .probe() or .remove() callbacks.
Several PCI drivers (such as ath11k_pci, wil6210, amdgpu, and xe) call
register_pm_notifier() or unregister_pm_notifier() in those callbacks,
which blocks on down_write(&pm_chain_head.rwsem) while the suspend thread
holds down_read(&pm_chain_head.rwsem) waiting for the IRQ thread to finish.
> +
> + case PM_POST_SUSPEND:
> + /* Enable link up/down interrupts and force link re-scan. */
> + if (host->started) {
> + /*
> + * The link re-train in resume triggers the link up IRQ
> + * if there are connected devices. Clear the DL_UpDown
> + * status and trigger link_rescan to avoid
> + * re-enumerating the already connected devices.
> + */
> + writel_relaxed(RZG3S_PCI_PEIS0_DL_UPDOWN,
> + host->axi + RZG3S_PCI_PEIS0);
> + WRITE_ONCE(host->link_rescan, true);
> + writel_relaxed(RZG3S_PCI_PEIE0_DL_UPDOWN,
> + host->axi + RZG3S_PCI_PEIE0);
> + }
> + enable_irq(host->event_irq);
> + if (host->started)
> + irq_wake_thread(host->event_irq, host);
> + break;
> + }
> +
> + return NOTIFY_DONE;
> +}
[Severity: High]
rzg3s_pcie_pm_ops uses NOIRQ_SYSTEM_SLEEP_PM_OPS(rzg3s_pcie_suspend_noirq,
rzg3s_pcie_resume_noirq), which wires up the freeze_noirq, thaw_noirq,
poweroff_noirq, and restore_noirq callbacks for hibernation. However,
rzg3s_pcie_pm_notifier() only handles PM_SUSPEND_PREPARE and
PM_POST_SUSPEND.
During hibernation, won't omitting PM_HIBERNATION_PREPARE /
PM_RESTORE_PREPARE leave host->event_irq enabled during device freeze and
poweroff, and won't omitting PM_POST_HIBERNATION / PM_POST_RESTORE leave
RZG3S_PCI_PEIE0 cleared to 0 (after rzg3s_pcie_resume_noirq() ->
rzg3s_pcie_host_start() -> rzg3s_pcie_irq_init() resets it) and skip waking
the IRQ thread to rescan the bus?
>
> static int rzg3s_pcie_probe(struct platform_device *pdev)
> {
[ ... ]
> @@ -1999,15 +2244,38 @@ static int rzg3s_pcie_probe(struct platform_device *pdev)
> if (ret)
> goto power_resets_assert;
>
> + host->started = true;
> + host->pm_nb.notifier_call = rzg3s_pcie_pm_notifier;
> + ret = register_pm_notifier(&host->pm_nb);
> + if (ret)
> + goto host_probe_teardown;
> +
> bridge->sysdata = host;
> bridge->ops = &rzg3s_pcie_root_ops;
> bridge->child_ops = &rzg3s_pcie_child_ops;
> ret = pci_host_probe(bridge);
> if (ret)
> - goto host_probe_teardown;
> + goto pm_notifier_unregister;
> +
> + pci_lock_rescan_remove();
> + rzg3s_pcie_cache_connected_dev_id(host);
> + pci_unlock_rescan_remove();
> +
> + /*
> + * The link re-train triggers the link up IRQ if there are connected
> + * devices. Clear DL_UpDown status and trigger link_rescan to avoid
> + * re-enumerating the devices detected while pci_host_probe() and loose
> + * any potential event.
> + */
> + writel_relaxed(RZG3S_PCI_PEIS0_DL_UPDOWN, host->axi + RZG3S_PCI_PEIS0);
> + WRITE_ONCE(host->link_rescan, true);
> + writel_relaxed(RZG3S_PCI_PEIE0_DL_UPDOWN, host->axi + RZG3S_PCI_PEIE0);
> + irq_wake_thread(host->event_irq, host);
>
> return 0;
[Severity: High]
Because rzg3s_pcie_driver sets PROBE_PREFER_ASYNCHRONOUS, and system
suspend runs pm_notifier_call_chain_robust(PM_SUSPEND_PREPARE,
PM_POST_SUSPEND) before dpm_prepare() calls wait_for_device_probe(), can
rzg3s_pcie_probe() race with rzg3s_pcie_pm_notifier()?
1. host->started is set to true and register_pm_notifier(&host->pm_nb) is
called before pci_host_probe(bridge) initializes bridge->bus. If
suspend_prepare() aborts before pci_register_host_bridge() sets
bridge->bus, PM_POST_SUSPEND wakes rzg3s_pcie_event_irq_thread() ->
rzg3s_pcie_link_event(), which dereferences bridge->bus while it is still
NULL.
2. If PM_SUSPEND_PREPARE runs while rzg3s_pcie_probe() is inside
pci_host_probe(), rzg3s_pcie_pm_notifier() clears RZG3S_PCI_PEIE0 and calls
disable_irq(host->event_irq), but rzg3s_pcie_probe() then finishes before
wait_for_device_probe() returns and unconditionally enables
RZG3S_PCI_PEIE0_DL_UPDOWN and calls irq_wake_thread(host->event_irq, host),
waking the IRQ thread during suspend.
3. If PM_SUSPEND_PREPARE runs before register_pm_notifier(&host->pm_nb) is
called in rzg3s_pcie_probe(), PM_POST_SUSPEND will later call
enable_irq(host->event_irq) without a matching disable_irq().
[Severity: Medium]
In addition, clearing RZG3S_PCI_PEIS0_DL_UPDOWN here at the end of
rzg3s_pcie_probe() (after pci_host_probe(bridge)) and in
rzg3s_pcie_pm_notifier() under PM_POST_SUSPEND (after all device resume
callbacks or after an aborted suspend), rather than immediately after link
training in rzg3s_pcie_host_setup(), seems able to lose link bounce events:
If the same endpoint experiences a link down/up bounce during
pci_host_probe(), between rzg3s_pcie_resume_noirq() and PM_POST_SUSPEND, or
during an aborted suspend, clearing RZG3S_PCI_PEIS0_DL_UPDOWN discards the
hardware event so bounced is false. Because the same device is still
attached, rzg3s_pcie_device_replaced() returns false and
rzg3s_pcie_link_event() exits at:
if (!bounced && !replaced)
goto unlock;
leaving the endpoint with unconfigured BARs, PCI_COMMAND, and MSI state (or
at Gen1 speed if link-up raced with rzg3s_pcie_wait_for_link_up() timing
out in rzg3s_pcie_host_setup()). Should RZG3S_PCI_PEIS0_DL_UPDOWN be
cleared in rzg3s_pcie_host_setup() right after link training instead?
[ ... ]
> @@ -2041,8 +2309,25 @@ static int rzg3s_pcie_suspend_noirq(struct device *dev)
> static int rzg3s_pcie_resume_noirq(struct device *dev)
> {
> struct rzg3s_pcie_host *host = dev_get_drvdata(dev);
> + int ret;
>
> - return rzg3s_pcie_host_start(host);
> + ret = rzg3s_pcie_host_start(host);
> + if (ret)
> + return ret;
> +
> + if (rzg3s_pcie_device_replaced(host)) {
> + struct pci_host_bridge *bridge =
> + pci_host_bridge_from_priv(host);
> + struct pci_dev *port __free(pci_dev_put) =
> + pci_get_slot(bridge->bus, PCI_DEVFN(0, 0));
> +
> + if (port && port->subordinate) {
> + pci_walk_bus(port->subordinate,
> + pci_dev_set_disconnected, NULL);
> + }
> + }
> +
> + return 0;
> }
[Severity: Medium]
If rzg3s_pcie_host_start(host) fails in rzg3s_pcie_resume_noirq(), its
error path asserts host->power_resets and clears
RZG3S_SYSC_FUNC_ID_RST_RSM_B, leaving host->started as false.
Although rzg3s_pcie_pm_notifier() checks host->started to avoid touching
controller registers after a failed resume, the PM core still runs
pci_pm_resume_noirq() and pci_pm_resume() on the child PCI devices. Since
rzg3s_pcie_resume_noirq() returns early before marking the devices
disconnected, and neither rzg3s_pcie_root_ops nor rzg3s_pcie_child_ops
checks host->started, won't config-space accesses during child device
resume (and later runtime accesses to the remaining PCI devices) still read
and write host->pcie and host->axi while the controller is held in reset?
Should the devices also be marked disconnected (or config accesses guarded
by host->started) when rzg3s_pcie_host_start() fails?
[ ... ]
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261019065025.2146539-1-claudiu.beznea@kernel.org?part=13
next prev parent reply other threads:[~2026-10-19 7:30 UTC|newest]
Thread overview: 31+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-19 6:50 [PATCH v7 00/14] PCI: rzg3s-host: Add PCIe hotplug support Claudiu Beznea
2026-10-19 6:50 ` [PATCH v7 01/14] PCI: rzg3s-host: Follow hardware manual clock/reset initialization order Claudiu Beznea
2026-10-19 6:57 ` sashiko-bot
2026-10-19 6:50 ` [PATCH v7 02/14] PCI: rzg3s-host: Fix runtime PM handling in the NOIRQ suspend/resume phase Claudiu Beznea
2026-10-19 6:57 ` sashiko-bot
2026-10-19 6:50 ` [PATCH v7 03/14] PCI: rzg3s-host: Don't mix goto-based unwinding and cleanup helpers Claudiu Beznea
2026-10-19 6:54 ` sashiko-bot
2026-10-19 6:50 ` [PATCH v7 04/14] PCI: rzg3s-host: Create the INTx IRQ domain before setting the chained handlers Claudiu Beznea
2026-10-19 6:56 ` sashiko-bot
2026-10-19 6:50 ` [PATCH v7 05/14] PCI: rzg3s-host: Initialize the IRQ domains after the IRQs are masked Claudiu Beznea
2026-10-19 7:03 ` sashiko-bot
2026-10-19 6:50 ` [PATCH v7 06/14] PCI: rzg3s-host: Drop read-modify-write approach for the R/W1C registers Claudiu Beznea
2026-10-19 6:54 ` sashiko-bot
2026-10-19 6:50 ` [PATCH v7 07/14] PCI: rzg3s-host: Use cached MSI address in .irq_compose_msi_msg() Claudiu Beznea
2026-10-19 6:54 ` sashiko-bot
2026-10-20 8:45 ` Lad, Prabhakar
2026-10-20 9:10 ` Claudiu Beznea
2026-10-19 6:50 ` [PATCH v7 08/14] PCI: rzg3s-host: Drop nop instructions Claudiu Beznea
2026-10-19 6:55 ` sashiko-bot
2026-10-19 6:50 ` [PATCH v7 09/14] PCI: rzg3s-host: Move host configuration code together Claudiu Beznea
2026-10-19 6:55 ` sashiko-bot
2026-10-19 6:50 ` [PATCH v7 10/14] PCI: rzg3s-host: Move suspend/resume code into dedicated functions Claudiu Beznea
2026-10-19 6:57 ` sashiko-bot
2026-10-19 6:50 ` [PATCH v7 11/14] PCI: rzg3s-host: Move IRQ domain setup code Claudiu Beznea
2026-10-19 6:55 ` sashiko-bot
2026-10-19 6:50 ` [PATCH v7 12/14] PCI: rzg3s-host: Add helper to wait for link up Claudiu Beznea
2026-10-19 6:55 ` sashiko-bot
2026-10-19 6:50 ` [PATCH v7 13/14] PCI: rzg3s-host: Re-enumerate the bus on PCIe link-state changes Claudiu Beznea
2026-10-19 7:30 ` sashiko-bot [this message]
2026-10-19 6:50 ` [PATCH v7 14/14] PCI: rzg3s-host: Add bridge::reset_root_port() Claudiu Beznea
2026-10-19 7:25 ` sashiko-bot
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=sashiko-outbox-176746@kernel.org \
--to=sashiko-bot@kernel.org \
--cc=claudiu.beznea@kernel.org \
--cc=linux-pci@vger.kernel.org \
--cc=sashiko-reviews@lists.linux.dev \
--cc=wsa+renesas@sang-engineering.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