On Sun, 22 Nov 2020 22:17:16 -0500 Jarod Wilson <ja...@redhat.com> wrote:
> Have run into a case where bond_option_mode_set() gets called before > hw_features has been filled in, and very bad things happen when > netdev_change_features() then gets called, because the empty hw_features > wipes out almost all features. Further reading of netdev feature flag > documentation suggests drivers aren't supposed to touch wanted_features, > so this changes bond_option_mode_set() to use netdev_increment_features() > and &= ~BOND_XFRM_FEATURES on mode changes and then only calling > netdev_features_change() if there was actually a change of features. This > specifically fixes bonding on top of mlxsw interfaces, and has been > regression-tested with ixgbe interfaces. This change also simplifies the > xfrm-specific code in bond_setup() a little bit as well. Hi Jarod, the reason is not correct... The problem is not with empty ->hw_features but with empty ->wanted_features. During bond device creation bond_newlink() is called. It calls bond_changelink() first and afterwards register_netdevice(). The problem is that ->wanted_features are initialized in register_netdevice() so during bond_changlink() call ->wanted_features is 0. So... bond_newlink() -> bond_changelink() -> __bond_opt_set() -> bond_option_mode_set() -> netdev_change_features() -> __netdev_update_features() features = netdev_get_wanted_features() { dev->features & ~dev->hw_features) | dev->wanted_features } dev->wanted_features is here zero so the rest of the expression clears a bunch of bits from dev->features... In case of mlxsw it is important that NETIF_F_HW_VLAN_CTAG_FILTER bit is cleared in bonding device because in this case vlan_add_rx_filter_info() does not call bond_vlan_rx_add_vid() so mlxsw_sp_port_add_vid() is not called as well. Later this causes a WARN in mlxsw_sp_inetaddr_port_vlan_event() because instance of mlxsw_sp_port_vlan does not exist as mlxsw_sp_port_add_vid() was not called. Btw. it should be enough to call existing snippet in bond_option_mode_set() only when device is already registered? E.g.: diff --git a/drivers/net/bonding/bond_options.c b/drivers/net/bonding/bond_options.c index 9abfaae1c6f7..ca4913fee5a9 100644 --- a/drivers/net/bonding/bond_options.c +++ b/drivers/net/bonding/bond_options.c @@ -768,11 +768,13 @@ static int bond_option_mode_set(struct bonding *bond, bond->params.tlb_dynamic_lb = 1; #ifdef CONFIG_XFRM_OFFLOAD - if (newval->value == BOND_MODE_ACTIVEBACKUP) - bond->dev->wanted_features |= BOND_XFRM_FEATURES; - else - bond->dev->wanted_features &= ~BOND_XFRM_FEATURES; - netdev_change_features(bond->dev); + if (dev->reg_state == NETREG_REGISTERED) { + if (newval->value == BOND_MODE_ACTIVEBACKUP) + bond->dev->wanted_features |= BOND_XFRM_FEATURES; + else + bond->dev->wanted_features &= ~BOND_XFRM_FEATURES; + netdev_change_features(bond->dev); + } #endif /* CONFIG_XFRM_OFFLOAD */ Thanks, Ivan