Skip to content

Commit

Permalink
bonding: support hardware encryption offload to slaves
Browse files Browse the repository at this point in the history
Currently, this support is limited to active-backup mode, as I'm not sure
about the feasilibity of mapping an xfrm_state's offload handle to
multiple hardware devices simultaneously, and we rely on being able to
pass some hints to both the xfrm and NIC driver about whether or not
they're operating on a slave device.

I've tested this atop an Intel x520 device (ixgbe) using libreswan in
transport mode, succesfully achieving ~4.3Gbps throughput with netperf
(more or less identical to throughput on a bare NIC in this system),
as well as successful failover and recovery mid-netperf.

v2: just use CONFIG_XFRM_OFFLOAD for wrapping, isolate more code with it

CC: Jay Vosburgh <[email protected]>
CC: Veaceslav Falico <[email protected]>
CC: Andy Gospodarek <[email protected]>
CC: "David S. Miller" <[email protected]>
CC: Jeff Kirsher <[email protected]>
CC: Jakub Kicinski <[email protected]>
CC: Steffen Klassert <[email protected]>
CC: Herbert Xu <[email protected]>
CC: [email protected]
CC: [email protected]
Signed-off-by: Jarod Wilson <[email protected]>
Signed-off-by: David S. Miller <[email protected]>
  • Loading branch information
jarodwilson authored and davem330 committed Jun 22, 2020
1 parent bf3a058 commit 18cb261
Show file tree
Hide file tree
Showing 2 changed files with 128 additions and 2 deletions.
127 changes: 125 additions & 2 deletions drivers/net/bonding/bond_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@
#include <net/pkt_sched.h>
#include <linux/rculist.h>
#include <net/flow_dissector.h>
#include <net/xfrm.h>
#include <net/bonding.h>
#include <net/bond_3ad.h>
#include <net/bond_alb.h>
Expand Down Expand Up @@ -278,8 +279,6 @@ const char *bond_mode_name(int mode)
return names[mode];
}

/*---------------------------------- VLAN -----------------------------------*/

/**
* bond_dev_queue_xmit - Prepare skb for xmit.
*
Expand All @@ -302,6 +301,8 @@ netdev_tx_t bond_dev_queue_xmit(struct bonding *bond, struct sk_buff *skb,
return dev_queue_xmit(skb);
}

/*---------------------------------- VLAN -----------------------------------*/

/* In the following 2 functions, bond_vlan_rx_add_vid and bond_vlan_rx_kill_vid,
* We don't protect the slave list iteration with a lock because:
* a. This operation is performed in IOCTL context,
Expand Down Expand Up @@ -372,6 +373,84 @@ static int bond_vlan_rx_kill_vid(struct net_device *bond_dev,
return 0;
}

/*---------------------------------- XFRM -----------------------------------*/

#ifdef CONFIG_XFRM_OFFLOAD
/**
* bond_ipsec_add_sa - program device with a security association
* @xs: pointer to transformer state struct
**/
static int bond_ipsec_add_sa(struct xfrm_state *xs)
{
struct net_device *bond_dev = xs->xso.dev;
struct bonding *bond = netdev_priv(bond_dev);
struct slave *slave = rtnl_dereference(bond->curr_active_slave);

xs->xso.slave_dev = slave->dev;
bond->xs = xs;

if (!(slave->dev->xfrmdev_ops
&& slave->dev->xfrmdev_ops->xdo_dev_state_add)) {
slave_warn(bond_dev, slave->dev, "Slave does not support ipsec offload\n");
return -EINVAL;
}

return slave->dev->xfrmdev_ops->xdo_dev_state_add(xs);
}

