[PATCH net-next v5 09/10] net: team: Add new tx_enabled team port option

Marc Harvey
2026-04-06T03:05:04Z

This option allows independent control over tx enablement without
affecting rx enablement. Like the rx_enabled option, this also
implicitly affects the enabled option.

If this option is not used, then the enabled option will continue to
behave as it did before.

Tested in a follow-up patch with a new selftest.

Signed-off-by: Marc Harvey <marcharvey@google.com>
---
Changes in v5:
- None

Changes in v4:
- New patch: split from the original monolithic v3 patch "net: team:
Decouple rx and tx enablement in the team driver".
- Link to v3: https://lore.kernel.org/netdev/20260402-teaming-driver-internal-v3-6-e8cfdec3b5c2@google.com/
---
drivers/net/team/team_core.c | 55 ++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 55 insertions(+)

diff --git a/drivers/net/team/team_core.c b/drivers/net/team/team_core.c
index 67f77de4cf10..0c87f9972457 100644
--- a/drivers/net/team/team_core.c
+++ b/drivers/net/team/team_core.c
@@ -978,6 +978,21 @@ static void __team_port_enable_tx(struct team *team,
team_tx_port_index_hash(team, port->tx_index));
}

+static void team_port_enable_tx(struct team *team,
+ struct team_port *port)
+{
+ if (team_port_tx_enabled(port))
+ return;
+
+ __team_port_enable_tx(team, port);
+ team_adjust_ops(team);
+ team_queue_override_port_add(team, port);
+
+ /* Don't rejoin multicast, since this port might not be receiving. */
+ team_notify_peers(team);
+ team_lower_state_changed(port);
+}
+
static void __reconstruct_port_hlist(struct team *team, int rm_index)
{
struct hlist_head *tx_port_index_hash;
@@ -1007,6 +1022,19 @@ static void __team_port_disable_tx(struct team *team,
WRITE_ONCE(team->tx_en_port_count, team->tx_en_port_count - 1);
}

+static void team_port_disable_tx(struct team *team,
+ struct team_port *port)
+{
+ if (!team_port_tx_enabled(port))
+ return;
+
+ __team_port_disable_tx(team, port);
+
+ team_queue_override_port_del(team, port);
+ team_adjust_ops(team);
+ team_lower_state_changed(port);
+}
+
/*
* Enable TX AND RX on the port.
*/
@@ -1529,6 +1557,26 @@ static int team_port_rx_en_option_set(struct team *team,
return 0;
}

+static void team_port_tx_en_option_get(struct team *team,
+ struct team_gsetter_ctx *ctx)
+{
+ struct team_port *port = ctx->info->port;
+
+ ctx->data.bool_val = team_port_tx_enabled(port);
+}
+
+static int team_port_tx_en_option_set(struct team *team,
+ struct team_gsetter_ctx *ctx)
+{
+ struct team_port *port = ctx->info->port;
+
+ if (ctx->data.bool_val)
+ team_port_enable_tx(team, port);
+ else
+ team_port_disable_tx(team, port);
+ return 0;
+}
+
static void team_user_linkup_option_get(struct team *team,
struct team_gsetter_ctx *ctx)
{
@@ -1657,6 +1705,13 @@ static const struct team_option team_options[] = {
.getter = team_port_rx_en_option_get,
.setter = team_port_rx_en_option_set,
},
+ {
+ .name = "tx_enabled",
+ .type = TEAM_OPTION_TYPE_BOOL,
+ .per_port = true,
+ .getter = team_port_tx_en_option_get,
+ .setter = team_port_tx_en_option_set,
+ },
{
.name = "user_linkup",
.type = TEAM_OPTION_TYPE_BOOL,

--
2.53.0.1185.g05d4b7b318-goog