xref: /kernel/linux/linux-5.10/drivers/net/team/team.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * drivers/net/team/team.c - Network team device driver
48c2ecf20Sopenharmony_ci * Copyright (c) 2011 Jiri Pirko <jpirko@redhat.com>
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/kernel.h>
88c2ecf20Sopenharmony_ci#include <linux/types.h>
98c2ecf20Sopenharmony_ci#include <linux/module.h>
108c2ecf20Sopenharmony_ci#include <linux/init.h>
118c2ecf20Sopenharmony_ci#include <linux/slab.h>
128c2ecf20Sopenharmony_ci#include <linux/rcupdate.h>
138c2ecf20Sopenharmony_ci#include <linux/errno.h>
148c2ecf20Sopenharmony_ci#include <linux/ctype.h>
158c2ecf20Sopenharmony_ci#include <linux/notifier.h>
168c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
178c2ecf20Sopenharmony_ci#include <linux/netpoll.h>
188c2ecf20Sopenharmony_ci#include <linux/if_vlan.h>
198c2ecf20Sopenharmony_ci#include <linux/if_arp.h>
208c2ecf20Sopenharmony_ci#include <linux/socket.h>
218c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
228c2ecf20Sopenharmony_ci#include <linux/rtnetlink.h>
238c2ecf20Sopenharmony_ci#include <net/rtnetlink.h>
248c2ecf20Sopenharmony_ci#include <net/genetlink.h>
258c2ecf20Sopenharmony_ci#include <net/netlink.h>
268c2ecf20Sopenharmony_ci#include <net/sch_generic.h>
278c2ecf20Sopenharmony_ci#include <generated/utsrelease.h>
288c2ecf20Sopenharmony_ci#include <linux/if_team.h>
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#define DRV_NAME "team"
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci/**********
348c2ecf20Sopenharmony_ci * Helpers
358c2ecf20Sopenharmony_ci **********/
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_cistatic struct team_port *team_port_get_rtnl(const struct net_device *dev)
388c2ecf20Sopenharmony_ci{
398c2ecf20Sopenharmony_ci	struct team_port *port = rtnl_dereference(dev->rx_handler_data);
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	return netif_is_team_port(dev) ? port : NULL;
428c2ecf20Sopenharmony_ci}
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci/*
458c2ecf20Sopenharmony_ci * Since the ability to change device address for open port device is tested in
468c2ecf20Sopenharmony_ci * team_port_add, this function can be called without control of return value
478c2ecf20Sopenharmony_ci */
488c2ecf20Sopenharmony_cistatic int __set_port_dev_addr(struct net_device *port_dev,
498c2ecf20Sopenharmony_ci			       const unsigned char *dev_addr)
508c2ecf20Sopenharmony_ci{
518c2ecf20Sopenharmony_ci	struct sockaddr_storage addr;
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	memcpy(addr.__data, dev_addr, port_dev->addr_len);
548c2ecf20Sopenharmony_ci	addr.ss_family = port_dev->type;
558c2ecf20Sopenharmony_ci	return dev_set_mac_address(port_dev, (struct sockaddr *)&addr, NULL);
568c2ecf20Sopenharmony_ci}
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cistatic int team_port_set_orig_dev_addr(struct team_port *port)
598c2ecf20Sopenharmony_ci{
608c2ecf20Sopenharmony_ci	return __set_port_dev_addr(port->dev, port->orig.dev_addr);
618c2ecf20Sopenharmony_ci}
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_cistatic int team_port_set_team_dev_addr(struct team *team,
648c2ecf20Sopenharmony_ci				       struct team_port *port)
658c2ecf20Sopenharmony_ci{
668c2ecf20Sopenharmony_ci	return __set_port_dev_addr(port->dev, team->dev->dev_addr);
678c2ecf20Sopenharmony_ci}
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ciint team_modeop_port_enter(struct team *team, struct team_port *port)
708c2ecf20Sopenharmony_ci{
718c2ecf20Sopenharmony_ci	return team_port_set_team_dev_addr(team, port);
728c2ecf20Sopenharmony_ci}
738c2ecf20Sopenharmony_ciEXPORT_SYMBOL(team_modeop_port_enter);
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_civoid team_modeop_port_change_dev_addr(struct team *team,
768c2ecf20Sopenharmony_ci				      struct team_port *port)
778c2ecf20Sopenharmony_ci{
788c2ecf20Sopenharmony_ci	team_port_set_team_dev_addr(team, port);
798c2ecf20Sopenharmony_ci}
808c2ecf20Sopenharmony_ciEXPORT_SYMBOL(team_modeop_port_change_dev_addr);
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_cistatic void team_lower_state_changed(struct team_port *port)
838c2ecf20Sopenharmony_ci{
848c2ecf20Sopenharmony_ci	struct netdev_lag_lower_state_info info;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	info.link_up = port->linkup;
878c2ecf20Sopenharmony_ci	info.tx_enabled = team_port_enabled(port);
888c2ecf20Sopenharmony_ci	netdev_lower_state_changed(port->dev, &info);
898c2ecf20Sopenharmony_ci}
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_cistatic void team_refresh_port_linkup(struct team_port *port)
928c2ecf20Sopenharmony_ci{
938c2ecf20Sopenharmony_ci	bool new_linkup = port->user.linkup_enabled ? port->user.linkup :
948c2ecf20Sopenharmony_ci						      port->state.linkup;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	if (port->linkup != new_linkup) {
978c2ecf20Sopenharmony_ci		port->linkup = new_linkup;
988c2ecf20Sopenharmony_ci		team_lower_state_changed(port);
998c2ecf20Sopenharmony_ci	}
1008c2ecf20Sopenharmony_ci}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci/*******************
1048c2ecf20Sopenharmony_ci * Options handling
1058c2ecf20Sopenharmony_ci *******************/
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_cistruct team_option_inst { /* One for each option instance */
1088c2ecf20Sopenharmony_ci	struct list_head list;
1098c2ecf20Sopenharmony_ci	struct list_head tmp_list;
1108c2ecf20Sopenharmony_ci	struct team_option *option;
1118c2ecf20Sopenharmony_ci	struct team_option_inst_info info;
1128c2ecf20Sopenharmony_ci	bool changed;
1138c2ecf20Sopenharmony_ci	bool removed;
1148c2ecf20Sopenharmony_ci};
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_cistatic struct team_option *__team_find_option(struct team *team,
1178c2ecf20Sopenharmony_ci					      const char *opt_name)
1188c2ecf20Sopenharmony_ci{
1198c2ecf20Sopenharmony_ci	struct team_option *option;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	list_for_each_entry(option, &team->option_list, list) {
1228c2ecf20Sopenharmony_ci		if (strcmp(option->name, opt_name) == 0)
1238c2ecf20Sopenharmony_ci			return option;
1248c2ecf20Sopenharmony_ci	}
1258c2ecf20Sopenharmony_ci	return NULL;
1268c2ecf20Sopenharmony_ci}
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_cistatic void __team_option_inst_del(struct team_option_inst *opt_inst)
1298c2ecf20Sopenharmony_ci{
1308c2ecf20Sopenharmony_ci	list_del(&opt_inst->list);
1318c2ecf20Sopenharmony_ci	kfree(opt_inst);
1328c2ecf20Sopenharmony_ci}
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_cistatic void __team_option_inst_del_option(struct team *team,
1358c2ecf20Sopenharmony_ci					  struct team_option *option)
1368c2ecf20Sopenharmony_ci{
1378c2ecf20Sopenharmony_ci	struct team_option_inst *opt_inst, *tmp;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	list_for_each_entry_safe(opt_inst, tmp, &team->option_inst_list, list) {
1408c2ecf20Sopenharmony_ci		if (opt_inst->option == option)
1418c2ecf20Sopenharmony_ci			__team_option_inst_del(opt_inst);
1428c2ecf20Sopenharmony_ci	}
1438c2ecf20Sopenharmony_ci}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_cistatic int __team_option_inst_add(struct team *team, struct team_option *option,
1468c2ecf20Sopenharmony_ci				  struct team_port *port)
1478c2ecf20Sopenharmony_ci{
1488c2ecf20Sopenharmony_ci	struct team_option_inst *opt_inst;
1498c2ecf20Sopenharmony_ci	unsigned int array_size;
1508c2ecf20Sopenharmony_ci	unsigned int i;
1518c2ecf20Sopenharmony_ci	int err;
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	array_size = option->array_size;
1548c2ecf20Sopenharmony_ci	if (!array_size)
1558c2ecf20Sopenharmony_ci		array_size = 1; /* No array but still need one instance */
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	for (i = 0; i < array_size; i++) {
1588c2ecf20Sopenharmony_ci		opt_inst = kmalloc(sizeof(*opt_inst), GFP_KERNEL);
1598c2ecf20Sopenharmony_ci		if (!opt_inst)
1608c2ecf20Sopenharmony_ci			return -ENOMEM;
1618c2ecf20Sopenharmony_ci		opt_inst->option = option;
1628c2ecf20Sopenharmony_ci		opt_inst->info.port = port;
1638c2ecf20Sopenharmony_ci		opt_inst->info.array_index = i;
1648c2ecf20Sopenharmony_ci		opt_inst->changed = true;
1658c2ecf20Sopenharmony_ci		opt_inst->removed = false;
1668c2ecf20Sopenharmony_ci		list_add_tail(&opt_inst->list, &team->option_inst_list);
1678c2ecf20Sopenharmony_ci		if (option->init) {
1688c2ecf20Sopenharmony_ci			err = option->init(team, &opt_inst->info);
1698c2ecf20Sopenharmony_ci			if (err)
1708c2ecf20Sopenharmony_ci				return err;
1718c2ecf20Sopenharmony_ci		}
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	}
1748c2ecf20Sopenharmony_ci	return 0;
1758c2ecf20Sopenharmony_ci}
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_cistatic int __team_option_inst_add_option(struct team *team,
1788c2ecf20Sopenharmony_ci					 struct team_option *option)
1798c2ecf20Sopenharmony_ci{
1808c2ecf20Sopenharmony_ci	int err;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	if (!option->per_port) {
1838c2ecf20Sopenharmony_ci		err = __team_option_inst_add(team, option, NULL);
1848c2ecf20Sopenharmony_ci		if (err)
1858c2ecf20Sopenharmony_ci			goto inst_del_option;
1868c2ecf20Sopenharmony_ci	}
1878c2ecf20Sopenharmony_ci	return 0;
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ciinst_del_option:
1908c2ecf20Sopenharmony_ci	__team_option_inst_del_option(team, option);
1918c2ecf20Sopenharmony_ci	return err;
1928c2ecf20Sopenharmony_ci}
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_cistatic void __team_option_inst_mark_removed_option(struct team *team,
1958c2ecf20Sopenharmony_ci						   struct team_option *option)
1968c2ecf20Sopenharmony_ci{
1978c2ecf20Sopenharmony_ci	struct team_option_inst *opt_inst;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	list_for_each_entry(opt_inst, &team->option_inst_list, list) {
2008c2ecf20Sopenharmony_ci		if (opt_inst->option == option) {
2018c2ecf20Sopenharmony_ci			opt_inst->changed = true;
2028c2ecf20Sopenharmony_ci			opt_inst->removed = true;
2038c2ecf20Sopenharmony_ci		}
2048c2ecf20Sopenharmony_ci	}
2058c2ecf20Sopenharmony_ci}
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_cistatic void __team_option_inst_del_port(struct team *team,
2088c2ecf20Sopenharmony_ci					struct team_port *port)
2098c2ecf20Sopenharmony_ci{
2108c2ecf20Sopenharmony_ci	struct team_option_inst *opt_inst, *tmp;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	list_for_each_entry_safe(opt_inst, tmp, &team->option_inst_list, list) {
2138c2ecf20Sopenharmony_ci		if (opt_inst->option->per_port &&
2148c2ecf20Sopenharmony_ci		    opt_inst->info.port == port)
2158c2ecf20Sopenharmony_ci			__team_option_inst_del(opt_inst);
2168c2ecf20Sopenharmony_ci	}
2178c2ecf20Sopenharmony_ci}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_cistatic int __team_option_inst_add_port(struct team *team,
2208c2ecf20Sopenharmony_ci				       struct team_port *port)
2218c2ecf20Sopenharmony_ci{
2228c2ecf20Sopenharmony_ci	struct team_option *option;
2238c2ecf20Sopenharmony_ci	int err;
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	list_for_each_entry(option, &team->option_list, list) {
2268c2ecf20Sopenharmony_ci		if (!option->per_port)
2278c2ecf20Sopenharmony_ci			continue;
2288c2ecf20Sopenharmony_ci		err = __team_option_inst_add(team, option, port);
2298c2ecf20Sopenharmony_ci		if (err)
2308c2ecf20Sopenharmony_ci			goto inst_del_port;
2318c2ecf20Sopenharmony_ci	}
2328c2ecf20Sopenharmony_ci	return 0;
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ciinst_del_port:
2358c2ecf20Sopenharmony_ci	__team_option_inst_del_port(team, port);
2368c2ecf20Sopenharmony_ci	return err;
2378c2ecf20Sopenharmony_ci}
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_cistatic void __team_option_inst_mark_removed_port(struct team *team,
2408c2ecf20Sopenharmony_ci						 struct team_port *port)
2418c2ecf20Sopenharmony_ci{
2428c2ecf20Sopenharmony_ci	struct team_option_inst *opt_inst;
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	list_for_each_entry(opt_inst, &team->option_inst_list, list) {
2458c2ecf20Sopenharmony_ci		if (opt_inst->info.port == port) {
2468c2ecf20Sopenharmony_ci			opt_inst->changed = true;
2478c2ecf20Sopenharmony_ci			opt_inst->removed = true;
2488c2ecf20Sopenharmony_ci		}
2498c2ecf20Sopenharmony_ci	}
2508c2ecf20Sopenharmony_ci}
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_cistatic int __team_options_register(struct team *team,
2538c2ecf20Sopenharmony_ci				   const struct team_option *option,
2548c2ecf20Sopenharmony_ci				   size_t option_count)
2558c2ecf20Sopenharmony_ci{
2568c2ecf20Sopenharmony_ci	int i;
2578c2ecf20Sopenharmony_ci	struct team_option **dst_opts;
2588c2ecf20Sopenharmony_ci	int err;
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	dst_opts = kcalloc(option_count, sizeof(struct team_option *),
2618c2ecf20Sopenharmony_ci			   GFP_KERNEL);
2628c2ecf20Sopenharmony_ci	if (!dst_opts)
2638c2ecf20Sopenharmony_ci		return -ENOMEM;
2648c2ecf20Sopenharmony_ci	for (i = 0; i < option_count; i++, option++) {
2658c2ecf20Sopenharmony_ci		if (__team_find_option(team, option->name)) {
2668c2ecf20Sopenharmony_ci			err = -EEXIST;
2678c2ecf20Sopenharmony_ci			goto alloc_rollback;
2688c2ecf20Sopenharmony_ci		}
2698c2ecf20Sopenharmony_ci		dst_opts[i] = kmemdup(option, sizeof(*option), GFP_KERNEL);
2708c2ecf20Sopenharmony_ci		if (!dst_opts[i]) {
2718c2ecf20Sopenharmony_ci			err = -ENOMEM;
2728c2ecf20Sopenharmony_ci			goto alloc_rollback;
2738c2ecf20Sopenharmony_ci		}
2748c2ecf20Sopenharmony_ci	}
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	for (i = 0; i < option_count; i++) {
2778c2ecf20Sopenharmony_ci		err = __team_option_inst_add_option(team, dst_opts[i]);
2788c2ecf20Sopenharmony_ci		if (err)
2798c2ecf20Sopenharmony_ci			goto inst_rollback;
2808c2ecf20Sopenharmony_ci		list_add_tail(&dst_opts[i]->list, &team->option_list);
2818c2ecf20Sopenharmony_ci	}
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	kfree(dst_opts);
2848c2ecf20Sopenharmony_ci	return 0;
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ciinst_rollback:
2878c2ecf20Sopenharmony_ci	for (i--; i >= 0; i--) {
2888c2ecf20Sopenharmony_ci		__team_option_inst_del_option(team, dst_opts[i]);
2898c2ecf20Sopenharmony_ci		list_del(&dst_opts[i]->list);
2908c2ecf20Sopenharmony_ci	}
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	i = option_count;
2938c2ecf20Sopenharmony_cialloc_rollback:
2948c2ecf20Sopenharmony_ci	for (i--; i >= 0; i--)
2958c2ecf20Sopenharmony_ci		kfree(dst_opts[i]);
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	kfree(dst_opts);
2988c2ecf20Sopenharmony_ci	return err;
2998c2ecf20Sopenharmony_ci}
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_cistatic void __team_options_mark_removed(struct team *team,
3028c2ecf20Sopenharmony_ci					const struct team_option *option,
3038c2ecf20Sopenharmony_ci					size_t option_count)
3048c2ecf20Sopenharmony_ci{
3058c2ecf20Sopenharmony_ci	int i;
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	for (i = 0; i < option_count; i++, option++) {
3088c2ecf20Sopenharmony_ci		struct team_option *del_opt;
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci		del_opt = __team_find_option(team, option->name);
3118c2ecf20Sopenharmony_ci		if (del_opt)
3128c2ecf20Sopenharmony_ci			__team_option_inst_mark_removed_option(team, del_opt);
3138c2ecf20Sopenharmony_ci	}
3148c2ecf20Sopenharmony_ci}
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_cistatic void __team_options_unregister(struct team *team,
3178c2ecf20Sopenharmony_ci				      const struct team_option *option,
3188c2ecf20Sopenharmony_ci				      size_t option_count)
3198c2ecf20Sopenharmony_ci{
3208c2ecf20Sopenharmony_ci	int i;
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	for (i = 0; i < option_count; i++, option++) {
3238c2ecf20Sopenharmony_ci		struct team_option *del_opt;
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci		del_opt = __team_find_option(team, option->name);
3268c2ecf20Sopenharmony_ci		if (del_opt) {
3278c2ecf20Sopenharmony_ci			__team_option_inst_del_option(team, del_opt);
3288c2ecf20Sopenharmony_ci			list_del(&del_opt->list);
3298c2ecf20Sopenharmony_ci			kfree(del_opt);
3308c2ecf20Sopenharmony_ci		}
3318c2ecf20Sopenharmony_ci	}
3328c2ecf20Sopenharmony_ci}
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_cistatic void __team_options_change_check(struct team *team);
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ciint team_options_register(struct team *team,
3378c2ecf20Sopenharmony_ci			  const struct team_option *option,
3388c2ecf20Sopenharmony_ci			  size_t option_count)
3398c2ecf20Sopenharmony_ci{
3408c2ecf20Sopenharmony_ci	int err;
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	err = __team_options_register(team, option, option_count);
3438c2ecf20Sopenharmony_ci	if (err)
3448c2ecf20Sopenharmony_ci		return err;
3458c2ecf20Sopenharmony_ci	__team_options_change_check(team);
3468c2ecf20Sopenharmony_ci	return 0;
3478c2ecf20Sopenharmony_ci}
3488c2ecf20Sopenharmony_ciEXPORT_SYMBOL(team_options_register);
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_civoid team_options_unregister(struct team *team,
3518c2ecf20Sopenharmony_ci			     const struct team_option *option,
3528c2ecf20Sopenharmony_ci			     size_t option_count)
3538c2ecf20Sopenharmony_ci{
3548c2ecf20Sopenharmony_ci	__team_options_mark_removed(team, option, option_count);
3558c2ecf20Sopenharmony_ci	__team_options_change_check(team);
3568c2ecf20Sopenharmony_ci	__team_options_unregister(team, option, option_count);
3578c2ecf20Sopenharmony_ci}
3588c2ecf20Sopenharmony_ciEXPORT_SYMBOL(team_options_unregister);
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_cistatic int team_option_get(struct team *team,
3618c2ecf20Sopenharmony_ci			   struct team_option_inst *opt_inst,
3628c2ecf20Sopenharmony_ci			   struct team_gsetter_ctx *ctx)
3638c2ecf20Sopenharmony_ci{
3648c2ecf20Sopenharmony_ci	if (!opt_inst->option->getter)
3658c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
3668c2ecf20Sopenharmony_ci	return opt_inst->option->getter(team, ctx);
3678c2ecf20Sopenharmony_ci}
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_cistatic int team_option_set(struct team *team,
3708c2ecf20Sopenharmony_ci			   struct team_option_inst *opt_inst,
3718c2ecf20Sopenharmony_ci			   struct team_gsetter_ctx *ctx)
3728c2ecf20Sopenharmony_ci{
3738c2ecf20Sopenharmony_ci	if (!opt_inst->option->setter)
3748c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
3758c2ecf20Sopenharmony_ci	return opt_inst->option->setter(team, ctx);
3768c2ecf20Sopenharmony_ci}
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_civoid team_option_inst_set_change(struct team_option_inst_info *opt_inst_info)
3798c2ecf20Sopenharmony_ci{
3808c2ecf20Sopenharmony_ci	struct team_option_inst *opt_inst;
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	opt_inst = container_of(opt_inst_info, struct team_option_inst, info);
3838c2ecf20Sopenharmony_ci	opt_inst->changed = true;
3848c2ecf20Sopenharmony_ci}
3858c2ecf20Sopenharmony_ciEXPORT_SYMBOL(team_option_inst_set_change);
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_civoid team_options_change_check(struct team *team)
3888c2ecf20Sopenharmony_ci{
3898c2ecf20Sopenharmony_ci	__team_options_change_check(team);
3908c2ecf20Sopenharmony_ci}
3918c2ecf20Sopenharmony_ciEXPORT_SYMBOL(team_options_change_check);
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci/****************
3958c2ecf20Sopenharmony_ci * Mode handling
3968c2ecf20Sopenharmony_ci ****************/
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_cistatic LIST_HEAD(mode_list);
3998c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(mode_list_lock);
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_cistruct team_mode_item {
4028c2ecf20Sopenharmony_ci	struct list_head list;
4038c2ecf20Sopenharmony_ci	const struct team_mode *mode;
4048c2ecf20Sopenharmony_ci};
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_cistatic struct team_mode_item *__find_mode(const char *kind)
4078c2ecf20Sopenharmony_ci{
4088c2ecf20Sopenharmony_ci	struct team_mode_item *mitem;
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	list_for_each_entry(mitem, &mode_list, list) {
4118c2ecf20Sopenharmony_ci		if (strcmp(mitem->mode->kind, kind) == 0)
4128c2ecf20Sopenharmony_ci			return mitem;
4138c2ecf20Sopenharmony_ci	}
4148c2ecf20Sopenharmony_ci	return NULL;
4158c2ecf20Sopenharmony_ci}
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_cistatic bool is_good_mode_name(const char *name)
4188c2ecf20Sopenharmony_ci{
4198c2ecf20Sopenharmony_ci	while (*name != '\0') {
4208c2ecf20Sopenharmony_ci		if (!isalpha(*name) && !isdigit(*name) && *name != '_')
4218c2ecf20Sopenharmony_ci			return false;
4228c2ecf20Sopenharmony_ci		name++;
4238c2ecf20Sopenharmony_ci	}
4248c2ecf20Sopenharmony_ci	return true;
4258c2ecf20Sopenharmony_ci}
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ciint team_mode_register(const struct team_mode *mode)
4288c2ecf20Sopenharmony_ci{
4298c2ecf20Sopenharmony_ci	int err = 0;
4308c2ecf20Sopenharmony_ci	struct team_mode_item *mitem;
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	if (!is_good_mode_name(mode->kind) ||
4338c2ecf20Sopenharmony_ci	    mode->priv_size > TEAM_MODE_PRIV_SIZE)
4348c2ecf20Sopenharmony_ci		return -EINVAL;
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	mitem = kmalloc(sizeof(*mitem), GFP_KERNEL);
4378c2ecf20Sopenharmony_ci	if (!mitem)
4388c2ecf20Sopenharmony_ci		return -ENOMEM;
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci	spin_lock(&mode_list_lock);
4418c2ecf20Sopenharmony_ci	if (__find_mode(mode->kind)) {
4428c2ecf20Sopenharmony_ci		err = -EEXIST;
4438c2ecf20Sopenharmony_ci		kfree(mitem);
4448c2ecf20Sopenharmony_ci		goto unlock;
4458c2ecf20Sopenharmony_ci	}
4468c2ecf20Sopenharmony_ci	mitem->mode = mode;
4478c2ecf20Sopenharmony_ci	list_add_tail(&mitem->list, &mode_list);
4488c2ecf20Sopenharmony_ciunlock:
4498c2ecf20Sopenharmony_ci	spin_unlock(&mode_list_lock);
4508c2ecf20Sopenharmony_ci	return err;
4518c2ecf20Sopenharmony_ci}
4528c2ecf20Sopenharmony_ciEXPORT_SYMBOL(team_mode_register);
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_civoid team_mode_unregister(const struct team_mode *mode)
4558c2ecf20Sopenharmony_ci{
4568c2ecf20Sopenharmony_ci	struct team_mode_item *mitem;
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	spin_lock(&mode_list_lock);
4598c2ecf20Sopenharmony_ci	mitem = __find_mode(mode->kind);
4608c2ecf20Sopenharmony_ci	if (mitem) {
4618c2ecf20Sopenharmony_ci		list_del_init(&mitem->list);
4628c2ecf20Sopenharmony_ci		kfree(mitem);
4638c2ecf20Sopenharmony_ci	}
4648c2ecf20Sopenharmony_ci	spin_unlock(&mode_list_lock);
4658c2ecf20Sopenharmony_ci}
4668c2ecf20Sopenharmony_ciEXPORT_SYMBOL(team_mode_unregister);
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_cistatic const struct team_mode *team_mode_get(const char *kind)
4698c2ecf20Sopenharmony_ci{
4708c2ecf20Sopenharmony_ci	struct team_mode_item *mitem;
4718c2ecf20Sopenharmony_ci	const struct team_mode *mode = NULL;
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci	if (!try_module_get(THIS_MODULE))
4748c2ecf20Sopenharmony_ci		return NULL;
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci	spin_lock(&mode_list_lock);
4778c2ecf20Sopenharmony_ci	mitem = __find_mode(kind);
4788c2ecf20Sopenharmony_ci	if (!mitem) {
4798c2ecf20Sopenharmony_ci		spin_unlock(&mode_list_lock);
4808c2ecf20Sopenharmony_ci		request_module("team-mode-%s", kind);
4818c2ecf20Sopenharmony_ci		spin_lock(&mode_list_lock);
4828c2ecf20Sopenharmony_ci		mitem = __find_mode(kind);
4838c2ecf20Sopenharmony_ci	}
4848c2ecf20Sopenharmony_ci	if (mitem) {
4858c2ecf20Sopenharmony_ci		mode = mitem->mode;
4868c2ecf20Sopenharmony_ci		if (!try_module_get(mode->owner))
4878c2ecf20Sopenharmony_ci			mode = NULL;
4888c2ecf20Sopenharmony_ci	}
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci	spin_unlock(&mode_list_lock);
4918c2ecf20Sopenharmony_ci	module_put(THIS_MODULE);
4928c2ecf20Sopenharmony_ci	return mode;
4938c2ecf20Sopenharmony_ci}
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_cistatic void team_mode_put(const struct team_mode *mode)
4968c2ecf20Sopenharmony_ci{
4978c2ecf20Sopenharmony_ci	module_put(mode->owner);
4988c2ecf20Sopenharmony_ci}
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_cistatic bool team_dummy_transmit(struct team *team, struct sk_buff *skb)
5018c2ecf20Sopenharmony_ci{
5028c2ecf20Sopenharmony_ci	dev_kfree_skb_any(skb);
5038c2ecf20Sopenharmony_ci	return false;
5048c2ecf20Sopenharmony_ci}
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_cistatic rx_handler_result_t team_dummy_receive(struct team *team,
5078c2ecf20Sopenharmony_ci					      struct team_port *port,
5088c2ecf20Sopenharmony_ci					      struct sk_buff *skb)
5098c2ecf20Sopenharmony_ci{
5108c2ecf20Sopenharmony_ci	return RX_HANDLER_ANOTHER;
5118c2ecf20Sopenharmony_ci}
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_cistatic const struct team_mode __team_no_mode = {
5148c2ecf20Sopenharmony_ci	.kind		= "*NOMODE*",
5158c2ecf20Sopenharmony_ci};
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_cistatic bool team_is_mode_set(struct team *team)
5188c2ecf20Sopenharmony_ci{
5198c2ecf20Sopenharmony_ci	return team->mode != &__team_no_mode;
5208c2ecf20Sopenharmony_ci}
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_cistatic void team_set_no_mode(struct team *team)
5238c2ecf20Sopenharmony_ci{
5248c2ecf20Sopenharmony_ci	team->user_carrier_enabled = false;
5258c2ecf20Sopenharmony_ci	team->mode = &__team_no_mode;
5268c2ecf20Sopenharmony_ci}
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_cistatic void team_adjust_ops(struct team *team)
5298c2ecf20Sopenharmony_ci{
5308c2ecf20Sopenharmony_ci	/*
5318c2ecf20Sopenharmony_ci	 * To avoid checks in rx/tx skb paths, ensure here that non-null and
5328c2ecf20Sopenharmony_ci	 * correct ops are always set.
5338c2ecf20Sopenharmony_ci	 */
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci	if (!team->en_port_count || !team_is_mode_set(team) ||
5368c2ecf20Sopenharmony_ci	    !team->mode->ops->transmit)
5378c2ecf20Sopenharmony_ci		team->ops.transmit = team_dummy_transmit;
5388c2ecf20Sopenharmony_ci	else
5398c2ecf20Sopenharmony_ci		team->ops.transmit = team->mode->ops->transmit;
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci	if (!team->en_port_count || !team_is_mode_set(team) ||
5428c2ecf20Sopenharmony_ci	    !team->mode->ops->receive)
5438c2ecf20Sopenharmony_ci		team->ops.receive = team_dummy_receive;
5448c2ecf20Sopenharmony_ci	else
5458c2ecf20Sopenharmony_ci		team->ops.receive = team->mode->ops->receive;
5468c2ecf20Sopenharmony_ci}
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci/*
5498c2ecf20Sopenharmony_ci * We can benefit from the fact that it's ensured no port is present
5508c2ecf20Sopenharmony_ci * at the time of mode change. Therefore no packets are in fly so there's no
5518c2ecf20Sopenharmony_ci * need to set mode operations in any special way.
5528c2ecf20Sopenharmony_ci */
5538c2ecf20Sopenharmony_cistatic int __team_change_mode(struct team *team,
5548c2ecf20Sopenharmony_ci			      const struct team_mode *new_mode)
5558c2ecf20Sopenharmony_ci{
5568c2ecf20Sopenharmony_ci	/* Check if mode was previously set and do cleanup if so */
5578c2ecf20Sopenharmony_ci	if (team_is_mode_set(team)) {
5588c2ecf20Sopenharmony_ci		void (*exit_op)(struct team *team) = team->ops.exit;
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci		/* Clear ops area so no callback is called any longer */
5618c2ecf20Sopenharmony_ci		memset(&team->ops, 0, sizeof(struct team_mode_ops));
5628c2ecf20Sopenharmony_ci		team_adjust_ops(team);
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_ci		if (exit_op)
5658c2ecf20Sopenharmony_ci			exit_op(team);
5668c2ecf20Sopenharmony_ci		team_mode_put(team->mode);
5678c2ecf20Sopenharmony_ci		team_set_no_mode(team);
5688c2ecf20Sopenharmony_ci		/* zero private data area */
5698c2ecf20Sopenharmony_ci		memset(&team->mode_priv, 0,
5708c2ecf20Sopenharmony_ci		       sizeof(struct team) - offsetof(struct team, mode_priv));
5718c2ecf20Sopenharmony_ci	}
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci	if (!new_mode)
5748c2ecf20Sopenharmony_ci		return 0;
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci	if (new_mode->ops->init) {
5778c2ecf20Sopenharmony_ci		int err;
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci		err = new_mode->ops->init(team);
5808c2ecf20Sopenharmony_ci		if (err)
5818c2ecf20Sopenharmony_ci			return err;
5828c2ecf20Sopenharmony_ci	}
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci	team->mode = new_mode;
5858c2ecf20Sopenharmony_ci	memcpy(&team->ops, new_mode->ops, sizeof(struct team_mode_ops));
5868c2ecf20Sopenharmony_ci	team_adjust_ops(team);
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci	return 0;
5898c2ecf20Sopenharmony_ci}
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_cistatic int team_change_mode(struct team *team, const char *kind)
5928c2ecf20Sopenharmony_ci{
5938c2ecf20Sopenharmony_ci	const struct team_mode *new_mode;
5948c2ecf20Sopenharmony_ci	struct net_device *dev = team->dev;
5958c2ecf20Sopenharmony_ci	int err;
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci	if (!list_empty(&team->port_list)) {
5988c2ecf20Sopenharmony_ci		netdev_err(dev, "No ports can be present during mode change\n");
5998c2ecf20Sopenharmony_ci		return -EBUSY;
6008c2ecf20Sopenharmony_ci	}
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci	if (team_is_mode_set(team) && strcmp(team->mode->kind, kind) == 0) {
6038c2ecf20Sopenharmony_ci		netdev_err(dev, "Unable to change to the same mode the team is in\n");
6048c2ecf20Sopenharmony_ci		return -EINVAL;
6058c2ecf20Sopenharmony_ci	}
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci	new_mode = team_mode_get(kind);
6088c2ecf20Sopenharmony_ci	if (!new_mode) {
6098c2ecf20Sopenharmony_ci		netdev_err(dev, "Mode \"%s\" not found\n", kind);
6108c2ecf20Sopenharmony_ci		return -EINVAL;
6118c2ecf20Sopenharmony_ci	}
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci	err = __team_change_mode(team, new_mode);
6148c2ecf20Sopenharmony_ci	if (err) {
6158c2ecf20Sopenharmony_ci		netdev_err(dev, "Failed to change to mode \"%s\"\n", kind);
6168c2ecf20Sopenharmony_ci		team_mode_put(new_mode);
6178c2ecf20Sopenharmony_ci		return err;
6188c2ecf20Sopenharmony_ci	}
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_ci	netdev_info(dev, "Mode changed to \"%s\"\n", kind);
6218c2ecf20Sopenharmony_ci	return 0;
6228c2ecf20Sopenharmony_ci}
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_ci/*********************
6268c2ecf20Sopenharmony_ci * Peers notification
6278c2ecf20Sopenharmony_ci *********************/
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_cistatic void team_notify_peers_work(struct work_struct *work)
6308c2ecf20Sopenharmony_ci{
6318c2ecf20Sopenharmony_ci	struct team *team;
6328c2ecf20Sopenharmony_ci	int val;
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_ci	team = container_of(work, struct team, notify_peers.dw.work);
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_ci	if (!rtnl_trylock()) {
6378c2ecf20Sopenharmony_ci		schedule_delayed_work(&team->notify_peers.dw, 0);
6388c2ecf20Sopenharmony_ci		return;
6398c2ecf20Sopenharmony_ci	}
6408c2ecf20Sopenharmony_ci	val = atomic_dec_if_positive(&team->notify_peers.count_pending);
6418c2ecf20Sopenharmony_ci	if (val < 0) {
6428c2ecf20Sopenharmony_ci		rtnl_unlock();
6438c2ecf20Sopenharmony_ci		return;
6448c2ecf20Sopenharmony_ci	}
6458c2ecf20Sopenharmony_ci	call_netdevice_notifiers(NETDEV_NOTIFY_PEERS, team->dev);
6468c2ecf20Sopenharmony_ci	rtnl_unlock();
6478c2ecf20Sopenharmony_ci	if (val)
6488c2ecf20Sopenharmony_ci		schedule_delayed_work(&team->notify_peers.dw,
6498c2ecf20Sopenharmony_ci				      msecs_to_jiffies(team->notify_peers.interval));
6508c2ecf20Sopenharmony_ci}
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_cistatic void team_notify_peers(struct team *team)
6538c2ecf20Sopenharmony_ci{
6548c2ecf20Sopenharmony_ci	if (!team->notify_peers.count || !netif_running(team->dev))
6558c2ecf20Sopenharmony_ci		return;
6568c2ecf20Sopenharmony_ci	atomic_add(team->notify_peers.count, &team->notify_peers.count_pending);
6578c2ecf20Sopenharmony_ci	schedule_delayed_work(&team->notify_peers.dw, 0);
6588c2ecf20Sopenharmony_ci}
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_cistatic void team_notify_peers_init(struct team *team)
6618c2ecf20Sopenharmony_ci{
6628c2ecf20Sopenharmony_ci	INIT_DELAYED_WORK(&team->notify_peers.dw, team_notify_peers_work);
6638c2ecf20Sopenharmony_ci}
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_cistatic void team_notify_peers_fini(struct team *team)
6668c2ecf20Sopenharmony_ci{
6678c2ecf20Sopenharmony_ci	cancel_delayed_work_sync(&team->notify_peers.dw);
6688c2ecf20Sopenharmony_ci}
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_ci
6718c2ecf20Sopenharmony_ci/*******************************
6728c2ecf20Sopenharmony_ci * Send multicast group rejoins
6738c2ecf20Sopenharmony_ci *******************************/
6748c2ecf20Sopenharmony_ci
6758c2ecf20Sopenharmony_cistatic void team_mcast_rejoin_work(struct work_struct *work)
6768c2ecf20Sopenharmony_ci{
6778c2ecf20Sopenharmony_ci	struct team *team;
6788c2ecf20Sopenharmony_ci	int val;
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_ci	team = container_of(work, struct team, mcast_rejoin.dw.work);
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_ci	if (!rtnl_trylock()) {
6838c2ecf20Sopenharmony_ci		schedule_delayed_work(&team->mcast_rejoin.dw, 0);
6848c2ecf20Sopenharmony_ci		return;
6858c2ecf20Sopenharmony_ci	}
6868c2ecf20Sopenharmony_ci	val = atomic_dec_if_positive(&team->mcast_rejoin.count_pending);
6878c2ecf20Sopenharmony_ci	if (val < 0) {
6888c2ecf20Sopenharmony_ci		rtnl_unlock();
6898c2ecf20Sopenharmony_ci		return;
6908c2ecf20Sopenharmony_ci	}
6918c2ecf20Sopenharmony_ci	call_netdevice_notifiers(NETDEV_RESEND_IGMP, team->dev);
6928c2ecf20Sopenharmony_ci	rtnl_unlock();
6938c2ecf20Sopenharmony_ci	if (val)
6948c2ecf20Sopenharmony_ci		schedule_delayed_work(&team->mcast_rejoin.dw,
6958c2ecf20Sopenharmony_ci				      msecs_to_jiffies(team->mcast_rejoin.interval));
6968c2ecf20Sopenharmony_ci}
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_cistatic void team_mcast_rejoin(struct team *team)
6998c2ecf20Sopenharmony_ci{
7008c2ecf20Sopenharmony_ci	if (!team->mcast_rejoin.count || !netif_running(team->dev))
7018c2ecf20Sopenharmony_ci		return;
7028c2ecf20Sopenharmony_ci	atomic_add(team->mcast_rejoin.count, &team->mcast_rejoin.count_pending);
7038c2ecf20Sopenharmony_ci	schedule_delayed_work(&team->mcast_rejoin.dw, 0);
7048c2ecf20Sopenharmony_ci}
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_cistatic void team_mcast_rejoin_init(struct team *team)
7078c2ecf20Sopenharmony_ci{
7088c2ecf20Sopenharmony_ci	INIT_DELAYED_WORK(&team->mcast_rejoin.dw, team_mcast_rejoin_work);
7098c2ecf20Sopenharmony_ci}
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_cistatic void team_mcast_rejoin_fini(struct team *team)
7128c2ecf20Sopenharmony_ci{
7138c2ecf20Sopenharmony_ci	cancel_delayed_work_sync(&team->mcast_rejoin.dw);
7148c2ecf20Sopenharmony_ci}
7158c2ecf20Sopenharmony_ci
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci/************************
7188c2ecf20Sopenharmony_ci * Rx path frame handler
7198c2ecf20Sopenharmony_ci ************************/
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_ci/* note: already called with rcu_read_lock */
7228c2ecf20Sopenharmony_cistatic rx_handler_result_t team_handle_frame(struct sk_buff **pskb)
7238c2ecf20Sopenharmony_ci{
7248c2ecf20Sopenharmony_ci	struct sk_buff *skb = *pskb;
7258c2ecf20Sopenharmony_ci	struct team_port *port;
7268c2ecf20Sopenharmony_ci	struct team *team;
7278c2ecf20Sopenharmony_ci	rx_handler_result_t res;
7288c2ecf20Sopenharmony_ci
7298c2ecf20Sopenharmony_ci	skb = skb_share_check(skb, GFP_ATOMIC);
7308c2ecf20Sopenharmony_ci	if (!skb)
7318c2ecf20Sopenharmony_ci		return RX_HANDLER_CONSUMED;
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci	*pskb = skb;
7348c2ecf20Sopenharmony_ci
7358c2ecf20Sopenharmony_ci	port = team_port_get_rcu(skb->dev);
7368c2ecf20Sopenharmony_ci	team = port->team;
7378c2ecf20Sopenharmony_ci	if (!team_port_enabled(port)) {
7388c2ecf20Sopenharmony_ci		/* allow exact match delivery for disabled ports */
7398c2ecf20Sopenharmony_ci		res = RX_HANDLER_EXACT;
7408c2ecf20Sopenharmony_ci	} else {
7418c2ecf20Sopenharmony_ci		res = team->ops.receive(team, port, skb);
7428c2ecf20Sopenharmony_ci	}
7438c2ecf20Sopenharmony_ci	if (res == RX_HANDLER_ANOTHER) {
7448c2ecf20Sopenharmony_ci		struct team_pcpu_stats *pcpu_stats;
7458c2ecf20Sopenharmony_ci
7468c2ecf20Sopenharmony_ci		pcpu_stats = this_cpu_ptr(team->pcpu_stats);
7478c2ecf20Sopenharmony_ci		u64_stats_update_begin(&pcpu_stats->syncp);
7488c2ecf20Sopenharmony_ci		pcpu_stats->rx_packets++;
7498c2ecf20Sopenharmony_ci		pcpu_stats->rx_bytes += skb->len;
7508c2ecf20Sopenharmony_ci		if (skb->pkt_type == PACKET_MULTICAST)
7518c2ecf20Sopenharmony_ci			pcpu_stats->rx_multicast++;
7528c2ecf20Sopenharmony_ci		u64_stats_update_end(&pcpu_stats->syncp);
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_ci		skb->dev = team->dev;
7558c2ecf20Sopenharmony_ci	} else if (res == RX_HANDLER_EXACT) {
7568c2ecf20Sopenharmony_ci		this_cpu_inc(team->pcpu_stats->rx_nohandler);
7578c2ecf20Sopenharmony_ci	} else {
7588c2ecf20Sopenharmony_ci		this_cpu_inc(team->pcpu_stats->rx_dropped);
7598c2ecf20Sopenharmony_ci	}
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_ci	return res;
7628c2ecf20Sopenharmony_ci}
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_ci/*************************************
7668c2ecf20Sopenharmony_ci * Multiqueue Tx port select override
7678c2ecf20Sopenharmony_ci *************************************/
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_cistatic int team_queue_override_init(struct team *team)
7708c2ecf20Sopenharmony_ci{
7718c2ecf20Sopenharmony_ci	struct list_head *listarr;
7728c2ecf20Sopenharmony_ci	unsigned int queue_cnt = team->dev->num_tx_queues - 1;
7738c2ecf20Sopenharmony_ci	unsigned int i;
7748c2ecf20Sopenharmony_ci
7758c2ecf20Sopenharmony_ci	if (!queue_cnt)
7768c2ecf20Sopenharmony_ci		return 0;
7778c2ecf20Sopenharmony_ci	listarr = kmalloc_array(queue_cnt, sizeof(struct list_head),
7788c2ecf20Sopenharmony_ci				GFP_KERNEL);
7798c2ecf20Sopenharmony_ci	if (!listarr)
7808c2ecf20Sopenharmony_ci		return -ENOMEM;
7818c2ecf20Sopenharmony_ci	team->qom_lists = listarr;
7828c2ecf20Sopenharmony_ci	for (i = 0; i < queue_cnt; i++)
7838c2ecf20Sopenharmony_ci		INIT_LIST_HEAD(listarr++);
7848c2ecf20Sopenharmony_ci	return 0;
7858c2ecf20Sopenharmony_ci}
7868c2ecf20Sopenharmony_ci
7878c2ecf20Sopenharmony_cistatic void team_queue_override_fini(struct team *team)
7888c2ecf20Sopenharmony_ci{
7898c2ecf20Sopenharmony_ci	kfree(team->qom_lists);
7908c2ecf20Sopenharmony_ci}
7918c2ecf20Sopenharmony_ci
7928c2ecf20Sopenharmony_cistatic struct list_head *__team_get_qom_list(struct team *team, u16 queue_id)
7938c2ecf20Sopenharmony_ci{
7948c2ecf20Sopenharmony_ci	return &team->qom_lists[queue_id - 1];
7958c2ecf20Sopenharmony_ci}
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_ci/*
7988c2ecf20Sopenharmony_ci * note: already called with rcu_read_lock
7998c2ecf20Sopenharmony_ci */
8008c2ecf20Sopenharmony_cistatic bool team_queue_override_transmit(struct team *team, struct sk_buff *skb)
8018c2ecf20Sopenharmony_ci{
8028c2ecf20Sopenharmony_ci	struct list_head *qom_list;
8038c2ecf20Sopenharmony_ci	struct team_port *port;
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ci	if (!team->queue_override_enabled || !skb->queue_mapping)
8068c2ecf20Sopenharmony_ci		return false;
8078c2ecf20Sopenharmony_ci	qom_list = __team_get_qom_list(team, skb->queue_mapping);
8088c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(port, qom_list, qom_list) {
8098c2ecf20Sopenharmony_ci		if (!team_dev_queue_xmit(team, port, skb))
8108c2ecf20Sopenharmony_ci			return true;
8118c2ecf20Sopenharmony_ci	}
8128c2ecf20Sopenharmony_ci	return false;
8138c2ecf20Sopenharmony_ci}
8148c2ecf20Sopenharmony_ci
8158c2ecf20Sopenharmony_cistatic void __team_queue_override_port_del(struct team *team,
8168c2ecf20Sopenharmony_ci					   struct team_port *port)
8178c2ecf20Sopenharmony_ci{
8188c2ecf20Sopenharmony_ci	if (!port->queue_id)
8198c2ecf20Sopenharmony_ci		return;
8208c2ecf20Sopenharmony_ci	list_del_rcu(&port->qom_list);
8218c2ecf20Sopenharmony_ci}
8228c2ecf20Sopenharmony_ci
8238c2ecf20Sopenharmony_cistatic bool team_queue_override_port_has_gt_prio_than(struct team_port *port,
8248c2ecf20Sopenharmony_ci						      struct team_port *cur)
8258c2ecf20Sopenharmony_ci{
8268c2ecf20Sopenharmony_ci	if (port->priority < cur->priority)
8278c2ecf20Sopenharmony_ci		return true;
8288c2ecf20Sopenharmony_ci	if (port->priority > cur->priority)
8298c2ecf20Sopenharmony_ci		return false;
8308c2ecf20Sopenharmony_ci	if (port->index < cur->index)
8318c2ecf20Sopenharmony_ci		return true;
8328c2ecf20Sopenharmony_ci	return false;
8338c2ecf20Sopenharmony_ci}
8348c2ecf20Sopenharmony_ci
8358c2ecf20Sopenharmony_cistatic void __team_queue_override_port_add(struct team *team,
8368c2ecf20Sopenharmony_ci					   struct team_port *port)
8378c2ecf20Sopenharmony_ci{
8388c2ecf20Sopenharmony_ci	struct team_port *cur;
8398c2ecf20Sopenharmony_ci	struct list_head *qom_list;
8408c2ecf20Sopenharmony_ci	struct list_head *node;
8418c2ecf20Sopenharmony_ci
8428c2ecf20Sopenharmony_ci	if (!port->queue_id)
8438c2ecf20Sopenharmony_ci		return;
8448c2ecf20Sopenharmony_ci	qom_list = __team_get_qom_list(team, port->queue_id);
8458c2ecf20Sopenharmony_ci	node = qom_list;
8468c2ecf20Sopenharmony_ci	list_for_each_entry(cur, qom_list, qom_list) {
8478c2ecf20Sopenharmony_ci		if (team_queue_override_port_has_gt_prio_than(port, cur))
8488c2ecf20Sopenharmony_ci			break;
8498c2ecf20Sopenharmony_ci		node = &cur->qom_list;
8508c2ecf20Sopenharmony_ci	}
8518c2ecf20Sopenharmony_ci	list_add_tail_rcu(&port->qom_list, node);
8528c2ecf20Sopenharmony_ci}
8538c2ecf20Sopenharmony_ci
8548c2ecf20Sopenharmony_cistatic void __team_queue_override_enabled_check(struct team *team)
8558c2ecf20Sopenharmony_ci{
8568c2ecf20Sopenharmony_ci	struct team_port *port;
8578c2ecf20Sopenharmony_ci	bool enabled = false;
8588c2ecf20Sopenharmony_ci
8598c2ecf20Sopenharmony_ci	list_for_each_entry(port, &team->port_list, list) {
8608c2ecf20Sopenharmony_ci		if (port->queue_id) {
8618c2ecf20Sopenharmony_ci			enabled = true;
8628c2ecf20Sopenharmony_ci			break;
8638c2ecf20Sopenharmony_ci		}
8648c2ecf20Sopenharmony_ci	}
8658c2ecf20Sopenharmony_ci	if (enabled == team->queue_override_enabled)
8668c2ecf20Sopenharmony_ci		return;
8678c2ecf20Sopenharmony_ci	netdev_dbg(team->dev, "%s queue override\n",
8688c2ecf20Sopenharmony_ci		   enabled ? "Enabling" : "Disabling");
8698c2ecf20Sopenharmony_ci	team->queue_override_enabled = enabled;
8708c2ecf20Sopenharmony_ci}
8718c2ecf20Sopenharmony_ci
8728c2ecf20Sopenharmony_cistatic void team_queue_override_port_prio_changed(struct team *team,
8738c2ecf20Sopenharmony_ci						  struct team_port *port)
8748c2ecf20Sopenharmony_ci{
8758c2ecf20Sopenharmony_ci	if (!port->queue_id || team_port_enabled(port))
8768c2ecf20Sopenharmony_ci		return;
8778c2ecf20Sopenharmony_ci	__team_queue_override_port_del(team, port);
8788c2ecf20Sopenharmony_ci	__team_queue_override_port_add(team, port);
8798c2ecf20Sopenharmony_ci	__team_queue_override_enabled_check(team);
8808c2ecf20Sopenharmony_ci}
8818c2ecf20Sopenharmony_ci
8828c2ecf20Sopenharmony_cistatic void team_queue_override_port_change_queue_id(struct team *team,
8838c2ecf20Sopenharmony_ci						     struct team_port *port,
8848c2ecf20Sopenharmony_ci						     u16 new_queue_id)
8858c2ecf20Sopenharmony_ci{
8868c2ecf20Sopenharmony_ci	if (team_port_enabled(port)) {
8878c2ecf20Sopenharmony_ci		__team_queue_override_port_del(team, port);
8888c2ecf20Sopenharmony_ci		port->queue_id = new_queue_id;
8898c2ecf20Sopenharmony_ci		__team_queue_override_port_add(team, port);
8908c2ecf20Sopenharmony_ci		__team_queue_override_enabled_check(team);
8918c2ecf20Sopenharmony_ci	} else {
8928c2ecf20Sopenharmony_ci		port->queue_id = new_queue_id;
8938c2ecf20Sopenharmony_ci	}
8948c2ecf20Sopenharmony_ci}
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_cistatic void team_queue_override_port_add(struct team *team,
8978c2ecf20Sopenharmony_ci					 struct team_port *port)
8988c2ecf20Sopenharmony_ci{
8998c2ecf20Sopenharmony_ci	__team_queue_override_port_add(team, port);
9008c2ecf20Sopenharmony_ci	__team_queue_override_enabled_check(team);
9018c2ecf20Sopenharmony_ci}
9028c2ecf20Sopenharmony_ci
9038c2ecf20Sopenharmony_cistatic void team_queue_override_port_del(struct team *team,
9048c2ecf20Sopenharmony_ci					 struct team_port *port)
9058c2ecf20Sopenharmony_ci{
9068c2ecf20Sopenharmony_ci	__team_queue_override_port_del(team, port);
9078c2ecf20Sopenharmony_ci	__team_queue_override_enabled_check(team);
9088c2ecf20Sopenharmony_ci}
9098c2ecf20Sopenharmony_ci
9108c2ecf20Sopenharmony_ci
9118c2ecf20Sopenharmony_ci/****************
9128c2ecf20Sopenharmony_ci * Port handling
9138c2ecf20Sopenharmony_ci ****************/
9148c2ecf20Sopenharmony_ci
9158c2ecf20Sopenharmony_cistatic bool team_port_find(const struct team *team,
9168c2ecf20Sopenharmony_ci			   const struct team_port *port)
9178c2ecf20Sopenharmony_ci{
9188c2ecf20Sopenharmony_ci	struct team_port *cur;
9198c2ecf20Sopenharmony_ci
9208c2ecf20Sopenharmony_ci	list_for_each_entry(cur, &team->port_list, list)
9218c2ecf20Sopenharmony_ci		if (cur == port)
9228c2ecf20Sopenharmony_ci			return true;
9238c2ecf20Sopenharmony_ci	return false;
9248c2ecf20Sopenharmony_ci}
9258c2ecf20Sopenharmony_ci
9268c2ecf20Sopenharmony_ci/*
9278c2ecf20Sopenharmony_ci * Enable/disable port by adding to enabled port hashlist and setting
9288c2ecf20Sopenharmony_ci * port->index (Might be racy so reader could see incorrect ifindex when
9298c2ecf20Sopenharmony_ci * processing a flying packet, but that is not a problem). Write guarded
9308c2ecf20Sopenharmony_ci * by team->lock.
9318c2ecf20Sopenharmony_ci */
9328c2ecf20Sopenharmony_cistatic void team_port_enable(struct team *team,
9338c2ecf20Sopenharmony_ci			     struct team_port *port)
9348c2ecf20Sopenharmony_ci{
9358c2ecf20Sopenharmony_ci	if (team_port_enabled(port))
9368c2ecf20Sopenharmony_ci		return;
9378c2ecf20Sopenharmony_ci	port->index = team->en_port_count++;
9388c2ecf20Sopenharmony_ci	hlist_add_head_rcu(&port->hlist,
9398c2ecf20Sopenharmony_ci			   team_port_index_hash(team, port->index));
9408c2ecf20Sopenharmony_ci	team_adjust_ops(team);
9418c2ecf20Sopenharmony_ci	team_queue_override_port_add(team, port);
9428c2ecf20Sopenharmony_ci	if (team->ops.port_enabled)
9438c2ecf20Sopenharmony_ci		team->ops.port_enabled(team, port);
9448c2ecf20Sopenharmony_ci	team_notify_peers(team);
9458c2ecf20Sopenharmony_ci	team_mcast_rejoin(team);
9468c2ecf20Sopenharmony_ci	team_lower_state_changed(port);
9478c2ecf20Sopenharmony_ci}
9488c2ecf20Sopenharmony_ci
9498c2ecf20Sopenharmony_cistatic void __reconstruct_port_hlist(struct team *team, int rm_index)
9508c2ecf20Sopenharmony_ci{
9518c2ecf20Sopenharmony_ci	int i;
9528c2ecf20Sopenharmony_ci	struct team_port *port;
9538c2ecf20Sopenharmony_ci
9548c2ecf20Sopenharmony_ci	for (i = rm_index + 1; i < team->en_port_count; i++) {
9558c2ecf20Sopenharmony_ci		port = team_get_port_by_index(team, i);
9568c2ecf20Sopenharmony_ci		hlist_del_rcu(&port->hlist);
9578c2ecf20Sopenharmony_ci		port->index--;
9588c2ecf20Sopenharmony_ci		hlist_add_head_rcu(&port->hlist,
9598c2ecf20Sopenharmony_ci				   team_port_index_hash(team, port->index));
9608c2ecf20Sopenharmony_ci	}
9618c2ecf20Sopenharmony_ci}
9628c2ecf20Sopenharmony_ci
9638c2ecf20Sopenharmony_cistatic void team_port_disable(struct team *team,
9648c2ecf20Sopenharmony_ci			      struct team_port *port)
9658c2ecf20Sopenharmony_ci{
9668c2ecf20Sopenharmony_ci	if (!team_port_enabled(port))
9678c2ecf20Sopenharmony_ci		return;
9688c2ecf20Sopenharmony_ci	if (team->ops.port_disabled)
9698c2ecf20Sopenharmony_ci		team->ops.port_disabled(team, port);
9708c2ecf20Sopenharmony_ci	hlist_del_rcu(&port->hlist);
9718c2ecf20Sopenharmony_ci	__reconstruct_port_hlist(team, port->index);
9728c2ecf20Sopenharmony_ci	port->index = -1;
9738c2ecf20Sopenharmony_ci	team->en_port_count--;
9748c2ecf20Sopenharmony_ci	team_queue_override_port_del(team, port);
9758c2ecf20Sopenharmony_ci	team_adjust_ops(team);
9768c2ecf20Sopenharmony_ci	team_lower_state_changed(port);
9778c2ecf20Sopenharmony_ci}
9788c2ecf20Sopenharmony_ci
9798c2ecf20Sopenharmony_ci#define TEAM_VLAN_FEATURES (NETIF_F_HW_CSUM | NETIF_F_SG | \
9808c2ecf20Sopenharmony_ci			    NETIF_F_FRAGLIST | NETIF_F_ALL_TSO | \
9818c2ecf20Sopenharmony_ci			    NETIF_F_HIGHDMA | NETIF_F_LRO)
9828c2ecf20Sopenharmony_ci
9838c2ecf20Sopenharmony_ci#define TEAM_ENC_FEATURES	(NETIF_F_HW_CSUM | NETIF_F_SG | \
9848c2ecf20Sopenharmony_ci				 NETIF_F_RXCSUM | NETIF_F_ALL_TSO)
9858c2ecf20Sopenharmony_ci
9868c2ecf20Sopenharmony_cistatic void __team_compute_features(struct team *team)
9878c2ecf20Sopenharmony_ci{
9888c2ecf20Sopenharmony_ci	struct team_port *port;
9898c2ecf20Sopenharmony_ci	netdev_features_t vlan_features = TEAM_VLAN_FEATURES &
9908c2ecf20Sopenharmony_ci					  NETIF_F_ALL_FOR_ALL;
9918c2ecf20Sopenharmony_ci	netdev_features_t enc_features  = TEAM_ENC_FEATURES;
9928c2ecf20Sopenharmony_ci	unsigned short max_hard_header_len = ETH_HLEN;
9938c2ecf20Sopenharmony_ci	unsigned int dst_release_flag = IFF_XMIT_DST_RELEASE |
9948c2ecf20Sopenharmony_ci					IFF_XMIT_DST_RELEASE_PERM;
9958c2ecf20Sopenharmony_ci
9968c2ecf20Sopenharmony_ci	rcu_read_lock();
9978c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(port, &team->port_list, list) {
9988c2ecf20Sopenharmony_ci		vlan_features = netdev_increment_features(vlan_features,
9998c2ecf20Sopenharmony_ci					port->dev->vlan_features,
10008c2ecf20Sopenharmony_ci					TEAM_VLAN_FEATURES);
10018c2ecf20Sopenharmony_ci		enc_features =
10028c2ecf20Sopenharmony_ci			netdev_increment_features(enc_features,
10038c2ecf20Sopenharmony_ci						  port->dev->hw_enc_features,
10048c2ecf20Sopenharmony_ci						  TEAM_ENC_FEATURES);
10058c2ecf20Sopenharmony_ci
10068c2ecf20Sopenharmony_ci
10078c2ecf20Sopenharmony_ci		dst_release_flag &= port->dev->priv_flags;
10088c2ecf20Sopenharmony_ci		if (port->dev->hard_header_len > max_hard_header_len)
10098c2ecf20Sopenharmony_ci			max_hard_header_len = port->dev->hard_header_len;
10108c2ecf20Sopenharmony_ci	}
10118c2ecf20Sopenharmony_ci	rcu_read_unlock();
10128c2ecf20Sopenharmony_ci
10138c2ecf20Sopenharmony_ci	team->dev->vlan_features = vlan_features;
10148c2ecf20Sopenharmony_ci	team->dev->hw_enc_features = enc_features | NETIF_F_GSO_ENCAP_ALL |
10158c2ecf20Sopenharmony_ci				     NETIF_F_HW_VLAN_CTAG_TX |
10168c2ecf20Sopenharmony_ci				     NETIF_F_HW_VLAN_STAG_TX |
10178c2ecf20Sopenharmony_ci				     NETIF_F_GSO_UDP_L4;
10188c2ecf20Sopenharmony_ci	team->dev->hard_header_len = max_hard_header_len;
10198c2ecf20Sopenharmony_ci
10208c2ecf20Sopenharmony_ci	team->dev->priv_flags &= ~IFF_XMIT_DST_RELEASE;
10218c2ecf20Sopenharmony_ci	if (dst_release_flag == (IFF_XMIT_DST_RELEASE | IFF_XMIT_DST_RELEASE_PERM))
10228c2ecf20Sopenharmony_ci		team->dev->priv_flags |= IFF_XMIT_DST_RELEASE;
10238c2ecf20Sopenharmony_ci}
10248c2ecf20Sopenharmony_ci
10258c2ecf20Sopenharmony_cistatic void team_compute_features(struct team *team)
10268c2ecf20Sopenharmony_ci{
10278c2ecf20Sopenharmony_ci	__team_compute_features(team);
10288c2ecf20Sopenharmony_ci	netdev_change_features(team->dev);
10298c2ecf20Sopenharmony_ci}
10308c2ecf20Sopenharmony_ci
10318c2ecf20Sopenharmony_cistatic int team_port_enter(struct team *team, struct team_port *port)
10328c2ecf20Sopenharmony_ci{
10338c2ecf20Sopenharmony_ci	int err = 0;
10348c2ecf20Sopenharmony_ci
10358c2ecf20Sopenharmony_ci	dev_hold(team->dev);
10368c2ecf20Sopenharmony_ci	if (team->ops.port_enter) {
10378c2ecf20Sopenharmony_ci		err = team->ops.port_enter(team, port);
10388c2ecf20Sopenharmony_ci		if (err) {
10398c2ecf20Sopenharmony_ci			netdev_err(team->dev, "Device %s failed to enter team mode\n",
10408c2ecf20Sopenharmony_ci				   port->dev->name);
10418c2ecf20Sopenharmony_ci			goto err_port_enter;
10428c2ecf20Sopenharmony_ci		}
10438c2ecf20Sopenharmony_ci	}
10448c2ecf20Sopenharmony_ci
10458c2ecf20Sopenharmony_ci	return 0;
10468c2ecf20Sopenharmony_ci
10478c2ecf20Sopenharmony_cierr_port_enter:
10488c2ecf20Sopenharmony_ci	dev_put(team->dev);
10498c2ecf20Sopenharmony_ci
10508c2ecf20Sopenharmony_ci	return err;
10518c2ecf20Sopenharmony_ci}
10528c2ecf20Sopenharmony_ci
10538c2ecf20Sopenharmony_cistatic void team_port_leave(struct team *team, struct team_port *port)
10548c2ecf20Sopenharmony_ci{
10558c2ecf20Sopenharmony_ci	if (team->ops.port_leave)
10568c2ecf20Sopenharmony_ci		team->ops.port_leave(team, port);
10578c2ecf20Sopenharmony_ci	dev_put(team->dev);
10588c2ecf20Sopenharmony_ci}
10598c2ecf20Sopenharmony_ci
10608c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER
10618c2ecf20Sopenharmony_cistatic int __team_port_enable_netpoll(struct team_port *port)
10628c2ecf20Sopenharmony_ci{
10638c2ecf20Sopenharmony_ci	struct netpoll *np;
10648c2ecf20Sopenharmony_ci	int err;
10658c2ecf20Sopenharmony_ci
10668c2ecf20Sopenharmony_ci	np = kzalloc(sizeof(*np), GFP_KERNEL);
10678c2ecf20Sopenharmony_ci	if (!np)
10688c2ecf20Sopenharmony_ci		return -ENOMEM;
10698c2ecf20Sopenharmony_ci
10708c2ecf20Sopenharmony_ci	err = __netpoll_setup(np, port->dev);
10718c2ecf20Sopenharmony_ci	if (err) {
10728c2ecf20Sopenharmony_ci		kfree(np);
10738c2ecf20Sopenharmony_ci		return err;
10748c2ecf20Sopenharmony_ci	}
10758c2ecf20Sopenharmony_ci	port->np = np;
10768c2ecf20Sopenharmony_ci	return err;
10778c2ecf20Sopenharmony_ci}
10788c2ecf20Sopenharmony_ci
10798c2ecf20Sopenharmony_cistatic int team_port_enable_netpoll(struct team_port *port)
10808c2ecf20Sopenharmony_ci{
10818c2ecf20Sopenharmony_ci	if (!port->team->dev->npinfo)
10828c2ecf20Sopenharmony_ci		return 0;
10838c2ecf20Sopenharmony_ci
10848c2ecf20Sopenharmony_ci	return __team_port_enable_netpoll(port);
10858c2ecf20Sopenharmony_ci}
10868c2ecf20Sopenharmony_ci
10878c2ecf20Sopenharmony_cistatic void team_port_disable_netpoll(struct team_port *port)
10888c2ecf20Sopenharmony_ci{
10898c2ecf20Sopenharmony_ci	struct netpoll *np = port->np;
10908c2ecf20Sopenharmony_ci
10918c2ecf20Sopenharmony_ci	if (!np)
10928c2ecf20Sopenharmony_ci		return;
10938c2ecf20Sopenharmony_ci	port->np = NULL;
10948c2ecf20Sopenharmony_ci
10958c2ecf20Sopenharmony_ci	__netpoll_free(np);
10968c2ecf20Sopenharmony_ci}
10978c2ecf20Sopenharmony_ci#else
10988c2ecf20Sopenharmony_cistatic int team_port_enable_netpoll(struct team_port *port)
10998c2ecf20Sopenharmony_ci{
11008c2ecf20Sopenharmony_ci	return 0;
11018c2ecf20Sopenharmony_ci}
11028c2ecf20Sopenharmony_cistatic void team_port_disable_netpoll(struct team_port *port)
11038c2ecf20Sopenharmony_ci{
11048c2ecf20Sopenharmony_ci}
11058c2ecf20Sopenharmony_ci#endif
11068c2ecf20Sopenharmony_ci
11078c2ecf20Sopenharmony_cistatic int team_upper_dev_link(struct team *team, struct team_port *port,
11088c2ecf20Sopenharmony_ci			       struct netlink_ext_ack *extack)
11098c2ecf20Sopenharmony_ci{
11108c2ecf20Sopenharmony_ci	struct netdev_lag_upper_info lag_upper_info;
11118c2ecf20Sopenharmony_ci	int err;
11128c2ecf20Sopenharmony_ci
11138c2ecf20Sopenharmony_ci	lag_upper_info.tx_type = team->mode->lag_tx_type;
11148c2ecf20Sopenharmony_ci	lag_upper_info.hash_type = NETDEV_LAG_HASH_UNKNOWN;
11158c2ecf20Sopenharmony_ci	err = netdev_master_upper_dev_link(port->dev, team->dev, NULL,
11168c2ecf20Sopenharmony_ci					   &lag_upper_info, extack);
11178c2ecf20Sopenharmony_ci	if (err)
11188c2ecf20Sopenharmony_ci		return err;
11198c2ecf20Sopenharmony_ci	port->dev->priv_flags |= IFF_TEAM_PORT;
11208c2ecf20Sopenharmony_ci	return 0;
11218c2ecf20Sopenharmony_ci}
11228c2ecf20Sopenharmony_ci
11238c2ecf20Sopenharmony_cistatic void team_upper_dev_unlink(struct team *team, struct team_port *port)
11248c2ecf20Sopenharmony_ci{
11258c2ecf20Sopenharmony_ci	netdev_upper_dev_unlink(port->dev, team->dev);
11268c2ecf20Sopenharmony_ci	port->dev->priv_flags &= ~IFF_TEAM_PORT;
11278c2ecf20Sopenharmony_ci}
11288c2ecf20Sopenharmony_ci
11298c2ecf20Sopenharmony_cistatic void __team_port_change_port_added(struct team_port *port, bool linkup);
11308c2ecf20Sopenharmony_cistatic int team_dev_type_check_change(struct net_device *dev,
11318c2ecf20Sopenharmony_ci				      struct net_device *port_dev);
11328c2ecf20Sopenharmony_ci
11338c2ecf20Sopenharmony_cistatic int team_port_add(struct team *team, struct net_device *port_dev,
11348c2ecf20Sopenharmony_ci			 struct netlink_ext_ack *extack)
11358c2ecf20Sopenharmony_ci{
11368c2ecf20Sopenharmony_ci	struct net_device *dev = team->dev;
11378c2ecf20Sopenharmony_ci	struct team_port *port;
11388c2ecf20Sopenharmony_ci	char *portname = port_dev->name;
11398c2ecf20Sopenharmony_ci	int err;
11408c2ecf20Sopenharmony_ci
11418c2ecf20Sopenharmony_ci	if (port_dev->flags & IFF_LOOPBACK) {
11428c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Loopback device can't be added as a team port");
11438c2ecf20Sopenharmony_ci		netdev_err(dev, "Device %s is loopback device. Loopback devices can't be added as a team port\n",
11448c2ecf20Sopenharmony_ci			   portname);
11458c2ecf20Sopenharmony_ci		return -EINVAL;
11468c2ecf20Sopenharmony_ci	}
11478c2ecf20Sopenharmony_ci
11488c2ecf20Sopenharmony_ci	if (netif_is_team_port(port_dev)) {
11498c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Device is already a port of a team device");
11508c2ecf20Sopenharmony_ci		netdev_err(dev, "Device %s is already a port "
11518c2ecf20Sopenharmony_ci				"of a team device\n", portname);
11528c2ecf20Sopenharmony_ci		return -EBUSY;
11538c2ecf20Sopenharmony_ci	}
11548c2ecf20Sopenharmony_ci
11558c2ecf20Sopenharmony_ci	if (dev == port_dev) {
11568c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Cannot enslave team device to itself");
11578c2ecf20Sopenharmony_ci		netdev_err(dev, "Cannot enslave team device to itself\n");
11588c2ecf20Sopenharmony_ci		return -EINVAL;
11598c2ecf20Sopenharmony_ci	}
11608c2ecf20Sopenharmony_ci
11618c2ecf20Sopenharmony_ci	if (netdev_has_upper_dev(dev, port_dev)) {
11628c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Device is already an upper device of the team interface");
11638c2ecf20Sopenharmony_ci		netdev_err(dev, "Device %s is already an upper device of the team interface\n",
11648c2ecf20Sopenharmony_ci			   portname);
11658c2ecf20Sopenharmony_ci		return -EBUSY;
11668c2ecf20Sopenharmony_ci	}
11678c2ecf20Sopenharmony_ci
11688c2ecf20Sopenharmony_ci	if (port_dev->features & NETIF_F_VLAN_CHALLENGED &&
11698c2ecf20Sopenharmony_ci	    vlan_uses_dev(dev)) {
11708c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Device is VLAN challenged and team device has VLAN set up");
11718c2ecf20Sopenharmony_ci		netdev_err(dev, "Device %s is VLAN challenged and team device has VLAN set up\n",
11728c2ecf20Sopenharmony_ci			   portname);
11738c2ecf20Sopenharmony_ci		return -EPERM;
11748c2ecf20Sopenharmony_ci	}
11758c2ecf20Sopenharmony_ci
11768c2ecf20Sopenharmony_ci	err = team_dev_type_check_change(dev, port_dev);
11778c2ecf20Sopenharmony_ci	if (err)
11788c2ecf20Sopenharmony_ci		return err;
11798c2ecf20Sopenharmony_ci
11808c2ecf20Sopenharmony_ci	if (port_dev->flags & IFF_UP) {
11818c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Device is up. Set it down before adding it as a team port");
11828c2ecf20Sopenharmony_ci		netdev_err(dev, "Device %s is up. Set it down before adding it as a team port\n",
11838c2ecf20Sopenharmony_ci			   portname);
11848c2ecf20Sopenharmony_ci		return -EBUSY;
11858c2ecf20Sopenharmony_ci	}
11868c2ecf20Sopenharmony_ci
11878c2ecf20Sopenharmony_ci	port = kzalloc(sizeof(struct team_port) + team->mode->port_priv_size,
11888c2ecf20Sopenharmony_ci		       GFP_KERNEL);
11898c2ecf20Sopenharmony_ci	if (!port)
11908c2ecf20Sopenharmony_ci		return -ENOMEM;
11918c2ecf20Sopenharmony_ci
11928c2ecf20Sopenharmony_ci	port->dev = port_dev;
11938c2ecf20Sopenharmony_ci	port->team = team;
11948c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&port->qom_list);
11958c2ecf20Sopenharmony_ci
11968c2ecf20Sopenharmony_ci	port->orig.mtu = port_dev->mtu;
11978c2ecf20Sopenharmony_ci	err = dev_set_mtu(port_dev, dev->mtu);
11988c2ecf20Sopenharmony_ci	if (err) {
11998c2ecf20Sopenharmony_ci		netdev_dbg(dev, "Error %d calling dev_set_mtu\n", err);
12008c2ecf20Sopenharmony_ci		goto err_set_mtu;
12018c2ecf20Sopenharmony_ci	}
12028c2ecf20Sopenharmony_ci
12038c2ecf20Sopenharmony_ci	memcpy(port->orig.dev_addr, port_dev->dev_addr, port_dev->addr_len);
12048c2ecf20Sopenharmony_ci
12058c2ecf20Sopenharmony_ci	err = team_port_enter(team, port);
12068c2ecf20Sopenharmony_ci	if (err) {
12078c2ecf20Sopenharmony_ci		netdev_err(dev, "Device %s failed to enter team mode\n",
12088c2ecf20Sopenharmony_ci			   portname);
12098c2ecf20Sopenharmony_ci		goto err_port_enter;
12108c2ecf20Sopenharmony_ci	}
12118c2ecf20Sopenharmony_ci
12128c2ecf20Sopenharmony_ci	err = dev_open(port_dev, extack);
12138c2ecf20Sopenharmony_ci	if (err) {
12148c2ecf20Sopenharmony_ci		netdev_dbg(dev, "Device %s opening failed\n",
12158c2ecf20Sopenharmony_ci			   portname);
12168c2ecf20Sopenharmony_ci		goto err_dev_open;
12178c2ecf20Sopenharmony_ci	}
12188c2ecf20Sopenharmony_ci
12198c2ecf20Sopenharmony_ci	err = vlan_vids_add_by_dev(port_dev, dev);
12208c2ecf20Sopenharmony_ci	if (err) {
12218c2ecf20Sopenharmony_ci		netdev_err(dev, "Failed to add vlan ids to device %s\n",
12228c2ecf20Sopenharmony_ci				portname);
12238c2ecf20Sopenharmony_ci		goto err_vids_add;
12248c2ecf20Sopenharmony_ci	}
12258c2ecf20Sopenharmony_ci
12268c2ecf20Sopenharmony_ci	err = team_port_enable_netpoll(port);
12278c2ecf20Sopenharmony_ci	if (err) {
12288c2ecf20Sopenharmony_ci		netdev_err(dev, "Failed to enable netpoll on device %s\n",
12298c2ecf20Sopenharmony_ci			   portname);
12308c2ecf20Sopenharmony_ci		goto err_enable_netpoll;
12318c2ecf20Sopenharmony_ci	}
12328c2ecf20Sopenharmony_ci
12338c2ecf20Sopenharmony_ci	if (!(dev->features & NETIF_F_LRO))
12348c2ecf20Sopenharmony_ci		dev_disable_lro(port_dev);
12358c2ecf20Sopenharmony_ci
12368c2ecf20Sopenharmony_ci	err = netdev_rx_handler_register(port_dev, team_handle_frame,
12378c2ecf20Sopenharmony_ci					 port);
12388c2ecf20Sopenharmony_ci	if (err) {
12398c2ecf20Sopenharmony_ci		netdev_err(dev, "Device %s failed to register rx_handler\n",
12408c2ecf20Sopenharmony_ci			   portname);
12418c2ecf20Sopenharmony_ci		goto err_handler_register;
12428c2ecf20Sopenharmony_ci	}
12438c2ecf20Sopenharmony_ci
12448c2ecf20Sopenharmony_ci	err = team_upper_dev_link(team, port, extack);
12458c2ecf20Sopenharmony_ci	if (err) {
12468c2ecf20Sopenharmony_ci		netdev_err(dev, "Device %s failed to set upper link\n",
12478c2ecf20Sopenharmony_ci			   portname);
12488c2ecf20Sopenharmony_ci		goto err_set_upper_link;
12498c2ecf20Sopenharmony_ci	}
12508c2ecf20Sopenharmony_ci
12518c2ecf20Sopenharmony_ci	err = __team_option_inst_add_port(team, port);
12528c2ecf20Sopenharmony_ci	if (err) {
12538c2ecf20Sopenharmony_ci		netdev_err(dev, "Device %s failed to add per-port options\n",
12548c2ecf20Sopenharmony_ci			   portname);
12558c2ecf20Sopenharmony_ci		goto err_option_port_add;
12568c2ecf20Sopenharmony_ci	}
12578c2ecf20Sopenharmony_ci
12588c2ecf20Sopenharmony_ci	/* set promiscuity level to new slave */
12598c2ecf20Sopenharmony_ci	if (dev->flags & IFF_PROMISC) {
12608c2ecf20Sopenharmony_ci		err = dev_set_promiscuity(port_dev, 1);
12618c2ecf20Sopenharmony_ci		if (err)
12628c2ecf20Sopenharmony_ci			goto err_set_slave_promisc;
12638c2ecf20Sopenharmony_ci	}
12648c2ecf20Sopenharmony_ci
12658c2ecf20Sopenharmony_ci	/* set allmulti level to new slave */
12668c2ecf20Sopenharmony_ci	if (dev->flags & IFF_ALLMULTI) {
12678c2ecf20Sopenharmony_ci		err = dev_set_allmulti(port_dev, 1);
12688c2ecf20Sopenharmony_ci		if (err) {
12698c2ecf20Sopenharmony_ci			if (dev->flags & IFF_PROMISC)
12708c2ecf20Sopenharmony_ci				dev_set_promiscuity(port_dev, -1);
12718c2ecf20Sopenharmony_ci			goto err_set_slave_promisc;
12728c2ecf20Sopenharmony_ci		}
12738c2ecf20Sopenharmony_ci	}
12748c2ecf20Sopenharmony_ci
12758c2ecf20Sopenharmony_ci	if (dev->flags & IFF_UP) {
12768c2ecf20Sopenharmony_ci		netif_addr_lock_bh(dev);
12778c2ecf20Sopenharmony_ci		dev_uc_sync_multiple(port_dev, dev);
12788c2ecf20Sopenharmony_ci		dev_mc_sync_multiple(port_dev, dev);
12798c2ecf20Sopenharmony_ci		netif_addr_unlock_bh(dev);
12808c2ecf20Sopenharmony_ci	}
12818c2ecf20Sopenharmony_ci
12828c2ecf20Sopenharmony_ci	port->index = -1;
12838c2ecf20Sopenharmony_ci	list_add_tail_rcu(&port->list, &team->port_list);
12848c2ecf20Sopenharmony_ci	team_port_enable(team, port);
12858c2ecf20Sopenharmony_ci	__team_compute_features(team);
12868c2ecf20Sopenharmony_ci	__team_port_change_port_added(port, !!netif_oper_up(port_dev));
12878c2ecf20Sopenharmony_ci	__team_options_change_check(team);
12888c2ecf20Sopenharmony_ci
12898c2ecf20Sopenharmony_ci	netdev_info(dev, "Port device %s added\n", portname);
12908c2ecf20Sopenharmony_ci
12918c2ecf20Sopenharmony_ci	return 0;
12928c2ecf20Sopenharmony_ci
12938c2ecf20Sopenharmony_cierr_set_slave_promisc:
12948c2ecf20Sopenharmony_ci	__team_option_inst_del_port(team, port);
12958c2ecf20Sopenharmony_ci
12968c2ecf20Sopenharmony_cierr_option_port_add:
12978c2ecf20Sopenharmony_ci	team_upper_dev_unlink(team, port);
12988c2ecf20Sopenharmony_ci
12998c2ecf20Sopenharmony_cierr_set_upper_link:
13008c2ecf20Sopenharmony_ci	netdev_rx_handler_unregister(port_dev);
13018c2ecf20Sopenharmony_ci
13028c2ecf20Sopenharmony_cierr_handler_register:
13038c2ecf20Sopenharmony_ci	team_port_disable_netpoll(port);
13048c2ecf20Sopenharmony_ci
13058c2ecf20Sopenharmony_cierr_enable_netpoll:
13068c2ecf20Sopenharmony_ci	vlan_vids_del_by_dev(port_dev, dev);
13078c2ecf20Sopenharmony_ci
13088c2ecf20Sopenharmony_cierr_vids_add:
13098c2ecf20Sopenharmony_ci	dev_close(port_dev);
13108c2ecf20Sopenharmony_ci
13118c2ecf20Sopenharmony_cierr_dev_open:
13128c2ecf20Sopenharmony_ci	team_port_leave(team, port);
13138c2ecf20Sopenharmony_ci	team_port_set_orig_dev_addr(port);
13148c2ecf20Sopenharmony_ci
13158c2ecf20Sopenharmony_cierr_port_enter:
13168c2ecf20Sopenharmony_ci	dev_set_mtu(port_dev, port->orig.mtu);
13178c2ecf20Sopenharmony_ci
13188c2ecf20Sopenharmony_cierr_set_mtu:
13198c2ecf20Sopenharmony_ci	kfree(port);
13208c2ecf20Sopenharmony_ci
13218c2ecf20Sopenharmony_ci	return err;
13228c2ecf20Sopenharmony_ci}
13238c2ecf20Sopenharmony_ci
13248c2ecf20Sopenharmony_cistatic void __team_port_change_port_removed(struct team_port *port);
13258c2ecf20Sopenharmony_ci
13268c2ecf20Sopenharmony_cistatic int team_port_del(struct team *team, struct net_device *port_dev)
13278c2ecf20Sopenharmony_ci{
13288c2ecf20Sopenharmony_ci	struct net_device *dev = team->dev;
13298c2ecf20Sopenharmony_ci	struct team_port *port;
13308c2ecf20Sopenharmony_ci	char *portname = port_dev->name;
13318c2ecf20Sopenharmony_ci
13328c2ecf20Sopenharmony_ci	port = team_port_get_rtnl(port_dev);
13338c2ecf20Sopenharmony_ci	if (!port || !team_port_find(team, port)) {
13348c2ecf20Sopenharmony_ci		netdev_err(dev, "Device %s does not act as a port of this team\n",
13358c2ecf20Sopenharmony_ci			   portname);
13368c2ecf20Sopenharmony_ci		return -ENOENT;
13378c2ecf20Sopenharmony_ci	}
13388c2ecf20Sopenharmony_ci
13398c2ecf20Sopenharmony_ci	team_port_disable(team, port);
13408c2ecf20Sopenharmony_ci	list_del_rcu(&port->list);
13418c2ecf20Sopenharmony_ci
13428c2ecf20Sopenharmony_ci	if (dev->flags & IFF_PROMISC)
13438c2ecf20Sopenharmony_ci		dev_set_promiscuity(port_dev, -1);
13448c2ecf20Sopenharmony_ci	if (dev->flags & IFF_ALLMULTI)
13458c2ecf20Sopenharmony_ci		dev_set_allmulti(port_dev, -1);
13468c2ecf20Sopenharmony_ci
13478c2ecf20Sopenharmony_ci	team_upper_dev_unlink(team, port);
13488c2ecf20Sopenharmony_ci	netdev_rx_handler_unregister(port_dev);
13498c2ecf20Sopenharmony_ci	team_port_disable_netpoll(port);
13508c2ecf20Sopenharmony_ci	vlan_vids_del_by_dev(port_dev, dev);
13518c2ecf20Sopenharmony_ci	if (dev->flags & IFF_UP) {
13528c2ecf20Sopenharmony_ci		dev_uc_unsync(port_dev, dev);
13538c2ecf20Sopenharmony_ci		dev_mc_unsync(port_dev, dev);
13548c2ecf20Sopenharmony_ci	}
13558c2ecf20Sopenharmony_ci	dev_close(port_dev);
13568c2ecf20Sopenharmony_ci	team_port_leave(team, port);
13578c2ecf20Sopenharmony_ci
13588c2ecf20Sopenharmony_ci	__team_option_inst_mark_removed_port(team, port);
13598c2ecf20Sopenharmony_ci	__team_options_change_check(team);
13608c2ecf20Sopenharmony_ci	__team_option_inst_del_port(team, port);
13618c2ecf20Sopenharmony_ci	__team_port_change_port_removed(port);
13628c2ecf20Sopenharmony_ci
13638c2ecf20Sopenharmony_ci	team_port_set_orig_dev_addr(port);
13648c2ecf20Sopenharmony_ci	dev_set_mtu(port_dev, port->orig.mtu);
13658c2ecf20Sopenharmony_ci	kfree_rcu(port, rcu);
13668c2ecf20Sopenharmony_ci	netdev_info(dev, "Port device %s removed\n", portname);
13678c2ecf20Sopenharmony_ci	__team_compute_features(team);
13688c2ecf20Sopenharmony_ci
13698c2ecf20Sopenharmony_ci	return 0;
13708c2ecf20Sopenharmony_ci}
13718c2ecf20Sopenharmony_ci
13728c2ecf20Sopenharmony_ci
13738c2ecf20Sopenharmony_ci/*****************
13748c2ecf20Sopenharmony_ci * Net device ops
13758c2ecf20Sopenharmony_ci *****************/
13768c2ecf20Sopenharmony_ci
13778c2ecf20Sopenharmony_cistatic int team_mode_option_get(struct team *team, struct team_gsetter_ctx *ctx)
13788c2ecf20Sopenharmony_ci{
13798c2ecf20Sopenharmony_ci	ctx->data.str_val = team->mode->kind;
13808c2ecf20Sopenharmony_ci	return 0;
13818c2ecf20Sopenharmony_ci}
13828c2ecf20Sopenharmony_ci
13838c2ecf20Sopenharmony_cistatic int team_mode_option_set(struct team *team, struct team_gsetter_ctx *ctx)
13848c2ecf20Sopenharmony_ci{
13858c2ecf20Sopenharmony_ci	return team_change_mode(team, ctx->data.str_val);
13868c2ecf20Sopenharmony_ci}
13878c2ecf20Sopenharmony_ci
13888c2ecf20Sopenharmony_cistatic int team_notify_peers_count_get(struct team *team,
13898c2ecf20Sopenharmony_ci				       struct team_gsetter_ctx *ctx)
13908c2ecf20Sopenharmony_ci{
13918c2ecf20Sopenharmony_ci	ctx->data.u32_val = team->notify_peers.count;
13928c2ecf20Sopenharmony_ci	return 0;
13938c2ecf20Sopenharmony_ci}
13948c2ecf20Sopenharmony_ci
13958c2ecf20Sopenharmony_cistatic int team_notify_peers_count_set(struct team *team,
13968c2ecf20Sopenharmony_ci				       struct team_gsetter_ctx *ctx)
13978c2ecf20Sopenharmony_ci{
13988c2ecf20Sopenharmony_ci	team->notify_peers.count = ctx->data.u32_val;
13998c2ecf20Sopenharmony_ci	return 0;
14008c2ecf20Sopenharmony_ci}
14018c2ecf20Sopenharmony_ci
14028c2ecf20Sopenharmony_cistatic int team_notify_peers_interval_get(struct team *team,
14038c2ecf20Sopenharmony_ci					  struct team_gsetter_ctx *ctx)
14048c2ecf20Sopenharmony_ci{
14058c2ecf20Sopenharmony_ci	ctx->data.u32_val = team->notify_peers.interval;
14068c2ecf20Sopenharmony_ci	return 0;
14078c2ecf20Sopenharmony_ci}
14088c2ecf20Sopenharmony_ci
14098c2ecf20Sopenharmony_cistatic int team_notify_peers_interval_set(struct team *team,
14108c2ecf20Sopenharmony_ci					  struct team_gsetter_ctx *ctx)
14118c2ecf20Sopenharmony_ci{
14128c2ecf20Sopenharmony_ci	team->notify_peers.interval = ctx->data.u32_val;
14138c2ecf20Sopenharmony_ci	return 0;
14148c2ecf20Sopenharmony_ci}
14158c2ecf20Sopenharmony_ci
14168c2ecf20Sopenharmony_cistatic int team_mcast_rejoin_count_get(struct team *team,
14178c2ecf20Sopenharmony_ci				       struct team_gsetter_ctx *ctx)
14188c2ecf20Sopenharmony_ci{
14198c2ecf20Sopenharmony_ci	ctx->data.u32_val = team->mcast_rejoin.count;
14208c2ecf20Sopenharmony_ci	return 0;
14218c2ecf20Sopenharmony_ci}
14228c2ecf20Sopenharmony_ci
14238c2ecf20Sopenharmony_cistatic int team_mcast_rejoin_count_set(struct team *team,
14248c2ecf20Sopenharmony_ci				       struct team_gsetter_ctx *ctx)
14258c2ecf20Sopenharmony_ci{
14268c2ecf20Sopenharmony_ci	team->mcast_rejoin.count = ctx->data.u32_val;
14278c2ecf20Sopenharmony_ci	return 0;
14288c2ecf20Sopenharmony_ci}
14298c2ecf20Sopenharmony_ci
14308c2ecf20Sopenharmony_cistatic int team_mcast_rejoin_interval_get(struct team *team,
14318c2ecf20Sopenharmony_ci					  struct team_gsetter_ctx *ctx)
14328c2ecf20Sopenharmony_ci{
14338c2ecf20Sopenharmony_ci	ctx->data.u32_val = team->mcast_rejoin.interval;
14348c2ecf20Sopenharmony_ci	return 0;
14358c2ecf20Sopenharmony_ci}
14368c2ecf20Sopenharmony_ci
14378c2ecf20Sopenharmony_cistatic int team_mcast_rejoin_interval_set(struct team *team,
14388c2ecf20Sopenharmony_ci					  struct team_gsetter_ctx *ctx)
14398c2ecf20Sopenharmony_ci{
14408c2ecf20Sopenharmony_ci	team->mcast_rejoin.interval = ctx->data.u32_val;
14418c2ecf20Sopenharmony_ci	return 0;
14428c2ecf20Sopenharmony_ci}
14438c2ecf20Sopenharmony_ci
14448c2ecf20Sopenharmony_cistatic int team_port_en_option_get(struct team *team,
14458c2ecf20Sopenharmony_ci				   struct team_gsetter_ctx *ctx)
14468c2ecf20Sopenharmony_ci{
14478c2ecf20Sopenharmony_ci	struct team_port *port = ctx->info->port;
14488c2ecf20Sopenharmony_ci
14498c2ecf20Sopenharmony_ci	ctx->data.bool_val = team_port_enabled(port);
14508c2ecf20Sopenharmony_ci	return 0;
14518c2ecf20Sopenharmony_ci}
14528c2ecf20Sopenharmony_ci
14538c2ecf20Sopenharmony_cistatic int team_port_en_option_set(struct team *team,
14548c2ecf20Sopenharmony_ci				   struct team_gsetter_ctx *ctx)
14558c2ecf20Sopenharmony_ci{
14568c2ecf20Sopenharmony_ci	struct team_port *port = ctx->info->port;
14578c2ecf20Sopenharmony_ci
14588c2ecf20Sopenharmony_ci	if (ctx->data.bool_val)
14598c2ecf20Sopenharmony_ci		team_port_enable(team, port);
14608c2ecf20Sopenharmony_ci	else
14618c2ecf20Sopenharmony_ci		team_port_disable(team, port);
14628c2ecf20Sopenharmony_ci	return 0;
14638c2ecf20Sopenharmony_ci}
14648c2ecf20Sopenharmony_ci
14658c2ecf20Sopenharmony_cistatic int team_user_linkup_option_get(struct team *team,
14668c2ecf20Sopenharmony_ci				       struct team_gsetter_ctx *ctx)
14678c2ecf20Sopenharmony_ci{
14688c2ecf20Sopenharmony_ci	struct team_port *port = ctx->info->port;
14698c2ecf20Sopenharmony_ci
14708c2ecf20Sopenharmony_ci	ctx->data.bool_val = port->user.linkup;
14718c2ecf20Sopenharmony_ci	return 0;
14728c2ecf20Sopenharmony_ci}
14738c2ecf20Sopenharmony_ci
14748c2ecf20Sopenharmony_cistatic void __team_carrier_check(struct team *team);
14758c2ecf20Sopenharmony_ci
14768c2ecf20Sopenharmony_cistatic int team_user_linkup_option_set(struct team *team,
14778c2ecf20Sopenharmony_ci				       struct team_gsetter_ctx *ctx)
14788c2ecf20Sopenharmony_ci{
14798c2ecf20Sopenharmony_ci	struct team_port *port = ctx->info->port;
14808c2ecf20Sopenharmony_ci
14818c2ecf20Sopenharmony_ci	port->user.linkup = ctx->data.bool_val;
14828c2ecf20Sopenharmony_ci	team_refresh_port_linkup(port);
14838c2ecf20Sopenharmony_ci	__team_carrier_check(port->team);
14848c2ecf20Sopenharmony_ci	return 0;
14858c2ecf20Sopenharmony_ci}
14868c2ecf20Sopenharmony_ci
14878c2ecf20Sopenharmony_cistatic int team_user_linkup_en_option_get(struct team *team,
14888c2ecf20Sopenharmony_ci					  struct team_gsetter_ctx *ctx)
14898c2ecf20Sopenharmony_ci{
14908c2ecf20Sopenharmony_ci	struct team_port *port = ctx->info->port;
14918c2ecf20Sopenharmony_ci
14928c2ecf20Sopenharmony_ci	ctx->data.bool_val = port->user.linkup_enabled;
14938c2ecf20Sopenharmony_ci	return 0;
14948c2ecf20Sopenharmony_ci}
14958c2ecf20Sopenharmony_ci
14968c2ecf20Sopenharmony_cistatic int team_user_linkup_en_option_set(struct team *team,
14978c2ecf20Sopenharmony_ci					  struct team_gsetter_ctx *ctx)
14988c2ecf20Sopenharmony_ci{
14998c2ecf20Sopenharmony_ci	struct team_port *port = ctx->info->port;
15008c2ecf20Sopenharmony_ci
15018c2ecf20Sopenharmony_ci	port->user.linkup_enabled = ctx->data.bool_val;
15028c2ecf20Sopenharmony_ci	team_refresh_port_linkup(port);
15038c2ecf20Sopenharmony_ci	__team_carrier_check(port->team);
15048c2ecf20Sopenharmony_ci	return 0;
15058c2ecf20Sopenharmony_ci}
15068c2ecf20Sopenharmony_ci
15078c2ecf20Sopenharmony_cistatic int team_priority_option_get(struct team *team,
15088c2ecf20Sopenharmony_ci				    struct team_gsetter_ctx *ctx)
15098c2ecf20Sopenharmony_ci{
15108c2ecf20Sopenharmony_ci	struct team_port *port = ctx->info->port;
15118c2ecf20Sopenharmony_ci
15128c2ecf20Sopenharmony_ci	ctx->data.s32_val = port->priority;
15138c2ecf20Sopenharmony_ci	return 0;
15148c2ecf20Sopenharmony_ci}
15158c2ecf20Sopenharmony_ci
15168c2ecf20Sopenharmony_cistatic int team_priority_option_set(struct team *team,
15178c2ecf20Sopenharmony_ci				    struct team_gsetter_ctx *ctx)
15188c2ecf20Sopenharmony_ci{
15198c2ecf20Sopenharmony_ci	struct team_port *port = ctx->info->port;
15208c2ecf20Sopenharmony_ci	s32 priority = ctx->data.s32_val;
15218c2ecf20Sopenharmony_ci
15228c2ecf20Sopenharmony_ci	if (port->priority == priority)
15238c2ecf20Sopenharmony_ci		return 0;
15248c2ecf20Sopenharmony_ci	port->priority = priority;
15258c2ecf20Sopenharmony_ci	team_queue_override_port_prio_changed(team, port);
15268c2ecf20Sopenharmony_ci	return 0;
15278c2ecf20Sopenharmony_ci}
15288c2ecf20Sopenharmony_ci
15298c2ecf20Sopenharmony_cistatic int team_queue_id_option_get(struct team *team,
15308c2ecf20Sopenharmony_ci				    struct team_gsetter_ctx *ctx)
15318c2ecf20Sopenharmony_ci{
15328c2ecf20Sopenharmony_ci	struct team_port *port = ctx->info->port;
15338c2ecf20Sopenharmony_ci
15348c2ecf20Sopenharmony_ci	ctx->data.u32_val = port->queue_id;
15358c2ecf20Sopenharmony_ci	return 0;
15368c2ecf20Sopenharmony_ci}
15378c2ecf20Sopenharmony_ci
15388c2ecf20Sopenharmony_cistatic int team_queue_id_option_set(struct team *team,
15398c2ecf20Sopenharmony_ci				    struct team_gsetter_ctx *ctx)
15408c2ecf20Sopenharmony_ci{
15418c2ecf20Sopenharmony_ci	struct team_port *port = ctx->info->port;
15428c2ecf20Sopenharmony_ci	u16 new_queue_id = ctx->data.u32_val;
15438c2ecf20Sopenharmony_ci
15448c2ecf20Sopenharmony_ci	if (port->queue_id == new_queue_id)
15458c2ecf20Sopenharmony_ci		return 0;
15468c2ecf20Sopenharmony_ci	if (new_queue_id >= team->dev->real_num_tx_queues)
15478c2ecf20Sopenharmony_ci		return -EINVAL;
15488c2ecf20Sopenharmony_ci	team_queue_override_port_change_queue_id(team, port, new_queue_id);
15498c2ecf20Sopenharmony_ci	return 0;
15508c2ecf20Sopenharmony_ci}
15518c2ecf20Sopenharmony_ci
15528c2ecf20Sopenharmony_cistatic const struct team_option team_options[] = {
15538c2ecf20Sopenharmony_ci	{
15548c2ecf20Sopenharmony_ci		.name = "mode",
15558c2ecf20Sopenharmony_ci		.type = TEAM_OPTION_TYPE_STRING,
15568c2ecf20Sopenharmony_ci		.getter = team_mode_option_get,
15578c2ecf20Sopenharmony_ci		.setter = team_mode_option_set,
15588c2ecf20Sopenharmony_ci	},
15598c2ecf20Sopenharmony_ci	{
15608c2ecf20Sopenharmony_ci		.name = "notify_peers_count",
15618c2ecf20Sopenharmony_ci		.type = TEAM_OPTION_TYPE_U32,
15628c2ecf20Sopenharmony_ci		.getter = team_notify_peers_count_get,
15638c2ecf20Sopenharmony_ci		.setter = team_notify_peers_count_set,
15648c2ecf20Sopenharmony_ci	},
15658c2ecf20Sopenharmony_ci	{
15668c2ecf20Sopenharmony_ci		.name = "notify_peers_interval",
15678c2ecf20Sopenharmony_ci		.type = TEAM_OPTION_TYPE_U32,
15688c2ecf20Sopenharmony_ci		.getter = team_notify_peers_interval_get,
15698c2ecf20Sopenharmony_ci		.setter = team_notify_peers_interval_set,
15708c2ecf20Sopenharmony_ci	},
15718c2ecf20Sopenharmony_ci	{
15728c2ecf20Sopenharmony_ci		.name = "mcast_rejoin_count",
15738c2ecf20Sopenharmony_ci		.type = TEAM_OPTION_TYPE_U32,
15748c2ecf20Sopenharmony_ci		.getter = team_mcast_rejoin_count_get,
15758c2ecf20Sopenharmony_ci		.setter = team_mcast_rejoin_count_set,
15768c2ecf20Sopenharmony_ci	},
15778c2ecf20Sopenharmony_ci	{
15788c2ecf20Sopenharmony_ci		.name = "mcast_rejoin_interval",
15798c2ecf20Sopenharmony_ci		.type = TEAM_OPTION_TYPE_U32,
15808c2ecf20Sopenharmony_ci		.getter = team_mcast_rejoin_interval_get,
15818c2ecf20Sopenharmony_ci		.setter = team_mcast_rejoin_interval_set,
15828c2ecf20Sopenharmony_ci	},
15838c2ecf20Sopenharmony_ci	{
15848c2ecf20Sopenharmony_ci		.name = "enabled",
15858c2ecf20Sopenharmony_ci		.type = TEAM_OPTION_TYPE_BOOL,
15868c2ecf20Sopenharmony_ci		.per_port = true,
15878c2ecf20Sopenharmony_ci		.getter = team_port_en_option_get,
15888c2ecf20Sopenharmony_ci		.setter = team_port_en_option_set,
15898c2ecf20Sopenharmony_ci	},
15908c2ecf20Sopenharmony_ci	{
15918c2ecf20Sopenharmony_ci		.name = "user_linkup",
15928c2ecf20Sopenharmony_ci		.type = TEAM_OPTION_TYPE_BOOL,
15938c2ecf20Sopenharmony_ci		.per_port = true,
15948c2ecf20Sopenharmony_ci		.getter = team_user_linkup_option_get,
15958c2ecf20Sopenharmony_ci		.setter = team_user_linkup_option_set,
15968c2ecf20Sopenharmony_ci	},
15978c2ecf20Sopenharmony_ci	{
15988c2ecf20Sopenharmony_ci		.name = "user_linkup_enabled",
15998c2ecf20Sopenharmony_ci		.type = TEAM_OPTION_TYPE_BOOL,
16008c2ecf20Sopenharmony_ci		.per_port = true,
16018c2ecf20Sopenharmony_ci		.getter = team_user_linkup_en_option_get,
16028c2ecf20Sopenharmony_ci		.setter = team_user_linkup_en_option_set,
16038c2ecf20Sopenharmony_ci	},
16048c2ecf20Sopenharmony_ci	{
16058c2ecf20Sopenharmony_ci		.name = "priority",
16068c2ecf20Sopenharmony_ci		.type = TEAM_OPTION_TYPE_S32,
16078c2ecf20Sopenharmony_ci		.per_port = true,
16088c2ecf20Sopenharmony_ci		.getter = team_priority_option_get,
16098c2ecf20Sopenharmony_ci		.setter = team_priority_option_set,
16108c2ecf20Sopenharmony_ci	},
16118c2ecf20Sopenharmony_ci	{
16128c2ecf20Sopenharmony_ci		.name = "queue_id",
16138c2ecf20Sopenharmony_ci		.type = TEAM_OPTION_TYPE_U32,
16148c2ecf20Sopenharmony_ci		.per_port = true,
16158c2ecf20Sopenharmony_ci		.getter = team_queue_id_option_get,
16168c2ecf20Sopenharmony_ci		.setter = team_queue_id_option_set,
16178c2ecf20Sopenharmony_ci	},
16188c2ecf20Sopenharmony_ci};
16198c2ecf20Sopenharmony_ci
16208c2ecf20Sopenharmony_ci
16218c2ecf20Sopenharmony_cistatic int team_init(struct net_device *dev)
16228c2ecf20Sopenharmony_ci{
16238c2ecf20Sopenharmony_ci	struct team *team = netdev_priv(dev);
16248c2ecf20Sopenharmony_ci	int i;
16258c2ecf20Sopenharmony_ci	int err;
16268c2ecf20Sopenharmony_ci
16278c2ecf20Sopenharmony_ci	team->dev = dev;
16288c2ecf20Sopenharmony_ci	team_set_no_mode(team);
16298c2ecf20Sopenharmony_ci	team->notifier_ctx = false;
16308c2ecf20Sopenharmony_ci
16318c2ecf20Sopenharmony_ci	team->pcpu_stats = netdev_alloc_pcpu_stats(struct team_pcpu_stats);
16328c2ecf20Sopenharmony_ci	if (!team->pcpu_stats)
16338c2ecf20Sopenharmony_ci		return -ENOMEM;
16348c2ecf20Sopenharmony_ci
16358c2ecf20Sopenharmony_ci	for (i = 0; i < TEAM_PORT_HASHENTRIES; i++)
16368c2ecf20Sopenharmony_ci		INIT_HLIST_HEAD(&team->en_port_hlist[i]);
16378c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&team->port_list);
16388c2ecf20Sopenharmony_ci	err = team_queue_override_init(team);
16398c2ecf20Sopenharmony_ci	if (err)
16408c2ecf20Sopenharmony_ci		goto err_team_queue_override_init;
16418c2ecf20Sopenharmony_ci
16428c2ecf20Sopenharmony_ci	team_adjust_ops(team);
16438c2ecf20Sopenharmony_ci
16448c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&team->option_list);
16458c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&team->option_inst_list);
16468c2ecf20Sopenharmony_ci
16478c2ecf20Sopenharmony_ci	team_notify_peers_init(team);
16488c2ecf20Sopenharmony_ci	team_mcast_rejoin_init(team);
16498c2ecf20Sopenharmony_ci
16508c2ecf20Sopenharmony_ci	err = team_options_register(team, team_options, ARRAY_SIZE(team_options));
16518c2ecf20Sopenharmony_ci	if (err)
16528c2ecf20Sopenharmony_ci		goto err_options_register;
16538c2ecf20Sopenharmony_ci	netif_carrier_off(dev);
16548c2ecf20Sopenharmony_ci
16558c2ecf20Sopenharmony_ci	lockdep_register_key(&team->team_lock_key);
16568c2ecf20Sopenharmony_ci	__mutex_init(&team->lock, "team->team_lock_key", &team->team_lock_key);
16578c2ecf20Sopenharmony_ci	netdev_lockdep_set_classes(dev);
16588c2ecf20Sopenharmony_ci
16598c2ecf20Sopenharmony_ci	return 0;
16608c2ecf20Sopenharmony_ci
16618c2ecf20Sopenharmony_cierr_options_register:
16628c2ecf20Sopenharmony_ci	team_mcast_rejoin_fini(team);
16638c2ecf20Sopenharmony_ci	team_notify_peers_fini(team);
16648c2ecf20Sopenharmony_ci	team_queue_override_fini(team);
16658c2ecf20Sopenharmony_cierr_team_queue_override_init:
16668c2ecf20Sopenharmony_ci	free_percpu(team->pcpu_stats);
16678c2ecf20Sopenharmony_ci
16688c2ecf20Sopenharmony_ci	return err;
16698c2ecf20Sopenharmony_ci}
16708c2ecf20Sopenharmony_ci
16718c2ecf20Sopenharmony_cistatic void team_uninit(struct net_device *dev)
16728c2ecf20Sopenharmony_ci{
16738c2ecf20Sopenharmony_ci	struct team *team = netdev_priv(dev);
16748c2ecf20Sopenharmony_ci	struct team_port *port;
16758c2ecf20Sopenharmony_ci	struct team_port *tmp;
16768c2ecf20Sopenharmony_ci
16778c2ecf20Sopenharmony_ci	mutex_lock(&team->lock);
16788c2ecf20Sopenharmony_ci	list_for_each_entry_safe(port, tmp, &team->port_list, list)
16798c2ecf20Sopenharmony_ci		team_port_del(team, port->dev);
16808c2ecf20Sopenharmony_ci
16818c2ecf20Sopenharmony_ci	__team_change_mode(team, NULL); /* cleanup */
16828c2ecf20Sopenharmony_ci	__team_options_unregister(team, team_options, ARRAY_SIZE(team_options));
16838c2ecf20Sopenharmony_ci	team_mcast_rejoin_fini(team);
16848c2ecf20Sopenharmony_ci	team_notify_peers_fini(team);
16858c2ecf20Sopenharmony_ci	team_queue_override_fini(team);
16868c2ecf20Sopenharmony_ci	mutex_unlock(&team->lock);
16878c2ecf20Sopenharmony_ci	netdev_change_features(dev);
16888c2ecf20Sopenharmony_ci	lockdep_unregister_key(&team->team_lock_key);
16898c2ecf20Sopenharmony_ci}
16908c2ecf20Sopenharmony_ci
16918c2ecf20Sopenharmony_cistatic void team_destructor(struct net_device *dev)
16928c2ecf20Sopenharmony_ci{
16938c2ecf20Sopenharmony_ci	struct team *team = netdev_priv(dev);
16948c2ecf20Sopenharmony_ci
16958c2ecf20Sopenharmony_ci	free_percpu(team->pcpu_stats);
16968c2ecf20Sopenharmony_ci}
16978c2ecf20Sopenharmony_ci
16988c2ecf20Sopenharmony_cistatic int team_open(struct net_device *dev)
16998c2ecf20Sopenharmony_ci{
17008c2ecf20Sopenharmony_ci	return 0;
17018c2ecf20Sopenharmony_ci}
17028c2ecf20Sopenharmony_ci
17038c2ecf20Sopenharmony_cistatic int team_close(struct net_device *dev)
17048c2ecf20Sopenharmony_ci{
17058c2ecf20Sopenharmony_ci	struct team *team = netdev_priv(dev);
17068c2ecf20Sopenharmony_ci	struct team_port *port;
17078c2ecf20Sopenharmony_ci
17088c2ecf20Sopenharmony_ci	list_for_each_entry(port, &team->port_list, list) {
17098c2ecf20Sopenharmony_ci		dev_uc_unsync(port->dev, dev);
17108c2ecf20Sopenharmony_ci		dev_mc_unsync(port->dev, dev);
17118c2ecf20Sopenharmony_ci	}
17128c2ecf20Sopenharmony_ci
17138c2ecf20Sopenharmony_ci	return 0;
17148c2ecf20Sopenharmony_ci}
17158c2ecf20Sopenharmony_ci
17168c2ecf20Sopenharmony_ci/*
17178c2ecf20Sopenharmony_ci * note: already called with rcu_read_lock
17188c2ecf20Sopenharmony_ci */
17198c2ecf20Sopenharmony_cistatic netdev_tx_t team_xmit(struct sk_buff *skb, struct net_device *dev)
17208c2ecf20Sopenharmony_ci{
17218c2ecf20Sopenharmony_ci	struct team *team = netdev_priv(dev);
17228c2ecf20Sopenharmony_ci	bool tx_success;
17238c2ecf20Sopenharmony_ci	unsigned int len = skb->len;
17248c2ecf20Sopenharmony_ci
17258c2ecf20Sopenharmony_ci	tx_success = team_queue_override_transmit(team, skb);
17268c2ecf20Sopenharmony_ci	if (!tx_success)
17278c2ecf20Sopenharmony_ci		tx_success = team->ops.transmit(team, skb);
17288c2ecf20Sopenharmony_ci	if (tx_success) {
17298c2ecf20Sopenharmony_ci		struct team_pcpu_stats *pcpu_stats;
17308c2ecf20Sopenharmony_ci
17318c2ecf20Sopenharmony_ci		pcpu_stats = this_cpu_ptr(team->pcpu_stats);
17328c2ecf20Sopenharmony_ci		u64_stats_update_begin(&pcpu_stats->syncp);
17338c2ecf20Sopenharmony_ci		pcpu_stats->tx_packets++;
17348c2ecf20Sopenharmony_ci		pcpu_stats->tx_bytes += len;
17358c2ecf20Sopenharmony_ci		u64_stats_update_end(&pcpu_stats->syncp);
17368c2ecf20Sopenharmony_ci	} else {
17378c2ecf20Sopenharmony_ci		this_cpu_inc(team->pcpu_stats->tx_dropped);
17388c2ecf20Sopenharmony_ci	}
17398c2ecf20Sopenharmony_ci
17408c2ecf20Sopenharmony_ci	return NETDEV_TX_OK;
17418c2ecf20Sopenharmony_ci}
17428c2ecf20Sopenharmony_ci
17438c2ecf20Sopenharmony_cistatic u16 team_select_queue(struct net_device *dev, struct sk_buff *skb,
17448c2ecf20Sopenharmony_ci			     struct net_device *sb_dev)
17458c2ecf20Sopenharmony_ci{
17468c2ecf20Sopenharmony_ci	/*
17478c2ecf20Sopenharmony_ci	 * This helper function exists to help dev_pick_tx get the correct
17488c2ecf20Sopenharmony_ci	 * destination queue.  Using a helper function skips a call to
17498c2ecf20Sopenharmony_ci	 * skb_tx_hash and will put the skbs in the queue we expect on their
17508c2ecf20Sopenharmony_ci	 * way down to the team driver.
17518c2ecf20Sopenharmony_ci	 */
17528c2ecf20Sopenharmony_ci	u16 txq = skb_rx_queue_recorded(skb) ? skb_get_rx_queue(skb) : 0;
17538c2ecf20Sopenharmony_ci
17548c2ecf20Sopenharmony_ci	/*
17558c2ecf20Sopenharmony_ci	 * Save the original txq to restore before passing to the driver
17568c2ecf20Sopenharmony_ci	 */
17578c2ecf20Sopenharmony_ci	qdisc_skb_cb(skb)->slave_dev_queue_mapping = skb->queue_mapping;
17588c2ecf20Sopenharmony_ci
17598c2ecf20Sopenharmony_ci	if (unlikely(txq >= dev->real_num_tx_queues)) {
17608c2ecf20Sopenharmony_ci		do {
17618c2ecf20Sopenharmony_ci			txq -= dev->real_num_tx_queues;
17628c2ecf20Sopenharmony_ci		} while (txq >= dev->real_num_tx_queues);
17638c2ecf20Sopenharmony_ci	}
17648c2ecf20Sopenharmony_ci	return txq;
17658c2ecf20Sopenharmony_ci}
17668c2ecf20Sopenharmony_ci
17678c2ecf20Sopenharmony_cistatic void team_change_rx_flags(struct net_device *dev, int change)
17688c2ecf20Sopenharmony_ci{
17698c2ecf20Sopenharmony_ci	struct team *team = netdev_priv(dev);
17708c2ecf20Sopenharmony_ci	struct team_port *port;
17718c2ecf20Sopenharmony_ci	int inc;
17728c2ecf20Sopenharmony_ci
17738c2ecf20Sopenharmony_ci	rcu_read_lock();
17748c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(port, &team->port_list, list) {
17758c2ecf20Sopenharmony_ci		if (change & IFF_PROMISC) {
17768c2ecf20Sopenharmony_ci			inc = dev->flags & IFF_PROMISC ? 1 : -1;
17778c2ecf20Sopenharmony_ci			dev_set_promiscuity(port->dev, inc);
17788c2ecf20Sopenharmony_ci		}
17798c2ecf20Sopenharmony_ci		if (change & IFF_ALLMULTI) {
17808c2ecf20Sopenharmony_ci			inc = dev->flags & IFF_ALLMULTI ? 1 : -1;
17818c2ecf20Sopenharmony_ci			dev_set_allmulti(port->dev, inc);
17828c2ecf20Sopenharmony_ci		}
17838c2ecf20Sopenharmony_ci	}
17848c2ecf20Sopenharmony_ci	rcu_read_unlock();
17858c2ecf20Sopenharmony_ci}
17868c2ecf20Sopenharmony_ci
17878c2ecf20Sopenharmony_cistatic void team_set_rx_mode(struct net_device *dev)
17888c2ecf20Sopenharmony_ci{
17898c2ecf20Sopenharmony_ci	struct team *team = netdev_priv(dev);
17908c2ecf20Sopenharmony_ci	struct team_port *port;
17918c2ecf20Sopenharmony_ci
17928c2ecf20Sopenharmony_ci	rcu_read_lock();
17938c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(port, &team->port_list, list) {
17948c2ecf20Sopenharmony_ci		dev_uc_sync_multiple(port->dev, dev);
17958c2ecf20Sopenharmony_ci		dev_mc_sync_multiple(port->dev, dev);
17968c2ecf20Sopenharmony_ci	}
17978c2ecf20Sopenharmony_ci	rcu_read_unlock();
17988c2ecf20Sopenharmony_ci}
17998c2ecf20Sopenharmony_ci
18008c2ecf20Sopenharmony_cistatic int team_set_mac_address(struct net_device *dev, void *p)
18018c2ecf20Sopenharmony_ci{
18028c2ecf20Sopenharmony_ci	struct sockaddr *addr = p;
18038c2ecf20Sopenharmony_ci	struct team *team = netdev_priv(dev);
18048c2ecf20Sopenharmony_ci	struct team_port *port;
18058c2ecf20Sopenharmony_ci
18068c2ecf20Sopenharmony_ci	if (dev->type == ARPHRD_ETHER && !is_valid_ether_addr(addr->sa_data))
18078c2ecf20Sopenharmony_ci		return -EADDRNOTAVAIL;
18088c2ecf20Sopenharmony_ci	memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
18098c2ecf20Sopenharmony_ci	mutex_lock(&team->lock);
18108c2ecf20Sopenharmony_ci	list_for_each_entry(port, &team->port_list, list)
18118c2ecf20Sopenharmony_ci		if (team->ops.port_change_dev_addr)
18128c2ecf20Sopenharmony_ci			team->ops.port_change_dev_addr(team, port);
18138c2ecf20Sopenharmony_ci	mutex_unlock(&team->lock);
18148c2ecf20Sopenharmony_ci	return 0;
18158c2ecf20Sopenharmony_ci}
18168c2ecf20Sopenharmony_ci
18178c2ecf20Sopenharmony_cistatic int team_change_mtu(struct net_device *dev, int new_mtu)
18188c2ecf20Sopenharmony_ci{
18198c2ecf20Sopenharmony_ci	struct team *team = netdev_priv(dev);
18208c2ecf20Sopenharmony_ci	struct team_port *port;
18218c2ecf20Sopenharmony_ci	int err;
18228c2ecf20Sopenharmony_ci
18238c2ecf20Sopenharmony_ci	/*
18248c2ecf20Sopenharmony_ci	 * Alhough this is reader, it's guarded by team lock. It's not possible
18258c2ecf20Sopenharmony_ci	 * to traverse list in reverse under rcu_read_lock
18268c2ecf20Sopenharmony_ci	 */
18278c2ecf20Sopenharmony_ci	mutex_lock(&team->lock);
18288c2ecf20Sopenharmony_ci	team->port_mtu_change_allowed = true;
18298c2ecf20Sopenharmony_ci	list_for_each_entry(port, &team->port_list, list) {
18308c2ecf20Sopenharmony_ci		err = dev_set_mtu(port->dev, new_mtu);
18318c2ecf20Sopenharmony_ci		if (err) {
18328c2ecf20Sopenharmony_ci			netdev_err(dev, "Device %s failed to change mtu",
18338c2ecf20Sopenharmony_ci				   port->dev->name);
18348c2ecf20Sopenharmony_ci			goto unwind;
18358c2ecf20Sopenharmony_ci		}
18368c2ecf20Sopenharmony_ci	}
18378c2ecf20Sopenharmony_ci	team->port_mtu_change_allowed = false;
18388c2ecf20Sopenharmony_ci	mutex_unlock(&team->lock);
18398c2ecf20Sopenharmony_ci
18408c2ecf20Sopenharmony_ci	dev->mtu = new_mtu;
18418c2ecf20Sopenharmony_ci
18428c2ecf20Sopenharmony_ci	return 0;
18438c2ecf20Sopenharmony_ci
18448c2ecf20Sopenharmony_ciunwind:
18458c2ecf20Sopenharmony_ci	list_for_each_entry_continue_reverse(port, &team->port_list, list)
18468c2ecf20Sopenharmony_ci		dev_set_mtu(port->dev, dev->mtu);
18478c2ecf20Sopenharmony_ci	team->port_mtu_change_allowed = false;
18488c2ecf20Sopenharmony_ci	mutex_unlock(&team->lock);
18498c2ecf20Sopenharmony_ci
18508c2ecf20Sopenharmony_ci	return err;
18518c2ecf20Sopenharmony_ci}
18528c2ecf20Sopenharmony_ci
18538c2ecf20Sopenharmony_cistatic void
18548c2ecf20Sopenharmony_citeam_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
18558c2ecf20Sopenharmony_ci{
18568c2ecf20Sopenharmony_ci	struct team *team = netdev_priv(dev);
18578c2ecf20Sopenharmony_ci	struct team_pcpu_stats *p;
18588c2ecf20Sopenharmony_ci	u64 rx_packets, rx_bytes, rx_multicast, tx_packets, tx_bytes;
18598c2ecf20Sopenharmony_ci	u32 rx_dropped = 0, tx_dropped = 0, rx_nohandler = 0;
18608c2ecf20Sopenharmony_ci	unsigned int start;
18618c2ecf20Sopenharmony_ci	int i;
18628c2ecf20Sopenharmony_ci
18638c2ecf20Sopenharmony_ci	for_each_possible_cpu(i) {
18648c2ecf20Sopenharmony_ci		p = per_cpu_ptr(team->pcpu_stats, i);
18658c2ecf20Sopenharmony_ci		do {
18668c2ecf20Sopenharmony_ci			start = u64_stats_fetch_begin_irq(&p->syncp);
18678c2ecf20Sopenharmony_ci			rx_packets	= p->rx_packets;
18688c2ecf20Sopenharmony_ci			rx_bytes	= p->rx_bytes;
18698c2ecf20Sopenharmony_ci			rx_multicast	= p->rx_multicast;
18708c2ecf20Sopenharmony_ci			tx_packets	= p->tx_packets;
18718c2ecf20Sopenharmony_ci			tx_bytes	= p->tx_bytes;
18728c2ecf20Sopenharmony_ci		} while (u64_stats_fetch_retry_irq(&p->syncp, start));
18738c2ecf20Sopenharmony_ci
18748c2ecf20Sopenharmony_ci		stats->rx_packets	+= rx_packets;
18758c2ecf20Sopenharmony_ci		stats->rx_bytes		+= rx_bytes;
18768c2ecf20Sopenharmony_ci		stats->multicast	+= rx_multicast;
18778c2ecf20Sopenharmony_ci		stats->tx_packets	+= tx_packets;
18788c2ecf20Sopenharmony_ci		stats->tx_bytes		+= tx_bytes;
18798c2ecf20Sopenharmony_ci		/*
18808c2ecf20Sopenharmony_ci		 * rx_dropped, tx_dropped & rx_nohandler are u32,
18818c2ecf20Sopenharmony_ci		 * updated without syncp protection.
18828c2ecf20Sopenharmony_ci		 */
18838c2ecf20Sopenharmony_ci		rx_dropped	+= p->rx_dropped;
18848c2ecf20Sopenharmony_ci		tx_dropped	+= p->tx_dropped;
18858c2ecf20Sopenharmony_ci		rx_nohandler	+= p->rx_nohandler;
18868c2ecf20Sopenharmony_ci	}
18878c2ecf20Sopenharmony_ci	stats->rx_dropped	= rx_dropped;
18888c2ecf20Sopenharmony_ci	stats->tx_dropped	= tx_dropped;
18898c2ecf20Sopenharmony_ci	stats->rx_nohandler	= rx_nohandler;
18908c2ecf20Sopenharmony_ci}
18918c2ecf20Sopenharmony_ci
18928c2ecf20Sopenharmony_cistatic int team_vlan_rx_add_vid(struct net_device *dev, __be16 proto, u16 vid)
18938c2ecf20Sopenharmony_ci{
18948c2ecf20Sopenharmony_ci	struct team *team = netdev_priv(dev);
18958c2ecf20Sopenharmony_ci	struct team_port *port;
18968c2ecf20Sopenharmony_ci	int err;
18978c2ecf20Sopenharmony_ci
18988c2ecf20Sopenharmony_ci	/*
18998c2ecf20Sopenharmony_ci	 * Alhough this is reader, it's guarded by team lock. It's not possible
19008c2ecf20Sopenharmony_ci	 * to traverse list in reverse under rcu_read_lock
19018c2ecf20Sopenharmony_ci	 */
19028c2ecf20Sopenharmony_ci	mutex_lock(&team->lock);
19038c2ecf20Sopenharmony_ci	list_for_each_entry(port, &team->port_list, list) {
19048c2ecf20Sopenharmony_ci		err = vlan_vid_add(port->dev, proto, vid);
19058c2ecf20Sopenharmony_ci		if (err)
19068c2ecf20Sopenharmony_ci			goto unwind;
19078c2ecf20Sopenharmony_ci	}
19088c2ecf20Sopenharmony_ci	mutex_unlock(&team->lock);
19098c2ecf20Sopenharmony_ci
19108c2ecf20Sopenharmony_ci	return 0;
19118c2ecf20Sopenharmony_ci
19128c2ecf20Sopenharmony_ciunwind:
19138c2ecf20Sopenharmony_ci	list_for_each_entry_continue_reverse(port, &team->port_list, list)
19148c2ecf20Sopenharmony_ci		vlan_vid_del(port->dev, proto, vid);
19158c2ecf20Sopenharmony_ci	mutex_unlock(&team->lock);
19168c2ecf20Sopenharmony_ci
19178c2ecf20Sopenharmony_ci	return err;
19188c2ecf20Sopenharmony_ci}
19198c2ecf20Sopenharmony_ci
19208c2ecf20Sopenharmony_cistatic int team_vlan_rx_kill_vid(struct net_device *dev, __be16 proto, u16 vid)
19218c2ecf20Sopenharmony_ci{
19228c2ecf20Sopenharmony_ci	struct team *team = netdev_priv(dev);
19238c2ecf20Sopenharmony_ci	struct team_port *port;
19248c2ecf20Sopenharmony_ci
19258c2ecf20Sopenharmony_ci	mutex_lock(&team->lock);
19268c2ecf20Sopenharmony_ci	list_for_each_entry(port, &team->port_list, list)
19278c2ecf20Sopenharmony_ci		vlan_vid_del(port->dev, proto, vid);
19288c2ecf20Sopenharmony_ci	mutex_unlock(&team->lock);
19298c2ecf20Sopenharmony_ci
19308c2ecf20Sopenharmony_ci	return 0;
19318c2ecf20Sopenharmony_ci}
19328c2ecf20Sopenharmony_ci
19338c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER
19348c2ecf20Sopenharmony_cistatic void team_poll_controller(struct net_device *dev)
19358c2ecf20Sopenharmony_ci{
19368c2ecf20Sopenharmony_ci}
19378c2ecf20Sopenharmony_ci
19388c2ecf20Sopenharmony_cistatic void __team_netpoll_cleanup(struct team *team)
19398c2ecf20Sopenharmony_ci{
19408c2ecf20Sopenharmony_ci	struct team_port *port;
19418c2ecf20Sopenharmony_ci
19428c2ecf20Sopenharmony_ci	list_for_each_entry(port, &team->port_list, list)
19438c2ecf20Sopenharmony_ci		team_port_disable_netpoll(port);
19448c2ecf20Sopenharmony_ci}
19458c2ecf20Sopenharmony_ci
19468c2ecf20Sopenharmony_cistatic void team_netpoll_cleanup(struct net_device *dev)
19478c2ecf20Sopenharmony_ci{
19488c2ecf20Sopenharmony_ci	struct team *team = netdev_priv(dev);
19498c2ecf20Sopenharmony_ci
19508c2ecf20Sopenharmony_ci	mutex_lock(&team->lock);
19518c2ecf20Sopenharmony_ci	__team_netpoll_cleanup(team);
19528c2ecf20Sopenharmony_ci	mutex_unlock(&team->lock);
19538c2ecf20Sopenharmony_ci}
19548c2ecf20Sopenharmony_ci
19558c2ecf20Sopenharmony_cistatic int team_netpoll_setup(struct net_device *dev,
19568c2ecf20Sopenharmony_ci			      struct netpoll_info *npifo)
19578c2ecf20Sopenharmony_ci{
19588c2ecf20Sopenharmony_ci	struct team *team = netdev_priv(dev);
19598c2ecf20Sopenharmony_ci	struct team_port *port;
19608c2ecf20Sopenharmony_ci	int err = 0;
19618c2ecf20Sopenharmony_ci
19628c2ecf20Sopenharmony_ci	mutex_lock(&team->lock);
19638c2ecf20Sopenharmony_ci	list_for_each_entry(port, &team->port_list, list) {
19648c2ecf20Sopenharmony_ci		err = __team_port_enable_netpoll(port);
19658c2ecf20Sopenharmony_ci		if (err) {
19668c2ecf20Sopenharmony_ci			__team_netpoll_cleanup(team);
19678c2ecf20Sopenharmony_ci			break;
19688c2ecf20Sopenharmony_ci		}
19698c2ecf20Sopenharmony_ci	}
19708c2ecf20Sopenharmony_ci	mutex_unlock(&team->lock);
19718c2ecf20Sopenharmony_ci	return err;
19728c2ecf20Sopenharmony_ci}
19738c2ecf20Sopenharmony_ci#endif
19748c2ecf20Sopenharmony_ci
19758c2ecf20Sopenharmony_cistatic int team_add_slave(struct net_device *dev, struct net_device *port_dev,
19768c2ecf20Sopenharmony_ci			  struct netlink_ext_ack *extack)
19778c2ecf20Sopenharmony_ci{
19788c2ecf20Sopenharmony_ci	struct team *team = netdev_priv(dev);
19798c2ecf20Sopenharmony_ci	int err;
19808c2ecf20Sopenharmony_ci
19818c2ecf20Sopenharmony_ci	mutex_lock(&team->lock);
19828c2ecf20Sopenharmony_ci	err = team_port_add(team, port_dev, extack);
19838c2ecf20Sopenharmony_ci	mutex_unlock(&team->lock);
19848c2ecf20Sopenharmony_ci
19858c2ecf20Sopenharmony_ci	if (!err)
19868c2ecf20Sopenharmony_ci		netdev_change_features(dev);
19878c2ecf20Sopenharmony_ci
19888c2ecf20Sopenharmony_ci	return err;
19898c2ecf20Sopenharmony_ci}
19908c2ecf20Sopenharmony_ci
19918c2ecf20Sopenharmony_cistatic int team_del_slave(struct net_device *dev, struct net_device *port_dev)
19928c2ecf20Sopenharmony_ci{
19938c2ecf20Sopenharmony_ci	struct team *team = netdev_priv(dev);
19948c2ecf20Sopenharmony_ci	int err;
19958c2ecf20Sopenharmony_ci
19968c2ecf20Sopenharmony_ci	mutex_lock(&team->lock);
19978c2ecf20Sopenharmony_ci	err = team_port_del(team, port_dev);
19988c2ecf20Sopenharmony_ci	mutex_unlock(&team->lock);
19998c2ecf20Sopenharmony_ci
20008c2ecf20Sopenharmony_ci	if (err)
20018c2ecf20Sopenharmony_ci		return err;
20028c2ecf20Sopenharmony_ci
20038c2ecf20Sopenharmony_ci	if (netif_is_team_master(port_dev)) {
20048c2ecf20Sopenharmony_ci		lockdep_unregister_key(&team->team_lock_key);
20058c2ecf20Sopenharmony_ci		lockdep_register_key(&team->team_lock_key);
20068c2ecf20Sopenharmony_ci		lockdep_set_class(&team->lock, &team->team_lock_key);
20078c2ecf20Sopenharmony_ci	}
20088c2ecf20Sopenharmony_ci	netdev_change_features(dev);
20098c2ecf20Sopenharmony_ci
20108c2ecf20Sopenharmony_ci	return err;
20118c2ecf20Sopenharmony_ci}
20128c2ecf20Sopenharmony_ci
20138c2ecf20Sopenharmony_cistatic netdev_features_t team_fix_features(struct net_device *dev,
20148c2ecf20Sopenharmony_ci					   netdev_features_t features)
20158c2ecf20Sopenharmony_ci{
20168c2ecf20Sopenharmony_ci	struct team_port *port;
20178c2ecf20Sopenharmony_ci	struct team *team = netdev_priv(dev);
20188c2ecf20Sopenharmony_ci	netdev_features_t mask;
20198c2ecf20Sopenharmony_ci
20208c2ecf20Sopenharmony_ci	mask = features;
20218c2ecf20Sopenharmony_ci	features &= ~NETIF_F_ONE_FOR_ALL;
20228c2ecf20Sopenharmony_ci	features |= NETIF_F_ALL_FOR_ALL;
20238c2ecf20Sopenharmony_ci
20248c2ecf20Sopenharmony_ci	rcu_read_lock();
20258c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(port, &team->port_list, list) {
20268c2ecf20Sopenharmony_ci		features = netdev_increment_features(features,
20278c2ecf20Sopenharmony_ci						     port->dev->features,
20288c2ecf20Sopenharmony_ci						     mask);
20298c2ecf20Sopenharmony_ci	}
20308c2ecf20Sopenharmony_ci	rcu_read_unlock();
20318c2ecf20Sopenharmony_ci
20328c2ecf20Sopenharmony_ci	features = netdev_add_tso_features(features, mask);
20338c2ecf20Sopenharmony_ci
20348c2ecf20Sopenharmony_ci	return features;
20358c2ecf20Sopenharmony_ci}
20368c2ecf20Sopenharmony_ci
20378c2ecf20Sopenharmony_cistatic int team_change_carrier(struct net_device *dev, bool new_carrier)
20388c2ecf20Sopenharmony_ci{
20398c2ecf20Sopenharmony_ci	struct team *team = netdev_priv(dev);
20408c2ecf20Sopenharmony_ci
20418c2ecf20Sopenharmony_ci	team->user_carrier_enabled = true;
20428c2ecf20Sopenharmony_ci
20438c2ecf20Sopenharmony_ci	if (new_carrier)
20448c2ecf20Sopenharmony_ci		netif_carrier_on(dev);
20458c2ecf20Sopenharmony_ci	else
20468c2ecf20Sopenharmony_ci		netif_carrier_off(dev);
20478c2ecf20Sopenharmony_ci	return 0;
20488c2ecf20Sopenharmony_ci}
20498c2ecf20Sopenharmony_ci
20508c2ecf20Sopenharmony_cistatic const struct net_device_ops team_netdev_ops = {
20518c2ecf20Sopenharmony_ci	.ndo_init		= team_init,
20528c2ecf20Sopenharmony_ci	.ndo_uninit		= team_uninit,
20538c2ecf20Sopenharmony_ci	.ndo_open		= team_open,
20548c2ecf20Sopenharmony_ci	.ndo_stop		= team_close,
20558c2ecf20Sopenharmony_ci	.ndo_start_xmit		= team_xmit,
20568c2ecf20Sopenharmony_ci	.ndo_select_queue	= team_select_queue,
20578c2ecf20Sopenharmony_ci	.ndo_change_rx_flags	= team_change_rx_flags,
20588c2ecf20Sopenharmony_ci	.ndo_set_rx_mode	= team_set_rx_mode,
20598c2ecf20Sopenharmony_ci	.ndo_set_mac_address	= team_set_mac_address,
20608c2ecf20Sopenharmony_ci	.ndo_change_mtu		= team_change_mtu,
20618c2ecf20Sopenharmony_ci	.ndo_get_stats64	= team_get_stats64,
20628c2ecf20Sopenharmony_ci	.ndo_vlan_rx_add_vid	= team_vlan_rx_add_vid,
20638c2ecf20Sopenharmony_ci	.ndo_vlan_rx_kill_vid	= team_vlan_rx_kill_vid,
20648c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER
20658c2ecf20Sopenharmony_ci	.ndo_poll_controller	= team_poll_controller,
20668c2ecf20Sopenharmony_ci	.ndo_netpoll_setup	= team_netpoll_setup,
20678c2ecf20Sopenharmony_ci	.ndo_netpoll_cleanup	= team_netpoll_cleanup,
20688c2ecf20Sopenharmony_ci#endif
20698c2ecf20Sopenharmony_ci	.ndo_add_slave		= team_add_slave,
20708c2ecf20Sopenharmony_ci	.ndo_del_slave		= team_del_slave,
20718c2ecf20Sopenharmony_ci	.ndo_fix_features	= team_fix_features,
20728c2ecf20Sopenharmony_ci	.ndo_change_carrier     = team_change_carrier,
20738c2ecf20Sopenharmony_ci	.ndo_features_check	= passthru_features_check,
20748c2ecf20Sopenharmony_ci};
20758c2ecf20Sopenharmony_ci
20768c2ecf20Sopenharmony_ci/***********************
20778c2ecf20Sopenharmony_ci * ethtool interface
20788c2ecf20Sopenharmony_ci ***********************/
20798c2ecf20Sopenharmony_ci
20808c2ecf20Sopenharmony_cistatic void team_ethtool_get_drvinfo(struct net_device *dev,
20818c2ecf20Sopenharmony_ci				     struct ethtool_drvinfo *drvinfo)
20828c2ecf20Sopenharmony_ci{
20838c2ecf20Sopenharmony_ci	strlcpy(drvinfo->driver, DRV_NAME, sizeof(drvinfo->driver));
20848c2ecf20Sopenharmony_ci	strlcpy(drvinfo->version, UTS_RELEASE, sizeof(drvinfo->version));
20858c2ecf20Sopenharmony_ci}
20868c2ecf20Sopenharmony_ci
20878c2ecf20Sopenharmony_cistatic int team_ethtool_get_link_ksettings(struct net_device *dev,
20888c2ecf20Sopenharmony_ci					   struct ethtool_link_ksettings *cmd)
20898c2ecf20Sopenharmony_ci{
20908c2ecf20Sopenharmony_ci	struct team *team= netdev_priv(dev);
20918c2ecf20Sopenharmony_ci	unsigned long speed = 0;
20928c2ecf20Sopenharmony_ci	struct team_port *port;
20938c2ecf20Sopenharmony_ci
20948c2ecf20Sopenharmony_ci	cmd->base.duplex = DUPLEX_UNKNOWN;
20958c2ecf20Sopenharmony_ci	cmd->base.port = PORT_OTHER;
20968c2ecf20Sopenharmony_ci
20978c2ecf20Sopenharmony_ci	rcu_read_lock();
20988c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(port, &team->port_list, list) {
20998c2ecf20Sopenharmony_ci		if (team_port_txable(port)) {
21008c2ecf20Sopenharmony_ci			if (port->state.speed != SPEED_UNKNOWN)
21018c2ecf20Sopenharmony_ci				speed += port->state.speed;
21028c2ecf20Sopenharmony_ci			if (cmd->base.duplex == DUPLEX_UNKNOWN &&
21038c2ecf20Sopenharmony_ci			    port->state.duplex != DUPLEX_UNKNOWN)
21048c2ecf20Sopenharmony_ci				cmd->base.duplex = port->state.duplex;
21058c2ecf20Sopenharmony_ci		}
21068c2ecf20Sopenharmony_ci	}
21078c2ecf20Sopenharmony_ci	rcu_read_unlock();
21088c2ecf20Sopenharmony_ci
21098c2ecf20Sopenharmony_ci	cmd->base.speed = speed ? : SPEED_UNKNOWN;
21108c2ecf20Sopenharmony_ci
21118c2ecf20Sopenharmony_ci	return 0;
21128c2ecf20Sopenharmony_ci}
21138c2ecf20Sopenharmony_ci
21148c2ecf20Sopenharmony_cistatic const struct ethtool_ops team_ethtool_ops = {
21158c2ecf20Sopenharmony_ci	.get_drvinfo		= team_ethtool_get_drvinfo,
21168c2ecf20Sopenharmony_ci	.get_link		= ethtool_op_get_link,
21178c2ecf20Sopenharmony_ci	.get_link_ksettings	= team_ethtool_get_link_ksettings,
21188c2ecf20Sopenharmony_ci};
21198c2ecf20Sopenharmony_ci
21208c2ecf20Sopenharmony_ci/***********************
21218c2ecf20Sopenharmony_ci * rt netlink interface
21228c2ecf20Sopenharmony_ci ***********************/
21238c2ecf20Sopenharmony_ci
21248c2ecf20Sopenharmony_cistatic void team_setup_by_port(struct net_device *dev,
21258c2ecf20Sopenharmony_ci			       struct net_device *port_dev)
21268c2ecf20Sopenharmony_ci{
21278c2ecf20Sopenharmony_ci	struct team *team = netdev_priv(dev);
21288c2ecf20Sopenharmony_ci
21298c2ecf20Sopenharmony_ci	if (port_dev->type == ARPHRD_ETHER)
21308c2ecf20Sopenharmony_ci		dev->header_ops	= team->header_ops_cache;
21318c2ecf20Sopenharmony_ci	else
21328c2ecf20Sopenharmony_ci		dev->header_ops	= port_dev->header_ops;
21338c2ecf20Sopenharmony_ci	dev->type = port_dev->type;
21348c2ecf20Sopenharmony_ci	dev->hard_header_len = port_dev->hard_header_len;
21358c2ecf20Sopenharmony_ci	dev->needed_headroom = port_dev->needed_headroom;
21368c2ecf20Sopenharmony_ci	dev->addr_len = port_dev->addr_len;
21378c2ecf20Sopenharmony_ci	dev->mtu = port_dev->mtu;
21388c2ecf20Sopenharmony_ci	memcpy(dev->broadcast, port_dev->broadcast, port_dev->addr_len);
21398c2ecf20Sopenharmony_ci	eth_hw_addr_inherit(dev, port_dev);
21408c2ecf20Sopenharmony_ci
21418c2ecf20Sopenharmony_ci	if (port_dev->flags & IFF_POINTOPOINT) {
21428c2ecf20Sopenharmony_ci		dev->flags &= ~(IFF_BROADCAST | IFF_MULTICAST);
21438c2ecf20Sopenharmony_ci		dev->flags |= (IFF_POINTOPOINT | IFF_NOARP);
21448c2ecf20Sopenharmony_ci	} else if ((port_dev->flags & (IFF_BROADCAST | IFF_MULTICAST)) ==
21458c2ecf20Sopenharmony_ci		    (IFF_BROADCAST | IFF_MULTICAST)) {
21468c2ecf20Sopenharmony_ci		dev->flags |= (IFF_BROADCAST | IFF_MULTICAST);
21478c2ecf20Sopenharmony_ci		dev->flags &= ~(IFF_POINTOPOINT | IFF_NOARP);
21488c2ecf20Sopenharmony_ci	}
21498c2ecf20Sopenharmony_ci}
21508c2ecf20Sopenharmony_ci
21518c2ecf20Sopenharmony_cistatic int team_dev_type_check_change(struct net_device *dev,
21528c2ecf20Sopenharmony_ci				      struct net_device *port_dev)
21538c2ecf20Sopenharmony_ci{
21548c2ecf20Sopenharmony_ci	struct team *team = netdev_priv(dev);
21558c2ecf20Sopenharmony_ci	char *portname = port_dev->name;
21568c2ecf20Sopenharmony_ci	int err;
21578c2ecf20Sopenharmony_ci
21588c2ecf20Sopenharmony_ci	if (dev->type == port_dev->type)
21598c2ecf20Sopenharmony_ci		return 0;
21608c2ecf20Sopenharmony_ci	if (!list_empty(&team->port_list)) {
21618c2ecf20Sopenharmony_ci		netdev_err(dev, "Device %s is of different type\n", portname);
21628c2ecf20Sopenharmony_ci		return -EBUSY;
21638c2ecf20Sopenharmony_ci	}
21648c2ecf20Sopenharmony_ci	err = call_netdevice_notifiers(NETDEV_PRE_TYPE_CHANGE, dev);
21658c2ecf20Sopenharmony_ci	err = notifier_to_errno(err);
21668c2ecf20Sopenharmony_ci	if (err) {
21678c2ecf20Sopenharmony_ci		netdev_err(dev, "Refused to change device type\n");
21688c2ecf20Sopenharmony_ci		return err;
21698c2ecf20Sopenharmony_ci	}
21708c2ecf20Sopenharmony_ci	dev_uc_flush(dev);
21718c2ecf20Sopenharmony_ci	dev_mc_flush(dev);
21728c2ecf20Sopenharmony_ci	team_setup_by_port(dev, port_dev);
21738c2ecf20Sopenharmony_ci	call_netdevice_notifiers(NETDEV_POST_TYPE_CHANGE, dev);
21748c2ecf20Sopenharmony_ci	return 0;
21758c2ecf20Sopenharmony_ci}
21768c2ecf20Sopenharmony_ci
21778c2ecf20Sopenharmony_cistatic void team_setup(struct net_device *dev)
21788c2ecf20Sopenharmony_ci{
21798c2ecf20Sopenharmony_ci	struct team *team = netdev_priv(dev);
21808c2ecf20Sopenharmony_ci
21818c2ecf20Sopenharmony_ci	ether_setup(dev);
21828c2ecf20Sopenharmony_ci	dev->max_mtu = ETH_MAX_MTU;
21838c2ecf20Sopenharmony_ci	team->header_ops_cache = dev->header_ops;
21848c2ecf20Sopenharmony_ci
21858c2ecf20Sopenharmony_ci	dev->netdev_ops = &team_netdev_ops;
21868c2ecf20Sopenharmony_ci	dev->ethtool_ops = &team_ethtool_ops;
21878c2ecf20Sopenharmony_ci	dev->needs_free_netdev = true;
21888c2ecf20Sopenharmony_ci	dev->priv_destructor = team_destructor;
21898c2ecf20Sopenharmony_ci	dev->priv_flags &= ~(IFF_XMIT_DST_RELEASE | IFF_TX_SKB_SHARING);
21908c2ecf20Sopenharmony_ci	dev->priv_flags |= IFF_NO_QUEUE;
21918c2ecf20Sopenharmony_ci	dev->priv_flags |= IFF_TEAM;
21928c2ecf20Sopenharmony_ci
21938c2ecf20Sopenharmony_ci	/*
21948c2ecf20Sopenharmony_ci	 * Indicate we support unicast address filtering. That way core won't
21958c2ecf20Sopenharmony_ci	 * bring us to promisc mode in case a unicast addr is added.
21968c2ecf20Sopenharmony_ci	 * Let this up to underlay drivers.
21978c2ecf20Sopenharmony_ci	 */
21988c2ecf20Sopenharmony_ci	dev->priv_flags |= IFF_UNICAST_FLT | IFF_LIVE_ADDR_CHANGE;
21998c2ecf20Sopenharmony_ci
22008c2ecf20Sopenharmony_ci	dev->features |= NETIF_F_LLTX;
22018c2ecf20Sopenharmony_ci	dev->features |= NETIF_F_GRO;
22028c2ecf20Sopenharmony_ci
22038c2ecf20Sopenharmony_ci	/* Don't allow team devices to change network namespaces. */
22048c2ecf20Sopenharmony_ci	dev->features |= NETIF_F_NETNS_LOCAL;
22058c2ecf20Sopenharmony_ci
22068c2ecf20Sopenharmony_ci	dev->hw_features = TEAM_VLAN_FEATURES |
22078c2ecf20Sopenharmony_ci			   NETIF_F_HW_VLAN_CTAG_RX |
22088c2ecf20Sopenharmony_ci			   NETIF_F_HW_VLAN_CTAG_FILTER |
22098c2ecf20Sopenharmony_ci			   NETIF_F_HW_VLAN_STAG_RX |
22108c2ecf20Sopenharmony_ci			   NETIF_F_HW_VLAN_STAG_FILTER;
22118c2ecf20Sopenharmony_ci
22128c2ecf20Sopenharmony_ci	dev->hw_features |= NETIF_F_GSO_ENCAP_ALL | NETIF_F_GSO_UDP_L4;
22138c2ecf20Sopenharmony_ci	dev->features |= dev->hw_features;
22148c2ecf20Sopenharmony_ci	dev->features |= NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_STAG_TX;
22158c2ecf20Sopenharmony_ci}
22168c2ecf20Sopenharmony_ci
22178c2ecf20Sopenharmony_cistatic int team_newlink(struct net *src_net, struct net_device *dev,
22188c2ecf20Sopenharmony_ci			struct nlattr *tb[], struct nlattr *data[],
22198c2ecf20Sopenharmony_ci			struct netlink_ext_ack *extack)
22208c2ecf20Sopenharmony_ci{
22218c2ecf20Sopenharmony_ci	if (tb[IFLA_ADDRESS] == NULL)
22228c2ecf20Sopenharmony_ci		eth_hw_addr_random(dev);
22238c2ecf20Sopenharmony_ci
22248c2ecf20Sopenharmony_ci	return register_netdevice(dev);
22258c2ecf20Sopenharmony_ci}
22268c2ecf20Sopenharmony_ci
22278c2ecf20Sopenharmony_cistatic int team_validate(struct nlattr *tb[], struct nlattr *data[],
22288c2ecf20Sopenharmony_ci			 struct netlink_ext_ack *extack)
22298c2ecf20Sopenharmony_ci{
22308c2ecf20Sopenharmony_ci	if (tb[IFLA_ADDRESS]) {
22318c2ecf20Sopenharmony_ci		if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN)
22328c2ecf20Sopenharmony_ci			return -EINVAL;
22338c2ecf20Sopenharmony_ci		if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS])))
22348c2ecf20Sopenharmony_ci			return -EADDRNOTAVAIL;
22358c2ecf20Sopenharmony_ci	}
22368c2ecf20Sopenharmony_ci	return 0;
22378c2ecf20Sopenharmony_ci}
22388c2ecf20Sopenharmony_ci
22398c2ecf20Sopenharmony_cistatic unsigned int team_get_num_tx_queues(void)
22408c2ecf20Sopenharmony_ci{
22418c2ecf20Sopenharmony_ci	return TEAM_DEFAULT_NUM_TX_QUEUES;
22428c2ecf20Sopenharmony_ci}
22438c2ecf20Sopenharmony_ci
22448c2ecf20Sopenharmony_cistatic unsigned int team_get_num_rx_queues(void)
22458c2ecf20Sopenharmony_ci{
22468c2ecf20Sopenharmony_ci	return TEAM_DEFAULT_NUM_RX_QUEUES;
22478c2ecf20Sopenharmony_ci}
22488c2ecf20Sopenharmony_ci
22498c2ecf20Sopenharmony_cistatic struct rtnl_link_ops team_link_ops __read_mostly = {
22508c2ecf20Sopenharmony_ci	.kind			= DRV_NAME,
22518c2ecf20Sopenharmony_ci	.priv_size		= sizeof(struct team),
22528c2ecf20Sopenharmony_ci	.setup			= team_setup,
22538c2ecf20Sopenharmony_ci	.newlink		= team_newlink,
22548c2ecf20Sopenharmony_ci	.validate		= team_validate,
22558c2ecf20Sopenharmony_ci	.get_num_tx_queues	= team_get_num_tx_queues,
22568c2ecf20Sopenharmony_ci	.get_num_rx_queues	= team_get_num_rx_queues,
22578c2ecf20Sopenharmony_ci};
22588c2ecf20Sopenharmony_ci
22598c2ecf20Sopenharmony_ci
22608c2ecf20Sopenharmony_ci/***********************************
22618c2ecf20Sopenharmony_ci * Generic netlink custom interface
22628c2ecf20Sopenharmony_ci ***********************************/
22638c2ecf20Sopenharmony_ci
22648c2ecf20Sopenharmony_cistatic struct genl_family team_nl_family;
22658c2ecf20Sopenharmony_ci
22668c2ecf20Sopenharmony_cistatic const struct nla_policy team_nl_policy[TEAM_ATTR_MAX + 1] = {
22678c2ecf20Sopenharmony_ci	[TEAM_ATTR_UNSPEC]			= { .type = NLA_UNSPEC, },
22688c2ecf20Sopenharmony_ci	[TEAM_ATTR_TEAM_IFINDEX]		= { .type = NLA_U32 },
22698c2ecf20Sopenharmony_ci	[TEAM_ATTR_LIST_OPTION]			= { .type = NLA_NESTED },
22708c2ecf20Sopenharmony_ci	[TEAM_ATTR_LIST_PORT]			= { .type = NLA_NESTED },
22718c2ecf20Sopenharmony_ci};
22728c2ecf20Sopenharmony_ci
22738c2ecf20Sopenharmony_cistatic const struct nla_policy
22748c2ecf20Sopenharmony_citeam_nl_option_policy[TEAM_ATTR_OPTION_MAX + 1] = {
22758c2ecf20Sopenharmony_ci	[TEAM_ATTR_OPTION_UNSPEC]		= { .type = NLA_UNSPEC, },
22768c2ecf20Sopenharmony_ci	[TEAM_ATTR_OPTION_NAME] = {
22778c2ecf20Sopenharmony_ci		.type = NLA_STRING,
22788c2ecf20Sopenharmony_ci		.len = TEAM_STRING_MAX_LEN,
22798c2ecf20Sopenharmony_ci	},
22808c2ecf20Sopenharmony_ci	[TEAM_ATTR_OPTION_CHANGED]		= { .type = NLA_FLAG },
22818c2ecf20Sopenharmony_ci	[TEAM_ATTR_OPTION_TYPE]			= { .type = NLA_U8 },
22828c2ecf20Sopenharmony_ci	[TEAM_ATTR_OPTION_DATA]			= { .type = NLA_BINARY },
22838c2ecf20Sopenharmony_ci	[TEAM_ATTR_OPTION_PORT_IFINDEX]		= { .type = NLA_U32 },
22848c2ecf20Sopenharmony_ci	[TEAM_ATTR_OPTION_ARRAY_INDEX]		= { .type = NLA_U32 },
22858c2ecf20Sopenharmony_ci};
22868c2ecf20Sopenharmony_ci
22878c2ecf20Sopenharmony_cistatic int team_nl_cmd_noop(struct sk_buff *skb, struct genl_info *info)
22888c2ecf20Sopenharmony_ci{
22898c2ecf20Sopenharmony_ci	struct sk_buff *msg;
22908c2ecf20Sopenharmony_ci	void *hdr;
22918c2ecf20Sopenharmony_ci	int err;
22928c2ecf20Sopenharmony_ci
22938c2ecf20Sopenharmony_ci	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
22948c2ecf20Sopenharmony_ci	if (!msg)
22958c2ecf20Sopenharmony_ci		return -ENOMEM;
22968c2ecf20Sopenharmony_ci
22978c2ecf20Sopenharmony_ci	hdr = genlmsg_put(msg, info->snd_portid, info->snd_seq,
22988c2ecf20Sopenharmony_ci			  &team_nl_family, 0, TEAM_CMD_NOOP);
22998c2ecf20Sopenharmony_ci	if (!hdr) {
23008c2ecf20Sopenharmony_ci		err = -EMSGSIZE;
23018c2ecf20Sopenharmony_ci		goto err_msg_put;
23028c2ecf20Sopenharmony_ci	}
23038c2ecf20Sopenharmony_ci
23048c2ecf20Sopenharmony_ci	genlmsg_end(msg, hdr);
23058c2ecf20Sopenharmony_ci
23068c2ecf20Sopenharmony_ci	return genlmsg_unicast(genl_info_net(info), msg, info->snd_portid);
23078c2ecf20Sopenharmony_ci
23088c2ecf20Sopenharmony_cierr_msg_put:
23098c2ecf20Sopenharmony_ci	nlmsg_free(msg);
23108c2ecf20Sopenharmony_ci
23118c2ecf20Sopenharmony_ci	return err;
23128c2ecf20Sopenharmony_ci}
23138c2ecf20Sopenharmony_ci
23148c2ecf20Sopenharmony_ci/*
23158c2ecf20Sopenharmony_ci * Netlink cmd functions should be locked by following two functions.
23168c2ecf20Sopenharmony_ci * Since dev gets held here, that ensures dev won't disappear in between.
23178c2ecf20Sopenharmony_ci */
23188c2ecf20Sopenharmony_cistatic struct team *team_nl_team_get(struct genl_info *info)
23198c2ecf20Sopenharmony_ci{
23208c2ecf20Sopenharmony_ci	struct net *net = genl_info_net(info);
23218c2ecf20Sopenharmony_ci	int ifindex;
23228c2ecf20Sopenharmony_ci	struct net_device *dev;
23238c2ecf20Sopenharmony_ci	struct team *team;
23248c2ecf20Sopenharmony_ci
23258c2ecf20Sopenharmony_ci	if (!info->attrs[TEAM_ATTR_TEAM_IFINDEX])
23268c2ecf20Sopenharmony_ci		return NULL;
23278c2ecf20Sopenharmony_ci
23288c2ecf20Sopenharmony_ci	ifindex = nla_get_u32(info->attrs[TEAM_ATTR_TEAM_IFINDEX]);
23298c2ecf20Sopenharmony_ci	dev = dev_get_by_index(net, ifindex);
23308c2ecf20Sopenharmony_ci	if (!dev || dev->netdev_ops != &team_netdev_ops) {
23318c2ecf20Sopenharmony_ci		if (dev)
23328c2ecf20Sopenharmony_ci			dev_put(dev);
23338c2ecf20Sopenharmony_ci		return NULL;
23348c2ecf20Sopenharmony_ci	}
23358c2ecf20Sopenharmony_ci
23368c2ecf20Sopenharmony_ci	team = netdev_priv(dev);
23378c2ecf20Sopenharmony_ci	mutex_lock(&team->lock);
23388c2ecf20Sopenharmony_ci	return team;
23398c2ecf20Sopenharmony_ci}
23408c2ecf20Sopenharmony_ci
23418c2ecf20Sopenharmony_cistatic void team_nl_team_put(struct team *team)
23428c2ecf20Sopenharmony_ci{
23438c2ecf20Sopenharmony_ci	mutex_unlock(&team->lock);
23448c2ecf20Sopenharmony_ci	dev_put(team->dev);
23458c2ecf20Sopenharmony_ci}
23468c2ecf20Sopenharmony_ci
23478c2ecf20Sopenharmony_citypedef int team_nl_send_func_t(struct sk_buff *skb,
23488c2ecf20Sopenharmony_ci				struct team *team, u32 portid);
23498c2ecf20Sopenharmony_ci
23508c2ecf20Sopenharmony_cistatic int team_nl_send_unicast(struct sk_buff *skb, struct team *team, u32 portid)
23518c2ecf20Sopenharmony_ci{
23528c2ecf20Sopenharmony_ci	return genlmsg_unicast(dev_net(team->dev), skb, portid);
23538c2ecf20Sopenharmony_ci}
23548c2ecf20Sopenharmony_ci
23558c2ecf20Sopenharmony_cistatic int team_nl_fill_one_option_get(struct sk_buff *skb, struct team *team,
23568c2ecf20Sopenharmony_ci				       struct team_option_inst *opt_inst)
23578c2ecf20Sopenharmony_ci{
23588c2ecf20Sopenharmony_ci	struct nlattr *option_item;
23598c2ecf20Sopenharmony_ci	struct team_option *option = opt_inst->option;
23608c2ecf20Sopenharmony_ci	struct team_option_inst_info *opt_inst_info = &opt_inst->info;
23618c2ecf20Sopenharmony_ci	struct team_gsetter_ctx ctx;
23628c2ecf20Sopenharmony_ci	int err;
23638c2ecf20Sopenharmony_ci
23648c2ecf20Sopenharmony_ci	ctx.info = opt_inst_info;
23658c2ecf20Sopenharmony_ci	err = team_option_get(team, opt_inst, &ctx);
23668c2ecf20Sopenharmony_ci	if (err)
23678c2ecf20Sopenharmony_ci		return err;
23688c2ecf20Sopenharmony_ci
23698c2ecf20Sopenharmony_ci	option_item = nla_nest_start_noflag(skb, TEAM_ATTR_ITEM_OPTION);
23708c2ecf20Sopenharmony_ci	if (!option_item)
23718c2ecf20Sopenharmony_ci		return -EMSGSIZE;
23728c2ecf20Sopenharmony_ci
23738c2ecf20Sopenharmony_ci	if (nla_put_string(skb, TEAM_ATTR_OPTION_NAME, option->name))
23748c2ecf20Sopenharmony_ci		goto nest_cancel;
23758c2ecf20Sopenharmony_ci	if (opt_inst_info->port &&
23768c2ecf20Sopenharmony_ci	    nla_put_u32(skb, TEAM_ATTR_OPTION_PORT_IFINDEX,
23778c2ecf20Sopenharmony_ci			opt_inst_info->port->dev->ifindex))
23788c2ecf20Sopenharmony_ci		goto nest_cancel;
23798c2ecf20Sopenharmony_ci	if (opt_inst->option->array_size &&
23808c2ecf20Sopenharmony_ci	    nla_put_u32(skb, TEAM_ATTR_OPTION_ARRAY_INDEX,
23818c2ecf20Sopenharmony_ci			opt_inst_info->array_index))
23828c2ecf20Sopenharmony_ci		goto nest_cancel;
23838c2ecf20Sopenharmony_ci
23848c2ecf20Sopenharmony_ci	switch (option->type) {
23858c2ecf20Sopenharmony_ci	case TEAM_OPTION_TYPE_U32:
23868c2ecf20Sopenharmony_ci		if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_U32))
23878c2ecf20Sopenharmony_ci			goto nest_cancel;
23888c2ecf20Sopenharmony_ci		if (nla_put_u32(skb, TEAM_ATTR_OPTION_DATA, ctx.data.u32_val))
23898c2ecf20Sopenharmony_ci			goto nest_cancel;
23908c2ecf20Sopenharmony_ci		break;
23918c2ecf20Sopenharmony_ci	case TEAM_OPTION_TYPE_STRING:
23928c2ecf20Sopenharmony_ci		if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_STRING))
23938c2ecf20Sopenharmony_ci			goto nest_cancel;
23948c2ecf20Sopenharmony_ci		if (nla_put_string(skb, TEAM_ATTR_OPTION_DATA,
23958c2ecf20Sopenharmony_ci				   ctx.data.str_val))
23968c2ecf20Sopenharmony_ci			goto nest_cancel;
23978c2ecf20Sopenharmony_ci		break;
23988c2ecf20Sopenharmony_ci	case TEAM_OPTION_TYPE_BINARY:
23998c2ecf20Sopenharmony_ci		if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_BINARY))
24008c2ecf20Sopenharmony_ci			goto nest_cancel;
24018c2ecf20Sopenharmony_ci		if (nla_put(skb, TEAM_ATTR_OPTION_DATA, ctx.data.bin_val.len,
24028c2ecf20Sopenharmony_ci			    ctx.data.bin_val.ptr))
24038c2ecf20Sopenharmony_ci			goto nest_cancel;
24048c2ecf20Sopenharmony_ci		break;
24058c2ecf20Sopenharmony_ci	case TEAM_OPTION_TYPE_BOOL:
24068c2ecf20Sopenharmony_ci		if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_FLAG))
24078c2ecf20Sopenharmony_ci			goto nest_cancel;
24088c2ecf20Sopenharmony_ci		if (ctx.data.bool_val &&
24098c2ecf20Sopenharmony_ci		    nla_put_flag(skb, TEAM_ATTR_OPTION_DATA))
24108c2ecf20Sopenharmony_ci			goto nest_cancel;
24118c2ecf20Sopenharmony_ci		break;
24128c2ecf20Sopenharmony_ci	case TEAM_OPTION_TYPE_S32:
24138c2ecf20Sopenharmony_ci		if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_S32))
24148c2ecf20Sopenharmony_ci			goto nest_cancel;
24158c2ecf20Sopenharmony_ci		if (nla_put_s32(skb, TEAM_ATTR_OPTION_DATA, ctx.data.s32_val))
24168c2ecf20Sopenharmony_ci			goto nest_cancel;
24178c2ecf20Sopenharmony_ci		break;
24188c2ecf20Sopenharmony_ci	default:
24198c2ecf20Sopenharmony_ci		BUG();
24208c2ecf20Sopenharmony_ci	}
24218c2ecf20Sopenharmony_ci	if (opt_inst->removed && nla_put_flag(skb, TEAM_ATTR_OPTION_REMOVED))
24228c2ecf20Sopenharmony_ci		goto nest_cancel;
24238c2ecf20Sopenharmony_ci	if (opt_inst->changed) {
24248c2ecf20Sopenharmony_ci		if (nla_put_flag(skb, TEAM_ATTR_OPTION_CHANGED))
24258c2ecf20Sopenharmony_ci			goto nest_cancel;
24268c2ecf20Sopenharmony_ci		opt_inst->changed = false;
24278c2ecf20Sopenharmony_ci	}
24288c2ecf20Sopenharmony_ci	nla_nest_end(skb, option_item);
24298c2ecf20Sopenharmony_ci	return 0;
24308c2ecf20Sopenharmony_ci
24318c2ecf20Sopenharmony_cinest_cancel:
24328c2ecf20Sopenharmony_ci	nla_nest_cancel(skb, option_item);
24338c2ecf20Sopenharmony_ci	return -EMSGSIZE;
24348c2ecf20Sopenharmony_ci}
24358c2ecf20Sopenharmony_ci
24368c2ecf20Sopenharmony_cistatic int __send_and_alloc_skb(struct sk_buff **pskb,
24378c2ecf20Sopenharmony_ci				struct team *team, u32 portid,
24388c2ecf20Sopenharmony_ci				team_nl_send_func_t *send_func)
24398c2ecf20Sopenharmony_ci{
24408c2ecf20Sopenharmony_ci	int err;
24418c2ecf20Sopenharmony_ci
24428c2ecf20Sopenharmony_ci	if (*pskb) {
24438c2ecf20Sopenharmony_ci		err = send_func(*pskb, team, portid);
24448c2ecf20Sopenharmony_ci		if (err)
24458c2ecf20Sopenharmony_ci			return err;
24468c2ecf20Sopenharmony_ci	}
24478c2ecf20Sopenharmony_ci	*pskb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
24488c2ecf20Sopenharmony_ci	if (!*pskb)
24498c2ecf20Sopenharmony_ci		return -ENOMEM;
24508c2ecf20Sopenharmony_ci	return 0;
24518c2ecf20Sopenharmony_ci}
24528c2ecf20Sopenharmony_ci
24538c2ecf20Sopenharmony_cistatic int team_nl_send_options_get(struct team *team, u32 portid, u32 seq,
24548c2ecf20Sopenharmony_ci				    int flags, team_nl_send_func_t *send_func,
24558c2ecf20Sopenharmony_ci				    struct list_head *sel_opt_inst_list)
24568c2ecf20Sopenharmony_ci{
24578c2ecf20Sopenharmony_ci	struct nlattr *option_list;
24588c2ecf20Sopenharmony_ci	struct nlmsghdr *nlh;
24598c2ecf20Sopenharmony_ci	void *hdr;
24608c2ecf20Sopenharmony_ci	struct team_option_inst *opt_inst;
24618c2ecf20Sopenharmony_ci	int err;
24628c2ecf20Sopenharmony_ci	struct sk_buff *skb = NULL;
24638c2ecf20Sopenharmony_ci	bool incomplete;
24648c2ecf20Sopenharmony_ci	int i;
24658c2ecf20Sopenharmony_ci
24668c2ecf20Sopenharmony_ci	opt_inst = list_first_entry(sel_opt_inst_list,
24678c2ecf20Sopenharmony_ci				    struct team_option_inst, tmp_list);
24688c2ecf20Sopenharmony_ci
24698c2ecf20Sopenharmony_cistart_again:
24708c2ecf20Sopenharmony_ci	err = __send_and_alloc_skb(&skb, team, portid, send_func);
24718c2ecf20Sopenharmony_ci	if (err)
24728c2ecf20Sopenharmony_ci		return err;
24738c2ecf20Sopenharmony_ci
24748c2ecf20Sopenharmony_ci	hdr = genlmsg_put(skb, portid, seq, &team_nl_family, flags | NLM_F_MULTI,
24758c2ecf20Sopenharmony_ci			  TEAM_CMD_OPTIONS_GET);
24768c2ecf20Sopenharmony_ci	if (!hdr) {
24778c2ecf20Sopenharmony_ci		nlmsg_free(skb);
24788c2ecf20Sopenharmony_ci		return -EMSGSIZE;
24798c2ecf20Sopenharmony_ci	}
24808c2ecf20Sopenharmony_ci
24818c2ecf20Sopenharmony_ci	if (nla_put_u32(skb, TEAM_ATTR_TEAM_IFINDEX, team->dev->ifindex))
24828c2ecf20Sopenharmony_ci		goto nla_put_failure;
24838c2ecf20Sopenharmony_ci	option_list = nla_nest_start_noflag(skb, TEAM_ATTR_LIST_OPTION);
24848c2ecf20Sopenharmony_ci	if (!option_list)
24858c2ecf20Sopenharmony_ci		goto nla_put_failure;
24868c2ecf20Sopenharmony_ci
24878c2ecf20Sopenharmony_ci	i = 0;
24888c2ecf20Sopenharmony_ci	incomplete = false;
24898c2ecf20Sopenharmony_ci	list_for_each_entry_from(opt_inst, sel_opt_inst_list, tmp_list) {
24908c2ecf20Sopenharmony_ci		err = team_nl_fill_one_option_get(skb, team, opt_inst);
24918c2ecf20Sopenharmony_ci		if (err) {
24928c2ecf20Sopenharmony_ci			if (err == -EMSGSIZE) {
24938c2ecf20Sopenharmony_ci				if (!i)
24948c2ecf20Sopenharmony_ci					goto errout;
24958c2ecf20Sopenharmony_ci				incomplete = true;
24968c2ecf20Sopenharmony_ci				break;
24978c2ecf20Sopenharmony_ci			}
24988c2ecf20Sopenharmony_ci			goto errout;
24998c2ecf20Sopenharmony_ci		}
25008c2ecf20Sopenharmony_ci		i++;
25018c2ecf20Sopenharmony_ci	}
25028c2ecf20Sopenharmony_ci
25038c2ecf20Sopenharmony_ci	nla_nest_end(skb, option_list);
25048c2ecf20Sopenharmony_ci	genlmsg_end(skb, hdr);
25058c2ecf20Sopenharmony_ci	if (incomplete)
25068c2ecf20Sopenharmony_ci		goto start_again;
25078c2ecf20Sopenharmony_ci
25088c2ecf20Sopenharmony_cisend_done:
25098c2ecf20Sopenharmony_ci	nlh = nlmsg_put(skb, portid, seq, NLMSG_DONE, 0, flags | NLM_F_MULTI);
25108c2ecf20Sopenharmony_ci	if (!nlh) {
25118c2ecf20Sopenharmony_ci		err = __send_and_alloc_skb(&skb, team, portid, send_func);
25128c2ecf20Sopenharmony_ci		if (err)
25138c2ecf20Sopenharmony_ci			return err;
25148c2ecf20Sopenharmony_ci		goto send_done;
25158c2ecf20Sopenharmony_ci	}
25168c2ecf20Sopenharmony_ci
25178c2ecf20Sopenharmony_ci	return send_func(skb, team, portid);
25188c2ecf20Sopenharmony_ci
25198c2ecf20Sopenharmony_cinla_put_failure:
25208c2ecf20Sopenharmony_ci	err = -EMSGSIZE;
25218c2ecf20Sopenharmony_cierrout:
25228c2ecf20Sopenharmony_ci	nlmsg_free(skb);
25238c2ecf20Sopenharmony_ci	return err;
25248c2ecf20Sopenharmony_ci}
25258c2ecf20Sopenharmony_ci
25268c2ecf20Sopenharmony_cistatic int team_nl_cmd_options_get(struct sk_buff *skb, struct genl_info *info)
25278c2ecf20Sopenharmony_ci{
25288c2ecf20Sopenharmony_ci	struct team *team;
25298c2ecf20Sopenharmony_ci	struct team_option_inst *opt_inst;
25308c2ecf20Sopenharmony_ci	int err;
25318c2ecf20Sopenharmony_ci	LIST_HEAD(sel_opt_inst_list);
25328c2ecf20Sopenharmony_ci
25338c2ecf20Sopenharmony_ci	team = team_nl_team_get(info);
25348c2ecf20Sopenharmony_ci	if (!team)
25358c2ecf20Sopenharmony_ci		return -EINVAL;
25368c2ecf20Sopenharmony_ci
25378c2ecf20Sopenharmony_ci	list_for_each_entry(opt_inst, &team->option_inst_list, list)
25388c2ecf20Sopenharmony_ci		list_add_tail(&opt_inst->tmp_list, &sel_opt_inst_list);
25398c2ecf20Sopenharmony_ci	err = team_nl_send_options_get(team, info->snd_portid, info->snd_seq,
25408c2ecf20Sopenharmony_ci				       NLM_F_ACK, team_nl_send_unicast,
25418c2ecf20Sopenharmony_ci				       &sel_opt_inst_list);
25428c2ecf20Sopenharmony_ci
25438c2ecf20Sopenharmony_ci	team_nl_team_put(team);
25448c2ecf20Sopenharmony_ci
25458c2ecf20Sopenharmony_ci	return err;
25468c2ecf20Sopenharmony_ci}
25478c2ecf20Sopenharmony_ci
25488c2ecf20Sopenharmony_cistatic int team_nl_send_event_options_get(struct team *team,
25498c2ecf20Sopenharmony_ci					  struct list_head *sel_opt_inst_list);
25508c2ecf20Sopenharmony_ci
25518c2ecf20Sopenharmony_cistatic int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info)
25528c2ecf20Sopenharmony_ci{
25538c2ecf20Sopenharmony_ci	struct team *team;
25548c2ecf20Sopenharmony_ci	int err = 0;
25558c2ecf20Sopenharmony_ci	int i;
25568c2ecf20Sopenharmony_ci	struct nlattr *nl_option;
25578c2ecf20Sopenharmony_ci
25588c2ecf20Sopenharmony_ci	rtnl_lock();
25598c2ecf20Sopenharmony_ci
25608c2ecf20Sopenharmony_ci	team = team_nl_team_get(info);
25618c2ecf20Sopenharmony_ci	if (!team) {
25628c2ecf20Sopenharmony_ci		err = -EINVAL;
25638c2ecf20Sopenharmony_ci		goto rtnl_unlock;
25648c2ecf20Sopenharmony_ci	}
25658c2ecf20Sopenharmony_ci
25668c2ecf20Sopenharmony_ci	err = -EINVAL;
25678c2ecf20Sopenharmony_ci	if (!info->attrs[TEAM_ATTR_LIST_OPTION]) {
25688c2ecf20Sopenharmony_ci		err = -EINVAL;
25698c2ecf20Sopenharmony_ci		goto team_put;
25708c2ecf20Sopenharmony_ci	}
25718c2ecf20Sopenharmony_ci
25728c2ecf20Sopenharmony_ci	nla_for_each_nested(nl_option, info->attrs[TEAM_ATTR_LIST_OPTION], i) {
25738c2ecf20Sopenharmony_ci		struct nlattr *opt_attrs[TEAM_ATTR_OPTION_MAX + 1];
25748c2ecf20Sopenharmony_ci		struct nlattr *attr;
25758c2ecf20Sopenharmony_ci		struct nlattr *attr_data;
25768c2ecf20Sopenharmony_ci		LIST_HEAD(opt_inst_list);
25778c2ecf20Sopenharmony_ci		enum team_option_type opt_type;
25788c2ecf20Sopenharmony_ci		int opt_port_ifindex = 0; /* != 0 for per-port options */
25798c2ecf20Sopenharmony_ci		u32 opt_array_index = 0;
25808c2ecf20Sopenharmony_ci		bool opt_is_array = false;
25818c2ecf20Sopenharmony_ci		struct team_option_inst *opt_inst;
25828c2ecf20Sopenharmony_ci		char *opt_name;
25838c2ecf20Sopenharmony_ci		bool opt_found = false;
25848c2ecf20Sopenharmony_ci
25858c2ecf20Sopenharmony_ci		if (nla_type(nl_option) != TEAM_ATTR_ITEM_OPTION) {
25868c2ecf20Sopenharmony_ci			err = -EINVAL;
25878c2ecf20Sopenharmony_ci			goto team_put;
25888c2ecf20Sopenharmony_ci		}
25898c2ecf20Sopenharmony_ci		err = nla_parse_nested_deprecated(opt_attrs,
25908c2ecf20Sopenharmony_ci						  TEAM_ATTR_OPTION_MAX,
25918c2ecf20Sopenharmony_ci						  nl_option,
25928c2ecf20Sopenharmony_ci						  team_nl_option_policy,
25938c2ecf20Sopenharmony_ci						  info->extack);
25948c2ecf20Sopenharmony_ci		if (err)
25958c2ecf20Sopenharmony_ci			goto team_put;
25968c2ecf20Sopenharmony_ci		if (!opt_attrs[TEAM_ATTR_OPTION_NAME] ||
25978c2ecf20Sopenharmony_ci		    !opt_attrs[TEAM_ATTR_OPTION_TYPE]) {
25988c2ecf20Sopenharmony_ci			err = -EINVAL;
25998c2ecf20Sopenharmony_ci			goto team_put;
26008c2ecf20Sopenharmony_ci		}
26018c2ecf20Sopenharmony_ci		switch (nla_get_u8(opt_attrs[TEAM_ATTR_OPTION_TYPE])) {
26028c2ecf20Sopenharmony_ci		case NLA_U32:
26038c2ecf20Sopenharmony_ci			opt_type = TEAM_OPTION_TYPE_U32;
26048c2ecf20Sopenharmony_ci			break;
26058c2ecf20Sopenharmony_ci		case NLA_STRING:
26068c2ecf20Sopenharmony_ci			opt_type = TEAM_OPTION_TYPE_STRING;
26078c2ecf20Sopenharmony_ci			break;
26088c2ecf20Sopenharmony_ci		case NLA_BINARY:
26098c2ecf20Sopenharmony_ci			opt_type = TEAM_OPTION_TYPE_BINARY;
26108c2ecf20Sopenharmony_ci			break;
26118c2ecf20Sopenharmony_ci		case NLA_FLAG:
26128c2ecf20Sopenharmony_ci			opt_type = TEAM_OPTION_TYPE_BOOL;
26138c2ecf20Sopenharmony_ci			break;
26148c2ecf20Sopenharmony_ci		case NLA_S32:
26158c2ecf20Sopenharmony_ci			opt_type = TEAM_OPTION_TYPE_S32;
26168c2ecf20Sopenharmony_ci			break;
26178c2ecf20Sopenharmony_ci		default:
26188c2ecf20Sopenharmony_ci			goto team_put;
26198c2ecf20Sopenharmony_ci		}
26208c2ecf20Sopenharmony_ci
26218c2ecf20Sopenharmony_ci		attr_data = opt_attrs[TEAM_ATTR_OPTION_DATA];
26228c2ecf20Sopenharmony_ci		if (opt_type != TEAM_OPTION_TYPE_BOOL && !attr_data) {
26238c2ecf20Sopenharmony_ci			err = -EINVAL;
26248c2ecf20Sopenharmony_ci			goto team_put;
26258c2ecf20Sopenharmony_ci		}
26268c2ecf20Sopenharmony_ci
26278c2ecf20Sopenharmony_ci		opt_name = nla_data(opt_attrs[TEAM_ATTR_OPTION_NAME]);
26288c2ecf20Sopenharmony_ci		attr = opt_attrs[TEAM_ATTR_OPTION_PORT_IFINDEX];
26298c2ecf20Sopenharmony_ci		if (attr)
26308c2ecf20Sopenharmony_ci			opt_port_ifindex = nla_get_u32(attr);
26318c2ecf20Sopenharmony_ci
26328c2ecf20Sopenharmony_ci		attr = opt_attrs[TEAM_ATTR_OPTION_ARRAY_INDEX];
26338c2ecf20Sopenharmony_ci		if (attr) {
26348c2ecf20Sopenharmony_ci			opt_is_array = true;
26358c2ecf20Sopenharmony_ci			opt_array_index = nla_get_u32(attr);
26368c2ecf20Sopenharmony_ci		}
26378c2ecf20Sopenharmony_ci
26388c2ecf20Sopenharmony_ci		list_for_each_entry(opt_inst, &team->option_inst_list, list) {
26398c2ecf20Sopenharmony_ci			struct team_option *option = opt_inst->option;
26408c2ecf20Sopenharmony_ci			struct team_gsetter_ctx ctx;
26418c2ecf20Sopenharmony_ci			struct team_option_inst_info *opt_inst_info;
26428c2ecf20Sopenharmony_ci			int tmp_ifindex;
26438c2ecf20Sopenharmony_ci
26448c2ecf20Sopenharmony_ci			opt_inst_info = &opt_inst->info;
26458c2ecf20Sopenharmony_ci			tmp_ifindex = opt_inst_info->port ?
26468c2ecf20Sopenharmony_ci				      opt_inst_info->port->dev->ifindex : 0;
26478c2ecf20Sopenharmony_ci			if (option->type != opt_type ||
26488c2ecf20Sopenharmony_ci			    strcmp(option->name, opt_name) ||
26498c2ecf20Sopenharmony_ci			    tmp_ifindex != opt_port_ifindex ||
26508c2ecf20Sopenharmony_ci			    (option->array_size && !opt_is_array) ||
26518c2ecf20Sopenharmony_ci			    opt_inst_info->array_index != opt_array_index)
26528c2ecf20Sopenharmony_ci				continue;
26538c2ecf20Sopenharmony_ci			opt_found = true;
26548c2ecf20Sopenharmony_ci			ctx.info = opt_inst_info;
26558c2ecf20Sopenharmony_ci			switch (opt_type) {
26568c2ecf20Sopenharmony_ci			case TEAM_OPTION_TYPE_U32:
26578c2ecf20Sopenharmony_ci				ctx.data.u32_val = nla_get_u32(attr_data);
26588c2ecf20Sopenharmony_ci				break;
26598c2ecf20Sopenharmony_ci			case TEAM_OPTION_TYPE_STRING:
26608c2ecf20Sopenharmony_ci				if (nla_len(attr_data) > TEAM_STRING_MAX_LEN) {
26618c2ecf20Sopenharmony_ci					err = -EINVAL;
26628c2ecf20Sopenharmony_ci					goto team_put;
26638c2ecf20Sopenharmony_ci				}
26648c2ecf20Sopenharmony_ci				ctx.data.str_val = nla_data(attr_data);
26658c2ecf20Sopenharmony_ci				break;
26668c2ecf20Sopenharmony_ci			case TEAM_OPTION_TYPE_BINARY:
26678c2ecf20Sopenharmony_ci				ctx.data.bin_val.len = nla_len(attr_data);
26688c2ecf20Sopenharmony_ci				ctx.data.bin_val.ptr = nla_data(attr_data);
26698c2ecf20Sopenharmony_ci				break;
26708c2ecf20Sopenharmony_ci			case TEAM_OPTION_TYPE_BOOL:
26718c2ecf20Sopenharmony_ci				ctx.data.bool_val = attr_data ? true : false;
26728c2ecf20Sopenharmony_ci				break;
26738c2ecf20Sopenharmony_ci			case TEAM_OPTION_TYPE_S32:
26748c2ecf20Sopenharmony_ci				ctx.data.s32_val = nla_get_s32(attr_data);
26758c2ecf20Sopenharmony_ci				break;
26768c2ecf20Sopenharmony_ci			default:
26778c2ecf20Sopenharmony_ci				BUG();
26788c2ecf20Sopenharmony_ci			}
26798c2ecf20Sopenharmony_ci			err = team_option_set(team, opt_inst, &ctx);
26808c2ecf20Sopenharmony_ci			if (err)
26818c2ecf20Sopenharmony_ci				goto team_put;
26828c2ecf20Sopenharmony_ci			opt_inst->changed = true;
26838c2ecf20Sopenharmony_ci			list_add(&opt_inst->tmp_list, &opt_inst_list);
26848c2ecf20Sopenharmony_ci		}
26858c2ecf20Sopenharmony_ci		if (!opt_found) {
26868c2ecf20Sopenharmony_ci			err = -ENOENT;
26878c2ecf20Sopenharmony_ci			goto team_put;
26888c2ecf20Sopenharmony_ci		}
26898c2ecf20Sopenharmony_ci
26908c2ecf20Sopenharmony_ci		err = team_nl_send_event_options_get(team, &opt_inst_list);
26918c2ecf20Sopenharmony_ci		if (err)
26928c2ecf20Sopenharmony_ci			break;
26938c2ecf20Sopenharmony_ci	}
26948c2ecf20Sopenharmony_ci
26958c2ecf20Sopenharmony_citeam_put:
26968c2ecf20Sopenharmony_ci	team_nl_team_put(team);
26978c2ecf20Sopenharmony_cirtnl_unlock:
26988c2ecf20Sopenharmony_ci	rtnl_unlock();
26998c2ecf20Sopenharmony_ci	return err;
27008c2ecf20Sopenharmony_ci}
27018c2ecf20Sopenharmony_ci
27028c2ecf20Sopenharmony_cistatic int team_nl_fill_one_port_get(struct sk_buff *skb,
27038c2ecf20Sopenharmony_ci				     struct team_port *port)
27048c2ecf20Sopenharmony_ci{
27058c2ecf20Sopenharmony_ci	struct nlattr *port_item;
27068c2ecf20Sopenharmony_ci
27078c2ecf20Sopenharmony_ci	port_item = nla_nest_start_noflag(skb, TEAM_ATTR_ITEM_PORT);
27088c2ecf20Sopenharmony_ci	if (!port_item)
27098c2ecf20Sopenharmony_ci		goto nest_cancel;
27108c2ecf20Sopenharmony_ci	if (nla_put_u32(skb, TEAM_ATTR_PORT_IFINDEX, port->dev->ifindex))
27118c2ecf20Sopenharmony_ci		goto nest_cancel;
27128c2ecf20Sopenharmony_ci	if (port->changed) {
27138c2ecf20Sopenharmony_ci		if (nla_put_flag(skb, TEAM_ATTR_PORT_CHANGED))
27148c2ecf20Sopenharmony_ci			goto nest_cancel;
27158c2ecf20Sopenharmony_ci		port->changed = false;
27168c2ecf20Sopenharmony_ci	}
27178c2ecf20Sopenharmony_ci	if ((port->removed &&
27188c2ecf20Sopenharmony_ci	     nla_put_flag(skb, TEAM_ATTR_PORT_REMOVED)) ||
27198c2ecf20Sopenharmony_ci	    (port->state.linkup &&
27208c2ecf20Sopenharmony_ci	     nla_put_flag(skb, TEAM_ATTR_PORT_LINKUP)) ||
27218c2ecf20Sopenharmony_ci	    nla_put_u32(skb, TEAM_ATTR_PORT_SPEED, port->state.speed) ||
27228c2ecf20Sopenharmony_ci	    nla_put_u8(skb, TEAM_ATTR_PORT_DUPLEX, port->state.duplex))
27238c2ecf20Sopenharmony_ci		goto nest_cancel;
27248c2ecf20Sopenharmony_ci	nla_nest_end(skb, port_item);
27258c2ecf20Sopenharmony_ci	return 0;
27268c2ecf20Sopenharmony_ci
27278c2ecf20Sopenharmony_cinest_cancel:
27288c2ecf20Sopenharmony_ci	nla_nest_cancel(skb, port_item);
27298c2ecf20Sopenharmony_ci	return -EMSGSIZE;
27308c2ecf20Sopenharmony_ci}
27318c2ecf20Sopenharmony_ci
27328c2ecf20Sopenharmony_cistatic int team_nl_send_port_list_get(struct team *team, u32 portid, u32 seq,
27338c2ecf20Sopenharmony_ci				      int flags, team_nl_send_func_t *send_func,
27348c2ecf20Sopenharmony_ci				      struct team_port *one_port)
27358c2ecf20Sopenharmony_ci{
27368c2ecf20Sopenharmony_ci	struct nlattr *port_list;
27378c2ecf20Sopenharmony_ci	struct nlmsghdr *nlh;
27388c2ecf20Sopenharmony_ci	void *hdr;
27398c2ecf20Sopenharmony_ci	struct team_port *port;
27408c2ecf20Sopenharmony_ci	int err;
27418c2ecf20Sopenharmony_ci	struct sk_buff *skb = NULL;
27428c2ecf20Sopenharmony_ci	bool incomplete;
27438c2ecf20Sopenharmony_ci	int i;
27448c2ecf20Sopenharmony_ci
27458c2ecf20Sopenharmony_ci	port = list_first_entry_or_null(&team->port_list,
27468c2ecf20Sopenharmony_ci					struct team_port, list);
27478c2ecf20Sopenharmony_ci
27488c2ecf20Sopenharmony_cistart_again:
27498c2ecf20Sopenharmony_ci	err = __send_and_alloc_skb(&skb, team, portid, send_func);
27508c2ecf20Sopenharmony_ci	if (err)
27518c2ecf20Sopenharmony_ci		return err;
27528c2ecf20Sopenharmony_ci
27538c2ecf20Sopenharmony_ci	hdr = genlmsg_put(skb, portid, seq, &team_nl_family, flags | NLM_F_MULTI,
27548c2ecf20Sopenharmony_ci			  TEAM_CMD_PORT_LIST_GET);
27558c2ecf20Sopenharmony_ci	if (!hdr) {
27568c2ecf20Sopenharmony_ci		nlmsg_free(skb);
27578c2ecf20Sopenharmony_ci		return -EMSGSIZE;
27588c2ecf20Sopenharmony_ci	}
27598c2ecf20Sopenharmony_ci
27608c2ecf20Sopenharmony_ci	if (nla_put_u32(skb, TEAM_ATTR_TEAM_IFINDEX, team->dev->ifindex))
27618c2ecf20Sopenharmony_ci		goto nla_put_failure;
27628c2ecf20Sopenharmony_ci	port_list = nla_nest_start_noflag(skb, TEAM_ATTR_LIST_PORT);
27638c2ecf20Sopenharmony_ci	if (!port_list)
27648c2ecf20Sopenharmony_ci		goto nla_put_failure;
27658c2ecf20Sopenharmony_ci
27668c2ecf20Sopenharmony_ci	i = 0;
27678c2ecf20Sopenharmony_ci	incomplete = false;
27688c2ecf20Sopenharmony_ci
27698c2ecf20Sopenharmony_ci	/* If one port is selected, called wants to send port list containing
27708c2ecf20Sopenharmony_ci	 * only this port. Otherwise go through all listed ports and send all
27718c2ecf20Sopenharmony_ci	 */
27728c2ecf20Sopenharmony_ci	if (one_port) {
27738c2ecf20Sopenharmony_ci		err = team_nl_fill_one_port_get(skb, one_port);
27748c2ecf20Sopenharmony_ci		if (err)
27758c2ecf20Sopenharmony_ci			goto errout;
27768c2ecf20Sopenharmony_ci	} else if (port) {
27778c2ecf20Sopenharmony_ci		list_for_each_entry_from(port, &team->port_list, list) {
27788c2ecf20Sopenharmony_ci			err = team_nl_fill_one_port_get(skb, port);
27798c2ecf20Sopenharmony_ci			if (err) {
27808c2ecf20Sopenharmony_ci				if (err == -EMSGSIZE) {
27818c2ecf20Sopenharmony_ci					if (!i)
27828c2ecf20Sopenharmony_ci						goto errout;
27838c2ecf20Sopenharmony_ci					incomplete = true;
27848c2ecf20Sopenharmony_ci					break;
27858c2ecf20Sopenharmony_ci				}
27868c2ecf20Sopenharmony_ci				goto errout;
27878c2ecf20Sopenharmony_ci			}
27888c2ecf20Sopenharmony_ci			i++;
27898c2ecf20Sopenharmony_ci		}
27908c2ecf20Sopenharmony_ci	}
27918c2ecf20Sopenharmony_ci
27928c2ecf20Sopenharmony_ci	nla_nest_end(skb, port_list);
27938c2ecf20Sopenharmony_ci	genlmsg_end(skb, hdr);
27948c2ecf20Sopenharmony_ci	if (incomplete)
27958c2ecf20Sopenharmony_ci		goto start_again;
27968c2ecf20Sopenharmony_ci
27978c2ecf20Sopenharmony_cisend_done:
27988c2ecf20Sopenharmony_ci	nlh = nlmsg_put(skb, portid, seq, NLMSG_DONE, 0, flags | NLM_F_MULTI);
27998c2ecf20Sopenharmony_ci	if (!nlh) {
28008c2ecf20Sopenharmony_ci		err = __send_and_alloc_skb(&skb, team, portid, send_func);
28018c2ecf20Sopenharmony_ci		if (err)
28028c2ecf20Sopenharmony_ci			return err;
28038c2ecf20Sopenharmony_ci		goto send_done;
28048c2ecf20Sopenharmony_ci	}
28058c2ecf20Sopenharmony_ci
28068c2ecf20Sopenharmony_ci	return send_func(skb, team, portid);
28078c2ecf20Sopenharmony_ci
28088c2ecf20Sopenharmony_cinla_put_failure:
28098c2ecf20Sopenharmony_ci	err = -EMSGSIZE;
28108c2ecf20Sopenharmony_cierrout:
28118c2ecf20Sopenharmony_ci	nlmsg_free(skb);
28128c2ecf20Sopenharmony_ci	return err;
28138c2ecf20Sopenharmony_ci}
28148c2ecf20Sopenharmony_ci
28158c2ecf20Sopenharmony_cistatic int team_nl_cmd_port_list_get(struct sk_buff *skb,
28168c2ecf20Sopenharmony_ci				     struct genl_info *info)
28178c2ecf20Sopenharmony_ci{
28188c2ecf20Sopenharmony_ci	struct team *team;
28198c2ecf20Sopenharmony_ci	int err;
28208c2ecf20Sopenharmony_ci
28218c2ecf20Sopenharmony_ci	team = team_nl_team_get(info);
28228c2ecf20Sopenharmony_ci	if (!team)
28238c2ecf20Sopenharmony_ci		return -EINVAL;
28248c2ecf20Sopenharmony_ci
28258c2ecf20Sopenharmony_ci	err = team_nl_send_port_list_get(team, info->snd_portid, info->snd_seq,
28268c2ecf20Sopenharmony_ci					 NLM_F_ACK, team_nl_send_unicast, NULL);
28278c2ecf20Sopenharmony_ci
28288c2ecf20Sopenharmony_ci	team_nl_team_put(team);
28298c2ecf20Sopenharmony_ci
28308c2ecf20Sopenharmony_ci	return err;
28318c2ecf20Sopenharmony_ci}
28328c2ecf20Sopenharmony_ci
28338c2ecf20Sopenharmony_cistatic const struct genl_small_ops team_nl_ops[] = {
28348c2ecf20Sopenharmony_ci	{
28358c2ecf20Sopenharmony_ci		.cmd = TEAM_CMD_NOOP,
28368c2ecf20Sopenharmony_ci		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
28378c2ecf20Sopenharmony_ci		.doit = team_nl_cmd_noop,
28388c2ecf20Sopenharmony_ci	},
28398c2ecf20Sopenharmony_ci	{
28408c2ecf20Sopenharmony_ci		.cmd = TEAM_CMD_OPTIONS_SET,
28418c2ecf20Sopenharmony_ci		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
28428c2ecf20Sopenharmony_ci		.doit = team_nl_cmd_options_set,
28438c2ecf20Sopenharmony_ci		.flags = GENL_ADMIN_PERM,
28448c2ecf20Sopenharmony_ci	},
28458c2ecf20Sopenharmony_ci	{
28468c2ecf20Sopenharmony_ci		.cmd = TEAM_CMD_OPTIONS_GET,
28478c2ecf20Sopenharmony_ci		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
28488c2ecf20Sopenharmony_ci		.doit = team_nl_cmd_options_get,
28498c2ecf20Sopenharmony_ci		.flags = GENL_ADMIN_PERM,
28508c2ecf20Sopenharmony_ci	},
28518c2ecf20Sopenharmony_ci	{
28528c2ecf20Sopenharmony_ci		.cmd = TEAM_CMD_PORT_LIST_GET,
28538c2ecf20Sopenharmony_ci		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
28548c2ecf20Sopenharmony_ci		.doit = team_nl_cmd_port_list_get,
28558c2ecf20Sopenharmony_ci		.flags = GENL_ADMIN_PERM,
28568c2ecf20Sopenharmony_ci	},
28578c2ecf20Sopenharmony_ci};
28588c2ecf20Sopenharmony_ci
28598c2ecf20Sopenharmony_cistatic const struct genl_multicast_group team_nl_mcgrps[] = {
28608c2ecf20Sopenharmony_ci	{ .name = TEAM_GENL_CHANGE_EVENT_MC_GRP_NAME, },
28618c2ecf20Sopenharmony_ci};
28628c2ecf20Sopenharmony_ci
28638c2ecf20Sopenharmony_cistatic struct genl_family team_nl_family __ro_after_init = {
28648c2ecf20Sopenharmony_ci	.name		= TEAM_GENL_NAME,
28658c2ecf20Sopenharmony_ci	.version	= TEAM_GENL_VERSION,
28668c2ecf20Sopenharmony_ci	.maxattr	= TEAM_ATTR_MAX,
28678c2ecf20Sopenharmony_ci	.policy = team_nl_policy,
28688c2ecf20Sopenharmony_ci	.netnsok	= true,
28698c2ecf20Sopenharmony_ci	.module		= THIS_MODULE,
28708c2ecf20Sopenharmony_ci	.small_ops	= team_nl_ops,
28718c2ecf20Sopenharmony_ci	.n_small_ops	= ARRAY_SIZE(team_nl_ops),
28728c2ecf20Sopenharmony_ci	.mcgrps		= team_nl_mcgrps,
28738c2ecf20Sopenharmony_ci	.n_mcgrps	= ARRAY_SIZE(team_nl_mcgrps),
28748c2ecf20Sopenharmony_ci};
28758c2ecf20Sopenharmony_ci
28768c2ecf20Sopenharmony_cistatic int team_nl_send_multicast(struct sk_buff *skb,
28778c2ecf20Sopenharmony_ci				  struct team *team, u32 portid)
28788c2ecf20Sopenharmony_ci{
28798c2ecf20Sopenharmony_ci	return genlmsg_multicast_netns(&team_nl_family, dev_net(team->dev),
28808c2ecf20Sopenharmony_ci				       skb, 0, 0, GFP_KERNEL);
28818c2ecf20Sopenharmony_ci}
28828c2ecf20Sopenharmony_ci
28838c2ecf20Sopenharmony_cistatic int team_nl_send_event_options_get(struct team *team,
28848c2ecf20Sopenharmony_ci					  struct list_head *sel_opt_inst_list)
28858c2ecf20Sopenharmony_ci{
28868c2ecf20Sopenharmony_ci	return team_nl_send_options_get(team, 0, 0, 0, team_nl_send_multicast,
28878c2ecf20Sopenharmony_ci					sel_opt_inst_list);
28888c2ecf20Sopenharmony_ci}
28898c2ecf20Sopenharmony_ci
28908c2ecf20Sopenharmony_cistatic int team_nl_send_event_port_get(struct team *team,
28918c2ecf20Sopenharmony_ci				       struct team_port *port)
28928c2ecf20Sopenharmony_ci{
28938c2ecf20Sopenharmony_ci	return team_nl_send_port_list_get(team, 0, 0, 0, team_nl_send_multicast,
28948c2ecf20Sopenharmony_ci					  port);
28958c2ecf20Sopenharmony_ci}
28968c2ecf20Sopenharmony_ci
28978c2ecf20Sopenharmony_cistatic int __init team_nl_init(void)
28988c2ecf20Sopenharmony_ci{
28998c2ecf20Sopenharmony_ci	return genl_register_family(&team_nl_family);
29008c2ecf20Sopenharmony_ci}
29018c2ecf20Sopenharmony_ci
29028c2ecf20Sopenharmony_cistatic void team_nl_fini(void)
29038c2ecf20Sopenharmony_ci{
29048c2ecf20Sopenharmony_ci	genl_unregister_family(&team_nl_family);
29058c2ecf20Sopenharmony_ci}
29068c2ecf20Sopenharmony_ci
29078c2ecf20Sopenharmony_ci
29088c2ecf20Sopenharmony_ci/******************
29098c2ecf20Sopenharmony_ci * Change checkers
29108c2ecf20Sopenharmony_ci ******************/
29118c2ecf20Sopenharmony_ci
29128c2ecf20Sopenharmony_cistatic void __team_options_change_check(struct team *team)
29138c2ecf20Sopenharmony_ci{
29148c2ecf20Sopenharmony_ci	int err;
29158c2ecf20Sopenharmony_ci	struct team_option_inst *opt_inst;
29168c2ecf20Sopenharmony_ci	LIST_HEAD(sel_opt_inst_list);
29178c2ecf20Sopenharmony_ci
29188c2ecf20Sopenharmony_ci	list_for_each_entry(opt_inst, &team->option_inst_list, list) {
29198c2ecf20Sopenharmony_ci		if (opt_inst->changed)
29208c2ecf20Sopenharmony_ci			list_add_tail(&opt_inst->tmp_list, &sel_opt_inst_list);
29218c2ecf20Sopenharmony_ci	}
29228c2ecf20Sopenharmony_ci	err = team_nl_send_event_options_get(team, &sel_opt_inst_list);
29238c2ecf20Sopenharmony_ci	if (err && err != -ESRCH)
29248c2ecf20Sopenharmony_ci		netdev_warn(team->dev, "Failed to send options change via netlink (err %d)\n",
29258c2ecf20Sopenharmony_ci			    err);
29268c2ecf20Sopenharmony_ci}
29278c2ecf20Sopenharmony_ci
29288c2ecf20Sopenharmony_ci/* rtnl lock is held */
29298c2ecf20Sopenharmony_ci
29308c2ecf20Sopenharmony_cistatic void __team_port_change_send(struct team_port *port, bool linkup)
29318c2ecf20Sopenharmony_ci{
29328c2ecf20Sopenharmony_ci	int err;
29338c2ecf20Sopenharmony_ci
29348c2ecf20Sopenharmony_ci	port->changed = true;
29358c2ecf20Sopenharmony_ci	port->state.linkup = linkup;
29368c2ecf20Sopenharmony_ci	team_refresh_port_linkup(port);
29378c2ecf20Sopenharmony_ci	if (linkup) {
29388c2ecf20Sopenharmony_ci		struct ethtool_link_ksettings ecmd;
29398c2ecf20Sopenharmony_ci
29408c2ecf20Sopenharmony_ci		err = __ethtool_get_link_ksettings(port->dev, &ecmd);
29418c2ecf20Sopenharmony_ci		if (!err) {
29428c2ecf20Sopenharmony_ci			port->state.speed = ecmd.base.speed;
29438c2ecf20Sopenharmony_ci			port->state.duplex = ecmd.base.duplex;
29448c2ecf20Sopenharmony_ci			goto send_event;
29458c2ecf20Sopenharmony_ci		}
29468c2ecf20Sopenharmony_ci	}
29478c2ecf20Sopenharmony_ci	port->state.speed = 0;
29488c2ecf20Sopenharmony_ci	port->state.duplex = 0;
29498c2ecf20Sopenharmony_ci
29508c2ecf20Sopenharmony_cisend_event:
29518c2ecf20Sopenharmony_ci	err = team_nl_send_event_port_get(port->team, port);
29528c2ecf20Sopenharmony_ci	if (err && err != -ESRCH)
29538c2ecf20Sopenharmony_ci		netdev_warn(port->team->dev, "Failed to send port change of device %s via netlink (err %d)\n",
29548c2ecf20Sopenharmony_ci			    port->dev->name, err);
29558c2ecf20Sopenharmony_ci
29568c2ecf20Sopenharmony_ci}
29578c2ecf20Sopenharmony_ci
29588c2ecf20Sopenharmony_cistatic void __team_carrier_check(struct team *team)
29598c2ecf20Sopenharmony_ci{
29608c2ecf20Sopenharmony_ci	struct team_port *port;
29618c2ecf20Sopenharmony_ci	bool team_linkup;
29628c2ecf20Sopenharmony_ci
29638c2ecf20Sopenharmony_ci	if (team->user_carrier_enabled)
29648c2ecf20Sopenharmony_ci		return;
29658c2ecf20Sopenharmony_ci
29668c2ecf20Sopenharmony_ci	team_linkup = false;
29678c2ecf20Sopenharmony_ci	list_for_each_entry(port, &team->port_list, list) {
29688c2ecf20Sopenharmony_ci		if (port->linkup) {
29698c2ecf20Sopenharmony_ci			team_linkup = true;
29708c2ecf20Sopenharmony_ci			break;
29718c2ecf20Sopenharmony_ci		}
29728c2ecf20Sopenharmony_ci	}
29738c2ecf20Sopenharmony_ci
29748c2ecf20Sopenharmony_ci	if (team_linkup)
29758c2ecf20Sopenharmony_ci		netif_carrier_on(team->dev);
29768c2ecf20Sopenharmony_ci	else
29778c2ecf20Sopenharmony_ci		netif_carrier_off(team->dev);
29788c2ecf20Sopenharmony_ci}
29798c2ecf20Sopenharmony_ci
29808c2ecf20Sopenharmony_cistatic void __team_port_change_check(struct team_port *port, bool linkup)
29818c2ecf20Sopenharmony_ci{
29828c2ecf20Sopenharmony_ci	if (port->state.linkup != linkup)
29838c2ecf20Sopenharmony_ci		__team_port_change_send(port, linkup);
29848c2ecf20Sopenharmony_ci	__team_carrier_check(port->team);
29858c2ecf20Sopenharmony_ci}
29868c2ecf20Sopenharmony_ci
29878c2ecf20Sopenharmony_cistatic void __team_port_change_port_added(struct team_port *port, bool linkup)
29888c2ecf20Sopenharmony_ci{
29898c2ecf20Sopenharmony_ci	__team_port_change_send(port, linkup);
29908c2ecf20Sopenharmony_ci	__team_carrier_check(port->team);
29918c2ecf20Sopenharmony_ci}
29928c2ecf20Sopenharmony_ci
29938c2ecf20Sopenharmony_cistatic void __team_port_change_port_removed(struct team_port *port)
29948c2ecf20Sopenharmony_ci{
29958c2ecf20Sopenharmony_ci	port->removed = true;
29968c2ecf20Sopenharmony_ci	__team_port_change_send(port, false);
29978c2ecf20Sopenharmony_ci	__team_carrier_check(port->team);
29988c2ecf20Sopenharmony_ci}
29998c2ecf20Sopenharmony_ci
30008c2ecf20Sopenharmony_cistatic void team_port_change_check(struct team_port *port, bool linkup)
30018c2ecf20Sopenharmony_ci{
30028c2ecf20Sopenharmony_ci	struct team *team = port->team;
30038c2ecf20Sopenharmony_ci
30048c2ecf20Sopenharmony_ci	mutex_lock(&team->lock);
30058c2ecf20Sopenharmony_ci	__team_port_change_check(port, linkup);
30068c2ecf20Sopenharmony_ci	mutex_unlock(&team->lock);
30078c2ecf20Sopenharmony_ci}
30088c2ecf20Sopenharmony_ci
30098c2ecf20Sopenharmony_ci
30108c2ecf20Sopenharmony_ci/************************************
30118c2ecf20Sopenharmony_ci * Net device notifier event handler
30128c2ecf20Sopenharmony_ci ************************************/
30138c2ecf20Sopenharmony_ci
30148c2ecf20Sopenharmony_cistatic int team_device_event(struct notifier_block *unused,
30158c2ecf20Sopenharmony_ci			     unsigned long event, void *ptr)
30168c2ecf20Sopenharmony_ci{
30178c2ecf20Sopenharmony_ci	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
30188c2ecf20Sopenharmony_ci	struct team_port *port;
30198c2ecf20Sopenharmony_ci
30208c2ecf20Sopenharmony_ci	port = team_port_get_rtnl(dev);
30218c2ecf20Sopenharmony_ci	if (!port)
30228c2ecf20Sopenharmony_ci		return NOTIFY_DONE;
30238c2ecf20Sopenharmony_ci
30248c2ecf20Sopenharmony_ci	switch (event) {
30258c2ecf20Sopenharmony_ci	case NETDEV_UP:
30268c2ecf20Sopenharmony_ci		if (netif_oper_up(dev))
30278c2ecf20Sopenharmony_ci			team_port_change_check(port, true);
30288c2ecf20Sopenharmony_ci		break;
30298c2ecf20Sopenharmony_ci	case NETDEV_DOWN:
30308c2ecf20Sopenharmony_ci		team_port_change_check(port, false);
30318c2ecf20Sopenharmony_ci		break;
30328c2ecf20Sopenharmony_ci	case NETDEV_CHANGE:
30338c2ecf20Sopenharmony_ci		if (netif_running(port->dev))
30348c2ecf20Sopenharmony_ci			team_port_change_check(port,
30358c2ecf20Sopenharmony_ci					       !!netif_oper_up(port->dev));
30368c2ecf20Sopenharmony_ci		break;
30378c2ecf20Sopenharmony_ci	case NETDEV_UNREGISTER:
30388c2ecf20Sopenharmony_ci		team_del_slave(port->team->dev, dev);
30398c2ecf20Sopenharmony_ci		break;
30408c2ecf20Sopenharmony_ci	case NETDEV_FEAT_CHANGE:
30418c2ecf20Sopenharmony_ci		if (!port->team->notifier_ctx) {
30428c2ecf20Sopenharmony_ci			port->team->notifier_ctx = true;
30438c2ecf20Sopenharmony_ci			team_compute_features(port->team);
30448c2ecf20Sopenharmony_ci			port->team->notifier_ctx = false;
30458c2ecf20Sopenharmony_ci		}
30468c2ecf20Sopenharmony_ci		break;
30478c2ecf20Sopenharmony_ci	case NETDEV_PRECHANGEMTU:
30488c2ecf20Sopenharmony_ci		/* Forbid to change mtu of underlaying device */
30498c2ecf20Sopenharmony_ci		if (!port->team->port_mtu_change_allowed)
30508c2ecf20Sopenharmony_ci			return NOTIFY_BAD;
30518c2ecf20Sopenharmony_ci		break;
30528c2ecf20Sopenharmony_ci	case NETDEV_PRE_TYPE_CHANGE:
30538c2ecf20Sopenharmony_ci		/* Forbid to change type of underlaying device */
30548c2ecf20Sopenharmony_ci		return NOTIFY_BAD;
30558c2ecf20Sopenharmony_ci	case NETDEV_RESEND_IGMP:
30568c2ecf20Sopenharmony_ci		/* Propagate to master device */
30578c2ecf20Sopenharmony_ci		call_netdevice_notifiers(event, port->team->dev);
30588c2ecf20Sopenharmony_ci		break;
30598c2ecf20Sopenharmony_ci	}
30608c2ecf20Sopenharmony_ci	return NOTIFY_DONE;
30618c2ecf20Sopenharmony_ci}
30628c2ecf20Sopenharmony_ci
30638c2ecf20Sopenharmony_cistatic struct notifier_block team_notifier_block __read_mostly = {
30648c2ecf20Sopenharmony_ci	.notifier_call = team_device_event,
30658c2ecf20Sopenharmony_ci};
30668c2ecf20Sopenharmony_ci
30678c2ecf20Sopenharmony_ci
30688c2ecf20Sopenharmony_ci/***********************
30698c2ecf20Sopenharmony_ci * Module init and exit
30708c2ecf20Sopenharmony_ci ***********************/
30718c2ecf20Sopenharmony_ci
30728c2ecf20Sopenharmony_cistatic int __init team_module_init(void)
30738c2ecf20Sopenharmony_ci{
30748c2ecf20Sopenharmony_ci	int err;
30758c2ecf20Sopenharmony_ci
30768c2ecf20Sopenharmony_ci	register_netdevice_notifier(&team_notifier_block);
30778c2ecf20Sopenharmony_ci
30788c2ecf20Sopenharmony_ci	err = rtnl_link_register(&team_link_ops);
30798c2ecf20Sopenharmony_ci	if (err)
30808c2ecf20Sopenharmony_ci		goto err_rtnl_reg;
30818c2ecf20Sopenharmony_ci
30828c2ecf20Sopenharmony_ci	err = team_nl_init();
30838c2ecf20Sopenharmony_ci	if (err)
30848c2ecf20Sopenharmony_ci		goto err_nl_init;
30858c2ecf20Sopenharmony_ci
30868c2ecf20Sopenharmony_ci	return 0;
30878c2ecf20Sopenharmony_ci
30888c2ecf20Sopenharmony_cierr_nl_init:
30898c2ecf20Sopenharmony_ci	rtnl_link_unregister(&team_link_ops);
30908c2ecf20Sopenharmony_ci
30918c2ecf20Sopenharmony_cierr_rtnl_reg:
30928c2ecf20Sopenharmony_ci	unregister_netdevice_notifier(&team_notifier_block);
30938c2ecf20Sopenharmony_ci
30948c2ecf20Sopenharmony_ci	return err;
30958c2ecf20Sopenharmony_ci}
30968c2ecf20Sopenharmony_ci
30978c2ecf20Sopenharmony_cistatic void __exit team_module_exit(void)
30988c2ecf20Sopenharmony_ci{
30998c2ecf20Sopenharmony_ci	team_nl_fini();
31008c2ecf20Sopenharmony_ci	rtnl_link_unregister(&team_link_ops);
31018c2ecf20Sopenharmony_ci	unregister_netdevice_notifier(&team_notifier_block);
31028c2ecf20Sopenharmony_ci}
31038c2ecf20Sopenharmony_ci
31048c2ecf20Sopenharmony_cimodule_init(team_module_init);
31058c2ecf20Sopenharmony_cimodule_exit(team_module_exit);
31068c2ecf20Sopenharmony_ci
31078c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
31088c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jiri Pirko <jpirko@redhat.com>");
31098c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Ethernet team device driver");
31108c2ecf20Sopenharmony_ciMODULE_ALIAS_RTNL_LINK(DRV_NAME);
3111