Add a new ndo_update_offloads callback to net_device_ops that allows devices to compute and update their offload features during feature updates. This callback enabling master devices to recompute their features based on current slave device configuration. This is particularly useful for bonding, bridging, team, and failover devices that need to aggregate features from their lower devices. The callback is optional and only implemented by devices that need dynamic offload feature computation. Signed-off-by: Hangbin Liu --- include/linux/netdevice.h | 7 +++++++ net/core/dev.c | 3 +++ 2 files changed, 10 insertions(+) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index d4e6e00bb90a..1169091ccb9a 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1281,6 +1281,12 @@ struct netdev_net_notifier { * constraints, and returns the resulting flags. Must not modify * the device state. * + * void (*ndo_update_offloads)(struct net_device *dev); + * Called during feature update to allow device to compute and update + * offload features (like vlan_features, hw_enc_features) based on + * current lower device configuration. Typically used by master + * devices to aggregate features from slave devices. + * * int (*ndo_set_features)(struct net_device *dev, netdev_features_t features); * Called to update device configuration to new features. Passed * feature set might be less than what was returned by ndo_fix_features()). @@ -1558,6 +1564,7 @@ struct net_device_ops { struct sock *sk); netdev_features_t (*ndo_fix_features)(struct net_device *dev, netdev_features_t features); + void (*ndo_update_offloads)(struct net_device *dev); int (*ndo_set_features)(struct net_device *dev, netdev_features_t features); int (*ndo_neigh_construct)(struct net_device *dev, diff --git a/net/core/dev.c b/net/core/dev.c index 096b3ff13f6b..d05837c0713a 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -10982,6 +10982,9 @@ int __netdev_update_features(struct net_device *dev) ASSERT_RTNL(); netdev_ops_assert_locked(dev); + if (dev->netdev_ops->ndo_update_offloads) + dev->netdev_ops->ndo_update_offloads(dev); + features = netdev_get_wanted_features(dev); if (dev->netdev_ops->ndo_fix_features) -- 2.50.1 Convert bonding, bridge, and team drivers to use the new ndo_update_offloads callback instead of manually calling netdev_compute_master_upper_features() during port add/remove operations. This change centralizes the feature computation flow: Before: - netdev_compute_master_upper_features() - 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_update_offloads() - netdev_compute_master_upper_features() - update other 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 --- drivers/net/bonding/bond_main.c | 10 +++++++--- drivers/net/team/team_core.c | 11 +++++++---- net/bridge/br_device.c | 6 ++++++ net/bridge/br_if.c | 4 ---- net/core/dev.c | 8 ++++++-- 5 files changed, 26 insertions(+), 13 deletions(-) diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 55a960da42b5..dc524f595e28 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -1504,6 +1504,11 @@ static netdev_features_t bond_fix_features(struct net_device *dev, return features; } +static void bond_update_offloads(struct net_device *dev) +{ + netdev_compute_master_upper_features(dev, true); +} + static void bond_setup_by_slave(struct net_device *bond_dev, struct net_device *slave_dev) { @@ -2224,7 +2229,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 @@ -2479,7 +2483,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"); @@ -3971,7 +3974,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; @@ -5895,6 +5898,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_update_offloads = bond_update_offloads, .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 c08a5c1bd6e4..fa62965b61de 100644 --- a/drivers/net/team/team_core.c +++ b/drivers/net/team/team_core.c @@ -1249,7 +1249,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); @@ -1333,7 +1332,6 @@ static int team_port_del(struct team *team, struct net_device *port_dev) dev_set_mtu(port_dev, port->orig.mtu); kfree_rcu(port, rcu); netdev_info(dev, "Port device %s removed\n", portname); - netdev_compute_master_upper_features(team->dev, true); return 0; } @@ -1641,7 +1639,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) @@ -1959,6 +1956,11 @@ static netdev_features_t team_fix_features(struct net_device *dev, return features; } +static void team_update_offloads(struct net_device *dev) +{ + netdev_compute_master_upper_features(dev, true); +} + static int team_change_carrier(struct net_device *dev, bool new_carrier) { struct team *team = netdev_priv(dev); @@ -1994,6 +1996,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_update_offloads = team_update_offloads, .ndo_change_carrier = team_change_carrier, .ndo_features_check = passthru_features_check, }; @@ -2931,7 +2934,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 a818fdc22da9..3f2e8f5249fb 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c @@ -289,6 +289,11 @@ static netdev_features_t br_fix_features(struct net_device *dev, return br_features_recompute(br, features); } +static void br_update_offloads(struct net_device *dev) +{ + netdev_compute_master_upper_features(dev, false); +} + #ifdef CONFIG_NET_POLL_CONTROLLER static void br_poll_controller(struct net_device *br_dev) { @@ -456,6 +461,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_update_offloads = br_update_offloads, .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 4c67a32745f6..7806436340ef 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 d05837c0713a..7043ee022980 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -8893,6 +8893,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: @@ -8985,6 +8988,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); } /** @@ -12851,8 +12857,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); -- 2.50.1 Convert net_failover to use the new ndo_update_offloads callback instead of manually calling netdev_compute_master_upper_features() 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 --- drivers/net/net_failover.c | 64 ++++---------------------------------- include/net/net_failover.h | 7 ----- 2 files changed, 6 insertions(+), 65 deletions(-) diff --git a/drivers/net/net_failover.c b/drivers/net/net_failover.c index d0361aaf25ef..527175f554a6 100644 --- a/drivers/net/net_failover.c +++ b/drivers/net/net_failover.c @@ -300,6 +300,11 @@ static int net_failover_vlan_rx_kill_vid(struct net_device *dev, __be16 proto, return 0; } +static void failover_update_offloads(struct net_device *dev) +{ + netdev_compute_master_upper_features(dev, true); +} + static const struct net_device_ops failover_dev_ops = { .ndo_open = net_failover_open, .ndo_stop = net_failover_close, @@ -312,6 +317,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_update_offloads = failover_update_offloads, }; #define FAILOVER_NAME "net_failover" @@ -373,61 +379,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 +501,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 +571,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); 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 */ -- 2.50.1