Netdev List
 help / color / mirror / Atom feed
* Re: [PATCH net-next v2] net: mana: Add handler for sriov configure
From: Paolo Abeni @ 2026-07-08  8:08 UTC (permalink / raw)
  To: haiyangz
  Cc: linux-hyperv, netdev, kys, haiyangz, wei.liu, decui, longli,
	andrew+netdev, davem, edumazet, kuba, pabeni, horms, ernis,
	dipayanroy, gargaditya, shradhagupta, linux-kernel, paulros
In-Reply-To: <20260701180116.507690-1-haiyangz@linux.microsoft.com>

From: AI Reviewer <ai@example.com>

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/
---
net: mana: Add handler for sriov configure

Add callback function for the pci_driver / sriov_configure.

It asks the NIC to provide certain number of VFs, or disable
VFs if the request is zero.

> diff --git a/drivers/net/ethernet/microsoft/mana/gdma_main.c b/drivers/net/ethernet/microsoft/mana/gdma_main.c
> index e8b7ffb47eb98..f7a79874fd0eb 100644
> --- a/drivers/net/ethernet/microsoft/mana/gdma_main.c
> +++ b/drivers/net/ethernet/microsoft/mana/gdma_main.c
> @@ -2496,6 +2496,20 @@ static void mana_gd_shutdown(struct pci_dev *pdev)
>  	pci_disable_device(pdev);
>  }

[Severity: High]
With the addition of SR-IOV support in this patch, do mana_gd_shutdown() and
mana_gd_remove() need to call pci_disable_sriov()?

If a user enables VFs via sysfs and then unbinds the driver or shuts down the
system, could this omission introduce a regression where orphaned, dead VF PCI
devices are left in the kernel?

Accessing these orphaned VFs might trigger use-after-free crashes or hardware
faults, as the underlying PF hardware and driver context have been destroyed.

> +static int mana_sriov_configure(struct pci_dev *pdev, int numvfs)
> +{
> +	int err = 0;
> +
> +	dev_info(&pdev->dev, "Requested num VFs: %d\n", numvfs);
> +
> +	if (numvfs > 0)
> +		err = pci_enable_sriov(pdev, numvfs);
> +	else
> +		pci_disable_sriov(pdev);

[Severity: High]
Should this path check if the VFs are currently assigned to a guest VM before
disabling SR-IOV?

If a privileged user writes 0 to sriov_numvfs while VFs are actively assigned
to a VM (e.g., via vfio-pci), could forcefully removing the PCI devices cause
a regression resulting in host IOMMU faults and guest kernel panics?

Would it be better to check pci_vfs_assigned(pdev) here, or use the
pci_sriov_configure_simple() helper to safely enforce this?

> +
> +	return err ? err : numvfs;
> +}
-- 
This is an AI-generated review.


^ permalink raw reply

* Re: [PATCH net] net: mdio: select REGMAP_MMIO instead of depending on it
From: patchwork-bot+netdevbpf @ 2026-07-08  8:10 UTC (permalink / raw)
  To: Rosen Penev
  Cc: netdev, andrew, hkallweit1, linux, davem, edumazet, kuba, pabeni,
	dtcccc, linux-kernel
In-Reply-To: <20260702032653.1580616-1-rosenp@gmail.com>

Hello:

This patch was applied to netdev/net.git (main)
by Paolo Abeni <pabeni@redhat.com>:

On Wed,  1 Jul 2026 20:26:52 -0700 you wrote:
> REGMAP_MMIO is a hidden (non-user-visible) tristate symbol. Using
> depends on it is incorrect because there is no way for the user to
> enable it directly. Change to select, which is the convention used
> by every other driver in the tree that needs REGMAP_MMIO.
> 
> Fixes: 8057cbb8335c ("net: mdio: mscc-miim: Add depend of REGMAP_MMIO on MDIO_MSCC_MIIM")
> Assisted-by: opencode:big-pickle
> Signed-off-by: Rosen Penev <rosenp@gmail.com>
> 
> [...]

Here is the summary with links:
  - [net] net: mdio: select REGMAP_MMIO instead of depending on it
    https://git.kernel.org/netdev/net/c/1a3267a8c9ec

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



^ permalink raw reply

* Re: [PATCH net-next v3] selftests/net/openvswitch: add output truncation test
From: patchwork-bot+netdevbpf @ 2026-07-08  8:10 UTC (permalink / raw)
  To: Minxi Hou
  Cc: netdev, aconole, echaudro, i.maximets, davem, edumazet, kuba,
	pabeni, horms, shuah, dev, linux-kselftest
In-Reply-To: <20260702074926.1174810-1-houminxi@gmail.com>

Hello:

This patch was applied to netdev/net-next.git (main)
by Paolo Abeni <pabeni@redhat.com>:

On Thu,  2 Jul 2026 03:49:26 -0400 you wrote:
> Add test_trunc exercising the OVS_ACTION_ATTR_TRUNC action. The test
> verifies truncation limits in four steps: reject trunc(1) and
> trunc(13) which are below ETH_HLEN, confirm normal forwarding works,
> apply trunc(14) which truncates packets to the Ethernet header and
> verify ping fails, then restore normal forwarding and verify recovery.
> 
> The kernel requires max_len >= ETH_HLEN (14 bytes). trunc(14) sets
> OVS_CB(skb)->cutlen so pskb_trim strips the IP payload at output
> time; the receiver drops the runt frame and no echo reply is
> generated.
> 
> [...]

Here is the summary with links:
  - [net-next,v3] selftests/net/openvswitch: add output truncation test
    https://git.kernel.org/netdev/net-next/c/a211b03fee2c

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



^ permalink raw reply

* Re: [PATCH nf-next v4 5/6] net: netfilter: Add SIT tunnel flowtable acceleration
From: Lorenzo Bianconi @ 2026-07-08  8:17 UTC (permalink / raw)
  To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Felix Fietkau, Matthias Brugger,
	AngeloGioacchino Del Regno, Simon Horman, David Ahern,
	Ido Schimmel, Pablo Neira Ayuso, Florian Westphal, Phil Sutter,
	Shuah Khan
  Cc: linux-arm-kernel, linux-mediatek, netdev, netfilter-devel,
	coreteam, linux-kselftest
In-Reply-To: <20260703-b4-flowtable-sw-accel-ip6ip-v4-5-00398cd12382@kernel.org>

