Netdev List
 help / color / mirror / Atom feed
* [PATCH net] bonding: 3ad: select a port when the TX array is empty
@ 2026-09-15  9:02 Yun Lu
  2026-09-17 21:02 ` netdev-bot+sashiko
  0 siblings, 1 reply; 3+ messages in thread
From: Yun Lu @ 2026-09-15  9:02 UTC (permalink / raw)
  To: jv, andrew+netdev, davem, edumazet, kuba, pabeni; +Cc: razor, maheshb, netdev

From: Yun Lu <luyun@kylinos.cn>

During 802.3ad bond bring-up, userspace can observe carrier up while
ARP requests still fail to transmit. This can happen even though the
active aggregator already has ports eligible for transmission.

Commit 23de0d7b6f0e ("bonding: pair enable_port with
slave_arr_updates") made port enablement request an array rebuild, but
the rebuild remains asynchronous. The 802.3ad state machine enables a
port before queueing slave_arr_work. Until the worker publishes the new
usable_slaves array, the transmit path drops packets. RTNL contention
can prolong this window because the worker retries later.

When ordinary 802.3ad skb transmission finds an empty usable array,
select a port directly from the active aggregator. Hold mode_lock across
the counting and selection passes so aggregator reselection cannot expose
the intermediate state where all is_active flags are clear. The caller's
RCU read lock protects the slave lifetime.

Use the same bond_slave_can_tx() eligibility and list order as the array
path, so hash selection remains equivalent when the eligible set is
stable. Skip the fallback in netpoll context because TX may be re-entered
while mode_lock is already held.

Fixes: ee6377147409 ("bonding: Simplify the xmit function for modes that use xmit_hash")
Signed-off-by: Yun Lu <luyun@kylinos.cn>
---
 drivers/net/bonding/bond_main.c | 66 +++++++++++++++++++++++++++++++--
 1 file changed, 62 insertions(+), 4 deletions(-)

diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index a9bff7663eec..2cf280bf134f 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -5233,11 +5233,11 @@ static struct slave *bond_xmit_3ad_xor_slave_get(struct bonding *bond,
 	unsigned int count;
 	u32 hash;
 
-	hash = bond_xmit_hash(bond, skb);
 	count = slaves ? READ_ONCE(slaves->count) : 0;
 	if (unlikely(!count))
 		return NULL;
 
+	hash = bond_xmit_hash(bond, skb);
 	slave = slaves->arr[hash % count];
 	return slave;
 }
@@ -5289,9 +5289,65 @@ static bool bond_should_broadcast_neighbor(struct sk_buff *skb,
 	return false;
 }
 
-/* Use this Xmit function for 3AD as well as XOR modes. The current
- * usable slave array is formed in the control path. The xmit function
- * just calculates hash and sends the packet out.
+/* Called with RCU and bond->mode_lock held. */
+static bool bond_3ad_slave_is_eligible(struct slave *slave)
+{
+	const struct aggregator *agg;
+
+	agg = rcu_dereference(SLAVE_AD_INFO(slave)->port.aggregator);
+	return agg && agg->is_active && bond_slave_can_tx(slave);
+}
+
+/* Called with RCU held when the transmit array has not caught up with
+ * the 802.3ad state machine. Do not enable ports here: use the same
+ * eligibility checks as bond_update_slave_arr().
+ */
+static struct slave *bond_3ad_xmit_fallback(struct bonding *bond, u32 hash)
+{
+	struct slave *selected = NULL;
+	unsigned int eligible = 0;
+	unsigned int target;
+	struct list_head *iter;
+	struct slave *slave;
+
+	/* Netpoll can re-enter TX while the state machine holds mode_lock.
+	 * Keep the existing empty-array drop behavior in that context.
+	 */
+	if (unlikely(netpoll_tx_running(bond->dev)))
+		return NULL;
+
+	/* Aggregator selection temporarily clears all is_active flags. Keep
+	 * both loops under mode_lock to avoid observing that intermediate
+	 * state. The caller's RCU read lock protects the selected slave.
+	 */
+	spin_lock_bh(&bond->mode_lock);
+	bond_for_each_slave_rcu(bond, slave, iter)
+		if (bond_3ad_slave_is_eligible(slave))
+			eligible++;
+
+	if (!eligible)
+		goto out;
+
+	target = hash % eligible;
+	bond_for_each_slave_rcu(bond, slave, iter) {
+		if (!bond_3ad_slave_is_eligible(slave))
+			continue;
+
+		if (!target) {
+			selected = slave;
+			break;
+		}
+		target--;
+	}
+
+out:
+	spin_unlock_bh(&bond->mode_lock);
+	return selected;
+}
+
+/* Use this Xmit function for 3AD as well as XOR modes. The usable slave
+ * array is formed in the control path.  In 3AD mode, fall back to the
+ * current port state while an empty array awaits an update.
  */
 static netdev_tx_t bond_3ad_xor_xmit(struct sk_buff *skb,
 				     struct net_device *dev)
@@ -5302,6 +5358,8 @@ static netdev_tx_t bond_3ad_xor_xmit(struct sk_buff *skb,
 
 	slaves = rcu_dereference(bond->usable_slaves);
 	slave = bond_xmit_3ad_xor_slave_get(bond, skb, slaves);
+	if (unlikely(!slave) && BOND_MODE(bond) == BOND_MODE_8023AD)
+		slave = bond_3ad_xmit_fallback(bond, bond_xmit_hash(bond, skb));
 	if (likely(slave))
 		return bond_dev_queue_xmit(bond, skb, slave->dev);
 
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2026-09-18  9:38 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-15  9:02 [PATCH net] bonding: 3ad: select a port when the TX array is empty Yun Lu
2026-09-17 21:02 ` netdev-bot+sashiko
2026-09-18  9:37   ` luyun

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox