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 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.