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); + #endifdiff --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); } +#endifdiff --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_Hdiff --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