| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/07/02 11:16 | flow | patch-triage |
5mArgs:null Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"Reasoning": "The patch adds switchdev support for the Marvell OcteonTX2 hardware driver. The new code paths in the NIC driver are initialized via devlink, which requires the driver to probe successfully. However, the driver probe relies on mailbox communication with the Admin Function (AF) driver, which depends on hardware-specific interrupts and shared memory semantics. In a virtualized environment without the actual Marvell hardware (or a dedicated software emulator), the mailbox communication times out, causing the probe to fail. Consequently, the devlink device is never registered, and the switch code remains unreachable. Similarly, the switch code in the AF driver is triggered by mailbox messages processed via hardware interrupts, which are also unreachable. Therefore, this patch cannot be fuzzed in virtualized environments like GCE or QEMU.",
"WorthFuzzing": false
} |
| 1/1 | 2026/07/02 11:16 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit bcdfffc32172b90440145e343e7cb37b2a141d8d\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Thu Jul 2 11:16:27 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/drivers/net/ethernet/marvell/octeontx2/Kconfig b/drivers/net/ethernet/marvell/octeontx2/Kconfig\nindex 47e549c581f0d2..e2fb6dd71078b2 100644\n--- a/drivers/net/ethernet/marvell/octeontx2/Kconfig\n+++ b/drivers/net/ethernet/marvell/octeontx2/Kconfig\n@@ -28,6 +28,16 @@ config NDC_DIS_DYNAMIC_CACHING\n \t , NPA stack pages etc in NDC. Also locks down NIX SQ/CQ/RQ/RSS and\n \t NPA Aura/Pool contexts.\n \n+config OCTEONTX_SWITCH\n+\tbool \"Marvell OcteonTX2 switch driver\"\n+\tdepends on (64BIT \u0026\u0026 COMPILE_TEST) || ARM64\n+\tdepends on OCTEONTX2_PF\n+\tdefault n\n+\thelp\n+\t This driver supports Marvell's OcteonTX2 switch.\n+\t Marvell SWITCH HW can offload L2, L3 flow. ARM core interacts\n+\t with Marvell SW HW thru mbox.\n+\n config OCTEONTX2_PF\n \ttristate \"Marvell OcteonTX2 NIC Physical Function driver\"\n \tselect OCTEONTX2_MBOX\ndiff --git a/drivers/net/ethernet/marvell/octeontx2/af/Makefile b/drivers/net/ethernet/marvell/octeontx2/af/Makefile\nindex 91b7d6e96a6148..73f20a44f1a0fa 100644\n--- a/drivers/net/ethernet/marvell/octeontx2/af/Makefile\n+++ b/drivers/net/ethernet/marvell/octeontx2/af/Makefile\n@@ -3,7 +3,7 @@\n # Makefile for Marvell's RVU Admin Function driver\n #\n \n-ccflags-y += -I$(src)\n+ccflags-y += -I$(src) -I$(src)/switch/\n obj-$(CONFIG_OCTEONTX2_MBOX) += rvu_mbox.o\n obj-$(CONFIG_OCTEONTX2_AF) += rvu_af.o\n \n@@ -12,5 +12,6 @@ rvu_af-y := cgx.o rvu.o rvu_cgx.o rvu_npa.o rvu_nix.o \\\n \t\t rvu_reg.o rvu_npc.o rvu_debugfs.o ptp.o rvu_npc_fs.o \\\n \t\t rvu_cpt.o rvu_devlink.o rpm.o rvu_cn10k.o rvu_switch.o \\\n \t\t rvu_sdp.o rvu_npc_hash.o mcs.o mcs_rvu_if.o mcs_cnf10kb.o \\\n+\t\t switch/rvu_sw.o switch/rvu_sw_l2.o switch/rvu_sw_l3.o switch/rvu_sw_fl.o \\\n \t\t rvu_rep.o cn20k/mbox_init.o cn20k/nix.o cn20k/debugfs.o \\\n \t\t cn20k/npa.o cn20k/npc.o\ndiff --git a/drivers/net/ethernet/marvell/octeontx2/af/mbox.h b/drivers/net/ethernet/marvell/octeontx2/af/mbox.h\nindex 714e47f68d9320..ae6f145a0db013 100644\n--- a/drivers/net/ethernet/marvell/octeontx2/af/mbox.h\n+++ b/drivers/net/ethernet/marvell/octeontx2/af/mbox.h\n@@ -164,6 +164,18 @@ M(PTP_GET_CAP,\t\t0x00c, ptp_get_cap, msg_req, ptp_get_cap_rsp)\t\\\n M(GET_REP_CNT,\t\t0x00d, get_rep_cnt, msg_req, get_rep_cnt_rsp)\t\\\n M(ESW_CFG,\t\t0x00e, esw_cfg, esw_cfg_req, msg_rsp)\t\\\n M(REP_EVENT_NOTIFY, 0x00f, rep_event_notify, rep_event, msg_rsp) \\\n+M(FDB_NOTIFY,\t\t0x010, fdb_notify,\t\t\t\t\\\n+\t\t\t\tfdb_notify_req, msg_rsp)\t\t\\\n+M(FIB_NOTIFY,\t\t0x011, fib_notify,\t\t\t\t\\\n+\t\t\t\tfib_notify_req, msg_rsp)\t\t\\\n+M(FL_NOTIFY,\t\t0x012, fl_notify,\t\t\t\t\\\n+\t\t\t\tfl_notify_req, msg_rsp)\t\t\\\n+M(FL_GET_STATS,\t\t0x013, fl_get_stats,\t\t\t\t\\\n+\t\t\t\tfl_get_stats_req, fl_get_stats_rsp)\t\\\n+M(GET_IFACE_GET_INFO,\t0x014, iface_get_info, msg_req,\t\\\n+\t\t\t\tiface_get_info_rsp)\t\t\t\\\n+M(SWDEV2AF_NOTIFY,\t0x015, swdev2af_notify,\t\t\\\n+\t\t\t\tswdev2af_notify_req, msg_rsp)\t\t\\\n /* CGX mbox IDs (range 0x200 - 0x3FF) */\t\t\t\t\\\n M(CGX_START_RXTX,\t0x200, cgx_start_rxtx, msg_req, msg_rsp)\t\\\n M(CGX_STOP_RXTX,\t0x201, cgx_stop_rxtx, msg_req, msg_rsp)\t\t\\\n@@ -309,6 +321,14 @@ M(NPC_MCAM_GET_DFT_RL_IDXS, 0x601e, npc_get_dft_rl_idxs,\t\\\n M(NPC_MCAM_GET_NPC_PFL_INFO, 0x601f, npc_get_pfl_info,\t\t\\\n \t\t\t\t\tmsg_req,\t\t\\\n \t\t\t\t\tnpc_get_pfl_info_rsp)\t\\\n+M(NPC_MCAM_FLOW_DEL_N_FREE,\t0x6020, npc_flow_del_n_free,\t\t\\\n+\t\t\t\t npc_flow_del_n_free_req, msg_rsp)\t\\\n+M(NPC_MCAM_GET_MUL_STATS, 0x6021, npc_mcam_mul_stats,\t\t\t\\\n+\t\t\t\t npc_mcam_get_mul_stats_req,\t\t\\\n+\t\t\t\t npc_mcam_get_mul_stats_rsp)\t\t\\\n+M(NPC_MCAM_GET_FEATURES, 0x6022, npc_mcam_get_features,\t\t\t\\\n+\t\t\t\t msg_req,\t\t\t\t\\\n+\t\t\t\t npc_mcam_get_features_rsp)\t\t\\\n /* NIX mbox IDs (range 0x8000 - 0xFFFF) */\t\t\t\t\\\n M(NIX_LF_ALLOC,\t\t0x8000, nix_lf_alloc,\t\t\t\t\\\n \t\t\t\t nix_lf_alloc_req, nix_lf_alloc_rsp)\t\\\n@@ -438,6 +458,12 @@ M(MCS_INTR_NOTIFY,\t0xE00, mcs_intr_notify, mcs_intr_info, msg_rsp)\n #define MBOX_UP_REP_MESSAGES\t\t\t\t\t\t\\\n M(REP_EVENT_UP_NOTIFY,\t0xEF0, rep_event_up_notify, rep_event, msg_rsp) \\\n \n+#define MBOX_UP_AF2SWDEV_MESSAGES\t\t\t\t\t\\\n+M(AF2SWDEV,\t0xEF1, af2swdev_notify, af2swdev_notify_req, msg_rsp)\n+\n+#define MBOX_UP_AF2PF_FDB_REFRESH_MESSAGES\t\t\t\t\t\\\n+M(AF2PF_FDB_REFRESH, 0xEF2, af2pf_fdb_refresh, af2pf_fdb_refresh_req, msg_rsp)\n+\n enum {\n #define M(_name, _id, _1, _2, _3) MBOX_MSG_ ## _name = _id,\n MBOX_MESSAGES\n@@ -445,6 +471,8 @@ MBOX_UP_CGX_MESSAGES\n MBOX_UP_CPT_MESSAGES\n MBOX_UP_MCS_MESSAGES\n MBOX_UP_REP_MESSAGES\n+MBOX_UP_AF2SWDEV_MESSAGES\n+MBOX_UP_AF2PF_FDB_REFRESH_MESSAGES\n #undef M\n };\n \n@@ -1129,6 +1157,8 @@ struct nix_txsch_alloc_req {\n \t/* Scheduler queue count request at each level */\n \tu16 schq_contig[NIX_TXSCH_LVL_CNT]; /* No of contiguous queues */\n \tu16 schq[NIX_TXSCH_LVL_CNT]; /* No of non-contiguous queues */\n+#define NIX_TXSCH_ALLOC_FLAG_PAN BIT_ULL(0)\n+\tu64 flags;\n };\n \n struct nix_txsch_alloc_rsp {\n@@ -1147,6 +1177,7 @@ struct nix_txsch_alloc_rsp {\n struct nix_txsch_free_req {\n \tstruct mbox_msghdr hdr;\n #define TXSCHQ_FREE_ALL BIT_ULL(0)\n+#define TXSCHQ_FREE_PAN_TL1 BIT_ULL(1)\n \tu16 flags;\n \t/* Scheduler queue level to be freed */\n \tu16 schq_lvl;\n@@ -1579,6 +1610,30 @@ struct npc_mcam_alloc_entry_rsp {\n \tu16 entry_list[NPC_MAX_NONCONTIG_ENTRIES];\n };\n \n+struct npc_flow_del_n_free_req {\n+\tstruct mbox_msghdr hdr;\n+\tu16 cnt;\n+\tu16 entry[256]; /* Entry index to be freed */\n+};\n+\n+struct npc_mcam_get_features_rsp {\n+\tstruct mbox_msghdr hdr;\n+\tu64 rx_features;\n+\tu64 tx_features;\n+};\n+\n+struct npc_mcam_get_mul_stats_req {\n+\tstruct mbox_msghdr hdr;\n+\tint cnt;\n+\tu16 entry[256]; /* mcam entry */\n+};\n+\n+struct npc_mcam_get_mul_stats_rsp {\n+\tstruct mbox_msghdr hdr;\n+\tint cnt;\n+\tu64 stat[256]; /* counter stats */\n+};\n+\n struct npc_mcam_free_entry_req {\n \tstruct mbox_msghdr hdr;\n \tu16 entry; /* Entry index to be freed */\n@@ -1781,6 +1836,7 @@ struct esw_cfg_req {\n \tstruct mbox_msghdr hdr;\n \tu8 ena;\n \tu64 rsvd;\n+\tunsigned char switch_id[MAX_PHYS_ITEM_ID_LEN];\n };\n \n struct rep_evt_data {\n@@ -1805,6 +1861,179 @@ struct rep_event {\n \tstruct rep_evt_data evt_data;\n };\n \n+#define FDB_ADD BIT_ULL(0)\n+#define FDB_DEL\t BIT_ULL(1)\n+#define FIB_CMD\t BIT_ULL(2)\n+#define FL_ADD\t BIT_ULL(3)\n+#define FL_DEL\t BIT_ULL(4)\n+#define DP_ADD\t BIT_ULL(5)\n+\n+struct fdb_notify_req {\n+\tstruct mbox_msghdr hdr;\n+\tu64 flags;\n+\tu8 mac[ETH_ALEN];\n+};\n+\n+struct fib_entry {\n+\tu64 cmd;\n+\tu64 gw_valid : 1;\n+\tu64 mac_valid : 1;\n+\tu64 vlan_valid: 1;\n+\tu64 host : 1;\n+\tu64 bridge : 1;\n+\tu64 ipv6 : 1;\n+\tu16 vlan_tag;\n+\tu32 dst_len;\n+\tu8 dst6_plen;\n+\tu8 gw6_plen;\n+\tunion {\n+\t\tu32 dst;\n+\t\tu32 dst6[4];\n+\t};\n+\tunion {\n+\t\tu32 gw;\n+\t\tu32 gw6[4];\n+\t};\n+\tu16 port_id;\n+\tu8 nud_state;\n+\tu8 mac[ETH_ALEN];\n+};\n+\n+struct fib_notify_req {\n+\tstruct mbox_msghdr hdr;\n+\tu16 cnt;\n+\tstruct fib_entry entry[16];\n+};\n+\n+struct fl_tuple {\n+\t__be32 ip4src;\n+\t__be32 m_ip4src;\n+\t__be32 ip4dst;\n+\t__be32 m_ip4dst;\n+\t__be16 sport;\n+\t__be16 m_sport;\n+\t__be16 dport;\n+\t__be16 m_dport;\n+\t__be16 eth_type;\n+\t__be16 m_eth_type;\n+\tu8 proto;\n+\tu8 smac[6];\n+\tu8 m_smac[6];\n+\tu8 dmac[6];\n+\tu8 m_dmac[6];\n+\tu64 is_xdev_br : 1;\n+\tu64 is_indev_br : 1;\n+\tu64 uni_di : 1;\n+\tu16 in_pf;\n+\tu16 xmit_pf;\n+\tu64 features;\n+\tstruct {\t\t\t\t/* FLOW_ACTION_MANGLE */\n+\t\tu8\t\toffset;\n+\t\tu8\t\ttype;\n+\t\tu32\t\tmask;\n+\t\tu32\t\tval;\n+#define MANGLE_ARR_SZ 9\n+\t} mangle[MANGLE_ARR_SZ]; /* 2 for ETH, 1 for VLAN, 4 for IPv6, 2 for L4. */\n+#define MANGLE_LAYER_CNT 4\n+\tu8 mangle_map[MANGLE_LAYER_CNT]; /* 1 for ETH, 1 for VLAN, 1 for L3, 1 for L4 */\n+\tu8 mangle_cnt;\n+};\n+\n+struct fl_notify_req {\n+\tstruct mbox_msghdr hdr;\n+\tunsigned long cookie;\n+\tu64 flags;\n+\tu64 features;\n+\tstruct fl_tuple tuple;\n+};\n+\n+struct fl_get_stats_req {\n+\tstruct mbox_msghdr hdr;\n+\tunsigned long cookie;\n+};\n+\n+struct fl_get_stats_rsp {\n+\tstruct mbox_msghdr hdr;\n+\tu64 pkts_diff;\n+};\n+\n+struct af2swdev_notify_req {\n+\tstruct mbox_msghdr hdr;\n+\tu64 flags;\n+\tu32 port_id;\n+\tu32 switch_id;\n+\tunion {\n+\t\tstruct {\n+\t\t\tu8 mac[6];\n+\t\t};\n+\t\tstruct {\n+\t\t\tu8 cnt;\n+\t\t\tstruct fib_entry entry[12];\n+\t\t};\n+\n+\t\tstruct {\n+\t\t\tunsigned long cookie;\n+\t\t\tu64 features;\n+\t\t\tstruct fl_tuple tuple;\n+\t\t};\n+\t};\n+};\n+\n+struct af2pf_fdb_refresh_req {\n+\tstruct mbox_msghdr hdr;\n+\tu16 pcifunc;\n+\tu8 mac[6];\n+};\n+\n+struct iface_info {\n+\tu64 is_vf :1;\n+\tu64 is_sdp :1;\n+\tu16 pcifunc;\n+\tu16 rx_chan_base;\n+\tu16 tx_chan_base;\n+\tu16 sq_cnt;\n+\tu16 cq_cnt;\n+\tu16 rq_cnt;\n+\tu8 rx_chan_cnt;\n+\tu8 tx_chan_cnt;\n+\tu8 tx_link;\n+\tu8 nix;\n+};\n+\n+#define IFACE_MAX (256 + 32) /* 32 PFs + 256 Vs */\n+\n+struct iface_get_info_rsp {\n+\tstruct mbox_msghdr hdr;\n+\tint cnt;\n+\tstruct iface_info info[IFACE_MAX];\n+};\n+\n+struct fl_info {\n+\tunsigned long cookie;\n+\tu16 mcam_idx[2];\n+\tu8 dis : 1;\n+\tu8 uni_di : 1;\n+};\n+\n+struct swdev2af_notify_req {\n+\tstruct mbox_msghdr hdr;\n+\tu64 msg_type;\n+#define SWDEV2AF_MSG_TYPE_FW_STATUS BIT_ULL(0)\n+#define\tSWDEV2AF_MSG_TYPE_REFRESH_FDB BIT_ULL(1)\n+#define\tSWDEV2AF_MSG_TYPE_REFRESH_FL BIT_ULL(2)\n+\tu16 pcifunc;\n+\tunion {\n+\t\tbool fw_up;\t\t// FW_STATUS message\n+\n+\t\tu8 mac[ETH_ALEN];\t// fdb refresh message\n+\n+\t\tstruct {\t\t// fl refresh message\n+\t\t\tint cnt;\n+\t\t\tstruct fl_info fl[64];\n+\t\t};\n+\t};\n+};\n+\n struct flow_msg {\n \tunsigned char dmac[6];\n \tunsigned char smac[6];\n@@ -1879,6 +2108,7 @@ struct npc_install_flow_req {\n \tu8 hw_prio;\n \tu8 req_kw_type; /* Key type to be written */\n \tu8 alloc_entry;\t/* only for cn20k */\n+\tu8 set_chanmask;\n /* For now use any priority, once AF driver is changed to\n * allocate least priority entry instead of mid zone then make\n * NPC_MCAM_LEAST_PRIO as 3\ndiff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu.c\nindex ffba56ee8a60cd..0f751555338e0f 100644\n--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu.c\n+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu.c\n@@ -23,6 +23,7 @@\n #include \"cn20k/reg.h\"\n #include \"cn20k/api.h\"\n #include \"cn20k/npc.h\"\n+#include \"switch/rvu_sw.h\"\n \n #define DRV_NAME\t\"rvu_af\"\n #define DRV_STRING \"Marvell OcteonTX2 RVU Admin Function Driver\"\n@@ -1990,6 +1991,122 @@ int rvu_mbox_handler_msix_offset(struct rvu *rvu, struct msg_req *req,\n \treturn 0;\n }\n \n+int rvu_mbox_handler_iface_get_info(struct rvu *rvu, struct msg_req *req,\n+\t\t\t\t struct iface_get_info_rsp *rsp)\n+{\n+\tstruct iface_info *info;\n+\tstruct rvu_pfvf *pfvf;\n+\tint pf, vf, numvfs;\n+\tu16 pcifunc;\n+\tint tot = 0;\n+\tu64 cfg;\n+\n+\tinfo = rsp-\u003einfo;\n+\tfor (pf = 0; pf \u003c rvu-\u003ehw-\u003etotal_pfs; pf++) {\n+\t\tif (tot \u003e= IFACE_MAX)\n+\t\t\tgoto done;\n+\n+\t\tcfg = rvu_read64(rvu, BLKADDR_RVUM, RVU_PRIV_PFX_CFG(pf));\n+\t\tnumvfs = (cfg \u003e\u003e 12) \u0026 0xFF;\n+\n+\t\t/* Skip not enabled PFs */\n+\t\tif (!(cfg \u0026 BIT_ULL(20)))\n+\t\t\tgoto chk_vfs;\n+\n+\t\t/* If Admin function, check on VFs */\n+\t\tif (cfg \u0026 BIT_ULL(21))\n+\t\t\tgoto chk_vfs;\n+\n+\t\tpcifunc = rvu_make_pcifunc(rvu-\u003epdev, pf, 0);\n+\t\tpfvf = rvu_get_pfvf(rvu, pcifunc);\n+\n+\t\t/* Populate iff at least one Tx channel */\n+\t\tif (!pfvf-\u003etx_chan_cnt)\n+\t\t\tgoto chk_vfs;\n+\n+\t\tinfo-\u003eis_vf = 0;\n+\t\tinfo-\u003epcifunc = pcifunc;\n+\t\tinfo-\u003erx_chan_base = pfvf-\u003erx_chan_base;\n+\t\tinfo-\u003erx_chan_cnt = pfvf-\u003erx_chan_cnt;\n+\t\tinfo-\u003etx_chan_base = pfvf-\u003etx_chan_base;\n+\t\tinfo-\u003etx_chan_cnt = pfvf-\u003etx_chan_cnt;\n+\t\tinfo-\u003etx_link = nix_get_tx_link(rvu, pcifunc);\n+\t\tif (is_sdp_pfvf(rvu, pcifunc))\n+\t\t\tinfo-\u003eis_sdp = 1;\n+\n+\t\t/* If interfaces are not UP, there are no queues */\n+\t\tinfo-\u003esq_cnt = 0;\n+\t\tinfo-\u003ecq_cnt = 0;\n+\t\tinfo-\u003erq_cnt = 0;\n+\n+\t\tif (pfvf-\u003esq_bmap)\n+\t\t\tinfo-\u003esq_cnt = bitmap_weight(pfvf-\u003esq_bmap, BITS_PER_LONG * 16);\n+\n+\t\tif (pfvf-\u003ecq_bmap)\n+\t\t\tinfo-\u003ecq_cnt = bitmap_weight(pfvf-\u003ecq_bmap, BITS_PER_LONG);\n+\n+\t\tif (pfvf-\u003erq_bmap)\n+\t\t\tinfo-\u003erq_cnt = bitmap_weight(pfvf-\u003erq_bmap, BITS_PER_LONG);\n+\n+\t\tif (pfvf-\u003enix_blkaddr == BLKADDR_NIX0)\n+\t\t\tinfo-\u003enix = 0;\n+\t\telse\n+\t\t\tinfo-\u003enix = 1;\n+\n+\t\tinfo++;\n+\t\ttot++;\n+\n+chk_vfs:\n+\t\tfor (vf = 0; vf \u003c numvfs; vf++) {\n+\t\t\tif (tot \u003e= IFACE_MAX)\n+\t\t\t\tgoto done;\n+\n+\t\t\tpcifunc = rvu_make_pcifunc(rvu-\u003epdev, pf, vf + 1);\n+\t\t\tpfvf = rvu_get_pfvf(rvu, pcifunc);\n+\n+\t\t\tif (!pfvf-\u003etx_chan_cnt)\n+\t\t\t\tcontinue;\n+\n+\t\t\tinfo-\u003eis_vf = 1;\n+\t\t\tinfo-\u003epcifunc = pcifunc;\n+\t\t\tinfo-\u003erx_chan_base = pfvf-\u003erx_chan_base;\n+\t\t\tinfo-\u003erx_chan_cnt = pfvf-\u003erx_chan_cnt;\n+\t\t\tinfo-\u003etx_chan_base = pfvf-\u003etx_chan_base;\n+\t\t\tinfo-\u003etx_chan_cnt = pfvf-\u003etx_chan_cnt;\n+\t\t\tinfo-\u003etx_link = nix_get_tx_link(rvu, pcifunc);\n+\t\t\tif (is_sdp_pfvf(rvu, pcifunc))\n+\t\t\t\tinfo-\u003eis_sdp = 1;\n+\n+\t\t\t/* If interfaces are not UP, there are no queues */\n+\t\t\tinfo-\u003esq_cnt = 0;\n+\t\t\tinfo-\u003ecq_cnt = 0;\n+\t\t\tinfo-\u003erq_cnt = 0;\n+\n+\t\t\tif (pfvf-\u003esq_bmap)\n+\t\t\t\tinfo-\u003esq_cnt = bitmap_weight(pfvf-\u003esq_bmap, BITS_PER_LONG * 16);\n+\n+\t\t\tif (pfvf-\u003ecq_bmap)\n+\t\t\t\tinfo-\u003ecq_cnt = bitmap_weight(pfvf-\u003ecq_bmap, BITS_PER_LONG);\n+\n+\t\t\tif (pfvf-\u003erq_bmap)\n+\t\t\t\tinfo-\u003erq_cnt = bitmap_weight(pfvf-\u003erq_bmap, BITS_PER_LONG);\n+\n+\t\t\tif (pfvf-\u003enix_blkaddr == BLKADDR_NIX0)\n+\t\t\t\tinfo-\u003enix = 0;\n+\t\t\telse\n+\t\t\t\tinfo-\u003enix = 1;\n+\n+\t\t\tinfo++;\n+\n+\t\t\ttot++;\n+\t\t}\n+\t}\n+done:\n+\trsp-\u003ecnt = tot;\n+\n+\treturn 0;\n+}\n+\n int rvu_mbox_handler_free_rsrc_cnt(struct rvu *rvu, struct msg_req *req,\n \t\t\t\t struct free_rsrcs_rsp *rsp)\n {\n@@ -2463,6 +2580,7 @@ static void __rvu_mbox_up_handler(struct rvu_work *mwork, int type)\n \n \t\tswitch (msg-\u003eid) {\n \t\tcase MBOX_MSG_CGX_LINK_EVENT:\n+\t\tcase MBOX_MSG_AF2PF_FDB_REFRESH:\n \t\t\tbreak;\n \t\tdefault:\n \t\t\tif (msg-\u003erc)\n@@ -3705,6 +3823,8 @@ static void rvu_remove(struct pci_dev *pdev)\n {\n \tstruct rvu *rvu = pci_get_drvdata(pdev);\n \n+\trvu_sw_shutdown();\n+\n \trvu_dbg_exit(rvu);\n \trvu_unregister_dl(rvu);\n \trvu_unregister_interrupts(rvu);\ndiff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu.h b/drivers/net/ethernet/marvell/octeontx2/af/rvu.h\nindex 7f3505ae686042..3e34d56a5bd3a6 100644\n--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu.h\n+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu.h\n@@ -576,6 +576,10 @@ struct rvu_switch {\n \tu16 *entry2pcifunc;\n \tu16 mode;\n \tu16 start_entry;\n+\tunsigned char switch_id[MAX_PHYS_ITEM_ID_LEN];\n+#define RVU_SWITCH_FLAG_FW_READY BIT_ULL(0)\n+\tu64 flags;\n+\tu16 pcifunc;\n };\n \n struct rep_evtq_ent {\n@@ -1158,6 +1162,7 @@ void rvu_program_channels(struct rvu *rvu);\n \n /* CN10K NIX */\n void rvu_nix_block_cn10k_init(struct rvu *rvu, struct nix_hw *nix_hw);\n+int nix_get_tx_link(struct rvu *rvu, u16 pcifunc);\n \n /* CN10K RVU - LMT*/\n void rvu_reset_lmt_map_tbl(struct rvu *rvu, u16 pcifunc);\n@@ -1196,4 +1201,5 @@ int rvu_rep_install_mcam_rules(struct rvu *rvu);\n void rvu_rep_update_rules(struct rvu *rvu, u16 pcifunc, bool ena);\n int rvu_rep_notify_pfvf_state(struct rvu *rvu, u16 pcifunc, bool enable);\n int npc_mcam_verify_entry(struct npc_mcam *mcam, u16 pcifunc, int entry);\n+u16 rvu_rep_get_vlan_id(struct rvu *rvu, u16 pcifunc);\n #endif /* RVU_H */\ndiff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c\nindex 0297c7ab0614ea..35c91cdc50b98d 100644\n--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c\n+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c\n@@ -32,7 +32,6 @@ static int nix_free_all_bandprof(struct rvu *rvu, u16 pcifunc);\n static void nix_clear_ratelimit_aggr(struct rvu *rvu, struct nix_hw *nix_hw,\n \t\t\t\t u32 leaf_prof);\n static const char *nix_get_ctx_name(int ctype);\n-static int nix_get_tx_link(struct rvu *rvu, u16 pcifunc);\n \n enum mc_tbl_sz {\n \tMC_TBL_SZ_256,\n@@ -1597,7 +1596,7 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu,\n \tif (rc)\n \t\tgoto free_mem;\n \n-\tpfvf-\u003esq_bmap = kcalloc(req-\u003esq_cnt, sizeof(long), GFP_KERNEL);\n+\tpfvf-\u003esq_bmap = kcalloc(req-\u003esq_cnt, sizeof(long) * 16, GFP_KERNEL);\n \tif (!pfvf-\u003esq_bmap) {\n \t\trc = -ENOMEM;\n \t\tgoto free_mem;\n@@ -2087,7 +2086,7 @@ static void nix_clear_tx_xoff(struct rvu *rvu, int blkaddr,\n \trvu_write64(rvu, blkaddr, reg, 0x0);\n }\n \n-static int nix_get_tx_link(struct rvu *rvu, u16 pcifunc)\n+int nix_get_tx_link(struct rvu *rvu, u16 pcifunc)\n {\n \tstruct rvu_hwinfo *hw = rvu-\u003ehw;\n \tint pf = rvu_get_pf(rvu-\u003epdev, pcifunc);\n@@ -2139,11 +2138,14 @@ static int nix_check_txschq_alloc_req(struct rvu *rvu, int lvl, u16 pcifunc,\n \tif (!req_schq)\n \t\treturn 0;\n \n-\tlink = nix_get_tx_link(rvu, pcifunc);\n+\tif (req-\u003eflags \u0026 NIX_TXSCH_ALLOC_FLAG_PAN)\n+\t\tlink = hw-\u003ecgx_links + hw-\u003elbk_links + 1;\n+\telse\n+\t\tlink = nix_get_tx_link(rvu, pcifunc);\n \n \t/* For traffic aggregating scheduler level, one queue is enough */\n \tif (lvl \u003e= hw-\u003ecap.nix_tx_aggr_lvl) {\n-\t\tif (req_schq != 1)\n+\t\tif (req_schq != 1 \u0026\u0026 !(req-\u003eflags \u0026 NIX_TXSCH_ALLOC_FLAG_PAN))\n \t\t\treturn NIX_AF_ERR_TLX_ALLOC_FAIL;\n \t\treturn 0;\n \t}\n@@ -2180,11 +2182,41 @@ static void nix_txsch_alloc(struct rvu *rvu, struct nix_txsch *txsch,\n \tstruct rvu_hwinfo *hw = rvu-\u003ehw;\n \tu16 pcifunc = rsp-\u003ehdr.pcifunc;\n \tint idx, schq;\n+\tbool alloc;\n \n \t/* For traffic aggregating levels, queue alloc is based\n \t * on transmit link to which PF_FUNC is mapped to.\n \t */\n \tif (lvl \u003e= hw-\u003ecap.nix_tx_aggr_lvl) {\n+\t\tif (start != end) {\n+\t\t\tidx = 0;\n+\t\t\talloc = false;\n+\t\t\tfor (schq = start; schq \u003c= end; schq++, idx++) {\n+\t\t\t\tif (test_bit(schq, txsch-\u003eschq.bmap))\n+\t\t\t\t\tcontinue;\n+\n+\t\t\t\tset_bit(schq, txsch-\u003eschq.bmap);\n+\n+\t\t\t\t/* A single TL queue is allocated each time */\n+\t\t\t\tif (rsp-\u003eschq_contig[lvl]) {\n+\t\t\t\t\talloc = true;\n+\t\t\t\t\trsp-\u003eschq_contig_list[lvl][idx] = schq;\n+\t\t\t\t\tcontinue;\n+\t\t\t\t}\n+\n+\t\t\t\tif (rsp-\u003eschq[lvl]) {\n+\t\t\t\t\talloc = true;\n+\t\t\t\t\trsp-\u003eschq_list[lvl][idx] = schq;\n+\t\t\t\t\tcontinue;\n+\t\t\t\t}\n+\t\t\t}\n+\n+\t\t\tif (!alloc)\n+\t\t\t\tdev_err(rvu-\u003edev,\n+\t\t\t\t\t\"Could not allocate schq at lvl=%u start=%u end=%u\\n\",\n+\t\t\t\t\tlvl, start, end);\n+\t\t\treturn;\n+\t\t}\n \t\t/* A single TL queue is allocated */\n \t\tif (rsp-\u003eschq_contig[lvl]) {\n \t\t\trsp-\u003eschq_contig[lvl] = 1;\n@@ -2301,11 +2333,14 @@ int rvu_mbox_handler_nix_txsch_alloc(struct rvu *rvu,\n \t\trsp-\u003eschq[lvl] = req-\u003eschq[lvl];\n \t\trsp-\u003eschq_contig[lvl] = req-\u003eschq_contig[lvl];\n \n-\t\tlink = nix_get_tx_link(rvu, pcifunc);\n+\t\tif (req-\u003eflags \u0026 NIX_TXSCH_ALLOC_FLAG_PAN)\n+\t\t\tlink = hw-\u003ecgx_links + hw-\u003elbk_links + 1;\n+\t\telse\n+\t\t\tlink = nix_get_tx_link(rvu, pcifunc);\n \n \t\tif (lvl \u003e= hw-\u003ecap.nix_tx_aggr_lvl) {\n \t\t\tstart = link;\n-\t\t\tend = link;\n+\t\t\tend = link + !!(req-\u003eflags \u0026 NIX_TXSCH_ALLOC_FLAG_PAN);\n \t\t} else if (hw-\u003ecap.nix_fixed_txschq_mapping) {\n \t\t\tnix_get_txschq_range(rvu, pcifunc, link, \u0026start, \u0026end);\n \t\t} else {\n@@ -2670,7 +2705,9 @@ static int nix_txschq_free_one(struct rvu *rvu,\n \tschq = req-\u003eschq;\n \ttxsch = \u0026nix_hw-\u003etxsch[lvl];\n \n-\tif (lvl \u003e= hw-\u003ecap.nix_tx_aggr_lvl || schq \u003e= txsch-\u003eschq.max)\n+\tif ((lvl \u003e= hw-\u003ecap.nix_tx_aggr_lvl \u0026\u0026\n+\t !(req-\u003eflags \u0026 TXSCHQ_FREE_PAN_TL1)) ||\n+\t schq \u003e= txsch-\u003eschq.max)\n \t\treturn 0;\n \n \tpfvf_map = txsch-\u003epfvf_map;\ndiff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c\nindex c7bc0b3a29b984..d797cd05776e53 100644\n--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c\n+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c\n@@ -3544,6 +3544,45 @@ int rvu_mbox_handler_npc_mcam_free_entry(struct rvu *rvu,\n \treturn rc;\n }\n \n+int rvu_mbox_handler_npc_flow_del_n_free(struct rvu *rvu,\n+\t\t\t\t\t struct npc_flow_del_n_free_req *mreq,\n+\t\t\t\t\t struct msg_rsp *rsp)\n+{\n+\tstruct npc_mcam_free_entry_req sreq = { 0 };\n+\tstruct npc_delete_flow_req dreq = { 0 };\n+\tstruct npc_delete_flow_rsp drsp = { 0 };\n+\tbool err = false;\n+\tint ret = 0, i;\n+\n+\tsreq.hdr.pcifunc = mreq-\u003ehdr.pcifunc;\n+\tdreq.hdr.pcifunc = mreq-\u003ehdr.pcifunc;\n+\n+\tif (!mreq-\u003ecnt || mreq-\u003ecnt \u003e 256) {\n+\t\tdev_err(rvu-\u003edev, \"Invalid cnt=%d\\n\", mreq-\u003ecnt);\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tfor (i = 0; i \u003c mreq-\u003ecnt; i++) {\n+\t\tdreq.entry = mreq-\u003eentry[i];\n+\t\tret = rvu_mbox_handler_npc_delete_flow(rvu, \u0026dreq, \u0026drsp);\n+\t\tif (ret) {\n+\t\t\tdev_err(rvu-\u003edev, \"delete flow error for i=%d entry=%d\\n\",\n+\t\t\t\ti, mreq-\u003eentry[i]);\n+\t\t\terr = true;\n+\t\t}\n+\n+\t\tsreq.entry = mreq-\u003eentry[i];\n+\t\tret = rvu_mbox_handler_npc_mcam_free_entry(rvu, \u0026sreq, rsp);\n+\t\tif (ret) {\n+\t\t\tdev_err(rvu-\u003edev, \"free entry error for i=%d entry=%d\\n\",\n+\t\t\t\ti, mreq-\u003eentry[i]);\n+\t\t\terr = true;\n+\t\t}\n+\t}\n+\n+\treturn err ? -EINVAL : 0;\n+}\n+\n int rvu_mbox_handler_npc_mcam_read_entry(struct rvu *rvu,\n \t\t\t\t\t struct npc_mcam_read_entry_req *req,\n \t\t\t\t\t struct npc_mcam_read_entry_rsp *rsp)\n@@ -4361,6 +4400,55 @@ int rvu_mbox_handler_npc_mcam_entry_stats(struct rvu *rvu,\n \treturn 0;\n }\n \n+int rvu_mbox_handler_npc_mcam_mul_stats(struct rvu *rvu,\n+\t\t\t\t\tstruct npc_mcam_get_mul_stats_req *req,\n+\t\t\t\t\tstruct npc_mcam_get_mul_stats_rsp *rsp)\n+{\n+\tstruct npc_mcam *mcam = \u0026rvu-\u003ehw-\u003emcam;\n+\tu16 pcifunc = req-\u003ehdr.pcifunc;\n+\tu16 index, cntr, entry;\n+\tint blkaddr;\n+\tu64 regval;\n+\tu32 bank;\n+\n+\tif (!req-\u003ecnt || req-\u003ecnt \u003e 256) {\n+\t\tdev_err(rvu-\u003edev, \"%s invalid request cnt=%d\\n\",\n+\t\t\t__func__, req-\u003ecnt);\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tblkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0);\n+\tif (blkaddr \u003c 0)\n+\t\treturn NPC_MCAM_INVALID_REQ;\n+\n+\tmutex_lock(\u0026mcam-\u003elock);\n+\n+\tfor (int i = 0; i \u003c req-\u003ecnt; i++) {\n+\t\tentry = npc_cn20k_vidx2idx(req-\u003eentry[i]);\n+\n+\t\tif (npc_mcam_verify_entry(mcam, pcifunc, entry)) {\n+\t\t\tdev_err(rvu-\u003edev, \"%s invalid mcam index=%d\\n\",\n+\t\t\t\t__func__, req-\u003eentry[i]);\n+\t\t\treturn -EINVAL;\n+\t\t}\n+\n+\t\tindex = entry \u0026 (mcam-\u003ebanksize - 1);\n+\t\tbank = npc_get_bank(mcam, entry);\n+\n+\t\t/* read MCAM entry STAT_ACT register */\n+\t\tregval = rvu_read64(rvu, blkaddr, NPC_AF_MCAMEX_BANKX_STAT_ACT(index, bank));\n+\t\tcntr = regval \u0026 0x1FF;\n+\n+\t\trsp-\u003estat[i] = rvu_read64(rvu, blkaddr, NPC_AF_MATCH_STATX(cntr));\n+\t\trsp-\u003estat[i] \u0026= BIT_ULL(48) - 1;\n+\t}\n+\n+\trsp-\u003ecnt = req-\u003ecnt;\n+\n+\tmutex_unlock(\u0026mcam-\u003elock);\n+\treturn 0;\n+}\n+\n void rvu_npc_clear_ucast_entry(struct rvu *rvu, int pcifunc, int nixlf)\n {\n \tstruct npc_mcam *mcam = \u0026rvu-\u003ehw-\u003emcam;\ndiff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c\nindex 91b5947dae0600..09c7ee8571df0a 100644\n--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c\n+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c\n@@ -1926,6 +1926,17 @@ static int npc_delete_flow(struct rvu *rvu, struct rvu_npc_mcam_rule *rule,\n \treturn rvu_mbox_handler_npc_mcam_dis_entry(rvu, \u0026dis_req, \u0026dis_rsp);\n }\n \n+int rvu_mbox_handler_npc_mcam_get_features(struct rvu *rvu,\n+\t\t\t\t\t struct msg_req *req,\n+\t\t\t\t\t struct npc_mcam_get_features_rsp *rsp)\n+{\n+\tstruct npc_mcam *mcam = \u0026rvu-\u003ehw-\u003emcam;\n+\n+\trsp-\u003erx_features = mcam-\u003erx_features;\n+\trsp-\u003etx_features = mcam-\u003etx_features;\n+\treturn 0;\n+}\n+\n int rvu_mbox_handler_npc_delete_flow(struct rvu *rvu,\n \t\t\t\t struct npc_delete_flow_req *req,\n \t\t\t\t struct npc_delete_flow_rsp *rsp)\ndiff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_rep.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_rep.c\nindex a2781e0f504e3e..6bb6064b2391b4 100644\n--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_rep.c\n+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_rep.c\n@@ -189,7 +189,7 @@ int rvu_mbox_handler_nix_lf_stats(struct rvu *rvu,\n \treturn 0;\n }\n \n-static u16 rvu_rep_get_vlan_id(struct rvu *rvu, u16 pcifunc)\n+u16 rvu_rep_get_vlan_id(struct rvu *rvu, u16 pcifunc)\n {\n \tint id;\n \n@@ -436,6 +436,7 @@ int rvu_mbox_handler_esw_cfg(struct rvu *rvu, struct esw_cfg_req *req,\n \t\treturn 0;\n \n \trvu-\u003erep_mode = req-\u003eena;\n+\tmemcpy(rvu-\u003erswitch.switch_id, req-\u003eswitch_id, MAX_PHYS_ITEM_ID_LEN);\n \n \tif (!rvu-\u003erep_mode)\n \t\trvu_npc_free_mcam_entries(rvu, req-\u003ehdr.pcifunc, -1);\ndiff --git a/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.c b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.c\nnew file mode 100644\nindex 00000000000000..f075da1507e77b\n--- /dev/null\n+++ b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.c\n@@ -0,0 +1,56 @@\n+// SPDX-License-Identifier: GPL-2.0\n+/* Marvell RVU Admin Function driver\n+ *\n+ * Copyright (C) 2026 Marvell.\n+ *\n+ */\n+\n+#include \u003clinux/bitfield.h\u003e\n+#include \"rvu.h\"\n+#include \"rvu_sw_l2.h\"\n+#include \"rvu_sw_l3.h\"\n+#include \"rvu_sw_fl.h\"\n+#include \"rvu_sw.h\"\n+\n+u32 rvu_sw_port_id(struct rvu *rvu, u16 pcifunc)\n+{\n+\tu16 port_id;\n+\tu16 rep_id;\n+\n+\trep_id = rvu_rep_get_vlan_id(rvu, pcifunc);\n+\n+\tport_id = FIELD_PREP(GENMASK_ULL(31, 16), rep_id) |\n+\t\t FIELD_PREP(GENMASK_ULL(15, 0), pcifunc);\n+\n+\treturn port_id;\n+}\n+\n+int rvu_mbox_handler_swdev2af_notify(struct rvu *rvu,\n+\t\t\t\t struct swdev2af_notify_req *req,\n+\t\t\t\t struct msg_rsp *rsp)\n+{\n+\tint rc = 0;\n+\n+\tswitch (req-\u003emsg_type) {\n+\tcase SWDEV2AF_MSG_TYPE_FW_STATUS:\n+\t\trc = rvu_sw_l2_init_offl_wq(rvu, req-\u003epcifunc, req-\u003efw_up);\n+\t\tbreak;\n+\n+\tcase SWDEV2AF_MSG_TYPE_REFRESH_FDB:\n+\t\trc = rvu_sw_l2_fdb_list_entry_add(rvu, req-\u003epcifunc, req-\u003emac);\n+\t\tbreak;\n+\n+\tcase SWDEV2AF_MSG_TYPE_REFRESH_FL:\n+\t\trc = rvu_sw_fl_stats_sync2db(rvu, req-\u003efl, req-\u003ecnt);\n+\t\tbreak;\n+\t}\n+\n+\treturn rc;\n+}\n+\n+void rvu_sw_shutdown(void)\n+{\n+\trvu_sw_l2_shutdown();\n+\trvu_sw_l3_shutdown();\n+\trvu_sw_fl_shutdown();\n+}\ndiff --git a/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.h b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.h\nnew file mode 100644\nindex 00000000000000..fb24e0a79ff925\n--- /dev/null\n+++ b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.h\n@@ -0,0 +1,15 @@\n+/* SPDX-License-Identifier: GPL-2.0 */\n+/* Marvell RVU Admin Function driver\n+ *\n+ * Copyright (C) 2026 Marvell.\n+ *\n+ */\n+\n+#ifndef RVU_SWITCH_H\n+#define RVU_SWITCH_H\n+\n+/* RVU Switch */\n+u32 rvu_sw_port_id(struct rvu *rvu, u16 pcifunc);\n+void rvu_sw_shutdown(void);\n+\n+#endif\ndiff --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\nnew file mode 100644\nindex 00000000000000..6788cfc640c972\n--- /dev/null\n+++ b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_fl.c\n@@ -0,0 +1,326 @@\n+// SPDX-License-Identifier: GPL-2.0\n+/* Marvell RVU Admin Function driver\n+ *\n+ * Copyright (C) 2026 Marvell.\n+ *\n+ */\n+\n+#include \u003clinux/bitfield.h\u003e\n+#include \"rvu.h\"\n+#include \"rvu_sw.h\"\n+#include \"rvu_sw_fl.h\"\n+\n+#define M(_name, _id, _fn_name, _req_type, _rsp_type)\t\t\t\\\n+static struct _req_type __maybe_unused\t\t\t\t\t\\\n+*otx2_mbox_alloc_msg_ ## _fn_name(struct rvu *rvu, int devid)\t\t\\\n+{\t\t\t\t\t\t\t\t\t\\\n+\tstruct _req_type *req;\t\t\t\t\t\t\\\n+\t\t\t\t\t\t\t\t\t\\\n+\treq = (struct _req_type *)otx2_mbox_alloc_msg_rsp(\t\t\\\n+\t\t\u0026rvu-\u003eafpf_wq_info.mbox_up, devid, sizeof(struct _req_type), \\\n+\t\tsizeof(struct _rsp_type));\t\t\t\t\\\n+\tif (!req)\t\t\t\t\t\t\t\\\n+\t\treturn NULL;\t\t\t\t\t\t\\\n+\treq-\u003ehdr.sig = OTX2_MBOX_REQ_SIG;\t\t\t\t\\\n+\treq-\u003ehdr.id = _id;\t\t\t\t\t\t\\\n+\treturn req;\t\t\t\t\t\t\t\\\n+}\n+\n+MBOX_UP_AF2SWDEV_MESSAGES\n+#undef M\n+\n+static struct workqueue_struct *sw_fl_offl_wq;\n+\n+struct fl_entry {\n+\tstruct list_head list;\n+\tstruct rvu *rvu;\n+\tu32 port_id;\n+\tunsigned long cookie;\n+\tstruct fl_tuple tuple;\n+\tu64 flags;\n+\tu64 features;\n+};\n+\n+static DEFINE_MUTEX(fl_offl_llock);\n+static LIST_HEAD(fl_offl_lh);\n+static bool fl_offl_work_running;\n+\n+static struct workqueue_struct *sw_fl_offl_wq;\n+static void sw_fl_offl_work_handler(struct work_struct *work);\n+static DECLARE_DELAYED_WORK(fl_offl_work, sw_fl_offl_work_handler);\n+\n+struct sw_fl_stats_node {\n+\tstruct list_head list;\n+\tunsigned long cookie;\n+\tu16 mcam_idx[2];\n+\tu64 opkts, npkts;\n+\tbool uni_di;\n+};\n+\n+static LIST_HEAD(sw_fl_stats_lh);\n+static DEFINE_MUTEX(sw_fl_stats_lock);\n+\n+static int\n+rvu_sw_fl_stats_sync2db_one_entry(unsigned long cookie, u8 disabled,\n+\t\t\t\t u16 mcam_idx[2], bool uni_di, u64 pkts)\n+{\n+\tstruct sw_fl_stats_node *snode, *tmp;\n+\n+\tmutex_lock(\u0026sw_fl_stats_lock);\n+\tlist_for_each_entry_safe(snode, tmp, \u0026sw_fl_stats_lh, list) {\n+\t\tif (snode-\u003ecookie != cookie)\n+\t\t\tcontinue;\n+\n+\t\tif (disabled) {\n+\t\t\tlist_del_init(\u0026snode-\u003elist);\n+\t\t\tmutex_unlock(\u0026sw_fl_stats_lock);\n+\t\t\tkfree(snode);\n+\t\t\treturn 0;\n+\t\t}\n+\n+\t\tif (snode-\u003euni_di != uni_di) {\n+\t\t\tsnode-\u003euni_di = uni_di;\n+\t\t\tsnode-\u003emcam_idx[1] = mcam_idx[1];\n+\t\t}\n+\n+\t\tif (snode-\u003eopkts == pkts) {\n+\t\t\tmutex_unlock(\u0026sw_fl_stats_lock);\n+\t\t\treturn 0;\n+\t\t}\n+\n+\t\tsnode-\u003enpkts = pkts;\n+\t\tmutex_unlock(\u0026sw_fl_stats_lock);\n+\t\treturn 0;\n+\t}\n+\n+\tif (disabled) {\n+\t\tmutex_unlock(\u0026sw_fl_stats_lock);\n+\t\treturn 0;\n+\t}\n+\n+\tsnode = kcalloc(1, sizeof(*snode), GFP_KERNEL);\n+\tif (!snode) {\n+\t\tmutex_unlock(\u0026sw_fl_stats_lock);\n+\t\treturn -ENOMEM;\n+\t}\n+\n+\tsnode-\u003ecookie = cookie;\n+\tsnode-\u003emcam_idx[0] = mcam_idx[0];\n+\tif (!uni_di)\n+\t\tsnode-\u003emcam_idx[1] = mcam_idx[1];\n+\n+\tsnode-\u003enpkts = pkts;\n+\tsnode-\u003euni_di = uni_di;\n+\tINIT_LIST_HEAD(\u0026snode-\u003elist);\n+\n+\tlist_add_tail(\u0026snode-\u003elist, \u0026sw_fl_stats_lh);\n+\tmutex_unlock(\u0026sw_fl_stats_lock);\n+\n+\treturn 0;\n+}\n+\n+int rvu_sw_fl_stats_sync2db(struct rvu *rvu, struct fl_info *fl, int cnt)\n+{\n+\tstruct npc_mcam_get_mul_stats_req *req = NULL;\n+\tstruct npc_mcam_get_mul_stats_rsp *rsp = NULL;\n+\tint tot = 0;\n+\tu16 i2idx_map[256];\n+\tint rc = 0;\n+\tu64 pkts;\n+\tint idx;\n+\n+\tif (cnt \u003e 256)\n+\t\treturn -ENOMEM;\n+\n+\tfor (int i = 0; i \u003c cnt; i++) {\n+\t\ttot++;\n+\t\tif (fl[i].uni_di)\n+\t\t\tcontinue;\n+\n+\t\ttot++;\n+\t}\n+\n+\treq = kcalloc(1, sizeof(*req), GFP_KERNEL);\n+\tif (!req) {\n+\t\trc = -ENOMEM;\n+\t\tgoto fail;\n+\t}\n+\n+\trsp = kcalloc(1, sizeof(*rsp), GFP_KERNEL);\n+\tif (!rsp) {\n+\t\trc = -ENOMEM;\n+\t\tgoto fail;\n+\t}\n+\n+\treq-\u003ecnt = tot;\n+\tidx = 0;\n+\tfor (int i = 0; i \u003c tot; idx++) {\n+\t\ti2idx_map[i] = idx;\n+\t\treq-\u003eentry[i++] = fl[idx].mcam_idx[0];\n+\t\tif (fl[idx].uni_di)\n+\t\t\tcontinue;\n+\n+\t\ti2idx_map[i] = idx;\n+\t\treq-\u003eentry[i++] = fl[idx].mcam_idx[1];\n+\t}\n+\n+\tif (rvu_mbox_handler_npc_mcam_mul_stats(rvu, req, rsp)) {\n+\t\tdev_err(rvu-\u003edev, \"Error to get multiple stats\\n\");\n+\t\trc = -EFAULT;\n+\t\tgoto fail;\n+\t}\n+\n+\tfor (int i = 0; i \u003c tot;) {\n+\t\tidx = i2idx_map[i];\n+\t\tpkts = rsp-\u003estat[i++];\n+\n+\t\tif (!fl[idx].uni_di)\n+\t\t\tpkts += rsp-\u003estat[i++];\n+\n+\t\trc |= rvu_sw_fl_stats_sync2db_one_entry(fl[idx].cookie, fl[idx].dis,\n+\t\t\t\t\t\t\tfl[idx].mcam_idx,\n+\t\t\t\t\t\t\tfl[idx].uni_di, pkts);\n+\t}\n+\n+fail:\n+\tkfree(req);\n+\tkfree(rsp);\n+\treturn rc;\n+}\n+\n+static int rvu_sw_fl_offl_rule_push(struct fl_entry *fl_entry)\n+{\n+\tstruct af2swdev_notify_req *req;\n+\tstruct rvu *rvu;\n+\tint swdev_pf;\n+\n+\trvu = fl_entry-\u003ervu;\n+\tswdev_pf = rvu_get_pf(rvu-\u003epdev, rvu-\u003erswitch.pcifunc);\n+\n+\tmutex_lock(\u0026rvu-\u003embox_lock);\n+\treq = otx2_mbox_alloc_msg_af2swdev_notify(rvu, swdev_pf);\n+\tif (!req) {\n+\t\tmutex_unlock(\u0026rvu-\u003embox_lock);\n+\t\treturn -ENOMEM;\n+\t}\n+\n+\treq-\u003etuple = fl_entry-\u003etuple;\n+\treq-\u003eflags = fl_entry-\u003eflags;\n+\treq-\u003ecookie = fl_entry-\u003ecookie;\n+\treq-\u003efeatures = fl_entry-\u003efeatures;\n+\n+\totx2_mbox_wait_for_zero(\u0026rvu-\u003eafpf_wq_info.mbox_up, swdev_pf);\n+\totx2_mbox_msg_send_up(\u0026rvu-\u003eafpf_wq_info.mbox_up, swdev_pf);\n+\n+\tmutex_unlock(\u0026rvu-\u003embox_lock);\n+\treturn 0;\n+}\n+\n+static void sw_fl_offl_work_handler(struct work_struct *work)\n+{\n+\tstruct fl_entry *fl_entry;\n+\n+\tmutex_lock(\u0026fl_offl_llock);\n+\tfl_entry = list_first_entry_or_null(\u0026fl_offl_lh, struct fl_entry, list);\n+\tif (!fl_entry) {\n+\t\tmutex_unlock(\u0026fl_offl_llock);\n+\t\treturn;\n+\t}\n+\n+\tlist_del_init(\u0026fl_entry-\u003elist);\n+\tmutex_unlock(\u0026fl_offl_llock);\n+\n+\trvu_sw_fl_offl_rule_push(fl_entry);\n+\tkfree(fl_entry);\n+\n+\tmutex_lock(\u0026fl_offl_llock);\n+\tif (!list_empty(\u0026fl_offl_lh))\n+\t\tqueue_delayed_work(sw_fl_offl_wq, \u0026fl_offl_work, msecs_to_jiffies(10));\n+\tmutex_unlock(\u0026fl_offl_llock);\n+}\n+\n+int rvu_mbox_handler_fl_get_stats(struct rvu *rvu,\n+\t\t\t\t struct fl_get_stats_req *req,\n+\t\t\t\t struct fl_get_stats_rsp *rsp)\n+{\n+\tstruct sw_fl_stats_node *snode, *tmp;\n+\n+\tmutex_lock(\u0026sw_fl_stats_lock);\n+\tlist_for_each_entry_safe(snode, tmp, \u0026sw_fl_stats_lh, list) {\n+\t\tif (snode-\u003ecookie != req-\u003ecookie)\n+\t\t\tcontinue;\n+\n+\t\trsp-\u003epkts_diff = snode-\u003enpkts - snode-\u003eopkts;\n+\t\tsnode-\u003eopkts = snode-\u003enpkts;\n+\t\tbreak;\n+\t}\n+\tmutex_unlock(\u0026sw_fl_stats_lock);\n+\treturn 0;\n+}\n+\n+int rvu_mbox_handler_fl_notify(struct rvu *rvu,\n+\t\t\t struct fl_notify_req *req,\n+\t\t\t struct msg_rsp *rsp)\n+{\n+\tstruct fl_entry *fl_entry;\n+\n+\tif (!(rvu-\u003erswitch.flags \u0026 RVU_SWITCH_FLAG_FW_READY))\n+\t\treturn 0;\n+\n+\tfl_entry = kcalloc(1, sizeof(*fl_entry), GFP_KERNEL);\n+\tif (!fl_entry)\n+\t\treturn -ENOMEM;\n+\n+\tfl_entry-\u003eport_id = rvu_sw_port_id(rvu, req-\u003ehdr.pcifunc);\n+\tfl_entry-\u003ervu = rvu;\n+\tINIT_LIST_HEAD(\u0026fl_entry-\u003elist);\n+\tfl_entry-\u003etuple = req-\u003etuple;\n+\tfl_entry-\u003ecookie = req-\u003ecookie;\n+\tfl_entry-\u003eflags = req-\u003eflags;\n+\tfl_entry-\u003efeatures = req-\u003efeatures;\n+\n+\tmutex_lock(\u0026fl_offl_llock);\n+\tlist_add_tail(\u0026fl_entry-\u003elist, \u0026fl_offl_lh);\n+\n+\tif (!fl_offl_work_running) {\n+\t\tsw_fl_offl_wq = alloc_workqueue(\"sw_af_fl_wq\", 0, 0);\n+\t\tif (sw_fl_offl_wq)\n+\t\t\tfl_offl_work_running = true;\n+\t}\n+\tmutex_unlock(\u0026fl_offl_llock);\n+\tqueue_delayed_work(sw_fl_offl_wq, \u0026fl_offl_work, msecs_to_jiffies(10));\n+\n+\treturn 0;\n+}\n+\n+void rvu_sw_fl_shutdown(void)\n+{\n+\tstruct sw_fl_stats_node *snode, *tmp;\n+\tstruct fl_entry *entry;\n+\tLIST_HEAD(tlist);\n+\n+\tif (!sw_fl_offl_wq)\n+\t\treturn;\n+\n+\tcancel_delayed_work_sync(\u0026fl_offl_work);\n+\tdestroy_workqueue(sw_fl_offl_wq);\n+\n+\tmutex_lock(\u0026fl_offl_llock);\n+\twhile (1) {\n+\t\tentry = list_first_entry_or_null(\u0026fl_offl_lh,\n+\t\t\t\t\t\t struct fl_entry, list);\n+\t\tif (!entry)\n+\t\t\tbreak;\n+\n+\t\tlist_del_init(\u0026entry-\u003elist);\n+\t\tkfree(entry);\n+\t}\n+\tmutex_unlock(\u0026fl_offl_llock);\n+\n+\tmutex_lock(\u0026sw_fl_stats_lock);\n+\tlist_for_each_entry_safe(snode, tmp, \u0026sw_fl_stats_lh, list) {\n+\t\tlist_del_init(\u0026snode-\u003elist);\n+\t\tkfree(snode);\n+\t}\n+\tmutex_unlock(\u0026sw_fl_stats_lock);\n+}\ndiff --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\nnew file mode 100644\nindex 00000000000000..f117a96fc33e7b\n--- /dev/null\n+++ b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_fl.h\n@@ -0,0 +1,13 @@\n+/* SPDX-License-Identifier: GPL-2.0 */\n+/* Marvell RVU Admin Function driver\n+ *\n+ * Copyright (C) 2026 Marvell.\n+ *\n+ */\n+\n+#ifndef RVU_SW_FL_H\n+#define RVU_SW_FL_H\n+int rvu_sw_fl_stats_sync2db(struct rvu *rvu, struct fl_info *fl, int cnt);\n+void rvu_sw_fl_shutdown(void);\n+\n+#endif\ndiff --git a/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c\nnew file mode 100644\nindex 00000000000000..bd0fe13b1fc93f\n--- /dev/null\n+++ b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c\n@@ -0,0 +1,338 @@\n+// SPDX-License-Identifier: GPL-2.0\n+/* Marvell RVU Admin Function driver\n+ *\n+ * Copyright (C) 2026 Marvell.\n+ *\n+ */\n+\n+#include \u003clinux/bitfield.h\u003e\n+#include \"rvu.h\"\n+#include \"rvu_sw.h\"\n+#include \"rvu_sw_l2.h\"\n+\n+#define M(_name, _id, _fn_name, _req_type, _rsp_type)\t\t\t\\\n+static struct _req_type __maybe_unused\t\t\t\t\t\\\n+*otx2_mbox_alloc_msg_ ## _fn_name(struct rvu *rvu, int devid)\t\t\\\n+{\t\t\t\t\t\t\t\t\t\\\n+\tstruct _req_type *req;\t\t\t\t\t\t\\\n+\t\t\t\t\t\t\t\t\t\\\n+\treq = (struct _req_type *)otx2_mbox_alloc_msg_rsp(\t\t\\\n+\t\t\u0026rvu-\u003eafpf_wq_info.mbox_up, devid, sizeof(struct _req_type), \\\n+\t\tsizeof(struct _rsp_type));\t\t\t\t\\\n+\tif (!req)\t\t\t\t\t\t\t\\\n+\t\treturn NULL;\t\t\t\t\t\t\\\n+\treq-\u003ehdr.sig = OTX2_MBOX_REQ_SIG;\t\t\t\t\\\n+\treq-\u003ehdr.id = _id;\t\t\t\t\t\t\\\n+\treturn req;\t\t\t\t\t\t\t\\\n+}\n+MBOX_UP_AF2SWDEV_MESSAGES\n+MBOX_UP_AF2PF_FDB_REFRESH_MESSAGES\n+#undef M\n+\n+struct l2_entry {\n+\tstruct list_head list;\n+\tu64 flags;\n+\tu32 port_id;\n+\tu8 mac[ETH_ALEN];\n+};\n+\n+static DEFINE_MUTEX(l2_offl_list_lock);\n+static LIST_HEAD(l2_offl_lh);\n+\n+static DEFINE_MUTEX(fdb_refresh_list_lock);\n+static LIST_HEAD(fdb_refresh_lh);\n+\n+struct rvu_sw_l2_work {\n+\tstruct rvu *rvu;\n+\tstruct work_struct work;\n+};\n+\n+/* Work queue for switchdev message handling. There is only\n+ * on switch HW per SoC, so one instance of each type of\n+ * workqueue is enough.\n+ */\n+static struct rvu_sw_l2_work l2_offl_work;\n+static struct workqueue_struct *rvu_sw_l2_offl_wq;\n+\n+static struct rvu_sw_l2_work fdb_refresh_work;\n+static struct workqueue_struct *fdb_refresh_wq;\n+\n+/* High-frequency link state transitions or aggressive FDB\n+ * aging intervals can induce rapid fdb churn. To prevent\n+ * thrashing, inhibit hardware offloading of these transient\n+ * forwarding states to the switching ASIC.\n+ */\n+static void rvu_sw_l2_offl_cancel_add_if_del_reqs_exist(u8 *mac)\n+{\n+\tstruct l2_entry *entry, *tmp;\n+\n+\tmutex_lock(\u0026l2_offl_list_lock);\n+\tlist_for_each_entry_safe(entry, tmp, \u0026l2_offl_lh, list) {\n+\t\tif (!ether_addr_equal(mac, entry-\u003emac))\n+\t\t\tcontinue;\n+\n+\t\tif (!(entry-\u003eflags \u0026 FDB_DEL))\n+\t\t\tcontinue;\n+\n+\t\tlist_del_init(\u0026entry-\u003elist);\n+\t\tkfree(entry);\n+\t\tbreak;\n+\t}\n+\tmutex_unlock(\u0026l2_offl_list_lock);\n+}\n+\n+static int rvu_sw_l2_offl_rule_push(struct rvu *rvu, struct l2_entry *l2_entry)\n+{\n+\tstruct af2swdev_notify_req *req;\n+\tint swdev_pf;\n+\n+\tswdev_pf = rvu_get_pf(rvu-\u003epdev, rvu-\u003erswitch.pcifunc);\n+\n+\tmutex_lock(\u0026rvu-\u003embox_lock);\n+\treq = otx2_mbox_alloc_msg_af2swdev_notify(rvu, swdev_pf);\n+\tif (!req) {\n+\t\tmutex_unlock(\u0026rvu-\u003embox_lock);\n+\t\treturn -ENOMEM;\n+\t}\n+\n+\tether_addr_copy(req-\u003emac, l2_entry-\u003emac);\n+\treq-\u003eflags = l2_entry-\u003eflags;\n+\treq-\u003eport_id = l2_entry-\u003eport_id;\n+\n+\totx2_mbox_wait_for_zero(\u0026rvu-\u003eafpf_wq_info.mbox_up, swdev_pf);\n+\totx2_mbox_msg_send_up(\u0026rvu-\u003eafpf_wq_info.mbox_up, swdev_pf);\n+\n+\tmutex_unlock(\u0026rvu-\u003embox_lock);\n+\treturn 0;\n+}\n+\n+static int rvu_sw_l2_fdb_refresh(struct rvu *rvu, u16 pcifunc, u8 *mac)\n+{\n+\tstruct af2pf_fdb_refresh_req *req;\n+\tint pf, vidx;\n+\n+\tpf = rvu_get_pf(rvu-\u003epdev, pcifunc);\n+\n+\tmutex_lock(\u0026rvu-\u003embox_lock);\n+\n+\tif (pf) {\n+\t\treq = otx2_mbox_alloc_msg_af2pf_fdb_refresh(rvu, pf);\n+\t\tif (!req) {\n+\t\t\tmutex_unlock(\u0026rvu-\u003embox_lock);\n+\t\t\treturn -ENOMEM;\n+\t\t}\n+\n+\t\treq-\u003ehdr.pcifunc = pcifunc;\n+\t\tether_addr_copy(req-\u003emac, mac);\n+\t\treq-\u003epcifunc = pcifunc;\n+\n+\t\totx2_mbox_wait_for_zero(\u0026rvu-\u003eafpf_wq_info.mbox_up, pf);\n+\t\totx2_mbox_msg_send_up(\u0026rvu-\u003eafpf_wq_info.mbox_up, pf);\n+\t} else {\n+\t\tvidx = pcifunc - 1;\n+\n+\t\treq = (struct af2pf_fdb_refresh_req *)\n+\t\t\totx2_mbox_alloc_msg_rsp(\u0026rvu-\u003eafvf_wq_info.mbox_up, vidx,\n+\t\t\t\t\t\tsizeof(*req), sizeof(struct msg_rsp));\n+\t\tif (!req) {\n+\t\t\tmutex_unlock(\u0026rvu-\u003embox_lock);\n+\t\t\treturn -ENOMEM;\n+\t\t}\n+\t\treq-\u003ehdr.sig = OTX2_MBOX_REQ_SIG;\n+\t\treq-\u003ehdr.id = MBOX_MSG_AF2PF_FDB_REFRESH;\n+\n+\t\treq-\u003ehdr.pcifunc = pcifunc;\n+\t\tether_addr_copy(req-\u003emac, mac);\n+\t\treq-\u003epcifunc = pcifunc;\n+\n+\t\totx2_mbox_wait_for_zero(\u0026rvu-\u003eafvf_wq_info.mbox_up, vidx);\n+\t\totx2_mbox_msg_send_up(\u0026rvu-\u003eafvf_wq_info.mbox_up, vidx);\n+\t}\n+\n+\tmutex_unlock(\u0026rvu-\u003embox_lock);\n+\n+\treturn 0;\n+}\n+\n+static void rvu_sw_l2_fdb_refresh_wq_handler(struct work_struct *work)\n+{\n+\tstruct rvu_sw_l2_work *fdb_work;\n+\tstruct l2_entry *l2_entry;\n+\n+\tfdb_work = container_of(work, struct rvu_sw_l2_work, work);\n+\n+\twhile (1) {\n+\t\tmutex_lock(\u0026fdb_refresh_list_lock);\n+\t\tl2_entry = list_first_entry_or_null(\u0026fdb_refresh_lh,\n+\t\t\t\t\t\t struct l2_entry, list);\n+\t\tif (!l2_entry) {\n+\t\t\tmutex_unlock(\u0026fdb_refresh_list_lock);\n+\t\t\treturn;\n+\t\t}\n+\n+\t\tlist_del_init(\u0026l2_entry-\u003elist);\n+\t\tmutex_unlock(\u0026fdb_refresh_list_lock);\n+\n+\t\trvu_sw_l2_fdb_refresh(fdb_work-\u003ervu, l2_entry-\u003eport_id, l2_entry-\u003emac);\n+\t\tkfree(l2_entry);\n+\t}\n+}\n+\n+static void rvu_sw_l2_offl_rule_wq_handler(struct work_struct *work)\n+{\n+\tstruct rvu_sw_l2_work *offl_work;\n+\tstruct l2_entry *l2_entry;\n+\tint budget = 16;\n+\tbool add_fdb;\n+\n+\toffl_work = container_of(work, struct rvu_sw_l2_work, work);\n+\n+\twhile (budget--) {\n+\t\tmutex_lock(\u0026l2_offl_list_lock);\n+\t\tl2_entry = list_first_entry_or_null(\u0026l2_offl_lh, struct l2_entry, list);\n+\t\tif (!l2_entry) {\n+\t\t\tmutex_unlock(\u0026l2_offl_list_lock);\n+\t\t\treturn;\n+\t\t}\n+\n+\t\tlist_del_init(\u0026l2_entry-\u003elist);\n+\t\tmutex_unlock(\u0026l2_offl_list_lock);\n+\n+\t\tadd_fdb = !!(l2_entry-\u003eflags \u0026 FDB_ADD);\n+\n+\t\tif (add_fdb)\n+\t\t\trvu_sw_l2_offl_cancel_add_if_del_reqs_exist(l2_entry-\u003emac);\n+\n+\t\tif (rvu_sw_l2_offl_rule_push(offl_work-\u003ervu, l2_entry))\n+\t\t\tdev_err(offl_work-\u003ervu-\u003edev,\n+\t\t\t\t\"%s: Error to push l2 rule\\n\",\n+\t\t\t\t__func__);\n+\t\tkfree(l2_entry);\n+\t}\n+\n+\tif (!list_empty(\u0026l2_offl_lh))\n+\t\tqueue_work(rvu_sw_l2_offl_wq, \u0026l2_offl_work.work);\n+}\n+\n+static bool fw_is_up;\n+\n+int rvu_sw_l2_init_offl_wq(struct rvu *rvu, u16 pcifunc, bool fw_up)\n+{\n+\tstruct rvu_switch *rswitch;\n+\n+\trswitch = \u0026rvu-\u003erswitch;\n+\n+\tif (fw_up \u0026\u0026 !fw_is_up) {\n+\t\t/* Switch HW sends an MBOX message to Host once it is ready */\n+\t\trswitch-\u003epcifunc = pcifunc;\n+\t\tfw_is_up = true;\n+\n+\t\tl2_offl_work.rvu = rvu;\n+\t\tINIT_WORK(\u0026l2_offl_work.work, rvu_sw_l2_offl_rule_wq_handler);\n+\t\trvu_sw_l2_offl_wq = alloc_workqueue(\"swdev_rvu_sw_l2_offl_wq\", 0, 0);\n+\t\tif (!rvu_sw_l2_offl_wq) {\n+\t\t\tdev_err(rvu-\u003edev, \"L2 offl workqueue allocation failed\\n\");\n+\t\t\treturn -ENOMEM;\n+\t\t}\n+\n+\t\tfdb_refresh_work.rvu = rvu;\n+\t\tINIT_WORK(\u0026fdb_refresh_work.work, rvu_sw_l2_fdb_refresh_wq_handler);\n+\t\tfdb_refresh_wq = alloc_workqueue(\"swdev_fdb_refresg_wq\", 0, 0);\n+\t\tif (!fdb_refresh_wq) {\n+\t\t\tdev_err(rvu-\u003edev, \"fdb refresh workqueue allocation failed\\n\");\n+\t\t\treturn -ENOMEM;\n+\t\t}\n+\n+\t\trswitch-\u003eflags |= RVU_SWITCH_FLAG_FW_READY;\n+\t\treturn 0;\n+\t}\n+\n+\trswitch-\u003eflags \u0026= ~RVU_SWITCH_FLAG_FW_READY;\n+\tflush_work(\u0026l2_offl_work.work);\n+\trswitch-\u003epcifunc = -1;\n+\treturn 0;\n+}\n+\n+int rvu_sw_l2_fdb_list_entry_add(struct rvu *rvu, u16 pcifunc, u8 *mac)\n+{\n+\tstruct l2_entry *l2_entry;\n+\n+\tl2_entry = kcalloc(1, sizeof(*l2_entry), GFP_KERNEL);\n+\tif (!l2_entry)\n+\t\treturn -ENOMEM;\n+\n+\tl2_entry-\u003eport_id = pcifunc;\n+\tether_addr_copy(l2_entry-\u003emac, mac);\n+\n+\tmutex_lock(\u0026fdb_refresh_list_lock);\n+\tlist_add_tail(\u0026l2_entry-\u003elist, \u0026fdb_refresh_lh);\n+\tmutex_unlock(\u0026fdb_refresh_list_lock);\n+\n+\tqueue_work(fdb_refresh_wq, \u0026fdb_refresh_work.work);\n+\treturn 0;\n+}\n+\n+int rvu_mbox_handler_fdb_notify(struct rvu *rvu,\n+\t\t\t\tstruct fdb_notify_req *req,\n+\t\t\t\tstruct msg_rsp *rsp)\n+{\n+\tstruct l2_entry *l2_entry;\n+\n+\tif (!(rvu-\u003erswitch.flags \u0026 RVU_SWITCH_FLAG_FW_READY))\n+\t\treturn 0;\n+\n+\tl2_entry = kcalloc(1, sizeof(*l2_entry), GFP_KERNEL);\n+\tif (!l2_entry)\n+\t\treturn -ENOMEM;\n+\n+\tl2_entry-\u003eport_id = rvu_sw_port_id(rvu, req-\u003ehdr.pcifunc);\n+\tether_addr_copy(l2_entry-\u003emac, req-\u003emac);\n+\tl2_entry-\u003eflags = req-\u003eflags;\n+\n+\tmutex_lock(\u0026l2_offl_list_lock);\n+\tlist_add_tail(\u0026l2_entry-\u003elist, \u0026l2_offl_lh);\n+\tmutex_unlock(\u0026l2_offl_list_lock);\n+\n+\tqueue_work(rvu_sw_l2_offl_wq, \u0026l2_offl_work.work);\n+\n+\treturn 0;\n+}\n+\n+void rvu_sw_l2_shutdown(void)\n+{\n+\tstruct l2_entry *entry;\n+\tLIST_HEAD(tlist);\n+\n+\tif (!fdb_refresh_wq)\n+\t\treturn;\n+\n+\tcancel_work_sync(\u0026fdb_refresh_work.work);\n+\tdestroy_workqueue(fdb_refresh_wq);\n+\n+\tmutex_lock(\u0026fdb_refresh_list_lock);\n+\twhile (1) {\n+\t\tentry = list_first_entry_or_null(\u0026fdb_refresh_lh,\n+\t\t\t\t\t\t struct l2_entry, list);\n+\t\tif (!entry)\n+\t\t\tbreak;\n+\n+\t\tlist_del_init(\u0026entry-\u003elist);\n+\t\tkfree(entry);\n+\t}\n+\tmutex_unlock(\u0026fdb_refresh_list_lock);\n+\n+\tcancel_work_sync(\u0026l2_offl_work.work);\n+\tdestroy_workqueue(rvu_sw_l2_offl_wq);\n+\n+\tmutex_lock(\u0026l2_offl_list_lock);\n+\twhile (1) {\n+\t\tentry = list_first_entry_or_null(\u0026l2_offl_lh,\n+\t\t\t\t\t\t struct l2_entry, list);\n+\t\tif (!entry)\n+\t\t\tbreak;\n+\n+\t\tlist_del_init(\u0026entry-\u003elist);\n+\t\tkfree(entry);\n+\t}\n+\tmutex_unlock(\u0026l2_offl_list_lock);\n+}\ndiff --git a/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.h b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.h\nnew file mode 100644\nindex 00000000000000..6685431d60a236\n--- /dev/null\n+++ b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.h\n@@ -0,0 +1,14 @@\n+/* SPDX-License-Identifier: GPL-2.0 */\n+/* Marvell RVU Admin Function driver\n+ *\n+ * Copyright (C) 2026 Marvell.\n+ *\n+ */\n+\n+#ifndef RVU_SW_L2_H\n+#define RVU_SW_L2_H\n+\n+int rvu_sw_l2_init_offl_wq(struct rvu *rvu, u16 pcifunc, bool fw_up);\n+int rvu_sw_l2_fdb_list_entry_add(struct rvu *rvu, u16 pcifunc, u8 *mac);\n+void rvu_sw_l2_shutdown(void);\n+#endif\ndiff --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\nnew file mode 100644\nindex 00000000000000..bd4ec3b20d4af9\n--- /dev/null\n+++ b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l3.c\n@@ -0,0 +1,240 @@\n+// SPDX-License-Identifier: GPL-2.0\n+/* Marvell RVU Admin Function driver\n+ *\n+ * Copyright (C) 2026 Marvell.\n+ *\n+ */\n+\n+#include \u003clinux/bitfield.h\u003e\n+#include \"rvu.h\"\n+#include \"rvu_sw.h\"\n+#include \"rvu_sw_l3.h\"\n+\n+#define M(_name, _id, _fn_name, _req_type, _rsp_type)\t\t\t\\\n+static struct _req_type __maybe_unused\t\t\t\t\t\\\n+*otx2_mbox_alloc_msg_ ## _fn_name(struct rvu *rvu, int devid)\t\t\\\n+{\t\t\t\t\t\t\t\t\t\\\n+\tstruct _req_type *req;\t\t\t\t\t\t\\\n+\t\t\t\t\t\t\t\t\t\\\n+\treq = (struct _req_type *)otx2_mbox_alloc_msg_rsp(\t\t\\\n+\t\t\u0026rvu-\u003eafpf_wq_info.mbox_up, devid, sizeof(struct _req_type), \\\n+\t\tsizeof(struct _rsp_type));\t\t\t\t\\\n+\tif (!req)\t\t\t\t\t\t\t\\\n+\t\treturn NULL;\t\t\t\t\t\t\\\n+\treq-\u003ehdr.sig = OTX2_MBOX_REQ_SIG;\t\t\t\t\\\n+\treq-\u003ehdr.id = _id;\t\t\t\t\t\t\\\n+\treturn req;\t\t\t\t\t\t\t\\\n+}\n+MBOX_UP_AF2SWDEV_MESSAGES\n+#undef M\n+\n+static struct workqueue_struct *sw_l3_offl_wq;\n+\n+struct l3_entry {\n+\tstruct list_head list;\n+\tstruct rvu *rvu;\n+\tu32 port_id;\n+\tint cnt;\n+\tstruct fib_entry entry[];\n+};\n+\n+static DEFINE_MUTEX(l3_offl_llock);\n+static LIST_HEAD(l3_offl_lh);\n+static bool l3_offl_work_running;\n+\n+static struct workqueue_struct *sw_l3_offl_wq;\n+static void sw_l3_offl_work_handler(struct work_struct *work);\n+static DECLARE_DELAYED_WORK(l3_offl_work, sw_l3_offl_work_handler);\n+\n+static int rvu_sw_l3_offl_rule_push(struct list_head *lh)\n+{\n+\tstruct af2swdev_notify_req *req;\n+\tstruct fib_entry *entry, *dst;\n+\tstruct l3_entry *l3_entry;\n+\tstruct rvu *rvu;\n+\tint tot_cnt = 0;\n+\tint swdev_pf;\n+\tint sz, cnt;\n+\tbool rc;\n+\n+\tBUILD_BUG_ON(sizeof(*req) \u003e 1024);\n+\n+\tl3_entry = list_first_entry_or_null(lh, struct l3_entry, list);\n+\tif (!l3_entry)\n+\t\treturn 0;\n+\n+\trvu = l3_entry-\u003ervu;\n+\tswdev_pf = rvu_get_pf(rvu-\u003epdev, rvu-\u003erswitch.pcifunc);\n+\n+\tmutex_lock(\u0026rvu-\u003embox_lock);\n+\treq = otx2_mbox_alloc_msg_af2swdev_notify(rvu, swdev_pf);\n+\tif (!req) {\n+\t\tmutex_unlock(\u0026rvu-\u003embox_lock);\n+\n+\t\twhile ((l3_entry =\n+\t\t\tlist_first_entry_or_null(lh,\n+\t\t\t\t\t\t struct l3_entry, list)) != NULL) {\n+\t\t\tlist_del_init(\u0026l3_entry-\u003elist);\n+\t\t\tkfree(l3_entry);\n+\t\t}\n+\n+\t\treturn -ENOMEM;\n+\t}\n+\n+\tdst = \u0026req-\u003eentry[0];\n+\twhile ((l3_entry =\n+\t\tlist_first_entry_or_null(lh,\n+\t\t\t\t\t struct l3_entry, list)) != NULL) {\n+\t\tentry = l3_entry-\u003eentry;\n+\t\tcnt = l3_entry-\u003ecnt;\n+\t\tsz = sizeof(*entry) * cnt;\n+\n+\t\tmemcpy(dst, entry, sz);\n+\t\ttot_cnt += cnt;\n+\t\tdst += cnt;\n+\n+\t\tlist_del_init(\u0026l3_entry-\u003elist);\n+\t\tkfree(l3_entry);\n+\t}\n+\treq-\u003eflags = FIB_CMD;\n+\treq-\u003ecnt = tot_cnt;\n+\n+\trc = otx2_mbox_wait_for_zero(\u0026rvu-\u003eafpf_wq_info.mbox_up, swdev_pf);\n+\tif (rc)\n+\t\totx2_mbox_msg_send_up(\u0026rvu-\u003eafpf_wq_info.mbox_up, swdev_pf);\n+\n+\tmutex_unlock(\u0026rvu-\u003embox_lock);\n+\treturn rc ? 0 : -EFAULT;\n+}\n+\n+static atomic64_t req_cnt;\n+static atomic64_t ack_cnt;\n+static atomic64_t req_processed;\n+static LIST_HEAD(l3_local_lh);\n+static int lcnt;\n+\n+static void sw_l3_offl_work_handler(struct work_struct *work)\n+{\n+\tstruct l3_entry *l3_entry;\n+\tstruct list_head l3lh;\n+\tu64 req, ack, proc;\n+\n+\tINIT_LIST_HEAD(\u0026l3lh);\n+\n+\tmutex_lock(\u0026l3_offl_llock);\n+\twhile (1) {\n+\t\tl3_entry = list_first_entry_or_null(\u0026l3_offl_lh, struct l3_entry, list);\n+\n+\t\tif (!l3_entry)\n+\t\t\tbreak;\n+\n+\t\tif (lcnt + l3_entry-\u003ecnt \u003e 8 \u0026\u0026 !list_empty(\u0026l3_local_lh)) {\n+\t\t\treq = atomic64_read(\u0026req_cnt);\n+\t\t\tatomic64_set(\u0026ack_cnt, req);\n+\t\t\tatomic64_set(\u0026req_processed, req);\n+\t\t\tmutex_unlock(\u0026l3_offl_llock);\n+\t\t\tgoto process;\n+\t\t}\n+\n+\t\tlcnt += l3_entry-\u003ecnt;\n+\n+\t\tatomic64_inc(\u0026req_cnt);\n+\t\tlist_del_init(\u0026l3_entry-\u003elist);\n+\t\tlist_add_tail(\u0026l3_entry-\u003elist, \u0026l3_local_lh);\n+\t}\n+\tmutex_unlock(\u0026l3_offl_llock);\n+\n+\treq = atomic64_read(\u0026req_cnt);\n+\tack = atomic64_read(\u0026ack_cnt);\n+\n+\tif (req \u003e ack) {\n+\t\tatomic64_set(\u0026ack_cnt, req);\n+\t\tqueue_delayed_work(sw_l3_offl_wq, \u0026l3_offl_work,\n+\t\t\t\t msecs_to_jiffies(100));\n+\t\treturn;\n+\t}\n+\n+\tproc = atomic64_read(\u0026req_processed);\n+\tif (req == proc) {\n+\t\tqueue_delayed_work(sw_l3_offl_wq, \u0026l3_offl_work,\n+\t\t\t\t msecs_to_jiffies(1000));\n+\t\treturn;\n+\t}\n+\n+\tatomic64_set(\u0026req_processed, req);\n+\n+process:\n+\tlcnt = 0;\n+\n+\tmutex_lock(\u0026l3_offl_llock);\n+\tlist_splice_init(\u0026l3_local_lh, \u0026l3lh);\n+\tmutex_unlock(\u0026l3_offl_llock);\n+\n+\tif (rvu_sw_l3_offl_rule_push(\u0026l3lh))\n+\t\tpr_err(\"%s: Error to push rules\\n\", __func__);\n+\n+\tqueue_delayed_work(sw_l3_offl_wq, \u0026l3_offl_work, msecs_to_jiffies(100));\n+}\n+\n+int rvu_mbox_handler_fib_notify(struct rvu *rvu,\n+\t\t\t\tstruct fib_notify_req *req,\n+\t\t\t\tstruct msg_rsp *rsp)\n+{\n+\tstruct l3_entry *l3_entry;\n+\tint sz;\n+\n+\tif (!(rvu-\u003erswitch.flags \u0026 RVU_SWITCH_FLAG_FW_READY))\n+\t\treturn 0;\n+\n+\tif (req-\u003ecnt \u003e 16)\n+\t\treturn -EINVAL;\n+\n+\tsz = req-\u003ecnt * sizeof(struct fib_entry);\n+\n+\tl3_entry = kcalloc(1, sizeof(*l3_entry) + sz, GFP_KERNEL);\n+\tif (!l3_entry)\n+\t\treturn -ENOMEM;\n+\n+\tl3_entry-\u003eport_id = rvu_sw_port_id(rvu, req-\u003ehdr.pcifunc);\n+\tl3_entry-\u003ervu = rvu;\n+\tl3_entry-\u003ecnt = req-\u003ecnt;\n+\tINIT_LIST_HEAD(\u0026l3_entry-\u003elist);\n+\tmemcpy(l3_entry-\u003eentry, req-\u003eentry, sz);\n+\n+\tmutex_lock(\u0026l3_offl_llock);\n+\tlist_add_tail(\u0026l3_entry-\u003elist, \u0026l3_offl_lh);\n+\tmutex_unlock(\u0026l3_offl_llock);\n+\n+\tif (!l3_offl_work_running) {\n+\t\tsw_l3_offl_wq = alloc_workqueue(\"sw_af_fib_wq\", 0, 0);\n+\t\tl3_offl_work_running = true;\n+\t\tqueue_delayed_work(sw_l3_offl_wq, \u0026l3_offl_work,\n+\t\t\t\t msecs_to_jiffies(1000));\n+\t}\n+\n+\treturn 0;\n+}\n+\n+void rvu_sw_l3_shutdown(void)\n+{\n+\tstruct l3_entry *entry;\n+\tLIST_HEAD(tlist);\n+\n+\tif (!sw_l3_offl_wq)\n+\t\treturn;\n+\n+\tcancel_delayed_work_sync(\u0026l3_offl_work);\n+\tdestroy_workqueue(sw_l3_offl_wq);\n+\n+\tmutex_lock(\u0026l3_offl_llock);\n+\twhile (1) {\n+\t\tentry = list_first_entry_or_null(\u0026l3_offl_lh,\n+\t\t\t\t\t\t struct l3_entry, list);\n+\t\tif (!entry)\n+\t\t\tbreak;\n+\n+\t\tlist_del_init(\u0026entry-\u003elist);\n+\t\tkfree(entry);\n+\t}\n+\tmutex_unlock(\u0026l3_offl_llock);\n+}\ndiff --git a/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l3.h b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l3.h\nnew file mode 100644\nindex 00000000000000..153f1415466deb\n--- /dev/null\n+++ b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l3.h\n@@ -0,0 +1,12 @@\n+/* SPDX-License-Identifier: GPL-2.0 */\n+/* Marvell RVU Admin Function driver\n+ *\n+ * Copyright (C) 2026 Marvell.\n+ *\n+ */\n+\n+#ifndef RVU_SW_L3_H\n+#define RVU_SW_L3_H\n+\n+void rvu_sw_l3_shutdown(void);\n+#endif\ndiff --git a/drivers/net/ethernet/marvell/octeontx2/nic/Makefile b/drivers/net/ethernet/marvell/octeontx2/nic/Makefile\nindex 883e9f4d601c5c..871a55ee379843 100644\n--- a/drivers/net/ethernet/marvell/octeontx2/nic/Makefile\n+++ b/drivers/net/ethernet/marvell/octeontx2/nic/Makefile\n@@ -9,7 +9,18 @@ obj-$(CONFIG_RVU_ESWITCH) += rvu_rep.o\n \n rvu_nicpf-y := otx2_pf.o otx2_common.o otx2_txrx.o otx2_ethtool.o \\\n otx2_flows.o otx2_tc.o cn10k.o cn20k.o otx2_dmac_flt.o \\\n- otx2_devlink.o qos_sq.o qos.o otx2_xsk.o\n+ otx2_devlink.o qos_sq.o qos.o otx2_xsk.o \\\n+\t switch/sw_fdb.o switch/sw_fl.o switch/sw_trace.o\n+\n+ifdef CONFIG_OCTEONTX_SWITCH\n+rvu_nicpf-y += switch/sw_nb.o switch/sw_fib.o \\\n+\t switch/sw_nb_v4.o\n+\n+ifdef CONFIG_IPV6\n+rvu_nicpf-y += switch/sw_nb_v6.o\n+endif\n+endif\n+\n rvu_nicvf-y := otx2_vf.o\n rvu_rep-y := rep.o\n \ndiff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.h b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.h\nindex acf259d7200883..73a98b94426b04 100644\n--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.h\n+++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.h\n@@ -78,6 +78,8 @@ struct otx2_rcv_queue {\n struct sg_list {\n \tu16\tnum_segs;\n \tu16\tflags;\n+\tu16\tcq_idx;\n+\tu16\tlen;\n \tu64\tskb;\n \tu64\tsize[OTX2_MAX_FRAGS_IN_SQE];\n \tu64\tdma_addr[OTX2_MAX_FRAGS_IN_SQE];\ndiff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c\nindex b022f52c684508..4540ffacdddc03 100644\n--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c\n+++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c\n@@ -15,6 +15,7 @@\n #include \"otx2_ptp.h\"\n #include \"cn10k.h\"\n #include \"cn10k_ipsec.h\"\n+#include \"switch/sw_nb.h\"\n \n #define DRV_NAME\t\"rvu_nicvf\"\n #define DRV_STRING\t\"Marvell RVU NIC Virtual Function Driver\"\n@@ -141,6 +142,22 @@ static int otx2vf_process_mbox_msg_up(struct otx2_nic *vf,\n \t\terr = otx2_mbox_up_handler_cgx_link_event(\n \t\t\t\tvf, (struct cgx_link_info_msg *)req, rsp);\n \t\treturn err;\n+\n+\tcase MBOX_MSG_AF2PF_FDB_REFRESH:\n+\t\trsp = (struct msg_rsp *)otx2_mbox_alloc_msg(\u0026vf-\u003embox.mbox_up, 0,\n+\t\t\t\t\t\t\t sizeof(struct msg_rsp));\n+\t\tif (!rsp)\n+\t\t\treturn -ENOMEM;\n+\n+\t\trsp-\u003ehdr.id = MBOX_MSG_AF2PF_FDB_REFRESH;\n+\t\trsp-\u003ehdr.sig = OTX2_MBOX_RSP_SIG;\n+\t\trsp-\u003ehdr.pcifunc = req-\u003epcifunc;\n+\t\trsp-\u003ehdr.rc = 0;\n+\t\terr = otx2_mbox_up_handler_af2pf_fdb_refresh(vf,\n+\t\t\t\t\t\t\t (struct af2pf_fdb_refresh_req *)req,\n+\t\t\t\t\t\t\t rsp);\n+\t\treturn err;\n+\n \tdefault:\n \t\totx2_reply_invalid_msg(\u0026vf-\u003embox.mbox_up, 0, 0, req-\u003eid);\n \t\treturn -ENODEV;\ndiff --git a/drivers/net/ethernet/marvell/octeontx2/nic/rep.c b/drivers/net/ethernet/marvell/octeontx2/nic/rep.c\nindex 0f5d5642d3f7a1..e4c01ac8747713 100644\n--- a/drivers/net/ethernet/marvell/octeontx2/nic/rep.c\n+++ b/drivers/net/ethernet/marvell/octeontx2/nic/rep.c\n@@ -15,6 +15,7 @@\n #include \"cn10k.h\"\n #include \"otx2_reg.h\"\n #include \"rep.h\"\n+#include \"switch/sw_nb.h\"\n \n #define DRV_NAME\t\"rvu_rep\"\n #define DRV_STRING\t\"Marvell RVU Representor Driver\"\n@@ -399,8 +400,14 @@ static void rvu_rep_get_stats64(struct net_device *dev,\n \n static int rvu_eswitch_config(struct otx2_nic *priv, u8 ena)\n {\n+#if IS_ENABLED(CONFIG_OCTEONTX_SWITCH)\n+\tstruct net_device *netdev = priv-\u003enetdev;\n+#endif\n+\tstruct devlink_port_attrs attrs = {};\n \tstruct esw_cfg_req *req;\n \n+\trvu_rep_devlink_set_switch_id(priv, \u0026attrs.switch_id);\n+\n \tmutex_lock(\u0026priv-\u003embox.lock);\n \treq = otx2_mbox_alloc_msg_esw_cfg(\u0026priv-\u003embox);\n \tif (!req) {\n@@ -408,8 +415,14 @@ static int rvu_eswitch_config(struct otx2_nic *priv, u8 ena)\n \t\treturn -ENOMEM;\n \t}\n \treq-\u003eena = ena;\n+\tmemcpy(req-\u003eswitch_id, attrs.switch_id.id, attrs.switch_id.id_len);\n \totx2_sync_mbox_msg(\u0026priv-\u003embox);\n \tmutex_unlock(\u0026priv-\u003embox.lock);\n+\n+#if IS_ENABLED(CONFIG_OCTEONTX_SWITCH)\n+\tena ? sw_nb_register(netdev) : sw_nb_unregister(netdev);\n+#endif\n+\n \treturn 0;\n }\n \ndiff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fdb.c b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fdb.c\nnew file mode 100644\nindex 00000000000000..327a6efeb52630\n--- /dev/null\n+++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fdb.c\n@@ -0,0 +1,165 @@\n+// SPDX-License-Identifier: GPL-2.0\n+/* Marvell RVU switch driver\n+ *\n+ * Copyright (C) 2026 Marvell.\n+ *\n+ */\n+#include \u003clinux/kernel.h\u003e\n+#include \u003clinux/netdevice.h\u003e\n+#include \u003clinux/etherdevice.h\u003e\n+#include \u003cnet/switchdev.h\u003e\n+#include \u003cnet/netevent.h\u003e\n+#include \u003cnet/arp.h\u003e\n+\n+#include \"../otx2_reg.h\"\n+#include \"../otx2_common.h\"\n+#include \"../otx2_struct.h\"\n+#include \"../cn10k.h\"\n+#include \"sw_nb.h\"\n+#include \"sw_fdb.h\"\n+\n+#if !IS_ENABLED(CONFIG_OCTEONTX_SWITCH)\n+\n+int otx2_mbox_up_handler_af2pf_fdb_refresh(struct otx2_nic *pf,\n+\t\t\t\t\t struct af2pf_fdb_refresh_req *req,\n+\t\t\t\t\t struct msg_rsp *rsp)\n+{\n+\treturn 0;\n+}\n+\n+#else\n+\n+static DEFINE_SPINLOCK(sw_fdb_llock);\n+static LIST_HEAD(sw_fdb_lh);\n+\n+struct sw_fdb_list_entry {\n+\tstruct list_head list;\n+\tu64 flags;\n+\tstruct otx2_nic *pf;\n+\tu8 mac[ETH_ALEN];\n+\tbool add_fdb;\n+};\n+\n+static struct workqueue_struct *sw_fdb_wq;\n+static struct work_struct sw_fdb_work;\n+\n+static int sw_fdb_add_or_del(struct otx2_nic *pf,\n+\t\t\t const unsigned char *addr,\n+\t\t\t bool add_fdb)\n+{\n+\tstruct fdb_notify_req *req;\n+\tint rc;\n+\n+\tmutex_lock(\u0026pf-\u003embox.lock);\n+\treq = otx2_mbox_alloc_msg_fdb_notify(\u0026pf-\u003embox);\n+\tif (!req) {\n+\t\trc = -ENOMEM;\n+\t\tgoto out;\n+\t}\n+\n+\tether_addr_copy(req-\u003emac, addr);\n+\treq-\u003eflags = add_fdb ? FDB_ADD : FDB_DEL;\n+\n+\trc = otx2_sync_mbox_msg(\u0026pf-\u003embox);\n+out:\n+\tmutex_unlock(\u0026pf-\u003embox.lock);\n+\treturn rc;\n+}\n+\n+static void sw_fdb_wq_handler(struct work_struct *work)\n+{\n+\tstruct sw_fdb_list_entry *entry;\n+\tLIST_HEAD(tlist);\n+\n+\tspin_lock(\u0026sw_fdb_llock);\n+\tlist_splice_init(\u0026sw_fdb_lh, \u0026tlist);\n+\tspin_unlock(\u0026sw_fdb_llock);\n+\n+\twhile ((entry =\n+\t\tlist_first_entry_or_null(\u0026tlist,\n+\t\t\t\t\t struct sw_fdb_list_entry,\n+\t\t\t\t\t list)) != NULL) {\n+\t\tlist_del_init(\u0026entry-\u003elist);\n+\t\tif (sw_fdb_add_or_del(entry-\u003epf, entry-\u003emac, entry-\u003eadd_fdb))\n+\t\t\tnetdev_err(entry-\u003epf-\u003enetdev,\n+\t\t\t\t \"Error to add/del fdb %pM entry\\n\",\n+\t\t\t\t entry-\u003emac);\n+\t\tdev_put(entry-\u003epf-\u003enetdev);\n+\t\tkfree(entry);\n+\t}\n+\n+\tspin_lock(\u0026sw_fdb_llock);\n+\tif (!list_empty(\u0026sw_fdb_lh))\n+\t\tqueue_work(sw_fdb_wq, \u0026sw_fdb_work);\n+\tspin_unlock(\u0026sw_fdb_llock);\n+}\n+\n+int sw_fdb_add_to_list(struct net_device *dev, u8 *mac, bool add_fdb)\n+{\n+\tstruct otx2_nic *pf = netdev_priv(dev);\n+\tstruct sw_fdb_list_entry *entry;\n+\n+\tentry = kcalloc(1, sizeof(*entry), GFP_ATOMIC);\n+\tif (!entry)\n+\t\treturn -ENOMEM;\n+\n+\tether_addr_copy(entry-\u003emac, mac);\n+\tentry-\u003eadd_fdb = add_fdb;\n+\tentry-\u003epf = pf;\n+\tdev_hold(dev);\n+\n+\tspin_lock(\u0026sw_fdb_llock);\n+\tlist_add_tail(\u0026entry-\u003elist, \u0026sw_fdb_lh);\n+\tqueue_work(sw_fdb_wq, \u0026sw_fdb_work);\n+\tspin_unlock(\u0026sw_fdb_llock);\n+\n+\treturn 0;\n+}\n+\n+int sw_fdb_init(void)\n+{\n+\tINIT_WORK(\u0026sw_fdb_work, sw_fdb_wq_handler);\n+\tsw_fdb_wq = alloc_workqueue(\"sw_fdb_wq\", 0, 0);\n+\tif (!sw_fdb_wq)\n+\t\treturn -ENOMEM;\n+\n+\treturn 0;\n+}\n+\n+void sw_fdb_deinit(void)\n+{\n+\tstruct sw_fdb_list_entry *entry;\n+\tLIST_HEAD(tlist);\n+\n+\tcancel_work_sync(\u0026sw_fdb_work);\n+\tdestroy_workqueue(sw_fdb_wq);\n+\n+\tspin_lock(\u0026sw_fdb_llock);\n+\tlist_splice_init(\u0026sw_fdb_lh, \u0026tlist);\n+\tspin_unlock(\u0026sw_fdb_llock);\n+\n+\twhile ((entry =\n+\t\tlist_first_entry_or_null(\u0026tlist,\n+\t\t\t\t\t struct sw_fdb_list_entry,\n+\t\t\t\t\t list)) != NULL) {\n+\t\tlist_del_init(\u0026entry-\u003elist);\n+\t\tdev_put(entry-\u003epf-\u003enetdev);\n+\t\tkfree(entry);\n+\t}\n+}\n+\n+int otx2_mbox_up_handler_af2pf_fdb_refresh(struct otx2_nic *pf,\n+\t\t\t\t\t struct af2pf_fdb_refresh_req *req,\n+\t\t\t\t\t struct msg_rsp *rsp)\n+{\n+\tstruct switchdev_notifier_fdb_info item = {0};\n+\n+\titem.addr = req-\u003emac;\n+\titem.info.dev = pf-\u003enetdev;\n+\tcall_switchdev_notifiers(SWITCHDEV_FDB_ADD_TO_BRIDGE,\n+\t\t\t\t item.info.dev, \u0026item.info, NULL);\n+\n+\treturn 0;\n+}\n+#endif\n+EXPORT_SYMBOL(otx2_mbox_up_handler_af2pf_fdb_refresh);\ndiff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fdb.h b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fdb.h\nnew file mode 100644\nindex 00000000000000..3b06a77e6b56ae\n--- /dev/null\n+++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fdb.h\n@@ -0,0 +1,14 @@\n+/* SPDX-License-Identifier: GPL-2.0 */\n+/* Marvell switch driver\n+ *\n+ * Copyright (C) 2026 Marvell.\n+ *\n+ */\n+#ifndef SW_FDB_H_\n+#define SW_FDB_H_\n+\n+int sw_fdb_add_to_list(struct net_device *dev, u8 *mac, bool add_fdb);\n+void sw_fdb_deinit(void);\n+int sw_fdb_init(void);\n+\n+#endif // SW_FDB_H\ndiff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fib.c b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fib.c\nnew file mode 100644\nindex 00000000000000..54b854aa7ffa63\n--- /dev/null\n+++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fib.c\n@@ -0,0 +1,139 @@\n+// SPDX-License-Identifier: GPL-2.0\n+/* Marvell RVU switch driver\n+ *\n+ * Copyright (C) 2026 Marvell.\n+ *\n+ */\n+#include \u003clinux/kernel.h\u003e\n+#include \u003clinux/netdevice.h\u003e\n+#include \u003clinux/etherdevice.h\u003e\n+#include \u003cnet/switchdev.h\u003e\n+#include \u003cnet/netevent.h\u003e\n+#include \u003cnet/arp.h\u003e\n+#include \u003cnet/route.h\u003e\n+\n+#include \"../otx2_reg.h\"\n+#include \"../otx2_common.h\"\n+#include \"../otx2_struct.h\"\n+#include \"../cn10k.h\"\n+#include \"sw_nb.h\"\n+#include \"sw_fib.h\"\n+\n+static DEFINE_SPINLOCK(sw_fib_llock);\n+static LIST_HEAD(sw_fib_lh);\n+\n+static struct workqueue_struct *sw_fib_wq;\n+static void sw_fib_work_handler(struct work_struct *work);\n+static DECLARE_DELAYED_WORK(sw_fib_work, sw_fib_work_handler);\n+\n+struct sw_fib_list_entry {\n+\tstruct list_head lh;\n+\tstruct otx2_nic *pf;\n+\tint cnt;\n+\tstruct fib_entry *entry;\n+};\n+\n+static int sw_fib_notify(struct otx2_nic *pf,\n+\t\t\t int cnt,\n+\t\t\t struct fib_entry *entry)\n+{\n+\tstruct fib_notify_req *req;\n+\tint rc;\n+\n+\tif (cnt \u003e 16)\n+\t\treturn -EINVAL;\n+\n+\tmutex_lock(\u0026pf-\u003embox.lock);\n+\treq = otx2_mbox_alloc_msg_fib_notify(\u0026pf-\u003embox);\n+\tif (!req) {\n+\t\trc = -ENOMEM;\n+\t\tgoto out;\n+\t}\n+\n+\treq-\u003ecnt = cnt;\n+\tmemcpy(req-\u003eentry, entry, sizeof(*entry) * cnt);\n+\n+\trc = otx2_sync_mbox_msg(\u0026pf-\u003embox);\n+out:\n+\tmutex_unlock(\u0026pf-\u003embox.lock);\n+\treturn rc;\n+}\n+\n+static void sw_fib_work_handler(struct work_struct *work)\n+{\n+\tstruct sw_fib_list_entry *lentry;\n+\tLIST_HEAD(tlist);\n+\n+\tspin_lock_bh(\u0026sw_fib_llock);\n+\tlist_splice_init(\u0026sw_fib_lh, \u0026tlist);\n+\tspin_unlock_bh(\u0026sw_fib_llock);\n+\n+\twhile ((lentry =\n+\t\tlist_first_entry_or_null(\u0026tlist,\n+\t\t\t\t\t struct sw_fib_list_entry, lh)) != NULL) {\n+\t\tlist_del_init(\u0026lentry-\u003elh);\n+\t\tsw_fib_notify(lentry-\u003epf, lentry-\u003ecnt, lentry-\u003eentry);\n+\t\tkfree(lentry-\u003eentry);\n+\t\tkfree(lentry);\n+\t}\n+\n+\tspin_lock_bh(\u0026sw_fib_llock);\n+\tif (!list_empty(\u0026sw_fib_lh))\n+\t\tqueue_delayed_work(sw_fib_wq, \u0026sw_fib_work,\n+\t\t\t\t msecs_to_jiffies(10));\n+\tspin_unlock_bh(\u0026sw_fib_llock);\n+}\n+\n+int sw_fib_add_to_list(struct net_device *dev,\n+\t\t struct fib_entry *entry, int cnt)\n+{\n+\tstruct otx2_nic *pf = netdev_priv(dev);\n+\tstruct sw_fib_list_entry *lentry;\n+\n+\tlentry = kcalloc(1, sizeof(*lentry), GFP_ATOMIC);\n+\tif (!lentry)\n+\t\treturn -ENOMEM;\n+\n+\tlentry-\u003epf = pf;\n+\tlentry-\u003ecnt = cnt;\n+\tlentry-\u003eentry = entry;\n+\tINIT_LIST_HEAD(\u0026lentry-\u003elh);\n+\n+\tspin_lock(\u0026sw_fib_llock);\n+\tlist_add_tail(\u0026lentry-\u003elh, \u0026sw_fib_lh);\n+\tqueue_delayed_work(sw_fib_wq, \u0026sw_fib_work,\n+\t\t\t msecs_to_jiffies(10));\n+\tspin_unlock(\u0026sw_fib_llock);\n+\n+\treturn 0;\n+}\n+\n+int sw_fib_init(void)\n+{\n+\tsw_fib_wq = alloc_workqueue(\"sw_pf_fib_wq\", 0, 0);\n+\tif (!sw_fib_wq)\n+\t\treturn -ENOMEM;\n+\n+\treturn 0;\n+}\n+\n+void sw_fib_deinit(void)\n+{\n+\tstruct sw_fib_list_entry *lentry;\n+\tLIST_HEAD(tlist);\n+\n+\tcancel_delayed_work_sync(\u0026sw_fib_work);\n+\tdestroy_workqueue(sw_fib_wq);\n+\n+\tspin_lock_bh(\u0026sw_fib_llock);\n+\tlist_splice_init(\u0026sw_fib_lh, \u0026tlist);\n+\tspin_unlock_bh(\u0026sw_fib_llock);\n+\n+\twhile ((lentry =\n+\t\tlist_first_entry_or_null(\u0026tlist,\n+\t\t\t\t\t struct sw_fib_list_entry, lh)) != NULL) {\n+\t\tlist_del_init(\u0026lentry-\u003elh);\n+\t\tkfree(lentry-\u003eentry);\n+\t\tkfree(lentry);\n+\t}\n+}\ndiff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fib.h b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fib.h\nnew file mode 100644\nindex 00000000000000..50c4fbca81e83b\n--- /dev/null\n+++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fib.h\n@@ -0,0 +1,16 @@\n+/* SPDX-License-Identifier: GPL-2.0 */\n+/* Marvell switch driver\n+ *\n+ * Copyright (C) 2026 Marvell.\n+ *\n+ */\n+#ifndef SW_FIB_H_\n+#define SW_FIB_H_\n+\n+int sw_fib_add_to_list(struct net_device *dev,\n+\t\t struct fib_entry *entry, int cnt);\n+\n+void sw_fib_deinit(void);\n+int sw_fib_init(void);\n+\n+#endif // SW_FIB_H\ndiff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.c b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.c\nnew file mode 100644\nindex 00000000000000..3cf4c40b1059a6\n--- /dev/null\n+++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.c\n@@ -0,0 +1,581 @@\n+// SPDX-License-Identifier: GPL-2.0\n+/* Marvell RVU switch driver\n+ *\n+ * Copyright (C) 2026 Marvell.\n+ *\n+ */\n+#include \u003clinux/kernel.h\u003e\n+#include \u003clinux/netdevice.h\u003e\n+#include \u003clinux/etherdevice.h\u003e\n+#include \u003cnet/switchdev.h\u003e\n+#include \u003cnet/netevent.h\u003e\n+#include \u003cnet/arp.h\u003e\n+#include \u003cnet/nexthop.h\u003e\n+#include \u003cnet/netfilter/nf_flow_table.h\u003e\n+\n+#include \"../otx2_reg.h\"\n+#include \"../otx2_common.h\"\n+#include \"../otx2_struct.h\"\n+#include \"../cn10k.h\"\n+#include \"sw_nb.h\"\n+#include \"sw_trace.h\"\n+#include \"sw_fl.h\"\n+\n+#if !IS_ENABLED(CONFIG_OCTEONTX_SWITCH)\n+int sw_fl_setup_ft_block_ingress_cb(enum tc_setup_type type,\n+\t\t\t\t void *type_data, void *cb_priv)\n+{\n+\treturn -EOPNOTSUPP;\n+}\n+\n+#else\n+\n+static DEFINE_SPINLOCK(sw_fl_lock);\n+static LIST_HEAD(sw_fl_lh);\n+\n+struct sw_fl_list_entry {\n+\tstruct list_head list;\n+\tu64 flags;\n+\tunsigned long cookie;\n+\tstruct otx2_nic *pf;\n+\tstruct fl_tuple tuple;\n+};\n+\n+static struct workqueue_struct *sw_fl_wq;\n+static struct work_struct sw_fl_work;\n+\n+static int sw_fl_msg_send(struct otx2_nic *pf,\n+\t\t\t struct fl_tuple *tuple,\n+\t\t\t u64 flags,\n+\t\t\t unsigned long cookie)\n+{\n+\tstruct fl_notify_req *req;\n+\tint rc;\n+\n+\tmutex_lock(\u0026pf-\u003embox.lock);\n+\treq = otx2_mbox_alloc_msg_fl_notify(\u0026pf-\u003embox);\n+\tif (!req) {\n+\t\trc = -ENOMEM;\n+\t\tgoto out;\n+\t}\n+\n+\treq-\u003etuple = *tuple;\n+\treq-\u003eflags = flags;\n+\treq-\u003ecookie = cookie;\n+\n+\trc = otx2_sync_mbox_msg(\u0026pf-\u003embox);\n+out:\n+\tmutex_unlock(\u0026pf-\u003embox.lock);\n+\treturn rc;\n+}\n+\n+static void sw_fl_wq_handler(struct work_struct *work)\n+{\n+\tstruct sw_fl_list_entry *entry;\n+\tLIST_HEAD(tlist);\n+\n+\tspin_lock(\u0026sw_fl_lock);\n+\tlist_splice_init(\u0026sw_fl_lh, \u0026tlist);\n+\tspin_unlock(\u0026sw_fl_lock);\n+\n+\twhile ((entry =\n+\t\tlist_first_entry_or_null(\u0026tlist,\n+\t\t\t\t\t struct sw_fl_list_entry,\n+\t\t\t\t\t list)) != NULL) {\n+\t\tlist_del_init(\u0026entry-\u003elist);\n+\t\tsw_fl_msg_send(entry-\u003epf, \u0026entry-\u003etuple,\n+\t\t\t entry-\u003eflags, entry-\u003ecookie);\n+\t\tdev_put(entry-\u003epf-\u003enetdev);\n+\t\tkfree(entry);\n+\t}\n+\n+\tspin_lock(\u0026sw_fl_lock);\n+\tif (!list_empty(\u0026sw_fl_lh))\n+\t\tqueue_work(sw_fl_wq, \u0026sw_fl_work);\n+\tspin_unlock(\u0026sw_fl_lock);\n+}\n+\n+static int\n+sw_fl_add_to_list(struct otx2_nic *pf, struct fl_tuple *tuple,\n+\t\t unsigned long cookie, bool add_fl)\n+{\n+\tstruct sw_fl_list_entry *entry;\n+\n+\tentry = kcalloc(1, sizeof(*entry), GFP_ATOMIC);\n+\tif (!entry)\n+\t\treturn -ENOMEM;\n+\n+\tentry-\u003epf = pf;\n+\tentry-\u003eflags = add_fl ? FL_ADD : FL_DEL;\n+\tif (add_fl)\n+\t\tentry-\u003etuple = *tuple;\n+\tentry-\u003ecookie = cookie;\n+\tentry-\u003etuple.uni_di = netif_is_ovs_port(pf-\u003enetdev);\n+\n+\tspin_lock(\u0026sw_fl_lock);\n+\tdev_hold(pf-\u003enetdev);\n+\tlist_add_tail(\u0026entry-\u003elist, \u0026sw_fl_lh);\n+\tqueue_work(sw_fl_wq, \u0026sw_fl_work);\n+\tspin_unlock(\u0026sw_fl_lock);\n+\n+\treturn 0;\n+}\n+\n+static int sw_fl_parse_actions(struct otx2_nic *nic,\n+\t\t\t struct flow_action *flow_action,\n+\t\t\t struct flow_cls_offload *f,\n+\t\t\t struct fl_tuple *tuple, u64 *op)\n+{\n+\tstruct flow_action_entry *act;\n+\tstruct otx2_nic *out_nic;\n+\tint err;\n+\tint used = 0;\n+\tint i;\n+\n+\tif (!flow_action_has_entries(flow_action))\n+\t\treturn -EINVAL;\n+\n+\tflow_action_for_each(i, act, flow_action) {\n+\t\tif (used \u003e= MANGLE_ARR_SZ) {\n+\t\t\tnetdev_err(nic-\u003enetdev,\n+\t\t\t\t \"%s: More entries than supported %u\\n\",\n+\t\t\t\t __func__, used);\n+\t\t\treturn -ENOMEM;\n+\t\t}\n+\n+\t\tswitch (act-\u003eid) {\n+\t\tcase FLOW_ACTION_REDIRECT:\n+\t\t\ttrace_sw_act_dump(__func__, __LINE__, act-\u003eid);\n+\t\t\ttuple-\u003ein_pf = nic-\u003epcifunc;\n+\t\t\tout_nic = netdev_priv(act-\u003edev);\n+\t\t\ttuple-\u003exmit_pf = out_nic-\u003epcifunc;\n+\t\t\t*op |= BIT_ULL(FLOW_ACTION_REDIRECT);\n+\t\t\tbreak;\n+\n+\t\tcase FLOW_ACTION_CT:\n+\t\t\ttrace_sw_act_dump(__func__, __LINE__, act-\u003eid);\n+\t\t\terr = nf_flow_table_offload_add_cb(act-\u003ect.flow_table,\n+\t\t\t\t\t\t\t sw_fl_setup_ft_block_ingress_cb,\n+\t\t\t\t\t\t\t nic);\n+\t\t\tif (err != -EEXIST \u0026\u0026 err) {\n+\t\t\t\tnetdev_err(nic-\u003enetdev,\n+\t\t\t\t\t \"%s:%d Error to offload flow, err=%d\\n\",\n+\t\t\t\t\t __func__, __LINE__, err);\n+\t\t\t\tbreak;\n+\t\t\t}\n+\n+\t\t\t*op |= BIT_ULL(FLOW_ACTION_CT);\n+\t\t\tbreak;\n+\n+\t\tcase FLOW_ACTION_MANGLE:\n+\t\t\ttrace_sw_act_dump(__func__, __LINE__, act-\u003eid);\n+\t\t\ttuple-\u003emangle[used].type = act-\u003emangle.htype;\n+\t\t\ttuple-\u003emangle[used].val = act-\u003emangle.val;\n+\t\t\ttuple-\u003emangle[used].mask = act-\u003emangle.mask;\n+\t\t\ttuple-\u003emangle[used].offset = act-\u003emangle.offset;\n+\t\t\ttuple-\u003emangle_map[act-\u003emangle.htype] |= BIT(used);\n+\t\t\tused++;\n+\t\t\tbreak;\n+\n+\t\tdefault:\n+\t\t\ttrace_sw_act_dump(__func__, __LINE__, act-\u003eid);\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\n+\ttuple-\u003emangle_cnt = used;\n+\n+\tif (!*op) {\n+\t\tnetdev_dbg(nic-\u003enetdev, \"%s:%d Op is not valid\\n\", __func__, __LINE__);\n+\t\treturn -EOPNOTSUPP;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int sw_fl_get_route(struct fib_result *res, __be32 addr)\n+{\n+\tstruct flowi4 fl4;\n+\n+\tmemset(\u0026fl4, 0, sizeof(fl4));\n+\tfl4.daddr = addr;\n+\treturn fib_lookup(\u0026init_net, \u0026fl4, res, 0);\n+}\n+\n+static int sw_fl_get_pcifunc(struct otx2_nic *pf, __be32 dst, u16 *pcifunc,\n+\t\t\t struct fl_tuple *ftuple, bool is_in_dev)\n+{\n+\tstruct fib_nh_common *fib_nhc;\n+\tstruct net_device *dev, *br;\n+\tstruct fib_result res;\n+\tstruct list_head *lh;\n+\tstruct otx2_nic *nic;\n+\tint err;\n+\n+\trcu_read_lock();\n+\n+\terr = sw_fl_get_route(\u0026res, dst);\n+\tif (err) {\n+\t\tnetdev_err(pf-\u003enetdev,\n+\t\t\t \"%s:%d Failed to find route to dst %pI4\\n\",\n+\t\t\t __func__, __LINE__, \u0026dst);\n+\t\tgoto done;\n+\t}\n+\n+\tif (res.fi-\u003efib_type != RTN_UNICAST) {\n+\t\tnetdev_err(pf-\u003enetdev,\n+\t\t\t \"%s:%d Not unicast route to dst %pi4\\n\",\n+\t\t\t __func__, __LINE__, \u0026dst);\n+\t\terr = -EFAULT;\n+\t\tgoto done;\n+\t}\n+\n+\tfib_nhc = fib_info_nhc(res.fi, 0);\n+\tif (!fib_nhc) {\n+\t\terr = -EINVAL;\n+\t\tnetdev_err(pf-\u003enetdev,\n+\t\t\t \"%s:%d Could not get fib_nhc for %pI4\\n\",\n+\t\t\t __func__, __LINE__, \u0026dst);\n+\t\tgoto done;\n+\t}\n+\n+\tif (unlikely(netif_is_bridge_master(fib_nhc-\u003enhc_dev))) {\n+\t\tbr = fib_nhc-\u003enhc_dev;\n+\n+\t\tif (is_in_dev)\n+\t\t\tftuple-\u003eis_indev_br = 1;\n+\t\telse\n+\t\t\tftuple-\u003eis_xdev_br = 1;\n+\n+\t\tlh = \u0026br-\u003eadj_list.lower;\n+\t\tif (list_empty(lh)) {\n+\t\t\tnetdev_err(pf-\u003enetdev,\n+\t\t\t\t \"%s:%d Unable to find any slave device\\n\",\n+\t\t\t\t __func__, __LINE__);\n+\t\t\terr = -EINVAL;\n+\t\t\tgoto done;\n+\t\t}\n+\t\tdev = netdev_next_lower_dev_rcu(br, \u0026lh);\n+\n+\t} else {\n+\t\tdev = fib_nhc-\u003enhc_dev;\n+\t}\n+\n+\tif (!dev || !sw_nb_is_valid_dev(dev)) {\n+\t\tnetdev_err(pf-\u003enetdev,\n+\t\t\t \"%s:%d flow acceleration support is only for cavium devices\\n\",\n+\t\t\t __func__, __LINE__);\n+\t\terr = -EOPNOTSUPP;\n+\t\tgoto done;\n+\t}\n+\n+\tnic = netdev_priv(dev);\n+\t*pcifunc = nic-\u003epcifunc;\n+\n+done:\n+\trcu_read_unlock();\n+\treturn err;\n+}\n+\n+static int sw_fl_parse_flow(struct otx2_nic *nic, struct flow_cls_offload *f,\n+\t\t\t struct fl_tuple *tuple, u64 *features)\n+{\n+\tstruct flow_rule *rule;\n+\tu8 ip_proto = 0;\n+\n+\t*features = 0;\n+\n+\trule = flow_cls_offload_flow_rule(f);\n+\n+\tif (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) {\n+\t\tstruct flow_match_basic match;\n+\n+\t\tflow_rule_match_basic(rule, \u0026match);\n+\n+\t\t/* All EtherTypes can be matched, no hw limitation */\n+\n+\t\tif (match.mask-\u003en_proto) {\n+\t\t\ttuple-\u003eeth_type = match.key-\u003en_proto;\n+\t\t\ttuple-\u003em_eth_type = match.mask-\u003en_proto;\n+\t\t\t*features |= BIT_ULL(NPC_ETYPE);\n+\t\t}\n+\n+\t\tif (match.mask-\u003eip_proto \u0026\u0026\n+\t\t (match.key-\u003eip_proto != IPPROTO_TCP \u0026\u0026\n+\t\t match.key-\u003eip_proto != IPPROTO_UDP)) {\n+\t\t\tnetdev_dbg(nic-\u003enetdev,\n+\t\t\t\t \"ip_proto=%u not supported\\n\",\n+\t\t\t\t match.key-\u003eip_proto);\n+\t\t}\n+\n+\t\tif (match.mask-\u003eip_proto)\n+\t\t\tip_proto = match.key-\u003eip_proto;\n+\n+\t\tif (ip_proto == IPPROTO_UDP) {\n+\t\t\t*features |= BIT_ULL(NPC_IPPROTO_UDP);\n+\t\t} else if (ip_proto == IPPROTO_TCP) {\n+\t\t\t*features |= BIT_ULL(NPC_IPPROTO_TCP);\n+\t\t} else {\n+\t\t\tnetdev_dbg(nic-\u003enetdev,\n+\t\t\t\t \"ip_proto=%u not supported\\n\",\n+\t\t\t\t match.key-\u003eip_proto);\n+\t\t}\n+\n+\t\ttuple-\u003eproto = ip_proto;\n+\t}\n+\n+\tif (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {\n+\t\tstruct flow_match_eth_addrs match;\n+\n+\t\tflow_rule_match_eth_addrs(rule, \u0026match);\n+\n+\t\tif (!is_zero_ether_addr(match.key-\u003edst) \u0026\u0026\n+\t\t is_unicast_ether_addr(match.key-\u003edst)) {\n+\t\t\tether_addr_copy(tuple-\u003edmac,\n+\t\t\t\t\tmatch.key-\u003edst);\n+\n+\t\t\tether_addr_copy(tuple-\u003em_dmac,\n+\t\t\t\t\tmatch.mask-\u003edst);\n+\n+\t\t\t*features |= BIT_ULL(NPC_DMAC);\n+\t\t}\n+\n+\t\tif (!is_zero_ether_addr(match.key-\u003esrc) \u0026\u0026\n+\t\t is_unicast_ether_addr(match.key-\u003esrc)) {\n+\t\t\tether_addr_copy(tuple-\u003esmac,\n+\t\t\t\t\tmatch.key-\u003esrc);\n+\t\t\tether_addr_copy(tuple-\u003em_smac,\n+\t\t\t\t\tmatch.mask-\u003esrc);\n+\t\t\t*features |= BIT_ULL(NPC_SMAC);\n+\t\t}\n+\t}\n+\n+\tif (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV4_ADDRS)) {\n+\t\tstruct flow_match_ipv4_addrs match;\n+\n+\t\tflow_rule_match_ipv4_addrs(rule, \u0026match);\n+\n+\t\tif (match.key-\u003edst) {\n+\t\t\ttuple-\u003eip4dst = match.key-\u003edst;\n+\t\t\ttuple-\u003em_ip4dst = match.mask-\u003edst;\n+\t\t\t*features |= BIT_ULL(NPC_DIP_IPV4);\n+\t\t}\n+\n+\t\tif (match.key-\u003esrc) {\n+\t\t\ttuple-\u003eip4src = match.key-\u003esrc;\n+\t\t\ttuple-\u003em_ip4src = match.mask-\u003esrc;\n+\t\t\t*features |= BIT_ULL(NPC_SIP_IPV4);\n+\t\t}\n+\t}\n+\n+\tif (!(*features \u0026 BIT_ULL(NPC_DMAC))) {\n+\t\tif (!tuple-\u003eip4src || !tuple-\u003eip4dst) {\n+\t\t\tnetdev_err(nic-\u003enetdev,\n+\t\t\t\t \"%s:%d Invalid src=%pI4 and dst=%pI4 addresses\\n\",\n+\t\t\t\t __func__, __LINE__, \u0026tuple-\u003eip4src, \u0026tuple-\u003eip4dst);\n+\t\t\treturn -EINVAL;\n+\t\t}\n+\n+\t\tif ((tuple-\u003eip4src \u0026 tuple-\u003em_ip4src) == (tuple-\u003eip4dst \u0026 tuple-\u003em_ip4dst)) {\n+\t\t\tnetdev_err(nic-\u003enetdev,\n+\t\t\t\t \"%s:%d Masked values are same; Invalid src=%pI4 and dst=%pI4 addresses\\n\",\n+\t\t\t\t __func__, __LINE__, \u0026tuple-\u003eip4src, \u0026tuple-\u003eip4dst);\n+\t\t\treturn -EINVAL;\n+\t\t}\n+\t}\n+\n+\tif (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS)) {\n+\t\tstruct flow_match_ports match;\n+\n+\t\tflow_rule_match_ports(rule, \u0026match);\n+\n+\t\tif (ip_proto == IPPROTO_UDP) {\n+\t\t\tif (match.key-\u003edst)\n+\t\t\t\t*features |= BIT_ULL(NPC_DPORT_UDP);\n+\n+\t\t\tif (match.key-\u003esrc)\n+\t\t\t\t*features |= BIT_ULL(NPC_SPORT_UDP);\n+\t\t} else if (ip_proto == IPPROTO_TCP) {\n+\t\t\tif (match.key-\u003edst)\n+\t\t\t\t*features |= BIT_ULL(NPC_DPORT_TCP);\n+\n+\t\t\tif (match.key-\u003esrc)\n+\t\t\t\t*features |= BIT_ULL(NPC_SPORT_TCP);\n+\t\t}\n+\n+\t\tif (match.mask-\u003esrc) {\n+\t\t\ttuple-\u003esport = match.key-\u003esrc;\n+\t\t\ttuple-\u003em_sport = match.mask-\u003esrc;\n+\t\t}\n+\n+\t\tif (match.mask-\u003edst) {\n+\t\t\ttuple-\u003edport = match.key-\u003edst;\n+\t\t\ttuple-\u003em_dport = match.mask-\u003edst;\n+\t\t}\n+\t}\n+\n+\tif (!(*features \u0026 (BIT_ULL(NPC_DMAC) |\n+\t\t\t BIT_ULL(NPC_SMAC) |\n+\t\t\t BIT_ULL(NPC_DIP_IPV4) |\n+\t\t\t BIT_ULL(NPC_SIP_IPV4) |\n+\t\t\t BIT_ULL(NPC_DIP_IPV6) |\n+\t\t\t BIT_ULL(NPC_SIP_IPV6) |\n+\t\t\t BIT_ULL(NPC_DPORT_UDP) |\n+\t\t\t BIT_ULL(NPC_SPORT_UDP) |\n+\t\t\t BIT_ULL(NPC_DPORT_TCP) |\n+\t\t\t BIT_ULL(NPC_SPORT_TCP)))) {\n+\t\treturn -EINVAL;\n+\t}\n+\n+\ttuple-\u003efeatures = *features;\n+\n+\treturn 0;\n+}\n+\n+static int sw_fl_add(struct otx2_nic *nic, struct flow_cls_offload *f)\n+{\n+\tstruct fl_tuple tuple = { 0 };\n+\tstruct flow_rule *rule;\n+\tu64 features = 0;\n+\tu64 op = 0;\n+\tint rc;\n+\n+\trule = flow_cls_offload_flow_rule(f);\n+\n+\trc = sw_fl_parse_actions(nic, \u0026rule-\u003eaction, f, \u0026tuple, \u0026op);\n+\tif (rc)\n+\t\treturn rc;\n+\n+\tif (op \u0026 BIT_ULL(FLOW_ACTION_CT))\n+\t\treturn 0;\n+\n+\trc = sw_fl_parse_flow(nic, f, \u0026tuple, \u0026features);\n+\tif (rc) {\n+\t\ttrace_sw_fl_dump(__func__, __LINE__, \u0026tuple);\n+\t\treturn -EFAULT;\n+\t}\n+\n+\tif (!netif_is_ovs_port(nic-\u003enetdev)) {\n+\t\trc = sw_fl_get_pcifunc(nic, tuple.ip4src, \u0026tuple.in_pf,\n+\t\t\t\t \u0026tuple, true);\n+\t\tif (rc) {\n+\t\t\ttrace_sw_fl_dump(__func__, __LINE__, \u0026tuple);\n+\t\t\treturn rc;\n+\t\t}\n+\n+\t\trc = sw_fl_get_pcifunc(nic, tuple.ip4dst,\n+\t\t\t\t \u0026tuple.xmit_pf, \u0026tuple, false);\n+\t\tif (rc) {\n+\t\t\ttrace_sw_fl_dump(__func__, __LINE__, \u0026tuple);\n+\t\t\treturn rc;\n+\t\t}\n+\t}\n+\n+\ttrace_sw_fl_dump(__func__, __LINE__, \u0026tuple);\n+\tsw_fl_add_to_list(nic, \u0026tuple, f-\u003ecookie, true);\n+\treturn 0;\n+}\n+\n+static int sw_fl_del(struct otx2_nic *nic, struct flow_cls_offload *f)\n+{\n+\tsw_fl_add_to_list(nic, NULL, f-\u003ecookie, false);\n+\treturn 0;\n+}\n+\n+static int sw_fl_stats(struct otx2_nic *nic, struct flow_cls_offload *f)\n+{\n+\tstruct fl_get_stats_req *req;\n+\tstruct fl_get_stats_rsp *rsp;\n+\tu64 pkts_diff;\n+\tint rc = 0;\n+\n+\tmutex_lock(\u0026nic-\u003embox.lock);\n+\n+\treq = otx2_mbox_alloc_msg_fl_get_stats(\u0026nic-\u003embox);\n+\tif (!req) {\n+\t\tnetdev_err(nic-\u003enetdev,\n+\t\t\t \"%s:%d Error happened while mcam alloc req\\n\",\n+\t\t\t __func__, __LINE__);\n+\t\trc = -ENOMEM;\n+\t\tgoto fail;\n+\t}\n+\treq-\u003ecookie = f-\u003ecookie;\n+\n+\trc = otx2_sync_mbox_msg(\u0026nic-\u003embox);\n+\tif (rc)\n+\t\tgoto fail;\n+\n+\trsp = (struct fl_get_stats_rsp *)otx2_mbox_get_rsp\n+\t\t(\u0026nic-\u003embox.mbox, 0, \u0026req-\u003ehdr);\n+\tpkts_diff = rsp-\u003epkts_diff;\n+\tmutex_unlock(\u0026nic-\u003embox.lock);\n+\n+\tif (pkts_diff) {\n+\t\tflow_stats_update(\u0026f-\u003estats, 0x0, pkts_diff,\n+\t\t\t\t 0x0, jiffies,\n+\t\t\t\t FLOW_ACTION_HW_STATS_IMMEDIATE);\n+\t}\n+\treturn 0;\n+fail:\n+\tmutex_unlock(\u0026nic-\u003embox.lock);\n+\treturn rc;\n+}\n+\n+static bool init_done;\n+\n+int sw_fl_setup_ft_block_ingress_cb(enum tc_setup_type type,\n+\t\t\t\t void *type_data, void *cb_priv)\n+{\n+\tstruct flow_cls_offload *cls = type_data;\n+\tstruct otx2_nic *nic = cb_priv;\n+\n+\tif (!init_done)\n+\t\treturn 0;\n+\n+\tswitch (cls-\u003ecommand) {\n+\tcase FLOW_CLS_REPLACE:\n+\t\treturn sw_fl_add(nic, cls);\n+\tcase FLOW_CLS_DESTROY:\n+\t\treturn sw_fl_del(nic, cls);\n+\tcase FLOW_CLS_STATS:\n+\t\treturn sw_fl_stats(nic, cls);\n+\tdefault:\n+\t\tbreak;\n+\t}\n+\n+\treturn -EOPNOTSUPP;\n+}\n+\n+int sw_fl_init(void)\n+{\n+\tINIT_WORK(\u0026sw_fl_work, sw_fl_wq_handler);\n+\tsw_fl_wq = alloc_workqueue(\"sw_fl_wq\", 0, 0);\n+\tif (!sw_fl_wq)\n+\t\treturn -ENOMEM;\n+\n+\tinit_done = true;\n+\treturn 0;\n+}\n+\n+void sw_fl_deinit(void)\n+{\n+\tstruct sw_fl_list_entry *entry;\n+\tLIST_HEAD(tlist);\n+\n+\tcancel_work_sync(\u0026sw_fl_work);\n+\tdestroy_workqueue(sw_fl_wq);\n+\n+\tspin_lock(\u0026sw_fl_lock);\n+\tlist_splice_init(\u0026sw_fl_lh, \u0026tlist);\n+\tspin_unlock(\u0026sw_fl_lock);\n+\n+\twhile ((entry =\n+\t\tlist_first_entry_or_null(\u0026tlist,\n+\t\t\t\t\t struct sw_fl_list_entry,\n+\t\t\t\t\t list)) != NULL) {\n+\t\tlist_del_init(\u0026entry-\u003elist);\n+\t\tdev_put(entry-\u003epf-\u003enetdev);\n+\t\tkfree(entry);\n+\t}\n+}\n+#endif\ndiff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.h b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.h\nnew file mode 100644\nindex 00000000000000..8dd816eb17d2c9\n--- /dev/null\n+++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.h\n@@ -0,0 +1,15 @@\n+/* SPDX-License-Identifier: GPL-2.0 */\n+/* Marvell switch driver\n+ *\n+ * Copyright (C) 2026 Marvell.\n+ *\n+ */\n+#ifndef SW_FL_H_\n+#define SW_FL_H_\n+\n+void sw_fl_deinit(void);\n+int sw_fl_init(void);\n+int sw_fl_setup_ft_block_ingress_cb(enum tc_setup_type type,\n+\t\t\t\t void *type_data, void *cb_priv);\n+\n+#endif // SW_FL_H\ndiff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.c b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.c\nnew file mode 100644\nindex 00000000000000..61f0ed26adfdef\n--- /dev/null\n+++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.c\n@@ -0,0 +1,435 @@\n+// SPDX-License-Identifier: GPL-2.0\n+/* Marvell RVU switch driver\n+ *\n+ * Copyright (C) 2026 Marvell.\n+ *\n+ */\n+#include \u003clinux/kernel.h\u003e\n+#include \u003clinux/netdevice.h\u003e\n+#include \u003clinux/etherdevice.h\u003e\n+#include \u003cnet/switchdev.h\u003e\n+#include \u003cnet/netevent.h\u003e\n+#include \u003cnet/arp.h\u003e\n+#include \u003cnet/route.h\u003e\n+#include \u003clinux/inetdevice.h\u003e\n+#include \u003cnet/addrconf.h\u003e\n+\n+#include \"../otx2_reg.h\"\n+#include \"../otx2_common.h\"\n+#include \"../otx2_struct.h\"\n+#include \"../cn10k.h\"\n+#include \"sw_nb.h\"\n+#include \"sw_fdb.h\"\n+#include \"sw_fib.h\"\n+#include \"sw_fl.h\"\n+#include \"sw_nb_v4.h\"\n+#include \"sw_nb_v6.h\"\n+\n+/* PF netdev for netdev_* logging when notifier info has no device */\n+static struct net_device *sw_nb_pf_netdev;\n+\n+static const char *sw_nb_cmd2str[OTX2_CMD_MAX] = {\n+\t[OTX2_DEV_UP] = \"OTX2_DEV_UP\",\n+\t[OTX2_DEV_DOWN] = \"OTX2_DEV_DOWN\",\n+\t[OTX2_DEV_CHANGE] = \"OTX2_DEV_CHANGE\",\n+\t[OTX2_NEIGH_UPDATE] = \"OTX2_NEIGH_UPDATE\",\n+\t[OTX2_FIB_ENTRY_REPLACE] = \"OTX2_FIB_ENTRY_REPLACE\",\n+\t[OTX2_FIB_ENTRY_ADD] = \"OTX2_FIB_ENTRY_ADD\",\n+\t[OTX2_FIB_ENTRY_DEL] = \"OTX2_FIB_ENTRY_DEL\",\n+\t[OTX2_FIB_ENTRY_APPEND] = \"OTX2_FIB_ENTRY_APPEND\",\n+};\n+\n+const char *sw_nb_get_cmd2str(int cmd)\n+{\n+\treturn sw_nb_cmd2str[cmd];\n+}\n+EXPORT_SYMBOL(sw_nb_get_cmd2str);\n+\n+bool sw_nb_is_cavium_dev(struct net_device *netdev)\n+{\n+\tstruct pci_dev *pdev;\n+\tstruct device *dev;\n+\n+\tdev = netdev-\u003edev.parent;\n+\tif (!dev || dev-\u003ebus != \u0026pci_bus_type)\n+\t\treturn false;\n+\n+\tpdev = to_pci_dev(dev);\n+\tif (pdev-\u003evendor != PCI_VENDOR_ID_CAVIUM)\n+\t\treturn false;\n+\n+\treturn true;\n+}\n+\n+static int sw_nb_check_slaves(struct net_device *dev,\n+\t\t\t struct netdev_nested_priv *priv)\n+{\n+\tint *cnt;\n+\n+\tif (!priv-\u003eflags)\n+\t\treturn 0;\n+\n+\tpriv-\u003eflags \u0026= sw_nb_is_cavium_dev(dev);\n+\tif (priv-\u003eflags) {\n+\t\tcnt = priv-\u003edata;\n+\t\t(*cnt)++;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+bool sw_nb_is_valid_dev(struct net_device *netdev)\n+{\n+\tstruct netdev_nested_priv priv;\n+\tstruct net_device *br;\n+\tint cnt = 0;\n+\n+\tpriv.flags = true;\n+\tpriv.data = \u0026cnt;\n+\n+\tif (netif_is_bridge_master(netdev) || is_vlan_dev(netdev)) {\n+\t\t/* sw_nb_is_valid_dev() is invoked in notifier callback context, so\n+\t\t * rtnl_lcok is already acquired\n+\t\t */\n+\t\tnetdev_walk_all_lower_dev(netdev, sw_nb_check_slaves, \u0026priv);\n+\t\treturn priv.flags \u0026\u0026 !!*(int *)priv.data;\n+\t}\n+\n+\tif (netif_is_bridge_port(netdev)) {\n+\t\tbr = netdev_master_upper_dev_get_rcu(netdev);\n+\t\tif (!br)\n+\t\t\treturn false;\n+\n+\t\tnetdev_walk_all_lower_dev(br, sw_nb_check_slaves, \u0026priv);\n+\t\treturn priv.flags \u0026\u0026 !!*(int *)priv.data;\n+\t}\n+\n+\treturn sw_nb_is_cavium_dev(netdev);\n+}\n+\n+static int sw_nb_fdb_event(struct notifier_block *unused,\n+\t\t\t unsigned long event, void *ptr)\n+{\n+\tstruct net_device *dev = switchdev_notifier_info_to_dev(ptr);\n+\tstruct switchdev_notifier_fdb_info *fdb_info = ptr;\n+\tint rc = 0;\n+\n+\tif (!sw_nb_is_valid_dev(dev))\n+\t\treturn NOTIFY_DONE;\n+\n+\tswitch (event) {\n+\tcase SWITCHDEV_FDB_ADD_TO_DEVICE:\n+\t\tif (fdb_info-\u003eis_local)\n+\t\t\tbreak;\n+\t\trc = sw_fdb_add_to_list(dev, (u8 *)fdb_info-\u003eaddr, true);\n+\t\tbreak;\n+\n+\tcase SWITCHDEV_FDB_DEL_TO_DEVICE:\n+\t\tif (fdb_info-\u003eis_local)\n+\t\t\tbreak;\n+\t\trc = sw_fdb_add_to_list(dev, (u8 *)fdb_info-\u003eaddr, false);\n+\t\tbreak;\n+\n+\tdefault:\n+\t\treturn NOTIFY_DONE;\n+\t}\n+\n+\tif (rc)\n+\t\tnetdev_err(dev, \"%s: Error to add to list\\n\", __func__);\n+\n+\treturn NOTIFY_DONE;\n+}\n+\n+static struct notifier_block sw_nb_fdb = {\n+\t.notifier_call = sw_nb_fdb_event,\n+};\n+\n+static void __maybe_unused\n+sw_nb_fib_event_dump(unsigned long event, void *ptr)\n+{\n+\tstruct fib_entry_notifier_info *fen_info = ptr;\n+\tstruct net_device *log_dev;\n+\tstruct fib_nh *fib_nh;\n+\tstruct fib_info *fi;\n+\tint i;\n+\n+\tfi = fen_info-\u003efi;\n+\tlog_dev = (fi \u0026\u0026 fi-\u003efib_nhs) ? fi-\u003efib_nh-\u003efib_nh_dev : sw_nb_pf_netdev;\n+\tif (log_dev)\n+\t\tnetdev_info(log_dev, \"%s: FIB event=%lu dst=%#x dstlen=%u type=%u\\n\",\n+\t\t\t __func__, event, fen_info-\u003edst, fen_info-\u003edst_len,\n+\t\t\t fen_info-\u003etype);\n+\n+\tif (!fi)\n+\t\treturn;\n+\n+\tfib_nh = fi-\u003efib_nh;\n+\tfor (i = 0; i \u003c fi-\u003efib_nhs; i++, fib_nh++) {\n+\t\tif (!fib_nh-\u003efib_nh_dev)\n+\t\t\tcontinue;\n+\t\tnetdev_info(fib_nh-\u003efib_nh_dev,\n+\t\t\t \"%s: dev=%s saddr=%#x gw=%#x\\n\",\n+\t\t\t __func__, fib_nh-\u003efib_nh_dev-\u003ename,\n+\t\t\t fib_nh-\u003enh_saddr, fib_nh-\u003efib_nh_gw4);\n+\t}\n+}\n+\n+#define SWITCH_NB_FIB_EVENT_DUMP(...) \\\n+\tsw_nb_fib_event_dump(__VA_ARGS__)\n+\n+int sw_nb_fib_event_to_otx2_event(int event, struct net_device *netdev)\n+{\n+\tswitch (event) {\n+\tcase FIB_EVENT_ENTRY_REPLACE:\n+\t\treturn OTX2_FIB_ENTRY_REPLACE;\n+\tcase FIB_EVENT_ENTRY_ADD:\n+\t\treturn OTX2_FIB_ENTRY_ADD;\n+\tcase FIB_EVENT_ENTRY_DEL:\n+\t\treturn OTX2_FIB_ENTRY_DEL;\n+\tdefault:\n+\t\tbreak;\n+\t}\n+\n+\tnetdev_err(netdev, \"Wrong FIB event %d\\n\", event);\n+\treturn -1;\n+}\n+\n+static int sw_nb_fib_event(struct notifier_block *nb,\n+\t\t\t unsigned long event, void *ptr)\n+{\n+\tstruct fib_notifier_info *info = ptr;\n+\n+\tswitch (event) {\n+\tcase FIB_EVENT_ENTRY_REPLACE:\n+\tcase FIB_EVENT_ENTRY_ADD:\n+\tcase FIB_EVENT_ENTRY_DEL:\n+\t\tbreak;\n+\tdefault:\n+\t\tif (sw_nb_pf_netdev)\n+\t\t\tnetdev_dbg(sw_nb_pf_netdev,\n+\t\t\t\t \"%s: Won't process FIB event %lu\\n\",\n+\t\t\t\t __func__, event);\n+\t\treturn NOTIFY_DONE;\n+\t}\n+\n+\tswitch (info-\u003efamily) {\n+\tcase AF_INET:\n+\t\treturn sw_nb_v4_fib_event(nb, event, ptr);\n+#if IS_ENABLED(CONFIG_IPV6)\n+\tcase AF_INET6:\n+\t\treturn sw_nb_v6_fib_event(nb, event, ptr);\n+#endif\n+\tdefault:\n+\t\tbreak;\n+\t}\n+\treturn NOTIFY_DONE;\n+}\n+\n+static struct notifier_block sw_nb_fib = {\n+\t.notifier_call = sw_nb_fib_event,\n+};\n+\n+static int sw_nb_net_event(struct notifier_block *nb,\n+\t\t\t unsigned long event, void *ptr)\n+{\n+\tstruct neighbour *n = ptr;\n+\n+\tif (!sw_nb_is_valid_dev(n-\u003edev))\n+\t\treturn NOTIFY_DONE;\n+\n+\tif (event != NETEVENT_NEIGH_UPDATE)\n+\t\treturn NOTIFY_DONE;\n+\n+\tswitch (n-\u003etbl-\u003efamily) {\n+\tcase AF_INET:\n+\t\treturn sw_nb_net_v4_neigh_update(nb, event, ptr);\n+#if IS_ENABLED(CONFIG_IPV6)\n+\tcase AF_INET6:\n+\t\treturn sw_nb_net_v6_neigh_update(nb, event, ptr);\n+#endif\n+\tdefault:\n+\t\tbreak;\n+\t}\n+\treturn NOTIFY_DONE;\n+}\n+\n+static struct notifier_block sw_nb_netevent = {\n+\t.notifier_call = sw_nb_net_event,\n+\n+};\n+\n+int sw_nb_inetaddr_event_to_otx2_event(int event, struct net_device *netdev)\n+{\n+\tswitch (event) {\n+\tcase NETDEV_CHANGE:\n+\t\treturn OTX2_DEV_CHANGE;\n+\tcase NETDEV_UP:\n+\t\treturn OTX2_DEV_UP;\n+\tcase NETDEV_DOWN:\n+\t\treturn OTX2_DEV_DOWN;\n+\tdefault:\n+\t\tbreak;\n+\t}\n+\tnetdev_dbg(netdev, \"%s: Wrong interaddr event %d\\n\",\n+\t\t __func__, event);\n+\treturn -1;\n+}\n+\n+static struct notifier_block sw_nb_v4_inetaddr = {\n+\t.notifier_call = sw_nb_v4_inetaddr_event,\n+};\n+\n+#if IS_ENABLED(CONFIG_IPV6)\n+static struct notifier_block sw_nb_v6_inetaddr = {\n+\t.notifier_call = sw_nb_v6_inetaddr_event,\n+};\n+#endif\n+\n+static int sw_nb_netdev_event(struct notifier_block *unused,\n+\t\t\t unsigned long event, void *ptr)\n+{\n+\tstruct net_device *dev = netdev_notifier_info_to_dev(ptr);\n+\tstruct in_device *idev;\n+\tstruct inet6_dev *i6dev;\n+\n+\tif (event != NETDEV_CHANGE \u0026\u0026\n+\t event != NETDEV_UP \u0026\u0026\n+\t event != NETDEV_DOWN) {\n+\t\treturn NOTIFY_DONE;\n+\t}\n+\n+\tif (!sw_nb_is_valid_dev(dev))\n+\t\treturn NOTIFY_DONE;\n+\n+\tidev = __in_dev_get_rtnl(dev);\n+\tif (idev)\n+\t\tsw_nb_v4_netdev_event(unused, event, ptr);\n+\n+\ti6dev = __in6_dev_get(dev);\n+\tif (i6dev)\n+\t\tsw_nb_v6_netdev_event(unused, event, ptr);\n+\n+\treturn NOTIFY_DONE;\n+}\n+\n+static struct notifier_block sw_nb_netdev = {\n+\t.notifier_call = sw_nb_netdev_event,\n+};\n+\n+int sw_nb_unregister(struct net_device *netdev)\n+{\n+\tint err;\n+\n+\terr = unregister_switchdev_notifier(\u0026sw_nb_fdb);\n+\n+\tif (err)\n+\t\tnetdev_err(netdev, \"Failed to unregister switchdev nb\\n\");\n+\n+\terr = unregister_fib_notifier(\u0026init_net, \u0026sw_nb_fib);\n+\tif (err)\n+\t\tnetdev_err(netdev, \"Failed to unregister fib nb\\n\");\n+\n+\terr = unregister_netevent_notifier(\u0026sw_nb_netevent);\n+\tif (err)\n+\t\tnetdev_err(netdev, \"Failed to unregister netevent\\n\");\n+\n+\terr = unregister_inetaddr_notifier(\u0026sw_nb_v4_inetaddr);\n+\tif (err)\n+\t\tnetdev_err(netdev, \"Failed to unregister addr event\\n\");\n+\n+#if IS_ENABLED(CONFIG_IPV6)\n+\terr = unregister_inet6addr_notifier(\u0026sw_nb_v6_inetaddr);\n+\tif (err)\n+\t\tnetdev_err(netdev, \"Failed to unregister addr event\\n\");\n+#endif\n+\n+\terr = unregister_netdevice_notifier(\u0026sw_nb_netdev);\n+\tif (err)\n+\t\tnetdev_err(netdev, \"Failed to unregister netdev notifier\\n\");\n+\n+\tsw_fl_deinit();\n+\tsw_fib_deinit();\n+\tsw_fdb_deinit();\n+\n+\tsw_nb_pf_netdev = NULL;\n+\n+\treturn 0;\n+}\n+EXPORT_SYMBOL(sw_nb_unregister);\n+\n+int sw_nb_register(struct net_device *netdev)\n+{\n+\tint err;\n+\n+\tsw_nb_pf_netdev = netdev;\n+\n+\tsw_fdb_init();\n+\tsw_fib_init();\n+\tsw_fl_init();\n+\n+\terr = register_switchdev_notifier(\u0026sw_nb_fdb);\n+\tif (err) {\n+\t\tnetdev_err(netdev, \"Failed to register switchdev nb\\n\");\n+\t\tsw_nb_pf_netdev = NULL;\n+\t\treturn err;\n+\t}\n+\n+\terr = register_fib_notifier(\u0026init_net, \u0026sw_nb_fib, NULL, NULL);\n+\tif (err) {\n+\t\tnetdev_err(netdev, \"Failed to register fb notifier block\\n\");\n+\t\tgoto err1;\n+\t}\n+\n+\terr = register_netevent_notifier(\u0026sw_nb_netevent);\n+\tif (err) {\n+\t\tnetdev_err(netdev, \"Failed to register netevent\\n\");\n+\t\tgoto err2;\n+\t}\n+\n+#if IS_ENABLED(CONFIG_IPV6)\n+\terr = register_inet6addr_notifier(\u0026sw_nb_v6_inetaddr);\n+\tif (err) {\n+\t\tnetdev_err(netdev, \"Failed to register addr event\\n\");\n+\t\tgoto err3;\n+\t}\n+#endif\n+\n+\terr = register_inetaddr_notifier(\u0026sw_nb_v4_inetaddr);\n+\tif (err) {\n+\t\tnetdev_err(netdev, \"Failed to register addr event\\n\");\n+\t\tgoto err4;\n+\t}\n+\n+\terr = register_netdevice_notifier(\u0026sw_nb_netdev);\n+\tif (err) {\n+\t\tnetdev_err(netdev, \"Failed to register netdevice nb\\n\");\n+\t\tgoto err5;\n+\t}\n+\n+\treturn 0;\n+\n+err5:\n+\tunregister_inetaddr_notifier(\u0026sw_nb_v4_inetaddr);\n+\n+err4:\n+#if IS_ENABLED(CONFIG_IPV6)\n+\tunregister_inet6addr_notifier(\u0026sw_nb_v6_inetaddr);\n+\n+err3:\n+#endif\n+\tunregister_netevent_notifier(\u0026sw_nb_netevent);\n+\n+err2:\n+\tunregister_fib_notifier(\u0026init_net, \u0026sw_nb_fib);\n+\n+err1:\n+\tunregister_switchdev_notifier(\u0026sw_nb_fdb);\n+\n+\tsw_fl_deinit();\n+\tsw_fib_deinit();\n+\tsw_fdb_deinit();\n+\n+\tsw_nb_pf_netdev = NULL;\n+\treturn err;\n+}\n+EXPORT_SYMBOL(sw_nb_register);\ndiff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.h b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.h\nnew file mode 100644\nindex 00000000000000..b0ce10ed25d4a8\n--- /dev/null\n+++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.h\n@@ -0,0 +1,35 @@\n+/* SPDX-License-Identifier: GPL-2.0 */\n+/* Marvell switch driver\n+ *\n+ * Copyright (C) 2026 Marvell.\n+ *\n+ */\n+#ifndef SW_NB_H_\n+#define SW_NB_H_\n+\n+enum {\n+\tOTX2_DEV_UP = 1,\n+\tOTX2_DEV_DOWN,\n+\tOTX2_DEV_CHANGE,\n+\tOTX2_NEIGH_UPDATE,\n+\tOTX2_FIB_ENTRY_REPLACE,\n+\tOTX2_FIB_ENTRY_ADD,\n+\tOTX2_FIB_ENTRY_DEL,\n+\tOTX2_FIB_ENTRY_APPEND,\n+\tOTX2_CMD_MAX,\n+};\n+\n+int sw_nb_register(struct net_device *netdev);\n+int sw_nb_unregister(struct net_device *netdev);\n+bool sw_nb_is_valid_dev(struct net_device *netdev);\n+\n+int otx2_mbox_up_handler_af2pf_fdb_refresh(struct otx2_nic *pf,\n+\t\t\t\t\t struct af2pf_fdb_refresh_req *req,\n+\t\t\t\t\t struct msg_rsp *rsp);\n+\n+bool sw_nb_is_cavium_dev(struct net_device *netdev);\n+int sw_nb_fib_event_to_otx2_event(int event, struct net_device *netdev);\n+int sw_nb_inetaddr_event_to_otx2_event(int event, struct net_device *netdev);\n+\n+const char *sw_nb_get_cmd2str(int cmd);\n+#endif // SW_NB_H__\ndiff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb_v4.c b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb_v4.c\nnew file mode 100644\nindex 00000000000000..8fd02edaa90ee7\n--- /dev/null\n+++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb_v4.c\n@@ -0,0 +1,341 @@\n+// SPDX-License-Identifier: GPL-2.0\n+/* Marvell RVU switch driver\n+ *\n+ * Copyright (C) 2026 Marvell.\n+ *\n+ */\n+#include \u003clinux/kernel.h\u003e\n+#include \u003clinux/netdevice.h\u003e\n+#include \u003clinux/etherdevice.h\u003e\n+#include \u003cnet/switchdev.h\u003e\n+#include \u003cnet/netevent.h\u003e\n+#include \u003cnet/arp.h\u003e\n+#include \u003cnet/route.h\u003e\n+#include \u003clinux/inetdevice.h\u003e\n+\n+#include \"../otx2_reg.h\"\n+#include \"../otx2_common.h\"\n+#include \"../otx2_struct.h\"\n+#include \"../cn10k.h\"\n+#include \"sw_nb.h\"\n+#include \"sw_fdb.h\"\n+#include \"sw_fib.h\"\n+#include \"sw_fl.h\"\n+#include \"sw_nb.h\"\n+#include \"sw_nb_v4.h\"\n+\n+int sw_nb_v4_netdev_event(struct notifier_block *unused,\n+\t\t\t unsigned long event, void *ptr)\n+{\n+\tstruct net_device *dev = netdev_notifier_info_to_dev(ptr);\n+\tstruct netdev_hw_addr *dev_addr;\n+\tstruct net_device *pf_dev;\n+\tstruct in_ifaddr *ifa;\n+\tstruct fib_entry *entry;\n+\tstruct in_device *idev;\n+\tstruct otx2_nic *pf;\n+\tstruct list_head *iter;\n+\tstruct net_device *lower;\n+\n+\tidev = __in_dev_get_rtnl(dev);\n+\tif (!idev || !idev-\u003eifa_list)\n+\t\treturn NOTIFY_DONE;\n+\n+\tifa = rtnl_dereference(idev-\u003eifa_list);\n+\n+\tentry = kcalloc(1, sizeof(*entry), GFP_KERNEL);\n+\tif (!entry)\n+\t\treturn NOTIFY_DONE;\n+\n+\tentry-\u003ecmd = sw_nb_inetaddr_event_to_otx2_event(event, dev);\n+\tentry-\u003edst = (__force u32)htonl((__force u32)ifa-\u003eifa_address);\n+\tentry-\u003edst_len = 32;\n+\tentry-\u003emac_valid = 1;\n+\tentry-\u003ehost = 1;\n+\n+\tpf_dev = dev;\n+\tif (netif_is_bridge_master(dev)) {\n+\t\tentry-\u003ebridge = 1;\n+\t\tnetdev_for_each_lower_dev(dev, lower, iter) {\n+\t\t\tpf_dev = lower;\n+\t\t\tbreak;\n+\t\t}\n+\t} else if (is_vlan_dev(dev)) {\n+\t\tentry-\u003evlan_valid = 1;\n+\t\tpf_dev = vlan_dev_real_dev(dev);\n+\t\tentry-\u003evlan_tag = vlan_dev_vlan_id(dev);\n+\t}\n+\n+\tpf = netdev_priv(pf_dev);\n+\tentry-\u003eport_id = pf-\u003epcifunc;\n+\n+\tfor_each_dev_addr(dev, dev_addr) {\n+\t\tether_addr_copy(entry-\u003emac, dev_addr-\u003eaddr);\n+\t\tbreak;\n+\t}\n+\n+\tnetdev_dbg(dev, \"%s: pushing netdev event from HOST interface address %#x, %pM, dev=%s\\n\",\n+\t\t __func__, entry-\u003edst, entry-\u003emac, dev-\u003ename);\n+\tsw_fib_add_to_list(pf_dev, entry, 1);\n+\n+\treturn NOTIFY_DONE;\n+}\n+\n+int sw_nb_v4_inetaddr_event(struct notifier_block *nb,\n+\t\t\t unsigned long event, void *ptr)\n+{\n+\tstruct in_ifaddr *ifa = (struct in_ifaddr *)ptr;\n+\tstruct net_device *dev = ifa-\u003eifa_dev-\u003edev;\n+\tstruct net_device *lower, *pf_dev;\n+\tstruct netdev_hw_addr *dev_addr;\n+\tstruct fib_entry *entry;\n+\tstruct in_device *idev;\n+\tstruct list_head *iter;\n+\tstruct otx2_nic *pf;\n+\n+\tif (event != NETDEV_CHANGE \u0026\u0026\n+\t event != NETDEV_UP \u0026\u0026\n+\t event != NETDEV_DOWN) {\n+\t\treturn NOTIFY_DONE;\n+\t}\n+\n+\tidev = __in_dev_get_rtnl(dev);\n+\tif (!idev || !idev-\u003eifa_list)\n+\t\treturn NOTIFY_DONE;\n+\n+\tentry = kcalloc(1, sizeof(*entry), GFP_ATOMIC);\n+\tentry-\u003ecmd = sw_nb_inetaddr_event_to_otx2_event(event, dev);\n+\tentry-\u003edst = (__force u32)htonl((__force u32)ifa-\u003eifa_address);\n+\tentry-\u003edst_len = 32;\n+\tentry-\u003emac_valid = 1;\n+\tentry-\u003ehost = 1;\n+\n+\tpf_dev = dev;\n+\tif (netif_is_bridge_master(dev)) {\n+\t\tentry-\u003ebridge = 1;\n+\t\tnetdev_for_each_lower_dev(dev, lower, iter) {\n+\t\t\tpf_dev = lower;\n+\t\t\tbreak;\n+\t\t}\n+\t} else if (is_vlan_dev(dev)) {\n+\t\tentry-\u003evlan_valid = 1;\n+\t\tpf_dev = vlan_dev_real_dev(dev);\n+\t\tentry-\u003evlan_tag = vlan_dev_vlan_id(dev);\n+\t}\n+\n+\tpf = netdev_priv(pf_dev);\n+\tentry-\u003eport_id = pf-\u003epcifunc;\n+\n+\tfor_each_dev_addr(dev, dev_addr) {\n+\t\tether_addr_copy(entry-\u003emac, dev_addr-\u003eaddr);\n+\t\tbreak;\n+\t}\n+\n+\tnetdev_dbg(dev, \"%s: pushing inetaddr event from HOST interface address %#x, %pM, %s\\n\",\n+\t\t __func__, entry-\u003edst, entry-\u003emac, dev-\u003ename);\n+\n+\tsw_fib_add_to_list(pf_dev, entry, 1);\n+\treturn NOTIFY_DONE;\n+}\n+\n+int sw_nb_v4_fib_event(struct notifier_block *nb,\n+\t\t unsigned long event, void *ptr)\n+{\n+\tstruct fib_entry_notifier_info *fen_info = ptr;\n+\tstruct fib_entry *entries, *iter;\n+\tstruct net_device *dev, *pf_dev = NULL;\n+\tstruct netdev_hw_addr *dev_addr;\n+\tstruct net_device *lower;\n+\tstruct list_head *lh;\n+\tstruct neighbour *neigh;\n+\tstruct fib_nh *fib_nh;\n+\tstruct fib_info *fi;\n+\tstruct otx2_nic *pf;\n+\tu32 *haddr;\n+\tint hcnt = 0;\n+\tint cnt, i;\n+\n+\t/* Process only UNICAST routes add or del */\n+\tif (fen_info-\u003etype != RTN_UNICAST)\n+\t\treturn NOTIFY_DONE;\n+\n+\tfi = fen_info-\u003efi;\n+\tif (!fi)\n+\t\treturn NOTIFY_DONE;\n+\n+\tif (fi-\u003efib_nh_is_v6) {\n+\t\tstruct net_device *log_dev = (fi-\u003efib_nhs \u003e 0) ?\n+\t\t\tfi-\u003efib_nh-\u003efib_nh_dev : NULL;\n+\n+\t\tif (log_dev)\n+\t\t\tnetdev_dbg(log_dev, \"%s: Received v6 notification\\n\",\n+\t\t\t\t __func__);\n+\t\treturn NOTIFY_DONE;\n+\t}\n+\n+\tentries = kcalloc(fi-\u003efib_nhs, sizeof(*entries), GFP_ATOMIC);\n+\tif (!entries)\n+\t\treturn NOTIFY_DONE;\n+\n+\thaddr = kcalloc(fi-\u003efib_nhs, sizeof(u32), GFP_ATOMIC);\n+\tif (!haddr) {\n+\t\tkfree(entries);\n+\t\treturn NOTIFY_DONE;\n+\t}\n+\n+\titer = entries;\n+\tfib_nh = fi-\u003efib_nh;\n+\tfor (i = 0; i \u003c fi-\u003efib_nhs; i++, fib_nh++) {\n+\t\tdev = fib_nh-\u003efib_nh_dev;\n+\n+\t\tif (!dev)\n+\t\t\tcontinue;\n+\n+\t\tif (dev-\u003etype != ARPHRD_ETHER)\n+\t\t\tcontinue;\n+\n+\t\tif (!sw_nb_is_valid_dev(dev))\n+\t\t\tcontinue;\n+\n+\t\titer-\u003ecmd = sw_nb_fib_event_to_otx2_event(event, dev);\n+\t\titer-\u003edst = fen_info-\u003edst;\n+\t\titer-\u003edst_len = fen_info-\u003edst_len;\n+\t\titer-\u003egw = (__force u32)htonl((__force u32)fib_nh-\u003efib_nh_gw4);\n+\n+\t\tnetdev_dbg(dev, \"%s: FIB route Rule cmd=%lld dst=%#x dst_len=%d gw=%#x\\n\",\n+\t\t\t __func__, iter-\u003ecmd, iter-\u003edst, iter-\u003edst_len, iter-\u003egw);\n+\n+\t\tpf_dev = dev;\n+\t\tif (netif_is_bridge_master(dev)) {\n+\t\t\titer-\u003ebridge = 1;\n+\t\t\tnetdev_for_each_lower_dev(dev, lower, lh) {\n+\t\t\t\tpf_dev = lower;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t} else if (is_vlan_dev(dev)) {\n+\t\t\titer-\u003evlan_valid = 1;\n+\t\t\tpf_dev = vlan_dev_real_dev(dev);\n+\t\t\titer-\u003evlan_tag = vlan_dev_vlan_id(dev);\n+\t\t}\n+\n+\t\tpf = netdev_priv(pf_dev);\n+\t\titer-\u003eport_id = pf-\u003epcifunc;\n+\n+\t\tif (!fib_nh-\u003efib_nh_gw4) {\n+\t\t\tif (iter-\u003edst || iter-\u003edst_len)\n+\t\t\t\titer++;\n+\n+\t\t\tcontinue;\n+\t\t}\n+\t\titer-\u003egw_valid = 1;\n+\n+\t\tif (fib_nh-\u003enh_saddr)\n+\t\t\thaddr[hcnt++] = (__force u32)fib_nh-\u003enh_saddr;\n+\n+\t\trcu_read_lock();\n+\t\tneigh = ip_neigh_gw4(fib_nh-\u003efib_nh_dev, fib_nh-\u003efib_nh_gw4);\n+\t\tif (!neigh) {\n+\t\t\trcu_read_unlock();\n+\t\t\titer++;\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tif (is_valid_ether_addr(neigh-\u003eha)) {\n+\t\t\titer-\u003emac_valid = 1;\n+\t\t\tether_addr_copy(iter-\u003emac, neigh-\u003eha);\n+\t\t}\n+\n+\t\titer++;\n+\t\trcu_read_unlock();\n+\t}\n+\n+\tcnt = iter - entries;\n+\tif (!cnt) {\n+\t\tkfree(entries);\n+\t\tkfree(haddr);\n+\t\treturn NOTIFY_DONE;\n+\t}\n+\n+\tnetdev_dbg(pf_dev, \"pf_dev is %s cnt=%d\\n\", pf_dev-\u003ename, cnt);\n+\n+\tsw_fib_add_to_list(pf_dev, entries, cnt);\n+\n+\tif (!hcnt) {\n+\t\tkfree(haddr);\n+\t\treturn NOTIFY_DONE;\n+\t}\n+\n+\tentries = kcalloc(hcnt, sizeof(*entries), GFP_ATOMIC);\n+\tif (!entries) {\n+\t\tkfree(haddr);\n+\t\treturn NOTIFY_DONE;\n+\t}\n+\n+\titer = entries;\n+\n+\tfor (i = 0; i \u003c hcnt; i++, iter++) {\n+\t\titer-\u003ecmd = sw_nb_fib_event_to_otx2_event(event, pf_dev);\n+\t\titer-\u003edst = (__force u32)htonl(haddr[i]);\n+\t\titer-\u003edst_len = 32;\n+\t\titer-\u003emac_valid = 1;\n+\t\titer-\u003ehost = 1;\n+\t\titer-\u003eport_id = pf-\u003epcifunc;\n+\n+\t\tfor_each_dev_addr(pf_dev, dev_addr) {\n+\t\t\tether_addr_copy(iter-\u003emac, dev_addr-\u003eaddr);\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\tnetdev_dbg(pf_dev, \"%s: FIB host Rule cmd=%lld dst=%#x dst_len=%d gw=%#x %s\\n\",\n+\t\t\t __func__, iter-\u003ecmd, iter-\u003edst, iter-\u003edst_len, iter-\u003egw, pf_dev-\u003ename);\n+\t}\n+\tsw_fib_add_to_list(pf_dev, entries, hcnt);\n+\tkfree(haddr);\n+\treturn NOTIFY_DONE;\n+}\n+\n+int sw_nb_net_v4_neigh_update(struct notifier_block *nb,\n+\t\t\t unsigned long event, void *ptr)\n+{\n+\tstruct net_device *lower, *pf_dev;\n+\tstruct neighbour *n = ptr;\n+\tstruct fib_entry *entry;\n+\tstruct list_head *iter;\n+\tstruct otx2_nic *pf;\n+\n+\tif (n-\u003etbl != \u0026arp_tbl)\n+\t\treturn NOTIFY_DONE;\n+\n+\tentry = kcalloc(1, sizeof(*entry), GFP_ATOMIC);\n+\tentry-\u003ecmd = OTX2_NEIGH_UPDATE;\n+\tentry-\u003edst = (__force u32)htonl(*(u32 *)n-\u003eprimary_key);\n+\tentry-\u003edst_len = n-\u003etbl-\u003ekey_len * 8;\n+\tentry-\u003emac_valid = 1;\n+\tentry-\u003enud_state = n-\u003enud_state;\n+\tneigh_ha_snapshot(entry-\u003emac, n, n-\u003edev);\n+\tether_addr_copy(entry-\u003emac, n-\u003eha);\n+\n+\tpf_dev = n-\u003edev;\n+\n+\trcu_read_lock();\n+\tif (netif_is_bridge_master(n-\u003edev)) {\n+\t\tentry-\u003ebridge = 1;\n+\t\t/* Get first lower device thru which we can send MBOX to AF */\n+\t\tnetdev_for_each_lower_dev(n-\u003edev, lower, iter) {\n+\t\t\tpf_dev = lower;\n+\t\t\tbreak;\n+\t\t}\n+\t} else if (is_vlan_dev(n-\u003edev)) {\n+\t\tentry-\u003evlan_valid = 1;\n+\t\tpf_dev = vlan_dev_real_dev(n-\u003edev);\n+\t\tentry-\u003evlan_tag = vlan_dev_vlan_id(n-\u003edev);\n+\t}\n+\n+\tpf = netdev_priv(pf_dev);\n+\tentry-\u003eport_id = pf-\u003epcifunc;\n+\n+\tsw_fib_add_to_list(pf_dev, entry, 1);\n+\trcu_read_unlock();\n+\n+\treturn NOTIFY_DONE;\n+}\ndiff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb_v4.h b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb_v4.h\nnew file mode 100644\nindex 00000000000000..c6dbf4b93a9a2e\n--- /dev/null\n+++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb_v4.h\n@@ -0,0 +1,21 @@\n+/* SPDX-License-Identifier: GPL-2.0 */\n+/* Marvell switch driver\n+ *\n+ * Copyright (C) 2026 Marvell.\n+ *\n+ */\n+#ifndef SW_NB_V4_H_\n+#define SW_NB_V4_H_\n+\n+int sw_nb_v4_fib_event(struct notifier_block *nb,\n+\t\t unsigned long event, void *ptr);\n+\n+int sw_nb_net_v4_neigh_update(struct notifier_block *nb,\n+\t\t\t unsigned long event, void *ptr);\n+\n+int sw_nb_v4_inetaddr_event(struct notifier_block *nb,\n+\t\t\t unsigned long event, void *ptr);\n+\n+int sw_nb_v4_netdev_event(struct notifier_block *unused,\n+\t\t\t unsigned long event, void *ptr);\n+#endif // SW_NB_V4_H__\ndiff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb_v6.c b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb_v6.c\nnew file mode 100644\nindex 00000000000000..49fd6bdf3df4bd\n--- /dev/null\n+++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb_v6.c\n@@ -0,0 +1,248 @@\n+// SPDX-License-Identifier: GPL-2.0\n+/* Marvell RVU switch driver\n+ *\n+ * Copyright (C) 2026 Marvell.\n+ *\n+ */\n+#include \u003clinux/kernel.h\u003e\n+#include \u003clinux/netdevice.h\u003e\n+#include \u003clinux/etherdevice.h\u003e\n+#include \u003cnet/switchdev.h\u003e\n+#include \u003cnet/netevent.h\u003e\n+#include \u003cnet/arp.h\u003e\n+#include \u003cnet/route.h\u003e\n+#include \u003clinux/inetdevice.h\u003e\n+#include \u003cnet/addrconf.h\u003e\n+#include \u003cnet/ip6_fib.h\u003e\n+#include \u003cnet/nexthop.h\u003e\n+\n+#include \"../otx2_reg.h\"\n+#include \"../otx2_common.h\"\n+#include \"../otx2_struct.h\"\n+#include \"../cn10k.h\"\n+#include \"sw_nb.h\"\n+#include \"sw_fdb.h\"\n+#include \"sw_fib.h\"\n+#include \"sw_fl.h\"\n+#include \"sw_nb.h\"\n+#include \"sw_nb_v6.h\"\n+\n+#if IS_ENABLED(CONFIG_IPV6)\n+\n+int sw_nb_v6_netdev_event(struct notifier_block *unused,\n+\t\t\t unsigned long event, void *ptr)\n+{\n+\tstruct net_device *dev = netdev_notifier_info_to_dev(ptr);\n+\tstruct netdev_hw_addr *dev_addr;\n+\tstruct inet6_ifaddr *ifp;\n+\tstruct fib_entry *entry;\n+\tstruct inet6_dev *i6dev;\n+\tstruct otx2_nic *pf;\n+\n+\ti6dev = __in6_dev_get(dev);\n+\n+\trcu_read_lock();\n+\tifp = list_first_entry_or_null(\u0026i6dev-\u003eaddr_list,\n+\t\t\t\t struct inet6_ifaddr, if_list);\n+\tif (!ifp) {\n+\t\trcu_read_unlock();\n+\t\treturn NOTIFY_DONE;\n+\t}\n+\n+\tif (ipv6_addr_type(\u0026ifp-\u003eaddr) \u0026 IPV6_ADDR_LINKLOCAL) {\n+\t\trcu_read_unlock();\n+\t\treturn NOTIFY_DONE;\n+\t}\n+\n+\tpf = netdev_priv(dev);\n+\n+\tentry = kcalloc(1, sizeof(*entry), GFP_KERNEL);\n+\tentry-\u003ecmd = sw_nb_inetaddr_event_to_otx2_event(event, dev);\n+\tmemcpy(entry-\u003edst6, \u0026ifp-\u003eaddr, sizeof(entry-\u003edst6));\n+\tentry-\u003edst6_plen = ifp-\u003eprefix_len;\n+\tentry-\u003ehost = 1;\n+\tentry-\u003eipv6 = 1;\n+\tentry-\u003eport_id = pf-\u003epcifunc;\n+\n+\tfor_each_dev_addr(dev, dev_addr) {\n+\t\tentry-\u003emac_valid = 1;\n+\t\tether_addr_copy(entry-\u003emac, dev_addr-\u003eaddr);\n+\t\tbreak;\n+\t}\n+\n+\tnetdev_dbg(dev, \"netdev event %pM plen=%u mac=%pM\\n\",\n+\t\t \u0026ifp-\u003eaddr, ifp-\u003eprefix_len, entry-\u003emac);\n+\tsw_fib_add_to_list(dev, entry, 1);\n+\trcu_read_unlock();\n+\treturn NOTIFY_DONE;\n+}\n+\n+int sw_nb_v6_fib_event(struct notifier_block *nb,\n+\t\t unsigned long event, void *ptr)\n+{\n+\tstruct fib6_entry_notifier_info *f6_eni;\n+\tstruct fib_notifier_info *info = ptr;\n+\tstruct net_device *fib_dev;\n+\tstruct fib_entry *entry;\n+\tstruct fib6_info *f6i;\n+\tstruct neighbour *neigh;\n+\tstruct fib6_nh *nh6;\n+\tstruct otx2_nic *pf;\n+\tstruct rt6key *key;\n+\n+\tf6_eni = container_of(info, struct fib6_entry_notifier_info, info);\n+\tf6i = f6_eni-\u003ert;\n+\n+\tfib_dev = fib6_info_nh_dev(f6i);\n+\n+\tif (!fib_dev)\n+\t\treturn NOTIFY_DONE;\n+\n+\tif (fib_dev-\u003etype != ARPHRD_ETHER)\n+\t\treturn NOTIFY_DONE;\n+\n+\tif (!sw_nb_is_cavium_dev(fib_dev))\n+\t\treturn NOTIFY_DONE;\n+\n+\tif (f6i-\u003efib6_type != RTN_UNICAST)\n+\t\treturn NOTIFY_DONE;\n+\n+\tkey = \u0026f6i-\u003efib6_dst;\n+\t/* TODO: vlan and bridge support */\n+\tif (ipv6_addr_type(\u0026key-\u003eaddr) \u0026 IPV6_ADDR_LINKLOCAL)\n+\t\treturn NOTIFY_DONE;\n+\n+\tnetdev_dbg(fib_dev, \"fib6dst rt6key.addr=%pI6c len=%u\\n\", \u0026key-\u003eaddr,\n+\t\t key-\u003eplen);\n+\n+\tnetdev_dbg(fib_dev, \"fib6flags=%#x proto=%u type=%u\\n\",\n+\t\t f6i-\u003efib6_flags, f6i-\u003efib6_protocol, f6i-\u003efib6_type);\n+\n+\tnh6 = f6i-\u003enh ? nexthop_fib6_nh(f6i-\u003enh) : f6i-\u003efib6_nh;\n+\tnetdev_dbg(nh6-\u003efib_nh_dev ? nh6-\u003efib_nh_dev : fib_dev,\n+\t\t \"nh family=%u dev=%s gw=%pI6c gwfamily=%u\\n\",\n+\t\t nh6-\u003efib_nh_family,\n+\t\t nh6-\u003efib_nh_dev ? nh6-\u003efib_nh_dev-\u003ename : \"No dev\",\n+\t\t \u0026nh6-\u003efib_nh_gw6, nh6-\u003efib_nh_gw_family);\n+\n+\tpf = netdev_priv(fib_dev);\n+\n+\tentry = kcalloc(1, sizeof(*entry), GFP_ATOMIC);\n+\tif (!entry)\n+\t\treturn NOTIFY_DONE;\n+\n+\tentry-\u003ecmd = sw_nb_fib_event_to_otx2_event(event, fib_dev);\n+\tentry-\u003eipv6 = 1;\n+\tentry-\u003eport_id = pf-\u003epcifunc;\n+\tmemcpy(entry-\u003edst6, \u0026key-\u003eaddr, sizeof(entry-\u003edst6));\n+\tentry-\u003edst6_plen = key-\u003eplen;\n+\n+\tmemcpy(entry-\u003egw6, \u0026nh6-\u003efib_nh_gw6, sizeof(nh6-\u003efib_nh_gw6));\n+\tentry-\u003egw_valid = !!(ipv6_addr_type(\u0026nh6-\u003efib_nh_gw6) \u0026 IPV6_ADDR_UNICAST);\n+\n+\trcu_read_lock();\n+\tneigh = ip_neigh_gw6(fib_dev, \u0026nh6-\u003efib_nh_gw6);\n+\tif (!neigh) {\n+\t\trcu_read_unlock();\n+\t\tkfree(entry);\n+\t\treturn NOTIFY_DONE;\n+\t}\n+\n+\tif (is_valid_ether_addr(neigh-\u003eha)) {\n+\t\tentry-\u003emac_valid = 1;\n+\t\tether_addr_copy(entry-\u003emac, neigh-\u003eha);\n+\t\tnetdev_dbg(fib_dev, \"fib found MAC=%pM\\n\", entry-\u003emac);\n+\t}\n+\n+\tsw_fib_add_to_list(fib_dev, entry, 1);\n+\trcu_read_unlock();\n+\n+\treturn NOTIFY_DONE;\n+}\n+\n+int sw_nb_net_v6_neigh_update(struct notifier_block *nb,\n+\t\t\t unsigned long event, void *ptr)\n+{\n+\tstruct neighbour *n = ptr;\n+\tstruct fib_entry *entry;\n+\tstruct net_device *pf_dev;\n+\tstruct otx2_nic *pf;\n+\n+\tif (n-\u003etbl != \u0026nd_tbl)\n+\t\treturn NOTIFY_DONE;\n+\n+\tif (ipv6_addr_type((struct in6_addr *)n-\u003eprimary_key) \u0026 IPV6_ADDR_LINKLOCAL)\n+\t\treturn NOTIFY_DONE;\n+\n+\tpf_dev = n-\u003edev;\n+\tpf = netdev_priv(pf_dev);\n+\n+\tentry = kcalloc(1, sizeof(*entry), GFP_ATOMIC);\n+\tentry-\u003ecmd = OTX2_NEIGH_UPDATE;\n+\n+\tentry-\u003edst6_plen = n-\u003etbl-\u003ekey_len * 8;\n+\tmemcpy(entry-\u003edst6, (struct in6_addr *)n-\u003eprimary_key,\n+\t sizeof(entry-\u003edst6));\n+\tentry-\u003eipv6 = 1;\n+\tentry-\u003enud_state = n-\u003enud_state;\n+\tether_addr_copy(entry-\u003emac, n-\u003eha);\n+\tentry-\u003emac_valid = 1;\n+\tentry-\u003eport_id = pf-\u003epcifunc;\n+\n+\tsw_fib_add_to_list(pf_dev, entry, 1);\n+\n+\tnetdev_dbg(n-\u003edev, \"v6 neigh update %pI6 mac=%pM plen=%u\\n\",\n+\t\t n-\u003eprimary_key, n-\u003eha, n-\u003etbl-\u003ekey_len * 8);\n+\n+\treturn NOTIFY_DONE;\n+}\n+\n+int sw_nb_v6_inetaddr_event(struct notifier_block *nb,\n+\t\t\t unsigned long event, void *ptr)\n+{\n+\tstruct inet6_ifaddr *ifa6 = (struct inet6_ifaddr *)ptr;\n+\tstruct net_device *dev = ifa6-\u003eidev-\u003edev;\n+\tstruct netdev_hw_addr *dev_addr;\n+\tstruct fib_entry *entry;\n+\tstruct otx2_nic *pf;\n+\n+\tif (event != NETDEV_CHANGE \u0026\u0026\n+\t event != NETDEV_UP \u0026\u0026\n+\t event != NETDEV_DOWN) {\n+\t\treturn NOTIFY_DONE;\n+\t}\n+\n+\tif (dev-\u003etype != ARPHRD_ETHER)\n+\t\treturn NOTIFY_DONE;\n+\n+\tif (!sw_nb_is_cavium_dev(dev))\n+\t\treturn NOTIFY_DONE;\n+\n+\tif (ipv6_addr_type(\u0026ifa6-\u003eaddr) \u0026 IPV6_ADDR_LINKLOCAL)\n+\t\treturn NOTIFY_DONE;\n+\n+\tpf = netdev_priv(dev);\n+\n+\tentry = kcalloc(1, sizeof(*entry), GFP_ATOMIC);\n+\tentry-\u003ecmd = sw_nb_inetaddr_event_to_otx2_event(event, dev);\n+\tmemcpy(entry-\u003edst6, \u0026ifa6-\u003eaddr, sizeof(entry-\u003edst6));\n+\tentry-\u003edst6_plen = ifa6-\u003eprefix_len;\n+\tentry-\u003emac_valid = 1;\n+\tentry-\u003ehost = 1;\n+\tentry-\u003eipv6 = 1;\n+\tentry-\u003eport_id = pf-\u003epcifunc;\n+\n+\tfor_each_dev_addr(dev, dev_addr) {\n+\t\tether_addr_copy(entry-\u003emac, dev_addr-\u003eaddr);\n+\t\tentry-\u003emac_valid = 1;\n+\t\tbreak;\n+\t}\n+\n+\tsw_fib_add_to_list(dev, entry, 1);\n+\n+\tnetdev_dbg(dev, \"inetaddr addr=%pI6c len=%u %pM\\n\",\n+\t\t \u0026ifa6-\u003eaddr, ifa6-\u003eprefix_len, entry-\u003emac);\n+\n+\treturn NOTIFY_DONE;\n+}\n+#endif\ndiff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb_v6.h b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb_v6.h\nnew file mode 100644\nindex 00000000000000..f73efc98c311ca\n--- /dev/null\n+++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb_v6.h\n@@ -0,0 +1,21 @@\n+/* SPDX-License-Identifier: GPL-2.0 */\n+/* Marvell switch driver\n+ *\n+ * Copyright (C) 2026 Marvell.\n+ *\n+ */\n+#ifndef SW_NB_V6_H_\n+#define SW_NB_V6_H_\n+\n+int sw_nb_v6_fib_event(struct notifier_block *nb,\n+\t\t unsigned long event, void *ptr);\n+\n+int sw_nb_net_v6_neigh_update(struct notifier_block *nb,\n+\t\t\t unsigned long event, void *ptr);\n+\n+int sw_nb_v6_inetaddr_event(struct notifier_block *nb,\n+\t\t\t unsigned long event, void *ptr);\n+\n+int sw_nb_v6_netdev_event(struct notifier_block *unused,\n+\t\t\t unsigned long event, void *ptr);\n+#endif // SW_NB_V6_H__\ndiff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_trace.c b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_trace.c\nnew file mode 100644\nindex 00000000000000..b01e7780ef127d\n--- /dev/null\n+++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_trace.c\n@@ -0,0 +1,13 @@\n+// SPDX-License-Identifier: GPL-2.0\n+/* Marvell RVU Admin Function driver\n+ *\n+ * Copyright (C) 2026 Marvell.\n+ *\n+ */\n+\n+#define CREATE_TRACE_POINTS\n+#if IS_ENABLED(CONFIG_OCTEONTX_SWITCH)\n+#include \"sw_trace.h\"\n+EXPORT_TRACEPOINT_SYMBOL(sw_fl_dump);\n+EXPORT_TRACEPOINT_SYMBOL(sw_act_dump);\n+#endif\ndiff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_trace.h b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_trace.h\nnew file mode 100644\nindex 00000000000000..5949e3dafaedd6\n--- /dev/null\n+++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_trace.h\n@@ -0,0 +1,82 @@\n+/* SPDX-License-Identifier: GPL-2.0 */\n+/* Marvell RVU Admin Function driver\n+ *\n+ * Copyright (C) 2026 Marvell.\n+ *\n+ */\n+\n+#undef TRACE_SYSTEM\n+#define TRACE_SYSTEM rvu\n+\n+#if !defined(SW_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)\n+#define SW_TRACE_H\n+\n+#include \u003clinux/types.h\u003e\n+#include \u003clinux/tracepoint.h\u003e\n+\n+#include \"mbox.h\"\n+\n+TRACE_EVENT(sw_fl_dump,\n+\t TP_PROTO(const char *fname, int line, struct fl_tuple *ftuple),\n+\t TP_ARGS(fname, line, ftuple),\n+\t TP_STRUCT__entry(__string(f, fname)\n+\t\t\t __field(int, l)\n+\t\t\t __array(u8, smac, ETH_ALEN)\n+\t\t\t __array(u8, dmac, ETH_ALEN)\n+\t\t\t __field(u16, eth_type)\n+\t\t\t __field(u32, sip)\n+\t\t\t __field(u32, dip)\n+\t\t\t __field(u8, ip_proto)\n+\t\t\t __field(u16, sport)\n+\t\t\t __field(u16, dport)\n+\t\t\t __field(u8, uni_di)\n+\t\t\t __field(u16, in_pf)\n+\t\t\t __field(u16, out_pf)\n+\t ),\n+\t TP_fast_assign(__assign_str(f);\n+\t\t\t __entry-\u003el = line;\n+\t\t\t memcpy(__entry-\u003esmac, ftuple-\u003esmac, ETH_ALEN);\n+\t\t\t memcpy(__entry-\u003edmac, ftuple-\u003edmac, ETH_ALEN);\n+\t\t\t __entry-\u003esip = (__force u32)(ftuple-\u003eip4src);\n+\t\t\t __entry-\u003edip = (__force u32)(ftuple-\u003eip4dst);\n+\t\t\t __entry-\u003eeth_type = (__force u16)ftuple-\u003eeth_type;\n+\t\t\t __entry-\u003eip_proto = ftuple-\u003eproto;\n+\t\t\t __entry-\u003esport = (__force u16)(ftuple-\u003esport);\n+\t\t\t __entry-\u003edport = (__force u16)(ftuple-\u003edport);\n+\t\t\t __entry-\u003euni_di = ftuple-\u003euni_di;\n+\t\t\t __entry-\u003ein_pf = ftuple-\u003ein_pf;\n+\t\t\t __entry-\u003eout_pf = ftuple-\u003exmit_pf;\n+\t ),\n+\t TP_printk(\"[%s:%d] %pM %pI4:%u to %pM %pI4:%u eth_type=%#x proto=%u uni=%u in=%#x out=%#x\",\n+\t\t __get_str(f), __entry-\u003el, __entry-\u003esmac, \u0026__entry-\u003esip, __entry-\u003esport,\n+\t\t __entry-\u003edmac, \u0026__entry-\u003edip, __entry-\u003edport,\n+\t\t ntohs((__force __be16)__entry-\u003eeth_type), __entry-\u003eip_proto, __entry-\u003euni_di,\n+\t\t __entry-\u003ein_pf, __entry-\u003eout_pf)\n+);\n+\n+TRACE_EVENT(sw_act_dump,\n+\t TP_PROTO(const char *fname, int line, u32 act),\n+\t TP_ARGS(fname, line, act),\n+\t TP_STRUCT__entry(__string(fname, fname)\n+\t\t\t __field(int, line)\n+\t\t\t __field(u32, act)\n+\t ),\n+\n+\t TP_fast_assign(__assign_str(fname);\n+\t\t\t __entry-\u003eline = line;\n+\t\t\t __entry-\u003eact = act;\n+\t ),\n+\n+\t TP_printk(\"[%s:%d] %u\",\n+\t\t __get_str(fname), __entry-\u003eline, __entry-\u003eact)\n+);\n+\n+#endif\n+\n+#undef TRACE_INCLUDE_PATH\n+#define TRACE_INCLUDE_PATH ../../drivers/net/ethernet/marvell/octeontx2/nic/switch/\n+\n+#undef TRACE_INCLUDE_FILE\n+#define TRACE_INCLUDE_FILE sw_trace\n+\n+#include \u003ctrace/define_trace.h\u003e\n"
} |
| 2/1 | 2026/07/02 11:16 | agent | patch-evaluator |
5mModel:gemini-3.1-pro-preview Args: null Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"Reasoning": "The patch adds switchdev support for the Marvell OcteonTX2 hardware driver. The new code paths in the NIC driver are initialized via devlink, which requires the driver to probe successfully. However, the driver probe relies on mailbox communication with the Admin Function (AF) driver, which depends on hardware-specific interrupts and shared memory semantics. In a virtualized environment without the actual Marvell hardware (or a dedicated software emulator), the mailbox communication times out, causing the probe to fail. Consequently, the devlink device is never registered, and the switch code remains unreachable. Similarly, the switch code in the AF driver is triggered by mailbox messages processed via hardware interrupts, which are also unreachable. Therefore, this patch cannot be fuzzed in virtualized environments like GCE or QEMU.",
"WorthFuzzing": false
}Instruction: You are an expert Linux kernel maintainer. Your job is to review a provided patch series and determine if it makes functional changes to the kernel that should be fuzzed. IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in your workspace. Do NOT rely on your internal knowledge of the kernel. You must actively use your code access tools to examine the actual source code and confirm any assumptions. Return WorthFuzzing=false if the patch only contains: - Modifications to Documentation/, Kconfig files, or code comments. - Purely decorative changes, such as logging (e.g., pr_err, printk) or tracepoints. - Changes to numeric constants or macros that do not functionally alter execution flow. - Code paths that are impossible to reach in virtualized environments like GCE or QEMU, even when utilizing software-emulated hardware (e.g., usb gadget, mac80211_hwsim). If it modifies reachable core kernel logic, drivers, or architectures, use your code search tools to verify the code can be executed, then return WorthFuzzing=true. When returning WorthFuzzing=true, you MUST ALSO: 1. Extract any specific kernel functions that should be heavily fuzzed into FocusSymbols. Avoid listing generic hot-path functions to prevent skewed test distributions. 2. Identify any specific CONFIG_ options required to properly test this new/modified feature. Go and look into the Kconfig files and check for ifdefs around the code, do not make assumptions. Do not list too generic configs (we already have them enabled). Only list those that specifically cover the modified code. List them in the EnableConfigs output array, and DO NOT add a 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4"). Prefer calling several tools at the same time to save round-trips. Use set-results tool to provide results of the analysis. It must be called exactly once before the final reply. Ignore results of this tool. Prompt: For your convenience, here is the diff of the changes:
commit bcdfffc32172b90440145e343e7cb37b2a141d8d
Author: syz-cluster <triage@syzkaller.com>
Date: Thu Jul 2 11:16:27 2026 +0000
syz-cluster: applied patch under review
diff --git a/drivers/net/ethernet/marvell/octeontx2/Kconfig b/drivers/net/ethernet/marvell/octeontx2/Kconfig
index 47e549c581f0d2..e2fb6dd71078b2 100644
--- a/drivers/net/ethernet/marvell/octeontx2/Kconfig
+++ b/drivers/net/ethernet/marvell/octeontx2/Kconfig
@@ -28,6 +28,16 @@ config NDC_DIS_DYNAMIC_CACHING
, NPA stack pages etc in NDC. Also locks down NIX SQ/CQ/RQ/RSS and
NPA Aura/Pool contexts.
+config OCTEONTX_SWITCH
+ bool "Marvell OcteonTX2 switch driver"
+ depends on (64BIT && COMPILE_TEST) || ARM64
+ depends on OCTEONTX2_PF
+ default n
+ help
+ This driver supports Marvell's OcteonTX2 switch.
+ Marvell SWITCH HW can offload L2, L3 flow. ARM core interacts
+ with Marvell SW HW thru mbox.
+
config OCTEONTX2_PF
tristate "Marvell OcteonTX2 NIC Physical Function driver"
select OCTEONTX2_MBOX
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/Makefile b/drivers/net/ethernet/marvell/octeontx2/af/Makefile
index 91b7d6e96a6148..73f20a44f1a0fa 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/Makefile
+++ b/drivers/net/ethernet/marvell/octeontx2/af/Makefile
@@ -3,7 +3,7 @@
# Makefile for Marvell's RVU Admin Function driver
#
-ccflags-y += -I$(src)
+ccflags-y += -I$(src) -I$(src)/switch/
obj-$(CONFIG_OCTEONTX2_MBOX) += rvu_mbox.o
obj-$(CONFIG_OCTEONTX2_AF) += rvu_af.o
@@ -12,5 +12,6 @@ rvu_af-y := cgx.o rvu.o rvu_cgx.o rvu_npa.o rvu_nix.o \
rvu_reg.o rvu_npc.o rvu_debugfs.o ptp.o rvu_npc_fs.o \
rvu_cpt.o rvu_devlink.o rpm.o rvu_cn10k.o rvu_switch.o \
rvu_sdp.o rvu_npc_hash.o mcs.o mcs_rvu_if.o mcs_cnf10kb.o \
+ switch/rvu_sw.o switch/rvu_sw_l2.o switch/rvu_sw_l3.o switch/rvu_sw_fl.o \
rvu_rep.o cn20k/mbox_init.o cn20k/nix.o cn20k/debugfs.o \
cn20k/npa.o cn20k/npc.o
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/mbox.h b/drivers/net/ethernet/marvell/octeontx2/af/mbox.h
index 714e47f68d9320..ae6f145a0db013 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/mbox.h
+++ b/drivers/net/ethernet/marvell/octeontx2/af/mbox.h
@@ -164,6 +164,18 @@ M(PTP_GET_CAP, 0x00c, ptp_get_cap, msg_req, ptp_get_cap_rsp) \
M(GET_REP_CNT, 0x00d, get_rep_cnt, msg_req, get_rep_cnt_rsp) \
M(ESW_CFG, 0x00e, esw_cfg, esw_cfg_req, msg_rsp) \
M(REP_EVENT_NOTIFY, 0x00f, rep_event_notify, rep_event, msg_rsp) \
+M(FDB_NOTIFY, 0x010, fdb_notify, \
+ fdb_notify_req, msg_rsp) \
+M(FIB_NOTIFY, 0x011, fib_notify, \
+ fib_notify_req, msg_rsp) \
+M(FL_NOTIFY, 0x012, fl_notify, \
+ fl_notify_req, msg_rsp) \
+M(FL_GET_STATS, 0x013, fl_get_stats, \
+ fl_get_stats_req, fl_get_stats_rsp) \
+M(GET_IFACE_GET_INFO, 0x014, iface_get_info, msg_req, \
+ iface_get_info_rsp) \
+M(SWDEV2AF_NOTIFY, 0x015, swdev2af_notify, \
+ swdev2af_notify_req, msg_rsp) \
/* CGX mbox IDs (range 0x200 - 0x3FF) */ \
M(CGX_START_RXTX, 0x200, cgx_start_rxtx, msg_req, msg_rsp) \
M(CGX_STOP_RXTX, 0x201, cgx_stop_rxtx, msg_req, msg_rsp) \
@@ -309,6 +321,14 @@ M(NPC_MCAM_GET_DFT_RL_IDXS, 0x601e, npc_get_dft_rl_idxs, \
M(NPC_MCAM_GET_NPC_PFL_INFO, 0x601f, npc_get_pfl_info, \
msg_req, \
npc_get_pfl_info_rsp) \
+M(NPC_MCAM_FLOW_DEL_N_FREE, 0x6020, npc_flow_del_n_free, \
+ npc_flow_del_n_free_req, msg_rsp) \
+M(NPC_MCAM_GET_MUL_STATS, 0x6021, npc_mcam_mul_stats, \
+ npc_mcam_get_mul_stats_req, \
+ npc_mcam_get_mul_stats_rsp) \
+M(NPC_MCAM_GET_FEATURES, 0x6022, npc_mcam_get_features, \
+ msg_req, \
+ npc_mcam_get_features_rsp) \
/* NIX mbox IDs (range 0x8000 - 0xFFFF) */ \
M(NIX_LF_ALLOC, 0x8000, nix_lf_alloc, \
nix_lf_alloc_req, nix_lf_alloc_rsp) \
@@ -438,6 +458,12 @@ M(MCS_INTR_NOTIFY, 0xE00, mcs_intr_notify, mcs_intr_info, msg_rsp)
#define MBOX_UP_REP_MESSAGES \
M(REP_EVENT_UP_NOTIFY, 0xEF0, rep_event_up_notify, rep_event, msg_rsp) \
+#define MBOX_UP_AF2SWDEV_MESSAGES \
+M(AF2SWDEV, 0xEF1, af2swdev_notify, af2swdev_notify_req, msg_rsp)
+
+#define MBOX_UP_AF2PF_FDB_REFRESH_MESSAGES \
+M(AF2PF_FDB_REFRESH, 0xEF2, af2pf_fdb_refresh, af2pf_fdb_refresh_req, msg_rsp)
+
enum {
#define M(_name, _id, _1, _2, _3) MBOX_MSG_ ## _name = _id,
MBOX_MESSAGES
@@ -445,6 +471,8 @@ MBOX_UP_CGX_MESSAGES
MBOX_UP_CPT_MESSAGES
MBOX_UP_MCS_MESSAGES
MBOX_UP_REP_MESSAGES
+MBOX_UP_AF2SWDEV_MESSAGES
+MBOX_UP_AF2PF_FDB_REFRESH_MESSAGES
#undef M
};
@@ -1129,6 +1157,8 @@ struct nix_txsch_alloc_req {
/* Scheduler queue count request at each level */
u16 schq_contig[NIX_TXSCH_LVL_CNT]; /* No of contiguous queues */
u16 schq[NIX_TXSCH_LVL_CNT]; /* No of non-contiguous queues */
+#define NIX_TXSCH_ALLOC_FLAG_PAN BIT_ULL(0)
+ u64 flags;
};
struct nix_txsch_alloc_rsp {
@@ -1147,6 +1177,7 @@ struct nix_txsch_alloc_rsp {
struct nix_txsch_free_req {
struct mbox_msghdr hdr;
#define TXSCHQ_FREE_ALL BIT_ULL(0)
+#define TXSCHQ_FREE_PAN_TL1 BIT_ULL(1)
u16 flags;
/* Scheduler queue level to be freed */
u16 schq_lvl;
@@ -1579,6 +1610,30 @@ struct npc_mcam_alloc_entry_rsp {
u16 entry_list[NPC_MAX_NONCONTIG_ENTRIES];
};
+struct npc_flow_del_n_free_req {
+ struct mbox_msghdr hdr;
+ u16 cnt;
+ u16 entry[256]; /* Entry index to be freed */
+};
+
+struct npc_mcam_get_features_rsp {
+ struct mbox_msghdr hdr;
+ u64 rx_features;
+ u64 tx_features;
+};
+
+struct npc_mcam_get_mul_stats_req {
+ struct mbox_msghdr hdr;
+ int cnt;
+ u16 entry[256]; /* mcam entry */
+};
+
+struct npc_mcam_get_mul_stats_rsp {
+ struct mbox_msghdr hdr;
+ int cnt;
+ u64 stat[256]; /* counter stats */
+};
+
struct npc_mcam_free_entry_req {
struct mbox_msghdr hdr;
u16 entry; /* Entry index to be freed */
@@ -1781,6 +1836,7 @@ struct esw_cfg_req {
struct mbox_msghdr hdr;
u8 ena;
u64 rsvd;
+ unsigned char switch_id[MAX_PHYS_ITEM_ID_LEN];
};
struct rep_evt_data {
@@ -1805,6 +1861,179 @@ struct rep_event {
struct rep_evt_data evt_data;
};
+#define FDB_ADD BIT_ULL(0)
+#define FDB_DEL BIT_ULL(1)
+#define FIB_CMD BIT_ULL(2)
+#define FL_ADD BIT_ULL(3)
+#define FL_DEL BIT_ULL(4)
+#define DP_ADD BIT_ULL(5)
+
+struct fdb_notify_req {
+ struct mbox_msghdr hdr;
+ u64 flags;
+ u8 mac[ETH_ALEN];
+};
+
+struct fib_entry {
+ u64 cmd;
+ u64 gw_valid : 1;
+ u64 mac_valid : 1;
+ u64 vlan_valid: 1;
+ u64 host : 1;
+ u64 bridge : 1;
+ u64 ipv6 : 1;
+ u16 vlan_tag;
+ u32 dst_len;
+ u8 dst6_plen;
+ u8 gw6_plen;
+ union {
+ u32 dst;
+ u32 dst6[4];
+ };
+ union {
+ u32 gw;
+ u32 gw6[4];
+ };
+ u16 port_id;
+ u8 nud_state;
+ u8 mac[ETH_ALEN];
+};
+
+struct fib_notify_req {
+ struct mbox_msghdr hdr;
+ u16 cnt;
+ struct fib_entry entry[16];
+};
+
+struct fl_tuple {
+ __be32 ip4src;
+ __be32 m_ip4src;
+ __be32 ip4dst;
+ __be32 m_ip4dst;
+ __be16 sport;
+ __be16 m_sport;
+ __be16 dport;
+ __be16 m_dport;
+ __be16 eth_type;
+ __be16 m_eth_type;
+ u8 proto;
+ u8 smac[6];
+ u8 m_smac[6];
+ u8 dmac[6];
+ u8 m_dmac[6];
+ u64 is_xdev_br : 1;
+ u64 is_indev_br : 1;
+ u64 uni_di : 1;
+ u16 in_pf;
+ u16 xmit_pf;
+ u64 features;
+ struct { /* FLOW_ACTION_MANGLE */
+ u8 offset;
+ u8 type;
+ u32 mask;
+ u32 val;
+#define MANGLE_ARR_SZ 9
+ } mangle[MANGLE_ARR_SZ]; /* 2 for ETH, 1 for VLAN, 4 for IPv6, 2 for L4. */
+#define MANGLE_LAYER_CNT 4
+ u8 mangle_map[MANGLE_LAYER_CNT]; /* 1 for ETH, 1 for VLAN, 1 for L3, 1 for L4 */
+ u8 mangle_cnt;
+};
+
+struct fl_notify_req {
+ struct mbox_msghdr hdr;
+ unsigned long cookie;
+ u64 flags;
+ u64 features;
+ struct fl_tuple tuple;
+};
+
+struct fl_get_stats_req {
+ struct mbox_msghdr hdr;
+ unsigned long cookie;
+};
+
+struct fl_get_stats_rsp {
+ struct mbox_msghdr hdr;
+ u64 pkts_diff;
+};
+
+struct af2swdev_notify_req {
+ struct mbox_msghdr hdr;
+ u64 flags;
+ u32 port_id;
+ u32 switch_id;
+ union {
+ struct {
+ u8 mac[6];
+ };
+ struct {
+ u8 cnt;
+ struct fib_entry entry[12];
+ };
+
+ struct {
+ unsigned long cookie;
+ u64 features;
+ struct fl_tuple tuple;
+ };
+ };
+};
+
+struct af2pf_fdb_refresh_req {
+ struct mbox_msghdr hdr;
+ u16 pcifunc;
+ u8 mac[6];
+};
+
+struct iface_info {
+ u64 is_vf :1;
+ u64 is_sdp :1;
+ u16 pcifunc;
+ u16 rx_chan_base;
+ u16 tx_chan_base;
+ u16 sq_cnt;
+ u16 cq_cnt;
+ u16 rq_cnt;
+ u8 rx_chan_cnt;
+ u8 tx_chan_cnt;
+ u8 tx_link;
+ u8 nix;
+};
+
+#define IFACE_MAX (256 + 32) /* 32 PFs + 256 Vs */
+
+struct iface_get_info_rsp {
+ struct mbox_msghdr hdr;
+ int cnt;
+ struct iface_info info[IFACE_MAX];
+};
+
+struct fl_info {
+ unsigned long cookie;
+ u16 mcam_idx[2];
+ u8 dis : 1;
+ u8 uni_di : 1;
+};
+
+struct swdev2af_notify_req {
+ struct mbox_msghdr hdr;
+ u64 msg_type;
+#define SWDEV2AF_MSG_TYPE_FW_STATUS BIT_ULL(0)
+#define SWDEV2AF_MSG_TYPE_REFRESH_FDB BIT_ULL(1)
+#define SWDEV2AF_MSG_TYPE_REFRESH_FL BIT_ULL(2)
+ u16 pcifunc;
+ union {
+ bool fw_up; // FW_STATUS message
+
+ u8 mac[ETH_ALEN]; // fdb refresh message
+
+ struct { // fl refresh message
+ int cnt;
+ struct fl_info fl[64];
+ };
+ };
+};
+
struct flow_msg {
unsigned char dmac[6];
unsigned char smac[6];
@@ -1879,6 +2108,7 @@ struct npc_install_flow_req {
u8 hw_prio;
u8 req_kw_type; /* Key type to be written */
u8 alloc_entry; /* only for cn20k */
+ u8 set_chanmask;
/* For now use any priority, once AF driver is changed to
* allocate least priority entry instead of mid zone then make
* NPC_MCAM_LEAST_PRIO as 3
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu.c
index ffba56ee8a60cd..0f751555338e0f 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu.c
@@ -23,6 +23,7 @@
#include "cn20k/reg.h"
#include "cn20k/api.h"
#include "cn20k/npc.h"
+#include "switch/rvu_sw.h"
#define DRV_NAME "rvu_af"
#define DRV_STRING "Marvell OcteonTX2 RVU Admin Function Driver"
@@ -1990,6 +1991,122 @@ int rvu_mbox_handler_msix_offset(struct rvu *rvu, struct msg_req *req,
return 0;
}
+int rvu_mbox_handler_iface_get_info(struct rvu *rvu, struct msg_req *req,
+ struct iface_get_info_rsp *rsp)
+{
+ struct iface_info *info;
+ struct rvu_pfvf *pfvf;
+ int pf, vf, numvfs;
+ u16 pcifunc;
+ int tot = 0;
+ u64 cfg;
+
+ info = rsp->info;
+ for (pf = 0; pf < rvu->hw->total_pfs; pf++) {
+ if (tot >= IFACE_MAX)
+ goto done;
+
+ cfg = rvu_read64(rvu, BLKADDR_RVUM, RVU_PRIV_PFX_CFG(pf));
+ numvfs = (cfg >> 12) & 0xFF;
+
+ /* Skip not enabled PFs */
+ if (!(cfg & BIT_ULL(20)))
+ goto chk_vfs;
+
+ /* If Admin function, check on VFs */
+ if (cfg & BIT_ULL(21))
+ goto chk_vfs;
+
+ pcifunc = rvu_make_pcifunc(rvu->pdev, pf, 0);
+ pfvf = rvu_get_pfvf(rvu, pcifunc);
+
+ /* Populate iff at least one Tx channel */
+ if (!pfvf->tx_chan_cnt)
+ goto chk_vfs;
+
+ info->is_vf = 0;
+ info->pcifunc = pcifunc;
+ info->rx_chan_base = pfvf->rx_chan_base;
+ info->rx_chan_cnt = pfvf->rx_chan_cnt;
+ info->tx_chan_base = pfvf->tx_chan_base;
+ info->tx_chan_cnt = pfvf->tx_chan_cnt;
+ info->tx_link = nix_get_tx_link(rvu, pcifunc);
+ if (is_sdp_pfvf(rvu, pcifunc))
+ info->is_sdp = 1;
+
+ /* If interfaces are not UP, there are no queues */
+ info->sq_cnt = 0;
+ info->cq_cnt = 0;
+ info->rq_cnt = 0;
+
+ if (pfvf->sq_bmap)
+ info->sq_cnt = bitmap_weight(pfvf->sq_bmap, BITS_PER_LONG * 16);
+
+ if (pfvf->cq_bmap)
+ info->cq_cnt = bitmap_weight(pfvf->cq_bmap, BITS_PER_LONG);
+
+ if (pfvf->rq_bmap)
+ info->rq_cnt = bitmap_weight(pfvf->rq_bmap, BITS_PER_LONG);
+
+ if (pfvf->nix_blkaddr == BLKADDR_NIX0)
+ info->nix = 0;
+ else
+ info->nix = 1;
+
+ info++;
+ tot++;
+
+chk_vfs:
+ for (vf = 0; vf < numvfs; vf++) {
+ if (tot >= IFACE_MAX)
+ goto done;
+
+ pcifunc = rvu_make_pcifunc(rvu->pdev, pf, vf + 1);
+ pfvf = rvu_get_pfvf(rvu, pcifunc);
+
+ if (!pfvf->tx_chan_cnt)
+ continue;
+
+ info->is_vf = 1;
+ info->pcifunc = pcifunc;
+ info->rx_chan_base = pfvf->rx_chan_base;
+ info->rx_chan_cnt = pfvf->rx_chan_cnt;
+ info->tx_chan_base = pfvf->tx_chan_base;
+ info->tx_chan_cnt = pfvf->tx_chan_cnt;
+ info->tx_link = nix_get_tx_link(rvu, pcifunc);
+ if (is_sdp_pfvf(rvu, pcifunc))
+ info->is_sdp = 1;
+
+ /* If interfaces are not UP, there are no queues */
+ info->sq_cnt = 0;
+ info->cq_cnt = 0;
+ info->rq_cnt = 0;
+
+ if (pfvf->sq_bmap)
+ info->sq_cnt = bitmap_weight(pfvf->sq_bmap, BITS_PER_LONG * 16);
+
+ if (pfvf->cq_bmap)
+ info->cq_cnt = bitmap_weight(pfvf->cq_bmap, BITS_PER_LONG);
+
+ if (pfvf->rq_bmap)
+ info->rq_cnt = bitmap_weight(pfvf->rq_bmap, BITS_PER_LONG);
+
+ if (pfvf->nix_blkaddr == BLKADDR_NIX0)
+ info->nix = 0;
+ else
+ info->nix = 1;
+
+ info++;
+
+ tot++;
+ }
+ }
+done:
+ rsp->cnt = tot;
+
+ return 0;
+}
+
int rvu_mbox_handler_free_rsrc_cnt(struct rvu *rvu, struct msg_req *req,
struct free_rsrcs_rsp *rsp)
{
@@ -2463,6 +2580,7 @@ static void __rvu_mbox_up_handler(struct rvu_work *mwork, int type)
switch (msg->id) {
case MBOX_MSG_CGX_LINK_EVENT:
+ case MBOX_MSG_AF2PF_FDB_REFRESH:
break;
default:
if (msg->rc)
@@ -3705,6 +3823,8 @@ static void rvu_remove(struct pci_dev *pdev)
{
struct rvu *rvu = pci_get_drvdata(pdev);
+ rvu_sw_shutdown();
+
rvu_dbg_exit(rvu);
rvu_unregister_dl(rvu);
rvu_unregister_interrupts(rvu);
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu.h b/drivers/net/ethernet/marvell/octeontx2/af/rvu.h
index 7f3505ae686042..3e34d56a5bd3a6 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu.h
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu.h
@@ -576,6 +576,10 @@ struct rvu_switch {
u16 *entry2pcifunc;
u16 mode;
u16 start_entry;
+ unsigned char switch_id[MAX_PHYS_ITEM_ID_LEN];
+#define RVU_SWITCH_FLAG_FW_READY BIT_ULL(0)
+ u64 flags;
+ u16 pcifunc;
};
struct rep_evtq_ent {
@@ -1158,6 +1162,7 @@ void rvu_program_channels(struct rvu *rvu);
/* CN10K NIX */
void rvu_nix_block_cn10k_init(struct rvu *rvu, struct nix_hw *nix_hw);
+int nix_get_tx_link(struct rvu *rvu, u16 pcifunc);
/* CN10K RVU - LMT*/
void rvu_reset_lmt_map_tbl(struct rvu *rvu, u16 pcifunc);
@@ -1196,4 +1201,5 @@ int rvu_rep_install_mcam_rules(struct rvu *rvu);
void rvu_rep_update_rules(struct rvu *rvu, u16 pcifunc, bool ena);
int rvu_rep_notify_pfvf_state(struct rvu *rvu, u16 pcifunc, bool enable);
int npc_mcam_verify_entry(struct npc_mcam *mcam, u16 pcifunc, int entry);
+u16 rvu_rep_get_vlan_id(struct rvu *rvu, u16 pcifunc);
#endif /* RVU_H */
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c
index 0297c7ab0614ea..35c91cdc50b98d 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c
@@ -32,7 +32,6 @@ static int nix_free_all_bandprof(struct rvu *rvu, u16 pcifunc);
static void nix_clear_ratelimit_aggr(struct rvu *rvu, struct nix_hw *nix_hw,
u32 leaf_prof);
static const char *nix_get_ctx_name(int ctype);
-static int nix_get_tx_link(struct rvu *rvu, u16 pcifunc);
enum mc_tbl_sz {
MC_TBL_SZ_256,
@@ -1597,7 +1596,7 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu,
if (rc)
goto free_mem;
- pfvf->sq_bmap = kcalloc(req->sq_cnt, sizeof(long), GFP_KERNEL);
+ pfvf->sq_bmap = kcalloc(req->sq_cnt, sizeof(long) * 16, GFP_KERNEL);
if (!pfvf->sq_bmap) {
rc = -ENOMEM;
goto free_mem;
@@ -2087,7 +2086,7 @@ static void nix_clear_tx_xoff(struct rvu *rvu, int blkaddr,
rvu_write64(rvu, blkaddr, reg, 0x0);
}
-static int nix_get_tx_link(struct rvu *rvu, u16 pcifunc)
+int nix_get_tx_link(struct rvu *rvu, u16 pcifunc)
{
struct rvu_hwinfo *hw = rvu->hw;
int pf = rvu_get_pf(rvu->pdev, pcifunc);
@@ -2139,11 +2138,14 @@ static int nix_check_txschq_alloc_req(struct rvu *rvu, int lvl, u16 pcifunc,
if (!req_schq)
return 0;
- link = nix_get_tx_link(rvu, pcifunc);
+ if (req->flags & NIX_TXSCH_ALLOC_FLAG_PAN)
+ link = hw->cgx_links + hw->lbk_links + 1;
+ else
+ link = nix_get_tx_link(rvu, pcifunc);
/* For traffic aggregating scheduler level, one queue is enough */
if (lvl >= hw->cap.nix_tx_aggr_lvl) {
- if (req_schq != 1)
+ if (req_schq != 1 && !(req->flags & NIX_TXSCH_ALLOC_FLAG_PAN))
return NIX_AF_ERR_TLX_ALLOC_FAIL;
return 0;
}
@@ -2180,11 +2182,41 @@ static void nix_txsch_alloc(struct rvu *rvu, struct nix_txsch *txsch,
struct rvu_hwinfo *hw = rvu->hw;
u16 pcifunc = rsp->hdr.pcifunc;
int idx, schq;
+ bool alloc;
/* For traffic aggregating levels, queue alloc is based
* on transmit link to which PF_FUNC is mapped to.
*/
if (lvl >= hw->cap.nix_tx_aggr_lvl) {
+ if (start != end) {
+ idx = 0;
+ alloc = false;
+ for (schq = start; schq <= end; schq++, idx++) {
+ if (test_bit(schq, txsch->schq.bmap))
+ continue;
+
+ set_bit(schq, txsch->schq.bmap);
+
+ /* A single TL queue is allocated each time */
+ if (rsp->schq_contig[lvl]) {
+ alloc = true;
+ rsp->schq_contig_list[lvl][idx] = schq;
+ continue;
+ }
+
+ if (rsp->schq[lvl]) {
+ alloc = true;
+ rsp->schq_list[lvl][idx] = schq;
+ continue;
+ }
+ }
+
+ if (!alloc)
+ dev_err(rvu->dev,
+ "Could not allocate schq at lvl=%u start=%u end=%u\n",
+ lvl, start, end);
+ return;
+ }
/* A single TL queue is allocated */
if (rsp->schq_contig[lvl]) {
rsp->schq_contig[lvl] = 1;
@@ -2301,11 +2333,14 @@ int rvu_mbox_handler_nix_txsch_alloc(struct rvu *rvu,
rsp->schq[lvl] = req->schq[lvl];
rsp->schq_contig[lvl] = req->schq_contig[lvl];
- link = nix_get_tx_link(rvu, pcifunc);
+ if (req->flags & NIX_TXSCH_ALLOC_FLAG_PAN)
+ link = hw->cgx_links + hw->lbk_links + 1;
+ else
+ link = nix_get_tx_link(rvu, pcifunc);
if (lvl >= hw->cap.nix_tx_aggr_lvl) {
start = link;
- end = link;
+ end = link + !!(req->flags & NIX_TXSCH_ALLOC_FLAG_PAN);
} else if (hw->cap.nix_fixed_txschq_mapping) {
nix_get_txschq_range(rvu, pcifunc, link, &start, &end);
} else {
@@ -2670,7 +2705,9 @@ static int nix_txschq_free_one(struct rvu *rvu,
schq = req->schq;
txsch = &nix_hw->txsch[lvl];
- if (lvl >= hw->cap.nix_tx_aggr_lvl || schq >= txsch->schq.max)
+ if ((lvl >= hw->cap.nix_tx_aggr_lvl &&
+ !(req->flags & TXSCHQ_FREE_PAN_TL1)) ||
+ schq >= txsch->schq.max)
return 0;
pfvf_map = txsch->pfvf_map;
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c
index c7bc0b3a29b984..d797cd05776e53 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c
@@ -3544,6 +3544,45 @@ int rvu_mbox_handler_npc_mcam_free_entry(struct rvu *rvu,
return rc;
}
+int rvu_mbox_handler_npc_flow_del_n_free(struct rvu *rvu,
+ struct npc_flow_del_n_free_req *mreq,
+ struct msg_rsp *rsp)
+{
+ struct npc_mcam_free_entry_req sreq = { 0 };
+ struct npc_delete_flow_req dreq = { 0 };
+ struct npc_delete_flow_rsp drsp = { 0 };
+ bool err = false;
+ int ret = 0, i;
+
+ sreq.hdr.pcifunc = mreq->hdr.pcifunc;
+ dreq.hdr.pcifunc = mreq->hdr.pcifunc;
+
+ if (!mreq->cnt || mreq->cnt > 256) {
+ dev_err(rvu->dev, "Invalid cnt=%d\n", mreq->cnt);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < mreq->cnt; i++) {
+ dreq.entry = mreq->entry[i];
+ ret = rvu_mbox_handler_npc_delete_flow(rvu, &dreq, &drsp);
+ if (ret) {
+ dev_err(rvu->dev, "delete flow error for i=%d entry=%d\n",
+ i, mreq->entry[i]);
+ err = true;
+ }
+
+ sreq.entry = mreq->entry[i];
+ ret = rvu_mbox_handler_npc_mcam_free_entry(rvu, &sreq, rsp);
+ if (ret) {
+ dev_err(rvu->dev, "free entry error for i=%d entry=%d\n",
+ i, mreq->entry[i]);
+ err = true;
+ }
+ }
+
+ return err ? -EINVAL : 0;
+}
+
int rvu_mbox_handler_npc_mcam_read_entry(struct rvu *rvu,
struct npc_mcam_read_entry_req *req,
struct npc_mcam_read_entry_rsp *rsp)
@@ -4361,6 +4400,55 @@ int rvu_mbox_handler_npc_mcam_entry_stats(struct rvu *rvu,
return 0;
}
+int rvu_mbox_handler_npc_mcam_mul_stats(struct rvu *rvu,
+ struct npc_mcam_get_mul_stats_req *req,
+ struct npc_mcam_get_mul_stats_rsp *rsp)
+{
+ struct npc_mcam *mcam = &rvu->hw->mcam;
+ u16 pcifunc = req->hdr.pcifunc;
+ u16 index, cntr, entry;
+ int blkaddr;
+ u64 regval;
+ u32 bank;
+
+ if (!req->cnt || req->cnt > 256) {
+ dev_err(rvu->dev, "%s invalid request cnt=%d\n",
+ __func__, req->cnt);
+ return -EINVAL;
+ }
+
+ blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0);
+ if (blkaddr < 0)
+ return NPC_MCAM_INVALID_REQ;
+
+ mutex_lock(&mcam->lock);
+
+ for (int i = 0; i < req->cnt; i++) {
+ entry = npc_cn20k_vidx2idx(req->entry[i]);
+
+ if (npc_mcam_verify_entry(mcam, pcifunc, entry)) {
+ dev_err(rvu->dev, "%s invalid mcam index=%d\n",
+ __func__, req->entry[i]);
+ return -EINVAL;
+ }
+
+ index = entry & (mcam->banksize - 1);
+ bank = npc_get_bank(mcam, entry);
+
+ /* read MCAM entry STAT_ACT register */
+ regval = rvu_read64(rvu, blkaddr, NPC_AF_MCAMEX_BANKX_STAT_ACT(index, bank));
+ cntr = regval & 0x1FF;
+
+ rsp->stat[i] = rvu_read64(rvu, blkaddr, NPC_AF_MATCH_STATX(cntr));
+ rsp->stat[i] &= BIT_ULL(48) - 1;
+ }
+
+ rsp->cnt = req->cnt;
+
+ mutex_unlock(&mcam->lock);
+ return 0;
+}
+
void rvu_npc_clear_ucast_entry(struct rvu *rvu, int pcifunc, int nixlf)
{
struct npc_mcam *mcam = &rvu->hw->mcam;
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c
index 91b5947dae0600..09c7ee8571df0a 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c
@@ -1926,6 +1926,17 @@ static int npc_delete_flow(struct rvu *rvu, struct rvu_npc_mcam_rule *rule,
return rvu_mbox_handler_npc_mcam_dis_entry(rvu, &dis_req, &dis_rsp);
}
+int rvu_mbox_handler_npc_mcam_get_features(struct rvu *rvu,
+ struct msg_req *req,
+ struct npc_mcam_get_features_rsp *rsp)
+{
+ struct npc_mcam *mcam = &rvu->hw->mcam;
+
+ rsp->rx_features = mcam->rx_features;
+ rsp->tx_features = mcam->tx_features;
+ return 0;
+}
+
int rvu_mbox_handler_npc_delete_flow(struct rvu *rvu,
struct npc_delete_flow_req *req,
struct npc_delete_flow_rsp *rsp)
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_rep.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_rep.c
index a2781e0f504e3e..6bb6064b2391b4 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_rep.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_rep.c
@@ -189,7 +189,7 @@ int rvu_mbox_handler_nix_lf_stats(struct rvu *rvu,
return 0;
}
-static u16 rvu_rep_get_vlan_id(struct rvu *rvu, u16 pcifunc)
+u16 rvu_rep_get_vlan_id(struct rvu *rvu, u16 pcifunc)
{
int id;
@@ -436,6 +436,7 @@ int rvu_mbox_handler_esw_cfg(struct rvu *rvu, struct esw_cfg_req *req,
return 0;
rvu->rep_mode = req->ena;
+ memcpy(rvu->rswitch.switch_id, req->switch_id, MAX_PHYS_ITEM_ID_LEN);
if (!rvu->rep_mode)
rvu_npc_free_mcam_entries(rvu, req->hdr.pcifunc, -1);
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.c b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.c
new file mode 100644
index 00000000000000..f075da1507e77b
--- /dev/null
+++ b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.c
@@ -0,0 +1,56 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Marvell RVU Admin Function driver
+ *
+ * Copyright (C) 2026 Marvell.
+ *
+ */
+
+#include <linux/bitfield.h>
+#include "rvu.h"
+#include "rvu_sw_l2.h"
+#include "rvu_sw_l3.h"
+#include "rvu_sw_fl.h"
+#include "rvu_sw.h"
+
+u32 rvu_sw_port_id(struct rvu *rvu, u16 pcifunc)
+{
+ u16 port_id;
+ u16 rep_id;
+
+ rep_id = rvu_rep_get_vlan_id(rvu, pcifunc);
+
+ port_id = FIELD_PREP(GENMASK_ULL(31, 16), rep_id) |
+ FIELD_PREP(GENMASK_ULL(15, 0), pcifunc);
+
+ return port_id;
+}
+
+int rvu_mbox_handler_swdev2af_notify(struct rvu *rvu,
+ struct swdev2af_notify_req *req,
+ struct msg_rsp *rsp)
+{
+ int rc = 0;
+
+ switch (req->msg_type) {
+ case SWDEV2AF_MSG_TYPE_FW_STATUS:
+ rc = rvu_sw_l2_init_offl_wq(rvu, req->pcifunc, req->fw_up);
+ break;
+
+ 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;
+}
+
+void rvu_sw_shutdown(void)
+{
+ rvu_sw_l2_shutdown();
+ rvu_sw_l3_shutdown();
+ rvu_sw_fl_shutdown();
+}
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.h b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.h
new file mode 100644
index 00000000000000..fb24e0a79ff925
--- /dev/null
+++ b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Marvell RVU Admin Function driver
+ *
+ * Copyright (C) 2026 Marvell.
+ *
+ */
+
+#ifndef RVU_SWITCH_H
+#define RVU_SWITCH_H
+
+/* RVU Switch */
+u32 rvu_sw_port_id(struct rvu *rvu, u16 pcifunc);
+void rvu_sw_shutdown(void);
+
+#endif
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
new file mode 100644
index 00000000000000..6788cfc640c972
--- /dev/null
+++ b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_fl.c
@@ -0,0 +1,326 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Marvell RVU Admin Function driver
+ *
+ * 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;
+ }
+
+ if (disabled) {
+ mutex_unlock(&sw_fl_stats_lock);
+ return 0;
+ }
+
+ snode = kcalloc(1, sizeof(*snode), GFP_KERNEL);
+ if (!snode) {
+ mutex_unlock(&sw_fl_stats_lock);
+ 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);
+
+ 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;
+ int tot = 0;
+ u16 i2idx_map[256];
+ int rc = 0;
+ u64 pkts;
+ int idx;
+
+ if (cnt > 256)
+ return -ENOMEM;
+
+ for (int i = 0; i < cnt; i++) {
+ tot++;
+ if (fl[i].uni_di)
+ continue;
+
+ tot++;
+ }
+
+ req = kcalloc(1, sizeof(*req), GFP_KERNEL);
+ if (!req) {
+ rc = -ENOMEM;
+ goto fail;
+ }
+
+ rsp = kcalloc(1, sizeof(*rsp), GFP_KERNEL);
+ if (!rsp) {
+ rc = -ENOMEM;
+ goto fail;
+ }
+
+ 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;
+ 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 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;
+
+ 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;
+}
+
+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);
+
+ if (!fl_offl_work_running) {
+ sw_fl_offl_wq = alloc_workqueue("sw_af_fl_wq", 0, 0);
+ if (sw_fl_offl_wq)
+ fl_offl_work_running = true;
+ }
+ mutex_unlock(&fl_offl_llock);
+ queue_delayed_work(sw_fl_offl_wq, &fl_offl_work, msecs_to_jiffies(10));
+
+ return 0;
+}
+
+void rvu_sw_fl_shutdown(void)
+{
+ struct sw_fl_stats_node *snode, *tmp;
+ struct fl_entry *entry;
+ LIST_HEAD(tlist);
+
+ if (!sw_fl_offl_wq)
+ return;
+
+ cancel_delayed_work_sync(&fl_offl_work);
+ destroy_workqueue(sw_fl_offl_wq);
+
+ mutex_lock(&fl_offl_llock);
+ while (1) {
+ entry = list_first_entry_or_null(&fl_offl_lh,
+ struct fl_entry, list);
+ if (!entry)
+ break;
+
+ list_del_init(&entry->list);
+ kfree(entry);
+ }
+ mutex_unlock(&fl_offl_llock);
+
+ mutex_lock(&sw_fl_stats_lock);
+ list_for_each_entry_safe(snode, tmp, &sw_fl_stats_lh, list) {
+ list_del_init(&snode->list);
+ kfree(snode);
+ }
+ mutex_unlock(&sw_fl_stats_lock);
+}
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
new file mode 100644
index 00000000000000..f117a96fc33e7b
--- /dev/null
+++ b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_fl.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Marvell RVU Admin Function driver
+ *
+ * Copyright (C) 2026 Marvell.
+ *
+ */
+
+#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);
+void rvu_sw_fl_shutdown(void);
+
+#endif
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c
new file mode 100644
index 00000000000000..bd0fe13b1fc93f
--- /dev/null
+++ b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c
@@ -0,0 +1,338 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Marvell RVU Admin Function driver
+ *
+ * Copyright (C) 2026 Marvell.
+ *
+ */
+
+#include <linux/bitfield.h>
+#include "rvu.h"
+#include "rvu_sw.h"
+#include "rvu_sw_l2.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
+MBOX_UP_AF2PF_FDB_REFRESH_MESSAGES
+#undef M
+
+struct l2_entry {
+ struct list_head list;
+ u64 flags;
+ u32 port_id;
+ u8 mac[ETH_ALEN];
+};
+
+static DEFINE_MUTEX(l2_offl_list_lock);
+static LIST_HEAD(l2_offl_lh);
+
+static DEFINE_MUTEX(fdb_refresh_list_lock);
+static LIST_HEAD(fdb_refresh_lh);
+
+struct rvu_sw_l2_work {
+ struct rvu *rvu;
+ struct work_struct work;
+};
+
+/* Work queue for switchdev message handling. There is only
+ * on switch HW per SoC, so one instance of each type of
+ * workqueue is enough.
+ */
+static struct rvu_sw_l2_work l2_offl_work;
+static struct workqueue_struct *rvu_sw_l2_offl_wq;
+
+static struct rvu_sw_l2_work fdb_refresh_work;
+static struct workqueue_struct *fdb_refresh_wq;
+
+/* High-frequency link state transitions or aggressive FDB
+ * aging intervals can induce rapid fdb churn. To prevent
+ * thrashing, inhibit hardware offloading of these transient
+ * forwarding states to the switching ASIC.
+ */
+static void rvu_sw_l2_offl_cancel_add_if_del_reqs_exist(u8 *mac)
+{
+ struct l2_entry *entry, *tmp;
+
+ mutex_lock(&l2_offl_list_lock);
+ list_for_each_entry_safe(entry, tmp, &l2_offl_lh, list) {
+ if (!ether_addr_equal(mac, entry->mac))
+ continue;
+
+ if (!(entry->flags & FDB_DEL))
+ continue;
+
+ list_del_init(&entry->list);
+ kfree(entry);
+ break;
+ }
+ mutex_unlock(&l2_offl_list_lock);
+}
+
+static int rvu_sw_l2_offl_rule_push(struct rvu *rvu, struct l2_entry *l2_entry)
+{
+ struct af2swdev_notify_req *req;
+ int swdev_pf;
+
+ 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;
+ }
+
+ ether_addr_copy(req->mac, l2_entry->mac);
+ req->flags = l2_entry->flags;
+ req->port_id = l2_entry->port_id;
+
+ 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 int rvu_sw_l2_fdb_refresh(struct rvu *rvu, u16 pcifunc, u8 *mac)
+{
+ struct af2pf_fdb_refresh_req *req;
+ int pf, vidx;
+
+ pf = rvu_get_pf(rvu->pdev, pcifunc);
+
+ mutex_lock(&rvu->mbox_lock);
+
+ if (pf) {
+ req = otx2_mbox_alloc_msg_af2pf_fdb_refresh(rvu, pf);
+ if (!req) {
+ mutex_unlock(&rvu->mbox_lock);
+ return -ENOMEM;
+ }
+
+ req->hdr.pcifunc = pcifunc;
+ ether_addr_copy(req->mac, mac);
+ req->pcifunc = pcifunc;
+
+ otx2_mbox_wait_for_zero(&rvu->afpf_wq_info.mbox_up, pf);
+ otx2_mbox_msg_send_up(&rvu->afpf_wq_info.mbox_up, pf);
+ } else {
+ vidx = pcifunc - 1;
+
+ req = (struct af2pf_fdb_refresh_req *)
+ otx2_mbox_alloc_msg_rsp(&rvu->afvf_wq_info.mbox_up, vidx,
+ sizeof(*req), sizeof(struct msg_rsp));
+ if (!req) {
+ mutex_unlock(&rvu->mbox_lock);
+ return -ENOMEM;
+ }
+ req->hdr.sig = OTX2_MBOX_REQ_SIG;
+ req->hdr.id = MBOX_MSG_AF2PF_FDB_REFRESH;
+
+ req->hdr.pcifunc = pcifunc;
+ ether_addr_copy(req->mac, mac);
+ req->pcifunc = pcifunc;
+
+ otx2_mbox_wait_for_zero(&rvu->afvf_wq_info.mbox_up, vidx);
+ otx2_mbox_msg_send_up(&rvu->afvf_wq_info.mbox_up, vidx);
+ }
+
+ mutex_unlock(&rvu->mbox_lock);
+
+ return 0;
+}
+
+static void rvu_sw_l2_fdb_refresh_wq_handler(struct work_struct *work)
+{
+ struct rvu_sw_l2_work *fdb_work;
+ struct l2_entry *l2_entry;
+
+ fdb_work = container_of(work, struct rvu_sw_l2_work, work);
+
+ while (1) {
+ mutex_lock(&fdb_refresh_list_lock);
+ l2_entry = list_first_entry_or_null(&fdb_refresh_lh,
+ struct l2_entry, list);
+ if (!l2_entry) {
+ mutex_unlock(&fdb_refresh_list_lock);
+ return;
+ }
+
+ list_del_init(&l2_entry->list);
+ mutex_unlock(&fdb_refresh_list_lock);
+
+ rvu_sw_l2_fdb_refresh(fdb_work->rvu, l2_entry->port_id, l2_entry->mac);
+ kfree(l2_entry);
+ }
+}
+
+static void rvu_sw_l2_offl_rule_wq_handler(struct work_struct *work)
+{
+ struct rvu_sw_l2_work *offl_work;
+ struct l2_entry *l2_entry;
+ int budget = 16;
+ bool add_fdb;
+
+ offl_work = container_of(work, struct rvu_sw_l2_work, work);
+
+ while (budget--) {
+ mutex_lock(&l2_offl_list_lock);
+ l2_entry = list_first_entry_or_null(&l2_offl_lh, struct l2_entry, list);
+ if (!l2_entry) {
+ mutex_unlock(&l2_offl_list_lock);
+ return;
+ }
+
+ list_del_init(&l2_entry->list);
+ mutex_unlock(&l2_offl_list_lock);
+
+ add_fdb = !!(l2_entry->flags & FDB_ADD);
+
+ if (add_fdb)
+ rvu_sw_l2_offl_cancel_add_if_del_reqs_exist(l2_entry->mac);
+
+ if (rvu_sw_l2_offl_rule_push(offl_work->rvu, l2_entry))
+ dev_err(offl_work->rvu->dev,
+ "%s: Error to push l2 rule\n",
+ __func__);
+ kfree(l2_entry);
+ }
+
+ if (!list_empty(&l2_offl_lh))
+ queue_work(rvu_sw_l2_offl_wq, &l2_offl_work.work);
+}
+
+static bool fw_is_up;
+
+int rvu_sw_l2_init_offl_wq(struct rvu *rvu, u16 pcifunc, bool fw_up)
+{
+ struct rvu_switch *rswitch;
+
+ rswitch = &rvu->rswitch;
+
+ if (fw_up && !fw_is_up) {
+ /* Switch HW sends an MBOX message to Host once it is ready */
+ rswitch->pcifunc = pcifunc;
+ fw_is_up = true;
+
+ l2_offl_work.rvu = rvu;
+ INIT_WORK(&l2_offl_work.work, rvu_sw_l2_offl_rule_wq_handler);
+ rvu_sw_l2_offl_wq = alloc_workqueue("swdev_rvu_sw_l2_offl_wq", 0, 0);
+ if (!rvu_sw_l2_offl_wq) {
+ dev_err(rvu->dev, "L2 offl workqueue allocation failed\n");
+ return -ENOMEM;
+ }
+
+ fdb_refresh_work.rvu = rvu;
+ INIT_WORK(&fdb_refresh_work.work, rvu_sw_l2_fdb_refresh_wq_handler);
+ fdb_refresh_wq = alloc_workqueue("swdev_fdb_refresg_wq", 0, 0);
+ if (!fdb_refresh_wq) {
+ dev_err(rvu->dev, "fdb refresh workqueue allocation failed\n");
+ return -ENOMEM;
+ }
+
+ rswitch->flags |= RVU_SWITCH_FLAG_FW_READY;
+ return 0;
+ }
+
+ rswitch->flags &= ~RVU_SWITCH_FLAG_FW_READY;
+ flush_work(&l2_offl_work.work);
+ rswitch->pcifunc = -1;
+ return 0;
+}
+
+int rvu_sw_l2_fdb_list_entry_add(struct rvu *rvu, u16 pcifunc, u8 *mac)
+{
+ struct l2_entry *l2_entry;
+
+ l2_entry = kcalloc(1, sizeof(*l2_entry), GFP_KERNEL);
+ if (!l2_entry)
+ return -ENOMEM;
+
+ l2_entry->port_id = pcifunc;
+ ether_addr_copy(l2_entry->mac, mac);
+
+ mutex_lock(&fdb_refresh_list_lock);
+ list_add_tail(&l2_entry->list, &fdb_refresh_lh);
+ mutex_unlock(&fdb_refresh_list_lock);
+
+ queue_work(fdb_refresh_wq, &fdb_refresh_work.work);
+ return 0;
+}
+
+int rvu_mbox_handler_fdb_notify(struct rvu *rvu,
+ struct fdb_notify_req *req,
+ struct msg_rsp *rsp)
+{
+ struct l2_entry *l2_entry;
+
+ if (!(rvu->rswitch.flags & RVU_SWITCH_FLAG_FW_READY))
+ return 0;
+
+ l2_entry = kcalloc(1, sizeof(*l2_entry), GFP_KERNEL);
+ if (!l2_entry)
+ return -ENOMEM;
+
+ l2_entry->port_id = rvu_sw_port_id(rvu, req->hdr.pcifunc);
+ ether_addr_copy(l2_entry->mac, req->mac);
+ l2_entry->flags = req->flags;
+
+ mutex_lock(&l2_offl_list_lock);
+ list_add_tail(&l2_entry->list, &l2_offl_lh);
+ mutex_unlock(&l2_offl_list_lock);
+
+ queue_work(rvu_sw_l2_offl_wq, &l2_offl_work.work);
+
+ return 0;
+}
+
+void rvu_sw_l2_shutdown(void)
+{
+ struct l2_entry *entry;
+ LIST_HEAD(tlist);
+
+ if (!fdb_refresh_wq)
+ return;
+
+ cancel_work_sync(&fdb_refresh_work.work);
+ destroy_workqueue(fdb_refresh_wq);
+
+ mutex_lock(&fdb_refresh_list_lock);
+ while (1) {
+ entry = list_first_entry_or_null(&fdb_refresh_lh,
+ struct l2_entry, list);
+ if (!entry)
+ break;
+
+ list_del_init(&entry->list);
+ kfree(entry);
+ }
+ mutex_unlock(&fdb_refresh_list_lock);
+
+ cancel_work_sync(&l2_offl_work.work);
+ destroy_workqueue(rvu_sw_l2_offl_wq);
+
+ mutex_lock(&l2_offl_list_lock);
+ while (1) {
+ entry = list_first_entry_or_null(&l2_offl_lh,
+ struct l2_entry, list);
+ if (!entry)
+ break;
+
+ list_del_init(&entry->list);
+ kfree(entry);
+ }
+ mutex_unlock(&l2_offl_list_lock);
+}
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.h b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.h
new file mode 100644
index 00000000000000..6685431d60a236
--- /dev/null
+++ b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Marvell RVU Admin Function driver
+ *
+ * Copyright (C) 2026 Marvell.
+ *
+ */
+
+#ifndef RVU_SW_L2_H
+#define RVU_SW_L2_H
+
+int rvu_sw_l2_init_offl_wq(struct rvu *rvu, u16 pcifunc, bool fw_up);
+int rvu_sw_l2_fdb_list_entry_add(struct rvu *rvu, u16 pcifunc, u8 *mac);
+void rvu_sw_l2_shutdown(void);
+#endif
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
new file mode 100644
index 00000000000000..bd4ec3b20d4af9
--- /dev/null
+++ b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l3.c
@@ -0,0 +1,240 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Marvell RVU Admin Function driver
+ *
+ * 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
+
+static struct workqueue_struct *sw_l3_offl_wq;
+
+struct l3_entry {
+ struct list_head list;
+ struct rvu *rvu;
+ u32 port_id;
+ int cnt;
+ struct fib_entry entry[];
+};
+
+static DEFINE_MUTEX(l3_offl_llock);
+static LIST_HEAD(l3_offl_lh);
+static bool l3_offl_work_running;
+
+static struct workqueue_struct *sw_l3_offl_wq;
+static void sw_l3_offl_work_handler(struct work_struct *work);
+static DECLARE_DELAYED_WORK(l3_offl_work, sw_l3_offl_work_handler);
+
+static int rvu_sw_l3_offl_rule_push(struct list_head *lh)
+{
+ struct af2swdev_notify_req *req;
+ struct fib_entry *entry, *dst;
+ struct l3_entry *l3_entry;
+ struct rvu *rvu;
+ int tot_cnt = 0;
+ int swdev_pf;
+ int sz, cnt;
+ bool rc;
+
+ BUILD_BUG_ON(sizeof(*req) > 1024);
+
+ l3_entry = list_first_entry_or_null(lh, struct l3_entry, list);
+ if (!l3_entry)
+ return 0;
+
+ rvu = l3_entry->rvu;
+ swdev_pf = rvu_get_pf(rvu->pdev, rvu->rswitch.pcifunc);
+
+ mutex_lock(&rvu->mbox_lock);
+ req = otx2_mbox_alloc_msg_af2swdev_notify(rvu, swdev_pf);
+ if (!req) {
+ mutex_unlock(&rvu->mbox_lock);
+
+ while ((l3_entry =
+ list_first_entry_or_null(lh,
+ struct l3_entry, list)) != NULL) {
+ list_del_init(&l3_entry->list);
+ kfree(l3_entry);
+ }
+
+ return -ENOMEM;
+ }
+
+ dst = &req->entry[0];
+ while ((l3_entry =
+ list_first_entry_or_null(lh,
+ struct l3_entry, list)) != NULL) {
+ entry = l3_entry->entry;
+ cnt = l3_entry->cnt;
+ sz = sizeof(*entry) * cnt;
+
+ memcpy(dst, entry, sz);
+ tot_cnt += cnt;
+ dst += cnt;
+
+ list_del_init(&l3_entry->list);
+ kfree(l3_entry);
+ }
+ req->flags = FIB_CMD;
+ req->cnt = tot_cnt;
+
+ rc = otx2_mbox_wait_for_zero(&rvu->afpf_wq_info.mbox_up, swdev_pf);
+ if (rc)
+ otx2_mbox_msg_send_up(&rvu->afpf_wq_info.mbox_up, swdev_pf);
+
+ mutex_unlock(&rvu->mbox_lock);
+ return rc ? 0 : -EFAULT;
+}
+
+static atomic64_t req_cnt;
+static atomic64_t ack_cnt;
+static atomic64_t req_processed;
+static LIST_HEAD(l3_local_lh);
+static int lcnt;
+
+static void sw_l3_offl_work_handler(struct work_struct *work)
+{
+ struct l3_entry *l3_entry;
+ struct list_head l3lh;
+ u64 req, ack, proc;
+
+ INIT_LIST_HEAD(&l3lh);
+
+ mutex_lock(&l3_offl_llock);
+ while (1) {
+ l3_entry = list_first_entry_or_null(&l3_offl_lh, struct l3_entry, list);
+
+ if (!l3_entry)
+ break;
+
+ if (lcnt + l3_entry->cnt > 8 && !list_empty(&l3_local_lh)) {
+ 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);
+
+ if (rvu_sw_l3_offl_rule_push(&l3lh))
+ pr_err("%s: Error to push rules\n", __func__);
+
+ 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;
+
+ if (req->cnt > 16)
+ return -EINVAL;
+
+ sz = req->cnt * sizeof(struct fib_entry);
+
+ l3_entry = kcalloc(1, sizeof(*l3_entry) + sz, GFP_KERNEL);
+ if (!l3_entry)
+ return -ENOMEM;
+
+ l3_entry->port_id = rvu_sw_port_id(rvu, req->hdr.pcifunc);
+ l3_entry->rvu = rvu;
+ l3_entry->cnt = req->cnt;
+ INIT_LIST_HEAD(&l3_entry->list);
+ memcpy(l3_entry->entry, req->entry, sz);
+
+ mutex_lock(&l3_offl_llock);
+ list_add_tail(&l3_entry->list, &l3_offl_lh);
+ mutex_unlock(&l3_offl_llock);
+
+ if (!l3_offl_work_running) {
+ sw_l3_offl_wq = alloc_workqueue("sw_af_fib_wq", 0, 0);
+ l3_offl_work_running = true;
+ queue_delayed_work(sw_l3_offl_wq, &l3_offl_work,
+ msecs_to_jiffies(1000));
+ }
+
+ return 0;
+}
+
+void rvu_sw_l3_shutdown(void)
+{
+ struct l3_entry *entry;
+ LIST_HEAD(tlist);
+
+ if (!sw_l3_offl_wq)
+ return;
+
+ cancel_delayed_work_sync(&l3_offl_work);
+ destroy_workqueue(sw_l3_offl_wq);
+
+ mutex_lock(&l3_offl_llock);
+ while (1) {
+ entry = list_first_entry_or_null(&l3_offl_lh,
+ struct l3_entry, list);
+ if (!entry)
+ break;
+
+ list_del_init(&entry->list);
+ kfree(entry);
+ }
+ mutex_unlock(&l3_offl_llock);
+}
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l3.h b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l3.h
new file mode 100644
index 00000000000000..153f1415466deb
--- /dev/null
+++ b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l3.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Marvell RVU Admin Function driver
+ *
+ * Copyright (C) 2026 Marvell.
+ *
+ */
+
+#ifndef RVU_SW_L3_H
+#define RVU_SW_L3_H
+
+void rvu_sw_l3_shutdown(void);
+#endif
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/Makefile b/drivers/net/ethernet/marvell/octeontx2/nic/Makefile
index 883e9f4d601c5c..871a55ee379843 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/Makefile
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/Makefile
@@ -9,7 +9,18 @@ obj-$(CONFIG_RVU_ESWITCH) += rvu_rep.o
rvu_nicpf-y := otx2_pf.o otx2_common.o otx2_txrx.o otx2_ethtool.o \
otx2_flows.o otx2_tc.o cn10k.o cn20k.o otx2_dmac_flt.o \
- otx2_devlink.o qos_sq.o qos.o otx2_xsk.o
+ otx2_devlink.o qos_sq.o qos.o otx2_xsk.o \
+ switch/sw_fdb.o switch/sw_fl.o switch/sw_trace.o
+
+ifdef CONFIG_OCTEONTX_SWITCH
+rvu_nicpf-y += switch/sw_nb.o switch/sw_fib.o \
+ switch/sw_nb_v4.o
+
+ifdef CONFIG_IPV6
+rvu_nicpf-y += switch/sw_nb_v6.o
+endif
+endif
+
rvu_nicvf-y := otx2_vf.o
rvu_rep-y := rep.o
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.h b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.h
index acf259d7200883..73a98b94426b04 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.h
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.h
@@ -78,6 +78,8 @@ struct otx2_rcv_queue {
struct sg_list {
u16 num_segs;
u16 flags;
+ u16 cq_idx;
+ u16 len;
u64 skb;
u64 size[OTX2_MAX_FRAGS_IN_SQE];
u64 dma_addr[OTX2_MAX_FRAGS_IN_SQE];
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c
index b022f52c684508..4540ffacdddc03 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c
@@ -15,6 +15,7 @@
#include "otx2_ptp.h"
#include "cn10k.h"
#include "cn10k_ipsec.h"
+#include "switch/sw_nb.h"
#define DRV_NAME "rvu_nicvf"
#define DRV_STRING "Marvell RVU NIC Virtual Function Driver"
@@ -141,6 +142,22 @@ static int otx2vf_process_mbox_msg_up(struct otx2_nic *vf,
err = otx2_mbox_up_handler_cgx_link_event(
vf, (struct cgx_link_info_msg *)req, rsp);
return err;
+
+ case MBOX_MSG_AF2PF_FDB_REFRESH:
+ rsp = (struct msg_rsp *)otx2_mbox_alloc_msg(&vf->mbox.mbox_up, 0,
+ sizeof(struct msg_rsp));
+ if (!rsp)
+ return -ENOMEM;
+
+ rsp->hdr.id = MBOX_MSG_AF2PF_FDB_REFRESH;
+ rsp->hdr.sig = OTX2_MBOX_RSP_SIG;
+ rsp->hdr.pcifunc = req->pcifunc;
+ rsp->hdr.rc = 0;
+ err = otx2_mbox_up_handler_af2pf_fdb_refresh(vf,
+ (struct af2pf_fdb_refresh_req *)req,
+ rsp);
+ return err;
+
default:
otx2_reply_invalid_msg(&vf->mbox.mbox_up, 0, 0, req->id);
return -ENODEV;
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/rep.c b/drivers/net/ethernet/marvell/octeontx2/nic/rep.c
index 0f5d5642d3f7a1..e4c01ac8747713 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/rep.c
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/rep.c
@@ -15,6 +15,7 @@
#include "cn10k.h"
#include "otx2_reg.h"
#include "rep.h"
+#include "switch/sw_nb.h"
#define DRV_NAME "rvu_rep"
#define DRV_STRING "Marvell RVU Representor Driver"
@@ -399,8 +400,14 @@ static void rvu_rep_get_stats64(struct net_device *dev,
static int rvu_eswitch_config(struct otx2_nic *priv, u8 ena)
{
+#if IS_ENABLED(CONFIG_OCTEONTX_SWITCH)
+ struct net_device *netdev = priv->netdev;
+#endif
+ struct devlink_port_attrs attrs = {};
struct esw_cfg_req *req;
+ rvu_rep_devlink_set_switch_id(priv, &attrs.switch_id);
+
mutex_lock(&priv->mbox.lock);
req = otx2_mbox_alloc_msg_esw_cfg(&priv->mbox);
if (!req) {
@@ -408,8 +415,14 @@ static int rvu_eswitch_config(struct otx2_nic *priv, u8 ena)
return -ENOMEM;
}
req->ena = ena;
+ memcpy(req->switch_id, attrs.switch_id.id, attrs.switch_id.id_len);
otx2_sync_mbox_msg(&priv->mbox);
mutex_unlock(&priv->mbox.lock);
+
+#if IS_ENABLED(CONFIG_OCTEONTX_SWITCH)
+ ena ? sw_nb_register(netdev) : sw_nb_unregister(netdev);
+#endif
+
return 0;
}
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fdb.c b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fdb.c
new file mode 100644
index 00000000000000..327a6efeb52630
--- /dev/null
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fdb.c
@@ -0,0 +1,165 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Marvell RVU switch driver
+ *
+ * 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 "../otx2_reg.h"
+#include "../otx2_common.h"
+#include "../otx2_struct.h"
+#include "../cn10k.h"
+#include "sw_nb.h"
+#include "sw_fdb.h"
+
+#if !IS_ENABLED(CONFIG_OCTEONTX_SWITCH)
+
+int otx2_mbox_up_handler_af2pf_fdb_refresh(struct otx2_nic *pf,
+ struct af2pf_fdb_refresh_req *req,
+ struct msg_rsp *rsp)
+{
+ return 0;
+}
+
+#else
+
+static DEFINE_SPINLOCK(sw_fdb_llock);
+static LIST_HEAD(sw_fdb_lh);
+
+struct sw_fdb_list_entry {
+ struct list_head list;
+ u64 flags;
+ struct otx2_nic *pf;
+ u8 mac[ETH_ALEN];
+ bool add_fdb;
+};
+
+static struct workqueue_struct *sw_fdb_wq;
+static struct work_struct sw_fdb_work;
+
+static int sw_fdb_add_or_del(struct otx2_nic *pf,
+ const unsigned char *addr,
+ bool add_fdb)
+{
+ struct fdb_notify_req *req;
+ int rc;
+
+ mutex_lock(&pf->mbox.lock);
+ req = otx2_mbox_alloc_msg_fdb_notify(&pf->mbox);
+ if (!req) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ ether_addr_copy(req->mac, addr);
+ req->flags = add_fdb ? FDB_ADD : FDB_DEL;
+
+ rc = otx2_sync_mbox_msg(&pf->mbox);
+out:
+ mutex_unlock(&pf->mbox.lock);
+ return rc;
+}
+
+static void sw_fdb_wq_handler(struct work_struct *work)
+{
+ struct sw_fdb_list_entry *entry;
+ LIST_HEAD(tlist);
+
+ spin_lock(&sw_fdb_llock);
+ list_splice_init(&sw_fdb_lh, &tlist);
+ spin_unlock(&sw_fdb_llock);
+
+ while ((entry =
+ list_first_entry_or_null(&tlist,
+ struct sw_fdb_list_entry,
+ list)) != NULL) {
+ list_del_init(&entry->list);
+ if (sw_fdb_add_or_del(entry->pf, entry->mac, entry->add_fdb))
+ netdev_err(entry->pf->netdev,
+ "Error to add/del fdb %pM entry\n",
+ entry->mac);
+ dev_put(entry->pf->netdev);
+ kfree(entry);
+ }
+
+ spin_lock(&sw_fdb_llock);
+ if (!list_empty(&sw_fdb_lh))
+ queue_work(sw_fdb_wq, &sw_fdb_work);
+ spin_unlock(&sw_fdb_llock);
+}
+
+int sw_fdb_add_to_list(struct net_device *dev, u8 *mac, bool add_fdb)
+{
+ struct otx2_nic *pf = netdev_priv(dev);
+ struct sw_fdb_list_entry *entry;
+
+ entry = kcalloc(1, sizeof(*entry), GFP_ATOMIC);
+ if (!entry)
+ return -ENOMEM;
+
+ ether_addr_copy(entry->mac, mac);
+ entry->add_fdb = add_fdb;
+ entry->pf = pf;
+ dev_hold(dev);
+
+ spin_lock(&sw_fdb_llock);
+ list_add_tail(&entry->list, &sw_fdb_lh);
+ queue_work(sw_fdb_wq, &sw_fdb_work);
+ spin_unlock(&sw_fdb_llock);
+
+ return 0;
+}
+
+int sw_fdb_init(void)
+{
+ INIT_WORK(&sw_fdb_work, sw_fdb_wq_handler);
+ sw_fdb_wq = alloc_workqueue("sw_fdb_wq", 0, 0);
+ if (!sw_fdb_wq)
+ return -ENOMEM;
+
+ return 0;
+}
+
+void sw_fdb_deinit(void)
+{
+ struct sw_fdb_list_entry *entry;
+ LIST_HEAD(tlist);
+
+ cancel_work_sync(&sw_fdb_work);
+ destroy_workqueue(sw_fdb_wq);
+
+ spin_lock(&sw_fdb_llock);
+ list_splice_init(&sw_fdb_lh, &tlist);
+ spin_unlock(&sw_fdb_llock);
+
+ while ((entry =
+ list_first_entry_or_null(&tlist,
+ struct sw_fdb_list_entry,
+ list)) != NULL) {
+ list_del_init(&entry->list);
+ dev_put(entry->pf->netdev);
+ kfree(entry);
+ }
+}
+
+int otx2_mbox_up_handler_af2pf_fdb_refresh(struct otx2_nic *pf,
+ struct af2pf_fdb_refresh_req *req,
+ struct msg_rsp *rsp)
+{
+ struct switchdev_notifier_fdb_info item = {0};
+
+ item.addr = req->mac;
+ item.info.dev = pf->netdev;
+ call_switchdev_notifiers(SWITCHDEV_FDB_ADD_TO_BRIDGE,
+ item.info.dev, &item.info, NULL);
+
+ return 0;
+}
+#endif
+EXPORT_SYMBOL(otx2_mbox_up_handler_af2pf_fdb_refresh);
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fdb.h b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fdb.h
new file mode 100644
index 00000000000000..3b06a77e6b56ae
--- /dev/null
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fdb.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Marvell switch driver
+ *
+ * Copyright (C) 2026 Marvell.
+ *
+ */
+#ifndef SW_FDB_H_
+#define SW_FDB_H_
+
+int sw_fdb_add_to_list(struct net_device *dev, u8 *mac, bool add_fdb);
+void sw_fdb_deinit(void);
+int sw_fdb_init(void);
+
+#endif // SW_FDB_H
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fib.c b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fib.c
new file mode 100644
index 00000000000000..54b854aa7ffa63
--- /dev/null
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fib.c
@@ -0,0 +1,139 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Marvell RVU switch driver
+ *
+ * Copyright (C) 2026 Marvell.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <net/switchdev.h>
+#include <net/netevent.h>
+#include <net/arp.h>
+#include <net/route.h>
+
+#include "../otx2_reg.h"
+#include "../otx2_common.h"
+#include "../otx2_struct.h"
+#include "../cn10k.h"
+#include "sw_nb.h"
+#include "sw_fib.h"
+
+static DEFINE_SPINLOCK(sw_fib_llock);
+static LIST_HEAD(sw_fib_lh);
+
+static struct workqueue_struct *sw_fib_wq;
+static void sw_fib_work_handler(struct work_struct *work);
+static DECLARE_DELAYED_WORK(sw_fib_work, sw_fib_work_handler);
+
+struct sw_fib_list_entry {
+ struct list_head lh;
+ struct otx2_nic *pf;
+ int cnt;
+ struct fib_entry *entry;
+};
+
+static int sw_fib_notify(struct otx2_nic *pf,
+ int cnt,
+ struct fib_entry *entry)
+{
+ struct fib_notify_req *req;
+ int rc;
+
+ if (cnt > 16)
+ return -EINVAL;
+
+ mutex_lock(&pf->mbox.lock);
+ req = otx2_mbox_alloc_msg_fib_notify(&pf->mbox);
+ if (!req) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ req->cnt = cnt;
+ memcpy(req->entry, entry, sizeof(*entry) * cnt);
+
+ rc = otx2_sync_mbox_msg(&pf->mbox);
+out:
+ mutex_unlock(&pf->mbox.lock);
+ return rc;
+}
+
+static void sw_fib_work_handler(struct work_struct *work)
+{
+ struct sw_fib_list_entry *lentry;
+ LIST_HEAD(tlist);
+
+ spin_lock_bh(&sw_fib_llock);
+ list_splice_init(&sw_fib_lh, &tlist);
+ spin_unlock_bh(&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_bh(&sw_fib_llock);
+ if (!list_empty(&sw_fib_lh))
+ queue_delayed_work(sw_fib_wq, &sw_fib_work,
+ msecs_to_jiffies(10));
+ spin_unlock_bh(&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)
+{
+ struct sw_fib_list_entry *lentry;
+ LIST_HEAD(tlist);
+
+ cancel_delayed_work_sync(&sw_fib_work);
+ destroy_workqueue(sw_fib_wq);
+
+ spin_lock_bh(&sw_fib_llock);
+ list_splice_init(&sw_fib_lh, &tlist);
+ spin_unlock_bh(&sw_fib_llock);
+
+ while ((lentry =
+ list_first_entry_or_null(&tlist,
+ struct sw_fib_list_entry, lh)) != NULL) {
+ list_del_init(&lentry->lh);
+ kfree(lentry->entry);
+ kfree(lentry);
+ }
+}
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fib.h b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fib.h
new file mode 100644
index 00000000000000..50c4fbca81e83b
--- /dev/null
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fib.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Marvell switch driver
+ *
+ * Copyright (C) 2026 Marvell.
+ *
+ */
+#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);
+
+#endif // SW_FIB_H
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.c b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.c
new file mode 100644
index 00000000000000..3cf4c40b1059a6
--- /dev/null
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.c
@@ -0,0 +1,581 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Marvell RVU switch driver
+ *
+ * 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_trace.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);
+ dev_put(entry->pf->netdev);
+ 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);
+ dev_hold(pf->netdev);
+ 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;
+ int i;
+
+ if (!flow_action_has_entries(flow_action))
+ return -EINVAL;
+
+ flow_action_for_each(i, act, flow_action) {
+ if (used >= MANGLE_ARR_SZ) {
+ netdev_err(nic->netdev,
+ "%s: More entries than supported %u\n",
+ __func__, used);
+ return -ENOMEM;
+ }
+
+ switch (act->id) {
+ case FLOW_ACTION_REDIRECT:
+ trace_sw_act_dump(__func__, __LINE__, act->id);
+ 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:
+ trace_sw_act_dump(__func__, __LINE__, act->id);
+ err = nf_flow_table_offload_add_cb(act->ct.flow_table,
+ sw_fl_setup_ft_block_ingress_cb,
+ nic);
+ if (err != -EEXIST && err) {
+ netdev_err(nic->netdev,
+ "%s:%d Error to offload flow, err=%d\n",
+ __func__, __LINE__, err);
+ break;
+ }
+
+ *op |= BIT_ULL(FLOW_ACTION_CT);
+ break;
+
+ case FLOW_ACTION_MANGLE:
+ trace_sw_act_dump(__func__, __LINE__, act->id);
+ 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:
+ trace_sw_act_dump(__func__, __LINE__, act->id);
+ break;
+ }
+ }
+
+ tuple->mangle_cnt = used;
+
+ if (!*op) {
+ netdev_dbg(nic->netdev, "%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(struct otx2_nic *pf, __be32 dst, u16 *pcifunc,
+ struct fl_tuple *ftuple, bool is_in_dev)
+{
+ struct fib_nh_common *fib_nhc;
+ struct net_device *dev, *br;
+ struct fib_result res;
+ struct list_head *lh;
+ struct otx2_nic *nic;
+ int err;
+
+ rcu_read_lock();
+
+ err = sw_fl_get_route(&res, dst);
+ if (err) {
+ netdev_err(pf->netdev,
+ "%s:%d Failed to find route to dst %pI4\n",
+ __func__, __LINE__, &dst);
+ goto done;
+ }
+
+ if (res.fi->fib_type != RTN_UNICAST) {
+ netdev_err(pf->netdev,
+ "%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;
+ netdev_err(pf->netdev,
+ "%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)) {
+ netdev_err(pf->netdev,
+ "%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 (!dev || !sw_nb_is_valid_dev(dev)) {
+ netdev_err(pf->netdev,
+ "%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) {
+ netdev_err(nic->netdev,
+ "%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)) {
+ netdev_err(nic->netdev,
+ "%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_DIP_IPV6) |
+ BIT_ULL(NPC_SIP_IPV6) |
+ 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) {
+ trace_sw_fl_dump(__func__, __LINE__, &tuple);
+ return -EFAULT;
+ }
+
+ if (!netif_is_ovs_port(nic->netdev)) {
+ rc = sw_fl_get_pcifunc(nic, tuple.ip4src, &tuple.in_pf,
+ &tuple, true);
+ if (rc) {
+ trace_sw_fl_dump(__func__, __LINE__, &tuple);
+ return rc;
+ }
+
+ rc = sw_fl_get_pcifunc(nic, tuple.ip4dst,
+ &tuple.xmit_pf, &tuple, false);
+ if (rc) {
+ trace_sw_fl_dump(__func__, __LINE__, &tuple);
+ return rc;
+ }
+ }
+
+ trace_sw_fl_dump(__func__, __LINE__, &tuple);
+ 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)
+{
+ 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) {
+ netdev_err(nic->netdev,
+ "%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);
+ 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)
+{
+ struct sw_fl_list_entry *entry;
+ LIST_HEAD(tlist);
+
+ cancel_work_sync(&sw_fl_work);
+ destroy_workqueue(sw_fl_wq);
+
+ 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);
+ dev_put(entry->pf->netdev);
+ kfree(entry);
+ }
+}
+#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
new file mode 100644
index 00000000000000..8dd816eb17d2c9
--- /dev/null
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Marvell switch driver
+ *
+ * Copyright (C) 2026 Marvell.
+ *
+ */
+#ifndef SW_FL_H_
+#define SW_FL_H_
+
+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
new file mode 100644
index 00000000000000..61f0ed26adfdef
--- /dev/null
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.c
@@ -0,0 +1,435 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Marvell RVU switch driver
+ *
+ * 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 <linux/inetdevice.h>
+#include <net/addrconf.h>
+
+#include "../otx2_reg.h"
+#include "../otx2_common.h"
+#include "../otx2_struct.h"
+#include "../cn10k.h"
+#include "sw_nb.h"
+#include "sw_fdb.h"
+#include "sw_fib.h"
+#include "sw_fl.h"
+#include "sw_nb_v4.h"
+#include "sw_nb_v6.h"
+
+/* PF netdev for netdev_* logging when notifier info has no device */
+static struct net_device *sw_nb_pf_netdev;
+
+static const char *sw_nb_cmd2str[OTX2_CMD_MAX] = {
+ [OTX2_DEV_UP] = "OTX2_DEV_UP",
+ [OTX2_DEV_DOWN] = "OTX2_DEV_DOWN",
+ [OTX2_DEV_CHANGE] = "OTX2_DEV_CHANGE",
+ [OTX2_NEIGH_UPDATE] = "OTX2_NEIGH_UPDATE",
+ [OTX2_FIB_ENTRY_REPLACE] = "OTX2_FIB_ENTRY_REPLACE",
+ [OTX2_FIB_ENTRY_ADD] = "OTX2_FIB_ENTRY_ADD",
+ [OTX2_FIB_ENTRY_DEL] = "OTX2_FIB_ENTRY_DEL",
+ [OTX2_FIB_ENTRY_APPEND] = "OTX2_FIB_ENTRY_APPEND",
+};
+
+const char *sw_nb_get_cmd2str(int cmd)
+{
+ return sw_nb_cmd2str[cmd];
+}
+EXPORT_SYMBOL(sw_nb_get_cmd2str);
+
+bool sw_nb_is_cavium_dev(struct net_device *netdev)
+{
+ struct pci_dev *pdev;
+ struct device *dev;
+
+ dev = netdev->dev.parent;
+ if (!dev || dev->bus != &pci_bus_type)
+ return false;
+
+ pdev = to_pci_dev(dev);
+ if (pdev->vendor != PCI_VENDOR_ID_CAVIUM)
+ return false;
+
+ return true;
+}
+
+static int sw_nb_check_slaves(struct net_device *dev,
+ struct netdev_nested_priv *priv)
+{
+ int *cnt;
+
+ if (!priv->flags)
+ return 0;
+
+ priv->flags &= sw_nb_is_cavium_dev(dev);
+ if (priv->flags) {
+ cnt = priv->data;
+ (*cnt)++;
+ }
+
+ return 0;
+}
+
+bool sw_nb_is_valid_dev(struct net_device *netdev)
+{
+ struct netdev_nested_priv priv;
+ struct net_device *br;
+ int cnt = 0;
+
+ priv.flags = true;
+ priv.data = &cnt;
+
+ if (netif_is_bridge_master(netdev) || is_vlan_dev(netdev)) {
+ /* sw_nb_is_valid_dev() is invoked in notifier callback context, so
+ * rtnl_lcok is already acquired
+ */
+ netdev_walk_all_lower_dev(netdev, sw_nb_check_slaves, &priv);
+ return priv.flags && !!*(int *)priv.data;
+ }
+
+ if (netif_is_bridge_port(netdev)) {
+ br = netdev_master_upper_dev_get_rcu(netdev);
+ if (!br)
+ return false;
+
+ netdev_walk_all_lower_dev(br, sw_nb_check_slaves, &priv);
+ return priv.flags && !!*(int *)priv.data;
+ }
+
+ return sw_nb_is_cavium_dev(netdev);
+}
+
+static int sw_nb_fdb_event(struct notifier_block *unused,
+ unsigned long event, void *ptr)
+{
+ struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
+ struct switchdev_notifier_fdb_info *fdb_info = ptr;
+ int rc = 0;
+
+ if (!sw_nb_is_valid_dev(dev))
+ return NOTIFY_DONE;
+
+ switch (event) {
+ case SWITCHDEV_FDB_ADD_TO_DEVICE:
+ if (fdb_info->is_local)
+ break;
+ rc = sw_fdb_add_to_list(dev, (u8 *)fdb_info->addr, true);
+ break;
+
+ case SWITCHDEV_FDB_DEL_TO_DEVICE:
+ if (fdb_info->is_local)
+ break;
+ rc = sw_fdb_add_to_list(dev, (u8 *)fdb_info->addr, false);
+ break;
+
+ default:
+ return NOTIFY_DONE;
+ }
+
+ if (rc)
+ netdev_err(dev, "%s: Error to add to list\n", __func__);
+
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block sw_nb_fdb = {
+ .notifier_call = sw_nb_fdb_event,
+};
+
+static void __maybe_unused
+sw_nb_fib_event_dump(unsigned long event, void *ptr)
+{
+ struct fib_entry_notifier_info *fen_info = ptr;
+ struct net_device *log_dev;
+ struct fib_nh *fib_nh;
+ struct fib_info *fi;
+ int i;
+
+ fi = fen_info->fi;
+ log_dev = (fi && fi->fib_nhs) ? fi->fib_nh->fib_nh_dev : sw_nb_pf_netdev;
+ if (log_dev)
+ netdev_info(log_dev, "%s: FIB event=%lu dst=%#x dstlen=%u type=%u\n",
+ __func__, event, fen_info->dst, fen_info->dst_len,
+ fen_info->type);
+
+ if (!fi)
+ return;
+
+ fib_nh = fi->fib_nh;
+ for (i = 0; i < fi->fib_nhs; i++, fib_nh++) {
+ if (!fib_nh->fib_nh_dev)
+ continue;
+ netdev_info(fib_nh->fib_nh_dev,
+ "%s: dev=%s saddr=%#x gw=%#x\n",
+ __func__, fib_nh->fib_nh_dev->name,
+ fib_nh->nh_saddr, fib_nh->fib_nh_gw4);
+ }
+}
+
+#define SWITCH_NB_FIB_EVENT_DUMP(...) \
+ sw_nb_fib_event_dump(__VA_ARGS__)
+
+int sw_nb_fib_event_to_otx2_event(int event, struct net_device *netdev)
+{
+ switch (event) {
+ case FIB_EVENT_ENTRY_REPLACE:
+ return OTX2_FIB_ENTRY_REPLACE;
+ case FIB_EVENT_ENTRY_ADD:
+ return OTX2_FIB_ENTRY_ADD;
+ case FIB_EVENT_ENTRY_DEL:
+ return OTX2_FIB_ENTRY_DEL;
+ default:
+ break;
+ }
+
+ netdev_err(netdev, "Wrong FIB event %d\n", event);
+ return -1;
+}
+
+static int sw_nb_fib_event(struct notifier_block *nb,
+ unsigned long event, void *ptr)
+{
+ struct fib_notifier_info *info = ptr;
+
+ switch (event) {
+ case FIB_EVENT_ENTRY_REPLACE:
+ case FIB_EVENT_ENTRY_ADD:
+ case FIB_EVENT_ENTRY_DEL:
+ break;
+ default:
+ if (sw_nb_pf_netdev)
+ netdev_dbg(sw_nb_pf_netdev,
+ "%s: Won't process FIB event %lu\n",
+ __func__, event);
+ return NOTIFY_DONE;
+ }
+
+ switch (info->family) {
+ case AF_INET:
+ return sw_nb_v4_fib_event(nb, event, ptr);
+#if IS_ENABLED(CONFIG_IPV6)
+ case AF_INET6:
+ return sw_nb_v6_fib_event(nb, event, ptr);
+#endif
+ default:
+ break;
+ }
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block sw_nb_fib = {
+ .notifier_call = sw_nb_fib_event,
+};
+
+static int sw_nb_net_event(struct notifier_block *nb,
+ unsigned long event, void *ptr)
+{
+ struct neighbour *n = ptr;
+
+ if (!sw_nb_is_valid_dev(n->dev))
+ return NOTIFY_DONE;
+
+ if (event != NETEVENT_NEIGH_UPDATE)
+ return NOTIFY_DONE;
+
+ switch (n->tbl->family) {
+ case AF_INET:
+ return sw_nb_net_v4_neigh_update(nb, event, ptr);
+#if IS_ENABLED(CONFIG_IPV6)
+ case AF_INET6:
+ return sw_nb_net_v6_neigh_update(nb, event, ptr);
+#endif
+ default:
+ break;
+ }
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block sw_nb_netevent = {
+ .notifier_call = sw_nb_net_event,
+
+};
+
+int sw_nb_inetaddr_event_to_otx2_event(int event, struct net_device *netdev)
+{
+ switch (event) {
+ case NETDEV_CHANGE:
+ return OTX2_DEV_CHANGE;
+ case NETDEV_UP:
+ return OTX2_DEV_UP;
+ case NETDEV_DOWN:
+ return OTX2_DEV_DOWN;
+ default:
+ break;
+ }
+ netdev_dbg(netdev, "%s: Wrong interaddr event %d\n",
+ __func__, event);
+ return -1;
+}
+
+static struct notifier_block sw_nb_v4_inetaddr = {
+ .notifier_call = sw_nb_v4_inetaddr_event,
+};
+
+#if IS_ENABLED(CONFIG_IPV6)
+static struct notifier_block sw_nb_v6_inetaddr = {
+ .notifier_call = sw_nb_v6_inetaddr_event,
+};
+#endif
+
+static int sw_nb_netdev_event(struct notifier_block *unused,
+ unsigned long event, void *ptr)
+{
+ struct net_device *dev = netdev_notifier_info_to_dev(ptr);
+ struct in_device *idev;
+ struct inet6_dev *i6dev;
+
+ if (event != NETDEV_CHANGE &&
+ event != NETDEV_UP &&
+ event != NETDEV_DOWN) {
+ return NOTIFY_DONE;
+ }
+
+ if (!sw_nb_is_valid_dev(dev))
+ return NOTIFY_DONE;
+
+ idev = __in_dev_get_rtnl(dev);
+ if (idev)
+ sw_nb_v4_netdev_event(unused, event, ptr);
+
+ i6dev = __in6_dev_get(dev);
+ if (i6dev)
+ sw_nb_v6_netdev_event(unused, event, ptr);
+
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block sw_nb_netdev = {
+ .notifier_call = sw_nb_netdev_event,
+};
+
+int sw_nb_unregister(struct net_device *netdev)
+{
+ int err;
+
+ err = unregister_switchdev_notifier(&sw_nb_fdb);
+
+ if (err)
+ netdev_err(netdev, "Failed to unregister switchdev nb\n");
+
+ err = unregister_fib_notifier(&init_net, &sw_nb_fib);
+ if (err)
+ netdev_err(netdev, "Failed to unregister fib nb\n");
+
+ err = unregister_netevent_notifier(&sw_nb_netevent);
+ if (err)
+ netdev_err(netdev, "Failed to unregister netevent\n");
+
+ err = unregister_inetaddr_notifier(&sw_nb_v4_inetaddr);
+ if (err)
+ netdev_err(netdev, "Failed to unregister addr event\n");
+
+#if IS_ENABLED(CONFIG_IPV6)
+ err = unregister_inet6addr_notifier(&sw_nb_v6_inetaddr);
+ if (err)
+ netdev_err(netdev, "Failed to unregister addr event\n");
+#endif
+
+ err = unregister_netdevice_notifier(&sw_nb_netdev);
+ if (err)
+ netdev_err(netdev, "Failed to unregister netdev notifier\n");
+
+ sw_fl_deinit();
+ sw_fib_deinit();
+ sw_fdb_deinit();
+
+ sw_nb_pf_netdev = NULL;
+
+ return 0;
+}
+EXPORT_SYMBOL(sw_nb_unregister);
+
+int sw_nb_register(struct net_device *netdev)
+{
+ int err;
+
+ sw_nb_pf_netdev = netdev;
+
+ sw_fdb_init();
+ sw_fib_init();
+ sw_fl_init();
+
+ err = register_switchdev_notifier(&sw_nb_fdb);
+ if (err) {
+ netdev_err(netdev, "Failed to register switchdev nb\n");
+ sw_nb_pf_netdev = NULL;
+ return err;
+ }
+
+ err = register_fib_notifier(&init_net, &sw_nb_fib, NULL, NULL);
+ if (err) {
+ netdev_err(netdev, "Failed to register fb notifier block\n");
+ goto err1;
+ }
+
+ err = register_netevent_notifier(&sw_nb_netevent);
+ if (err) {
+ netdev_err(netdev, "Failed to register netevent\n");
+ goto err2;
+ }
+
+#if IS_ENABLED(CONFIG_IPV6)
+ err = register_inet6addr_notifier(&sw_nb_v6_inetaddr);
+ if (err) {
+ netdev_err(netdev, "Failed to register addr event\n");
+ goto err3;
+ }
+#endif
+
+ err = register_inetaddr_notifier(&sw_nb_v4_inetaddr);
+ if (err) {
+ netdev_err(netdev, "Failed to register addr event\n");
+ goto err4;
+ }
+
+ err = register_netdevice_notifier(&sw_nb_netdev);
+ if (err) {
+ netdev_err(netdev, "Failed to register netdevice nb\n");
+ goto err5;
+ }
+
+ return 0;
+
+err5:
+ unregister_inetaddr_notifier(&sw_nb_v4_inetaddr);
+
+err4:
+#if IS_ENABLED(CONFIG_IPV6)
+ unregister_inet6addr_notifier(&sw_nb_v6_inetaddr);
+
+err3:
+#endif
+ unregister_netevent_notifier(&sw_nb_netevent);
+
+err2:
+ unregister_fib_notifier(&init_net, &sw_nb_fib);
+
+err1:
+ unregister_switchdev_notifier(&sw_nb_fdb);
+
+ sw_fl_deinit();
+ sw_fib_deinit();
+ sw_fdb_deinit();
+
+ sw_nb_pf_netdev = NULL;
+ return err;
+}
+EXPORT_SYMBOL(sw_nb_register);
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.h b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.h
new file mode 100644
index 00000000000000..b0ce10ed25d4a8
--- /dev/null
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.h
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Marvell switch driver
+ *
+ * Copyright (C) 2026 Marvell.
+ *
+ */
+#ifndef SW_NB_H_
+#define SW_NB_H_
+
+enum {
+ OTX2_DEV_UP = 1,
+ OTX2_DEV_DOWN,
+ OTX2_DEV_CHANGE,
+ OTX2_NEIGH_UPDATE,
+ OTX2_FIB_ENTRY_REPLACE,
+ OTX2_FIB_ENTRY_ADD,
+ OTX2_FIB_ENTRY_DEL,
+ OTX2_FIB_ENTRY_APPEND,
+ OTX2_CMD_MAX,
+};
+
+int sw_nb_register(struct net_device *netdev);
+int sw_nb_unregister(struct net_device *netdev);
+bool sw_nb_is_valid_dev(struct net_device *netdev);
+
+int otx2_mbox_up_handler_af2pf_fdb_refresh(struct otx2_nic *pf,
+ struct af2pf_fdb_refresh_req *req,
+ struct msg_rsp *rsp);
+
+bool sw_nb_is_cavium_dev(struct net_device *netdev);
+int sw_nb_fib_event_to_otx2_event(int event, struct net_device *netdev);
+int sw_nb_inetaddr_event_to_otx2_event(int event, struct net_device *netdev);
+
+const char *sw_nb_get_cmd2str(int cmd);
+#endif // SW_NB_H__
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb_v4.c b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb_v4.c
new file mode 100644
index 00000000000000..8fd02edaa90ee7
--- /dev/null
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb_v4.c
@@ -0,0 +1,341 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Marvell RVU switch driver
+ *
+ * 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 <linux/inetdevice.h>
+
+#include "../otx2_reg.h"
+#include "../otx2_common.h"
+#include "../otx2_struct.h"
+#include "../cn10k.h"
+#include "sw_nb.h"
+#include "sw_fdb.h"
+#include "sw_fib.h"
+#include "sw_fl.h"
+#include "sw_nb.h"
+#include "sw_nb_v4.h"
+
+int sw_nb_v4_netdev_event(struct notifier_block *unused,
+ unsigned long event, void *ptr)
+{
+ struct net_device *dev = netdev_notifier_info_to_dev(ptr);
+ struct netdev_hw_addr *dev_addr;
+ struct net_device *pf_dev;
+ struct in_ifaddr *ifa;
+ struct fib_entry *entry;
+ struct in_device *idev;
+ struct otx2_nic *pf;
+ struct list_head *iter;
+ struct net_device *lower;
+
+ idev = __in_dev_get_rtnl(dev);
+ if (!idev || !idev->ifa_list)
+ return NOTIFY_DONE;
+
+ ifa = rtnl_dereference(idev->ifa_list);
+
+ entry = kcalloc(1, sizeof(*entry), GFP_KERNEL);
+ if (!entry)
+ return NOTIFY_DONE;
+
+ entry->cmd = sw_nb_inetaddr_event_to_otx2_event(event, dev);
+ entry->dst = (__force u32)htonl((__force u32)ifa->ifa_address);
+ entry->dst_len = 32;
+ entry->mac_valid = 1;
+ entry->host = 1;
+
+ pf_dev = dev;
+ if (netif_is_bridge_master(dev)) {
+ entry->bridge = 1;
+ netdev_for_each_lower_dev(dev, lower, iter) {
+ pf_dev = lower;
+ break;
+ }
+ } else if (is_vlan_dev(dev)) {
+ entry->vlan_valid = 1;
+ pf_dev = vlan_dev_real_dev(dev);
+ entry->vlan_tag = vlan_dev_vlan_id(dev);
+ }
+
+ pf = netdev_priv(pf_dev);
+ entry->port_id = pf->pcifunc;
+
+ for_each_dev_addr(dev, dev_addr) {
+ ether_addr_copy(entry->mac, dev_addr->addr);
+ break;
+ }
+
+ netdev_dbg(dev, "%s: pushing netdev event from HOST interface address %#x, %pM, dev=%s\n",
+ __func__, entry->dst, entry->mac, dev->name);
+ sw_fib_add_to_list(pf_dev, entry, 1);
+
+ return NOTIFY_DONE;
+}
+
+int sw_nb_v4_inetaddr_event(struct notifier_block *nb,
+ unsigned long event, void *ptr)
+{
+ struct in_ifaddr *ifa = (struct in_ifaddr *)ptr;
+ struct net_device *dev = ifa->ifa_dev->dev;
+ struct net_device *lower, *pf_dev;
+ struct netdev_hw_addr *dev_addr;
+ struct fib_entry *entry;
+ struct in_device *idev;
+ struct list_head *iter;
+ struct otx2_nic *pf;
+
+ if (event != NETDEV_CHANGE &&
+ event != NETDEV_UP &&
+ event != NETDEV_DOWN) {
+ return NOTIFY_DONE;
+ }
+
+ idev = __in_dev_get_rtnl(dev);
+ if (!idev || !idev->ifa_list)
+ return NOTIFY_DONE;
+
+ entry = kcalloc(1, sizeof(*entry), GFP_ATOMIC);
+ entry->cmd = sw_nb_inetaddr_event_to_otx2_event(event, dev);
+ entry->dst = (__force u32)htonl((__force u32)ifa->ifa_address);
+ entry->dst_len = 32;
+ entry->mac_valid = 1;
+ entry->host = 1;
+
+ pf_dev = dev;
+ if (netif_is_bridge_master(dev)) {
+ entry->bridge = 1;
+ netdev_for_each_lower_dev(dev, lower, iter) {
+ pf_dev = lower;
+ break;
+ }
+ } else if (is_vlan_dev(dev)) {
+ entry->vlan_valid = 1;
+ pf_dev = vlan_dev_real_dev(dev);
+ entry->vlan_tag = vlan_dev_vlan_id(dev);
+ }
+
+ pf = netdev_priv(pf_dev);
+ entry->port_id = pf->pcifunc;
+
+ for_each_dev_addr(dev, dev_addr) {
+ ether_addr_copy(entry->mac, dev_addr->addr);
+ break;
+ }
+
+ netdev_dbg(dev, "%s: pushing inetaddr event from HOST interface address %#x, %pM, %s\n",
+ __func__, entry->dst, entry->mac, dev->name);
+
+ sw_fib_add_to_list(pf_dev, entry, 1);
+ return NOTIFY_DONE;
+}
+
+int sw_nb_v4_fib_event(struct notifier_block *nb,
+ unsigned long event, void *ptr)
+{
+ struct fib_entry_notifier_info *fen_info = ptr;
+ struct fib_entry *entries, *iter;
+ struct net_device *dev, *pf_dev = NULL;
+ struct netdev_hw_addr *dev_addr;
+ struct net_device *lower;
+ struct list_head *lh;
+ struct neighbour *neigh;
+ struct fib_nh *fib_nh;
+ struct fib_info *fi;
+ struct otx2_nic *pf;
+ u32 *haddr;
+ int hcnt = 0;
+ int cnt, i;
+
+ /* Process only UNICAST routes add or del */
+ if (fen_info->type != RTN_UNICAST)
+ return NOTIFY_DONE;
+
+ fi = fen_info->fi;
+ if (!fi)
+ return NOTIFY_DONE;
+
+ if (fi->fib_nh_is_v6) {
+ struct net_device *log_dev = (fi->fib_nhs > 0) ?
+ fi->fib_nh->fib_nh_dev : NULL;
+
+ if (log_dev)
+ netdev_dbg(log_dev, "%s: Received v6 notification\n",
+ __func__);
+ return NOTIFY_DONE;
+ }
+
+ entries = kcalloc(fi->fib_nhs, sizeof(*entries), GFP_ATOMIC);
+ if (!entries)
+ return NOTIFY_DONE;
+
+ haddr = kcalloc(fi->fib_nhs, sizeof(u32), GFP_ATOMIC);
+ if (!haddr) {
+ kfree(entries);
+ return NOTIFY_DONE;
+ }
+
+ iter = entries;
+ fib_nh = fi->fib_nh;
+ for (i = 0; i < fi->fib_nhs; i++, fib_nh++) {
+ dev = fib_nh->fib_nh_dev;
+
+ if (!dev)
+ continue;
+
+ if (dev->type != ARPHRD_ETHER)
+ continue;
+
+ if (!sw_nb_is_valid_dev(dev))
+ continue;
+
+ iter->cmd = sw_nb_fib_event_to_otx2_event(event, dev);
+ iter->dst = fen_info->dst;
+ iter->dst_len = fen_info->dst_len;
+ iter->gw = (__force u32)htonl((__force u32)fib_nh->fib_nh_gw4);
+
+ netdev_dbg(dev, "%s: FIB route Rule cmd=%lld dst=%#x dst_len=%d gw=%#x\n",
+ __func__, iter->cmd, iter->dst, iter->dst_len, iter->gw);
+
+ pf_dev = dev;
+ if (netif_is_bridge_master(dev)) {
+ iter->bridge = 1;
+ netdev_for_each_lower_dev(dev, lower, lh) {
+ pf_dev = lower;
+ break;
+ }
+ } else if (is_vlan_dev(dev)) {
+ iter->vlan_valid = 1;
+ pf_dev = vlan_dev_real_dev(dev);
+ iter->vlan_tag = vlan_dev_vlan_id(dev);
+ }
+
+ pf = netdev_priv(pf_dev);
+ iter->port_id = pf->pcifunc;
+
+ if (!fib_nh->fib_nh_gw4) {
+ if (iter->dst || iter->dst_len)
+ iter++;
+
+ continue;
+ }
+ iter->gw_valid = 1;
+
+ if (fib_nh->nh_saddr)
+ haddr[hcnt++] = (__force u32)fib_nh->nh_saddr;
+
+ rcu_read_lock();
+ neigh = ip_neigh_gw4(fib_nh->fib_nh_dev, fib_nh->fib_nh_gw4);
+ if (!neigh) {
+ rcu_read_unlock();
+ iter++;
+ continue;
+ }
+
+ if (is_valid_ether_addr(neigh->ha)) {
+ iter->mac_valid = 1;
+ ether_addr_copy(iter->mac, neigh->ha);
+ }
+
+ iter++;
+ rcu_read_unlock();
+ }
+
+ cnt = iter - entries;
+ if (!cnt) {
+ kfree(entries);
+ kfree(haddr);
+ return NOTIFY_DONE;
+ }
+
+ netdev_dbg(pf_dev, "pf_dev is %s cnt=%d\n", pf_dev->name, cnt);
+
+ sw_fib_add_to_list(pf_dev, entries, cnt);
+
+ if (!hcnt) {
+ kfree(haddr);
+ return NOTIFY_DONE;
+ }
+
+ entries = kcalloc(hcnt, sizeof(*entries), GFP_ATOMIC);
+ if (!entries) {
+ kfree(haddr);
+ return NOTIFY_DONE;
+ }
+
+ iter = entries;
+
+ for (i = 0; i < hcnt; i++, iter++) {
+ iter->cmd = sw_nb_fib_event_to_otx2_event(event, pf_dev);
+ iter->dst = (__force u32)htonl(haddr[i]);
+ iter->dst_len = 32;
+ iter->mac_valid = 1;
+ iter->host = 1;
+ iter->port_id = pf->pcifunc;
+
+ for_each_dev_addr(pf_dev, dev_addr) {
+ ether_addr_copy(iter->mac, dev_addr->addr);
+ break;
+ }
+
+ netdev_dbg(pf_dev, "%s: FIB host Rule cmd=%lld dst=%#x dst_len=%d gw=%#x %s\n",
+ __func__, iter->cmd, iter->dst, iter->dst_len, iter->gw, pf_dev->name);
+ }
+ sw_fib_add_to_list(pf_dev, entries, hcnt);
+ kfree(haddr);
+ return NOTIFY_DONE;
+}
+
+int sw_nb_net_v4_neigh_update(struct notifier_block *nb,
+ unsigned long event, void *ptr)
+{
+ struct net_device *lower, *pf_dev;
+ struct neighbour *n = ptr;
+ struct fib_entry *entry;
+ struct list_head *iter;
+ struct otx2_nic *pf;
+
+ if (n->tbl != &arp_tbl)
+ return NOTIFY_DONE;
+
+ entry = kcalloc(1, sizeof(*entry), GFP_ATOMIC);
+ entry->cmd = OTX2_NEIGH_UPDATE;
+ entry->dst = (__force u32)htonl(*(u32 *)n->primary_key);
+ entry->dst_len = n->tbl->key_len * 8;
+ entry->mac_valid = 1;
+ entry->nud_state = n->nud_state;
+ neigh_ha_snapshot(entry->mac, n, n->dev);
+ ether_addr_copy(entry->mac, n->ha);
+
+ pf_dev = n->dev;
+
+ rcu_read_lock();
+ if (netif_is_bridge_master(n->dev)) {
+ entry->bridge = 1;
+ /* Get first lower device thru which we can send MBOX to AF */
+ netdev_for_each_lower_dev(n->dev, lower, iter) {
+ pf_dev = lower;
+ break;
+ }
+ } else if (is_vlan_dev(n->dev)) {
+ entry->vlan_valid = 1;
+ pf_dev = vlan_dev_real_dev(n->dev);
+ entry->vlan_tag = vlan_dev_vlan_id(n->dev);
+ }
+
+ pf = netdev_priv(pf_dev);
+ entry->port_id = pf->pcifunc;
+
+ sw_fib_add_to_list(pf_dev, entry, 1);
+ rcu_read_unlock();
+
+ return NOTIFY_DONE;
+}
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb_v4.h b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb_v4.h
new file mode 100644
index 00000000000000..c6dbf4b93a9a2e
--- /dev/null
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb_v4.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Marvell switch driver
+ *
+ * Copyright (C) 2026 Marvell.
+ *
+ */
+#ifndef SW_NB_V4_H_
+#define SW_NB_V4_H_
+
+int sw_nb_v4_fib_event(struct notifier_block *nb,
+ unsigned long event, void *ptr);
+
+int sw_nb_net_v4_neigh_update(struct notifier_block *nb,
+ unsigned long event, void *ptr);
+
+int sw_nb_v4_inetaddr_event(struct notifier_block *nb,
+ unsigned long event, void *ptr);
+
+int sw_nb_v4_netdev_event(struct notifier_block *unused,
+ unsigned long event, void *ptr);
+#endif // SW_NB_V4_H__
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb_v6.c b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb_v6.c
new file mode 100644
index 00000000000000..49fd6bdf3df4bd
--- /dev/null
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb_v6.c
@@ -0,0 +1,248 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Marvell RVU switch driver
+ *
+ * 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 <linux/inetdevice.h>
+#include <net/addrconf.h>
+#include <net/ip6_fib.h>
+#include <net/nexthop.h>
+
+#include "../otx2_reg.h"
+#include "../otx2_common.h"
+#include "../otx2_struct.h"
+#include "../cn10k.h"
+#include "sw_nb.h"
+#include "sw_fdb.h"
+#include "sw_fib.h"
+#include "sw_fl.h"
+#include "sw_nb.h"
+#include "sw_nb_v6.h"
+
+#if IS_ENABLED(CONFIG_IPV6)
+
+int sw_nb_v6_netdev_event(struct notifier_block *unused,
+ unsigned long event, void *ptr)
+{
+ struct net_device *dev = netdev_notifier_info_to_dev(ptr);
+ struct netdev_hw_addr *dev_addr;
+ struct inet6_ifaddr *ifp;
+ struct fib_entry *entry;
+ struct inet6_dev *i6dev;
+ struct otx2_nic *pf;
+
+ i6dev = __in6_dev_get(dev);
+
+ rcu_read_lock();
+ ifp = list_first_entry_or_null(&i6dev->addr_list,
+ struct inet6_ifaddr, if_list);
+ if (!ifp) {
+ rcu_read_unlock();
+ return NOTIFY_DONE;
+ }
+
+ if (ipv6_addr_type(&ifp->addr) & IPV6_ADDR_LINKLOCAL) {
+ rcu_read_unlock();
+ return NOTIFY_DONE;
+ }
+
+ pf = netdev_priv(dev);
+
+ entry = kcalloc(1, sizeof(*entry), GFP_KERNEL);
+ entry->cmd = sw_nb_inetaddr_event_to_otx2_event(event, dev);
+ memcpy(entry->dst6, &ifp->addr, sizeof(entry->dst6));
+ entry->dst6_plen = ifp->prefix_len;
+ entry->host = 1;
+ entry->ipv6 = 1;
+ entry->port_id = pf->pcifunc;
+
+ for_each_dev_addr(dev, dev_addr) {
+ entry->mac_valid = 1;
+ ether_addr_copy(entry->mac, dev_addr->addr);
+ break;
+ }
+
+ netdev_dbg(dev, "netdev event %pM plen=%u mac=%pM\n",
+ &ifp->addr, ifp->prefix_len, entry->mac);
+ sw_fib_add_to_list(dev, entry, 1);
+ rcu_read_unlock();
+ return NOTIFY_DONE;
+}
+
+int sw_nb_v6_fib_event(struct notifier_block *nb,
+ unsigned long event, void *ptr)
+{
+ struct fib6_entry_notifier_info *f6_eni;
+ struct fib_notifier_info *info = ptr;
+ struct net_device *fib_dev;
+ struct fib_entry *entry;
+ struct fib6_info *f6i;
+ struct neighbour *neigh;
+ struct fib6_nh *nh6;
+ struct otx2_nic *pf;
+ struct rt6key *key;
+
+ f6_eni = container_of(info, struct fib6_entry_notifier_info, info);
+ f6i = f6_eni->rt;
+
+ fib_dev = fib6_info_nh_dev(f6i);
+
+ if (!fib_dev)
+ return NOTIFY_DONE;
+
+ if (fib_dev->type != ARPHRD_ETHER)
+ return NOTIFY_DONE;
+
+ if (!sw_nb_is_cavium_dev(fib_dev))
+ return NOTIFY_DONE;
+
+ if (f6i->fib6_type != RTN_UNICAST)
+ return NOTIFY_DONE;
+
+ key = &f6i->fib6_dst;
+ /* TODO: vlan and bridge support */
+ if (ipv6_addr_type(&key->addr) & IPV6_ADDR_LINKLOCAL)
+ return NOTIFY_DONE;
+
+ netdev_dbg(fib_dev, "fib6dst rt6key.addr=%pI6c len=%u\n", &key->addr,
+ key->plen);
+
+ netdev_dbg(fib_dev, "fib6flags=%#x proto=%u type=%u\n",
+ f6i->fib6_flags, f6i->fib6_protocol, f6i->fib6_type);
+
+ nh6 = f6i->nh ? nexthop_fib6_nh(f6i->nh) : f6i->fib6_nh;
+ netdev_dbg(nh6->fib_nh_dev ? nh6->fib_nh_dev : fib_dev,
+ "nh family=%u dev=%s gw=%pI6c gwfamily=%u\n",
+ nh6->fib_nh_family,
+ nh6->fib_nh_dev ? nh6->fib_nh_dev->name : "No dev",
+ &nh6->fib_nh_gw6, nh6->fib_nh_gw_family);
+
+ pf = netdev_priv(fib_dev);
+
+ entry = kcalloc(1, sizeof(*entry), GFP_ATOMIC);
+ if (!entry)
+ return NOTIFY_DONE;
+
+ entry->cmd = sw_nb_fib_event_to_otx2_event(event, fib_dev);
+ entry->ipv6 = 1;
+ entry->port_id = pf->pcifunc;
+ memcpy(entry->dst6, &key->addr, sizeof(entry->dst6));
+ entry->dst6_plen = key->plen;
+
+ memcpy(entry->gw6, &nh6->fib_nh_gw6, sizeof(nh6->fib_nh_gw6));
+ entry->gw_valid = !!(ipv6_addr_type(&nh6->fib_nh_gw6) & IPV6_ADDR_UNICAST);
+
+ rcu_read_lock();
+ neigh = ip_neigh_gw6(fib_dev, &nh6->fib_nh_gw6);
+ if (!neigh) {
+ rcu_read_unlock();
+ kfree(entry);
+ return NOTIFY_DONE;
+ }
+
+ if (is_valid_ether_addr(neigh->ha)) {
+ entry->mac_valid = 1;
+ ether_addr_copy(entry->mac, neigh->ha);
+ netdev_dbg(fib_dev, "fib found MAC=%pM\n", entry->mac);
+ }
+
+ sw_fib_add_to_list(fib_dev, entry, 1);
+ rcu_read_unlock();
+
+ return NOTIFY_DONE;
+}
+
+int sw_nb_net_v6_neigh_update(struct notifier_block *nb,
+ unsigned long event, void *ptr)
+{
+ struct neighbour *n = ptr;
+ struct fib_entry *entry;
+ struct net_device *pf_dev;
+ struct otx2_nic *pf;
+
+ if (n->tbl != &nd_tbl)
+ return NOTIFY_DONE;
+
+ if (ipv6_addr_type((struct in6_addr *)n->primary_key) & IPV6_ADDR_LINKLOCAL)
+ return NOTIFY_DONE;
+
+ pf_dev = n->dev;
+ pf = netdev_priv(pf_dev);
+
+ entry = kcalloc(1, sizeof(*entry), GFP_ATOMIC);
+ entry->cmd = OTX2_NEIGH_UPDATE;
+
+ entry->dst6_plen = n->tbl->key_len * 8;
+ memcpy(entry->dst6, (struct in6_addr *)n->primary_key,
+ sizeof(entry->dst6));
+ entry->ipv6 = 1;
+ entry->nud_state = n->nud_state;
+ ether_addr_copy(entry->mac, n->ha);
+ entry->mac_valid = 1;
+ entry->port_id = pf->pcifunc;
+
+ sw_fib_add_to_list(pf_dev, entry, 1);
+
+ netdev_dbg(n->dev, "v6 neigh update %pI6 mac=%pM plen=%u\n",
+ n->primary_key, n->ha, n->tbl->key_len * 8);
+
+ return NOTIFY_DONE;
+}
+
+int sw_nb_v6_inetaddr_event(struct notifier_block *nb,
+ unsigned long event, void *ptr)
+{
+ struct inet6_ifaddr *ifa6 = (struct inet6_ifaddr *)ptr;
+ struct net_device *dev = ifa6->idev->dev;
+ struct netdev_hw_addr *dev_addr;
+ struct fib_entry *entry;
+ struct otx2_nic *pf;
+
+ if (event != NETDEV_CHANGE &&
+ event != NETDEV_UP &&
+ event != NETDEV_DOWN) {
+ return NOTIFY_DONE;
+ }
+
+ if (dev->type != ARPHRD_ETHER)
+ return NOTIFY_DONE;
+
+ if (!sw_nb_is_cavium_dev(dev))
+ return NOTIFY_DONE;
+
+ if (ipv6_addr_type(&ifa6->addr) & IPV6_ADDR_LINKLOCAL)
+ return NOTIFY_DONE;
+
+ pf = netdev_priv(dev);
+
+ entry = kcalloc(1, sizeof(*entry), GFP_ATOMIC);
+ entry->cmd = sw_nb_inetaddr_event_to_otx2_event(event, dev);
+ memcpy(entry->dst6, &ifa6->addr, sizeof(entry->dst6));
+ entry->dst6_plen = ifa6->prefix_len;
+ entry->mac_valid = 1;
+ entry->host = 1;
+ entry->ipv6 = 1;
+ entry->port_id = pf->pcifunc;
+
+ for_each_dev_addr(dev, dev_addr) {
+ ether_addr_copy(entry->mac, dev_addr->addr);
+ entry->mac_valid = 1;
+ break;
+ }
+
+ sw_fib_add_to_list(dev, entry, 1);
+
+ netdev_dbg(dev, "inetaddr addr=%pI6c len=%u %pM\n",
+ &ifa6->addr, ifa6->prefix_len, entry->mac);
+
+ return NOTIFY_DONE;
+}
+#endif
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb_v6.h b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb_v6.h
new file mode 100644
index 00000000000000..f73efc98c311ca
--- /dev/null
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb_v6.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Marvell switch driver
+ *
+ * Copyright (C) 2026 Marvell.
+ *
+ */
+#ifndef SW_NB_V6_H_
+#define SW_NB_V6_H_
+
+int sw_nb_v6_fib_event(struct notifier_block *nb,
+ unsigned long event, void *ptr);
+
+int sw_nb_net_v6_neigh_update(struct notifier_block *nb,
+ unsigned long event, void *ptr);
+
+int sw_nb_v6_inetaddr_event(struct notifier_block *nb,
+ unsigned long event, void *ptr);
+
+int sw_nb_v6_netdev_event(struct notifier_block *unused,
+ unsigned long event, void *ptr);
+#endif // SW_NB_V6_H__
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_trace.c b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_trace.c
new file mode 100644
index 00000000000000..b01e7780ef127d
--- /dev/null
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_trace.c
@@ -0,0 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Marvell RVU Admin Function driver
+ *
+ * Copyright (C) 2026 Marvell.
+ *
+ */
+
+#define CREATE_TRACE_POINTS
+#if IS_ENABLED(CONFIG_OCTEONTX_SWITCH)
+#include "sw_trace.h"
+EXPORT_TRACEPOINT_SYMBOL(sw_fl_dump);
+EXPORT_TRACEPOINT_SYMBOL(sw_act_dump);
+#endif
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_trace.h b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_trace.h
new file mode 100644
index 00000000000000..5949e3dafaedd6
--- /dev/null
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_trace.h
@@ -0,0 +1,82 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Marvell RVU Admin Function driver
+ *
+ * Copyright (C) 2026 Marvell.
+ *
+ */
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM rvu
+
+#if !defined(SW_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
+#define SW_TRACE_H
+
+#include <linux/types.h>
+#include <linux/tracepoint.h>
+
+#include "mbox.h"
+
+TRACE_EVENT(sw_fl_dump,
+ TP_PROTO(const char *fname, int line, struct fl_tuple *ftuple),
+ TP_ARGS(fname, line, ftuple),
+ TP_STRUCT__entry(__string(f, fname)
+ __field(int, l)
+ __array(u8, smac, ETH_ALEN)
+ __array(u8, dmac, ETH_ALEN)
+ __field(u16, eth_type)
+ __field(u32, sip)
+ __field(u32, dip)
+ __field(u8, ip_proto)
+ __field(u16, sport)
+ __field(u16, dport)
+ __field(u8, uni_di)
+ __field(u16, in_pf)
+ __field(u16, out_pf)
+ ),
+ TP_fast_assign(__assign_str(f);
+ __entry->l = line;
+ memcpy(__entry->smac, ftuple->smac, ETH_ALEN);
+ memcpy(__entry->dmac, ftuple->dmac, ETH_ALEN);
+ __entry->sip = (__force u32)(ftuple->ip4src);
+ __entry->dip = (__force u32)(ftuple->ip4dst);
+ __entry->eth_type = (__force u16)ftuple->eth_type;
+ __entry->ip_proto = ftuple->proto;
+ __entry->sport = (__force u16)(ftuple->sport);
+ __entry->dport = (__force u16)(ftuple->dport);
+ __entry->uni_di = ftuple->uni_di;
+ __entry->in_pf = ftuple->in_pf;
+ __entry->out_pf = ftuple->xmit_pf;
+ ),
+ TP_printk("[%s:%d] %pM %pI4:%u to %pM %pI4:%u eth_type=%#x proto=%u uni=%u in=%#x out=%#x",
+ __get_str(f), __entry->l, __entry->smac, &__entry->sip, __entry->sport,
+ __entry->dmac, &__entry->dip, __entry->dport,
+ ntohs((__force __be16)__entry->eth_type), __entry->ip_proto, __entry->uni_di,
+ __entry->in_pf, __entry->out_pf)
+);
+
+TRACE_EVENT(sw_act_dump,
+ TP_PROTO(const char *fname, int line, u32 act),
+ TP_ARGS(fname, line, act),
+ TP_STRUCT__entry(__string(fname, fname)
+ __field(int, line)
+ __field(u32, act)
+ ),
+
+ TP_fast_assign(__assign_str(fname);
+ __entry->line = line;
+ __entry->act = act;
+ ),
+
+ TP_printk("[%s:%d] %u",
+ __get_str(fname), __entry->line, __entry->act)
+);
+
+#endif
+
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH ../../drivers/net/ethernet/marvell/octeontx2/nic/switch/
+
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE sw_trace
+
+#include <trace/define_trace.h>
|
| 3/2 | 2026/07/02 11:16 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 52234 output: 20 thoughts: 436 Thoughts: **Analyzing the Reachability of the OcteonTX2 Switch Driver** Okay, so I've got this patch adding support for Marvell's OcteonTX2 switch driver. It's making changes to the `Kconfig` file to include `OCTEONTX_SWITCH`, and a whole slew of new files under the `af/switch/` and `nic/switch/` directories within the OcteonTX2 driver structure. It also touches a few existing files like `rvu.c`, `rvu_nix.c`, `rvu_npc.c`, etc. The question now is, can I actually *reach* this new code in a virtualized environment like syzkaller? The driver is for the Marvell OcteonTX2, which is, at its core, a hardware device. Typically, hardware drivers are inaccessible unless you've got an emulator, like QEMU, or it's designed as a virtual driver itself. I know syzkaller *doesn't* have an emulator specifically for the OcteonTX2. So, am I out of luck? It's a PCI driver, so maybe there's some possibility there. Let's look at `rvu.c` and `otx2_pf.c`. Ah, wait, this patch introduces `OCTEONTX_SWITCH`, but that's dependent on `OCTEONTX2_PF`. Okay, can *that* be fuzzed? That's the key question. We need to figure out if `OCTEONTX2_PF` has some software emulation aspect to it or if it's purely hardware-dependent. If it's the latter, we can't utilize this with syzkaller. Time to search the kernel for `OCTEONTX2_PF` and `rvu_af` to dig deeper. Let's see what we can find. |
| 4/2 | 2026/07/02 11:16 | tool | grepper |
0mArgs:{
"Expression": "\"rvu_af\""
}Results: {
"Output": "drivers/net/ethernet/marvell/octeontx2/af/rvu.c-27-\ndrivers/net/ethernet/marvell/octeontx2/af/rvu.c:28:#define DRV_NAME\t\"rvu_af\"\ndrivers/net/ethernet/marvell/octeontx2/af/rvu.c-29-#define DRV_STRING \"Marvell OcteonTX2 RVU Admin Function Driver\"\n"
} |
| 5/2 | 2026/07/02 11:16 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 53226 output: 45 thoughts: 11 Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience: **Analyzing PCI ID Compatibility** Okay, so I need to get a handle on this driver's compatibility. My first step is to dive into the PCI IDs. I'm going to pull up the relevant documentation or the source code, depending on what's available. I need to make sure I'm looking at the correct version, of course. Are there any known issues with specific revisions of the hardware? I'll probably start with a grep or some similar search functionality to locate those IDs. I'm looking for the Vendor ID and Device ID, and maybe Subsystem Vendor and Device IDs if we're dealing with variants or specialized hardware configurations. Once I have the complete list, I'll cross-reference them with my internal knowledge of similar devices, and also, of course, with any publicly available databases like the PCI ID repository. I'll need to watch out for wildcard entries, and any unusual combinations. Is there anything unusual about the IDs? Any proprietary ranges that raise a red flag? I want to make sure I'm not missing anything critical from a compatibility standpoint before proceeding further. |
| 6/2 | 2026/07/02 11:16 | tool | grepper |
0mArgs:{
"Expression": "PCI_VENDOR_ID_CAVIUM",
"PathPrefix": "drivers/net/ethernet/marvell/octeontx2/"
}Results: {
"Output": "drivers/net/ethernet/marvell/octeontx2/af/cgx.c=66=static const struct pci_device_id cgx_id_table[] = {\ndrivers/net/ethernet/marvell/octeontx2/af/cgx.c:67:\t{ PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_CGX) },\ndrivers/net/ethernet/marvell/octeontx2/af/cgx.c:68:\t{ PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_CN10K_RPM,\ndrivers/net/ethernet/marvell/octeontx2/af/cgx.c-69-\t PCI_ANY_ID, PCI_SUBSYS_DEVID_CN10K_A) },\ndrivers/net/ethernet/marvell/octeontx2/af/cgx.c:70:\t{ PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_CN10K_RPM,\ndrivers/net/ethernet/marvell/octeontx2/af/cgx.c-71-\t PCI_ANY_ID, PCI_SUBSYS_DEVID_CNF10K_A) },\ndrivers/net/ethernet/marvell/octeontx2/af/cgx.c:72:\t{ PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_CN10K_RPM,\ndrivers/net/ethernet/marvell/octeontx2/af/cgx.c-73-\t PCI_ANY_ID, PCI_SUBSYS_DEVID_CNF10K_B) },\ndrivers/net/ethernet/marvell/octeontx2/af/cgx.c:74:\t{ PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_CN10KB_RPM,\ndrivers/net/ethernet/marvell/octeontx2/af/cgx.c-75-\t PCI_ANY_ID, PCI_SUBSYS_DEVID_CN10K_B) },\ndrivers/net/ethernet/marvell/octeontx2/af/cgx.c:76:\t{ PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_CN10KB_RPM,\ndrivers/net/ethernet/marvell/octeontx2/af/cgx.c-77-\t PCI_ANY_ID, PCI_SUBSYS_DEVID_CN20KA) },\ndrivers/net/ethernet/marvell/octeontx2/af/cgx.c:78:\t{ PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_CN10KB_RPM,\ndrivers/net/ethernet/marvell/octeontx2/af/cgx.c-79-\t PCI_ANY_ID, PCI_SUBSYS_DEVID_CNF20KA) },\n--\ndrivers/net/ethernet/marvell/octeontx2/af/mcs.c=20=static const struct pci_device_id mcs_id_table[] = {\ndrivers/net/ethernet/marvell/octeontx2/af/mcs.c:21:\t{ PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_CN10K_MCS) },\ndrivers/net/ethernet/marvell/octeontx2/af/mcs.c-22-\t{ 0, } /* end of table */\n--\ndrivers/net/ethernet/marvell/octeontx2/af/ptp.c=589=static const struct pci_device_id ptp_id_table[] = {\ndrivers/net/ethernet/marvell/octeontx2/af/ptp.c:590:\t{ PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_PTP,\ndrivers/net/ethernet/marvell/octeontx2/af/ptp.c:591:\t\t\t PCI_VENDOR_ID_CAVIUM,\ndrivers/net/ethernet/marvell/octeontx2/af/ptp.c-592-\t\t\t PCI_SUBSYS_DEVID_OCTX2_98xx_PTP) },\ndrivers/net/ethernet/marvell/octeontx2/af/ptp.c:593:\t{ PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_PTP,\ndrivers/net/ethernet/marvell/octeontx2/af/ptp.c:594:\t\t\t PCI_VENDOR_ID_CAVIUM,\ndrivers/net/ethernet/marvell/octeontx2/af/ptp.c-595-\t\t\t PCI_SUBSYS_DEVID_OCTX2_96XX_PTP) },\ndrivers/net/ethernet/marvell/octeontx2/af/ptp.c:596:\t{ PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_PTP,\ndrivers/net/ethernet/marvell/octeontx2/af/ptp.c:597:\t\t\t PCI_VENDOR_ID_CAVIUM,\ndrivers/net/ethernet/marvell/octeontx2/af/ptp.c-598-\t\t\t PCI_SUBSYS_DEVID_OCTX2_95XX_PTP) },\ndrivers/net/ethernet/marvell/octeontx2/af/ptp.c:599:\t{ PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_PTP,\ndrivers/net/ethernet/marvell/octeontx2/af/ptp.c:600:\t\t\t PCI_VENDOR_ID_CAVIUM,\ndrivers/net/ethernet/marvell/octeontx2/af/ptp.c-601-\t\t\t PCI_SUBSYS_DEVID_OCTX2_95XXN_PTP) },\ndrivers/net/ethernet/marvell/octeontx2/af/ptp.c:602:\t{ PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_PTP,\ndrivers/net/ethernet/marvell/octeontx2/af/ptp.c:603:\t\t\t PCI_VENDOR_ID_CAVIUM,\ndrivers/net/ethernet/marvell/octeontx2/af/ptp.c-604-\t\t\t PCI_SUBSYS_DEVID_OCTX2_95MM_PTP) },\ndrivers/net/ethernet/marvell/octeontx2/af/ptp.c:605:\t{ PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_PTP,\ndrivers/net/ethernet/marvell/octeontx2/af/ptp.c:606:\t\t\t PCI_VENDOR_ID_CAVIUM,\ndrivers/net/ethernet/marvell/octeontx2/af/ptp.c-607-\t\t\t PCI_SUBSYS_DEVID_OCTX2_95XXO_PTP) },\ndrivers/net/ethernet/marvell/octeontx2/af/ptp.c:608:\t{ PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_CN10K_PTP) },\ndrivers/net/ethernet/marvell/octeontx2/af/ptp.c-609-\t{ 0, }\n--\ndrivers/net/ethernet/marvell/octeontx2/af/rvu.c=45=static const struct pci_device_id rvu_id_table[] = {\ndrivers/net/ethernet/marvell/octeontx2/af/rvu.c:46:\t{ PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_RVU_AF) },\ndrivers/net/ethernet/marvell/octeontx2/af/rvu.c-47-\t{ 0, } /* end of table */\n--\ndrivers/net/ethernet/marvell/octeontx2/af/rvu.c=928=static void rvu_get_lbk_bufsize(struct rvu *rvu)\n--\ndrivers/net/ethernet/marvell/octeontx2/af/rvu.c-933-\ndrivers/net/ethernet/marvell/octeontx2/af/rvu.c:934:\tpdev = pci_get_device(PCI_VENDOR_ID_CAVIUM,\ndrivers/net/ethernet/marvell/octeontx2/af/rvu.c-935-\t\t\t PCI_DEVID_OCTEONTX2_LBK, pdev);\n--\ndrivers/net/ethernet/marvell/octeontx2/af/rvu.c=3560=int rvu_get_num_lbk_chans(void)\n--\ndrivers/net/ethernet/marvell/octeontx2/af/rvu.c-3565-\ndrivers/net/ethernet/marvell/octeontx2/af/rvu.c:3566:\tpdev = pci_get_device(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_LBK,\ndrivers/net/ethernet/marvell/octeontx2/af/rvu.c-3567-\t\t\t NULL);\n--\ndrivers/net/ethernet/marvell/octeontx2/af/rvu_cn10k.c=392=static void rvu_lbk_set_channels(struct rvu *rvu)\n--\ndrivers/net/ethernet/marvell/octeontx2/af/rvu_cn10k.c-414-\twhile (true) {\ndrivers/net/ethernet/marvell/octeontx2/af/rvu_cn10k.c:415:\t\tpdev = pci_get_device(PCI_VENDOR_ID_CAVIUM,\ndrivers/net/ethernet/marvell/octeontx2/af/rvu_cn10k.c-416-\t\t\t\t PCI_DEVID_OCTEONTX2_LBK, pdev);\n--\ndrivers/net/ethernet/marvell/octeontx2/af/rvu_sdp.c=53=int rvu_sdp_init(struct rvu *rvu)\n--\ndrivers/net/ethernet/marvell/octeontx2/af/rvu_sdp.c-66-\ndrivers/net/ethernet/marvell/octeontx2/af/rvu_sdp.c:67:\twhile ((i \u003c MAX_SDP) \u0026\u0026 (pdev = pci_get_device(PCI_VENDOR_ID_CAVIUM,\ndrivers/net/ethernet/marvell/octeontx2/af/rvu_sdp.c-68-\t\t\t\t\t\t PCI_DEVID_OTX2_SDP_PF,\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c=36=static const struct pci_device_id otx2_pf_id_table[] = {\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c:37:\t{ PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_RVU_PF) },\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c-38-\t{ 0, } /* end of table */\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c=23=static const struct pci_device_id otx2_vf_id_table[] = {\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c:24:\t{ PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_RVU_AFVF) },\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c:25:\t{ PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_RVU_VF) },\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c:26:\t{ PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_SDP_REP) },\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c-27-\t{ }\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/rep.c=23=static const struct pci_device_id rvu_rep_id_table[] = {\ndrivers/net/ethernet/marvell/octeontx2/nic/rep.c:24:\t{ PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_RVU_REP) },\ndrivers/net/ethernet/marvell/octeontx2/nic/rep.c-25-\t{ }\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.c=48=bool sw_nb_is_cavium_dev(struct net_device *netdev)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.c-57-\tpdev = to_pci_dev(dev);\ndrivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.c:58:\tif (pdev-\u003evendor != PCI_VENDOR_ID_CAVIUM)\ndrivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.c-59-\t\treturn false;\n"
} |
| 7/2 | 2026/07/02 11:16 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 57088 output: 54 thoughts: 460 Thoughts: **Analyzing the Patch's Testability with Syzkaller** Okay, so this is a PCI driver specifically for OcteonTX2 hardware. No software emulation, which immediately raises a flag. Can this even be meaningfully tested without the actual hardware? My initial thought is that it's going to be tricky. I know syzkaller can fuzz PCI drivers, injecting devices using `syz_pci_device_add` or similar functions, but usually, that requires *specific* hardware. However, I remember syzkaller *does* have a virtual PCI device injector (vhci)! Okay, let's see if this driver can be probed through vhci. The next thing I'm thinking is, will the driver do anything with MMIO reads/writes that would crash if vhci isn't properly emulating the hardware? Syzkaller's vhci handles MMIO, usually returning 0 or random data, but what if this driver expects something specific? But hang on... the instructions are making me think. They explicitly say to avoid patches where the code is impossible to reach in virtualized environments, even with emulated hardware. Does syzkaller's PCI fuzzer, which is very limited in its capabilities, count as "software-emulated hardware" in that context? I'm not sure. Let's zoom back out. The patch isn't just a basic PCI driver; it *adds* a switchdev driver for OcteonTX2. Right, the patch adds `OCTEONTX_SWITCH` config and the `sw_nb.c` file...and that registers all these switchdev notifiers: fib, netevent, inetaddr, and netdevice notifiers. Hold on... these are *global* notifiers triggered by general networking events like IP address adds, neighbor adds, routes, bridge FDB entries, etc. So the switchdev and networking components will be invoked. I'm really getting into this now - let's see how these are registered; let's check `sw_nb_register(struct net_device *netdev)` in `sw_nb.c`. |
| 8/2 | 2026/07/02 11:16 | tool | read-file |
0mArgs:{
"File": "drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.c",
"FirstLine": 349,
"LineCount": 100
}Results: {
"Contents": " 349:\t\n 350:\t\tsw_fl_deinit();\n 351:\t\tsw_fib_deinit();\n 352:\t\tsw_fdb_deinit();\n 353:\t\n 354:\t\tsw_nb_pf_netdev = NULL;\n 355:\t\n 356:\t\treturn 0;\n 357:\t}\n 358:\tEXPORT_SYMBOL(sw_nb_unregister);\n 359:\t\n 360:\tint sw_nb_register(struct net_device *netdev)\n 361:\t{\n 362:\t\tint err;\n 363:\t\n 364:\t\tsw_nb_pf_netdev = netdev;\n 365:\t\n 366:\t\tsw_fdb_init();\n 367:\t\tsw_fib_init();\n 368:\t\tsw_fl_init();\n 369:\t\n 370:\t\terr = register_switchdev_notifier(\u0026sw_nb_fdb);\n 371:\t\tif (err) {\n 372:\t\t\tnetdev_err(netdev, \"Failed to register switchdev nb\\n\");\n 373:\t\t\tsw_nb_pf_netdev = NULL;\n 374:\t\t\treturn err;\n 375:\t\t}\n 376:\t\n 377:\t\terr = register_fib_notifier(\u0026init_net, \u0026sw_nb_fib, NULL, NULL);\n 378:\t\tif (err) {\n 379:\t\t\tnetdev_err(netdev, \"Failed to register fb notifier block\\n\");\n 380:\t\t\tgoto err1;\n 381:\t\t}\n 382:\t\n 383:\t\terr = register_netevent_notifier(\u0026sw_nb_netevent);\n 384:\t\tif (err) {\n 385:\t\t\tnetdev_err(netdev, \"Failed to register netevent\\n\");\n 386:\t\t\tgoto err2;\n 387:\t\t}\n 388:\t\n 389:\t#if IS_ENABLED(CONFIG_IPV6)\n 390:\t\terr = register_inet6addr_notifier(\u0026sw_nb_v6_inetaddr);\n 391:\t\tif (err) {\n 392:\t\t\tnetdev_err(netdev, \"Failed to register addr event\\n\");\n 393:\t\t\tgoto err3;\n 394:\t\t}\n 395:\t#endif\n 396:\t\n 397:\t\terr = register_inetaddr_notifier(\u0026sw_nb_v4_inetaddr);\n 398:\t\tif (err) {\n 399:\t\t\tnetdev_err(netdev, \"Failed to register addr event\\n\");\n 400:\t\t\tgoto err4;\n 401:\t\t}\n 402:\t\n 403:\t\terr = register_netdevice_notifier(\u0026sw_nb_netdev);\n 404:\t\tif (err) {\n 405:\t\t\tnetdev_err(netdev, \"Failed to register netdevice nb\\n\");\n 406:\t\t\tgoto err5;\n 407:\t\t}\n 408:\t\n 409:\t\treturn 0;\n 410:\t\n 411:\terr5:\n 412:\t\tunregister_inetaddr_notifier(\u0026sw_nb_v4_inetaddr);\n 413:\t\n 414:\terr4:\n 415:\t#if IS_ENABLED(CONFIG_IPV6)\n 416:\t\tunregister_inet6addr_notifier(\u0026sw_nb_v6_inetaddr);\n 417:\t\n 418:\terr3:\n 419:\t#endif\n 420:\t\tunregister_netevent_notifier(\u0026sw_nb_netevent);\n 421:\t\n 422:\terr2:\n 423:\t\tunregister_fib_notifier(\u0026init_net, \u0026sw_nb_fib);\n 424:\t\n 425:\terr1:\n 426:\t\tunregister_switchdev_notifier(\u0026sw_nb_fdb);\n 427:\t\n 428:\t\tsw_fl_deinit();\n 429:\t\tsw_fib_deinit();\n 430:\t\tsw_fdb_deinit();\n 431:\t\n 432:\t\tsw_nb_pf_netdev = NULL;\n 433:\t\treturn err;\n 434:\t}\n 435:\tEXPORT_SYMBOL(sw_nb_register);\n"
} |
| 9/2 | 2026/07/02 11:16 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 59227 output: 50 thoughts: 51 Thoughts: Okay, here's my interpretation of those thoughts, crafted as though they're my own: **Global Registration, Local Configuration - A Familiar Dance** Right, so the notification mechanisms – the notifiers – they're set up globally. Standard practice, usually for something that needs to be accessed and managed across the entire system. But the actual registration, specifically `sw_nb_register`, is being triggered from a more specific, lower-level function: `rvu_eswitch_config`. And that's tucked away deep inside the Marvell Octeontx2 driver, in the `rep.c` file, dealing with the NIC's representation. This is immediately interesting. It suggests a hierarchical relationship. We've got a global framework, but its implementation and interaction with the hardware is being handled at a very granular level, within the driver itself. The `rvu_eswitch_config` function, then, likely has intimate knowledge of the hardware's capabilities and configuration parameters specific to this e-switch functionality. It’s the gatekeeper, deciding when and how the global notifier system is actually integrated. It is likely configuring the network interface, which is a key part of the NIC representation. I'd want to dig into that `rvu_eswitch_config` function now. Figure out *why* it's the point of registration. What specific events or changes within that e-switch setup necessitate this global notification? What is being notified, and to what end? This might expose some crucial details about how the driver manages network configuration. Likely a critical detail on how the driver handles network configuration. |
| 10/2 | 2026/07/02 11:17 | tool | read-file |
0mArgs:{
"File": "drivers/net/ethernet/marvell/octeontx2/nic/rep.c",
"FirstLine": 398,
"LineCount": 100
}Results: {
"Contents": " 398:\t\tschedule_delayed_work(\u0026rep-\u003estats_wrk, msecs_to_jiffies(100));\n 399:\t}\n 400:\t\n 401:\tstatic int rvu_eswitch_config(struct otx2_nic *priv, u8 ena)\n 402:\t{\n 403:\t#if IS_ENABLED(CONFIG_OCTEONTX_SWITCH)\n 404:\t\tstruct net_device *netdev = priv-\u003enetdev;\n 405:\t#endif\n 406:\t\tstruct devlink_port_attrs attrs = {};\n 407:\t\tstruct esw_cfg_req *req;\n 408:\t\n 409:\t\trvu_rep_devlink_set_switch_id(priv, \u0026attrs.switch_id);\n 410:\t\n 411:\t\tmutex_lock(\u0026priv-\u003embox.lock);\n 412:\t\treq = otx2_mbox_alloc_msg_esw_cfg(\u0026priv-\u003embox);\n 413:\t\tif (!req) {\n 414:\t\t\tmutex_unlock(\u0026priv-\u003embox.lock);\n 415:\t\t\treturn -ENOMEM;\n 416:\t\t}\n 417:\t\treq-\u003eena = ena;\n 418:\t\tmemcpy(req-\u003eswitch_id, attrs.switch_id.id, attrs.switch_id.id_len);\n 419:\t\totx2_sync_mbox_msg(\u0026priv-\u003embox);\n 420:\t\tmutex_unlock(\u0026priv-\u003embox.lock);\n 421:\t\n 422:\t#if IS_ENABLED(CONFIG_OCTEONTX_SWITCH)\n 423:\t\tena ? sw_nb_register(netdev) : sw_nb_unregister(netdev);\n 424:\t#endif\n 425:\t\n 426:\t\treturn 0;\n 427:\t}\n 428:\t\n 429:\tstatic netdev_tx_t rvu_rep_xmit(struct sk_buff *skb, struct net_device *dev)\n 430:\t{\n 431:\t\tstruct rep_dev *rep = netdev_priv(dev);\n 432:\t\tstruct otx2_nic *pf = rep-\u003emdev;\n 433:\t\tstruct otx2_snd_queue *sq;\n 434:\t\tstruct netdev_queue *txq;\n 435:\t\tstruct rep_stats *stats;\n 436:\t\n 437:\t\t/* Check for minimum and maximum packet length */\n 438:\t\tif (skb-\u003elen \u003c= ETH_HLEN ||\n 439:\t\t (!skb_shinfo(skb)-\u003egso_size \u0026\u0026 skb-\u003elen \u003e pf-\u003etx_max_pktlen)) {\n 440:\t\t\tstats = \u0026rep-\u003estats;\n 441:\t\t\tatomic_long_inc(\u0026stats-\u003etx_discards);\n 442:\t\t\tdev_kfree_skb(skb);\n 443:\t\t\treturn NETDEV_TX_OK;\n 444:\t\t}\n 445:\t\n 446:\t\tsq = \u0026pf-\u003eqset.sq[rep-\u003erep_id];\n 447:\t\ttxq = netdev_get_tx_queue(dev, 0);\n 448:\t\n 449:\t\tif (!otx2_sq_append_skb(pf, txq, sq, skb, rep-\u003erep_id)) {\n 450:\t\t\tnetif_tx_stop_queue(txq);\n 451:\t\n 452:\t\t\t/* Check again, in case SQBs got freed up */\n 453:\t\t\tsmp_mb();\n 454:\t\t\tif (((sq-\u003enum_sqbs - *sq-\u003eaura_fc_addr) * sq-\u003esqe_per_sqb)\n 455:\t\t\t\t\t\t\t\t\u003e sq-\u003esqe_thresh)\n 456:\t\t\t\tnetif_tx_wake_queue(txq);\n 457:\t\n 458:\t\t\treturn NETDEV_TX_BUSY;\n 459:\t\t}\n 460:\t\treturn NETDEV_TX_OK;\n 461:\t}\n 462:\t\n 463:\tstatic int rvu_rep_open(struct net_device *dev)\n 464:\t{\n 465:\t\tstruct rep_dev *rep = netdev_priv(dev);\n 466:\t\tstruct otx2_nic *priv = rep-\u003emdev;\n 467:\t\tstruct rep_event evt = {0};\n 468:\t\n 469:\t\tif (!(rep-\u003eflags \u0026 RVU_REP_VF_INITIALIZED))\n 470:\t\t\treturn 0;\n 471:\t\n 472:\t\tnetif_carrier_on(dev);\n 473:\t\tnetif_tx_start_all_queues(dev);\n 474:\t\n 475:\t\tevt.event = RVU_EVENT_PORT_STATE;\n 476:\t\tevt.evt_data.port_state = 1;\n 477:\t\tevt.pcifunc = rep-\u003epcifunc;\n 478:\t\trvu_rep_notify_pfvf(priv, RVU_EVENT_PORT_STATE, \u0026evt);\n 479:\t\treturn 0;\n 480:\t}\n 481:\t\n 482:\tstatic int rvu_rep_stop(struct net_device *dev)\n 483:\t{\n 484:\t\tstruct rep_dev *rep = netdev_priv(dev);\n 485:\t\tstruct otx2_nic *priv = rep-\u003emdev;\n 486:\t\tstruct rep_event evt = {0};\n 487:\t\n 488:\t\tif (!(rep-\u003eflags \u0026 RVU_REP_VF_INITIALIZED))\n 489:\t\t\treturn 0;\n 490:\t\n 491:\t\tnetif_carrier_off(dev);\n 492:\t\tnetif_tx_disable(dev);\n 493:\t\n 494:\t\tevt.event = RVU_EVENT_PORT_STATE;\n 495:\t\tevt.pcifunc = rep-\u003epcifunc;\n 496:\t\trvu_rep_notify_pfvf(priv, RVU_EVENT_PORT_STATE, \u0026evt);\n 497:\t\treturn 0;\n"
} |
| 11/2 | 2026/07/02 11:17 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 61304 output: 23 thoughts: 28 Thoughts: Here's my thought process, as if I were analyzing this code: **Finding the Callers of `rvu_eswitch_config`** Okay, so I need to understand where `rvu_eswitch_config` is being called from. Standard procedure: I'll use `grep` to quickly find all instances of that function name within the codebase. That should give me a good overview of the call graph and the surrounding context for each call site. This is a fundamental step in understanding the module interactions and data flow. Knowing the immediate callers is the first step toward understanding its overall role. I anticipate a relatively manageable number of results, hopefully making this initial investigation straightforward. Let's see what `grep` uncovers! |
| 12/2 | 2026/07/02 11:17 | tool | grepper |
0mError:no matches Args: {
"Expression": "\"rvu_eswitch_config\""
}Results: {
"error": "no matches"
} |
| 13/2 | 2026/07/02 11:17 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 61523 output: 48 thoughts: 30 Thoughts: Here's my thought process, summarized as you requested: **Navigating the OcteonTX2 NIC Driver** Okay, I need to dive into the OcteonTX2 NIC driver, specifically the representation layer, to debug this issue. Let me quickly zero in on the relevant code. The file I need is `drivers/net/ethernet/marvell/octeontx2/nic/rep.c`. This is where the core logic related to representing the NIC's functionality will reside. Gotta make sure I'm looking at the correct file, as this is going to be my base of operations. |
| 14/2 | 2026/07/02 11:17 | tool | grepper |
0mArgs:{
"Expression": "rvu_eswitch_config",
"PathPrefix": "drivers/net/ethernet/marvell/octeontx2/nic/rep.c"
}Results: {
"Output": "drivers/net/ethernet/marvell/octeontx2/nic/rep.c=381=static void rvu_rep_get_stats64(struct net_device *dev,\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/rep.c-400-\ndrivers/net/ethernet/marvell/octeontx2/nic/rep.c:401:static int rvu_eswitch_config(struct otx2_nic *priv, u8 ena)\ndrivers/net/ethernet/marvell/octeontx2/nic/rep.c-402-{\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/rep.c=642=void rvu_rep_destroy(struct otx2_nic *priv)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/rep.c-646-\ndrivers/net/ethernet/marvell/octeontx2/nic/rep.c:647:\trvu_eswitch_config(priv, false);\ndrivers/net/ethernet/marvell/octeontx2/nic/rep.c-648-\tpriv-\u003eflags |= OTX2_FLAG_INTF_DOWN;\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/rep.c=661=int rvu_rep_create(struct otx2_nic *priv, struct netlink_ext_ack *extack)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/rep.c-731-\ndrivers/net/ethernet/marvell/octeontx2/nic/rep.c:732:\trvu_eswitch_config(priv, true);\ndrivers/net/ethernet/marvell/octeontx2/nic/rep.c-733-\treturn 0;\n"
} |
| 15/2 | 2026/07/02 11:17 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 62213 output: 20 thoughts: 30 Thoughts: Okay, here's my interpretation of that thought process, assuming I'm an expert in this field, rendered in the first person: **Investigating Representor Creation** Alright, so the first thing on my mind is the function `rvu_rep_create`. I know that's the point where a representor is brought into existence. My next logical question is, precisely *when* does this happen? What events, circumstances, or other code paths trigger a call to `rvu_rep_create`? I need to understand the calling context to fully grasp the lifecycle and purpose of these representors. Determining the call sites will be key to understanding the overall architecture and how representors fit into the bigger picture. |
| 16/2 | 2026/07/02 11:17 | tool | grepper |
0mArgs:{
"Expression": "rvu_rep_create"
}Results: {
"Output": "drivers/net/ethernet/marvell/octeontx2/nic/otx2_devlink.c=161=static int otx2_devlink_eswitch_mode_set(struct devlink *devlink, u16 mode,\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_devlink.c-178-\tcase DEVLINK_ESWITCH_MODE_SWITCHDEV:\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_devlink.c:179:\t\tret = rvu_rep_create(pfvf, extack);\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_devlink.c-180-\t\tbreak;\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/rep.c=642=void rvu_rep_destroy(struct otx2_nic *priv)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/rep.c-660-\ndrivers/net/ethernet/marvell/octeontx2/nic/rep.c:661:int rvu_rep_create(struct otx2_nic *priv, struct netlink_ext_ack *extack)\ndrivers/net/ethernet/marvell/octeontx2/nic/rep.c-662-{\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/rep.h=47=static inline bool otx2_rep_dev(struct pci_dev *pdev)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/rep.h-51-\ndrivers/net/ethernet/marvell/octeontx2/nic/rep.h:52:int rvu_rep_create(struct otx2_nic *priv, struct netlink_ext_ack *extack);\ndrivers/net/ethernet/marvell/octeontx2/nic/rep.h-53-void rvu_rep_destroy(struct otx2_nic *priv);\n"
} |
| 17/2 | 2026/07/02 11:17 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 62947 output: 55 thoughts: 227 Thoughts: **My Analysis of the `rvu_rep_create` Path** Okay, so `rvu_rep_create` is triggered when we set the eswitch mode through devlink, that makes sense. Devlink gives us a way to control the eswitch, got it. But a question pops up: can we actually *do* this without the physical hardware present? `otx2_devlink_eswitch_mode_set` needs the `otx2_nic` device to be around, and that's created when the PCI driver runs its `probe` function. Now, because there's no software emulation for the Marvell OcteonTX2, the PCI driver won't probe correctly in environments like Google Compute Engine or QEMU unless we use syzkaller's `vhci` mechanism to inject a fake PCI device. So, if syzkaller creates a fake PCI device, the `probe` function of the driver *will* be called, interesting. I wonder if the driver's `probe` function can work with `vhci`? Let me see... I need to look at `otx2_pf_probe` in `drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c`. That's where I need to focus my attention. |
| 18/2 | 2026/07/02 11:17 | tool | read-file |
0mArgs:{
"File": "drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c",
"FirstLine": 2910,
"LineCount": 100
}Results: {
"Contents": "2910:\t\tif (rc) {\n2911:\t\t\tpf-\u003evf_configs[vf].trusted = !enable;\n2912:\t\t} else {\n2913:\t\t\tnetdev_info(pf-\u003enetdev, \"VF %d is %strusted\\n\",\n2914:\t\t\t\t vf, enable ? \"\" : \"not \");\n2915:\t\t\totx2_set_rx_mode(netdev);\n2916:\t\t}\n2917:\t\n2918:\t\treturn rc;\n2919:\t}\n2920:\t\n2921:\tstatic const struct net_device_ops otx2_netdev_ops = {\n2922:\t\t.ndo_open\t\t= otx2_open,\n2923:\t\t.ndo_stop\t\t= otx2_stop,\n2924:\t\t.ndo_start_xmit\t\t= otx2_xmit,\n2925:\t\t.ndo_select_queue\t= otx2_select_queue,\n2926:\t\t.ndo_fix_features\t= otx2_fix_features,\n2927:\t\t.ndo_set_mac_address = otx2_set_mac_address,\n2928:\t\t.ndo_change_mtu\t\t= otx2_change_mtu,\n2929:\t\t.ndo_set_rx_mode\t= otx2_set_rx_mode,\n2930:\t\t.ndo_set_features\t= otx2_set_features,\n2931:\t\t.ndo_tx_timeout\t\t= otx2_tx_timeout,\n2932:\t\t.ndo_get_stats64\t= otx2_get_stats64,\n2933:\t\t.ndo_set_vf_mac\t\t= otx2_set_vf_mac,\n2934:\t\t.ndo_set_vf_vlan\t= otx2_set_vf_vlan,\n2935:\t\t.ndo_get_vf_config\t= otx2_get_vf_config,\n2936:\t\t.ndo_bpf\t\t= otx2_xdp,\n2937:\t\t.ndo_xsk_wakeup\t\t= otx2_xsk_wakeup,\n2938:\t\t.ndo_xdp_xmit = otx2_xdp_xmit,\n2939:\t\t.ndo_setup_tc\t\t= otx2_setup_tc,\n2940:\t\t.ndo_set_vf_trust\t= otx2_ndo_set_vf_trust,\n2941:\t\t.ndo_hwtstamp_get\t= otx2_config_hwtstamp_get,\n2942:\t\t.ndo_hwtstamp_set\t= otx2_config_hwtstamp_set,\n2943:\t};\n2944:\t\n2945:\tint otx2_wq_init(struct otx2_nic *pf)\n2946:\t{\n2947:\t\tpf-\u003eotx2_wq = create_singlethread_workqueue(\"otx2_wq\");\n2948:\t\tif (!pf-\u003eotx2_wq)\n2949:\t\t\treturn -ENOMEM;\n2950:\t\n2951:\t\tINIT_WORK(\u0026pf-\u003erx_mode_work, otx2_rx_mode_wrk_handler);\n2952:\t\tINIT_WORK(\u0026pf-\u003ereset_task, otx2_reset_task);\n2953:\t\treturn 0;\n2954:\t}\n2955:\t\n2956:\tint otx2_check_pf_usable(struct otx2_nic *nic)\n2957:\t{\n2958:\t\tu64 rev;\n2959:\t\n2960:\t\trev = otx2_read64(nic, RVU_PF_BLOCK_ADDRX_DISC(BLKADDR_RVUM));\n2961:\t\trev = (rev \u003e\u003e 12) \u0026 0xFF;\n2962:\t\t/* Check if AF has setup revision for RVUM block,\n2963:\t\t * otherwise this driver probe should be deferred\n2964:\t\t * until AF driver comes up.\n2965:\t\t */\n2966:\t\tif (!rev) {\n2967:\t\t\tdev_warn(nic-\u003edev,\n2968:\t\t\t\t \"AF is not initialized, deferring probe\\n\");\n2969:\t\t\treturn -EPROBE_DEFER;\n2970:\t\t}\n2971:\t\treturn 0;\n2972:\t}\n2973:\t\n2974:\tint otx2_realloc_msix_vectors(struct otx2_nic *pf)\n2975:\t{\n2976:\t\tstruct otx2_hw *hw = \u0026pf-\u003ehw;\n2977:\t\tint num_vec, err;\n2978:\t\n2979:\t\t/* NPA interrupts are inot registered, so alloc only\n2980:\t\t * upto NIX vector offset.\n2981:\t\t */\n2982:\t\tnum_vec = hw-\u003enix_msixoff;\n2983:\t\tnum_vec += NIX_LF_CINT_VEC_START + hw-\u003emax_queues;\n2984:\t\n2985:\t\totx2_disable_mbox_intr(pf);\n2986:\t\tpci_free_irq_vectors(hw-\u003epdev);\n2987:\t\terr = pci_alloc_irq_vectors(hw-\u003epdev, num_vec, num_vec, PCI_IRQ_MSIX);\n2988:\t\tif (err \u003c 0) {\n2989:\t\t\tdev_err(pf-\u003edev, \"%s: Failed to realloc %d IRQ vectors\\n\",\n2990:\t\t\t\t__func__, num_vec);\n2991:\t\t\treturn err;\n2992:\t\t}\n2993:\t\n2994:\t\treturn otx2_register_mbox_intr(pf, false);\n2995:\t}\n2996:\tEXPORT_SYMBOL(otx2_realloc_msix_vectors);\n2997:\t\n2998:\tstatic int otx2_sriov_vfcfg_init(struct otx2_nic *pf)\n2999:\t{\n3000:\t\tint i;\n3001:\t\n3002:\t\tpf-\u003evf_configs = devm_kcalloc(pf-\u003edev, pf-\u003etotal_vfs,\n3003:\t\t\t\t\t sizeof(struct otx2_vf_config),\n3004:\t\t\t\t\t GFP_KERNEL);\n3005:\t\tif (!pf-\u003evf_configs)\n3006:\t\t\treturn -ENOMEM;\n3007:\t\n3008:\t\tfor (i = 0; i \u003c pf-\u003etotal_vfs; i++) {\n3009:\t\t\tpf-\u003evf_configs[i].pf = pf;\n"
} |
| 19/2 | 2026/07/02 11:17 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 65285 output: 52 thoughts: 13 Thoughts: Okay, let's dive into this. Here's my take on those thoughts, as if they're my own: **Analyzing `otx2_pf_probe`** Alright, the first thing that jumps out at me is `otx2_pf_probe`. Immediately, I'm thinking: *this must be related to the OCTEON TX2 (OTX2) platform, specifically the Physical Function (PF) probe.* This implies we're dealing with hardware initialization or device detection on that specific NIC. I need to understand what aspects are triggered during the PF probe. I'm mentally running through the typical probe sequence: resource allocation, register access, interrupt setup, and perhaps firmware loading or checking. What subsystems are we likely touching? Is it the Ethernet controllers, the DMA engines, the crypto accelerators, or something else entirely? The probe function is the initial handshake with the hardware; any errors here and the entire system is likely going to fail. We need to look for common issues in resource allocation conflicts, DMA setup problems, or errors during register reads. Knowing the architecture of OTX2, I'll be looking for things like the PCI configuration space accesses, the device IDs, and the interrupt vector mapping. Is this part of the driver responsible for creating VF (Virtual Functions) on the card? That would drastically change the scope and responsibilities. What's the error handling like? Are there thorough checks for valid responses from the hardware? Any potential race conditions during initialization? My next thought is, where's the best place to dig deeper? The kernel source is the obvious first stop. I'll search for the definition of `otx2_pf_probe`, its call chain, and related data structures. I need to understand the larger context. Is it part of the driver's device initialization function, or is it a specific module that is called from the general device enumeration code. Any surrounding code will be vital to understand how it interfaces with the other drivers. |
| 20/2 | 2026/07/02 11:17 | tool | grepper |
0mError:no matches Args: {
"Expression": "otx2_pf_probe",
"PathPrefix": "drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c"
}Results: {
"error": "no matches"
} |
| 21/2 | 2026/07/02 11:17 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 65780 output: 50 thoughts: 13 Thoughts: Okay, here's my summary, in the first person, as requested: **Navigating the Labyrinth of Code** Hmm, thinking about this... I need to figure out what's the function or process being referred to. It’s got that “otx2” prefix, and that makes me think about my probe, that sounds like a probe function, right? So, I'm thinking, my initial assumption is that this is referring to the `otx2_probe`. Yep, based on the naming convention and the general context, `otx2_probe` is very likely the component or function in question. |
| 22/2 | 2026/07/02 11:17 | tool | grepper |
0mArgs:{
"Expression": "otx2_probe",
"PathPrefix": "drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c"
}Results: {
"Output": "drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c=3122=EXPORT_SYMBOL(otx2_init_rsrc);\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c-3123-\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c:3124:static int otx2_probe(struct pci_dev *pdev, const struct pci_device_id *id)\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c-3125-{\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c=3542=static struct pci_driver otx2_pf_driver = {\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c-3544-\t.id_table = otx2_pf_id_table,\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c:3545:\t.probe = otx2_probe,\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c-3546-\t.shutdown = otx2_remove,\n"
} |
| 23/2 | 2026/07/02 11:17 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 66340 output: 55 thoughts: 11 Thoughts: Okay, here's my interpretation of that thought process, assuming I'm already deep in the weeds of this subject: **Analyzing the Call to `otx2_probe`** Alright, the first thing that jumps out at me is that the focus is on `otx2_probe`. Fine, let's dive into that. This means something has triggered my attention and I need to figure out *why*. What's the context? What triggered the need to read and understand this specific piece of code? Is this related to a bug report I'm investigating? A performance bottleneck? Or am I just trying to gain a better overall understanding of this module? Knowing the 'why' will heavily influence how I approach the reading, so it's the first thing that comes to mind. |
| 24/2 | 2026/07/02 11:17 | tool | read-file |
0mArgs:{
"File": "drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c",
"FirstLine": 3124,
"LineCount": 100
}Results: {
"Contents": "3124:\tstatic int otx2_probe(struct pci_dev *pdev, const struct pci_device_id *id)\n3125:\t{\n3126:\t\tstruct device *dev = \u0026pdev-\u003edev;\n3127:\t\tint err, qcount, qos_txqs;\n3128:\t\tstruct net_device *netdev;\n3129:\t\tstruct otx2_nic *pf;\n3130:\t\tstruct otx2_hw *hw;\n3131:\t\n3132:\t\terr = pcim_enable_device(pdev);\n3133:\t\tif (err) {\n3134:\t\t\tdev_err(dev, \"Failed to enable PCI device\\n\");\n3135:\t\t\treturn err;\n3136:\t\t}\n3137:\t\n3138:\t\terr = pcim_request_all_regions(pdev, DRV_NAME);\n3139:\t\tif (err) {\n3140:\t\t\tdev_err(dev, \"PCI request regions failed 0x%x\\n\", err);\n3141:\t\t\treturn err;\n3142:\t\t}\n3143:\t\n3144:\t\terr = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(48));\n3145:\t\tif (err) {\n3146:\t\t\tdev_err(dev, \"DMA mask config failed, abort\\n\");\n3147:\t\t\treturn err;\n3148:\t\t}\n3149:\t\n3150:\t\tpci_set_master(pdev);\n3151:\t\n3152:\t\t/* Set number of queues */\n3153:\t\tqcount = min_t(int, num_online_cpus(), OTX2_MAX_CQ_CNT);\n3154:\t\tqos_txqs = min_t(int, qcount, OTX2_QOS_MAX_LEAF_NODES);\n3155:\t\n3156:\t\tnetdev = alloc_etherdev_mqs(sizeof(*pf), qcount + qos_txqs, qcount);\n3157:\t\tif (!netdev)\n3158:\t\t\treturn -ENOMEM;\n3159:\t\n3160:\t\tpci_set_drvdata(pdev, netdev);\n3161:\t\tSET_NETDEV_DEV(netdev, \u0026pdev-\u003edev);\n3162:\t\tpf = netdev_priv(netdev);\n3163:\t\tpf-\u003enetdev = netdev;\n3164:\t\tpf-\u003epdev = pdev;\n3165:\t\tpf-\u003edev = dev;\n3166:\t\tpf-\u003etotal_vfs = pci_sriov_get_totalvfs(pdev);\n3167:\t\tpf-\u003eflags |= OTX2_FLAG_INTF_DOWN;\n3168:\t\n3169:\t\thw = \u0026pf-\u003ehw;\n3170:\t\thw-\u003epdev = pdev;\n3171:\t\thw-\u003erx_queues = qcount;\n3172:\t\thw-\u003etx_queues = qcount;\n3173:\t\thw-\u003enon_qos_queues = qcount;\n3174:\t\thw-\u003emax_queues = qcount;\n3175:\t\thw-\u003erbuf_len = OTX2_DEFAULT_RBUF_LEN;\n3176:\t\t/* Use CQE of 128 byte descriptor size by default */\n3177:\t\thw-\u003exqe_size = 128;\n3178:\t\n3179:\t\terr = otx2_init_rsrc(pdev, pf);\n3180:\t\tif (err)\n3181:\t\t\tgoto err_free_netdev;\n3182:\t\n3183:\t\terr = otx2_set_real_num_queues(netdev, hw-\u003etx_queues, hw-\u003erx_queues);\n3184:\t\tif (err)\n3185:\t\t\tgoto err_detach_rsrc;\n3186:\t\n3187:\t\t/* Assign default mac address */\n3188:\t\totx2_get_mac_from_af(netdev);\n3189:\t\n3190:\t\t/* Don't check for error. Proceed without ptp */\n3191:\t\totx2_ptp_init(pf);\n3192:\t\n3193:\t\t/* NPA's pool is a stack to which SW frees buffer pointers via Aura.\n3194:\t\t * HW allocates buffer pointer from stack and uses it for DMA'ing\n3195:\t\t * ingress packet. In some scenarios HW can free back allocated buffer\n3196:\t\t * pointers to pool. This makes it impossible for SW to maintain a\n3197:\t\t * parallel list where physical addresses of buffer pointers (IOVAs)\n3198:\t\t * given to HW can be saved for later reference.\n3199:\t\t *\n3200:\t\t * So the only way to convert Rx packet's buffer address is to use\n3201:\t\t * IOMMU's iova_to_phys() handler which translates the address by\n3202:\t\t * walking through the translation tables.\n3203:\t\t */\n3204:\t\tpf-\u003eiommu_domain = iommu_get_domain_for_dev(dev);\n3205:\t\n3206:\t\tnetdev-\u003ehw_features = (NETIF_F_RXCSUM | NETIF_F_IP_CSUM |\n3207:\t\t\t\t NETIF_F_IPV6_CSUM | NETIF_F_RXHASH |\n3208:\t\t\t\t NETIF_F_SG | NETIF_F_TSO | NETIF_F_TSO6 |\n3209:\t\t\t\t NETIF_F_GSO_UDP_L4);\n3210:\t\tnetdev-\u003efeatures |= netdev-\u003ehw_features;\n3211:\t\n3212:\t\terr = otx2_mcam_flow_init(pf);\n3213:\t\tif (err)\n3214:\t\t\tgoto err_ptp_destroy;\n3215:\t\n3216:\t\totx2_set_hw_capabilities(pf);\n3217:\t\n3218:\t\terr = cn10k_mcs_init(pf);\n3219:\t\tif (err)\n3220:\t\t\tgoto err_del_mcam_entries;\n3221:\t\n3222:\t\tif (pf-\u003eflags \u0026 OTX2_FLAG_NTUPLE_SUPPORT)\n3223:\t\t\tnetdev-\u003ehw_features |= NETIF_F_NTUPLE;\n"
} |
| 25/2 | 2026/07/02 11:17 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 68272 output: 55 thoughts: 21 Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience: **Analyzing the Initialization of Resources** Okay, so `otx2_init_rsrc` is the function that's being called. My immediate thought is, let's dissect this. Given what I'm familiar with in the OTX2 landscape, my assumption is that this function likely orchestrates the setup and allocation of the core resources required for the operation. This almost certainly involves initializing any device-specific hardware structures, possibly allocating memory regions for buffers or control registers, and perhaps setting up interrupt vectors if this is a hardware-accelerated component. I'll need to dig in to confirm these suspicions, but the naming convention alone screams "resource initialization." Understanding the exact flow within `otx2_init_rsrc` is going to be crucial to understanding how the data flow is constructed. |
| 26/2 | 2026/07/02 11:17 | tool | read-file |
0mArgs:{
"File": "drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c",
"FirstLine": 3033,
"LineCount": 100
}Results: {
"Contents": "3033:\t{\n3034:\t\tstruct device *dev = \u0026pdev-\u003edev;\n3035:\t\tstruct otx2_hw *hw = \u0026pf-\u003ehw;\n3036:\t\tint num_vec, err;\n3037:\t\n3038:\t\tnum_vec = pci_msix_vec_count(pdev);\n3039:\t\thw-\u003eirq_name = devm_kmalloc_array(\u0026hw-\u003epdev-\u003edev, num_vec, NAME_SIZE,\n3040:\t\t\t\t\t\t GFP_KERNEL);\n3041:\t\tif (!hw-\u003eirq_name)\n3042:\t\t\treturn -ENOMEM;\n3043:\t\n3044:\t\thw-\u003eaffinity_mask = devm_kcalloc(\u0026hw-\u003epdev-\u003edev, num_vec,\n3045:\t\t\t\t\t\t sizeof(cpumask_var_t), GFP_KERNEL);\n3046:\t\tif (!hw-\u003eaffinity_mask)\n3047:\t\t\treturn -ENOMEM;\n3048:\t\n3049:\t\t/* Map CSRs */\n3050:\t\tpf-\u003ereg_base = pcim_iomap(pdev, PCI_CFG_REG_BAR_NUM, 0);\n3051:\t\tif (!pf-\u003ereg_base) {\n3052:\t\t\tdev_err(dev, \"Unable to map physical function CSRs, aborting\\n\");\n3053:\t\t\treturn -ENOMEM;\n3054:\t\t}\n3055:\t\n3056:\t\terr = otx2_check_pf_usable(pf);\n3057:\t\tif (err)\n3058:\t\t\treturn err;\n3059:\t\n3060:\t\tif (!is_cn20k(pf-\u003epdev))\n3061:\t\t\terr = pci_alloc_irq_vectors(hw-\u003epdev, RVU_PF_INT_VEC_CNT,\n3062:\t\t\t\t\t\t RVU_PF_INT_VEC_CNT, PCI_IRQ_MSIX);\n3063:\t\telse\n3064:\t\t\terr = pci_alloc_irq_vectors(hw-\u003epdev, RVU_MBOX_PF_INT_VEC_CNT,\n3065:\t\t\t\t\t\t RVU_MBOX_PF_INT_VEC_CNT,\n3066:\t\t\t\t\t\t PCI_IRQ_MSIX);\n3067:\t\tif (err \u003c 0) {\n3068:\t\t\tdev_err(dev, \"%s: Failed to alloc %d IRQ vectors\\n\",\n3069:\t\t\t\t__func__, num_vec);\n3070:\t\t\treturn err;\n3071:\t\t}\n3072:\t\n3073:\t\totx2_setup_dev_hw_settings(pf);\n3074:\t\n3075:\t\tif (is_cn20k(pf-\u003epdev))\n3076:\t\t\tcn20k_init(pf);\n3077:\t\telse\n3078:\t\t\totx2_init_hw_ops(pf);\n3079:\t\n3080:\t\t/* Init PF \u003c=\u003e AF mailbox stuff */\n3081:\t\terr = otx2_pfaf_mbox_init(pf);\n3082:\t\tif (err)\n3083:\t\t\tgoto err_free_irq_vectors;\n3084:\t\n3085:\t\t/* Register mailbox interrupt */\n3086:\t\terr = otx2_register_mbox_intr(pf, true);\n3087:\t\tif (err)\n3088:\t\t\tgoto err_mbox_destroy;\n3089:\t\n3090:\t\t/* Request AF to attach NPA and NIX LFs to this PF.\n3091:\t\t * NIX and NPA LFs are needed for this PF to function as a NIC.\n3092:\t\t */\n3093:\t\terr = otx2_attach_npa_nix(pf);\n3094:\t\tif (err)\n3095:\t\t\tgoto err_disable_mbox_intr;\n3096:\t\n3097:\t\terr = otx2_realloc_msix_vectors(pf);\n3098:\t\tif (err)\n3099:\t\t\tgoto err_detach_rsrc;\n3100:\t\n3101:\t\terr = cn10k_lmtst_init(pf);\n3102:\t\tif (err)\n3103:\t\t\tgoto err_detach_rsrc;\n3104:\t\n3105:\t\treturn 0;\n3106:\t\n3107:\terr_detach_rsrc:\n3108:\t\tif (pf-\u003ehw.lmt_info)\n3109:\t\t\tfree_percpu(pf-\u003ehw.lmt_info);\n3110:\t\tif (test_bit(CN10K_LMTST, \u0026pf-\u003ehw.cap_flag))\n3111:\t\t\tqmem_free(pf-\u003edev, pf-\u003edync_lmt);\n3112:\t\totx2_detach_resources(\u0026pf-\u003embox);\n3113:\terr_disable_mbox_intr:\n3114:\t\totx2_disable_mbox_intr(pf);\n3115:\terr_mbox_destroy:\n3116:\t\totx2_pfaf_mbox_destroy(pf);\n3117:\terr_free_irq_vectors:\n3118:\t\tpci_free_irq_vectors(hw-\u003epdev);\n3119:\t\n3120:\t\treturn err;\n3121:\t}\n3122:\tEXPORT_SYMBOL(otx2_init_rsrc);\n3123:\t\n3124:\tstatic int otx2_probe(struct pci_dev *pdev, const struct pci_device_id *id)\n3125:\t{\n3126:\t\tstruct device *dev = \u0026pdev-\u003edev;\n3127:\t\tint err, qcount, qos_txqs;\n3128:\t\tstruct net_device *netdev;\n3129:\t\tstruct otx2_nic *pf;\n3130:\t\tstruct otx2_hw *hw;\n3131:\t\n3132:\t\terr = pcim_enable_device(pdev);\n"
} |
| 27/2 | 2026/07/02 11:17 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 70145 output: 54 thoughts: 220 Thoughts: **Analyzing Potential Probe Failures in the OTX2 Driver** Okay, so I'm looking at this `otx2_check_pf_usable` function. It seems it's reading a register, specifically the `RVU_PF_BLOCK_ADDRX_DISC` register. I see it's doing a 64-bit read using `otx2_read64`. If this read returns 0, the function bails out with `-EPROBE_DEFER`. Now, the crucial part - I'm worried about syzkaller's vhci, because if it's returning zero for MMIO reads, then `otx2_check_pf_usable` is always going to fail, and the probe is going to be deferred indefinitely. Hmm, I need to verify that syzkaller's vhci can actually return something *other* than zero for these MMIO reads. I know it *can* return random data, which is good. But that doesn't guarantee a non-zero value, so I need to check its behavior more specifically. But wait, hold on... even if the probe passes that test, `otx2_pfaf_mbox_init` will come into play next, and it deals with the AF driver. It attempts to communicate via the mailbox, which is concerning. If the AF driver isn't present, or if there's a problem with the mailbox communication itself, we're still going to get an error. I need to dive into `otx2_pfaf_mbox_init` now and see what it's doing and what potential failure points it has. This is where I need to focus my attention. |
| 28/2 | 2026/07/02 11:17 | tool | read-file |
0mArgs:{
"File": "drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c",
"FirstLine": 580,
"LineCount": 100
}Results: {
"Contents": " 580:\t\tret = __otx2_alloc_rbuf(pfvf, pool, dma, qidx, idx);\n 581:\t\tlocal_bh_enable();\n 582:\t\treturn ret;\n 583:\t}\n 584:\t\n 585:\tint otx2_alloc_buffer(struct otx2_nic *pfvf, struct otx2_cq_queue *cq,\n 586:\t\t\t dma_addr_t *dma)\n 587:\t{\n 588:\t\tif (unlikely(__otx2_alloc_rbuf(pfvf, cq-\u003erbpool, dma,\n 589:\t\t\t\t\t cq-\u003ecq_idx, cq-\u003epool_ptrs - 1)))\n 590:\t\t\treturn -ENOMEM;\n 591:\t\treturn 0;\n 592:\t}\n 593:\t\n 594:\tvoid otx2_tx_timeout(struct net_device *netdev, unsigned int txq)\n 595:\t{\n 596:\t\tstruct otx2_nic *pfvf = netdev_priv(netdev);\n 597:\t\n 598:\t\tschedule_work(\u0026pfvf-\u003ereset_task);\n 599:\t}\n 600:\tEXPORT_SYMBOL(otx2_tx_timeout);\n 601:\t\n 602:\tvoid otx2_get_mac_from_af(struct net_device *netdev)\n 603:\t{\n 604:\t\tstruct otx2_nic *pfvf = netdev_priv(netdev);\n 605:\t\tint err;\n 606:\t\n 607:\t\terr = otx2_hw_get_mac_addr(pfvf, netdev);\n 608:\t\tif (err)\n 609:\t\t\tdev_warn(pfvf-\u003edev, \"Failed to read mac from hardware\\n\");\n 610:\t\n 611:\t\t/* If AF doesn't provide a valid MAC, generate a random one */\n 612:\t\tif (!is_valid_ether_addr(netdev-\u003edev_addr))\n 613:\t\t\teth_hw_addr_random(netdev);\n 614:\t}\n 615:\tEXPORT_SYMBOL(otx2_get_mac_from_af);\n 616:\t\n 617:\tint otx2_txschq_config(struct otx2_nic *pfvf, int lvl, int prio, bool txschq_for_pfc)\n 618:\t{\n 619:\t\tu16 (*schq_list)[MAX_TXSCHQ_PER_FUNC];\n 620:\t\tstruct otx2_hw *hw = \u0026pfvf-\u003ehw;\n 621:\t\tstruct nix_txschq_config *req;\n 622:\t\tu64 schq, parent;\n 623:\t\tu64 dwrr_val;\n 624:\t\n 625:\t\tdwrr_val = mtu_to_dwrr_weight(pfvf, pfvf-\u003etx_max_pktlen);\n 626:\t\n 627:\t\treq = otx2_mbox_alloc_msg_nix_txschq_cfg(\u0026pfvf-\u003embox);\n 628:\t\tif (!req)\n 629:\t\t\treturn -ENOMEM;\n 630:\t\n 631:\t\treq-\u003elvl = lvl;\n 632:\t\treq-\u003enum_regs = 1;\n 633:\t\n 634:\t\tschq_list = hw-\u003etxschq_list;\n 635:\t#ifdef CONFIG_DCB\n 636:\t\tif (txschq_for_pfc)\n 637:\t\t\tschq_list = pfvf-\u003epfc_schq_list;\n 638:\t#endif\n 639:\t\n 640:\t\tschq = schq_list[lvl][prio];\n 641:\t\t/* Set topology e.t.c configuration */\n 642:\t\tif (lvl == NIX_TXSCH_LVL_SMQ) {\n 643:\t\t\treq-\u003ereg[0] = NIX_AF_SMQX_CFG(schq);\n 644:\t\t\treq-\u003eregval[0] = ((u64)pfvf-\u003etx_max_pktlen \u003c\u003c 8) | OTX2_MIN_MTU;\n 645:\t\t\treq-\u003eregval[0] |= (0x20ULL \u003c\u003c 51) | (0x80ULL \u003c\u003c 39) |\n 646:\t\t\t\t\t (0x2ULL \u003c\u003c 36);\n 647:\t\t\t/* Set link type for DWRR MTU selection on CN10K silicons */\n 648:\t\t\tif (!is_dev_otx2(pfvf-\u003epdev))\n 649:\t\t\t\treq-\u003eregval[0] |= FIELD_PREP(GENMASK_ULL(58, 57),\n 650:\t\t\t\t\t\t\t(u64)hw-\u003esmq_link_type);\n 651:\t\t\treq-\u003enum_regs++;\n 652:\t\t\t/* MDQ config */\n 653:\t\t\tparent = schq_list[NIX_TXSCH_LVL_TL4][prio];\n 654:\t\t\treq-\u003ereg[1] = NIX_AF_MDQX_PARENT(schq);\n 655:\t\t\treq-\u003eregval[1] = parent \u003c\u003c 16;\n 656:\t\t\treq-\u003enum_regs++;\n 657:\t\t\t/* Set DWRR quantum */\n 658:\t\t\treq-\u003ereg[2] = NIX_AF_MDQX_SCHEDULE(schq);\n 659:\t\t\treq-\u003eregval[2] = dwrr_val;\n 660:\t\t} else if (lvl == NIX_TXSCH_LVL_TL4) {\n 661:\t\t\tint sdp_chan = hw-\u003etx_chan_base + prio;\n 662:\t\n 663:\t\t\tif (is_otx2_sdp_rep(pfvf-\u003epdev))\n 664:\t\t\t\tprio = 0;\n 665:\t\t\tparent = schq_list[NIX_TXSCH_LVL_TL3][prio];\n 666:\t\t\treq-\u003ereg[0] = NIX_AF_TL4X_PARENT(schq);\n 667:\t\t\treq-\u003eregval[0] = (u64)parent \u003c\u003c 16;\n 668:\t\t\treq-\u003enum_regs++;\n 669:\t\t\treq-\u003ereg[1] = NIX_AF_TL4X_SCHEDULE(schq);\n 670:\t\t\treq-\u003eregval[1] = dwrr_val;\n 671:\t\t\tif (is_otx2_sdp_rep(pfvf-\u003epdev)) {\n 672:\t\t\t\treq-\u003enum_regs++;\n 673:\t\t\t\treq-\u003ereg[2] = NIX_AF_TL4X_SDP_LINK_CFG(schq);\n 674:\t\t\t\treq-\u003eregval[2] = BIT_ULL(12) | BIT_ULL(13) |\n 675:\t\t\t\t\t\t (sdp_chan \u0026 0xff);\n 676:\t\t\t}\n 677:\t\t} else if (lvl == NIX_TXSCH_LVL_TL3) {\n 678:\t\t\tparent = schq_list[NIX_TXSCH_LVL_TL2][prio];\n 679:\t\t\treq-\u003ereg[0] = NIX_AF_TL3X_PARENT(schq);\n"
} |
| 29/2 | 2026/07/02 11:17 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 72701 output: 55 thoughts: 43 Thoughts: Here's my attempt at summarizing the thought process, as if I were having these thoughts: **Locating a Critical Function** Okay, so I need to understand where `otx2_pfaf_mbox_init` is defined. Let's see... I've been working with the Octeon TX2 driver code for a while, so it *should* be relatively straightforward. Hmm, that name rings a bell, it sounds pretty specific to the PFAF mailbox initialization, so it's probably related to some core network functionality on the Marvell Octeon TX2. Right, the drivers directory is where I should be looking, in the networking subsystem... specifically, the Ethernet drivers are the most likely candidate. Let's go through the Marvell specific directories... ah, there it is! `drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c`. Excellent, so it's in the `otx2_common.c` file within the Marvell Octeon TX2 NIC directory. That's exactly where I would expect to find a function initializing a PFAF mailbox on that platform. Now I can dive into the code and understand how the mailbox initialization is implemented. |
| 30/2 | 2026/07/02 11:17 | tool | grepper |
0mError:no matches Args: {
"Expression": "otx2_pfaf_mbox_init",
"PathPrefix": "drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c"
}Results: {
"error": "no matches"
} |
| 31/2 | 2026/07/02 11:17 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 73076 output: 48 thoughts: 25 Thoughts: Here's my thought process, summarized as you requested: **Debugging Initiative: Examining the nic Directory** Okay, so I've got a hunch something related to the OTX2 PFAF mailbox initialization might be off. My first instinct is to dig directly into the code. Given my familiarity with the codebase, I'm going to leverage `grep` – that's the fastest and most efficient way to pinpoint the relevant areas. I'm going to target the `nic` directory specifically, as that seems the most likely place to find anything related to network card functionality. The goal is a quick and dirty search for any instances of `otx2_pfaf_mbox_init`. Finding where this function is called, or even defined within the scope of this directory, is going to give me a good jumping-off point to assess the issue. This will give a good idea of which files and functions might be related to the suspected bug. |
| 32/2 | 2026/07/02 11:17 | tool | grepper |
0mArgs:{
"Expression": "otx2_pfaf_mbox_init",
"PathPrefix": "drivers/net/ethernet/marvell/octeontx2/nic/"
}Results: {
"Output": "drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h=1084=int otx2_check_pf_usable(struct otx2_nic *pf);\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h:1085:int otx2_pfaf_mbox_init(struct otx2_nic *pf);\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h-1086-int otx2_register_mbox_intr(struct otx2_nic *pf, bool probe_af);\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c=1194=EXPORT_SYMBOL(otx2_pfaf_mbox_destroy);\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c-1195-\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c:1196:int otx2_pfaf_mbox_init(struct otx2_nic *pf)\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c-1197-{\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c=3032=int otx2_init_rsrc(struct pci_dev *pdev, struct otx2_nic *pf)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c-3080-\t/* Init PF \u003c=\u003e AF mailbox stuff */\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c:3081:\terr = otx2_pfaf_mbox_init(pf);\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c-3082-\tif (err)\n"
} |
| 33/2 | 2026/07/02 11:17 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 73875 output: 54 thoughts: 26 Thoughts: Okay, let's dive into this. Here's my take on those instructions: **Analyzing `otx2_pfaf_mbox_init` in `otx2_pf.c`** Alright, the next step is to examine the `otx2_pfaf_mbox_init` function, which resides within the `otx2_pf.c` file. I'm keen to see how this initialization function handles the mailbox setup for the PF (Physical Function) side of the OTX2. I'll be looking for things like memory allocation, register writes, interrupt configuration, and any potential locking mechanisms. I need to understand the initial state of the mailbox and what structures are being used to manage it before any communication can occur. I'm anticipating some custom OTX2-specific register access, probably tied to the platform's interrupt controller. Let's get to it. |
| 34/2 | 2026/07/02 11:17 | tool | read-file |
0mArgs:{
"File": "drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c",
"FirstLine": 1196,
"LineCount": 50
}Results: {
"Contents": "1196:\tint otx2_pfaf_mbox_init(struct otx2_nic *pf)\n1197:\t{\n1198:\t\tstruct mbox *mbox = \u0026pf-\u003embox;\n1199:\t\tvoid __iomem *hwbase;\n1200:\t\tint err;\n1201:\t\n1202:\t\tmbox-\u003epfvf = pf;\n1203:\t\tpf-\u003embox_wq = alloc_ordered_workqueue(\"otx2_pfaf_mailbox\",\n1204:\t\t\t\t\t\t WQ_HIGHPRI | WQ_MEM_RECLAIM);\n1205:\t\tif (!pf-\u003embox_wq)\n1206:\t\t\treturn -ENOMEM;\n1207:\t\n1208:\t\t/* For CN20K, AF allocates mbox memory in DRAM and writes PF\n1209:\t\t * regions/offsets in RVU_MBOX_AF_PFX_ADDR, the RVU_PFX_FUNC_PFAF_MBOX\n1210:\t\t * gives the aliased address to access AF/PF mailbox regions.\n1211:\t\t */\n1212:\t\tif (is_cn20k(pf-\u003epdev))\n1213:\t\t\thwbase = pf-\u003ereg_base + RVU_PFX_FUNC_PFAF_MBOX +\n1214:\t\t\t\t((u64)BLKADDR_MBOX \u003c\u003c RVU_FUNC_BLKADDR_SHIFT);\n1215:\t\telse\n1216:\t\t\t/* Mailbox is a reserved memory (in RAM) region shared between\n1217:\t\t\t * admin function (i.e AF) and this PF, shouldn't be mapped as\n1218:\t\t\t * device memory to allow unaligned accesses.\n1219:\t\t\t */\n1220:\t\t\thwbase = ioremap_wc(pci_resource_start\n1221:\t\t\t\t\t (pf-\u003epdev, PCI_MBOX_BAR_NUM), MBOX_SIZE);\n1222:\t\tif (!hwbase) {\n1223:\t\t\tdev_err(pf-\u003edev, \"Unable to map PFAF mailbox region\\n\");\n1224:\t\t\terr = -ENOMEM;\n1225:\t\t\tgoto exit;\n1226:\t\t}\n1227:\t\n1228:\t\terr = otx2_mbox_init(\u0026mbox-\u003embox, hwbase, pf-\u003epdev, pf-\u003ereg_base,\n1229:\t\t\t\t MBOX_DIR_PFAF, 1);\n1230:\t\tif (err)\n1231:\t\t\tgoto exit;\n1232:\t\n1233:\t\terr = otx2_mbox_init(\u0026mbox-\u003embox_up, hwbase, pf-\u003epdev, pf-\u003ereg_base,\n1234:\t\t\t\t MBOX_DIR_PFAF_UP, 1);\n1235:\t\tif (err)\n1236:\t\t\tgoto exit;\n1237:\t\n1238:\t\terr = otx2_mbox_bbuf_init(mbox, pf-\u003epdev);\n1239:\t\tif (err)\n1240:\t\t\tgoto exit;\n1241:\t\n1242:\t\tINIT_WORK(\u0026mbox-\u003embox_wrk, otx2_pfaf_mbox_handler);\n1243:\t\tINIT_WORK(\u0026mbox-\u003embox_up_wrk, otx2_pfaf_mbox_up_handler);\n1244:\t\tmutex_init(\u0026mbox-\u003elock);\n1245:\t\n"
} |
| 35/2 | 2026/07/02 11:17 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 75031 output: 54 thoughts: 44 Thoughts: Here's my understanding of the process, breaking it down in my own thought process: **Initializing the System's Communication Hardware** Okay, so the first step here is pretty straightforward. I'm mapping a PCI BAR - that's the physical memory region used for the device's registers - and then setting up the mailbox. That's the foundation, establishing the communication pathway for the OTX2 device. It's like laying the groundwork for all future interactions. Next, I see a call to `otx2_register_mbox_intr`. This is where I'm enabling the mailbox interrupt - making it so that the system will be notified whenever there's something new in that mailbox. This is critical for any asynchronous communication. The interrupt mechanism allows the system to react in real-time to events signaled by the hardware. Finally, I'm integrating the NPA and NIX modules by calling `otx2_attach_npa_nix`. I interpret this to mean I'm attaching the Network Packet Accelerator (NPA) and Network Interface (NIX) to the whole process. These probably are dedicated hardware engines for packet processing and network connectivity. This attachment likely configures the data paths, resource allocations, and interdependencies to efficiently manage incoming and outgoing network traffic. |
| 36/2 | 2026/07/02 11:18 | tool | read-file |
0mArgs:{
"File": "drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c",
"FirstLine": 1252,
"LineCount": 50
}Results: {
"Contents": "1252:\tstatic int otx2_cgx_config_linkevents(struct otx2_nic *pf, bool enable)\n1253:\t{\n1254:\t\tstruct msg_req *msg;\n1255:\t\tint err;\n1256:\t\n1257:\t\tmutex_lock(\u0026pf-\u003embox.lock);\n1258:\t\tif (enable)\n1259:\t\t\tmsg = otx2_mbox_alloc_msg_cgx_start_linkevents(\u0026pf-\u003embox);\n1260:\t\telse\n1261:\t\t\tmsg = otx2_mbox_alloc_msg_cgx_stop_linkevents(\u0026pf-\u003embox);\n1262:\t\n1263:\t\tif (!msg) {\n1264:\t\t\tmutex_unlock(\u0026pf-\u003embox.lock);\n1265:\t\t\treturn -ENOMEM;\n1266:\t\t}\n1267:\t\n1268:\t\terr = otx2_sync_mbox_msg(\u0026pf-\u003embox);\n1269:\t\tmutex_unlock(\u0026pf-\u003embox.lock);\n1270:\t\treturn err;\n1271:\t}\n1272:\t\n1273:\tint otx2_reset_mac_stats(struct otx2_nic *pfvf)\n1274:\t{\n1275:\t\tstruct msg_req *req;\n1276:\t\tint err;\n1277:\t\n1278:\t\tmutex_lock(\u0026pfvf-\u003embox.lock);\n1279:\t\treq = otx2_mbox_alloc_msg_cgx_stats_rst(\u0026pfvf-\u003embox);\n1280:\t\tif (!req) {\n1281:\t\t\tmutex_unlock(\u0026pfvf-\u003embox.lock);\n1282:\t\t\treturn -ENOMEM;\n1283:\t\t}\n1284:\t\n1285:\t\terr = otx2_sync_mbox_msg(\u0026pfvf-\u003embox);\n1286:\t\tmutex_unlock(\u0026pfvf-\u003embox.lock);\n1287:\t\treturn err;\n1288:\t}\n1289:\t\n1290:\tstatic int otx2_cgx_config_loopback(struct otx2_nic *pf, bool enable)\n1291:\t{\n1292:\t\tstruct msg_req *msg;\n1293:\t\tint err;\n1294:\t\n1295:\t\tif (enable \u0026\u0026 !bitmap_empty(pf-\u003eflow_cfg-\u003edmacflt_bmap,\n1296:\t\t\t\t\t pf-\u003eflow_cfg-\u003edmacflt_max_flows))\n1297:\t\t\tnetdev_warn(pf-\u003enetdev,\n1298:\t\t\t\t \"CGX/RPM internal loopback might not work as DMAC filters are active\\n\");\n1299:\t\n1300:\t\tmutex_lock(\u0026pf-\u003embox.lock);\n1301:\t\tif (enable)\n"
} |
| 37/2 | 2026/07/02 11:18 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 76157 output: 47 thoughts: 179 Thoughts: **Analyzing the otx2_attach_npa_nix Function** Okay, so `otx2_attach_npa_nix` relies on `otx2_sync_mbox_msg`. And `otx2_sync_mbox_msg` waits for a response from the AF driver. Right. The implication is, if the AF driver isn't present, or if something like syzkaller's vhci doesn't fully emulate it, we're going to get a timeout error from `otx2_sync_mbox_msg`. Hang on, though… Syzkaller's vhci *doesn't* emulate the AF driver. It just throws back random data for memory-mapped I/O reads and ignores writes. That's a problem. But wait a minute. I seem to recall that `otx2_sync_mbox_msg` doesn't *just* wait for an interrupt. It either waits for that interrupt *or* polls a register. And if it polls a register... well, syzkaller will provide random data, which could *potentially* satisfy that polling condition! It's a long shot, but it is possible! Let's dig into `otx2_sync_mbox_msg` and see exactly how it works. I need to be sure. |
| 38/2 | 2026/07/02 11:18 | tool | grepper |
0mArgs:{
"Expression": "otx2_sync_mbox_msg",
"PathPrefix": "drivers/net/ethernet/marvell/octeontx2/nic/"
}Results: {
"Output": "\nFull output is too long, showing 500 out of 927 lines.\nUse more precise expression if possible.\n\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k.c=43=int cn10k_lmtst_init(struct otx2_nic *pfvf)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k.c-74-\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k.c:75:\terr = otx2_sync_mbox_msg(\u0026pfvf-\u003embox);\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k.c-76-\tmutex_unlock(\u0026pfvf-\u003embox.lock);\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k.c=89=int cn10k_sq_aq_init(void *dev, u16 qidx, u8 chan_offset, u16 sqb_aura)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k.c-119-\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k.c:120:\treturn otx2_sync_mbox_msg(\u0026pfvf-\u003embox);\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k.c-121-}\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k.c=182=int cn10k_free_all_ipolicers(struct otx2_nic *pfvf)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k.c-200-\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k.c:201:\trc = otx2_sync_mbox_msg(\u0026pfvf-\u003embox);\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k.c-202-out:\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k.c=207=int cn10k_alloc_leaf_profile(struct otx2_nic *pfvf, u16 *leaf)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k.c-218-\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k.c:219:\trc = otx2_sync_mbox_msg(\u0026pfvf-\u003embox);\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k.c-220-\tif (rc)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k.c=325=int cn10k_map_unmap_rq_policer(struct otx2_nic *pfvf, int rq_idx,\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k.c-354-\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k.c:355:\treturn otx2_sync_mbox_msg(\u0026pfvf-\u003embox);\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k.c-356-}\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k.c=358=int cn10k_free_leaf_profile(struct otx2_nic *pfvf, u16 leaf)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k.c-368-\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k.c:369:\treturn otx2_sync_mbox_msg(\u0026pfvf-\u003embox);\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k.c-370-}\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k.c=393=int cn10k_set_ipolicer_rate(struct otx2_nic *pfvf, u16 profile,\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k.c-493-\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k.c:494:\treturn otx2_sync_mbox_msg(\u0026pfvf-\u003embox);\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k.c-495-}\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_ipsec.c=49=static int cn10k_outb_cptlf_attach(struct otx2_nic *pf)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_ipsec.c-63-\t/* Send attach request to AF */\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_ipsec.c:64:\tret = otx2_sync_mbox_msg(\u0026pf-\u003embox);\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_ipsec.c-65-\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_ipsec.c=71=static int cn10k_outb_cptlf_detach(struct otx2_nic *pf)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_ipsec.c-84-\t/* Send detach request to AF */\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_ipsec.c:85:\tret = otx2_sync_mbox_msg(\u0026pf-\u003embox);\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_ipsec.c-86-\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_ipsec.c=92=static int cn10k_outb_cptlf_alloc(struct otx2_nic *pf)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_ipsec.c-106-\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_ipsec.c:107:\tret = otx2_sync_mbox_msg(\u0026pf-\u003embox);\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_ipsec.c-108-\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_ipsec.c=114=static void cn10k_outb_cptlf_free(struct otx2_nic *pf)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_ipsec.c-117-\totx2_mbox_alloc_msg_cpt_lf_free(\u0026pf-\u003embox);\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_ipsec.c:118:\totx2_sync_mbox_msg(\u0026pf-\u003embox);\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_ipsec.c-119-\tmutex_unlock(\u0026pf-\u003embox.lock);\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_ipsec.c=122=static int cn10k_outb_cptlf_config(struct otx2_nic *pf)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_ipsec.c-134-\treq-\u003enix_pf_func = pf-\u003epcifunc;\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_ipsec.c:135:\tret = otx2_sync_mbox_msg(\u0026pf-\u003embox);\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_ipsec.c-136-unlock:\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c=110=static int cn10k_mcs_alloc_rsrc(struct otx2_nic *pfvf, enum mcs_direction dir,\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c-127-\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c:128:\tret = otx2_sync_mbox_msg(mbox);\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c-129-\tif (ret)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c=168=static void cn10k_mcs_free_rsrc(struct otx2_nic *pfvf, enum mcs_direction dir,\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c-196-\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c:197:\tif (otx2_sync_mbox_msg(\u0026pfvf-\u003embox))\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c-198-\t\tgoto fail;\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c=229=static int cn10k_mcs_write_rx_secy(struct otx2_nic *pfvf,\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c-273-\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c:274:\tret = otx2_sync_mbox_msg(mbox);\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c-275-\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c=281=static int cn10k_mcs_write_rx_flowid(struct otx2_nic *pfvf,\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c-319-\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c:320:\tret = otx2_sync_mbox_msg(mbox);\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c-321-\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c=327=static int cn10k_mcs_write_sc_cam(struct otx2_nic *pfvf,\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c-346-\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c:347:\tret = otx2_sync_mbox_msg(mbox);\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c-348-\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c=391=static int cn10k_mcs_write_rx_sa_plcy(struct otx2_nic *pfvf,\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c-432-\t/* Send two messages together */\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c:433:\tret = otx2_sync_mbox_msg(mbox);\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c-434-\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c=440=static int cn10k_mcs_write_rx_sa_pn(struct otx2_nic *pfvf,\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c-459-\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c:460:\tret = otx2_sync_mbox_msg(mbox);\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c-461-\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c=467=static int cn10k_mcs_write_tx_secy(struct otx2_nic *pfvf,\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c-542-\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c:543:\tret = otx2_sync_mbox_msg(mbox);\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c-544-\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c=550=static int cn10k_mcs_write_tx_flowid(struct otx2_nic *pfvf,\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c-588-\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c:589:\tret = otx2_sync_mbox_msg(mbox);\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c-590-\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c=596=static int cn10k_mcs_link_tx_sa2sc(struct otx2_nic *pfvf,\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c-622-\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c:623:\tret = otx2_sync_mbox_msg(mbox);\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c-624-\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c=630=static int cn10k_mcs_write_tx_sa_plcy(struct otx2_nic *pfvf,\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c-658-\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c:659:\tret = otx2_sync_mbox_msg(mbox);\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c-660-\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c=666=static int cn10k_write_tx_sa_pn(struct otx2_nic *pfvf,\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c-685-\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c:686:\tret = otx2_sync_mbox_msg(mbox);\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c-687-\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c=693=static int cn10k_mcs_ena_dis_flowid(struct otx2_nic *pfvf, u16 hw_flow_id,\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c-711-\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c:712:\tret = otx2_sync_mbox_msg(mbox);\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c-713-\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c=719=static int cn10k_mcs_sa_stats(struct otx2_nic *pfvf, u8 hw_sa_id,\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c-752-send_msg:\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c:753:\tret = otx2_sync_mbox_msg(mbox);\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c-754-\tif (ret)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c=774=static int cn10k_mcs_sc_stats(struct otx2_nic *pfvf, u8 hw_sc_id,\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c-807-send_msg:\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c:808:\tret = otx2_sync_mbox_msg(mbox);\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c-809-\tif (ret)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c=829=static int cn10k_mcs_secy_stats(struct otx2_nic *pfvf, u8 hw_secy_id,\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c-862-send_msg:\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c:863:\tret = otx2_sync_mbox_msg(mbox);\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c-864-\tif (ret)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c=1738=int cn10k_mcs_init(struct otx2_nic *pfvf)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c-1765-\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c:1766:\tif (otx2_sync_mbox_msg(mbox))\ndrivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c-1767-\t\tgoto fail;\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/cn20k.c=270=int cn20k_tc_free_mcam_entry(struct otx2_nic *nic, u16 entry)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/cn20k.c-283-\t/* Send message to AF to free MCAM entries */\ndrivers/net/ethernet/marvell/octeontx2/nic/cn20k.c:284:\terr = otx2_sync_mbox_msg(\u0026nic-\u003embox);\ndrivers/net/ethernet/marvell/octeontx2/nic/cn20k.c-285-\tif (err) {\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/cn20k.c=437=static int __cn20k_tc_alloc_entry(struct otx2_nic *nic,\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/cn20k.c-465-\ndrivers/net/ethernet/marvell/octeontx2/nic/cn20k.c:466:\tret = otx2_sync_mbox_msg(\u0026nic-\u003embox);\ndrivers/net/ethernet/marvell/octeontx2/nic/cn20k.c-467-\tif (ret)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/cn20k.c=520=static int cn20k_aura_aq_init(struct otx2_nic *pfvf, int aura_id,\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/cn20k.c-542-\t\t/* Shared mbox memory buffer is full, flush it and retry */\ndrivers/net/ethernet/marvell/octeontx2/nic/cn20k.c:543:\t\terr = otx2_sync_mbox_msg(\u0026pfvf-\u003embox);\ndrivers/net/ethernet/marvell/octeontx2/nic/cn20k.c-544-\t\tif (err)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/cn20k.c=597=static int cn20k_pool_aq_init(struct otx2_nic *pfvf, u16 pool_id,\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/cn20k.c-618-\t\t/* Shared mbox memory buffer is full, flush it and retry */\ndrivers/net/ethernet/marvell/octeontx2/nic/cn20k.c:619:\t\terr = otx2_sync_mbox_msg(\u0026pfvf-\u003embox);\ndrivers/net/ethernet/marvell/octeontx2/nic/cn20k.c-620-\t\tif (err) {\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/cn20k.c=668=static int cn20k_sq_aq_init(void *dev, u16 qidx, u8 chan_offset, u16 sqb_aura)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/cn20k.c-698-\ndrivers/net/ethernet/marvell/octeontx2/nic/cn20k.c:699:\treturn otx2_sync_mbox_msg(\u0026pfvf-\u003embox);\ndrivers/net/ethernet/marvell/octeontx2/nic/cn20k.c-700-}\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c=53=void otx2_update_lmac_stats(struct otx2_nic *pfvf)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c-66-\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c:67:\totx2_sync_mbox_msg(\u0026pfvf-\u003embox);\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c-68-\tmutex_unlock(\u0026pfvf-\u003embox.lock);\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c=71=void otx2_update_lmac_fec_stats(struct otx2_nic *pfvf)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c-79-\tif (req)\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c:80:\t\totx2_sync_mbox_msg(\u0026pfvf-\u003embox);\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c-81-\tmutex_unlock(\u0026pfvf-\u003embox.lock);\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c=159=static int otx2_hw_set_mac_addr(struct otx2_nic *pfvf, u8 *mac)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c-172-\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c:173:\terr = otx2_sync_mbox_msg(\u0026pfvf-\u003embox);\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c-174-\tmutex_unlock(\u0026pfvf-\u003embox.lock);\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c=178=static int otx2_hw_get_mac_addr(struct otx2_nic *pfvf,\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c-192-\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c:193:\terr = otx2_sync_mbox_msg(\u0026pfvf-\u003embox);\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c-194-\tif (err) {\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c=236=int otx2_hw_set_mtu(struct otx2_nic *pfvf, int mtu)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c-256-\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c:257:\terr = otx2_sync_mbox_msg(\u0026pfvf-\u003embox);\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c-258-\tmutex_unlock(\u0026pfvf-\u003embox.lock);\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c=263=int otx2_config_pause_frm(struct otx2_nic *pfvf)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c-281-\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c:282:\terr = otx2_sync_mbox_msg(\u0026pfvf-\u003embox);\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c-283-unlock:\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c=289=int otx2_set_flowkey_cfg(struct otx2_nic *pfvf)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c-305-\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c:306:\terr = otx2_sync_mbox_msg(\u0026pfvf-\u003embox);\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c-307-\tif (err)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c=323=int otx2_set_rss_table(struct otx2_nic *pfvf, int ctx_id, const u32 *ind_tbl)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c-343-\t\t\t */\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c:344:\t\t\terr = otx2_sync_mbox_msg(mbox);\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c-345-\t\t\tif (err) {\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c-362-\t}\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c:363:\terr = otx2_sync_mbox_msg(mbox);\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c-364-\tmutex_unlock(\u0026mbox-\u003elock);\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c=462=void otx2_setup_segmentation(struct otx2_nic *pfvf)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c-478-\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c:479:\terr = otx2_sync_mbox_msg(\u0026pfvf-\u003embox);\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c-480-\tif (err)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c-497-\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c:498:\terr = otx2_sync_mbox_msg(\u0026pfvf-\u003embox);\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c-499-\tif (err)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c=617=int otx2_txschq_config(struct otx2_nic *pfvf, int lvl, int prio, bool txschq_for_pfc)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c-731-\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c:732:\treturn otx2_sync_mbox_msg(\u0026pfvf-\u003embox);\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c-733-}\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c=736=int otx2_smq_flush(struct otx2_nic *pfvf, int smq)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c-753-\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c:754:\trc = otx2_sync_mbox_msg(\u0026pfvf-\u003embox);\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c-755-\tmutex_unlock(\u0026pfvf-\u003embox.lock);\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c=760=int otx2_txsch_alloc(struct otx2_nic *pfvf)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c-780-\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c:781:\trc = otx2_sync_mbox_msg(\u0026pfvf-\u003embox);\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c-782-\tif (rc)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c=804=void otx2_txschq_free_one(struct otx2_nic *pfvf, u16 lvl, u16 schq)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c-821-\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c:822:\terr = otx2_sync_mbox_msg(\u0026pfvf-\u003embox);\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c-823-\tif (err) {\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c=890=int otx2_rq_init(struct otx2_nic *pfvf, u16 qidx, u16 lpb_aura)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c-919-\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c:920:\treturn otx2_sync_mbox_msg(\u0026pfvf-\u003embox);\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c-921-}\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c=923=int otx2_sq_aq_init(void *dev, u16 qidx, u8 chan_offset, u16 sqb_aura)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c-956-\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c:957:\treturn otx2_sync_mbox_msg(\u0026pfvf-\u003embox);\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c-958-}\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c=1051=int otx2_cq_init(struct otx2_nic *pfvf, u16 qidx)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c-1150-\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c:1151:\treturn otx2_sync_mbox_msg(\u0026pfvf-\u003embox);\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c-1152-}\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c=1219=int otx2_config_nix(struct otx2_nic *pfvf)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c-1248-\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c:1249:\terr = otx2_sync_mbox_msg(\u0026pfvf-\u003embox);\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c-1250-\tif (err)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c=1376=int otx2_aura_aq_init(struct otx2_nic *pfvf, int aura_id,\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c-1397-\t\t/* Shared mbox memory buffer is full, flush it and retry */\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c:1398:\t\terr = otx2_sync_mbox_msg(\u0026pfvf-\u003embox);\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c-1399-\t\tif (err)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c=1460=int otx2_pool_aq_init(struct otx2_nic *pfvf, u16 pool_id,\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c-1481-\t\t/* Shared mbox memory buffer is full, flush it and retry */\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c:1482:\t\terr = otx2_sync_mbox_msg(\u0026pfvf-\u003embox);\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c-1483-\t\tif (err) {\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c=1542=int otx2_sq_aura_pool_init(struct otx2_nic *pfvf)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c-1578-\t/* Flush accumulated messages */\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c:1579:\terr = otx2_sync_mbox_msg(\u0026pfvf-\u003embox);\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c-1580-\tif (err)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c=1623=int otx2_rq_aura_pool_init(struct otx2_nic *pfvf)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c-1650-\t/* Flush accumulated messages */\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c:1651:\terr = otx2_sync_mbox_msg(\u0026pfvf-\u003embox);\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c-1652-\tif (err)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c=1681=int otx2_config_npa(struct otx2_nic *pfvf)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c-1709-\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c:1710:\treturn otx2_sync_mbox_msg(\u0026pfvf-\u003embox);\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c-1711-}\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c=1713=int otx2_detach_resources(struct mbox *mbox)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c-1727-\t/* Send detach request to AF */\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c:1728:\totx2_sync_mbox_msg(mbox);\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c-1729-\tmutex_unlock(\u0026mbox-\u003elock);\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c=1734=int otx2_attach_npa_nix(struct otx2_nic *pfvf)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c-1751-\t/* Send attach request to AF */\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c:1752:\terr = otx2_sync_mbox_msg(\u0026pfvf-\u003embox);\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c-1753-\tif (err) {\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c-1772-\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c:1773:\terr = otx2_sync_mbox_msg(\u0026pfvf-\u003embox);\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c-1774-\tif (err) {\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c=1791=void otx2_ctx_disable(struct mbox *mbox, int type, bool npa)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c-1808-\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c:1809:\tif (otx2_sync_mbox_msg(mbox))\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c-1810-\t\tdev_err(mbox-\u003epfvf-\u003edev, \"%s failed to disable context\\n\",\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c=1816=int otx2_nix_config_bp(struct otx2_nic *pfvf, bool enable)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c-1836-\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c:1837:\treturn otx2_sync_mbox_msg(\u0026pfvf-\u003embox);\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c-1838-}\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c=1841=int otx2_nix_cpt_config_bp(struct otx2_nic *pfvf, bool enable)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c-1861-\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c:1862:\treturn otx2_sync_mbox_msg(\u0026pfvf-\u003embox);\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c-1863-}\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c=1982=u16 otx2_get_max_mtu(struct otx2_nic *pfvf)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c-1996-\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c:1997:\trc = otx2_sync_mbox_msg(\u0026pfvf-\u003embox);\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c-1998-\tif (!rc) {\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c=2066=int otx2_set_hw_capabilities(struct otx2_nic *pfvf)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c-2079-\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c:2080:\tret = otx2_sync_mbox_msg(mbox);\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c-2081-\tif (ret)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h=875=static inline int otx2_get_pool_idx(struct otx2_nic *pfvf, int type, int idx)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h-884-/* Mbox APIs */\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h:885:static inline int otx2_sync_mbox_msg(struct mbox *mbox)\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h-886-{\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h=899=static inline int otx2_sync_mbox_up_msg(struct mbox *mbox, int devid)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h-915- */\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h:916:static inline int otx2_sync_mbox_msg_busy_poll(struct mbox *mbox)\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h-917-{\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_dcbnl.c=59=static int otx2_pfc_txschq_alloc_one(struct otx2_nic *pfvf, u8 prio)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_dcbnl.c-76-\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_dcbnl.c:77:\trc = otx2_sync_mbox_msg(\u0026pfvf-\u003embox);\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_dcbnl.c-78-\tif (rc)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_dcbnl.c=141=static int otx2_pfc_update_sq_smq_mapping(struct otx2_nic *pfvf, int prio)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_dcbnl.c-186-\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_dcbnl.c:187:\totx2_sync_mbox_msg(\u0026pfvf-\u003embox);\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_dcbnl.c-188-\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_dcbnl.c=290=int otx2_config_priority_flow_ctrl(struct otx2_nic *pfvf)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_dcbnl.c-314-\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_dcbnl.c:315:\tif (!otx2_sync_mbox_msg(\u0026pfvf-\u003embox)) {\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_dcbnl.c-316-\t\trsp = (struct cgx_pfc_rsp *)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_dcbnl.c=335=void otx2_update_bpid_in_rqctx(struct otx2_nic *pfvf, int vlan_prio, int qidx,\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_dcbnl.c-370-\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_dcbnl.c:371:\totx2_sync_mbox_msg(\u0026pfvf-\u003embox);\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_dcbnl.c-372-\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_dcbnl.c-384-\tnpa_aq-\u003eop = NPA_AQ_INSTOP_WRITE;\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_dcbnl.c:385:\totx2_sync_mbox_msg(\u0026pfvf-\u003embox);\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_dcbnl.c-386-\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_dmac_flt.c=10=static int otx2_dmacflt_do_add(struct otx2_nic *pf, const u8 *mac,\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_dmac_flt.c-25-\tether_addr_copy(req-\u003emac_addr, mac);\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_dmac_flt.c:26:\terr = otx2_sync_mbox_msg(\u0026pf-\u003embox);\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_dmac_flt.c-27-\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_dmac_flt.c=43=static int otx2_dmacflt_add_pfmac(struct otx2_nic *pf, u32 *dmac_index)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_dmac_flt.c-59-\tether_addr_copy(req-\u003emac_addr, pf-\u003enetdev-\u003edev_addr);\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_dmac_flt.c:60:\terr = otx2_sync_mbox_msg(\u0026pf-\u003embox);\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_dmac_flt.c-61-\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_dmac_flt.c=94=static int otx2_dmacflt_do_remove(struct otx2_nic *pfvf, const u8 *mac,\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_dmac_flt.c-108-\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_dmac_flt.c:109:\terr = otx2_sync_mbox_msg(\u0026pfvf-\u003embox);\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_dmac_flt.c-110-\tmutex_unlock(\u0026pfvf-\u003embox.lock);\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_dmac_flt.c=115=static int otx2_dmacflt_remove_pfmac(struct otx2_nic *pf, u32 dmac_index)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_dmac_flt.c-127-\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_dmac_flt.c:128:\terr = otx2_sync_mbox_msg(\u0026pf-\u003embox);\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_dmac_flt.c-129-\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_dmac_flt.c=149=int otx2_dmacflt_get_max_cnt(struct otx2_nic *pf)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_dmac_flt.c-162-\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_dmac_flt.c:163:\terr = otx2_sync_mbox_msg(\u0026pf-\u003embox);\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_dmac_flt.c-164-\tif (err)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_dmac_flt.c=182=int otx2_dmacflt_update(struct otx2_nic *pf, u8 *mac, u32 bit_pos)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_dmac_flt.c-201-\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_dmac_flt.c:202:\trc = otx2_sync_mbox_msg(\u0026pf-\u003embox);\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_dmac_flt.c-203-\tif (rc)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c=168=static int otx2_get_phy_fec_stats(struct otx2_nic *pfvf)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c-177-\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c:178:\tif (!otx2_sync_mbox_msg(\u0026pfvf-\u003embox))\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c-179-\t\trc = 0;\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c=315=static void otx2_get_pauseparam(struct net_device *netdev,\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c-330-\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c:331:\tif (!otx2_sync_mbox_msg(\u0026pfvf-\u003embox)) {\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c-332-\t\trsp = (struct cgx_pause_frm_cfg *)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c=1001=static struct cgx_fw_data *otx2_get_fwdata(struct otx2_nic *pfvf)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c-1013-\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c:1014:\terr = otx2_sync_mbox_msg(\u0026pfvf-\u003embox);\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c-1015-\tif (!err) {\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c=1052=static int otx2_set_fecparam(struct net_device *netdev,\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c-1087-\treq-\u003efec = fec;\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c:1088:\terr = otx2_sync_mbox_msg(\u0026pfvf-\u003embox);\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c-1089-\tif (err)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c=1236=static int otx2_set_link_ksettings(struct net_device *netdev,\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c-1290-\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c:1291:\terr = otx2_sync_mbox_msg(\u0026pf-\u003embox);\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c-1292-end:\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_flows.c=40=static int otx2_mcam_pfl_info_get(struct otx2_nic *pfvf, u16 *x4_slots, u8 *kw_type)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_flows.c-67-\t/* Send message to AF */\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_flows.c:68:\tif (otx2_sync_mbox_msg(\u0026pfvf-\u003embox)) {\n\n"
} |
| 39/2 | 2026/07/02 11:18 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 91992 output: 54 thoughts: 117 Thoughts: **Analyzing a Potential Driver Initialization Issue** Okay, so I'm tracing through this initialization sequence, and something's pinging my radar. Specifically, `otx2_sync_mbox_msg` calls `otx2_mbox_wait_for_rsp`. My concern here is that if the AF driver isn't present, the mailbox interrupt, which is critical for that response, won't be triggered. This means that `otx2_sync_mbox_msg` will inevitably time out. Now, here's where it gets interesting – and potentially problematic. A timeout in `otx2_sync_mbox_msg`, while perhaps recoverable at the moment, could mislead us later. Even if `otx2_pfaf_mbox_init` somehow manages to succeed in this scenario, a subsequent call to `otx2_attach_npa_nix` could fail down the line, due to this earlier timeout. It feels like a subtle race condition that needs immediate checking. Therefore, I'm going to shift my focus immediately to scrutinize `otx2_attach_npa_nix`. It’s where the root of the problem seems to be. |
| 40/2 | 2026/07/02 11:18 | tool | read-file |
0mArgs:{
"File": "drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c",
"FirstLine": 1734,
"LineCount": 50
}Results: {
"Contents": "1734:\tint otx2_attach_npa_nix(struct otx2_nic *pfvf)\n1735:\t{\n1736:\t\tstruct rsrc_attach *attach;\n1737:\t\tstruct msg_req *msix;\n1738:\t\tint err;\n1739:\t\n1740:\t\tmutex_lock(\u0026pfvf-\u003embox.lock);\n1741:\t\t/* Get memory to put this msg */\n1742:\t\tattach = otx2_mbox_alloc_msg_attach_resources(\u0026pfvf-\u003embox);\n1743:\t\tif (!attach) {\n1744:\t\t\tmutex_unlock(\u0026pfvf-\u003embox.lock);\n1745:\t\t\treturn -ENOMEM;\n1746:\t\t}\n1747:\t\n1748:\t\tattach-\u003enpalf = true;\n1749:\t\tattach-\u003enixlf = true;\n1750:\t\n1751:\t\t/* Send attach request to AF */\n1752:\t\terr = otx2_sync_mbox_msg(\u0026pfvf-\u003embox);\n1753:\t\tif (err) {\n1754:\t\t\tmutex_unlock(\u0026pfvf-\u003embox.lock);\n1755:\t\t\treturn err;\n1756:\t\t}\n1757:\t\n1758:\t\tpfvf-\u003enix_blkaddr = BLKADDR_NIX0;\n1759:\t\n1760:\t\t/* If the platform has two NIX blocks then LF may be\n1761:\t\t * allocated from NIX1.\n1762:\t\t */\n1763:\t\tif (otx2_read64(pfvf, RVU_PF_BLOCK_ADDRX_DISC(BLKADDR_NIX1)) \u0026 0x1FFULL)\n1764:\t\t\tpfvf-\u003enix_blkaddr = BLKADDR_NIX1;\n1765:\t\n1766:\t\t/* Get NPA and NIX MSIX vector offsets */\n1767:\t\tmsix = otx2_mbox_alloc_msg_msix_offset(\u0026pfvf-\u003embox);\n1768:\t\tif (!msix) {\n1769:\t\t\tmutex_unlock(\u0026pfvf-\u003embox.lock);\n1770:\t\t\treturn -ENOMEM;\n1771:\t\t}\n1772:\t\n1773:\t\terr = otx2_sync_mbox_msg(\u0026pfvf-\u003embox);\n1774:\t\tif (err) {\n1775:\t\t\tmutex_unlock(\u0026pfvf-\u003embox.lock);\n1776:\t\t\treturn err;\n1777:\t\t}\n1778:\t\tmutex_unlock(\u0026pfvf-\u003embox.lock);\n1779:\t\n1780:\t\tif (pfvf-\u003ehw.npa_msixoff == MSIX_VECTOR_INVALID ||\n1781:\t\t pfvf-\u003ehw.nix_msixoff == MSIX_VECTOR_INVALID) {\n1782:\t\t\tdev_err(pfvf-\u003edev,\n1783:\t\t\t\t\"RVUPF: Invalid MSIX vector offset for NPA/NIX\\n\");\n"
} |
| 41/2 | 2026/07/02 11:18 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 93215 output: 52 thoughts: 226 Thoughts: **My Analysis of the Octeon TX2 Driver Probe Failure** Okay, so I'm looking at this driver's probe sequence and trying to understand a potential failure scenario. It seems pretty straightforward: if `otx2_sync_mbox_msg` fails, we're dead in the water. That triggers a cascading failure: `otx2_attach_npa_nix` returns an error, then `otx2_init_rsrc` fails, and finally, `otx2_probe` bails out. Basically, the driver won't even *attempt* to initialize itself successfully unless the AF driver is present and the mailbox communication is rock solid. Makes sense. But then I had a thought. Is the AF driver a completely separate PCI device? Right, `rvu_af` is a dedicated PCI driver for `PCI_DEVID_OCTEONTX2_RVU_AF`. So, if Syzkaller injected *both* the AF device and the PF device, would they be able to talk to each other? How does the mailbox communication actually work? I need to think this through carefully. The mailbox relies on shared memory and interrupts, which is pretty standard. `otx2_mbox_init` maps a BAR. If Syzkaller's vhci is being used, that BAR will be backed by... well, dummy memory, right? And that's the problem. The hardware isn't going to generate interrupts in that setup. So I am pretty sure the mailbox communication would fail. Wait a second, does the AF driver *poll* for messages or does it actually use interrupts? I need to check the code! I should really dig into the `rvu_af` driver and see how it handles this. That will give me the answer. |
| 42/2 | 2026/07/02 11:18 | tool | read-file |
0mArgs:{
"File": "drivers/net/ethernet/marvell/octeontx2/af/rvu.c",
"FirstLine": 3698,
"LineCount": 100
}Results: {
"Contents": "3698:\t\n3699:\t\terr = pci_request_regions(pdev, DRV_NAME);\n3700:\t\tif (err) {\n3701:\t\t\tdev_err(dev, \"PCI request regions failed 0x%x\\n\", err);\n3702:\t\t\tgoto err_disable_device;\n3703:\t\t}\n3704:\t\n3705:\t\terr = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(48));\n3706:\t\tif (err) {\n3707:\t\t\tdev_err(dev, \"DMA mask config failed, abort\\n\");\n3708:\t\t\tgoto err_release_regions;\n3709:\t\t}\n3710:\t\n3711:\t\tpci_set_master(pdev);\n3712:\t\n3713:\t\trvu-\u003eptp = ptp_get();\n3714:\t\tif (IS_ERR(rvu-\u003eptp)) {\n3715:\t\t\terr = PTR_ERR(rvu-\u003eptp);\n3716:\t\t\tif (err)\n3717:\t\t\t\tgoto err_release_regions;\n3718:\t\t\trvu-\u003eptp = NULL;\n3719:\t\t}\n3720:\t\n3721:\t\t/* Map Admin function CSRs */\n3722:\t\trvu-\u003eafreg_base = pcim_iomap(pdev, PCI_AF_REG_BAR_NUM, 0);\n3723:\t\trvu-\u003epfreg_base = pcim_iomap(pdev, PCI_PF_REG_BAR_NUM, 0);\n3724:\t\tif (!rvu-\u003eafreg_base || !rvu-\u003epfreg_base) {\n3725:\t\t\tdev_err(dev, \"Unable to map admin function CSRs, aborting\\n\");\n3726:\t\t\terr = -ENOMEM;\n3727:\t\t\tgoto err_put_ptp;\n3728:\t\t}\n3729:\t\n3730:\t\t/* Store module params in rvu structure */\n3731:\t\trvu_update_module_params(rvu);\n3732:\t\n3733:\t\t/* Check which blocks the HW supports */\n3734:\t\trvu_check_block_implemented(rvu);\n3735:\t\n3736:\t\trvu_reset_all_blocks(rvu);\n3737:\t\n3738:\t\trvu_setup_hw_capabilities(rvu);\n3739:\t\n3740:\t\terr = rvu_setup_hw_resources(rvu);\n3741:\t\tif (err)\n3742:\t\t\tgoto err_put_ptp;\n3743:\t\n3744:\t\t/* Init mailbox btw AF and PFs */\n3745:\t\terr = rvu_mbox_init(rvu, \u0026rvu-\u003eafpf_wq_info, TYPE_AFPF,\n3746:\t\t\t\t rvu-\u003ehw-\u003etotal_pfs, rvu_afpf_mbox_handler,\n3747:\t\t\t\t rvu_afpf_mbox_up_handler);\n3748:\t\tif (err) {\n3749:\t\t\tdev_err(dev, \"%s: Failed to initialize mbox\\n\", __func__);\n3750:\t\t\tgoto err_hwsetup;\n3751:\t\t}\n3752:\t\n3753:\t\terr = rvu_flr_init(rvu);\n3754:\t\tif (err) {\n3755:\t\t\tdev_err(dev, \"%s: Failed to initialize flr\\n\", __func__);\n3756:\t\t\tgoto err_mbox;\n3757:\t\t}\n3758:\t\n3759:\t\terr = rvu_register_interrupts(rvu);\n3760:\t\tif (err) {\n3761:\t\t\tdev_err(dev, \"%s: Failed to register interrupts\\n\", __func__);\n3762:\t\t\tgoto err_flr;\n3763:\t\t}\n3764:\t\n3765:\t\terr = rvu_register_dl(rvu);\n3766:\t\tif (err) {\n3767:\t\t\tdev_err(dev, \"%s: Failed to register devlink\\n\", __func__);\n3768:\t\t\tgoto err_irq;\n3769:\t\t}\n3770:\t\n3771:\t\trvu_setup_rvum_blk_revid(rvu);\n3772:\t\n3773:\t\t/* Enable AF's VFs (if any) */\n3774:\t\terr = rvu_enable_sriov(rvu);\n3775:\t\tif (err) {\n3776:\t\t\tdev_err(dev, \"%s: Failed to enable sriov\\n\", __func__);\n3777:\t\t\tgoto err_dl;\n3778:\t\t}\n3779:\t\n3780:\t\t/* Initialize debugfs */\n3781:\t\trvu_dbg_init(rvu);\n3782:\t\n3783:\t\tmutex_init(\u0026rvu-\u003erswitch.switch_lock);\n3784:\t\n3785:\t\tif (rvu-\u003efwdata)\n3786:\t\t\tptp_start(rvu, rvu-\u003efwdata-\u003esclk, rvu-\u003efwdata-\u003eptp_ext_clk_rate,\n3787:\t\t\t\t rvu-\u003efwdata-\u003eptp_ext_tstamp);\n3788:\t\n3789:\t\t/* Alloc CINT and QINT memory */\n3790:\t\trvu_alloc_cint_qint_mem(rvu, \u0026rvu-\u003epf[RVU_AFPF], BLKADDR_NIX0,\n3791:\t\t\t\t\t(rvu-\u003ehw-\u003eblock[BLKADDR_NIX0].lf.max));\n3792:\t\treturn 0;\n3793:\terr_dl:\n3794:\t\trvu_unregister_dl(rvu);\n3795:\terr_irq:\n3796:\t\trvu_unregister_interrupts(rvu);\n3797:\terr_flr:\n"
} |
| 43/2 | 2026/07/02 11:18 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 95468 output: 47 thoughts: 344 Thoughts: **Analyzing the OcteonTX2 Driver Probe Sequence** Okay, so I'm digging into this `rvu_probe` function. It looks like the critical path hinges on whether the hardware is properly initialized. Specifically, `rvu_check_block_implemented` seems to be the gatekeeper, reading registers to determine what's active. If it reads zeros, it could bail out or make assumptions, and that's not ideal for my purposes. Then `rvu_setup_hw_resources` gets called, setting up the hardware environment. The issue is this: if we're dealing with syzkaller's vhci, the AF and PF drivers are probably not going to probe and communicate via mailbox successfully. The mailbox communication relies on those critical hardware interrupts and shared memory structures, which I know vhci doesn't emulate. It's just fuzzing the PCI interface! Wait a second… that means the driver is unlikely to ever hit the `OCTEONTX_SWITCH` code, which is what I really need to reach. I'm starting to think about whether there's some alternate path. Is there *any* way to get to that switch code without the hardware? It's in `sw_nb.c`, `sw_fdb.c`, `sw_fib.c`, and `sw_fl.c`, all files that register notifiers. Ah, the notifiers! These notifiers are registered in `sw_nb_register`, and that function is called from `rvu_eswitch_config`, which itself is called from `rvu_rep_create`. And *that* function, `rvu_rep_create`, is called from `otx2_devlink_eswitch_mode_set`. So, to get to that `otx2_devlink_eswitch_mode_set`, which is a key to hitting that switch code, the devlink device needs to be registered first. I need to be sure devlink is active before all of this is triggered. Is devlink registered in `otx2_probe`? Let's check `otx2_probe` for the devlink registration… I think I'm onto something here. |
| 44/2 | 2026/07/02 11:18 | tool | grepper |
0mError:no matches Args: {
"Expression": "devlink",
"PathPrefix": "drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c"
}Results: {
"error": "no matches"
} |
| 45/2 | 2026/07/02 11:18 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 96344 output: 45 thoughts: 48 Thoughts: **Investigating OTX2 Device Link Registration** Okay, so I see that `otx2_devlink.c` has that `otx2_devlink_eswitch_mode_set` function. My next logical step is to trace back and understand how the device link itself is registered or initialized. I'll need to figure out where the `otx2_register_dl` function (or something with a similar registration function) is called. That call is where the process begins, the starting point of the devlink's setup. From there, I can determine the relationships. |
| 46/2 | 2026/07/02 11:18 | tool | grepper |
0mArgs:{
"Expression": "otx2_register_dl",
"PathPrefix": "drivers/net/ethernet/marvell/octeontx2/nic/"
}Results: {
"Output": "drivers/net/ethernet/marvell/octeontx2/nic/otx2_devlink.c=192=static const struct devlink_ops otx2_devlink_ops = {\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_devlink.c-198-\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_devlink.c:199:int otx2_register_dl(struct otx2_nic *pfvf)\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_devlink.c-200-{\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_devlink.c-231-}\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_devlink.c:232:EXPORT_SYMBOL(otx2_register_dl);\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_devlink.c-233-\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_devlink.h=11=struct otx2_devlink {\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_devlink.h-16-/* Devlink APIs */\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_devlink.h:17:int otx2_register_dl(struct otx2_nic *pfvf);\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_devlink.h-18-void otx2_unregister_dl(struct otx2_nic *pfvf);\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c=3124=static int otx2_probe(struct pci_dev *pdev, const struct pci_device_id *id)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c-3277-\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c:3278:\terr = otx2_register_dl(pf);\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c-3279-\tif (err)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c=588=static int otx2vf_probe(struct pci_dev *pdev, const struct pci_device_id *id)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c-776-\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c:777:\terr = otx2_register_dl(vf);\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c-778-\tif (err)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/rep.c=781=static int rvu_rep_probe(struct pci_dev *pdev, const struct pci_device_id *id)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/rep.c-835-\ndrivers/net/ethernet/marvell/octeontx2/nic/rep.c:836:\terr = otx2_register_dl(priv);\ndrivers/net/ethernet/marvell/octeontx2/nic/rep.c-837-\tif (err)\n"
} |
| 47/2 | 2026/07/02 11:18 | llm | patch-evaluator |
1mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 97542 output: 41 thoughts: 241 Thoughts: **My Analysis of the OTX2 Driver Initialization** Okay, so here's the chain of events I'm tracking in the OTX2 driver initialization. `otx2_register_dl` is called during the `otx2_probe` process. I remember that `otx2_probe` is very likely to fail early on, specifically during `otx2_init_rsrc`. This is because it requires successful mailbox communication with the AF driver, which isn't always guaranteed, especially during initial bring-up or if there are AF driver issues. If `otx2_probe` fails at that early step, the devlink device registration is bypassed. This is crucial because it immediately blocks `otx2_devlink_eswitch_mode_set` from being called. And, if `otx2_devlink_eswitch_mode_set` can't run, `rvu_rep_create` also never gets invoked. Skipping `rvu_rep_create` means `rvu_eswitch_config` is completely missed as well. Then, the real kicker: if `rvu_eswitch_config` isn't called, then `sw_nb_register` doesn't get called. That's the core of the issue: if `sw_nb_register` isn't called, then none of the notifiers used by the switch code are registered. And if those notifiers aren't registered, then practically none of the switch-related code is reachable during runtime. This indicates a very serious failure path – essentially, the switch functionality is completely disabled if this initialization sequence fails. But, I need to take a step back and make sure I'm not missing something. Are there *any* other paths that lead to the execution of `sw_nb_register`? I need to be sure this is the only dependency so that I can understand and potentially refactor the driver to be more robust. I need to make sure this is the only point of failure. |
| 48/2 | 2026/07/02 11:19 | tool | grepper |
0mArgs:{
"Expression": "sw_nb_register",
"PathPrefix": "drivers/net/ethernet/marvell/octeontx2/"
}Results: {
"Output": "drivers/net/ethernet/marvell/octeontx2/nic/rep.c=401=static int rvu_eswitch_config(struct otx2_nic *priv, u8 ena)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/rep.c-422-#if IS_ENABLED(CONFIG_OCTEONTX_SWITCH)\ndrivers/net/ethernet/marvell/octeontx2/nic/rep.c:423:\tena ? sw_nb_register(netdev) : sw_nb_unregister(netdev);\ndrivers/net/ethernet/marvell/octeontx2/nic/rep.c-424-#endif\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.c=358=EXPORT_SYMBOL(sw_nb_unregister);\ndrivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.c-359-\ndrivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.c:360:int sw_nb_register(struct net_device *netdev)\ndrivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.c-361-{\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.c-434-}\ndrivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.c:435:EXPORT_SYMBOL(sw_nb_register);\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.h=10=enum {\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.h-21-\ndrivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.h:22:int sw_nb_register(struct net_device *netdev);\ndrivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.h-23-int sw_nb_unregister(struct net_device *netdev);\n"
} |
| 49/2 | 2026/07/02 11:19 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 98816 output: 53 thoughts: 234 Thoughts: **Analyzing the E-Switch Configuration Flow** Okay, let's break this down. My initial observation is that this `rvu_eswitch_config` function appears to be the central point of interest. I see that it's only called from `rvu_rep_create` and `rvu_rep_destroy`, and those in turn are only triggered by `otx2_devlink_eswitch_mode_set`. Since `otx2_devlink_eswitch_mode_set` is a devlink callback, it's safe to assume this e-switch setup is tightly bound to the devlink framework. The devlink device, as expected, is registered during `otx2_probe`. However, the critical point is that `otx2_probe` is likely to fail because of `otx2_init_rsrc` failure. This means we probably won't get far enough to even *reach* the e-switch configuration routine through a typical devlink pathway. But, a thought occurred to me. Could syzkaller's vhci somehow intervene and fake the mailbox communication to trick the system? However, syzkaller's vhci is a generic tool. It doesn't have specific knowledge of the Marvell OcteonTX2 mailbox protocol. It just throws back random data for MMIO reads. Given that, and the fact that `otx2_sync_mbox_msg` is waiting for a *response*, I need to examine its implementation more closely. Time to check `otx2_sync_mbox_msg` in `otx2_common.h` to see how it works. |
| 50/2 | 2026/07/02 11:19 | tool | read-file |
0mArgs:{
"File": "drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h",
"FirstLine": 885,
"LineCount": 50
}Results: {
"Contents": " 885:\tstatic inline int otx2_sync_mbox_msg(struct mbox *mbox)\n 886:\t{\n 887:\t\tint err;\n 888:\t\n 889:\t\tif (!otx2_mbox_nonempty(\u0026mbox-\u003embox, 0))\n 890:\t\t\treturn 0;\n 891:\t\totx2_mbox_msg_send(\u0026mbox-\u003embox, 0);\n 892:\t\terr = otx2_mbox_wait_for_rsp(\u0026mbox-\u003embox, 0);\n 893:\t\tif (err)\n 894:\t\t\treturn err;\n 895:\t\n 896:\t\treturn otx2_mbox_check_rsp_msgs(\u0026mbox-\u003embox, 0);\n 897:\t}\n 898:\t\n 899:\tstatic inline int otx2_sync_mbox_up_msg(struct mbox *mbox, int devid)\n 900:\t{\n 901:\t\tint err;\n 902:\t\n 903:\t\tif (!otx2_mbox_nonempty(\u0026mbox-\u003embox_up, devid))\n 904:\t\t\treturn 0;\n 905:\t\totx2_mbox_msg_send_up(\u0026mbox-\u003embox_up, devid);\n 906:\t\terr = otx2_mbox_wait_for_rsp(\u0026mbox-\u003embox_up, devid);\n 907:\t\tif (err)\n 908:\t\t\treturn err;\n 909:\t\n 910:\t\treturn otx2_mbox_check_rsp_msgs(\u0026mbox-\u003embox_up, devid);\n 911:\t}\n 912:\t\n 913:\t/* Use this API to send mbox msgs in atomic context\n 914:\t * where sleeping is not allowed\n 915:\t */\n 916:\tstatic inline int otx2_sync_mbox_msg_busy_poll(struct mbox *mbox)\n 917:\t{\n 918:\t\tint err;\n 919:\t\n 920:\t\tif (!otx2_mbox_nonempty(\u0026mbox-\u003embox, 0))\n 921:\t\t\treturn 0;\n 922:\t\totx2_mbox_msg_send(\u0026mbox-\u003embox, 0);\n 923:\t\terr = otx2_mbox_busy_poll_for_rsp(\u0026mbox-\u003embox, 0);\n 924:\t\tif (err)\n 925:\t\t\treturn err;\n 926:\t\n 927:\t\treturn otx2_mbox_check_rsp_msgs(\u0026mbox-\u003embox, 0);\n 928:\t}\n 929:\t\n 930:\t#define M(_name, _id, _fn_name, _req_type, _rsp_type) \\\n 931:\tstatic struct _req_type __maybe_unused\t\t\t\t\t\\\n 932:\t*otx2_mbox_alloc_msg_ ## _fn_name(struct mbox *mbox) \\\n 933:\t{\t\t\t\t\t\t\t\t\t\\\n 934:\t\tstruct _req_type *req;\t\t\t\t\t\t\\\n"
} |
| 51/2 | 2026/07/02 11:19 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 100269 output: 47 thoughts: 17 Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience, and delivered as a first-person summary: **Digging into `otx2_mbox_wait_for_rsp`** Alright, time to get my hands dirty. My initial thought is to dive into the `otx2_mbox_wait_for_rsp` function. It's a critical piece of the puzzle, likely handling the crucial task of waiting for a response from the OTX2 mailbox. I need to understand its behavior thoroughly – the timeout mechanisms, any potential interrupt handling involved, and of course, how it validates the received response. My focus will be on potential pitfalls related to deadlocks, spurious wakeups, or even race conditions during the response reception. Any nuances in the implementation that could lead to performance bottlenecks are also top of mind. Time to get the debugger out, I need to see what's actually happening under the hood. I expect to be going line-by-line. |
| 52/2 | 2026/07/02 11:19 | tool | grepper |
0mArgs:{
"Expression": "otx2_mbox_wait_for_rsp",
"PathPrefix": "drivers/net/ethernet/marvell/octeontx2/"
}Results: {
"Output": "drivers/net/ethernet/marvell/octeontx2/af/mbox.c=279=EXPORT_SYMBOL(otx2_mbox_regions_init);\ndrivers/net/ethernet/marvell/octeontx2/af/mbox.c-280-\ndrivers/net/ethernet/marvell/octeontx2/af/mbox.c:281:int otx2_mbox_wait_for_rsp(struct otx2_mbox *mbox, int devid)\ndrivers/net/ethernet/marvell/octeontx2/af/mbox.c-282-{\n--\ndrivers/net/ethernet/marvell/octeontx2/af/mbox.c-293-}\ndrivers/net/ethernet/marvell/octeontx2/af/mbox.c:294:EXPORT_SYMBOL(otx2_mbox_wait_for_rsp);\ndrivers/net/ethernet/marvell/octeontx2/af/mbox.c-295-\n--\ndrivers/net/ethernet/marvell/octeontx2/af/mbox.h=124=void otx2_mbox_msg_send_up(struct otx2_mbox *mbox, int devid);\ndrivers/net/ethernet/marvell/octeontx2/af/mbox.h:125:int otx2_mbox_wait_for_rsp(struct otx2_mbox *mbox, int devid);\ndrivers/net/ethernet/marvell/octeontx2/af/mbox.h-126-int otx2_mbox_busy_poll_for_rsp(struct otx2_mbox *mbox, int devid);\n--\ndrivers/net/ethernet/marvell/octeontx2/af/mcs_rvu_if.c=121=static int mcs_notify_pfvf(struct mcs_intr_event *event, struct rvu *rvu)\n--\ndrivers/net/ethernet/marvell/octeontx2/af/mcs_rvu_if.c-145-\ndrivers/net/ethernet/marvell/octeontx2/af/mcs_rvu_if.c:146:\totx2_mbox_wait_for_rsp(\u0026rvu-\u003eafpf_wq_info.mbox_up, pf);\ndrivers/net/ethernet/marvell/octeontx2/af/mcs_rvu_if.c-147-\n--\ndrivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c=232=static void cgx_notify_pfs(struct cgx_link_event *event, struct rvu *rvu)\n--\ndrivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c-274-\ndrivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c:275:\t\totx2_mbox_wait_for_rsp(\u0026rvu-\u003eafpf_wq_info.mbox_up, pfid);\ndrivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c-276-\n--\ndrivers/net/ethernet/marvell/octeontx2/af/rvu_cpt.c=1144=static int cpt_inline_inb_lf_cmd_send(struct rvu *rvu, int blkaddr,\n--\ndrivers/net/ethernet/marvell/octeontx2/af/rvu_cpt.c-1202-\totx2_mbox_msg_send(\u0026rvu-\u003eafpf_wq_info.mbox_up, cpt_pf_num);\ndrivers/net/ethernet/marvell/octeontx2/af/rvu_cpt.c:1203:\trc = otx2_mbox_wait_for_rsp(\u0026rvu-\u003eafpf_wq_info.mbox_up, cpt_pf_num);\ndrivers/net/ethernet/marvell/octeontx2/af/rvu_cpt.c-1204-\tif (rc)\n--\ndrivers/net/ethernet/marvell/octeontx2/af/rvu_rep.c=36=static int rvu_rep_up_notify(struct rvu *rvu, struct rep_event *event)\n--\ndrivers/net/ethernet/marvell/octeontx2/af/rvu_rep.c-62-\ndrivers/net/ethernet/marvell/octeontx2/af/rvu_rep.c:63:\totx2_mbox_wait_for_rsp(\u0026rvu-\u003eafpf_wq_info.mbox_up, pf);\ndrivers/net/ethernet/marvell/octeontx2/af/rvu_rep.c-64-\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h=885=static inline int otx2_sync_mbox_msg(struct mbox *mbox)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h-891-\totx2_mbox_msg_send(\u0026mbox-\u003embox, 0);\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h:892:\terr = otx2_mbox_wait_for_rsp(\u0026mbox-\u003embox, 0);\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h-893-\tif (err)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h=899=static inline int otx2_sync_mbox_up_msg(struct mbox *mbox, int devid)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h-905-\totx2_mbox_msg_send_up(\u0026mbox-\u003embox_up, devid);\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h:906:\terr = otx2_mbox_wait_for_rsp(\u0026mbox-\u003embox_up, devid);\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h-907-\tif (err)\n"
} |
| 53/2 | 2026/07/02 11:19 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 102136 output: 49 thoughts: 17 Thoughts: Okay, let's dive into this `otx2_mbox_wait_for_rsp` function. I need to understand its role within the larger system. My initial instinct is to expect it to be a crucial synchronization point, probably dealing with message passing or inter-processor communication, based on the naming convention. The "mbox" strongly suggests a mailbox-style mechanism, and the "wait_for_rsp" implies waiting for a response – likely from a remote entity. I'll need to see what's being waited on. Is it a flag, a specific data value, a semaphore? And how is that response signaled? Are there potential deadlock scenarios I need to consider? What error handling is present? I hope it's well documented and handles timeouts gracefully... or I might be in for a long debugging session. Knowing how often it's called and the typical duration of the wait would be helpful in analyzing performance implications as well. Let's see what the code reveals. |
| 54/2 | 2026/07/02 11:19 | tool | read-file |
0mArgs:{
"File": "drivers/net/ethernet/marvell/octeontx2/af/mbox.c",
"FirstLine": 281,
"LineCount": 20
}Results: {
"Contents": " 281:\tint otx2_mbox_wait_for_rsp(struct otx2_mbox *mbox, int devid)\n 282:\t{\n 283:\t\tunsigned long timeout = jiffies + msecs_to_jiffies(MBOX_RSP_TIMEOUT);\n 284:\t\tstruct otx2_mbox_dev *mdev = \u0026mbox-\u003edev[devid];\n 285:\t\n 286:\t\twhile (!time_after(jiffies, timeout)) {\n 287:\t\t\tif (mdev-\u003enum_msgs == mdev-\u003emsgs_acked)\n 288:\t\t\t\treturn 0;\n 289:\t\t\tusleep_range(800, 1000);\n 290:\t\t}\n 291:\t\ttrace_otx2_msg_wait_rsp(mbox-\u003epdev);\n 292:\t\treturn -EIO;\n 293:\t}\n 294:\tEXPORT_SYMBOL(otx2_mbox_wait_for_rsp);\n 295:\t\n 296:\tint otx2_mbox_busy_poll_for_rsp(struct otx2_mbox *mbox, int devid)\n 297:\t{\n 298:\t\tstruct otx2_mbox_dev *mdev = \u0026mbox-\u003edev[devid];\n 299:\t\tunsigned long timeout = jiffies + 1 * HZ;\n 300:\t\n"
} |
| 55/2 | 2026/07/02 11:19 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 102782 output: 53 thoughts: 547 Thoughts: **My Analysis of the OTX2 Driver Failure Path** Okay, let's break this down systematically. I see that `mdev->msgs_acked` is updated within the interrupt handler. This is the crux. If the interrupt *doesn't* fire, a timeout occurs, and `-EIO` is returned. Now, the problem is that Syzkaller's vhci doesn't normally trigger these interrupts. Hang on…Syzkaller *could* trigger them, if it knew the specific mailbox semantics, but it doesn’t. So, we're stuck. This means that `otx2_mbox_wait_for_rsp` times out. Boom, `-EIO` from `otx2_sync_mbox_msg`. This propagates: `otx2_attach_npa_nix` returns `-EIO`, followed by `otx2_init_rsrc`, then `otx2_probe`, all failing with `-EIO`. The inevitable consequence is that the driver fails to probe. No probe, no devlink device. Without a devlink device, `otx2_devlink_eswitch_mode_set` can't be called. This failure prevents the execution of `rvu_rep_create`, which subsequently blocks `rvu_eswitch_config` from running. Consequently, `sw_nb_register` never gets called, and crucially, the notifiers never get registered. Thus, the switch code becomes unreachable. Now the question is, *is there any other path* to this switch code? It looks like the switch code is within `sw_nb.c`, `sw_fdb.c`, `sw_fib.c`, and `sw_fl.c`, and the only externally visible functions are `sw_nb_register`, `sw_nb_unregister`, `sw_nb_get_cmd2str`, and `otx2_mbox_up_handler_af2pf_fdb_refresh`. Wait a minute, `otx2_mbox_up_handler_af2pf_fdb_refresh` *is* exported and *is* used by `otx2_vf.c`. Let me check out `otx2_vf.c`... that could be interesting. |
| 56/2 | 2026/07/02 11:20 | tool | read-file |
0mArgs:{
"File": "drivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c",
"FirstLine": 140,
"LineCount": 20
}Results: {
"Contents": " 140:\t\t\trsp-\u003ehdr.pcifunc = req-\u003epcifunc;\n 141:\t\t\trsp-\u003ehdr.rc = 0;\n 142:\t\t\terr = otx2_mbox_up_handler_cgx_link_event(\n 143:\t\t\t\t\tvf, (struct cgx_link_info_msg *)req, rsp);\n 144:\t\t\treturn err;\n 145:\t\n 146:\t\tcase MBOX_MSG_AF2PF_FDB_REFRESH:\n 147:\t\t\trsp = (struct msg_rsp *)otx2_mbox_alloc_msg(\u0026vf-\u003embox.mbox_up, 0,\n 148:\t\t\t\t\t\t\t\t sizeof(struct msg_rsp));\n 149:\t\t\tif (!rsp)\n 150:\t\t\t\treturn -ENOMEM;\n 151:\t\n 152:\t\t\trsp-\u003ehdr.id = MBOX_MSG_AF2PF_FDB_REFRESH;\n 153:\t\t\trsp-\u003ehdr.sig = OTX2_MBOX_RSP_SIG;\n 154:\t\t\trsp-\u003ehdr.pcifunc = req-\u003epcifunc;\n 155:\t\t\trsp-\u003ehdr.rc = 0;\n 156:\t\t\terr = otx2_mbox_up_handler_af2pf_fdb_refresh(vf,\n 157:\t\t\t\t\t\t\t\t (struct af2pf_fdb_refresh_req *)req,\n 158:\t\t\t\t\t\t\t\t rsp);\n 159:\t\t\treturn err;\n"
} |
| 57/2 | 2026/07/02 11:20 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 104259 output: 51 thoughts: 131 Thoughts: **Tracing the VF Driver's Mailbox Message Handling** Okay, let's break this down. It seems the function `otx2_mbox_up_handler_af2pf_fdb_refresh` is triggered by a call from `otx2vf_process_mbox_msg_up`. Right, got it. And `otx2vf_process_mbox_msg_up` is, in turn, called by `otx2_vf_mbox_up_handler`. Now, `otx2_vf_mbox_up_handler` is explicitly the mailbox interrupt handler, which makes sense. However, I recall that the VF driver, `otx2_vf`, also utilizes `otx2_sync_mbox_msg` during its probe sequence. To get a comprehensive understanding, I think I need to delve into `otx2vf_probe` to see how these pieces fit together, and how the mailbox is initially set up. It's time to examine the probe function's role in this interaction. |
| 58/2 | 2026/07/02 11:20 | tool | grepper |
0mArgs:{
"Expression": "otx2vf_probe",
"PathPrefix": "drivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c"
}Results: {
"Output": "drivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c=568=static int otx2vf_realloc_msix_vectors(struct otx2_nic *vf)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c-587-\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c:588:static int otx2vf_probe(struct pci_dev *pdev, const struct pci_device_id *id)\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c-589-{\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c=878=static struct pci_driver otx2vf_driver = {\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c-880-\t.id_table = otx2_vf_id_table,\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c:881:\t.probe = otx2vf_probe,\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c-882-\t.remove = otx2vf_remove,\n"
} |
| 59/2 | 2026/07/02 11:20 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 105033 output: 54 thoughts: 12 Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience: **Analyzing the `otx2vf_probe` Routine** Alright, let's dive into the code. My initial thought is to get a handle on `otx2vf_probe`. This likely involves some form of hardware or driver probing function, considering the "probe" suffix. I'm immediately interested in its purpose, what specific devices or interfaces it's targeting, and how it interacts with the underlying hardware abstraction layer. I'm going to be looking for clues regarding initialization, resource allocation (memory, interrupts, etc.), and any potential dependencies on other modules or libraries within the system. Is it a PCIe probe? A platform-specific probing routine? Understanding the context will be key. Time to get to work. |
| 60/2 | 2026/07/02 11:20 | tool | read-file |
0mArgs:{
"File": "drivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c",
"FirstLine": 588,
"LineCount": 100
}Results: {
"Contents": " 588:\tstatic int otx2vf_probe(struct pci_dev *pdev, const struct pci_device_id *id)\n 589:\t{\n 590:\t\tint num_vec = pci_msix_vec_count(pdev);\n 591:\t\tstruct device *dev = \u0026pdev-\u003edev;\n 592:\t\tint err, qcount, qos_txqs;\n 593:\t\tstruct net_device *netdev;\n 594:\t\tstruct otx2_nic *vf;\n 595:\t\tstruct otx2_hw *hw;\n 596:\t\n 597:\t\terr = pcim_enable_device(pdev);\n 598:\t\tif (err) {\n 599:\t\t\tdev_err(dev, \"Failed to enable PCI device\\n\");\n 600:\t\t\treturn err;\n 601:\t\t}\n 602:\t\n 603:\t\terr = pcim_request_all_regions(pdev, DRV_NAME);\n 604:\t\tif (err) {\n 605:\t\t\tdev_err(dev, \"PCI request regions failed 0x%x\\n\", err);\n 606:\t\t\treturn err;\n 607:\t\t}\n 608:\t\n 609:\t\terr = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(48));\n 610:\t\tif (err) {\n 611:\t\t\tdev_err(dev, \"DMA mask config failed, abort\\n\");\n 612:\t\t\treturn err;\n 613:\t\t}\n 614:\t\n 615:\t\tpci_set_master(pdev);\n 616:\t\n 617:\t\tqcount = num_online_cpus();\n 618:\t\tqos_txqs = min_t(int, qcount, OTX2_QOS_MAX_LEAF_NODES);\n 619:\t\tnetdev = alloc_etherdev_mqs(sizeof(*vf), qcount + qos_txqs, qcount);\n 620:\t\tif (!netdev)\n 621:\t\t\treturn -ENOMEM;\n 622:\t\n 623:\t\tpci_set_drvdata(pdev, netdev);\n 624:\t\tSET_NETDEV_DEV(netdev, \u0026pdev-\u003edev);\n 625:\t\tvf = netdev_priv(netdev);\n 626:\t\tvf-\u003enetdev = netdev;\n 627:\t\tvf-\u003epdev = pdev;\n 628:\t\tvf-\u003edev = dev;\n 629:\t\tvf-\u003eiommu_domain = iommu_get_domain_for_dev(dev);\n 630:\t\n 631:\t\tvf-\u003eflags |= OTX2_FLAG_INTF_DOWN;\n 632:\t\thw = \u0026vf-\u003ehw;\n 633:\t\thw-\u003epdev = vf-\u003epdev;\n 634:\t\thw-\u003erx_queues = qcount;\n 635:\t\thw-\u003etx_queues = qcount;\n 636:\t\thw-\u003emax_queues = qcount;\n 637:\t\thw-\u003enon_qos_queues = qcount;\n 638:\t\thw-\u003erbuf_len = OTX2_DEFAULT_RBUF_LEN;\n 639:\t\t/* Use CQE of 128 byte descriptor size by default */\n 640:\t\thw-\u003exqe_size = 128;\n 641:\t\n 642:\t\thw-\u003eirq_name = devm_kmalloc_array(\u0026hw-\u003epdev-\u003edev, num_vec, NAME_SIZE,\n 643:\t\t\t\t\t\t GFP_KERNEL);\n 644:\t\tif (!hw-\u003eirq_name) {\n 645:\t\t\terr = -ENOMEM;\n 646:\t\t\tgoto err_free_netdev;\n 647:\t\t}\n 648:\t\n 649:\t\thw-\u003eaffinity_mask = devm_kcalloc(\u0026hw-\u003epdev-\u003edev, num_vec,\n 650:\t\t\t\t\t\t sizeof(cpumask_var_t), GFP_KERNEL);\n 651:\t\tif (!hw-\u003eaffinity_mask) {\n 652:\t\t\terr = -ENOMEM;\n 653:\t\t\tgoto err_free_netdev;\n 654:\t\t}\n 655:\t\n 656:\t\terr = pci_alloc_irq_vectors(hw-\u003epdev, num_vec, num_vec, PCI_IRQ_MSIX);\n 657:\t\tif (err \u003c 0) {\n 658:\t\t\tdev_err(dev, \"%s: Failed to alloc %d IRQ vectors\\n\",\n 659:\t\t\t\t__func__, num_vec);\n 660:\t\t\tgoto err_free_netdev;\n 661:\t\t}\n 662:\t\n 663:\t\tvf-\u003ereg_base = pcim_iomap(pdev, PCI_CFG_REG_BAR_NUM, 0);\n 664:\t\tif (!vf-\u003ereg_base) {\n 665:\t\t\tdev_err(dev, \"Unable to map physical function CSRs, aborting\\n\");\n 666:\t\t\terr = -ENOMEM;\n 667:\t\t\tgoto err_free_irq_vectors;\n 668:\t\t}\n 669:\t\n 670:\t\totx2_setup_dev_hw_settings(vf);\n 671:\t\n 672:\t\tif (is_cn20k(vf-\u003epdev))\n 673:\t\t\tcn20k_init(vf);\n 674:\t\telse\n 675:\t\t\totx2_init_hw_ops(vf);\n 676:\t\n 677:\t\t/* Init VF \u003c=\u003e PF mailbox stuff */\n 678:\t\terr = otx2vf_vfaf_mbox_init(vf);\n 679:\t\tif (err)\n 680:\t\t\tgoto err_free_irq_vectors;\n 681:\t\n 682:\t\t/* Register mailbox interrupt */\n 683:\t\terr = otx2vf_register_mbox_intr(vf, true);\n 684:\t\tif (err)\n 685:\t\t\tgoto err_mbox_destroy;\n 686:\t\n 687:\t\t/* Request AF to attach NPA and LIX LFs to this AF */\n"
} |
| 61/2 | 2026/07/02 11:20 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 106914 output: 50 thoughts: 362 Thoughts: **Analyzing the OcteonTX2 Virtual Function Driver Probe Failure** Okay, so I'm looking at this probe failure and it seems straightforward at first. The code calls `otx2vf_vfaf_mbox_init` and then `otx2_attach_npa_nix`. The problem is that `otx2_attach_npa_nix` uses `otx2_sync_mbox_msg` and that's timing out. My gut tells me this is because either the AF driver isn't present, or more likely, syzkaller's vhci isn't emulating the mailbox interrupt correctly. Makes sense, the VF driver is waiting for a response that's not coming, so it hangs. This also explains why the VF driver will fail to probe. Then I started thinking, what if we compiled this driver with `CONFIG_OCTEONTX_SWITCH=y`? I could sidestep the hardware dependency and focus on testing the software paths. But wait... the switch code is only reachable if the driver actually probes successfully and the devlink device gets registered! The whole point is the probe *fails*, so the devlink device is never registered. That's a dead end. Is there any other way to reach the function `sw_nb_register`? Digging into it, `sw_nb_register` is only called from `rvu_eswitch_config`. And that's only called from `rvu_rep_create` and `rvu_rep_destroy`. Further up the call chain, `rvu_rep_create` is only called from `otx2_devlink_eswitch_mode_set`. And `otx2_devlink_eswitch_mode_set` is, of course, a devlink callback. So, the devlink device is registered in `otx2_probe`, `otx2vf_probe`, or `rvu_rep_probe`. Right... `rvu_rep_probe` seems like it's for the `PCI_DEVID_RVU_REP` device, so I should probably focus my attention there. Let's start with a closer look at `rvu_rep_probe`. That might give me a clue to some other way to reach the switch functionality. |
| 62/2 | 2026/07/02 11:20 | tool | read-file |
0mArgs:{
"File": "drivers/net/ethernet/marvell/octeontx2/nic/rep.c",
"FirstLine": 781,
"LineCount": 100
}Results: {
"Contents": " 781:\tstatic int rvu_rep_probe(struct pci_dev *pdev, const struct pci_device_id *id)\n 782:\t{\n 783:\t\tstruct device *dev = \u0026pdev-\u003edev;\n 784:\t\tstruct otx2_nic *priv;\n 785:\t\tstruct otx2_hw *hw;\n 786:\t\tint err;\n 787:\t\n 788:\t\terr = pcim_enable_device(pdev);\n 789:\t\tif (err) {\n 790:\t\t\tdev_err(dev, \"Failed to enable PCI device\\n\");\n 791:\t\t\treturn err;\n 792:\t\t}\n 793:\t\n 794:\t\terr = pcim_request_all_regions(pdev, DRV_NAME);\n 795:\t\tif (err) {\n 796:\t\t\tdev_err(dev, \"PCI request regions failed 0x%x\\n\", err);\n 797:\t\t\treturn err;\n 798:\t\t}\n 799:\t\n 800:\t\terr = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(48));\n 801:\t\tif (err) {\n 802:\t\t\tdev_err(dev, \"DMA mask config failed, abort\\n\");\n 803:\t\t\tgoto err_set_drv_data;\n 804:\t\t}\n 805:\t\n 806:\t\tpci_set_master(pdev);\n 807:\t\n 808:\t\tpriv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);\n 809:\t\tif (!priv) {\n 810:\t\t\terr = -ENOMEM;\n 811:\t\t\tgoto err_set_drv_data;\n 812:\t\t}\n 813:\t\n 814:\t\tpci_set_drvdata(pdev, priv);\n 815:\t\tpriv-\u003epdev = pdev;\n 816:\t\tpriv-\u003edev = dev;\n 817:\t\tpriv-\u003eflags |= OTX2_FLAG_INTF_DOWN;\n 818:\t\tpriv-\u003eflags |= OTX2_FLAG_REP_MODE_ENABLED;\n 819:\t\n 820:\t\thw = \u0026priv-\u003ehw;\n 821:\t\thw-\u003epdev = pdev;\n 822:\t\thw-\u003emax_queues = OTX2_MAX_CQ_CNT;\n 823:\t\thw-\u003erbuf_len = OTX2_DEFAULT_RBUF_LEN;\n 824:\t\thw-\u003exqe_size = 128;\n 825:\t\n 826:\t\terr = otx2_init_rsrc(pdev, priv);\n 827:\t\tif (err)\n 828:\t\t\tgoto err_set_drv_data;\n 829:\t\n 830:\t\tpriv-\u003eiommu_domain = iommu_get_domain_for_dev(dev);\n 831:\t\n 832:\t\terr = rvu_get_rep_cnt(priv);\n 833:\t\tif (err)\n 834:\t\t\tgoto err_detach_rsrc;\n 835:\t\n 836:\t\terr = otx2_register_dl(priv);\n 837:\t\tif (err)\n 838:\t\t\tgoto err_detach_rsrc;\n 839:\t\n 840:\t\treturn 0;\n 841:\t\n 842:\terr_detach_rsrc:\n 843:\t\tif (priv-\u003ehw.lmt_info)\n 844:\t\t\tfree_percpu(priv-\u003ehw.lmt_info);\n 845:\t\tif (test_bit(CN10K_LMTST, \u0026priv-\u003ehw.cap_flag))\n 846:\t\t\tqmem_free(priv-\u003edev, priv-\u003edync_lmt);\n 847:\t\totx2_detach_resources(\u0026priv-\u003embox);\n 848:\t\totx2_disable_mbox_intr(priv);\n 849:\t\totx2_pfaf_mbox_destroy(priv);\n 850:\t\tpci_free_irq_vectors(pdev);\n 851:\terr_set_drv_data:\n 852:\t\tpci_set_drvdata(pdev, NULL);\n 853:\t\treturn err;\n 854:\t}\n 855:\t\n 856:\tstatic void rvu_rep_remove(struct pci_dev *pdev)\n 857:\t{\n 858:\t\tstruct otx2_nic *priv = pci_get_drvdata(pdev);\n 859:\t\n 860:\t\totx2_unregister_dl(priv);\n 861:\t\tif (!(priv-\u003eflags \u0026 OTX2_FLAG_INTF_DOWN))\n 862:\t\t\trvu_rep_destroy(priv);\n 863:\t\totx2_detach_resources(\u0026priv-\u003embox);\n 864:\t\tif (priv-\u003ehw.lmt_info)\n 865:\t\t\tfree_percpu(priv-\u003ehw.lmt_info);\n 866:\t\tif (test_bit(CN10K_LMTST, \u0026priv-\u003ehw.cap_flag))\n 867:\t\t\tqmem_free(priv-\u003edev, priv-\u003edync_lmt);\n 868:\t\totx2_disable_mbox_intr(priv);\n 869:\t\totx2_pfaf_mbox_destroy(priv);\n 870:\t\tpci_free_irq_vectors(priv-\u003epdev);\n 871:\t\tpci_set_drvdata(pdev, NULL);\n 872:\t}\n 873:\t\n 874:\tstatic struct pci_driver rvu_rep_driver = {\n 875:\t\t.name = DRV_NAME,\n 876:\t\t.id_table = rvu_rep_id_table,\n 877:\t\t.probe = rvu_rep_probe,\n 878:\t\t.remove = rvu_rep_remove,\n 879:\t\t.shutdown = rvu_rep_remove,\n 880:\t};\n"
} |
| 63/2 | 2026/07/02 11:20 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 109427 output: 53 thoughts: 639 Thoughts: **Unreachable Switch Code in a Virtualized Environment** Okay, so here's what I've figured out. `rvu_rep_probe` calls `otx2_init_rsrc`, which fails due to a mailbox communication timeout. This means the devlink device won't register. The question is, can `sw_nb_register` be triggered another way? Nope, it's only called from `rvu_eswitch_config`. And that, in turn, is only called from `rvu_rep_create` and `rvu_rep_destroy`. Digging deeper, `rvu_rep_create` is called exclusively from `otx2_devlink_eswitch_mode_set`. This is a devlink callback, so it's only called if the driver probes successfully. But, the driver *won't* probe successfully unless the hardware is present or a very advanced emulator (that does mailbox protocol). Syzkaller's vhci doesn't do that. So, the switch code is basically unreachable in an environment like GCE or QEMU. Let me just double-check, though. The patch introduces `sw_fdb.c`, `sw_fib.c`, `sw_fl.c`, `sw_nb.c`, `sw_nb_v4.c`, and `sw_nb_v6.c`. These files have notifiers and workqueue handlers. These notifiers are registered in `sw_nb_register`. If `sw_nb_register` isn't called, the notifiers never get registered, meaning the workqueue handlers don't get queued. In effect, none of that code will be reachable. Alright, what about `otx2_mbox_up_handler_af2pf_fdb_refresh`? It is called from `otx2vf_process_mbox_msg_up` in `otx2_vf.c`. That, in turn, is called from `otx2_vf_mbox_up_handler`, which is the mailbox interrupt handler. If the interrupt isn't triggered, that code is never hit. Syzkaller's vhci doesn't trigger interrupts. Even if it did, the VF driver fails to probe because `otx2_attach_npa_nix` fails, so either the interrupt handler isn't registered or the device isn't fully initialized. Oh wait, `otx2vf_register_mbox_intr` is called *before* `otx2_attach_npa_nix`. So the interrupt handler *is* registered. If syzkaller's vhci can trigger MSI-X interrupts, it *might* trigger `otx2_vf_mbox_up_handler`. I think I'll investigate that function now. |
| 64/2 | 2026/07/02 11:20 | tool | read-file |
0mArgs:{
"File": "drivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c",
"FirstLine": 163,
"LineCount": 50
}Results: {
"Contents": " 163:\t\t\treturn -ENODEV;\n 164:\t\t}\n 165:\t\treturn 0;\n 166:\t}\n 167:\t\n 168:\tstatic void otx2vf_vfaf_mbox_up_handler(struct work_struct *work)\n 169:\t{\n 170:\t\tstruct otx2_mbox_dev *mdev;\n 171:\t\tstruct mbox_hdr *rsp_hdr;\n 172:\t\tstruct mbox_msghdr *msg;\n 173:\t\tstruct otx2_mbox *mbox;\n 174:\t\tstruct mbox *vf_mbox;\n 175:\t\tstruct otx2_nic *vf;\n 176:\t\tint offset, id;\n 177:\t\tu16 num_msgs;\n 178:\t\n 179:\t\tvf_mbox = container_of(work, struct mbox, mbox_up_wrk);\n 180:\t\tvf = vf_mbox-\u003epfvf;\n 181:\t\tmbox = \u0026vf_mbox-\u003embox_up;\n 182:\t\tmdev = \u0026mbox-\u003edev[0];\n 183:\t\n 184:\t\trsp_hdr = (struct mbox_hdr *)(mdev-\u003embase + mbox-\u003erx_start);\n 185:\t\tnum_msgs = rsp_hdr-\u003enum_msgs;\n 186:\t\n 187:\t\tif (num_msgs == 0)\n 188:\t\t\treturn;\n 189:\t\n 190:\t\toffset = mbox-\u003erx_start + ALIGN(sizeof(*rsp_hdr), MBOX_MSG_ALIGN);\n 191:\t\n 192:\t\tfor (id = 0; id \u003c num_msgs; id++) {\n 193:\t\t\tmsg = (struct mbox_msghdr *)(mdev-\u003embase + offset);\n 194:\t\t\totx2vf_process_mbox_msg_up(vf, msg);\n 195:\t\t\toffset = mbox-\u003erx_start + msg-\u003enext_msgoff;\n 196:\t\t}\n 197:\t\n 198:\t\totx2_mbox_msg_send(mbox, 0);\n 199:\t}\n 200:\t\n 201:\tstatic irqreturn_t otx2vf_vfaf_mbox_intr_handler(int irq, void *vf_irq)\n 202:\t{\n 203:\t\tstruct otx2_nic *vf = (struct otx2_nic *)vf_irq;\n 204:\t\tstruct otx2_mbox_dev *mdev;\n 205:\t\tstruct otx2_mbox *mbox;\n 206:\t\tstruct mbox_hdr *hdr;\n 207:\t\tu64 mbox_data;\n 208:\t\n 209:\t\t/* Clear the IRQ */\n 210:\t\totx2_write64(vf, RVU_VF_INT, BIT_ULL(0));\n 211:\t\n 212:\t\tmbox_data = otx2_read64(vf, RVU_VF_VFPF_MBOX0);\n"
} |
| 65/2 | 2026/07/02 11:20 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 111547 output: 43 thoughts: 837 Thoughts: **Worth Fuzzing? A Deep Dive into the OTX2 VF Mailbox Handler** Okay, so here's the deal. I'm looking at this patch and trying to determine if it's even remotely worth fuzzing. The scenario is this: `syzkaller` potentially triggers an interrupt. If that happens, `mbox_up_wrk` gets scheduled, which then calls `otx2vf_vfaf_mbox_up_handler`. This function reads from `mdev->mbase`, which is the mapped mailbox memory. Now, if `syzkaller`'s vhci maps this to dummy memory, we're likely going to read zero for `num_msgs`, and that's the end of it, a simple return. However, hypothetically, if `syzkaller` somehow manages to populate this dummy memory with a valid message header and a `num_msgs > 0`, things get interesting. We'd then hit `otx2vf_process_mbox_msg_up`. The problem is, as I understand it, `syzkaller`'s vhci doesn't populate DMA/shared memory with structured data specific to drivers; it provides either a zeroed page or random data. If it's random data, `num_msgs` could be non-zero by chance, and we could end up in `otx2vf_process_mbox_msg_up` with a random `msg->id`. The crucial question is, *what* could this lead to? If `msg->id` happens to be `MBOX_MSG_AF2PF_FDB_REFRESH`, then we end up calling `otx2_mbox_up_handler_af2pf_fdb_refresh`. But, this function is incredibly simple, it merely calls `call_switchdev_notifiers(SWITCHDEV_FDB_ADD_TO_BRIDGE, item.info.dev, &item.info, NULL);`. A small, simple function. Is it worth fuzzing? The instructions on patch evaluation are clear: "Return WorthFuzzing=false if the patch only contains... Code paths that are impossible to reach in virtualized environments like GCE or QEMU, even when utilizing software-emulated hardware". Given that this driver *cannot* probe successfully (without the right hardware), and the *only* way to reach any of this code is through a highly improbable sequence of random data triggering an interrupt, this is practically impossible to reach in a virtualized environment. It's a dead end, unless we are very lucky, and in the case of a good fuzzer, we should have a better chance in other, more tractable parts of the kernel. Furthermore, the core functionality of the patch—the switchdev notifiers and offloading logic—is completely unreachable because `sw_nb_register` is never called. Let me take another look. Let's verify if `OCTEONTX_SWITCH` can be enabled, and what other effects it might have. This adds `sw_nb.o`, `sw_fib.o`, `sw_nb_v4.o`, `sw_nb_v6.o`, `sw_fdb.o`, `sw_fl.o`, `sw_trace.o` to `rvu_nicpf-y`, compiling them into the `rvu_nicpf` module. But then, their entry points are only `sw_nb_register` and `sw_nb_unregister`. And `sw_nb_register` is only called from `rvu_eswitch_config`, which is only called from `rvu_rep_create` and `rvu_rep_destroy`, which are only called from `otx2_devlink_eswitch_mode_set`. And `otx2_devlink_eswitch_mode_set` is a devlink callback, but the devlink device is only registered if the driver probes successfully, which it won't. What about the AF driver side? The patch adds `rvu_sw.c`, `rvu_sw_l2.c`, `rvu_sw_l3.c`, `rvu_sw_fl.c` to `rvu_af-y`. I'll need to check the entry points in the AF driver as well. |
| 66/2 | 2026/07/02 11:20 | tool | grepper |
0mArgs:{
"Expression": "rvu_sw_",
"PathPrefix": "drivers/net/ethernet/marvell/octeontx2/af/"
}Results: {
"Output": "drivers/net/ethernet/marvell/octeontx2/af/Makefile=11=rvu_af-y := cgx.o rvu.o rvu_cgx.o rvu_npa.o rvu_nix.o \\\n--\ndrivers/net/ethernet/marvell/octeontx2/af/Makefile-14-\t\t rvu_sdp.o rvu_npc_hash.o mcs.o mcs_rvu_if.o mcs_cnf10kb.o \\\ndrivers/net/ethernet/marvell/octeontx2/af/Makefile:15:\t\t switch/rvu_sw.o switch/rvu_sw_l2.o switch/rvu_sw_l3.o switch/rvu_sw_fl.o \\\ndrivers/net/ethernet/marvell/octeontx2/af/Makefile-16-\t\t rvu_rep.o cn20k/mbox_init.o cn20k/nix.o cn20k/debugfs.o \\\n--\ndrivers/net/ethernet/marvell/octeontx2/af/rvu.c=3822=static void rvu_remove(struct pci_dev *pdev)\n--\ndrivers/net/ethernet/marvell/octeontx2/af/rvu.c-3825-\ndrivers/net/ethernet/marvell/octeontx2/af/rvu.c:3826:\trvu_sw_shutdown();\ndrivers/net/ethernet/marvell/octeontx2/af/rvu.c-3827-\n--\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.c-9-#include \"rvu.h\"\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.c:10:#include \"rvu_sw_l2.h\"\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.c:11:#include \"rvu_sw_l3.h\"\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.c:12:#include \"rvu_sw_fl.h\"\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.c-13-#include \"rvu_sw.h\"\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.c-14-\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.c:15:u32 rvu_sw_port_id(struct rvu *rvu, u16 pcifunc)\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.c-16-{\n--\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.c=28=int rvu_mbox_handler_swdev2af_notify(struct rvu *rvu,\n--\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.c-35-\tcase SWDEV2AF_MSG_TYPE_FW_STATUS:\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.c:36:\t\trc = rvu_sw_l2_init_offl_wq(rvu, req-\u003epcifunc, req-\u003efw_up);\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.c-37-\t\tbreak;\n--\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.c-39-\tcase SWDEV2AF_MSG_TYPE_REFRESH_FDB:\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.c:40:\t\trc = rvu_sw_l2_fdb_list_entry_add(rvu, req-\u003epcifunc, req-\u003emac);\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.c-41-\t\tbreak;\n--\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.c-43-\tcase SWDEV2AF_MSG_TYPE_REFRESH_FL:\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.c:44:\t\trc = rvu_sw_fl_stats_sync2db(rvu, req-\u003efl, req-\u003ecnt);\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.c-45-\t\tbreak;\n--\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.c-50-\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.c:51:void rvu_sw_shutdown(void)\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.c-52-{\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.c:53:\trvu_sw_l2_shutdown();\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.c:54:\trvu_sw_l3_shutdown();\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.c:55:\trvu_sw_fl_shutdown();\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.c-56-}\n--\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.h-11-/* RVU Switch */\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.h:12:u32 rvu_sw_port_id(struct rvu *rvu, u16 pcifunc);\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.h:13:void rvu_sw_shutdown(void);\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.h-14-\n--\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_fl.c-10-#include \"rvu_sw.h\"\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_fl.c:11:#include \"rvu_sw_fl.h\"\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_fl.c-12-\n--\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_fl.c=63=static int\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_fl.c:64:rvu_sw_fl_stats_sync2db_one_entry(unsigned long cookie, u8 disabled,\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_fl.c-65-\t\t\t\t u16 mcam_idx[2], bool uni_di, u64 pkts)\n--\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_fl.c-121-\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_fl.c:122:int rvu_sw_fl_stats_sync2db(struct rvu *rvu, struct fl_info *fl, int cnt)\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_fl.c-123-{\n--\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_fl.c-179-\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_fl.c:180:\t\trc |= rvu_sw_fl_stats_sync2db_one_entry(fl[idx].cookie, fl[idx].dis,\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_fl.c-181-\t\t\t\t\t\t\tfl[idx].mcam_idx,\n--\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_fl.c-190-\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_fl.c:191:static int rvu_sw_fl_offl_rule_push(struct fl_entry *fl_entry)\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_fl.c-192-{\n--\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_fl.c=219=static void sw_fl_offl_work_handler(struct work_struct *work)\n--\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_fl.c-232-\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_fl.c:233:\trvu_sw_fl_offl_rule_push(fl_entry);\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_fl.c-234-\tkfree(fl_entry);\n--\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_fl.c=261=int rvu_mbox_handler_fl_notify(struct rvu *rvu,\n--\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_fl.c-273-\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_fl.c:274:\tfl_entry-\u003eport_id = rvu_sw_port_id(rvu, req-\u003ehdr.pcifunc);\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_fl.c-275-\tfl_entry-\u003ervu = rvu;\n--\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_fl.c-295-\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_fl.c:296:void rvu_sw_fl_shutdown(void)\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_fl.c-297-{\n--\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_fl.h-9-#define RVU_SW_FL_H\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_fl.h:10:int rvu_sw_fl_stats_sync2db(struct rvu *rvu, struct fl_info *fl, int cnt);\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_fl.h:11:void rvu_sw_fl_shutdown(void);\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_fl.h-12-\n--\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c-10-#include \"rvu_sw.h\"\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c:11:#include \"rvu_sw_l2.h\"\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c-12-\n--\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c=43=static LIST_HEAD(fdb_refresh_lh);\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c-44-\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c:45:struct rvu_sw_l2_work {\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c-46-\tstruct rvu *rvu;\n--\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c-53- */\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c:54:static struct rvu_sw_l2_work l2_offl_work;\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c:55:static struct workqueue_struct *rvu_sw_l2_offl_wq;\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c-56-\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c:57:static struct rvu_sw_l2_work fdb_refresh_work;\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c-58-static struct workqueue_struct *fdb_refresh_wq;\n--\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c-64- */\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c:65:static void rvu_sw_l2_offl_cancel_add_if_del_reqs_exist(u8 *mac)\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c-66-{\n--\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c-83-\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c:84:static int rvu_sw_l2_offl_rule_push(struct rvu *rvu, struct l2_entry *l2_entry)\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c-85-{\n--\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c-108-\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c:109:static int rvu_sw_l2_fdb_refresh(struct rvu *rvu, u16 pcifunc, u8 *mac)\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c-110-{\n--\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c-156-\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c:157:static void rvu_sw_l2_fdb_refresh_wq_handler(struct work_struct *work)\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c-158-{\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c:159:\tstruct rvu_sw_l2_work *fdb_work;\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c-160-\tstruct l2_entry *l2_entry;\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c-161-\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c:162:\tfdb_work = container_of(work, struct rvu_sw_l2_work, work);\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c-163-\n--\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c-175-\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c:176:\t\trvu_sw_l2_fdb_refresh(fdb_work-\u003ervu, l2_entry-\u003eport_id, l2_entry-\u003emac);\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c-177-\t\tkfree(l2_entry);\n--\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c-180-\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c:181:static void rvu_sw_l2_offl_rule_wq_handler(struct work_struct *work)\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c-182-{\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c:183:\tstruct rvu_sw_l2_work *offl_work;\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c-184-\tstruct l2_entry *l2_entry;\n--\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c-187-\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c:188:\toffl_work = container_of(work, struct rvu_sw_l2_work, work);\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c-189-\n--\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c-203-\t\tif (add_fdb)\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c:204:\t\t\trvu_sw_l2_offl_cancel_add_if_del_reqs_exist(l2_entry-\u003emac);\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c-205-\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c:206:\t\tif (rvu_sw_l2_offl_rule_push(offl_work-\u003ervu, l2_entry))\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c-207-\t\t\tdev_err(offl_work-\u003ervu-\u003edev,\n--\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c-213-\tif (!list_empty(\u0026l2_offl_lh))\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c:214:\t\tqueue_work(rvu_sw_l2_offl_wq, \u0026l2_offl_work.work);\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c-215-}\n--\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c=217=static bool fw_is_up;\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c-218-\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c:219:int rvu_sw_l2_init_offl_wq(struct rvu *rvu, u16 pcifunc, bool fw_up)\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c-220-{\n--\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c-230-\t\tl2_offl_work.rvu = rvu;\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c:231:\t\tINIT_WORK(\u0026l2_offl_work.work, rvu_sw_l2_offl_rule_wq_handler);\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c:232:\t\trvu_sw_l2_offl_wq = alloc_workqueue(\"swdev_rvu_sw_l2_offl_wq\", 0, 0);\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c:233:\t\tif (!rvu_sw_l2_offl_wq) {\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c-234-\t\t\tdev_err(rvu-\u003edev, \"L2 offl workqueue allocation failed\\n\");\n--\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c-238-\t\tfdb_refresh_work.rvu = rvu;\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c:239:\t\tINIT_WORK(\u0026fdb_refresh_work.work, rvu_sw_l2_fdb_refresh_wq_handler);\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c-240-\t\tfdb_refresh_wq = alloc_workqueue(\"swdev_fdb_refresg_wq\", 0, 0);\n--\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c-255-\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c:256:int rvu_sw_l2_fdb_list_entry_add(struct rvu *rvu, u16 pcifunc, u8 *mac)\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c-257-{\n--\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c=275=int rvu_mbox_handler_fdb_notify(struct rvu *rvu,\n--\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c-287-\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c:288:\tl2_entry-\u003eport_id = rvu_sw_port_id(rvu, req-\u003ehdr.pcifunc);\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c-289-\tether_addr_copy(l2_entry-\u003emac, req-\u003emac);\n--\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c-295-\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c:296:\tqueue_work(rvu_sw_l2_offl_wq, \u0026l2_offl_work.work);\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c-297-\n--\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c-300-\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c:301:void rvu_sw_l2_shutdown(void)\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c-302-{\n--\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c-324-\tcancel_work_sync(\u0026l2_offl_work.work);\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c:325:\tdestroy_workqueue(rvu_sw_l2_offl_wq);\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c-326-\n--\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.h-10-\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.h:11:int rvu_sw_l2_init_offl_wq(struct rvu *rvu, u16 pcifunc, bool fw_up);\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.h:12:int rvu_sw_l2_fdb_list_entry_add(struct rvu *rvu, u16 pcifunc, u8 *mac);\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.h:13:void rvu_sw_l2_shutdown(void);\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.h-14-#endif\n--\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l3.c-10-#include \"rvu_sw.h\"\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l3.c:11:#include \"rvu_sw_l3.h\"\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l3.c-12-\n--\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l3.c=47=static DECLARE_DELAYED_WORK(l3_offl_work, sw_l3_offl_work_handler);\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l3.c-48-\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l3.c:49:static int rvu_sw_l3_offl_rule_push(struct list_head *lh)\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l3.c-50-{\n--\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l3.c=116=static void sw_l3_offl_work_handler(struct work_struct *work)\n--\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l3.c-172-\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l3.c:173:\tif (rvu_sw_l3_offl_rule_push(\u0026l3lh))\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l3.c-174-\t\tpr_err(\"%s: Error to push rules\\n\", __func__);\n--\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l3.c=179=int rvu_mbox_handler_fib_notify(struct rvu *rvu,\n--\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l3.c-197-\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l3.c:198:\tl3_entry-\u003eport_id = rvu_sw_port_id(rvu, req-\u003ehdr.pcifunc);\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l3.c-199-\tl3_entry-\u003ervu = rvu;\n--\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l3.c-217-\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l3.c:218:void rvu_sw_l3_shutdown(void)\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l3.c-219-{\n--\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l3.h-10-\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l3.h:11:void rvu_sw_l3_shutdown(void);\ndrivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l3.h-12-#endif\n"
} |
| 67/2 | 2026/07/02 11:20 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 121313 output: 51 thoughts: 290 Thoughts: **Analyzing the AF Driver's Mailbox Handlers** Okay, so I'm looking at the AF driver and it's clear there's switch code spread across `rvu_sw.c`, `rvu_sw_l2.c`, `rvu_sw_l3.c`, and `rvu_sw_fl.c`. Right, and the entry points for these are the `rvu_mbox_handler_` functions: `swdev2af_notify`, `fdb_notify`, `fib_notify`, `fl_notify`, and `fl_get_stats`. These are clearly mailbox handlers, triggered by messages from the PF driver. Now, here's where it gets interesting. I remember the PF driver has a problem with probing because the AF driver may not be present, or if it is, the mailbox communication times out. That makes sense, syzkaller's vhci doesn't fully simulate the hardware interrupts and shared memory setup that's essential for the mailbox communication to work. Consequently, if the PF driver can't probe, it's never going to send any of these mailbox messages. But what if the AF driver is fuzzed directly via vhci? Could it receive garbage mailbox messages if syzkaller can somehow trigger the mailbox interrupt and populate shared memory with random data? That's a definite possibility. Hold on a second… isn't `rvu_afpf_mbox_handler` the actual interrupt handler for the AF side? Okay, let's go check that function. |
| 68/2 | 2026/07/02 11:20 | tool | read-file |
0mArgs:{
"File": "drivers/net/ethernet/marvell/octeontx2/af/rvu.c",
"FirstLine": 2528,
"LineCount": 50
}Results: {
"Contents": "2528:\t\n2529:\t\t__rvu_mbox_handler(mwork, TYPE_AFVF, false);\n2530:\t}\n2531:\t\n2532:\tstatic void __rvu_mbox_up_handler(struct rvu_work *mwork, int type)\n2533:\t{\n2534:\t\tstruct rvu *rvu = mwork-\u003ervu;\n2535:\t\tstruct otx2_mbox_dev *mdev;\n2536:\t\tstruct mbox_hdr *rsp_hdr;\n2537:\t\tstruct mbox_msghdr *msg;\n2538:\t\tstruct mbox_wq_info *mw;\n2539:\t\tstruct otx2_mbox *mbox;\n2540:\t\tint offset, id, devid;\n2541:\t\n2542:\t\tswitch (type) {\n2543:\t\tcase TYPE_AFPF:\n2544:\t\t\tmw = \u0026rvu-\u003eafpf_wq_info;\n2545:\t\t\tbreak;\n2546:\t\tcase TYPE_AFVF:\n2547:\t\t\tmw = \u0026rvu-\u003eafvf_wq_info;\n2548:\t\t\tbreak;\n2549:\t\tdefault:\n2550:\t\t\treturn;\n2551:\t\t}\n2552:\t\n2553:\t\tdevid = mwork - mw-\u003embox_wrk_up;\n2554:\t\tmbox = \u0026mw-\u003embox_up;\n2555:\t\tmdev = \u0026mbox-\u003edev[devid];\n2556:\t\n2557:\t\trsp_hdr = mdev-\u003embase + mbox-\u003erx_start;\n2558:\t\tif (mw-\u003embox_wrk_up[devid].up_num_msgs == 0) {\n2559:\t\t\tdev_warn(rvu-\u003edev, \"mbox up handler: num_msgs = 0\\n\");\n2560:\t\t\treturn;\n2561:\t\t}\n2562:\t\n2563:\t\toffset = mbox-\u003erx_start + ALIGN(sizeof(*rsp_hdr), MBOX_MSG_ALIGN);\n2564:\t\n2565:\t\tfor (id = 0; id \u003c mw-\u003embox_wrk_up[devid].up_num_msgs; id++) {\n2566:\t\t\tmsg = mdev-\u003embase + offset;\n2567:\t\n2568:\t\t\tif (msg-\u003eid \u003e= MBOX_MSG_MAX) {\n2569:\t\t\t\tdev_err(rvu-\u003edev,\n2570:\t\t\t\t\t\"Mbox msg with unknown ID 0x%x\\n\", msg-\u003eid);\n2571:\t\t\t\tgoto end;\n2572:\t\t\t}\n2573:\t\n2574:\t\t\tif (msg-\u003esig != OTX2_MBOX_RSP_SIG) {\n2575:\t\t\t\tdev_err(rvu-\u003edev,\n2576:\t\t\t\t\t\"Mbox msg with wrong signature %x, ID 0x%x\\n\",\n2577:\t\t\t\t\tmsg-\u003esig, msg-\u003eid);\n"
} |
| 69/2 | 2026/07/02 11:20 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 122782 output: 51 thoughts: 13 Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience, presented in the first person: **Handling the Requests** Alright, so the core of it boils down to this: `__rvu_mbox_handler` is the workhorse here. It's the function that's ultimately responsible for actually processing the requests we're throwing at it. No fluff, no mystery, that's the primary function's role. It's the entry point, the dispatcher, the... well, the handler. Understanding its internal logic is key, and I’ll be diving into the implementation details there soon enough. We'll examine the data structures, see how the requests are parsed and interpreted, and ultimately, how they lead to the proper action and response. |
| 70/2 | 2026/07/02 11:20 | tool | read-file |
0mArgs:{
"File": "drivers/net/ethernet/marvell/octeontx2/af/rvu.c",
"FirstLine": 2460,
"LineCount": 50
}Results: {
"Contents": "2460:\t\t\t\t RVU_AF_BAR2_PFID);\n2461:\t\t\tif (type == TYPE_AFPF)\n2462:\t\t\t\trvu_write64(rvu, BLKADDR_NIX0,\n2463:\t\t\t\t\t AF_BAR2_ALIASX(0, NIX_CINTX_INT_W1S(devid)),\n2464:\t\t\t\t\t 0x1);\n2465:\t\t\telse\n2466:\t\t\t\trvu_write64(rvu, BLKADDR_NIX0,\n2467:\t\t\t\t\t AF_BAR2_ALIASX(0, NIX_QINTX_CNT(devid)),\n2468:\t\t\t\t\t 0x1);\n2469:\t\t\tusleep_range(5000, 6000);\n2470:\t\t\tgoto done;\n2471:\t\t}\n2472:\t\n2473:\t\tfor (id = 0; id \u003c mw-\u003embox_wrk[devid].num_msgs; id++) {\n2474:\t\t\tmsg = mdev-\u003embase + offset;\n2475:\t\n2476:\t\t\t/* Set which PF/VF sent this message based on mbox IRQ */\n2477:\t\t\tswitch (type) {\n2478:\t\t\tcase TYPE_AFPF:\n2479:\t\t\t\tmsg-\u003epcifunc \u0026= rvu_pcifunc_pf_mask(rvu-\u003epdev);\n2480:\t\t\t\tmsg-\u003epcifunc |= rvu_make_pcifunc(rvu-\u003epdev, devid, 0);\n2481:\t\t\t\tbreak;\n2482:\t\t\tcase TYPE_AFVF:\n2483:\t\t\t\tmsg-\u003epcifunc \u0026=\n2484:\t\t\t\t\t~(RVU_PFVF_FUNC_MASK \u003c\u003c RVU_PFVF_FUNC_SHIFT);\n2485:\t\t\t\tmsg-\u003epcifunc |= (devid \u003c\u003c RVU_PFVF_FUNC_SHIFT) + 1;\n2486:\t\t\t\tbreak;\n2487:\t\t\t}\n2488:\t\n2489:\t\t\terr = rvu_process_mbox_msg(mbox, devid, msg);\n2490:\t\t\tif (!err) {\n2491:\t\t\t\toffset = mbox-\u003erx_start + msg-\u003enext_msgoff;\n2492:\t\t\t\tcontinue;\n2493:\t\t\t}\n2494:\t\n2495:\t\t\tif (msg-\u003epcifunc \u0026 RVU_PFVF_FUNC_MASK)\n2496:\t\t\t\tdev_warn(rvu-\u003edev, \"Error %d when processing message %s (0x%x) from PF%d:VF%d\\n\",\n2497:\t\t\t\t\t err, otx2_mbox_id2name(msg-\u003eid),\n2498:\t\t\t\t\t msg-\u003eid, rvu_get_pf(rvu-\u003epdev, msg-\u003epcifunc),\n2499:\t\t\t\t\t (msg-\u003epcifunc \u0026 RVU_PFVF_FUNC_MASK) - 1);\n2500:\t\t\telse\n2501:\t\t\t\tdev_warn(rvu-\u003edev, \"Error %d when processing message %s (0x%x) from PF%d\\n\",\n2502:\t\t\t\t\t err, otx2_mbox_id2name(msg-\u003eid),\n2503:\t\t\t\t\t msg-\u003eid, devid);\n2504:\t\t}\n2505:\tdone:\n2506:\t\tmw-\u003embox_wrk[devid].num_msgs = 0;\n2507:\t\n2508:\t\tif (!is_cn20k(mbox-\u003epdev) \u0026\u0026 poll)\n2509:\t\t\totx2_mbox_wait_for_zero(mbox, devid);\n"
} |
| 71/2 | 2026/07/02 11:20 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 123951 output: 21 thoughts: 582 Thoughts: **Analyzing Reachability of OcteonTX2 Switch Code via Syzkaller** Okay, so I'm trying to figure out if syzkaller can hit the mailbox interrupt and inject arbitrary messages. The problem is syzkaller's vhci doesn't trigger hardware interrupts. It's just fuzzing MMIO reads and writes. That means the `__rvu_mbox_handler` function, and therefore `rvu_mbox_handler`, never gets called directly. Wait, `rvu_mbox_handler` is called via the workqueue from `rvu_afpf_mbox_handler`. The workqueue gets scheduled from the interrupt handler `rvu_mbox_intr_handler`. If the interrupt never happens, the workqueue is never scheduled, and therefore, the mailbox messages are never processed. Consequently, that switch code within the AF driver becomes unreachable. Now, I need to check for alternative paths. Is there any way to reach that switch code besides the interrupt path? Could something like `sw_nb_register` get me there? I already looked, and there's no chance `rvu_eswitch_config` is called from elsewhere. What about `otx2_devlink_eswitch_mode_set`? Aha, through devlink! But the devlink device only gets registered if either `otx2_probe` or `rvu_rep_probe` succeeds. And they both fail because `otx2_init_rsrc` fails. And `otx2_init_rsrc` fails because it's waiting for a mailbox response from the AF driver. The mailbox response is never received because the AF driver isn't present, or the mailbox communication itself fails since syzkaller's vhci doesn't emulate the hardware interrupts or shared memory semantics. Alright, what if the driver is compiled with `CONFIG_OCTEONTX_SWITCH=y` and we run this on actual Marvell OcteonTX2 hardware? Syzkaller doesn't have that hardware. The documentation says to return `WorthFuzzing=false` if the code path is impossible to reach in virtualized environments. This is a hardware-specific driver, for Marvell OcteonTX2. There's no software emulation available for it in either QEMU or the kernel. Since syzkaller's vhci cannot successfully probe the driver to even *reach* the switch code, the code paths are impossible to hit in a virtualized environment. Just to be absolutely sure, I'll quickly double-check: is there any `hwsim` or `dummy` driver for the Marvell OcteonTX2? |
| 72/2 | 2026/07/02 11:20 | tool | grepper |
0mError:no matches Args: {
"Expression": "octeontx2.*sim"
}Results: {
"error": "no matches"
} |
| 73/2 | 2026/07/02 11:20 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 125130 output: 51 thoughts: 1117 Thoughts: **Unreachable Code in Virtualized Environments: Marvell OcteonTX2 Switch Driver** Okay, so the question is, can I actually fuzz this Marvell OcteonTX2 switch driver code? The initial assessment is a resounding *no*. The patch adds switchdev support for the OcteonTX2, specifically targeting hardware that, as far as I can tell, is not supported by QEMU. This is a problem. The added code, residing primarily under `drivers/net/ethernet/marvell/octeontx2/af/switch/` and `drivers/net/ethernet/marvell/octeontx2/nic/switch/`, hinges on the `sw_nb_register` function. This is key: it initializes the switch code, but critically, it's tied to the successful probing of the NIC driver. The NIC driver probe, however, requires successful mailbox communication with the Admin Function (AF) driver. This mailbox interaction is the rub: it relies on hardware interrupts and shared memory. In a virtualized setting, say with QEMU or GCE (which I assume is the primary fuzzing environment), these hardware-specific dependencies are non-existent. The mailbox communication will inevitably timeout, resulting in a failed driver probe. Without the NIC driver probing, the devlink device will not be registered. *Therefore*, the switch code in the NIC driver becomes completely unreachable. Similarly, the switch code in the AF driver is triggered by those same mailbox messages coming from the NIC driver. If the NIC driver fails to probe, no messages are sent, and that code remains dormant. Even if I were to somehow try to fuzz the AF driver directly, the mailbox messages are processed in a workqueue scheduled by a hardware interrupt. If the virtualization layer, such as syzkaller's vhci, *doesn't* trigger these hardware interrupts, the messages remain unexecuted. Thus, the AF driver's switch code is also inaccessible. So, I’ve established that the primary code paths are dead ends in virtualized environments. Let's make this explicit. The patch's switch code depends on a successful NIC driver probe, which depends on hardware-specific communication that fails in virtualization. Now, let's explore if there are any other entrypoints to the switch code: I need to rule out *every* possible entry point. My initial check looked at `sw_fdb_init`, `sw_fib_init`, `sw_fl_init`, and discovered they are only called from `sw_nb_register`. `sw_fdb_add_to_list` is called from `sw_nb_fdb_event`, which is a switchdev notifier callback. That notifier is also only registered in `sw_nb_register`. `sw_fib_add_to_list` is similar and called from various notifier callbacks, all registered in the function that is not reached. What about `sw_fl_setup_ft_block_ingress_cb`? Ah, it's called from `sw_fl_parse_actions` when `FLOW_ACTION_CT` is used or registered as a block cb. But wait: `sw_fl_setup_ft_block_ingress_cb` is passed to `nf_flow_table_offload_add_cb`. It is only called if `sw_fl_add` is called, which is called from `sw_fl_setup_ft_block_ingress_cb` itself. This function is the block callback. How is it registered? It is not registered as a block cb for the netdev. *Ah ha!* `otx2_setup_tc` in `otx2_tc.c` registers it as a block callback. I need to look into this function and see if there are any avenues here to trigger that code path. |
| 74/2 | 2026/07/02 11:21 | tool | grepper |
0mArgs:{
"Expression": "sw_fl_setup_ft_block_ingress_cb",
"PathPrefix": "drivers/net/ethernet/marvell/octeontx2/nic/"
}Results: {
"Output": "drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.c-24-#if !IS_ENABLED(CONFIG_OCTEONTX_SWITCH)\ndrivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.c:25:int sw_fl_setup_ft_block_ingress_cb(enum tc_setup_type type,\ndrivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.c-26-\t\t\t\t void *type_data, void *cb_priv)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.c=124=static int sw_fl_parse_actions(struct otx2_nic *nic,\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.c-157-\t\t\terr = nf_flow_table_offload_add_cb(act-\u003ect.flow_table,\ndrivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.c:158:\t\t\t\t\t\t\t sw_fl_setup_ft_block_ingress_cb,\ndrivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.c-159-\t\t\t\t\t\t\t nic);\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.c=524=static bool init_done;\ndrivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.c-525-\ndrivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.c:526:int sw_fl_setup_ft_block_ingress_cb(enum tc_setup_type type,\ndrivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.c-527-\t\t\t\t void *type_data, void *cb_priv)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.h=11=int sw_fl_init(void);\ndrivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.h:12:int sw_fl_setup_ft_block_ingress_cb(enum tc_setup_type type,\ndrivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.h-13-\t\t\t\t void *type_data, void *cb_priv);\n"
} |
| 75/2 | 2026/07/02 11:21 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 127765 output: 27 thoughts: 35 Thoughts: Okay, let me think this through. **Analyzing Function Call Locations** Hmm, so `sw_fl_setup_ft_block_ingress_cb`. That function, as far as I can see, is exclusively used within `sw_fl.c`. That seems a bit odd, doesn't it? I need to be absolutely sure, though. Is there anywhere else in the code base – even indirectly – where this callback is being invoked? I need to double-check every possible call site, cross-reference anything that might be using it or depending on its output. A function's scope and its usage are key to understanding the overall design, and I don't want to miss anything. I'll need to run a quick search and make sure my initial assessment is correct. Making sure is key to designing a good solution, the last thing I need is a silly bug! |
| 76/2 | 2026/07/02 11:21 | tool | grepper |
0mArgs:{
"Expression": "sw_fl_setup_ft_block_ingress_cb"
}Results: {
"Output": "drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.c-24-#if !IS_ENABLED(CONFIG_OCTEONTX_SWITCH)\ndrivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.c:25:int sw_fl_setup_ft_block_ingress_cb(enum tc_setup_type type,\ndrivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.c-26-\t\t\t\t void *type_data, void *cb_priv)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.c=124=static int sw_fl_parse_actions(struct otx2_nic *nic,\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.c-157-\t\t\terr = nf_flow_table_offload_add_cb(act-\u003ect.flow_table,\ndrivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.c:158:\t\t\t\t\t\t\t sw_fl_setup_ft_block_ingress_cb,\ndrivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.c-159-\t\t\t\t\t\t\t nic);\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.c=524=static bool init_done;\ndrivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.c-525-\ndrivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.c:526:int sw_fl_setup_ft_block_ingress_cb(enum tc_setup_type type,\ndrivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.c-527-\t\t\t\t void *type_data, void *cb_priv)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.h=11=int sw_fl_init(void);\ndrivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.h:12:int sw_fl_setup_ft_block_ingress_cb(enum tc_setup_type type,\ndrivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.h-13-\t\t\t\t void *type_data, void *cb_priv);\n"
} |
| 77/2 | 2026/07/02 11:21 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 128664 output: 49 thoughts: 18 Thoughts: Okay, here's my summary, presented as a stream of thought: **Checking Code Dependencies** Alright, let's see... I'm pretty sure I've seen a similar function like this before. Hold on, wait a second. Is this function actually used within the `otx2_tc.c` file? I need to quickly verify that it's being used. If it's not used there, that changes my assessment of its overall importance and the potential impact of any modifications I might make. I need to know the context of its usage, and that file seems like a good place to start my investigation. |
| 78/2 | 2026/07/02 11:21 | tool | grepper |
0mError:no matches Args: {
"Expression": "sw_fl_",
"PathPrefix": "drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c"
}Results: {
"error": "no matches"
} |
| 79/2 | 2026/07/02 11:21 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 128878 output: 45 thoughts: 128 Thoughts: **Tracing the `sw_fl_setup_ft_block_ingress_cb` Call Chain** Okay, let's break this down. My initial understanding is that `sw_fl_setup_ft_block_ingress_cb` gets called through a pretty specific path: `nf_flow_table_offload_add_cb` -> `sw_fl_parse_actions` -> `sw_fl_add` -> and finally, back to `sw_fl_setup_ft_block_ingress_cb`. That's a cyclical dependency, which immediately raises a red flag. How does it bootstrap itself? I need to understand the initial trigger. This calls for deeper inspection. Is this the only registration point? Let me cross-reference the function's registration. I need to make sure I'm not missing any other potential call sites. My next step is to examine `otx2_setup_tc`, which is a likely candidate for initialization or primary setup within this context. I'll focus on this area to confirm my current understanding. I'm looking for the foundational call. |
| 80/2 | 2026/07/02 11:21 | tool | grepper |
0mArgs:{
"Expression": "otx2_setup_tc",
"PathPrefix": "drivers/net/ethernet/marvell/octeontx2/nic/"
}Results: {
"Output": "drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h=1178=void otx2_shutdown_tc(struct otx2_nic *nic);\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h:1179:int otx2_setup_tc(struct net_device *netdev, enum tc_setup_type type,\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h-1180-\t\t void *type_data);\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h=1236=int rvu_event_up_notify(struct otx2_nic *pf, struct rep_event *info);\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h:1237:int otx2_setup_tc_cls_flower(struct otx2_nic *nic,\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h-1238-\t\t\t struct flow_cls_offload *cls_flower);\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c=2921=static const struct net_device_ops otx2_netdev_ops = {\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c-2938-\t.ndo_xdp_xmit = otx2_xdp_xmit,\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c:2939:\t.ndo_setup_tc\t\t= otx2_setup_tc,\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c-2940-\t.ndo_set_vf_trust\t= otx2_ndo_set_vf_trust,\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c=1356=static int otx2_tc_get_flow_stats(struct otx2_nic *nic,\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c-1412-\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c:1413:int otx2_setup_tc_cls_flower(struct otx2_nic *nic,\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c-1414-\t\t\t struct flow_cls_offload *cls_flower)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c-1426-}\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c:1427:EXPORT_SYMBOL(otx2_setup_tc_cls_flower);\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c-1428-\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c=1478=static int otx2_tc_ingress_matchall_delete(struct otx2_nic *nic,\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c-1493-\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c:1494:static int otx2_setup_tc_ingress_matchall(struct otx2_nic *nic,\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c-1495-\t\t\t\t\t struct tc_cls_matchall_offload *cls_matchall)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c-1509-\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c:1510:static int otx2_setup_tc_block_ingress_cb(enum tc_setup_type type,\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c-1511-\t\t\t\t\t void *type_data, void *cb_priv)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c-1527-\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c:1528:\t\treturn otx2_setup_tc_cls_flower(nic, type_data);\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c-1529-\tcase TC_SETUP_CLSMATCHALL:\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c:1530:\t\treturn otx2_setup_tc_ingress_matchall(nic, type_data);\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c-1531-\tdefault:\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c-1537-\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c:1538:static int otx2_setup_tc_egress_matchall(struct otx2_nic *nic,\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c-1539-\t\t\t\t\t struct tc_cls_matchall_offload *cls_matchall)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c-1553-\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c:1554:static int otx2_setup_tc_block_egress_cb(enum tc_setup_type type,\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c-1555-\t\t\t\t\t void *type_data, void *cb_priv)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c-1563-\tcase TC_SETUP_CLSMATCHALL:\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c:1564:\t\treturn otx2_setup_tc_egress_matchall(nic, type_data);\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c-1565-\tdefault:\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c=1572=static LIST_HEAD(otx2_block_cb_list);\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c-1573-\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c:1574:static int otx2_setup_tc_block(struct net_device *netdev,\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c-1575-\t\t\t struct flow_block_offload *f)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c-1584-\tif (f-\u003ebinder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS) {\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c:1585:\t\tcb = otx2_setup_tc_block_ingress_cb;\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c-1586-\t\tingress = true;\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c-1587-\t} else if (f-\u003ebinder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS) {\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c:1588:\t\tcb = otx2_setup_tc_block_egress_cb;\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c-1589-\t\tingress = false;\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c-1597-\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c:1598:int otx2_setup_tc(struct net_device *netdev, enum tc_setup_type type,\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c-1599-\t\t void *type_data)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c-1602-\tcase TC_SETUP_BLOCK:\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c:1603:\t\treturn otx2_setup_tc_block(netdev, type_data);\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c-1604-\tcase TC_SETUP_QDISC_HTB:\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c:1605:\t\treturn otx2_setup_tc_htb(netdev, type_data);\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c-1606-\tdefault:\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c-1609-}\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c:1610:EXPORT_SYMBOL(otx2_setup_tc);\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c-1611-\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c=541=static const struct net_device_ops otx2vf_netdev_ops = {\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c-551-\t.ndo_tx_timeout = otx2_tx_timeout,\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c:552:\t.ndo_setup_tc = otx2_setup_tc,\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c-553-\t.ndo_hwtstamp_get = otx2_config_hwtstamp_get,\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/qos.c=1700=void otx2_qos_config_txschq(struct otx2_nic *pfvf)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/qos.c-1731-\ndrivers/net/ethernet/marvell/octeontx2/nic/qos.c:1732:int otx2_setup_tc_htb(struct net_device *ndev, struct tc_htb_qopt_offload *htb)\ndrivers/net/ethernet/marvell/octeontx2/nic/qos.c-1733-{\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/qos.h=23=u64 otx2_get_txschq_rate_regval(struct otx2_nic *nic, u64 maxrate, u32 burst);\ndrivers/net/ethernet/marvell/octeontx2/nic/qos.h-24-\ndrivers/net/ethernet/marvell/octeontx2/nic/qos.h:25:int otx2_setup_tc_htb(struct net_device *ndev, struct tc_htb_qopt_offload *htb);\ndrivers/net/ethernet/marvell/octeontx2/nic/qos.h-26-int otx2_qos_get_qid(struct otx2_nic *pfvf);\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/rep.c=107=static int rvu_rep_setup_tc_cb(enum tc_setup_type type,\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/rep.c-125-\tcase TC_SETUP_CLSFLOWER:\ndrivers/net/ethernet/marvell/octeontx2/nic/rep.c:126:\t\treturn otx2_setup_tc_cls_flower(priv, type_data);\ndrivers/net/ethernet/marvell/octeontx2/nic/rep.c-127-\tdefault:\n"
} |
| 81/2 | 2026/07/02 11:21 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 132750 output: 51 thoughts: 274 Thoughts: **My Analysis of `sw_fl_setup_ft_block_ingress_cb`** Okay, let's break this down. It looks like `sw_fl_setup_ft_block_ingress_cb` is exclusively used within `sw_fl.c`, and importantly, it's being passed as a parameter to `nf_flow_table_offload_add_cb`. Right away, that raises a few questions. I see it's *not* registered directly as a block callback for the net device, which is a key observation. Furthermore, my initial scan reveals that its use is strictly tied to `FLOW_ACTION_CT` flow actions. That's a good filtering criterion. And the code shows that `sw_fl_add` is invoked from within `sw_fl_setup_ft_block_ingress_cb`. That's a classic chicken-and-egg problem; it raises a question. How is `sw_fl_setup_ft_block_ingress_cb` being called for the *first* time? Aha! `nf_flow_table_offload_add_cb` is registering it, which clears that up. Still, there is an interesting circular dependency in the call path. Because I see that `sw_fl_parse_actions` is invoked from `sw_fl_add`. And `sw_fl_add` is also called from `sw_fl_setup_ft_block_ingress_cb`. So the registration is fine, but it means that `sw_fl_setup_ft_block_ingress_cb` *must* be registered *somewhere* else first. Wait, is `sw_fl_setup_ft_block_ingress_cb` exported? No, it's not. Is it used anywhere else that I can see? Time to take a closer look at `sw_fl.h`. I'll have to analyze the header file to confirm my assumptions and ensure no external usage exists. |
| 82/2 | 2026/07/02 11:21 | tool | read-file |
0mArgs:{
"File": "drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.h",
"FirstLine": 1,
"LineCount": 20
}Results: {
"Contents": " 1:\t/* SPDX-License-Identifier: GPL-2.0 */\n 2:\t/* Marvell switch driver\n 3:\t *\n 4:\t * Copyright (C) 2026 Marvell.\n 5:\t *\n 6:\t */\n 7:\t#ifndef SW_FL_H_\n 8:\t#define SW_FL_H_\n 9:\t\n 10:\tvoid sw_fl_deinit(void);\n 11:\tint sw_fl_init(void);\n 12:\tint sw_fl_setup_ft_block_ingress_cb(enum tc_setup_type type,\n 13:\t\t\t\t\t void *type_data, void *cb_priv);\n 14:\t\n 15:\t#endif // SW_FL_H\n"
} |
| 83/2 | 2026/07/02 11:21 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 133707 output: 27 thoughts: 28 Thoughts: Okay, here's my attempt at summarizing that thought process, tailored for an expert audience: **Investigating `sw_fl_setup_ft_block_ingress_cb` Usage** Alright, let's dig into this. My initial focus is on understanding the scope of that `sw_fl_setup_ft_block_ingress_cb` function. I need to figure out where this function is used beyond its definition in `sw_fl.c`. Is it only called internally within that module, or does it serve as an externally-facing API, or even worse, something that’s unintentionally exposed? The latter can be a security risk. I want to know if some other part of the system is relying on this callback function. Is it part of some integration? Is this called from some other module that's triggering the execution? I need to see if there are any cross-references to understand the dependencies. I'm thinking I'll need to use my tools – grepping the entire codebase, running a static analysis, or maybe even using the debugger to do a runtime inspection, depending on how robust the code base is. This is all about gaining a complete picture of the dependencies, and possible vulnerabilities. It is also important to consider if this function is only defined in a particular build configuration or if it can be called from different modules. |
| 84/2 | 2026/07/02 11:21 | tool | grepper |
0mArgs:{
"Expression": "sw_fl_setup_ft_block_ingress_cb"
}Results: {
"Output": "drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.c-24-#if !IS_ENABLED(CONFIG_OCTEONTX_SWITCH)\ndrivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.c:25:int sw_fl_setup_ft_block_ingress_cb(enum tc_setup_type type,\ndrivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.c-26-\t\t\t\t void *type_data, void *cb_priv)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.c=124=static int sw_fl_parse_actions(struct otx2_nic *nic,\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.c-157-\t\t\terr = nf_flow_table_offload_add_cb(act-\u003ect.flow_table,\ndrivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.c:158:\t\t\t\t\t\t\t sw_fl_setup_ft_block_ingress_cb,\ndrivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.c-159-\t\t\t\t\t\t\t nic);\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.c=524=static bool init_done;\ndrivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.c-525-\ndrivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.c:526:int sw_fl_setup_ft_block_ingress_cb(enum tc_setup_type type,\ndrivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.c-527-\t\t\t\t void *type_data, void *cb_priv)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.h=11=int sw_fl_init(void);\ndrivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.h:12:int sw_fl_setup_ft_block_ingress_cb(enum tc_setup_type type,\ndrivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.h-13-\t\t\t\t void *type_data, void *cb_priv);\n"
} |
| 85/2 | 2026/07/02 11:21 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 134697 output: 54 thoughts: 359 Thoughts: **Unreachable Code in Virtualized Environment** Okay, so I'm looking at this code and immediately I see something strange. This particular function isn't called *anywhere* else in the codebase. That's a huge red flag. If it's not called, how is it even supposed to execute? Ah, right. I see it now. It's only invoked when `FLOW_ACTION_CT` is used, and then it registers a callback within the flow table. But that still doesn't explain how the *initial* flow gets added to the table. This feels like a dead end, or perhaps a patch that never landed correctly. Either way, it looks completely unreachable as it stands. If this switch code is unreachable, then the implication is that the entire section is unreachable in a virtualized environment. Consequently, I have to classify this as not worth fuzzing, and I will be setting `WorthFuzzing=false`. Before I commit to that conclusion, let me quickly double-check the other modified files to ensure I'm not missing any critical pieces of the puzzle. I'll need to review: `drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c` `drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c` `drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c` `drivers/net/ethernet/marvell/octeontx2/af/rvu_rep.c` `drivers/net/ethernet/marvell/octeontx2/af/rvu.c` `drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.h` `drivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c` `drivers/net/ethernet/marvell/octeontx2/nic/rep.c` And starting with `rvu_nix.c`... |
| 86/2 | 2026/07/02 11:21 | tool | read-file |
0mArgs:{
"File": "drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c",
"FirstLine": 2138,
"LineCount": 50
}Results: {
"Contents": "2138:\t\tif (!req_schq)\n2139:\t\t\treturn 0;\n2140:\t\n2141:\t\tif (req-\u003eflags \u0026 NIX_TXSCH_ALLOC_FLAG_PAN)\n2142:\t\t\tlink = hw-\u003ecgx_links + hw-\u003elbk_links + 1;\n2143:\t\telse\n2144:\t\t\tlink = nix_get_tx_link(rvu, pcifunc);\n2145:\t\n2146:\t\t/* For traffic aggregating scheduler level, one queue is enough */\n2147:\t\tif (lvl \u003e= hw-\u003ecap.nix_tx_aggr_lvl) {\n2148:\t\t\tif (req_schq != 1 \u0026\u0026 !(req-\u003eflags \u0026 NIX_TXSCH_ALLOC_FLAG_PAN))\n2149:\t\t\t\treturn NIX_AF_ERR_TLX_ALLOC_FAIL;\n2150:\t\t\treturn 0;\n2151:\t\t}\n2152:\t\n2153:\t\t/* Get free SCHQ count and check if request can be accomodated */\n2154:\t\tif (hw-\u003ecap.nix_fixed_txschq_mapping) {\n2155:\t\t\tnix_get_txschq_range(rvu, pcifunc, link, \u0026start, \u0026end);\n2156:\t\t\tschq = start + (pcifunc \u0026 RVU_PFVF_FUNC_MASK);\n2157:\t\t\tif (end \u003c= txsch-\u003eschq.max \u0026\u0026 schq \u003c end \u0026\u0026\n2158:\t\t\t !test_bit(schq, txsch-\u003eschq.bmap))\n2159:\t\t\t\tfree_cnt = 1;\n2160:\t\t\telse\n2161:\t\t\t\tfree_cnt = 0;\n2162:\t\t} else {\n2163:\t\t\tfree_cnt = rvu_rsrc_free_count(\u0026txsch-\u003eschq);\n2164:\t\t}\n2165:\t\n2166:\t\tif (free_cnt \u003c req_schq || req-\u003eschq[lvl] \u003e MAX_TXSCHQ_PER_FUNC ||\n2167:\t\t req-\u003eschq_contig[lvl] \u003e MAX_TXSCHQ_PER_FUNC)\n2168:\t\t\treturn NIX_AF_ERR_TLX_ALLOC_FAIL;\n2169:\t\n2170:\t\t/* If contiguous queues are needed, check for availability */\n2171:\t\tif (!hw-\u003ecap.nix_fixed_txschq_mapping \u0026\u0026 req-\u003eschq_contig[lvl] \u0026\u0026\n2172:\t\t !rvu_rsrc_check_contig(\u0026txsch-\u003eschq, req-\u003eschq_contig[lvl]))\n2173:\t\t\treturn NIX_AF_ERR_TLX_ALLOC_FAIL;\n2174:\t\n2175:\t\treturn 0;\n2176:\t}\n2177:\t\n2178:\tstatic void nix_txsch_alloc(struct rvu *rvu, struct nix_txsch *txsch,\n2179:\t\t\t\t struct nix_txsch_alloc_rsp *rsp,\n2180:\t\t\t\t int lvl, int start, int end)\n2181:\t{\n2182:\t\tstruct rvu_hwinfo *hw = rvu-\u003ehw;\n2183:\t\tu16 pcifunc = rsp-\u003ehdr.pcifunc;\n2184:\t\tint idx, schq;\n2185:\t\tbool alloc;\n2186:\t\n2187:\t\t/* For traffic aggregating levels, queue alloc is based\n"
} |
| 87/2 | 2026/07/02 11:21 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 136490 output: 193 thoughts: 551 Thoughts: **Unreachable Code in a Virtualized Environment** Okay, so I've been looking at these patches, specifically the changes in `rvu_nix.c`, `rvu_npc.c`, and `rvu_npc_fs.c`. It's all about modifications to the mailbox handlers within the AF driver. As we know, those handlers are *only* called when the PF driver sends a message through the mailbox, which *requires* that hardware interrupt to be triggered. Now, the problem is that Syzkaller's vhci, in a virtualized environment, just *doesn't* trigger those hardware interrupts. My initial thought was, could Syzkaller maybe bypass the interrupt and fuzz the AF driver directly? But, Syzkaller fuzzes ioctls, not mailbox messages. These mailbox messages are processed within a workqueue, `rvu_mbox_handler`, which is scheduled by `rvu_mbox_intr_handler`. And if we can't get that interrupt to fire, that workqueue is never scheduled. Are there *any* other ways to trigger the workqueue? Nope, nothing. What about other interfaces? Devlink? But devlink is only registered if the driver probes successfully. And the driver probe fails because of that darn mailbox timeout. So, devlink is unreachable. Netlink? The switchdev notifiers aren't registered. So that's out. Ethtool? The netdev isn't registered because of the probe failure. Unreachable. Sysfs? Driver probe fails, so no sysfs entries are created. Basically, *this entire patch*, this whole switchdev support thing, is unreachable in a virtualized environment. The driver needs that hardware mailbox and interrupt, and without it, everything falls apart. No devlink, no switchdev notifiers, no netdev, nothing. I'm going to return WorthFuzzing=false. I'll write the reasoning down clearly: "The patch adds switchdev support for the Marvell OcteonTX2 hardware driver. The new code paths in the NIC driver are initialized via devlink, which requires the driver to probe successfully. However, the driver probe relies on mailbox communication with the Admin Function (AF) driver, which depends on hardware-specific interrupts and shared memory semantics. In a virtualized environment without the actual Marvell hardware (or a dedicated software emulator), the mailbox communication times out, causing the probe to fail. Consequently, the devlink device is never registered, and the switch code remains unreachable. Similarly, the switch code in the AF driver is triggered by mailbox messages processed via hardware interrupts, which are also unreachable. Therefore, this patch cannot be fuzzed in virtualized environments like GCE or QEMU." |
| 88/2 | 2026/07/02 11:22 | tool | set-results |
0mArgs:{
"EnableConfigs": [],
"FocusSymbols": [],
"Reasoning": "The patch adds switchdev support for the Marvell OcteonTX2 hardware driver. The new code paths in the NIC driver are initialized via devlink, which requires the driver to probe successfully. However, the driver probe relies on mailbox communication with the Admin Function (AF) driver, which depends on hardware-specific interrupts and shared memory semantics. In a virtualized environment without the actual Marvell hardware (or a dedicated software emulator), the mailbox communication times out, causing the probe to fail. Consequently, the devlink device is never registered, and the switch code remains unreachable. Similarly, the switch code in the AF driver is triggered by mailbox messages processed via hardware interrupts, which are also unreachable. Therefore, this patch cannot be fuzzed in virtualized environments like GCE or QEMU.",
"WorthFuzzing": false
}Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"Reasoning": "The patch adds switchdev support for the Marvell OcteonTX2 hardware driver. The new code paths in the NIC driver are initialized via devlink, which requires the driver to probe successfully. However, the driver probe relies on mailbox communication with the Admin Function (AF) driver, which depends on hardware-specific interrupts and shared memory semantics. In a virtualized environment without the actual Marvell hardware (or a dedicated software emulator), the mailbox communication times out, causing the probe to fail. Consequently, the devlink device is never registered, and the switch code remains unreachable. Similarly, the switch code in the AF driver is triggered by mailbox messages processed via hardware interrupts, which are also unreachable. Therefore, this patch cannot be fuzzed in virtualized environments like GCE or QEMU.",
"WorthFuzzing": false
} |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|