Commit ae15ed6e authored by Yevhen Orlov's avatar Yevhen Orlov Committed by Jakub Kicinski

net: marvell: prestera: Propagate nh state from hw to kernel

We poll nexthops in HW and call for each active nexthop appropriate
neighbour.

Also we provide implicity neighbour resolving.
For example, user have added nexthop route:
  # ip route add 5.5.5.5 via 1.1.1.2
But neighbour 1.1.1.2 doesn't exist. In this case we will try to call
neigh_event_send, even if there is no traffic.
This is useful, when you have add route, which will be used after some
time but with a lot of traffic (burst). So, we has prepared, offloaded
route in advance.
Co-developed-by: default avatarTaras Chornyi <tchornyi@marvell.com>
Signed-off-by: default avatarTaras Chornyi <tchornyi@marvell.com>
Co-developed-by: default avatarOleksandr Mazur <oleksandr.mazur@plvision.eu>
Signed-off-by: default avatarOleksandr Mazur <oleksandr.mazur@plvision.eu>
Signed-off-by: default avatarYevhen Orlov <yevhen.orlov@plvision.eu>
Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent 396b80cb
......@@ -324,6 +324,9 @@ struct prestera_router {
struct notifier_block netevent_nb;
u8 *nhgrp_hw_state_cache; /* Bitmap cached hw state of nhs */
unsigned long nhgrp_hw_cache_kick; /* jiffies */
struct {
struct delayed_work dw;
} neighs_update;
};
struct prestera_rxtx_params {
......
......@@ -16,6 +16,9 @@
#include "prestera.h"
#include "prestera_router_hw.h"
#define PRESTERA_IMPLICITY_RESOLVE_DEAD_NEIGH
#define PRESTERA_NH_PROBE_INTERVAL 5000 /* ms */
struct prestera_kern_neigh_cache_key {
struct prestera_ip_addr addr;
struct net_device *dev;
......@@ -32,6 +35,7 @@ struct prestera_kern_neigh_cache {
/* Lock cache if neigh is present in kernel */
bool in_kernel;
};
struct prestera_kern_fib_cache_key {
struct prestera_ip_addr addr;
u32 prefix_len;
......@@ -1021,6 +1025,78 @@ __prestera_k_arb_util_fib_overlapped(struct prestera_switch *sw,
return rfc;
}
static void __prestera_k_arb_hw_state_upd(struct prestera_switch *sw,
struct prestera_kern_neigh_cache *nc)
{
struct prestera_nh_neigh_key nh_key;
struct prestera_nh_neigh *nh_neigh;
struct neighbour *n;
bool hw_active;
prestera_util_nc_key2nh_key(&nc->key, &nh_key);
nh_neigh = prestera_nh_neigh_find(sw, &nh_key);
if (!nh_neigh) {
pr_err("Cannot find nh_neigh for cached %pI4n",
&nc->key.addr.u.ipv4);
return;
}
hw_active = prestera_nh_neigh_util_hw_state(sw, nh_neigh);
#ifdef PRESTERA_IMPLICITY_RESOLVE_DEAD_NEIGH
if (!hw_active && nc->in_kernel)
goto out;
#else /* PRESTERA_IMPLICITY_RESOLVE_DEAD_NEIGH */
if (!hw_active)
goto out;
#endif /* PRESTERA_IMPLICITY_RESOLVE_DEAD_NEIGH */
if (nc->key.addr.v == PRESTERA_IPV4) {
n = neigh_lookup(&arp_tbl, &nc->key.addr.u.ipv4,
nc->key.dev);
if (!n)
n = neigh_create(&arp_tbl, &nc->key.addr.u.ipv4,
nc->key.dev);
} else {
n = NULL;
}
if (!IS_ERR(n) && n) {
neigh_event_send(n, NULL);
neigh_release(n);
} else {
pr_err("Cannot create neighbour %pI4n", &nc->key.addr.u.ipv4);
}
out:
return;
}
/* Propagate hw state to kernel */
static void prestera_k_arb_hw_evt(struct prestera_switch *sw)
{
struct prestera_kern_neigh_cache *n_cache;
struct rhashtable_iter iter;
rhashtable_walk_enter(&sw->router->kern_neigh_cache_ht, &iter);
rhashtable_walk_start(&iter);
while (1) {
n_cache = rhashtable_walk_next(&iter);
if (!n_cache)
break;
if (IS_ERR(n_cache))
continue;
rhashtable_walk_stop(&iter);
__prestera_k_arb_hw_state_upd(sw, n_cache);
rhashtable_walk_start(&iter);
}
rhashtable_walk_stop(&iter);
rhashtable_walk_exit(&iter);
}
/* Propagate kernel event to hw */
static void prestera_k_arb_n_evt(struct prestera_switch *sw,
struct neighbour *n)
......@@ -1441,6 +1517,34 @@ static int prestera_router_netevent_event(struct notifier_block *nb,
return NOTIFY_DONE;
}
static void prestera_router_update_neighs_work(struct work_struct *work)
{
struct prestera_router *router;
router = container_of(work, struct prestera_router,
neighs_update.dw.work);
rtnl_lock();
prestera_k_arb_hw_evt(router->sw);
rtnl_unlock();
prestera_queue_delayed_work(&router->neighs_update.dw,
msecs_to_jiffies(PRESTERA_NH_PROBE_INTERVAL));
}
static int prestera_neigh_work_init(struct prestera_switch *sw)
{
INIT_DELAYED_WORK(&sw->router->neighs_update.dw,
prestera_router_update_neighs_work);
prestera_queue_delayed_work(&sw->router->neighs_update.dw, 0);
return 0;
}
static void prestera_neigh_work_fini(struct prestera_switch *sw)
{
cancel_delayed_work_sync(&sw->router->neighs_update.dw);
}
int prestera_router_init(struct prestera_switch *sw)
{
struct prestera_router *router;
......@@ -1474,6 +1578,10 @@ int prestera_router_init(struct prestera_switch *sw)
goto err_nh_state_cache_alloc;
}
err = prestera_neigh_work_init(sw);
if (err)
goto err_neigh_work_init;
router->inetaddr_valid_nb.notifier_call = __prestera_inetaddr_valid_cb;
err = register_inetaddr_validator_notifier(&router->inetaddr_valid_nb);
if (err)
......@@ -1504,6 +1612,8 @@ int prestera_router_init(struct prestera_switch *sw)
err_register_inetaddr_notifier:
unregister_inetaddr_validator_notifier(&router->inetaddr_valid_nb);
err_register_inetaddr_validator_notifier:
prestera_neigh_work_fini(sw);
err_neigh_work_init:
kfree(router->nhgrp_hw_state_cache);
err_nh_state_cache_alloc:
rhashtable_destroy(&router->kern_neigh_cache_ht);
......@@ -1522,6 +1632,7 @@ void prestera_router_fini(struct prestera_switch *sw)
unregister_netevent_notifier(&sw->router->netevent_nb);
unregister_inetaddr_notifier(&sw->router->inetaddr_nb);
unregister_inetaddr_validator_notifier(&sw->router->inetaddr_valid_nb);
prestera_neigh_work_fini(sw);
prestera_queue_drain();
prestera_k_arb_abort(sw);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment