AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/09/08 07:05 flow patch-triage
0m Args:
null

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "KMSANReasoning": "",
  "NeedsKMSAN": false,
  "Reasoning": "The patch modifies only the Marvell OcteonTX2 network driver (drivers/net/ethernet/marvell/octeontx2/), adding support for mqprio hardware offload rate limiting and related queue scheduler adjustments. This driver targets physical Marvell/Cavium OcteonTX2 hardware / SmartNICs which are not emulated in standard virtualized fuzzing environments (QEMU/GCE), making the modified code structurally unreachable.",
  "WorthFuzzing": false
}

1/1 2026/09/08 07:05 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit bc2c960012a1d9e4a89a3352fd3f0b3c5788d661\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Tue Sep 8 07:05:22 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 153eb57bad067..c9a4326d725d8 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 175992188c18a..a519e0a97a2b7 100644\n--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c\n+++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c\n@@ -615,6 +615,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@@ -651,7 +787,11 @@ 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\tparent = schq_list[NIX_TXSCH_LVL_TL4][0];\n+\t\telse\n+\t\t\tparent = schq_list[NIX_TXSCH_LVL_TL4][prio];\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@@ -779,6 +919,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;\n@@ -844,6 +987,7 @@ void otx2_txschq_stop(struct otx2_nic *pfvf)\n \n \t/* Clear the txschq list */\n \tfor (lvl = 0; lvl \u003c NIX_TXSCH_LVL_CNT; lvl++) {\n+\t\tpfvf-\u003ehw.txschq_cnt[lvl] = 0;\n \t\tfor (schq = 0; schq \u003c MAX_TXSCHQ_PER_FUNC; schq++)\n \t\t\tpfvf-\u003ehw.txschq_list[lvl][schq] = 0;\n \t}\ndiff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h\nindex eecee612b7b2c..ede7f1113b714 100644\n--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h\n+++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h\n@@ -17,6 +17,7 @@\n #include \u003clinux/soc/marvell/silicons.h\u003e\n #include \u003clinux/soc/marvell/octeontx2/asm.h\u003e\n #include \u003cnet/macsec.h\u003e\n+#include \u003cuapi/linux/pkt_sched.h\u003e\n #include \u003cnet/pkt_cls.h\u003e\n #include \u003cnet/devlink.h\u003e\n #include \u003clinux/time64.h\u003e\n@@ -483,6 +484,23 @@ struct pf_irq_data {\n \tint mdevs;\n };\n \n+struct mq_offload_snap {\n+\tu64 min_rate[TC_QOPT_MAX_QUEUE];\n+\tu64 max_rate[TC_QOPT_MAX_QUEUE];\n+\t__u8 num_tc;\n+\t__u16 count[TC_QOPT_MAX_QUEUE];\n+\t__u16 offset[TC_QOPT_MAX_QUEUE];\n+};\n+\n+struct otx2_mqprio {\n+\tu32\tflags;\n+\tu64\t*min_rate;\n+\tu64\t*max_rate;\n+\tbool\trate_limit;\n+\tbool\treplace_setup_done;\n+\tbool\treplace_graft_done;\n+};\n+\n struct otx2_nic {\n \tvoid __iomem\t\t*reg_base;\n \tstruct net_device\t*netdev;\n@@ -515,6 +533,10 @@ struct otx2_nic {\n \tu64\t\t\tflags;\n \tu64\t\t\t*cq_op_addr;\n \n+\tstruct otx2_mqprio\tmqprio;\n+\tstruct mq_offload_snap\t*cur_mq_snap;\n+\tstruct mq_offload_snap\t*old_mq_snap;\n+\n \tstruct bpf_prog\t\t*xdp_prog;\n \tstruct otx2_qset\tqset;\n \tstruct otx2_hw\t\thw;\n@@ -1246,6 +1268,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 9bee1b91eeaa6..ec2601c6c255a 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 c0e2100de1d93..68ad2f3d22277 100644\n--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c\n+++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c\n@@ -907,11 +907,13 @@ static int otx2_mbox_up_handler_rep_event_up_notify(struct otx2_nic *pf,\n \n \tif (info-\u003eevent == RVU_EVENT_PORT_STATE) {\n \t\tif (info-\u003eevt_data.port_state) {\n-\t\t\tpf-\u003eflags |= OTX2_FLAG_PORT_UP;\n+\t\t\tset_bit(__builtin_ctzll(OTX2_FLAG_PORT_UP),\n+\t\t\t\t(unsigned long *)\u0026pf-\u003eflags);\n \t\t\tnetif_carrier_on(netdev);\n \t\t\tnetif_tx_start_all_queues(netdev);\n \t\t} else {\n-\t\t\tpf-\u003eflags \u0026= ~OTX2_FLAG_PORT_UP;\n+\t\t\tclear_bit(__builtin_ctzll(OTX2_FLAG_PORT_UP),\n+\t\t\t\t  (unsigned long *)\u0026pf-\u003eflags);\n \t\t\tnetif_tx_stop_all_queues(netdev);\n \t\t\tnetif_carrier_off(netdev);\n \t\t}\n@@ -2007,6 +2009,14 @@ int otx2_open(struct net_device *netdev)\n \tif (err)\n \t\tgoto err_free_mem;\n \n+\terr = otx2_mqprio_up(pf);\n+\tif (err) {\n+\t\tnetdev_err(pf-\u003enetdev,\n+\t\t\t   \"mqprio: failed to restore shapers during open: %d\\n\",\n+\t\t\t   err);\n+\t\tgoto err_free_hw;\n+\t}\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@@ -2205,6 +2215,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@@ -2226,7 +2237,8 @@ int otx2_stop(struct net_device *netdev)\n \tnetif_carrier_off(netdev);\n \tnetif_tx_stop_all_queues(netdev);\n \n-\tpf-\u003eflags |= OTX2_FLAG_INTF_DOWN;\n+\tset_bit(__builtin_ctzll(OTX2_FLAG_INTF_DOWN),\n+\t\t(unsigned long *)\u0026pf-\u003eflags);\n \t/* 'intf_down' may be checked on any cpu */\n \tsmp_wmb();\n \n@@ -2280,6 +2292,7 @@ int otx2_stop(struct net_device *netdev)\n \tfor (qidx = 0; qidx \u003c netdev-\u003enum_tx_queues; qidx++)\n \t\tnetdev_tx_reset_queue(netdev_get_tx_queue(netdev, qidx));\n \n+\tsynchronize_net();\n \totx2_free_queue_mem(qset);\n \t/* Do not clear RQ/SQ ringsize settings */\n \tmemset_startat(qset, 0, sqe_cnt);\n@@ -2923,6 +2936,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..045b4573ce14e 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,752 @@ 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, bool replacing)\n+{\n+\tu16 num_txq = pfvf-\u003ehw.non_qos_queues;\n+\n+\tif (replacing \u0026\u0026 pfvf-\u003emqprio.min_rate \u0026\u0026 pfvf-\u003emqprio.max_rate) {\n+\t\tmemset(pfvf-\u003emqprio.min_rate, 0,\n+\t\t       num_txq * sizeof(*pfvf-\u003emqprio.min_rate));\n+\t\tmemset(pfvf-\u003emqprio.max_rate, 0,\n+\t\t       num_txq * sizeof(*pfvf-\u003emqprio.max_rate));\n+\t\tpfvf-\u003emqprio.flags = 0;\n+\t\treturn 0;\n+\t}\n+\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 void otx2_mqprio_snap_free(struct otx2_nic *pfvf,\n+\t\t\t\t  struct mq_offload_snap **snap)\n+{\n+\tif (!*snap)\n+\t\treturn;\n+\n+\tdevm_kfree(pfvf-\u003edev, *snap);\n+\t*snap = NULL;\n+}\n+\n+static int otx2_mqprio_snap_copy(struct otx2_nic *pfvf,\n+\t\t\t\t struct mq_offload_snap **dst,\n+\t\t\t\t const struct tc_mqprio_qopt_offload *mqprio)\n+{\n+\tconst struct tc_mqprio_qopt *qopt = \u0026mqprio-\u003eqopt;\n+\tstruct mq_offload_snap *snap;\n+\tint tc;\n+\n+\tif (!*dst) {\n+\t\tsnap = devm_kzalloc(pfvf-\u003edev, sizeof(*snap), GFP_KERNEL);\n+\t\tif (!snap)\n+\t\t\treturn -ENOMEM;\n+\t\t*dst = snap;\n+\t} else {\n+\t\tsnap = *dst;\n+\t}\n+\n+\tsnap-\u003enum_tc = qopt-\u003enum_tc;\n+\tfor (tc = 0; tc \u003c TC_QOPT_MAX_QUEUE; tc++) {\n+\t\tsnap-\u003ecount[tc] = qopt-\u003ecount[tc];\n+\t\tsnap-\u003eoffset[tc] = qopt-\u003eoffset[tc];\n+\t\tsnap-\u003emin_rate[tc] = 0;\n+\t\tsnap-\u003emax_rate[tc] = 0;\n+\t}\n+\n+\tfor (tc = 0; tc \u003c qopt-\u003enum_tc; tc++) {\n+\t\tif (mqprio-\u003eflags \u0026 TC_MQPRIO_F_MIN_RATE)\n+\t\t\tsnap-\u003emin_rate[tc] = mqprio-\u003emin_rate[tc];\n+\t\tif (mqprio-\u003eflags \u0026 TC_MQPRIO_F_MAX_RATE)\n+\t\t\tsnap-\u003emax_rate[tc] = mqprio-\u003emax_rate[tc];\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int otx2_mqprio_stage_cur(struct otx2_nic *pfvf,\n+\t\t\t\t const struct tc_mqprio_qopt_offload *mqprio)\n+{\n+\treturn otx2_mqprio_snap_copy(pfvf, \u0026pfvf-\u003ecur_mq_snap, mqprio);\n+}\n+\n+static void otx2_mqprio_snap_commit(struct otx2_nic *pfvf)\n+{\n+\totx2_mqprio_snap_free(pfvf, \u0026pfvf-\u003eold_mq_snap);\n+\tpfvf-\u003eold_mq_snap = pfvf-\u003ecur_mq_snap;\n+\tpfvf-\u003ecur_mq_snap = NULL;\n+}\n+\n+static void otx2_mqprio_clear_replace_state(struct otx2_nic *pfvf)\n+{\n+\tpfvf-\u003emqprio.replace_setup_done = false;\n+\tpfvf-\u003emqprio.replace_graft_done = false;\n+}\n+\n+static bool otx2_mqprio_mdq_allocated(struct otx2_nic *pfvf)\n+{\n+\treturn pfvf-\u003ehw.txschq_cnt[NIX_TXSCH_LVL_MDQ] != 0;\n+}\n+\n+static int otx2_mqprio_restart_netdev(struct net_device *netdev, bool rate_limit);\n+\n+static int otx2_mqprio_restore_old(struct otx2_nic *pfvf)\n+{\n+\tstruct mq_offload_snap *snap = pfvf-\u003eold_mq_snap;\n+\tstruct net_device *netdev = pfvf-\u003enetdev;\n+\tu16 num_txq = pfvf-\u003ehw.non_qos_queues;\n+\tint tc, txq, err;\n+\n+\tif (!snap)\n+\t\treturn 0;\n+\n+\terr = otx2_mqprio_alloc_cache(pfvf, false);\n+\tif (err)\n+\t\treturn err;\n+\n+\tmemset(pfvf-\u003emqprio.min_rate, 0, num_txq * sizeof(*pfvf-\u003emqprio.min_rate));\n+\tmemset(pfvf-\u003emqprio.max_rate, 0, num_txq * sizeof(*pfvf-\u003emqprio.max_rate));\n+\tpfvf-\u003emqprio.flags = 0;\n+\n+\tfor (tc = 0; tc \u003c snap-\u003enum_tc; tc++) {\n+\t\tu64 min_rate = snap-\u003emin_rate[tc];\n+\t\tu64 max_rate = snap-\u003emax_rate[tc];\n+\n+\t\tif (min_rate)\n+\t\t\tpfvf-\u003emqprio.flags |= TC_MQPRIO_F_MIN_RATE;\n+\t\tif (max_rate)\n+\t\t\tpfvf-\u003emqprio.flags |= TC_MQPRIO_F_MAX_RATE;\n+\n+\t\tfor (txq = snap-\u003eoffset[tc];\n+\t\t     txq \u003c snap-\u003eoffset[tc] + snap-\u003ecount[tc]; txq++) {\n+\t\t\tpfvf-\u003emqprio.min_rate[txq] = min_rate;\n+\t\t\tpfvf-\u003emqprio.max_rate[txq] = max_rate;\n+\t\t}\n+\t}\n+\n+\tnetdev_set_num_tc(netdev, snap-\u003enum_tc);\n+\tfor (tc = 0; tc \u003c snap-\u003enum_tc; tc++)\n+\t\tnetdev_set_tc_queue(netdev, tc, snap-\u003ecount[tc],\n+\t\t\t\t    snap-\u003eoffset[tc]);\n+\n+\tif (otx2_mqprio_mdq_allocated(pfvf)) {\n+\t\terr = otx2_nix_tm_clear_queue_shaper(pfvf);\n+\t\tif (err)\n+\t\t\treturn err;\n+\t}\n+\n+\t/* Rebuild the TX scheduler via netdev restart when running; otx2_mqprio_up()\n+\t * alone is insufficient after a failed replace that already bounced the\n+\t * interface. If open failed, TX schedulers were freed; defer shaper restore\n+\t * to the next successful ndo_open() via otx2_mqprio_up().\n+\t */\n+\tpfvf-\u003emqprio.rate_limit = true;\n+\n+\tif (netif_running(netdev)) {\n+\t\terr = otx2_mqprio_restart_netdev(netdev, true);\n+\t\tif (err)\n+\t\t\treturn err;\n+\t} else if (pfvf-\u003ehw.txschq_cnt[NIX_TXSCH_LVL_SMQ]) {\n+\t\terr = otx2_mqprio_up(pfvf);\n+\t\tif (err)\n+\t\t\treturn err;\n+\t}\n+\n+\totx2_mqprio_snap_free(pfvf, \u0026pfvf-\u003ecur_mq_snap);\n+\n+\treturn 0;\n+}\n+\n+static void otx2_mqprio_snap_destroy(struct otx2_nic *pfvf)\n+{\n+\totx2_mqprio_snap_free(pfvf, \u0026pfvf-\u003ecur_mq_snap);\n+\totx2_mqprio_snap_free(pfvf, \u0026pfvf-\u003eold_mq_snap);\n+}\n+\n+static void otx2_mqprio_clear_sw(struct otx2_nic *pfvf)\n+{\n+\tstruct net_device *netdev = pfvf-\u003enetdev;\n+\n+\tpfvf-\u003emqprio.rate_limit = false;\n+\totx2_mqprio_clear_replace_state(pfvf);\n+\tnetdev_set_num_tc(netdev, 0);\n+\totx2_mqprio_free_cache(pfvf);\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+\tint err = 0;\n+\n+\tif (!pfvf-\u003emqprio.rate_limit)\n+\t\treturn 0;\n+\n+\tif (netif_running(pfvf-\u003enetdev) \u0026\u0026\n+\t    otx2_mqprio_mdq_allocated(pfvf))\n+\t\terr = otx2_nix_tm_clear_queue_shaper(pfvf);\n+\n+\tif (err) {\n+\t\tnetdev_err(pfvf-\u003enetdev,\n+\t\t\t   \"mqprio: failed to clear hardware shapers: %d\\n\",\n+\t\t\t   err);\n+\t\treturn err;\n+\t}\n+\n+\totx2_mqprio_clear_sw(pfvf);\n+\n+\treturn 0;\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.\n+ *\n+ * Intentional behaviour: this full stop/open cycle drops in-flight traffic\n+ * (carrier off, IRQ/NAPI teardown, queue drain).  The NIX TX scheduler must\n+ * be reallocated (e.g. one SMQ per non-QoS queue) and cannot be reprogrammed\n+ * live today, so a netdev bounce is required on every mqprio add, replace,\n+ * delete, and rollback.  Users see a brief connectivity blip; this is not a\n+ * bug to \"fix\" without implementing the live-reprogramming path noted below.\n+ * If open fails, the interface is left administratively down without calling\n+ * ndo_stop() again on resources already torn down by the open error path.\n+ *\n+ * Quiesce the transmit path like __dev_close_many() before ndo_stop() so\n+ * otx2_xmit() cannot race otx2_free_queue_mem().\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 running = netif_running(netdev);\n+\tint err;\n+\n+\t/* TODO: Explore live TX scheduler reprogramming to avoid a full\n+\t * ndo_stop()/ndo_open() bounce on every mqprio change.\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 (running) {\n+\t\tclear_bit(__LINK_STATE_START, \u0026netdev-\u003estate);\n+\t\tsmp_mb__after_atomic(); /* Commit netif_running(). */\n+\t}\n+\tdev_deactivate(netdev, true);\n+\n+\terr = ops-\u003endo_stop(netdev);\n+\tif (err) {\n+\t\tif (running) {\n+\t\t\tset_bit(__LINK_STATE_START, \u0026netdev-\u003estate);\n+\t\t\tdev_activate(netdev);\n+\t\t}\n+\t\treturn err;\n+\t}\n+\n+\t/* Set before ndo_open() so otx2_txsch_alloc() widens SMQ allocation.\n+\t * On teardown, drop mqprio software state so ndo_open() does not\n+\t * re-apply bandwidth limits via otx2_mqprio_up() after the kernel\n+\t * removed the qdisc.\n+\t */\n+\tif (rate_limit)\n+\t\tpfvf-\u003emqprio.rate_limit = true;\n+\telse\n+\t\totx2_mqprio_clear_sw(pfvf);\n+\n+\terr = ops-\u003endo_open(netdev);\n+\tif (!err \u0026\u0026 running) {\n+\t\tset_bit(__LINK_STATE_START, \u0026netdev-\u003estate);\n+\t\tdev_activate(netdev);\n+\t} else if (err) {\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\t/* ndo_open() already freed the TX schedulers on failure while\n+\t\t * netif_running() may still be true; drop mqprio software state\n+\t\t * only instead of sending shaper clears to freed queues.\n+\t\t */\n+\t\totx2_mqprio_clear_sw(pfvf);\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\tset_bit(__builtin_ctzll(OTX2_FLAG_INTF_DOWN),\n+\t\t\t(unsigned long *)\u0026pfvf-\u003eflags);\n+\t\t/* visible to otx2_stop() on other cpus */\n+\t\tsmp_wmb();\n+\t\tnetif_close(netdev);\n+\t}\n+\n+\treturn err;\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)\n+\t\treturn 0;\n+\n+\tif (qcount \u003c= 1)\n+\t\treturn 0;\n+\n+\t/* TODO: mqprio min_rate/max_rate are per traffic class, but bandwidth\n+\t * offload shapes on per-queue MDQ nodes parented under a single TL4.\n+\t * Without per-TC TL4 shapers the driver cannot honor TC-level limits\n+\t * for a traffic class that spans multiple queues without either\n+\t * dividing the rate across queues (uAPI mismatch) or exceeding the TC\n+\t * cap when every member queue is active. Reject until per-TC TL4\n+\t * shaping can be implemented without allocating additional TL4 nodes\n+\t * beyond the existing hierarchy.\n+\t */\n+\tnetdev_err(netdev,\n+\t\t   \"mqprio: %s rate for tc %d not supported with %u queues\\n\",\n+\t\t   name, tc, qcount);\n+\tNL_SET_ERR_MSG_FMT_MOD(extack,\n+\t\t\t       \"mqprio: %s rate for tc %d not supported with %u queues\",\n+\t\t\t       name, tc, qcount);\n+\treturn -EOPNOTSUPP;\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 int otx2_teardown_tc_mqprio(struct otx2_nic *pfvf,\n+\t\t\t\t   struct tc_mqprio_qopt_offload *mqprio)\n+{\n+\tstruct tc_mqprio_qopt *qopt = \u0026mqprio-\u003eqopt;\n+\tbool had_mqprio = pfvf-\u003emqprio.rate_limit;\n+\tstruct net_device *netdev = pfvf-\u003enetdev;\n+\tbool if_up = netif_running(netdev);\n+\tint err;\n+\n+\tqopt-\u003ehw = 0;\n+\n+\t/* tc qdisc replace runs setup on the new mqprio before destroying the\n+\t * old one. replace_setup_done and TC_ROOT_GRAFT distinguish stale\n+\t * old-instance teardown from graft failure after setup.\n+\t */\n+\tif (pfvf-\u003emqprio.replace_setup_done \u0026\u0026 pfvf-\u003ecur_mq_snap) {\n+\t\terr = 0;\n+\t\tif (pfvf-\u003emqprio.replace_graft_done)\n+\t\t\totx2_mqprio_snap_commit(pfvf);\n+\t\telse\n+\t\t\terr = otx2_mqprio_restore_old(pfvf);\n+\t\totx2_mqprio_clear_replace_state(pfvf);\n+\t\treturn err;\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+\tif (if_up) {\n+\t\tint down_err, err;\n+\n+\t\tdown_err = otx2_mqprio_down(pfvf);\n+\t\tif (down_err)\n+\t\t\treturn down_err;\n+\t\terr = otx2_mqprio_restart_netdev(netdev, false);\n+\t\tif (err)\n+\t\t\treturn err;\n+\t\treturn down_err;\n+\t}\n+\n+\t/* ndo_stop() already freed the TX scheduler TL nodes; drop software\n+\t * state only.\n+\t */\n+\totx2_mqprio_clear_sw(pfvf);\n+\treturn 0;\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 netlink_ext_ack *extack = mqprio-\u003eextack;\n+\tstruct otx2_nic *pfvf = netdev_priv(netdev);\n+\tstruct tc_mqprio_qopt *qopt = \u0026mqprio-\u003eqopt;\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 (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+\tif (!list_empty(\u0026pfvf-\u003eqos.qos_tree)) {\n+\t\tnetdev_err(netdev,\n+\t\t\t   \"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+\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(min_rate)) {\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(max_rate)) {\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_stage_cur(pfvf, mqprio);\n+\tif (err)\n+\t\treturn err;\n+\n+\terr = otx2_mqprio_restart_netdev(pfvf-\u003enetdev, true);\n+\tif (err)\n+\t\tgoto cleanup;\n+\n+\terr = otx2_mqprio_alloc_cache(pfvf, replacing);\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_mqprio_mdq_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(). Multi-queue TCs with rates\n+\t\t * are rejected above.\n+\t\t */\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\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, pfvf-\u003ecur_mq_snap-\u003enum_tc);\n+\tfor (i = 0; i \u003c pfvf-\u003ecur_mq_snap-\u003enum_tc; i++)\n+\t\tnetdev_set_tc_queue(netdev, i, pfvf-\u003ecur_mq_snap-\u003ecount[i],\n+\t\t\t\t    qopt-\u003eoffset[i]);\n+\n+\tqopt-\u003ehw = TC_MQPRIO_HW_OFFLOAD_TCS;\n+\n+\tif (replacing) {\n+\t\tpfvf-\u003emqprio.replace_setup_done = true;\n+\t\tpfvf-\u003emqprio.replace_graft_done = false;\n+\t} else {\n+\t\totx2_mqprio_snap_commit(pfvf);\n+\t}\n+\n+\treturn 0;\n+\n+cleanup:\n+\tqopt-\u003ehw = 0;\n+\tif (replacing) {\n+\t\tint restore_err = otx2_mqprio_restore_old(pfvf);\n+\n+\t\totx2_mqprio_clear_replace_state(pfvf);\n+\t\tif (restore_err) {\n+\t\t\tnetdev_err(netdev,\n+\t\t\t\t   \"mqprio: replace failed and prior configuration rollback failed: %d\\n\",\n+\t\t\t\t   restore_err);\n+\t\t\tif (extack)\n+\t\t\t\tNL_SET_ERR_MSG_FMT_MOD(extack,\n+\t\t\t\t\t\t       \"mqprio: replace failed and prior configuration rollback failed: %d\",\n+\t\t\t\t\t\t       restore_err);\n+\t\t} else {\n+\t\t\tnetdev_err(netdev,\n+\t\t\t\t   \"mqprio: replace failed; prior configuration restored\\n\");\n+\t\t\tif (extack)\n+\t\t\t\tNL_SET_ERR_MSG_MOD(extack,\n+\t\t\t\t\t\t   \"mqprio: replace failed; prior configuration restored\");\n+\t\t}\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 (pfvf-\u003emqprio.replace_setup_done)\n+\t\t\tpfvf-\u003emqprio.replace_graft_done = true;\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}\n@@ -1632,6 +2379,7 @@ EXPORT_SYMBOL(otx2_init_tc);\n void otx2_shutdown_tc(struct otx2_nic *nic)\n {\n \totx2_destroy_tc_flow_list(nic);\n+\totx2_mqprio_snap_destroy(nic);\n }\n EXPORT_SYMBOL(otx2_shutdown_tc);\n \ndiff --git a/drivers/net/ethernet/marvell/octeontx2/nic/qos.c b/drivers/net/ethernet/marvell/octeontx2/nic/qos.c\nindex 69c0911e28e91..c0bb7b932056a 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@@ -1038,6 +1041,14 @@ static int otx2_qos_root_add(struct otx2_nic *pfvf, u16 htb_maj_id, u16 htb_defc\n \t\t   \"TC_HTB_CREATE: handle=0x%x defcls=0x%x\\n\",\n \t\t   htb_maj_id, htb_defcls);\n \n+\tif (pfvf-\u003emqprio.rate_limit) {\n+\t\tnetdev_err(pfvf-\u003enetdev,\n+\t\t\t   \"HTB: cannot enable while mqprio bandwidth offload is active\\n\");\n+\t\tNL_SET_ERR_MSG_MOD(extack,\n+\t\t\t\t   \"HTB: cannot enable while mqprio bandwidth offload is active\");\n+\t\treturn -EOPNOTSUPP;\n+\t}\n+\n \troot = otx2_qos_alloc_root(pfvf);\n \tif (IS_ERR(root)) {\n \t\terr = PTR_ERR(root);\n"
}

