.. | .. |
---|
| 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); |
---|
| 1627 | + team->notifier_ctx = false; |
---|
1634 | 1628 | |
---|
1635 | 1629 | team->pcpu_stats = netdev_alloc_pcpu_stats(struct team_pcpu_stats); |
---|
1636 | 1630 | if (!team->pcpu_stats) |
---|
.. | .. |
---|
1656 | 1650 | goto err_options_register; |
---|
1657 | 1651 | netif_carrier_off(dev); |
---|
1658 | 1652 | |
---|
| 1653 | + lockdep_register_key(&team->team_lock_key); |
---|
| 1654 | + __mutex_init(&team->lock, "team->team_lock_key", &team->team_lock_key); |
---|
1659 | 1655 | netdev_lockdep_set_classes(dev); |
---|
1660 | 1656 | |
---|
1661 | 1657 | return 0; |
---|
.. | .. |
---|
1687 | 1683 | team_queue_override_fini(team); |
---|
1688 | 1684 | mutex_unlock(&team->lock); |
---|
1689 | 1685 | netdev_change_features(dev); |
---|
| 1686 | + lockdep_unregister_key(&team->team_lock_key); |
---|
1690 | 1687 | } |
---|
1691 | 1688 | |
---|
1692 | 1689 | static void team_destructor(struct net_device *dev) |
---|
.. | .. |
---|
1703 | 1700 | |
---|
1704 | 1701 | static int team_close(struct net_device *dev) |
---|
1705 | 1702 | { |
---|
| 1703 | + struct team *team = netdev_priv(dev); |
---|
| 1704 | + struct team_port *port; |
---|
| 1705 | + |
---|
| 1706 | + list_for_each_entry(port, &team->port_list, list) { |
---|
| 1707 | + dev_uc_unsync(port->dev, dev); |
---|
| 1708 | + dev_mc_unsync(port->dev, dev); |
---|
| 1709 | + } |
---|
| 1710 | + |
---|
1706 | 1711 | return 0; |
---|
1707 | 1712 | } |
---|
1708 | 1713 | |
---|
.. | .. |
---|
1734 | 1739 | } |
---|
1735 | 1740 | |
---|
1736 | 1741 | 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) |
---|
| 1742 | + struct net_device *sb_dev) |
---|
1739 | 1743 | { |
---|
1740 | 1744 | /* |
---|
1741 | 1745 | * This helper function exists to help dev_pick_tx get the correct |
---|
.. | .. |
---|
1991 | 1995 | err = team_port_del(team, port_dev); |
---|
1992 | 1996 | mutex_unlock(&team->lock); |
---|
1993 | 1997 | |
---|
1994 | | - if (!err) |
---|
1995 | | - netdev_change_features(dev); |
---|
| 1998 | + if (err) |
---|
| 1999 | + return err; |
---|
| 2000 | + |
---|
| 2001 | + if (netif_is_team_master(port_dev)) { |
---|
| 2002 | + lockdep_unregister_key(&team->team_lock_key); |
---|
| 2003 | + lockdep_register_key(&team->team_lock_key); |
---|
| 2004 | + lockdep_set_class(&team->lock, &team->team_lock_key); |
---|
| 2005 | + } |
---|
| 2006 | + netdev_change_features(dev); |
---|
1996 | 2007 | |
---|
1997 | 2008 | return err; |
---|
1998 | 2009 | } |
---|
.. | .. |
---|
2071 | 2082 | strlcpy(drvinfo->version, UTS_RELEASE, sizeof(drvinfo->version)); |
---|
2072 | 2083 | } |
---|
2073 | 2084 | |
---|
| 2085 | +static int team_ethtool_get_link_ksettings(struct net_device *dev, |
---|
| 2086 | + struct ethtool_link_ksettings *cmd) |
---|
| 2087 | +{ |
---|
| 2088 | + struct team *team= netdev_priv(dev); |
---|
| 2089 | + unsigned long speed = 0; |
---|
| 2090 | + struct team_port *port; |
---|
| 2091 | + |
---|
| 2092 | + cmd->base.duplex = DUPLEX_UNKNOWN; |
---|
| 2093 | + cmd->base.port = PORT_OTHER; |
---|
| 2094 | + |
---|
| 2095 | + rcu_read_lock(); |
---|
| 2096 | + list_for_each_entry_rcu(port, &team->port_list, list) { |
---|
| 2097 | + if (team_port_txable(port)) { |
---|
| 2098 | + if (port->state.speed != SPEED_UNKNOWN) |
---|
| 2099 | + speed += port->state.speed; |
---|
| 2100 | + if (cmd->base.duplex == DUPLEX_UNKNOWN && |
---|
| 2101 | + port->state.duplex != DUPLEX_UNKNOWN) |
---|
| 2102 | + cmd->base.duplex = port->state.duplex; |
---|
| 2103 | + } |
---|
| 2104 | + } |
---|
| 2105 | + rcu_read_unlock(); |
---|
| 2106 | + |
---|
| 2107 | + cmd->base.speed = speed ? : SPEED_UNKNOWN; |
---|
| 2108 | + |
---|
| 2109 | + return 0; |
---|
| 2110 | +} |
---|
| 2111 | + |
---|
2074 | 2112 | static const struct ethtool_ops team_ethtool_ops = { |
---|
2075 | 2113 | .get_drvinfo = team_ethtool_get_drvinfo, |
---|
2076 | 2114 | .get_link = ethtool_op_get_link, |
---|
| 2115 | + .get_link_ksettings = team_ethtool_get_link_ksettings, |
---|
2077 | 2116 | }; |
---|
2078 | 2117 | |
---|
2079 | 2118 | /*********************** |
---|
.. | .. |
---|
2083 | 2122 | static void team_setup_by_port(struct net_device *dev, |
---|
2084 | 2123 | struct net_device *port_dev) |
---|
2085 | 2124 | { |
---|
2086 | | - dev->header_ops = port_dev->header_ops; |
---|
| 2125 | + struct team *team = netdev_priv(dev); |
---|
| 2126 | + |
---|
| 2127 | + if (port_dev->type == ARPHRD_ETHER) |
---|
| 2128 | + dev->header_ops = team->header_ops_cache; |
---|
| 2129 | + else |
---|
| 2130 | + dev->header_ops = port_dev->header_ops; |
---|
2087 | 2131 | dev->type = port_dev->type; |
---|
2088 | 2132 | dev->hard_header_len = port_dev->hard_header_len; |
---|
2089 | 2133 | dev->needed_headroom = port_dev->needed_headroom; |
---|
.. | .. |
---|
2091 | 2135 | dev->mtu = port_dev->mtu; |
---|
2092 | 2136 | memcpy(dev->broadcast, port_dev->broadcast, port_dev->addr_len); |
---|
2093 | 2137 | eth_hw_addr_inherit(dev, port_dev); |
---|
| 2138 | + |
---|
| 2139 | + if (port_dev->flags & IFF_POINTOPOINT) { |
---|
| 2140 | + dev->flags &= ~(IFF_BROADCAST | IFF_MULTICAST); |
---|
| 2141 | + dev->flags |= (IFF_POINTOPOINT | IFF_NOARP); |
---|
| 2142 | + } else if ((port_dev->flags & (IFF_BROADCAST | IFF_MULTICAST)) == |
---|
| 2143 | + (IFF_BROADCAST | IFF_MULTICAST)) { |
---|
| 2144 | + dev->flags |= (IFF_BROADCAST | IFF_MULTICAST); |
---|
| 2145 | + dev->flags &= ~(IFF_POINTOPOINT | IFF_NOARP); |
---|
| 2146 | + } |
---|
2094 | 2147 | } |
---|
2095 | 2148 | |
---|
2096 | 2149 | static int team_dev_type_check_change(struct net_device *dev, |
---|
.. | .. |
---|
2121 | 2174 | |
---|
2122 | 2175 | static void team_setup(struct net_device *dev) |
---|
2123 | 2176 | { |
---|
| 2177 | + struct team *team = netdev_priv(dev); |
---|
| 2178 | + |
---|
2124 | 2179 | ether_setup(dev); |
---|
2125 | 2180 | dev->max_mtu = ETH_MAX_MTU; |
---|
| 2181 | + team->header_ops_cache = dev->header_ops; |
---|
2126 | 2182 | |
---|
2127 | 2183 | dev->netdev_ops = &team_netdev_ops; |
---|
2128 | 2184 | dev->ethtool_ops = &team_ethtool_ops; |
---|
.. | .. |
---|
2147 | 2203 | |
---|
2148 | 2204 | dev->hw_features = TEAM_VLAN_FEATURES | |
---|
2149 | 2205 | NETIF_F_HW_VLAN_CTAG_RX | |
---|
2150 | | - NETIF_F_HW_VLAN_CTAG_FILTER; |
---|
| 2206 | + NETIF_F_HW_VLAN_CTAG_FILTER | |
---|
| 2207 | + NETIF_F_HW_VLAN_STAG_RX | |
---|
| 2208 | + NETIF_F_HW_VLAN_STAG_FILTER; |
---|
2151 | 2209 | |
---|
2152 | 2210 | dev->hw_features |= NETIF_F_GSO_ENCAP_ALL | NETIF_F_GSO_UDP_L4; |
---|
2153 | 2211 | dev->features |= dev->hw_features; |
---|
.. | .. |
---|
2306 | 2364 | if (err) |
---|
2307 | 2365 | return err; |
---|
2308 | 2366 | |
---|
2309 | | - option_item = nla_nest_start(skb, TEAM_ATTR_ITEM_OPTION); |
---|
| 2367 | + option_item = nla_nest_start_noflag(skb, TEAM_ATTR_ITEM_OPTION); |
---|
2310 | 2368 | if (!option_item) |
---|
2311 | 2369 | return -EMSGSIZE; |
---|
2312 | 2370 | |
---|
.. | .. |
---|
2420 | 2478 | |
---|
2421 | 2479 | if (nla_put_u32(skb, TEAM_ATTR_TEAM_IFINDEX, team->dev->ifindex)) |
---|
2422 | 2480 | goto nla_put_failure; |
---|
2423 | | - option_list = nla_nest_start(skb, TEAM_ATTR_LIST_OPTION); |
---|
| 2481 | + option_list = nla_nest_start_noflag(skb, TEAM_ATTR_LIST_OPTION); |
---|
2424 | 2482 | if (!option_list) |
---|
2425 | 2483 | goto nla_put_failure; |
---|
2426 | 2484 | |
---|
.. | .. |
---|
2526 | 2584 | err = -EINVAL; |
---|
2527 | 2585 | goto team_put; |
---|
2528 | 2586 | } |
---|
2529 | | - err = nla_parse_nested(opt_attrs, TEAM_ATTR_OPTION_MAX, |
---|
2530 | | - nl_option, team_nl_option_policy, |
---|
2531 | | - info->extack); |
---|
| 2587 | + err = nla_parse_nested_deprecated(opt_attrs, |
---|
| 2588 | + TEAM_ATTR_OPTION_MAX, |
---|
| 2589 | + nl_option, |
---|
| 2590 | + team_nl_option_policy, |
---|
| 2591 | + info->extack); |
---|
2532 | 2592 | if (err) |
---|
2533 | 2593 | goto team_put; |
---|
2534 | 2594 | if (!opt_attrs[TEAM_ATTR_OPTION_NAME] || |
---|
.. | .. |
---|
2642 | 2702 | { |
---|
2643 | 2703 | struct nlattr *port_item; |
---|
2644 | 2704 | |
---|
2645 | | - port_item = nla_nest_start(skb, TEAM_ATTR_ITEM_PORT); |
---|
| 2705 | + port_item = nla_nest_start_noflag(skb, TEAM_ATTR_ITEM_PORT); |
---|
2646 | 2706 | if (!port_item) |
---|
2647 | 2707 | goto nest_cancel; |
---|
2648 | 2708 | if (nla_put_u32(skb, TEAM_ATTR_PORT_IFINDEX, port->dev->ifindex)) |
---|
.. | .. |
---|
2697 | 2757 | |
---|
2698 | 2758 | if (nla_put_u32(skb, TEAM_ATTR_TEAM_IFINDEX, team->dev->ifindex)) |
---|
2699 | 2759 | goto nla_put_failure; |
---|
2700 | | - port_list = nla_nest_start(skb, TEAM_ATTR_LIST_PORT); |
---|
| 2760 | + port_list = nla_nest_start_noflag(skb, TEAM_ATTR_LIST_PORT); |
---|
2701 | 2761 | if (!port_list) |
---|
2702 | 2762 | goto nla_put_failure; |
---|
2703 | 2763 | |
---|
.. | .. |
---|
2768 | 2828 | return err; |
---|
2769 | 2829 | } |
---|
2770 | 2830 | |
---|
2771 | | -static const struct genl_ops team_nl_ops[] = { |
---|
| 2831 | +static const struct genl_small_ops team_nl_ops[] = { |
---|
2772 | 2832 | { |
---|
2773 | 2833 | .cmd = TEAM_CMD_NOOP, |
---|
| 2834 | + .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
---|
2774 | 2835 | .doit = team_nl_cmd_noop, |
---|
2775 | | - .policy = team_nl_policy, |
---|
2776 | 2836 | }, |
---|
2777 | 2837 | { |
---|
2778 | 2838 | .cmd = TEAM_CMD_OPTIONS_SET, |
---|
| 2839 | + .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
---|
2779 | 2840 | .doit = team_nl_cmd_options_set, |
---|
2780 | | - .policy = team_nl_policy, |
---|
2781 | 2841 | .flags = GENL_ADMIN_PERM, |
---|
2782 | 2842 | }, |
---|
2783 | 2843 | { |
---|
2784 | 2844 | .cmd = TEAM_CMD_OPTIONS_GET, |
---|
| 2845 | + .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
---|
2785 | 2846 | .doit = team_nl_cmd_options_get, |
---|
2786 | | - .policy = team_nl_policy, |
---|
2787 | 2847 | .flags = GENL_ADMIN_PERM, |
---|
2788 | 2848 | }, |
---|
2789 | 2849 | { |
---|
2790 | 2850 | .cmd = TEAM_CMD_PORT_LIST_GET, |
---|
| 2851 | + .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
---|
2791 | 2852 | .doit = team_nl_cmd_port_list_get, |
---|
2792 | | - .policy = team_nl_policy, |
---|
2793 | 2853 | .flags = GENL_ADMIN_PERM, |
---|
2794 | 2854 | }, |
---|
2795 | 2855 | }; |
---|
.. | .. |
---|
2802 | 2862 | .name = TEAM_GENL_NAME, |
---|
2803 | 2863 | .version = TEAM_GENL_VERSION, |
---|
2804 | 2864 | .maxattr = TEAM_ATTR_MAX, |
---|
| 2865 | + .policy = team_nl_policy, |
---|
2805 | 2866 | .netnsok = true, |
---|
2806 | 2867 | .module = THIS_MODULE, |
---|
2807 | | - .ops = team_nl_ops, |
---|
2808 | | - .n_ops = ARRAY_SIZE(team_nl_ops), |
---|
| 2868 | + .small_ops = team_nl_ops, |
---|
| 2869 | + .n_small_ops = ARRAY_SIZE(team_nl_ops), |
---|
2809 | 2870 | .mcgrps = team_nl_mcgrps, |
---|
2810 | 2871 | .n_mcgrps = ARRAY_SIZE(team_nl_mcgrps), |
---|
2811 | 2872 | }; |
---|
.. | .. |
---|
2975 | 3036 | team_del_slave(port->team->dev, dev); |
---|
2976 | 3037 | break; |
---|
2977 | 3038 | case NETDEV_FEAT_CHANGE: |
---|
2978 | | - team_compute_features(port->team); |
---|
| 3039 | + if (!port->team->notifier_ctx) { |
---|
| 3040 | + port->team->notifier_ctx = true; |
---|
| 3041 | + team_compute_features(port->team); |
---|
| 3042 | + port->team->notifier_ctx = false; |
---|
| 3043 | + } |
---|
2979 | 3044 | break; |
---|
2980 | 3045 | case NETDEV_PRECHANGEMTU: |
---|
2981 | 3046 | /* Forbid to change mtu of underlaying device */ |
---|