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