summaryrefslogtreecommitdiff
path: root/drivers/net/team/team_core.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/team/team_core.c')
-rw-r--r--drivers/net/team/team_core.c65
1 files changed, 64 insertions, 1 deletions
diff --git a/drivers/net/team/team_core.c b/drivers/net/team/team_core.c
index b7282f5c9632..120aeb539d9f 100644
--- a/drivers/net/team/team_core.c
+++ b/drivers/net/team/team_core.c
@@ -2058,6 +2058,68 @@ static const struct ethtool_ops team_ethtool_ops = {
* rt netlink interface
***********************/
+/* For tx path we need a linkup && enabled port and for parse any port
+ * suffices.
+ */
+static struct team_port *team_header_port_get_rcu(struct team *team,
+ bool txable)
+{
+ struct team_port *port;
+
+ list_for_each_entry_rcu(port, &team->port_list, list) {
+ if (!txable || team_port_txable(port))
+ return port;
+ }
+
+ return NULL;
+}
+
+static int team_header_create(struct sk_buff *skb, struct net_device *team_dev,
+ unsigned short type, const void *daddr,
+ const void *saddr, unsigned int len)
+{
+ struct team *team = netdev_priv(team_dev);
+ const struct header_ops *port_ops;
+ struct team_port *port;
+ int ret = 0;
+
+ rcu_read_lock();
+ port = team_header_port_get_rcu(team, true);
+ if (port) {
+ port_ops = READ_ONCE(port->dev->header_ops);
+ if (port_ops && port_ops->create)
+ ret = port_ops->create(skb, port->dev,
+ type, daddr, saddr, len);
+ }
+ rcu_read_unlock();
+ return ret;
+}
+
+static int team_header_parse(const struct sk_buff *skb,
+ const struct net_device *team_dev,
+ unsigned char *haddr)
+{
+ struct team *team = netdev_priv(team_dev);
+ const struct header_ops *port_ops;
+ struct team_port *port;
+ int ret = 0;
+
+ rcu_read_lock();
+ port = team_header_port_get_rcu(team, false);
+ if (port) {
+ port_ops = READ_ONCE(port->dev->header_ops);
+ if (port_ops && port_ops->parse)
+ ret = port_ops->parse(skb, port->dev, haddr);
+ }
+ rcu_read_unlock();
+ return ret;
+}
+
+static const struct header_ops team_header_ops = {
+ .create = team_header_create,
+ .parse = team_header_parse,
+};
+
static void team_setup_by_port(struct net_device *dev,
struct net_device *port_dev)
{
@@ -2066,7 +2128,8 @@ static void team_setup_by_port(struct net_device *dev,
if (port_dev->type == ARPHRD_ETHER)
dev->header_ops = team->header_ops_cache;
else
- dev->header_ops = port_dev->header_ops;
+ dev->header_ops = port_dev->header_ops ?
+ &team_header_ops : NULL;
dev->type = port_dev->type;
dev->hard_header_len = port_dev->hard_header_len;
dev->needed_headroom = port_dev->needed_headroom;