[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <CAH-L+nMLUUxg9=NgonGHveNogAiqf3s_-Qb0TMm8G+tMh4g3WA@mail.gmail.com>
Date: Thu, 8 Jan 2026 21:37:44 +0530
From: Kalesh Anakkur Purayil <kalesh-anakkur.purayil@...adcom.com>
To: Ratheesh Kannoth <rkannoth@...vell.com>
Cc: netdev@...r.kernel.org, linux-kernel@...r.kernel.org,
andrew+netdev@...n.ch, sgoutham@...vell.com, davem@...emloft.net,
edumazet@...gle.com, kuba@...nel.org, pabeni@...hat.com
Subject: Re: [PATCH net-next v2 09/10] octeontx2: switch: Flow offload support
On Wed, Jan 7, 2026 at 7:23 PM Ratheesh Kannoth <rkannoth@...vell.com> wrote:
>
> 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 <rkannoth@...vell.com>
> ---
> .../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
> + 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?
> + 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
> +
> 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?
> +{
> + 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
Download attachment "smime.p7s" of type "application/pkcs7-signature" (5509 bytes)
Powered by blists - more mailing lists