summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/net/team/team_core.c104
-rw-r--r--drivers/net/team/team_mode_loadbalance.c2
-rw-r--r--include/linux/if_team.h16
3 files changed, 95 insertions, 27 deletions
diff --git a/drivers/net/team/team_core.c b/drivers/net/team/team_core.c
index 826769473878..e437099a5a17 100644
--- a/drivers/net/team/team_core.c
+++ b/drivers/net/team/team_core.c
@@ -87,7 +87,7 @@ static void team_lower_state_changed(struct team_port *port)
struct netdev_lag_lower_state_info info;
info.link_up = port->linkup;
- info.tx_enabled = team_port_enabled(port);
+ info.tx_enabled = team_port_tx_enabled(port);
netdev_lower_state_changed(port->dev, &info);
}
@@ -538,7 +538,7 @@ static void team_adjust_ops(struct team *team)
else
team->ops.transmit = team->mode->ops->transmit;
- if (!team->tx_en_port_count || !team_is_mode_set(team) ||
+ if (!team->rx_en_port_count || !team_is_mode_set(team) ||
!team->mode->ops->receive)
team->ops.receive = team_dummy_receive;
else
@@ -734,7 +734,7 @@ static rx_handler_result_t team_handle_frame(struct sk_buff **pskb)
port = team_port_get_rcu(skb->dev);
team = port->team;
- if (!team_port_enabled(port)) {
+ if (!team_port_rx_enabled(port)) {
if (is_link_local_ether_addr(eth_hdr(skb)->h_dest))
/* link-local packets are mostly useful when stack receives them
* with the link they arrive on.
@@ -876,7 +876,7 @@ static void __team_queue_override_enabled_check(struct team *team)
static void team_queue_override_port_prio_changed(struct team *team,
struct team_port *port)
{
- if (!port->queue_id || !team_port_enabled(port))
+ if (!port->queue_id || !team_port_tx_enabled(port))
return;
__team_queue_override_port_del(team, port);
__team_queue_override_port_add(team, port);
@@ -887,7 +887,7 @@ static void team_queue_override_port_change_queue_id(struct team *team,
struct team_port *port,
u16 new_queue_id)
{
- if (team_port_enabled(port)) {
+ if (team_port_tx_enabled(port)) {
__team_queue_override_port_del(team, port);
port->queue_id = new_queue_id;
__team_queue_override_port_add(team, port);
@@ -927,26 +927,33 @@ static bool team_port_find(const struct team *team,
return false;
}
+static void __team_port_enable_rx(struct team *team,
+ struct team_port *port)
+{
+ team->rx_en_port_count++;
+ WRITE_ONCE(port->rx_enabled, true);
+}
+
+static void __team_port_disable_rx(struct team *team,
+ struct team_port *port)
+{
+ team->rx_en_port_count--;
+ WRITE_ONCE(port->rx_enabled, false);
+}
+
/*
- * Enable/disable port by adding to enabled port hashlist and setting
- * port->tx_index (Might be racy so reader could see incorrect ifindex when
- * processing a flying packet, but that is not a problem). Write guarded
- * by RTNL.
+ * Enable just TX on the port by adding to tx-enabled port hashlist and
+ * setting port->tx_index (Might be racy so reader could see incorrect
+ * ifindex when processing a flying packet, but that is not a problem).
+ * Write guarded by RTNL.
*/
-static void team_port_enable(struct team *team,
- struct team_port *port)
+static void __team_port_enable_tx(struct team *team,
+ struct team_port *port)
{
- if (team_port_enabled(port))
- return;
WRITE_ONCE(port->tx_index, team->tx_en_port_count);
WRITE_ONCE(team->tx_en_port_count, team->tx_en_port_count + 1);
hlist_add_head_rcu(&port->tx_hlist,
team_tx_port_index_hash(team, port->tx_index));
- team_adjust_ops(team);
- team_queue_override_port_add(team, port);
- team_notify_peers(team);
- team_mcast_rejoin(team);
- team_lower_state_changed(port);
}
static void __reconstruct_port_hlist(struct team *team, int rm_index)
@@ -965,20 +972,69 @@ static void __reconstruct_port_hlist(struct team *team, int rm_index)
}
}
-static void team_port_disable(struct team *team,
- struct team_port *port)
+static void __team_port_disable_tx(struct team *team,
+ struct team_port *port)
{
- if (!team_port_enabled(port))
- return;
if (team->ops.port_tx_disabled)
team->ops.port_tx_disabled(team, port);
+
hlist_del_rcu(&port->tx_hlist);
__reconstruct_port_hlist(team, port->tx_index);
+
WRITE_ONCE(port->tx_index, -1);
WRITE_ONCE(team->tx_en_port_count, team->tx_en_port_count - 1);
- team_queue_override_port_del(team, port);
+}
+
+/*
+ * Enable TX AND RX on the port.
+ */
+static void team_port_enable(struct team *team,
+ struct team_port *port)
+{
+ bool rx_was_enabled;
+ bool tx_was_enabled;
+
+ if (team_port_enabled(port))
+ return;
+
+ rx_was_enabled = team_port_rx_enabled(port);
+ tx_was_enabled = team_port_tx_enabled(port);
+
+ if (!rx_was_enabled)
+ __team_port_enable_rx(team, port);
+ if (!tx_was_enabled)
+ __team_port_enable_tx(team, port);
+
+ team_adjust_ops(team);
+ if (!tx_was_enabled)
+ team_queue_override_port_add(team, port);
+ team_notify_peers(team);
+ if (!rx_was_enabled)
+ team_mcast_rejoin(team);
+ if (!tx_was_enabled)
+ team_lower_state_changed(port);
+}
+
+static void team_port_disable(struct team *team,
+ struct team_port *port)
+{
+ bool rx_was_enabled = team_port_rx_enabled(port);
+ bool tx_was_enabled = team_port_tx_enabled(port);
+
+ if (!tx_was_enabled && !rx_was_enabled)
+ return;
+
+ if (tx_was_enabled) {
+ __team_port_disable_tx(team, port);
+ team_queue_override_port_del(team, port);
+ }
+ if (rx_was_enabled)
+ __team_port_disable_rx(team, port);
+
team_adjust_ops(team);
- team_lower_state_changed(port);
+
+ if (tx_was_enabled)
+ team_lower_state_changed(port);
}
static int team_port_enter(struct team *team, struct team_port *port)
diff --git a/drivers/net/team/team_mode_loadbalance.c b/drivers/net/team/team_mode_loadbalance.c
index 4833fbfe241e..38a459649569 100644
--- a/drivers/net/team/team_mode_loadbalance.c
+++ b/drivers/net/team/team_mode_loadbalance.c
@@ -380,7 +380,7 @@ static int lb_tx_hash_to_port_mapping_set(struct team *team,
list_for_each_entry(port, &team->port_list, list) {
if (ctx->data.u32_val == port->dev->ifindex &&
- team_port_enabled(port)) {
+ team_port_tx_enabled(port)) {
rcu_assign_pointer(LB_HTPM_PORT_BY_HASH(lb_priv, hash),
port);
return 0;
diff --git a/include/linux/if_team.h b/include/linux/if_team.h
index c777170ef552..3d21e06fda67 100644
--- a/include/linux/if_team.h
+++ b/include/linux/if_team.h
@@ -31,6 +31,7 @@ struct team_port {
struct list_head list; /* node in ordinary list */
struct team *team;
int tx_index; /* index of tx enabled port. If disabled, -1 */
+ bool rx_enabled;
bool linkup; /* either state.linkup or user.linkup */
@@ -75,14 +76,24 @@ static inline struct team_port *team_port_get_rcu(const struct net_device *dev)
return rcu_dereference(dev->rx_handler_data);
}
-static inline bool team_port_enabled(struct team_port *port)
+static inline bool team_port_rx_enabled(struct team_port *port)
+{
+ return READ_ONCE(port->rx_enabled);
+}
+
+static inline bool team_port_tx_enabled(struct team_port *port)
{
return READ_ONCE(port->tx_index) != -1;
}
+static inline bool team_port_enabled(struct team_port *port)
+{
+ return team_port_rx_enabled(port) && team_port_tx_enabled(port);
+}
+
static inline bool team_port_txable(struct team_port *port)
{
- return port->linkup && team_port_enabled(port);
+ return port->linkup && team_port_tx_enabled(port);
}
static inline bool team_port_dev_txable(const struct net_device *port_dev)
@@ -193,6 +204,7 @@ struct team {
* List of tx-enabled ports and counts of rx and tx-enabled ports.
*/
int tx_en_port_count;
+ int rx_en_port_count;
struct hlist_head tx_en_port_hlist[TEAM_PORT_HASHENTRIES];
struct list_head port_list; /* list of all ports */