lkml.org 
[lkml]   [2022]   [Jul]   [21]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH net-next v2 6/9] net: marvell: prestera: Add heplers to interact with fib_notifier_info
    Date
    This will be used to implement nexthops related logic in next patches.
    Also try to keep ipv4/6 abstraction to be able to reuse helpers for ipv6
    in the future.

    Co-developed-by: Taras Chornyi <tchornyi@marvell.com>
    Signed-off-by: Taras Chornyi <tchornyi@marvell.com>
    Co-developed-by: Oleksandr Mazur <oleksandr.mazur@plvision.eu>
    Signed-off-by: Oleksandr Mazur <oleksandr.mazur@plvision.eu>
    Signed-off-by: Yevhen Orlov <yevhen.orlov@plvision.eu>
    ---
    .../marvell/prestera/prestera_router.c | 311 ++++++++++++++++--
    1 file changed, 277 insertions(+), 34 deletions(-)

    diff --git a/drivers/net/ethernet/marvell/prestera/prestera_router.c b/drivers/net/ethernet/marvell/prestera/prestera_router.c
    index db327ab4a072..7e3117399432 100644
    --- a/drivers/net/ethernet/marvell/prestera/prestera_router.c
    +++ b/drivers/net/ethernet/marvell/prestera/prestera_router.c
    @@ -7,6 +7,7 @@
    #include <net/inet_dscp.h>
    #include <net/switchdev.h>
    #include <linux/rhashtable.h>
    +#include <net/nexthop.h>

    #include "prestera.h"
    #include "prestera_router_hw.h"
    @@ -26,9 +27,10 @@ struct prestera_kern_fib_cache {
    } 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 fib_info *fi;
    - dscp_t kern_dscp;
    - u8 kern_type;
    + union {
    + struct fib_notifier_info info; /* point to any of 4/6 */
    + struct fib_entry_notifier_info fen4_info;
    + };
    bool reachable;
    };

    @@ -51,15 +53,253 @@ static u32 prestera_fix_tb_id(u32 tb_id)
    }

    static void
    -prestera_util_fen_info2fib_cache_key(struct fib_entry_notifier_info *fen_info,
    +prestera_util_fen_info2fib_cache_key(struct fib_notifier_info *info,
    struct prestera_kern_fib_cache_key *key)
    {
    + struct fib_entry_notifier_info *fen_info =
    + container_of(info, struct fib_entry_notifier_info, info);
    +
    memset(key, 0, sizeof(*key));
    + key->addr.v = PRESTERA_IPV4;
    key->addr.u.ipv4 = cpu_to_be32(fen_info->dst);
    key->prefix_len = fen_info->dst_len;
    key->kern_tb_id = fen_info->tb_id;
    }

    +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;
    +
    + return 0;
    +}
    +
    +static bool
    +__prestera_util_kern_n_is_reachable_v4(u32 tb_id, __be32 *addr,
    + struct net_device *dev)
    +{
    + struct fib_nh *fib_nh;
    + struct fib_result res;
    + bool reachable;
    +
    + reachable = false;
    +
    + if (!prestera_util_kern_get_route(&res, tb_id, addr))
    + if (prestera_fi_is_direct(res.fi)) {
    + fib_nh = fib_info_nh(res.fi, 0);
    + if (dev == fib_nh->fib_nh_dev)
    + reachable = true;
    + }
    +
    + return reachable;
    +}
    +
    +/* Check if neigh route is reachable */
    +static bool
    +prestera_util_kern_n_is_reachable(u32 tb_id,
    + struct prestera_ip_addr *addr,
    + struct net_device *dev)
    +{
    + if (addr->v == PRESTERA_IPV4)
    + return __prestera_util_kern_n_is_reachable_v4(tb_id,
    + &addr->u.ipv4,
    + dev);
    + else
    + return false;
    +}
    +
    +static void prestera_util_kern_set_neigh_offload(struct neighbour *n,
    + bool offloaded)
    +{
    + if (offloaded)
    + n->flags |= NTF_OFFLOADED;
    + else
    + n->flags &= ~NTF_OFFLOADED;
    +}
    +
    +static void
    +prestera_util_kern_set_nh_offload(struct fib_nh_common *nhc, bool offloaded, bool trap)
    +{
    + if (offloaded)
    + nhc->nhc_flags |= RTNH_F_OFFLOAD;
    + else
    + nhc->nhc_flags &= ~RTNH_F_OFFLOAD;
    +
    + if (trap)
    + nhc->nhc_flags |= RTNH_F_TRAP;
    + else
    + nhc->nhc_flags &= ~RTNH_F_TRAP;
    +}
    +
    +static struct fib_nh_common *
    +prestera_kern_fib_info_nhc(struct fib_notifier_info *info, int n)
    +{
    + struct fib6_entry_notifier_info *fen6_info;
    + struct fib_entry_notifier_info *fen4_info;
    + struct fib6_info *iter;
    +
    + if (info->family == AF_INET) {
    + fen4_info = container_of(info, struct fib_entry_notifier_info,
    + info);
    + return &fib_info_nh(fen4_info->fi, n)->nh_common;
    + } else if (info->family == AF_INET6) {
    + fen6_info = container_of(info, struct fib6_entry_notifier_info,
    + info);
    + if (!n)
    + return &fen6_info->rt->fib6_nh->nh_common;
    +
    + list_for_each_entry(iter, &fen6_info->rt->fib6_siblings,
    + fib6_siblings) {
    + if (!--n)
    + return &iter->fib6_nh->nh_common;
    + }
    + }
    +
    + /* if family is incorrect - than upper functions has BUG */
    + /* if doesn't find requested index - there is alsi bug, because
    + * valid index must be produced by nhs, which checks list length
    + */
    + WARN(1, "Invalid parameters passed to %s n=%d i=%p",
    + __func__, n, info);
    + return NULL;
    +}
    +
    +static int prestera_kern_fib_info_nhs(struct fib_notifier_info *info)
    +{
    + struct fib6_entry_notifier_info *fen6_info;
    + struct fib_entry_notifier_info *fen4_info;
    +
    + if (info->family == AF_INET) {
    + fen4_info = container_of(info, struct fib_entry_notifier_info,
    + info);
    + return fib_info_num_path(fen4_info->fi);
    + } else if (info->family == AF_INET6) {
    + fen6_info = container_of(info, struct fib6_entry_notifier_info,
    + info);
    + return fen6_info->rt->fib6_nh ?
    + (fen6_info->rt->fib6_nsiblings + 1) : 0;
    + }
    +
    + return 0;
    +}
    +
    +static unsigned char
    +prestera_kern_fib_info_type(struct fib_notifier_info *info)
    +{
    + struct fib6_entry_notifier_info *fen6_info;
    + struct fib_entry_notifier_info *fen4_info;
    +
    + if (info->family == AF_INET) {
    + fen4_info = container_of(info, struct fib_entry_notifier_info,
    + info);
    + return fen4_info->fi->fib_type;
    + } else if (info->family == AF_INET6) {
    + fen6_info = container_of(info, struct fib6_entry_notifier_info,
    + info);
    + /* TODO: ECMP in ipv6 is several routes.
    + * Every route has single nh.
    + */
    + return fen6_info->rt->fib6_type;
    + }
    +
    + return RTN_UNSPEC;
    +}
    +
    static struct prestera_kern_fib_cache *
    prestera_kern_fib_cache_find(struct prestera_switch *sw,
    struct prestera_kern_fib_cache_key *key)
    @@ -76,7 +316,7 @@ static void
    prestera_kern_fib_cache_destroy(struct prestera_switch *sw,
    struct prestera_kern_fib_cache *fib_cache)
    {
    - fib_info_put(fib_cache->fi);
    + fib_info_put(fib_cache->fen4_info.fi);
    rhashtable_remove_fast(&sw->router->kern_fib_cache_ht,
    &fib_cache->ht_node,
    __prestera_kern_fib_cache_ht_params);
    @@ -89,8 +329,10 @@ prestera_kern_fib_cache_destroy(struct prestera_switch *sw,
    static struct prestera_kern_fib_cache *
    prestera_kern_fib_cache_create(struct prestera_switch *sw,
    struct prestera_kern_fib_cache_key *key,
    - struct fib_info *fi, dscp_t dscp, u8 type)
    + struct fib_notifier_info *info)
    {
    + struct fib_entry_notifier_info *fen_info =
    + container_of(info, struct fib_entry_notifier_info, info);
    struct prestera_kern_fib_cache *fib_cache;
    int err;

    @@ -99,10 +341,8 @@ prestera_kern_fib_cache_create(struct prestera_switch *sw,
    goto err_kzalloc;

    memcpy(&fib_cache->key, key, sizeof(*key));
    - fib_info_hold(fi);
    - fib_cache->fi = fi;
    - fib_cache->kern_dscp = dscp;
    - fib_cache->kern_type = type;
    + fib_info_hold(fen_info->fi);
    + memcpy(&fib_cache->fen4_info, fen_info, sizeof(*fen_info));

    err = rhashtable_insert_fast(&sw->router->kern_fib_cache_ht,
    &fib_cache->ht_node,
    @@ -113,7 +353,7 @@ prestera_kern_fib_cache_create(struct prestera_switch *sw,
    return fib_cache;

    err_ht_insert:
    - fib_info_put(fi);
    + fib_info_put(fen_info->fi);
    kfree(fib_cache);
    err_kzalloc:
    return NULL;
    @@ -126,21 +366,25 @@ __prestera_k_arb_fib_lpm_offload_set(struct prestera_switch *sw,
    {
    struct fib_rt_info fri;

    - if (fc->key.addr.v != PRESTERA_IPV4)
    + switch (fc->key.addr.v) {
    + case PRESTERA_IPV4:
    + fri.fi = fc->fen4_info.fi;
    + fri.tb_id = fc->key.kern_tb_id;
    + fri.dst = fc->key.addr.u.ipv4;
    + fri.dst_len = fc->key.prefix_len;
    + fri.dscp = fc->fen4_info.dscp;
    + fri.type = fc->fen4_info.type;
    + /* flags begin */
    + fri.offload = offload;
    + fri.trap = trap;
    + fri.offload_failed = fail;
    + /* flags end */
    + fib_alias_hw_flags_set(&init_net, &fri);
    return;
    -
    - fri.fi = fc->fi;
    - fri.tb_id = fc->key.kern_tb_id;
    - fri.dst = fc->key.addr.u.ipv4;
    - fri.dst_len = fc->key.prefix_len;
    - fri.dscp = fc->kern_dscp;
    - fri.type = fc->kern_type;
    - /* flags begin */
    - fri.offload = offload;
    - fri.trap = trap;
    - fri.offload_failed = fail;
    - /* flags end */
    - fib_alias_hw_flags_set(&init_net, &fri);
    + case PRESTERA_IPV6:
    + /* TODO */
    + return;
    + }
    }

    static int
    @@ -149,7 +393,7 @@ __prestera_pr_k_arb_fc_lpm_info_calc(struct prestera_switch *sw,
    {
    memset(&fc->lpm_info, 0, sizeof(fc->lpm_info));

    - switch (fc->fi->fib_type) {
    + switch (prestera_kern_fib_info_type(&fc->info)) {
    case RTN_UNICAST:
    fc->lpm_info.fib_type = PRESTERA_FIB_TYPE_TRAP;
    break;
    @@ -274,14 +518,14 @@ __prestera_k_arb_util_fib_overlapped(struct prestera_switch *sw,
    static int
    prestera_k_arb_fib_evt(struct prestera_switch *sw,
    bool replace, /* replace or del */
    - struct fib_entry_notifier_info *fen_info)
    + struct fib_notifier_info *info)
    {
    struct prestera_kern_fib_cache *tfib_cache, *bfib_cache; /* top/btm */
    struct prestera_kern_fib_cache_key fc_key;
    struct prestera_kern_fib_cache *fib_cache;
    int err;

    - prestera_util_fen_info2fib_cache_key(fen_info, &fc_key);
    + prestera_util_fen_info2fib_cache_key(info, &fc_key);
    fib_cache = prestera_kern_fib_cache_find(sw, &fc_key);
    if (fib_cache) {
    fib_cache->reachable = false;
    @@ -304,10 +548,7 @@ prestera_k_arb_fib_evt(struct prestera_switch *sw,
    }

    if (replace) {
    - fib_cache = prestera_kern_fib_cache_create(sw, &fc_key,
    - fen_info->fi,
    - fen_info->dscp,
    - fen_info->type);
    + fib_cache = prestera_kern_fib_cache_create(sw, &fc_key, info);
    if (!fib_cache) {
    dev_err(sw->dev->dev, "fib_cache == NULL");
    return -ENOENT;
    @@ -512,13 +753,15 @@ static void __prestera_router_fib_event_work(struct work_struct *work)

    switch (fib_work->event) {
    case FIB_EVENT_ENTRY_REPLACE:
    - err = prestera_k_arb_fib_evt(sw, true, &fib_work->fen_info);
    + err = prestera_k_arb_fib_evt(sw, true,
    + &fib_work->fen_info.info);
    if (err)
    goto err_out;

    break;
    case FIB_EVENT_ENTRY_DEL:
    - err = prestera_k_arb_fib_evt(sw, false, &fib_work->fen_info);
    + err = prestera_k_arb_fib_evt(sw, false,
    + &fib_work->fen_info.info);
    if (err)
    goto err_out;

    --
    2.17.1
    \
     
     \ /
      Last update: 2022-07-22 00:14    [W:3.898 / U:0.040 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site