/**
* bond_ipsec_del_sa - clear out this specific SA
* @xs: pointer to transformer state struct
**/
static void bond_ipsec_del_sa(struct xfrm_state *xs)
{
struct net_device *bond_dev = xs->xso.dev;
struct bonding *bond = netdev_priv(bond_dev);
struct slave *slave = rtnl_dereference(bond->curr_active_slave);

if (!slave)
return;

xs->xso.slave_dev = slave->dev;

if (!(slave->dev->xfrmdev_ops
&& slave->dev->xfrmdev_ops->xdo_dev_state_delete)) {
slave_warn(bond_dev, slave->dev, "%s: no slave xdo_dev_state_delete\n", __func__);
return;
}

slave->dev->xfrmdev_ops->xdo_dev_state_delete(xs);
}

/**
* bond_ipsec_offload_ok - can this packet use the xfrm hw offload
* @skb: current data packet
* @xs: pointer to transformer state struct
**/
static bool bond_ipsec_offload_ok(struct sk_buff *skb, struct xfrm_state *xs)
{
struct net_device *bond_dev = xs->xso.dev;
struct bonding *bond = netdev_priv(bond_dev);
struct slave *curr_active = rtnl_dereference(bond->curr_active_slave);
struct net_device *slave_dev = curr_active->dev;

if (!(slave_dev->xfrmdev_ops
&& slave_dev->xfrmdev_ops->xdo_dev_offload_ok)) {
slave_warn(bond_dev, slave_dev, "%s: no slave xdo_dev_offload_ok\n", __func__);
return false;
}

xs->xso.slave_dev = slave_dev;
return slave_dev->xfrmdev_ops->xdo_dev_offload_ok(skb, xs);
}

static const struct xfrmdev_ops bond_xfrmdev_ops = {
.xdo_dev_state_add = bond_ipsec_add_sa,
.xdo_dev_state_delete = bond_ipsec_del_sa,
.xdo_dev_offload_ok = bond_ipsec_offload_ok,
};
#endif /* CONFIG_XFRM_OFFLOAD */

/*------------------------------- Link status -------------------------------*/

/* Set the carrier state for the master according to the state of its
Expand Down Expand Up @@ -879,6 +958,11 @@ void bond_change_active_slave(struct bonding *bond, struct slave *new_active)
return;

if (new_active) {
#ifdef CONFIG_XFRM_OFFLOAD
if ((BOND_MODE(bond) == BOND_MODE_ACTIVEBACKUP) && bond->xs)
bond_ipsec_del_sa(bond->xs);
#endif /* CONFIG_XFRM_OFFLOAD */

new_active->last_link_up = jiffies;

if (new_active->link == BOND_LINK_BACK) {
Expand Down Expand Up @@ -941,6 +1025,13 @@ void bond_change_active_slave(struct bonding *bond, struct slave *new_active)
bond_should_notify_peers(bond);
}

#ifdef CONFIG_XFRM_OFFLOAD
if (old_active && bond->xs) {
xfrm_dev_state_flush(dev_net(bond->dev), bond->dev, true);
bond_ipsec_add_sa(bond->xs);
}
#endif /* CONFIG_XFRM_OFFLOAD */

call_netdevice_notifiers(NETDEV_BONDING_FAILOVER, bond->dev);
if (should_notify_peers) {
bond->send_peer_notif--;
Expand Down Expand Up @@ -1127,15 +1218,24 @@ static netdev_features_t bond_fix_features(struct net_device *dev,
#define BOND_ENC_FEATURES (NETIF_F_HW_CSUM | NETIF_F_SG | \
NETIF_F_RXCSUM | NETIF_F_ALL_TSO)

#ifdef CONFIG_XFRM_OFFLOAD
#define BOND_XFRM_FEATURES (NETIF_F_HW_ESP | NETIF_F_HW_ESP_TX_CSUM | \
NETIF_F_GSO_ESP)
#endif /* CONFIG_XFRM_OFFLOAD */

#define BOND_MPLS_FEATURES (NETIF_F_HW_CSUM | NETIF_F_SG | \
NETIF_F_ALL_TSO)


