From: netdev-bot+sashiko@kernel.org
To: javen_xu@realsil.com.cn
Cc: hkallweit1@gmail.com, nic_swsd@realtek.com,
andrew+netdev@lunn.ch, davem@davemloft.net, edumazet@google.com,
kuba@kernel.org, pabeni@redhat.com,
maxime.chevallier@bootlin.com, horms@kernel.org,
netdev@vger.kernel.org, linux-kernel@vger.kernel.org,
daniel@makrotopia.org, linux@armlinux.org.uk,
enelsonmoore@gmail.com, daniel@thingy.jp
Subject: Re: [PATCH net-next v9 7/7] r8169: fix RTL8116af can not enter s0idle and c10
Date: Fri, 04 Sep 2026 22:25:23 +0000 [thread overview]
Message-ID: <178856072395.219967.5123094668587461591@kernel.org> (raw)
In-Reply-To: <20260831053745.1197-8-javen_xu@realsil.com.cn>
Thank you for your contribution! Sashiko AI review found 4 potential
issue(s) to consider:
Critical: 0 · High: 1 · Medium: 2 · Low: 1
- [High] The hidden-function power/ASPM programming is never re-applied
on the runtime-PM resume path, so the bug class the patch claims to…
- [Medium] rtl_lowpower_hidden_functions() unconditionally ORs
PCI_EXP_LNKCTL_ASPMC (both L0s and L1) and PCI_EXP_LNKCTL_CLKREQ_EN…
- [Medium] rtl_lowpower_hidden_functions() writes PCI_EXP_LNKCTL (force
ASPM L0s/L1 + CLKREQ_EN) and PCI_PM_CTRL (D3hot + RW1C PME_STATUS) to…
- [Low] Commit message names the wrong config-space accessors.
--
r8169: fix RTL8116af can not enter s0idle and c10
This adds rtl_lowpower_hidden_functions(), which walks functions 2 to 7 of
the NIC's slot and programs their PCIe Link Control and PM Control
registers to enable ASPM and put them into D3hot. It is called from
rtl_init_one() and rtl8169_resume() when rtl_is_8116af() is true.
A few questions below.
> Put functions 2 to 7 into D3hot and enable ASPM on their PCIe link control
> register. Since these functions are hidden, access their configuration
> space through pci_bus_read_config_dword() / pci_bus_write_config_dword()
> using the same slot and the target function numbers.
[Severity: Low]
This isn't a bug, but the changelog names accessors that the patch never
calls. All four config accesses in rtl_lowpower_hidden_functions() are
16-bit:
pci_bus_read_config_word(bus, devfn, pos + PCI_EXP_LNKCTL, &val);
pci_bus_write_config_word(bus, devfn, pos + PCI_EXP_LNKCTL, val);
pci_bus_read_config_word(bus, devfn, pos + PCI_PM_CTRL, &val);
pci_bus_write_config_word(bus, devfn, pos + PCI_PM_CTRL, val);
Could the description say pci_bus_read_config_word() /
pci_bus_write_config_word() instead? It may also be worth mentioning that
whether a function is touched at all is decided by
pci_bus_find_capability(), i.e. by walking the capability list of an
un-enumerated function.
> diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c
> index 5dbbc3a48c09a..5415ff62a2869 100644
> --- a/drivers/net/ethernet/realtek/r8169_main.c
> +++ b/drivers/net/ethernet/realtek/r8169_main.c
[ ... ]
> @@ -3773,6 +3774,41 @@ static void rtl_hw_start_8168ep_3(struct rtl8169_private *tp)
> r8168_mac_ocp_modify(tp, 0xe860, 0x0000, 0x0080);
> }
>
> +static void rtl_lowpower_hidden_functions(struct pci_dev *pdev)
> +{
> + unsigned int slot = PCI_SLOT(pdev->devfn);
> + struct pci_bus *bus = pdev->bus;
> + int func, pos;
> + u16 val;
> +
> + for (func = 2; func < 8; func++) {
> + unsigned int devfn = PCI_DEVFN(slot, func);
> +
> + pos = pci_bus_find_capability(bus, devfn, PCI_CAP_ID_EXP);
> + if (pos) {
> + pci_bus_read_config_word(bus, devfn, pos + PCI_EXP_LNKCTL, &val);
> +
> + if (PCI_POSSIBLE_ERROR(val))
> + continue;
> +
> + val |= (PCI_EXP_LNKCTL_ASPMC | PCI_EXP_LNKCTL_CLKREQ_EN);
> + pci_bus_write_config_word(bus, devfn, pos + PCI_EXP_LNKCTL, val);
> + }
[Severity: Medium]
Should this check PCI_EXP_LNKCAP before setting these bits? Both ASPM
states plus Clock Power Management are enabled unconditionally, without
reading LNKCAP to see which of L0s/L1 and PCI_EXP_LNKCAP_CLKPM are
advertised. The ASPM core encodes that rule explicitly:
drivers/pci/pcie/aspm.c:pcie_set_clkpm() {
if (!link->clkpm_capable || link->clkpm_disable)
enable = 0;
}
There is also a policy conflict on the same physical link. rtl_init_one()
does:
if (rtl_aspm_is_safe(tp)) {
dev_info(&pdev->dev, "System vendor flags ASPM as safe\n");
rc = 0;
} else {
rc = pci_disable_link_state(pdev, PCIE_LINK_STATE_L1);
}
tp->aspm_manageable = !rc;
and rtl8169_tx_timeout() can later call
pci_disable_link_state(tp->pci_dev, PCIE_LINK_STATE_L1 |
PCIE_LINK_STATE_L0S). Neither of those can reach functions 2 to 7, since
pcie_config_aspm_link() and pcie_set_clkpm_nocheck() only walk
linkbus->devices, which contains enumerated pci_devs only.
So can functions 2 to 7 end up with L0s, L1 and CLKREQ# enabled on a link
where the administrator or a blacklist (pcie_aspm=off, sysfs link_state) or
the driver's own workaround asked for ASPM to be off? aspm.c also notes
that spec sec 7.5.3.7 recommends the same ASPM Control value for all
functions of a multi-function device, which this diverges from.
> +
> + pos = pci_bus_find_capability(bus, devfn, PCI_CAP_ID_PM);
> + if (pos) {
> + pci_bus_read_config_word(bus, devfn, pos + PCI_PM_CTRL, &val);
> +
> + if (PCI_POSSIBLE_ERROR(val))
> + continue;
> +
> + val &= ~PCI_PM_CTRL_STATE_MASK;
> + val |= (RTL_PM_CTRL_D3HOT | PCI_PM_CTRL_PME_STATUS);
> + pci_bus_write_config_word(bus, devfn, pos + PCI_PM_CTRL, val);
> + }
> + }
> +}
[Severity: Medium]
What guarantees the devfn being written here is really one of the hidden
functions? The loop derives devfn purely from PCI_SLOT(pdev->devfn), and
never reads PCI_VENDOR_ID, never looks for an existing struct pci_dev (for
example via pci_get_slot()), and never excludes PCI_FUNC(pdev->devfn).
The two guards present do not separate "hidden" from "present and owned by
another driver". A non-zero pos only says the function answers config
reads, and PCI_POSSIBLE_ERROR(val) looks unreachable, because a
non-responding function already fails the capability walk with hdr_type
0xff:
drivers/pci/pci.c:__pci_bus_find_cap_start() {
switch (hdr_type) {
case PCI_HEADER_TYPE_NORMAL:
case PCI_HEADER_TYPE_BRIDGE:
return PCI_CAPABILITY_LIST;
...
return 0;
}
If some devfn in 2..7 of that slot is an enumerated device bound to another
driver (composed multi-function slots in a guest, for instance), does this
put it into D3hot while the PCI core still believes it is in PCI_D0, so
pci_set_power_state() will not repair the state? And does the
unconditional PCI_PM_CTRL_PME_STATUS write discard a pending wake event of
a function nobody is tracking?
Related question: if the NIC itself were ever at function 2..7, the call
from rtl8169_resume() would put it into D3hot right before
rtl8169_runtime_resume() touches its registers. Is that ruled out
somewhere? The rtl_is_8116af() gate only checks the MAC version and the
MAC-OCP package/hardware IDs, not which PCI function the NIC occupies.
[ ... ]
> @@ -5326,6 +5362,9 @@ static int rtl8169_resume(struct device *device)
> /* Some chip versions may truncate packets without this initialization */
> rtl_init_rxcfg(tp);
>
> + if (rtl_is_8116af(tp))
> + rtl_lowpower_hidden_functions(tp->pci_dev);
> +
> return rtl8169_runtime_resume(device);
> }
[Severity: High]
Does the runtime-PM resume path need this too? The only two call sites are
rtl_init_one() and rtl8169_resume(), but rtl8169_runtime_resume() is
registered as a peer callback:
static const struct dev_pm_ops rtl8169_pm_ops = {
SYSTEM_SLEEP_PM_OPS(rtl8169_suspend, rtl8169_resume)
RUNTIME_PM_OPS(rtl8169_runtime_suspend, rtl8169_runtime_resume,
rtl8169_runtime_idle)
};
and it does not re-apply the hidden-function programming:
static int rtl8169_runtime_resume(struct device *dev)
{
struct rtl8169_private *tp = dev_get_drvdata(dev);
rtl_rar_set(tp, tp->dev->dev_addr);
__rtl8169_set_wol(tp, tp->saved_wolopts);
if (tp->TxDescArray)
rtl8169_up(tp);
netif_device_attach(tp->dev);
return 0;
}
Runtime suspend is a normal event for this driver: rtl_init_one() arms it
with
if (pci_dev_run_wake(pdev))
pm_runtime_put_sync(&pdev->dev);
and rtl_mac_link_down() -> pm_runtime_idle() plus rtl8169_runtime_idle()
schedule it on link down. If the PCI core places the endpoint in D3cold
(parent bridge powered down), the hidden functions' PMCSR and Link Control
registers return to power-on defaults, D0 with ASPM disabled, and the core
only restores saved config space for enumerated pci_devs. After that, is
there any path that re-programs functions 2..7?
The same gap seems to apply when the device stays runtime-suspended across
a system sleep (PCI direct_complete), where the core skips
rtl8169_resume() entirely and the device comes back through the runtime
callback.
Since rtl8169_resume() already tail-calls rtl8169_runtime_resume(), would
moving the rtl_is_8116af() call into rtl8169_runtime_resume() cover both
wake paths from a single site?
[ ... ]
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260831053745.1197-1-javen_xu%40realsil.com.cn
next prev parent reply other threads:[~2026-09-04 22:25 UTC|newest]
Thread overview: 22+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-31 5:37 [PATCH net-next v9 0/7] r8169: add support for phylink javen
2026-08-31 5:37 ` [PATCH net-next v9 1/7] r8169: add speed in private struct javen
2026-09-04 22:25 ` netdev-bot+sashiko
2026-08-31 5:37 ` [PATCH net-next v9 2/7] net: phy: phylink: add helper to modify pause javen
2026-09-04 22:25 ` netdev-bot+sashiko
2026-08-31 5:37 ` [PATCH net-next v9 3/7] r8169: add support for phylink javen
2026-09-02 14:31 ` Andrew Lunn
2026-09-04 22:25 ` netdev-bot+sashiko
2026-08-31 5:37 ` [PATCH net-next v9 4/7] r8169: add support for RTL8116af javen
2026-09-02 14:38 ` Andrew Lunn
2026-09-04 22:25 ` netdev-bot+sashiko
2026-08-31 5:37 ` [PATCH net-next v9 5/7] r8169: add support for RTL8127atf javen
2026-09-02 14:41 ` Andrew Lunn
2026-09-04 22:25 ` netdev-bot+sashiko
2026-08-31 5:37 ` [PATCH net-next v9 6/7] r8169: add ltr support for RTL8117 series javen
2026-09-02 14:42 ` Andrew Lunn
2026-09-04 22:25 ` netdev-bot+sashiko
2026-08-31 5:37 ` [PATCH net-next v9 7/7] r8169: fix RTL8116af can not enter s0idle and c10 javen
2026-09-02 14:42 ` Andrew Lunn
2026-09-04 22:25 ` netdev-bot+sashiko [this message]
2026-09-02 14:28 ` [PATCH net-next v9 0/7] r8169: add support for phylink Andrew Lunn
2026-09-04 21:50 ` patchwork-bot+netdevbpf
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=178856072395.219967.5123094668587461591@kernel.org \
--to=netdev-bot+sashiko@kernel.org \
--cc=andrew+netdev@lunn.ch \
--cc=daniel@makrotopia.org \
--cc=daniel@thingy.jp \
--cc=davem@davemloft.net \
--cc=edumazet@google.com \
--cc=enelsonmoore@gmail.com \
--cc=hkallweit1@gmail.com \
--cc=horms@kernel.org \
--cc=javen_xu@realsil.com.cn \
--cc=kuba@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux@armlinux.org.uk \
--cc=maxime.chevallier@bootlin.com \
--cc=netdev@vger.kernel.org \
--cc=nic_swsd@realtek.com \
--cc=pabeni@redhat.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
all inboxes | Powered by JetHome®