Thread (21 messages) 21 messages, 3 authors, 2026-01-09

Re: [PATCH net-next v2 09/10] octeontx2: switch: Flow offload support

From: Kalesh Anakkur Purayil <kalesh-anakkur.purayil@broadcom.com>
Date: 2026-01-08 16:08:00
Also in: lkml

On Wed, Jan 7, 2026 at 7:23 PM Ratheesh Kannoth [off-list ref] wrote:
quoted hunk ↗ jump to hunk
OVS/NFT pushed HW acceleration rules to pf driver thru .ndo_tc().
Switchdev HW flow table is filled with this information.
Once populated, flow will be accelerated.

Signed-off-by: Ratheesh Kannoth <redacted>
---
 .../marvell/octeontx2/af/switch/rvu_sw.c      |   4 +
 .../marvell/octeontx2/af/switch/rvu_sw_fl.c   | 278 ++++++++++
 .../marvell/octeontx2/af/switch/rvu_sw_fl.h   |   2 +
 .../ethernet/marvell/octeontx2/nic/otx2_tc.c  |  17 +-
 .../marvell/octeontx2/nic/switch/sw_fl.c      | 520 ++++++++++++++++++
 .../marvell/octeontx2/nic/switch/sw_fl.h      |   2 +
 .../marvell/octeontx2/nic/switch/sw_nb.c      |   1 -
 7 files changed, 822 insertions(+), 2 deletions(-)
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 fe91b0a6baf5..10aed0ca5934 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.c
@@ -37,6 +37,10 @@ int rvu_mbox_handler_swdev2af_notify(struct rvu *rvu,
        case SWDEV2AF_MSG_TYPE_REFRESH_FDB:
                rc = rvu_sw_l2_fdb_list_entry_add(rvu, req->pcifunc, req->mac);
                break;
+
+       case SWDEV2AF_MSG_TYPE_REFRESH_FL:
+               rc = rvu_sw_fl_stats_sync2db(rvu, req->fl, req->cnt);
+               break;
        }

        return rc;
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_fl.c b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_fl.c
index 1f8b82a84a5d..3cca0672d9cf 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_fl.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_fl.c
@@ -4,12 +4,260 @@
  * Copyright (C) 2026 Marvell.
  *
  */
+
+#include <linux/bitfield.h>
 #include "rvu.h"
