From: Ratheesh Kannoth <rkannoth@marvell.com>
To: <linux-kernel@vger.kernel.org>, <netdev@vger.kernel.org>
Cc: <andrew+netdev@lunn.ch>, <davem@davemloft.net>,
<edumazet@google.com>, <kuba@kernel.org>, <pabeni@redhat.com>,
<sgoutham@marvell.com>, "Ratheesh Kannoth" <rkannoth@marvell.com>
Subject: [PATCH net-next 8/9] octeontx2: offload host FIB updates to switch via AF mailbox
Date: Tue, 30 Jun 2026 08:17:14 +0530 [thread overview]
Message-ID: <20260630024715.4124281-9-rkannoth@marvell.com> (raw)
In-Reply-To: <20260630024715.4124281-1-rkannoth@marvell.com>
Queue IPv4/IPv6 FIB-derived updates from the switch notifier path
and handle fib_notify in the RVU AF by batching fib_entry
structures and sending them to the switch PF through the
AF-to-switchdev FIB_CMD). Require the switch firmware to
be ready before accepting offload work.
Signed-off-by: Ratheesh Kannoth <rkannoth@marvell.com>
---
.../net/ethernet/marvell/octeontx2/af/mbox.h | 2 +-
.../marvell/octeontx2/af/switch/rvu_sw_l3.c | 187 ++++++++++++++++++
.../marvell/octeontx2/nic/switch/sw_fib.c | 103 ++++++++++
.../marvell/octeontx2/nic/switch/sw_fib.h | 3 +
.../marvell/octeontx2/nic/switch/sw_nb.c | 10 +-
.../marvell/octeontx2/nic/switch/sw_nb_v4.c | 26 ++-
.../marvell/octeontx2/nic/switch/sw_nb_v6.c | 10 +-
7 files changed, 324 insertions(+), 17 deletions(-)
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/mbox.h b/drivers/net/ethernet/marvell/octeontx2/af/mbox.h
index 835fe2528f03..d3ee1f1904af 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/mbox.h
+++ b/drivers/net/ethernet/marvell/octeontx2/af/mbox.h
@@ -1968,7 +1968,7 @@ struct af2swdev_notify_req {
};
struct {
u8 cnt;
- struct fib_entry entry[16];
+ struct fib_entry entry[12];
};
struct {
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l3.c b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l3.c
index 2b798d5f0644..bd021e7258a1 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l3.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l3.c
@@ -4,11 +4,198 @@
* Copyright (C) 2026 Marvell.
*
*/
+
+#include <linux/bitfield.h>
#include "rvu.h"
+#include "rvu_sw.h"
+
+#define M(_name, _id, _fn_name, _req_type, _rsp_type) \
+static struct _req_type __maybe_unused \
+*otx2_mbox_alloc_msg_ ## _fn_name(struct rvu *rvu, int devid) \
+{ \
+ struct _req_type *req; \
+ \
+ req = (struct _req_type *)otx2_mbox_alloc_msg_rsp( \
+ &rvu->afpf_wq_info.mbox_up, devid, sizeof(struct _req_type), \
+ sizeof(struct _rsp_type)); \
+ if (!req) \
+ return NULL; \
+ req->hdr.sig = OTX2_MBOX_REQ_SIG; \
+ req->hdr.id = _id; \
+ return req; \
+}
+MBOX_UP_AF2SWDEV_MESSAGES
+#undef M
+
+static struct workqueue_struct *sw_l3_offl_wq;
+
+struct l3_entry {
+ struct list_head list;
+ struct rvu *rvu;
+ u32 port_id;
+ int cnt;
+ struct fib_entry entry[];
+};
+
+static DEFINE_MUTEX(l3_offl_llock);
+static LIST_HEAD(l3_offl_lh);
+static bool l3_offl_work_running;
+
+static struct workqueue_struct *sw_l3_offl_wq;
+static void sw_l3_offl_work_handler(struct work_struct *work);
+static DECLARE_DELAYED_WORK(l3_offl_work, sw_l3_offl_work_handler);
+
+static int rvu_sw_l3_offl_rule_push(struct list_head *lh)
+{
+ struct af2swdev_notify_req *req;
+ struct fib_entry *entry, *dst;
+ struct l3_entry *l3_entry;
+ struct rvu *rvu;
+ int swdev_pf;
+ int sz, cnt;
+ int tot_cnt = 0;
+
+ BUILD_BUG_ON(sizeof(*req) > 1024);
+
+ l3_entry = list_first_entry_or_null(lh, struct l3_entry, list);
+ if (!l3_entry)
+ return 0;
+
+ rvu = l3_entry->rvu;
+ swdev_pf = rvu_get_pf(rvu->pdev, rvu->rswitch.pcifunc);
+
+ mutex_lock(&rvu->mbox_lock);
+ req = otx2_mbox_alloc_msg_af2swdev_notify(rvu, swdev_pf);
+ if (!req) {
+ mutex_unlock(&rvu->mbox_lock);
+ return -ENOMEM;
+ }
+
+ dst = &req->entry[0];
+ while ((l3_entry =
+ list_first_entry_or_null(lh,
+ struct l3_entry, list)) != NULL) {
+ entry = l3_entry->entry;
+ cnt = l3_entry->cnt;
+ sz = sizeof(*entry) * cnt;
+
+ memcpy(dst, entry, sz);
+ tot_cnt += cnt;
+ dst += cnt;
+
+ list_del_init(&l3_entry->list);
+ kfree(l3_entry);
+ }
+ req->flags = FIB_CMD;
+ req->cnt = tot_cnt;
+
+ otx2_mbox_wait_for_zero(&rvu->afpf_wq_info.mbox_up, swdev_pf);
+ otx2_mbox_msg_send_up(&rvu->afpf_wq_info.mbox_up, swdev_pf);
+
+ mutex_unlock(&rvu->mbox_lock);
+ return 0;
+}
+
+static atomic64_t req_cnt;
+static atomic64_t ack_cnt;
+static atomic64_t req_processed;
+static LIST_HEAD(l3_local_lh);
+static int lcnt;
+
+static void sw_l3_offl_work_handler(struct work_struct *work)
+{
+ struct l3_entry *l3_entry;
+ struct list_head l3lh;
+ u64 req, ack, proc;
+
+ INIT_LIST_HEAD(&l3lh);
+
+ mutex_lock(&l3_offl_llock);
+ while (1) {
+ l3_entry = list_first_entry_or_null(&l3_offl_lh, struct l3_entry, list);
+
+ if (!l3_entry)
+ break;
+
+ if (lcnt + l3_entry->cnt > 8) {
+ req = atomic64_read(&req_cnt);
+ atomic64_set(&ack_cnt, req);
+ atomic64_set(&req_processed, req);
+ mutex_unlock(&l3_offl_llock);
+ goto process;
+ }
+
+ lcnt += l3_entry->cnt;
+
+ atomic64_inc(&req_cnt);
+ list_del_init(&l3_entry->list);
+ list_add_tail(&l3_entry->list, &l3_local_lh);
+ }
+ mutex_unlock(&l3_offl_llock);
+
+ req = atomic64_read(&req_cnt);
+ ack = atomic64_read(&ack_cnt);
+
+ if (req > ack) {
+ atomic64_set(&ack_cnt, req);
+ queue_delayed_work(sw_l3_offl_wq, &l3_offl_work,
+ msecs_to_jiffies(100));
+ return;
+ }
+
+ proc = atomic64_read(&req_processed);
+ if (req == proc) {
+ queue_delayed_work(sw_l3_offl_wq, &l3_offl_work,
+ msecs_to_jiffies(1000));
+ return;
+ }
+
+ atomic64_set(&req_processed, req);
+
+process:
+ lcnt = 0;
+
+ mutex_lock(&l3_offl_llock);
+ list_splice_init(&l3_local_lh, &l3lh);
+ mutex_unlock(&l3_offl_llock);
+
+ rvu_sw_l3_offl_rule_push(&l3lh);
+
+ queue_delayed_work(sw_l3_offl_wq, &l3_offl_work, msecs_to_jiffies(100));
+}
int rvu_mbox_handler_fib_notify(struct rvu *rvu,
struct fib_notify_req *req,
struct msg_rsp *rsp)
{
+ struct l3_entry *l3_entry;
+ int sz;
+
+ if (!(rvu->rswitch.flags & RVU_SWITCH_FLAG_FW_READY))
+ return 0;
+
+ sz = req->cnt * sizeof(struct fib_entry);
+
+ l3_entry = kcalloc(1, sizeof(*l3_entry) + sz, GFP_KERNEL);
+ if (!l3_entry)
+ return -ENOMEM;
+
+ l3_entry->port_id = rvu_sw_port_id(rvu, req->hdr.pcifunc);
+ l3_entry->rvu = rvu;
+ l3_entry->cnt = req->cnt;
+ INIT_LIST_HEAD(&l3_entry->list);
+ memcpy(l3_entry->entry, req->entry, sz);
+
+ mutex_lock(&l3_offl_llock);
+ list_add_tail(&l3_entry->list, &l3_offl_lh);
+ mutex_unlock(&l3_offl_llock);
+
+ if (!l3_offl_work_running) {
+ sw_l3_offl_wq = alloc_workqueue("sw_af_fib_wq", 0, 0);
+ l3_offl_work_running = true;
+ queue_delayed_work(sw_l3_offl_wq, &l3_offl_work,
+ msecs_to_jiffies(1000));
+ }
+
return 0;
}
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fib.c b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fib.c
index 12ddf8119372..384f11961b9d 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fib.c
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fib.c
@@ -4,13 +4,116 @@
* Copyright (C) 2026 Marvell.
*
*/
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <net/switchdev.h>
+#include <net/netevent.h>
+#include <net/arp.h>
+#include <net/route.h>
+
+#include "../otx2_reg.h"
+#include "../otx2_common.h"
+#include "../otx2_struct.h"
+#include "../cn10k.h"
+#include "sw_nb.h"
#include "sw_fib.h"
+static DEFINE_SPINLOCK(sw_fib_llock);
+static LIST_HEAD(sw_fib_lh);
+
+static struct workqueue_struct *sw_fib_wq;
+static void sw_fib_work_handler(struct work_struct *work);
+static DECLARE_DELAYED_WORK(sw_fib_work, sw_fib_work_handler);
+
+struct sw_fib_list_entry {
+ struct list_head lh;
+ struct otx2_nic *pf;
+ int cnt;
+ struct fib_entry *entry;
+};
+
+static int sw_fib_notify(struct otx2_nic *pf,
+ int cnt,
+ struct fib_entry *entry)
+{
+ struct fib_notify_req *req;
+ int rc;
+
+ mutex_lock(&pf->mbox.lock);
+ req = otx2_mbox_alloc_msg_fib_notify(&pf->mbox);
+ if (!req) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ req->cnt = cnt;
+ memcpy(req->entry, entry, sizeof(*entry) * cnt);
+
+ rc = otx2_sync_mbox_msg(&pf->mbox);
+out:
+ mutex_unlock(&pf->mbox.lock);
+ return rc;
+}
+
+static void sw_fib_work_handler(struct work_struct *work)
+{
+ struct sw_fib_list_entry *lentry;
+ LIST_HEAD(tlist);
+
+ spin_lock(&sw_fib_llock);
+ list_splice_init(&sw_fib_lh, &tlist);
+ spin_unlock(&sw_fib_llock);
+
+ while ((lentry =
+ list_first_entry_or_null(&tlist,
+ struct sw_fib_list_entry, lh)) != NULL) {
+ list_del_init(&lentry->lh);
+ sw_fib_notify(lentry->pf, lentry->cnt, lentry->entry);
+ kfree(lentry->entry);
+ kfree(lentry);
+ }
+
+ spin_lock(&sw_fib_llock);
+ if (!list_empty(&sw_fib_lh))
+ queue_delayed_work(sw_fib_wq, &sw_fib_work,
+ msecs_to_jiffies(10));
+ spin_unlock(&sw_fib_llock);
+}
+
+int sw_fib_add_to_list(struct net_device *dev,
+ struct fib_entry *entry, int cnt)
+{
+ struct otx2_nic *pf = netdev_priv(dev);
+ struct sw_fib_list_entry *lentry;
+
+ lentry = kcalloc(1, sizeof(*lentry), GFP_ATOMIC);
+
+ lentry->pf = pf;
+ lentry->cnt = cnt;
+ lentry->entry = entry;
+ INIT_LIST_HEAD(&lentry->lh);
+
+ spin_lock(&sw_fib_llock);
+ list_add_tail(&lentry->lh, &sw_fib_lh);
+ queue_delayed_work(sw_fib_wq, &sw_fib_work,
+ msecs_to_jiffies(10));
+ spin_unlock(&sw_fib_llock);
+
+ return 0;
+}
+
int sw_fib_init(void)
{
+ sw_fib_wq = alloc_workqueue("sw_pf_fib_wq", 0, 0);
+ if (!sw_fib_wq)
+ return -ENOMEM;
+
return 0;
}
void sw_fib_deinit(void)
{
+ cancel_delayed_work_sync(&sw_fib_work);
+ destroy_workqueue(sw_fib_wq);
}
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fib.h b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fib.h
index a51d15c2b80e..50c4fbca81e8 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fib.h
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fib.h
@@ -7,6 +7,9 @@
#ifndef SW_FIB_H_
#define SW_FIB_H_
+int sw_fib_add_to_list(struct net_device *dev,
+ struct fib_entry *entry, int cnt);
+
void sw_fib_deinit(void);
int sw_fib_init(void);
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.c b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.c
index 62d8ff4e5085..2f88d2c2fbf3 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.c
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.c
@@ -109,6 +109,7 @@ static int sw_nb_fdb_event(struct notifier_block *unused,
{
struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
struct switchdev_notifier_fdb_info *fdb_info = ptr;
+ int rc = 0;
if (!sw_nb_is_valid_dev(dev))
return NOTIFY_DONE;
@@ -117,19 +118,22 @@ static int sw_nb_fdb_event(struct notifier_block *unused,
case SWITCHDEV_FDB_ADD_TO_DEVICE:
if (fdb_info->is_local)
break;
- sw_fdb_add_to_list(dev, (u8 *)fdb_info->addr, true);
+ rc = sw_fdb_add_to_list(dev, (u8 *)fdb_info->addr, true);
break;
case SWITCHDEV_FDB_DEL_TO_DEVICE:
if (fdb_info->is_local)
break;
- sw_fdb_add_to_list(dev, (u8 *)fdb_info->addr, false);
+ rc = sw_fdb_add_to_list(dev, (u8 *)fdb_info->addr, false);
break;
default:
return NOTIFY_DONE;
}
+ if (rc)
+ netdev_err(dev, "%s: Error to add to list\n", __func__);
+
return NOTIFY_DONE;
}
@@ -298,11 +302,9 @@ static int sw_nb_netdev_event(struct notifier_block *unused,
if (idev)
sw_nb_v4_netdev_event(unused, event, ptr);
-#if IS_ENABLED(CONFIG_IPV6)
i6dev = __in6_dev_get(dev);
if (i6dev)
sw_nb_v6_netdev_event(unused, event, ptr);
-#endif
return NOTIFY_DONE;
}
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb_v4.c b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb_v4.c
index 14db824ddc06..1502e404d1e7 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb_v4.c
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb_v4.c
@@ -71,9 +71,10 @@ int sw_nb_v4_netdev_event(struct notifier_block *unused,
break;
}
+ sw_fib_add_to_list(pf_dev, entry, 1);
+
netdev_dbg(dev, "%s: pushing netdev event from HOST interface address %#x, %pM, dev=%s\n",
__func__, entry->dst, entry->mac, dev->name);
- kfree(entry);
return NOTIFY_DONE;
}
@@ -131,7 +132,7 @@ int sw_nb_v4_inetaddr_event(struct notifier_block *nb,
netdev_dbg(dev, "%s: pushing inetaddr event from HOST interface address %#x, %pM, %s\n",
__func__, entry->dst, entry->mac, dev->name);
- kfree(entry);
+ sw_fib_add_to_list(pf_dev, entry, 1);
return NOTIFY_DONE;
}
@@ -243,18 +244,26 @@ int sw_nb_v4_fib_event(struct notifier_block *nb,
}
cnt = iter - entries;
- if (!cnt)
+ if (!cnt) {
+ kfree(entries);
+ kfree(haddr);
return NOTIFY_DONE;
+ }
netdev_dbg(pf_dev, "pf_dev is %s cnt=%d\n", pf_dev->name, cnt);
- kfree(entries);
- if (!hcnt)
+ sw_fib_add_to_list(pf_dev, entries, cnt);
+
+ if (!hcnt) {
+ kfree(haddr);
return NOTIFY_DONE;
+ }
entries = kcalloc(hcnt, sizeof(*entries), GFP_ATOMIC);
- if (!entries)
+ if (!entries) {
+ kfree(haddr);
return NOTIFY_DONE;
+ }
iter = entries;
@@ -274,7 +283,7 @@ int sw_nb_v4_fib_event(struct notifier_block *nb,
netdev_dbg(pf_dev, "%s: FIB host Rule cmd=%lld dst=%#x dst_len=%d gw=%#x %s\n",
__func__, iter->cmd, iter->dst, iter->dst_len, iter->gw, pf_dev->name);
}
- kfree(entries);
+ sw_fib_add_to_list(pf_dev, entries, hcnt);
kfree(haddr);
return NOTIFY_DONE;
}
@@ -315,7 +324,8 @@ int sw_nb_net_v4_neigh_update(struct notifier_block *nb,
pf = netdev_priv(pf_dev);
entry->port_id = pf->pcifunc;
- kfree(entry);
+ sw_fib_add_to_list(pf_dev, entry, 1);
+
return NOTIFY_DONE;
err:
kfree(entry);
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb_v6.c b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb_v6.c
index e43c28d4f15c..80c386a4245d 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb_v6.c
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb_v6.c
@@ -63,10 +63,10 @@ int sw_nb_v6_netdev_event(struct notifier_block *unused,
ether_addr_copy(entry->mac, dev_addr->addr);
break;
}
+ sw_fib_add_to_list(dev, entry, 1);
netdev_dbg(dev, "netdev event %pM plen=%u mac=%pM\n",
&ifp->addr, ifp->prefix_len, entry->mac);
- kfree(entry);
return NOTIFY_DONE;
}
@@ -145,8 +145,8 @@ int sw_nb_v6_fib_event(struct notifier_block *nb,
netdev_dbg(fib_dev, "fib found MAC=%pM\n", entry->mac);
}
+ sw_fib_add_to_list(fib_dev, entry, 1);
rcu_read_unlock();
- kfree(entry);
return NOTIFY_DONE;
}
@@ -180,9 +180,10 @@ int sw_nb_net_v6_neigh_update(struct notifier_block *nb,
entry->mac_valid = 1;
entry->port_id = pf->pcifunc;
+ sw_fib_add_to_list(pf_dev, entry, 1);
+
netdev_dbg(n->dev, "v6 neigh update %pI6 mac=%pM plen=%u\n",
n->primary_key, n->ha, n->tbl->key_len * 8);
- kfree(entry);
return NOTIFY_DONE;
}
@@ -228,9 +229,10 @@ int sw_nb_v6_inetaddr_event(struct notifier_block *nb,
break;
}
+ sw_fib_add_to_list(dev, entry, 1);
+
netdev_dbg(dev, "inetaddr addr=%pI6c len=%u %pM\n",
&ifa6->addr, ifa6->prefix_len, entry->mac);
- kfree(entry);
return NOTIFY_DONE;
}
--
2.43.0
next prev parent reply other threads:[~2026-06-30 2:47 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-30 2:47 [PATCH net-next 0/9] Switch support Ratheesh Kannoth
2026-06-30 2:47 ` [PATCH net-next 1/9] octeontx2-af: switch: Add AF to switch mbox and skeleton files Ratheesh Kannoth
2026-06-30 2:47 ` [PATCH net-next 2/9] octeontx2-af: switch: Add switch dev to AF mboxes Ratheesh Kannoth
2026-06-30 2:47 ` [PATCH net-next 3/9] octeontx2-pf: switch: Add pf files hierarchy Ratheesh Kannoth
2026-06-30 2:47 ` [PATCH net-next 4/9] octeontx2-af: switch: Representor for switch port Ratheesh Kannoth
2026-06-30 2:47 ` [PATCH net-next 5/9] octeontx2-af: PAN switch TL1 scheduling and NPC channel control Ratheesh Kannoth
2026-06-30 2:47 ` [PATCH net-next 6/9] octeontx2-pf: register switch notifiers for eswitch offload Ratheesh Kannoth
2026-06-30 2:47 ` [PATCH net-next 7/9] octeontx2: plumb bridge FDB updates through AF and switchdev Ratheesh Kannoth
2026-06-30 2:47 ` Ratheesh Kannoth [this message]
2026-06-30 2:47 ` [PATCH net-next 9/9] octeontx2: add TC flow offload path for switch flows Ratheesh Kannoth
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260630024715.4124281-9-rkannoth@marvell.com \
--to=rkannoth@marvell.com \
--cc=andrew+netdev@lunn.ch \
--cc=davem@davemloft.net \
--cc=edumazet@google.com \
--cc=kuba@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=sgoutham@marvell.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox