From: Erwan Dufour Implement XFRM policy offload functions for bond device in active-backup mode. - xdo_dev_policy_add = bond_ipsec_add_sp - xdo_dev_policy_delete = bond_ipsec_del_sp _ xdo_deb_policy_free = bond_ipsec_free_sp Modification of the function signature for copying on SA models. Also add netdevice pointer to avoid to use real_dev which is obsolete and deleted for policy. Store the bond's xfrm policies in the struct bond_ipsec. Also rename these functions: - bond_ipsec_del_sa_all -> bond_ipsec_del_sa_sp_all - bond_ipsec_add_sa_all -> bond_ipsec_add_sa_sp_all Now bond_ipsec_{del,add}_sa_sp_all remove/add also the bond's policies stores in same struct as SA. Tested on Mellanox ConnectX-6 Dx Crypto Enable Cards. --- drivers/net/bonding/bond_main.c | 279 +++++++++++++++--- .../mellanox/mlx5/core/en_accel/ipsec.c | 10 +- include/linux/netdevice.h | 6 +- include/net/bonding.h | 1 + include/net/xfrm.h | 4 +- net/xfrm/xfrm_device.c | 2 +- 6 files changed, 246 insertions(+), 56 deletions(-) diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index c4d53e8e7c15..85017635f9b5 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -512,7 +512,7 @@ static int bond_ipsec_add_sa(struct net_device *bond_dev, return err; } -static void bond_ipsec_add_sa_all(struct bonding *bond) +static void bond_ipsec_add_sa_sp_all(struct bonding *bond) { struct net_device *bond_dev = bond->dev; struct net_device *real_dev; @@ -536,29 +536,44 @@ static void bond_ipsec_add_sa_all(struct bonding *bond) } list_for_each_entry(ipsec, &bond->ipsec_list, list) { - /* If new state is added before ipsec_lock acquired */ - if (ipsec->xs->xso.real_dev == real_dev) - continue; + if (ipsec->xs) { + /* If new state is added before ipsec_lock acquired */ + if (ipsec->xs->xso.real_dev == real_dev) + continue; - if (real_dev->xfrmdev_ops->xdo_dev_state_add(real_dev, - ipsec->xs, NULL)) { - slave_warn(bond_dev, real_dev, "%s: failed to add SA\n", __func__); - continue; - } + if (real_dev->xfrmdev_ops->xdo_dev_state_add(real_dev, + ipsec->xs, NULL)) { + slave_warn(bond_dev, real_dev, "%s: failed to add SA\n", __func__); + continue; + } - spin_lock_bh(&ipsec->xs->lock); - /* xs might have been killed by the user during the migration - * to the new dev, but bond_ipsec_del_sa() should have done - * nothing, as xso.real_dev is NULL. - * Delete it from the device we just added it to. The pending - * bond_ipsec_free_sa() call will do the rest of the cleanup. - */ - if (ipsec->xs->km.state == XFRM_STATE_DEAD && - real_dev->xfrmdev_ops->xdo_dev_state_delete) - real_dev->xfrmdev_ops->xdo_dev_state_delete(real_dev, - ipsec->xs); - ipsec->xs->xso.real_dev = real_dev; - spin_unlock_bh(&ipsec->xs->lock); + spin_lock_bh(&ipsec->xs->lock); + /* xs might have been killed by the user during the migration + * to the new dev, but bond_ipsec_del_sa() should have done + * nothing, as xso.real_dev is NULL. + * Delete it from the device we just added it to. The pending + * bond_ipsec_free_sa() call will do the rest of the cleanup. + */ + if (ipsec->xs->km.state == XFRM_STATE_DEAD && + real_dev->xfrmdev_ops->xdo_dev_state_delete) + real_dev->xfrmdev_ops->xdo_dev_state_delete(real_dev, + ipsec->xs); + ipsec->xs->xso.real_dev = real_dev; + spin_unlock_bh(&ipsec->xs->lock); + } else { + /* XFRM Policy Part */ + if (ipsec->xp->xdo.real_dev == real_dev) + continue; + + if (real_dev->xfrmdev_ops->xdo_dev_policy_add(real_dev, + ipsec->xp, NULL)) { + slave_warn(bond_dev, real_dev, "%s: failed to add SP\n", __func__); + continue; + } + write_lock_bh(&ipsec->xp->lock); + ipsec->xp->xdo.real_dev = real_dev; + write_unlock_bh(&ipsec->xp->lock); + } } out: mutex_unlock(&bond->ipsec_lock); @@ -589,7 +604,7 @@ static void bond_ipsec_del_sa(struct net_device *bond_dev, real_dev->xfrmdev_ops->xdo_dev_state_delete(real_dev, xs); } -static void bond_ipsec_del_sa_all(struct bonding *bond) +static void bond_ipsec_del_sa_sp_all(struct bonding *bond) { struct net_device *bond_dev = bond->dev; struct net_device *real_dev; @@ -603,29 +618,55 @@ static void bond_ipsec_del_sa_all(struct bonding *bond) mutex_lock(&bond->ipsec_lock); list_for_each_entry(ipsec, &bond->ipsec_list, list) { - if (!ipsec->xs->xso.real_dev) - continue; + if (ipsec->xs) { + if (!ipsec->xs->xso.real_dev) + continue; - if (!real_dev->xfrmdev_ops || - !real_dev->xfrmdev_ops->xdo_dev_state_delete || - netif_is_bond_master(real_dev)) { - slave_warn(bond_dev, real_dev, - "%s: no slave xdo_dev_state_delete\n", - __func__); - continue; - } + if (!real_dev->xfrmdev_ops || + !real_dev->xfrmdev_ops->xdo_dev_state_delete || + netif_is_bond_master(real_dev)) { + slave_warn(bond_dev, real_dev, + "%s: no slave xdo_dev_state_delete\n", + __func__); + continue; + } - spin_lock_bh(&ipsec->xs->lock); - ipsec->xs->xso.real_dev = NULL; - /* Don't double delete states killed by the user. */ - if (ipsec->xs->km.state != XFRM_STATE_DEAD) - real_dev->xfrmdev_ops->xdo_dev_state_delete(real_dev, - ipsec->xs); - spin_unlock_bh(&ipsec->xs->lock); + spin_lock_bh(&ipsec->xs->lock); + ipsec->xs->xso.real_dev = NULL; + /* Don't double delete states killed by the user. */ + if (ipsec->xs->km.state != XFRM_STATE_DEAD) + real_dev->xfrmdev_ops->xdo_dev_state_delete(real_dev, + ipsec->xs); + spin_unlock_bh(&ipsec->xs->lock); + + if (real_dev->xfrmdev_ops->xdo_dev_state_free) + real_dev->xfrmdev_ops->xdo_dev_state_free(real_dev, + ipsec->xs); + } else { + /* XFRM Policy part */ + if (!ipsec->xp->xdo.real_dev) + continue; - if (real_dev->xfrmdev_ops->xdo_dev_state_free) - real_dev->xfrmdev_ops->xdo_dev_state_free(real_dev, - ipsec->xs); + if (!real_dev->xfrmdev_ops || + !real_dev->xfrmdev_ops->xdo_dev_policy_delete || + netif_is_bond_master(real_dev)) { + slave_warn(bond_dev, real_dev, + "%s: no slave xdo_dev_policy_delete\n", + __func__); + continue; + } + /* use write rwlock */ + write_lock_bh(&ipsec->xp->lock); + ipsec->xp->xdo.real_dev = NULL; + write_unlock_bh(&ipsec->xp->lock); + + real_dev->xfrmdev_ops->xdo_dev_policy_delete(real_dev, + ipsec->xp); + + if (real_dev->xfrmdev_ops->xdo_dev_state_free) + real_dev->xfrmdev_ops->xdo_dev_policy_free(real_dev, + ipsec->xp); + } } mutex_unlock(&bond->ipsec_lock); } @@ -731,6 +772,151 @@ static void bond_xfrm_update_stats(struct xfrm_state *xs) rcu_read_unlock(); } +/** + * bond_ipsec_add_sp - program device with a security policy + * @bond_dev: pointer to net device + * @xs: pointer to transformer policy struct + * @extack: extack point to fill failure reason + **/ +static int bond_ipsec_add_sp(struct net_device *bond_dev, + struct xfrm_policy *xp, + struct netlink_ext_ack *extack) +{ + struct net_device *real_dev; + netdevice_tracker tracker; + struct bond_ipsec *ipsec; + struct bonding *bond; + struct slave *slave; + int err; + + if (!bond_dev) + return -EINVAL; + + rcu_read_lock(); + bond = netdev_priv(bond_dev); + slave = rcu_dereference(bond->curr_active_slave); + real_dev = slave ? slave->dev : NULL; + netdev_hold(real_dev, &tracker, GFP_ATOMIC); + rcu_read_unlock(); + if (!real_dev) { + err = -ENODEV; + goto out; + } + + if (!real_dev->xfrmdev_ops || + !real_dev->xfrmdev_ops->xdo_dev_policy_add || + netif_is_bond_master(real_dev)) { + NL_SET_ERR_MSG_MOD(extack, "Slave does not support security policy offload"); + err = -EINVAL; + goto out; + } + + ipsec = kmalloc(sizeof(*ipsec), GFP_KERNEL); + if (!ipsec) { + err = -ENOMEM; + goto out; + } + + err = real_dev->xfrmdev_ops->xdo_dev_policy_add(real_dev, xp, extack); + if (!err) { + xp->xdo.real_dev = real_dev; + ipsec->xp = xp; + INIT_LIST_HEAD(&ipsec->list); + mutex_lock(&bond->ipsec_lock); + list_add(&ipsec->list, &bond->ipsec_list); + mutex_unlock(&bond->ipsec_lock); + } else { + kfree(ipsec); + } +out: + netdev_put(real_dev, &tracker); + return err; +} + +/** + * bond_ipsec_del_sp - clear out this specific SP + * @bond_dev: pointer to net device + * @xs: pointer to transformer policy struct + **/ +static void bond_ipsec_del_sp(struct net_device *bond_dev, struct xfrm_policy *xp) +{ + struct net_device *real_dev; + netdevice_tracker tracker; + struct bond_ipsec *ipsec; + struct bonding *bond; + struct slave *slave; + + if (!bond_dev) + return; + + rcu_read_lock(); + bond = netdev_priv(bond_dev); + slave = rcu_dereference(bond->curr_active_slave); + real_dev = slave ? slave->dev : NULL; + netdev_hold(real_dev, &tracker, GFP_ATOMIC); + rcu_read_unlock(); + + if (!slave) + goto out; + + if (!xp->xdo.real_dev) + goto out; + + WARN_ON(xp->xdo.real_dev != real_dev); + + if (!real_dev->xfrmdev_ops || + !real_dev->xfrmdev_ops->xdo_dev_policy_delete || + netif_is_bond_master(real_dev)) { + slave_warn(bond_dev, real_dev, "%s: no slave xdo_dev_policy_delete\n", __func__); + goto out; + } + + real_dev->xfrmdev_ops->xdo_dev_policy_delete(real_dev, xp); +out: + netdev_put(real_dev, &tracker); + mutex_lock(&bond->ipsec_lock); + list_for_each_entry(ipsec, &bond->ipsec_list, list) { + if (ipsec->xp == xp) { + list_del(&ipsec->list); + kfree(ipsec); + break; + } + } + mutex_unlock(&bond->ipsec_lock); +} + +static void bond_ipsec_free_sp(struct net_device *bond_dev, struct xfrm_policy *xp) +{ + struct net_device *real_dev; + netdevice_tracker tracker; + struct bonding *bond; + struct slave *slave; + + if (!bond_dev) + return; + + rcu_read_lock(); + bond = netdev_priv(bond_dev); + slave = rcu_dereference(bond->curr_active_slave); + real_dev = slave ? slave->dev : NULL; + netdev_hold(real_dev, &tracker, GFP_ATOMIC); + rcu_read_unlock(); + + if (!slave) + goto out; + + if (!xp->xdo.real_dev) + goto out; + + WARN_ON(xp->xdo.real_dev != real_dev); + + if (real_dev && real_dev->xfrmdev_ops && + real_dev->xfrmdev_ops->xdo_dev_policy_free) + real_dev->xfrmdev_ops->xdo_dev_policy_free(real_dev, xp); +out: + netdev_put(real_dev, &tracker); +} + static const struct xfrmdev_ops bond_xfrmdev_ops = { .xdo_dev_state_add = bond_ipsec_add_sa, .xdo_dev_state_delete = bond_ipsec_del_sa, @@ -738,6 +924,9 @@ static const struct xfrmdev_ops bond_xfrmdev_ops = { .xdo_dev_offload_ok = bond_ipsec_offload_ok, .xdo_dev_state_advance_esn = bond_advance_esn_state, .xdo_dev_state_update_stats = bond_xfrm_update_stats, + .xdo_dev_policy_add = bond_ipsec_add_sp, + .xdo_dev_policy_delete = bond_ipsec_del_sp, + .xdo_dev_policy_free = bond_ipsec_free_sp, }; #endif /* CONFIG_XFRM_OFFLOAD */ @@ -1277,7 +1466,7 @@ void bond_change_active_slave(struct bonding *bond, struct slave *new_active) return; #ifdef CONFIG_XFRM_OFFLOAD - bond_ipsec_del_sa_all(bond); + bond_ipsec_del_sa_sp_all(bond); #endif /* CONFIG_XFRM_OFFLOAD */ if (new_active) { @@ -1352,7 +1541,7 @@ void bond_change_active_slave(struct bonding *bond, struct slave *new_active) } #ifdef CONFIG_XFRM_OFFLOAD - bond_ipsec_add_sa_all(bond); + bond_ipsec_add_sa_sp_all(bond); #endif /* CONFIG_XFRM_OFFLOAD */ /* resend IGMP joins since active slave has changed or diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c index 77f61cd28a79..f5e3fc054f41 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c @@ -1161,15 +1161,15 @@ mlx5e_ipsec_build_accel_pol_attrs(struct mlx5e_ipsec_pol_entry *pol_entry, attrs->prio = x->priority; } -static int mlx5e_xfrm_add_policy(struct xfrm_policy *x, +static int mlx5e_xfrm_add_policy(struct net_device *dev, + struct xfrm_policy *x, struct netlink_ext_ack *extack) { - struct net_device *netdev = x->xdo.dev; struct mlx5e_ipsec_pol_entry *pol_entry; struct mlx5e_priv *priv; int err; - priv = netdev_priv(netdev); + priv = netdev_priv(dev); if (!priv->ipsec) { NL_SET_ERR_MSG_MOD(extack, "Device doesn't support IPsec packet offload"); return -EOPNOTSUPP; @@ -1207,7 +1207,7 @@ static int mlx5e_xfrm_add_policy(struct xfrm_policy *x, return err; } -static void mlx5e_xfrm_del_policy(struct xfrm_policy *x) +static void mlx5e_xfrm_del_policy(struct net_device *dev, struct xfrm_policy *x) { struct mlx5e_ipsec_pol_entry *pol_entry = to_ipsec_pol_entry(x); @@ -1215,7 +1215,7 @@ static void mlx5e_xfrm_del_policy(struct xfrm_policy *x) mlx5_eswitch_unblock_ipsec(pol_entry->ipsec->mdev); } -static void mlx5e_xfrm_free_policy(struct xfrm_policy *x) +static void mlx5e_xfrm_free_policy(struct net_device *dev, struct xfrm_policy *x) { struct mlx5e_ipsec_pol_entry *pol_entry = to_ipsec_pol_entry(x); diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index adb14db25798..7c3d74d28ef4 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1024,9 +1024,9 @@ struct xfrmdev_ops { struct xfrm_state *x); void (*xdo_dev_state_advance_esn) (struct xfrm_state *x); void (*xdo_dev_state_update_stats) (struct xfrm_state *x); - int (*xdo_dev_policy_add) (struct xfrm_policy *x, struct netlink_ext_ack *extack); - void (*xdo_dev_policy_delete) (struct xfrm_policy *x); - void (*xdo_dev_policy_free) (struct xfrm_policy *x); + int (*xdo_dev_policy_add) (struct net_device *dev, struct xfrm_policy *x, struct netlink_ext_ack *extack); + void (*xdo_dev_policy_delete) (struct net_device *dev, struct xfrm_policy *x); + void (*xdo_dev_policy_free) (struct net_device *dev, struct xfrm_policy *x); }; #endif diff --git a/include/net/bonding.h b/include/net/bonding.h index 95f67b308c19..6ac079673f87 100644 --- a/include/net/bonding.h +++ b/include/net/bonding.h @@ -207,6 +207,7 @@ struct bond_up_slave { struct bond_ipsec { struct list_head list; struct xfrm_state *xs; + struct xfrm_policy *xp; }; /* diff --git a/include/net/xfrm.h b/include/net/xfrm.h index a21e276dbe44..ffae7cc1f989 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -2116,7 +2116,7 @@ static inline void xfrm_dev_policy_delete(struct xfrm_policy *x) struct net_device *dev = xdo->dev; if (dev && dev->xfrmdev_ops && dev->xfrmdev_ops->xdo_dev_policy_delete) - dev->xfrmdev_ops->xdo_dev_policy_delete(x); + dev->xfrmdev_ops->xdo_dev_policy_delete(dev, x); } static inline void xfrm_dev_policy_free(struct xfrm_policy *x) @@ -2126,7 +2126,7 @@ static inline void xfrm_dev_policy_free(struct xfrm_policy *x) if (dev && dev->xfrmdev_ops) { if (dev->xfrmdev_ops->xdo_dev_policy_free) - dev->xfrmdev_ops->xdo_dev_policy_free(x); + dev->xfrmdev_ops->xdo_dev_policy_free(dev, x); xdo->dev = NULL; netdev_put(dev, &xdo->dev_tracker); } diff --git a/net/xfrm/xfrm_device.c b/net/xfrm/xfrm_device.c index 81fd486b5e56..643679b8d13c 100644 --- a/net/xfrm/xfrm_device.c +++ b/net/xfrm/xfrm_device.c @@ -394,7 +394,7 @@ int xfrm_dev_policy_add(struct net *net, struct xfrm_policy *xp, return -EINVAL; } - err = dev->xfrmdev_ops->xdo_dev_policy_add(xp, extack); + err = dev->xfrmdev_ops->xdo_dev_policy_add(dev, xp, extack); if (err) { xdo->dev = NULL; xdo->type = XFRM_DEV_OFFLOAD_UNSPECIFIED; -- 2.43.0