2/1 2026/09/08 07:05 agent patch-evaluator
0m Model:
core

Args:
null

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch modifies only the Marvell OcteonTX2 network driver (drivers/net/ethernet/marvell/octeontx2/), adding support for mqprio hardware offload rate limiting and related queue scheduler adjustments. This driver targets physical Marvell/Cavium OcteonTX2 hardware / SmartNICs which are not emulated in standard virtualized fuzzing environments (QEMU/GCE), making the modified code structurally unreachable.",
  "WorthFuzzing": false
}

Instruction:
You are an expert Linux kernel maintainer and security engineer.
Your job is to review a provided patch series and evaluate whether it warrants fuzzing with syzkaller.

IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on internal assumptions. You must actively use your code access
tools to inspect the actual source code, callers, and surrounding context.

================================================================================
1. CORE TRIAGE PHILOSOPHY
================================================================================
The goal of patch fuzzing is to discover crashes, regressions, exposed latent bugs,
and newly triggered assertions introduced by the patch series.

- REACHABILITY IS THE PRIMARY GATE:
  Fuzzing can only discover bugs in code that can actually execute in standard virtualized
  environments (GCE or QEMU, utilizing software-emulated devices like USB gadgets, netdev, tun/tap).
  If the modified code is structurally unreachable (see Section 2), it MUST NOT be fuzzed,
  regardless of whether it adds assertions or complex logic.

