summaryrefslogtreecommitdiff
path: root/drivers/net/team
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/team')
-rw-r--r--drivers/net/team/Makefile1
-rw-r--r--drivers/net/team/team_core.c (renamed from drivers/net/team/team.c)119
-rw-r--r--drivers/net/team/team_nl.c59
-rw-r--r--drivers/net/team/team_nl.h29
4 files changed, 134 insertions, 74 deletions
diff --git a/drivers/net/team/Makefile b/drivers/net/team/Makefile
index f582d81a5091..7a5aa20d286b 100644
--- a/drivers/net/team/Makefile
+++ b/drivers/net/team/Makefile
@@ -3,6 +3,7 @@
# Makefile for the network team driver
#
+team-y:= team_core.o team_nl.o
obj-$(CONFIG_NET_TEAM) += team.o
obj-$(CONFIG_NET_TEAM_MODE_BROADCAST) += team_mode_broadcast.o
obj-$(CONFIG_NET_TEAM_MODE_ROUNDROBIN) += team_mode_roundrobin.o
diff --git a/drivers/net/team/team.c b/drivers/net/team/team_core.c
index 0a44bbdcfb7b..8bc56186b2a3 100644
--- a/drivers/net/team/team.c
+++ b/drivers/net/team/team_core.c
@@ -23,10 +23,13 @@
#include <linux/rtnetlink.h>
#include <net/rtnetlink.h>
#include <net/genetlink.h>
+#include <net/netdev_lock.h>
#include <net/netlink.h>
#include <net/sch_generic.h>
#include <linux/if_team.h>
+#include "team_nl.h"
+
#define DRV_NAME "team"
@@ -52,7 +55,7 @@ static int __set_port_dev_addr(struct net_device *port_dev,
memcpy(addr.__data, dev_addr, port_dev->addr_len);
addr.ss_family = port_dev->type;
- return dev_set_mac_address(port_dev, (struct sockaddr *)&addr, NULL);
+ return dev_set_mac_address(port_dev, &addr, NULL);
}
static int team_port_set_orig_dev_addr(struct team_port *port)
@@ -981,7 +984,8 @@ static void team_port_disable(struct team *team,
#define TEAM_VLAN_FEATURES (NETIF_F_HW_CSUM | NETIF_F_SG | \
NETIF_F_FRAGLIST | NETIF_F_GSO_SOFTWARE | \
- NETIF_F_HIGHDMA | NETIF_F_LRO)
+ NETIF_F_HIGHDMA | NETIF_F_LRO | \
+ NETIF_F_GSO_ENCAP_ALL)
#define TEAM_ENC_FEATURES (NETIF_F_HW_CSUM | NETIF_F_SG | \
NETIF_F_RXCSUM | NETIF_F_GSO_SOFTWARE)
@@ -989,14 +993,19 @@ static void team_port_disable(struct team *team,
static void __team_compute_features(struct team *team)
{
struct team_port *port;
- netdev_features_t vlan_features = TEAM_VLAN_FEATURES &
- NETIF_F_ALL_FOR_ALL;
+ netdev_features_t vlan_features = TEAM_VLAN_FEATURES;
netdev_features_t enc_features = TEAM_ENC_FEATURES;
unsigned short max_hard_header_len = ETH_HLEN;
unsigned int dst_release_flag = IFF_XMIT_DST_RELEASE |
IFF_XMIT_DST_RELEASE_PERM;
rcu_read_lock();
+ if (list_empty(&team->port_list))
+ goto done;
+
+ vlan_features = netdev_base_features(vlan_features);
+ enc_features = netdev_base_features(enc_features);
+
list_for_each_entry_rcu(port, &team->port_list, list) {
vlan_features = netdev_increment_features(vlan_features,
port->dev->vlan_features,
@@ -1006,11 +1015,11 @@ static void __team_compute_features(struct team *team)
port->dev->hw_enc_features,
TEAM_ENC_FEATURES);
-
dst_release_flag &= port->dev->priv_flags;
if (port->dev->hard_header_len > max_hard_header_len)
max_hard_header_len = port->dev->hard_header_len;
}
+done:
rcu_read_unlock();
team->dev->vlan_features = vlan_features;
@@ -1167,6 +1176,13 @@ static int team_port_add(struct team *team, struct net_device *port_dev,
return -EBUSY;
}
+ if (netdev_has_upper_dev(port_dev, dev)) {
+ NL_SET_ERR_MSG(extack, "Device is already a lower device of the team interface");
+ netdev_err(dev, "Device %s is already a lower device of the team interface\n",
+ portname);
+ return -EBUSY;
+ }
+
if (port_dev->features & NETIF_F_VLAN_CHALLENGED &&
vlan_uses_dev(dev)) {
NL_SET_ERR_MSG(extack, "Device is VLAN challenged and team device has VLAN set up");
@@ -1762,8 +1778,8 @@ static void team_change_rx_flags(struct net_device *dev, int change)
struct team_port *port;
int inc;
- rcu_read_lock();
- list_for_each_entry_rcu(port, &team->port_list, list) {
+ mutex_lock(&team->lock);
+ list_for_each_entry(port, &team->port_list, list) {
if (change & IFF_PROMISC) {
inc = dev->flags & IFF_PROMISC ? 1 : -1;
dev_set_promiscuity(port->dev, inc);
@@ -1773,7 +1789,7 @@ static void team_change_rx_flags(struct net_device *dev, int change)
dev_set_allmulti(port->dev, inc);
}
}
- rcu_read_unlock();
+ mutex_unlock(&team->lock);
}
static void team_set_rx_mode(struct net_device *dev)
@@ -1829,7 +1845,7 @@ static int team_change_mtu(struct net_device *dev, int new_mtu)
team->port_mtu_change_allowed = false;
mutex_unlock(&team->lock);
- dev->mtu = new_mtu;
+ WRITE_ONCE(dev->mtu, new_mtu);
return 0;
@@ -1944,8 +1960,7 @@ static void team_netpoll_cleanup(struct net_device *dev)
mutex_unlock(&team->lock);
}
-static int team_netpoll_setup(struct net_device *dev,
- struct netpoll_info *npifo)
+static int team_netpoll_setup(struct net_device *dev)
{
struct team *team = netdev_priv(dev);
struct team_port *port;
@@ -2010,8 +2025,7 @@ static netdev_features_t team_fix_features(struct net_device *dev,
netdev_features_t mask;
mask = features;
- features &= ~NETIF_F_ONE_FOR_ALL;
- features |= NETIF_F_ALL_FOR_ALL;
+ features = netdev_base_features(features);
rcu_read_lock();
list_for_each_entry_rcu(port, &team->port_list, list) {
@@ -2187,12 +2201,12 @@ static void team_setup(struct net_device *dev)
* Let this up to underlay drivers.
*/
dev->priv_flags |= IFF_UNICAST_FLT | IFF_LIVE_ADDR_CHANGE;
-
- dev->features |= NETIF_F_LLTX;
- dev->features |= NETIF_F_GRO;
+ dev->lltx = true;
/* Don't allow team devices to change network namespaces. */
- dev->features |= NETIF_F_NETNS_LOCAL;
+ dev->netns_immutable = true;
+
+ dev->features |= NETIF_F_GRO;
dev->hw_features = TEAM_VLAN_FEATURES |
NETIF_F_HW_VLAN_CTAG_RX |
@@ -2205,10 +2219,12 @@ static void team_setup(struct net_device *dev)
dev->features |= NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_STAG_TX;
}
-static int team_newlink(struct net *src_net, struct net_device *dev,
- struct nlattr *tb[], struct nlattr *data[],
+static int team_newlink(struct net_device *dev,
+ struct rtnl_newlink_params *params,
struct netlink_ext_ack *extack)
{
+ struct nlattr **tb = params->tb;
+
if (tb[IFLA_ADDRESS] == NULL)
eth_hw_addr_random(dev);
@@ -2254,28 +2270,7 @@ static struct rtnl_link_ops team_link_ops __read_mostly = {
static struct genl_family team_nl_family;
-static const struct nla_policy team_nl_policy[TEAM_ATTR_MAX + 1] = {
- [TEAM_ATTR_UNSPEC] = { .type = NLA_UNSPEC, },
- [TEAM_ATTR_TEAM_IFINDEX] = { .type = NLA_U32 },
- [TEAM_ATTR_LIST_OPTION] = { .type = NLA_NESTED },
- [TEAM_ATTR_LIST_PORT] = { .type = NLA_NESTED },
-};
-
-static const struct nla_policy
-team_nl_option_policy[TEAM_ATTR_OPTION_MAX + 1] = {
- [TEAM_ATTR_OPTION_UNSPEC] = { .type = NLA_UNSPEC, },
- [TEAM_ATTR_OPTION_NAME] = {
- .type = NLA_STRING,
- .len = TEAM_STRING_MAX_LEN,
- },
- [TEAM_ATTR_OPTION_CHANGED] = { .type = NLA_FLAG },
- [TEAM_ATTR_OPTION_TYPE] = { .type = NLA_U8 },
- [TEAM_ATTR_OPTION_DATA] = { .type = NLA_BINARY },
- [TEAM_ATTR_OPTION_PORT_IFINDEX] = { .type = NLA_U32 },
- [TEAM_ATTR_OPTION_ARRAY_INDEX] = { .type = NLA_U32 },
-};
-
-static int team_nl_cmd_noop(struct sk_buff *skb, struct genl_info *info)
+int team_nl_noop_doit(struct sk_buff *skb, struct genl_info *info)
{
struct sk_buff *msg;
void *hdr;
@@ -2513,7 +2508,7 @@ errout:
return err;
}
-static int team_nl_cmd_options_get(struct sk_buff *skb, struct genl_info *info)
+int team_nl_options_get_doit(struct sk_buff *skb, struct genl_info *info)
{
struct team *team;
struct team_option_inst *opt_inst;
@@ -2538,7 +2533,7 @@ static int team_nl_cmd_options_get(struct sk_buff *skb, struct genl_info *info)
static int team_nl_send_event_options_get(struct team *team,
struct list_head *sel_opt_inst_list);
-static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info)
+int team_nl_options_set_doit(struct sk_buff *skb, struct genl_info *info)
{
struct team *team;
int err = 0;
@@ -2579,7 +2574,7 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info)
err = nla_parse_nested_deprecated(opt_attrs,
TEAM_ATTR_OPTION_MAX,
nl_option,
- team_nl_option_policy,
+ team_attr_option_nl_policy,
info->extack);
if (err)
goto team_put;
@@ -2647,7 +2642,9 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info)
ctx.data.u32_val = nla_get_u32(attr_data);
break;
case TEAM_OPTION_TYPE_STRING:
- if (nla_len(attr_data) > TEAM_STRING_MAX_LEN) {
+ if (nla_len(attr_data) > TEAM_STRING_MAX_LEN ||
+ !memchr(nla_data(attr_data), '\0',
+ nla_len(attr_data))) {
err = -EINVAL;
goto team_put;
}
@@ -2802,8 +2799,8 @@ errout:
return err;
}
-static int team_nl_cmd_port_list_get(struct sk_buff *skb,
- struct genl_info *info)
+int team_nl_port_list_get_doit(struct sk_buff *skb,
+ struct genl_info *info)
{
struct team *team;
int err;
@@ -2820,32 +2817,6 @@ static int team_nl_cmd_port_list_get(struct sk_buff *skb,
return err;
}
-static const struct genl_small_ops team_nl_ops[] = {
- {
- .cmd = TEAM_CMD_NOOP,
- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
- .doit = team_nl_cmd_noop,
- },
- {
- .cmd = TEAM_CMD_OPTIONS_SET,
- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
- .doit = team_nl_cmd_options_set,
- .flags = GENL_ADMIN_PERM,
- },
- {
- .cmd = TEAM_CMD_OPTIONS_GET,
- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
- .doit = team_nl_cmd_options_get,
- .flags = GENL_ADMIN_PERM,
- },
- {
- .cmd = TEAM_CMD_PORT_LIST_GET,
- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
- .doit = team_nl_cmd_port_list_get,
- .flags = GENL_ADMIN_PERM,
- },
-};
-
static const struct genl_multicast_group team_nl_mcgrps[] = {
{ .name = TEAM_GENL_CHANGE_EVENT_MC_GRP_NAME, },
};
@@ -2853,7 +2824,7 @@ static const struct genl_multicast_group team_nl_mcgrps[] = {
static struct genl_family team_nl_family __ro_after_init = {
.name = TEAM_GENL_NAME,
.version = TEAM_GENL_VERSION,
- .maxattr = TEAM_ATTR_MAX,
+ .maxattr = ARRAY_SIZE(team_nl_policy) - 1,
.policy = team_nl_policy,
.netnsok = true,
.module = THIS_MODULE,
diff --git a/drivers/net/team/team_nl.c b/drivers/net/team/team_nl.c
new file mode 100644
index 000000000000..208424ab78f5
--- /dev/null
+++ b/drivers/net/team/team_nl.c
@@ -0,0 +1,59 @@
+// SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)
+/* Do not edit directly, auto-generated from: */
+/* Documentation/netlink/specs/team.yaml */
+/* YNL-GEN kernel source */
+
+#include <net/netlink.h>
+#include <net/genetlink.h>
+
+#include "team_nl.h"
+
+#include <uapi/linux/if_team.h>
+
+/* Common nested types */
+const struct nla_policy team_attr_option_nl_policy[TEAM_ATTR_OPTION_ARRAY_INDEX + 1] = {
+ [TEAM_ATTR_OPTION_NAME] = { .type = NLA_STRING, .len = TEAM_STRING_MAX_LEN, },
+ [TEAM_ATTR_OPTION_CHANGED] = { .type = NLA_FLAG, },
+ [TEAM_ATTR_OPTION_TYPE] = { .type = NLA_U8, },
+ [TEAM_ATTR_OPTION_DATA] = { .type = NLA_BINARY, },
+ [TEAM_ATTR_OPTION_REMOVED] = { .type = NLA_FLAG, },
+ [TEAM_ATTR_OPTION_PORT_IFINDEX] = { .type = NLA_U32, },
+ [TEAM_ATTR_OPTION_ARRAY_INDEX] = { .type = NLA_U32, },
+};
+
+const struct nla_policy team_item_option_nl_policy[TEAM_ATTR_ITEM_OPTION + 1] = {
+ [TEAM_ATTR_ITEM_OPTION] = NLA_POLICY_NESTED(team_attr_option_nl_policy),
+};
+
+/* Global operation policy for team */
+const struct nla_policy team_nl_policy[TEAM_ATTR_LIST_OPTION + 1] = {
+ [TEAM_ATTR_TEAM_IFINDEX] = { .type = NLA_U32, },
+ [TEAM_ATTR_LIST_OPTION] = NLA_POLICY_NESTED(team_item_option_nl_policy),
+};
+
+/* Ops table for team */
+const struct genl_small_ops team_nl_ops[4] = {
+ {
+ .cmd = TEAM_CMD_NOOP,
+ .validate = GENL_DONT_VALIDATE_STRICT,
+ .doit = team_nl_noop_doit,
+ },
+ {
+ .cmd = TEAM_CMD_OPTIONS_SET,
+ .validate = GENL_DONT_VALIDATE_STRICT,
+ .doit = team_nl_options_set_doit,
+ .flags = GENL_ADMIN_PERM,
+ },
+ {
+ .cmd = TEAM_CMD_OPTIONS_GET,
+ .validate = GENL_DONT_VALIDATE_STRICT,
+ .doit = team_nl_options_get_doit,
+ .flags = GENL_ADMIN_PERM,
+ },
+ {
+ .cmd = TEAM_CMD_PORT_LIST_GET,
+ .validate = GENL_DONT_VALIDATE_STRICT,
+ .doit = team_nl_port_list_get_doit,
+ .flags = GENL_ADMIN_PERM,
+ },
+};
diff --git a/drivers/net/team/team_nl.h b/drivers/net/team/team_nl.h
new file mode 100644
index 000000000000..c9ec1b22ac4d
--- /dev/null
+++ b/drivers/net/team/team_nl.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
+/* Do not edit directly, auto-generated from: */
+/* Documentation/netlink/specs/team.yaml */
+/* YNL-GEN kernel header */
+
+#ifndef _LINUX_TEAM_GEN_H
+#define _LINUX_TEAM_GEN_H
+
+#include <net/netlink.h>
+#include <net/genetlink.h>
+
+#include <uapi/linux/if_team.h>
+
+/* Common nested types */
+extern const struct nla_policy team_attr_option_nl_policy[TEAM_ATTR_OPTION_ARRAY_INDEX + 1];
+extern const struct nla_policy team_item_option_nl_policy[TEAM_ATTR_ITEM_OPTION + 1];
+
+/* Global operation policy for team */
+extern const struct nla_policy team_nl_policy[TEAM_ATTR_LIST_OPTION + 1];
+
+/* Ops table for team */
+extern const struct genl_small_ops team_nl_ops[4];
+
+int team_nl_noop_doit(struct sk_buff *skb, struct genl_info *info);
+int team_nl_options_set_doit(struct sk_buff *skb, struct genl_info *info);
+int team_nl_options_get_doit(struct sk_buff *skb, struct genl_info *info);
+int team_nl_port_list_get_doit(struct sk_buff *skb, struct genl_info *info);
+
+#endif /* _LINUX_TEAM_GEN_H */