All of lore.kernel.org
 help / color / mirror / Atom feed
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 5/7] r8169: add support and enable rss
Date: Tue, 15 Sep 2026 18:24:14 -0700	[thread overview]
Message-ID: <20260916012414.1649482-1-kuba@kernel.org> (raw)
In-Reply-To: <20260910063110.1350-6-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 and enable rss

This adds RSS support for RTL8127: a new RSS descriptor layout selected
through tp->init_rx_desc_type, an RSS key and indirection table kept in
struct rtl8169_rss_data and programmed from rtl_set_rss_config(), and a
change of the RTL_GIGA_MAC_VER_80 interrupt request to a multi-vector
allocation so more than one RX ring can be used.

The commit message says:

  This patch adds support and enable rss for RTL8127.

[Severity: Low]
Could the message describe the behaviour changes that come with it?  A few
that are not mentioned:

rtl_alloc_irq() changes the RTL_GIGA_MAC_VER_80 request from
pci_alloc_irq_vectors(pdev, 1, 1, flags) to an all-or-nothing 30..32 vector
request with a fallback to exactly one vector, so a machine that can only
supply 29 vectors silently ends up with a single vector and no RSS.

struct RxDesc becomes a union with a second, differently ordered layout that
every RX accessor now switches on.

rtl_init_one() sets dev->features |= NETIF_F_RXHASH, so RX hashing is on by
default.

rtl8169_set_rss_hash_opt() enables UDP 4-tuple hashing unconditionally via
RSS_CTRL_UDP_IPV4_SUPP | RSS_CTRL_UDP_IPV6_SUPP.

rtl_rx() changes the NETIF_F_RXALL accept/drop decision for the RSS
descriptor format.

Also, "This patch adds support and enable rss" reads better in imperative
mood, e.g. "Add support for RSS and enable it on RTL8127".

> diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c
> index 6e7a73f6bbb07..ad4ed3ffbec18 100644
> --- a/drivers/net/ethernet/realtek/r8169_main.c
> +++ b/drivers/net/ethernet/realtek/r8169_main.c

[ ... ]

> @@ -512,6 +525,9 @@ enum rtl_register_content {
>  	RxRUNT	= (1 << 20),
>  	RxCRC	= (1 << 19),
>  
> +	RXRUNT_RSS	= (1 << 21),
> +	RXCRC_RSS	= (1 << 20),
> +
>  	/* ChipCmdBits */
>  	StopReq		= 0x80,
>  	CmdReset	= 0x10,

[ ... ]

> @@ -617,6 +633,22 @@ enum rtl_register_content {
>  #define	ISRIMR_LINKCHG	BIT(29)
>  #define	ISRIMR_TOK_Q0	BIT(8)
>  #define	ISRIMR_ROK_Q0	BIT(0)
> +#define RTL_DESC_TYPE_CTRL		0xd8
> +#define RSS_KEY_REG			0x4600
> +#define RSS_INDIRECTION_TBL_REG		0x4700
> +#define RSS_CTRL_TCP_IPV4_SUPP		BIT(0)
> +#define RTL_DESC_TYPE_RSS		BIT(1)
> +#define RSS_CTRL_IPV4_SUPP		BIT(1)
> +#define RSS_CTRL_TCP_IPV6_SUPP		BIT(2)
> +#define RSS_CTRL_IPV6_SUPP		BIT(3)
> +#define RSS_CTRL_IPV6_EXT_SUPP		BIT(4)
> +#define RSS_CTRL_TCP_IPV6_EXT_SUPP	BIT(5)
> +#define RSS_CTRL_UDP_IPV4_SUPP		BIT(11)
> +#define RSS_CTRL_UDP_IPV6_SUPP		BIT(12)
> +#define	RX_RES_RSS			BIT(22)
> +#define	RX_RUNT_RSS			BIT(21)
> +#define	RX_CRC_RSS			BIT(20)
> +#define RTL_RX_Q_NUM_MASK		GENMASK(4, 2)
>  };

[Severity: Low]
This isn't a bug, but the same two descriptor error bits now have two names.
RXRUNT_RSS/RXCRC_RSS are added as enum values above and RX_RUNT_RSS/
RX_CRC_RSS are added as macros here, with identical values:

	RXRUNT_RSS	= (1 << 21),	vs	#define RX_RUNT_RSS BIT(21)
	RXCRC_RSS	= (1 << 20),	vs	#define RX_CRC_RSS  BIT(20)

rtl8169_check_rx_desc_error() uses the macro spellings while rtl_rx() uses
the enum spellings, so a later correction to one copy would miss the other.
Could one set be dropped?

[ ... ]

> @@ -2967,6 +3061,58 @@ static void rtl_set_rx_max_size(struct rtl8169_private *tp)
>  	RTL_W16(tp, RxMaxSize, R8169_RX_BUF_SIZE + 1);
>  }
>  
> +static void rtl8169_store_rss_key(struct rtl8169_private *tp)

