1 | // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) |
2 | /* Copyright (C) 2017-2018 Netronome Systems, Inc. */ |
3 | |
4 | #include <linux/etherdevice.h> |
5 | #include <linux/inetdevice.h> |
6 | #include <net/netevent.h> |
7 | #include <linux/idr.h> |
8 | #include <net/dst_metadata.h> |
9 | #include <net/arp.h> |
10 | |
11 | #include "cmsg.h" |
12 | #include "main.h" |
13 | #include "../nfp_net_repr.h" |
14 | #include "../nfp_net.h" |
15 | |
16 | #define NFP_FL_MAX_ROUTES 32 |
17 | |
18 | #define NFP_TUN_PRE_TUN_RULE_LIMIT 32 |
19 | #define NFP_TUN_PRE_TUN_RULE_DEL BIT(0) |
20 | #define NFP_TUN_PRE_TUN_IDX_BIT BIT(3) |
21 | #define NFP_TUN_PRE_TUN_IPV6_BIT BIT(7) |
22 | |
23 | /** |
24 | * struct nfp_tun_pre_tun_rule - rule matched before decap |
25 | * @flags: options for the rule offset |
26 | * @port_idx: index of destination MAC address for the rule |
27 | * @vlan_tci: VLAN info associated with MAC |
28 | * @host_ctx_id: stats context of rule to update |
29 | */ |
30 | struct nfp_tun_pre_tun_rule { |
31 | __be32 flags; |
32 | __be16 port_idx; |
33 | __be16 vlan_tci; |
34 | __be32 host_ctx_id; |
35 | }; |
36 | |
37 | /** |
38 | * struct nfp_tun_active_tuns - periodic message of active tunnels |
39 | * @seq: sequence number of the message |
40 | * @count: number of tunnels report in message |
41 | * @flags: options part of the request |
42 | * @tun_info.ipv4: dest IPv4 address of active route |
43 | * @tun_info.egress_port: port the encapsulated packet egressed |
44 | * @tun_info.extra: reserved for future use |
45 | * @tun_info: tunnels that have sent traffic in reported period |
46 | */ |
47 | struct nfp_tun_active_tuns { |
48 | __be32 seq; |
49 | __be32 count; |
50 | __be32 flags; |
51 | struct route_ip_info { |
52 | __be32 ipv4; |
53 | __be32 egress_port; |
54 | __be32 [2]; |
55 | } tun_info[]; |
56 | }; |
57 | |
58 | /** |
59 | * struct nfp_tun_active_tuns_v6 - periodic message of active IPv6 tunnels |
60 | * @seq: sequence number of the message |
61 | * @count: number of tunnels report in message |
62 | * @flags: options part of the request |
63 | * @tun_info.ipv6: dest IPv6 address of active route |
64 | * @tun_info.egress_port: port the encapsulated packet egressed |
65 | * @tun_info.extra: reserved for future use |
66 | * @tun_info: tunnels that have sent traffic in reported period |
67 | */ |
68 | struct nfp_tun_active_tuns_v6 { |
69 | __be32 seq; |
70 | __be32 count; |
71 | __be32 flags; |
72 | struct route_ip_info_v6 { |
73 | struct in6_addr ipv6; |
74 | __be32 egress_port; |
75 | __be32 [2]; |
76 | } tun_info[]; |
77 | }; |
78 | |
79 | /** |
80 | * struct nfp_tun_req_route_ipv4 - NFP requests a route/neighbour lookup |
81 | * @ingress_port: ingress port of packet that signalled request |
82 | * @ipv4_addr: destination ipv4 address for route |
83 | * @reserved: reserved for future use |
84 | */ |
85 | struct nfp_tun_req_route_ipv4 { |
86 | __be32 ingress_port; |
87 | __be32 ipv4_addr; |
88 | __be32 reserved[2]; |
89 | }; |
90 | |
91 | /** |
92 | * struct nfp_tun_req_route_ipv6 - NFP requests an IPv6 route/neighbour lookup |
93 | * @ingress_port: ingress port of packet that signalled request |
94 | * @ipv6_addr: destination ipv6 address for route |
95 | */ |
96 | struct nfp_tun_req_route_ipv6 { |
97 | __be32 ingress_port; |
98 | struct in6_addr ipv6_addr; |
99 | }; |
100 | |
101 | /** |
102 | * struct nfp_offloaded_route - routes that are offloaded to the NFP |
103 | * @list: list pointer |
104 | * @ip_add: destination of route - can be IPv4 or IPv6 |
105 | */ |
106 | struct nfp_offloaded_route { |
107 | struct list_head list; |
108 | u8 ip_add[]; |
109 | }; |
110 | |
111 | #define NFP_FL_IPV4_ADDRS_MAX 32 |
112 | |
113 | /** |
114 | * struct nfp_tun_ipv4_addr - set the IP address list on the NFP |
115 | * @count: number of IPs populated in the array |
116 | * @ipv4_addr: array of IPV4_ADDRS_MAX 32 bit IPv4 addresses |
117 | */ |
118 | struct nfp_tun_ipv4_addr { |
119 | __be32 count; |
120 | __be32 ipv4_addr[NFP_FL_IPV4_ADDRS_MAX]; |
121 | }; |
122 | |
123 | /** |
124 | * struct nfp_ipv4_addr_entry - cached IPv4 addresses |
125 | * @ipv4_addr: IP address |
126 | * @ref_count: number of rules currently using this IP |
127 | * @list: list pointer |
128 | */ |
129 | struct nfp_ipv4_addr_entry { |
130 | __be32 ipv4_addr; |
131 | int ref_count; |
132 | struct list_head list; |
133 | }; |
134 | |
135 | #define NFP_FL_IPV6_ADDRS_MAX 4 |
136 | |
137 | /** |
138 | * struct nfp_tun_ipv6_addr - set the IP address list on the NFP |
139 | * @count: number of IPs populated in the array |
140 | * @ipv6_addr: array of IPV6_ADDRS_MAX 128 bit IPv6 addresses |
141 | */ |
142 | struct nfp_tun_ipv6_addr { |
143 | __be32 count; |
144 | struct in6_addr ipv6_addr[NFP_FL_IPV6_ADDRS_MAX]; |
145 | }; |
146 | |
147 | #define NFP_TUN_MAC_OFFLOAD_DEL_FLAG 0x2 |
148 | |
149 | /** |
150 | * struct nfp_tun_mac_addr_offload - configure MAC address of tunnel EP on NFP |
151 | * @flags: MAC address offload options |
152 | * @count: number of MAC addresses in the message (should be 1) |
153 | * @index: index of MAC address in the lookup table |
154 | * @addr: interface MAC address |
155 | */ |
156 | struct nfp_tun_mac_addr_offload { |
157 | __be16 flags; |
158 | __be16 count; |
159 | __be16 index; |
160 | u8 addr[ETH_ALEN]; |
161 | }; |
162 | |
163 | /** |
164 | * struct nfp_neigh_update_work - update neighbour information to nfp |
165 | * @work: Work queue for writing neigh to the nfp |
166 | * @n: neighbour entry |
167 | * @app: Back pointer to app |
168 | */ |
169 | struct nfp_neigh_update_work { |
170 | struct work_struct work; |
171 | struct neighbour *n; |
172 | struct nfp_app *app; |
173 | }; |
174 | |
175 | enum nfp_flower_mac_offload_cmd { |
176 | NFP_TUNNEL_MAC_OFFLOAD_ADD = 0, |
177 | NFP_TUNNEL_MAC_OFFLOAD_DEL = 1, |
178 | NFP_TUNNEL_MAC_OFFLOAD_MOD = 2, |
179 | }; |
180 | |
181 | #define NFP_MAX_MAC_INDEX 0xff |
182 | |
183 | /** |
184 | * struct nfp_tun_offloaded_mac - hashtable entry for an offloaded MAC |
185 | * @ht_node: Hashtable entry |
186 | * @addr: Offloaded MAC address |
187 | * @index: Offloaded index for given MAC address |
188 | * @ref_count: Number of devs using this MAC address |
189 | * @repr_list: List of reprs sharing this MAC address |
190 | * @bridge_count: Number of bridge/internal devs with MAC |
191 | */ |
192 | struct nfp_tun_offloaded_mac { |
193 | struct rhash_head ht_node; |
194 | u8 addr[ETH_ALEN]; |
195 | u16 index; |
196 | int ref_count; |
197 | struct list_head repr_list; |
198 | int bridge_count; |
199 | }; |
200 | |
201 | static const struct rhashtable_params offloaded_macs_params = { |
202 | .key_offset = offsetof(struct nfp_tun_offloaded_mac, addr), |
203 | .head_offset = offsetof(struct nfp_tun_offloaded_mac, ht_node), |
204 | .key_len = ETH_ALEN, |
205 | .automatic_shrinking = true, |
206 | }; |
207 | |
208 | void nfp_tunnel_keep_alive(struct nfp_app *app, struct sk_buff *skb) |
209 | { |
210 | struct nfp_tun_active_tuns *payload; |
211 | struct net_device *netdev; |
212 | int count, i, pay_len; |
213 | struct neighbour *n; |
214 | __be32 ipv4_addr; |
215 | u32 port; |
216 | |
217 | payload = nfp_flower_cmsg_get_data(skb); |
218 | count = be32_to_cpu(payload->count); |
219 | if (count > NFP_FL_MAX_ROUTES) { |
220 | nfp_flower_cmsg_warn(app, "Tunnel keep-alive request exceeds max routes.\n" ); |
221 | return; |
222 | } |
223 | |
224 | pay_len = nfp_flower_cmsg_get_data_len(skb); |
225 | if (pay_len != struct_size(payload, tun_info, count)) { |
226 | nfp_flower_cmsg_warn(app, "Corruption in tunnel keep-alive message.\n" ); |
227 | return; |
228 | } |
229 | |
230 | rcu_read_lock(); |
231 | for (i = 0; i < count; i++) { |
232 | ipv4_addr = payload->tun_info[i].ipv4; |
233 | port = be32_to_cpu(payload->tun_info[i].egress_port); |
234 | netdev = nfp_app_dev_get(app, id: port, NULL); |
235 | if (!netdev) |
236 | continue; |
237 | |
238 | n = neigh_lookup(tbl: &arp_tbl, pkey: &ipv4_addr, dev: netdev); |
239 | if (!n) |
240 | continue; |
241 | |
242 | /* Update the used timestamp of neighbour */ |
243 | neigh_event_send(neigh: n, NULL); |
244 | neigh_release(neigh: n); |
245 | } |
246 | rcu_read_unlock(); |
247 | } |
248 | |
249 | void nfp_tunnel_keep_alive_v6(struct nfp_app *app, struct sk_buff *skb) |
250 | { |
251 | #if IS_ENABLED(CONFIG_IPV6) |
252 | struct nfp_tun_active_tuns_v6 *payload; |
253 | struct net_device *netdev; |
254 | int count, i, pay_len; |
255 | struct neighbour *n; |
256 | void *ipv6_add; |
257 | u32 port; |
258 | |
259 | payload = nfp_flower_cmsg_get_data(skb); |
260 | count = be32_to_cpu(payload->count); |
261 | if (count > NFP_FL_IPV6_ADDRS_MAX) { |
262 | nfp_flower_cmsg_warn(app, "IPv6 tunnel keep-alive request exceeds max routes.\n" ); |
263 | return; |
264 | } |
265 | |
266 | pay_len = nfp_flower_cmsg_get_data_len(skb); |
267 | if (pay_len != struct_size(payload, tun_info, count)) { |
268 | nfp_flower_cmsg_warn(app, "Corruption in tunnel keep-alive message.\n" ); |
269 | return; |
270 | } |
271 | |
272 | rcu_read_lock(); |
273 | for (i = 0; i < count; i++) { |
274 | ipv6_add = &payload->tun_info[i].ipv6; |
275 | port = be32_to_cpu(payload->tun_info[i].egress_port); |
276 | netdev = nfp_app_dev_get(app, id: port, NULL); |
277 | if (!netdev) |
278 | continue; |
279 | |
280 | n = neigh_lookup(tbl: &nd_tbl, pkey: ipv6_add, dev: netdev); |
281 | if (!n) |
282 | continue; |
283 | |
284 | /* Update the used timestamp of neighbour */ |
285 | neigh_event_send(neigh: n, NULL); |
286 | neigh_release(neigh: n); |
287 | } |
288 | rcu_read_unlock(); |
289 | #endif |
290 | } |
291 | |
292 | static int |
293 | nfp_flower_xmit_tun_conf(struct nfp_app *app, u8 mtype, u16 plen, void *pdata, |
294 | gfp_t flag) |
295 | { |
296 | struct nfp_flower_priv *priv = app->priv; |
297 | struct sk_buff *skb; |
298 | unsigned char *msg; |
299 | |
300 | if (!(priv->flower_ext_feats & NFP_FL_FEATS_DECAP_V2) && |
301 | (mtype == NFP_FLOWER_CMSG_TYPE_TUN_NEIGH || |
302 | mtype == NFP_FLOWER_CMSG_TYPE_TUN_NEIGH_V6)) |
303 | plen -= sizeof(struct nfp_tun_neigh_ext); |
304 | |
305 | if (!(priv->flower_ext_feats & NFP_FL_FEATS_TUNNEL_NEIGH_LAG) && |
306 | (mtype == NFP_FLOWER_CMSG_TYPE_TUN_NEIGH || |
307 | mtype == NFP_FLOWER_CMSG_TYPE_TUN_NEIGH_V6)) |
308 | plen -= sizeof(struct nfp_tun_neigh_lag); |
309 | |
310 | skb = nfp_flower_cmsg_alloc(app, size: plen, type: mtype, flag); |
311 | if (!skb) |
312 | return -ENOMEM; |
313 | |
314 | msg = nfp_flower_cmsg_get_data(skb); |
315 | memcpy(msg, pdata, nfp_flower_cmsg_get_data_len(skb)); |
316 | |
317 | nfp_ctrl_tx(nn: app->ctrl, skb); |
318 | return 0; |
319 | } |
320 | |
321 | static void |
322 | nfp_tun_mutual_link(struct nfp_predt_entry *predt, |
323 | struct nfp_neigh_entry *neigh) |
324 | { |
325 | struct nfp_fl_payload *flow_pay = predt->flow_pay; |
326 | struct nfp_tun_neigh_ext *ext; |
327 | struct nfp_tun_neigh *common; |
328 | |
329 | if (flow_pay->pre_tun_rule.is_ipv6 != neigh->is_ipv6) |
330 | return; |
331 | |
332 | /* In the case of bonding it is possible that there might already |
333 | * be a flow linked (as the MAC address gets shared). If a flow |
334 | * is already linked just return. |
335 | */ |
336 | if (neigh->flow) |
337 | return; |
338 | |
339 | common = neigh->is_ipv6 ? |
340 | &((struct nfp_tun_neigh_v6 *)neigh->payload)->common : |
341 | &((struct nfp_tun_neigh_v4 *)neigh->payload)->common; |
342 | ext = neigh->is_ipv6 ? |
343 | &((struct nfp_tun_neigh_v6 *)neigh->payload)->ext : |
344 | &((struct nfp_tun_neigh_v4 *)neigh->payload)->ext; |
345 | |
346 | if (memcmp(p: flow_pay->pre_tun_rule.loc_mac, |
347 | q: common->src_addr, ETH_ALEN) || |
348 | memcmp(p: flow_pay->pre_tun_rule.rem_mac, |
349 | q: common->dst_addr, ETH_ALEN)) |
350 | return; |
351 | |
352 | list_add(new: &neigh->list_head, head: &predt->nn_list); |
353 | neigh->flow = predt; |
354 | ext->host_ctx = flow_pay->meta.host_ctx_id; |
355 | ext->vlan_tci = flow_pay->pre_tun_rule.vlan_tci; |
356 | ext->vlan_tpid = flow_pay->pre_tun_rule.vlan_tpid; |
357 | } |
358 | |
359 | static void |
360 | nfp_tun_link_predt_entries(struct nfp_app *app, |
361 | struct nfp_neigh_entry *nn_entry) |
362 | { |
363 | struct nfp_flower_priv *priv = app->priv; |
364 | struct nfp_predt_entry *predt, *tmp; |
365 | |
366 | list_for_each_entry_safe(predt, tmp, &priv->predt_list, list_head) { |
367 | nfp_tun_mutual_link(predt, neigh: nn_entry); |
368 | } |
369 | } |
370 | |
371 | void nfp_tun_link_and_update_nn_entries(struct nfp_app *app, |
372 | struct nfp_predt_entry *predt) |
373 | { |
374 | struct nfp_flower_priv *priv = app->priv; |
375 | struct nfp_neigh_entry *nn_entry; |
376 | struct rhashtable_iter iter; |
377 | size_t neigh_size; |
378 | u8 type; |
379 | |
380 | rhashtable_walk_enter(ht: &priv->neigh_table, iter: &iter); |
381 | rhashtable_walk_start(iter: &iter); |
382 | while ((nn_entry = rhashtable_walk_next(iter: &iter)) != NULL) { |
383 | if (IS_ERR(ptr: nn_entry)) |
384 | continue; |
385 | nfp_tun_mutual_link(predt, neigh: nn_entry); |
386 | neigh_size = nn_entry->is_ipv6 ? |
387 | sizeof(struct nfp_tun_neigh_v6) : |
388 | sizeof(struct nfp_tun_neigh_v4); |
389 | type = nn_entry->is_ipv6 ? NFP_FLOWER_CMSG_TYPE_TUN_NEIGH_V6 : |
390 | NFP_FLOWER_CMSG_TYPE_TUN_NEIGH; |
391 | nfp_flower_xmit_tun_conf(app, mtype: type, plen: neigh_size, |
392 | pdata: nn_entry->payload, |
393 | GFP_ATOMIC); |
394 | } |
395 | rhashtable_walk_stop(iter: &iter); |
396 | rhashtable_walk_exit(iter: &iter); |
397 | } |
398 | |
399 | static void nfp_tun_cleanup_nn_entries(struct nfp_app *app) |
400 | { |
401 | struct nfp_flower_priv *priv = app->priv; |
402 | struct nfp_neigh_entry *neigh; |
403 | struct nfp_tun_neigh_ext *ext; |
404 | struct rhashtable_iter iter; |
405 | size_t neigh_size; |
406 | u8 type; |
407 | |
408 | rhashtable_walk_enter(ht: &priv->neigh_table, iter: &iter); |
409 | rhashtable_walk_start(iter: &iter); |
410 | while ((neigh = rhashtable_walk_next(iter: &iter)) != NULL) { |
411 | if (IS_ERR(ptr: neigh)) |
412 | continue; |
413 | ext = neigh->is_ipv6 ? |
414 | &((struct nfp_tun_neigh_v6 *)neigh->payload)->ext : |
415 | &((struct nfp_tun_neigh_v4 *)neigh->payload)->ext; |
416 | ext->host_ctx = cpu_to_be32(U32_MAX); |
417 | ext->vlan_tpid = cpu_to_be16(U16_MAX); |
418 | ext->vlan_tci = cpu_to_be16(U16_MAX); |
419 | |
420 | neigh_size = neigh->is_ipv6 ? |
421 | sizeof(struct nfp_tun_neigh_v6) : |
422 | sizeof(struct nfp_tun_neigh_v4); |
423 | type = neigh->is_ipv6 ? NFP_FLOWER_CMSG_TYPE_TUN_NEIGH_V6 : |
424 | NFP_FLOWER_CMSG_TYPE_TUN_NEIGH; |
425 | nfp_flower_xmit_tun_conf(app, mtype: type, plen: neigh_size, pdata: neigh->payload, |
426 | GFP_ATOMIC); |
427 | |
428 | rhashtable_remove_fast(ht: &priv->neigh_table, obj: &neigh->ht_node, |
429 | params: neigh_table_params); |
430 | if (neigh->flow) |
431 | list_del(entry: &neigh->list_head); |
432 | kfree(objp: neigh); |
433 | } |
434 | rhashtable_walk_stop(iter: &iter); |
435 | rhashtable_walk_exit(iter: &iter); |
436 | } |
437 | |
438 | void nfp_tun_unlink_and_update_nn_entries(struct nfp_app *app, |
439 | struct nfp_predt_entry *predt) |
440 | { |
441 | struct nfp_neigh_entry *neigh, *tmp; |
442 | struct nfp_tun_neigh_ext *ext; |
443 | size_t neigh_size; |
444 | u8 type; |
445 | |
446 | list_for_each_entry_safe(neigh, tmp, &predt->nn_list, list_head) { |
447 | ext = neigh->is_ipv6 ? |
448 | &((struct nfp_tun_neigh_v6 *)neigh->payload)->ext : |
449 | &((struct nfp_tun_neigh_v4 *)neigh->payload)->ext; |
450 | neigh->flow = NULL; |
451 | ext->host_ctx = cpu_to_be32(U32_MAX); |
452 | ext->vlan_tpid = cpu_to_be16(U16_MAX); |
453 | ext->vlan_tci = cpu_to_be16(U16_MAX); |
454 | list_del(entry: &neigh->list_head); |
455 | neigh_size = neigh->is_ipv6 ? |
456 | sizeof(struct nfp_tun_neigh_v6) : |
457 | sizeof(struct nfp_tun_neigh_v4); |
458 | type = neigh->is_ipv6 ? NFP_FLOWER_CMSG_TYPE_TUN_NEIGH_V6 : |
459 | NFP_FLOWER_CMSG_TYPE_TUN_NEIGH; |
460 | nfp_flower_xmit_tun_conf(app, mtype: type, plen: neigh_size, pdata: neigh->payload, |
461 | GFP_ATOMIC); |
462 | } |
463 | } |
464 | |
465 | static void |
466 | nfp_tun_write_neigh(struct net_device *netdev, struct nfp_app *app, |
467 | void *flow, struct neighbour *neigh, bool is_ipv6, |
468 | bool override) |
469 | { |
470 | bool neigh_invalid = !(neigh->nud_state & NUD_VALID) || neigh->dead; |
471 | size_t neigh_size = is_ipv6 ? sizeof(struct nfp_tun_neigh_v6) : |
472 | sizeof(struct nfp_tun_neigh_v4); |
473 | unsigned long cookie = (unsigned long)neigh; |
474 | struct nfp_flower_priv *priv = app->priv; |
475 | struct nfp_tun_neigh_lag lag_info; |
476 | struct nfp_neigh_entry *nn_entry; |
477 | u32 port_id; |
478 | u8 mtype; |
479 | |
480 | port_id = nfp_flower_get_port_id_from_netdev(app, netdev); |
481 | if (!port_id) |
482 | return; |
483 | |
484 | if ((port_id & NFP_FL_LAG_OUT) == NFP_FL_LAG_OUT) { |
485 | memset(&lag_info, 0, sizeof(struct nfp_tun_neigh_lag)); |
486 | nfp_flower_lag_get_info_from_netdev(app, netdev, lag: &lag_info); |
487 | } |
488 | |
489 | spin_lock_bh(lock: &priv->predt_lock); |
490 | nn_entry = rhashtable_lookup_fast(ht: &priv->neigh_table, key: &cookie, |
491 | params: neigh_table_params); |
492 | if (!nn_entry && !neigh_invalid) { |
493 | struct nfp_tun_neigh_ext *ext; |
494 | struct nfp_tun_neigh_lag *lag; |
495 | struct nfp_tun_neigh *common; |
496 | |
497 | nn_entry = kzalloc(size: sizeof(*nn_entry) + neigh_size, |
498 | GFP_ATOMIC); |
499 | if (!nn_entry) |
500 | goto err; |
501 | |
502 | nn_entry->payload = (char *)&nn_entry[1]; |
503 | nn_entry->neigh_cookie = cookie; |
504 | nn_entry->is_ipv6 = is_ipv6; |
505 | nn_entry->flow = NULL; |
506 | if (is_ipv6) { |
507 | struct flowi6 *flowi6 = (struct flowi6 *)flow; |
508 | struct nfp_tun_neigh_v6 *payload; |
509 | |
510 | payload = (struct nfp_tun_neigh_v6 *)nn_entry->payload; |
511 | payload->src_ipv6 = flowi6->saddr; |
512 | payload->dst_ipv6 = flowi6->daddr; |
513 | common = &payload->common; |
514 | ext = &payload->ext; |
515 | lag = &payload->lag; |
516 | mtype = NFP_FLOWER_CMSG_TYPE_TUN_NEIGH_V6; |
517 | } else { |
518 | struct flowi4 *flowi4 = (struct flowi4 *)flow; |
519 | struct nfp_tun_neigh_v4 *payload; |
520 | |
521 | payload = (struct nfp_tun_neigh_v4 *)nn_entry->payload; |
522 | payload->src_ipv4 = flowi4->saddr; |
523 | payload->dst_ipv4 = flowi4->daddr; |
524 | common = &payload->common; |
525 | ext = &payload->ext; |
526 | lag = &payload->lag; |
527 | mtype = NFP_FLOWER_CMSG_TYPE_TUN_NEIGH; |
528 | } |
529 | ext->host_ctx = cpu_to_be32(U32_MAX); |
530 | ext->vlan_tpid = cpu_to_be16(U16_MAX); |
531 | ext->vlan_tci = cpu_to_be16(U16_MAX); |
532 | ether_addr_copy(dst: common->src_addr, src: netdev->dev_addr); |
533 | neigh_ha_snapshot(dst: common->dst_addr, n: neigh, dev: netdev); |
534 | |
535 | if ((port_id & NFP_FL_LAG_OUT) == NFP_FL_LAG_OUT) |
536 | memcpy(lag, &lag_info, sizeof(struct nfp_tun_neigh_lag)); |
537 | common->port_id = cpu_to_be32(port_id); |
538 | |
539 | if (rhashtable_insert_fast(ht: &priv->neigh_table, |
540 | obj: &nn_entry->ht_node, |
541 | params: neigh_table_params)) |
542 | goto err; |
543 | |
544 | nfp_tun_link_predt_entries(app, nn_entry); |
545 | nfp_flower_xmit_tun_conf(app, mtype, plen: neigh_size, |
546 | pdata: nn_entry->payload, |
547 | GFP_ATOMIC); |
548 | } else if (nn_entry && neigh_invalid) { |
549 | if (is_ipv6) { |
550 | struct flowi6 *flowi6 = (struct flowi6 *)flow; |
551 | struct nfp_tun_neigh_v6 *payload; |
552 | |
553 | payload = (struct nfp_tun_neigh_v6 *)nn_entry->payload; |
554 | memset(payload, 0, sizeof(struct nfp_tun_neigh_v6)); |
555 | payload->dst_ipv6 = flowi6->daddr; |
556 | mtype = NFP_FLOWER_CMSG_TYPE_TUN_NEIGH_V6; |
557 | } else { |
558 | struct flowi4 *flowi4 = (struct flowi4 *)flow; |
559 | struct nfp_tun_neigh_v4 *payload; |
560 | |
561 | payload = (struct nfp_tun_neigh_v4 *)nn_entry->payload; |
562 | memset(payload, 0, sizeof(struct nfp_tun_neigh_v4)); |
563 | payload->dst_ipv4 = flowi4->daddr; |
564 | mtype = NFP_FLOWER_CMSG_TYPE_TUN_NEIGH; |
565 | } |
566 | /* Trigger ARP to verify invalid neighbour state. */ |
567 | neigh_event_send(neigh, NULL); |
568 | rhashtable_remove_fast(ht: &priv->neigh_table, |
569 | obj: &nn_entry->ht_node, |
570 | params: neigh_table_params); |
571 | |
572 | nfp_flower_xmit_tun_conf(app, mtype, plen: neigh_size, |
573 | pdata: nn_entry->payload, |
574 | GFP_ATOMIC); |
575 | |
576 | if (nn_entry->flow) |
577 | list_del(entry: &nn_entry->list_head); |
578 | kfree(objp: nn_entry); |
579 | } else if (nn_entry && !neigh_invalid) { |
580 | struct nfp_tun_neigh *common; |
581 | u8 dst_addr[ETH_ALEN]; |
582 | bool is_mac_change; |
583 | |
584 | if (is_ipv6) { |
585 | struct nfp_tun_neigh_v6 *payload; |
586 | |
587 | payload = (struct nfp_tun_neigh_v6 *)nn_entry->payload; |
588 | common = &payload->common; |
589 | mtype = NFP_FLOWER_CMSG_TYPE_TUN_NEIGH_V6; |
590 | } else { |
591 | struct nfp_tun_neigh_v4 *payload; |
592 | |
593 | payload = (struct nfp_tun_neigh_v4 *)nn_entry->payload; |
594 | common = &payload->common; |
595 | mtype = NFP_FLOWER_CMSG_TYPE_TUN_NEIGH; |
596 | } |
597 | |
598 | ether_addr_copy(dst: dst_addr, src: common->dst_addr); |
599 | neigh_ha_snapshot(dst: common->dst_addr, n: neigh, dev: netdev); |
600 | is_mac_change = !ether_addr_equal(addr1: dst_addr, addr2: common->dst_addr); |
601 | if (override || is_mac_change) { |
602 | if (is_mac_change && nn_entry->flow) { |
603 | list_del(entry: &nn_entry->list_head); |
604 | nn_entry->flow = NULL; |
605 | } |
606 | nfp_tun_link_predt_entries(app, nn_entry); |
607 | nfp_flower_xmit_tun_conf(app, mtype, plen: neigh_size, |
608 | pdata: nn_entry->payload, |
609 | GFP_ATOMIC); |
610 | } |
611 | } |
612 | |
613 | spin_unlock_bh(lock: &priv->predt_lock); |
614 | return; |
615 | |
616 | err: |
617 | kfree(objp: nn_entry); |
618 | spin_unlock_bh(lock: &priv->predt_lock); |
619 | nfp_flower_cmsg_warn(app, "Neighbour configuration failed.\n" ); |
620 | } |
621 | |
622 | static void |
623 | nfp_tun_release_neigh_update_work(struct nfp_neigh_update_work *update_work) |
624 | { |
625 | neigh_release(neigh: update_work->n); |
626 | kfree(objp: update_work); |
627 | } |
628 | |
629 | static void nfp_tun_neigh_update(struct work_struct *work) |
630 | { |
631 | struct nfp_neigh_update_work *update_work; |
632 | struct nfp_app *app; |
633 | struct neighbour *n; |
634 | bool neigh_invalid; |
635 | int err; |
636 | |
637 | update_work = container_of(work, struct nfp_neigh_update_work, work); |
638 | app = update_work->app; |
639 | n = update_work->n; |
640 | |
641 | if (!nfp_flower_get_port_id_from_netdev(app, netdev: n->dev)) |
642 | goto out; |
643 | |
644 | #if IS_ENABLED(CONFIG_INET) |
645 | neigh_invalid = !(n->nud_state & NUD_VALID) || n->dead; |
646 | if (n->tbl->family == AF_INET6) { |
647 | #if IS_ENABLED(CONFIG_IPV6) |
648 | struct flowi6 flow6 = {}; |
649 | |
650 | flow6.daddr = *(struct in6_addr *)n->primary_key; |
651 | if (!neigh_invalid) { |
652 | struct dst_entry *dst; |
653 | /* Use ipv6_dst_lookup_flow to populate flow6->saddr |
654 | * and other fields. This information is only needed |
655 | * for new entries, lookup can be skipped when an entry |
656 | * gets invalidated - as only the daddr is needed for |
657 | * deleting. |
658 | */ |
659 | dst = ip6_dst_lookup_flow(net: dev_net(dev: n->dev), NULL, |
660 | fl6: &flow6, NULL); |
661 | if (IS_ERR(ptr: dst)) |
662 | goto out; |
663 | |
664 | dst_release(dst); |
665 | } |
666 | nfp_tun_write_neigh(netdev: n->dev, app, flow: &flow6, neigh: n, is_ipv6: true, override: false); |
667 | #endif /* CONFIG_IPV6 */ |
668 | } else { |
669 | struct flowi4 flow4 = {}; |
670 | |
671 | flow4.daddr = *(__be32 *)n->primary_key; |
672 | if (!neigh_invalid) { |
673 | struct rtable *rt; |
674 | /* Use ip_route_output_key to populate flow4->saddr and |
675 | * other fields. This information is only needed for |
676 | * new entries, lookup can be skipped when an entry |
677 | * gets invalidated - as only the daddr is needed for |
678 | * deleting. |
679 | */ |
680 | rt = ip_route_output_key(net: dev_net(dev: n->dev), flp: &flow4); |
681 | err = PTR_ERR_OR_ZERO(ptr: rt); |
682 | if (err) |
683 | goto out; |
684 | |
685 | ip_rt_put(rt); |
686 | } |
687 | nfp_tun_write_neigh(netdev: n->dev, app, flow: &flow4, neigh: n, is_ipv6: false, override: false); |
688 | } |
689 | #endif /* CONFIG_INET */ |
690 | out: |
691 | nfp_tun_release_neigh_update_work(update_work); |
692 | } |
693 | |
694 | static struct nfp_neigh_update_work * |
695 | nfp_tun_alloc_neigh_update_work(struct nfp_app *app, struct neighbour *n) |
696 | { |
697 | struct nfp_neigh_update_work *update_work; |
698 | |
699 | update_work = kzalloc(size: sizeof(*update_work), GFP_ATOMIC); |
700 | if (!update_work) |
701 | return NULL; |
702 | |
703 | INIT_WORK(&update_work->work, nfp_tun_neigh_update); |
704 | neigh_hold(n); |
705 | update_work->n = n; |
706 | update_work->app = app; |
707 | |
708 | return update_work; |
709 | } |
710 | |
711 | static int |
712 | nfp_tun_neigh_event_handler(struct notifier_block *nb, unsigned long event, |
713 | void *ptr) |
714 | { |
715 | struct nfp_neigh_update_work *update_work; |
716 | struct nfp_flower_priv *app_priv; |
717 | struct netevent_redirect *redir; |
718 | struct neighbour *n; |
719 | struct nfp_app *app; |
720 | |
721 | switch (event) { |
722 | case NETEVENT_REDIRECT: |
723 | redir = (struct netevent_redirect *)ptr; |
724 | n = redir->neigh; |
725 | break; |
726 | case NETEVENT_NEIGH_UPDATE: |
727 | n = (struct neighbour *)ptr; |
728 | break; |
729 | default: |
730 | return NOTIFY_DONE; |
731 | } |
732 | #if IS_ENABLED(CONFIG_IPV6) |
733 | if (n->tbl != ipv6_stub->nd_tbl && n->tbl != &arp_tbl) |
734 | #else |
735 | if (n->tbl != &arp_tbl) |
736 | #endif |
737 | return NOTIFY_DONE; |
738 | |
739 | app_priv = container_of(nb, struct nfp_flower_priv, tun.neigh_nb); |
740 | app = app_priv->app; |
741 | update_work = nfp_tun_alloc_neigh_update_work(app, n); |
742 | if (!update_work) |
743 | return NOTIFY_DONE; |
744 | |
745 | queue_work(wq: system_highpri_wq, work: &update_work->work); |
746 | |
747 | return NOTIFY_DONE; |
748 | } |
749 | |
750 | void nfp_tunnel_request_route_v4(struct nfp_app *app, struct sk_buff *skb) |
751 | { |
752 | struct nfp_tun_req_route_ipv4 *payload; |
753 | struct net_device *netdev; |
754 | struct flowi4 flow = {}; |
755 | struct neighbour *n; |
756 | struct rtable *rt; |
757 | int err; |
758 | |
759 | payload = nfp_flower_cmsg_get_data(skb); |
760 | |
761 | rcu_read_lock(); |
762 | netdev = nfp_app_dev_get(app, be32_to_cpu(payload->ingress_port), NULL); |
763 | if (!netdev) |
764 | goto fail_rcu_unlock; |
765 | dev_hold(dev: netdev); |
766 | |
767 | flow.daddr = payload->ipv4_addr; |
768 | flow.flowi4_proto = IPPROTO_UDP; |
769 | |
770 | #if IS_ENABLED(CONFIG_INET) |
771 | /* Do a route lookup on same namespace as ingress port. */ |
772 | rt = ip_route_output_key(net: dev_net(dev: netdev), flp: &flow); |
773 | err = PTR_ERR_OR_ZERO(ptr: rt); |
774 | if (err) |
775 | goto fail_rcu_unlock; |
776 | #else |
777 | goto fail_rcu_unlock; |
778 | #endif |
779 | |
780 | /* Get the neighbour entry for the lookup */ |
781 | n = dst_neigh_lookup(dst: &rt->dst, daddr: &flow.daddr); |
782 | ip_rt_put(rt); |
783 | if (!n) |
784 | goto fail_rcu_unlock; |
785 | rcu_read_unlock(); |
786 | |
787 | nfp_tun_write_neigh(netdev: n->dev, app, flow: &flow, neigh: n, is_ipv6: false, override: true); |
788 | neigh_release(neigh: n); |
789 | dev_put(dev: netdev); |
790 | return; |
791 | |
792 | fail_rcu_unlock: |
793 | rcu_read_unlock(); |
794 | dev_put(dev: netdev); |
795 | nfp_flower_cmsg_warn(app, "Requested route not found.\n" ); |
796 | } |
797 | |
798 | void nfp_tunnel_request_route_v6(struct nfp_app *app, struct sk_buff *skb) |
799 | { |
800 | struct nfp_tun_req_route_ipv6 *payload; |
801 | struct net_device *netdev; |
802 | struct flowi6 flow = {}; |
803 | struct dst_entry *dst; |
804 | struct neighbour *n; |
805 | |
806 | payload = nfp_flower_cmsg_get_data(skb); |
807 | |
808 | rcu_read_lock(); |
809 | netdev = nfp_app_dev_get(app, be32_to_cpu(payload->ingress_port), NULL); |
810 | if (!netdev) |
811 | goto fail_rcu_unlock; |
812 | dev_hold(dev: netdev); |
813 | |
814 | flow.daddr = payload->ipv6_addr; |
815 | flow.flowi6_proto = IPPROTO_UDP; |
816 | |
817 | #if IS_ENABLED(CONFIG_INET) && IS_ENABLED(CONFIG_IPV6) |
818 | dst = ipv6_stub->ipv6_dst_lookup_flow(dev_net(dev: netdev), NULL, &flow, |
819 | NULL); |
820 | if (IS_ERR(ptr: dst)) |
821 | goto fail_rcu_unlock; |
822 | #else |
823 | goto fail_rcu_unlock; |
824 | #endif |
825 | |
826 | n = dst_neigh_lookup(dst, daddr: &flow.daddr); |
827 | dst_release(dst); |
828 | if (!n) |
829 | goto fail_rcu_unlock; |
830 | rcu_read_unlock(); |
831 | |
832 | nfp_tun_write_neigh(netdev: n->dev, app, flow: &flow, neigh: n, is_ipv6: true, override: true); |
833 | neigh_release(neigh: n); |
834 | dev_put(dev: netdev); |
835 | return; |
836 | |
837 | fail_rcu_unlock: |
838 | rcu_read_unlock(); |
839 | dev_put(dev: netdev); |
840 | nfp_flower_cmsg_warn(app, "Requested IPv6 route not found.\n" ); |
841 | } |
842 | |
843 | static void nfp_tun_write_ipv4_list(struct nfp_app *app) |
844 | { |
845 | struct nfp_flower_priv *priv = app->priv; |
846 | struct nfp_ipv4_addr_entry *entry; |
847 | struct nfp_tun_ipv4_addr payload; |
848 | struct list_head *ptr, *storage; |
849 | int count; |
850 | |
851 | memset(&payload, 0, sizeof(struct nfp_tun_ipv4_addr)); |
852 | mutex_lock(&priv->tun.ipv4_off_lock); |
853 | count = 0; |
854 | list_for_each_safe(ptr, storage, &priv->tun.ipv4_off_list) { |
855 | if (count >= NFP_FL_IPV4_ADDRS_MAX) { |
856 | mutex_unlock(lock: &priv->tun.ipv4_off_lock); |
857 | nfp_flower_cmsg_warn(app, "IPv4 offload exceeds limit.\n" ); |
858 | return; |
859 | } |
860 | entry = list_entry(ptr, struct nfp_ipv4_addr_entry, list); |
861 | payload.ipv4_addr[count++] = entry->ipv4_addr; |
862 | } |
863 | payload.count = cpu_to_be32(count); |
864 | mutex_unlock(lock: &priv->tun.ipv4_off_lock); |
865 | |
866 | nfp_flower_xmit_tun_conf(app, mtype: NFP_FLOWER_CMSG_TYPE_TUN_IPS, |
867 | plen: sizeof(struct nfp_tun_ipv4_addr), |
868 | pdata: &payload, GFP_KERNEL); |
869 | } |
870 | |
871 | void nfp_tunnel_add_ipv4_off(struct nfp_app *app, __be32 ipv4) |
872 | { |
873 | struct nfp_flower_priv *priv = app->priv; |
874 | struct nfp_ipv4_addr_entry *entry; |
875 | struct list_head *ptr, *storage; |
876 | |
877 | mutex_lock(&priv->tun.ipv4_off_lock); |
878 | list_for_each_safe(ptr, storage, &priv->tun.ipv4_off_list) { |
879 | entry = list_entry(ptr, struct nfp_ipv4_addr_entry, list); |
880 | if (entry->ipv4_addr == ipv4) { |
881 | entry->ref_count++; |
882 | mutex_unlock(lock: &priv->tun.ipv4_off_lock); |
883 | return; |
884 | } |
885 | } |
886 | |
887 | entry = kmalloc(size: sizeof(*entry), GFP_KERNEL); |
888 | if (!entry) { |
889 | mutex_unlock(lock: &priv->tun.ipv4_off_lock); |
890 | nfp_flower_cmsg_warn(app, "Mem error when offloading IP address.\n" ); |
891 | return; |
892 | } |
893 | entry->ipv4_addr = ipv4; |
894 | entry->ref_count = 1; |
895 | list_add_tail(new: &entry->list, head: &priv->tun.ipv4_off_list); |
896 | mutex_unlock(lock: &priv->tun.ipv4_off_lock); |
897 | |
898 | nfp_tun_write_ipv4_list(app); |
899 | } |
900 | |
901 | void nfp_tunnel_del_ipv4_off(struct nfp_app *app, __be32 ipv4) |
902 | { |
903 | struct nfp_flower_priv *priv = app->priv; |
904 | struct nfp_ipv4_addr_entry *entry; |
905 | struct list_head *ptr, *storage; |
906 | |
907 | mutex_lock(&priv->tun.ipv4_off_lock); |
908 | list_for_each_safe(ptr, storage, &priv->tun.ipv4_off_list) { |
909 | entry = list_entry(ptr, struct nfp_ipv4_addr_entry, list); |
910 | if (entry->ipv4_addr == ipv4) { |
911 | entry->ref_count--; |
912 | if (!entry->ref_count) { |
913 | list_del(entry: &entry->list); |
914 | kfree(objp: entry); |
915 | } |
916 | break; |
917 | } |
918 | } |
919 | mutex_unlock(lock: &priv->tun.ipv4_off_lock); |
920 | |
921 | nfp_tun_write_ipv4_list(app); |
922 | } |
923 | |
924 | static void nfp_tun_write_ipv6_list(struct nfp_app *app) |
925 | { |
926 | struct nfp_flower_priv *priv = app->priv; |
927 | struct nfp_ipv6_addr_entry *entry; |
928 | struct nfp_tun_ipv6_addr payload; |
929 | int count = 0; |
930 | |
931 | memset(&payload, 0, sizeof(struct nfp_tun_ipv6_addr)); |
932 | mutex_lock(&priv->tun.ipv6_off_lock); |
933 | list_for_each_entry(entry, &priv->tun.ipv6_off_list, list) { |
934 | if (count >= NFP_FL_IPV6_ADDRS_MAX) { |
935 | nfp_flower_cmsg_warn(app, "Too many IPv6 tunnel endpoint addresses, some cannot be offloaded.\n" ); |
936 | break; |
937 | } |
938 | payload.ipv6_addr[count++] = entry->ipv6_addr; |
939 | } |
940 | mutex_unlock(lock: &priv->tun.ipv6_off_lock); |
941 | payload.count = cpu_to_be32(count); |
942 | |
943 | nfp_flower_xmit_tun_conf(app, mtype: NFP_FLOWER_CMSG_TYPE_TUN_IPS_V6, |
944 | plen: sizeof(struct nfp_tun_ipv6_addr), |
945 | pdata: &payload, GFP_KERNEL); |
946 | } |
947 | |
948 | struct nfp_ipv6_addr_entry * |
949 | nfp_tunnel_add_ipv6_off(struct nfp_app *app, struct in6_addr *ipv6) |
950 | { |
951 | struct nfp_flower_priv *priv = app->priv; |
952 | struct nfp_ipv6_addr_entry *entry; |
953 | |
954 | mutex_lock(&priv->tun.ipv6_off_lock); |
955 | list_for_each_entry(entry, &priv->tun.ipv6_off_list, list) |
956 | if (!memcmp(p: &entry->ipv6_addr, q: ipv6, size: sizeof(*ipv6))) { |
957 | entry->ref_count++; |
958 | mutex_unlock(lock: &priv->tun.ipv6_off_lock); |
959 | return entry; |
960 | } |
961 | |
962 | entry = kmalloc(size: sizeof(*entry), GFP_KERNEL); |
963 | if (!entry) { |
964 | mutex_unlock(lock: &priv->tun.ipv6_off_lock); |
965 | nfp_flower_cmsg_warn(app, "Mem error when offloading IP address.\n" ); |
966 | return NULL; |
967 | } |
968 | entry->ipv6_addr = *ipv6; |
969 | entry->ref_count = 1; |
970 | list_add_tail(new: &entry->list, head: &priv->tun.ipv6_off_list); |
971 | mutex_unlock(lock: &priv->tun.ipv6_off_lock); |
972 | |
973 | nfp_tun_write_ipv6_list(app); |
974 | |
975 | return entry; |
976 | } |
977 | |
978 | void |
979 | nfp_tunnel_put_ipv6_off(struct nfp_app *app, struct nfp_ipv6_addr_entry *entry) |
980 | { |
981 | struct nfp_flower_priv *priv = app->priv; |
982 | bool freed = false; |
983 | |
984 | mutex_lock(&priv->tun.ipv6_off_lock); |
985 | if (!--entry->ref_count) { |
986 | list_del(entry: &entry->list); |
987 | kfree(objp: entry); |
988 | freed = true; |
989 | } |
990 | mutex_unlock(lock: &priv->tun.ipv6_off_lock); |
991 | |
992 | if (freed) |
993 | nfp_tun_write_ipv6_list(app); |
994 | } |
995 | |
996 | static int |
997 | __nfp_tunnel_offload_mac(struct nfp_app *app, const u8 *mac, u16 idx, bool del) |
998 | { |
999 | struct nfp_tun_mac_addr_offload payload; |
1000 | |
1001 | memset(&payload, 0, sizeof(payload)); |
1002 | |
1003 | if (del) |
1004 | payload.flags = cpu_to_be16(NFP_TUN_MAC_OFFLOAD_DEL_FLAG); |
1005 | |
1006 | /* FW supports multiple MACs per cmsg but restrict to single. */ |
1007 | payload.count = cpu_to_be16(1); |
1008 | payload.index = cpu_to_be16(idx); |
1009 | ether_addr_copy(dst: payload.addr, src: mac); |
1010 | |
1011 | return nfp_flower_xmit_tun_conf(app, mtype: NFP_FLOWER_CMSG_TYPE_TUN_MAC, |
1012 | plen: sizeof(struct nfp_tun_mac_addr_offload), |
1013 | pdata: &payload, GFP_KERNEL); |
1014 | } |
1015 | |
1016 | static bool nfp_tunnel_port_is_phy_repr(int port) |
1017 | { |
1018 | if (FIELD_GET(NFP_FLOWER_CMSG_PORT_TYPE, port) == |
1019 | NFP_FLOWER_CMSG_PORT_TYPE_PHYS_PORT) |
1020 | return true; |
1021 | |
1022 | return false; |
1023 | } |
1024 | |
1025 | static u16 nfp_tunnel_get_mac_idx_from_phy_port_id(int port) |
1026 | { |
1027 | return port << 8 | NFP_FLOWER_CMSG_PORT_TYPE_PHYS_PORT; |
1028 | } |
1029 | |
1030 | static u16 nfp_tunnel_get_global_mac_idx_from_ida(int id) |
1031 | { |
1032 | return id << 8 | NFP_FLOWER_CMSG_PORT_TYPE_OTHER_PORT; |
1033 | } |
1034 | |
1035 | static int nfp_tunnel_get_ida_from_global_mac_idx(u16 nfp_mac_idx) |
1036 | { |
1037 | return nfp_mac_idx >> 8; |
1038 | } |
1039 | |
1040 | static bool nfp_tunnel_is_mac_idx_global(u16 nfp_mac_idx) |
1041 | { |
1042 | return (nfp_mac_idx & 0xff) == NFP_FLOWER_CMSG_PORT_TYPE_OTHER_PORT; |
1043 | } |
1044 | |
1045 | static struct nfp_tun_offloaded_mac * |
1046 | nfp_tunnel_lookup_offloaded_macs(struct nfp_app *app, const u8 *mac) |
1047 | { |
1048 | struct nfp_flower_priv *priv = app->priv; |
1049 | |
1050 | return rhashtable_lookup_fast(ht: &priv->tun.offloaded_macs, key: mac, |
1051 | params: offloaded_macs_params); |
1052 | } |
1053 | |
1054 | static void |
1055 | nfp_tunnel_offloaded_macs_inc_ref_and_link(struct nfp_tun_offloaded_mac *entry, |
1056 | struct net_device *netdev, bool mod) |
1057 | { |
1058 | if (nfp_netdev_is_nfp_repr(netdev)) { |
1059 | struct nfp_flower_repr_priv *repr_priv; |
1060 | struct nfp_repr *repr; |
1061 | |
1062 | repr = netdev_priv(dev: netdev); |
1063 | repr_priv = repr->app_priv; |
1064 | |
1065 | /* If modifing MAC, remove repr from old list first. */ |
1066 | if (mod) |
1067 | list_del(entry: &repr_priv->mac_list); |
1068 | |
1069 | list_add_tail(new: &repr_priv->mac_list, head: &entry->repr_list); |
1070 | } else if (nfp_flower_is_supported_bridge(netdev)) { |
1071 | entry->bridge_count++; |
1072 | } |
1073 | |
1074 | entry->ref_count++; |
1075 | } |
1076 | |
1077 | static int |
1078 | nfp_tunnel_add_shared_mac(struct nfp_app *app, struct net_device *netdev, |
1079 | int port, bool mod) |
1080 | { |
1081 | struct nfp_flower_priv *priv = app->priv; |
1082 | struct nfp_tun_offloaded_mac *entry; |
1083 | int ida_idx = -1, err; |
1084 | u16 nfp_mac_idx = 0; |
1085 | |
1086 | entry = nfp_tunnel_lookup_offloaded_macs(app, mac: netdev->dev_addr); |
1087 | if (entry && (nfp_tunnel_is_mac_idx_global(nfp_mac_idx: entry->index) || netif_is_lag_port(dev: netdev))) { |
1088 | if (entry->bridge_count || |
1089 | !nfp_flower_is_supported_bridge(netdev)) { |
1090 | nfp_tunnel_offloaded_macs_inc_ref_and_link(entry, |
1091 | netdev, mod); |
1092 | return 0; |
1093 | } |
1094 | |
1095 | /* MAC is global but matches need to go to pre_tun table. */ |
1096 | nfp_mac_idx = entry->index | NFP_TUN_PRE_TUN_IDX_BIT; |
1097 | } |
1098 | |
1099 | if (!nfp_mac_idx) { |
1100 | /* Assign a global index if non-repr or MAC is now shared. */ |
1101 | if (entry || !port) { |
1102 | ida_idx = ida_alloc_max(ida: &priv->tun.mac_off_ids, |
1103 | NFP_MAX_MAC_INDEX, GFP_KERNEL); |
1104 | if (ida_idx < 0) |
1105 | return ida_idx; |
1106 | |
1107 | nfp_mac_idx = |
1108 | nfp_tunnel_get_global_mac_idx_from_ida(id: ida_idx); |
1109 | |
1110 | if (nfp_flower_is_supported_bridge(netdev)) |
1111 | nfp_mac_idx |= NFP_TUN_PRE_TUN_IDX_BIT; |
1112 | |
1113 | } else { |
1114 | nfp_mac_idx = |
1115 | nfp_tunnel_get_mac_idx_from_phy_port_id(port); |
1116 | } |
1117 | } |
1118 | |
1119 | if (!entry) { |
1120 | entry = kzalloc(size: sizeof(*entry), GFP_KERNEL); |
1121 | if (!entry) { |
1122 | err = -ENOMEM; |
1123 | goto err_free_ida; |
1124 | } |
1125 | |
1126 | ether_addr_copy(dst: entry->addr, src: netdev->dev_addr); |
1127 | INIT_LIST_HEAD(list: &entry->repr_list); |
1128 | |
1129 | if (rhashtable_insert_fast(ht: &priv->tun.offloaded_macs, |
1130 | obj: &entry->ht_node, |
1131 | params: offloaded_macs_params)) { |
1132 | err = -ENOMEM; |
1133 | goto err_free_entry; |
1134 | } |
1135 | } |
1136 | |
1137 | err = __nfp_tunnel_offload_mac(app, mac: netdev->dev_addr, |
1138 | idx: nfp_mac_idx, del: false); |
1139 | if (err) { |
1140 | /* If not shared then free. */ |
1141 | if (!entry->ref_count) |
1142 | goto err_remove_hash; |
1143 | goto err_free_ida; |
1144 | } |
1145 | |
1146 | entry->index = nfp_mac_idx; |
1147 | nfp_tunnel_offloaded_macs_inc_ref_and_link(entry, netdev, mod); |
1148 | |
1149 | return 0; |
1150 | |
1151 | err_remove_hash: |
1152 | rhashtable_remove_fast(ht: &priv->tun.offloaded_macs, obj: &entry->ht_node, |
1153 | params: offloaded_macs_params); |
1154 | err_free_entry: |
1155 | kfree(objp: entry); |
1156 | err_free_ida: |
1157 | if (ida_idx != -1) |
1158 | ida_free(&priv->tun.mac_off_ids, id: ida_idx); |
1159 | |
1160 | return err; |
1161 | } |
1162 | |
1163 | static int |
1164 | nfp_tunnel_del_shared_mac(struct nfp_app *app, struct net_device *netdev, |
1165 | const u8 *mac, bool mod) |
1166 | { |
1167 | struct nfp_flower_priv *priv = app->priv; |
1168 | struct nfp_flower_repr_priv *repr_priv; |
1169 | struct nfp_tun_offloaded_mac *entry; |
1170 | struct nfp_repr *repr; |
1171 | u16 nfp_mac_idx; |
1172 | int ida_idx; |
1173 | |
1174 | entry = nfp_tunnel_lookup_offloaded_macs(app, mac); |
1175 | if (!entry) |
1176 | return 0; |
1177 | |
1178 | entry->ref_count--; |
1179 | /* If del is part of a mod then mac_list is still in use elsewhere. */ |
1180 | if (nfp_netdev_is_nfp_repr(netdev) && !mod) { |
1181 | repr = netdev_priv(dev: netdev); |
1182 | repr_priv = repr->app_priv; |
1183 | list_del(entry: &repr_priv->mac_list); |
1184 | } |
1185 | |
1186 | if (nfp_flower_is_supported_bridge(netdev)) { |
1187 | entry->bridge_count--; |
1188 | |
1189 | if (!entry->bridge_count && entry->ref_count) { |
1190 | nfp_mac_idx = entry->index & ~NFP_TUN_PRE_TUN_IDX_BIT; |
1191 | if (__nfp_tunnel_offload_mac(app, mac, idx: nfp_mac_idx, |
1192 | del: false)) { |
1193 | nfp_flower_cmsg_warn(app, "MAC offload index revert failed on %s.\n" , |
1194 | netdev_name(netdev)); |
1195 | return 0; |
1196 | } |
1197 | |
1198 | entry->index = nfp_mac_idx; |
1199 | return 0; |
1200 | } |
1201 | } |
1202 | |
1203 | /* If MAC is now used by 1 repr set the offloaded MAC index to port. */ |
1204 | if (entry->ref_count == 1 && list_is_singular(head: &entry->repr_list)) { |
1205 | int port, err; |
1206 | |
1207 | repr_priv = list_first_entry(&entry->repr_list, |
1208 | struct nfp_flower_repr_priv, |
1209 | mac_list); |
1210 | repr = repr_priv->nfp_repr; |
1211 | port = nfp_repr_get_port_id(netdev: repr->netdev); |
1212 | nfp_mac_idx = nfp_tunnel_get_mac_idx_from_phy_port_id(port); |
1213 | err = __nfp_tunnel_offload_mac(app, mac, idx: nfp_mac_idx, del: false); |
1214 | if (err) { |
1215 | nfp_flower_cmsg_warn(app, "MAC offload index revert failed on %s.\n" , |
1216 | netdev_name(netdev)); |
1217 | return 0; |
1218 | } |
1219 | |
1220 | ida_idx = nfp_tunnel_get_ida_from_global_mac_idx(nfp_mac_idx: entry->index); |
1221 | ida_free(&priv->tun.mac_off_ids, id: ida_idx); |
1222 | entry->index = nfp_mac_idx; |
1223 | return 0; |
1224 | } |
1225 | |
1226 | if (entry->ref_count) |
1227 | return 0; |
1228 | |
1229 | WARN_ON_ONCE(rhashtable_remove_fast(&priv->tun.offloaded_macs, |
1230 | &entry->ht_node, |
1231 | offloaded_macs_params)); |
1232 | |
1233 | if (nfp_flower_is_supported_bridge(netdev)) |
1234 | nfp_mac_idx = entry->index & ~NFP_TUN_PRE_TUN_IDX_BIT; |
1235 | else |
1236 | nfp_mac_idx = entry->index; |
1237 | |
1238 | /* If MAC has global ID then extract and free the ida entry. */ |
1239 | if (nfp_tunnel_is_mac_idx_global(nfp_mac_idx)) { |
1240 | ida_idx = nfp_tunnel_get_ida_from_global_mac_idx(nfp_mac_idx: entry->index); |
1241 | ida_free(&priv->tun.mac_off_ids, id: ida_idx); |
1242 | } |
1243 | |
1244 | kfree(objp: entry); |
1245 | |
1246 | return __nfp_tunnel_offload_mac(app, mac, idx: 0, del: true); |
1247 | } |
1248 | |
1249 | static int |
1250 | nfp_tunnel_offload_mac(struct nfp_app *app, struct net_device *netdev, |
1251 | enum nfp_flower_mac_offload_cmd cmd) |
1252 | { |
1253 | struct nfp_flower_non_repr_priv *nr_priv = NULL; |
1254 | bool non_repr = false, *mac_offloaded; |
1255 | u8 *off_mac = NULL; |
1256 | int err, port = 0; |
1257 | |
1258 | if (nfp_netdev_is_nfp_repr(netdev)) { |
1259 | struct nfp_flower_repr_priv *repr_priv; |
1260 | struct nfp_repr *repr; |
1261 | |
1262 | repr = netdev_priv(dev: netdev); |
1263 | if (repr->app != app) |
1264 | return 0; |
1265 | |
1266 | repr_priv = repr->app_priv; |
1267 | if (repr_priv->on_bridge) |
1268 | return 0; |
1269 | |
1270 | mac_offloaded = &repr_priv->mac_offloaded; |
1271 | off_mac = &repr_priv->offloaded_mac_addr[0]; |
1272 | port = nfp_repr_get_port_id(netdev); |
1273 | if (!nfp_tunnel_port_is_phy_repr(port)) |
1274 | return 0; |
1275 | } else if (nfp_fl_is_netdev_to_offload(netdev)) { |
1276 | nr_priv = nfp_flower_non_repr_priv_get(app, netdev); |
1277 | if (!nr_priv) |
1278 | return -ENOMEM; |
1279 | |
1280 | mac_offloaded = &nr_priv->mac_offloaded; |
1281 | off_mac = &nr_priv->offloaded_mac_addr[0]; |
1282 | non_repr = true; |
1283 | } else { |
1284 | return 0; |
1285 | } |
1286 | |
1287 | if (!is_valid_ether_addr(addr: netdev->dev_addr)) { |
1288 | err = -EINVAL; |
1289 | goto err_put_non_repr_priv; |
1290 | } |
1291 | |
1292 | if (cmd == NFP_TUNNEL_MAC_OFFLOAD_MOD && !*mac_offloaded) |
1293 | cmd = NFP_TUNNEL_MAC_OFFLOAD_ADD; |
1294 | |
1295 | switch (cmd) { |
1296 | case NFP_TUNNEL_MAC_OFFLOAD_ADD: |
1297 | err = nfp_tunnel_add_shared_mac(app, netdev, port, mod: false); |
1298 | if (err) |
1299 | goto err_put_non_repr_priv; |
1300 | |
1301 | if (non_repr) |
1302 | __nfp_flower_non_repr_priv_get(non_repr_priv: nr_priv); |
1303 | |
1304 | *mac_offloaded = true; |
1305 | ether_addr_copy(dst: off_mac, src: netdev->dev_addr); |
1306 | break; |
1307 | case NFP_TUNNEL_MAC_OFFLOAD_DEL: |
1308 | /* Only attempt delete if add was successful. */ |
1309 | if (!*mac_offloaded) |
1310 | break; |
1311 | |
1312 | if (non_repr) |
1313 | __nfp_flower_non_repr_priv_put(non_repr_priv: nr_priv); |
1314 | |
1315 | *mac_offloaded = false; |
1316 | |
1317 | err = nfp_tunnel_del_shared_mac(app, netdev, mac: netdev->dev_addr, |
1318 | mod: false); |
1319 | if (err) |
1320 | goto err_put_non_repr_priv; |
1321 | |
1322 | break; |
1323 | case NFP_TUNNEL_MAC_OFFLOAD_MOD: |
1324 | /* Ignore if changing to the same address. */ |
1325 | if (ether_addr_equal(addr1: netdev->dev_addr, addr2: off_mac)) |
1326 | break; |
1327 | |
1328 | err = nfp_tunnel_add_shared_mac(app, netdev, port, mod: true); |
1329 | if (err) |
1330 | goto err_put_non_repr_priv; |
1331 | |
1332 | /* Delete the previous MAC address. */ |
1333 | err = nfp_tunnel_del_shared_mac(app, netdev, mac: off_mac, mod: true); |
1334 | if (err) |
1335 | nfp_flower_cmsg_warn(app, "Failed to remove offload of replaced MAC addr on %s.\n" , |
1336 | netdev_name(netdev)); |
1337 | |
1338 | ether_addr_copy(dst: off_mac, src: netdev->dev_addr); |
1339 | break; |
1340 | default: |
1341 | err = -EINVAL; |
1342 | goto err_put_non_repr_priv; |
1343 | } |
1344 | |
1345 | if (non_repr) |
1346 | __nfp_flower_non_repr_priv_put(non_repr_priv: nr_priv); |
1347 | |
1348 | return 0; |
1349 | |
1350 | err_put_non_repr_priv: |
1351 | if (non_repr) |
1352 | __nfp_flower_non_repr_priv_put(non_repr_priv: nr_priv); |
1353 | |
1354 | return err; |
1355 | } |
1356 | |
1357 | int nfp_tunnel_mac_event_handler(struct nfp_app *app, |
1358 | struct net_device *netdev, |
1359 | unsigned long event, void *ptr) |
1360 | { |
1361 | int err; |
1362 | |
1363 | if (event == NETDEV_DOWN) { |
1364 | err = nfp_tunnel_offload_mac(app, netdev, |
1365 | cmd: NFP_TUNNEL_MAC_OFFLOAD_DEL); |
1366 | if (err) |
1367 | nfp_flower_cmsg_warn(app, "Failed to delete offload MAC on %s.\n" , |
1368 | netdev_name(netdev)); |
1369 | } else if (event == NETDEV_UP) { |
1370 | err = nfp_tunnel_offload_mac(app, netdev, |
1371 | cmd: NFP_TUNNEL_MAC_OFFLOAD_ADD); |
1372 | if (err) |
1373 | nfp_flower_cmsg_warn(app, "Failed to offload MAC on %s.\n" , |
1374 | netdev_name(netdev)); |
1375 | } else if (event == NETDEV_CHANGEADDR) { |
1376 | /* Only offload addr change if netdev is already up. */ |
1377 | if (!(netdev->flags & IFF_UP)) |
1378 | return NOTIFY_OK; |
1379 | |
1380 | err = nfp_tunnel_offload_mac(app, netdev, |
1381 | cmd: NFP_TUNNEL_MAC_OFFLOAD_MOD); |
1382 | if (err) |
1383 | nfp_flower_cmsg_warn(app, "Failed to offload MAC change on %s.\n" , |
1384 | netdev_name(netdev)); |
1385 | } else if (event == NETDEV_CHANGEUPPER) { |
1386 | /* If a repr is attached to a bridge then tunnel packets |
1387 | * entering the physical port are directed through the bridge |
1388 | * datapath and cannot be directly detunneled. Therefore, |
1389 | * associated offloaded MACs and indexes should not be used |
1390 | * by fw for detunneling. |
1391 | */ |
1392 | struct netdev_notifier_changeupper_info *info = ptr; |
1393 | struct net_device *upper = info->upper_dev; |
1394 | struct nfp_flower_repr_priv *repr_priv; |
1395 | struct nfp_repr *repr; |
1396 | |
1397 | if (!nfp_netdev_is_nfp_repr(netdev) || |
1398 | !nfp_flower_is_supported_bridge(netdev: upper)) |
1399 | return NOTIFY_OK; |
1400 | |
1401 | repr = netdev_priv(dev: netdev); |
1402 | if (repr->app != app) |
1403 | return NOTIFY_OK; |
1404 | |
1405 | repr_priv = repr->app_priv; |
1406 | |
1407 | if (info->linking) { |
1408 | if (nfp_tunnel_offload_mac(app, netdev, |
1409 | cmd: NFP_TUNNEL_MAC_OFFLOAD_DEL)) |
1410 | nfp_flower_cmsg_warn(app, "Failed to delete offloaded MAC on %s.\n" , |
1411 | netdev_name(netdev)); |
1412 | repr_priv->on_bridge = true; |
1413 | } else { |
1414 | repr_priv->on_bridge = false; |
1415 | |
1416 | if (!(netdev->flags & IFF_UP)) |
1417 | return NOTIFY_OK; |
1418 | |
1419 | if (nfp_tunnel_offload_mac(app, netdev, |
1420 | cmd: NFP_TUNNEL_MAC_OFFLOAD_ADD)) |
1421 | nfp_flower_cmsg_warn(app, "Failed to offload MAC on %s.\n" , |
1422 | netdev_name(netdev)); |
1423 | } |
1424 | } |
1425 | return NOTIFY_OK; |
1426 | } |
1427 | |
1428 | int nfp_flower_xmit_pre_tun_flow(struct nfp_app *app, |
1429 | struct nfp_fl_payload *flow) |
1430 | { |
1431 | struct nfp_flower_priv *app_priv = app->priv; |
1432 | struct nfp_tun_offloaded_mac *mac_entry; |
1433 | struct nfp_flower_meta_tci *key_meta; |
1434 | struct nfp_tun_pre_tun_rule payload; |
1435 | struct net_device *internal_dev; |
1436 | int err; |
1437 | |
1438 | if (app_priv->pre_tun_rule_cnt == NFP_TUN_PRE_TUN_RULE_LIMIT) |
1439 | return -ENOSPC; |
1440 | |
1441 | memset(&payload, 0, sizeof(struct nfp_tun_pre_tun_rule)); |
1442 | |
1443 | internal_dev = flow->pre_tun_rule.dev; |
1444 | payload.vlan_tci = flow->pre_tun_rule.vlan_tci; |
1445 | payload.host_ctx_id = flow->meta.host_ctx_id; |
1446 | |
1447 | /* Lookup MAC index for the pre-tunnel rule egress device. |
1448 | * Note that because the device is always an internal port, it will |
1449 | * have a constant global index so does not need to be tracked. |
1450 | */ |
1451 | mac_entry = nfp_tunnel_lookup_offloaded_macs(app, |
1452 | mac: internal_dev->dev_addr); |
1453 | if (!mac_entry) |
1454 | return -ENOENT; |
1455 | |
1456 | /* Set/clear IPV6 bit. cpu_to_be16() swap will lead to MSB being |
1457 | * set/clear for port_idx. |
1458 | */ |
1459 | key_meta = (struct nfp_flower_meta_tci *)flow->unmasked_data; |
1460 | if (key_meta->nfp_flow_key_layer & NFP_FLOWER_LAYER_IPV6) |
1461 | mac_entry->index |= NFP_TUN_PRE_TUN_IPV6_BIT; |
1462 | else |
1463 | mac_entry->index &= ~NFP_TUN_PRE_TUN_IPV6_BIT; |
1464 | |
1465 | payload.port_idx = cpu_to_be16(mac_entry->index); |
1466 | |
1467 | /* Copy mac id and vlan to flow - dev may not exist at delete time. */ |
1468 | flow->pre_tun_rule.vlan_tci = payload.vlan_tci; |
1469 | flow->pre_tun_rule.port_idx = payload.port_idx; |
1470 | |
1471 | err = nfp_flower_xmit_tun_conf(app, mtype: NFP_FLOWER_CMSG_TYPE_PRE_TUN_RULE, |
1472 | plen: sizeof(struct nfp_tun_pre_tun_rule), |
1473 | pdata: (unsigned char *)&payload, GFP_KERNEL); |
1474 | if (err) |
1475 | return err; |
1476 | |
1477 | app_priv->pre_tun_rule_cnt++; |
1478 | |
1479 | return 0; |
1480 | } |
1481 | |
1482 | int nfp_flower_xmit_pre_tun_del_flow(struct nfp_app *app, |
1483 | struct nfp_fl_payload *flow) |
1484 | { |
1485 | struct nfp_flower_priv *app_priv = app->priv; |
1486 | struct nfp_tun_pre_tun_rule payload; |
1487 | u32 tmp_flags = 0; |
1488 | int err; |
1489 | |
1490 | memset(&payload, 0, sizeof(struct nfp_tun_pre_tun_rule)); |
1491 | |
1492 | tmp_flags |= NFP_TUN_PRE_TUN_RULE_DEL; |
1493 | payload.flags = cpu_to_be32(tmp_flags); |
1494 | payload.vlan_tci = flow->pre_tun_rule.vlan_tci; |
1495 | payload.port_idx = flow->pre_tun_rule.port_idx; |
1496 | |
1497 | err = nfp_flower_xmit_tun_conf(app, mtype: NFP_FLOWER_CMSG_TYPE_PRE_TUN_RULE, |
1498 | plen: sizeof(struct nfp_tun_pre_tun_rule), |
1499 | pdata: (unsigned char *)&payload, GFP_KERNEL); |
1500 | if (err) |
1501 | return err; |
1502 | |
1503 | app_priv->pre_tun_rule_cnt--; |
1504 | |
1505 | return 0; |
1506 | } |
1507 | |
1508 | int nfp_tunnel_config_start(struct nfp_app *app) |
1509 | { |
1510 | struct nfp_flower_priv *priv = app->priv; |
1511 | int err; |
1512 | |
1513 | /* Initialise rhash for MAC offload tracking. */ |
1514 | err = rhashtable_init(ht: &priv->tun.offloaded_macs, |
1515 | params: &offloaded_macs_params); |
1516 | if (err) |
1517 | return err; |
1518 | |
1519 | ida_init(ida: &priv->tun.mac_off_ids); |
1520 | |
1521 | /* Initialise priv data for IPv4/v6 offloading. */ |
1522 | mutex_init(&priv->tun.ipv4_off_lock); |
1523 | INIT_LIST_HEAD(list: &priv->tun.ipv4_off_list); |
1524 | mutex_init(&priv->tun.ipv6_off_lock); |
1525 | INIT_LIST_HEAD(list: &priv->tun.ipv6_off_list); |
1526 | |
1527 | /* Initialise priv data for neighbour offloading. */ |
1528 | priv->tun.neigh_nb.notifier_call = nfp_tun_neigh_event_handler; |
1529 | |
1530 | err = register_netevent_notifier(nb: &priv->tun.neigh_nb); |
1531 | if (err) { |
1532 | rhashtable_free_and_destroy(ht: &priv->tun.offloaded_macs, |
1533 | free_fn: nfp_check_rhashtable_empty, NULL); |
1534 | return err; |
1535 | } |
1536 | |
1537 | return 0; |
1538 | } |
1539 | |
1540 | void nfp_tunnel_config_stop(struct nfp_app *app) |
1541 | { |
1542 | struct nfp_flower_priv *priv = app->priv; |
1543 | struct nfp_ipv4_addr_entry *ip_entry; |
1544 | struct list_head *ptr, *storage; |
1545 | |
1546 | unregister_netevent_notifier(nb: &priv->tun.neigh_nb); |
1547 | |
1548 | ida_destroy(ida: &priv->tun.mac_off_ids); |
1549 | |
1550 | /* Free any memory that may be occupied by ipv4 list. */ |
1551 | list_for_each_safe(ptr, storage, &priv->tun.ipv4_off_list) { |
1552 | ip_entry = list_entry(ptr, struct nfp_ipv4_addr_entry, list); |
1553 | list_del(entry: &ip_entry->list); |
1554 | kfree(objp: ip_entry); |
1555 | } |
1556 | |
1557 | mutex_destroy(lock: &priv->tun.ipv6_off_lock); |
1558 | |
1559 | /* Destroy rhash. Entries should be cleaned on netdev notifier unreg. */ |
1560 | rhashtable_free_and_destroy(ht: &priv->tun.offloaded_macs, |
1561 | free_fn: nfp_check_rhashtable_empty, NULL); |
1562 | |
1563 | nfp_tun_cleanup_nn_entries(app); |
1564 | } |
1565 | |