[PATCH net-next v1 2/2] net: marvell: prestera: Handle ipv6 lpm/neigh events

From: Yevhen Orlov
Date: Sun Dec 18 2022 - 17:16:42 EST


Handle AF_INET6 for events:
- NETEVENT_NEIGH_UPDATE
- FIB_EVENT_ENTRY_REPLACE
- FIB_EVENT_ENTRY_DEL

Also try to make wrappers for ipv4/6 specific functions.
This allow us to pass fib_notifier_info for most cases.
E.g prestera_util_fen_info_copy, prestera_util_fen_info_release.
Main idea is to increase number of agnostic about ip version functions.

Limitations:
- Only "local" and "main" tables supported
- Only generic interfaces supported for router (no bridges or vlans)

Co-developed-by: Taras Chornyi <taras.chornyi@xxxxxxxxxxx>
Signed-off-by: Taras Chornyi <taras.chornyi@xxxxxxxxxxx>
Co-developed-by: Elad Nachman <enachman@xxxxxxxxxxx>
Signed-off-by: Elad Nachman <enachman@xxxxxxxxxxx>
Signed-off-by: Yevhen Orlov <yevhen.orlov@xxxxxxxxxxx>
---
.../marvell/prestera/prestera_router.c | 138 +++++++++++++-----
1 file changed, 101 insertions(+), 37 deletions(-)

diff --git a/drivers/net/ethernet/marvell/prestera/prestera_router.c b/drivers/net/ethernet/marvell/prestera/prestera_router.c
index a9a1028cb17b..5ee0a9511878 100644
--- a/drivers/net/ethernet/marvell/prestera/prestera_router.c
+++ b/drivers/net/ethernet/marvell/prestera/prestera_router.c
@@ -12,6 +12,7 @@
#include <linux/if_vlan.h>
#include <linux/if_macvlan.h>
#include <net/netevent.h>
+#include <net/ip6_route.h>

#include "prestera.h"
#include "prestera_router_hw.h"
@@ -60,6 +61,7 @@ struct prestera_kern_fib_cache {
union {
struct fib_notifier_info info; /* point to any of 4/6 */
struct fib_entry_notifier_info fen4_info;
+ struct fib6_entry_notifier_info fen6_info;
};
bool reachable;
};
@@ -89,18 +91,67 @@ static u32 prestera_fix_tb_id(u32 tb_id)
return tb_id;
}

+static void
+prestera_util_fen_info_copy(struct fib_notifier_info *sinfo,
+ struct fib_notifier_info *tinfo)
+{
+ struct fib6_entry_notifier_info *sinfo6 =
+ container_of(sinfo, struct fib6_entry_notifier_info, info);
+ struct fib_entry_notifier_info *sinfo4 =
+ container_of(sinfo, struct fib_entry_notifier_info, info);
+ struct fib6_entry_notifier_info *tinfo6 =
+ container_of(tinfo, struct fib6_entry_notifier_info, info);
+ struct fib_entry_notifier_info *tinfo4 =
+ container_of(tinfo, struct fib_entry_notifier_info, info);
+
+ if (sinfo->family == AF_INET) {
+ fib_info_hold(sinfo4->fi);
+ *tinfo4 = *sinfo4;
+ } else if (sinfo->family == AF_INET6) {
+ fib6_info_hold(sinfo6->rt);
+ *tinfo6 = *sinfo6;
+ } else {
+ WARN(1, "Invalid address family %s %d", __func__, sinfo->family);
+ }
+}
+
+static void prestera_util_fen_info_release(struct fib_notifier_info *info)
+{
+ struct fib6_entry_notifier_info *info6 =
+ container_of(info, struct fib6_entry_notifier_info, info);
+ struct fib_entry_notifier_info *info4 =
+ container_of(info, struct fib_entry_notifier_info, info);
+
+ if (info->family == AF_INET)
+ fib_info_put(info4->fi);
+ else if (info->family == AF_INET6)
+ fib6_info_release(info6->rt);
+ else
+ WARN(1, "Invalid address family %s %d",
+ __func__, info->family);
+}
+
static void
prestera_util_fen_info2fib_cache_key(struct fib_notifier_info *info,
struct prestera_kern_fib_cache_key *key)
{
+ 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);

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;
+ if (info->family == AF_INET) {
+ 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;
+ } else if (info->family == AF_INET6) {
+ key->addr.v = PRESTERA_IPV6;
+ key->addr.u.ipv6 = fen6_info->rt->fib6_dst.addr;
+ key->prefix_len = fen6_info->rt->fib6_dst.plen;
+ key->kern_tb_id = fen6_info->rt->fib6_table->tb6_id;
+ }
}

static int prestera_util_nhc2nc_key(struct prestera_switch *sw,
@@ -155,6 +206,9 @@ prestera_util_neigh2nc_key(struct prestera_switch *sw, struct neighbour *n,
if (n->tbl->family == AF_INET) {
key->addr.v = PRESTERA_IPV4;
key->addr.u.ipv4 = *(__be32 *)n->primary_key;
+ } else if (n->tbl->family == AF_INET6) {
+ key->addr.v = PRESTERA_IPV6;
+ key->addr.u.ipv6 = *(struct in6_addr *)n->primary_key;
} else {
return -ENOENT;
}
@@ -683,8 +737,15 @@ __prestera_k_arb_n_offload_set(struct prestera_switch *sw,
{
struct neighbour *n;

- n = neigh_lookup(&arp_tbl, &nc->key.addr.u.ipv4,
- nc->key.dev);
+ if (nc->key.addr.v == PRESTERA_IPV4)
+ n = neigh_lookup(&arp_tbl, &nc->key.addr.u.ipv4,
+ nc->key.dev);
+ else if (nc->key.addr.v == PRESTERA_IPV6)
+ n = neigh_lookup(&nd_tbl, &nc->key.addr.u.ipv6,
+ nc->key.dev);
+ else
+ n = NULL;
+
if (!n)
return;

@@ -715,7 +776,8 @@ __prestera_k_arb_fib_lpm_offload_set(struct prestera_switch *sw,
fib_alias_hw_flags_set(&init_net, &fri);
return;
case PRESTERA_IPV6:
- /* TODO */
+ fib6_info_hw_flags_set(&init_net, fc->fen6_info.rt,
+ offload, trap, fail);
return;
}
}
@@ -1384,44 +1446,43 @@ static int __prestera_inetaddr_valid_cb(struct notifier_block *nb,
struct prestera_fib_event_work {
struct work_struct work;
struct prestera_switch *sw;
- struct fib_entry_notifier_info fen_info;
+ union {
+ struct fib_notifier_info info; /* point to any of 4/6 */
+ struct fib6_entry_notifier_info fen6_info;
+ struct fib_entry_notifier_info fen4_info;
+ };
unsigned long event;
};

static void __prestera_router_fib_event_work(struct work_struct *work)
{
struct prestera_fib_event_work *fib_work =
- container_of(work, struct prestera_fib_event_work, work);
- struct prestera_switch *sw = fib_work->sw;
+ container_of(work, struct prestera_fib_event_work,
+ work);
int err;

rtnl_lock();

switch (fib_work->event) {
case FIB_EVENT_ENTRY_REPLACE:
- err = prestera_k_arb_fib_evt(sw, true,
- &fib_work->fen_info.info);
+ err = prestera_k_arb_fib_evt(fib_work->sw, true,
+ &fib_work->info);
if (err)
- goto err_out;
+ dev_err(fib_work->sw->dev->dev,
+ "Error when processing lpm entry");

break;
case FIB_EVENT_ENTRY_DEL:
- err = prestera_k_arb_fib_evt(sw, false,
- &fib_work->fen_info.info);
+ err = prestera_k_arb_fib_evt(fib_work->sw, false,
+ &fib_work->info);
if (err)
- goto err_out;
+ dev_err(fib_work->sw->dev->dev,
+ "Cant delete lpm entry");

break;
}

- goto out;
-
-err_out:
- dev_err(sw->dev->dev, "Error when processing %pI4h/%d",
- &fib_work->fen_info.dst,
- fib_work->fen_info.dst_len);
-out:
- fib_info_put(fib_work->fen_info.fi);
+ prestera_util_fen_info_release(&fib_work->info);
rtnl_unlock();
kfree(fib_work);
}
@@ -1430,30 +1491,33 @@ static void __prestera_router_fib_event_work(struct work_struct *work)
static int __prestera_router_fib_event(struct notifier_block *nb,
unsigned long event, void *ptr)
{
+ struct fib6_entry_notifier_info *info6 =
+ container_of(ptr, struct fib6_entry_notifier_info, info);
+ struct fib_entry_notifier_info *info4 =
+ container_of(ptr, struct fib_entry_notifier_info, info);
+ struct prestera_router *router =
+ container_of(nb, struct prestera_router, fib_nb);
struct prestera_fib_event_work *fib_work;
- struct fib_entry_notifier_info *fen_info;
struct fib_notifier_info *info = ptr;
- struct prestera_router *router;
-
- if (info->family != AF_INET)
- return NOTIFY_DONE;
-
- router = container_of(nb, struct prestera_router, fib_nb);

switch (event) {
case FIB_EVENT_ENTRY_REPLACE:
case FIB_EVENT_ENTRY_DEL:
- fen_info = container_of(info, struct fib_entry_notifier_info,
- info);
- if (!fen_info->fi)
+ if (info->family == AF_INET6) {
+ if (!info6->rt)
+ return NOTIFY_DONE;
+ } else if (info->family == AF_INET) {
+ if (!info4->fi)
+ return NOTIFY_DONE;
+ } else {
return NOTIFY_DONE;
+ }

fib_work = kzalloc(sizeof(*fib_work), GFP_ATOMIC);
if (WARN_ON(!fib_work))
return NOTIFY_BAD;

- fib_info_hold(fen_info->fi);
- fib_work->fen_info = *fen_info;
+ prestera_util_fen_info_copy(info, &fib_work->info);
fib_work->event = event;
fib_work->sw = router->sw;
INIT_WORK(&fib_work->work, __prestera_router_fib_event_work);
@@ -1500,7 +1564,7 @@ static int prestera_router_netevent_event(struct notifier_block *nb,

switch (event) {
case NETEVENT_NEIGH_UPDATE:
- if (n->tbl->family != AF_INET)
+ if (n->tbl->family != AF_INET && n->tbl->family != AF_INET6)
return NOTIFY_DONE;

net_work = kzalloc(sizeof(*net_work), GFP_ATOMIC);
--
2.17.1