Commit | Line | Data |
---|---|---|
96de2506 JK |
1 | // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) |
2 | /* Copyright (C) 2018 Netronome Systems, Inc. */ | |
bb9a8d03 JH |
3 | |
4 | #include "main.h" | |
5 | ||
6 | /* LAG group config flags. */ | |
7 | #define NFP_FL_LAG_LAST BIT(1) | |
8 | #define NFP_FL_LAG_FIRST BIT(2) | |
2e1cc522 JH |
9 | #define NFP_FL_LAG_DATA BIT(3) |
10 | #define NFP_FL_LAG_XON BIT(4) | |
11 | #define NFP_FL_LAG_SYNC BIT(5) | |
bb9a8d03 JH |
12 | #define NFP_FL_LAG_SWITCH BIT(6) |
13 | #define NFP_FL_LAG_RESET BIT(7) | |
14 | ||
15 | /* LAG port state flags. */ | |
16 | #define NFP_PORT_LAG_LINK_UP BIT(0) | |
17 | #define NFP_PORT_LAG_TX_ENABLED BIT(1) | |
18 | #define NFP_PORT_LAG_CHANGED BIT(2) | |
19 | ||
20 | enum nfp_fl_lag_batch { | |
21 | NFP_FL_LAG_BATCH_FIRST, | |
22 | NFP_FL_LAG_BATCH_MEMBER, | |
23 | NFP_FL_LAG_BATCH_FINISHED | |
24 | }; | |
25 | ||
26 | /** | |
27 | * struct nfp_flower_cmsg_lag_config - control message payload for LAG config | |
28 | * @ctrl_flags: Configuration flags | |
29 | * @reserved: Reserved for future use | |
30 | * @ttl: Time to live of packet - host always sets to 0xff | |
31 | * @pkt_number: Config message packet number - increment for each message | |
32 | * @batch_ver: Batch version of messages - increment for each batch of messages | |
33 | * @group_id: Group ID applicable | |
34 | * @group_inst: Group instance number - increment when group is reused | |
35 | * @members: Array of 32-bit words listing all active group members | |
36 | */ | |
37 | struct nfp_flower_cmsg_lag_config { | |
38 | u8 ctrl_flags; | |
39 | u8 reserved[2]; | |
40 | u8 ttl; | |
41 | __be32 pkt_number; | |
42 | __be32 batch_ver; | |
43 | __be32 group_id; | |
44 | __be32 group_inst; | |
45 | __be32 members[]; | |
46 | }; | |
47 | ||
48 | /** | |
49 | * struct nfp_fl_lag_group - list entry for each LAG group | |
50 | * @group_id: Assigned group ID for host/kernel sync | |
51 | * @group_inst: Group instance in case of ID reuse | |
52 | * @list: List entry | |
53 | * @master_ndev: Group master Netdev | |
54 | * @dirty: Marked if the group needs synced to HW | |
55 | * @offloaded: Marked if the group is currently offloaded to NIC | |
56 | * @to_remove: Marked if the group should be removed from NIC | |
57 | * @to_destroy: Marked if the group should be removed from driver | |
58 | * @slave_cnt: Number of slaves in group | |
59 | */ | |
60 | struct nfp_fl_lag_group { | |
61 | unsigned int group_id; | |
62 | u8 group_inst; | |
63 | struct list_head list; | |
64 | struct net_device *master_ndev; | |
65 | bool dirty; | |
66 | bool offloaded; | |
67 | bool to_remove; | |
68 | bool to_destroy; | |
69 | unsigned int slave_cnt; | |
70 | }; | |
71 | ||
72 | #define NFP_FL_LAG_PKT_NUMBER_MASK GENMASK(30, 0) | |
73 | #define NFP_FL_LAG_VERSION_MASK GENMASK(22, 0) | |
74 | #define NFP_FL_LAG_HOST_TTL 0xff | |
75 | ||
76 | /* Use this ID with zero members to ack a batch config */ | |
77 | #define NFP_FL_LAG_SYNC_ID 0 | |
78 | #define NFP_FL_LAG_GROUP_MIN 1 /* ID 0 reserved */ | |
363096a2 | 79 | #define NFP_FL_LAG_GROUP_MAX 31 /* IDs 1 to 31 are valid */ |
bb9a8d03 JH |
80 | |
81 | /* wait for more config */ | |
82 | #define NFP_FL_LAG_DELAY (msecs_to_jiffies(2)) | |
83 | ||
2e1cc522 JH |
84 | #define NFP_FL_LAG_RETRANS_LIMIT 100 /* max retrans cmsgs to store */ |
85 | ||
bb9a8d03 JH |
86 | static unsigned int nfp_fl_get_next_pkt_number(struct nfp_fl_lag *lag) |
87 | { | |
88 | lag->pkt_num++; | |
89 | lag->pkt_num &= NFP_FL_LAG_PKT_NUMBER_MASK; | |
90 | ||
91 | return lag->pkt_num; | |
92 | } | |
93 | ||
94 | static void nfp_fl_increment_version(struct nfp_fl_lag *lag) | |
95 | { | |
96 | /* LSB is not considered by firmware so add 2 for each increment. */ | |
97 | lag->batch_ver += 2; | |
98 | lag->batch_ver &= NFP_FL_LAG_VERSION_MASK; | |
99 | ||
100 | /* Zero is reserved by firmware. */ | |
101 | if (!lag->batch_ver) | |
102 | lag->batch_ver += 2; | |
103 | } | |
104 | ||
105 | static struct nfp_fl_lag_group * | |
106 | nfp_fl_lag_group_create(struct nfp_fl_lag *lag, struct net_device *master) | |
107 | { | |
108 | struct nfp_fl_lag_group *group; | |
109 | struct nfp_flower_priv *priv; | |
110 | int id; | |
111 | ||
112 | priv = container_of(lag, struct nfp_flower_priv, nfp_lag); | |
113 | ||
363096a2 CJ |
114 | id = ida_alloc_range(&lag->ida_handle, NFP_FL_LAG_GROUP_MIN, |
115 | NFP_FL_LAG_GROUP_MAX, GFP_KERNEL); | |
bb9a8d03 JH |
116 | if (id < 0) { |
117 | nfp_flower_cmsg_warn(priv->app, | |
118 | "No more bonding groups available\n"); | |
119 | return ERR_PTR(id); | |
120 | } | |
121 | ||
122 | group = kmalloc(sizeof(*group), GFP_KERNEL); | |
123 | if (!group) { | |
363096a2 | 124 | ida_free(&lag->ida_handle, id); |
bb9a8d03 JH |
125 | return ERR_PTR(-ENOMEM); |
126 | } | |
127 | ||
128 | group->group_id = id; | |
129 | group->master_ndev = master; | |
130 | group->dirty = true; | |
131 | group->offloaded = false; | |
132 | group->to_remove = false; | |
133 | group->to_destroy = false; | |
134 | group->slave_cnt = 0; | |
135 | group->group_inst = ++lag->global_inst; | |
136 | list_add_tail(&group->list, &lag->group_list); | |
137 | ||
138 | return group; | |
139 | } | |
140 | ||
141 | static struct nfp_fl_lag_group * | |
142 | nfp_fl_lag_find_group_for_master_with_lag(struct nfp_fl_lag *lag, | |
143 | struct net_device *master) | |
144 | { | |
145 | struct nfp_fl_lag_group *entry; | |
146 | ||
147 | if (!master) | |
148 | return NULL; | |
149 | ||
150 | list_for_each_entry(entry, &lag->group_list, list) | |
151 | if (entry->master_ndev == master) | |
152 | return entry; | |
153 | ||
154 | return NULL; | |
155 | } | |
156 | ||
abc21095 YL |
157 | static int nfp_fl_lag_get_group_info(struct nfp_app *app, |
158 | struct net_device *netdev, | |
159 | __be16 *group_id, | |
160 | u8 *batch_ver, | |
161 | u8 *group_inst) | |
7e24a593 JH |
162 | { |
163 | struct nfp_flower_priv *priv = app->priv; | |
164 | struct nfp_fl_lag_group *group = NULL; | |
165 | __be32 temp_vers; | |
166 | ||
167 | mutex_lock(&priv->nfp_lag.lock); | |
168 | group = nfp_fl_lag_find_group_for_master_with_lag(&priv->nfp_lag, | |
abc21095 | 169 | netdev); |
7e24a593 JH |
170 | if (!group) { |
171 | mutex_unlock(&priv->nfp_lag.lock); | |
172 | return -ENOENT; | |
173 | } | |
174 | ||
abc21095 YL |
175 | if (group_id) |
176 | *group_id = cpu_to_be16(group->group_id); | |
177 | ||
178 | if (batch_ver) { | |
179 | temp_vers = cpu_to_be32(priv->nfp_lag.batch_ver << | |
180 | NFP_FL_PRE_LAG_VER_OFF); | |
181 | memcpy(batch_ver, &temp_vers, 3); | |
182 | } | |
183 | ||
184 | if (group_inst) | |
185 | *group_inst = group->group_inst; | |
186 | ||
7e24a593 JH |
187 | mutex_unlock(&priv->nfp_lag.lock); |
188 | ||
189 | return 0; | |
190 | } | |
191 | ||
abc21095 YL |
192 | int nfp_flower_lag_populate_pre_action(struct nfp_app *app, |
193 | struct net_device *master, | |
194 | struct nfp_fl_pre_lag *pre_act, | |
195 | struct netlink_ext_ack *extack) | |
196 | { | |
197 | if (nfp_fl_lag_get_group_info(app, master, &pre_act->group_id, | |
198 | pre_act->lag_version, | |
199 | &pre_act->instance)) { | |
200 | NL_SET_ERR_MSG_MOD(extack, "invalid entry: group does not exist for LAG action"); | |
201 | return -ENOENT; | |
202 | } | |
203 | ||
204 | return 0; | |
205 | } | |
206 | ||
207 | void nfp_flower_lag_get_info_from_netdev(struct nfp_app *app, | |
208 | struct net_device *netdev, | |
209 | struct nfp_tun_neigh_lag *lag) | |
210 | { | |
211 | nfp_fl_lag_get_group_info(app, netdev, NULL, | |
212 | lag->lag_version, &lag->lag_instance); | |
213 | } | |
214 | ||
7e24a593 JH |
215 | int nfp_flower_lag_get_output_id(struct nfp_app *app, struct net_device *master) |
216 | { | |
217 | struct nfp_flower_priv *priv = app->priv; | |
218 | struct nfp_fl_lag_group *group = NULL; | |
219 | int group_id = -ENOENT; | |
220 | ||
221 | mutex_lock(&priv->nfp_lag.lock); | |
222 | group = nfp_fl_lag_find_group_for_master_with_lag(&priv->nfp_lag, | |
223 | master); | |
224 | if (group) | |
225 | group_id = group->group_id; | |
226 | mutex_unlock(&priv->nfp_lag.lock); | |
227 | ||
228 | return group_id; | |
229 | } | |
230 | ||
bb9a8d03 JH |
231 | static int |
232 | nfp_fl_lag_config_group(struct nfp_fl_lag *lag, struct nfp_fl_lag_group *group, | |
233 | struct net_device **active_members, | |
234 | unsigned int member_cnt, enum nfp_fl_lag_batch *batch) | |
235 | { | |
236 | struct nfp_flower_cmsg_lag_config *cmsg_payload; | |
237 | struct nfp_flower_priv *priv; | |
238 | unsigned long int flags; | |
239 | unsigned int size, i; | |
240 | struct sk_buff *skb; | |
241 | ||
242 | priv = container_of(lag, struct nfp_flower_priv, nfp_lag); | |
243 | size = sizeof(*cmsg_payload) + sizeof(__be32) * member_cnt; | |
244 | skb = nfp_flower_cmsg_alloc(priv->app, size, | |
245 | NFP_FLOWER_CMSG_TYPE_LAG_CONFIG, | |
246 | GFP_KERNEL); | |
247 | if (!skb) | |
248 | return -ENOMEM; | |
249 | ||
250 | cmsg_payload = nfp_flower_cmsg_get_data(skb); | |
251 | flags = 0; | |
252 | ||
253 | /* Increment batch version for each new batch of config messages. */ | |
254 | if (*batch == NFP_FL_LAG_BATCH_FIRST) { | |
255 | flags |= NFP_FL_LAG_FIRST; | |
256 | nfp_fl_increment_version(lag); | |
257 | *batch = NFP_FL_LAG_BATCH_MEMBER; | |
258 | } | |
259 | ||
260 | /* If it is a reset msg then it is also the end of the batch. */ | |
261 | if (lag->rst_cfg) { | |
262 | flags |= NFP_FL_LAG_RESET; | |
263 | *batch = NFP_FL_LAG_BATCH_FINISHED; | |
264 | } | |
265 | ||
266 | /* To signal the end of a batch, both the switch and last flags are set | |
9bacb93b | 267 | * and the reserved SYNC group ID is used. |
bb9a8d03 JH |
268 | */ |
269 | if (*batch == NFP_FL_LAG_BATCH_FINISHED) { | |
270 | flags |= NFP_FL_LAG_SWITCH | NFP_FL_LAG_LAST; | |
271 | lag->rst_cfg = false; | |
272 | cmsg_payload->group_id = cpu_to_be32(NFP_FL_LAG_SYNC_ID); | |
273 | cmsg_payload->group_inst = 0; | |
274 | } else { | |
275 | cmsg_payload->group_id = cpu_to_be32(group->group_id); | |
276 | cmsg_payload->group_inst = cpu_to_be32(group->group_inst); | |
277 | } | |
278 | ||
279 | cmsg_payload->reserved[0] = 0; | |
280 | cmsg_payload->reserved[1] = 0; | |
281 | cmsg_payload->ttl = NFP_FL_LAG_HOST_TTL; | |
282 | cmsg_payload->ctrl_flags = flags; | |
283 | cmsg_payload->batch_ver = cpu_to_be32(lag->batch_ver); | |
284 | cmsg_payload->pkt_number = cpu_to_be32(nfp_fl_get_next_pkt_number(lag)); | |
285 | ||
286 | for (i = 0; i < member_cnt; i++) | |
287 | cmsg_payload->members[i] = | |
288 | cpu_to_be32(nfp_repr_get_port_id(active_members[i])); | |
289 | ||
290 | nfp_ctrl_tx(priv->app->ctrl, skb); | |
291 | return 0; | |
292 | } | |
293 | ||
294 | static void nfp_fl_lag_do_work(struct work_struct *work) | |
295 | { | |
296 | enum nfp_fl_lag_batch batch = NFP_FL_LAG_BATCH_FIRST; | |
297 | struct nfp_fl_lag_group *entry, *storage; | |
298 | struct delayed_work *delayed_work; | |
299 | struct nfp_flower_priv *priv; | |
300 | struct nfp_fl_lag *lag; | |
301 | int err; | |
302 | ||
303 | delayed_work = to_delayed_work(work); | |
304 | lag = container_of(delayed_work, struct nfp_fl_lag, work); | |
305 | priv = container_of(lag, struct nfp_flower_priv, nfp_lag); | |
306 | ||
307 | mutex_lock(&lag->lock); | |
308 | list_for_each_entry_safe(entry, storage, &lag->group_list, list) { | |
309 | struct net_device *iter_netdev, **acti_netdevs; | |
310 | struct nfp_flower_repr_priv *repr_priv; | |
311 | int active_count = 0, slaves = 0; | |
312 | struct nfp_repr *repr; | |
313 | unsigned long *flags; | |
314 | ||
315 | if (entry->to_remove) { | |
316 | /* Active count of 0 deletes group on hw. */ | |
317 | err = nfp_fl_lag_config_group(lag, entry, NULL, 0, | |
318 | &batch); | |
319 | if (!err) { | |
320 | entry->to_remove = false; | |
321 | entry->offloaded = false; | |
322 | } else { | |
323 | nfp_flower_cmsg_warn(priv->app, | |
324 | "group delete failed\n"); | |
325 | schedule_delayed_work(&lag->work, | |
326 | NFP_FL_LAG_DELAY); | |
327 | continue; | |
328 | } | |
329 | ||
330 | if (entry->to_destroy) { | |
363096a2 | 331 | ida_free(&lag->ida_handle, entry->group_id); |
bb9a8d03 JH |
332 | list_del(&entry->list); |
333 | kfree(entry); | |
334 | } | |
335 | continue; | |
336 | } | |
337 | ||
338 | acti_netdevs = kmalloc_array(entry->slave_cnt, | |
339 | sizeof(*acti_netdevs), GFP_KERNEL); | |
84e95149 DZ |
340 | if (!acti_netdevs) { |
341 | schedule_delayed_work(&lag->work, | |
342 | NFP_FL_LAG_DELAY); | |
343 | continue; | |
344 | } | |
bb9a8d03 JH |
345 | |
346 | /* Include sanity check in the loop. It may be that a bond has | |
347 | * changed between processing the last notification and the | |
348 | * work queue triggering. If the number of slaves has changed | |
349 | * or it now contains netdevs that cannot be offloaded, ignore | |
350 | * the group until pending notifications are processed. | |
351 | */ | |
352 | rcu_read_lock(); | |
353 | for_each_netdev_in_bond_rcu(entry->master_ndev, iter_netdev) { | |
354 | if (!nfp_netdev_is_nfp_repr(iter_netdev)) { | |
355 | slaves = 0; | |
356 | break; | |
357 | } | |
358 | ||
359 | repr = netdev_priv(iter_netdev); | |
360 | ||
361 | if (repr->app != priv->app) { | |
362 | slaves = 0; | |
363 | break; | |
364 | } | |
365 | ||
366 | slaves++; | |
367 | if (slaves > entry->slave_cnt) | |
368 | break; | |
369 | ||
370 | /* Check the ports for state changes. */ | |
371 | repr_priv = repr->app_priv; | |
372 | flags = &repr_priv->lag_port_flags; | |
373 | ||
374 | if (*flags & NFP_PORT_LAG_CHANGED) { | |
375 | *flags &= ~NFP_PORT_LAG_CHANGED; | |
376 | entry->dirty = true; | |
377 | } | |
378 | ||
379 | if ((*flags & NFP_PORT_LAG_TX_ENABLED) && | |
380 | (*flags & NFP_PORT_LAG_LINK_UP)) | |
381 | acti_netdevs[active_count++] = iter_netdev; | |
382 | } | |
383 | rcu_read_unlock(); | |
384 | ||
385 | if (slaves != entry->slave_cnt || !entry->dirty) { | |
386 | kfree(acti_netdevs); | |
387 | continue; | |
388 | } | |
389 | ||
390 | err = nfp_fl_lag_config_group(lag, entry, acti_netdevs, | |
391 | active_count, &batch); | |
392 | if (!err) { | |
393 | entry->offloaded = true; | |
394 | entry->dirty = false; | |
395 | } else { | |
396 | nfp_flower_cmsg_warn(priv->app, | |
397 | "group offload failed\n"); | |
398 | schedule_delayed_work(&lag->work, NFP_FL_LAG_DELAY); | |
399 | } | |
400 | ||
401 | kfree(acti_netdevs); | |
402 | } | |
403 | ||
404 | /* End the config batch if at least one packet has been batched. */ | |
405 | if (batch == NFP_FL_LAG_BATCH_MEMBER) { | |
406 | batch = NFP_FL_LAG_BATCH_FINISHED; | |
407 | err = nfp_fl_lag_config_group(lag, NULL, NULL, 0, &batch); | |
408 | if (err) | |
409 | nfp_flower_cmsg_warn(priv->app, | |
410 | "group batch end cmsg failed\n"); | |
411 | } | |
412 | ||
413 | mutex_unlock(&lag->lock); | |
414 | } | |
415 | ||
2e1cc522 JH |
416 | static int |
417 | nfp_fl_lag_put_unprocessed(struct nfp_fl_lag *lag, struct sk_buff *skb) | |
418 | { | |
419 | struct nfp_flower_cmsg_lag_config *cmsg_payload; | |
420 | ||
421 | cmsg_payload = nfp_flower_cmsg_get_data(skb); | |
363096a2 | 422 | if (be32_to_cpu(cmsg_payload->group_id) > NFP_FL_LAG_GROUP_MAX) |
2e1cc522 JH |
423 | return -EINVAL; |
424 | ||
425 | /* Drop cmsg retrans if storage limit is exceeded to prevent | |
426 | * overloading. If the fw notices that expected messages have not been | |
427 | * received in a given time block, it will request a full resync. | |
428 | */ | |
429 | if (skb_queue_len(&lag->retrans_skbs) >= NFP_FL_LAG_RETRANS_LIMIT) | |
430 | return -ENOSPC; | |
431 | ||
432 | __skb_queue_tail(&lag->retrans_skbs, skb); | |
433 | ||
434 | return 0; | |
435 | } | |
436 | ||
437 | static void nfp_fl_send_unprocessed(struct nfp_fl_lag *lag) | |
438 | { | |
439 | struct nfp_flower_priv *priv; | |
440 | struct sk_buff *skb; | |
441 | ||
442 | priv = container_of(lag, struct nfp_flower_priv, nfp_lag); | |
443 | ||
444 | while ((skb = __skb_dequeue(&lag->retrans_skbs))) | |
445 | nfp_ctrl_tx(priv->app->ctrl, skb); | |
446 | } | |
447 | ||
448 | bool nfp_flower_lag_unprocessed_msg(struct nfp_app *app, struct sk_buff *skb) | |
449 | { | |
450 | struct nfp_flower_cmsg_lag_config *cmsg_payload; | |
451 | struct nfp_flower_priv *priv = app->priv; | |
452 | struct nfp_fl_lag_group *group_entry; | |
453 | unsigned long int flags; | |
454 | bool store_skb = false; | |
455 | int err; | |
456 | ||
457 | cmsg_payload = nfp_flower_cmsg_get_data(skb); | |
458 | flags = cmsg_payload->ctrl_flags; | |
459 | ||
460 | /* Note the intentional fall through below. If DATA and XON are both | |
461 | * set, the message will stored and sent again with the rest of the | |
462 | * unprocessed messages list. | |
463 | */ | |
464 | ||
465 | /* Store */ | |
466 | if (flags & NFP_FL_LAG_DATA) | |
467 | if (!nfp_fl_lag_put_unprocessed(&priv->nfp_lag, skb)) | |
468 | store_skb = true; | |
469 | ||
470 | /* Send stored */ | |
471 | if (flags & NFP_FL_LAG_XON) | |
472 | nfp_fl_send_unprocessed(&priv->nfp_lag); | |
473 | ||
474 | /* Resend all */ | |
475 | if (flags & NFP_FL_LAG_SYNC) { | |
476 | /* To resend all config: | |
477 | * 1) Clear all unprocessed messages | |
478 | * 2) Mark all groups dirty | |
479 | * 3) Reset NFP group config | |
480 | * 4) Schedule a LAG config update | |
481 | */ | |
482 | ||
483 | __skb_queue_purge(&priv->nfp_lag.retrans_skbs); | |
484 | ||
485 | mutex_lock(&priv->nfp_lag.lock); | |
486 | list_for_each_entry(group_entry, &priv->nfp_lag.group_list, | |
487 | list) | |
488 | group_entry->dirty = true; | |
489 | ||
490 | err = nfp_flower_lag_reset(&priv->nfp_lag); | |
491 | if (err) | |
492 | nfp_flower_cmsg_warn(priv->app, | |
493 | "mem err in group reset msg\n"); | |
494 | mutex_unlock(&priv->nfp_lag.lock); | |
495 | ||
496 | schedule_delayed_work(&priv->nfp_lag.work, 0); | |
497 | } | |
498 | ||
499 | return store_skb; | |
500 | } | |
501 | ||
bb9a8d03 JH |
502 | static void |
503 | nfp_fl_lag_schedule_group_remove(struct nfp_fl_lag *lag, | |
504 | struct nfp_fl_lag_group *group) | |
505 | { | |
506 | group->to_remove = true; | |
507 | ||
508 | schedule_delayed_work(&lag->work, NFP_FL_LAG_DELAY); | |
509 | } | |
510 | ||
a558c982 | 511 | static void |
bb9a8d03 JH |
512 | nfp_fl_lag_schedule_group_delete(struct nfp_fl_lag *lag, |
513 | struct net_device *master) | |
514 | { | |
515 | struct nfp_fl_lag_group *group; | |
a558c982 JK |
516 | struct nfp_flower_priv *priv; |
517 | ||
518 | priv = container_of(lag, struct nfp_flower_priv, nfp_lag); | |
519 | ||
520 | if (!netif_is_bond_master(master)) | |
521 | return; | |
bb9a8d03 JH |
522 | |
523 | mutex_lock(&lag->lock); | |
524 | group = nfp_fl_lag_find_group_for_master_with_lag(lag, master); | |
525 | if (!group) { | |
526 | mutex_unlock(&lag->lock); | |
a558c982 JK |
527 | nfp_warn(priv->app->cpp, "untracked bond got unregistered %s\n", |
528 | netdev_name(master)); | |
529 | return; | |
bb9a8d03 JH |
530 | } |
531 | ||
532 | group->to_remove = true; | |
533 | group->to_destroy = true; | |
534 | mutex_unlock(&lag->lock); | |
535 | ||
536 | schedule_delayed_work(&lag->work, NFP_FL_LAG_DELAY); | |
bb9a8d03 JH |
537 | } |
538 | ||
539 | static int | |
540 | nfp_fl_lag_changeupper_event(struct nfp_fl_lag *lag, | |
541 | struct netdev_notifier_changeupper_info *info) | |
542 | { | |
543 | struct net_device *upper = info->upper_dev, *iter_netdev; | |
544 | struct netdev_lag_upper_info *lag_upper_info; | |
545 | struct nfp_fl_lag_group *group; | |
546 | struct nfp_flower_priv *priv; | |
547 | unsigned int slave_count = 0; | |
548 | bool can_offload = true; | |
549 | struct nfp_repr *repr; | |
550 | ||
551 | if (!netif_is_lag_master(upper)) | |
552 | return 0; | |
553 | ||
554 | priv = container_of(lag, struct nfp_flower_priv, nfp_lag); | |
555 | ||
556 | rcu_read_lock(); | |
557 | for_each_netdev_in_bond_rcu(upper, iter_netdev) { | |
558 | if (!nfp_netdev_is_nfp_repr(iter_netdev)) { | |
559 | can_offload = false; | |
560 | break; | |
561 | } | |
562 | repr = netdev_priv(iter_netdev); | |
563 | ||
564 | /* Ensure all ports are created by the same app/on same card. */ | |
565 | if (repr->app != priv->app) { | |
566 | can_offload = false; | |
567 | break; | |
568 | } | |
569 | ||
570 | slave_count++; | |
571 | } | |
572 | rcu_read_unlock(); | |
573 | ||
574 | lag_upper_info = info->upper_info; | |
575 | ||
576 | /* Firmware supports active/backup and L3/L4 hash bonds. */ | |
577 | if (lag_upper_info && | |
578 | lag_upper_info->tx_type != NETDEV_LAG_TX_TYPE_ACTIVEBACKUP && | |
579 | (lag_upper_info->tx_type != NETDEV_LAG_TX_TYPE_HASH || | |
635cf43d JH |
580 | (lag_upper_info->hash_type != NETDEV_LAG_HASH_L34 && |
581 | lag_upper_info->hash_type != NETDEV_LAG_HASH_E34 && | |
582 | lag_upper_info->hash_type != NETDEV_LAG_HASH_UNKNOWN))) { | |
bb9a8d03 JH |
583 | can_offload = false; |
584 | nfp_flower_cmsg_warn(priv->app, | |
585 | "Unable to offload tx_type %u hash %u\n", | |
586 | lag_upper_info->tx_type, | |
587 | lag_upper_info->hash_type); | |
588 | } | |
589 | ||
590 | mutex_lock(&lag->lock); | |
591 | group = nfp_fl_lag_find_group_for_master_with_lag(lag, upper); | |
592 | ||
593 | if (slave_count == 0 || !can_offload) { | |
594 | /* Cannot offload the group - remove if previously offloaded. */ | |
595 | if (group && group->offloaded) | |
596 | nfp_fl_lag_schedule_group_remove(lag, group); | |
597 | ||
598 | mutex_unlock(&lag->lock); | |
599 | return 0; | |
600 | } | |
601 | ||
602 | if (!group) { | |
603 | group = nfp_fl_lag_group_create(lag, upper); | |
604 | if (IS_ERR(group)) { | |
605 | mutex_unlock(&lag->lock); | |
606 | return PTR_ERR(group); | |
607 | } | |
608 | } | |
609 | ||
610 | group->dirty = true; | |
611 | group->slave_cnt = slave_count; | |
612 | ||
9bacb93b | 613 | /* Group may have been on queue for removal but is now offloadable. */ |
bb9a8d03 JH |
614 | group->to_remove = false; |
615 | mutex_unlock(&lag->lock); | |
616 | ||
617 | schedule_delayed_work(&lag->work, NFP_FL_LAG_DELAY); | |
618 | return 0; | |
619 | } | |
620 | ||
659bb404 | 621 | static void |
bb9a8d03 JH |
622 | nfp_fl_lag_changels_event(struct nfp_fl_lag *lag, struct net_device *netdev, |
623 | struct netdev_notifier_changelowerstate_info *info) | |
624 | { | |
625 | struct netdev_lag_lower_state_info *lag_lower_info; | |
626 | struct nfp_flower_repr_priv *repr_priv; | |
627 | struct nfp_flower_priv *priv; | |
628 | struct nfp_repr *repr; | |
629 | unsigned long *flags; | |
630 | ||
631 | if (!netif_is_lag_port(netdev) || !nfp_netdev_is_nfp_repr(netdev)) | |
659bb404 | 632 | return; |
bb9a8d03 JH |
633 | |
634 | lag_lower_info = info->lower_state_info; | |
635 | if (!lag_lower_info) | |
659bb404 | 636 | return; |
bb9a8d03 JH |
637 | |
638 | priv = container_of(lag, struct nfp_flower_priv, nfp_lag); | |
639 | repr = netdev_priv(netdev); | |
640 | ||
641 | /* Verify that the repr is associated with this app. */ | |
642 | if (repr->app != priv->app) | |
659bb404 | 643 | return; |
bb9a8d03 JH |
644 | |
645 | repr_priv = repr->app_priv; | |
646 | flags = &repr_priv->lag_port_flags; | |
647 | ||
648 | mutex_lock(&lag->lock); | |
649 | if (lag_lower_info->link_up) | |
650 | *flags |= NFP_PORT_LAG_LINK_UP; | |
651 | else | |
652 | *flags &= ~NFP_PORT_LAG_LINK_UP; | |
653 | ||
654 | if (lag_lower_info->tx_enabled) | |
655 | *flags |= NFP_PORT_LAG_TX_ENABLED; | |
656 | else | |
657 | *flags &= ~NFP_PORT_LAG_TX_ENABLED; | |
658 | ||
659 | *flags |= NFP_PORT_LAG_CHANGED; | |
660 | mutex_unlock(&lag->lock); | |
661 | ||
662 | schedule_delayed_work(&lag->work, NFP_FL_LAG_DELAY); | |
bb9a8d03 JH |
663 | } |
664 | ||
0c665e2b JK |
665 | int nfp_flower_lag_netdev_event(struct nfp_flower_priv *priv, |
666 | struct net_device *netdev, | |
667 | unsigned long event, void *ptr) | |
bb9a8d03 | 668 | { |
0c665e2b | 669 | struct nfp_fl_lag *lag = &priv->nfp_lag; |
bb9a8d03 JH |
670 | int err; |
671 | ||
bb9a8d03 JH |
672 | switch (event) { |
673 | case NETDEV_CHANGEUPPER: | |
674 | err = nfp_fl_lag_changeupper_event(lag, ptr); | |
675 | if (err) | |
676 | return NOTIFY_BAD; | |
677 | return NOTIFY_OK; | |
678 | case NETDEV_CHANGELOWERSTATE: | |
659bb404 | 679 | nfp_fl_lag_changels_event(lag, netdev, ptr); |
bb9a8d03 JH |
680 | return NOTIFY_OK; |
681 | case NETDEV_UNREGISTER: | |
a558c982 JK |
682 | nfp_fl_lag_schedule_group_delete(lag, netdev); |
683 | return NOTIFY_OK; | |
bb9a8d03 JH |
684 | } |
685 | ||
686 | return NOTIFY_DONE; | |
687 | } | |
688 | ||
689 | int nfp_flower_lag_reset(struct nfp_fl_lag *lag) | |
690 | { | |
691 | enum nfp_fl_lag_batch batch = NFP_FL_LAG_BATCH_FIRST; | |
692 | ||
693 | lag->rst_cfg = true; | |
694 | return nfp_fl_lag_config_group(lag, NULL, NULL, 0, &batch); | |
695 | } | |
696 | ||
697 | void nfp_flower_lag_init(struct nfp_fl_lag *lag) | |
698 | { | |
699 | INIT_DELAYED_WORK(&lag->work, nfp_fl_lag_do_work); | |
700 | INIT_LIST_HEAD(&lag->group_list); | |
701 | mutex_init(&lag->lock); | |
702 | ida_init(&lag->ida_handle); | |
703 | ||
2e1cc522 JH |
704 | __skb_queue_head_init(&lag->retrans_skbs); |
705 | ||
bb9a8d03 JH |
706 | /* 0 is a reserved batch version so increment to first valid value. */ |
707 | nfp_fl_increment_version(lag); | |
bb9a8d03 JH |
708 | } |
709 | ||
710 | void nfp_flower_lag_cleanup(struct nfp_fl_lag *lag) | |
711 | { | |
712 | struct nfp_fl_lag_group *entry, *storage; | |
713 | ||
714 | cancel_delayed_work_sync(&lag->work); | |
715 | ||
2e1cc522 JH |
716 | __skb_queue_purge(&lag->retrans_skbs); |
717 | ||
bb9a8d03 JH |
718 | /* Remove all groups. */ |
719 | mutex_lock(&lag->lock); | |
720 | list_for_each_entry_safe(entry, storage, &lag->group_list, list) { | |
721 | list_del(&entry->list); | |
722 | kfree(entry); | |
723 | } | |
724 | mutex_unlock(&lag->lock); | |
725 | mutex_destroy(&lag->lock); | |
726 | ida_destroy(&lag->ida_handle); | |
727 | } |