[ ... ]

> +static void rtl8169_set_rss_hash_opt(struct rtl8169_private *tp)
> +{
> +	u32 rss_ctrl;
> +
> +	rss_ctrl = FIELD_PREP(RSS_CPU_NUM_MASK, ilog2(tp->num_rx_rings));
> +
> +	/* Perform hash on these packet types */
> +	rss_ctrl |= RSS_CTRL_TCP_IPV4_SUPP |
> +		    RSS_CTRL_IPV4_SUPP |
> +		    RSS_CTRL_IPV6_SUPP |
> +		    RSS_CTRL_IPV6_EXT_SUPP |
> +		    RSS_CTRL_TCP_IPV6_SUPP |
> +		    RSS_CTRL_TCP_IPV6_EXT_SUPP |
> +		    RSS_CTRL_UDP_IPV4_SUPP |
> +		    RSS_CTRL_UDP_IPV6_SUPP;
> +
> +	rss_ctrl |= FIELD_PREP(RSS_HASH_MASK,
> +			       ilog2(tp->rss_data->hw_supp_indir_tbl_entries));
> +
> +	RTL_W32(tp, RSS_CTRL_8125, rss_ctrl);
> +}
> +
> +static void rtl_set_rss_config(struct rtl8169_private *tp)
> +{
> +	rtl8169_set_rss_hash_opt(tp);
> +	rtl8169_store_reta(tp);
> +	rtl8169_store_rss_key(tp);
> +}
> +

[Severity: Low]
Is the ethtool RSS interface meant to be added later in the series?  The
driver now generates and programs a key and an indirection table
(rtl8169_init_rss() fills rss_key via netdev_rss_key_fill() and
rss_indir_tbl via ethtool_rxfh_indir_default(), rtl_set_rss_config() writes
both to hardware), but rtl8169_ethtool_ops gains none of
.get_rxfh_key_size, .get_rxfh_indir_size, .get_rxfh, .set_rxfh, .get_rxnfc
or .set_rxnfc, at this commit or at the end of the series.

So ethtool -x, -X and -N return EOPNOTSUPP: the key cannot be read, the
redirection table cannot be changed, and the UDP 4-tuple hashing enabled
above by RSS_CTRL_UDP_IPV4_SUPP | RSS_CTRL_UDP_IPV6_SUPP cannot be turned
off.

[ ... ]

> @@ -5160,8 +5431,14 @@ static int rtl_rx(struct net_device *dev, struct rtl8169_private *tp,
>  
>  			if (!(dev->features & NETIF_F_RXALL))
>  				goto release_descriptor;
> -			else if (status & RxRWT || !(status & (RxRUNT | RxCRC)))
> -				goto release_descriptor;
> +
> +			if (tp->init_rx_desc_type == RX_DESC_TYPE_DEFAULT) {
> +				if (status & RxRWT || !(status & (RxRUNT | RxCRC)))
> +					goto release_descriptor;
> +			} else {
> +				if (!(status & (RXRUNT_RSS | RXCRC_RSS)))
> +					goto release_descriptor;
> +			}
>  		}
>  
>  		pkt_size = status & GENMASK(13, 0);

[Severity: Medium]
This isn't introduced by this patch, but since the accept/drop decision is
being reworked here and a second acceptance branch is added, can pkt_size
underflow for a salvaged error frame?

		pkt_size = status & GENMASK(13, 0);
		if (likely(!(dev->features & NETIF_F_RXFCS)))
			pkt_size -= ETH_FCS_LEN;

With rx-all enabled, a RUNT/CRC descriptor reporting a length of 0..3 makes
the unsigned subtraction wrap to roughly 4 GiB.  The following
napi_alloc_skb() does not fail in that case, because it wraps too:

net/core/skbuff.c:napi_alloc_skb() {
	...
	len += NET_SKB_PAD + NET_IP_ALIGN;
	if (len <= SKB_WITH_OVERHEAD(SKB_SMALL_HEAD_CACHE_SIZE) || ...)
		skb = __alloc_skb(len, gfp_mask, SKB_ALLOC_RX | SKB_ALLOC_NAPI, NUMA_NO_NODE);
	...
}

0xfffffffc + NET_SKB_PAD + NET_IP_ALIGN wraps to a small value, so a small
head is allocated and skb_copy_to_linear_data(skb, rx_buf, pkt_size) then
copies the un-wrapped length into it, followed by skb->tail += pkt_size and
skb->len = pkt_size.  Would a minimum length check before the ETH_FCS_LEN
subtraction be worthwhile here?

[ ... ]