+#include "rvu_sw.h"
+#include "rvu_sw_fl.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_fl_offl_wq;
+
+struct fl_entry {
+       struct list_head list;
+       struct rvu *rvu;
+       u32 port_id;
+       unsigned long cookie;
+       struct fl_tuple tuple;
+       u64 flags;
+       u64 features;
+};
+
+static DEFINE_MUTEX(fl_offl_llock);
+static LIST_HEAD(fl_offl_lh);
+static bool fl_offl_work_running;
+
+static struct workqueue_struct *sw_fl_offl_wq;
+static void sw_fl_offl_work_handler(struct work_struct *work);
+static DECLARE_DELAYED_WORK(fl_offl_work, sw_fl_offl_work_handler);
+
+struct sw_fl_stats_node {
+       struct list_head list;
+       unsigned long cookie;
+       u16 mcam_idx[2];
+       u64 opkts, npkts;
+       bool uni_di;
+};
+
+static LIST_HEAD(sw_fl_stats_lh);
+static DEFINE_MUTEX(sw_fl_stats_lock);
+
+static int
+rvu_sw_fl_stats_sync2db_one_entry(unsigned long cookie, u8 disabled,
+                                 u16 mcam_idx[2], bool uni_di, u64 pkts)
+{
+       struct sw_fl_stats_node *snode, *tmp;
+
+       mutex_lock(&sw_fl_stats_lock);
+       list_for_each_entry_safe(snode, tmp, &sw_fl_stats_lh, list) {
+               if (snode->cookie != cookie)
+                       continue;
+
+               if (disabled) {
+                       list_del_init(&snode->list);
+                       mutex_unlock(&sw_fl_stats_lock);
+                       kfree(snode);
+                       return 0;
+               }
+
+               if (snode->uni_di != uni_di) {
+                       snode->uni_di = uni_di;
+                       snode->mcam_idx[1] = mcam_idx[1];
+               }
+
+               if (snode->opkts == pkts) {
+                       mutex_unlock(&sw_fl_stats_lock);
+                       return 0;
+               }
+
+               snode->npkts = pkts;
+               mutex_unlock(&sw_fl_stats_lock);
+               return 0;
+       }
+       mutex_unlock(&sw_fl_stats_lock);
+
+       snode = kcalloc(1, sizeof(*snode), GFP_KERNEL);
Why not kzalloc() instead of kcalloc with size 1?
+       if (!snode)
+               return -ENOMEM;
+
+       snode->cookie = cookie;
+       snode->mcam_idx[0] = mcam_idx[0];
+       if (!uni_di)
+               snode->mcam_idx[1] = mcam_idx[1];
+
+       snode->npkts = pkts;
+       snode->uni_di = uni_di;
+       INIT_LIST_HEAD(&snode->list);
+
+       mutex_lock(&sw_fl_stats_lock);
+       list_add_tail(&snode->list, &sw_fl_stats_lh);
+       mutex_unlock(&sw_fl_stats_lock);
+
+       return 0;
+}
+
+int rvu_sw_fl_stats_sync2db(struct rvu *rvu, struct fl_info *fl, int cnt)
+{
+       struct npc_mcam_get_mul_stats_req *req = NULL;
+       struct npc_mcam_get_mul_stats_rsp *rsp = NULL;
there is no need to initialize these two variables
+       int tot = 0;
+       u16 i2idx_map[256];
follow RCT order
+       int rc = 0;
+       u64 pkts;
+       int idx;
+
+       cnt = min(cnt, 64);
+
+       for (int i = 0; i < cnt; i++) {
I think you can move the declaration of i at the beginning of the
function. it is repeated in the for loops below as well
+               tot++;
+               if (fl[i].uni_di)
+                       continue;
+
+               tot++;
+       }
+
+       req = kcalloc(1, sizeof(*req), GFP_KERNEL);
I think you can use kzalloc kere
+       if (!req) {
+               rc = -ENOMEM;
You can return directly here
+               goto fail;
+       }
+
+       rsp = kcalloc(1, sizeof(*rsp), GFP_KERNEL);
+       if (!rsp) {
+               rc = -ENOMEM;
+               goto fail;
better do individual cleanup by adding a label and use goto free_req
+       }
+
+       req->cnt = tot;
+       idx = 0;
+       for (int i = 0; i < tot; idx++) {
+               i2idx_map[i] = idx;
+               req->entry[i++] = fl[idx].mcam_idx[0];
+               if (fl[idx].uni_di)
+                       continue;
+
+               i2idx_map[i] = idx;
+               req->entry[i++] = fl[idx].mcam_idx[1];
+       }
+
+       if (rvu_mbox_handler_npc_mcam_mul_stats(rvu, req, rsp)) {
+               dev_err(rvu->dev, "Error to get multiple stats\n");
+               rc = -EFAULT;
You can add a new label and use goto free_resp
quoted hunk ↗ jump to hunk
+               goto fail;
+       }
+
+       for (int i = 0; i < tot;) {
+               idx = i2idx_map[i];
+               pkts =  rsp->stat[i++];
+
+               if (!fl[idx].uni_di)
+                       pkts += rsp->stat[i++];
+
+               rc |= rvu_sw_fl_stats_sync2db_one_entry(fl[idx].cookie, fl[idx].dis,
+                                                       fl[idx].mcam_idx,
+                                                       fl[idx].uni_di, pkts);
+       }
+
+fail:
+       kfree(req);
+       kfree(rsp);
+       return rc;
+}
+
+static void sw_fl_offl_dump(struct fl_entry *fl_entry)
+{
+       struct fl_tuple *tuple = &fl_entry->tuple;
+
+       pr_debug("%pI4 to %pI4\n", &tuple->ip4src, &tuple->ip4dst);
+}
+
+static int rvu_sw_fl_offl_rule_push(struct fl_entry *fl_entry)
+{
+       struct af2swdev_notify_req *req;
+       struct rvu *rvu;
+       int swdev_pf;
+
+       rvu = fl_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;
+       }
+
+       req->tuple = fl_entry->tuple;
+       req->flags = fl_entry->flags;
+       req->cookie = fl_entry->cookie;
+       req->features = fl_entry->features;
+
+       sw_fl_offl_dump(fl_entry);
+
+       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 void sw_fl_offl_work_handler(struct work_struct *work)
+{
+       struct fl_entry *fl_entry;
+
+       mutex_lock(&fl_offl_llock);
+       fl_entry = list_first_entry_or_null(&fl_offl_lh, struct fl_entry, list);
+       if (!fl_entry) {
+               mutex_unlock(&fl_offl_llock);
+               return;
+       }
+
+       list_del_init(&fl_entry->list);
+       mutex_unlock(&fl_offl_llock);
+
+       rvu_sw_fl_offl_rule_push(fl_entry);
+       kfree(fl_entry);
+
+       mutex_lock(&fl_offl_llock);
+       if (!list_empty(&fl_offl_lh))
+               queue_delayed_work(sw_fl_offl_wq, &fl_offl_work, msecs_to_jiffies(10));
+       mutex_unlock(&fl_offl_llock);
+}

 int rvu_mbox_handler_fl_get_stats(struct rvu *rvu,
                                  struct fl_get_stats_req *req,
                                  struct fl_get_stats_rsp *rsp)
 {
+       struct sw_fl_stats_node *snode, *tmp;
+
+       mutex_lock(&sw_fl_stats_lock);
+       list_for_each_entry_safe(snode, tmp, &sw_fl_stats_lh, list) {
+               if (snode->cookie != req->cookie)
+                       continue;
+
+               rsp->pkts_diff = snode->npkts - snode->opkts;
+               snode->opkts = snode->npkts;
+               break;
+       }
+       mutex_unlock(&sw_fl_stats_lock);
        return 0;
 }
