Netdev List
 help / color / mirror / Atom feed
From: Jihong Min <hurryman2212@gmail.com>
To: netdev@vger.kernel.org
Cc: Jay Vosburgh <jv@jvosburgh.net>,
	Andrew Lunn <andrew+netdev@lunn.ch>,
	"David S. Miller" <davem@davemloft.net>,
	Eric Dumazet <edumazet@google.com>,
	Jakub Kicinski <kuba@kernel.org>, Paolo Abeni <pabeni@redhat.com>,
	Simon Horman <horms@kernel.org>,
	Steffen Klassert <steffen.klassert@secunet.com>,
	Herbert Xu <herbert@gondor.apana.org.au>,
	linux-kernel@vger.kernel.org, Jihong Min <hurryman2212@gmail.com>
Subject: [PATCH RFC net-next 3/4] bonding: expose user-controlled IPsec features for LAG
Date: Wed, 20 May 2026 17:10:03 +0900	[thread overview]
Message-ID: <20260520081004.2232091-4-hurryman2212@gmail.com> (raw)
In-Reply-To: <20260520081004.2232091-1-hurryman2212@gmail.com>

Expose LAG IPsec offload as user-controlled bonding features instead of
enabling it by default. Keep the existing active-backup default behavior,
but make newly eligible LAG bonds start with ESP/XFRM features explicitly
disabled so users opt in with ethtool.

Let 802.3ad and balance-xor with layer3+4 advertise the intersection of
XFRM features across running eligible slaves, with supported features
shown as mutable off features rather than fixed-off capabilities.

Propagate mutable XFRM feature requests to running lower devices, verify
that requested features are actually enabled, and roll lower devices back
if propagation fails. Disable dependent ESP checksum and segmentation
features when HW ESP is not available.

Assisted-by: Codex:gpt-5.5
Signed-off-by: Jihong Min <hurryman2212@gmail.com>
---
 drivers/net/bonding/bond_main.c    | 232 +++++++++++++++++++++++++++++
 drivers/net/bonding/bond_options.c |   2 +-
 2 files changed, 233 insertions(+), 1 deletion(-)

diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index 66435de852e9..d81dae5a1902 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -2048,6 +2048,13 @@ static netdev_features_t bond_fix_features(struct net_device *dev,
 	struct list_head *iter;
 	netdev_features_t mask;
 	struct slave *slave;
+#ifdef CONFIG_XFRM_OFFLOAD
+	netdev_features_t lag_xfrm_features = BOND_XFRM_FEATURES;
+	bool ab_xfrm = BOND_MODE(bond) == BOND_MODE_ACTIVEBACKUP;
+	bool lag_xfrm_ok = true;
+	bool lag_xfrm = bond_mode_can_use_lag_xfrm(bond);
+	int lag_xfrm_slaves = 0;
+#endif /* CONFIG_XFRM_OFFLOAD */
 
 	mask = features;
 	features = netdev_base_features(features);
@@ -2056,12 +2063,234 @@ static netdev_features_t bond_fix_features(struct net_device *dev,
 		features = netdev_increment_features(features,
 						     slave->dev->features,
 						     mask);
+#ifdef CONFIG_XFRM_OFFLOAD
+		if (lag_xfrm && (mask & BOND_XFRM_FEATURES) &&
+		    netif_running(slave->dev)) {
+			netdev_features_t slave_xfrm_features;
+			netdev_features_t slave_xfrm_enableable;
+			netdev_features_t missing;
+
+			slave_xfrm_features = slave->dev->features &
+					      BOND_XFRM_FEATURES;
+			slave_xfrm_enableable = slave->dev->hw_features &
+						mask & BOND_XFRM_FEATURES;
+			slave_xfrm_features |= slave_xfrm_enableable;
+			missing = (BOND_XFRM_FEATURES & mask) &
+				  ~slave_xfrm_features;
+			if (missing)
+				slave_dbg(dev, slave->dev,
+					  "missing LAG XFRM feature(s) %pNF\n",
+					  &missing);
+			lag_xfrm_features &= slave_xfrm_features;
+
+			if (!(slave_xfrm_features & NETIF_F_HW_ESP) ||
+			    !bond_ipsec_lag_slave_has_ops(slave->dev)) {
+				slave_dbg(dev, slave->dev,
+					  "missing LAG XFRM offload ops\n");
+				lag_xfrm_ok = false;
+			}
+			lag_xfrm_slaves++;
+		}
+#endif /* CONFIG_XFRM_OFFLOAD */
 	}
 	features = netdev_add_tso_features(features, mask);
 
+#ifdef CONFIG_XFRM_OFFLOAD
+	if (!ab_xfrm && !lag_xfrm)
+		features &= ~BOND_XFRM_FEATURES;
+	else if (lag_xfrm && (!lag_xfrm_ok || !lag_xfrm_slaves))
+		features &= ~BOND_XFRM_FEATURES;
+	else if (lag_xfrm)
+		features = (features & ~BOND_XFRM_FEATURES) |
+			   (lag_xfrm_features & mask);
+	if (!(features & NETIF_F_HW_ESP))
+		features &= ~(NETIF_F_HW_ESP_TX_CSUM | NETIF_F_GSO_ESP);
+#endif /* CONFIG_XFRM_OFFLOAD */
+
 	return features;
 }
 
