Re: [net-next,v2,1/4] net: use ndo_set_features to set offload features for bonding/bridge/team

Simon Horman <[email protected]>
Newsgroups gmane.linux.network.bridge
Message-ID <20260315161943.1419237-1-horms__23258.0513855041$1773591627$gmane$org@kernel.org>
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);
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.