static void bond_compute_features(struct bonding *bond)
{
unsigned int dst_release_flag = IFF_XMIT_DST_RELEASE |
IFF_XMIT_DST_RELEASE_PERM;
netdev_features_t vlan_features = BOND_VLAN_FEATURES;
netdev_features_t enc_features = BOND_ENC_FEATURES;
#ifdef CONFIG_XFRM_OFFLOAD
netdev_features_t xfrm_features = BOND_XFRM_FEATURES;
#endif /* CONFIG_XFRM_OFFLOAD */
netdev_features_t mpls_features = BOND_MPLS_FEATURES;
struct net_device *bond_dev = bond->dev;
struct list_head *iter;
Expand All @@ -1157,6 +1257,12 @@ static void bond_compute_features(struct bonding *bond)
slave->dev->hw_enc_features,
BOND_ENC_FEATURES);

#ifdef CONFIG_XFRM_OFFLOAD
xfrm_features = netdev_increment_features(xfrm_features,
slave->dev->hw_enc_features,
BOND_XFRM_FEATURES);
#endif /* CONFIG_XFRM_OFFLOAD */

mpls_features = netdev_increment_features(mpls_features,
slave->dev->mpls_features,
BOND_MPLS_FEATURES);
Expand All @@ -1176,6 +1282,9 @@ static void bond_compute_features(struct bonding *bond)
NETIF_F_HW_VLAN_CTAG_TX |
NETIF_F_HW_VLAN_STAG_TX |
NETIF_F_GSO_UDP_L4;
#ifdef CONFIG_XFRM_OFFLOAD
bond_dev->hw_enc_features |= xfrm_features;
#endif /* CONFIG_XFRM_OFFLOAD */
bond_dev->mpls_features = mpls_features;
bond_dev->gso_max_segs = gso_max_segs;
netif_set_gso_max_size(bond_dev, gso_max_size);
Expand Down Expand Up @@ -1464,6 +1573,9 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev,
slave_dbg(bond_dev, slave_dev, "is !NETIF_F_VLAN_CHALLENGED\n");
}

if (slave_dev->features & NETIF_F_HW_ESP)
slave_dbg(bond_dev, slave_dev, "is esp-hw-offload capable\n");

/* Old ifenslave binaries are no longer supported. These can
* be identified with moderate accuracy by the state of the slave:
* the current ifenslave will set the interface down prior to
Expand Down Expand Up @@ -4540,6 +4652,13 @@ void bond_setup(struct net_device *bond_dev)
bond_dev->priv_flags |= IFF_BONDING | IFF_UNICAST_FLT | IFF_NO_QUEUE;
bond_dev->priv_flags &= ~(IFF_XMIT_DST_RELEASE | IFF_TX_SKB_SHARING);

#ifdef CONFIG_XFRM_OFFLOAD
/* set up xfrm device ops (only supported in active-backup right now) */
if ((BOND_MODE(bond) == BOND_MODE_ACTIVEBACKUP))
bond_dev->xfrmdev_ops = &bond_xfrmdev_ops;
bond->xs = NULL;
#endif /* CONFIG_XFRM_OFFLOAD */

/* don't acquire bond device's netif_tx_lock when transmitting */
bond_dev->features |= NETIF_F_LLTX;

Expand All @@ -4558,6 +4677,10 @@ void bond_setup(struct net_device *bond_dev)
NETIF_F_HW_VLAN_CTAG_FILTER;

bond_dev->hw_features |= NETIF_F_GSO_ENCAP_ALL | NETIF_F_GSO_UDP_L4;
#ifdef CONFIG_XFRM_OFFLOAD
if ((BOND_MODE(bond) == BOND_MODE_ACTIVEBACKUP))
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;
}
Expand Down
3 changes: 3 additions & 0 deletions include/net/bonding.h
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,9 @@ struct bonding {
struct dentry *debug_dir;
#endif /* CONFIG_DEBUG_FS */
struct rtnl_link_stats64 bond_stats;
#ifdef CONFIG_XFRM_OFFLOAD
struct xfrm_state *xs;
#endif /* CONFIG_XFRM_OFFLOAD */
};

#define bond_slave_get_rcu(dev) \
Expand Down

0 comments on commit 18cb261

Please sign in to comment.