1646 lines · c
1// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.02/* Copyright (c) 2019-2021 Marvell International Ltd. All rights reserved */3 4#include <linux/kernel.h>5#include <linux/types.h>6#include <linux/inetdevice.h>7#include <net/inet_dscp.h>8#include <net/switchdev.h>9#include <linux/rhashtable.h>10#include <net/nexthop.h>11#include <net/arp.h>12#include <linux/if_vlan.h>13#include <linux/if_macvlan.h>14#include <net/netevent.h>15 16#include "prestera.h"17#include "prestera_router_hw.h"18 19#define PRESTERA_IMPLICITY_RESOLVE_DEAD_NEIGH20#define PRESTERA_NH_PROBE_INTERVAL 5000 /* ms */21 22struct prestera_kern_neigh_cache_key {23 struct prestera_ip_addr addr;24 struct net_device *dev;25};26 27struct prestera_kern_neigh_cache {28 struct prestera_kern_neigh_cache_key key;29 struct rhash_head ht_node;30 struct list_head kern_fib_cache_list;31 /* Hold prepared nh_neigh info if is in_kernel */32 struct prestera_neigh_info nh_neigh_info;33 /* Indicate if neighbour is reachable by direct route */34 bool reachable;35 /* Lock cache if neigh is present in kernel */36 bool in_kernel;37};38 39struct prestera_kern_fib_cache_key {40 struct prestera_ip_addr addr;41 u32 prefix_len;42 u32 kern_tb_id; /* tb_id from kernel (not fixed) */43};44 45/* Subscribing on neighbours in kernel */46struct prestera_kern_fib_cache {47 struct prestera_kern_fib_cache_key key;48 struct {49 struct prestera_fib_key fib_key;50 enum prestera_fib_type fib_type;51 struct prestera_nexthop_group_key nh_grp_key;52 } lpm_info; /* hold prepared lpm info */53 /* Indicate if route is not overlapped by another table */54 struct rhash_head ht_node; /* node of prestera_router */55 struct prestera_kern_neigh_cache_head {56 struct prestera_kern_fib_cache *this;57 struct list_head head;58 struct prestera_kern_neigh_cache *n_cache;59 } kern_neigh_cache_head[PRESTERA_NHGR_SIZE_MAX];60 union {61 struct fib_notifier_info info; /* point to any of 4/6 */62 struct fib_entry_notifier_info fen4_info;63 };64 bool reachable;65};66 67static const struct rhashtable_params __prestera_kern_neigh_cache_ht_params = {68 .key_offset = offsetof(struct prestera_kern_neigh_cache, key),69 .head_offset = offsetof(struct prestera_kern_neigh_cache, ht_node),70 .key_len = sizeof(struct prestera_kern_neigh_cache_key),71 .automatic_shrinking = true,72};73 74static const struct rhashtable_params __prestera_kern_fib_cache_ht_params = {75 .key_offset = offsetof(struct prestera_kern_fib_cache, key),76 .head_offset = offsetof(struct prestera_kern_fib_cache, ht_node),77 .key_len = sizeof(struct prestera_kern_fib_cache_key),78 .automatic_shrinking = true,79};80 81/* This util to be used, to convert kernel rules for default vr in hw_vr */82static u32 prestera_fix_tb_id(u32 tb_id)83{84 if (tb_id == RT_TABLE_UNSPEC ||85 tb_id == RT_TABLE_LOCAL ||86 tb_id == RT_TABLE_DEFAULT)87 tb_id = RT_TABLE_MAIN;88 89 return tb_id;90}91 92static void93prestera_util_fen_info2fib_cache_key(struct fib_notifier_info *info,94 struct prestera_kern_fib_cache_key *key)95{96 struct fib_entry_notifier_info *fen_info =97 container_of(info, struct fib_entry_notifier_info, info);98 99 memset(key, 0, sizeof(*key));100 key->addr.v = PRESTERA_IPV4;101 key->addr.u.ipv4 = cpu_to_be32(fen_info->dst);102 key->prefix_len = fen_info->dst_len;103 key->kern_tb_id = fen_info->tb_id;104}105 106static int prestera_util_nhc2nc_key(struct prestera_switch *sw,107 struct fib_nh_common *nhc,108 struct prestera_kern_neigh_cache_key *nk)109{110 memset(nk, 0, sizeof(*nk));111 if (nhc->nhc_gw_family == AF_INET) {112 nk->addr.v = PRESTERA_IPV4;113 nk->addr.u.ipv4 = nhc->nhc_gw.ipv4;114 } else {115 nk->addr.v = PRESTERA_IPV6;116 nk->addr.u.ipv6 = nhc->nhc_gw.ipv6;117 }118 119 nk->dev = nhc->nhc_dev;120 return 0;121}122 123static void124prestera_util_nc_key2nh_key(struct prestera_kern_neigh_cache_key *ck,125 struct prestera_nh_neigh_key *nk)126{127 memset(nk, 0, sizeof(*nk));128 nk->addr = ck->addr;129 nk->rif = (void *)ck->dev;130}131 132static bool133prestera_util_nhc_eq_n_cache_key(struct prestera_switch *sw,134 struct fib_nh_common *nhc,135 struct prestera_kern_neigh_cache_key *nk)136{137 struct prestera_kern_neigh_cache_key tk;138 int err;139 140 err = prestera_util_nhc2nc_key(sw, nhc, &tk);141 if (err)142 return false;143 144 if (memcmp(&tk, nk, sizeof(tk)))145 return false;146 147 return true;148}149 150static int151prestera_util_neigh2nc_key(struct prestera_switch *sw, struct neighbour *n,152 struct prestera_kern_neigh_cache_key *key)153{154 memset(key, 0, sizeof(*key));155 if (n->tbl->family == AF_INET) {156 key->addr.v = PRESTERA_IPV4;157 key->addr.u.ipv4 = *(__be32 *)n->primary_key;158 } else {159 return -ENOENT;160 }161 162 key->dev = n->dev;163 164 return 0;165}166 167static bool __prestera_fi_is_direct(struct fib_info *fi)168{169 struct fib_nh_common *fib_nhc;170 171 if (fib_info_num_path(fi) == 1) {172 fib_nhc = fib_info_nhc(fi, 0);173 if (fib_nhc->nhc_gw_family == AF_UNSPEC)174 return true;175 }176 177 return false;178}179 180static bool prestera_fi_is_direct(struct fib_info *fi)181{182 if (fi->fib_type != RTN_UNICAST)183 return false;184 185 return __prestera_fi_is_direct(fi);186}187 188static bool prestera_fi_is_nh(struct fib_info *fi)189{190 if (fi->fib_type != RTN_UNICAST)191 return false;192 193 return !__prestera_fi_is_direct(fi);194}195 196static bool __prestera_fi6_is_direct(struct fib6_info *fi)197{198 if (!fi->fib6_nh->nh_common.nhc_gw_family)199 return true;200 201 return false;202}203 204static bool prestera_fi6_is_direct(struct fib6_info *fi)205{206 if (fi->fib6_type != RTN_UNICAST)207 return false;208 209 return __prestera_fi6_is_direct(fi);210}211 212static bool prestera_fi6_is_nh(struct fib6_info *fi)213{214 if (fi->fib6_type != RTN_UNICAST)215 return false;216 217 return !__prestera_fi6_is_direct(fi);218}219 220static bool prestera_fib_info_is_direct(struct fib_notifier_info *info)221{222 struct fib6_entry_notifier_info *fen6_info =223 container_of(info, struct fib6_entry_notifier_info, info);224 struct fib_entry_notifier_info *fen_info =225 container_of(info, struct fib_entry_notifier_info, info);226 227 if (info->family == AF_INET)228 return prestera_fi_is_direct(fen_info->fi);229 else230 return prestera_fi6_is_direct(fen6_info->rt);231}232 233static bool prestera_fib_info_is_nh(struct fib_notifier_info *info)234{235 struct fib6_entry_notifier_info *fen6_info =236 container_of(info, struct fib6_entry_notifier_info, info);237 struct fib_entry_notifier_info *fen_info =238 container_of(info, struct fib_entry_notifier_info, info);239 240 if (info->family == AF_INET)241 return prestera_fi_is_nh(fen_info->fi);242 else243 return prestera_fi6_is_nh(fen6_info->rt);244}245 246/* must be called with rcu_read_lock() */247static int prestera_util_kern_get_route(struct fib_result *res, u32 tb_id,248 __be32 *addr)249{250 struct flowi4 fl4;251 252 /* TODO: walkthrough appropriate tables in kernel253 * to know if the same prefix exists in several tables254 */255 memset(&fl4, 0, sizeof(fl4));256 fl4.daddr = *addr;257 return fib_lookup(&init_net, &fl4, res, 0 /* FIB_LOOKUP_NOREF */);258}259 260static bool261__prestera_util_kern_n_is_reachable_v4(u32 tb_id, __be32 *addr,262 struct net_device *dev)263{264 struct fib_nh_common *fib_nhc;265 struct fib_result res;266 bool reachable;267 268 reachable = false;269 270 if (!prestera_util_kern_get_route(&res, tb_id, addr))271 if (prestera_fi_is_direct(res.fi)) {272 fib_nhc = fib_info_nhc(res.fi, 0);273 if (dev == fib_nhc->nhc_dev)274 reachable = true;275 }276 277 return reachable;278}279 280/* Check if neigh route is reachable */281static bool282prestera_util_kern_n_is_reachable(u32 tb_id,283 struct prestera_ip_addr *addr,284 struct net_device *dev)285{286 if (addr->v == PRESTERA_IPV4)287 return __prestera_util_kern_n_is_reachable_v4(tb_id,288 &addr->u.ipv4,289 dev);290 else291 return false;292}293 294static void prestera_util_kern_set_neigh_offload(struct neighbour *n,295 bool offloaded)296{297 if (offloaded)298 n->flags |= NTF_OFFLOADED;299 else300 n->flags &= ~NTF_OFFLOADED;301}302 303static void304prestera_util_kern_set_nh_offload(struct fib_nh_common *nhc, bool offloaded, bool trap)305{306 if (offloaded)307 nhc->nhc_flags |= RTNH_F_OFFLOAD;308 else309 nhc->nhc_flags &= ~RTNH_F_OFFLOAD;310 311 if (trap)312 nhc->nhc_flags |= RTNH_F_TRAP;313 else314 nhc->nhc_flags &= ~RTNH_F_TRAP;315}316 317static struct fib_nh_common *318prestera_kern_fib_info_nhc(struct fib_notifier_info *info, int n)319{320 struct fib6_entry_notifier_info *fen6_info;321 struct fib_entry_notifier_info *fen4_info;322 struct fib6_info *iter;323 324 if (info->family == AF_INET) {325 fen4_info = container_of(info, struct fib_entry_notifier_info,326 info);327 return fib_info_nhc(fen4_info->fi, n);328 } else if (info->family == AF_INET6) {329 fen6_info = container_of(info, struct fib6_entry_notifier_info,330 info);331 if (!n)332 return &fen6_info->rt->fib6_nh->nh_common;333 334 list_for_each_entry(iter, &fen6_info->rt->fib6_siblings,335 fib6_siblings) {336 if (!--n)337 return &iter->fib6_nh->nh_common;338 }339 }340 341 /* if family is incorrect - than upper functions has BUG */342 /* if doesn't find requested index - there is alsi bug, because343 * valid index must be produced by nhs, which checks list length344 */345 WARN(1, "Invalid parameters passed to %s n=%d i=%p",346 __func__, n, info);347 return NULL;348}349 350static int prestera_kern_fib_info_nhs(struct fib_notifier_info *info)351{352 struct fib6_entry_notifier_info *fen6_info;353 struct fib_entry_notifier_info *fen4_info;354 355 if (info->family == AF_INET) {356 fen4_info = container_of(info, struct fib_entry_notifier_info,357 info);358 return fib_info_num_path(fen4_info->fi);359 } else if (info->family == AF_INET6) {360 fen6_info = container_of(info, struct fib6_entry_notifier_info,361 info);362 return fen6_info->rt->fib6_nsiblings + 1;363 }364 365 return 0;366}367 368static unsigned char369prestera_kern_fib_info_type(struct fib_notifier_info *info)370{371 struct fib6_entry_notifier_info *fen6_info;372 struct fib_entry_notifier_info *fen4_info;373 374 if (info->family == AF_INET) {375 fen4_info = container_of(info, struct fib_entry_notifier_info,376 info);377 return fen4_info->fi->fib_type;378 } else if (info->family == AF_INET6) {379 fen6_info = container_of(info, struct fib6_entry_notifier_info,380 info);381 /* TODO: ECMP in ipv6 is several routes.382 * Every route has single nh.383 */384 return fen6_info->rt->fib6_type;385 }386 387 return RTN_UNSPEC;388}389 390/* Decided, that uc_nh route with key==nh is obviously neighbour route */391static bool392prestera_fib_node_util_is_neighbour(struct prestera_fib_node *fib_node)393{394 if (fib_node->info.type != PRESTERA_FIB_TYPE_UC_NH)395 return false;396 397 if (fib_node->info.nh_grp->nh_neigh_head[1].neigh)398 return false;399 400 if (!fib_node->info.nh_grp->nh_neigh_head[0].neigh)401 return false;402 403 if (memcmp(&fib_node->info.nh_grp->nh_neigh_head[0].neigh->key.addr,404 &fib_node->key.addr, sizeof(struct prestera_ip_addr)))405 return false;406 407 return true;408}409 410static int prestera_dev_if_type(const struct net_device *dev)411{412 struct macvlan_dev *vlan;413 414 if (is_vlan_dev(dev) &&415 netif_is_bridge_master(vlan_dev_real_dev(dev))) {416 return PRESTERA_IF_VID_E;417 } else if (netif_is_bridge_master(dev)) {418 return PRESTERA_IF_VID_E;419 } else if (netif_is_lag_master(dev)) {420 return PRESTERA_IF_LAG_E;421 } else if (netif_is_macvlan(dev)) {422 vlan = netdev_priv(dev);423 return prestera_dev_if_type(vlan->lowerdev);424 } else {425 return PRESTERA_IF_PORT_E;426 }427}428 429static int430prestera_neigh_iface_init(struct prestera_switch *sw,431 struct prestera_iface *iface,432 struct neighbour *n)433{434 struct prestera_port *port;435 436 iface->vlan_id = 0; /* TODO: vlan egress */437 iface->type = prestera_dev_if_type(n->dev);438 if (iface->type != PRESTERA_IF_PORT_E)439 return -EINVAL;440 441 if (!prestera_netdev_check(n->dev))442 return -EINVAL;443 444 port = netdev_priv(n->dev);445 iface->dev_port.hw_dev_num = port->dev_id;446 iface->dev_port.port_num = port->hw_id;447 448 return 0;449}450 451static struct prestera_kern_neigh_cache *452prestera_kern_neigh_cache_find(struct prestera_switch *sw,453 struct prestera_kern_neigh_cache_key *key)454{455 struct prestera_kern_neigh_cache *n_cache;456 457 n_cache =458 rhashtable_lookup_fast(&sw->router->kern_neigh_cache_ht, key,459 __prestera_kern_neigh_cache_ht_params);460 return n_cache;461}462 463static void464__prestera_kern_neigh_cache_destruct(struct prestera_switch *sw,465 struct prestera_kern_neigh_cache *n_cache)466{467 dev_put(n_cache->key.dev);468}469 470static void471__prestera_kern_neigh_cache_destroy(struct prestera_switch *sw,472 struct prestera_kern_neigh_cache *n_cache)473{474 rhashtable_remove_fast(&sw->router->kern_neigh_cache_ht,475 &n_cache->ht_node,476 __prestera_kern_neigh_cache_ht_params);477 __prestera_kern_neigh_cache_destruct(sw, n_cache);478 kfree(n_cache);479}480 481static struct prestera_kern_neigh_cache *482__prestera_kern_neigh_cache_create(struct prestera_switch *sw,483 struct prestera_kern_neigh_cache_key *key)484{485 struct prestera_kern_neigh_cache *n_cache;486 int err;487 488 n_cache = kzalloc(sizeof(*n_cache), GFP_KERNEL);489 if (!n_cache)490 goto err_kzalloc;491 492 memcpy(&n_cache->key, key, sizeof(*key));493 dev_hold(n_cache->key.dev);494 495 INIT_LIST_HEAD(&n_cache->kern_fib_cache_list);496 err = rhashtable_insert_fast(&sw->router->kern_neigh_cache_ht,497 &n_cache->ht_node,498 __prestera_kern_neigh_cache_ht_params);499 if (err)500 goto err_ht_insert;501 502 return n_cache;503 504err_ht_insert:505 dev_put(n_cache->key.dev);506 kfree(n_cache);507err_kzalloc:508 return NULL;509}510 511static struct prestera_kern_neigh_cache *512prestera_kern_neigh_cache_get(struct prestera_switch *sw,513 struct prestera_kern_neigh_cache_key *key)514{515 struct prestera_kern_neigh_cache *n_cache;516 517 n_cache = prestera_kern_neigh_cache_find(sw, key);518 if (!n_cache)519 n_cache = __prestera_kern_neigh_cache_create(sw, key);520 521 return n_cache;522}523 524static struct prestera_kern_neigh_cache *525prestera_kern_neigh_cache_put(struct prestera_switch *sw,526 struct prestera_kern_neigh_cache *n_cache)527{528 if (!n_cache->in_kernel &&529 list_empty(&n_cache->kern_fib_cache_list)) {530 __prestera_kern_neigh_cache_destroy(sw, n_cache);531 return NULL;532 }533 534 return n_cache;535}536 537static struct prestera_kern_fib_cache *538prestera_kern_fib_cache_find(struct prestera_switch *sw,539 struct prestera_kern_fib_cache_key *key)540{541 struct prestera_kern_fib_cache *fib_cache;542 543 fib_cache =544 rhashtable_lookup_fast(&sw->router->kern_fib_cache_ht, key,545 __prestera_kern_fib_cache_ht_params);546 return fib_cache;547}548 549static void550__prestera_kern_fib_cache_destruct(struct prestera_switch *sw,551 struct prestera_kern_fib_cache *fib_cache)552{553 struct prestera_kern_neigh_cache *n_cache;554 int i;555 556 for (i = 0; i < PRESTERA_NHGR_SIZE_MAX; i++) {557 n_cache = fib_cache->kern_neigh_cache_head[i].n_cache;558 if (n_cache) {559 list_del(&fib_cache->kern_neigh_cache_head[i].head);560 prestera_kern_neigh_cache_put(sw, n_cache);561 }562 }563 564 fib_info_put(fib_cache->fen4_info.fi);565}566 567static void568prestera_kern_fib_cache_destroy(struct prestera_switch *sw,569 struct prestera_kern_fib_cache *fib_cache)570{571 rhashtable_remove_fast(&sw->router->kern_fib_cache_ht,572 &fib_cache->ht_node,573 __prestera_kern_fib_cache_ht_params);574 __prestera_kern_fib_cache_destruct(sw, fib_cache);575 kfree(fib_cache);576}577 578static int579__prestera_kern_fib_cache_create_nhs(struct prestera_switch *sw,580 struct prestera_kern_fib_cache *fc)581{582 struct prestera_kern_neigh_cache_key nc_key;583 struct prestera_kern_neigh_cache *n_cache;584 struct fib_nh_common *nhc;585 int i, nhs, err;586 587 if (!prestera_fib_info_is_nh(&fc->info))588 return 0;589 590 nhs = prestera_kern_fib_info_nhs(&fc->info);591 if (nhs > PRESTERA_NHGR_SIZE_MAX)592 return 0;593 594 for (i = 0; i < nhs; i++) {595 nhc = prestera_kern_fib_info_nhc(&fc->fen4_info.info, i);596 err = prestera_util_nhc2nc_key(sw, nhc, &nc_key);597 if (err)598 return 0;599 600 n_cache = prestera_kern_neigh_cache_get(sw, &nc_key);601 if (!n_cache)602 return 0;603 604 fc->kern_neigh_cache_head[i].this = fc;605 fc->kern_neigh_cache_head[i].n_cache = n_cache;606 list_add(&fc->kern_neigh_cache_head[i].head,607 &n_cache->kern_fib_cache_list);608 }609 610 return 0;611}612 613/* Operations on fi (offload, etc) must be wrapped in utils.614 * This function just create storage.615 */616static struct prestera_kern_fib_cache *617prestera_kern_fib_cache_create(struct prestera_switch *sw,618 struct prestera_kern_fib_cache_key *key,619 struct fib_notifier_info *info)620{621 struct fib_entry_notifier_info *fen_info =622 container_of(info, struct fib_entry_notifier_info, info);623 struct prestera_kern_fib_cache *fib_cache;624 int err;625 626 fib_cache = kzalloc(sizeof(*fib_cache), GFP_KERNEL);627 if (!fib_cache)628 goto err_kzalloc;629 630 memcpy(&fib_cache->key, key, sizeof(*key));631 fib_info_hold(fen_info->fi);632 memcpy(&fib_cache->fen4_info, fen_info, sizeof(*fen_info));633 634 err = rhashtable_insert_fast(&sw->router->kern_fib_cache_ht,635 &fib_cache->ht_node,636 __prestera_kern_fib_cache_ht_params);637 if (err)638 goto err_ht_insert;639 640 /* Handle nexthops */641 err = __prestera_kern_fib_cache_create_nhs(sw, fib_cache);642 if (err)643 goto out; /* Not critical */644 645out:646 return fib_cache;647 648err_ht_insert:649 fib_info_put(fen_info->fi);650 kfree(fib_cache);651err_kzalloc:652 return NULL;653}654 655static void656__prestera_k_arb_fib_nh_offload_set(struct prestera_switch *sw,657 struct prestera_kern_fib_cache *fibc,658 struct prestera_kern_neigh_cache *nc,659 bool offloaded, bool trap)660{661 struct fib_nh_common *nhc;662 int i, nhs;663 664 nhs = prestera_kern_fib_info_nhs(&fibc->info);665 for (i = 0; i < nhs; i++) {666 nhc = prestera_kern_fib_info_nhc(&fibc->info, i);667 if (!nc) {668 prestera_util_kern_set_nh_offload(nhc, offloaded, trap);669 continue;670 }671 672 if (prestera_util_nhc_eq_n_cache_key(sw, nhc, &nc->key)) {673 prestera_util_kern_set_nh_offload(nhc, offloaded, trap);674 break;675 }676 }677}678 679static void680__prestera_k_arb_n_offload_set(struct prestera_switch *sw,681 struct prestera_kern_neigh_cache *nc,682 bool offloaded)683{684 struct neighbour *n;685 686 n = neigh_lookup(&arp_tbl, &nc->key.addr.u.ipv4,687 nc->key.dev);688 if (!n)689 return;690 691 prestera_util_kern_set_neigh_offload(n, offloaded);692 neigh_release(n);693}694 695static void696__prestera_k_arb_fib_lpm_offload_set(struct prestera_switch *sw,697 struct prestera_kern_fib_cache *fc,698 bool fail, bool offload, bool trap)699{700 struct fib_rt_info fri;701 702 switch (fc->key.addr.v) {703 case PRESTERA_IPV4:704 fri.fi = fc->fen4_info.fi;705 fri.tb_id = fc->key.kern_tb_id;706 fri.dst = fc->key.addr.u.ipv4;707 fri.dst_len = fc->key.prefix_len;708 fri.dscp = fc->fen4_info.dscp;709 fri.type = fc->fen4_info.type;710 /* flags begin */711 fri.offload = offload;712 fri.trap = trap;713 fri.offload_failed = fail;714 /* flags end */715 fib_alias_hw_flags_set(&init_net, &fri);716 return;717 case PRESTERA_IPV6:718 /* TODO */719 return;720 }721}722 723static void724__prestera_k_arb_n_lpm_set(struct prestera_switch *sw,725 struct prestera_kern_neigh_cache *n_cache,726 bool enabled)727{728 struct prestera_nexthop_group_key nh_grp_key;729 struct prestera_kern_fib_cache_key fc_key;730 struct prestera_kern_fib_cache *fib_cache;731 struct prestera_fib_node *fib_node;732 struct prestera_fib_key fib_key;733 734 /* Exception for fc with prefix 32: LPM entry is already used by fib */735 memset(&fc_key, 0, sizeof(fc_key));736 fc_key.addr = n_cache->key.addr;737 fc_key.prefix_len = PRESTERA_IP_ADDR_PLEN(n_cache->key.addr.v);738 /* But better to use tb_id of route, which pointed to this neighbour. */739 /* We take it from rif, because rif inconsistent.740 * Must be separated in_rif and out_rif.741 * Also note: for each fib pointed to this neigh should be separated742 * neigh lpm entry (for each ingress vr)743 */744 fc_key.kern_tb_id = l3mdev_fib_table(n_cache->key.dev);745 fib_cache = prestera_kern_fib_cache_find(sw, &fc_key);746 memset(&fib_key, 0, sizeof(fib_key));747 fib_key.addr = n_cache->key.addr;748 fib_key.prefix_len = PRESTERA_IP_ADDR_PLEN(n_cache->key.addr.v);749 fib_key.tb_id = prestera_fix_tb_id(fc_key.kern_tb_id);750 fib_node = prestera_fib_node_find(sw, &fib_key);751 if (!fib_cache || !fib_cache->reachable) {752 if (!enabled && fib_node) {753 if (prestera_fib_node_util_is_neighbour(fib_node))754 prestera_fib_node_destroy(sw, fib_node);755 return;756 }757 }758 759 if (enabled && !fib_node) {760 memset(&nh_grp_key, 0, sizeof(nh_grp_key));761 prestera_util_nc_key2nh_key(&n_cache->key,762 &nh_grp_key.neigh[0]);763 fib_node = prestera_fib_node_create(sw, &fib_key,764 PRESTERA_FIB_TYPE_UC_NH,765 &nh_grp_key);766 if (!fib_node)767 pr_err("%s failed ip=%pI4n", "prestera_fib_node_create",768 &fib_key.addr.u.ipv4);769 return;770 }771}772 773static void774__prestera_k_arb_nc_kern_fib_fetch(struct prestera_switch *sw,775 struct prestera_kern_neigh_cache *nc)776{777 if (prestera_util_kern_n_is_reachable(l3mdev_fib_table(nc->key.dev),778 &nc->key.addr, nc->key.dev))779 nc->reachable = true;780 else781 nc->reachable = false;782}783 784/* Kernel neighbour -> neigh_cache info */785static void786__prestera_k_arb_nc_kern_n_fetch(struct prestera_switch *sw,787 struct prestera_kern_neigh_cache *nc)788{789 struct neighbour *n;790 int err;791 792 memset(&nc->nh_neigh_info, 0, sizeof(nc->nh_neigh_info));793 n = neigh_lookup(&arp_tbl, &nc->key.addr.u.ipv4, nc->key.dev);794 if (!n)795 goto out;796 797 read_lock_bh(&n->lock);798 if (n->nud_state & NUD_VALID && !n->dead) {799 err = prestera_neigh_iface_init(sw, &nc->nh_neigh_info.iface,800 n);801 if (err)802 goto n_read_out;803 804 memcpy(&nc->nh_neigh_info.ha[0], &n->ha[0], ETH_ALEN);805 nc->nh_neigh_info.connected = true;806 }807n_read_out:808 read_unlock_bh(&n->lock);809out:810 nc->in_kernel = nc->nh_neigh_info.connected;811 if (n)812 neigh_release(n);813}814 815/* neigh_cache info -> lpm update */816static void817__prestera_k_arb_nc_apply(struct prestera_switch *sw,818 struct prestera_kern_neigh_cache *nc)819{820 struct prestera_kern_neigh_cache_head *nhead;821 struct prestera_nh_neigh_key nh_key;822 struct prestera_nh_neigh *nh_neigh;823 int err;824 825 __prestera_k_arb_n_lpm_set(sw, nc, nc->reachable && nc->in_kernel);826 __prestera_k_arb_n_offload_set(sw, nc, nc->reachable && nc->in_kernel);827 828 prestera_util_nc_key2nh_key(&nc->key, &nh_key);829 nh_neigh = prestera_nh_neigh_find(sw, &nh_key);830 if (!nh_neigh)831 goto out;832 833 /* Do hw update only if something changed to prevent nh flap */834 if (memcmp(&nc->nh_neigh_info, &nh_neigh->info,835 sizeof(nh_neigh->info))) {836 memcpy(&nh_neigh->info, &nc->nh_neigh_info,837 sizeof(nh_neigh->info));838 err = prestera_nh_neigh_set(sw, nh_neigh);839 if (err) {840 pr_err("%s failed with err=%d ip=%pI4n mac=%pM",841 "prestera_nh_neigh_set", err,842 &nh_neigh->key.addr.u.ipv4,843 &nh_neigh->info.ha[0]);844 goto out;845 }846 }847 848out:849 list_for_each_entry(nhead, &nc->kern_fib_cache_list, head) {850 __prestera_k_arb_fib_nh_offload_set(sw, nhead->this, nc,851 nc->in_kernel,852 !nc->in_kernel);853 }854}855 856static int857__prestera_pr_k_arb_fc_lpm_info_calc(struct prestera_switch *sw,858 struct prestera_kern_fib_cache *fc)859{860 struct fib_nh_common *nhc;861 int nh_cnt;862 863 memset(&fc->lpm_info, 0, sizeof(fc->lpm_info));864 865 switch (prestera_kern_fib_info_type(&fc->info)) {866 case RTN_UNICAST:867 if (prestera_fib_info_is_direct(&fc->info) &&868 fc->key.prefix_len ==869 PRESTERA_IP_ADDR_PLEN(fc->key.addr.v)) {870 /* This is special case.871 * When prefix is 32. Than we will have conflict in lpm872 * for direct route - once TRAP added, there is no873 * place for neighbour entry. So represent direct route874 * with prefix 32, as NH. So neighbour will be resolved875 * as nexthop of this route.876 */877 nhc = prestera_kern_fib_info_nhc(&fc->info, 0);878 fc->lpm_info.fib_type = PRESTERA_FIB_TYPE_UC_NH;879 fc->lpm_info.nh_grp_key.neigh[0].addr =880 fc->key.addr;881 fc->lpm_info.nh_grp_key.neigh[0].rif =882 nhc->nhc_dev;883 884 break;885 }886 887 /* We can also get nh_grp_key from fi. This will be correct to888 * because cache not always represent, what actually written to889 * lpm. But we use nh cache, as well for now (for this case).890 */891 for (nh_cnt = 0; nh_cnt < PRESTERA_NHGR_SIZE_MAX; nh_cnt++) {892 if (!fc->kern_neigh_cache_head[nh_cnt].n_cache)893 break;894 895 fc->lpm_info.nh_grp_key.neigh[nh_cnt].addr =896 fc->kern_neigh_cache_head[nh_cnt].n_cache->key.addr;897 fc->lpm_info.nh_grp_key.neigh[nh_cnt].rif =898 fc->kern_neigh_cache_head[nh_cnt].n_cache->key.dev;899 }900 901 fc->lpm_info.fib_type = nh_cnt ?902 PRESTERA_FIB_TYPE_UC_NH :903 PRESTERA_FIB_TYPE_TRAP;904 break;905 /* Unsupported. Leave it for kernel: */906 case RTN_BROADCAST:907 case RTN_MULTICAST:908 /* Routes we must trap by design: */909 case RTN_LOCAL:910 case RTN_UNREACHABLE:911 case RTN_PROHIBIT:912 fc->lpm_info.fib_type = PRESTERA_FIB_TYPE_TRAP;913 break;914 case RTN_BLACKHOLE:915 fc->lpm_info.fib_type = PRESTERA_FIB_TYPE_DROP;916 break;917 default:918 dev_err(sw->dev->dev, "Unsupported fib_type");919 return -EOPNOTSUPP;920 }921 922 fc->lpm_info.fib_key.addr = fc->key.addr;923 fc->lpm_info.fib_key.prefix_len = fc->key.prefix_len;924 fc->lpm_info.fib_key.tb_id = prestera_fix_tb_id(fc->key.kern_tb_id);925 926 return 0;927}928 929static int __prestera_k_arb_f_lpm_set(struct prestera_switch *sw,930 struct prestera_kern_fib_cache *fc,931 bool enabled)932{933 struct prestera_fib_node *fib_node;934 935 fib_node = prestera_fib_node_find(sw, &fc->lpm_info.fib_key);936 if (fib_node)937 prestera_fib_node_destroy(sw, fib_node);938 939 if (!enabled)940 return 0;941 942 fib_node = prestera_fib_node_create(sw, &fc->lpm_info.fib_key,943 fc->lpm_info.fib_type,944 &fc->lpm_info.nh_grp_key);945 946 if (!fib_node) {947 dev_err(sw->dev->dev, "fib_node=NULL %pI4n/%d kern_tb_id = %d",948 &fc->key.addr.u.ipv4, fc->key.prefix_len,949 fc->key.kern_tb_id);950 return -ENOENT;951 }952 953 return 0;954}955 956static int __prestera_k_arb_fc_apply(struct prestera_switch *sw,957 struct prestera_kern_fib_cache *fc)958{959 int err;960 961 err = __prestera_pr_k_arb_fc_lpm_info_calc(sw, fc);962 if (err)963 return err;964 965 err = __prestera_k_arb_f_lpm_set(sw, fc, fc->reachable);966 if (err) {967 __prestera_k_arb_fib_lpm_offload_set(sw, fc,968 true, false, false);969 return err;970 }971 972 switch (fc->lpm_info.fib_type) {973 case PRESTERA_FIB_TYPE_UC_NH:974 __prestera_k_arb_fib_lpm_offload_set(sw, fc, false,975 fc->reachable, false);976 break;977 case PRESTERA_FIB_TYPE_TRAP:978 __prestera_k_arb_fib_lpm_offload_set(sw, fc, false,979 false, fc->reachable);980 break;981 case PRESTERA_FIB_TYPE_DROP:982 __prestera_k_arb_fib_lpm_offload_set(sw, fc, false, true,983 fc->reachable);984 break;985 case PRESTERA_FIB_TYPE_INVALID:986 break;987 }988 989 return 0;990}991 992static struct prestera_kern_fib_cache *993__prestera_k_arb_util_fib_overlaps(struct prestera_switch *sw,994 struct prestera_kern_fib_cache *fc)995{996 struct prestera_kern_fib_cache_key fc_key;997 struct prestera_kern_fib_cache *rfc;998 999 /* TODO: parse kernel rules */1000 rfc = NULL;1001 if (fc->key.kern_tb_id == RT_TABLE_LOCAL) {1002 memcpy(&fc_key, &fc->key, sizeof(fc_key));1003 fc_key.kern_tb_id = RT_TABLE_MAIN;1004 rfc = prestera_kern_fib_cache_find(sw, &fc_key);1005 }1006 1007 return rfc;1008}1009 1010static struct prestera_kern_fib_cache *1011__prestera_k_arb_util_fib_overlapped(struct prestera_switch *sw,1012 struct prestera_kern_fib_cache *fc)1013{1014 struct prestera_kern_fib_cache_key fc_key;1015 struct prestera_kern_fib_cache *rfc;1016 1017 /* TODO: parse kernel rules */1018 rfc = NULL;1019 if (fc->key.kern_tb_id == RT_TABLE_MAIN) {1020 memcpy(&fc_key, &fc->key, sizeof(fc_key));1021 fc_key.kern_tb_id = RT_TABLE_LOCAL;1022 rfc = prestera_kern_fib_cache_find(sw, &fc_key);1023 }1024 1025 return rfc;1026}1027 1028static void __prestera_k_arb_hw_state_upd(struct prestera_switch *sw,1029 struct prestera_kern_neigh_cache *nc)1030{1031 struct prestera_nh_neigh_key nh_key;1032 struct prestera_nh_neigh *nh_neigh;1033 struct neighbour *n;1034 bool hw_active;1035 1036 prestera_util_nc_key2nh_key(&nc->key, &nh_key);1037 nh_neigh = prestera_nh_neigh_find(sw, &nh_key);1038 if (!nh_neigh) {1039 pr_err("Cannot find nh_neigh for cached %pI4n",1040 &nc->key.addr.u.ipv4);1041 return;1042 }1043 1044 hw_active = prestera_nh_neigh_util_hw_state(sw, nh_neigh);1045 1046#ifdef PRESTERA_IMPLICITY_RESOLVE_DEAD_NEIGH1047 if (!hw_active && nc->in_kernel)1048 goto out;1049#else /* PRESTERA_IMPLICITY_RESOLVE_DEAD_NEIGH */1050 if (!hw_active)1051 goto out;1052#endif /* PRESTERA_IMPLICITY_RESOLVE_DEAD_NEIGH */1053 1054 if (nc->key.addr.v == PRESTERA_IPV4) {1055 n = neigh_lookup(&arp_tbl, &nc->key.addr.u.ipv4,1056 nc->key.dev);1057 if (!n)1058 n = neigh_create(&arp_tbl, &nc->key.addr.u.ipv4,1059 nc->key.dev);1060 } else {1061 n = NULL;1062 }1063 1064 if (!IS_ERR(n) && n) {1065 neigh_event_send(n, NULL);1066 neigh_release(n);1067 } else {1068 pr_err("Cannot create neighbour %pI4n", &nc->key.addr.u.ipv4);1069 }1070 1071out:1072 return;1073}1074 1075/* Propagate hw state to kernel */1076static void prestera_k_arb_hw_evt(struct prestera_switch *sw)1077{1078 struct prestera_kern_neigh_cache *n_cache;1079 struct rhashtable_iter iter;1080 1081 rhashtable_walk_enter(&sw->router->kern_neigh_cache_ht, &iter);1082 rhashtable_walk_start(&iter);1083 while (1) {1084 n_cache = rhashtable_walk_next(&iter);1085 1086 if (!n_cache)1087 break;1088 1089 if (IS_ERR(n_cache))1090 continue;1091 1092 rhashtable_walk_stop(&iter);1093 __prestera_k_arb_hw_state_upd(sw, n_cache);1094 rhashtable_walk_start(&iter);1095 }1096 rhashtable_walk_stop(&iter);1097 rhashtable_walk_exit(&iter);1098}1099 1100/* Propagate kernel event to hw */1101static void prestera_k_arb_n_evt(struct prestera_switch *sw,1102 struct neighbour *n)1103{1104 struct prestera_kern_neigh_cache_key n_key;1105 struct prestera_kern_neigh_cache *n_cache;1106 int err;1107 1108 err = prestera_util_neigh2nc_key(sw, n, &n_key);1109 if (err)1110 return;1111 1112 n_cache = prestera_kern_neigh_cache_find(sw, &n_key);1113 if (!n_cache) {1114 n_cache = prestera_kern_neigh_cache_get(sw, &n_key);1115 if (!n_cache)1116 return;1117 __prestera_k_arb_nc_kern_fib_fetch(sw, n_cache);1118 }1119 1120 __prestera_k_arb_nc_kern_n_fetch(sw, n_cache);1121 __prestera_k_arb_nc_apply(sw, n_cache);1122 1123 prestera_kern_neigh_cache_put(sw, n_cache);1124}1125 1126static void __prestera_k_arb_fib_evt2nc(struct prestera_switch *sw)1127{1128 struct prestera_kern_neigh_cache *n_cache;1129 struct rhashtable_iter iter;1130 1131 rhashtable_walk_enter(&sw->router->kern_neigh_cache_ht, &iter);1132 rhashtable_walk_start(&iter);1133 while (1) {1134 n_cache = rhashtable_walk_next(&iter);1135 1136 if (!n_cache)1137 break;1138 1139 if (IS_ERR(n_cache))1140 continue;1141 1142 rhashtable_walk_stop(&iter);1143 __prestera_k_arb_nc_kern_fib_fetch(sw, n_cache);1144 __prestera_k_arb_nc_apply(sw, n_cache);1145 rhashtable_walk_start(&iter);1146 }1147 rhashtable_walk_stop(&iter);1148 rhashtable_walk_exit(&iter);1149}1150 1151static int1152prestera_k_arb_fib_evt(struct prestera_switch *sw,1153 bool replace, /* replace or del */1154 struct fib_notifier_info *info)1155{1156 struct prestera_kern_fib_cache *tfib_cache, *bfib_cache; /* top/btm */1157 struct prestera_kern_fib_cache_key fc_key;1158 struct prestera_kern_fib_cache *fib_cache;1159 int err;1160 1161 prestera_util_fen_info2fib_cache_key(info, &fc_key);1162 fib_cache = prestera_kern_fib_cache_find(sw, &fc_key);1163 if (fib_cache) {1164 fib_cache->reachable = false;1165 err = __prestera_k_arb_fc_apply(sw, fib_cache);1166 if (err)1167 dev_err(sw->dev->dev,1168 "Applying destroyed fib_cache failed");1169 1170 bfib_cache = __prestera_k_arb_util_fib_overlaps(sw, fib_cache);1171 tfib_cache = __prestera_k_arb_util_fib_overlapped(sw, fib_cache);1172 if (!tfib_cache && bfib_cache) {1173 bfib_cache->reachable = true;1174 err = __prestera_k_arb_fc_apply(sw, bfib_cache);1175 if (err)1176 dev_err(sw->dev->dev,1177 "Applying fib_cache btm failed");1178 }1179 1180 prestera_kern_fib_cache_destroy(sw, fib_cache);1181 }1182 1183 if (replace) {1184 fib_cache = prestera_kern_fib_cache_create(sw, &fc_key, info);1185 if (!fib_cache) {1186 dev_err(sw->dev->dev, "fib_cache == NULL");1187 return -ENOENT;1188 }1189 1190 bfib_cache = __prestera_k_arb_util_fib_overlaps(sw, fib_cache);1191 tfib_cache = __prestera_k_arb_util_fib_overlapped(sw, fib_cache);1192 if (!tfib_cache)1193 fib_cache->reachable = true;1194 1195 if (bfib_cache) {1196 bfib_cache->reachable = false;1197 err = __prestera_k_arb_fc_apply(sw, bfib_cache);1198 if (err)1199 dev_err(sw->dev->dev,1200 "Applying fib_cache btm failed");1201 }1202 1203 err = __prestera_k_arb_fc_apply(sw, fib_cache);1204 if (err)1205 dev_err(sw->dev->dev, "Applying fib_cache failed");1206 }1207 1208 /* Update all neighs to resolve overlapped and apply related */1209 __prestera_k_arb_fib_evt2nc(sw);1210 1211 return 0;1212}1213 1214static void __prestera_k_arb_abort_neigh_ht_cb(void *ptr, void *arg)1215{1216 struct prestera_kern_neigh_cache *n_cache = ptr;1217 struct prestera_switch *sw = arg;1218 1219 if (!list_empty(&n_cache->kern_fib_cache_list)) {1220 WARN_ON(1); /* BUG */1221 return;1222 }1223 __prestera_k_arb_n_offload_set(sw, n_cache, false);1224 n_cache->in_kernel = false;1225 /* No need to destroy lpm.1226 * It will be aborted by destroy_ht1227 */1228 __prestera_kern_neigh_cache_destruct(sw, n_cache);1229 kfree(n_cache);1230}1231 1232static void __prestera_k_arb_abort_fib_ht_cb(void *ptr, void *arg)1233{1234 struct prestera_kern_fib_cache *fib_cache = ptr;1235 struct prestera_switch *sw = arg;1236 1237 __prestera_k_arb_fib_lpm_offload_set(sw, fib_cache,1238 false, false,1239 false);1240 __prestera_k_arb_fib_nh_offload_set(sw, fib_cache, NULL,1241 false, false);1242 /* No need to destroy lpm.1243 * It will be aborted by destroy_ht1244 */1245 __prestera_kern_fib_cache_destruct(sw, fib_cache);1246 kfree(fib_cache);1247}1248 1249static void prestera_k_arb_abort(struct prestera_switch *sw)1250{1251 /* Function to remove all arbiter entries and related hw objects. */1252 /* Sequence:1253 * 1) Clear arbiter tables, but don't touch hw1254 * 2) Clear hw1255 * We use such approach, because arbiter object is not directly mapped1256 * to hw. So deletion of one arbiter object may even lead to creation of1257 * hw object (e.g. in case of overlapped routes).1258 */1259 rhashtable_free_and_destroy(&sw->router->kern_fib_cache_ht,1260 __prestera_k_arb_abort_fib_ht_cb,1261 sw);1262 rhashtable_free_and_destroy(&sw->router->kern_neigh_cache_ht,1263 __prestera_k_arb_abort_neigh_ht_cb,1264 sw);1265}1266 1267static int __prestera_inetaddr_port_event(struct net_device *port_dev,1268 unsigned long event,1269 struct netlink_ext_ack *extack)1270{1271 struct prestera_port *port = netdev_priv(port_dev);1272 struct prestera_rif_entry_key re_key = {};1273 struct prestera_rif_entry *re;1274 u32 kern_tb_id;1275 int err;1276 1277 err = prestera_is_valid_mac_addr(port, port_dev->dev_addr);1278 if (err) {1279 NL_SET_ERR_MSG_MOD(extack, "RIF MAC must have the same prefix");1280 return err;1281 }1282 1283 kern_tb_id = l3mdev_fib_table(port_dev);1284 re_key.iface.type = PRESTERA_IF_PORT_E;1285 re_key.iface.dev_port.hw_dev_num = port->dev_id;1286 re_key.iface.dev_port.port_num = port->hw_id;1287 re = prestera_rif_entry_find(port->sw, &re_key);1288 1289 switch (event) {1290 case NETDEV_UP:1291 if (re) {1292 NL_SET_ERR_MSG_MOD(extack, "RIF already exist");1293 return -EEXIST;1294 }1295 re = prestera_rif_entry_create(port->sw, &re_key,1296 prestera_fix_tb_id(kern_tb_id),1297 port_dev->dev_addr);1298 if (!re) {1299 NL_SET_ERR_MSG_MOD(extack, "Can't create RIF");1300 return -EINVAL;1301 }1302 dev_hold(port_dev);1303 break;1304 case NETDEV_DOWN:1305 if (!re) {1306 NL_SET_ERR_MSG_MOD(extack, "Can't find RIF");1307 return -EEXIST;1308 }1309 prestera_rif_entry_destroy(port->sw, re);1310 dev_put(port_dev);1311 break;1312 }1313 1314 return 0;1315}1316 1317static int __prestera_inetaddr_event(struct prestera_switch *sw,1318 struct net_device *dev,1319 unsigned long event,1320 struct netlink_ext_ack *extack)1321{1322 if (!prestera_netdev_check(dev) || netif_is_any_bridge_port(dev) ||1323 netif_is_lag_port(dev))1324 return 0;1325 1326 return __prestera_inetaddr_port_event(dev, event, extack);1327}1328 1329static int __prestera_inetaddr_cb(struct notifier_block *nb,1330 unsigned long event, void *ptr)1331{1332 struct in_ifaddr *ifa = (struct in_ifaddr *)ptr;1333 struct net_device *dev = ifa->ifa_dev->dev;1334 struct prestera_router *router = container_of(nb,1335 struct prestera_router,1336 inetaddr_nb);1337 struct in_device *idev;1338 int err = 0;1339 1340 if (event != NETDEV_DOWN)1341 goto out;1342 1343 /* Ignore if this is not latest address */1344 idev = __in_dev_get_rtnl(dev);1345 if (idev && idev->ifa_list)1346 goto out;1347 1348 err = __prestera_inetaddr_event(router->sw, dev, event, NULL);1349out:1350 return notifier_from_errno(err);1351}1352 1353static int __prestera_inetaddr_valid_cb(struct notifier_block *nb,1354 unsigned long event, void *ptr)1355{1356 struct in_validator_info *ivi = (struct in_validator_info *)ptr;1357 struct net_device *dev = ivi->ivi_dev->dev;1358 struct prestera_router *router = container_of(nb,1359 struct prestera_router,1360 inetaddr_valid_nb);1361 struct in_device *idev;1362 int err = 0;1363 1364 if (event != NETDEV_UP)1365 goto out;1366 1367 /* Ignore if this is not first address */1368 idev = __in_dev_get_rtnl(dev);1369 if (idev && idev->ifa_list)1370 goto out;1371 1372 if (ipv4_is_multicast(ivi->ivi_addr)) {1373 NL_SET_ERR_MSG_MOD(ivi->extack,1374 "Multicast addr on RIF is not supported");1375 err = -EINVAL;1376 goto out;1377 }1378 1379 err = __prestera_inetaddr_event(router->sw, dev, event, ivi->extack);1380out:1381 return notifier_from_errno(err);1382}1383 1384struct prestera_fib_event_work {1385 struct work_struct work;1386 struct prestera_switch *sw;1387 struct fib_entry_notifier_info fen_info;1388 unsigned long event;1389};1390 1391static void __prestera_router_fib_event_work(struct work_struct *work)1392{1393 struct prestera_fib_event_work *fib_work =1394 container_of(work, struct prestera_fib_event_work, work);1395 struct prestera_switch *sw = fib_work->sw;1396 int err;1397 1398 rtnl_lock();1399 1400 switch (fib_work->event) {1401 case FIB_EVENT_ENTRY_REPLACE:1402 err = prestera_k_arb_fib_evt(sw, true,1403 &fib_work->fen_info.info);1404 if (err)1405 goto err_out;1406 1407 break;1408 case FIB_EVENT_ENTRY_DEL:1409 err = prestera_k_arb_fib_evt(sw, false,1410 &fib_work->fen_info.info);1411 if (err)1412 goto err_out;1413 1414 break;1415 }1416 1417 goto out;1418 1419err_out:1420 dev_err(sw->dev->dev, "Error when processing %pI4h/%d",1421 &fib_work->fen_info.dst,1422 fib_work->fen_info.dst_len);1423out:1424 fib_info_put(fib_work->fen_info.fi);1425 rtnl_unlock();1426 kfree(fib_work);1427}1428 1429/* Called with rcu_read_lock() */1430static int __prestera_router_fib_event(struct notifier_block *nb,1431 unsigned long event, void *ptr)1432{1433 struct prestera_fib_event_work *fib_work;1434 struct fib_entry_notifier_info *fen_info;1435 struct fib_notifier_info *info = ptr;1436 struct prestera_router *router;1437 1438 if (info->family != AF_INET)1439 return NOTIFY_DONE;1440 1441 router = container_of(nb, struct prestera_router, fib_nb);1442 1443 switch (event) {1444 case FIB_EVENT_ENTRY_REPLACE:1445 case FIB_EVENT_ENTRY_DEL:1446 fen_info = container_of(info, struct fib_entry_notifier_info,1447 info);1448 if (!fen_info->fi)1449 return NOTIFY_DONE;1450 1451 fib_work = kzalloc(sizeof(*fib_work), GFP_ATOMIC);1452 if (WARN_ON(!fib_work))1453 return NOTIFY_BAD;1454 1455 fib_info_hold(fen_info->fi);1456 fib_work->fen_info = *fen_info;1457 fib_work->event = event;1458 fib_work->sw = router->sw;1459 INIT_WORK(&fib_work->work, __prestera_router_fib_event_work);1460 prestera_queue_work(&fib_work->work);1461 break;1462 default:1463 return NOTIFY_DONE;1464 }1465 1466 return NOTIFY_DONE;1467}1468 1469struct prestera_netevent_work {1470 struct work_struct work;1471 struct prestera_switch *sw;1472 struct neighbour *n;1473};1474 1475static void prestera_router_neigh_event_work(struct work_struct *work)1476{1477 struct prestera_netevent_work *net_work =1478 container_of(work, struct prestera_netevent_work, work);1479 struct prestera_switch *sw = net_work->sw;1480 struct neighbour *n = net_work->n;1481 1482 /* neigh - its not hw related object. It stored only in kernel. So... */1483 rtnl_lock();1484 1485 prestera_k_arb_n_evt(sw, n);1486 1487 neigh_release(n);1488 rtnl_unlock();1489 kfree(net_work);1490}1491 1492static int prestera_router_netevent_event(struct notifier_block *nb,1493 unsigned long event, void *ptr)1494{1495 struct prestera_netevent_work *net_work;1496 struct prestera_router *router;1497 struct neighbour *n = ptr;1498 1499 router = container_of(nb, struct prestera_router, netevent_nb);1500 1501 switch (event) {1502 case NETEVENT_NEIGH_UPDATE:1503 if (n->tbl->family != AF_INET)1504 return NOTIFY_DONE;1505 1506 net_work = kzalloc(sizeof(*net_work), GFP_ATOMIC);1507 if (WARN_ON(!net_work))1508 return NOTIFY_BAD;1509 1510 neigh_clone(n);1511 net_work->n = n;1512 net_work->sw = router->sw;1513 INIT_WORK(&net_work->work, prestera_router_neigh_event_work);1514 prestera_queue_work(&net_work->work);1515 }1516 1517 return NOTIFY_DONE;1518}1519 1520static void prestera_router_update_neighs_work(struct work_struct *work)1521{1522 struct prestera_router *router;1523 1524 router = container_of(work, struct prestera_router,1525 neighs_update.dw.work);1526 rtnl_lock();1527 1528 prestera_k_arb_hw_evt(router->sw);1529 1530 rtnl_unlock();1531 prestera_queue_delayed_work(&router->neighs_update.dw,1532 msecs_to_jiffies(PRESTERA_NH_PROBE_INTERVAL));1533}1534 1535static int prestera_neigh_work_init(struct prestera_switch *sw)1536{1537 INIT_DELAYED_WORK(&sw->router->neighs_update.dw,1538 prestera_router_update_neighs_work);1539 prestera_queue_delayed_work(&sw->router->neighs_update.dw, 0);1540 return 0;1541}1542 1543static void prestera_neigh_work_fini(struct prestera_switch *sw)1544{1545 cancel_delayed_work_sync(&sw->router->neighs_update.dw);1546}1547 1548int prestera_router_init(struct prestera_switch *sw)1549{1550 struct prestera_router *router;1551 int err, nhgrp_cache_bytes;1552 1553 router = kzalloc(sizeof(*sw->router), GFP_KERNEL);1554 if (!router)1555 return -ENOMEM;1556 1557 sw->router = router;1558 router->sw = sw;1559 1560 err = prestera_router_hw_init(sw);1561 if (err)1562 goto err_router_lib_init;1563 1564 err = rhashtable_init(&router->kern_fib_cache_ht,1565 &__prestera_kern_fib_cache_ht_params);1566 if (err)1567 goto err_kern_fib_cache_ht_init;1568 1569 err = rhashtable_init(&router->kern_neigh_cache_ht,1570 &__prestera_kern_neigh_cache_ht_params);1571 if (err)1572 goto err_kern_neigh_cache_ht_init;1573 1574 nhgrp_cache_bytes = sw->size_tbl_router_nexthop / 8 + 1;1575 router->nhgrp_hw_state_cache = kzalloc(nhgrp_cache_bytes, GFP_KERNEL);1576 if (!router->nhgrp_hw_state_cache) {1577 err = -ENOMEM;1578 goto err_nh_state_cache_alloc;1579 }1580 1581 err = prestera_neigh_work_init(sw);1582 if (err)1583 goto err_neigh_work_init;1584 1585 router->inetaddr_valid_nb.notifier_call = __prestera_inetaddr_valid_cb;1586 err = register_inetaddr_validator_notifier(&router->inetaddr_valid_nb);1587 if (err)1588 goto err_register_inetaddr_validator_notifier;1589 1590 router->inetaddr_nb.notifier_call = __prestera_inetaddr_cb;1591 err = register_inetaddr_notifier(&router->inetaddr_nb);1592 if (err)1593 goto err_register_inetaddr_notifier;1594 1595 router->netevent_nb.notifier_call = prestera_router_netevent_event;1596 err = register_netevent_notifier(&router->netevent_nb);1597 if (err)1598 goto err_register_netevent_notifier;1599 1600 router->fib_nb.notifier_call = __prestera_router_fib_event;1601 err = register_fib_notifier(&init_net, &router->fib_nb,1602 /* TODO: flush fib entries */ NULL, NULL);1603 if (err)1604 goto err_register_fib_notifier;1605 1606 return 0;1607 1608err_register_fib_notifier:1609 unregister_netevent_notifier(&router->netevent_nb);1610err_register_netevent_notifier:1611 unregister_inetaddr_notifier(&router->inetaddr_nb);1612err_register_inetaddr_notifier:1613 unregister_inetaddr_validator_notifier(&router->inetaddr_valid_nb);1614err_register_inetaddr_validator_notifier:1615 prestera_neigh_work_fini(sw);1616err_neigh_work_init:1617 kfree(router->nhgrp_hw_state_cache);1618err_nh_state_cache_alloc:1619 rhashtable_destroy(&router->kern_neigh_cache_ht);1620err_kern_neigh_cache_ht_init:1621 rhashtable_destroy(&router->kern_fib_cache_ht);1622err_kern_fib_cache_ht_init:1623 prestera_router_hw_fini(sw);1624err_router_lib_init:1625 kfree(sw->router);1626 return err;1627}1628 1629void prestera_router_fini(struct prestera_switch *sw)1630{1631 unregister_fib_notifier(&init_net, &sw->router->fib_nb);1632 unregister_netevent_notifier(&sw->router->netevent_nb);1633 unregister_inetaddr_notifier(&sw->router->inetaddr_nb);1634 unregister_inetaddr_validator_notifier(&sw->router->inetaddr_valid_nb);1635 prestera_neigh_work_fini(sw);1636 prestera_queue_drain();1637 1638 prestera_k_arb_abort(sw);1639 1640 kfree(sw->router->nhgrp_hw_state_cache);1641 rhashtable_destroy(&sw->router->kern_fib_cache_ht);1642 prestera_router_hw_fini(sw);1643 kfree(sw->router);1644 sw->router = NULL;1645}1646