Re: [PATCH net-next v6 8/9] net: marvell: prestera: Add neighbour cache accounting

From: Paolo Abeni
Date: Thu Sep 22 2022 - 06:17:13 EST


On Sun, 2022-09-18 at 22:46 +0300, Yevhen Orlov wrote:
> Move forward and use new PRESTERA_FIB_TYPE_UC_NH to provide basic
> nexthop routes support.
> Provide deinitialization sequence for all created router objects.
>
> Limitations:
> - Only "local" and "main" tables supported
> - Only generic interfaces supported for router (no bridges or vlans)
>
> Co-developed-by: Taras Chornyi <tchornyi@xxxxxxxxxxx>
> Signed-off-by: Taras Chornyi <tchornyi@xxxxxxxxxxx>
> Co-developed-by: Oleksandr Mazur <oleksandr.mazur@xxxxxxxxxxx>
> Signed-off-by: Oleksandr Mazur <oleksandr.mazur@xxxxxxxxxxx>
> Signed-off-by: Yevhen Orlov <yevhen.orlov@xxxxxxxxxxx>
> ---
> .../net/ethernet/marvell/prestera/prestera.h | 1 +
> .../marvell/prestera/prestera_router.c | 815 +++++++++++++++++-
> 2 files changed, 813 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/net/ethernet/marvell/prestera/prestera.h b/drivers/net/ethernet/marvell/prestera/prestera.h
> index 2f2f80e7e358..540a36069b79 100644
> --- a/drivers/net/ethernet/marvell/prestera/prestera.h
> +++ b/drivers/net/ethernet/marvell/prestera/prestera.h
> @@ -316,6 +316,7 @@ struct prestera_router {
> struct rhashtable nh_neigh_ht;
> struct rhashtable nexthop_group_ht;
> struct rhashtable fib_ht;
> + struct rhashtable kern_neigh_cache_ht;
> struct rhashtable kern_fib_cache_ht;
> struct notifier_block inetaddr_nb;
> struct notifier_block inetaddr_valid_nb;
> diff --git a/drivers/net/ethernet/marvell/prestera/prestera_router.c b/drivers/net/ethernet/marvell/prestera/prestera_router.c
> index a6af3b53838e..771d123345ac 100644
> --- a/drivers/net/ethernet/marvell/prestera/prestera_router.c
> +++ b/drivers/net/ethernet/marvell/prestera/prestera_router.c
> @@ -8,11 +8,30 @@
> #include <net/switchdev.h>
> #include <linux/rhashtable.h>
> #include <net/nexthop.h>
> +#include <net/arp.h>
> +#include <linux/if_vlan.h>
> +#include <linux/if_macvlan.h>
> #include <net/netevent.h>
>
> #include "prestera.h"
> #include "prestera_router_hw.h"
>
> +struct prestera_kern_neigh_cache_key {
> + struct prestera_ip_addr addr;
> + struct net_device *dev;
> +};
> +
> +struct prestera_kern_neigh_cache {
> + struct prestera_kern_neigh_cache_key key;
> + struct rhash_head ht_node;
> + struct list_head kern_fib_cache_list;
> + /* Hold prepared nh_neigh info if is in_kernel */
> + struct prestera_neigh_info nh_neigh_info;
> + /* Indicate if neighbour is reachable by direct route */
> + bool reachable;
> + /* 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;
> @@ -25,9 +44,15 @@ struct prestera_kern_fib_cache {
> struct {
> struct prestera_fib_key fib_key;
> enum prestera_fib_type fib_type;
> + struct prestera_nexthop_group_key nh_grp_key;
> } lpm_info; /* hold prepared lpm info */
> /* Indicate if route is not overlapped by another table */
> struct rhash_head ht_node; /* node of prestera_router */
> + struct prestera_kern_neigh_cache_head {
> + struct prestera_kern_fib_cache *this;
> + struct list_head head;
> + struct prestera_kern_neigh_cache *n_cache;
> + } kern_neigh_cache_head[PRESTERA_NHGR_SIZE_MAX];
> union {
> struct fib_notifier_info info; /* point to any of 4/6 */
> struct fib_entry_notifier_info fen4_info;
> @@ -35,6 +60,13 @@ struct prestera_kern_fib_cache {
> bool reachable;
> };
>
> +static const struct rhashtable_params __prestera_kern_neigh_cache_ht_params = {
> + .key_offset = offsetof(struct prestera_kern_neigh_cache, key),
> + .head_offset = offsetof(struct prestera_kern_neigh_cache, ht_node),
> + .key_len = sizeof(struct prestera_kern_neigh_cache_key),
> + .automatic_shrinking = true,
> +};
> +
> static const struct rhashtable_params __prestera_kern_fib_cache_ht_params = {
> .key_offset = offsetof(struct prestera_kern_fib_cache, key),
> .head_offset = offsetof(struct prestera_kern_fib_cache, ht_node),
> @@ -67,6 +99,278 @@ prestera_util_fen_info2fib_cache_key(struct fib_notifier_info *info,
> key->kern_tb_id = fen_info->tb_id;
> }
>
> +static int prestera_util_nhc2nc_key(struct prestera_switch *sw,
> + struct fib_nh_common *nhc,
> + struct prestera_kern_neigh_cache_key *nk)
> +{
> + memset(nk, 0, sizeof(*nk));
> + if (nhc->nhc_gw_family == AF_INET) {
> + nk->addr.v = PRESTERA_IPV4;
> + nk->addr.u.ipv4 = nhc->nhc_gw.ipv4;
> + } else {
> + nk->addr.v = PRESTERA_IPV6;
> + nk->addr.u.ipv6 = nhc->nhc_gw.ipv6;
> + }
> +
> + nk->dev = nhc->nhc_dev;
> + return 0;
> +}
> +
> +static void
> +prestera_util_nc_key2nh_key(struct prestera_kern_neigh_cache_key *ck,
> + struct prestera_nh_neigh_key *nk)
> +{
> + memset(nk, 0, sizeof(*nk));
> + nk->addr = ck->addr;
> + nk->rif = (void *)ck->dev;
> +}
> +
> +static bool
> +prestera_util_nhc_eq_n_cache_key(struct prestera_switch *sw,
> + struct fib_nh_common *nhc,
> + struct prestera_kern_neigh_cache_key *nk)
> +{
> + struct prestera_kern_neigh_cache_key tk;
> + int err;
> +
> + err = prestera_util_nhc2nc_key(sw, nhc, &tk);
> + if (err)
> + return false;
> +
> + if (memcmp(&tk, nk, sizeof(tk)))
> + return false;
> +
> + return true;
> +}
> +
> +static int
> +prestera_util_neigh2nc_key(struct prestera_switch *sw, struct neighbour *n,
> + struct prestera_kern_neigh_cache_key *key)
> +{
> + memset(key, 0, sizeof(*key));
> + if (n->tbl->family == AF_INET) {
> + key->addr.v = PRESTERA_IPV4;
> + key->addr.u.ipv4 = *(__be32 *)n->primary_key;
> + } else {
> + return -ENOENT;
> + }
> +
> + key->dev = n->dev;
> +
> + return 0;
> +}
> +
> +static bool __prestera_fi_is_direct(struct fib_info *fi)
> +{
> + struct fib_nh *fib_nh;
> +
> + if (fib_info_num_path(fi) == 1) {
> + fib_nh = fib_info_nh(fi, 0);
> + if (fib_nh->fib_nh_gw_family == AF_UNSPEC)
> + return true;
> + }
> +
> + return false;
> +}
> +
> +static bool prestera_fi_is_direct(struct fib_info *fi)
> +{
> + if (fi->fib_type != RTN_UNICAST)
> + return false;
> +
> + return __prestera_fi_is_direct(fi);
> +}
> +
> +static bool prestera_fi_is_nh(struct fib_info *fi)
> +{
> + if (fi->fib_type != RTN_UNICAST)
> + return false;
> +
> + return !__prestera_fi_is_direct(fi);
> +}
> +
> +static bool __prestera_fi6_is_direct(struct fib6_info *fi)
> +{
> + if (!fi->fib6_nh->nh_common.nhc_gw_family)
> + return true;
> +
> + return false;
> +}
> +
> +static bool prestera_fi6_is_direct(struct fib6_info *fi)
> +{
> + if (fi->fib6_type != RTN_UNICAST)
> + return false;
> +
> + return __prestera_fi6_is_direct(fi);
> +}
> +
> +static bool prestera_fi6_is_nh(struct fib6_info *fi)
> +{
> + if (fi->fib6_type != RTN_UNICAST)
> + return false;
> +
> + return !__prestera_fi6_is_direct(fi);
> +}
> +
> +static bool prestera_fib_info_is_direct(struct fib_notifier_info *info)
> +{
> + struct fib6_entry_notifier_info *fen6_info =
> + container_of(info, struct fib6_entry_notifier_info, info);
> + struct fib_entry_notifier_info *fen_info =
> + container_of(info, struct fib_entry_notifier_info, info);
> +
> + if (info->family == AF_INET)
> + return prestera_fi_is_direct(fen_info->fi);
> + else
> + return prestera_fi6_is_direct(fen6_info->rt);
> +}
> +
> +static bool prestera_fib_info_is_nh(struct fib_notifier_info *info)
> +{
> + struct fib6_entry_notifier_info *fen6_info =
> + container_of(info, struct fib6_entry_notifier_info, info);
> + struct fib_entry_notifier_info *fen_info =
> + container_of(info, struct fib_entry_notifier_info, info);
> +
> + if (info->family == AF_INET)
> + return prestera_fi_is_nh(fen_info->fi);
> + else
> + return prestera_fi6_is_nh(fen6_info->rt);
> +}
> +
> +/* must be called with rcu_read_lock() */
> +static int prestera_util_kern_get_route(struct fib_result *res, u32 tb_id,
> + __be32 *addr)
> +{
> + struct fib_table *tb;
> + struct flowi4 fl4;
> + int ret;
> +
> + /* TODO: walkthrough appropriate tables in kernel
> + * to know if the same prefix exists in several tables
> + */
> + tb = fib_new_table(&init_net, tb_id);
> + if (!tb)
> + return -ENOENT;
> +
> + memset(&fl4, 0, sizeof(fl4));
> + fl4.daddr = *addr;
> + ret = fib_table_lookup(tb, &fl4, res, FIB_LOOKUP_NOREF);
> + if (ret)
> + return ret;

Likely you can use fib_lookup() instead of all the above.

Thanks,

Paolo