1 | // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 |
2 | /* Copyright (c) 2019-2021 Marvell International Ltd. All rights reserved */ |
3 | |
4 | #include <linux/rhashtable.h> |
5 | |
6 | #include "prestera.h" |
7 | #include "prestera_hw.h" |
8 | #include "prestera_router_hw.h" |
9 | #include "prestera_acl.h" |
10 | |
11 | /* Nexthop is pointed |
12 | * to port (not rif) |
13 | * +-------+ |
14 | * +>|nexthop| |
15 | * | +-------+ |
16 | * | |
17 | * +--+ +-----++ |
18 | * +------->|vr|<-+ +>|nh_grp| |
19 | * | +--+ | | +------+ |
20 | * | | | |
21 | * +-+-------+ +--+---+-+ |
22 | * |rif_entry| |fib_node| |
23 | * +---------+ +--------+ |
24 | * Rif is Fib - is exit point |
25 | * used as |
26 | * entry point |
27 | * for vr in hw |
28 | */ |
29 | |
30 | #define PRESTERA_NHGR_UNUSED (0) |
31 | #define PRESTERA_NHGR_DROP (0xFFFFFFFF) |
32 | /* Need to merge it with router_manager */ |
33 | #define PRESTERA_NH_ACTIVE_JIFFER_FILTER 3000 /* ms */ |
34 | |
35 | static const struct rhashtable_params __prestera_fib_ht_params = { |
36 | .key_offset = offsetof(struct prestera_fib_node, key), |
37 | .head_offset = offsetof(struct prestera_fib_node, ht_node), |
38 | .key_len = sizeof(struct prestera_fib_key), |
39 | .automatic_shrinking = true, |
40 | }; |
41 | |
42 | static const struct rhashtable_params __prestera_nh_neigh_ht_params = { |
43 | .key_offset = offsetof(struct prestera_nh_neigh, key), |
44 | .key_len = sizeof(struct prestera_nh_neigh_key), |
45 | .head_offset = offsetof(struct prestera_nh_neigh, ht_node), |
46 | }; |
47 | |
48 | static const struct rhashtable_params __prestera_nexthop_group_ht_params = { |
49 | .key_offset = offsetof(struct prestera_nexthop_group, key), |
50 | .key_len = sizeof(struct prestera_nexthop_group_key), |
51 | .head_offset = offsetof(struct prestera_nexthop_group, ht_node), |
52 | }; |
53 | |
54 | static int prestera_nexthop_group_set(struct prestera_switch *sw, |
55 | struct prestera_nexthop_group *nh_grp); |
56 | static bool |
57 | prestera_nexthop_group_util_hw_state(struct prestera_switch *sw, |
58 | struct prestera_nexthop_group *nh_grp); |
59 | static void prestera_fib_node_destroy_ht_cb(void *ptr, void *arg); |
60 | |
61 | /* TODO: move to router.h as macros */ |
62 | static bool prestera_nh_neigh_key_is_valid(struct prestera_nh_neigh_key *key) |
63 | { |
64 | return memchr_inv(p: key, c: 0, size: sizeof(*key)) ? true : false; |
65 | } |
66 | |
67 | int prestera_router_hw_init(struct prestera_switch *sw) |
68 | { |
69 | int err; |
70 | |
71 | err = rhashtable_init(ht: &sw->router->nh_neigh_ht, |
72 | params: &__prestera_nh_neigh_ht_params); |
73 | if (err) |
74 | goto err_nh_neigh_ht_init; |
75 | |
76 | err = rhashtable_init(ht: &sw->router->nexthop_group_ht, |
77 | params: &__prestera_nexthop_group_ht_params); |
78 | if (err) |
79 | goto err_nexthop_grp_ht_init; |
80 | |
81 | err = rhashtable_init(ht: &sw->router->fib_ht, |
82 | params: &__prestera_fib_ht_params); |
83 | if (err) |
84 | goto err_fib_ht_init; |
85 | |
86 | INIT_LIST_HEAD(list: &sw->router->vr_list); |
87 | INIT_LIST_HEAD(list: &sw->router->rif_entry_list); |
88 | |
89 | return 0; |
90 | |
91 | err_fib_ht_init: |
92 | rhashtable_destroy(ht: &sw->router->nexthop_group_ht); |
93 | err_nexthop_grp_ht_init: |
94 | rhashtable_destroy(ht: &sw->router->nh_neigh_ht); |
95 | err_nh_neigh_ht_init: |
96 | return 0; |
97 | } |
98 | |
99 | void prestera_router_hw_fini(struct prestera_switch *sw) |
100 | { |
101 | rhashtable_free_and_destroy(ht: &sw->router->fib_ht, |
102 | free_fn: prestera_fib_node_destroy_ht_cb, arg: sw); |
103 | WARN_ON(!list_empty(&sw->router->vr_list)); |
104 | WARN_ON(!list_empty(&sw->router->rif_entry_list)); |
105 | rhashtable_destroy(ht: &sw->router->fib_ht); |
106 | rhashtable_destroy(ht: &sw->router->nexthop_group_ht); |
107 | rhashtable_destroy(ht: &sw->router->nh_neigh_ht); |
108 | } |
109 | |
110 | static struct prestera_vr *__prestera_vr_find(struct prestera_switch *sw, |
111 | u32 tb_id) |
112 | { |
113 | struct prestera_vr *vr; |
114 | |
115 | list_for_each_entry(vr, &sw->router->vr_list, router_node) { |
116 | if (vr->tb_id == tb_id) |
117 | return vr; |
118 | } |
119 | |
120 | return NULL; |
121 | } |
122 | |
123 | static struct prestera_vr *__prestera_vr_create(struct prestera_switch *sw, |
124 | u32 tb_id, |
125 | struct netlink_ext_ack *extack) |
126 | { |
127 | struct prestera_vr *vr; |
128 | int err; |
129 | |
130 | vr = kzalloc(size: sizeof(*vr), GFP_KERNEL); |
131 | if (!vr) { |
132 | err = -ENOMEM; |
133 | goto err_alloc_vr; |
134 | } |
135 | |
136 | vr->tb_id = tb_id; |
137 | |
138 | err = prestera_hw_vr_create(sw, vr_id: &vr->hw_vr_id); |
139 | if (err) |
140 | goto err_hw_create; |
141 | |
142 | list_add(new: &vr->router_node, head: &sw->router->vr_list); |
143 | |
144 | return vr; |
145 | |
146 | err_hw_create: |
147 | kfree(objp: vr); |
148 | err_alloc_vr: |
149 | return ERR_PTR(error: err); |
150 | } |
151 | |
152 | static void __prestera_vr_destroy(struct prestera_switch *sw, |
153 | struct prestera_vr *vr) |
154 | { |
155 | list_del(entry: &vr->router_node); |
156 | prestera_hw_vr_delete(sw, vr_id: vr->hw_vr_id); |
157 | kfree(objp: vr); |
158 | } |
159 | |
160 | static struct prestera_vr *prestera_vr_get(struct prestera_switch *sw, u32 tb_id, |
161 | struct netlink_ext_ack *extack) |
162 | { |
163 | struct prestera_vr *vr; |
164 | |
165 | vr = __prestera_vr_find(sw, tb_id); |
166 | if (vr) { |
167 | refcount_inc(r: &vr->refcount); |
168 | } else { |
169 | vr = __prestera_vr_create(sw, tb_id, extack); |
170 | if (IS_ERR(ptr: vr)) |
171 | return ERR_CAST(ptr: vr); |
172 | |
173 | refcount_set(r: &vr->refcount, n: 1); |
174 | } |
175 | |
176 | return vr; |
177 | } |
178 | |
179 | static void prestera_vr_put(struct prestera_switch *sw, struct prestera_vr *vr) |
180 | { |
181 | if (refcount_dec_and_test(r: &vr->refcount)) |
182 | __prestera_vr_destroy(sw, vr); |
183 | } |
184 | |
185 | /* iface is overhead struct. vr_id also can be removed. */ |
186 | static int |
187 | __prestera_rif_entry_key_copy(const struct prestera_rif_entry_key *in, |
188 | struct prestera_rif_entry_key *out) |
189 | { |
190 | memset(out, 0, sizeof(*out)); |
191 | |
192 | switch (in->iface.type) { |
193 | case PRESTERA_IF_PORT_E: |
194 | out->iface.dev_port.hw_dev_num = in->iface.dev_port.hw_dev_num; |
195 | out->iface.dev_port.port_num = in->iface.dev_port.port_num; |
196 | break; |
197 | case PRESTERA_IF_LAG_E: |
198 | out->iface.lag_id = in->iface.lag_id; |
199 | break; |
200 | case PRESTERA_IF_VID_E: |
201 | out->iface.vlan_id = in->iface.vlan_id; |
202 | break; |
203 | default: |
204 | WARN(1, "Unsupported iface type" ); |
205 | return -EINVAL; |
206 | } |
207 | |
208 | out->iface.type = in->iface.type; |
209 | return 0; |
210 | } |
211 | |
212 | struct prestera_rif_entry * |
213 | prestera_rif_entry_find(const struct prestera_switch *sw, |
214 | const struct prestera_rif_entry_key *k) |
215 | { |
216 | struct prestera_rif_entry *rif_entry; |
217 | struct prestera_rif_entry_key lk; /* lookup key */ |
218 | |
219 | if (__prestera_rif_entry_key_copy(in: k, out: &lk)) |
220 | return NULL; |
221 | |
222 | list_for_each_entry(rif_entry, &sw->router->rif_entry_list, |
223 | router_node) { |
224 | if (!memcmp(p: k, q: &rif_entry->key, size: sizeof(*k))) |
225 | return rif_entry; |
226 | } |
227 | |
228 | return NULL; |
229 | } |
230 | |
231 | void prestera_rif_entry_destroy(struct prestera_switch *sw, |
232 | struct prestera_rif_entry *e) |
233 | { |
234 | struct prestera_iface iface; |
235 | |
236 | list_del(entry: &e->router_node); |
237 | |
238 | memcpy(&iface, &e->key.iface, sizeof(iface)); |
239 | iface.vr_id = e->vr->hw_vr_id; |
240 | prestera_hw_rif_delete(sw, rif_id: e->hw_id, iif: &iface); |
241 | |
242 | prestera_vr_put(sw, vr: e->vr); |
243 | kfree(objp: e); |
244 | } |
245 | |
246 | struct prestera_rif_entry * |
247 | prestera_rif_entry_create(struct prestera_switch *sw, |
248 | struct prestera_rif_entry_key *k, |
249 | u32 tb_id, const unsigned char *addr) |
250 | { |
251 | int err; |
252 | struct prestera_rif_entry *e; |
253 | struct prestera_iface iface; |
254 | |
255 | e = kzalloc(size: sizeof(*e), GFP_KERNEL); |
256 | if (!e) |
257 | goto err_kzalloc; |
258 | |
259 | if (__prestera_rif_entry_key_copy(in: k, out: &e->key)) |
260 | goto err_key_copy; |
261 | |
262 | e->vr = prestera_vr_get(sw, tb_id, NULL); |
263 | if (IS_ERR(ptr: e->vr)) |
264 | goto err_vr_get; |
265 | |
266 | memcpy(&e->addr, addr, sizeof(e->addr)); |
267 | |
268 | /* HW */ |
269 | memcpy(&iface, &e->key.iface, sizeof(iface)); |
270 | iface.vr_id = e->vr->hw_vr_id; |
271 | err = prestera_hw_rif_create(sw, iif: &iface, mac: e->addr, rif_id: &e->hw_id); |
272 | if (err) |
273 | goto err_hw_create; |
274 | |
275 | list_add(new: &e->router_node, head: &sw->router->rif_entry_list); |
276 | |
277 | return e; |
278 | |
279 | err_hw_create: |
280 | prestera_vr_put(sw, vr: e->vr); |
281 | err_vr_get: |
282 | err_key_copy: |
283 | kfree(objp: e); |
284 | err_kzalloc: |
285 | return NULL; |
286 | } |
287 | |
288 | static void __prestera_nh_neigh_destroy(struct prestera_switch *sw, |
289 | struct prestera_nh_neigh *neigh) |
290 | { |
291 | rhashtable_remove_fast(ht: &sw->router->nh_neigh_ht, |
292 | obj: &neigh->ht_node, |
293 | params: __prestera_nh_neigh_ht_params); |
294 | kfree(objp: neigh); |
295 | } |
296 | |
297 | static struct prestera_nh_neigh * |
298 | __prestera_nh_neigh_create(struct prestera_switch *sw, |
299 | struct prestera_nh_neigh_key *key) |
300 | { |
301 | struct prestera_nh_neigh *neigh; |
302 | int err; |
303 | |
304 | neigh = kzalloc(size: sizeof(*neigh), GFP_KERNEL); |
305 | if (!neigh) |
306 | goto err_kzalloc; |
307 | |
308 | memcpy(&neigh->key, key, sizeof(*key)); |
309 | neigh->info.connected = false; |
310 | INIT_LIST_HEAD(list: &neigh->nexthop_group_list); |
311 | err = rhashtable_insert_fast(ht: &sw->router->nh_neigh_ht, |
312 | obj: &neigh->ht_node, |
313 | params: __prestera_nh_neigh_ht_params); |
314 | if (err) |
315 | goto err_rhashtable_insert; |
316 | |
317 | return neigh; |
318 | |
319 | err_rhashtable_insert: |
320 | kfree(objp: neigh); |
321 | err_kzalloc: |
322 | return NULL; |
323 | } |
324 | |
325 | struct prestera_nh_neigh * |
326 | prestera_nh_neigh_find(struct prestera_switch *sw, |
327 | struct prestera_nh_neigh_key *key) |
328 | { |
329 | struct prestera_nh_neigh *nh_neigh; |
330 | |
331 | nh_neigh = rhashtable_lookup_fast(ht: &sw->router->nh_neigh_ht, |
332 | key, params: __prestera_nh_neigh_ht_params); |
333 | return nh_neigh; |
334 | } |
335 | |
336 | struct prestera_nh_neigh * |
337 | prestera_nh_neigh_get(struct prestera_switch *sw, |
338 | struct prestera_nh_neigh_key *key) |
339 | { |
340 | struct prestera_nh_neigh *neigh; |
341 | |
342 | neigh = prestera_nh_neigh_find(sw, key); |
343 | if (!neigh) |
344 | return __prestera_nh_neigh_create(sw, key); |
345 | |
346 | return neigh; |
347 | } |
348 | |
349 | void prestera_nh_neigh_put(struct prestera_switch *sw, |
350 | struct prestera_nh_neigh *neigh) |
351 | { |
352 | if (list_empty(head: &neigh->nexthop_group_list)) |
353 | __prestera_nh_neigh_destroy(sw, neigh); |
354 | } |
355 | |
356 | /* Updates new prestera_neigh_info */ |
357 | int prestera_nh_neigh_set(struct prestera_switch *sw, |
358 | struct prestera_nh_neigh *neigh) |
359 | { |
360 | struct prestera_nh_neigh_head *nh_head; |
361 | struct prestera_nexthop_group *nh_grp; |
362 | int err; |
363 | |
364 | list_for_each_entry(nh_head, &neigh->nexthop_group_list, head) { |
365 | nh_grp = nh_head->this; |
366 | err = prestera_nexthop_group_set(sw, nh_grp); |
367 | if (err) |
368 | return err; |
369 | } |
370 | |
371 | return 0; |
372 | } |
373 | |
374 | bool prestera_nh_neigh_util_hw_state(struct prestera_switch *sw, |
375 | struct prestera_nh_neigh *nh_neigh) |
376 | { |
377 | bool state; |
378 | struct prestera_nh_neigh_head *nh_head, *tmp; |
379 | |
380 | state = false; |
381 | list_for_each_entry_safe(nh_head, tmp, |
382 | &nh_neigh->nexthop_group_list, head) { |
383 | state = prestera_nexthop_group_util_hw_state(sw, nh_grp: nh_head->this); |
384 | if (state) |
385 | goto out; |
386 | } |
387 | |
388 | out: |
389 | return state; |
390 | } |
391 | |
392 | static struct prestera_nexthop_group * |
393 | __prestera_nexthop_group_create(struct prestera_switch *sw, |
394 | struct prestera_nexthop_group_key *key) |
395 | { |
396 | struct prestera_nexthop_group *nh_grp; |
397 | struct prestera_nh_neigh *nh_neigh; |
398 | int nh_cnt, err, gid; |
399 | |
400 | nh_grp = kzalloc(size: sizeof(*nh_grp), GFP_KERNEL); |
401 | if (!nh_grp) |
402 | goto err_kzalloc; |
403 | |
404 | memcpy(&nh_grp->key, key, sizeof(*key)); |
405 | for (nh_cnt = 0; nh_cnt < PRESTERA_NHGR_SIZE_MAX; nh_cnt++) { |
406 | if (!prestera_nh_neigh_key_is_valid(key: &nh_grp->key.neigh[nh_cnt])) |
407 | break; |
408 | |
409 | nh_neigh = prestera_nh_neigh_get(sw, |
410 | key: &nh_grp->key.neigh[nh_cnt]); |
411 | if (!nh_neigh) |
412 | goto err_nh_neigh_get; |
413 | |
414 | nh_grp->nh_neigh_head[nh_cnt].neigh = nh_neigh; |
415 | nh_grp->nh_neigh_head[nh_cnt].this = nh_grp; |
416 | list_add(new: &nh_grp->nh_neigh_head[nh_cnt].head, |
417 | head: &nh_neigh->nexthop_group_list); |
418 | } |
419 | |
420 | err = prestera_hw_nh_group_create(sw, nh_count: nh_cnt, grp_id: &nh_grp->grp_id); |
421 | if (err) |
422 | goto err_nh_group_create; |
423 | |
424 | err = prestera_nexthop_group_set(sw, nh_grp); |
425 | if (err) |
426 | goto err_nexthop_group_set; |
427 | |
428 | err = rhashtable_insert_fast(ht: &sw->router->nexthop_group_ht, |
429 | obj: &nh_grp->ht_node, |
430 | params: __prestera_nexthop_group_ht_params); |
431 | if (err) |
432 | goto err_ht_insert; |
433 | |
434 | /* reset cache for created group */ |
435 | gid = nh_grp->grp_id; |
436 | sw->router->nhgrp_hw_state_cache[gid / 8] &= ~BIT(gid % 8); |
437 | |
438 | return nh_grp; |
439 | |
440 | err_ht_insert: |
441 | err_nexthop_group_set: |
442 | prestera_hw_nh_group_delete(sw, nh_count: nh_cnt, grp_id: nh_grp->grp_id); |
443 | err_nh_group_create: |
444 | err_nh_neigh_get: |
445 | for (nh_cnt--; nh_cnt >= 0; nh_cnt--) { |
446 | list_del(entry: &nh_grp->nh_neigh_head[nh_cnt].head); |
447 | prestera_nh_neigh_put(sw, neigh: nh_grp->nh_neigh_head[nh_cnt].neigh); |
448 | } |
449 | |
450 | kfree(objp: nh_grp); |
451 | err_kzalloc: |
452 | return NULL; |
453 | } |
454 | |
455 | static void |
456 | __prestera_nexthop_group_destroy(struct prestera_switch *sw, |
457 | struct prestera_nexthop_group *nh_grp) |
458 | { |
459 | struct prestera_nh_neigh *nh_neigh; |
460 | int nh_cnt; |
461 | |
462 | rhashtable_remove_fast(ht: &sw->router->nexthop_group_ht, |
463 | obj: &nh_grp->ht_node, |
464 | params: __prestera_nexthop_group_ht_params); |
465 | |
466 | for (nh_cnt = 0; nh_cnt < PRESTERA_NHGR_SIZE_MAX; nh_cnt++) { |
467 | nh_neigh = nh_grp->nh_neigh_head[nh_cnt].neigh; |
468 | if (!nh_neigh) |
469 | break; |
470 | |
471 | list_del(entry: &nh_grp->nh_neigh_head[nh_cnt].head); |
472 | prestera_nh_neigh_put(sw, neigh: nh_neigh); |
473 | } |
474 | |
475 | prestera_hw_nh_group_delete(sw, nh_count: nh_cnt, grp_id: nh_grp->grp_id); |
476 | kfree(objp: nh_grp); |
477 | } |
478 | |
479 | static struct prestera_nexthop_group * |
480 | __prestera_nexthop_group_find(struct prestera_switch *sw, |
481 | struct prestera_nexthop_group_key *key) |
482 | { |
483 | struct prestera_nexthop_group *nh_grp; |
484 | |
485 | nh_grp = rhashtable_lookup_fast(ht: &sw->router->nexthop_group_ht, |
486 | key, params: __prestera_nexthop_group_ht_params); |
487 | return nh_grp; |
488 | } |
489 | |
490 | static struct prestera_nexthop_group * |
491 | prestera_nexthop_group_get(struct prestera_switch *sw, |
492 | struct prestera_nexthop_group_key *key) |
493 | { |
494 | struct prestera_nexthop_group *nh_grp; |
495 | |
496 | nh_grp = __prestera_nexthop_group_find(sw, key); |
497 | if (nh_grp) { |
498 | refcount_inc(r: &nh_grp->refcount); |
499 | } else { |
500 | nh_grp = __prestera_nexthop_group_create(sw, key); |
501 | if (!nh_grp) |
502 | return ERR_PTR(error: -ENOMEM); |
503 | |
504 | refcount_set(r: &nh_grp->refcount, n: 1); |
505 | } |
506 | |
507 | return nh_grp; |
508 | } |
509 | |
510 | static void prestera_nexthop_group_put(struct prestera_switch *sw, |
511 | struct prestera_nexthop_group *nh_grp) |
512 | { |
513 | if (refcount_dec_and_test(r: &nh_grp->refcount)) |
514 | __prestera_nexthop_group_destroy(sw, nh_grp); |
515 | } |
516 | |
517 | /* Updates with new nh_neigh's info */ |
518 | static int prestera_nexthop_group_set(struct prestera_switch *sw, |
519 | struct prestera_nexthop_group *nh_grp) |
520 | { |
521 | struct prestera_neigh_info info[PRESTERA_NHGR_SIZE_MAX]; |
522 | struct prestera_nh_neigh *neigh; |
523 | int nh_cnt; |
524 | |
525 | memset(&info[0], 0, sizeof(info)); |
526 | for (nh_cnt = 0; nh_cnt < PRESTERA_NHGR_SIZE_MAX; nh_cnt++) { |
527 | neigh = nh_grp->nh_neigh_head[nh_cnt].neigh; |
528 | if (!neigh) |
529 | break; |
530 | |
531 | memcpy(&info[nh_cnt], &neigh->info, sizeof(neigh->info)); |
532 | } |
533 | |
534 | return prestera_hw_nh_entries_set(sw, count: nh_cnt, nhs: &info[0], grp_id: nh_grp->grp_id); |
535 | } |
536 | |
537 | static bool |
538 | prestera_nexthop_group_util_hw_state(struct prestera_switch *sw, |
539 | struct prestera_nexthop_group *nh_grp) |
540 | { |
541 | int err; |
542 | u32 buf_size = sw->size_tbl_router_nexthop / 8 + 1; |
543 | u32 gid = nh_grp->grp_id; |
544 | u8 *cache = sw->router->nhgrp_hw_state_cache; |
545 | |
546 | /* Antijitter |
547 | * Prevent situation, when we read state of nh_grp twice in short time, |
548 | * and state bit is still cleared on second call. So just stuck active |
549 | * state for PRESTERA_NH_ACTIVE_JIFFER_FILTER, after last occurred. |
550 | */ |
551 | if (!time_before(jiffies, sw->router->nhgrp_hw_cache_kick + |
552 | msecs_to_jiffies(PRESTERA_NH_ACTIVE_JIFFER_FILTER))) { |
553 | err = prestera_hw_nhgrp_blk_get(sw, hw_state: cache, buf_size); |
554 | if (err) { |
555 | pr_err("Failed to get hw state nh_grp's" ); |
556 | return false; |
557 | } |
558 | |
559 | sw->router->nhgrp_hw_cache_kick = jiffies; |
560 | } |
561 | |
562 | if (cache[gid / 8] & BIT(gid % 8)) |
563 | return true; |
564 | |
565 | return false; |
566 | } |
567 | |
568 | struct prestera_fib_node * |
569 | prestera_fib_node_find(struct prestera_switch *sw, struct prestera_fib_key *key) |
570 | { |
571 | struct prestera_fib_node *fib_node; |
572 | |
573 | fib_node = rhashtable_lookup_fast(ht: &sw->router->fib_ht, key, |
574 | params: __prestera_fib_ht_params); |
575 | return fib_node; |
576 | } |
577 | |
578 | static void __prestera_fib_node_destruct(struct prestera_switch *sw, |
579 | struct prestera_fib_node *fib_node) |
580 | { |
581 | struct prestera_vr *vr; |
582 | |
583 | vr = fib_node->info.vr; |
584 | prestera_hw_lpm_del(sw, vr_id: vr->hw_vr_id, dst: fib_node->key.addr.u.ipv4, |
585 | dst_len: fib_node->key.prefix_len); |
586 | switch (fib_node->info.type) { |
587 | case PRESTERA_FIB_TYPE_UC_NH: |
588 | prestera_nexthop_group_put(sw, nh_grp: fib_node->info.nh_grp); |
589 | break; |
590 | case PRESTERA_FIB_TYPE_TRAP: |
591 | break; |
592 | case PRESTERA_FIB_TYPE_DROP: |
593 | break; |
594 | default: |
595 | pr_err("Unknown fib_node->info.type = %d" , |
596 | fib_node->info.type); |
597 | } |
598 | |
599 | prestera_vr_put(sw, vr); |
600 | } |
601 | |
602 | void prestera_fib_node_destroy(struct prestera_switch *sw, |
603 | struct prestera_fib_node *fib_node) |
604 | { |
605 | __prestera_fib_node_destruct(sw, fib_node); |
606 | rhashtable_remove_fast(ht: &sw->router->fib_ht, obj: &fib_node->ht_node, |
607 | params: __prestera_fib_ht_params); |
608 | kfree(objp: fib_node); |
609 | } |
610 | |
611 | static void prestera_fib_node_destroy_ht_cb(void *ptr, void *arg) |
612 | { |
613 | struct prestera_fib_node *node = ptr; |
614 | struct prestera_switch *sw = arg; |
615 | |
616 | __prestera_fib_node_destruct(sw, fib_node: node); |
617 | kfree(objp: node); |
618 | } |
619 | |
620 | struct prestera_fib_node * |
621 | prestera_fib_node_create(struct prestera_switch *sw, |
622 | struct prestera_fib_key *key, |
623 | enum prestera_fib_type fib_type, |
624 | struct prestera_nexthop_group_key *nh_grp_key) |
625 | { |
626 | struct prestera_fib_node *fib_node; |
627 | u32 grp_id; |
628 | struct prestera_vr *vr; |
629 | int err; |
630 | |
631 | fib_node = kzalloc(size: sizeof(*fib_node), GFP_KERNEL); |
632 | if (!fib_node) |
633 | goto err_kzalloc; |
634 | |
635 | memcpy(&fib_node->key, key, sizeof(*key)); |
636 | fib_node->info.type = fib_type; |
637 | |
638 | vr = prestera_vr_get(sw, tb_id: key->tb_id, NULL); |
639 | if (IS_ERR(ptr: vr)) |
640 | goto err_vr_get; |
641 | |
642 | fib_node->info.vr = vr; |
643 | |
644 | switch (fib_type) { |
645 | case PRESTERA_FIB_TYPE_TRAP: |
646 | grp_id = PRESTERA_NHGR_UNUSED; |
647 | break; |
648 | case PRESTERA_FIB_TYPE_DROP: |
649 | grp_id = PRESTERA_NHGR_DROP; |
650 | break; |
651 | case PRESTERA_FIB_TYPE_UC_NH: |
652 | fib_node->info.nh_grp = prestera_nexthop_group_get(sw, |
653 | key: nh_grp_key); |
654 | if (IS_ERR(ptr: fib_node->info.nh_grp)) |
655 | goto err_nh_grp_get; |
656 | |
657 | grp_id = fib_node->info.nh_grp->grp_id; |
658 | break; |
659 | default: |
660 | pr_err("Unsupported fib_type %d" , fib_type); |
661 | goto err_nh_grp_get; |
662 | } |
663 | |
664 | err = prestera_hw_lpm_add(sw, vr_id: vr->hw_vr_id, dst: key->addr.u.ipv4, |
665 | dst_len: key->prefix_len, grp_id); |
666 | if (err) |
667 | goto err_lpm_add; |
668 | |
669 | err = rhashtable_insert_fast(ht: &sw->router->fib_ht, obj: &fib_node->ht_node, |
670 | params: __prestera_fib_ht_params); |
671 | if (err) |
672 | goto err_ht_insert; |
673 | |
674 | return fib_node; |
675 | |
676 | err_ht_insert: |
677 | prestera_hw_lpm_del(sw, vr_id: vr->hw_vr_id, dst: key->addr.u.ipv4, |
678 | dst_len: key->prefix_len); |
679 | err_lpm_add: |
680 | if (fib_type == PRESTERA_FIB_TYPE_UC_NH) |
681 | prestera_nexthop_group_put(sw, nh_grp: fib_node->info.nh_grp); |
682 | err_nh_grp_get: |
683 | prestera_vr_put(sw, vr); |
684 | err_vr_get: |
685 | kfree(objp: fib_node); |
686 | err_kzalloc: |
687 | return NULL; |
688 | } |
689 | |