Netdev List
 help / color / mirror / Atom feed
* Re: [PATCH net v6 4/4] macsec: Support VLAN-filtering lower devices
From: Sabrina Dubroca @ 2026-04-08 11:01 UTC (permalink / raw)
  To: Cosmin Ratiu
  Cc: andrew+netdev@lunn.ch, davem@davemloft.net,
	linux-kselftest@vger.kernel.org, Dragos Tatulea, sdf@fomichev.me,
	shuah@kernel.org, kuba@kernel.org, pabeni@redhat.com,
	horms@kernel.org, edumazet@google.com, netdev@vger.kernel.org
In-Reply-To: <3aab1732c62fa8241ea91f2e26720ba47d646a3a.camel@nvidia.com>

2026-04-08, 10:24:28 +0000, Cosmin Ratiu wrote:
> On Wed, 2026-04-08 at 10:25 +0200, Sabrina Dubroca wrote:
> > 2026-04-07, 15:07:47 +0000, Cosmin Ratiu wrote:
> > > On Thu, 2026-04-02 at 16:48 +0200, Sabrina Dubroca wrote:
> > > > If this happens on a real device, the VLAN filters will be
> > > > broken.
> > > > I'm
> > > > not sure what the right behavior would be:
> > > > 
> > > > 1. reject the request to enable offload
> > > > 2. switch to promiscuous mode
> > > 
> > > I implemented and tested option 1. In the unlikely scenario adding
> > > VLAN
> > > filters prevents offloading
> > 
> > How unlikely is it? Resource allocation, talking to HW, device
> > limits,
> > anything else that could fail?
> > 
> > > , it's better for the driver to be explicit
> > > and let the user turn on promisc mode themselves. Keeping track of
> > > whether VLAN filters failed and promisc was used as a fallback adds
> > > some extra complexity.
> > 
> > If it's indeed (very) unlikely, sure. There's still a bit of a
> > regression/change of behavior for users compared to before the
> > IFF_UNICAST_FLT patch, but I think we can wait until someone
> > complains, and then add the tracking if that happens.
> > 
> > > What would be the point of IFF_UNICAST_FLT then?
> > 
> > The point is that this would just be a fallback.
> > 
> > > Please let me know if you agree with this approach, so I can send
> > > v8
> > > with it.
> > 
> > If you can confirm it's on the "very unlikely" side, yes, this
> > approach sounds ok. Thanks.
> 
> There are many, many drivers which implement .ndo_vlan_rx_add_vid, I
> can't really browse them all and determine overall likelihood of

Sorry, I wasn't requesting that much work, more of a "feeling" based
on your understanding. Anyway for macsec offload we don't have to care
about the vast majority of drivers.

> failure. But presumably there are some memory allocations involved and
> some FW communication to add the new filter.

I guess if FW communication fails we're in trouble anyway [*], and memory
allocation failure is an acceptable reason to reject offloading
anyway.

[*] rolling back the mdo_add_secy could be a problem if we had
difficulties talking to the HW?

> An AI prompt of "Explore implementations of .ndo_vlan_rx_add_vid in
> drivers and classify potential failure sources and their likelihood of
> failure." resulted in (edited for compactness):
> """
> Analyzed 24+ implementations across major drivers. Here's the
> breakdown:  
> Category   Likelihood Drivers                         Typical Error
> HW/FW cmds Low        mlx4, nfp, hinic, qeth, vmxnet3 Device-specific
> Mem alloc  Very Low   bnx2x, qede, virtio_net         -ENOMEM
> Filter add Low-Medium mlx5, mlx4, ice, qede, ocelot   -ENOSPC, -EEXIST
> Locking    Very Low   be2net, mlx4, nfp, hinic        -EBUSY
> Res limit  Medium     qede (quota check)              Promisc fallback
> 
> Three Driver Archetypes  
> 
> 1. Simple bitfield drivers (never fail): e1000, e1000e, ixgbe, igb —
> just set a VFTA bit in hardware registers, always return 0.
> 
> 2. Complex offload drivers (can fail): mlx5 (flow steering rules), mlx4
> (FW VLAN registration), ice (switch rules + promisc management), qede
> (quota-aware with promisc fallback), nfp (mailbox commands), hinic (FW
> commands with rollback).
> 
> 3. Delegation/passthrough drivers (inherit failures): bonding, team,
> hsr, dsa, macvlan, ipvlan, macsec — propagate vlan_vid_add() to lower
> devices with unwind-on-failure patterns.
> """
> 
> So I guess we go with option 1 for now.

ACK

-- 
Sabrina

^ permalink raw reply

* [PATCH net 2/2] batman-adv: hold claim backbone gateways by reference
From: Simon Wunderlich @ 2026-04-08 11:02 UTC (permalink / raw)
  To: davem, kuba
  Cc: netdev, b.a.t.m.a.n, Haoze Xie, stable, Yifan Wu, Juefei Pu,
	Yuan Tan, Xin Liu, Ao Zhou, Sven Eckelmann, Simon Wunderlich
In-Reply-To: <20260408110255.976389-1-sw@simonwunderlich.de>

From: Haoze Xie <royenheart@gmail.com>

batadv_bla_add_claim() can replace claim->backbone_gw and drop the old
gateway's last reference while readers still follow the pointer.

The netlink claim dump path dereferences claim->backbone_gw->orig and
takes claim->backbone_gw->crc_lock without pinning the underlying
backbone gateway. batadv_bla_check_claim() still has the same naked
pointer access pattern.

Reuse batadv_bla_claim_get_backbone_gw() in both readers so they operate
on a stable gateway reference until the read-side work is complete.
This keeps the dump and claim-check paths aligned with the lifetime
rules introduced for the other BLA claim readers.

Fixes: 23721387c409 ("batman-adv: add basic bridge loop avoidance code")
Fixes: 04f3f5bf1883 ("batman-adv: add B.A.T.M.A.N. Dump BLA claims via netlink")
Cc: stable@vger.kernel.org
Reported-by: Yifan Wu <yifanwucs@gmail.com>
Reported-by: Juefei Pu <tomapufckgml@gmail.com>
Co-developed-by: Yuan Tan <yuantan098@gmail.com>
Signed-off-by: Yuan Tan <yuantan098@gmail.com>
Suggested-by: Xin Liu <bird@lzu.edu.cn>
Signed-off-by: Haoze Xie <royenheart@gmail.com>
Signed-off-by: Ao Zhou <n05ec@lzu.edu.cn>
Signed-off-by: Sven Eckelmann <sven@narfation.org>
Signed-off-by: Simon Wunderlich <sw@simonwunderlich.de>
---
 net/batman-adv/bridge_loop_avoidance.c | 27 +++++++++++++++++---------
 1 file changed, 18 insertions(+), 9 deletions(-)

diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c
index 3dc791c15bf72..648fa97ea913f 100644
--- a/net/batman-adv/bridge_loop_avoidance.c
+++ b/net/batman-adv/bridge_loop_avoidance.c
@@ -2130,6 +2130,7 @@ batadv_bla_claim_dump_entry(struct sk_buff *msg, u32 portid,
 			    struct batadv_bla_claim *claim)
 {
 	const u8 *primary_addr = primary_if->net_dev->dev_addr;
+	struct batadv_bla_backbone_gw *backbone_gw;
 	u16 backbone_crc;
 	bool is_own;
 	void *hdr;
@@ -2145,32 +2146,35 @@ batadv_bla_claim_dump_entry(struct sk_buff *msg, u32 portid,
 
 	genl_dump_check_consistent(cb, hdr);
 
-	is_own = batadv_compare_eth(claim->backbone_gw->orig,
-				    primary_addr);
+	backbone_gw = batadv_bla_claim_get_backbone_gw(claim);
+
+	is_own = batadv_compare_eth(backbone_gw->orig, primary_addr);
 
-	spin_lock_bh(&claim->backbone_gw->crc_lock);
-	backbone_crc = claim->backbone_gw->crc;
-	spin_unlock_bh(&claim->backbone_gw->crc_lock);
+	spin_lock_bh(&backbone_gw->crc_lock);
+	backbone_crc = backbone_gw->crc;
+	spin_unlock_bh(&backbone_gw->crc_lock);
 
 	if (is_own)
 		if (nla_put_flag(msg, BATADV_ATTR_BLA_OWN)) {
 			genlmsg_cancel(msg, hdr);
-			goto out;
+			goto put_backbone_gw;
 		}
 
 	if (nla_put(msg, BATADV_ATTR_BLA_ADDRESS, ETH_ALEN, claim->addr) ||
 	    nla_put_u16(msg, BATADV_ATTR_BLA_VID, claim->vid) ||
 	    nla_put(msg, BATADV_ATTR_BLA_BACKBONE, ETH_ALEN,
-		    claim->backbone_gw->orig) ||
+		    backbone_gw->orig) ||
 	    nla_put_u16(msg, BATADV_ATTR_BLA_CRC,
 			backbone_crc)) {
 		genlmsg_cancel(msg, hdr);
-		goto out;
+		goto put_backbone_gw;
 	}
 
 	genlmsg_end(msg, hdr);
 	ret = 0;
 
+put_backbone_gw:
+	batadv_backbone_gw_put(backbone_gw);
 out:
 	return ret;
 }
