From: Jakub Kicinski <kuba@kernel.org>
To: javen_xu@realsil.com.cn
Cc: Jakub Kicinski <kuba@kernel.org>,
hkallweit1@gmail.com, nic_swsd@realtek.com,
andrew+netdev@lunn.ch, davem@davemloft.net, edumazet@google.com,
pabeni@redhat.com, horms@kernel.org, netdev@vger.kernel.org,
linux-kernel@vger.kernel.org
Subject: Re: [PATCH net-next v13 3/7] r8169: add support for new interrupt mapping
Date: Tue, 15 Sep 2026 18:24:13 -0700 [thread overview]
Message-ID: <20260916012413.1649432-1-kuba@kernel.org> (raw)
In-Reply-To: <20260910063110.1350-4-javen_xu@realsil.com.cn>
This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
---
r8169: add support for new interrupt mapping
This adds the vector-map interrupt registers (IMR_CLEAR_VEC_MAP_REG,
ISR_VEC_MAP_REG, IMR_SET_VEC_MAP_REG) and, when more than one IRQ vector is
present, switches to per-vector MSI-X handlers and separate Rx/Tx NAPI poll
functions. A dedicated handler is registered on vector 29 for link change
events.
A few questions below.
> diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c
> index f80ad229ae5f8..361e833354233 100644
> --- a/drivers/net/ethernet/realtek/r8169_main.c
> +++ b/drivers/net/ethernet/realtek/r8169_main.c
> @@ -477,8 +477,12 @@ enum rtl8125_registers {
> RSS_CTRL_8125 = 0x4500,
> Q_NUM_CTRL_8125 = 0x4800,
> EEE_TXIDLE_TIMER_8125 = 0x6048,
> + IMR_CLEAR_VEC_MAP_REG = 0x0d00,
> + ISR_VEC_MAP_REG = 0x0d04,
> + IMR_SET_VEC_MAP_REG = 0x0d0c,
> };
>
> +#define MSIX_ID_VEC_MAP_LINKCHG 29
> #define LEDSEL_MASK_8125 0x23f
>
> #define RX_VLAN_INNER_8125 BIT(22)
> @@ -609,6 +613,9 @@ enum rtl_register_content {
>
> /* magic enable v2 */
> MagicPacket_v2 = (1 << 16), /* Wake up when receives a Magic Packet */
> +#define ISRIMR_LINKCHG BIT(29)
> +#define ISRIMR_TOK_Q0 BIT(8)
> +#define ISRIMR_ROK_Q0 BIT(0)
> };
[ ... ]
> @@ -5198,13 +5221,66 @@ static irqreturn_t rtl8169_interrupt(int irq, void *dev_instance)
> return IRQ_HANDLED;
> }
>
[ ... ]
> +static irqreturn_t rtl8169_interrupt_msix(int irq, void *dev_instance)
> +{
> + struct napi_struct *napi = dev_instance;
> + struct net_device *dev = napi->dev;
> + struct rtl8169_private *tp;
> + int message_id;
> +
> + tp = netdev_priv(dev);
> + message_id = napi - tp->rtl8169_napi;
> +
> + rtl8169_disable_hw_interrupt_msix(tp, message_id);
> + rtl8169_clear_hw_isr(tp, message_id);
> +
> + napi_schedule(napi);
> +
> + return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t rtl8169_interrupt_other(int irq, void *dev_instance)
> +{
> + struct rtl8169_private *tp = dev_instance;
> +
> + rtl8169_clear_hw_isr(tp, MSIX_ID_VEC_MAP_LINKCHG);
> + phy_mac_interrupt(tp->phydev);
> + return IRQ_HANDLED;
> }
[Severity: Critical]
Can tp->phydev be NULL here?
The existing handler for the same LinkChg event, rtl8169_interrupt(), guards
the pointer and has an SFP fallback:
if (status & LinkChg) {
if (tp->phydev)
phy_mac_interrupt(tp->phydev);
else if (tp->sfp_mode)
phylink_mac_change(tp->phylink,
!!(RTL_R8(tp, PHYstatus) & LinkStatus));
}
tp->phydev is only assigned by r8169_mdio_register(), and rtl_init_one()
calls that only for the non-SFP case:
if (tp->sfp_mode == RTL_SFP_NONE) {
rc = r8169_mdio_register(tp);
...
}
For an RTL8127 in fiber mode rtl_get_sfp_mode() returns RTL_SFP_8127_ATF,
so tp->phydev stays NULL (rtl_open() likewise does
"if (tp->phydev) r8169_phy_connect(tp);").
phy_mac_interrupt() dereferences the pointer straight away:
drivers/net/phy/phy.c:phy_mac_interrupt() {
/* Trigger a state machine change */
phy_trigger_machine(phydev);
}
with phy_queue_state_machine() forming &phydev->state_queue. Since this runs
in hard IRQ context, wouldn't the resulting oops be fatal?
The vector is armed regardless of sfp_mode, because rtl_set_irq_mask() sets
ISRIMR_LINKCHG for every multi-vector configuration, and vector 29 is
registered whenever irq_nvecs reaches MSIX_ID_VEC_MAP_LINKCHG. On RTL8127
get_min_irq_nvecs()/get_max_irq_nvecs() request 30..32 vectors once
"r8169: enable new interrupt mapping" lands, so the trigger is just a fiber
link up/down, e.g. plugging the module or the remote port toggling.
Should this handler mirror the tp->phydev check and the phylink_mac_change()
fallback? As written the SFP link state also never reaches phylink even if
the dereference were guarded.
>
> static int rtl8169_request_irq(struct rtl8169_private *tp)
> @@ -5215,8 +5291,27 @@ static int rtl8169_request_irq(struct rtl8169_private *tp)
>
> for (i = 0; i < tp->irq_nvecs; i++) {
> napi = &tp->rtl8169_napi[i];
> - rc = pci_request_irq(tp->pci_dev, i, rtl8169_interrupt,
> - NULL, napi, "%s-%d", dev->name, i);
> + if (tp->irq_nvecs > 1) {
> + /* RX: 0~7, TX: 8*/
> + if (i <= R8169_MAX_RX_QUEUES)
> + rc = pci_request_irq(tp->pci_dev, i,
> + rtl8169_interrupt_msix,
> + NULL, napi, "%s-%d",
> + dev->name, i);
> + else if (i == MSIX_ID_VEC_MAP_LINKCHG)
> + rc = pci_request_irq(tp->pci_dev, i,
> + rtl8169_interrupt_other,
> + NULL, tp, "%s-%d",
> + dev->name, i);
> + else
> + continue;
> + } else {
> + rc = pci_request_irq(tp->pci_dev, i,
> + rtl8169_interrupt,
> + NULL, napi, "%s-%d",
> + dev->name, i);
> + }
> +
> if (rc)
> goto free_irq;
> }
[ ... ]
> @@ -5648,10 +5752,16 @@ static const struct net_device_ops rtl_netdev_ops = {
>
> static void rtl_set_irq_mask(struct rtl8169_private *tp)
> {
> - tp->irq_mask = RxOK | RxErr | TxOK | TxErr | LinkChg;
> + if (tp->irq_nvecs > 1) {
> + tp->irq_mask = ISRIMR_LINKCHG | ISRIMR_TOK_Q0;
> + for (int i = 0; i < tp->num_rx_rings; i++)
> + tp->irq_mask |= ISRIMR_ROK_Q0 << i;
> + } else {
> + tp->irq_mask = RxOK | RxErr | TxOK | TxErr | LinkChg;
>
> - if (tp->mac_version <= RTL_GIGA_MAC_VER_06)
> - tp->irq_mask |= SYSErr | RxFIFOOver;
> + if (tp->mac_version <= RTL_GIGA_MAC_VER_06)
> + tp->irq_mask |= SYSErr | RxFIFOOver;
> + }
> }
[ ... ]
> @@ -6197,10 +6307,63 @@ static int rtl_init_phylink(struct rtl8169_private *tp)
> return 0;
> }
>
> +static int rtl8169_poll_msix_rx(struct napi_struct *napi, int budget)
> +{
> + struct net_device *dev = napi->dev;
> + struct rtl8169_private *tp;
> + int work_done = 0;
> + int message_id;
> +
> + tp = netdev_priv(dev);
> + message_id = napi - tp->rtl8169_napi;
> +
> + if (message_id < tp->num_rx_rings)
> + work_done += rtl_rx(dev, tp, &tp->rx_ring[message_id],
> + budget, napi);
> +
> + if (work_done < budget && napi_complete_done(napi, work_done))
> + rtl8169_enable_hw_interrupt_msix(tp, message_id);
> +
> + return work_done;
> +}
[ ... ]
> +/* RTL8127 MSI-X vector layout:
> + * Vectors 0 .. (RxQs - 1) : Rx Queues
> + * Vectors RxQs .. (RxQs + TxQs - 1) : Tx Queues
> + * NAPI is only allocated for data path
> + */
[Severity: Low]
This isn't a bug, but does this comment match the code below it?
The comment says the Tx vectors start at RxQs, while r8169_init_napi() keys
the Tx poll function on the fixed constant R8169_MAX_RX_QUEUES, and
rtl8169_request_irq() states the layout differently:
/* RX: 0~7, TX: 8*/
if (i <= R8169_MAX_RX_QUEUES)
The hardware defines added in this patch also point at a fixed boundary,
ISRIMR_ROK_Q0 = BIT(0) and ISRIMR_TOK_Q0 = BIT(8), i.e. Tx is bit 8 no
matter how many Rx rings are active. With tp->num_rx_rings typically 1, 2 or
4, the comment would place Tx on vector 1, 2 or 4, which is where the code
installs rtl8169_poll_msix_rx().
Should the comment also mention the third handler this patch registers, on
vector 29 (MSIX_ID_VEC_MAP_LINKCHG)?
> static void r8169_init_napi(struct rtl8169_private *tp)
> {
> - for (int i = 0; i < tp->irq_nvecs; i++) {
> - netif_napi_add(tp->dev, &tp->rtl8169_napi[i], rtl8169_poll);
> + int napi_num = min(tp->irq_nvecs, R8169_MAX_RX_QUEUES + 1);
> +
> + for (int i = 0; i < napi_num; i++) {
> + int (*poll_fn)(struct napi_struct *, int) = rtl8169_poll;
> +
> + if (tp->irq_nvecs > 1) {
> + if (i < R8169_MAX_RX_QUEUES)
> + poll_fn = rtl8169_poll_msix_rx;
> + else
> + poll_fn = rtl8169_poll_msix_tx;
> + }
> + netif_napi_add(tp->dev, &tp->rtl8169_napi[i], poll_fn);
> netif_napi_set_irq(&tp->rtl8169_napi[i],
> pci_irq_vector(tp->pci_dev, i));
> }
[Severity: Low]
Should this loop bound be derived from tp->num_rx_rings instead of
R8169_MAX_RX_QUEUES?
With MSI-X this creates 9 NAPI instances (8 Rx + 1 Tx) and calls
netif_napi_set_irq() for each, while the number of Rx queues reported to the
core and the number of rings allocated both come from tp->num_rx_rings:
rc = netif_set_real_num_queues(tp->dev, 1, tp->num_rx_rings);
...
tp->rx_ring = kcalloc(tp->num_rx_rings, sizeof(struct rtl8169_rx_ring),
GFP_KERNEL);
tp->num_rx_rings is rounddown_pow_of_two(min(netif_get_num_default_rss_queues(),
hw_supp_num_rx_queues)), so it is typically 1, 2 or 4 on machines with fewer
than 16 CPUs. rtl8169_poll_msix_rx() appears to acknowledge the surplus
instances with its "if (message_id < tp->num_rx_rings)" guard, and still
re-arms those vectors through rtl8169_enable_hw_interrupt_msix().
rtl8169_request_irq() also requests handlers for those vectors and
rtl8169_netpoll() kicks their NAPIs. Is reporting more NAPI instances to the
netdev core than there are Rx queues intentional?
One more question, about code outside this diff.
[Severity: Medium]
Do the two new allocation failure paths in rtl_init_one() leak the phylink
instance created by rtl_init_phylink()? These call sites come from the
earlier patches in this series rather than from this commit:
tp->rx_ring = kcalloc(tp->num_rx_rings, sizeof(struct rtl8169_rx_ring),
GFP_KERNEL);
if (!tp->rx_ring)
return -ENOMEM;
...
tp->rtl8169_napi = kcalloc(tp->irq_nvecs, sizeof(struct napi_struct),
GFP_KERNEL);
if (!tp->rtl8169_napi) {
kfree(tp->rx_ring);
return -ENOMEM;
}
Both return after rtl_init_phylink() has stored the object in tp->phylink,
without calling phylink_destroy(). The neighbouring error paths in the same
function do release it:
if (tp->sfp_mode == RTL_SFP_NONE) {
rc = r8169_mdio_register(tp);
if (rc) {
phylink_destroy(tp->phylink);
return rc;
}
}
...
rc = register_netdev(dev);
if (rc) {
phylink_destroy(tp->phylink);
goto err_free_napi;
}
With 30 to 32 napi_structs requested on RTL8127, is the napi kcalloc()
failure path worth wiring into the same cleanup?
next prev parent reply other threads:[~2026-09-16 1:24 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-10 6:31 [PATCH net-next v13 0/7] r8169: add RSS support for RTL8127 javen
2026-09-10 6:31 ` [PATCH net-next v13 1/7] r8169: add support for multi irqs javen
2026-09-16 1:24 ` Jakub Kicinski
2026-09-10 6:31 ` [PATCH net-next v13 2/7] r8169: refactor RX path to prepare for multi-queue javen
2026-09-16 1:24 ` Jakub Kicinski
2026-09-10 6:31 ` [PATCH net-next v13 3/7] r8169: add support for new interrupt mapping javen
2026-09-16 1:24 ` Jakub Kicinski [this message]
2026-09-10 6:31 ` [PATCH net-next v13 4/7] r8169: enable " javen
2026-09-10 6:31 ` [PATCH net-next v13 5/7] r8169: add support and enable rss javen
2026-09-16 1:24 ` Jakub Kicinski
2026-09-10 6:31 ` [PATCH net-next v13 6/7] r8169: move struct ethtool_ops javen
2026-09-10 6:31 ` [PATCH net-next v13 7/7] r8169: add get_channel support for ethtool javen
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=20260916012413.1649432-1-kuba@kernel.org \
--to=kuba@kernel.org \
--cc=andrew+netdev@lunn.ch \
--cc=davem@davemloft.net \
--cc=edumazet@google.com \
--cc=hkallweit1@gmail.com \
--cc=horms@kernel.org \
--cc=javen_xu@realsil.com.cn \
--cc=linux-kernel@vger.kernel.org \
--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®