> @@ -5817,6 +6098,32 @@ static void rtl_set_irq_mask(struct rtl8169_private *tp)
>  	}
>  }
>  
> +static int get_max_irq_nvecs(struct rtl8169_private *tp)
> +{
> +	if (tp->mac_version == RTL_GIGA_MAC_VER_80)
> +		return R8127_MAX_NUM_IRQVEC;
> +	return R8169_IRQ_DEFAULT;
> +}
> +
> +static int get_min_irq_nvecs(struct rtl8169_private *tp)
> +{
> +	if (tp->mac_version == RTL_GIGA_MAC_VER_80)
> +		return R8127_MIN_NUM_IRQVEC;
> +	return R8169_IRQ_DEFAULT;
> +}
> +
> +static void rtl8169_set_rx_ring_num(struct rtl8169_private *tp)
> +{
> +	if (tp->irq_nvecs >= get_min_irq_nvecs(tp)) {
> +		unsigned int rss_queue_num = netif_get_num_default_rss_queues();
> +
> +		tp->num_rx_rings = rounddown_pow_of_two(min(rss_queue_num,
> +							    tp->hw_supp_num_rx_queues));
> +		if (tp->num_rx_rings >= 2)
> +			tp->init_rx_desc_type = RX_DESC_TYPE_RSS;
> +	}
> +}
> +

[Severity: Low]
Since tp->num_rx_rings can now be smaller than the number of RX vectors
that were allocated, does rtl8169_netpoll() leave the hardware mask out of
sync with tp->irq_mask?

rtl8169_netpoll() iterates min(tp->irq_nvecs, R8169_MAX_RX_QUEUES + 1)
entries and calls rtl8169_interrupt_msix() for each one.  For a vector with
message_id >= tp->num_rx_rings, rtl8169_poll_msix_rx() does no ring work,
completes immediately and still calls rtl8169_enable_hw_interrupt_msix(),
setting that queue's bit in IMR_SET_VEC_MAP_REG, while rtl_set_irq_mask()
deliberately only sets ISRIMR_ROK_Q0 << i for i < tp->num_rx_rings.

Would bounding the loop by tp->num_rx_rings be more consistent?

>  static int rtl_alloc_irq(struct rtl8169_private *tp)
>  {
>  	struct pci_dev *pdev = tp->pci_dev;
> @@ -5837,7 +6144,11 @@ static int rtl_alloc_irq(struct rtl8169_private *tp)
>  		break;
>  	}
>  
> -	nvecs = pci_alloc_irq_vectors(pdev, 1, 1, flags);
> +	nvecs = pci_alloc_irq_vectors(pdev, get_min_irq_nvecs(tp),
> +				      get_max_irq_nvecs(tp), flags);
> +
> +	if (nvecs < 0)
> +		nvecs = pci_alloc_irq_vectors(pdev, 1, 1, flags);
>  
>  	if (nvecs < 0)
>  		return nvecs;

[Severity: High]
This is the first time tp->irq_nvecs can be greater than 1 for
RTL_GIGA_MAC_VER_80, which activates the MSI-X path in
rtl8169_request_irq().  Can that dereference a NULL tp->phydev on the SFP
variant?

rtl8169_request_irq() registers vector MSIX_ID_VEC_MAP_LINKCHG with
rtl8169_interrupt_other() when tp->irq_nvecs > 1, and rtl_set_irq_mask()
sets ISRIMR_LINKCHG so the vector is armed.  The handler does:

	rtl8169_clear_hw_isr(tp, MSIX_ID_VEC_MAP_LINKCHG);
	phy_mac_interrupt(tp->phydev);

while the single-vector handler treats a NULL phydev as an expected
configuration:

rtl8169_interrupt() {
	...
	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));
	}
	...
}

On an RTL8127 where rtl_get_sfp_mode() returns RTL_SFP_8127_ATF,
rtl_init_one() skips r8169_mdio_register(), which is the only place
tp->phydev is assigned, so tp->phydev stays NULL and the first link change
would dereference NULL in hard IRQ context.

Related: even with a valid phydev, rtl8169_interrupt_other() never performs
the phylink_mac_change() notification, so would link state ever be reported
on SFP boards using the MSI-X path?

[ ... ]

> @@ -6531,6 +6849,11 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
>  	if (rc < 0)
>  		return dev_err_probe(&pdev->dev, rc, "Can't allocate interrupt\n");
>  
> +	rtl8169_set_rx_ring_num(tp);
> +
> +	if (rtl_hw_support_rss(tp))
> +		rtl8169_init_rss(tp);
> +

[Severity: Medium]
While looking at rtl_init_one() in this series: do the two allocation
failure paths added further down leak the phylink instance created by
rtl_init_phylink()?

	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 run after rtl_init_phylink() succeeded, and phylink_create() is not
devm managed.  The neighbouring failure paths in the same function do clean
up, e.g.:

	rc = register_netdev(dev);
	if (rc) {
		phylink_destroy(tp->phylink);
		goto err_free_napi;
	}

These two returns are still without phylink_destroy() at the end of the
series.

[ ... ]

  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
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 [this message]
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=20260916012414.1649482-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.