@@ -17,5 +265,35 @@ int rvu_mbox_handler_fl_notify(struct rvu *rvu,
                               struct fl_notify_req *req,
                               struct msg_rsp *rsp)
 {
+       struct fl_entry *fl_entry;
+
+       if (!(rvu->rswitch.flags & RVU_SWITCH_FLAG_FW_READY))
+               return 0;
+
+       fl_entry = kcalloc(1, sizeof(*fl_entry), GFP_KERNEL);
+       if (!fl_entry)
+               return -ENOMEM;
+
+       fl_entry->port_id = rvu_sw_port_id(rvu, req->hdr.pcifunc);
+       fl_entry->rvu = rvu;
+       INIT_LIST_HEAD(&fl_entry->list);
+       fl_entry->tuple = req->tuple;
+       fl_entry->cookie = req->cookie;
+       fl_entry->flags = req->flags;
+       fl_entry->features = req->features;
+
+       mutex_lock(&fl_offl_llock);
+       list_add_tail(&fl_entry->list, &fl_offl_lh);
+       mutex_unlock(&fl_offl_llock);
+
+       if (!fl_offl_work_running) {
+               sw_fl_offl_wq = alloc_workqueue("sw_af_fl_wq", 0, 0);
+               if (!sw_fl_offl_wq)
free fl_entry here? also, do you want to move list_add_tail() after
this if() condition?
quoted hunk ↗ jump to hunk
+                       return -ENOMEM;
+
+               fl_offl_work_running = true;
+       }
+       queue_delayed_work(sw_fl_offl_wq, &fl_offl_work, msecs_to_jiffies(10));
+
        return 0;
 }
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_fl.h b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_fl.h
index cf3e5b884f77..aa375413bc14 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_fl.h
+++ b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_fl.h
@@ -8,4 +8,6 @@
 #ifndef RVU_SW_FL_H
 #define RVU_SW_FL_H

+int rvu_sw_fl_stats_sync2db(struct rvu *rvu, struct fl_info *fl, int cnt);
+
 #endif
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c
index 26a08d2cfbb1..716764d74e6a 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c
@@ -20,6 +20,7 @@
 #include "cn10k.h"
 #include "otx2_common.h"
 #include "qos.h"
+#include "switch/sw_fl.h"

 #define CN10K_MAX_BURST_MANTISSA       0x7FFFULL
 #define CN10K_MAX_BURST_SIZE           8453888ULL
@@ -1238,7 +1239,6 @@ static int otx2_tc_del_flow(struct otx2_nic *nic,
                mutex_unlock(&nic->mbox.lock);
        }

-
 free_mcam_flow:
        otx2_del_mcam_flow_entry(nic, flow_node->entry, NULL);
        otx2_tc_update_mcam_table(nic, flow_cfg, flow_node, false);
@@ -1595,11 +1595,26 @@ static int otx2_setup_tc_block(struct net_device *netdev,
 int otx2_setup_tc(struct net_device *netdev, enum tc_setup_type type,
                  void *type_data)
 {
+       struct otx2_nic *nic = netdev_priv(netdev);
+
        switch (type) {
        case TC_SETUP_BLOCK:
+               if (netif_is_ovs_port(netdev)) {
+                       return flow_block_cb_setup_simple(type_data,
+                                                         &otx2_block_cb_list,
+                                                         sw_fl_setup_ft_block_ingress_cb,
+                                                         nic, nic, true);
+               }
braces are not required here
quoted hunk ↗ jump to hunk
+
                return otx2_setup_tc_block(netdev, type_data);
        case TC_SETUP_QDISC_HTB:
                return otx2_setup_tc_htb(netdev, type_data);
+
+       case TC_SETUP_FT:
+               return flow_block_cb_setup_simple(type_data,
+                                                 &otx2_block_cb_list,
+                                                 sw_fl_setup_ft_block_ingress_cb,
+                                                 nic, nic, true);
        default:
                return -EOPNOTSUPP;
        }
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.c b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.c
index 36a2359a0a48..fbb56f9bede9 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.c
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.c
@@ -4,13 +4,533 @@
  * 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/nexthop.h>
+#include <net/netfilter/nf_flow_table.h>
+
+#include "../otx2_reg.h"
+#include "../otx2_common.h"
+#include "../otx2_struct.h"
+#include "../cn10k.h"
+#include "sw_nb.h"
 #include "sw_fl.h"

+#if !IS_ENABLED(CONFIG_OCTEONTX_SWITCH)
+int sw_fl_setup_ft_block_ingress_cb(enum tc_setup_type type,
+                                   void *type_data, void *cb_priv)
+{
+       return -EOPNOTSUPP;
+}
+
+#else
+
+static DEFINE_SPINLOCK(sw_fl_lock);
+static LIST_HEAD(sw_fl_lh);
+
+struct sw_fl_list_entry {
+       struct list_head list;
+       u64 flags;
+       unsigned long cookie;
+       struct otx2_nic *pf;
+       struct fl_tuple tuple;
+};
+
+static struct workqueue_struct *sw_fl_wq;
+static struct work_struct sw_fl_work;
+
+static int sw_fl_msg_send(struct otx2_nic *pf,
+                         struct fl_tuple *tuple,
+                         u64 flags,
+                         unsigned long cookie)
+{
+       struct fl_notify_req *req;
+       int rc;
+
+       mutex_lock(&pf->mbox.lock);
+       req = otx2_mbox_alloc_msg_fl_notify(&pf->mbox);
+       if (!req) {
+               rc = -ENOMEM;
+               goto out;
+       }
+
+       req->tuple = *tuple;
+       req->flags = flags;
+       req->cookie = cookie;
+
+       rc = otx2_sync_mbox_msg(&pf->mbox);
+out:
+       mutex_unlock(&pf->mbox.lock);
+       return rc;
+}
+
+static void sw_fl_wq_handler(struct work_struct *work)
+{
+       struct sw_fl_list_entry *entry;
+       LIST_HEAD(tlist);
+
+       spin_lock(&sw_fl_lock);
+       list_splice_init(&sw_fl_lh, &tlist);
+       spin_unlock(&sw_fl_lock);
+
+       while ((entry =
+               list_first_entry_or_null(&tlist,
+                                        struct sw_fl_list_entry,
+                                        list)) != NULL) {
+               list_del_init(&entry->list);
+               sw_fl_msg_send(entry->pf, &entry->tuple,
+                              entry->flags, entry->cookie);
+               kfree(entry);
+       }
+
+       spin_lock(&sw_fl_lock);
+       if (!list_empty(&sw_fl_lh))
+               queue_work(sw_fl_wq, &sw_fl_work);
+       spin_unlock(&sw_fl_lock);
+}
+
+static int
+sw_fl_add_to_list(struct otx2_nic *pf, struct fl_tuple *tuple,
+                 unsigned long cookie, bool add_fl)
+{
+       struct sw_fl_list_entry *entry;
+
+       entry = kcalloc(1, sizeof(*entry), GFP_ATOMIC);
+       if (!entry)
+               return -ENOMEM;
+
+       entry->pf = pf;
+       entry->flags = add_fl ? FL_ADD : FL_DEL;
+       if (add_fl)
+               entry->tuple = *tuple;
+       entry->cookie = cookie;
+       entry->tuple.uni_di = netif_is_ovs_port(pf->netdev);
+
+       spin_lock(&sw_fl_lock);
+       list_add_tail(&entry->list, &sw_fl_lh);
+       queue_work(sw_fl_wq, &sw_fl_work);
+       spin_unlock(&sw_fl_lock);
+
+       return 0;
+}
+
+static int sw_fl_parse_actions(struct otx2_nic *nic,
+                              struct flow_action *flow_action,
+                              struct flow_cls_offload *f,
+                              struct fl_tuple *tuple, u64 *op)
+{
+       struct flow_action_entry *act;
+       struct otx2_nic *out_nic;
+       int err;
+       int used = 0;
RCT order here
+       int i;
+
+       if (!flow_action_has_entries(flow_action))
+               return -EINVAL;
+
+       flow_action_for_each(i, act, flow_action) {
+               WARN_ON(used >= MANGLE_ARR_SZ);
+
+               switch (act->id) {
+               case FLOW_ACTION_REDIRECT:
+                       tuple->in_pf = nic->pcifunc;
+                       out_nic = netdev_priv(act->dev);
+                       tuple->xmit_pf = out_nic->pcifunc;
+                       *op |= BIT_ULL(FLOW_ACTION_REDIRECT);
+                       break;
+
+               case FLOW_ACTION_CT:
+                       err = nf_flow_table_offload_add_cb(act->ct.flow_table,
+                                                          sw_fl_setup_ft_block_ingress_cb,
+                                                          nic);
+                       if (err != -EEXIST && err) {
+                               pr_err("%s:%d Error to offload flow, err=%d\n",
+                                      __func__, __LINE__, err);
+                               break;
+                       }
+
+                       *op |= BIT_ULL(FLOW_ACTION_CT);
+                       break;
+
+               case FLOW_ACTION_MANGLE:
+                       tuple->mangle[used].type = act->mangle.htype;
+                       tuple->mangle[used].val = act->mangle.val;
+                       tuple->mangle[used].mask = act->mangle.mask;
+                       tuple->mangle[used].offset = act->mangle.offset;
+                       tuple->mangle_map[act->mangle.htype] |= BIT(used);
+                       used++;
+                       break;
+
+               default:
+                       break;
+               }
+       }
+
+       tuple->mangle_cnt = used;
+
+       if (!*op) {
+               pr_debug("%s:%d Op is not valid\n", __func__, __LINE__);
+               return -EOPNOTSUPP;
+       }
+
+       return 0;
+}
+
+static int sw_fl_get_route(struct fib_result *res, __be32 addr)
+{
+       struct flowi4 fl4;
+
+       memset(&fl4, 0, sizeof(fl4));
+       fl4.daddr = addr;
+       return fib_lookup(&init_net, &fl4, res, 0);
+}
+
+static int sw_fl_get_pcifunc(__be32 dst, u16 *pcifunc, struct fl_tuple *ftuple, bool is_in_dev)
+{
+       struct fib_nh_common *fib_nhc;
+       struct net_device *dev, *br;
+       struct otx2_nic *nic;
+       struct fib_result res;
+       struct list_head *lh;
+       int err;
+
+       rcu_read_lock();
+
+       err = sw_fl_get_route(&res, dst);
+       if (err) {
+               pr_err("%s:%d Failed to find route to dst %pI4\n",
+                      __func__, __LINE__, &dst);
+               goto done;
+       }
+
+       if (res.fi->fib_type != RTN_UNICAST) {
+               pr_err("%s:%d Not unicast  route to dst %pi4\n",
+                      __func__, __LINE__, &dst);
+               err = -EFAULT;
+               goto done;
+       }
+
+       fib_nhc = fib_info_nhc(res.fi, 0);
+       if (!fib_nhc) {
+               err = -EINVAL;
+               pr_err("%s:%d Could not get fib_nhc for %pI4\n",
+                      __func__, __LINE__, &dst);
+               goto done;
+       }
+
+       if (unlikely(netif_is_bridge_master(fib_nhc->nhc_dev))) {
+               br = fib_nhc->nhc_dev;
+
+               if (is_in_dev)
+                       ftuple->is_indev_br = 1;
+               else
+                       ftuple->is_xdev_br = 1;
+
+               lh = &br->adj_list.lower;
+               if (list_empty(lh)) {
+                       pr_err("%s:%d Unable to find any slave device\n",
+                              __func__, __LINE__);
+                       err = -EINVAL;
+                       goto done;
+               }
+               dev = netdev_next_lower_dev_rcu(br, &lh);
+
+       } else {
+               dev = fib_nhc->nhc_dev;
+       }
+
+       if (!sw_nb_is_valid_dev(dev)) {
+               pr_err("%s:%d flow acceleration support is only for cavium devices\n",
+                      __func__, __LINE__);
+               err = -EOPNOTSUPP;
+               goto done;
+       }
+
+       nic = netdev_priv(dev);
+       *pcifunc = nic->pcifunc;
+
+done:
+       rcu_read_unlock();
+       return err;
+}
+
+static int sw_fl_parse_flow(struct otx2_nic *nic, struct flow_cls_offload *f,
+                           struct fl_tuple *tuple, u64 *features)
+{
+       struct flow_rule *rule;
+       u8 ip_proto = 0;
+
+       *features = 0;
+
+       rule = flow_cls_offload_flow_rule(f);
+
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) {
+               struct flow_match_basic match;
+
+               flow_rule_match_basic(rule, &match);
+
+               /* All EtherTypes can be matched, no hw limitation */
+
+               if (match.mask->n_proto) {
+                       tuple->eth_type = match.key->n_proto;
+                       tuple->m_eth_type = match.mask->n_proto;
+                       *features |= BIT_ULL(NPC_ETYPE);
+               }
+
+               if (match.mask->ip_proto &&
+                   (match.key->ip_proto != IPPROTO_TCP &&
+                    match.key->ip_proto != IPPROTO_UDP)) {
+                       netdev_dbg(nic->netdev,
+                                  "ip_proto=%u not supported\n",
+                                  match.key->ip_proto);
+               }
+
+               if (match.mask->ip_proto)
+                       ip_proto = match.key->ip_proto;
+
+               if (ip_proto == IPPROTO_UDP) {
+                       *features |= BIT_ULL(NPC_IPPROTO_UDP);
+               } else if (ip_proto == IPPROTO_TCP) {
+                       *features |= BIT_ULL(NPC_IPPROTO_TCP);
+               } else {
+                       netdev_dbg(nic->netdev,
+                                  "ip_proto=%u not supported\n",
+                                  match.key->ip_proto);
+               }
+
+               tuple->proto = ip_proto;
+       }
+
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
+               struct flow_match_eth_addrs match;
+
+               flow_rule_match_eth_addrs(rule, &match);
+
+               if (!is_zero_ether_addr(match.key->dst) &&
+                   is_unicast_ether_addr(match.key->dst)) {
+                       ether_addr_copy(tuple->dmac,
+                                       match.key->dst);
+
+                       ether_addr_copy(tuple->m_dmac,
+                                       match.mask->dst);
+
+                       *features |= BIT_ULL(NPC_DMAC);
+               }
+
+               if (!is_zero_ether_addr(match.key->src) &&
+                   is_unicast_ether_addr(match.key->src)) {
+                       ether_addr_copy(tuple->smac,
+                                       match.key->src);
+                       ether_addr_copy(tuple->m_smac,
+                                       match.mask->src);
+                       *features |= BIT_ULL(NPC_SMAC);
+               }
+       }
+
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV4_ADDRS)) {
+               struct flow_match_ipv4_addrs match;
+
+               flow_rule_match_ipv4_addrs(rule, &match);
+
+               if (match.key->dst) {
+                       tuple->ip4dst = match.key->dst;
+                       tuple->m_ip4dst = match.mask->dst;
+                       *features |= BIT_ULL(NPC_DIP_IPV4);
+               }
+
+               if (match.key->src) {
+                       tuple->ip4src = match.key->src;
+                       tuple->m_ip4src = match.mask->src;
+                       *features |= BIT_ULL(NPC_SIP_IPV4);
+               }
+       }
+
+       if (!(*features & BIT_ULL(NPC_DMAC))) {
+               if (!tuple->ip4src || !tuple->ip4dst) {
+                       pr_err("%s:%d Invalid src=%pI4 and dst=%pI4 addresses\n",
+                              __func__, __LINE__, &tuple->ip4src, &tuple->ip4dst);
+                       return -EINVAL;
+               }
+
+               if ((tuple->ip4src & tuple->m_ip4src) == (tuple->ip4dst & tuple->m_ip4dst)) {
+                       pr_err("%s:%d Masked values are same; Invalid src=%pI4 and dst=%pI4 addresses\n",
+                              __func__, __LINE__, &tuple->ip4src, &tuple->ip4dst);
+                       return -EINVAL;
+               }
+       }
+
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS)) {
+               struct flow_match_ports match;
+
+               flow_rule_match_ports(rule, &match);
+
+               if (ip_proto == IPPROTO_UDP) {
+                       if (match.key->dst)
+                               *features |= BIT_ULL(NPC_DPORT_UDP);
+
+                       if (match.key->src)
+                               *features |= BIT_ULL(NPC_SPORT_UDP);
+               } else if (ip_proto == IPPROTO_TCP) {
+                       if (match.key->dst)
+                               *features |= BIT_ULL(NPC_DPORT_TCP);
+
+                       if (match.key->src)
+                               *features |= BIT_ULL(NPC_SPORT_TCP);
+               }
+
+               if (match.mask->src) {
+                       tuple->sport = match.key->src;
+                       tuple->m_sport = match.mask->src;
+               }
+
+               if (match.mask->dst) {
+                       tuple->dport = match.key->dst;
+                       tuple->m_dport = match.mask->dst;
+               }
+       }
+
+       if (!(*features & (BIT_ULL(NPC_DMAC) |
+                          BIT_ULL(NPC_SMAC) |
+                          BIT_ULL(NPC_DIP_IPV4) |
+                          BIT_ULL(NPC_SIP_IPV4) |
+                          BIT_ULL(NPC_DPORT_UDP) |
+                          BIT_ULL(NPC_SPORT_UDP) |
+                          BIT_ULL(NPC_DPORT_TCP) |
+                          BIT_ULL(NPC_SPORT_TCP)))) {
+               return -EINVAL;
+       }
+
+       tuple->features = *features;
+
+       return 0;
+}
+
+static int sw_fl_add(struct otx2_nic *nic, struct flow_cls_offload *f)
+{
+       struct fl_tuple tuple = { 0 };
+       struct flow_rule *rule;
+       u64 features = 0;
+       u64 op = 0;
+       int rc;
+
+       rule = flow_cls_offload_flow_rule(f);
+
+       rc = sw_fl_parse_actions(nic, &rule->action, f, &tuple, &op);
+       if (rc)
+               return rc;
+
+       if (op & BIT_ULL(FLOW_ACTION_CT))
+               return 0;
+
+       rc  = sw_fl_parse_flow(nic, f, &tuple, &features);
+       if (rc)
+               return -EFAULT;
+
+       if (!netif_is_ovs_port(nic->netdev)) {
+               rc = sw_fl_get_pcifunc(tuple.ip4src, &tuple.in_pf, &tuple, true);
+               if (rc)
+                       return rc;
+
+               rc = sw_fl_get_pcifunc(tuple.ip4dst, &tuple.xmit_pf, &tuple, false);
+               if (rc)
+                       return rc;
+       }
+
+       sw_fl_add_to_list(nic, &tuple, f->cookie, true);
+       return 0;
+}
+
+static int sw_fl_del(struct otx2_nic *nic, struct flow_cls_offload *f)
function prototype can be changed to void?
quoted hunk ↗ jump to hunk
+{
+       sw_fl_add_to_list(nic, NULL, f->cookie, false);
+       return 0;
+}
+
+static int sw_fl_stats(struct otx2_nic *nic, struct flow_cls_offload *f)
+{
+       struct fl_get_stats_req *req;
+       struct fl_get_stats_rsp *rsp;
+       u64 pkts_diff;
+       int rc = 0;
+
+       mutex_lock(&nic->mbox.lock);
+
+       req = otx2_mbox_alloc_msg_fl_get_stats(&nic->mbox);
+       if (!req) {
+               pr_err("%s:%d Error happened while mcam alloc req\n", __func__, __LINE__);
+               rc = -ENOMEM;
+               goto fail;
+       }
+       req->cookie = f->cookie;
+
+       rc = otx2_sync_mbox_msg(&nic->mbox);
+       if (rc)
+               goto fail;
+
+       rsp = (struct fl_get_stats_rsp *)otx2_mbox_get_rsp
+               (&nic->mbox.mbox, 0, &req->hdr);
+       if (IS_ERR(rsp)) {
+               rc = PTR_ERR(rsp);
+               goto fail;
+       }
+
+       pkts_diff = rsp->pkts_diff;
+       mutex_unlock(&nic->mbox.lock);
+
+       if (pkts_diff) {
+               flow_stats_update(&f->stats, 0x0, pkts_diff,
+                                 0x0, jiffies,
+                                 FLOW_ACTION_HW_STATS_IMMEDIATE);
+       }
+       return 0;
+fail:
+       mutex_unlock(&nic->mbox.lock);
+       return rc;
+}
+
+static bool init_done;
+
+int sw_fl_setup_ft_block_ingress_cb(enum tc_setup_type type,
+                                   void *type_data, void *cb_priv)
+{
+       struct flow_cls_offload *cls = type_data;
+       struct otx2_nic *nic = cb_priv;
+
+       if (!init_done)
+               return 0;
+
+       switch (cls->command) {
+       case FLOW_CLS_REPLACE:
+               return sw_fl_add(nic, cls);
+       case FLOW_CLS_DESTROY:
+               return sw_fl_del(nic, cls);
+       case FLOW_CLS_STATS:
+               return sw_fl_stats(nic, cls);
+       default:
+               break;
+       }
+
+       return -EOPNOTSUPP;
+}
+
 int sw_fl_init(void)
 {
+       INIT_WORK(&sw_fl_work, sw_fl_wq_handler);
+       sw_fl_wq = alloc_workqueue("sw_fl_wq", 0, 0);
+       if (!sw_fl_wq)
+               return -ENOMEM;
+
+       init_done = true;
        return 0;
 }

 void sw_fl_deinit(void)
 {
+       cancel_work_sync(&sw_fl_work);
+       destroy_workqueue(sw_fl_wq);
 }
+#endif
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.h b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.h
index cd018d770a8a..8dd816eb17d2 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.h
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.h
@@ -9,5 +9,7 @@

 void sw_fl_deinit(void);
 int sw_fl_init(void);
+int sw_fl_setup_ft_block_ingress_cb(enum tc_setup_type type,
+                                   void *type_data, void *cb_priv);

 #endif // SW_FL_H
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 7a0ed52eae95..c316aeac2e81 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.c
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.c
@@ -21,7 +21,6 @@
 #include "sw_fdb.h"
 #include "sw_fib.h"
 #include "sw_fl.h"
-#include "sw_nb.h"

 static const char *sw_nb_cmd2str[OTX2_CMD_MAX] = {
        [OTX2_DEV_UP]  = "OTX2_DEV_UP",
--
2.43.0

--
Regards,
Kalesh AP

Attachments

  • smime.p7s [application/pkcs7-signature] 5509 bytes
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help