- DO NOT BLINDLY TRUST "NO FUNCTIONAL CHANGE" (NFCI) OR "REFACTORING" CLAIMS:
  Patch authors routinely label changes as "cleanups", "refactorings", or state
  "No functional change intended". Do NOT take these claims at face value.
  Code refactorings that rearrange logic, introduce helper functions, or alter state management
  in core subsystems frequently introduce subtle semantic shifts or uncover latent kernel bugs.
  If reachable executable code is modified or refactored, it MUST be fuzzed.

- NEW OR MODIFIED ASSERTIONS IN REACHABLE CODE MUST BE FUZZED:
  When a patch introduces or modifies runtime checks or assertions (e.g., WARN_ON*, VM_WARN_ON*,
  BUG_ON*, lockdep_assert*) in reachable code paths, it enforces new or stricter invariants.
  Even if the author believes the invariant always holds, fuzzing is essential to verify whether
  an unusual sequence of operations can violate it.

================================================================================
2. WHEN TO RETURN WorthFuzzing=false (NEGATIVE CRITERIA)
================================================================================
Return WorthFuzzing=false ONLY IF all modified code falls strictly into one or more of these categories:

- Non-kernel and non-executable changes:
  * Modifications to Documentation/, comments, or spelling fixes.
  * User-space directories, self-tests, samples, or scripts (e.g., tools/, samples/, scripts/, usr/)
    that do not affect the compiled kernel image (vmlinux) or kernel modules.
  * Purely decorative logging (e.g., message strings in pr_err, printk, dev_info) or tracepoints
    that do not alter control flow or data structures.
  * Build system or Kconfig changes that do not alter compiled C logic.
