| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/08/14 02:03 | flow | patch-triage |
1mArgs:null Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"KMSANReasoning": "",
"NeedsKMSAN": false,
"Reasoning": "The patch only modifies the `bnge` driver (Broadcom ThorUltra Ethernet), which is a vendor-specific PCIe driver for physical hardware not emulated in standard QEMU. Therefore, the modified code paths are unreachable in virtualized fuzzing environments.",
"WorthFuzzing": false
} |
| 1/1 | 2026/08/14 02:03 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit 79b03b7bc098c397fe73ab4f82b23e18093f040a\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Fri Aug 14 02:03:40 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/drivers/net/ethernet/broadcom/bnge/Makefile b/drivers/net/ethernet/broadcom/bnge/Makefile\nindex 8e07cb307d214..d0a3cb24dc1d3 100644\n--- a/drivers/net/ethernet/broadcom/bnge/Makefile\n+++ b/drivers/net/ethernet/broadcom/bnge/Makefile\n@@ -12,4 +12,6 @@ bng_en-y := bnge_core.o \\\n \t bnge_ethtool.o \\\n \t bnge_auxr.o \\\n \t bnge_txrx.o \\\n-\t bnge_link.o\n+\t bnge_link.o \\\n+\t bnge_vnic.o \\\n+\t bnge_filter.o\ndiff --git a/drivers/net/ethernet/broadcom/bnge/bnge.h b/drivers/net/ethernet/broadcom/bnge/bnge.h\nindex 4479ccd071f53..5a1a9186c2cbf 100644\n--- a/drivers/net/ethernet/broadcom/bnge/bnge.h\n+++ b/drivers/net/ethernet/broadcom/bnge/bnge.h\n@@ -74,6 +74,7 @@ enum {\n \tBNGE_EN_STRIP_VLAN\t\t\t\t= BIT_ULL(2),\n \tBNGE_EN_SHARED_CHNL\t\t\t\t= BIT_ULL(3),\n \tBNGE_EN_UDP_GSO_SUPP\t\t\t\t= BIT_ULL(4),\n+\tBNGE_EN_ARFS_CAP\t\t\t\t= BIT_ULL(5),\n };\n \n #define BNGE_EN_ROCE\t\t(BNGE_EN_ROCE_V1 | BNGE_EN_ROCE_V2)\n@@ -87,6 +88,7 @@ enum {\n \tBNGE_RSS_CAP_AH_V6_RSS_CAP\t\t\t= BIT(5),\n \tBNGE_RSS_CAP_ESP_V4_RSS_CAP\t\t\t= BIT(6),\n \tBNGE_RSS_CAP_ESP_V6_RSS_CAP\t\t\t= BIT(7),\n+\tBNGE_RSS_CAP_IPV6_FLOW_LABEL_RSS_CAP\t\t= BIT(8),\n };\n \n #define BNGE_MAX_QUEUE\t\t8\n@@ -160,16 +162,8 @@ struct bnge_dev {\n \tu16\t\t\ttso_max_segs;\n \n \tint\t\t\tmax_fltr;\n-#define BNGE_L2_FLTR_MAX_FLTR\t1024\n \n \tu32\t\t\t*rss_indir_tbl;\n-#define BNGE_RSS_TABLE_ENTRIES\t64\n-#define BNGE_RSS_TABLE_SIZE\t\t(BNGE_RSS_TABLE_ENTRIES * 4)\n-#define BNGE_RSS_TABLE_MAX_TBL\t8\n-#define BNGE_MAX_RSS_TABLE_SIZE\t\t\t\t\\\n-\t(BNGE_RSS_TABLE_SIZE * BNGE_RSS_TABLE_MAX_TBL)\n-#define BNGE_MAX_RSS_TABLE_ENTRIES\t\t\t\t\\\n-\t(BNGE_RSS_TABLE_ENTRIES * BNGE_RSS_TABLE_MAX_TBL)\n \tu16\t\t\trss_indir_tbl_entries;\n \n \tu32\t\t\trss_cap;\n@@ -225,6 +219,11 @@ static inline bool bnge_is_roce_en(struct bnge_dev *bd)\n \treturn bd-\u003eflags \u0026 BNGE_EN_ROCE;\n }\n \n+static inline bool bnge_is_arfs_cap(struct bnge_dev *bd)\n+{\n+\treturn bd-\u003eflags \u0026 BNGE_EN_ARFS_CAP;\n+}\n+\n static inline bool bnge_is_agg_reqd(struct bnge_dev *bd)\n {\n \tif (bd-\u003enetdev) {\ndiff --git a/drivers/net/ethernet/broadcom/bnge/bnge_core.c b/drivers/net/ethernet/broadcom/bnge/bnge_core.c\nindex 68b74eb2c3a29..90e61a53a77d5 100644\n--- a/drivers/net/ethernet/broadcom/bnge/bnge_core.c\n+++ b/drivers/net/ethernet/broadcom/bnge/bnge_core.c\n@@ -93,6 +93,13 @@ static int bnge_func_qrcaps_qcfg(struct bnge_dev *bd)\n \t\treturn rc;\n \t}\n \n+\trc = bnge_hwrm_cfa_adv_flow_mgnt_qcaps(bd);\n+\tif (rc) {\n+\t\tdev_warn(bd-\u003edev, \"hwrm query adv flow mgnt failure rc: %d\\n\",\n+\t\t\t rc);\n+\t\treturn rc;\n+\t}\n+\n \trc = bnge_hwrm_vnic_qcaps(bd);\n \tif (rc) {\n \t\tdev_err(bd-\u003edev, \"vnic caps failure rc: %d\\n\", rc);\ndiff --git a/drivers/net/ethernet/broadcom/bnge/bnge_ethtool.c b/drivers/net/ethernet/broadcom/bnge/bnge_ethtool.c\nindex 2467e44de291a..8252609def1ec 100644\n--- a/drivers/net/ethernet/broadcom/bnge/bnge_ethtool.c\n+++ b/drivers/net/ethernet/broadcom/bnge/bnge_ethtool.c\n@@ -10,8 +10,12 @@\n #include \u003clinux/ethtool_netlink.h\u003e\n \n #include \"bnge.h\"\n+#include \"bnge_netdev.h\"\n+#include \"bnge_vnic.h\"\n+#include \"bnge_resc.h\"\n #include \"bnge_ethtool.h\"\n #include \"bnge_hwrm_lib.h\"\n+#include \"bnge_filter.h\"\n \n static int bnge_nway_reset(struct net_device *dev)\n {\n@@ -740,6 +744,984 @@ static int bnge_set_pauseparam(struct net_device *dev,\n \treturn rc;\n }\n \n+static u64 bnge_get_ethtool_ipv4_rss(struct bnge_dev *bd)\n+{\n+\tif (bd-\u003erss_hash_cfg \u0026 VNIC_RSS_CFG_REQ_HASH_TYPE_IPV4)\n+\t\treturn RXH_IP_SRC | RXH_IP_DST;\n+\treturn 0;\n+}\n+\n+static u64 bnge_get_ethtool_ipv6_rss(struct bnge_dev *bd)\n+{\n+\tif (bd-\u003erss_hash_cfg \u0026 VNIC_RSS_CFG_REQ_HASH_TYPE_IPV6)\n+\t\treturn RXH_IP_SRC | RXH_IP_DST;\n+\tif (bd-\u003erss_hash_cfg \u0026 VNIC_RSS_CFG_REQ_HASH_TYPE_IPV6_FLOW_LABEL)\n+\t\treturn RXH_IP_SRC | RXH_IP_DST | RXH_IP6_FL;\n+\treturn 0;\n+}\n+\n+static int bnge_get_rxfh_fields(struct net_device *dev,\n+\t\t\t\tstruct ethtool_rxfh_fields *cmd)\n+{\n+\tstruct bnge_net *bn = netdev_priv(dev);\n+\tstruct bnge_dev *bd = bn-\u003ebd;\n+\n+\tcmd-\u003edata = 0;\n+\tswitch (cmd-\u003eflow_type) {\n+\tcase TCP_V4_FLOW:\n+\t\tif (bd-\u003erss_hash_cfg \u0026 VNIC_RSS_CFG_REQ_HASH_TYPE_TCP_IPV4)\n+\t\t\tcmd-\u003edata |= RXH_IP_SRC | RXH_IP_DST |\n+\t\t\t\t RXH_L4_B_0_1 | RXH_L4_B_2_3;\n+\t\tcmd-\u003edata |= bnge_get_ethtool_ipv4_rss(bd);\n+\t\tbreak;\n+\tcase UDP_V4_FLOW:\n+\t\tif (bd-\u003erss_hash_cfg \u0026 VNIC_RSS_CFG_REQ_HASH_TYPE_UDP_IPV4)\n+\t\t\tcmd-\u003edata |= RXH_IP_SRC | RXH_IP_DST |\n+\t\t\t\t RXH_L4_B_0_1 | RXH_L4_B_2_3;\n+\t\tfallthrough;\n+\tcase AH_ESP_V4_FLOW:\n+\t\tif (bd-\u003erss_hash_cfg \u0026\n+\t\t (VNIC_RSS_CFG_REQ_HASH_TYPE_AH_SPI_IPV4 |\n+\t\t VNIC_RSS_CFG_REQ_HASH_TYPE_ESP_SPI_IPV4))\n+\t\t\tcmd-\u003edata |= RXH_IP_SRC | RXH_IP_DST |\n+\t\t\t\t RXH_L4_B_0_1 | RXH_L4_B_2_3;\n+\t\tfallthrough;\n+\tcase SCTP_V4_FLOW:\n+\tcase AH_V4_FLOW:\n+\tcase ESP_V4_FLOW:\n+\tcase IPV4_FLOW:\n+\t\tcmd-\u003edata |= bnge_get_ethtool_ipv4_rss(bd);\n+\t\tbreak;\n+\tcase TCP_V6_FLOW:\n+\t\tif (bd-\u003erss_hash_cfg \u0026 VNIC_RSS_CFG_REQ_HASH_TYPE_TCP_IPV6)\n+\t\t\tcmd-\u003edata |= RXH_IP_SRC | RXH_IP_DST |\n+\t\t\t\t RXH_L4_B_0_1 | RXH_L4_B_2_3;\n+\t\tcmd-\u003edata |= bnge_get_ethtool_ipv6_rss(bd);\n+\t\tbreak;\n+\tcase UDP_V6_FLOW:\n+\t\tif (bd-\u003erss_hash_cfg \u0026 VNIC_RSS_CFG_REQ_HASH_TYPE_UDP_IPV6)\n+\t\t\tcmd-\u003edata |= RXH_IP_SRC | RXH_IP_DST |\n+\t\t\t\t RXH_L4_B_0_1 | RXH_L4_B_2_3;\n+\t\tfallthrough;\n+\tcase AH_ESP_V6_FLOW:\n+\t\tif (bd-\u003erss_hash_cfg \u0026\n+\t\t (VNIC_RSS_CFG_REQ_HASH_TYPE_AH_SPI_IPV6 |\n+\t\t VNIC_RSS_CFG_REQ_HASH_TYPE_ESP_SPI_IPV6))\n+\t\t\tcmd-\u003edata |= RXH_IP_SRC | RXH_IP_DST |\n+\t\t\t\t RXH_L4_B_0_1 | RXH_L4_B_2_3;\n+\t\tfallthrough;\n+\tcase SCTP_V6_FLOW:\n+\tcase AH_V6_FLOW:\n+\tcase ESP_V6_FLOW:\n+\tcase IPV6_FLOW:\n+\t\tcmd-\u003edata |= bnge_get_ethtool_ipv6_rss(bd);\n+\t\tbreak;\n+\t}\n+\treturn 0;\n+}\n+\n+#define RXH_4TUPLE (RXH_IP_SRC | RXH_IP_DST | RXH_L4_B_0_1 | RXH_L4_B_2_3)\n+#define RXH_2TUPLE (RXH_IP_SRC | RXH_IP_DST)\n+\n+static int bnge_set_rxfh_fields(struct net_device *dev,\n+\t\t\t\tconst struct ethtool_rxfh_fields *cmd,\n+\t\t\t\tstruct netlink_ext_ack *extack)\n+{\n+\tstruct bnge_net *bn = netdev_priv(dev);\n+\tstruct bnge_dev *bd = bn-\u003ebd;\n+\tint tuple, rc = 0;\n+\tu32 rss_hash_cfg;\n+\n+\trss_hash_cfg = bd-\u003erss_hash_cfg;\n+\n+\tif (cmd-\u003edata == RXH_4TUPLE)\n+\t\ttuple = 4;\n+\telse if (cmd-\u003edata == RXH_2TUPLE ||\n+\t\t cmd-\u003edata == (RXH_2TUPLE | RXH_IP6_FL))\n+\t\ttuple = 2;\n+\telse if (!cmd-\u003edata)\n+\t\ttuple = 0;\n+\telse\n+\t\treturn -EINVAL;\n+\n+\tif (cmd-\u003edata \u0026 RXH_IP6_FL \u0026\u0026\n+\t !(bd-\u003erss_cap \u0026 BNGE_RSS_CAP_IPV6_FLOW_LABEL_RSS_CAP))\n+\t\treturn -EINVAL;\n+\n+\tif (cmd-\u003eflow_type == TCP_V4_FLOW) {\n+\t\trss_hash_cfg \u0026= ~VNIC_RSS_CFG_REQ_HASH_TYPE_TCP_IPV4;\n+\t\tif (tuple == 4)\n+\t\t\trss_hash_cfg |= VNIC_RSS_CFG_REQ_HASH_TYPE_TCP_IPV4;\n+\t} else if (cmd-\u003eflow_type == UDP_V4_FLOW) {\n+\t\trss_hash_cfg \u0026= ~VNIC_RSS_CFG_REQ_HASH_TYPE_UDP_IPV4;\n+\t\tif (tuple == 4)\n+\t\t\trss_hash_cfg |= VNIC_RSS_CFG_REQ_HASH_TYPE_UDP_IPV4;\n+\t} else if (cmd-\u003eflow_type == TCP_V6_FLOW) {\n+\t\trss_hash_cfg \u0026= ~VNIC_RSS_CFG_REQ_HASH_TYPE_TCP_IPV6;\n+\t\tif (tuple == 4)\n+\t\t\trss_hash_cfg |= VNIC_RSS_CFG_REQ_HASH_TYPE_TCP_IPV6;\n+\t} else if (cmd-\u003eflow_type == UDP_V6_FLOW) {\n+\t\trss_hash_cfg \u0026= ~VNIC_RSS_CFG_REQ_HASH_TYPE_UDP_IPV6;\n+\t\tif (tuple == 4)\n+\t\t\trss_hash_cfg |= VNIC_RSS_CFG_REQ_HASH_TYPE_UDP_IPV6;\n+\t} else if (cmd-\u003eflow_type == AH_ESP_V4_FLOW) {\n+\t\tif (tuple == 4 \u0026\u0026\n+\t\t (!(bd-\u003erss_cap \u0026 BNGE_RSS_CAP_AH_V4_RSS_CAP) ||\n+\t\t !(bd-\u003erss_cap \u0026 BNGE_RSS_CAP_ESP_V4_RSS_CAP)))\n+\t\t\treturn -EINVAL;\n+\t\trss_hash_cfg \u0026= ~(VNIC_RSS_CFG_REQ_HASH_TYPE_AH_SPI_IPV4 |\n+\t\t\t\t VNIC_RSS_CFG_REQ_HASH_TYPE_ESP_SPI_IPV4);\n+\t\tif (tuple == 4)\n+\t\t\trss_hash_cfg |= VNIC_RSS_CFG_REQ_HASH_TYPE_AH_SPI_IPV4 |\n+\t\t\t\t\tVNIC_RSS_CFG_REQ_HASH_TYPE_ESP_SPI_IPV4;\n+\t} else if (cmd-\u003eflow_type == AH_ESP_V6_FLOW) {\n+\t\tif (tuple == 4 \u0026\u0026\n+\t\t (!(bd-\u003erss_cap \u0026 BNGE_RSS_CAP_AH_V6_RSS_CAP) ||\n+\t\t !(bd-\u003erss_cap \u0026 BNGE_RSS_CAP_ESP_V6_RSS_CAP)))\n+\t\t\treturn -EINVAL;\n+\t\trss_hash_cfg \u0026= ~(VNIC_RSS_CFG_REQ_HASH_TYPE_AH_SPI_IPV6 |\n+\t\t\t\t VNIC_RSS_CFG_REQ_HASH_TYPE_ESP_SPI_IPV6);\n+\t\tif (tuple == 4)\n+\t\t\trss_hash_cfg |= VNIC_RSS_CFG_REQ_HASH_TYPE_AH_SPI_IPV6 |\n+\t\t\t\t\tVNIC_RSS_CFG_REQ_HASH_TYPE_ESP_SPI_IPV6;\n+\t} else if (tuple == 4) {\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tswitch (cmd-\u003eflow_type) {\n+\tcase TCP_V4_FLOW:\n+\tcase UDP_V4_FLOW:\n+\tcase SCTP_V4_FLOW:\n+\tcase AH_ESP_V4_FLOW:\n+\tcase AH_V4_FLOW:\n+\tcase ESP_V4_FLOW:\n+\tcase IPV4_FLOW:\n+\t\tif (tuple == 2)\n+\t\t\trss_hash_cfg |= VNIC_RSS_CFG_REQ_HASH_TYPE_IPV4;\n+\t\telse if (!tuple)\n+\t\t\trss_hash_cfg \u0026= ~VNIC_RSS_CFG_REQ_HASH_TYPE_IPV4;\n+\t\tbreak;\n+\n+\tcase TCP_V6_FLOW:\n+\tcase UDP_V6_FLOW:\n+\tcase SCTP_V6_FLOW:\n+\tcase AH_ESP_V6_FLOW:\n+\tcase AH_V6_FLOW:\n+\tcase ESP_V6_FLOW:\n+\tcase IPV6_FLOW:\n+\t\trss_hash_cfg \u0026= ~(VNIC_RSS_CFG_REQ_HASH_TYPE_IPV6 |\n+\t\t\t\t VNIC_RSS_CFG_REQ_HASH_TYPE_IPV6_FLOW_LABEL);\n+\t\tif (!tuple)\n+\t\t\tbreak;\n+\t\tif (cmd-\u003edata \u0026 RXH_IP6_FL)\n+\t\t\trss_hash_cfg |=\n+\t\t\t\tVNIC_RSS_CFG_REQ_HASH_TYPE_IPV6_FLOW_LABEL;\n+\t\telse if (tuple == 2)\n+\t\t\trss_hash_cfg |= VNIC_RSS_CFG_REQ_HASH_TYPE_IPV6;\n+\t\tbreak;\n+\t}\n+\n+\tif (bd-\u003erss_hash_cfg == rss_hash_cfg)\n+\t\treturn 0;\n+\n+\tbd-\u003erss_hash_cfg = rss_hash_cfg;\n+\tif (netif_running(bn-\u003enetdev)) {\n+\t\tbnge_close_core(bn);\n+\t\trc = bnge_open_core(bn);\n+\t}\n+\treturn rc;\n+}\n+\n+static u32 bnge_get_rxfh_indir_size_eth(struct net_device *dev)\n+{\n+\tstruct bnge_net *bn = netdev_priv(dev);\n+\tstruct bnge_dev *bd = bn-\u003ebd;\n+\n+\treturn bnge_get_rxfh_indir_size(bd);\n+}\n+\n+static u32 bnge_get_rxfh_key_size(struct net_device *dev)\n+{\n+\treturn HW_HASH_KEY_SIZE;\n+}\n+\n+static int bnge_get_rxfh(struct net_device *dev,\n+\t\t\t struct ethtool_rxfh_param *rxfh)\n+{\n+\tstruct bnge_net *bn = netdev_priv(dev);\n+\tstruct bnge_rss_ctx *rss_ctx = NULL;\n+\tstruct bnge_dev *bd = bn-\u003ebd;\n+\tstruct bnge_vnic_info *vnic;\n+\tu32 i, tbl_size;\n+\tu32 *indir_tbl;\n+\n+\tindir_tbl = bd-\u003erss_indir_tbl;\n+\trxfh-\u003ehfunc = ETH_RSS_HASH_TOP;\n+\n+\tif (!bn-\u003evnic_info)\n+\t\treturn 0;\n+\n+\tvnic = \u0026bn-\u003evnic_info[BNGE_VNIC_DEFAULT];\n+\tif (rxfh-\u003erss_context) {\n+\t\tstruct ethtool_rxfh_context *ctx;\n+\n+\t\tctx = xa_load(\u0026bn-\u003enetdev-\u003eethtool-\u003erss_ctx,\n+\t\t\t rxfh-\u003erss_context);\n+\t\tif (!ctx)\n+\t\t\treturn -EINVAL;\n+\t\tindir_tbl = ethtool_rxfh_context_indir(ctx);\n+\t\trss_ctx = ethtool_rxfh_context_priv(ctx);\n+\t\tvnic = \u0026rss_ctx-\u003evnic;\n+\t}\n+\n+\tif (rxfh-\u003eindir \u0026\u0026 indir_tbl) {\n+\t\ttbl_size = bnge_get_rxfh_indir_size(bd);\n+\t\tfor (i = 0; i \u003c tbl_size; i++)\n+\t\t\trxfh-\u003eindir[i] = indir_tbl[i];\n+\t}\n+\n+\tif (rxfh-\u003ekey \u0026\u0026 vnic-\u003erss_hash_key)\n+\t\tmemcpy(rxfh-\u003ekey, vnic-\u003erss_hash_key, HW_HASH_KEY_SIZE);\n+\n+\treturn 0;\n+}\n+\n+static int bnge_set_rxfh(struct net_device *dev,\n+\t\t\t struct ethtool_rxfh_param *rxfh,\n+\t\t\t struct netlink_ext_ack *extack)\n+{\n+\tstruct bnge_net *bn = netdev_priv(dev);\n+\tint rc = 0;\n+\n+\tif (rxfh-\u003ehfunc \u0026\u0026 rxfh-\u003ehfunc != ETH_RSS_HASH_TOP)\n+\t\treturn -EOPNOTSUPP;\n+\n+\tbnge_modify_rss(bn, NULL, NULL, rxfh);\n+\n+\tif (netif_running(bn-\u003enetdev)) {\n+\t\tbnge_close_core(bn);\n+\t\trc = bnge_open_core(bn);\n+\t}\n+\treturn rc;\n+}\n+\n+static u32 bnge_get_rx_ring_count(struct net_device *dev)\n+{\n+\tstruct bnge_net *bn = netdev_priv(dev);\n+\tstruct bnge_dev *bd = bn-\u003ebd;\n+\n+\treturn bd-\u003erx_nr_rings;\n+}\n+\n+static int bnge_rxfh_context_check(struct bnge_net *bn,\n+\t\t\t\t const struct ethtool_rxfh_param *rxfh,\n+\t\t\t\t struct netlink_ext_ack *extack)\n+{\n+\tif (rxfh-\u003ehfunc \u0026\u0026 rxfh-\u003ehfunc != ETH_RSS_HASH_TOP) {\n+\t\tNL_SET_ERR_MSG_MOD(extack, \"RSS hash function not supported\");\n+\t\treturn -EOPNOTSUPP;\n+\t}\n+\n+\tif (!netif_running(bn-\u003enetdev)) {\n+\t\tNL_SET_ERR_MSG_MOD(extack, \"Unable to set RSS contexts when interface is down\");\n+\t\treturn -EAGAIN;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int bnge_create_rxfh_context(struct net_device *dev,\n+\t\t\t\t struct ethtool_rxfh_context *ctx,\n+\t\t\t\t const struct ethtool_rxfh_param *rxfh,\n+\t\t\t\t struct netlink_ext_ack *extack)\n+{\n+\tstruct bnge_net *bn = netdev_priv(dev);\n+\tstruct bnge_rss_ctx *rss_ctx;\n+\tstruct bnge_vnic_info *vnic;\n+\tint rc;\n+\n+\trc = bnge_rxfh_context_check(bn, rxfh, extack);\n+\tif (rc)\n+\t\treturn rc;\n+\n+\tif (bn-\u003enum_rss_ctx \u003e= BNGE_MAX_ETH_RSS_CTX) {\n+\t\tNL_SET_ERR_MSG_FMT_MOD(extack, \"Out of RSS contexts, maximum %u\",\n+\t\t\t\t BNGE_MAX_ETH_RSS_CTX);\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tif (!bnge_arfs_capable(bn-\u003ebd, true)) {\n+\t\tNL_SET_ERR_MSG_MOD(extack, \"Out of hardware resources\");\n+\t\treturn -ENOMEM;\n+\t}\n+\n+\trss_ctx = ethtool_rxfh_context_priv(ctx);\n+\n+\tbn-\u003enum_rss_ctx++;\n+\n+\tvnic = \u0026rss_ctx-\u003evnic;\n+\n+\tbnge_init_vnic_mem(vnic);\n+\n+\tvnic-\u003erss_ctx = ctx;\n+\tvnic-\u003eflags |= BNGE_VNIC_RSSCTX_FLAG;\n+\trc = bnge_alloc_vnic_rss_table(bn, vnic);\n+\tif (rc)\n+\t\tgoto err_del_rss_ctx;\n+\n+\t/* Populate defaults in the context */\n+\tbnge_set_dflt_rss_indir_tbl(bn-\u003ebd, ctx);\n+\tctx-\u003ehfunc = ETH_RSS_HASH_TOP;\n+\tmemcpy(vnic-\u003erss_hash_key, bn-\u003erss_hash_key, HW_HASH_KEY_SIZE);\n+\tmemcpy(ethtool_rxfh_context_key(ctx),\n+\t bn-\u003erss_hash_key, HW_HASH_KEY_SIZE);\n+\n+\trc = bnge_hwrm_vnic_alloc(bn-\u003ebd, vnic, bn-\u003ebd-\u003erx_nr_rings);\n+\tif (rc) {\n+\t\tNL_SET_ERR_MSG_MOD(extack, \"Unable to allocate VNIC\");\n+\t\tgoto err_del_rss_ctx;\n+\t}\n+\n+\trc = bnge_hwrm_vnic_set_tpa(bn-\u003ebd, vnic,\n+\t\t\t\t bn-\u003epriv_flags \u0026 BNGE_NET_EN_TPA);\n+\tif (rc) {\n+\t\tNL_SET_ERR_MSG_MOD(extack,\n+\t\t\t\t \"Unable to set TPA settings to vnic\");\n+\t\tgoto err_del_rss_ctx;\n+\t}\n+\tbnge_modify_rss(bn, ctx, rss_ctx, rxfh);\n+\n+\trc = bnge_setup_vnic(bn, vnic);\n+\tif (rc) {\n+\t\tNL_SET_ERR_MSG_MOD(extack, \"Unable to setup vnic\");\n+\t\tgoto err_del_rss_ctx;\n+\t}\n+\n+\trss_ctx-\u003eindex = rxfh-\u003erss_context;\n+\treturn 0;\n+\n+err_del_rss_ctx:\n+\tbnge_del_one_rss_ctx(bn, rss_ctx, true);\n+\treturn rc;\n+}\n+\n+static int bnge_modify_rxfh_context(struct net_device *dev,\n+\t\t\t\t struct ethtool_rxfh_context *ctx,\n+\t\t\t\t const struct ethtool_rxfh_param *rxfh,\n+\t\t\t\t struct netlink_ext_ack *extack)\n+{\n+\tstruct bnge_net *bn = netdev_priv(dev);\n+\tstruct bnge_rss_ctx *rss_ctx;\n+\tint rc;\n+\n+\trc = bnge_rxfh_context_check(bn, rxfh, extack);\n+\tif (rc)\n+\t\treturn rc;\n+\n+\trss_ctx = ethtool_rxfh_context_priv(ctx);\n+\n+\tbnge_modify_rss(bn, ctx, rss_ctx, rxfh);\n+\n+\treturn bnge_hwrm_vnic_rss_cfg(bn, \u0026rss_ctx-\u003evnic);\n+}\n+\n+static int bnge_remove_rxfh_context(struct net_device *dev,\n+\t\t\t\t struct ethtool_rxfh_context *ctx,\n+\t\t\t\t u32 rss_context,\n+\t\t\t\t struct netlink_ext_ack *extack)\n+{\n+\tstruct bnge_net *bn = netdev_priv(dev);\n+\tstruct bnge_rss_ctx *rss_ctx;\n+\n+\trss_ctx = ethtool_rxfh_context_priv(ctx);\n+\n+\tbnge_del_one_rss_ctx(bn, rss_ctx, true);\n+\treturn 0;\n+}\n+\n+#define BNGE_IP_PROTO_FULL_MASK\t0xFF\n+#define BNGE_IP_PROTO_WILDCARD\t0x0\n+\n+static u32 bnge_get_all_fltr_ids_rcu(struct bnge_net *bn,\n+\t\t\t\t struct hlist_head tbl[],\n+\t\t\t\t u32 tbl_size, u32 *ids, u32 start,\n+\t\t\t\t u32 id_cnt, u32 offset)\n+{\n+\tu32 i, j = start;\n+\n+\tif (j \u003e= id_cnt)\n+\t\treturn j;\n+\n+\tfor (i = 0; i \u003c tbl_size; i++) {\n+\t\tstruct bnge_filter_base *fltr;\n+\t\tstruct hlist_head *head;\n+\n+\t\thead = \u0026tbl[i];\n+\t\thlist_for_each_entry_rcu(fltr, head, hlist) {\n+\t\t\tif (!fltr-\u003eflags ||\n+\t\t\t test_bit(BNGE_FLTR_FW_DELETED, \u0026fltr-\u003estate))\n+\t\t\t\tcontinue;\n+\t\t\tids[j++] = fltr-\u003esw_id + offset;\n+\t\t\tif (j == id_cnt)\n+\t\t\t\treturn j;\n+\t\t}\n+\t}\n+\treturn j;\n+}\n+\n+static struct bnge_filter_base *bnge_get_one_fltr_rcu(struct bnge_net *bn,\n+\t\t\t\t\t\t struct hlist_head tbl[],\n+\t\t\t\t\t\t u32 tbl_size, u32 id,\n+\t\t\t\t\t\t u32 offset)\n+{\n+\tu32 i;\n+\n+\tfor (i = 0; i \u003c tbl_size; i++) {\n+\t\tstruct bnge_filter_base *fltr;\n+\t\tstruct hlist_head *head;\n+\n+\t\thead = \u0026tbl[i];\n+\t\thlist_for_each_entry_rcu(fltr, head, hlist) {\n+\t\t\tif (fltr-\u003eflags \u0026\u0026 fltr-\u003esw_id + offset == id)\n+\t\t\t\treturn fltr;\n+\t\t}\n+\t}\n+\treturn NULL;\n+}\n+\n+static int bnge_grxclsrlall(struct bnge_net *bn, struct ethtool_rxnfc *cmd,\n+\t\t\t u32 *rule_locs)\n+{\n+\tu32 count;\n+\n+\tcmd-\u003edata = bn-\u003euser_fltr_count;\n+\trcu_read_lock();\n+\tcount = bnge_get_all_fltr_ids_rcu(bn, bn-\u003el2_fltr_hash_tbl,\n+\t\t\t\t\t BNGE_L2_FLTR_HASH_SIZE, rule_locs, 0,\n+\t\t\t\t\t cmd-\u003erule_cnt, 0);\n+\tcmd-\u003erule_cnt = bnge_get_all_fltr_ids_rcu(bn, bn-\u003entp_fltr_hash_tbl,\n+\t\t\t\t\t\t BNGE_NTP_FLTR_HASH_SIZE,\n+\t\t\t\t\t\t rule_locs, count,\n+\t\t\t\t\t\t cmd-\u003erule_cnt,\n+\t\t\t\t\t\t BNGE_MAX_L2_FLTRS);\n+\trcu_read_unlock();\n+\n+\treturn 0;\n+}\n+\n+static int bnge_grxclsrule(struct bnge_net *bn, struct ethtool_rxnfc *cmd)\n+{\n+\tstruct ethtool_rx_flow_spec *fs =\n+\t\t(struct ethtool_rx_flow_spec *)\u0026cmd-\u003efs;\n+\tstruct bnge_filter_base *fltr_base;\n+\tstruct bnge_ntuple_filter *fltr;\n+\tstruct bnge_flow_masks *fmasks;\n+\tstruct bnge_dev *bd = bn-\u003ebd;\n+\tstruct flow_keys *fkeys;\n+\tint rc = -EINVAL;\n+\n+\tif (fs-\u003elocation \u003e= BNGE_MAX_L2_FLTRS + bd-\u003emax_fltr)\n+\t\treturn rc;\n+\n+\trcu_read_lock();\n+\tfltr_base = bnge_get_one_fltr_rcu(bn, bn-\u003el2_fltr_hash_tbl,\n+\t\t\t\t\t BNGE_L2_FLTR_HASH_SIZE,\n+\t\t\t\t\t fs-\u003elocation, 0);\n+\tif (fltr_base) {\n+\t\tstruct ethhdr *h_ether = \u0026fs-\u003eh_u.ether_spec;\n+\t\tstruct ethhdr *m_ether = \u0026fs-\u003em_u.ether_spec;\n+\t\tstruct bnge_l2_filter *l2_fltr;\n+\t\tstruct bnge_l2_key *l2_key;\n+\n+\t\tl2_fltr = container_of(fltr_base, struct bnge_l2_filter, base);\n+\t\tl2_key = \u0026l2_fltr-\u003el2_key;\n+\t\tfs-\u003eflow_type = ETHER_FLOW;\n+\t\tether_addr_copy(h_ether-\u003eh_dest, l2_key-\u003edst_mac_addr);\n+\t\teth_broadcast_addr(m_ether-\u003eh_dest);\n+\t\tif (l2_key-\u003evlan) {\n+\t\t\tstruct ethtool_flow_ext *m_ext = \u0026fs-\u003em_ext;\n+\t\t\tstruct ethtool_flow_ext *h_ext = \u0026fs-\u003eh_ext;\n+\n+\t\t\tfs-\u003eflow_type |= FLOW_EXT;\n+\t\t\tm_ext-\u003evlan_tci = htons(0xfff);\n+\t\t\th_ext-\u003evlan_tci = htons(l2_key-\u003evlan);\n+\t\t}\n+\t\tif (fltr_base-\u003eflags \u0026 BNGE_ACT_RING_DST)\n+\t\t\tfs-\u003ering_cookie = fltr_base-\u003erxq;\n+\t\trcu_read_unlock();\n+\t\treturn 0;\n+\t}\n+\tfltr_base = bnge_get_one_fltr_rcu(bn, bn-\u003entp_fltr_hash_tbl,\n+\t\t\t\t\t BNGE_NTP_FLTR_HASH_SIZE,\n+\t\t\t\t\t fs-\u003elocation, BNGE_MAX_L2_FLTRS);\n+\tif (!fltr_base) {\n+\t\trcu_read_unlock();\n+\t\treturn rc;\n+\t}\n+\tfltr = container_of(fltr_base, struct bnge_ntuple_filter, base);\n+\n+\tfkeys = \u0026fltr-\u003efkeys;\n+\tfmasks = \u0026fltr-\u003efmasks;\n+\tif (fkeys-\u003ebasic.n_proto == htons(ETH_P_IP)) {\n+\t\tif (fkeys-\u003ebasic.ip_proto == BNGE_IP_PROTO_WILDCARD) {\n+\t\t\tfs-\u003eflow_type = IP_USER_FLOW;\n+\t\t\tfs-\u003eh_u.usr_ip4_spec.ip_ver = ETH_RX_NFC_IP4;\n+\t\t\tfs-\u003eh_u.usr_ip4_spec.proto = BNGE_IP_PROTO_WILDCARD;\n+\t\t\tfs-\u003em_u.usr_ip4_spec.proto = 0;\n+\t\t} else if (fkeys-\u003ebasic.ip_proto == IPPROTO_ICMP) {\n+\t\t\tfs-\u003eflow_type = IP_USER_FLOW;\n+\t\t\tfs-\u003eh_u.usr_ip4_spec.ip_ver = ETH_RX_NFC_IP4;\n+\t\t\tfs-\u003eh_u.usr_ip4_spec.proto = IPPROTO_ICMP;\n+\t\t\tfs-\u003em_u.usr_ip4_spec.proto = BNGE_IP_PROTO_FULL_MASK;\n+\t\t} else if (fkeys-\u003ebasic.ip_proto == IPPROTO_TCP) {\n+\t\t\tfs-\u003eflow_type = TCP_V4_FLOW;\n+\t\t} else if (fkeys-\u003ebasic.ip_proto == IPPROTO_UDP) {\n+\t\t\tfs-\u003eflow_type = UDP_V4_FLOW;\n+\t\t} else {\n+\t\t\tgoto fltr_err;\n+\t\t}\n+\n+\t\tfs-\u003eh_u.tcp_ip4_spec.ip4src = fkeys-\u003eaddrs.v4addrs.src;\n+\t\tfs-\u003em_u.tcp_ip4_spec.ip4src = fmasks-\u003eaddrs.v4addrs.src;\n+\t\tfs-\u003eh_u.tcp_ip4_spec.ip4dst = fkeys-\u003eaddrs.v4addrs.dst;\n+\t\tfs-\u003em_u.tcp_ip4_spec.ip4dst = fmasks-\u003eaddrs.v4addrs.dst;\n+\t\tif (fs-\u003eflow_type == TCP_V4_FLOW ||\n+\t\t fs-\u003eflow_type == UDP_V4_FLOW) {\n+\t\t\tfs-\u003eh_u.tcp_ip4_spec.psrc = fkeys-\u003eports.src;\n+\t\t\tfs-\u003em_u.tcp_ip4_spec.psrc = fmasks-\u003eports.src;\n+\t\t\tfs-\u003eh_u.tcp_ip4_spec.pdst = fkeys-\u003eports.dst;\n+\t\t\tfs-\u003em_u.tcp_ip4_spec.pdst = fmasks-\u003eports.dst;\n+\t\t}\n+\t} else {\n+\t\tif (fkeys-\u003ebasic.ip_proto == BNGE_IP_PROTO_WILDCARD) {\n+\t\t\tfs-\u003eflow_type = IPV6_USER_FLOW;\n+\t\t\tfs-\u003eh_u.usr_ip6_spec.l4_proto =\n+\t\t\t\tBNGE_IP_PROTO_WILDCARD;\n+\t\t\tfs-\u003em_u.usr_ip6_spec.l4_proto = 0;\n+\t\t} else if (fkeys-\u003ebasic.ip_proto == IPPROTO_ICMPV6) {\n+\t\t\tfs-\u003eflow_type = IPV6_USER_FLOW;\n+\t\t\tfs-\u003eh_u.usr_ip6_spec.l4_proto = IPPROTO_ICMPV6;\n+\t\t\tfs-\u003em_u.usr_ip6_spec.l4_proto =\n+\t\t\t\tBNGE_IP_PROTO_FULL_MASK;\n+\t\t} else if (fkeys-\u003ebasic.ip_proto == IPPROTO_TCP) {\n+\t\t\tfs-\u003eflow_type = TCP_V6_FLOW;\n+\t\t} else if (fkeys-\u003ebasic.ip_proto == IPPROTO_UDP) {\n+\t\t\tfs-\u003eflow_type = UDP_V6_FLOW;\n+\t\t} else {\n+\t\t\tgoto fltr_err;\n+\t\t}\n+\n+\t\tmemcpy(fs-\u003eh_u.tcp_ip6_spec.ip6src,\n+\t\t \u0026fkeys-\u003eaddrs.v6addrs.src,\n+\t\t sizeof(struct in6_addr));\n+\t\tmemcpy(fs-\u003em_u.tcp_ip6_spec.ip6src,\n+\t\t \u0026fmasks-\u003eaddrs.v6addrs.src,\n+\t\t sizeof(struct in6_addr));\n+\t\tmemcpy(fs-\u003eh_u.tcp_ip6_spec.ip6dst,\n+\t\t \u0026fkeys-\u003eaddrs.v6addrs.dst,\n+\t\t sizeof(struct in6_addr));\n+\t\tmemcpy(fs-\u003em_u.tcp_ip6_spec.ip6dst,\n+\t\t \u0026fmasks-\u003eaddrs.v6addrs.dst,\n+\t\t sizeof(struct in6_addr));\n+\n+\t\tif (fs-\u003eflow_type == TCP_V6_FLOW ||\n+\t\t fs-\u003eflow_type == UDP_V6_FLOW) {\n+\t\t\tfs-\u003eh_u.tcp_ip6_spec.psrc = fkeys-\u003eports.src;\n+\t\t\tfs-\u003em_u.tcp_ip6_spec.psrc = fmasks-\u003eports.src;\n+\t\t\tfs-\u003eh_u.tcp_ip6_spec.pdst = fkeys-\u003eports.dst;\n+\t\t\tfs-\u003em_u.tcp_ip6_spec.pdst = fmasks-\u003eports.dst;\n+\t\t}\n+\t}\n+\n+\tif (fltr-\u003ebase.flags \u0026 BNGE_ACT_DROP) {\n+\t\tfs-\u003ering_cookie = RX_CLS_FLOW_DISC;\n+\t} else if (fltr-\u003ebase.flags \u0026 BNGE_ACT_RSS_CTX) {\n+\t\tfs-\u003eflow_type |= FLOW_RSS;\n+\t\tcmd-\u003erss_context = fltr-\u003ebase.fw_vnic_id;\n+\t} else {\n+\t\tfs-\u003ering_cookie = fltr-\u003ebase.rxq;\n+\t}\n+\trc = 0;\n+\n+fltr_err:\n+\trcu_read_unlock();\n+\n+\treturn rc;\n+}\n+\n+static bool bnge_verify_ntuple_ip4_flow(struct ethtool_usrip4_spec *ip_spec,\n+\t\t\t\t\tstruct ethtool_usrip4_spec *ip_mask)\n+{\n+\tu8 mproto = ip_mask-\u003eproto;\n+\tu8 sproto = ip_spec-\u003eproto;\n+\n+\tif (ip_mask-\u003el4_4_bytes || ip_mask-\u003etos ||\n+\t ip_spec-\u003eip_ver != ETH_RX_NFC_IP4 ||\n+\t (mproto \u0026\u0026 (mproto != BNGE_IP_PROTO_FULL_MASK ||\n+\t\t\tsproto != IPPROTO_ICMP)))\n+\t\treturn false;\n+\treturn true;\n+}\n+\n+static bool bnge_verify_ntuple_ip6_flow(struct ethtool_usrip6_spec *ip_spec,\n+\t\t\t\t\tstruct ethtool_usrip6_spec *ip_mask)\n+{\n+\tu8 mproto = ip_mask-\u003el4_proto;\n+\tu8 sproto = ip_spec-\u003el4_proto;\n+\n+\tif (ip_mask-\u003el4_4_bytes || ip_mask-\u003etclass ||\n+\t (mproto \u0026\u0026 (mproto != BNGE_IP_PROTO_FULL_MASK ||\n+\t\t\tsproto != IPPROTO_ICMPV6)))\n+\t\treturn false;\n+\treturn true;\n+}\n+\n+static int bnge_add_ntuple_cls_rule(struct bnge_net *bn,\n+\t\t\t\t struct ethtool_rxnfc *cmd)\n+{\n+\tstruct ethtool_rx_flow_spec *fs = \u0026cmd-\u003efs;\n+\tstruct bnge_ntuple_filter *new_fltr, *fltr;\n+\tu32 flow_type = fs-\u003eflow_type \u0026 0xff;\n+\tstruct bnge_l2_filter *l2_fltr;\n+\tstruct bnge_flow_masks *fmasks;\n+\tstruct flow_keys *fkeys;\n+\tu32 idx;\n+\tint rc;\n+\n+\tif (!bn-\u003evnic_info)\n+\t\treturn -EAGAIN;\n+\n+\tif (fs-\u003eflow_type \u0026 (FLOW_MAC_EXT | FLOW_EXT))\n+\t\treturn -EOPNOTSUPP;\n+\n+\tif (fs-\u003ering_cookie != RX_CLS_FLOW_DISC \u0026\u0026\n+\t ethtool_get_flow_spec_ring_vf(fs-\u003ering_cookie))\n+\t\treturn -EOPNOTSUPP;\n+\n+\tif (flow_type == IP_USER_FLOW) {\n+\t\tif (!bnge_verify_ntuple_ip4_flow(\u0026fs-\u003eh_u.usr_ip4_spec,\n+\t\t\t\t\t\t \u0026fs-\u003em_u.usr_ip4_spec))\n+\t\t\treturn -EOPNOTSUPP;\n+\t}\n+\n+\tif (flow_type == IPV6_USER_FLOW) {\n+\t\tif (!bnge_verify_ntuple_ip6_flow(\u0026fs-\u003eh_u.usr_ip6_spec,\n+\t\t\t\t\t\t \u0026fs-\u003em_u.usr_ip6_spec))\n+\t\t\treturn -EOPNOTSUPP;\n+\t}\n+\n+\tnew_fltr = kzalloc_obj(*new_fltr, GFP_KERNEL);\n+\tif (!new_fltr)\n+\t\treturn -ENOMEM;\n+\n+\tl2_fltr = bn-\u003evnic_info[BNGE_VNIC_DEFAULT].l2_filters[0];\n+\tnew_fltr-\u003el2_filter_id = l2_fltr-\u003ebase.filter_id;\n+\tfmasks = \u0026new_fltr-\u003efmasks;\n+\tfkeys = \u0026new_fltr-\u003efkeys;\n+\n+\trc = -EOPNOTSUPP;\n+\tswitch (flow_type) {\n+\tcase IP_USER_FLOW: {\n+\t\tstruct ethtool_usrip4_spec *ip_spec = \u0026fs-\u003eh_u.usr_ip4_spec;\n+\t\tstruct ethtool_usrip4_spec *ip_mask = \u0026fs-\u003em_u.usr_ip4_spec;\n+\n+\t\tfkeys-\u003ebasic.ip_proto = ip_mask-\u003eproto ? ip_spec-\u003eproto\n+\t\t\t\t\t\t : BNGE_IP_PROTO_WILDCARD;\n+\t\tfkeys-\u003ebasic.n_proto = htons(ETH_P_IP);\n+\t\tfkeys-\u003eaddrs.v4addrs.src = ip_spec-\u003eip4src;\n+\t\tfmasks-\u003eaddrs.v4addrs.src = ip_mask-\u003eip4src;\n+\t\tfkeys-\u003eaddrs.v4addrs.dst = ip_spec-\u003eip4dst;\n+\t\tfmasks-\u003eaddrs.v4addrs.dst = ip_mask-\u003eip4dst;\n+\t\tbreak;\n+\t}\n+\tcase TCP_V4_FLOW:\n+\tcase UDP_V4_FLOW: {\n+\t\tstruct ethtool_tcpip4_spec *ip_spec = \u0026fs-\u003eh_u.tcp_ip4_spec;\n+\t\tstruct ethtool_tcpip4_spec *ip_mask = \u0026fs-\u003em_u.tcp_ip4_spec;\n+\n+\t\tfkeys-\u003ebasic.ip_proto = IPPROTO_TCP;\n+\t\tif (flow_type == UDP_V4_FLOW)\n+\t\t\tfkeys-\u003ebasic.ip_proto = IPPROTO_UDP;\n+\t\tfkeys-\u003ebasic.n_proto = htons(ETH_P_IP);\n+\t\tfkeys-\u003eaddrs.v4addrs.src = ip_spec-\u003eip4src;\n+\t\tfmasks-\u003eaddrs.v4addrs.src = ip_mask-\u003eip4src;\n+\t\tfkeys-\u003eaddrs.v4addrs.dst = ip_spec-\u003eip4dst;\n+\t\tfmasks-\u003eaddrs.v4addrs.dst = ip_mask-\u003eip4dst;\n+\t\tfkeys-\u003eports.src = ip_spec-\u003epsrc;\n+\t\tfmasks-\u003eports.src = ip_mask-\u003epsrc;\n+\t\tfkeys-\u003eports.dst = ip_spec-\u003epdst;\n+\t\tfmasks-\u003eports.dst = ip_mask-\u003epdst;\n+\t\tbreak;\n+\t}\n+\tcase IPV6_USER_FLOW: {\n+\t\tstruct ethtool_usrip6_spec *ip_spec = \u0026fs-\u003eh_u.usr_ip6_spec;\n+\t\tstruct ethtool_usrip6_spec *ip_mask = \u0026fs-\u003em_u.usr_ip6_spec;\n+\n+\t\tfkeys-\u003ebasic.ip_proto = ip_mask-\u003el4_proto ? ip_spec-\u003el4_proto\n+\t\t\t\t\t: BNGE_IP_PROTO_WILDCARD;\n+\t\tfkeys-\u003ebasic.n_proto = htons(ETH_P_IPV6);\n+\n+\t\tmemcpy(\u0026fkeys-\u003eaddrs.v6addrs.src, ip_spec-\u003eip6src,\n+\t\t sizeof(struct in6_addr));\n+\t\tmemcpy(\u0026fmasks-\u003eaddrs.v6addrs.src, ip_mask-\u003eip6src,\n+\t\t sizeof(struct in6_addr));\n+\t\tmemcpy(\u0026fkeys-\u003eaddrs.v6addrs.dst, ip_spec-\u003eip6dst,\n+\t\t sizeof(struct in6_addr));\n+\t\tmemcpy(\u0026fmasks-\u003eaddrs.v6addrs.dst, ip_mask-\u003eip6dst,\n+\t\t sizeof(struct in6_addr));\n+\t\tbreak;\n+\t}\n+\tcase TCP_V6_FLOW:\n+\tcase UDP_V6_FLOW: {\n+\t\tstruct ethtool_tcpip6_spec *ip_spec = \u0026fs-\u003eh_u.tcp_ip6_spec;\n+\t\tstruct ethtool_tcpip6_spec *ip_mask = \u0026fs-\u003em_u.tcp_ip6_spec;\n+\n+\t\tfkeys-\u003ebasic.ip_proto = IPPROTO_TCP;\n+\t\tif (flow_type == UDP_V6_FLOW)\n+\t\t\tfkeys-\u003ebasic.ip_proto = IPPROTO_UDP;\n+\t\tfkeys-\u003ebasic.n_proto = htons(ETH_P_IPV6);\n+\n+\t\tmemcpy(\u0026fkeys-\u003eaddrs.v6addrs.src, ip_spec-\u003eip6src,\n+\t\t sizeof(struct in6_addr));\n+\t\tmemcpy(\u0026fmasks-\u003eaddrs.v6addrs.src, ip_mask-\u003eip6src,\n+\t\t sizeof(struct in6_addr));\n+\t\tmemcpy(\u0026fkeys-\u003eaddrs.v6addrs.dst, ip_spec-\u003eip6dst,\n+\t\t sizeof(struct in6_addr));\n+\t\tmemcpy(\u0026fmasks-\u003eaddrs.v6addrs.dst, ip_mask-\u003eip6dst,\n+\t\t sizeof(struct in6_addr));\n+\n+\t\tfkeys-\u003eports.src = ip_spec-\u003epsrc;\n+\t\tfmasks-\u003eports.src = ip_mask-\u003epsrc;\n+\t\tfkeys-\u003eports.dst = ip_spec-\u003epdst;\n+\t\tfmasks-\u003eports.dst = ip_mask-\u003epdst;\n+\t\tbreak;\n+\t}\n+\tdefault:\n+\t\trc = -EOPNOTSUPP;\n+\t\tgoto err_free_fltr;\n+\t}\n+\tif (!memcmp(\u0026BNGE_FLOW_MASK_NONE, fmasks, sizeof(*fmasks)))\n+\t\tgoto err_free_fltr;\n+\n+\tidx = bnge_get_ntp_filter_idx(bn, fkeys, NULL);\n+\trcu_read_lock();\n+\tfltr = bnge_lookup_ntp_filter_from_idx(bn, new_fltr, idx);\n+\tif (fltr) {\n+\t\trcu_read_unlock();\n+\t\trc = -EEXIST;\n+\t\tgoto err_free_fltr;\n+\t}\n+\trcu_read_unlock();\n+\n+\tnew_fltr-\u003ebase.flags = BNGE_ACT_NO_AGING;\n+\tif (fs-\u003eflow_type \u0026 FLOW_RSS) {\n+\t\tstruct bnge_rss_ctx *rss_ctx;\n+\n+\t\tnew_fltr-\u003ebase.fw_vnic_id = 0;\n+\t\tnew_fltr-\u003ebase.flags |= BNGE_ACT_RSS_CTX;\n+\t\trss_ctx = bnge_get_rss_ctx_from_index(bn, cmd-\u003erss_context);\n+\t\tif (rss_ctx) {\n+\t\t\tnew_fltr-\u003ebase.fw_vnic_id = rss_ctx-\u003eindex;\n+\t\t} else {\n+\t\t\trc = -EINVAL;\n+\t\t\tgoto err_free_fltr;\n+\t\t}\n+\t}\n+\tif (fs-\u003ering_cookie == RX_CLS_FLOW_DISC)\n+\t\tnew_fltr-\u003ebase.flags |= BNGE_ACT_DROP;\n+\telse\n+\t\tnew_fltr-\u003ebase.rxq = ethtool_get_flow_spec_ring(fs-\u003ering_cookie);\n+\t__set_bit(BNGE_FLTR_VALID, \u0026new_fltr-\u003ebase.state);\n+\trc = bnge_insert_ntp_filter(bn, new_fltr, idx);\n+\tif (!rc) {\n+\t\trc = bnge_hwrm_cfa_ntuple_filter_alloc(bn-\u003ebd, new_fltr);\n+\t\tif (rc) {\n+\t\t\tbnge_del_ntp_filter(bn, new_fltr);\n+\t\t\treturn rc;\n+\t\t}\n+\t\tfs-\u003elocation = BNGE_MAX_L2_FLTRS + new_fltr-\u003ebase.sw_id;\n+\t\treturn 0;\n+\t}\n+\n+err_free_fltr:\n+\tkfree(new_fltr);\n+\treturn rc;\n+}\n+\n+static int bnge_add_l2_cls_rule(struct bnge_net *bn,\n+\t\t\t\tstruct ethtool_rx_flow_spec *fs)\n+{\n+\tu32 ring = ethtool_get_flow_spec_ring(fs-\u003ering_cookie);\n+\tstruct ethhdr *h_ether = \u0026fs-\u003eh_u.ether_spec;\n+\tstruct ethhdr *m_ether = \u0026fs-\u003em_u.ether_spec;\n+\tstruct bnge_l2_filter *fltr;\n+\tstruct bnge_l2_key key;\n+\tu16 vnic_id;\n+\tu8 flags;\n+\tint rc;\n+\n+\tif (!is_broadcast_ether_addr(m_ether-\u003eh_dest))\n+\t\treturn -EINVAL;\n+\n+\tif (is_broadcast_ether_addr(h_ether-\u003eh_dest) ||\n+\t is_multicast_ether_addr(h_ether-\u003eh_dest))\n+\t\treturn -EINVAL;\n+\n+\tether_addr_copy(key.dst_mac_addr, h_ether-\u003eh_dest);\n+\tkey.vlan = 0;\n+\tif (fs-\u003eflow_type \u0026 FLOW_EXT) {\n+\t\tstruct ethtool_flow_ext *m_ext = \u0026fs-\u003em_ext;\n+\t\tstruct ethtool_flow_ext *h_ext = \u0026fs-\u003eh_ext;\n+\n+\t\tif (m_ext-\u003evlan_tci != htons(0xfff) || !h_ext-\u003evlan_tci)\n+\t\t\treturn -EINVAL;\n+\t\tkey.vlan = ntohs(h_ext-\u003evlan_tci);\n+\t}\n+\n+\tflags = BNGE_ACT_RING_DST;\n+\tvnic_id = bn-\u003evnic_info[BNGE_VNIC_DEFAULT].fw_vnic_id;\n+\n+\tfltr = bnge_alloc_user_l2_filter(bn, \u0026key, flags);\n+\tif (IS_ERR(fltr))\n+\t\treturn PTR_ERR(fltr);\n+\n+\tfltr-\u003ebase.fw_vnic_id = vnic_id;\n+\tfltr-\u003ebase.rxq = ring;\n+\trc = bnge_hwrm_l2_filter_alloc(bn-\u003ebd, fltr);\n+\tif (rc)\n+\t\tbnge_del_l2_filter_rcu(bn, fltr);\n+\telse\n+\t\tfs-\u003elocation = fltr-\u003ebase.sw_id;\n+\treturn rc;\n+}\n+\n+static int bnge_srxclsrlins(struct bnge_net *bn, struct ethtool_rxnfc *cmd)\n+{\n+\tstruct ethtool_rx_flow_spec *fs = \u0026cmd-\u003efs;\n+\tstruct bnge_dev *bd = bn-\u003ebd;\n+\tu32 ring, flow_type;\n+\tint rc;\n+\n+\tif (!netif_running(bn-\u003enetdev))\n+\t\treturn -EAGAIN;\n+\tif (!(bn-\u003epriv_flags \u0026 BNGE_NET_EN_NTUPLE))\n+\t\treturn -EPERM;\n+\tif (fs-\u003elocation != RX_CLS_LOC_ANY)\n+\t\treturn -EINVAL;\n+\n+\tflow_type = fs-\u003eflow_type;\n+\tif ((flow_type == IP_USER_FLOW ||\n+\t flow_type == IPV6_USER_FLOW) \u0026\u0026\n+\t !(bd-\u003efw_cap \u0026 BNGE_FW_CAP_CFA_NTUPLE_RX_EXT_IP_PROTO))\n+\t\treturn -EOPNOTSUPP;\n+\n+\tif (flow_type \u0026 FLOW_MAC_EXT)\n+\t\treturn -EINVAL;\n+\n+\tflow_type \u0026= ~FLOW_EXT;\n+\n+\tif (fs-\u003ering_cookie == RX_CLS_FLOW_DISC \u0026\u0026 flow_type != ETHER_FLOW)\n+\t\treturn bnge_add_ntuple_cls_rule(bn, cmd);\n+\n+\tring = ethtool_get_flow_spec_ring(fs-\u003ering_cookie);\n+\tif (ring \u003e= bd-\u003erx_nr_rings)\n+\t\treturn -EINVAL;\n+\n+\tif (flow_type == ETHER_FLOW)\n+\t\trc = bnge_add_l2_cls_rule(bn, fs);\n+\telse\n+\t\trc = bnge_add_ntuple_cls_rule(bn, cmd);\n+\treturn rc;\n+}\n+\n+static int bnge_srxclsrldel(struct bnge_net *bn, struct ethtool_rxnfc *cmd)\n+{\n+\tstruct ethtool_rx_flow_spec *fs = \u0026cmd-\u003efs;\n+\tstruct bnge_filter_base *fltr_base;\n+\tstruct bnge_ntuple_filter *fltr;\n+\tu32 id = fs-\u003elocation;\n+\n+\trcu_read_lock();\n+\tfltr_base = bnge_get_one_fltr_rcu(bn, bn-\u003el2_fltr_hash_tbl,\n+\t\t\t\t\t BNGE_L2_FLTR_HASH_SIZE, id, 0);\n+\tif (fltr_base) {\n+\t\tstruct bnge_l2_filter *l2_fltr;\n+\n+\t\tl2_fltr = container_of(fltr_base, struct bnge_l2_filter, base);\n+\t\trcu_read_unlock();\n+\t\tbnge_hwrm_l2_filter_free(bn-\u003ebd, l2_fltr);\n+\t\tbnge_del_l2_filter_rcu(bn, l2_fltr);\n+\t\treturn 0;\n+\t}\n+\tfltr_base = bnge_get_one_fltr_rcu(bn, bn-\u003entp_fltr_hash_tbl,\n+\t\t\t\t\t BNGE_NTP_FLTR_HASH_SIZE, id,\n+\t\t\t\t\t BNGE_MAX_L2_FLTRS);\n+\tif (!fltr_base) {\n+\t\trcu_read_unlock();\n+\t\treturn -ENOENT;\n+\t}\n+\n+\tfltr = container_of(fltr_base, struct bnge_ntuple_filter, base);\n+\tif (!(fltr-\u003ebase.flags \u0026 BNGE_ACT_NO_AGING)) {\n+\t\trcu_read_unlock();\n+\t\treturn -EINVAL;\n+\t}\n+\trcu_read_unlock();\n+\tbnge_hwrm_cfa_ntuple_filter_free(bn-\u003ebd, fltr);\n+\tbnge_del_ntp_filter(bn, fltr);\n+\treturn 0;\n+}\n+\n+static int bnge_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd,\n+\t\t\t u32 *rule_locs)\n+{\n+\tstruct bnge_net *bn = netdev_priv(dev);\n+\tstruct bnge_dev *bd = bn-\u003ebd;\n+\tint rc = 0;\n+\n+\tswitch (cmd-\u003ecmd) {\n+\tcase ETHTOOL_GRXCLSRLCNT:\n+\t\tcmd-\u003erule_cnt = bn-\u003euser_fltr_count;\n+\t\tcmd-\u003edata = bd-\u003emax_fltr | RX_CLS_LOC_SPECIAL;\n+\t\tbreak;\n+\n+\tcase ETHTOOL_GRXCLSRLALL:\n+\t\trc = bnge_grxclsrlall(bn, cmd, (u32 *)rule_locs);\n+\t\tbreak;\n+\n+\tcase ETHTOOL_GRXCLSRULE:\n+\t\trc = bnge_grxclsrule(bn, cmd);\n+\t\tbreak;\n+\n+\tdefault:\n+\t\trc = -EOPNOTSUPP;\n+\t\tbreak;\n+\t}\n+\n+\treturn rc;\n+}\n+\n+static int bnge_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd)\n+{\n+\tstruct bnge_net *bn = netdev_priv(dev);\n+\tint rc;\n+\n+\tswitch (cmd-\u003ecmd) {\n+\tcase ETHTOOL_SRXCLSRLINS:\n+\t\trc = bnge_srxclsrlins(bn, cmd);\n+\t\tbreak;\n+\n+\tcase ETHTOOL_SRXCLSRLDEL:\n+\t\trc = bnge_srxclsrldel(bn, cmd);\n+\t\tbreak;\n+\n+\tdefault:\n+\t\trc = -EOPNOTSUPP;\n+\t\tbreak;\n+\t}\n+\treturn rc;\n+}\n+\n static const struct ethtool_ops bnge_ethtool_ops = {\n \t.cap_link_lanes_supported\t= 1,\n \t.get_link_ksettings\t= bnge_get_link_ksettings,\n@@ -757,6 +1739,23 @@ static const struct ethtool_ops bnge_ethtool_ops = {\n \t.get_eth_ctrl_stats\t= bnge_get_eth_ctrl_stats,\n \t.get_pause_stats\t= bnge_get_pause_stats,\n \t.get_rmon_stats\t\t= bnge_get_rmon_stats,\n+\t/* RXFH */\n+\t.rxfh_per_ctx_key\t= 1,\n+\t.rxfh_max_num_contexts\t= BNGE_MAX_ETH_RSS_CTX + 1,\n+\t.rxfh_indir_space\t= BNGE_MAX_RSS_TABLE_ENTRIES,\n+\t.rxfh_priv_size\t\t= sizeof(struct bnge_rss_ctx),\n+\t.get_rx_ring_count\t= bnge_get_rx_ring_count,\n+\t.get_rxfh_indir_size\t= bnge_get_rxfh_indir_size_eth,\n+\t.get_rxfh_key_size\t= bnge_get_rxfh_key_size,\n+\t.get_rxfh\t\t= bnge_get_rxfh,\n+\t.set_rxfh\t\t= bnge_set_rxfh,\n+\t.get_rxfh_fields\t= bnge_get_rxfh_fields,\n+\t.set_rxfh_fields\t= bnge_set_rxfh_fields,\n+\t.create_rxfh_context\t= bnge_create_rxfh_context,\n+\t.modify_rxfh_context\t= bnge_modify_rxfh_context,\n+\t.remove_rxfh_context\t= bnge_remove_rxfh_context,\n+\t.get_rxnfc\t\t= bnge_get_rxnfc,\n+\t.set_rxnfc\t\t= bnge_set_rxnfc,\n };\n \n void bnge_set_ethtool_ops(struct net_device *dev)\ndiff --git a/drivers/net/ethernet/broadcom/bnge/bnge_filter.c b/drivers/net/ethernet/broadcom/bnge/bnge_filter.c\nnew file mode 100644\nindex 0000000000000..6f75e08a4996e\n--- /dev/null\n+++ b/drivers/net/ethernet/broadcom/bnge/bnge_filter.c\n@@ -0,0 +1,633 @@\n+// SPDX-License-Identifier: GPL-2.0\n+// Copyright (c) 2026 Broadcom.\n+\n+#include \u003clinux/kernel.h\u003e\n+#include \u003clinux/dma-mapping.h\u003e\n+#include \u003clinux/jhash.h\u003e\n+#include \u003cnet/netdev_lock.h\u003e\n+#include \u003cnet/ipv6.h\u003e\n+\n+#include \"bnge.h\"\n+#include \"bnge_netdev.h\"\n+#include \"bnge_vnic.h\"\n+#include \"bnge_hwrm_lib.h\"\n+#include \"bnge_filter.h\"\n+\n+#define BNGE_IPV6_MASK_ALL {{{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \\\n+\t\t\t 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }}}\n+#define BNGE_IPV6_MASK_NONE {{{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }}}\n+\n+const struct bnge_flow_masks BNGE_FLOW_MASK_NONE = {\n+\t.ports = {\n+\t\t.src = 0,\n+\t\t.dst = 0,\n+\t},\n+\t.addrs = {\n+\t\t.v6addrs = {\n+\t\t\t.src = BNGE_IPV6_MASK_NONE,\n+\t\t\t.dst = BNGE_IPV6_MASK_NONE,\n+\t\t},\n+\t},\n+};\n+\n+const struct bnge_flow_masks BNGE_FLOW_IPV6_MASK_ALL = {\n+\t.ports = {\n+\t\t.src = cpu_to_be16(0xffff),\n+\t\t.dst = cpu_to_be16(0xffff),\n+\t},\n+\t.addrs = {\n+\t\t.v6addrs = {\n+\t\t\t.src = BNGE_IPV6_MASK_ALL,\n+\t\t\t.dst = BNGE_IPV6_MASK_ALL,\n+\t\t},\n+\t},\n+};\n+\n+const struct bnge_flow_masks BNGE_FLOW_IPV4_MASK_ALL = {\n+\t.ports = {\n+\t\t.src = cpu_to_be16(0xffff),\n+\t\t.dst = cpu_to_be16(0xffff),\n+\t},\n+\t.addrs = {\n+\t\t.v4addrs = {\n+\t\t\t.src = cpu_to_be32(0xffffffff),\n+\t\t\t.dst = cpu_to_be32(0xffffffff),\n+\t\t},\n+\t},\n+};\n+\n+static void bnge_insert_usr_fltr(struct bnge_net *bn,\n+\t\t\t\t struct bnge_filter_base *fltr)\n+{\n+\tu8 type = fltr-\u003etype, flags = fltr-\u003eflags;\n+\n+\tINIT_LIST_HEAD(\u0026fltr-\u003elist_node);\n+\tif ((type == BNGE_FLTR_TYPE_L2 \u0026\u0026 flags \u0026 BNGE_ACT_RING_DST) ||\n+\t (type == BNGE_FLTR_TYPE_NTUPLE \u0026\u0026 flags \u0026 BNGE_ACT_NO_AGING)) {\n+\t\tlist_add_tail(\u0026fltr-\u003elist_node, \u0026bn-\u003eusr_fltr_list);\n+\t\tbn-\u003euser_fltr_count++;\n+\t}\n+}\n+\n+static void bnge_del_usr_fltr_node(struct bnge_net *bn,\n+\t\t\t\t struct bnge_filter_base *fltr)\n+{\n+\tif (!list_empty(\u0026fltr-\u003elist_node)) {\n+\t\tlist_del_init(\u0026fltr-\u003elist_node);\n+\t\tbn-\u003euser_fltr_count--;\n+\t}\n+}\n+\n+void bnge_del_l2_filter_rcu(struct bnge_net *bn, struct bnge_l2_filter *lfltr)\n+{\n+\tstruct bnge_filter_base *fltr = \u0026lfltr-\u003ebase;\n+\n+\thlist_del_rcu(\u0026fltr-\u003ehlist);\n+\tbnge_del_usr_fltr_node(bn, fltr);\n+\tclear_bit(fltr-\u003esw_id, bn-\u003el2_fltr_bmap);\n+\tbn-\u003el2_fltr_count--;\n+\tkfree_rcu(lfltr, base.rcu);\n+}\n+\n+void bnge_del_l2_filter(struct bnge_net *bn, struct bnge_l2_filter *lfltr)\n+{\n+\tstruct bnge_filter_base *fltr = \u0026lfltr-\u003ebase;\n+\n+\thlist_del(\u0026fltr-\u003ehlist);\n+\tbnge_del_usr_fltr_node(bn, fltr);\n+\tclear_bit(fltr-\u003esw_id, bn-\u003el2_fltr_bmap);\n+\tbn-\u003el2_fltr_count--;\n+\tkfree(fltr);\n+}\n+\n+void bnge_del_ntp_filter(struct bnge_net *bn, struct bnge_ntuple_filter *nfltr)\n+{\n+\tstruct bnge_filter_base *fltr = \u0026nfltr-\u003ebase;\n+\n+\thlist_del(\u0026fltr-\u003ehlist);\n+\tbnge_del_usr_fltr_node(bn, fltr);\n+\tclear_bit(fltr-\u003esw_id, bn-\u003entp_fltr_bmap);\n+\tbn-\u003entp_fltr_count--;\n+\tkfree(fltr);\n+}\n+\n+void bnge_del_ntp_filter_rcu(struct bnge_net *bn,\n+\t\t\t struct bnge_ntuple_filter *fltr)\n+{\n+\tspin_lock_bh(\u0026bn-\u003entp_fltr_lock);\n+\thlist_del_rcu(\u0026fltr-\u003ebase.hlist);\n+\tbnge_del_usr_fltr_node(bn, \u0026fltr-\u003ebase);\n+\tbn-\u003entp_fltr_count--;\n+\tspin_unlock_bh(\u0026bn-\u003entp_fltr_lock);\n+\n+\tclear_bit(fltr-\u003ebase.sw_id, bn-\u003entp_fltr_bmap);\n+\tkfree_rcu(fltr, base.rcu);\n+}\n+\n+\n+static int bnge_init_l2_filter(struct bnge_net *bn,\n+\t\t\t struct bnge_l2_filter *fltr,\n+\t\t\t struct bnge_l2_key *key, u32 idx)\n+{\n+\tstruct hlist_head *head;\n+\tint bit_id;\n+\n+\tether_addr_copy(fltr-\u003el2_key.dst_mac_addr, key-\u003edst_mac_addr);\n+\tfltr-\u003el2_key.vlan = key-\u003evlan;\n+\tfltr-\u003ebase.type = BNGE_FLTR_TYPE_L2;\n+\n+\tbit_id = bitmap_find_free_region(bn-\u003el2_fltr_bmap,\n+\t\t\t\t\t BNGE_MAX_L2_FLTRS, 0);\n+\tif (bit_id \u003c 0)\n+\t\treturn -ENOMEM;\n+\tfltr-\u003ebase.sw_id = (u16)bit_id;\n+\n+\tbn-\u003el2_fltr_count++;\n+\n+\thead = \u0026bn-\u003el2_fltr_hash_tbl[idx];\n+\thlist_add_head_rcu(\u0026fltr-\u003ebase.hlist, head);\n+\tbnge_insert_usr_fltr(bn, \u0026fltr-\u003ebase);\n+\treturn 0;\n+}\n+\n+struct bnge_l2_filter *bnge_lookup_l2_filter(struct bnge_net *bn,\n+\t\t\t\t\t struct bnge_l2_key *key,\n+\t\t\t\t\t u32 idx)\n+{\n+\tstruct bnge_l2_filter *fltr;\n+\tstruct hlist_head *head;\n+\n+\thead = \u0026bn-\u003el2_fltr_hash_tbl[idx];\n+\thlist_for_each_entry_rcu(fltr, head, base.hlist) {\n+\t\tstruct bnge_l2_key *l2_key = \u0026fltr-\u003el2_key;\n+\n+\t\tif (ether_addr_equal(l2_key-\u003edst_mac_addr, key-\u003edst_mac_addr) \u0026\u0026\n+\t\t l2_key-\u003evlan == key-\u003evlan)\n+\t\t\treturn fltr;\n+\t}\n+\treturn NULL;\n+}\n+\n+u64 bnge_lookup_l2_filter_rcu(struct bnge_net *bn,\n+\t\t\t struct bnge_l2_key *key,\n+\t\t\t u32 idx)\n+{\n+\tu64 id = BNGE_FLTR_ID_INVALID;\n+\tstruct bnge_l2_filter *fltr;\n+\n+\trcu_read_lock();\n+\tfltr = bnge_lookup_l2_filter(bn, key, idx);\n+\tif (!fltr)\n+\t\tgoto exit;\n+\n+\tid = fltr-\u003ebase.filter_id;\n+\n+exit:\n+\trcu_read_unlock();\n+\treturn id;\n+}\n+\n+static struct bnge_l2_filter *bnge_alloc_l2_filter(struct bnge_net *bn,\n+\t\t\t\t\t\t struct bnge_l2_key *key,\n+\t\t\t\t\t\t gfp_t gfp)\n+{\n+\tstruct bnge_l2_filter *fltr;\n+\tu32 idx;\n+\tint rc;\n+\n+\tidx = jhash2(\u0026key-\u003efilter_key, BNGE_L2_KEY_SIZE, bn-\u003ehash_seed) \u0026\n+\t BNGE_L2_FLTR_HASH_MASK;\n+\tfltr = bnge_lookup_l2_filter(bn, key, idx);\n+\tif (fltr)\n+\t\treturn fltr;\n+\n+\tfltr = kzalloc_obj(*fltr, gfp);\n+\tif (!fltr)\n+\t\treturn ERR_PTR(-ENOMEM);\n+\n+\trc = bnge_init_l2_filter(bn, fltr, key, idx);\n+\tif (rc) {\n+\t\tkfree(fltr);\n+\t\tfltr = ERR_PTR(rc);\n+\t}\n+\n+\treturn fltr;\n+}\n+\n+struct bnge_l2_filter *bnge_alloc_user_l2_filter(struct bnge_net *bn,\n+\t\t\t\t\t\t struct bnge_l2_key *key,\n+\t\t\t\t\t\t u8 flags)\n+{\n+\tstruct bnge_l2_filter *fltr;\n+\tu32 idx;\n+\tint rc;\n+\n+\tidx = jhash2(\u0026key-\u003efilter_key, BNGE_L2_KEY_SIZE, bn-\u003ehash_seed) \u0026\n+\t BNGE_L2_FLTR_HASH_MASK;\n+\tfltr = bnge_lookup_l2_filter(bn, key, idx);\n+\tif (fltr) {\n+\t\tfltr = ERR_PTR(-EEXIST);\n+\t\tgoto l2_filter_exit;\n+\t}\n+\tfltr = kzalloc_obj(*fltr, GFP_ATOMIC);\n+\tif (!fltr) {\n+\t\tfltr = ERR_PTR(-ENOMEM);\n+\t\tgoto l2_filter_exit;\n+\t}\n+\tfltr-\u003ebase.flags = flags;\n+\trc = bnge_init_l2_filter(bn, fltr, key, idx);\n+\tif (rc) {\n+\t\tkfree(fltr);\n+\t\treturn ERR_PTR(rc);\n+\t}\n+\n+l2_filter_exit:\n+\treturn fltr;\n+}\n+\n+void bnge_free_l2_filters(struct bnge_net *bn)\n+{\n+\tint i;\n+\n+\tnetdev_assert_locked_or_invisible(bn-\u003enetdev);\n+\n+\tfor (i = 0; i \u003c BNGE_L2_FLTR_HASH_SIZE; i++) {\n+\t\tstruct bnge_l2_filter *fltr;\n+\t\tstruct hlist_head *head;\n+\t\tstruct hlist_node *tmp;\n+\n+\t\thead = \u0026bn-\u003el2_fltr_hash_tbl[i];\n+\t\thlist_for_each_entry_safe(fltr, tmp, head, base.hlist)\n+\t\t\tbnge_del_l2_filter(bn, fltr);\n+\t}\n+\n+\tbitmap_free(bn-\u003el2_fltr_bmap);\n+\tbn-\u003el2_fltr_bmap = NULL;\n+\tbn-\u003el2_fltr_count = 0;\n+}\n+\n+void bnge_free_ntp_fltrs(struct bnge_net *bn, bool skip_user)\n+{\n+\tint i;\n+\n+\tnetdev_assert_locked_or_invisible(bn-\u003enetdev);\n+\n+\t/* Under netdev instance lock and all our NAPIs have been disabled.\n+\t * It's safe to delete the hash table.\n+\t */\n+\tfor (i = 0; i \u003c BNGE_NTP_FLTR_HASH_SIZE; i++) {\n+\t\tstruct bnge_ntuple_filter *fltr;\n+\t\tstruct hlist_head *head;\n+\t\tstruct hlist_node *tmp;\n+\n+\t\thead = \u0026bn-\u003entp_fltr_hash_tbl[i];\n+\t\thlist_for_each_entry_safe(fltr, tmp, head, base.hlist) {\n+\t\t\tif (skip_user \u0026\u0026 !list_empty(\u0026fltr-\u003ebase.list_node))\n+\t\t\t\tcontinue;\n+\t\t\tbnge_del_ntp_filter(bn, fltr);\n+\t\t}\n+\t}\n+\n+\tif (skip_user)\n+\t\treturn;\n+\n+\tbitmap_free(bn-\u003entp_fltr_bmap);\n+\tbn-\u003entp_fltr_bmap = NULL;\n+\tbn-\u003entp_fltr_count = 0;\n+}\n+\n+static int bnge_alloc_l2_fltrs_mem(struct bnge_net *bn)\n+{\n+\tint i, rc = 0;\n+\n+\tif (bn-\u003el2_fltr_bmap)\n+\t\treturn 0;\n+\n+\tfor (i = 0; i \u003c BNGE_L2_FLTR_HASH_SIZE; i++)\n+\t\tINIT_HLIST_HEAD(\u0026bn-\u003el2_fltr_hash_tbl[i]);\n+\n+\tbn-\u003el2_fltr_count = 0;\n+\tbn-\u003el2_fltr_bmap = bitmap_zalloc(BNGE_MAX_L2_FLTRS, GFP_KERNEL);\n+\n+\tif (!bn-\u003el2_fltr_bmap)\n+\t\trc = -ENOMEM;\n+\n+\treturn rc;\n+}\n+\n+static int bnge_alloc_ntp_fltrs_mem(struct bnge_net *bn)\n+{\n+\tstruct bnge_dev *bd = bn-\u003ebd;\n+\tint i, rc = 0;\n+\n+\tif (!(bn-\u003epriv_flags \u0026 BNGE_NET_EN_NTUPLE) || bn-\u003entp_fltr_bmap)\n+\t\treturn 0;\n+\n+\tfor (i = 0; i \u003c BNGE_NTP_FLTR_HASH_SIZE; i++)\n+\t\tINIT_HLIST_HEAD(\u0026bn-\u003entp_fltr_hash_tbl[i]);\n+\n+\tbn-\u003entp_fltr_count = 0;\n+\tbn-\u003entp_fltr_bmap = bitmap_zalloc(bd-\u003emax_fltr, GFP_KERNEL);\n+\n+\tif (!bn-\u003entp_fltr_bmap)\n+\t\trc = -ENOMEM;\n+\n+\treturn rc;\n+}\n+\n+int bnge_fltrs_mem(struct bnge_net *bn)\n+{\n+\tint rc;\n+\n+\trc = bnge_alloc_ntp_fltrs_mem(bn);\n+\tif (rc)\n+\t\treturn rc;\n+\n+\trc = bnge_alloc_l2_fltrs_mem(bn);\n+\tif (rc)\n+\t\tgoto err_free_ntp_mem;\n+\n+\treturn 0;\n+\n+err_free_ntp_mem:\n+\tbitmap_free(bn-\u003entp_fltr_bmap);\n+\tbn-\u003entp_fltr_bmap = NULL;\n+\treturn rc;\n+}\n+\n+#define BNGE_IPV4_4TUPLE(bd, fkeys)\t\t\t\t\t\\\n+\t(((fkeys)-\u003ebasic.ip_proto == IPPROTO_TCP \u0026\u0026\t\t\t\\\n+\t (bd)-\u003erss_hash_cfg \u0026 VNIC_RSS_CFG_REQ_HASH_TYPE_TCP_IPV4) ||\t\\\n+\t ((fkeys)-\u003ebasic.ip_proto == IPPROTO_UDP \u0026\u0026\t\t\t\\\n+\t (bd)-\u003erss_hash_cfg \u0026 VNIC_RSS_CFG_REQ_HASH_TYPE_UDP_IPV4))\n+\n+#define BNGE_IPV6_4TUPLE(bd, fkeys)\t\t\t\t\t\\\n+\t(((fkeys)-\u003ebasic.ip_proto == IPPROTO_TCP \u0026\u0026\t\t\t\\\n+\t (bd)-\u003erss_hash_cfg \u0026 VNIC_RSS_CFG_REQ_HASH_TYPE_TCP_IPV6) ||\t\\\n+\t ((fkeys)-\u003ebasic.ip_proto == IPPROTO_UDP \u0026\u0026\t\t\t\\\n+\t (bd)-\u003erss_hash_cfg \u0026 VNIC_RSS_CFG_REQ_HASH_TYPE_UDP_IPV6))\n+\n+static u32 bnge_get_rss_flow_tuple_len(struct bnge_dev *bd,\n+\t\t\t\t struct flow_keys *fkeys)\n+{\n+\tif (fkeys-\u003ebasic.n_proto == htons(ETH_P_IP)) {\n+\t\tif (BNGE_IPV4_4TUPLE(bd, fkeys))\n+\t\t\treturn sizeof(fkeys-\u003eaddrs.v4addrs) +\n+\t\t\t sizeof(fkeys-\u003eports);\n+\n+\t\tif (bd-\u003erss_hash_cfg \u0026 VNIC_RSS_CFG_REQ_HASH_TYPE_IPV4)\n+\t\t\treturn sizeof(fkeys-\u003eaddrs.v4addrs);\n+\t}\n+\n+\tif (fkeys-\u003ebasic.n_proto == htons(ETH_P_IPV6)) {\n+\t\tif (BNGE_IPV6_4TUPLE(bd, fkeys))\n+\t\t\treturn sizeof(fkeys-\u003eaddrs.v6addrs) +\n+\t\t\t sizeof(fkeys-\u003eports);\n+\n+\t\tif (bd-\u003erss_hash_cfg \u0026 VNIC_RSS_CFG_REQ_HASH_TYPE_IPV6)\n+\t\t\treturn sizeof(fkeys-\u003eaddrs.v6addrs);\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static u32 bnge_toeplitz(struct bnge_net *bn, struct flow_keys *fkeys,\n+\t\t\t const unsigned char *key)\n+{\n+\tu64 prefix = bn-\u003etoeplitz_prefix, hash = 0;\n+\tstruct bnge_ipv4_tuple tuple4;\n+\tstruct bnge_ipv6_tuple tuple6;\n+\tstruct bnge_dev *bd = bn-\u003ebd;\n+\tu8 *four_tuple;\n+\tint i, j, len;\n+\n+\tlen = bnge_get_rss_flow_tuple_len(bd, fkeys);\n+\tif (!len)\n+\t\treturn 0;\n+\n+\tif (fkeys-\u003ebasic.n_proto == htons(ETH_P_IP)) {\n+\t\ttuple4.v4addrs = fkeys-\u003eaddrs.v4addrs;\n+\t\ttuple4.ports = fkeys-\u003eports;\n+\t\tfour_tuple = (u8 *)\u0026tuple4;\n+\t} else {\n+\t\ttuple6.v6addrs = fkeys-\u003eaddrs.v6addrs;\n+\t\ttuple6.ports = fkeys-\u003eports;\n+\t\tfour_tuple = (u8 *)\u0026tuple6;\n+\t}\n+\n+\tfor (i = 0, j = 8; i \u003c len; i++, j++) {\n+\t\tu8 byte = four_tuple[i];\n+\t\tint bit;\n+\n+\t\tfor (bit = 0; bit \u003c 8; bit++, prefix \u003c\u003c= 1, byte \u003c\u003c= 1) {\n+\t\t\tif (byte \u0026 0x80)\n+\t\t\t\thash ^= prefix;\n+\t\t}\n+\t\tprefix |= (j \u003c HW_HASH_KEY_SIZE) ? key[j] : 0;\n+\t}\n+\n+\t/* The valid part of the hash is in the upper 32 bits. */\n+\treturn (hash \u003e\u003e 32) \u0026 BNGE_NTP_FLTR_HASH_MASK;\n+}\n+\n+u32 bnge_get_ntp_filter_idx(struct bnge_net *bn, struct flow_keys *fkeys,\n+\t\t\t const struct sk_buff *skb)\n+{\n+\tstruct bnge_vnic_info *vnic;\n+\n+\tif (skb)\n+\t\treturn skb_get_hash_raw(skb) \u0026 BNGE_NTP_FLTR_HASH_MASK;\n+\n+\tvnic = \u0026bn-\u003evnic_info[BNGE_VNIC_DEFAULT];\n+\treturn bnge_toeplitz(bn, fkeys, (void *)vnic-\u003erss_hash_key);\n+}\n+\n+int bnge_insert_ntp_filter(struct bnge_net *bn, struct bnge_ntuple_filter *fltr,\n+\t\t\t u32 idx)\n+{\n+\tstruct hlist_head *head;\n+\tint bit_id;\n+\n+\tspin_lock_bh(\u0026bn-\u003entp_fltr_lock);\n+\tbit_id = bitmap_find_free_region(bn-\u003entp_fltr_bmap,\n+\t\t\t\t\t bn-\u003ebd-\u003emax_fltr, 0);\n+\tif (bit_id \u003c 0) {\n+\t\tspin_unlock_bh(\u0026bn-\u003entp_fltr_lock);\n+\t\treturn -ENOMEM;\n+\t}\n+\n+\tfltr-\u003ebase.sw_id = (u16)bit_id;\n+\tfltr-\u003ebase.type = BNGE_FLTR_TYPE_NTUPLE;\n+\tfltr-\u003ebase.flags |= BNGE_ACT_RING_DST;\n+\n+\thead = \u0026bn-\u003entp_fltr_hash_tbl[idx];\n+\thlist_add_head_rcu(\u0026fltr-\u003ebase.hlist, head);\n+\n+\tbnge_insert_usr_fltr(bn, \u0026fltr-\u003ebase);\n+\tbn-\u003entp_fltr_count++;\n+\n+\tspin_unlock_bh(\u0026bn-\u003entp_fltr_lock);\n+\n+\treturn 0;\n+}\n+\n+static bool bnge_fltr_match(struct bnge_ntuple_filter *f1,\n+\t\t\t struct bnge_ntuple_filter *f2)\n+{\n+\tstruct bnge_flow_masks *masks1 = \u0026f1-\u003efmasks;\n+\tstruct bnge_flow_masks *masks2 = \u0026f2-\u003efmasks;\n+\tstruct flow_keys *keys1 = \u0026f1-\u003efkeys;\n+\tstruct flow_keys *keys2 = \u0026f2-\u003efkeys;\n+\n+\tif (keys1-\u003ebasic.n_proto != keys2-\u003ebasic.n_proto ||\n+\t keys1-\u003ebasic.ip_proto != keys2-\u003ebasic.ip_proto)\n+\t\treturn false;\n+\n+\tif (keys1-\u003ebasic.n_proto == htons(ETH_P_IP)) {\n+\t\tif (keys1-\u003eaddrs.v4addrs.src != keys2-\u003eaddrs.v4addrs.src ||\n+\t\t masks1-\u003eaddrs.v4addrs.src != masks2-\u003eaddrs.v4addrs.src ||\n+\t\t keys1-\u003eaddrs.v4addrs.dst != keys2-\u003eaddrs.v4addrs.dst ||\n+\t\t masks1-\u003eaddrs.v4addrs.dst != masks2-\u003eaddrs.v4addrs.dst)\n+\t\t\treturn false;\n+\t} else {\n+\t\tif (!ipv6_addr_equal(\u0026keys1-\u003eaddrs.v6addrs.src,\n+\t\t\t\t \u0026keys2-\u003eaddrs.v6addrs.src) ||\n+\t\t !ipv6_addr_equal(\u0026masks1-\u003eaddrs.v6addrs.src,\n+\t\t\t\t \u0026masks2-\u003eaddrs.v6addrs.src) ||\n+\t\t !ipv6_addr_equal(\u0026keys1-\u003eaddrs.v6addrs.dst,\n+\t\t\t\t \u0026keys2-\u003eaddrs.v6addrs.dst) ||\n+\t\t !ipv6_addr_equal(\u0026masks1-\u003eaddrs.v6addrs.dst,\n+\t\t\t\t \u0026masks2-\u003eaddrs.v6addrs.dst))\n+\t\t\treturn false;\n+\t}\n+\n+\treturn keys1-\u003eports.src == keys2-\u003eports.src \u0026\u0026\n+\t masks1-\u003eports.src == masks2-\u003eports.src \u0026\u0026\n+\t keys1-\u003eports.dst == keys2-\u003eports.dst \u0026\u0026\n+\t masks1-\u003eports.dst == masks2-\u003eports.dst \u0026\u0026\n+\t keys1-\u003econtrol.flags == keys2-\u003econtrol.flags \u0026\u0026\n+\t f1-\u003el2_filter_id == f2-\u003el2_filter_id;\n+}\n+\n+struct bnge_ntuple_filter *\n+bnge_lookup_ntp_filter_from_idx(struct bnge_net *bn,\n+\t\t\t\tstruct bnge_ntuple_filter *fltr, u32 idx)\n+{\n+\tstruct bnge_ntuple_filter *f;\n+\tstruct hlist_head *head;\n+\n+\thead = \u0026bn-\u003entp_fltr_hash_tbl[idx];\n+\thlist_for_each_entry_rcu(f, head, base.hlist) {\n+\t\tif (bnge_fltr_match(f, fltr))\n+\t\t\treturn f;\n+\t}\n+\treturn NULL;\n+}\n+\n+int bnge_hwrm_set_vnic_filter(struct bnge_net *bn, u16 vnic_id, u16 idx,\n+\t\t\t const u8 *mac_addr)\n+{\n+\tstruct bnge_l2_filter *fltr;\n+\tstruct bnge_l2_key key;\n+\tint rc;\n+\n+\tether_addr_copy(key.dst_mac_addr, mac_addr);\n+\tkey.vlan = 0;\n+\tfltr = bnge_alloc_l2_filter(bn, \u0026key, GFP_KERNEL);\n+\tif (IS_ERR(fltr))\n+\t\treturn PTR_ERR(fltr);\n+\n+\tfltr-\u003ebase.fw_vnic_id = bn-\u003evnic_info[vnic_id].fw_vnic_id;\n+\trc = bnge_hwrm_l2_filter_alloc(bn-\u003ebd, fltr);\n+\tif (rc)\n+\t\tgoto err_del_l2_filter;\n+\tbn-\u003evnic_info[vnic_id].l2_filters[idx] = fltr;\n+\treturn rc;\n+\n+err_del_l2_filter:\n+\tbnge_del_l2_filter(bn, fltr);\n+\treturn rc;\n+}\n+\n+static void bnge_cfg_one_usr_fltr(struct bnge_net *bn,\n+\t\t\t\t struct bnge_filter_base *fltr)\n+{\n+\tstruct bnge_ntuple_filter *ntp_fltr;\n+\tstruct bnge_l2_filter *l2_fltr;\n+\n+\tif (list_empty(\u0026fltr-\u003elist_node))\n+\t\treturn;\n+\n+\tif (fltr-\u003etype == BNGE_FLTR_TYPE_NTUPLE) {\n+\t\tntp_fltr = container_of(fltr, struct bnge_ntuple_filter, base);\n+\t\tl2_fltr = bn-\u003evnic_info[BNGE_VNIC_DEFAULT].l2_filters[0];\n+\t\tntp_fltr-\u003el2_filter_id = l2_fltr-\u003ebase.filter_id;\n+\t\tif (bnge_hwrm_cfa_ntuple_filter_alloc(bn-\u003ebd, ntp_fltr)) {\n+\t\t\tnetdev_err(bn-\u003enetdev,\n+\t\t\t\t \"restoring previously configured ntuple filter id %d failed\\n\",\n+\t\t\t\t fltr-\u003esw_id);\n+\t\t\tbnge_del_ntp_filter(bn, ntp_fltr);\n+\t\t}\n+\t} else if (fltr-\u003etype == BNGE_FLTR_TYPE_L2) {\n+\t\tl2_fltr = container_of(fltr, struct bnge_l2_filter, base);\n+\t\tif (bnge_hwrm_l2_filter_alloc(bn-\u003ebd, l2_fltr)) {\n+\t\t\tnetdev_err(bn-\u003enetdev,\n+\t\t\t\t \"restoring previously configured l2 filter id %d failed\\n\",\n+\t\t\t\t fltr-\u003esw_id);\n+\t\t\tbnge_del_l2_filter(bn, l2_fltr);\n+\t\t}\n+\t}\n+}\n+\n+void bnge_cfg_usr_fltrs(struct bnge_net *bn)\n+{\n+\tstruct bnge_filter_base *usr_fltr, *tmp;\n+\n+\tlist_for_each_entry_safe(usr_fltr, tmp, \u0026bn-\u003eusr_fltr_list, list_node)\n+\t\tbnge_cfg_one_usr_fltr(bn, usr_fltr);\n+}\n+\n+void bnge_cfg_ntp_filters(struct bnge_net *bn)\n+{\n+#ifdef CONFIG_RFS_ACCEL\n+\tstruct bnge_ntuple_filter *fltr, *tmp;\n+\tLIST_HEAD(install_list);\n+\tLIST_HEAD(expire_list);\n+\tint i, rc;\n+\n+\trcu_read_lock();\n+\tfor (i = 0; i \u003c BNGE_NTP_FLTR_HASH_SIZE; i++) {\n+\t\thlist_for_each_entry_rcu(fltr, \u0026bn-\u003entp_fltr_hash_tbl[i],\n+\t\t\t\t\t base.hlist) {\n+\t\t\tif (test_bit(BNGE_FLTR_VALID, \u0026fltr-\u003ebase.state)) {\n+\t\t\t\tif (fltr-\u003ebase.flags \u0026 BNGE_ACT_NO_AGING)\n+\t\t\t\t\tcontinue;\n+\t\t\t\tif (rps_may_expire_flow(bn-\u003enetdev,\n+\t\t\t\t\t\t\tfltr-\u003ebase.rxq,\n+\t\t\t\t\t\t\tfltr-\u003eflow_id,\n+\t\t\t\t\t\t\tfltr-\u003ebase.sw_id))\n+\t\t\t\t\tlist_add(\u0026fltr-\u003ebase.list_node,\n+\t\t\t\t\t\t \u0026expire_list);\n+\t\t\t} else {\n+\t\t\t\tlist_add(\u0026fltr-\u003ebase.list_node, \u0026install_list);\n+\t\t\t}\n+\t\t}\n+\t}\n+\trcu_read_unlock();\n+\n+\tlist_for_each_entry_safe(fltr, tmp, \u0026install_list, base.list_node) {\n+\t\tlist_del_init(\u0026fltr-\u003ebase.list_node);\n+\t\trc = bnge_hwrm_cfa_ntuple_filter_alloc(bn-\u003ebd, fltr);\n+\t\tif (rc)\n+\t\t\tbnge_del_ntp_filter(bn, fltr);\n+\t\telse\n+\t\t\tset_bit(BNGE_FLTR_VALID, \u0026fltr-\u003ebase.state);\n+\t}\n+\n+\tlist_for_each_entry_safe(fltr, tmp, \u0026expire_list, base.list_node) {\n+\t\tlist_del_init(\u0026fltr-\u003ebase.list_node);\n+\t\tbnge_hwrm_cfa_ntuple_filter_free(bn-\u003ebd, fltr);\n+\t\tbnge_del_ntp_filter(bn, fltr);\n+\t}\n+#endif\n+}\ndiff --git a/drivers/net/ethernet/broadcom/bnge/bnge_filter.h b/drivers/net/ethernet/broadcom/bnge/bnge_filter.h\nnew file mode 100644\nindex 0000000000000..c2f2f754452c8\n--- /dev/null\n+++ b/drivers/net/ethernet/broadcom/bnge/bnge_filter.h\n@@ -0,0 +1,122 @@\n+/* SPDX-License-Identifier: GPL-2.0 */\n+/* Copyright (c) 2026 Broadcom */\n+\n+#ifndef _BNGE_FILTER_H_\n+#define _BNGE_FILTER_H_\n+\n+#include \u003clinux/jhash.h\u003e\n+\n+enum {\n+\tBNGE_FLTR_TYPE_L2\t= 1,\n+\tBNGE_FLTR_TYPE_NTUPLE\t= 2\n+};\n+\n+enum {\n+\tBNGE_FLTR_VALID,\n+\tBNGE_FLTR_FW_DELETED\n+};\n+\n+enum {\n+\tBNGE_ACT_DROP\t\t= 0x1,\n+\tBNGE_ACT_RING_DST\t= 0x2,\n+\tBNGE_ACT_NO_AGING\t= 0x4,\n+\tBNGE_ACT_RSS_CTX\t= 0x8\n+};\n+\n+#define BNGE_MAX_L2_FLTRS\t128\n+#define BNGE_MAX_NTUPLE_FLTRS\t(8 \u003c\u003c 10)\n+\n+struct bnge_filter_base {\n+\tstruct hlist_node\thlist;\n+\tstruct list_head\tlist_node;\n+\t__le64\t\t\tfilter_id;\n+\tu8\t\t\ttype;\n+\tu8\t\t\tflags;\n+\tu16\t\t\tsw_id;\n+\tu16\t\t\trxq;\n+\tu16\t\t\tfw_vnic_id;\n+\tu16\t\t\tvf_idx;\n+\tunsigned long\t\tstate;\n+\n+\tstruct rcu_head\t\trcu;\n+};\n+\n+struct bnge_l2_key {\n+\tunion {\n+\t\tstruct {\n+\t\t\tu8\tdst_mac_addr[ETH_ALEN];\n+\t\t\tu16\tvlan;\n+\t\t};\n+\t\tu32\tfilter_key;\n+\t};\n+};\n+\n+#define BNGE_L2_KEY_SIZE\t(sizeof(struct bnge_l2_key) / 4)\n+struct bnge_l2_filter {\n+\t/* base filter must be the first member */\n+\tstruct bnge_filter_base\tbase;\n+\tstruct bnge_l2_key\tl2_key;\n+};\n+\n+struct bnge_ipv4_tuple {\n+\tstruct flow_dissector_key_ipv4_addrs v4addrs;\n+\tstruct flow_dissector_key_ports ports;\n+};\n+\n+struct bnge_ipv6_tuple {\n+\tstruct flow_dissector_key_ipv6_addrs v6addrs;\n+\tstruct flow_dissector_key_ports ports;\n+};\n+\n+struct bnge_flow_masks {\n+\tstruct flow_dissector_key_ports ports;\n+\tstruct flow_dissector_key_addrs addrs;\n+};\n+\n+extern const struct bnge_flow_masks BNGE_FLOW_MASK_NONE;\n+extern const struct bnge_flow_masks BNGE_FLOW_IPV6_MASK_ALL;\n+extern const struct bnge_flow_masks BNGE_FLOW_IPV4_MASK_ALL;\n+\n+struct bnge_ntuple_filter {\n+\t/* base filter must be the first member */\n+\tstruct bnge_filter_base\tbase;\n+\tstruct flow_keys\tfkeys;\n+\tstruct bnge_flow_masks\tfmasks;\n+\t__le64\t\t\tl2_filter_id;\n+\tu32\t\t\tflow_id;\n+};\n+\n+#define BNGE_L2_FLTR_IS_NTP_MAC(fltr) ((fltr)-\u003ebase.flags)\n+\n+#define BNGE_FLTR_ID_INVALID\t0xffffffffffffffffULL\n+\n+void bnge_free_ntp_fltrs(struct bnge_net *bn, bool skip_user_filters);\n+u32 bnge_get_ntp_filter_idx(struct bnge_net *bn, struct flow_keys *fkeys,\n+\t\t\t const struct sk_buff *skb);\n+int bnge_insert_ntp_filter(struct bnge_net *bn, struct bnge_ntuple_filter *fltr,\n+\t\t\t u32 idx);\n+struct bnge_ntuple_filter *\n+bnge_lookup_ntp_filter_from_idx(struct bnge_net *bn,\n+\t\t\t\tstruct bnge_ntuple_filter *fltr, u32 idx);\n+void bnge_del_l2_filter(struct bnge_net *bn, struct bnge_l2_filter *fltr);\n+void bnge_del_l2_filter_rcu(struct bnge_net *bn, struct bnge_l2_filter *fltr);\n+struct bnge_l2_filter *bnge_lookup_l2_filter(struct bnge_net *bn,\n+\t\t\t\t\t struct bnge_l2_key *key,\n+\t\t\t\t\t u32 idx);\n+u64 bnge_lookup_l2_filter_rcu(struct bnge_net *bn,\n+\t\t\t struct bnge_l2_key *key,\n+\t\t\t u32 idx);\n+void bnge_free_l2_filters(struct bnge_net *bn);\n+int bnge_hwrm_set_vnic_filter(struct bnge_net *bn, u16 vnic_id, u16 idx,\n+\t\t\t const u8 *mac_addr);\n+struct bnge_l2_filter *bnge_alloc_user_l2_filter(struct bnge_net *bn,\n+\t\t\t\t\t\t struct bnge_l2_key *key,\n+\t\t\t\t\t\t u8 flags);\n+int bnge_fltrs_mem(struct bnge_net *bn);\n+void bnge_del_ntp_filter(struct bnge_net *bn,\n+\t\t\t struct bnge_ntuple_filter *nfltr);\n+void bnge_del_ntp_filter_rcu(struct bnge_net *bn,\n+\t\t\t struct bnge_ntuple_filter *fltr);\n+void bnge_cfg_usr_fltrs(struct bnge_net *bn);\n+void bnge_cfg_ntp_filters(struct bnge_net *bn);\n+#endif /* _BNGE_FILTER_H_ */\ndiff --git a/drivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c b/drivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c\nindex 651c5e783516c..6df1ad896610a 100644\n--- a/drivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c\n+++ b/drivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c\n@@ -8,6 +8,7 @@\n #include \u003clinux/bnge/hsi.h\u003e\n #include \u003clinux/if_vlan.h\u003e\n #include \u003cnet/netdev_queues.h\u003e\n+#include \u003clinux/ethtool.h\u003e\n \n #include \"bnge.h\"\n #include \"bnge_hwrm.h\"\n@@ -15,6 +16,8 @@\n #include \"bnge_rmem.h\"\n #include \"bnge_resc.h\"\n #include \"bnge_netdev.h\"\n+#include \"bnge_vnic.h\"\n+#include \"bnge_filter.h\"\n \n static const u16 bnge_async_events_arr[] = {\n \tASYNC_EVENT_CMPL_EVENT_ID_LINK_STATUS_CHANGE,\n@@ -674,6 +677,8 @@ int bnge_hwrm_vnic_qcaps(struct bnge_dev *bd)\n \t\t\tbd-\u003erss_cap |= BNGE_RSS_CAP_ESP_V4_RSS_CAP;\n \t\tif (flags \u0026 VNIC_QCAPS_RESP_FLAGS_RSS_IPSEC_ESP_SPI_IPV6_CAP)\n \t\t\tbd-\u003erss_cap |= BNGE_RSS_CAP_ESP_V6_RSS_CAP;\n+\t\tif (flags \u0026 VNIC_QCAPS_RESP_FLAGS_RSS_IPV6_FLOW_LABEL_CAP)\n+\t\t\tbd-\u003erss_cap |= BNGE_RSS_CAP_IPV6_FLOW_LABEL_RSS_CAP;\n \t}\n \tbnge_hwrm_req_drop(bd, req);\n \n@@ -901,15 +906,17 @@ int bnge_hwrm_l2_filter_alloc(struct bnge_dev *bd, struct bnge_l2_filter *fltr)\n {\n \tstruct hwrm_cfa_l2_filter_alloc_output *resp;\n \tstruct hwrm_cfa_l2_filter_alloc_input *req;\n+\tu32 flags;\n \tint rc;\n \n \trc = bnge_hwrm_req_init(bd, req, HWRM_CFA_L2_FILTER_ALLOC);\n \tif (rc)\n \t\treturn rc;\n \n-\treq-\u003eflags = cpu_to_le32(CFA_L2_FILTER_ALLOC_REQ_FLAGS_PATH_RX);\n-\n-\treq-\u003eflags |= cpu_to_le32(CFA_L2_FILTER_ALLOC_REQ_FLAGS_OUTERMOST);\n+\tflags = CFA_L2_FILTER_ALLOC_REQ_FLAGS_PATH_RX |\n+\t\tCFA_L2_FILTER_ALLOC_REQ_FLAGS_TRAFFIC_L2 |\n+\t\tCFA_L2_FILTER_ALLOC_REQ_FLAGS_OUTERMOST;\n+\treq-\u003eflags = cpu_to_le32(flags);\n \treq-\u003edst_id = cpu_to_le16(fltr-\u003ebase.fw_vnic_id);\n \treq-\u003eenables =\n \t\tcpu_to_le32(CFA_L2_FILTER_ALLOC_REQ_ENABLES_L2_ADDR |\n@@ -928,15 +935,155 @@ int bnge_hwrm_l2_filter_alloc(struct bnge_dev *bd, struct bnge_l2_filter *fltr)\n \t\treq-\u003el2_ivlan_mask = cpu_to_le16(0xfff);\n \t}\n \n+\tif (BNGE_L2_FLTR_IS_NTP_MAC(fltr)) {\n+\t\treq-\u003eenables |= cpu_to_le32(CFA_L2_FILTER_ALLOC_REQ_ENABLES_RFS_RING_TBL_IDX);\n+\t\treq-\u003erfs_ring_tbl_idx = cpu_to_le16(fltr-\u003ebase.rxq);\n+\t}\n+\n \tresp = bnge_hwrm_req_hold(bd, req);\n \trc = bnge_hwrm_req_send(bd, req);\n-\tif (!rc)\n+\tif (!rc) {\n \t\tfltr-\u003ebase.filter_id = resp-\u003el2_filter_id;\n+\t\tset_bit(BNGE_FLTR_VALID, \u0026fltr-\u003ebase.state);\n+\t}\n \n \tbnge_hwrm_req_drop(bd, req);\n \treturn rc;\n }\n \n+int bnge_hwrm_cfa_ntuple_filter_free(struct bnge_dev *bd,\n+\t\t\t\t struct bnge_ntuple_filter *fltr)\n+{\n+\tstruct hwrm_cfa_ntuple_filter_free_input *req;\n+\tint rc;\n+\n+\tset_bit(BNGE_FLTR_FW_DELETED, \u0026fltr-\u003ebase.state);\n+\tif (!test_bit(BNGE_STATE_OPEN, \u0026bd-\u003estate))\n+\t\treturn 0;\n+\n+\trc = bnge_hwrm_req_init(bd, req, HWRM_CFA_NTUPLE_FILTER_FREE);\n+\tif (rc)\n+\t\treturn rc;\n+\n+\treq-\u003entuple_filter_id = fltr-\u003ebase.filter_id;\n+\treturn bnge_hwrm_req_send(bd, req);\n+}\n+\n+#define BNGE_NTP_FLTR_FLAGS\t\t\t\t\t\\\n+\t(CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_L2_FILTER_ID |\t\\\n+\t CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_ETHERTYPE |\t\\\n+\t CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_IPADDR_TYPE |\t\\\n+\t CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_SRC_IPADDR |\t\\\n+\t CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_SRC_IPADDR_MASK |\t\\\n+\t CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_DST_IPADDR |\t\\\n+\t CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_DST_IPADDR_MASK |\t\\\n+\t CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_IP_PROTOCOL |\t\\\n+\t CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_SRC_PORT |\t\\\n+\t CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_SRC_PORT_MASK |\t\\\n+\t CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_DST_PORT |\t\\\n+\t CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_DST_PORT_MASK |\t\\\n+\t CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_DST_ID)\n+\n+#define BNGE_NTP_TUNNEL_FLTR_FLAG\t\t\t\t\\\n+\t\tCFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_TUNNEL_TYPE\n+\n+static void\n+bnge_cfg_rfs_ring_tbl_idx(struct bnge_dev *bd,\n+\t\t\t struct hwrm_cfa_ntuple_filter_alloc_input *req,\n+\t\t\t struct bnge_ntuple_filter *fltr)\n+{\n+\tstruct bnge_net *bn = netdev_priv(bd-\u003enetdev);\n+\tstruct bnge_vnic_info *def_vnic;\n+\tu32 rxq = fltr-\u003ebase.rxq;\n+\tu32 enables;\n+\n+\tif (fltr-\u003ebase.flags \u0026 BNGE_ACT_RSS_CTX) {\n+\t\tstruct ethtool_rxfh_context *ctx;\n+\t\tstruct bnge_rss_ctx *rss_ctx;\n+\t\tstruct bnge_vnic_info *vnic;\n+\n+\t\tctx = xa_load(\u0026bd-\u003enetdev-\u003eethtool-\u003erss_ctx,\n+\t\t\t fltr-\u003ebase.fw_vnic_id);\n+\t\tif (ctx) {\n+\t\t\trss_ctx = ethtool_rxfh_context_priv(ctx);\n+\t\t\tvnic = \u0026rss_ctx-\u003evnic;\n+\n+\t\t\treq-\u003edst_id = cpu_to_le16(vnic-\u003efw_vnic_id);\n+\t\t}\n+\t\treturn;\n+\t}\n+\n+\tdef_vnic = \u0026bn-\u003evnic_info[BNGE_VNIC_NTUPLE];\n+\treq-\u003edst_id = cpu_to_le16(def_vnic-\u003efw_vnic_id);\n+\tenables = CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_RFS_RING_TBL_IDX;\n+\treq-\u003eenables |= cpu_to_le32(enables);\n+\treq-\u003erfs_ring_tbl_idx = cpu_to_le32(rxq);\n+}\n+\n+int bnge_hwrm_cfa_ntuple_filter_alloc(struct bnge_dev *bd,\n+\t\t\t\t struct bnge_ntuple_filter *fltr)\n+{\n+\tstruct hwrm_cfa_ntuple_filter_alloc_output *resp;\n+\tstruct hwrm_cfa_ntuple_filter_alloc_input *req;\n+\tstruct bnge_flow_masks *masks = \u0026fltr-\u003efmasks;\n+\tstruct flow_keys *keys = \u0026fltr-\u003efkeys;\n+\tint rc;\n+\n+\trc = bnge_hwrm_req_init(bd, req, HWRM_CFA_NTUPLE_FILTER_ALLOC);\n+\tif (rc)\n+\t\treturn rc;\n+\n+\treq-\u003el2_filter_id = fltr-\u003el2_filter_id;\n+\n+\tif (fltr-\u003ebase.flags \u0026 BNGE_ACT_DROP)\n+\t\treq-\u003eflags =\n+\t\t\tcpu_to_le32(CFA_NTUPLE_FILTER_ALLOC_REQ_FLAGS_DROP);\n+\telse\n+\t\tbnge_cfg_rfs_ring_tbl_idx(bd, req, fltr);\n+\n+\treq-\u003eenables |= cpu_to_le32(BNGE_NTP_FLTR_FLAGS);\n+\n+\treq-\u003eethertype = htons(ETH_P_IP);\n+\treq-\u003eip_addr_type = CFA_NTUPLE_FILTER_ALLOC_REQ_IP_ADDR_TYPE_IPV4;\n+\treq-\u003eip_protocol = keys-\u003ebasic.ip_proto;\n+\n+\tif (keys-\u003ebasic.n_proto == htons(ETH_P_IPV6)) {\n+\t\treq-\u003eethertype = htons(ETH_P_IPV6);\n+\t\treq-\u003eip_addr_type =\n+\t\t\tCFA_NTUPLE_FILTER_ALLOC_REQ_IP_ADDR_TYPE_IPV6;\n+\t\tmemcpy(\u0026req-\u003esrc_ipaddr, \u0026keys-\u003eaddrs.v6addrs.src,\n+\t\t sizeof(struct in6_addr));\n+\t\tmemcpy(\u0026req-\u003esrc_ipaddr_mask, \u0026masks-\u003eaddrs.v6addrs.src,\n+\t\t sizeof(struct in6_addr));\n+\t\tmemcpy(\u0026req-\u003edst_ipaddr, \u0026keys-\u003eaddrs.v6addrs.dst,\n+\t\t sizeof(struct in6_addr));\n+\t\tmemcpy(\u0026req-\u003edst_ipaddr_mask, \u0026masks-\u003eaddrs.v6addrs.dst,\n+\t\t sizeof(struct in6_addr));\n+\t} else {\n+\t\treq-\u003esrc_ipaddr[0] = keys-\u003eaddrs.v4addrs.src;\n+\t\treq-\u003esrc_ipaddr_mask[0] = masks-\u003eaddrs.v4addrs.src;\n+\t\treq-\u003edst_ipaddr[0] = keys-\u003eaddrs.v4addrs.dst;\n+\t\treq-\u003edst_ipaddr_mask[0] = masks-\u003eaddrs.v4addrs.dst;\n+\t}\n+\tif (keys-\u003econtrol.flags \u0026 FLOW_DIS_ENCAPSULATION) {\n+\t\treq-\u003eenables |= cpu_to_le32(BNGE_NTP_TUNNEL_FLTR_FLAG);\n+\t\treq-\u003etunnel_type =\n+\t\t\tCFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_ANYTUNNEL;\n+\t}\n+\n+\treq-\u003esrc_port = keys-\u003eports.src;\n+\treq-\u003esrc_port_mask = masks-\u003eports.src;\n+\treq-\u003edst_port = keys-\u003eports.dst;\n+\treq-\u003edst_port_mask = masks-\u003eports.dst;\n+\n+\tresp = bnge_hwrm_req_hold(bd, req);\n+\trc = bnge_hwrm_req_send(bd, req);\n+\tif (!rc)\n+\t\tfltr-\u003ebase.filter_id = resp-\u003entuple_filter_id;\n+\tbnge_hwrm_req_drop(bd, req);\n+\treturn rc;\n+}\n+\n int bnge_hwrm_cfa_l2_set_rx_mask(struct bnge_dev *bd,\n \t\t\t\t struct bnge_vnic_info *vnic)\n {\n@@ -1634,3 +1781,30 @@ int bnge_hwrm_port_qstats(struct bnge_dev *bd, u8 flags)\n \n \treturn bnge_hwrm_req_send(bd, req);\n }\n+\n+int bnge_hwrm_cfa_adv_flow_mgnt_qcaps(struct bnge_dev *bd)\n+{\n+\tstruct hwrm_cfa_adv_flow_mgnt_qcaps_output *resp;\n+\tstruct hwrm_cfa_adv_flow_mgnt_qcaps_input *req;\n+\tu32 flags;\n+\tint rc;\n+\n+\trc = bnge_hwrm_req_init(bd, req, HWRM_CFA_ADV_FLOW_MGNT_QCAPS);\n+\tif (rc)\n+\t\treturn rc;\n+\n+\tresp = bnge_hwrm_req_hold(bd, req);\n+\trc = bnge_hwrm_req_send(bd, req);\n+\tif (rc)\n+\t\tgoto err_hwrm_drop;\n+\n+\tflags = le32_to_cpu(resp-\u003eflags);\n+\n+\tif (flags \u0026\n+\t CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_NTUPLE_FLOW_RX_EXT_IP_PROTO_SUPPORTED)\n+\t\tbd-\u003efw_cap |= BNGE_FW_CAP_CFA_NTUPLE_RX_EXT_IP_PROTO;\n+\n+err_hwrm_drop:\n+\tbnge_hwrm_req_drop(bd, req);\n+\treturn rc;\n+}\ndiff --git a/drivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.h b/drivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.h\nindex bf452e390d5bd..5c5bb864fc4da 100644\n--- a/drivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.h\n+++ b/drivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.h\n@@ -4,6 +4,8 @@\n #ifndef _BNGE_HWRM_LIB_H_\n #define _BNGE_HWRM_LIB_H_\n \n+struct bnge_l2_filter;\n+\n #define BNGE_PLC_EN_JUMBO_THRES_VALID\t\t\\\n \tVNIC_PLCMODES_CFG_REQ_ENABLES_JUMBO_THRESH_VALID\n #define BNGE_PLC_EN_HDS_THRES_VALID\t\t\\\n@@ -11,6 +13,8 @@\n #define BNGE_VNIC_CFG_ROCE_DUAL_MODE\t\t\\\n \tVNIC_CFG_REQ_FLAGS_ROCE_DUAL_VNIC_MODE\n \n+struct bnge_ntuple_filter;\n+\n int bnge_hwrm_ver_get(struct bnge_dev *bd);\n int bnge_hwrm_func_reset(struct bnge_dev *bd);\n int bnge_hwrm_fw_set_time(struct bnge_dev *bd);\n@@ -65,4 +69,10 @@ int bnge_hwrm_shutdown_link(struct bnge_dev *bd);\n int bnge_hwrm_port_qstats(struct bnge_dev *bd, u8 flags);\n int bnge_hwrm_port_qstats_ext(struct bnge_dev *bd, u8 flags);\n int bnge_hwrm_func_qstat_ext(struct bnge_dev *bd, struct bnge_stats_mem *stats);\n+int bnge_hwrm_cfa_adv_flow_mgnt_qcaps(struct bnge_dev *bd);\n+\n+int bnge_hwrm_cfa_ntuple_filter_alloc(struct bnge_dev *bd,\n+\t\t\t\t struct bnge_ntuple_filter *fltr);\n+int bnge_hwrm_cfa_ntuple_filter_free(struct bnge_dev *bd,\n+\t\t\t\t struct bnge_ntuple_filter *fltr);\n #endif /* _BNGE_HWRM_LIB_H_ */\ndiff --git a/drivers/net/ethernet/broadcom/bnge/bnge_netdev.c b/drivers/net/ethernet/broadcom/bnge/bnge_netdev.c\nindex a4288f0258f87..48de88924a7f3 100644\n--- a/drivers/net/ethernet/broadcom/bnge/bnge_netdev.c\n+++ b/drivers/net/ethernet/broadcom/bnge/bnge_netdev.c\n@@ -18,6 +18,7 @@\n #include \u003cnet/ip.h\u003e\n #include \u003clinux/skbuff.h\u003e\n #include \u003cnet/page_pool/helpers.h\u003e\n+#include \u003clinux/cpu_rmap.h\u003e\n \n #include \"bnge.h\"\n #include \"bnge_hwrm.h\"\n@@ -25,6 +26,8 @@\n #include \"bnge_ethtool.h\"\n #include \"bnge_rmem.h\"\n #include \"bnge_txrx.h\"\n+#include \"bnge_vnic.h\"\n+#include \"bnge_filter.h\"\n \n #define BNGE_RING_TO_TC_OFF(bd, tx)\t\\\n \t((tx) % (bd)-\u003etx_nr_rings_per_tc)\n@@ -301,6 +304,11 @@ static void bnge_timer(struct timer_list *t)\n \tif (BNGE_LINK_IS_UP(bd) \u0026\u0026 bn-\u003estats_coal_ticks)\n \t\tbnge_queue_sp_work(bn, BNGE_PERIODIC_STATS_SP_EVENT);\n \n+#ifdef CONFIG_RFS_ACCEL\n+\tif ((bn-\u003epriv_flags \u0026 BNGE_NET_EN_NTUPLE) \u0026\u0026 bn-\u003entp_fltr_count)\n+\t\tbnge_queue_sp_work(bn, BNGE_RX_NTP_FLTR_SP_EVENT);\n+#endif\n+\n \tmod_timer(\u0026bn-\u003etimer, jiffies + bn-\u003ecurrent_interval);\n }\n \n@@ -442,6 +450,9 @@ static void bnge_sp_task(struct work_struct *work)\n \t\t\tbnge_init_ethtool_link_settings(bn);\n \t}\n \n+\tif (test_and_clear_bit(BNGE_RX_NTP_FLTR_SP_EVENT, \u0026bn-\u003esp_event))\n+\t\tbnge_cfg_ntp_filters(bn);\n+\n \tnetdev_unlock(bn-\u003enetdev);\n }\n \n@@ -1147,12 +1158,10 @@ static int bnge_alloc_vnic_attributes(struct bnge_net *bn)\n \n static int bnge_alloc_vnics(struct bnge_net *bn)\n {\n-\tint num_vnics;\n+\tint num_vnics = 1;\n \n-\t/* Allocate only 1 VNIC for now\n-\t * Additional VNICs will be added based on RFS/NTUPLE in future patches\n-\t */\n-\tnum_vnics = 1;\n+\tif (bn-\u003epriv_flags \u0026 BNGE_NET_EN_NTUPLE)\n+\t\tnum_vnics++;\n \n \tbn-\u003evnic_info = kzalloc_objs(struct bnge_vnic_info, num_vnics);\n \tif (!bn-\u003evnic_info)\n@@ -1205,6 +1214,10 @@ static void bnge_free_core(struct bnge_net *bn)\n \tbnge_free_ring_stats(bn);\n \tbnge_free_ring_grps(bn);\n \tbnge_free_vnics(bn);\n+\n+\t/* Free non-user filters only */\n+\tbnge_free_ntp_fltrs(bn, true);\n+\n \tkfree(bn-\u003etx_ring_map);\n \tbn-\u003etx_ring_map = NULL;\n \tkfree(bn-\u003etx_ring);\n@@ -1293,6 +1306,10 @@ static int bnge_alloc_core(struct bnge_net *bn)\n \n \tbnge_init_stats(bn);\n \n+\trc = bnge_fltrs_mem(bn);\n+\tif (rc)\n+\t\tgoto err_free_core;\n+\n \trc = bnge_alloc_vnics(bn);\n \tif (rc)\n \t\tgoto err_free_core;\n@@ -1318,6 +1335,10 @@ static int bnge_alloc_core(struct bnge_net *bn)\n \tbn-\u003evnic_info[BNGE_VNIC_DEFAULT].flags |= BNGE_VNIC_RSS_FLAG |\n \t\t\t\t\t\t BNGE_VNIC_MCAST_FLAG |\n \t\t\t\t\t\t BNGE_VNIC_UCAST_FLAG;\n+\tif (bn-\u003epriv_flags \u0026 BNGE_NET_EN_NTUPLE)\n+\t\tbn-\u003evnic_info[BNGE_VNIC_NTUPLE].flags |= BNGE_VNIC_RSS_FLAG |\n+\t\t\t\t\t\t\t BNGE_VNIC_NTUPLE_FLAG;\n+\n \trc = bnge_alloc_vnic_attributes(bn);\n \tif (rc)\n \t\tgoto err_free_core;\n@@ -1977,174 +1998,6 @@ static int bnge_hwrm_ring_alloc(struct bnge_net *bn)\n \treturn rc;\n }\n \n-void bnge_fill_hw_rss_tbl(struct bnge_net *bn, struct bnge_vnic_info *vnic)\n-{\n-\t__le16 *ring_tbl = vnic-\u003erss_table;\n-\tstruct bnge_rx_ring_info *rxr;\n-\tstruct bnge_dev *bd = bn-\u003ebd;\n-\tu16 tbl_size, i;\n-\n-\ttbl_size = bnge_get_rxfh_indir_size(bd);\n-\n-\tfor (i = 0; i \u003c tbl_size; i++) {\n-\t\tu32 j;\n-\n-\t\tj = bd-\u003erss_indir_tbl[i];\n-\t\trxr = \u0026bn-\u003erx_ring[j];\n-\n-\t\t*ring_tbl++ = cpu_to_le16(rxr-\u003erx_ring_struct.fw_ring_id);\n-\t\t*ring_tbl++ = cpu_to_le16(bnge_cp_ring_for_rx(rxr));\n-\t}\n-}\n-\n-static int bnge_hwrm_vnic_rss_cfg(struct bnge_net *bn,\n-\t\t\t\t struct bnge_vnic_info *vnic)\n-{\n-\tint rc;\n-\n-\trc = bnge_hwrm_vnic_set_rss(bn, vnic, true);\n-\tif (rc) {\n-\t\tnetdev_err(bn-\u003enetdev, \"hwrm vnic %d set rss failure rc: %d\\n\",\n-\t\t\t vnic-\u003evnic_id, rc);\n-\t\treturn rc;\n-\t}\n-\trc = bnge_hwrm_vnic_cfg(bn, vnic);\n-\tif (rc)\n-\t\tnetdev_err(bn-\u003enetdev, \"hwrm vnic %d cfg failure rc: %d\\n\",\n-\t\t\t vnic-\u003evnic_id, rc);\n-\treturn rc;\n-}\n-\n-static int bnge_setup_vnic(struct bnge_net *bn, struct bnge_vnic_info *vnic)\n-{\n-\tstruct bnge_dev *bd = bn-\u003ebd;\n-\tint rc, i, nr_ctxs;\n-\n-\tnr_ctxs = bnge_cal_nr_rss_ctxs(bd-\u003erx_nr_rings);\n-\tfor (i = 0; i \u003c nr_ctxs; i++) {\n-\t\trc = bnge_hwrm_vnic_ctx_alloc(bd, vnic, i);\n-\t\tif (rc) {\n-\t\t\tnetdev_err(bn-\u003enetdev, \"hwrm vnic %d ctx %d alloc failure rc: %d\\n\",\n-\t\t\t\t vnic-\u003evnic_id, i, rc);\n-\t\t\treturn -ENOMEM;\n-\t\t}\n-\t\tbn-\u003ersscos_nr_ctxs++;\n-\t}\n-\n-\trc = bnge_hwrm_vnic_rss_cfg(bn, vnic);\n-\tif (rc)\n-\t\treturn rc;\n-\n-\tif (bnge_is_agg_reqd(bd)) {\n-\t\trc = bnge_hwrm_vnic_set_hds(bn, vnic);\n-\t\tif (rc)\n-\t\t\tnetdev_err(bn-\u003enetdev, \"hwrm vnic %d set hds failure rc: %d\\n\",\n-\t\t\t\t vnic-\u003evnic_id, rc);\n-\t}\n-\treturn rc;\n-}\n-\n-static void bnge_del_l2_filter(struct bnge_net *bn, struct bnge_l2_filter *fltr)\n-{\n-\tif (!refcount_dec_and_test(\u0026fltr-\u003erefcnt))\n-\t\treturn;\n-\thlist_del_rcu(\u0026fltr-\u003ebase.hash);\n-\tkfree_rcu(fltr, base.rcu);\n-}\n-\n-static void bnge_init_l2_filter(struct bnge_net *bn,\n-\t\t\t\tstruct bnge_l2_filter *fltr,\n-\t\t\t\tstruct bnge_l2_key *key, u32 idx)\n-{\n-\tstruct hlist_head *head;\n-\n-\tether_addr_copy(fltr-\u003el2_key.dst_mac_addr, key-\u003edst_mac_addr);\n-\tfltr-\u003el2_key.vlan = key-\u003evlan;\n-\tfltr-\u003ebase.type = BNGE_FLTR_TYPE_L2;\n-\n-\thead = \u0026bn-\u003el2_fltr_hash_tbl[idx];\n-\thlist_add_head_rcu(\u0026fltr-\u003ebase.hash, head);\n-\trefcount_set(\u0026fltr-\u003erefcnt, 1);\n-}\n-\n-static struct bnge_l2_filter *__bnge_lookup_l2_filter(struct bnge_net *bn,\n-\t\t\t\t\t\t struct bnge_l2_key *key,\n-\t\t\t\t\t\t u32 idx)\n-{\n-\tstruct bnge_l2_filter *fltr;\n-\tstruct hlist_head *head;\n-\n-\thead = \u0026bn-\u003el2_fltr_hash_tbl[idx];\n-\thlist_for_each_entry_rcu(fltr, head, base.hash) {\n-\t\tstruct bnge_l2_key *l2_key = \u0026fltr-\u003el2_key;\n-\n-\t\tif (ether_addr_equal(l2_key-\u003edst_mac_addr, key-\u003edst_mac_addr) \u0026\u0026\n-\t\t l2_key-\u003evlan == key-\u003evlan)\n-\t\t\treturn fltr;\n-\t}\n-\treturn NULL;\n-}\n-\n-static struct bnge_l2_filter *bnge_lookup_l2_filter(struct bnge_net *bn,\n-\t\t\t\t\t\t struct bnge_l2_key *key,\n-\t\t\t\t\t\t u32 idx)\n-{\n-\tstruct bnge_l2_filter *fltr;\n-\n-\trcu_read_lock();\n-\tfltr = __bnge_lookup_l2_filter(bn, key, idx);\n-\tif (fltr)\n-\t\trefcount_inc(\u0026fltr-\u003erefcnt);\n-\trcu_read_unlock();\n-\treturn fltr;\n-}\n-\n-static struct bnge_l2_filter *bnge_alloc_l2_filter(struct bnge_net *bn,\n-\t\t\t\t\t\t struct bnge_l2_key *key,\n-\t\t\t\t\t\t gfp_t gfp)\n-{\n-\tstruct bnge_l2_filter *fltr;\n-\tu32 idx;\n-\n-\tidx = jhash2(\u0026key-\u003efilter_key, BNGE_L2_KEY_SIZE, bn-\u003ehash_seed) \u0026\n-\t BNGE_L2_FLTR_HASH_MASK;\n-\tfltr = bnge_lookup_l2_filter(bn, key, idx);\n-\tif (fltr)\n-\t\treturn fltr;\n-\n-\tfltr = kzalloc_obj(*fltr, gfp);\n-\tif (!fltr)\n-\t\treturn ERR_PTR(-ENOMEM);\n-\n-\tbnge_init_l2_filter(bn, fltr, key, idx);\n-\treturn fltr;\n-}\n-\n-static int bnge_hwrm_set_vnic_filter(struct bnge_net *bn, u16 vnic_id, u16 idx,\n-\t\t\t\t const u8 *mac_addr)\n-{\n-\tstruct bnge_l2_filter *fltr;\n-\tstruct bnge_l2_key key;\n-\tint rc;\n-\n-\tether_addr_copy(key.dst_mac_addr, mac_addr);\n-\tkey.vlan = 0;\n-\tfltr = bnge_alloc_l2_filter(bn, \u0026key, GFP_KERNEL);\n-\tif (IS_ERR(fltr))\n-\t\treturn PTR_ERR(fltr);\n-\n-\tfltr-\u003ebase.fw_vnic_id = bn-\u003evnic_info[vnic_id].fw_vnic_id;\n-\trc = bnge_hwrm_l2_filter_alloc(bn-\u003ebd, fltr);\n-\tif (rc)\n-\t\tgoto err_del_l2_filter;\n-\tbn-\u003evnic_info[vnic_id].l2_filters[idx] = fltr;\n-\treturn rc;\n-\n-err_del_l2_filter:\n-\tbnge_del_l2_filter(bn, fltr);\n-\treturn rc;\n-}\n-\n static bool bnge_mc_list_updated(struct bnge_net *bn, u32 *rx_mask,\n \t\t\t\t const struct netdev_hw_addr_list *mc)\n {\n@@ -2580,10 +2433,23 @@ static int bnge_setup_interrupts(struct bnge_net *bn)\n {\n \tstruct net_device *dev = bn-\u003enetdev;\n \tstruct bnge_dev *bd = bn-\u003ebd;\n+\tint rc;\n \n \tbnge_setup_msix(bn);\n \n-\treturn netif_set_real_num_queues(dev, bd-\u003etx_nr_rings, bd-\u003erx_nr_rings);\n+\trc = netif_set_real_num_queues(dev, bd-\u003etx_nr_rings, bd-\u003erx_nr_rings);\n+\tif (rc)\n+\t\treturn rc;\n+\n+#ifdef CONFIG_RFS_ACCEL\n+\tif (bn-\u003epriv_flags \u0026 BNGE_NET_EN_NTUPLE) {\n+\t\tdev-\u003erx_cpu_rmap = alloc_irq_cpu_rmap(bd-\u003erx_nr_rings);\n+\t\tif (!dev-\u003erx_cpu_rmap)\n+\t\t\treturn -ENOMEM;\n+\t}\n+#endif\n+\n+\treturn rc;\n }\n \n static void bnge_hwrm_resource_free(struct bnge_net *bn, bool close_path)\n@@ -2599,6 +2465,11 @@ static void bnge_free_irq(struct bnge_net *bn)\n \tstruct bnge_irq *irq;\n \tint i;\n \n+#ifdef CONFIG_RFS_ACCEL\n+\tfree_irq_cpu_rmap(bn-\u003enetdev-\u003erx_cpu_rmap);\n+\tbn-\u003enetdev-\u003erx_cpu_rmap = NULL;\n+#endif\n+\n \tfor (i = 0; i \u003c bd-\u003enq_nr_rings; i++) {\n \t\tint map_idx = bnge_cp_num_to_irq_num(bn, i);\n \n@@ -2618,6 +2489,7 @@ static void bnge_free_irq(struct bnge_net *bn)\n \n static int bnge_request_irq(struct bnge_net *bn)\n {\n+\tstruct cpu_rmap *rmap = NULL;\n \tstruct bnge_dev *bd = bn-\u003ebd;\n \tint i, rc;\n \n@@ -2626,10 +2498,23 @@ static int bnge_request_irq(struct bnge_net *bn)\n \t\tnetdev_err(bn-\u003enetdev, \"bnge_setup_interrupts err: %d\\n\", rc);\n \t\treturn rc;\n \t}\n+\n+#ifdef CONFIG_RFS_ACCEL\n+\trmap = bn-\u003enetdev-\u003erx_cpu_rmap;\n+#endif\n+\n \tfor (i = 0; i \u003c bd-\u003enq_nr_rings; i++) {\n \t\tint map_idx = bnge_cp_num_to_irq_num(bn, i);\n \t\tstruct bnge_irq *irq = \u0026bd-\u003eirq_tbl[map_idx];\n \n+\t\tif (IS_ENABLED(CONFIG_RFS_ACCEL) \u0026\u0026\n+\t\t rmap \u0026\u0026 bn-\u003ebnapi[i]-\u003erx_ring) {\n+\t\t\trc = irq_cpu_rmap_add(rmap, irq-\u003evector);\n+\t\t\tif (rc)\n+\t\t\t\tnetdev_warn(bn-\u003enetdev,\n+\t\t\t\t\t \"failed adding irq rmap for ring %d\\n\", i);\n+\t\t}\n+\n \t\trc = request_irq(irq-\u003evector, irq-\u003ehandler, 0, irq-\u003ename,\n \t\t\t\t bn-\u003ebnapi[i]);\n \t\tif (rc)\n@@ -2712,6 +2597,12 @@ static int bnge_init_chip(struct bnge_net *bn)\n \tif (rc)\n \t\tgoto err_out;\n \n+\tif (bn-\u003epriv_flags \u0026 BNGE_NET_EN_NTUPLE) {\n+\t\trc = bnge_alloc_rfs_vnic(bn);\n+\t\tif (rc)\n+\t\t\tgoto err_out;\n+\t}\n+\n \tif (bd-\u003erss_cap \u0026 BNGE_RSS_CAP_RSS_HASH_TYPE_DELTA)\n \t\tbnge_hwrm_update_rss_hash_cfg(bn);\n \n@@ -2881,7 +2772,7 @@ static int bnge_hwrm_if_change(struct bnge_dev *bd, bool up)\n \treturn bnge_hwrm_req_send(bd, req);\n }\n \n-static int bnge_open_core(struct bnge_net *bn)\n+int bnge_open_core(struct bnge_net *bn)\n {\n \tstruct bnge_dev *bd = bn-\u003ebd;\n \tint rc;\n@@ -2943,6 +2834,9 @@ static int bnge_open_core(struct bnge_net *bn)\n \t/* Poll link status and check for SFP+ module status */\n \tbnge_get_port_module_status(bn);\n \n+\tbnge_hwrm_realloc_rss_ctx_vnic(bn);\n+\tbnge_cfg_usr_fltrs(bn);\n+\n \treturn 0;\n \n err_free_irq:\n@@ -3151,7 +3045,7 @@ static void bnge_save_ring_stats(struct bnge_net *bn)\n \t}\n }\n \n-static void bnge_close_core(struct bnge_net *bn)\n+void bnge_close_core(struct bnge_net *bn)\n {\n \tstruct bnge_dev *bd = bn-\u003ebd;\n \n@@ -3160,7 +3054,10 @@ static void bnge_close_core(struct bnge_net *bn)\n \tclear_bit(BNGE_STATE_OPEN, \u0026bd-\u003estate);\n \n \ttimer_delete_sync(\u0026bn-\u003etimer);\n+\n+\tbnge_clear_rss_ctxs(bn);\n \tbnge_shutdown_nic(bn);\n+\n \tbnge_disable_napi(bn);\n \n \t/* Save ring stats before shutdown */\n@@ -3267,6 +3164,96 @@ static const struct netdev_stat_ops bnge_stat_ops = {\n \t.get_base_stats\t\t= bnge_get_base_stats,\n };\n \n+#ifdef CONFIG_RFS_ACCEL\n+static u32 bnge_lookup_l2_filter_from_key(struct bnge_net *bn,\n+\t\t\t\t\t struct bnge_l2_key *key)\n+{\n+\tu32 idx;\n+\n+\tidx = jhash2(\u0026key-\u003efilter_key, BNGE_L2_KEY_SIZE, bn-\u003ehash_seed) \u0026\n+\t BNGE_L2_FLTR_HASH_MASK;\n+\treturn bnge_lookup_l2_filter_rcu(bn, key, idx);\n+}\n+\n+static int bnge_rx_flow_steer(struct net_device *dev, const struct sk_buff *skb,\n+\t\t\t u16 rxq_index, u32 flow_id)\n+{\n+\tstruct ethhdr *eth = (struct ethhdr *)skb_mac_header(skb);\n+\tstruct bnge_ntuple_filter *fltr, *new_fltr;\n+\tstruct bnge_net *bn = netdev_priv(dev);\n+\tu32 flags, idx, filter_id;\n+\tstruct flow_keys *fkeys;\n+\tint rc = 0;\n+\n+\tif (ether_addr_equal(dev-\u003edev_addr, eth-\u003eh_dest)) {\n+\t\tstruct bnge_l2_filter *l2_filter;\n+\n+\t\tl2_filter = bn-\u003evnic_info[BNGE_VNIC_DEFAULT].l2_filters[0];\n+\t\tfilter_id = l2_filter-\u003ebase.filter_id;\n+\n+\t} else {\n+\t\tstruct bnge_l2_key key;\n+\n+\t\tether_addr_copy(key.dst_mac_addr, eth-\u003eh_dest);\n+\t\tkey.vlan = 0;\n+\n+\t\tfilter_id = bnge_lookup_l2_filter_from_key(bn, \u0026key);\n+\t\tif (filter_id == BNGE_FLTR_ID_INVALID)\n+\t\t\treturn -EINVAL;\n+\t}\n+\tnew_fltr = kzalloc_obj(*new_fltr, GFP_ATOMIC);\n+\tif (!new_fltr)\n+\t\treturn -ENOMEM;\n+\n+\tfkeys = \u0026new_fltr-\u003efkeys;\n+\tif (!skb_flow_dissect_flow_keys(skb, fkeys, 0)) {\n+\t\trc = -EPROTONOSUPPORT;\n+\t\tgoto err_free;\n+\t}\n+\n+\tif ((fkeys-\u003ebasic.n_proto != htons(ETH_P_IP) \u0026\u0026\n+\t fkeys-\u003ebasic.n_proto != htons(ETH_P_IPV6)) ||\n+\t ((fkeys-\u003ebasic.ip_proto != IPPROTO_TCP) \u0026\u0026\n+\t (fkeys-\u003ebasic.ip_proto != IPPROTO_UDP))) {\n+\t\trc = -EPROTONOSUPPORT;\n+\t\tgoto err_free;\n+\t}\n+\tnew_fltr-\u003efmasks = BNGE_FLOW_IPV4_MASK_ALL;\n+\tif (fkeys-\u003ebasic.n_proto == htons(ETH_P_IPV6))\n+\t\tnew_fltr-\u003efmasks = BNGE_FLOW_IPV6_MASK_ALL;\n+\n+\tflags = fkeys-\u003econtrol.flags;\n+\tif (flags \u0026 FLOW_DIS_IS_FRAGMENT) {\n+\t\trc = -EPROTONOSUPPORT;\n+\t\tgoto err_free;\n+\t}\n+\n+\tnew_fltr-\u003el2_filter_id = filter_id;\n+\n+\tidx = bnge_get_ntp_filter_idx(bn, fkeys, skb);\n+\trcu_read_lock();\n+\tfltr = bnge_lookup_ntp_filter_from_idx(bn, new_fltr, idx);\n+\tif (fltr) {\n+\t\trc = fltr-\u003ebase.sw_id;\n+\t\trcu_read_unlock();\n+\t\tgoto err_free;\n+\t}\n+\trcu_read_unlock();\n+\n+\tnew_fltr-\u003eflow_id = flow_id;\n+\tnew_fltr-\u003ebase.rxq = rxq_index;\n+\trc = bnge_insert_ntp_filter(bn, new_fltr, idx);\n+\tif (!rc) {\n+\t\tbnge_queue_sp_work(bn, BNGE_RX_NTP_FLTR_SP_EVENT);\n+\t\treturn new_fltr-\u003ebase.sw_id;\n+\t}\n+\n+err_free:\n+\tkfree(new_fltr);\n+\treturn rc;\n+}\n+#endif\n+\n static const struct net_device_ops bnge_netdev_ops = {\n \t.ndo_open\t\t= bnge_open,\n \t.ndo_stop\t\t= bnge_close,\n@@ -3274,6 +3261,9 @@ static const struct net_device_ops bnge_netdev_ops = {\n \t.ndo_get_stats64\t= bnge_get_stats64,\n \t.ndo_set_rx_mode_async\t= bnge_set_rx_mode,\n \t.ndo_features_check\t= bnge_features_check,\n+#ifdef CONFIG_RFS_ACCEL\n+\t.ndo_rx_flow_steer\t= bnge_rx_flow_steer,\n+#endif\n };\n \n static void bnge_init_mac_addr(struct bnge_dev *bd)\n@@ -3399,6 +3389,17 @@ static void bnge_init_ring_params(struct bnge_net *bn)\n \tbn-\u003enetdev-\u003ecfg-\u003ehds_thresh = max(BNGE_DEFAULT_RX_COPYBREAK, rx_size);\n }\n \n+static void bnge_set_dflt_rfs(struct bnge_net *bn)\n+{\n+\tbn-\u003enetdev-\u003ehw_features |= NETIF_F_NTUPLE;\n+\tbn-\u003enetdev-\u003efeatures \u0026= ~NETIF_F_NTUPLE;\n+\tbn-\u003epriv_flags \u0026= ~BNGE_NET_EN_NTUPLE;\n+\tif (bnge_is_arfs_cap(bn-\u003ebd)) {\n+\t\tbn-\u003epriv_flags |= BNGE_NET_EN_NTUPLE;\n+\t\tbn-\u003enetdev-\u003efeatures |= NETIF_F_NTUPLE;\n+\t}\n+}\n+\n int bnge_netdev_alloc(struct bnge_dev *bd, int max_irqs)\n {\n \tstruct net_device *netdev;\n@@ -3505,6 +3506,8 @@ int bnge_netdev_alloc(struct bnge_dev *bd, int max_irqs)\n \tbnge_set_ring_params(bd);\n \n \tbnge_init_l2_fltr_tbl(bn);\n+\tbnge_set_dflt_rfs(bn);\n+\n \tbnge_init_mac_addr(bd);\n \n \trc = bnge_probe_phy(bn, true);\n@@ -3516,6 +3519,9 @@ int bnge_netdev_alloc(struct bnge_dev *bd, int max_irqs)\n \t\tgoto err_free_workq;\n \tspin_lock_init(\u0026bn-\u003estats_lock);\n \n+\tspin_lock_init(\u0026bn-\u003entp_fltr_lock);\n+\tINIT_LIST_HEAD(\u0026bn-\u003eusr_fltr_list);\n+\n \tnetdev-\u003erequest_ops_lock = true;\n \trc = register_netdev(netdev);\n \tif (rc) {\n@@ -3543,6 +3549,9 @@ void bnge_netdev_free(struct bnge_dev *bd)\n \n \tunregister_netdev(netdev);\n \n+\tbnge_free_ntp_fltrs(bn, false);\n+\tbnge_free_l2_filters(bn);\n+\n \ttimer_shutdown_sync(\u0026bn-\u003etimer);\n \tcancel_work_sync(\u0026bn-\u003esp_task);\n \tbn-\u003esp_event = 0;\ndiff --git a/drivers/net/ethernet/broadcom/bnge/bnge_netdev.h b/drivers/net/ethernet/broadcom/bnge/bnge_netdev.h\nindex 476b5bab96fed..3146023621a61 100644\n--- a/drivers/net/ethernet/broadcom/bnge/bnge_netdev.h\n+++ b/drivers/net/ethernet/broadcom/bnge/bnge_netdev.h\n@@ -152,6 +152,7 @@ enum {\n \tBNGE_NET_EN_GRO\t\t= BIT(0),\n \tBNGE_NET_EN_LRO\t\t= BIT(1),\n \tBNGE_NET_EN_JUMBO\t= BIT(2),\n+\tBNGE_NET_EN_NTUPLE\t= BIT(3),\n };\n \n #define BNGE_NET_EN_TPA\t\t(BNGE_NET_EN_GRO | BNGE_NET_EN_LRO)\n@@ -281,8 +282,12 @@ enum bnge_sp_event {\n \tBNGE_LINK_CFG_CHANGE_SP_EVENT,\n \tBNGE_UPDATE_PHY_SP_EVENT,\n \tBNGE_PERIODIC_STATS_SP_EVENT,\n+\tBNGE_RX_NTP_FLTR_SP_EVENT,\n };\n \n+#define BNGE_NTP_FLTR_HASH_SIZE\t512\n+#define BNGE_NTP_FLTR_HASH_MASK\t(BNGE_NTP_FLTR_HASH_SIZE - 1)\n+\n struct bnge_net {\n \tstruct bnge_dev\t\t*bd;\n \tstruct net_device\t*netdev;\n@@ -370,6 +375,18 @@ struct bnge_net {\n \n \tu8\t\t\tpri2cos_idx[8];\n \tbool\t\t\tpri2cos_valid;\n+\n+\tu16\t\t\tnum_rss_ctx;\n+\n+\tstruct list_head\tusr_fltr_list;\n+\tunsigned long\t\t*ntp_fltr_bmap;\n+\tunsigned long\t\t*l2_fltr_bmap;\n+\tint\t\t\tntp_fltr_count;\n+\tint\t\t\tl2_fltr_count;\n+\tint\t\t\tuser_fltr_count;\n+\n+\tstruct hlist_head\tntp_fltr_hash_tbl[BNGE_NTP_FLTR_HASH_SIZE];\n+\tspinlock_t\t\tntp_fltr_lock;\t/* for hash table add, del */\n };\n \n #define BNGE_DEFAULT_RX_RING_SIZE\t511\n@@ -558,8 +575,6 @@ struct bnge_napi {\n };\n \n #define INVALID_STATS_CTX_ID\t-1\n-#define BNGE_VNIC_DEFAULT\t0\n-#define BNGE_MAX_UC_ADDRS\t4\n \n #define BNGE_RX_MASK_CFG_FLAGS\t\t\t\t\\\n \t(CFA_L2_SET_RX_MASK_REQ_MASK_PROMISCUOUS |\t\\\n@@ -567,78 +582,8 @@ struct bnge_napi {\n \t CFA_L2_SET_RX_MASK_REQ_MASK_ALL_MCAST |\t\\\n \t CFA_L2_SET_RX_MASK_REQ_MASK_BCAST)\n \n-struct bnge_vnic_info {\n-\tu16\t\tfw_vnic_id;\n-#define BNGE_MAX_CTX_PER_VNIC\t8\n-\tu16\t\tfw_rss_cos_lb_ctx[BNGE_MAX_CTX_PER_VNIC];\n-\tu16\t\tmru;\n-\t/* index 0 always dev_addr */\n-\tstruct bnge_l2_filter *l2_filters[BNGE_MAX_UC_ADDRS];\n-\tu16\t\tuc_filter_count;\n-\tu8\t\t*uc_list;\n-\tdma_addr_t\trss_table_dma_addr;\n-\t__le16\t\t*rss_table;\n-\tdma_addr_t\trss_hash_key_dma_addr;\n-\tu64\t\t*rss_hash_key;\n-\tint\t\trss_table_size;\n-#define BNGE_RSS_TABLE_ENTRIES\t\t64\n-#define BNGE_RSS_TABLE_SIZE\t\t(BNGE_RSS_TABLE_ENTRIES * 4)\n-#define BNGE_RSS_TABLE_MAX_TBL\t\t8\n-#define BNGE_MAX_RSS_TABLE_SIZE\t\t\t\\\n-\t(BNGE_RSS_TABLE_SIZE * BNGE_RSS_TABLE_MAX_TBL)\n-\tu32\t\trx_mask;\n-\n-\tu8\t\t*mc_list;\n-\tint\t\tmc_list_size;\n-\tint\t\tmc_list_count;\n-\tdma_addr_t\tmc_list_mapping;\n-#define BNGE_MAX_MC_ADDRS\t16\n-\n-\tu32\t\tflags;\n-#define BNGE_VNIC_RSS_FLAG\t1\n-#define BNGE_VNIC_MCAST_FLAG\t4\n-#define BNGE_VNIC_UCAST_FLAG\t8\n-\tu32\t\tvnic_id;\n-};\n-\n-struct bnge_filter_base {\n-\tstruct hlist_node\thash;\n-\tstruct list_head\tlist;\n-\t__le64\t\t\tfilter_id;\n-\tu8\t\t\ttype;\n-#define BNGE_FLTR_TYPE_L2\t2\n-\tu8\t\t\tflags;\n-\tu16\t\t\trxq;\n-\tu16\t\t\tfw_vnic_id;\n-\tu16\t\t\tvf_idx;\n-\tunsigned long\t\tstate;\n-#define BNGE_FLTR_VALID\t\t0\n-#define BNGE_FLTR_FW_DELETED\t2\n-\n-\tstruct rcu_head rcu;\n-};\n-\n-struct bnge_l2_key {\n-\tunion {\n-\t\tstruct {\n-\t\t\tu8\tdst_mac_addr[ETH_ALEN];\n-\t\t\tu16\tvlan;\n-\t\t};\n-\t\tu32\tfilter_key;\n-\t};\n-};\n-\n-#define BNGE_L2_KEY_SIZE\t(sizeof(struct bnge_l2_key) / 4)\n-struct bnge_l2_filter {\n-\t/* base filter must be the first member */\n-\tstruct bnge_filter_base\tbase;\n-\tstruct bnge_l2_key\tl2_key;\n-\trefcount_t\t\trefcnt;\n-};\n-\n u32 bnge_cp_ring_for_rx(struct bnge_rx_ring_info *rxr);\n u32 bnge_cp_ring_for_tx(struct bnge_tx_ring_info *txr);\n-void bnge_fill_hw_rss_tbl(struct bnge_net *bn, struct bnge_vnic_info *vnic);\n int bnge_alloc_rx_data(struct bnge_net *bn, struct bnge_rx_ring_info *rxr,\n \t\t u16 prod, gfp_t gfp);\n u16 bnge_find_next_agg_idx(struct bnge_rx_ring_info *rxr, u16 idx);\n@@ -648,4 +593,6 @@ int bnge_alloc_rx_netmem(struct bnge_net *bn, struct bnge_rx_ring_info *rxr,\n \t\t\t u16 prod, gfp_t gfp);\n void __bnge_queue_sp_work(struct bnge_net *bn);\n void bnge_copy_hw_masks(u64 *mask_arr, __le64 *hw_mask_arr, int count);\n+int bnge_open_core(struct bnge_net *bn);\n+void bnge_close_core(struct bnge_net *bn);\n #endif /* _BNGE_NETDEV_H_ */\ndiff --git a/drivers/net/ethernet/broadcom/bnge/bnge_resc.c b/drivers/net/ethernet/broadcom/bnge/bnge_resc.c\nindex 4711dd4945ffe..d2e21f76909c5 100644\n--- a/drivers/net/ethernet/broadcom/bnge/bnge_resc.c\n+++ b/drivers/net/ethernet/broadcom/bnge/bnge_resc.c\n@@ -11,6 +11,8 @@\n #include \"bnge_hwrm.h\"\n #include \"bnge_hwrm_lib.h\"\n #include \"bnge_resc.h\"\n+#include \"bnge_vnic.h\"\n+#include \"bnge_filter.h\"\n \n static u16 bnge_num_tx_to_cp(struct bnge_dev *bd, u16 tx)\n {\n@@ -177,6 +179,16 @@ static int bnge_adjust_rings(struct bnge_dev *bd, u16 *rx,\n \treturn bnge_fix_rings_count(rx, tx, max_nq, sh);\n }\n \n+static unsigned int bnge_get_max_func_rss_ctxs(struct bnge_dev *bd)\n+{\n+\treturn bd-\u003ehw_resc.max_rsscos_ctxs;\n+}\n+\n+static unsigned int bnge_get_max_func_vnics(struct bnge_dev *bd)\n+{\n+\treturn bd-\u003ehw_resc.max_vnics;\n+}\n+\n int bnge_cal_nr_rss_ctxs(u16 rx_rings)\n {\n \tif (!rx_rings)\n@@ -186,14 +198,27 @@ int bnge_cal_nr_rss_ctxs(u16 rx_rings)\n \t\t\t\t BNGE_RSS_TABLE_ENTRIES);\n }\n \n-static u16 bnge_rss_ctxs_in_use(struct bnge_dev *bd,\n-\t\t\t\tstruct bnge_hw_rings *hwr)\n+static u16 bnge_get_total_rss_ctxs(struct bnge_dev *bd,\n+\t\t\t\t struct bnge_hw_rings *hwr)\n {\n-\treturn bnge_cal_nr_rss_ctxs(hwr-\u003egrp);\n+\tu16 rss_ctx = bnge_cal_nr_rss_ctxs(hwr-\u003egrp);\n+\n+\trss_ctx *= hwr-\u003evnic;\n+\n+\treturn rss_ctx;\n }\n \n-static u16 bnge_get_total_vnics(struct bnge_dev *bd, u16 rx_rings)\n+static u16 bnge_get_total_vnics(struct bnge_dev *bd)\n {\n+\tif (bd-\u003enetdev) {\n+\t\tstruct bnge_net *bn = netdev_priv(bd-\u003enetdev);\n+\n+\t\tif (bn-\u003epriv_flags \u0026 BNGE_NET_EN_NTUPLE)\n+\t\t\treturn 2 + bn-\u003enum_rss_ctx;\n+\t} else if (bnge_is_arfs_cap(bd)) {\n+\t\treturn 2;\n+\t}\n+\n \treturn 1;\n }\n \n@@ -203,24 +228,6 @@ u32 bnge_get_rxfh_indir_size(struct bnge_dev *bd)\n \t BNGE_RSS_TABLE_ENTRIES;\n }\n \n-static void bnge_set_dflt_rss_indir_tbl(struct bnge_dev *bd)\n-{\n-\tu16 max_entries, pad;\n-\tu32 *rss_indir_tbl;\n-\tint i;\n-\n-\tmax_entries = bnge_get_rxfh_indir_size(bd);\n-\trss_indir_tbl = \u0026bd-\u003erss_indir_tbl[0];\n-\n-\tfor (i = 0; i \u003c max_entries; i++)\n-\t\trss_indir_tbl[i] = ethtool_rxfh_indir_default(i,\n-\t\t\t\t\t\t\t bd-\u003erx_nr_rings);\n-\n-\tpad = bd-\u003erss_indir_tbl_entries - max_entries;\n-\tif (pad)\n-\t\tmemset(\u0026rss_indir_tbl[i], 0, pad * sizeof(*rss_indir_tbl));\n-}\n-\n static void bnge_copy_reserved_rings(struct bnge_dev *bd,\n \t\t\t\t struct bnge_hw_rings *hwr)\n {\n@@ -253,7 +260,7 @@ static bool bnge_need_reserve_rings(struct bnge_dev *bd)\n \tif (hw_resc-\u003eresv_tx_rings != bd-\u003etx_nr_rings)\n \t\treturn true;\n \n-\tvnic = bnge_get_total_vnics(bd, rx);\n+\tvnic = bnge_get_total_vnics(bd);\n \n \tif (bnge_is_agg_reqd(bd))\n \t\trx \u003c\u003c= 1;\n@@ -299,12 +306,12 @@ int bnge_reserve_rings(struct bnge_dev *bd)\n \t\tsh = true;\n \thwr.cmpl = hwr.rx + hwr.tx;\n \n-\thwr.vnic = bnge_get_total_vnics(bd, hwr.rx);\n+\thwr.vnic = bnge_get_total_vnics(bd);\n \n \tif (bnge_is_agg_reqd(bd))\n \t\thwr.rx \u003c\u003c= 1;\n \thwr.grp = bd-\u003erx_nr_rings;\n-\thwr.rss_ctx = bnge_rss_ctxs_in_use(bd, \u0026hwr);\n+\thwr.rss_ctx = bnge_get_total_rss_ctxs(bd, \u0026hwr);\n \thwr.stat = bnge_func_stat_ctxs_demand(bd);\n \told_rx_rings = bd-\u003ehw_resc.resv_rx_rings;\n \n@@ -347,7 +354,7 @@ int bnge_reserve_rings(struct bnge_dev *bd)\n \t\treturn -ENOMEM;\n \n \tif (old_rx_rings != bd-\u003ehw_resc.resv_rx_rings)\n-\t\tbnge_set_dflt_rss_indir_tbl(bd);\n+\t\tbnge_set_dflt_rss_indir_tbl(bd, NULL);\n \n \tif (!bnge_aux_registered(bd)) {\n \t\tu16 resv_msix, resv_ctx, aux_ctxs;\n@@ -580,6 +587,47 @@ static int bnge_alloc_rss_indir_tbl(struct bnge_dev *bd)\n \treturn 0;\n }\n \n+/* If runtime conditions support RFS */\n+bool bnge_arfs_capable(struct bnge_dev *bd, bool new_rss_ctx)\n+{\n+\tstruct bnge_hw_rings hwr = {};\n+\tint max_vnics, max_rss_ctxs;\n+\n+\thwr.grp = bd-\u003erx_nr_rings;\n+\thwr.vnic = bnge_get_total_vnics(bd);\n+\tif (new_rss_ctx)\n+\t\thwr.vnic++;\n+\thwr.rss_ctx = bnge_get_total_rss_ctxs(bd, \u0026hwr);\n+\tmax_vnics = bnge_get_max_func_vnics(bd);\n+\tmax_rss_ctxs = bnge_get_max_func_rss_ctxs(bd);\n+\n+\tif (hwr.vnic \u003e max_vnics || hwr.rss_ctx \u003e max_rss_ctxs) {\n+\t\tif (bd-\u003erx_nr_rings \u003e 1)\n+\t\t\tdev_warn(bd-\u003edev,\n+\t\t\t\t \"Not enough resources to support NTUPLE filters\\n\");\n+\t\treturn false;\n+\t}\n+\n+\t/* Do not reduce VNIC and RSS ctx reservations. There is a FW\n+\t * issue that will mess up the default VNIC if we reduce the\n+\t * reservations.\n+\t */\n+\tif (hwr.vnic \u003c= bd-\u003ehw_resc.resv_vnics \u0026\u0026\n+\t hwr.rss_ctx \u003c= bd-\u003ehw_resc.resv_rsscos_ctxs)\n+\t\treturn true;\n+\n+\tbnge_hwrm_reserve_rings(bd, \u0026hwr);\n+\tif (hwr.vnic \u003c= bd-\u003ehw_resc.resv_vnics \u0026\u0026\n+\t hwr.rss_ctx \u003c= bd-\u003ehw_resc.resv_rsscos_ctxs)\n+\t\treturn true;\n+\n+\tdev_warn(bd-\u003edev, \"Unable to reserve resources to support NTUPLE filters\\n\");\n+\thwr.vnic = 1;\n+\thwr.rss_ctx = 0;\n+\tbnge_hwrm_reserve_rings(bd, \u0026hwr);\n+\treturn false;\n+}\n+\n int bnge_net_init_dflt_config(struct bnge_dev *bd)\n {\n \tstruct bnge_hw_resc *hw_resc;\n@@ -589,13 +637,15 @@ int bnge_net_init_dflt_config(struct bnge_dev *bd)\n \tif (rc)\n \t\treturn rc;\n \n+\tif (bnge_arfs_capable(bd, false))\n+\t\tbd-\u003eflags |= BNGE_EN_ARFS_CAP;\n+\n \trc = bnge_net_init_dflt_rings(bd, true);\n \tif (rc)\n \t\tgoto err_free_tbl;\n \n \thw_resc = \u0026bd-\u003ehw_resc;\n-\tbd-\u003emax_fltr = hw_resc-\u003emax_rx_em_flows + hw_resc-\u003emax_rx_wm_flows +\n-\t\t BNGE_L2_FLTR_MAX_FLTR;\n+\tbd-\u003emax_fltr = BNGE_MAX_NTUPLE_FLTRS;\n \n \treturn 0;\n \ndiff --git a/drivers/net/ethernet/broadcom/bnge/bnge_resc.h b/drivers/net/ethernet/broadcom/bnge/bnge_resc.h\nindex b62a634669f62..1e55fbe6985b1 100644\n--- a/drivers/net/ethernet/broadcom/bnge/bnge_resc.h\n+++ b/drivers/net/ethernet/broadcom/bnge/bnge_resc.h\n@@ -75,6 +75,7 @@ void bnge_aux_init_dflt_config(struct bnge_dev *bd);\n u32 bnge_get_rxfh_indir_size(struct bnge_dev *bd);\n int bnge_cal_nr_rss_ctxs(u16 rx_rings);\n bool bnge_aux_has_enough_resources(struct bnge_dev *bd);\n+bool bnge_arfs_capable(struct bnge_dev *bd, bool new_rss_ctx);\n \n static inline u32\n bnge_adjust_pow_two(u32 total_ent, u16 ent_per_blk)\ndiff --git a/drivers/net/ethernet/broadcom/bnge/bnge_vnic.c b/drivers/net/ethernet/broadcom/bnge/bnge_vnic.c\nnew file mode 100644\nindex 0000000000000..d738233f0bc9b\n--- /dev/null\n+++ b/drivers/net/ethernet/broadcom/bnge/bnge_vnic.c\n@@ -0,0 +1,272 @@\n+// SPDX-License-Identifier: GPL-2.0\n+// Copyright (c) 2026 Broadcom.\n+\n+#include \u003clinux/kernel.h\u003e\n+#include \u003clinux/dma-mapping.h\u003e\n+#include \u003clinux/ethtool.h\u003e\n+\n+#include \"bnge.h\"\n+#include \"bnge_netdev.h\"\n+#include \"bnge_vnic.h\"\n+#include \"bnge_hwrm_lib.h\"\n+#include \"bnge_filter.h\"\n+#include \"bnge_ethtool.h\"\n+\n+void bnge_set_dflt_rss_indir_tbl(struct bnge_dev *bd,\n+\t\t\t\t struct ethtool_rxfh_context *rss_ctx)\n+{\n+\tu16 max_entries, pad;\n+\tu32 *rss_indir_tbl;\n+\tu16 i;\n+\n+\tmax_entries = bnge_get_rxfh_indir_size(bd);\n+\n+\tif (rss_ctx)\n+\t\trss_indir_tbl = ethtool_rxfh_context_indir(rss_ctx);\n+\telse\n+\t\trss_indir_tbl = \u0026bd-\u003erss_indir_tbl[0];\n+\n+\tfor (i = 0; i \u003c max_entries; i++)\n+\t\trss_indir_tbl[i] = ethtool_rxfh_indir_default(i,\n+\t\t\t\t\t\t\t bd-\u003erx_nr_rings);\n+\n+\tpad = bd-\u003erss_indir_tbl_entries - max_entries;\n+\tif (pad)\n+\t\tmemset(\u0026rss_indir_tbl[i], 0, pad * sizeof(*rss_indir_tbl));\n+}\n+\n+void bnge_fill_hw_rss_tbl(struct bnge_net *bn, struct bnge_vnic_info *vnic)\n+{\n+\t__le16 *ring_tbl = vnic-\u003erss_table;\n+\tstruct bnge_rx_ring_info *rxr;\n+\tstruct bnge_dev *bd = bn-\u003ebd;\n+\tu16 tbl_size, i;\n+\n+\ttbl_size = bnge_get_rxfh_indir_size(bd);\n+\n+\tfor (i = 0; i \u003c tbl_size; i++) {\n+\t\tu16 ring_id, j;\n+\n+\t\tif (vnic-\u003eflags \u0026 BNGE_VNIC_NTUPLE_FLAG)\n+\t\t\tj = ethtool_rxfh_indir_default(i, bd-\u003erx_nr_rings);\n+\t\telse if (vnic-\u003eflags \u0026 BNGE_VNIC_RSSCTX_FLAG)\n+\t\t\tj = ethtool_rxfh_context_indir(vnic-\u003erss_ctx)[i];\n+\t\telse\n+\t\t\tj = bd-\u003erss_indir_tbl[i];\n+\n+\t\trxr = \u0026bn-\u003erx_ring[j];\n+\n+\t\tring_id = rxr-\u003erx_ring_struct.fw_ring_id;\n+\t\t*ring_tbl++ = cpu_to_le16(ring_id);\n+\t\tring_id = bnge_cp_ring_for_rx(rxr);\n+\t\t*ring_tbl++ = cpu_to_le16(ring_id);\n+\t}\n+}\n+\n+int bnge_hwrm_vnic_rss_cfg(struct bnge_net *bn,\n+\t\t\t struct bnge_vnic_info *vnic)\n+{\n+\tint rc;\n+\n+\trc = bnge_hwrm_vnic_set_rss(bn, vnic, true);\n+\tif (rc) {\n+\t\tnetdev_err(bn-\u003enetdev, \"hwrm vnic %d set rss failure rc: %d\\n\",\n+\t\t\t vnic-\u003evnic_id, rc);\n+\t\treturn rc;\n+\t}\n+\trc = bnge_hwrm_vnic_cfg(bn, vnic);\n+\tif (rc)\n+\t\tnetdev_err(bn-\u003enetdev, \"hwrm vnic %d cfg failure rc: %d\\n\",\n+\t\t\t vnic-\u003evnic_id, rc);\n+\treturn rc;\n+}\n+\n+int bnge_setup_vnic(struct bnge_net *bn, struct bnge_vnic_info *vnic)\n+{\n+\tstruct bnge_dev *bd = bn-\u003ebd;\n+\tint rc, i, nr_ctxs;\n+\n+\tnr_ctxs = bnge_cal_nr_rss_ctxs(bd-\u003erx_nr_rings);\n+\tfor (i = 0; i \u003c nr_ctxs; i++) {\n+\t\trc = bnge_hwrm_vnic_ctx_alloc(bd, vnic, i);\n+\t\tif (rc) {\n+\t\t\tnetdev_err(bn-\u003enetdev, \"hwrm vnic %d ctx %d alloc failure rc: %d\\n\",\n+\t\t\t\t vnic-\u003evnic_id, i, rc);\n+\t\t\treturn -ENOMEM;\n+\t\t}\n+\t\tbn-\u003ersscos_nr_ctxs++;\n+\t}\n+\n+\trc = bnge_hwrm_vnic_rss_cfg(bn, vnic);\n+\tif (rc)\n+\t\treturn rc;\n+\n+\tif (bnge_is_agg_reqd(bd)) {\n+\t\trc = bnge_hwrm_vnic_set_hds(bn, vnic);\n+\t\tif (rc)\n+\t\t\tnetdev_err(bn-\u003enetdev, \"hwrm vnic %d set hds failure rc: %d\\n\",\n+\t\t\t\t vnic-\u003evnic_id, rc);\n+\t}\n+\treturn rc;\n+}\n+\n+static int bnge_alloc_and_setup_vnic(struct bnge_net *bn,\n+\t\t\t\t struct bnge_vnic_info *vnic,\n+\t\t\t\t u16 rx_rings)\n+{\n+\tint rc;\n+\n+\trc = bnge_hwrm_vnic_alloc(bn-\u003ebd, vnic, rx_rings);\n+\tif (rc) {\n+\t\tnetdev_err(bn-\u003enetdev, \"hwrm vnic %u alloc failure rc: %d\\n\",\n+\t\t\t vnic-\u003evnic_id, rc);\n+\t\treturn rc;\n+\t}\n+\treturn bnge_setup_vnic(bn, vnic);\n+}\n+\n+int bnge_alloc_rfs_vnic(struct bnge_net *bn)\n+{\n+\tstruct bnge_vnic_info *vnic;\n+\n+\tvnic = \u0026bn-\u003evnic_info[BNGE_VNIC_NTUPLE];\n+\treturn bnge_alloc_and_setup_vnic(bn, vnic, bn-\u003ebd-\u003erx_nr_rings);\n+}\n+\n+void bnge_modify_rss(struct bnge_net *bn, struct ethtool_rxfh_context *ctx,\n+\t\t struct bnge_rss_ctx *rss_ctx,\n+\t\t const struct ethtool_rxfh_param *rxfh)\n+{\n+\tstruct bnge_dev *bd = bn-\u003ebd;\n+\n+\tif (rxfh-\u003ekey) {\n+\t\tif (rss_ctx) {\n+\t\t\tmemcpy(rss_ctx-\u003evnic.rss_hash_key, rxfh-\u003ekey,\n+\t\t\t HW_HASH_KEY_SIZE);\n+\t\t} else {\n+\t\t\tmemcpy(bn-\u003erss_hash_key, rxfh-\u003ekey, HW_HASH_KEY_SIZE);\n+\t\t\tbn-\u003erss_hash_key_updated = true;\n+\t\t}\n+\t}\n+\n+\tif (rxfh-\u003eindir) {\n+\t\tu32 i, pad, tbl_size = bnge_get_rxfh_indir_size(bd);\n+\t\tu32 *indir_tbl = bd-\u003erss_indir_tbl;\n+\n+\t\tif (rss_ctx)\n+\t\t\tindir_tbl = ethtool_rxfh_context_indir(ctx);\n+\t\tfor (i = 0; i \u003c tbl_size; i++)\n+\t\t\tindir_tbl[i] = rxfh-\u003eindir[i];\n+\t\tpad = bd-\u003erss_indir_tbl_entries - tbl_size;\n+\t\tif (pad)\n+\t\t\tmemset(\u0026indir_tbl[i], 0, pad * sizeof(*indir_tbl));\n+\t}\n+}\n+\n+void bnge_del_one_rss_ctx(struct bnge_net *bn, struct bnge_rss_ctx *rss_ctx,\n+\t\t\t bool all)\n+{\n+\tstruct bnge_vnic_info *vnic = \u0026rss_ctx-\u003evnic;\n+\tstruct bnge_filter_base *usr_fltr, *tmp;\n+\tstruct bnge_ntuple_filter *ntp_fltr;\n+\tint i;\n+\n+\tbnge_hwrm_vnic_free_one(bn-\u003ebd, \u0026rss_ctx-\u003evnic);\n+\tfor (i = 0; i \u003c BNGE_MAX_CTX_PER_VNIC; i++) {\n+\t\tif (vnic-\u003efw_rss_cos_lb_ctx[i] != INVALID_HW_RING_ID)\n+\t\t\tbnge_hwrm_vnic_ctx_free_one(bn-\u003ebd, vnic, i);\n+\t}\n+\n+\tif (!all)\n+\t\treturn;\n+\n+\t/* Delete all filters associated with this user RSS context */\n+\tlist_for_each_entry_safe(usr_fltr, tmp, \u0026bn-\u003eusr_fltr_list, list_node) {\n+\t\tif ((usr_fltr-\u003eflags \u0026 BNGE_ACT_RSS_CTX) \u0026\u0026\n+\t\t usr_fltr-\u003efw_vnic_id == rss_ctx-\u003eindex) {\n+\t\t\tntp_fltr = container_of(usr_fltr,\n+\t\t\t\t\t\tstruct bnge_ntuple_filter,\n+\t\t\t\t\t\tbase);\n+\t\t\tbnge_hwrm_cfa_ntuple_filter_free(bn-\u003ebd, ntp_fltr);\n+\t\t\tbnge_del_ntp_filter(bn, ntp_fltr);\n+\t\t}\n+\t}\n+\n+\tif (vnic-\u003erss_table)\n+\t\tdma_free_coherent(bn-\u003ebd-\u003edev, vnic-\u003erss_table_size,\n+\t\t\t\t vnic-\u003erss_table,\n+\t\t\t\t vnic-\u003erss_table_dma_addr);\n+\tbn-\u003enum_rss_ctx--;\n+}\n+\n+void bnge_hwrm_realloc_rss_ctx_vnic(struct bnge_net *bn)\n+{\n+\tbool set_tpa = !!(bn-\u003epriv_flags \u0026 BNGE_NET_EN_TPA);\n+\tstruct ethtool_rxfh_context *ctx;\n+\tunsigned long context;\n+\n+\txa_for_each(\u0026bn-\u003enetdev-\u003eethtool-\u003erss_ctx, context, ctx) {\n+\t\tstruct bnge_rss_ctx *rss_ctx = ethtool_rxfh_context_priv(ctx);\n+\t\tstruct bnge_vnic_info *vnic = \u0026rss_ctx-\u003evnic;\n+\n+\t\tif (bnge_hwrm_vnic_alloc(bn-\u003ebd, vnic, bn-\u003ebd-\u003erx_nr_rings) ||\n+\t\t bnge_hwrm_vnic_set_tpa(bn-\u003ebd, vnic, set_tpa) ||\n+\t\t bnge_setup_vnic(bn, vnic)) {\n+\t\t\tnetdev_err(bn-\u003enetdev, \"Failed to restore RSS ctx %d\\n\",\n+\t\t\t\t rss_ctx-\u003eindex);\n+\t\t\tbnge_del_one_rss_ctx(bn, rss_ctx, true);\n+\t\t\tethtool_rxfh_context_lost(bn-\u003enetdev, rss_ctx-\u003eindex);\n+\t\t}\n+\t}\n+}\n+\n+void bnge_clear_rss_ctxs(struct bnge_net *bn)\n+{\n+\tstruct ethtool_rxfh_context *ctx;\n+\tunsigned long context;\n+\n+\txa_for_each(\u0026bn-\u003enetdev-\u003eethtool-\u003erss_ctx, context, ctx) {\n+\t\tstruct bnge_rss_ctx *rss_ctx = ethtool_rxfh_context_priv(ctx);\n+\n+\t\tbnge_del_one_rss_ctx(bn, rss_ctx, false);\n+\t}\n+}\n+\n+struct bnge_rss_ctx *bnge_get_rss_ctx_from_index(struct bnge_net *bn, u32 idx)\n+{\n+\tstruct ethtool_rxfh_context *ctx;\n+\n+\tctx = xa_load(\u0026bn-\u003enetdev-\u003eethtool-\u003erss_ctx, idx);\n+\tif (!ctx)\n+\t\treturn NULL;\n+\treturn ethtool_rxfh_context_priv(ctx);\n+}\n+\n+int bnge_alloc_vnic_rss_table(struct bnge_net *bn,\n+\t\t\t struct bnge_vnic_info *vnic)\n+{\n+\tunsigned int size = L1_CACHE_ALIGN(BNGE_MAX_RSS_TABLE_SIZE);\n+\n+\tvnic-\u003erss_table_size = size + HW_HASH_KEY_SIZE;\n+\tvnic-\u003erss_table = dma_alloc_coherent(bn-\u003ebd-\u003edev,\n+\t\t\t\t\t vnic-\u003erss_table_size,\n+\t\t\t\t\t \u0026vnic-\u003erss_table_dma_addr,\n+\t\t\t\t\t GFP_KERNEL);\n+\tif (!vnic-\u003erss_table)\n+\t\treturn -ENOMEM;\n+\n+\tvnic-\u003erss_hash_key = ((void *)vnic-\u003erss_table) + size;\n+\tvnic-\u003erss_hash_key_dma_addr = vnic-\u003erss_table_dma_addr + size;\n+\treturn 0;\n+}\n+\n+void bnge_init_vnic_mem(struct bnge_vnic_info *vnic)\n+{\n+\tint i;\n+\n+\tvnic-\u003efw_vnic_id = INVALID_HW_RING_ID;\n+\tvnic-\u003evnic_id = BNGE_VNIC_ID_INVALID;\n+\n+\tfor (i = 0; i \u003c BNGE_MAX_CTX_PER_VNIC; i++)\n+\t\tvnic-\u003efw_rss_cos_lb_ctx[i] = INVALID_HW_RING_ID;\n+}\ndiff --git a/drivers/net/ethernet/broadcom/bnge/bnge_vnic.h b/drivers/net/ethernet/broadcom/bnge/bnge_vnic.h\nnew file mode 100644\nindex 0000000000000..3954c450315de\n--- /dev/null\n+++ b/drivers/net/ethernet/broadcom/bnge/bnge_vnic.h\n@@ -0,0 +1,91 @@\n+/* SPDX-License-Identifier: GPL-2.0 */\n+/* Copyright (c) 2026 Broadcom */\n+\n+#ifndef _BNGE_VNIC_H_\n+#define _BNGE_VNIC_H_\n+\n+struct bnge_net;\n+struct bnge_l2_filter;\n+\n+#define BNGE_RSS_TABLE_ENTRIES\t\t64\n+#define BNGE_RSS_TABLE_SIZE\t\t(BNGE_RSS_TABLE_ENTRIES * 4)\n+#define BNGE_RSS_TABLE_MAX_TBL\t\t8\n+#define BNGE_MAX_RSS_TABLE_SIZE\t\t\t\\\n+\t(BNGE_RSS_TABLE_SIZE * BNGE_RSS_TABLE_MAX_TBL)\n+#define BNGE_MAX_RSS_TABLE_ENTRIES\t\t\t\t\\\n+\t(BNGE_RSS_TABLE_ENTRIES * BNGE_RSS_TABLE_MAX_TBL)\n+\n+#define BNGE_MAX_ETH_RSS_CTX\t32\n+#define BNGE_MAX_CTX_PER_VNIC\t8\n+\n+#define BNGE_MAX_MC_ADDRS\t16\n+#define BNGE_MAX_UC_ADDRS\t4\n+\n+#define BNGE_VNIC_ID_INVALID\t0xffffffff\n+\n+struct ethtool_rxfh_context;\n+struct ethtool_rxfh_param;\n+\n+enum {\n+\tBNGE_VNIC_DEFAULT\t= 0,\n+\tBNGE_VNIC_NTUPLE\t= 1\n+};\n+\n+enum {\n+\tBNGE_VNIC_RSS_FLAG\t= BIT(0),\n+\tBNGE_VNIC_MCAST_FLAG\t= BIT(1),\n+\tBNGE_VNIC_UCAST_FLAG\t= BIT(2),\n+\tBNGE_VNIC_NTUPLE_FLAG\t= BIT(3),\n+\tBNGE_VNIC_RSSCTX_FLAG\t= BIT(4)\n+};\n+\n+struct bnge_vnic_info {\n+\tu16\t\tfw_vnic_id;\n+\tu16\t\tfw_rss_cos_lb_ctx[BNGE_MAX_CTX_PER_VNIC];\n+\tu16\t\tmru;\n+\t/* index 0 always dev_addr */\n+\tstruct bnge_l2_filter *l2_filters[BNGE_MAX_UC_ADDRS];\n+\tu16\t\tuc_filter_count;\n+\tu8\t\t*uc_list;\n+\tdma_addr_t\trss_table_dma_addr;\n+\t__le16\t\t*rss_table;\n+\tdma_addr_t\trss_hash_key_dma_addr;\n+\tu64\t\t*rss_hash_key;\n+\tint\t\trss_table_size;\n+\tu32\t\trx_mask;\n+\n+\tu8\t\t*mc_list;\n+\tint\t\tmc_list_size;\n+\tint\t\tmc_list_count;\n+\tdma_addr_t\tmc_list_mapping;\n+\n+\tu32\t\tflags;\n+\tu32\t\tvnic_id;\n+\n+\tstruct ethtool_rxfh_context\t*rss_ctx;\n+};\n+\n+struct bnge_rss_ctx {\n+\tstruct bnge_vnic_info vnic;\n+\tu32 index;\n+};\n+\n+void bnge_fill_hw_rss_tbl(struct bnge_net *bn, struct bnge_vnic_info *vnic);\n+int bnge_hwrm_vnic_rss_cfg(struct bnge_net *bn,\n+\t\t\t struct bnge_vnic_info *vnic);\n+int bnge_setup_vnic(struct bnge_net *bn, struct bnge_vnic_info *vnic);\n+void bnge_set_dflt_rss_indir_tbl(struct bnge_dev *bd,\n+\t\t\t\t struct ethtool_rxfh_context *ctx);\n+int bnge_alloc_rfs_vnic(struct bnge_net *bn);\n+int bnge_alloc_vnic_rss_table(struct bnge_net *bn,\n+\t\t\t struct bnge_vnic_info *vnic);\n+struct bnge_rss_ctx *bnge_get_rss_ctx_from_index(struct bnge_net *bn, u32 idx);\n+void bnge_modify_rss(struct bnge_net *bn, struct ethtool_rxfh_context *ctx,\n+\t\t struct bnge_rss_ctx *rss_ctx,\n+\t\t const struct ethtool_rxfh_param *rxfh);\n+void bnge_del_one_rss_ctx(struct bnge_net *bn, struct bnge_rss_ctx *rss_ctx,\n+\t\t\t bool all);\n+void bnge_hwrm_realloc_rss_ctx_vnic(struct bnge_net *bn);\n+void bnge_clear_rss_ctxs(struct bnge_net *bn);\n+void bnge_init_vnic_mem(struct bnge_vnic_info *vnic);\n+#endif /* _BNGE_VNIC_H_ */\ndiff --git a/include/linux/bnge/hsi.h b/include/linux/bnge/hsi.h\nindex 1f7bd96415a52..d4fe662d60b10 100644\n--- a/include/linux/bnge/hsi.h\n+++ b/include/linux/bnge/hsi.h\n@@ -8607,6 +8607,7 @@ struct hwrm_cfa_l2_filter_alloc_input {\n \t#define CFA_L2_FILTER_ALLOC_REQ_ENABLES_MIRROR_VNIC_ID 0x10000UL\n \t#define CFA_L2_FILTER_ALLOC_REQ_ENABLES_NUM_VLANS 0x20000UL\n \t#define CFA_L2_FILTER_ALLOC_REQ_ENABLES_T_NUM_VLANS 0x40000UL\n+\t#define CFA_L2_FILTER_ALLOC_REQ_ENABLES_RFS_RING_TBL_IDX 0x80000UL\n \tu8\tl2_addr[6];\n \tu8\tnum_vlans;\n \tu8\tt_num_vlans;\n@@ -8663,7 +8664,8 @@ struct hwrm_cfa_l2_filter_alloc_input {\n \t#define CFA_L2_FILTER_ALLOC_REQ_PRI_HINT_MIN 0x4UL\n \t#define CFA_L2_FILTER_ALLOC_REQ_PRI_HINT_LAST CFA_L2_FILTER_ALLOC_REQ_PRI_HINT_MIN\n \tu8\tunused_5;\n-\t__le32\tunused_6;\n+\t__le16\trfs_ring_tbl_idx;\n+\t__le16\tunused_6;\n \t__le64\tl2_filter_id_hint;\n };\n \n@@ -8900,6 +8902,95 @@ struct hwrm_cfa_tunnel_filter_free_output {\n \tu8\tvalid;\n };\n \n+/* hwrm_cfa_ntuple_filter_alloc_input (size:1024b/128B) */\n+struct hwrm_cfa_ntuple_filter_alloc_input {\n+\t__le16\treq_type;\n+\t__le16\tcmpl_ring;\n+\t__le16\tseq_id;\n+\t__le16\ttarget_id;\n+\t__le64\tresp_addr;\n+\t__le32\tflags;\n+\t#define CFA_NTUPLE_FILTER_ALLOC_REQ_FLAGS_LOOPBACK 0x1UL\n+\t#define CFA_NTUPLE_FILTER_ALLOC_REQ_FLAGS_DROP 0x2UL\n+\t#define CFA_NTUPLE_FILTER_ALLOC_REQ_FLAGS_METER 0x4UL\n+\t#define CFA_NTUPLE_FILTER_ALLOC_REQ_FLAGS_DEST_FID 0x8UL\n+\t#define CFA_NTUPLE_FILTER_ALLOC_REQ_FLAGS_ARP_REPLY 0x10UL\n+\t#define CFA_NTUPLE_FILTER_ALLOC_REQ_FLAGS_DEST_RFS_RING_IDX 0x20UL\n+\t#define CFA_NTUPLE_FILTER_ALLOC_REQ_FLAGS_NO_L2_CONTEXT 0x40UL\n+\t__le32\tenables;\n+\t#define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_L2_FILTER_ID 0x1UL\n+\t#define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_ETHERTYPE 0x2UL\n+\t#define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_TUNNEL_TYPE 0x4UL\n+\t#define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_SRC_MACADDR 0x8UL\n+\t#define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_IPADDR_TYPE 0x10UL\n+\t#define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_SRC_IPADDR 0x20UL\n+\t#define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_SRC_IPADDR_MASK 0x40UL\n+\t#define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_DST_IPADDR 0x80UL\n+\t#define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_DST_IPADDR_MASK 0x100UL\n+\t#define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_IP_PROTOCOL 0x200UL\n+\t#define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_SRC_PORT 0x400UL\n+\t#define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_SRC_PORT_MASK 0x800UL\n+\t#define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_DST_PORT 0x1000UL\n+\t#define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_DST_PORT_MASK 0x2000UL\n+\t#define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_PRI_HINT 0x4000UL\n+\t#define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_NTUPLE_FILTER_ID 0x8000UL\n+\t#define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_DST_ID 0x10000UL\n+\t#define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_MIRROR_VNIC_ID 0x20000UL\n+\t#define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_DST_MACADDR 0x40000UL\n+\t#define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_RFS_RING_TBL_IDX 0x80000UL\n+\t__le64\tl2_filter_id;\n+\tu8\tsrc_macaddr[6];\n+\t__be16\tethertype;\n+\tu8\tip_addr_type;\n+\t#define CFA_NTUPLE_FILTER_ALLOC_REQ_IP_ADDR_TYPE_UNKNOWN 0x0UL\n+\t#define CFA_NTUPLE_FILTER_ALLOC_REQ_IP_ADDR_TYPE_IPV4 0x4UL\n+\t#define CFA_NTUPLE_FILTER_ALLOC_REQ_IP_ADDR_TYPE_IPV6 0x6UL\n+\t#define CFA_NTUPLE_FILTER_ALLOC_REQ_IP_ADDR_TYPE_LAST CFA_NTUPLE_FILTER_ALLOC_REQ_IP_ADDR_TYPE_IPV6\n+\tu8\tip_protocol;\n+\t#define CFA_NTUPLE_FILTER_ALLOC_REQ_IP_PROTOCOL_UNKNOWN 0x0UL\n+\t#define CFA_NTUPLE_FILTER_ALLOC_REQ_IP_PROTOCOL_TCP 0x6UL\n+\t#define CFA_NTUPLE_FILTER_ALLOC_REQ_IP_PROTOCOL_UDP 0x11UL\n+\t#define CFA_NTUPLE_FILTER_ALLOC_REQ_IP_PROTOCOL_ICMP 0x1UL\n+\t#define CFA_NTUPLE_FILTER_ALLOC_REQ_IP_PROTOCOL_ICMPV6 0x3aUL\n+\t#define CFA_NTUPLE_FILTER_ALLOC_REQ_IP_PROTOCOL_RSVD 0xffUL\n+\t#define CFA_NTUPLE_FILTER_ALLOC_REQ_IP_PROTOCOL_LAST CFA_NTUPLE_FILTER_ALLOC_REQ_IP_PROTOCOL_RSVD\n+\t__le16\tdst_id;\n+\t__le16\trfs_ring_tbl_idx;\n+\tu8\ttunnel_type;\n+\t#define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_NONTUNNEL 0x0UL\n+\t#define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_VXLAN 0x1UL\n+\t#define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_NVGRE 0x2UL\n+\t#define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_L2GRE 0x3UL\n+\t#define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_IPIP 0x4UL\n+\t#define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_GENEVE 0x5UL\n+\t#define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_MPLS 0x6UL\n+\t#define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_STT 0x7UL\n+\t#define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_IPGRE 0x8UL\n+\t#define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_VXLAN_V4 0x9UL\n+\t#define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_IPGRE_V1 0xaUL\n+\t#define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_L2_ETYPE 0xbUL\n+\t#define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_VXLAN_GPE_V6 0xcUL\n+\t#define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_VXLAN_GPE 0x10UL\n+\t#define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_ANYTUNNEL 0xffUL\n+\t#define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_LAST CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_ANYTUNNEL\n+\tu8\tpri_hint;\n+\t#define CFA_NTUPLE_FILTER_ALLOC_REQ_PRI_HINT_NO_PREFER 0x0UL\n+\t#define CFA_NTUPLE_FILTER_ALLOC_REQ_PRI_HINT_ABOVE 0x1UL\n+\t#define CFA_NTUPLE_FILTER_ALLOC_REQ_PRI_HINT_BELOW 0x2UL\n+\t#define CFA_NTUPLE_FILTER_ALLOC_REQ_PRI_HINT_HIGHEST 0x3UL\n+\t#define CFA_NTUPLE_FILTER_ALLOC_REQ_PRI_HINT_LOWEST 0x4UL\n+\t#define CFA_NTUPLE_FILTER_ALLOC_REQ_PRI_HINT_LAST CFA_NTUPLE_FILTER_ALLOC_REQ_PRI_HINT_LOWEST\n+\t__be32\tsrc_ipaddr[4];\n+\t__be32\tsrc_ipaddr_mask[4];\n+\t__be32\tdst_ipaddr[4];\n+\t__be32\tdst_ipaddr_mask[4];\n+\t__be16\tsrc_port;\n+\t__be16\tsrc_port_mask;\n+\t__be16\tdst_port;\n+\t__be16\tdst_port_mask;\n+\t__le64\tntuple_filter_id_hint;\n+};\n+\n /* hwrm_cfa_ntuple_filter_alloc_output (size:192b/24B) */\n struct hwrm_cfa_ntuple_filter_alloc_output {\n \t__le16\terror_code;\n@@ -9014,6 +9105,49 @@ struct hwrm_cfa_ntuple_filter_cfg_output {\n \tu8\tvalid;\n };\n \n+/* hwrm_cfa_adv_flow_mgnt_qcaps_input (size:256b/32B) */\n+struct hwrm_cfa_adv_flow_mgnt_qcaps_input {\n+\t__le16\treq_type;\n+\t__le16\tcmpl_ring;\n+\t__le16\tseq_id;\n+\t__le16\ttarget_id;\n+\t__le64\tresp_addr;\n+\t__le32\tunused_0[4];\n+};\n+\n+/* hwrm_cfa_adv_flow_mgnt_qcaps_output (size:128b/16B) */\n+struct hwrm_cfa_adv_flow_mgnt_qcaps_output {\n+\t__le16\terror_code;\n+\t__le16\treq_type;\n+\t__le16\tseq_id;\n+\t__le16\tresp_len;\n+\t__le32\tflags;\n+\t#define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_FLOW_HND_16BIT_SUPPORTED 0x1UL\n+\t#define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_FLOW_HND_64BIT_SUPPORTED 0x2UL\n+\t#define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_FLOW_BATCH_DELETE_SUPPORTED 0x4UL\n+\t#define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_FLOW_RESET_ALL_SUPPORTED 0x8UL\n+\t#define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_NTUPLE_FLOW_DEST_FUNC_SUPPORTED 0x10UL\n+\t#define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_TX_EEM_FLOW_SUPPORTED 0x20UL\n+\t#define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_RX_EEM_FLOW_SUPPORTED 0x40UL\n+\t#define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_FLOW_COUNTER_ALLOC_SUPPORTED 0x80UL\n+\t#define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_RFS_RING_TBL_IDX_SUPPORTED 0x100UL\n+\t#define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_UNTAGGED_VLAN_SUPPORTED 0x200UL\n+\t#define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_XDP_SUPPORTED 0x400UL\n+\t#define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_L2_HEADER_SOURCE_FIELDS_SUPPORTED 0x800UL\n+\t#define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_NTUPLE_FLOW_RX_ARP_SUPPORTED 0x1000UL\n+\t#define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_RFS_RING_TBL_IDX_V2_SUPPORTED 0x2000UL\n+\t#define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_NTUPLE_FLOW_RX_ETHERTYPE_IP_SUPPORTED 0x4000UL\n+\t#define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_TRUFLOW_CAPABLE 0x8000UL\n+\t#define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_L2_FILTER_TRAFFIC_TYPE_L2_ROCE_SUPPORTED 0x10000UL\n+\t#define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_LAG_SUPPORTED 0x20000UL\n+\t#define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_NTUPLE_FLOW_NO_L2CTX_SUPPORTED 0x40000UL\n+\t#define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_NIC_FLOW_STATS_SUPPORTED 0x80000UL\n+\t#define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_NTUPLE_FLOW_RX_EXT_IP_PROTO_SUPPORTED 0x100000UL\n+\t#define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_RFS_RING_TBL_IDX_V3_SUPPORTED 0x200000UL\n+\tu8\tunused_0[3];\n+\tu8\tvalid;\n+};\n+\n /* hwrm_tunnel_dst_port_alloc_input (size:192b/24B) */\n struct hwrm_tunnel_dst_port_alloc_input {\n \t__le16\treq_type;\n"
} |
| 2/1 | 2026/08/14 02:03 | agent | patch-evaluator |
1mModel:best-expensive Args: null Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"Reasoning": "The patch only modifies the `bnge` driver (Broadcom ThorUltra Ethernet), which is a vendor-specific PCIe driver for physical hardware not emulated in standard QEMU. Therefore, the modified code paths are unreachable in virtualized fuzzing environments.",
"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). - Code in vendor-specific PCIe switch, SmartNIC, or GPU drivers (e.g., mlxsw, pds_core, qed, ionic, amdgpu) that require physical PCIe hardware cards not emulated in standard QEMU. - Driver .remove, .shutdown, or pci_unregister_driver teardown callbacks (e.g., igb_remove) that are executed only during PCI hot-unplug or sysfs driver unbind operations. 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. Prefer non-static, non-inlined API entrypoint functions over internal static helper functions (which are inlined by the compiler and do not have distinct symbol addresses). 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. Also check "depends on" lines in Kconfig to include any non-standard parent subsystem configs needed for Kbuild to compile the code statically into vmlinux. 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 79b03b7bc098c397fe73ab4f82b23e18093f040a
Author: syz-cluster <triage@syzkaller.com>
Date: Fri Aug 14 02:03:40 2026 +0000
syz-cluster: applied patch under review
diff --git a/drivers/net/ethernet/broadcom/bnge/Makefile b/drivers/net/ethernet/broadcom/bnge/Makefile
index 8e07cb307d214..d0a3cb24dc1d3 100644
--- a/drivers/net/ethernet/broadcom/bnge/Makefile
+++ b/drivers/net/ethernet/broadcom/bnge/Makefile
@@ -12,4 +12,6 @@ bng_en-y := bnge_core.o \
bnge_ethtool.o \
bnge_auxr.o \
bnge_txrx.o \
- bnge_link.o
+ bnge_link.o \
+ bnge_vnic.o \
+ bnge_filter.o
diff --git a/drivers/net/ethernet/broadcom/bnge/bnge.h b/drivers/net/ethernet/broadcom/bnge/bnge.h
index 4479ccd071f53..5a1a9186c2cbf 100644
--- a/drivers/net/ethernet/broadcom/bnge/bnge.h
+++ b/drivers/net/ethernet/broadcom/bnge/bnge.h
@@ -74,6 +74,7 @@ enum {
BNGE_EN_STRIP_VLAN = BIT_ULL(2),
BNGE_EN_SHARED_CHNL = BIT_ULL(3),
BNGE_EN_UDP_GSO_SUPP = BIT_ULL(4),
+ BNGE_EN_ARFS_CAP = BIT_ULL(5),
};
#define BNGE_EN_ROCE (BNGE_EN_ROCE_V1 | BNGE_EN_ROCE_V2)
@@ -87,6 +88,7 @@ enum {
BNGE_RSS_CAP_AH_V6_RSS_CAP = BIT(5),
BNGE_RSS_CAP_ESP_V4_RSS_CAP = BIT(6),
BNGE_RSS_CAP_ESP_V6_RSS_CAP = BIT(7),
+ BNGE_RSS_CAP_IPV6_FLOW_LABEL_RSS_CAP = BIT(8),
};
#define BNGE_MAX_QUEUE 8
@@ -160,16 +162,8 @@ struct bnge_dev {
u16 tso_max_segs;
int max_fltr;
-#define BNGE_L2_FLTR_MAX_FLTR 1024
u32 *rss_indir_tbl;
-#define BNGE_RSS_TABLE_ENTRIES 64
-#define BNGE_RSS_TABLE_SIZE (BNGE_RSS_TABLE_ENTRIES * 4)
-#define BNGE_RSS_TABLE_MAX_TBL 8
-#define BNGE_MAX_RSS_TABLE_SIZE \
- (BNGE_RSS_TABLE_SIZE * BNGE_RSS_TABLE_MAX_TBL)
-#define BNGE_MAX_RSS_TABLE_ENTRIES \
- (BNGE_RSS_TABLE_ENTRIES * BNGE_RSS_TABLE_MAX_TBL)
u16 rss_indir_tbl_entries;
u32 rss_cap;
@@ -225,6 +219,11 @@ static inline bool bnge_is_roce_en(struct bnge_dev *bd)
return bd->flags & BNGE_EN_ROCE;
}
+static inline bool bnge_is_arfs_cap(struct bnge_dev *bd)
+{
+ return bd->flags & BNGE_EN_ARFS_CAP;
+}
+
static inline bool bnge_is_agg_reqd(struct bnge_dev *bd)
{
if (bd->netdev) {
diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_core.c b/drivers/net/ethernet/broadcom/bnge/bnge_core.c
index 68b74eb2c3a29..90e61a53a77d5 100644
--- a/drivers/net/ethernet/broadcom/bnge/bnge_core.c
+++ b/drivers/net/ethernet/broadcom/bnge/bnge_core.c
@@ -93,6 +93,13 @@ static int bnge_func_qrcaps_qcfg(struct bnge_dev *bd)
return rc;
}
+ rc = bnge_hwrm_cfa_adv_flow_mgnt_qcaps(bd);
+ if (rc) {
+ dev_warn(bd->dev, "hwrm query adv flow mgnt failure rc: %d\n",
+ rc);
+ return rc;
+ }
+
rc = bnge_hwrm_vnic_qcaps(bd);
if (rc) {
dev_err(bd->dev, "vnic caps failure rc: %d\n", rc);
diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_ethtool.c b/drivers/net/ethernet/broadcom/bnge/bnge_ethtool.c
index 2467e44de291a..8252609def1ec 100644
--- a/drivers/net/ethernet/broadcom/bnge/bnge_ethtool.c
+++ b/drivers/net/ethernet/broadcom/bnge/bnge_ethtool.c
@@ -10,8 +10,12 @@
#include <linux/ethtool_netlink.h>
#include "bnge.h"
+#include "bnge_netdev.h"
+#include "bnge_vnic.h"
+#include "bnge_resc.h"
#include "bnge_ethtool.h"
#include "bnge_hwrm_lib.h"
+#include "bnge_filter.h"
static int bnge_nway_reset(struct net_device *dev)
{
@@ -740,6 +744,984 @@ static int bnge_set_pauseparam(struct net_device *dev,
return rc;
}
+static u64 bnge_get_ethtool_ipv4_rss(struct bnge_dev *bd)
+{
+ if (bd->rss_hash_cfg & VNIC_RSS_CFG_REQ_HASH_TYPE_IPV4)
+ return RXH_IP_SRC | RXH_IP_DST;
+ return 0;
+}
+
+static u64 bnge_get_ethtool_ipv6_rss(struct bnge_dev *bd)
+{
+ if (bd->rss_hash_cfg & VNIC_RSS_CFG_REQ_HASH_TYPE_IPV6)
+ return RXH_IP_SRC | RXH_IP_DST;
+ if (bd->rss_hash_cfg & VNIC_RSS_CFG_REQ_HASH_TYPE_IPV6_FLOW_LABEL)
+ return RXH_IP_SRC | RXH_IP_DST | RXH_IP6_FL;
+ return 0;
+}
+
+static int bnge_get_rxfh_fields(struct net_device *dev,
+ struct ethtool_rxfh_fields *cmd)
+{
+ struct bnge_net *bn = netdev_priv(dev);
+ struct bnge_dev *bd = bn->bd;
+
+ cmd->data = 0;
+ switch (cmd->flow_type) {
+ case TCP_V4_FLOW:
+ if (bd->rss_hash_cfg & VNIC_RSS_CFG_REQ_HASH_TYPE_TCP_IPV4)
+ cmd->data |= RXH_IP_SRC | RXH_IP_DST |
+ RXH_L4_B_0_1 | RXH_L4_B_2_3;
+ cmd->data |= bnge_get_ethtool_ipv4_rss(bd);
+ break;
+ case UDP_V4_FLOW:
+ if (bd->rss_hash_cfg & VNIC_RSS_CFG_REQ_HASH_TYPE_UDP_IPV4)
+ cmd->data |= RXH_IP_SRC | RXH_IP_DST |
+ RXH_L4_B_0_1 | RXH_L4_B_2_3;
+ fallthrough;
+ case AH_ESP_V4_FLOW:
+ if (bd->rss_hash_cfg &
+ (VNIC_RSS_CFG_REQ_HASH_TYPE_AH_SPI_IPV4 |
+ VNIC_RSS_CFG_REQ_HASH_TYPE_ESP_SPI_IPV4))
+ cmd->data |= RXH_IP_SRC | RXH_IP_DST |
+ RXH_L4_B_0_1 | RXH_L4_B_2_3;
+ fallthrough;
+ case SCTP_V4_FLOW:
+ case AH_V4_FLOW:
+ case ESP_V4_FLOW:
+ case IPV4_FLOW:
+ cmd->data |= bnge_get_ethtool_ipv4_rss(bd);
+ break;
+ case TCP_V6_FLOW:
+ if (bd->rss_hash_cfg & VNIC_RSS_CFG_REQ_HASH_TYPE_TCP_IPV6)
+ cmd->data |= RXH_IP_SRC | RXH_IP_DST |
+ RXH_L4_B_0_1 | RXH_L4_B_2_3;
+ cmd->data |= bnge_get_ethtool_ipv6_rss(bd);
+ break;
+ case UDP_V6_FLOW:
+ if (bd->rss_hash_cfg & VNIC_RSS_CFG_REQ_HASH_TYPE_UDP_IPV6)
+ cmd->data |= RXH_IP_SRC | RXH_IP_DST |
+ RXH_L4_B_0_1 | RXH_L4_B_2_3;
+ fallthrough;
+ case AH_ESP_V6_FLOW:
+ if (bd->rss_hash_cfg &
+ (VNIC_RSS_CFG_REQ_HASH_TYPE_AH_SPI_IPV6 |
+ VNIC_RSS_CFG_REQ_HASH_TYPE_ESP_SPI_IPV6))
+ cmd->data |= RXH_IP_SRC | RXH_IP_DST |
+ RXH_L4_B_0_1 | RXH_L4_B_2_3;
+ fallthrough;
+ case SCTP_V6_FLOW:
+ case AH_V6_FLOW:
+ case ESP_V6_FLOW:
+ case IPV6_FLOW:
+ cmd->data |= bnge_get_ethtool_ipv6_rss(bd);
+ break;
+ }
+ return 0;
+}
+
+#define RXH_4TUPLE (RXH_IP_SRC | RXH_IP_DST | RXH_L4_B_0_1 | RXH_L4_B_2_3)
+#define RXH_2TUPLE (RXH_IP_SRC | RXH_IP_DST)
+
+static int bnge_set_rxfh_fields(struct net_device *dev,
+ const struct ethtool_rxfh_fields *cmd,
+ struct netlink_ext_ack *extack)
+{
+ struct bnge_net *bn = netdev_priv(dev);
+ struct bnge_dev *bd = bn->bd;
+ int tuple, rc = 0;
+ u32 rss_hash_cfg;
+
+ rss_hash_cfg = bd->rss_hash_cfg;
+
+ if (cmd->data == RXH_4TUPLE)
+ tuple = 4;
+ else if (cmd->data == RXH_2TUPLE ||
+ cmd->data == (RXH_2TUPLE | RXH_IP6_FL))
+ tuple = 2;
+ else if (!cmd->data)
+ tuple = 0;
+ else
+ return -EINVAL;
+
+ if (cmd->data & RXH_IP6_FL &&
+ !(bd->rss_cap & BNGE_RSS_CAP_IPV6_FLOW_LABEL_RSS_CAP))
+ return -EINVAL;
+
+ if (cmd->flow_type == TCP_V4_FLOW) {
+ rss_hash_cfg &= ~VNIC_RSS_CFG_REQ_HASH_TYPE_TCP_IPV4;
+ if (tuple == 4)
+ rss_hash_cfg |= VNIC_RSS_CFG_REQ_HASH_TYPE_TCP_IPV4;
+ } else if (cmd->flow_type == UDP_V4_FLOW) {
+ rss_hash_cfg &= ~VNIC_RSS_CFG_REQ_HASH_TYPE_UDP_IPV4;
+ if (tuple == 4)
+ rss_hash_cfg |= VNIC_RSS_CFG_REQ_HASH_TYPE_UDP_IPV4;
+ } else if (cmd->flow_type == TCP_V6_FLOW) {
+ rss_hash_cfg &= ~VNIC_RSS_CFG_REQ_HASH_TYPE_TCP_IPV6;
+ if (tuple == 4)
+ rss_hash_cfg |= VNIC_RSS_CFG_REQ_HASH_TYPE_TCP_IPV6;
+ } else if (cmd->flow_type == UDP_V6_FLOW) {
+ rss_hash_cfg &= ~VNIC_RSS_CFG_REQ_HASH_TYPE_UDP_IPV6;
+ if (tuple == 4)
+ rss_hash_cfg |= VNIC_RSS_CFG_REQ_HASH_TYPE_UDP_IPV6;
+ } else if (cmd->flow_type == AH_ESP_V4_FLOW) {
+ if (tuple == 4 &&
+ (!(bd->rss_cap & BNGE_RSS_CAP_AH_V4_RSS_CAP) ||
+ !(bd->rss_cap & BNGE_RSS_CAP_ESP_V4_RSS_CAP)))
+ return -EINVAL;
+ rss_hash_cfg &= ~(VNIC_RSS_CFG_REQ_HASH_TYPE_AH_SPI_IPV4 |
+ VNIC_RSS_CFG_REQ_HASH_TYPE_ESP_SPI_IPV4);
+ if (tuple == 4)
+ rss_hash_cfg |= VNIC_RSS_CFG_REQ_HASH_TYPE_AH_SPI_IPV4 |
+ VNIC_RSS_CFG_REQ_HASH_TYPE_ESP_SPI_IPV4;
+ } else if (cmd->flow_type == AH_ESP_V6_FLOW) {
+ if (tuple == 4 &&
+ (!(bd->rss_cap & BNGE_RSS_CAP_AH_V6_RSS_CAP) ||
+ !(bd->rss_cap & BNGE_RSS_CAP_ESP_V6_RSS_CAP)))
+ return -EINVAL;
+ rss_hash_cfg &= ~(VNIC_RSS_CFG_REQ_HASH_TYPE_AH_SPI_IPV6 |
+ VNIC_RSS_CFG_REQ_HASH_TYPE_ESP_SPI_IPV6);
+ if (tuple == 4)
+ rss_hash_cfg |= VNIC_RSS_CFG_REQ_HASH_TYPE_AH_SPI_IPV6 |
+ VNIC_RSS_CFG_REQ_HASH_TYPE_ESP_SPI_IPV6;
+ } else if (tuple == 4) {
+ return -EINVAL;
+ }
+
+ switch (cmd->flow_type) {
+ case TCP_V4_FLOW:
+ case UDP_V4_FLOW:
+ case SCTP_V4_FLOW:
+ case AH_ESP_V4_FLOW:
+ case AH_V4_FLOW:
+ case ESP_V4_FLOW:
+ case IPV4_FLOW:
+ if (tuple == 2)
+ rss_hash_cfg |= VNIC_RSS_CFG_REQ_HASH_TYPE_IPV4;
+ else if (!tuple)
+ rss_hash_cfg &= ~VNIC_RSS_CFG_REQ_HASH_TYPE_IPV4;
+ break;
+
+ case TCP_V6_FLOW:
+ case UDP_V6_FLOW:
+ case SCTP_V6_FLOW:
+ case AH_ESP_V6_FLOW:
+ case AH_V6_FLOW:
+ case ESP_V6_FLOW:
+ case IPV6_FLOW:
+ rss_hash_cfg &= ~(VNIC_RSS_CFG_REQ_HASH_TYPE_IPV6 |
+ VNIC_RSS_CFG_REQ_HASH_TYPE_IPV6_FLOW_LABEL);
+ if (!tuple)
+ break;
+ if (cmd->data & RXH_IP6_FL)
+ rss_hash_cfg |=
+ VNIC_RSS_CFG_REQ_HASH_TYPE_IPV6_FLOW_LABEL;
+ else if (tuple == 2)
+ rss_hash_cfg |= VNIC_RSS_CFG_REQ_HASH_TYPE_IPV6;
+ break;
+ }
+
+ if (bd->rss_hash_cfg == rss_hash_cfg)
+ return 0;
+
+ bd->rss_hash_cfg = rss_hash_cfg;
+ if (netif_running(bn->netdev)) {
+ bnge_close_core(bn);
+ rc = bnge_open_core(bn);
+ }
+ return rc;
+}
+
+static u32 bnge_get_rxfh_indir_size_eth(struct net_device *dev)
+{
+ struct bnge_net *bn = netdev_priv(dev);
+ struct bnge_dev *bd = bn->bd;
+
+ return bnge_get_rxfh_indir_size(bd);
+}
+
+static u32 bnge_get_rxfh_key_size(struct net_device *dev)
+{
+ return HW_HASH_KEY_SIZE;
+}
+
+static int bnge_get_rxfh(struct net_device *dev,
+ struct ethtool_rxfh_param *rxfh)
+{
+ struct bnge_net *bn = netdev_priv(dev);
+ struct bnge_rss_ctx *rss_ctx = NULL;
+ struct bnge_dev *bd = bn->bd;
+ struct bnge_vnic_info *vnic;
+ u32 i, tbl_size;
+ u32 *indir_tbl;
+
+ indir_tbl = bd->rss_indir_tbl;
+ rxfh->hfunc = ETH_RSS_HASH_TOP;
+
+ if (!bn->vnic_info)
+ return 0;
+
+ vnic = &bn->vnic_info[BNGE_VNIC_DEFAULT];
+ if (rxfh->rss_context) {
+ struct ethtool_rxfh_context *ctx;
+
+ ctx = xa_load(&bn->netdev->ethtool->rss_ctx,
+ rxfh->rss_context);
+ if (!ctx)
+ return -EINVAL;
+ indir_tbl = ethtool_rxfh_context_indir(ctx);
+ rss_ctx = ethtool_rxfh_context_priv(ctx);
+ vnic = &rss_ctx->vnic;
+ }
+
+ if (rxfh->indir && indir_tbl) {
+ tbl_size = bnge_get_rxfh_indir_size(bd);
+ for (i = 0; i < tbl_size; i++)
+ rxfh->indir[i] = indir_tbl[i];
+ }
+
+ if (rxfh->key && vnic->rss_hash_key)
+ memcpy(rxfh->key, vnic->rss_hash_key, HW_HASH_KEY_SIZE);
+
+ return 0;
+}
+
+static int bnge_set_rxfh(struct net_device *dev,
+ struct ethtool_rxfh_param *rxfh,
+ struct netlink_ext_ack *extack)
+{
+ struct bnge_net *bn = netdev_priv(dev);
+ int rc = 0;
+
+ if (rxfh->hfunc && rxfh->hfunc != ETH_RSS_HASH_TOP)
+ return -EOPNOTSUPP;
+
+ bnge_modify_rss(bn, NULL, NULL, rxfh);
+
+ if (netif_running(bn->netdev)) {
+ bnge_close_core(bn);
+ rc = bnge_open_core(bn);
+ }
+ return rc;
+}
+
+static u32 bnge_get_rx_ring_count(struct net_device *dev)
+{
+ struct bnge_net *bn = netdev_priv(dev);
+ struct bnge_dev *bd = bn->bd;
+
+ return bd->rx_nr_rings;
+}
+
+static int bnge_rxfh_context_check(struct bnge_net *bn,
+ const struct ethtool_rxfh_param *rxfh,
+ struct netlink_ext_ack *extack)
+{
+ if (rxfh->hfunc && rxfh->hfunc != ETH_RSS_HASH_TOP) {
+ NL_SET_ERR_MSG_MOD(extack, "RSS hash function not supported");
+ return -EOPNOTSUPP;
+ }
+
+ if (!netif_running(bn->netdev)) {
+ NL_SET_ERR_MSG_MOD(extack, "Unable to set RSS contexts when interface is down");
+ return -EAGAIN;
+ }
+
+ return 0;
+}
+
+static int bnge_create_rxfh_context(struct net_device *dev,
+ struct ethtool_rxfh_context *ctx,
+ const struct ethtool_rxfh_param *rxfh,
+ struct netlink_ext_ack *extack)
+{
+ struct bnge_net *bn = netdev_priv(dev);
+ struct bnge_rss_ctx *rss_ctx;
+ struct bnge_vnic_info *vnic;
+ int rc;
+
+ rc = bnge_rxfh_context_check(bn, rxfh, extack);
+ if (rc)
+ return rc;
+
+ if (bn->num_rss_ctx >= BNGE_MAX_ETH_RSS_CTX) {
+ NL_SET_ERR_MSG_FMT_MOD(extack, "Out of RSS contexts, maximum %u",
+ BNGE_MAX_ETH_RSS_CTX);
+ return -EINVAL;
+ }
+
+ if (!bnge_arfs_capable(bn->bd, true)) {
+ NL_SET_ERR_MSG_MOD(extack, "Out of hardware resources");
+ return -ENOMEM;
+ }
+
+ rss_ctx = ethtool_rxfh_context_priv(ctx);
+
+ bn->num_rss_ctx++;
+
+ vnic = &rss_ctx->vnic;
+
+ bnge_init_vnic_mem(vnic);
+
+ vnic->rss_ctx = ctx;
+ vnic->flags |= BNGE_VNIC_RSSCTX_FLAG;
+ rc = bnge_alloc_vnic_rss_table(bn, vnic);
+ if (rc)
+ goto err_del_rss_ctx;
+
+ /* Populate defaults in the context */
+ bnge_set_dflt_rss_indir_tbl(bn->bd, ctx);
+ ctx->hfunc = ETH_RSS_HASH_TOP;
+ memcpy(vnic->rss_hash_key, bn->rss_hash_key, HW_HASH_KEY_SIZE);
+ memcpy(ethtool_rxfh_context_key(ctx),
+ bn->rss_hash_key, HW_HASH_KEY_SIZE);
+
+ rc = bnge_hwrm_vnic_alloc(bn->bd, vnic, bn->bd->rx_nr_rings);
+ if (rc) {
+ NL_SET_ERR_MSG_MOD(extack, "Unable to allocate VNIC");
+ goto err_del_rss_ctx;
+ }
+
+ rc = bnge_hwrm_vnic_set_tpa(bn->bd, vnic,
+ bn->priv_flags & BNGE_NET_EN_TPA);
+ if (rc) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Unable to set TPA settings to vnic");
+ goto err_del_rss_ctx;
+ }
+ bnge_modify_rss(bn, ctx, rss_ctx, rxfh);
+
+ rc = bnge_setup_vnic(bn, vnic);
+ if (rc) {
+ NL_SET_ERR_MSG_MOD(extack, "Unable to setup vnic");
+ goto err_del_rss_ctx;
+ }
+
+ rss_ctx->index = rxfh->rss_context;
+ return 0;
+
+err_del_rss_ctx:
+ bnge_del_one_rss_ctx(bn, rss_ctx, true);
+ return rc;
+}
+
+static int bnge_modify_rxfh_context(struct net_device *dev,
+ struct ethtool_rxfh_context *ctx,
+ const struct ethtool_rxfh_param *rxfh,
+ struct netlink_ext_ack *extack)
+{
+ struct bnge_net *bn = netdev_priv(dev);
+ struct bnge_rss_ctx *rss_ctx;
+ int rc;
+
+ rc = bnge_rxfh_context_check(bn, rxfh, extack);
+ if (rc)
+ return rc;
+
+ rss_ctx = ethtool_rxfh_context_priv(ctx);
+
+ bnge_modify_rss(bn, ctx, rss_ctx, rxfh);
+
+ return bnge_hwrm_vnic_rss_cfg(bn, &rss_ctx->vnic);
+}
+
+static int bnge_remove_rxfh_context(struct net_device *dev,
+ struct ethtool_rxfh_context *ctx,
+ u32 rss_context,
+ struct netlink_ext_ack *extack)
+{
+ struct bnge_net *bn = netdev_priv(dev);
+ struct bnge_rss_ctx *rss_ctx;
+
+ rss_ctx = ethtool_rxfh_context_priv(ctx);
+
+ bnge_del_one_rss_ctx(bn, rss_ctx, true);
+ return 0;
+}
+
+#define BNGE_IP_PROTO_FULL_MASK 0xFF
+#define BNGE_IP_PROTO_WILDCARD 0x0
+
+static u32 bnge_get_all_fltr_ids_rcu(struct bnge_net *bn,
+ struct hlist_head tbl[],
+ u32 tbl_size, u32 *ids, u32 start,
+ u32 id_cnt, u32 offset)
+{
+ u32 i, j = start;
+
+ if (j >= id_cnt)
+ return j;
+
+ for (i = 0; i < tbl_size; i++) {
+ struct bnge_filter_base *fltr;
+ struct hlist_head *head;
+
+ head = &tbl[i];
+ hlist_for_each_entry_rcu(fltr, head, hlist) {
+ if (!fltr->flags ||
+ test_bit(BNGE_FLTR_FW_DELETED, &fltr->state))
+ continue;
+ ids[j++] = fltr->sw_id + offset;
+ if (j == id_cnt)
+ return j;
+ }
+ }
+ return j;
+}
+
+static struct bnge_filter_base *bnge_get_one_fltr_rcu(struct bnge_net *bn,
+ struct hlist_head tbl[],
+ u32 tbl_size, u32 id,
+ u32 offset)
+{
+ u32 i;
+
+ for (i = 0; i < tbl_size; i++) {
+ struct bnge_filter_base *fltr;
+ struct hlist_head *head;
+
+ head = &tbl[i];
+ hlist_for_each_entry_rcu(fltr, head, hlist) {
+ if (fltr->flags && fltr->sw_id + offset == id)
+ return fltr;
+ }
+ }
+ return NULL;
+}
+
+static int bnge_grxclsrlall(struct bnge_net *bn, struct ethtool_rxnfc *cmd,
+ u32 *rule_locs)
+{
+ u32 count;
+
+ cmd->data = bn->user_fltr_count;
+ rcu_read_lock();
+ count = bnge_get_all_fltr_ids_rcu(bn, bn->l2_fltr_hash_tbl,
+ BNGE_L2_FLTR_HASH_SIZE, rule_locs, 0,
+ cmd->rule_cnt, 0);
+ cmd->rule_cnt = bnge_get_all_fltr_ids_rcu(bn, bn->ntp_fltr_hash_tbl,
+ BNGE_NTP_FLTR_HASH_SIZE,
+ rule_locs, count,
+ cmd->rule_cnt,
+ BNGE_MAX_L2_FLTRS);
+ rcu_read_unlock();
+
+ return 0;
+}
+
+static int bnge_grxclsrule(struct bnge_net *bn, struct ethtool_rxnfc *cmd)
+{
+ struct ethtool_rx_flow_spec *fs =
+ (struct ethtool_rx_flow_spec *)&cmd->fs;
+ struct bnge_filter_base *fltr_base;
+ struct bnge_ntuple_filter *fltr;
+ struct bnge_flow_masks *fmasks;
+ struct bnge_dev *bd = bn->bd;
+ struct flow_keys *fkeys;
+ int rc = -EINVAL;
+
+ if (fs->location >= BNGE_MAX_L2_FLTRS + bd->max_fltr)
+ return rc;
+
+ rcu_read_lock();
+ fltr_base = bnge_get_one_fltr_rcu(bn, bn->l2_fltr_hash_tbl,
+ BNGE_L2_FLTR_HASH_SIZE,
+ fs->location, 0);
+ if (fltr_base) {
+ struct ethhdr *h_ether = &fs->h_u.ether_spec;
+ struct ethhdr *m_ether = &fs->m_u.ether_spec;
+ struct bnge_l2_filter *l2_fltr;
+ struct bnge_l2_key *l2_key;
+
+ l2_fltr = container_of(fltr_base, struct bnge_l2_filter, base);
+ l2_key = &l2_fltr->l2_key;
+ fs->flow_type = ETHER_FLOW;
+ ether_addr_copy(h_ether->h_dest, l2_key->dst_mac_addr);
+ eth_broadcast_addr(m_ether->h_dest);
+ if (l2_key->vlan) {
+ struct ethtool_flow_ext *m_ext = &fs->m_ext;
+ struct ethtool_flow_ext *h_ext = &fs->h_ext;
+
+ fs->flow_type |= FLOW_EXT;
+ m_ext->vlan_tci = htons(0xfff);
+ h_ext->vlan_tci = htons(l2_key->vlan);
+ }
+ if (fltr_base->flags & BNGE_ACT_RING_DST)
+ fs->ring_cookie = fltr_base->rxq;
+ rcu_read_unlock();
+ return 0;
+ }
+ fltr_base = bnge_get_one_fltr_rcu(bn, bn->ntp_fltr_hash_tbl,
+ BNGE_NTP_FLTR_HASH_SIZE,
+ fs->location, BNGE_MAX_L2_FLTRS);
+ if (!fltr_base) {
+ rcu_read_unlock();
+ return rc;
+ }
+ fltr = container_of(fltr_base, struct bnge_ntuple_filter, base);
+
+ fkeys = &fltr->fkeys;
+ fmasks = &fltr->fmasks;
+ if (fkeys->basic.n_proto == htons(ETH_P_IP)) {
+ if (fkeys->basic.ip_proto == BNGE_IP_PROTO_WILDCARD) {
+ fs->flow_type = IP_USER_FLOW;
+ fs->h_u.usr_ip4_spec.ip_ver = ETH_RX_NFC_IP4;
+ fs->h_u.usr_ip4_spec.proto = BNGE_IP_PROTO_WILDCARD;
+ fs->m_u.usr_ip4_spec.proto = 0;
+ } else if (fkeys->basic.ip_proto == IPPROTO_ICMP) {
+ fs->flow_type = IP_USER_FLOW;
+ fs->h_u.usr_ip4_spec.ip_ver = ETH_RX_NFC_IP4;
+ fs->h_u.usr_ip4_spec.proto = IPPROTO_ICMP;
+ fs->m_u.usr_ip4_spec.proto = BNGE_IP_PROTO_FULL_MASK;
+ } else if (fkeys->basic.ip_proto == IPPROTO_TCP) {
+ fs->flow_type = TCP_V4_FLOW;
+ } else if (fkeys->basic.ip_proto == IPPROTO_UDP) {
+ fs->flow_type = UDP_V4_FLOW;
+ } else {
+ goto fltr_err;
+ }
+
+ fs->h_u.tcp_ip4_spec.ip4src = fkeys->addrs.v4addrs.src;
+ fs->m_u.tcp_ip4_spec.ip4src = fmasks->addrs.v4addrs.src;
+ fs->h_u.tcp_ip4_spec.ip4dst = fkeys->addrs.v4addrs.dst;
+ fs->m_u.tcp_ip4_spec.ip4dst = fmasks->addrs.v4addrs.dst;
+ if (fs->flow_type == TCP_V4_FLOW ||
+ fs->flow_type == UDP_V4_FLOW) {
+ fs->h_u.tcp_ip4_spec.psrc = fkeys->ports.src;
+ fs->m_u.tcp_ip4_spec.psrc = fmasks->ports.src;
+ fs->h_u.tcp_ip4_spec.pdst = fkeys->ports.dst;
+ fs->m_u.tcp_ip4_spec.pdst = fmasks->ports.dst;
+ }
+ } else {
+ if (fkeys->basic.ip_proto == BNGE_IP_PROTO_WILDCARD) {
+ fs->flow_type = IPV6_USER_FLOW;
+ fs->h_u.usr_ip6_spec.l4_proto =
+ BNGE_IP_PROTO_WILDCARD;
+ fs->m_u.usr_ip6_spec.l4_proto = 0;
+ } else if (fkeys->basic.ip_proto == IPPROTO_ICMPV6) {
+ fs->flow_type = IPV6_USER_FLOW;
+ fs->h_u.usr_ip6_spec.l4_proto = IPPROTO_ICMPV6;
+ fs->m_u.usr_ip6_spec.l4_proto =
+ BNGE_IP_PROTO_FULL_MASK;
+ } else if (fkeys->basic.ip_proto == IPPROTO_TCP) {
+ fs->flow_type = TCP_V6_FLOW;
+ } else if (fkeys->basic.ip_proto == IPPROTO_UDP) {
+ fs->flow_type = UDP_V6_FLOW;
+ } else {
+ goto fltr_err;
+ }
+
+ memcpy(fs->h_u.tcp_ip6_spec.ip6src,
+ &fkeys->addrs.v6addrs.src,
+ sizeof(struct in6_addr));
+ memcpy(fs->m_u.tcp_ip6_spec.ip6src,
+ &fmasks->addrs.v6addrs.src,
+ sizeof(struct in6_addr));
+ memcpy(fs->h_u.tcp_ip6_spec.ip6dst,
+ &fkeys->addrs.v6addrs.dst,
+ sizeof(struct in6_addr));
+ memcpy(fs->m_u.tcp_ip6_spec.ip6dst,
+ &fmasks->addrs.v6addrs.dst,
+ sizeof(struct in6_addr));
+
+ if (fs->flow_type == TCP_V6_FLOW ||
+ fs->flow_type == UDP_V6_FLOW) {
+ fs->h_u.tcp_ip6_spec.psrc = fkeys->ports.src;
+ fs->m_u.tcp_ip6_spec.psrc = fmasks->ports.src;
+ fs->h_u.tcp_ip6_spec.pdst = fkeys->ports.dst;
+ fs->m_u.tcp_ip6_spec.pdst = fmasks->ports.dst;
+ }
+ }
+
+ if (fltr->base.flags & BNGE_ACT_DROP) {
+ fs->ring_cookie = RX_CLS_FLOW_DISC;
+ } else if (fltr->base.flags & BNGE_ACT_RSS_CTX) {
+ fs->flow_type |= FLOW_RSS;
+ cmd->rss_context = fltr->base.fw_vnic_id;
+ } else {
+ fs->ring_cookie = fltr->base.rxq;
+ }
+ rc = 0;
+
+fltr_err:
+ rcu_read_unlock();
+
+ return rc;
+}
+
+static bool bnge_verify_ntuple_ip4_flow(struct ethtool_usrip4_spec *ip_spec,
+ struct ethtool_usrip4_spec *ip_mask)
+{
+ u8 mproto = ip_mask->proto;
+ u8 sproto = ip_spec->proto;
+
+ if (ip_mask->l4_4_bytes || ip_mask->tos ||
+ ip_spec->ip_ver != ETH_RX_NFC_IP4 ||
+ (mproto && (mproto != BNGE_IP_PROTO_FULL_MASK ||
+ sproto != IPPROTO_ICMP)))
+ return false;
+ return true;
+}
+
+static bool bnge_verify_ntuple_ip6_flow(struct ethtool_usrip6_spec *ip_spec,
+ struct ethtool_usrip6_spec *ip_mask)
+{
+ u8 mproto = ip_mask->l4_proto;
+ u8 sproto = ip_spec->l4_proto;
+
+ if (ip_mask->l4_4_bytes || ip_mask->tclass ||
+ (mproto && (mproto != BNGE_IP_PROTO_FULL_MASK ||
+ sproto != IPPROTO_ICMPV6)))
+ return false;
+ return true;
+}
+
+static int bnge_add_ntuple_cls_rule(struct bnge_net *bn,
+ struct ethtool_rxnfc *cmd)
+{
+ struct ethtool_rx_flow_spec *fs = &cmd->fs;
+ struct bnge_ntuple_filter *new_fltr, *fltr;
+ u32 flow_type = fs->flow_type & 0xff;
+ struct bnge_l2_filter *l2_fltr;
+ struct bnge_flow_masks *fmasks;
+ struct flow_keys *fkeys;
+ u32 idx;
+ int rc;
+
+ if (!bn->vnic_info)
+ return -EAGAIN;
+
+ if (fs->flow_type & (FLOW_MAC_EXT | FLOW_EXT))
+ return -EOPNOTSUPP;
+
+ if (fs->ring_cookie != RX_CLS_FLOW_DISC &&
+ ethtool_get_flow_spec_ring_vf(fs->ring_cookie))
+ return -EOPNOTSUPP;
+
+ if (flow_type == IP_USER_FLOW) {
+ if (!bnge_verify_ntuple_ip4_flow(&fs->h_u.usr_ip4_spec,
+ &fs->m_u.usr_ip4_spec))
+ return -EOPNOTSUPP;
+ }
+
+ if (flow_type == IPV6_USER_FLOW) {
+ if (!bnge_verify_ntuple_ip6_flow(&fs->h_u.usr_ip6_spec,
+ &fs->m_u.usr_ip6_spec))
+ return -EOPNOTSUPP;
+ }
+
+ new_fltr = kzalloc_obj(*new_fltr, GFP_KERNEL);
+ if (!new_fltr)
+ return -ENOMEM;
+
+ l2_fltr = bn->vnic_info[BNGE_VNIC_DEFAULT].l2_filters[0];
+ new_fltr->l2_filter_id = l2_fltr->base.filter_id;
+ fmasks = &new_fltr->fmasks;
+ fkeys = &new_fltr->fkeys;
+
+ rc = -EOPNOTSUPP;
+ switch (flow_type) {
+ case IP_USER_FLOW: {
+ struct ethtool_usrip4_spec *ip_spec = &fs->h_u.usr_ip4_spec;
+ struct ethtool_usrip4_spec *ip_mask = &fs->m_u.usr_ip4_spec;
+
+ fkeys->basic.ip_proto = ip_mask->proto ? ip_spec->proto
+ : BNGE_IP_PROTO_WILDCARD;
+ fkeys->basic.n_proto = htons(ETH_P_IP);
+ fkeys->addrs.v4addrs.src = ip_spec->ip4src;
+ fmasks->addrs.v4addrs.src = ip_mask->ip4src;
+ fkeys->addrs.v4addrs.dst = ip_spec->ip4dst;
+ fmasks->addrs.v4addrs.dst = ip_mask->ip4dst;
+ break;
+ }
+ case TCP_V4_FLOW:
+ case UDP_V4_FLOW: {
+ struct ethtool_tcpip4_spec *ip_spec = &fs->h_u.tcp_ip4_spec;
+ struct ethtool_tcpip4_spec *ip_mask = &fs->m_u.tcp_ip4_spec;
+
+ fkeys->basic.ip_proto = IPPROTO_TCP;
+ if (flow_type == UDP_V4_FLOW)
+ fkeys->basic.ip_proto = IPPROTO_UDP;
+ fkeys->basic.n_proto = htons(ETH_P_IP);
+ fkeys->addrs.v4addrs.src = ip_spec->ip4src;
+ fmasks->addrs.v4addrs.src = ip_mask->ip4src;
+ fkeys->addrs.v4addrs.dst = ip_spec->ip4dst;
+ fmasks->addrs.v4addrs.dst = ip_mask->ip4dst;
+ fkeys->ports.src = ip_spec->psrc;
+ fmasks->ports.src = ip_mask->psrc;
+ fkeys->ports.dst = ip_spec->pdst;
+ fmasks->ports.dst = ip_mask->pdst;
+ break;
+ }
+ case IPV6_USER_FLOW: {
+ struct ethtool_usrip6_spec *ip_spec = &fs->h_u.usr_ip6_spec;
+ struct ethtool_usrip6_spec *ip_mask = &fs->m_u.usr_ip6_spec;
+
+ fkeys->basic.ip_proto = ip_mask->l4_proto ? ip_spec->l4_proto
+ : BNGE_IP_PROTO_WILDCARD;
+ fkeys->basic.n_proto = htons(ETH_P_IPV6);
+
+ memcpy(&fkeys->addrs.v6addrs.src, ip_spec->ip6src,
+ sizeof(struct in6_addr));
+ memcpy(&fmasks->addrs.v6addrs.src, ip_mask->ip6src,
+ sizeof(struct in6_addr));
+ memcpy(&fkeys->addrs.v6addrs.dst, ip_spec->ip6dst,
+ sizeof(struct in6_addr));
+ memcpy(&fmasks->addrs.v6addrs.dst, ip_mask->ip6dst,
+ sizeof(struct in6_addr));
+ break;
+ }
+ case TCP_V6_FLOW:
+ case UDP_V6_FLOW: {
+ struct ethtool_tcpip6_spec *ip_spec = &fs->h_u.tcp_ip6_spec;
+ struct ethtool_tcpip6_spec *ip_mask = &fs->m_u.tcp_ip6_spec;
+
+ fkeys->basic.ip_proto = IPPROTO_TCP;
+ if (flow_type == UDP_V6_FLOW)
+ fkeys->basic.ip_proto = IPPROTO_UDP;
+ fkeys->basic.n_proto = htons(ETH_P_IPV6);
+
+ memcpy(&fkeys->addrs.v6addrs.src, ip_spec->ip6src,
+ sizeof(struct in6_addr));
+ memcpy(&fmasks->addrs.v6addrs.src, ip_mask->ip6src,
+ sizeof(struct in6_addr));
+ memcpy(&fkeys->addrs.v6addrs.dst, ip_spec->ip6dst,
+ sizeof(struct in6_addr));
+ memcpy(&fmasks->addrs.v6addrs.dst, ip_mask->ip6dst,
+ sizeof(struct in6_addr));
+
+ fkeys->ports.src = ip_spec->psrc;
+ fmasks->ports.src = ip_mask->psrc;
+ fkeys->ports.dst = ip_spec->pdst;
+ fmasks->ports.dst = ip_mask->pdst;
+ break;
+ }
+ default:
+ rc = -EOPNOTSUPP;
+ goto err_free_fltr;
+ }
+ if (!memcmp(&BNGE_FLOW_MASK_NONE, fmasks, sizeof(*fmasks)))
+ goto err_free_fltr;
+
+ idx = bnge_get_ntp_filter_idx(bn, fkeys, NULL);
+ rcu_read_lock();
+ fltr = bnge_lookup_ntp_filter_from_idx(bn, new_fltr, idx);
+ if (fltr) {
+ rcu_read_unlock();
+ rc = -EEXIST;
+ goto err_free_fltr;
+ }
+ rcu_read_unlock();
+
+ new_fltr->base.flags = BNGE_ACT_NO_AGING;
+ if (fs->flow_type & FLOW_RSS) {
+ struct bnge_rss_ctx *rss_ctx;
+
+ new_fltr->base.fw_vnic_id = 0;
+ new_fltr->base.flags |= BNGE_ACT_RSS_CTX;
+ rss_ctx = bnge_get_rss_ctx_from_index(bn, cmd->rss_context);
+ if (rss_ctx) {
+ new_fltr->base.fw_vnic_id = rss_ctx->index;
+ } else {
+ rc = -EINVAL;
+ goto err_free_fltr;
+ }
+ }
+ if (fs->ring_cookie == RX_CLS_FLOW_DISC)
+ new_fltr->base.flags |= BNGE_ACT_DROP;
+ else
+ new_fltr->base.rxq = ethtool_get_flow_spec_ring(fs->ring_cookie);
+ __set_bit(BNGE_FLTR_VALID, &new_fltr->base.state);
+ rc = bnge_insert_ntp_filter(bn, new_fltr, idx);
+ if (!rc) {
+ rc = bnge_hwrm_cfa_ntuple_filter_alloc(bn->bd, new_fltr);
+ if (rc) {
+ bnge_del_ntp_filter(bn, new_fltr);
+ return rc;
+ }
+ fs->location = BNGE_MAX_L2_FLTRS + new_fltr->base.sw_id;
+ return 0;
+ }
+
+err_free_fltr:
+ kfree(new_fltr);
+ return rc;
+}
+
+static int bnge_add_l2_cls_rule(struct bnge_net *bn,
+ struct ethtool_rx_flow_spec *fs)
+{
+ u32 ring = ethtool_get_flow_spec_ring(fs->ring_cookie);
+ struct ethhdr *h_ether = &fs->h_u.ether_spec;
+ struct ethhdr *m_ether = &fs->m_u.ether_spec;
+ struct bnge_l2_filter *fltr;
+ struct bnge_l2_key key;
+ u16 vnic_id;
+ u8 flags;
+ int rc;
+
+ if (!is_broadcast_ether_addr(m_ether->h_dest))
+ return -EINVAL;
+
+ if (is_broadcast_ether_addr(h_ether->h_dest) ||
+ is_multicast_ether_addr(h_ether->h_dest))
+ return -EINVAL;
+
+ ether_addr_copy(key.dst_mac_addr, h_ether->h_dest);
+ key.vlan = 0;
+ if (fs->flow_type & FLOW_EXT) {
+ struct ethtool_flow_ext *m_ext = &fs->m_ext;
+ struct ethtool_flow_ext *h_ext = &fs->h_ext;
+
+ if (m_ext->vlan_tci != htons(0xfff) || !h_ext->vlan_tci)
+ return -EINVAL;
+ key.vlan = ntohs(h_ext->vlan_tci);
+ }
+
+ flags = BNGE_ACT_RING_DST;
+ vnic_id = bn->vnic_info[BNGE_VNIC_DEFAULT].fw_vnic_id;
+
+ fltr = bnge_alloc_user_l2_filter(bn, &key, flags);
+ if (IS_ERR(fltr))
+ return PTR_ERR(fltr);
+
+ fltr->base.fw_vnic_id = vnic_id;
+ fltr->base.rxq = ring;
+ rc = bnge_hwrm_l2_filter_alloc(bn->bd, fltr);
+ if (rc)
+ bnge_del_l2_filter_rcu(bn, fltr);
+ else
+ fs->location = fltr->base.sw_id;
+ return rc;
+}
+
+static int bnge_srxclsrlins(struct bnge_net *bn, struct ethtool_rxnfc *cmd)
+{
+ struct ethtool_rx_flow_spec *fs = &cmd->fs;
+ struct bnge_dev *bd = bn->bd;
+ u32 ring, flow_type;
+ int rc;
+
+ if (!netif_running(bn->netdev))
+ return -EAGAIN;
+ if (!(bn->priv_flags & BNGE_NET_EN_NTUPLE))
+ return -EPERM;
+ if (fs->location != RX_CLS_LOC_ANY)
+ return -EINVAL;
+
+ flow_type = fs->flow_type;
+ if ((flow_type == IP_USER_FLOW ||
+ flow_type == IPV6_USER_FLOW) &&
+ !(bd->fw_cap & BNGE_FW_CAP_CFA_NTUPLE_RX_EXT_IP_PROTO))
+ return -EOPNOTSUPP;
+
+ if (flow_type & FLOW_MAC_EXT)
+ return -EINVAL;
+
+ flow_type &= ~FLOW_EXT;
+
+ if (fs->ring_cookie == RX_CLS_FLOW_DISC && flow_type != ETHER_FLOW)
+ return bnge_add_ntuple_cls_rule(bn, cmd);
+
+ ring = ethtool_get_flow_spec_ring(fs->ring_cookie);
+ if (ring >= bd->rx_nr_rings)
+ return -EINVAL;
+
+ if (flow_type == ETHER_FLOW)
+ rc = bnge_add_l2_cls_rule(bn, fs);
+ else
+ rc = bnge_add_ntuple_cls_rule(bn, cmd);
+ return rc;
+}
+
+static int bnge_srxclsrldel(struct bnge_net *bn, struct ethtool_rxnfc *cmd)
+{
+ struct ethtool_rx_flow_spec *fs = &cmd->fs;
+ struct bnge_filter_base *fltr_base;
+ struct bnge_ntuple_filter *fltr;
+ u32 id = fs->location;
+
+ rcu_read_lock();
+ fltr_base = bnge_get_one_fltr_rcu(bn, bn->l2_fltr_hash_tbl,
+ BNGE_L2_FLTR_HASH_SIZE, id, 0);
+ if (fltr_base) {
+ struct bnge_l2_filter *l2_fltr;
+
+ l2_fltr = container_of(fltr_base, struct bnge_l2_filter, base);
+ rcu_read_unlock();
+ bnge_hwrm_l2_filter_free(bn->bd, l2_fltr);
+ bnge_del_l2_filter_rcu(bn, l2_fltr);
+ return 0;
+ }
+ fltr_base = bnge_get_one_fltr_rcu(bn, bn->ntp_fltr_hash_tbl,
+ BNGE_NTP_FLTR_HASH_SIZE, id,
+ BNGE_MAX_L2_FLTRS);
+ if (!fltr_base) {
+ rcu_read_unlock();
+ return -ENOENT;
+ }
+
+ fltr = container_of(fltr_base, struct bnge_ntuple_filter, base);
+ if (!(fltr->base.flags & BNGE_ACT_NO_AGING)) {
+ rcu_read_unlock();
+ return -EINVAL;
+ }
+ rcu_read_unlock();
+ bnge_hwrm_cfa_ntuple_filter_free(bn->bd, fltr);
+ bnge_del_ntp_filter(bn, fltr);
+ return 0;
+}
+
+static int bnge_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd,
+ u32 *rule_locs)
+{
+ struct bnge_net *bn = netdev_priv(dev);
+ struct bnge_dev *bd = bn->bd;
+ int rc = 0;
+
+ switch (cmd->cmd) {
+ case ETHTOOL_GRXCLSRLCNT:
+ cmd->rule_cnt = bn->user_fltr_count;
+ cmd->data = bd->max_fltr | RX_CLS_LOC_SPECIAL;
+ break;
+
+ case ETHTOOL_GRXCLSRLALL:
+ rc = bnge_grxclsrlall(bn, cmd, (u32 *)rule_locs);
+ break;
+
+ case ETHTOOL_GRXCLSRULE:
+ rc = bnge_grxclsrule(bn, cmd);
+ break;
+
+ default:
+ rc = -EOPNOTSUPP;
+ break;
+ }
+
+ return rc;
+}
+
+static int bnge_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd)
+{
+ struct bnge_net *bn = netdev_priv(dev);
+ int rc;
+
+ switch (cmd->cmd) {
+ case ETHTOOL_SRXCLSRLINS:
+ rc = bnge_srxclsrlins(bn, cmd);
+ break;
+
+ case ETHTOOL_SRXCLSRLDEL:
+ rc = bnge_srxclsrldel(bn, cmd);
+ break;
+
+ default:
+ rc = -EOPNOTSUPP;
+ break;
+ }
+ return rc;
+}
+
static const struct ethtool_ops bnge_ethtool_ops = {
.cap_link_lanes_supported = 1,
.get_link_ksettings = bnge_get_link_ksettings,
@@ -757,6 +1739,23 @@ static const struct ethtool_ops bnge_ethtool_ops = {
.get_eth_ctrl_stats = bnge_get_eth_ctrl_stats,
.get_pause_stats = bnge_get_pause_stats,
.get_rmon_stats = bnge_get_rmon_stats,
+ /* RXFH */
+ .rxfh_per_ctx_key = 1,
+ .rxfh_max_num_contexts = BNGE_MAX_ETH_RSS_CTX + 1,
+ .rxfh_indir_space = BNGE_MAX_RSS_TABLE_ENTRIES,
+ .rxfh_priv_size = sizeof(struct bnge_rss_ctx),
+ .get_rx_ring_count = bnge_get_rx_ring_count,
+ .get_rxfh_indir_size = bnge_get_rxfh_indir_size_eth,
+ .get_rxfh_key_size = bnge_get_rxfh_key_size,
+ .get_rxfh = bnge_get_rxfh,
+ .set_rxfh = bnge_set_rxfh,
+ .get_rxfh_fields = bnge_get_rxfh_fields,
+ .set_rxfh_fields = bnge_set_rxfh_fields,
+ .create_rxfh_context = bnge_create_rxfh_context,
+ .modify_rxfh_context = bnge_modify_rxfh_context,
+ .remove_rxfh_context = bnge_remove_rxfh_context,
+ .get_rxnfc = bnge_get_rxnfc,
+ .set_rxnfc = bnge_set_rxnfc,
};
void bnge_set_ethtool_ops(struct net_device *dev)
diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_filter.c b/drivers/net/ethernet/broadcom/bnge/bnge_filter.c
new file mode 100644
index 0000000000000..6f75e08a4996e
--- /dev/null
+++ b/drivers/net/ethernet/broadcom/bnge/bnge_filter.c
@@ -0,0 +1,633 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2026 Broadcom.
+
+#include <linux/kernel.h>
+#include <linux/dma-mapping.h>
+#include <linux/jhash.h>
+#include <net/netdev_lock.h>
+#include <net/ipv6.h>
+
+#include "bnge.h"
+#include "bnge_netdev.h"
+#include "bnge_vnic.h"
+#include "bnge_hwrm_lib.h"
+#include "bnge_filter.h"
+
+#define BNGE_IPV6_MASK_ALL {{{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }}}
+#define BNGE_IPV6_MASK_NONE {{{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }}}
+
+const struct bnge_flow_masks BNGE_FLOW_MASK_NONE = {
+ .ports = {
+ .src = 0,
+ .dst = 0,
+ },
+ .addrs = {
+ .v6addrs = {
+ .src = BNGE_IPV6_MASK_NONE,
+ .dst = BNGE_IPV6_MASK_NONE,
+ },
+ },
+};
+
+const struct bnge_flow_masks BNGE_FLOW_IPV6_MASK_ALL = {
+ .ports = {
+ .src = cpu_to_be16(0xffff),
+ .dst = cpu_to_be16(0xffff),
+ },
+ .addrs = {
+ .v6addrs = {
+ .src = BNGE_IPV6_MASK_ALL,
+ .dst = BNGE_IPV6_MASK_ALL,
+ },
+ },
+};
+
+const struct bnge_flow_masks BNGE_FLOW_IPV4_MASK_ALL = {
+ .ports = {
+ .src = cpu_to_be16(0xffff),
+ .dst = cpu_to_be16(0xffff),
+ },
+ .addrs = {
+ .v4addrs = {
+ .src = cpu_to_be32(0xffffffff),
+ .dst = cpu_to_be32(0xffffffff),
+ },
+ },
+};
+
+static void bnge_insert_usr_fltr(struct bnge_net *bn,
+ struct bnge_filter_base *fltr)
+{
+ u8 type = fltr->type, flags = fltr->flags;
+
+ INIT_LIST_HEAD(&fltr->list_node);
+ if ((type == BNGE_FLTR_TYPE_L2 && flags & BNGE_ACT_RING_DST) ||
+ (type == BNGE_FLTR_TYPE_NTUPLE && flags & BNGE_ACT_NO_AGING)) {
+ list_add_tail(&fltr->list_node, &bn->usr_fltr_list);
+ bn->user_fltr_count++;
+ }
+}
+
+static void bnge_del_usr_fltr_node(struct bnge_net *bn,
+ struct bnge_filter_base *fltr)
+{
+ if (!list_empty(&fltr->list_node)) {
+ list_del_init(&fltr->list_node);
+ bn->user_fltr_count--;
+ }
+}
+
+void bnge_del_l2_filter_rcu(struct bnge_net *bn, struct bnge_l2_filter *lfltr)
+{
+ struct bnge_filter_base *fltr = &lfltr->base;
+
+ hlist_del_rcu(&fltr->hlist);
+ bnge_del_usr_fltr_node(bn, fltr);
+ clear_bit(fltr->sw_id, bn->l2_fltr_bmap);
+ bn->l2_fltr_count--;
+ kfree_rcu(lfltr, base.rcu);
+}
+
+void bnge_del_l2_filter(struct bnge_net *bn, struct bnge_l2_filter *lfltr)
+{
+ struct bnge_filter_base *fltr = &lfltr->base;
+
+ hlist_del(&fltr->hlist);
+ bnge_del_usr_fltr_node(bn, fltr);
+ clear_bit(fltr->sw_id, bn->l2_fltr_bmap);
+ bn->l2_fltr_count--;
+ kfree(fltr);
+}
+
+void bnge_del_ntp_filter(struct bnge_net *bn, struct bnge_ntuple_filter *nfltr)
+{
+ struct bnge_filter_base *fltr = &nfltr->base;
+
+ hlist_del(&fltr->hlist);
+ bnge_del_usr_fltr_node(bn, fltr);
+ clear_bit(fltr->sw_id, bn->ntp_fltr_bmap);
+ bn->ntp_fltr_count--;
+ kfree(fltr);
+}
+
+void bnge_del_ntp_filter_rcu(struct bnge_net *bn,
+ struct bnge_ntuple_filter *fltr)
+{
+ spin_lock_bh(&bn->ntp_fltr_lock);
+ hlist_del_rcu(&fltr->base.hlist);
+ bnge_del_usr_fltr_node(bn, &fltr->base);
+ bn->ntp_fltr_count--;
+ spin_unlock_bh(&bn->ntp_fltr_lock);
+
+ clear_bit(fltr->base.sw_id, bn->ntp_fltr_bmap);
+ kfree_rcu(fltr, base.rcu);
+}
+
+
+static int bnge_init_l2_filter(struct bnge_net *bn,
+ struct bnge_l2_filter *fltr,
+ struct bnge_l2_key *key, u32 idx)
+{
+ struct hlist_head *head;
+ int bit_id;
+
+ ether_addr_copy(fltr->l2_key.dst_mac_addr, key->dst_mac_addr);
+ fltr->l2_key.vlan = key->vlan;
+ fltr->base.type = BNGE_FLTR_TYPE_L2;
+
+ bit_id = bitmap_find_free_region(bn->l2_fltr_bmap,
+ BNGE_MAX_L2_FLTRS, 0);
+ if (bit_id < 0)
+ return -ENOMEM;
+ fltr->base.sw_id = (u16)bit_id;
+
+ bn->l2_fltr_count++;
+
+ head = &bn->l2_fltr_hash_tbl[idx];
+ hlist_add_head_rcu(&fltr->base.hlist, head);
+ bnge_insert_usr_fltr(bn, &fltr->base);
+ return 0;
+}
+
+struct bnge_l2_filter *bnge_lookup_l2_filter(struct bnge_net *bn,
+ struct bnge_l2_key *key,
+ u32 idx)
+{
+ struct bnge_l2_filter *fltr;
+ struct hlist_head *head;
+
+ head = &bn->l2_fltr_hash_tbl[idx];
+ hlist_for_each_entry_rcu(fltr, head, base.hlist) {
+ struct bnge_l2_key *l2_key = &fltr->l2_key;
+
+ if (ether_addr_equal(l2_key->dst_mac_addr, key->dst_mac_addr) &&
+ l2_key->vlan == key->vlan)
+ return fltr;
+ }
+ return NULL;
+}
+
+u64 bnge_lookup_l2_filter_rcu(struct bnge_net *bn,
+ struct bnge_l2_key *key,
+ u32 idx)
+{
+ u64 id = BNGE_FLTR_ID_INVALID;
+ struct bnge_l2_filter *fltr;
+
+ rcu_read_lock();
+ fltr = bnge_lookup_l2_filter(bn, key, idx);
+ if (!fltr)
+ goto exit;
+
+ id = fltr->base.filter_id;
+
+exit:
+ rcu_read_unlock();
+ return id;
+}
+
+static struct bnge_l2_filter *bnge_alloc_l2_filter(struct bnge_net *bn,
+ struct bnge_l2_key *key,
+ gfp_t gfp)
+{
+ struct bnge_l2_filter *fltr;
+ u32 idx;
+ int rc;
+
+ idx = jhash2(&key->filter_key, BNGE_L2_KEY_SIZE, bn->hash_seed) &
+ BNGE_L2_FLTR_HASH_MASK;
+ fltr = bnge_lookup_l2_filter(bn, key, idx);
+ if (fltr)
+ return fltr;
+
+ fltr = kzalloc_obj(*fltr, gfp);
+ if (!fltr)
+ return ERR_PTR(-ENOMEM);
+
+ rc = bnge_init_l2_filter(bn, fltr, key, idx);
+ if (rc) {
+ kfree(fltr);
+ fltr = ERR_PTR(rc);
+ }
+
+ return fltr;
+}
+
+struct bnge_l2_filter *bnge_alloc_user_l2_filter(struct bnge_net *bn,
+ struct bnge_l2_key *key,
+ u8 flags)
+{
+ struct bnge_l2_filter *fltr;
+ u32 idx;
+ int rc;
+
+ idx = jhash2(&key->filter_key, BNGE_L2_KEY_SIZE, bn->hash_seed) &
+ BNGE_L2_FLTR_HASH_MASK;
+ fltr = bnge_lookup_l2_filter(bn, key, idx);
+ if (fltr) {
+ fltr = ERR_PTR(-EEXIST);
+ goto l2_filter_exit;
+ }
+ fltr = kzalloc_obj(*fltr, GFP_ATOMIC);
+ if (!fltr) {
+ fltr = ERR_PTR(-ENOMEM);
+ goto l2_filter_exit;
+ }
+ fltr->base.flags = flags;
+ rc = bnge_init_l2_filter(bn, fltr, key, idx);
+ if (rc) {
+ kfree(fltr);
+ return ERR_PTR(rc);
+ }
+
+l2_filter_exit:
+ return fltr;
+}
+
+void bnge_free_l2_filters(struct bnge_net *bn)
+{
+ int i;
+
+ netdev_assert_locked_or_invisible(bn->netdev);
+
+ for (i = 0; i < BNGE_L2_FLTR_HASH_SIZE; i++) {
+ struct bnge_l2_filter *fltr;
+ struct hlist_head *head;
+ struct hlist_node *tmp;
+
+ head = &bn->l2_fltr_hash_tbl[i];
+ hlist_for_each_entry_safe(fltr, tmp, head, base.hlist)
+ bnge_del_l2_filter(bn, fltr);
+ }
+
+ bitmap_free(bn->l2_fltr_bmap);
+ bn->l2_fltr_bmap = NULL;
+ bn->l2_fltr_count = 0;
+}
+
+void bnge_free_ntp_fltrs(struct bnge_net *bn, bool skip_user)
+{
+ int i;
+
+ netdev_assert_locked_or_invisible(bn->netdev);
+
+ /* Under netdev instance lock and all our NAPIs have been disabled.
+ * It's safe to delete the hash table.
+ */
+ for (i = 0; i < BNGE_NTP_FLTR_HASH_SIZE; i++) {
+ struct bnge_ntuple_filter *fltr;
+ struct hlist_head *head;
+ struct hlist_node *tmp;
+
+ head = &bn->ntp_fltr_hash_tbl[i];
+ hlist_for_each_entry_safe(fltr, tmp, head, base.hlist) {
+ if (skip_user && !list_empty(&fltr->base.list_node))
+ continue;
+ bnge_del_ntp_filter(bn, fltr);
+ }
+ }
+
+ if (skip_user)
+ return;
+
+ bitmap_free(bn->ntp_fltr_bmap);
+ bn->ntp_fltr_bmap = NULL;
+ bn->ntp_fltr_count = 0;
+}
+
+static int bnge_alloc_l2_fltrs_mem(struct bnge_net *bn)
+{
+ int i, rc = 0;
+
+ if (bn->l2_fltr_bmap)
+ return 0;
+
+ for (i = 0; i < BNGE_L2_FLTR_HASH_SIZE; i++)
+ INIT_HLIST_HEAD(&bn->l2_fltr_hash_tbl[i]);
+
+ bn->l2_fltr_count = 0;
+ bn->l2_fltr_bmap = bitmap_zalloc(BNGE_MAX_L2_FLTRS, GFP_KERNEL);
+
+ if (!bn->l2_fltr_bmap)
+ rc = -ENOMEM;
+
+ return rc;
+}
+
+static int bnge_alloc_ntp_fltrs_mem(struct bnge_net *bn)
+{
+ struct bnge_dev *bd = bn->bd;
+ int i, rc = 0;
+
+ if (!(bn->priv_flags & BNGE_NET_EN_NTUPLE) || bn->ntp_fltr_bmap)
+ return 0;
+
+ for (i = 0; i < BNGE_NTP_FLTR_HASH_SIZE; i++)
+ INIT_HLIST_HEAD(&bn->ntp_fltr_hash_tbl[i]);
+
+ bn->ntp_fltr_count = 0;
+ bn->ntp_fltr_bmap = bitmap_zalloc(bd->max_fltr, GFP_KERNEL);
+
+ if (!bn->ntp_fltr_bmap)
+ rc = -ENOMEM;
+
+ return rc;
+}
+
+int bnge_fltrs_mem(struct bnge_net *bn)
+{
+ int rc;
+
+ rc = bnge_alloc_ntp_fltrs_mem(bn);
+ if (rc)
+ return rc;
+
+ rc = bnge_alloc_l2_fltrs_mem(bn);
+ if (rc)
+ goto err_free_ntp_mem;
+
+ return 0;
+
+err_free_ntp_mem:
+ bitmap_free(bn->ntp_fltr_bmap);
+ bn->ntp_fltr_bmap = NULL;
+ return rc;
+}
+
+#define BNGE_IPV4_4TUPLE(bd, fkeys) \
+ (((fkeys)->basic.ip_proto == IPPROTO_TCP && \
+ (bd)->rss_hash_cfg & VNIC_RSS_CFG_REQ_HASH_TYPE_TCP_IPV4) || \
+ ((fkeys)->basic.ip_proto == IPPROTO_UDP && \
+ (bd)->rss_hash_cfg & VNIC_RSS_CFG_REQ_HASH_TYPE_UDP_IPV4))
+
+#define BNGE_IPV6_4TUPLE(bd, fkeys) \
+ (((fkeys)->basic.ip_proto == IPPROTO_TCP && \
+ (bd)->rss_hash_cfg & VNIC_RSS_CFG_REQ_HASH_TYPE_TCP_IPV6) || \
+ ((fkeys)->basic.ip_proto == IPPROTO_UDP && \
+ (bd)->rss_hash_cfg & VNIC_RSS_CFG_REQ_HASH_TYPE_UDP_IPV6))
+
+static u32 bnge_get_rss_flow_tuple_len(struct bnge_dev *bd,
+ struct flow_keys *fkeys)
+{
+ if (fkeys->basic.n_proto == htons(ETH_P_IP)) {
+ if (BNGE_IPV4_4TUPLE(bd, fkeys))
+ return sizeof(fkeys->addrs.v4addrs) +
+ sizeof(fkeys->ports);
+
+ if (bd->rss_hash_cfg & VNIC_RSS_CFG_REQ_HASH_TYPE_IPV4)
+ return sizeof(fkeys->addrs.v4addrs);
+ }
+
+ if (fkeys->basic.n_proto == htons(ETH_P_IPV6)) {
+ if (BNGE_IPV6_4TUPLE(bd, fkeys))
+ return sizeof(fkeys->addrs.v6addrs) +
+ sizeof(fkeys->ports);
+
+ if (bd->rss_hash_cfg & VNIC_RSS_CFG_REQ_HASH_TYPE_IPV6)
+ return sizeof(fkeys->addrs.v6addrs);
+ }
+
+ return 0;
+}
+
+static u32 bnge_toeplitz(struct bnge_net *bn, struct flow_keys *fkeys,
+ const unsigned char *key)
+{
+ u64 prefix = bn->toeplitz_prefix, hash = 0;
+ struct bnge_ipv4_tuple tuple4;
+ struct bnge_ipv6_tuple tuple6;
+ struct bnge_dev *bd = bn->bd;
+ u8 *four_tuple;
+ int i, j, len;
+
+ len = bnge_get_rss_flow_tuple_len(bd, fkeys);
+ if (!len)
+ return 0;
+
+ if (fkeys->basic.n_proto == htons(ETH_P_IP)) {
+ tuple4.v4addrs = fkeys->addrs.v4addrs;
+ tuple4.ports = fkeys->ports;
+ four_tuple = (u8 *)&tuple4;
+ } else {
+ tuple6.v6addrs = fkeys->addrs.v6addrs;
+ tuple6.ports = fkeys->ports;
+ four_tuple = (u8 *)&tuple6;
+ }
+
+ for (i = 0, j = 8; i < len; i++, j++) {
+ u8 byte = four_tuple[i];
+ int bit;
+
+ for (bit = 0; bit < 8; bit++, prefix <<= 1, byte <<= 1) {
+ if (byte & 0x80)
+ hash ^= prefix;
+ }
+ prefix |= (j < HW_HASH_KEY_SIZE) ? key[j] : 0;
+ }
+
+ /* The valid part of the hash is in the upper 32 bits. */
+ return (hash >> 32) & BNGE_NTP_FLTR_HASH_MASK;
+}
+
+u32 bnge_get_ntp_filter_idx(struct bnge_net *bn, struct flow_keys *fkeys,
+ const struct sk_buff *skb)
+{
+ struct bnge_vnic_info *vnic;
+
+ if (skb)
+ return skb_get_hash_raw(skb) & BNGE_NTP_FLTR_HASH_MASK;
+
+ vnic = &bn->vnic_info[BNGE_VNIC_DEFAULT];
+ return bnge_toeplitz(bn, fkeys, (void *)vnic->rss_hash_key);
+}
+
+int bnge_insert_ntp_filter(struct bnge_net *bn, struct bnge_ntuple_filter *fltr,
+ u32 idx)
+{
+ struct hlist_head *head;
+ int bit_id;
+
+ spin_lock_bh(&bn->ntp_fltr_lock);
+ bit_id = bitmap_find_free_region(bn->ntp_fltr_bmap,
+ bn->bd->max_fltr, 0);
+ if (bit_id < 0) {
+ spin_unlock_bh(&bn->ntp_fltr_lock);
+ return -ENOMEM;
+ }
+
+ fltr->base.sw_id = (u16)bit_id;
+ fltr->base.type = BNGE_FLTR_TYPE_NTUPLE;
+ fltr->base.flags |= BNGE_ACT_RING_DST;
+
+ head = &bn->ntp_fltr_hash_tbl[idx];
+ hlist_add_head_rcu(&fltr->base.hlist, head);
+
+ bnge_insert_usr_fltr(bn, &fltr->base);
+ bn->ntp_fltr_count++;
+
+ spin_unlock_bh(&bn->ntp_fltr_lock);
+
+ return 0;
+}
+
+static bool bnge_fltr_match(struct bnge_ntuple_filter *f1,
+ struct bnge_ntuple_filter *f2)
+{
+ struct bnge_flow_masks *masks1 = &f1->fmasks;
+ struct bnge_flow_masks *masks2 = &f2->fmasks;
+ struct flow_keys *keys1 = &f1->fkeys;
+ struct flow_keys *keys2 = &f2->fkeys;
+
+ if (keys1->basic.n_proto != keys2->basic.n_proto ||
+ keys1->basic.ip_proto != keys2->basic.ip_proto)
+ return false;
+
+ if (keys1->basic.n_proto == htons(ETH_P_IP)) {
+ if (keys1->addrs.v4addrs.src != keys2->addrs.v4addrs.src ||
+ masks1->addrs.v4addrs.src != masks2->addrs.v4addrs.src ||
+ keys1->addrs.v4addrs.dst != keys2->addrs.v4addrs.dst ||
+ masks1->addrs.v4addrs.dst != masks2->addrs.v4addrs.dst)
+ return false;
+ } else {
+ if (!ipv6_addr_equal(&keys1->addrs.v6addrs.src,
+ &keys2->addrs.v6addrs.src) ||
+ !ipv6_addr_equal(&masks1->addrs.v6addrs.src,
+ &masks2->addrs.v6addrs.src) ||
+ !ipv6_addr_equal(&keys1->addrs.v6addrs.dst,
+ &keys2->addrs.v6addrs.dst) ||
+ !ipv6_addr_equal(&masks1->addrs.v6addrs.dst,
+ &masks2->addrs.v6addrs.dst))
+ return false;
+ }
+
+ return keys1->ports.src == keys2->ports.src &&
+ masks1->ports.src == masks2->ports.src &&
+ keys1->ports.dst == keys2->ports.dst &&
+ masks1->ports.dst == masks2->ports.dst &&
+ keys1->control.flags == keys2->control.flags &&
+ f1->l2_filter_id == f2->l2_filter_id;
+}
+
+struct bnge_ntuple_filter *
+bnge_lookup_ntp_filter_from_idx(struct bnge_net *bn,
+ struct bnge_ntuple_filter *fltr, u32 idx)
+{
+ struct bnge_ntuple_filter *f;
+ struct hlist_head *head;
+
+ head = &bn->ntp_fltr_hash_tbl[idx];
+ hlist_for_each_entry_rcu(f, head, base.hlist) {
+ if (bnge_fltr_match(f, fltr))
+ return f;
+ }
+ return NULL;
+}
+
+int bnge_hwrm_set_vnic_filter(struct bnge_net *bn, u16 vnic_id, u16 idx,
+ const u8 *mac_addr)
+{
+ struct bnge_l2_filter *fltr;
+ struct bnge_l2_key key;
+ int rc;
+
+ ether_addr_copy(key.dst_mac_addr, mac_addr);
+ key.vlan = 0;
+ fltr = bnge_alloc_l2_filter(bn, &key, GFP_KERNEL);
+ if (IS_ERR(fltr))
+ return PTR_ERR(fltr);
+
+ fltr->base.fw_vnic_id = bn->vnic_info[vnic_id].fw_vnic_id;
+ rc = bnge_hwrm_l2_filter_alloc(bn->bd, fltr);
+ if (rc)
+ goto err_del_l2_filter;
+ bn->vnic_info[vnic_id].l2_filters[idx] = fltr;
+ return rc;
+
+err_del_l2_filter:
+ bnge_del_l2_filter(bn, fltr);
+ return rc;
+}
+
+static void bnge_cfg_one_usr_fltr(struct bnge_net *bn,
+ struct bnge_filter_base *fltr)
+{
+ struct bnge_ntuple_filter *ntp_fltr;
+ struct bnge_l2_filter *l2_fltr;
+
+ if (list_empty(&fltr->list_node))
+ return;
+
+ if (fltr->type == BNGE_FLTR_TYPE_NTUPLE) {
+ ntp_fltr = container_of(fltr, struct bnge_ntuple_filter, base);
+ l2_fltr = bn->vnic_info[BNGE_VNIC_DEFAULT].l2_filters[0];
+ ntp_fltr->l2_filter_id = l2_fltr->base.filter_id;
+ if (bnge_hwrm_cfa_ntuple_filter_alloc(bn->bd, ntp_fltr)) {
+ netdev_err(bn->netdev,
+ "restoring previously configured ntuple filter id %d failed\n",
+ fltr->sw_id);
+ bnge_del_ntp_filter(bn, ntp_fltr);
+ }
+ } else if (fltr->type == BNGE_FLTR_TYPE_L2) {
+ l2_fltr = container_of(fltr, struct bnge_l2_filter, base);
+ if (bnge_hwrm_l2_filter_alloc(bn->bd, l2_fltr)) {
+ netdev_err(bn->netdev,
+ "restoring previously configured l2 filter id %d failed\n",
+ fltr->sw_id);
+ bnge_del_l2_filter(bn, l2_fltr);
+ }
+ }
+}
+
+void bnge_cfg_usr_fltrs(struct bnge_net *bn)
+{
+ struct bnge_filter_base *usr_fltr, *tmp;
+
+ list_for_each_entry_safe(usr_fltr, tmp, &bn->usr_fltr_list, list_node)
+ bnge_cfg_one_usr_fltr(bn, usr_fltr);
+}
+
+void bnge_cfg_ntp_filters(struct bnge_net *bn)
+{
+#ifdef CONFIG_RFS_ACCEL
+ struct bnge_ntuple_filter *fltr, *tmp;
+ LIST_HEAD(install_list);
+ LIST_HEAD(expire_list);
+ int i, rc;
+
+ rcu_read_lock();
+ for (i = 0; i < BNGE_NTP_FLTR_HASH_SIZE; i++) {
+ hlist_for_each_entry_rcu(fltr, &bn->ntp_fltr_hash_tbl[i],
+ base.hlist) {
+ if (test_bit(BNGE_FLTR_VALID, &fltr->base.state)) {
+ if (fltr->base.flags & BNGE_ACT_NO_AGING)
+ continue;
+ if (rps_may_expire_flow(bn->netdev,
+ fltr->base.rxq,
+ fltr->flow_id,
+ fltr->base.sw_id))
+ list_add(&fltr->base.list_node,
+ &expire_list);
+ } else {
+ list_add(&fltr->base.list_node, &install_list);
+ }
+ }
+ }
+ rcu_read_unlock();
+
+ list_for_each_entry_safe(fltr, tmp, &install_list, base.list_node) {
+ list_del_init(&fltr->base.list_node);
+ rc = bnge_hwrm_cfa_ntuple_filter_alloc(bn->bd, fltr);
+ if (rc)
+ bnge_del_ntp_filter(bn, fltr);
+ else
+ set_bit(BNGE_FLTR_VALID, &fltr->base.state);
+ }
+
+ list_for_each_entry_safe(fltr, tmp, &expire_list, base.list_node) {
+ list_del_init(&fltr->base.list_node);
+ bnge_hwrm_cfa_ntuple_filter_free(bn->bd, fltr);
+ bnge_del_ntp_filter(bn, fltr);
+ }
+#endif
+}
diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_filter.h b/drivers/net/ethernet/broadcom/bnge/bnge_filter.h
new file mode 100644
index 0000000000000..c2f2f754452c8
--- /dev/null
+++ b/drivers/net/ethernet/broadcom/bnge/bnge_filter.h
@@ -0,0 +1,122 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2026 Broadcom */
+
+#ifndef _BNGE_FILTER_H_
+#define _BNGE_FILTER_H_
+
+#include <linux/jhash.h>
+
+enum {
+ BNGE_FLTR_TYPE_L2 = 1,
+ BNGE_FLTR_TYPE_NTUPLE = 2
+};
+
+enum {
+ BNGE_FLTR_VALID,
+ BNGE_FLTR_FW_DELETED
+};
+
+enum {
+ BNGE_ACT_DROP = 0x1,
+ BNGE_ACT_RING_DST = 0x2,
+ BNGE_ACT_NO_AGING = 0x4,
+ BNGE_ACT_RSS_CTX = 0x8
+};
+
+#define BNGE_MAX_L2_FLTRS 128
+#define BNGE_MAX_NTUPLE_FLTRS (8 << 10)
+
+struct bnge_filter_base {
+ struct hlist_node hlist;
+ struct list_head list_node;
+ __le64 filter_id;
+ u8 type;
+ u8 flags;
+ u16 sw_id;
+ u16 rxq;
+ u16 fw_vnic_id;
+ u16 vf_idx;
+ unsigned long state;
+
+ struct rcu_head rcu;
+};
+
+struct bnge_l2_key {
+ union {
+ struct {
+ u8 dst_mac_addr[ETH_ALEN];
+ u16 vlan;
+ };
+ u32 filter_key;
+ };
+};
+
+#define BNGE_L2_KEY_SIZE (sizeof(struct bnge_l2_key) / 4)
+struct bnge_l2_filter {
+ /* base filter must be the first member */
+ struct bnge_filter_base base;
+ struct bnge_l2_key l2_key;
+};
+
+struct bnge_ipv4_tuple {
+ struct flow_dissector_key_ipv4_addrs v4addrs;
+ struct flow_dissector_key_ports ports;
+};
+
+struct bnge_ipv6_tuple {
+ struct flow_dissector_key_ipv6_addrs v6addrs;
+ struct flow_dissector_key_ports ports;
+};
+
+struct bnge_flow_masks {
+ struct flow_dissector_key_ports ports;
+ struct flow_dissector_key_addrs addrs;
+};
+
+extern const struct bnge_flow_masks BNGE_FLOW_MASK_NONE;
+extern const struct bnge_flow_masks BNGE_FLOW_IPV6_MASK_ALL;
+extern const struct bnge_flow_masks BNGE_FLOW_IPV4_MASK_ALL;
+
+struct bnge_ntuple_filter {
+ /* base filter must be the first member */
+ struct bnge_filter_base base;
+ struct flow_keys fkeys;
+ struct bnge_flow_masks fmasks;
+ __le64 l2_filter_id;
+ u32 flow_id;
+};
+
+#define BNGE_L2_FLTR_IS_NTP_MAC(fltr) ((fltr)->base.flags)
+
+#define BNGE_FLTR_ID_INVALID 0xffffffffffffffffULL
+
+void bnge_free_ntp_fltrs(struct bnge_net *bn, bool skip_user_filters);
+u32 bnge_get_ntp_filter_idx(struct bnge_net *bn, struct flow_keys *fkeys,
+ const struct sk_buff *skb);
+int bnge_insert_ntp_filter(struct bnge_net *bn, struct bnge_ntuple_filter *fltr,
+ u32 idx);
+struct bnge_ntuple_filter *
+bnge_lookup_ntp_filter_from_idx(struct bnge_net *bn,
+ struct bnge_ntuple_filter *fltr, u32 idx);
+void bnge_del_l2_filter(struct bnge_net *bn, struct bnge_l2_filter *fltr);
+void bnge_del_l2_filter_rcu(struct bnge_net *bn, struct bnge_l2_filter *fltr);
+struct bnge_l2_filter *bnge_lookup_l2_filter(struct bnge_net *bn,
+ struct bnge_l2_key *key,
+ u32 idx);
+u64 bnge_lookup_l2_filter_rcu(struct bnge_net *bn,
+ struct bnge_l2_key *key,
+ u32 idx);
+void bnge_free_l2_filters(struct bnge_net *bn);
+int bnge_hwrm_set_vnic_filter(struct bnge_net *bn, u16 vnic_id, u16 idx,
+ const u8 *mac_addr);
+struct bnge_l2_filter *bnge_alloc_user_l2_filter(struct bnge_net *bn,
+ struct bnge_l2_key *key,
+ u8 flags);
+int bnge_fltrs_mem(struct bnge_net *bn);
+void bnge_del_ntp_filter(struct bnge_net *bn,
+ struct bnge_ntuple_filter *nfltr);
+void bnge_del_ntp_filter_rcu(struct bnge_net *bn,
+ struct bnge_ntuple_filter *fltr);
+void bnge_cfg_usr_fltrs(struct bnge_net *bn);
+void bnge_cfg_ntp_filters(struct bnge_net *bn);
+#endif /* _BNGE_FILTER_H_ */
diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c b/drivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c
index 651c5e783516c..6df1ad896610a 100644
--- a/drivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c
+++ b/drivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c
@@ -8,6 +8,7 @@
#include <linux/bnge/hsi.h>
#include <linux/if_vlan.h>
#include <net/netdev_queues.h>
+#include <linux/ethtool.h>
#include "bnge.h"
#include "bnge_hwrm.h"
@@ -15,6 +16,8 @@
#include "bnge_rmem.h"
#include "bnge_resc.h"
#include "bnge_netdev.h"
+#include "bnge_vnic.h"
+#include "bnge_filter.h"
static const u16 bnge_async_events_arr[] = {
ASYNC_EVENT_CMPL_EVENT_ID_LINK_STATUS_CHANGE,
@@ -674,6 +677,8 @@ int bnge_hwrm_vnic_qcaps(struct bnge_dev *bd)
bd->rss_cap |= BNGE_RSS_CAP_ESP_V4_RSS_CAP;
if (flags & VNIC_QCAPS_RESP_FLAGS_RSS_IPSEC_ESP_SPI_IPV6_CAP)
bd->rss_cap |= BNGE_RSS_CAP_ESP_V6_RSS_CAP;
+ if (flags & VNIC_QCAPS_RESP_FLAGS_RSS_IPV6_FLOW_LABEL_CAP)
+ bd->rss_cap |= BNGE_RSS_CAP_IPV6_FLOW_LABEL_RSS_CAP;
}
bnge_hwrm_req_drop(bd, req);
@@ -901,15 +906,17 @@ int bnge_hwrm_l2_filter_alloc(struct bnge_dev *bd, struct bnge_l2_filter *fltr)
{
struct hwrm_cfa_l2_filter_alloc_output *resp;
struct hwrm_cfa_l2_filter_alloc_input *req;
+ u32 flags;
int rc;
rc = bnge_hwrm_req_init(bd, req, HWRM_CFA_L2_FILTER_ALLOC);
if (rc)
return rc;
- req->flags = cpu_to_le32(CFA_L2_FILTER_ALLOC_REQ_FLAGS_PATH_RX);
-
- req->flags |= cpu_to_le32(CFA_L2_FILTER_ALLOC_REQ_FLAGS_OUTERMOST);
+ flags = CFA_L2_FILTER_ALLOC_REQ_FLAGS_PATH_RX |
+ CFA_L2_FILTER_ALLOC_REQ_FLAGS_TRAFFIC_L2 |
+ CFA_L2_FILTER_ALLOC_REQ_FLAGS_OUTERMOST;
+ req->flags = cpu_to_le32(flags);
req->dst_id = cpu_to_le16(fltr->base.fw_vnic_id);
req->enables =
cpu_to_le32(CFA_L2_FILTER_ALLOC_REQ_ENABLES_L2_ADDR |
@@ -928,15 +935,155 @@ int bnge_hwrm_l2_filter_alloc(struct bnge_dev *bd, struct bnge_l2_filter *fltr)
req->l2_ivlan_mask = cpu_to_le16(0xfff);
}
+ if (BNGE_L2_FLTR_IS_NTP_MAC(fltr)) {
+ req->enables |= cpu_to_le32(CFA_L2_FILTER_ALLOC_REQ_ENABLES_RFS_RING_TBL_IDX);
+ req->rfs_ring_tbl_idx = cpu_to_le16(fltr->base.rxq);
+ }
+
resp = bnge_hwrm_req_hold(bd, req);
rc = bnge_hwrm_req_send(bd, req);
- if (!rc)
+ if (!rc) {
fltr->base.filter_id = resp->l2_filter_id;
+ set_bit(BNGE_FLTR_VALID, &fltr->base.state);
+ }
bnge_hwrm_req_drop(bd, req);
return rc;
}
+int bnge_hwrm_cfa_ntuple_filter_free(struct bnge_dev *bd,
+ struct bnge_ntuple_filter *fltr)
+{
+ struct hwrm_cfa_ntuple_filter_free_input *req;
+ int rc;
+
+ set_bit(BNGE_FLTR_FW_DELETED, &fltr->base.state);
+ if (!test_bit(BNGE_STATE_OPEN, &bd->state))
+ return 0;
+
+ rc = bnge_hwrm_req_init(bd, req, HWRM_CFA_NTUPLE_FILTER_FREE);
+ if (rc)
+ return rc;
+
+ req->ntuple_filter_id = fltr->base.filter_id;
+ return bnge_hwrm_req_send(bd, req);
+}
+
+#define BNGE_NTP_FLTR_FLAGS \
+ (CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_L2_FILTER_ID | \
+ CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_ETHERTYPE | \
+ CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_IPADDR_TYPE | \
+ CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_SRC_IPADDR | \
+ CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_SRC_IPADDR_MASK | \
+ CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_DST_IPADDR | \
+ CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_DST_IPADDR_MASK | \
+ CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_IP_PROTOCOL | \
+ CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_SRC_PORT | \
+ CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_SRC_PORT_MASK | \
+ CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_DST_PORT | \
+ CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_DST_PORT_MASK | \
+ CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_DST_ID)
+
+#define BNGE_NTP_TUNNEL_FLTR_FLAG \
+ CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_TUNNEL_TYPE
+
+static void
+bnge_cfg_rfs_ring_tbl_idx(struct bnge_dev *bd,
+ struct hwrm_cfa_ntuple_filter_alloc_input *req,
+ struct bnge_ntuple_filter *fltr)
+{
+ struct bnge_net *bn = netdev_priv(bd->netdev);
+ struct bnge_vnic_info *def_vnic;
+ u32 rxq = fltr->base.rxq;
+ u32 enables;
+
+ if (fltr->base.flags & BNGE_ACT_RSS_CTX) {
+ struct ethtool_rxfh_context *ctx;
+ struct bnge_rss_ctx *rss_ctx;
+ struct bnge_vnic_info *vnic;
+
+ ctx = xa_load(&bd->netdev->ethtool->rss_ctx,
+ fltr->base.fw_vnic_id);
+ if (ctx) {
+ rss_ctx = ethtool_rxfh_context_priv(ctx);
+ vnic = &rss_ctx->vnic;
+
+ req->dst_id = cpu_to_le16(vnic->fw_vnic_id);
+ }
+ return;
+ }
+
+ def_vnic = &bn->vnic_info[BNGE_VNIC_NTUPLE];
+ req->dst_id = cpu_to_le16(def_vnic->fw_vnic_id);
+ enables = CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_RFS_RING_TBL_IDX;
+ req->enables |= cpu_to_le32(enables);
+ req->rfs_ring_tbl_idx = cpu_to_le32(rxq);
+}
+
+int bnge_hwrm_cfa_ntuple_filter_alloc(struct bnge_dev *bd,
+ struct bnge_ntuple_filter *fltr)
+{
+ struct hwrm_cfa_ntuple_filter_alloc_output *resp;
+ struct hwrm_cfa_ntuple_filter_alloc_input *req;
+ struct bnge_flow_masks *masks = &fltr->fmasks;
+ struct flow_keys *keys = &fltr->fkeys;
+ int rc;
+
+ rc = bnge_hwrm_req_init(bd, req, HWRM_CFA_NTUPLE_FILTER_ALLOC);
+ if (rc)
+ return rc;
+
+ req->l2_filter_id = fltr->l2_filter_id;
+
+ if (fltr->base.flags & BNGE_ACT_DROP)
+ req->flags =
+ cpu_to_le32(CFA_NTUPLE_FILTER_ALLOC_REQ_FLAGS_DROP);
+ else
+ bnge_cfg_rfs_ring_tbl_idx(bd, req, fltr);
+
+ req->enables |= cpu_to_le32(BNGE_NTP_FLTR_FLAGS);
+
+ req->ethertype = htons(ETH_P_IP);
+ req->ip_addr_type = CFA_NTUPLE_FILTER_ALLOC_REQ_IP_ADDR_TYPE_IPV4;
+ req->ip_protocol = keys->basic.ip_proto;
+
+ if (keys->basic.n_proto == htons(ETH_P_IPV6)) {
+ req->ethertype = htons(ETH_P_IPV6);
+ req->ip_addr_type =
+ CFA_NTUPLE_FILTER_ALLOC_REQ_IP_ADDR_TYPE_IPV6;
+ memcpy(&req->src_ipaddr, &keys->addrs.v6addrs.src,
+ sizeof(struct in6_addr));
+ memcpy(&req->src_ipaddr_mask, &masks->addrs.v6addrs.src,
+ sizeof(struct in6_addr));
+ memcpy(&req->dst_ipaddr, &keys->addrs.v6addrs.dst,
+ sizeof(struct in6_addr));
+ memcpy(&req->dst_ipaddr_mask, &masks->addrs.v6addrs.dst,
+ sizeof(struct in6_addr));
+ } else {
+ req->src_ipaddr[0] = keys->addrs.v4addrs.src;
+ req->src_ipaddr_mask[0] = masks->addrs.v4addrs.src;
+ req->dst_ipaddr[0] = keys->addrs.v4addrs.dst;
+ req->dst_ipaddr_mask[0] = masks->addrs.v4addrs.dst;
+ }
+ if (keys->control.flags & FLOW_DIS_ENCAPSULATION) {
+ req->enables |= cpu_to_le32(BNGE_NTP_TUNNEL_FLTR_FLAG);
+ req->tunnel_type =
+ CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_ANYTUNNEL;
+ }
+
+ req->src_port = keys->ports.src;
+ req->src_port_mask = masks->ports.src;
+ req->dst_port = keys->ports.dst;
+ req->dst_port_mask = masks->ports.dst;
+
+ resp = bnge_hwrm_req_hold(bd, req);
+ rc = bnge_hwrm_req_send(bd, req);
+ if (!rc)
+ fltr->base.filter_id = resp->ntuple_filter_id;
+ bnge_hwrm_req_drop(bd, req);
+ return rc;
+}
+
int bnge_hwrm_cfa_l2_set_rx_mask(struct bnge_dev *bd,
struct bnge_vnic_info *vnic)
{
@@ -1634,3 +1781,30 @@ int bnge_hwrm_port_qstats(struct bnge_dev *bd, u8 flags)
return bnge_hwrm_req_send(bd, req);
}
+
+int bnge_hwrm_cfa_adv_flow_mgnt_qcaps(struct bnge_dev *bd)
+{
+ struct hwrm_cfa_adv_flow_mgnt_qcaps_output *resp;
+ struct hwrm_cfa_adv_flow_mgnt_qcaps_input *req;
+ u32 flags;
+ int rc;
+
+ rc = bnge_hwrm_req_init(bd, req, HWRM_CFA_ADV_FLOW_MGNT_QCAPS);
+ if (rc)
+ return rc;
+
+ resp = bnge_hwrm_req_hold(bd, req);
+ rc = bnge_hwrm_req_send(bd, req);
+ if (rc)
+ goto err_hwrm_drop;
+
+ flags = le32_to_cpu(resp->flags);
+
+ if (flags &
+ CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_NTUPLE_FLOW_RX_EXT_IP_PROTO_SUPPORTED)
+ bd->fw_cap |= BNGE_FW_CAP_CFA_NTUPLE_RX_EXT_IP_PROTO;
+
+err_hwrm_drop:
+ bnge_hwrm_req_drop(bd, req);
+ return rc;
+}
diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.h b/drivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.h
index bf452e390d5bd..5c5bb864fc4da 100644
--- a/drivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.h
+++ b/drivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.h
@@ -4,6 +4,8 @@
#ifndef _BNGE_HWRM_LIB_H_
#define _BNGE_HWRM_LIB_H_
+struct bnge_l2_filter;
+
#define BNGE_PLC_EN_JUMBO_THRES_VALID \
VNIC_PLCMODES_CFG_REQ_ENABLES_JUMBO_THRESH_VALID
#define BNGE_PLC_EN_HDS_THRES_VALID \
@@ -11,6 +13,8 @@
#define BNGE_VNIC_CFG_ROCE_DUAL_MODE \
VNIC_CFG_REQ_FLAGS_ROCE_DUAL_VNIC_MODE
+struct bnge_ntuple_filter;
+
int bnge_hwrm_ver_get(struct bnge_dev *bd);
int bnge_hwrm_func_reset(struct bnge_dev *bd);
int bnge_hwrm_fw_set_time(struct bnge_dev *bd);
@@ -65,4 +69,10 @@ int bnge_hwrm_shutdown_link(struct bnge_dev *bd);
int bnge_hwrm_port_qstats(struct bnge_dev *bd, u8 flags);
int bnge_hwrm_port_qstats_ext(struct bnge_dev *bd, u8 flags);
int bnge_hwrm_func_qstat_ext(struct bnge_dev *bd, struct bnge_stats_mem *stats);
+int bnge_hwrm_cfa_adv_flow_mgnt_qcaps(struct bnge_dev *bd);
+
+int bnge_hwrm_cfa_ntuple_filter_alloc(struct bnge_dev *bd,
+ struct bnge_ntuple_filter *fltr);
+int bnge_hwrm_cfa_ntuple_filter_free(struct bnge_dev *bd,
+ struct bnge_ntuple_filter *fltr);
#endif /* _BNGE_HWRM_LIB_H_ */
diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_netdev.c b/drivers/net/ethernet/broadcom/bnge/bnge_netdev.c
index a4288f0258f87..48de88924a7f3 100644
--- a/drivers/net/ethernet/broadcom/bnge/bnge_netdev.c
+++ b/drivers/net/ethernet/broadcom/bnge/bnge_netdev.c
@@ -18,6 +18,7 @@
#include <net/ip.h>
#include <linux/skbuff.h>
#include <net/page_pool/helpers.h>
+#include <linux/cpu_rmap.h>
#include "bnge.h"
#include "bnge_hwrm.h"
@@ -25,6 +26,8 @@
#include "bnge_ethtool.h"
#include "bnge_rmem.h"
#include "bnge_txrx.h"
+#include "bnge_vnic.h"
+#include "bnge_filter.h"
#define BNGE_RING_TO_TC_OFF(bd, tx) \
((tx) % (bd)->tx_nr_rings_per_tc)
@@ -301,6 +304,11 @@ static void bnge_timer(struct timer_list *t)
if (BNGE_LINK_IS_UP(bd) && bn->stats_coal_ticks)
bnge_queue_sp_work(bn, BNGE_PERIODIC_STATS_SP_EVENT);
+#ifdef CONFIG_RFS_ACCEL
+ if ((bn->priv_flags & BNGE_NET_EN_NTUPLE) && bn->ntp_fltr_count)
+ bnge_queue_sp_work(bn, BNGE_RX_NTP_FLTR_SP_EVENT);
+#endif
+
mod_timer(&bn->timer, jiffies + bn->current_interval);
}
@@ -442,6 +450,9 @@ static void bnge_sp_task(struct work_struct *work)
bnge_init_ethtool_link_settings(bn);
}
+ if (test_and_clear_bit(BNGE_RX_NTP_FLTR_SP_EVENT, &bn->sp_event))
+ bnge_cfg_ntp_filters(bn);
+
netdev_unlock(bn->netdev);
}
@@ -1147,12 +1158,10 @@ static int bnge_alloc_vnic_attributes(struct bnge_net *bn)
static int bnge_alloc_vnics(struct bnge_net *bn)
{
- int num_vnics;
+ int num_vnics = 1;
- /* Allocate only 1 VNIC for now
- * Additional VNICs will be added based on RFS/NTUPLE in future patches
- */
- num_vnics = 1;
+ if (bn->priv_flags & BNGE_NET_EN_NTUPLE)
+ num_vnics++;
bn->vnic_info = kzalloc_objs(struct bnge_vnic_info, num_vnics);
if (!bn->vnic_info)
@@ -1205,6 +1214,10 @@ static void bnge_free_core(struct bnge_net *bn)
bnge_free_ring_stats(bn);
bnge_free_ring_grps(bn);
bnge_free_vnics(bn);
+
+ /* Free non-user filters only */
+ bnge_free_ntp_fltrs(bn, true);
+
kfree(bn->tx_ring_map);
bn->tx_ring_map = NULL;
kfree(bn->tx_ring);
@@ -1293,6 +1306,10 @@ static int bnge_alloc_core(struct bnge_net *bn)
bnge_init_stats(bn);
+ rc = bnge_fltrs_mem(bn);
+ if (rc)
+ goto err_free_core;
+
rc = bnge_alloc_vnics(bn);
if (rc)
goto err_free_core;
@@ -1318,6 +1335,10 @@ static int bnge_alloc_core(struct bnge_net *bn)
bn->vnic_info[BNGE_VNIC_DEFAULT].flags |= BNGE_VNIC_RSS_FLAG |
BNGE_VNIC_MCAST_FLAG |
BNGE_VNIC_UCAST_FLAG;
+ if (bn->priv_flags & BNGE_NET_EN_NTUPLE)
+ bn->vnic_info[BNGE_VNIC_NTUPLE].flags |= BNGE_VNIC_RSS_FLAG |
+ BNGE_VNIC_NTUPLE_FLAG;
+
rc = bnge_alloc_vnic_attributes(bn);
if (rc)
goto err_free_core;
@@ -1977,174 +1998,6 @@ static int bnge_hwrm_ring_alloc(struct bnge_net *bn)
return rc;
}
-void bnge_fill_hw_rss_tbl(struct bnge_net *bn, struct bnge_vnic_info *vnic)
-{
- __le16 *ring_tbl = vnic->rss_table;
- struct bnge_rx_ring_info *rxr;
- struct bnge_dev *bd = bn->bd;
- u16 tbl_size, i;
-
- tbl_size = bnge_get_rxfh_indir_size(bd);
-
- for (i = 0; i < tbl_size; i++) {
- u32 j;
-
- j = bd->rss_indir_tbl[i];
- rxr = &bn->rx_ring[j];
-
- *ring_tbl++ = cpu_to_le16(rxr->rx_ring_struct.fw_ring_id);
- *ring_tbl++ = cpu_to_le16(bnge_cp_ring_for_rx(rxr));
- }
-}
-
-static int bnge_hwrm_vnic_rss_cfg(struct bnge_net *bn,
- struct bnge_vnic_info *vnic)
-{
- int rc;
-
- rc = bnge_hwrm_vnic_set_rss(bn, vnic, true);
- if (rc) {
- netdev_err(bn->netdev, "hwrm vnic %d set rss failure rc: %d\n",
- vnic->vnic_id, rc);
- return rc;
- }
- rc = bnge_hwrm_vnic_cfg(bn, vnic);
- if (rc)
- netdev_err(bn->netdev, "hwrm vnic %d cfg failure rc: %d\n",
- vnic->vnic_id, rc);
- return rc;
-}
-
-static int bnge_setup_vnic(struct bnge_net *bn, struct bnge_vnic_info *vnic)
-{
- struct bnge_dev *bd = bn->bd;
- int rc, i, nr_ctxs;
-
- nr_ctxs = bnge_cal_nr_rss_ctxs(bd->rx_nr_rings);
- for (i = 0; i < nr_ctxs; i++) {
- rc = bnge_hwrm_vnic_ctx_alloc(bd, vnic, i);
- if (rc) {
- netdev_err(bn->netdev, "hwrm vnic %d ctx %d alloc failure rc: %d\n",
- vnic->vnic_id, i, rc);
- return -ENOMEM;
- }
- bn->rsscos_nr_ctxs++;
- }
-
- rc = bnge_hwrm_vnic_rss_cfg(bn, vnic);
- if (rc)
- return rc;
-
- if (bnge_is_agg_reqd(bd)) {
- rc = bnge_hwrm_vnic_set_hds(bn, vnic);
- if (rc)
- netdev_err(bn->netdev, "hwrm vnic %d set hds failure rc: %d\n",
- vnic->vnic_id, rc);
- }
- return rc;
-}
-
-static void bnge_del_l2_filter(struct bnge_net *bn, struct bnge_l2_filter *fltr)
-{
- if (!refcount_dec_and_test(&fltr->refcnt))
- return;
- hlist_del_rcu(&fltr->base.hash);
- kfree_rcu(fltr, base.rcu);
-}
-
-static void bnge_init_l2_filter(struct bnge_net *bn,
- struct bnge_l2_filter *fltr,
- struct bnge_l2_key *key, u32 idx)
-{
- struct hlist_head *head;
-
- ether_addr_copy(fltr->l2_key.dst_mac_addr, key->dst_mac_addr);
- fltr->l2_key.vlan = key->vlan;
- fltr->base.type = BNGE_FLTR_TYPE_L2;
-
- head = &bn->l2_fltr_hash_tbl[idx];
- hlist_add_head_rcu(&fltr->base.hash, head);
- refcount_set(&fltr->refcnt, 1);
-}
-
-static struct bnge_l2_filter *__bnge_lookup_l2_filter(struct bnge_net *bn,
- struct bnge_l2_key *key,
- u32 idx)
-{
- struct bnge_l2_filter *fltr;
- struct hlist_head *head;
-
- head = &bn->l2_fltr_hash_tbl[idx];
- hlist_for_each_entry_rcu(fltr, head, base.hash) {
- struct bnge_l2_key *l2_key = &fltr->l2_key;
-
- if (ether_addr_equal(l2_key->dst_mac_addr, key->dst_mac_addr) &&
- l2_key->vlan == key->vlan)
- return fltr;
- }
- return NULL;
-}
-
-static struct bnge_l2_filter *bnge_lookup_l2_filter(struct bnge_net *bn,
- struct bnge_l2_key *key,
- u32 idx)
-{
- struct bnge_l2_filter *fltr;
-
- rcu_read_lock();
- fltr = __bnge_lookup_l2_filter(bn, key, idx);
- if (fltr)
- refcount_inc(&fltr->refcnt);
- rcu_read_unlock();
- return fltr;
-}
-
-static struct bnge_l2_filter *bnge_alloc_l2_filter(struct bnge_net *bn,
- struct bnge_l2_key *key,
- gfp_t gfp)
-{
- struct bnge_l2_filter *fltr;
- u32 idx;
-
- idx = jhash2(&key->filter_key, BNGE_L2_KEY_SIZE, bn->hash_seed) &
- BNGE_L2_FLTR_HASH_MASK;
- fltr = bnge_lookup_l2_filter(bn, key, idx);
- if (fltr)
- return fltr;
-
- fltr = kzalloc_obj(*fltr, gfp);
- if (!fltr)
- return ERR_PTR(-ENOMEM);
-
- bnge_init_l2_filter(bn, fltr, key, idx);
- return fltr;
-}
-
-static int bnge_hwrm_set_vnic_filter(struct bnge_net *bn, u16 vnic_id, u16 idx,
- const u8 *mac_addr)
-{
- struct bnge_l2_filter *fltr;
- struct bnge_l2_key key;
- int rc;
-
- ether_addr_copy(key.dst_mac_addr, mac_addr);
- key.vlan = 0;
- fltr = bnge_alloc_l2_filter(bn, &key, GFP_KERNEL);
- if (IS_ERR(fltr))
- return PTR_ERR(fltr);
-
- fltr->base.fw_vnic_id = bn->vnic_info[vnic_id].fw_vnic_id;
- rc = bnge_hwrm_l2_filter_alloc(bn->bd, fltr);
- if (rc)
- goto err_del_l2_filter;
- bn->vnic_info[vnic_id].l2_filters[idx] = fltr;
- return rc;
-
-err_del_l2_filter:
- bnge_del_l2_filter(bn, fltr);
- return rc;
-}
-
static bool bnge_mc_list_updated(struct bnge_net *bn, u32 *rx_mask,
const struct netdev_hw_addr_list *mc)
{
@@ -2580,10 +2433,23 @@ static int bnge_setup_interrupts(struct bnge_net *bn)
{
struct net_device *dev = bn->netdev;
struct bnge_dev *bd = bn->bd;
+ int rc;
bnge_setup_msix(bn);
- return netif_set_real_num_queues(dev, bd->tx_nr_rings, bd->rx_nr_rings);
+ rc = netif_set_real_num_queues(dev, bd->tx_nr_rings, bd->rx_nr_rings);
+ if (rc)
+ return rc;
+
+#ifdef CONFIG_RFS_ACCEL
+ if (bn->priv_flags & BNGE_NET_EN_NTUPLE) {
+ dev->rx_cpu_rmap = alloc_irq_cpu_rmap(bd->rx_nr_rings);
+ if (!dev->rx_cpu_rmap)
+ return -ENOMEM;
+ }
+#endif
+
+ return rc;
}
static void bnge_hwrm_resource_free(struct bnge_net *bn, bool close_path)
@@ -2599,6 +2465,11 @@ static void bnge_free_irq(struct bnge_net *bn)
struct bnge_irq *irq;
int i;
+#ifdef CONFIG_RFS_ACCEL
+ free_irq_cpu_rmap(bn->netdev->rx_cpu_rmap);
+ bn->netdev->rx_cpu_rmap = NULL;
+#endif
+
for (i = 0; i < bd->nq_nr_rings; i++) {
int map_idx = bnge_cp_num_to_irq_num(bn, i);
@@ -2618,6 +2489,7 @@ static void bnge_free_irq(struct bnge_net *bn)
static int bnge_request_irq(struct bnge_net *bn)
{
+ struct cpu_rmap *rmap = NULL;
struct bnge_dev *bd = bn->bd;
int i, rc;
@@ -2626,10 +2498,23 @@ static int bnge_request_irq(struct bnge_net *bn)
netdev_err(bn->netdev, "bnge_setup_interrupts err: %d\n", rc);
return rc;
}
+
+#ifdef CONFIG_RFS_ACCEL
+ rmap = bn->netdev->rx_cpu_rmap;
+#endif
+
for (i = 0; i < bd->nq_nr_rings; i++) {
int map_idx = bnge_cp_num_to_irq_num(bn, i);
struct bnge_irq *irq = &bd->irq_tbl[map_idx];
+ if (IS_ENABLED(CONFIG_RFS_ACCEL) &&
+ rmap && bn->bnapi[i]->rx_ring) {
+ rc = irq_cpu_rmap_add(rmap, irq->vector);
+ if (rc)
+ netdev_warn(bn->netdev,
+ "failed adding irq rmap for ring %d\n", i);
+ }
+
rc = request_irq(irq->vector, irq->handler, 0, irq->name,
bn->bnapi[i]);
if (rc)
@@ -2712,6 +2597,12 @@ static int bnge_init_chip(struct bnge_net *bn)
if (rc)
goto err_out;
+ if (bn->priv_flags & BNGE_NET_EN_NTUPLE) {
+ rc = bnge_alloc_rfs_vnic(bn);
+ if (rc)
+ goto err_out;
+ }
+
if (bd->rss_cap & BNGE_RSS_CAP_RSS_HASH_TYPE_DELTA)
bnge_hwrm_update_rss_hash_cfg(bn);
@@ -2881,7 +2772,7 @@ static int bnge_hwrm_if_change(struct bnge_dev *bd, bool up)
return bnge_hwrm_req_send(bd, req);
}
-static int bnge_open_core(struct bnge_net *bn)
+int bnge_open_core(struct bnge_net *bn)
{
struct bnge_dev *bd = bn->bd;
int rc;
@@ -2943,6 +2834,9 @@ static int bnge_open_core(struct bnge_net *bn)
/* Poll link status and check for SFP+ module status */
bnge_get_port_module_status(bn);
+ bnge_hwrm_realloc_rss_ctx_vnic(bn);
+ bnge_cfg_usr_fltrs(bn);
+
return 0;
err_free_irq:
@@ -3151,7 +3045,7 @@ static void bnge_save_ring_stats(struct bnge_net *bn)
}
}
-static void bnge_close_core(struct bnge_net *bn)
+void bnge_close_core(struct bnge_net *bn)
{
struct bnge_dev *bd = bn->bd;
@@ -3160,7 +3054,10 @@ static void bnge_close_core(struct bnge_net *bn)
clear_bit(BNGE_STATE_OPEN, &bd->state);
timer_delete_sync(&bn->timer);
+
+ bnge_clear_rss_ctxs(bn);
bnge_shutdown_nic(bn);
+
bnge_disable_napi(bn);
/* Save ring stats before shutdown */
@@ -3267,6 +3164,96 @@ static const struct netdev_stat_ops bnge_stat_ops = {
.get_base_stats = bnge_get_base_stats,
};
+#ifdef CONFIG_RFS_ACCEL
+static u32 bnge_lookup_l2_filter_from_key(struct bnge_net *bn,
+ struct bnge_l2_key *key)
+{
+ u32 idx;
+
+ idx = jhash2(&key->filter_key, BNGE_L2_KEY_SIZE, bn->hash_seed) &
+ BNGE_L2_FLTR_HASH_MASK;
+ return bnge_lookup_l2_filter_rcu(bn, key, idx);
+}
+
+static int bnge_rx_flow_steer(struct net_device *dev, const struct sk_buff *skb,
+ u16 rxq_index, u32 flow_id)
+{
+ struct ethhdr *eth = (struct ethhdr *)skb_mac_header(skb);
+ struct bnge_ntuple_filter *fltr, *new_fltr;
+ struct bnge_net *bn = netdev_priv(dev);
+ u32 flags, idx, filter_id;
+ struct flow_keys *fkeys;
+ int rc = 0;
+
+ if (ether_addr_equal(dev->dev_addr, eth->h_dest)) {
+ struct bnge_l2_filter *l2_filter;
+
+ l2_filter = bn->vnic_info[BNGE_VNIC_DEFAULT].l2_filters[0];
+ filter_id = l2_filter->base.filter_id;
+
+ } else {
+ struct bnge_l2_key key;
+
+ ether_addr_copy(key.dst_mac_addr, eth->h_dest);
+ key.vlan = 0;
+
+ filter_id = bnge_lookup_l2_filter_from_key(bn, &key);
+ if (filter_id == BNGE_FLTR_ID_INVALID)
+ return -EINVAL;
+ }
+ new_fltr = kzalloc_obj(*new_fltr, GFP_ATOMIC);
+ if (!new_fltr)
+ return -ENOMEM;
+
+ fkeys = &new_fltr->fkeys;
+ if (!skb_flow_dissect_flow_keys(skb, fkeys, 0)) {
+ rc = -EPROTONOSUPPORT;
+ goto err_free;
+ }
+
+ if ((fkeys->basic.n_proto != htons(ETH_P_IP) &&
+ fkeys->basic.n_proto != htons(ETH_P_IPV6)) ||
+ ((fkeys->basic.ip_proto != IPPROTO_TCP) &&
+ (fkeys->basic.ip_proto != IPPROTO_UDP))) {
+ rc = -EPROTONOSUPPORT;
+ goto err_free;
+ }
+ new_fltr->fmasks = BNGE_FLOW_IPV4_MASK_ALL;
+ if (fkeys->basic.n_proto == htons(ETH_P_IPV6))
+ new_fltr->fmasks = BNGE_FLOW_IPV6_MASK_ALL;
+
+ flags = fkeys->control.flags;
+ if (flags & FLOW_DIS_IS_FRAGMENT) {
+ rc = -EPROTONOSUPPORT;
+ goto err_free;
+ }
+
+ new_fltr->l2_filter_id = filter_id;
+
+ idx = bnge_get_ntp_filter_idx(bn, fkeys, skb);
+ rcu_read_lock();
+ fltr = bnge_lookup_ntp_filter_from_idx(bn, new_fltr, idx);
+ if (fltr) {
+ rc = fltr->base.sw_id;
+ rcu_read_unlock();
+ goto err_free;
+ }
+ rcu_read_unlock();
+
+ new_fltr->flow_id = flow_id;
+ new_fltr->base.rxq = rxq_index;
+ rc = bnge_insert_ntp_filter(bn, new_fltr, idx);
+ if (!rc) {
+ bnge_queue_sp_work(bn, BNGE_RX_NTP_FLTR_SP_EVENT);
+ return new_fltr->base.sw_id;
+ }
+
+err_free:
+ kfree(new_fltr);
+ return rc;
+}
+#endif
+
static const struct net_device_ops bnge_netdev_ops = {
.ndo_open = bnge_open,
.ndo_stop = bnge_close,
@@ -3274,6 +3261,9 @@ static const struct net_device_ops bnge_netdev_ops = {
.ndo_get_stats64 = bnge_get_stats64,
.ndo_set_rx_mode_async = bnge_set_rx_mode,
.ndo_features_check = bnge_features_check,
+#ifdef CONFIG_RFS_ACCEL
+ .ndo_rx_flow_steer = bnge_rx_flow_steer,
+#endif
};
static void bnge_init_mac_addr(struct bnge_dev *bd)
@@ -3399,6 +3389,17 @@ static void bnge_init_ring_params(struct bnge_net *bn)
bn->netdev->cfg->hds_thresh = max(BNGE_DEFAULT_RX_COPYBREAK, rx_size);
}
+static void bnge_set_dflt_rfs(struct bnge_net *bn)
+{
+ bn->netdev->hw_features |= NETIF_F_NTUPLE;
+ bn->netdev->features &= ~NETIF_F_NTUPLE;
+ bn->priv_flags &= ~BNGE_NET_EN_NTUPLE;
+ if (bnge_is_arfs_cap(bn->bd)) {
+ bn->priv_flags |= BNGE_NET_EN_NTUPLE;
+ bn->netdev->features |= NETIF_F_NTUPLE;
+ }
+}
+
int bnge_netdev_alloc(struct bnge_dev *bd, int max_irqs)
{
struct net_device *netdev;
@@ -3505,6 +3506,8 @@ int bnge_netdev_alloc(struct bnge_dev *bd, int max_irqs)
bnge_set_ring_params(bd);
bnge_init_l2_fltr_tbl(bn);
+ bnge_set_dflt_rfs(bn);
+
bnge_init_mac_addr(bd);
rc = bnge_probe_phy(bn, true);
@@ -3516,6 +3519,9 @@ int bnge_netdev_alloc(struct bnge_dev *bd, int max_irqs)
goto err_free_workq;
spin_lock_init(&bn->stats_lock);
+ spin_lock_init(&bn->ntp_fltr_lock);
+ INIT_LIST_HEAD(&bn->usr_fltr_list);
+
netdev->request_ops_lock = true;
rc = register_netdev(netdev);
if (rc) {
@@ -3543,6 +3549,9 @@ void bnge_netdev_free(struct bnge_dev *bd)
unregister_netdev(netdev);
+ bnge_free_ntp_fltrs(bn, false);
+ bnge_free_l2_filters(bn);
+
timer_shutdown_sync(&bn->timer);
cancel_work_sync(&bn->sp_task);
bn->sp_event = 0;
diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_netdev.h b/drivers/net/ethernet/broadcom/bnge/bnge_netdev.h
index 476b5bab96fed..3146023621a61 100644
--- a/drivers/net/ethernet/broadcom/bnge/bnge_netdev.h
+++ b/drivers/net/ethernet/broadcom/bnge/bnge_netdev.h
@@ -152,6 +152,7 @@ enum {
BNGE_NET_EN_GRO = BIT(0),
BNGE_NET_EN_LRO = BIT(1),
BNGE_NET_EN_JUMBO = BIT(2),
+ BNGE_NET_EN_NTUPLE = BIT(3),
};
#define BNGE_NET_EN_TPA (BNGE_NET_EN_GRO | BNGE_NET_EN_LRO)
@@ -281,8 +282,12 @@ enum bnge_sp_event {
BNGE_LINK_CFG_CHANGE_SP_EVENT,
BNGE_UPDATE_PHY_SP_EVENT,
BNGE_PERIODIC_STATS_SP_EVENT,
+ BNGE_RX_NTP_FLTR_SP_EVENT,
};
+#define BNGE_NTP_FLTR_HASH_SIZE 512
+#define BNGE_NTP_FLTR_HASH_MASK (BNGE_NTP_FLTR_HASH_SIZE - 1)
+
struct bnge_net {
struct bnge_dev *bd;
struct net_device *netdev;
@@ -370,6 +375,18 @@ struct bnge_net {
u8 pri2cos_idx[8];
bool pri2cos_valid;
+
+ u16 num_rss_ctx;
+
+ struct list_head usr_fltr_list;
+ unsigned long *ntp_fltr_bmap;
+ unsigned long *l2_fltr_bmap;
+ int ntp_fltr_count;
+ int l2_fltr_count;
+ int user_fltr_count;
+
+ struct hlist_head ntp_fltr_hash_tbl[BNGE_NTP_FLTR_HASH_SIZE];
+ spinlock_t ntp_fltr_lock; /* for hash table add, del */
};
#define BNGE_DEFAULT_RX_RING_SIZE 511
@@ -558,8 +575,6 @@ struct bnge_napi {
};
#define INVALID_STATS_CTX_ID -1
-#define BNGE_VNIC_DEFAULT 0
-#define BNGE_MAX_UC_ADDRS 4
#define BNGE_RX_MASK_CFG_FLAGS \
(CFA_L2_SET_RX_MASK_REQ_MASK_PROMISCUOUS | \
@@ -567,78 +582,8 @@ struct bnge_napi {
CFA_L2_SET_RX_MASK_REQ_MASK_ALL_MCAST | \
CFA_L2_SET_RX_MASK_REQ_MASK_BCAST)
-struct bnge_vnic_info {
- u16 fw_vnic_id;
-#define BNGE_MAX_CTX_PER_VNIC 8
- u16 fw_rss_cos_lb_ctx[BNGE_MAX_CTX_PER_VNIC];
- u16 mru;
- /* index 0 always dev_addr */
- struct bnge_l2_filter *l2_filters[BNGE_MAX_UC_ADDRS];
- u16 uc_filter_count;
- u8 *uc_list;
- dma_addr_t rss_table_dma_addr;
- __le16 *rss_table;
- dma_addr_t rss_hash_key_dma_addr;
- u64 *rss_hash_key;
- int rss_table_size;
-#define BNGE_RSS_TABLE_ENTRIES 64
-#define BNGE_RSS_TABLE_SIZE (BNGE_RSS_TABLE_ENTRIES * 4)
-#define BNGE_RSS_TABLE_MAX_TBL 8
-#define BNGE_MAX_RSS_TABLE_SIZE \
- (BNGE_RSS_TABLE_SIZE * BNGE_RSS_TABLE_MAX_TBL)
- u32 rx_mask;
-
- u8 *mc_list;
- int mc_list_size;
- int mc_list_count;
- dma_addr_t mc_list_mapping;
-#define BNGE_MAX_MC_ADDRS 16
-
- u32 flags;
-#define BNGE_VNIC_RSS_FLAG 1
-#define BNGE_VNIC_MCAST_FLAG 4
-#define BNGE_VNIC_UCAST_FLAG 8
- u32 vnic_id;
-};
-
-struct bnge_filter_base {
- struct hlist_node hash;
- struct list_head list;
- __le64 filter_id;
- u8 type;
-#define BNGE_FLTR_TYPE_L2 2
- u8 flags;
- u16 rxq;
- u16 fw_vnic_id;
- u16 vf_idx;
- unsigned long state;
-#define BNGE_FLTR_VALID 0
-#define BNGE_FLTR_FW_DELETED 2
-
- struct rcu_head rcu;
-};
-
-struct bnge_l2_key {
- union {
- struct {
- u8 dst_mac_addr[ETH_ALEN];
- u16 vlan;
- };
- u32 filter_key;
- };
-};
-
-#define BNGE_L2_KEY_SIZE (sizeof(struct bnge_l2_key) / 4)
-struct bnge_l2_filter {
- /* base filter must be the first member */
- struct bnge_filter_base base;
- struct bnge_l2_key l2_key;
- refcount_t refcnt;
-};
-
u32 bnge_cp_ring_for_rx(struct bnge_rx_ring_info *rxr);
u32 bnge_cp_ring_for_tx(struct bnge_tx_ring_info *txr);
-void bnge_fill_hw_rss_tbl(struct bnge_net *bn, struct bnge_vnic_info *vnic);
int bnge_alloc_rx_data(struct bnge_net *bn, struct bnge_rx_ring_info *rxr,
u16 prod, gfp_t gfp);
u16 bnge_find_next_agg_idx(struct bnge_rx_ring_info *rxr, u16 idx);
@@ -648,4 +593,6 @@ int bnge_alloc_rx_netmem(struct bnge_net *bn, struct bnge_rx_ring_info *rxr,
u16 prod, gfp_t gfp);
void __bnge_queue_sp_work(struct bnge_net *bn);
void bnge_copy_hw_masks(u64 *mask_arr, __le64 *hw_mask_arr, int count);
+int bnge_open_core(struct bnge_net *bn);
+void bnge_close_core(struct bnge_net *bn);
#endif /* _BNGE_NETDEV_H_ */
diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_resc.c b/drivers/net/ethernet/broadcom/bnge/bnge_resc.c
index 4711dd4945ffe..d2e21f76909c5 100644
--- a/drivers/net/ethernet/broadcom/bnge/bnge_resc.c
+++ b/drivers/net/ethernet/broadcom/bnge/bnge_resc.c
@@ -11,6 +11,8 @@
#include "bnge_hwrm.h"
#include "bnge_hwrm_lib.h"
#include "bnge_resc.h"
+#include "bnge_vnic.h"
+#include "bnge_filter.h"
static u16 bnge_num_tx_to_cp(struct bnge_dev *bd, u16 tx)
{
@@ -177,6 +179,16 @@ static int bnge_adjust_rings(struct bnge_dev *bd, u16 *rx,
return bnge_fix_rings_count(rx, tx, max_nq, sh);
}
+static unsigned int bnge_get_max_func_rss_ctxs(struct bnge_dev *bd)
+{
+ return bd->hw_resc.max_rsscos_ctxs;
+}
+
+static unsigned int bnge_get_max_func_vnics(struct bnge_dev *bd)
+{
+ return bd->hw_resc.max_vnics;
+}
+
int bnge_cal_nr_rss_ctxs(u16 rx_rings)
{
if (!rx_rings)
@@ -186,14 +198,27 @@ int bnge_cal_nr_rss_ctxs(u16 rx_rings)
BNGE_RSS_TABLE_ENTRIES);
}
-static u16 bnge_rss_ctxs_in_use(struct bnge_dev *bd,
- struct bnge_hw_rings *hwr)
+static u16 bnge_get_total_rss_ctxs(struct bnge_dev *bd,
+ struct bnge_hw_rings *hwr)
{
- return bnge_cal_nr_rss_ctxs(hwr->grp);
+ u16 rss_ctx = bnge_cal_nr_rss_ctxs(hwr->grp);
+
+ rss_ctx *= hwr->vnic;
+
+ return rss_ctx;
}
-static u16 bnge_get_total_vnics(struct bnge_dev *bd, u16 rx_rings)
+static u16 bnge_get_total_vnics(struct bnge_dev *bd)
{
+ if (bd->netdev) {
+ struct bnge_net *bn = netdev_priv(bd->netdev);
+
+ if (bn->priv_flags & BNGE_NET_EN_NTUPLE)
+ return 2 + bn->num_rss_ctx;
+ } else if (bnge_is_arfs_cap(bd)) {
+ return 2;
+ }
+
return 1;
}
@@ -203,24 +228,6 @@ u32 bnge_get_rxfh_indir_size(struct bnge_dev *bd)
BNGE_RSS_TABLE_ENTRIES;
}
-static void bnge_set_dflt_rss_indir_tbl(struct bnge_dev *bd)
-{
- u16 max_entries, pad;
- u32 *rss_indir_tbl;
- int i;
-
- max_entries = bnge_get_rxfh_indir_size(bd);
- rss_indir_tbl = &bd->rss_indir_tbl[0];
-
- for (i = 0; i < max_entries; i++)
- rss_indir_tbl[i] = ethtool_rxfh_indir_default(i,
- bd->rx_nr_rings);
-
- pad = bd->rss_indir_tbl_entries - max_entries;
- if (pad)
- memset(&rss_indir_tbl[i], 0, pad * sizeof(*rss_indir_tbl));
-}
-
static void bnge_copy_reserved_rings(struct bnge_dev *bd,
struct bnge_hw_rings *hwr)
{
@@ -253,7 +260,7 @@ static bool bnge_need_reserve_rings(struct bnge_dev *bd)
if (hw_resc->resv_tx_rings != bd->tx_nr_rings)
return true;
- vnic = bnge_get_total_vnics(bd, rx);
+ vnic = bnge_get_total_vnics(bd);
if (bnge_is_agg_reqd(bd))
rx <<= 1;
@@ -299,12 +306,12 @@ int bnge_reserve_rings(struct bnge_dev *bd)
sh = true;
hwr.cmpl = hwr.rx + hwr.tx;
- hwr.vnic = bnge_get_total_vnics(bd, hwr.rx);
+ hwr.vnic = bnge_get_total_vnics(bd);
if (bnge_is_agg_reqd(bd))
hwr.rx <<= 1;
hwr.grp = bd->rx_nr_rings;
- hwr.rss_ctx = bnge_rss_ctxs_in_use(bd, &hwr);
+ hwr.rss_ctx = bnge_get_total_rss_ctxs(bd, &hwr);
hwr.stat = bnge_func_stat_ctxs_demand(bd);
old_rx_rings = bd->hw_resc.resv_rx_rings;
@@ -347,7 +354,7 @@ int bnge_reserve_rings(struct bnge_dev *bd)
return -ENOMEM;
if (old_rx_rings != bd->hw_resc.resv_rx_rings)
- bnge_set_dflt_rss_indir_tbl(bd);
+ bnge_set_dflt_rss_indir_tbl(bd, NULL);
if (!bnge_aux_registered(bd)) {
u16 resv_msix, resv_ctx, aux_ctxs;
@@ -580,6 +587,47 @@ static int bnge_alloc_rss_indir_tbl(struct bnge_dev *bd)
return 0;
}
+/* If runtime conditions support RFS */
+bool bnge_arfs_capable(struct bnge_dev *bd, bool new_rss_ctx)
+{
+ struct bnge_hw_rings hwr = {};
+ int max_vnics, max_rss_ctxs;
+
+ hwr.grp = bd->rx_nr_rings;
+ hwr.vnic = bnge_get_total_vnics(bd);
+ if (new_rss_ctx)
+ hwr.vnic++;
+ hwr.rss_ctx = bnge_get_total_rss_ctxs(bd, &hwr);
+ max_vnics = bnge_get_max_func_vnics(bd);
+ max_rss_ctxs = bnge_get_max_func_rss_ctxs(bd);
+
+ if (hwr.vnic > max_vnics || hwr.rss_ctx > max_rss_ctxs) {
+ if (bd->rx_nr_rings > 1)
+ dev_warn(bd->dev,
+ "Not enough resources to support NTUPLE filters\n");
+ return false;
+ }
+
+ /* Do not reduce VNIC and RSS ctx reservations. There is a FW
+ * issue that will mess up the default VNIC if we reduce the
+ * reservations.
+ */
+ if (hwr.vnic <= bd->hw_resc.resv_vnics &&
+ hwr.rss_ctx <= bd->hw_resc.resv_rsscos_ctxs)
+ return true;
+
+ bnge_hwrm_reserve_rings(bd, &hwr);
+ if (hwr.vnic <= bd->hw_resc.resv_vnics &&
+ hwr.rss_ctx <= bd->hw_resc.resv_rsscos_ctxs)
+ return true;
+
+ dev_warn(bd->dev, "Unable to reserve resources to support NTUPLE filters\n");
+ hwr.vnic = 1;
+ hwr.rss_ctx = 0;
+ bnge_hwrm_reserve_rings(bd, &hwr);
+ return false;
+}
+
int bnge_net_init_dflt_config(struct bnge_dev *bd)
{
struct bnge_hw_resc *hw_resc;
@@ -589,13 +637,15 @@ int bnge_net_init_dflt_config(struct bnge_dev *bd)
if (rc)
return rc;
+ if (bnge_arfs_capable(bd, false))
+ bd->flags |= BNGE_EN_ARFS_CAP;
+
rc = bnge_net_init_dflt_rings(bd, true);
if (rc)
goto err_free_tbl;
hw_resc = &bd->hw_resc;
- bd->max_fltr = hw_resc->max_rx_em_flows + hw_resc->max_rx_wm_flows +
- BNGE_L2_FLTR_MAX_FLTR;
+ bd->max_fltr = BNGE_MAX_NTUPLE_FLTRS;
return 0;
diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_resc.h b/drivers/net/ethernet/broadcom/bnge/bnge_resc.h
index b62a634669f62..1e55fbe6985b1 100644
--- a/drivers/net/ethernet/broadcom/bnge/bnge_resc.h
+++ b/drivers/net/ethernet/broadcom/bnge/bnge_resc.h
@@ -75,6 +75,7 @@ void bnge_aux_init_dflt_config(struct bnge_dev *bd);
u32 bnge_get_rxfh_indir_size(struct bnge_dev *bd);
int bnge_cal_nr_rss_ctxs(u16 rx_rings);
bool bnge_aux_has_enough_resources(struct bnge_dev *bd);
+bool bnge_arfs_capable(struct bnge_dev *bd, bool new_rss_ctx);
static inline u32
bnge_adjust_pow_two(u32 total_ent, u16 ent_per_blk)
diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_vnic.c b/drivers/net/ethernet/broadcom/bnge/bnge_vnic.c
new file mode 100644
index 0000000000000..d738233f0bc9b
--- /dev/null
+++ b/drivers/net/ethernet/broadcom/bnge/bnge_vnic.c
@@ -0,0 +1,272 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2026 Broadcom.
+
+#include <linux/kernel.h>
+#include <linux/dma-mapping.h>
+#include <linux/ethtool.h>
+
+#include "bnge.h"
+#include "bnge_netdev.h"
+#include "bnge_vnic.h"
+#include "bnge_hwrm_lib.h"
+#include "bnge_filter.h"
+#include "bnge_ethtool.h"
+
+void bnge_set_dflt_rss_indir_tbl(struct bnge_dev *bd,
+ struct ethtool_rxfh_context *rss_ctx)
+{
+ u16 max_entries, pad;
+ u32 *rss_indir_tbl;
+ u16 i;
+
+ max_entries = bnge_get_rxfh_indir_size(bd);
+
+ if (rss_ctx)
+ rss_indir_tbl = ethtool_rxfh_context_indir(rss_ctx);
+ else
+ rss_indir_tbl = &bd->rss_indir_tbl[0];
+
+ for (i = 0; i < max_entries; i++)
+ rss_indir_tbl[i] = ethtool_rxfh_indir_default(i,
+ bd->rx_nr_rings);
+
+ pad = bd->rss_indir_tbl_entries - max_entries;
+ if (pad)
+ memset(&rss_indir_tbl[i], 0, pad * sizeof(*rss_indir_tbl));
+}
+
+void bnge_fill_hw_rss_tbl(struct bnge_net *bn, struct bnge_vnic_info *vnic)
+{
+ __le16 *ring_tbl = vnic->rss_table;
+ struct bnge_rx_ring_info *rxr;
+ struct bnge_dev *bd = bn->bd;
+ u16 tbl_size, i;
+
+ tbl_size = bnge_get_rxfh_indir_size(bd);
+
+ for (i = 0; i < tbl_size; i++) {
+ u16 ring_id, j;
+
+ if (vnic->flags & BNGE_VNIC_NTUPLE_FLAG)
+ j = ethtool_rxfh_indir_default(i, bd->rx_nr_rings);
+ else if (vnic->flags & BNGE_VNIC_RSSCTX_FLAG)
+ j = ethtool_rxfh_context_indir(vnic->rss_ctx)[i];
+ else
+ j = bd->rss_indir_tbl[i];
+
+ rxr = &bn->rx_ring[j];
+
+ ring_id = rxr->rx_ring_struct.fw_ring_id;
+ *ring_tbl++ = cpu_to_le16(ring_id);
+ ring_id = bnge_cp_ring_for_rx(rxr);
+ *ring_tbl++ = cpu_to_le16(ring_id);
+ }
+}
+
+int bnge_hwrm_vnic_rss_cfg(struct bnge_net *bn,
+ struct bnge_vnic_info *vnic)
+{
+ int rc;
+
+ rc = bnge_hwrm_vnic_set_rss(bn, vnic, true);
+ if (rc) {
+ netdev_err(bn->netdev, "hwrm vnic %d set rss failure rc: %d\n",
+ vnic->vnic_id, rc);
+ return rc;
+ }
+ rc = bnge_hwrm_vnic_cfg(bn, vnic);
+ if (rc)
+ netdev_err(bn->netdev, "hwrm vnic %d cfg failure rc: %d\n",
+ vnic->vnic_id, rc);
+ return rc;
+}
+
+int bnge_setup_vnic(struct bnge_net *bn, struct bnge_vnic_info *vnic)
+{
+ struct bnge_dev *bd = bn->bd;
+ int rc, i, nr_ctxs;
+
+ nr_ctxs = bnge_cal_nr_rss_ctxs(bd->rx_nr_rings);
+ for (i = 0; i < nr_ctxs; i++) {
+ rc = bnge_hwrm_vnic_ctx_alloc(bd, vnic, i);
+ if (rc) {
+ netdev_err(bn->netdev, "hwrm vnic %d ctx %d alloc failure rc: %d\n",
+ vnic->vnic_id, i, rc);
+ return -ENOMEM;
+ }
+ bn->rsscos_nr_ctxs++;
+ }
+
+ rc = bnge_hwrm_vnic_rss_cfg(bn, vnic);
+ if (rc)
+ return rc;
+
+ if (bnge_is_agg_reqd(bd)) {
+ rc = bnge_hwrm_vnic_set_hds(bn, vnic);
+ if (rc)
+ netdev_err(bn->netdev, "hwrm vnic %d set hds failure rc: %d\n",
+ vnic->vnic_id, rc);
+ }
+ return rc;
+}
+
+static int bnge_alloc_and_setup_vnic(struct bnge_net *bn,
+ struct bnge_vnic_info *vnic,
+ u16 rx_rings)
+{
+ int rc;
+
+ rc = bnge_hwrm_vnic_alloc(bn->bd, vnic, rx_rings);
+ if (rc) {
+ netdev_err(bn->netdev, "hwrm vnic %u alloc failure rc: %d\n",
+ vnic->vnic_id, rc);
+ return rc;
+ }
+ return bnge_setup_vnic(bn, vnic);
+}
+
+int bnge_alloc_rfs_vnic(struct bnge_net *bn)
+{
+ struct bnge_vnic_info *vnic;
+
+ vnic = &bn->vnic_info[BNGE_VNIC_NTUPLE];
+ return bnge_alloc_and_setup_vnic(bn, vnic, bn->bd->rx_nr_rings);
+}
+
+void bnge_modify_rss(struct bnge_net *bn, struct ethtool_rxfh_context *ctx,
+ struct bnge_rss_ctx *rss_ctx,
+ const struct ethtool_rxfh_param *rxfh)
+{
+ struct bnge_dev *bd = bn->bd;
+
+ if (rxfh->key) {
+ if (rss_ctx) {
+ memcpy(rss_ctx->vnic.rss_hash_key, rxfh->key,
+ HW_HASH_KEY_SIZE);
+ } else {
+ memcpy(bn->rss_hash_key, rxfh->key, HW_HASH_KEY_SIZE);
+ bn->rss_hash_key_updated = true;
+ }
+ }
+
+ if (rxfh->indir) {
+ u32 i, pad, tbl_size = bnge_get_rxfh_indir_size(bd);
+ u32 *indir_tbl = bd->rss_indir_tbl;
+
+ if (rss_ctx)
+ indir_tbl = ethtool_rxfh_context_indir(ctx);
+ for (i = 0; i < tbl_size; i++)
+ indir_tbl[i] = rxfh->indir[i];
+ pad = bd->rss_indir_tbl_entries - tbl_size;
+ if (pad)
+ memset(&indir_tbl[i], 0, pad * sizeof(*indir_tbl));
+ }
+}
+
+void bnge_del_one_rss_ctx(struct bnge_net *bn, struct bnge_rss_ctx *rss_ctx,
+ bool all)
+{
+ struct bnge_vnic_info *vnic = &rss_ctx->vnic;
+ struct bnge_filter_base *usr_fltr, *tmp;
+ struct bnge_ntuple_filter *ntp_fltr;
+ int i;
+
+ bnge_hwrm_vnic_free_one(bn->bd, &rss_ctx->vnic);
+ for (i = 0; i < BNGE_MAX_CTX_PER_VNIC; i++) {
+ if (vnic->fw_rss_cos_lb_ctx[i] != INVALID_HW_RING_ID)
+ bnge_hwrm_vnic_ctx_free_one(bn->bd, vnic, i);
+ }
+
+ if (!all)
+ return;
+
+ /* Delete all filters associated with this user RSS context */
+ list_for_each_entry_safe(usr_fltr, tmp, &bn->usr_fltr_list, list_node) {
+ if ((usr_fltr->flags & BNGE_ACT_RSS_CTX) &&
+ usr_fltr->fw_vnic_id == rss_ctx->index) {
+ ntp_fltr = container_of(usr_fltr,
+ struct bnge_ntuple_filter,
+ base);
+ bnge_hwrm_cfa_ntuple_filter_free(bn->bd, ntp_fltr);
+ bnge_del_ntp_filter(bn, ntp_fltr);
+ }
+ }
+
+ if (vnic->rss_table)
+ dma_free_coherent(bn->bd->dev, vnic->rss_table_size,
+ vnic->rss_table,
+ vnic->rss_table_dma_addr);
+ bn->num_rss_ctx--;
+}
+
+void bnge_hwrm_realloc_rss_ctx_vnic(struct bnge_net *bn)
+{
+ bool set_tpa = !!(bn->priv_flags & BNGE_NET_EN_TPA);
+ struct ethtool_rxfh_context *ctx;
+ unsigned long context;
+
+ xa_for_each(&bn->netdev->ethtool->rss_ctx, context, ctx) {
+ struct bnge_rss_ctx *rss_ctx = ethtool_rxfh_context_priv(ctx);
+ struct bnge_vnic_info *vnic = &rss_ctx->vnic;
+
+ if (bnge_hwrm_vnic_alloc(bn->bd, vnic, bn->bd->rx_nr_rings) ||
+ bnge_hwrm_vnic_set_tpa(bn->bd, vnic, set_tpa) ||
+ bnge_setup_vnic(bn, vnic)) {
+ netdev_err(bn->netdev, "Failed to restore RSS ctx %d\n",
+ rss_ctx->index);
+ bnge_del_one_rss_ctx(bn, rss_ctx, true);
+ ethtool_rxfh_context_lost(bn->netdev, rss_ctx->index);
+ }
+ }
+}
+
+void bnge_clear_rss_ctxs(struct bnge_net *bn)
+{
+ struct ethtool_rxfh_context *ctx;
+ unsigned long context;
+
+ xa_for_each(&bn->netdev->ethtool->rss_ctx, context, ctx) {
+ struct bnge_rss_ctx *rss_ctx = ethtool_rxfh_context_priv(ctx);
+
+ bnge_del_one_rss_ctx(bn, rss_ctx, false);
+ }
+}
+
+struct bnge_rss_ctx *bnge_get_rss_ctx_from_index(struct bnge_net *bn, u32 idx)
+{
+ struct ethtool_rxfh_context *ctx;
+
+ ctx = xa_load(&bn->netdev->ethtool->rss_ctx, idx);
+ if (!ctx)
+ return NULL;
+ return ethtool_rxfh_context_priv(ctx);
+}
+
+int bnge_alloc_vnic_rss_table(struct bnge_net *bn,
+ struct bnge_vnic_info *vnic)
+{
+ unsigned int size = L1_CACHE_ALIGN(BNGE_MAX_RSS_TABLE_SIZE);
+
+ vnic->rss_table_size = size + HW_HASH_KEY_SIZE;
+ vnic->rss_table = dma_alloc_coherent(bn->bd->dev,
+ vnic->rss_table_size,
+ &vnic->rss_table_dma_addr,
+ GFP_KERNEL);
+ if (!vnic->rss_table)
+ return -ENOMEM;
+
+ vnic->rss_hash_key = ((void *)vnic->rss_table) + size;
+ vnic->rss_hash_key_dma_addr = vnic->rss_table_dma_addr + size;
+ return 0;
+}
+
+void bnge_init_vnic_mem(struct bnge_vnic_info *vnic)
+{
+ int i;
+
+ vnic->fw_vnic_id = INVALID_HW_RING_ID;
+ vnic->vnic_id = BNGE_VNIC_ID_INVALID;
+
+ for (i = 0; i < BNGE_MAX_CTX_PER_VNIC; i++)
+ vnic->fw_rss_cos_lb_ctx[i] = INVALID_HW_RING_ID;
+}
diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_vnic.h b/drivers/net/ethernet/broadcom/bnge/bnge_vnic.h
new file mode 100644
index 0000000000000..3954c450315de
--- /dev/null
+++ b/drivers/net/ethernet/broadcom/bnge/bnge_vnic.h
@@ -0,0 +1,91 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2026 Broadcom */
+
+#ifndef _BNGE_VNIC_H_
+#define _BNGE_VNIC_H_
+
+struct bnge_net;
+struct bnge_l2_filter;
+
+#define BNGE_RSS_TABLE_ENTRIES 64
+#define BNGE_RSS_TABLE_SIZE (BNGE_RSS_TABLE_ENTRIES * 4)
+#define BNGE_RSS_TABLE_MAX_TBL 8
+#define BNGE_MAX_RSS_TABLE_SIZE \
+ (BNGE_RSS_TABLE_SIZE * BNGE_RSS_TABLE_MAX_TBL)
+#define BNGE_MAX_RSS_TABLE_ENTRIES \
+ (BNGE_RSS_TABLE_ENTRIES * BNGE_RSS_TABLE_MAX_TBL)
+
+#define BNGE_MAX_ETH_RSS_CTX 32
+#define BNGE_MAX_CTX_PER_VNIC 8
+
+#define BNGE_MAX_MC_ADDRS 16
+#define BNGE_MAX_UC_ADDRS 4
+
+#define BNGE_VNIC_ID_INVALID 0xffffffff
+
+struct ethtool_rxfh_context;
+struct ethtool_rxfh_param;
+
+enum {
+ BNGE_VNIC_DEFAULT = 0,
+ BNGE_VNIC_NTUPLE = 1
+};
+
+enum {
+ BNGE_VNIC_RSS_FLAG = BIT(0),
+ BNGE_VNIC_MCAST_FLAG = BIT(1),
+ BNGE_VNIC_UCAST_FLAG = BIT(2),
+ BNGE_VNIC_NTUPLE_FLAG = BIT(3),
+ BNGE_VNIC_RSSCTX_FLAG = BIT(4)
+};
+
+struct bnge_vnic_info {
+ u16 fw_vnic_id;
+ u16 fw_rss_cos_lb_ctx[BNGE_MAX_CTX_PER_VNIC];
+ u16 mru;
+ /* index 0 always dev_addr */
+ struct bnge_l2_filter *l2_filters[BNGE_MAX_UC_ADDRS];
+ u16 uc_filter_count;
+ u8 *uc_list;
+ dma_addr_t rss_table_dma_addr;
+ __le16 *rss_table;
+ dma_addr_t rss_hash_key_dma_addr;
+ u64 *rss_hash_key;
+ int rss_table_size;
+ u32 rx_mask;
+
+ u8 *mc_list;
+ int mc_list_size;
+ int mc_list_count;
+ dma_addr_t mc_list_mapping;
+
+ u32 flags;
+ u32 vnic_id;
+
+ struct ethtool_rxfh_context *rss_ctx;
+};
+
+struct bnge_rss_ctx {
+ struct bnge_vnic_info vnic;
+ u32 index;
+};
+
+void bnge_fill_hw_rss_tbl(struct bnge_net *bn, struct bnge_vnic_info *vnic);
+int bnge_hwrm_vnic_rss_cfg(struct bnge_net *bn,
+ struct bnge_vnic_info *vnic);
+int bnge_setup_vnic(struct bnge_net *bn, struct bnge_vnic_info *vnic);
+void bnge_set_dflt_rss_indir_tbl(struct bnge_dev *bd,
+ struct ethtool_rxfh_context *ctx);
+int bnge_alloc_rfs_vnic(struct bnge_net *bn);
+int bnge_alloc_vnic_rss_table(struct bnge_net *bn,
+ struct bnge_vnic_info *vnic);
+struct bnge_rss_ctx *bnge_get_rss_ctx_from_index(struct bnge_net *bn, u32 idx);
+void bnge_modify_rss(struct bnge_net *bn, struct ethtool_rxfh_context *ctx,
+ struct bnge_rss_ctx *rss_ctx,
+ const struct ethtool_rxfh_param *rxfh);
+void bnge_del_one_rss_ctx(struct bnge_net *bn, struct bnge_rss_ctx *rss_ctx,
+ bool all);
+void bnge_hwrm_realloc_rss_ctx_vnic(struct bnge_net *bn);
+void bnge_clear_rss_ctxs(struct bnge_net *bn);
+void bnge_init_vnic_mem(struct bnge_vnic_info *vnic);
+#endif /* _BNGE_VNIC_H_ */
diff --git a/include/linux/bnge/hsi.h b/include/linux/bnge/hsi.h
index 1f7bd96415a52..d4fe662d60b10 100644
--- a/include/linux/bnge/hsi.h
+++ b/include/linux/bnge/hsi.h
@@ -8607,6 +8607,7 @@ struct hwrm_cfa_l2_filter_alloc_input {
#define CFA_L2_FILTER_ALLOC_REQ_ENABLES_MIRROR_VNIC_ID 0x10000UL
#define CFA_L2_FILTER_ALLOC_REQ_ENABLES_NUM_VLANS 0x20000UL
#define CFA_L2_FILTER_ALLOC_REQ_ENABLES_T_NUM_VLANS 0x40000UL
+ #define CFA_L2_FILTER_ALLOC_REQ_ENABLES_RFS_RING_TBL_IDX 0x80000UL
u8 l2_addr[6];
u8 num_vlans;
u8 t_num_vlans;
@@ -8663,7 +8664,8 @@ struct hwrm_cfa_l2_filter_alloc_input {
#define CFA_L2_FILTER_ALLOC_REQ_PRI_HINT_MIN 0x4UL
#define CFA_L2_FILTER_ALLOC_REQ_PRI_HINT_LAST CFA_L2_FILTER_ALLOC_REQ_PRI_HINT_MIN
u8 unused_5;
- __le32 unused_6;
+ __le16 rfs_ring_tbl_idx;
+ __le16 unused_6;
__le64 l2_filter_id_hint;
};
@@ -8900,6 +8902,95 @@ struct hwrm_cfa_tunnel_filter_free_output {
u8 valid;
};
+/* hwrm_cfa_ntuple_filter_alloc_input (size:1024b/128B) */
+struct hwrm_cfa_ntuple_filter_alloc_input {
+ __le16 req_type;
+ __le16 cmpl_ring;
+ __le16 seq_id;
+ __le16 target_id;
+ __le64 resp_addr;
+ __le32 flags;
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_FLAGS_LOOPBACK 0x1UL
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_FLAGS_DROP 0x2UL
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_FLAGS_METER 0x4UL
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_FLAGS_DEST_FID 0x8UL
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_FLAGS_ARP_REPLY 0x10UL
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_FLAGS_DEST_RFS_RING_IDX 0x20UL
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_FLAGS_NO_L2_CONTEXT 0x40UL
+ __le32 enables;
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_L2_FILTER_ID 0x1UL
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_ETHERTYPE 0x2UL
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_TUNNEL_TYPE 0x4UL
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_SRC_MACADDR 0x8UL
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_IPADDR_TYPE 0x10UL
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_SRC_IPADDR 0x20UL
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_SRC_IPADDR_MASK 0x40UL
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_DST_IPADDR 0x80UL
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_DST_IPADDR_MASK 0x100UL
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_IP_PROTOCOL 0x200UL
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_SRC_PORT 0x400UL
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_SRC_PORT_MASK 0x800UL
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_DST_PORT 0x1000UL
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_DST_PORT_MASK 0x2000UL
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_PRI_HINT 0x4000UL
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_NTUPLE_FILTER_ID 0x8000UL
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_DST_ID 0x10000UL
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_MIRROR_VNIC_ID 0x20000UL
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_DST_MACADDR 0x40000UL
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_RFS_RING_TBL_IDX 0x80000UL
+ __le64 l2_filter_id;
+ u8 src_macaddr[6];
+ __be16 ethertype;
+ u8 ip_addr_type;
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_IP_ADDR_TYPE_UNKNOWN 0x0UL
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_IP_ADDR_TYPE_IPV4 0x4UL
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_IP_ADDR_TYPE_IPV6 0x6UL
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_IP_ADDR_TYPE_LAST CFA_NTUPLE_FILTER_ALLOC_REQ_IP_ADDR_TYPE_IPV6
+ u8 ip_protocol;
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_IP_PROTOCOL_UNKNOWN 0x0UL
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_IP_PROTOCOL_TCP 0x6UL
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_IP_PROTOCOL_UDP 0x11UL
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_IP_PROTOCOL_ICMP 0x1UL
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_IP_PROTOCOL_ICMPV6 0x3aUL
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_IP_PROTOCOL_RSVD 0xffUL
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_IP_PROTOCOL_LAST CFA_NTUPLE_FILTER_ALLOC_REQ_IP_PROTOCOL_RSVD
+ __le16 dst_id;
+ __le16 rfs_ring_tbl_idx;
+ u8 tunnel_type;
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_NONTUNNEL 0x0UL
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_VXLAN 0x1UL
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_NVGRE 0x2UL
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_L2GRE 0x3UL
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_IPIP 0x4UL
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_GENEVE 0x5UL
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_MPLS 0x6UL
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_STT 0x7UL
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_IPGRE 0x8UL
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_VXLAN_V4 0x9UL
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_IPGRE_V1 0xaUL
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_L2_ETYPE 0xbUL
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_VXLAN_GPE_V6 0xcUL
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_VXLAN_GPE 0x10UL
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_ANYTUNNEL 0xffUL
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_LAST CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_ANYTUNNEL
+ u8 pri_hint;
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_PRI_HINT_NO_PREFER 0x0UL
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_PRI_HINT_ABOVE 0x1UL
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_PRI_HINT_BELOW 0x2UL
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_PRI_HINT_HIGHEST 0x3UL
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_PRI_HINT_LOWEST 0x4UL
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_PRI_HINT_LAST CFA_NTUPLE_FILTER_ALLOC_REQ_PRI_HINT_LOWEST
+ __be32 src_ipaddr[4];
+ __be32 src_ipaddr_mask[4];
+ __be32 dst_ipaddr[4];
+ __be32 dst_ipaddr_mask[4];
+ __be16 src_port;
+ __be16 src_port_mask;
+ __be16 dst_port;
+ __be16 dst_port_mask;
+ __le64 ntuple_filter_id_hint;
+};
+
/* hwrm_cfa_ntuple_filter_alloc_output (size:192b/24B) */
struct hwrm_cfa_ntuple_filter_alloc_output {
__le16 error_code;
@@ -9014,6 +9105,49 @@ struct hwrm_cfa_ntuple_filter_cfg_output {
u8 valid;
};
+/* hwrm_cfa_adv_flow_mgnt_qcaps_input (size:256b/32B) */
+struct hwrm_cfa_adv_flow_mgnt_qcaps_input {
+ __le16 req_type;
+ __le16 cmpl_ring;
+ __le16 seq_id;
+ __le16 target_id;
+ __le64 resp_addr;
+ __le32 unused_0[4];
+};
+
+/* hwrm_cfa_adv_flow_mgnt_qcaps_output (size:128b/16B) */
+struct hwrm_cfa_adv_flow_mgnt_qcaps_output {
+ __le16 error_code;
+ __le16 req_type;
+ __le16 seq_id;
+ __le16 resp_len;
+ __le32 flags;
+ #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_FLOW_HND_16BIT_SUPPORTED 0x1UL
+ #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_FLOW_HND_64BIT_SUPPORTED 0x2UL
+ #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_FLOW_BATCH_DELETE_SUPPORTED 0x4UL
+ #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_FLOW_RESET_ALL_SUPPORTED 0x8UL
+ #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_NTUPLE_FLOW_DEST_FUNC_SUPPORTED 0x10UL
+ #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_TX_EEM_FLOW_SUPPORTED 0x20UL
+ #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_RX_EEM_FLOW_SUPPORTED 0x40UL
+ #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_FLOW_COUNTER_ALLOC_SUPPORTED 0x80UL
+ #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_RFS_RING_TBL_IDX_SUPPORTED 0x100UL
+ #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_UNTAGGED_VLAN_SUPPORTED 0x200UL
+ #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_XDP_SUPPORTED 0x400UL
+ #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_L2_HEADER_SOURCE_FIELDS_SUPPORTED 0x800UL
+ #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_NTUPLE_FLOW_RX_ARP_SUPPORTED 0x1000UL
+ #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_RFS_RING_TBL_IDX_V2_SUPPORTED 0x2000UL
+ #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_NTUPLE_FLOW_RX_ETHERTYPE_IP_SUPPORTED 0x4000UL
+ #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_TRUFLOW_CAPABLE 0x8000UL
+ #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_L2_FILTER_TRAFFIC_TYPE_L2_ROCE_SUPPORTED 0x10000UL
+ #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_LAG_SUPPORTED 0x20000UL
+ #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_NTUPLE_FLOW_NO_L2CTX_SUPPORTED 0x40000UL
+ #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_NIC_FLOW_STATS_SUPPORTED 0x80000UL
+ #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_NTUPLE_FLOW_RX_EXT_IP_PROTO_SUPPORTED 0x100000UL
+ #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_RFS_RING_TBL_IDX_V3_SUPPORTED 0x200000UL
+ u8 unused_0[3];
+ u8 valid;
+};
+
/* hwrm_tunnel_dst_port_alloc_input (size:192b/24B) */
struct hwrm_tunnel_dst_port_alloc_input {
__le16 req_type;
|
| 3/2 | 2026/08/14 02:03 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 52393 output: 40 thoughts: 435 Thoughts: **Analyzing the BNGE Driver Patch for Ntuple Filter Support** Okay, so I've got this patch for the `bnge` driver. It looks like they're adding support for ntuple filters – aRFS and ethtool rxnfc, which is interesting. I see a whole slew of new files and modifications: `bnge_filter.c`, `bnge_vnic.c`, and changes to `bnge_ethtool.c`, `bnge_netdev.c`, `bnge_hwrm_lib.c` and others. This means it's a non-trivial implementation. Specifically, it looks like they've implemented all the necessary ethtool functions: `get_rxnfc`, `set_rxnfc`, and all the related commands. Plus, there's `get_rxfh`, `set_rxfh`, `get_rxfh_fields`, `set_rxfh_fields`, and the context-management functions like `create_rxfh_context`, `modify_rxfh_context`, and `remove_rxfh_context`. The `ndo_rx_flow_steer` is also implemented using the `bnge_rx_flow_steer` function. This indicates that it supports RSS. They're also handling HWRM messages for ntuple filter allocation and freeing. Wait, hold on a second... `bnge`? That rings a bell. Let me just double-check where this driver resides within the kernel source. Ah, yes, `drivers/net/ethernet/broadcom/bnge/`. This is interesting. Is this a real Broadcom driver or some kind of pseudo-driver? It's not immediately obvious to me, because typically, Broadcom devices are associated with the `bnxt` driver. It might be a syzkaller-specific thing, or perhaps a repurposed device. I should probably try to see if it's reachable in a QEMU instance to confirm. Better search for all instances of `bnge` in the tree just to make sure. Let me get on that. |
| 4/2 | 2026/08/14 02:03 | tool | grepper |
0mArgs:{
"Expression": "bnge_netdev_alloc",
"PathPrefix": "drivers/net/ethernet/broadcom/bnge/"
}Results: {
"Output": "drivers/net/ethernet/broadcom/bnge/bnge_core.c=268=static int bnge_probe_one(struct pci_dev *pdev, const struct pci_device_id *ent)\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-349-\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:350:\trc = bnge_netdev_alloc(bd, max_irqs);\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-351-\tif (rc)\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_netdev.c=3392=static void bnge_set_dflt_rfs(struct bnge_net *bn)\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_netdev.c-3402-\ndrivers/net/ethernet/broadcom/bnge/bnge_netdev.c:3403:int bnge_netdev_alloc(struct bnge_dev *bd, int max_irqs)\ndrivers/net/ethernet/broadcom/bnge/bnge_netdev.c-3404-{\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_netdev.h=291=struct bnge_net {\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_netdev.h-394-\ndrivers/net/ethernet/broadcom/bnge/bnge_netdev.h:395:int bnge_netdev_alloc(struct bnge_dev *bd, int max_irqs);\ndrivers/net/ethernet/broadcom/bnge/bnge_netdev.h-396-void bnge_netdev_free(struct bnge_dev *bd);\n"
} |
| 5/2 | 2026/08/14 02:03 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 53800 output: 38 thoughts: 25 Thoughts: Here's my thought process, as if I were considering this: **Initial Assessment: Diving into the PCI Driver** Okay, so we're looking at a PCI driver here. Immediately, I want to zero in on the PCI ID configuration. I need to understand which devices this driver is designed to support. The logical place to start is the core file – that's where the fundamental device identification usually resides. So, let's crack open `bnge_core.c`. I'm going to look for those PCI vendor and device ID definitions. That will be my first step to understanding the driver's target hardware. From there, I can get a better feel for the scope of this project. |
| 6/2 | 2026/08/14 02:04 | tool | grepper |
0mArgs:{
"Expression": "MODULE_DEVICE_TABLE",
"PathPrefix": "drivers/net/ethernet/broadcom/bnge/"
}Results: {
"Output": "drivers/net/ethernet/broadcom/bnge/bnge_core.c=26=static const struct pci_device_id bnge_pci_tbl[] = {\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-30-};\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:31:MODULE_DEVICE_TABLE(pci, bnge_pci_tbl);\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-32-\n"
} |
| 7/2 | 2026/08/14 02:04 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 54158 output: 40 thoughts: 16 Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience and written in the first person: **Analyzing the Contents of `bnge_pci_tbl`** Alright, let's dive into this. I need to understand what `bnge_pci_tbl` holds. Given my expertise, I'm immediately thinking this likely represents a PCI device table, specific to the `bnge` driver, which, if I recall correctly, handles Broadcom Gigabit Ethernet controllers. I'm expecting this table to contain crucial information for the driver's device initialization and operation. It should include the PCI vendor and device IDs, potentially revision IDs or subsystem IDs, and associated data like configuration parameters, interrupt mapping information, or function pointers to handle specific device variations. I'll need to figure out the table's structure. Is it an array of structs? A linked list? How is the table indexed or searched? Knowing the exact structure and the data's meaning is the key to understanding how the driver identifies and interacts with the various Broadcom NICs it supports. Let's get to it and decipher its contents. |
| 8/2 | 2026/08/14 02:04 | tool | grepper |
0mArgs:{
"Expression": "bnge_pci_tbl",
"PathPrefix": "drivers/net/ethernet/broadcom/bnge/"
}Results: {
"Output": "drivers/net/ethernet/broadcom/bnge/bnge_core.c=20=static const struct {\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-25-\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:26:static const struct pci_device_id bnge_pci_tbl[] = {\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-27-\t{ PCI_VDEVICE(BROADCOM, 0x1780), .driver_data = BCM57708 },\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-30-};\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:31:MODULE_DEVICE_TABLE(pci, bnge_pci_tbl);\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-32-\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c=424=static struct pci_driver bnge_driver = {\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-425-\t.name\t\t= bnge_driver_name,\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:426:\t.id_table\t= bnge_pci_tbl,\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-427-\t.probe\t\t= bnge_probe_one,\n"
} |
| 9/2 | 2026/08/14 02:04 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 54861 output: 36 thoughts: 155 Thoughts: **Analyzing the Broadcom Device Connection** Okay, so `0x1780` is definitely a Broadcom device. My first thought is, is this device emulated within the QEMU environment? That's the crucial question. I need to figure out the nature of the `bnge` device driver here. Is it a pseudo-device specific to Syzkaller, or is it a real-world Broadcom driver? Hang on a minute, `bnge` isn't a standard Linux driver. The typical Broadcom NetXtreme driver is `bnxt`. That's a key observation. Now I'm leaning toward `bnge` being a Syzkaller-introduced pseudo-device, or possibly a legitimate driver that expects real, physical hardware. If `bnge` is hardware-dependent, then it's certainly not something I can trigger within a QEMU instance. So, the next logical step is to confirm whether `bnge` is a synthetic device crafted by Syzkaller for fuzzing purposes. That's where I should focus. |
| 10/2 | 2026/08/14 02:04 | tool | grepper |
0mArgs:{
"Expression": "bnge_",
"PathPrefix": "drivers/net/ethernet/broadcom/bnge/"
}Results: {
"Output": "\nFull output is too long, showing 500 out of 7181 lines.\nUse more precise expression if possible.\n\ndrivers/net/ethernet/broadcom/bnge/Makefile=3=obj-$(CONFIG_BNGE) += bng_en.o\ndrivers/net/ethernet/broadcom/bnge/Makefile-4-\ndrivers/net/ethernet/broadcom/bnge/Makefile:5:bng_en-y := bnge_core.o \\\ndrivers/net/ethernet/broadcom/bnge/Makefile:6:\t bnge_devlink.o \\\ndrivers/net/ethernet/broadcom/bnge/Makefile:7:\t bnge_hwrm.o \\\ndrivers/net/ethernet/broadcom/bnge/Makefile:8:\t bnge_hwrm_lib.o \\\ndrivers/net/ethernet/broadcom/bnge/Makefile:9:\t bnge_rmem.o \\\ndrivers/net/ethernet/broadcom/bnge/Makefile:10:\t bnge_resc.o \\\ndrivers/net/ethernet/broadcom/bnge/Makefile:11:\t bnge_netdev.o \\\ndrivers/net/ethernet/broadcom/bnge/Makefile:12:\t bnge_ethtool.o \\\ndrivers/net/ethernet/broadcom/bnge/Makefile:13:\t bnge_auxr.o \\\ndrivers/net/ethernet/broadcom/bnge/Makefile:14:\t bnge_txrx.o \\\ndrivers/net/ethernet/broadcom/bnge/Makefile:15:\t bnge_link.o \\\ndrivers/net/ethernet/broadcom/bnge/Makefile:16:\t bnge_vnic.o \\\ndrivers/net/ethernet/broadcom/bnge/Makefile:17:\t bnge_filter.o\n--\ndrivers/net/ethernet/broadcom/bnge/bnge.h-11-#include \u003clinux/bnge/hsi.h\u003e\ndrivers/net/ethernet/broadcom/bnge/bnge.h:12:#include \"bnge_rmem.h\"\ndrivers/net/ethernet/broadcom/bnge/bnge.h:13:#include \"bnge_resc.h\"\ndrivers/net/ethernet/broadcom/bnge/bnge.h:14:#include \"bnge_auxr.h\"\ndrivers/net/ethernet/broadcom/bnge/bnge.h-15-\n--\ndrivers/net/ethernet/broadcom/bnge/bnge.h-19-\ndrivers/net/ethernet/broadcom/bnge/bnge.h:20:extern char bnge_driver_name[];\ndrivers/net/ethernet/broadcom/bnge/bnge.h-21-\ndrivers/net/ethernet/broadcom/bnge/bnge.h=22=enum board_idx {\n--\ndrivers/net/ethernet/broadcom/bnge/bnge.h-25-\ndrivers/net/ethernet/broadcom/bnge/bnge.h:26:struct bnge_auxr_priv {\ndrivers/net/ethernet/broadcom/bnge/bnge.h-27-\tstruct auxiliary_device aux_dev;\ndrivers/net/ethernet/broadcom/bnge/bnge.h:28:\tstruct bnge_auxr_dev *auxr_dev;\ndrivers/net/ethernet/broadcom/bnge/bnge.h-29-\tint id;\n--\ndrivers/net/ethernet/broadcom/bnge/bnge.h-31-\ndrivers/net/ethernet/broadcom/bnge/bnge.h:32:struct bnge_pf_info {\ndrivers/net/ethernet/broadcom/bnge/bnge.h-33-\tu16\tfw_fid;\n--\ndrivers/net/ethernet/broadcom/bnge/bnge.h=82=enum {\n--\ndrivers/net/ethernet/broadcom/bnge/bnge.h-94-#define BNGE_MAX_QUEUE\t\t8\ndrivers/net/ethernet/broadcom/bnge/bnge.h:95:struct bnge_queue_info {\ndrivers/net/ethernet/broadcom/bnge/bnge.h-96-\tu8 queue_id;\n--\ndrivers/net/ethernet/broadcom/bnge/bnge.h-104-\ndrivers/net/ethernet/broadcom/bnge/bnge.h:105:struct bnge_dev {\ndrivers/net/ethernet/broadcom/bnge/bnge.h-106-\tstruct device\t*dev;\n--\ndrivers/net/ethernet/broadcom/bnge/bnge.h-146-\ndrivers/net/ethernet/broadcom/bnge/bnge.h:147:\tstruct bnge_pf_info\tpf;\ndrivers/net/ethernet/broadcom/bnge/bnge.h-148-\n--\ndrivers/net/ethernet/broadcom/bnge/bnge.h-155-\t/* Backing stores */\ndrivers/net/ethernet/broadcom/bnge/bnge.h:156:\tstruct bnge_ctx_mem_info\t*ctx;\ndrivers/net/ethernet/broadcom/bnge/bnge.h-157-\n--\ndrivers/net/ethernet/broadcom/bnge/bnge.h-159-\ndrivers/net/ethernet/broadcom/bnge/bnge.h:160:\tstruct bnge_hw_resc\thw_resc;\ndrivers/net/ethernet/broadcom/bnge/bnge.h-161-\n--\ndrivers/net/ethernet/broadcom/bnge/bnge.h-198-\tu8\t\t\tmax_lltc;\t/* lossless TCs */\ndrivers/net/ethernet/broadcom/bnge/bnge.h:199:\tstruct bnge_queue_info\tq_info[BNGE_MAX_QUEUE];\ndrivers/net/ethernet/broadcom/bnge/bnge.h-200-\tu8\t\t\ttc_to_qidx[BNGE_MAX_QUEUE];\n--\ndrivers/net/ethernet/broadcom/bnge/bnge.h-204-\ndrivers/net/ethernet/broadcom/bnge/bnge.h:205:\tstruct bnge_irq\t\t*irq_tbl;\ndrivers/net/ethernet/broadcom/bnge/bnge.h-206-\tu16\t\t\tirqs_acquired;\ndrivers/net/ethernet/broadcom/bnge/bnge.h-207-\ndrivers/net/ethernet/broadcom/bnge/bnge.h:208:\tstruct bnge_auxr_priv\t*aux_priv;\ndrivers/net/ethernet/broadcom/bnge/bnge.h:209:\tstruct bnge_auxr_dev\t*auxr_dev;\ndrivers/net/ethernet/broadcom/bnge/bnge.h-210-\ndrivers/net/ethernet/broadcom/bnge/bnge.h:211:\tstruct bnge_link_info\tlink_info;\ndrivers/net/ethernet/broadcom/bnge/bnge.h-212-\n--\ndrivers/net/ethernet/broadcom/bnge/bnge.h-216-\ndrivers/net/ethernet/broadcom/bnge/bnge.h:217:static inline bool bnge_is_roce_en(struct bnge_dev *bd)\ndrivers/net/ethernet/broadcom/bnge/bnge.h-218-{\n--\ndrivers/net/ethernet/broadcom/bnge/bnge.h-221-\ndrivers/net/ethernet/broadcom/bnge/bnge.h:222:static inline bool bnge_is_arfs_cap(struct bnge_dev *bd)\ndrivers/net/ethernet/broadcom/bnge/bnge.h-223-{\n--\ndrivers/net/ethernet/broadcom/bnge/bnge.h-226-\ndrivers/net/ethernet/broadcom/bnge/bnge.h:227:static inline bool bnge_is_agg_reqd(struct bnge_dev *bd)\ndrivers/net/ethernet/broadcom/bnge/bnge.h-228-{\ndrivers/net/ethernet/broadcom/bnge/bnge.h-229-\tif (bd-\u003enetdev) {\ndrivers/net/ethernet/broadcom/bnge/bnge.h:230:\t\tstruct bnge_net *bn = netdev_priv(bd-\u003enetdev);\ndrivers/net/ethernet/broadcom/bnge/bnge.h-231-\n--\ndrivers/net/ethernet/broadcom/bnge/bnge.h-241-\ndrivers/net/ethernet/broadcom/bnge/bnge.h:242:static inline void bnge_writeq(struct bnge_dev *bd, u64 val,\ndrivers/net/ethernet/broadcom/bnge/bnge.h-243-\t\t\t void __iomem *addr)\n--\ndrivers/net/ethernet/broadcom/bnge/bnge.h-254-/* For TX and RX ring doorbells */\ndrivers/net/ethernet/broadcom/bnge/bnge.h:255:static inline void bnge_db_write(struct bnge_dev *bd, struct bnge_db_info *db,\ndrivers/net/ethernet/broadcom/bnge/bnge.h-256-\t\t\t\t u32 idx)\ndrivers/net/ethernet/broadcom/bnge/bnge.h-257-{\ndrivers/net/ethernet/broadcom/bnge/bnge.h:258:\tbnge_writeq(bd, db-\u003edb_key64 | DB_RING_IDX(db, idx),\ndrivers/net/ethernet/broadcom/bnge/bnge.h-259-\t\t db-\u003edoorbell);\n--\ndrivers/net/ethernet/broadcom/bnge/bnge.h-261-\ndrivers/net/ethernet/broadcom/bnge/bnge.h:262:bool bnge_aux_registered(struct bnge_dev *bd);\ndrivers/net/ethernet/broadcom/bnge/bnge.h:263:u16 bnge_aux_get_msix(struct bnge_dev *bd);\ndrivers/net/ethernet/broadcom/bnge/bnge.h-264-\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c-19-#include \"bnge.h\"\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c:20:#include \"bnge_hwrm.h\"\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c:21:#include \"bnge_auxr.h\"\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c-22-\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c:23:static DEFINE_IDA(bnge_aux_dev_ids);\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c-24-\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c:25:static void bnge_fill_msix_vecs(struct bnge_dev *bd,\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c:26:\t\t\t\tstruct bnge_msix_info *info)\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c-27-{\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c:28:\tstruct bnge_auxr_dev *auxr_dev = bd-\u003eauxr_dev;\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c-29-\tint num_msix, i;\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c-42-\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c:43:int bnge_register_dev(struct bnge_auxr_dev *auxr_dev,\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c-44-\t\t void *handle)\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c-45-{\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c:46:\tstruct bnge_dev *bd = pci_get_drvdata(auxr_dev-\u003epdev);\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c:47:\tstruct bnge_auxr_info *auxr_info;\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c-48-\tint rc = 0;\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c-56-\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c:57:\tif (!bnge_aux_has_enough_resources(bd)) {\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c-58-\t\trc = -ENOMEM;\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c-66-\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c:67:\tbnge_fill_msix_vecs(bd, bd-\u003eauxr_dev-\u003emsix_info);\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c-68-\tauxr_dev-\u003eflags |= BNGE_ARDEV_MSIX_ALLOC;\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c-74-}\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c:75:EXPORT_SYMBOL(bnge_register_dev);\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c-76-\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c:77:void bnge_unregister_dev(struct bnge_auxr_dev *auxr_dev)\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c-78-{\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c:79:\tstruct bnge_dev *bd = pci_get_drvdata(auxr_dev-\u003epdev);\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c:80:\tstruct bnge_auxr_info *auxr_info;\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c-81-\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c-91-}\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c:92:EXPORT_SYMBOL(bnge_unregister_dev);\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c-93-\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c:94:int bnge_send_msg(struct bnge_auxr_dev *auxr_dev, struct bnge_fw_msg *fw_msg)\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c-95-{\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c:96:\tstruct bnge_dev *bd = pci_get_drvdata(auxr_dev-\u003epdev);\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c-97-\tstruct output *resp;\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c-101-\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c:102:\trc = bnge_hwrm_req_init(bd, req, 0 /* don't care */);\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c-103-\tif (rc)\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c-105-\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c:106:\trc = bnge_hwrm_req_replace(bd, req, fw_msg-\u003emsg, fw_msg-\u003emsg_len);\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c-107-\tif (rc)\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c-109-\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c:110:\tbnge_hwrm_req_timeout(bd, req, fw_msg-\u003etimeout);\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c:111:\tresp = bnge_hwrm_req_hold(bd, req);\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c:112:\trc = bnge_hwrm_req_send(bd, req);\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c-113-\tresp_len = le16_to_cpu(resp-\u003eresp_len);\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c-120-drop_req:\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c:121:\tbnge_hwrm_req_drop(bd, req);\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c-122-\treturn rc;\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c-123-}\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c:124:EXPORT_SYMBOL(bnge_send_msg);\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c-125-\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c:126:void bnge_rdma_aux_device_uninit(struct bnge_dev *bd)\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c-127-{\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c:128:\tstruct bnge_auxr_priv *aux_priv;\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c-129-\tstruct auxiliary_device *adev;\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c-139-\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c:140:static void bnge_aux_dev_release(struct device *dev)\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c-141-{\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c:142:\tstruct bnge_auxr_priv *aux_priv =\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c:143:\t\t\tcontainer_of(dev, struct bnge_auxr_priv, aux_dev.dev);\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c:144:\tstruct bnge_auxr_dev *auxr_dev = aux_priv-\u003eauxr_dev;\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c:145:\tstruct bnge_dev *bd = pci_get_drvdata(to_pci_dev(dev-\u003eparent));\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c-146-\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c:147:\tida_free(\u0026bnge_aux_dev_ids, aux_priv-\u003eid);\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c-148-\tif (auxr_dev) {\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c-156-\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c:157:void bnge_rdma_aux_device_del(struct bnge_dev *bd)\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c-158-{\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c-164-\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c:165:static void bnge_set_auxr_dev_info(struct bnge_auxr_dev *auxr_dev,\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c:166:\t\t\t\t struct bnge_dev *bd)\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c-167-{\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c-185-\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c:186:void bnge_rdma_aux_device_add(struct bnge_dev *bd)\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c-187-{\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c-205-\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c:206:void bnge_rdma_aux_device_init(struct bnge_dev *bd)\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c-207-{\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c-208-\tstruct auxiliary_device *aux_dev;\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c:209:\tstruct bnge_auxr_info *auxr_info;\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c:210:\tstruct bnge_auxr_priv *aux_priv;\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c:211:\tstruct bnge_auxr_dev *auxr_dev;\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c-212-\tint rc;\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c-213-\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c:214:\tif (!bnge_is_roce_en(bd))\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c-215-\t\treturn;\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c-220-\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c:221:\taux_priv-\u003eid = ida_alloc(\u0026bnge_aux_dev_ids, GFP_KERNEL);\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c-222-\tif (aux_priv-\u003eid \u003c 0) {\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c-231-\taux_dev-\u003edev.parent = \u0026bd-\u003epdev-\u003edev;\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c:232:\taux_dev-\u003edev.release = bnge_aux_dev_release;\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c-233-\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c-235-\tif (rc) {\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c:236:\t\tida_free(\u0026bnge_aux_dev_ids, aux_priv-\u003eid);\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c-237-\t\tkfree(aux_priv);\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c-253-\tbd-\u003eauxr_dev = auxr_dev;\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c:254:\tbnge_set_auxr_dev_info(auxr_dev, bd);\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c-255-\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.h=15=struct bnge;\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.h-16-\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.h:17:struct bnge_msix_info {\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.h-18-\tu32\tvector;\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.h-22-\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.h:23:struct bnge_fw_msg {\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.h-24-\tvoid\t*msg;\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.h-30-\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.h:31:struct bnge_auxr_info {\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.h-32-\tvoid\t\t*handle;\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.h=36=enum {\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.h-44-\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.h:45:struct bnge_auxr_dev {\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.h-46-\tstruct net_device\t*net;\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.h-49-\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.h:50:\tstruct bnge_msix_info\tmsix_info[BNGE_MAX_ROCE_MSIX];\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.h-51-\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.h-53-\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.h:54:\tstruct bnge_auxr_info\t*auxr_info;\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.h-55-\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.h-74-\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.h:75:void bnge_rdma_aux_device_uninit(struct bnge_dev *bdev);\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.h:76:void bnge_rdma_aux_device_del(struct bnge_dev *bdev);\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.h:77:void bnge_rdma_aux_device_add(struct bnge_dev *bdev);\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.h:78:void bnge_rdma_aux_device_init(struct bnge_dev *bdev);\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.h:79:int bnge_register_dev(struct bnge_auxr_dev *adev,\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.h-80-\t\t void *handle);\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.h:81:void bnge_unregister_dev(struct bnge_auxr_dev *adev);\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.h:82:int bnge_send_msg(struct bnge_auxr_dev *adev, struct bnge_fw_msg *fw_msg);\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.h-83-\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-9-#include \"bnge.h\"\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:10:#include \"bnge_devlink.h\"\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:11:#include \"bnge_hwrm.h\"\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:12:#include \"bnge_hwrm_lib.h\"\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:13:#include \"bnge_link.h\"\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-14-\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c=16=MODULE_DESCRIPTION(DRV_SUMMARY);\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-17-\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:18:char bnge_driver_name[] = DRV_NAME;\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-19-\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c=20=static const struct {\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-25-\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:26:static const struct pci_device_id bnge_pci_tbl[] = {\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-27-\t{ PCI_VDEVICE(BROADCOM, 0x1780), .driver_data = BCM57708 },\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-30-};\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:31:MODULE_DEVICE_TABLE(pci, bnge_pci_tbl);\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-32-\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:33:static void bnge_print_device_info(struct pci_dev *pdev, enum board_idx idx)\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-34-{\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-42-\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:43:bool bnge_aux_registered(struct bnge_dev *bd)\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-44-{\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:45:\tstruct bnge_auxr_dev *ba_dev = bd-\u003eauxr_dev;\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-46-\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-52-\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:53:static void bnge_nvm_cfg_ver_get(struct bnge_dev *bd)\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-54-{\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-56-\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:57:\tif (!bnge_hwrm_nvm_dev_info(bd, \u0026nvm_info))\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-58-\t\tsnprintf(bd-\u003envm_cfg_ver, FW_VER_STR_LEN, \"%d.%d.%d\",\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-62-\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:63:static int bnge_func_qcaps(struct bnge_dev *bd)\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-64-{\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-66-\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:67:\trc = bnge_hwrm_func_qcaps(bd);\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-68-\tif (rc)\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-70-\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:71:\trc = bnge_hwrm_queue_qportcfg(bd);\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-72-\tif (rc) {\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-79-\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:80:static int bnge_func_qrcaps_qcfg(struct bnge_dev *bd)\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-81-{\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-83-\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:84:\trc = bnge_hwrm_func_resc_qcaps(bd);\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-85-\tif (rc) {\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-89-\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:90:\trc = bnge_hwrm_func_qcfg(bd);\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-91-\tif (rc) {\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-95-\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:96:\trc = bnge_hwrm_cfa_adv_flow_mgnt_qcaps(bd);\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-97-\tif (rc) {\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-102-\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:103:\trc = bnge_hwrm_vnic_qcaps(bd);\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-104-\tif (rc) {\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-111-\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:112:static void bnge_fw_unregister_dev(struct bnge_dev *bd)\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-113-{\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-114-\t/* ctx mem free after unrgtr only */\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:115:\tbnge_hwrm_func_drv_unrgtr(bd);\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:116:\tbnge_free_ctx_mem(bd);\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-117-}\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-118-\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:119:static void bnge_set_dflt_rss_hash_type(struct bnge_dev *bd)\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-120-{\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-128-\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:129:static int bnge_fw_register_dev(struct bnge_dev *bd)\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-130-{\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-133-\tbd-\u003efw_cap = 0;\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:134:\trc = bnge_hwrm_ver_get(bd);\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-135-\tif (rc) {\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-139-\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:140:\tbnge_nvm_cfg_ver_get(bd);\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-141-\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:142:\trc = bnge_hwrm_func_reset(bd);\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-143-\tif (rc) {\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-147-\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:148:\tbnge_hwrm_fw_set_time(bd);\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-149-\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-150-\t/* Get the resources and configuration from firmware */\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:151:\trc = bnge_func_qcaps(bd);\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-152-\tif (rc) {\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-156-\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:157:\trc = bnge_alloc_ctx_mem(bd);\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-158-\tif (rc) {\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-162-\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:163:\trc = bnge_hwrm_func_drv_rgtr(bd);\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-164-\tif (rc) {\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-168-\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:169:\trc = bnge_func_qrcaps_qcfg(bd);\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-170-\tif (rc) {\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-174-\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:175:\tbnge_set_dflt_rss_hash_type(bd);\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-176-\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-179-err_func_unrgtr:\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:180:\tbnge_hwrm_func_drv_unrgtr(bd);\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-181-err_free_ctx_mem:\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:182:\tbnge_free_ctx_mem(bd);\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-183-\treturn rc;\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-185-\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:186:static void bnge_pci_disable(struct pci_dev *pdev)\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-187-{\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-192-\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:193:static int bnge_pci_enable(struct pci_dev *pdev)\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-194-{\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-209-\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:210:\trc = pci_request_regions(pdev, bnge_driver_name);\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-211-\tif (rc) {\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-226-\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:227:static void bnge_unmap_bars(struct pci_dev *pdev)\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-228-{\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:229:\tstruct bnge_dev *bd = pci_get_drvdata(pdev);\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-230-\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-241-\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:242:static void bnge_set_max_func_irqs(struct bnge_dev *bd,\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-243-\t\t\t\t unsigned int max_irqs)\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-247-\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:248:static int bnge_get_max_irq(struct pci_dev *pdev)\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-249-{\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-255-\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:256:static int bnge_map_db_bar(struct bnge_dev *bd)\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-257-{\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-267-\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:268:static int bnge_probe_one(struct pci_dev *pdev, const struct pci_device_id *ent)\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-269-{\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-270-\tunsigned int max_irqs;\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:271:\tstruct bnge_dev *bd;\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-272-\tint rc;\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-286-\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:287:\trc = bnge_pci_enable(pdev);\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-288-\tif (rc)\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-290-\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:291:\tbnge_print_device_info(pdev, ent-\u003edriver_data);\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-292-\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:293:\tbd = bnge_devlink_alloc(pdev);\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-294-\tif (!bd) {\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-306-\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:307:\trc = bnge_init_hwrm_resources(bd);\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-308-\tif (rc)\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-310-\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:311:\trc = bnge_fw_register_dev(bd);\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-312-\tif (rc) {\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-316-\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:317:\tbnge_devlink_register(bd);\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-318-\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:319:\tmax_irqs = bnge_get_max_irq(pdev);\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:320:\tbnge_set_max_func_irqs(bd, max_irqs);\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-321-\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:322:\tbnge_aux_init_dflt_config(bd);\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-323-\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:324:\trc = bnge_net_init_dflt_config(bd);\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-325-\tif (rc) {\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-330-\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:331:\trc = bnge_map_db_bar(bd);\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-332-\tif (rc) {\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-341-\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:342:\tbnge_rdma_aux_device_init(bd);\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-343-\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:344:\trc = bnge_alloc_irqs(bd);\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-345-\tif (rc) {\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-349-\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:350:\trc = bnge_netdev_alloc(bd, max_irqs);\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-351-\tif (rc)\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-353-\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:354:\tbnge_rdma_aux_device_add(bd);\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-355-\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-360-err_free_irq:\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:361:\tbnge_free_irqs(bd);\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-362-\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-363-err_uninit_auxr:\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:364:\tbnge_rdma_aux_device_uninit(bd);\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-365-\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-366-err_config_uninit:\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:367:\tbnge_net_uninit_dflt_config(bd);\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-368-\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-369-err_fw_reg:\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:370:\tbnge_devlink_unregister(bd);\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:371:\tbnge_fw_unregister_dev(bd);\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-372-\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-373-err_hwrm_cleanup:\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:374:\tbnge_cleanup_hwrm_resources(bd);\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-375-\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-376-err_bar_unmap:\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:377:\tbnge_unmap_bars(pdev);\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-378-\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-379-err_devl_free:\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:380:\tbnge_devlink_free(bd);\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-381-\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-382-err_pci_disable:\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:383:\tbnge_pci_disable(pdev);\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-384-\treturn rc;\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-386-\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c:387:static void bnge_remove_one(struct pci_dev *pdev)\ndrivers/net/ethernet/broadcom/bnge/bnge_core.c-388-{\n\n"
} |
| 11/2 | 2026/08/14 02:04 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 69079 output: 46 thoughts: 430 Thoughts: **Analyzing the `bnge` Driver** Okay, so I'm trying to figure out what `bnge` *actually* is. My initial question is whether it's a Syzkaller pseudo-device or a genuine piece of hardware. I see the PCI ID, `0x1780`, and I need to determine if that maps to a real Broadcom device or if it's been cooked up specifically for Syzkaller. My first instinct is to check the usual Syzkaller hangouts – places like `drivers/net/ethernet/syzkaller/` – but hey, maybe they’ve integrated it into the `broadcom/bnge` directory directly? Wait, the kernel doesn't have it upstream, so... it's either Syzkaller-specific or a brand-new driver under review. The "syz-cluster" tag in the commit gives it away! It’s likely a patch currently being reviewed. But is the `bnge` driver for real? Let's test if it is a real driver by checking if it is reachable in QEMU. Real drivers for physical PCIe cards are *never* reachable in QEMU, unless there's an emulator for it. I know QEMU emulates e1000, e1000e, rtl8139, pcnet, virtio-net, vmxnet3, igb, and tulip, but I'm pretty sure it doesn't do Broadcom NetXtreme-E (bnxt). So, if `bnge` is for real hardware, it's a no-go in QEMU. Back to the Syzkaller hypothesis! Now, the classic telltale signs of a Syzkaller pseudo-device is a "syz" in the name or an obvious location under the `syzkaller` directory. But wait, could this be a Syzkaller mock driver for `bnxt`? Okay, I'm going to look at `drivers/net/ethernet/broadcom/bnge/bnge_core.c` to see if it is a mock driver. |
| 12/2 | 2026/08/14 02:04 | tool | read-file |
0mArgs:{
"File": "drivers/net/ethernet/broadcom/bnge/bnge_core.c",
"FirstLine": 1,
"LineCount": 100
}Results: {
"Contents": " 1:\t// SPDX-License-Identifier: GPL-2.0\n 2:\t// Copyright (c) 2025 Broadcom.\n 3:\t\n 4:\t#include \u003clinux/init.h\u003e\n 5:\t#include \u003clinux/crash_dump.h\u003e\n 6:\t#include \u003clinux/module.h\u003e\n 7:\t#include \u003clinux/pci.h\u003e\n 8:\t\n 9:\t#include \"bnge.h\"\n 10:\t#include \"bnge_devlink.h\"\n 11:\t#include \"bnge_hwrm.h\"\n 12:\t#include \"bnge_hwrm_lib.h\"\n 13:\t#include \"bnge_link.h\"\n 14:\t\n 15:\tMODULE_LICENSE(\"GPL\");\n 16:\tMODULE_DESCRIPTION(DRV_SUMMARY);\n 17:\t\n 18:\tchar bnge_driver_name[] = DRV_NAME;\n 19:\t\n 20:\tstatic const struct {\n 21:\t\tchar *name;\n 22:\t} board_info[] = {\n 23:\t\t[BCM57708] = { \"Broadcom BCM57708 ThorUltra 50Gb/100Gb/200Gb/400Gb/800Gb Ethernet\" },\n 24:\t};\n 25:\t\n 26:\tstatic const struct pci_device_id bnge_pci_tbl[] = {\n 27:\t\t{ PCI_VDEVICE(BROADCOM, 0x1780), .driver_data = BCM57708 },\n 28:\t\t/* Required last entry */\n 29:\t\t{0, }\n 30:\t};\n 31:\tMODULE_DEVICE_TABLE(pci, bnge_pci_tbl);\n 32:\t\n 33:\tstatic void bnge_print_device_info(struct pci_dev *pdev, enum board_idx idx)\n 34:\t{\n 35:\t\tstruct device *dev = \u0026pdev-\u003edev;\n 36:\t\n 37:\t\tdev_info(dev, \"%s found at mem %lx\\n\", board_info[idx].name,\n 38:\t\t\t (long)pci_resource_start(pdev, 0));\n 39:\t\n 40:\t\tpcie_print_link_status(pdev);\n 41:\t}\n 42:\t\n 43:\tbool bnge_aux_registered(struct bnge_dev *bd)\n 44:\t{\n 45:\t\tstruct bnge_auxr_dev *ba_dev = bd-\u003eauxr_dev;\n 46:\t\n 47:\t\tif (ba_dev \u0026\u0026 ba_dev-\u003eauxr_info-\u003emsix_requested)\n 48:\t\t\treturn true;\n 49:\t\n 50:\t\treturn false;\n 51:\t}\n 52:\t\n 53:\tstatic void bnge_nvm_cfg_ver_get(struct bnge_dev *bd)\n 54:\t{\n 55:\t\tstruct hwrm_nvm_get_dev_info_output nvm_info;\n 56:\t\n 57:\t\tif (!bnge_hwrm_nvm_dev_info(bd, \u0026nvm_info))\n 58:\t\t\tsnprintf(bd-\u003envm_cfg_ver, FW_VER_STR_LEN, \"%d.%d.%d\",\n 59:\t\t\t\t nvm_info.nvm_cfg_ver_maj, nvm_info.nvm_cfg_ver_min,\n 60:\t\t\t\t nvm_info.nvm_cfg_ver_upd);\n 61:\t}\n 62:\t\n 63:\tstatic int bnge_func_qcaps(struct bnge_dev *bd)\n 64:\t{\n 65:\t\tint rc;\n 66:\t\n 67:\t\trc = bnge_hwrm_func_qcaps(bd);\n 68:\t\tif (rc)\n 69:\t\t\treturn rc;\n 70:\t\n 71:\t\trc = bnge_hwrm_queue_qportcfg(bd);\n 72:\t\tif (rc) {\n 73:\t\t\tdev_err(bd-\u003edev, \"query qportcfg failure rc: %d\\n\", rc);\n 74:\t\t\treturn rc;\n 75:\t\t}\n 76:\t\n 77:\t\treturn 0;\n 78:\t}\n 79:\t\n 80:\tstatic int bnge_func_qrcaps_qcfg(struct bnge_dev *bd)\n 81:\t{\n 82:\t\tint rc;\n 83:\t\n 84:\t\trc = bnge_hwrm_func_resc_qcaps(bd);\n 85:\t\tif (rc) {\n 86:\t\t\tdev_err(bd-\u003edev, \"query resc caps failure rc: %d\\n\", rc);\n 87:\t\t\treturn rc;\n 88:\t\t}\n 89:\t\n 90:\t\trc = bnge_hwrm_func_qcfg(bd);\n 91:\t\tif (rc) {\n 92:\t\t\tdev_err(bd-\u003edev, \"query config failure rc: %d\\n\", rc);\n 93:\t\t\treturn rc;\n 94:\t\t}\n 95:\t\n 96:\t\trc = bnge_hwrm_cfa_adv_flow_mgnt_qcaps(bd);\n 97:\t\tif (rc) {\n 98:\t\t\tdev_warn(bd-\u003edev, \"hwrm query adv flow mgnt failure rc: %d\\n\",\n 99:\t\t\t\t rc);\n 100:\t\t\treturn rc;\n"
} |
| 13/2 | 2026/08/14 02:04 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 71471 output: 39 thoughts: 202 Thoughts: **My Assessment of the Broadcom BCM57708 Driver in a QEMU Context** Okay, so we're looking at the "Broadcom BCM57708 ThorUltra" – that's a serious piece of network hardware, a real NIC. My first thought is: is this thing emulated in QEMU? Logically, the answer is no, not directly. That's a hardware driver we're talking about. Unless something really specific is set up, it won't be reachable within a QEMU environment. Now, hold on. If it's a *real* hardware driver, it's virtually impossible to test through standard QEMU; that's just not how it works. Let's make sure, though, that there isn't some obscure emulation that I'm missing. I would normally scan the QEMU source for something like `bnge`, but I don't have it handy. However, given my experience, I can almost guarantee that standard QEMU doesn't offer emulation for the Broadcom NetXtreme-E or ThorUltra families. Wait, is `bnge` possibly a mock driver used with syzkaller? It's time to check if there is a `hwrm` mock within the driver. |
| 14/2 | 2026/08/14 02:04 | tool | grepper |
0mArgs:{
"Expression": "hwrm_req_send",
"PathPrefix": "drivers/net/ethernet/broadcom/bnge/"
}Results: {
"Output": "drivers/net/ethernet/broadcom/bnge/bnge_auxr.c=94=int bnge_send_msg(struct bnge_auxr_dev *auxr_dev, struct bnge_fw_msg *fw_msg)\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c-111-\tresp = bnge_hwrm_req_hold(bd, req);\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c:112:\trc = bnge_hwrm_req_send(bd, req);\ndrivers/net/ethernet/broadcom/bnge/bnge_auxr.c-113-\tresp_len = le16_to_cpu(resp-\u003eresp_len);\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm.c=288=static int __hwrm_send_ctx(struct bnge_dev *bd, struct bnge_hwrm_ctx *ctx)\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm.c-464-\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm.c:465:int bnge_hwrm_req_send(struct bnge_dev *bd, void *req)\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm.c-466-{\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm.c-474-\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm.c:475:int bnge_hwrm_req_send_silent(struct bnge_dev *bd, void *req)\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm.c-476-{\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm.c-477-\tbnge_hwrm_req_flags(bd, req, BNGE_HWRM_CTX_SILENT);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm.c:478:\treturn bnge_hwrm_req_send(bd, req);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm.c-479-}\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm.h=103=void bnge_hwrm_req_timeout(struct bnge_dev *bd, void *req,\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm.h-104-\t\t\t unsigned int timeout);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm.h:105:int bnge_hwrm_req_send(struct bnge_dev *bd, void *req);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm.h:106:int bnge_hwrm_req_send_silent(struct bnge_dev *bd, void *req);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm.h-107-void bnge_hwrm_req_alloc_flags(struct bnge_dev *bd, void *req, gfp_t flags);\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c=29=int bnge_hwrm_ver_get(struct bnge_dev *bd)\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-47-\tresp = bnge_hwrm_req_hold(bd, req);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c:48:\trc = bnge_hwrm_req_send(bd, req);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-49-\tif (rc)\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c=124=bnge_hwrm_nvm_dev_info(struct bnge_dev *bd,\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-135-\tresp = bnge_hwrm_req_hold(bd, req);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c:136:\trc = bnge_hwrm_req_send(bd, req);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-137-\tif (!rc)\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c=143=int bnge_hwrm_func_reset(struct bnge_dev *bd)\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-153-\tbnge_hwrm_req_timeout(bd, req, BNGE_HWRM_RESET_TIMEOUT);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c:154:\treturn bnge_hwrm_req_send(bd, req);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-155-}\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c=157=int bnge_hwrm_fw_set_time(struct bnge_dev *bd)\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-174-\treq-\u003esecond = tm.tm_sec;\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c:175:\treturn bnge_hwrm_req_send(bd, req);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-176-}\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c=178=int bnge_hwrm_func_drv_rgtr(struct bnge_dev *bd)\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-214-\tresp = bnge_hwrm_req_hold(bd, req);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c:215:\trc = bnge_hwrm_req_send(bd, req);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-216-\tif (!rc) {\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c=226=int bnge_hwrm_func_drv_unrgtr(struct bnge_dev *bd)\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-236-\t\treturn rc;\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c:237:\treturn bnge_hwrm_req_send(bd, req);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-238-}\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c=278=int bnge_hwrm_func_backing_store_qcaps(struct bnge_dev *bd)\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-306-\t\treq-\u003etype = cpu_to_le16(type);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c:307:\t\trc = bnge_hwrm_req_send(bd, req);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-308-\t\tif (rc)\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c=364=int bnge_hwrm_func_backing_store(struct bnge_dev *bd,\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-405-\t\t\t\tcpu_to_le32(BNGE_BS_CFG_ALL_DONE);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c:406:\t\trc = bnge_hwrm_req_send(bd, req);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-407-\t}\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c=413=static int bnge_hwrm_get_rings(struct bnge_dev *bd)\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-427-\tresp = bnge_hwrm_req_hold(bd, req);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c:428:\trc = bnge_hwrm_req_send(bd, req);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-429-\tif (rc) {\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c=498=bnge_hwrm_reserve_pf_rings(struct bnge_dev *bd, struct bnge_hw_rings *hwr)\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-511-\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c:512:\trc = bnge_hwrm_req_send(bd, req);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-513-\tif (rc)\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c=524=int bnge_hwrm_func_qcfg(struct bnge_dev *bd)\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-535-\tresp = bnge_hwrm_req_hold(bd, req);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c:536:\trc = bnge_hwrm_req_send(bd, req);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-537-\tif (rc)\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c=559=int bnge_hwrm_func_resc_qcaps(struct bnge_dev *bd)\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-571-\tresp = bnge_hwrm_req_hold(bd, req);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c:572:\trc = bnge_hwrm_req_send_silent(bd, req);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-573-\tif (rc)\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c=602=int bnge_hwrm_func_qcaps(struct bnge_dev *bd)\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-615-\tresp = bnge_hwrm_req_hold(bd, req);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c:616:\trc = bnge_hwrm_req_send(bd, req);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-617-\tif (rc)\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c=643=int bnge_hwrm_vnic_qcaps(struct bnge_dev *bd)\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-656-\tresp = bnge_hwrm_req_hold(bd, req);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c:657:\trc = bnge_hwrm_req_send(bd, req);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-658-\tif (!rc) {\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c=692=int bnge_hwrm_queue_qportcfg(struct bnge_dev *bd)\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-704-\tresp = bnge_hwrm_req_hold(bd, req);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c:705:\trc = bnge_hwrm_req_send(bd, req);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-706-\tif (rc)\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c=742=int bnge_hwrm_vnic_set_hds(struct bnge_net *bn, struct bnge_vnic_info *vnic)\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-764-\treq-\u003evnic_id = cpu_to_le32(vnic-\u003efw_vnic_id);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c:765:\treturn bnge_hwrm_req_send(bd, req);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-766-}\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c=768=int bnge_hwrm_vnic_ctx_alloc(struct bnge_dev *bd,\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-779-\tresp = bnge_hwrm_req_hold(bd, req);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c:780:\trc = bnge_hwrm_req_send(bd, req);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-781-\tif (!rc)\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c=805=int bnge_hwrm_vnic_set_rss(struct bnge_net *bn,\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-819-\tif (!set_rss)\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c:820:\t\treturn bnge_hwrm_req_send(bd, req);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-821-\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-830-\t\treq-\u003erss_ctx_idx = cpu_to_le16(vnic-\u003efw_rss_cos_lb_ctx[i]);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c:831:\t\trc = bnge_hwrm_req_send(bd, req);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-832-\t\tif (rc)\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c=841=int bnge_hwrm_vnic_cfg(struct bnge_net *bn, struct bnge_vnic_info *vnic)\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-868-\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c:869:\treturn bnge_hwrm_req_send(bd, req);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-870-}\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c=872=void bnge_hwrm_update_rss_hash_cfg(struct bnge_net *bn)\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-885-\tresp = bnge_hwrm_req_hold(bd, req);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c:886:\tif (!bnge_hwrm_req_send(bd, req))\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-887-\t\tbd-\u003erss_hash_cfg =\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c=892=int bnge_hwrm_l2_filter_free(struct bnge_dev *bd, struct bnge_l2_filter *fltr)\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-901-\treq-\u003el2_filter_id = fltr-\u003ebase.filter_id;\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c:902:\treturn bnge_hwrm_req_send(bd, req);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-903-}\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c=905=int bnge_hwrm_l2_filter_alloc(struct bnge_dev *bd, struct bnge_l2_filter *fltr)\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-943-\tresp = bnge_hwrm_req_hold(bd, req);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c:944:\trc = bnge_hwrm_req_send(bd, req);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-945-\tif (!rc) {\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c=954=int bnge_hwrm_cfa_ntuple_filter_free(struct bnge_dev *bd,\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-968-\treq-\u003entuple_filter_id = fltr-\u003ebase.filter_id;\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c:969:\treturn bnge_hwrm_req_send(bd, req);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-970-}\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c=1023=int bnge_hwrm_cfa_ntuple_filter_alloc(struct bnge_dev *bd,\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-1079-\tresp = bnge_hwrm_req_hold(bd, req);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c:1080:\trc = bnge_hwrm_req_send(bd, req);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-1081-\tif (!rc)\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c=1087=int bnge_hwrm_cfa_l2_set_rx_mask(struct bnge_dev *bd,\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-1102-\treq-\u003emask = cpu_to_le32(vnic-\u003erx_mask);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c:1103:\treturn bnge_hwrm_req_send_silent(bd, req);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-1104-}\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c=1106=int bnge_hwrm_vnic_alloc(struct bnge_dev *bd, struct bnge_vnic_info *vnic,\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-1123-\tresp = bnge_hwrm_req_hold(bd, req);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c:1124:\trc = bnge_hwrm_req_send(bd, req);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-1125-\tif (!rc)\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c=1131=void bnge_hwrm_vnic_free_one(struct bnge_dev *bd, struct bnge_vnic_info *vnic)\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-1140-\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c:1141:\t\tbnge_hwrm_req_send(bd, req);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-1142-\t\tvnic-\u003efw_vnic_id = INVALID_HW_RING_ID;\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c=1146=void bnge_hwrm_vnic_ctx_free_one(struct bnge_dev *bd,\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-1156-\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c:1157:\tbnge_hwrm_req_send(bd, req);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-1158-\tvnic-\u003efw_rss_cos_lb_ctx[ctx_idx] = INVALID_HW_RING_ID;\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c=1167=int bnge_hwrm_phy_qcaps(struct bnge_dev *bd)\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-1178-\tresp = bnge_hwrm_req_hold(bd, req);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c:1179:\trc = bnge_hwrm_req_send(bd, req);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-1180-\tif (rc)\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c=1212=int bnge_hwrm_set_link_setting(struct bnge_net *bn, bool set_pause)\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-1226-\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c:1227:\trc = bnge_hwrm_req_send(bd, req);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-1228-\tif (!rc)\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c=1234=int bnge_update_link(struct bnge_net *bn, bool chng_link_state)\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-1251-\tresp = bnge_hwrm_req_hold(bd, req);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c:1252:\trc = bnge_hwrm_req_send(bd, req);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-1253-\tif (rc) {\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c=1310=int bnge_hwrm_set_pause(struct bnge_net *bn)\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-1330-\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c:1331:\trc = bnge_hwrm_req_send(bd, req);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-1332-\tif (!rc \u0026\u0026 !pause_autoneg) {\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c=1350=int bnge_hwrm_shutdown_link(struct bnge_dev *bd)\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-1362-\treq-\u003eflags = cpu_to_le32(PORT_PHY_CFG_REQ_FLAGS_FORCE_LINK_DWN);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c:1363:\trc = bnge_hwrm_req_send(bd, req);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-1364-\tif (!rc) {\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c=1375=void bnge_hwrm_stat_ctx_free(struct bnge_net *bn)\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-1390-\t\t\treq-\u003estat_ctx_id = cpu_to_le32(nqr-\u003ehw_stats_ctx_id);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c:1391:\t\t\tbnge_hwrm_req_send(bd, req);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-1392-\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c=1399=int bnge_hwrm_stat_ctx_alloc(struct bnge_net *bn)\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-1419-\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c:1420:\t\trc = bnge_hwrm_req_send(bd, req);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-1421-\t\tif (rc)\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c=1431=int hwrm_ring_free_send_msg(struct bnge_net *bn,\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-1447-\tbnge_hwrm_req_hold(bd, req);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c:1448:\trc = bnge_hwrm_req_send(bd, req);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-1449-\tbnge_hwrm_req_drop(bd, req);\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c=1458=int hwrm_ring_alloc_send_msg(struct bnge_net *bn,\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-1549-\tresp = bnge_hwrm_req_hold(bd, req);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c:1550:\trc = bnge_hwrm_req_send(bd, req);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-1551-\tring_id = le32_to_cpu(resp-\u003ering_id);\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c=1563=int bnge_hwrm_set_async_event_cr(struct bnge_dev *bd, int idx)\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-1574-\treq-\u003easync_event_cr = cpu_to_le16(idx);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c:1575:\treturn bnge_hwrm_req_send(bd, req);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-1576-}\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c=1603=int bnge_hwrm_vnic_set_tpa(struct bnge_dev *bd, struct bnge_vnic_info *vnic,\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-1639-\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c:1640:\treturn bnge_hwrm_req_send(bd, req);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-1641-}\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c=1643=int bnge_hwrm_func_qstat_ext(struct bnge_dev *bd, struct bnge_stats_mem *stats)\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-1660-\tresp = bnge_hwrm_req_hold(bd, req);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c:1661:\trc = bnge_hwrm_req_send(bd, req);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-1662-\tif (!rc) {\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c=1670=int bnge_hwrm_port_qstats_ext(struct bnge_dev *bd, u8 flags)\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-1701-\tresp_qs = bnge_hwrm_req_hold(bd, req_qs);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c:1702:\trc = bnge_hwrm_req_send(bd, req_qs);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-1703-\tif (!rc) {\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-1729-\tresp_qc = bnge_hwrm_req_hold(bd, req_qc);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c:1730:\trc = bnge_hwrm_req_send(bd, req_qc);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-1731-\tif (!rc) {\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c=1759=int bnge_hwrm_port_qstats(struct bnge_dev *bd, u8 flags)\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-1781-\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c:1782:\treturn bnge_hwrm_req_send(bd, req);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-1783-}\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c=1785=int bnge_hwrm_cfa_adv_flow_mgnt_qcaps(struct bnge_dev *bd)\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-1796-\tresp = bnge_hwrm_req_hold(bd, req);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c:1797:\trc = bnge_hwrm_req_send(bd, req);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c-1798-\tif (rc)\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_netdev.c=2757=static int bnge_hwrm_if_change(struct bnge_dev *bd, bool up)\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_netdev.c-2771-\ndrivers/net/ethernet/broadcom/bnge/bnge_netdev.c:2772:\treturn bnge_hwrm_req_send(bd, req);\ndrivers/net/ethernet/broadcom/bnge/bnge_netdev.c-2773-}\n"
} |
| 15/2 | 2026/08/14 02:04 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 80914 output: 49 thoughts: 55 Thoughts: Here's my thought process, as requested: **Deconstructing `bnge_hwrm_req_send`** Okay, so I'm trying to understand how `bnge_hwrm_req_send` actually *works*. The code base is fairly well organized, so I'm thinking the implementation should be in `bnge_hwrm.c`. That's the logical place. Now, the real question is, what's *actually* happening? Is this function directly writing to PCI Memory-Mapped I/O (MMIO), or is it possibly a mock implementation that interacts with the hardware indirectly or through some other layer? I need to check the code itself to determine how it's talking to the underlying hardware. Knowing whether it's MMIO or a mock is fundamental to understanding its behavior and potential performance characteristics. Time to dive into that `bnge_hwrm.c` file and see what's going on. |
| 16/2 | 2026/08/14 02:04 | tool | grepper |
0mArgs:{
"Expression": "bnge_hwrm_req_send",
"PathPrefix": "drivers/net/ethernet/broadcom/bnge/bnge_hwrm.c"
}Results: {
"Output": "drivers/net/ethernet/broadcom/bnge/bnge_hwrm.c=288=static int __hwrm_send_ctx(struct bnge_dev *bd, struct bnge_hwrm_ctx *ctx)\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm.c-464-\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm.c:465:int bnge_hwrm_req_send(struct bnge_dev *bd, void *req)\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm.c-466-{\n--\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm.c-474-\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm.c:475:int bnge_hwrm_req_send_silent(struct bnge_dev *bd, void *req)\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm.c-476-{\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm.c-477-\tbnge_hwrm_req_flags(bd, req, BNGE_HWRM_CTX_SILENT);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm.c:478:\treturn bnge_hwrm_req_send(bd, req);\ndrivers/net/ethernet/broadcom/bnge/bnge_hwrm.c-479-}\n"
} |
| 17/2 | 2026/08/14 02:04 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 81615 output: 49 thoughts: 35 Thoughts: Here's my summary, as though I'm reading the provided text and thinking through the implications: **Analyzing the bnge_hwrm Send Process** Okay, time to dive into the `bnge_hwrm.c` code. Specifically, I need to understand the interaction between `bnge_hwrm_req_send` and `__hwrm_send_ctx`. I suspect there's something fundamental about how the HWRM (Hardware Resource Manager) requests are constructed and transmitted within this driver. I'm guessing `bnge_hwrm_req_send` is the top-level function that initiates the whole process. I'll need to figure out what data it's taking as input, and what sort of context it requires. Then, I need to see how `__hwrm_send_ctx` fits into the picture. Is it creating a specific structure for the send operation? Is it handling the actual transmission, or is it just setting up the environment for it? Knowing the relationship between these two functions will be key to understanding the overall HWRM request flow and potentially identifying any performance bottlenecks or points of failure. Let's get to it. |
| 18/2 | 2026/08/14 02:04 | tool | read-file |
0mArgs:{
"File": "drivers/net/ethernet/broadcom/bnge/bnge_hwrm.c",
"FirstLine": 280,
"LineCount": 100
}Results: {
"Contents": " 280:\t#define bnge_hwrm_err(bd, ctx, fmt, ...)\t\t\\\n 281:\t\tdo {\t\t\t\t\t\t\t \\\n 282:\t\t\tif ((ctx)-\u003eflags \u0026 BNGE_HWRM_CTX_SILENT)\t \\\n 283:\t\t\t\tdev_dbg((bd)-\u003edev, fmt, __VA_ARGS__); \\\n 284:\t\t\telse\t\t\t\t\t\t \\\n 285:\t\t\t\tdev_err((bd)-\u003edev, fmt, __VA_ARGS__); \\\n 286:\t\t} while (0)\n 287:\t\n 288:\tstatic int __hwrm_send_ctx(struct bnge_dev *bd, struct bnge_hwrm_ctx *ctx)\n 289:\t{\n 290:\t\tu32 doorbell_offset = BNGE_GRCPF_REG_CHIMP_COMM_TRIGGER;\n 291:\t\tenum bnge_hwrm_chnl dst = BNGE_HWRM_CHNL_CHIMP;\n 292:\t\tu32 bar_offset = BNGE_GRCPF_REG_CHIMP_COMM;\n 293:\t\tstruct bnge_hwrm_wait_token *token = NULL;\n 294:\t\tu16 max_req_len = BNGE_HWRM_MAX_REQ_LEN;\n 295:\t\tunsigned int i, timeout, tmo_count;\n 296:\t\tu32 *data = (u32 *)ctx-\u003ereq;\n 297:\t\tu32 msg_len = ctx-\u003ereq_len;\n 298:\t\tint rc = -EBUSY;\n 299:\t\tu32 req_type;\n 300:\t\tu16 len = 0;\n 301:\t\tu8 *valid;\n 302:\t\n 303:\t\tif (ctx-\u003eflags \u0026 BNGE_HWRM_INTERNAL_RESP_DIRTY)\n 304:\t\t\tmemset(ctx-\u003eresp, 0, PAGE_SIZE);\n 305:\t\n 306:\t\treq_type = le16_to_cpu(ctx-\u003ereq-\u003ereq_type);\n 307:\t\n 308:\t\tif (msg_len \u003e BNGE_HWRM_MAX_REQ_LEN \u0026\u0026\n 309:\t\t msg_len \u003e bd-\u003ehwrm_max_ext_req_len) {\n 310:\t\t\tdev_warn(bd-\u003edev, \"oversized hwrm request, req_type 0x%x\",\n 311:\t\t\t\t req_type);\n 312:\t\t\trc = -E2BIG;\n 313:\t\t\tgoto exit;\n 314:\t\t}\n 315:\t\n 316:\t\ttoken = bnge_hwrm_create_token(bd, dst);\n 317:\t\tif (!token) {\n 318:\t\t\trc = -ENOMEM;\n 319:\t\t\tgoto exit;\n 320:\t\t}\n 321:\t\tctx-\u003ereq-\u003eseq_id = cpu_to_le16(token-\u003eseq_id);\n 322:\t\n 323:\t\t/* Ensure any associated DMA buffers are written before doorbell */\n 324:\t\twmb();\n 325:\t\n 326:\t\t/* Write request msg to hwrm channel */\n 327:\t\t__iowrite32_copy(bd-\u003ebar0 + bar_offset, data, msg_len / 4);\n 328:\t\n 329:\t\tfor (i = msg_len; i \u003c max_req_len; i += 4)\n 330:\t\t\twritel(0, bd-\u003ebar0 + bar_offset + i);\n 331:\t\n 332:\t\t/* Ring channel doorbell */\n 333:\t\twritel(1, bd-\u003ebar0 + doorbell_offset);\n 334:\t\n 335:\t\tbnge_hwrm_req_dbg(bd, ctx-\u003ereq);\n 336:\t\n 337:\t\t/* Limit timeout to an upper limit */\n 338:\t\ttimeout = min(ctx-\u003etimeout,\n 339:\t\t\t bd-\u003ehwrm_cmd_max_timeout ?: BNGE_HWRM_CMD_MAX_TIMEOUT);\n 340:\t\t/* convert timeout to usec */\n 341:\t\ttimeout *= 1000;\n 342:\t\n 343:\t\ti = 0;\n 344:\t\t/* Short timeout for the first few iterations:\n 345:\t\t * number of loops = number of loops for short timeout +\n 346:\t\t * number of loops for standard timeout.\n 347:\t\t */\n 348:\t\ttmo_count = BNGE_HWRM_SHORT_TIMEOUT_COUNTER;\n 349:\t\ttimeout = timeout - BNGE_HWRM_SHORT_MIN_TIMEOUT *\n 350:\t\t\t\tBNGE_HWRM_SHORT_TIMEOUT_COUNTER;\n 351:\t\ttmo_count += DIV_ROUND_UP(timeout, BNGE_HWRM_MIN_TIMEOUT);\n 352:\t\n 353:\t\tif (le16_to_cpu(ctx-\u003ereq-\u003ecmpl_ring) != INVALID_HW_RING_ID) {\n 354:\t\t\t/* Wait until hwrm response cmpl interrupt is processed */\n 355:\t\t\twhile (READ_ONCE(token-\u003estate) \u003c BNGE_HWRM_COMPLETE \u0026\u0026\n 356:\t\t\t i++ \u003c tmo_count) {\n 357:\t\t\t\t/* on first few passes, just barely sleep */\n 358:\t\t\t\tif (i \u003c BNGE_HWRM_SHORT_TIMEOUT_COUNTER) {\n 359:\t\t\t\t\tusleep_range(BNGE_HWRM_SHORT_MIN_TIMEOUT,\n 360:\t\t\t\t\t\t BNGE_HWRM_SHORT_MAX_TIMEOUT);\n 361:\t\t\t\t} else {\n 362:\t\t\t\t\tusleep_range(BNGE_HWRM_MIN_TIMEOUT,\n 363:\t\t\t\t\t\t BNGE_HWRM_MAX_TIMEOUT);\n 364:\t\t\t\t}\n 365:\t\t\t}\n 366:\t\n 367:\t\t\tif (READ_ONCE(token-\u003estate) != BNGE_HWRM_COMPLETE) {\n 368:\t\t\t\tbnge_hwrm_err(bd, ctx, \"No hwrm cmpl received: 0x%x\\n\",\n 369:\t\t\t\t\t req_type);\n 370:\t\t\t\tgoto exit;\n 371:\t\t\t}\n 372:\t\t\tlen = le16_to_cpu(READ_ONCE(ctx-\u003eresp-\u003eresp_len));\n 373:\t\t\tvalid = ((u8 *)ctx-\u003eresp) + len - 1;\n 374:\t\t} else {\n 375:\t\t\t__le16 seen_out_of_seq = ctx-\u003ereq-\u003eseq_id; /* will never see */\n 376:\t\t\tint j;\n 377:\t\n 378:\t\t\t/* Check if response len is updated */\n 379:\t\t\tfor (i = 0; i \u003c tmo_count; i++) {\n"
} |
| 19/2 | 2026/08/14 02:04 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 83736 output: 42 thoughts: 363 Thoughts: **Analyzing the `bnge` Driver for Fuzzing Potential** Okay, so I see a write to `bd->bar0 + bar_offset` being done by `bnge`. My first thought is, if this is a real driver, how on earth is this being fuzzed? Syzkaller, of course, can fuzz PCI devices, either through the `syz_pci_setup` method or by emulating the device in QEMU. But wait, is there a pseudo-device framework specific for PCI fuzzing in Syzkaller? No, it's more about injecting MMIO reads/writes directly, or using things like `vhci` for USB or `mac80211_hwsim` for Wi-Fi. Hold on a second, the instructions explicitly say to return `WorthFuzzing=false` if the changes are limited to vendor-specific drivers that need physical hardware, like those for PCIe switches, SmartNICs, or GPUs—things like `mlxsw`, `pds_core`, `qed`, `ionic`, and `amdgpu`. Is `bnge` one of *those*? Yes, it's a Broadcom BCM57708 ThorUltra driver, so probably. That's a bit of a head-scratcher because the commit author is listed as `syz-cluster <triage@syzkaller.com>`. And the patch is definitely in `drivers/net/ethernet/broadcom/bnge/`. This is weird, because `bnge` isn't in the *upstream* kernel. Okay, this means I need to check if `bnge` is a Syzkaller mock driver for fuzzing or if it's a real driver. If it's real, and I suspect it is, it's likely *not* being emulated in QEMU. Time to check `drivers/net/ethernet/broadcom/bnge/Kconfig`. |
| 20/2 | 2026/08/14 02:04 | tool | read-file |
0mError:the file does not exist Args: {
"File": "drivers/net/ethernet/broadcom/bnge/Kconfig",
"FirstLine": 1,
"LineCount": 100
}Results: {
"error": "the file does not exist"
} |
| 21/2 | 2026/08/14 02:04 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 84581 output: 34 thoughts: 29 Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience: **Kconfig Inquiry** Alright, hold on a second. I could have sworn there was a `Kconfig` for Broadcom Ethernet drivers. Am I misremembering, or has something changed in the tree? Let me quickly verify. The usual location would be under `drivers/net/ethernet/broadcom/Kconfig`. Time to confirm my assumptions and make sure my mental model of the build system is still accurate. Gotta double-check before I make a fool of myself! |
| 22/2 | 2026/08/14 02:04 | tool | grepper |
0mError:no matches Args: {
"Expression": "bnge",
"PathPrefix": "drivers/net/ethernet/broadcom/Kconfig"
}Results: {
"error": "no matches"
} |
| 23/2 | 2026/08/14 02:04 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 84777 output: 33 thoughts: 36 Thoughts: **Investigating the Compilation of `bnge`** Okay, so I'm seeing `bnge` pop up, but it's not directly enabled via a Kconfig option. That immediately raises a flag. How is this thing getting compiled in? My gut says I need to dig into the build system directly. Let's see... the most likely culprit would be a Makefile somewhere in the driver structure. Given the driver's name, `bnge`, my first stop has to be the Ethernet Broadcom drivers. I'll head straight for `drivers/net/ethernet/broadcom/Makefile`. That's where I expect to find the conditional logic determining whether this module gets built or not. Time to see what kind of dependency magic is at play here. |
| 24/2 | 2026/08/14 02:05 | tool | grepper |
0mArgs:{
"Expression": "bnge",
"PathPrefix": "drivers/net/ethernet/broadcom/Makefile"
}Results: {
"Output": "drivers/net/ethernet/broadcom/Makefile=20=obj-$(CONFIG_BCMASP) += asp2/\ndrivers/net/ethernet/broadcom/Makefile:21:obj-$(CONFIG_BNGE) += bnge/\n"
} |
| 25/2 | 2026/08/14 02:05 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 85076 output: 24 thoughts: 36 Thoughts: Here's my thought process, as if I were considering this issue: **Investigating a Configuration Discrepancy** Hmm, that's odd. I'm seeing `CONFIG_BNGE` referenced within the Makefile, but I don't recall encountering it defined in the `Kconfig` files. That's a potential red flag right there – configurations should be managed consistently, and if they're in the Makefile, there's a good chance it will break something. It seems like the configuration is being applied outside of the typical configuration management system. So, I need to understand where this variable is coming from and if it has side effects. Okay, time to do a deeper dive. Let's run a full-tree search for `CONFIG_BNGE` to figure out where it's being used and how it's being defined or referenced. Hopefully, I'll find a corresponding `kconfig` that was forgotten. Let's see what turns up. It's probably in the wrong place, but the goal is to see where it *should* be, and fix it. |
| 26/2 | 2026/08/14 02:05 | tool | grepper |
0mArgs:{
"Expression": "CONFIG_BNGE",
"PathPrefix": ""
}Results: {
"Output": "drivers/net/ethernet/broadcom/Makefile=20=obj-$(CONFIG_BCMASP) += asp2/\ndrivers/net/ethernet/broadcom/Makefile:21:obj-$(CONFIG_BNGE) += bnge/\n--\ndrivers/net/ethernet/broadcom/bnge/Makefile-2-\ndrivers/net/ethernet/broadcom/bnge/Makefile:3:obj-$(CONFIG_BNGE) += bng_en.o\ndrivers/net/ethernet/broadcom/bnge/Makefile-4-\n"
} |
| 27/2 | 2026/08/14 02:05 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 85509 output: 37 thoughts: 57 Thoughts: **Debugging a Configuration Issue** Okay, hold on a second. This doesn't seem to be showing up in *any* Kconfig file, which is strange. I could have sworn I'd seen something related. Let's start with the most likely location: `drivers/net/ethernet/broadcom/Kconfig`. Maybe I was too hasty in my initial search and didn't account for a potential case-sensitivity issue when searching for "bnge"? Alright, let's try that search again, but this time I'll be sure to make it case-insensitive to cover all possibilities. I'll need to double check the results with the correct search terms. |
| 28/2 | 2026/08/14 02:05 | tool | grepper |
0mError:bad expression: fatal: -e option, '(?i)bnge': Invalid preceding regular expression Args: {
"Expression": "(?i)bnge",
"PathPrefix": "drivers/net/ethernet/broadcom/Kconfig"
}Results: {
"error": "bad expression: fatal: -e option, '(?i)bnge': Invalid preceding regular expression"
} |
| 29/2 | 2026/08/14 02:05 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 85778 output: 34 thoughts: 56 Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience: **Git Grep Frustrations and Kconfig Exploration** Damn it, I was hoping to quickly leverage `git grep`'s extended regexp capabilities with a case-insensitive match using `(?i)`. Looks like that's not supported – a classic limitation. I suppose I could fall back to `-i` for case-insensitive, but I can't easily pass flags in this instance. That's a minor annoyance. I need a quick way to locate something. Alright, let's get down to brass tacks. I'll need to locate all instances of `BNGE` within the `drivers/net/ethernet/broadcom/Kconfig` file. Time to run that grep command and see what turns up. It's likely related to Broadcom's Gigabit Ethernet support. Hopefully, it is not too noisy. Let's see what we can find. |
| 30/2 | 2026/08/14 02:05 | tool | grepper |
0mArgs:{
"Expression": "BNGE",
"PathPrefix": "drivers/net/ethernet/broadcom/Kconfig"
}Results: {
"Output": "drivers/net/ethernet/broadcom/Kconfig=250=config BNXT_HWMON\n--\ndrivers/net/ethernet/broadcom/Kconfig-257-\ndrivers/net/ethernet/broadcom/Kconfig:258:config BNGE\ndrivers/net/ethernet/broadcom/Kconfig-259-\ttristate \"Broadcom ThorUltra Ethernet device support\"\n"
} |
| 31/2 | 2026/08/14 02:05 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 86181 output: 83 thoughts: 522 Thoughts: **WorthFuzzing Decision for BNGE Driver** Okay, let's break this down. This patch touches `drivers/net/ethernet/broadcom/Kconfig`, so it's about the `BNGE` driver. Right, it's for the Broadcom ThorUltra Ethernet device – a real, physical PCIe card (BCM57708). Given that this is a *real* hardware driver, not something emulated in standard QEMU, the rule applies. Specifically, the rule dictates `WorthFuzzing=false` when the patch *only* modifies code in vendor-specific PCIe switch, SmartNIC, or GPU drivers that need physical hardware not emulated in standard QEMU. First, let me confirm if `bnge` is in that category. Yes, it's a Broadcom Ethernet device, similar to `bnxt`, which I know is a SmartNIC kind of thing. And is it emulated? Nope. QEMU and GCE aren't going to be able to run this. It needs that physical card. Alright, so the patch itself: it's adding `bnge_filter.c`, `bnge_vnic.c`, and modifying `bnge_core.c`, `bnge_ethtool.c`, `bnge_hwrm_lib.c`, `bnge_netdev.c`, and `bnge_resc.c`. All contained within `drivers/net/ethernet/broadcom/bnge/`. This confirms that it's entirely within the scope of that vendor-specific PCIe driver. No other code changes are present. Now, let's verify the rule's wording, just to be absolutely sure: "Return WorthFuzzing=false if the patch only contains: ... - Code in vendor-specific PCIe switch, SmartNIC, or GPU drivers (e.g., mlxsw, pds_core, qed, ionic, amdgpu) that require physical PCIe hardware cards not emulated in standard QEMU." Yep, spot on. `bnge` (Broadcom ThorUltra) aligns perfectly. It's a high-speed Ethernet driver, essentially a SmartNIC. Given that it requires that physical hardware, fuzzing this in a virtual environment is a non-starter. Therefore, the decision is clear: `WorthFuzzing=false`. I'll document the reasoning appropriately. |
| 32/2 | 2026/08/14 02:05 | tool | set-results |
0mArgs:{
"EnableConfigs": [],
"FocusSymbols": [],
"Reasoning": "The patch only modifies the `bnge` driver (Broadcom ThorUltra Ethernet), which is a vendor-specific PCIe driver for physical hardware not emulated in standard QEMU. Therefore, the modified code paths are unreachable in virtualized fuzzing environments.",
"WorthFuzzing": false
}Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"Reasoning": "The patch only modifies the `bnge` driver (Broadcom ThorUltra Ethernet), which is a vendor-specific PCIe driver for physical hardware not emulated in standard QEMU. Therefore, the modified code paths are unreachable in virtualized fuzzing environments.",
"WorthFuzzing": false
} |
| 33/1 | 2026/08/14 02:05 | action | If |
0mArgs:{
"WorthFuzzing": false
}Results: null |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|