+#ifdef CONFIG_XFRM_OFFLOAD
+static int bond_set_slave_xfrm_features(struct bonding *bond,
+					struct slave *slave,
+					netdev_features_t features)
+{
+	struct net_device *real_dev = slave->dev;
+	netdev_features_t xfrm_features;
+	netdev_features_t mutable;
+	bool notifier_ctx;
+	int err = 0;
+
+	mutable = real_dev->hw_features & BOND_XFRM_FEATURES;
+	if (!mutable)
+		return 0;
+
+	xfrm_features = features & BOND_XFRM_FEATURES;
+
+	notifier_ctx = bond->notifier_ctx;
+	bond->notifier_ctx = true;
+	netdev_lock_ops(real_dev);
+	real_dev->wanted_features &= ~mutable;
+	real_dev->wanted_features |= xfrm_features & mutable;
+	err = __netdev_update_features(real_dev);
+	if (err)
+		netdev_features_change(real_dev);
+	netdev_unlock_ops(real_dev);
+	bond->notifier_ctx = notifier_ctx;
+
+	return err < 0 ? err : 0;
+}
+
+static void bond_restore_slave_xfrm_features(struct bonding *bond,
+					     netdev_features_t features)
+{
+	struct list_head *iter;
+	struct slave *slave;
+
+	bond_for_each_slave(bond, slave, iter) {
+		if (!netif_running(slave->dev))
+			continue;
+
+		bond_set_slave_xfrm_features(bond, slave, features);
+	}
+}
+
+static void bond_sync_slave_xfrm_features(struct bonding *bond,
+					  struct slave *slave)
+{
+	netdev_features_t requested = bond->dev->wanted_features;
+	netdev_features_t old_wanted = slave->dev->wanted_features;
+	netdev_features_t available;
+	netdev_features_t missing;
+	int err;
+
+	if (!bond_mode_can_use_lag_xfrm(bond))
+		return;
+
+	if (!netif_running(slave->dev))
+		return;
+
+	requested &= BOND_XFRM_FEATURES;
+	if (!requested)
+		return;
+
+	available = slave->dev->features | slave->dev->hw_features;
+	missing = requested & ~available;
+	if ((requested & NETIF_F_HW_ESP) &&
+	    !bond_ipsec_lag_slave_has_ops(slave->dev))
+		missing |= NETIF_F_HW_ESP;
+	if (missing)
+		goto disable_missing;
+
+	err = bond_set_slave_xfrm_features(bond, slave, requested);
+	missing = requested & ~slave->dev->features;
+	if (err && !missing)
+		missing = requested;
+	if (!missing)
+		return;
+
+	bond_set_slave_xfrm_features(bond, slave, old_wanted);
+
+disable_missing:
+	if (missing & NETIF_F_HW_ESP)
+		missing |= BOND_XFRM_FEATURES;
+	slave_warn(bond->dev, slave->dev,
+		   "disabling XFRM feature(s) %pNF after slave enable failed\n",
+		   &missing);
+	bond->dev->wanted_features &= ~missing;
+}
+
+static int bond_set_features(struct net_device *dev, netdev_features_t features)
+{
+	struct bonding *bond = netdev_priv(dev);
+	netdev_features_t changed;
+	netdev_features_t enabled;
+	struct list_head *iter;
+	struct slave *slave;
+	int err = 0;
+
+	if (!bond_mode_can_use_lag_xfrm(bond))
+		return 0;
+
+	changed = (dev->features ^ features) & BOND_XFRM_FEATURES;
+	if (!changed)
+		return 0;
+
+	enabled = features & BOND_XFRM_FEATURES;
+	if (enabled) {
+		int targets = 0;
+
+		bond_for_each_slave(bond, slave, iter) {
+			netdev_features_t available;
+			netdev_features_t missing;
+
+			if (!netif_running(slave->dev))
+				continue;
+
+			available = slave->dev->features | slave->dev->hw_features;
+			missing = enabled & ~available;
+			if ((enabled & NETIF_F_HW_ESP) &&
+			    !bond_ipsec_lag_slave_has_ops(slave->dev))
+				missing |= NETIF_F_HW_ESP;
+			if (missing) {
+				slave_warn(dev, slave->dev,
+					   "missing XFRM feature(s) %pNF\n",
+					   &missing);
+				return -EOPNOTSUPP;
+			}
+			targets++;
+		}
+		if (!targets)
+			return -EOPNOTSUPP;
+	}
+
+	if ((dev->features & NETIF_F_HW_ESP) &&
+	    !(features & NETIF_F_HW_ESP)) {
+		bond_ipsec_lag_begin_flush(bond);
+		xfrm_dev_state_flush(dev_net(dev), dev, true);
+	}
+
+	bond_for_each_slave(bond, slave, iter) {
+		if (!netif_running(slave->dev))
+			continue;
+
+		err = bond_set_slave_xfrm_features(bond, slave, features);
+		if (err)
+			break;
+	}
+	if (err) {
+		bond_restore_slave_xfrm_features(bond, dev->features);
+		if ((dev->features & NETIF_F_HW_ESP) &&
+		    !(features & NETIF_F_HW_ESP))
+			bond_ipsec_lag_end_flush(bond);
+		return err;
+	}
+
+	bond_for_each_slave(bond, slave, iter) {
+		netdev_features_t missing = enabled & ~slave->dev->features;
+
+		if (!netif_running(slave->dev))
+			continue;
+
+		if (missing) {
+			slave_warn(dev, slave->dev,
+				   "failed to enable XFRM feature(s) %pNF\n",
+				   &missing);
+			bond_restore_slave_xfrm_features(bond, dev->features);
+			if ((dev->features & NETIF_F_HW_ESP) &&
+			    !(features & NETIF_F_HW_ESP))
+				bond_ipsec_lag_end_flush(bond);
+			return -EOPNOTSUPP;
+		}
+	}
+
+	if (features & NETIF_F_HW_ESP)
+		bond_ipsec_lag_end_flush(bond);
+
+	return 0;
+}
+#endif /* CONFIG_XFRM_OFFLOAD */
+
 static int bond_header_create(struct sk_buff *skb, struct net_device *bond_dev,
 			      unsigned short type, const void *daddr,
 			      const void *saddr, unsigned int len)
