1a0393e3eSHangbin Liu // SPDX-License-Identifier: GPL-2.0-or-later
2a0393e3eSHangbin Liu /*
3a0393e3eSHangbin Liu * drivers/net/team/team.c - Network team device driver
4a0393e3eSHangbin Liu * Copyright (c) 2011 Jiri Pirko <jpirko@redhat.com>
5a0393e3eSHangbin Liu */
6a0393e3eSHangbin Liu
7a0393e3eSHangbin Liu #include <linux/ethtool.h>
8a0393e3eSHangbin Liu #include <linux/kernel.h>
9a0393e3eSHangbin Liu #include <linux/types.h>
10a0393e3eSHangbin Liu #include <linux/module.h>
11a0393e3eSHangbin Liu #include <linux/init.h>
12a0393e3eSHangbin Liu #include <linux/slab.h>
13a0393e3eSHangbin Liu #include <linux/rcupdate.h>
14a0393e3eSHangbin Liu #include <linux/errno.h>
15a0393e3eSHangbin Liu #include <linux/ctype.h>
16a0393e3eSHangbin Liu #include <linux/notifier.h>
17a0393e3eSHangbin Liu #include <linux/netdevice.h>
18a0393e3eSHangbin Liu #include <linux/netpoll.h>
19a0393e3eSHangbin Liu #include <linux/if_vlan.h>
20a0393e3eSHangbin Liu #include <linux/if_arp.h>
21a0393e3eSHangbin Liu #include <linux/socket.h>
22a0393e3eSHangbin Liu #include <linux/etherdevice.h>
23a0393e3eSHangbin Liu #include <linux/rtnetlink.h>
24a0393e3eSHangbin Liu #include <net/rtnetlink.h>
25a0393e3eSHangbin Liu #include <net/genetlink.h>
26a0393e3eSHangbin Liu #include <net/netlink.h>
27a0393e3eSHangbin Liu #include <net/sch_generic.h>
28a0393e3eSHangbin Liu #include <linux/if_team.h>
29a0393e3eSHangbin Liu
30948dbafcSHangbin Liu #include "team_nl.h"
31948dbafcSHangbin Liu
32a0393e3eSHangbin Liu #define DRV_NAME "team"
33a0393e3eSHangbin Liu
34a0393e3eSHangbin Liu
35a0393e3eSHangbin Liu /**********
36a0393e3eSHangbin Liu * Helpers
37a0393e3eSHangbin Liu **********/
38a0393e3eSHangbin Liu
team_port_get_rtnl(const struct net_device * dev)39a0393e3eSHangbin Liu static struct team_port *team_port_get_rtnl(const struct net_device *dev)
40a0393e3eSHangbin Liu {
41a0393e3eSHangbin Liu struct team_port *port = rtnl_dereference(dev->rx_handler_data);
42a0393e3eSHangbin Liu
43a0393e3eSHangbin Liu return netif_is_team_port(dev) ? port : NULL;
44a0393e3eSHangbin Liu }
45a0393e3eSHangbin Liu
46a0393e3eSHangbin Liu /*
47a0393e3eSHangbin Liu * Since the ability to change device address for open port device is tested in
48a0393e3eSHangbin Liu * team_port_add, this function can be called without control of return value
49a0393e3eSHangbin Liu */
__set_port_dev_addr(struct net_device * port_dev,const unsigned char * dev_addr)50a0393e3eSHangbin Liu static int __set_port_dev_addr(struct net_device *port_dev,
51a0393e3eSHangbin Liu const unsigned char *dev_addr)
52a0393e3eSHangbin Liu {
53a0393e3eSHangbin Liu struct sockaddr_storage addr;
54a0393e3eSHangbin Liu
55a0393e3eSHangbin Liu memcpy(addr.__data, dev_addr, port_dev->addr_len);
56a0393e3eSHangbin Liu addr.ss_family = port_dev->type;
57a0393e3eSHangbin Liu return dev_set_mac_address(port_dev, (struct sockaddr *)&addr, NULL);
58a0393e3eSHangbin Liu }
59a0393e3eSHangbin Liu
team_port_set_orig_dev_addr(struct team_port * port)60a0393e3eSHangbin Liu static int team_port_set_orig_dev_addr(struct team_port *port)
61a0393e3eSHangbin Liu {
62a0393e3eSHangbin Liu return __set_port_dev_addr(port->dev, port->orig.dev_addr);
63a0393e3eSHangbin Liu }
64a0393e3eSHangbin Liu
team_port_set_team_dev_addr(struct team * team,struct team_port * port)65a0393e3eSHangbin Liu static int team_port_set_team_dev_addr(struct team *team,
66a0393e3eSHangbin Liu struct team_port *port)
67a0393e3eSHangbin Liu {
68a0393e3eSHangbin Liu return __set_port_dev_addr(port->dev, team->dev->dev_addr);
69a0393e3eSHangbin Liu }
70a0393e3eSHangbin Liu
team_modeop_port_enter(struct team * team,struct team_port * port)71a0393e3eSHangbin Liu int team_modeop_port_enter(struct team *team, struct team_port *port)
72a0393e3eSHangbin Liu {
73a0393e3eSHangbin Liu return team_port_set_team_dev_addr(team, port);
74a0393e3eSHangbin Liu }
75a0393e3eSHangbin Liu EXPORT_SYMBOL(team_modeop_port_enter);
76a0393e3eSHangbin Liu
team_modeop_port_change_dev_addr(struct team * team,struct team_port * port)77a0393e3eSHangbin Liu void team_modeop_port_change_dev_addr(struct team *team,
78a0393e3eSHangbin Liu struct team_port *port)
79a0393e3eSHangbin Liu {
80a0393e3eSHangbin Liu team_port_set_team_dev_addr(team, port);
81a0393e3eSHangbin Liu }
82a0393e3eSHangbin Liu EXPORT_SYMBOL(team_modeop_port_change_dev_addr);
83a0393e3eSHangbin Liu
team_lower_state_changed(struct team_port * port)84a0393e3eSHangbin Liu static void team_lower_state_changed(struct team_port *port)
85a0393e3eSHangbin Liu {
86a0393e3eSHangbin Liu struct netdev_lag_lower_state_info info;
87a0393e3eSHangbin Liu
88a0393e3eSHangbin Liu info.link_up = port->linkup;
89a0393e3eSHangbin Liu info.tx_enabled = team_port_enabled(port);
90a0393e3eSHangbin Liu netdev_lower_state_changed(port->dev, &info);
91a0393e3eSHangbin Liu }
92a0393e3eSHangbin Liu
team_refresh_port_linkup(struct team_port * port)93a0393e3eSHangbin Liu static void team_refresh_port_linkup(struct team_port *port)
94a0393e3eSHangbin Liu {
95a0393e3eSHangbin Liu bool new_linkup = port->user.linkup_enabled ? port->user.linkup :
96a0393e3eSHangbin Liu port->state.linkup;
97a0393e3eSHangbin Liu
98a0393e3eSHangbin Liu if (port->linkup != new_linkup) {
99a0393e3eSHangbin Liu port->linkup = new_linkup;
100a0393e3eSHangbin Liu team_lower_state_changed(port);
101a0393e3eSHangbin Liu }
102a0393e3eSHangbin Liu }
103a0393e3eSHangbin Liu
104a0393e3eSHangbin Liu
105a0393e3eSHangbin Liu /*******************
106a0393e3eSHangbin Liu * Options handling
107a0393e3eSHangbin Liu *******************/
108a0393e3eSHangbin Liu
109a0393e3eSHangbin Liu struct team_option_inst { /* One for each option instance */
110a0393e3eSHangbin Liu struct list_head list;
111a0393e3eSHangbin Liu struct list_head tmp_list;
112a0393e3eSHangbin Liu struct team_option *option;
113a0393e3eSHangbin Liu struct team_option_inst_info info;
114a0393e3eSHangbin Liu bool changed;
115a0393e3eSHangbin Liu bool removed;
116a0393e3eSHangbin Liu };
117a0393e3eSHangbin Liu
__team_find_option(struct team * team,const char * opt_name)118a0393e3eSHangbin Liu static struct team_option *__team_find_option(struct team *team,
119a0393e3eSHangbin Liu const char *opt_name)
120a0393e3eSHangbin Liu {
121a0393e3eSHangbin Liu struct team_option *option;
122a0393e3eSHangbin Liu
123a0393e3eSHangbin Liu list_for_each_entry(option, &team->option_list, list) {
124a0393e3eSHangbin Liu if (strcmp(option->name, opt_name) == 0)
125a0393e3eSHangbin Liu return option;
126a0393e3eSHangbin Liu }
127a0393e3eSHangbin Liu return NULL;
128a0393e3eSHangbin Liu }
129a0393e3eSHangbin Liu
__team_option_inst_del(struct team_option_inst * opt_inst)130a0393e3eSHangbin Liu static void __team_option_inst_del(struct team_option_inst *opt_inst)
131a0393e3eSHangbin Liu {
132a0393e3eSHangbin Liu list_del(&opt_inst->list);
133a0393e3eSHangbin Liu kfree(opt_inst);
134a0393e3eSHangbin Liu }
135a0393e3eSHangbin Liu
__team_option_inst_del_option(struct team * team,struct team_option * option)136a0393e3eSHangbin Liu static void __team_option_inst_del_option(struct team *team,
137a0393e3eSHangbin Liu struct team_option *option)
138a0393e3eSHangbin Liu {
139a0393e3eSHangbin Liu struct team_option_inst *opt_inst, *tmp;
140a0393e3eSHangbin Liu
141a0393e3eSHangbin Liu list_for_each_entry_safe(opt_inst, tmp, &team->option_inst_list, list) {
142a0393e3eSHangbin Liu if (opt_inst->option == option)
143a0393e3eSHangbin Liu __team_option_inst_del(opt_inst);
144a0393e3eSHangbin Liu }
145a0393e3eSHangbin Liu }
146a0393e3eSHangbin Liu
__team_option_inst_add(struct team * team,struct team_option * option,struct team_port * port)147a0393e3eSHangbin Liu static int __team_option_inst_add(struct team *team, struct team_option *option,
148a0393e3eSHangbin Liu struct team_port *port)
149a0393e3eSHangbin Liu {
150a0393e3eSHangbin Liu struct team_option_inst *opt_inst;
151a0393e3eSHangbin Liu unsigned int array_size;
152a0393e3eSHangbin Liu unsigned int i;
153a0393e3eSHangbin Liu
154a0393e3eSHangbin Liu array_size = option->array_size;
155a0393e3eSHangbin Liu if (!array_size)
156a0393e3eSHangbin Liu array_size = 1; /* No array but still need one instance */
157a0393e3eSHangbin Liu
158a0393e3eSHangbin Liu for (i = 0; i < array_size; i++) {
159a0393e3eSHangbin Liu opt_inst = kmalloc(sizeof(*opt_inst), GFP_KERNEL);
160a0393e3eSHangbin Liu if (!opt_inst)
161a0393e3eSHangbin Liu return -ENOMEM;
162a0393e3eSHangbin Liu opt_inst->option = option;
163a0393e3eSHangbin Liu opt_inst->info.port = port;
164a0393e3eSHangbin Liu opt_inst->info.array_index = i;
165a0393e3eSHangbin Liu opt_inst->changed = true;
166a0393e3eSHangbin Liu opt_inst->removed = false;
167a0393e3eSHangbin Liu list_add_tail(&opt_inst->list, &team->option_inst_list);
168a0393e3eSHangbin Liu if (option->init)
169a0393e3eSHangbin Liu option->init(team, &opt_inst->info);
170a0393e3eSHangbin Liu
171a0393e3eSHangbin Liu }
172a0393e3eSHangbin Liu return 0;
173a0393e3eSHangbin Liu }
174a0393e3eSHangbin Liu
__team_option_inst_add_option(struct team * team,struct team_option * option)175a0393e3eSHangbin Liu static int __team_option_inst_add_option(struct team *team,
176a0393e3eSHangbin Liu struct team_option *option)
177a0393e3eSHangbin Liu {
178a0393e3eSHangbin Liu int err;
179a0393e3eSHangbin Liu
180a0393e3eSHangbin Liu if (!option->per_port) {
181a0393e3eSHangbin Liu err = __team_option_inst_add(team, option, NULL);
182a0393e3eSHangbin Liu if (err)
183a0393e3eSHangbin Liu goto inst_del_option;
184a0393e3eSHangbin Liu }
185a0393e3eSHangbin Liu return 0;
186a0393e3eSHangbin Liu
187a0393e3eSHangbin Liu inst_del_option:
188a0393e3eSHangbin Liu __team_option_inst_del_option(team, option);
189a0393e3eSHangbin Liu return err;
190a0393e3eSHangbin Liu }
191a0393e3eSHangbin Liu
__team_option_inst_mark_removed_option(struct team * team,struct team_option * option)192a0393e3eSHangbin Liu static void __team_option_inst_mark_removed_option(struct team *team,
193a0393e3eSHangbin Liu struct team_option *option)
194a0393e3eSHangbin Liu {
195a0393e3eSHangbin Liu struct team_option_inst *opt_inst;
196a0393e3eSHangbin Liu
197a0393e3eSHangbin Liu list_for_each_entry(opt_inst, &team->option_inst_list, list) {
198a0393e3eSHangbin Liu if (opt_inst->option == option) {
199a0393e3eSHangbin Liu opt_inst->changed = true;
200a0393e3eSHangbin Liu opt_inst->removed = true;
201a0393e3eSHangbin Liu }
202a0393e3eSHangbin Liu }
203a0393e3eSHangbin Liu }
204a0393e3eSHangbin Liu
__team_option_inst_del_port(struct team * team,struct team_port * port)205a0393e3eSHangbin Liu static void __team_option_inst_del_port(struct team *team,
206a0393e3eSHangbin Liu struct team_port *port)
207a0393e3eSHangbin Liu {
208a0393e3eSHangbin Liu struct team_option_inst *opt_inst, *tmp;
209a0393e3eSHangbin Liu
210a0393e3eSHangbin Liu list_for_each_entry_safe(opt_inst, tmp, &team->option_inst_list, list) {
211a0393e3eSHangbin Liu if (opt_inst->option->per_port &&
212a0393e3eSHangbin Liu opt_inst->info.port == port)
213a0393e3eSHangbin Liu __team_option_inst_del(opt_inst);
214a0393e3eSHangbin Liu }
215a0393e3eSHangbin Liu }
216a0393e3eSHangbin Liu
__team_option_inst_add_port(struct team * team,struct team_port * port)217a0393e3eSHangbin Liu static int __team_option_inst_add_port(struct team *team,
218a0393e3eSHangbin Liu struct team_port *port)
219a0393e3eSHangbin Liu {
220a0393e3eSHangbin Liu struct team_option *option;
221a0393e3eSHangbin Liu int err;
222a0393e3eSHangbin Liu
223a0393e3eSHangbin Liu list_for_each_entry(option, &team->option_list, list) {
224a0393e3eSHangbin Liu if (!option->per_port)
225a0393e3eSHangbin Liu continue;
226a0393e3eSHangbin Liu err = __team_option_inst_add(team, option, port);
227a0393e3eSHangbin Liu if (err)
228a0393e3eSHangbin Liu goto inst_del_port;
229a0393e3eSHangbin Liu }
230a0393e3eSHangbin Liu return 0;
231a0393e3eSHangbin Liu
232a0393e3eSHangbin Liu inst_del_port:
233a0393e3eSHangbin Liu __team_option_inst_del_port(team, port);
234a0393e3eSHangbin Liu return err;
235a0393e3eSHangbin Liu }
236a0393e3eSHangbin Liu
__team_option_inst_mark_removed_port(struct team * team,struct team_port * port)237a0393e3eSHangbin Liu static void __team_option_inst_mark_removed_port(struct team *team,
238a0393e3eSHangbin Liu struct team_port *port)
239a0393e3eSHangbin Liu {
240a0393e3eSHangbin Liu struct team_option_inst *opt_inst;
241a0393e3eSHangbin Liu
242a0393e3eSHangbin Liu list_for_each_entry(opt_inst, &team->option_inst_list, list) {
243a0393e3eSHangbin Liu if (opt_inst->info.port == port) {
244a0393e3eSHangbin Liu opt_inst->changed = true;
245a0393e3eSHangbin Liu opt_inst->removed = true;
246a0393e3eSHangbin Liu }
247a0393e3eSHangbin Liu }
248a0393e3eSHangbin Liu }
249a0393e3eSHangbin Liu
__team_options_register(struct team * team,const struct team_option * option,size_t option_count)250a0393e3eSHangbin Liu static int __team_options_register(struct team *team,
251a0393e3eSHangbin Liu const struct team_option *option,
252a0393e3eSHangbin Liu size_t option_count)
253a0393e3eSHangbin Liu {
254a0393e3eSHangbin Liu int i;
255a0393e3eSHangbin Liu struct team_option **dst_opts;
256a0393e3eSHangbin Liu int err;
257a0393e3eSHangbin Liu
258a0393e3eSHangbin Liu dst_opts = kcalloc(option_count, sizeof(struct team_option *),
259a0393e3eSHangbin Liu GFP_KERNEL);
260a0393e3eSHangbin Liu if (!dst_opts)
261a0393e3eSHangbin Liu return -ENOMEM;
262a0393e3eSHangbin Liu for (i = 0; i < option_count; i++, option++) {
263a0393e3eSHangbin Liu if (__team_find_option(team, option->name)) {
264a0393e3eSHangbin Liu err = -EEXIST;
265a0393e3eSHangbin Liu goto alloc_rollback;
266a0393e3eSHangbin Liu }
267a0393e3eSHangbin Liu dst_opts[i] = kmemdup(option, sizeof(*option), GFP_KERNEL);
268a0393e3eSHangbin Liu if (!dst_opts[i]) {
269a0393e3eSHangbin Liu err = -ENOMEM;
270a0393e3eSHangbin Liu goto alloc_rollback;
271a0393e3eSHangbin Liu }
272a0393e3eSHangbin Liu }
273a0393e3eSHangbin Liu
274a0393e3eSHangbin Liu for (i = 0; i < option_count; i++) {
275a0393e3eSHangbin Liu err = __team_option_inst_add_option(team, dst_opts[i]);
276a0393e3eSHangbin Liu if (err)
277a0393e3eSHangbin Liu goto inst_rollback;
278a0393e3eSHangbin Liu list_add_tail(&dst_opts[i]->list, &team->option_list);
279a0393e3eSHangbin Liu }
280a0393e3eSHangbin Liu
281a0393e3eSHangbin Liu kfree(dst_opts);
282a0393e3eSHangbin Liu return 0;
283a0393e3eSHangbin Liu
284a0393e3eSHangbin Liu inst_rollback:
285a0393e3eSHangbin Liu for (i--; i >= 0; i--) {
286a0393e3eSHangbin Liu __team_option_inst_del_option(team, dst_opts[i]);
287a0393e3eSHangbin Liu list_del(&dst_opts[i]->list);
288a0393e3eSHangbin Liu }
289a0393e3eSHangbin Liu
290a0393e3eSHangbin Liu i = option_count;
291a0393e3eSHangbin Liu alloc_rollback:
292a0393e3eSHangbin Liu for (i--; i >= 0; i--)
293a0393e3eSHangbin Liu kfree(dst_opts[i]);
294a0393e3eSHangbin Liu
295a0393e3eSHangbin Liu kfree(dst_opts);
296a0393e3eSHangbin Liu return err;
297a0393e3eSHangbin Liu }
298a0393e3eSHangbin Liu
__team_options_mark_removed(struct team * team,const struct team_option * option,size_t option_count)299a0393e3eSHangbin Liu static void __team_options_mark_removed(struct team *team,
300a0393e3eSHangbin Liu const struct team_option *option,
301a0393e3eSHangbin Liu size_t option_count)
302a0393e3eSHangbin Liu {
303a0393e3eSHangbin Liu int i;
304a0393e3eSHangbin Liu
305a0393e3eSHangbin Liu for (i = 0; i < option_count; i++, option++) {
306a0393e3eSHangbin Liu struct team_option *del_opt;
307a0393e3eSHangbin Liu
308a0393e3eSHangbin Liu del_opt = __team_find_option(team, option->name);
309a0393e3eSHangbin Liu if (del_opt)
310a0393e3eSHangbin Liu __team_option_inst_mark_removed_option(team, del_opt);
311a0393e3eSHangbin Liu }
312a0393e3eSHangbin Liu }
313a0393e3eSHangbin Liu
__team_options_unregister(struct team * team,const struct team_option * option,size_t option_count)314a0393e3eSHangbin Liu static void __team_options_unregister(struct team *team,
315a0393e3eSHangbin Liu const struct team_option *option,
316a0393e3eSHangbin Liu size_t option_count)
317a0393e3eSHangbin Liu {
318a0393e3eSHangbin Liu int i;
319a0393e3eSHangbin Liu
320a0393e3eSHangbin Liu for (i = 0; i < option_count; i++, option++) {
321a0393e3eSHangbin Liu struct team_option *del_opt;
322a0393e3eSHangbin Liu
323a0393e3eSHangbin Liu del_opt = __team_find_option(team, option->name);
324a0393e3eSHangbin Liu if (del_opt) {
325a0393e3eSHangbin Liu __team_option_inst_del_option(team, del_opt);
326a0393e3eSHangbin Liu list_del(&del_opt->list);
327a0393e3eSHangbin Liu kfree(del_opt);
328a0393e3eSHangbin Liu }
329a0393e3eSHangbin Liu }
330a0393e3eSHangbin Liu }
331a0393e3eSHangbin Liu
332a0393e3eSHangbin Liu static void __team_options_change_check(struct team *team);
333a0393e3eSHangbin Liu
team_options_register(struct team * team,const struct team_option * option,size_t option_count)334a0393e3eSHangbin Liu int team_options_register(struct team *team,
335a0393e3eSHangbin Liu const struct team_option *option,
336a0393e3eSHangbin Liu size_t option_count)
337a0393e3eSHangbin Liu {
338a0393e3eSHangbin Liu int err;
339a0393e3eSHangbin Liu
340a0393e3eSHangbin Liu err = __team_options_register(team, option, option_count);
341a0393e3eSHangbin Liu if (err)
342a0393e3eSHangbin Liu return err;
343a0393e3eSHangbin Liu __team_options_change_check(team);
344a0393e3eSHangbin Liu return 0;
345a0393e3eSHangbin Liu }
346a0393e3eSHangbin Liu EXPORT_SYMBOL(team_options_register);
347a0393e3eSHangbin Liu
team_options_unregister(struct team * team,const struct team_option * option,size_t option_count)348a0393e3eSHangbin Liu void team_options_unregister(struct team *team,
349a0393e3eSHangbin Liu const struct team_option *option,
350a0393e3eSHangbin Liu size_t option_count)
351a0393e3eSHangbin Liu {
352a0393e3eSHangbin Liu __team_options_mark_removed(team, option, option_count);
353a0393e3eSHangbin Liu __team_options_change_check(team);
354a0393e3eSHangbin Liu __team_options_unregister(team, option, option_count);
355a0393e3eSHangbin Liu }
356a0393e3eSHangbin Liu EXPORT_SYMBOL(team_options_unregister);
357a0393e3eSHangbin Liu
team_option_get(struct team * team,struct team_option_inst * opt_inst,struct team_gsetter_ctx * ctx)358a0393e3eSHangbin Liu static int team_option_get(struct team *team,
359a0393e3eSHangbin Liu struct team_option_inst *opt_inst,
360a0393e3eSHangbin Liu struct team_gsetter_ctx *ctx)
361a0393e3eSHangbin Liu {
362a0393e3eSHangbin Liu if (!opt_inst->option->getter)
363a0393e3eSHangbin Liu return -EOPNOTSUPP;
364a0393e3eSHangbin Liu
365a0393e3eSHangbin Liu opt_inst->option->getter(team, ctx);
366a0393e3eSHangbin Liu return 0;
367a0393e3eSHangbin Liu }
368a0393e3eSHangbin Liu
team_option_set(struct team * team,struct team_option_inst * opt_inst,struct team_gsetter_ctx * ctx)369a0393e3eSHangbin Liu static int team_option_set(struct team *team,
370a0393e3eSHangbin Liu struct team_option_inst *opt_inst,
371a0393e3eSHangbin Liu struct team_gsetter_ctx *ctx)
372a0393e3eSHangbin Liu {
373a0393e3eSHangbin Liu if (!opt_inst->option->setter)
374a0393e3eSHangbin Liu return -EOPNOTSUPP;
375a0393e3eSHangbin Liu return opt_inst->option->setter(team, ctx);
376a0393e3eSHangbin Liu }
377a0393e3eSHangbin Liu
team_option_inst_set_change(struct team_option_inst_info * opt_inst_info)378a0393e3eSHangbin Liu void team_option_inst_set_change(struct team_option_inst_info *opt_inst_info)
379a0393e3eSHangbin Liu {
380a0393e3eSHangbin Liu struct team_option_inst *opt_inst;
381a0393e3eSHangbin Liu
382a0393e3eSHangbin Liu opt_inst = container_of(opt_inst_info, struct team_option_inst, info);
383a0393e3eSHangbin Liu opt_inst->changed = true;
384a0393e3eSHangbin Liu }
385a0393e3eSHangbin Liu EXPORT_SYMBOL(team_option_inst_set_change);
386a0393e3eSHangbin Liu
team_options_change_check(struct team * team)387a0393e3eSHangbin Liu void team_options_change_check(struct team *team)
388a0393e3eSHangbin Liu {
389a0393e3eSHangbin Liu __team_options_change_check(team);
390a0393e3eSHangbin Liu }
391a0393e3eSHangbin Liu EXPORT_SYMBOL(team_options_change_check);
392a0393e3eSHangbin Liu
393a0393e3eSHangbin Liu
394a0393e3eSHangbin Liu /****************
395a0393e3eSHangbin Liu * Mode handling
396a0393e3eSHangbin Liu ****************/
397a0393e3eSHangbin Liu
398a0393e3eSHangbin Liu static LIST_HEAD(mode_list);
399a0393e3eSHangbin Liu static DEFINE_SPINLOCK(mode_list_lock);
400a0393e3eSHangbin Liu
401a0393e3eSHangbin Liu struct team_mode_item {
402a0393e3eSHangbin Liu struct list_head list;
403a0393e3eSHangbin Liu const struct team_mode *mode;
404a0393e3eSHangbin Liu };
405a0393e3eSHangbin Liu
__find_mode(const char * kind)406a0393e3eSHangbin Liu static struct team_mode_item *__find_mode(const char *kind)
407a0393e3eSHangbin Liu {
408a0393e3eSHangbin Liu struct team_mode_item *mitem;
409a0393e3eSHangbin Liu
410a0393e3eSHangbin Liu list_for_each_entry(mitem, &mode_list, list) {
411a0393e3eSHangbin Liu if (strcmp(mitem->mode->kind, kind) == 0)
412a0393e3eSHangbin Liu return mitem;
413a0393e3eSHangbin Liu }
414a0393e3eSHangbin Liu return NULL;
415a0393e3eSHangbin Liu }
416a0393e3eSHangbin Liu
is_good_mode_name(const char * name)417a0393e3eSHangbin Liu static bool is_good_mode_name(const char *name)
418a0393e3eSHangbin Liu {
419a0393e3eSHangbin Liu while (*name != '\0') {
420a0393e3eSHangbin Liu if (!isalpha(*name) && !isdigit(*name) && *name != '_')
421a0393e3eSHangbin Liu return false;
422a0393e3eSHangbin Liu name++;
423a0393e3eSHangbin Liu }
424a0393e3eSHangbin Liu return true;
425a0393e3eSHangbin Liu }
426a0393e3eSHangbin Liu
team_mode_register(const struct team_mode * mode)427a0393e3eSHangbin Liu int team_mode_register(const struct team_mode *mode)
428a0393e3eSHangbin Liu {
429a0393e3eSHangbin Liu int err = 0;
430a0393e3eSHangbin Liu struct team_mode_item *mitem;
431a0393e3eSHangbin Liu
432a0393e3eSHangbin Liu if (!is_good_mode_name(mode->kind) ||
433a0393e3eSHangbin Liu mode->priv_size > TEAM_MODE_PRIV_SIZE)
434a0393e3eSHangbin Liu return -EINVAL;
435a0393e3eSHangbin Liu
436a0393e3eSHangbin Liu mitem = kmalloc(sizeof(*mitem), GFP_KERNEL);
437a0393e3eSHangbin Liu if (!mitem)
438a0393e3eSHangbin Liu return -ENOMEM;
439a0393e3eSHangbin Liu
440a0393e3eSHangbin Liu spin_lock(&mode_list_lock);
441a0393e3eSHangbin Liu if (__find_mode(mode->kind)) {
442a0393e3eSHangbin Liu err = -EEXIST;
443a0393e3eSHangbin Liu kfree(mitem);
444a0393e3eSHangbin Liu goto unlock;
445a0393e3eSHangbin Liu }
446a0393e3eSHangbin Liu mitem->mode = mode;
447a0393e3eSHangbin Liu list_add_tail(&mitem->list, &mode_list);
448a0393e3eSHangbin Liu unlock:
449a0393e3eSHangbin Liu spin_unlock(&mode_list_lock);
450a0393e3eSHangbin Liu return err;
451a0393e3eSHangbin Liu }
452a0393e3eSHangbin Liu EXPORT_SYMBOL(team_mode_register);
453a0393e3eSHangbin Liu
team_mode_unregister(const struct team_mode * mode)454a0393e3eSHangbin Liu void team_mode_unregister(const struct team_mode *mode)
455a0393e3eSHangbin Liu {
456a0393e3eSHangbin Liu struct team_mode_item *mitem;
457a0393e3eSHangbin Liu
458a0393e3eSHangbin Liu spin_lock(&mode_list_lock);
459a0393e3eSHangbin Liu mitem = __find_mode(mode->kind);
460a0393e3eSHangbin Liu if (mitem) {
461a0393e3eSHangbin Liu list_del_init(&mitem->list);
462a0393e3eSHangbin Liu kfree(mitem);
463a0393e3eSHangbin Liu }
464a0393e3eSHangbin Liu spin_unlock(&mode_list_lock);
465a0393e3eSHangbin Liu }
466a0393e3eSHangbin Liu EXPORT_SYMBOL(team_mode_unregister);
467a0393e3eSHangbin Liu
team_mode_get(const char * kind)468a0393e3eSHangbin Liu static const struct team_mode *team_mode_get(const char *kind)
469a0393e3eSHangbin Liu {
470a0393e3eSHangbin Liu struct team_mode_item *mitem;
471a0393e3eSHangbin Liu const struct team_mode *mode = NULL;
472a0393e3eSHangbin Liu
473a0393e3eSHangbin Liu if (!try_module_get(THIS_MODULE))
474a0393e3eSHangbin Liu return NULL;
475a0393e3eSHangbin Liu
476a0393e3eSHangbin Liu spin_lock(&mode_list_lock);
477a0393e3eSHangbin Liu mitem = __find_mode(kind);
478a0393e3eSHangbin Liu if (!mitem) {
479a0393e3eSHangbin Liu spin_unlock(&mode_list_lock);
480a0393e3eSHangbin Liu request_module("team-mode-%s", kind);
481a0393e3eSHangbin Liu spin_lock(&mode_list_lock);
482a0393e3eSHangbin Liu mitem = __find_mode(kind);
483a0393e3eSHangbin Liu }
484a0393e3eSHangbin Liu if (mitem) {
485a0393e3eSHangbin Liu mode = mitem->mode;
486a0393e3eSHangbin Liu if (!try_module_get(mode->owner))
487a0393e3eSHangbin Liu mode = NULL;
488a0393e3eSHangbin Liu }
489a0393e3eSHangbin Liu
490a0393e3eSHangbin Liu spin_unlock(&mode_list_lock);
491a0393e3eSHangbin Liu module_put(THIS_MODULE);
492a0393e3eSHangbin Liu return mode;
493a0393e3eSHangbin Liu }
494a0393e3eSHangbin Liu
team_mode_put(const struct team_mode * mode)495a0393e3eSHangbin Liu static void team_mode_put(const struct team_mode *mode)
496a0393e3eSHangbin Liu {
497a0393e3eSHangbin Liu module_put(mode->owner);
498a0393e3eSHangbin Liu }
499a0393e3eSHangbin Liu
team_dummy_transmit(struct team * team,struct sk_buff * skb)500a0393e3eSHangbin Liu static bool team_dummy_transmit(struct team *team, struct sk_buff *skb)
501a0393e3eSHangbin Liu {
502a0393e3eSHangbin Liu dev_kfree_skb_any(skb);
503a0393e3eSHangbin Liu return false;
504a0393e3eSHangbin Liu }
505a0393e3eSHangbin Liu
team_dummy_receive(struct team * team,struct team_port * port,struct sk_buff * skb)506a0393e3eSHangbin Liu static rx_handler_result_t team_dummy_receive(struct team *team,
507a0393e3eSHangbin Liu struct team_port *port,
508a0393e3eSHangbin Liu struct sk_buff *skb)
509a0393e3eSHangbin Liu {
510a0393e3eSHangbin Liu return RX_HANDLER_ANOTHER;
511a0393e3eSHangbin Liu }
512a0393e3eSHangbin Liu
513a0393e3eSHangbin Liu static const struct team_mode __team_no_mode = {
514a0393e3eSHangbin Liu .kind = "*NOMODE*",
515a0393e3eSHangbin Liu };
516a0393e3eSHangbin Liu
team_is_mode_set(struct team * team)517a0393e3eSHangbin Liu static bool team_is_mode_set(struct team *team)
518a0393e3eSHangbin Liu {
519a0393e3eSHangbin Liu return team->mode != &__team_no_mode;
520a0393e3eSHangbin Liu }
521a0393e3eSHangbin Liu
team_set_no_mode(struct team * team)522a0393e3eSHangbin Liu static void team_set_no_mode(struct team *team)
523a0393e3eSHangbin Liu {
524a0393e3eSHangbin Liu team->user_carrier_enabled = false;
525a0393e3eSHangbin Liu team->mode = &__team_no_mode;
526a0393e3eSHangbin Liu }
527a0393e3eSHangbin Liu
team_adjust_ops(struct team * team)528a0393e3eSHangbin Liu static void team_adjust_ops(struct team *team)
529a0393e3eSHangbin Liu {
530a0393e3eSHangbin Liu /*
531a0393e3eSHangbin Liu * To avoid checks in rx/tx skb paths, ensure here that non-null and
532a0393e3eSHangbin Liu * correct ops are always set.
533a0393e3eSHangbin Liu */
534a0393e3eSHangbin Liu
535a0393e3eSHangbin Liu if (!team->en_port_count || !team_is_mode_set(team) ||
536a0393e3eSHangbin Liu !team->mode->ops->transmit)
537a0393e3eSHangbin Liu team->ops.transmit = team_dummy_transmit;
538a0393e3eSHangbin Liu else
539a0393e3eSHangbin Liu team->ops.transmit = team->mode->ops->transmit;
540a0393e3eSHangbin Liu
541a0393e3eSHangbin Liu if (!team->en_port_count || !team_is_mode_set(team) ||
542a0393e3eSHangbin Liu !team->mode->ops->receive)
543a0393e3eSHangbin Liu team->ops.receive = team_dummy_receive;
544a0393e3eSHangbin Liu else
545a0393e3eSHangbin Liu team->ops.receive = team->mode->ops->receive;
546a0393e3eSHangbin Liu }
547a0393e3eSHangbin Liu
548a0393e3eSHangbin Liu /*
549a0393e3eSHangbin Liu * We can benefit from the fact that it's ensured no port is present
550a0393e3eSHangbin Liu * at the time of mode change. Therefore no packets are in fly so there's no
551a0393e3eSHangbin Liu * need to set mode operations in any special way.
552a0393e3eSHangbin Liu */
__team_change_mode(struct team * team,const struct team_mode * new_mode)553a0393e3eSHangbin Liu static int __team_change_mode(struct team *team,
554a0393e3eSHangbin Liu const struct team_mode *new_mode)
555a0393e3eSHangbin Liu {
556a0393e3eSHangbin Liu /* Check if mode was previously set and do cleanup if so */
557a0393e3eSHangbin Liu if (team_is_mode_set(team)) {
558a0393e3eSHangbin Liu void (*exit_op)(struct team *team) = team->ops.exit;
559a0393e3eSHangbin Liu
560a0393e3eSHangbin Liu /* Clear ops area so no callback is called any longer */
561a0393e3eSHangbin Liu memset(&team->ops, 0, sizeof(struct team_mode_ops));
562a0393e3eSHangbin Liu team_adjust_ops(team);
563a0393e3eSHangbin Liu
564a0393e3eSHangbin Liu if (exit_op)
565a0393e3eSHangbin Liu exit_op(team);
566a0393e3eSHangbin Liu team_mode_put(team->mode);
567a0393e3eSHangbin Liu team_set_no_mode(team);
568a0393e3eSHangbin Liu /* zero private data area */
569a0393e3eSHangbin Liu memset(&team->mode_priv, 0,
570a0393e3eSHangbin Liu sizeof(struct team) - offsetof(struct team, mode_priv));
571a0393e3eSHangbin Liu }
572a0393e3eSHangbin Liu
573a0393e3eSHangbin Liu if (!new_mode)
574a0393e3eSHangbin Liu return 0;
575a0393e3eSHangbin Liu
576a0393e3eSHangbin Liu if (new_mode->ops->init) {
577a0393e3eSHangbin Liu int err;
578a0393e3eSHangbin Liu
579a0393e3eSHangbin Liu err = new_mode->ops->init(team);
580a0393e3eSHangbin Liu if (err)
581a0393e3eSHangbin Liu return err;
582a0393e3eSHangbin Liu }
583a0393e3eSHangbin Liu
584a0393e3eSHangbin Liu team->mode = new_mode;
585a0393e3eSHangbin Liu memcpy(&team->ops, new_mode->ops, sizeof(struct team_mode_ops));
586a0393e3eSHangbin Liu team_adjust_ops(team);
587a0393e3eSHangbin Liu
588a0393e3eSHangbin Liu return 0;
589a0393e3eSHangbin Liu }
590a0393e3eSHangbin Liu
team_change_mode(struct team * team,const char * kind)591a0393e3eSHangbin Liu static int team_change_mode(struct team *team, const char *kind)
592a0393e3eSHangbin Liu {
593a0393e3eSHangbin Liu const struct team_mode *new_mode;
594a0393e3eSHangbin Liu struct net_device *dev = team->dev;
595a0393e3eSHangbin Liu int err;
596a0393e3eSHangbin Liu
597a0393e3eSHangbin Liu if (!list_empty(&team->port_list)) {
598a0393e3eSHangbin Liu netdev_err(dev, "No ports can be present during mode change\n");
599a0393e3eSHangbin Liu return -EBUSY;
600a0393e3eSHangbin Liu }
601a0393e3eSHangbin Liu
602a0393e3eSHangbin Liu if (team_is_mode_set(team) && strcmp(team->mode->kind, kind) == 0) {
603a0393e3eSHangbin Liu netdev_err(dev, "Unable to change to the same mode the team is in\n");
604a0393e3eSHangbin Liu return -EINVAL;
605a0393e3eSHangbin Liu }
606a0393e3eSHangbin Liu
607a0393e3eSHangbin Liu new_mode = team_mode_get(kind);
608a0393e3eSHangbin Liu if (!new_mode) {
609a0393e3eSHangbin Liu netdev_err(dev, "Mode \"%s\" not found\n", kind);
610a0393e3eSHangbin Liu return -EINVAL;
611a0393e3eSHangbin Liu }
612a0393e3eSHangbin Liu
613a0393e3eSHangbin Liu err = __team_change_mode(team, new_mode);
614a0393e3eSHangbin Liu if (err) {
615a0393e3eSHangbin Liu netdev_err(dev, "Failed to change to mode \"%s\"\n", kind);
616a0393e3eSHangbin Liu team_mode_put(new_mode);
617a0393e3eSHangbin Liu return err;
618a0393e3eSHangbin Liu }
619a0393e3eSHangbin Liu
620a0393e3eSHangbin Liu netdev_info(dev, "Mode changed to \"%s\"\n", kind);
621a0393e3eSHangbin Liu return 0;
622a0393e3eSHangbin Liu }
623a0393e3eSHangbin Liu
624a0393e3eSHangbin Liu
625a0393e3eSHangbin Liu /*********************
626a0393e3eSHangbin Liu * Peers notification
627a0393e3eSHangbin Liu *********************/
628a0393e3eSHangbin Liu
team_notify_peers_work(struct work_struct * work)629a0393e3eSHangbin Liu static void team_notify_peers_work(struct work_struct *work)
630a0393e3eSHangbin Liu {
631a0393e3eSHangbin Liu struct team *team;
632a0393e3eSHangbin Liu int val;
633a0393e3eSHangbin Liu
634a0393e3eSHangbin Liu team = container_of(work, struct team, notify_peers.dw.work);
635a0393e3eSHangbin Liu
636a0393e3eSHangbin Liu if (!rtnl_trylock()) {
637a0393e3eSHangbin Liu schedule_delayed_work(&team->notify_peers.dw, 0);
638a0393e3eSHangbin Liu return;
639a0393e3eSHangbin Liu }
640a0393e3eSHangbin Liu val = atomic_dec_if_positive(&team->notify_peers.count_pending);
641a0393e3eSHangbin Liu if (val < 0) {
642a0393e3eSHangbin Liu rtnl_unlock();
643a0393e3eSHangbin Liu return;
644a0393e3eSHangbin Liu }
645a0393e3eSHangbin Liu call_netdevice_notifiers(NETDEV_NOTIFY_PEERS, team->dev);
646a0393e3eSHangbin Liu rtnl_unlock();
647a0393e3eSHangbin Liu if (val)
648a0393e3eSHangbin Liu schedule_delayed_work(&team->notify_peers.dw,
649a0393e3eSHangbin Liu msecs_to_jiffies(team->notify_peers.interval));
650a0393e3eSHangbin Liu }
651a0393e3eSHangbin Liu
team_notify_peers(struct team * team)652a0393e3eSHangbin Liu static void team_notify_peers(struct team *team)
653a0393e3eSHangbin Liu {
654a0393e3eSHangbin Liu if (!team->notify_peers.count || !netif_running(team->dev))
655a0393e3eSHangbin Liu return;
656a0393e3eSHangbin Liu atomic_add(team->notify_peers.count, &team->notify_peers.count_pending);
657a0393e3eSHangbin Liu schedule_delayed_work(&team->notify_peers.dw, 0);
658a0393e3eSHangbin Liu }
659a0393e3eSHangbin Liu
team_notify_peers_init(struct team * team)660a0393e3eSHangbin Liu static void team_notify_peers_init(struct team *team)
661a0393e3eSHangbin Liu {
662a0393e3eSHangbin Liu INIT_DELAYED_WORK(&team->notify_peers.dw, team_notify_peers_work);
663a0393e3eSHangbin Liu }
664a0393e3eSHangbin Liu
team_notify_peers_fini(struct team * team)665a0393e3eSHangbin Liu static void team_notify_peers_fini(struct team *team)
666a0393e3eSHangbin Liu {
667a0393e3eSHangbin Liu cancel_delayed_work_sync(&team->notify_peers.dw);
668a0393e3eSHangbin Liu }
669a0393e3eSHangbin Liu
670a0393e3eSHangbin Liu
671a0393e3eSHangbin Liu /*******************************
672a0393e3eSHangbin Liu * Send multicast group rejoins
673a0393e3eSHangbin Liu *******************************/
674a0393e3eSHangbin Liu
team_mcast_rejoin_work(struct work_struct * work)675a0393e3eSHangbin Liu static void team_mcast_rejoin_work(struct work_struct *work)
676a0393e3eSHangbin Liu {
677a0393e3eSHangbin Liu struct team *team;
678a0393e3eSHangbin Liu int val;
679a0393e3eSHangbin Liu
680a0393e3eSHangbin Liu team = container_of(work, struct team, mcast_rejoin.dw.work);
681a0393e3eSHangbin Liu
682a0393e3eSHangbin Liu if (!rtnl_trylock()) {
683a0393e3eSHangbin Liu schedule_delayed_work(&team->mcast_rejoin.dw, 0);
684a0393e3eSHangbin Liu return;
685a0393e3eSHangbin Liu }
686a0393e3eSHangbin Liu val = atomic_dec_if_positive(&team->mcast_rejoin.count_pending);
687a0393e3eSHangbin Liu if (val < 0) {
688a0393e3eSHangbin Liu rtnl_unlock();
689a0393e3eSHangbin Liu return;
690a0393e3eSHangbin Liu }
691a0393e3eSHangbin Liu call_netdevice_notifiers(NETDEV_RESEND_IGMP, team->dev);
692a0393e3eSHangbin Liu rtnl_unlock();
693a0393e3eSHangbin Liu if (val)
694a0393e3eSHangbin Liu schedule_delayed_work(&team->mcast_rejoin.dw,
695a0393e3eSHangbin Liu msecs_to_jiffies(team->mcast_rejoin.interval));
696a0393e3eSHangbin Liu }
697a0393e3eSHangbin Liu
team_mcast_rejoin(struct team * team)698a0393e3eSHangbin Liu static void team_mcast_rejoin(struct team *team)
699a0393e3eSHangbin Liu {
700a0393e3eSHangbin Liu if (!team->mcast_rejoin.count || !netif_running(team->dev))
701a0393e3eSHangbin Liu return;
702a0393e3eSHangbin Liu atomic_add(team->mcast_rejoin.count, &team->mcast_rejoin.count_pending);
703a0393e3eSHangbin Liu schedule_delayed_work(&team->mcast_rejoin.dw, 0);
704a0393e3eSHangbin Liu }
705a0393e3eSHangbin Liu
team_mcast_rejoin_init(struct team * team)706a0393e3eSHangbin Liu static void team_mcast_rejoin_init(struct team *team)
707a0393e3eSHangbin Liu {
708a0393e3eSHangbin Liu INIT_DELAYED_WORK(&team->mcast_rejoin.dw, team_mcast_rejoin_work);
709a0393e3eSHangbin Liu }
710a0393e3eSHangbin Liu
team_mcast_rejoin_fini(struct team * team)711a0393e3eSHangbin Liu static void team_mcast_rejoin_fini(struct team *team)
712a0393e3eSHangbin Liu {
713a0393e3eSHangbin Liu cancel_delayed_work_sync(&team->mcast_rejoin.dw);
714a0393e3eSHangbin Liu }
715a0393e3eSHangbin Liu
716a0393e3eSHangbin Liu
717a0393e3eSHangbin Liu /************************
718a0393e3eSHangbin Liu * Rx path frame handler
719a0393e3eSHangbin Liu ************************/
720a0393e3eSHangbin Liu
721a0393e3eSHangbin Liu /* note: already called with rcu_read_lock */
team_handle_frame(struct sk_buff ** pskb)722a0393e3eSHangbin Liu static rx_handler_result_t team_handle_frame(struct sk_buff **pskb)
723a0393e3eSHangbin Liu {
724a0393e3eSHangbin Liu struct sk_buff *skb = *pskb;
725a0393e3eSHangbin Liu struct team_port *port;
726a0393e3eSHangbin Liu struct team *team;
727a0393e3eSHangbin Liu rx_handler_result_t res;
728a0393e3eSHangbin Liu
729a0393e3eSHangbin Liu skb = skb_share_check(skb, GFP_ATOMIC);
730a0393e3eSHangbin Liu if (!skb)
731a0393e3eSHangbin Liu return RX_HANDLER_CONSUMED;
732a0393e3eSHangbin Liu
733a0393e3eSHangbin Liu *pskb = skb;
734a0393e3eSHangbin Liu
735a0393e3eSHangbin Liu port = team_port_get_rcu(skb->dev);
736a0393e3eSHangbin Liu team = port->team;
737a0393e3eSHangbin Liu if (!team_port_enabled(port)) {
738a0393e3eSHangbin Liu if (is_link_local_ether_addr(eth_hdr(skb)->h_dest))
739a0393e3eSHangbin Liu /* link-local packets are mostly useful when stack receives them
740a0393e3eSHangbin Liu * with the link they arrive on.
741a0393e3eSHangbin Liu */
742a0393e3eSHangbin Liu return RX_HANDLER_PASS;
743a0393e3eSHangbin Liu /* allow exact match delivery for disabled ports */
744a0393e3eSHangbin Liu res = RX_HANDLER_EXACT;
745a0393e3eSHangbin Liu } else {
746a0393e3eSHangbin Liu res = team->ops.receive(team, port, skb);
747a0393e3eSHangbin Liu }
748a0393e3eSHangbin Liu if (res == RX_HANDLER_ANOTHER) {
749a0393e3eSHangbin Liu struct team_pcpu_stats *pcpu_stats;
750a0393e3eSHangbin Liu
751a0393e3eSHangbin Liu pcpu_stats = this_cpu_ptr(team->pcpu_stats);
752a0393e3eSHangbin Liu u64_stats_update_begin(&pcpu_stats->syncp);
753a0393e3eSHangbin Liu u64_stats_inc(&pcpu_stats->rx_packets);
754a0393e3eSHangbin Liu u64_stats_add(&pcpu_stats->rx_bytes, skb->len);
755a0393e3eSHangbin Liu if (skb->pkt_type == PACKET_MULTICAST)
756a0393e3eSHangbin Liu u64_stats_inc(&pcpu_stats->rx_multicast);
757a0393e3eSHangbin Liu u64_stats_update_end(&pcpu_stats->syncp);
758a0393e3eSHangbin Liu
759a0393e3eSHangbin Liu skb->dev = team->dev;
760a0393e3eSHangbin Liu } else if (res == RX_HANDLER_EXACT) {
761a0393e3eSHangbin Liu this_cpu_inc(team->pcpu_stats->rx_nohandler);
762a0393e3eSHangbin Liu } else {
763a0393e3eSHangbin Liu this_cpu_inc(team->pcpu_stats->rx_dropped);
764a0393e3eSHangbin Liu }
765a0393e3eSHangbin Liu
766a0393e3eSHangbin Liu return res;
767a0393e3eSHangbin Liu }
768a0393e3eSHangbin Liu
769a0393e3eSHangbin Liu
770a0393e3eSHangbin Liu /*************************************
771a0393e3eSHangbin Liu * Multiqueue Tx port select override
772a0393e3eSHangbin Liu *************************************/
773a0393e3eSHangbin Liu
team_queue_override_init(struct team * team)774a0393e3eSHangbin Liu static int team_queue_override_init(struct team *team)
775a0393e3eSHangbin Liu {
776a0393e3eSHangbin Liu struct list_head *listarr;
777a0393e3eSHangbin Liu unsigned int queue_cnt = team->dev->num_tx_queues - 1;
778a0393e3eSHangbin Liu unsigned int i;
779a0393e3eSHangbin Liu
780a0393e3eSHangbin Liu if (!queue_cnt)
781a0393e3eSHangbin Liu return 0;
782a0393e3eSHangbin Liu listarr = kmalloc_array(queue_cnt, sizeof(struct list_head),
783a0393e3eSHangbin Liu GFP_KERNEL);
784a0393e3eSHangbin Liu if (!listarr)
785a0393e3eSHangbin Liu return -ENOMEM;
786a0393e3eSHangbin Liu team->qom_lists = listarr;
787a0393e3eSHangbin Liu for (i = 0; i < queue_cnt; i++)
788a0393e3eSHangbin Liu INIT_LIST_HEAD(listarr++);
789a0393e3eSHangbin Liu return 0;
790a0393e3eSHangbin Liu }
791a0393e3eSHangbin Liu
team_queue_override_fini(struct team * team)792a0393e3eSHangbin Liu static void team_queue_override_fini(struct team *team)
793a0393e3eSHangbin Liu {
794a0393e3eSHangbin Liu kfree(team->qom_lists);
795a0393e3eSHangbin Liu }
796a0393e3eSHangbin Liu
__team_get_qom_list(struct team * team,u16 queue_id)797a0393e3eSHangbin Liu static struct list_head *__team_get_qom_list(struct team *team, u16 queue_id)
798a0393e3eSHangbin Liu {
799a0393e3eSHangbin Liu return &team->qom_lists[queue_id - 1];
800a0393e3eSHangbin Liu }
801a0393e3eSHangbin Liu
802a0393e3eSHangbin Liu /*
803a0393e3eSHangbin Liu * note: already called with rcu_read_lock
804a0393e3eSHangbin Liu */
team_queue_override_transmit(struct team * team,struct sk_buff * skb)805a0393e3eSHangbin Liu static bool team_queue_override_transmit(struct team *team, struct sk_buff *skb)
806a0393e3eSHangbin Liu {
807a0393e3eSHangbin Liu struct list_head *qom_list;
808a0393e3eSHangbin Liu struct team_port *port;
809a0393e3eSHangbin Liu
810a0393e3eSHangbin Liu if (!team->queue_override_enabled || !skb->queue_mapping)
811a0393e3eSHangbin Liu return false;
812a0393e3eSHangbin Liu qom_list = __team_get_qom_list(team, skb->queue_mapping);
813a0393e3eSHangbin Liu list_for_each_entry_rcu(port, qom_list, qom_list) {
814a0393e3eSHangbin Liu if (!team_dev_queue_xmit(team, port, skb))
815a0393e3eSHangbin Liu return true;
816a0393e3eSHangbin Liu }
817a0393e3eSHangbin Liu return false;
818a0393e3eSHangbin Liu }
819a0393e3eSHangbin Liu
__team_queue_override_port_del(struct team * team,struct team_port * port)820a0393e3eSHangbin Liu static void __team_queue_override_port_del(struct team *team,
821a0393e3eSHangbin Liu struct team_port *port)
822a0393e3eSHangbin Liu {
823a0393e3eSHangbin Liu if (!port->queue_id)
824a0393e3eSHangbin Liu return;
825a0393e3eSHangbin Liu list_del_rcu(&port->qom_list);
826a0393e3eSHangbin Liu }
827a0393e3eSHangbin Liu
team_queue_override_port_has_gt_prio_than(struct team_port * port,struct team_port * cur)828a0393e3eSHangbin Liu static bool team_queue_override_port_has_gt_prio_than(struct team_port *port,
829a0393e3eSHangbin Liu struct team_port *cur)
830a0393e3eSHangbin Liu {
831a0393e3eSHangbin Liu if (port->priority < cur->priority)
832a0393e3eSHangbin Liu return true;
833a0393e3eSHangbin Liu if (port->priority > cur->priority)
834a0393e3eSHangbin Liu return false;
835a0393e3eSHangbin Liu if (port->index < cur->index)
836a0393e3eSHangbin Liu return true;
837a0393e3eSHangbin Liu return false;
838a0393e3eSHangbin Liu }
839a0393e3eSHangbin Liu
__team_queue_override_port_add(struct team * team,struct team_port * port)840a0393e3eSHangbin Liu static void __team_queue_override_port_add(struct team *team,
841a0393e3eSHangbin Liu struct team_port *port)
842a0393e3eSHangbin Liu {
843a0393e3eSHangbin Liu struct team_port *cur;
844a0393e3eSHangbin Liu struct list_head *qom_list;
845a0393e3eSHangbin Liu struct list_head *node;
846a0393e3eSHangbin Liu
847a0393e3eSHangbin Liu if (!port->queue_id)
848a0393e3eSHangbin Liu return;
849a0393e3eSHangbin Liu qom_list = __team_get_qom_list(team, port->queue_id);
850a0393e3eSHangbin Liu node = qom_list;
851a0393e3eSHangbin Liu list_for_each_entry(cur, qom_list, qom_list) {
852a0393e3eSHangbin Liu if (team_queue_override_port_has_gt_prio_than(port, cur))
853a0393e3eSHangbin Liu break;
854a0393e3eSHangbin Liu node = &cur->qom_list;
855a0393e3eSHangbin Liu }
856a0393e3eSHangbin Liu list_add_tail_rcu(&port->qom_list, node);
857a0393e3eSHangbin Liu }
858a0393e3eSHangbin Liu
__team_queue_override_enabled_check(struct team * team)859a0393e3eSHangbin Liu static void __team_queue_override_enabled_check(struct team *team)
860a0393e3eSHangbin Liu {
861a0393e3eSHangbin Liu struct team_port *port;
862a0393e3eSHangbin Liu bool enabled = false;
863a0393e3eSHangbin Liu
864a0393e3eSHangbin Liu list_for_each_entry(port, &team->port_list, list) {
865a0393e3eSHangbin Liu if (port->queue_id) {
866a0393e3eSHangbin Liu enabled = true;
867a0393e3eSHangbin Liu break;
868a0393e3eSHangbin Liu }
869a0393e3eSHangbin Liu }
870a0393e3eSHangbin Liu if (enabled == team->queue_override_enabled)
871a0393e3eSHangbin Liu return;
872a0393e3eSHangbin Liu netdev_dbg(team->dev, "%s queue override\n",
873a0393e3eSHangbin Liu enabled ? "Enabling" : "Disabling");
874a0393e3eSHangbin Liu team->queue_override_enabled = enabled;
875a0393e3eSHangbin Liu }
876a0393e3eSHangbin Liu
team_queue_override_port_prio_changed(struct team * team,struct team_port * port)877a0393e3eSHangbin Liu static void team_queue_override_port_prio_changed(struct team *team,
878a0393e3eSHangbin Liu struct team_port *port)
879a0393e3eSHangbin Liu {
880a0393e3eSHangbin Liu if (!port->queue_id || team_port_enabled(port))
881a0393e3eSHangbin Liu return;
882a0393e3eSHangbin Liu __team_queue_override_port_del(team, port);
883a0393e3eSHangbin Liu __team_queue_override_port_add(team, port);
884a0393e3eSHangbin Liu __team_queue_override_enabled_check(team);
885a0393e3eSHangbin Liu }
886a0393e3eSHangbin Liu
team_queue_override_port_change_queue_id(struct team * team,struct team_port * port,u16 new_queue_id)887a0393e3eSHangbin Liu static void team_queue_override_port_change_queue_id(struct team *team,
888a0393e3eSHangbin Liu struct team_port *port,
889a0393e3eSHangbin Liu u16 new_queue_id)
890a0393e3eSHangbin Liu {
891a0393e3eSHangbin Liu if (team_port_enabled(port)) {
892a0393e3eSHangbin Liu __team_queue_override_port_del(team, port);
893a0393e3eSHangbin Liu port->queue_id = new_queue_id;
894a0393e3eSHangbin Liu __team_queue_override_port_add(team, port);
895a0393e3eSHangbin Liu __team_queue_override_enabled_check(team);
896a0393e3eSHangbin Liu } else {
897a0393e3eSHangbin Liu port->queue_id = new_queue_id;
898a0393e3eSHangbin Liu }
899a0393e3eSHangbin Liu }
900a0393e3eSHangbin Liu
team_queue_override_port_add(struct team * team,struct team_port * port)901a0393e3eSHangbin Liu static void team_queue_override_port_add(struct team *team,
902a0393e3eSHangbin Liu struct team_port *port)
903a0393e3eSHangbin Liu {
904a0393e3eSHangbin Liu __team_queue_override_port_add(team, port);
905a0393e3eSHangbin Liu __team_queue_override_enabled_check(team);
906a0393e3eSHangbin Liu }
907a0393e3eSHangbin Liu
team_queue_override_port_del(struct team * team,struct team_port * port)908a0393e3eSHangbin Liu static void team_queue_override_port_del(struct team *team,
909a0393e3eSHangbin Liu struct team_port *port)
910a0393e3eSHangbin Liu {
911a0393e3eSHangbin Liu __team_queue_override_port_del(team, port);
912a0393e3eSHangbin Liu __team_queue_override_enabled_check(team);
913a0393e3eSHangbin Liu }
914a0393e3eSHangbin Liu
915a0393e3eSHangbin Liu
916a0393e3eSHangbin Liu /****************
917a0393e3eSHangbin Liu * Port handling
918a0393e3eSHangbin Liu ****************/
919a0393e3eSHangbin Liu
team_port_find(const struct team * team,const struct team_port * port)920a0393e3eSHangbin Liu static bool team_port_find(const struct team *team,
921a0393e3eSHangbin Liu const struct team_port *port)
922a0393e3eSHangbin Liu {
923a0393e3eSHangbin Liu struct team_port *cur;
924a0393e3eSHangbin Liu
925a0393e3eSHangbin Liu list_for_each_entry(cur, &team->port_list, list)
926a0393e3eSHangbin Liu if (cur == port)
927a0393e3eSHangbin Liu return true;
928a0393e3eSHangbin Liu return false;
929a0393e3eSHangbin Liu }
930a0393e3eSHangbin Liu
931a0393e3eSHangbin Liu /*
932a0393e3eSHangbin Liu * Enable/disable port by adding to enabled port hashlist and setting
933a0393e3eSHangbin Liu * port->index (Might be racy so reader could see incorrect ifindex when
934a0393e3eSHangbin Liu * processing a flying packet, but that is not a problem). Write guarded
935a0393e3eSHangbin Liu * by team->lock.
936a0393e3eSHangbin Liu */
team_port_enable(struct team * team,struct team_port * port)937a0393e3eSHangbin Liu static void team_port_enable(struct team *team,
938a0393e3eSHangbin Liu struct team_port *port)
939a0393e3eSHangbin Liu {
940a0393e3eSHangbin Liu if (team_port_enabled(port))
941a0393e3eSHangbin Liu return;
942a0393e3eSHangbin Liu port->index = team->en_port_count++;
943a0393e3eSHangbin Liu hlist_add_head_rcu(&port->hlist,
944a0393e3eSHangbin Liu team_port_index_hash(team, port->index));
945a0393e3eSHangbin Liu team_adjust_ops(team);
946a0393e3eSHangbin Liu team_queue_override_port_add(team, port);
947a0393e3eSHangbin Liu if (team->ops.port_enabled)
948a0393e3eSHangbin Liu team->ops.port_enabled(team, port);
949a0393e3eSHangbin Liu team_notify_peers(team);
950a0393e3eSHangbin Liu team_mcast_rejoin(team);
951a0393e3eSHangbin Liu team_lower_state_changed(port);
952a0393e3eSHangbin Liu }
953a0393e3eSHangbin Liu
__reconstruct_port_hlist(struct team * team,int rm_index)954a0393e3eSHangbin Liu static void __reconstruct_port_hlist(struct team *team, int rm_index)
955a0393e3eSHangbin Liu {
956a0393e3eSHangbin Liu int i;
957a0393e3eSHangbin Liu struct team_port *port;
958a0393e3eSHangbin Liu
959a0393e3eSHangbin Liu for (i = rm_index + 1; i < team->en_port_count; i++) {
960a0393e3eSHangbin Liu port = team_get_port_by_index(team, i);
961a0393e3eSHangbin Liu hlist_del_rcu(&port->hlist);
962a0393e3eSHangbin Liu port->index--;
963a0393e3eSHangbin Liu hlist_add_head_rcu(&port->hlist,
964a0393e3eSHangbin Liu team_port_index_hash(team, port->index));
965a0393e3eSHangbin Liu }
966a0393e3eSHangbin Liu }
967a0393e3eSHangbin Liu
team_port_disable(struct team * team,struct team_port * port)968a0393e3eSHangbin Liu static void team_port_disable(struct team *team,
969a0393e3eSHangbin Liu struct team_port *port)
970a0393e3eSHangbin Liu {
971a0393e3eSHangbin Liu if (!team_port_enabled(port))
972a0393e3eSHangbin Liu return;
973a0393e3eSHangbin Liu if (team->ops.port_disabled)
974a0393e3eSHangbin Liu team->ops.port_disabled(team, port);
975a0393e3eSHangbin Liu hlist_del_rcu(&port->hlist);
976a0393e3eSHangbin Liu __reconstruct_port_hlist(team, port->index);
977a0393e3eSHangbin Liu port->index = -1;
978a0393e3eSHangbin Liu team->en_port_count--;
979a0393e3eSHangbin Liu team_queue_override_port_del(team, port);
980a0393e3eSHangbin Liu team_adjust_ops(team);
981a0393e3eSHangbin Liu team_lower_state_changed(port);
982a0393e3eSHangbin Liu }
983a0393e3eSHangbin Liu
984a0393e3eSHangbin Liu #define TEAM_VLAN_FEATURES (NETIF_F_HW_CSUM | NETIF_F_SG | \
985a0393e3eSHangbin Liu NETIF_F_FRAGLIST | NETIF_F_GSO_SOFTWARE | \
986a0393e3eSHangbin Liu NETIF_F_HIGHDMA | NETIF_F_LRO)
987a0393e3eSHangbin Liu
988a0393e3eSHangbin Liu #define TEAM_ENC_FEATURES (NETIF_F_HW_CSUM | NETIF_F_SG | \
989a0393e3eSHangbin Liu NETIF_F_RXCSUM | NETIF_F_GSO_SOFTWARE)
990a0393e3eSHangbin Liu
__team_compute_features(struct team * team)991a0393e3eSHangbin Liu static void __team_compute_features(struct team *team)
992a0393e3eSHangbin Liu {
993a0393e3eSHangbin Liu struct team_port *port;
994a0393e3eSHangbin Liu netdev_features_t vlan_features = TEAM_VLAN_FEATURES &
995a0393e3eSHangbin Liu NETIF_F_ALL_FOR_ALL;
996a0393e3eSHangbin Liu netdev_features_t enc_features = TEAM_ENC_FEATURES;
997a0393e3eSHangbin Liu unsigned short max_hard_header_len = ETH_HLEN;
998a0393e3eSHangbin Liu unsigned int dst_release_flag = IFF_XMIT_DST_RELEASE |
999a0393e3eSHangbin Liu IFF_XMIT_DST_RELEASE_PERM;
1000a0393e3eSHangbin Liu
1001a0393e3eSHangbin Liu rcu_read_lock();
1002a0393e3eSHangbin Liu list_for_each_entry_rcu(port, &team->port_list, list) {
1003a0393e3eSHangbin Liu vlan_features = netdev_increment_features(vlan_features,
1004a0393e3eSHangbin Liu port->dev->vlan_features,
1005a0393e3eSHangbin Liu TEAM_VLAN_FEATURES);
1006a0393e3eSHangbin Liu enc_features =
1007a0393e3eSHangbin Liu netdev_increment_features(enc_features,
1008a0393e3eSHangbin Liu port->dev->hw_enc_features,
1009a0393e3eSHangbin Liu TEAM_ENC_FEATURES);
1010a0393e3eSHangbin Liu
1011a0393e3eSHangbin Liu
1012a0393e3eSHangbin Liu dst_release_flag &= port->dev->priv_flags;
1013a0393e3eSHangbin Liu if (port->dev->hard_header_len > max_hard_header_len)
1014a0393e3eSHangbin Liu max_hard_header_len = port->dev->hard_header_len;
1015a0393e3eSHangbin Liu }
1016a0393e3eSHangbin Liu rcu_read_unlock();
1017a0393e3eSHangbin Liu
1018a0393e3eSHangbin Liu team->dev->vlan_features = vlan_features;
1019a0393e3eSHangbin Liu team->dev->hw_enc_features = enc_features | NETIF_F_GSO_ENCAP_ALL |
1020a0393e3eSHangbin Liu NETIF_F_HW_VLAN_CTAG_TX |
1021a0393e3eSHangbin Liu NETIF_F_HW_VLAN_STAG_TX;
1022a0393e3eSHangbin Liu team->dev->hard_header_len = max_hard_header_len;
1023a0393e3eSHangbin Liu
1024a0393e3eSHangbin Liu team->dev->priv_flags &= ~IFF_XMIT_DST_RELEASE;
1025a0393e3eSHangbin Liu if (dst_release_flag == (IFF_XMIT_DST_RELEASE | IFF_XMIT_DST_RELEASE_PERM))
1026a0393e3eSHangbin Liu team->dev->priv_flags |= IFF_XMIT_DST_RELEASE;
1027a0393e3eSHangbin Liu }
1028a0393e3eSHangbin Liu
team_compute_features(struct team * team)1029a0393e3eSHangbin Liu static void team_compute_features(struct team *team)
1030a0393e3eSHangbin Liu {
1031a0393e3eSHangbin Liu __team_compute_features(team);
1032a0393e3eSHangbin Liu netdev_change_features(team->dev);
1033a0393e3eSHangbin Liu }
1034a0393e3eSHangbin Liu
team_port_enter(struct team * team,struct team_port * port)1035a0393e3eSHangbin Liu static int team_port_enter(struct team *team, struct team_port *port)
1036a0393e3eSHangbin Liu {
1037a0393e3eSHangbin Liu int err = 0;
1038a0393e3eSHangbin Liu
1039a0393e3eSHangbin Liu dev_hold(team->dev);
1040a0393e3eSHangbin Liu if (team->ops.port_enter) {
1041a0393e3eSHangbin Liu err = team->ops.port_enter(team, port);
1042a0393e3eSHangbin Liu if (err) {
1043a0393e3eSHangbin Liu netdev_err(team->dev, "Device %s failed to enter team mode\n",
1044a0393e3eSHangbin Liu port->dev->name);
1045a0393e3eSHangbin Liu goto err_port_enter;
1046a0393e3eSHangbin Liu }
1047a0393e3eSHangbin Liu }
1048a0393e3eSHangbin Liu
1049a0393e3eSHangbin Liu return 0;
1050a0393e3eSHangbin Liu
1051a0393e3eSHangbin Liu err_port_enter:
1052a0393e3eSHangbin Liu dev_put(team->dev);
1053a0393e3eSHangbin Liu
1054a0393e3eSHangbin Liu return err;
1055a0393e3eSHangbin Liu }
1056a0393e3eSHangbin Liu
team_port_leave(struct team * team,struct team_port * port)1057a0393e3eSHangbin Liu static void team_port_leave(struct team *team, struct team_port *port)
1058a0393e3eSHangbin Liu {
1059a0393e3eSHangbin Liu if (team->ops.port_leave)
1060a0393e3eSHangbin Liu team->ops.port_leave(team, port);
1061a0393e3eSHangbin Liu dev_put(team->dev);
1062a0393e3eSHangbin Liu }
1063a0393e3eSHangbin Liu
1064a0393e3eSHangbin Liu #ifdef CONFIG_NET_POLL_CONTROLLER
__team_port_enable_netpoll(struct team_port * port)1065a0393e3eSHangbin Liu static int __team_port_enable_netpoll(struct team_port *port)
1066a0393e3eSHangbin Liu {
1067a0393e3eSHangbin Liu struct netpoll *np;
1068a0393e3eSHangbin Liu int err;
1069a0393e3eSHangbin Liu
1070a0393e3eSHangbin Liu np = kzalloc(sizeof(*np), GFP_KERNEL);
1071a0393e3eSHangbin Liu if (!np)
1072a0393e3eSHangbin Liu return -ENOMEM;
1073a0393e3eSHangbin Liu
1074a0393e3eSHangbin Liu err = __netpoll_setup(np, port->dev);
1075a0393e3eSHangbin Liu if (err) {
1076a0393e3eSHangbin Liu kfree(np);
1077a0393e3eSHangbin Liu return err;
1078a0393e3eSHangbin Liu }
1079a0393e3eSHangbin Liu port->np = np;
1080a0393e3eSHangbin Liu return err;
1081a0393e3eSHangbin Liu }
1082a0393e3eSHangbin Liu
team_port_enable_netpoll(struct team_port * port)1083a0393e3eSHangbin Liu static int team_port_enable_netpoll(struct team_port *port)
1084a0393e3eSHangbin Liu {
1085a0393e3eSHangbin Liu if (!port->team->dev->npinfo)
1086a0393e3eSHangbin Liu return 0;
1087a0393e3eSHangbin Liu
1088a0393e3eSHangbin Liu return __team_port_enable_netpoll(port);
1089a0393e3eSHangbin Liu }
1090a0393e3eSHangbin Liu
team_port_disable_netpoll(struct team_port * port)1091a0393e3eSHangbin Liu static void team_port_disable_netpoll(struct team_port *port)
1092a0393e3eSHangbin Liu {
1093a0393e3eSHangbin Liu struct netpoll *np = port->np;
1094a0393e3eSHangbin Liu
1095a0393e3eSHangbin Liu if (!np)
1096a0393e3eSHangbin Liu return;
1097a0393e3eSHangbin Liu port->np = NULL;
1098a0393e3eSHangbin Liu
1099a0393e3eSHangbin Liu __netpoll_free(np);
1100a0393e3eSHangbin Liu }
1101a0393e3eSHangbin Liu #else
team_port_enable_netpoll(struct team_port * port)1102a0393e3eSHangbin Liu static int team_port_enable_netpoll(struct team_port *port)
1103a0393e3eSHangbin Liu {
1104a0393e3eSHangbin Liu return 0;
1105a0393e3eSHangbin Liu }
team_port_disable_netpoll(struct team_port * port)1106a0393e3eSHangbin Liu static void team_port_disable_netpoll(struct team_port *port)
1107a0393e3eSHangbin Liu {
1108a0393e3eSHangbin Liu }
1109a0393e3eSHangbin Liu #endif
1110a0393e3eSHangbin Liu
team_upper_dev_link(struct team * team,struct team_port * port,struct netlink_ext_ack * extack)1111a0393e3eSHangbin Liu static int team_upper_dev_link(struct team *team, struct team_port *port,
1112a0393e3eSHangbin Liu struct netlink_ext_ack *extack)
1113a0393e3eSHangbin Liu {
1114a0393e3eSHangbin Liu struct netdev_lag_upper_info lag_upper_info;
1115a0393e3eSHangbin Liu int err;
1116a0393e3eSHangbin Liu
1117a0393e3eSHangbin Liu lag_upper_info.tx_type = team->mode->lag_tx_type;
1118a0393e3eSHangbin Liu lag_upper_info.hash_type = NETDEV_LAG_HASH_UNKNOWN;
1119a0393e3eSHangbin Liu err = netdev_master_upper_dev_link(port->dev, team->dev, NULL,
1120a0393e3eSHangbin Liu &lag_upper_info, extack);
1121a0393e3eSHangbin Liu if (err)
1122a0393e3eSHangbin Liu return err;
1123a0393e3eSHangbin Liu port->dev->priv_flags |= IFF_TEAM_PORT;
1124a0393e3eSHangbin Liu return 0;
1125a0393e3eSHangbin Liu }
1126a0393e3eSHangbin Liu
team_upper_dev_unlink(struct team * team,struct team_port * port)1127a0393e3eSHangbin Liu static void team_upper_dev_unlink(struct team *team, struct team_port *port)
1128a0393e3eSHangbin Liu {
1129a0393e3eSHangbin Liu netdev_upper_dev_unlink(port->dev, team->dev);
1130a0393e3eSHangbin Liu port->dev->priv_flags &= ~IFF_TEAM_PORT;
1131a0393e3eSHangbin Liu }
1132a0393e3eSHangbin Liu
1133a0393e3eSHangbin Liu static void __team_port_change_port_added(struct team_port *port, bool linkup);
1134a0393e3eSHangbin Liu static int team_dev_type_check_change(struct net_device *dev,
1135a0393e3eSHangbin Liu struct net_device *port_dev);
1136a0393e3eSHangbin Liu
team_port_add(struct team * team,struct net_device * port_dev,struct netlink_ext_ack * extack)1137a0393e3eSHangbin Liu static int team_port_add(struct team *team, struct net_device *port_dev,
1138a0393e3eSHangbin Liu struct netlink_ext_ack *extack)
1139a0393e3eSHangbin Liu {
1140a0393e3eSHangbin Liu struct net_device *dev = team->dev;
1141a0393e3eSHangbin Liu struct team_port *port;
1142a0393e3eSHangbin Liu char *portname = port_dev->name;
1143a0393e3eSHangbin Liu int err;
1144a0393e3eSHangbin Liu
1145a0393e3eSHangbin Liu if (port_dev->flags & IFF_LOOPBACK) {
1146a0393e3eSHangbin Liu NL_SET_ERR_MSG(extack, "Loopback device can't be added as a team port");
1147a0393e3eSHangbin Liu netdev_err(dev, "Device %s is loopback device. Loopback devices can't be added as a team port\n",
1148a0393e3eSHangbin Liu portname);
1149a0393e3eSHangbin Liu return -EINVAL;
1150a0393e3eSHangbin Liu }
1151a0393e3eSHangbin Liu
1152a0393e3eSHangbin Liu if (netif_is_team_port(port_dev)) {
1153a0393e3eSHangbin Liu NL_SET_ERR_MSG(extack, "Device is already a port of a team device");
1154a0393e3eSHangbin Liu netdev_err(dev, "Device %s is already a port "
1155a0393e3eSHangbin Liu "of a team device\n", portname);
1156a0393e3eSHangbin Liu return -EBUSY;
1157a0393e3eSHangbin Liu }
1158a0393e3eSHangbin Liu
1159a0393e3eSHangbin Liu if (dev == port_dev) {
1160a0393e3eSHangbin Liu NL_SET_ERR_MSG(extack, "Cannot enslave team device to itself");
1161a0393e3eSHangbin Liu netdev_err(dev, "Cannot enslave team device to itself\n");
1162a0393e3eSHangbin Liu return -EINVAL;
1163a0393e3eSHangbin Liu }
1164a0393e3eSHangbin Liu
1165a0393e3eSHangbin Liu if (netdev_has_upper_dev(dev, port_dev)) {
1166a0393e3eSHangbin Liu NL_SET_ERR_MSG(extack, "Device is already an upper device of the team interface");
1167a0393e3eSHangbin Liu netdev_err(dev, "Device %s is already an upper device of the team interface\n",
1168a0393e3eSHangbin Liu portname);
1169a0393e3eSHangbin Liu return -EBUSY;
1170a0393e3eSHangbin Liu }
1171a0393e3eSHangbin Liu
1172a0393e3eSHangbin Liu if (port_dev->features & NETIF_F_VLAN_CHALLENGED &&
1173a0393e3eSHangbin Liu vlan_uses_dev(dev)) {
1174a0393e3eSHangbin Liu NL_SET_ERR_MSG(extack, "Device is VLAN challenged and team device has VLAN set up");
1175a0393e3eSHangbin Liu netdev_err(dev, "Device %s is VLAN challenged and team device has VLAN set up\n",
1176a0393e3eSHangbin Liu portname);
1177a0393e3eSHangbin Liu return -EPERM;
1178a0393e3eSHangbin Liu }
1179a0393e3eSHangbin Liu
1180a0393e3eSHangbin Liu err = team_dev_type_check_change(dev, port_dev);
1181a0393e3eSHangbin Liu if (err)
1182a0393e3eSHangbin Liu return err;
1183a0393e3eSHangbin Liu
1184a0393e3eSHangbin Liu if (port_dev->flags & IFF_UP) {
1185a0393e3eSHangbin Liu NL_SET_ERR_MSG(extack, "Device is up. Set it down before adding it as a team port");
1186a0393e3eSHangbin Liu netdev_err(dev, "Device %s is up. Set it down before adding it as a team port\n",
1187a0393e3eSHangbin Liu portname);
1188a0393e3eSHangbin Liu return -EBUSY;
1189a0393e3eSHangbin Liu }
1190a0393e3eSHangbin Liu
1191a0393e3eSHangbin Liu port = kzalloc(sizeof(struct team_port) + team->mode->port_priv_size,
1192a0393e3eSHangbin Liu GFP_KERNEL);
1193a0393e3eSHangbin Liu if (!port)
1194a0393e3eSHangbin Liu return -ENOMEM;
1195a0393e3eSHangbin Liu
1196a0393e3eSHangbin Liu port->dev = port_dev;
1197a0393e3eSHangbin Liu port->team = team;
1198a0393e3eSHangbin Liu INIT_LIST_HEAD(&port->qom_list);
1199a0393e3eSHangbin Liu
1200a0393e3eSHangbin Liu port->orig.mtu = port_dev->mtu;
1201a0393e3eSHangbin Liu err = dev_set_mtu(port_dev, dev->mtu);
1202a0393e3eSHangbin Liu if (err) {
1203a0393e3eSHangbin Liu netdev_dbg(dev, "Error %d calling dev_set_mtu\n", err);
1204a0393e3eSHangbin Liu goto err_set_mtu;
1205a0393e3eSHangbin Liu }
1206a0393e3eSHangbin Liu
1207a0393e3eSHangbin Liu memcpy(port->orig.dev_addr, port_dev->dev_addr, port_dev->addr_len);
1208a0393e3eSHangbin Liu
1209a0393e3eSHangbin Liu err = team_port_enter(team, port);
1210a0393e3eSHangbin Liu if (err) {
1211a0393e3eSHangbin Liu netdev_err(dev, "Device %s failed to enter team mode\n",
1212a0393e3eSHangbin Liu portname);
1213a0393e3eSHangbin Liu goto err_port_enter;
1214a0393e3eSHangbin Liu }
1215a0393e3eSHangbin Liu
1216a0393e3eSHangbin Liu err = dev_open(port_dev, extack);
1217a0393e3eSHangbin Liu if (err) {
1218a0393e3eSHangbin Liu netdev_dbg(dev, "Device %s opening failed\n",
1219a0393e3eSHangbin Liu portname);
1220a0393e3eSHangbin Liu goto err_dev_open;
1221a0393e3eSHangbin Liu }
1222a0393e3eSHangbin Liu
1223a0393e3eSHangbin Liu err = vlan_vids_add_by_dev(port_dev, dev);
1224a0393e3eSHangbin Liu if (err) {
1225a0393e3eSHangbin Liu netdev_err(dev, "Failed to add vlan ids to device %s\n",
1226a0393e3eSHangbin Liu portname);
1227a0393e3eSHangbin Liu goto err_vids_add;
1228a0393e3eSHangbin Liu }
1229a0393e3eSHangbin Liu
1230a0393e3eSHangbin Liu err = team_port_enable_netpoll(port);
1231a0393e3eSHangbin Liu if (err) {
1232a0393e3eSHangbin Liu netdev_err(dev, "Failed to enable netpoll on device %s\n",
1233a0393e3eSHangbin Liu portname);
1234a0393e3eSHangbin Liu goto err_enable_netpoll;
1235a0393e3eSHangbin Liu }
1236a0393e3eSHangbin Liu
1237a0393e3eSHangbin Liu if (!(dev->features & NETIF_F_LRO))
1238a0393e3eSHangbin Liu dev_disable_lro(port_dev);
1239a0393e3eSHangbin Liu
1240a0393e3eSHangbin Liu err = netdev_rx_handler_register(port_dev, team_handle_frame,
1241a0393e3eSHangbin Liu port);
1242a0393e3eSHangbin Liu if (err) {
1243a0393e3eSHangbin Liu netdev_err(dev, "Device %s failed to register rx_handler\n",
1244a0393e3eSHangbin Liu portname);
1245a0393e3eSHangbin Liu goto err_handler_register;
1246a0393e3eSHangbin Liu }
1247a0393e3eSHangbin Liu
1248a0393e3eSHangbin Liu err = team_upper_dev_link(team, port, extack);
1249a0393e3eSHangbin Liu if (err) {
1250a0393e3eSHangbin Liu netdev_err(dev, "Device %s failed to set upper link\n",
1251a0393e3eSHangbin Liu portname);
1252a0393e3eSHangbin Liu goto err_set_upper_link;
1253a0393e3eSHangbin Liu }
1254a0393e3eSHangbin Liu
1255a0393e3eSHangbin Liu err = __team_option_inst_add_port(team, port);
1256a0393e3eSHangbin Liu if (err) {
1257a0393e3eSHangbin Liu netdev_err(dev, "Device %s failed to add per-port options\n",
1258a0393e3eSHangbin Liu portname);
1259a0393e3eSHangbin Liu goto err_option_port_add;
1260a0393e3eSHangbin Liu }
1261a0393e3eSHangbin Liu
1262a0393e3eSHangbin Liu /* set promiscuity level to new slave */
1263a0393e3eSHangbin Liu if (dev->flags & IFF_PROMISC) {
1264a0393e3eSHangbin Liu err = dev_set_promiscuity(port_dev, 1);
1265a0393e3eSHangbin Liu if (err)
1266a0393e3eSHangbin Liu goto err_set_slave_promisc;
1267a0393e3eSHangbin Liu }
1268a0393e3eSHangbin Liu
1269a0393e3eSHangbin Liu /* set allmulti level to new slave */
1270a0393e3eSHangbin Liu if (dev->flags & IFF_ALLMULTI) {
1271a0393e3eSHangbin Liu err = dev_set_allmulti(port_dev, 1);
1272a0393e3eSHangbin Liu if (err) {
1273a0393e3eSHangbin Liu if (dev->flags & IFF_PROMISC)
1274a0393e3eSHangbin Liu dev_set_promiscuity(port_dev, -1);
1275a0393e3eSHangbin Liu goto err_set_slave_promisc;
1276a0393e3eSHangbin Liu }
1277a0393e3eSHangbin Liu }
1278a0393e3eSHangbin Liu
1279a0393e3eSHangbin Liu if (dev->flags & IFF_UP) {
1280a0393e3eSHangbin Liu netif_addr_lock_bh(dev);
1281a0393e3eSHangbin Liu dev_uc_sync_multiple(port_dev, dev);
1282a0393e3eSHangbin Liu dev_mc_sync_multiple(port_dev, dev);
1283a0393e3eSHangbin Liu netif_addr_unlock_bh(dev);
1284a0393e3eSHangbin Liu }
1285a0393e3eSHangbin Liu
1286a0393e3eSHangbin Liu port->index = -1;
1287a0393e3eSHangbin Liu list_add_tail_rcu(&port->list, &team->port_list);
1288a0393e3eSHangbin Liu team_port_enable(team, port);
1289a0393e3eSHangbin Liu __team_compute_features(team);
1290a0393e3eSHangbin Liu __team_port_change_port_added(port, !!netif_oper_up(port_dev));
1291a0393e3eSHangbin Liu __team_options_change_check(team);
1292a0393e3eSHangbin Liu
1293a0393e3eSHangbin Liu netdev_info(dev, "Port device %s added\n", portname);
1294a0393e3eSHangbin Liu
1295a0393e3eSHangbin Liu return 0;
1296a0393e3eSHangbin Liu
1297a0393e3eSHangbin Liu err_set_slave_promisc:
1298a0393e3eSHangbin Liu __team_option_inst_del_port(team, port);
1299a0393e3eSHangbin Liu
1300a0393e3eSHangbin Liu err_option_port_add:
1301a0393e3eSHangbin Liu team_upper_dev_unlink(team, port);
1302a0393e3eSHangbin Liu
1303a0393e3eSHangbin Liu err_set_upper_link:
1304a0393e3eSHangbin Liu netdev_rx_handler_unregister(port_dev);
1305a0393e3eSHangbin Liu
1306a0393e3eSHangbin Liu err_handler_register:
1307a0393e3eSHangbin Liu team_port_disable_netpoll(port);
1308a0393e3eSHangbin Liu
1309a0393e3eSHangbin Liu err_enable_netpoll:
1310a0393e3eSHangbin Liu vlan_vids_del_by_dev(port_dev, dev);
1311a0393e3eSHangbin Liu
1312a0393e3eSHangbin Liu err_vids_add:
1313a0393e3eSHangbin Liu dev_close(port_dev);
1314a0393e3eSHangbin Liu
1315a0393e3eSHangbin Liu err_dev_open:
1316a0393e3eSHangbin Liu team_port_leave(team, port);
1317a0393e3eSHangbin Liu team_port_set_orig_dev_addr(port);
1318a0393e3eSHangbin Liu
1319a0393e3eSHangbin Liu err_port_enter:
1320a0393e3eSHangbin Liu dev_set_mtu(port_dev, port->orig.mtu);
1321a0393e3eSHangbin Liu
1322a0393e3eSHangbin Liu err_set_mtu:
1323a0393e3eSHangbin Liu kfree(port);
1324a0393e3eSHangbin Liu
1325a0393e3eSHangbin Liu return err;
1326a0393e3eSHangbin Liu }
1327a0393e3eSHangbin Liu
1328a0393e3eSHangbin Liu static void __team_port_change_port_removed(struct team_port *port);
1329a0393e3eSHangbin Liu
team_port_del(struct team * team,struct net_device * port_dev)1330a0393e3eSHangbin Liu static int team_port_del(struct team *team, struct net_device *port_dev)
1331a0393e3eSHangbin Liu {
1332a0393e3eSHangbin Liu struct net_device *dev = team->dev;
1333a0393e3eSHangbin Liu struct team_port *port;
1334a0393e3eSHangbin Liu char *portname = port_dev->name;
1335a0393e3eSHangbin Liu
1336a0393e3eSHangbin Liu port = team_port_get_rtnl(port_dev);
1337a0393e3eSHangbin Liu if (!port || !team_port_find(team, port)) {
1338a0393e3eSHangbin Liu netdev_err(dev, "Device %s does not act as a port of this team\n",
1339a0393e3eSHangbin Liu portname);
1340a0393e3eSHangbin Liu return -ENOENT;
1341a0393e3eSHangbin Liu }
1342a0393e3eSHangbin Liu
1343a0393e3eSHangbin Liu team_port_disable(team, port);
1344a0393e3eSHangbin Liu list_del_rcu(&port->list);
1345a0393e3eSHangbin Liu
1346a0393e3eSHangbin Liu if (dev->flags & IFF_PROMISC)
1347a0393e3eSHangbin Liu dev_set_promiscuity(port_dev, -1);
1348a0393e3eSHangbin Liu if (dev->flags & IFF_ALLMULTI)
1349a0393e3eSHangbin Liu dev_set_allmulti(port_dev, -1);
1350a0393e3eSHangbin Liu
1351a0393e3eSHangbin Liu team_upper_dev_unlink(team, port);
1352a0393e3eSHangbin Liu netdev_rx_handler_unregister(port_dev);
1353a0393e3eSHangbin Liu team_port_disable_netpoll(port);
1354a0393e3eSHangbin Liu vlan_vids_del_by_dev(port_dev, dev);
1355a0393e3eSHangbin Liu if (dev->flags & IFF_UP) {
1356a0393e3eSHangbin Liu dev_uc_unsync(port_dev, dev);
1357a0393e3eSHangbin Liu dev_mc_unsync(port_dev, dev);
1358a0393e3eSHangbin Liu }
1359a0393e3eSHangbin Liu dev_close(port_dev);
1360a0393e3eSHangbin Liu team_port_leave(team, port);
1361a0393e3eSHangbin Liu
1362a0393e3eSHangbin Liu __team_option_inst_mark_removed_port(team, port);
1363a0393e3eSHangbin Liu __team_options_change_check(team);
1364a0393e3eSHangbin Liu __team_option_inst_del_port(team, port);
1365a0393e3eSHangbin Liu __team_port_change_port_removed(port);
1366a0393e3eSHangbin Liu
1367a0393e3eSHangbin Liu team_port_set_orig_dev_addr(port);
1368a0393e3eSHangbin Liu dev_set_mtu(port_dev, port->orig.mtu);
1369a0393e3eSHangbin Liu kfree_rcu(port, rcu);
1370a0393e3eSHangbin Liu netdev_info(dev, "Port device %s removed\n", portname);
1371a0393e3eSHangbin Liu __team_compute_features(team);
1372a0393e3eSHangbin Liu
1373a0393e3eSHangbin Liu return 0;
1374a0393e3eSHangbin Liu }
1375a0393e3eSHangbin Liu
1376a0393e3eSHangbin Liu
1377a0393e3eSHangbin Liu /*****************
1378a0393e3eSHangbin Liu * Net device ops
1379a0393e3eSHangbin Liu *****************/
1380a0393e3eSHangbin Liu
team_mode_option_get(struct team * team,struct team_gsetter_ctx * ctx)1381a0393e3eSHangbin Liu static void team_mode_option_get(struct team *team, struct team_gsetter_ctx *ctx)
1382a0393e3eSHangbin Liu {
1383a0393e3eSHangbin Liu ctx->data.str_val = team->mode->kind;
1384a0393e3eSHangbin Liu }
1385a0393e3eSHangbin Liu
team_mode_option_set(struct team * team,struct team_gsetter_ctx * ctx)1386a0393e3eSHangbin Liu static int team_mode_option_set(struct team *team, struct team_gsetter_ctx *ctx)
1387a0393e3eSHangbin Liu {
1388a0393e3eSHangbin Liu return team_change_mode(team, ctx->data.str_val);
1389a0393e3eSHangbin Liu }
1390a0393e3eSHangbin Liu
team_notify_peers_count_get(struct team * team,struct team_gsetter_ctx * ctx)1391a0393e3eSHangbin Liu static void team_notify_peers_count_get(struct team *team,
1392a0393e3eSHangbin Liu struct team_gsetter_ctx *ctx)
1393a0393e3eSHangbin Liu {
1394a0393e3eSHangbin Liu ctx->data.u32_val = team->notify_peers.count;
1395a0393e3eSHangbin Liu }
1396a0393e3eSHangbin Liu
team_notify_peers_count_set(struct team * team,struct team_gsetter_ctx * ctx)1397a0393e3eSHangbin Liu static int team_notify_peers_count_set(struct team *team,
1398a0393e3eSHangbin Liu struct team_gsetter_ctx *ctx)
1399a0393e3eSHangbin Liu {
1400a0393e3eSHangbin Liu team->notify_peers.count = ctx->data.u32_val;
1401a0393e3eSHangbin Liu return 0;
1402a0393e3eSHangbin Liu }
1403a0393e3eSHangbin Liu
team_notify_peers_interval_get(struct team * team,struct team_gsetter_ctx * ctx)1404a0393e3eSHangbin Liu static void team_notify_peers_interval_get(struct team *team,
1405a0393e3eSHangbin Liu struct team_gsetter_ctx *ctx)
1406a0393e3eSHangbin Liu {
1407a0393e3eSHangbin Liu ctx->data.u32_val = team->notify_peers.interval;
1408a0393e3eSHangbin Liu }
1409a0393e3eSHangbin Liu
team_notify_peers_interval_set(struct team * team,struct team_gsetter_ctx * ctx)1410a0393e3eSHangbin Liu static int team_notify_peers_interval_set(struct team *team,
1411a0393e3eSHangbin Liu struct team_gsetter_ctx *ctx)
1412a0393e3eSHangbin Liu {
1413a0393e3eSHangbin Liu team->notify_peers.interval = ctx->data.u32_val;
1414a0393e3eSHangbin Liu return 0;
1415a0393e3eSHangbin Liu }
1416a0393e3eSHangbin Liu
team_mcast_rejoin_count_get(struct team * team,struct team_gsetter_ctx * ctx)1417a0393e3eSHangbin Liu static void team_mcast_rejoin_count_get(struct team *team,
1418a0393e3eSHangbin Liu struct team_gsetter_ctx *ctx)
1419a0393e3eSHangbin Liu {
1420a0393e3eSHangbin Liu ctx->data.u32_val = team->mcast_rejoin.count;
1421a0393e3eSHangbin Liu }
1422a0393e3eSHangbin Liu
team_mcast_rejoin_count_set(struct team * team,struct team_gsetter_ctx * ctx)1423a0393e3eSHangbin Liu static int team_mcast_rejoin_count_set(struct team *team,
1424a0393e3eSHangbin Liu struct team_gsetter_ctx *ctx)
1425a0393e3eSHangbin Liu {
1426a0393e3eSHangbin Liu team->mcast_rejoin.count = ctx->data.u32_val;
1427a0393e3eSHangbin Liu return 0;
1428a0393e3eSHangbin Liu }
1429a0393e3eSHangbin Liu
team_mcast_rejoin_interval_get(struct team * team,struct team_gsetter_ctx * ctx)1430a0393e3eSHangbin Liu static void team_mcast_rejoin_interval_get(struct team *team,
1431a0393e3eSHangbin Liu struct team_gsetter_ctx *ctx)
1432a0393e3eSHangbin Liu {
1433a0393e3eSHangbin Liu ctx->data.u32_val = team->mcast_rejoin.interval;
1434a0393e3eSHangbin Liu }
1435a0393e3eSHangbin Liu
team_mcast_rejoin_interval_set(struct team * team,struct team_gsetter_ctx * ctx)1436a0393e3eSHangbin Liu static int team_mcast_rejoin_interval_set(struct team *team,
1437a0393e3eSHangbin Liu struct team_gsetter_ctx *ctx)
1438a0393e3eSHangbin Liu {
1439a0393e3eSHangbin Liu team->mcast_rejoin.interval = ctx->data.u32_val;
1440a0393e3eSHangbin Liu return 0;
1441a0393e3eSHangbin Liu }
1442a0393e3eSHangbin Liu
team_port_en_option_get(struct team * team,struct team_gsetter_ctx * ctx)1443a0393e3eSHangbin Liu static void team_port_en_option_get(struct team *team,
1444a0393e3eSHangbin Liu struct team_gsetter_ctx *ctx)
1445a0393e3eSHangbin Liu {
1446a0393e3eSHangbin Liu struct team_port *port = ctx->info->port;
1447a0393e3eSHangbin Liu
1448a0393e3eSHangbin Liu ctx->data.bool_val = team_port_enabled(port);
1449a0393e3eSHangbin Liu }
1450a0393e3eSHangbin Liu
team_port_en_option_set(struct team * team,struct team_gsetter_ctx * ctx)1451a0393e3eSHangbin Liu static int team_port_en_option_set(struct team *team,
1452a0393e3eSHangbin Liu struct team_gsetter_ctx *ctx)
1453a0393e3eSHangbin Liu {
1454a0393e3eSHangbin Liu struct team_port *port = ctx->info->port;
1455a0393e3eSHangbin Liu
1456a0393e3eSHangbin Liu if (ctx->data.bool_val)
1457a0393e3eSHangbin Liu team_port_enable(team, port);
1458a0393e3eSHangbin Liu else
1459a0393e3eSHangbin Liu team_port_disable(team, port);
1460a0393e3eSHangbin Liu return 0;
1461a0393e3eSHangbin Liu }
1462a0393e3eSHangbin Liu
team_user_linkup_option_get(struct team * team,struct team_gsetter_ctx * ctx)1463a0393e3eSHangbin Liu static void team_user_linkup_option_get(struct team *team,
1464a0393e3eSHangbin Liu struct team_gsetter_ctx *ctx)
1465a0393e3eSHangbin Liu {
1466a0393e3eSHangbin Liu struct team_port *port = ctx->info->port;
1467a0393e3eSHangbin Liu
1468a0393e3eSHangbin Liu ctx->data.bool_val = port->user.linkup;
1469a0393e3eSHangbin Liu }
1470a0393e3eSHangbin Liu
1471a0393e3eSHangbin Liu static void __team_carrier_check(struct team *team);
1472a0393e3eSHangbin Liu
team_user_linkup_option_set(struct team * team,struct team_gsetter_ctx * ctx)1473a0393e3eSHangbin Liu static int team_user_linkup_option_set(struct team *team,
1474a0393e3eSHangbin Liu struct team_gsetter_ctx *ctx)
1475a0393e3eSHangbin Liu {
1476a0393e3eSHangbin Liu struct team_port *port = ctx->info->port;
1477a0393e3eSHangbin Liu
1478a0393e3eSHangbin Liu port->user.linkup = ctx->data.bool_val;
1479a0393e3eSHangbin Liu team_refresh_port_linkup(port);
1480a0393e3eSHangbin Liu __team_carrier_check(port->team);
1481a0393e3eSHangbin Liu return 0;
1482a0393e3eSHangbin Liu }
1483a0393e3eSHangbin Liu
team_user_linkup_en_option_get(struct team * team,struct team_gsetter_ctx * ctx)1484a0393e3eSHangbin Liu static void team_user_linkup_en_option_get(struct team *team,
1485a0393e3eSHangbin Liu struct team_gsetter_ctx *ctx)
1486a0393e3eSHangbin Liu {
1487a0393e3eSHangbin Liu struct team_port *port = ctx->info->port;
1488a0393e3eSHangbin Liu
1489a0393e3eSHangbin Liu ctx->data.bool_val = port->user.linkup_enabled;
1490a0393e3eSHangbin Liu }
1491a0393e3eSHangbin Liu
team_user_linkup_en_option_set(struct team * team,struct team_gsetter_ctx * ctx)1492a0393e3eSHangbin Liu static int team_user_linkup_en_option_set(struct team *team,
1493a0393e3eSHangbin Liu struct team_gsetter_ctx *ctx)
1494a0393e3eSHangbin Liu {
1495a0393e3eSHangbin Liu struct team_port *port = ctx->info->port;
1496a0393e3eSHangbin Liu
1497a0393e3eSHangbin Liu port->user.linkup_enabled = ctx->data.bool_val;
1498a0393e3eSHangbin Liu team_refresh_port_linkup(port);
1499a0393e3eSHangbin Liu __team_carrier_check(port->team);
1500a0393e3eSHangbin Liu return 0;
1501a0393e3eSHangbin Liu }
1502a0393e3eSHangbin Liu
team_priority_option_get(struct team * team,struct team_gsetter_ctx * ctx)1503a0393e3eSHangbin Liu static void team_priority_option_get(struct team *team,
1504a0393e3eSHangbin Liu struct team_gsetter_ctx *ctx)
1505a0393e3eSHangbin Liu {
1506a0393e3eSHangbin Liu struct team_port *port = ctx->info->port;
1507a0393e3eSHangbin Liu
1508a0393e3eSHangbin Liu ctx->data.s32_val = port->priority;
1509a0393e3eSHangbin Liu }
1510a0393e3eSHangbin Liu
team_priority_option_set(struct team * team,struct team_gsetter_ctx * ctx)1511a0393e3eSHangbin Liu static int team_priority_option_set(struct team *team,
1512a0393e3eSHangbin Liu struct team_gsetter_ctx *ctx)
1513a0393e3eSHangbin Liu {
1514a0393e3eSHangbin Liu struct team_port *port = ctx->info->port;
1515a0393e3eSHangbin Liu s32 priority = ctx->data.s32_val;
1516a0393e3eSHangbin Liu
1517a0393e3eSHangbin Liu if (port->priority == priority)
1518a0393e3eSHangbin Liu return 0;
1519a0393e3eSHangbin Liu port->priority = priority;
1520a0393e3eSHangbin Liu team_queue_override_port_prio_changed(team, port);
1521a0393e3eSHangbin Liu return 0;
1522a0393e3eSHangbin Liu }
1523a0393e3eSHangbin Liu
team_queue_id_option_get(struct team * team,struct team_gsetter_ctx * ctx)1524a0393e3eSHangbin Liu static void team_queue_id_option_get(struct team *team,
1525a0393e3eSHangbin Liu struct team_gsetter_ctx *ctx)
1526a0393e3eSHangbin Liu {
1527a0393e3eSHangbin Liu struct team_port *port = ctx->info->port;
1528a0393e3eSHangbin Liu
1529a0393e3eSHangbin Liu ctx->data.u32_val = port->queue_id;
1530a0393e3eSHangbin Liu }
1531a0393e3eSHangbin Liu
team_queue_id_option_set(struct team * team,struct team_gsetter_ctx * ctx)1532a0393e3eSHangbin Liu static int team_queue_id_option_set(struct team *team,
1533a0393e3eSHangbin Liu struct team_gsetter_ctx *ctx)
1534a0393e3eSHangbin Liu {
1535a0393e3eSHangbin Liu struct team_port *port = ctx->info->port;
1536a0393e3eSHangbin Liu u16 new_queue_id = ctx->data.u32_val;
1537a0393e3eSHangbin Liu
1538a0393e3eSHangbin Liu if (port->queue_id == new_queue_id)
1539a0393e3eSHangbin Liu return 0;
1540a0393e3eSHangbin Liu if (new_queue_id >= team->dev->real_num_tx_queues)
1541a0393e3eSHangbin Liu return -EINVAL;
1542a0393e3eSHangbin Liu team_queue_override_port_change_queue_id(team, port, new_queue_id);
1543a0393e3eSHangbin Liu return 0;
1544a0393e3eSHangbin Liu }
1545a0393e3eSHangbin Liu
1546a0393e3eSHangbin Liu static const struct team_option team_options[] = {
1547a0393e3eSHangbin Liu {
1548a0393e3eSHangbin Liu .name = "mode",
1549a0393e3eSHangbin Liu .type = TEAM_OPTION_TYPE_STRING,
1550a0393e3eSHangbin Liu .getter = team_mode_option_get,
1551a0393e3eSHangbin Liu .setter = team_mode_option_set,
1552a0393e3eSHangbin Liu },
1553a0393e3eSHangbin Liu {
1554a0393e3eSHangbin Liu .name = "notify_peers_count",
1555a0393e3eSHangbin Liu .type = TEAM_OPTION_TYPE_U32,
1556a0393e3eSHangbin Liu .getter = team_notify_peers_count_get,
1557a0393e3eSHangbin Liu .setter = team_notify_peers_count_set,
1558a0393e3eSHangbin Liu },
1559a0393e3eSHangbin Liu {
1560a0393e3eSHangbin Liu .name = "notify_peers_interval",
1561a0393e3eSHangbin Liu .type = TEAM_OPTION_TYPE_U32,
1562a0393e3eSHangbin Liu .getter = team_notify_peers_interval_get,
1563a0393e3eSHangbin Liu .setter = team_notify_peers_interval_set,
1564a0393e3eSHangbin Liu },
1565a0393e3eSHangbin Liu {
1566a0393e3eSHangbin Liu .name = "mcast_rejoin_count",
1567a0393e3eSHangbin Liu .type = TEAM_OPTION_TYPE_U32,
1568a0393e3eSHangbin Liu .getter = team_mcast_rejoin_count_get,
1569a0393e3eSHangbin Liu .setter = team_mcast_rejoin_count_set,
1570a0393e3eSHangbin Liu },
1571a0393e3eSHangbin Liu {
1572a0393e3eSHangbin Liu .name = "mcast_rejoin_interval",
1573a0393e3eSHangbin Liu .type = TEAM_OPTION_TYPE_U32,
1574a0393e3eSHangbin Liu .getter = team_mcast_rejoin_interval_get,
1575a0393e3eSHangbin Liu .setter = team_mcast_rejoin_interval_set,
1576a0393e3eSHangbin Liu },
1577a0393e3eSHangbin Liu {
1578a0393e3eSHangbin Liu .name = "enabled",
1579a0393e3eSHangbin Liu .type = TEAM_OPTION_TYPE_BOOL,
1580a0393e3eSHangbin Liu .per_port = true,
1581a0393e3eSHangbin Liu .getter = team_port_en_option_get,
1582a0393e3eSHangbin Liu .setter = team_port_en_option_set,
1583a0393e3eSHangbin Liu },
1584a0393e3eSHangbin Liu {
1585a0393e3eSHangbin Liu .name = "user_linkup",
1586a0393e3eSHangbin Liu .type = TEAM_OPTION_TYPE_BOOL,
1587a0393e3eSHangbin Liu .per_port = true,
1588a0393e3eSHangbin Liu .getter = team_user_linkup_option_get,
1589a0393e3eSHangbin Liu .setter = team_user_linkup_option_set,
1590a0393e3eSHangbin Liu },
1591a0393e3eSHangbin Liu {
1592a0393e3eSHangbin Liu .name = "user_linkup_enabled",
1593a0393e3eSHangbin Liu .type = TEAM_OPTION_TYPE_BOOL,
1594a0393e3eSHangbin Liu .per_port = true,
1595a0393e3eSHangbin Liu .getter = team_user_linkup_en_option_get,
1596a0393e3eSHangbin Liu .setter = team_user_linkup_en_option_set,
1597a0393e3eSHangbin Liu },
1598a0393e3eSHangbin Liu {
1599a0393e3eSHangbin Liu .name = "priority",
1600a0393e3eSHangbin Liu .type = TEAM_OPTION_TYPE_S32,
1601a0393e3eSHangbin Liu .per_port = true,
1602a0393e3eSHangbin Liu .getter = team_priority_option_get,
1603a0393e3eSHangbin Liu .setter = team_priority_option_set,
1604a0393e3eSHangbin Liu },
1605a0393e3eSHangbin Liu {
1606a0393e3eSHangbin Liu .name = "queue_id",
1607a0393e3eSHangbin Liu .type = TEAM_OPTION_TYPE_U32,
1608a0393e3eSHangbin Liu .per_port = true,
1609a0393e3eSHangbin Liu .getter = team_queue_id_option_get,
1610a0393e3eSHangbin Liu .setter = team_queue_id_option_set,
1611a0393e3eSHangbin Liu },
1612a0393e3eSHangbin Liu };
1613a0393e3eSHangbin Liu
1614a0393e3eSHangbin Liu
team_init(struct net_device * dev)1615a0393e3eSHangbin Liu static int team_init(struct net_device *dev)
1616a0393e3eSHangbin Liu {
1617a0393e3eSHangbin Liu struct team *team = netdev_priv(dev);
1618a0393e3eSHangbin Liu int i;
1619a0393e3eSHangbin Liu int err;
1620a0393e3eSHangbin Liu
1621a0393e3eSHangbin Liu team->dev = dev;
1622a0393e3eSHangbin Liu team_set_no_mode(team);
1623a0393e3eSHangbin Liu team->notifier_ctx = false;
1624a0393e3eSHangbin Liu
1625a0393e3eSHangbin Liu team->pcpu_stats = netdev_alloc_pcpu_stats(struct team_pcpu_stats);
1626a0393e3eSHangbin Liu if (!team->pcpu_stats)
1627a0393e3eSHangbin Liu return -ENOMEM;
1628a0393e3eSHangbin Liu
1629a0393e3eSHangbin Liu for (i = 0; i < TEAM_PORT_HASHENTRIES; i++)
1630a0393e3eSHangbin Liu INIT_HLIST_HEAD(&team->en_port_hlist[i]);
1631a0393e3eSHangbin Liu INIT_LIST_HEAD(&team->port_list);
1632a0393e3eSHangbin Liu err = team_queue_override_init(team);
1633a0393e3eSHangbin Liu if (err)
1634a0393e3eSHangbin Liu goto err_team_queue_override_init;
1635a0393e3eSHangbin Liu
1636a0393e3eSHangbin Liu team_adjust_ops(team);
1637a0393e3eSHangbin Liu
1638a0393e3eSHangbin Liu INIT_LIST_HEAD(&team->option_list);
1639a0393e3eSHangbin Liu INIT_LIST_HEAD(&team->option_inst_list);
1640a0393e3eSHangbin Liu
1641a0393e3eSHangbin Liu team_notify_peers_init(team);
1642a0393e3eSHangbin Liu team_mcast_rejoin_init(team);
1643a0393e3eSHangbin Liu
1644a0393e3eSHangbin Liu err = team_options_register(team, team_options, ARRAY_SIZE(team_options));
1645a0393e3eSHangbin Liu if (err)
1646a0393e3eSHangbin Liu goto err_options_register;
1647a0393e3eSHangbin Liu netif_carrier_off(dev);
1648a0393e3eSHangbin Liu
1649a0393e3eSHangbin Liu lockdep_register_key(&team->team_lock_key);
1650a0393e3eSHangbin Liu __mutex_init(&team->lock, "team->team_lock_key", &team->team_lock_key);
1651a0393e3eSHangbin Liu netdev_lockdep_set_classes(dev);
1652a0393e3eSHangbin Liu
1653a0393e3eSHangbin Liu return 0;
1654a0393e3eSHangbin Liu
1655a0393e3eSHangbin Liu err_options_register:
1656a0393e3eSHangbin Liu team_mcast_rejoin_fini(team);
1657a0393e3eSHangbin Liu team_notify_peers_fini(team);
1658a0393e3eSHangbin Liu team_queue_override_fini(team);
1659a0393e3eSHangbin Liu err_team_queue_override_init:
1660a0393e3eSHangbin Liu free_percpu(team->pcpu_stats);
1661a0393e3eSHangbin Liu
1662a0393e3eSHangbin Liu return err;
1663a0393e3eSHangbin Liu }
1664a0393e3eSHangbin Liu
team_uninit(struct net_device * dev)1665a0393e3eSHangbin Liu static void team_uninit(struct net_device *dev)
1666a0393e3eSHangbin Liu {
1667a0393e3eSHangbin Liu struct team *team = netdev_priv(dev);
1668a0393e3eSHangbin Liu struct team_port *port;
1669a0393e3eSHangbin Liu struct team_port *tmp;
1670a0393e3eSHangbin Liu
1671a0393e3eSHangbin Liu mutex_lock(&team->lock);
1672a0393e3eSHangbin Liu list_for_each_entry_safe(port, tmp, &team->port_list, list)
1673a0393e3eSHangbin Liu team_port_del(team, port->dev);
1674a0393e3eSHangbin Liu
1675a0393e3eSHangbin Liu __team_change_mode(team, NULL); /* cleanup */
1676a0393e3eSHangbin Liu __team_options_unregister(team, team_options, ARRAY_SIZE(team_options));
1677a0393e3eSHangbin Liu team_mcast_rejoin_fini(team);
1678a0393e3eSHangbin Liu team_notify_peers_fini(team);
1679a0393e3eSHangbin Liu team_queue_override_fini(team);
1680a0393e3eSHangbin Liu mutex_unlock(&team->lock);
1681a0393e3eSHangbin Liu netdev_change_features(dev);
1682a0393e3eSHangbin Liu lockdep_unregister_key(&team->team_lock_key);
1683a0393e3eSHangbin Liu }
1684a0393e3eSHangbin Liu
team_destructor(struct net_device * dev)1685a0393e3eSHangbin Liu static void team_destructor(struct net_device *dev)
1686a0393e3eSHangbin Liu {
1687a0393e3eSHangbin Liu struct team *team = netdev_priv(dev);
1688a0393e3eSHangbin Liu
1689a0393e3eSHangbin Liu free_percpu(team->pcpu_stats);
1690a0393e3eSHangbin Liu }
1691a0393e3eSHangbin Liu
team_open(struct net_device * dev)1692a0393e3eSHangbin Liu static int team_open(struct net_device *dev)
1693a0393e3eSHangbin Liu {
1694a0393e3eSHangbin Liu return 0;
1695a0393e3eSHangbin Liu }
1696a0393e3eSHangbin Liu
team_close(struct net_device * dev)1697a0393e3eSHangbin Liu static int team_close(struct net_device *dev)
1698a0393e3eSHangbin Liu {
1699a0393e3eSHangbin Liu struct team *team = netdev_priv(dev);
1700a0393e3eSHangbin Liu struct team_port *port;
1701a0393e3eSHangbin Liu
1702a0393e3eSHangbin Liu list_for_each_entry(port, &team->port_list, list) {
1703a0393e3eSHangbin Liu dev_uc_unsync(port->dev, dev);
1704a0393e3eSHangbin Liu dev_mc_unsync(port->dev, dev);
1705a0393e3eSHangbin Liu }
1706a0393e3eSHangbin Liu
1707a0393e3eSHangbin Liu return 0;
1708a0393e3eSHangbin Liu }
1709a0393e3eSHangbin Liu
1710a0393e3eSHangbin Liu /*
1711a0393e3eSHangbin Liu * note: already called with rcu_read_lock
1712a0393e3eSHangbin Liu */
team_xmit(struct sk_buff * skb,struct net_device * dev)1713a0393e3eSHangbin Liu static netdev_tx_t team_xmit(struct sk_buff *skb, struct net_device *dev)
1714a0393e3eSHangbin Liu {
1715a0393e3eSHangbin Liu struct team *team = netdev_priv(dev);
1716a0393e3eSHangbin Liu bool tx_success;
1717a0393e3eSHangbin Liu unsigned int len = skb->len;
1718a0393e3eSHangbin Liu
1719a0393e3eSHangbin Liu tx_success = team_queue_override_transmit(team, skb);
1720a0393e3eSHangbin Liu if (!tx_success)
1721a0393e3eSHangbin Liu tx_success = team->ops.transmit(team, skb);
1722a0393e3eSHangbin Liu if (tx_success) {
1723a0393e3eSHangbin Liu struct team_pcpu_stats *pcpu_stats;
1724a0393e3eSHangbin Liu
1725a0393e3eSHangbin Liu pcpu_stats = this_cpu_ptr(team->pcpu_stats);
1726a0393e3eSHangbin Liu u64_stats_update_begin(&pcpu_stats->syncp);
1727a0393e3eSHangbin Liu u64_stats_inc(&pcpu_stats->tx_packets);
1728a0393e3eSHangbin Liu u64_stats_add(&pcpu_stats->tx_bytes, len);
1729a0393e3eSHangbin Liu u64_stats_update_end(&pcpu_stats->syncp);
1730a0393e3eSHangbin Liu } else {
1731a0393e3eSHangbin Liu this_cpu_inc(team->pcpu_stats->tx_dropped);
1732a0393e3eSHangbin Liu }
1733a0393e3eSHangbin Liu
1734a0393e3eSHangbin Liu return NETDEV_TX_OK;
1735a0393e3eSHangbin Liu }
1736a0393e3eSHangbin Liu
team_select_queue(struct net_device * dev,struct sk_buff * skb,struct net_device * sb_dev)1737a0393e3eSHangbin Liu static u16 team_select_queue(struct net_device *dev, struct sk_buff *skb,
1738a0393e3eSHangbin Liu struct net_device *sb_dev)
1739a0393e3eSHangbin Liu {
1740a0393e3eSHangbin Liu /*
1741a0393e3eSHangbin Liu * This helper function exists to help dev_pick_tx get the correct
1742a0393e3eSHangbin Liu * destination queue. Using a helper function skips a call to
1743a0393e3eSHangbin Liu * skb_tx_hash and will put the skbs in the queue we expect on their
1744a0393e3eSHangbin Liu * way down to the team driver.
1745a0393e3eSHangbin Liu */
1746a0393e3eSHangbin Liu u16 txq = skb_rx_queue_recorded(skb) ? skb_get_rx_queue(skb) : 0;
1747a0393e3eSHangbin Liu
1748a0393e3eSHangbin Liu /*
1749a0393e3eSHangbin Liu * Save the original txq to restore before passing to the driver
1750a0393e3eSHangbin Liu */
1751a0393e3eSHangbin Liu qdisc_skb_cb(skb)->slave_dev_queue_mapping = skb->queue_mapping;
1752a0393e3eSHangbin Liu
1753a0393e3eSHangbin Liu if (unlikely(txq >= dev->real_num_tx_queues)) {
1754a0393e3eSHangbin Liu do {
1755a0393e3eSHangbin Liu txq -= dev->real_num_tx_queues;
1756a0393e3eSHangbin Liu } while (txq >= dev->real_num_tx_queues);
1757a0393e3eSHangbin Liu }
1758a0393e3eSHangbin Liu return txq;
1759a0393e3eSHangbin Liu }
1760a0393e3eSHangbin Liu
team_change_rx_flags(struct net_device * dev,int change)1761a0393e3eSHangbin Liu static void team_change_rx_flags(struct net_device *dev, int change)
1762a0393e3eSHangbin Liu {
1763a0393e3eSHangbin Liu struct team *team = netdev_priv(dev);
1764a0393e3eSHangbin Liu struct team_port *port;
1765a0393e3eSHangbin Liu int inc;
1766a0393e3eSHangbin Liu
1767a0393e3eSHangbin Liu rcu_read_lock();
1768a0393e3eSHangbin Liu list_for_each_entry_rcu(port, &team->port_list, list) {
1769a0393e3eSHangbin Liu if (change & IFF_PROMISC) {
1770a0393e3eSHangbin Liu inc = dev->flags & IFF_PROMISC ? 1 : -1;
1771a0393e3eSHangbin Liu dev_set_promiscuity(port->dev, inc);
1772a0393e3eSHangbin Liu }
1773a0393e3eSHangbin Liu if (change & IFF_ALLMULTI) {
1774a0393e3eSHangbin Liu inc = dev->flags & IFF_ALLMULTI ? 1 : -1;
1775a0393e3eSHangbin Liu dev_set_allmulti(port->dev, inc);
1776a0393e3eSHangbin Liu }
1777a0393e3eSHangbin Liu }
1778a0393e3eSHangbin Liu rcu_read_unlock();
1779a0393e3eSHangbin Liu }
1780a0393e3eSHangbin Liu
team_set_rx_mode(struct net_device * dev)1781a0393e3eSHangbin Liu static void team_set_rx_mode(struct net_device *dev)
1782a0393e3eSHangbin Liu {
1783a0393e3eSHangbin Liu struct team *team = netdev_priv(dev);
1784a0393e3eSHangbin Liu struct team_port *port;
1785a0393e3eSHangbin Liu
1786a0393e3eSHangbin Liu rcu_read_lock();
1787a0393e3eSHangbin Liu list_for_each_entry_rcu(port, &team->port_list, list) {
1788a0393e3eSHangbin Liu dev_uc_sync_multiple(port->dev, dev);
1789a0393e3eSHangbin Liu dev_mc_sync_multiple(port->dev, dev);
1790a0393e3eSHangbin Liu }
1791a0393e3eSHangbin Liu rcu_read_unlock();
1792a0393e3eSHangbin Liu }
1793a0393e3eSHangbin Liu
team_set_mac_address(struct net_device * dev,void * p)1794a0393e3eSHangbin Liu static int team_set_mac_address(struct net_device *dev, void *p)
1795a0393e3eSHangbin Liu {
1796a0393e3eSHangbin Liu struct sockaddr *addr = p;
1797a0393e3eSHangbin Liu struct team *team = netdev_priv(dev);
1798a0393e3eSHangbin Liu struct team_port *port;
1799a0393e3eSHangbin Liu
1800a0393e3eSHangbin Liu if (dev->type == ARPHRD_ETHER && !is_valid_ether_addr(addr->sa_data))
1801a0393e3eSHangbin Liu return -EADDRNOTAVAIL;
1802a0393e3eSHangbin Liu dev_addr_set(dev, addr->sa_data);
1803a0393e3eSHangbin Liu mutex_lock(&team->lock);
1804a0393e3eSHangbin Liu list_for_each_entry(port, &team->port_list, list)
1805a0393e3eSHangbin Liu if (team->ops.port_change_dev_addr)
1806a0393e3eSHangbin Liu team->ops.port_change_dev_addr(team, port);
1807a0393e3eSHangbin Liu mutex_unlock(&team->lock);
1808a0393e3eSHangbin Liu return 0;
1809a0393e3eSHangbin Liu }
1810a0393e3eSHangbin Liu
team_change_mtu(struct net_device * dev,int new_mtu)1811a0393e3eSHangbin Liu static int team_change_mtu(struct net_device *dev, int new_mtu)
1812a0393e3eSHangbin Liu {
1813a0393e3eSHangbin Liu struct team *team = netdev_priv(dev);
1814a0393e3eSHangbin Liu struct team_port *port;
1815a0393e3eSHangbin Liu int err;
1816a0393e3eSHangbin Liu
1817a0393e3eSHangbin Liu /*
1818a0393e3eSHangbin Liu * Alhough this is reader, it's guarded by team lock. It's not possible
1819a0393e3eSHangbin Liu * to traverse list in reverse under rcu_read_lock
1820a0393e3eSHangbin Liu */
1821a0393e3eSHangbin Liu mutex_lock(&team->lock);
1822a0393e3eSHangbin Liu team->port_mtu_change_allowed = true;
1823a0393e3eSHangbin Liu list_for_each_entry(port, &team->port_list, list) {
1824a0393e3eSHangbin Liu err = dev_set_mtu(port->dev, new_mtu);
1825a0393e3eSHangbin Liu if (err) {
1826a0393e3eSHangbin Liu netdev_err(dev, "Device %s failed to change mtu",
1827a0393e3eSHangbin Liu port->dev->name);
1828a0393e3eSHangbin Liu goto unwind;
1829a0393e3eSHangbin Liu }
1830a0393e3eSHangbin Liu }
1831a0393e3eSHangbin Liu team->port_mtu_change_allowed = false;
1832a0393e3eSHangbin Liu mutex_unlock(&team->lock);
1833a0393e3eSHangbin Liu
18341eb2cdedSEric Dumazet WRITE_ONCE(dev->mtu, new_mtu);
1835a0393e3eSHangbin Liu
1836a0393e3eSHangbin Liu return 0;
1837a0393e3eSHangbin Liu
1838a0393e3eSHangbin Liu unwind:
1839a0393e3eSHangbin Liu list_for_each_entry_continue_reverse(port, &team->port_list, list)
1840a0393e3eSHangbin Liu dev_set_mtu(port->dev, dev->mtu);
1841a0393e3eSHangbin Liu team->port_mtu_change_allowed = false;
1842a0393e3eSHangbin Liu mutex_unlock(&team->lock);
1843a0393e3eSHangbin Liu
1844a0393e3eSHangbin Liu return err;
1845a0393e3eSHangbin Liu }
1846a0393e3eSHangbin Liu
1847a0393e3eSHangbin Liu static void
team_get_stats64(struct net_device * dev,struct rtnl_link_stats64 * stats)1848a0393e3eSHangbin Liu team_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
1849a0393e3eSHangbin Liu {
1850a0393e3eSHangbin Liu struct team *team = netdev_priv(dev);
1851a0393e3eSHangbin Liu struct team_pcpu_stats *p;
1852a0393e3eSHangbin Liu u64 rx_packets, rx_bytes, rx_multicast, tx_packets, tx_bytes;
1853a0393e3eSHangbin Liu u32 rx_dropped = 0, tx_dropped = 0, rx_nohandler = 0;
1854a0393e3eSHangbin Liu unsigned int start;
1855a0393e3eSHangbin Liu int i;
1856a0393e3eSHangbin Liu
1857a0393e3eSHangbin Liu for_each_possible_cpu(i) {
1858a0393e3eSHangbin Liu p = per_cpu_ptr(team->pcpu_stats, i);
1859a0393e3eSHangbin Liu do {
1860a0393e3eSHangbin Liu start = u64_stats_fetch_begin(&p->syncp);
1861a0393e3eSHangbin Liu rx_packets = u64_stats_read(&p->rx_packets);
1862a0393e3eSHangbin Liu rx_bytes = u64_stats_read(&p->rx_bytes);
1863a0393e3eSHangbin Liu rx_multicast = u64_stats_read(&p->rx_multicast);
1864a0393e3eSHangbin Liu tx_packets = u64_stats_read(&p->tx_packets);
1865a0393e3eSHangbin Liu tx_bytes = u64_stats_read(&p->tx_bytes);
1866a0393e3eSHangbin Liu } while (u64_stats_fetch_retry(&p->syncp, start));
1867a0393e3eSHangbin Liu
1868a0393e3eSHangbin Liu stats->rx_packets += rx_packets;
1869a0393e3eSHangbin Liu stats->rx_bytes += rx_bytes;
1870a0393e3eSHangbin Liu stats->multicast += rx_multicast;
1871a0393e3eSHangbin Liu stats->tx_packets += tx_packets;
1872a0393e3eSHangbin Liu stats->tx_bytes += tx_bytes;
1873a0393e3eSHangbin Liu /*
1874a0393e3eSHangbin Liu * rx_dropped, tx_dropped & rx_nohandler are u32,
1875a0393e3eSHangbin Liu * updated without syncp protection.
1876a0393e3eSHangbin Liu */
1877a0393e3eSHangbin Liu rx_dropped += READ_ONCE(p->rx_dropped);
1878a0393e3eSHangbin Liu tx_dropped += READ_ONCE(p->tx_dropped);
1879a0393e3eSHangbin Liu rx_nohandler += READ_ONCE(p->rx_nohandler);
1880a0393e3eSHangbin Liu }
1881a0393e3eSHangbin Liu stats->rx_dropped = rx_dropped;
1882a0393e3eSHangbin Liu stats->tx_dropped = tx_dropped;
1883a0393e3eSHangbin Liu stats->rx_nohandler = rx_nohandler;
1884a0393e3eSHangbin Liu }
1885a0393e3eSHangbin Liu
team_vlan_rx_add_vid(struct net_device * dev,__be16 proto,u16 vid)1886a0393e3eSHangbin Liu static int team_vlan_rx_add_vid(struct net_device *dev, __be16 proto, u16 vid)
1887a0393e3eSHangbin Liu {
1888a0393e3eSHangbin Liu struct team *team = netdev_priv(dev);
1889a0393e3eSHangbin Liu struct team_port *port;
1890a0393e3eSHangbin Liu int err;
1891a0393e3eSHangbin Liu
1892a0393e3eSHangbin Liu /*
1893a0393e3eSHangbin Liu * Alhough this is reader, it's guarded by team lock. It's not possible
1894a0393e3eSHangbin Liu * to traverse list in reverse under rcu_read_lock
1895a0393e3eSHangbin Liu */
1896a0393e3eSHangbin Liu mutex_lock(&team->lock);
1897a0393e3eSHangbin Liu list_for_each_entry(port, &team->port_list, list) {
1898a0393e3eSHangbin Liu err = vlan_vid_add(port->dev, proto, vid);
1899a0393e3eSHangbin Liu if (err)
1900a0393e3eSHangbin Liu goto unwind;
1901a0393e3eSHangbin Liu }
1902a0393e3eSHangbin Liu mutex_unlock(&team->lock);
1903a0393e3eSHangbin Liu
1904a0393e3eSHangbin Liu return 0;
1905a0393e3eSHangbin Liu
1906a0393e3eSHangbin Liu unwind:
1907a0393e3eSHangbin Liu list_for_each_entry_continue_reverse(port, &team->port_list, list)
1908a0393e3eSHangbin Liu vlan_vid_del(port->dev, proto, vid);
1909a0393e3eSHangbin Liu mutex_unlock(&team->lock);
1910a0393e3eSHangbin Liu
1911a0393e3eSHangbin Liu return err;
1912a0393e3eSHangbin Liu }
1913a0393e3eSHangbin Liu
team_vlan_rx_kill_vid(struct net_device * dev,__be16 proto,u16 vid)1914a0393e3eSHangbin Liu static int team_vlan_rx_kill_vid(struct net_device *dev, __be16 proto, u16 vid)
1915a0393e3eSHangbin Liu {
1916a0393e3eSHangbin Liu struct team *team = netdev_priv(dev);
1917a0393e3eSHangbin Liu struct team_port *port;
1918a0393e3eSHangbin Liu
1919a0393e3eSHangbin Liu mutex_lock(&team->lock);
1920a0393e3eSHangbin Liu list_for_each_entry(port, &team->port_list, list)
1921a0393e3eSHangbin Liu vlan_vid_del(port->dev, proto, vid);
1922a0393e3eSHangbin Liu mutex_unlock(&team->lock);
1923a0393e3eSHangbin Liu
1924a0393e3eSHangbin Liu return 0;
1925a0393e3eSHangbin Liu }
1926a0393e3eSHangbin Liu
1927a0393e3eSHangbin Liu #ifdef CONFIG_NET_POLL_CONTROLLER
team_poll_controller(struct net_device * dev)1928a0393e3eSHangbin Liu static void team_poll_controller(struct net_device *dev)
1929a0393e3eSHangbin Liu {
1930a0393e3eSHangbin Liu }
1931a0393e3eSHangbin Liu
__team_netpoll_cleanup(struct team * team)1932a0393e3eSHangbin Liu static void __team_netpoll_cleanup(struct team *team)
1933a0393e3eSHangbin Liu {
1934a0393e3eSHangbin Liu struct team_port *port;
1935a0393e3eSHangbin Liu
1936a0393e3eSHangbin Liu list_for_each_entry(port, &team->port_list, list)
1937a0393e3eSHangbin Liu team_port_disable_netpoll(port);
1938a0393e3eSHangbin Liu }
1939a0393e3eSHangbin Liu
team_netpoll_cleanup(struct net_device * dev)1940a0393e3eSHangbin Liu static void team_netpoll_cleanup(struct net_device *dev)
1941a0393e3eSHangbin Liu {
1942a0393e3eSHangbin Liu struct team *team = netdev_priv(dev);
1943a0393e3eSHangbin Liu
1944a0393e3eSHangbin Liu mutex_lock(&team->lock);
1945a0393e3eSHangbin Liu __team_netpoll_cleanup(team);
1946a0393e3eSHangbin Liu mutex_unlock(&team->lock);
1947a0393e3eSHangbin Liu }
1948a0393e3eSHangbin Liu
team_netpoll_setup(struct net_device * dev,struct netpoll_info * npifo)1949a0393e3eSHangbin Liu static int team_netpoll_setup(struct net_device *dev,
1950a0393e3eSHangbin Liu struct netpoll_info *npifo)
1951a0393e3eSHangbin Liu {
1952a0393e3eSHangbin Liu struct team *team = netdev_priv(dev);
1953a0393e3eSHangbin Liu struct team_port *port;
1954a0393e3eSHangbin Liu int err = 0;
1955a0393e3eSHangbin Liu
1956a0393e3eSHangbin Liu mutex_lock(&team->lock);
1957a0393e3eSHangbin Liu list_for_each_entry(port, &team->port_list, list) {
1958a0393e3eSHangbin Liu err = __team_port_enable_netpoll(port);
1959a0393e3eSHangbin Liu if (err) {
1960a0393e3eSHangbin Liu __team_netpoll_cleanup(team);
1961a0393e3eSHangbin Liu break;
1962a0393e3eSHangbin Liu }
1963a0393e3eSHangbin Liu }
1964a0393e3eSHangbin Liu mutex_unlock(&team->lock);
1965a0393e3eSHangbin Liu return err;
1966a0393e3eSHangbin Liu }
1967a0393e3eSHangbin Liu #endif
1968a0393e3eSHangbin Liu
team_add_slave(struct net_device * dev,struct net_device * port_dev,struct netlink_ext_ack * extack)1969a0393e3eSHangbin Liu static int team_add_slave(struct net_device *dev, struct net_device *port_dev,
1970a0393e3eSHangbin Liu struct netlink_ext_ack *extack)
1971a0393e3eSHangbin Liu {
1972a0393e3eSHangbin Liu struct team *team = netdev_priv(dev);
1973a0393e3eSHangbin Liu int err;
1974a0393e3eSHangbin Liu
1975a0393e3eSHangbin Liu mutex_lock(&team->lock);
1976a0393e3eSHangbin Liu err = team_port_add(team, port_dev, extack);
1977a0393e3eSHangbin Liu mutex_unlock(&team->lock);
1978a0393e3eSHangbin Liu
1979a0393e3eSHangbin Liu if (!err)
1980a0393e3eSHangbin Liu netdev_change_features(dev);
1981a0393e3eSHangbin Liu
1982a0393e3eSHangbin Liu return err;
1983a0393e3eSHangbin Liu }
1984a0393e3eSHangbin Liu
team_del_slave(struct net_device * dev,struct net_device * port_dev)1985a0393e3eSHangbin Liu static int team_del_slave(struct net_device *dev, struct net_device *port_dev)
1986a0393e3eSHangbin Liu {
1987a0393e3eSHangbin Liu struct team *team = netdev_priv(dev);
1988a0393e3eSHangbin Liu int err;
1989a0393e3eSHangbin Liu
1990a0393e3eSHangbin Liu mutex_lock(&team->lock);
1991a0393e3eSHangbin Liu err = team_port_del(team, port_dev);
1992a0393e3eSHangbin Liu mutex_unlock(&team->lock);
1993a0393e3eSHangbin Liu
1994a0393e3eSHangbin Liu if (err)
1995a0393e3eSHangbin Liu return err;
1996a0393e3eSHangbin Liu
1997a0393e3eSHangbin Liu if (netif_is_team_master(port_dev)) {
1998a0393e3eSHangbin Liu lockdep_unregister_key(&team->team_lock_key);
1999a0393e3eSHangbin Liu lockdep_register_key(&team->team_lock_key);
2000a0393e3eSHangbin Liu lockdep_set_class(&team->lock, &team->team_lock_key);
2001a0393e3eSHangbin Liu }
2002a0393e3eSHangbin Liu netdev_change_features(dev);
2003a0393e3eSHangbin Liu
2004a0393e3eSHangbin Liu return err;
2005a0393e3eSHangbin Liu }
2006a0393e3eSHangbin Liu
team_fix_features(struct net_device * dev,netdev_features_t features)2007a0393e3eSHangbin Liu static netdev_features_t team_fix_features(struct net_device *dev,
2008a0393e3eSHangbin Liu netdev_features_t features)
2009a0393e3eSHangbin Liu {
2010a0393e3eSHangbin Liu struct team_port *port;
2011a0393e3eSHangbin Liu struct team *team = netdev_priv(dev);
2012a0393e3eSHangbin Liu netdev_features_t mask;
2013a0393e3eSHangbin Liu
2014a0393e3eSHangbin Liu mask = features;
2015a0393e3eSHangbin Liu features &= ~NETIF_F_ONE_FOR_ALL;
2016a0393e3eSHangbin Liu features |= NETIF_F_ALL_FOR_ALL;
2017a0393e3eSHangbin Liu
2018a0393e3eSHangbin Liu rcu_read_lock();
2019a0393e3eSHangbin Liu list_for_each_entry_rcu(port, &team->port_list, list) {
2020a0393e3eSHangbin Liu features = netdev_increment_features(features,
2021a0393e3eSHangbin Liu port->dev->features,
2022a0393e3eSHangbin Liu mask);
2023a0393e3eSHangbin Liu }
2024a0393e3eSHangbin Liu rcu_read_unlock();
2025a0393e3eSHangbin Liu
2026a0393e3eSHangbin Liu features = netdev_add_tso_features(features, mask);
2027a0393e3eSHangbin Liu
2028a0393e3eSHangbin Liu return features;
2029a0393e3eSHangbin Liu }
2030a0393e3eSHangbin Liu
team_change_carrier(struct net_device * dev,bool new_carrier)2031a0393e3eSHangbin Liu static int team_change_carrier(struct net_device *dev, bool new_carrier)
2032a0393e3eSHangbin Liu {
2033a0393e3eSHangbin Liu struct team *team = netdev_priv(dev);
2034a0393e3eSHangbin Liu
2035a0393e3eSHangbin Liu team->user_carrier_enabled = true;
2036a0393e3eSHangbin Liu
2037a0393e3eSHangbin Liu if (new_carrier)
2038a0393e3eSHangbin Liu netif_carrier_on(dev);
2039a0393e3eSHangbin Liu else
2040a0393e3eSHangbin Liu netif_carrier_off(dev);
2041a0393e3eSHangbin Liu return 0;
2042a0393e3eSHangbin Liu }
2043a0393e3eSHangbin Liu
2044a0393e3eSHangbin Liu static const struct net_device_ops team_netdev_ops = {
2045a0393e3eSHangbin Liu .ndo_init = team_init,
2046a0393e3eSHangbin Liu .ndo_uninit = team_uninit,
2047a0393e3eSHangbin Liu .ndo_open = team_open,
2048a0393e3eSHangbin Liu .ndo_stop = team_close,
2049a0393e3eSHangbin Liu .ndo_start_xmit = team_xmit,
2050a0393e3eSHangbin Liu .ndo_select_queue = team_select_queue,
2051a0393e3eSHangbin Liu .ndo_change_rx_flags = team_change_rx_flags,
2052a0393e3eSHangbin Liu .ndo_set_rx_mode = team_set_rx_mode,
2053a0393e3eSHangbin Liu .ndo_set_mac_address = team_set_mac_address,
2054a0393e3eSHangbin Liu .ndo_change_mtu = team_change_mtu,
2055a0393e3eSHangbin Liu .ndo_get_stats64 = team_get_stats64,
2056a0393e3eSHangbin Liu .ndo_vlan_rx_add_vid = team_vlan_rx_add_vid,
2057a0393e3eSHangbin Liu .ndo_vlan_rx_kill_vid = team_vlan_rx_kill_vid,
2058a0393e3eSHangbin Liu #ifdef CONFIG_NET_POLL_CONTROLLER
2059a0393e3eSHangbin Liu .ndo_poll_controller = team_poll_controller,
2060a0393e3eSHangbin Liu .ndo_netpoll_setup = team_netpoll_setup,
2061a0393e3eSHangbin Liu .ndo_netpoll_cleanup = team_netpoll_cleanup,
2062a0393e3eSHangbin Liu #endif
2063a0393e3eSHangbin Liu .ndo_add_slave = team_add_slave,
2064a0393e3eSHangbin Liu .ndo_del_slave = team_del_slave,
2065a0393e3eSHangbin Liu .ndo_fix_features = team_fix_features,
2066a0393e3eSHangbin Liu .ndo_change_carrier = team_change_carrier,
2067a0393e3eSHangbin Liu .ndo_features_check = passthru_features_check,
2068a0393e3eSHangbin Liu };
2069a0393e3eSHangbin Liu
2070a0393e3eSHangbin Liu /***********************
2071a0393e3eSHangbin Liu * ethtool interface
2072a0393e3eSHangbin Liu ***********************/
2073a0393e3eSHangbin Liu
team_ethtool_get_drvinfo(struct net_device * dev,struct ethtool_drvinfo * drvinfo)2074a0393e3eSHangbin Liu static void team_ethtool_get_drvinfo(struct net_device *dev,
2075a0393e3eSHangbin Liu struct ethtool_drvinfo *drvinfo)
2076a0393e3eSHangbin Liu {
2077a0393e3eSHangbin Liu strscpy(drvinfo->driver, DRV_NAME, sizeof(drvinfo->driver));
2078a0393e3eSHangbin Liu }
2079a0393e3eSHangbin Liu
team_ethtool_get_link_ksettings(struct net_device * dev,struct ethtool_link_ksettings * cmd)2080a0393e3eSHangbin Liu static int team_ethtool_get_link_ksettings(struct net_device *dev,
2081a0393e3eSHangbin Liu struct ethtool_link_ksettings *cmd)
2082a0393e3eSHangbin Liu {
2083a0393e3eSHangbin Liu struct team *team= netdev_priv(dev);
2084a0393e3eSHangbin Liu unsigned long speed = 0;
2085a0393e3eSHangbin Liu struct team_port *port;
2086a0393e3eSHangbin Liu
2087a0393e3eSHangbin Liu cmd->base.duplex = DUPLEX_UNKNOWN;
2088a0393e3eSHangbin Liu cmd->base.port = PORT_OTHER;
2089a0393e3eSHangbin Liu
2090a0393e3eSHangbin Liu rcu_read_lock();
2091a0393e3eSHangbin Liu list_for_each_entry_rcu(port, &team->port_list, list) {
2092a0393e3eSHangbin Liu if (team_port_txable(port)) {
2093a0393e3eSHangbin Liu if (port->state.speed != SPEED_UNKNOWN)
2094a0393e3eSHangbin Liu speed += port->state.speed;
2095a0393e3eSHangbin Liu if (cmd->base.duplex == DUPLEX_UNKNOWN &&
2096a0393e3eSHangbin Liu port->state.duplex != DUPLEX_UNKNOWN)
2097a0393e3eSHangbin Liu cmd->base.duplex = port->state.duplex;
2098a0393e3eSHangbin Liu }
2099a0393e3eSHangbin Liu }
2100a0393e3eSHangbin Liu rcu_read_unlock();
2101a0393e3eSHangbin Liu
2102a0393e3eSHangbin Liu cmd->base.speed = speed ? : SPEED_UNKNOWN;
2103a0393e3eSHangbin Liu
2104a0393e3eSHangbin Liu return 0;
2105a0393e3eSHangbin Liu }
2106a0393e3eSHangbin Liu
2107a0393e3eSHangbin Liu static const struct ethtool_ops team_ethtool_ops = {
2108a0393e3eSHangbin Liu .get_drvinfo = team_ethtool_get_drvinfo,
2109a0393e3eSHangbin Liu .get_link = ethtool_op_get_link,
2110a0393e3eSHangbin Liu .get_link_ksettings = team_ethtool_get_link_ksettings,
2111a0393e3eSHangbin Liu };
2112a0393e3eSHangbin Liu
2113a0393e3eSHangbin Liu /***********************
2114a0393e3eSHangbin Liu * rt netlink interface
2115a0393e3eSHangbin Liu ***********************/
2116a0393e3eSHangbin Liu
team_setup_by_port(struct net_device * dev,struct net_device * port_dev)2117a0393e3eSHangbin Liu static void team_setup_by_port(struct net_device *dev,
2118a0393e3eSHangbin Liu struct net_device *port_dev)
2119a0393e3eSHangbin Liu {
2120a0393e3eSHangbin Liu struct team *team = netdev_priv(dev);
2121a0393e3eSHangbin Liu
2122a0393e3eSHangbin Liu if (port_dev->type == ARPHRD_ETHER)
2123a0393e3eSHangbin Liu dev->header_ops = team->header_ops_cache;
2124a0393e3eSHangbin Liu else
2125a0393e3eSHangbin Liu dev->header_ops = port_dev->header_ops;
2126a0393e3eSHangbin Liu dev->type = port_dev->type;
2127a0393e3eSHangbin Liu dev->hard_header_len = port_dev->hard_header_len;
2128a0393e3eSHangbin Liu dev->needed_headroom = port_dev->needed_headroom;
2129a0393e3eSHangbin Liu dev->addr_len = port_dev->addr_len;
2130a0393e3eSHangbin Liu dev->mtu = port_dev->mtu;
2131a0393e3eSHangbin Liu memcpy(dev->broadcast, port_dev->broadcast, port_dev->addr_len);
2132a0393e3eSHangbin Liu eth_hw_addr_inherit(dev, port_dev);
2133a0393e3eSHangbin Liu
2134a0393e3eSHangbin Liu if (port_dev->flags & IFF_POINTOPOINT) {
2135a0393e3eSHangbin Liu dev->flags &= ~(IFF_BROADCAST | IFF_MULTICAST);
2136a0393e3eSHangbin Liu dev->flags |= (IFF_POINTOPOINT | IFF_NOARP);
2137a0393e3eSHangbin Liu } else if ((port_dev->flags & (IFF_BROADCAST | IFF_MULTICAST)) ==
2138a0393e3eSHangbin Liu (IFF_BROADCAST | IFF_MULTICAST)) {
2139a0393e3eSHangbin Liu dev->flags |= (IFF_BROADCAST | IFF_MULTICAST);
2140a0393e3eSHangbin Liu dev->flags &= ~(IFF_POINTOPOINT | IFF_NOARP);
2141a0393e3eSHangbin Liu }
2142a0393e3eSHangbin Liu }
2143a0393e3eSHangbin Liu
team_dev_type_check_change(struct net_device * dev,struct net_device * port_dev)2144a0393e3eSHangbin Liu static int team_dev_type_check_change(struct net_device *dev,
2145a0393e3eSHangbin Liu struct net_device *port_dev)
2146a0393e3eSHangbin Liu {
2147a0393e3eSHangbin Liu struct team *team = netdev_priv(dev);
2148a0393e3eSHangbin Liu char *portname = port_dev->name;
2149a0393e3eSHangbin Liu int err;
2150a0393e3eSHangbin Liu
2151a0393e3eSHangbin Liu if (dev->type == port_dev->type)
2152a0393e3eSHangbin Liu return 0;
2153a0393e3eSHangbin Liu if (!list_empty(&team->port_list)) {
2154a0393e3eSHangbin Liu netdev_err(dev, "Device %s is of different type\n", portname);
2155a0393e3eSHangbin Liu return -EBUSY;
2156a0393e3eSHangbin Liu }
2157a0393e3eSHangbin Liu err = call_netdevice_notifiers(NETDEV_PRE_TYPE_CHANGE, dev);
2158a0393e3eSHangbin Liu err = notifier_to_errno(err);
2159a0393e3eSHangbin Liu if (err) {
2160a0393e3eSHangbin Liu netdev_err(dev, "Refused to change device type\n");
2161a0393e3eSHangbin Liu return err;
2162a0393e3eSHangbin Liu }
2163a0393e3eSHangbin Liu dev_uc_flush(dev);
2164a0393e3eSHangbin Liu dev_mc_flush(dev);
2165a0393e3eSHangbin Liu team_setup_by_port(dev, port_dev);
2166a0393e3eSHangbin Liu call_netdevice_notifiers(NETDEV_POST_TYPE_CHANGE, dev);
2167a0393e3eSHangbin Liu return 0;
2168a0393e3eSHangbin Liu }
2169a0393e3eSHangbin Liu
team_setup(struct net_device * dev)2170a0393e3eSHangbin Liu static void team_setup(struct net_device *dev)
2171a0393e3eSHangbin Liu {
2172a0393e3eSHangbin Liu struct team *team = netdev_priv(dev);
2173a0393e3eSHangbin Liu
2174a0393e3eSHangbin Liu ether_setup(dev);
2175a0393e3eSHangbin Liu dev->max_mtu = ETH_MAX_MTU;
2176a0393e3eSHangbin Liu team->header_ops_cache = dev->header_ops;
2177a0393e3eSHangbin Liu
2178a0393e3eSHangbin Liu dev->netdev_ops = &team_netdev_ops;
2179a0393e3eSHangbin Liu dev->ethtool_ops = &team_ethtool_ops;
2180a0393e3eSHangbin Liu dev->needs_free_netdev = true;
2181a0393e3eSHangbin Liu dev->priv_destructor = team_destructor;
2182a0393e3eSHangbin Liu dev->priv_flags &= ~(IFF_XMIT_DST_RELEASE | IFF_TX_SKB_SHARING);
2183a0393e3eSHangbin Liu dev->priv_flags |= IFF_NO_QUEUE;
2184a0393e3eSHangbin Liu dev->priv_flags |= IFF_TEAM;
2185a0393e3eSHangbin Liu
2186a0393e3eSHangbin Liu /*
2187a0393e3eSHangbin Liu * Indicate we support unicast address filtering. That way core won't
2188a0393e3eSHangbin Liu * bring us to promisc mode in case a unicast addr is added.
2189a0393e3eSHangbin Liu * Let this up to underlay drivers.
2190a0393e3eSHangbin Liu */
2191a0393e3eSHangbin Liu dev->priv_flags |= IFF_UNICAST_FLT | IFF_LIVE_ADDR_CHANGE;
219200d066a4SAlexander Lobakin dev->lltx = true;
2193a0393e3eSHangbin Liu
2194a0393e3eSHangbin Liu /* Don't allow team devices to change network namespaces. */
2195*05c1280aSAlexander Lobakin dev->netns_local = true;
2196*05c1280aSAlexander Lobakin
2197*05c1280aSAlexander Lobakin dev->features |= NETIF_F_GRO;
2198a0393e3eSHangbin Liu
2199a0393e3eSHangbin Liu dev->hw_features = TEAM_VLAN_FEATURES |
2200a0393e3eSHangbin Liu NETIF_F_HW_VLAN_CTAG_RX |
2201a0393e3eSHangbin Liu NETIF_F_HW_VLAN_CTAG_FILTER |
2202a0393e3eSHangbin Liu NETIF_F_HW_VLAN_STAG_RX |
2203a0393e3eSHangbin Liu NETIF_F_HW_VLAN_STAG_FILTER;
2204a0393e3eSHangbin Liu
2205a0393e3eSHangbin Liu dev->hw_features |= NETIF_F_GSO_ENCAP_ALL;
2206a0393e3eSHangbin Liu dev->features |= dev->hw_features;
2207a0393e3eSHangbin Liu dev->features |= NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_STAG_TX;
2208a0393e3eSHangbin Liu }
2209a0393e3eSHangbin Liu
team_newlink(struct net * src_net,struct net_device * dev,struct nlattr * tb[],struct nlattr * data[],struct netlink_ext_ack * extack)2210a0393e3eSHangbin Liu static int team_newlink(struct net *src_net, struct net_device *dev,
2211a0393e3eSHangbin Liu struct nlattr *tb[], struct nlattr *data[],
2212a0393e3eSHangbin Liu struct netlink_ext_ack *extack)
2213a0393e3eSHangbin Liu {
2214a0393e3eSHangbin Liu if (tb[IFLA_ADDRESS] == NULL)
2215a0393e3eSHangbin Liu eth_hw_addr_random(dev);
2216a0393e3eSHangbin Liu
2217a0393e3eSHangbin Liu return register_netdevice(dev);
2218a0393e3eSHangbin Liu }
2219a0393e3eSHangbin Liu
team_validate(struct nlattr * tb[],struct nlattr * data[],struct netlink_ext_ack * extack)2220a0393e3eSHangbin Liu static int team_validate(struct nlattr *tb[], struct nlattr *data[],
2221a0393e3eSHangbin Liu struct netlink_ext_ack *extack)
2222a0393e3eSHangbin Liu {
2223a0393e3eSHangbin Liu if (tb[IFLA_ADDRESS]) {
2224a0393e3eSHangbin Liu if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN)
2225a0393e3eSHangbin Liu return -EINVAL;
2226a0393e3eSHangbin Liu if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS])))
2227a0393e3eSHangbin Liu return -EADDRNOTAVAIL;
2228a0393e3eSHangbin Liu }
2229a0393e3eSHangbin Liu return 0;
2230a0393e3eSHangbin Liu }
2231a0393e3eSHangbin Liu
team_get_num_tx_queues(void)2232a0393e3eSHangbin Liu static unsigned int team_get_num_tx_queues(void)
2233a0393e3eSHangbin Liu {
2234a0393e3eSHangbin Liu return TEAM_DEFAULT_NUM_TX_QUEUES;
2235a0393e3eSHangbin Liu }
2236a0393e3eSHangbin Liu
team_get_num_rx_queues(void)2237a0393e3eSHangbin Liu static unsigned int team_get_num_rx_queues(void)
2238a0393e3eSHangbin Liu {
2239a0393e3eSHangbin Liu return TEAM_DEFAULT_NUM_RX_QUEUES;
2240a0393e3eSHangbin Liu }
2241a0393e3eSHangbin Liu
2242a0393e3eSHangbin Liu static struct rtnl_link_ops team_link_ops __read_mostly = {
2243a0393e3eSHangbin Liu .kind = DRV_NAME,
2244a0393e3eSHangbin Liu .priv_size = sizeof(struct team),
2245a0393e3eSHangbin Liu .setup = team_setup,
2246a0393e3eSHangbin Liu .newlink = team_newlink,
2247a0393e3eSHangbin Liu .validate = team_validate,
2248a0393e3eSHangbin Liu .get_num_tx_queues = team_get_num_tx_queues,
2249a0393e3eSHangbin Liu .get_num_rx_queues = team_get_num_rx_queues,
2250a0393e3eSHangbin Liu };
2251a0393e3eSHangbin Liu
2252a0393e3eSHangbin Liu
2253a0393e3eSHangbin Liu /***********************************
2254a0393e3eSHangbin Liu * Generic netlink custom interface
2255a0393e3eSHangbin Liu ***********************************/
2256a0393e3eSHangbin Liu
2257a0393e3eSHangbin Liu static struct genl_family team_nl_family;
2258a0393e3eSHangbin Liu
team_nl_noop_doit(struct sk_buff * skb,struct genl_info * info)2259948dbafcSHangbin Liu int team_nl_noop_doit(struct sk_buff *skb, struct genl_info *info)
2260a0393e3eSHangbin Liu {
2261a0393e3eSHangbin Liu struct sk_buff *msg;
2262a0393e3eSHangbin Liu void *hdr;
2263a0393e3eSHangbin Liu int err;
2264a0393e3eSHangbin Liu
2265a0393e3eSHangbin Liu msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
2266a0393e3eSHangbin Liu if (!msg)
2267a0393e3eSHangbin Liu return -ENOMEM;
2268a0393e3eSHangbin Liu
2269a0393e3eSHangbin Liu hdr = genlmsg_put(msg, info->snd_portid, info->snd_seq,
2270a0393e3eSHangbin Liu &team_nl_family, 0, TEAM_CMD_NOOP);
2271a0393e3eSHangbin Liu if (!hdr) {
2272a0393e3eSHangbin Liu err = -EMSGSIZE;
2273a0393e3eSHangbin Liu goto err_msg_put;
2274a0393e3eSHangbin Liu }
2275a0393e3eSHangbin Liu
2276a0393e3eSHangbin Liu genlmsg_end(msg, hdr);
2277a0393e3eSHangbin Liu
2278a0393e3eSHangbin Liu return genlmsg_unicast(genl_info_net(info), msg, info->snd_portid);
2279a0393e3eSHangbin Liu
2280a0393e3eSHangbin Liu err_msg_put:
2281a0393e3eSHangbin Liu nlmsg_free(msg);
2282a0393e3eSHangbin Liu
2283a0393e3eSHangbin Liu return err;
2284a0393e3eSHangbin Liu }
2285a0393e3eSHangbin Liu
2286a0393e3eSHangbin Liu /*
2287a0393e3eSHangbin Liu * Netlink cmd functions should be locked by following two functions.
2288a0393e3eSHangbin Liu * Since dev gets held here, that ensures dev won't disappear in between.
2289a0393e3eSHangbin Liu */
team_nl_team_get(struct genl_info * info)2290a0393e3eSHangbin Liu static struct team *team_nl_team_get(struct genl_info *info)
2291a0393e3eSHangbin Liu {
2292a0393e3eSHangbin Liu struct net *net = genl_info_net(info);
2293a0393e3eSHangbin Liu int ifindex;
2294a0393e3eSHangbin Liu struct net_device *dev;
2295a0393e3eSHangbin Liu struct team *team;
2296a0393e3eSHangbin Liu
2297a0393e3eSHangbin Liu if (!info->attrs[TEAM_ATTR_TEAM_IFINDEX])
2298a0393e3eSHangbin Liu return NULL;
2299a0393e3eSHangbin Liu
2300a0393e3eSHangbin Liu ifindex = nla_get_u32(info->attrs[TEAM_ATTR_TEAM_IFINDEX]);
2301a0393e3eSHangbin Liu dev = dev_get_by_index(net, ifindex);
2302a0393e3eSHangbin Liu if (!dev || dev->netdev_ops != &team_netdev_ops) {
2303a0393e3eSHangbin Liu dev_put(dev);
2304a0393e3eSHangbin Liu return NULL;
2305a0393e3eSHangbin Liu }
2306a0393e3eSHangbin Liu
2307a0393e3eSHangbin Liu team = netdev_priv(dev);
2308a0393e3eSHangbin Liu mutex_lock(&team->lock);
2309a0393e3eSHangbin Liu return team;
2310a0393e3eSHangbin Liu }
2311a0393e3eSHangbin Liu
team_nl_team_put(struct team * team)2312a0393e3eSHangbin Liu static void team_nl_team_put(struct team *team)
2313a0393e3eSHangbin Liu {
2314a0393e3eSHangbin Liu mutex_unlock(&team->lock);
2315a0393e3eSHangbin Liu dev_put(team->dev);
2316a0393e3eSHangbin Liu }
2317a0393e3eSHangbin Liu
2318a0393e3eSHangbin Liu typedef int team_nl_send_func_t(struct sk_buff *skb,
2319a0393e3eSHangbin Liu struct team *team, u32 portid);
2320a0393e3eSHangbin Liu
team_nl_send_unicast(struct sk_buff * skb,struct team * team,u32 portid)2321a0393e3eSHangbin Liu static int team_nl_send_unicast(struct sk_buff *skb, struct team *team, u32 portid)
2322a0393e3eSHangbin Liu {
2323a0393e3eSHangbin Liu return genlmsg_unicast(dev_net(team->dev), skb, portid);
2324a0393e3eSHangbin Liu }
2325a0393e3eSHangbin Liu
team_nl_fill_one_option_get(struct sk_buff * skb,struct team * team,struct team_option_inst * opt_inst)2326a0393e3eSHangbin Liu static int team_nl_fill_one_option_get(struct sk_buff *skb, struct team *team,
2327a0393e3eSHangbin Liu struct team_option_inst *opt_inst)
2328a0393e3eSHangbin Liu {
2329a0393e3eSHangbin Liu struct nlattr *option_item;
2330a0393e3eSHangbin Liu struct team_option *option = opt_inst->option;
2331a0393e3eSHangbin Liu struct team_option_inst_info *opt_inst_info = &opt_inst->info;
2332a0393e3eSHangbin Liu struct team_gsetter_ctx ctx;
2333a0393e3eSHangbin Liu int err;
2334a0393e3eSHangbin Liu
2335a0393e3eSHangbin Liu ctx.info = opt_inst_info;
2336a0393e3eSHangbin Liu err = team_option_get(team, opt_inst, &ctx);
2337a0393e3eSHangbin Liu if (err)
2338a0393e3eSHangbin Liu return err;
2339a0393e3eSHangbin Liu
2340a0393e3eSHangbin Liu option_item = nla_nest_start_noflag(skb, TEAM_ATTR_ITEM_OPTION);
2341a0393e3eSHangbin Liu if (!option_item)
2342a0393e3eSHangbin Liu return -EMSGSIZE;
2343a0393e3eSHangbin Liu
2344a0393e3eSHangbin Liu if (nla_put_string(skb, TEAM_ATTR_OPTION_NAME, option->name))
2345a0393e3eSHangbin Liu goto nest_cancel;
2346a0393e3eSHangbin Liu if (opt_inst_info->port &&
2347a0393e3eSHangbin Liu nla_put_u32(skb, TEAM_ATTR_OPTION_PORT_IFINDEX,
2348a0393e3eSHangbin Liu opt_inst_info->port->dev->ifindex))
2349a0393e3eSHangbin Liu goto nest_cancel;
2350a0393e3eSHangbin Liu if (opt_inst->option->array_size &&
2351a0393e3eSHangbin Liu nla_put_u32(skb, TEAM_ATTR_OPTION_ARRAY_INDEX,
2352a0393e3eSHangbin Liu opt_inst_info->array_index))
2353a0393e3eSHangbin Liu goto nest_cancel;
2354a0393e3eSHangbin Liu
2355a0393e3eSHangbin Liu switch (option->type) {
2356a0393e3eSHangbin Liu case TEAM_OPTION_TYPE_U32:
2357a0393e3eSHangbin Liu if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_U32))
2358a0393e3eSHangbin Liu goto nest_cancel;
2359a0393e3eSHangbin Liu if (nla_put_u32(skb, TEAM_ATTR_OPTION_DATA, ctx.data.u32_val))
2360a0393e3eSHangbin Liu goto nest_cancel;
2361a0393e3eSHangbin Liu break;
2362a0393e3eSHangbin Liu case TEAM_OPTION_TYPE_STRING:
2363a0393e3eSHangbin Liu if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_STRING))
2364a0393e3eSHangbin Liu goto nest_cancel;
2365a0393e3eSHangbin Liu if (nla_put_string(skb, TEAM_ATTR_OPTION_DATA,
2366a0393e3eSHangbin Liu ctx.data.str_val))
2367a0393e3eSHangbin Liu goto nest_cancel;
2368a0393e3eSHangbin Liu break;
2369a0393e3eSHangbin Liu case TEAM_OPTION_TYPE_BINARY:
2370a0393e3eSHangbin Liu if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_BINARY))
2371a0393e3eSHangbin Liu goto nest_cancel;
2372a0393e3eSHangbin Liu if (nla_put(skb, TEAM_ATTR_OPTION_DATA, ctx.data.bin_val.len,
2373a0393e3eSHangbin Liu ctx.data.bin_val.ptr))
2374a0393e3eSHangbin Liu goto nest_cancel;
2375a0393e3eSHangbin Liu break;
2376a0393e3eSHangbin Liu case TEAM_OPTION_TYPE_BOOL:
2377a0393e3eSHangbin Liu if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_FLAG))
2378a0393e3eSHangbin Liu goto nest_cancel;
2379a0393e3eSHangbin Liu if (ctx.data.bool_val &&
2380a0393e3eSHangbin Liu nla_put_flag(skb, TEAM_ATTR_OPTION_DATA))
2381a0393e3eSHangbin Liu goto nest_cancel;
2382a0393e3eSHangbin Liu break;
2383a0393e3eSHangbin Liu case TEAM_OPTION_TYPE_S32:
2384a0393e3eSHangbin Liu if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_S32))
2385a0393e3eSHangbin Liu goto nest_cancel;
2386a0393e3eSHangbin Liu if (nla_put_s32(skb, TEAM_ATTR_OPTION_DATA, ctx.data.s32_val))
2387a0393e3eSHangbin Liu goto nest_cancel;
2388a0393e3eSHangbin Liu break;
2389a0393e3eSHangbin Liu default:
2390a0393e3eSHangbin Liu BUG();
2391a0393e3eSHangbin Liu }
2392a0393e3eSHangbin Liu if (opt_inst->removed && nla_put_flag(skb, TEAM_ATTR_OPTION_REMOVED))
2393a0393e3eSHangbin Liu goto nest_cancel;
2394a0393e3eSHangbin Liu if (opt_inst->changed) {
2395a0393e3eSHangbin Liu if (nla_put_flag(skb, TEAM_ATTR_OPTION_CHANGED))
2396a0393e3eSHangbin Liu goto nest_cancel;
2397a0393e3eSHangbin Liu opt_inst->changed = false;
2398a0393e3eSHangbin Liu }
2399a0393e3eSHangbin Liu nla_nest_end(skb, option_item);
2400a0393e3eSHangbin Liu return 0;
2401a0393e3eSHangbin Liu
2402a0393e3eSHangbin Liu nest_cancel:
2403a0393e3eSHangbin Liu nla_nest_cancel(skb, option_item);
2404a0393e3eSHangbin Liu return -EMSGSIZE;
2405a0393e3eSHangbin Liu }
2406a0393e3eSHangbin Liu
__send_and_alloc_skb(struct sk_buff ** pskb,struct team * team,u32 portid,team_nl_send_func_t * send_func)2407a0393e3eSHangbin Liu static int __send_and_alloc_skb(struct sk_buff **pskb,
2408a0393e3eSHangbin Liu struct team *team, u32 portid,
2409a0393e3eSHangbin Liu team_nl_send_func_t *send_func)
2410a0393e3eSHangbin Liu {
2411a0393e3eSHangbin Liu int err;
2412a0393e3eSHangbin Liu
2413a0393e3eSHangbin Liu if (*pskb) {
2414a0393e3eSHangbin Liu err = send_func(*pskb, team, portid);
2415a0393e3eSHangbin Liu if (err)
2416a0393e3eSHangbin Liu return err;
2417a0393e3eSHangbin Liu }
2418a0393e3eSHangbin Liu *pskb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
2419a0393e3eSHangbin Liu if (!*pskb)
2420a0393e3eSHangbin Liu return -ENOMEM;
2421a0393e3eSHangbin Liu return 0;
2422a0393e3eSHangbin Liu }
2423a0393e3eSHangbin Liu
team_nl_send_options_get(struct team * team,u32 portid,u32 seq,int flags,team_nl_send_func_t * send_func,struct list_head * sel_opt_inst_list)2424a0393e3eSHangbin Liu static int team_nl_send_options_get(struct team *team, u32 portid, u32 seq,
2425a0393e3eSHangbin Liu int flags, team_nl_send_func_t *send_func,
2426a0393e3eSHangbin Liu struct list_head *sel_opt_inst_list)
2427a0393e3eSHangbin Liu {
2428a0393e3eSHangbin Liu struct nlattr *option_list;
2429a0393e3eSHangbin Liu struct nlmsghdr *nlh;
2430a0393e3eSHangbin Liu void *hdr;
2431a0393e3eSHangbin Liu struct team_option_inst *opt_inst;
2432a0393e3eSHangbin Liu int err;
2433a0393e3eSHangbin Liu struct sk_buff *skb = NULL;
2434a0393e3eSHangbin Liu bool incomplete;
2435a0393e3eSHangbin Liu int i;
2436a0393e3eSHangbin Liu
2437a0393e3eSHangbin Liu opt_inst = list_first_entry(sel_opt_inst_list,
2438a0393e3eSHangbin Liu struct team_option_inst, tmp_list);
2439a0393e3eSHangbin Liu
2440a0393e3eSHangbin Liu start_again:
2441a0393e3eSHangbin Liu err = __send_and_alloc_skb(&skb, team, portid, send_func);
2442a0393e3eSHangbin Liu if (err)
2443a0393e3eSHangbin Liu return err;
2444a0393e3eSHangbin Liu
2445a0393e3eSHangbin Liu hdr = genlmsg_put(skb, portid, seq, &team_nl_family, flags | NLM_F_MULTI,
2446a0393e3eSHangbin Liu TEAM_CMD_OPTIONS_GET);
2447a0393e3eSHangbin Liu if (!hdr) {
2448a0393e3eSHangbin Liu nlmsg_free(skb);
2449a0393e3eSHangbin Liu return -EMSGSIZE;
2450a0393e3eSHangbin Liu }
2451a0393e3eSHangbin Liu
2452a0393e3eSHangbin Liu if (nla_put_u32(skb, TEAM_ATTR_TEAM_IFINDEX, team->dev->ifindex))
2453a0393e3eSHangbin Liu goto nla_put_failure;
2454a0393e3eSHangbin Liu option_list = nla_nest_start_noflag(skb, TEAM_ATTR_LIST_OPTION);
2455a0393e3eSHangbin Liu if (!option_list)
2456a0393e3eSHangbin Liu goto nla_put_failure;
2457a0393e3eSHangbin Liu
2458a0393e3eSHangbin Liu i = 0;
2459a0393e3eSHangbin Liu incomplete = false;
2460a0393e3eSHangbin Liu list_for_each_entry_from(opt_inst, sel_opt_inst_list, tmp_list) {
2461a0393e3eSHangbin Liu err = team_nl_fill_one_option_get(skb, team, opt_inst);
2462a0393e3eSHangbin Liu if (err) {
2463a0393e3eSHangbin Liu if (err == -EMSGSIZE) {
2464a0393e3eSHangbin Liu if (!i)
2465a0393e3eSHangbin Liu goto errout;
2466a0393e3eSHangbin Liu incomplete = true;
2467a0393e3eSHangbin Liu break;
2468a0393e3eSHangbin Liu }
2469a0393e3eSHangbin Liu goto errout;
2470a0393e3eSHangbin Liu }
2471a0393e3eSHangbin Liu i++;
2472a0393e3eSHangbin Liu }
2473a0393e3eSHangbin Liu
2474a0393e3eSHangbin Liu nla_nest_end(skb, option_list);
2475a0393e3eSHangbin Liu genlmsg_end(skb, hdr);
2476a0393e3eSHangbin Liu if (incomplete)
2477a0393e3eSHangbin Liu goto start_again;
2478a0393e3eSHangbin Liu
2479a0393e3eSHangbin Liu send_done:
2480a0393e3eSHangbin Liu nlh = nlmsg_put(skb, portid, seq, NLMSG_DONE, 0, flags | NLM_F_MULTI);
2481a0393e3eSHangbin Liu if (!nlh) {
2482a0393e3eSHangbin Liu err = __send_and_alloc_skb(&skb, team, portid, send_func);
2483a0393e3eSHangbin Liu if (err)
2484a0393e3eSHangbin Liu return err;
2485a0393e3eSHangbin Liu goto send_done;
2486a0393e3eSHangbin Liu }
2487a0393e3eSHangbin Liu
2488a0393e3eSHangbin Liu return send_func(skb, team, portid);
2489a0393e3eSHangbin Liu
2490a0393e3eSHangbin Liu nla_put_failure:
2491a0393e3eSHangbin Liu err = -EMSGSIZE;
2492a0393e3eSHangbin Liu errout:
2493a0393e3eSHangbin Liu nlmsg_free(skb);
2494a0393e3eSHangbin Liu return err;
2495a0393e3eSHangbin Liu }
2496a0393e3eSHangbin Liu
team_nl_options_get_doit(struct sk_buff * skb,struct genl_info * info)2497948dbafcSHangbin Liu int team_nl_options_get_doit(struct sk_buff *skb, struct genl_info *info)
2498a0393e3eSHangbin Liu {
2499a0393e3eSHangbin Liu struct team *team;
2500a0393e3eSHangbin Liu struct team_option_inst *opt_inst;
2501a0393e3eSHangbin Liu int err;
2502a0393e3eSHangbin Liu LIST_HEAD(sel_opt_inst_list);
2503a0393e3eSHangbin Liu
2504a0393e3eSHangbin Liu team = team_nl_team_get(info);
2505a0393e3eSHangbin Liu if (!team)
2506a0393e3eSHangbin Liu return -EINVAL;
2507a0393e3eSHangbin Liu
2508a0393e3eSHangbin Liu list_for_each_entry(opt_inst, &team->option_inst_list, list)
2509a0393e3eSHangbin Liu list_add_tail(&opt_inst->tmp_list, &sel_opt_inst_list);
2510a0393e3eSHangbin Liu err = team_nl_send_options_get(team, info->snd_portid, info->snd_seq,
2511a0393e3eSHangbin Liu NLM_F_ACK, team_nl_send_unicast,
2512a0393e3eSHangbin Liu &sel_opt_inst_list);
2513a0393e3eSHangbin Liu
2514a0393e3eSHangbin Liu team_nl_team_put(team);
2515a0393e3eSHangbin Liu
2516a0393e3eSHangbin Liu return err;
2517a0393e3eSHangbin Liu }
2518a0393e3eSHangbin Liu
2519a0393e3eSHangbin Liu static int team_nl_send_event_options_get(struct team *team,
2520a0393e3eSHangbin Liu struct list_head *sel_opt_inst_list);
2521a0393e3eSHangbin Liu
team_nl_options_set_doit(struct sk_buff * skb,struct genl_info * info)2522948dbafcSHangbin Liu int team_nl_options_set_doit(struct sk_buff *skb, struct genl_info *info)
2523a0393e3eSHangbin Liu {
2524a0393e3eSHangbin Liu struct team *team;
2525a0393e3eSHangbin Liu int err = 0;
2526a0393e3eSHangbin Liu int i;
2527a0393e3eSHangbin Liu struct nlattr *nl_option;
2528a0393e3eSHangbin Liu
2529a0393e3eSHangbin Liu rtnl_lock();
2530a0393e3eSHangbin Liu
2531a0393e3eSHangbin Liu team = team_nl_team_get(info);
2532a0393e3eSHangbin Liu if (!team) {
2533a0393e3eSHangbin Liu err = -EINVAL;
2534a0393e3eSHangbin Liu goto rtnl_unlock;
2535a0393e3eSHangbin Liu }
2536a0393e3eSHangbin Liu
2537a0393e3eSHangbin Liu err = -EINVAL;
2538a0393e3eSHangbin Liu if (!info->attrs[TEAM_ATTR_LIST_OPTION]) {
2539a0393e3eSHangbin Liu err = -EINVAL;
2540a0393e3eSHangbin Liu goto team_put;
2541a0393e3eSHangbin Liu }
2542a0393e3eSHangbin Liu
2543a0393e3eSHangbin Liu nla_for_each_nested(nl_option, info->attrs[TEAM_ATTR_LIST_OPTION], i) {
2544a0393e3eSHangbin Liu struct nlattr *opt_attrs[TEAM_ATTR_OPTION_MAX + 1];
2545a0393e3eSHangbin Liu struct nlattr *attr;
2546a0393e3eSHangbin Liu struct nlattr *attr_data;
2547a0393e3eSHangbin Liu LIST_HEAD(opt_inst_list);
2548a0393e3eSHangbin Liu enum team_option_type opt_type;
2549a0393e3eSHangbin Liu int opt_port_ifindex = 0; /* != 0 for per-port options */
2550a0393e3eSHangbin Liu u32 opt_array_index = 0;
2551a0393e3eSHangbin Liu bool opt_is_array = false;
2552a0393e3eSHangbin Liu struct team_option_inst *opt_inst;
2553a0393e3eSHangbin Liu char *opt_name;
2554a0393e3eSHangbin Liu bool opt_found = false;
2555a0393e3eSHangbin Liu
2556a0393e3eSHangbin Liu if (nla_type(nl_option) != TEAM_ATTR_ITEM_OPTION) {
2557a0393e3eSHangbin Liu err = -EINVAL;
2558a0393e3eSHangbin Liu goto team_put;
2559a0393e3eSHangbin Liu }
2560a0393e3eSHangbin Liu err = nla_parse_nested_deprecated(opt_attrs,
2561a0393e3eSHangbin Liu TEAM_ATTR_OPTION_MAX,
2562a0393e3eSHangbin Liu nl_option,
2563948dbafcSHangbin Liu team_attr_option_nl_policy,
2564a0393e3eSHangbin Liu info->extack);
2565a0393e3eSHangbin Liu if (err)
2566a0393e3eSHangbin Liu goto team_put;
2567a0393e3eSHangbin Liu if (!opt_attrs[TEAM_ATTR_OPTION_NAME] ||
2568a0393e3eSHangbin Liu !opt_attrs[TEAM_ATTR_OPTION_TYPE]) {
2569a0393e3eSHangbin Liu err = -EINVAL;
2570a0393e3eSHangbin Liu goto team_put;
2571a0393e3eSHangbin Liu }
2572a0393e3eSHangbin Liu switch (nla_get_u8(opt_attrs[TEAM_ATTR_OPTION_TYPE])) {
2573a0393e3eSHangbin Liu case NLA_U32:
2574a0393e3eSHangbin Liu opt_type = TEAM_OPTION_TYPE_U32;
2575a0393e3eSHangbin Liu break;
2576a0393e3eSHangbin Liu case NLA_STRING:
2577a0393e3eSHangbin Liu opt_type = TEAM_OPTION_TYPE_STRING;
2578a0393e3eSHangbin Liu break;
2579a0393e3eSHangbin Liu case NLA_BINARY:
2580a0393e3eSHangbin Liu opt_type = TEAM_OPTION_TYPE_BINARY;
2581a0393e3eSHangbin Liu break;
2582a0393e3eSHangbin Liu case NLA_FLAG:
2583a0393e3eSHangbin Liu opt_type = TEAM_OPTION_TYPE_BOOL;
2584a0393e3eSHangbin Liu break;
2585a0393e3eSHangbin Liu case NLA_S32:
2586a0393e3eSHangbin Liu opt_type = TEAM_OPTION_TYPE_S32;
2587a0393e3eSHangbin Liu break;
2588a0393e3eSHangbin Liu default:
2589a0393e3eSHangbin Liu goto team_put;
2590a0393e3eSHangbin Liu }
2591a0393e3eSHangbin Liu
2592a0393e3eSHangbin Liu attr_data = opt_attrs[TEAM_ATTR_OPTION_DATA];
2593a0393e3eSHangbin Liu if (opt_type != TEAM_OPTION_TYPE_BOOL && !attr_data) {
2594a0393e3eSHangbin Liu err = -EINVAL;
2595a0393e3eSHangbin Liu goto team_put;
2596a0393e3eSHangbin Liu }
2597a0393e3eSHangbin Liu
2598a0393e3eSHangbin Liu opt_name = nla_data(opt_attrs[TEAM_ATTR_OPTION_NAME]);
2599a0393e3eSHangbin Liu attr = opt_attrs[TEAM_ATTR_OPTION_PORT_IFINDEX];
2600a0393e3eSHangbin Liu if (attr)
2601a0393e3eSHangbin Liu opt_port_ifindex = nla_get_u32(attr);
2602a0393e3eSHangbin Liu
2603a0393e3eSHangbin Liu attr = opt_attrs[TEAM_ATTR_OPTION_ARRAY_INDEX];
2604a0393e3eSHangbin Liu if (attr) {
2605a0393e3eSHangbin Liu opt_is_array = true;
2606a0393e3eSHangbin Liu opt_array_index = nla_get_u32(attr);
2607a0393e3eSHangbin Liu }
2608a0393e3eSHangbin Liu
2609a0393e3eSHangbin Liu list_for_each_entry(opt_inst, &team->option_inst_list, list) {
2610a0393e3eSHangbin Liu struct team_option *option = opt_inst->option;
2611a0393e3eSHangbin Liu struct team_gsetter_ctx ctx;
2612a0393e3eSHangbin Liu struct team_option_inst_info *opt_inst_info;
2613a0393e3eSHangbin Liu int tmp_ifindex;
2614a0393e3eSHangbin Liu
2615a0393e3eSHangbin Liu opt_inst_info = &opt_inst->info;
2616a0393e3eSHangbin Liu tmp_ifindex = opt_inst_info->port ?
2617a0393e3eSHangbin Liu opt_inst_info->port->dev->ifindex : 0;
2618a0393e3eSHangbin Liu if (option->type != opt_type ||
2619a0393e3eSHangbin Liu strcmp(option->name, opt_name) ||
2620a0393e3eSHangbin Liu tmp_ifindex != opt_port_ifindex ||
2621a0393e3eSHangbin Liu (option->array_size && !opt_is_array) ||
2622a0393e3eSHangbin Liu opt_inst_info->array_index != opt_array_index)
2623a0393e3eSHangbin Liu continue;
2624a0393e3eSHangbin Liu opt_found = true;
2625a0393e3eSHangbin Liu ctx.info = opt_inst_info;
2626a0393e3eSHangbin Liu switch (opt_type) {
2627a0393e3eSHangbin Liu case TEAM_OPTION_TYPE_U32:
2628a0393e3eSHangbin Liu ctx.data.u32_val = nla_get_u32(attr_data);
2629a0393e3eSHangbin Liu break;
2630a0393e3eSHangbin Liu case TEAM_OPTION_TYPE_STRING:
2631a0393e3eSHangbin Liu if (nla_len(attr_data) > TEAM_STRING_MAX_LEN) {
2632a0393e3eSHangbin Liu err = -EINVAL;
2633a0393e3eSHangbin Liu goto team_put;
2634a0393e3eSHangbin Liu }
2635a0393e3eSHangbin Liu ctx.data.str_val = nla_data(attr_data);
2636a0393e3eSHangbin Liu break;
2637a0393e3eSHangbin Liu case TEAM_OPTION_TYPE_BINARY:
2638a0393e3eSHangbin Liu ctx.data.bin_val.len = nla_len(attr_data);
2639a0393e3eSHangbin Liu ctx.data.bin_val.ptr = nla_data(attr_data);
2640a0393e3eSHangbin Liu break;
2641a0393e3eSHangbin Liu case TEAM_OPTION_TYPE_BOOL:
2642a0393e3eSHangbin Liu ctx.data.bool_val = attr_data ? true : false;
2643a0393e3eSHangbin Liu break;
2644a0393e3eSHangbin Liu case TEAM_OPTION_TYPE_S32:
2645a0393e3eSHangbin Liu ctx.data.s32_val = nla_get_s32(attr_data);
2646a0393e3eSHangbin Liu break;
2647a0393e3eSHangbin Liu default:
2648a0393e3eSHangbin Liu BUG();
2649a0393e3eSHangbin Liu }
2650a0393e3eSHangbin Liu err = team_option_set(team, opt_inst, &ctx);
2651a0393e3eSHangbin Liu if (err)
2652a0393e3eSHangbin Liu goto team_put;
2653a0393e3eSHangbin Liu opt_inst->changed = true;
2654a0393e3eSHangbin Liu list_add(&opt_inst->tmp_list, &opt_inst_list);
2655a0393e3eSHangbin Liu }
2656a0393e3eSHangbin Liu if (!opt_found) {
2657a0393e3eSHangbin Liu err = -ENOENT;
2658a0393e3eSHangbin Liu goto team_put;
2659a0393e3eSHangbin Liu }
2660a0393e3eSHangbin Liu
2661a0393e3eSHangbin Liu err = team_nl_send_event_options_get(team, &opt_inst_list);
2662a0393e3eSHangbin Liu if (err)
2663a0393e3eSHangbin Liu break;
2664a0393e3eSHangbin Liu }
2665a0393e3eSHangbin Liu
2666a0393e3eSHangbin Liu team_put:
2667a0393e3eSHangbin Liu team_nl_team_put(team);
2668a0393e3eSHangbin Liu rtnl_unlock:
2669a0393e3eSHangbin Liu rtnl_unlock();
2670a0393e3eSHangbin Liu return err;
2671a0393e3eSHangbin Liu }
2672a0393e3eSHangbin Liu
team_nl_fill_one_port_get(struct sk_buff * skb,struct team_port * port)2673a0393e3eSHangbin Liu static int team_nl_fill_one_port_get(struct sk_buff *skb,
2674a0393e3eSHangbin Liu struct team_port *port)
2675a0393e3eSHangbin Liu {
2676a0393e3eSHangbin Liu struct nlattr *port_item;
2677a0393e3eSHangbin Liu
2678a0393e3eSHangbin Liu port_item = nla_nest_start_noflag(skb, TEAM_ATTR_ITEM_PORT);
2679a0393e3eSHangbin Liu if (!port_item)
2680a0393e3eSHangbin Liu goto nest_cancel;
2681a0393e3eSHangbin Liu if (nla_put_u32(skb, TEAM_ATTR_PORT_IFINDEX, port->dev->ifindex))
2682a0393e3eSHangbin Liu goto nest_cancel;
2683a0393e3eSHangbin Liu if (port->changed) {
2684a0393e3eSHangbin Liu if (nla_put_flag(skb, TEAM_ATTR_PORT_CHANGED))
2685a0393e3eSHangbin Liu goto nest_cancel;
2686a0393e3eSHangbin Liu port->changed = false;
2687a0393e3eSHangbin Liu }
2688a0393e3eSHangbin Liu if ((port->removed &&
2689a0393e3eSHangbin Liu nla_put_flag(skb, TEAM_ATTR_PORT_REMOVED)) ||
2690a0393e3eSHangbin Liu (port->state.linkup &&
2691a0393e3eSHangbin Liu nla_put_flag(skb, TEAM_ATTR_PORT_LINKUP)) ||
2692a0393e3eSHangbin Liu nla_put_u32(skb, TEAM_ATTR_PORT_SPEED, port->state.speed) ||
2693a0393e3eSHangbin Liu nla_put_u8(skb, TEAM_ATTR_PORT_DUPLEX, port->state.duplex))
2694a0393e3eSHangbin Liu goto nest_cancel;
2695a0393e3eSHangbin Liu nla_nest_end(skb, port_item);
2696a0393e3eSHangbin Liu return 0;
2697a0393e3eSHangbin Liu
2698a0393e3eSHangbin Liu nest_cancel:
2699a0393e3eSHangbin Liu nla_nest_cancel(skb, port_item);
2700a0393e3eSHangbin Liu return -EMSGSIZE;
2701a0393e3eSHangbin Liu }
2702a0393e3eSHangbin Liu
team_nl_send_port_list_get(struct team * team,u32 portid,u32 seq,int flags,team_nl_send_func_t * send_func,struct team_port * one_port)2703a0393e3eSHangbin Liu static int team_nl_send_port_list_get(struct team *team, u32 portid, u32 seq,
2704a0393e3eSHangbin Liu int flags, team_nl_send_func_t *send_func,
2705a0393e3eSHangbin Liu struct team_port *one_port)
2706a0393e3eSHangbin Liu {
2707a0393e3eSHangbin Liu struct nlattr *port_list;
2708a0393e3eSHangbin Liu struct nlmsghdr *nlh;
2709a0393e3eSHangbin Liu void *hdr;
2710a0393e3eSHangbin Liu struct team_port *port;
2711a0393e3eSHangbin Liu int err;
2712a0393e3eSHangbin Liu struct sk_buff *skb = NULL;
2713a0393e3eSHangbin Liu bool incomplete;
2714a0393e3eSHangbin Liu int i;
2715a0393e3eSHangbin Liu
2716a0393e3eSHangbin Liu port = list_first_entry_or_null(&team->port_list,
2717a0393e3eSHangbin Liu struct team_port, list);
2718a0393e3eSHangbin Liu
2719a0393e3eSHangbin Liu start_again:
2720a0393e3eSHangbin Liu err = __send_and_alloc_skb(&skb, team, portid, send_func);
2721a0393e3eSHangbin Liu if (err)
2722a0393e3eSHangbin Liu return err;
2723a0393e3eSHangbin Liu
2724a0393e3eSHangbin Liu hdr = genlmsg_put(skb, portid, seq, &team_nl_family, flags | NLM_F_MULTI,
2725a0393e3eSHangbin Liu TEAM_CMD_PORT_LIST_GET);
2726a0393e3eSHangbin Liu if (!hdr) {
2727a0393e3eSHangbin Liu nlmsg_free(skb);
2728a0393e3eSHangbin Liu return -EMSGSIZE;
2729a0393e3eSHangbin Liu }
2730a0393e3eSHangbin Liu
2731a0393e3eSHangbin Liu if (nla_put_u32(skb, TEAM_ATTR_TEAM_IFINDEX, team->dev->ifindex))
2732a0393e3eSHangbin Liu goto nla_put_failure;
2733a0393e3eSHangbin Liu port_list = nla_nest_start_noflag(skb, TEAM_ATTR_LIST_PORT);
2734a0393e3eSHangbin Liu if (!port_list)
2735a0393e3eSHangbin Liu goto nla_put_failure;
2736a0393e3eSHangbin Liu
2737a0393e3eSHangbin Liu i = 0;
2738a0393e3eSHangbin Liu incomplete = false;
2739a0393e3eSHangbin Liu
2740a0393e3eSHangbin Liu /* If one port is selected, called wants to send port list containing
2741a0393e3eSHangbin Liu * only this port. Otherwise go through all listed ports and send all
2742a0393e3eSHangbin Liu */
2743a0393e3eSHangbin Liu if (one_port) {
2744a0393e3eSHangbin Liu err = team_nl_fill_one_port_get(skb, one_port);
2745a0393e3eSHangbin Liu if (err)
2746a0393e3eSHangbin Liu goto errout;
2747a0393e3eSHangbin Liu } else if (port) {
2748a0393e3eSHangbin Liu list_for_each_entry_from(port, &team->port_list, list) {
2749a0393e3eSHangbin Liu err = team_nl_fill_one_port_get(skb, port);
2750a0393e3eSHangbin Liu if (err) {
2751a0393e3eSHangbin Liu if (err == -EMSGSIZE) {
2752a0393e3eSHangbin Liu if (!i)
2753a0393e3eSHangbin Liu goto errout;
2754a0393e3eSHangbin Liu incomplete = true;
2755a0393e3eSHangbin Liu break;
2756a0393e3eSHangbin Liu }
2757a0393e3eSHangbin Liu goto errout;
2758a0393e3eSHangbin Liu }
2759a0393e3eSHangbin Liu i++;
2760a0393e3eSHangbin Liu }
2761a0393e3eSHangbin Liu }
2762a0393e3eSHangbin Liu
2763a0393e3eSHangbin Liu nla_nest_end(skb, port_list);
2764a0393e3eSHangbin Liu genlmsg_end(skb, hdr);
2765a0393e3eSHangbin Liu if (incomplete)
2766a0393e3eSHangbin Liu goto start_again;
2767a0393e3eSHangbin Liu
2768a0393e3eSHangbin Liu send_done:
2769a0393e3eSHangbin Liu nlh = nlmsg_put(skb, portid, seq, NLMSG_DONE, 0, flags | NLM_F_MULTI);
2770a0393e3eSHangbin Liu if (!nlh) {
2771a0393e3eSHangbin Liu err = __send_and_alloc_skb(&skb, team, portid, send_func);
2772a0393e3eSHangbin Liu if (err)
2773a0393e3eSHangbin Liu return err;
2774a0393e3eSHangbin Liu goto send_done;
2775a0393e3eSHangbin Liu }
2776a0393e3eSHangbin Liu
2777a0393e3eSHangbin Liu return send_func(skb, team, portid);
2778a0393e3eSHangbin Liu
2779a0393e3eSHangbin Liu nla_put_failure:
2780a0393e3eSHangbin Liu err = -EMSGSIZE;
2781a0393e3eSHangbin Liu errout:
2782a0393e3eSHangbin Liu nlmsg_free(skb);
2783a0393e3eSHangbin Liu return err;
2784a0393e3eSHangbin Liu }
2785a0393e3eSHangbin Liu
team_nl_port_list_get_doit(struct sk_buff * skb,struct genl_info * info)2786948dbafcSHangbin Liu int team_nl_port_list_get_doit(struct sk_buff *skb,
2787a0393e3eSHangbin Liu struct genl_info *info)
2788a0393e3eSHangbin Liu {
2789a0393e3eSHangbin Liu struct team *team;
2790a0393e3eSHangbin Liu int err;
2791a0393e3eSHangbin Liu
2792a0393e3eSHangbin Liu team = team_nl_team_get(info);
2793a0393e3eSHangbin Liu if (!team)
2794a0393e3eSHangbin Liu return -EINVAL;
2795a0393e3eSHangbin Liu
2796a0393e3eSHangbin Liu err = team_nl_send_port_list_get(team, info->snd_portid, info->snd_seq,
2797a0393e3eSHangbin Liu NLM_F_ACK, team_nl_send_unicast, NULL);
2798a0393e3eSHangbin Liu
2799a0393e3eSHangbin Liu team_nl_team_put(team);
2800a0393e3eSHangbin Liu
2801a0393e3eSHangbin Liu return err;
2802a0393e3eSHangbin Liu }
2803a0393e3eSHangbin Liu
2804a0393e3eSHangbin Liu static const struct genl_multicast_group team_nl_mcgrps[] = {
2805a0393e3eSHangbin Liu { .name = TEAM_GENL_CHANGE_EVENT_MC_GRP_NAME, },
2806a0393e3eSHangbin Liu };
2807a0393e3eSHangbin Liu
2808a0393e3eSHangbin Liu static struct genl_family team_nl_family __ro_after_init = {
2809a0393e3eSHangbin Liu .name = TEAM_GENL_NAME,
2810a0393e3eSHangbin Liu .version = TEAM_GENL_VERSION,
28118750539bSHangbin Liu .maxattr = ARRAY_SIZE(team_nl_policy) - 1,
2812a0393e3eSHangbin Liu .policy = team_nl_policy,
2813a0393e3eSHangbin Liu .netnsok = true,
2814a0393e3eSHangbin Liu .module = THIS_MODULE,
2815a0393e3eSHangbin Liu .small_ops = team_nl_ops,
2816a0393e3eSHangbin Liu .n_small_ops = ARRAY_SIZE(team_nl_ops),
2817a0393e3eSHangbin Liu .resv_start_op = TEAM_CMD_PORT_LIST_GET + 1,
2818a0393e3eSHangbin Liu .mcgrps = team_nl_mcgrps,
2819a0393e3eSHangbin Liu .n_mcgrps = ARRAY_SIZE(team_nl_mcgrps),
2820a0393e3eSHangbin Liu };
2821a0393e3eSHangbin Liu
team_nl_send_multicast(struct sk_buff * skb,struct team * team,u32 portid)2822a0393e3eSHangbin Liu static int team_nl_send_multicast(struct sk_buff *skb,
2823a0393e3eSHangbin Liu struct team *team, u32 portid)
2824a0393e3eSHangbin Liu {
2825a0393e3eSHangbin Liu return genlmsg_multicast_netns(&team_nl_family, dev_net(team->dev),
2826a0393e3eSHangbin Liu skb, 0, 0, GFP_KERNEL);
2827a0393e3eSHangbin Liu }
2828a0393e3eSHangbin Liu
team_nl_send_event_options_get(struct team * team,struct list_head * sel_opt_inst_list)2829a0393e3eSHangbin Liu static int team_nl_send_event_options_get(struct team *team,
2830a0393e3eSHangbin Liu struct list_head *sel_opt_inst_list)
2831a0393e3eSHangbin Liu {
2832a0393e3eSHangbin Liu return team_nl_send_options_get(team, 0, 0, 0, team_nl_send_multicast,
2833a0393e3eSHangbin Liu sel_opt_inst_list);
2834a0393e3eSHangbin Liu }
2835a0393e3eSHangbin Liu
team_nl_send_event_port_get(struct team * team,struct team_port * port)2836a0393e3eSHangbin Liu static int team_nl_send_event_port_get(struct team *team,
2837a0393e3eSHangbin Liu struct team_port *port)
2838a0393e3eSHangbin Liu {
2839a0393e3eSHangbin Liu return team_nl_send_port_list_get(team, 0, 0, 0, team_nl_send_multicast,
2840a0393e3eSHangbin Liu port);
2841a0393e3eSHangbin Liu }
2842a0393e3eSHangbin Liu
team_nl_init(void)2843a0393e3eSHangbin Liu static int __init team_nl_init(void)
2844a0393e3eSHangbin Liu {
2845a0393e3eSHangbin Liu return genl_register_family(&team_nl_family);
2846a0393e3eSHangbin Liu }
2847a0393e3eSHangbin Liu
team_nl_fini(void)2848a0393e3eSHangbin Liu static void __exit team_nl_fini(void)
2849a0393e3eSHangbin Liu {
2850a0393e3eSHangbin Liu genl_unregister_family(&team_nl_family);
2851a0393e3eSHangbin Liu }
2852a0393e3eSHangbin Liu
2853a0393e3eSHangbin Liu
2854a0393e3eSHangbin Liu /******************
2855a0393e3eSHangbin Liu * Change checkers
2856a0393e3eSHangbin Liu ******************/
2857a0393e3eSHangbin Liu
__team_options_change_check(struct team * team)2858a0393e3eSHangbin Liu static void __team_options_change_check(struct team *team)
2859a0393e3eSHangbin Liu {
2860a0393e3eSHangbin Liu int err;
2861a0393e3eSHangbin Liu struct team_option_inst *opt_inst;
2862a0393e3eSHangbin Liu LIST_HEAD(sel_opt_inst_list);
2863a0393e3eSHangbin Liu
2864a0393e3eSHangbin Liu list_for_each_entry(opt_inst, &team->option_inst_list, list) {
2865a0393e3eSHangbin Liu if (opt_inst->changed)
2866a0393e3eSHangbin Liu list_add_tail(&opt_inst->tmp_list, &sel_opt_inst_list);
2867a0393e3eSHangbin Liu }
2868a0393e3eSHangbin Liu err = team_nl_send_event_options_get(team, &sel_opt_inst_list);
2869a0393e3eSHangbin Liu if (err && err != -ESRCH)
2870a0393e3eSHangbin Liu netdev_warn(team->dev, "Failed to send options change via netlink (err %d)\n",
2871a0393e3eSHangbin Liu err);
2872a0393e3eSHangbin Liu }
2873a0393e3eSHangbin Liu
2874a0393e3eSHangbin Liu /* rtnl lock is held */
2875a0393e3eSHangbin Liu
__team_port_change_send(struct team_port * port,bool linkup)2876a0393e3eSHangbin Liu static void __team_port_change_send(struct team_port *port, bool linkup)
2877a0393e3eSHangbin Liu {
2878a0393e3eSHangbin Liu int err;
2879a0393e3eSHangbin Liu
2880a0393e3eSHangbin Liu port->changed = true;
2881a0393e3eSHangbin Liu port->state.linkup = linkup;
2882a0393e3eSHangbin Liu team_refresh_port_linkup(port);
2883a0393e3eSHangbin Liu if (linkup) {
2884a0393e3eSHangbin Liu struct ethtool_link_ksettings ecmd;
2885a0393e3eSHangbin Liu
2886a0393e3eSHangbin Liu err = __ethtool_get_link_ksettings(port->dev, &ecmd);
2887a0393e3eSHangbin Liu if (!err) {
2888a0393e3eSHangbin Liu port->state.speed = ecmd.base.speed;
2889a0393e3eSHangbin Liu port->state.duplex = ecmd.base.duplex;
2890a0393e3eSHangbin Liu goto send_event;
2891a0393e3eSHangbin Liu }
2892a0393e3eSHangbin Liu }
2893a0393e3eSHangbin Liu port->state.speed = 0;
2894a0393e3eSHangbin Liu port->state.duplex = 0;
2895a0393e3eSHangbin Liu
2896a0393e3eSHangbin Liu send_event:
2897a0393e3eSHangbin Liu err = team_nl_send_event_port_get(port->team, port);
2898a0393e3eSHangbin Liu if (err && err != -ESRCH)
2899a0393e3eSHangbin Liu netdev_warn(port->team->dev, "Failed to send port change of device %s via netlink (err %d)\n",
2900a0393e3eSHangbin Liu port->dev->name, err);
2901a0393e3eSHangbin Liu
2902a0393e3eSHangbin Liu }
2903a0393e3eSHangbin Liu
__team_carrier_check(struct team * team)2904a0393e3eSHangbin Liu static void __team_carrier_check(struct team *team)
2905a0393e3eSHangbin Liu {
2906a0393e3eSHangbin Liu struct team_port *port;
2907a0393e3eSHangbin Liu bool team_linkup;
2908a0393e3eSHangbin Liu
2909a0393e3eSHangbin Liu if (team->user_carrier_enabled)
2910a0393e3eSHangbin Liu return;
2911a0393e3eSHangbin Liu
2912a0393e3eSHangbin Liu team_linkup = false;
2913a0393e3eSHangbin Liu list_for_each_entry(port, &team->port_list, list) {
2914a0393e3eSHangbin Liu if (port->linkup) {
2915a0393e3eSHangbin Liu team_linkup = true;
2916a0393e3eSHangbin Liu break;
2917a0393e3eSHangbin Liu }
2918a0393e3eSHangbin Liu }
2919a0393e3eSHangbin Liu
2920a0393e3eSHangbin Liu if (team_linkup)
2921a0393e3eSHangbin Liu netif_carrier_on(team->dev);
2922a0393e3eSHangbin Liu else
2923a0393e3eSHangbin Liu netif_carrier_off(team->dev);
2924a0393e3eSHangbin Liu }
2925a0393e3eSHangbin Liu
__team_port_change_check(struct team_port * port,bool linkup)2926a0393e3eSHangbin Liu static void __team_port_change_check(struct team_port *port, bool linkup)
2927a0393e3eSHangbin Liu {
2928a0393e3eSHangbin Liu if (port->state.linkup != linkup)
2929a0393e3eSHangbin Liu __team_port_change_send(port, linkup);
2930a0393e3eSHangbin Liu __team_carrier_check(port->team);
2931a0393e3eSHangbin Liu }
2932a0393e3eSHangbin Liu
__team_port_change_port_added(struct team_port * port,bool linkup)2933a0393e3eSHangbin Liu static void __team_port_change_port_added(struct team_port *port, bool linkup)
2934a0393e3eSHangbin Liu {
2935a0393e3eSHangbin Liu __team_port_change_send(port, linkup);
2936a0393e3eSHangbin Liu __team_carrier_check(port->team);
2937a0393e3eSHangbin Liu }
2938a0393e3eSHangbin Liu
__team_port_change_port_removed(struct team_port * port)2939a0393e3eSHangbin Liu static void __team_port_change_port_removed(struct team_port *port)
2940a0393e3eSHangbin Liu {
2941a0393e3eSHangbin Liu port->removed = true;
2942a0393e3eSHangbin Liu __team_port_change_send(port, false);
2943a0393e3eSHangbin Liu __team_carrier_check(port->team);
2944a0393e3eSHangbin Liu }
2945a0393e3eSHangbin Liu
team_port_change_check(struct team_port * port,bool linkup)2946a0393e3eSHangbin Liu static void team_port_change_check(struct team_port *port, bool linkup)
2947a0393e3eSHangbin Liu {
2948a0393e3eSHangbin Liu struct team *team = port->team;
2949a0393e3eSHangbin Liu
2950a0393e3eSHangbin Liu mutex_lock(&team->lock);
2951a0393e3eSHangbin Liu __team_port_change_check(port, linkup);
2952a0393e3eSHangbin Liu mutex_unlock(&team->lock);
2953a0393e3eSHangbin Liu }
2954a0393e3eSHangbin Liu
2955a0393e3eSHangbin Liu
2956a0393e3eSHangbin Liu /************************************
2957a0393e3eSHangbin Liu * Net device notifier event handler
2958a0393e3eSHangbin Liu ************************************/
2959a0393e3eSHangbin Liu
team_device_event(struct notifier_block * unused,unsigned long event,void * ptr)2960a0393e3eSHangbin Liu static int team_device_event(struct notifier_block *unused,
2961a0393e3eSHangbin Liu unsigned long event, void *ptr)
2962a0393e3eSHangbin Liu {
2963a0393e3eSHangbin Liu struct net_device *dev = netdev_notifier_info_to_dev(ptr);
2964a0393e3eSHangbin Liu struct team_port *port;
2965a0393e3eSHangbin Liu
2966a0393e3eSHangbin Liu port = team_port_get_rtnl(dev);
2967a0393e3eSHangbin Liu if (!port)
2968a0393e3eSHangbin Liu return NOTIFY_DONE;
2969a0393e3eSHangbin Liu
2970a0393e3eSHangbin Liu switch (event) {
2971a0393e3eSHangbin Liu case NETDEV_UP:
2972a0393e3eSHangbin Liu if (netif_oper_up(dev))
2973a0393e3eSHangbin Liu team_port_change_check(port, true);
2974a0393e3eSHangbin Liu break;
2975a0393e3eSHangbin Liu case NETDEV_DOWN:
2976a0393e3eSHangbin Liu team_port_change_check(port, false);
2977a0393e3eSHangbin Liu break;
2978a0393e3eSHangbin Liu case NETDEV_CHANGE:
2979a0393e3eSHangbin Liu if (netif_running(port->dev))
2980a0393e3eSHangbin Liu team_port_change_check(port,
2981a0393e3eSHangbin Liu !!netif_oper_up(port->dev));
2982a0393e3eSHangbin Liu break;
2983a0393e3eSHangbin Liu case NETDEV_UNREGISTER:
2984a0393e3eSHangbin Liu team_del_slave(port->team->dev, dev);
2985a0393e3eSHangbin Liu break;
2986a0393e3eSHangbin Liu case NETDEV_FEAT_CHANGE:
2987a0393e3eSHangbin Liu if (!port->team->notifier_ctx) {
2988a0393e3eSHangbin Liu port->team->notifier_ctx = true;
2989a0393e3eSHangbin Liu team_compute_features(port->team);
2990a0393e3eSHangbin Liu port->team->notifier_ctx = false;
2991a0393e3eSHangbin Liu }
2992a0393e3eSHangbin Liu break;
2993a0393e3eSHangbin Liu case NETDEV_PRECHANGEMTU:
2994a0393e3eSHangbin Liu /* Forbid to change mtu of underlaying device */
2995a0393e3eSHangbin Liu if (!port->team->port_mtu_change_allowed)
2996a0393e3eSHangbin Liu return NOTIFY_BAD;
2997a0393e3eSHangbin Liu break;
2998a0393e3eSHangbin Liu case NETDEV_PRE_TYPE_CHANGE:
2999a0393e3eSHangbin Liu /* Forbid to change type of underlaying device */
3000a0393e3eSHangbin Liu return NOTIFY_BAD;
3001a0393e3eSHangbin Liu case NETDEV_RESEND_IGMP:
3002a0393e3eSHangbin Liu /* Propagate to master device */
3003a0393e3eSHangbin Liu call_netdevice_notifiers(event, port->team->dev);
3004a0393e3eSHangbin Liu break;
3005a0393e3eSHangbin Liu }
3006a0393e3eSHangbin Liu return NOTIFY_DONE;
3007a0393e3eSHangbin Liu }
3008a0393e3eSHangbin Liu
3009a0393e3eSHangbin Liu static struct notifier_block team_notifier_block __read_mostly = {
3010a0393e3eSHangbin Liu .notifier_call = team_device_event,
3011a0393e3eSHangbin Liu };
3012a0393e3eSHangbin Liu
3013a0393e3eSHangbin Liu
3014a0393e3eSHangbin Liu /***********************
3015a0393e3eSHangbin Liu * Module init and exit
3016a0393e3eSHangbin Liu ***********************/
3017a0393e3eSHangbin Liu
team_module_init(void)3018a0393e3eSHangbin Liu static int __init team_module_init(void)
3019a0393e3eSHangbin Liu {
3020a0393e3eSHangbin Liu int err;
3021a0393e3eSHangbin Liu
3022a0393e3eSHangbin Liu register_netdevice_notifier(&team_notifier_block);
3023a0393e3eSHangbin Liu
3024a0393e3eSHangbin Liu err = rtnl_link_register(&team_link_ops);
3025a0393e3eSHangbin Liu if (err)
3026a0393e3eSHangbin Liu goto err_rtnl_reg;
3027a0393e3eSHangbin Liu
3028a0393e3eSHangbin Liu err = team_nl_init();
3029a0393e3eSHangbin Liu if (err)
3030a0393e3eSHangbin Liu goto err_nl_init;
3031a0393e3eSHangbin Liu
3032a0393e3eSHangbin Liu return 0;
3033a0393e3eSHangbin Liu
3034a0393e3eSHangbin Liu err_nl_init:
3035a0393e3eSHangbin Liu rtnl_link_unregister(&team_link_ops);
3036a0393e3eSHangbin Liu
3037a0393e3eSHangbin Liu err_rtnl_reg:
3038a0393e3eSHangbin Liu unregister_netdevice_notifier(&team_notifier_block);
3039a0393e3eSHangbin Liu
3040a0393e3eSHangbin Liu return err;
3041a0393e3eSHangbin Liu }
3042a0393e3eSHangbin Liu
team_module_exit(void)3043a0393e3eSHangbin Liu static void __exit team_module_exit(void)
3044a0393e3eSHangbin Liu {
3045a0393e3eSHangbin Liu team_nl_fini();
3046a0393e3eSHangbin Liu rtnl_link_unregister(&team_link_ops);
3047a0393e3eSHangbin Liu unregister_netdevice_notifier(&team_notifier_block);
3048a0393e3eSHangbin Liu }
3049a0393e3eSHangbin Liu
3050a0393e3eSHangbin Liu module_init(team_module_init);
3051a0393e3eSHangbin Liu module_exit(team_module_exit);
3052a0393e3eSHangbin Liu
3053a0393e3eSHangbin Liu MODULE_LICENSE("GPL v2");
3054a0393e3eSHangbin Liu MODULE_AUTHOR("Jiri Pirko <jpirko@redhat.com>");
3055a0393e3eSHangbin Liu MODULE_DESCRIPTION("Ethernet team device driver");
3056a0393e3eSHangbin Liu MODULE_ALIAS_RTNL_LINK(DRV_NAME);
3057