[-- Attachment #1: Type: text/plain, Size: 18900 bytes --]

> Introduce sw flowtable acceleration for the TX/RX paths of
> SIT tunnels, relying on the netfilter flowtable infrastructure.
> The feature can be tested with a forwarding scenario between two
> NICs (eth0 and eth1), where a SIT tunnel is used to reach a remote
> site via eth1 as the underlay device:
> 
>     ETH0 -- TUN0 <==> ETH1 -- [IP network] -- TUN1 (192.168.2.2)
> 
> [IP configuration]
> 
> 6: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
>     link/ether 00:00:22:33:11:55 brd ff:ff:ff:ff:ff:ff
>     inet6 2001:db8:1::2/64 scope global nodad
>        valid_lft forever preferred_lft forever
> 7: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
>     link/ether 00:11:22:33:11:55 brd ff:ff:ff:ff:ff:ff
>     inet 192.168.2.1/24 scope global eth1
>        valid_lft forever preferred_lft forever
> 8: tun0@NONE: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1480 qdisc noqueue state UNKNOWN group default qlen 1000
>     link/sit 192.168.2.1 peer 192.168.2.2
>     inet6 2001:db8:200::1/64 scope global nodad
>        valid_lft forever preferred_lft forever

commenting on sashiko's report:
https://sashiko.dev/#/patchset/20260703-b4-flowtable-sw-accel-ip6ip-v4-0-00398cd12382%40kernel.org

> 
> $ ip route show
> 192.168.2.0/24 dev eth1 proto kernel scope link src 192.168.2.1
> 
> $ ip -6 route show
> 2001:db8:1::/64 dev eth0 proto kernel metric 256 pref medium
> 2001:db8:200::/64 dev tun0 proto kernel metric 256 pref medium
> default via 2001:db8:200::2 dev tun0 metric 1024 pref medium
> 
> $ nft list ruleset
> table inet filter {
>     flowtable ft {
>         hook ingress priority filter
>         devices = { eth0, eth1 }
>     }
> 
>     chain forward {
>         type filter hook forward priority filter; policy accept;
>         meta l4proto { tcp, udp } flow add @ft
>     }
> }
> 
> When reproducing this scenario using veth interfaces, the following
> results were observed:
> 
> - TCP stream received from SIT tunnel:
>   - net-next (baseline):                ~118 Gbps
>   - net-next + SIT flowtable support: ~148 Gbps
> 
> - TCP stream transmitted to SIT tunnel:
>   - net-next (baseline):                ~131 Gbps
>   - net-next + SIT flowtable support: ~147 Gbps
> 
> Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
> ---
>  net/ipv6/sit.c                   |  26 ++++
>  net/netfilter/nf_flow_table_ip.c | 290 +++++++++++++++++++++------------------
>  2 files changed, 182 insertions(+), 134 deletions(-)
> 
> diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c
> index a38b24fb8384..0ac6f7839878 100644
> --- a/net/ipv6/sit.c
> +++ b/net/ipv6/sit.c
> @@ -1365,6 +1365,31 @@ ipip6_tunnel_ctl(struct net_device *dev, struct ip_tunnel_parm_kern *p,
>  	}
>  }
>  
> +static int ipip6_tunnel_fill_forward_path(struct net_device_path_ctx *ctx,
> +					  struct net_device_path *path)
> +{
> +	struct ip_tunnel *tunnel = netdev_priv(ctx->dev);
> +	const struct iphdr *tiph = &tunnel->parms.iph;
> +	struct rtable *rt;
> +
> +	rt = ip_route_output(dev_net(ctx->dev), tiph->daddr, 0, 0, 0,
> +			     RT_SCOPE_UNIVERSE);

- Does this handle NBMA (ISATAP/6to4/6rd) SIT tunnels correctly? For these
  tunnels, tiph->daddr is typically 0 since the remote destination is resolved
  per-packet.
  If we unconditionally query the routing table here with tiph->daddr, it
  will route to 0.0.0.0, causing the flow offload tuple to use 0.0.0.0 as the
  outer encapsulation destination. Could this result in all offloaded traffic
  for these tunnels being silently dropped?
  - I will fix it in v5

> +	if (IS_ERR(rt))
> +		return PTR_ERR(rt);
> +
> +	path->type = DEV_PATH_TUN;
> +	path->tun.src_v4.s_addr = tiph->saddr;
> +	path->tun.dst_v4.s_addr = tiph->daddr;
> +	path->tun.l3_proto = IPPROTO_IPV6;

- Is it safe to hardcode path->tun.l3_proto to IPPROTO_IPV6 here?
  SIT tunnels can also support IPv4 payloads. If an IPv4 flow is offloaded
  over a SIT tunnel, the flow tuple will erroneously record the payload as
  IPv6. During encapsulation, nf_flow_tunnel_ipip_push() might then parse
  the inner IPv4 header as an IPv6 header, which could corrupt TOS and TTL
  extraction.
  - I will fix it in v5

> +	path->tun.encap_proto = AF_INET;
> +	path->dev = ctx->dev;
> +
> +	ctx->dev = rt->dst.dev;
> +	ip_rt_put(rt);
> +
> +	return 0;
> +}
> +
>  static int
>  ipip6_tunnel_siocdevprivate(struct net_device *dev, struct ifreq *ifr,
>  			    void __user *data, int cmd)
> @@ -1401,6 +1426,7 @@ static const struct net_device_ops ipip6_netdev_ops = {
>  	.ndo_siocdevprivate = ipip6_tunnel_siocdevprivate,
>  	.ndo_get_iflink = ip_tunnel_get_iflink,
>  	.ndo_tunnel_ctl = ipip6_tunnel_ctl,
> +	.ndo_fill_forward_path = ipip6_tunnel_fill_forward_path,
>  };
>  
>  static void ipip6_dev_free(struct net_device *dev)
> diff --git a/net/netfilter/nf_flow_table_ip.c b/net/netfilter/nf_flow_table_ip.c
> index 4b6de16bd4f3..f02e71ae5448 100644
> --- a/net/netfilter/nf_flow_table_ip.c
> +++ b/net/netfilter/nf_flow_table_ip.c
> @@ -338,7 +338,7 @@ static bool nf_flow_ip4_tunnel_proto(struct nf_flowtable_ctx *ctx,
>  	if (iph->ttl <= 1)
>  		return false;
>  
> -	if (iph->protocol == IPPROTO_IPIP) {
> +	if (iph->protocol == IPPROTO_IPIP || iph->protocol == IPPROTO_IPV6) {
>  		ctx->tun.proto = iph->protocol;
>  		ctx->tun.hdr_size = size;
>  		ctx->offset += ctx->tun.hdr_size;
> @@ -464,21 +464,6 @@ static void nf_flow_encap_pop(struct nf_flowtable_ctx *ctx,
>  		nf_flow_ip_tunnel_pop(ctx, skb);
>  }
>  
> -static struct flow_offload_tuple_rhash *
> -nf_flow_offload_lookup(struct nf_flowtable_ctx *ctx,
> -		       struct nf_flowtable *flow_table, struct sk_buff *skb)
> -{
> -	struct flow_offload_tuple tuple = {};
> -
> -	if (!nf_flow_skb_encap_protocol(ctx, skb, htons(ETH_P_IP)))
> -		return NULL;
> -
> -	if (nf_flow_tuple_ip(ctx, skb, &tuple) < 0)
> -		return NULL;
> -
> -	return flow_offload_lookup(flow_table, &tuple);
> -}
> -
>  static int nf_flow_offload_forward(struct nf_flowtable_ctx *ctx,
>  				   struct nf_flowtable *flow_table,
>  				   struct flow_offload_tuple_rhash *tuplehash,
> @@ -606,19 +591,33 @@ static int nf_flow_tunnel_ipip_push(struct net *net, struct sk_buff *skb,
>  				    struct flow_offload_tuple *tuple,
>  				    __be32 *ip_daddr)
>  {
> -	struct iphdr *iph = (struct iphdr *)skb_network_header(skb);
>  	struct rtable *rt = dst_rtable(tuple->dst_cache);
> -	u8 tos = iph->tos, ttl = iph->ttl;
> -	__be16 frag_off = iph->frag_off;
> -	u32 headroom = sizeof(*iph);
> +	__be16 frag_off = 0;
> +	struct iphdr *iph;
> +	u8 tos = 0, ttl;
> +	u32 headroom;
>  	int err;
>  
> +	if (tuple->tun.l3_proto == IPPROTO_IPV6) {
> +		struct ipv6hdr *ip6h;
> +
> +		ip6h = (struct ipv6hdr *)skb_network_header(skb);
> +		tos = ipv6_get_dsfield(ip6h);
> +		ttl = ip6h->hop_limit;
> +	} else {
> +		iph = (struct iphdr *)skb_network_header(skb);
> +		frag_off = iph->frag_off;
> +		tos = iph->tos;
> +		ttl = iph->ttl;
> +	}
> +
>  	err = iptunnel_handle_offloads(skb, SKB_GSO_IPXIP4);
>  	if (err)
>  		return err;
>  
> -	skb_set_inner_ipproto(skb, IPPROTO_IPIP);
> -	headroom += LL_RESERVED_SPACE(rt->dst.dev) + rt->dst.header_len;
> +	skb_set_inner_ipproto(skb, tuple->tun.l3_proto);
> +	headroom = sizeof(*iph) + LL_RESERVED_SPACE(rt->dst.dev) +
> +		   rt->dst.header_len;

- This isn't a bug introduced by this patch, but is it correct to calculate
  the headroom using rt->dst.dev?
  The rt variable is derived from the reply direction tuple, so rt->dst.dev
  points to the internal ingress device (e.g. eth0) rather than the egress
  underlay device (e.g. eth1).
  If the physical egress device requires more headroom than the internal
  device, this allocation might be too small and trigger an skb_under_panic
  during encapsulation.
  - This is already fixed by a Pablo's fix

>  	err = skb_cow_head(skb, headroom);
>  	if (err)
>  		return err;
> @@ -629,6 +628,7 @@ static int nf_flow_tunnel_ipip_push(struct net *net, struct sk_buff *skb,
>  	/* Push down and install the IP header. */
>  	skb_push(skb, sizeof(*iph));
>  	skb_reset_network_header(skb);
> +	skb->protocol = htons(ETH_P_IP);
>  
>  	iph = ip_hdr(skb);
>  	iph->version	= 4;
> @@ -723,16 +723,6 @@ static int nf_flow_tunnel_push(struct net *net, struct sk_buff *skb,
>  	}
>  }
>  
> -static int nf_flow_tunnel_v6_push(struct net *net, struct sk_buff *skb,
> -				  struct flow_offload_tuple *tuple,
> -				  struct in6_addr **ip6_daddr)
> -{
> -	if (tuple->tun_num)
> -		return nf_flow_tunnel_ip6ip6_push(net, skb, tuple, ip6_daddr);
> -
> -	return 0;
> -}
> -
>  static int nf_flow_encap_push(struct sk_buff *skb,
>  			      struct flow_offload_tuple *tuple,
>  			      struct net_device *outdev)
> @@ -830,103 +820,6 @@ static unsigned int nf_flow_queue_xmit(struct net *net, struct sk_buff *skb,
>  	return NF_STOLEN;
>  }
>  
> -unsigned int
> -nf_flow_offload_ip_hook(void *priv, struct sk_buff *skb,
> -			const struct nf_hook_state *state)
> -{
> -	struct flow_offload_tuple_rhash *tuplehash;
> -	struct nf_flowtable *flow_table = priv;
> -	struct flow_offload_tuple *other_tuple;
> -	enum flow_offload_tuple_dir dir;
> -	struct nf_flowtable_ctx ctx = {
> -		.in	= state->in,
> -	};
> -	struct nf_flow_xmit xmit = {};
> -	struct in6_addr *ip6_daddr;
> -	struct flow_offload *flow;
> -	struct neighbour *neigh;
> -	struct rtable *rt;
> -	__be32 ip_daddr;
> -	int ret;
> -
> -	tuplehash = nf_flow_offload_lookup(&ctx, flow_table, skb);
> -	if (!tuplehash)
> -		return NF_ACCEPT;
> -
> -	ret = nf_flow_offload_forward(&ctx, flow_table, tuplehash, skb);
> -	if (ret < 0)
> -		return NF_DROP;
> -	else if (ret == 0)
> -		return NF_ACCEPT;
> -
> -	if (unlikely(tuplehash->tuple.xmit_type == FLOW_OFFLOAD_XMIT_XFRM)) {
> -		rt = dst_rtable(tuplehash->tuple.dst_cache);
> -		memset(skb->cb, 0, sizeof(struct inet_skb_parm));
> -		IPCB(skb)->iif = skb->dev->ifindex;
> -		IPCB(skb)->flags = IPSKB_FORWARDED;
> -		return nf_flow_xmit_xfrm(skb, state, &rt->dst);

- Since ctx.tun.proto can now be IPPROTO_IPV6 for an inner IPv6 packet
  encapsulated in IPv4, is it safe to unconditionally cast the route to
  rtable and initialize IPCB(skb) here?
  - This is not a real problem since IP tunnel flowtable acceleration is not
    supported for FLOW_OFFLOAD_XMIT_XFRM xmit_type.

Regards,
Lorenzo

> -	}
> -
> -	dir = tuplehash->tuple.dir;
> -	flow = container_of(tuplehash, struct flow_offload, tuplehash[dir]);
> -	other_tuple = &flow->tuplehash[!dir].tuple;
> -	ip_daddr = other_tuple->src_v4.s_addr;
> -
> -	if (nf_flow_tunnel_push(state->net, skb, other_tuple, &ip_daddr,
> -				&ip6_daddr) < 0)
> -		return NF_DROP;
> -
> -	switch (tuplehash->tuple.xmit_type) {
> -	case FLOW_OFFLOAD_XMIT_NEIGH: {
> -		struct dst_entry *dst;
> -
> -		xmit.outdev = dev_get_by_index_rcu(state->net, tuplehash->tuple.ifidx);
> -		if (!xmit.outdev) {
> -			flow_offload_teardown(flow);
> -			return NF_DROP;
> -		}
> -		if (other_tuple->tun.encap_proto == AF_INET6 ||
> -		    ctx.tun.proto == IPPROTO_IPV6) {
> -			struct rt6_info *rt6;
> -
> -			rt6 = dst_rt6_info(tuplehash->tuple.dst_cache);
> -			neigh = ip_neigh_gw6(rt6->dst.dev,
> -					     rt6_nexthop(rt6, ip6_daddr));
> -			dst = &rt6->dst;
> -		} else {
> -			rt = dst_rtable(tuplehash->tuple.dst_cache);
> -			neigh = ip_neigh_gw4(rt->dst.dev,
> -					     rt_nexthop(rt, ip_daddr));
> -			dst = &rt->dst;
> -		}
> -		if (IS_ERR(neigh)) {
> -			flow_offload_teardown(flow);
> -			return NF_DROP;
> -		}
> -		xmit.dest = neigh->ha;
> -		skb_dst_set_noref(skb, dst);
> -		break;
> -	}
> -	case FLOW_OFFLOAD_XMIT_DIRECT:
> -		xmit.outdev = dev_get_by_index_rcu(state->net, tuplehash->tuple.out.ifidx);
> -		if (!xmit.outdev) {
> -			flow_offload_teardown(flow);
> -			return NF_DROP;
> -		}
> -		xmit.dest = tuplehash->tuple.out.h_dest;
> -		xmit.source = tuplehash->tuple.out.h_source;
> -		break;
> -	default:
> -		WARN_ON_ONCE(1);
> -		return NF_DROP;
> -	}
> -	xmit.tuple = other_tuple;
> -	xmit.needs_gso_segment = tuplehash->tuple.needs_gso_segment;
> -
> -	return nf_flow_queue_xmit(state->net, skb, &xmit);
> -}
> -EXPORT_SYMBOL_GPL(nf_flow_offload_ip_hook);
> -
>  static void nf_flow_nat_ipv6_tcp(struct sk_buff *skb, unsigned int thoff,
>  				 struct in6_addr *addr,
>  				 struct in6_addr *new_addr,
> @@ -1111,8 +1004,16 @@ static int nf_flow_offload_ipv6_forward(struct nf_flowtable_ctx *ctx,
>  	flow = container_of(tuplehash, struct flow_offload, tuplehash[dir]);
>  
>  	mtu = flow->tuplehash[dir].tuple.mtu + ctx->offset;
> -	if (flow->tuplehash[!dir].tuple.tun_num)
> +	switch (flow->tuplehash[!dir].tuple.tun.encap_proto) {
> +	case AF_INET:
> +		mtu -= sizeof(struct iphdr);
> +		break;
> +	case AF_INET6:
>  		mtu -= sizeof(*ip6h);
> +		break;
> +	default:
> +		break;
> +	}
>  
>  	if (unlikely(nf_flow_exceeds_mtu(skb, mtu)))
>  		return 0;
> @@ -1146,6 +1047,25 @@ static int nf_flow_offload_ipv6_forward(struct nf_flowtable_ctx *ctx,
>  	return 1;
>  }
>  
> +static struct flow_offload_tuple_rhash *
> +nf_flow_offload_lookup(struct nf_flowtable_ctx *ctx,
> +		       struct nf_flowtable *flow_table, struct sk_buff *skb)
> +{
> +	struct flow_offload_tuple tuple = {};
> +
> +	if (!nf_flow_skb_encap_protocol(ctx, skb, htons(ETH_P_IP)))
> +		return NULL;
> +
> +	if (ctx->tun.proto == IPPROTO_IPV6) {
> +		if (nf_flow_tuple_ipv6(ctx, skb, &tuple) < 0)
> +			return NULL;
> +	} else if (nf_flow_tuple_ip(ctx, skb, &tuple) < 0) {
> +		return NULL;
> +	}
> +
> +	return flow_offload_lookup(flow_table, &tuple);
> +}
> +
>  static struct flow_offload_tuple_rhash *
>  nf_flow_offload_ipv6_lookup(struct nf_flowtable_ctx *ctx,
>  			    struct nf_flowtable *flow_table,
> @@ -1166,6 +1086,108 @@ nf_flow_offload_ipv6_lookup(struct nf_flowtable_ctx *ctx,
>  	return flow_offload_lookup(flow_table, &tuple);
>  }
>  
> +unsigned int
> +nf_flow_offload_ip_hook(void *priv, struct sk_buff *skb,
> +			const struct nf_hook_state *state)
> +{
> +	struct flow_offload_tuple_rhash *tuplehash;
> +	struct nf_flowtable *flow_table = priv;
> +	struct flow_offload_tuple *other_tuple;
> +	enum flow_offload_tuple_dir dir;
> +	struct nf_flowtable_ctx ctx = {
> +		.in	= state->in,
> +	};
> +	struct nf_flow_xmit xmit = {};
> +	struct in6_addr *ip6_daddr;
> +	struct flow_offload *flow;
> +	struct neighbour *neigh;
> +	struct rtable *rt;
> +	__be32 ip_daddr;
> +	int ret;
> +
> +	tuplehash = nf_flow_offload_lookup(&ctx, flow_table, skb);
> +	if (!tuplehash)
> +		return NF_ACCEPT;
> +
> +	if (ctx.tun.proto == IPPROTO_IPV6)
> +		ret = nf_flow_offload_ipv6_forward(&ctx, flow_table, tuplehash,
> +						   skb);
> +	else
> +		ret = nf_flow_offload_forward(&ctx, flow_table, tuplehash, skb);
> +	if (ret < 0)
> +		return NF_DROP;
> +	else if (ret == 0)
> +		return NF_ACCEPT;
> +
> +	if (unlikely(tuplehash->tuple.xmit_type == FLOW_OFFLOAD_XMIT_XFRM)) {
> +		rt = dst_rtable(tuplehash->tuple.dst_cache);
> +		memset(skb->cb, 0, sizeof(struct inet_skb_parm));
> +		IPCB(skb)->iif = skb->dev->ifindex;
> +		IPCB(skb)->flags = IPSKB_FORWARDED;
> +		return nf_flow_xmit_xfrm(skb, state, &rt->dst);
> +	}
> +
> +	dir = tuplehash->tuple.dir;
> +	flow = container_of(tuplehash, struct flow_offload, tuplehash[dir]);
> +	other_tuple = &flow->tuplehash[!dir].tuple;
> +	ip_daddr = other_tuple->src_v4.s_addr;
> +	ip6_daddr = &other_tuple->src_v6;
> +
> +	if (nf_flow_tunnel_push(state->net, skb, other_tuple, &ip_daddr,
> +				&ip6_daddr) < 0)
> +		return NF_DROP;
> +
> +	switch (tuplehash->tuple.xmit_type) {
> +	case FLOW_OFFLOAD_XMIT_NEIGH: {
> +		struct dst_entry *dst;
> +
> +		xmit.outdev = dev_get_by_index_rcu(state->net, tuplehash->tuple.ifidx);
> +		if (!xmit.outdev) {
> +			flow_offload_teardown(flow);
> +			return NF_DROP;
> +		}
> +		if (other_tuple->tun.encap_proto == AF_INET6 ||
> +		    ctx.tun.proto == IPPROTO_IPV6) {
> +			struct rt6_info *rt6;
> +
> +			rt6 = dst_rt6_info(tuplehash->tuple.dst_cache);
> +			neigh = ip_neigh_gw6(rt6->dst.dev,
> +					     rt6_nexthop(rt6, ip6_daddr));
> +			dst = &rt6->dst;
> +		} else {
> +			rt = dst_rtable(tuplehash->tuple.dst_cache);
> +			neigh = ip_neigh_gw4(rt->dst.dev,
> +					     rt_nexthop(rt, ip_daddr));
> +			dst = &rt->dst;
> +		}
> +		if (IS_ERR(neigh)) {
> +			flow_offload_teardown(flow);
> +			return NF_DROP;
> +		}
> +		xmit.dest = neigh->ha;
> +		skb_dst_set_noref(skb, dst);
> +		break;
> +	}
> +	case FLOW_OFFLOAD_XMIT_DIRECT:
> +		xmit.outdev = dev_get_by_index_rcu(state->net, tuplehash->tuple.out.ifidx);
> +		if (!xmit.outdev) {
> +			flow_offload_teardown(flow);
> +			return NF_DROP;
> +		}
> +		xmit.dest = tuplehash->tuple.out.h_dest;
> +		xmit.source = tuplehash->tuple.out.h_source;
> +		break;
> +	default:
> +		WARN_ON_ONCE(1);
> +		return NF_DROP;
> +	}
> +	xmit.tuple = other_tuple;
> +	xmit.needs_gso_segment = tuplehash->tuple.needs_gso_segment;
> +
> +	return nf_flow_queue_xmit(state->net, skb, &xmit);
> +}
> +EXPORT_SYMBOL_GPL(nf_flow_offload_ip_hook);
> +
>  unsigned int
>  nf_flow_offload_ipv6_hook(void *priv, struct sk_buff *skb,
>  			  const struct nf_hook_state *state)
> @@ -1182,6 +1204,7 @@ nf_flow_offload_ipv6_hook(void *priv, struct sk_buff *skb,
>  	struct flow_offload *flow;
>  	struct neighbour *neigh;
>  	struct rt6_info *rt;
> +	__be32 ip_daddr;
>  	int ret;
>  
>  	tuplehash = nf_flow_offload_ipv6_lookup(&ctx, flow_table, skb);
> @@ -1209,10 +1232,11 @@ nf_flow_offload_ipv6_hook(void *priv, struct sk_buff *skb,
>  	dir = tuplehash->tuple.dir;
>  	flow = container_of(tuplehash, struct flow_offload, tuplehash[dir]);
>  	other_tuple = &flow->tuplehash[!dir].tuple;
> +	ip_daddr = other_tuple->src_v4.s_addr;
>  	ip6_daddr = &other_tuple->src_v6;
>  
> -	if (nf_flow_tunnel_v6_push(state->net, skb, other_tuple,
> -				   &ip6_daddr) < 0)
> +	if (nf_flow_tunnel_push(state->net, skb, other_tuple, &ip_daddr,
> +				&ip6_daddr) < 0)
>  		return NF_DROP;
>  
>  	switch (tuplehash->tuple.xmit_type) {
> @@ -1226,10 +1250,8 @@ nf_flow_offload_ipv6_hook(void *priv, struct sk_buff *skb,
>  		}
>  		if (other_tuple->tun.encap_proto == AF_INET ||
>  		    ctx.tun.proto == IPPROTO_IPIP) {
> -			__be32 ip_daddr = other_tuple->src_v4.s_addr;
>  			struct rtable *rt4;
>  
> -			skb->protocol = htons(ETH_P_IP);
>  			rt4 = dst_rtable(tuplehash->tuple.dst_cache);
>  			neigh = ip_neigh_gw4(rt4->dst.dev,
>  					     rt_nexthop(rt4, ip_daddr));
> 
> -- 
> 2.55.0
> 

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply

* Re: [PATCH net-next] gtp: annotate PDP lookups under RTNL
From: patchwork-bot+netdevbpf @ 2026-07-08  8:20 UTC (permalink / raw)
  To: Runyu Xiao
  Cc: pablo, laforge, andrew+netdev, davem, edumazet, kuba, pabeni,
	osmocom-net-gprs, netdev, linux-kernel, jianhao.xu
In-Reply-To: <20260701123925.3193089-1-runyu.xiao@seu.edu.cn>

Hello:

This patch was applied to netdev/net-next.git (main)
by Paolo Abeni <pabeni@redhat.com>:

On Wed,  1 Jul 2026 20:39:25 +0800 you wrote:
> The GTP PDP lookup helpers are shared by RCU-protected data and report
> paths and RTNL-protected control paths such as gtp_genl_new_pdp(). The
> helpers walk RCU hlists, but they do not currently pass the RTNL
> condition for the control-path lookups.
> 
> Pass lockdep_rtnl_is_held() to the PDP hlist iterators. Existing
> RCU-reader callers remain valid because the RCU-list macros also accept
> an active RCU read-side section; the added condition only documents the
> non-RCU protection already used by RTNL control paths.
> 
> [...]

Here is the summary with links:
  - [net-next] gtp: annotate PDP lookups under RTNL
    https://git.kernel.org/netdev/net-next/c/0be5c3f0fbef

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



^ permalink raw reply

* Re: [PATCH net-next v3] net: skb: isolate skb data area allocations into a separate bucket
From: Paolo Abeni @ 2026-07-08  8:30 UTC (permalink / raw)
  To: Pedro Falcato, David S. Miller, Eric Dumazet, Jakub Kicinski
  Cc: Simon Horman, Jason Xing, Kuniyuki Iwashima, netdev, linux-kernel,
	linux-hardening, Kees Cook
In-Reply-To: <20260702170728.168755-1-pfalcato@suse.de>

On 7/2/26 7:07 PM, Pedro Falcato wrote:> @@ -586,6 +586,8 @@ struct
sk_buff *napi_build_skb(void *data, unsigned int frag_size)
>  }
>  EXPORT_SYMBOL(napi_build_skb);
>  
> +static kmem_buckets *skb_data_buckets __ro_after_init;
> +
>  static void *kmalloc_pfmemalloc(size_t obj_size, gfp_t flags, int node)
>  {
>  	if (!gfp_pfmemalloc_allowed(flags))
> @@ -593,7 +595,8 @@ static void *kmalloc_pfmemalloc(size_t obj_size, gfp_t flags, int node)
>  	if (!obj_size)
>  		return kmem_cache_alloc_node(net_hotdata.skb_small_head_cache,
>  					     flags, node);
> -	return kmalloc_node_track_caller(obj_size, flags, node);
> +	return kmem_buckets_alloc_node_track_caller(skb_data_buckets, obj_size,
> +						    flags, node);

Sashiko noted that some drivers may require GFP_DMA buckets, and the
above may break them:

https://sashiko.dev/#/patchset/20260702170728.168755-1-pfalcato%40suse.de

>  }
>  
>  /*
> @@ -634,7 +637,7 @@ static void *kmalloc_reserve(unsigned int *size, gfp_t flags, int node,
>  	 * Try a regular allocation, when that fails and we're not entitled
>  	 * to the reserves, fail.
>  	 */
> -	obj = kmalloc_node_track_caller(obj_size,
> +	obj = kmem_buckets_alloc_node_track_caller(skb_data_buckets, obj_size,
>  					flags | __GFP_NOMEMALLOC | __GFP_NOWARN,
>  					node);

Minor nit: checkpatch laments WRT brackets alignment.

/P


^ permalink raw reply

* Re: [PATCH net-next V5 6/6] net/mlx5: Apply devlink eswitch mode boot default on probe
From: Jiri Pirko @ 2026-07-08  8:34 UTC (permalink / raw)
  To: Mark Bloch
  Cc: Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
	Saeed Mahameed, Leon Romanovsky, Tariq Toukan, Andrew Lunn,
	Jonathan Corbet, Shuah Khan, netdev, linux-rdma, linux-doc
In-Reply-To: <20260707174527.425134-7-mbloch@nvidia.com>

Tue, Jul 07, 2026 at 07:45:27PM +0200, mbloch@nvidia.com wrote:
>Apply devlink_eswitch_mode= boot defaults for mlx5 after the initial
>probe finishes device initialization while holding the devlink instance
>lock.
>
>At this point the devlink instance is registered and mlx5 can perform an
>eswitch mode change. Calling devl_apply_default_esw_mode() also clears
>any pending default apply work queued by devl_register(), so the queued
>work will not apply the same default again.
>
>Keep this call in mlx5_init_one() rather than the lower-level
>devl-locked init helper. That helper is also used by devlink reload, and
>devlink core already applies the boot default after a successful
>DRIVER_REINIT reload.
>
>Signed-off-by: Mark Bloch <mbloch@nvidia.com>
>---
> drivers/net/ethernet/mellanox/mlx5/core/main.c | 13 +++++++++++++
> 1 file changed, 13 insertions(+)
>
>diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c
>index 643b4aac2033..0712efea74cc 100644
>--- a/drivers/net/ethernet/mellanox/mlx5/core/main.c
>+++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c
>@@ -1392,6 +1392,17 @@ static void mlx5_unload(struct mlx5_core_dev *dev)
> 	mlx5_free_bfreg(dev, &dev->priv.bfreg);
> }
> 
>+static void mlx5_devl_apply_default_esw_mode(struct mlx5_core_dev *dev)
>+{
>+	struct devlink *devlink = priv_to_devlink(dev);
>+
>+	if (!MLX5_ESWITCH_MANAGER(dev))
>+		return;
>+
>+	devl_assert_locked(devlink);
>+	devl_apply_default_esw_mode(devlink);
>+}
>+
> int mlx5_init_one_devl_locked(struct mlx5_core_dev *dev)
> {
> 	bool light_probe = mlx5_dev_is_lightweight(dev);
>@@ -1471,6 +1482,8 @@ int mlx5_init_one(struct mlx5_core_dev *dev)
> 	err = mlx5_init_one_devl_locked(dev);
> 	if (err)
> 		devl_unregister(devlink);
>+	else
>+		mlx5_devl_apply_default_esw_mode(dev);

I don't understand why this patch is needed at all. Just leave the job
to the devlink core, no? That was the point to not pollute drivers with
code like this. Is it some kind of leftover?



> unlock:
> 	devl_unlock(devlink);
> 	return err;
>-- 
>2.43.0
>

^ permalink raw reply

* Re: [PATCH net-next] net: rmnet: annotate endpoint lookup under RTNL
From: patchwork-bot+netdevbpf @ 2026-07-08  8:40 UTC (permalink / raw)
  To: Runyu Xiao
  Cc: subash.a.kasiviswanathan, sean.tranchetti, andrew+netdev, davem,
	edumazet, kuba, pabeni, netdev, linux-kernel, jianhao.xu
In-Reply-To: <20260701124017.3205729-1-runyu.xiao@seu.edu.cn>

Hello:

This patch was applied to netdev/net-next.git (main)
by Paolo Abeni <pabeni@redhat.com>:

On Wed,  1 Jul 2026 20:40:17 +0800 you wrote:
> rmnet_get_endpoint() is shared by packet receive paths and
> RTNL-protected control paths. The receive paths already run under RCU/BH
> context through the RX handler, while the control paths reach
> rmnet_get_endpoint() after obtaining the rmnet port with
> rmnet_get_port_rtnl().
> 
> The helper walks port->muxed_ep[] with hlist_for_each_entry_rcu(). Pass
> lockdep_rtnl_is_held() as the non-RCU protection condition so
> CONFIG_PROVE_RCU_LIST can see the RTNL-protected control-path calls
> while preserving the existing RCU-reader behavior for data paths.
> 
> [...]

Here is the summary with links:
  - [net-next] net: rmnet: annotate endpoint lookup under RTNL
    https://git.kernel.org/netdev/net-next/c/4fa0619d039f

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



^ permalink raw reply

* Re: [PATCH net-next] gtp: annotate PDP lookups under RTNL
From: Pablo Neira Ayuso @ 2026-07-08  8:42 UTC (permalink / raw)
  To: patchwork-bot+netdevbpf
  Cc: Runyu Xiao, laforge, andrew+netdev, davem, edumazet, kuba, pabeni,
	osmocom-net-gprs, netdev, linux-kernel, jianhao.xu
In-Reply-To: <178349880988.2367097.8758757633307166154.git-patchwork-notify@kernel.org>

Hi,

On Wed, Jul 08, 2026 at 08:20:09AM +0000, patchwork-bot+netdevbpf@kernel.org wrote:
> Hello:
> 
> This patch was applied to netdev/net-next.git (main)
> by Paolo Abeni <pabeni@redhat.com>:
> 
> On Wed,  1 Jul 2026 20:39:25 +0800 you wrote:
> > The GTP PDP lookup helpers are shared by RCU-protected data and report
> > paths and RTNL-protected control paths such as gtp_genl_new_pdp(). The
> > helpers walk RCU hlists, but they do not currently pass the RTNL
> > condition for the control-path lookups.
> > 
> > Pass lockdep_rtnl_is_held() to the PDP hlist iterators. Existing
> > RCU-reader callers remain valid because the RCU-list macros also accept
> > an active RCU read-side section; the added condition only documents the
> > non-RCU protection already used by RTNL control paths.
> > 
> > [...]
> 
> Here is the summary with links:
>   - [net-next] gtp: annotate PDP lookups under RTNL
>     https://git.kernel.org/netdev/net-next/c/0be5c3f0fbef

For the record, I think this patch was not OK.

PDP context hashtable is protected by the genl_mutex, not rtnl.

If I'm mistaken, please let know, thanks!

^ permalink raw reply

* Re: [PATCH 0/4] drivers/net/ethernet: replace __get_free_pages() with kmalloc()
From: patchwork-bot+netdevbpf @ 2026-07-08  8:50 UTC (permalink / raw)
  To: Mike Rapoport
  Cc: andrew+netdev, davem, edumazet, kuba, manishc, pabeni,
	ecree.xilinx, przemyslaw.kitszel, skalluru, anthony.l.nguyen,
	intel-wired-lan, linux-kernel, linux-mm, linux-net-drivers,
	netdev
In-Reply-To: <20260701-b4-drivers-ethernet-v1-0-58776615db6e@kernel.org>

Hello:

This series was applied to netdev/net-next.git (main)
by Paolo Abeni <pabeni@redhat.com>:

On Wed, 01 Jul 2026 16:57:17 +0300 you wrote:
> This is a (small) part of larger work of replacing page allocator calls
> with kmalloc.
> 
> My initial intention a few month ago was to remove ugly casts [1], but then
> willy pointed out that Linus objected to something like this [2] and it
> looks like more than a decade old technical debt.
> 
> [...]

Here is the summary with links:
  - [1/4] bnx2x: use kzalloc() to allocate mac filtering list
    https://git.kernel.org/netdev/net-next/c/28a236c54c9a
  - [2/4] ice: use kzalloc() to allocate staging buffer for reading from GNSS
    https://git.kernel.org/netdev/net-next/c/764f2a0c6d1e
  - [3/4] sfc/siena: use kmalloc() to allocate logging buffer
    https://git.kernel.org/netdev/net-next/c/50bfc5eac4cb
  - [4/4] sfc: use kmalloc() to allocate logging buffer
    https://git.kernel.org/netdev/net-next/c/e8cfc70720ea

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



^ permalink raw reply

* Re: [PATCH v5 net 0/7] i40e: re-init and UAF fixes
From: Paolo Abeni @ 2026-07-08  8:51 UTC (permalink / raw)
  To: Maciej Fijalkowski, intel-wired-lan
  Cc: netdev, magnus.karlsson, kuba, horms, przemyslaw.kitszel,
	jacob.e.keller
In-Reply-To: <20260701124524.13644-1-maciej.fijalkowski@intel.com>

On 7/1/26 2:45 PM, Maciej Fijalkowski wrote:
> v5:
> - include three new patches to address last Sashiko review
>   *
> - do not release the irq lump in rebuild path in patch 7
> - clear dangling pointers from rx and xdp rings arrays
> v4:
> - add preceding patch that fixes a case when some of re-init allocations
>   failed and we missed de-registering netdev at failure path
> - pull out i40e_vsi_setup() changes onto separate patch
> v3:
> - address UAF when ring arrays were freed before q_vector's ring
>   containers (Sashiko, Jacob)
> - remove bool params from alloc/free array routines (Simon)
> v2:
> - NULL vsi->tx_rings in i40e_vsi_alloc_arrays() (Sashiko)

FTR, I'm not 100% sure about the actual target here: the subj prefix
point to the net tree, but the to recipient is IWL.

Since this is not sent by Tony, I assume the real target is IWL and
tentatively set the series to 'Awaiting upstream'. Note that for 'net'
inclusion you will need an actual, not empty, cover letter.

/P


^ permalink raw reply

* Re: [PATCH nf v2] ipvs: make destination flags atomic
From: Vadim Fedorenko @ 2026-07-08  8:53 UTC (permalink / raw)
  To: Yizhou Zhao, Simon Horman, Julian Anastasov, David Ahern,
	Ido Schimmel, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Pablo Neira Ayuso, Florian Westphal, Phil Sutter,
	Alexander Frolkin
  Cc: netdev, lvs-devel, linux-kernel, netfilter-devel, coreteam,
	stable, Yuxiang Yang, Ao Wang, Xuewei Feng, Qi Li, Ke Xu
In-Reply-To: <20260708060454.20534-1-zhaoyz24@mails.tsinghua.edu.cn>

On 08/07/2026 07:04, Yizhou Zhao wrote:
> IPVS destination schedulers read dest->flags from packet processing paths
> while holding only the RCU read lock.  The same word is updated by plain
> read-modify-write operations from connection accounting and destination
> update paths, for example ip_vs_bind_dest(), ip_vs_unbind_dest(), and
> __ip_vs_update_dest().
> 
> The RCU read lock protects the destination lifetime, but it does not
> serialize accesses to dest->flags.  A plain load can therefore race with a
> plain write, and concurrent plain read-modify-write updates can lose an
> AVAILABLE or OVERLOAD bit update.
> 
> KCSAN reports the race with a standard IPVS configuration using the SH
> scheduler and a destination with u_threshold set:
> 
>    BUG: KCSAN: data-race in __ip_vs_update_dest / ip_vs_sh_schedule
>    write to ... of 4 bytes by task ipvs_cfg:
>      __ip_vs_update_dest
>      ip_vs_edit_dest
>      do_ip_vs_set_ctl
>      __x64_sys_setsockopt
>    read to ... of 4 bytes by task ipvs_churn:
>      ip_vs_sh_schedule
>      ip_vs_schedule
>      tcp_conn_schedule
>      ip_vs_in_hook
>      tcp_connect
>      __x64_sys_connect
>    value changed: 0x00000003 -> 0x00000001
> 
> Convert dest->flags to atomic_t and use atomic_read(), atomic_or(), and
> atomic_and() for all destination flag tests and updates.  This preserves
> the existing 32-bit field size while making the flag updates atomic RMW
> operations and making readers use atomic accesses.  Valid minimum-sized
> IPVS configuration and scheduling paths are unchanged; only the
> synchronization of the destination status flags changes.
> 
> This is limited to synchronizing the flags word itself.  It does not add
> ordering for readers, and it does not make scheduler decisions operate on a
> fresh snapshot of all destination state; readers may still observe stale
> state in the usual IPVS fast path. This keeps the packet fast path free
> of additional barriers or locks.
> 
> Fixes: eba3b5a78799d ("ipvs: SH fallback and L4 hashing")
> Cc: stable@vger.kernel.org
> Reported-by: Yizhou Zhao <zhaoyz24@mails.tsinghua.edu.cn>
> Reported-by: Yuxiang Yang <yangyx22@mails.tsinghua.edu.cn>
> Reported-by: Ao Wang <wangao@seu.edu.cn>
> Reported-by: Xuewei Feng <fengxw06@126.com>
> Reported-by: Qi Li <qli01@tsinghua.edu.cn>
> Reported-by: Ke Xu <xuke@tsinghua.edu.cn>
> Assisted-by: Claude-Code:GLM-5.2
> Signed-off-by: Yizhou Zhao <zhaoyz24@mails.tsinghua.edu.cn>
> ---
> Changes in v2:
> - Clarify that the patch fixes the flags data race and RMW lost updates,
>    but does not prevent readers from observing stale scheduling state.
> - Fix checkpatch logical-continuation warnings.
> - Suggested by Julian Anastasov.
> - Link to v1: https://lore.kernel.org/netfilter-devel/20260707085706.96322-1-zhaoyz24@mails.tsinghua.edu.cn/
> ---
> diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h
> index 49297fec448a..bb969738ed73 100644
> --- a/include/net/ip_vs.h
> +++ b/include/net/ip_vs.h
> @@ -972,7 +972,7 @@ struct ip_vs_dest {
>   	u16			af;		/* address family */
>   	__be16			port;		/* port number of the server */
>   	union nf_inet_addr	addr;		/* IP address of the server */
> -	volatile unsigned int	flags;		/* dest status flags */
> +	atomic_t		flags;		/* dest status flags */
>   	atomic_t		conn_flags;	/* flags to copy to conn */
>   	atomic_t		weight;		/* server weight */
>   	atomic_t		last_weight;	/* server latest weight */

It would be quite interesting to look at pahole output of ip_vs_dest
structure after the modification, it may have some "areas to improve"
for the performance

^ permalink raw reply

* Re: [PATCH 0/4] drivers/net: replace __get_free_pages() with kmalloc()
From: Paolo Abeni @ 2026-07-08  8:55 UTC (permalink / raw)
  To: Johannes Berg
  Cc: Brian Norris, Francesco Dolcini, Jakub Kicinski, b43-dev,
	libertas-dev, linux-kernel, linux-mm, linux-wireless, netdev,
	Mike Rapoport (Microsoft)
In-Reply-To: <20260701-b4-drivers-wireless-v1-0-60264cdf2efe@kernel.org>

On 7/1/26 3:59 PM, Mike Rapoport (Microsoft) wrote:
> This is a (small) part of larger work of replacing page allocator calls
> with kmalloc.
> 
> My initial intention a few month ago was to remove ugly casts [1], but then
> willy pointed out that Linus objected to something like this [2] and it
> looks like more than a decade old technical debt.
> 
> Largely, anything that doesn't need struct page (or a memdesc in the
> future) should just use kmalloc() or kvmalloc() to allocate memory.
> kmalloc() guarantees alignment, physical contiguity and working
> virt_to_phys() and beside nicer API that returns void * on alloc and
> doesn't require to know the allocation size on free, kmalloc() provides
> better debugging capabilities than page allocator.
> 
> Another thing is that touching these allocation sites gives the reviewers
> opportunity to see if a PAGE_SIZE buffer is actually needed or maybe
> another size is appropriate.
> 
> For larger allocations that don't need physically contiguous memory
> kvmalloc() can be a better option that __get_free_pages() because under
> memory pressure it's is easier to allocate several order-0 pages than a
> physically contiguous chunk with the same number of pages.
> 
> And last, but not least, removing needless calls to page allocator should
> help with memdesc (aka project folio) conversion. There will be way less
> places to audit to see if the user was actually using struct page.
> 
> Also in git:
> https://git.kernel.org/pub/scm/linux/kernel/git/rppt/linux.git gfp-to-kmalloc/drivers-net-wireless
> 
> [1] https://lore.kernel.org/all/20251018093002.3660549-1-rppt@kernel.org/
> [2] https://lore.kernel.org/all/CA+55aFwp4iy4rtX2gE2WjBGFL=NxMVnoFeHqYa2j1dYOMMGqxg@mail.gmail.com/
> 
> ---

@Johannes: just an head-up, I assume this series will go via your tree
(despite the slightly misleading subj)

/P


^ permalink raw reply

* Re: [PATCH net-next V5 4/6] devlink: Apply eswitch mode boot defaults
From: Jiri Pirko @ 2026-07-08  8:59 UTC (permalink / raw)
  To: Mark Bloch
  Cc: Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
	Saeed Mahameed, Leon Romanovsky, Tariq Toukan, Andrew Lunn,
	Jonathan Corbet, Shuah Khan, netdev, linux-rdma, linux-doc
In-Reply-To: <20260707174527.425134-5-mbloch@nvidia.com>

Tue, Jul 07, 2026 at 07:45:25PM +0200, mbloch@nvidia.com wrote:
>Apply parsed devlink_eswitch_mode= defaults after devlink registration
>and after successful reload.
>
>devl_register() may still be called before the device is ready for an
>eswitch mode change. Keep the registration path passive and let the
>regular devl_unlock() path queue the async apply work once the instance
>is registered and the default is still pending.
>
>The queueing path runs while the devlink instance lock is held, so the
>queued work gets its devlink reference before the caller drops the lock.
>The worker then takes the devlink instance lock normally and applies the
>default only if the instance is still registered and the default is still
>pending.

This is very code-descriptive. What's the benefit of that?


>
>For successful reloads that performed DRIVER_REINIT, devlink_reload()
>already holds the devlink instance lock and the driver has completed
>reload_up(). Clear pending work and apply the default directly from the
>reload path instead of queueing work.
>
>Preserve the user configured mode when it is set before devlink applies
>the default.
>

[..]


>+void devlink_default_esw_mode_apply_locked(struct devlink *devlink)
>+{
>+	const struct devlink_ops *ops = devlink->ops;
>+	int err;
>+
>+	devl_assert_locked(devlink);
>+
>+	if (!devlink_default_esw_mode_match(devlink))
>+		return;
>+
>+	if (!ops->eswitch_mode_set) {
>+		if (!devlink_default_esw_mode_match_all)
>+			devl_warn(devlink,
>+				  "devlink_eswitch_mode= selected this device but eswitch mode setting is not supported\n");
>+		return;
>+	}
>+
>+	err = devlink_eswitch_mode_set(devlink, devlink_default_esw_mode, NULL);
>+	if (err)
>+		devl_warn(devlink,
>+			  "Couldn't apply default eswitch mode, err %d\n",
>+			  err);
>+}
>+
>+void devlink_default_esw_mode_queue_apply_work(struct devlink *devlink)

eswitch/esw - we call it "eswitch" consistently everywhere. Why "esw"
here?



>+{
>+	devl_assert_locked(devlink);
>+
>+	if (!devlink_default_esw_mode_enabled || !devlink_default_esw_mode_wq)
>+		return;
>+	if (!devlink->default_esw_mode_apply_pending ||
>+	    !__devl_is_registered(devlink))
>+		return;
>+	if (!devlink_try_get(devlink))
>+		return;
>+	if (!queue_work(devlink_default_esw_mode_wq,
>+			&devlink->default_esw_mode_apply_work))
>+		devlink_put(devlink);
>+}
>+
>+static void devlink_default_esw_mode_apply_work(struct work_struct *work)
>+{
>+	struct devlink *devlink;
>+
>+	devlink = container_of(work, struct devlink,
>+			       default_esw_mode_apply_work);
>+

What happens if userspace eswitch mode set happens now? Any userspace
attempt should cancel the default apply. I don't see such mechanism in
your patches, did I miss it?



>+	devl_lock(devlink);
>+
>+	if (devl_is_registered(devlink) &&
>+	    devlink->default_esw_mode_apply_pending) {
>+		devlink_default_esw_mode_apply_locked(devlink);
>+		devlink->default_esw_mode_apply_pending = false;
>+	}
>+
>+	devl_unlock(devlink);
>+	devlink_put(devlink);
>+}
>+
>+void devlink_default_esw_mode_instance_init(struct devlink *devlink)

Why "_instance_"? Care to drop?


>+{
>+	INIT_WORK(&devlink->default_esw_mode_apply_work,
>+		  devlink_default_esw_mode_apply_work);
>+	devlink->default_esw_mode_apply_pending = true;
>+}
>+
>+void devlink_default_esw_mode_apply_pending_clear(struct devlink *devlink)
>+{
>+	devl_assert_locked(devlink);
>+
>+	devlink->default_esw_mode_apply_pending = false;
>+}
>+
>+void devlink_default_esw_mode_instance_cleanup(struct devlink *devlink)

Why "_instance_"? Care to drop?


>+{
>+	if (cancel_work_sync(&devlink->default_esw_mode_apply_work))
>+		devlink_put(devlink);
>+}
>+
> static int __init devlink_default_esw_mode_setup(char *str)
> {
> 	devlink_default_esw_mode_param = str;
>@@ -228,10 +325,21 @@ int __init devlink_default_esw_mode_init(void)
> 		return err;
> 	}
> 
>+	devlink_default_esw_mode_wq = alloc_workqueue("devlink_default_esw_mode",
>+						      WQ_UNBOUND | WQ_MEM_RECLAIM,
>+						      0);
>+	if (!devlink_default_esw_mode_wq) {
>+		devlink_default_esw_mode_param = NULL;
>+		devlink_default_esw_mode_nodes_clear();
>+		pr_warn("devlink: devlink_eswitch_mode parameter ignored, failed to allocate workqueue\n");

Why you don't "return"  here? I think that we don't need to allow the
case wq is not allocated.


>+	}
>+
> 	return 0;
> }
> 
> void __init devlink_default_esw_mode_cleanup(void)
> {
>+	if (devlink_default_esw_mode_wq)
>+		destroy_workqueue(devlink_default_esw_mode_wq);
> 	devlink_default_esw_mode_nodes_clear();
> }

[..]

^ permalink raw reply

* Re: [PATCH net-next 00/10] net: dsa: microchip: move non-common function out of ksz_common.c
From: patchwork-bot+netdevbpf @ 2026-07-08  9:10 UTC (permalink / raw)
  To: Bastien Curutchet
  Cc: woojung.huh, UNGLinuxDriver, andrew, olteanv, davem, edumazet,
	kuba, pabeni, linux, pascal.eberhard, miquel.raynal,
	thomas.petazzoni, netdev, linux-kernel, vladimir.oltean
In-Reply-To: <20260702-clean-ksz-4th-v1-0-93441e695fa4@bootlin.com>

Hello:

This series was applied to netdev/net-next.git (main)
by Paolo Abeni <pabeni@redhat.com>:

On Thu, 02 Jul 2026 11:07:22 +0200 you wrote:
> Hi all,
> 
> This series continues the rework of the KSZ driver initiated by three
> previous series (see [1], [2] & [3]).
> 
> The KSZ driver handles more than 20 switches split in several families.
> This was previously handled through a common set of dsa_switch_ops
> operations that used device-specific ksz_dev_ops callbacks. The three
> previous series have split this common struct dsa_switch_ops into 5
> to connect the ksz_dev_ops's implentations directly to the new
> dsa_swicth ops.
> 
> [...]

Here is the summary with links:
  - [net-next,01/10] net: dsa: microchip: split ksz8_change_mtu()
    https://git.kernel.org/netdev/net-next/c/15604320877e
  - [net-next,02/10] net: dsa: microchip: split port_max_mtu() implementation
    https://git.kernel.org/netdev/net-next/c/18a856b23680
  - [net-next,03/10] net: dsa: microchip: make ksz_is_port_mac_global_usable() static
    https://git.kernel.org/netdev/net-next/c/caf5d68b51e6
  - [net-next,04/10] net: dsa: microchip: move ksz88xx stats handling in ksz8.c
    https://git.kernel.org/netdev/net-next/c/a7e806f11f37
  - [net-next,05/10] net: dsa: microchip: move ksz_get_gbit() and ksz_get_xmii() to ksz9477.c
    https://git.kernel.org/netdev/net-next/c/7a83196e9012
  - [net-next,06/10] net: dsa: microchip: move KSZ9477 errata handling to ksz9477.c
    https://git.kernel.org/netdev/net-next/c/ab4b571d44fb
  - [net-next,07/10] net: dsa: microchip: handle KSZ8-specific tc setup in ksz8.c
    https://git.kernel.org/netdev/net-next/c/610106257cd7
  - [net-next,08/10] net: dsa: microchip: move ksz9477_set_default_prio_queue_mapping() to ksz9477.c
    https://git.kernel.org/netdev/net-next/c/e8de229a62ec
  - [net-next,09/10] net: dsa: microchip: rename ksz9477_drive_strength_write()
    https://git.kernel.org/netdev/net-next/c/da466bf62b01
  - [net-next,10/10] net: dsa: microchip: move the drive strength config out of the common section
    https://git.kernel.org/netdev/net-next/c/90c676dd4bb6

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



^ permalink raw reply

* Re: [PATCH net v2] ppp: defer channel free to an RCU grace period to fix pppol2tp RX UAF
From: Sebastian Andrzej Siewior @ 2026-07-08  9:11 UTC (permalink / raw)
  To: Paul E. McKenney
  Cc: Petr Pavlu, Qingfang Deng, Breno Leitao, Norbert Szetei,
	Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Taegu Ha, Kees Cook, linux-ppp, linux-kernel,
	Guillaume Nault, netdev, Luis Chamberlain, Daniel Gomez,
	Sami Tolvanen, Aaron Tomlin, linux-modules
In-Reply-To: <7ff3af97-1c10-4f46-84ae-ffbf757f7e9e@paulmck-laptop>

On 2026-07-07 09:39:01 [-0700], Paul E. McKenney wrote:
> Alternatives include:
> 
> o	Provide a patch like that above, but only execute the
> 	rcu_barrier() in some debug mode.  If your code works when
> 	that debug is enabled but does not otherwise, you add the
> 	rcu_barrier().
> 
> o	If debug is enabled, make rcu_do_batch() check the function
> 	before invoking it.  If the function is not mapped, issue a
> 	diagnostic, and don't try to invoke the function.  (But is
> 	there a sufficiently cheap way to check for the function not
> 	being mapped?)

In both cases you would see a backtrace and the name of the last
unloaded module. And since we don't see a lot of these reports, people
either don't run into this because it does not exist or RCU is quick
enough.

> 
> 							Thanx, Paul

Sebastian

^ permalink raw reply

* Re: [PATCH net v2] net: stmmac: resume PHY before hardware setup when opening the interface
From: Maxime Chevallier @ 2026-07-08  9:16 UTC (permalink / raw)
  To: Stefan Agner, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Andrew Lunn
  Cc: Russell King (Oracle), Ovidiu Panait, Maxime Coquelin,
	Alexandre Torgue, netdev, linux-stm32, linux-arm-kernel,
	regressions
In-Reply-To: <20260707195425.405989-1-stefan@agner.ch>

Hi Stefan,

On 7/7/26 21:54, Stefan Agner wrote:
> Since the referenced commit, changing the MTU on a running interface no
> longer disconnects and reconnects the PHY; __stmmac_release() merely
> stops phylink, which also suspends the PHY (BMCR power-down) when WoL
> is not enabled. __stmmac_open() then performs the DMA software reset in
> stmmac_hw_setup() before phylink_start() resumes the PHY again.
> 
> IEEE 802.3 22.2.4.1.5 allows a PHY to stop its receive clock while
> powered down, and stmmac requires a running receive clock for the DMA
> software reset to complete (the phylink config sets mac_requires_rxc).
> On such setups, e.g. the RK3566-based Home Assistant Green with an
> RTL8211F-VD PHY in RGMII mode, any runtime MTU change now times out and
> leaves the interface dead:
> 
>   rk_gmac-dwmac fe010000.ethernet end0: Failed to reset the dma
>   rk_gmac-dwmac fe010000.ethernet end0: stmmac_hw_setup: DMA engine initialization failed
>   rk_gmac-dwmac fe010000.ethernet end0: __stmmac_open: Hw setup failed
>   rk_gmac-dwmac fe010000.ethernet end0: failed reopening the interface after MTU change
> 
> In the field this is triggered by NetworkManager applying an MTU while
> activating the connection, breaking networking entirely.
> 
> Resume the PHY in __stmmac_open() before the hardware setup, making it
> the counterpart of the phylink_stop() in __stmmac_release(), like
> stmmac_resume() already does for the same reason. phylink_start() also
> resumes the PHY, but only after stmmac_hw_setup(), and it cannot be
> moved before the hardware setup since it may bring the link up
> immediately from a workqueue, racing with the initialization (see the
> comment in stmmac_resume()). For the regular ndo_open path the PHY has
> just been attached and is not suspended, in which case
> phylink_prepare_resume() does nothing.
> 
> Fixes: db299a0c09e9 ("net: stmmac: move PHY handling out of __stmmac_open()/release()")
> Link: https://github.com/home-assistant/operating-system/issues/4858
> Signed-off-by: Stefan Agner <stefan@agner.ch>

I was able to reproduce the issue on imx8mp and socfpga. Adding this case to
my periodic test list...

Indeed the assymetry isn't very nice, OTOH there's not phylink counterpart
for phylink_prepare_resume(). This helper was added for the suspend/resume
case, and only for it as the doc states, it just happens to do exactly
what we need to fix the issue :

/**
 * phylink_prepare_resume() - prepare to resume a network device
 * @pl: a pointer to a &struct phylink returned from phylink_create()
 *
 * Optional, but if called must be called prior to phylink_resume().
 *
 * Prepare to resume a network device, preparing the PHY as necessary.
 */
void phylink_prepare_resume(struct phylink *pl)

I think this helper should be renamed and the doc updated, stmmac is the
sole user, and it's really about controlling that rxc and not about
suspend / resume. Resuming from suspend is just one of the cases where we
need that RXC early on.

So either something like 'phylink_prepare_start_or_resume' but it's long,
or maybe we can be more explicit about it and simply call it:

phylink_start_rxc(pl)

(without a corresponding stop)

Maxime


^ permalink raw reply

* [PATCH net 5/9] batman-adv: tt: prevent TVLV OOB check overflow
From: Simon Wunderlich @ 2026-07-08  9:18 UTC (permalink / raw)
  To: netdev
  Cc: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, b.a.t.m.a.n, Sven Eckelmann, stable,
	Simon Wunderlich
In-Reply-To: <20260708091821.314516-1-sw@simonwunderlich.de>

From: Sven Eckelmann <sven@narfation.org>

A TT unicast TVLV contains the number of VLANs stored in it. This number is
an u16 and gets multiplied by the size of the struct
batadv_tvlv_tt_vlan_data (8 bytes). The size can therefore overflow the u16
used to store the tt_vlan_len. All additional safety checks to prevent
out-of-bounds access of the TVLV buffer are invalid due to this overflow.

Using size_t prevents this overflow and ensures that the safety checks
compare against the actual buffer requirements.

Cc: stable@vger.kernel.org
Fixes: 7ea7b4a14275 ("batman-adv: make the TT CRC logic VLAN specific")
Signed-off-by: Sven Eckelmann <sven@narfation.org>
Signed-off-by: Simon Wunderlich <sw@simonwunderlich.de>
---
 net/batman-adv/translation-table.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c
index aae72015645a4..dae5e1d8c0385 100644
--- a/net/batman-adv/translation-table.c
+++ b/net/batman-adv/translation-table.c
@@ -4033,7 +4033,8 @@ static int batadv_tt_tvlv_unicast_handler_v1(struct batadv_priv *bat_priv,
 					     u16 tvlv_value_len)
 {
 	struct batadv_tvlv_tt_data *tt_data;
-	u16 tt_vlan_len, tt_num_entries;
+	u16 tt_num_entries;
+	size_t tt_vlan_len;
 	char tt_flag;
 	bool ret;
 
-- 
2.47.3


^ permalink raw reply related

* [PATCH net 7/9] batman-adv: frag: fix primary_if leak on failed linearization
From: Simon Wunderlich @ 2026-07-08  9:18 UTC (permalink / raw)
  To: netdev
  Cc: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, b.a.t.m.a.n, Sven Eckelmann, stable, Sashiko,
	Simon Wunderlich
In-Reply-To: <20260708091821.314516-1-sw@simonwunderlich.de>

From: Sven Eckelmann <sven@narfation.org>

If the skb has a frag_list, it must be linearized before it can be split
using skb_split(). But when this step failed, it must not only free the skb
but also take care of the reference to the already found primary_if.

Cc: stable@vger.kernel.org
Reported-by: Sashiko <sashiko-bot@kernel.org>
Fixes: a063f2fba3fa ("batman-adv: Don't skb_split skbuffs with frag_list")
Signed-off-by: Sven Eckelmann <sven@narfation.org>
Signed-off-by: Simon Wunderlich <sw@simonwunderlich.de>
---
 net/batman-adv/fragmentation.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/net/batman-adv/fragmentation.c b/net/batman-adv/fragmentation.c
index 13d4689d332dc..2e20a2cb64cbf 100644
--- a/net/batman-adv/fragmentation.c
+++ b/net/batman-adv/fragmentation.c
@@ -547,7 +547,7 @@ int batadv_frag_send_packet(struct sk_buff *skb,
 	 */
 	if (skb_has_frag_list(skb) && __skb_linearize(skb)) {
 		ret = -ENOMEM;
-		goto free_skb;
+		goto put_primary_if;
 	}
 
 	/* Create one header to be copied to all fragments */
-- 
2.47.3


^ permalink raw reply related

* [PATCH net 8/9] batman-adv: mcast: avoid OOB read of num_dests header
From: Simon Wunderlich @ 2026-07-08  9:18 UTC (permalink / raw)
  To: netdev
  Cc: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, b.a.t.m.a.n, Sven Eckelmann, stable,
	Simon Wunderlich
In-Reply-To: <20260708091821.314516-1-sw@simonwunderlich.de>

From: Sven Eckelmann <sven@narfation.org>

Before the access to struct batadv_tvlv_mcast_tracker's num_dests, it is
attempted to check whether enough space is actually in the network header.
But instead of using offsetofend() to check for the whole size (2) which
must be accessible, offsetof() of is called. The latter is always returning
0. The comparison with the network header length will always return that
enough data is available - even when only 1 or 0 bytes are accessible.

Instead of using offsetofend(), use the more common check for the whole
header.

Cc: stable@vger.kernel.org
Fixes: 07afe1ba288c ("batman-adv: mcast: implement multicast packet reception and forwarding")
Signed-off-by: Sven Eckelmann <sven@narfation.org>
Signed-off-by: Simon Wunderlich <sw@simonwunderlich.de>
---
 net/batman-adv/multicast_forw.c | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/net/batman-adv/multicast_forw.c b/net/batman-adv/multicast_forw.c
index b8668a80b94a1..1404a3b7adfb1 100644
--- a/net/batman-adv/multicast_forw.c
+++ b/net/batman-adv/multicast_forw.c
@@ -927,11 +927,11 @@ static int batadv_mcast_forw_packet(struct batadv_priv *bat_priv,
 {
 	struct batadv_tvlv_mcast_tracker *mcast_tracker;
 	struct batadv_neigh_node *neigh_node;
-	unsigned long offset, num_dests_off;
 	struct sk_buff *nexthop_skb;
 	unsigned char *skb_net_hdr;
 	bool local_recv = false;
 	unsigned int tvlv_len;
+	unsigned long offset;
 	bool xmitted = false;
 	u8 *dest, *next_dest;
 	u16 num_dests;
@@ -940,9 +940,8 @@ static int batadv_mcast_forw_packet(struct batadv_priv *bat_priv,
 	/* (at least) TVLV part needs to be linearized */
 	SKB_LINEAR_ASSERT(skb);
 
-	/* check if num_dests is within skb length */
-	num_dests_off = offsetof(struct batadv_tvlv_mcast_tracker, num_dests);
-	if (num_dests_off > skb_network_header_len(skb))
+	/* check if batadv_tvlv_mcast_tracker header is within skb length */
+	if (sizeof(*mcast_tracker) > skb_network_header_len(skb))
 		return -EINVAL;
 
 	skb_net_hdr = skb_network_header(skb);
-- 
2.47.3


^ permalink raw reply related

* [PATCH net 6/9] batman-adv: frag: free unfragmentable packet
From: Simon Wunderlich @ 2026-07-08  9:18 UTC (permalink / raw)
  To: netdev
  Cc: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, b.a.t.m.a.n, Sven Eckelmann, stable,
	Simon Wunderlich
In-Reply-To: <20260708091821.314516-1-sw@simonwunderlich.de>

From: Sven Eckelmann <sven@narfation.org>

The caller of batadv_frag_send_packet() assume that the skb provided to the
function are always consumed. But the pre-check for an empty payload or the
zero fragment size returned an error without any further actions.

A failed pre-check must use the same error handling code as the rest of the
function.

Cc: stable@vger.kernel.org
Fixes: ee75ed88879a ("batman-adv: Fragment and send skbs larger than mtu")
Signed-off-by: Sven Eckelmann <sven@narfation.org>
Signed-off-by: Simon Wunderlich <sw@simonwunderlich.de>
---
 net/batman-adv/fragmentation.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/net/batman-adv/fragmentation.c b/net/batman-adv/fragmentation.c
index 8a006a0473a87..13d4689d332dc 100644
--- a/net/batman-adv/fragmentation.c
+++ b/net/batman-adv/fragmentation.c
@@ -518,8 +518,10 @@ int batadv_frag_send_packet(struct sk_buff *skb,
 	mtu = min_t(unsigned int, mtu, BATADV_FRAG_MAX_FRAG_SIZE);
 	max_fragment_size = mtu - header_size;
 
-	if (skb->len == 0 || max_fragment_size == 0)
-		return -EINVAL;
+	if (skb->len == 0 || max_fragment_size == 0) {
+		ret = -EINVAL;
+		goto free_skb;
+	}
 
 	num_fragments = (skb->len - 1) / max_fragment_size + 1;
 	max_fragment_size = (skb->len - 1) / num_fragments + 1;
-- 
2.47.3


^ permalink raw reply related

* [PATCH net 9/9] batman-adv: dat: fix tie-break for candidate selection
From: Simon Wunderlich @ 2026-07-08  9:18 UTC (permalink / raw)
  To: netdev
  Cc: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, b.a.t.m.a.n, Sven Eckelmann, stable,
	Simon Wunderlich
In-Reply-To: <20260708091821.314516-1-sw@simonwunderlich.de>

From: Sven Eckelmann <sven@narfation.org>

The original version of the candidate selection for DAT attempted to
compare both candidate and max_orig_node to identify which has the smaller
MAC address. This comparison is required as tie-break when a hash collision
happened.

But the used function returned 0 when the function was not equal and a
non-zero value when it was equal. As result, the actually selected
node was dependent on the order of entries in the orig_hash and not
actually on the mac addresses. The last originator in the hash collision
would always win.

To have a proper ordering, it must diff the actual MAC address bytes and
reject the candidate when the diff is not smaller than 0.

Cc: stable@vger.kernel.org
Fixes: 785ea1144182 ("batman-adv: Distributed ARP Table - create DHT helper functions")
Signed-off-by: Sven Eckelmann <sven@narfation.org>
Signed-off-by: Simon Wunderlich <sw@simonwunderlich.de>
---
 net/batman-adv/distributed-arp-table.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c
index c40c9e02391be..a6fe4820f65b9 100644
--- a/net/batman-adv/distributed-arp-table.c
+++ b/net/batman-adv/distributed-arp-table.c
@@ -546,7 +546,7 @@ static bool batadv_is_orig_node_eligible(struct batadv_dat_candidate *res,
 	 * the one with the lowest address
 	 */
 	if (tmp_max == max && max_orig_node &&
-	    batadv_compare_eth(candidate->orig, max_orig_node->orig))
+	    memcmp(candidate->orig, max_orig_node->orig, ETH_ALEN) >= 0)
 		goto out;
 
 	ret = true;
-- 
2.47.3


^ permalink raw reply related

* [PATCH net-next v9 0/9] net: dsa: add DSA support for the LAN9645x switch chip family
From: Jens Emil Schulz Østergaard @ 2026-07-08  9:18 UTC (permalink / raw)
  To: UNGLinuxDriver, Andrew Lunn, Vladimir Oltean, David S. Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Woojung Huh,
	Russell King, Steen Hegelund, Daniel Machon
  Cc: linux-kernel, netdev, devicetree,
	Jens Emil Schulz Østergaard

This series provides the Microchip LAN9645X Switch driver.

The LAN9645x is a family of chips with ethernet switch functionality and
multiple peripheral functions. The switch delivers up to 9 ethernet
ports and 12 Gbps switching bandwidth.

The switch chip has 5 integrated copper PHYs, support for 2x RGMII
interfaces, 2x SGMII and one QSGMII interface.

The switch chip is from the same design architecture family as ocelot
and lan966x, and the driver reflects this similarity. However, LAN9645x
does not have an internal CPU in any package, and must be driven
externally. For register IO it supports interfaces such as SPI, I2C and
MDIO.

The chip supports a variety of network features such as

* Mactable for MDB/FDB functionality
* Bridge forwarding offload
* VLAN-aware bridging
* IGMP/MLD snooping
* Link aggregation
* PTP timestamping
* FRER (802.1CB)
* Media Redundancy Protocol
* Parallel Redundancy and High-Availability Seamless Redundancy
  (HSR/PRP) in DANH/DANP mode
* Per stream filtering and policing
* Shapers such as Credit Based Shaping and Time Aware Shaing
* Frame preemption
* A TCAM (VCAP) for line-rate frame processing

The LAN9645x family consists of the following SKUs:

LAN96455F
LAN96457F
LAN96459F
LAN96455S
LAN96457S
LAN96459S

The difference between the SKUs is the number of supported ports (5, 7
or 9) and features supported. The F subfamily supports HSR/PRP and TSN,
while the S subfamily does not.

The intended way to bind this driver is using a parent MFD driver,
responsible for the register IO protocol, and distributing regmaps to
child devices. The goal is to use the same approach as the MFD driver in
drivers/mfd/ocelot-spi.c.

This driver expects to request named regmaps from a parent device. This
approach is similar to the DSA driver

drivers/net/dsa/ocelot/ocelot_ext.c

which supports being driven by an external CPU via SPI with parent
device drivers/mfd/ocelot-spi.c.

The MFD driver will come in a later series, because there are
requirements on the number of child devices before a driver qualifies as
a MFD device.

Development is done using the LAN966x as a host CPU, running the lan966x
swichdev driver, using the EVB-LAN9668 EDS2 board.

The datasheet is available here:
https://ww1.microchip.com/downloads/aemDocuments/documents/UNG/ProductDocuments/DataSheets/LAN9645xF-Data-Sheet-DS00006065.pdf

This series will deliver the following features:

* Standalone ports
* Bridge forwarding and FDB offloading
* VLAN-aware bridge
* Stats integration

More support will be added at a later stage. Here is a tentative plan of
future patches for this DSA driver:

* Add LAG support.
* Add MDB support.
* Add TC matchall mirror support.
* Add TC matchall police support.
* Add DCB/qos support.
* Add simple TC support: mqprio, cbs, tbf, ebf.
* Add TC flower filter support.
* Add HSR/PRP offloading support.
* Add PTP support.
* Add TC taprio support.

For completeness I include tentative plan of planned patches for
LAN9645x peripherals:

* Extend pinctrl-ocelot for LAN9645x:
  https://lore.kernel.org/linux-gpio/20260119-pinctrl_ocelot_extend_support_for_lan9645x-v1-0-1228155ed0ee@microchip.com/
* Add driver for internal PHY:
  https://lore.kernel.org/netdev/20260123-phy_micrel_add_support_for_lan9645x_internal_phy-v1-1-8484b1a5a7fd@microchip.com/
* MFD driver for managing register IO protocol and child device
  initialization.
* Extend pinctrl-microchip-sgpio for LAN9645x support.
* Extend i2c_designware for LAN9645x support.
* Add driver for outbound interrupt controller.
* Add serdes driver for lan9645x.

Signed-off-by: Jens Emil Schulz Østergaard <jensemil.schulzostergaard@microchip.com>
---
Changes in v9:
- Individual patches mention specific v9 changes
- Drop RGMII muxing to port 4.
- Link to v8: https://lore.kernel.org/r/20260702-dsa_lan9645x_switch_driver_base-v8-0-90228d8bba58@microchip.com

Changes in v8:
- Individual patches mention specific v8 changes
- Do not use the name CPU_PORT for the chips internal CPU port module,
  as it collides with the DSA CPU port (the NPI front port). Drop the
  CPU_PORT macro and reference the module as lan9645x->num_phys_ports,
  mirroring ocelot/felix.
- Reword the port map and related comments to distinguish the chip CPU
  port modules (indices 9-10) from the DSA CPU port.
- Add a port_mux_lock mutex to serialize port mux arbitration in
  the phylink mac_prepare path.
- Link to v7: https://lore.kernel.org/r/20260603-dsa_lan9645x_switch_driver_base-v7-0-b2f90e676707@microchip.com

Changes in v7:
- Individual patches mention specific v7 changes
- Do not mark the BPDU range 01:80:C2:00:00:0X as offloaded in the tag driver.
- Refactor cpu queue based frame classification to use categories default, trap
  and copy, which mirrors usage instead of being based on frame types.
- Add registers ANA:COMMON:CPUQ_8021_CFG for bpdu cpu queue configuration.
- Use cpu queue LAN9645X_CPUQ_TRAP for bpdu frames.
- Refactor IGMP/MLD/IPMC_CTRL to use new LAN9645X_CPUQ_DEF,
  LAN9645X_CPUQ_TRAP and LAN9645X_CPUQ_COPY queues.
- Add __aligned(2) to mac variable in lan9645x_mdb_update_dest.
- Link to v6: https://lore.kernel.org/r/20260527-dsa_lan9645x_switch_driver_base-v6-0-4d409ae64f3c@microchip.com

Changes in v6:
- Individual patches mention specific v6 changes
- Rebased on net-next, bumping DSA_TAG_PROTO_LAN9645X_VALUE to 34
- Link to v5: https://lore.kernel.org/r/20260518-dsa_lan9645x_switch_driver_base-v5-0-968fbf34ffa3@microchip.com

Changes in v5:
- Individual patches mention specific v5 changes
- Undo offset fix in postpull_rcsum. The original logic was correct for
  CHECKSUM_COMPLETE host NICs
- Use __always_inline in lan9645x_ifh_{get,set}
- remove double space after = in set_merge_mask
- remove unused fields dd_dis and tsn_dis, and add SKU supported port validation
  during setup
- phylink: remove MAC_2500FD
- phylink: add comment about empty supported_interfaces for port 5-6.
- phylink: fix 2:1 rgmii port muxing for port module 4 and 7 to be fully
  dynamic and validate requested mux settings.
- phylink: add comment about 2:1 rgmii port muxing for port module 4
  and 7.
- rx/tx-internal-delay-ps checked against supported 2ns value
- init lan9645x->npi = -1 at probe and check port < 0 in npi_deinit
- use ds->ageing_time_max
- use packed p->host_flood_req for atomic r/w
- fix typo in set_ageing_time comment
- include lan9645x->bridge deref under lock in brige_join
- switch -EBUSY to -EINVAL for vlan add/del in the reserved range.
- remove reserved HSR vlan
- lan9645x_mac_init returns error on table init timeout
- add comment about skipping LOCKED entries on fdb dump
- skip igmp/mld redir for npi port
- make lan9645x_stats_init void
- add SCNT_TX_BUFDROP to tx_dropped
- change rmon range {0,64} -> {64, 64}. Runt frames counted elsewhere.
- remove rx_crc, rx_symbol_err from rx_packets, as they are already
  counted in SZ_* buckets.
- add defensive cancel_delayed_work_sync in stats_free
- Link to v4: https://lore.kernel.org/r/20260430-dsa_lan9645x_switch_driver_base-v4-0-f1b6005fa8b7@microchip.com

Changes in v4:
- v3 was deferred, but I made some changes based on the Sashiko review
- Individual patches mention specific v4 changes
- Fix offset in postpull_rcsum so prefix eth header is cleared, not
  actual eth header, so tag driver works with CHECKSUM_COMPLETE host
  NICs
- Fix untagged rx on vlan aware port with pvid
- Add comment to QSYS_RES_CFG configuration
- Phylink_mac_prepare: fix to make sure we can dynamically change rgmii
  on port 4
- Move ports allocation to probe
- tag_npi_setup: reject cascaded setups
- Skip WARN_ON in lan9645x_to_port
- set_host_flood changed to per port work to coalesce values and skip
  atomic allocations
- Fix clear HOST_PVID vlan membership when a port joins a bridge.
- Explicit default value write to tag type register for untagged frames
- Use lan_rmw for ANA_DROP_CFG
- Add comment for error path in lan9645x_vlan_hw_wr
- Remove mac_entries list and just do direct IO to mac table from
  fdb_add/fdb_del.
- Clean up fresh mdb when hw mac table write fails.
- Remove rx_uc and tx_uc from ethtool stats list, as they are derivable
  from the eth-mac group
- Split stats_init into stats_alloc and stats_init, use alloc in probe
  and init in dsa_setup
- Link to v3: https://lore.kernel.org/r/20260410-dsa_lan9645x_switch_driver_base-v3-0-aadc8595306d@microchip.com

Changes in v3:
- Individual patches mention specific v3 changes.
- Add guard before vlan_remove_tag on xmit
- Add pskb_may_pull checks on rx
- Remove additionalProperties: true in bindings
- Remove unnecessary | from description in bindings
- Change top level $ref to dsa.yaml#/$defs/ethernet-ports
- Use ethernet-ports and ethernet-port
- Move ethernet-ports under properties instead of patternProperties
- Move unevaluatedProperties: false after $ref
- Update bindings example to use ethernet-ports and ethernet-port
- Move DEV_MAC_TAGS_CFG to port setup, instead of vlan config, so vlan
  overhead is always included in port frame maxlen calculation.
- Remove code disabling ipv6 on conduit
- Use of_property_read_u32 for {rx,tx}-internal-delay-ps
- Use dsa_user_ports(ds) instead of
  GENMASK(lan9645x->num_phys_ports - 1, 0) as base flood mask.
- Add comment explaining obey vlan
- Allow disabling aging with explicit zero parameters.
- Fix non-forwarding STP states in bridge fwd calculation.
- Restore host flood state on bridge leave.
- Avoid mac_entry dealloc when mac table writes fail.
- Avoid mdb_entry dealloc when mac table writes fail.
- Dealloc mac_entries on deinit.
- Dealloc mdb_entries on deinit.
- Link to v2: https://lore.kernel.org/r/20260324-dsa_lan9645x_switch_driver_base-v2-0-f7504e3b0681@microchip.com

Changes in v2:
- Individual patches have specific v2 changes.
- Ran DSA, and std counters, selftests, which prompted several changes.
  The following selftests pass, except for some expected failures:
    - bridge_vlan_aware.sh
    - bridge_vlan_unaware.sh
    - bridge_vlan_mcast.sh
    - no_forwarding.sh
    - bridge_mdb.sh
    - bridge_mld.sh
    - test_fdb_stress_test.sh
    - .../drivers/net/hw/ethtool_rmon.sh
    - .../drivers/net/hw/ethtool_std_stats.sh (from Ioana's series)
- Added new patch for MDB management, as this was required for selftests.
- Added port_set_host_flood to enable unknown traffic to standalone during
  promisc/ALL_MULTI (selftests).
- Remove the dubugfs.
- Link to v1: https://lore.kernel.org/r/20260303-dsa_lan9645x_switch_driver_base-v1-0-bff8ca1396f5@microchip.com

---
Jens Emil Schulz Østergaard (9):
      net: dsa: add tag driver for LAN9645X
      dt-bindings: net: lan9645x: add LAN9645X switch bindings
      net: dsa: lan9645x: add autogenerated register macros
      net: dsa: lan9645x: add basic dsa driver for LAN9645X
      net: dsa: lan9645x: add bridge support
      net: dsa: lan9645x: add vlan support
      net: dsa: lan9645x: add mac table integration
      net: dsa: lan9645x: add mdb management
      net: dsa: lan9645x: add port statistics

 .../net/dsa/microchip,lan96455s-switch.yaml        |  111 ++
 MAINTAINERS                                        |   10 +
 drivers/net/dsa/Kconfig                            |    2 +
 drivers/net/dsa/microchip/Makefile                 |    1 +
 drivers/net/dsa/microchip/lan9645x/Kconfig         |   11 +
 drivers/net/dsa/microchip/lan9645x/Makefile        |   12 +
 drivers/net/dsa/microchip/lan9645x/lan9645x_mac.c  |  307 ++++
 drivers/net/dsa/microchip/lan9645x/lan9645x_main.c | 1038 +++++++++++
 drivers/net/dsa/microchip/lan9645x/lan9645x_main.h |  439 +++++
 drivers/net/dsa/microchip/lan9645x/lan9645x_mdb.c  |  398 ++++
 drivers/net/dsa/microchip/lan9645x/lan9645x_npi.c  |   85 +
 .../net/dsa/microchip/lan9645x/lan9645x_phylink.c  |  382 ++++
 drivers/net/dsa/microchip/lan9645x/lan9645x_port.c |  226 +++
 drivers/net/dsa/microchip/lan9645x/lan9645x_regs.h | 1937 ++++++++++++++++++++
 .../net/dsa/microchip/lan9645x/lan9645x_stats.c    |  929 ++++++++++
 .../net/dsa/microchip/lan9645x/lan9645x_stats.h    |  279 +++
 drivers/net/dsa/microchip/lan9645x/lan9645x_vlan.c |  403 ++++
 include/linux/dsa/lan9645x.h                       |  144 ++
 include/net/dsa.h                                  |    2 +
 net/dsa/Kconfig                                    |   11 +
 net/dsa/Makefile                                   |    1 +
 net/dsa/tag_lan9645x.c                             |  311 ++++
 22 files changed, 7039 insertions(+)
---
base-commit: 06f42c77a95502df125adbf46ce2a2dbf3573ccd
change-id: 20260210-dsa_lan9645x_switch_driver_base-312bbfc37edb

Best regards,
-- 
Jens Emil Schulz Østergaard <jensemil.schulzostergaard@microchip.com>


^ permalink raw reply

* [PATCH net-next v9 2/9] dt-bindings: net: lan9645x: add LAN9645X switch bindings
From: Jens Emil Schulz Østergaard @ 2026-07-08  9:18 UTC (permalink / raw)
  To: UNGLinuxDriver, Andrew Lunn, Vladimir Oltean, David S. Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Woojung Huh,
	Russell King, Steen Hegelund, Daniel Machon
  Cc: linux-kernel, netdev, devicetree,
	Jens Emil Schulz Østergaard
In-Reply-To: <20260708-dsa_lan9645x_switch_driver_base-v9-0-0d1512a326d7@microchip.com>

Add bindings for LAN9645X switch. We use a fallback compatible for the
smallest SKU microchip,lan96455s-switch.

Reviewed-by: Steen Hegelund <Steen.Hegelund@microchip.com>
Signed-off-by: Jens Emil Schulz Østergaard <jensemil.schulzostergaard@microchip.com>
Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
---
Changes in v5:
- No changes.

Changes in v4:
- No changes.

Changes in v3:
- remove additionalProperties: true
- remove unnecessary | from description
- change top level $ref to dsa.yaml#/$defs/ethernet-ports
- use ethernet-ports and ethernet-port
- move ethernet-ports under properties instead of patternProperties
- move unevaluatedProperties: false after $ref
- update example to use ethernet-ports and ethernet-port

Changes in v2:
- rename file to microchip,lan96455s-switch.yaml
- remove led vendor property
- add {rx,tx}-internal-delay-ps for rgmii delay
- remove labels from example
- remove container node from example
---
 .../net/dsa/microchip,lan96455s-switch.yaml        | 111 +++++++++++++++++++++
 MAINTAINERS                                        |   1 +
 2 files changed, 112 insertions(+)

diff --git a/Documentation/devicetree/bindings/net/dsa/microchip,lan96455s-switch.yaml b/Documentation/devicetree/bindings/net/dsa/microchip,lan96455s-switch.yaml
new file mode 100644
index 000000000000..043fb48922b4
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/dsa/microchip,lan96455s-switch.yaml
@@ -0,0 +1,111 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/net/dsa/microchip,lan96455s-switch.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Microchip LAN9645x Ethernet switch
+
+maintainers:
+  - Jens Emil Schulz Østergaard <jensemil.schulzostergaard@microchip.com>
+
+description:
+  The LAN9645x switch is a multi-port Gigabit AVB/TSN Ethernet switch with
+  five integrated 10/100/1000Base-T PHYs. In addition to the integrated PHYs,
+  it supports up to 2 RGMII/RMII, up to 2 BASE-X/SERDES/2.5GBASE-X and one
+  Quad-SGMII interfaces.
+
+properties:
+  compatible:
+    oneOf:
+      - enum:
+          - microchip,lan96455s-switch
+      - items:
+          - enum:
+              - microchip,lan96455f-switch
+              - microchip,lan96457f-switch
+              - microchip,lan96459f-switch
+              - microchip,lan96457s-switch
+              - microchip,lan96459s-switch
+          - const: microchip,lan96455s-switch
+
+  reg:
+    maxItems: 1
+
+  ethernet-ports:
+    type: object
+    patternProperties:
+      "^ethernet-port@[0-8]$":
+        type: object
+        description: Ethernet switch ports
+
+        $ref: dsa-port.yaml#
+        unevaluatedProperties: false
+
+        properties:
+          rx-internal-delay-ps:
+            const: 2000
+
+          tx-internal-delay-ps:
+            const: 2000
+
+$ref: dsa.yaml#/$defs/ethernet-ports
+
+required:
+  - compatible
+  - reg
+  - ethernet-ports
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    ethernet-switch@4000 {
+        compatible = "microchip,lan96459f-switch", "microchip,lan96455s-switch";
+        reg = <0x4000 0x244>;
+
+        ethernet-ports {
+            #address-cells = <1>;
+            #size-cells = <0>;
+
+            ethernet-port@0 {
+                reg = <0>;
+                phy-mode = "gmii";
+                phy-handle = <&cuphy0>;
+            };
+
+            ethernet-port@1 {
+                reg = <1>;
+                phy-mode = "gmii";
+                phy-handle = <&cuphy1>;
+            };
+
+            ethernet-port@2 {
+                reg = <2>;
+                phy-mode = "gmii";
+                phy-handle = <&cuphy2>;
+            };
+
+            ethernet-port@3 {
+                reg = <3>;
+                phy-mode = "gmii";
+                phy-handle = <&cuphy3>;
+            };
+
+            ethernet-port@7 {
+                reg = <7>;
+                phy-mode = "rgmii";
+                ethernet = <&cpu_host_port>;
+                rx-internal-delay-ps = <2000>;
+                tx-internal-delay-ps = <2000>;
+
+                fixed-link {
+                    speed = <1000>;
+                    full-duplex;
+                    pause;
+                };
+            };
+        };
+    };
+...
+
diff --git a/MAINTAINERS b/MAINTAINERS
index f261c378eff3..8e507eaf7393 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -17626,6 +17626,7 @@ M:	Jens Emil Schulz Østergaard <jensemil.schulzostergaard@microchip.com>
 M:	UNGLinuxDriver@microchip.com
 L:	netdev@vger.kernel.org
 S:	Maintained
+F:	Documentation/devicetree/bindings/net/dsa/microchip,lan96455s-switch.yaml
 F:	include/linux/dsa/lan9645x.h
 F:	net/dsa/tag_lan9645x.c
 

-- 
2.52.0


^ permalink raw reply related

* [PATCH net-next v9 1/9] net: dsa: add tag driver for LAN9645X
From: Jens Emil Schulz Østergaard @ 2026-07-08  9:18 UTC (permalink / raw)
  To: UNGLinuxDriver, Andrew Lunn, Vladimir Oltean, David S. Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Woojung Huh,
	Russell King, Steen Hegelund, Daniel Machon
  Cc: linux-kernel, netdev, devicetree,
	Jens Emil Schulz Østergaard
In-Reply-To: <20260708-dsa_lan9645x_switch_driver_base-v9-0-0d1512a326d7@microchip.com>

Add tag driver for LAN9645x using a front port as CPU port. This mode
is called an NPI port in the datasheet.
Use long prefix on extraction (RX) and no prefix on injection (TX). A
long prefix on extraction helps get through the conduit port on host
side, since it will see a broadcast MAC.

The LAN9645x chip is in the same design architecture family as ocelot
and lan966x. The tagging protocol has the same structure as these chips,
but the particular fields are different or have different sizes.
Therefore, this tag driver is similar to tag_ocelot.c, but the
differences in fields makes it hard to reuse.

LAN9645x supports 3 different tag formats for extraction/injection of
frames from a CPU port: long prefix, short prefix and no prefix.

The tag is prepended to the frame. The critical data for the chip is
contained in an internal frame header (IFH) which is 28 bytes. The
prefix formats look like this:

Long prefix (16 bytes) + IFH:
- DMAC    = 0xffffffffffff on extraction.
- SMAC    = 0xfeffffffffff on extraction.
- ETYPE   = 0x8880
- payload = 0x0011
- IFH

Short prefix (4 bytes) + IFH:
- 0x8880
- 0x0011
- IFH

No prefix:
- IFH

The format can be configured asymmetrically on RX and TX.

The IFH get/set functions are declared as inline. All the field
constants are compile-time known, so when these calls are inlined
efficient code is generated with branches pruned and loops unrolled.
During testing it was observed that without explicit inlining GCC would
have trouble inlining the functions, which hurt performance.

Reviewed-by: Steen Hegelund <Steen.Hegelund@microchip.com>
Signed-off-by: Jens Emil Schulz Østergaard <jensemil.schulzostergaard@microchip.com>
---
Changes in v8:
- Drop the cpu_port local and use ds->num_ports directly as the IFH
  source port (the CPU port module index).
- Reword the reflection comment to refer to "the CPU".

Changes in v7:
- Introduce cpu queue based frame classification, and refactor to the
  categories default, trap and copy, which mirrors usage instead of
  being based on frame types.

Changes in v6:
- rebased on net-next, bumping DSA_TAG_PROTO_LAN9645X_VALUE to 34

Changes in v5:
- Undo offset fix in postpull_rcsum. The original logic was correct for
  CHECKSUM_COMPLETE host NICs
- Use __always_inline in lan9645x_ifh_{get,set}
- remove double space after = in set_merge_mask

Changes in v4:
- Fix offset in postpull_rcsum so prefix eth header is cleared, not
  actual eth header, so tag driver works with CHECKSUM_COMPLETE host
  NICs
- Fix untagged rx on vlan aware port with pvid

Changes in v3:
- guard vlan_remove_tag behind skb_headlen(skb) >= VLAN_ETH_HLEN on xmit
- add pskb_may_pull checks in rx path

Changes in v2:
- sorting in net/dsa/Kconfig
- sorting in net/dsa/Makefile
- remove default zero promisc_on_conduit
- move functions to to .c file
- add justification for inline usage to commit message
- add __skb_put_padto on xmit path
- fix hwaccel_put_tag
---
 MAINTAINERS                  |   8 ++
 include/linux/dsa/lan9645x.h | 144 ++++++++++++++++++++
 include/net/dsa.h            |   2 +
 net/dsa/Kconfig              |  11 ++
 net/dsa/Makefile             |   1 +
 net/dsa/tag_lan9645x.c       | 311 +++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 477 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 52f1a55eca99..f261c378eff3 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -17621,6 +17621,14 @@ L:	netdev@vger.kernel.org
 S:	Maintained
 F:	drivers/net/phy/microchip_t1.c
 
+MICROCHIP LAN9645X ETHERNET SWITCH DRIVER
+M:	Jens Emil Schulz Østergaard <jensemil.schulzostergaard@microchip.com>
+M:	UNGLinuxDriver@microchip.com
+L:	netdev@vger.kernel.org
+S:	Maintained
+F:	include/linux/dsa/lan9645x.h
+F:	net/dsa/tag_lan9645x.c
+
 MICROCHIP LAN966X ETHERNET DRIVER
 M:	Horatiu Vultur <horatiu.vultur@microchip.com>
 M:	UNGLinuxDriver@microchip.com
diff --git a/include/linux/dsa/lan9645x.h b/include/linux/dsa/lan9645x.h
new file mode 100644
index 000000000000..9896fab649c4
--- /dev/null
+++ b/include/linux/dsa/lan9645x.h
@@ -0,0 +1,144 @@
+/* SPDX-License-Identifier: GPL-2.0
+ * Copyright (C) 2026 Microchip Technology Inc.
+ */
+
+#ifndef _NET_DSA_TAG_LAN9645X_H_
+#define _NET_DSA_TAG_LAN9645X_H_
+
+#include <net/dsa.h>
+
+/* LAN9645x supports 3 different formats on an NPI port, long prefix, short
+ * prefix and no prefix. The format can be configured asymmetrically on RX and
+ * TX. We use long prefix on extraction (RX), and no prefix on injection.
+ * The long prefix on extraction helps get through the conduit port on host
+ * side, since it will see a broadcast MAC.
+ *
+ * The internal frame header (IFH) is 28 bytes, and the fields are documented
+ * below.
+ *
+ * Long prefix, 16 bytes + IFH:
+ * - DMAC    = 0xFFFFFFFFFFFF on extraction.
+ * - SMAC    = 0xFEFFFFFFFFFF on extraction.
+ * - ETYPE   = 0x8880
+ * - payload = 0x0011
+ * - IFH
+ *
+ * Short prefix, 4 bytes + IFH:
+ * - 0x8880
+ * - 0x0011
+ * - IFH
+ *
+ * No prefix:
+ * - IFH
+ *
+ */
+#define LAN9645X_IFH_TAG_TYPE_C	0
+#define LAN9645X_IFH_TAG_TYPE_S	1
+#define LAN9645X_IFH_LEN_U32		7
+#define LAN9645X_IFH_LEN		(LAN9645X_IFH_LEN_U32 * sizeof(u32))
+#define LAN9645X_IFH_BITS		(LAN9645X_IFH_LEN * BITS_PER_BYTE)
+#define LAN9645X_SHORT_PREFIX_LEN	4
+#define LAN9645X_LONG_PREFIX_LEN	16
+#define LAN9645X_TOTAL_TAG_LEN (LAN9645X_LONG_PREFIX_LEN + LAN9645X_IFH_LEN)
+
+#define IFH_INJ_TIMESTAMP		192
+#define IFH_BYPASS			191
+#define IFH_MASQ			190
+#define IFH_TIMESTAMP			186
+#define IFH_TIMESTAMP_NS		194
+#define IFH_TIMESTAMP_SUBNS		186
+#define IFH_MASQ_PORT			186
+#define IFH_RCT_INJ			185
+#define IFH_LEN				171
+#define IFH_WRDMODE			169
+#define IFH_RTAGD			167
+#define IFH_CUTTHRU			166
+#define IFH_REW_CMD			156
+#define IFH_REW_OAM			155
+#define IFH_PDU_TYPE			151
+#define IFH_FCS_UPD			150
+#define IFH_DP				149
+#define IFH_RTE_INB_UPDATE		148
+#define IFH_POP_CNT			146
+#define IFH_ETYPE_OFS			144
+#define IFH_SRCPORT			140
+#define IFH_SEQ_NUM			120
+#define IFH_TAG_TYPE			119
+#define IFH_TCI				103
+#define IFH_DSCP			97
+#define IFH_QOS_CLASS			94
+#define IFH_CPUQ			86
+#define IFH_LEARN_FLAGS			84
+#define IFH_SFLOW_ID			80
+#define IFH_ACL_HIT			79
+#define IFH_ACL_IDX			73
+#define IFH_ISDX			65
+#define IFH_DSTS			55
+#define IFH_FLOOD			53
+#define IFH_SEQ_OP			51
+#define IFH_IPV				48
+#define IFH_AFI				47
+#define IFH_RTP_ID			37
+#define IFH_RTP_SUBID			36
+#define IFH_PN_DATA_STATUS		28
+#define IFH_PN_TRANSF_STATUS_ZERO	27
+#define IFH_PN_CC			11
+#define IFH_DUPL_DISC_ENA		10
+#define IFH_RCT_AVAIL			9
+
+#define IFH_INJ_TIMESTAMP_SZ		32
+#define IFH_BYPASS_SZ			1
+#define IFH_MASQ_SZ			1
+#define IFH_TIMESTAMP_SZ		38
+#define IFH_TIMESTAMP_NS_SZ		30
+#define IFH_TIMESTAMP_SUBNS_SZ		8
+#define IFH_MASQ_PORT_SZ		4
+#define IFH_RCT_INJ_SZ			1
+#define IFH_LEN_SZ			14
+#define IFH_WRDMODE_SZ			2
+#define IFH_RTAGD_SZ			2
+#define IFH_CUTTHRU_SZ			1
+#define IFH_REW_CMD_SZ			10
+#define IFH_REW_OAM_SZ			1
+#define IFH_PDU_TYPE_SZ			4
+#define IFH_FCS_UPD_SZ			1
+#define IFH_DP_SZ			1
+#define IFH_RTE_INB_UPDATE_SZ		1
+#define IFH_POP_CNT_SZ			2
+#define IFH_ETYPE_OFS_SZ		2
+#define IFH_SRCPORT_SZ			4
+#define IFH_SEQ_NUM_SZ			16
+#define IFH_TAG_TYPE_SZ			1
+#define IFH_TCI_SZ			16
+#define IFH_DSCP_SZ			6
+#define IFH_QOS_CLASS_SZ		3
+#define IFH_CPUQ_SZ			8
+#define IFH_LEARN_FLAGS_SZ		2
+#define IFH_SFLOW_ID_SZ			4
+#define IFH_ACL_HIT_SZ			1
+#define IFH_ACL_IDX_SZ			6
+#define IFH_ISDX_SZ			8
+#define IFH_DSTS_SZ			10
+#define IFH_FLOOD_SZ			2
+#define IFH_SEQ_OP_SZ			2
+#define IFH_IPV_SZ			3
+#define IFH_AFI_SZ			1
+#define IFH_RTP_ID_SZ			10
+#define IFH_RTP_SUBID_SZ		1
+#define IFH_PN_DATA_STATUS_SZ		8
+#define IFH_PN_TRANSF_STATUS_ZERO_SZ	1
+#define IFH_PN_CC_SZ			16
+#define IFH_DUPL_DISC_ENA_SZ		1
+#define IFH_RCT_AVAIL_SZ		1
+
+/* Chip has 8 cpu queues. The cpu queues used by a frame is passed as a mask in
+ * the IFH on extraction. We use this to avoid classifying BPDU, IGMP and MLD
+ * frames in the tag driver.
+ */
+enum {
+	LAN9645X_CPUQ_DEF = 0,
+	LAN9645X_CPUQ_TRAP = 1,
+	LAN9645X_CPUQ_COPY = 2,
+};
+
+#endif /* _NET_DSA_TAG_LAN9645X_H_ */
diff --git a/include/net/dsa.h b/include/net/dsa.h
index 8c16ef23cc10..6ebd8c635d17 100644
--- a/include/net/dsa.h
+++ b/include/net/dsa.h
@@ -59,6 +59,7 @@ struct tc_action;
 #define DSA_TAG_PROTO_MXL_GSW1XX_VALUE		31
 #define DSA_TAG_PROTO_MXL862_VALUE		32
 #define DSA_TAG_PROTO_NETC_VALUE		33
+#define DSA_TAG_PROTO_LAN9645X_VALUE		34
 
 enum dsa_tag_protocol {
 	DSA_TAG_PROTO_NONE		= DSA_TAG_PROTO_NONE_VALUE,
@@ -95,6 +96,7 @@ enum dsa_tag_protocol {
 	DSA_TAG_PROTO_MXL_GSW1XX	= DSA_TAG_PROTO_MXL_GSW1XX_VALUE,
 	DSA_TAG_PROTO_MXL862		= DSA_TAG_PROTO_MXL862_VALUE,
 	DSA_TAG_PROTO_NETC		= DSA_TAG_PROTO_NETC_VALUE,
+	DSA_TAG_PROTO_LAN9645X		= DSA_TAG_PROTO_LAN9645X_VALUE,
 };
 
 struct dsa_switch;
diff --git a/net/dsa/Kconfig b/net/dsa/Kconfig
index d5e725b90d78..a0a0096e7e43 100644
--- a/net/dsa/Kconfig
+++ b/net/dsa/Kconfig
@@ -75,6 +75,17 @@ config NET_DSA_TAG_HELLCREEK
 	  Say Y or M if you want to enable support for tagging frames
 	  for the Hirschmann Hellcreek TSN switches.
 
+config NET_DSA_TAG_LAN9645X
+	tristate "Tag driver for Lan9645x switches"
+	help
+	  Say Y or M if you want to enable NPI tagging for the Lan9645x switches.
+	  In this mode, the frames over the Ethernet CPU port are prepended with
+	  a hardware-defined injection/extraction frame header.
+	  On injection a 28 byte internal frame header (IFH) is used. On
+	  extraction a 16 byte prefix is prepended before the internal frame
+	  header. This prefix starts with a broadcast MAC, to ease passage
+	  through the host side RX filter.
+
 config NET_DSA_TAG_GSWIP
 	tristate "Tag driver for Lantiq / Intel GSWIP switches"
 	help
diff --git a/net/dsa/Makefile b/net/dsa/Makefile
index b8c2667cd14a..600975e0f2f7 100644
--- a/net/dsa/Makefile
+++ b/net/dsa/Makefile
@@ -27,6 +27,7 @@ obj-$(CONFIG_NET_DSA_TAG_GSWIP) += tag_gswip.o
 obj-$(CONFIG_NET_DSA_TAG_HELLCREEK) += tag_hellcreek.o
 obj-$(CONFIG_NET_DSA_TAG_KSZ) += tag_ksz.o
 obj-$(CONFIG_NET_DSA_TAG_LAN9303) += tag_lan9303.o
+obj-$(CONFIG_NET_DSA_TAG_LAN9645X) += tag_lan9645x.o
 obj-$(CONFIG_NET_DSA_TAG_MTK) += tag_mtk.o
 obj-$(CONFIG_NET_DSA_TAG_MXL_862XX) += tag_mxl862xx.o
 obj-$(CONFIG_NET_DSA_TAG_MXL_GSW1XX) += tag_mxl-gsw1xx.o
diff --git a/net/dsa/tag_lan9645x.c b/net/dsa/tag_lan9645x.c
new file mode 100644
index 000000000000..c9049e3620a1
--- /dev/null
+++ b/net/dsa/tag_lan9645x.c
@@ -0,0 +1,311 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2026 Microchip Technology Inc.
+ */
+
+#include <linux/dsa/lan9645x.h>
+
+#include "tag.h"
+
+#define LAN9645X_NAME "lan9645x"
+
+#define BTM_MSK(n)	((u8)GENMASK(n, 0))
+#define TOP_MSK(n)	((u8)GENMASK(7, n))
+
+static inline void set_merge_mask(u8 *on_zero, u8 on_one, u8 mask)
+{
+	*on_zero = *on_zero ^ ((*on_zero ^ on_one) & mask);
+}
+
+/* The internal frame header (IFH) is a big-endian 28 byte unpadded bit array.
+ * Frames can be prepended with an IFH on injection and extraction. There
+ * are two field layouts, one for extraction and one for injection.
+ *
+ *    IFH bits go from high to low, for instance
+ *    ifh[0]  = [223:216]
+ *    ifh[27] = [7:0]
+ *
+ * Here is an example of setting a value starting at bit 13 of bit length 17.
+ *
+ * val    = 0x1ff
+ * pos    = 13
+ * length = 17
+ *
+ *
+ * IFH[]   0                         23       24       25        26      27
+ *
+ *                                           end_u8           start_u8
+ *      +--------+----------------+--------+--------+--------+--------+--------+
+ *      |        |                |        |        |        |        |        |
+ * IFH  |        | ....           |        |  vvvvvvvvvvvvvvvvvvv     |        |
+ *      |        |                |        |  |     |        |  |     |        |
+ *      +--------+----------------+--------+--+-----+--------+--+-----+--------+
+ * Bits  223                       39    32 31|   24 23    16 15|    8 7      0
+ *                                            |                 |
+ *                                            |                 |
+ *                                            |                 |
+ *                                            v                 v
+ *                                        end       = 29       pos        = 13
+ *                                        end_rem   = 5        pos_rem    = 5
+ *                                        end_u8    = 3        start_u8   = 1
+ *                                        BTM_MSK(5)= 0x3f     TOP_MSK(5) = 0xe0
+ *
+ *
+ * In end_u8 and start_u8 we must merge the existing IFH byte with the new
+ * value. In the 'middle' bytes of the value we can overwrite the corresponding
+ * IFH byte.
+ */
+static __always_inline void lan9645x_ifh_set(u8 *ifh, u32 val, size_t pos,
+					     size_t length)
+{
+	size_t end = (pos + length) - 1;
+	size_t end_rem = end & 0x7;
+	size_t pos_rem = pos & 0x7;
+	size_t start_u8 = pos >> 3;
+	size_t end_u8 = end >> 3;
+	u8 end_mask, start_mask;
+	size_t vshift;
+	u8 *ptr;
+
+	BUILD_BUG_ON_MSG(length > 32, "IFH field size wider than 32.");
+	BUILD_BUG_ON_MSG(length == 0, "IFH field size of 0.");
+	BUILD_BUG_ON_MSG(pos + length > LAN9645X_IFH_BITS,
+			 "IFH field overflows IFH");
+
+	end_mask = BTM_MSK(end_rem);
+	start_mask = TOP_MSK(pos_rem);
+
+	ptr = &ifh[LAN9645X_IFH_LEN - 1 - end_u8];
+
+	if (end_u8 == start_u8)
+		return set_merge_mask(ptr, val << pos_rem,
+				      end_mask & start_mask);
+
+	vshift = length - end_rem - 1;
+	set_merge_mask(ptr++, val >> vshift, end_mask);
+
+	for (size_t j = 1; j < end_u8 - start_u8; j++) {
+		vshift -= 8;
+		*ptr++ = val >> vshift;
+	}
+
+	set_merge_mask(ptr, val << pos_rem, start_mask);
+}
+
+static __always_inline u32 lan9645x_ifh_get(const u8 *ifh, size_t pos,
+					    size_t length)
+{
+	size_t end = (pos + length) - 1;
+	size_t end_rem = end & 0x7;
+	size_t pos_rem = pos & 0x7;
+	size_t start_u8 = pos >> 3;
+	size_t end_u8 = end >> 3;
+	u8 end_mask, start_mask;
+	const u8 *ptr;
+	u32 val;
+
+	BUILD_BUG_ON_MSG(length > 32, "IFH field size wider than 32.");
+	BUILD_BUG_ON_MSG(length == 0, "IFH field size of 0.");
+	BUILD_BUG_ON_MSG(pos + length > LAN9645X_IFH_BITS,
+			 "IFH field overflows IFH");
+
+	end_mask = BTM_MSK(end_rem);
+	start_mask = TOP_MSK(pos_rem);
+
+	ptr = &ifh[LAN9645X_IFH_LEN - 1 - end_u8];
+
+	if (end_u8 == start_u8)
+		return (*ptr & end_mask & start_mask) >> pos_rem;
+
+	val = *ptr++ & end_mask;
+
+	for (size_t j = 1; j < end_u8 - start_u8; j++)
+		val = val << 8 | *ptr++;
+
+	return val << (8 - pos_rem) | (*ptr & start_mask) >> pos_rem;
+}
+
+static void lan9645x_xmit_get_vlan_info(struct sk_buff *skb,
+					struct net_device *br,
+					u32 *vlan_tci, u32 *tag_type)
+{
+	struct vlan_ethhdr *hdr;
+	u16 proto, tci;
+
+	if (!br || !br_vlan_enabled(br)) {
+		*vlan_tci = 0;
+		*tag_type = LAN9645X_IFH_TAG_TYPE_C;
+		return;
+	}
+
+	hdr = (struct vlan_ethhdr *)skb_mac_header(skb);
+	br_vlan_get_proto(br, &proto);
+
+	if (skb_headlen(skb) >= VLAN_ETH_HLEN &&
+	    ntohs(hdr->h_vlan_proto) == proto) {
+		vlan_remove_tag(skb, &tci);
+		*vlan_tci = tci;
+	} else {
+		rcu_read_lock();
+		br_vlan_get_pvid_rcu(br, &tci);
+		rcu_read_unlock();
+		*vlan_tci = tci;
+	}
+
+	*tag_type = (proto != ETH_P_8021Q) ? LAN9645X_IFH_TAG_TYPE_S :
+					     LAN9645X_IFH_TAG_TYPE_C;
+}
+
+static void lan9645x_offload_fwd_mark(struct sk_buff *skb, u32 cpuq)
+{
+	/* Trapped frames must be forwarded by the stack. */
+	if (cpuq & BIT(LAN9645X_CPUQ_TRAP)) {
+		skb->offload_fwd_mark = 0;
+		return;
+	}
+
+	dsa_default_offload_fwd_mark(skb);
+}
+
+static struct sk_buff *lan9645x_xmit(struct sk_buff *skb,
+				     struct net_device *ndev)
+{
+	struct dsa_port *dp = dsa_user_to_port(ndev);
+	struct dsa_switch *ds = dp->ds;
+	u32 vlan_tci, tag_type;
+	u32 qos_class;
+	void *ifh;
+
+	lan9645x_xmit_get_vlan_info(skb, dsa_port_bridge_dev_get(dp), &vlan_tci,
+				    &tag_type);
+
+	/* We need to make sure frame has the proper size after IFH is stripped
+	 * by hw.
+	 */
+	if (__skb_put_padto(skb, ETH_ZLEN, false))
+		return NULL;
+
+	qos_class = netdev_get_num_tc(ndev) ?
+		    netdev_get_prio_tc_map(ndev, skb->priority) :
+		    skb->priority;
+
+	/* Make room for IFH */
+	ifh = skb_push(skb, LAN9645X_IFH_LEN);
+	memset(ifh, 0, LAN9645X_IFH_LEN);
+
+	lan9645x_ifh_set(ifh, 1, IFH_BYPASS, IFH_BYPASS_SZ);
+	lan9645x_ifh_set(ifh, ds->num_ports, IFH_SRCPORT, IFH_SRCPORT_SZ);
+	lan9645x_ifh_set(ifh, tag_type, IFH_TAG_TYPE, IFH_TAG_TYPE_SZ);
+	lan9645x_ifh_set(ifh, vlan_tci, IFH_TCI, IFH_TCI_SZ);
+	lan9645x_ifh_set(ifh, qos_class, IFH_QOS_CLASS, IFH_QOS_CLASS_SZ);
+	lan9645x_ifh_set(ifh, BIT(dp->index), IFH_DSTS, IFH_DSTS_SZ);
+
+	return skb;
+}
+
+static struct sk_buff *lan9645x_rcv(struct sk_buff *skb,
+				    struct net_device *ndev)
+{
+	u32 src_port, qos_class, vlan_tci, tag_type, popcnt, etype_ofs, cpuq;
+	struct dsa_port *dp;
+	u32 ifh_gap_len = 0;
+	u16 vlan_tpid;
+	u8 *ifh;
+
+	/* DSA master already consumed DMAC,SMAC,ETYPE from long prefix. Go back
+	 * to beginning of frame.
+	 */
+	skb_push(skb, ETH_HLEN);
+
+	if (unlikely(!pskb_may_pull(skb, LAN9645X_TOTAL_TAG_LEN)))
+		return NULL;
+
+	/* IFH starts after our long prefix */
+	ifh = skb_pull(skb, LAN9645X_LONG_PREFIX_LEN);
+
+	popcnt = lan9645x_ifh_get(ifh, IFH_POP_CNT, IFH_POP_CNT_SZ);
+	etype_ofs = lan9645x_ifh_get(ifh, IFH_ETYPE_OFS, IFH_ETYPE_OFS_SZ);
+	src_port = lan9645x_ifh_get(ifh, IFH_SRCPORT, IFH_SRCPORT_SZ);
+	tag_type = lan9645x_ifh_get(ifh, IFH_TAG_TYPE, IFH_TAG_TYPE_SZ);
+	vlan_tci = lan9645x_ifh_get(ifh, IFH_TCI, IFH_TCI_SZ);
+	qos_class = lan9645x_ifh_get(ifh, IFH_QOS_CLASS, IFH_QOS_CLASS_SZ);
+	cpuq = lan9645x_ifh_get(ifh, IFH_CPUQ, IFH_CPUQ_SZ);
+
+	/* Set skb->data at start of real header
+	 *
+	 * Since REW_PORT_NO_REWRITE=0 is required on the NPI port, we need to
+	 * account for any tags popped by the hardware, as that will leave a gap
+	 * between the IFH and DMAC.
+	 */
+	if (popcnt == 0 && etype_ofs == 0)
+		ifh_gap_len = 2 * VLAN_HLEN;
+	else if (popcnt == 3)
+		ifh_gap_len = VLAN_HLEN;
+
+	skb_pull(skb, LAN9645X_IFH_LEN);
+
+	if (unlikely(!pskb_may_pull(skb, ifh_gap_len + ETH_HLEN)))
+		return NULL;
+
+	skb_pull(skb, ifh_gap_len);
+	skb_reset_mac_header(skb);
+	skb_set_network_header(skb, ETH_HLEN);
+	skb_reset_mac_len(skb);
+
+	/* Reset skb->data past the actual ethernet header. */
+	skb_pull(skb, ETH_HLEN);
+
+	/* We must deliver the skb so skb->csum only covers the data beyond the
+	 * real ethernet header. The fake ethernet header in the prefix is
+	 * not part of skb->csum already. We must subtract what remains of the
+	 * prefix, the ifh and the gap.
+	 */
+	skb_postpull_rcsum(skb,
+			   skb->data - LAN9645X_TOTAL_TAG_LEN - ifh_gap_len,
+			   LAN9645X_TOTAL_TAG_LEN + ifh_gap_len);
+
+	skb->dev = dsa_conduit_find_user(ndev, 0, src_port);
+	if (WARN_ON_ONCE(!skb->dev)) {
+		/* This should never happen since we have disabled reflection
+		 * back to the CPU.
+		 */
+		return NULL;
+	}
+
+	lan9645x_offload_fwd_mark(skb, cpuq);
+
+	skb->priority = qos_class;
+
+	/* While we have REW_PORT_NO_REWRITE=0 on the NPI port, we still disable
+	 * port VLAN tagging with REW_TAG_CFG. Any classified VID, different
+	 * from a VID in the frame, will not be written to the frame, but is
+	 * only communicated via the IFH. So for VLAN-aware ports we add the IFH
+	 * vlan to the skb.
+	 */
+	dp = dsa_user_to_port(skb->dev);
+	vlan_tpid = tag_type ? ETH_P_8021AD : ETH_P_8021Q;
+
+	if (dsa_port_is_vlan_filtering(dp) && vlan_tci) {
+		u16 port_pvid = 0;
+
+		br_vlan_get_pvid_rcu(skb->dev, &port_pvid);
+
+		if ((vlan_tci & VLAN_VID_MASK) != port_pvid)
+			__vlan_hwaccel_put_tag(skb, htons(vlan_tpid), vlan_tci);
+	}
+
+	return skb;
+}
+
+static const struct dsa_device_ops lan9645x_netdev_ops = {
+	.name = LAN9645X_NAME,
+	.proto = DSA_TAG_PROTO_LAN9645X,
+	.xmit = lan9645x_xmit,
+	.rcv = lan9645x_rcv,
+	.needed_headroom = LAN9645X_TOTAL_TAG_LEN,
+};
+
+MODULE_DESCRIPTION("DSA tag driver for LAN9645x family of switches, using NPI port");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_LAN9645X, LAN9645X_NAME);
+
+module_dsa_tag_driver(lan9645x_netdev_ops);

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