@@ -6510,6 +6739,9 @@ 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,
+#ifdef CONFIG_XFRM_OFFLOAD
+	.ndo_set_features	= bond_set_features,
+#endif /* CONFIG_XFRM_OFFLOAD */
 	.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/bonding/bond_options.c b/drivers/net/bonding/bond_options.c
index 7380cc4ee75a..634b42c0d8e9 100644
--- a/drivers/net/bonding/bond_options.c
+++ b/drivers/net/bonding/bond_options.c
@@ -885,7 +885,7 @@ static bool bond_set_xfrm_features(struct bonding *bond)
 
 	if (BOND_MODE(bond) == BOND_MODE_ACTIVEBACKUP)
 		bond->dev->wanted_features |= BOND_XFRM_FEATURES;
-	else
+	else if (!bond_mode_can_use_lag_xfrm(bond))
 		bond->dev->wanted_features &= ~BOND_XFRM_FEATURES;
 
 	return true;
-- 
2.53.0

  parent reply	other threads:[~2026-05-20  8:10 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-05-20  8:10 [PATCH RFC net-next 0/4] bonding: support LAG IPsec offload with replicated SAs Jihong Min
2026-05-20  8:10 ` [PATCH RFC net-next 1/4] xfrm: add a lower-device offload handle resolver Jihong Min
2026-05-20  8:10 ` [PATCH RFC net-next 2/4] bonding: replicate XFRM offload state across LAG slaves Jihong Min
2026-05-20  8:10 ` Jihong Min [this message]
2026-05-20  8:10 ` [PATCH RFC net-next 4/4] bonding: handle replicated IPsec SAs across LAG changes Jihong Min

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260520081004.2232091-4-hurryman2212@gmail.com \
    --to=hurryman2212@gmail.com \
    --cc=andrew+netdev@lunn.ch \
    --cc=davem@davemloft.net \
    --cc=edumazet@google.com \
    --cc=herbert@gondor.apana.org.au \
    --cc=horms@kernel.org \
    --cc=jv@jvosburgh.net \
    --cc=kuba@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=netdev@vger.kernel.org \
    --cc=pabeni@redhat.com \
    --cc=steffen.klassert@secunet.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox