1 | // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 |
2 | /* Copyright (c) 2018 Mellanox Technologies. All rights reserved */ |
3 | |
4 | #include <linux/err.h> |
5 | #include <linux/gfp.h> |
6 | #include <linux/kernel.h> |
7 | #include <linux/list.h> |
8 | #include <linux/netlink.h> |
9 | #include <linux/rtnetlink.h> |
10 | #include <linux/slab.h> |
11 | #include <net/inet_ecn.h> |
12 | #include <net/ipv6.h> |
13 | |
14 | #include "reg.h" |
15 | #include "spectrum.h" |
16 | #include "spectrum_nve.h" |
17 | |
18 | const struct mlxsw_sp_nve_ops *mlxsw_sp1_nve_ops_arr[] = { |
19 | [MLXSW_SP_NVE_TYPE_VXLAN] = &mlxsw_sp1_nve_vxlan_ops, |
20 | }; |
21 | |
22 | const struct mlxsw_sp_nve_ops *mlxsw_sp2_nve_ops_arr[] = { |
23 | [MLXSW_SP_NVE_TYPE_VXLAN] = &mlxsw_sp2_nve_vxlan_ops, |
24 | }; |
25 | |
26 | struct mlxsw_sp_nve_mc_entry; |
27 | struct mlxsw_sp_nve_mc_record; |
28 | struct mlxsw_sp_nve_mc_list; |
29 | |
30 | struct mlxsw_sp_nve_mc_record_ops { |
31 | enum mlxsw_reg_tnumt_record_type type; |
32 | int (*entry_add)(struct mlxsw_sp_nve_mc_record *mc_record, |
33 | struct mlxsw_sp_nve_mc_entry *mc_entry, |
34 | const union mlxsw_sp_l3addr *addr); |
35 | void (*entry_del)(const struct mlxsw_sp_nve_mc_record *mc_record, |
36 | const struct mlxsw_sp_nve_mc_entry *mc_entry); |
37 | void (*entry_set)(const struct mlxsw_sp_nve_mc_record *mc_record, |
38 | const struct mlxsw_sp_nve_mc_entry *mc_entry, |
39 | char *tnumt_pl, unsigned int entry_index); |
40 | bool (*entry_compare)(const struct mlxsw_sp_nve_mc_record *mc_record, |
41 | const struct mlxsw_sp_nve_mc_entry *mc_entry, |
42 | const union mlxsw_sp_l3addr *addr); |
43 | }; |
44 | |
45 | struct mlxsw_sp_nve_mc_list_key { |
46 | u16 fid_index; |
47 | }; |
48 | |
49 | struct mlxsw_sp_nve_mc_ipv6_entry { |
50 | struct in6_addr addr6; |
51 | u32 addr6_kvdl_index; |
52 | }; |
53 | |
54 | struct mlxsw_sp_nve_mc_entry { |
55 | union { |
56 | __be32 addr4; |
57 | struct mlxsw_sp_nve_mc_ipv6_entry ipv6_entry; |
58 | }; |
59 | u8 valid:1; |
60 | }; |
61 | |
62 | struct mlxsw_sp_nve_mc_record { |
63 | struct list_head list; |
64 | enum mlxsw_sp_l3proto proto; |
65 | unsigned int num_entries; |
66 | struct mlxsw_sp *mlxsw_sp; |
67 | struct mlxsw_sp_nve_mc_list *mc_list; |
68 | const struct mlxsw_sp_nve_mc_record_ops *ops; |
69 | u32 kvdl_index; |
70 | struct mlxsw_sp_nve_mc_entry entries[]; |
71 | }; |
72 | |
73 | struct mlxsw_sp_nve_mc_list { |
74 | struct list_head records_list; |
75 | struct rhash_head ht_node; |
76 | struct mlxsw_sp_nve_mc_list_key key; |
77 | }; |
78 | |
79 | static const struct rhashtable_params mlxsw_sp_nve_mc_list_ht_params = { |
80 | .key_len = sizeof(struct mlxsw_sp_nve_mc_list_key), |
81 | .key_offset = offsetof(struct mlxsw_sp_nve_mc_list, key), |
82 | .head_offset = offsetof(struct mlxsw_sp_nve_mc_list, ht_node), |
83 | }; |
84 | |
85 | static int |
86 | mlxsw_sp_nve_mc_record_ipv4_entry_add(struct mlxsw_sp_nve_mc_record *mc_record, |
87 | struct mlxsw_sp_nve_mc_entry *mc_entry, |
88 | const union mlxsw_sp_l3addr *addr) |
89 | { |
90 | mc_entry->addr4 = addr->addr4; |
91 | |
92 | return 0; |
93 | } |
94 | |
95 | static void |
96 | mlxsw_sp_nve_mc_record_ipv4_entry_del(const struct mlxsw_sp_nve_mc_record *mc_record, |
97 | const struct mlxsw_sp_nve_mc_entry *mc_entry) |
98 | { |
99 | } |
100 | |
101 | static void |
102 | mlxsw_sp_nve_mc_record_ipv4_entry_set(const struct mlxsw_sp_nve_mc_record *mc_record, |
103 | const struct mlxsw_sp_nve_mc_entry *mc_entry, |
104 | char *tnumt_pl, unsigned int entry_index) |
105 | { |
106 | u32 udip = be32_to_cpu(mc_entry->addr4); |
107 | |
108 | mlxsw_reg_tnumt_udip_set(buf: tnumt_pl, index: entry_index, val: udip); |
109 | } |
110 | |
111 | static bool |
112 | mlxsw_sp_nve_mc_record_ipv4_entry_compare(const struct mlxsw_sp_nve_mc_record *mc_record, |
113 | const struct mlxsw_sp_nve_mc_entry *mc_entry, |
114 | const union mlxsw_sp_l3addr *addr) |
115 | { |
116 | return mc_entry->addr4 == addr->addr4; |
117 | } |
118 | |
119 | static const struct mlxsw_sp_nve_mc_record_ops |
120 | mlxsw_sp_nve_mc_record_ipv4_ops = { |
121 | .type = MLXSW_REG_TNUMT_RECORD_TYPE_IPV4, |
122 | .entry_add = &mlxsw_sp_nve_mc_record_ipv4_entry_add, |
123 | .entry_del = &mlxsw_sp_nve_mc_record_ipv4_entry_del, |
124 | .entry_set = &mlxsw_sp_nve_mc_record_ipv4_entry_set, |
125 | .entry_compare = &mlxsw_sp_nve_mc_record_ipv4_entry_compare, |
126 | }; |
127 | |
128 | static int |
129 | mlxsw_sp_nve_mc_record_ipv6_entry_add(struct mlxsw_sp_nve_mc_record *mc_record, |
130 | struct mlxsw_sp_nve_mc_entry *mc_entry, |
131 | const union mlxsw_sp_l3addr *addr) |
132 | { |
133 | u32 kvdl_index; |
134 | int err; |
135 | |
136 | err = mlxsw_sp_ipv6_addr_kvdl_index_get(mlxsw_sp: mc_record->mlxsw_sp, |
137 | addr6: &addr->addr6, p_kvdl_index: &kvdl_index); |
138 | if (err) |
139 | return err; |
140 | |
141 | mc_entry->ipv6_entry.addr6 = addr->addr6; |
142 | mc_entry->ipv6_entry.addr6_kvdl_index = kvdl_index; |
143 | return 0; |
144 | } |
145 | |
146 | static void |
147 | mlxsw_sp_nve_mc_record_ipv6_entry_del(const struct mlxsw_sp_nve_mc_record *mc_record, |
148 | const struct mlxsw_sp_nve_mc_entry *mc_entry) |
149 | { |
150 | mlxsw_sp_ipv6_addr_put(mlxsw_sp: mc_record->mlxsw_sp, |
151 | addr6: &mc_entry->ipv6_entry.addr6); |
152 | } |
153 | |
154 | static void |
155 | mlxsw_sp_nve_mc_record_ipv6_entry_set(const struct mlxsw_sp_nve_mc_record *mc_record, |
156 | const struct mlxsw_sp_nve_mc_entry *mc_entry, |
157 | char *tnumt_pl, unsigned int entry_index) |
158 | { |
159 | u32 udip_ptr = mc_entry->ipv6_entry.addr6_kvdl_index; |
160 | |
161 | mlxsw_reg_tnumt_udip_ptr_set(buf: tnumt_pl, index: entry_index, val: udip_ptr); |
162 | } |
163 | |
164 | static bool |
165 | mlxsw_sp_nve_mc_record_ipv6_entry_compare(const struct mlxsw_sp_nve_mc_record *mc_record, |
166 | const struct mlxsw_sp_nve_mc_entry *mc_entry, |
167 | const union mlxsw_sp_l3addr *addr) |
168 | { |
169 | return ipv6_addr_equal(a1: &mc_entry->ipv6_entry.addr6, a2: &addr->addr6); |
170 | } |
171 | |
172 | static const struct mlxsw_sp_nve_mc_record_ops |
173 | mlxsw_sp_nve_mc_record_ipv6_ops = { |
174 | .type = MLXSW_REG_TNUMT_RECORD_TYPE_IPV6, |
175 | .entry_add = &mlxsw_sp_nve_mc_record_ipv6_entry_add, |
176 | .entry_del = &mlxsw_sp_nve_mc_record_ipv6_entry_del, |
177 | .entry_set = &mlxsw_sp_nve_mc_record_ipv6_entry_set, |
178 | .entry_compare = &mlxsw_sp_nve_mc_record_ipv6_entry_compare, |
179 | }; |
180 | |
181 | static const struct mlxsw_sp_nve_mc_record_ops * |
182 | mlxsw_sp_nve_mc_record_ops_arr[] = { |
183 | [MLXSW_SP_L3_PROTO_IPV4] = &mlxsw_sp_nve_mc_record_ipv4_ops, |
184 | [MLXSW_SP_L3_PROTO_IPV6] = &mlxsw_sp_nve_mc_record_ipv6_ops, |
185 | }; |
186 | |
187 | int mlxsw_sp_nve_learned_ip_resolve(struct mlxsw_sp *mlxsw_sp, u32 uip, |
188 | enum mlxsw_sp_l3proto proto, |
189 | union mlxsw_sp_l3addr *addr) |
190 | { |
191 | switch (proto) { |
192 | case MLXSW_SP_L3_PROTO_IPV4: |
193 | addr->addr4 = cpu_to_be32(uip); |
194 | return 0; |
195 | default: |
196 | WARN_ON(1); |
197 | return -EINVAL; |
198 | } |
199 | } |
200 | |
201 | static struct mlxsw_sp_nve_mc_list * |
202 | mlxsw_sp_nve_mc_list_find(struct mlxsw_sp *mlxsw_sp, |
203 | const struct mlxsw_sp_nve_mc_list_key *key) |
204 | { |
205 | struct mlxsw_sp_nve *nve = mlxsw_sp->nve; |
206 | |
207 | return rhashtable_lookup_fast(ht: &nve->mc_list_ht, key, |
208 | params: mlxsw_sp_nve_mc_list_ht_params); |
209 | } |
210 | |
211 | static struct mlxsw_sp_nve_mc_list * |
212 | mlxsw_sp_nve_mc_list_create(struct mlxsw_sp *mlxsw_sp, |
213 | const struct mlxsw_sp_nve_mc_list_key *key) |
214 | { |
215 | struct mlxsw_sp_nve *nve = mlxsw_sp->nve; |
216 | struct mlxsw_sp_nve_mc_list *mc_list; |
217 | int err; |
218 | |
219 | mc_list = kmalloc(size: sizeof(*mc_list), GFP_KERNEL); |
220 | if (!mc_list) |
221 | return ERR_PTR(error: -ENOMEM); |
222 | |
223 | INIT_LIST_HEAD(list: &mc_list->records_list); |
224 | mc_list->key = *key; |
225 | |
226 | err = rhashtable_insert_fast(ht: &nve->mc_list_ht, obj: &mc_list->ht_node, |
227 | params: mlxsw_sp_nve_mc_list_ht_params); |
228 | if (err) |
229 | goto err_rhashtable_insert; |
230 | |
231 | return mc_list; |
232 | |
233 | err_rhashtable_insert: |
234 | kfree(objp: mc_list); |
235 | return ERR_PTR(error: err); |
236 | } |
237 | |
238 | static void mlxsw_sp_nve_mc_list_destroy(struct mlxsw_sp *mlxsw_sp, |
239 | struct mlxsw_sp_nve_mc_list *mc_list) |
240 | { |
241 | struct mlxsw_sp_nve *nve = mlxsw_sp->nve; |
242 | |
243 | rhashtable_remove_fast(ht: &nve->mc_list_ht, obj: &mc_list->ht_node, |
244 | params: mlxsw_sp_nve_mc_list_ht_params); |
245 | WARN_ON(!list_empty(&mc_list->records_list)); |
246 | kfree(objp: mc_list); |
247 | } |
248 | |
249 | static struct mlxsw_sp_nve_mc_list * |
250 | mlxsw_sp_nve_mc_list_get(struct mlxsw_sp *mlxsw_sp, |
251 | const struct mlxsw_sp_nve_mc_list_key *key) |
252 | { |
253 | struct mlxsw_sp_nve_mc_list *mc_list; |
254 | |
255 | mc_list = mlxsw_sp_nve_mc_list_find(mlxsw_sp, key); |
256 | if (mc_list) |
257 | return mc_list; |
258 | |
259 | return mlxsw_sp_nve_mc_list_create(mlxsw_sp, key); |
260 | } |
261 | |
262 | static void |
263 | mlxsw_sp_nve_mc_list_put(struct mlxsw_sp *mlxsw_sp, |
264 | struct mlxsw_sp_nve_mc_list *mc_list) |
265 | { |
266 | if (!list_empty(head: &mc_list->records_list)) |
267 | return; |
268 | mlxsw_sp_nve_mc_list_destroy(mlxsw_sp, mc_list); |
269 | } |
270 | |
271 | static struct mlxsw_sp_nve_mc_record * |
272 | mlxsw_sp_nve_mc_record_create(struct mlxsw_sp *mlxsw_sp, |
273 | struct mlxsw_sp_nve_mc_list *mc_list, |
274 | enum mlxsw_sp_l3proto proto) |
275 | { |
276 | unsigned int num_max_entries = mlxsw_sp->nve->num_max_mc_entries[proto]; |
277 | struct mlxsw_sp_nve_mc_record *mc_record; |
278 | int err; |
279 | |
280 | mc_record = kzalloc(struct_size(mc_record, entries, num_max_entries), |
281 | GFP_KERNEL); |
282 | if (!mc_record) |
283 | return ERR_PTR(error: -ENOMEM); |
284 | |
285 | err = mlxsw_sp_kvdl_alloc(mlxsw_sp, type: MLXSW_SP_KVDL_ENTRY_TYPE_TNUMT, entry_count: 1, |
286 | p_entry_index: &mc_record->kvdl_index); |
287 | if (err) |
288 | goto err_kvdl_alloc; |
289 | |
290 | mc_record->ops = mlxsw_sp_nve_mc_record_ops_arr[proto]; |
291 | mc_record->mlxsw_sp = mlxsw_sp; |
292 | mc_record->mc_list = mc_list; |
293 | mc_record->proto = proto; |
294 | list_add_tail(new: &mc_record->list, head: &mc_list->records_list); |
295 | |
296 | return mc_record; |
297 | |
298 | err_kvdl_alloc: |
299 | kfree(objp: mc_record); |
300 | return ERR_PTR(error: err); |
301 | } |
302 | |
303 | static void |
304 | mlxsw_sp_nve_mc_record_destroy(struct mlxsw_sp_nve_mc_record *mc_record) |
305 | { |
306 | struct mlxsw_sp *mlxsw_sp = mc_record->mlxsw_sp; |
307 | |
308 | list_del(entry: &mc_record->list); |
309 | mlxsw_sp_kvdl_free(mlxsw_sp, type: MLXSW_SP_KVDL_ENTRY_TYPE_TNUMT, entry_count: 1, |
310 | entry_index: mc_record->kvdl_index); |
311 | WARN_ON(mc_record->num_entries); |
312 | kfree(objp: mc_record); |
313 | } |
314 | |
315 | static struct mlxsw_sp_nve_mc_record * |
316 | mlxsw_sp_nve_mc_record_get(struct mlxsw_sp *mlxsw_sp, |
317 | struct mlxsw_sp_nve_mc_list *mc_list, |
318 | enum mlxsw_sp_l3proto proto) |
319 | { |
320 | struct mlxsw_sp_nve_mc_record *mc_record; |
321 | |
322 | list_for_each_entry_reverse(mc_record, &mc_list->records_list, list) { |
323 | unsigned int num_entries = mc_record->num_entries; |
324 | struct mlxsw_sp_nve *nve = mlxsw_sp->nve; |
325 | |
326 | if (mc_record->proto == proto && |
327 | num_entries < nve->num_max_mc_entries[proto]) |
328 | return mc_record; |
329 | } |
330 | |
331 | return mlxsw_sp_nve_mc_record_create(mlxsw_sp, mc_list, proto); |
332 | } |
333 | |
334 | static void |
335 | mlxsw_sp_nve_mc_record_put(struct mlxsw_sp_nve_mc_record *mc_record) |
336 | { |
337 | if (mc_record->num_entries != 0) |
338 | return; |
339 | |
340 | mlxsw_sp_nve_mc_record_destroy(mc_record); |
341 | } |
342 | |
343 | static struct mlxsw_sp_nve_mc_entry * |
344 | mlxsw_sp_nve_mc_free_entry_find(struct mlxsw_sp_nve_mc_record *mc_record) |
345 | { |
346 | struct mlxsw_sp_nve *nve = mc_record->mlxsw_sp->nve; |
347 | unsigned int num_max_entries; |
348 | int i; |
349 | |
350 | num_max_entries = nve->num_max_mc_entries[mc_record->proto]; |
351 | for (i = 0; i < num_max_entries; i++) { |
352 | if (mc_record->entries[i].valid) |
353 | continue; |
354 | return &mc_record->entries[i]; |
355 | } |
356 | |
357 | return NULL; |
358 | } |
359 | |
360 | static int |
361 | mlxsw_sp_nve_mc_record_refresh(struct mlxsw_sp_nve_mc_record *mc_record) |
362 | { |
363 | enum mlxsw_reg_tnumt_record_type type = mc_record->ops->type; |
364 | struct mlxsw_sp_nve_mc_list *mc_list = mc_record->mc_list; |
365 | struct mlxsw_sp *mlxsw_sp = mc_record->mlxsw_sp; |
366 | char tnumt_pl[MLXSW_REG_TNUMT_LEN]; |
367 | unsigned int num_max_entries; |
368 | unsigned int num_entries = 0; |
369 | u32 next_kvdl_index = 0; |
370 | bool next_valid = false; |
371 | int i; |
372 | |
373 | if (!list_is_last(list: &mc_record->list, head: &mc_list->records_list)) { |
374 | struct mlxsw_sp_nve_mc_record *next_record; |
375 | |
376 | next_record = list_next_entry(mc_record, list); |
377 | next_kvdl_index = next_record->kvdl_index; |
378 | next_valid = true; |
379 | } |
380 | |
381 | mlxsw_reg_tnumt_pack(payload: tnumt_pl, type, tport: MLXSW_REG_TUNNEL_PORT_NVE, |
382 | underlay_mc_ptr: mc_record->kvdl_index, vnext: next_valid, |
383 | next_underlay_mc_ptr: next_kvdl_index, record_size: mc_record->num_entries); |
384 | |
385 | num_max_entries = mlxsw_sp->nve->num_max_mc_entries[mc_record->proto]; |
386 | for (i = 0; i < num_max_entries; i++) { |
387 | struct mlxsw_sp_nve_mc_entry *mc_entry; |
388 | |
389 | mc_entry = &mc_record->entries[i]; |
390 | if (!mc_entry->valid) |
391 | continue; |
392 | mc_record->ops->entry_set(mc_record, mc_entry, tnumt_pl, |
393 | num_entries++); |
394 | } |
395 | |
396 | WARN_ON(num_entries != mc_record->num_entries); |
397 | |
398 | return mlxsw_reg_write(mlxsw_core: mlxsw_sp->core, MLXSW_REG(tnumt), payload: tnumt_pl); |
399 | } |
400 | |
401 | static bool |
402 | mlxsw_sp_nve_mc_record_is_first(struct mlxsw_sp_nve_mc_record *mc_record) |
403 | { |
404 | struct mlxsw_sp_nve_mc_list *mc_list = mc_record->mc_list; |
405 | struct mlxsw_sp_nve_mc_record *first_record; |
406 | |
407 | first_record = list_first_entry(&mc_list->records_list, |
408 | struct mlxsw_sp_nve_mc_record, list); |
409 | |
410 | return mc_record == first_record; |
411 | } |
412 | |
413 | static struct mlxsw_sp_nve_mc_entry * |
414 | mlxsw_sp_nve_mc_entry_find(struct mlxsw_sp_nve_mc_record *mc_record, |
415 | union mlxsw_sp_l3addr *addr) |
416 | { |
417 | struct mlxsw_sp_nve *nve = mc_record->mlxsw_sp->nve; |
418 | unsigned int num_max_entries; |
419 | int i; |
420 | |
421 | num_max_entries = nve->num_max_mc_entries[mc_record->proto]; |
422 | for (i = 0; i < num_max_entries; i++) { |
423 | struct mlxsw_sp_nve_mc_entry *mc_entry; |
424 | |
425 | mc_entry = &mc_record->entries[i]; |
426 | if (!mc_entry->valid) |
427 | continue; |
428 | if (mc_record->ops->entry_compare(mc_record, mc_entry, addr)) |
429 | return mc_entry; |
430 | } |
431 | |
432 | return NULL; |
433 | } |
434 | |
435 | static int |
436 | mlxsw_sp_nve_mc_record_ip_add(struct mlxsw_sp_nve_mc_record *mc_record, |
437 | union mlxsw_sp_l3addr *addr) |
438 | { |
439 | struct mlxsw_sp_nve_mc_entry *mc_entry = NULL; |
440 | int err; |
441 | |
442 | mc_entry = mlxsw_sp_nve_mc_free_entry_find(mc_record); |
443 | if (WARN_ON(!mc_entry)) |
444 | return -EINVAL; |
445 | |
446 | err = mc_record->ops->entry_add(mc_record, mc_entry, addr); |
447 | if (err) |
448 | return err; |
449 | mc_record->num_entries++; |
450 | mc_entry->valid = true; |
451 | |
452 | err = mlxsw_sp_nve_mc_record_refresh(mc_record); |
453 | if (err) |
454 | goto err_record_refresh; |
455 | |
456 | /* If this is a new record and not the first one, then we need to |
457 | * update the next pointer of the previous entry |
458 | */ |
459 | if (mc_record->num_entries != 1 || |
460 | mlxsw_sp_nve_mc_record_is_first(mc_record)) |
461 | return 0; |
462 | |
463 | err = mlxsw_sp_nve_mc_record_refresh(list_prev_entry(mc_record, list)); |
464 | if (err) |
465 | goto err_prev_record_refresh; |
466 | |
467 | return 0; |
468 | |
469 | err_prev_record_refresh: |
470 | err_record_refresh: |
471 | mc_entry->valid = false; |
472 | mc_record->num_entries--; |
473 | mc_record->ops->entry_del(mc_record, mc_entry); |
474 | return err; |
475 | } |
476 | |
477 | static void |
478 | mlxsw_sp_nve_mc_record_entry_del(struct mlxsw_sp_nve_mc_record *mc_record, |
479 | struct mlxsw_sp_nve_mc_entry *mc_entry) |
480 | { |
481 | struct mlxsw_sp_nve_mc_list *mc_list = mc_record->mc_list; |
482 | |
483 | mc_entry->valid = false; |
484 | mc_record->num_entries--; |
485 | |
486 | /* When the record continues to exist we only need to invalidate |
487 | * the requested entry |
488 | */ |
489 | if (mc_record->num_entries != 0) { |
490 | mlxsw_sp_nve_mc_record_refresh(mc_record); |
491 | mc_record->ops->entry_del(mc_record, mc_entry); |
492 | return; |
493 | } |
494 | |
495 | /* If the record needs to be deleted, but it is not the first, |
496 | * then we need to make sure that the previous record no longer |
497 | * points to it. Remove deleted record from the list to reflect |
498 | * that and then re-add it at the end, so that it could be |
499 | * properly removed by the record destruction code |
500 | */ |
501 | if (!mlxsw_sp_nve_mc_record_is_first(mc_record)) { |
502 | struct mlxsw_sp_nve_mc_record *prev_record; |
503 | |
504 | prev_record = list_prev_entry(mc_record, list); |
505 | list_del(entry: &mc_record->list); |
506 | mlxsw_sp_nve_mc_record_refresh(mc_record: prev_record); |
507 | list_add_tail(new: &mc_record->list, head: &mc_list->records_list); |
508 | mc_record->ops->entry_del(mc_record, mc_entry); |
509 | return; |
510 | } |
511 | |
512 | /* If the first record needs to be deleted, but the list is not |
513 | * singular, then the second record needs to be written in the |
514 | * first record's address, as this address is stored as a property |
515 | * of the FID |
516 | */ |
517 | if (mlxsw_sp_nve_mc_record_is_first(mc_record) && |
518 | !list_is_singular(head: &mc_list->records_list)) { |
519 | struct mlxsw_sp_nve_mc_record *next_record; |
520 | |
521 | next_record = list_next_entry(mc_record, list); |
522 | swap(mc_record->kvdl_index, next_record->kvdl_index); |
523 | mlxsw_sp_nve_mc_record_refresh(mc_record: next_record); |
524 | mc_record->ops->entry_del(mc_record, mc_entry); |
525 | return; |
526 | } |
527 | |
528 | /* This is the last case where the last remaining record needs to |
529 | * be deleted. Simply delete the entry |
530 | */ |
531 | mc_record->ops->entry_del(mc_record, mc_entry); |
532 | } |
533 | |
534 | static struct mlxsw_sp_nve_mc_record * |
535 | mlxsw_sp_nve_mc_record_find(struct mlxsw_sp_nve_mc_list *mc_list, |
536 | enum mlxsw_sp_l3proto proto, |
537 | union mlxsw_sp_l3addr *addr, |
538 | struct mlxsw_sp_nve_mc_entry **mc_entry) |
539 | { |
540 | struct mlxsw_sp_nve_mc_record *mc_record; |
541 | |
542 | list_for_each_entry(mc_record, &mc_list->records_list, list) { |
543 | if (mc_record->proto != proto) |
544 | continue; |
545 | |
546 | *mc_entry = mlxsw_sp_nve_mc_entry_find(mc_record, addr); |
547 | if (*mc_entry) |
548 | return mc_record; |
549 | } |
550 | |
551 | return NULL; |
552 | } |
553 | |
554 | static int mlxsw_sp_nve_mc_list_ip_add(struct mlxsw_sp *mlxsw_sp, |
555 | struct mlxsw_sp_nve_mc_list *mc_list, |
556 | enum mlxsw_sp_l3proto proto, |
557 | union mlxsw_sp_l3addr *addr) |
558 | { |
559 | struct mlxsw_sp_nve_mc_record *mc_record; |
560 | int err; |
561 | |
562 | mc_record = mlxsw_sp_nve_mc_record_get(mlxsw_sp, mc_list, proto); |
563 | if (IS_ERR(ptr: mc_record)) |
564 | return PTR_ERR(ptr: mc_record); |
565 | |
566 | err = mlxsw_sp_nve_mc_record_ip_add(mc_record, addr); |
567 | if (err) |
568 | goto err_ip_add; |
569 | |
570 | return 0; |
571 | |
572 | err_ip_add: |
573 | mlxsw_sp_nve_mc_record_put(mc_record); |
574 | return err; |
575 | } |
576 | |
577 | static void mlxsw_sp_nve_mc_list_ip_del(struct mlxsw_sp *mlxsw_sp, |
578 | struct mlxsw_sp_nve_mc_list *mc_list, |
579 | enum mlxsw_sp_l3proto proto, |
580 | union mlxsw_sp_l3addr *addr) |
581 | { |
582 | struct mlxsw_sp_nve_mc_record *mc_record; |
583 | struct mlxsw_sp_nve_mc_entry *mc_entry; |
584 | |
585 | mc_record = mlxsw_sp_nve_mc_record_find(mc_list, proto, addr, |
586 | mc_entry: &mc_entry); |
587 | if (!mc_record) |
588 | return; |
589 | |
590 | mlxsw_sp_nve_mc_record_entry_del(mc_record, mc_entry); |
591 | mlxsw_sp_nve_mc_record_put(mc_record); |
592 | } |
593 | |
594 | static int |
595 | mlxsw_sp_nve_fid_flood_index_set(struct mlxsw_sp_fid *fid, |
596 | struct mlxsw_sp_nve_mc_list *mc_list) |
597 | { |
598 | struct mlxsw_sp_nve_mc_record *mc_record; |
599 | |
600 | /* The address of the first record in the list is a property of |
601 | * the FID and we never change it. It only needs to be set when |
602 | * a new list is created |
603 | */ |
604 | if (mlxsw_sp_fid_nve_flood_index_is_set(fid)) |
605 | return 0; |
606 | |
607 | mc_record = list_first_entry(&mc_list->records_list, |
608 | struct mlxsw_sp_nve_mc_record, list); |
609 | |
610 | return mlxsw_sp_fid_nve_flood_index_set(fid, nve_flood_index: mc_record->kvdl_index); |
611 | } |
612 | |
613 | static void |
614 | mlxsw_sp_nve_fid_flood_index_clear(struct mlxsw_sp_fid *fid, |
615 | struct mlxsw_sp_nve_mc_list *mc_list) |
616 | { |
617 | struct mlxsw_sp_nve_mc_record *mc_record; |
618 | |
619 | /* The address of the first record needs to be invalidated only when |
620 | * the last record is about to be removed |
621 | */ |
622 | if (!list_is_singular(head: &mc_list->records_list)) |
623 | return; |
624 | |
625 | mc_record = list_first_entry(&mc_list->records_list, |
626 | struct mlxsw_sp_nve_mc_record, list); |
627 | if (mc_record->num_entries != 1) |
628 | return; |
629 | |
630 | return mlxsw_sp_fid_nve_flood_index_clear(fid); |
631 | } |
632 | |
633 | int mlxsw_sp_nve_flood_ip_add(struct mlxsw_sp *mlxsw_sp, |
634 | struct mlxsw_sp_fid *fid, |
635 | enum mlxsw_sp_l3proto proto, |
636 | union mlxsw_sp_l3addr *addr) |
637 | { |
638 | struct mlxsw_sp_nve_mc_list_key key = { 0 }; |
639 | struct mlxsw_sp_nve_mc_list *mc_list; |
640 | int err; |
641 | |
642 | key.fid_index = mlxsw_sp_fid_index(fid); |
643 | mc_list = mlxsw_sp_nve_mc_list_get(mlxsw_sp, key: &key); |
644 | if (IS_ERR(ptr: mc_list)) |
645 | return PTR_ERR(ptr: mc_list); |
646 | |
647 | err = mlxsw_sp_nve_mc_list_ip_add(mlxsw_sp, mc_list, proto, addr); |
648 | if (err) |
649 | goto err_add_ip; |
650 | |
651 | err = mlxsw_sp_nve_fid_flood_index_set(fid, mc_list); |
652 | if (err) |
653 | goto err_fid_flood_index_set; |
654 | |
655 | return 0; |
656 | |
657 | err_fid_flood_index_set: |
658 | mlxsw_sp_nve_mc_list_ip_del(mlxsw_sp, mc_list, proto, addr); |
659 | err_add_ip: |
660 | mlxsw_sp_nve_mc_list_put(mlxsw_sp, mc_list); |
661 | return err; |
662 | } |
663 | |
664 | void mlxsw_sp_nve_flood_ip_del(struct mlxsw_sp *mlxsw_sp, |
665 | struct mlxsw_sp_fid *fid, |
666 | enum mlxsw_sp_l3proto proto, |
667 | union mlxsw_sp_l3addr *addr) |
668 | { |
669 | struct mlxsw_sp_nve_mc_list_key key = { 0 }; |
670 | struct mlxsw_sp_nve_mc_list *mc_list; |
671 | |
672 | key.fid_index = mlxsw_sp_fid_index(fid); |
673 | mc_list = mlxsw_sp_nve_mc_list_find(mlxsw_sp, key: &key); |
674 | if (!mc_list) |
675 | return; |
676 | |
677 | mlxsw_sp_nve_fid_flood_index_clear(fid, mc_list); |
678 | mlxsw_sp_nve_mc_list_ip_del(mlxsw_sp, mc_list, proto, addr); |
679 | mlxsw_sp_nve_mc_list_put(mlxsw_sp, mc_list); |
680 | } |
681 | |
682 | static void |
683 | mlxsw_sp_nve_mc_record_delete(struct mlxsw_sp_nve_mc_record *mc_record) |
684 | { |
685 | struct mlxsw_sp_nve *nve = mc_record->mlxsw_sp->nve; |
686 | unsigned int num_max_entries; |
687 | int i; |
688 | |
689 | num_max_entries = nve->num_max_mc_entries[mc_record->proto]; |
690 | for (i = 0; i < num_max_entries; i++) { |
691 | struct mlxsw_sp_nve_mc_entry *mc_entry = &mc_record->entries[i]; |
692 | |
693 | if (!mc_entry->valid) |
694 | continue; |
695 | mlxsw_sp_nve_mc_record_entry_del(mc_record, mc_entry); |
696 | } |
697 | |
698 | WARN_ON(mc_record->num_entries); |
699 | mlxsw_sp_nve_mc_record_put(mc_record); |
700 | } |
701 | |
702 | static void mlxsw_sp_nve_flood_ip_flush(struct mlxsw_sp *mlxsw_sp, |
703 | struct mlxsw_sp_fid *fid) |
704 | { |
705 | struct mlxsw_sp_nve_mc_record *mc_record, *tmp; |
706 | struct mlxsw_sp_nve_mc_list_key key = { 0 }; |
707 | struct mlxsw_sp_nve_mc_list *mc_list; |
708 | |
709 | if (!mlxsw_sp_fid_nve_flood_index_is_set(fid)) |
710 | return; |
711 | |
712 | mlxsw_sp_fid_nve_flood_index_clear(fid); |
713 | |
714 | key.fid_index = mlxsw_sp_fid_index(fid); |
715 | mc_list = mlxsw_sp_nve_mc_list_find(mlxsw_sp, key: &key); |
716 | if (WARN_ON(!mc_list)) |
717 | return; |
718 | |
719 | list_for_each_entry_safe(mc_record, tmp, &mc_list->records_list, list) |
720 | mlxsw_sp_nve_mc_record_delete(mc_record); |
721 | |
722 | WARN_ON(!list_empty(&mc_list->records_list)); |
723 | mlxsw_sp_nve_mc_list_put(mlxsw_sp, mc_list); |
724 | } |
725 | |
726 | static int mlxsw_sp_nve_tunnel_init(struct mlxsw_sp *mlxsw_sp, |
727 | struct mlxsw_sp_nve_config *config) |
728 | { |
729 | struct mlxsw_sp_nve *nve = mlxsw_sp->nve; |
730 | const struct mlxsw_sp_nve_ops *ops; |
731 | int err; |
732 | |
733 | if (nve->num_nve_tunnels++ != 0) |
734 | return 0; |
735 | |
736 | nve->config = *config; |
737 | |
738 | err = mlxsw_sp_kvdl_alloc(mlxsw_sp, type: MLXSW_SP_KVDL_ENTRY_TYPE_ADJ, entry_count: 1, |
739 | p_entry_index: &nve->tunnel_index); |
740 | if (err) |
741 | goto err_kvdl_alloc; |
742 | |
743 | ops = nve->nve_ops_arr[config->type]; |
744 | err = ops->init(nve, config); |
745 | if (err) |
746 | goto err_ops_init; |
747 | |
748 | return 0; |
749 | |
750 | err_ops_init: |
751 | mlxsw_sp_kvdl_free(mlxsw_sp, type: MLXSW_SP_KVDL_ENTRY_TYPE_ADJ, entry_count: 1, |
752 | entry_index: nve->tunnel_index); |
753 | err_kvdl_alloc: |
754 | memset(&nve->config, 0, sizeof(nve->config)); |
755 | nve->num_nve_tunnels--; |
756 | return err; |
757 | } |
758 | |
759 | static void mlxsw_sp_nve_tunnel_fini(struct mlxsw_sp *mlxsw_sp) |
760 | { |
761 | struct mlxsw_sp_nve *nve = mlxsw_sp->nve; |
762 | const struct mlxsw_sp_nve_ops *ops; |
763 | |
764 | ops = nve->nve_ops_arr[nve->config.type]; |
765 | |
766 | if (mlxsw_sp->nve->num_nve_tunnels == 1) { |
767 | ops->fini(nve); |
768 | mlxsw_sp_kvdl_free(mlxsw_sp, type: MLXSW_SP_KVDL_ENTRY_TYPE_ADJ, entry_count: 1, |
769 | entry_index: nve->tunnel_index); |
770 | memset(&nve->config, 0, sizeof(nve->config)); |
771 | } |
772 | nve->num_nve_tunnels--; |
773 | } |
774 | |
775 | static void mlxsw_sp_nve_fdb_flush_by_fid(struct mlxsw_sp *mlxsw_sp, |
776 | u16 fid_index) |
777 | { |
778 | char sfdf_pl[MLXSW_REG_SFDF_LEN]; |
779 | |
780 | mlxsw_reg_sfdf_pack(payload: sfdf_pl, type: MLXSW_REG_SFDF_FLUSH_PER_NVE_AND_FID); |
781 | mlxsw_reg_sfdf_fid_set(buf: sfdf_pl, val: fid_index); |
782 | mlxsw_reg_write(mlxsw_core: mlxsw_sp->core, MLXSW_REG(sfdf), payload: sfdf_pl); |
783 | } |
784 | |
785 | static void mlxsw_sp_nve_fdb_clear_offload(struct mlxsw_sp *mlxsw_sp, |
786 | const struct mlxsw_sp_fid *fid, |
787 | const struct net_device *nve_dev, |
788 | __be32 vni) |
789 | { |
790 | const struct mlxsw_sp_nve_ops *ops; |
791 | enum mlxsw_sp_nve_type type; |
792 | |
793 | if (WARN_ON(mlxsw_sp_fid_nve_type(fid, &type))) |
794 | return; |
795 | |
796 | ops = mlxsw_sp->nve->nve_ops_arr[type]; |
797 | ops->fdb_clear_offload(nve_dev, vni); |
798 | } |
799 | |
800 | struct mlxsw_sp_nve_ipv6_ht_key { |
801 | u8 mac[ETH_ALEN]; |
802 | u16 fid_index; |
803 | }; |
804 | |
805 | struct mlxsw_sp_nve_ipv6_ht_node { |
806 | struct rhash_head ht_node; |
807 | struct list_head list; |
808 | struct mlxsw_sp_nve_ipv6_ht_key key; |
809 | struct in6_addr addr6; |
810 | }; |
811 | |
812 | static const struct rhashtable_params mlxsw_sp_nve_ipv6_ht_params = { |
813 | .key_len = sizeof(struct mlxsw_sp_nve_ipv6_ht_key), |
814 | .key_offset = offsetof(struct mlxsw_sp_nve_ipv6_ht_node, key), |
815 | .head_offset = offsetof(struct mlxsw_sp_nve_ipv6_ht_node, ht_node), |
816 | }; |
817 | |
818 | int mlxsw_sp_nve_ipv6_addr_kvdl_set(struct mlxsw_sp *mlxsw_sp, |
819 | const struct in6_addr *addr6, |
820 | u32 *p_kvdl_index) |
821 | { |
822 | return mlxsw_sp_ipv6_addr_kvdl_index_get(mlxsw_sp, addr6, p_kvdl_index); |
823 | } |
824 | |
825 | void mlxsw_sp_nve_ipv6_addr_kvdl_unset(struct mlxsw_sp *mlxsw_sp, |
826 | const struct in6_addr *addr6) |
827 | { |
828 | mlxsw_sp_ipv6_addr_put(mlxsw_sp, addr6); |
829 | } |
830 | |
831 | static struct mlxsw_sp_nve_ipv6_ht_node * |
832 | mlxsw_sp_nve_ipv6_ht_node_lookup(struct mlxsw_sp *mlxsw_sp, const char *mac, |
833 | u16 fid_index) |
834 | { |
835 | struct mlxsw_sp_nve_ipv6_ht_key key = {}; |
836 | |
837 | ether_addr_copy(dst: key.mac, src: mac); |
838 | key.fid_index = fid_index; |
839 | return rhashtable_lookup_fast(ht: &mlxsw_sp->nve->ipv6_ht, key: &key, |
840 | params: mlxsw_sp_nve_ipv6_ht_params); |
841 | } |
842 | |
843 | static int mlxsw_sp_nve_ipv6_ht_insert(struct mlxsw_sp *mlxsw_sp, |
844 | const char *mac, u16 fid_index, |
845 | const struct in6_addr *addr6) |
846 | { |
847 | struct mlxsw_sp_nve_ipv6_ht_node *ipv6_ht_node; |
848 | struct mlxsw_sp_nve *nve = mlxsw_sp->nve; |
849 | int err; |
850 | |
851 | ipv6_ht_node = kzalloc(size: sizeof(*ipv6_ht_node), GFP_KERNEL); |
852 | if (!ipv6_ht_node) |
853 | return -ENOMEM; |
854 | |
855 | ether_addr_copy(dst: ipv6_ht_node->key.mac, src: mac); |
856 | ipv6_ht_node->key.fid_index = fid_index; |
857 | ipv6_ht_node->addr6 = *addr6; |
858 | |
859 | err = rhashtable_insert_fast(ht: &nve->ipv6_ht, obj: &ipv6_ht_node->ht_node, |
860 | params: mlxsw_sp_nve_ipv6_ht_params); |
861 | if (err) |
862 | goto err_rhashtable_insert; |
863 | |
864 | list_add(new: &ipv6_ht_node->list, head: &nve->ipv6_addr_list); |
865 | |
866 | return 0; |
867 | |
868 | err_rhashtable_insert: |
869 | kfree(objp: ipv6_ht_node); |
870 | return err; |
871 | } |
872 | |
873 | static void |
874 | mlxsw_sp_nve_ipv6_ht_remove(struct mlxsw_sp *mlxsw_sp, |
875 | struct mlxsw_sp_nve_ipv6_ht_node *ipv6_ht_node) |
876 | { |
877 | struct mlxsw_sp_nve *nve = mlxsw_sp->nve; |
878 | |
879 | list_del(entry: &ipv6_ht_node->list); |
880 | rhashtable_remove_fast(ht: &nve->ipv6_ht, obj: &ipv6_ht_node->ht_node, |
881 | params: mlxsw_sp_nve_ipv6_ht_params); |
882 | kfree(objp: ipv6_ht_node); |
883 | } |
884 | |
885 | int |
886 | mlxsw_sp_nve_ipv6_addr_map_replace(struct mlxsw_sp *mlxsw_sp, const char *mac, |
887 | u16 fid_index, |
888 | const struct in6_addr *new_addr6) |
889 | { |
890 | struct mlxsw_sp_nve_ipv6_ht_node *ipv6_ht_node; |
891 | |
892 | ASSERT_RTNL(); |
893 | |
894 | ipv6_ht_node = mlxsw_sp_nve_ipv6_ht_node_lookup(mlxsw_sp, mac, |
895 | fid_index); |
896 | if (!ipv6_ht_node) |
897 | return mlxsw_sp_nve_ipv6_ht_insert(mlxsw_sp, mac, fid_index, |
898 | addr6: new_addr6); |
899 | |
900 | mlxsw_sp_ipv6_addr_put(mlxsw_sp, addr6: &ipv6_ht_node->addr6); |
901 | ipv6_ht_node->addr6 = *new_addr6; |
902 | return 0; |
903 | } |
904 | |
905 | void mlxsw_sp_nve_ipv6_addr_map_del(struct mlxsw_sp *mlxsw_sp, const char *mac, |
906 | u16 fid_index) |
907 | { |
908 | struct mlxsw_sp_nve_ipv6_ht_node *ipv6_ht_node; |
909 | |
910 | ASSERT_RTNL(); |
911 | |
912 | ipv6_ht_node = mlxsw_sp_nve_ipv6_ht_node_lookup(mlxsw_sp, mac, |
913 | fid_index); |
914 | if (WARN_ON(!ipv6_ht_node)) |
915 | return; |
916 | |
917 | mlxsw_sp_nve_ipv6_ht_remove(mlxsw_sp, ipv6_ht_node); |
918 | } |
919 | |
920 | static void mlxsw_sp_nve_ipv6_addr_flush_by_fid(struct mlxsw_sp *mlxsw_sp, |
921 | u16 fid_index) |
922 | { |
923 | struct mlxsw_sp_nve_ipv6_ht_node *ipv6_ht_node, *tmp; |
924 | struct mlxsw_sp_nve *nve = mlxsw_sp->nve; |
925 | |
926 | list_for_each_entry_safe(ipv6_ht_node, tmp, &nve->ipv6_addr_list, |
927 | list) { |
928 | if (ipv6_ht_node->key.fid_index != fid_index) |
929 | continue; |
930 | |
931 | mlxsw_sp_ipv6_addr_put(mlxsw_sp, addr6: &ipv6_ht_node->addr6); |
932 | mlxsw_sp_nve_ipv6_ht_remove(mlxsw_sp, ipv6_ht_node); |
933 | } |
934 | } |
935 | |
936 | int mlxsw_sp_nve_fid_enable(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fid *fid, |
937 | struct mlxsw_sp_nve_params *params, |
938 | struct netlink_ext_ack *extack) |
939 | { |
940 | struct mlxsw_sp_nve *nve = mlxsw_sp->nve; |
941 | const struct mlxsw_sp_nve_ops *ops; |
942 | struct mlxsw_sp_nve_config config; |
943 | int err; |
944 | |
945 | ops = nve->nve_ops_arr[params->type]; |
946 | |
947 | if (!ops->can_offload(nve, params, extack)) |
948 | return -EINVAL; |
949 | |
950 | memset(&config, 0, sizeof(config)); |
951 | ops->nve_config(nve, params, &config); |
952 | if (nve->num_nve_tunnels && |
953 | memcmp(p: &config, q: &nve->config, size: sizeof(config))) { |
954 | NL_SET_ERR_MSG_MOD(extack, "Conflicting NVE tunnels configuration" ); |
955 | return -EINVAL; |
956 | } |
957 | |
958 | err = mlxsw_sp_nve_tunnel_init(mlxsw_sp, config: &config); |
959 | if (err) { |
960 | NL_SET_ERR_MSG_MOD(extack, "Failed to initialize NVE tunnel" ); |
961 | return err; |
962 | } |
963 | |
964 | err = mlxsw_sp_fid_vni_set(fid, type: params->type, vni: params->vni, |
965 | nve_ifindex: params->dev->ifindex); |
966 | if (err) { |
967 | NL_SET_ERR_MSG_MOD(extack, "Failed to set VNI on FID" ); |
968 | goto err_fid_vni_set; |
969 | } |
970 | |
971 | err = ops->fdb_replay(params->dev, params->vni, extack); |
972 | if (err) |
973 | goto err_fdb_replay; |
974 | |
975 | return 0; |
976 | |
977 | err_fdb_replay: |
978 | mlxsw_sp_fid_vni_clear(fid); |
979 | err_fid_vni_set: |
980 | mlxsw_sp_nve_tunnel_fini(mlxsw_sp); |
981 | return err; |
982 | } |
983 | |
984 | void mlxsw_sp_nve_fid_disable(struct mlxsw_sp *mlxsw_sp, |
985 | struct mlxsw_sp_fid *fid) |
986 | { |
987 | u16 fid_index = mlxsw_sp_fid_index(fid); |
988 | struct net_device *nve_dev; |
989 | int nve_ifindex; |
990 | __be32 vni; |
991 | |
992 | /* Necessary for __dev_get_by_index() below. */ |
993 | ASSERT_RTNL(); |
994 | |
995 | mlxsw_sp_nve_flood_ip_flush(mlxsw_sp, fid); |
996 | mlxsw_sp_nve_fdb_flush_by_fid(mlxsw_sp, fid_index); |
997 | mlxsw_sp_nve_ipv6_addr_flush_by_fid(mlxsw_sp, fid_index); |
998 | |
999 | if (WARN_ON(mlxsw_sp_fid_nve_ifindex(fid, &nve_ifindex) || |
1000 | mlxsw_sp_fid_vni(fid, &vni))) |
1001 | goto out; |
1002 | |
1003 | nve_dev = __dev_get_by_index(net: mlxsw_sp_net(mlxsw_sp), ifindex: nve_ifindex); |
1004 | if (!nve_dev) |
1005 | goto out; |
1006 | |
1007 | mlxsw_sp_nve_fdb_clear_offload(mlxsw_sp, fid, nve_dev, vni); |
1008 | mlxsw_sp_fid_fdb_clear_offload(fid, nve_dev); |
1009 | |
1010 | out: |
1011 | mlxsw_sp_fid_vni_clear(fid); |
1012 | mlxsw_sp_nve_tunnel_fini(mlxsw_sp); |
1013 | } |
1014 | |
1015 | int mlxsw_sp_port_nve_init(struct mlxsw_sp_port *mlxsw_sp_port) |
1016 | { |
1017 | struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; |
1018 | char tnqdr_pl[MLXSW_REG_TNQDR_LEN]; |
1019 | |
1020 | mlxsw_reg_tnqdr_pack(payload: tnqdr_pl, local_port: mlxsw_sp_port->local_port); |
1021 | return mlxsw_reg_write(mlxsw_core: mlxsw_sp->core, MLXSW_REG(tnqdr), payload: tnqdr_pl); |
1022 | } |
1023 | |
1024 | void mlxsw_sp_port_nve_fini(struct mlxsw_sp_port *mlxsw_sp_port) |
1025 | { |
1026 | } |
1027 | |
1028 | static int mlxsw_sp_nve_qos_init(struct mlxsw_sp *mlxsw_sp) |
1029 | { |
1030 | char tnqcr_pl[MLXSW_REG_TNQCR_LEN]; |
1031 | |
1032 | mlxsw_reg_tnqcr_pack(payload: tnqcr_pl); |
1033 | return mlxsw_reg_write(mlxsw_core: mlxsw_sp->core, MLXSW_REG(tnqcr), payload: tnqcr_pl); |
1034 | } |
1035 | |
1036 | static int mlxsw_sp_nve_ecn_encap_init(struct mlxsw_sp *mlxsw_sp) |
1037 | { |
1038 | int i; |
1039 | |
1040 | /* Iterate over inner ECN values */ |
1041 | for (i = INET_ECN_NOT_ECT; i <= INET_ECN_CE; i++) { |
1042 | u8 outer_ecn = INET_ECN_encapsulate(outer: 0, inner: i); |
1043 | char tneem_pl[MLXSW_REG_TNEEM_LEN]; |
1044 | int err; |
1045 | |
1046 | mlxsw_reg_tneem_pack(payload: tneem_pl, overlay_ecn: i, underlay_ecn: outer_ecn); |
1047 | err = mlxsw_reg_write(mlxsw_core: mlxsw_sp->core, MLXSW_REG(tneem), |
1048 | payload: tneem_pl); |
1049 | if (err) |
1050 | return err; |
1051 | } |
1052 | |
1053 | return 0; |
1054 | } |
1055 | |
1056 | static int __mlxsw_sp_nve_ecn_decap_init(struct mlxsw_sp *mlxsw_sp, |
1057 | u8 inner_ecn, u8 outer_ecn) |
1058 | { |
1059 | char tndem_pl[MLXSW_REG_TNDEM_LEN]; |
1060 | u8 new_inner_ecn; |
1061 | bool trap_en; |
1062 | |
1063 | new_inner_ecn = mlxsw_sp_tunnel_ecn_decap(outer_ecn, inner_ecn, |
1064 | trap_en: &trap_en); |
1065 | mlxsw_reg_tndem_pack(payload: tndem_pl, underlay_ecn: outer_ecn, overlay_ecn: inner_ecn, ecn: new_inner_ecn, |
1066 | trap_en, trap_id: trap_en ? MLXSW_TRAP_ID_DECAP_ECN0 : 0); |
1067 | return mlxsw_reg_write(mlxsw_core: mlxsw_sp->core, MLXSW_REG(tndem), payload: tndem_pl); |
1068 | } |
1069 | |
1070 | static int mlxsw_sp_nve_ecn_decap_init(struct mlxsw_sp *mlxsw_sp) |
1071 | { |
1072 | int i; |
1073 | |
1074 | /* Iterate over inner ECN values */ |
1075 | for (i = INET_ECN_NOT_ECT; i <= INET_ECN_CE; i++) { |
1076 | int j; |
1077 | |
1078 | /* Iterate over outer ECN values */ |
1079 | for (j = INET_ECN_NOT_ECT; j <= INET_ECN_CE; j++) { |
1080 | int err; |
1081 | |
1082 | err = __mlxsw_sp_nve_ecn_decap_init(mlxsw_sp, inner_ecn: i, outer_ecn: j); |
1083 | if (err) |
1084 | return err; |
1085 | } |
1086 | } |
1087 | |
1088 | return 0; |
1089 | } |
1090 | |
1091 | static int mlxsw_sp_nve_ecn_init(struct mlxsw_sp *mlxsw_sp) |
1092 | { |
1093 | int err; |
1094 | |
1095 | err = mlxsw_sp_nve_ecn_encap_init(mlxsw_sp); |
1096 | if (err) |
1097 | return err; |
1098 | |
1099 | return mlxsw_sp_nve_ecn_decap_init(mlxsw_sp); |
1100 | } |
1101 | |
1102 | static int mlxsw_sp_nve_resources_query(struct mlxsw_sp *mlxsw_sp) |
1103 | { |
1104 | unsigned int max; |
1105 | |
1106 | if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_NVE_MC_ENTRIES_IPV4) || |
1107 | !MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_NVE_MC_ENTRIES_IPV6)) |
1108 | return -EIO; |
1109 | max = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_NVE_MC_ENTRIES_IPV4); |
1110 | mlxsw_sp->nve->num_max_mc_entries[MLXSW_SP_L3_PROTO_IPV4] = max; |
1111 | max = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_NVE_MC_ENTRIES_IPV6); |
1112 | mlxsw_sp->nve->num_max_mc_entries[MLXSW_SP_L3_PROTO_IPV6] = max; |
1113 | |
1114 | return 0; |
1115 | } |
1116 | |
1117 | int mlxsw_sp_nve_init(struct mlxsw_sp *mlxsw_sp) |
1118 | { |
1119 | struct mlxsw_sp_nve *nve; |
1120 | int err; |
1121 | |
1122 | nve = kzalloc(size: sizeof(*mlxsw_sp->nve), GFP_KERNEL); |
1123 | if (!nve) |
1124 | return -ENOMEM; |
1125 | mlxsw_sp->nve = nve; |
1126 | nve->mlxsw_sp = mlxsw_sp; |
1127 | nve->nve_ops_arr = mlxsw_sp->nve_ops_arr; |
1128 | |
1129 | err = rhashtable_init(ht: &nve->mc_list_ht, |
1130 | params: &mlxsw_sp_nve_mc_list_ht_params); |
1131 | if (err) |
1132 | goto err_mc_rhashtable_init; |
1133 | |
1134 | err = rhashtable_init(ht: &nve->ipv6_ht, params: &mlxsw_sp_nve_ipv6_ht_params); |
1135 | if (err) |
1136 | goto err_ipv6_rhashtable_init; |
1137 | |
1138 | INIT_LIST_HEAD(list: &nve->ipv6_addr_list); |
1139 | |
1140 | err = mlxsw_sp_nve_qos_init(mlxsw_sp); |
1141 | if (err) |
1142 | goto err_nve_qos_init; |
1143 | |
1144 | err = mlxsw_sp_nve_ecn_init(mlxsw_sp); |
1145 | if (err) |
1146 | goto err_nve_ecn_init; |
1147 | |
1148 | err = mlxsw_sp_nve_resources_query(mlxsw_sp); |
1149 | if (err) |
1150 | goto err_nve_resources_query; |
1151 | |
1152 | return 0; |
1153 | |
1154 | err_nve_resources_query: |
1155 | err_nve_ecn_init: |
1156 | err_nve_qos_init: |
1157 | rhashtable_destroy(ht: &nve->ipv6_ht); |
1158 | err_ipv6_rhashtable_init: |
1159 | rhashtable_destroy(ht: &nve->mc_list_ht); |
1160 | err_mc_rhashtable_init: |
1161 | mlxsw_sp->nve = NULL; |
1162 | kfree(objp: nve); |
1163 | return err; |
1164 | } |
1165 | |
1166 | void mlxsw_sp_nve_fini(struct mlxsw_sp *mlxsw_sp) |
1167 | { |
1168 | WARN_ON(mlxsw_sp->nve->num_nve_tunnels); |
1169 | WARN_ON(!list_empty(&mlxsw_sp->nve->ipv6_addr_list)); |
1170 | rhashtable_destroy(ht: &mlxsw_sp->nve->ipv6_ht); |
1171 | rhashtable_destroy(ht: &mlxsw_sp->nve->mc_list_ht); |
1172 | kfree(objp: mlxsw_sp->nve); |
1173 | mlxsw_sp->nve = NULL; |
1174 | } |
1175 | |