Commit | Line | Data |
---|---|---|
37923ed6 DA |
1 | /* |
2 | * Copyright (c) 2018 Cumulus Networks. All rights reserved. | |
3 | * Copyright (c) 2018 David Ahern <dsa@cumulusnetworks.com> | |
4 | * | |
5 | * This software is licensed under the GNU General License Version 2, | |
6 | * June 1991 as shown in the file COPYING in the top-level directory of this | |
7 | * source tree. | |
8 | * | |
9 | * THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" | |
10 | * WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, | |
11 | * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS | |
12 | * FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE | |
13 | * OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME | |
14 | * THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. | |
15 | */ | |
16 | ||
48bb9eb4 IS |
17 | #include <linux/in6.h> |
18 | #include <linux/kernel.h> | |
19 | #include <linux/list.h> | |
20 | #include <linux/rhashtable.h> | |
21 | #include <linux/spinlock_types.h> | |
22 | #include <linux/types.h> | |
37923ed6 DA |
23 | #include <net/fib_notifier.h> |
24 | #include <net/ip_fib.h> | |
25 | #include <net/ip6_fib.h> | |
26 | #include <net/fib_rules.h> | |
a5facc4c | 27 | #include <net/net_namespace.h> |
8fa84742 | 28 | #include <net/nexthop.h> |
37923ed6 DA |
29 | |
30 | #include "netdevsim.h" | |
31 | ||
32 | struct nsim_fib_entry { | |
33 | u64 max; | |
34 | u64 num; | |
35 | }; | |
36 | ||
37 | struct nsim_per_fib_data { | |
38 | struct nsim_fib_entry fib; | |
39 | struct nsim_fib_entry rules; | |
40 | }; | |
41 | ||
42 | struct nsim_fib_data { | |
a5facc4c | 43 | struct notifier_block fib_nb; |
37923ed6 DA |
44 | struct nsim_per_fib_data ipv4; |
45 | struct nsim_per_fib_data ipv6; | |
35266255 | 46 | struct nsim_fib_entry nexthops; |
48bb9eb4 IS |
47 | struct rhashtable fib_rt_ht; |
48 | struct list_head fib_rt_list; | |
49 | spinlock_t fib_lock; /* Protects hashtable, list and accounting */ | |
8fa84742 IS |
50 | struct notifier_block nexthop_nb; |
51 | struct rhashtable nexthop_ht; | |
48bb9eb4 IS |
52 | struct devlink *devlink; |
53 | }; | |
54 | ||
55 | struct nsim_fib_rt_key { | |
56 | unsigned char addr[sizeof(struct in6_addr)]; | |
57 | unsigned char prefix_len; | |
58 | int family; | |
59 | u32 tb_id; | |
60 | }; | |
61 | ||
62 | struct nsim_fib_rt { | |
63 | struct nsim_fib_rt_key key; | |
64 | struct rhash_head ht_node; | |
65 | struct list_head list; /* Member of fib_rt_list */ | |
66 | }; | |
67 | ||
68 | struct nsim_fib4_rt { | |
69 | struct nsim_fib_rt common; | |
70 | struct fib_info *fi; | |
71 | u8 tos; | |
72 | u8 type; | |
73 | }; | |
74 | ||
75 | struct nsim_fib6_rt { | |
76 | struct nsim_fib_rt common; | |
77 | struct list_head nh_list; | |
78 | unsigned int nhs; | |
79 | }; | |
80 | ||
81 | struct nsim_fib6_rt_nh { | |
82 | struct list_head list; /* Member of nh_list */ | |
83 | struct fib6_info *rt; | |
84 | }; | |
85 | ||
86 | static const struct rhashtable_params nsim_fib_rt_ht_params = { | |
87 | .key_offset = offsetof(struct nsim_fib_rt, key), | |
88 | .head_offset = offsetof(struct nsim_fib_rt, ht_node), | |
89 | .key_len = sizeof(struct nsim_fib_rt_key), | |
90 | .automatic_shrinking = true, | |
37923ed6 DA |
91 | }; |
92 | ||
8fa84742 IS |
93 | struct nsim_nexthop { |
94 | struct rhash_head ht_node; | |
95 | u64 occ; | |
96 | u32 id; | |
97 | }; | |
98 | ||
99 | static const struct rhashtable_params nsim_nexthop_ht_params = { | |
100 | .key_offset = offsetof(struct nsim_nexthop, id), | |
101 | .head_offset = offsetof(struct nsim_nexthop, ht_node), | |
102 | .key_len = sizeof(u32), | |
103 | .automatic_shrinking = true, | |
104 | }; | |
105 | ||
a5facc4c JP |
106 | u64 nsim_fib_get_val(struct nsim_fib_data *fib_data, |
107 | enum nsim_resource_id res_id, bool max) | |
37923ed6 | 108 | { |
37923ed6 DA |
109 | struct nsim_fib_entry *entry; |
110 | ||
111 | switch (res_id) { | |
112 | case NSIM_RESOURCE_IPV4_FIB: | |
113 | entry = &fib_data->ipv4.fib; | |
114 | break; | |
115 | case NSIM_RESOURCE_IPV4_FIB_RULES: | |
116 | entry = &fib_data->ipv4.rules; | |
117 | break; | |
118 | case NSIM_RESOURCE_IPV6_FIB: | |
119 | entry = &fib_data->ipv6.fib; | |
120 | break; | |
121 | case NSIM_RESOURCE_IPV6_FIB_RULES: | |
122 | entry = &fib_data->ipv6.rules; | |
123 | break; | |
35266255 IS |
124 | case NSIM_RESOURCE_NEXTHOPS: |
125 | entry = &fib_data->nexthops; | |
126 | break; | |
37923ed6 DA |
127 | default: |
128 | return 0; | |
129 | } | |
130 | ||
131 | return max ? entry->max : entry->num; | |
132 | } | |
133 | ||
75ba029f JP |
134 | static void nsim_fib_set_max(struct nsim_fib_data *fib_data, |
135 | enum nsim_resource_id res_id, u64 val) | |
37923ed6 | 136 | { |
37923ed6 | 137 | struct nsim_fib_entry *entry; |
37923ed6 DA |
138 | |
139 | switch (res_id) { | |
140 | case NSIM_RESOURCE_IPV4_FIB: | |
141 | entry = &fib_data->ipv4.fib; | |
142 | break; | |
143 | case NSIM_RESOURCE_IPV4_FIB_RULES: | |
144 | entry = &fib_data->ipv4.rules; | |
145 | break; | |
146 | case NSIM_RESOURCE_IPV6_FIB: | |
147 | entry = &fib_data->ipv6.fib; | |
148 | break; | |
149 | case NSIM_RESOURCE_IPV6_FIB_RULES: | |
150 | entry = &fib_data->ipv6.rules; | |
151 | break; | |
35266255 IS |
152 | case NSIM_RESOURCE_NEXTHOPS: |
153 | entry = &fib_data->nexthops; | |
154 | break; | |
37923ed6 | 155 | default: |
75ba029f JP |
156 | WARN_ON(1); |
157 | return; | |
7fa76d77 | 158 | } |
75ba029f | 159 | entry->max = val; |
37923ed6 DA |
160 | } |
161 | ||
162 | static int nsim_fib_rule_account(struct nsim_fib_entry *entry, bool add, | |
163 | struct netlink_ext_ack *extack) | |
164 | { | |
165 | int err = 0; | |
166 | ||
167 | if (add) { | |
168 | if (entry->num < entry->max) { | |
169 | entry->num++; | |
170 | } else { | |
171 | err = -ENOSPC; | |
172 | NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported fib rule entries"); | |
173 | } | |
174 | } else { | |
175 | entry->num--; | |
176 | } | |
177 | ||
178 | return err; | |
179 | } | |
180 | ||
a5facc4c JP |
181 | static int nsim_fib_rule_event(struct nsim_fib_data *data, |
182 | struct fib_notifier_info *info, bool add) | |
37923ed6 | 183 | { |
37923ed6 DA |
184 | struct netlink_ext_ack *extack = info->extack; |
185 | int err = 0; | |
186 | ||
187 | switch (info->family) { | |
188 | case AF_INET: | |
189 | err = nsim_fib_rule_account(&data->ipv4.rules, add, extack); | |
190 | break; | |
191 | case AF_INET6: | |
192 | err = nsim_fib_rule_account(&data->ipv6.rules, add, extack); | |
193 | break; | |
194 | } | |
195 | ||
196 | return err; | |
197 | } | |
198 | ||
199 | static int nsim_fib_account(struct nsim_fib_entry *entry, bool add, | |
200 | struct netlink_ext_ack *extack) | |
201 | { | |
202 | int err = 0; | |
203 | ||
204 | if (add) { | |
205 | if (entry->num < entry->max) { | |
206 | entry->num++; | |
207 | } else { | |
208 | err = -ENOSPC; | |
209 | NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported fib entries"); | |
210 | } | |
211 | } else { | |
212 | entry->num--; | |
213 | } | |
214 | ||
215 | return err; | |
216 | } | |
217 | ||
48bb9eb4 IS |
218 | static void nsim_fib_rt_init(struct nsim_fib_data *data, |
219 | struct nsim_fib_rt *fib_rt, const void *addr, | |
220 | size_t addr_len, unsigned int prefix_len, | |
221 | int family, u32 tb_id) | |
222 | { | |
223 | memcpy(fib_rt->key.addr, addr, addr_len); | |
224 | fib_rt->key.prefix_len = prefix_len; | |
225 | fib_rt->key.family = family; | |
226 | fib_rt->key.tb_id = tb_id; | |
227 | list_add(&fib_rt->list, &data->fib_rt_list); | |
228 | } | |
229 | ||
230 | static void nsim_fib_rt_fini(struct nsim_fib_rt *fib_rt) | |
231 | { | |
232 | list_del(&fib_rt->list); | |
233 | } | |
234 | ||
235 | static struct nsim_fib_rt *nsim_fib_rt_lookup(struct rhashtable *fib_rt_ht, | |
236 | const void *addr, size_t addr_len, | |
237 | unsigned int prefix_len, | |
238 | int family, u32 tb_id) | |
239 | { | |
240 | struct nsim_fib_rt_key key; | |
241 | ||
242 | memset(&key, 0, sizeof(key)); | |
243 | memcpy(key.addr, addr, addr_len); | |
244 | key.prefix_len = prefix_len; | |
245 | key.family = family; | |
246 | key.tb_id = tb_id; | |
247 | ||
248 | return rhashtable_lookup_fast(fib_rt_ht, &key, nsim_fib_rt_ht_params); | |
249 | } | |
250 | ||
251 | static struct nsim_fib4_rt * | |
252 | nsim_fib4_rt_create(struct nsim_fib_data *data, | |
253 | struct fib_entry_notifier_info *fen_info) | |
254 | { | |
255 | struct nsim_fib4_rt *fib4_rt; | |
256 | ||
257 | fib4_rt = kzalloc(sizeof(*fib4_rt), GFP_ATOMIC); | |
258 | if (!fib4_rt) | |
259 | return NULL; | |
260 | ||
261 | nsim_fib_rt_init(data, &fib4_rt->common, &fen_info->dst, sizeof(u32), | |
262 | fen_info->dst_len, AF_INET, fen_info->tb_id); | |
263 | ||
264 | fib4_rt->fi = fen_info->fi; | |
265 | fib_info_hold(fib4_rt->fi); | |
266 | fib4_rt->tos = fen_info->tos; | |
267 | fib4_rt->type = fen_info->type; | |
268 | ||
269 | return fib4_rt; | |
270 | } | |
271 | ||
272 | static void nsim_fib4_rt_destroy(struct nsim_fib4_rt *fib4_rt) | |
273 | { | |
274 | fib_info_put(fib4_rt->fi); | |
275 | nsim_fib_rt_fini(&fib4_rt->common); | |
276 | kfree(fib4_rt); | |
277 | } | |
278 | ||
279 | static struct nsim_fib4_rt * | |
280 | nsim_fib4_rt_lookup(struct rhashtable *fib_rt_ht, | |
281 | const struct fib_entry_notifier_info *fen_info) | |
282 | { | |
283 | struct nsim_fib_rt *fib_rt; | |
284 | ||
285 | fib_rt = nsim_fib_rt_lookup(fib_rt_ht, &fen_info->dst, sizeof(u32), | |
286 | fen_info->dst_len, AF_INET, | |
287 | fen_info->tb_id); | |
288 | if (!fib_rt) | |
289 | return NULL; | |
290 | ||
291 | return container_of(fib_rt, struct nsim_fib4_rt, common); | |
292 | } | |
293 | ||
294 | static void nsim_fib4_rt_hw_flags_set(struct net *net, | |
295 | const struct nsim_fib4_rt *fib4_rt, | |
296 | bool trap) | |
297 | { | |
298 | u32 *p_dst = (u32 *) fib4_rt->common.key.addr; | |
299 | int dst_len = fib4_rt->common.key.prefix_len; | |
300 | struct fib_rt_info fri; | |
301 | ||
302 | fri.fi = fib4_rt->fi; | |
303 | fri.tb_id = fib4_rt->common.key.tb_id; | |
304 | fri.dst = cpu_to_be32(*p_dst); | |
305 | fri.dst_len = dst_len; | |
306 | fri.tos = fib4_rt->tos; | |
307 | fri.type = fib4_rt->type; | |
308 | fri.offload = false; | |
309 | fri.trap = trap; | |
310 | fib_alias_hw_flags_set(net, &fri); | |
311 | } | |
312 | ||
313 | static int nsim_fib4_rt_add(struct nsim_fib_data *data, | |
314 | struct nsim_fib4_rt *fib4_rt, | |
315 | struct netlink_ext_ack *extack) | |
316 | { | |
317 | struct net *net = devlink_net(data->devlink); | |
318 | int err; | |
319 | ||
320 | err = nsim_fib_account(&data->ipv4.fib, true, extack); | |
321 | if (err) | |
322 | return err; | |
323 | ||
324 | err = rhashtable_insert_fast(&data->fib_rt_ht, | |
325 | &fib4_rt->common.ht_node, | |
326 | nsim_fib_rt_ht_params); | |
327 | if (err) { | |
328 | NL_SET_ERR_MSG_MOD(extack, "Failed to insert IPv4 route"); | |
329 | goto err_fib_dismiss; | |
330 | } | |
331 | ||
332 | nsim_fib4_rt_hw_flags_set(net, fib4_rt, true); | |
333 | ||
334 | return 0; | |
335 | ||
336 | err_fib_dismiss: | |
337 | nsim_fib_account(&data->ipv4.fib, false, extack); | |
338 | return err; | |
339 | } | |
340 | ||
341 | static int nsim_fib4_rt_replace(struct nsim_fib_data *data, | |
342 | struct nsim_fib4_rt *fib4_rt, | |
343 | struct nsim_fib4_rt *fib4_rt_old, | |
344 | struct netlink_ext_ack *extack) | |
345 | { | |
346 | struct net *net = devlink_net(data->devlink); | |
347 | int err; | |
348 | ||
349 | /* We are replacing a route, so no need to change the accounting. */ | |
350 | err = rhashtable_replace_fast(&data->fib_rt_ht, | |
351 | &fib4_rt_old->common.ht_node, | |
352 | &fib4_rt->common.ht_node, | |
353 | nsim_fib_rt_ht_params); | |
354 | if (err) { | |
355 | NL_SET_ERR_MSG_MOD(extack, "Failed to replace IPv4 route"); | |
356 | return err; | |
357 | } | |
358 | ||
359 | nsim_fib4_rt_hw_flags_set(net, fib4_rt, true); | |
360 | ||
361 | nsim_fib4_rt_hw_flags_set(net, fib4_rt_old, false); | |
362 | nsim_fib4_rt_destroy(fib4_rt_old); | |
363 | ||
364 | return 0; | |
365 | } | |
366 | ||
367 | static int nsim_fib4_rt_insert(struct nsim_fib_data *data, | |
368 | struct fib_entry_notifier_info *fen_info) | |
369 | { | |
370 | struct netlink_ext_ack *extack = fen_info->info.extack; | |
371 | struct nsim_fib4_rt *fib4_rt, *fib4_rt_old; | |
372 | int err; | |
373 | ||
374 | fib4_rt = nsim_fib4_rt_create(data, fen_info); | |
375 | if (!fib4_rt) | |
376 | return -ENOMEM; | |
377 | ||
378 | fib4_rt_old = nsim_fib4_rt_lookup(&data->fib_rt_ht, fen_info); | |
379 | if (!fib4_rt_old) | |
380 | err = nsim_fib4_rt_add(data, fib4_rt, extack); | |
381 | else | |
382 | err = nsim_fib4_rt_replace(data, fib4_rt, fib4_rt_old, extack); | |
383 | ||
384 | if (err) | |
385 | nsim_fib4_rt_destroy(fib4_rt); | |
386 | ||
387 | return err; | |
388 | } | |
389 | ||
390 | static void nsim_fib4_rt_remove(struct nsim_fib_data *data, | |
391 | const struct fib_entry_notifier_info *fen_info) | |
392 | { | |
393 | struct netlink_ext_ack *extack = fen_info->info.extack; | |
394 | struct nsim_fib4_rt *fib4_rt; | |
395 | ||
396 | fib4_rt = nsim_fib4_rt_lookup(&data->fib_rt_ht, fen_info); | |
397 | if (WARN_ON_ONCE(!fib4_rt)) | |
398 | return; | |
399 | ||
400 | rhashtable_remove_fast(&data->fib_rt_ht, &fib4_rt->common.ht_node, | |
401 | nsim_fib_rt_ht_params); | |
402 | nsim_fib_account(&data->ipv4.fib, false, extack); | |
403 | nsim_fib4_rt_destroy(fib4_rt); | |
404 | } | |
405 | ||
406 | static int nsim_fib4_event(struct nsim_fib_data *data, | |
407 | struct fib_notifier_info *info, | |
408 | unsigned long event) | |
409 | { | |
410 | struct fib_entry_notifier_info *fen_info; | |
411 | int err = 0; | |
412 | ||
413 | fen_info = container_of(info, struct fib_entry_notifier_info, info); | |
414 | ||
48bb9eb4 IS |
415 | switch (event) { |
416 | case FIB_EVENT_ENTRY_REPLACE: | |
417 | err = nsim_fib4_rt_insert(data, fen_info); | |
418 | break; | |
419 | case FIB_EVENT_ENTRY_DEL: | |
420 | nsim_fib4_rt_remove(data, fen_info); | |
421 | break; | |
422 | default: | |
423 | break; | |
424 | } | |
425 | ||
426 | return err; | |
427 | } | |
428 | ||
429 | static struct nsim_fib6_rt_nh * | |
430 | nsim_fib6_rt_nh_find(const struct nsim_fib6_rt *fib6_rt, | |
431 | const struct fib6_info *rt) | |
432 | { | |
433 | struct nsim_fib6_rt_nh *fib6_rt_nh; | |
434 | ||
435 | list_for_each_entry(fib6_rt_nh, &fib6_rt->nh_list, list) { | |
436 | if (fib6_rt_nh->rt == rt) | |
437 | return fib6_rt_nh; | |
438 | } | |
439 | ||
440 | return NULL; | |
441 | } | |
442 | ||
443 | static int nsim_fib6_rt_nh_add(struct nsim_fib6_rt *fib6_rt, | |
444 | struct fib6_info *rt) | |
445 | { | |
446 | struct nsim_fib6_rt_nh *fib6_rt_nh; | |
447 | ||
448 | fib6_rt_nh = kzalloc(sizeof(*fib6_rt_nh), GFP_ATOMIC); | |
449 | if (!fib6_rt_nh) | |
450 | return -ENOMEM; | |
451 | ||
452 | fib6_info_hold(rt); | |
453 | fib6_rt_nh->rt = rt; | |
454 | list_add_tail(&fib6_rt_nh->list, &fib6_rt->nh_list); | |
455 | fib6_rt->nhs++; | |
456 | ||
457 | return 0; | |
458 | } | |
459 | ||
460 | static void nsim_fib6_rt_nh_del(struct nsim_fib6_rt *fib6_rt, | |
461 | const struct fib6_info *rt) | |
462 | { | |
463 | struct nsim_fib6_rt_nh *fib6_rt_nh; | |
464 | ||
465 | fib6_rt_nh = nsim_fib6_rt_nh_find(fib6_rt, rt); | |
466 | if (WARN_ON_ONCE(!fib6_rt_nh)) | |
467 | return; | |
468 | ||
469 | fib6_rt->nhs--; | |
470 | list_del(&fib6_rt_nh->list); | |
471 | #if IS_ENABLED(CONFIG_IPV6) | |
472 | fib6_info_release(fib6_rt_nh->rt); | |
473 | #endif | |
474 | kfree(fib6_rt_nh); | |
475 | } | |
476 | ||
477 | static struct nsim_fib6_rt * | |
478 | nsim_fib6_rt_create(struct nsim_fib_data *data, | |
479 | struct fib6_entry_notifier_info *fen6_info) | |
480 | { | |
481 | struct fib6_info *iter, *rt = fen6_info->rt; | |
482 | struct nsim_fib6_rt *fib6_rt; | |
483 | int i = 0; | |
484 | int err; | |
485 | ||
486 | fib6_rt = kzalloc(sizeof(*fib6_rt), GFP_ATOMIC); | |
487 | if (!fib6_rt) | |
41cdc741 | 488 | return ERR_PTR(-ENOMEM); |
48bb9eb4 IS |
489 | |
490 | nsim_fib_rt_init(data, &fib6_rt->common, &rt->fib6_dst.addr, | |
491 | sizeof(rt->fib6_dst.addr), rt->fib6_dst.plen, AF_INET6, | |
492 | rt->fib6_table->tb6_id); | |
493 | ||
494 | /* We consider a multipath IPv6 route as one entry, but it can be made | |
495 | * up from several fib6_info structs (one for each nexthop), so we | |
496 | * add them all to the same list under the entry. | |
497 | */ | |
498 | INIT_LIST_HEAD(&fib6_rt->nh_list); | |
499 | ||
500 | err = nsim_fib6_rt_nh_add(fib6_rt, rt); | |
501 | if (err) | |
502 | goto err_fib_rt_fini; | |
503 | ||
504 | if (!fen6_info->nsiblings) | |
505 | return fib6_rt; | |
506 | ||
507 | list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings) { | |
508 | if (i == fen6_info->nsiblings) | |
509 | break; | |
510 | ||
511 | err = nsim_fib6_rt_nh_add(fib6_rt, iter); | |
512 | if (err) | |
513 | goto err_fib6_rt_nh_del; | |
514 | i++; | |
515 | } | |
516 | WARN_ON_ONCE(i != fen6_info->nsiblings); | |
517 | ||
518 | return fib6_rt; | |
519 | ||
520 | err_fib6_rt_nh_del: | |
521 | list_for_each_entry_continue_reverse(iter, &rt->fib6_siblings, | |
522 | fib6_siblings) | |
523 | nsim_fib6_rt_nh_del(fib6_rt, iter); | |
524 | nsim_fib6_rt_nh_del(fib6_rt, rt); | |
525 | err_fib_rt_fini: | |
526 | nsim_fib_rt_fini(&fib6_rt->common); | |
527 | kfree(fib6_rt); | |
528 | return ERR_PTR(err); | |
529 | } | |
530 | ||
531 | static void nsim_fib6_rt_destroy(struct nsim_fib6_rt *fib6_rt) | |
532 | { | |
533 | struct nsim_fib6_rt_nh *iter, *tmp; | |
534 | ||
535 | list_for_each_entry_safe(iter, tmp, &fib6_rt->nh_list, list) | |
536 | nsim_fib6_rt_nh_del(fib6_rt, iter->rt); | |
537 | WARN_ON_ONCE(!list_empty(&fib6_rt->nh_list)); | |
538 | nsim_fib_rt_fini(&fib6_rt->common); | |
539 | kfree(fib6_rt); | |
540 | } | |
541 | ||
542 | static struct nsim_fib6_rt * | |
543 | nsim_fib6_rt_lookup(struct rhashtable *fib_rt_ht, const struct fib6_info *rt) | |
544 | { | |
545 | struct nsim_fib_rt *fib_rt; | |
546 | ||
547 | fib_rt = nsim_fib_rt_lookup(fib_rt_ht, &rt->fib6_dst.addr, | |
548 | sizeof(rt->fib6_dst.addr), | |
549 | rt->fib6_dst.plen, AF_INET6, | |
550 | rt->fib6_table->tb6_id); | |
551 | if (!fib_rt) | |
552 | return NULL; | |
553 | ||
554 | return container_of(fib_rt, struct nsim_fib6_rt, common); | |
555 | } | |
556 | ||
557 | static int nsim_fib6_rt_append(struct nsim_fib_data *data, | |
558 | struct fib6_entry_notifier_info *fen6_info) | |
559 | { | |
560 | struct fib6_info *iter, *rt = fen6_info->rt; | |
561 | struct nsim_fib6_rt *fib6_rt; | |
562 | int i = 0; | |
563 | int err; | |
564 | ||
565 | fib6_rt = nsim_fib6_rt_lookup(&data->fib_rt_ht, rt); | |
566 | if (WARN_ON_ONCE(!fib6_rt)) | |
567 | return -EINVAL; | |
568 | ||
569 | err = nsim_fib6_rt_nh_add(fib6_rt, rt); | |
570 | if (err) | |
571 | return err; | |
572 | rt->trap = true; | |
573 | ||
574 | if (!fen6_info->nsiblings) | |
575 | return 0; | |
576 | ||
577 | list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings) { | |
578 | if (i == fen6_info->nsiblings) | |
579 | break; | |
580 | ||
581 | err = nsim_fib6_rt_nh_add(fib6_rt, iter); | |
582 | if (err) | |
583 | goto err_fib6_rt_nh_del; | |
584 | iter->trap = true; | |
585 | i++; | |
586 | } | |
587 | WARN_ON_ONCE(i != fen6_info->nsiblings); | |
588 | ||
589 | return 0; | |
590 | ||
591 | err_fib6_rt_nh_del: | |
592 | list_for_each_entry_continue_reverse(iter, &rt->fib6_siblings, | |
593 | fib6_siblings) { | |
594 | iter->trap = false; | |
595 | nsim_fib6_rt_nh_del(fib6_rt, iter); | |
596 | } | |
597 | rt->trap = false; | |
598 | nsim_fib6_rt_nh_del(fib6_rt, rt); | |
599 | return err; | |
600 | } | |
601 | ||
602 | static void nsim_fib6_rt_hw_flags_set(const struct nsim_fib6_rt *fib6_rt, | |
603 | bool trap) | |
604 | { | |
605 | struct nsim_fib6_rt_nh *fib6_rt_nh; | |
606 | ||
607 | list_for_each_entry(fib6_rt_nh, &fib6_rt->nh_list, list) | |
608 | fib6_info_hw_flags_set(fib6_rt_nh->rt, false, trap); | |
609 | } | |
610 | ||
611 | static int nsim_fib6_rt_add(struct nsim_fib_data *data, | |
612 | struct nsim_fib6_rt *fib6_rt, | |
613 | struct netlink_ext_ack *extack) | |
614 | { | |
615 | int err; | |
616 | ||
617 | err = nsim_fib_account(&data->ipv6.fib, true, extack); | |
618 | if (err) | |
619 | return err; | |
620 | ||
621 | err = rhashtable_insert_fast(&data->fib_rt_ht, | |
622 | &fib6_rt->common.ht_node, | |
623 | nsim_fib_rt_ht_params); | |
624 | if (err) { | |
625 | NL_SET_ERR_MSG_MOD(extack, "Failed to insert IPv6 route"); | |
626 | goto err_fib_dismiss; | |
627 | } | |
628 | ||
629 | nsim_fib6_rt_hw_flags_set(fib6_rt, true); | |
630 | ||
631 | return 0; | |
632 | ||
633 | err_fib_dismiss: | |
634 | nsim_fib_account(&data->ipv6.fib, false, extack); | |
635 | return err; | |
636 | } | |
637 | ||
638 | static int nsim_fib6_rt_replace(struct nsim_fib_data *data, | |
639 | struct nsim_fib6_rt *fib6_rt, | |
640 | struct nsim_fib6_rt *fib6_rt_old, | |
641 | struct netlink_ext_ack *extack) | |
642 | { | |
643 | int err; | |
644 | ||
645 | /* We are replacing a route, so no need to change the accounting. */ | |
646 | err = rhashtable_replace_fast(&data->fib_rt_ht, | |
647 | &fib6_rt_old->common.ht_node, | |
648 | &fib6_rt->common.ht_node, | |
649 | nsim_fib_rt_ht_params); | |
650 | if (err) { | |
651 | NL_SET_ERR_MSG_MOD(extack, "Failed to replace IPv6 route"); | |
652 | return err; | |
653 | } | |
654 | ||
655 | nsim_fib6_rt_hw_flags_set(fib6_rt, true); | |
656 | ||
657 | nsim_fib6_rt_hw_flags_set(fib6_rt_old, false); | |
658 | nsim_fib6_rt_destroy(fib6_rt_old); | |
659 | ||
660 | return 0; | |
661 | } | |
662 | ||
663 | static int nsim_fib6_rt_insert(struct nsim_fib_data *data, | |
664 | struct fib6_entry_notifier_info *fen6_info) | |
665 | { | |
666 | struct netlink_ext_ack *extack = fen6_info->info.extack; | |
667 | struct nsim_fib6_rt *fib6_rt, *fib6_rt_old; | |
668 | int err; | |
669 | ||
670 | fib6_rt = nsim_fib6_rt_create(data, fen6_info); | |
41cdc741 ED |
671 | if (IS_ERR(fib6_rt)) |
672 | return PTR_ERR(fib6_rt); | |
48bb9eb4 IS |
673 | |
674 | fib6_rt_old = nsim_fib6_rt_lookup(&data->fib_rt_ht, fen6_info->rt); | |
675 | if (!fib6_rt_old) | |
676 | err = nsim_fib6_rt_add(data, fib6_rt, extack); | |
677 | else | |
678 | err = nsim_fib6_rt_replace(data, fib6_rt, fib6_rt_old, extack); | |
679 | ||
680 | if (err) | |
681 | nsim_fib6_rt_destroy(fib6_rt); | |
682 | ||
683 | return err; | |
684 | } | |
685 | ||
686 | static void | |
687 | nsim_fib6_rt_remove(struct nsim_fib_data *data, | |
688 | const struct fib6_entry_notifier_info *fen6_info) | |
689 | { | |
690 | struct netlink_ext_ack *extack = fen6_info->info.extack; | |
691 | struct nsim_fib6_rt *fib6_rt; | |
692 | ||
693 | /* Multipath routes are first added to the FIB trie and only then | |
694 | * notified. If we vetoed the addition, we will get a delete | |
695 | * notification for a route we do not have. Therefore, do not warn if | |
696 | * route was not found. | |
697 | */ | |
698 | fib6_rt = nsim_fib6_rt_lookup(&data->fib_rt_ht, fen6_info->rt); | |
699 | if (!fib6_rt) | |
700 | return; | |
701 | ||
702 | /* If not all the nexthops are deleted, then only reduce the nexthop | |
703 | * group. | |
704 | */ | |
705 | if (fen6_info->nsiblings + 1 != fib6_rt->nhs) { | |
706 | nsim_fib6_rt_nh_del(fib6_rt, fen6_info->rt); | |
707 | return; | |
708 | } | |
709 | ||
710 | rhashtable_remove_fast(&data->fib_rt_ht, &fib6_rt->common.ht_node, | |
711 | nsim_fib_rt_ht_params); | |
712 | nsim_fib_account(&data->ipv6.fib, false, extack); | |
713 | nsim_fib6_rt_destroy(fib6_rt); | |
714 | } | |
715 | ||
716 | static int nsim_fib6_event(struct nsim_fib_data *data, | |
717 | struct fib_notifier_info *info, | |
718 | unsigned long event) | |
719 | { | |
720 | struct fib6_entry_notifier_info *fen6_info; | |
721 | int err = 0; | |
722 | ||
723 | fen6_info = container_of(info, struct fib6_entry_notifier_info, info); | |
724 | ||
48bb9eb4 IS |
725 | if (fen6_info->rt->fib6_src.plen) { |
726 | NL_SET_ERR_MSG_MOD(info->extack, "IPv6 source-specific route is not supported"); | |
727 | return 0; | |
728 | } | |
729 | ||
730 | switch (event) { | |
731 | case FIB_EVENT_ENTRY_REPLACE: | |
732 | err = nsim_fib6_rt_insert(data, fen6_info); | |
733 | break; | |
734 | case FIB_EVENT_ENTRY_APPEND: | |
735 | err = nsim_fib6_rt_append(data, fen6_info); | |
736 | break; | |
737 | case FIB_EVENT_ENTRY_DEL: | |
738 | nsim_fib6_rt_remove(data, fen6_info); | |
739 | break; | |
740 | default: | |
741 | break; | |
742 | } | |
743 | ||
744 | return err; | |
745 | } | |
746 | ||
a5facc4c | 747 | static int nsim_fib_event(struct nsim_fib_data *data, |
48bb9eb4 | 748 | struct fib_notifier_info *info, unsigned long event) |
37923ed6 | 749 | { |
37923ed6 DA |
750 | int err = 0; |
751 | ||
752 | switch (info->family) { | |
753 | case AF_INET: | |
48bb9eb4 | 754 | err = nsim_fib4_event(data, info, event); |
37923ed6 DA |
755 | break; |
756 | case AF_INET6: | |
48bb9eb4 | 757 | err = nsim_fib6_event(data, info, event); |
37923ed6 DA |
758 | break; |
759 | } | |
760 | ||
761 | return err; | |
762 | } | |
763 | ||
764 | static int nsim_fib_event_nb(struct notifier_block *nb, unsigned long event, | |
765 | void *ptr) | |
766 | { | |
a5facc4c JP |
767 | struct nsim_fib_data *data = container_of(nb, struct nsim_fib_data, |
768 | fib_nb); | |
37923ed6 DA |
769 | struct fib_notifier_info *info = ptr; |
770 | int err = 0; | |
771 | ||
48bb9eb4 IS |
772 | /* IPv6 routes can be added via RAs from softIRQ. */ |
773 | spin_lock_bh(&data->fib_lock); | |
774 | ||
37923ed6 | 775 | switch (event) { |
df561f66 | 776 | case FIB_EVENT_RULE_ADD: |
37923ed6 | 777 | case FIB_EVENT_RULE_DEL: |
a5facc4c JP |
778 | err = nsim_fib_rule_event(data, info, |
779 | event == FIB_EVENT_RULE_ADD); | |
37923ed6 DA |
780 | break; |
781 | ||
df561f66 GS |
782 | case FIB_EVENT_ENTRY_REPLACE: |
783 | case FIB_EVENT_ENTRY_APPEND: | |
37923ed6 | 784 | case FIB_EVENT_ENTRY_DEL: |
48bb9eb4 | 785 | err = nsim_fib_event(data, info, event); |
37923ed6 DA |
786 | break; |
787 | } | |
788 | ||
48bb9eb4 IS |
789 | spin_unlock_bh(&data->fib_lock); |
790 | ||
37923ed6 DA |
791 | return notifier_from_errno(err); |
792 | } | |
793 | ||
48bb9eb4 IS |
794 | static void nsim_fib4_rt_free(struct nsim_fib_rt *fib_rt, |
795 | struct nsim_fib_data *data) | |
796 | { | |
797 | struct devlink *devlink = data->devlink; | |
798 | struct nsim_fib4_rt *fib4_rt; | |
799 | ||
800 | fib4_rt = container_of(fib_rt, struct nsim_fib4_rt, common); | |
801 | nsim_fib4_rt_hw_flags_set(devlink_net(devlink), fib4_rt, false); | |
802 | nsim_fib_account(&data->ipv4.fib, false, NULL); | |
803 | nsim_fib4_rt_destroy(fib4_rt); | |
804 | } | |
805 | ||
806 | static void nsim_fib6_rt_free(struct nsim_fib_rt *fib_rt, | |
807 | struct nsim_fib_data *data) | |
808 | { | |
809 | struct nsim_fib6_rt *fib6_rt; | |
810 | ||
811 | fib6_rt = container_of(fib_rt, struct nsim_fib6_rt, common); | |
812 | nsim_fib6_rt_hw_flags_set(fib6_rt, false); | |
813 | nsim_fib_account(&data->ipv6.fib, false, NULL); | |
814 | nsim_fib6_rt_destroy(fib6_rt); | |
815 | } | |
816 | ||
817 | static void nsim_fib_rt_free(void *ptr, void *arg) | |
818 | { | |
819 | struct nsim_fib_rt *fib_rt = ptr; | |
820 | struct nsim_fib_data *data = arg; | |
821 | ||
822 | switch (fib_rt->key.family) { | |
823 | case AF_INET: | |
824 | nsim_fib4_rt_free(fib_rt, data); | |
825 | break; | |
826 | case AF_INET6: | |
827 | nsim_fib6_rt_free(fib_rt, data); | |
828 | break; | |
829 | default: | |
830 | WARN_ON_ONCE(1); | |
831 | } | |
832 | } | |
833 | ||
37923ed6 DA |
834 | /* inconsistent dump, trying again */ |
835 | static void nsim_fib_dump_inconsistent(struct notifier_block *nb) | |
836 | { | |
a5facc4c JP |
837 | struct nsim_fib_data *data = container_of(nb, struct nsim_fib_data, |
838 | fib_nb); | |
48bb9eb4 IS |
839 | struct nsim_fib_rt *fib_rt, *fib_rt_tmp; |
840 | ||
841 | /* The notifier block is still not registered, so we do not need to | |
842 | * take any locks here. | |
843 | */ | |
844 | list_for_each_entry_safe(fib_rt, fib_rt_tmp, &data->fib_rt_list, list) { | |
845 | rhashtable_remove_fast(&data->fib_rt_ht, &fib_rt->ht_node, | |
846 | nsim_fib_rt_ht_params); | |
847 | nsim_fib_rt_free(fib_rt, data); | |
848 | } | |
59c84b9f | 849 | |
a5facc4c | 850 | data->ipv4.rules.num = 0ULL; |
a5facc4c JP |
851 | data->ipv6.rules.num = 0ULL; |
852 | } | |
59c84b9f | 853 | |
8fa84742 IS |
854 | static struct nsim_nexthop *nsim_nexthop_create(struct nsim_fib_data *data, |
855 | struct nh_notifier_info *info) | |
856 | { | |
857 | struct nsim_nexthop *nexthop; | |
858 | u64 occ = 0; | |
859 | int i; | |
860 | ||
861 | nexthop = kzalloc(sizeof(*nexthop), GFP_KERNEL); | |
862 | if (!nexthop) | |
09ad6bec | 863 | return ERR_PTR(-ENOMEM); |
8fa84742 IS |
864 | |
865 | nexthop->id = info->id; | |
866 | ||
867 | /* Determine the number of nexthop entries the new nexthop will | |
868 | * occupy. | |
869 | */ | |
870 | ||
09ad6bec IS |
871 | switch (info->type) { |
872 | case NH_NOTIFIER_INFO_TYPE_SINGLE: | |
8fa84742 | 873 | occ = 1; |
09ad6bec IS |
874 | break; |
875 | case NH_NOTIFIER_INFO_TYPE_GRP: | |
876 | for (i = 0; i < info->nh_grp->num_nh; i++) | |
877 | occ += info->nh_grp->nh_entries[i].weight; | |
878 | break; | |
879 | default: | |
880 | NL_SET_ERR_MSG_MOD(info->extack, "Unsupported nexthop type"); | |
881 | kfree(nexthop); | |
882 | return ERR_PTR(-EOPNOTSUPP); | |
8fa84742 IS |
883 | } |
884 | ||
8fa84742 IS |
885 | nexthop->occ = occ; |
886 | return nexthop; | |
887 | } | |
888 | ||
889 | static void nsim_nexthop_destroy(struct nsim_nexthop *nexthop) | |
890 | { | |
891 | kfree(nexthop); | |
892 | } | |
893 | ||
894 | static int nsim_nexthop_account(struct nsim_fib_data *data, u64 occ, | |
895 | bool add, struct netlink_ext_ack *extack) | |
896 | { | |
897 | int err = 0; | |
898 | ||
899 | if (add) { | |
900 | if (data->nexthops.num + occ <= data->nexthops.max) { | |
901 | data->nexthops.num += occ; | |
902 | } else { | |
903 | err = -ENOSPC; | |
904 | NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported nexthops"); | |
905 | } | |
906 | } else { | |
907 | if (WARN_ON(occ > data->nexthops.num)) | |
908 | return -EINVAL; | |
909 | data->nexthops.num -= occ; | |
910 | } | |
911 | ||
912 | return err; | |
913 | } | |
914 | ||
915 | static int nsim_nexthop_add(struct nsim_fib_data *data, | |
916 | struct nsim_nexthop *nexthop, | |
917 | struct netlink_ext_ack *extack) | |
918 | { | |
919 | struct net *net = devlink_net(data->devlink); | |
920 | int err; | |
921 | ||
922 | err = nsim_nexthop_account(data, nexthop->occ, true, extack); | |
923 | if (err) | |
924 | return err; | |
925 | ||
926 | err = rhashtable_insert_fast(&data->nexthop_ht, &nexthop->ht_node, | |
927 | nsim_nexthop_ht_params); | |
928 | if (err) { | |
929 | NL_SET_ERR_MSG_MOD(extack, "Failed to insert nexthop"); | |
930 | goto err_nexthop_dismiss; | |
931 | } | |
932 | ||
933 | nexthop_set_hw_flags(net, nexthop->id, false, true); | |
934 | ||
935 | return 0; | |
936 | ||
937 | err_nexthop_dismiss: | |
938 | nsim_nexthop_account(data, nexthop->occ, false, extack); | |
939 | return err; | |
940 | } | |
941 | ||
942 | static int nsim_nexthop_replace(struct nsim_fib_data *data, | |
943 | struct nsim_nexthop *nexthop, | |
944 | struct nsim_nexthop *nexthop_old, | |
945 | struct netlink_ext_ack *extack) | |
946 | { | |
947 | struct net *net = devlink_net(data->devlink); | |
948 | int err; | |
949 | ||
950 | err = nsim_nexthop_account(data, nexthop->occ, true, extack); | |
951 | if (err) | |
952 | return err; | |
953 | ||
954 | err = rhashtable_replace_fast(&data->nexthop_ht, | |
955 | &nexthop_old->ht_node, &nexthop->ht_node, | |
956 | nsim_nexthop_ht_params); | |
957 | if (err) { | |
958 | NL_SET_ERR_MSG_MOD(extack, "Failed to replace nexthop"); | |
959 | goto err_nexthop_dismiss; | |
960 | } | |
961 | ||
962 | nexthop_set_hw_flags(net, nexthop->id, false, true); | |
963 | nsim_nexthop_account(data, nexthop_old->occ, false, extack); | |
964 | nsim_nexthop_destroy(nexthop_old); | |
965 | ||
966 | return 0; | |
967 | ||
968 | err_nexthop_dismiss: | |
969 | nsim_nexthop_account(data, nexthop->occ, false, extack); | |
970 | return err; | |
971 | } | |
972 | ||
973 | static int nsim_nexthop_insert(struct nsim_fib_data *data, | |
974 | struct nh_notifier_info *info) | |
975 | { | |
976 | struct nsim_nexthop *nexthop, *nexthop_old; | |
977 | int err; | |
978 | ||
979 | nexthop = nsim_nexthop_create(data, info); | |
09ad6bec IS |
980 | if (IS_ERR(nexthop)) |
981 | return PTR_ERR(nexthop); | |
8fa84742 IS |
982 | |
983 | nexthop_old = rhashtable_lookup_fast(&data->nexthop_ht, &info->id, | |
984 | nsim_nexthop_ht_params); | |
985 | if (!nexthop_old) | |
986 | err = nsim_nexthop_add(data, nexthop, info->extack); | |
987 | else | |
988 | err = nsim_nexthop_replace(data, nexthop, nexthop_old, | |
989 | info->extack); | |
990 | ||
991 | if (err) | |
992 | nsim_nexthop_destroy(nexthop); | |
993 | ||
994 | return err; | |
995 | } | |
996 | ||
997 | static void nsim_nexthop_remove(struct nsim_fib_data *data, | |
998 | struct nh_notifier_info *info) | |
999 | { | |
1000 | struct nsim_nexthop *nexthop; | |
1001 | ||
1002 | nexthop = rhashtable_lookup_fast(&data->nexthop_ht, &info->id, | |
1003 | nsim_nexthop_ht_params); | |
1004 | if (!nexthop) | |
1005 | return; | |
1006 | ||
1007 | rhashtable_remove_fast(&data->nexthop_ht, &nexthop->ht_node, | |
1008 | nsim_nexthop_ht_params); | |
1009 | nsim_nexthop_account(data, nexthop->occ, false, info->extack); | |
1010 | nsim_nexthop_destroy(nexthop); | |
1011 | } | |
1012 | ||
1013 | static int nsim_nexthop_event_nb(struct notifier_block *nb, unsigned long event, | |
1014 | void *ptr) | |
1015 | { | |
1016 | struct nsim_fib_data *data = container_of(nb, struct nsim_fib_data, | |
1017 | nexthop_nb); | |
1018 | struct nh_notifier_info *info = ptr; | |
1019 | int err = 0; | |
1020 | ||
1021 | ASSERT_RTNL(); | |
1022 | ||
1023 | switch (event) { | |
1024 | case NEXTHOP_EVENT_REPLACE: | |
1025 | err = nsim_nexthop_insert(data, info); | |
1026 | break; | |
1027 | case NEXTHOP_EVENT_DEL: | |
1028 | nsim_nexthop_remove(data, info); | |
1029 | break; | |
1030 | default: | |
1031 | break; | |
1032 | } | |
1033 | ||
1034 | return notifier_from_errno(err); | |
1035 | } | |
1036 | ||
1037 | static void nsim_nexthop_free(void *ptr, void *arg) | |
1038 | { | |
1039 | struct nsim_nexthop *nexthop = ptr; | |
1040 | struct nsim_fib_data *data = arg; | |
1041 | struct net *net; | |
1042 | ||
1043 | net = devlink_net(data->devlink); | |
1044 | nexthop_set_hw_flags(net, nexthop->id, false, false); | |
1045 | nsim_nexthop_account(data, nexthop->occ, false, NULL); | |
1046 | nsim_nexthop_destroy(nexthop); | |
1047 | } | |
1048 | ||
a5facc4c JP |
1049 | static u64 nsim_fib_ipv4_resource_occ_get(void *priv) |
1050 | { | |
1051 | struct nsim_fib_data *data = priv; | |
37923ed6 | 1052 | |
a5facc4c | 1053 | return nsim_fib_get_val(data, NSIM_RESOURCE_IPV4_FIB, false); |
37923ed6 DA |
1054 | } |
1055 | ||
a5facc4c | 1056 | static u64 nsim_fib_ipv4_rules_res_occ_get(void *priv) |
59c84b9f | 1057 | { |
a5facc4c | 1058 | struct nsim_fib_data *data = priv; |
37923ed6 | 1059 | |
a5facc4c JP |
1060 | return nsim_fib_get_val(data, NSIM_RESOURCE_IPV4_FIB_RULES, false); |
1061 | } | |
37923ed6 | 1062 | |
a5facc4c JP |
1063 | static u64 nsim_fib_ipv6_resource_occ_get(void *priv) |
1064 | { | |
1065 | struct nsim_fib_data *data = priv; | |
37923ed6 | 1066 | |
a5facc4c | 1067 | return nsim_fib_get_val(data, NSIM_RESOURCE_IPV6_FIB, false); |
59c84b9f | 1068 | } |
37923ed6 | 1069 | |
a5facc4c | 1070 | static u64 nsim_fib_ipv6_rules_res_occ_get(void *priv) |
59c84b9f | 1071 | { |
a5facc4c JP |
1072 | struct nsim_fib_data *data = priv; |
1073 | ||
1074 | return nsim_fib_get_val(data, NSIM_RESOURCE_IPV6_FIB_RULES, false); | |
5fc49422 JP |
1075 | } |
1076 | ||
35266255 IS |
1077 | static u64 nsim_fib_nexthops_res_occ_get(void *priv) |
1078 | { | |
1079 | struct nsim_fib_data *data = priv; | |
1080 | ||
1081 | return nsim_fib_get_val(data, NSIM_RESOURCE_NEXTHOPS, false); | |
1082 | } | |
1083 | ||
75ba029f JP |
1084 | static void nsim_fib_set_max_all(struct nsim_fib_data *data, |
1085 | struct devlink *devlink) | |
1086 | { | |
1087 | enum nsim_resource_id res_ids[] = { | |
1088 | NSIM_RESOURCE_IPV4_FIB, NSIM_RESOURCE_IPV4_FIB_RULES, | |
35266255 IS |
1089 | NSIM_RESOURCE_IPV6_FIB, NSIM_RESOURCE_IPV6_FIB_RULES, |
1090 | NSIM_RESOURCE_NEXTHOPS, | |
75ba029f JP |
1091 | }; |
1092 | int i; | |
1093 | ||
1094 | for (i = 0; i < ARRAY_SIZE(res_ids); i++) { | |
1095 | int err; | |
1096 | u64 val; | |
1097 | ||
1098 | err = devlink_resource_size_get(devlink, res_ids[i], &val); | |
1099 | if (err) | |
1100 | val = (u64) -1; | |
1101 | nsim_fib_set_max(data, res_ids[i], val); | |
1102 | } | |
1103 | } | |
1104 | ||
1105 | struct nsim_fib_data *nsim_fib_create(struct devlink *devlink, | |
1106 | struct netlink_ext_ack *extack) | |
5fc49422 | 1107 | { |
a5facc4c | 1108 | struct nsim_fib_data *data; |
59c84b9f DA |
1109 | int err; |
1110 | ||
a5facc4c JP |
1111 | data = kzalloc(sizeof(*data), GFP_KERNEL); |
1112 | if (!data) | |
1113 | return ERR_PTR(-ENOMEM); | |
48bb9eb4 IS |
1114 | data->devlink = devlink; |
1115 | ||
8fa84742 IS |
1116 | err = rhashtable_init(&data->nexthop_ht, &nsim_nexthop_ht_params); |
1117 | if (err) | |
1118 | goto err_data_free; | |
1119 | ||
48bb9eb4 IS |
1120 | spin_lock_init(&data->fib_lock); |
1121 | INIT_LIST_HEAD(&data->fib_rt_list); | |
1122 | err = rhashtable_init(&data->fib_rt_ht, &nsim_fib_rt_ht_params); | |
1123 | if (err) | |
8fa84742 | 1124 | goto err_rhashtable_nexthop_destroy; |
59c84b9f | 1125 | |
75ba029f | 1126 | nsim_fib_set_max_all(data, devlink); |
a5facc4c | 1127 | |
8fa84742 IS |
1128 | data->nexthop_nb.notifier_call = nsim_nexthop_event_nb; |
1129 | err = register_nexthop_notifier(devlink_net(devlink), &data->nexthop_nb, | |
1130 | extack); | |
1131 | if (err) { | |
1132 | pr_err("Failed to register nexthop notifier\n"); | |
1133 | goto err_rhashtable_fib_destroy; | |
1134 | } | |
1135 | ||
a5facc4c | 1136 | data->fib_nb.notifier_call = nsim_fib_event_nb; |
4f174bbc | 1137 | err = register_fib_notifier(devlink_net(devlink), &data->fib_nb, |
75ba029f | 1138 | nsim_fib_dump_inconsistent, extack); |
a5facc4c | 1139 | if (err) { |
59c84b9f | 1140 | pr_err("Failed to register fib notifier\n"); |
8fa84742 | 1141 | goto err_nexthop_nb_unregister; |
59c84b9f DA |
1142 | } |
1143 | ||
a5facc4c JP |
1144 | devlink_resource_occ_get_register(devlink, |
1145 | NSIM_RESOURCE_IPV4_FIB, | |
1146 | nsim_fib_ipv4_resource_occ_get, | |
1147 | data); | |
1148 | devlink_resource_occ_get_register(devlink, | |
1149 | NSIM_RESOURCE_IPV4_FIB_RULES, | |
1150 | nsim_fib_ipv4_rules_res_occ_get, | |
1151 | data); | |
1152 | devlink_resource_occ_get_register(devlink, | |
1153 | NSIM_RESOURCE_IPV6_FIB, | |
1154 | nsim_fib_ipv6_resource_occ_get, | |
1155 | data); | |
1156 | devlink_resource_occ_get_register(devlink, | |
1157 | NSIM_RESOURCE_IPV6_FIB_RULES, | |
1158 | nsim_fib_ipv6_rules_res_occ_get, | |
1159 | data); | |
35266255 IS |
1160 | devlink_resource_occ_get_register(devlink, |
1161 | NSIM_RESOURCE_NEXTHOPS, | |
1162 | nsim_fib_nexthops_res_occ_get, | |
1163 | data); | |
a5facc4c JP |
1164 | return data; |
1165 | ||
8fa84742 IS |
1166 | err_nexthop_nb_unregister: |
1167 | unregister_nexthop_notifier(devlink_net(devlink), &data->nexthop_nb); | |
1168 | err_rhashtable_fib_destroy: | |
48bb9eb4 IS |
1169 | rhashtable_free_and_destroy(&data->fib_rt_ht, nsim_fib_rt_free, |
1170 | data); | |
8fa84742 IS |
1171 | err_rhashtable_nexthop_destroy: |
1172 | rhashtable_free_and_destroy(&data->nexthop_ht, nsim_nexthop_free, | |
1173 | data); | |
48bb9eb4 | 1174 | err_data_free: |
a5facc4c JP |
1175 | kfree(data); |
1176 | return ERR_PTR(err); | |
1177 | } | |
1178 | ||
1179 | void nsim_fib_destroy(struct devlink *devlink, struct nsim_fib_data *data) | |
1180 | { | |
35266255 IS |
1181 | devlink_resource_occ_get_unregister(devlink, |
1182 | NSIM_RESOURCE_NEXTHOPS); | |
a5facc4c JP |
1183 | devlink_resource_occ_get_unregister(devlink, |
1184 | NSIM_RESOURCE_IPV6_FIB_RULES); | |
1185 | devlink_resource_occ_get_unregister(devlink, | |
1186 | NSIM_RESOURCE_IPV6_FIB); | |
1187 | devlink_resource_occ_get_unregister(devlink, | |
1188 | NSIM_RESOURCE_IPV4_FIB_RULES); | |
1189 | devlink_resource_occ_get_unregister(devlink, | |
1190 | NSIM_RESOURCE_IPV4_FIB); | |
4f174bbc | 1191 | unregister_fib_notifier(devlink_net(devlink), &data->fib_nb); |
8fa84742 | 1192 | unregister_nexthop_notifier(devlink_net(devlink), &data->nexthop_nb); |
48bb9eb4 IS |
1193 | rhashtable_free_and_destroy(&data->fib_rt_ht, nsim_fib_rt_free, |
1194 | data); | |
8fa84742 IS |
1195 | rhashtable_free_and_destroy(&data->nexthop_ht, nsim_nexthop_free, |
1196 | data); | |
48bb9eb4 | 1197 | WARN_ON_ONCE(!list_empty(&data->fib_rt_list)); |
a5facc4c | 1198 | kfree(data); |
37923ed6 | 1199 | } |