* [PATCH net-next v2 0/4] net: move netdev_compute_master_upper_features to ndo_set_features
@ 2026-03-13 3:03 Hangbin Liu
2026-03-13 3:03 ` [PATCH net-next v2 1/4] net: use ndo_set_features to set offload features for bonding/bridge/team Hangbin Liu
` (3 more replies)
0 siblings, 4 replies; 7+ messages in thread
From: Hangbin Liu @ 2026-03-13 3:03 UTC (permalink / raw)
To: Jay Vosburgh, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Jiri Pirko, Nikolay Aleksandrov,
Ido Schimmel, Simon Horman, Sabrina Dubroca, Sridhar Samudrala
Cc: netdev, linux-kernel, bridge, Hangbin Liu
Currently, master devices (bonding, bridge, team) manually call
netdev_compute_master_upper_features() scattered throughout their port
add/remove operations. This approach requires each driver to remember
to update features at the right times and leads to code duplication.
The series moves netdev_compute_master_upper_features() to callback
ndo_set_features so that the offload compute could automatically
invoked during feature updates when upper/lower device relationships
change. This centralizes the feature computation flow and removes the
burden from individual drivers.
---
Changes in v2:
- Fix macsec Security Entity uninitialized issue (Sabrina Dubroca)
- Tested with macsec-offload-sh, rtnetlink-sh, link-netns-py, all pssed
- Link to v1: https://lore.kernel.org/r/20260310-offload_compute-v1-0-3df79c09ea65@gmail.com
---
Hangbin Liu (4):
net: use ndo_set_features to set offload features for bonding/bridge/team
macsec: move netdev_upper_dev_link() after macsec_changelink_common()
failover: use ndo_set_features for failover offload compute
net: no need to disable LRO specifically
drivers/net/bonding/bond_main.c | 14 +++++----
drivers/net/macsec.c | 23 +++++++-------
drivers/net/net_failover.c | 67 +++++------------------------------------
drivers/net/team/team_core.c | 15 ++++-----
include/net/net_failover.h | 7 -----
net/8021q/vlan.c | 2 --
net/bridge/br_device.c | 7 +++++
net/bridge/br_if.c | 6 ----
net/core/dev.c | 8 +++--
net/hsr/hsr_slave.c | 1 -
10 files changed, 49 insertions(+), 101 deletions(-)
---
base-commit: 06fc88a6973fa6203c7c0cd3f5cef9d3405928ca
change-id: 20260310-offload_compute-4c0bafa2e022
Best regards,
--
Hangbin Liu <liuhangbin@gmail.com>
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH net-next v2 1/4] net: use ndo_set_features to set offload features for bonding/bridge/team
2026-03-13 3:03 [PATCH net-next v2 0/4] net: move netdev_compute_master_upper_features to ndo_set_features Hangbin Liu
@ 2026-03-13 3:03 ` Hangbin Liu
2026-03-15 16:19 ` [net-next,v2,1/4] " Simon Horman
2026-03-13 3:03 ` [PATCH net-next v2 2/4] macsec: move netdev_upper_dev_link() after macsec_changelink_common() Hangbin Liu
` (2 subsequent siblings)
3 siblings, 1 reply; 7+ messages in thread
From: Hangbin Liu @ 2026-03-13 3:03 UTC (permalink / raw)
To: Jay Vosburgh, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Jiri Pirko, Nikolay Aleksandrov,
Ido Schimmel, Simon Horman, Sabrina Dubroca, Sridhar Samudrala
Cc: netdev, linux-kernel, bridge, Hangbin Liu
Convert bonding, bridge, and team drivers to use ndo_set_features callback
instead of manually calling netdev_compute_master_upper_features() during
port add/remove operations.
This change centralizes the feature computation flow:
Before:
- netdev_master_upper_dev_link()
- netdev_compute_master_upper_features() <- called manually
- compute offload features
- netdev_change_features()
- __netdev_update_features()
- update other features
After:
- netdev_master_upper_dev_link()
- __netdev_upper_dev_link()
- netdev_change_features()
- __netdev_update_features()
- ndo_set_features()
- netdev_compute_master_upper_features()
This ensures offload features are computed automatically when
upper/lower device links change, removing the need for manual
feature computation calls in the driver code.
The netdev_change_features() call in team_uninit() is also removed
since it calls team_port_del() for each port, which now triggers
feature updates automatically.
Signed-off-by: Hangbin Liu <liuhangbin@gmail.com>
---
drivers/net/bonding/bond_main.c | 11 ++++++++---
drivers/net/team/team_core.c | 12 ++++++++----
net/bridge/br_device.c | 7 +++++++
net/bridge/br_if.c | 4 ----
net/core/dev.c | 8 ++++++--
5 files changed, 29 insertions(+), 13 deletions(-)
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index 90b71bd53040..2c84d3ce3b04 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -1509,6 +1509,12 @@ static netdev_features_t bond_fix_features(struct net_device *dev,
return features;
}
+static int bond_set_features(struct net_device *dev, netdev_features_t features)
+{
+ netdev_compute_master_upper_features(dev, true);
+ return 0;
+}
+
static int bond_header_create(struct sk_buff *skb, struct net_device *bond_dev,
unsigned short type, const void *daddr,
const void *saddr, unsigned int len)
@@ -2273,7 +2279,6 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev,
}
bond->slave_cnt++;
- netdev_compute_master_upper_features(bond->dev, true);
bond_set_carrier(bond);
/* Needs to be called before bond_select_active_slave(), which will
@@ -2528,7 +2533,6 @@ static int __bond_release_one(struct net_device *bond_dev,
call_netdevice_notifiers(NETDEV_RELEASE, bond->dev);
}
- netdev_compute_master_upper_features(bond->dev, true);
if (!(bond_dev->features & NETIF_F_VLAN_CHALLENGED) &&
(old_features & NETIF_F_VLAN_CHALLENGED))
slave_info(bond_dev, slave_dev, "last VLAN challenged slave left bond - VLAN blocking is removed\n");
@@ -4026,7 +4030,7 @@ static int bond_slave_netdev_event(unsigned long event,
case NETDEV_FEAT_CHANGE:
if (!bond->notifier_ctx) {
bond->notifier_ctx = true;
- netdev_compute_master_upper_features(bond->dev, true);
+ netdev_change_features(bond->dev);
bond->notifier_ctx = false;
}
break;
@@ -5953,6 +5957,7 @@ static const struct net_device_ops bond_netdev_ops = {
.ndo_add_slave = bond_enslave,
.ndo_del_slave = bond_release,
.ndo_fix_features = bond_fix_features,
+ .ndo_set_features = bond_set_features,
.ndo_features_check = passthru_features_check,
.ndo_get_xmit_slave = bond_xmit_get_slave,
.ndo_sk_get_lower_dev = bond_sk_get_lower_dev,
diff --git a/drivers/net/team/team_core.c b/drivers/net/team/team_core.c
index b7282f5c9632..4906ea3717b1 100644
--- a/drivers/net/team/team_core.c
+++ b/drivers/net/team/team_core.c
@@ -1247,7 +1247,6 @@ static int team_port_add(struct team *team, struct net_device *port_dev,
port->index = -1;
list_add_tail_rcu(&port->list, &team->port_list);
team_port_enable(team, port);
- netdev_compute_master_upper_features(team->dev, true);
__team_port_change_port_added(port, !!netif_oper_up(port_dev));
__team_options_change_check(team);
@@ -1337,7 +1336,6 @@ static int team_port_del(struct team *team, struct net_device *port_dev, bool un
}
kfree_rcu(port, rcu);
netdev_info(dev, "Port device %s removed\n", portname);
- netdev_compute_master_upper_features(team->dev, true);
return 0;
}
@@ -1645,7 +1643,6 @@ static void team_uninit(struct net_device *dev)
team_mcast_rejoin_fini(team);
team_notify_peers_fini(team);
team_queue_override_fini(team);
- netdev_change_features(dev);
}
static void team_destructor(struct net_device *dev)
@@ -1972,6 +1969,12 @@ static netdev_features_t team_fix_features(struct net_device *dev,
return features;
}
+static int team_set_features(struct net_device *dev, netdev_features_t features)
+{
+ netdev_compute_master_upper_features(dev, true);
+ return 0;
+}
+
static int team_change_carrier(struct net_device *dev, bool new_carrier)
{
struct team *team = netdev_priv(dev);
@@ -2007,6 +2010,7 @@ static const struct net_device_ops team_netdev_ops = {
.ndo_add_slave = team_add_slave,
.ndo_del_slave = team_del_slave,
.ndo_fix_features = team_fix_features,
+ .ndo_set_features = team_set_features,
.ndo_change_carrier = team_change_carrier,
.ndo_features_check = passthru_features_check,
};
@@ -2944,7 +2948,7 @@ static int team_device_event(struct notifier_block *unused,
case NETDEV_FEAT_CHANGE:
if (!port->team->notifier_ctx) {
port->team->notifier_ctx = true;
- netdev_compute_master_upper_features(port->team->dev, true);
+ netdev_change_features(port->team->dev);
port->team->notifier_ctx = false;
}
break;
diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
index f7502e62dd35..f82082050e36 100644
--- a/net/bridge/br_device.c
+++ b/net/bridge/br_device.c
@@ -289,6 +289,12 @@ static netdev_features_t br_fix_features(struct net_device *dev,
return br_features_recompute(br, features);
}
+static int br_set_features(struct net_device *dev, netdev_features_t features)
+{
+ netdev_compute_master_upper_features(dev, false);
+ return 0;
+}
+
#ifdef CONFIG_NET_POLL_CONTROLLER
static void br_poll_controller(struct net_device *br_dev)
{
@@ -456,6 +462,7 @@ static const struct net_device_ops br_netdev_ops = {
.ndo_add_slave = br_add_slave,
.ndo_del_slave = br_del_slave,
.ndo_fix_features = br_fix_features,
+ .ndo_set_features = br_set_features,
.ndo_fdb_add = br_fdb_add,
.ndo_fdb_del = br_fdb_delete,
.ndo_fdb_del_bulk = br_fdb_delete_bulk,
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
index d39571e13744..030248bc94c5 100644
--- a/net/bridge/br_if.c
+++ b/net/bridge/br_if.c
@@ -680,8 +680,6 @@ int br_add_if(struct net_bridge *br, struct net_device *dev,
br_mtu_auto_adjust(br);
- netdev_compute_master_upper_features(br->dev, false);
-
kobject_uevent(&p->kobj, KOBJ_ADD);
return 0;
@@ -734,8 +732,6 @@ int br_del_if(struct net_bridge *br, struct net_device *dev)
if (changed_addr)
call_netdevice_notifiers(NETDEV_CHANGEADDR, br->dev);
- netdev_compute_master_upper_features(br->dev, false);
-
return 0;
}
diff --git a/net/core/dev.c b/net/core/dev.c
index f48dc299e4b2..16b3fc620807 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -8920,6 +8920,9 @@ static int __netdev_upper_dev_link(struct net_device *dev,
__netdev_walk_all_upper_dev(upper_dev, __netdev_update_lower_level,
priv);
+ /* re-compute all features after adding link */
+ netdev_change_features(upper_dev);
+
return 0;
rollback:
@@ -9012,6 +9015,9 @@ static void __netdev_upper_dev_unlink(struct net_device *dev,
__netdev_update_lower_level(upper_dev, priv);
__netdev_walk_all_upper_dev(upper_dev, __netdev_update_lower_level,
priv);
+
+ /* re-compute all features after removing link */
+ netdev_change_features(upper_dev);
}
/**
@@ -12874,8 +12880,6 @@ void netdev_compute_master_upper_features(struct net_device *dev, bool update_he
netif_set_tso_max_segs(dev, tso_max_segs);
netif_set_tso_max_size(dev, tso_max_size);
-
- netdev_change_features(dev);
}
EXPORT_SYMBOL(netdev_compute_master_upper_features);
--
Git-155)
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH net-next v2 2/4] macsec: move netdev_upper_dev_link() after macsec_changelink_common()
2026-03-13 3:03 [PATCH net-next v2 0/4] net: move netdev_compute_master_upper_features to ndo_set_features Hangbin Liu
2026-03-13 3:03 ` [PATCH net-next v2 1/4] net: use ndo_set_features to set offload features for bonding/bridge/team Hangbin Liu
@ 2026-03-13 3:03 ` Hangbin Liu
2026-03-13 3:03 ` [PATCH net-next v2 3/4] failover: use ndo_set_features for failover offload compute Hangbin Liu
2026-03-13 3:03 ` [PATCH net-next v2 4/4] net: no need to disable LRO specifically Hangbin Liu
3 siblings, 0 replies; 7+ messages in thread
From: Hangbin Liu @ 2026-03-13 3:03 UTC (permalink / raw)
To: Jay Vosburgh, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Jiri Pirko, Nikolay Aleksandrov,
Ido Schimmel, Simon Horman, Sabrina Dubroca, Sridhar Samudrala
Cc: netdev, linux-kernel, bridge, Hangbin Liu
After calling netdev_change_features() in __netdev_upper_dev_link(),
the call stack looks like:
- netdev_upper_dev_link
- __netdev_upper_dev_link
- netdev_change_features
- notifier_call_chain
- rtnetlink_event
- rtmsg_ifinfo_event
- rtmsg_ifinfo_build_skb
In macsec, we call netdev_upper_dev_link() before
macsec_changelink_common(), which causes the fields of the MACsec
Security Entity to be uninitialized. Later, macsec_fill_info() returns
-EMSGSIZE and triggers WARN_ON() in rtmsg_ifinfo_build_skb.
Fix this by moving netdev_upper_dev_link() after
macsec_changelink_common(), and return 0 if secy->key_len is not
initialized yet.
Suggested-by: Sabrina Dubroca <sd@queasysnail.net>
Signed-off-by: Hangbin Liu <liuhangbin@gmail.com>
---
drivers/net/macsec.c | 23 ++++++++++++-----------
1 file changed, 12 insertions(+), 11 deletions(-)
diff --git a/drivers/net/macsec.c b/drivers/net/macsec.c
index f6cad0746a02..6bb38084bc1e 100644
--- a/drivers/net/macsec.c
+++ b/drivers/net/macsec.c
@@ -4161,10 +4161,6 @@ static int macsec_newlink(struct net_device *dev,
lockdep_set_class(&dev->addr_list_lock,
&macsec_netdev_addr_lock_key);
- err = netdev_upper_dev_link(real_dev, dev, extack);
- if (err < 0)
- goto unregister;
-
/* need to be already registered so that ->init has run and
* the MAC addr is set
*/
@@ -4177,12 +4173,12 @@ static int macsec_newlink(struct net_device *dev,
if (rx_handler && sci_exists(real_dev, sci)) {
err = -EBUSY;
- goto unlink;
+ goto unregister;
}
err = macsec_add_dev(dev, sci, icv_len);
if (err)
- goto unlink;
+ goto unregister;
if (data) {
err = macsec_changelink_common(dev, data);
@@ -4190,6 +4186,10 @@ static int macsec_newlink(struct net_device *dev,
goto del_dev;
}
+ err = netdev_upper_dev_link(real_dev, dev, extack);
+ if (err < 0)
+ goto del_dev;
+
/* If h/w offloading is available, propagate to the device */
if (macsec_is_offloaded(macsec)) {
const struct macsec_ops *ops;
@@ -4200,7 +4200,7 @@ static int macsec_newlink(struct net_device *dev,
ctx.secy = &macsec->secy;
err = macsec_offload(ops->mdo_add_secy, &ctx);
if (err)
- goto del_dev;
+ goto unlink;
macsec->insert_tx_tag =
macsec_needs_tx_tag(macsec, ops);
@@ -4209,7 +4209,7 @@ static int macsec_newlink(struct net_device *dev,
err = register_macsec_dev(real_dev, dev);
if (err < 0)
- goto del_dev;
+ goto unlink;
netdev_update_features(dev);
netif_stacked_transfer_operstate(real_dev, dev);
@@ -4219,10 +4219,10 @@ static int macsec_newlink(struct net_device *dev,
return 0;
-del_dev:
- macsec_del_dev(macsec);
unlink:
netdev_upper_dev_unlink(real_dev, dev);
+del_dev:
+ macsec_del_dev(macsec);
unregister:
unregister_netdevice(dev);
return err;
@@ -4337,7 +4337,8 @@ static int macsec_fill_info(struct sk_buff *skb,
csid = secy->xpn ? MACSEC_CIPHER_ID_GCM_AES_XPN_256 : MACSEC_CIPHER_ID_GCM_AES_256;
break;
default:
- goto nla_put_failure;
+ WARN_ON_ONCE(1);
+ return 0;
}
if (nla_put_sci(skb, IFLA_MACSEC_SCI, secy->sci,
--
Git-155)
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH net-next v2 3/4] failover: use ndo_set_features for failover offload compute
2026-03-13 3:03 [PATCH net-next v2 0/4] net: move netdev_compute_master_upper_features to ndo_set_features Hangbin Liu
2026-03-13 3:03 ` [PATCH net-next v2 1/4] net: use ndo_set_features to set offload features for bonding/bridge/team Hangbin Liu
2026-03-13 3:03 ` [PATCH net-next v2 2/4] macsec: move netdev_upper_dev_link() after macsec_changelink_common() Hangbin Liu
@ 2026-03-13 3:03 ` Hangbin Liu
2026-03-13 3:03 ` [PATCH net-next v2 4/4] net: no need to disable LRO specifically Hangbin Liu
3 siblings, 0 replies; 7+ messages in thread
From: Hangbin Liu @ 2026-03-13 3:03 UTC (permalink / raw)
To: Jay Vosburgh, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Jiri Pirko, Nikolay Aleksandrov,
Ido Schimmel, Simon Horman, Sabrina Dubroca, Sridhar Samudrala
Cc: netdev, linux-kernel, bridge, Hangbin Liu
Convert net_failover to use the ndo_set_features callback and calls
netdev_compute_master_upper_features() to compute new offload flags
during slave registration/unregistration.
This simplifies the failover code significantly by removing the custom
feature computation logic and relying on the centralized feature update
mechanism. The callback is automatically invoked during feature updates
when upper/lower device links change.
Signed-off-by: Hangbin Liu <liuhangbin@gmail.com>
---
drivers/net/net_failover.c | 67 ++++++----------------------------------------
include/net/net_failover.h | 7 -----
2 files changed, 8 insertions(+), 66 deletions(-)
diff --git a/drivers/net/net_failover.c b/drivers/net/net_failover.c
index d0361aaf25ef..10041a271bf5 100644
--- a/drivers/net/net_failover.c
+++ b/drivers/net/net_failover.c
@@ -300,6 +300,12 @@ static int net_failover_vlan_rx_kill_vid(struct net_device *dev, __be16 proto,
return 0;
}
+static int net_failover_set_features(struct net_device *dev, netdev_features_t features)
+{
+ netdev_compute_master_upper_features(dev, true);
+ return 0;
+}
+
static const struct net_device_ops failover_dev_ops = {
.ndo_open = net_failover_open,
.ndo_stop = net_failover_close,
@@ -312,6 +318,7 @@ static const struct net_device_ops failover_dev_ops = {
.ndo_vlan_rx_kill_vid = net_failover_vlan_rx_kill_vid,
.ndo_validate_addr = eth_validate_addr,
.ndo_features_check = passthru_features_check,
+ .ndo_set_features = net_failover_set_features,
};
#define FAILOVER_NAME "net_failover"
@@ -373,61 +380,6 @@ static rx_handler_result_t net_failover_handle_frame(struct sk_buff **pskb)
return RX_HANDLER_ANOTHER;
}
-static void net_failover_compute_features(struct net_device *dev)
-{
- netdev_features_t vlan_features = FAILOVER_VLAN_FEATURES &
- NETIF_F_ALL_FOR_ALL;
- netdev_features_t enc_features = FAILOVER_ENC_FEATURES;
- unsigned short max_hard_header_len = ETH_HLEN;
- unsigned int dst_release_flag = IFF_XMIT_DST_RELEASE |
- IFF_XMIT_DST_RELEASE_PERM;
- struct net_failover_info *nfo_info = netdev_priv(dev);
- struct net_device *primary_dev, *standby_dev;
-
- primary_dev = rcu_dereference(nfo_info->primary_dev);
- if (primary_dev) {
- vlan_features =
- netdev_increment_features(vlan_features,
- primary_dev->vlan_features,
- FAILOVER_VLAN_FEATURES);
- enc_features =
- netdev_increment_features(enc_features,
- primary_dev->hw_enc_features,
- FAILOVER_ENC_FEATURES);
-
- dst_release_flag &= primary_dev->priv_flags;
- if (primary_dev->hard_header_len > max_hard_header_len)
- max_hard_header_len = primary_dev->hard_header_len;
- }
-
- standby_dev = rcu_dereference(nfo_info->standby_dev);
- if (standby_dev) {
- vlan_features =
- netdev_increment_features(vlan_features,
- standby_dev->vlan_features,
- FAILOVER_VLAN_FEATURES);
- enc_features =
- netdev_increment_features(enc_features,
- standby_dev->hw_enc_features,
- FAILOVER_ENC_FEATURES);
-
- dst_release_flag &= standby_dev->priv_flags;
- if (standby_dev->hard_header_len > max_hard_header_len)
- max_hard_header_len = standby_dev->hard_header_len;
- }
-
- dev->vlan_features = vlan_features;
- dev->hw_enc_features = enc_features | NETIF_F_GSO_ENCAP_ALL;
- dev->hard_header_len = max_hard_header_len;
-
- dev->priv_flags &= ~IFF_XMIT_DST_RELEASE;
- if (dst_release_flag == (IFF_XMIT_DST_RELEASE |
- IFF_XMIT_DST_RELEASE_PERM))
- dev->priv_flags |= IFF_XMIT_DST_RELEASE;
-
- netdev_change_features(dev);
-}
-
static void net_failover_lower_state_changed(struct net_device *slave_dev,
struct net_device *primary_dev,
struct net_device *standby_dev)
@@ -550,7 +502,6 @@ static int net_failover_slave_register(struct net_device *slave_dev,
}
net_failover_lower_state_changed(slave_dev, primary_dev, standby_dev);
- net_failover_compute_features(failover_dev);
call_netdevice_notifiers(NETDEV_JOIN, slave_dev);
@@ -621,8 +572,6 @@ static int net_failover_slave_unregister(struct net_device *slave_dev,
dev_put(slave_dev);
- net_failover_compute_features(failover_dev);
-
netdev_info(failover_dev, "failover %s slave:%s unregistered\n",
slave_is_standby ? "standby" : "primary", slave_dev->name);
@@ -736,7 +685,7 @@ struct failover *net_failover_create(struct net_device *standby_dev)
/* Don't allow failover devices to change network namespaces. */
failover_dev->netns_immutable = true;
- failover_dev->hw_features = FAILOVER_VLAN_FEATURES |
+ failover_dev->hw_features = MASTER_UPPER_DEV_VLAN_FEATURES |
NETIF_F_HW_VLAN_CTAG_TX |
NETIF_F_HW_VLAN_CTAG_RX |
NETIF_F_HW_VLAN_CTAG_FILTER;
diff --git a/include/net/net_failover.h b/include/net/net_failover.h
index b12a1c469d1c..f0f038d113a0 100644
--- a/include/net/net_failover.h
+++ b/include/net/net_failover.h
@@ -30,11 +30,4 @@ struct net_failover_info {
struct failover *net_failover_create(struct net_device *standby_dev);
void net_failover_destroy(struct failover *failover);
-#define FAILOVER_VLAN_FEATURES (NETIF_F_HW_CSUM | NETIF_F_SG | \
- NETIF_F_FRAGLIST | NETIF_F_ALL_TSO | \
- NETIF_F_HIGHDMA | NETIF_F_LRO)
-
-#define FAILOVER_ENC_FEATURES (NETIF_F_HW_CSUM | NETIF_F_SG | \
- NETIF_F_RXCSUM | NETIF_F_ALL_TSO)
-
#endif /* _NET_FAILOVER_H */
--
Git-155)
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH net-next v2 4/4] net: no need to disable LRO specifically
2026-03-13 3:03 [PATCH net-next v2 0/4] net: move netdev_compute_master_upper_features to ndo_set_features Hangbin Liu
` (2 preceding siblings ...)
2026-03-13 3:03 ` [PATCH net-next v2 3/4] failover: use ndo_set_features for failover offload compute Hangbin Liu
@ 2026-03-13 3:03 ` Hangbin Liu
3 siblings, 0 replies; 7+ messages in thread
From: Hangbin Liu @ 2026-03-13 3:03 UTC (permalink / raw)
To: Jay Vosburgh, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Jiri Pirko, Nikolay Aleksandrov,
Ido Schimmel, Simon Horman, Sabrina Dubroca, Sridhar Samudrala
Cc: netdev, linux-kernel, bridge, Hangbin Liu
In commit "net: use ndo_set_features to set offload features for
bonding/bridge/team" we called netdev_change_features() in
__netdev_upper_dev_link(), which will disable LRO automatically on lower
device if upper device doesn't have LRO enabled. So we don't need to
dev_disable_lro() again after netdev_upper_dev_link().
Signed-off-by: Hangbin Liu <liuhangbin@gmail.com>
---
drivers/net/bonding/bond_main.c | 3 ---
drivers/net/team/team_core.c | 3 ---
net/8021q/vlan.c | 2 --
net/bridge/br_if.c | 2 --
net/hsr/hsr_slave.c | 1 -
5 files changed, 11 deletions(-)
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index 2c84d3ce3b04..57620e93cc37 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -2222,9 +2222,6 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev,
}
#endif
- if (!(bond_dev->features & NETIF_F_LRO))
- dev_disable_lro(slave_dev);
-
res = netdev_rx_handler_register(slave_dev, bond_handle_frame,
new_slave);
if (res) {
diff --git a/drivers/net/team/team_core.c b/drivers/net/team/team_core.c
index 4906ea3717b1..73d4f68a7ccd 100644
--- a/drivers/net/team/team_core.c
+++ b/drivers/net/team/team_core.c
@@ -1191,9 +1191,6 @@ static int team_port_add(struct team *team, struct net_device *port_dev,
goto err_enable_netpoll;
}
- if (!(dev->features & NETIF_F_LRO))
- dev_disable_lro(port_dev);
-
err = netdev_rx_handler_register(port_dev, team_handle_frame,
port);
if (err) {
diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c
index 2b74ed56eb16..fda3a80e9340 100644
--- a/net/8021q/vlan.c
+++ b/net/8021q/vlan.c
@@ -193,8 +193,6 @@ int register_vlan_dev(struct net_device *dev, struct netlink_ext_ack *extack)
vlan_group_set_device(grp, vlan->vlan_proto, vlan_id, dev);
grp->nr_vlan_devs++;
- netdev_update_features(dev);
-
return 0;
out_unregister_netdev:
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
index 030248bc94c5..0aa653a1e651 100644
--- a/net/bridge/br_if.c
+++ b/net/bridge/br_if.c
@@ -620,8 +620,6 @@ int br_add_if(struct net_bridge *br, struct net_device *dev,
if (err)
goto err5;
- dev_disable_lro(dev);
-
list_add_rcu(&p->list, &br->port_list);
nbp_update_port_count(br);
diff --git a/net/hsr/hsr_slave.c b/net/hsr/hsr_slave.c
index 44f83c8c56a7..4b6ab185392b 100644
--- a/net/hsr/hsr_slave.c
+++ b/net/hsr/hsr_slave.c
@@ -169,7 +169,6 @@ static int hsr_portdev_setup(struct hsr_priv *hsr, struct net_device *dev,
res = netdev_rx_handler_register(dev, hsr_handle_frame, port);
if (res)
goto fail_rx_handler;
- dev_disable_lro(dev);
return 0;
--
Git-155)
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [net-next,v2,1/4] net: use ndo_set_features to set offload features for bonding/bridge/team
2026-03-13 3:03 ` [PATCH net-next v2 1/4] net: use ndo_set_features to set offload features for bonding/bridge/team Hangbin Liu
@ 2026-03-15 16:19 ` Simon Horman
2026-03-16 1:02 ` Hangbin Liu
0 siblings, 1 reply; 7+ messages in thread
From: Simon Horman @ 2026-03-15 16:19 UTC (permalink / raw)
To: liuhangbin
Cc: Simon Horman, jv, linux-kernel, netdev, davem, sd, andrew+netdev,
pabeni, sridhar.samudrala, jiri, bridge, edumazet, kuba, razor,
idosch
This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
For local reproduction steps see https://netdev-ai.bots.linux.dev/ai-local.html
---
net: use ndo_set_features to set offload features for bonding/bridge/team
This commit converts bonding, bridge, and team drivers to use the
ndo_set_features callback instead of manually calling
netdev_compute_master_upper_features() during port add/remove operations.
> diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
> index 90b71bd53040..2c84d3ce3b04 100644
> --- a/drivers/net/bonding/bond_main.c
> +++ b/drivers/net/bonding/bond_main.c
[ ... ]
> @@ -5953,6 +5957,7 @@ static const struct net_device_ops bond_netdev_ops = {
> .ndo_add_slave = bond_enslave,
> .ndo_del_slave = bond_release,
> .ndo_fix_features = bond_fix_features,
> + .ndo_set_features = bond_set_features,
> .ndo_features_check = passthru_features_check,
> .ndo_get_xmit_slave = bond_xmit_get_slave,
> .ndo_sk_get_lower_dev = bond_sk_get_lower_dev,
[ ... ]
> diff --git a/drivers/net/team/team_core.c b/drivers/net/team/team_core.c
> index b7282f5c9632..4906ea3717b1 100644
> --- a/drivers/net/team/team_core.c
> +++ b/drivers/net/team/team_core.c
[ ... ]
> @@ -2007,6 +2010,7 @@ static const struct net_device_ops team_netdev_ops = {
> .ndo_add_slave = team_add_slave,
> .ndo_del_slave = team_del_slave,
> .ndo_fix_features = team_fix_features,
> + .ndo_set_features = team_set_features,
> .ndo_change_carrier = team_change_carrier,
> .ndo_features_check = passthru_features_check,
> };
[ ... ]
> diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
> index f7502e62dd35..f82082050e36 100644
> --- a/net/bridge/br_device.c
> +++ b/net/bridge/br_device.c
[ ... ]
> @@ -456,6 +462,7 @@ static const struct net_device_ops br_netdev_ops = {
> .ndo_add_slave = br_add_slave,
> .ndo_del_slave = br_del_slave,
> .ndo_fix_features = br_fix_features,
> + .ndo_set_features = br_set_features,
> .ndo_fdb_add = br_fdb_add,
> .ndo_fdb_del = br_fdb_delete,
> .ndo_fdb_del_bulk = br_fdb_delete_bulk,
[ ... ]
> diff --git a/net/core/dev.c b/net/core/dev.c
> index f48dc299e4b2..16b3fc620807 100644
> --- a/net/core/dev.c
> +++ b/net/core/dev.c
> @@ -8920,6 +8920,9 @@ static int __netdev_upper_dev_link(struct net_device *dev,
> __netdev_walk_all_upper_dev(upper_dev, __netdev_update_lower_level,
> priv);
>
> + /* re-compute all features after adding link */
> + netdev_change_features(upper_dev);
> +
> return 0;
>
> rollback:
Can the feature computation skip updating ancillary fields when features
don't change?
The new architecture routes computation through netdev_change_features() ->
__netdev_update_features() -> ndo_set_features(). However,
__netdev_update_features() has this check:
net/core/dev.c:__netdev_update_features() {
...
if (dev->features == features)
goto sync_lower;
This skips the ndo_set_features callback entirely when computed features
match current features. This means netdev_compute_master_upper_features() is
never called when dev->features doesn't change, even though it updates
several ancillary fields independent of dev->features:
- vlan_features
- hw_enc_features
- mpls_features
- gso_partial_features
- tso_max_size
- tso_max_segs
- hard_header_len
- needed_headroom
- needed_tailroom
For example, adding a second bond slave with identical dev->features but
different tso_max_size or vlan_features will not trigger ndo_set_features,
leaving the bond's ancillary fields stale.
In the old code, netdev_compute_master_upper_features() was called
unconditionally and always updated these fields regardless of whether
dev->features changed.
> @@ -9012,6 +9015,9 @@ static void __netdev_upper_dev_unlink(struct net_device *dev,
> __netdev_update_lower_level(upper_dev, priv);
> __netdev_walk_all_upper_dev(upper_dev, __netdev_update_lower_level,
> priv);
> +
> + /* re-compute all features after removing link */
> + netdev_change_features(upper_dev);
> }
The same issue applies here. When a port is removed but dev->features
doesn't change, the ancillary fields won't be recomputed.
> @@ -12874,8 +12880,6 @@ void netdev_compute_master_upper_features(struct net_device *dev, bool update_he
>
> netif_set_tso_max_segs(dev, tso_max_segs);
> netif_set_tso_max_size(dev, tso_max_size);
> -
> - netdev_change_features(dev);
> }
> EXPORT_SYMBOL(netdev_compute_master_upper_features);
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [net-next,v2,1/4] net: use ndo_set_features to set offload features for bonding/bridge/team
2026-03-15 16:19 ` [net-next,v2,1/4] " Simon Horman
@ 2026-03-16 1:02 ` Hangbin Liu
0 siblings, 0 replies; 7+ messages in thread
From: Hangbin Liu @ 2026-03-16 1:02 UTC (permalink / raw)
To: Simon Horman
Cc: jv, linux-kernel, netdev, davem, sd, andrew+netdev, pabeni,
sridhar.samudrala, jiri, bridge, edumazet, kuba, razor, idosch
On Sun, Mar 15, 2026 at 04:19:43PM +0000, Simon Horman wrote:
> > diff --git a/net/core/dev.c b/net/core/dev.c
> > index f48dc299e4b2..16b3fc620807 100644
> > --- a/net/core/dev.c
> > +++ b/net/core/dev.c
> > @@ -8920,6 +8920,9 @@ static int __netdev_upper_dev_link(struct net_device *dev,
> > __netdev_walk_all_upper_dev(upper_dev, __netdev_update_lower_level,
> > priv);
> >
> > + /* re-compute all features after adding link */
> > + netdev_change_features(upper_dev);
> > +
> > return 0;
> >
> > rollback:
>
> Can the feature computation skip updating ancillary fields when features
> don't change?
>
> The new architecture routes computation through netdev_change_features() ->
> __netdev_update_features() -> ndo_set_features(). However,
> __netdev_update_features() has this check:
>
> net/core/dev.c:__netdev_update_features() {
> ...
> if (dev->features == features)
> goto sync_lower;
>
Ah, yes. In the RFC patch set[1], I wanted to use a new ndo_update_offloads
callback to handle offload updates. But in the formal patch, I thought we
could avoid introducing a new callback and use ndo_set_features directly.
However, it looks like we need to fall back to ndo_update_offloads now.
[1] https://lore.kernel.org/netdev/20260226114208.27774-1-liuhangbin@gmail.com/
Thanks
Hangbin
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-03-16 1:02 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-13 3:03 [PATCH net-next v2 0/4] net: move netdev_compute_master_upper_features to ndo_set_features Hangbin Liu
2026-03-13 3:03 ` [PATCH net-next v2 1/4] net: use ndo_set_features to set offload features for bonding/bridge/team Hangbin Liu
2026-03-15 16:19 ` [net-next,v2,1/4] " Simon Horman
2026-03-16 1:02 ` Hangbin Liu
2026-03-13 3:03 ` [PATCH net-next v2 2/4] macsec: move netdev_upper_dev_link() after macsec_changelink_common() Hangbin Liu
2026-03-13 3:03 ` [PATCH net-next v2 3/4] failover: use ndo_set_features for failover offload compute Hangbin Liu
2026-03-13 3:03 ` [PATCH net-next v2 4/4] net: no need to disable LRO specifically Hangbin Liu
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox