xref: /kernel/linux/linux-6.6/drivers/net/team/team.c (revision 62306a36)
162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * drivers/net/team/team.c - Network team device driver
462306a36Sopenharmony_ci * Copyright (c) 2011 Jiri Pirko <jpirko@redhat.com>
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/ethtool.h>
862306a36Sopenharmony_ci#include <linux/kernel.h>
962306a36Sopenharmony_ci#include <linux/types.h>
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/init.h>
1262306a36Sopenharmony_ci#include <linux/slab.h>
1362306a36Sopenharmony_ci#include <linux/rcupdate.h>
1462306a36Sopenharmony_ci#include <linux/errno.h>
1562306a36Sopenharmony_ci#include <linux/ctype.h>
1662306a36Sopenharmony_ci#include <linux/notifier.h>
1762306a36Sopenharmony_ci#include <linux/netdevice.h>
1862306a36Sopenharmony_ci#include <linux/netpoll.h>
1962306a36Sopenharmony_ci#include <linux/if_vlan.h>
2062306a36Sopenharmony_ci#include <linux/if_arp.h>
2162306a36Sopenharmony_ci#include <linux/socket.h>
2262306a36Sopenharmony_ci#include <linux/etherdevice.h>
2362306a36Sopenharmony_ci#include <linux/rtnetlink.h>
2462306a36Sopenharmony_ci#include <net/rtnetlink.h>
2562306a36Sopenharmony_ci#include <net/genetlink.h>
2662306a36Sopenharmony_ci#include <net/netlink.h>
2762306a36Sopenharmony_ci#include <net/sch_generic.h>
2862306a36Sopenharmony_ci#include <generated/utsrelease.h>
2962306a36Sopenharmony_ci#include <linux/if_team.h>
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#define DRV_NAME "team"
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci/**********
3562306a36Sopenharmony_ci * Helpers
3662306a36Sopenharmony_ci **********/
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistatic struct team_port *team_port_get_rtnl(const struct net_device *dev)
3962306a36Sopenharmony_ci{
4062306a36Sopenharmony_ci	struct team_port *port = rtnl_dereference(dev->rx_handler_data);
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	return netif_is_team_port(dev) ? port : NULL;
4362306a36Sopenharmony_ci}
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci/*
4662306a36Sopenharmony_ci * Since the ability to change device address for open port device is tested in
4762306a36Sopenharmony_ci * team_port_add, this function can be called without control of return value
4862306a36Sopenharmony_ci */
4962306a36Sopenharmony_cistatic int __set_port_dev_addr(struct net_device *port_dev,
5062306a36Sopenharmony_ci			       const unsigned char *dev_addr)
5162306a36Sopenharmony_ci{
5262306a36Sopenharmony_ci	struct sockaddr_storage addr;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	memcpy(addr.__data, dev_addr, port_dev->addr_len);
5562306a36Sopenharmony_ci	addr.ss_family = port_dev->type;
5662306a36Sopenharmony_ci	return dev_set_mac_address(port_dev, (struct sockaddr *)&addr, NULL);
5762306a36Sopenharmony_ci}
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_cistatic int team_port_set_orig_dev_addr(struct team_port *port)
6062306a36Sopenharmony_ci{
6162306a36Sopenharmony_ci	return __set_port_dev_addr(port->dev, port->orig.dev_addr);
6262306a36Sopenharmony_ci}
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cistatic int team_port_set_team_dev_addr(struct team *team,
6562306a36Sopenharmony_ci				       struct team_port *port)
6662306a36Sopenharmony_ci{
6762306a36Sopenharmony_ci	return __set_port_dev_addr(port->dev, team->dev->dev_addr);
6862306a36Sopenharmony_ci}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ciint team_modeop_port_enter(struct team *team, struct team_port *port)
7162306a36Sopenharmony_ci{
7262306a36Sopenharmony_ci	return team_port_set_team_dev_addr(team, port);
7362306a36Sopenharmony_ci}
7462306a36Sopenharmony_ciEXPORT_SYMBOL(team_modeop_port_enter);
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_civoid team_modeop_port_change_dev_addr(struct team *team,
7762306a36Sopenharmony_ci				      struct team_port *port)
7862306a36Sopenharmony_ci{
7962306a36Sopenharmony_ci	team_port_set_team_dev_addr(team, port);
8062306a36Sopenharmony_ci}
8162306a36Sopenharmony_ciEXPORT_SYMBOL(team_modeop_port_change_dev_addr);
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cistatic void team_lower_state_changed(struct team_port *port)
8462306a36Sopenharmony_ci{
8562306a36Sopenharmony_ci	struct netdev_lag_lower_state_info info;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	info.link_up = port->linkup;
8862306a36Sopenharmony_ci	info.tx_enabled = team_port_enabled(port);
8962306a36Sopenharmony_ci	netdev_lower_state_changed(port->dev, &info);
9062306a36Sopenharmony_ci}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_cistatic void team_refresh_port_linkup(struct team_port *port)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	bool new_linkup = port->user.linkup_enabled ? port->user.linkup :
9562306a36Sopenharmony_ci						      port->state.linkup;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	if (port->linkup != new_linkup) {
9862306a36Sopenharmony_ci		port->linkup = new_linkup;
9962306a36Sopenharmony_ci		team_lower_state_changed(port);
10062306a36Sopenharmony_ci	}
10162306a36Sopenharmony_ci}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci/*******************
10562306a36Sopenharmony_ci * Options handling
10662306a36Sopenharmony_ci *******************/
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_cistruct team_option_inst { /* One for each option instance */
10962306a36Sopenharmony_ci	struct list_head list;
11062306a36Sopenharmony_ci	struct list_head tmp_list;
11162306a36Sopenharmony_ci	struct team_option *option;
11262306a36Sopenharmony_ci	struct team_option_inst_info info;
11362306a36Sopenharmony_ci	bool changed;
11462306a36Sopenharmony_ci	bool removed;
11562306a36Sopenharmony_ci};
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_cistatic struct team_option *__team_find_option(struct team *team,
11862306a36Sopenharmony_ci					      const char *opt_name)
11962306a36Sopenharmony_ci{
12062306a36Sopenharmony_ci	struct team_option *option;
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	list_for_each_entry(option, &team->option_list, list) {
12362306a36Sopenharmony_ci		if (strcmp(option->name, opt_name) == 0)
12462306a36Sopenharmony_ci			return option;
12562306a36Sopenharmony_ci	}
12662306a36Sopenharmony_ci	return NULL;
12762306a36Sopenharmony_ci}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_cistatic void __team_option_inst_del(struct team_option_inst *opt_inst)
13062306a36Sopenharmony_ci{
13162306a36Sopenharmony_ci	list_del(&opt_inst->list);
13262306a36Sopenharmony_ci	kfree(opt_inst);
13362306a36Sopenharmony_ci}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_cistatic void __team_option_inst_del_option(struct team *team,
13662306a36Sopenharmony_ci					  struct team_option *option)
13762306a36Sopenharmony_ci{
13862306a36Sopenharmony_ci	struct team_option_inst *opt_inst, *tmp;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	list_for_each_entry_safe(opt_inst, tmp, &team->option_inst_list, list) {
14162306a36Sopenharmony_ci		if (opt_inst->option == option)
14262306a36Sopenharmony_ci			__team_option_inst_del(opt_inst);
14362306a36Sopenharmony_ci	}
14462306a36Sopenharmony_ci}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_cistatic int __team_option_inst_add(struct team *team, struct team_option *option,
14762306a36Sopenharmony_ci				  struct team_port *port)
14862306a36Sopenharmony_ci{
14962306a36Sopenharmony_ci	struct team_option_inst *opt_inst;
15062306a36Sopenharmony_ci	unsigned int array_size;
15162306a36Sopenharmony_ci	unsigned int i;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	array_size = option->array_size;
15462306a36Sopenharmony_ci	if (!array_size)
15562306a36Sopenharmony_ci		array_size = 1; /* No array but still need one instance */
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	for (i = 0; i < array_size; i++) {
15862306a36Sopenharmony_ci		opt_inst = kmalloc(sizeof(*opt_inst), GFP_KERNEL);
15962306a36Sopenharmony_ci		if (!opt_inst)
16062306a36Sopenharmony_ci			return -ENOMEM;
16162306a36Sopenharmony_ci		opt_inst->option = option;
16262306a36Sopenharmony_ci		opt_inst->info.port = port;
16362306a36Sopenharmony_ci		opt_inst->info.array_index = i;
16462306a36Sopenharmony_ci		opt_inst->changed = true;
16562306a36Sopenharmony_ci		opt_inst->removed = false;
16662306a36Sopenharmony_ci		list_add_tail(&opt_inst->list, &team->option_inst_list);
16762306a36Sopenharmony_ci		if (option->init)
16862306a36Sopenharmony_ci			option->init(team, &opt_inst->info);
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	}
17162306a36Sopenharmony_ci	return 0;
17262306a36Sopenharmony_ci}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_cistatic int __team_option_inst_add_option(struct team *team,
17562306a36Sopenharmony_ci					 struct team_option *option)
17662306a36Sopenharmony_ci{
17762306a36Sopenharmony_ci	int err;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	if (!option->per_port) {
18062306a36Sopenharmony_ci		err = __team_option_inst_add(team, option, NULL);
18162306a36Sopenharmony_ci		if (err)
18262306a36Sopenharmony_ci			goto inst_del_option;
18362306a36Sopenharmony_ci	}
18462306a36Sopenharmony_ci	return 0;
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ciinst_del_option:
18762306a36Sopenharmony_ci	__team_option_inst_del_option(team, option);
18862306a36Sopenharmony_ci	return err;
18962306a36Sopenharmony_ci}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_cistatic void __team_option_inst_mark_removed_option(struct team *team,
19262306a36Sopenharmony_ci						   struct team_option *option)
19362306a36Sopenharmony_ci{
19462306a36Sopenharmony_ci	struct team_option_inst *opt_inst;
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	list_for_each_entry(opt_inst, &team->option_inst_list, list) {
19762306a36Sopenharmony_ci		if (opt_inst->option == option) {
19862306a36Sopenharmony_ci			opt_inst->changed = true;
19962306a36Sopenharmony_ci			opt_inst->removed = true;
20062306a36Sopenharmony_ci		}
20162306a36Sopenharmony_ci	}
20262306a36Sopenharmony_ci}
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_cistatic void __team_option_inst_del_port(struct team *team,
20562306a36Sopenharmony_ci					struct team_port *port)
20662306a36Sopenharmony_ci{
20762306a36Sopenharmony_ci	struct team_option_inst *opt_inst, *tmp;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	list_for_each_entry_safe(opt_inst, tmp, &team->option_inst_list, list) {
21062306a36Sopenharmony_ci		if (opt_inst->option->per_port &&
21162306a36Sopenharmony_ci		    opt_inst->info.port == port)
21262306a36Sopenharmony_ci			__team_option_inst_del(opt_inst);
21362306a36Sopenharmony_ci	}
21462306a36Sopenharmony_ci}
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_cistatic int __team_option_inst_add_port(struct team *team,
21762306a36Sopenharmony_ci				       struct team_port *port)
21862306a36Sopenharmony_ci{
21962306a36Sopenharmony_ci	struct team_option *option;
22062306a36Sopenharmony_ci	int err;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	list_for_each_entry(option, &team->option_list, list) {
22362306a36Sopenharmony_ci		if (!option->per_port)
22462306a36Sopenharmony_ci			continue;
22562306a36Sopenharmony_ci		err = __team_option_inst_add(team, option, port);
22662306a36Sopenharmony_ci		if (err)
22762306a36Sopenharmony_ci			goto inst_del_port;
22862306a36Sopenharmony_ci	}
22962306a36Sopenharmony_ci	return 0;
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ciinst_del_port:
23262306a36Sopenharmony_ci	__team_option_inst_del_port(team, port);
23362306a36Sopenharmony_ci	return err;
23462306a36Sopenharmony_ci}
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_cistatic void __team_option_inst_mark_removed_port(struct team *team,
23762306a36Sopenharmony_ci						 struct team_port *port)
23862306a36Sopenharmony_ci{
23962306a36Sopenharmony_ci	struct team_option_inst *opt_inst;
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	list_for_each_entry(opt_inst, &team->option_inst_list, list) {
24262306a36Sopenharmony_ci		if (opt_inst->info.port == port) {
24362306a36Sopenharmony_ci			opt_inst->changed = true;
24462306a36Sopenharmony_ci			opt_inst->removed = true;
24562306a36Sopenharmony_ci		}
24662306a36Sopenharmony_ci	}
24762306a36Sopenharmony_ci}
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_cistatic int __team_options_register(struct team *team,
25062306a36Sopenharmony_ci				   const struct team_option *option,
25162306a36Sopenharmony_ci				   size_t option_count)
25262306a36Sopenharmony_ci{
25362306a36Sopenharmony_ci	int i;
25462306a36Sopenharmony_ci	struct team_option **dst_opts;
25562306a36Sopenharmony_ci	int err;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	dst_opts = kcalloc(option_count, sizeof(struct team_option *),
25862306a36Sopenharmony_ci			   GFP_KERNEL);
25962306a36Sopenharmony_ci	if (!dst_opts)
26062306a36Sopenharmony_ci		return -ENOMEM;
26162306a36Sopenharmony_ci	for (i = 0; i < option_count; i++, option++) {
26262306a36Sopenharmony_ci		if (__team_find_option(team, option->name)) {
26362306a36Sopenharmony_ci			err = -EEXIST;
26462306a36Sopenharmony_ci			goto alloc_rollback;
26562306a36Sopenharmony_ci		}
26662306a36Sopenharmony_ci		dst_opts[i] = kmemdup(option, sizeof(*option), GFP_KERNEL);
26762306a36Sopenharmony_ci		if (!dst_opts[i]) {
26862306a36Sopenharmony_ci			err = -ENOMEM;
26962306a36Sopenharmony_ci			goto alloc_rollback;
27062306a36Sopenharmony_ci		}
27162306a36Sopenharmony_ci	}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	for (i = 0; i < option_count; i++) {
27462306a36Sopenharmony_ci		err = __team_option_inst_add_option(team, dst_opts[i]);
27562306a36Sopenharmony_ci		if (err)
27662306a36Sopenharmony_ci			goto inst_rollback;
27762306a36Sopenharmony_ci		list_add_tail(&dst_opts[i]->list, &team->option_list);
27862306a36Sopenharmony_ci	}
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	kfree(dst_opts);
28162306a36Sopenharmony_ci	return 0;
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ciinst_rollback:
28462306a36Sopenharmony_ci	for (i--; i >= 0; i--) {
28562306a36Sopenharmony_ci		__team_option_inst_del_option(team, dst_opts[i]);
28662306a36Sopenharmony_ci		list_del(&dst_opts[i]->list);
28762306a36Sopenharmony_ci	}
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	i = option_count;
29062306a36Sopenharmony_cialloc_rollback:
29162306a36Sopenharmony_ci	for (i--; i >= 0; i--)
29262306a36Sopenharmony_ci		kfree(dst_opts[i]);
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	kfree(dst_opts);
29562306a36Sopenharmony_ci	return err;
29662306a36Sopenharmony_ci}
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_cistatic void __team_options_mark_removed(struct team *team,
29962306a36Sopenharmony_ci					const struct team_option *option,
30062306a36Sopenharmony_ci					size_t option_count)
30162306a36Sopenharmony_ci{
30262306a36Sopenharmony_ci	int i;
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	for (i = 0; i < option_count; i++, option++) {
30562306a36Sopenharmony_ci		struct team_option *del_opt;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci		del_opt = __team_find_option(team, option->name);
30862306a36Sopenharmony_ci		if (del_opt)
30962306a36Sopenharmony_ci			__team_option_inst_mark_removed_option(team, del_opt);
31062306a36Sopenharmony_ci	}
31162306a36Sopenharmony_ci}
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_cistatic void __team_options_unregister(struct team *team,
31462306a36Sopenharmony_ci				      const struct team_option *option,
31562306a36Sopenharmony_ci				      size_t option_count)
31662306a36Sopenharmony_ci{
31762306a36Sopenharmony_ci	int i;
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	for (i = 0; i < option_count; i++, option++) {
32062306a36Sopenharmony_ci		struct team_option *del_opt;
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci		del_opt = __team_find_option(team, option->name);
32362306a36Sopenharmony_ci		if (del_opt) {
32462306a36Sopenharmony_ci			__team_option_inst_del_option(team, del_opt);
32562306a36Sopenharmony_ci			list_del(&del_opt->list);
32662306a36Sopenharmony_ci			kfree(del_opt);
32762306a36Sopenharmony_ci		}
32862306a36Sopenharmony_ci	}
32962306a36Sopenharmony_ci}
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_cistatic void __team_options_change_check(struct team *team);
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ciint team_options_register(struct team *team,
33462306a36Sopenharmony_ci			  const struct team_option *option,
33562306a36Sopenharmony_ci			  size_t option_count)
33662306a36Sopenharmony_ci{
33762306a36Sopenharmony_ci	int err;
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	err = __team_options_register(team, option, option_count);
34062306a36Sopenharmony_ci	if (err)
34162306a36Sopenharmony_ci		return err;
34262306a36Sopenharmony_ci	__team_options_change_check(team);
34362306a36Sopenharmony_ci	return 0;
34462306a36Sopenharmony_ci}
34562306a36Sopenharmony_ciEXPORT_SYMBOL(team_options_register);
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_civoid team_options_unregister(struct team *team,
34862306a36Sopenharmony_ci			     const struct team_option *option,
34962306a36Sopenharmony_ci			     size_t option_count)
35062306a36Sopenharmony_ci{
35162306a36Sopenharmony_ci	__team_options_mark_removed(team, option, option_count);
35262306a36Sopenharmony_ci	__team_options_change_check(team);
35362306a36Sopenharmony_ci	__team_options_unregister(team, option, option_count);
35462306a36Sopenharmony_ci}
35562306a36Sopenharmony_ciEXPORT_SYMBOL(team_options_unregister);
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_cistatic int team_option_get(struct team *team,
35862306a36Sopenharmony_ci			   struct team_option_inst *opt_inst,
35962306a36Sopenharmony_ci			   struct team_gsetter_ctx *ctx)
36062306a36Sopenharmony_ci{
36162306a36Sopenharmony_ci	if (!opt_inst->option->getter)
36262306a36Sopenharmony_ci		return -EOPNOTSUPP;
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	opt_inst->option->getter(team, ctx);
36562306a36Sopenharmony_ci	return 0;
36662306a36Sopenharmony_ci}
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_cistatic int team_option_set(struct team *team,
36962306a36Sopenharmony_ci			   struct team_option_inst *opt_inst,
37062306a36Sopenharmony_ci			   struct team_gsetter_ctx *ctx)
37162306a36Sopenharmony_ci{
37262306a36Sopenharmony_ci	if (!opt_inst->option->setter)
37362306a36Sopenharmony_ci		return -EOPNOTSUPP;
37462306a36Sopenharmony_ci	return opt_inst->option->setter(team, ctx);
37562306a36Sopenharmony_ci}
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_civoid team_option_inst_set_change(struct team_option_inst_info *opt_inst_info)
37862306a36Sopenharmony_ci{
37962306a36Sopenharmony_ci	struct team_option_inst *opt_inst;
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	opt_inst = container_of(opt_inst_info, struct team_option_inst, info);
38262306a36Sopenharmony_ci	opt_inst->changed = true;
38362306a36Sopenharmony_ci}
38462306a36Sopenharmony_ciEXPORT_SYMBOL(team_option_inst_set_change);
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_civoid team_options_change_check(struct team *team)
38762306a36Sopenharmony_ci{
38862306a36Sopenharmony_ci	__team_options_change_check(team);
38962306a36Sopenharmony_ci}
39062306a36Sopenharmony_ciEXPORT_SYMBOL(team_options_change_check);
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci/****************
39462306a36Sopenharmony_ci * Mode handling
39562306a36Sopenharmony_ci ****************/
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_cistatic LIST_HEAD(mode_list);
39862306a36Sopenharmony_cistatic DEFINE_SPINLOCK(mode_list_lock);
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_cistruct team_mode_item {
40162306a36Sopenharmony_ci	struct list_head list;
40262306a36Sopenharmony_ci	const struct team_mode *mode;
40362306a36Sopenharmony_ci};
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_cistatic struct team_mode_item *__find_mode(const char *kind)
40662306a36Sopenharmony_ci{
40762306a36Sopenharmony_ci	struct team_mode_item *mitem;
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	list_for_each_entry(mitem, &mode_list, list) {
41062306a36Sopenharmony_ci		if (strcmp(mitem->mode->kind, kind) == 0)
41162306a36Sopenharmony_ci			return mitem;
41262306a36Sopenharmony_ci	}
41362306a36Sopenharmony_ci	return NULL;
41462306a36Sopenharmony_ci}
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_cistatic bool is_good_mode_name(const char *name)
41762306a36Sopenharmony_ci{
41862306a36Sopenharmony_ci	while (*name != '\0') {
41962306a36Sopenharmony_ci		if (!isalpha(*name) && !isdigit(*name) && *name != '_')
42062306a36Sopenharmony_ci			return false;
42162306a36Sopenharmony_ci		name++;
42262306a36Sopenharmony_ci	}
42362306a36Sopenharmony_ci	return true;
42462306a36Sopenharmony_ci}
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ciint team_mode_register(const struct team_mode *mode)
42762306a36Sopenharmony_ci{
42862306a36Sopenharmony_ci	int err = 0;
42962306a36Sopenharmony_ci	struct team_mode_item *mitem;
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	if (!is_good_mode_name(mode->kind) ||
43262306a36Sopenharmony_ci	    mode->priv_size > TEAM_MODE_PRIV_SIZE)
43362306a36Sopenharmony_ci		return -EINVAL;
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	mitem = kmalloc(sizeof(*mitem), GFP_KERNEL);
43662306a36Sopenharmony_ci	if (!mitem)
43762306a36Sopenharmony_ci		return -ENOMEM;
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	spin_lock(&mode_list_lock);
44062306a36Sopenharmony_ci	if (__find_mode(mode->kind)) {
44162306a36Sopenharmony_ci		err = -EEXIST;
44262306a36Sopenharmony_ci		kfree(mitem);
44362306a36Sopenharmony_ci		goto unlock;
44462306a36Sopenharmony_ci	}
44562306a36Sopenharmony_ci	mitem->mode = mode;
44662306a36Sopenharmony_ci	list_add_tail(&mitem->list, &mode_list);
44762306a36Sopenharmony_ciunlock:
44862306a36Sopenharmony_ci	spin_unlock(&mode_list_lock);
44962306a36Sopenharmony_ci	return err;
45062306a36Sopenharmony_ci}
45162306a36Sopenharmony_ciEXPORT_SYMBOL(team_mode_register);
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_civoid team_mode_unregister(const struct team_mode *mode)
45462306a36Sopenharmony_ci{
45562306a36Sopenharmony_ci	struct team_mode_item *mitem;
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	spin_lock(&mode_list_lock);
45862306a36Sopenharmony_ci	mitem = __find_mode(mode->kind);
45962306a36Sopenharmony_ci	if (mitem) {
46062306a36Sopenharmony_ci		list_del_init(&mitem->list);
46162306a36Sopenharmony_ci		kfree(mitem);
46262306a36Sopenharmony_ci	}
46362306a36Sopenharmony_ci	spin_unlock(&mode_list_lock);
46462306a36Sopenharmony_ci}
46562306a36Sopenharmony_ciEXPORT_SYMBOL(team_mode_unregister);
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_cistatic const struct team_mode *team_mode_get(const char *kind)
46862306a36Sopenharmony_ci{
46962306a36Sopenharmony_ci	struct team_mode_item *mitem;
47062306a36Sopenharmony_ci	const struct team_mode *mode = NULL;
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	if (!try_module_get(THIS_MODULE))
47362306a36Sopenharmony_ci		return NULL;
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	spin_lock(&mode_list_lock);
47662306a36Sopenharmony_ci	mitem = __find_mode(kind);
47762306a36Sopenharmony_ci	if (!mitem) {
47862306a36Sopenharmony_ci		spin_unlock(&mode_list_lock);
47962306a36Sopenharmony_ci		request_module("team-mode-%s", kind);
48062306a36Sopenharmony_ci		spin_lock(&mode_list_lock);
48162306a36Sopenharmony_ci		mitem = __find_mode(kind);
48262306a36Sopenharmony_ci	}
48362306a36Sopenharmony_ci	if (mitem) {
48462306a36Sopenharmony_ci		mode = mitem->mode;
48562306a36Sopenharmony_ci		if (!try_module_get(mode->owner))
48662306a36Sopenharmony_ci			mode = NULL;
48762306a36Sopenharmony_ci	}
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	spin_unlock(&mode_list_lock);
49062306a36Sopenharmony_ci	module_put(THIS_MODULE);
49162306a36Sopenharmony_ci	return mode;
49262306a36Sopenharmony_ci}
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_cistatic void team_mode_put(const struct team_mode *mode)
49562306a36Sopenharmony_ci{
49662306a36Sopenharmony_ci	module_put(mode->owner);
49762306a36Sopenharmony_ci}
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_cistatic bool team_dummy_transmit(struct team *team, struct sk_buff *skb)
50062306a36Sopenharmony_ci{
50162306a36Sopenharmony_ci	dev_kfree_skb_any(skb);
50262306a36Sopenharmony_ci	return false;
50362306a36Sopenharmony_ci}
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_cistatic rx_handler_result_t team_dummy_receive(struct team *team,
50662306a36Sopenharmony_ci					      struct team_port *port,
50762306a36Sopenharmony_ci					      struct sk_buff *skb)
50862306a36Sopenharmony_ci{
50962306a36Sopenharmony_ci	return RX_HANDLER_ANOTHER;
51062306a36Sopenharmony_ci}
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_cistatic const struct team_mode __team_no_mode = {
51362306a36Sopenharmony_ci	.kind		= "*NOMODE*",
51462306a36Sopenharmony_ci};
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_cistatic bool team_is_mode_set(struct team *team)
51762306a36Sopenharmony_ci{
51862306a36Sopenharmony_ci	return team->mode != &__team_no_mode;
51962306a36Sopenharmony_ci}
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_cistatic void team_set_no_mode(struct team *team)
52262306a36Sopenharmony_ci{
52362306a36Sopenharmony_ci	team->user_carrier_enabled = false;
52462306a36Sopenharmony_ci	team->mode = &__team_no_mode;
52562306a36Sopenharmony_ci}
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_cistatic void team_adjust_ops(struct team *team)
52862306a36Sopenharmony_ci{
52962306a36Sopenharmony_ci	/*
53062306a36Sopenharmony_ci	 * To avoid checks in rx/tx skb paths, ensure here that non-null and
53162306a36Sopenharmony_ci	 * correct ops are always set.
53262306a36Sopenharmony_ci	 */
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	if (!team->en_port_count || !team_is_mode_set(team) ||
53562306a36Sopenharmony_ci	    !team->mode->ops->transmit)
53662306a36Sopenharmony_ci		team->ops.transmit = team_dummy_transmit;
53762306a36Sopenharmony_ci	else
53862306a36Sopenharmony_ci		team->ops.transmit = team->mode->ops->transmit;
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	if (!team->en_port_count || !team_is_mode_set(team) ||
54162306a36Sopenharmony_ci	    !team->mode->ops->receive)
54262306a36Sopenharmony_ci		team->ops.receive = team_dummy_receive;
54362306a36Sopenharmony_ci	else
54462306a36Sopenharmony_ci		team->ops.receive = team->mode->ops->receive;
54562306a36Sopenharmony_ci}
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci/*
54862306a36Sopenharmony_ci * We can benefit from the fact that it's ensured no port is present
54962306a36Sopenharmony_ci * at the time of mode change. Therefore no packets are in fly so there's no
55062306a36Sopenharmony_ci * need to set mode operations in any special way.
55162306a36Sopenharmony_ci */
55262306a36Sopenharmony_cistatic int __team_change_mode(struct team *team,
55362306a36Sopenharmony_ci			      const struct team_mode *new_mode)
55462306a36Sopenharmony_ci{
55562306a36Sopenharmony_ci	/* Check if mode was previously set and do cleanup if so */
55662306a36Sopenharmony_ci	if (team_is_mode_set(team)) {
55762306a36Sopenharmony_ci		void (*exit_op)(struct team *team) = team->ops.exit;
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci		/* Clear ops area so no callback is called any longer */
56062306a36Sopenharmony_ci		memset(&team->ops, 0, sizeof(struct team_mode_ops));
56162306a36Sopenharmony_ci		team_adjust_ops(team);
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci		if (exit_op)
56462306a36Sopenharmony_ci			exit_op(team);
56562306a36Sopenharmony_ci		team_mode_put(team->mode);
56662306a36Sopenharmony_ci		team_set_no_mode(team);
56762306a36Sopenharmony_ci		/* zero private data area */
56862306a36Sopenharmony_ci		memset(&team->mode_priv, 0,
56962306a36Sopenharmony_ci		       sizeof(struct team) - offsetof(struct team, mode_priv));
57062306a36Sopenharmony_ci	}
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	if (!new_mode)
57362306a36Sopenharmony_ci		return 0;
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	if (new_mode->ops->init) {
57662306a36Sopenharmony_ci		int err;
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci		err = new_mode->ops->init(team);
57962306a36Sopenharmony_ci		if (err)
58062306a36Sopenharmony_ci			return err;
58162306a36Sopenharmony_ci	}
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	team->mode = new_mode;
58462306a36Sopenharmony_ci	memcpy(&team->ops, new_mode->ops, sizeof(struct team_mode_ops));
58562306a36Sopenharmony_ci	team_adjust_ops(team);
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	return 0;
58862306a36Sopenharmony_ci}
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_cistatic int team_change_mode(struct team *team, const char *kind)
59162306a36Sopenharmony_ci{
59262306a36Sopenharmony_ci	const struct team_mode *new_mode;
59362306a36Sopenharmony_ci	struct net_device *dev = team->dev;
59462306a36Sopenharmony_ci	int err;
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	if (!list_empty(&team->port_list)) {
59762306a36Sopenharmony_ci		netdev_err(dev, "No ports can be present during mode change\n");
59862306a36Sopenharmony_ci		return -EBUSY;
59962306a36Sopenharmony_ci	}
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	if (team_is_mode_set(team) && strcmp(team->mode->kind, kind) == 0) {
60262306a36Sopenharmony_ci		netdev_err(dev, "Unable to change to the same mode the team is in\n");
60362306a36Sopenharmony_ci		return -EINVAL;
60462306a36Sopenharmony_ci	}
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	new_mode = team_mode_get(kind);
60762306a36Sopenharmony_ci	if (!new_mode) {
60862306a36Sopenharmony_ci		netdev_err(dev, "Mode \"%s\" not found\n", kind);
60962306a36Sopenharmony_ci		return -EINVAL;
61062306a36Sopenharmony_ci	}
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci	err = __team_change_mode(team, new_mode);
61362306a36Sopenharmony_ci	if (err) {
61462306a36Sopenharmony_ci		netdev_err(dev, "Failed to change to mode \"%s\"\n", kind);
61562306a36Sopenharmony_ci		team_mode_put(new_mode);
61662306a36Sopenharmony_ci		return err;
61762306a36Sopenharmony_ci	}
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	netdev_info(dev, "Mode changed to \"%s\"\n", kind);
62062306a36Sopenharmony_ci	return 0;
62162306a36Sopenharmony_ci}
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci/*********************
62562306a36Sopenharmony_ci * Peers notification
62662306a36Sopenharmony_ci *********************/
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_cistatic void team_notify_peers_work(struct work_struct *work)
62962306a36Sopenharmony_ci{
63062306a36Sopenharmony_ci	struct team *team;
63162306a36Sopenharmony_ci	int val;
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	team = container_of(work, struct team, notify_peers.dw.work);
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci	if (!rtnl_trylock()) {
63662306a36Sopenharmony_ci		schedule_delayed_work(&team->notify_peers.dw, 0);
63762306a36Sopenharmony_ci		return;
63862306a36Sopenharmony_ci	}
63962306a36Sopenharmony_ci	val = atomic_dec_if_positive(&team->notify_peers.count_pending);
64062306a36Sopenharmony_ci	if (val < 0) {
64162306a36Sopenharmony_ci		rtnl_unlock();
64262306a36Sopenharmony_ci		return;
64362306a36Sopenharmony_ci	}
64462306a36Sopenharmony_ci	call_netdevice_notifiers(NETDEV_NOTIFY_PEERS, team->dev);
64562306a36Sopenharmony_ci	rtnl_unlock();
64662306a36Sopenharmony_ci	if (val)
64762306a36Sopenharmony_ci		schedule_delayed_work(&team->notify_peers.dw,
64862306a36Sopenharmony_ci				      msecs_to_jiffies(team->notify_peers.interval));
64962306a36Sopenharmony_ci}
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_cistatic void team_notify_peers(struct team *team)
65262306a36Sopenharmony_ci{
65362306a36Sopenharmony_ci	if (!team->notify_peers.count || !netif_running(team->dev))
65462306a36Sopenharmony_ci		return;
65562306a36Sopenharmony_ci	atomic_add(team->notify_peers.count, &team->notify_peers.count_pending);
65662306a36Sopenharmony_ci	schedule_delayed_work(&team->notify_peers.dw, 0);
65762306a36Sopenharmony_ci}
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_cistatic void team_notify_peers_init(struct team *team)
66062306a36Sopenharmony_ci{
66162306a36Sopenharmony_ci	INIT_DELAYED_WORK(&team->notify_peers.dw, team_notify_peers_work);
66262306a36Sopenharmony_ci}
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_cistatic void team_notify_peers_fini(struct team *team)
66562306a36Sopenharmony_ci{
66662306a36Sopenharmony_ci	cancel_delayed_work_sync(&team->notify_peers.dw);
66762306a36Sopenharmony_ci}
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci/*******************************
67162306a36Sopenharmony_ci * Send multicast group rejoins
67262306a36Sopenharmony_ci *******************************/
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_cistatic void team_mcast_rejoin_work(struct work_struct *work)
67562306a36Sopenharmony_ci{
67662306a36Sopenharmony_ci	struct team *team;
67762306a36Sopenharmony_ci	int val;
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	team = container_of(work, struct team, mcast_rejoin.dw.work);
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	if (!rtnl_trylock()) {
68262306a36Sopenharmony_ci		schedule_delayed_work(&team->mcast_rejoin.dw, 0);
68362306a36Sopenharmony_ci		return;
68462306a36Sopenharmony_ci	}
68562306a36Sopenharmony_ci	val = atomic_dec_if_positive(&team->mcast_rejoin.count_pending);
68662306a36Sopenharmony_ci	if (val < 0) {
68762306a36Sopenharmony_ci		rtnl_unlock();
68862306a36Sopenharmony_ci		return;
68962306a36Sopenharmony_ci	}
69062306a36Sopenharmony_ci	call_netdevice_notifiers(NETDEV_RESEND_IGMP, team->dev);
69162306a36Sopenharmony_ci	rtnl_unlock();
69262306a36Sopenharmony_ci	if (val)
69362306a36Sopenharmony_ci		schedule_delayed_work(&team->mcast_rejoin.dw,
69462306a36Sopenharmony_ci				      msecs_to_jiffies(team->mcast_rejoin.interval));
69562306a36Sopenharmony_ci}
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_cistatic void team_mcast_rejoin(struct team *team)
69862306a36Sopenharmony_ci{
69962306a36Sopenharmony_ci	if (!team->mcast_rejoin.count || !netif_running(team->dev))
70062306a36Sopenharmony_ci		return;
70162306a36Sopenharmony_ci	atomic_add(team->mcast_rejoin.count, &team->mcast_rejoin.count_pending);
70262306a36Sopenharmony_ci	schedule_delayed_work(&team->mcast_rejoin.dw, 0);
70362306a36Sopenharmony_ci}
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_cistatic void team_mcast_rejoin_init(struct team *team)
70662306a36Sopenharmony_ci{
70762306a36Sopenharmony_ci	INIT_DELAYED_WORK(&team->mcast_rejoin.dw, team_mcast_rejoin_work);
70862306a36Sopenharmony_ci}
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_cistatic void team_mcast_rejoin_fini(struct team *team)
71162306a36Sopenharmony_ci{
71262306a36Sopenharmony_ci	cancel_delayed_work_sync(&team->mcast_rejoin.dw);
71362306a36Sopenharmony_ci}
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci/************************
71762306a36Sopenharmony_ci * Rx path frame handler
71862306a36Sopenharmony_ci ************************/
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci/* note: already called with rcu_read_lock */
72162306a36Sopenharmony_cistatic rx_handler_result_t team_handle_frame(struct sk_buff **pskb)
72262306a36Sopenharmony_ci{
72362306a36Sopenharmony_ci	struct sk_buff *skb = *pskb;
72462306a36Sopenharmony_ci	struct team_port *port;
72562306a36Sopenharmony_ci	struct team *team;
72662306a36Sopenharmony_ci	rx_handler_result_t res;
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	skb = skb_share_check(skb, GFP_ATOMIC);
72962306a36Sopenharmony_ci	if (!skb)
73062306a36Sopenharmony_ci		return RX_HANDLER_CONSUMED;
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci	*pskb = skb;
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci	port = team_port_get_rcu(skb->dev);
73562306a36Sopenharmony_ci	team = port->team;
73662306a36Sopenharmony_ci	if (!team_port_enabled(port)) {
73762306a36Sopenharmony_ci		if (is_link_local_ether_addr(eth_hdr(skb)->h_dest))
73862306a36Sopenharmony_ci			/* link-local packets are mostly useful when stack receives them
73962306a36Sopenharmony_ci			 * with the link they arrive on.
74062306a36Sopenharmony_ci			 */
74162306a36Sopenharmony_ci			return RX_HANDLER_PASS;
74262306a36Sopenharmony_ci		/* allow exact match delivery for disabled ports */
74362306a36Sopenharmony_ci		res = RX_HANDLER_EXACT;
74462306a36Sopenharmony_ci	} else {
74562306a36Sopenharmony_ci		res = team->ops.receive(team, port, skb);
74662306a36Sopenharmony_ci	}
74762306a36Sopenharmony_ci	if (res == RX_HANDLER_ANOTHER) {
74862306a36Sopenharmony_ci		struct team_pcpu_stats *pcpu_stats;
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci		pcpu_stats = this_cpu_ptr(team->pcpu_stats);
75162306a36Sopenharmony_ci		u64_stats_update_begin(&pcpu_stats->syncp);
75262306a36Sopenharmony_ci		u64_stats_inc(&pcpu_stats->rx_packets);
75362306a36Sopenharmony_ci		u64_stats_add(&pcpu_stats->rx_bytes, skb->len);
75462306a36Sopenharmony_ci		if (skb->pkt_type == PACKET_MULTICAST)
75562306a36Sopenharmony_ci			u64_stats_inc(&pcpu_stats->rx_multicast);
75662306a36Sopenharmony_ci		u64_stats_update_end(&pcpu_stats->syncp);
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci		skb->dev = team->dev;
75962306a36Sopenharmony_ci	} else if (res == RX_HANDLER_EXACT) {
76062306a36Sopenharmony_ci		this_cpu_inc(team->pcpu_stats->rx_nohandler);
76162306a36Sopenharmony_ci	} else {
76262306a36Sopenharmony_ci		this_cpu_inc(team->pcpu_stats->rx_dropped);
76362306a36Sopenharmony_ci	}
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci	return res;
76662306a36Sopenharmony_ci}
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci/*************************************
77062306a36Sopenharmony_ci * Multiqueue Tx port select override
77162306a36Sopenharmony_ci *************************************/
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_cistatic int team_queue_override_init(struct team *team)
77462306a36Sopenharmony_ci{
77562306a36Sopenharmony_ci	struct list_head *listarr;
77662306a36Sopenharmony_ci	unsigned int queue_cnt = team->dev->num_tx_queues - 1;
77762306a36Sopenharmony_ci	unsigned int i;
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci	if (!queue_cnt)
78062306a36Sopenharmony_ci		return 0;
78162306a36Sopenharmony_ci	listarr = kmalloc_array(queue_cnt, sizeof(struct list_head),
78262306a36Sopenharmony_ci				GFP_KERNEL);
78362306a36Sopenharmony_ci	if (!listarr)
78462306a36Sopenharmony_ci		return -ENOMEM;
78562306a36Sopenharmony_ci	team->qom_lists = listarr;
78662306a36Sopenharmony_ci	for (i = 0; i < queue_cnt; i++)
78762306a36Sopenharmony_ci		INIT_LIST_HEAD(listarr++);
78862306a36Sopenharmony_ci	return 0;
78962306a36Sopenharmony_ci}
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_cistatic void team_queue_override_fini(struct team *team)
79262306a36Sopenharmony_ci{
79362306a36Sopenharmony_ci	kfree(team->qom_lists);
79462306a36Sopenharmony_ci}
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_cistatic struct list_head *__team_get_qom_list(struct team *team, u16 queue_id)
79762306a36Sopenharmony_ci{
79862306a36Sopenharmony_ci	return &team->qom_lists[queue_id - 1];
79962306a36Sopenharmony_ci}
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_ci/*
80262306a36Sopenharmony_ci * note: already called with rcu_read_lock
80362306a36Sopenharmony_ci */
80462306a36Sopenharmony_cistatic bool team_queue_override_transmit(struct team *team, struct sk_buff *skb)
80562306a36Sopenharmony_ci{
80662306a36Sopenharmony_ci	struct list_head *qom_list;
80762306a36Sopenharmony_ci	struct team_port *port;
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci	if (!team->queue_override_enabled || !skb->queue_mapping)
81062306a36Sopenharmony_ci		return false;
81162306a36Sopenharmony_ci	qom_list = __team_get_qom_list(team, skb->queue_mapping);
81262306a36Sopenharmony_ci	list_for_each_entry_rcu(port, qom_list, qom_list) {
81362306a36Sopenharmony_ci		if (!team_dev_queue_xmit(team, port, skb))
81462306a36Sopenharmony_ci			return true;
81562306a36Sopenharmony_ci	}
81662306a36Sopenharmony_ci	return false;
81762306a36Sopenharmony_ci}
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_cistatic void __team_queue_override_port_del(struct team *team,
82062306a36Sopenharmony_ci					   struct team_port *port)
82162306a36Sopenharmony_ci{
82262306a36Sopenharmony_ci	if (!port->queue_id)
82362306a36Sopenharmony_ci		return;
82462306a36Sopenharmony_ci	list_del_rcu(&port->qom_list);
82562306a36Sopenharmony_ci}
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_cistatic bool team_queue_override_port_has_gt_prio_than(struct team_port *port,
82862306a36Sopenharmony_ci						      struct team_port *cur)
82962306a36Sopenharmony_ci{
83062306a36Sopenharmony_ci	if (port->priority < cur->priority)
83162306a36Sopenharmony_ci		return true;
83262306a36Sopenharmony_ci	if (port->priority > cur->priority)
83362306a36Sopenharmony_ci		return false;
83462306a36Sopenharmony_ci	if (port->index < cur->index)
83562306a36Sopenharmony_ci		return true;
83662306a36Sopenharmony_ci	return false;
83762306a36Sopenharmony_ci}
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_cistatic void __team_queue_override_port_add(struct team *team,
84062306a36Sopenharmony_ci					   struct team_port *port)
84162306a36Sopenharmony_ci{
84262306a36Sopenharmony_ci	struct team_port *cur;
84362306a36Sopenharmony_ci	struct list_head *qom_list;
84462306a36Sopenharmony_ci	struct list_head *node;
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	if (!port->queue_id)
84762306a36Sopenharmony_ci		return;
84862306a36Sopenharmony_ci	qom_list = __team_get_qom_list(team, port->queue_id);
84962306a36Sopenharmony_ci	node = qom_list;
85062306a36Sopenharmony_ci	list_for_each_entry(cur, qom_list, qom_list) {
85162306a36Sopenharmony_ci		if (team_queue_override_port_has_gt_prio_than(port, cur))
85262306a36Sopenharmony_ci			break;
85362306a36Sopenharmony_ci		node = &cur->qom_list;
85462306a36Sopenharmony_ci	}
85562306a36Sopenharmony_ci	list_add_tail_rcu(&port->qom_list, node);
85662306a36Sopenharmony_ci}
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_cistatic void __team_queue_override_enabled_check(struct team *team)
85962306a36Sopenharmony_ci{
86062306a36Sopenharmony_ci	struct team_port *port;
86162306a36Sopenharmony_ci	bool enabled = false;
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_ci	list_for_each_entry(port, &team->port_list, list) {
86462306a36Sopenharmony_ci		if (port->queue_id) {
86562306a36Sopenharmony_ci			enabled = true;
86662306a36Sopenharmony_ci			break;
86762306a36Sopenharmony_ci		}
86862306a36Sopenharmony_ci	}
86962306a36Sopenharmony_ci	if (enabled == team->queue_override_enabled)
87062306a36Sopenharmony_ci		return;
87162306a36Sopenharmony_ci	netdev_dbg(team->dev, "%s queue override\n",
87262306a36Sopenharmony_ci		   enabled ? "Enabling" : "Disabling");
87362306a36Sopenharmony_ci	team->queue_override_enabled = enabled;
87462306a36Sopenharmony_ci}
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_cistatic void team_queue_override_port_prio_changed(struct team *team,
87762306a36Sopenharmony_ci						  struct team_port *port)
87862306a36Sopenharmony_ci{
87962306a36Sopenharmony_ci	if (!port->queue_id || team_port_enabled(port))
88062306a36Sopenharmony_ci		return;
88162306a36Sopenharmony_ci	__team_queue_override_port_del(team, port);
88262306a36Sopenharmony_ci	__team_queue_override_port_add(team, port);
88362306a36Sopenharmony_ci	__team_queue_override_enabled_check(team);
88462306a36Sopenharmony_ci}
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_cistatic void team_queue_override_port_change_queue_id(struct team *team,
88762306a36Sopenharmony_ci						     struct team_port *port,
88862306a36Sopenharmony_ci						     u16 new_queue_id)
88962306a36Sopenharmony_ci{
89062306a36Sopenharmony_ci	if (team_port_enabled(port)) {
89162306a36Sopenharmony_ci		__team_queue_override_port_del(team, port);
89262306a36Sopenharmony_ci		port->queue_id = new_queue_id;
89362306a36Sopenharmony_ci		__team_queue_override_port_add(team, port);
89462306a36Sopenharmony_ci		__team_queue_override_enabled_check(team);
89562306a36Sopenharmony_ci	} else {
89662306a36Sopenharmony_ci		port->queue_id = new_queue_id;
89762306a36Sopenharmony_ci	}
89862306a36Sopenharmony_ci}
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_cistatic void team_queue_override_port_add(struct team *team,
90162306a36Sopenharmony_ci					 struct team_port *port)
90262306a36Sopenharmony_ci{
90362306a36Sopenharmony_ci	__team_queue_override_port_add(team, port);
90462306a36Sopenharmony_ci	__team_queue_override_enabled_check(team);
90562306a36Sopenharmony_ci}
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_cistatic void team_queue_override_port_del(struct team *team,
90862306a36Sopenharmony_ci					 struct team_port *port)
90962306a36Sopenharmony_ci{
91062306a36Sopenharmony_ci	__team_queue_override_port_del(team, port);
91162306a36Sopenharmony_ci	__team_queue_override_enabled_check(team);
91262306a36Sopenharmony_ci}
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_ci/****************
91662306a36Sopenharmony_ci * Port handling
91762306a36Sopenharmony_ci ****************/
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_cistatic bool team_port_find(const struct team *team,
92062306a36Sopenharmony_ci			   const struct team_port *port)
92162306a36Sopenharmony_ci{
92262306a36Sopenharmony_ci	struct team_port *cur;
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_ci	list_for_each_entry(cur, &team->port_list, list)
92562306a36Sopenharmony_ci		if (cur == port)
92662306a36Sopenharmony_ci			return true;
92762306a36Sopenharmony_ci	return false;
92862306a36Sopenharmony_ci}
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci/*
93162306a36Sopenharmony_ci * Enable/disable port by adding to enabled port hashlist and setting
93262306a36Sopenharmony_ci * port->index (Might be racy so reader could see incorrect ifindex when
93362306a36Sopenharmony_ci * processing a flying packet, but that is not a problem). Write guarded
93462306a36Sopenharmony_ci * by team->lock.
93562306a36Sopenharmony_ci */
93662306a36Sopenharmony_cistatic void team_port_enable(struct team *team,
93762306a36Sopenharmony_ci			     struct team_port *port)
93862306a36Sopenharmony_ci{
93962306a36Sopenharmony_ci	if (team_port_enabled(port))
94062306a36Sopenharmony_ci		return;
94162306a36Sopenharmony_ci	port->index = team->en_port_count++;
94262306a36Sopenharmony_ci	hlist_add_head_rcu(&port->hlist,
94362306a36Sopenharmony_ci			   team_port_index_hash(team, port->index));
94462306a36Sopenharmony_ci	team_adjust_ops(team);
94562306a36Sopenharmony_ci	team_queue_override_port_add(team, port);
94662306a36Sopenharmony_ci	if (team->ops.port_enabled)
94762306a36Sopenharmony_ci		team->ops.port_enabled(team, port);
94862306a36Sopenharmony_ci	team_notify_peers(team);
94962306a36Sopenharmony_ci	team_mcast_rejoin(team);
95062306a36Sopenharmony_ci	team_lower_state_changed(port);
95162306a36Sopenharmony_ci}
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_cistatic void __reconstruct_port_hlist(struct team *team, int rm_index)
95462306a36Sopenharmony_ci{
95562306a36Sopenharmony_ci	int i;
95662306a36Sopenharmony_ci	struct team_port *port;
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci	for (i = rm_index + 1; i < team->en_port_count; i++) {
95962306a36Sopenharmony_ci		port = team_get_port_by_index(team, i);
96062306a36Sopenharmony_ci		hlist_del_rcu(&port->hlist);
96162306a36Sopenharmony_ci		port->index--;
96262306a36Sopenharmony_ci		hlist_add_head_rcu(&port->hlist,
96362306a36Sopenharmony_ci				   team_port_index_hash(team, port->index));
96462306a36Sopenharmony_ci	}
96562306a36Sopenharmony_ci}
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_cistatic void team_port_disable(struct team *team,
96862306a36Sopenharmony_ci			      struct team_port *port)
96962306a36Sopenharmony_ci{
97062306a36Sopenharmony_ci	if (!team_port_enabled(port))
97162306a36Sopenharmony_ci		return;
97262306a36Sopenharmony_ci	if (team->ops.port_disabled)
97362306a36Sopenharmony_ci		team->ops.port_disabled(team, port);
97462306a36Sopenharmony_ci	hlist_del_rcu(&port->hlist);
97562306a36Sopenharmony_ci	__reconstruct_port_hlist(team, port->index);
97662306a36Sopenharmony_ci	port->index = -1;
97762306a36Sopenharmony_ci	team->en_port_count--;
97862306a36Sopenharmony_ci	team_queue_override_port_del(team, port);
97962306a36Sopenharmony_ci	team_adjust_ops(team);
98062306a36Sopenharmony_ci	team_lower_state_changed(port);
98162306a36Sopenharmony_ci}
98262306a36Sopenharmony_ci
98362306a36Sopenharmony_ci#define TEAM_VLAN_FEATURES (NETIF_F_HW_CSUM | NETIF_F_SG | \
98462306a36Sopenharmony_ci			    NETIF_F_FRAGLIST | NETIF_F_GSO_SOFTWARE | \
98562306a36Sopenharmony_ci			    NETIF_F_HIGHDMA | NETIF_F_LRO)
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_ci#define TEAM_ENC_FEATURES	(NETIF_F_HW_CSUM | NETIF_F_SG | \
98862306a36Sopenharmony_ci				 NETIF_F_RXCSUM | NETIF_F_GSO_SOFTWARE)
98962306a36Sopenharmony_ci
99062306a36Sopenharmony_cistatic void __team_compute_features(struct team *team)
99162306a36Sopenharmony_ci{
99262306a36Sopenharmony_ci	struct team_port *port;
99362306a36Sopenharmony_ci	netdev_features_t vlan_features = TEAM_VLAN_FEATURES &
99462306a36Sopenharmony_ci					  NETIF_F_ALL_FOR_ALL;
99562306a36Sopenharmony_ci	netdev_features_t enc_features  = TEAM_ENC_FEATURES;
99662306a36Sopenharmony_ci	unsigned short max_hard_header_len = ETH_HLEN;
99762306a36Sopenharmony_ci	unsigned int dst_release_flag = IFF_XMIT_DST_RELEASE |
99862306a36Sopenharmony_ci					IFF_XMIT_DST_RELEASE_PERM;
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_ci	rcu_read_lock();
100162306a36Sopenharmony_ci	list_for_each_entry_rcu(port, &team->port_list, list) {
100262306a36Sopenharmony_ci		vlan_features = netdev_increment_features(vlan_features,
100362306a36Sopenharmony_ci					port->dev->vlan_features,
100462306a36Sopenharmony_ci					TEAM_VLAN_FEATURES);
100562306a36Sopenharmony_ci		enc_features =
100662306a36Sopenharmony_ci			netdev_increment_features(enc_features,
100762306a36Sopenharmony_ci						  port->dev->hw_enc_features,
100862306a36Sopenharmony_ci						  TEAM_ENC_FEATURES);
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_ci		dst_release_flag &= port->dev->priv_flags;
101262306a36Sopenharmony_ci		if (port->dev->hard_header_len > max_hard_header_len)
101362306a36Sopenharmony_ci			max_hard_header_len = port->dev->hard_header_len;
101462306a36Sopenharmony_ci	}
101562306a36Sopenharmony_ci	rcu_read_unlock();
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_ci	team->dev->vlan_features = vlan_features;
101862306a36Sopenharmony_ci	team->dev->hw_enc_features = enc_features | NETIF_F_GSO_ENCAP_ALL |
101962306a36Sopenharmony_ci				     NETIF_F_HW_VLAN_CTAG_TX |
102062306a36Sopenharmony_ci				     NETIF_F_HW_VLAN_STAG_TX;
102162306a36Sopenharmony_ci	team->dev->hard_header_len = max_hard_header_len;
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci	team->dev->priv_flags &= ~IFF_XMIT_DST_RELEASE;
102462306a36Sopenharmony_ci	if (dst_release_flag == (IFF_XMIT_DST_RELEASE | IFF_XMIT_DST_RELEASE_PERM))
102562306a36Sopenharmony_ci		team->dev->priv_flags |= IFF_XMIT_DST_RELEASE;
102662306a36Sopenharmony_ci}
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_cistatic void team_compute_features(struct team *team)
102962306a36Sopenharmony_ci{
103062306a36Sopenharmony_ci	__team_compute_features(team);
103162306a36Sopenharmony_ci	netdev_change_features(team->dev);
103262306a36Sopenharmony_ci}
103362306a36Sopenharmony_ci
103462306a36Sopenharmony_cistatic int team_port_enter(struct team *team, struct team_port *port)
103562306a36Sopenharmony_ci{
103662306a36Sopenharmony_ci	int err = 0;
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci	dev_hold(team->dev);
103962306a36Sopenharmony_ci	if (team->ops.port_enter) {
104062306a36Sopenharmony_ci		err = team->ops.port_enter(team, port);
104162306a36Sopenharmony_ci		if (err) {
104262306a36Sopenharmony_ci			netdev_err(team->dev, "Device %s failed to enter team mode\n",
104362306a36Sopenharmony_ci				   port->dev->name);
104462306a36Sopenharmony_ci			goto err_port_enter;
104562306a36Sopenharmony_ci		}
104662306a36Sopenharmony_ci	}
104762306a36Sopenharmony_ci
104862306a36Sopenharmony_ci	return 0;
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_cierr_port_enter:
105162306a36Sopenharmony_ci	dev_put(team->dev);
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_ci	return err;
105462306a36Sopenharmony_ci}
105562306a36Sopenharmony_ci
105662306a36Sopenharmony_cistatic void team_port_leave(struct team *team, struct team_port *port)
105762306a36Sopenharmony_ci{
105862306a36Sopenharmony_ci	if (team->ops.port_leave)
105962306a36Sopenharmony_ci		team->ops.port_leave(team, port);
106062306a36Sopenharmony_ci	dev_put(team->dev);
106162306a36Sopenharmony_ci}
106262306a36Sopenharmony_ci
106362306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER
106462306a36Sopenharmony_cistatic int __team_port_enable_netpoll(struct team_port *port)
106562306a36Sopenharmony_ci{
106662306a36Sopenharmony_ci	struct netpoll *np;
106762306a36Sopenharmony_ci	int err;
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_ci	np = kzalloc(sizeof(*np), GFP_KERNEL);
107062306a36Sopenharmony_ci	if (!np)
107162306a36Sopenharmony_ci		return -ENOMEM;
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_ci	err = __netpoll_setup(np, port->dev);
107462306a36Sopenharmony_ci	if (err) {
107562306a36Sopenharmony_ci		kfree(np);
107662306a36Sopenharmony_ci		return err;
107762306a36Sopenharmony_ci	}
107862306a36Sopenharmony_ci	port->np = np;
107962306a36Sopenharmony_ci	return err;
108062306a36Sopenharmony_ci}
108162306a36Sopenharmony_ci
108262306a36Sopenharmony_cistatic int team_port_enable_netpoll(struct team_port *port)
108362306a36Sopenharmony_ci{
108462306a36Sopenharmony_ci	if (!port->team->dev->npinfo)
108562306a36Sopenharmony_ci		return 0;
108662306a36Sopenharmony_ci
108762306a36Sopenharmony_ci	return __team_port_enable_netpoll(port);
108862306a36Sopenharmony_ci}
108962306a36Sopenharmony_ci
109062306a36Sopenharmony_cistatic void team_port_disable_netpoll(struct team_port *port)
109162306a36Sopenharmony_ci{
109262306a36Sopenharmony_ci	struct netpoll *np = port->np;
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_ci	if (!np)
109562306a36Sopenharmony_ci		return;
109662306a36Sopenharmony_ci	port->np = NULL;
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci	__netpoll_free(np);
109962306a36Sopenharmony_ci}
110062306a36Sopenharmony_ci#else
110162306a36Sopenharmony_cistatic int team_port_enable_netpoll(struct team_port *port)
110262306a36Sopenharmony_ci{
110362306a36Sopenharmony_ci	return 0;
110462306a36Sopenharmony_ci}
110562306a36Sopenharmony_cistatic void team_port_disable_netpoll(struct team_port *port)
110662306a36Sopenharmony_ci{
110762306a36Sopenharmony_ci}
110862306a36Sopenharmony_ci#endif
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_cistatic int team_upper_dev_link(struct team *team, struct team_port *port,
111162306a36Sopenharmony_ci			       struct netlink_ext_ack *extack)
111262306a36Sopenharmony_ci{
111362306a36Sopenharmony_ci	struct netdev_lag_upper_info lag_upper_info;
111462306a36Sopenharmony_ci	int err;
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_ci	lag_upper_info.tx_type = team->mode->lag_tx_type;
111762306a36Sopenharmony_ci	lag_upper_info.hash_type = NETDEV_LAG_HASH_UNKNOWN;
111862306a36Sopenharmony_ci	err = netdev_master_upper_dev_link(port->dev, team->dev, NULL,
111962306a36Sopenharmony_ci					   &lag_upper_info, extack);
112062306a36Sopenharmony_ci	if (err)
112162306a36Sopenharmony_ci		return err;
112262306a36Sopenharmony_ci	port->dev->priv_flags |= IFF_TEAM_PORT;
112362306a36Sopenharmony_ci	return 0;
112462306a36Sopenharmony_ci}
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_cistatic void team_upper_dev_unlink(struct team *team, struct team_port *port)
112762306a36Sopenharmony_ci{
112862306a36Sopenharmony_ci	netdev_upper_dev_unlink(port->dev, team->dev);
112962306a36Sopenharmony_ci	port->dev->priv_flags &= ~IFF_TEAM_PORT;
113062306a36Sopenharmony_ci}
113162306a36Sopenharmony_ci
113262306a36Sopenharmony_cistatic void __team_port_change_port_added(struct team_port *port, bool linkup);
113362306a36Sopenharmony_cistatic int team_dev_type_check_change(struct net_device *dev,
113462306a36Sopenharmony_ci				      struct net_device *port_dev);
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_cistatic int team_port_add(struct team *team, struct net_device *port_dev,
113762306a36Sopenharmony_ci			 struct netlink_ext_ack *extack)
113862306a36Sopenharmony_ci{
113962306a36Sopenharmony_ci	struct net_device *dev = team->dev;
114062306a36Sopenharmony_ci	struct team_port *port;
114162306a36Sopenharmony_ci	char *portname = port_dev->name;
114262306a36Sopenharmony_ci	int err;
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_ci	if (port_dev->flags & IFF_LOOPBACK) {
114562306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Loopback device can't be added as a team port");
114662306a36Sopenharmony_ci		netdev_err(dev, "Device %s is loopback device. Loopback devices can't be added as a team port\n",
114762306a36Sopenharmony_ci			   portname);
114862306a36Sopenharmony_ci		return -EINVAL;
114962306a36Sopenharmony_ci	}
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_ci	if (netif_is_team_port(port_dev)) {
115262306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Device is already a port of a team device");
115362306a36Sopenharmony_ci		netdev_err(dev, "Device %s is already a port "
115462306a36Sopenharmony_ci				"of a team device\n", portname);
115562306a36Sopenharmony_ci		return -EBUSY;
115662306a36Sopenharmony_ci	}
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_ci	if (dev == port_dev) {
115962306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Cannot enslave team device to itself");
116062306a36Sopenharmony_ci		netdev_err(dev, "Cannot enslave team device to itself\n");
116162306a36Sopenharmony_ci		return -EINVAL;
116262306a36Sopenharmony_ci	}
116362306a36Sopenharmony_ci
116462306a36Sopenharmony_ci	if (netdev_has_upper_dev(dev, port_dev)) {
116562306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Device is already an upper device of the team interface");
116662306a36Sopenharmony_ci		netdev_err(dev, "Device %s is already an upper device of the team interface\n",
116762306a36Sopenharmony_ci			   portname);
116862306a36Sopenharmony_ci		return -EBUSY;
116962306a36Sopenharmony_ci	}
117062306a36Sopenharmony_ci
117162306a36Sopenharmony_ci	if (port_dev->features & NETIF_F_VLAN_CHALLENGED &&
117262306a36Sopenharmony_ci	    vlan_uses_dev(dev)) {
117362306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Device is VLAN challenged and team device has VLAN set up");
117462306a36Sopenharmony_ci		netdev_err(dev, "Device %s is VLAN challenged and team device has VLAN set up\n",
117562306a36Sopenharmony_ci			   portname);
117662306a36Sopenharmony_ci		return -EPERM;
117762306a36Sopenharmony_ci	}
117862306a36Sopenharmony_ci
117962306a36Sopenharmony_ci	err = team_dev_type_check_change(dev, port_dev);
118062306a36Sopenharmony_ci	if (err)
118162306a36Sopenharmony_ci		return err;
118262306a36Sopenharmony_ci
118362306a36Sopenharmony_ci	if (port_dev->flags & IFF_UP) {
118462306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Device is up. Set it down before adding it as a team port");
118562306a36Sopenharmony_ci		netdev_err(dev, "Device %s is up. Set it down before adding it as a team port\n",
118662306a36Sopenharmony_ci			   portname);
118762306a36Sopenharmony_ci		return -EBUSY;
118862306a36Sopenharmony_ci	}
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_ci	port = kzalloc(sizeof(struct team_port) + team->mode->port_priv_size,
119162306a36Sopenharmony_ci		       GFP_KERNEL);
119262306a36Sopenharmony_ci	if (!port)
119362306a36Sopenharmony_ci		return -ENOMEM;
119462306a36Sopenharmony_ci
119562306a36Sopenharmony_ci	port->dev = port_dev;
119662306a36Sopenharmony_ci	port->team = team;
119762306a36Sopenharmony_ci	INIT_LIST_HEAD(&port->qom_list);
119862306a36Sopenharmony_ci
119962306a36Sopenharmony_ci	port->orig.mtu = port_dev->mtu;
120062306a36Sopenharmony_ci	err = dev_set_mtu(port_dev, dev->mtu);
120162306a36Sopenharmony_ci	if (err) {
120262306a36Sopenharmony_ci		netdev_dbg(dev, "Error %d calling dev_set_mtu\n", err);
120362306a36Sopenharmony_ci		goto err_set_mtu;
120462306a36Sopenharmony_ci	}
120562306a36Sopenharmony_ci
120662306a36Sopenharmony_ci	memcpy(port->orig.dev_addr, port_dev->dev_addr, port_dev->addr_len);
120762306a36Sopenharmony_ci
120862306a36Sopenharmony_ci	err = team_port_enter(team, port);
120962306a36Sopenharmony_ci	if (err) {
121062306a36Sopenharmony_ci		netdev_err(dev, "Device %s failed to enter team mode\n",
121162306a36Sopenharmony_ci			   portname);
121262306a36Sopenharmony_ci		goto err_port_enter;
121362306a36Sopenharmony_ci	}
121462306a36Sopenharmony_ci
121562306a36Sopenharmony_ci	err = dev_open(port_dev, extack);
121662306a36Sopenharmony_ci	if (err) {
121762306a36Sopenharmony_ci		netdev_dbg(dev, "Device %s opening failed\n",
121862306a36Sopenharmony_ci			   portname);
121962306a36Sopenharmony_ci		goto err_dev_open;
122062306a36Sopenharmony_ci	}
122162306a36Sopenharmony_ci
122262306a36Sopenharmony_ci	err = vlan_vids_add_by_dev(port_dev, dev);
122362306a36Sopenharmony_ci	if (err) {
122462306a36Sopenharmony_ci		netdev_err(dev, "Failed to add vlan ids to device %s\n",
122562306a36Sopenharmony_ci				portname);
122662306a36Sopenharmony_ci		goto err_vids_add;
122762306a36Sopenharmony_ci	}
122862306a36Sopenharmony_ci
122962306a36Sopenharmony_ci	err = team_port_enable_netpoll(port);
123062306a36Sopenharmony_ci	if (err) {
123162306a36Sopenharmony_ci		netdev_err(dev, "Failed to enable netpoll on device %s\n",
123262306a36Sopenharmony_ci			   portname);
123362306a36Sopenharmony_ci		goto err_enable_netpoll;
123462306a36Sopenharmony_ci	}
123562306a36Sopenharmony_ci
123662306a36Sopenharmony_ci	if (!(dev->features & NETIF_F_LRO))
123762306a36Sopenharmony_ci		dev_disable_lro(port_dev);
123862306a36Sopenharmony_ci
123962306a36Sopenharmony_ci	err = netdev_rx_handler_register(port_dev, team_handle_frame,
124062306a36Sopenharmony_ci					 port);
124162306a36Sopenharmony_ci	if (err) {
124262306a36Sopenharmony_ci		netdev_err(dev, "Device %s failed to register rx_handler\n",
124362306a36Sopenharmony_ci			   portname);
124462306a36Sopenharmony_ci		goto err_handler_register;
124562306a36Sopenharmony_ci	}
124662306a36Sopenharmony_ci
124762306a36Sopenharmony_ci	err = team_upper_dev_link(team, port, extack);
124862306a36Sopenharmony_ci	if (err) {
124962306a36Sopenharmony_ci		netdev_err(dev, "Device %s failed to set upper link\n",
125062306a36Sopenharmony_ci			   portname);
125162306a36Sopenharmony_ci		goto err_set_upper_link;
125262306a36Sopenharmony_ci	}
125362306a36Sopenharmony_ci
125462306a36Sopenharmony_ci	err = __team_option_inst_add_port(team, port);
125562306a36Sopenharmony_ci	if (err) {
125662306a36Sopenharmony_ci		netdev_err(dev, "Device %s failed to add per-port options\n",
125762306a36Sopenharmony_ci			   portname);
125862306a36Sopenharmony_ci		goto err_option_port_add;
125962306a36Sopenharmony_ci	}
126062306a36Sopenharmony_ci
126162306a36Sopenharmony_ci	/* set promiscuity level to new slave */
126262306a36Sopenharmony_ci	if (dev->flags & IFF_PROMISC) {
126362306a36Sopenharmony_ci		err = dev_set_promiscuity(port_dev, 1);
126462306a36Sopenharmony_ci		if (err)
126562306a36Sopenharmony_ci			goto err_set_slave_promisc;
126662306a36Sopenharmony_ci	}
126762306a36Sopenharmony_ci
126862306a36Sopenharmony_ci	/* set allmulti level to new slave */
126962306a36Sopenharmony_ci	if (dev->flags & IFF_ALLMULTI) {
127062306a36Sopenharmony_ci		err = dev_set_allmulti(port_dev, 1);
127162306a36Sopenharmony_ci		if (err) {
127262306a36Sopenharmony_ci			if (dev->flags & IFF_PROMISC)
127362306a36Sopenharmony_ci				dev_set_promiscuity(port_dev, -1);
127462306a36Sopenharmony_ci			goto err_set_slave_promisc;
127562306a36Sopenharmony_ci		}
127662306a36Sopenharmony_ci	}
127762306a36Sopenharmony_ci
127862306a36Sopenharmony_ci	if (dev->flags & IFF_UP) {
127962306a36Sopenharmony_ci		netif_addr_lock_bh(dev);
128062306a36Sopenharmony_ci		dev_uc_sync_multiple(port_dev, dev);
128162306a36Sopenharmony_ci		dev_mc_sync_multiple(port_dev, dev);
128262306a36Sopenharmony_ci		netif_addr_unlock_bh(dev);
128362306a36Sopenharmony_ci	}
128462306a36Sopenharmony_ci
128562306a36Sopenharmony_ci	port->index = -1;
128662306a36Sopenharmony_ci	list_add_tail_rcu(&port->list, &team->port_list);
128762306a36Sopenharmony_ci	team_port_enable(team, port);
128862306a36Sopenharmony_ci	__team_compute_features(team);
128962306a36Sopenharmony_ci	__team_port_change_port_added(port, !!netif_oper_up(port_dev));
129062306a36Sopenharmony_ci	__team_options_change_check(team);
129162306a36Sopenharmony_ci
129262306a36Sopenharmony_ci	netdev_info(dev, "Port device %s added\n", portname);
129362306a36Sopenharmony_ci
129462306a36Sopenharmony_ci	return 0;
129562306a36Sopenharmony_ci
129662306a36Sopenharmony_cierr_set_slave_promisc:
129762306a36Sopenharmony_ci	__team_option_inst_del_port(team, port);
129862306a36Sopenharmony_ci
129962306a36Sopenharmony_cierr_option_port_add:
130062306a36Sopenharmony_ci	team_upper_dev_unlink(team, port);
130162306a36Sopenharmony_ci
130262306a36Sopenharmony_cierr_set_upper_link:
130362306a36Sopenharmony_ci	netdev_rx_handler_unregister(port_dev);
130462306a36Sopenharmony_ci
130562306a36Sopenharmony_cierr_handler_register:
130662306a36Sopenharmony_ci	team_port_disable_netpoll(port);
130762306a36Sopenharmony_ci
130862306a36Sopenharmony_cierr_enable_netpoll:
130962306a36Sopenharmony_ci	vlan_vids_del_by_dev(port_dev, dev);
131062306a36Sopenharmony_ci
131162306a36Sopenharmony_cierr_vids_add:
131262306a36Sopenharmony_ci	dev_close(port_dev);
131362306a36Sopenharmony_ci
131462306a36Sopenharmony_cierr_dev_open:
131562306a36Sopenharmony_ci	team_port_leave(team, port);
131662306a36Sopenharmony_ci	team_port_set_orig_dev_addr(port);
131762306a36Sopenharmony_ci
131862306a36Sopenharmony_cierr_port_enter:
131962306a36Sopenharmony_ci	dev_set_mtu(port_dev, port->orig.mtu);
132062306a36Sopenharmony_ci
132162306a36Sopenharmony_cierr_set_mtu:
132262306a36Sopenharmony_ci	kfree(port);
132362306a36Sopenharmony_ci
132462306a36Sopenharmony_ci	return err;
132562306a36Sopenharmony_ci}
132662306a36Sopenharmony_ci
132762306a36Sopenharmony_cistatic void __team_port_change_port_removed(struct team_port *port);
132862306a36Sopenharmony_ci
132962306a36Sopenharmony_cistatic int team_port_del(struct team *team, struct net_device *port_dev)
133062306a36Sopenharmony_ci{
133162306a36Sopenharmony_ci	struct net_device *dev = team->dev;
133262306a36Sopenharmony_ci	struct team_port *port;
133362306a36Sopenharmony_ci	char *portname = port_dev->name;
133462306a36Sopenharmony_ci
133562306a36Sopenharmony_ci	port = team_port_get_rtnl(port_dev);
133662306a36Sopenharmony_ci	if (!port || !team_port_find(team, port)) {
133762306a36Sopenharmony_ci		netdev_err(dev, "Device %s does not act as a port of this team\n",
133862306a36Sopenharmony_ci			   portname);
133962306a36Sopenharmony_ci		return -ENOENT;
134062306a36Sopenharmony_ci	}
134162306a36Sopenharmony_ci
134262306a36Sopenharmony_ci	team_port_disable(team, port);
134362306a36Sopenharmony_ci	list_del_rcu(&port->list);
134462306a36Sopenharmony_ci
134562306a36Sopenharmony_ci	if (dev->flags & IFF_PROMISC)
134662306a36Sopenharmony_ci		dev_set_promiscuity(port_dev, -1);
134762306a36Sopenharmony_ci	if (dev->flags & IFF_ALLMULTI)
134862306a36Sopenharmony_ci		dev_set_allmulti(port_dev, -1);
134962306a36Sopenharmony_ci
135062306a36Sopenharmony_ci	team_upper_dev_unlink(team, port);
135162306a36Sopenharmony_ci	netdev_rx_handler_unregister(port_dev);
135262306a36Sopenharmony_ci	team_port_disable_netpoll(port);
135362306a36Sopenharmony_ci	vlan_vids_del_by_dev(port_dev, dev);
135462306a36Sopenharmony_ci	if (dev->flags & IFF_UP) {
135562306a36Sopenharmony_ci		dev_uc_unsync(port_dev, dev);
135662306a36Sopenharmony_ci		dev_mc_unsync(port_dev, dev);
135762306a36Sopenharmony_ci	}
135862306a36Sopenharmony_ci	dev_close(port_dev);
135962306a36Sopenharmony_ci	team_port_leave(team, port);
136062306a36Sopenharmony_ci
136162306a36Sopenharmony_ci	__team_option_inst_mark_removed_port(team, port);
136262306a36Sopenharmony_ci	__team_options_change_check(team);
136362306a36Sopenharmony_ci	__team_option_inst_del_port(team, port);
136462306a36Sopenharmony_ci	__team_port_change_port_removed(port);
136562306a36Sopenharmony_ci
136662306a36Sopenharmony_ci	team_port_set_orig_dev_addr(port);
136762306a36Sopenharmony_ci	dev_set_mtu(port_dev, port->orig.mtu);
136862306a36Sopenharmony_ci	kfree_rcu(port, rcu);
136962306a36Sopenharmony_ci	netdev_info(dev, "Port device %s removed\n", portname);
137062306a36Sopenharmony_ci	__team_compute_features(team);
137162306a36Sopenharmony_ci
137262306a36Sopenharmony_ci	return 0;
137362306a36Sopenharmony_ci}
137462306a36Sopenharmony_ci
137562306a36Sopenharmony_ci
137662306a36Sopenharmony_ci/*****************
137762306a36Sopenharmony_ci * Net device ops
137862306a36Sopenharmony_ci *****************/
137962306a36Sopenharmony_ci
138062306a36Sopenharmony_cistatic void team_mode_option_get(struct team *team, struct team_gsetter_ctx *ctx)
138162306a36Sopenharmony_ci{
138262306a36Sopenharmony_ci	ctx->data.str_val = team->mode->kind;
138362306a36Sopenharmony_ci}
138462306a36Sopenharmony_ci
138562306a36Sopenharmony_cistatic int team_mode_option_set(struct team *team, struct team_gsetter_ctx *ctx)
138662306a36Sopenharmony_ci{
138762306a36Sopenharmony_ci	return team_change_mode(team, ctx->data.str_val);
138862306a36Sopenharmony_ci}
138962306a36Sopenharmony_ci
139062306a36Sopenharmony_cistatic void team_notify_peers_count_get(struct team *team,
139162306a36Sopenharmony_ci					struct team_gsetter_ctx *ctx)
139262306a36Sopenharmony_ci{
139362306a36Sopenharmony_ci	ctx->data.u32_val = team->notify_peers.count;
139462306a36Sopenharmony_ci}
139562306a36Sopenharmony_ci
139662306a36Sopenharmony_cistatic int team_notify_peers_count_set(struct team *team,
139762306a36Sopenharmony_ci				       struct team_gsetter_ctx *ctx)
139862306a36Sopenharmony_ci{
139962306a36Sopenharmony_ci	team->notify_peers.count = ctx->data.u32_val;
140062306a36Sopenharmony_ci	return 0;
140162306a36Sopenharmony_ci}
140262306a36Sopenharmony_ci
140362306a36Sopenharmony_cistatic void team_notify_peers_interval_get(struct team *team,
140462306a36Sopenharmony_ci					   struct team_gsetter_ctx *ctx)
140562306a36Sopenharmony_ci{
140662306a36Sopenharmony_ci	ctx->data.u32_val = team->notify_peers.interval;
140762306a36Sopenharmony_ci}
140862306a36Sopenharmony_ci
140962306a36Sopenharmony_cistatic int team_notify_peers_interval_set(struct team *team,
141062306a36Sopenharmony_ci					  struct team_gsetter_ctx *ctx)
141162306a36Sopenharmony_ci{
141262306a36Sopenharmony_ci	team->notify_peers.interval = ctx->data.u32_val;
141362306a36Sopenharmony_ci	return 0;
141462306a36Sopenharmony_ci}
141562306a36Sopenharmony_ci
141662306a36Sopenharmony_cistatic void team_mcast_rejoin_count_get(struct team *team,
141762306a36Sopenharmony_ci					struct team_gsetter_ctx *ctx)
141862306a36Sopenharmony_ci{
141962306a36Sopenharmony_ci	ctx->data.u32_val = team->mcast_rejoin.count;
142062306a36Sopenharmony_ci}
142162306a36Sopenharmony_ci
142262306a36Sopenharmony_cistatic int team_mcast_rejoin_count_set(struct team *team,
142362306a36Sopenharmony_ci				       struct team_gsetter_ctx *ctx)
142462306a36Sopenharmony_ci{
142562306a36Sopenharmony_ci	team->mcast_rejoin.count = ctx->data.u32_val;
142662306a36Sopenharmony_ci	return 0;
142762306a36Sopenharmony_ci}
142862306a36Sopenharmony_ci
142962306a36Sopenharmony_cistatic void team_mcast_rejoin_interval_get(struct team *team,
143062306a36Sopenharmony_ci					   struct team_gsetter_ctx *ctx)
143162306a36Sopenharmony_ci{
143262306a36Sopenharmony_ci	ctx->data.u32_val = team->mcast_rejoin.interval;
143362306a36Sopenharmony_ci}
143462306a36Sopenharmony_ci
143562306a36Sopenharmony_cistatic int team_mcast_rejoin_interval_set(struct team *team,
143662306a36Sopenharmony_ci					  struct team_gsetter_ctx *ctx)
143762306a36Sopenharmony_ci{
143862306a36Sopenharmony_ci	team->mcast_rejoin.interval = ctx->data.u32_val;
143962306a36Sopenharmony_ci	return 0;
144062306a36Sopenharmony_ci}
144162306a36Sopenharmony_ci
144262306a36Sopenharmony_cistatic void team_port_en_option_get(struct team *team,
144362306a36Sopenharmony_ci				    struct team_gsetter_ctx *ctx)
144462306a36Sopenharmony_ci{
144562306a36Sopenharmony_ci	struct team_port *port = ctx->info->port;
144662306a36Sopenharmony_ci
144762306a36Sopenharmony_ci	ctx->data.bool_val = team_port_enabled(port);
144862306a36Sopenharmony_ci}
144962306a36Sopenharmony_ci
145062306a36Sopenharmony_cistatic int team_port_en_option_set(struct team *team,
145162306a36Sopenharmony_ci				   struct team_gsetter_ctx *ctx)
145262306a36Sopenharmony_ci{
145362306a36Sopenharmony_ci	struct team_port *port = ctx->info->port;
145462306a36Sopenharmony_ci
145562306a36Sopenharmony_ci	if (ctx->data.bool_val)
145662306a36Sopenharmony_ci		team_port_enable(team, port);
145762306a36Sopenharmony_ci	else
145862306a36Sopenharmony_ci		team_port_disable(team, port);
145962306a36Sopenharmony_ci	return 0;
146062306a36Sopenharmony_ci}
146162306a36Sopenharmony_ci
146262306a36Sopenharmony_cistatic void team_user_linkup_option_get(struct team *team,
146362306a36Sopenharmony_ci					struct team_gsetter_ctx *ctx)
146462306a36Sopenharmony_ci{
146562306a36Sopenharmony_ci	struct team_port *port = ctx->info->port;
146662306a36Sopenharmony_ci
146762306a36Sopenharmony_ci	ctx->data.bool_val = port->user.linkup;
146862306a36Sopenharmony_ci}
146962306a36Sopenharmony_ci
147062306a36Sopenharmony_cistatic void __team_carrier_check(struct team *team);
147162306a36Sopenharmony_ci
147262306a36Sopenharmony_cistatic int team_user_linkup_option_set(struct team *team,
147362306a36Sopenharmony_ci				       struct team_gsetter_ctx *ctx)
147462306a36Sopenharmony_ci{
147562306a36Sopenharmony_ci	struct team_port *port = ctx->info->port;
147662306a36Sopenharmony_ci
147762306a36Sopenharmony_ci	port->user.linkup = ctx->data.bool_val;
147862306a36Sopenharmony_ci	team_refresh_port_linkup(port);
147962306a36Sopenharmony_ci	__team_carrier_check(port->team);
148062306a36Sopenharmony_ci	return 0;
148162306a36Sopenharmony_ci}
148262306a36Sopenharmony_ci
148362306a36Sopenharmony_cistatic void team_user_linkup_en_option_get(struct team *team,
148462306a36Sopenharmony_ci					   struct team_gsetter_ctx *ctx)
148562306a36Sopenharmony_ci{
148662306a36Sopenharmony_ci	struct team_port *port = ctx->info->port;
148762306a36Sopenharmony_ci
148862306a36Sopenharmony_ci	ctx->data.bool_val = port->user.linkup_enabled;
148962306a36Sopenharmony_ci}
149062306a36Sopenharmony_ci
149162306a36Sopenharmony_cistatic int team_user_linkup_en_option_set(struct team *team,
149262306a36Sopenharmony_ci					  struct team_gsetter_ctx *ctx)
149362306a36Sopenharmony_ci{
149462306a36Sopenharmony_ci	struct team_port *port = ctx->info->port;
149562306a36Sopenharmony_ci
149662306a36Sopenharmony_ci	port->user.linkup_enabled = ctx->data.bool_val;
149762306a36Sopenharmony_ci	team_refresh_port_linkup(port);
149862306a36Sopenharmony_ci	__team_carrier_check(port->team);
149962306a36Sopenharmony_ci	return 0;
150062306a36Sopenharmony_ci}
150162306a36Sopenharmony_ci
150262306a36Sopenharmony_cistatic void team_priority_option_get(struct team *team,
150362306a36Sopenharmony_ci				     struct team_gsetter_ctx *ctx)
150462306a36Sopenharmony_ci{
150562306a36Sopenharmony_ci	struct team_port *port = ctx->info->port;
150662306a36Sopenharmony_ci
150762306a36Sopenharmony_ci	ctx->data.s32_val = port->priority;
150862306a36Sopenharmony_ci}
150962306a36Sopenharmony_ci
151062306a36Sopenharmony_cistatic int team_priority_option_set(struct team *team,
151162306a36Sopenharmony_ci				    struct team_gsetter_ctx *ctx)
151262306a36Sopenharmony_ci{
151362306a36Sopenharmony_ci	struct team_port *port = ctx->info->port;
151462306a36Sopenharmony_ci	s32 priority = ctx->data.s32_val;
151562306a36Sopenharmony_ci
151662306a36Sopenharmony_ci	if (port->priority == priority)
151762306a36Sopenharmony_ci		return 0;
151862306a36Sopenharmony_ci	port->priority = priority;
151962306a36Sopenharmony_ci	team_queue_override_port_prio_changed(team, port);
152062306a36Sopenharmony_ci	return 0;
152162306a36Sopenharmony_ci}
152262306a36Sopenharmony_ci
152362306a36Sopenharmony_cistatic void team_queue_id_option_get(struct team *team,
152462306a36Sopenharmony_ci				     struct team_gsetter_ctx *ctx)
152562306a36Sopenharmony_ci{
152662306a36Sopenharmony_ci	struct team_port *port = ctx->info->port;
152762306a36Sopenharmony_ci
152862306a36Sopenharmony_ci	ctx->data.u32_val = port->queue_id;
152962306a36Sopenharmony_ci}
153062306a36Sopenharmony_ci
153162306a36Sopenharmony_cistatic int team_queue_id_option_set(struct team *team,
153262306a36Sopenharmony_ci				    struct team_gsetter_ctx *ctx)
153362306a36Sopenharmony_ci{
153462306a36Sopenharmony_ci	struct team_port *port = ctx->info->port;
153562306a36Sopenharmony_ci	u16 new_queue_id = ctx->data.u32_val;
153662306a36Sopenharmony_ci
153762306a36Sopenharmony_ci	if (port->queue_id == new_queue_id)
153862306a36Sopenharmony_ci		return 0;
153962306a36Sopenharmony_ci	if (new_queue_id >= team->dev->real_num_tx_queues)
154062306a36Sopenharmony_ci		return -EINVAL;
154162306a36Sopenharmony_ci	team_queue_override_port_change_queue_id(team, port, new_queue_id);
154262306a36Sopenharmony_ci	return 0;
154362306a36Sopenharmony_ci}
154462306a36Sopenharmony_ci
154562306a36Sopenharmony_cistatic const struct team_option team_options[] = {
154662306a36Sopenharmony_ci	{
154762306a36Sopenharmony_ci		.name = "mode",
154862306a36Sopenharmony_ci		.type = TEAM_OPTION_TYPE_STRING,
154962306a36Sopenharmony_ci		.getter = team_mode_option_get,
155062306a36Sopenharmony_ci		.setter = team_mode_option_set,
155162306a36Sopenharmony_ci	},
155262306a36Sopenharmony_ci	{
155362306a36Sopenharmony_ci		.name = "notify_peers_count",
155462306a36Sopenharmony_ci		.type = TEAM_OPTION_TYPE_U32,
155562306a36Sopenharmony_ci		.getter = team_notify_peers_count_get,
155662306a36Sopenharmony_ci		.setter = team_notify_peers_count_set,
155762306a36Sopenharmony_ci	},
155862306a36Sopenharmony_ci	{
155962306a36Sopenharmony_ci		.name = "notify_peers_interval",
156062306a36Sopenharmony_ci		.type = TEAM_OPTION_TYPE_U32,
156162306a36Sopenharmony_ci		.getter = team_notify_peers_interval_get,
156262306a36Sopenharmony_ci		.setter = team_notify_peers_interval_set,
156362306a36Sopenharmony_ci	},
156462306a36Sopenharmony_ci	{
156562306a36Sopenharmony_ci		.name = "mcast_rejoin_count",
156662306a36Sopenharmony_ci		.type = TEAM_OPTION_TYPE_U32,
156762306a36Sopenharmony_ci		.getter = team_mcast_rejoin_count_get,
156862306a36Sopenharmony_ci		.setter = team_mcast_rejoin_count_set,
156962306a36Sopenharmony_ci	},
157062306a36Sopenharmony_ci	{
157162306a36Sopenharmony_ci		.name = "mcast_rejoin_interval",
157262306a36Sopenharmony_ci		.type = TEAM_OPTION_TYPE_U32,
157362306a36Sopenharmony_ci		.getter = team_mcast_rejoin_interval_get,
157462306a36Sopenharmony_ci		.setter = team_mcast_rejoin_interval_set,
157562306a36Sopenharmony_ci	},
157662306a36Sopenharmony_ci	{
157762306a36Sopenharmony_ci		.name = "enabled",
157862306a36Sopenharmony_ci		.type = TEAM_OPTION_TYPE_BOOL,
157962306a36Sopenharmony_ci		.per_port = true,
158062306a36Sopenharmony_ci		.getter = team_port_en_option_get,
158162306a36Sopenharmony_ci		.setter = team_port_en_option_set,
158262306a36Sopenharmony_ci	},
158362306a36Sopenharmony_ci	{
158462306a36Sopenharmony_ci		.name = "user_linkup",
158562306a36Sopenharmony_ci		.type = TEAM_OPTION_TYPE_BOOL,
158662306a36Sopenharmony_ci		.per_port = true,
158762306a36Sopenharmony_ci		.getter = team_user_linkup_option_get,
158862306a36Sopenharmony_ci		.setter = team_user_linkup_option_set,
158962306a36Sopenharmony_ci	},
159062306a36Sopenharmony_ci	{
159162306a36Sopenharmony_ci		.name = "user_linkup_enabled",
159262306a36Sopenharmony_ci		.type = TEAM_OPTION_TYPE_BOOL,
159362306a36Sopenharmony_ci		.per_port = true,
159462306a36Sopenharmony_ci		.getter = team_user_linkup_en_option_get,
159562306a36Sopenharmony_ci		.setter = team_user_linkup_en_option_set,
159662306a36Sopenharmony_ci	},
159762306a36Sopenharmony_ci	{
159862306a36Sopenharmony_ci		.name = "priority",
159962306a36Sopenharmony_ci		.type = TEAM_OPTION_TYPE_S32,
160062306a36Sopenharmony_ci		.per_port = true,
160162306a36Sopenharmony_ci		.getter = team_priority_option_get,
160262306a36Sopenharmony_ci		.setter = team_priority_option_set,
160362306a36Sopenharmony_ci	},
160462306a36Sopenharmony_ci	{
160562306a36Sopenharmony_ci		.name = "queue_id",
160662306a36Sopenharmony_ci		.type = TEAM_OPTION_TYPE_U32,
160762306a36Sopenharmony_ci		.per_port = true,
160862306a36Sopenharmony_ci		.getter = team_queue_id_option_get,
160962306a36Sopenharmony_ci		.setter = team_queue_id_option_set,
161062306a36Sopenharmony_ci	},
161162306a36Sopenharmony_ci};
161262306a36Sopenharmony_ci
161362306a36Sopenharmony_ci
161462306a36Sopenharmony_cistatic int team_init(struct net_device *dev)
161562306a36Sopenharmony_ci{
161662306a36Sopenharmony_ci	struct team *team = netdev_priv(dev);
161762306a36Sopenharmony_ci	int i;
161862306a36Sopenharmony_ci	int err;
161962306a36Sopenharmony_ci
162062306a36Sopenharmony_ci	team->dev = dev;
162162306a36Sopenharmony_ci	team_set_no_mode(team);
162262306a36Sopenharmony_ci	team->notifier_ctx = false;
162362306a36Sopenharmony_ci
162462306a36Sopenharmony_ci	team->pcpu_stats = netdev_alloc_pcpu_stats(struct team_pcpu_stats);
162562306a36Sopenharmony_ci	if (!team->pcpu_stats)
162662306a36Sopenharmony_ci		return -ENOMEM;
162762306a36Sopenharmony_ci
162862306a36Sopenharmony_ci	for (i = 0; i < TEAM_PORT_HASHENTRIES; i++)
162962306a36Sopenharmony_ci		INIT_HLIST_HEAD(&team->en_port_hlist[i]);
163062306a36Sopenharmony_ci	INIT_LIST_HEAD(&team->port_list);
163162306a36Sopenharmony_ci	err = team_queue_override_init(team);
163262306a36Sopenharmony_ci	if (err)
163362306a36Sopenharmony_ci		goto err_team_queue_override_init;
163462306a36Sopenharmony_ci
163562306a36Sopenharmony_ci	team_adjust_ops(team);
163662306a36Sopenharmony_ci
163762306a36Sopenharmony_ci	INIT_LIST_HEAD(&team->option_list);
163862306a36Sopenharmony_ci	INIT_LIST_HEAD(&team->option_inst_list);
163962306a36Sopenharmony_ci
164062306a36Sopenharmony_ci	team_notify_peers_init(team);
164162306a36Sopenharmony_ci	team_mcast_rejoin_init(team);
164262306a36Sopenharmony_ci
164362306a36Sopenharmony_ci	err = team_options_register(team, team_options, ARRAY_SIZE(team_options));
164462306a36Sopenharmony_ci	if (err)
164562306a36Sopenharmony_ci		goto err_options_register;
164662306a36Sopenharmony_ci	netif_carrier_off(dev);
164762306a36Sopenharmony_ci
164862306a36Sopenharmony_ci	lockdep_register_key(&team->team_lock_key);
164962306a36Sopenharmony_ci	__mutex_init(&team->lock, "team->team_lock_key", &team->team_lock_key);
165062306a36Sopenharmony_ci	netdev_lockdep_set_classes(dev);
165162306a36Sopenharmony_ci
165262306a36Sopenharmony_ci	return 0;
165362306a36Sopenharmony_ci
165462306a36Sopenharmony_cierr_options_register:
165562306a36Sopenharmony_ci	team_mcast_rejoin_fini(team);
165662306a36Sopenharmony_ci	team_notify_peers_fini(team);
165762306a36Sopenharmony_ci	team_queue_override_fini(team);
165862306a36Sopenharmony_cierr_team_queue_override_init:
165962306a36Sopenharmony_ci	free_percpu(team->pcpu_stats);
166062306a36Sopenharmony_ci
166162306a36Sopenharmony_ci	return err;
166262306a36Sopenharmony_ci}
166362306a36Sopenharmony_ci
166462306a36Sopenharmony_cistatic void team_uninit(struct net_device *dev)
166562306a36Sopenharmony_ci{
166662306a36Sopenharmony_ci	struct team *team = netdev_priv(dev);
166762306a36Sopenharmony_ci	struct team_port *port;
166862306a36Sopenharmony_ci	struct team_port *tmp;
166962306a36Sopenharmony_ci
167062306a36Sopenharmony_ci	mutex_lock(&team->lock);
167162306a36Sopenharmony_ci	list_for_each_entry_safe(port, tmp, &team->port_list, list)
167262306a36Sopenharmony_ci		team_port_del(team, port->dev);
167362306a36Sopenharmony_ci
167462306a36Sopenharmony_ci	__team_change_mode(team, NULL); /* cleanup */
167562306a36Sopenharmony_ci	__team_options_unregister(team, team_options, ARRAY_SIZE(team_options));
167662306a36Sopenharmony_ci	team_mcast_rejoin_fini(team);
167762306a36Sopenharmony_ci	team_notify_peers_fini(team);
167862306a36Sopenharmony_ci	team_queue_override_fini(team);
167962306a36Sopenharmony_ci	mutex_unlock(&team->lock);
168062306a36Sopenharmony_ci	netdev_change_features(dev);
168162306a36Sopenharmony_ci	lockdep_unregister_key(&team->team_lock_key);
168262306a36Sopenharmony_ci}
168362306a36Sopenharmony_ci
168462306a36Sopenharmony_cistatic void team_destructor(struct net_device *dev)
168562306a36Sopenharmony_ci{
168662306a36Sopenharmony_ci	struct team *team = netdev_priv(dev);
168762306a36Sopenharmony_ci
168862306a36Sopenharmony_ci	free_percpu(team->pcpu_stats);
168962306a36Sopenharmony_ci}
169062306a36Sopenharmony_ci
169162306a36Sopenharmony_cistatic int team_open(struct net_device *dev)
169262306a36Sopenharmony_ci{
169362306a36Sopenharmony_ci	return 0;
169462306a36Sopenharmony_ci}
169562306a36Sopenharmony_ci
169662306a36Sopenharmony_cistatic int team_close(struct net_device *dev)
169762306a36Sopenharmony_ci{
169862306a36Sopenharmony_ci	struct team *team = netdev_priv(dev);
169962306a36Sopenharmony_ci	struct team_port *port;
170062306a36Sopenharmony_ci
170162306a36Sopenharmony_ci	list_for_each_entry(port, &team->port_list, list) {
170262306a36Sopenharmony_ci		dev_uc_unsync(port->dev, dev);
170362306a36Sopenharmony_ci		dev_mc_unsync(port->dev, dev);
170462306a36Sopenharmony_ci	}
170562306a36Sopenharmony_ci
170662306a36Sopenharmony_ci	return 0;
170762306a36Sopenharmony_ci}
170862306a36Sopenharmony_ci
170962306a36Sopenharmony_ci/*
171062306a36Sopenharmony_ci * note: already called with rcu_read_lock
171162306a36Sopenharmony_ci */
171262306a36Sopenharmony_cistatic netdev_tx_t team_xmit(struct sk_buff *skb, struct net_device *dev)
171362306a36Sopenharmony_ci{
171462306a36Sopenharmony_ci	struct team *team = netdev_priv(dev);
171562306a36Sopenharmony_ci	bool tx_success;
171662306a36Sopenharmony_ci	unsigned int len = skb->len;
171762306a36Sopenharmony_ci
171862306a36Sopenharmony_ci	tx_success = team_queue_override_transmit(team, skb);
171962306a36Sopenharmony_ci	if (!tx_success)
172062306a36Sopenharmony_ci		tx_success = team->ops.transmit(team, skb);
172162306a36Sopenharmony_ci	if (tx_success) {
172262306a36Sopenharmony_ci		struct team_pcpu_stats *pcpu_stats;
172362306a36Sopenharmony_ci
172462306a36Sopenharmony_ci		pcpu_stats = this_cpu_ptr(team->pcpu_stats);
172562306a36Sopenharmony_ci		u64_stats_update_begin(&pcpu_stats->syncp);
172662306a36Sopenharmony_ci		u64_stats_inc(&pcpu_stats->tx_packets);
172762306a36Sopenharmony_ci		u64_stats_add(&pcpu_stats->tx_bytes, len);
172862306a36Sopenharmony_ci		u64_stats_update_end(&pcpu_stats->syncp);
172962306a36Sopenharmony_ci	} else {
173062306a36Sopenharmony_ci		this_cpu_inc(team->pcpu_stats->tx_dropped);
173162306a36Sopenharmony_ci	}
173262306a36Sopenharmony_ci
173362306a36Sopenharmony_ci	return NETDEV_TX_OK;
173462306a36Sopenharmony_ci}
173562306a36Sopenharmony_ci
173662306a36Sopenharmony_cistatic u16 team_select_queue(struct net_device *dev, struct sk_buff *skb,
173762306a36Sopenharmony_ci			     struct net_device *sb_dev)
173862306a36Sopenharmony_ci{
173962306a36Sopenharmony_ci	/*
174062306a36Sopenharmony_ci	 * This helper function exists to help dev_pick_tx get the correct
174162306a36Sopenharmony_ci	 * destination queue.  Using a helper function skips a call to
174262306a36Sopenharmony_ci	 * skb_tx_hash and will put the skbs in the queue we expect on their
174362306a36Sopenharmony_ci	 * way down to the team driver.
174462306a36Sopenharmony_ci	 */
174562306a36Sopenharmony_ci	u16 txq = skb_rx_queue_recorded(skb) ? skb_get_rx_queue(skb) : 0;
174662306a36Sopenharmony_ci
174762306a36Sopenharmony_ci	/*
174862306a36Sopenharmony_ci	 * Save the original txq to restore before passing to the driver
174962306a36Sopenharmony_ci	 */
175062306a36Sopenharmony_ci	qdisc_skb_cb(skb)->slave_dev_queue_mapping = skb->queue_mapping;
175162306a36Sopenharmony_ci
175262306a36Sopenharmony_ci	if (unlikely(txq >= dev->real_num_tx_queues)) {
175362306a36Sopenharmony_ci		do {
175462306a36Sopenharmony_ci			txq -= dev->real_num_tx_queues;
175562306a36Sopenharmony_ci		} while (txq >= dev->real_num_tx_queues);
175662306a36Sopenharmony_ci	}
175762306a36Sopenharmony_ci	return txq;
175862306a36Sopenharmony_ci}
175962306a36Sopenharmony_ci
176062306a36Sopenharmony_cistatic void team_change_rx_flags(struct net_device *dev, int change)
176162306a36Sopenharmony_ci{
176262306a36Sopenharmony_ci	struct team *team = netdev_priv(dev);
176362306a36Sopenharmony_ci	struct team_port *port;
176462306a36Sopenharmony_ci	int inc;
176562306a36Sopenharmony_ci
176662306a36Sopenharmony_ci	rcu_read_lock();
176762306a36Sopenharmony_ci	list_for_each_entry_rcu(port, &team->port_list, list) {
176862306a36Sopenharmony_ci		if (change & IFF_PROMISC) {
176962306a36Sopenharmony_ci			inc = dev->flags & IFF_PROMISC ? 1 : -1;
177062306a36Sopenharmony_ci			dev_set_promiscuity(port->dev, inc);
177162306a36Sopenharmony_ci		}
177262306a36Sopenharmony_ci		if (change & IFF_ALLMULTI) {
177362306a36Sopenharmony_ci			inc = dev->flags & IFF_ALLMULTI ? 1 : -1;
177462306a36Sopenharmony_ci			dev_set_allmulti(port->dev, inc);
177562306a36Sopenharmony_ci		}
177662306a36Sopenharmony_ci	}
177762306a36Sopenharmony_ci	rcu_read_unlock();
177862306a36Sopenharmony_ci}
177962306a36Sopenharmony_ci
178062306a36Sopenharmony_cistatic void team_set_rx_mode(struct net_device *dev)
178162306a36Sopenharmony_ci{
178262306a36Sopenharmony_ci	struct team *team = netdev_priv(dev);
178362306a36Sopenharmony_ci	struct team_port *port;
178462306a36Sopenharmony_ci
178562306a36Sopenharmony_ci	rcu_read_lock();
178662306a36Sopenharmony_ci	list_for_each_entry_rcu(port, &team->port_list, list) {
178762306a36Sopenharmony_ci		dev_uc_sync_multiple(port->dev, dev);
178862306a36Sopenharmony_ci		dev_mc_sync_multiple(port->dev, dev);
178962306a36Sopenharmony_ci	}
179062306a36Sopenharmony_ci	rcu_read_unlock();
179162306a36Sopenharmony_ci}
179262306a36Sopenharmony_ci
179362306a36Sopenharmony_cistatic int team_set_mac_address(struct net_device *dev, void *p)
179462306a36Sopenharmony_ci{
179562306a36Sopenharmony_ci	struct sockaddr *addr = p;
179662306a36Sopenharmony_ci	struct team *team = netdev_priv(dev);
179762306a36Sopenharmony_ci	struct team_port *port;
179862306a36Sopenharmony_ci
179962306a36Sopenharmony_ci	if (dev->type == ARPHRD_ETHER && !is_valid_ether_addr(addr->sa_data))
180062306a36Sopenharmony_ci		return -EADDRNOTAVAIL;
180162306a36Sopenharmony_ci	dev_addr_set(dev, addr->sa_data);
180262306a36Sopenharmony_ci	mutex_lock(&team->lock);
180362306a36Sopenharmony_ci	list_for_each_entry(port, &team->port_list, list)
180462306a36Sopenharmony_ci		if (team->ops.port_change_dev_addr)
180562306a36Sopenharmony_ci			team->ops.port_change_dev_addr(team, port);
180662306a36Sopenharmony_ci	mutex_unlock(&team->lock);
180762306a36Sopenharmony_ci	return 0;
180862306a36Sopenharmony_ci}
180962306a36Sopenharmony_ci
181062306a36Sopenharmony_cistatic int team_change_mtu(struct net_device *dev, int new_mtu)
181162306a36Sopenharmony_ci{
181262306a36Sopenharmony_ci	struct team *team = netdev_priv(dev);
181362306a36Sopenharmony_ci	struct team_port *port;
181462306a36Sopenharmony_ci	int err;
181562306a36Sopenharmony_ci
181662306a36Sopenharmony_ci	/*
181762306a36Sopenharmony_ci	 * Alhough this is reader, it's guarded by team lock. It's not possible
181862306a36Sopenharmony_ci	 * to traverse list in reverse under rcu_read_lock
181962306a36Sopenharmony_ci	 */
182062306a36Sopenharmony_ci	mutex_lock(&team->lock);
182162306a36Sopenharmony_ci	team->port_mtu_change_allowed = true;
182262306a36Sopenharmony_ci	list_for_each_entry(port, &team->port_list, list) {
182362306a36Sopenharmony_ci		err = dev_set_mtu(port->dev, new_mtu);
182462306a36Sopenharmony_ci		if (err) {
182562306a36Sopenharmony_ci			netdev_err(dev, "Device %s failed to change mtu",
182662306a36Sopenharmony_ci				   port->dev->name);
182762306a36Sopenharmony_ci			goto unwind;
182862306a36Sopenharmony_ci		}
182962306a36Sopenharmony_ci	}
183062306a36Sopenharmony_ci	team->port_mtu_change_allowed = false;
183162306a36Sopenharmony_ci	mutex_unlock(&team->lock);
183262306a36Sopenharmony_ci
183362306a36Sopenharmony_ci	dev->mtu = new_mtu;
183462306a36Sopenharmony_ci
183562306a36Sopenharmony_ci	return 0;
183662306a36Sopenharmony_ci
183762306a36Sopenharmony_ciunwind:
183862306a36Sopenharmony_ci	list_for_each_entry_continue_reverse(port, &team->port_list, list)
183962306a36Sopenharmony_ci		dev_set_mtu(port->dev, dev->mtu);
184062306a36Sopenharmony_ci	team->port_mtu_change_allowed = false;
184162306a36Sopenharmony_ci	mutex_unlock(&team->lock);
184262306a36Sopenharmony_ci
184362306a36Sopenharmony_ci	return err;
184462306a36Sopenharmony_ci}
184562306a36Sopenharmony_ci
184662306a36Sopenharmony_cistatic void
184762306a36Sopenharmony_citeam_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
184862306a36Sopenharmony_ci{
184962306a36Sopenharmony_ci	struct team *team = netdev_priv(dev);
185062306a36Sopenharmony_ci	struct team_pcpu_stats *p;
185162306a36Sopenharmony_ci	u64 rx_packets, rx_bytes, rx_multicast, tx_packets, tx_bytes;
185262306a36Sopenharmony_ci	u32 rx_dropped = 0, tx_dropped = 0, rx_nohandler = 0;
185362306a36Sopenharmony_ci	unsigned int start;
185462306a36Sopenharmony_ci	int i;
185562306a36Sopenharmony_ci
185662306a36Sopenharmony_ci	for_each_possible_cpu(i) {
185762306a36Sopenharmony_ci		p = per_cpu_ptr(team->pcpu_stats, i);
185862306a36Sopenharmony_ci		do {
185962306a36Sopenharmony_ci			start = u64_stats_fetch_begin(&p->syncp);
186062306a36Sopenharmony_ci			rx_packets	= u64_stats_read(&p->rx_packets);
186162306a36Sopenharmony_ci			rx_bytes	= u64_stats_read(&p->rx_bytes);
186262306a36Sopenharmony_ci			rx_multicast	= u64_stats_read(&p->rx_multicast);
186362306a36Sopenharmony_ci			tx_packets	= u64_stats_read(&p->tx_packets);
186462306a36Sopenharmony_ci			tx_bytes	= u64_stats_read(&p->tx_bytes);
186562306a36Sopenharmony_ci		} while (u64_stats_fetch_retry(&p->syncp, start));
186662306a36Sopenharmony_ci
186762306a36Sopenharmony_ci		stats->rx_packets	+= rx_packets;
186862306a36Sopenharmony_ci		stats->rx_bytes		+= rx_bytes;
186962306a36Sopenharmony_ci		stats->multicast	+= rx_multicast;
187062306a36Sopenharmony_ci		stats->tx_packets	+= tx_packets;
187162306a36Sopenharmony_ci		stats->tx_bytes		+= tx_bytes;
187262306a36Sopenharmony_ci		/*
187362306a36Sopenharmony_ci		 * rx_dropped, tx_dropped & rx_nohandler are u32,
187462306a36Sopenharmony_ci		 * updated without syncp protection.
187562306a36Sopenharmony_ci		 */
187662306a36Sopenharmony_ci		rx_dropped	+= READ_ONCE(p->rx_dropped);
187762306a36Sopenharmony_ci		tx_dropped	+= READ_ONCE(p->tx_dropped);
187862306a36Sopenharmony_ci		rx_nohandler	+= READ_ONCE(p->rx_nohandler);
187962306a36Sopenharmony_ci	}
188062306a36Sopenharmony_ci	stats->rx_dropped	= rx_dropped;
188162306a36Sopenharmony_ci	stats->tx_dropped	= tx_dropped;
188262306a36Sopenharmony_ci	stats->rx_nohandler	= rx_nohandler;
188362306a36Sopenharmony_ci}
188462306a36Sopenharmony_ci
188562306a36Sopenharmony_cistatic int team_vlan_rx_add_vid(struct net_device *dev, __be16 proto, u16 vid)
188662306a36Sopenharmony_ci{
188762306a36Sopenharmony_ci	struct team *team = netdev_priv(dev);
188862306a36Sopenharmony_ci	struct team_port *port;
188962306a36Sopenharmony_ci	int err;
189062306a36Sopenharmony_ci
189162306a36Sopenharmony_ci	/*
189262306a36Sopenharmony_ci	 * Alhough this is reader, it's guarded by team lock. It's not possible
189362306a36Sopenharmony_ci	 * to traverse list in reverse under rcu_read_lock
189462306a36Sopenharmony_ci	 */
189562306a36Sopenharmony_ci	mutex_lock(&team->lock);
189662306a36Sopenharmony_ci	list_for_each_entry(port, &team->port_list, list) {
189762306a36Sopenharmony_ci		err = vlan_vid_add(port->dev, proto, vid);
189862306a36Sopenharmony_ci		if (err)
189962306a36Sopenharmony_ci			goto unwind;
190062306a36Sopenharmony_ci	}
190162306a36Sopenharmony_ci	mutex_unlock(&team->lock);
190262306a36Sopenharmony_ci
190362306a36Sopenharmony_ci	return 0;
190462306a36Sopenharmony_ci
190562306a36Sopenharmony_ciunwind:
190662306a36Sopenharmony_ci	list_for_each_entry_continue_reverse(port, &team->port_list, list)
190762306a36Sopenharmony_ci		vlan_vid_del(port->dev, proto, vid);
190862306a36Sopenharmony_ci	mutex_unlock(&team->lock);
190962306a36Sopenharmony_ci
191062306a36Sopenharmony_ci	return err;
191162306a36Sopenharmony_ci}
191262306a36Sopenharmony_ci
191362306a36Sopenharmony_cistatic int team_vlan_rx_kill_vid(struct net_device *dev, __be16 proto, u16 vid)
191462306a36Sopenharmony_ci{
191562306a36Sopenharmony_ci	struct team *team = netdev_priv(dev);
191662306a36Sopenharmony_ci	struct team_port *port;
191762306a36Sopenharmony_ci
191862306a36Sopenharmony_ci	mutex_lock(&team->lock);
191962306a36Sopenharmony_ci	list_for_each_entry(port, &team->port_list, list)
192062306a36Sopenharmony_ci		vlan_vid_del(port->dev, proto, vid);
192162306a36Sopenharmony_ci	mutex_unlock(&team->lock);
192262306a36Sopenharmony_ci
192362306a36Sopenharmony_ci	return 0;
192462306a36Sopenharmony_ci}
192562306a36Sopenharmony_ci
192662306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER
192762306a36Sopenharmony_cistatic void team_poll_controller(struct net_device *dev)
192862306a36Sopenharmony_ci{
192962306a36Sopenharmony_ci}
193062306a36Sopenharmony_ci
193162306a36Sopenharmony_cistatic void __team_netpoll_cleanup(struct team *team)
193262306a36Sopenharmony_ci{
193362306a36Sopenharmony_ci	struct team_port *port;
193462306a36Sopenharmony_ci
193562306a36Sopenharmony_ci	list_for_each_entry(port, &team->port_list, list)
193662306a36Sopenharmony_ci		team_port_disable_netpoll(port);
193762306a36Sopenharmony_ci}
193862306a36Sopenharmony_ci
193962306a36Sopenharmony_cistatic void team_netpoll_cleanup(struct net_device *dev)
194062306a36Sopenharmony_ci{
194162306a36Sopenharmony_ci	struct team *team = netdev_priv(dev);
194262306a36Sopenharmony_ci
194362306a36Sopenharmony_ci	mutex_lock(&team->lock);
194462306a36Sopenharmony_ci	__team_netpoll_cleanup(team);
194562306a36Sopenharmony_ci	mutex_unlock(&team->lock);
194662306a36Sopenharmony_ci}
194762306a36Sopenharmony_ci
194862306a36Sopenharmony_cistatic int team_netpoll_setup(struct net_device *dev,
194962306a36Sopenharmony_ci			      struct netpoll_info *npifo)
195062306a36Sopenharmony_ci{
195162306a36Sopenharmony_ci	struct team *team = netdev_priv(dev);
195262306a36Sopenharmony_ci	struct team_port *port;
195362306a36Sopenharmony_ci	int err = 0;
195462306a36Sopenharmony_ci
195562306a36Sopenharmony_ci	mutex_lock(&team->lock);
195662306a36Sopenharmony_ci	list_for_each_entry(port, &team->port_list, list) {
195762306a36Sopenharmony_ci		err = __team_port_enable_netpoll(port);
195862306a36Sopenharmony_ci		if (err) {
195962306a36Sopenharmony_ci			__team_netpoll_cleanup(team);
196062306a36Sopenharmony_ci			break;
196162306a36Sopenharmony_ci		}
196262306a36Sopenharmony_ci	}
196362306a36Sopenharmony_ci	mutex_unlock(&team->lock);
196462306a36Sopenharmony_ci	return err;
196562306a36Sopenharmony_ci}
196662306a36Sopenharmony_ci#endif
196762306a36Sopenharmony_ci
196862306a36Sopenharmony_cistatic int team_add_slave(struct net_device *dev, struct net_device *port_dev,
196962306a36Sopenharmony_ci			  struct netlink_ext_ack *extack)
197062306a36Sopenharmony_ci{
197162306a36Sopenharmony_ci	struct team *team = netdev_priv(dev);
197262306a36Sopenharmony_ci	int err;
197362306a36Sopenharmony_ci
197462306a36Sopenharmony_ci	mutex_lock(&team->lock);
197562306a36Sopenharmony_ci	err = team_port_add(team, port_dev, extack);
197662306a36Sopenharmony_ci	mutex_unlock(&team->lock);
197762306a36Sopenharmony_ci
197862306a36Sopenharmony_ci	if (!err)
197962306a36Sopenharmony_ci		netdev_change_features(dev);
198062306a36Sopenharmony_ci
198162306a36Sopenharmony_ci	return err;
198262306a36Sopenharmony_ci}
198362306a36Sopenharmony_ci
198462306a36Sopenharmony_cistatic int team_del_slave(struct net_device *dev, struct net_device *port_dev)
198562306a36Sopenharmony_ci{
198662306a36Sopenharmony_ci	struct team *team = netdev_priv(dev);
198762306a36Sopenharmony_ci	int err;
198862306a36Sopenharmony_ci
198962306a36Sopenharmony_ci	mutex_lock(&team->lock);
199062306a36Sopenharmony_ci	err = team_port_del(team, port_dev);
199162306a36Sopenharmony_ci	mutex_unlock(&team->lock);
199262306a36Sopenharmony_ci
199362306a36Sopenharmony_ci	if (err)
199462306a36Sopenharmony_ci		return err;
199562306a36Sopenharmony_ci
199662306a36Sopenharmony_ci	if (netif_is_team_master(port_dev)) {
199762306a36Sopenharmony_ci		lockdep_unregister_key(&team->team_lock_key);
199862306a36Sopenharmony_ci		lockdep_register_key(&team->team_lock_key);
199962306a36Sopenharmony_ci		lockdep_set_class(&team->lock, &team->team_lock_key);
200062306a36Sopenharmony_ci	}
200162306a36Sopenharmony_ci	netdev_change_features(dev);
200262306a36Sopenharmony_ci
200362306a36Sopenharmony_ci	return err;
200462306a36Sopenharmony_ci}
200562306a36Sopenharmony_ci
200662306a36Sopenharmony_cistatic netdev_features_t team_fix_features(struct net_device *dev,
200762306a36Sopenharmony_ci					   netdev_features_t features)
200862306a36Sopenharmony_ci{
200962306a36Sopenharmony_ci	struct team_port *port;
201062306a36Sopenharmony_ci	struct team *team = netdev_priv(dev);
201162306a36Sopenharmony_ci	netdev_features_t mask;
201262306a36Sopenharmony_ci
201362306a36Sopenharmony_ci	mask = features;
201462306a36Sopenharmony_ci	features &= ~NETIF_F_ONE_FOR_ALL;
201562306a36Sopenharmony_ci	features |= NETIF_F_ALL_FOR_ALL;
201662306a36Sopenharmony_ci
201762306a36Sopenharmony_ci	rcu_read_lock();
201862306a36Sopenharmony_ci	list_for_each_entry_rcu(port, &team->port_list, list) {
201962306a36Sopenharmony_ci		features = netdev_increment_features(features,
202062306a36Sopenharmony_ci						     port->dev->features,
202162306a36Sopenharmony_ci						     mask);
202262306a36Sopenharmony_ci	}
202362306a36Sopenharmony_ci	rcu_read_unlock();
202462306a36Sopenharmony_ci
202562306a36Sopenharmony_ci	features = netdev_add_tso_features(features, mask);
202662306a36Sopenharmony_ci
202762306a36Sopenharmony_ci	return features;
202862306a36Sopenharmony_ci}
202962306a36Sopenharmony_ci
203062306a36Sopenharmony_cistatic int team_change_carrier(struct net_device *dev, bool new_carrier)
203162306a36Sopenharmony_ci{
203262306a36Sopenharmony_ci	struct team *team = netdev_priv(dev);
203362306a36Sopenharmony_ci
203462306a36Sopenharmony_ci	team->user_carrier_enabled = true;
203562306a36Sopenharmony_ci
203662306a36Sopenharmony_ci	if (new_carrier)
203762306a36Sopenharmony_ci		netif_carrier_on(dev);
203862306a36Sopenharmony_ci	else
203962306a36Sopenharmony_ci		netif_carrier_off(dev);
204062306a36Sopenharmony_ci	return 0;
204162306a36Sopenharmony_ci}
204262306a36Sopenharmony_ci
204362306a36Sopenharmony_cistatic const struct net_device_ops team_netdev_ops = {
204462306a36Sopenharmony_ci	.ndo_init		= team_init,
204562306a36Sopenharmony_ci	.ndo_uninit		= team_uninit,
204662306a36Sopenharmony_ci	.ndo_open		= team_open,
204762306a36Sopenharmony_ci	.ndo_stop		= team_close,
204862306a36Sopenharmony_ci	.ndo_start_xmit		= team_xmit,
204962306a36Sopenharmony_ci	.ndo_select_queue	= team_select_queue,
205062306a36Sopenharmony_ci	.ndo_change_rx_flags	= team_change_rx_flags,
205162306a36Sopenharmony_ci	.ndo_set_rx_mode	= team_set_rx_mode,
205262306a36Sopenharmony_ci	.ndo_set_mac_address	= team_set_mac_address,
205362306a36Sopenharmony_ci	.ndo_change_mtu		= team_change_mtu,
205462306a36Sopenharmony_ci	.ndo_get_stats64	= team_get_stats64,
205562306a36Sopenharmony_ci	.ndo_vlan_rx_add_vid	= team_vlan_rx_add_vid,
205662306a36Sopenharmony_ci	.ndo_vlan_rx_kill_vid	= team_vlan_rx_kill_vid,
205762306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER
205862306a36Sopenharmony_ci	.ndo_poll_controller	= team_poll_controller,
205962306a36Sopenharmony_ci	.ndo_netpoll_setup	= team_netpoll_setup,
206062306a36Sopenharmony_ci	.ndo_netpoll_cleanup	= team_netpoll_cleanup,
206162306a36Sopenharmony_ci#endif
206262306a36Sopenharmony_ci	.ndo_add_slave		= team_add_slave,
206362306a36Sopenharmony_ci	.ndo_del_slave		= team_del_slave,
206462306a36Sopenharmony_ci	.ndo_fix_features	= team_fix_features,
206562306a36Sopenharmony_ci	.ndo_change_carrier     = team_change_carrier,
206662306a36Sopenharmony_ci	.ndo_features_check	= passthru_features_check,
206762306a36Sopenharmony_ci};
206862306a36Sopenharmony_ci
206962306a36Sopenharmony_ci/***********************
207062306a36Sopenharmony_ci * ethtool interface
207162306a36Sopenharmony_ci ***********************/
207262306a36Sopenharmony_ci
207362306a36Sopenharmony_cistatic void team_ethtool_get_drvinfo(struct net_device *dev,
207462306a36Sopenharmony_ci				     struct ethtool_drvinfo *drvinfo)
207562306a36Sopenharmony_ci{
207662306a36Sopenharmony_ci	strscpy(drvinfo->driver, DRV_NAME, sizeof(drvinfo->driver));
207762306a36Sopenharmony_ci	strscpy(drvinfo->version, UTS_RELEASE, sizeof(drvinfo->version));
207862306a36Sopenharmony_ci}
207962306a36Sopenharmony_ci
208062306a36Sopenharmony_cistatic int team_ethtool_get_link_ksettings(struct net_device *dev,
208162306a36Sopenharmony_ci					   struct ethtool_link_ksettings *cmd)
208262306a36Sopenharmony_ci{
208362306a36Sopenharmony_ci	struct team *team= netdev_priv(dev);
208462306a36Sopenharmony_ci	unsigned long speed = 0;
208562306a36Sopenharmony_ci	struct team_port *port;
208662306a36Sopenharmony_ci
208762306a36Sopenharmony_ci	cmd->base.duplex = DUPLEX_UNKNOWN;
208862306a36Sopenharmony_ci	cmd->base.port = PORT_OTHER;
208962306a36Sopenharmony_ci
209062306a36Sopenharmony_ci	rcu_read_lock();
209162306a36Sopenharmony_ci	list_for_each_entry_rcu(port, &team->port_list, list) {
209262306a36Sopenharmony_ci		if (team_port_txable(port)) {
209362306a36Sopenharmony_ci			if (port->state.speed != SPEED_UNKNOWN)
209462306a36Sopenharmony_ci				speed += port->state.speed;
209562306a36Sopenharmony_ci			if (cmd->base.duplex == DUPLEX_UNKNOWN &&
209662306a36Sopenharmony_ci			    port->state.duplex != DUPLEX_UNKNOWN)
209762306a36Sopenharmony_ci				cmd->base.duplex = port->state.duplex;
209862306a36Sopenharmony_ci		}
209962306a36Sopenharmony_ci	}
210062306a36Sopenharmony_ci	rcu_read_unlock();
210162306a36Sopenharmony_ci
210262306a36Sopenharmony_ci	cmd->base.speed = speed ? : SPEED_UNKNOWN;
210362306a36Sopenharmony_ci
210462306a36Sopenharmony_ci	return 0;
210562306a36Sopenharmony_ci}
210662306a36Sopenharmony_ci
210762306a36Sopenharmony_cistatic const struct ethtool_ops team_ethtool_ops = {
210862306a36Sopenharmony_ci	.get_drvinfo		= team_ethtool_get_drvinfo,
210962306a36Sopenharmony_ci	.get_link		= ethtool_op_get_link,
211062306a36Sopenharmony_ci	.get_link_ksettings	= team_ethtool_get_link_ksettings,
211162306a36Sopenharmony_ci};
211262306a36Sopenharmony_ci
211362306a36Sopenharmony_ci/***********************
211462306a36Sopenharmony_ci * rt netlink interface
211562306a36Sopenharmony_ci ***********************/
211662306a36Sopenharmony_ci
211762306a36Sopenharmony_cistatic void team_setup_by_port(struct net_device *dev,
211862306a36Sopenharmony_ci			       struct net_device *port_dev)
211962306a36Sopenharmony_ci{
212062306a36Sopenharmony_ci	struct team *team = netdev_priv(dev);
212162306a36Sopenharmony_ci
212262306a36Sopenharmony_ci	if (port_dev->type == ARPHRD_ETHER)
212362306a36Sopenharmony_ci		dev->header_ops	= team->header_ops_cache;
212462306a36Sopenharmony_ci	else
212562306a36Sopenharmony_ci		dev->header_ops	= port_dev->header_ops;
212662306a36Sopenharmony_ci	dev->type = port_dev->type;
212762306a36Sopenharmony_ci	dev->hard_header_len = port_dev->hard_header_len;
212862306a36Sopenharmony_ci	dev->needed_headroom = port_dev->needed_headroom;
212962306a36Sopenharmony_ci	dev->addr_len = port_dev->addr_len;
213062306a36Sopenharmony_ci	dev->mtu = port_dev->mtu;
213162306a36Sopenharmony_ci	memcpy(dev->broadcast, port_dev->broadcast, port_dev->addr_len);
213262306a36Sopenharmony_ci	eth_hw_addr_inherit(dev, port_dev);
213362306a36Sopenharmony_ci
213462306a36Sopenharmony_ci	if (port_dev->flags & IFF_POINTOPOINT) {
213562306a36Sopenharmony_ci		dev->flags &= ~(IFF_BROADCAST | IFF_MULTICAST);
213662306a36Sopenharmony_ci		dev->flags |= (IFF_POINTOPOINT | IFF_NOARP);
213762306a36Sopenharmony_ci	} else if ((port_dev->flags & (IFF_BROADCAST | IFF_MULTICAST)) ==
213862306a36Sopenharmony_ci		    (IFF_BROADCAST | IFF_MULTICAST)) {
213962306a36Sopenharmony_ci		dev->flags |= (IFF_BROADCAST | IFF_MULTICAST);
214062306a36Sopenharmony_ci		dev->flags &= ~(IFF_POINTOPOINT | IFF_NOARP);
214162306a36Sopenharmony_ci	}
214262306a36Sopenharmony_ci}
214362306a36Sopenharmony_ci
214462306a36Sopenharmony_cistatic int team_dev_type_check_change(struct net_device *dev,
214562306a36Sopenharmony_ci				      struct net_device *port_dev)
214662306a36Sopenharmony_ci{
214762306a36Sopenharmony_ci	struct team *team = netdev_priv(dev);
214862306a36Sopenharmony_ci	char *portname = port_dev->name;
214962306a36Sopenharmony_ci	int err;
215062306a36Sopenharmony_ci
215162306a36Sopenharmony_ci	if (dev->type == port_dev->type)
215262306a36Sopenharmony_ci		return 0;
215362306a36Sopenharmony_ci	if (!list_empty(&team->port_list)) {
215462306a36Sopenharmony_ci		netdev_err(dev, "Device %s is of different type\n", portname);
215562306a36Sopenharmony_ci		return -EBUSY;
215662306a36Sopenharmony_ci	}
215762306a36Sopenharmony_ci	err = call_netdevice_notifiers(NETDEV_PRE_TYPE_CHANGE, dev);
215862306a36Sopenharmony_ci	err = notifier_to_errno(err);
215962306a36Sopenharmony_ci	if (err) {
216062306a36Sopenharmony_ci		netdev_err(dev, "Refused to change device type\n");
216162306a36Sopenharmony_ci		return err;
216262306a36Sopenharmony_ci	}
216362306a36Sopenharmony_ci	dev_uc_flush(dev);
216462306a36Sopenharmony_ci	dev_mc_flush(dev);
216562306a36Sopenharmony_ci	team_setup_by_port(dev, port_dev);
216662306a36Sopenharmony_ci	call_netdevice_notifiers(NETDEV_POST_TYPE_CHANGE, dev);
216762306a36Sopenharmony_ci	return 0;
216862306a36Sopenharmony_ci}
216962306a36Sopenharmony_ci
217062306a36Sopenharmony_cistatic void team_setup(struct net_device *dev)
217162306a36Sopenharmony_ci{
217262306a36Sopenharmony_ci	struct team *team = netdev_priv(dev);
217362306a36Sopenharmony_ci
217462306a36Sopenharmony_ci	ether_setup(dev);
217562306a36Sopenharmony_ci	dev->max_mtu = ETH_MAX_MTU;
217662306a36Sopenharmony_ci	team->header_ops_cache = dev->header_ops;
217762306a36Sopenharmony_ci
217862306a36Sopenharmony_ci	dev->netdev_ops = &team_netdev_ops;
217962306a36Sopenharmony_ci	dev->ethtool_ops = &team_ethtool_ops;
218062306a36Sopenharmony_ci	dev->needs_free_netdev = true;
218162306a36Sopenharmony_ci	dev->priv_destructor = team_destructor;
218262306a36Sopenharmony_ci	dev->priv_flags &= ~(IFF_XMIT_DST_RELEASE | IFF_TX_SKB_SHARING);
218362306a36Sopenharmony_ci	dev->priv_flags |= IFF_NO_QUEUE;
218462306a36Sopenharmony_ci	dev->priv_flags |= IFF_TEAM;
218562306a36Sopenharmony_ci
218662306a36Sopenharmony_ci	/*
218762306a36Sopenharmony_ci	 * Indicate we support unicast address filtering. That way core won't
218862306a36Sopenharmony_ci	 * bring us to promisc mode in case a unicast addr is added.
218962306a36Sopenharmony_ci	 * Let this up to underlay drivers.
219062306a36Sopenharmony_ci	 */
219162306a36Sopenharmony_ci	dev->priv_flags |= IFF_UNICAST_FLT | IFF_LIVE_ADDR_CHANGE;
219262306a36Sopenharmony_ci
219362306a36Sopenharmony_ci	dev->features |= NETIF_F_LLTX;
219462306a36Sopenharmony_ci	dev->features |= NETIF_F_GRO;
219562306a36Sopenharmony_ci
219662306a36Sopenharmony_ci	/* Don't allow team devices to change network namespaces. */
219762306a36Sopenharmony_ci	dev->features |= NETIF_F_NETNS_LOCAL;
219862306a36Sopenharmony_ci
219962306a36Sopenharmony_ci	dev->hw_features = TEAM_VLAN_FEATURES |
220062306a36Sopenharmony_ci			   NETIF_F_HW_VLAN_CTAG_RX |
220162306a36Sopenharmony_ci			   NETIF_F_HW_VLAN_CTAG_FILTER |
220262306a36Sopenharmony_ci			   NETIF_F_HW_VLAN_STAG_RX |
220362306a36Sopenharmony_ci			   NETIF_F_HW_VLAN_STAG_FILTER;
220462306a36Sopenharmony_ci
220562306a36Sopenharmony_ci	dev->hw_features |= NETIF_F_GSO_ENCAP_ALL;
220662306a36Sopenharmony_ci	dev->features |= dev->hw_features;
220762306a36Sopenharmony_ci	dev->features |= NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_STAG_TX;
220862306a36Sopenharmony_ci}
220962306a36Sopenharmony_ci
221062306a36Sopenharmony_cistatic int team_newlink(struct net *src_net, struct net_device *dev,
221162306a36Sopenharmony_ci			struct nlattr *tb[], struct nlattr *data[],
221262306a36Sopenharmony_ci			struct netlink_ext_ack *extack)
221362306a36Sopenharmony_ci{
221462306a36Sopenharmony_ci	if (tb[IFLA_ADDRESS] == NULL)
221562306a36Sopenharmony_ci		eth_hw_addr_random(dev);
221662306a36Sopenharmony_ci
221762306a36Sopenharmony_ci	return register_netdevice(dev);
221862306a36Sopenharmony_ci}
221962306a36Sopenharmony_ci
222062306a36Sopenharmony_cistatic int team_validate(struct nlattr *tb[], struct nlattr *data[],
222162306a36Sopenharmony_ci			 struct netlink_ext_ack *extack)
222262306a36Sopenharmony_ci{
222362306a36Sopenharmony_ci	if (tb[IFLA_ADDRESS]) {
222462306a36Sopenharmony_ci		if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN)
222562306a36Sopenharmony_ci			return -EINVAL;
222662306a36Sopenharmony_ci		if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS])))
222762306a36Sopenharmony_ci			return -EADDRNOTAVAIL;
222862306a36Sopenharmony_ci	}
222962306a36Sopenharmony_ci	return 0;
223062306a36Sopenharmony_ci}
223162306a36Sopenharmony_ci
223262306a36Sopenharmony_cistatic unsigned int team_get_num_tx_queues(void)
223362306a36Sopenharmony_ci{
223462306a36Sopenharmony_ci	return TEAM_DEFAULT_NUM_TX_QUEUES;
223562306a36Sopenharmony_ci}
223662306a36Sopenharmony_ci
223762306a36Sopenharmony_cistatic unsigned int team_get_num_rx_queues(void)
223862306a36Sopenharmony_ci{
223962306a36Sopenharmony_ci	return TEAM_DEFAULT_NUM_RX_QUEUES;
224062306a36Sopenharmony_ci}
224162306a36Sopenharmony_ci
224262306a36Sopenharmony_cistatic struct rtnl_link_ops team_link_ops __read_mostly = {
224362306a36Sopenharmony_ci	.kind			= DRV_NAME,
224462306a36Sopenharmony_ci	.priv_size		= sizeof(struct team),
224562306a36Sopenharmony_ci	.setup			= team_setup,
224662306a36Sopenharmony_ci	.newlink		= team_newlink,
224762306a36Sopenharmony_ci	.validate		= team_validate,
224862306a36Sopenharmony_ci	.get_num_tx_queues	= team_get_num_tx_queues,
224962306a36Sopenharmony_ci	.get_num_rx_queues	= team_get_num_rx_queues,
225062306a36Sopenharmony_ci};
225162306a36Sopenharmony_ci
225262306a36Sopenharmony_ci
225362306a36Sopenharmony_ci/***********************************
225462306a36Sopenharmony_ci * Generic netlink custom interface
225562306a36Sopenharmony_ci ***********************************/
225662306a36Sopenharmony_ci
225762306a36Sopenharmony_cistatic struct genl_family team_nl_family;
225862306a36Sopenharmony_ci
225962306a36Sopenharmony_cistatic const struct nla_policy team_nl_policy[TEAM_ATTR_MAX + 1] = {
226062306a36Sopenharmony_ci	[TEAM_ATTR_UNSPEC]			= { .type = NLA_UNSPEC, },
226162306a36Sopenharmony_ci	[TEAM_ATTR_TEAM_IFINDEX]		= { .type = NLA_U32 },
226262306a36Sopenharmony_ci	[TEAM_ATTR_LIST_OPTION]			= { .type = NLA_NESTED },
226362306a36Sopenharmony_ci	[TEAM_ATTR_LIST_PORT]			= { .type = NLA_NESTED },
226462306a36Sopenharmony_ci};
226562306a36Sopenharmony_ci
226662306a36Sopenharmony_cistatic const struct nla_policy
226762306a36Sopenharmony_citeam_nl_option_policy[TEAM_ATTR_OPTION_MAX + 1] = {
226862306a36Sopenharmony_ci	[TEAM_ATTR_OPTION_UNSPEC]		= { .type = NLA_UNSPEC, },
226962306a36Sopenharmony_ci	[TEAM_ATTR_OPTION_NAME] = {
227062306a36Sopenharmony_ci		.type = NLA_STRING,
227162306a36Sopenharmony_ci		.len = TEAM_STRING_MAX_LEN,
227262306a36Sopenharmony_ci	},
227362306a36Sopenharmony_ci	[TEAM_ATTR_OPTION_CHANGED]		= { .type = NLA_FLAG },
227462306a36Sopenharmony_ci	[TEAM_ATTR_OPTION_TYPE]			= { .type = NLA_U8 },
227562306a36Sopenharmony_ci	[TEAM_ATTR_OPTION_DATA]			= { .type = NLA_BINARY },
227662306a36Sopenharmony_ci	[TEAM_ATTR_OPTION_PORT_IFINDEX]		= { .type = NLA_U32 },
227762306a36Sopenharmony_ci	[TEAM_ATTR_OPTION_ARRAY_INDEX]		= { .type = NLA_U32 },
227862306a36Sopenharmony_ci};
227962306a36Sopenharmony_ci
228062306a36Sopenharmony_cistatic int team_nl_cmd_noop(struct sk_buff *skb, struct genl_info *info)
228162306a36Sopenharmony_ci{
228262306a36Sopenharmony_ci	struct sk_buff *msg;
228362306a36Sopenharmony_ci	void *hdr;
228462306a36Sopenharmony_ci	int err;
228562306a36Sopenharmony_ci
228662306a36Sopenharmony_ci	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
228762306a36Sopenharmony_ci	if (!msg)
228862306a36Sopenharmony_ci		return -ENOMEM;
228962306a36Sopenharmony_ci
229062306a36Sopenharmony_ci	hdr = genlmsg_put(msg, info->snd_portid, info->snd_seq,
229162306a36Sopenharmony_ci			  &team_nl_family, 0, TEAM_CMD_NOOP);
229262306a36Sopenharmony_ci	if (!hdr) {
229362306a36Sopenharmony_ci		err = -EMSGSIZE;
229462306a36Sopenharmony_ci		goto err_msg_put;
229562306a36Sopenharmony_ci	}
229662306a36Sopenharmony_ci
229762306a36Sopenharmony_ci	genlmsg_end(msg, hdr);
229862306a36Sopenharmony_ci
229962306a36Sopenharmony_ci	return genlmsg_unicast(genl_info_net(info), msg, info->snd_portid);
230062306a36Sopenharmony_ci
230162306a36Sopenharmony_cierr_msg_put:
230262306a36Sopenharmony_ci	nlmsg_free(msg);
230362306a36Sopenharmony_ci
230462306a36Sopenharmony_ci	return err;
230562306a36Sopenharmony_ci}
230662306a36Sopenharmony_ci
230762306a36Sopenharmony_ci/*
230862306a36Sopenharmony_ci * Netlink cmd functions should be locked by following two functions.
230962306a36Sopenharmony_ci * Since dev gets held here, that ensures dev won't disappear in between.
231062306a36Sopenharmony_ci */
231162306a36Sopenharmony_cistatic struct team *team_nl_team_get(struct genl_info *info)
231262306a36Sopenharmony_ci{
231362306a36Sopenharmony_ci	struct net *net = genl_info_net(info);
231462306a36Sopenharmony_ci	int ifindex;
231562306a36Sopenharmony_ci	struct net_device *dev;
231662306a36Sopenharmony_ci	struct team *team;
231762306a36Sopenharmony_ci
231862306a36Sopenharmony_ci	if (!info->attrs[TEAM_ATTR_TEAM_IFINDEX])
231962306a36Sopenharmony_ci		return NULL;
232062306a36Sopenharmony_ci
232162306a36Sopenharmony_ci	ifindex = nla_get_u32(info->attrs[TEAM_ATTR_TEAM_IFINDEX]);
232262306a36Sopenharmony_ci	dev = dev_get_by_index(net, ifindex);
232362306a36Sopenharmony_ci	if (!dev || dev->netdev_ops != &team_netdev_ops) {
232462306a36Sopenharmony_ci		dev_put(dev);
232562306a36Sopenharmony_ci		return NULL;
232662306a36Sopenharmony_ci	}
232762306a36Sopenharmony_ci
232862306a36Sopenharmony_ci	team = netdev_priv(dev);
232962306a36Sopenharmony_ci	mutex_lock(&team->lock);
233062306a36Sopenharmony_ci	return team;
233162306a36Sopenharmony_ci}
233262306a36Sopenharmony_ci
233362306a36Sopenharmony_cistatic void team_nl_team_put(struct team *team)
233462306a36Sopenharmony_ci{
233562306a36Sopenharmony_ci	mutex_unlock(&team->lock);
233662306a36Sopenharmony_ci	dev_put(team->dev);
233762306a36Sopenharmony_ci}
233862306a36Sopenharmony_ci
233962306a36Sopenharmony_citypedef int team_nl_send_func_t(struct sk_buff *skb,
234062306a36Sopenharmony_ci				struct team *team, u32 portid);
234162306a36Sopenharmony_ci
234262306a36Sopenharmony_cistatic int team_nl_send_unicast(struct sk_buff *skb, struct team *team, u32 portid)
234362306a36Sopenharmony_ci{
234462306a36Sopenharmony_ci	return genlmsg_unicast(dev_net(team->dev), skb, portid);
234562306a36Sopenharmony_ci}
234662306a36Sopenharmony_ci
234762306a36Sopenharmony_cistatic int team_nl_fill_one_option_get(struct sk_buff *skb, struct team *team,
234862306a36Sopenharmony_ci				       struct team_option_inst *opt_inst)
234962306a36Sopenharmony_ci{
235062306a36Sopenharmony_ci	struct nlattr *option_item;
235162306a36Sopenharmony_ci	struct team_option *option = opt_inst->option;
235262306a36Sopenharmony_ci	struct team_option_inst_info *opt_inst_info = &opt_inst->info;
235362306a36Sopenharmony_ci	struct team_gsetter_ctx ctx;
235462306a36Sopenharmony_ci	int err;
235562306a36Sopenharmony_ci
235662306a36Sopenharmony_ci	ctx.info = opt_inst_info;
235762306a36Sopenharmony_ci	err = team_option_get(team, opt_inst, &ctx);
235862306a36Sopenharmony_ci	if (err)
235962306a36Sopenharmony_ci		return err;
236062306a36Sopenharmony_ci
236162306a36Sopenharmony_ci	option_item = nla_nest_start_noflag(skb, TEAM_ATTR_ITEM_OPTION);
236262306a36Sopenharmony_ci	if (!option_item)
236362306a36Sopenharmony_ci		return -EMSGSIZE;
236462306a36Sopenharmony_ci
236562306a36Sopenharmony_ci	if (nla_put_string(skb, TEAM_ATTR_OPTION_NAME, option->name))
236662306a36Sopenharmony_ci		goto nest_cancel;
236762306a36Sopenharmony_ci	if (opt_inst_info->port &&
236862306a36Sopenharmony_ci	    nla_put_u32(skb, TEAM_ATTR_OPTION_PORT_IFINDEX,
236962306a36Sopenharmony_ci			opt_inst_info->port->dev->ifindex))
237062306a36Sopenharmony_ci		goto nest_cancel;
237162306a36Sopenharmony_ci	if (opt_inst->option->array_size &&
237262306a36Sopenharmony_ci	    nla_put_u32(skb, TEAM_ATTR_OPTION_ARRAY_INDEX,
237362306a36Sopenharmony_ci			opt_inst_info->array_index))
237462306a36Sopenharmony_ci		goto nest_cancel;
237562306a36Sopenharmony_ci
237662306a36Sopenharmony_ci	switch (option->type) {
237762306a36Sopenharmony_ci	case TEAM_OPTION_TYPE_U32:
237862306a36Sopenharmony_ci		if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_U32))
237962306a36Sopenharmony_ci			goto nest_cancel;
238062306a36Sopenharmony_ci		if (nla_put_u32(skb, TEAM_ATTR_OPTION_DATA, ctx.data.u32_val))
238162306a36Sopenharmony_ci			goto nest_cancel;
238262306a36Sopenharmony_ci		break;
238362306a36Sopenharmony_ci	case TEAM_OPTION_TYPE_STRING:
238462306a36Sopenharmony_ci		if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_STRING))
238562306a36Sopenharmony_ci			goto nest_cancel;
238662306a36Sopenharmony_ci		if (nla_put_string(skb, TEAM_ATTR_OPTION_DATA,
238762306a36Sopenharmony_ci				   ctx.data.str_val))
238862306a36Sopenharmony_ci			goto nest_cancel;
238962306a36Sopenharmony_ci		break;
239062306a36Sopenharmony_ci	case TEAM_OPTION_TYPE_BINARY:
239162306a36Sopenharmony_ci		if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_BINARY))
239262306a36Sopenharmony_ci			goto nest_cancel;
239362306a36Sopenharmony_ci		if (nla_put(skb, TEAM_ATTR_OPTION_DATA, ctx.data.bin_val.len,
239462306a36Sopenharmony_ci			    ctx.data.bin_val.ptr))
239562306a36Sopenharmony_ci			goto nest_cancel;
239662306a36Sopenharmony_ci		break;
239762306a36Sopenharmony_ci	case TEAM_OPTION_TYPE_BOOL:
239862306a36Sopenharmony_ci		if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_FLAG))
239962306a36Sopenharmony_ci			goto nest_cancel;
240062306a36Sopenharmony_ci		if (ctx.data.bool_val &&
240162306a36Sopenharmony_ci		    nla_put_flag(skb, TEAM_ATTR_OPTION_DATA))
240262306a36Sopenharmony_ci			goto nest_cancel;
240362306a36Sopenharmony_ci		break;
240462306a36Sopenharmony_ci	case TEAM_OPTION_TYPE_S32:
240562306a36Sopenharmony_ci		if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_S32))
240662306a36Sopenharmony_ci			goto nest_cancel;
240762306a36Sopenharmony_ci		if (nla_put_s32(skb, TEAM_ATTR_OPTION_DATA, ctx.data.s32_val))
240862306a36Sopenharmony_ci			goto nest_cancel;
240962306a36Sopenharmony_ci		break;
241062306a36Sopenharmony_ci	default:
241162306a36Sopenharmony_ci		BUG();
241262306a36Sopenharmony_ci	}
241362306a36Sopenharmony_ci	if (opt_inst->removed && nla_put_flag(skb, TEAM_ATTR_OPTION_REMOVED))
241462306a36Sopenharmony_ci		goto nest_cancel;
241562306a36Sopenharmony_ci	if (opt_inst->changed) {
241662306a36Sopenharmony_ci		if (nla_put_flag(skb, TEAM_ATTR_OPTION_CHANGED))
241762306a36Sopenharmony_ci			goto nest_cancel;
241862306a36Sopenharmony_ci		opt_inst->changed = false;
241962306a36Sopenharmony_ci	}
242062306a36Sopenharmony_ci	nla_nest_end(skb, option_item);
242162306a36Sopenharmony_ci	return 0;
242262306a36Sopenharmony_ci
242362306a36Sopenharmony_cinest_cancel:
242462306a36Sopenharmony_ci	nla_nest_cancel(skb, option_item);
242562306a36Sopenharmony_ci	return -EMSGSIZE;
242662306a36Sopenharmony_ci}
242762306a36Sopenharmony_ci
242862306a36Sopenharmony_cistatic int __send_and_alloc_skb(struct sk_buff **pskb,
242962306a36Sopenharmony_ci				struct team *team, u32 portid,
243062306a36Sopenharmony_ci				team_nl_send_func_t *send_func)
243162306a36Sopenharmony_ci{
243262306a36Sopenharmony_ci	int err;
243362306a36Sopenharmony_ci
243462306a36Sopenharmony_ci	if (*pskb) {
243562306a36Sopenharmony_ci		err = send_func(*pskb, team, portid);
243662306a36Sopenharmony_ci		if (err)
243762306a36Sopenharmony_ci			return err;
243862306a36Sopenharmony_ci	}
243962306a36Sopenharmony_ci	*pskb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
244062306a36Sopenharmony_ci	if (!*pskb)
244162306a36Sopenharmony_ci		return -ENOMEM;
244262306a36Sopenharmony_ci	return 0;
244362306a36Sopenharmony_ci}
244462306a36Sopenharmony_ci
244562306a36Sopenharmony_cistatic int team_nl_send_options_get(struct team *team, u32 portid, u32 seq,
244662306a36Sopenharmony_ci				    int flags, team_nl_send_func_t *send_func,
244762306a36Sopenharmony_ci				    struct list_head *sel_opt_inst_list)
244862306a36Sopenharmony_ci{
244962306a36Sopenharmony_ci	struct nlattr *option_list;
245062306a36Sopenharmony_ci	struct nlmsghdr *nlh;
245162306a36Sopenharmony_ci	void *hdr;
245262306a36Sopenharmony_ci	struct team_option_inst *opt_inst;
245362306a36Sopenharmony_ci	int err;
245462306a36Sopenharmony_ci	struct sk_buff *skb = NULL;
245562306a36Sopenharmony_ci	bool incomplete;
245662306a36Sopenharmony_ci	int i;
245762306a36Sopenharmony_ci
245862306a36Sopenharmony_ci	opt_inst = list_first_entry(sel_opt_inst_list,
245962306a36Sopenharmony_ci				    struct team_option_inst, tmp_list);
246062306a36Sopenharmony_ci
246162306a36Sopenharmony_cistart_again:
246262306a36Sopenharmony_ci	err = __send_and_alloc_skb(&skb, team, portid, send_func);
246362306a36Sopenharmony_ci	if (err)
246462306a36Sopenharmony_ci		return err;
246562306a36Sopenharmony_ci
246662306a36Sopenharmony_ci	hdr = genlmsg_put(skb, portid, seq, &team_nl_family, flags | NLM_F_MULTI,
246762306a36Sopenharmony_ci			  TEAM_CMD_OPTIONS_GET);
246862306a36Sopenharmony_ci	if (!hdr) {
246962306a36Sopenharmony_ci		nlmsg_free(skb);
247062306a36Sopenharmony_ci		return -EMSGSIZE;
247162306a36Sopenharmony_ci	}
247262306a36Sopenharmony_ci
247362306a36Sopenharmony_ci	if (nla_put_u32(skb, TEAM_ATTR_TEAM_IFINDEX, team->dev->ifindex))
247462306a36Sopenharmony_ci		goto nla_put_failure;
247562306a36Sopenharmony_ci	option_list = nla_nest_start_noflag(skb, TEAM_ATTR_LIST_OPTION);
247662306a36Sopenharmony_ci	if (!option_list)
247762306a36Sopenharmony_ci		goto nla_put_failure;
247862306a36Sopenharmony_ci
247962306a36Sopenharmony_ci	i = 0;
248062306a36Sopenharmony_ci	incomplete = false;
248162306a36Sopenharmony_ci	list_for_each_entry_from(opt_inst, sel_opt_inst_list, tmp_list) {
248262306a36Sopenharmony_ci		err = team_nl_fill_one_option_get(skb, team, opt_inst);
248362306a36Sopenharmony_ci		if (err) {
248462306a36Sopenharmony_ci			if (err == -EMSGSIZE) {
248562306a36Sopenharmony_ci				if (!i)
248662306a36Sopenharmony_ci					goto errout;
248762306a36Sopenharmony_ci				incomplete = true;
248862306a36Sopenharmony_ci				break;
248962306a36Sopenharmony_ci			}
249062306a36Sopenharmony_ci			goto errout;
249162306a36Sopenharmony_ci		}
249262306a36Sopenharmony_ci		i++;
249362306a36Sopenharmony_ci	}
249462306a36Sopenharmony_ci
249562306a36Sopenharmony_ci	nla_nest_end(skb, option_list);
249662306a36Sopenharmony_ci	genlmsg_end(skb, hdr);
249762306a36Sopenharmony_ci	if (incomplete)
249862306a36Sopenharmony_ci		goto start_again;
249962306a36Sopenharmony_ci
250062306a36Sopenharmony_cisend_done:
250162306a36Sopenharmony_ci	nlh = nlmsg_put(skb, portid, seq, NLMSG_DONE, 0, flags | NLM_F_MULTI);
250262306a36Sopenharmony_ci	if (!nlh) {
250362306a36Sopenharmony_ci		err = __send_and_alloc_skb(&skb, team, portid, send_func);
250462306a36Sopenharmony_ci		if (err)
250562306a36Sopenharmony_ci			return err;
250662306a36Sopenharmony_ci		goto send_done;
250762306a36Sopenharmony_ci	}
250862306a36Sopenharmony_ci
250962306a36Sopenharmony_ci	return send_func(skb, team, portid);
251062306a36Sopenharmony_ci
251162306a36Sopenharmony_cinla_put_failure:
251262306a36Sopenharmony_ci	err = -EMSGSIZE;
251362306a36Sopenharmony_cierrout:
251462306a36Sopenharmony_ci	nlmsg_free(skb);
251562306a36Sopenharmony_ci	return err;
251662306a36Sopenharmony_ci}
251762306a36Sopenharmony_ci
251862306a36Sopenharmony_cistatic int team_nl_cmd_options_get(struct sk_buff *skb, struct genl_info *info)
251962306a36Sopenharmony_ci{
252062306a36Sopenharmony_ci	struct team *team;
252162306a36Sopenharmony_ci	struct team_option_inst *opt_inst;
252262306a36Sopenharmony_ci	int err;
252362306a36Sopenharmony_ci	LIST_HEAD(sel_opt_inst_list);
252462306a36Sopenharmony_ci
252562306a36Sopenharmony_ci	team = team_nl_team_get(info);
252662306a36Sopenharmony_ci	if (!team)
252762306a36Sopenharmony_ci		return -EINVAL;
252862306a36Sopenharmony_ci
252962306a36Sopenharmony_ci	list_for_each_entry(opt_inst, &team->option_inst_list, list)
253062306a36Sopenharmony_ci		list_add_tail(&opt_inst->tmp_list, &sel_opt_inst_list);
253162306a36Sopenharmony_ci	err = team_nl_send_options_get(team, info->snd_portid, info->snd_seq,
253262306a36Sopenharmony_ci				       NLM_F_ACK, team_nl_send_unicast,
253362306a36Sopenharmony_ci				       &sel_opt_inst_list);
253462306a36Sopenharmony_ci
253562306a36Sopenharmony_ci	team_nl_team_put(team);
253662306a36Sopenharmony_ci
253762306a36Sopenharmony_ci	return err;
253862306a36Sopenharmony_ci}
253962306a36Sopenharmony_ci
254062306a36Sopenharmony_cistatic int team_nl_send_event_options_get(struct team *team,
254162306a36Sopenharmony_ci					  struct list_head *sel_opt_inst_list);
254262306a36Sopenharmony_ci
254362306a36Sopenharmony_cistatic int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info)
254462306a36Sopenharmony_ci{
254562306a36Sopenharmony_ci	struct team *team;
254662306a36Sopenharmony_ci	int err = 0;
254762306a36Sopenharmony_ci	int i;
254862306a36Sopenharmony_ci	struct nlattr *nl_option;
254962306a36Sopenharmony_ci
255062306a36Sopenharmony_ci	rtnl_lock();
255162306a36Sopenharmony_ci
255262306a36Sopenharmony_ci	team = team_nl_team_get(info);
255362306a36Sopenharmony_ci	if (!team) {
255462306a36Sopenharmony_ci		err = -EINVAL;
255562306a36Sopenharmony_ci		goto rtnl_unlock;
255662306a36Sopenharmony_ci	}
255762306a36Sopenharmony_ci
255862306a36Sopenharmony_ci	err = -EINVAL;
255962306a36Sopenharmony_ci	if (!info->attrs[TEAM_ATTR_LIST_OPTION]) {
256062306a36Sopenharmony_ci		err = -EINVAL;
256162306a36Sopenharmony_ci		goto team_put;
256262306a36Sopenharmony_ci	}
256362306a36Sopenharmony_ci
256462306a36Sopenharmony_ci	nla_for_each_nested(nl_option, info->attrs[TEAM_ATTR_LIST_OPTION], i) {
256562306a36Sopenharmony_ci		struct nlattr *opt_attrs[TEAM_ATTR_OPTION_MAX + 1];
256662306a36Sopenharmony_ci		struct nlattr *attr;
256762306a36Sopenharmony_ci		struct nlattr *attr_data;
256862306a36Sopenharmony_ci		LIST_HEAD(opt_inst_list);
256962306a36Sopenharmony_ci		enum team_option_type opt_type;
257062306a36Sopenharmony_ci		int opt_port_ifindex = 0; /* != 0 for per-port options */
257162306a36Sopenharmony_ci		u32 opt_array_index = 0;
257262306a36Sopenharmony_ci		bool opt_is_array = false;
257362306a36Sopenharmony_ci		struct team_option_inst *opt_inst;
257462306a36Sopenharmony_ci		char *opt_name;
257562306a36Sopenharmony_ci		bool opt_found = false;
257662306a36Sopenharmony_ci
257762306a36Sopenharmony_ci		if (nla_type(nl_option) != TEAM_ATTR_ITEM_OPTION) {
257862306a36Sopenharmony_ci			err = -EINVAL;
257962306a36Sopenharmony_ci			goto team_put;
258062306a36Sopenharmony_ci		}
258162306a36Sopenharmony_ci		err = nla_parse_nested_deprecated(opt_attrs,
258262306a36Sopenharmony_ci						  TEAM_ATTR_OPTION_MAX,
258362306a36Sopenharmony_ci						  nl_option,
258462306a36Sopenharmony_ci						  team_nl_option_policy,
258562306a36Sopenharmony_ci						  info->extack);
258662306a36Sopenharmony_ci		if (err)
258762306a36Sopenharmony_ci			goto team_put;
258862306a36Sopenharmony_ci		if (!opt_attrs[TEAM_ATTR_OPTION_NAME] ||
258962306a36Sopenharmony_ci		    !opt_attrs[TEAM_ATTR_OPTION_TYPE]) {
259062306a36Sopenharmony_ci			err = -EINVAL;
259162306a36Sopenharmony_ci			goto team_put;
259262306a36Sopenharmony_ci		}
259362306a36Sopenharmony_ci		switch (nla_get_u8(opt_attrs[TEAM_ATTR_OPTION_TYPE])) {
259462306a36Sopenharmony_ci		case NLA_U32:
259562306a36Sopenharmony_ci			opt_type = TEAM_OPTION_TYPE_U32;
259662306a36Sopenharmony_ci			break;
259762306a36Sopenharmony_ci		case NLA_STRING:
259862306a36Sopenharmony_ci			opt_type = TEAM_OPTION_TYPE_STRING;
259962306a36Sopenharmony_ci			break;
260062306a36Sopenharmony_ci		case NLA_BINARY:
260162306a36Sopenharmony_ci			opt_type = TEAM_OPTION_TYPE_BINARY;
260262306a36Sopenharmony_ci			break;
260362306a36Sopenharmony_ci		case NLA_FLAG:
260462306a36Sopenharmony_ci			opt_type = TEAM_OPTION_TYPE_BOOL;
260562306a36Sopenharmony_ci			break;
260662306a36Sopenharmony_ci		case NLA_S32:
260762306a36Sopenharmony_ci			opt_type = TEAM_OPTION_TYPE_S32;
260862306a36Sopenharmony_ci			break;
260962306a36Sopenharmony_ci		default:
261062306a36Sopenharmony_ci			goto team_put;
261162306a36Sopenharmony_ci		}
261262306a36Sopenharmony_ci
261362306a36Sopenharmony_ci		attr_data = opt_attrs[TEAM_ATTR_OPTION_DATA];
261462306a36Sopenharmony_ci		if (opt_type != TEAM_OPTION_TYPE_BOOL && !attr_data) {
261562306a36Sopenharmony_ci			err = -EINVAL;
261662306a36Sopenharmony_ci			goto team_put;
261762306a36Sopenharmony_ci		}
261862306a36Sopenharmony_ci
261962306a36Sopenharmony_ci		opt_name = nla_data(opt_attrs[TEAM_ATTR_OPTION_NAME]);
262062306a36Sopenharmony_ci		attr = opt_attrs[TEAM_ATTR_OPTION_PORT_IFINDEX];
262162306a36Sopenharmony_ci		if (attr)
262262306a36Sopenharmony_ci			opt_port_ifindex = nla_get_u32(attr);
262362306a36Sopenharmony_ci
262462306a36Sopenharmony_ci		attr = opt_attrs[TEAM_ATTR_OPTION_ARRAY_INDEX];
262562306a36Sopenharmony_ci		if (attr) {
262662306a36Sopenharmony_ci			opt_is_array = true;
262762306a36Sopenharmony_ci			opt_array_index = nla_get_u32(attr);
262862306a36Sopenharmony_ci		}
262962306a36Sopenharmony_ci
263062306a36Sopenharmony_ci		list_for_each_entry(opt_inst, &team->option_inst_list, list) {
263162306a36Sopenharmony_ci			struct team_option *option = opt_inst->option;
263262306a36Sopenharmony_ci			struct team_gsetter_ctx ctx;
263362306a36Sopenharmony_ci			struct team_option_inst_info *opt_inst_info;
263462306a36Sopenharmony_ci			int tmp_ifindex;
263562306a36Sopenharmony_ci
263662306a36Sopenharmony_ci			opt_inst_info = &opt_inst->info;
263762306a36Sopenharmony_ci			tmp_ifindex = opt_inst_info->port ?
263862306a36Sopenharmony_ci				      opt_inst_info->port->dev->ifindex : 0;
263962306a36Sopenharmony_ci			if (option->type != opt_type ||
264062306a36Sopenharmony_ci			    strcmp(option->name, opt_name) ||
264162306a36Sopenharmony_ci			    tmp_ifindex != opt_port_ifindex ||
264262306a36Sopenharmony_ci			    (option->array_size && !opt_is_array) ||
264362306a36Sopenharmony_ci			    opt_inst_info->array_index != opt_array_index)
264462306a36Sopenharmony_ci				continue;
264562306a36Sopenharmony_ci			opt_found = true;
264662306a36Sopenharmony_ci			ctx.info = opt_inst_info;
264762306a36Sopenharmony_ci			switch (opt_type) {
264862306a36Sopenharmony_ci			case TEAM_OPTION_TYPE_U32:
264962306a36Sopenharmony_ci				ctx.data.u32_val = nla_get_u32(attr_data);
265062306a36Sopenharmony_ci				break;
265162306a36Sopenharmony_ci			case TEAM_OPTION_TYPE_STRING:
265262306a36Sopenharmony_ci				if (nla_len(attr_data) > TEAM_STRING_MAX_LEN) {
265362306a36Sopenharmony_ci					err = -EINVAL;
265462306a36Sopenharmony_ci					goto team_put;
265562306a36Sopenharmony_ci				}
265662306a36Sopenharmony_ci				ctx.data.str_val = nla_data(attr_data);
265762306a36Sopenharmony_ci				break;
265862306a36Sopenharmony_ci			case TEAM_OPTION_TYPE_BINARY:
265962306a36Sopenharmony_ci				ctx.data.bin_val.len = nla_len(attr_data);
266062306a36Sopenharmony_ci				ctx.data.bin_val.ptr = nla_data(attr_data);
266162306a36Sopenharmony_ci				break;
266262306a36Sopenharmony_ci			case TEAM_OPTION_TYPE_BOOL:
266362306a36Sopenharmony_ci				ctx.data.bool_val = attr_data ? true : false;
266462306a36Sopenharmony_ci				break;
266562306a36Sopenharmony_ci			case TEAM_OPTION_TYPE_S32:
266662306a36Sopenharmony_ci				ctx.data.s32_val = nla_get_s32(attr_data);
266762306a36Sopenharmony_ci				break;
266862306a36Sopenharmony_ci			default:
266962306a36Sopenharmony_ci				BUG();
267062306a36Sopenharmony_ci			}
267162306a36Sopenharmony_ci			err = team_option_set(team, opt_inst, &ctx);
267262306a36Sopenharmony_ci			if (err)
267362306a36Sopenharmony_ci				goto team_put;
267462306a36Sopenharmony_ci			opt_inst->changed = true;
267562306a36Sopenharmony_ci			list_add(&opt_inst->tmp_list, &opt_inst_list);
267662306a36Sopenharmony_ci		}
267762306a36Sopenharmony_ci		if (!opt_found) {
267862306a36Sopenharmony_ci			err = -ENOENT;
267962306a36Sopenharmony_ci			goto team_put;
268062306a36Sopenharmony_ci		}
268162306a36Sopenharmony_ci
268262306a36Sopenharmony_ci		err = team_nl_send_event_options_get(team, &opt_inst_list);
268362306a36Sopenharmony_ci		if (err)
268462306a36Sopenharmony_ci			break;
268562306a36Sopenharmony_ci	}
268662306a36Sopenharmony_ci
268762306a36Sopenharmony_citeam_put:
268862306a36Sopenharmony_ci	team_nl_team_put(team);
268962306a36Sopenharmony_cirtnl_unlock:
269062306a36Sopenharmony_ci	rtnl_unlock();
269162306a36Sopenharmony_ci	return err;
269262306a36Sopenharmony_ci}
269362306a36Sopenharmony_ci
269462306a36Sopenharmony_cistatic int team_nl_fill_one_port_get(struct sk_buff *skb,
269562306a36Sopenharmony_ci				     struct team_port *port)
269662306a36Sopenharmony_ci{
269762306a36Sopenharmony_ci	struct nlattr *port_item;
269862306a36Sopenharmony_ci
269962306a36Sopenharmony_ci	port_item = nla_nest_start_noflag(skb, TEAM_ATTR_ITEM_PORT);
270062306a36Sopenharmony_ci	if (!port_item)
270162306a36Sopenharmony_ci		goto nest_cancel;
270262306a36Sopenharmony_ci	if (nla_put_u32(skb, TEAM_ATTR_PORT_IFINDEX, port->dev->ifindex))
270362306a36Sopenharmony_ci		goto nest_cancel;
270462306a36Sopenharmony_ci	if (port->changed) {
270562306a36Sopenharmony_ci		if (nla_put_flag(skb, TEAM_ATTR_PORT_CHANGED))
270662306a36Sopenharmony_ci			goto nest_cancel;
270762306a36Sopenharmony_ci		port->changed = false;
270862306a36Sopenharmony_ci	}
270962306a36Sopenharmony_ci	if ((port->removed &&
271062306a36Sopenharmony_ci	     nla_put_flag(skb, TEAM_ATTR_PORT_REMOVED)) ||
271162306a36Sopenharmony_ci	    (port->state.linkup &&
271262306a36Sopenharmony_ci	     nla_put_flag(skb, TEAM_ATTR_PORT_LINKUP)) ||
271362306a36Sopenharmony_ci	    nla_put_u32(skb, TEAM_ATTR_PORT_SPEED, port->state.speed) ||
271462306a36Sopenharmony_ci	    nla_put_u8(skb, TEAM_ATTR_PORT_DUPLEX, port->state.duplex))
271562306a36Sopenharmony_ci		goto nest_cancel;
271662306a36Sopenharmony_ci	nla_nest_end(skb, port_item);
271762306a36Sopenharmony_ci	return 0;
271862306a36Sopenharmony_ci
271962306a36Sopenharmony_cinest_cancel:
272062306a36Sopenharmony_ci	nla_nest_cancel(skb, port_item);
272162306a36Sopenharmony_ci	return -EMSGSIZE;
272262306a36Sopenharmony_ci}
272362306a36Sopenharmony_ci
272462306a36Sopenharmony_cistatic int team_nl_send_port_list_get(struct team *team, u32 portid, u32 seq,
272562306a36Sopenharmony_ci				      int flags, team_nl_send_func_t *send_func,
272662306a36Sopenharmony_ci				      struct team_port *one_port)
272762306a36Sopenharmony_ci{
272862306a36Sopenharmony_ci	struct nlattr *port_list;
272962306a36Sopenharmony_ci	struct nlmsghdr *nlh;
273062306a36Sopenharmony_ci	void *hdr;
273162306a36Sopenharmony_ci	struct team_port *port;
273262306a36Sopenharmony_ci	int err;
273362306a36Sopenharmony_ci	struct sk_buff *skb = NULL;
273462306a36Sopenharmony_ci	bool incomplete;
273562306a36Sopenharmony_ci	int i;
273662306a36Sopenharmony_ci
273762306a36Sopenharmony_ci	port = list_first_entry_or_null(&team->port_list,
273862306a36Sopenharmony_ci					struct team_port, list);
273962306a36Sopenharmony_ci
274062306a36Sopenharmony_cistart_again:
274162306a36Sopenharmony_ci	err = __send_and_alloc_skb(&skb, team, portid, send_func);
274262306a36Sopenharmony_ci	if (err)
274362306a36Sopenharmony_ci		return err;
274462306a36Sopenharmony_ci
274562306a36Sopenharmony_ci	hdr = genlmsg_put(skb, portid, seq, &team_nl_family, flags | NLM_F_MULTI,
274662306a36Sopenharmony_ci			  TEAM_CMD_PORT_LIST_GET);
274762306a36Sopenharmony_ci	if (!hdr) {
274862306a36Sopenharmony_ci		nlmsg_free(skb);
274962306a36Sopenharmony_ci		return -EMSGSIZE;
275062306a36Sopenharmony_ci	}
275162306a36Sopenharmony_ci
275262306a36Sopenharmony_ci	if (nla_put_u32(skb, TEAM_ATTR_TEAM_IFINDEX, team->dev->ifindex))
275362306a36Sopenharmony_ci		goto nla_put_failure;
275462306a36Sopenharmony_ci	port_list = nla_nest_start_noflag(skb, TEAM_ATTR_LIST_PORT);
275562306a36Sopenharmony_ci	if (!port_list)
275662306a36Sopenharmony_ci		goto nla_put_failure;
275762306a36Sopenharmony_ci
275862306a36Sopenharmony_ci	i = 0;
275962306a36Sopenharmony_ci	incomplete = false;
276062306a36Sopenharmony_ci
276162306a36Sopenharmony_ci	/* If one port is selected, called wants to send port list containing
276262306a36Sopenharmony_ci	 * only this port. Otherwise go through all listed ports and send all
276362306a36Sopenharmony_ci	 */
276462306a36Sopenharmony_ci	if (one_port) {
276562306a36Sopenharmony_ci		err = team_nl_fill_one_port_get(skb, one_port);
276662306a36Sopenharmony_ci		if (err)
276762306a36Sopenharmony_ci			goto errout;
276862306a36Sopenharmony_ci	} else if (port) {
276962306a36Sopenharmony_ci		list_for_each_entry_from(port, &team->port_list, list) {
277062306a36Sopenharmony_ci			err = team_nl_fill_one_port_get(skb, port);
277162306a36Sopenharmony_ci			if (err) {
277262306a36Sopenharmony_ci				if (err == -EMSGSIZE) {
277362306a36Sopenharmony_ci					if (!i)
277462306a36Sopenharmony_ci						goto errout;
277562306a36Sopenharmony_ci					incomplete = true;
277662306a36Sopenharmony_ci					break;
277762306a36Sopenharmony_ci				}
277862306a36Sopenharmony_ci				goto errout;
277962306a36Sopenharmony_ci			}
278062306a36Sopenharmony_ci			i++;
278162306a36Sopenharmony_ci		}
278262306a36Sopenharmony_ci	}
278362306a36Sopenharmony_ci
278462306a36Sopenharmony_ci	nla_nest_end(skb, port_list);
278562306a36Sopenharmony_ci	genlmsg_end(skb, hdr);
278662306a36Sopenharmony_ci	if (incomplete)
278762306a36Sopenharmony_ci		goto start_again;
278862306a36Sopenharmony_ci
278962306a36Sopenharmony_cisend_done:
279062306a36Sopenharmony_ci	nlh = nlmsg_put(skb, portid, seq, NLMSG_DONE, 0, flags | NLM_F_MULTI);
279162306a36Sopenharmony_ci	if (!nlh) {
279262306a36Sopenharmony_ci		err = __send_and_alloc_skb(&skb, team, portid, send_func);
279362306a36Sopenharmony_ci		if (err)
279462306a36Sopenharmony_ci			return err;
279562306a36Sopenharmony_ci		goto send_done;
279662306a36Sopenharmony_ci	}
279762306a36Sopenharmony_ci
279862306a36Sopenharmony_ci	return send_func(skb, team, portid);
279962306a36Sopenharmony_ci
280062306a36Sopenharmony_cinla_put_failure:
280162306a36Sopenharmony_ci	err = -EMSGSIZE;
280262306a36Sopenharmony_cierrout:
280362306a36Sopenharmony_ci	nlmsg_free(skb);
280462306a36Sopenharmony_ci	return err;
280562306a36Sopenharmony_ci}
280662306a36Sopenharmony_ci
280762306a36Sopenharmony_cistatic int team_nl_cmd_port_list_get(struct sk_buff *skb,
280862306a36Sopenharmony_ci				     struct genl_info *info)
280962306a36Sopenharmony_ci{
281062306a36Sopenharmony_ci	struct team *team;
281162306a36Sopenharmony_ci	int err;
281262306a36Sopenharmony_ci
281362306a36Sopenharmony_ci	team = team_nl_team_get(info);
281462306a36Sopenharmony_ci	if (!team)
281562306a36Sopenharmony_ci		return -EINVAL;
281662306a36Sopenharmony_ci
281762306a36Sopenharmony_ci	err = team_nl_send_port_list_get(team, info->snd_portid, info->snd_seq,
281862306a36Sopenharmony_ci					 NLM_F_ACK, team_nl_send_unicast, NULL);
281962306a36Sopenharmony_ci
282062306a36Sopenharmony_ci	team_nl_team_put(team);
282162306a36Sopenharmony_ci
282262306a36Sopenharmony_ci	return err;
282362306a36Sopenharmony_ci}
282462306a36Sopenharmony_ci
282562306a36Sopenharmony_cistatic const struct genl_small_ops team_nl_ops[] = {
282662306a36Sopenharmony_ci	{
282762306a36Sopenharmony_ci		.cmd = TEAM_CMD_NOOP,
282862306a36Sopenharmony_ci		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
282962306a36Sopenharmony_ci		.doit = team_nl_cmd_noop,
283062306a36Sopenharmony_ci	},
283162306a36Sopenharmony_ci	{
283262306a36Sopenharmony_ci		.cmd = TEAM_CMD_OPTIONS_SET,
283362306a36Sopenharmony_ci		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
283462306a36Sopenharmony_ci		.doit = team_nl_cmd_options_set,
283562306a36Sopenharmony_ci		.flags = GENL_ADMIN_PERM,
283662306a36Sopenharmony_ci	},
283762306a36Sopenharmony_ci	{
283862306a36Sopenharmony_ci		.cmd = TEAM_CMD_OPTIONS_GET,
283962306a36Sopenharmony_ci		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
284062306a36Sopenharmony_ci		.doit = team_nl_cmd_options_get,
284162306a36Sopenharmony_ci		.flags = GENL_ADMIN_PERM,
284262306a36Sopenharmony_ci	},
284362306a36Sopenharmony_ci	{
284462306a36Sopenharmony_ci		.cmd = TEAM_CMD_PORT_LIST_GET,
284562306a36Sopenharmony_ci		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
284662306a36Sopenharmony_ci		.doit = team_nl_cmd_port_list_get,
284762306a36Sopenharmony_ci		.flags = GENL_ADMIN_PERM,
284862306a36Sopenharmony_ci	},
284962306a36Sopenharmony_ci};
285062306a36Sopenharmony_ci
285162306a36Sopenharmony_cistatic const struct genl_multicast_group team_nl_mcgrps[] = {
285262306a36Sopenharmony_ci	{ .name = TEAM_GENL_CHANGE_EVENT_MC_GRP_NAME, },
285362306a36Sopenharmony_ci};
285462306a36Sopenharmony_ci
285562306a36Sopenharmony_cistatic struct genl_family team_nl_family __ro_after_init = {
285662306a36Sopenharmony_ci	.name		= TEAM_GENL_NAME,
285762306a36Sopenharmony_ci	.version	= TEAM_GENL_VERSION,
285862306a36Sopenharmony_ci	.maxattr	= TEAM_ATTR_MAX,
285962306a36Sopenharmony_ci	.policy = team_nl_policy,
286062306a36Sopenharmony_ci	.netnsok	= true,
286162306a36Sopenharmony_ci	.module		= THIS_MODULE,
286262306a36Sopenharmony_ci	.small_ops	= team_nl_ops,
286362306a36Sopenharmony_ci	.n_small_ops	= ARRAY_SIZE(team_nl_ops),
286462306a36Sopenharmony_ci	.resv_start_op	= TEAM_CMD_PORT_LIST_GET + 1,
286562306a36Sopenharmony_ci	.mcgrps		= team_nl_mcgrps,
286662306a36Sopenharmony_ci	.n_mcgrps	= ARRAY_SIZE(team_nl_mcgrps),
286762306a36Sopenharmony_ci};
286862306a36Sopenharmony_ci
286962306a36Sopenharmony_cistatic int team_nl_send_multicast(struct sk_buff *skb,
287062306a36Sopenharmony_ci				  struct team *team, u32 portid)
287162306a36Sopenharmony_ci{
287262306a36Sopenharmony_ci	return genlmsg_multicast_netns(&team_nl_family, dev_net(team->dev),
287362306a36Sopenharmony_ci				       skb, 0, 0, GFP_KERNEL);
287462306a36Sopenharmony_ci}
287562306a36Sopenharmony_ci
287662306a36Sopenharmony_cistatic int team_nl_send_event_options_get(struct team *team,
287762306a36Sopenharmony_ci					  struct list_head *sel_opt_inst_list)
287862306a36Sopenharmony_ci{
287962306a36Sopenharmony_ci	return team_nl_send_options_get(team, 0, 0, 0, team_nl_send_multicast,
288062306a36Sopenharmony_ci					sel_opt_inst_list);
288162306a36Sopenharmony_ci}
288262306a36Sopenharmony_ci
288362306a36Sopenharmony_cistatic int team_nl_send_event_port_get(struct team *team,
288462306a36Sopenharmony_ci				       struct team_port *port)
288562306a36Sopenharmony_ci{
288662306a36Sopenharmony_ci	return team_nl_send_port_list_get(team, 0, 0, 0, team_nl_send_multicast,
288762306a36Sopenharmony_ci					  port);
288862306a36Sopenharmony_ci}
288962306a36Sopenharmony_ci
289062306a36Sopenharmony_cistatic int __init team_nl_init(void)
289162306a36Sopenharmony_ci{
289262306a36Sopenharmony_ci	return genl_register_family(&team_nl_family);
289362306a36Sopenharmony_ci}
289462306a36Sopenharmony_ci
289562306a36Sopenharmony_cistatic void __exit team_nl_fini(void)
289662306a36Sopenharmony_ci{
289762306a36Sopenharmony_ci	genl_unregister_family(&team_nl_family);
289862306a36Sopenharmony_ci}
289962306a36Sopenharmony_ci
290062306a36Sopenharmony_ci
290162306a36Sopenharmony_ci/******************
290262306a36Sopenharmony_ci * Change checkers
290362306a36Sopenharmony_ci ******************/
290462306a36Sopenharmony_ci
290562306a36Sopenharmony_cistatic void __team_options_change_check(struct team *team)
290662306a36Sopenharmony_ci{
290762306a36Sopenharmony_ci	int err;
290862306a36Sopenharmony_ci	struct team_option_inst *opt_inst;
290962306a36Sopenharmony_ci	LIST_HEAD(sel_opt_inst_list);
291062306a36Sopenharmony_ci
291162306a36Sopenharmony_ci	list_for_each_entry(opt_inst, &team->option_inst_list, list) {
291262306a36Sopenharmony_ci		if (opt_inst->changed)
291362306a36Sopenharmony_ci			list_add_tail(&opt_inst->tmp_list, &sel_opt_inst_list);
291462306a36Sopenharmony_ci	}
291562306a36Sopenharmony_ci	err = team_nl_send_event_options_get(team, &sel_opt_inst_list);
291662306a36Sopenharmony_ci	if (err && err != -ESRCH)
291762306a36Sopenharmony_ci		netdev_warn(team->dev, "Failed to send options change via netlink (err %d)\n",
291862306a36Sopenharmony_ci			    err);
291962306a36Sopenharmony_ci}
292062306a36Sopenharmony_ci
292162306a36Sopenharmony_ci/* rtnl lock is held */
292262306a36Sopenharmony_ci
292362306a36Sopenharmony_cistatic void __team_port_change_send(struct team_port *port, bool linkup)
292462306a36Sopenharmony_ci{
292562306a36Sopenharmony_ci	int err;
292662306a36Sopenharmony_ci
292762306a36Sopenharmony_ci	port->changed = true;
292862306a36Sopenharmony_ci	port->state.linkup = linkup;
292962306a36Sopenharmony_ci	team_refresh_port_linkup(port);
293062306a36Sopenharmony_ci	if (linkup) {
293162306a36Sopenharmony_ci		struct ethtool_link_ksettings ecmd;
293262306a36Sopenharmony_ci
293362306a36Sopenharmony_ci		err = __ethtool_get_link_ksettings(port->dev, &ecmd);
293462306a36Sopenharmony_ci		if (!err) {
293562306a36Sopenharmony_ci			port->state.speed = ecmd.base.speed;
293662306a36Sopenharmony_ci			port->state.duplex = ecmd.base.duplex;
293762306a36Sopenharmony_ci			goto send_event;
293862306a36Sopenharmony_ci		}
293962306a36Sopenharmony_ci	}
294062306a36Sopenharmony_ci	port->state.speed = 0;
294162306a36Sopenharmony_ci	port->state.duplex = 0;
294262306a36Sopenharmony_ci
294362306a36Sopenharmony_cisend_event:
294462306a36Sopenharmony_ci	err = team_nl_send_event_port_get(port->team, port);
294562306a36Sopenharmony_ci	if (err && err != -ESRCH)
294662306a36Sopenharmony_ci		netdev_warn(port->team->dev, "Failed to send port change of device %s via netlink (err %d)\n",
294762306a36Sopenharmony_ci			    port->dev->name, err);
294862306a36Sopenharmony_ci
294962306a36Sopenharmony_ci}
295062306a36Sopenharmony_ci
295162306a36Sopenharmony_cistatic void __team_carrier_check(struct team *team)
295262306a36Sopenharmony_ci{
295362306a36Sopenharmony_ci	struct team_port *port;
295462306a36Sopenharmony_ci	bool team_linkup;
295562306a36Sopenharmony_ci
295662306a36Sopenharmony_ci	if (team->user_carrier_enabled)
295762306a36Sopenharmony_ci		return;
295862306a36Sopenharmony_ci
295962306a36Sopenharmony_ci	team_linkup = false;
296062306a36Sopenharmony_ci	list_for_each_entry(port, &team->port_list, list) {
296162306a36Sopenharmony_ci		if (port->linkup) {
296262306a36Sopenharmony_ci			team_linkup = true;
296362306a36Sopenharmony_ci			break;
296462306a36Sopenharmony_ci		}
296562306a36Sopenharmony_ci	}
296662306a36Sopenharmony_ci
296762306a36Sopenharmony_ci	if (team_linkup)
296862306a36Sopenharmony_ci		netif_carrier_on(team->dev);
296962306a36Sopenharmony_ci	else
297062306a36Sopenharmony_ci		netif_carrier_off(team->dev);
297162306a36Sopenharmony_ci}
297262306a36Sopenharmony_ci
297362306a36Sopenharmony_cistatic void __team_port_change_check(struct team_port *port, bool linkup)
297462306a36Sopenharmony_ci{
297562306a36Sopenharmony_ci	if (port->state.linkup != linkup)
297662306a36Sopenharmony_ci		__team_port_change_send(port, linkup);
297762306a36Sopenharmony_ci	__team_carrier_check(port->team);
297862306a36Sopenharmony_ci}
297962306a36Sopenharmony_ci
298062306a36Sopenharmony_cistatic void __team_port_change_port_added(struct team_port *port, bool linkup)
298162306a36Sopenharmony_ci{
298262306a36Sopenharmony_ci	__team_port_change_send(port, linkup);
298362306a36Sopenharmony_ci	__team_carrier_check(port->team);
298462306a36Sopenharmony_ci}
298562306a36Sopenharmony_ci
298662306a36Sopenharmony_cistatic void __team_port_change_port_removed(struct team_port *port)
298762306a36Sopenharmony_ci{
298862306a36Sopenharmony_ci	port->removed = true;
298962306a36Sopenharmony_ci	__team_port_change_send(port, false);
299062306a36Sopenharmony_ci	__team_carrier_check(port->team);
299162306a36Sopenharmony_ci}
299262306a36Sopenharmony_ci
299362306a36Sopenharmony_cistatic void team_port_change_check(struct team_port *port, bool linkup)
299462306a36Sopenharmony_ci{
299562306a36Sopenharmony_ci	struct team *team = port->team;
299662306a36Sopenharmony_ci
299762306a36Sopenharmony_ci	mutex_lock(&team->lock);
299862306a36Sopenharmony_ci	__team_port_change_check(port, linkup);
299962306a36Sopenharmony_ci	mutex_unlock(&team->lock);
300062306a36Sopenharmony_ci}
300162306a36Sopenharmony_ci
300262306a36Sopenharmony_ci
300362306a36Sopenharmony_ci/************************************
300462306a36Sopenharmony_ci * Net device notifier event handler
300562306a36Sopenharmony_ci ************************************/
300662306a36Sopenharmony_ci
300762306a36Sopenharmony_cistatic int team_device_event(struct notifier_block *unused,
300862306a36Sopenharmony_ci			     unsigned long event, void *ptr)
300962306a36Sopenharmony_ci{
301062306a36Sopenharmony_ci	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
301162306a36Sopenharmony_ci	struct team_port *port;
301262306a36Sopenharmony_ci
301362306a36Sopenharmony_ci	port = team_port_get_rtnl(dev);
301462306a36Sopenharmony_ci	if (!port)
301562306a36Sopenharmony_ci		return NOTIFY_DONE;
301662306a36Sopenharmony_ci
301762306a36Sopenharmony_ci	switch (event) {
301862306a36Sopenharmony_ci	case NETDEV_UP:
301962306a36Sopenharmony_ci		if (netif_oper_up(dev))
302062306a36Sopenharmony_ci			team_port_change_check(port, true);
302162306a36Sopenharmony_ci		break;
302262306a36Sopenharmony_ci	case NETDEV_DOWN:
302362306a36Sopenharmony_ci		team_port_change_check(port, false);
302462306a36Sopenharmony_ci		break;
302562306a36Sopenharmony_ci	case NETDEV_CHANGE:
302662306a36Sopenharmony_ci		if (netif_running(port->dev))
302762306a36Sopenharmony_ci			team_port_change_check(port,
302862306a36Sopenharmony_ci					       !!netif_oper_up(port->dev));
302962306a36Sopenharmony_ci		break;
303062306a36Sopenharmony_ci	case NETDEV_UNREGISTER:
303162306a36Sopenharmony_ci		team_del_slave(port->team->dev, dev);
303262306a36Sopenharmony_ci		break;
303362306a36Sopenharmony_ci	case NETDEV_FEAT_CHANGE:
303462306a36Sopenharmony_ci		if (!port->team->notifier_ctx) {
303562306a36Sopenharmony_ci			port->team->notifier_ctx = true;
303662306a36Sopenharmony_ci			team_compute_features(port->team);
303762306a36Sopenharmony_ci			port->team->notifier_ctx = false;
303862306a36Sopenharmony_ci		}
303962306a36Sopenharmony_ci		break;
304062306a36Sopenharmony_ci	case NETDEV_PRECHANGEMTU:
304162306a36Sopenharmony_ci		/* Forbid to change mtu of underlaying device */
304262306a36Sopenharmony_ci		if (!port->team->port_mtu_change_allowed)
304362306a36Sopenharmony_ci			return NOTIFY_BAD;
304462306a36Sopenharmony_ci		break;
304562306a36Sopenharmony_ci	case NETDEV_PRE_TYPE_CHANGE:
304662306a36Sopenharmony_ci		/* Forbid to change type of underlaying device */
304762306a36Sopenharmony_ci		return NOTIFY_BAD;
304862306a36Sopenharmony_ci	case NETDEV_RESEND_IGMP:
304962306a36Sopenharmony_ci		/* Propagate to master device */
305062306a36Sopenharmony_ci		call_netdevice_notifiers(event, port->team->dev);
305162306a36Sopenharmony_ci		break;
305262306a36Sopenharmony_ci	}
305362306a36Sopenharmony_ci	return NOTIFY_DONE;
305462306a36Sopenharmony_ci}
305562306a36Sopenharmony_ci
305662306a36Sopenharmony_cistatic struct notifier_block team_notifier_block __read_mostly = {
305762306a36Sopenharmony_ci	.notifier_call = team_device_event,
305862306a36Sopenharmony_ci};
305962306a36Sopenharmony_ci
306062306a36Sopenharmony_ci
306162306a36Sopenharmony_ci/***********************
306262306a36Sopenharmony_ci * Module init and exit
306362306a36Sopenharmony_ci ***********************/
306462306a36Sopenharmony_ci
306562306a36Sopenharmony_cistatic int __init team_module_init(void)
306662306a36Sopenharmony_ci{
306762306a36Sopenharmony_ci	int err;
306862306a36Sopenharmony_ci
306962306a36Sopenharmony_ci	register_netdevice_notifier(&team_notifier_block);
307062306a36Sopenharmony_ci
307162306a36Sopenharmony_ci	err = rtnl_link_register(&team_link_ops);
307262306a36Sopenharmony_ci	if (err)
307362306a36Sopenharmony_ci		goto err_rtnl_reg;
307462306a36Sopenharmony_ci
307562306a36Sopenharmony_ci	err = team_nl_init();
307662306a36Sopenharmony_ci	if (err)
307762306a36Sopenharmony_ci		goto err_nl_init;
307862306a36Sopenharmony_ci
307962306a36Sopenharmony_ci	return 0;
308062306a36Sopenharmony_ci
308162306a36Sopenharmony_cierr_nl_init:
308262306a36Sopenharmony_ci	rtnl_link_unregister(&team_link_ops);
308362306a36Sopenharmony_ci
308462306a36Sopenharmony_cierr_rtnl_reg:
308562306a36Sopenharmony_ci	unregister_netdevice_notifier(&team_notifier_block);
308662306a36Sopenharmony_ci
308762306a36Sopenharmony_ci	return err;
308862306a36Sopenharmony_ci}
308962306a36Sopenharmony_ci
309062306a36Sopenharmony_cistatic void __exit team_module_exit(void)
309162306a36Sopenharmony_ci{
309262306a36Sopenharmony_ci	team_nl_fini();
309362306a36Sopenharmony_ci	rtnl_link_unregister(&team_link_ops);
309462306a36Sopenharmony_ci	unregister_netdevice_notifier(&team_notifier_block);
309562306a36Sopenharmony_ci}
309662306a36Sopenharmony_ci
309762306a36Sopenharmony_cimodule_init(team_module_init);
309862306a36Sopenharmony_cimodule_exit(team_module_exit);
309962306a36Sopenharmony_ci
310062306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
310162306a36Sopenharmony_ciMODULE_AUTHOR("Jiri Pirko <jpirko@redhat.com>");
310262306a36Sopenharmony_ciMODULE_DESCRIPTION("Ethernet team device driver");
310362306a36Sopenharmony_ciMODULE_ALIAS_RTNL_LINK(DRV_NAME);
3104