| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/08/13 09:52 | flow | patch-triage |
0mArgs:null Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"KMSANReasoning": "",
"NeedsKMSAN": false,
"Reasoning": "The patch modifies the Marvell OcteonTX2 NIC driver, which is a vendor-specific hardware driver that requires physical PCIe hardware not emulated in standard QEMU. Therefore, the modified code paths are unreachable in virtualized fuzzing environments.",
"WorthFuzzing": false
} |
| 1/1 | 2026/08/13 09:52 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit e4c1704bacf8a2fb30692e1e487ef35f54baa9cf\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Thu Aug 13 09:52:00 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c\nindex b81c47ea023be..22f64c1ede91a 100644\n--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c\n+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c\n@@ -331,8 +331,12 @@ static bool is_valid_txschq(struct rvu *rvu, int blkaddr,\n \t\t\treturn true;\n \t}\n \n-\tif (map_func != pcifunc)\n+\tif (map_func != pcifunc) {\n+\t\tdev_err_ratelimited(rvu-\u003edev,\n+\t\t\t\t \"pcifunc %x map pcifunc %x not equal, lvl=%u schq=%u\\n\",\n+\t\t\t\t pcifunc, map_func, lvl, schq);\n \t\treturn false;\n+\t}\n \n \treturn true;\n }\ndiff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c\nindex ca73a94db7943..b6d8ee7e2d4c2 100644\n--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c\n+++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c\n@@ -614,6 +614,142 @@ void otx2_get_mac_from_af(struct net_device *netdev)\n }\n EXPORT_SYMBOL(otx2_get_mac_from_af);\n \n+static int\n+otx2_nix_tmq_reg_write(struct otx2_nic *pfvf, int cnt,\n+\t\t u64 reg_addr[MAX_REGS_PER_MBOX_MSG],\n+\t\t u64 reg_val[MAX_REGS_PER_MBOX_MSG])\n+{\n+\tstruct mbox *mbox = \u0026pfvf-\u003embox;\n+\tstruct nix_txschq_config *req;\n+\tint i, err;\n+\n+\tmutex_lock(\u0026mbox-\u003elock);\n+\treq = otx2_mbox_alloc_msg_nix_txschq_cfg(mbox);\n+\tif (!req) {\n+\t\tmutex_unlock(\u0026mbox-\u003elock);\n+\t\treturn -ENOMEM;\n+\t}\n+\n+\treq-\u003elvl = NIX_TXSCH_LVL_MDQ;\n+\treq-\u003enum_regs = cnt;\n+\n+\tfor (i = 0; i \u003c cnt; i++) {\n+\t\treq-\u003ereg[i] = reg_addr[i];\n+\t\treq-\u003eregval[i] = reg_val[i];\n+\t}\n+\n+\terr = otx2_sync_mbox_msg(mbox);\n+\tmutex_unlock(\u0026mbox-\u003elock);\n+\n+\treturn err;\n+}\n+\n+int otx2_nix_tm_clear_queue_shaper(struct otx2_nic *pfvf)\n+{\n+\tu64 reg_addr[MAX_REGS_PER_MBOX_MSG];\n+\tu64 reg_val[MAX_REGS_PER_MBOX_MSG];\n+\tint err, smq, i, cnt = 0;\n+\n+\tfor (i = 0; i \u003c pfvf-\u003ehw.txschq_cnt[NIX_TXSCH_LVL_SMQ]; i++) {\n+\t\tsmq = pfvf-\u003ehw.txschq_list[NIX_TXSCH_LVL_SMQ][i];\n+\n+\t\treg_addr[cnt] = NIX_AF_MDQX_PIR(smq);\n+\t\treg_val[cnt] = 0;\n+\t\tcnt++;\n+\n+\t\treg_addr[cnt] = NIX_AF_MDQX_CIR(smq);\n+\t\treg_val[cnt] = 0;\n+\t\tcnt++;\n+\n+\t\tif (cnt \u003c MAX_REGS_PER_MBOX_MSG - 1)\n+\t\t\tcontinue;\n+\n+\t\terr = otx2_nix_tmq_reg_write(pfvf, cnt,\n+\t\t\t\t\t reg_addr, reg_val);\n+\t\tif (err)\n+\t\t\tgoto fail;\n+\t\tcnt = 0;\n+\t}\n+\n+\tif (cnt) {\n+\t\terr = otx2_nix_tmq_reg_write(pfvf, cnt,\n+\t\t\t\t\t reg_addr, reg_val);\n+\t\tif (err)\n+\t\t\tgoto fail;\n+\t}\n+\n+\treturn 0;\n+fail:\n+\treturn err;\n+}\n+\n+int otx2_nix_tm_set_queue_shaper(struct otx2_nic *pfvf,\n+\t\t\t\t int txq, u64 minrate, u64 maxrate)\n+{\n+\tstruct mbox *mbox = \u0026pfvf-\u003embox;\n+\tstruct nix_txschq_config *req;\n+\tint err, smq, n = 0;\n+\tu64 reg_addr[2];\n+\tu64 reg_val[2];\n+\tu64 rate;\n+\n+\tif (!maxrate \u0026\u0026 !minrate) {\n+\t\tsmq = otx2_get_smq_idx(pfvf, txq);\n+\t\treg_addr[0] = NIX_AF_MDQX_PIR(smq);\n+\t\treg_val[0] = 0;\n+\t\treg_addr[1] = NIX_AF_MDQX_CIR(smq);\n+\t\treg_val[1] = 0;\n+\t\treturn otx2_nix_tmq_reg_write(pfvf, 2, reg_addr, reg_val);\n+\t}\n+\n+\tsmq = otx2_get_smq_idx(pfvf, txq);\n+\n+\tmutex_lock(\u0026mbox-\u003elock);\n+\treq = otx2_mbox_alloc_msg_nix_txschq_cfg(mbox);\n+\tif (!req) {\n+\t\tmutex_unlock(\u0026mbox-\u003elock);\n+\t\treturn -ENOMEM;\n+\t}\n+\n+\treq-\u003elvl = NIX_TXSCH_LVL_MDQ;\n+\n+\t/* MQPRIO exposes only min/max rate, not burst. Pass burst 0 so\n+\t * otx2_get_egress_burst_cfg() programmes the largest burst the NIX\n+\t * encoding supports (CN10K_MAX_BURST_SIZE on CN10K). This differs\n+\t * from the 65536 byte default used in the HTB path, which is a\n+\t * kernel-side default when no explicit burst is configured, not a\n+\t * hardware cap.\n+\t *\n+\t * mqprio setup restarts the netdev (otx2_mqprio_restart_netdev),\n+\t * which resets MDQ shapers to zero. Program both PIR and CIR on\n+\t * every update so omitted rates are applied explicitly rather than\n+\t * relying on stale hardware state.\n+\t */\n+\treq-\u003ereg[n] = NIX_AF_MDQX_PIR(smq);\n+\tif (maxrate) {\n+\t\trate = otx2_convert_rate(maxrate);\n+\t\treq-\u003eregval[n] = otx2_get_txschq_rate_regval(pfvf, rate, 0);\n+\t} else {\n+\t\treq-\u003eregval[n] = 0;\n+\t}\n+\tn++;\n+\n+\t/* CIR+PIR support is required and checked at mqprio setup. */\n+\treq-\u003ereg[n] = NIX_AF_MDQX_CIR(smq);\n+\tif (minrate) {\n+\t\trate = otx2_convert_rate(minrate);\n+\t\treq-\u003eregval[n] = otx2_get_txschq_rate_regval(pfvf, rate, 0);\n+\t} else {\n+\t\treq-\u003eregval[n] = 0;\n+\t}\n+\tn++;\n+\treq-\u003enum_regs = n;\n+\n+\terr = otx2_sync_mbox_msg(mbox);\n+\tmutex_unlock(\u0026mbox-\u003elock);\n+\treturn err;\n+}\n+\n int otx2_txschq_config(struct otx2_nic *pfvf, int lvl, int prio, bool txschq_for_pfc)\n {\n \tu16 (*schq_list)[MAX_TXSCHQ_PER_FUNC];\n@@ -650,7 +786,20 @@ int otx2_txschq_config(struct otx2_nic *pfvf, int lvl, int prio, bool txschq_for\n \t\t\t\t\t\t(u64)hw-\u003esmq_link_type);\n \t\treq-\u003enum_regs++;\n \t\t/* MDQ config */\n-\t\tparent = schq_list[NIX_TXSCH_LVL_TL4][prio];\n+\t\tif (pfvf-\u003emqprio.rate_limit) {\n+\t\t\t/* TODO: Program NIX_AF_TL4X_TOPOLOGY on TL4[0]. MQPRIO\n+\t\t\t * parents every MDQ under the same TL4 with schedule\n+\t\t\t * priority 0, so sibling arbitration depends on the TL4\n+\t\t\t * topology register. Only the HTB path programs this\n+\t\t\t * today and the AF does not reset it on schq realloc,\n+\t\t\t * so a TL4 reused after HTB teardown can retain stale\n+\t\t\t * RR_PRIO/PRIO_ANCHOR and starve some TX queues.\n+\t\t\t */\n+\t\t\tparent = schq_list[NIX_TXSCH_LVL_TL4][0];\n+\t\t} else {\n+\t\t\tparent = schq_list[NIX_TXSCH_LVL_TL4][prio];\n+\t\t}\n+\n \t\treq-\u003ereg[1] = NIX_AF_MDQX_PARENT(schq);\n \t\treq-\u003eregval[1] = parent \u003c\u003c 16;\n \t\treq-\u003enum_regs++;\n@@ -778,6 +927,9 @@ int otx2_txsch_alloc(struct otx2_nic *pfvf)\n \t\treq-\u003eschq[NIX_TXSCH_LVL_TL4] = chan_cnt;\n \t}\n \n+\tif (pfvf-\u003emqprio.rate_limit)\n+\t\treq-\u003eschq[NIX_TXSCH_LVL_SMQ] = pfvf-\u003ehw.non_qos_queues;\n+\n \trc = otx2_sync_mbox_msg(\u0026pfvf-\u003embox);\n \tif (rc)\n \t\treturn rc;\ndiff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h\nindex eecee612b7b2c..67d4aae9440fa 100644\n--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h\n+++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h\n@@ -483,6 +483,13 @@ struct pf_irq_data {\n \tint mdevs;\n };\n \n+struct otx2_mqprio {\n+\tu32\tflags;\n+\tu64\t*min_rate;\n+\tu64\t*max_rate;\n+\tbool\trate_limit;\n+};\n+\n struct otx2_nic {\n \tvoid __iomem\t\t*reg_base;\n \tstruct net_device\t*netdev;\n@@ -515,6 +522,10 @@ struct otx2_nic {\n \tu64\t\t\tflags;\n \tu64\t\t\t*cq_op_addr;\n \n+\tstruct otx2_mqprio\tmqprio;\n+\tbool\t\t\tmqprio_replace_pending;\n+\tbool\t\t\tmqprio_skip_teardown;\n+\n \tstruct bpf_prog\t\t*xdp_prog;\n \tstruct otx2_qset\tqset;\n \tstruct otx2_hw\t\thw;\n@@ -1246,6 +1257,11 @@ dma_addr_t otx2_dma_map_skb_frag(struct otx2_nic *pfvf,\n \t\t\t\t struct sk_buff *skb, int seg, int *len);\n void otx2_dma_unmap_skb_frags(struct otx2_nic *pfvf, struct sg_list *sg);\n int otx2_read_free_sqe(struct otx2_nic *pfvf, u16 qidx);\n+int otx2_nix_tm_set_queue_shaper(struct otx2_nic *pfvf, int txq,\n+\t\t\t\t u64 minrate, u64 maxrate);\n+int otx2_nix_tm_clear_queue_shaper(struct otx2_nic *pfvf);\n+int otx2_mqprio_down(struct otx2_nic *pfvf);\n+int otx2_mqprio_up(struct otx2_nic *pfvf);\n void otx2_queue_vf_work(struct mbox *mw, struct workqueue_struct *mbox_wq,\n \t\t\tint first, int mdevs, u64 intr);\n int otx2_del_mcam_flow_entry(struct otx2_nic *nic, u16 entry,\ndiff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_dcbnl.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_dcbnl.c\nindex f110dfa423605..4a70abc230bea 100644\n--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_dcbnl.c\n+++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_dcbnl.c\n@@ -413,6 +413,12 @@ static int otx2_dcbnl_ieee_setpfc(struct net_device *dev, struct ieee_pfc *pfc)\n \tu8 old_pfc_en;\n \tint err;\n \n+\tif (pfvf-\u003emqprio.rate_limit \u0026\u0026 pfc-\u003epfc_en) {\n+\t\tnetdev_err(dev,\n+\t\t\t \"PFC: cannot enable while mqprio bandwidth offload is active\\n\");\n+\t\treturn -EOPNOTSUPP;\n+\t}\n+\n \told_pfc_en = pfvf-\u003epfc_en;\n \tpfvf-\u003epfc_en = pfc-\u003epfc_en;\n \ndiff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c\nindex a0340f3422bf9..837d29024981c 100644\n--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c\n+++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c\n@@ -287,6 +287,14 @@ static int otx2_set_channels(struct net_device *dev,\n \t\treturn -EINVAL;\n \t}\n \n+\tif (pfvf-\u003emqprio.rate_limit \u0026\u0026\n+\t (channel-\u003etx_count != pfvf-\u003ehw.tx_queues ||\n+\t channel-\u003erx_count != pfvf-\u003ehw.rx_queues)) {\n+\t\tnetdev_info(dev,\n+\t\t\t \"Not permitted to change channel count while MQ prio is active\\n\");\n+\t\treturn -EINVAL;\n+\t}\n+\n \tif (if_up)\n \t\tdev-\u003enetdev_ops-\u003endo_stop(dev);\n \ndiff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c\nindex c995f29008590..06cb44e7263cf 100644\n--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c\n+++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c\n@@ -1980,6 +1980,16 @@ int otx2_open(struct net_device *netdev)\n \tif (err)\n \t\tgoto err_free_mem;\n \n+\t/* TODO: otx2_mqprio_up() failure here aborts the entire ndo_open()\n+\t * path even though mqprio_rate_limit and the rate cache remain set.\n+\t * Routine ip link set up or driver reset with active mqprio offload\n+\t * can leave the interface stuck down until the qdisc is removed and\n+\t * reapplied.\n+\t */\n+\terr = otx2_mqprio_up(pf);\n+\tif (err)\n+\t\tgoto err_free_hw;\n+\n \t/* Register NAPI handler */\n \tfor (qidx = 0; qidx \u003c pf-\u003ehw.cint_cnt; qidx++) {\n \t\tcq_poll = \u0026qset-\u003enapi[qidx];\n@@ -2140,6 +2150,7 @@ int otx2_open(struct net_device *netdev)\n \tfree_irq(vec, pf);\n err_disable_napi:\n \totx2_disable_napi(pf);\n+err_free_hw:\n \totx2_free_hw_resources(pf);\n err_free_mem:\n \totx2_free_queue_mem(qset);\n@@ -2846,6 +2857,12 @@ static int otx2_xdp_setup(struct otx2_nic *pf, struct bpf_prog *prog)\n \tbool if_up = netif_running(pf-\u003enetdev);\n \tstruct bpf_prog *old_prog;\n \n+\tif (prog \u0026\u0026 pf-\u003emqprio.rate_limit) {\n+\t\tnetdev_err(dev,\n+\t\t\t \"XDP: cannot attach while mqprio bandwidth offload is active\\n\");\n+\t\treturn -EOPNOTSUPP;\n+\t}\n+\n \tif (prog \u0026\u0026 dev-\u003emtu \u003e MAX_XDP_MTU) {\n \t\tnetdev_warn(dev, \"Jumbo frames not yet supported with XDP\\n\");\n \t\treturn -EOPNOTSUPP;\ndiff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c\nindex 039fd47ebf522..fc9677e340658 100644\n--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c\n+++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c\n@@ -16,6 +16,8 @@\n #include \u003cnet/tc_act/tc_mirred.h\u003e\n #include \u003cnet/tc_act/tc_vlan.h\u003e\n #include \u003cnet/ipv6.h\u003e\n+#include \u003cnet/pkt_sched.h\u003e\n+#include \u003cnet/sch_generic.h\u003e\n \n #include \"cn10k.h\"\n #include \"otx2_common.h\"\n@@ -31,6 +33,10 @@\n \n #define MCAST_INVALID_GRP\t\t(-1U)\n #define RATE_MANTISSA_BITS\t\t8\n+/* Min per-queue egress shaping rate the NIX TLX encoder supports (2 Mbps). */\n+#define OTX2_MQPRIO_MIN_RATE_BYTES_PS\t250000ULL\n+/* Max egress shaping rate the NIX TLX encoder supports (130816 Mbps). */\n+#define OTX2_MQPRIO_MAX_RATE_BYTES_PS\t((MAX_BURST_SIZE * 1000000ULL) / 8ULL)\n \n static void otx2_get_egress_burst_cfg(struct otx2_nic *nic, u32 burst,\n \t\t\t\t u32 *burst_exp, u32 *burst_mantissa)\n@@ -61,6 +67,9 @@ static void otx2_get_egress_burst_cfg(struct otx2_nic *nic, u32 burst,\n \t\t\t*burst_mantissa = tmp / (1ULL \u003c\u003c (*burst_exp - 7));\n \t\t}\n \t} else {\n+\t\t/* burst 0: largest encodable burst (CN10K_MAX_BURST_SIZE on\n+\t\t * CN10K), not a minimal burst.\n+\t\t */\n \t\t*burst_exp = MAX_BURST_EXPONENT;\n \t\t*burst_mantissa = max_mantissa;\n \t}\n@@ -1600,14 +1609,596 @@ static int otx2_setup_tc_block(struct net_device *netdev,\n \t\t\t\t\t nic, nic, ingress);\n }\n \n+/* Free the per-queue min/max rate caches. */\n+static void otx2_mqprio_free_cache(struct otx2_nic *pfvf)\n+{\n+\tdevm_kfree(pfvf-\u003edev, pfvf-\u003emqprio.min_rate);\n+\tdevm_kfree(pfvf-\u003edev, pfvf-\u003emqprio.max_rate);\n+\tpfvf-\u003emqprio.min_rate = NULL;\n+\tpfvf-\u003emqprio.max_rate = NULL;\n+\tpfvf-\u003emqprio.flags = 0;\n+}\n+\n+static int otx2_mqprio_alloc_cache(struct otx2_nic *pfvf)\n+{\n+\tu16 num_txq = pfvf-\u003ehw.non_qos_queues;\n+\n+\t/* TODO: otx2_mqprio_free_cache() here drops the committed rate cache\n+\t * on tc qdisc replace before the new mapping is complete. Stage the\n+\t * incoming rates separately and commit only after replace succeeds so\n+\t * a failure after netdev restart can roll back from the prior cache.\n+\t */\n+\totx2_mqprio_free_cache(pfvf);\n+\n+\tpfvf-\u003emqprio.min_rate = devm_kcalloc(pfvf-\u003edev, num_txq,\n+\t\t\t\t\t sizeof(*pfvf-\u003emqprio.min_rate),\n+\t\t\t\t\t GFP_KERNEL);\n+\tpfvf-\u003emqprio.max_rate = devm_kcalloc(pfvf-\u003edev, num_txq,\n+\t\t\t\t\t sizeof(*pfvf-\u003emqprio.max_rate),\n+\t\t\t\t\t GFP_KERNEL);\n+\tif (!pfvf-\u003emqprio.min_rate || !pfvf-\u003emqprio.max_rate) {\n+\t\totx2_mqprio_free_cache(pfvf);\n+\t\treturn -ENOMEM;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static bool otx2_txschq_is_allocated(struct otx2_nic *pfvf)\n+{\n+\tint lvl, idx;\n+\n+\tfor (lvl = 0; lvl \u003c NIX_TXSCH_LVL_CNT; lvl++) {\n+\t\tfor (idx = 0; idx \u003c pfvf-\u003ehw.txschq_cnt[lvl]; idx++) {\n+\t\t\tif (pfvf-\u003ehw.txschq_list[lvl][idx])\n+\t\t\t\treturn true;\n+\t\t}\n+\t}\n+\n+\treturn false;\n+}\n+\n+/* Tear down mqprio bandwidth offload: clear per-queue shapers,\n+ * mqprio_rate_limit, netdev TC mappings, and the cached rates. Called on\n+ * explicit mqprio teardown (tc qdisc del) and error cleanup, not on\n+ * routine netdev stop/open cycles where the offload stays active.\n+ */\n+int otx2_mqprio_down(struct otx2_nic *pfvf)\n+{\n+\tstruct net_device *netdev = pfvf-\u003enetdev;\n+\tint err = 0;\n+\n+\tif (!pfvf-\u003emqprio.rate_limit)\n+\t\treturn 0;\n+\n+\tif (otx2_txschq_is_allocated(pfvf))\n+\t\terr = otx2_nix_tm_clear_queue_shaper(pfvf);\n+\n+\t/* TODO: mqprio_down() clears mqprio_rate_limit, netdev TC mappings,\n+\t * and the rate cache even when otx2_nix_tm_clear_queue_shaper() fails\n+\t * partway through the MDQ batch. Software then reports offload as\n+\t * inactive while some queues may retain programmed CIR/PIR, so a later\n+\t * mqprio_up() or re-setup can shape with the wrong effective rates.\n+\t */\n+\tpfvf-\u003emqprio.rate_limit = false;\n+\tpfvf-\u003emqprio_replace_pending = false;\n+\tpfvf-\u003emqprio_skip_teardown = false;\n+\tnetdev_set_num_tc(netdev, 0);\n+\totx2_mqprio_free_cache(pfvf);\n+\n+\treturn err;\n+}\n+\n+int otx2_mqprio_up(struct otx2_nic *pfvf)\n+{\n+\tstruct net_device *netdev = pfvf-\u003enetdev;\n+\tint txq, err;\n+\n+\tif (!pfvf-\u003emqprio.rate_limit)\n+\t\treturn 0;\n+\n+\tif (!pfvf-\u003emqprio.min_rate || !pfvf-\u003emqprio.max_rate)\n+\t\treturn 0;\n+\n+\tfor (txq = 0; txq \u003c pfvf-\u003ehw.non_qos_queues; txq++) {\n+\t\tu64 min_rate = 0, max_rate = 0;\n+\n+\t\tif (pfvf-\u003emqprio.flags \u0026 TC_MQPRIO_F_MIN_RATE)\n+\t\t\tmin_rate = pfvf-\u003emqprio.min_rate[txq];\n+\t\tif (pfvf-\u003emqprio.flags \u0026 TC_MQPRIO_F_MAX_RATE)\n+\t\t\tmax_rate = pfvf-\u003emqprio.max_rate[txq];\n+\n+\t\tif (!min_rate \u0026\u0026 !max_rate)\n+\t\t\tcontinue;\n+\n+\t\terr = otx2_nix_tm_set_queue_shaper(pfvf, txq, min_rate,\n+\t\t\t\t\t\t max_rate);\n+\t\tif (err) {\n+\t\t\tnetdev_err(netdev,\n+\t\t\t\t \"mqprio: failed to restore shaper for txq %d: %d\\n\",\n+\t\t\t\t txq, err);\n+\t\t\treturn err;\n+\t\t}\n+\t}\n+\n+\treturn 0;\n+}\n+\n+/* Restart the netdev to reprogram the TX scheduler hierarchy for mqprio\n+ * bandwidth offload. Both mqprio add and delete (when offload was active)\n+ * take this path via ndo_stop()/ndo_open() so VF-specific open logic (e.g.\n+ * LBK carrier on) runs correctly. The full stop/open cycle clears\n+ * carrier, stops all TX queues, tears down IRQs/NAPI and drops in-flight\n+ * traffic. If open fails, the interface is left administratively down\n+ * without calling ndo_stop() again on resources already torn down by\n+ * the open error path.\n+ */\n+static int otx2_mqprio_restart_netdev(struct net_device *netdev, bool rate_limit)\n+{\n+\tstruct otx2_nic *pfvf = netdev_priv(netdev);\n+\tconst struct net_device_ops *ops = netdev-\u003enetdev_ops;\n+\tbool up = netdev-\u003eflags \u0026 IFF_UP;\n+\tint err;\n+\n+\t/* TODO: Every mqprio add, replace, and delete runs a full ndo_stop()/\n+\t * ndo_open() cycle (carrier drop, IRQ/NAPI teardown, in-flight traffic\n+\t * loss) to reprogram the TX scheduler. If ndo_open() fails the\n+\t * interface is left administratively down (OTX2_FLAG_INTF_DOWN) until\n+\t * manual recovery. Explore live reprogramming without bouncing the\n+\t * netdev.\n+\t */\n+\tnetdev_info(netdev,\n+\t\t \"mqprio: restarting interface to reprogram TX scheduler; in-flight traffic will be dropped\\n\");\n+\n+\tif (up)\n+\t\tdev_deactivate(netdev, true);\n+\n+\terr = ops-\u003endo_stop(netdev);\n+\tif (err)\n+\t\tgoto out_activate;\n+\n+\t/* Set before ndo_open() so otx2_txsch_alloc() widens SMQ allocation. */\n+\tif (rate_limit)\n+\t\tpfvf-\u003emqprio.rate_limit = true;\n+\n+\terr = ops-\u003endo_open(netdev);\n+\tif (err) {\n+\t\tint down_err;\n+\n+\t\tnetdev_err(netdev,\n+\t\t\t \"Failed to restart device after mqprio change: %d\\n\",\n+\t\t\t err);\n+\t\tdown_err = otx2_mqprio_down(pfvf);\n+\t\tif (down_err)\n+\t\t\tnetdev_err(netdev,\n+\t\t\t\t \"mqprio: failed to clear shapers after restart error: %d\\n\",\n+\t\t\t\t down_err);\n+\t\t/* ndo_open() rolls back on failure; mark the interface down so\n+\t\t * netif_close() does not invoke ndo_stop() on freed NAPI/queue\n+\t\t * state. Caller holds RTNL; dev_close() would deadlock.\n+\t\t */\n+\t\tpfvf-\u003eflags |= OTX2_FLAG_INTF_DOWN;\n+\t\t/* visible to otx2_stop() on other cpus */\n+\t\tsmp_wmb();\n+\t\tnetif_close(netdev);\n+\t\treturn err;\n+\t}\n+\n+out_activate:\n+\tif (up \u0026\u0026 !err)\n+\t\tdev_activate(netdev);\n+\n+\treturn err;\n+}\n+\n+static u64 otx2_mqprio_per_queue_rate(u64 rate, u32 qcount)\n+{\n+\tif (!rate || qcount \u003c= 1)\n+\t\treturn rate;\n+\n+\treturn div_u64(rate, qcount);\n+}\n+\n+static int otx2_mqprio_validate_tc_rate(struct net_device *netdev,\n+\t\t\t\t\tstruct netlink_ext_ack *extack,\n+\t\t\t\t\tu64 rate, u32 qcount, int tc,\n+\t\t\t\t\tconst char *name)\n+{\n+\tif (!rate || qcount \u003c= 1 || rate \u003e= qcount)\n+\t\treturn 0;\n+\n+\tnetdev_err(netdev,\n+\t\t \"mqprio: %s rate %llu for tc %d too small for %u queues\\n\",\n+\t\t name, rate, tc, qcount);\n+\tNL_SET_ERR_MSG_FMT_MOD(extack,\n+\t\t\t \"mqprio: %s rate %llu for tc %d too small for %u queues\",\n+\t\t\t name, rate, tc, qcount);\n+\treturn -EINVAL;\n+}\n+\n+static int otx2_mqprio_validate_txqs(struct net_device *netdev,\n+\t\t\t\t struct netlink_ext_ack *extack,\n+\t\t\t\t struct tc_mqprio_qopt *qopt)\n+{\n+\tstruct otx2_nic *pfvf = netdev_priv(netdev);\n+\tu16 num_txq = pfvf-\u003ehw.non_qos_queues;\n+\tint tc, txq;\n+\n+\tif (qopt-\u003enum_tc \u003e num_txq) {\n+\t\tnetdev_err(netdev, \"Number of TCs (%u) exceeds hw queues %u\\n\",\n+\t\t\t qopt-\u003enum_tc, num_txq);\n+\t\tNL_SET_ERR_MSG_FMT_MOD(extack,\n+\t\t\t\t \"Number of TCs (%u) exceeds hw queues %u\",\n+\t\t\t\t qopt-\u003enum_tc, num_txq);\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tif (num_txq \u003e MAX_TXSCHQ_PER_FUNC) {\n+\t\tnetdev_err(netdev,\n+\t\t\t \"Number of queues (%u) exceeds max scheduler queues %u\\n\",\n+\t\t\t num_txq, MAX_TXSCHQ_PER_FUNC);\n+\t\tNL_SET_ERR_MSG_FMT_MOD(extack,\n+\t\t\t\t \"Number of queues (%u) exceeds max scheduler queues %u\",\n+\t\t\t\t num_txq, MAX_TXSCHQ_PER_FUNC);\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tfor (tc = 0; tc \u003c qopt-\u003enum_tc; tc++) {\n+\t\tu32 qcount = qopt-\u003ecount[tc];\n+\n+\t\tfor (txq = qopt-\u003eoffset[tc];\n+\t\t txq \u003c qopt-\u003eoffset[tc] + qcount; txq++) {\n+\t\t\tif (txq \u003e= num_txq) {\n+\t\t\t\tnetdev_err(netdev,\n+\t\t\t\t\t \"mqprio: txq %d exceeds offload queue count %u\\n\",\n+\t\t\t\t\t txq, num_txq);\n+\t\t\t\tNL_SET_ERR_MSG_FMT_MOD(extack,\n+\t\t\t\t\t\t \"mqprio: txq %d exceeds offload queue count %u\",\n+\t\t\t\t\t\t txq, num_txq);\n+\t\t\t\treturn -EINVAL;\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static bool otx2_mqprio_rate_valid(u64 rate_bytes_ps)\n+{\n+\tu64 mbps;\n+\n+\tif (!rate_bytes_ps)\n+\t\treturn true;\n+\n+\tif (rate_bytes_ps \u003c OTX2_MQPRIO_MIN_RATE_BYTES_PS)\n+\t\treturn false;\n+\n+\tif (rate_bytes_ps \u003e OTX2_MQPRIO_MAX_RATE_BYTES_PS)\n+\t\treturn false;\n+\n+\tif (rate_bytes_ps \u003e div_u64(U64_MAX, 8))\n+\t\treturn false;\n+\n+\tmbps = otx2_convert_rate(rate_bytes_ps);\n+\treturn ilog2(mbps / 2) \u003c= MAX_RATE_EXPONENT;\n+}\n+\n+static void otx2_mqprio_replace_failed_msg(struct net_device *netdev,\n+\t\t\t\t\t struct netlink_ext_ack *extack)\n+{\n+\t/* TODO: On failed tc qdisc replace restore the prior rate cache,\n+\t * netdev TC mapping, and hardware shapers from a snapshot taken before\n+\t * reprogramming instead of leaving the new offload values active while\n+\t * the previous mqprio remains the grafted root qdisc.\n+\t */\n+\tnetdev_err(netdev,\n+\t\t \"mqprio: replace failed; new offload values active, old mqprio still root\\n\");\n+\tif (extack)\n+\t\tNL_SET_ERR_MSG_MOD(extack,\n+\t\t\t\t \"mqprio: replace failed; new offload values active, old mqprio still root\");\n+}\n+\n+static int otx2_teardown_tc_mqprio(struct otx2_nic *pfvf,\n+\t\t\t\t struct tc_mqprio_qopt_offload *mqprio)\n+{\n+\tbool had_mqprio = pfvf-\u003emqprio.rate_limit;\n+\tstruct tc_mqprio_qopt *qopt = \u0026mqprio-\u003eqopt;\n+\tstruct net_device *netdev = pfvf-\u003enetdev;\n+\tbool if_up = netif_running(netdev);\n+\tint down_err, err;\n+\n+\tqopt-\u003ehw = 0;\n+\n+\t/* tc qdisc replace grafts the new mqprio before destroying the old\n+\t * one. mqprio_skip_teardown is armed from TC_ROOT_GRAFT so teardown\n+\t * from the replaced qdisc is ignored after the new configuration has\n+\t * reprogrammed hardware and set mqprio_rate_limit.\n+\t */\n+\tif (pfvf-\u003emqprio_skip_teardown) {\n+\t\tpfvf-\u003emqprio_skip_teardown = false;\n+\t\tpfvf-\u003emqprio_replace_pending = false;\n+\t\treturn 0;\n+\t}\n+\n+\tif (pfvf-\u003emqprio_replace_pending) {\n+\t\totx2_mqprio_replace_failed_msg(netdev, NULL);\n+\t\tpfvf-\u003emqprio_replace_pending = false;\n+\t\treturn -EIO;\n+\t}\n+\n+\t/* Skip the netdev restart when mqprio offload was not active. */\n+\tif (!had_mqprio)\n+\t\treturn 0;\n+\n+\tdown_err = otx2_mqprio_down(pfvf);\n+\n+\tif (if_up) {\n+\t\terr = otx2_mqprio_restart_netdev(netdev, false);\n+\t\tif (err)\n+\t\t\treturn err;\n+\t}\n+\n+\treturn down_err;\n+}\n+\n+static int otx2_setup_tc_mqprio(struct net_device *netdev,\n+\t\t\t\tstruct tc_mqprio_qopt_offload *mqprio)\n+{\n+\tstruct otx2_nic *pfvf = netdev_priv(netdev);\n+\tstruct tc_mqprio_qopt *qopt = \u0026mqprio-\u003eqopt;\n+\tstruct netlink_ext_ack *extack = mqprio-\u003eextack;\n+\tbool replacing = pfvf-\u003emqprio.rate_limit;\n+\tbool if_up = netif_running(netdev);\n+\tint tc, txq, err, i;\n+\n+\tif (!qopt-\u003ehw)\n+\t\treturn otx2_teardown_tc_mqprio(pfvf, mqprio);\n+\n+\tif (!if_up) {\n+\t\tnetdev_err(netdev, \"mqprio: setup requires interface UP\\n\");\n+\t\tNL_SET_ERR_MSG_MOD(extack, \"mqprio: setup requires interface UP\");\n+\t\treturn -EOPNOTSUPP;\n+\t}\n+\n+\tif (mqprio-\u003eshaper != TC_MQPRIO_SHAPER_BW_RATE) {\n+\t\tnetdev_err(netdev, \"Unsupported mqprio shaper %#x\\n\", mqprio-\u003eshaper);\n+\t\tNL_SET_ERR_MSG_FMT_MOD(extack, \"Unsupported mqprio shaper %#x\",\n+\t\t\t\t mqprio-\u003eshaper);\n+\t\treturn -EOPNOTSUPP;\n+\t}\n+\n+\tif (!test_bit(QOS_CIR_PIR_SUPPORT, \u0026pfvf-\u003ehw.cap_flag)) {\n+\t\tnetdev_err(netdev,\n+\t\t\t \"mqprio: bandwidth offload requires CIR+PIR support\\n\");\n+\t\tNL_SET_ERR_MSG_MOD(extack,\n+\t\t\t\t \"mqprio: bandwidth offload requires CIR+PIR support\");\n+\t\treturn -EOPNOTSUPP;\n+\t}\n+\n+\tif (is_otx2_sdp_rep(pfvf-\u003epdev)) {\n+\t\tnetdev_err(netdev, \"mqprio: bandwidth offload not supported on SDP rep\\n\");\n+\t\tNL_SET_ERR_MSG_MOD(extack,\n+\t\t\t\t \"mqprio: bandwidth offload not supported on SDP rep\");\n+\t\treturn -EOPNOTSUPP;\n+\t}\n+\n+\tif (otx2_qos_htb_active(pfvf)) {\n+\t\tnetdev_err(netdev, \"mqprio: cannot enable offload while HTB is active\\n\");\n+\t\tNL_SET_ERR_MSG_MOD(extack,\n+\t\t\t\t \"mqprio: cannot enable offload while HTB is active\");\n+\t\treturn -EOPNOTSUPP;\n+\t}\n+\n+\tif (pfvf-\u003epfc_en) {\n+\t\tnetdev_err(netdev,\n+\t\t\t \"mqprio: cannot enable offload while PFC is enabled\\n\");\n+\t\tNL_SET_ERR_MSG_MOD(extack,\n+\t\t\t\t \"mqprio: cannot enable offload while PFC is enabled\");\n+\t\treturn -EOPNOTSUPP;\n+\t}\n+\n+\tif (pfvf-\u003exdp_prog) {\n+\t\tnetdev_err(netdev,\n+\t\t\t \"mqprio: cannot enable offload while XDP is active\\n\");\n+\t\tNL_SET_ERR_MSG_MOD(extack,\n+\t\t\t\t \"mqprio: cannot enable offload while XDP is active\");\n+\t\treturn -EOPNOTSUPP;\n+\t}\n+\n+\tfor (tc = 0; tc \u003c qopt-\u003enum_tc; tc++) {\n+\t\tu64 min_rate = 0, max_rate = 0;\n+\t\tu32 qcount = qopt-\u003ecount[tc];\n+\n+\t\tif (mqprio-\u003eflags \u0026 TC_MQPRIO_F_MIN_RATE)\n+\t\t\tmin_rate = mqprio-\u003emin_rate[tc];\n+\t\tif (mqprio-\u003eflags \u0026 TC_MQPRIO_F_MAX_RATE)\n+\t\t\tmax_rate = mqprio-\u003emax_rate[tc];\n+\n+\t\tif (min_rate \u0026\u0026 max_rate \u0026\u0026 min_rate \u003e max_rate) {\n+\t\t\tnetdev_err(netdev,\n+\t\t\t\t \"min_rate %llu exceeds max_rate %llu for tc %d\\n\",\n+\t\t\t\t min_rate, max_rate, tc);\n+\t\t\tNL_SET_ERR_MSG_FMT_MOD(extack,\n+\t\t\t\t\t \"min_rate %llu exceeds max_rate %llu for tc %d\",\n+\t\t\t\t\t min_rate, max_rate, tc);\n+\t\t\treturn -EINVAL;\n+\t\t}\n+\n+\t\tif (mqprio-\u003eflags \u0026 TC_MQPRIO_F_MIN_RATE) {\n+\t\t\terr = otx2_mqprio_validate_tc_rate(netdev, extack, min_rate,\n+\t\t\t\t\t\t\t qcount, tc, \"min\");\n+\t\t\tif (err)\n+\t\t\t\treturn err;\n+\t\t}\n+\n+\t\tif (mqprio-\u003eflags \u0026 TC_MQPRIO_F_MAX_RATE) {\n+\t\t\terr = otx2_mqprio_validate_tc_rate(netdev, extack, max_rate,\n+\t\t\t\t\t\t\t qcount, tc, \"max\");\n+\t\t\tif (err)\n+\t\t\t\treturn err;\n+\t\t}\n+\n+\t\tif (mqprio-\u003eflags \u0026 TC_MQPRIO_F_MIN_RATE \u0026\u0026\n+\t\t !otx2_mqprio_rate_valid(otx2_mqprio_per_queue_rate(min_rate,\n+\t\t\t\t\t\t\t\t qcount))) {\n+\t\t\tnetdev_err(netdev,\n+\t\t\t\t \"mqprio: min_rate %llu for tc %d is outside hardware limits\\n\",\n+\t\t\t\t min_rate, tc);\n+\t\t\tNL_SET_ERR_MSG_FMT_MOD(extack,\n+\t\t\t\t\t \"mqprio: min_rate %llu for tc %d is outside hardware limits\",\n+\t\t\t\t\t min_rate, tc);\n+\t\t\treturn -EINVAL;\n+\t\t}\n+\n+\t\tif (mqprio-\u003eflags \u0026 TC_MQPRIO_F_MAX_RATE \u0026\u0026\n+\t\t !otx2_mqprio_rate_valid(otx2_mqprio_per_queue_rate(max_rate,\n+\t\t\t\t\t\t\t\t qcount))) {\n+\t\t\tnetdev_err(netdev,\n+\t\t\t\t \"mqprio: max_rate %llu for tc %d is outside hardware limits\\n\",\n+\t\t\t\t max_rate, tc);\n+\t\t\tNL_SET_ERR_MSG_FMT_MOD(extack,\n+\t\t\t\t\t \"mqprio: max_rate %llu for tc %d is outside hardware limits\",\n+\t\t\t\t\t max_rate, tc);\n+\t\t\treturn -EINVAL;\n+\t\t}\n+\t}\n+\n+\terr = otx2_mqprio_validate_txqs(netdev, extack, qopt);\n+\tif (err)\n+\t\treturn err;\n+\n+\terr = otx2_mqprio_restart_netdev(pfvf-\u003enetdev, true);\n+\tif (err)\n+\t\treturn err;\n+\n+\t/* TODO: Failures from here through the end of init() (e.g. -ENOMEM\n+\t * from otx2_mqprio_alloc_cache(), mbox errors from\n+\t * otx2_nix_tm_clear_queue_shaper() or otx2_nix_tm_set_queue_shaper())\n+\t * reach cleanup. On tc qdisc replace the old mqprio remains grafted,\n+\t * so cleanup returns without rolling back netdev TC mapping, the rate\n+\t * cache, or partially reprogrammed hardware shapers. Restore the prior\n+\t * configuration instead of calling otx2_mqprio_down() and bouncing the\n+\t * interface a second time.\n+\t */\n+\terr = otx2_mqprio_alloc_cache(pfvf);\n+\tif (err)\n+\t\tgoto cleanup;\n+\n+\t/* otx2_mqprio_up() may have restored the previous configuration during\n+\t * the restart above. Clear every MDQ shaper before applying the new\n+\t * mapping so queues dropped from the TC layout do not keep stale\n+\t * limits in hardware.\n+\t */\n+\tif (otx2_txschq_is_allocated(pfvf)) {\n+\t\terr = otx2_nix_tm_clear_queue_shaper(pfvf);\n+\t\tif (err)\n+\t\t\tgoto cleanup;\n+\t}\n+\n+\tpfvf-\u003emqprio.flags = mqprio-\u003eflags;\n+\n+\tfor (tc = 0; tc \u003c qopt-\u003enum_tc; tc++) {\n+\t\tu64 min_rate = 0, max_rate = 0;\n+\t\tu32 qcount = qopt-\u003ecount[tc];\n+\n+\t\t/* Rates omitted from tc mqprio are passed as zero and both MDQ\n+\t\t * shaper registers are programmed; see\n+\t\t * otx2_nix_tm_set_queue_shaper().\n+\t\t */\n+\t\tif (mqprio-\u003eflags \u0026 TC_MQPRIO_F_MIN_RATE)\n+\t\t\tmin_rate = otx2_mqprio_per_queue_rate(mqprio-\u003emin_rate[tc],\n+\t\t\t\t\t\t\t qcount);\n+\t\tif (mqprio-\u003eflags \u0026 TC_MQPRIO_F_MAX_RATE)\n+\t\t\tmax_rate = otx2_mqprio_per_queue_rate(mqprio-\u003emax_rate[tc],\n+\t\t\t\t\t\t\t qcount);\n+\n+\t\tfor (txq = qopt-\u003eoffset[tc];\n+\t\t txq \u003c qopt-\u003eoffset[tc] + qcount; txq++) {\n+\t\t\tnetdev_dbg(netdev,\n+\t\t\t\t \"mqprio: tc %d txq %d min_rate %llu max_rate %llu\\n\",\n+\t\t\t\t tc, txq, min_rate, max_rate);\n+\n+\t\t\tpfvf-\u003emqprio.min_rate[txq] = min_rate;\n+\t\t\tpfvf-\u003emqprio.max_rate[txq] = max_rate;\n+\n+\t\t\terr = otx2_nix_tm_set_queue_shaper(pfvf, txq,\n+\t\t\t\t\t\t\t min_rate, max_rate);\n+\t\t\tif (err)\n+\t\t\t\tgoto cleanup;\n+\t\t}\n+\t}\n+\n+\tnetdev_set_num_tc(netdev, qopt-\u003enum_tc);\n+\tfor (i = 0; i \u003c qopt-\u003enum_tc; i++)\n+\t\tnetdev_set_tc_queue(netdev, i, qopt-\u003ecount[i], qopt-\u003eoffset[i]);\n+\n+\tqopt-\u003ehw = TC_MQPRIO_HW_OFFLOAD_TCS;\n+\n+\tif (replacing)\n+\t\tpfvf-\u003emqprio_replace_pending = true;\n+\n+\treturn 0;\n+\n+cleanup:\n+\t/* Clear hardware offload on this rejected request so a failed replace\n+\t * does not leave tc reporting offload for driver state that was torn\n+\t * down while the previous mqprio remains grafted.\n+\t */\n+\tqopt-\u003ehw = 0;\n+\tif (replacing) {\n+\t\totx2_mqprio_replace_failed_msg(netdev, extack);\n+\t\tpfvf-\u003emqprio_replace_pending = false;\n+\t\treturn err ? err : -EIO;\n+\t}\n+\totx2_teardown_tc_mqprio(pfvf, mqprio);\n+\treturn err;\n+}\n+\n+static int otx2_setup_tc_root(struct otx2_nic *pfvf,\n+\t\t\t struct tc_root_qopt_offload *root)\n+{\n+\tswitch (root-\u003ecommand) {\n+\tcase TC_ROOT_GRAFT:\n+\t\tif (root-\u003eingress)\n+\t\t\treturn 0;\n+\t\tif (pfvf-\u003emqprio_replace_pending) {\n+\t\t\tpfvf-\u003emqprio_skip_teardown = true;\n+\t\t\tpfvf-\u003emqprio_replace_pending = false;\n+\t\t}\n+\t\treturn 0;\n+\tdefault:\n+\t\treturn -EOPNOTSUPP;\n+\t}\n+}\n+\n+static int otx2_setup_tc_query_caps(void *type_data)\n+{\n+\tstruct tc_query_caps_base *base = type_data;\n+\tstruct tc_mqprio_caps *caps;\n+\n+\tif (base-\u003etype != TC_SETUP_QDISC_MQPRIO)\n+\t\treturn -EOPNOTSUPP;\n+\n+\tcaps = base-\u003ecaps;\n+\tcaps-\u003evalidate_queue_counts = true;\n+\n+\treturn 0;\n+}\n+\n int otx2_setup_tc(struct net_device *netdev, enum tc_setup_type type,\n \t\t void *type_data)\n {\n \tswitch (type) {\n+\tcase TC_QUERY_CAPS:\n+\t\treturn otx2_setup_tc_query_caps(type_data);\n \tcase TC_SETUP_BLOCK:\n \t\treturn otx2_setup_tc_block(netdev, type_data);\n \tcase TC_SETUP_QDISC_HTB:\n \t\treturn otx2_setup_tc_htb(netdev, type_data);\n+\tcase TC_SETUP_QDISC_MQPRIO:\n+\t\treturn otx2_setup_tc_mqprio(netdev, type_data);\n+\tcase TC_SETUP_ROOT_QDISC:\n+\t\treturn otx2_setup_tc_root(netdev_priv(netdev), type_data);\n \tdefault:\n \t\treturn -EOPNOTSUPP;\n \t}\ndiff --git a/drivers/net/ethernet/marvell/octeontx2/nic/qos.c b/drivers/net/ethernet/marvell/octeontx2/nic/qos.c\nindex 69c0911e28e91..cf00217883563 100644\n--- a/drivers/net/ethernet/marvell/octeontx2/nic/qos.c\n+++ b/drivers/net/ethernet/marvell/octeontx2/nic/qos.c\n@@ -118,6 +118,9 @@ static void otx2_config_sched_shaping(struct otx2_nic *pfvf,\n \t/* configure PIR */\n \tmaxrate = (node-\u003erate \u003e node-\u003eceil) ? node-\u003erate : node-\u003eceil;\n \n+\t/* 65536 is the kernel-side default burst when HTB does not supply an\n+\t * explicit value, not the NIX hardware maximum (CN10K_MAX_BURST_SIZE).\n+\t */\n \tcfg-\u003eregval[*num_regs] =\n \t\totx2_get_txschq_rate_regval(pfvf, maxrate, 65536);\n \t(*num_regs)++;\n@@ -1729,11 +1732,23 @@ void otx2_qos_config_txschq(struct otx2_nic *pfvf)\n \totx2_qos_root_destroy(pfvf);\n }\n \n+bool otx2_qos_htb_active(struct otx2_nic *pfvf)\n+{\n+\treturn otx2_sw_node_find(pfvf, OTX2_QOS_ROOT_CLASSID);\n+}\n+\n int otx2_setup_tc_htb(struct net_device *ndev, struct tc_htb_qopt_offload *htb)\n {\n \tstruct otx2_nic *pfvf = netdev_priv(ndev);\n \tint res;\n \n+\tif (pfvf-\u003emqprio.rate_limit \u0026\u0026\n+\t htb-\u003ecommand != TC_HTB_DESTROY) {\n+\t\tNL_SET_ERR_MSG_MOD(htb-\u003eextack,\n+\t\t\t\t \"HTB offload cannot be used with mqprio bandwidth offload active\");\n+\t\treturn -EOPNOTSUPP;\n+\t}\n+\n \tswitch (htb-\u003ecommand) {\n \tcase TC_HTB_CREATE:\n \t\treturn otx2_qos_root_add(pfvf, htb-\u003eparent_classid,\ndiff --git a/drivers/net/ethernet/marvell/octeontx2/nic/qos.h b/drivers/net/ethernet/marvell/octeontx2/nic/qos.h\nindex 221bd0438f603..c777cc42eac9c 100644\n--- a/drivers/net/ethernet/marvell/octeontx2/nic/qos.h\n+++ b/drivers/net/ethernet/marvell/octeontx2/nic/qos.h\n@@ -23,6 +23,7 @@ enum qos_smq_operations {\n u64 otx2_get_txschq_rate_regval(struct otx2_nic *nic, u64 maxrate, u32 burst);\n \n int otx2_setup_tc_htb(struct net_device *ndev, struct tc_htb_qopt_offload *htb);\n+bool otx2_qos_htb_active(struct otx2_nic *pfvf);\n int otx2_qos_get_qid(struct otx2_nic *pfvf);\n void otx2_qos_free_qid(struct otx2_nic *pfvf, int qidx);\n int otx2_qos_enable_sq(struct otx2_nic *pfvf, int qidx);\n"
} |
| 2/1 | 2026/08/13 09:52 | agent | patch-evaluator |
0mModel:best-expensive Args: null Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"Reasoning": "The patch modifies the Marvell OcteonTX2 NIC driver, which is a vendor-specific hardware driver that requires physical PCIe 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 e4c1704bacf8a2fb30692e1e487ef35f54baa9cf
Author: syz-cluster <triage@syzkaller.com>
Date: Thu Aug 13 09:52:00 2026 +0000
syz-cluster: applied patch under review
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c
index b81c47ea023be..22f64c1ede91a 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c
@@ -331,8 +331,12 @@ static bool is_valid_txschq(struct rvu *rvu, int blkaddr,
return true;
}
- if (map_func != pcifunc)
+ if (map_func != pcifunc) {
+ dev_err_ratelimited(rvu->dev,
+ "pcifunc %x map pcifunc %x not equal, lvl=%u schq=%u\n",
+ pcifunc, map_func, lvl, schq);
return false;
+ }
return true;
}
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c
index ca73a94db7943..b6d8ee7e2d4c2 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c
@@ -614,6 +614,142 @@ void otx2_get_mac_from_af(struct net_device *netdev)
}
EXPORT_SYMBOL(otx2_get_mac_from_af);
+static int
+otx2_nix_tmq_reg_write(struct otx2_nic *pfvf, int cnt,
+ u64 reg_addr[MAX_REGS_PER_MBOX_MSG],
+ u64 reg_val[MAX_REGS_PER_MBOX_MSG])
+{
+ struct mbox *mbox = &pfvf->mbox;
+ struct nix_txschq_config *req;
+ int i, err;
+
+ mutex_lock(&mbox->lock);
+ req = otx2_mbox_alloc_msg_nix_txschq_cfg(mbox);
+ if (!req) {
+ mutex_unlock(&mbox->lock);
+ return -ENOMEM;
+ }
+
+ req->lvl = NIX_TXSCH_LVL_MDQ;
+ req->num_regs = cnt;
+
+ for (i = 0; i < cnt; i++) {
+ req->reg[i] = reg_addr[i];
+ req->regval[i] = reg_val[i];
+ }
+
+ err = otx2_sync_mbox_msg(mbox);
+ mutex_unlock(&mbox->lock);
+
+ return err;
+}
+
+int otx2_nix_tm_clear_queue_shaper(struct otx2_nic *pfvf)
+{
+ u64 reg_addr[MAX_REGS_PER_MBOX_MSG];
+ u64 reg_val[MAX_REGS_PER_MBOX_MSG];
+ int err, smq, i, cnt = 0;
+
+ for (i = 0; i < pfvf->hw.txschq_cnt[NIX_TXSCH_LVL_SMQ]; i++) {
+ smq = pfvf->hw.txschq_list[NIX_TXSCH_LVL_SMQ][i];
+
+ reg_addr[cnt] = NIX_AF_MDQX_PIR(smq);
+ reg_val[cnt] = 0;
+ cnt++;
+
+ reg_addr[cnt] = NIX_AF_MDQX_CIR(smq);
+ reg_val[cnt] = 0;
+ cnt++;
+
+ if (cnt < MAX_REGS_PER_MBOX_MSG - 1)
+ continue;
+
+ err = otx2_nix_tmq_reg_write(pfvf, cnt,
+ reg_addr, reg_val);
+ if (err)
+ goto fail;
+ cnt = 0;
+ }
+
+ if (cnt) {
+ err = otx2_nix_tmq_reg_write(pfvf, cnt,
+ reg_addr, reg_val);
+ if (err)
+ goto fail;
+ }
+
+ return 0;
+fail:
+ return err;
+}
+
+int otx2_nix_tm_set_queue_shaper(struct otx2_nic *pfvf,
+ int txq, u64 minrate, u64 maxrate)
+{
+ struct mbox *mbox = &pfvf->mbox;
+ struct nix_txschq_config *req;
+ int err, smq, n = 0;
+ u64 reg_addr[2];
+ u64 reg_val[2];
+ u64 rate;
+
+ if (!maxrate && !minrate) {
+ smq = otx2_get_smq_idx(pfvf, txq);
+ reg_addr[0] = NIX_AF_MDQX_PIR(smq);
+ reg_val[0] = 0;
+ reg_addr[1] = NIX_AF_MDQX_CIR(smq);
+ reg_val[1] = 0;
+ return otx2_nix_tmq_reg_write(pfvf, 2, reg_addr, reg_val);
+ }
+
+ smq = otx2_get_smq_idx(pfvf, txq);
+
+ mutex_lock(&mbox->lock);
+ req = otx2_mbox_alloc_msg_nix_txschq_cfg(mbox);
+ if (!req) {
+ mutex_unlock(&mbox->lock);
+ return -ENOMEM;
+ }
+
+ req->lvl = NIX_TXSCH_LVL_MDQ;
+
+ /* MQPRIO exposes only min/max rate, not burst. Pass burst 0 so
+ * otx2_get_egress_burst_cfg() programmes the largest burst the NIX
+ * encoding supports (CN10K_MAX_BURST_SIZE on CN10K). This differs
+ * from the 65536 byte default used in the HTB path, which is a
+ * kernel-side default when no explicit burst is configured, not a
+ * hardware cap.
+ *
+ * mqprio setup restarts the netdev (otx2_mqprio_restart_netdev),
+ * which resets MDQ shapers to zero. Program both PIR and CIR on
+ * every update so omitted rates are applied explicitly rather than
+ * relying on stale hardware state.
+ */
+ req->reg[n] = NIX_AF_MDQX_PIR(smq);
+ if (maxrate) {
+ rate = otx2_convert_rate(maxrate);
+ req->regval[n] = otx2_get_txschq_rate_regval(pfvf, rate, 0);
+ } else {
+ req->regval[n] = 0;
+ }
+ n++;
+
+ /* CIR+PIR support is required and checked at mqprio setup. */
+ req->reg[n] = NIX_AF_MDQX_CIR(smq);
+ if (minrate) {
+ rate = otx2_convert_rate(minrate);
+ req->regval[n] = otx2_get_txschq_rate_regval(pfvf, rate, 0);
+ } else {
+ req->regval[n] = 0;
+ }
+ n++;
+ req->num_regs = n;
+
+ err = otx2_sync_mbox_msg(mbox);
+ mutex_unlock(&mbox->lock);
+ return err;
+}
+
int otx2_txschq_config(struct otx2_nic *pfvf, int lvl, int prio, bool txschq_for_pfc)
{
u16 (*schq_list)[MAX_TXSCHQ_PER_FUNC];
@@ -650,7 +786,20 @@ int otx2_txschq_config(struct otx2_nic *pfvf, int lvl, int prio, bool txschq_for
(u64)hw->smq_link_type);
req->num_regs++;
/* MDQ config */
- parent = schq_list[NIX_TXSCH_LVL_TL4][prio];
+ if (pfvf->mqprio.rate_limit) {
+ /* TODO: Program NIX_AF_TL4X_TOPOLOGY on TL4[0]. MQPRIO
+ * parents every MDQ under the same TL4 with schedule
+ * priority 0, so sibling arbitration depends on the TL4
+ * topology register. Only the HTB path programs this
+ * today and the AF does not reset it on schq realloc,
+ * so a TL4 reused after HTB teardown can retain stale
+ * RR_PRIO/PRIO_ANCHOR and starve some TX queues.
+ */
+ parent = schq_list[NIX_TXSCH_LVL_TL4][0];
+ } else {
+ parent = schq_list[NIX_TXSCH_LVL_TL4][prio];
+ }
+
req->reg[1] = NIX_AF_MDQX_PARENT(schq);
req->regval[1] = parent << 16;
req->num_regs++;
@@ -778,6 +927,9 @@ int otx2_txsch_alloc(struct otx2_nic *pfvf)
req->schq[NIX_TXSCH_LVL_TL4] = chan_cnt;
}
+ if (pfvf->mqprio.rate_limit)
+ req->schq[NIX_TXSCH_LVL_SMQ] = pfvf->hw.non_qos_queues;
+
rc = otx2_sync_mbox_msg(&pfvf->mbox);
if (rc)
return rc;
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h
index eecee612b7b2c..67d4aae9440fa 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h
@@ -483,6 +483,13 @@ struct pf_irq_data {
int mdevs;
};
+struct otx2_mqprio {
+ u32 flags;
+ u64 *min_rate;
+ u64 *max_rate;
+ bool rate_limit;
+};
+
struct otx2_nic {
void __iomem *reg_base;
struct net_device *netdev;
@@ -515,6 +522,10 @@ struct otx2_nic {
u64 flags;
u64 *cq_op_addr;
+ struct otx2_mqprio mqprio;
+ bool mqprio_replace_pending;
+ bool mqprio_skip_teardown;
+
struct bpf_prog *xdp_prog;
struct otx2_qset qset;
struct otx2_hw hw;
@@ -1246,6 +1257,11 @@ dma_addr_t otx2_dma_map_skb_frag(struct otx2_nic *pfvf,
struct sk_buff *skb, int seg, int *len);
void otx2_dma_unmap_skb_frags(struct otx2_nic *pfvf, struct sg_list *sg);
int otx2_read_free_sqe(struct otx2_nic *pfvf, u16 qidx);
+int otx2_nix_tm_set_queue_shaper(struct otx2_nic *pfvf, int txq,
+ u64 minrate, u64 maxrate);
+int otx2_nix_tm_clear_queue_shaper(struct otx2_nic *pfvf);
+int otx2_mqprio_down(struct otx2_nic *pfvf);
+int otx2_mqprio_up(struct otx2_nic *pfvf);
void otx2_queue_vf_work(struct mbox *mw, struct workqueue_struct *mbox_wq,
int first, int mdevs, u64 intr);
int otx2_del_mcam_flow_entry(struct otx2_nic *nic, u16 entry,
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_dcbnl.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_dcbnl.c
index f110dfa423605..4a70abc230bea 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_dcbnl.c
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_dcbnl.c
@@ -413,6 +413,12 @@ static int otx2_dcbnl_ieee_setpfc(struct net_device *dev, struct ieee_pfc *pfc)
u8 old_pfc_en;
int err;
+ if (pfvf->mqprio.rate_limit && pfc->pfc_en) {
+ netdev_err(dev,
+ "PFC: cannot enable while mqprio bandwidth offload is active\n");
+ return -EOPNOTSUPP;
+ }
+
old_pfc_en = pfvf->pfc_en;
pfvf->pfc_en = pfc->pfc_en;
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c
index a0340f3422bf9..837d29024981c 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c
@@ -287,6 +287,14 @@ static int otx2_set_channels(struct net_device *dev,
return -EINVAL;
}
+ if (pfvf->mqprio.rate_limit &&
+ (channel->tx_count != pfvf->hw.tx_queues ||
+ channel->rx_count != pfvf->hw.rx_queues)) {
+ netdev_info(dev,
+ "Not permitted to change channel count while MQ prio is active\n");
+ return -EINVAL;
+ }
+
if (if_up)
dev->netdev_ops->ndo_stop(dev);
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c
index c995f29008590..06cb44e7263cf 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c
@@ -1980,6 +1980,16 @@ int otx2_open(struct net_device *netdev)
if (err)
goto err_free_mem;
+ /* TODO: otx2_mqprio_up() failure here aborts the entire ndo_open()
+ * path even though mqprio_rate_limit and the rate cache remain set.
+ * Routine ip link set up or driver reset with active mqprio offload
+ * can leave the interface stuck down until the qdisc is removed and
+ * reapplied.
+ */
+ err = otx2_mqprio_up(pf);
+ if (err)
+ goto err_free_hw;
+
/* Register NAPI handler */
for (qidx = 0; qidx < pf->hw.cint_cnt; qidx++) {
cq_poll = &qset->napi[qidx];
@@ -2140,6 +2150,7 @@ int otx2_open(struct net_device *netdev)
free_irq(vec, pf);
err_disable_napi:
otx2_disable_napi(pf);
+err_free_hw:
otx2_free_hw_resources(pf);
err_free_mem:
otx2_free_queue_mem(qset);
@@ -2846,6 +2857,12 @@ static int otx2_xdp_setup(struct otx2_nic *pf, struct bpf_prog *prog)
bool if_up = netif_running(pf->netdev);
struct bpf_prog *old_prog;
+ if (prog && pf->mqprio.rate_limit) {
+ netdev_err(dev,
+ "XDP: cannot attach while mqprio bandwidth offload is active\n");
+ return -EOPNOTSUPP;
+ }
+
if (prog && dev->mtu > MAX_XDP_MTU) {
netdev_warn(dev, "Jumbo frames not yet supported with XDP\n");
return -EOPNOTSUPP;
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c
index 039fd47ebf522..fc9677e340658 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c
@@ -16,6 +16,8 @@
#include <net/tc_act/tc_mirred.h>
#include <net/tc_act/tc_vlan.h>
#include <net/ipv6.h>
+#include <net/pkt_sched.h>
+#include <net/sch_generic.h>
#include "cn10k.h"
#include "otx2_common.h"
@@ -31,6 +33,10 @@
#define MCAST_INVALID_GRP (-1U)
#define RATE_MANTISSA_BITS 8
+/* Min per-queue egress shaping rate the NIX TLX encoder supports (2 Mbps). */
+#define OTX2_MQPRIO_MIN_RATE_BYTES_PS 250000ULL
+/* Max egress shaping rate the NIX TLX encoder supports (130816 Mbps). */
+#define OTX2_MQPRIO_MAX_RATE_BYTES_PS ((MAX_BURST_SIZE * 1000000ULL) / 8ULL)
static void otx2_get_egress_burst_cfg(struct otx2_nic *nic, u32 burst,
u32 *burst_exp, u32 *burst_mantissa)
@@ -61,6 +67,9 @@ static void otx2_get_egress_burst_cfg(struct otx2_nic *nic, u32 burst,
*burst_mantissa = tmp / (1ULL << (*burst_exp - 7));
}
} else {
+ /* burst 0: largest encodable burst (CN10K_MAX_BURST_SIZE on
+ * CN10K), not a minimal burst.
+ */
*burst_exp = MAX_BURST_EXPONENT;
*burst_mantissa = max_mantissa;
}
@@ -1600,14 +1609,596 @@ static int otx2_setup_tc_block(struct net_device *netdev,
nic, nic, ingress);
}
+/* Free the per-queue min/max rate caches. */
+static void otx2_mqprio_free_cache(struct otx2_nic *pfvf)
+{
+ devm_kfree(pfvf->dev, pfvf->mqprio.min_rate);
+ devm_kfree(pfvf->dev, pfvf->mqprio.max_rate);
+ pfvf->mqprio.min_rate = NULL;
+ pfvf->mqprio.max_rate = NULL;
+ pfvf->mqprio.flags = 0;
+}
+
+static int otx2_mqprio_alloc_cache(struct otx2_nic *pfvf)
+{
+ u16 num_txq = pfvf->hw.non_qos_queues;
+
+ /* TODO: otx2_mqprio_free_cache() here drops the committed rate cache
+ * on tc qdisc replace before the new mapping is complete. Stage the
+ * incoming rates separately and commit only after replace succeeds so
+ * a failure after netdev restart can roll back from the prior cache.
+ */
+ otx2_mqprio_free_cache(pfvf);
+
+ pfvf->mqprio.min_rate = devm_kcalloc(pfvf->dev, num_txq,
+ sizeof(*pfvf->mqprio.min_rate),
+ GFP_KERNEL);
+ pfvf->mqprio.max_rate = devm_kcalloc(pfvf->dev, num_txq,
+ sizeof(*pfvf->mqprio.max_rate),
+ GFP_KERNEL);
+ if (!pfvf->mqprio.min_rate || !pfvf->mqprio.max_rate) {
+ otx2_mqprio_free_cache(pfvf);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static bool otx2_txschq_is_allocated(struct otx2_nic *pfvf)
+{
+ int lvl, idx;
+
+ for (lvl = 0; lvl < NIX_TXSCH_LVL_CNT; lvl++) {
+ for (idx = 0; idx < pfvf->hw.txschq_cnt[lvl]; idx++) {
+ if (pfvf->hw.txschq_list[lvl][idx])
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/* Tear down mqprio bandwidth offload: clear per-queue shapers,
+ * mqprio_rate_limit, netdev TC mappings, and the cached rates. Called on
+ * explicit mqprio teardown (tc qdisc del) and error cleanup, not on
+ * routine netdev stop/open cycles where the offload stays active.
+ */
+int otx2_mqprio_down(struct otx2_nic *pfvf)
+{
+ struct net_device *netdev = pfvf->netdev;
+ int err = 0;
+
+ if (!pfvf->mqprio.rate_limit)
+ return 0;
+
+ if (otx2_txschq_is_allocated(pfvf))
+ err = otx2_nix_tm_clear_queue_shaper(pfvf);
+
+ /* TODO: mqprio_down() clears mqprio_rate_limit, netdev TC mappings,
+ * and the rate cache even when otx2_nix_tm_clear_queue_shaper() fails
+ * partway through the MDQ batch. Software then reports offload as
+ * inactive while some queues may retain programmed CIR/PIR, so a later
+ * mqprio_up() or re-setup can shape with the wrong effective rates.
+ */
+ pfvf->mqprio.rate_limit = false;
+ pfvf->mqprio_replace_pending = false;
+ pfvf->mqprio_skip_teardown = false;
+ netdev_set_num_tc(netdev, 0);
+ otx2_mqprio_free_cache(pfvf);
+
+ return err;
+}
+
+int otx2_mqprio_up(struct otx2_nic *pfvf)
+{
+ struct net_device *netdev = pfvf->netdev;
+ int txq, err;
+
+ if (!pfvf->mqprio.rate_limit)
+ return 0;
+
+ if (!pfvf->mqprio.min_rate || !pfvf->mqprio.max_rate)
+ return 0;
+
+ for (txq = 0; txq < pfvf->hw.non_qos_queues; txq++) {
+ u64 min_rate = 0, max_rate = 0;
+
+ if (pfvf->mqprio.flags & TC_MQPRIO_F_MIN_RATE)
+ min_rate = pfvf->mqprio.min_rate[txq];
+ if (pfvf->mqprio.flags & TC_MQPRIO_F_MAX_RATE)
+ max_rate = pfvf->mqprio.max_rate[txq];
+
+ if (!min_rate && !max_rate)
+ continue;
+
+ err = otx2_nix_tm_set_queue_shaper(pfvf, txq, min_rate,
+ max_rate);
+ if (err) {
+ netdev_err(netdev,
+ "mqprio: failed to restore shaper for txq %d: %d\n",
+ txq, err);
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+/* Restart the netdev to reprogram the TX scheduler hierarchy for mqprio
+ * bandwidth offload. Both mqprio add and delete (when offload was active)
+ * take this path via ndo_stop()/ndo_open() so VF-specific open logic (e.g.
+ * LBK carrier on) runs correctly. The full stop/open cycle clears
+ * carrier, stops all TX queues, tears down IRQs/NAPI and drops in-flight
+ * traffic. If open fails, the interface is left administratively down
+ * without calling ndo_stop() again on resources already torn down by
+ * the open error path.
+ */
+static int otx2_mqprio_restart_netdev(struct net_device *netdev, bool rate_limit)
+{
+ struct otx2_nic *pfvf = netdev_priv(netdev);
+ const struct net_device_ops *ops = netdev->netdev_ops;
+ bool up = netdev->flags & IFF_UP;
+ int err;
+
+ /* TODO: Every mqprio add, replace, and delete runs a full ndo_stop()/
+ * ndo_open() cycle (carrier drop, IRQ/NAPI teardown, in-flight traffic
+ * loss) to reprogram the TX scheduler. If ndo_open() fails the
+ * interface is left administratively down (OTX2_FLAG_INTF_DOWN) until
+ * manual recovery. Explore live reprogramming without bouncing the
+ * netdev.
+ */
+ netdev_info(netdev,
+ "mqprio: restarting interface to reprogram TX scheduler; in-flight traffic will be dropped\n");
+
+ if (up)
+ dev_deactivate(netdev, true);
+
+ err = ops->ndo_stop(netdev);
+ if (err)
+ goto out_activate;
+
+ /* Set before ndo_open() so otx2_txsch_alloc() widens SMQ allocation. */
+ if (rate_limit)
+ pfvf->mqprio.rate_limit = true;
+
+ err = ops->ndo_open(netdev);
+ if (err) {
+ int down_err;
+
+ netdev_err(netdev,
+ "Failed to restart device after mqprio change: %d\n",
+ err);
+ down_err = otx2_mqprio_down(pfvf);
+ if (down_err)
+ netdev_err(netdev,
+ "mqprio: failed to clear shapers after restart error: %d\n",
+ down_err);
+ /* ndo_open() rolls back on failure; mark the interface down so
+ * netif_close() does not invoke ndo_stop() on freed NAPI/queue
+ * state. Caller holds RTNL; dev_close() would deadlock.
+ */
+ pfvf->flags |= OTX2_FLAG_INTF_DOWN;
+ /* visible to otx2_stop() on other cpus */
+ smp_wmb();
+ netif_close(netdev);
+ return err;
+ }
+
+out_activate:
+ if (up && !err)
+ dev_activate(netdev);
+
+ return err;
+}
+
+static u64 otx2_mqprio_per_queue_rate(u64 rate, u32 qcount)
+{
+ if (!rate || qcount <= 1)
+ return rate;
+
+ return div_u64(rate, qcount);
+}
+
+static int otx2_mqprio_validate_tc_rate(struct net_device *netdev,
+ struct netlink_ext_ack *extack,
+ u64 rate, u32 qcount, int tc,
+ const char *name)
+{
+ if (!rate || qcount <= 1 || rate >= qcount)
+ return 0;
+
+ netdev_err(netdev,
+ "mqprio: %s rate %llu for tc %d too small for %u queues\n",
+ name, rate, tc, qcount);
+ NL_SET_ERR_MSG_FMT_MOD(extack,
+ "mqprio: %s rate %llu for tc %d too small for %u queues",
+ name, rate, tc, qcount);
+ return -EINVAL;
+}
+
+static int otx2_mqprio_validate_txqs(struct net_device *netdev,
+ struct netlink_ext_ack *extack,
+ struct tc_mqprio_qopt *qopt)
+{
+ struct otx2_nic *pfvf = netdev_priv(netdev);
+ u16 num_txq = pfvf->hw.non_qos_queues;
+ int tc, txq;
+
+ if (qopt->num_tc > num_txq) {
+ netdev_err(netdev, "Number of TCs (%u) exceeds hw queues %u\n",
+ qopt->num_tc, num_txq);
+ NL_SET_ERR_MSG_FMT_MOD(extack,
+ "Number of TCs (%u) exceeds hw queues %u",
+ qopt->num_tc, num_txq);
+ return -EINVAL;
+ }
+
+ if (num_txq > MAX_TXSCHQ_PER_FUNC) {
+ netdev_err(netdev,
+ "Number of queues (%u) exceeds max scheduler queues %u\n",
+ num_txq, MAX_TXSCHQ_PER_FUNC);
+ NL_SET_ERR_MSG_FMT_MOD(extack,
+ "Number of queues (%u) exceeds max scheduler queues %u",
+ num_txq, MAX_TXSCHQ_PER_FUNC);
+ return -EINVAL;
+ }
+
+ for (tc = 0; tc < qopt->num_tc; tc++) {
+ u32 qcount = qopt->count[tc];
+
+ for (txq = qopt->offset[tc];
+ txq < qopt->offset[tc] + qcount; txq++) {
+ if (txq >= num_txq) {
+ netdev_err(netdev,
+ "mqprio: txq %d exceeds offload queue count %u\n",
+ txq, num_txq);
+ NL_SET_ERR_MSG_FMT_MOD(extack,
+ "mqprio: txq %d exceeds offload queue count %u",
+ txq, num_txq);
+ return -EINVAL;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static bool otx2_mqprio_rate_valid(u64 rate_bytes_ps)
+{
+ u64 mbps;
+
+ if (!rate_bytes_ps)
+ return true;
+
+ if (rate_bytes_ps < OTX2_MQPRIO_MIN_RATE_BYTES_PS)
+ return false;
+
+ if (rate_bytes_ps > OTX2_MQPRIO_MAX_RATE_BYTES_PS)
+ return false;
+
+ if (rate_bytes_ps > div_u64(U64_MAX, 8))
+ return false;
+
+ mbps = otx2_convert_rate(rate_bytes_ps);
+ return ilog2(mbps / 2) <= MAX_RATE_EXPONENT;
+}
+
+static void otx2_mqprio_replace_failed_msg(struct net_device *netdev,
+ struct netlink_ext_ack *extack)
+{
+ /* TODO: On failed tc qdisc replace restore the prior rate cache,
+ * netdev TC mapping, and hardware shapers from a snapshot taken before
+ * reprogramming instead of leaving the new offload values active while
+ * the previous mqprio remains the grafted root qdisc.
+ */
+ netdev_err(netdev,
+ "mqprio: replace failed; new offload values active, old mqprio still root\n");
+ if (extack)
+ NL_SET_ERR_MSG_MOD(extack,
+ "mqprio: replace failed; new offload values active, old mqprio still root");
+}
+
+static int otx2_teardown_tc_mqprio(struct otx2_nic *pfvf,
+ struct tc_mqprio_qopt_offload *mqprio)
+{
+ bool had_mqprio = pfvf->mqprio.rate_limit;
+ struct tc_mqprio_qopt *qopt = &mqprio->qopt;
+ struct net_device *netdev = pfvf->netdev;
+ bool if_up = netif_running(netdev);
+ int down_err, err;
+
+ qopt->hw = 0;
+
+ /* tc qdisc replace grafts the new mqprio before destroying the old
+ * one. mqprio_skip_teardown is armed from TC_ROOT_GRAFT so teardown
+ * from the replaced qdisc is ignored after the new configuration has
+ * reprogrammed hardware and set mqprio_rate_limit.
+ */
+ if (pfvf->mqprio_skip_teardown) {
+ pfvf->mqprio_skip_teardown = false;
+ pfvf->mqprio_replace_pending = false;
+ return 0;
+ }
+
+ if (pfvf->mqprio_replace_pending) {
+ otx2_mqprio_replace_failed_msg(netdev, NULL);
+ pfvf->mqprio_replace_pending = false;
+ return -EIO;
+ }
+
+ /* Skip the netdev restart when mqprio offload was not active. */
+ if (!had_mqprio)
+ return 0;
+
+ down_err = otx2_mqprio_down(pfvf);
+
+ if (if_up) {
+ err = otx2_mqprio_restart_netdev(netdev, false);
+ if (err)
+ return err;
+ }
+
+ return down_err;
+}
+
+static int otx2_setup_tc_mqprio(struct net_device *netdev,
+ struct tc_mqprio_qopt_offload *mqprio)
+{
+ struct otx2_nic *pfvf = netdev_priv(netdev);
+ struct tc_mqprio_qopt *qopt = &mqprio->qopt;
+ struct netlink_ext_ack *extack = mqprio->extack;
+ bool replacing = pfvf->mqprio.rate_limit;
+ bool if_up = netif_running(netdev);
+ int tc, txq, err, i;
+
+ if (!qopt->hw)
+ return otx2_teardown_tc_mqprio(pfvf, mqprio);
+
+ if (!if_up) {
+ netdev_err(netdev, "mqprio: setup requires interface UP\n");
+ NL_SET_ERR_MSG_MOD(extack, "mqprio: setup requires interface UP");
+ return -EOPNOTSUPP;
+ }
+
+ if (mqprio->shaper != TC_MQPRIO_SHAPER_BW_RATE) {
+ netdev_err(netdev, "Unsupported mqprio shaper %#x\n", mqprio->shaper);
+ NL_SET_ERR_MSG_FMT_MOD(extack, "Unsupported mqprio shaper %#x",
+ mqprio->shaper);
+ return -EOPNOTSUPP;
+ }
+
+ if (!test_bit(QOS_CIR_PIR_SUPPORT, &pfvf->hw.cap_flag)) {
+ netdev_err(netdev,
+ "mqprio: bandwidth offload requires CIR+PIR support\n");
+ NL_SET_ERR_MSG_MOD(extack,
+ "mqprio: bandwidth offload requires CIR+PIR support");
+ return -EOPNOTSUPP;
+ }
+
+ if (is_otx2_sdp_rep(pfvf->pdev)) {
+ netdev_err(netdev, "mqprio: bandwidth offload not supported on SDP rep\n");
+ NL_SET_ERR_MSG_MOD(extack,
+ "mqprio: bandwidth offload not supported on SDP rep");
+ return -EOPNOTSUPP;
+ }
+
+ if (otx2_qos_htb_active(pfvf)) {
+ netdev_err(netdev, "mqprio: cannot enable offload while HTB is active\n");
+ NL_SET_ERR_MSG_MOD(extack,
+ "mqprio: cannot enable offload while HTB is active");
+ return -EOPNOTSUPP;
+ }
+
+ if (pfvf->pfc_en) {
+ netdev_err(netdev,
+ "mqprio: cannot enable offload while PFC is enabled\n");
+ NL_SET_ERR_MSG_MOD(extack,
+ "mqprio: cannot enable offload while PFC is enabled");
+ return -EOPNOTSUPP;
+ }
+
+ if (pfvf->xdp_prog) {
+ netdev_err(netdev,
+ "mqprio: cannot enable offload while XDP is active\n");
+ NL_SET_ERR_MSG_MOD(extack,
+ "mqprio: cannot enable offload while XDP is active");
+ return -EOPNOTSUPP;
+ }
+
+ for (tc = 0; tc < qopt->num_tc; tc++) {
+ u64 min_rate = 0, max_rate = 0;
+ u32 qcount = qopt->count[tc];
+
+ if (mqprio->flags & TC_MQPRIO_F_MIN_RATE)
+ min_rate = mqprio->min_rate[tc];
+ if (mqprio->flags & TC_MQPRIO_F_MAX_RATE)
+ max_rate = mqprio->max_rate[tc];
+
+ if (min_rate && max_rate && min_rate > max_rate) {
+ netdev_err(netdev,
+ "min_rate %llu exceeds max_rate %llu for tc %d\n",
+ min_rate, max_rate, tc);
+ NL_SET_ERR_MSG_FMT_MOD(extack,
+ "min_rate %llu exceeds max_rate %llu for tc %d",
+ min_rate, max_rate, tc);
+ return -EINVAL;
+ }
+
+ if (mqprio->flags & TC_MQPRIO_F_MIN_RATE) {
+ err = otx2_mqprio_validate_tc_rate(netdev, extack, min_rate,
+ qcount, tc, "min");
+ if (err)
+ return err;
+ }
+
+ if (mqprio->flags & TC_MQPRIO_F_MAX_RATE) {
+ err = otx2_mqprio_validate_tc_rate(netdev, extack, max_rate,
+ qcount, tc, "max");
+ if (err)
+ return err;
+ }
+
+ if (mqprio->flags & TC_MQPRIO_F_MIN_RATE &&
+ !otx2_mqprio_rate_valid(otx2_mqprio_per_queue_rate(min_rate,
+ qcount))) {
+ netdev_err(netdev,
+ "mqprio: min_rate %llu for tc %d is outside hardware limits\n",
+ min_rate, tc);
+ NL_SET_ERR_MSG_FMT_MOD(extack,
+ "mqprio: min_rate %llu for tc %d is outside hardware limits",
+ min_rate, tc);
+ return -EINVAL;
+ }
+
+ if (mqprio->flags & TC_MQPRIO_F_MAX_RATE &&
+ !otx2_mqprio_rate_valid(otx2_mqprio_per_queue_rate(max_rate,
+ qcount))) {
+ netdev_err(netdev,
+ "mqprio: max_rate %llu for tc %d is outside hardware limits\n",
+ max_rate, tc);
+ NL_SET_ERR_MSG_FMT_MOD(extack,
+ "mqprio: max_rate %llu for tc %d is outside hardware limits",
+ max_rate, tc);
+ return -EINVAL;
+ }
+ }
+
+ err = otx2_mqprio_validate_txqs(netdev, extack, qopt);
+ if (err)
+ return err;
+
+ err = otx2_mqprio_restart_netdev(pfvf->netdev, true);
+ if (err)
+ return err;
+
+ /* TODO: Failures from here through the end of init() (e.g. -ENOMEM
+ * from otx2_mqprio_alloc_cache(), mbox errors from
+ * otx2_nix_tm_clear_queue_shaper() or otx2_nix_tm_set_queue_shaper())
+ * reach cleanup. On tc qdisc replace the old mqprio remains grafted,
+ * so cleanup returns without rolling back netdev TC mapping, the rate
+ * cache, or partially reprogrammed hardware shapers. Restore the prior
+ * configuration instead of calling otx2_mqprio_down() and bouncing the
+ * interface a second time.
+ */
+ err = otx2_mqprio_alloc_cache(pfvf);
+ if (err)
+ goto cleanup;
+
+ /* otx2_mqprio_up() may have restored the previous configuration during
+ * the restart above. Clear every MDQ shaper before applying the new
+ * mapping so queues dropped from the TC layout do not keep stale
+ * limits in hardware.
+ */
+ if (otx2_txschq_is_allocated(pfvf)) {
+ err = otx2_nix_tm_clear_queue_shaper(pfvf);
+ if (err)
+ goto cleanup;
+ }
+
+ pfvf->mqprio.flags = mqprio->flags;
+
+ for (tc = 0; tc < qopt->num_tc; tc++) {
+ u64 min_rate = 0, max_rate = 0;
+ u32 qcount = qopt->count[tc];
+
+ /* Rates omitted from tc mqprio are passed as zero and both MDQ
+ * shaper registers are programmed; see
+ * otx2_nix_tm_set_queue_shaper().
+ */
+ if (mqprio->flags & TC_MQPRIO_F_MIN_RATE)
+ min_rate = otx2_mqprio_per_queue_rate(mqprio->min_rate[tc],
+ qcount);
+ if (mqprio->flags & TC_MQPRIO_F_MAX_RATE)
+ max_rate = otx2_mqprio_per_queue_rate(mqprio->max_rate[tc],
+ qcount);
+
+ for (txq = qopt->offset[tc];
+ txq < qopt->offset[tc] + qcount; txq++) {
+ netdev_dbg(netdev,
+ "mqprio: tc %d txq %d min_rate %llu max_rate %llu\n",
+ tc, txq, min_rate, max_rate);
+
+ pfvf->mqprio.min_rate[txq] = min_rate;
+ pfvf->mqprio.max_rate[txq] = max_rate;
+
+ err = otx2_nix_tm_set_queue_shaper(pfvf, txq,
+ min_rate, max_rate);
+ if (err)
+ goto cleanup;
+ }
+ }
+
+ netdev_set_num_tc(netdev, qopt->num_tc);
+ for (i = 0; i < qopt->num_tc; i++)
+ netdev_set_tc_queue(netdev, i, qopt->count[i], qopt->offset[i]);
+
+ qopt->hw = TC_MQPRIO_HW_OFFLOAD_TCS;
+
+ if (replacing)
+ pfvf->mqprio_replace_pending = true;
+
+ return 0;
+
+cleanup:
+ /* Clear hardware offload on this rejected request so a failed replace
+ * does not leave tc reporting offload for driver state that was torn
+ * down while the previous mqprio remains grafted.
+ */
+ qopt->hw = 0;
+ if (replacing) {
+ otx2_mqprio_replace_failed_msg(netdev, extack);
+ pfvf->mqprio_replace_pending = false;
+ return err ? err : -EIO;
+ }
+ otx2_teardown_tc_mqprio(pfvf, mqprio);
+ return err;
+}
+
+static int otx2_setup_tc_root(struct otx2_nic *pfvf,
+ struct tc_root_qopt_offload *root)
+{
+ switch (root->command) {
+ case TC_ROOT_GRAFT:
+ if (root->ingress)
+ return 0;
+ if (pfvf->mqprio_replace_pending) {
+ pfvf->mqprio_skip_teardown = true;
+ pfvf->mqprio_replace_pending = false;
+ }
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int otx2_setup_tc_query_caps(void *type_data)
+{
+ struct tc_query_caps_base *base = type_data;
+ struct tc_mqprio_caps *caps;
+
+ if (base->type != TC_SETUP_QDISC_MQPRIO)
+ return -EOPNOTSUPP;
+
+ caps = base->caps;
+ caps->validate_queue_counts = true;
+
+ return 0;
+}
+
int otx2_setup_tc(struct net_device *netdev, enum tc_setup_type type,
void *type_data)
{
switch (type) {
+ case TC_QUERY_CAPS:
+ return otx2_setup_tc_query_caps(type_data);
case TC_SETUP_BLOCK:
return otx2_setup_tc_block(netdev, type_data);
case TC_SETUP_QDISC_HTB:
return otx2_setup_tc_htb(netdev, type_data);
+ case TC_SETUP_QDISC_MQPRIO:
+ return otx2_setup_tc_mqprio(netdev, type_data);
+ case TC_SETUP_ROOT_QDISC:
+ return otx2_setup_tc_root(netdev_priv(netdev), type_data);
default:
return -EOPNOTSUPP;
}
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/qos.c b/drivers/net/ethernet/marvell/octeontx2/nic/qos.c
index 69c0911e28e91..cf00217883563 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/qos.c
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/qos.c
@@ -118,6 +118,9 @@ static void otx2_config_sched_shaping(struct otx2_nic *pfvf,
/* configure PIR */
maxrate = (node->rate > node->ceil) ? node->rate : node->ceil;
+ /* 65536 is the kernel-side default burst when HTB does not supply an
+ * explicit value, not the NIX hardware maximum (CN10K_MAX_BURST_SIZE).
+ */
cfg->regval[*num_regs] =
otx2_get_txschq_rate_regval(pfvf, maxrate, 65536);
(*num_regs)++;
@@ -1729,11 +1732,23 @@ void otx2_qos_config_txschq(struct otx2_nic *pfvf)
otx2_qos_root_destroy(pfvf);
}
+bool otx2_qos_htb_active(struct otx2_nic *pfvf)
+{
+ return otx2_sw_node_find(pfvf, OTX2_QOS_ROOT_CLASSID);
+}
+
int otx2_setup_tc_htb(struct net_device *ndev, struct tc_htb_qopt_offload *htb)
{
struct otx2_nic *pfvf = netdev_priv(ndev);
int res;
+ if (pfvf->mqprio.rate_limit &&
+ htb->command != TC_HTB_DESTROY) {
+ NL_SET_ERR_MSG_MOD(htb->extack,
+ "HTB offload cannot be used with mqprio bandwidth offload active");
+ return -EOPNOTSUPP;
+ }
+
switch (htb->command) {
case TC_HTB_CREATE:
return otx2_qos_root_add(pfvf, htb->parent_classid,
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/qos.h b/drivers/net/ethernet/marvell/octeontx2/nic/qos.h
index 221bd0438f603..c777cc42eac9c 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/qos.h
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/qos.h
@@ -23,6 +23,7 @@ enum qos_smq_operations {
u64 otx2_get_txschq_rate_regval(struct otx2_nic *nic, u64 maxrate, u32 burst);
int otx2_setup_tc_htb(struct net_device *ndev, struct tc_htb_qopt_offload *htb);
+bool otx2_qos_htb_active(struct otx2_nic *pfvf);
int otx2_qos_get_qid(struct otx2_nic *pfvf);
void otx2_qos_free_qid(struct otx2_nic *pfvf, int qidx);
int otx2_qos_enable_sq(struct otx2_nic *pfvf, int qidx);
|
| 3/2 | 2026/08/13 09:52 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 15744 output: 40 thoughts: 389 Thoughts: **My Assessment of the Marvell OcteonTX2 mqprio Offload Patch** Okay, so I'm looking at this patch for the Marvell OcteonTX2 NIC driver, specifically adding Multi-Queue Priority (mqprio) bandwidth offload. My immediate thoughts are that this looks like a standard implementation, involving the usual modifications to setup functions and other NIC-specific configurations. I see changes to `otx2_setup_tc_mqprio`, `otx2_setup_tc`, the TX scheduler configurations (`otx2_txschq_config`, `otx2_txsch_alloc`), DCB configurations, channel setup, and even XDP-related functions. Pretty standard fare for enabling a feature like this. But wait, I need to make sure this meets the prompt's criteria for WorthFuzzing=false. The prompt explicitly says to skip patches that are vendor-specific code that requires specific hardware. And here, we're talking about the Marvell OcteonTX2. That's a dedicated hardware NIC. It's not like it's a generic NIC that QEMU could easily emulate. So my key question is: Is this Marvell OcteonTX2 NIC emulated in standard QEMU? From my experience with these kinds of devices, OcteonTX2 (or rather, the RVU or NIX parts of it) is almost always a physical NIC found in Marvell SoCs. I don't recall seeing good emulation for it in standard QEMU, because of its architecture-specific design. I'm going to quickly check to confirm this assumption. If my memory is right, and this *isn't* emulated in standard QEMU, then I will return WorthFuzzing=false, as this falls under the hardware-specific driver rule. |
| 4/2 | 2026/08/13 09:52 | tool | grepper |
0mArgs:{
"Expression": "octeontx2",
"PathPrefix": "drivers/net/ethernet/marvell/octeontx2"
}Results: {
"Output": "drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c-27-\ndrivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c:28:#define DEBUGFS_DIR_NAME \"octeontx2\"\ndrivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c-29-\n--\ndrivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c=4013=static const char *rvu_get_dbg_dir_name(struct rvu *rvu)\n--\ndrivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c-4020-\telse\ndrivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c:4021:\t\treturn \"octeontx2\";\ndrivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c-4022-}\n--\ndrivers/net/ethernet/marvell/octeontx2/af/rvu_devlink.c-15-\ndrivers/net/ethernet/marvell/octeontx2/af/rvu_devlink.c:16:#define DRV_NAME \"octeontx2-af\"\ndrivers/net/ethernet/marvell/octeontx2/af/rvu_devlink.c-17-\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/Makefile=18=rvu_nicpf-$(CONFIG_XFRM_OFFLOAD) += cn10k_ipsec.o\ndrivers/net/ethernet/marvell/octeontx2/nic/Makefile-19-\ndrivers/net/ethernet/marvell/octeontx2/nic/Makefile:20:ccflags-y += -I$(srctree)/drivers/net/ethernet/marvell/octeontx2/af\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h-17-#include \u003clinux/soc/marvell/silicons.h\u003e\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h:18:#include \u003clinux/soc/marvell/octeontx2/asm.h\u003e\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h-19-#include \u003cnet/macsec.h\u003e\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/qos.c=1118=static int otx2_qos_validate_quantum(struct otx2_nic *pfvf, u32 quantum)\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/qos.c-1122-\ndrivers/net/ethernet/marvell/octeontx2/nic/qos.c:1123:\t/* Max Round robin weight supported by octeontx2 and CN10K\ndrivers/net/ethernet/marvell/octeontx2/nic/qos.c-1124-\t * is different. Validate accordingly\n"
} |
| 5/2 | 2026/08/13 09:52 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 17381 output: 82 thoughts: 110 Thoughts: **My Assessment of the Marvell OcteonTX2 Driver** Okay, let's break this down. The Marvell OcteonTX2 driver, the one we're dealing with, is specifically designed to work with a physical, dedicated Network Interface Card (NIC). This is real, physical PCIe hardware. And that's the key. I need to make a judgment call here, and based on my understanding of the exclusion criteria, this driver is not a good fit for WorthFuzzing. The criteria explicitly mentions drivers in the realm of "vendor-specific PCIe switch, SmartNIC, or GPU drivers" which depend on *actual* PCIe hardware, not the emulated kind you'd find in a standard QEMU environment. This driver definitely falls into that category. I'm seeing "physical PCIe hardware cards not emulated in standard QEMU" in both the driver characteristics and the exclusion criteria. Therefore, I'm going to set WorthFuzzing to false. It's just not appropriate for this particular scenario. |
| 6/2 | 2026/08/13 09:52 | tool | set-results |
0mArgs:{
"EnableConfigs": [],
"FocusSymbols": [],
"Reasoning": "The patch modifies the Marvell OcteonTX2 NIC driver, which is a vendor-specific hardware driver that requires physical PCIe 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 modifies the Marvell OcteonTX2 NIC driver, which is a vendor-specific hardware driver that requires physical PCIe hardware not emulated in standard QEMU. Therefore, the modified code paths are unreachable in virtualized fuzzing environments.",
"WorthFuzzing": false
} |
| 7/1 | 2026/08/13 09:52 | 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) |
|---|