| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * drivers/net/team/team.c - Network team device driver |
|---|
| 3 | 4 | * Copyright (c) 2011 Jiri Pirko <jpirko@redhat.com> |
|---|
| 4 | | - * |
|---|
| 5 | | - * This program is free software; you can redistribute it and/or modify |
|---|
| 6 | | - * it under the terms of the GNU General Public License as published by |
|---|
| 7 | | - * the Free Software Foundation; either version 2 of the License, or |
|---|
| 8 | | - * (at your option) any later version. |
|---|
| 9 | 5 | */ |
|---|
| 10 | 6 | |
|---|
| 11 | 7 | #include <linux/kernel.h> |
|---|
| .. | .. |
|---|
| 28 | 24 | #include <net/genetlink.h> |
|---|
| 29 | 25 | #include <net/netlink.h> |
|---|
| 30 | 26 | #include <net/sch_generic.h> |
|---|
| 31 | | -#include <net/switchdev.h> |
|---|
| 32 | 27 | #include <generated/utsrelease.h> |
|---|
| 33 | 28 | #include <linux/if_team.h> |
|---|
| 34 | 29 | |
|---|
| .. | .. |
|---|
| 39 | 34 | * Helpers |
|---|
| 40 | 35 | **********/ |
|---|
| 41 | 36 | |
|---|
| 42 | | -#define team_port_exists(dev) (dev->priv_flags & IFF_TEAM_PORT) |
|---|
| 43 | | - |
|---|
| 44 | 37 | static struct team_port *team_port_get_rtnl(const struct net_device *dev) |
|---|
| 45 | 38 | { |
|---|
| 46 | 39 | struct team_port *port = rtnl_dereference(dev->rx_handler_data); |
|---|
| 47 | 40 | |
|---|
| 48 | | - return team_port_exists(dev) ? port : NULL; |
|---|
| 41 | + return netif_is_team_port(dev) ? port : NULL; |
|---|
| 49 | 42 | } |
|---|
| 50 | 43 | |
|---|
| 51 | 44 | /* |
|---|
| .. | .. |
|---|
| 59 | 52 | |
|---|
| 60 | 53 | memcpy(addr.__data, dev_addr, port_dev->addr_len); |
|---|
| 61 | 54 | addr.ss_family = port_dev->type; |
|---|
| 62 | | - return dev_set_mac_address(port_dev, (struct sockaddr *)&addr); |
|---|
| 55 | + return dev_set_mac_address(port_dev, (struct sockaddr *)&addr, NULL); |
|---|
| 63 | 56 | } |
|---|
| 64 | 57 | |
|---|
| 65 | 58 | static int team_port_set_orig_dev_addr(struct team_port *port) |
|---|
| .. | .. |
|---|
| 1097 | 1090 | return; |
|---|
| 1098 | 1091 | port->np = NULL; |
|---|
| 1099 | 1092 | |
|---|
| 1100 | | - /* Wait for transmitting packets to finish before freeing. */ |
|---|
| 1101 | | - synchronize_rcu_bh(); |
|---|
| 1102 | | - __netpoll_cleanup(np); |
|---|
| 1103 | | - kfree(np); |
|---|
| 1093 | + __netpoll_free(np); |
|---|
| 1104 | 1094 | } |
|---|
| 1105 | 1095 | #else |
|---|
| 1106 | 1096 | static int team_port_enable_netpoll(struct team_port *port) |
|---|
| .. | .. |
|---|
| 1153 | 1143 | return -EINVAL; |
|---|
| 1154 | 1144 | } |
|---|
| 1155 | 1145 | |
|---|
| 1156 | | - if (team_port_exists(port_dev)) { |
|---|
| 1146 | + if (netif_is_team_port(port_dev)) { |
|---|
| 1157 | 1147 | NL_SET_ERR_MSG(extack, "Device is already a port of a team device"); |
|---|
| 1158 | 1148 | netdev_err(dev, "Device %s is already a port " |
|---|
| 1159 | 1149 | "of a team device\n", portname); |
|---|
| .. | .. |
|---|
| 1217 | 1207 | goto err_port_enter; |
|---|
| 1218 | 1208 | } |
|---|
| 1219 | 1209 | |
|---|
| 1220 | | - err = dev_open(port_dev); |
|---|
| 1210 | + err = dev_open(port_dev, extack); |
|---|
| 1221 | 1211 | if (err) { |
|---|
| 1222 | 1212 | netdev_dbg(dev, "Device %s opening failed\n", |
|---|
| 1223 | 1213 | portname); |
|---|
| .. | .. |
|---|
| 1280 | 1270 | } |
|---|
| 1281 | 1271 | } |
|---|
| 1282 | 1272 | |
|---|
| 1283 | | - netif_addr_lock_bh(dev); |
|---|
| 1284 | | - dev_uc_sync_multiple(port_dev, dev); |
|---|
| 1285 | | - dev_mc_sync_multiple(port_dev, dev); |
|---|
| 1286 | | - netif_addr_unlock_bh(dev); |
|---|
| 1273 | + if (dev->flags & IFF_UP) { |
|---|
| 1274 | + netif_addr_lock_bh(dev); |
|---|
| 1275 | + dev_uc_sync_multiple(port_dev, dev); |
|---|
| 1276 | + dev_mc_sync_multiple(port_dev, dev); |
|---|
| 1277 | + netif_addr_unlock_bh(dev); |
|---|
| 1278 | + } |
|---|
| 1287 | 1279 | |
|---|
| 1288 | 1280 | port->index = -1; |
|---|
| 1289 | 1281 | list_add_tail_rcu(&port->list, &team->port_list); |
|---|
| .. | .. |
|---|
| 1354 | 1346 | netdev_rx_handler_unregister(port_dev); |
|---|
| 1355 | 1347 | team_port_disable_netpoll(port); |
|---|
| 1356 | 1348 | vlan_vids_del_by_dev(port_dev, dev); |
|---|
| 1357 | | - dev_uc_unsync(port_dev, dev); |
|---|
| 1358 | | - dev_mc_unsync(port_dev, dev); |
|---|
| 1349 | + if (dev->flags & IFF_UP) { |
|---|
| 1350 | + dev_uc_unsync(port_dev, dev); |
|---|
| 1351 | + dev_mc_unsync(port_dev, dev); |
|---|
| 1352 | + } |
|---|
| 1359 | 1353 | dev_close(port_dev); |
|---|
| 1360 | 1354 | team_port_leave(team, port); |
|---|
| 1361 | 1355 | |
|---|
| .. | .. |
|---|
| 1629 | 1623 | int err; |
|---|
| 1630 | 1624 | |
|---|
| 1631 | 1625 | team->dev = dev; |
|---|
| 1632 | | - mutex_init(&team->lock); |
|---|
| 1633 | 1626 | team_set_no_mode(team); |
|---|
| 1634 | 1627 | |
|---|
| 1635 | 1628 | team->pcpu_stats = netdev_alloc_pcpu_stats(struct team_pcpu_stats); |
|---|
| .. | .. |
|---|
| 1656 | 1649 | goto err_options_register; |
|---|
| 1657 | 1650 | netif_carrier_off(dev); |
|---|
| 1658 | 1651 | |
|---|
| 1652 | + lockdep_register_key(&team->team_lock_key); |
|---|
| 1653 | + __mutex_init(&team->lock, "team->team_lock_key", &team->team_lock_key); |
|---|
| 1659 | 1654 | netdev_lockdep_set_classes(dev); |
|---|
| 1660 | 1655 | |
|---|
| 1661 | 1656 | return 0; |
|---|
| .. | .. |
|---|
| 1687 | 1682 | team_queue_override_fini(team); |
|---|
| 1688 | 1683 | mutex_unlock(&team->lock); |
|---|
| 1689 | 1684 | netdev_change_features(dev); |
|---|
| 1685 | + lockdep_unregister_key(&team->team_lock_key); |
|---|
| 1690 | 1686 | } |
|---|
| 1691 | 1687 | |
|---|
| 1692 | 1688 | static void team_destructor(struct net_device *dev) |
|---|
| .. | .. |
|---|
| 1703 | 1699 | |
|---|
| 1704 | 1700 | static int team_close(struct net_device *dev) |
|---|
| 1705 | 1701 | { |
|---|
| 1702 | + struct team *team = netdev_priv(dev); |
|---|
| 1703 | + struct team_port *port; |
|---|
| 1704 | + |
|---|
| 1705 | + list_for_each_entry(port, &team->port_list, list) { |
|---|
| 1706 | + dev_uc_unsync(port->dev, dev); |
|---|
| 1707 | + dev_mc_unsync(port->dev, dev); |
|---|
| 1708 | + } |
|---|
| 1709 | + |
|---|
| 1706 | 1710 | return 0; |
|---|
| 1707 | 1711 | } |
|---|
| 1708 | 1712 | |
|---|
| .. | .. |
|---|
| 1734 | 1738 | } |
|---|
| 1735 | 1739 | |
|---|
| 1736 | 1740 | static u16 team_select_queue(struct net_device *dev, struct sk_buff *skb, |
|---|
| 1737 | | - struct net_device *sb_dev, |
|---|
| 1738 | | - select_queue_fallback_t fallback) |
|---|
| 1741 | + struct net_device *sb_dev) |
|---|
| 1739 | 1742 | { |
|---|
| 1740 | 1743 | /* |
|---|
| 1741 | 1744 | * This helper function exists to help dev_pick_tx get the correct |
|---|
| .. | .. |
|---|
| 1991 | 1994 | err = team_port_del(team, port_dev); |
|---|
| 1992 | 1995 | mutex_unlock(&team->lock); |
|---|
| 1993 | 1996 | |
|---|
| 1994 | | - if (!err) |
|---|
| 1995 | | - netdev_change_features(dev); |
|---|
| 1997 | + if (err) |
|---|
| 1998 | + return err; |
|---|
| 1999 | + |
|---|
| 2000 | + if (netif_is_team_master(port_dev)) { |
|---|
| 2001 | + lockdep_unregister_key(&team->team_lock_key); |
|---|
| 2002 | + lockdep_register_key(&team->team_lock_key); |
|---|
| 2003 | + lockdep_set_class(&team->lock, &team->team_lock_key); |
|---|
| 2004 | + } |
|---|
| 2005 | + netdev_change_features(dev); |
|---|
| 1996 | 2006 | |
|---|
| 1997 | 2007 | return err; |
|---|
| 1998 | 2008 | } |
|---|
| .. | .. |
|---|
| 2071 | 2081 | strlcpy(drvinfo->version, UTS_RELEASE, sizeof(drvinfo->version)); |
|---|
| 2072 | 2082 | } |
|---|
| 2073 | 2083 | |
|---|
| 2084 | +static int team_ethtool_get_link_ksettings(struct net_device *dev, |
|---|
| 2085 | + struct ethtool_link_ksettings *cmd) |
|---|
| 2086 | +{ |
|---|
| 2087 | + struct team *team= netdev_priv(dev); |
|---|
| 2088 | + unsigned long speed = 0; |
|---|
| 2089 | + struct team_port *port; |
|---|
| 2090 | + |
|---|
| 2091 | + cmd->base.duplex = DUPLEX_UNKNOWN; |
|---|
| 2092 | + cmd->base.port = PORT_OTHER; |
|---|
| 2093 | + |
|---|
| 2094 | + rcu_read_lock(); |
|---|
| 2095 | + list_for_each_entry_rcu(port, &team->port_list, list) { |
|---|
| 2096 | + if (team_port_txable(port)) { |
|---|
| 2097 | + if (port->state.speed != SPEED_UNKNOWN) |
|---|
| 2098 | + speed += port->state.speed; |
|---|
| 2099 | + if (cmd->base.duplex == DUPLEX_UNKNOWN && |
|---|
| 2100 | + port->state.duplex != DUPLEX_UNKNOWN) |
|---|
| 2101 | + cmd->base.duplex = port->state.duplex; |
|---|
| 2102 | + } |
|---|
| 2103 | + } |
|---|
| 2104 | + rcu_read_unlock(); |
|---|
| 2105 | + |
|---|
| 2106 | + cmd->base.speed = speed ? : SPEED_UNKNOWN; |
|---|
| 2107 | + |
|---|
| 2108 | + return 0; |
|---|
| 2109 | +} |
|---|
| 2110 | + |
|---|
| 2074 | 2111 | static const struct ethtool_ops team_ethtool_ops = { |
|---|
| 2075 | 2112 | .get_drvinfo = team_ethtool_get_drvinfo, |
|---|
| 2076 | 2113 | .get_link = ethtool_op_get_link, |
|---|
| 2114 | + .get_link_ksettings = team_ethtool_get_link_ksettings, |
|---|
| 2077 | 2115 | }; |
|---|
| 2078 | 2116 | |
|---|
| 2079 | 2117 | /*********************** |
|---|
| .. | .. |
|---|
| 2306 | 2344 | if (err) |
|---|
| 2307 | 2345 | return err; |
|---|
| 2308 | 2346 | |
|---|
| 2309 | | - option_item = nla_nest_start(skb, TEAM_ATTR_ITEM_OPTION); |
|---|
| 2347 | + option_item = nla_nest_start_noflag(skb, TEAM_ATTR_ITEM_OPTION); |
|---|
| 2310 | 2348 | if (!option_item) |
|---|
| 2311 | 2349 | return -EMSGSIZE; |
|---|
| 2312 | 2350 | |
|---|
| .. | .. |
|---|
| 2420 | 2458 | |
|---|
| 2421 | 2459 | if (nla_put_u32(skb, TEAM_ATTR_TEAM_IFINDEX, team->dev->ifindex)) |
|---|
| 2422 | 2460 | goto nla_put_failure; |
|---|
| 2423 | | - option_list = nla_nest_start(skb, TEAM_ATTR_LIST_OPTION); |
|---|
| 2461 | + option_list = nla_nest_start_noflag(skb, TEAM_ATTR_LIST_OPTION); |
|---|
| 2424 | 2462 | if (!option_list) |
|---|
| 2425 | 2463 | goto nla_put_failure; |
|---|
| 2426 | 2464 | |
|---|
| .. | .. |
|---|
| 2526 | 2564 | err = -EINVAL; |
|---|
| 2527 | 2565 | goto team_put; |
|---|
| 2528 | 2566 | } |
|---|
| 2529 | | - err = nla_parse_nested(opt_attrs, TEAM_ATTR_OPTION_MAX, |
|---|
| 2530 | | - nl_option, team_nl_option_policy, |
|---|
| 2531 | | - info->extack); |
|---|
| 2567 | + err = nla_parse_nested_deprecated(opt_attrs, |
|---|
| 2568 | + TEAM_ATTR_OPTION_MAX, |
|---|
| 2569 | + nl_option, |
|---|
| 2570 | + team_nl_option_policy, |
|---|
| 2571 | + info->extack); |
|---|
| 2532 | 2572 | if (err) |
|---|
| 2533 | 2573 | goto team_put; |
|---|
| 2534 | 2574 | if (!opt_attrs[TEAM_ATTR_OPTION_NAME] || |
|---|
| .. | .. |
|---|
| 2642 | 2682 | { |
|---|
| 2643 | 2683 | struct nlattr *port_item; |
|---|
| 2644 | 2684 | |
|---|
| 2645 | | - port_item = nla_nest_start(skb, TEAM_ATTR_ITEM_PORT); |
|---|
| 2685 | + port_item = nla_nest_start_noflag(skb, TEAM_ATTR_ITEM_PORT); |
|---|
| 2646 | 2686 | if (!port_item) |
|---|
| 2647 | 2687 | goto nest_cancel; |
|---|
| 2648 | 2688 | if (nla_put_u32(skb, TEAM_ATTR_PORT_IFINDEX, port->dev->ifindex)) |
|---|
| .. | .. |
|---|
| 2697 | 2737 | |
|---|
| 2698 | 2738 | if (nla_put_u32(skb, TEAM_ATTR_TEAM_IFINDEX, team->dev->ifindex)) |
|---|
| 2699 | 2739 | goto nla_put_failure; |
|---|
| 2700 | | - port_list = nla_nest_start(skb, TEAM_ATTR_LIST_PORT); |
|---|
| 2740 | + port_list = nla_nest_start_noflag(skb, TEAM_ATTR_LIST_PORT); |
|---|
| 2701 | 2741 | if (!port_list) |
|---|
| 2702 | 2742 | goto nla_put_failure; |
|---|
| 2703 | 2743 | |
|---|
| .. | .. |
|---|
| 2768 | 2808 | return err; |
|---|
| 2769 | 2809 | } |
|---|
| 2770 | 2810 | |
|---|
| 2771 | | -static const struct genl_ops team_nl_ops[] = { |
|---|
| 2811 | +static const struct genl_small_ops team_nl_ops[] = { |
|---|
| 2772 | 2812 | { |
|---|
| 2773 | 2813 | .cmd = TEAM_CMD_NOOP, |
|---|
| 2814 | + .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
|---|
| 2774 | 2815 | .doit = team_nl_cmd_noop, |
|---|
| 2775 | | - .policy = team_nl_policy, |
|---|
| 2776 | 2816 | }, |
|---|
| 2777 | 2817 | { |
|---|
| 2778 | 2818 | .cmd = TEAM_CMD_OPTIONS_SET, |
|---|
| 2819 | + .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
|---|
| 2779 | 2820 | .doit = team_nl_cmd_options_set, |
|---|
| 2780 | | - .policy = team_nl_policy, |
|---|
| 2781 | 2821 | .flags = GENL_ADMIN_PERM, |
|---|
| 2782 | 2822 | }, |
|---|
| 2783 | 2823 | { |
|---|
| 2784 | 2824 | .cmd = TEAM_CMD_OPTIONS_GET, |
|---|
| 2825 | + .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
|---|
| 2785 | 2826 | .doit = team_nl_cmd_options_get, |
|---|
| 2786 | | - .policy = team_nl_policy, |
|---|
| 2787 | 2827 | .flags = GENL_ADMIN_PERM, |
|---|
| 2788 | 2828 | }, |
|---|
| 2789 | 2829 | { |
|---|
| 2790 | 2830 | .cmd = TEAM_CMD_PORT_LIST_GET, |
|---|
| 2831 | + .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
|---|
| 2791 | 2832 | .doit = team_nl_cmd_port_list_get, |
|---|
| 2792 | | - .policy = team_nl_policy, |
|---|
| 2793 | 2833 | .flags = GENL_ADMIN_PERM, |
|---|
| 2794 | 2834 | }, |
|---|
| 2795 | 2835 | }; |
|---|
| .. | .. |
|---|
| 2802 | 2842 | .name = TEAM_GENL_NAME, |
|---|
| 2803 | 2843 | .version = TEAM_GENL_VERSION, |
|---|
| 2804 | 2844 | .maxattr = TEAM_ATTR_MAX, |
|---|
| 2845 | + .policy = team_nl_policy, |
|---|
| 2805 | 2846 | .netnsok = true, |
|---|
| 2806 | 2847 | .module = THIS_MODULE, |
|---|
| 2807 | | - .ops = team_nl_ops, |
|---|
| 2808 | | - .n_ops = ARRAY_SIZE(team_nl_ops), |
|---|
| 2848 | + .small_ops = team_nl_ops, |
|---|
| 2849 | + .n_small_ops = ARRAY_SIZE(team_nl_ops), |
|---|
| 2809 | 2850 | .mcgrps = team_nl_mcgrps, |
|---|
| 2810 | 2851 | .n_mcgrps = ARRAY_SIZE(team_nl_mcgrps), |
|---|
| 2811 | 2852 | }; |
|---|