@@ -2448,6 +2452,7 @@ int batadv_bla_backbone_dump(struct sk_buff *msg, struct netlink_callback *cb)
 bool batadv_bla_check_claim(struct batadv_priv *bat_priv,
 			    u8 *addr, unsigned short vid)
 {
+	struct batadv_bla_backbone_gw *backbone_gw;
 	struct batadv_bla_claim search_claim;
 	struct batadv_bla_claim *claim = NULL;
 	struct batadv_hard_iface *primary_if = NULL;
@@ -2470,9 +2475,13 @@ bool batadv_bla_check_claim(struct batadv_priv *bat_priv,
 	 * return false.
 	 */
 	if (claim) {
-		if (!batadv_compare_eth(claim->backbone_gw->orig,
+		backbone_gw = batadv_bla_claim_get_backbone_gw(claim);
+
+		if (!batadv_compare_eth(backbone_gw->orig,
 					primary_if->net_dev->dev_addr))
 			ret = false;
+
+		batadv_backbone_gw_put(backbone_gw);
 		batadv_claim_put(claim);
 	}
 
-- 
2.47.3


^ permalink raw reply related

* [PATCH iwl-net v2] ice: fix SMA and U.FL pin state changes affecting paired pin
From: Petr Oros @ 2026-04-08 11:05 UTC (permalink / raw)
  To: netdev
  Cc: Petr Oros, Tony Nguyen, Przemek Kitszel, Andrew Lunn,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Arkadiusz Kubalewski, Simon Horman, intel-wired-lan, linux-kernel

SMA and U.FL pins share physical signal paths in pairs (SMA1/U.FL1 and
SMA2/U.FL2) controlled by the PCA9575 GPIO expander.  Each pair can
only have one active pin at a time: SMA1 output and U.FL1 output share
the same CGU output, SMA2 input and U.FL2 input share the same CGU
input.  The PCA9575 register bits determine which connector in each
pair owns the signal path.

The driver does not account for this pairing in two places:

ice_dpll_ufl_pin_state_set() modifies PCA9575 bits and disables the
backing CGU pin without checking whether the U.FL pin is currently
active.  Disconnecting an already inactive U.FL pin flips bits that
the paired SMA pin relies on, breaking its connection.

ice_dpll_sma_direction_set() does not propagate direction changes to
the paired U.FL pin.  For SMA2/U.FL2 the ICE_SMA2_UFL2_RX_DIS bit is
never managed, so U.FL2 stays disconnected after SMA2 switches to
output.  For both pairs the backing CGU pin of the U.FL side is never
enabled when a direction change activates it, so userspace sees the
pin as disconnected even though the routing is correct.

Fix by guarding the U.FL disconnect path against inactive pins and by
updating the paired U.FL pin fully on SMA direction changes: manage
ICE_SMA2_UFL2_RX_DIS for the SMA2/U.FL2 pair and enable the backing
CGU pin whenever the peer becomes active.

Fixes: 2dd5d03c77e2 ("ice: redesign dpll sma/u.fl pins control")
Signed-off-by: Petr Oros <poros@redhat.com>
---
v2:
 - fix ice_dpll_sma_direction_set() to manage ICE_SMA2_UFL2_RX_DIS
   when SMA2 direction changes
 - enable paired U.FL backing CGU pin when direction change makes
   it active, so it reports as connected immediately
 - (both reported by Intel test on the SMA init and notification
   patch threads)
v1: https://lore.kernel.org/all/20260325151050.2081977-1-poros@redhat.com/
---
 drivers/net/ethernet/intel/ice/ice_dpll.c | 50 ++++++++++++++++++++++-
 1 file changed, 49 insertions(+), 1 deletion(-)

diff --git a/drivers/net/ethernet/intel/ice/ice_dpll.c b/drivers/net/ethernet/intel/ice/ice_dpll.c
index 498ec2c045f384..3f8cd5b8298b57 100644
--- a/drivers/net/ethernet/intel/ice/ice_dpll.c
+++ b/drivers/net/ethernet/intel/ice/ice_dpll.c
@@ -1171,6 +1171,8 @@ static int ice_dpll_sma_direction_set(struct ice_dpll_pin *p,
 				      enum dpll_pin_direction direction,
 				      struct netlink_ext_ack *extack)
 {
+	struct ice_dplls *d = &p->pf->dplls;
+	struct ice_dpll_pin *peer;
 	u8 data;
 	int ret;
 
@@ -1189,8 +1191,9 @@ static int ice_dpll_sma_direction_set(struct ice_dpll_pin *p,
 	case ICE_DPLL_PIN_SW_2_IDX:
 		if (direction == DPLL_PIN_DIRECTION_INPUT) {
 			data &= ~ICE_SMA2_DIR_EN;
+			data |= ICE_SMA2_UFL2_RX_DIS;
 		} else {
-			data &= ~ICE_SMA2_TX_EN;
+			data &= ~(ICE_SMA2_TX_EN | ICE_SMA2_UFL2_RX_DIS);
 			data |= ICE_SMA2_DIR_EN;
 		}
 		break;
@@ -1202,6 +1205,34 @@ static int ice_dpll_sma_direction_set(struct ice_dpll_pin *p,
 		ret = ice_dpll_pin_state_update(p->pf, p,
 						ICE_DPLL_PIN_TYPE_SOFTWARE,
 						extack);
+	if (ret)
+		return ret;
+
+	/* When a direction change activates the paired U.FL pin, enable
+	 * its backing CGU pin so the pin reports as connected. Without
+	 * this the U.FL routing is correct but the CGU pin stays disabled
+	 * and userspace sees the pin as disconnected.  Do not disable the
+	 * backing pin when U.FL becomes inactive because the SMA pin may
+	 * still be using it.
+	 */
+	peer = &d->ufl[p->idx];
+	if (peer->active) {
+		struct ice_dpll_pin *target;
+		enum ice_dpll_pin_type type;
+
+		if (peer->output) {
+			target = peer->output;
+			type = ICE_DPLL_PIN_TYPE_OUTPUT;
+		} else {
+			target = peer->input;
+			type = ICE_DPLL_PIN_TYPE_INPUT;
+		}
+		ret = ice_dpll_pin_enable(&p->pf->hw, target,
+					  d->eec.dpll_idx, type, extack);
+		if (!ret)
+			ret = ice_dpll_pin_state_update(p->pf, target,
+							type, extack);
+	}
 
 	return ret;
 }
@@ -1253,6 +1284,14 @@ ice_dpll_ufl_pin_state_set(const struct dpll_pin *pin, void *pin_priv,
 			data &= ~ICE_SMA1_MASK;
 			enable = true;
 		} else if (state == DPLL_PIN_STATE_DISCONNECTED) {
+			/* Skip if U.FL1 is not active, setting TX_EN
+			 * while DIR_EN is set would also deactivate
+			 * the paired SMA1 output.
+			 */
+			if (data & (ICE_SMA1_DIR_EN | ICE_SMA1_TX_EN)) {
+				ret = 0;
+				goto unlock;
+			}
 			data |= ICE_SMA1_TX_EN;
 			enable = false;
 		} else {
@@ -1267,6 +1306,15 @@ ice_dpll_ufl_pin_state_set(const struct dpll_pin *pin, void *pin_priv,
 			data &= ~ICE_SMA2_UFL2_RX_DIS;
 			enable = true;
 		} else if (state == DPLL_PIN_STATE_DISCONNECTED) {
+			/* Skip if U.FL2 is not active, setting
+			 * UFL2_RX_DIS could also disable the paired
+			 * SMA2 input.
+			 */
+			if (!(data & ICE_SMA2_DIR_EN) ||
+			    (data & ICE_SMA2_UFL2_RX_DIS)) {
+				ret = 0;
+				goto unlock;
+			}
 			data |= ICE_SMA2_UFL2_RX_DIS;
 			enable = false;
 		} else {
-- 
2.52.0


^ permalink raw reply related

* RE: [PATCH v4 net-next 12/14] net: dsa: netc: add more basic functions support
From: Wei Fang @ 2026-04-08 11:09 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: Claudiu Manoil, Vladimir Oltean, Clark Wang,
	andrew+netdev@lunn.ch, davem@davemloft.net, edumazet@google.com,
	pabeni@redhat.com, robh@kernel.org, krzk+dt@kernel.org,
	conor+dt@kernel.org, f.fainelli@gmail.com, Frank Li,
	chleroy@kernel.org, horms@kernel.org, linux@armlinux.org.uk,
	andrew@lunn.ch, netdev@vger.kernel.org,
	linux-kernel@vger.kernel.org, devicetree@vger.kernel.org,
	linuxppc-dev@lists.ozlabs.org,
	linux-arm-kernel@lists.infradead.org, imx@lists.linux.dev
In-Reply-To: <20260403011743.1795726-1-kuba@kernel.org>

> > +static int netc_add_standalone_vlan_entry(struct netc_switch *priv)
> > +{
> > +	u32 bitmap_stg = VFT_STG_ID(0) | netc_available_port_bitmap(priv);
> 
> Does putting all standalone ports into a single shared VLAN 0 break the DSA
> isolation requirements?
> 
> If a user manually adds a static FDB entry to a standalone port, it is
> programmed into VLAN 0. Because all standalone ports reside in this VLAN, a
> frame arriving on one standalone port with a matching destination MAC might
> be hardware-switched directly to another standalone port, bypassing the CPU.

I think the FDB entry can only be added to the CPU port if the port is a
standalone port not a bridge port. So this case will not be true.

> 
> [ ... ]
> 
> > +static int netc_port_set_fdb_entry(struct netc_port *np,
> > +				   const unsigned char *addr, u16 vid)
> > +{
> 
> [ ... ]
> 
> > +	/* If the entry already exists, but not on this port, we need to
> > +	 * update the port bitmap. In general, it should only be valid
> > +	 * for multicast or broadcast address.
> > +	 */
> > +	port_bitmap ^= BIT(port);
> > +	entry->cfge.port_bitmap = cpu_to_le32(port_bitmap);
> 
> Can this XOR operation cause multi-port floods during unicast MAC migrations?
> 
> When a unicast MAC address moves to a new port, the bridging layer issues an
> ADD for the new port. The XOR operation leaves the old port bit set alongside
> the new port bit. Does the driver need to differentiate between unicast and
> multicast addresses and explicitly overwrite the bitmap for unicast entries?

I think the old FDB entry should be removed first when a unicast MAC address
moves to a new port rather than only adding a new entry. If AI refers to adding
before deleting, then this problem will indeed occur, and it is unavoidable.

> 
> [ ... ]
> 
> > +static int netc_port_del_fdb_entry(struct netc_port *np,
> > +				   const unsigned char *addr, u16 vid)
> > +{
> > +	struct netc_switch *priv = np->switch_priv;
> > +	struct ntmp_user *ntmp = &priv->ntmp;
> > +	struct netc_fdb_entry *entry;
> > +	int port = np->dp->index;
> > +	u32 port_bitmap;
> > +	int err = 0;
> > +
> > +	mutex_lock(&priv->fdbt_lock);
> > +
> > +	entry = netc_lookup_fdb_entry(priv, addr, vid);
> > +	if (unlikely(!entry))
> > +		goto unlock_fdbt;
> 
> Will this prevent dynamically learned FDB entries from being deleted?
> 
> Since the CPU port is configured with MLO_HW, the switch hardware

Currently we only support single port mode, so only support static FDB
entry. The MLO_HW should be removed from the code, I will improve
the logic.

> dynamically
> learns MAC addresses, but they are not added to the software priv->fdb_list.
> If a user attempts to delete a dynamic entry, the software lookup will fail
> here and return 0 (success), leaving the stale dynamically learned entry
> active in hardware.
> 
> [ ... ]
> 
> > +static int netc_port_change_mtu(struct dsa_switch *ds,
> > +				int port, int mtu)
> > +{
> > +	u32 max_frame_size = mtu + VLAN_ETH_HLEN + ETH_FCS_LEN;
> > +	struct netc_port *np = NETC_PORT(ds, port);
> > +
> > +	if (dsa_is_cpu_port(ds, port))
> > +		max_frame_size += NETC_TAG_MAX_LEN;
> 
> Does this double-count the DSA tag overhead for the CPU port?
> 
> The DSA core computes the CPU port's MTU by adding the tagger overhead
> before
> calling the switch's .port_change_mtu() function. If the maximum MTU of 9582
> bytes is requested, the CPU port receives an MTU of 9596. Adding
> NETC_TAG_MAX_LEN again here results in 9628 bytes, which exceeds the
> hardware limit of 9600 (NETC_MAX_FRAME_LEN) and might lead to truncation.

It seems a bug, I will fix it.

> 
> [ ... ]
> 
> > +static int netc_port_fdb_add(struct dsa_switch *ds, int port,
> > +			     const unsigned char *addr, u16 vid,
> > +			     struct dsa_db db)
> > +{
> > +	struct netc_port *np = NETC_PORT(ds, port);
> > +
> > +	/* Currently, we only support standalone port mode, so all VLANs
> > +	 * should be converted to NETC_STANDALONE_PVID.
> > +	 */
> > +	return netc_port_set_fdb_entry(np, addr, NETC_STANDALONE_PVID);
> > +}
> 
> Is it safe to silently override user-requested VLAN IDs to VLAN 0?
> 
> If a user explicitly attempts to configure a MAC address for a specific VLAN,
> the hardware is programmed for VLAN 0 and returns success. Should this
> instead check if the requested vid is not 0 and not NETC_STANDALONE_PVID,
> and return -EOPNOTSUPP to inform the networking stack?

Okay, I will add a check.

> 
> [ ... ]
> 
> > +static void netc_port_set_host_flood(struct dsa_switch *ds, int port,
> > +				     bool uc, bool mc)
> > +{
> > +	struct netc_port *np = NETC_PORT(ds, port);
> > +
> > +	if (np->uc == uc && np->mc == mc)
> > +		return;
> > +
> > +	/* IPFT does not support in-place updates to the KEYE element,
> > +	 * so we need to delete the old IPFT entry and then add a new
> > +	 * one.
> > +	 */
> > +	if (np->host_flood)
> > +		netc_port_remove_host_flood(np);
> > +
> > +	if (netc_port_add_host_flood_rule(np, uc, mc))
> > +		dev_err(ds->dev, "Failed to add host flood rule on port %d\n",
> > +			port);
> > +}
> 
> Could this permanently lose the host flood configuration if the new rule
> addition fails?
> 
> If the call to netc_port_add_host_flood_rule() fails (for example, due to
> -ENOMEM), the function logs an error and returns, leaving the port entirely
> without a host flood rule. Should the driver revert to the previous
> configuration on failure to avoid blackholing traffic meant for the CPU?

Okay, I will improve it.


^ permalink raw reply

* [PATCH net 0/2] pull request: batman-adv 2025-04-08
From: Simon Wunderlich @ 2026-04-08 11:02 UTC (permalink / raw)
  To: davem, kuba; +Cc: netdev, b.a.t.m.a.n, Simon Wunderlich

Hi David, hi Jakub,

here are two bugfixes for batman-adv which we would like to have integrated into net.

Please pull or let me know of any problem!

Thank you,
      Simon

The following changes since commit 0d4aef630be9d5f9c1227d07669c26c4383b5ad0:

  batman-adv: avoid OGM aggregation when skb tailroom is insufficient (2026-03-14 08:29:47 +0100)

are available in the Git repository at:

  https://git.open-mesh.org/linux-merge.git tags/batadv-net-pullrequest-20260408

for you to fetch changes up to 82d8701b2c930d0e96b0dbc9115a218d791cb0d2:

  batman-adv: hold claim backbone gateways by reference (2026-04-06 15:42:29 +0200)

----------------------------------------------------------------
Here are two batman-adv bugfixes:

 - reject oversized global TT response buffers, by Ruide Cao

 - hold claim backbone gateways by reference, by Haoze Xie

----------------------------------------------------------------
Haoze Xie (1):
      batman-adv: hold claim backbone gateways by reference

Ruide Cao (1):
      batman-adv: reject oversized global TT response buffers

 net/batman-adv/bridge_loop_avoidance.c | 27 ++++++++++++++++++---------
 net/batman-adv/translation-table.c     |  9 +++++++--
 2 files changed, 25 insertions(+), 11 deletions(-)

^ permalink raw reply

* [PATCH net 1/2] batman-adv: reject oversized global TT response buffers
From: Simon Wunderlich @ 2026-04-08 11:02 UTC (permalink / raw)
  To: davem, kuba
  Cc: netdev, b.a.t.m.a.n, Ruide Cao, stable, Yifan Wu, Juefei Pu,
	Yuan Tan, Xin Liu, Ren Wei, Ren Wei, Sven Eckelmann,
	Simon Wunderlich
In-Reply-To: <20260408110255.976389-1-sw@simonwunderlich.de>

From: Ruide Cao <caoruide123@gmail.com>

batadv_tt_prepare_tvlv_global_data() builds the allocation length for a
global TT response in 16-bit temporaries. When a remote originator
advertises a large enough global TT, the TT payload length plus the VLAN
header offset can exceed 65535 and wrap before kmalloc().

The full-table response path still uses the original TT payload length when
it fills tt_change, so the wrapped allocation is too small and
batadv_tt_prepare_tvlv_global_data() writes past the end of the heap object
before the later packet-size check runs.

Fix this by rejecting TT responses whose TVLV value length cannot fit in
the 16-bit TVLV payload length field.

Fixes: 7ea7b4a14275 ("batman-adv: make the TT CRC logic VLAN specific")
Cc: stable@vger.kernel.org
Reported-by: Yifan Wu <yifanwucs@gmail.com>
Reported-by: Juefei Pu <tomapufckgml@gmail.com>
Co-developed-by: Yuan Tan <yuantan098@gmail.com>
Signed-off-by: Yuan Tan <yuantan098@gmail.com>
Suggested-by: Xin Liu <bird@lzu.edu.cn>
Tested-by: Ren Wei <enjou1224z@gmail.com>
Signed-off-by: Ruide Cao <caoruide123@gmail.com>
Signed-off-by: Ren Wei <n05ec@lzu.edu.cn>
Signed-off-by: Sven Eckelmann <sven@narfation.org>
Signed-off-by: Simon Wunderlich <sw@simonwunderlich.de>
---
 net/batman-adv/translation-table.c | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c
index 6e95e883c2bf0..05cddcf994f65 100644
--- a/net/batman-adv/translation-table.c
+++ b/net/batman-adv/translation-table.c
@@ -798,8 +798,8 @@ batadv_tt_prepare_tvlv_global_data(struct batadv_orig_node *orig_node,
 {
 	u16 num_vlan = 0;
 	u16 num_entries = 0;
-	u16 change_offset;
-	u16 tvlv_len;
+	u16 tvlv_len = 0;
+	unsigned int change_offset;
 	struct batadv_tvlv_tt_vlan_data *tt_vlan;
 	struct batadv_orig_node_vlan *vlan;
 	u8 *tt_change_ptr;
@@ -816,6 +816,11 @@ batadv_tt_prepare_tvlv_global_data(struct batadv_orig_node *orig_node,
 	if (*tt_len < 0)
 		*tt_len = batadv_tt_len(num_entries);
 
+	if (change_offset > U16_MAX || *tt_len > U16_MAX - change_offset) {
+		*tt_len = 0;
+		goto out;
+	}
+
 	tvlv_len = *tt_len;
 	tvlv_len += change_offset;
 
-- 
2.47.3


^ permalink raw reply related

* RE: [PATCH v4 net-next 13/14] net: dsa: netc: initialize buffer bool table and implement flow-control
From: Wei Fang @ 2026-04-08 11:16 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: Claudiu Manoil, Vladimir Oltean, Clark Wang,
	andrew+netdev@lunn.ch, davem@davemloft.net, edumazet@google.com,
	pabeni@redhat.com, robh@kernel.org, krzk+dt@kernel.org,
	conor+dt@kernel.org, f.fainelli@gmail.com, Frank Li,
	chleroy@kernel.org, horms@kernel.org, linux@armlinux.org.uk,
	andrew@lunn.ch, netdev@vger.kernel.org,
	linux-kernel@vger.kernel.org, devicetree@vger.kernel.org,
	linuxppc-dev@lists.ozlabs.org,
	linux-arm-kernel@lists.infradead.org, imx@lists.linux.dev
In-Reply-To: <20260403011745.1795759-1-kuba@kernel.org>

> > +static u32 netc_get_buffer_pool_num(struct netc_switch *priv)
> > +{
> > +	/* The BPCAPR register is a read only register, the hardware
> > +	 * guarantees that the BPCAPR_NUM_BP field will not be 0.
> > +	 */
> > +	return netc_base_rd(&priv->regs, NETC_BPCAPR) & BPCAPR_NUM_BP;
> > +}
> [ ... ]
> > +static void netc_ipv_to_buffer_pool_mapping(struct netc_switch *priv)
> > +{
> > +	int bp_per_port = priv->num_bp / priv->info->num_ports;
> > +	int q = NETC_IPV_NUM / bp_per_port;
> > +	int r = NETC_IPV_NUM % bp_per_port;
> 
> If the hardware register read in netc_get_buffer_pool_num() returns a value
> smaller than priv->info->num_ports (for instance, during a bus error or
> reset state), bp_per_port will evaluate to 0.
> 
> Can this lead to a division by zero panic here?
> 
> Should priv->num_bp be validated against the number of ports before
> performing this calculation to ensure the driver fails gracefully if the
> hardware returns an unexpected value?

If the hardware is so unstable and prone to such malfunctions, that would
be terrible, but I believe it won't. The AI ​​review is being a bit too nitpicky.
Anyway, I will ll add this check to avoid AI review warnings.


^ permalink raw reply

* [net-next v1 v1 4/5] net: stmmac: starfive: Add JHB100 SGMII interface
From: Minda Chen @ 2026-04-08  8:44 UTC (permalink / raw)
  To: Alexandre Torgue, Andrew Lunn, David S . Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Maxime Coquelin,
	Emil Renner Berthing, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, netdev
  Cc: linux-kernel, linux-stm32, devicetree, Minda Chen
In-Reply-To: <20260408084416.29753-1-minda.chen@starfivetech.com>

Add JHB100 compatible and SGMII support. JHB100 soc contains
2 SGMII interfaces and integrated with serdes PHY. SGMII with
split TX/RX MAC clock and need to set 2.5M/25M/125M TX/RX clock
rate in 10M/100M/1000M speed mode.

Signed-off-by: Minda Chen <minda.chen@starfivetech.com>
---
 .../ethernet/stmicro/stmmac/dwmac-starfive.c  | 36 +++++++++++++++++--
 1 file changed, 34 insertions(+), 2 deletions(-)

diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-starfive.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-starfive.c
index 16b955a6d77b..df7b2fc9989d 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-starfive.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-starfive.c
@@ -26,6 +26,7 @@ struct starfive_dwmac_data {
 struct starfive_dwmac {
 	struct device *dev;
 	const struct starfive_dwmac_data *data;
+	struct clk *sgmii_rx;
 };
 
 static int starfive_dwmac_set_mode(struct plat_stmmacenet_data *plat_dat)
@@ -36,6 +37,9 @@ static int starfive_dwmac_set_mode(struct plat_stmmacenet_data *plat_dat)
 	int phy_intf_sel;
 	int err;
 
+	if (plat_dat->phy_interface == PHY_INTERFACE_MODE_SGMII)
+		return 0;
+
 	phy_intf_sel = stmmac_get_phy_intf_sel(plat_dat->phy_interface);
 	if (phy_intf_sel != PHY_INTF_SEL_RGMII &&
 	    phy_intf_sel != PHY_INTF_SEL_RMII) {
@@ -68,6 +72,24 @@ static int starfive_dwmac_set_mode(struct plat_stmmacenet_data *plat_dat)
 	return 0;
 }
 
+static int stmmac_starfive_sgmii_set_clk_rate(void *bsp_priv, struct clk *clk_tx_i,
+					      phy_interface_t interface, int speed)
+{
+	struct starfive_dwmac *dwmac = (void *)bsp_priv;
+	long rate = rgmii_clock(speed);
+	int ret;
+
+	/* MAC clock rate the same as RGMII */
+	if (rate < 0)
+		return 0;
+
+	ret = clk_set_rate(clk_tx_i, rate);
+	if (ret)
+		return ret;
+
+	return clk_set_rate(dwmac->sgmii_rx, rate);
+}
+
 static int starfive_dwmac_probe(struct platform_device *pdev)
 {
 	struct plat_stmmacenet_data *plat_dat;
@@ -102,14 +124,23 @@ static int starfive_dwmac_probe(struct platform_device *pdev)
 		return dev_err_probe(&pdev->dev, PTR_ERR(clk_gtx),
 				     "error getting gtx clock\n");
 
+	dwmac->sgmii_rx = devm_clk_get_optional(&pdev->dev, "rx");
+	if (IS_ERR(dwmac->sgmii_rx))
+		return dev_err_probe(&pdev->dev, PTR_ERR(dwmac->sgmii_rx),
+				     "error getting sgmii rx clock\n");
+
 	/* Generally, the rgmii_tx clock is provided by the internal clock,
 	 * which needs to match the corresponding clock frequency according
 	 * to different speeds. If the rgmii_tx clock is provided by the
 	 * external rgmii_rxin, there is no need to configure the clock
 	 * internally, because rgmii_rxin will be adaptively adjusted.
 	 */
-	if (!device_property_read_bool(&pdev->dev, "starfive,tx-use-rgmii-clk"))
-		plat_dat->set_clk_tx_rate = stmmac_set_clk_tx_rate;
+	if (!device_property_read_bool(&pdev->dev, "starfive,tx-use-rgmii-clk")) {
+		if (plat_dat->phy_interface == PHY_INTERFACE_MODE_SGMII)
+			plat_dat->set_clk_tx_rate = stmmac_starfive_sgmii_set_clk_rate;
+		else
+			plat_dat->set_clk_tx_rate = stmmac_set_clk_tx_rate;
+	}
 
 	dwmac->dev = &pdev->dev;
 	plat_dat->flags |= STMMAC_FLAG_EN_TX_LPI_CLK_PHY_CAP;
@@ -130,6 +161,7 @@ static const struct starfive_dwmac_data jh7100_data = {
 static const struct of_device_id starfive_dwmac_match[] = {
 	{ .compatible = "starfive,jh7100-dwmac", .data = &jh7100_data },
 	{ .compatible = "starfive,jh7110-dwmac" },
+	{ .compatible = "starfive,jhb100-dwmac" },
 	{ /* sentinel */ }
 };
 MODULE_DEVICE_TABLE(of, starfive_dwmac_match);
-- 
2.17.1


^ permalink raw reply related

* Re: [PATCH net-next v3 0/4] net: move .getsockopt away from __user buffers
From: David Laight @ 2026-04-08 11:26 UTC (permalink / raw)
  To: Breno Leitao
  Cc: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Kuniyuki Iwashima, Willem de Bruijn, metze, axboe,
	Stanislav Fomichev, io-uring, bpf, netdev, Linus Torvalds,
	linux-kernel, kernel-team
In-Reply-To: <20260408-getsockopt-v3-0-061bb9cb355d@debian.org>

On Wed, 08 Apr 2026 03:30:28 -0700
Breno Leitao <leitao@debian.org> wrote:

> Currently, the .getsockopt callback requires __user pointers:
> 
>   int (*getsockopt)(struct socket *sock, int level,
>                     int optname, char __user *optval, int __user *optlen);
> 
> This prevents kernel callers (io_uring, BPF) from using getsockopt on
> levels other than SOL_SOCKET, since they pass kernel pointers.
> 
> Following Linus' suggestion [0], this series introduces sockopt_t, a
> type-safe wrapper around iov_iter, and a getsockopt_iter callback that
> works with both user and kernel buffers. AF_PACKET and CAN raw are
> converted as initial users, with selftests covering the trickiest
> conversion patterns.

What are you doing about the cases where 'optlen' is a complete lie?
IIRC there is one related to some form of async io where it is just
the length of the header, the actual buffer length depends on
data in the header.
This doesn't matter with the existing code for applications, when they
get it wrong they just crash.
But kernel users will need to pass the actual buffer length separately
from optlen.
It also affects any code that tries to cache the actual data and copy
it back to userspace in the syscall wrapper - which makes sense for
most short getsockopt.

(This is different from historic code where the length might be
assumed to be 4 regardless of what was passed.)

	David

^ permalink raw reply

* Re: [PATCH v2] rxrpc/proc: size address buffers for %pISpc output
From: David Howells @ 2026-04-08 11:40 UTC (permalink / raw)
  To: Pengpeng Hou
  Cc: dhowells, Marc Dionne, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, linux-afs, netdev, linux-kernel
In-Reply-To: <20260406170001.2-rxrpc-proc-v2-pengpeng@iscas.ac.cn>

I'm going to add:

    Fixes: 75b54cb57ca3 ("rxrpc: Add IPv6 support")

and change the subject to "rxrpc: proc:" rather than "rxrpc/proc:"

David


^ permalink raw reply

* [PATCH iwl-next 0/2] i40e: implement per-queue stats
From: Paolo Abeni @ 2026-04-08 11:43 UTC (permalink / raw)
  To: intel-wired-lan
  Cc: Tony Nguyen, Przemek Kitszel, Andrew Lunn, David S. Miller,
	Eric Dumazet, Jakub Kicinski, Alexei Starovoitov, Daniel Borkmann,
	Jesper Dangaard Brouer, John Fastabend, Stanislav Fomichev,
	netdev

The i40e driver already collects some per queue statistics, but does
not expose them to the user-space using the standard interface.

Implement the stat_ops callbacks and extends the already collected info
with basic GSO counters. Overall this allows passing the kernel NIC
drivers TSO test cases.

Paolo Abeni (2):
  i40e: implement basic per-queue stats
  i40e: keep track of per queue gso counters.

 drivers/net/ethernet/intel/i40e/i40e.h        |   9 ++
 drivers/net/ethernet/intel/i40e/i40e_main.c   | 144 ++++++++++++++++++
 drivers/net/ethernet/intel/i40e/i40e_txrx.c   |   8 +-
 drivers/net/ethernet/intel/i40e/i40e_txrx.h   |   2 +
 .../ethernet/intel/i40e/i40e_txrx_common.h    |   6 +-
 drivers/net/ethernet/intel/i40e/i40e_xsk.c    |   2 +-
 6 files changed, 168 insertions(+), 3 deletions(-)

-- 
2.53.0


^ permalink raw reply

* [PATCH iwl-next 1/2] i40e: implement basic per-queue stats
From: Paolo Abeni @ 2026-04-08 11:43 UTC (permalink / raw)
  To: intel-wired-lan
  Cc: Tony Nguyen, Przemek Kitszel, Andrew Lunn, David S. Miller,
	Eric Dumazet, Jakub Kicinski, Alexei Starovoitov, Daniel Borkmann,
	Jesper Dangaard Brouer, John Fastabend, Stanislav Fomichev,
	netdev
In-Reply-To: <cover.1775648513.git.pabeni@redhat.com>

Only expose the counters currently available (bytes, packets); add
account for base stats to deal with ring clear.

Signed-off-by: Paolo Abeni <pabeni@redhat.com>
---
 drivers/net/ethernet/intel/i40e/i40e.h      |   7 ++
 drivers/net/ethernet/intel/i40e/i40e_main.c | 133 ++++++++++++++++++++
 2 files changed, 140 insertions(+)

diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h
index dcb50c2e1aa2..fe642c464e9c 100644
--- a/drivers/net/ethernet/intel/i40e/i40e.h
+++ b/drivers/net/ethernet/intel/i40e/i40e.h
@@ -836,16 +836,23 @@ struct i40e_vsi {
 	struct i40e_eth_stats eth_stats;
 	struct i40e_eth_stats eth_stats_offsets;
 	u64 tx_restart;
+	u64 tx_restart_base;
 	u64 tx_busy;
+	u64 tx_busy_base;
 	u64 tx_linearize;
 	u64 tx_force_wb;
 	u64 tx_stopped;
+	u64 tx_stopped_base;
+	u64 tx_bytes;
+	u64 tx_packets;
 	u64 rx_buf_failed;
 	u64 rx_page_failed;
 	u64 rx_page_reuse;
 	u64 rx_page_alloc;
 	u64 rx_page_waive;
 	u64 rx_page_busy;
+	u64 rx_bytes;
+	u64 rx_packets;
 
 	/* These are containers of ring pointers, allocated at run-time */
 	struct i40e_ring **rx_rings;
diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c
index 926d001b2150..0d0b3619ec56 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_main.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
@@ -7,6 +7,7 @@
 #include <linux/if_bridge.h>
 #include <linux/if_macvlan.h>
 #include <linux/module.h>
+#include <net/netdev_queues.h>
 #include <net/pkt_cls.h>
 #include <net/xdp_sock_drv.h>
 
@@ -11686,6 +11687,26 @@ static int i40e_vsi_clear(struct i40e_vsi *vsi)
 	return 0;
 }
 
+static void i40e_vsi_aggregate_tx_counters(struct i40e_vsi *vsi,
+					   struct i40e_ring *tx_ring)
+{
+	if (!tx_ring)
+		return;
+
+	vsi->tx_bytes += tx_ring->stats.bytes;
+	vsi->tx_packets += tx_ring->stats.packets;
+}
+
+static void i40e_vsi_aggregate_rx_counters(struct i40e_vsi *vsi,
+					   struct i40e_ring *rx_ring)
+{
+	if (!rx_ring)
+		return;
+
+	vsi->rx_bytes += rx_ring->stats.bytes;
+	vsi->rx_packets += rx_ring->stats.packets;
+}
+
 /**
  * i40e_vsi_clear_rings - Deallocates the Rx and Tx rings for the provided VSI
  * @vsi: the VSI being cleaned
@@ -11696,6 +11717,13 @@ static void i40e_vsi_clear_rings(struct i40e_vsi *vsi)
 
 	if (vsi->tx_rings && vsi->tx_rings[0]) {
 		for (i = 0; i < vsi->alloc_queue_pairs; i++) {
+			struct i40e_ring *xdp_ring = vsi->xdp_rings ?
+						     vsi->xdp_rings[i] : NULL;
+
+			i40e_vsi_aggregate_tx_counters(vsi, vsi->tx_rings[i]);
+			i40e_vsi_aggregate_tx_counters(vsi, xdp_ring);
+			i40e_vsi_aggregate_rx_counters(vsi, vsi->rx_rings[i]);
+
 			kfree_rcu(vsi->tx_rings[i], rcu);
 			WRITE_ONCE(vsi->tx_rings[i], NULL);
 			WRITE_ONCE(vsi->rx_rings[i], NULL);
@@ -13625,6 +13653,110 @@ static const struct net_device_ops i40e_netdev_ops = {
 	.ndo_hwtstamp_set	= i40e_ptp_hwtstamp_set,
 };
 
+static void i40e_get_queue_stats_rx(struct net_device *dev, int idx,
+				    struct netdev_queue_stats_rx *rx)
+{
+	struct i40e_netdev_priv *np = netdev_priv(dev);
+	struct i40e_vsi *vsi = np->vsi;
+	struct i40e_ring *rx_ring;
+	unsigned int start;
+	u64 bytes, packets;
+
+	rcu_read_lock();
+	rx_ring = READ_ONCE(vsi->rx_rings[idx]);
+	if (!rx_ring)
+		goto unlock;
+
+	do {
+		start = u64_stats_fetch_begin(&rx_ring->syncp);
+		bytes = rx_ring->stats.bytes;
+		packets = rx_ring->stats.packets;
+	} while (u64_stats_fetch_retry(&rx_ring->syncp, start));
+
+	rx->bytes = bytes;
+	rx->packets = packets;
+
+unlock:
+	rcu_read_unlock();
+}
+
+static void i40e_zero_tx_ring_stats(struct netdev_queue_stats_tx *tx)
+{
+	tx->bytes = 0;
+	tx->packets = 0;
+	tx->stop = 0;
+	tx->wake = 0;
+	tx->hw_drops = 0;
+}
+
+static void i40e_add_tx_ring_stats(struct i40e_ring *tx_ring,
+				   struct netdev_queue_stats_tx *tx)
+{
+	u64 bytes, packets;
+	unsigned int start;
+
+	do {
+		start = u64_stats_fetch_begin(&tx_ring->syncp);
+		bytes = tx_ring->stats.bytes;
+		packets = tx_ring->stats.packets;
+	} while (u64_stats_fetch_retry(&tx_ring->syncp, start));
+
+	tx->bytes += bytes;
+	tx->packets += packets;
+
+	tx->stop += tx_ring->tx_stats.tx_stopped;
+	tx->wake += tx_ring->tx_stats.restart_queue;
+	tx->hw_drops += tx_ring->tx_stats.tx_busy;
+}
+
+static void i40e_get_queue_stats_tx(struct net_device *dev, int idx,
+				    struct netdev_queue_stats_tx *tx)
+{
+	struct i40e_netdev_priv *np = netdev_priv(dev);
+	struct i40e_vsi *vsi = np->vsi;
+	struct i40e_ring *tx_ring;
+
+	rcu_read_lock();
+	tx_ring = READ_ONCE(vsi->tx_rings[idx]);
+	if (!tx_ring)
+		goto out;
+
+	i40e_zero_tx_ring_stats(tx);
+	i40e_add_tx_ring_stats(tx_ring, tx);
+
+	if (i40e_enabled_xdp_vsi(vsi)) {
+		tx_ring = READ_ONCE(vsi->xdp_rings[idx]);
+		if (tx_ring)
+			i40e_add_tx_ring_stats(tx_ring, tx);
+	}
+
+out:
+	rcu_read_unlock();
+}
+
+static void i40e_get_base_stats(struct net_device *dev,
+				struct netdev_queue_stats_rx *rx,
+				struct netdev_queue_stats_tx *tx)
+{
+	struct i40e_netdev_priv *np = netdev_priv(dev);
+	struct i40e_vsi *vsi = np->vsi;
+
+	tx->bytes = vsi->tx_bytes;
+	tx->packets = vsi->tx_packets;
+	tx->wake = vsi->tx_restart_base;
+	tx->stop = vsi->tx_stopped_base;
+	tx->hw_drops = vsi->tx_busy_base;
+
+	rx->bytes = vsi->rx_bytes;
+	rx->packets = vsi->rx_packets;
+}
+
+static const struct netdev_stat_ops i40e_stat_ops = {
+	.get_queue_stats_rx	= i40e_get_queue_stats_rx,
+	.get_queue_stats_tx	= i40e_get_queue_stats_tx,
+	.get_base_stats		= i40e_get_base_stats,
+};
+
 /**
  * i40e_config_netdev - Setup the netdev flags
  * @vsi: the VSI being configured
@@ -13787,6 +13919,7 @@ static int i40e_config_netdev(struct i40e_vsi *vsi)
 	/* Setup netdev TC information */
 	i40e_vsi_config_netdev_tc(vsi, vsi->tc_config.enabled_tc);
 
+	netdev->stat_ops = &i40e_stat_ops;
 	netdev->netdev_ops = &i40e_netdev_ops;
 	netdev->watchdog_timeo = 5 * HZ;
 	i40e_set_ethtool_ops(netdev);
-- 
2.53.0


^ permalink raw reply related

* [PATCH iwl-next 2/2] i40e: keep track of per queue gso counters.
From: Paolo Abeni @ 2026-04-08 11:43 UTC (permalink / raw)
  To: intel-wired-lan
  Cc: Tony Nguyen, Przemek Kitszel, Andrew Lunn, David S. Miller,
	Eric Dumazet, Jakub Kicinski, Alexei Starovoitov, Daniel Borkmann,
	Jesper Dangaard Brouer, John Fastabend, Stanislav Fomichev,
	netdev
In-Reply-To: <cover.1775648513.git.pabeni@redhat.com>

Track the number of GSO and wire packets transmitted and expose the
counters via the queue stats.

Signed-off-by: Paolo Abeni <pabeni@redhat.com>
---
 drivers/net/ethernet/intel/i40e/i40e.h             |  2 ++
 drivers/net/ethernet/intel/i40e/i40e_main.c        | 13 ++++++++++++-
 drivers/net/ethernet/intel/i40e/i40e_txrx.c        |  8 +++++++-
 drivers/net/ethernet/intel/i40e/i40e_txrx.h        |  2 ++
 drivers/net/ethernet/intel/i40e/i40e_txrx_common.h |  6 +++++-
 drivers/net/ethernet/intel/i40e/i40e_xsk.c         |  2 +-
 6 files changed, 29 insertions(+), 4 deletions(-)

diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h
index fe642c464e9c..4a88c7d69f61 100644
--- a/drivers/net/ethernet/intel/i40e/i40e.h
+++ b/drivers/net/ethernet/intel/i40e/i40e.h
@@ -845,6 +845,8 @@ struct i40e_vsi {
 	u64 tx_stopped_base;
 	u64 tx_bytes;
 	u64 tx_packets;
+	u64 tx_gso_packets;
+	u64 tx_gso_wire_packets;
 	u64 rx_buf_failed;
 	u64 rx_page_failed;
 	u64 rx_page_reuse;
diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c
index 0d0b3619ec56..7c2af738ccc5 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_main.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
@@ -11695,6 +11695,8 @@ static void i40e_vsi_aggregate_tx_counters(struct i40e_vsi *vsi,
 
 	vsi->tx_bytes += tx_ring->stats.bytes;
 	vsi->tx_packets += tx_ring->stats.packets;
+	vsi->tx_gso_packets += tx_ring->tx_stats.tx_gso_packets;
+	vsi->tx_gso_wire_packets += tx_ring->tx_stats.tx_gso_wire_packets;
 }
 
 static void i40e_vsi_aggregate_rx_counters(struct i40e_vsi *vsi,
@@ -13684,6 +13686,8 @@ static void i40e_zero_tx_ring_stats(struct netdev_queue_stats_tx *tx)
 {
 	tx->bytes = 0;
 	tx->packets = 0;
+	tx->hw_gso_packets = 0;
+	tx->hw_gso_wire_packets = 0;
 	tx->stop = 0;
 	tx->wake = 0;
 	tx->hw_drops = 0;
@@ -13692,17 +13696,21 @@ static void i40e_zero_tx_ring_stats(struct netdev_queue_stats_tx *tx)
 static void i40e_add_tx_ring_stats(struct i40e_ring *tx_ring,
 				   struct netdev_queue_stats_tx *tx)
 {
-	u64 bytes, packets;
+	u64 bytes, packets, gso_packets, gso_wire_packets;
 	unsigned int start;
 
 	do {
 		start = u64_stats_fetch_begin(&tx_ring->syncp);
 		bytes = tx_ring->stats.bytes;
 		packets = tx_ring->stats.packets;
+		gso_packets = tx_ring->tx_stats.tx_gso_packets;
+		gso_wire_packets = tx_ring->tx_stats.tx_gso_wire_packets;
 	} while (u64_stats_fetch_retry(&tx_ring->syncp, start));
 
 	tx->bytes += bytes;
 	tx->packets += packets;
+	tx->hw_gso_packets += gso_packets;
+	tx->hw_gso_wire_packets += gso_wire_packets;
 
 	tx->stop += tx_ring->tx_stats.tx_stopped;
 	tx->wake += tx_ring->tx_stats.restart_queue;
@@ -13743,6 +13751,9 @@ static void i40e_get_base_stats(struct net_device *dev,
 
 	tx->bytes = vsi->tx_bytes;
 	tx->packets = vsi->tx_packets;
+	tx->hw_gso_packets = vsi->tx_gso_packets;
+	tx->hw_gso_wire_packets = vsi->tx_gso_wire_packets;
+
 	tx->wake = vsi->tx_restart_base;
 	tx->stop = vsi->tx_stopped_base;
 	tx->hw_drops = vsi->tx_busy_base;
diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c
index 894f2d06d39d..ec61cc43ae39 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c
@@ -928,6 +928,7 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi,
 			      struct i40e_ring *tx_ring, int napi_budget,
 			      unsigned int *tx_cleaned)
 {
+	unsigned int total_gso_packets = 0, total_gso_wire_packets = 0;
 	int i = tx_ring->next_to_clean;
 	struct i40e_tx_buffer *tx_buf;
 	struct i40e_tx_desc *tx_head;
@@ -959,6 +960,10 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi,
 		/* update the statistics for this packet */
 		total_bytes += tx_buf->bytecount;
 		total_packets += tx_buf->gso_segs;
+		if (tx_buf->gso_segs > 1) {
+			total_gso_packets++;
+			total_gso_wire_packets += tx_buf->gso_segs;
+		}
 
 		/* free the skb/XDP data */
 		if (ring_is_xdp(tx_ring))
@@ -1018,7 +1023,8 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi,
 
 	i += tx_ring->count;
 	tx_ring->next_to_clean = i;
-	i40e_update_tx_stats(tx_ring, total_packets, total_bytes);
+	i40e_update_tx_stats(tx_ring, total_packets, total_bytes,
+			     total_gso_packets, total_gso_wire_packets);
 	i40e_arm_wb(tx_ring, vsi, budget);
 
 	if (ring_is_xdp(tx_ring))
diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.h b/drivers/net/ethernet/intel/i40e/i40e_txrx.h
index 1e5fd63d47f4..15c608378467 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_txrx.h
+++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.h
@@ -295,6 +295,8 @@ struct i40e_tx_queue_stats {
 	u64 tx_linearize;
 	u64 tx_force_wb;
 	u64 tx_stopped;
+	u64 tx_gso_packets;
+	u64 tx_gso_wire_packets;
 	int prev_pkt_ctr;
 };
 
diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx_common.h b/drivers/net/ethernet/intel/i40e/i40e_txrx_common.h
index e26807fd2123..24220594ff1a 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_txrx_common.h
+++ b/drivers/net/ethernet/intel/i40e/i40e_txrx_common.h
@@ -45,11 +45,15 @@ static inline __le64 build_ctob(u32 td_cmd, u32 td_offset, unsigned int size,
  **/
 static inline void i40e_update_tx_stats(struct i40e_ring *tx_ring,
 					unsigned int total_packets,
-					unsigned int total_bytes)
+					unsigned int total_bytes,
+					unsigned int total_gso,
+					unsigned int total_gso_wire)
 {
 	u64_stats_update_begin(&tx_ring->syncp);
 	tx_ring->stats.bytes += total_bytes;
 	tx_ring->stats.packets += total_packets;
+	tx_ring->tx_stats.tx_gso_packets += total_gso;
+	tx_ring->tx_stats.tx_gso_wire_packets += total_gso_wire;
 	u64_stats_update_end(&tx_ring->syncp);
 	tx_ring->q_vector->tx.total_bytes += total_bytes;
 	tx_ring->q_vector->tx.total_packets += total_packets;
diff --git a/drivers/net/ethernet/intel/i40e/i40e_xsk.c b/drivers/net/ethernet/intel/i40e/i40e_xsk.c
index 9f47388eaba5..c2a465416a33 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_xsk.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_xsk.c
@@ -599,7 +599,7 @@ static bool i40e_xmit_zc(struct i40e_ring *xdp_ring, unsigned int budget)
 	i40e_set_rs_bit(xdp_ring);
 	i40e_xdp_ring_update_tail(xdp_ring);
 
-	i40e_update_tx_stats(xdp_ring, nb_pkts, total_bytes);
+	i40e_update_tx_stats(xdp_ring, nb_pkts, total_bytes, 0, 0);
 
 	return nb_pkts < budget;
 }
-- 
2.53.0


^ permalink raw reply related

* Re: [PATCH net] net: phy: micrel: Fix MMD register access during SPD in ksz9131_resume()
From: Geert Uytterhoeven @ 2026-04-08 11:45 UTC (permalink / raw)
  To: Ovidiu Panait
  Cc: andrew, hkallweit1, linux, davem, edumazet, kuba, pabeni,
	biju.das.jz, netdev, linux-kernel, linux-renesas-soc,
	Niklas Söderlund
In-Reply-To: <20260403111738.37749-1-ovidiu.panait.rb@renesas.com>

Hi Ovidiu,

On Fri, 3 Apr 2026 at 13:18, Ovidiu Panait <ovidiu.panait.rb@renesas.com> wrote:
> During system suspend, phy_suspend() puts the PHY into Software Power-Down
> (SPD) by setting the BMCR_PDOWN bit in MII_BMCR. According to the KSZ9131
> datasheet, MMD register access is restricted during SPD:
>
>   - Only access to the standard registers (0 through 31) is supported.
>   - Access to MMD address spaces other than MMD address space 1 is
>     possible if the spd_clock_gate_override bit is set.
>   - Access to MMD address space 1 is not possible.
>
> However, ksz9131_resume() calls ksz9131_config_rgmii_delay() before
> kszphy_resume() clears BMCR_PDOWN. This means MMD registers are accessed
> while the PHY is still in SPD, contrary to the datasheet.
>
> Additionally, on platforms where the PHY loses power during suspend
> (e.g. RZ/G3E), all settings from ksz9131_config_init(), not just the
> RGMII delays, are lost and need to be restored. When the MAC driver
> sets mac_managed_pm (e.g. stmmac), mdio_bus_phy_resume() is skipped,
> so phy_init_hw() (which calls config_init to restore all PHY settings)
> is never invoked during resume.
>
> Fix this by replacing the RGMII delay restoration with a call to
> phy_init_hw(), which takes the PHY out of SPD and performs full
> reinitialization.
>
> Fixes: f25a7eaa897f ("net: phy: micrel: Add ksz9131_resume()")
> Signed-off-by: Ovidiu Panait <ovidiu.panait.rb@renesas.com>

Thanks for your patch!

> --- a/drivers/net/phy/micrel.c
> +++ b/drivers/net/phy/micrel.c
> @@ -6016,8 +6016,13 @@ static int lan8841_suspend(struct phy_device *phydev)
>
>  static int ksz9131_resume(struct phy_device *phydev)
>  {
> -       if (phydev->suspended && phy_interface_is_rgmii(phydev))
> -               ksz9131_config_rgmii_delay(phydev);
> +       int ret;
> +
> +       if (phydev->suspended) {
> +               ret = phy_init_hw(phydev);
> +               if (ret)
> +                       return ret;
> +       }
>
>         return kszphy_resume(phydev);
>  }

This function is now no longer KSZ9131-specific.
I am wondering if this should be done for other Micrel PHYs, too,
e.g. by moving the phy_init_hw() call into kszphy_resume()?

Ethernet after resume has always been flaky on Salvator-X with KSZ9031
and R-Car M3-W ES1.0 (this seems to be specific to R-Car M3-W, as
boards with R-Car H3 or M3-N do not seem to suffer from this; don't
ask me why).

I have just tried:

-       .resume         = kszphy_resume,
+       .resume         = ksz9131_resume,

in the KSZ9031 entry, and ... surprise! Ethernet on R-Car M3-W now
works much better after resume!

Gr{oetje,eeting}s,

                        Geert

-- 
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds

^ permalink raw reply

* Re: [PATCH 3/3] net: dsa: microchip: implement KSZ87xx Module 3 low-loss cable errata
From: Fidelio LAWSON @ 2026-04-08 11:50 UTC (permalink / raw)
  To: Andrew Lunn
  Cc: Woojung Huh, UNGLinuxDriver, Vladimir Oltean, David S. Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Marek Vasut, Maxime Chevallier,
	netdev, devicetree, linux-kernel, Fidelio Lawson
In-Reply-To: <c235ee5c-6057-4c10-9960-9c5a3527bf22@lunn.ch>

On 4/4/26 16:45, Andrew Lunn wrote:
> On Fri, Apr 03, 2026 at 11:43:24AM +0200, Fidelio LAWSON wrote:
>> On 3/26/26 13:18, Andrew Lunn wrote:
>>>> +	mutex_lock(&dev->alu_mutex);
>>>> +
>>>> +	ret = ksz_write8(dev, regs[REG_IND_CTRL_0], 0xA0);
>>>> +
>>>> +	if (!ret)
>>>> +		ret = ksz_write8(dev, 0x6F, indir_reg);
>>>> +
>>>> +	if (!ret)
>>>> +		ret = ksz_write8(dev, regs[REG_IND_BYTE], indir_val);
>>>> +
>>>> +	mutex_unlock(&dev->alu_mutex);
>>>
>>> What address space are these registers in? Normally workarounds for a
>>> PHY would be in the PHY driver. But that assumes the registers are
>>> accessible from the PHY driver.
>>>
>>> 	   Andrew
>>
>> Hi Andrew,
>> These registers belong to the KSZ87xx switch address space, accessed through
>> the switch’s indirect access mechanism. In particular, the offsets used here
>> correspond to entries within the TABLE_LINK_MD_V indirect table of the
>> KSZ8-family switches.
> 
> So this errata is for ksz87xx only?
> 
> For this PHY, do all PHY register reads and writes go through
> 
> https://elixir.bootlin.com/linux/v6.19.11/source/drivers/net/dsa/microchip/ksz8.c#L957
> ksz8_r_phy()
> 
> and
> 
> https://elixir.bootlin.com/linux/v6.19.11/source/drivers/net/dsa/microchip/ksz8.c#L1221
> ksz8_w_phy()?
> 
> We already have some "interesting" things going on in these
> functions. PHY_REG_LINK_MD and PHY_REG_PHY_CTRL are not standard C22
> PHY registers. They take the values 0x1d and 0x1f. The 802.3 standard
> defines 0x10-0x1f as vendor specific, so this is O.K.
> 
> So you could define 2 bits in say register 0x1c to indicate the errata
> mode. You can have a PHY tunable which does reads/writes to these two
> bits, and ksz8_w_phy/ksz8_r_phy which translates them to indirect
> register accesses?
> 
> It is not even really violating the layering.
> 
> 	 Andrew

Hi Andrew,

Thanks a lot for the feedback, it was very helpful.

Yes, the erratum affects KSZ87xx devices only, and all accesses to the 
embedded PHYs indeed go through ksz8_r_phy() / ksz8_w_phy(), as you 
pointed out.
I followed your suggestion and reworked the implementation accordingly: 
the errata selection is now modeled as a vendor‑specific Clause 22 PHY 
register (0x1c), handled entirely in ksz8_r_phy() / ksz8_w_phy(), which 
translate reads and writes into the appropriate indirect TABLE_LINK_MD_V 
accesses. This keeps the PHY‑facing API clean without breaking the layering.
I’ve dropped the DT approach and adjusted the implementation based on 
the review comments. I’m sending a v2 with these changes shortly.

Thanks again for the guidance.
Best regards,
Fidelio


^ permalink raw reply

* Re: [Intel-wired-lan] [PATCH iwl-next 0/2] i40e: implement per-queue stats
From: Paul Menzel @ 2026-04-08 11:50 UTC (permalink / raw)
  To: Paolo Abeni
  Cc: intel-wired-lan, Tony Nguyen, Przemek Kitszel, Andrew Lunn,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Alexei Starovoitov,
	Daniel Borkmann, Jesper Dangaard Brouer, John Fastabend,
	Stanislav Fomichev, netdev
In-Reply-To: <cover.1775648513.git.pabeni@redhat.com>

Dear Paolo,


Thank you for your patches.

Am 08.04.26 um 13:43 schrieb Paolo Abeni:
> The i40e driver already collects some per queue statistics, but does
> not expose them to the user-space using the standard interface.
> 
> Implement the stat_ops callbacks and extends the already collected info

s/extends/extend/

> with basic GSO counters. Overall this allows passing the kernel NIC
> drivers TSO test cases.

It’d be great, if you added the commands to show the stats, and to run 
the test cases.

> Paolo Abeni (2):
>    i40e: implement basic per-queue stats
>    i40e: keep track of per queue gso counters.

No period needed at the end of the summary/title.

> 
>   drivers/net/ethernet/intel/i40e/i40e.h        |   9 ++
>   drivers/net/ethernet/intel/i40e/i40e_main.c   | 144 ++++++++++++++++++
>   drivers/net/ethernet/intel/i40e/i40e_txrx.c   |   8 +-
>   drivers/net/ethernet/intel/i40e/i40e_txrx.h   |   2 +
>   .../ethernet/intel/i40e/i40e_txrx_common.h    |   6 +-
>   drivers/net/ethernet/intel/i40e/i40e_xsk.c    |   2 +-
>   6 files changed, 168 insertions(+), 3 deletions(-)


Kind regards,

Paul

^ permalink raw reply

* [PATCH net v8 0/4] macsec: Add support for VLAN filtering in offload mode
From: Cosmin Ratiu @ 2026-04-08 11:52 UTC (permalink / raw)
  To: netdev
  Cc: Sabrina Dubroca, Andrew Lunn, David S . Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Simon Horman, Stanislav Fomichev,
	David Wei, Shuah Khan, linux-kselftest, Cosmin Ratiu,
	Dragos Tatulea

This short series adds support for VLANs in MACsec devices when offload
mode is enabled. This allows VLAN netdevs on top of MACsec netdevs to
function, which accidentally used to be the case in the past, but was
broken. This series adds back proper support.

As part of this, the existing nsim-only MACsec offload tests were
translated to Python so they can run against real HW and new
traffic-based tests were added for VLAN filter propagation, since
there's currently no uAPI to check VLAN filters.

---

V8:
- Undo filters on failure when enabling macsec offload and fail the op.
- Used a better string from 'ip macsec help' to detect support.
- Dropped unneded '-d' from the MAC address getter.
- Used os.path.join() instead of manual '+'.

V7: https://lore.kernel.org/netdev/ac5Wt4vWxHMTcsae@krikkit/T/
- Added missing ethtool feature checks.

V6: https://lore.kernel.org/netdev/20260330130130.989236-1-cratiu@nvidia.com/T/#u
- Resurrected nsim debugfs file for VLAN filters.
- Switched to WARN_ON_ONCE in nsim, which is always compiled in.
- Tweaked tests to check the nsim VLAN filter file on nsim.
- Disabled ping on nsim in offload mode, data plane doesn't work.
- Added CONFIG_VLAN_8021Q to the test config.

V5: https://lore.kernel.org/netdev/20260323123633.756163-1-cratiu@nvidia.com/T/#u
- Merged tests and macsec lib in a single file.
- Fixed Python linter issues.
- Added CONFIG_MACSEC to tools/testing/selftests/drivers/net/config

V4: https://lore.kernel.org/netdev/20260313105227.1884391-1-cratiu@nvidia.com/T/#u
- Migrated nsim-only macsec tests to Python, usable against real hw.
- Ran these tests against both nsim and mlx5.
- Gave up on nsim patches since the tests no longer use them.

V3: https://lore.kernel.org/netdev/20260306151004.2862198-1-cratiu@nvidia.com/t/#u
- Moved back to net.
- Added proper rollback support for VLAN filters in case of failure.
- Added VLAN as a requirement for the new macsec tests.

V2: https://lore.kernel.org/netdev/20260227090227.1552512-1-cratiu@nvidia.com/
- Sent to net-next instead of net because of apparent complexity.
- Changed VLAN filtering to only function in offload mode.
- Added tests.

V1: https://lore.kernel.org/netdev/20260107104723.2750725-1-cratiu@nvidia.com/

Cosmin Ratiu (4):
  selftests: Migrate nsim-only MACsec tests to Python
  nsim: Add support for VLAN filters
  selftests: Add MACsec VLAN propagation traffic test
  macsec: Support VLAN-filtering lower devices

 drivers/net/macsec.c                          |  71 +++-
 drivers/net/netdevsim/netdev.c                |  65 +++-
 drivers/net/netdevsim/netdevsim.h             |   8 +
 tools/testing/selftests/drivers/net/Makefile  |   1 +
 tools/testing/selftests/drivers/net/config    |   2 +
 .../selftests/drivers/net/lib/py/env.py       |   9 +
 tools/testing/selftests/drivers/net/macsec.py | 343 ++++++++++++++++++
 .../selftests/drivers/net/netdevsim/Makefile  |   1 -
 .../drivers/net/netdevsim/macsec-offload.sh   | 117 ------
 9 files changed, 489 insertions(+), 128 deletions(-)
 create mode 100755 tools/testing/selftests/drivers/net/macsec.py
 delete mode 100755 tools/testing/selftests/drivers/net/netdevsim/macsec-offload.sh

-- 
2.53.0


^ permalink raw reply

* [PATCH net v8 2/4] nsim: Add support for VLAN filters
From: Cosmin Ratiu @ 2026-04-08 11:52 UTC (permalink / raw)
  To: netdev
  Cc: Sabrina Dubroca, Andrew Lunn, David S . Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Simon Horman, Stanislav Fomichev,
	David Wei, Shuah Khan, linux-kselftest, Cosmin Ratiu,
	Dragos Tatulea
In-Reply-To: <20260408115240.1636047-1-cratiu@nvidia.com>

Add support for storing the list of VLANs in nsim devices, together with
ops for adding/removing them and a debug file to show them.

This will be used in upcoming tests.

Signed-off-by: Cosmin Ratiu <cratiu@nvidia.com>
---
 drivers/net/netdevsim/netdev.c    | 65 ++++++++++++++++++++++++++++++-
 drivers/net/netdevsim/netdevsim.h |  8 ++++
 2 files changed, 71 insertions(+), 2 deletions(-)

diff --git a/drivers/net/netdevsim/netdev.c b/drivers/net/netdevsim/netdev.c
index 3645ebde049a..19b0ff183c45 100644
--- a/drivers/net/netdevsim/netdev.c
+++ b/drivers/net/netdevsim/netdev.c
@@ -605,6 +605,36 @@ static int nsim_stop(struct net_device *dev)
 	return 0;
 }
 
+static int nsim_vlan_rx_add_vid(struct net_device *dev, __be16 proto, u16 vid)
+{
+	struct netdevsim *ns = netdev_priv(dev);
+
+	if (vid >= VLAN_N_VID)
+		return -EINVAL;
+
+	if (proto == htons(ETH_P_8021Q))
+		WARN_ON_ONCE(test_and_set_bit(vid, ns->vlan.ctag));
+	else if (proto == htons(ETH_P_8021AD))
+		WARN_ON_ONCE(test_and_set_bit(vid, ns->vlan.stag));
+
+	return 0;
+}
+
+static int nsim_vlan_rx_kill_vid(struct net_device *dev, __be16 proto, u16 vid)
+{
+	struct netdevsim *ns = netdev_priv(dev);
+
+	if (vid >= VLAN_N_VID)
+		return -EINVAL;
+
+	if (proto == htons(ETH_P_8021Q))
+		WARN_ON_ONCE(!test_and_clear_bit(vid, ns->vlan.ctag));
+	else if (proto == htons(ETH_P_8021AD))
+		WARN_ON_ONCE(!test_and_clear_bit(vid, ns->vlan.stag));
+
+	return 0;
+}
+
 static int nsim_shaper_set(struct net_shaper_binding *binding,
 			   const struct net_shaper *shaper,
 			   struct netlink_ext_ack *extack)
@@ -662,6 +692,8 @@ static const struct net_device_ops nsim_netdev_ops = {
 	.ndo_bpf		= nsim_bpf,
 	.ndo_open		= nsim_open,
 	.ndo_stop		= nsim_stop,
+	.ndo_vlan_rx_add_vid	= nsim_vlan_rx_add_vid,
+	.ndo_vlan_rx_kill_vid	= nsim_vlan_rx_kill_vid,
 	.net_shaper_ops		= &nsim_shaper_ops,
 };
 
@@ -673,6 +705,8 @@ static const struct net_device_ops nsim_vf_netdev_ops = {
 	.ndo_change_mtu		= nsim_change_mtu,
 	.ndo_setup_tc		= nsim_setup_tc,
 	.ndo_set_features	= nsim_set_features,
+	.ndo_vlan_rx_add_vid	= nsim_vlan_rx_add_vid,
+	.ndo_vlan_rx_kill_vid	= nsim_vlan_rx_kill_vid,
 };
 
 /* We don't have true per-queue stats, yet, so do some random fakery here.
@@ -970,6 +1004,20 @@ static const struct file_operations nsim_pp_hold_fops = {
 	.owner = THIS_MODULE,
 };
 
+static int nsim_vlan_show(struct seq_file *s, void *data)
+{
+	struct netdevsim *ns = s->private;
+	int vid;
+
+	for_each_set_bit(vid, ns->vlan.ctag, VLAN_N_VID)
+		seq_printf(s, "ctag %d\n", vid);
+	for_each_set_bit(vid, ns->vlan.stag, VLAN_N_VID)
+		seq_printf(s, "stag %d\n", vid);
+
+	return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(nsim_vlan);
+
 static void nsim_setup(struct net_device *dev)
 {
 	ether_setup(dev);
@@ -982,14 +1030,18 @@ static void nsim_setup(struct net_device *dev)
 			 NETIF_F_FRAGLIST |
 			 NETIF_F_HW_CSUM |
 			 NETIF_F_LRO |
-			 NETIF_F_TSO;
+			 NETIF_F_TSO |
+			 NETIF_F_HW_VLAN_CTAG_FILTER |
+			 NETIF_F_HW_VLAN_STAG_FILTER;
 	dev->hw_features |= NETIF_F_HW_TC |
 			    NETIF_F_SG |
 			    NETIF_F_FRAGLIST |
 			    NETIF_F_HW_CSUM |
 			    NETIF_F_LRO |
 			    NETIF_F_TSO |
-			    NETIF_F_LOOPBACK;
+			    NETIF_F_LOOPBACK |
+			    NETIF_F_HW_VLAN_CTAG_FILTER |
+			    NETIF_F_HW_VLAN_STAG_FILTER;
 	dev->pcpu_stat_type = NETDEV_PCPU_STAT_DSTATS;
 	dev->max_mtu = ETH_MAX_MTU;
 	dev->xdp_features = NETDEV_XDP_ACT_BASIC | NETDEV_XDP_ACT_HW_OFFLOAD;
@@ -1156,6 +1208,8 @@ struct netdevsim *nsim_create(struct nsim_dev *nsim_dev,
 	ns->qr_dfs = debugfs_create_file("queue_reset", 0200,
 					 nsim_dev_port->ddir, ns,
 					 &nsim_qreset_fops);
+	ns->vlan_dfs = debugfs_create_file("vlan", 0400, nsim_dev_port->ddir,
+					   ns, &nsim_vlan_fops);
 	return ns;
 
 err_free_netdev:
@@ -1167,7 +1221,9 @@ void nsim_destroy(struct netdevsim *ns)
 {
 	struct net_device *dev = ns->netdev;
 	struct netdevsim *peer;
+	u16 vid;
 
+	debugfs_remove(ns->vlan_dfs);
 	debugfs_remove(ns->qr_dfs);
 	debugfs_remove(ns->pp_dfs);
 
@@ -1193,6 +1249,11 @@ void nsim_destroy(struct netdevsim *ns)
 	if (nsim_dev_port_is_pf(ns->nsim_dev_port))
 		nsim_exit_netdevsim(ns);
 
+	for_each_set_bit(vid, ns->vlan.ctag, VLAN_N_VID)
+		WARN_ON_ONCE(1);
+	for_each_set_bit(vid, ns->vlan.stag, VLAN_N_VID)
+		WARN_ON_ONCE(1);
+
 	/* Put this intentionally late to exercise the orphaning path */
 	if (ns->page) {
 		page_pool_put_full_page(pp_page_to_nmdesc(ns->page)->pp,
diff --git a/drivers/net/netdevsim/netdevsim.h b/drivers/net/netdevsim/netdevsim.h
index f767fc8a7505..f844c27ca78b 100644
--- a/drivers/net/netdevsim/netdevsim.h
+++ b/drivers/net/netdevsim/netdevsim.h
@@ -18,6 +18,7 @@
 #include <linux/ethtool.h>
 #include <linux/ethtool_netlink.h>
 #include <linux/kernel.h>
+#include <linux/if_vlan.h>
 #include <linux/list.h>
 #include <linux/netdevice.h>
 #include <linux/ptp_mock.h>
@@ -75,6 +76,11 @@ struct nsim_macsec {
 	u8 nsim_secy_count;
 };
 
+struct nsim_vlan {
+	DECLARE_BITMAP(ctag, VLAN_N_VID);
+	DECLARE_BITMAP(stag, VLAN_N_VID);
+};
+
 struct nsim_ethtool_pauseparam {
 	bool rx;
 	bool tx;
@@ -135,6 +141,7 @@ struct netdevsim {
 	bool bpf_map_accept;
 	struct nsim_ipsec ipsec;
 	struct nsim_macsec macsec;
+	struct nsim_vlan vlan;
 	struct {
 		u32 inject_error;
 		u32 __ports[2][NSIM_UDP_TUNNEL_N_PORTS];
@@ -146,6 +153,7 @@ struct netdevsim {
 	struct page *page;
 	struct dentry *pp_dfs;
 	struct dentry *qr_dfs;
+	struct dentry *vlan_dfs;
 
 	struct nsim_ethtool ethtool;
 	struct netdevsim __rcu *peer;
-- 
2.53.0


^ permalink raw reply related

* [PATCH net v8 1/4] selftests: Migrate nsim-only MACsec tests to Python
From: Cosmin Ratiu @ 2026-04-08 11:52 UTC (permalink / raw)
  To: netdev
  Cc: Sabrina Dubroca, Andrew Lunn, David S . Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Simon Horman, Stanislav Fomichev,
	David Wei, Shuah Khan, linux-kselftest, Cosmin Ratiu,
	Dragos Tatulea
In-Reply-To: <20260408115240.1636047-1-cratiu@nvidia.com>

Move MACsec offload API and ethtool feature tests from
tools/testing/selftests/drivers/net/netdevsim/macsec-offload.sh to
tools/testing/selftests/drivers/net/macsec.py using the NetDrvEnv
framework so tests can run against both netdevsim (default) and real
hardware (NETIF=ethX). As some real hardware requires MACsec to use
encryption, add that to the tests.

Netdevsim-specific limit checks (max SecY, max RX SC) were moved into
separate test cases to avoid failures on real hardware.

Signed-off-by: Cosmin Ratiu <cratiu@nvidia.com>
---
 tools/testing/selftests/drivers/net/Makefile  |   1 +
 tools/testing/selftests/drivers/net/config    |   1 +
 tools/testing/selftests/drivers/net/macsec.py | 202 ++++++++++++++++++
 .../selftests/drivers/net/netdevsim/Makefile  |   1 -
 .../drivers/net/netdevsim/macsec-offload.sh   | 117 ----------
 5 files changed, 204 insertions(+), 118 deletions(-)
 create mode 100755 tools/testing/selftests/drivers/net/macsec.py
 delete mode 100755 tools/testing/selftests/drivers/net/netdevsim/macsec-offload.sh

diff --git a/tools/testing/selftests/drivers/net/Makefile b/tools/testing/selftests/drivers/net/Makefile
index 8154d6d429d3..5e045dde0273 100644
--- a/tools/testing/selftests/drivers/net/Makefile
+++ b/tools/testing/selftests/drivers/net/Makefile
@@ -13,6 +13,7 @@ TEST_GEN_FILES := \
 TEST_PROGS := \
 	gro.py \
 	hds.py \
+	macsec.py \
 	napi_id.py \
 	napi_threaded.py \
 	netpoll_basic.py \
diff --git a/tools/testing/selftests/drivers/net/config b/tools/testing/selftests/drivers/net/config
index 77ccf83d87e0..d4b31a317c09 100644
--- a/tools/testing/selftests/drivers/net/config
+++ b/tools/testing/selftests/drivers/net/config
@@ -3,6 +3,7 @@ CONFIG_DEBUG_INFO_BTF=y
 CONFIG_DEBUG_INFO_BTF_MODULES=n
 CONFIG_INET_PSP=y
 CONFIG_IPV6=y
+CONFIG_MACSEC=m
 CONFIG_NETCONSOLE=m
 CONFIG_NETCONSOLE_DYNAMIC=y
 CONFIG_NETCONSOLE_EXTENDED_LOG=y
diff --git a/tools/testing/selftests/drivers/net/macsec.py b/tools/testing/selftests/drivers/net/macsec.py
new file mode 100755
index 000000000000..5220c0656c0c
--- /dev/null
+++ b/tools/testing/selftests/drivers/net/macsec.py
@@ -0,0 +1,202 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+
+"""MACsec tests."""
+
+import os
+
+from lib.py import ksft_run, ksft_exit, ksft_eq, ksft_raises
+from lib.py import CmdExitFailure, KsftSkipEx
+from lib.py import NetDrvEpEnv
+from lib.py import cmd, ip, defer, ethtool
+
+# Unique prefix per run to avoid collisions in the shared netns.
+# Keep it short: IFNAMSIZ is 16 (incl. NUL), and VLAN names append ".<vid>".
+MACSEC_PFX = f"ms{os.getpid()}_"
+
+
+def _macsec_name(idx=0):
+    return f"{MACSEC_PFX}{idx}"
+
+
+def _get_macsec_offload(dev):
+    """Returns macsec offload mode string from ip -d link show."""
+    info = ip(f"-d link show dev {dev}", json=True)[0]
+    return info.get("linkinfo", {}).get("info_data", {}).get("offload")
+
+
+def _get_features(dev):
+    """Returns ethtool features dict for a device."""
+    return ethtool(f"-k {dev}", json=True)[0]
+
+
+def _require_ip_macsec(cfg):
+    """SKIP if iproute2 on local or remote lacks 'ip macsec' support."""
+    for host in [None, cfg.remote]:
+        out = cmd("ip macsec help", fail=False, host=host)
+        if "Usage" not in out.stdout + out.stderr:
+            where = "remote" if host else "local"
+            raise KsftSkipEx(f"iproute2 too old on {where},"
+                             " missing macsec support")
+
+
+def _require_ip_macsec_offload():
+    """SKIP if local iproute2 doesn't understand 'ip macsec offload'."""
+    out = cmd("ip macsec help", fail=False)
+    if "offload" not in out.stdout + out.stderr:
+        raise KsftSkipEx("iproute2 too old, missing macsec offload")
+
+
+def _require_macsec_offload(cfg):
+    """SKIP if local device doesn't support macsec-hw-offload."""
+    _require_ip_macsec_offload()
+    try:
+        feat = ethtool(f"-k {cfg.ifname}", json=True)[0]
+    except (CmdExitFailure, IndexError) as e:
+        raise KsftSkipEx(
+            f"can't query features: {e}") from e
+    if not feat.get("macsec-hw-offload", {}).get("active"):
+        raise KsftSkipEx("macsec-hw-offload not supported")
+
+
+def test_offload_api(cfg) -> None:
+    """MACsec offload API: create SecY, add SA/rx, toggle offload."""
+
+    _require_macsec_offload(cfg)
+    ms0 = _macsec_name(0)
+    ms1 = _macsec_name(1)
+    ms2 = _macsec_name(2)
+
+    # Create 3 SecY with offload
+    ip(f"link add link {cfg.ifname} {ms0} type macsec "
+       f"port 4 encrypt on offload mac")
+    defer(ip, f"link del {ms0}")
+
+    ip(f"link add link {cfg.ifname} {ms1} type macsec "
+       f"address aa:bb:cc:dd:ee:ff port 5 encrypt on offload mac")
+    defer(ip, f"link del {ms1}")
+
+    ip(f"link add link {cfg.ifname} {ms2} type macsec "
+       f"sci abbacdde01020304 encrypt on offload mac")
+    defer(ip, f"link del {ms2}")
+
+    # Add TX SA
+    ip(f"macsec add {ms0} tx sa 0 pn 1024 on "
+       "key 01 12345678901234567890123456789012")
+
+    # Add RX SC + SA
+    ip(f"macsec add {ms0} rx port 1234 address 1c:ed:de:ad:be:ef")
+    ip(f"macsec add {ms0} rx port 1234 address 1c:ed:de:ad:be:ef "
+       "sa 0 pn 1 on key 00 0123456789abcdef0123456789abcdef")
+
+    # Can't disable offload when SAs are configured
+    with ksft_raises(CmdExitFailure):
+        ip(f"link set {ms0} type macsec offload off")
+    with ksft_raises(CmdExitFailure):
+        ip(f"macsec offload {ms0} off")
+
+    # Toggle offload via rtnetlink on SA-free device
+    ip(f"link set {ms2} type macsec offload off")
+    ip(f"link set {ms2} type macsec encrypt on offload mac")
+
+    # Toggle offload via genetlink
+    ip(f"macsec offload {ms2} off")
+    ip(f"macsec offload {ms2} mac")
+
+
+def test_max_secy(cfg) -> None:
+    """nsim-only test for max number of SecYs."""
+
+    cfg.require_nsim()
+    _require_ip_macsec_offload()
+    ms0 = _macsec_name(0)
+    ms1 = _macsec_name(1)
+    ms2 = _macsec_name(2)
+    ms3 = _macsec_name(3)
+
+    ip(f"link add link {cfg.ifname} {ms0} type macsec "
+       f"port 4 encrypt on offload mac")
+    defer(ip, f"link del {ms0}")
+
+    ip(f"link add link {cfg.ifname} {ms1} type macsec "
+       f"address aa:bb:cc:dd:ee:ff port 5 encrypt on offload mac")
+    defer(ip, f"link del {ms1}")
+
+    ip(f"link add link {cfg.ifname} {ms2} type macsec "
+       f"sci abbacdde01020304 encrypt on offload mac")
+    defer(ip, f"link del {ms2}")
+    with ksft_raises(CmdExitFailure):
+        ip(f"link add link {cfg.ifname} {ms3} "
+           f"type macsec port 8 encrypt on offload mac")
+
+
+def test_max_sc(cfg) -> None:
+    """nsim-only test for max number of SCs."""
+
+    cfg.require_nsim()
+    _require_ip_macsec_offload()
+    ms0 = _macsec_name(0)
+
+    ip(f"link add link {cfg.ifname} {ms0} type macsec "
+       f"port 4 encrypt on offload mac")
+    defer(ip, f"link del {ms0}")
+    ip(f"macsec add {ms0} rx port 1234 address 1c:ed:de:ad:be:ef")
+    with ksft_raises(CmdExitFailure):
+        ip(f"macsec add {ms0} rx port 1235 address 1c:ed:de:ad:be:ef")
+
+
+def test_offload_state(cfg) -> None:
+    """Offload state reflects configuration changes."""
+
+    _require_macsec_offload(cfg)
+    ms0 = _macsec_name(0)
+
+    # Create with offload on
+    ip(f"link add link {cfg.ifname} {ms0} type macsec "
+       f"encrypt on offload mac")
+    cleanup = defer(ip, f"link del {ms0}")
+
+    ksft_eq(_get_macsec_offload(ms0), "mac",
+            "created with offload: should be mac")
+    feats_on_1 = _get_features(ms0)
+
+    ip(f"link set {ms0} type macsec offload off")
+    ksft_eq(_get_macsec_offload(ms0), "off",
+            "offload disabled: should be off")
+    feats_off_1 = _get_features(ms0)
+
+    ip(f"link set {ms0} type macsec encrypt on offload mac")
+    ksft_eq(_get_macsec_offload(ms0), "mac",
+            "offload re-enabled: should be mac")
+    ksft_eq(_get_features(ms0), feats_on_1,
+            "features should match first offload-on snapshot")
+
+    # Delete and recreate without offload
+    cleanup.exec()
+    ip(f"link add link {cfg.ifname} {ms0} type macsec")
+    defer(ip, f"link del {ms0}")
+    ksft_eq(_get_macsec_offload(ms0), "off",
+            "created without offload: should be off")
+    ksft_eq(_get_features(ms0), feats_off_1,
+            "features should match first offload-off snapshot")
+
+    ip(f"link set {ms0} type macsec encrypt on offload mac")
+    ksft_eq(_get_macsec_offload(ms0), "mac",
+            "offload enabled after create: should be mac")
+    ksft_eq(_get_features(ms0), feats_on_1,
+            "features should match first offload-on snapshot")
+
+
+def main() -> None:
+    """Main program."""
+    with NetDrvEpEnv(__file__) as cfg:
+        ksft_run([test_offload_api,
+                  test_max_secy,
+                  test_max_sc,
+                  test_offload_state,
+                  ], args=(cfg,))
+    ksft_exit()
+
+
+if __name__ == "__main__":
+    main()
diff --git a/tools/testing/selftests/drivers/net/netdevsim/Makefile b/tools/testing/selftests/drivers/net/netdevsim/Makefile
index 1a228c5430f5..9808c2fbae9e 100644
--- a/tools/testing/selftests/drivers/net/netdevsim/Makefile
+++ b/tools/testing/selftests/drivers/net/netdevsim/Makefile
@@ -11,7 +11,6 @@ TEST_PROGS := \
 	fib.sh \
 	fib_notifications.sh \
 	hw_stats_l3.sh \
-	macsec-offload.sh \
 	nexthop.sh \
 	peer.sh \
 	psample.sh \
diff --git a/tools/testing/selftests/drivers/net/netdevsim/macsec-offload.sh b/tools/testing/selftests/drivers/net/netdevsim/macsec-offload.sh
deleted file mode 100755
index 98033e6667d2..000000000000
--- a/tools/testing/selftests/drivers/net/netdevsim/macsec-offload.sh
+++ /dev/null
@@ -1,117 +0,0 @@
-#!/bin/bash
-# SPDX-License-Identifier: GPL-2.0-only
-
-source ethtool-common.sh
-
-NSIM_NETDEV=$(make_netdev)
-MACSEC_NETDEV=macsec_nsim
-
-set -o pipefail
-
-if ! ethtool -k $NSIM_NETDEV | grep -q 'macsec-hw-offload: on'; then
-    echo "SKIP: netdevsim doesn't support MACsec offload"
-    exit 4
-fi
-
-if ! ip link add link $NSIM_NETDEV $MACSEC_NETDEV type macsec offload mac 2>/dev/null; then
-    echo "SKIP: couldn't create macsec device"
-    exit 4
-fi
-ip link del $MACSEC_NETDEV
-
-#
-# test macsec offload API
-#
-
-ip link add link $NSIM_NETDEV "${MACSEC_NETDEV}" type macsec port 4 offload mac
-check $?
-
-ip link add link $NSIM_NETDEV "${MACSEC_NETDEV}2" type macsec address "aa:bb:cc:dd:ee:ff" port 5 offload mac
-check $?
-
-ip link add link $NSIM_NETDEV "${MACSEC_NETDEV}3" type macsec sci abbacdde01020304 offload mac
-check $?
-
-ip link add link $NSIM_NETDEV "${MACSEC_NETDEV}4" type macsec port 8 offload mac 2> /dev/null
-check $? '' '' 1
-
-ip macsec add "${MACSEC_NETDEV}" tx sa 0 pn 1024 on key 01 12345678901234567890123456789012
-check $?
-
-ip macsec add "${MACSEC_NETDEV}" rx port 1234 address "1c:ed:de:ad:be:ef"
-check $?
-
-ip macsec add "${MACSEC_NETDEV}" rx port 1234 address "1c:ed:de:ad:be:ef" sa 0 pn 1 on \
-    key 00 0123456789abcdef0123456789abcdef
-check $?
-
-ip macsec add "${MACSEC_NETDEV}" rx port 1235 address "1c:ed:de:ad:be:ef" 2> /dev/null
-check $? '' '' 1
-
-# can't disable macsec offload when SAs are configured
-ip link set "${MACSEC_NETDEV}" type macsec offload off 2> /dev/null
-check $? '' '' 1
-
-ip macsec offload "${MACSEC_NETDEV}" off 2> /dev/null
-check $? '' '' 1
-
-# toggle macsec offload via rtnetlink
-ip link set "${MACSEC_NETDEV}2" type macsec offload off
-check $?
-
-ip link set "${MACSEC_NETDEV}2" type macsec offload mac
-check $?
-
-# toggle macsec offload via genetlink
-ip macsec offload "${MACSEC_NETDEV}2" off
-check $?
-
-ip macsec offload "${MACSEC_NETDEV}2" mac
-check $?
-
-for dev in ${MACSEC_NETDEV}{,2,3} ; do
-    ip link del $dev
-    check $?
-done
-
-
-#
-# test ethtool features when toggling offload
-#
-
-ip link add link $NSIM_NETDEV $MACSEC_NETDEV type macsec offload mac
-TMP_FEATS_ON_1="$(ethtool -k $MACSEC_NETDEV)"
-
-ip link set $MACSEC_NETDEV type macsec offload off
-TMP_FEATS_OFF_1="$(ethtool -k $MACSEC_NETDEV)"
-
-ip link set $MACSEC_NETDEV type macsec offload mac
-TMP_FEATS_ON_2="$(ethtool -k $MACSEC_NETDEV)"
-
-[ "$TMP_FEATS_ON_1" = "$TMP_FEATS_ON_2" ]
-check $?
-
-ip link del $MACSEC_NETDEV
-
-ip link add link $NSIM_NETDEV $MACSEC_NETDEV type macsec
-check $?
-
-TMP_FEATS_OFF_2="$(ethtool -k $MACSEC_NETDEV)"
-[ "$TMP_FEATS_OFF_1" = "$TMP_FEATS_OFF_2" ]
-check $?
-
-ip link set $MACSEC_NETDEV type macsec offload mac
-check $?
-
-TMP_FEATS_ON_3="$(ethtool -k $MACSEC_NETDEV)"
-[ "$TMP_FEATS_ON_1" = "$TMP_FEATS_ON_3" ]
-check $?
-
-
-if [ $num_errors -eq 0 ]; then
-    echo "PASSED all $((num_passes)) checks"
-    exit 0
-else
-    echo "FAILED $num_errors/$((num_errors+num_passes)) checks"
-    exit 1
-fi
-- 
2.53.0


^ permalink raw reply related

* [PATCH net v8 3/4] selftests: Add MACsec VLAN propagation traffic test
From: Cosmin Ratiu @ 2026-04-08 11:52 UTC (permalink / raw)
  To: netdev
  Cc: Sabrina Dubroca, Andrew Lunn, David S . Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Simon Horman, Stanislav Fomichev,
	David Wei, Shuah Khan, linux-kselftest, Cosmin Ratiu,
	Dragos Tatulea
In-Reply-To: <20260408115240.1636047-1-cratiu@nvidia.com>

Add VLAN filter propagation tests through offloaded MACsec devices via
actual traffic.

The tests create MACsec tunnels with matching SAs on both endpoints,
stack VLANs on top, and verify connectivity with ping. Covered:
- Offloaded MACsec with VLAN (filters propagate to HW)
- Software MACsec with VLAN (no HW filter propagation)
- Offload on/off toggle and verifying traffic still works

On netdevsim this makes use of the VLAN filter debugfs file to actually
validate that filters are applied/removed correctly.
On real hardware the traffic should validate actual VLAN filter
propagation.

Signed-off-by: Cosmin Ratiu <cratiu@nvidia.com>
---
 tools/testing/selftests/drivers/net/config    |   1 +
 .../selftests/drivers/net/lib/py/env.py       |   9 ++
 tools/testing/selftests/drivers/net/macsec.py | 141 ++++++++++++++++++
 3 files changed, 151 insertions(+)

diff --git a/tools/testing/selftests/drivers/net/config b/tools/testing/selftests/drivers/net/config
index d4b31a317c09..fd16994366f4 100644
--- a/tools/testing/selftests/drivers/net/config
+++ b/tools/testing/selftests/drivers/net/config
@@ -8,4 +8,5 @@ CONFIG_NETCONSOLE=m
 CONFIG_NETCONSOLE_DYNAMIC=y
 CONFIG_NETCONSOLE_EXTENDED_LOG=y
 CONFIG_NETDEVSIM=m
+CONFIG_VLAN_8021Q=m
 CONFIG_XDP_SOCKETS=y
diff --git a/tools/testing/selftests/drivers/net/lib/py/env.py b/tools/testing/selftests/drivers/net/lib/py/env.py
index 41cc248ac848..b80666277e36 100644
--- a/tools/testing/selftests/drivers/net/lib/py/env.py
+++ b/tools/testing/selftests/drivers/net/lib/py/env.py
@@ -255,6 +255,15 @@ class NetDrvEpEnv(NetDrvEnvBase):
         if nsim_test is False and self._ns is not None:
             raise KsftXfailEx("Test does not work on netdevsim")
 
+    def get_local_nsim_dev(self):
+        """Returns the local netdevsim device or None.
+           Using this method is discouraged, as it makes tests nsim-specific.
+           Standard interfaces available on all HW should ideally be used.
+           This method is intended for the few cases where nsim-specific
+           assertions need to be verified which cannot be verified otherwise.
+        """
+        return self._ns
+
     def _require_cmd(self, comm, key, host=None):
         cached = self._required_cmd.get(comm, {})
         if cached.get(key) is None:
diff --git a/tools/testing/selftests/drivers/net/macsec.py b/tools/testing/selftests/drivers/net/macsec.py
index 5220c0656c0c..9a83d9542e04 100755
--- a/tools/testing/selftests/drivers/net/macsec.py
+++ b/tools/testing/selftests/drivers/net/macsec.py
@@ -6,10 +6,14 @@
 import os
 
 from lib.py import ksft_run, ksft_exit, ksft_eq, ksft_raises
+from lib.py import ksft_variants, KsftNamedVariant
 from lib.py import CmdExitFailure, KsftSkipEx
 from lib.py import NetDrvEpEnv
 from lib.py import cmd, ip, defer, ethtool
 
+MACSEC_KEY = "12345678901234567890123456789012"
+MACSEC_VLAN_VID = 10
+
 # Unique prefix per run to avoid collisions in the shared netns.
 # Keep it short: IFNAMSIZ is 16 (incl. NUL), and VLAN names append ".<vid>".
 MACSEC_PFX = f"ms{os.getpid()}_"
@@ -59,6 +63,78 @@ def _require_macsec_offload(cfg):
         raise KsftSkipEx("macsec-hw-offload not supported")
 
 
+def _get_mac(ifname, host=None):
+    """Gets MAC address of an interface."""
+    dev = ip(f"link show dev {ifname}", json=True, host=host)
+    return dev[0]["address"]
+
+
+def _setup_macsec_sa(cfg, name):
+    """Adds matching TX/RX SAs on both ends."""
+    local_mac = _get_mac(name)
+    remote_mac = _get_mac(name, host=cfg.remote)
+
+    ip(f"macsec add {name} tx sa 0 pn 1 on key 01 {MACSEC_KEY}")
+    ip(f"macsec add {name} rx port 1 address {remote_mac}")
+    ip(f"macsec add {name} rx port 1 address {remote_mac} "
+       f"sa 0 pn 1 on key 02 {MACSEC_KEY}")
+
+    ip(f"macsec add {name} tx sa 0 pn 1 on key 02 {MACSEC_KEY}",
+       host=cfg.remote)
+    ip(f"macsec add {name} rx port 1 address {local_mac}", host=cfg.remote)
+    ip(f"macsec add {name} rx port 1 address {local_mac} "
+       f"sa 0 pn 1 on key 01 {MACSEC_KEY}", host=cfg.remote)
+
+
+def _setup_macsec_devs(cfg, name, offload):
+    """Creates macsec devices on both ends.
+
+    Only the local device gets HW offload; the remote always uses software
+    MACsec since it may not support offload at all.
+    """
+    offload_arg = "mac" if offload else "off"
+
+    ip(f"link add link {cfg.ifname} {name} "
+       f"type macsec encrypt on offload {offload_arg}")
+    defer(ip, f"link del {name}")
+    ip(f"link add link {cfg.remote_ifname} {name} "
+       f"type macsec encrypt on", host=cfg.remote)
+    defer(ip, f"link del {name}", host=cfg.remote)
+
+
+def _set_offload(name, offload):
+    """Sets offload on the local macsec device only."""
+    offload_arg = "mac" if offload else "off"
+
+    ip(f"link set {name} type macsec encrypt on offload {offload_arg}")
+
+
+def _setup_vlans(cfg, name, vid):
+    """Adds VLANs on top of existing macsec devs."""
+    vlan_name = f"{name}.{vid}"
+
+    ip(f"link add link {name} {vlan_name} type vlan id {vid}")
+    defer(ip, f"link del {vlan_name}")
+    ip(f"link add link {name} {vlan_name} type vlan id {vid}", host=cfg.remote)
+    defer(ip, f"link del {vlan_name}", host=cfg.remote)
+
+
+def _setup_vlan_ips(cfg, name, vid):
+    """Adds VLANs and IPs and brings up the macsec + VLAN devices."""
+    local_ip = "198.51.100.1"
+    remote_ip = "198.51.100.2"
+    vlan_name = f"{name}.{vid}"
+
+    ip(f"addr add {local_ip}/24 dev {vlan_name}")
+    ip(f"addr add {remote_ip}/24 dev {vlan_name}", host=cfg.remote)
+    ip(f"link set {name} up")
+    ip(f"link set {name} up", host=cfg.remote)
+    ip(f"link set {vlan_name} up")
+    ip(f"link set {vlan_name} up", host=cfg.remote)
+
+    return vlan_name, remote_ip
+
+
 def test_offload_api(cfg) -> None:
     """MACsec offload API: create SecY, add SA/rx, toggle offload."""
 
@@ -187,6 +263,69 @@ def test_offload_state(cfg) -> None:
             "features should match first offload-on snapshot")
 
 
+def _check_nsim_vid(cfg, vid, expected) -> None:
+    """Checks if a VLAN is present. Only works on netdevsim."""
+
+    nsim = cfg.get_local_nsim_dev()
+    if not nsim:
+        return
+
+    vlan_path = os.path.join(nsim.nsims[0].dfs_dir, "vlan")
+    with open(vlan_path, encoding="utf-8") as f:
+        vids = f.read()
+    found = f"ctag {vid}\n" in vids
+    ksft_eq(found, expected,
+            f"VLAN {vid} {'expected' if expected else 'not expected'}"
+            f" in debugfs")
+
+
+@ksft_variants([
+    KsftNamedVariant("offloaded", True),
+    KsftNamedVariant("software", False),
+])
+def test_vlan(cfg, offload) -> None:
+    """Ping through VLAN-over-macsec."""
+
+    _require_ip_macsec(cfg)
+    if offload:
+        _require_macsec_offload(cfg)
+    else:
+        _require_ip_macsec_offload()
+    name = _macsec_name()
+    _setup_macsec_devs(cfg, name, offload=offload)
+    _setup_macsec_sa(cfg, name)
+    _setup_vlans(cfg, name, MACSEC_VLAN_VID)
+    vlan_name, remote_ip = _setup_vlan_ips(cfg, name, MACSEC_VLAN_VID)
+    _check_nsim_vid(cfg, MACSEC_VLAN_VID, offload)
+    # nsim doesn't handle the data path for offloaded macsec, so skip
+    # the ping when offloaded on nsim.
+    if not offload or not cfg.get_local_nsim_dev():
+        cmd(f"ping -I {vlan_name} -c 1 -W 5 {remote_ip}")
+
+
+@ksft_variants([
+    KsftNamedVariant("on_to_off", True),
+    KsftNamedVariant("off_to_on", False),
+])
+def test_vlan_toggle(cfg, offload) -> None:
+    """Toggle offload: VLAN filters propagate/remove correctly."""
+
+    _require_ip_macsec(cfg)
+    _require_macsec_offload(cfg)
+    name = _macsec_name()
+    _setup_macsec_devs(cfg, name, offload=offload)
+    _setup_vlans(cfg, name, MACSEC_VLAN_VID)
+    _check_nsim_vid(cfg, MACSEC_VLAN_VID, offload)
+    _set_offload(name, offload=not offload)
+    _check_nsim_vid(cfg, MACSEC_VLAN_VID, not offload)
+    vlan_name, remote_ip = _setup_vlan_ips(cfg, name, MACSEC_VLAN_VID)
+    _setup_macsec_sa(cfg, name)
+    # nsim doesn't handle the data path for offloaded macsec, so skip
+    # the ping when the final state is offloaded on nsim.
+    if offload or not cfg.get_local_nsim_dev():
+        cmd(f"ping -I {vlan_name} -c 1 -W 5 {remote_ip}")
+
+
 def main() -> None:
     """Main program."""
     with NetDrvEpEnv(__file__) as cfg:
@@ -194,6 +333,8 @@ def main() -> None:
                   test_max_secy,
                   test_max_sc,
                   test_offload_state,
+                  test_vlan,
+                  test_vlan_toggle,
                   ], args=(cfg,))
     ksft_exit()
 
-- 
2.53.0


^ permalink raw reply related

* [PATCH net v8 4/4] macsec: Support VLAN-filtering lower devices
From: Cosmin Ratiu @ 2026-04-08 11:52 UTC (permalink / raw)
  To: netdev
  Cc: Sabrina Dubroca, Andrew Lunn, David S . Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Simon Horman, Stanislav Fomichev,
	David Wei, Shuah Khan, linux-kselftest, Cosmin Ratiu,
	Dragos Tatulea
In-Reply-To: <20260408115240.1636047-1-cratiu@nvidia.com>

VLAN-filtering is done through two netdev features
(NETIF_F_HW_VLAN_CTAG_FILTER and NETIF_F_HW_VLAN_STAG_FILTER) and two
netdev ops (ndo_vlan_rx_add_vid and ndo_vlan_rx_kill_vid).

Implement these and advertise the features if the lower device supports
them. This allows proper VLAN filtering to work on top of MACsec
devices, when the lower device is capable of VLAN filtering.
As a concrete example, having this chain of interfaces now works:
vlan_filtering_capable_dev(1) -> macsec_dev(2) -> macsec_vlan_dev(3)

Before the mentioned commit this used to accidentally work because the
MACsec device (and thus the lower device) was put in promiscuous mode
and the VLAN filter was not used. But after commit [1] correctly made
the macsec driver expose the IFF_UNICAST_FLT flag, promiscuous mode was
no longer used and VLAN filters on dev 1 kicked in. Without support in
dev 2 for propagating VLAN filters down, the register_vlan_dev ->
vlan_vid_add -> __vlan_vid_add -> vlan_add_rx_filter_info call from dev
3 is silently eaten (because vlan_hw_filter_capable returns false and
vlan_add_rx_filter_info silently succeeds).

For MACsec, VLAN filters are only relevant for offload, otherwise
the VLANs are encrypted and the lower devices don't care about them. So
VLAN filters are only passed on to lower devices in offload mode.
Flipping between offload modes now needs to offload/unoffload the
filters with vlan_{get,drop}_rx_*_filter_info().

To avoid the back-and-forth filter updating during rollback, the setting
of macsec->offload is moved after the add/del secy ops. This is safe
since none of the code called from those requires macsec->offload.

In case adding the filters fails, the added ones are rolled back and an
error is returned to the operation toggling the offload state.

Fixes: 0349659fd72f ("macsec: set IFF_UNICAST_FLT priv flag")
Signed-off-by: Cosmin Ratiu <cratiu@nvidia.com>
---
 drivers/net/macsec.c | 71 +++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 63 insertions(+), 8 deletions(-)

diff --git a/drivers/net/macsec.c b/drivers/net/macsec.c
index f6cad0746a02..6147ee8b1d78 100644
--- a/drivers/net/macsec.c
+++ b/drivers/net/macsec.c
@@ -2584,7 +2584,9 @@ static void macsec_inherit_tso_max(struct net_device *dev)
 		netif_inherit_tso_max(dev, macsec->real_dev);
 }
 
-static int macsec_update_offload(struct net_device *dev, enum macsec_offload offload)
+static int macsec_update_offload(struct net_device *dev,
+				 enum macsec_offload offload,
+				 struct netlink_ext_ack *extack)
 {
 	enum macsec_offload prev_offload;
 	const struct macsec_ops *ops;
@@ -2616,14 +2618,35 @@ static int macsec_update_offload(struct net_device *dev, enum macsec_offload off
 	if (!ops)
 		return -EOPNOTSUPP;
 
-	macsec->offload = offload;
-
 	ctx.secy = &macsec->secy;
 	ret = offload == MACSEC_OFFLOAD_OFF ? macsec_offload(ops->mdo_del_secy, &ctx)
 					    : macsec_offload(ops->mdo_add_secy, &ctx);
-	if (ret) {
-		macsec->offload = prev_offload;
+	if (ret)
 		return ret;
+
+	/* Remove VLAN filters when disabling offload. */
+	if (offload == MACSEC_OFFLOAD_OFF) {
+		vlan_drop_rx_ctag_filter_info(dev);
+		vlan_drop_rx_stag_filter_info(dev);
+	}
+	macsec->offload = offload;
+	/* Add VLAN filters when enabling offload. */
+	if (prev_offload == MACSEC_OFFLOAD_OFF) {
+		ret = vlan_get_rx_ctag_filter_info(dev);
+		if (ret) {
+			NL_SET_ERR_MSG_FMT(extack,
+					   "adding ctag VLAN filters failed, err %d",
+					   ret);
+			goto rollback_offload;
+		}
+		ret = vlan_get_rx_stag_filter_info(dev);
+		if (ret) {
+			NL_SET_ERR_MSG_FMT(extack,
+					   "adding stag VLAN filters failed, err %d",
+					   ret);
+			vlan_drop_rx_ctag_filter_info(dev);
+			goto rollback_offload;
+		}
 	}
 
 	macsec_set_head_tail_room(dev);
@@ -2633,6 +2656,12 @@ static int macsec_update_offload(struct net_device *dev, enum macsec_offload off
 
 	netdev_update_features(dev);
 
+	return 0;
+
+rollback_offload:
+	macsec->offload = prev_offload;
+	macsec_offload(ops->mdo_del_secy, &ctx);
+
 	return ret;
 }
 
@@ -2673,7 +2702,7 @@ static int macsec_upd_offload(struct sk_buff *skb, struct genl_info *info)
 	offload = nla_get_u8(tb_offload[MACSEC_OFFLOAD_ATTR_TYPE]);
 
 	if (macsec->offload != offload)
-		ret = macsec_update_offload(dev, offload);
+		ret = macsec_update_offload(dev, offload, info->extack);
 out:
 	rtnl_unlock();
 	return ret;
@@ -3486,7 +3515,8 @@ static netdev_tx_t macsec_start_xmit(struct sk_buff *skb,
 }
 
 #define MACSEC_FEATURES \
-	(NETIF_F_SG | NETIF_F_HIGHDMA | NETIF_F_FRAGLIST)
+	(NETIF_F_SG | NETIF_F_HIGHDMA | NETIF_F_FRAGLIST | \
+	 NETIF_F_HW_VLAN_STAG_FILTER | NETIF_F_HW_VLAN_CTAG_FILTER)
 
 #define MACSEC_OFFLOAD_FEATURES \
 	(MACSEC_FEATURES | NETIF_F_GSO_SOFTWARE | NETIF_F_SOFT_FEATURES | \
@@ -3707,6 +3737,29 @@ static int macsec_set_mac_address(struct net_device *dev, void *p)
 	return err;
 }
 
+static int macsec_vlan_rx_add_vid(struct net_device *dev,
+				  __be16 proto, u16 vid)
+{
+	struct macsec_dev *macsec = netdev_priv(dev);
+
+	if (!macsec_is_offloaded(macsec))
+		return 0;
+
+	return vlan_vid_add(macsec->real_dev, proto, vid);
+}
+
+static int macsec_vlan_rx_kill_vid(struct net_device *dev,
+				   __be16 proto, u16 vid)
+{
+	struct macsec_dev *macsec = netdev_priv(dev);
+
+	if (!macsec_is_offloaded(macsec))
+		return 0;
+
+	vlan_vid_del(macsec->real_dev, proto, vid);
+	return 0;
+}
+
 static int macsec_change_mtu(struct net_device *dev, int new_mtu)
 {
 	struct macsec_dev *macsec = macsec_priv(dev);
@@ -3748,6 +3801,8 @@ static const struct net_device_ops macsec_netdev_ops = {
 	.ndo_set_rx_mode	= macsec_dev_set_rx_mode,
 	.ndo_change_rx_flags	= macsec_dev_change_rx_flags,
 	.ndo_set_mac_address	= macsec_set_mac_address,
+	.ndo_vlan_rx_add_vid	= macsec_vlan_rx_add_vid,
+	.ndo_vlan_rx_kill_vid	= macsec_vlan_rx_kill_vid,
 	.ndo_start_xmit		= macsec_start_xmit,
 	.ndo_get_stats64	= macsec_get_stats64,
 	.ndo_get_iflink		= macsec_get_iflink,
@@ -3912,7 +3967,7 @@ static int macsec_changelink(struct net_device *dev, struct nlattr *tb[],
 		offload = nla_get_u8(data[IFLA_MACSEC_OFFLOAD]);
 		if (macsec->offload != offload) {
 			macsec_offload_state_change = true;
-			ret = macsec_update_offload(dev, offload);
+			ret = macsec_update_offload(dev, offload, extack);
 			if (ret)
 				goto cleanup;
 		}
-- 
2.53.0


^ permalink raw reply related

* [PATCH v2] net: dsa: microchip: implement KSZ87xx Module 3 low-loss cable errata
From: Fidelio Lawson @ 2026-04-08 11:57 UTC (permalink / raw)
  To: Woojung Huh, UNGLinuxDriver, Andrew Lunn, Vladimir Oltean,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Marek Vasut,
	Maxime Chevallier
  Cc: Woojung Huh, netdev, devicetree, linux-kernel, Fidelio Lawson

Implement the "Module 3: Equalizer fix for short cables" erratum from
Microchip document DS80000687C for KSZ87xx switches.

The issue affects short or low-loss cable links (e.g. CAT5e/CAT6),
where the PHY receiver equalizer may amplify high-amplitude signals
excessively, resulting in internal distortion and link establishment
failures.

KSZ87xx devices require a workaround for the Module 3 low-loss cable
condition, controlled through the switch TABLE_LINK_MD_V indirect
registers.

The affected registers are part of the switch address space and are not
directly accessible from the PHY driver. To keep the PHY-facing API
clean and avoid leaking switch-specific details, model this errata
control as vendor-specific Clause 22 PHY registers.

Two vendor-defined bits are introduced in PHY_REG_LOW_LOSS_CTRL,
and ksz8_r_phy() / ksz8_w_phy() translate accesses to these bits
into the appropriate indirect TABLE_LINK_MD_V accesses.

The control register defines the following modes:
  bits [1:0]:
    00 = workaround disabled
    01 = workaround 1 (DSP EQ training adjustment, LinkMD reg 0x3c)
    10 = workaround 2 (receiver LPF bandwidth, LinkMD reg 0x4c)

Workaround 1: Adjusts the DSP EQ training behavior via LinkMD register
0x3C. Widens and optimizes the DSP EQ compensation range,
and is expected to solve most short/low-loss cable issues.

Workaround 2: for the cases where Workaround 1 is not sufficient.
This one adjusts the receiver low-pass filter bandwidth, effectively
reducing the high-frequency component of the received signal

The register is accessible through standard PHY read/write operations
(e.g. phytool), without requiring any switch-specific userspace
interface. This allows robust link establishment on short or
low-loss cabling without requiring DTS properties and without
constraining hardware design choices.

The erratum affects the shared PHY analog front-end and therefore
applies globally to the switch.

Signed-off-by: Fidelio Lawson <fidelio.lawson@exotec.com>
---
Hello,

This patch implements the “Module 3: Equalizer fix for short cables” erratum
described in Microchip document DS80000687C for KSZ87xx switches.

According to the erratum, the embedded PHY receiver in KSZ87xx switches is
tuned by default for long, high-loss Ethernet cables. When operating with
short or low-loss cables (for example CAT5e or CAT6), the PHY equalizer may
over-amplify the incoming signal, leading to internal distortion and link
establishment failures.

Microchip provides two workarounds, each requiring a write to a different
indirect PHY register access mechanism.

The workaround requires programming internal PHY/DSP registers located in the
LinkMD table, accessed through the KSZ8 indirect register mechanism. Since these
registers belong to the switch address space and are not directly accessible
from a standalone PHY driver, the erratum control is modeled as a vendor-specific
Clause 22 PHY register, virtualized by the KSZ8 DSA driver.

Reads and writes to this register are intercepted by ksz8_r_phy() /
ksz8_w_phy() and translated into the required TABLE_LINK_MD_V indirect accesses.
The erratum affects the shared PHY analog front-end and therefore applies
globally to the switch.

The register defines three modes:
  - 0x0: workaround disabled
  - 0x1: workaround 1 (DSP EQ training adjustment)
  - 0x2: workaround 2 (receiver low-pass filter bandwidth reduction)

The register can be read and written from userspace via standard Clause 22 PHY
accesses (for example using phytool) on DSA user ports.

This series is based on Linux v7.0-rc1.
---
Changes in v2:
- Dropped the device tree approache based on review feedback
- Modeled the errata control as a vendor-specific Clause 22 PHY register
- Added KSZ87xx-specific guards and replaced magic values with named macros
- Rebased on Linux v7.0-rc1
- Link to v1: https://patch.msgid.link/20260326-ksz87xx_errata_low_loss_connections-v1-0-79a698f43626@exotec.com
---
 drivers/net/dsa/microchip/ksz8.c       | 33 +++++++++++++++++++++++++++++++++
 drivers/net/dsa/microchip/ksz8_reg.h   | 20 +++++++++++++++++++-
 drivers/net/dsa/microchip/ksz_common.h |  3 +++
 3 files changed, 55 insertions(+), 1 deletion(-)

diff --git a/drivers/net/dsa/microchip/ksz8.c b/drivers/net/dsa/microchip/ksz8.c
index c354abdafc1b..d11da6e9ff54 100644
--- a/drivers/net/dsa/microchip/ksz8.c
+++ b/drivers/net/dsa/microchip/ksz8.c
@@ -1058,6 +1058,11 @@ int ksz8_r_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 *val)
 		if (ret)
 			return ret;
 
+		break;
+	case PHY_REG_KSZ87XX_LOW_LOSS:
+		if (!ksz_is_ksz87xx(dev))
+			return -EOPNOTSUPP;
+		data = dev->low_loss_wa_mode;
 		break;
 	default:
 		processed = false;
@@ -1271,6 +1276,34 @@ int ksz8_w_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 val)
 		if (ret)
 			return ret;
 		break;
+	case PHY_REG_KSZ87XX_LOW_LOSS:
+		if (!ksz_is_ksz87xx(dev))
+			return -EOPNOTSUPP;
+
+		switch (val & PHY_KSZ87XX_LOW_LOSS_MASK) {
+		case PHY_LOW_LOSS_ERRATA_DISABLED:
+			ret = ksz8_ind_write8(dev, TABLE_LINK_MD, KSZ87XX_REG_EQ_TRAIN,
+					      KSZ87XX_EQ_TRAIN_DEFAULT);
+			if (!ret)
+				ret = ksz8_ind_write8(dev, TABLE_LINK_MD,
+						      KSZ87XX_REG_PHY_LPF,
+						      KSZ87XX_PHY_LPF_DEFAULT);
+			break;
+		case KSZ87XX_LOW_LOSS_WA_EQ:
+			ret = ksz8_ind_write8(dev, TABLE_LINK_MD, KSZ87XX_REG_EQ_TRAIN,
+					      KSZ87XX_EQ_TRAIN_LOW_LOSS);
+			break;
+		case KSZ87XX_LOW_LOSS_WA_LPF:
+			ret = ksz8_ind_write8(dev, TABLE_LINK_MD, KSZ87XX_REG_PHY_LPF,
+					      KSZ87XX_PHY_LPF_62MHZ);
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		if (!ret)
+			dev->low_loss_wa_mode = val & PHY_KSZ87XX_LOW_LOSS_MASK;
+		return ret;
 	default:
 		break;
 	}
diff --git a/drivers/net/dsa/microchip/ksz8_reg.h b/drivers/net/dsa/microchip/ksz8_reg.h
index 332408567b47..cd1092aa0eaf 100644
--- a/drivers/net/dsa/microchip/ksz8_reg.h
+++ b/drivers/net/dsa/microchip/ksz8_reg.h
@@ -202,6 +202,10 @@
 #define REG_PORT_3_STATUS_0		0x38
 #define REG_PORT_4_STATUS_0		0x48
 
+/* KSZ87xx LinkMD registers (TABLE_LINK_MD_V) */
+#define KSZ87XX_REG_EQ_TRAIN		0x3C
+#define KSZ87XX_REG_PHY_LPF		0x4C
+
 /* For KSZ8765. */
 #define PORT_REMOTE_ASYM_PAUSE		BIT(5)
 #define PORT_REMOTE_SYM_PAUSE		BIT(4)
@@ -342,7 +346,7 @@
 #define TABLE_EEE			(TABLE_EEE_V << TABLE_EXT_SELECT_S)
 #define TABLE_ACL			(TABLE_ACL_V << TABLE_EXT_SELECT_S)
 #define TABLE_PME			(TABLE_PME_V << TABLE_EXT_SELECT_S)
-#define TABLE_LINK_MD			(TABLE_LINK_MD << TABLE_EXT_SELECT_S)
+#define TABLE_LINK_MD			(TABLE_LINK_MD_V << TABLE_EXT_SELECT_S)
 #define TABLE_READ			BIT(4)
 #define TABLE_SELECT_S			2
 #define TABLE_STATIC_MAC_V		0
@@ -729,6 +733,20 @@
 #define PHY_POWER_SAVING_ENABLE		BIT(2)
 #define PHY_REMOTE_LOOPBACK		BIT(1)
 
+/* Equalizer low-loss workaround */
+/* bits [1:0]: 00 = disabled, 01 = workaround 1, 10 = workaround 2 */
+#define PHY_REG_KSZ87XX_LOW_LOSS       0x1C
+#define PHY_KSZ87XX_LOW_LOSS_MASK      GENMASK(1, 0)
+
+#define PHY_LOW_LOSS_ERRATA_DISABLED		0
+#define KSZ87XX_LOW_LOSS_WA_EQ				1
+#define KSZ87XX_LOW_LOSS_WA_LPF			2
+
+#define KSZ87XX_EQ_TRAIN_DEFAULT       0x0A
+#define KSZ87XX_EQ_TRAIN_LOW_LOSS      0x15
+#define KSZ87XX_PHY_LPF_DEFAULT        0x00
+#define KSZ87XX_PHY_LPF_62MHZ          0x40
+
 /* KSZ8463 specific registers. */
 #define P1MBCR				0x4C
 #define P1MBSR				0x4E
diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h
index 929aff4c55de..729996c7160c 100644
--- a/drivers/net/dsa/microchip/ksz_common.h
+++ b/drivers/net/dsa/microchip/ksz_common.h
@@ -219,6 +219,9 @@ struct ksz_device {
 	 * the switch’s internal PHYs, bypassing the main SPI interface.
 	 */
 	struct mii_bus *parent_mdio_bus;
+
+	/* Equalizer low-loss workaround tunable */
+	u8 low_loss_wa_mode; /* bits [1:0]: 00 = disabled, 01 = workaround 1, 10 = workaround 2 */
 };
 
 /* List of supported models */

---
base-commit: 2d1373e4246da3b58e1df058374ed6b101804e07
change-id: 20260323-ksz87xx_errata_low_loss_connections-b65e76e2b403

Best regards,
--  
Fidelio Lawson <fidelio.lawson@exotec.com>


^ permalink raw reply related

* Re: [Intel-wired-lan] [PATCH iwl-next] igb: use ktime_get_real helpers in igb_ptp_reset()
From: Paul Menzel @ 2026-04-08 11:57 UTC (permalink / raw)
  To: Aleksandr Loktionov
  Cc: intel-wired-lan, anthony.l.nguyen, netdev, Jacob Keller,
	Simon Horman
In-Reply-To: <20260408083521.1620447-1-aleksandr.loktionov@intel.com>

Dear Aleksandr,


Thank you for your patch.

Am 08.04.26 um 10:35 schrieb Aleksandr Loktionov:
> Replace ktime_to_ns(ktime_get_real()) with the direct equivalent
> ktime_get_real_ns() and ktime_to_timespec64(ktime_get_real()) with
> ktime_get_real_ts64() in igb_ptp_reset().  Using the combined helpers
> avoids the unnecessary intermediate ktime_t variable and makes the
> intent clearer.

No intermediate variable is removed in the diff below. What am I missing?

> Suggested-by: Jacob Keller <jacob.e.keller@intel.com>
> Suggested-by: Simon Horman <horms@kernel.org>
> Signed-off-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
> ---
>   drivers/net/ethernet/intel/igb/igb_ptp.c | 5 +++--
>   1 file changed, 3 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/net/ethernet/intel/igb/igb_ptp.c b/drivers/net/ethernet/intel/igb/igb_ptp.c
> index bd85d02..638d824 100644
> --- a/drivers/net/ethernet/intel/igb/igb_ptp.c
> +++ b/drivers/net/ethernet/intel/igb/igb_ptp.c
> @@ -1500,12 +1500,13 @@ void igb_ptp_reset(struct igb_adapter *adapter)
>   
>   	/* Re-initialize the timer. */
>   	if ((hw->mac.type == e1000_i210) || (hw->mac.type == e1000_i211)) {
> -		struct timespec64 ts = ktime_to_timespec64(ktime_get_real());
> +		struct timespec64 ts;
>   
> +		ktime_get_real_ts64(&ts);
>   		igb_ptp_write_i210(adapter, &ts);
>   	} else {
>   		timecounter_init(&adapter->tc, &adapter->cc,
> -				 ktime_to_ns(ktime_get_real()));
> +				 ktime_get_real_ns());
>   	}
>   out:
>   	spin_unlock_irqrestore(&adapter->tmreg_lock, flags);

With the commit message clarified, feel free to add:

Reviewed-by: Paul Menzel <pmenzel@molgen.mpg.de>


Kind regards,

Paul

^ permalink raw reply

* RE: [Intel-wired-lan] [PATCH iwl-next 1/2] i40e: implement basic per-queue stats
From: Loktionov, Aleksandr @ 2026-04-08 12:07 UTC (permalink / raw)
  To: Paolo Abeni, intel-wired-lan@lists.osuosl.org
  Cc: Nguyen, Anthony L, Kitszel, Przemyslaw, Andrew Lunn,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Alexei Starovoitov,
	Daniel Borkmann, Jesper Dangaard Brouer, John Fastabend,
	Stanislav Fomichev, netdev@vger.kernel.org
In-Reply-To: <0815f1eb4b60faa653ea703e420395b724d05216.1775648513.git.pabeni@redhat.com>



> -----Original Message-----
> From: Intel-wired-lan <intel-wired-lan-bounces@osuosl.org> On Behalf
> Of Paolo Abeni
> Sent: Wednesday, April 8, 2026 1:44 PM
> To: intel-wired-lan@lists.osuosl.org
> Cc: Nguyen, Anthony L <anthony.l.nguyen@intel.com>; Kitszel,
> Przemyslaw <przemyslaw.kitszel@intel.com>; Andrew Lunn
> <andrew+netdev@lunn.ch>; David S. Miller <davem@davemloft.net>; Eric
> Dumazet <edumazet@google.com>; Jakub Kicinski <kuba@kernel.org>;
> Alexei Starovoitov <ast@kernel.org>; Daniel Borkmann
> <daniel@iogearbox.net>; Jesper Dangaard Brouer <hawk@kernel.org>; John
> Fastabend <john.fastabend@gmail.com>; Stanislav Fomichev
> <sdf@fomichev.me>; netdev@vger.kernel.org
> Subject: [Intel-wired-lan] [PATCH iwl-next 1/2] i40e: implement basic
> per-queue stats
> 
> Only expose the counters currently available (bytes, packets); add
> account for base stats to deal with ring clear.
> 
> Signed-off-by: Paolo Abeni <pabeni@redhat.com>
> ---
>  drivers/net/ethernet/intel/i40e/i40e.h      |   7 ++
>  drivers/net/ethernet/intel/i40e/i40e_main.c | 133
> ++++++++++++++++++++
>  2 files changed, 140 insertions(+)
> 
> diff --git a/drivers/net/ethernet/intel/i40e/i40e.h
> b/drivers/net/ethernet/intel/i40e/i40e.h
> index dcb50c2e1aa2..fe642c464e9c 100644
> --- a/drivers/net/ethernet/intel/i40e/i40e.h
> +++ b/drivers/net/ethernet/intel/i40e/i40e.h
> @@ -836,16 +836,23 @@ struct i40e_vsi {
>  	struct i40e_eth_stats eth_stats;
>  	struct i40e_eth_stats eth_stats_offsets;
>  	u64 tx_restart;

...

> +static void i40e_zero_tx_ring_stats(struct netdev_queue_stats_tx *tx)
> {
> +	tx->bytes = 0;
> +	tx->packets = 0;
> +	tx->stop = 0;
> +	tx->wake = 0;
> +	tx->hw_drops = 0;
> +}
> +
> +static void i40e_add_tx_ring_stats(struct i40e_ring *tx_ring,
> +				   struct netdev_queue_stats_tx *tx) {
> +	u64 bytes, packets;
> +	unsigned int start;
> +
> +	do {
> +		start = u64_stats_fetch_begin(&tx_ring->syncp);
> +		bytes = tx_ring->stats.bytes;
> +		packets = tx_ring->stats.packets;
> +	} while (u64_stats_fetch_retry(&tx_ring->syncp, start));
> +
> +	tx->bytes += bytes;
> +	tx->packets += packets;
> +
> +	tx->stop += tx_ring->tx_stats.tx_stopped;
> +	tx->wake += tx_ring->tx_stats.restart_queue;
> +	tx->hw_drops += tx_ring->tx_stats.tx_busy; }
Why the reads are outside the seqlock region? 
On 32-bit kernels, unprotected u64 reads can tear IMHO

> +
> +static void i40e_get_queue_stats_tx(struct net_device *dev, int idx,
> +				    struct netdev_queue_stats_tx *tx) {

...

>  	netdev->netdev_ops = &i40e_netdev_ops;
>  	netdev->watchdog_timeo = 5 * HZ;
>  	i40e_set_ethtool_ops(netdev);
> --
> 2.53.0


^ permalink raw reply

* RE: [Intel-wired-lan] [PATCH iwl-next 2/2] i40e: keep track of per queue gso counters.
From: Loktionov, Aleksandr @ 2026-04-08 12:08 UTC (permalink / raw)
  To: Paolo Abeni, intel-wired-lan@lists.osuosl.org
  Cc: Nguyen, Anthony L, Kitszel, Przemyslaw, Andrew Lunn,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Alexei Starovoitov,
	Daniel Borkmann, Jesper Dangaard Brouer, John Fastabend,
	Stanislav Fomichev, netdev@vger.kernel.org
In-Reply-To: <c70f2e9d8cbc4af419356ed022bc60a8c9cfc7d2.1775648513.git.pabeni@redhat.com>



> -----Original Message-----
> From: Intel-wired-lan <intel-wired-lan-bounces@osuosl.org> On Behalf
> Of Paolo Abeni
> Sent: Wednesday, April 8, 2026 1:44 PM
> To: intel-wired-lan@lists.osuosl.org
> Cc: Nguyen, Anthony L <anthony.l.nguyen@intel.com>; Kitszel,
> Przemyslaw <przemyslaw.kitszel@intel.com>; Andrew Lunn
> <andrew+netdev@lunn.ch>; David S. Miller <davem@davemloft.net>; Eric
> Dumazet <edumazet@google.com>; Jakub Kicinski <kuba@kernel.org>;
> Alexei Starovoitov <ast@kernel.org>; Daniel Borkmann
> <daniel@iogearbox.net>; Jesper Dangaard Brouer <hawk@kernel.org>; John
> Fastabend <john.fastabend@gmail.com>; Stanislav Fomichev
> <sdf@fomichev.me>; netdev@vger.kernel.org
> Subject: [Intel-wired-lan] [PATCH iwl-next 2/2] i40e: keep track of
> per queue gso counters.
> 
Please remove trailing '.'

Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>

> Track the number of GSO and wire packets transmitted and expose the
> counters via the queue stats.
> 
> Signed-off-by: Paolo Abeni <pabeni@redhat.com>
> ---
>  drivers/net/ethernet/intel/i40e/i40e.h             |  2 ++
>  drivers/net/ethernet/intel/i40e/i40e_main.c        | 13 ++++++++++++-
>  drivers/net/ethernet/intel/i40e/i40e_txrx.c        |  8 +++++++-
>  drivers/net/ethernet/intel/i40e/i40e_txrx.h        |  2 ++
>  drivers/net/ethernet/intel/i40e/i40e_txrx_common.h |  6 +++++-
>  drivers/net/ethernet/intel/i40e/i40e_xsk.c         |  2 +-
>  6 files changed, 29 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/net/ethernet/intel/i40e/i40e.h
> b/drivers/net/ethernet/intel/i40e/i40e.h
> index fe642c464e9c..4a88c7d69f61 100644
> --- a/drivers/net/ethernet/intel/i40e/i40e.h
> +++ b/drivers/net/ethernet/intel/i40e/i40e.h
> @@ -845,6 +845,8 @@ struct i40e_vsi {
>  	u64 tx_stopped_base;
>  	u64 tx_bytes;
>  	u64 tx_packets;

...

> 
>  	return nb_pkts < budget;
>  }
> --
> 2.53.0


^ permalink raw reply


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