[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20260107132408.3904352-9-rkannoth@marvell.com>
Date: Wed, 7 Jan 2026 18:54:06 +0530
From: Ratheesh Kannoth <rkannoth@...vell.com>
To: <netdev@...r.kernel.org>, <linux-kernel@...r.kernel.org>,
<andrew+netdev@...n.ch>
CC: <sgoutham@...vell.com>, <davem@...emloft.net>, <edumazet@...gle.com>,
<kuba@...nel.org>, <pabeni@...hat.com>,
Ratheesh Kannoth
<rkannoth@...vell.com>
Subject: [PATCH net-next v2 08/10] octeontx2: switch: L3 offload support
Linux route events are parsed to decide on destination DIP/MASK to fwd
packets. Switchdev HW flow table is filled with this information.
Once populated, all packet with DIP/MASK will be accelerated.
Signed-off-by: Ratheesh Kannoth <rkannoth@...vell.com>
---
.../marvell/octeontx2/af/switch/rvu_sw.c | 2 +-
.../marvell/octeontx2/af/switch/rvu_sw_l3.c | 202 ++++++++++++++++++
.../marvell/octeontx2/nic/switch/sw_fib.c | 119 +++++++++++
.../marvell/octeontx2/nic/switch/sw_fib.h | 3 +
.../marvell/octeontx2/nic/switch/sw_nb.c | 20 ++
5 files changed, 345 insertions(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.c b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.c
index b66f9c2eb981..fe91b0a6baf5 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.c
@@ -6,9 +6,9 @@
*/
#include "rvu.h"
-#include "rvu_sw.h"
#include "rvu_sw_l2.h"
#include "rvu_sw_fl.h"
+#include "rvu_sw.h"
u32 rvu_sw_port_id(struct rvu *rvu, u16 pcifunc)
{
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..dc01d0ff26a7 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,213 @@
* Copyright (C) 2026 Marvell.
*
*/
+
+#include <linux/bitfield.h>
#include "rvu.h"
+#include "rvu_sw.h"
+#include "rvu_sw_l3.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
+
+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 void sw_l3_offl_dump(struct l3_entry *l3_entry)
+{
+ struct fib_entry *entry = l3_entry->entry;
+ int i;
+
+ for (i = 0; i < l3_entry->cnt; i++, entry++) {
+ pr_debug("%s:%d cmd=%llu port_id=%#x dst=%#x dst_len=%d gw=%#x\n",
+ __func__, __LINE__, entry->cmd, entry->port_id, entry->dst,
+ entry->dst_len, entry->gw);
+ }
+}
+
+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;
+
+ 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;
+
+ sw_l3_offl_dump(l3_entry);
+
+ 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 > 16) {
+ 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);
+ if (!sw_l3_offl_wq)
+ return -EFAULT;
+
+ 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..3d6e09ac987d 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,132 @@
* 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 void sw_fib_dump(struct fib_entry *entry, int cnt)
+{
+ int i;
+
+ for (i = 0; i < cnt; i++, entry++) {
+ pr_debug("%s:%d cmd=%s gw_valid=%d mac_valid=%d dst=%#x len=%d gw=%#x mac=%pM nud_state=%#x\n",
+ __func__, __LINE__,
+ sw_nb_get_cmd2str(entry->cmd),
+ entry->gw_valid, entry->mac_valid, entry->dst, entry->dst_len,
+ entry->gw, entry->mac, entry->nud_state);
+ }
+}
+
+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);
+ sw_fib_dump(req->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);
+ if (!lentry)
+ return -ENOMEM;
+
+ 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 f5e00807c0fa..7a0ed52eae95 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.c
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.c
@@ -307,6 +307,12 @@ static int sw_nb_fib_event(struct notifier_block *nb,
return NOTIFY_DONE;
}
+ if (sw_fib_add_to_list(pf_dev, entries, cnt)) {
+ kfree(entries);
+ kfree(haddr);
+ return NOTIFY_DONE;
+ }
+
if (!hcnt) {
kfree(haddr);
return NOTIFY_DONE;
@@ -336,6 +342,7 @@ static int sw_nb_fib_event(struct notifier_block *nb,
iter->cmd, iter->dst, iter->dst_len, iter->gw, dev->name);
}
+ sw_fib_add_to_list(pf_dev, entries, hcnt);
kfree(haddr);
return NOTIFY_DONE;
}
@@ -390,6 +397,9 @@ static int sw_nb_net_event(struct notifier_block *nb,
pf = netdev_priv(pf_dev);
entry->port_id = pf->pcifunc;
+ if (sw_fib_add_to_list(pf_dev, entry, 1))
+ kfree(entry);
+
break;
}
@@ -469,6 +479,11 @@ static int sw_nb_inetaddr_event(struct notifier_block *nb,
break;
}
+ if (sw_fib_add_to_list(pf_dev, entry, 1)) {
+ kfree(entry);
+ return NOTIFY_DONE;
+ }
+
netdev_dbg(dev,
"%s:%d pushing inetaddr event from HOST interface address %#x, %pM, %s\n",
__func__, __LINE__, entry->dst, entry->mac, dev->name);
@@ -536,6 +551,11 @@ static int sw_nb_netdev_event(struct notifier_block *unused,
break;
}
+ if (sw_fib_add_to_list(pf_dev, entry, 1)) {
+ kfree(entry);
+ return NOTIFY_DONE;
+ }
+
netdev_dbg(dev,
"%s:%d pushing netdev event from HOST interface address %#x, %pM, dev=%s\n",
__func__, __LINE__, entry->dst, entry->mac, dev->name);
--
2.43.0
Powered by blists - more mailing lists