Netdev List
 help / color / mirror / Atom feed
* Re: [PATCH net v2] ipv6: Implement limits on extension header parsing
From: Daniel Borkmann @ 2026-04-26 10:38 UTC (permalink / raw)
  To: Justin Iurman, kuba
  Cc: edumazet, dsahern, tom, willemdebruijn.kernel, idosch, pabeni,
	netdev
In-Reply-To: <b50025ba-c59d-4575-a790-fdaf0a48961d@gmail.com>

Hi Justin,

On 4/25/26 12:19 PM, Justin Iurman wrote:
> On 4/25/26 09:55, Daniel Borkmann wrote:
>> ipv6_{skip_exthdr,find_hdr}() and ip6_{tnl_parse_tlv_enc_lim,
>> protocol_deliver_rcu}() iterate over IPv6 extension headers until they
>> find a non-extension-header protocol or run out of packet data. The
>> loops have no iteration counter, relying solely on the packet length
>> to bound them. For a crafted packet with 8-byte extension headers
>> filling a 64KB jumbogram, this means a worst case of up to ~8k
>> iterations with a skb_header_pointer call each. ipv6_skip_exthdr(),
>> for example, is used where it parses the inner quoted packet inside
>> an incoming ICMPv6 error:
>>
>>    - icmpv6_rcv
>>      - checksum validation
>>      - case ICMPV6_DEST_UNREACH
>>        - icmpv6_notify
>>          - pskb_may_pull()       <- pull inner IPv6 header
>>          - ipv6_skip_exthdr()    <- iterates here
>>          - pskb_may_pull()
>>          - ipprot->err_handler() <- sk lookup
>>
>> The per-iteration cost of ipv6_skip_exthdr itself is generally
>> light, but skb_header_pointer becomes more costly on reassembled
>> packets: the first ~1232 bytes of the inner packet are in the skb's
>> linear area, but the remaining ~63KB are in the frag_list where
>> skb_copy_bits is needed to read data.
>>
>> Add a configurable limit via a new sysctl net.ipv6.max_ext_hdrs_number
>> (default 8, minimum 1). All four extension header walking functions
>> are bound by this limit. The sysctl is in line with commit 47d3d7ac656a
>> ("ipv6: Implement limits on Hop-by-Hop and Destination options").
>> As documented, init_net is used to derive max_ext_hdrs_number to
>> be consistent given a net cannot always reliably be retrieved.
>>
>> Note that the check in ip6_protocol_deliver_rcu() happens right
>> before the goto resubmit, such that we don't have to have a test
>> for ipv6_ext_hdr() in the fast-path.
>>
>> There's an ongoing IETF draft-iurman-6man-eh-occurrences to enforce
>> IPv6 extension headers ordering and occurrence. The latter also
>> discusses security implications. As per RFC8200 section 4.1, the
>> occurrence rules for extension headers provide a practical upper
>> bound, thus 8 was used as the default.
>>
>> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
>> ---
>>   v1->v2:
>>     - Set the default to 8 (Justin)
>>     - Update IETF references (Justin)
>>     - Add core path coverage as well (Justin)
>>
>>   Documentation/networking/ip-sysctl.rst |  7 +++++++
>>   include/net/dropreason-core.h          |  6 ++++++
>>   include/net/ipv6.h                     |  2 ++
>>   include/net/netns/ipv6.h               |  1 +
>>   net/ipv6/af_inet6.c                    |  1 +
>>   net/ipv6/exthdrs_core.c                | 11 +++++++++++
>>   net/ipv6/ip6_input.c                   |  6 ++++++
>>   net/ipv6/ip6_tunnel.c                  |  5 +++++
>>   net/ipv6/sysctl_net_ipv6.c             |  8 ++++++++
>>   9 files changed, 47 insertions(+)
>>
> 
> [snip]
> 
>> diff --git a/net/ipv6/ip6_input.c b/net/ipv6/ip6_input.c
>> index 967b07aeb683..a5bbbc16e8d7 100644
>> --- a/net/ipv6/ip6_input.c
>> +++ b/net/ipv6/ip6_input.c
>> @@ -403,8 +403,10 @@ INDIRECT_CALLABLE_DECLARE(int tcp_v6_rcv(struct sk_buff *));
>>   void ip6_protocol_deliver_rcu(struct net *net, struct sk_buff *skb, int nexthdr,
>>                     bool have_final)
>>   {
>> +    int exthdr_max = READ_ONCE(init_net.ipv6.sysctl.max_ext_hdrs_cnt);
>>       const struct inet6_protocol *ipprot;
>>       struct inet6_dev *idev;
>> +    int exthdr_cnt = 0;
>>       unsigned int nhoff;
>>       SKB_DR(reason);
>>       bool raw;
>> @@ -487,6 +489,10 @@ void ip6_protocol_deliver_rcu(struct net *net, struct sk_buff *skb, int nexthdr,
>>                   nexthdr = ret;
>>                   goto resubmit_final;
>>               } else {
>> +                if (unlikely(exthdr_cnt++ >= exthdr_max)) {
>> +                    SKB_DR_SET(reason, IPV6_TOO_MANY_EXTHDRS);
>> +                    goto discard;
>> +                }
>>                   goto resubmit;
>>               }
>>           } else if (ret == 0) {
> 
> The hop-by-hop options header (if present) is not taken into account based on the above. However, the max number of extension headers (implicitly 7***, as per RFC 8200 Section 4.1) must include it. I suggest adding this at the beginning of ip6_protocol_deliver_rcu():
> 
> struct inet6_skb_parm *opt = IP6CB(skb);
> 
> if (opt->flags & IP6SKB_HOPBYHOP)
>      exthdr_cnt++;
> 
> *** FYI, rounding to 8 is fine for this fix

Ok, ack, I'll look into adding that in a v3.

>> diff --git a/net/ipv6/sysctl_net_ipv6.c b/net/ipv6/sysctl_net_ipv6.c
>> index d2cd33e2698d..93f865545a7c 100644
>> --- a/net/ipv6/sysctl_net_ipv6.c
>> +++ b/net/ipv6/sysctl_net_ipv6.c
>> @@ -135,6 +135,14 @@ static struct ctl_table ipv6_table_template[] = {
>>           .extra1        = SYSCTL_ZERO,
>>           .extra2        = &flowlabel_reflect_max,
>>       },
>> +    {
>> +        .procname    = "max_ext_hdrs_number",
>> +        .data        = &init_net.ipv6.sysctl.max_ext_hdrs_cnt,
>> +        .maxlen        = sizeof(int),
>> +        .mode        = 0644,
>> +        .proc_handler    = proc_dointvec_minmax,
>> +        .extra1        = SYSCTL_ONE,
>> +    },
>>       {
>>           .procname    = "max_dst_opts_number",
>>           .data        = &init_net.ipv6.sysctl.max_dst_opts_cnt,
> 
> I've given it a lot of thought. I came to the conclusion that we should use a hard-coded value here as well (just like we did for 076b8cad77aa, with the same logic), not a sysctl. IMO, the main reason is that it provides as is a suitable security fix to be backported, i.e., the max value is the max number of EHs allowed by RFC 8200, Section 4.1. Also, we remain consistent with draft-iurman-6man-eh-occurrences (I think Tom is about to send a revision of the series soon for net-next). What this series does is not only enforcing ordering, but also verifying the specific number of occurrences for each type of Extension Header. Which is totally compatible with what this patch does, i.e., limiting the total number of Extension Headers (regardless of their types) to 8. I guess what I'm trying to say is that it seems like a good plan/compromise and that the aforementioned series would build perfectly on top of this fix.


Initially, I had a hard-coded constant (when it was still 32), but Eric's comment
was to rather go with a sysctl, such that if someone unexpectedly complains, then
there is still a chance for that person to fix it up via sysctl without having to
rebuild the kernel. I'm okay either way, but presumably given we're now being more
"aggressive" into lowering the default to 8 rather than 32 then having such a fall-
back is probably better.

Thanks,
Daniel

^ permalink raw reply

* Re: [PATCH net] neigh: let neigh_xmit take skb ownership
From: Ido Schimmel @ 2026-04-26 10:12 UTC (permalink / raw)
  To: Florian Westphal
  Cc: netdev, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, horms, kuniyu
In-Reply-To: <20260424145843.74055-1-fw@strlen.de>

On Fri, Apr 24, 2026 at 04:58:38PM +0200, Florian Westphal wrote:
> neigh_xmit always releases the skb, except when no neighbour table is
> found. But even the first added user of neigh_xmit (mpls) relied on
> neigh_xmit to release the skb (or queue it for tx).
> 
> sashiko reported:
>  If neigh_xmit() is called with an uninitialized neighbor table (for
>  example, NEIGH_ND_TABLE when IPv6 is disabled), it returns -EAFNOSUPPORT
>  and bypasses its internal out_kfree_skb error path.  Because the return
>  value of neigh_xmit() is ignored here, does this leak the SKB?
> 
> Assume full ownership and remove the last code path that doesn't
> xmit or free skb.
> 
> Fixes: 4fd3d7d9e868 ("neigh: Add helper function neigh_xmit")
> Signed-off-by: Florian Westphal <fw@strlen.de>

Reviewed-by: Ido Schimmel <idosch@nvidia.com>

^ permalink raw reply

* Re: [PATCH net 1/3] netconsole: return count instead of strnlen(buf, count) from store callbacks
From: Simon Horman @ 2026-04-26  8:46 UTC (permalink / raw)
  To: Breno Leitao
  Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Keiichi Kii, Satyam Sharma, Andrew Morton,
	Matthew Wood, asantostc, gustavold, netdev, linux-kernel,
	kernel-team
In-Reply-To: <20260423-netconsole_ai_fixes-v1-1-92b8b7de9a2c@debian.org>

On Thu, Apr 23, 2026 at 02:41:15AM -0700, Breno Leitao wrote:
> Several configfs store callbacks in netconsole end with:
> 
> 	ret = strnlen(buf, count);
> 
> This under-reports the number of bytes consumed when the input
> contains an embedded NUL within count, telling the VFS that fewer
> bytes were written than userspace actually handed in. A conformant
> partial-write loop would then retry the trailing bytes against a
> callback that has already accepted them.
> 
> Every other configfs driver in the tree returns count directly from
> its store callbacks once parsing has succeeded, including
> drivers/nvme/target/configfs.c, drivers/gpio/gpio-sim.c,
> drivers/most/configfs.c, drivers/block/null_blk/main.c,
> drivers/pci/endpoint/pci-ep-cfs.c, and the rest of the configfs
> users. netconsole was the outlier (along with
> drivers/infiniband/core/cma_configfs.c, which has the same latent
> issue).
> 
> Align netconsole with the rest of the configfs ecosystem: return
> count once the parser/validator has accepted the input. The numeric
> and boolean parsers (kstrtobool, kstrtou16, mac_pton,
> netpoll_parse_ip_addr) have already validated the meaningful prefix;
> any trailing bytes are padding and should simply be reported as
> consumed.
> 
> Fixes: 0bcc1816188e ("[NET] netconsole: Support dynamic reconfiguration using configfs")
> Signed-off-by: Breno Leitao <leitao@debian.org>

Reviewed-by: Simon Horman <horms@kernel.org>

FTR: Sashiko has provided an AI generated review of this patch.
Like it's review of patch 3/3 - which I forwarded separately -
it flags that trim_newline() may perform an OOB access
if passed an empty string. But this is not correct because
trim_newline() correctly handles this case.

^ permalink raw reply

* Re: [PATCH net 3/3] netconsole: propagate device name truncation in dev_name_store()
From: Simon Horman @ 2026-04-26  8:39 UTC (permalink / raw)
  To: leitao
  Cc: 'Simon Horman', andrew+netdev, davem, edumazet, kuba,
	pabeni, k-keiichi, satyam, akpm, thepacketgeek, asantostc,
	gustavold, netdev, linux-kernel, kernel-team
In-Reply-To: <20260423-netconsole_ai_fixes-v1-3-92b8b7de9a2c@debian.org>

From: 'Simon Horman' <horms@kernel.org>

This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
Full review at: https://sashiko.dev
---
netconsole: propagate device name truncation in dev_name_store()

This commit rejects interface name writes that exceed the size of the
destination buffer to avoid silently truncating names and binding to
unintended devices.

> diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c
> index 4bef003d9df64..3914fb90f9afd 100644
> --- a/drivers/net/netconsole.c
> +++ b/drivers/net/netconsole.c
> @@ -816,6 +816,9 @@ static ssize_t dev_name_store(struct config_item *item, const char *buf,
>  {
>  	struct netconsole_target *nt = to_target(item);
>  
> +	if (count >= IFNAMSIZ)
> +		return -ENAMETOOLONG;
> +

Does this check incorrectly reject valid maximum-length interface names when
written with a trailing newline?

Standard userspace tools like echo append a trailing newline when writing to
configfs attributes. If userspace writes a 15-character name, the count will
be exactly 16 bytes.

Since IFNAMSIZ is 16, the condition count >= IFNAMSIZ evaluates to true,
causing dev_name_store() to reject the valid input with -ENAMETOOLONG.

Prior to this patch, strscpy() safely truncated the trailing newline by
copying the 15 characters and a NUL terminator.

^ permalink raw reply

* [PATCH net V2 2/2] net/mlx5e: psp: Hook PSP dev reg/unreg to profile enable/disable
From: Tariq Toukan @ 2026-04-26  8:38 UTC (permalink / raw)
  To: Eric Dumazet, Jakub Kicinski, Paolo Abeni, Andrew Lunn,
	David S. Miller
  Cc: Boris Pismenny, Saeed Mahameed, Leon Romanovsky, Tariq Toukan,
	Mark Bloch, Daniel Zahka, Willem de Bruijn, Cosmin Ratiu,
	Raed Salem, Rahul Rameshbabu, Dragos Tatulea, Kees Cook, netdev,
	linux-rdma, linux-kernel, Gal Pressman
In-Reply-To: <20260426083819.208937-1-tariqt@nvidia.com>

From: Cosmin Ratiu <cratiu@nvidia.com>

devlink reload while PSP connections are active does:

mlx5_unload_one_devl_locked() -> mlx5_detach_device()
-> _mlx5e_suspend()
  -> mlx5e_detach_netdev()
    -> profile->cleanup_rx
    -> profile->cleanup_tx
  -> mlx5e_destroy_mdev_resources() -> mlx5_core_dealloc_pd() fails:
...
mlx5_core 0000:08:00.0: mlx5_cmd_out_err:821:(pid 19722):
DEALLOC_PD(0x801) op_mod(0x0) failed, status bad resource state(0x9),
syndrome (0xef0c8a), err(-22)
...

The reason for failure is the existence of TX keys, which are removed by
the PSP dev unregistration happening in:
profile->cleanup() -> mlx5e_psp_unregister() -> mlx5e_psp_cleanup()
  -> psp_dev_unregister()
...but this isn't invoked in the devlink reload flow, only when changing
the NIC profile (e.g. when transitioning to switchdev mode) or on dev
teardown.

Move PSP device registration into mlx5e_nic_enable(), and unregistration
into the corresponding mlx5e_nic_disable(). These functions are called
during netdev attach/detach after RX & TX are set up.
This ensures that the keys will be gone by the time the PD is destroyed.

Fixes: 89ee2d92f66c ("net/mlx5e: Support PSP offload functionality")
Signed-off-by: Cosmin Ratiu <cratiu@nvidia.com>
Reviewed-by: Dragos Tatulea <dtatulea@nvidia.com>
Signed-off-by: Tariq Toukan <tariqt@nvidia.com>
---
 drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
index 5a46870c4b74..8e9443caa933 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
@@ -6023,7 +6023,6 @@ static int mlx5e_nic_init(struct mlx5_core_dev *mdev,
 	if (take_rtnl)
 		rtnl_lock();
 
-	mlx5e_psp_register(priv);
 	/* update XDP supported features */
 	mlx5e_set_xdp_feature(priv);
 
@@ -6036,7 +6035,6 @@ static int mlx5e_nic_init(struct mlx5_core_dev *mdev,
 static void mlx5e_nic_cleanup(struct mlx5e_priv *priv)
 {
 	mlx5e_health_destroy_reporters(priv);
-	mlx5e_psp_unregister(priv);
 	mlx5e_ktls_cleanup(priv);
 	mlx5e_psp_cleanup(priv);
 	mlx5e_fs_cleanup(priv->fs);
@@ -6160,6 +6158,7 @@ static void mlx5e_nic_enable(struct mlx5e_priv *priv)
 
 	mlx5e_fs_init_l2_addr(priv->fs, netdev);
 	mlx5e_ipsec_init(priv);
+	mlx5e_psp_register(priv);
 
 	err = mlx5e_macsec_init(priv);
 	if (err)
@@ -6230,6 +6229,7 @@ static void mlx5e_nic_disable(struct mlx5e_priv *priv)
 	mlx5_lag_remove_netdev(mdev, priv->netdev);
 	mlx5_vxlan_reset_to_default(mdev->vxlan);
 	mlx5e_macsec_cleanup(priv);
+	mlx5e_psp_unregister(priv);
 	mlx5e_ipsec_cleanup(priv);
 }
 
-- 
2.44.0


^ permalink raw reply related

* [PATCH net V2 1/2] net/mlx5e: psp: Fix invalid access on PSP dev registration fail
From: Tariq Toukan @ 2026-04-26  8:38 UTC (permalink / raw)
  To: Eric Dumazet, Jakub Kicinski, Paolo Abeni, Andrew Lunn,
	David S. Miller
  Cc: Boris Pismenny, Saeed Mahameed, Leon Romanovsky, Tariq Toukan,
	Mark Bloch, Daniel Zahka, Willem de Bruijn, Cosmin Ratiu,
	Raed Salem, Rahul Rameshbabu, Dragos Tatulea, Kees Cook, netdev,
	linux-rdma, linux-kernel, Gal Pressman
In-Reply-To: <20260426083819.208937-1-tariqt@nvidia.com>

From: Cosmin Ratiu <cratiu@nvidia.com>

priv->psp->psp is initialized with the PSP device as returned by
psp_dev_create(). This could also return an error, in which case a
future psp_dev_unregister() will result in unpleasantness.

Avoid that by using a local variable and only saving the PSP device when
registration succeeds.
Also apply some light refactoring of the functions managing the PSP
device in order to make them more readable/safe.

In case psp_dev_create() fails, priv->psp and steering structs are left
in place, but they will be inert. The unchecked access of priv->psp in
mlx5e_psp_offload_handle_rx_skb() won't happen because without a PSP
device, there can be no SAs added and therefore no packets will be
successfully decrypted and be handed off to the SW handler.

Fixes: 89ee2d92f66c ("net/mlx5e: Support PSP offload functionality")
Signed-off-by: Cosmin Ratiu <cratiu@nvidia.com>
Reviewed-by: Dragos Tatulea <dtatulea@nvidia.com>
Signed-off-by: Tariq Toukan <tariqt@nvidia.com>
---
 .../mellanox/mlx5/core/en_accel/psp.c         | 36 ++++++++++---------
 1 file changed, 20 insertions(+), 16 deletions(-)

diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp.c
index 6a50b6dec0fa..d9adb993e64d 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp.c
@@ -1070,29 +1070,37 @@ static struct psp_dev_ops mlx5_psp_ops = {
 
 void mlx5e_psp_unregister(struct mlx5e_priv *priv)
 {
-	if (!priv->psp || !priv->psp->psp)
+	struct mlx5e_psp *psp = priv->psp;
+
+	if (!psp || !psp->psp)
 		return;
 
-	psp_dev_unregister(priv->psp->psp);
+	psp_dev_unregister(psp->psp);
+	psp->psp = NULL;
 }
 
 void mlx5e_psp_register(struct mlx5e_priv *priv)
 {
+	struct mlx5e_psp *psp = priv->psp;
+	struct psp_dev *psd;
+
 	/* FW Caps missing */
 	if (!priv->psp)
 		return;
 
-	priv->psp->caps.assoc_drv_spc = sizeof(u32);
-	priv->psp->caps.versions = 1 << PSP_VERSION_HDR0_AES_GCM_128;
+	psp->caps.assoc_drv_spc = sizeof(u32);
+	psp->caps.versions = 1 << PSP_VERSION_HDR0_AES_GCM_128;
 	if (MLX5_CAP_PSP(priv->mdev, psp_crypto_esp_aes_gcm_256_encrypt) &&
 	    MLX5_CAP_PSP(priv->mdev, psp_crypto_esp_aes_gcm_256_decrypt))
-		priv->psp->caps.versions |= 1 << PSP_VERSION_HDR0_AES_GCM_256;
+		psp->caps.versions |= 1 << PSP_VERSION_HDR0_AES_GCM_256;
 
-	priv->psp->psp = psp_dev_create(priv->netdev, &mlx5_psp_ops,
-					&priv->psp->caps, NULL);
-	if (IS_ERR(priv->psp->psp))
+	psd = psp_dev_create(priv->netdev, &mlx5_psp_ops, &psp->caps, NULL);
+	if (IS_ERR(psd)) {
 		mlx5_core_err(priv->mdev, "PSP failed to register due to %pe\n",
-			      priv->psp->psp);
+			      psd);
+		return;
+	}
+	psp->psp = psd;
 }
 
 int mlx5e_psp_init(struct mlx5e_priv *priv)
@@ -1131,22 +1139,18 @@ int mlx5e_psp_init(struct mlx5e_priv *priv)
 	if (!psp)
 		return -ENOMEM;
 
-	priv->psp = psp;
 	fs = mlx5e_accel_psp_fs_init(priv);
 	if (IS_ERR(fs)) {
 		err = PTR_ERR(fs);
-		goto out_err;
+		kfree(psp);
+		return err;
 	}
 
 	psp->fs = fs;
+	priv->psp = psp;
 
 	mlx5_core_dbg(priv->mdev, "PSP attached to netdevice\n");
 	return 0;
-
-out_err:
-	priv->psp = NULL;
-	kfree(psp);
-	return err;
 }
 
 void mlx5e_psp_cleanup(struct mlx5e_priv *priv)
-- 
2.44.0


^ permalink raw reply related

* [PATCH net V2 0/2] net/mlx5e: PSP fixes
From: Tariq Toukan @ 2026-04-26  8:38 UTC (permalink / raw)
  To: Eric Dumazet, Jakub Kicinski, Paolo Abeni, Andrew Lunn,
	David S. Miller
  Cc: Boris Pismenny, Saeed Mahameed, Leon Romanovsky, Tariq Toukan,
	Mark Bloch, Daniel Zahka, Willem de Bruijn, Cosmin Ratiu,
	Raed Salem, Rahul Rameshbabu, Dragos Tatulea, Kees Cook, netdev,
	linux-rdma, linux-kernel, Gal Pressman

Hi,

This patchset provides bug fixes from Cosmin to the mlx5e PSP feature.

Thanks,
Tariq.

V2:
- Link to V1:
  https://lore.kernel.org/all/20260417050201.192070-1-tariqt@nvidia.com/
- Expand commit message in patch #1.

Cosmin Ratiu (2):
  net/mlx5e: psp: Fix invalid access on PSP dev registration fail
  net/mlx5e: psp: Hook PSP dev reg/unreg to profile enable/disable

 .../mellanox/mlx5/core/en_accel/psp.c         | 36 ++++++++++---------
 .../net/ethernet/mellanox/mlx5/core/en_main.c |  4 +--
 2 files changed, 22 insertions(+), 18 deletions(-)


base-commit: d40831b016b4986e70d20d0ad14e6a0c62318986
-- 
2.44.0


^ permalink raw reply

* Re: [PATCH net 2/3] netconsole: avoid clobbering userdatum value on truncated write
From: Simon Horman @ 2026-04-26  8:35 UTC (permalink / raw)
  To: leitao
  Cc: 'Simon Horman', andrew+netdev, davem, edumazet, kuba,
	pabeni, k-keiichi, satyam, akpm, thepacketgeek, asantostc,
	gustavold, netdev, linux-kernel, kernel-team
In-Reply-To: <20260423-netconsole_ai_fixes-v1-2-92b8b7de9a2c@debian.org>

From: 'Simon Horman' <horms@kernel.org>

This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
Full review at: https://sashiko.dev
---
netconsole: avoid clobbering userdatum value on truncated write

This patch tightens the boundary check on userdatum values to prevent
strscpy() from returning -E2BIG and leaving a truncated string in udm->value.

> diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c
> index 5713cb3783ef2..4bef003d9df64 100644
> --- a/drivers/net/netconsole.c
> +++ b/drivers/net/netconsole.c
> @@ -1074,7 +1074,7 @@ static ssize_t userdatum_value_store(struct config_item *item, const char *buf,
>  	struct userdata *ud;
>  	ssize_t ret;
>  
> -	if (count > MAX_EXTRADATA_VALUE_LEN)
> +	if (count >= MAX_EXTRADATA_VALUE_LEN)
>  		return -EMSGSIZE;
>  
>  	mutex_lock(&netconsole_subsys.su_mutex);

This patch addresses the state corruption caused by strscpy() failing, but does
this fully resolve the issue?

The commit message notes that returning an error after modifying udm->value
leaves the internal state out of sync until the next write. Looking slightly
further down in userdatum_value_store():

userdatum_value_store() {
        ...
        ret = strscpy(udm->value, buf, sizeof(udm->value));
        if (ret < 0)
                goto out_unlock;
        trim_newline(udm->value, sizeof(udm->value));

        ud = to_userdata(item->ci_parent);
        nt = userdata_to_target(ud);
        ret = update_userdata(nt);
        if (ret < 0)
                goto out_unlock;
        ...
}

If update_userdata() fails, the function jumps to out_unlock and returns an
error to userspace. Since udm->value has already been updated with the new
string, the next successful write to any entry on this target will unknowingly
pack this new value into the payload, recreating the same deferred-activation
issue.

This isn't a bug introduced by this patch, but does trim_newline() handle
empty strings correctly?

If userspace writes a single NUL byte, strscpy() will successfully copy it and
return 0. Inside trim_newline(), strnlen() would evaluate to 0.

The subsequent check for a newline character would evaluate s[-1]. If the byte
located immediately before the buffer happens to be a newline character, it
would be overwritten with a NUL byte.

^ permalink raw reply

* Re: [PATCH] net: net_failover: Fix the deadlock in slave register
From: Simon Horman @ 2026-04-26  8:10 UTC (permalink / raw)
  To: Faicker Mo
  Cc: sridhar.samudrala@intel.com, andrew+netdev@lunn.ch,
	David S. Miller, Eric Dumazet, Paolo Abeni, sdf@fomichev.me,
	open list:NETWORKING DRIVERS, open list
In-Reply-To: <PH0PR20MB435271FE3258110CE77261BBFA2A2@PH0PR20MB4352.namprd20.prod.outlook.com>

On Thu, Apr 23, 2026 at 07:59:43AM +0000, Faicker Mo wrote:
> There is netdev_lock_ops() before the NETDEV_REGISTER notifier
> in register_netdevice(), so use the non-locking functions
> in net_failover_slave_register().
> 
> Call Trace:
>  <TASK>
>  __schedule+0x30d/0x7a0
>  schedule+0x27/0x90
>  schedule_preempt_disabled+0x15/0x30
>  __mutex_lock.constprop.0+0x538/0x9e0
>  __mutex_lock_slowpath+0x13/0x20
>  mutex_lock+0x3b/0x50
>  dev_set_mtu+0x40/0xe0
>  net_failover_slave_register+0x24/0x280
>  failover_slave_register+0x103/0x1b0
>  failover_event+0x15e/0x210
>  ? dropmon_net_event+0xac/0xe0
>  notifier_call_chain+0x5e/0xe0
>  raw_notifier_call_chain+0x16/0x30
>  call_netdevice_notifiers_info+0x52/0xa0
>  register_netdevice+0x5f4/0x7c0
>  register_netdev+0x1e/0x40
>  _mlx5e_probe+0xe2/0x370 [mlx5_core]
>  mlx5e_probe+0x59/0x70 [mlx5_core]
>  ? __pfx_mlx5e_probe+0x10/0x10 [mlx5_core]
> 
> Fixes: 4c975fd70002 ("net: hold instance lock during NETDEV_REGISTER/UP")
> Signed-off-by: Faicker Mo <faicker.mo@zenlayer.com>

...

Unfortunately this patch does not apply: it seems that somehow all the tabs
were converted into spaces.

> @@ -565,7 +565,7 @@ static int net_failover_slave_register(struct net_device *slave_dev,
>         dev_close(slave_dev);

Claude Code flags that that this needs to be changed to netif_close()

>  err_dev_open:
>         dev_put(slave_dev);
> -       dev_set_mtu(slave_dev, orig_mtu);
> +       netif_set_mtu(slave_dev, orig_mtu);
>  done:
>         return err;
>  }

-- 
pw-bot: changes-requested

^ permalink raw reply

* Re: Help with PCIe THP on ConnectX-7
From: Yishai Hadas @ 2026-04-26  8:09 UTC (permalink / raw)
  To: bmerry; +Cc: netdev, yochai
In-Reply-To: <aepwZVpSFsN-Xegd@brucemerry.org.za>

On 23/04/2026 22:17, Bruce Merry wrote:
> On Thu, Apr 23, 2026 at 05:55:53PM +0300, Yishai Hadas wrote:
>> On 22/04/2026 18:22, Bruce Merry wrote:
>>> Hello
>>>
>>> I'm hoping someone from NVIDIA Networking can help with this; I wasn't
>>> sure of the best way to get in contact with the engineers working on
>>> mlx5 kernel code so thought I'd try here. I'm trying to write some
>>> (userspace) code using ibv_reg_mr_ex to register a memory region using
>>> TLP Processing Hints (THP) to improve performance on an Epyc Turin
> 
> ...
> 
>> It's supported only on CX8 for now.
> 
> Thanks. Does "for now" mean that you expect it to become available on
> CX7 in future, and if so, are you able to say anything about timelines?
> 

AFAIK there is a plan to support TPH on CX7 in the future (around the 
end of year).

It would be better to reach out to NVIDIA support about that.

Yishai

> Thanks
> Bruce


^ permalink raw reply

* Re: [PATCH bpf] bpf, sockmap: zero-initialize pages allocated in bpf_msg_push_data
From: Jiayuan Chen @ 2026-04-26  6:31 UTC (permalink / raw)
  To: Weiming Shi, Jiayuan Chen
  Cc: Martin KaFai Lau, Daniel Borkmann, Alexei Starovoitov,
	Andrii Nakryiko, Eduard Zingerman, Kumar Kartikeya Dwivedi,
	David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	John Fastabend, Stanislav Fomichev, Song Liu, Yonghong Song,
	Jiri Olsa, Simon Horman, bpf, netdev, Xiang Mei, Xinyu Ma
In-Reply-To: <aey55I9gC0VtaN1p@Air.local>


On 4/26/26 1:59 AM, Weiming Shi wrote:
> On 26-04-25 11:17, Jiayuan Chen wrote:
>> On 4/25/26 3:03 AM, Weiming Shi wrote:
>>> bpf_msg_push_data() allocates pages via alloc_pages() without
>>> __GFP_ZERO. In the non-copy path, the entire page of uninitialized
>>> heap content is added directly to the sk_msg scatterlist, which is
>>> then transmitted over TCP to userspace via tcp_bpf_push(). In the
>>> copy path, a gap of len bytes between the front and back memcpy
>>> regions is similarly left uninitialized.
>>>
>>> This leads to a kernel heap information leak: stale page content
>>> including kernel pointers from the direct-map and vmemmap regions
>>> is transmitted to userspace, which can be used to defeat KASLR.
>>>
>>> Add __GFP_ZERO to the alloc_pages() call to ensure the allocated
>>> page is always zeroed before it enters the scatterlist.
>>
>>
>> As the helper's own documentation says:
>>
>>      If a program of type BPF_PROG_TYPE_SK_MSG is run on a msg it may
>>      want to insert metadata or options into the msg. This can later be
>>      read and used by any of the lower layer BPF hooks.
>>
>> The inserted region is meant to be written by the BPF program — that's the
>> entire point of calling push.
>>
>> If the program doesn't fill it,  the push has no purpose to begin with.
>>
>>
>> Isn't the uninitialized content a bug in the BPF program rather than
>> something the kernel helper should paper over?
>>
> Hi, Thanks for the review.
>
> In my testing a process with only CAP_BPF + CAP_NET_ADMIN can receive
> kernel heap and vmalloc pointers through recv() from the uninitialized
> pushed region. The uninitialized memory contains critical kernel metadata
> such as direct-map and vmalloc pointers, which breaks KASLR.
>
> Kernels without CONFIG_INIT_ON_ALLOC_DEFAULT_ON (e.g. RHEL) are
> directly affected the leak is not masked by any mitigation.
>
> Thanks,
> Weiming Shi
>

Reviewed-by: Jiayuan Chen <jiayuan.chen@linux.dev>


Previously I thought this was as same as bpf_xdp_adjust_head / 
bpf_xdp_adjust_meta,
but the function itself allocates a page, I believed the cost of 
GFP_ZERO flag was irrelevant.

Add one more thing: in the future, more and more AI systems will 
complain about
this kind of problem. I believe it is worth it.



^ permalink raw reply

* Re: [PATCH net] net: mana: Optimize irq affinity for low vcpu configs
From: Shradha Gupta @ 2026-04-26  5:07 UTC (permalink / raw)
  To: Dipayaan Roy
  Cc: Dexuan Cui, Wei Liu, Haiyang Zhang, K. Y. Srinivasan, Andrew Lunn,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Konstantin Taranov, Simon Horman, Erni Sri Satya Vennela,
	Shiraz Saleem, Michael Kelley, Long Li, Yury Norov, linux-hyperv,
	linux-kernel, netdev, Paul Rosswurm, Shradha Gupta,
	Saurabh Singh Sengar, stable
In-Reply-To: <aeyMtcAi6B7DfAx+@linuxonhyperv3.guj3yctzbm1etfxqx2vob5hsef.xx.internal.cloudapp.net>

On Sat, Apr 25, 2026 at 02:43:17AM -0700, Dipayaan Roy wrote:
> On Fri, Apr 24, 2026 at 11:15:42PM -0700, Shradha Gupta wrote:
> > On Fri, Apr 24, 2026 at 05:21:23AM -0700, Dipayaan Roy wrote:
> > > On Thu, Apr 23, 2026 at 11:17:00PM -0700, Shradha Gupta wrote:
> > > > In mana driver, the number of IRQs allocated are capped by the
> > > > min(num_cpu + 1, queue count). In cases, where the IRQ count is greater
> > > > than the vcpu count, we want to utilize all the vcpus, irrespective of
> > > > their NUMA/core bindings.
> > > > 
> > > > This is important, especially in the envs where number of vcpus are so
> > > > few that the softIRQ handling overhead on two IRQs on the same vcpu is
> > > > much more than their overheads if they were spread across sibling vcpus
> > > > 
> > > > This behaviour is more evident with dynamic IRQ allocation. Since MANA
> > > > IRQs are assigned at a later stage compared to static allocation, other
> > > > device IRQs may already be affinitized to the vCPUs. As a result, IRQ
> > > > weights become imbalanced, causing multiple MANA IRQs to land on the
> > > > same vCPU.
> > > > 
> > > > In such cases when many parallel TCP connections are tested, the
> > > > throughput drops significantly
> > > > 
> > > > Test envs:
> > > > =======================================================
> > > > Case 1: without this patch
> > > > =======================================================
> > > > 4 vcpu(2 cores), 5 MANA IRQs (1 HWC + 4 Queue)
> > > > 
> > > > 	TYPE		effective vCPU aff
> > > > =======================================================
> > > > IRQ0:	HWC		0
> > > > IRQ1:	mana_q1		0
> > > > IRQ2:	mana_q2		2
> > > > IRQ3:	mana_q3		0
> > > > IRQ4:	mana_q4		3
> > > > 
> > > > %soft on each vCPU(mpstat -P ALL 1) on receiver
> > > > vCPU		0	1	2	3
> > > > =======================================================
> > > > pass 1:		38.85	0.03	24.89	24.65
> > > > pass 2:		39.15	0.03	24.57	25.28
> > > > pass 3:		40.36	0.03	23.20	23.17
> > > > 
> > > > =======================================================
> > > > Case 2: with this patch
> > > > =======================================================
> > > > 4 vcpu(2 cores), 5 MANA IRQs (1 HWC + 4 Queue)
> > > > 
> > > >         TYPE            effective vCPU aff
> > > > =======================================================
> > > > IRQ0:   HWC             0
> > > > IRQ1:   mana_q1         0
> > > > IRQ2:   mana_q2         1
> > > > IRQ3:   mana_q3         2
> > > > IRQ4:   mana_q4         3
> > > > 
> > > > %soft on each vCPU(mpstat -P ALL 1) on receiver
> > > > vCPU            0       1       2       3
> > > > =======================================================
> > > > pass 1:         15.42	15.85	14.99	14.51
> > > > pass 2:         15.53	15.94	15.81	15.93
> > > > pass 3:         16.41	16.35	16.40	16.36
> > > > 
> > > > =======================================================
> > > > Throughput Impact(in Gbps, same env)
> > > > =======================================================
> > > > TCP conn	with patch	w/o patch
> > > > 20480		15.65		7.73
> > > > 10240		15.63		8.93
> > > > 8192		15.64		9.69
> > > > 6144		15.64		13.16
> > > > 4096		15.69		15.75
> > > > 2048		15.69		15.83
> > > > 1024		15.71		15.28
> > > > 
> > > > Fixes: 755391121038 ("net: mana: Allocate MSI-X vectors dynamically")
> > > > Cc: stable@vger.kernel.org
> > > > Signed-off-by: Shradha Gupta <shradhagupta@linux.microsoft.com>
> > > > Signed-off-by: Erni Sri Satya Vennela <ernis@linux.microsoft.com>
> > > > Reviewed-by: Haiyang Zhang <haiyangz@microsoft.com>
> > > > ---
> > > >  .../net/ethernet/microsoft/mana/gdma_main.c   | 35 +++++++++++++++++--
> > > >  1 file changed, 33 insertions(+), 2 deletions(-)
> > > > 
> > > > diff --git a/drivers/net/ethernet/microsoft/mana/gdma_main.c b/drivers/net/ethernet/microsoft/mana/gdma_main.c
> > > > index 098fbda0d128..433c044d53c6 100644
> > > > --- a/drivers/net/ethernet/microsoft/mana/gdma_main.c
> > > > +++ b/drivers/net/ethernet/microsoft/mana/gdma_main.c
> > > > @@ -1672,6 +1672,23 @@ static int irq_setup(unsigned int *irqs, unsigned int len, int node,
> > > >  	return 0;
> > > >  }
> > > >  
> > > > +static int irq_setup_linear(unsigned int *irqs, unsigned int len)
> > > > +{
> > > > +	int cpu;
> > > > +
> > > > +	rcu_read_lock();
> > > We do not need to call rcu_read_lock here, as the caller of this
> > > function has already acquired cpus_read_lock.
> > 
> > Thanks for your comments Dipayaan, I think this is still needed for the
> > irq_set_affinity_and_hint(), to protect the pointer returned by
> > irq_to_desc(). You can also see the same in the original function
> > irq_setup() for the same reason.
> Hi Shradha,
> 
> The original irq_setup() function uses rcu_read_lock() because it relies
> on for_each_numa_hop_mask(), which explicitly mandates that RCU be held.
> You have not used it in irq_setup_linear(), hence the requirement does not apply
> here.
> https://elixir.bootlin.com/linux/v7.0.1/source/include/linux/topology.h#L314
> /**
>  * for_each_numa_hop_mask - iterate over cpumasks of increasing NUMA
>  * distance
>  *                          from a given node.
>  * @mask: the iteration variable.
>  * @node: the NUMA node to start the search from.
>  *
>  * Requires rcu_lock to be held.
>  *
> .....
> Regarding irq_set_affinity_and_hint it also garbs rcu locks internally:
> irq_set_affinity_and_hint ->__irq_apply_affinity_hint() -> irq_to_desc() -> mtree_load().
> Also see how irq_set_affinity_and_hint is called in mana_gd_setup_irqs.
> IMO we should drop the nesting when not needed, even though it appears
> harmless.
> 
> Thanks and Regards
> Dipayaan Roy
>

There was a small window after mtree_load() returns irq_desc* and
raw_spin_lock() being taken, where I thought the rcu_read_lock() was
needed. But on futher investigation, I agree it is not needed there as
well. Let me drop this in the v2. Thanks

> > 
> > > > +	for_each_online_cpu(cpu) {
> > > > +		if (len <= 0)
> > > len is unsigned here so <= doesnot makes sense. PLease change it to int
> > > or better use if(!len)
> > 
> > sure, I think I will change it to explicitly exit when len == 0
> > Thanks.
> > 
> > > > +			break;
> > > > +
> > > > +		irq_set_affinity_and_hint(*irqs++, cpumask_of(cpu));
> > > > +		len--;
> > > > +	}
> > > > +	rcu_read_unlock();
> > > > +
> > > > +	return 0;
> > > > +}
> > > > +
> > > >  static int mana_gd_setup_dyn_irqs(struct pci_dev *pdev, int nvec)
> > > >  {
> > > >  	struct gdma_context *gc = pci_get_drvdata(pdev);
> > > > @@ -1722,10 +1739,24 @@ static int mana_gd_setup_dyn_irqs(struct pci_dev *pdev, int nvec)
> > > >  	 * first CPU sibling group since they are already affinitized to HWC IRQ
> > > >  	 */
> > > >  	cpus_read_lock();
> > > > -	if (gc->num_msix_usable <= num_online_cpus())
> > > > +	if (gc->num_msix_usable <= num_online_cpus()) {
> > > >  		skip_first_cpu = true;
> > > > +		err = irq_setup(irqs, nvec, gc->numa_node, skip_first_cpu);
> > > > +	} else {
> > > > +		/*
> > > > +		 * In case our IRQs are more than num_online_cpus, we try to
> > > > +		 * make sure we are using all vcpus. In such a case NUMA or
> > > > +		 * CPU core affinity does not matter.
> > > > +		 * Note that in this case the total mana IRQ should always be
> > > > +		 * num_online_cpu + 1. The first HWC IRQ is already handled
> > > > +		 * in HWC setup calls
> > > > +		 * So, the nvec value in this path should always be equal to
> > > > +		 * num_online_cpu
> > > nit: typo: should be num_online_cpus
> > 
> > noted
> > 
> > > > +		 */
> > > > +		WARN_ON(nvec > num_online_cpus());
> > > > +		err = irq_setup_linear(irqs, nvec);
> > > > +	}
> > > >  
> > > > -	err = irq_setup(irqs, nvec, gc->numa_node, skip_first_cpu);
> > > >  	if (err) {
> > > >  		cpus_read_unlock();
> > > >  		goto free_irq;
> > > > 
> > > > base-commit: e728258debd553c95d2e70f9cd97c9fde27c7130
> > > > -- 
> > > > 2.34.1
> > > > 
> > > Regards
> > > Dipayaan Roy

^ permalink raw reply

* RE: [PATCH 2/2] drm/hyperv: use VMBUS_RING_SIZE()
From: Saurabh Singh Sengar @ 2026-04-26  5:00 UTC (permalink / raw)
  To: Hamza Mahfooz, linux-kernel@vger.kernel.org
  Cc: KY Srinivasan, Haiyang Zhang, Wei Liu, Dexuan Cui, Long Li,
	Stefano Garzarella, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Simon Horman, Himadri Pandya, Michael Kelley,
	linux-hyperv@vger.kernel.org, virtualization@lists.linux.dev,
	netdev@vger.kernel.org, Saurabh Sengar, Maarten Lankhorst,
	Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
	Deepak Rawat, dri-devel@lists.freedesktop.org,
	stable@kernel.vger.org
In-Reply-To: <20260425181719.1538483-2-hamzamahfooz@linux.microsoft.com>

> Subject: [PATCH 2/2] drm/hyperv: use VMBUS_RING_SIZE()
> 
> VMBUS ring buffers must be page aligned. So, use VMBUS_RING_SIZE() to
> ensure they are always aligned and large enough to hold all of the relevant
> data.
> 
> Cc: stable@kernel.vger.org
> Fixes: 76c56a5affeb ("drm/hyperv: Add DRM driver for hyperv synthetic video
> device")
> Signed-off-by: Hamza Mahfooz <hamzamahfooz@linux.microsoft.com>
> ---
>  drivers/gpu/drm/hyperv/hyperv_drm_proto.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/drivers/gpu/drm/hyperv/hyperv_drm_proto.c
> b/drivers/gpu/drm/hyperv/hyperv_drm_proto.c
> index 051ecc526832..753d97bff76f 100644
> --- a/drivers/gpu/drm/hyperv/hyperv_drm_proto.c
> +++ b/drivers/gpu/drm/hyperv/hyperv_drm_proto.c
> @@ -10,7 +10,7 @@
> 
>  #include "hyperv_drm.h"
> 
> -#define VMBUS_RING_BUFSIZE (256 * 1024)
> +#define VMBUS_RING_BUFSIZE VMBUS_RING_SIZE(256 * 1024)
>  #define VMBUS_VSP_TIMEOUT (10 * HZ)
> 
>  #define SYNTHVID_VERSION(major, minor) ((minor) << 16 | (major))
> --
> 2.54.0

Although this lgtm, but this may change the behaviour on ARM64 systems with page size > 4K ?
Have we tested it ?

Reviewed-by: Saurabh Sengar <ssengar@linux.microsoft.com>



^ permalink raw reply

* [PATCH v2 bpf] bpf: Free reuseport cBPF prog after RCU grace period.
From: Kuniyuki Iwashima @ 2026-04-26  1:26 UTC (permalink / raw)
  To: Martin KaFai Lau, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Eduard Zingerman, Kumar Kartikeya Dwivedi
  Cc: Kuniyuki Iwashima, Kuniyuki Iwashima, bpf, netdev, Eulgyu Kim

Eulgyu Kim reported the splat below with a repro. [0]

The repro sets up a UDP reuseport group with a cBPF prog and
replaces it with a new one while another thread is sending
a UDP packet to the group.

The reuseport prog is freed by sk_reuseport_prog_free().
bpf_prog_put() is called for "e"BPF prog to destruct through
multiple stages while cBPF prog is freed immediately by
bpf_release_orig_filter() and bpf_prog_free().

If a reuseport prog is detached from the setsockopt() path
(reuseport_attach_prog() or reuseport_detach_prog()),
sk_reuseport_prog_free() is called without waiting for RCU
readers to complete, resulting in various bugs.

Let's defer freeing the reuseport cBPF prog after one RCU
grace period.

Note "e"BPF prog is safe as is unless the fast path starts
to touch fields destroyed in bpf_prog_put_deferred() and
__bpf_prog_put_noref().

[0]:
BUG: KASAN: vmalloc-out-of-bounds in reuseport_select_sock+0xedc/0x1220 net/core/sock_reuseport.c:596
Read of size 4 at addr ffffc9000051e004 by task slowme/10208
CPU: 6 UID: 1000 PID: 10208 Comm: slowme Not tainted 7.0.0-geb7ac95ff75e #32 PREEMPT(full)
Hardware name: QEMU Ubuntu 24.04 PC v2 (i440FX + PIIX, arch_caps fix, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
Call Trace:
 <IRQ>
 dump_stack_lvl+0xe8/0x150 lib/dump_stack.c:120
 print_address_description mm/kasan/report.c:378 [inline]
 print_report+0xca/0x240 mm/kasan/report.c:482
 kasan_report+0x118/0x150 mm/kasan/report.c:595
 reuseport_select_sock+0xedc/0x1220 net/core/sock_reuseport.c:596
 udp4_lib_lookup2+0x3bc/0x950 net/ipv4/udp.c:495
 __udp4_lib_lookup+0x768/0xe20 net/ipv4/udp.c:723
 __udp4_lib_lookup_skb+0x297/0x390 net/ipv4/udp.c:752
 __udp4_lib_rcv+0x1312/0x2620 net/ipv4/udp.c:2752
 ip_protocol_deliver_rcu+0x282/0x440 net/ipv4/ip_input.c:207
 ip_local_deliver_finish+0x3bb/0x6f0 net/ipv4/ip_input.c:241
 NF_HOOK+0x30c/0x3a0 include/linux/netfilter.h:318
 NF_HOOK+0x30c/0x3a0 include/linux/netfilter.h:318
 __netif_receive_skb_one_core net/core/dev.c:6181 [inline]
 __netif_receive_skb net/core/dev.c:6294 [inline]
 process_backlog+0xaa4/0x1960 net/core/dev.c:6645
 __napi_poll+0xae/0x340 net/core/dev.c:7709
 napi_poll net/core/dev.c:7772 [inline]
 net_rx_action+0x5d7/0xf50 net/core/dev.c:7929
 handle_softirqs+0x22b/0x870 kernel/softirq.c:622
 do_softirq+0x76/0xd0 kernel/softirq.c:523
 </IRQ>
 <TASK>
 __local_bh_enable_ip+0xf8/0x130 kernel/softirq.c:450
 local_bh_enable include/linux/bottom_half.h:33 [inline]
 rcu_read_unlock_bh include/linux/rcupdate.h:924 [inline]
 __dev_queue_xmit+0x1dd7/0x3710 net/core/dev.c:4890
 neigh_output include/net/neighbour.h:556 [inline]
 ip_finish_output2+0xca9/0x1070 net/ipv4/ip_output.c:237
 NF_HOOK_COND include/linux/netfilter.h:307 [inline]
 ip_output+0x29f/0x450 net/ipv4/ip_output.c:438
 ip_send_skb+0x45/0xc0 net/ipv4/ip_output.c:1508
 udp_send_skb+0xb04/0x1510 net/ipv4/udp.c:1195
 udp_sendmsg+0x1a71/0x2350 net/ipv4/udp.c:1485
 sock_sendmsg_nosec net/socket.c:727 [inline]
 __sock_sendmsg net/socket.c:742 [inline]
 __sys_sendto+0x554/0x680 net/socket.c:2206
 __do_sys_sendto net/socket.c:2213 [inline]
 __se_sys_sendto net/socket.c:2209 [inline]
 __x64_sys_sendto+0xde/0x100 net/socket.c:2209
 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
 do_syscall_64+0x160/0xf80 arch/x86/entry/syscall_64.c:94
 entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x415a2d
Code: b3 66 2e 0f 1f 84 00 00 00 00 00 66 90 f3 0f 1e fa 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 b8 ff ff ff f7 d8 64 89 01 48
RSP: 002b:00007f6bc31e41e8 EFLAGS: 00000212 ORIG_RAX: 000000000000002c
RAX: ffffffffffffffda RBX: 00007f6bc31e4cdc RCX: 0000000000415a2d
RDX: 0000000000000001 RSI: 00007f6bc31e421f RDI: 0000000000000003
RBP: 00007f6bc31e4240 R08: 00007f6bc31e4220 R09: 0000000000000010
R10: 0000000000000000 R11: 0000000000000212 R12: 00007f6bc31e46c0
R13: ffffffffffffffb8 R14: 0000000000000000 R15: 00007ffc9b0d70b0
 </TASK>

Fixes: 538950a1b752 ("soreuseport: setsockopt SO_ATTACH_REUSEPORT_[CE]BPF")
Reported-by: Eulgyu Kim <eulgyukim@snu.ac.kr>
Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
---
v2: Drop unnecessary arg change.
v1: https://lore.kernel.org/bpf/20260424235247.1990272-1-kuniyu@google.com/
---
 net/core/filter.c | 15 ++++++++++++---
 1 file changed, 12 insertions(+), 3 deletions(-)

diff --git a/net/core/filter.c b/net/core/filter.c
index bc96c18df4e0..c77caebcf4d0 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -1654,15 +1654,24 @@ int sk_reuseport_attach_bpf(u32 ufd, struct sock *sk)
 	return err;
 }
 
+static void sk_reuseport_prog_free_rcu(struct rcu_head *rcu)
+{
+	struct bpf_prog_aux *aux = container_of(rcu, struct bpf_prog_aux, rcu);
+	struct bpf_prog *prog = aux->prog;
+
+	bpf_release_orig_filter(prog);
+	bpf_prog_free(prog);
+}
+
 void sk_reuseport_prog_free(struct bpf_prog *prog)
 {
 	if (!prog)
 		return;
 
-	if (prog->type == BPF_PROG_TYPE_SK_REUSEPORT)
-		bpf_prog_put(prog);
+	if (bpf_prog_was_classic(prog))
+		call_rcu(&prog->aux->rcu, sk_reuseport_prog_free_rcu);
 	else
-		bpf_prog_destroy(prog);
+		bpf_prog_put(prog);
 }
 
 static inline int __bpf_try_make_writable(struct sk_buff *skb,
-- 
2.54.0.rc2.544.gc7ae2d5bb8-goog


^ permalink raw reply related

* Re: [RFC Patch net-next v1 0/9] r8169: add RSS support for RTL8127
From: Heiner Kallweit @ 2026-04-25 22:49 UTC (permalink / raw)
  To: javen, nic_swsd, andrew+netdev, davem, edumazet, kuba, pabeni,
	horms
  Cc: netdev, linux-kernel
In-Reply-To: <20260420021957.1756-1-javen_xu@realsil.com.cn>

On 20.04.2026 04:19, javen wrote:
> From: Javen Xu <javen_xu@realsil.com.cn>
> 
> This series patch adds RSS support for RTL8127 in the r8169 driver.
> 
> Currently, without RSS support, a single CPU core handles all incoming
> traffic. Under heavy loads, this single core becomes a bottleneck, causing
> high softirq usage and leading to unstable and degraded network throughput.
> 
> As a result, we add rss support for RTL8127. This RFC patch is just for
> discussing. And we do some experiments on AMD platform. Below is the 
> result.
> 
> Platform: AMD Ryzen Embedded R2514 with Radeon Graphics(4 Cores/8 Threads)

An older embedded CPU (AFAICS from 2019, refreshed in 2022) in reality is
unlikely to be used with sustained 10GBit traffic. It would be too weak to
handle userspace apps making use of this high throughput. This hw edge case
IMO isn't really an argument for adding 1.000 LoC, blowing up driver structs,
and adding the complexity of dealing with a register layout changing every
two chip versions.

It's really a problem that Realtek frequently changes register layout and/or
register semantics in a not backward-compatible way (and doesn't provide
documentation), resulting in ugly versioned stuff like the following.

IMR_V2_SET_REG_8125	= 0x0d0c,
IMR_V2_CLEAR_REG_8125	= 0x0d00,
IMR_V4_L2_CLEAR_REG_8125 = 0x0d10,
ISR_V2_8125		= 0x0d04,
ISR_V4_L2_8125		= 0x0d14,

case RTL_GIGA_MAC_VER_80:
	tp->HwSuppIsrVer = 6;
default:
	tp->HwSuppIsrVer = 1;

This messy hw design makes it hard to develop maintainable drivers.
This is underlined by the fact that Realtek has separate r8125, r8126,
r8127 drivers, even though they share most of the code.

> Arch: x86_64
> Test command: 
>   Server: iperf3 -s
>   Client: iperf3 -c 192.168.2.1 -P 20 -t 3600
> Monitor: mpstat -P ALL 1
> 
> Before this patch (Without RSS):
>   Throughput: Unstable, fluctuating between 3.76 Gbits/sec and
>   8.2 Gbits/sec.
>   CPU Usage: A single CPU core is fully occupied with softirq reaching 
>   up to 96%.
> 
> After this patch (With RSS enabled):
>   Throughput: Stable at 9.42 Gbits/sec.
>   CPU Usage: The traffic load is evenly distributed across multiple CPU
>   cores. The maximum softirq on a single core dropped to 63%.
>   
> Patch summary:
>   Patch 1: Adds necessary macro and register definitions for RSS.
>   Patch 2-4: Support NAPI and multi RX/TX queues.

Driver supports NAPI already.

>   Patch 5-6: Support MSI-X and enables it specifically for RTL8127.

Also MSI-X is used already.

>   Patch 7: Enables RSS for RTL8127.
>   Patch 8-9: Adds ethtool support to configure the number of RX queues.
>   
> Javen Xu (9):
>   r8169: add some register definitions
>   r8169: add napi and irq support
>   r8169: add support for multi tx queues
>   r8169: add support for multi rx queues
>   r8169: add support for msix
>   r8169: enable msix for RTL8127
>   r8169: add support and enable rss
>   r8169: move struct ethtool_ops
>   r8169: add support for ethtool
> 
>  drivers/net/ethernet/realtek/r8169_main.c | 1437 ++++++++++++++++++---
>  1 file changed, 1238 insertions(+), 199 deletions(-)
> 

Series includes functions like rtl8169_desc_quirk() indicating a need to work
around hw errata. Would be helpful to add comments describing the hw erratum,
best with a link to documentation.


^ permalink raw reply

* Re: [RFC Patch net-next v1 1/9] r8169: add some register definitions
From: Heiner Kallweit @ 2026-04-25 22:32 UTC (permalink / raw)
  To: javen, nic_swsd, andrew+netdev, davem, edumazet, kuba, pabeni,
	horms
  Cc: netdev, linux-kernel
In-Reply-To: <20260420021957.1756-2-javen_xu@realsil.com.cn>

On 20.04.2026 04:19, javen wrote:
> From: Javen Xu <javen_xu@realsil.com.cn>
> 
> To support rss, this patch adds some macro definitions and register
> definitions.
> 
> Signed-off-by: Javen Xu <javen_xu@realsil.com.cn>
> ---
>  drivers/net/ethernet/realtek/r8169_main.c | 75 +++++++++++++++++++++++
>  1 file changed, 75 insertions(+)
> 
> diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c
> index 791277e750ba..0fbec27e4a0d 100644
> --- a/drivers/net/ethernet/realtek/r8169_main.c
> +++ b/drivers/net/ethernet/realtek/r8169_main.c
> @@ -77,6 +77,23 @@
>  #define R8169_RX_RING_BYTES	(NUM_RX_DESC * sizeof(struct RxDesc))
>  #define R8169_TX_STOP_THRS	(MAX_SKB_FRAGS + 1)
>  #define R8169_TX_START_THRS	(2 * R8169_TX_STOP_THRS)
> +#define R8169_MAX_RX_QUEUES	8
> +#define R8169_MAX_TX_QUEUES	1
> +#define R8169_MAX_MSIX_VEC	32
> +#define R8127_MAX_TX_QUEUES	1

Then why multi tx queue support?

> +#define R8127_MAX_RX_QUEUES	8
> +#define R8127_MAX_IRQ		32
> +#define R8127_MIN_IRQ		30

This isn't self-explanatory. What do min and max refer to here?

> +#define RTL8127_RSS_KEY_SIZE	40
> +#define RSS_CPU_NUM_OFFSET	16
> +#define RSS_MASK_BITS_OFFSET	8
> +#define RTL8127_MAX_INDIRECTION_TABLE_ENTRIES 128
> +#define RXS_8125B_RSS_UDP_V4 BIT(27)
> +#define RXS_8125_RSS_IPV4_V4 BIT(28)
> +#define RXS_8125_RSS_IPV6_V4 BIT(29)
> +#define RXS_8125_RSS_TCP_V4 BIT(30)
> +#define RTL8127_RXS_RSS_L3_TYPE_MASK_V4 (RXS_8125_RSS_IPV4_V4 | RXS_8125_RSS_IPV6_V4)
> +#define RTL8127_RXS_RSS_L4_TYPE_MASK_V4 (RXS_8125_RSS_TCP_V4 | RXS_8125B_RSS_UDP_V4)
>  
>  #define OCP_STD_PHY_BASE	0xa400
>  
> @@ -435,6 +452,8 @@ enum rtl8125_registers {
>  #define INT_CFG0_CLKREQEN		BIT(3)
>  	IntrMask_8125		= 0x38,
>  	IntrStatus_8125		= 0x3c,
> +	IntrMask1_8125		= 0x800,
> +	IntrStatus1_8125	= 0x802,
>  	INT_CFG1_8125		= 0x7a,
>  	LEDSEL2			= 0x84,
>  	LEDSEL1			= 0x86,
> @@ -444,6 +463,36 @@ enum rtl8125_registers {
>  	RSS_CTRL_8125		= 0x4500,
>  	Q_NUM_CTRL_8125		= 0x4800,
>  	EEE_TXIDLE_TIMER_8125	= 0x6048,
> +	TNPDS_Q1_LOW		= 0x2100,
> +	RDSAR_Q1_LOW		= 0x4000,
> +	IMR_V2_SET_REG_8125	= 0x0d0c,
> +	IMR_V2_CLEAR_REG_8125	= 0x0d00,
> +	IMR_V4_L2_CLEAR_REG_8125 = 0x0d10,
> +	ISR_V2_8125		= 0x0d04,
> +	ISR_V4_L2_8125		= 0x0d14,
> +};
> +
> +enum rtl8127_msix_id {
> +	MSIX_ID_V4_LINKCHG	= 29,
> +};
> +
> +enum rtl8127_rss_register_content {
> +	RSS_CTRL_TCP_IPV4_SUPP		= (1 << 0),
> +	RSS_CTRL_IPV4_SUPP		= (1 << 1),
> +	RSS_CTRL_TCP_IPV6_SUPP		= (1 << 2),
> +	RSS_CTRL_IPV6_SUPP		= (1 << 3),
> +	RSS_CTRL_IPV6_EXT_SUPP		= (1 << 4),
> +	RSS_CTRL_TCP_IPV6_EXT_SUPP	= (1 << 5),
> +	RSS_CTRL_UDP_IPV4_SUPP		= (1 << 11),
> +	RSS_CTRL_UDP_IPV6_SUPP		= (1 << 12),
> +	RSS_CTRL_UDP_IPV6_EXT_SUPP	= (1 << 13),
> +	RSS_INDIRECTION_TBL_8125_V2	= 0x4700,
> +	RSS_KEY_8125			= 0x4600,
> +};
> +
> +enum rtl8127_rss_flag {
> +	RTL_8125_RSS_FLAG_HASH_UDP_IPV4  = (1 << 0),
> +	RTL_8125_RSS_FLAG_HASH_UDP_IPV6  = (1 << 1),
>  };
>  
>  #define LEDSEL_MASK_8125	0x23f
> @@ -474,6 +523,10 @@ enum rtl_register_content {
>  	RxRUNT	= (1 << 20),
>  	RxCRC	= (1 << 19),
>  
> +	RxRES_RSS	= (1 << 22),
> +	RxRUNT_RSS	= (1 << 21),
> +	RxCRC_RSS	= (1 << 20),
> +
>  	/* ChipCmdBits */
>  	StopReq		= 0x80,
>  	CmdReset	= 0x10,
> @@ -576,6 +629,9 @@ enum rtl_register_content {
>  
>  	/* magic enable v2 */
>  	MagicPacket_v2	= (1 << 16),	/* Wake up when receives a Magic Packet */
> +	ISRIMR_V6_LINKCHG	= (1 << 29),
> +	ISRIMR_V6_TOK_Q0	= (1 << 8),
> +	ISRIMR_V6_ROK_Q0	= (1 << 0),
>  };
>  
>  enum rtl_desc_bit {
> @@ -633,6 +689,11 @@ enum rtl_rx_desc_bit {
>  #define RxProtoIP	(PID1 | PID0)
>  #define RxProtoMask	RxProtoIP
>  
> +	RxUDPT_v4	= (1 << 19),
> +	RxTCPT_v4	= (1 << 18),
> +	RxUDPF_v4	= (1 << 16), /* UDP/IP checksum failed */
> +	RxTCPF_v4	= (1 << 15), /* TCP/IP checksum failed */
> +
>  	IPFail		= (1 << 16), /* IP checksum failed */
>  	UDPFail		= (1 << 15), /* UDP/IP checksum failed */
>  	TCPFail		= (1 << 14), /* TCP/IP checksum failed */
> @@ -659,6 +720,11 @@ struct RxDesc {
>  	__le64 addr;
>  };
>  
> +enum features {
> +	RTL_FEATURE_MSI		= (1 << 1),
> +	RTL_FEATURE_MSIX	= (1 << 2),
> +};
> +
>  struct ring_info {
>  	struct sk_buff	*skb;
>  	u32		len;
> @@ -728,6 +794,13 @@ enum rtl_dash_type {
>  	RTL_DASH_25_BP,
>  };
>  
> +enum rx_desc_ring_type {
> +	RX_DESC_RING_TYPE_UNKNOWN = 0,
> +	RX_DESC_RING_TYPE_DEAFULT,
> +	RX_DESC_RING_TYPE_RSS,
> +	RX_DESC_RING_TYPE_MAX
> +};
> +
>  struct rtl8169_private {
>  	void __iomem *mmio_addr;	/* memory map physical address */
>  	struct pci_dev *pci_dev;
> @@ -763,6 +836,8 @@ struct rtl8169_private {
>  	unsigned aspm_manageable:1;
>  	unsigned dash_enabled:1;
>  	bool sfp_mode:1;
> +	bool rss_support:1;
> +	bool rss_enable:1;
>  	dma_addr_t counters_phys_addr;
>  	struct rtl8169_counters *counters;
>  	struct rtl8169_tc_offsets tc_offset;


^ permalink raw reply

* Re: [RFC Patch net-next v1 4/9] r8169: add support for multi rx queues
From: Heiner Kallweit @ 2026-04-25 22:23 UTC (permalink / raw)
  To: javen, nic_swsd, andrew+netdev, davem, edumazet, kuba, pabeni,
	horms
  Cc: netdev, linux-kernel
In-Reply-To: <20260420021957.1756-5-javen_xu@realsil.com.cn>

On 20.04.2026 04:19, javen wrote:
> From: Javen Xu <javen_xu@realsil.com.cn>
> 
> This patch supports for multi rx queues. But we set rx queue num 1 here.
> We will add support for rx queue num 8 in rss patch.
> 
> Signed-off-by: Javen Xu <javen_xu@realsil.com.cn>
> ---
>  drivers/net/ethernet/realtek/r8169_main.c | 272 +++++++++++++++++-----
>  1 file changed, 212 insertions(+), 60 deletions(-)
> 
> diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c
> index 05f0cb532a31..52e690eba644 100644
> --- a/drivers/net/ethernet/realtek/r8169_main.c
> +++ b/drivers/net/ethernet/realtek/r8169_main.c
> @@ -73,7 +73,6 @@
>  #define R8169_RX_BUF_SIZE	(SZ_16K - 1)
>  #define NUM_TX_DESC	256	/* Number of Tx descriptor registers */
>  #define NUM_RX_DESC	256	/* Number of Rx descriptor registers */
> -#define R8169_RX_RING_BYTES	(NUM_RX_DESC * sizeof(struct RxDesc))
>  #define R8169_TX_STOP_THRS	(MAX_SKB_FRAGS + 1)
>  #define R8169_TX_START_THRS	(2 * R8169_TX_STOP_THRS)
>  #define R8169_MAX_RX_QUEUES	8
> @@ -793,6 +792,19 @@ enum rtl_dash_type {
>  	RTL_DASH_25_BP,
>  };
>  
> +struct rtl8169_rx_ring {
> +	u32 index;				/* Rx queue index */
> +	u32 cur_rx;				/* Index into the Rx descriptor buffer of next Rx pkt. */
> +	u32 dirty_rx;				/* Index into the Rx descriptor buffer for recycling. */
> +	u32 num_rx_desc;			/* num of Rx desc */
> +	struct RxDesc *RxDescArray;		/* array of Rx Desc*/
> +	u32 RxDescAllocSize;			/* memory size per descs of ring */
> +	dma_addr_t RxDescPhyAddr[NUM_RX_DESC];	/* Rx data buffer physical dma address */
> +	dma_addr_t RxPhyAddr;			/* Rx desc physical address */
> +	struct page *Rx_databuff[NUM_RX_DESC];	/* Rx data buffers */
> +	u16 rdsar_reg;				/* Receive Descriptor Start Address */
> +};
> +
>  struct rtl8169_tx_ring {
>  	u32 index;				/* Tx queue index */
>  	u32 cur_tx;				/* Index into the Tx descriptor buffer of next Tx pkt. */
> @@ -833,12 +845,9 @@ struct rtl8169_private {
>  	struct napi_struct napi;
>  	enum mac_version mac_version;
>  	enum rtl_dash_type dash_type;
> -	u32 cur_rx; /* Index into the Rx descriptor buffer of next Rx pkt. */
> -	struct RxDesc *RxDescArray;	/* 256-aligned Rx descriptor ring */
> -	dma_addr_t RxPhyAddr;
> -	struct page *Rx_databuff[NUM_RX_DESC];	/* Rx data buffers */
>  	struct rtl8169_irq irq_tbl[R8169_MAX_MSIX_VEC];
>  	struct rtl8169_napi r8169napi[R8169_MAX_MSIX_VEC];
> +	struct rtl8169_rx_ring rx_ring[R8169_MAX_RX_QUEUES];
>  	struct rtl8169_tx_ring tx_ring[R8169_MAX_TX_QUEUES];
>  	u16 isr_reg[R8169_MAX_MSIX_VEC];
>  	u16 imr_reg[R8169_MAX_MSIX_VEC];
> @@ -848,11 +857,13 @@ struct rtl8169_private {
>  	u16 tx_lpi_timer;
>  	u32 irq_mask;
>  	u16 HwSuppNumTxQueues;
> +	u16 HwSuppNumRxQueues;
>  	u8 min_irq_nvecs;
>  	u8 max_irq_nvecs;
>  	u8 HwSuppIsrVer;
>  	u8 HwCurrIsrVer;
>  	u8 irq_nvecs;
> +	u8 InitRxDescType;
>  	u8 recheck_desc_ownbit;
>  	unsigned int features;
>  	int irq;
> @@ -2728,6 +2739,15 @@ static void rtl_init_rxcfg(struct rtl8169_private *tp)
>  	}
>  }
>  
> +static void rtl8169_rx_desc_init(struct rtl8169_private *tp)
> +{
> +	for (int i = 0; i < tp->num_rx_rings; i++) {
> +		struct rtl8169_rx_ring *ring = &tp->rx_ring[i];
> +
> +		memset(ring->RxDescArray, 0x0, ring->RxDescAllocSize);
> +	}
> +}
> +
>  static void rtl8169_tx_desc_init(struct rtl8169_private *tp)
>  {
>  	for (int i = 0; i < tp->num_tx_rings; i++) {
> @@ -2750,6 +2770,14 @@ static void rtl8169_init_ring_indexes(struct rtl8169_private *tp)
>  		ring->index = i;
>  		netdev_tx_reset_queue(netdev_get_tx_queue(dev, i));
>  	}
> +
> +	for (int i = 0; i < tp->HwSuppNumRxQueues; i++) {
> +		struct rtl8169_rx_ring *ring = &tp->rx_ring[i];
> +
> +		ring->dirty_rx = 0;
> +		ring->cur_rx = 0;
> +		ring->index = i;
> +	}
>  }
>  
>  static void rtl_jumbo_config(struct rtl8169_private *tp)
> @@ -2810,6 +2838,9 @@ static void rtl_hw_reset(struct rtl8169_private *tp)
>  
>  static void rtl_set_ring_size(struct rtl8169_private *tp, u32 rx_num, u32 tx_num)
>  {
> +	for (int i = 0; i < tp->HwSuppNumRxQueues; i++)
> +		tp->rx_ring[i].num_rx_desc = rx_num;
> +
>  	for (int i = 0; i < tp->HwSuppNumTxQueues; i++)
>  		tp->tx_ring[i].num_tx_desc = tx_num;
>  }
> @@ -2820,6 +2851,10 @@ static void rtl_setup_mqs_reg(struct rtl8169_private *tp)
>  	for (int i = 1; i < tp->HwSuppNumTxQueues; i++)
>  		tp->tx_ring[i].tdsar_reg = (u16)(TNPDS_Q1_LOW + (i - 1) * 8);
>  
> +	tp->rx_ring[0].rdsar_reg = RxDescAddrLow;
> +	for (int i = 1; i < tp->HwSuppNumRxQueues; i++)
> +		tp->rx_ring[i].rdsar_reg = (u16)(RDSAR_Q1_LOW + (i - 1) * 8);
> +
>  	if (tp->mac_version <= RTL_GIGA_MAC_VER_52) {
>  		tp->isr_reg[0] = IntrStatus;
>  		tp->imr_reg[0] = IntrMask;
> @@ -2845,15 +2880,18 @@ static void rtl_software_parameter_initialize(struct rtl8169_private *tp)
>  		tp->min_irq_nvecs = R8127_MIN_IRQ;
>  		tp->max_irq_nvecs = R8127_MAX_IRQ;
>  		tp->HwSuppNumTxQueues = R8127_MAX_TX_QUEUES;
> +		tp->HwSuppNumRxQueues = R8127_MAX_RX_QUEUES;
>  		tp->HwSuppIsrVer = 6;
>  		break;
>  	default:
>  		tp->min_irq_nvecs = 1;
>  		tp->max_irq_nvecs = 1;
>  		tp->HwSuppNumTxQueues = 1;
> +		tp->HwSuppNumRxQueues = 1;
>  		tp->HwSuppIsrVer = 1;
>  		break;
>  	}
> +	tp->InitRxDescType = RX_DESC_RING_TYPE_DEAFULT;
>  	tp->HwCurrIsrVer = tp->HwSuppIsrVer;
>  
>  	rtl_setup_mqs_reg(tp);
> @@ -2989,14 +3027,18 @@ static void rtl_set_rx_tx_desc_registers(struct rtl8169_private *tp)
>  	 * register to be written before TxDescAddrLow to work.
>  	 * Switching from MMIO to I/O access fixes the issue as well.
>  	 */
> -	RTL_W32(tp, RxDescAddrHigh, ((u64) tp->RxPhyAddr) >> 32);
> -	RTL_W32(tp, RxDescAddrLow, ((u64) tp->RxPhyAddr) & DMA_BIT_MASK(32));
>  	for (int i = 0; i < tp->num_tx_rings; i++) {
>  		struct rtl8169_tx_ring *ring = &tp->tx_ring[i];
>  
>  		RTL_W32(tp, ring->tdsar_reg, ((u64)ring->TxPhyAddr & DMA_BIT_MASK(32)));
>  		RTL_W32(tp, ring->tdsar_reg + 4, ((u64)ring->TxPhyAddr >> 32));
>  	}
> +	for (int i = 0; i < tp->num_rx_rings; i++) {
> +		struct rtl8169_rx_ring *ring = &tp->rx_ring[i];
> +
> +		RTL_W32(tp, ring->rdsar_reg, ((u64)ring->RxPhyAddr) & DMA_BIT_MASK(32));
> +		RTL_W32(tp, ring->rdsar_reg + 4, ((u64)ring->RxPhyAddr >> 32));
> +	}
>  }
>  
>  static void rtl8169_set_magic_reg(struct rtl8169_private *tp)
> @@ -4332,7 +4374,7 @@ static int rtl8169_change_mtu(struct net_device *dev, int new_mtu)
>  	return 0;
>  }
>  
> -static void rtl8169_mark_to_asic(struct RxDesc *desc)
> +static void rtl8169_mark_to_asic_default(struct RxDesc *desc)
>  {
>  	u32 eor = le32_to_cpu(desc->opts1) & RingEnd;
>  
> @@ -4342,13 +4384,19 @@ static void rtl8169_mark_to_asic(struct RxDesc *desc)
>  	WRITE_ONCE(desc->opts1, cpu_to_le32(DescOwn | eor | R8169_RX_BUF_SIZE));
>  }
>  
> +static void rtl8169_mark_to_asic(struct rtl8169_private *tp, struct RxDesc *desc)
> +{
> +	rtl8169_mark_to_asic_default(desc);
> +}
> +
>  static struct page *rtl8169_alloc_rx_data(struct rtl8169_private *tp,
> -					  struct RxDesc *desc)
> +					  struct rtl8169_rx_ring *ring, unsigned int index)
>  {
>  	struct device *d = tp_to_dev(tp);
>  	int node = dev_to_node(d);
>  	dma_addr_t mapping;
>  	struct page *data;
> +	struct RxDesc *desc = ring->RxDescArray + index;
>  
>  	data = alloc_pages_node(node, GFP_KERNEL, get_order(R8169_RX_BUF_SIZE));
>  	if (!data)
> @@ -4361,44 +4409,56 @@ static struct page *rtl8169_alloc_rx_data(struct rtl8169_private *tp,
>  		return NULL;
>  	}
>  
> +	ring->RxDescPhyAddr[index] = mapping;
>  	desc->addr = cpu_to_le64(mapping);
> -	rtl8169_mark_to_asic(desc);
> +	rtl8169_mark_to_asic(tp, desc);
>  
>  	return data;
>  }
>  
> -static void rtl8169_rx_clear(struct rtl8169_private *tp)
> +static void rtl8169_rx_clear(struct rtl8169_private *tp, struct rtl8169_rx_ring *ring)
>  {
>  	int i;
>  
> -	for (i = 0; i < NUM_RX_DESC && tp->Rx_databuff[i]; i++) {
> +	for (i = 0; i < NUM_RX_DESC && ring->Rx_databuff[i]; i++) {
>  		dma_unmap_page(tp_to_dev(tp),
> -			       le64_to_cpu(tp->RxDescArray[i].addr),
> +			       ring->RxDescPhyAddr[i],
>  			       R8169_RX_BUF_SIZE, DMA_FROM_DEVICE);
> -		__free_pages(tp->Rx_databuff[i], get_order(R8169_RX_BUF_SIZE));
> -		tp->Rx_databuff[i] = NULL;
> -		tp->RxDescArray[i].addr = 0;
> -		tp->RxDescArray[i].opts1 = 0;
> +		__free_pages(ring->Rx_databuff[i], get_order(R8169_RX_BUF_SIZE));
> +		ring->Rx_databuff[i] = NULL;
> +		ring->RxDescPhyAddr[i] = 0;
> +		ring->RxDescArray[i].addr = 0;
> +		ring->RxDescArray[i].opts1 = 0;
>  	}
>  }
>  
> -static int rtl8169_rx_fill(struct rtl8169_private *tp)
> +static void rtl8169_mark_as_last_descriptor_default(struct RxDesc *desc)
> +{
> +	desc->opts1 |= cpu_to_le32(RingEnd);
> +}
> +
> +static void rtl8169_mark_as_last_descriptor(struct rtl8169_private *tp, struct RxDesc *desc)
> +{
> +	rtl8169_mark_as_last_descriptor_default(desc);
> +}
> +
> +static int rtl8169_rx_fill(struct rtl8169_private *tp, struct rtl8169_rx_ring *ring)
>  {
>  	int i;
>  
>  	for (i = 0; i < NUM_RX_DESC; i++) {
>  		struct page *data;
>  
> -		data = rtl8169_alloc_rx_data(tp, tp->RxDescArray + i);
> +		data = rtl8169_alloc_rx_data(tp, ring, i);
>  		if (!data) {
> -			rtl8169_rx_clear(tp);
> +			rtl8169_rx_clear(tp, ring);
>  			return -ENOMEM;
>  		}
> -		tp->Rx_databuff[i] = data;
> +		ring->Rx_databuff[i] = data;
>  	}
>  
>  	/* mark as last descriptor in the ring */
> -	tp->RxDescArray[NUM_RX_DESC - 1].opts1 |= cpu_to_le32(RingEnd);
> +	rtl8169_mark_as_last_descriptor(tp, &ring->RxDescArray[NUM_RX_DESC - 1]);
>  
>  	return 0;
>  }
> @@ -4438,6 +4498,40 @@ static void rtl8169_free_tx_desc(struct rtl8169_private *tp)
>  	}
>  }
>  
> +static int rtl8169_alloc_rx_desc(struct rtl8169_private *tp)
> +{
> +	struct rtl8169_rx_ring *ring;
> +	struct pci_dev *pdev = tp->pci_dev;
> +
> +	for (int i = 0; i < tp->num_rx_rings; i++) {
> +		ring = &tp->rx_ring[i];
> +		ring->RxDescAllocSize = (ring->num_rx_desc + 1) * sizeof(struct RxDesc);
> +		ring->RxDescArray = dma_alloc_coherent(&pdev->dev,
> +						       ring->RxDescAllocSize,
> +						       &ring->RxPhyAddr,
> +						       GFP_KERNEL);
> +		if (!ring->RxDescArray)
> +			return -1;
> +	}
> +	return 0;
> +}
> +
> +static void rtl8169_free_rx_desc(struct rtl8169_private *tp)
> +{
> +	struct rtl8169_rx_ring *ring;
> +	struct pci_dev *pdev = tp->pci_dev;
> +
> +	for (int i = 0; i < tp->num_rx_rings; i++) {
> +		ring = &tp->rx_ring[i];
> +		if (ring->RxDescArray) {
> +			dma_free_coherent(&pdev->dev,
> +					  ring->RxDescAllocSize,
> +					  ring->RxDescArray,
> +					  ring->RxPhyAddr);
> +			ring->RxDescArray = NULL;
> +		}
> +	}
> +}
>  
>  static int rtl8169_init_ring(struct rtl8169_private *tp)
>  {
> @@ -4445,6 +4539,7 @@ static int rtl8169_init_ring(struct rtl8169_private *tp)
>  
>  	rtl8169_init_ring_indexes(tp);
>  	rtl8169_tx_desc_init(tp);
> +	rtl8169_rx_desc_init(tp);
>  
>  	for (int i = 0; i < tp->num_tx_rings; i++) {
>  		struct rtl8169_tx_ring *ring = &tp->tx_ring[i];
> @@ -4452,9 +4547,14 @@ static int rtl8169_init_ring(struct rtl8169_private *tp)
>  		memset(ring->tx_skb, 0x0, sizeof(ring->tx_skb));
>  	}
>  
> +	for (int i = 0; i < tp->num_rx_rings; i++) {
> +		struct rtl8169_rx_ring *ring = &tp->rx_ring[i];
>  
> +		memset(ring->Rx_databuff, 0, sizeof(ring->Rx_databuff));
> +		retval = rtl8169_rx_fill(tp, ring);
> +	}
>  
> -	return rtl8169_rx_fill(tp);
> +	return retval;
>  }
>  
>  static void rtl8169_unmap_tx_skb(struct rtl8169_private *tp,
> @@ -4549,16 +4649,23 @@ static void rtl8169_cleanup(struct rtl8169_private *tp)
>  	rtl8169_init_ring_indexes(tp);
>  }
>  
> -static void rtl_reset_work(struct rtl8169_private *tp)
> +static void rtl8169_rx_desc_reset(struct rtl8169_private *tp)
>  {
> -	int i;
> +	for (int i = 0; i < tp->num_rx_rings; i++) {
> +		struct rtl8169_rx_ring *ring = &tp->rx_ring[i];
> +
> +		for (int j = 0; j < ring->num_rx_desc; j++)
> +			rtl8169_mark_to_asic(tp, ring->RxDescArray + j);
> +	}
> +}
>  
> +static void rtl_reset_work(struct rtl8169_private *tp)
> +{
>  	netif_stop_queue(tp->dev);
>  
>  	rtl8169_cleanup(tp);
>  
> -	for (i = 0; i < NUM_RX_DESC; i++)
> -		rtl8169_mark_to_asic(tp->RxDescArray + i);
> +	rtl8169_rx_desc_reset(tp);
>  
>  	rtl8169_napi_enable(tp);
>  
> @@ -5033,9 +5140,11 @@ static inline int rtl8169_fragmented_frame(u32 status)
>  	return (status & (FirstFrag | LastFrag)) != (FirstFrag | LastFrag);
>  }
>  
> -static inline void rtl8169_rx_csum(struct sk_buff *skb, u32 opts1)
> +static inline void rtl8169_rx_csum_default(struct rtl8169_private *tp,
> +					   struct sk_buff *skb,
> +					   struct RxDesc *desc)
>  {
> -	u32 status = opts1 & (RxProtoMask | RxCSFailMask);
> +	u32 status = le32_to_cpu(desc->opts1) & (RxProtoMask | RxCSFailMask);
>  
>  	if (status == RxProtoTCP || status == RxProtoUDP)
>  		skb->ip_summed = CHECKSUM_UNNECESSARY;
> @@ -5043,22 +5152,67 @@ static inline void rtl8169_rx_csum(struct sk_buff *skb, u32 opts1)
>  		skb_checksum_none_assert(skb);
>  }
>  
> -static int rtl_rx(struct net_device *dev, struct rtl8169_private *tp, int budget)
> +static inline void rtl8169_rx_csum(struct rtl8169_private *tp,
> +				   struct sk_buff *skb,
> +				   struct RxDesc *desc)
> +{
> +	rtl8169_rx_csum_default(tp, skb, desc);
> +}
> +
> +static u32 rtl8169_rx_desc_opts1(struct rtl8169_private *tp, struct RxDesc *desc)
> +{
> +	return READ_ONCE(desc->opts1);
> +}
> +
> +static int rtl8169_check_rx_desc_error(struct net_device *dev,
> +				       struct rtl8169_private *tp,
> +				       u32 status)
> +{
> +	int ret = 0;
> +
> +	if (unlikely(status & RxRES)) {
> +		if (status & (RxRWT | RxRUNT))
> +			dev->stats.rx_length_errors++;
> +		if (status & RxCRC)
> +			dev->stats.rx_crc_errors++;
> +		ret = -1;

If not using bool, then at least use a proper ERRNO.

> +	}
> +	return ret;
> +}
> +
> +static inline void rtl8169_set_desc_dma_addr(struct rtl8169_private *tp,
> +					     struct RxDesc *desc,
> +					     dma_addr_t mapping)
> +{
> +	desc->addr = cpu_to_le64(mapping);
> +}
> +
> +static int rtl_rx(struct net_device *dev, struct rtl8169_private *tp,
> +		  struct rtl8169_rx_ring *ring, int budget)
>  {
>  	struct device *d = tp_to_dev(tp);
>  	int count;
>  
> -	for (count = 0; count < budget; count++, tp->cur_rx++) {
> -		unsigned int pkt_size, entry = tp->cur_rx % NUM_RX_DESC;
> -		struct RxDesc *desc = tp->RxDescArray + entry;
> +	for (count = 0; count < budget; count++, ring->cur_rx++) {
> +		unsigned int pkt_size, entry = ring->cur_rx % ring->num_rx_desc;
> +		struct RxDesc *desc = ring->RxDescArray + entry;
>  		struct sk_buff *skb;
>  		const void *rx_buf;
>  		dma_addr_t addr;
>  		u32 status;
>  
> -		status = le32_to_cpu(READ_ONCE(desc->opts1));
> -		if (status & DescOwn)
> -			break;
> +		status = le32_to_cpu(rtl8169_rx_desc_opts1(tp, desc));
> +
> +		if (status & DescOwn) {
> +			if (!tp->recheck_desc_ownbit)

This flag needs an explanation.

> +				break;
> +
> +			tp->recheck_desc_ownbit = false;
> +			rtl8169_desc_quirk(tp);
> +			status = le32_to_cpu(rtl8169_rx_desc_opts1(tp, desc));
> +			if (status & DescOwn)
> +				break;
> +		}
>  
>  		/* This barrier is needed to keep us from reading
>  		 * any other fields out of the Rx descriptor until
> @@ -5066,20 +5220,15 @@ static int rtl_rx(struct net_device *dev, struct rtl8169_private *tp, int budget
>  		 */
>  		dma_rmb();
>  
> -		if (unlikely(status & RxRES)) {
> +		if (rtl8169_check_rx_desc_error(dev, tp, status) < 0) {
>  			if (net_ratelimit())
>  				netdev_warn(dev, "Rx ERROR. status = %08x\n",
>  					    status);
> +
>  			dev->stats.rx_errors++;
> -			if (status & (RxRWT | RxRUNT))
> -				dev->stats.rx_length_errors++;
> -			if (status & RxCRC)
> -				dev->stats.rx_crc_errors++;
>  
>  			if (!(dev->features & NETIF_F_RXALL))
>  				goto release_descriptor;
> -			else if (status & RxRWT || !(status & (RxRUNT | RxCRC)))
> -				goto release_descriptor;
>  		}
>  
>  		pkt_size = status & GENMASK(13, 0);
> @@ -5095,14 +5244,14 @@ static int rtl_rx(struct net_device *dev, struct rtl8169_private *tp, int budget
>  			goto release_descriptor;
>  		}
>  
> -		skb = napi_alloc_skb(&tp->r8169napi[0].napi, pkt_size);
> +		skb = napi_alloc_skb(&tp->r8169napi[ring->index].napi, pkt_size);
>  		if (unlikely(!skb)) {
>  			dev->stats.rx_dropped++;
>  			goto release_descriptor;
>  		}
>  
> -		addr = le64_to_cpu(desc->addr);
> -		rx_buf = page_address(tp->Rx_databuff[entry]);
> +		addr = ring->RxDescPhyAddr[entry];
> +		rx_buf = page_address(ring->Rx_databuff[entry]);
>  
>  		dma_sync_single_for_cpu(d, addr, pkt_size, DMA_FROM_DEVICE);
>  		prefetch(rx_buf);
> @@ -5111,7 +5260,7 @@ static int rtl_rx(struct net_device *dev, struct rtl8169_private *tp, int budget
>  		skb->len = pkt_size;
>  		dma_sync_single_for_device(d, addr, pkt_size, DMA_FROM_DEVICE);
>  
> -		rtl8169_rx_csum(skb, status);
> +		rtl8169_rx_csum(tp, skb, desc);
>  		skb->protocol = eth_type_trans(skb, dev);
>  
>  		rtl8169_rx_vlan_tag(desc, skb);
> @@ -5119,11 +5268,13 @@ static int rtl_rx(struct net_device *dev, struct rtl8169_private *tp, int budget
>  		if (skb->pkt_type == PACKET_MULTICAST)
>  			dev->stats.multicast++;
>  
> -		napi_gro_receive(&tp->r8169napi[0].napi, skb);
> +		napi_gro_receive(&tp->r8169napi[ring->index].napi, skb);
>  
>  		dev_sw_netstats_rx_add(dev, pkt_size);
>  release_descriptor:
> -		rtl8169_mark_to_asic(desc);
> +		rtl8169_set_desc_dma_addr(tp, desc, ring->RxDescPhyAddr[entry]);
> +		dma_wmb();

Several parts of the code indicate that you didn't run checkpatch on it.
All memory barriers require a comment with an explanation.

> +		rtl8169_mark_to_asic(tp, desc);
>  	}
>  
>  	return count;
> @@ -5239,7 +5390,8 @@ static int rtl8169_poll(struct napi_struct *napi, int budget)
>  	for (int i = 0; i < tp->num_tx_rings; i++)
>  		rtl_tx(dev, tp, &tp->tx_ring[i], budget);
>  
> -	work_done = rtl_rx(dev, tp, budget);
> +	for (int i = 0; i < tp->num_rx_rings; i++)
> +		work_done += rtl_rx(dev, tp, &tp->rx_ring[i], budget);
>  
>  	if (work_done < budget && napi_complete_done(napi, work_done))
>  		rtl_irq_enable(tp);
> @@ -5371,16 +5523,16 @@ static int rtl8169_close(struct net_device *dev)
>  	netif_stop_queue(dev);
>  
>  	rtl8169_down(tp);
> -	rtl8169_rx_clear(tp);
>  
> +	for (int i = 0; i < tp->num_rx_rings; i++)
> +		rtl8169_rx_clear(tp, &tp->rx_ring[i]);
>  
>  	rtl8169_free_irq(tp);
>  
>  	phy_disconnect(tp->phydev);
>  
> -	dma_free_coherent(&pdev->dev, R8169_RX_RING_BYTES, tp->RxDescArray,
> -			  tp->RxPhyAddr);
> -	tp->RxDescArray = NULL;
> +	rtl8169_free_rx_desc(tp);
> +
>  	rtl8169_free_tx_desc(tp);
>  
>  	pm_runtime_put_sync(&pdev->dev);
> @@ -5411,11 +5563,12 @@ static int rtl_open(struct net_device *dev)
>  	 * dma_alloc_coherent provides more.
>  	 */
>  
> -	tp->RxDescArray = dma_alloc_coherent(&pdev->dev, R8169_RX_RING_BYTES,
> -					     &tp->RxPhyAddr, GFP_KERNEL);
>  	if (rtl8169_alloc_tx_desc(tp) < 0)
>  		goto err_free_tx_0;
>  
> +	if (rtl8169_alloc_rx_desc(tp) < 0)
> +		goto err_free_rx_1;
> +
>  	retval = rtl8169_init_ring(tp);
>  	if (retval < 0)
>  		goto err_free_rx_1;
> @@ -5444,11 +5597,10 @@ static int rtl_open(struct net_device *dev)
>  	rtl8169_free_irq(tp);
>  err_release_fw_2:
>  	rtl_release_firmware(tp);
> -	rtl8169_rx_clear(tp);
> +	for (int i = 0; i < tp->num_rx_rings; i++)
> +		rtl8169_rx_clear(tp, &tp->rx_ring[i]);
>  err_free_rx_1:
> -	dma_free_coherent(&pdev->dev, R8169_RX_RING_BYTES, tp->RxDescArray,
> -			  tp->RxPhyAddr);
> -	tp->RxDescArray = NULL;
> +	rtl8169_free_rx_desc(tp);
>  err_free_tx_0:
>  	rtl8169_free_tx_desc(tp);
>  	goto out;


^ permalink raw reply

* Re: [RFC Patch net-next v1 5/9] r8169: add support for msix
From: Heiner Kallweit @ 2026-04-25 22:14 UTC (permalink / raw)
  To: javen, nic_swsd, andrew+netdev, davem, edumazet, kuba, pabeni,
	horms
  Cc: netdev, linux-kernel
In-Reply-To: <20260420021957.1756-6-javen_xu@realsil.com.cn>

On 20.04.2026 04:19, javen wrote:
> From: Javen Xu <javen_xu@realsil.com.cn>
> 
> This patch add support for msix. But we still use MSI here. And we force
> nvecs to 1. We will modify it in rss patch.

This description is wrong. Also as of today r8169 supports MSIX.
Reason likely is that you're copying code from vendor driver.

> 
> Signed-off-by: Javen Xu <javen_xu@realsil.com.cn>
> ---
>  drivers/net/ethernet/realtek/r8169_main.c | 162 ++++++++++++++++++++--
>  1 file changed, 151 insertions(+), 11 deletions(-)
> 
> diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c
> index 52e690eba644..7d493342ab4b 100644
> --- a/drivers/net/ethernet/realtek/r8169_main.c
> +++ b/drivers/net/ethernet/realtek/r8169_main.c
> @@ -1764,26 +1764,40 @@ static u32 rtl_get_events(struct rtl8169_private *tp)
>  
>  static void rtl_ack_events(struct rtl8169_private *tp, u32 bits)
>  {
> -	if (rtl_is_8125(tp))
> +	if (rtl_is_8125(tp)) {
>  		RTL_W32(tp, IntrStatus_8125, bits);
> -	else
> +		if (tp->features & RTL_FEATURE_MSIX) {
> +			RTL_W32(tp, ISR_V2_8125, 0xffffffff);
> +			RTL_W32(tp, ISR_V4_L2_8125, 0xffffffff);
> +		}
> +	} else {
>  		RTL_W16(tp, IntrStatus, bits);
> +	}
>  }
>  
>  static void rtl_irq_disable(struct rtl8169_private *tp)
>  {
> -	if (rtl_is_8125(tp))
> +	if (rtl_is_8125(tp)) {
>  		RTL_W32(tp, IntrMask_8125, 0);
> -	else
> +		if (tp->features & RTL_FEATURE_MSIX) {
> +			RTL_W32(tp, IMR_V2_CLEAR_REG_8125, 0xffffffff);
> +			RTL_W32(tp, IMR_V4_L2_CLEAR_REG_8125, 0xffffffff);
> +		}
> +	} else {
>  		RTL_W16(tp, IntrMask, 0);
> +	}
>  }
>  
>  static void rtl_irq_enable(struct rtl8169_private *tp)
>  {
> -	if (rtl_is_8125(tp))
> -		RTL_W32(tp, IntrMask_8125, tp->irq_mask);
> -	else
> +	if (rtl_is_8125(tp)) {
> +		if (tp->features & RTL_FEATURE_MSIX)
> +			RTL_W32(tp, IMR_V2_SET_REG_8125, tp->irq_mask);
> +		else
> +			RTL_W32(tp, IntrMask_8125, tp->irq_mask);
> +	} else {
>  		RTL_W16(tp, IntrMask, tp->irq_mask);
> +	}
>  }
>  
>  static void rtl8169_irq_mask_and_ack(struct rtl8169_private *tp)
> @@ -2894,6 +2908,10 @@ static void rtl_software_parameter_initialize(struct rtl8169_private *tp)
>  	tp->InitRxDescType = RX_DESC_RING_TYPE_DEAFULT;
>  	tp->HwCurrIsrVer = tp->HwSuppIsrVer;
>  
> +	/* This just force nvecs, and will be remove in the following patch*/
> +	tp->min_irq_nvecs = 1;
> +	tp->max_irq_nvecs = 1;
> +
>  	rtl_setup_mqs_reg(tp);
>  	rtl_set_ring_size(tp, NUM_RX_DESC, NUM_TX_DESC);
>  }
> @@ -5321,6 +5339,44 @@ static void rtl8169_free_irq(struct rtl8169_private *tp)
>  	}
>  }
>  
> +static void rtl8169_disable_hw_interrupt_msix(struct rtl8169_private *tp, int message_id)
> +{
> +	RTL_W32(tp, IMR_V2_CLEAR_REG_8125, BIT(message_id));
> +}
> +
> +static void rtl8169_clear_hw_isr(struct rtl8169_private *tp, int message_id)
> +{
> +	RTL_W32(tp, ISR_V2_8125, BIT(message_id));
> +}
> +
> +static void rtl8169_enable_hw_interrupt_msix(struct rtl8169_private *tp, int message_id)
> +{
> +	RTL_W32(tp, IMR_V2_SET_REG_8125, BIT(message_id));
> +}
> +
> +static irqreturn_t rtl8169_interrupt_msix(int irq, void *dev_instance)
> +{
> +	struct rtl8169_napi *napi = dev_instance;
> +	struct rtl8169_private *tp = napi->priv;
> +	int message_id = napi->index;
> +
> +	rtl8169_disable_hw_interrupt_msix(tp, message_id);
> +
> +	rtl8169_clear_hw_isr(tp, message_id);
> +
> +	if (message_id == MSIX_ID_V4_LINKCHG) {
> +		phy_mac_interrupt(tp->phydev);
> +		rtl8169_enable_hw_interrupt_msix(tp, message_id);
> +		return IRQ_HANDLED;
> +	}
> +
> +	tp->recheck_desc_ownbit = true;
> +
> +	napi_schedule(&napi->napi);
> +
> +	return IRQ_HANDLED;
> +}
> +
>  static int rtl8169_request_irq(struct rtl8169_private *tp)
>  {
>  	struct net_device *dev = tp->dev;
> @@ -5331,10 +5387,14 @@ static int rtl8169_request_irq(struct rtl8169_private *tp)
>  
>  	for (int i = 0; i < tp->irq_nvecs; i++) {
>  		irq = &tp->irq_tbl[i];
> +		if (tp->features & RTL_FEATURE_MSIX && tp->HwCurrIsrVer > 1)
> +			irq->handler = rtl8169_interrupt_msix;
> +		else
> +			irq->handler = rtl8169_interrupt;
>  
>  		napi = &tp->r8169napi[i];
>  		snprintf(irq->name, len, "%s-%d", dev->name, i);
> -		rc = pci_request_irq(tp->pci_dev, i, rtl8169_interrupt, NULL, napi, irq->name);
> +		rc = pci_request_irq(tp->pci_dev, i, irq->handler, NULL, napi, irq->name);
>  
>  		if (rc)
>  			break;
> @@ -5786,10 +5846,18 @@ 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->features & RTL_FEATURE_MSIX) {
> +		tp->irq_mask = ISRIMR_V6_LINKCHG;
> +		for (int i = 0; i < tp->num_tx_rings; i++)
> +			tp->irq_mask |= ISRIMR_V6_TOK_Q0 << i;
> +		for (int i = 0; i < tp->num_rx_rings; i++)
> +			tp->irq_mask |= ISRIMR_V6_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;
> +	}
>  }
>  
>  static int rtl_alloc_irq(struct rtl8169_private *tp)
> @@ -5817,6 +5885,18 @@ static int rtl_alloc_irq(struct rtl8169_private *tp)
>  	if (nvecs < 0)
>  		nvecs = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES);
>  
> +	tp->features &= ~(RTL_FEATURE_MSIX | RTL_FEATURE_MSI);
> +
> +	if (nvecs > 0) {
> +		tp->irq_nvecs = nvecs;
> +		tp->irq = pci_irq_vector(pdev, 0);
> +		if (nvecs > 1)
> +			tp->features |= RTL_FEATURE_MSIX;
> +		else if (pci_dev_msi_enabled(pdev))
> +			tp->features |= RTL_FEATURE_MSI;
> +		return 0;

Such feature flags, especially for MSI/MSIX, are ugly.
Why don't you leave the interrupt type selection to PCI core?
This needs at least an explanation.

> +	}
> +
>  	tp->irq = pdev->irq;
>  	tp->irq_nvecs = 1;
>  
> @@ -6087,6 +6167,52 @@ static bool rtl_aspm_is_safe(struct rtl8169_private *tp)
>  	return false;
>  }
>  
> +static int rtl8169_poll_msix_rx(struct napi_struct *napi, int budget)
> +{
> +	struct rtl8169_napi *r8169_napi = container_of(napi, struct rtl8169_napi, napi);
> +	struct rtl8169_private *tp = r8169_napi->priv;
> +	struct net_device *dev = tp->dev;
> +	const int message_id = r8169_napi->index;
> +	int work_done = 0;
> +
> +	if (message_id < tp->num_rx_rings)
> +		work_done += rtl_rx(dev, tp, &tp->rx_ring[message_id], budget);
> +
> +	if (work_done < budget && napi_complete_done(napi, work_done))
> +		rtl8169_enable_hw_interrupt_msix(tp, message_id);
> +
> +	return work_done;
> +}
> +
> +static int rtl8169_poll_msix_tx(struct napi_struct *napi, int budget)
> +{
> +	struct rtl8169_napi *r8169_napi = container_of(napi, struct rtl8169_napi, napi);
> +	struct rtl8169_private *tp = r8169_napi->priv;
> +	struct net_device *dev = tp->dev;
> +	unsigned int work_done = 0;
> +	const int message_id = r8169_napi->index;
> +	int tx_ring_idx = message_id - 8;
> +
> +	if (tx_ring_idx >= 0 && tx_ring_idx < tp->num_tx_rings)
> +		work_done += rtl_tx(dev, tp, &tp->tx_ring[tx_ring_idx], budget);
> +
> +	if (work_done < budget && napi_complete_done(napi, work_done))
> +		rtl8169_enable_hw_interrupt_msix(tp, message_id);
> +
> +	return work_done;
> +}
> +
> +static int rtl8169_poll_msix_other(struct napi_struct *napi, int budget)
> +{
> +	struct rtl8169_napi *r8169_napi = container_of(napi, struct rtl8169_napi, napi);
> +	struct rtl8169_private *tp = r8169_napi->priv;
> +	const int message_id = r8169_napi->index;
> +
> +	napi_complete_done(napi, budget);
> +	rtl8169_enable_hw_interrupt_msix(tp, message_id);
> +
> +	return 1;
> +}
>  
>  static void r8169_init_napi(struct rtl8169_private *tp)
>  {
> @@ -6095,6 +6221,20 @@ static void r8169_init_napi(struct rtl8169_private *tp)
>  		int (*poll)(struct napi_struct *napi, int budget);
>  
>  		poll = rtl8169_poll;
> +		if (tp->features & RTL_FEATURE_MSIX) {
> +			switch (tp->HwCurrIsrVer) {
> +			case 6:
> +				if (i < R8127_MAX_RX_QUEUES)
> +					poll = rtl8169_poll_msix_rx;
> +				else if (i > 7 && i < 16)
> +					poll = rtl8169_poll_msix_tx;
> +				else
> +					poll = rtl8169_poll_msix_other;
> +				break;
> +			default:
> +				break;
> +			}
> +		}
>  		netif_napi_add(tp->dev, &r8169napi->napi, poll);
>  		r8169napi->priv = tp;
>  		r8169napi->index = i;


^ permalink raw reply

* Re: [RFC PATCH net-next 0/3] net: macb: candidate fixes for silent TX stall on BCM2712/RP1
From: Lukasz Raczylo @ 2026-04-25 21:48 UTC (permalink / raw)
  To: netdev
  Cc: Nicolas Ferre, Claudiu Beznea, Andrew Lunn, David S . Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni, linux-kernel,
	linux-arm-kernel
In-Reply-To: <cover.1777064117.git.lukasz@raczylo.com>

A follow-up runtime data point on this series.

Fleet state at 2026-04-25 21:46 UTC:

  * Patched uptime (since staggered rollout 2026-04-24 18:10-19:20 UTC):
    - shortest: 26h 26m   (last master upgraded)
    - longest:  27h 34m   (canary)
    - cumulative across 24 nodes: ~651 node-hours

  * Macb-attributable event counts (out-of-band userspace watchdog;
    the [tx-stall] detector watches /sys/class/net/end0/statistics/
    tx_packets + qdisc backlog every 1 s and would have fired
    ip link down/up if any node's TX path froze):
    - RECOVER trigger=tx-stall (actual stalls caught):    0
    - partial [tx-stall] markers (transient 1 s freezes): 0

  * Separately: 40 RECOVER events with trigger=ping fired in this
    window across the fleet, attributable to a brief upstream-network
    outage (gateway / switch event); each node simultaneously lost ping
    to gateway, VIP, and NAS within seconds of each other, then
    recovered.  These are unrelated to the macb hang the patch series
    targets — distinguishing them from a real TX stall is exactly what
    the trigger= tag in the watchdog log is for.

At the pre-patch rate referenced in the cover letter (50 stalls in
95 node-hours observed in our 2026-04-24 14:00-18:10 UTC reference
window, ~0.5 per node-hour), the projected stall count in
651 node-hours is on the order of 342;
observed is 0.

Same observability runs forward; will reply again after a full week
of uptime unless something changes.

--
Lukasz Raczylo

^ permalink raw reply

* Re: [PATCH net 1/1] openvswitch: reject oversized NSH MD2 metadata in push_nsh
From: Ilya Maximets @ 2026-04-25 20:19 UTC (permalink / raw)
  To: Ren Wei, netdev, dev
  Cc: i.maximets, aconole, echaudro, davem, edumazet, kuba, pabeni,
	horms, jbenc, e, yi.y.yang, yuantan098, yifanwucs, tomapufckgml,
	bird, ldy3087146292
In-Reply-To: <9d2b5c6127e149ebd35094d662bfd008c20347c2.1777120226.git.ldy3087146292@gmail.com>

On 4/25/26 6:40 PM, Ren Wei wrote:
> From: Douya Le <ldy3087146292@gmail.com>
> 
> The NSH header length is encoded in 4-byte words in a 6-bit field.
> The current push_nsh validation only checks the MD2 payload against
> NSH_CTX_HDRS_MAX_LEN, which still allows metadata sizes that cannot be
> represented once the base header is included.
> 
> Reject MD2 metadata lengths whose total NSH header size cannot be
> encoded exactly in the NSH length field, whether because the field
> would wrap or because the length is not a multiple of 4 bytes.
> 
> Fixes: b2d0f5d5dc53 ("openvswitch: enable NSH support")
> Cc: stable@kernel.org
> Reported-by: Yuan Tan <yuantan098@gmail.com>
> Reported-by: Yifan Wu <yifanwucs@gmail.com>
> Reported-by: Juefei Pu <tomapufckgml@gmail.com>
> Reported-by: Xin Liu <bird@lzu.edu.cn>
> Tested-by: Douya Le <ldy3087146292@gmail.com>
> Signed-off-by: Douya Le <ldy3087146292@gmail.com>
> Signed-off-by: Ren Wei <n05ec@lzu.edu.cn>
> ---
>  net/openvswitch/flow_netlink.c | 8 +++++++-
>  1 file changed, 7 insertions(+), 1 deletion(-)
> 
> diff --git a/net/openvswitch/flow_netlink.c b/net/openvswitch/flow_netlink.c
> index 13052408a132..8a1ae5309c2c 100644
> --- a/net/openvswitch/flow_netlink.c
> +++ b/net/openvswitch/flow_netlink.c
> @@ -1432,7 +1432,13 @@ static int nsh_key_put_from_nlattr(const struct nlattr *attr,
>  
>  			has_md2 = true;
>  			mdlen = nla_len(a);
> -			if (mdlen > NSH_CTX_HDRS_MAX_LEN || mdlen <= 0) {
> +			/* The NSH length field stores the total header size
> +			 * in 4-byte words in 6 bits. Reject MD2 metadata
> +			 * lengths that cannot be encoded exactly or would
> +			 * make the length field wrap.
> +			 */
> +			if (mdlen <= 0 || !IS_ALIGNED(mdlen, 4) ||
> +			    NSH_BASE_HDR_LEN + mdlen > (NSH_LEN_MASK << 2)) {
>  				OVS_NLERR(
>  				    log,
>  				    "Invalid MD length %d for MD type %d",

I don't think the check is a problem here, the problem is that the constants
are not correct.  We actually had them fixed in userspace, but looks like
no-one ported the change to the kernel side:
  https://patchwork.ozlabs.org/project/openvswitch/patch/1540503710-23597-1-git-send-email-pkusunyifeng@gmail.com/

Adjusting the macros should solve the potential overflow issue.  However,
even if the value overflows the 6-bit length, it will just be truncated to
a smaller value and nothing bad should happen in this case, as far as header
push is concerned.  The context was wrong already, so pushing a truncated
context doesn't make it more wrong.  But we should still fix the constants,
so the checks make sense, of course.

The same for alignment, if the user provides an invalid header that is not
multiple of 4, the code will just truncate it to the previous multiple of 4,
which should not be a problem, as it wasn't correct in the first place.  We
could add this check just to warn the user that they are doing something wrong,
but not having it should not cause any real issues.

Note, the userpace change linked above did fix the real issue in userspace,
but for the kernel side there seem to be no real memory related issues caused
by the wrong constants.

Best regards, Ilya Maximets.

^ permalink raw reply

* Re: [PATCH net-next v7 0/5] TLS read_sock performance scalability
From: Chuck Lever @ 2026-04-25 20:15 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: john.fastabend, Sabrina Dubroca, netdev, kernel-tls-handshake,
	Chuck Lever, Hannes Reinecke, Alistair Francis
In-Reply-To: <20260422190815.7f678ad6@kernel.org>


On Wed, Apr 22, 2026, at 10:08 PM, Jakub Kicinski wrote:
> On Wed, 22 Apr 2026 12:41:43 -0400 Chuck Lever wrote:
>> I see that this series is currently not in v7.1. What is left to do?
>
> It's marked as Changes requested in patchwork, possibly due to
> mis-reading of the discussion on patch 4. We process patches within 
> few days (well, we used to before the AI swarm) so if in doubt you
> should ask sooner :S

Thanks for the pointer to the netdevbpf patchworks instance. Anything
I can do to move this series to "Awaiting upsteam" ?

-- 
Chuck Lever

^ permalink raw reply

* [PATCH 2/2] drm/hyperv: use VMBUS_RING_SIZE()
From: Hamza Mahfooz @ 2026-04-25 18:17 UTC (permalink / raw)
  To: linux-kernel
  Cc: K. Y. Srinivasan, Haiyang Zhang, Wei Liu, Dexuan Cui, Long Li,
	Stefano Garzarella, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Simon Horman, Himadri Pandya, Michael Kelley,
	linux-hyperv, virtualization, netdev, Saurabh Sengar,
	Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
	Simona Vetter, Deepak Rawat, dri-devel, Hamza Mahfooz, stable
In-Reply-To: <20260425181719.1538483-1-hamzamahfooz@linux.microsoft.com>

VMBUS ring buffers must be page aligned. So, use VMBUS_RING_SIZE() to
ensure they are always aligned and large enough to hold all of the
relevant data.

Cc: stable@kernel.vger.org
Fixes: 76c56a5affeb ("drm/hyperv: Add DRM driver for hyperv synthetic video device")
Signed-off-by: Hamza Mahfooz <hamzamahfooz@linux.microsoft.com>
---
 drivers/gpu/drm/hyperv/hyperv_drm_proto.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/hyperv/hyperv_drm_proto.c b/drivers/gpu/drm/hyperv/hyperv_drm_proto.c
index 051ecc526832..753d97bff76f 100644
--- a/drivers/gpu/drm/hyperv/hyperv_drm_proto.c
+++ b/drivers/gpu/drm/hyperv/hyperv_drm_proto.c
@@ -10,7 +10,7 @@
 
 #include "hyperv_drm.h"
 
-#define VMBUS_RING_BUFSIZE (256 * 1024)
+#define VMBUS_RING_BUFSIZE VMBUS_RING_SIZE(256 * 1024)
 #define VMBUS_VSP_TIMEOUT (10 * HZ)
 
 #define SYNTHVID_VERSION(major, minor) ((minor) << 16 | (major))
-- 
2.54.0


^ permalink raw reply related

* [PATCH 1/2] hv_sock: fix ARM64 support
From: Hamza Mahfooz @ 2026-04-25 18:17 UTC (permalink / raw)
  To: linux-kernel
  Cc: K. Y. Srinivasan, Haiyang Zhang, Wei Liu, Dexuan Cui, Long Li,
	Stefano Garzarella, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Simon Horman, Himadri Pandya, Michael Kelley,
	linux-hyperv, virtualization, netdev, Saurabh Sengar,
	Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
	Simona Vetter, Deepak Rawat, dri-devel, Hamza Mahfooz, stable

VMBUS ring buffers must be page aligned. Therefore, the current value of
24K presents a challenge on ARM64 kernels (with 64K pages). So, use
VMBUS_RING_SIZE() to ensure they are always aligned and large enough to
hold all of the relevant data.

Cc: stable@kernel.vger.org
Fixes: 77ffe33363c0 ("hv_sock: use HV_HYP_PAGE_SIZE for Hyper-V communication")
Signed-off-by: Hamza Mahfooz <hamzamahfooz@linux.microsoft.com>
---
 net/vmw_vsock/hyperv_transport.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/net/vmw_vsock/hyperv_transport.c b/net/vmw_vsock/hyperv_transport.c
index 069386a74557..40f09b23efa3 100644
--- a/net/vmw_vsock/hyperv_transport.c
+++ b/net/vmw_vsock/hyperv_transport.c
@@ -375,10 +375,10 @@ static void hvs_open_connection(struct vmbus_channel *chan)
 	} else {
 		sndbuf = max_t(int, sk->sk_sndbuf, RINGBUFFER_HVS_SND_SIZE);
 		sndbuf = min_t(int, sndbuf, RINGBUFFER_HVS_MAX_SIZE);
-		sndbuf = ALIGN(sndbuf, HV_HYP_PAGE_SIZE);
+		sndbuf = VMBUS_RING_SIZE(sndbuf);
 		rcvbuf = max_t(int, sk->sk_rcvbuf, RINGBUFFER_HVS_RCV_SIZE);
 		rcvbuf = min_t(int, rcvbuf, RINGBUFFER_HVS_MAX_SIZE);
-		rcvbuf = ALIGN(rcvbuf, HV_HYP_PAGE_SIZE);
+		rcvbuf = VMBUS_RING_SIZE(rcvbuf);
 	}
 
 	chan->max_pkt_size = HVS_MAX_PKT_SIZE;
-- 
2.54.0


^ permalink raw reply related

* Re: [PATCH bpf] bpf, sockmap: zero-initialize pages allocated in bpf_msg_push_data
From: Weiming Shi @ 2026-04-25 17:59 UTC (permalink / raw)
  To: Jiayuan Chen
  Cc: Martin KaFai Lau, Daniel Borkmann, Alexei Starovoitov,
	Andrii Nakryiko, Eduard Zingerman, Kumar Kartikeya Dwivedi,
	David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	John Fastabend, Stanislav Fomichev, Song Liu, Yonghong Song,
	Jiri Olsa, Simon Horman, bpf, netdev, Xiang Mei, Xinyu Ma
In-Reply-To: <c4a9412d-b6ad-4b80-9580-865575f4152c@linux.dev>

On 26-04-25 11:17, Jiayuan Chen wrote:
> 
> On 4/25/26 3:03 AM, Weiming Shi wrote:
> > bpf_msg_push_data() allocates pages via alloc_pages() without
> > __GFP_ZERO. In the non-copy path, the entire page of uninitialized
> > heap content is added directly to the sk_msg scatterlist, which is
> > then transmitted over TCP to userspace via tcp_bpf_push(). In the
> > copy path, a gap of len bytes between the front and back memcpy
> > regions is similarly left uninitialized.
> > 
> > This leads to a kernel heap information leak: stale page content
> > including kernel pointers from the direct-map and vmemmap regions
> > is transmitted to userspace, which can be used to defeat KASLR.
> > 
> > Add __GFP_ZERO to the alloc_pages() call to ensure the allocated
> > page is always zeroed before it enters the scatterlist.
> 
> 
> 
> As the helper's own documentation says:
> 
>     If a program of type BPF_PROG_TYPE_SK_MSG is run on a msg it may
>     want to insert metadata or options into the msg. This can later be
>     read and used by any of the lower layer BPF hooks.
> 
> The inserted region is meant to be written by the BPF program — that's the
> entire point of calling push.
> 
> If the program doesn't fill it,  the push has no purpose to begin with.
> 
> 
> Isn't the uninitialized content a bug in the BPF program rather than
> something the kernel helper should paper over?
> 

Hi, Thanks for the review.

In my testing a process with only CAP_BPF + CAP_NET_ADMIN can receive
kernel heap and vmalloc pointers through recv() from the uninitialized
pushed region. The uninitialized memory contains critical kernel metadata
such as direct-map and vmalloc pointers, which breaks KASLR.

Kernels without CONFIG_INIT_ON_ALLOC_DEFAULT_ON (e.g. RHEL) are
directly affected the leak is not masked by any mitigation.

Thanks,
Weiming Shi

> 
> > Link: https://lore.kernel.org/all/20260424155913.A19FDC19425@smtp.kernel.org
> > Fixes: 6fff607e2f14 ("bpf: sk_msg program helper bpf_msg_push_data")
> > Tested-by: Xiang Mei <xmei5@asu.edu>
> > Tested-by: Xinyu Ma <mmmxny@gmail.com>
> > Signed-off-by: Weiming Shi <bestswngs@gmail.com>
> > ---
> >   net/core/filter.c | 2 +-
> >   1 file changed, 1 insertion(+), 1 deletion(-)
> > 
> > diff --git a/net/core/filter.c b/net/core/filter.c
> > index bc96c18df4e0..ea02239892fd 100644
> > --- a/net/core/filter.c
> > +++ b/net/core/filter.c
> > @@ -2820,7 +2820,7 @@ BPF_CALL_4(bpf_msg_push_data, struct sk_msg *, msg, u32, start,
> >   	if (!space || (space == 1 && start != offset))
> >   		copy = msg->sg.data[i].length;
> > -	page = alloc_pages(__GFP_NOWARN | GFP_ATOMIC | __GFP_COMP,
> > +	page = alloc_pages(__GFP_NOWARN | GFP_ATOMIC | __GFP_COMP | __GFP_ZERO,
> >   			   get_order(copy + len));
> >   	if (unlikely(!page))
> >   		return -ENOMEM;

^ permalink raw reply

* [PATCH net 1/1] openvswitch: reject oversized NSH MD2 metadata in push_nsh
From: Ren Wei @ 2026-04-25 16:40 UTC (permalink / raw)
  To: netdev, dev
  Cc: aconole, echaudro, i.maximets, davem, edumazet, kuba, pabeni,
	horms, jbenc, e, yi.y.yang, pshelar, yuantan098, yifanwucs,
	tomapufckgml, bird, ldy3087146292, n05ec
In-Reply-To: <cover.1776929256.git.ldy3087146292@gmail.com>

From: Douya Le <ldy3087146292@gmail.com>

The NSH header length is encoded in 4-byte words in a 6-bit field.
The current push_nsh validation only checks the MD2 payload against
NSH_CTX_HDRS_MAX_LEN, which still allows metadata sizes that cannot be
represented once the base header is included.

Reject MD2 metadata lengths whose total NSH header size cannot be
encoded exactly in the NSH length field, whether because the field
would wrap or because the length is not a multiple of 4 bytes.

Fixes: b2d0f5d5dc53 ("openvswitch: enable NSH support")
Cc: stable@kernel.org
Reported-by: Yuan Tan <yuantan098@gmail.com>
Reported-by: Yifan Wu <yifanwucs@gmail.com>
Reported-by: Juefei Pu <tomapufckgml@gmail.com>
Reported-by: Xin Liu <bird@lzu.edu.cn>
Tested-by: Douya Le <ldy3087146292@gmail.com>
Signed-off-by: Douya Le <ldy3087146292@gmail.com>
Signed-off-by: Ren Wei <n05ec@lzu.edu.cn>
---
 net/openvswitch/flow_netlink.c | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/net/openvswitch/flow_netlink.c b/net/openvswitch/flow_netlink.c
index 13052408a132..8a1ae5309c2c 100644
--- a/net/openvswitch/flow_netlink.c
+++ b/net/openvswitch/flow_netlink.c
@@ -1432,7 +1432,13 @@ static int nsh_key_put_from_nlattr(const struct nlattr *attr,
 
 			has_md2 = true;
 			mdlen = nla_len(a);
-			if (mdlen > NSH_CTX_HDRS_MAX_LEN || mdlen <= 0) {
+			/* The NSH length field stores the total header size
+			 * in 4-byte words in 6 bits. Reject MD2 metadata
+			 * lengths that cannot be encoded exactly or would
+			 * make the length field wrap.
+			 */
+			if (mdlen <= 0 || !IS_ALIGNED(mdlen, 4) ||
+			    NSH_BASE_HDR_LEN + mdlen > (NSH_LEN_MASK << 2)) {
 				OVS_NLERR(
 				    log,
 				    "Invalid MD length %d for MD type %d",
-- 
2.43.0


^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox