From: Xin Xie <xiexinet@gmail.com>
To: netdev@vger.kernel.org, linux-kselftest@vger.kernel.org,
linux-kernel@vger.kernel.org
Cc: davem@davemloft.net, edumazet@kernel.org, kuba@kernel.org,
pabeni@redhat.com, horms@kernel.org, andrew+netdev@lunn.ch,
shuah@kernel.org, kees@kernel.org, petr.wozniak@gmail.com,
qingfang.deng@linux.dev, fmaurer@redhat.com,
luka.gejak@linux.dev, bigeasy@linutronix.de,
xiaoliang.yang_1@nxp.com, skhawaja@google.com,
liuhangbin@gmail.com, stable@vger.kernel.org,
sdf.kernel@gmail.com, xiexinet@gmail.com, lukma@denx.de,
fmancera@suse.de, danishanwar@ti.com, corbet@lwn.net,
skhan@linuxfoundation.org, rdunlap@infradead.org,
linux-doc@vger.kernel.org
Subject: [PATCH net v9 1/4] net: hsr: keep GRO disabled on HSR/PRP ports
Date: Thu, 12 Nov 2026 16:33:30 +0100 [thread overview]
Message-ID: <20261112153333.20-2-xiexinet@gmail.com> (raw)
In-Reply-To: <20261112153333.20-1-xiexinet@gmail.com>
HSR and PRP tag and de-duplicate each wire frame separately. GRO on a
member device merges several frames into one skb that cannot get valid
per-frame metadata, and userspace can turn GRO back on at any time.
Mark direct members (slave A/B, interlink) with a kernel role bit.
While it is set, __netdev_update_features() drops software GRO and
configurable GRO_HW from wanted_features as well as from the computed
features, before ndo_fix_features(). GRO_HW that the driver keeps on
is left alone. The request has to go too: veth adds and deletes its
NAPI instances according to wanted_features, so with GRO still
requested, closing an attached veth spins in napi_disable(), and
detaching a veth that was attached while down and then brought up hits
the BUG_ON() in napi_enable(). As for LRO disabled by an upper device,
GRO is not restored when the port leaves; ethtool can turn it back on.
The rule covers every direct member, PRP ports and ports with HSR
offload included: one simple rule is preferred to per-port exceptions,
at some cost in receive throughput.
The attach fails if software GRO stays active. A failed attach does
not restore the GRO request either. The next patch checks all ports
before any of them is changed, so only a failure at run time, for
example a driver that cannot turn GRO off or the upper device nesting
limit (-EMLINK), leaves a port with its GRO request dropped.
GSO skbs can still arrive, for example from a stacked member whose
lower device runs GRO; a later patch segments them.
Fixes: 5055cccfc2d1 ("net: hsr: Provide RedBox support (HSR-SAN)")
Signed-off-by: Xin Xie <xiexinet@gmail.com>
---
.../networking/net_cachelines/net_device.rst | 1 +
include/linux/netdevice.h | 8 +++
net/core/dev.c | 15 ++++++
net/hsr/hsr_slave.c | 54 +++++++++++++++++++
4 files changed, 78 insertions(+)
diff --git a/Documentation/networking/net_cachelines/net_device.rst b/Documentation/networking/net_cachelines/net_device.rst
index 8eceaa80b686..0d87e4d92a58 100644
--- a/Documentation/networking/net_cachelines/net_device.rst
+++ b/Documentation/networking/net_cachelines/net_device.rst
@@ -169,6 +169,7 @@ unsigned_long:1 see_all_hwtstamp_requests
unsigned_long:1 change_proto_down
unsigned_long:1 netns_immutable
unsigned_long:1 fcoe_mtu
+unsigned_long:1 hsr_port
struct list_head net_notifier_list
struct macsec_ops* macsec_ops
struct udp_tunnel_nic_info* udp_tunnel_nic_info
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 435be7880d7e..0d13c7ecdd1f 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -2110,6 +2110,7 @@ enum netdev_reg_state {
* @change_proto_down: device supports setting carrier via IFLA_PROTO_DOWN
* @netns_immutable: interface can't change network namespaces
* @fcoe_mtu: device supports maximum FCoE MTU, 2158 bytes
+ * @hsr_port: direct HSR/PRP member (slave A/B or interlink)
*
* @net_notifier_list: List of per-net netdev notifier block
* that follow this device when it is moved
@@ -2531,6 +2532,13 @@ struct net_device {
unsigned long change_proto_down:1;
unsigned long netns_immutable:1;
unsigned long fcoe_mtu:1;
+ /* Direct HSR/PRP member: slave A/B or interlink. While set,
+ * software GRO and configurable GRO_HW are dropped from the
+ * request and before ndo_fix_features(); GRO_HW that the driver
+ * keeps on is left alone. Kernel role state only; no user ABI or
+ * offload capability.
+ */
+ unsigned long hsr_port:1;
struct list_head net_notifier_list;
diff --git a/net/core/dev.c b/net/core/dev.c
index df1a840b0334..c8c2de212556 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -11269,6 +11269,21 @@ int __netdev_update_features(struct net_device *dev)
features = netdev_get_wanted_features(dev);
+ /* A direct HSR/PRP port needs per-frame metadata. Drop software
+ * GRO and configurable GRO_HW from the request as well as from the
+ * features passed to ndo_fix_features(), as the core does for
+ * upper-disabled features: drivers such as veth derive state from
+ * wanted_features and need the requested and the active bit to
+ * agree. Fixed-on or driver-required GRO_HW is left to the driver.
+ */
+ if (dev->hsr_port) {
+ netdev_features_t gro = NETIF_F_GRO |
+ (dev->hw_features & NETIF_F_GRO_HW);
+
+ dev->wanted_features &= ~gro;
+ features &= ~gro;
+ }
+
if (dev->netdev_ops->ndo_fix_features)
features = dev->netdev_ops->ndo_fix_features(dev, features);
diff --git a/net/hsr/hsr_slave.c b/net/hsr/hsr_slave.c
index a546f70f9cc8..b7765a4bb992 100644
--- a/net/hsr/hsr_slave.c
+++ b/net/hsr/hsr_slave.c
@@ -11,6 +11,7 @@
#include <linux/etherdevice.h>
#include <linux/if_arp.h>
#include <linux/if_vlan.h>
+#include <net/netdev_lock.h>
#include "hsr_main.h"
#include "hsr_device.h"
#include "hsr_forward.h"
@@ -137,6 +138,37 @@ static int hsr_check_dev_ok(struct net_device *dev,
return 0;
}
+/* Member role and feature policy.
+ * Setting the role makes __netdev_update_features() clear GRO
+ * and configurable GRO_HW from the request and before
+ * ndo_fix_features(). A macvlan follows GRO on its lower device,
+ * which can turn it back on later. Only the role and feature
+ * update run under the device ops lock; unlink, promiscuous
+ * updates and the master recompute stay outside.
+ */
+static void hsr_portdev_role_set(struct net_device *dev)
+{
+ netdev_lock_ops(dev);
+ dev->hsr_port = true;
+ netdev_change_features(dev);
+ netdev_unlock_ops(dev);
+}
+
+static void hsr_portdev_role_clear(struct net_device *dev)
+{
+ netdev_lock_ops(dev);
+ dev->hsr_port = false;
+ /* Recompute features without the role. GRO was dropped from
+ * the request while the role was set and is not requested
+ * again; the user can turn it back on with ethtool. A dying
+ * device just drops the role, a netns move stays registered and
+ * is recomputed.
+ */
+ if (dev->reg_state == NETREG_REGISTERED)
+ netdev_change_features(dev);
+ netdev_unlock_ops(dev);
+}
+
/* Setup device to be added to the HSR bridge. */
static int hsr_portdev_setup(struct hsr_priv *hsr, struct net_device *dev,
struct hsr_port *port,
@@ -169,6 +201,22 @@ static int hsr_portdev_setup(struct hsr_priv *hsr, struct net_device *dev,
if (res)
goto fail_upper_dev_link;
+ /* Enable the member role and recompute before the RX handler
+ * is published, so that software GRO on this device is off
+ * before frames can arrive. This covers the direct member
+ * only: a stacked member receives from a lower device that
+ * may still run GRO and can deliver merged frames as GSO skbs.
+ * A still-active software GRO rejects the port, a remaining
+ * GRO_HW alone does not.
+ */
+ hsr_portdev_role_set(dev);
+ if (dev->features & NETIF_F_GRO) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "software GRO still on after feature update, cannot join");
+ res = -EBUSY;
+ goto fail_role_policy;
+ }
+
res = netdev_rx_handler_register(dev, hsr_handle_frame, port);
if (res)
goto fail_rx_handler;
@@ -177,7 +225,12 @@ static int hsr_portdev_setup(struct hsr_priv *hsr, struct net_device *dev,
return 0;
fail_rx_handler:
+fail_role_policy:
+ /* Rollback order: unlink the upper, clear the role and
+ * recompute, then undo this setup's promiscuous increment.
+ */
netdev_upper_dev_unlink(dev, hsr_dev);
+ hsr_portdev_role_clear(dev);
fail_upper_dev_link:
if (!port->hsr->fwd_offloaded || port->type == HSR_PT_INTERLINK)
dev_set_promiscuity(dev, -1);
@@ -248,6 +301,7 @@ void hsr_del_port(struct hsr_port *port)
if (port->type == HSR_PT_SLAVE_A || port->type == HSR_PT_SLAVE_B)
vlan_vids_del_by_dev(port->dev, master->dev);
netdev_upper_dev_unlink(port->dev, master->dev);
+ hsr_portdev_role_clear(port->dev);
if (hsr->prot_version == PRP_V1 &&
port->type == HSR_PT_SLAVE_B) {
eth_hw_addr_set(port->dev, port->original_macaddress);
--
2.53.0
next prev parent reply other threads:[~2026-11-12 15:33 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-12 15:33 [PATCH net v9 0/4] net: hsr: fix super-packet forwarding Xin Xie
2026-11-12 15:33 ` Xin Xie [this message]
2026-11-12 15:33 ` [PATCH net v9 2/4] net: hsr: refuse forwarding devices as HSR/PRP ports Xin Xie
2026-11-12 15:33 ` [PATCH net v9 3/4] net: hsr: segment GSO before per-frame forwarding Xin Xie
2026-11-12 15:33 ` [PATCH net v9 4/4] selftests: net: hsr: verify GRO policy and ordered forwarding Xin Xie
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261112153333.20-2-xiexinet@gmail.com \
--to=xiexinet@gmail.com \
--cc=andrew+netdev@lunn.ch \
--cc=bigeasy@linutronix.de \
--cc=corbet@lwn.net \
--cc=danishanwar@ti.com \
--cc=davem@davemloft.net \
--cc=edumazet@kernel.org \
--cc=fmancera@suse.de \
--cc=fmaurer@redhat.com \
--cc=horms@kernel.org \
--cc=kees@kernel.org \
--cc=kuba@kernel.org \
--cc=linux-doc@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-kselftest@vger.kernel.org \
--cc=liuhangbin@gmail.com \
--cc=luka.gejak@linux.dev \
--cc=lukma@denx.de \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=petr.wozniak@gmail.com \
--cc=qingfang.deng@linux.dev \
--cc=rdunlap@infradead.org \
--cc=sdf.kernel@gmail.com \
--cc=shuah@kernel.org \
--cc=skhan@linuxfoundation.org \
--cc=skhawaja@google.com \
--cc=stable@vger.kernel.org \
--cc=xiaoliang.yang_1@nxp.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.