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