- Structurally unreachable hardware:
  * Vendor-specific PCIe switches, SmartNICs, or GPU drivers (e.g., mlxsw, pds_core, qed,
    ionic, amdgpu) requiring physical ASIC/PCIe cards not emulated in standard QEMU.
- Unreachable execution paths:
  * Driver teardown callbacks (.remove, .shutdown, pci_unregister_driver) executed only during
    physical PCI hot-unplug or manual sysfs driver unbinding.
  * Code paths exclusive to architectures other than the target architecture.

================================================================================
3. WHEN TO RETURN WorthFuzzing=true (POSITIVE CRITERIA)
================================================================================
Return WorthFuzzing=true whenever the patch touches reachable executable code, including:
- Core Subsystems:
  * Any logic modifications in memory management (mm/), synchronization/locking (kernel/locking/),
    BPF, scheduler, core networking, VFS, or syscall handling.
- Refactorings and Code Cleanups:
  * Any restructuring of reachable data structures, helper abstractions, or algorithm flows.
- Runtime Assertions and Defensive Checks:
  * Any introduction or alteration of assertions (WARN_ON*, VM_WARN_ON*, BUG_ON*, etc.) in reachable paths.
- Reachable Drivers and Protocols:
  * Drivers accessible via virtual buses (virtio, USB gadget, loopback, netlink, binder, sockets, etc.).

================================================================================
4. EXTRACTING FocusSymbols (PREVENTING DILUTION)
================================================================================
When WorthFuzzing=true, you must extract specific kernel functions into FocusSymbols to guide the fuzzer:

- AVOID UBIQUITOUS LIFECYCLE HOT-PATHS:
  Do NOT list generic, ubiquitous functions called by almost every program in the corpus
  (including, but not limited to: general memory allocators and deallocators, page fault
  and trap handlers, or core synchronization primitives; this is not an exhaustive list).
  Listing ubiquitous functions causes the fuzzer to classify thousands of unrelated tests as "focused",
  which severely dilutes fuzzing effort away from the actual changes.

- TARGET SPECIFIC FEATURE LOGIC AND ENTRYPOINTS:
  List functions that specifically implement the logic being added or altered, or direct API entrypoints
  for the subsystem feature under review.

