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.
Fixes: a3b658cfb664 ("bonding: allow xfrm offload setup post-module-load") Reported-by: Ivan Vecera <ivec...@redhat.com> Cc: Jay Vosburgh <j.vosbu...@gmail.com> Cc: Veaceslav Falico <vfal...@gmail.com> Cc: Andy Gospodarek <a...@greyhouse.net> Cc: "David S. Miller" <da...@davemloft.net> Cc: Jakub Kicinski <k...@kernel.org> Cc: Thomas Davis <tada...@lbl.gov> Cc: netdev@vger.kernel.org Signed-off-by: Jarod Wilson <ja...@redhat.com> --- drivers/net/bonding/bond_main.c | 10 ++++------ drivers/net/bonding/bond_options.c | 14 +++++++++++--- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 71c9677d135f..b8e0cb4f9480 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -4721,15 +4721,13 @@ void bond_setup(struct net_device *bond_dev) NETIF_F_HW_VLAN_CTAG_FILTER; bond_dev->hw_features |= NETIF_F_GSO_ENCAP_ALL; -#ifdef CONFIG_XFRM_OFFLOAD - bond_dev->hw_features |= BOND_XFRM_FEATURES; -#endif /* CONFIG_XFRM_OFFLOAD */ bond_dev->features |= bond_dev->hw_features; bond_dev->features |= NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_STAG_TX; #ifdef CONFIG_XFRM_OFFLOAD - /* Disable XFRM features if this isn't an active-backup config */ - if (BOND_MODE(bond) != BOND_MODE_ACTIVEBACKUP) - bond_dev->features &= ~BOND_XFRM_FEATURES; + bond_dev->hw_features |= BOND_XFRM_FEATURES; + /* Only enable XFRM features if this is an active-backup config */ + if (BOND_MODE(bond) == BOND_MODE_ACTIVEBACKUP) + bond_dev->features |= BOND_XFRM_FEATURES; #endif /* CONFIG_XFRM_OFFLOAD */ } diff --git a/drivers/net/bonding/bond_options.c b/drivers/net/bonding/bond_options.c index 9abfaae1c6f7..bce34648d97d 100644 --- a/drivers/net/bonding/bond_options.c +++ b/drivers/net/bonding/bond_options.c @@ -748,6 +748,9 @@ const struct bond_option *bond_opt_get(unsigned int option) static int bond_option_mode_set(struct bonding *bond, const struct bond_opt_value *newval) { + netdev_features_t features = bond->dev->features; + netdev_features_t mask = features & BOND_XFRM_FEATURES; + if (!bond_mode_uses_arp(newval->value)) { if (bond->params.arp_interval) { netdev_dbg(bond->dev, "%s mode is incompatible with arp monitoring, start mii monitoring\n", @@ -769,10 +772,15 @@ static int bond_option_mode_set(struct bonding *bond, #ifdef CONFIG_XFRM_OFFLOAD if (newval->value == BOND_MODE_ACTIVEBACKUP) - bond->dev->wanted_features |= BOND_XFRM_FEATURES; + features = netdev_increment_features(features, + BOND_XFRM_FEATURES, mask); else - bond->dev->wanted_features &= ~BOND_XFRM_FEATURES; - netdev_change_features(bond->dev); + features &= ~BOND_XFRM_FEATURES; + + if (bond->dev->features != features) { + bond->dev->features = features; + netdev_features_change(bond->dev); + } #endif /* CONFIG_XFRM_OFFLOAD */ /* don't cache arp_validate between modes */ -- 2.28.0