AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/07/25 20:02 flow patch-triage
0m Args:
null

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch modifies the Airoha ethernet driver (drivers/net/ethernet/airoha/), which is a platform driver specific to Airoha SoCs (e.g., EN7581). This hardware is not emulated in standard QEMU or GCE environments, meaning the driver cannot be probed and the modified code paths are unreachable during fuzzing.",
  "WorthFuzzing": false
}

1/1 2026/07/25 20:02 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit 5e4f10e5f8e1c53f52c4cc64dd176789007b0355\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Sat Jul 25 20:02:43 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c\nindex 79418e682f71f3..02920587e0cc47 100644\n--- a/drivers/net/ethernet/airoha/airoha_eth.c\n+++ b/drivers/net/ethernet/airoha/airoha_eth.c\n@@ -934,7 +934,7 @@ static void airoha_qdma_wake_netdev_txqs(struct airoha_queue *q)\n \t\t\tif (!dev)\n \t\t\t\tcontinue;\n \n-\t\t\tif (dev-\u003eqdma != qdma)\n+\t\t\tif (rcu_access_pointer(dev-\u003eqdma) != qdma)\n \t\t\t\tcontinue;\n \n \t\t\tnetdev = netdev_from_priv(dev);\n@@ -1038,10 +1038,21 @@ static int airoha_qdma_tx_napi_poll(struct napi_struct *napi, int budget)\n \t\tq-\u003equeued--;\n \n \t\tif (skb) {\n+\t\t\tstruct airoha_gdm_dev *dev = netdev_priv(skb-\u003edev);\n+\t\t\tu16 qidx = skb_get_queue_mapping(skb);\n \t\t\tstruct netdev_queue *txq;\n \n \t\t\ttxq = skb_get_tx_queue(skb-\u003edev, skb);\n-\t\t\tnetdev_tx_completed_queue(txq, 1, skb-\u003elen);\n+\t\t\tif (rcu_access_pointer(dev-\u003eqdma) != qdma) {\n+\t\t\t\tatomic_add(1, \u0026dev-\u003ebql[qidx].pkts);\n+\t\t\t\tatomic_add(skb-\u003elen, \u0026dev-\u003ebql[qidx].bytes);\n+\t\t\t} else {\n+\t\t\t\tu32 pkts = 1, bytes = skb-\u003elen;\n+\n+\t\t\t\tbytes += atomic_xchg(\u0026dev-\u003ebql[qidx].bytes, 0);\n+\t\t\t\tpkts += atomic_xchg(\u0026dev-\u003ebql[qidx].pkts, 0);\n+\t\t\t\tnetdev_tx_completed_queue(txq, pkts, bytes);\n+\t\t\t}\n \t\t\tdev_kfree_skb_any(skb);\n \t\t}\n \n@@ -1905,8 +1916,8 @@ static int airoha_dev_open(struct net_device *netdev)\n {\n \tstruct airoha_gdm_dev *dev = netdev_priv(netdev);\n \tstruct airoha_gdm_port *port = dev-\u003eport;\n-\tstruct airoha_qdma *qdma = dev-\u003eqdma;\n \tu32 pse_port = FE_PSE_PORT_PPE1;\n+\tstruct airoha_qdma *qdma;\n \tint err;\n \n \tnetif_tx_start_all_queues(netdev);\n@@ -1914,6 +1925,7 @@ static int airoha_dev_open(struct net_device *netdev)\n \tif (err)\n \t\treturn err;\n \n+\tqdma = airoha_qdma_deref(dev);\n \tif (netdev_uses_dsa(netdev))\n \t\tairoha_fe_set(qdma-\u003eeth, REG_GDM_INGRESS_CFG(port-\u003eid),\n \t\t\t      GDM_STAG_EN_MASK);\n@@ -1937,7 +1949,6 @@ static int airoha_dev_stop(struct net_device *netdev)\n {\n \tstruct airoha_gdm_dev *dev = netdev_priv(netdev);\n \tstruct airoha_gdm_port *port = dev-\u003eport;\n-\tstruct airoha_qdma *qdma = dev-\u003eqdma;\n \n \tnetif_tx_disable(netdev);\n \tairoha_set_vip_for_gdm_port(dev, false);\n@@ -1945,7 +1956,7 @@ static int airoha_dev_stop(struct net_device *netdev)\n \tif (--port-\u003eusers)\n \t\tairoha_ppe_set_xmit_frame_size(dev);\n \telse\n-\t\tairoha_set_gdm_port_fwd_cfg(qdma-\u003eeth,\n+\t\tairoha_set_gdm_port_fwd_cfg(dev-\u003eeth,\n \t\t\t\t\t    REG_GDM_FWD_CFG(port-\u003eid),\n \t\t\t\t\t    FE_PSE_PORT_DROP);\n \treturn 0;\n@@ -2028,6 +2039,53 @@ static int airoha_enable_gdm2_loopback(struct airoha_gdm_dev *dev)\n \treturn 0;\n }\n \n+static int airoha_disable_gdm2_loopback(struct airoha_gdm_dev *dev)\n+{\n+\tstruct airoha_gdm_port *port = dev-\u003eport;\n+\tstruct airoha_eth *eth = dev-\u003eeth;\n+\tint i, src_port;\n+\tu32 pse_port;\n+\n+\tsrc_port = eth-\u003esoc-\u003eops.get_sport(dev-\u003eport, dev-\u003enbq);\n+\tif (src_port \u003c 0)\n+\t\treturn src_port;\n+\n+\tairoha_fe_clear(eth,\n+\t\t\tREG_SP_DFT_CPORT(src_port \u003e\u003e fls(SP_CPORT_DFT_MASK)),\n+\t\t\tSP_CPORT_MASK(src_port \u0026 SP_CPORT_DFT_MASK));\n+\n+\tairoha_fe_set(eth, REG_GDM_FWD_CFG(AIROHA_GDM2_IDX),\n+\t\t      GDM_STRIP_CRC_MASK);\n+\tairoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(AIROHA_GDM2_IDX),\n+\t\t\t\t    FE_PSE_PORT_DROP);\n+\tairoha_fe_clear(eth, REG_GDM_LPBK_CFG(AIROHA_GDM2_IDX),\n+\t\t\tLPBK_CHAN_MASK | LPBK_MODE_MASK | LPBK_EN_MASK);\n+\tpse_port = airoha_ppe_is_enabled(eth, 1) ? FE_PSE_PORT_PPE2\n+\t\t\t\t\t\t : FE_PSE_PORT_PPE1;\n+\tairoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(AIROHA_GDM2_IDX),\n+\t\t\t\t    pse_port);\n+\n+\tairoha_fe_rmw(eth, REG_FE_WAN_PORT, WAN0_MASK,\n+\t\t      FIELD_PREP(WAN0_MASK, AIROHA_GDM2_IDX));\n+\n+\tfor (i = 0; i \u003c eth-\u003esoc-\u003enum_ppe; i++)\n+\t\tairoha_fe_clear(eth, REG_PPE_DFT_CPORT(i, AIROHA_GDM2_IDX),\n+\t\t\t\tDFT_CPORT_MASK(AIROHA_GDM2_IDX));\n+\n+\t/* Enable VIP and IFC for GDM2 */\n+\tairoha_fe_set(eth, REG_FE_VIP_PORT_EN, BIT(AIROHA_GDM2_IDX));\n+\tairoha_fe_set(eth, REG_FE_IFC_PORT_EN, BIT(AIROHA_GDM2_IDX));\n+\n+\tif (port-\u003eid == AIROHA_GDM4_IDX \u0026\u0026 airoha_is_7581(eth)) {\n+\t\tu32 mask = FC_ID_OF_SRC_PORT_MASK(dev-\u003enbq);\n+\n+\t\tairoha_fe_rmw(eth, REG_SRC_PORT_FC_MAP6, mask,\n+\t\t\t      FC_MAP6_DEF_VALUE \u0026 mask);\n+\t}\n+\n+\treturn 0;\n+}\n+\n static struct airoha_gdm_dev *\n airoha_get_wan_gdm_dev(struct airoha_eth *eth)\n {\n@@ -2054,15 +2112,43 @@ airoha_get_wan_gdm_dev(struct airoha_eth *eth)\n static void airoha_dev_set_qdma(struct airoha_gdm_dev *dev)\n {\n \tstruct net_device *netdev = netdev_from_priv(dev);\n+\tstruct airoha_qdma *cur_qdma, *qdma;\n \tstruct airoha_eth *eth = dev-\u003eeth;\n-\tint ppe_id;\n+\tint i, ppe_id;\n \n \t/* QDMA0 is used for lan ports while QDMA1 is used for WAN ports */\n-\tdev-\u003eqdma = \u0026eth-\u003eqdma[!airoha_is_lan_gdm_dev(dev)];\n-\tnetdev-\u003eirq = dev-\u003eqdma-\u003eirq_banks[0].irq;\n+\tqdma = \u0026eth-\u003eqdma[!airoha_is_lan_gdm_dev(dev)];\n+\tcur_qdma = airoha_qdma_deref(dev);\n+\n+\tif (cur_qdma) {\n+\t\tnetif_tx_stop_all_queues(netdev);\n+\t\tfor (i = 0; i \u003c ARRAY_SIZE(cur_qdma-\u003eq_tx_irq); i++)\n+\t\t\tnapi_disable(\u0026cur_qdma-\u003eq_tx_irq[i].napi);\n+\t}\n+\n+\trcu_assign_pointer(dev-\u003eqdma, qdma);\n+\tnetdev-\u003eirq = qdma-\u003eirq_banks[0].irq;\n+\tsynchronize_rcu();\n \n \tppe_id = !airoha_is_lan_gdm_dev(dev) \u0026\u0026 airoha_ppe_is_enabled(eth, 1);\n \tairoha_ppe_set_cpu_port(dev, ppe_id, airoha_get_fe_port(dev));\n+\n+\t/* Seed qos_stats baselines with the new QDMA block's current\n+\t * counter values to avoid a spurious spike on the first stats\n+\t * query after init or migration.\n+\t */\n+\tfor (i = 0; i \u003c ARRAY_SIZE(dev-\u003eqos_stats); i++) {\n+\t\tdev-\u003eqos_stats[i].cpu_tx_packets =\n+\t\t\tairoha_qdma_rr(qdma, REG_CNTR_VAL(i \u003c\u003c 1));\n+\t\tdev-\u003eqos_stats[i].fwd_tx_packets =\n+\t\t\tairoha_qdma_rr(qdma, REG_CNTR_VAL((i \u003c\u003c 1) + 1));\n+\t}\n+\n+\tif (cur_qdma) {\n+\t\tfor (i = 0; i \u003c ARRAY_SIZE(cur_qdma-\u003eq_tx_irq); i++)\n+\t\t\tnapi_enable(\u0026cur_qdma-\u003eq_tx_irq[i].napi);\n+\t\tnetif_tx_wake_all_queues(netdev);\n+\t}\n }\n \n static int airoha_dev_init(struct net_device *netdev)\n@@ -2078,7 +2164,7 @@ static int airoha_dev_init(struct net_device *netdev)\n \t\tfallthrough;\n \tcase AIROHA_GDM2_IDX:\n \t\t/* GDM2 is always used as wan */\n-\t\tdev-\u003eflags |= AIROHA_PRIV_F_WAN;\n+\t\tset_bit(AIROHA_DEV_F_WAN, \u0026dev-\u003eflags);\n \t\tbreak;\n \tdefault:\n \t\tbreak;\n@@ -2217,9 +2303,9 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,\n \t\t\t\t   struct net_device *netdev)\n {\n \tstruct airoha_gdm_dev *dev = netdev_priv(netdev);\n-\tstruct airoha_qdma *qdma = dev-\u003eqdma;\n \tu32 nr_frags, tag, msg0, msg1, len;\n \tstruct airoha_queue_entry *e;\n+\tstruct airoha_qdma *qdma;\n \tstruct netdev_queue *txq;\n \tstruct airoha_queue *q;\n \tLIST_HEAD(tx_list);\n@@ -2228,6 +2314,8 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,\n \tu16 index;\n \tu8 fport;\n \n+\trcu_read_lock();\n+\tqdma = rcu_dereference(dev-\u003eqdma);\n \tqid = airoha_qdma_get_txq(qdma, skb_get_queue_mapping(skb));\n \ttag = airoha_get_dsa_tag(skb, netdev);\n \n@@ -2277,6 +2365,8 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,\n \t\tnetif_tx_stop_queue(txq);\n \t\tq-\u003etxq_stopped = true;\n \t\tspin_unlock_bh(\u0026q-\u003elock);\n+\t\trcu_read_unlock();\n+\n \t\treturn NETDEV_TX_BUSY;\n \t}\n \n@@ -2342,6 +2432,7 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,\n \t\t\t\tFIELD_PREP(TX_RING_CPU_IDX_MASK, index));\n \n \tspin_unlock_bh(\u0026q-\u003elock);\n+\trcu_read_unlock();\n \n \treturn NETDEV_TX_OK;\n \n@@ -2354,6 +2445,7 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,\n error:\n \tdev_kfree_skb_any(skb);\n \tnetdev-\u003estats.tx_dropped++;\n+\trcu_read_unlock();\n \n \treturn NETDEV_TX_OK;\n }\n@@ -2433,17 +2525,19 @@ static int airoha_qdma_set_chan_tx_sched(struct net_device *netdev,\n \t\t\t\t\t const u16 *weights, u8 n_weights)\n {\n \tstruct airoha_gdm_dev *dev = netdev_priv(netdev);\n+\tstruct airoha_qdma *qdma;\n \tint i;\n \n+\tqdma = airoha_qdma_deref(dev);\n \tfor (i = 0; i \u003c AIROHA_NUM_QOS_QUEUES; i++)\n-\t\tairoha_qdma_clear(dev-\u003eqdma, REG_QUEUE_CLOSE_CFG(channel),\n+\t\tairoha_qdma_clear(qdma, REG_QUEUE_CLOSE_CFG(channel),\n \t\t\t\t  TXQ_DISABLE_CHAN_QUEUE_MASK(channel, i));\n \n \tfor (i = 0; i \u003c n_weights; i++) {\n \t\tu32 status;\n \t\tint err;\n \n-\t\tairoha_qdma_wr(dev-\u003eqdma, REG_TXWRR_WEIGHT_CFG,\n+\t\tairoha_qdma_wr(qdma, REG_TXWRR_WEIGHT_CFG,\n \t\t\t       TWRR_RW_CMD_MASK |\n \t\t\t       FIELD_PREP(TWRR_CHAN_IDX_MASK, channel) |\n \t\t\t       FIELD_PREP(TWRR_QUEUE_IDX_MASK, i) |\n@@ -2451,12 +2545,12 @@ static int airoha_qdma_set_chan_tx_sched(struct net_device *netdev,\n \t\terr = read_poll_timeout(airoha_qdma_rr, status,\n \t\t\t\t\tstatus \u0026 TWRR_RW_CMD_DONE,\n \t\t\t\t\tUSEC_PER_MSEC, 10 * USEC_PER_MSEC,\n-\t\t\t\t\ttrue, dev-\u003eqdma, REG_TXWRR_WEIGHT_CFG);\n+\t\t\t\t\ttrue, qdma, REG_TXWRR_WEIGHT_CFG);\n \t\tif (err)\n \t\t\treturn err;\n \t}\n \n-\tairoha_qdma_rmw(dev-\u003eqdma, REG_CHAN_QOS_MODE(channel \u003e\u003e 3),\n+\tairoha_qdma_rmw(qdma, REG_CHAN_QOS_MODE(channel \u003e\u003e 3),\n \t\t\tCHAN_QOS_MODE_MASK(channel),\n \t\t\t__field_prep(CHAN_QOS_MODE_MASK(channel), mode));\n \n@@ -2520,17 +2614,22 @@ static int airoha_qdma_get_tx_ets_stats(struct net_device *netdev, int channel,\n \t\t\t\t\tstruct tc_ets_qopt_offload *opt)\n {\n \tstruct airoha_gdm_dev *dev = netdev_priv(netdev);\n-\tstruct airoha_qdma *qdma = dev-\u003eqdma;\n+\tu32 cpu_tx_packets, fwd_tx_packets;\n+\tstruct airoha_qdma *qdma;\n+\tu64 tx_packets;\n \n-\tu64 cpu_tx_packets = airoha_qdma_rr(qdma, REG_CNTR_VAL(channel \u003c\u003c 1));\n-\tu64 fwd_tx_packets = airoha_qdma_rr(qdma,\n-\t\t\t\t\t    REG_CNTR_VAL((channel \u003c\u003c 1) + 1));\n-\tu64 tx_packets = (cpu_tx_packets - dev-\u003ecpu_tx_packets) +\n-\t\t\t (fwd_tx_packets - dev-\u003efwd_tx_packets);\n+\tqdma = airoha_qdma_deref(dev);\n+\tcpu_tx_packets = airoha_qdma_rr(qdma, REG_CNTR_VAL(channel \u003c\u003c 1));\n+\tfwd_tx_packets = airoha_qdma_rr(qdma,\n+\t\t\t\t\tREG_CNTR_VAL((channel \u003c\u003c 1) + 1));\n+\ttx_packets = (u32)(cpu_tx_packets -\n+\t\t\t   dev-\u003eqos_stats[channel].cpu_tx_packets);\n+\ttx_packets += (u32)(fwd_tx_packets -\n+\t\t\t    dev-\u003eqos_stats[channel].fwd_tx_packets);\n \n \t_bstats_update(opt-\u003estats.bstats, 0, tx_packets);\n-\tdev-\u003ecpu_tx_packets = cpu_tx_packets;\n-\tdev-\u003efwd_tx_packets = fwd_tx_packets;\n+\tdev-\u003eqos_stats[channel].cpu_tx_packets = cpu_tx_packets;\n+\tdev-\u003eqos_stats[channel].fwd_tx_packets = fwd_tx_packets;\n \n \treturn 0;\n }\n@@ -2785,16 +2884,18 @@ static int airoha_qdma_set_tx_rate_limit(struct net_device *netdev,\n \t\t\t\t\t u32 bucket_size)\n {\n \tstruct airoha_gdm_dev *dev = netdev_priv(netdev);\n+\tstruct airoha_qdma *qdma;\n \tint i, err;\n \n+\tqdma = airoha_qdma_deref(dev);\n \tfor (i = 0; i \u003c= TRTCM_PEAK_MODE; i++) {\n-\t\terr = airoha_qdma_set_trtcm_config(dev-\u003eqdma, channel,\n+\t\terr = airoha_qdma_set_trtcm_config(qdma, channel,\n \t\t\t\t\t\t   REG_EGRESS_TRTCM_CFG, i,\n \t\t\t\t\t\t   !!rate, TRTCM_METER_MODE);\n \t\tif (err)\n \t\t\treturn err;\n \n-\t\terr = airoha_qdma_set_trtcm_token_bucket(dev-\u003eqdma, channel,\n+\t\terr = airoha_qdma_set_trtcm_token_bucket(qdma, channel,\n \t\t\t\t\t\t\t REG_EGRESS_TRTCM_CFG,\n \t\t\t\t\t\t\t i, rate, bucket_size);\n \t\tif (err)\n@@ -2830,11 +2931,12 @@ static int airoha_tc_htb_alloc_leaf_queue(struct net_device *netdev,\n \tu32 channel = TC_H_MIN(opt-\u003eclassid) % AIROHA_NUM_QOS_CHANNELS;\n \tint err, num_tx_queues = AIROHA_NUM_TX_RING + channel + 1;\n \tstruct airoha_gdm_dev *dev = netdev_priv(netdev);\n-\tstruct airoha_qdma *qdma = dev-\u003eqdma;\n+\tstruct airoha_qdma *qdma;\n \n \t/* Here we need to check the requested QDMA channel is not already\n \t * in use by another net_device running on the same QDMA block.\n \t */\n+\tqdma = airoha_qdma_deref(dev);\n \tif (test_and_set_bit(channel, qdma-\u003eqos_channel_map)) {\n \t\tNL_SET_ERR_MSG_MOD(opt-\u003eextack,\n \t\t\t\t   \"qdma qos channel already in use\");\n@@ -2870,7 +2972,7 @@ static int airoha_qdma_set_rx_meter(struct airoha_gdm_dev *dev,\n \t\t\t\t    u32 rate, u32 bucket_size,\n \t\t\t\t    enum trtcm_unit_type unit_type)\n {\n-\tstruct airoha_qdma *qdma = dev-\u003eqdma;\n+\tstruct airoha_qdma *qdma = airoha_qdma_deref(dev);\n \tint i;\n \n \tfor (i = 0; i \u003c ARRAY_SIZE(qdma-\u003eq_rx); i++) {\n@@ -3045,10 +3147,11 @@ static void airoha_tc_remove_htb_queue(struct net_device *netdev, int queue)\n {\n \tstruct airoha_gdm_dev *dev = netdev_priv(netdev);\n \tint num_tx_queues = AIROHA_NUM_TX_RING;\n-\tstruct airoha_qdma *qdma = dev-\u003eqdma;\n+\tstruct airoha_qdma *qdma;\n \n \tairoha_qdma_set_tx_rate_limit(netdev, queue, 0, 0);\n \n+\tqdma = airoha_qdma_deref(dev);\n \tclear_bit(queue, qdma-\u003eqos_channel_map);\n \tclear_bit(queue, dev-\u003eqos_sq_bmap);\n \n@@ -3074,6 +3177,100 @@ static int airoha_tc_htb_delete_leaf_queue(struct net_device *netdev,\n \treturn 0;\n }\n \n+static void airoha_disable_qos_for_gdm34(struct net_device *netdev)\n+{\n+\tstruct airoha_gdm_dev *dev = netdev_priv(netdev);\n+\tstruct airoha_gdm_port *port = dev-\u003eport;\n+\tint err;\n+\n+\tif (port-\u003eid != AIROHA_GDM3_IDX \u0026\u0026\n+\t    port-\u003eid != AIROHA_GDM4_IDX)\n+\t\treturn;\n+\n+\terr = airoha_disable_gdm2_loopback(dev);\n+\tif (err)\n+\t\tnetdev_warn(netdev,\n+\t\t\t    \"failed disabling GDM2 loopback: %d\\n\", err);\n+\n+\tclear_bit(AIROHA_DEV_F_WAN, \u0026dev-\u003eflags);\n+\tairoha_dev_set_qdma(dev);\n+\n+\tairoha_set_macaddr(dev, netdev-\u003edev_addr);\n+\tairoha_ppe_set_xmit_frame_size(dev);\n+\n+\tif (netif_running(netdev))\n+\t\tairoha_set_gdm_port_fwd_cfg(dev-\u003eeth,\n+\t\t\t\t\t    REG_GDM_FWD_CFG(port-\u003eid),\n+\t\t\t\t\t    FE_PSE_PORT_PPE1);\n+}\n+\n+static int airoha_enable_qos_for_gdm34(struct net_device *netdev,\n+\t\t\t\t       struct netlink_ext_ack *extack)\n+{\n+\tstruct airoha_gdm_dev *wan_dev, *dev = netdev_priv(netdev);\n+\tstruct airoha_gdm_port *port = dev-\u003eport;\n+\tstruct airoha_eth *eth = dev-\u003eeth;\n+\tint err = -EBUSY;\n+\n+\tif (port-\u003eid != AIROHA_GDM3_IDX \u0026\u0026\n+\t    port-\u003eid != AIROHA_GDM4_IDX) {\n+\t\t/* HW QoS is always supported by GDM1 and GDM2 */\n+\t\treturn 0;\n+\t}\n+\n+\tif (!airoha_is_lan_gdm_dev(dev)) /* Already enabled */\n+\t\treturn 0;\n+\n+\tmutex_lock(\u0026flow_offload_mutex);\n+\n+\twan_dev = airoha_get_wan_gdm_dev(eth);\n+\tif (wan_dev) {\n+\t\tif (test_bit(AIROHA_DEV_F_TX_QOS, \u0026wan_dev-\u003eflags) ||\n+\t\t    wan_dev-\u003eport-\u003eid == AIROHA_GDM2_IDX) {\n+\t\t\tNL_SET_ERR_MSG_MOD(extack,\n+\t\t\t\t\t   \"QoS configured for WAN device\");\n+\t\t\tgoto error_unlock;\n+\t\t}\n+\t\tairoha_disable_qos_for_gdm34(netdev_from_priv(wan_dev));\n+\t}\n+\n+\tset_bit(AIROHA_DEV_F_WAN, \u0026dev-\u003eflags);\n+\tairoha_dev_set_qdma(dev);\n+\terr = airoha_enable_gdm2_loopback(dev);\n+\tif (err)\n+\t\tgoto error_disable_wan;\n+\n+\terr = airoha_set_macaddr(dev, netdev-\u003edev_addr);\n+\tif (err)\n+\t\tgoto error_disable_loopback;\n+\n+\tairoha_dev_set_xmit_frame_size(netdev);\n+\tif (netif_running(netdev)) {\n+\t\tu32 pse_port;\n+\n+\t\tpse_port = airoha_ppe_is_enabled(eth, 1) ? FE_PSE_PORT_PPE2\n+\t\t\t\t\t\t\t : FE_PSE_PORT_PPE1;\n+\t\tairoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(port-\u003eid),\n+\t\t\t\t\t    pse_port);\n+\t}\n+\n+\tset_bit(AIROHA_DEV_F_TX_QOS, \u0026dev-\u003eflags);\n+\n+\tmutex_unlock(\u0026flow_offload_mutex);\n+\n+\treturn 0;\n+\n+error_disable_loopback:\n+\tairoha_disable_gdm2_loopback(dev);\n+error_disable_wan:\n+\tclear_bit(AIROHA_DEV_F_WAN, \u0026dev-\u003eflags);\n+\tairoha_dev_set_qdma(dev);\n+error_unlock:\n+\tmutex_unlock(\u0026flow_offload_mutex);\n+\n+\treturn err;\n+}\n+\n static int airoha_tc_htb_destroy(struct net_device *netdev)\n {\n \tstruct airoha_gdm_dev *dev = netdev_priv(netdev);\n@@ -3082,6 +3279,8 @@ static int airoha_tc_htb_destroy(struct net_device *netdev)\n \tfor_each_set_bit(q, dev-\u003eqos_sq_bmap, AIROHA_NUM_QOS_CHANNELS)\n \t\tairoha_tc_remove_htb_queue(netdev, q);\n \n+\tclear_bit(AIROHA_DEV_F_TX_QOS, \u0026dev-\u003eflags);\n+\n \treturn 0;\n }\n \n@@ -3101,24 +3300,24 @@ static int airoha_tc_get_htb_get_leaf_queue(struct net_device *netdev,\n \treturn 0;\n }\n \n-static int airoha_tc_setup_qdisc_htb(struct net_device *dev,\n+static int airoha_tc_setup_qdisc_htb(struct net_device *netdev,\n \t\t\t\t     struct tc_htb_qopt_offload *opt)\n {\n \tswitch (opt-\u003ecommand) {\n \tcase TC_HTB_CREATE:\n-\t\tbreak;\n+\t\treturn airoha_enable_qos_for_gdm34(netdev, opt-\u003eextack);\n \tcase TC_HTB_DESTROY:\n-\t\treturn airoha_tc_htb_destroy(dev);\n+\t\treturn airoha_tc_htb_destroy(netdev);\n \tcase TC_HTB_NODE_MODIFY:\n-\t\treturn airoha_tc_htb_modify_queue(dev, opt);\n+\t\treturn airoha_tc_htb_modify_queue(netdev, opt);\n \tcase TC_HTB_LEAF_ALLOC_QUEUE:\n-\t\treturn airoha_tc_htb_alloc_leaf_queue(dev, opt);\n+\t\treturn airoha_tc_htb_alloc_leaf_queue(netdev, opt);\n \tcase TC_HTB_LEAF_DEL:\n \tcase TC_HTB_LEAF_DEL_LAST:\n \tcase TC_HTB_LEAF_DEL_LAST_FORCE:\n-\t\treturn airoha_tc_htb_delete_leaf_queue(dev, opt);\n+\t\treturn airoha_tc_htb_delete_leaf_queue(netdev, opt);\n \tcase TC_HTB_LEAF_QUERY_QUEUE:\n-\t\treturn airoha_tc_get_htb_get_leaf_queue(dev, opt);\n+\t\treturn airoha_tc_get_htb_get_leaf_queue(netdev, opt);\n \tdefault:\n \t\treturn -EOPNOTSUPP;\n \t}\ndiff --git a/drivers/net/ethernet/airoha/airoha_eth.h b/drivers/net/ethernet/airoha/airoha_eth.h\nindex fe934f9ffe8a11..e6fbd42cf96429 100644\n--- a/drivers/net/ethernet/airoha/airoha_eth.h\n+++ b/drivers/net/ethernet/airoha/airoha_eth.h\n@@ -570,24 +570,31 @@ struct airoha_qdma {\n \tDECLARE_BITMAP(qos_channel_map, AIROHA_NUM_QOS_CHANNELS);\n };\n \n-enum airoha_priv_flags {\n-\tAIROHA_PRIV_F_WAN = BIT(0),\n+enum airoha_dev_flags {\n+\tAIROHA_DEV_F_WAN,\n+\tAIROHA_DEV_F_TX_QOS,\n };\n \n struct airoha_gdm_dev {\n+\tstruct airoha_qdma __rcu *qdma;\n \tstruct airoha_gdm_port *port;\n-\tstruct airoha_qdma *qdma;\n \tstruct airoha_eth *eth;\n \n \tDECLARE_BITMAP(qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS);\n-\t/* qos stats counters */\n-\tu64 cpu_tx_packets;\n-\tu64 fwd_tx_packets;\n+\tstruct {\n+\t\tu32 cpu_tx_packets;\n+\t\tu32 fwd_tx_packets;\n+\t} qos_stats[AIROHA_NUM_QOS_CHANNELS];\n \n-\tu32 flags;\n+\tunsigned long flags;\n \tint nbq;\n \n \tstruct airoha_hw_stats stats;\n+\n+\tstruct {\n+\t\tatomic_t bytes;\n+\t\tatomic_t pkts;\n+\t} bql[AIROHA_NUM_NETDEV_TX_RINGS];\n };\n \n struct airoha_gdm_port {\n@@ -694,7 +701,7 @@ static inline u16 airoha_qdma_get_txq(struct airoha_qdma *qdma, u16 qid)\n \n static inline bool airoha_is_lan_gdm_dev(struct airoha_gdm_dev *dev)\n {\n-\treturn !(dev-\u003eflags \u0026 AIROHA_PRIV_F_WAN);\n+\treturn !test_bit(AIROHA_DEV_F_WAN, \u0026dev-\u003eflags);\n }\n \n static inline bool airoha_is_7581(struct airoha_eth *eth)\n@@ -711,6 +718,16 @@ int airoha_get_fe_port(struct airoha_gdm_dev *dev);\n bool airoha_is_valid_gdm_dev(struct airoha_eth *eth,\n \t\t\t     struct airoha_gdm_dev *dev);\n \n+extern struct mutex flow_offload_mutex;\n+\n+static inline struct airoha_qdma *\n+airoha_qdma_deref(struct airoha_gdm_dev *dev)\n+{\n+\treturn rcu_dereference_protected(dev-\u003eqdma,\n+\t\t\t\t\t lockdep_rtnl_is_held() ||\n+\t\t\t\t\t lockdep_is_held(\u0026flow_offload_mutex));\n+}\n+\n void airoha_ppe_set_xmit_frame_size(struct airoha_gdm_dev *dev);\n void airoha_ppe_set_cpu_port(struct airoha_gdm_dev *dev, u8 ppe_id, u8 fport);\n bool airoha_ppe_is_enabled(struct airoha_eth *eth, int index);\ndiff --git a/drivers/net/ethernet/airoha/airoha_ppe.c b/drivers/net/ethernet/airoha/airoha_ppe.c\nindex e4c3644dd6ec7a..33ddf0d0785539 100644\n--- a/drivers/net/ethernet/airoha/airoha_ppe.c\n+++ b/drivers/net/ethernet/airoha/airoha_ppe.c\n@@ -15,7 +15,10 @@\n #include \"airoha_regs.h\"\n #include \"airoha_eth.h\"\n \n-static DEFINE_MUTEX(flow_offload_mutex);\n+/* Serialize airoha_gdm_dev flags, QDMA pointer and PPE CPU port\n+ * configuration.\n+ */\n+DEFINE_MUTEX(flow_offload_mutex);\n static DEFINE_SPINLOCK(ppe_lock);\n \n static const struct rhashtable_params airoha_flow_table_params = {\n@@ -86,8 +89,8 @@ static u32 airoha_ppe_get_timestamp(struct airoha_ppe *ppe)\n \n void airoha_ppe_set_cpu_port(struct airoha_gdm_dev *dev, u8 ppe_id, u8 fport)\n {\n-\tstruct airoha_qdma *qdma = dev-\u003eqdma;\n-\tstruct airoha_eth *eth = qdma-\u003eeth;\n+\tstruct airoha_qdma *qdma = airoha_qdma_deref(dev);\n+\tstruct airoha_eth *eth = dev-\u003eeth;\n \tu8 qdma_id = qdma - \u0026eth-\u003eqdma[0];\n \tu32 fe_cpu_port;\n \ndiff --git a/drivers/net/ethernet/airoha/airoha_regs.h b/drivers/net/ethernet/airoha/airoha_regs.h\nindex 6fed63d013b412..442b48c9b991ef 100644\n--- a/drivers/net/ethernet/airoha/airoha_regs.h\n+++ b/drivers/net/ethernet/airoha/airoha_regs.h\n@@ -375,6 +375,7 @@\n \n #define REG_SRC_PORT_FC_MAP6\t\t0x2298\n #define FC_ID_OF_SRC_PORT_MASK(_n)\tGENMASK(4 + ((_n) \u003c\u003c 3), ((_n) \u003c\u003c 3))\n+#define FC_MAP6_DEF_VALUE\t\t0x1b1a1918\n \n #define REG_WAN_MTU0\t\t\t0x2300\n #define WAN_MTU1_MASK\t\t\tGENMASK(29, 16)\n"
}

2/1 2026/07/25 20:02 agent patch-evaluator
0m Model:
best-expensive

Args:
null

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch modifies the Airoha ethernet driver (drivers/net/ethernet/airoha/), which is a platform driver specific to Airoha SoCs (e.g., EN7581). This hardware is not emulated in standard QEMU or GCE environments, meaning the driver cannot be probed and the modified code paths are unreachable during fuzzing.",
  "WorthFuzzing": false
}

Instruction:
You are an expert Linux kernel maintainer.
Your job is to review a provided patch series and determine
if it makes functional changes to the kernel that should be fuzzed.

IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on your internal knowledge of the kernel. You must actively
use your code access tools to examine the actual source code and confirm any assumptions.

Return WorthFuzzing=false if the patch only contains:
- Modifications to Documentation/, Kconfig files, or code comments.
- Purely decorative changes, such as logging (e.g., pr_err, printk) or tracepoints.
- Changes to numeric constants or macros that do not functionally alter execution flow.
- Code paths that are impossible to reach in virtualized environments like GCE or QEMU,
  even when utilizing software-emulated hardware (e.g., usb gadget, mac80211_hwsim).
- Code in vendor-specific PCIe switch, SmartNIC, or GPU drivers (e.g., mlxsw, pds_core, qed,
  ionic, amdgpu) that require physical PCIe hardware cards not emulated in standard QEMU.
- Driver .remove, .shutdown, or pci_unregister_driver teardown callbacks (e.g., igb_remove)
  that are executed only during PCI hot-unplug or sysfs driver unbind operations.

If it modifies reachable core kernel logic, drivers, or architectures, use your code search
tools to verify the code can be executed, then return WorthFuzzing=true.

When returning WorthFuzzing=true, you MUST ALSO:
1. Extract any specific kernel functions that should be heavily fuzzed into FocusSymbols.
   Avoid listing generic hot-path functions to prevent skewed test distributions.
   Prefer non-static, non-inlined API entrypoint functions over internal static helper functions
   (which are inlined by the compiler and do not have distinct symbol addresses).
2. Identify any specific CONFIG_ options required to properly test this new/modified feature.
   Go and look into the Kconfig files and check for ifdefs around the code, do not make assumptions.
   Also check "depends on" lines in Kconfig to include any non-standard parent subsystem configs
   needed for Kbuild to compile the code statically into vmlinux. List them in the EnableConfigs
   output array, and DO NOT add a 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4").
Prefer calling several tools at the same time to save round-trips.


Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.

Prompt:
For your convenience, here is the diff of the changes:
commit 5e4f10e5f8e1c53f52c4cc64dd176789007b0355
Author: syz-cluster <triage@syzkaller.com>
Date:   Sat Jul 25 20:02:43 2026 +0000

    syz-cluster: applied patch under review

diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
index 79418e682f71f3..02920587e0cc47 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -934,7 +934,7 @@ static void airoha_qdma_wake_netdev_txqs(struct airoha_queue *q)
 			if (!dev)
 				continue;
 
-			if (dev->qdma != qdma)
+			if (rcu_access_pointer(dev->qdma) != qdma)
 				continue;
 
 			netdev = netdev_from_priv(dev);
@@ -1038,10 +1038,21 @@ static int airoha_qdma_tx_napi_poll(struct napi_struct *napi, int budget)
 		q->queued--;
 
 		if (skb) {
+			struct airoha_gdm_dev *dev = netdev_priv(skb->dev);
+			u16 qidx = skb_get_queue_mapping(skb);
 			struct netdev_queue *txq;
 
 			txq = skb_get_tx_queue(skb->dev, skb);
-			netdev_tx_completed_queue(txq, 1, skb->len);
+			if (rcu_access_pointer(dev->qdma) != qdma) {
+				atomic_add(1, &dev->bql[qidx].pkts);
+				atomic_add(skb->len, &dev->bql[qidx].bytes);
+			} else {
+				u32 pkts = 1, bytes = skb->len;
+
+				bytes += atomic_xchg(&dev->bql[qidx].bytes, 0);
+				pkts += atomic_xchg(&dev->bql[qidx].pkts, 0);
+				netdev_tx_completed_queue(txq, pkts, bytes);
+			}
 			dev_kfree_skb_any(skb);
 		}
 
@@ -1905,8 +1916,8 @@ static int airoha_dev_open(struct net_device *netdev)
 {
 	struct airoha_gdm_dev *dev = netdev_priv(netdev);
 	struct airoha_gdm_port *port = dev->port;
-	struct airoha_qdma *qdma = dev->qdma;
 	u32 pse_port = FE_PSE_PORT_PPE1;
+	struct airoha_qdma *qdma;
 	int err;
 
 	netif_tx_start_all_queues(netdev);
@@ -1914,6 +1925,7 @@ static int airoha_dev_open(struct net_device *netdev)
 	if (err)
 		return err;
 
+	qdma = airoha_qdma_deref(dev);
 	if (netdev_uses_dsa(netdev))
 		airoha_fe_set(qdma->eth, REG_GDM_INGRESS_CFG(port->id),
 			      GDM_STAG_EN_MASK);
@@ -1937,7 +1949,6 @@ static int airoha_dev_stop(struct net_device *netdev)
 {
 	struct airoha_gdm_dev *dev = netdev_priv(netdev);
 	struct airoha_gdm_port *port = dev->port;
-	struct airoha_qdma *qdma = dev->qdma;
 
 	netif_tx_disable(netdev);
 	airoha_set_vip_for_gdm_port(dev, false);
@@ -1945,7 +1956,7 @@ static int airoha_dev_stop(struct net_device *netdev)
 	if (--port->users)
 		airoha_ppe_set_xmit_frame_size(dev);
 	else
-		airoha_set_gdm_port_fwd_cfg(qdma->eth,
+		airoha_set_gdm_port_fwd_cfg(dev->eth,
 					    REG_GDM_FWD_CFG(port->id),
 					    FE_PSE_PORT_DROP);
 	return 0;
@@ -2028,6 +2039,53 @@ static int airoha_enable_gdm2_loopback(struct airoha_gdm_dev *dev)
 	return 0;
 }
 
+static int airoha_disable_gdm2_loopback(struct airoha_gdm_dev *dev)
+{
+	struct airoha_gdm_port *port = dev->port;
+	struct airoha_eth *eth = dev->eth;
+	int i, src_port;
+	u32 pse_port;
+
+	src_port = eth->soc->ops.get_sport(dev->port, dev->nbq);
+	if (src_port < 0)
+		return src_port;
+
+	airoha_fe_clear(eth,
+			REG_SP_DFT_CPORT(src_port >> fls(SP_CPORT_DFT_MASK)),
+			SP_CPORT_MASK(src_port & SP_CPORT_DFT_MASK));
+
+	airoha_fe_set(eth, REG_GDM_FWD_CFG(AIROHA_GDM2_IDX),
+		      GDM_STRIP_CRC_MASK);
+	airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(AIROHA_GDM2_IDX),
+				    FE_PSE_PORT_DROP);
+	airoha_fe_clear(eth, REG_GDM_LPBK_CFG(AIROHA_GDM2_IDX),
+			LPBK_CHAN_MASK | LPBK_MODE_MASK | LPBK_EN_MASK);
+	pse_port = airoha_ppe_is_enabled(eth, 1) ? FE_PSE_PORT_PPE2
+						 : FE_PSE_PORT_PPE1;
+	airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(AIROHA_GDM2_IDX),
+				    pse_port);
+
+	airoha_fe_rmw(eth, REG_FE_WAN_PORT, WAN0_MASK,
+		      FIELD_PREP(WAN0_MASK, AIROHA_GDM2_IDX));
+
+	for (i = 0; i < eth->soc->num_ppe; i++)
+		airoha_fe_clear(eth, REG_PPE_DFT_CPORT(i, AIROHA_GDM2_IDX),
+				DFT_CPORT_MASK(AIROHA_GDM2_IDX));
+
+	/* Enable VIP and IFC for GDM2 */
+	airoha_fe_set(eth, REG_FE_VIP_PORT_EN, BIT(AIROHA_GDM2_IDX));
+	airoha_fe_set(eth, REG_FE_IFC_PORT_EN, BIT(AIROHA_GDM2_IDX));
+
+	if (port->id == AIROHA_GDM4_IDX && airoha_is_7581(eth)) {
+		u32 mask = FC_ID_OF_SRC_PORT_MASK(dev->nbq);
+
+		airoha_fe_rmw(eth, REG_SRC_PORT_FC_MAP6, mask,
+			      FC_MAP6_DEF_VALUE & mask);
+	}
+
+	return 0;
+}
+
 static struct airoha_gdm_dev *
 airoha_get_wan_gdm_dev(struct airoha_eth *eth)
 {
@@ -2054,15 +2112,43 @@ airoha_get_wan_gdm_dev(struct airoha_eth *eth)
 static void airoha_dev_set_qdma(struct airoha_gdm_dev *dev)
 {
 	struct net_device *netdev = netdev_from_priv(dev);
+	struct airoha_qdma *cur_qdma, *qdma;
 	struct airoha_eth *eth = dev->eth;
-	int ppe_id;
+	int i, ppe_id;
 
 	/* QDMA0 is used for lan ports while QDMA1 is used for WAN ports */
-	dev->qdma = &eth->qdma[!airoha_is_lan_gdm_dev(dev)];
-	netdev->irq = dev->qdma->irq_banks[0].irq;
+	qdma = &eth->qdma[!airoha_is_lan_gdm_dev(dev)];
+	cur_qdma = airoha_qdma_deref(dev);
+
+	if (cur_qdma) {
+		netif_tx_stop_all_queues(netdev);
+		for (i = 0; i < ARRAY_SIZE(cur_qdma->q_tx_irq); i++)
+			napi_disable(&cur_qdma->q_tx_irq[i].napi);
+	}
+
+	rcu_assign_pointer(dev->qdma, qdma);
+	netdev->irq = qdma->irq_banks[0].irq;
+	synchronize_rcu();
 
 	ppe_id = !airoha_is_lan_gdm_dev(dev) && airoha_ppe_is_enabled(eth, 1);
 	airoha_ppe_set_cpu_port(dev, ppe_id, airoha_get_fe_port(dev));
+
+	/* Seed qos_stats baselines with the new QDMA block's current
+	 * counter values to avoid a spurious spike on the first stats
+	 * query after init or migration.
+	 */
+	for (i = 0; i < ARRAY_SIZE(dev->qos_stats); i++) {
+		dev->qos_stats[i].cpu_tx_packets =
+			airoha_qdma_rr(qdma, REG_CNTR_VAL(i << 1));
+		dev->qos_stats[i].fwd_tx_packets =
+			airoha_qdma_rr(qdma, REG_CNTR_VAL((i << 1) + 1));
+	}
+
+	if (cur_qdma) {
+		for (i = 0; i < ARRAY_SIZE(cur_qdma->q_tx_irq); i++)
+			napi_enable(&cur_qdma->q_tx_irq[i].napi);
+		netif_tx_wake_all_queues(netdev);
+	}
 }
 
 static int airoha_dev_init(struct net_device *netdev)
@@ -2078,7 +2164,7 @@ static int airoha_dev_init(struct net_device *netdev)
 		fallthrough;
 	case AIROHA_GDM2_IDX:
 		/* GDM2 is always used as wan */
-		dev->flags |= AIROHA_PRIV_F_WAN;
+		set_bit(AIROHA_DEV_F_WAN, &dev->flags);
 		break;
 	default:
 		break;
@@ -2217,9 +2303,9 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
 				   struct net_device *netdev)
 {
 	struct airoha_gdm_dev *dev = netdev_priv(netdev);
-	struct airoha_qdma *qdma = dev->qdma;
 	u32 nr_frags, tag, msg0, msg1, len;
 	struct airoha_queue_entry *e;
+	struct airoha_qdma *qdma;
 	struct netdev_queue *txq;
 	struct airoha_queue *q;
 	LIST_HEAD(tx_list);
@@ -2228,6 +2314,8 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
 	u16 index;
 	u8 fport;
 
+	rcu_read_lock();
+	qdma = rcu_dereference(dev->qdma);
 	qid = airoha_qdma_get_txq(qdma, skb_get_queue_mapping(skb));
 	tag = airoha_get_dsa_tag(skb, netdev);
 
@@ -2277,6 +2365,8 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
 		netif_tx_stop_queue(txq);
 		q->txq_stopped = true;
 		spin_unlock_bh(&q->lock);
+		rcu_read_unlock();
+
 		return NETDEV_TX_BUSY;
 	}
 
@@ -2342,6 +2432,7 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
 				FIELD_PREP(TX_RING_CPU_IDX_MASK, index));
 
 	spin_unlock_bh(&q->lock);
+	rcu_read_unlock();
 
 	return NETDEV_TX_OK;
 
@@ -2354,6 +2445,7 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
 error:
 	dev_kfree_skb_any(skb);
 	netdev->stats.tx_dropped++;
+	rcu_read_unlock();
 
 	return NETDEV_TX_OK;
 }
@@ -2433,17 +2525,19 @@ static int airoha_qdma_set_chan_tx_sched(struct net_device *netdev,
 					 const u16 *weights, u8 n_weights)
 {
 	struct airoha_gdm_dev *dev = netdev_priv(netdev);
+	struct airoha_qdma *qdma;
 	int i;
 
+	qdma = airoha_qdma_deref(dev);
 	for (i = 0; i < AIROHA_NUM_QOS_QUEUES; i++)
-		airoha_qdma_clear(dev->qdma, REG_QUEUE_CLOSE_CFG(channel),
+		airoha_qdma_clear(qdma, REG_QUEUE_CLOSE_CFG(channel),
 				  TXQ_DISABLE_CHAN_QUEUE_MASK(channel, i));
 
 	for (i = 0; i < n_weights; i++) {
 		u32 status;
 		int err;
 
-		airoha_qdma_wr(dev->qdma, REG_TXWRR_WEIGHT_CFG,
+		airoha_qdma_wr(qdma, REG_TXWRR_WEIGHT_CFG,
 			       TWRR_RW_CMD_MASK |
 			       FIELD_PREP(TWRR_CHAN_IDX_MASK, channel) |
 			       FIELD_PREP(TWRR_QUEUE_IDX_MASK, i) |
@@ -2451,12 +2545,12 @@ static int airoha_qdma_set_chan_tx_sched(struct net_device *netdev,
 		err = read_poll_timeout(airoha_qdma_rr, status,
 					status & TWRR_RW_CMD_DONE,
 					USEC_PER_MSEC, 10 * USEC_PER_MSEC,
-					true, dev->qdma, REG_TXWRR_WEIGHT_CFG);
+					true, qdma, REG_TXWRR_WEIGHT_CFG);
 		if (err)
 			return err;
 	}
 
-	airoha_qdma_rmw(dev->qdma, REG_CHAN_QOS_MODE(channel >> 3),
+	airoha_qdma_rmw(qdma, REG_CHAN_QOS_MODE(channel >> 3),
 			CHAN_QOS_MODE_MASK(channel),
 			__field_prep(CHAN_QOS_MODE_MASK(channel), mode));
 
@@ -2520,17 +2614,22 @@ static int airoha_qdma_get_tx_ets_stats(struct net_device *netdev, int channel,
 					struct tc_ets_qopt_offload *opt)
 {
 	struct airoha_gdm_dev *dev = netdev_priv(netdev);
-	struct airoha_qdma *qdma = dev->qdma;
+	u32 cpu_tx_packets, fwd_tx_packets;
+	struct airoha_qdma *qdma;
+	u64 tx_packets;
 
-	u64 cpu_tx_packets = airoha_qdma_rr(qdma, REG_CNTR_VAL(channel << 1));
-	u64 fwd_tx_packets = airoha_qdma_rr(qdma,
-					    REG_CNTR_VAL((channel << 1) + 1));
-	u64 tx_packets = (cpu_tx_packets - dev->cpu_tx_packets) +
-			 (fwd_tx_packets - dev->fwd_tx_packets);
+	qdma = airoha_qdma_deref(dev);
+	cpu_tx_packets = airoha_qdma_rr(qdma, REG_CNTR_VAL(channel << 1));
+	fwd_tx_packets = airoha_qdma_rr(qdma,
+					REG_CNTR_VAL((channel << 1) + 1));
+	tx_packets = (u32)(cpu_tx_packets -
+			   dev->qos_stats[channel].cpu_tx_packets);
+	tx_packets += (u32)(fwd_tx_packets -
+			    dev->qos_stats[channel].fwd_tx_packets);
 
 	_bstats_update(opt->stats.bstats, 0, tx_packets);
-	dev->cpu_tx_packets = cpu_tx_packets;
-	dev->fwd_tx_packets = fwd_tx_packets;
+	dev->qos_stats[channel].cpu_tx_packets = cpu_tx_packets;
+	dev->qos_stats[channel].fwd_tx_packets = fwd_tx_packets;
 
 	return 0;
 }
@@ -2785,16 +2884,18 @@ static int airoha_qdma_set_tx_rate_limit(struct net_device *netdev,
 					 u32 bucket_size)
 {
 	struct airoha_gdm_dev *dev = netdev_priv(netdev);
+	struct airoha_qdma *qdma;
 	int i, err;
 
+	qdma = airoha_qdma_deref(dev);
 	for (i = 0; i <= TRTCM_PEAK_MODE; i++) {
-		err = airoha_qdma_set_trtcm_config(dev->qdma, channel,
+		err = airoha_qdma_set_trtcm_config(qdma, channel,
 						   REG_EGRESS_TRTCM_CFG, i,
 						   !!rate, TRTCM_METER_MODE);
 		if (err)
 			return err;
 
-		err = airoha_qdma_set_trtcm_token_bucket(dev->qdma, channel,
+		err = airoha_qdma_set_trtcm_token_bucket(qdma, channel,
 							 REG_EGRESS_TRTCM_CFG,
 							 i, rate, bucket_size);
 		if (err)
@@ -2830,11 +2931,12 @@ static int airoha_tc_htb_alloc_leaf_queue(struct net_device *netdev,
 	u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS;
 	int err, num_tx_queues = AIROHA_NUM_TX_RING + channel + 1;
 	struct airoha_gdm_dev *dev = netdev_priv(netdev);
-	struct airoha_qdma *qdma = dev->qdma;
+	struct airoha_qdma *qdma;
 
 	/* Here we need to check the requested QDMA channel is not already
 	 * in use by another net_device running on the same QDMA block.
 	 */
+	qdma = airoha_qdma_deref(dev);
 	if (test_and_set_bit(channel, qdma->qos_channel_map)) {
 		NL_SET_ERR_MSG_MOD(opt->extack,
 				   "qdma qos channel already in use");
@@ -2870,7 +2972,7 @@ static int airoha_qdma_set_rx_meter(struct airoha_gdm_dev *dev,
 				    u32 rate, u32 bucket_size,
 				    enum trtcm_unit_type unit_type)
 {
-	struct airoha_qdma *qdma = dev->qdma;
+	struct airoha_qdma *qdma = airoha_qdma_deref(dev);
 	int i;
 
 	for (i = 0; i < ARRAY_SIZE(qdma->q_rx); i++) {
@@ -3045,10 +3147,11 @@ static void airoha_tc_remove_htb_queue(struct net_device *netdev, int queue)
 {
 	struct airoha_gdm_dev *dev = netdev_priv(netdev);
 	int num_tx_queues = AIROHA_NUM_TX_RING;
-	struct airoha_qdma *qdma = dev->qdma;
+	struct airoha_qdma *qdma;
 
 	airoha_qdma_set_tx_rate_limit(netdev, queue, 0, 0);
 
+	qdma = airoha_qdma_deref(dev);
 	clear_bit(queue, qdma->qos_channel_map);
 	clear_bit(queue, dev->qos_sq_bmap);
 
@@ -3074,6 +3177,100 @@ static int airoha_tc_htb_delete_leaf_queue(struct net_device *netdev,
 	return 0;
 }
 
+static void airoha_disable_qos_for_gdm34(struct net_device *netdev)
+{
+	struct airoha_gdm_dev *dev = netdev_priv(netdev);
+	struct airoha_gdm_port *port = dev->port;
+	int err;
+
+	if (port->id != AIROHA_GDM3_IDX &&
+	    port->id != AIROHA_GDM4_IDX)
+		return;
+
+	err = airoha_disable_gdm2_loopback(dev);
+	if (err)
+		netdev_warn(netdev,
+			    "failed disabling GDM2 loopback: %d\n", err);
+
+	clear_bit(AIROHA_DEV_F_WAN, &dev->flags);
+	airoha_dev_set_qdma(dev);
+
+	airoha_set_macaddr(dev, netdev->dev_addr);
+	airoha_ppe_set_xmit_frame_size(dev);
+
+	if (netif_running(netdev))
+		airoha_set_gdm_port_fwd_cfg(dev->eth,
+					    REG_GDM_FWD_CFG(port->id),
+					    FE_PSE_PORT_PPE1);
+}
+
+static int airoha_enable_qos_for_gdm34(struct net_device *netdev,
+				       struct netlink_ext_ack *extack)
+{
+	struct airoha_gdm_dev *wan_dev, *dev = netdev_priv(netdev);
+	struct airoha_gdm_port *port = dev->port;
+	struct airoha_eth *eth = dev->eth;
+	int err = -EBUSY;
+
+	if (port->id != AIROHA_GDM3_IDX &&
+	    port->id != AIROHA_GDM4_IDX) {
+		/* HW QoS is always supported by GDM1 and GDM2 */
+		return 0;
+	}
+
+	if (!airoha_is_lan_gdm_dev(dev)) /* Already enabled */
+		return 0;
+
+	mutex_lock(&flow_offload_mutex);
+
+	wan_dev = airoha_get_wan_gdm_dev(eth);
+	if (wan_dev) {
+		if (test_bit(AIROHA_DEV_F_TX_QOS, &wan_dev->flags) ||
+		    wan_dev->port->id == AIROHA_GDM2_IDX) {
+			NL_SET_ERR_MSG_MOD(extack,
+					   "QoS configured for WAN device");
+			goto error_unlock;
+		}
+		airoha_disable_qos_for_gdm34(netdev_from_priv(wan_dev));
+	}
+
+	set_bit(AIROHA_DEV_F_WAN, &dev->flags);
+	airoha_dev_set_qdma(dev);
+	err = airoha_enable_gdm2_loopback(dev);
+	if (err)
+		goto error_disable_wan;
+
+	err = airoha_set_macaddr(dev, netdev->dev_addr);
+	if (err)
+		goto error_disable_loopback;
+
+	airoha_dev_set_xmit_frame_size(netdev);
+	if (netif_running(netdev)) {
+		u32 pse_port;
+
+		pse_port = airoha_ppe_is_enabled(eth, 1) ? FE_PSE_PORT_PPE2
+							 : FE_PSE_PORT_PPE1;
+		airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(port->id),
+					    pse_port);
+	}
+
+	set_bit(AIROHA_DEV_F_TX_QOS, &dev->flags);
+
+	mutex_unlock(&flow_offload_mutex);
+
+	return 0;
+
+error_disable_loopback:
+	airoha_disable_gdm2_loopback(dev);
+error_disable_wan:
+	clear_bit(AIROHA_DEV_F_WAN, &dev->flags);
+	airoha_dev_set_qdma(dev);
+error_unlock:
+	mutex_unlock(&flow_offload_mutex);
+
+	return err;
+}
+
 static int airoha_tc_htb_destroy(struct net_device *netdev)
 {
 	struct airoha_gdm_dev *dev = netdev_priv(netdev);
@@ -3082,6 +3279,8 @@ static int airoha_tc_htb_destroy(struct net_device *netdev)
 	for_each_set_bit(q, dev->qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS)
 		airoha_tc_remove_htb_queue(netdev, q);
 
+	clear_bit(AIROHA_DEV_F_TX_QOS, &dev->flags);
+
 	return 0;
 }
 
@@ -3101,24 +3300,24 @@ static int airoha_tc_get_htb_get_leaf_queue(struct net_device *netdev,
 	return 0;
 }
 
-static int airoha_tc_setup_qdisc_htb(struct net_device *dev,
+static int airoha_tc_setup_qdisc_htb(struct net_device *netdev,
 				     struct tc_htb_qopt_offload *opt)
 {
 	switch (opt->command) {
 	case TC_HTB_CREATE:
-		break;
+		return airoha_enable_qos_for_gdm34(netdev, opt->extack);
 	case TC_HTB_DESTROY:
-		return airoha_tc_htb_destroy(dev);
+		return airoha_tc_htb_destroy(netdev);
 	case TC_HTB_NODE_MODIFY:
-		return airoha_tc_htb_modify_queue(dev, opt);
+		return airoha_tc_htb_modify_queue(netdev, opt);
 	case TC_HTB_LEAF_ALLOC_QUEUE:
-		return airoha_tc_htb_alloc_leaf_queue(dev, opt);
+		return airoha_tc_htb_alloc_leaf_queue(netdev, opt);
 	case TC_HTB_LEAF_DEL:
 	case TC_HTB_LEAF_DEL_LAST:
 	case TC_HTB_LEAF_DEL_LAST_FORCE:
-		return airoha_tc_htb_delete_leaf_queue(dev, opt);
+		return airoha_tc_htb_delete_leaf_queue(netdev, opt);
 	case TC_HTB_LEAF_QUERY_QUEUE:
-		return airoha_tc_get_htb_get_leaf_queue(dev, opt);
+		return airoha_tc_get_htb_get_leaf_queue(netdev, opt);
 	default:
 		return -EOPNOTSUPP;
 	}
diff --git a/drivers/net/ethernet/airoha/airoha_eth.h b/drivers/net/ethernet/airoha/airoha_eth.h
index fe934f9ffe8a11..e6fbd42cf96429 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -570,24 +570,31 @@ struct airoha_qdma {
 	DECLARE_BITMAP(qos_channel_map, AIROHA_NUM_QOS_CHANNELS);
 };
 
-enum airoha_priv_flags {
-	AIROHA_PRIV_F_WAN = BIT(0),
+enum airoha_dev_flags {
+	AIROHA_DEV_F_WAN,
+	AIROHA_DEV_F_TX_QOS,
 };
 
 struct airoha_gdm_dev {
+	struct airoha_qdma __rcu *qdma;
 	struct airoha_gdm_port *port;
-	struct airoha_qdma *qdma;
 	struct airoha_eth *eth;
 
 	DECLARE_BITMAP(qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS);
-	/* qos stats counters */
-	u64 cpu_tx_packets;
-	u64 fwd_tx_packets;
+	struct {
+		u32 cpu_tx_packets;
+		u32 fwd_tx_packets;
+	} qos_stats[AIROHA_NUM_QOS_CHANNELS];
 
-	u32 flags;
+	unsigned long flags;
 	int nbq;
 
 	struct airoha_hw_stats stats;
+
+	struct {
+		atomic_t bytes;
+		atomic_t pkts;
+	} bql[AIROHA_NUM_NETDEV_TX_RINGS];
 };
 
 struct airoha_gdm_port {
@@ -694,7 +701,7 @@ static inline u16 airoha_qdma_get_txq(struct airoha_qdma *qdma, u16 qid)
 
 static inline bool airoha_is_lan_gdm_dev(struct airoha_gdm_dev *dev)
 {
-	return !(dev->flags & AIROHA_PRIV_F_WAN);
+	return !test_bit(AIROHA_DEV_F_WAN, &dev->flags);
 }
 
 static inline bool airoha_is_7581(struct airoha_eth *eth)
@@ -711,6 +718,16 @@ int airoha_get_fe_port(struct airoha_gdm_dev *dev);
 bool airoha_is_valid_gdm_dev(struct airoha_eth *eth,
 			     struct airoha_gdm_dev *dev);
 
+extern struct mutex flow_offload_mutex;
+
+static inline struct airoha_qdma *
+airoha_qdma_deref(struct airoha_gdm_dev *dev)
+{
+	return rcu_dereference_protected(dev->qdma,
+					 lockdep_rtnl_is_held() ||
+					 lockdep_is_held(&flow_offload_mutex));
+}
+
 void airoha_ppe_set_xmit_frame_size(struct airoha_gdm_dev *dev);
 void airoha_ppe_set_cpu_port(struct airoha_gdm_dev *dev, u8 ppe_id, u8 fport);
 bool airoha_ppe_is_enabled(struct airoha_eth *eth, int index);
diff --git a/drivers/net/ethernet/airoha/airoha_ppe.c b/drivers/net/ethernet/airoha/airoha_ppe.c
index e4c3644dd6ec7a..33ddf0d0785539 100644
--- a/drivers/net/ethernet/airoha/airoha_ppe.c
+++ b/drivers/net/ethernet/airoha/airoha_ppe.c
@@ -15,7 +15,10 @@
 #include "airoha_regs.h"
 #include "airoha_eth.h"
 
-static DEFINE_MUTEX(flow_offload_mutex);
+/* Serialize airoha_gdm_dev flags, QDMA pointer and PPE CPU port
+ * configuration.
+ */
+DEFINE_MUTEX(flow_offload_mutex);
 static DEFINE_SPINLOCK(ppe_lock);
 
 static const struct rhashtable_params airoha_flow_table_params = {
@@ -86,8 +89,8 @@ static u32 airoha_ppe_get_timestamp(struct airoha_ppe *ppe)
 
 void airoha_ppe_set_cpu_port(struct airoha_gdm_dev *dev, u8 ppe_id, u8 fport)
 {
-	struct airoha_qdma *qdma = dev->qdma;
-	struct airoha_eth *eth = qdma->eth;
+	struct airoha_qdma *qdma = airoha_qdma_deref(dev);
+	struct airoha_eth *eth = dev->eth;
 	u8 qdma_id = qdma - &eth->qdma[0];
 	u32 fe_cpu_port;
 
diff --git a/drivers/net/ethernet/airoha/airoha_regs.h b/drivers/net/ethernet/airoha/airoha_regs.h
index 6fed63d013b412..442b48c9b991ef 100644
--- a/drivers/net/ethernet/airoha/airoha_regs.h
+++ b/drivers/net/ethernet/airoha/airoha_regs.h
@@ -375,6 +375,7 @@
 
 #define REG_SRC_PORT_FC_MAP6		0x2298
 #define FC_ID_OF_SRC_PORT_MASK(_n)	GENMASK(4 + ((_n) << 3), ((_n) << 3))
+#define FC_MAP6_DEF_VALUE		0x1b1a1918
 
 #define REG_WAN_MTU0			0x2300
 #define WAN_MTU1_MASK			GENMASK(29, 16)

3/2 2026/07/25 20:02 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 11106
							output: 35
							thoughts: 235
						

Thoughts:
**Analyzing the Airoha Ethernet Driver Patch**

Okay, so I've got this patch for the `airoha` ethernet driver, specifically for Airoha EN7581 SoCs. It touches a few critical areas – the QDMA handling, which looks like it's getting an RCU update for `dev->qdma`, and some interesting additions like QoS support for GDM3 and GDM4.  It's also updating BQL handling, which is always good to see. My initial thought is to figure out the implications of this in virtualized environments.

Airoha is a networking SoC, likely ARM64, and the driver is *specific* to that SoC.  Generally, these SoC-specific ethernet drivers – similar to what you see with MediaTek and others – rely on the physical hardware or specific platform devices for the probe to succeed. So, I need to know if I can even test this in QEMU. Is this a platform driver?

To figure this out, I'll leverage the `default_api:grepper` and search for the driver registration. That should quickly tell me if it relies on a specific device tree, or if it has some kind of broader applicability. That's the first step in determining how easily this patch can be evaluated in a testing context.



4/2 2026/07/25 20:02 tool grepper
0m Args:
{
  "Expression": "module_platform_driver",
  "PathPrefix": "drivers/net/ethernet/airoha/"
}

Results:
{
  "Output": "drivers/net/ethernet/airoha/airoha_eth.c=3956=static struct platform_driver airoha_driver = {\n--\ndrivers/net/ethernet/airoha/airoha_eth.c-3963-};\ndrivers/net/ethernet/airoha/airoha_eth.c:3964:module_platform_driver(airoha_driver);\ndrivers/net/ethernet/airoha/airoha_eth.c-3965-\n--\ndrivers/net/ethernet/airoha/airoha_npu.c=813=static struct platform_driver airoha_npu_driver = {\n--\ndrivers/net/ethernet/airoha/airoha_npu.c-820-};\ndrivers/net/ethernet/airoha/airoha_npu.c:821:module_platform_driver(airoha_npu_driver);\ndrivers/net/ethernet/airoha/airoha_npu.c-822-\n"
}

5/2 2026/07/25 20:02 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 11908
							output: 107
							thoughts: 528
						

Thoughts:
**Assessment of the Airoha Ethernet Driver's Fuzzability**

Okay, so I've got this platform driver, `airoha_eth.c`, and my initial thought is that it's going to be a tough nut to crack for fuzzing. Platform drivers, especially those tied to specific SoCs like the Airoha EN7581, typically need a device tree node to probe properly, right? And QEMU, as far as I know, doesn't natively emulate this particular Airoha hardware or its associated ethernet controllers.

My first question is always, "Is there any sneaky way to get at this through a virtual device or some kind of mocking?" But, realistically, platform drivers for specific hardware are rarely fuzzable unless you have a custom emulator or they're part of a more generic, mockable framework. Syzkaller's not going to help here either, as it doesn't emulate the Airoha SoCs.

Let me take a look at the functions this patch is touching. Aha, the patch is changing things in `airoha_qdma_wake_netdev_txqs`, `airoha_qdma_tx_napi_poll`, `airoha_dev_open`, `airoha_dev_stop`, `airoha_enable_gdm2_loopback`, `airoha_disable_gdm2_loopback`, `airoha_dev_set_qdma`, `airoha_dev_init`, `airoha_dev_xmit`, `airoha_qdma_set_chan_tx_sched`, `airoha_qdma_get_tx_ets_stats`, `airoha_qdma_set_tx_rate_limit`, `airoha_tc_htb_alloc_leaf_queue`, `airoha_qdma_set_rx_meter`, `airoha_tc_remove_htb_queue`, `airoha_tc_htb_delete_leaf_queue`, `airoha_disable_qos_for_gdm34`, `airoha_enable_qos_for_gdm34`, `airoha_tc_htb_destroy`, `airoha_tc_get_htb_get_leaf_queue`, `airoha_tc_setup_qdisc_htb`. Yeah, those are all screaming "Airoha ethernet driver" to me.

Based on this, it's clear this is a platform driver deeply tied to the Airoha SoC. This means it needs real hardware or a very specific emulator to be probed and executed. Since QEMU, and therefore environments like GCE, aren't going to cut it, I'm going to have to conclude that reaching this code is impossible in a virtualized setting.

Therefore, I'm going to return `WorthFuzzing=false`. The rationale is straightforward: it's a hardware-specific platform driver for an Airoha SoC, and it cannot be probed or executed in standard QEMU or GCE environments. There's no effective path to fuzz it in a practical way.



6/2 2026/07/25 20:02 tool set-results
0m Args:
{
  "EnableConfigs": [],
  "FocusSymbols": [],
  "Reasoning": "The patch modifies the Airoha ethernet driver (drivers/net/ethernet/airoha/), which is a platform driver specific to Airoha SoCs (e.g., EN7581). This hardware is not emulated in standard QEMU or GCE environments, meaning the driver cannot be probed and the modified code paths are unreachable during fuzzing.",
  "WorthFuzzing": false
}

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch modifies the Airoha ethernet driver (drivers/net/ethernet/airoha/), which is a platform driver specific to Airoha SoCs (e.g., EN7581). This hardware is not emulated in standard QEMU or GCE environments, meaning the driver cannot be probed and the modified code paths are unreachable during fuzzing.",
  "WorthFuzzing": false
}

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)