- HANDLING STATIC INLINE FUNCTIONS IN HEADERS (.h):
  Compiler-inlined static functions (such as static inlines in mm/*.h or include/linux/*.h) lack
  distinct symbol addresses in vmlinux and cannot be targeted directly by symbol coverage filters.
  If the changes are primarily in static inline helpers, identify non-static, feature-specific caller
  functions in .c files that exercise them (avoiding ubiquitous lifecycle wrappers).

================================================================================
5. IDENTIFYING EnableConfigs
================================================================================
Identify any specific CONFIG_ options required to properly compile and reach the modified code:
- Inspect Kconfig files and #ifdef guards; do not make assumptions.
- Check "depends on" lines in Kconfig to include any non-standard parent subsystem configs needed.
- Strip any '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:
Target architecture: amd64

For your convenience, here is the diff of the changes:
commit bc2c960012a1d9e4a89a3352fd3f0b3c5788d661
Author: syz-cluster <triage@syzkaller.com>
Date:   Tue Sep 8 07:05:22 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 153eb57bad067..c9a4326d725d8 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 175992188c18a..a519e0a97a2b7 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c
@@ -615,6 +615,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];
@@ -651,7 +787,11 @@ 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)
+			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++;
@@ -779,6 +919,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;
@@ -844,6 +987,7 @@ void otx2_txschq_stop(struct otx2_nic *pfvf)
 
 	/* Clear the txschq list */
 	for (lvl = 0; lvl < NIX_TXSCH_LVL_CNT; lvl++) {
+		pfvf->hw.txschq_cnt[lvl] = 0;
 		for (schq = 0; schq < MAX_TXSCHQ_PER_FUNC; schq++)
 			pfvf->hw.txschq_list[lvl][schq] = 0;
 	}
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h
index eecee612b7b2c..ede7f1113b714 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h
@@ -17,6 +17,7 @@
 #include <linux/soc/marvell/silicons.h>
 #include <linux/soc/marvell/octeontx2/asm.h>
 #include <net/macsec.h>
+#include <uapi/linux/pkt_sched.h>
 #include <net/pkt_cls.h>
 #include <net/devlink.h>
 #include <linux/time64.h>
@@ -483,6 +484,23 @@ struct pf_irq_data {
 	int mdevs;
 };
 
+struct mq_offload_snap {
+	u64 min_rate[TC_QOPT_MAX_QUEUE];
+	u64 max_rate[TC_QOPT_MAX_QUEUE];
+	__u8 num_tc;
+	__u16 count[TC_QOPT_MAX_QUEUE];
+	__u16 offset[TC_QOPT_MAX_QUEUE];
+};
+
+struct otx2_mqprio {
+	u32	flags;
+	u64	*min_rate;
+	u64	*max_rate;
+	bool	rate_limit;
+	bool	replace_setup_done;
+	bool	replace_graft_done;
+};
+
 struct otx2_nic {
 	void __iomem		*reg_base;
 	struct net_device	*netdev;
@@ -515,6 +533,10 @@ struct otx2_nic {
 	u64			flags;
 	u64			*cq_op_addr;
 
+	struct otx2_mqprio	mqprio;
+	struct mq_offload_snap	*cur_mq_snap;
+	struct mq_offload_snap	*old_mq_snap;
+
 	struct bpf_prog		*xdp_prog;
 	struct otx2_qset	qset;
 	struct otx2_hw		hw;
@@ -1246,6 +1268,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 9bee1b91eeaa6..ec2601c6c255a 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 c0e2100de1d93..68ad2f3d22277 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c
@@ -907,11 +907,13 @@ static int otx2_mbox_up_handler_rep_event_up_notify(struct otx2_nic *pf,
 
 	if (info->event == RVU_EVENT_PORT_STATE) {
 		if (info->evt_data.port_state) {
-			pf->flags |= OTX2_FLAG_PORT_UP;
+			set_bit(__builtin_ctzll(OTX2_FLAG_PORT_UP),
+				(unsigned long *)&pf->flags);
 			netif_carrier_on(netdev);
 			netif_tx_start_all_queues(netdev);
 		} else {
-			pf->flags &= ~OTX2_FLAG_PORT_UP;
+			clear_bit(__builtin_ctzll(OTX2_FLAG_PORT_UP),
+				  (unsigned long *)&pf->flags);
 			netif_tx_stop_all_queues(netdev);
 			netif_carrier_off(netdev);
 		}
@@ -2007,6 +2009,14 @@ int otx2_open(struct net_device *netdev)
 	if (err)
 		goto err_free_mem;
 
+	err = otx2_mqprio_up(pf);
+	if (err) {
+		netdev_err(pf->netdev,
+			   "mqprio: failed to restore shapers during open: %d\n",
+			   err);
+		goto err_free_hw;
+	}
+
 	/* Register NAPI handler */
 	for (qidx = 0; qidx < pf->hw.cint_cnt; qidx++) {
 		cq_poll = &qset->napi[qidx];
@@ -2205,6 +2215,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);
@@ -2226,7 +2237,8 @@ int otx2_stop(struct net_device *netdev)
 	netif_carrier_off(netdev);
 	netif_tx_stop_all_queues(netdev);
 
-	pf->flags |= OTX2_FLAG_INTF_DOWN;
+	set_bit(__builtin_ctzll(OTX2_FLAG_INTF_DOWN),
+		(unsigned long *)&pf->flags);
 	/* 'intf_down' may be checked on any cpu */
 	smp_wmb();
 
@@ -2280,6 +2292,7 @@ int otx2_stop(struct net_device *netdev)
 	for (qidx = 0; qidx < netdev->num_tx_queues; qidx++)
 		netdev_tx_reset_queue(netdev_get_tx_queue(netdev, qidx));
 
+	synchronize_net();
 	otx2_free_queue_mem(qset);
 	/* Do not clear RQ/SQ ringsize settings */
 	memset_startat(qset, 0, sqe_cnt);
@@ -2923,6 +2936,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..045b4573ce14e 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,752 @@ 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, bool replacing)
+{
+	u16 num_txq = pfvf->hw.non_qos_queues;
+
+	if (replacing && pfvf->mqprio.min_rate && pfvf->mqprio.max_rate) {
+		memset(pfvf->mqprio.min_rate, 0,
+		       num_txq * sizeof(*pfvf->mqprio.min_rate));
+		memset(pfvf->mqprio.max_rate, 0,
+		       num_txq * sizeof(*pfvf->mqprio.max_rate));
+		pfvf->mqprio.flags = 0;
+		return 0;
+	}
+
+	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 void otx2_mqprio_snap_free(struct otx2_nic *pfvf,
+				  struct mq_offload_snap **snap)
+{
+	if (!*snap)
+		return;
+
+	devm_kfree(pfvf->dev, *snap);
+	*snap = NULL;
+}
+
+static int otx2_mqprio_snap_copy(struct otx2_nic *pfvf,
+				 struct mq_offload_snap **dst,
+				 const struct tc_mqprio_qopt_offload *mqprio)
+{
+	const struct tc_mqprio_qopt *qopt = &mqprio->qopt;
+	struct mq_offload_snap *snap;
+	int tc;
+
+	if (!*dst) {
+		snap = devm_kzalloc(pfvf->dev, sizeof(*snap), GFP_KERNEL);
+		if (!snap)
+			return -ENOMEM;
+		*dst = snap;
+	} else {
+		snap = *dst;
+	}
+
+	snap->num_tc = qopt->num_tc;
+	for (tc = 0; tc < TC_QOPT_MAX_QUEUE; tc++) {
+		snap->count[tc] = qopt->count[tc];
+		snap->offset[tc] = qopt->offset[tc];
+		snap->min_rate[tc] = 0;
+		snap->max_rate[tc] = 0;
+	}
+
+	for (tc = 0; tc < qopt->num_tc; tc++) {
+		if (mqprio->flags & TC_MQPRIO_F_MIN_RATE)
+			snap->min_rate[tc] = mqprio->min_rate[tc];
+		if (mqprio->flags & TC_MQPRIO_F_MAX_RATE)
+			snap->max_rate[tc] = mqprio->max_rate[tc];
+	}
+
+	return 0;
+}
+
+static int otx2_mqprio_stage_cur(struct otx2_nic *pfvf,
+				 const struct tc_mqprio_qopt_offload *mqprio)
+{
+	return otx2_mqprio_snap_copy(pfvf, &pfvf->cur_mq_snap, mqprio);
+}
+
+static void otx2_mqprio_snap_commit(struct otx2_nic *pfvf)
+{
+	otx2_mqprio_snap_free(pfvf, &pfvf->old_mq_snap);
+	pfvf->old_mq_snap = pfvf->cur_mq_snap;
+	pfvf->cur_mq_snap = NULL;
+}
+
+static void otx2_mqprio_clear_replace_state(struct otx2_nic *pfvf)
+{
+	pfvf->mqprio.replace_setup_done = false;
+	pfvf->mqprio.replace_graft_done = false;
+}
+
+static bool otx2_mqprio_mdq_allocated(struct otx2_nic *pfvf)
+{
+	return pfvf->hw.txschq_cnt[NIX_TXSCH_LVL_MDQ] != 0;
+}
+
+static int otx2_mqprio_restart_netdev(struct net_device *netdev, bool rate_limit);
+
+static int otx2_mqprio_restore_old(struct otx2_nic *pfvf)
+{
+	struct mq_offload_snap *snap = pfvf->old_mq_snap;
+	struct net_device *netdev = pfvf->netdev;
+	u16 num_txq = pfvf->hw.non_qos_queues;
+	int tc, txq, err;
+
+	if (!snap)
+		return 0;
+
+	err = otx2_mqprio_alloc_cache(pfvf, false);
+	if (err)
+		return err;
+
+	memset(pfvf->mqprio.min_rate, 0, num_txq * sizeof(*pfvf->mqprio.min_rate));
+	memset(pfvf->mqprio.max_rate, 0, num_txq * sizeof(*pfvf->mqprio.max_rate));
+	pfvf->mqprio.flags = 0;
+
+	for (tc = 0; tc < snap->num_tc; tc++) {
+		u64 min_rate = snap->min_rate[tc];
+		u64 max_rate = snap->max_rate[tc];
+
+		if (min_rate)
+			pfvf->mqprio.flags |= TC_MQPRIO_F_MIN_RATE;
+		if (max_rate)
+			pfvf->mqprio.flags |= TC_MQPRIO_F_MAX_RATE;
+
+		for (txq = snap->offset[tc];
+		     txq < snap->offset[tc] + snap->count[tc]; txq++) {
+			pfvf->mqprio.min_rate[txq] = min_rate;
+			pfvf->mqprio.max_rate[txq] = max_rate;
+		}
+	}
+
+	netdev_set_num_tc(netdev, snap->num_tc);
+	for (tc = 0; tc < snap->num_tc; tc++)
+		netdev_set_tc_queue(netdev, tc, snap->count[tc],
+				    snap->offset[tc]);
+
+	if (otx2_mqprio_mdq_allocated(pfvf)) {
+		err = otx2_nix_tm_clear_queue_shaper(pfvf);
+		if (err)
+			return err;
+	}
+
+	/* Rebuild the TX scheduler via netdev restart when running; otx2_mqprio_up()
+	 * alone is insufficient after a failed replace that already bounced the
+	 * interface. If open failed, TX schedulers were freed; defer shaper restore
+	 * to the next successful ndo_open() via otx2_mqprio_up().
+	 */
+	pfvf->mqprio.rate_limit = true;
+
+	if (netif_running(netdev)) {
+		err = otx2_mqprio_restart_netdev(netdev, true);
+		if (err)
+			return err;
+	} else if (pfvf->hw.txschq_cnt[NIX_TXSCH_LVL_SMQ]) {
+		err = otx2_mqprio_up(pfvf);
+		if (err)
+			return err;
+	}
+
+	otx2_mqprio_snap_free(pfvf, &pfvf->cur_mq_snap);
+
+	return 0;
+}
+
+static void otx2_mqprio_snap_destroy(struct otx2_nic *pfvf)
+{
+	otx2_mqprio_snap_free(pfvf, &pfvf->cur_mq_snap);
+	otx2_mqprio_snap_free(pfvf, &pfvf->old_mq_snap);
+}
+
+static void otx2_mqprio_clear_sw(struct otx2_nic *pfvf)
+{
+	struct net_device *netdev = pfvf->netdev;
+
+	pfvf->mqprio.rate_limit = false;
+	otx2_mqprio_clear_replace_state(pfvf);
+	netdev_set_num_tc(netdev, 0);
+	otx2_mqprio_free_cache(pfvf);
+}
+
+/* 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)
+{
+	int err = 0;
+
+	if (!pfvf->mqprio.rate_limit)
+		return 0;
+
+	if (netif_running(pfvf->netdev) &&
+	    otx2_mqprio_mdq_allocated(pfvf))
+		err = otx2_nix_tm_clear_queue_shaper(pfvf);
+
+	if (err) {
+		netdev_err(pfvf->netdev,
+			   "mqprio: failed to clear hardware shapers: %d\n",
+			   err);
+		return err;
+	}
+
+	otx2_mqprio_clear_sw(pfvf);
+
+	return 0;
+}
+
+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.
+ *
+ * Intentional behaviour: this full stop/open cycle drops in-flight traffic
+ * (carrier off, IRQ/NAPI teardown, queue drain).  The NIX TX scheduler must
+ * be reallocated (e.g. one SMQ per non-QoS queue) and cannot be reprogrammed
+ * live today, so a netdev bounce is required on every mqprio add, replace,
+ * delete, and rollback.  Users see a brief connectivity blip; this is not a
+ * bug to "fix" without implementing the live-reprogramming path noted below.
+ * If open fails, the interface is left administratively down without calling
+ * ndo_stop() again on resources already torn down by the open error path.
+ *
+ * Quiesce the transmit path like __dev_close_many() before ndo_stop() so
+ * otx2_xmit() cannot race otx2_free_queue_mem().
+ */
+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 running = netif_running(netdev);
+	int err;
+
+	/* TODO: Explore live TX scheduler reprogramming to avoid a full
+	 * ndo_stop()/ndo_open() bounce on every mqprio change.
+	 */
+	netdev_info(netdev,
+		    "mqprio: restarting interface to reprogram TX scheduler; in-flight traffic will be dropped\n");
+
+	if (running) {
+		clear_bit(__LINK_STATE_START, &netdev->state);
+		smp_mb__after_atomic(); /* Commit netif_running(). */
+	}
+	dev_deactivate(netdev, true);
+
+	err = ops->ndo_stop(netdev);
+	if (err) {
+		if (running) {
+			set_bit(__LINK_STATE_START, &netdev->state);
+			dev_activate(netdev);
+		}
+		return err;
+	}
+
+	/* Set before ndo_open() so otx2_txsch_alloc() widens SMQ allocation.
+	 * On teardown, drop mqprio software state so ndo_open() does not
+	 * re-apply bandwidth limits via otx2_mqprio_up() after the kernel
+	 * removed the qdisc.
+	 */
+	if (rate_limit)
+		pfvf->mqprio.rate_limit = true;
+	else
+		otx2_mqprio_clear_sw(pfvf);
+
+	err = ops->ndo_open(netdev);
+	if (!err && running) {
+		set_bit(__LINK_STATE_START, &netdev->state);
+		dev_activate(netdev);
+	} else if (err) {
+		netdev_err(netdev,
+			   "Failed to restart device after mqprio change: %d\n",
+			   err);
+		/* ndo_open() already freed the TX schedulers on failure while
+		 * netif_running() may still be true; drop mqprio software state
+		 * only instead of sending shaper clears to freed queues.
+		 */
+		otx2_mqprio_clear_sw(pfvf);
+		/* 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.
+		 */
+		set_bit(__builtin_ctzll(OTX2_FLAG_INTF_DOWN),
+			(unsigned long *)&pfvf->flags);
+		/* visible to otx2_stop() on other cpus */
+		smp_wmb();
+		netif_close(netdev);
+	}
+
+	return err;
+}
+
+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)
+		return 0;
+
+	if (qcount <= 1)
+		return 0;
+
+	/* TODO: mqprio min_rate/max_rate are per traffic class, but bandwidth
+	 * offload shapes on per-queue MDQ nodes parented under a single TL4.
+	 * Without per-TC TL4 shapers the driver cannot honor TC-level limits
+	 * for a traffic class that spans multiple queues without either
+	 * dividing the rate across queues (uAPI mismatch) or exceeding the TC
+	 * cap when every member queue is active. Reject until per-TC TL4
+	 * shaping can be implemented without allocating additional TL4 nodes
+	 * beyond the existing hierarchy.
+	 */
+	netdev_err(netdev,
+		   "mqprio: %s rate for tc %d not supported with %u queues\n",
+		   name, tc, qcount);
+	NL_SET_ERR_MSG_FMT_MOD(extack,
+			       "mqprio: %s rate for tc %d not supported with %u queues",
+			       name, tc, qcount);
+	return -EOPNOTSUPP;
+}
+
+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 int otx2_teardown_tc_mqprio(struct otx2_nic *pfvf,
+				   struct tc_mqprio_qopt_offload *mqprio)
+{
+	struct tc_mqprio_qopt *qopt = &mqprio->qopt;
+	bool had_mqprio = pfvf->mqprio.rate_limit;
+	struct net_device *netdev = pfvf->netdev;
+	bool if_up = netif_running(netdev);
+	int err;
+
+	qopt->hw = 0;
+
+	/* tc qdisc replace runs setup on the new mqprio before destroying the
+	 * old one. replace_setup_done and TC_ROOT_GRAFT distinguish stale
+	 * old-instance teardown from graft failure after setup.
+	 */
+	if (pfvf->mqprio.replace_setup_done && pfvf->cur_mq_snap) {
+		err = 0;
+		if (pfvf->mqprio.replace_graft_done)
+			otx2_mqprio_snap_commit(pfvf);
+		else
+			err = otx2_mqprio_restore_old(pfvf);
+		otx2_mqprio_clear_replace_state(pfvf);
+		return err;
+	}
+
+	/* Skip the netdev restart when mqprio offload was not active. */
+	if (!had_mqprio)
+		return 0;
+
+	if (if_up) {
+		int down_err, err;
+
+		down_err = otx2_mqprio_down(pfvf);
+		if (down_err)
+			return down_err;
+		err = otx2_mqprio_restart_netdev(netdev, false);
+		if (err)
+			return err;
+		return down_err;
+	}
+
+	/* ndo_stop() already freed the TX scheduler TL nodes; drop software
+	 * state only.
+	 */
+	otx2_mqprio_clear_sw(pfvf);
+	return 0;
+}
+
+static int otx2_setup_tc_mqprio(struct net_device *netdev,
+				struct tc_mqprio_qopt_offload *mqprio)
+{
+	struct netlink_ext_ack *extack = mqprio->extack;
+	struct otx2_nic *pfvf = netdev_priv(netdev);
+	struct tc_mqprio_qopt *qopt = &mqprio->qopt;
+	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 (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;
+	}
+
+	if (!list_empty(&pfvf->qos.qos_tree)) {
+		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;
+	}
+
+	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(min_rate)) {
+			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(max_rate)) {
+			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_stage_cur(pfvf, mqprio);
+	if (err)
+		return err;
+
+	err = otx2_mqprio_restart_netdev(pfvf->netdev, true);
+	if (err)
+		goto cleanup;
+
+	err = otx2_mqprio_alloc_cache(pfvf, replacing);
+	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_mqprio_mdq_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(). Multi-queue TCs with rates
+		 * are rejected above.
+		 */
+		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];
+
+		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, pfvf->cur_mq_snap->num_tc);
+	for (i = 0; i < pfvf->cur_mq_snap->num_tc; i++)
+		netdev_set_tc_queue(netdev, i, pfvf->cur_mq_snap->count[i],
+				    qopt->offset[i]);
+
+	qopt->hw = TC_MQPRIO_HW_OFFLOAD_TCS;
+
+	if (replacing) {
+		pfvf->mqprio.replace_setup_done = true;
+		pfvf->mqprio.replace_graft_done = false;
+	} else {
+		otx2_mqprio_snap_commit(pfvf);
+	}
+
+	return 0;
+
+cleanup:
+	qopt->hw = 0;
+	if (replacing) {
+		int restore_err = otx2_mqprio_restore_old(pfvf);
+
+		otx2_mqprio_clear_replace_state(pfvf);
+		if (restore_err) {
+			netdev_err(netdev,
+				   "mqprio: replace failed and prior configuration rollback failed: %d\n",
+				   restore_err);
+			if (extack)
+				NL_SET_ERR_MSG_FMT_MOD(extack,
+						       "mqprio: replace failed and prior configuration rollback failed: %d",
+						       restore_err);
+		} else {
+			netdev_err(netdev,
+				   "mqprio: replace failed; prior configuration restored\n");
+			if (extack)
+				NL_SET_ERR_MSG_MOD(extack,
+						   "mqprio: replace failed; prior configuration restored");
+		}
+		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 (pfvf->mqprio.replace_setup_done)
+			pfvf->mqprio.replace_graft_done = true;
+		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;
 	}
@@ -1632,6 +2379,7 @@ EXPORT_SYMBOL(otx2_init_tc);
 void otx2_shutdown_tc(struct otx2_nic *nic)
 {
 	otx2_destroy_tc_flow_list(nic);
+	otx2_mqprio_snap_destroy(nic);
 }
 EXPORT_SYMBOL(otx2_shutdown_tc);
 
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/qos.c b/drivers/net/ethernet/marvell/octeontx2/nic/qos.c
index 69c0911e28e91..c0bb7b932056a 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)++;
@@ -1038,6 +1041,14 @@ static int otx2_qos_root_add(struct otx2_nic *pfvf, u16 htb_maj_id, u16 htb_defc
 		   "TC_HTB_CREATE: handle=0x%x defcls=0x%x\n",
 		   htb_maj_id, htb_defcls);
 
+	if (pfvf->mqprio.rate_limit) {
+		netdev_err(pfvf->netdev,
+			   "HTB: cannot enable while mqprio bandwidth offload is active\n");
+		NL_SET_ERR_MSG_MOD(extack,
+				   "HTB: cannot enable while mqprio bandwidth offload is active");
+		return -EOPNOTSUPP;
+	}
+
 	root = otx2_qos_alloc_root(pfvf);
 	if (IS_ERR(root)) {
 		err = PTR_ERR(root);

3/2 2026/09/08 07:05 llm patch-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 18996
							output: 40
							thoughts: 350
						

Thoughts:
**Analyzing the Patch for Fuzzing Potential**

Okay, let's break this down. My initial assessment hinges on whether this patch presents a reasonable target for fuzzing based on our established guidelines. First, I need to understand the scope of the modifications. It appears this patch touches a significant chunk of the Marvell OcteonTX2 NIC driver, specifically within the `drivers/net/ethernet/marvell/octeontx2` directory. I see changes in `rvu_nix.c`, `otx2_common.c/h`, `otx2_dcbnl.c`, `otx2_ethtool.c`, `otx2_pf.c`, `otx2_tc.c`, and `qos.c`. This is a pretty comprehensive update to several key modules.

Next, I need to evaluate the driver and hardware reachability. The Marvell OcteonTX2 NIC driver is obviously the target (`drivers/net/ethernet/marvell/octeontx2`). Now, a critical question arises: is this driver tied to specific physical hardware, like a SmartNIC or a vendor-specific PCIe device, or does it also support a standard virtualized environment like QEMU? Knowing this will significantly impact our fuzzing approach. Physical hardware often introduces complexities related to accessibility and reproducibility, whereas virtualization provides a more controlled and potentially simpler testing environment.

I need to confirm if I can test this within an easily fuzzable setup. I'll need to check the Kconfig files for Marvell OcteonTX2 to understand the configuration options and potential build dependencies. This should provide some insight into the hardware requirements and virtual machine support.



4/2 2026/09/08 07:05 tool grepper
0m Args:
{
  "Expression": "OCTEONTX2",
  "PathPrefix": "drivers/net/ethernet/marvell/octeontx2"
}

Results:
{
  "Output": "drivers/net/ethernet/marvell/octeontx2/Kconfig-5-\ndrivers/net/ethernet/marvell/octeontx2/Kconfig:6:config OCTEONTX2_MBOX\ndrivers/net/ethernet/marvell/octeontx2/Kconfig-7-\ttristate\ndrivers/net/ethernet/marvell/octeontx2/Kconfig-8-\ndrivers/net/ethernet/marvell/octeontx2/Kconfig:9:config OCTEONTX2_AF\ndrivers/net/ethernet/marvell/octeontx2/Kconfig-10-\ttristate \"Marvell OcteonTX2 RVU Admin Function driver\"\ndrivers/net/ethernet/marvell/octeontx2/Kconfig:11:\tselect OCTEONTX2_MBOX\ndrivers/net/ethernet/marvell/octeontx2/Kconfig-12-\tselect NET_DEVLINK\n--\ndrivers/net/ethernet/marvell/octeontx2/Kconfig=22=config NDC_DIS_DYNAMIC_CACHING\ndrivers/net/ethernet/marvell/octeontx2/Kconfig-23-\tbool \"Disable caching of dynamic entries in NDC\"\ndrivers/net/ethernet/marvell/octeontx2/Kconfig:24:\tdepends on OCTEONTX2_AF\ndrivers/net/ethernet/marvell/octeontx2/Kconfig-25-\tdefault n\n--\ndrivers/net/ethernet/marvell/octeontx2/Kconfig-30-\ndrivers/net/ethernet/marvell/octeontx2/Kconfig:31:config OCTEONTX2_PF\ndrivers/net/ethernet/marvell/octeontx2/Kconfig-32-\ttristate \"Marvell OcteonTX2 NIC Physical Function driver\"\ndrivers/net/ethernet/marvell/octeontx2/Kconfig:33:\tselect OCTEONTX2_MBOX\ndrivers/net/ethernet/marvell/octeontx2/Kconfig-34-\tselect NET_DEVLINK\n--\ndrivers/net/ethernet/marvell/octeontx2/Kconfig-44-\ndrivers/net/ethernet/marvell/octeontx2/Kconfig:45:config OCTEONTX2_VF\ndrivers/net/ethernet/marvell/octeontx2/Kconfig-46-\ttristate \"Marvell OcteonTX2 NIC Virtual Function driver\"\ndrivers/net/ethernet/marvell/octeontx2/Kconfig:47:\tdepends on OCTEONTX2_PF\ndrivers/net/ethernet/marvell/octeontx2/Kconfig-48-\thelp\n--\ndrivers/net/ethernet/marvell/octeontx2/Kconfig=51=config RVU_ESWITCH\ndrivers/net/ethernet/marvell/octeontx2/Kconfig-52-\ttristate \"Marvell RVU E-Switch support\"\ndrivers/net/ethernet/marvell/octeontx2/Kconfig:53:\tdepends on OCTEONTX2_PF\ndrivers/net/ethernet/marvell/octeontx2/Kconfig-54-\tdefault m\n--\ndrivers/net/ethernet/marvell/octeontx2/Makefile-5-\ndrivers/net/ethernet/marvell/octeontx2/Makefile:6:obj-$(CONFIG_OCTEONTX2_MBOX) += af/\ndrivers/net/ethernet/marvell/octeontx2/Makefile:7:obj-$(CONFIG_OCTEONTX2_AF) += af/\ndrivers/net/ethernet/marvell/octeontx2/Makefile:8:obj-$(CONFIG_OCTEONTX2_PF) += nic/\n--\ndrivers/net/ethernet/marvell/octeontx2/af/Makefile=6=ccflags-y += -I$(src)\ndrivers/net/ethernet/marvell/octeontx2/af/Makefile:7:obj-$(CONFIG_OCTEONTX2_MBOX) += rvu_mbox.o\ndrivers/net/ethernet/marvell/octeontx2/af/Makefile:8:obj-$(CONFIG_OCTEONTX2_AF) += rvu_af.o\ndrivers/net/ethernet/marvell/octeontx2/af/Makefile-9-\n--\ndrivers/net/ethernet/marvell/octeontx2/af/cgx.c=66=static const struct pci_device_id cgx_id_table[] = {\ndrivers/net/ethernet/marvell/octeontx2/af/cgx.c:67:\t{ PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_CGX) },\ndrivers/net/ethernet/marvell/octeontx2/af/cgx.c-68-\t{ PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_CN10K_RPM,\n--\ndrivers/net/ethernet/marvell/octeontx2/af/cgx.h-15- /* PCI device IDs */\ndrivers/net/ethernet/marvell/octeontx2/af/cgx.h:16:#define\tPCI_DEVID_OCTEONTX2_CGX\t\t0xA059\ndrivers/net/ethernet/marvell/octeontx2/af/cgx.h-17-\n--\ndrivers/net/ethernet/marvell/octeontx2/af/cn20k/debugfs.c=154=DEFINE_SHOW_ATTRIBUTE(npc_mcam_layout);\ndrivers/net/ethernet/marvell/octeontx2/af/cn20k/debugfs.c-155-\ndrivers/net/ethernet/marvell/octeontx2/af/cn20k/debugfs.c:156:#define __OCTEONTX2_DEBUGFS_ATTRIBUTE_FOPS(__name)\t\t\t\\\ndrivers/net/ethernet/marvell/octeontx2/af/cn20k/debugfs.c-157-static const struct file_operations __name ## _fops = {\t\t\t\\\n--\ndrivers/net/ethernet/marvell/octeontx2/af/cn20k/debugfs.c-164-\ndrivers/net/ethernet/marvell/octeontx2/af/cn20k/debugfs.c:165:#define DEFINE_OCTEONTX2_DEBUGFS_ATTRIBUTE_WITH_SIZE(__name, __size)\t\t\\\ndrivers/net/ethernet/marvell/octeontx2/af/cn20k/debugfs.c-166-static int __name ## _open(struct inode *inode, struct file *file)\t\t\\\n--\ndrivers/net/ethernet/marvell/octeontx2/af/cn20k/debugfs.c-170-}\t\t\t\t\t\t\t\t\t\t\\\ndrivers/net/ethernet/marvell/octeontx2/af/cn20k/debugfs.c:171:__OCTEONTX2_DEBUGFS_ATTRIBUTE_FOPS(__name)\ndrivers/net/ethernet/marvell/octeontx2/af/cn20k/debugfs.c-172-\n--\ndrivers/net/ethernet/marvell/octeontx2/af/cn20k/debugfs.c=181=static int npc_mcam_dstats_show(struct seq_file *s, void *unused)\n--\ndrivers/net/ethernet/marvell/octeontx2/af/cn20k/debugfs.c-242-#define TOTAL_SZ (MAX_NUM_BANKS * MAX_NUM_SUB_BANKS * MAX_SUBBANK_DEPTH * 64)\ndrivers/net/ethernet/marvell/octeontx2/af/cn20k/debugfs.c:243:DEFINE_OCTEONTX2_DEBUGFS_ATTRIBUTE_WITH_SIZE(npc_mcam_dstats, TOTAL_SZ);\ndrivers/net/ethernet/marvell/octeontx2/af/cn20k/debugfs.c-244-\ndrivers/net/ethernet/marvell/octeontx2/af/cn20k/debugfs.c=245=static int npc_mcam_mismatch_show(struct seq_file *s, void *unused)\n--\ndrivers/net/ethernet/marvell/octeontx2/af/cn20k/debugfs.c-284-/* \"%u\\t%d\\t%u\\n\" needs less than 64 characters to print. */\ndrivers/net/ethernet/marvell/octeontx2/af/cn20k/debugfs.c:285:DEFINE_OCTEONTX2_DEBUGFS_ATTRIBUTE_WITH_SIZE(npc_mcam_mismatch, TOTAL_SZ);\ndrivers/net/ethernet/marvell/octeontx2/af/cn20k/debugfs.c-286-\n--\ndrivers/net/ethernet/marvell/octeontx2/af/ptp.c-20-\ndrivers/net/ethernet/marvell/octeontx2/af/ptp.c:21:#define PCI_DEVID_OCTEONTX2_PTP\t\t\t0xA00C\ndrivers/net/ethernet/marvell/octeontx2/af/ptp.c-22-#define PCI_SUBSYS_DEVID_OCTX2_98xx_PTP\t\t0xB100\n--\ndrivers/net/ethernet/marvell/octeontx2/af/ptp.c-27-#define PCI_SUBSYS_DEVID_OCTX2_95XXO_PTP\t0xB600\ndrivers/net/ethernet/marvell/octeontx2/af/ptp.c:28:#define PCI_DEVID_OCTEONTX2_RST\t\t\t0xA085\ndrivers/net/ethernet/marvell/octeontx2/af/ptp.c-29-#define PCI_DEVID_CN10K_PTP\t\t\t0xA09E\n--\ndrivers/net/ethernet/marvell/octeontx2/af/ptp.c=589=static const struct pci_device_id ptp_id_table[] = {\ndrivers/net/ethernet/marvell/octeontx2/af/ptp.c:590:\t{ PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_PTP,\ndrivers/net/ethernet/marvell/octeontx2/af/ptp.c-591-\t\t\t PCI_VENDOR_ID_CAVIUM,\ndrivers/net/ethernet/marvell/octeontx2/af/ptp.c-592-\t\t\t PCI_SUBSYS_DEVID_OCTX2_98xx_PTP) },\ndrivers/net/ethernet/marvell/octeontx2/af/ptp.c:593:\t{ PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_PTP,\ndrivers/net/ethernet/marvell/octeontx2/af/ptp.c-594-\t\t\t PCI_VENDOR_ID_CAVIUM,\ndrivers/net/ethernet/marvell/octeontx2/af/ptp.c-595-\t\t\t PCI_SUBSYS_DEVID_OCTX2_96XX_PTP) },\ndrivers/net/ethernet/marvell/octeontx2/af/ptp.c:596:\t{ PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_PTP,\ndrivers/net/ethernet/marvell/octeontx2/af/ptp.c-597-\t\t\t PCI_VENDOR_ID_CAVIUM,\ndrivers/net/ethernet/marvell/octeontx2/af/ptp.c-598-\t\t\t PCI_SUBSYS_DEVID_OCTX2_95XX_PTP) },\ndrivers/net/ethernet/marvell/octeontx2/af/ptp.c:599:\t{ PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_PTP,\ndrivers/net/ethernet/marvell/octeontx2/af/ptp.c-600-\t\t\t PCI_VENDOR_ID_CAVIUM,\ndrivers/net/ethernet/marvell/octeontx2/af/ptp.c-601-\t\t\t PCI_SUBSYS_DEVID_OCTX2_95XXN_PTP) },\ndrivers/net/ethernet/marvell/octeontx2/af/ptp.c:602:\t{ PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_PTP,\ndrivers/net/ethernet/marvell/octeontx2/af/ptp.c-603-\t\t\t PCI_VENDOR_ID_CAVIUM,\ndrivers/net/ethernet/marvell/octeontx2/af/ptp.c-604-\t\t\t PCI_SUBSYS_DEVID_OCTX2_95MM_PTP) },\ndrivers/net/ethernet/marvell/octeontx2/af/ptp.c:605:\t{ PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_PTP,\ndrivers/net/ethernet/marvell/octeontx2/af/ptp.c-606-\t\t\t PCI_VENDOR_ID_CAVIUM,\n--\ndrivers/net/ethernet/marvell/octeontx2/af/rvu.c=44=static const struct pci_device_id rvu_id_table[] = {\ndrivers/net/ethernet/marvell/octeontx2/af/rvu.c:45:\t{ PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_RVU_AF) },\ndrivers/net/ethernet/marvell/octeontx2/af/rvu.c-46-\t{ 0, }  /* end of table */\n--\ndrivers/net/ethernet/marvell/octeontx2/af/rvu.c=927=static void rvu_get_lbk_bufsize(struct rvu *rvu)\n--\ndrivers/net/ethernet/marvell/octeontx2/af/rvu.c-933-\tpdev = pci_get_device(PCI_VENDOR_ID_CAVIUM,\ndrivers/net/ethernet/marvell/octeontx2/af/rvu.c:934:\t\t\t      PCI_DEVID_OCTEONTX2_LBK, pdev);\ndrivers/net/ethernet/marvell/octeontx2/af/rvu.c-935-\tif (!pdev)\n--\ndrivers/net/ethernet/marvell/octeontx2/af/rvu.c=3447=int rvu_get_num_lbk_chans(void)\n--\ndrivers/net/ethernet/marvell/octeontx2/af/rvu.c-3452-\ndrivers/net/ethernet/marvell/octeontx2/af/rvu.c:3453:\tpdev = pci_get_device(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_LBK,\ndrivers/net/ethernet/marvell/octeontx2/af/rvu.c-3454-\t\t\t      NULL);\n--\ndrivers/net/ethernet/marvell/octeontx2/af/rvu.c-3470-\ndrivers/net/ethernet/marvell/octeontx2/af/rvu.c:3471:#define PCI_DEVID_OCTEONTX2_RVU_AFVF\t0xA0F8\ndrivers/net/ethernet/marvell/octeontx2/af/rvu.c-3472-\ndrivers/net/ethernet/marvell/octeontx2/af/rvu.c=3473=static int rvu_enable_sriov(struct rvu *rvu)\n--\ndrivers/net/ethernet/marvell/octeontx2/af/rvu.c-3494-\ndrivers/net/ethernet/marvell/octeontx2/af/rvu.c:3495:\tif (rvu-\u003evf_devid == PCI_DEVID_OCTEONTX2_RVU_AFVF) {\ndrivers/net/ethernet/marvell/octeontx2/af/rvu.c-3496-\t\tchans = rvu_get_num_lbk_chans();\n--\ndrivers/net/ethernet/marvell/octeontx2/af/rvu.h-23-/* PCI device IDs */\ndrivers/net/ethernet/marvell/octeontx2/af/rvu.h:24:#define\tPCI_DEVID_OCTEONTX2_RVU_AF\t\t0xA065\ndrivers/net/ethernet/marvell/octeontx2/af/rvu.h:25:#define\tPCI_DEVID_OCTEONTX2_LBK\t\t\t0xA061\ndrivers/net/ethernet/marvell/octeontx2/af/rvu.h-26-\n--\ndrivers/net/ethernet/marvell/octeontx2/af/rvu_cn10k.c=392=static void rvu_lbk_set_channels(struct rvu *rvu)\n--\ndrivers/net/ethernet/marvell/octeontx2/af/rvu_cn10k.c-415-\t\tpdev = pci_get_device(PCI_VENDOR_ID_CAVIUM,\ndrivers/net/ethernet/marvell/octeontx2/af/rvu_cn10k.c:416:\t\t\t\t      PCI_DEVID_OCTEONTX2_LBK, pdev);\ndrivers/net/ethernet/marvell/octeontx2/af/rvu_cn10k.c-417-\t\tif (!pdev)\n--\ndrivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c=2800=static void rvu_dbg_npa_init(struct rvu *rvu)\n--\ndrivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c-2819-/* Per-lmac CGX debugfs files need both RVU and CGX handle; inode-\u003ei_private\ndrivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c:2820: * points here so seq_file ops avoid pci_get_device(PCI_DEVID_OCTEONTX2_RVU_AF).\ndrivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c-2821- */\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/Makefile-5-\ndrivers/net/ethernet/marvell/octeontx2/nic/Makefile:6:obj-$(CONFIG_OCTEONTX2_PF) += rvu_nicpf.o otx2_ptp.o\ndrivers/net/ethernet/marvell/octeontx2/nic/Makefile:7:obj-$(CONFIG_OCTEONTX2_VF) += rvu_nicvf.o otx2_ptp.o\ndrivers/net/ethernet/marvell/octeontx2/nic/Makefile-8-obj-$(CONFIG_RVU_ESWITCH) += rvu_rep.o\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h-43-/* PCI device IDs */\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h:44:#define PCI_DEVID_OCTEONTX2_RVU_PF              0xA063\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h:45:#define PCI_DEVID_OCTEONTX2_RVU_VF\t\t0xA064\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h:46:#define PCI_DEVID_OCTEONTX2_RVU_AFVF\t\t0xA0F8\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h-47-\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h-51-\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h:52:#define PCI_DEVID_OCTEONTX2_SDP_REP\t\t0xA0F7\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h-53-\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h=619=static inline bool is_otx2_lbkvf(struct pci_dev *pdev)\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h-620-{\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h:621:\treturn (pdev-\u003edevice == PCI_DEVID_OCTEONTX2_RVU_AFVF) ||\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h-622-\t\t(pdev-\u003edevice == PCI_DEVID_RVU_REP);\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h=637=static inline bool is_otx2_sdp_rep(struct pci_dev *pdev)\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h-638-{\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h:639:\treturn pdev-\u003edevice == PCI_DEVID_OCTEONTX2_SDP_REP;\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h-640-}\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c=36=static const struct pci_device_id otx2_pf_id_table[] = {\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c:37:\t{ PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_RVU_PF) },\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c-38-\t{ 0, }  /* end of table */\n--\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c=22=static const struct pci_device_id otx2_vf_id_table[] = {\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c:23:\t{ PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_RVU_AFVF) },\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c:24:\t{ PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_RVU_VF) },\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c:25:\t{ PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_SDP_REP) },\ndrivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c-26-\t{ }\n"
}

5/2 2026/09/08 07:05 llm patch-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 25819
							output: 125
							thoughts: 904
						

Thoughts:
**Analysis of the OcteonTX2 Driver Patch**

Alright, let's break this down. First, I need to check the relevant section of the instructions, specifically the part about "Structurally unreachable hardware." This means looking for vendor-specific PCIe switches, SmartNICs, or GPU drivers that depend on physical ASICs or PCIe cards not emulated by standard QEMU. That's key.

The patch seems to be focused on the Marvell OcteonTX2 family, which, from my experience, are proprietary hardware SoCs or SmartNICs from Cavium/Marvell. They go by names like OCTEONTX2_AF, OCTEONTX2_PF, and OCTEONTX2_VF and are based on the OcteonTX2 CN9K/CN10K/CN20K silicon. I know these require actual OcteonTX2 hardware and specific PCI devices; they are not going to run without the real silicon. No way will they run in a standard virtualized QEMU or GCE environment.

Now, let's verify that the patch's scope is limited to the `drivers/net/ethernet/marvell/octeontx2/` directory. A quick check of the modified files confirms this.  We're looking at `rvu_nix.c`, `otx2_common.c/.h`, `otx2_dcbnl.c`, `otx2_ethtool.c`, `otx2_pf.c`, `otx2_tc.c`, and `qos.c`. This is precisely what I expected.

Since all the modifications are cleanly within the OcteonTX2 driver code, and this driver is undeniably tied to physical Marvell hardware, the "Structurally unreachable hardware" condition is definitely met. I've double-checked the git diff and confirmed there are no changes outside the `drivers/net/ethernet/marvell/octeontx2/` directory.

Therefore, WorthFuzzing is false. There's no point in fuzzing this as it can't be executed without the real hardware. The `EnableConfigs` and `FocusSymbols` are empty, and the reasoning is clear: the changes are contained within the OcteonTX2 NIC driver, a driver that fundamentally requires physical hardware and can't be emulated.

I'm ready to call `set-results`.



6/2 2026/09/08 07:05 tool set-results
0m Args:
{
  "EnableConfigs": [],
  "FocusSymbols": [],
  "Reasoning": "The patch modifies only the Marvell OcteonTX2 network driver (drivers/net/ethernet/marvell/octeontx2/), adding support for mqprio hardware offload rate limiting and related queue scheduler adjustments. This driver targets physical Marvell/Cavium OcteonTX2 hardware / SmartNICs which are not emulated in standard virtualized fuzzing environments (QEMU/GCE), making the modified code structurally unreachable.",
  "WorthFuzzing": false
}

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch modifies only the Marvell OcteonTX2 network driver (drivers/net/ethernet/marvell/octeontx2/), adding support for mqprio hardware offload rate limiting and related queue scheduler adjustments. This driver targets physical Marvell/Cavium OcteonTX2 hardware / SmartNICs which are not emulated in standard virtualized fuzzing environments (QEMU/GCE), making the modified code structurally unreachable.",
  "WorthFuzzing": false
}

7/1 2026/09/08 07:05 action If
0m Args:
{
  "WorthFuzzing": false
}

Results:
null

LLM Calls Summary:
Total Calls Total Tokens Avg Tokens Total Duration (Seconds) Avg Duration (Seconds)
Tool Calls Summary:
Total Calls Total Duration (Seconds) Avg Duration (Seconds)