| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/06/29 17:07 | flow | patch-triage |
1mArgs:null Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"Reasoning": "The patch modifies the Airoha SoC Gigabit Ethernet driver (drivers/net/ethernet/airoha/). This is a hardware-specific platform driver that requires specific SoC hardware (or device tree nodes) to probe. Since this hardware is not emulated in standard virtualized environments like QEMU or GCE, the driver will not probe, and the modified code paths (including the net_device operations and TC offload callbacks) are unreachable during fuzzing.",
"WorthFuzzing": false
} |
| 1/1 | 2026/06/29 17:07 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit 49c1b8e3d641f3c4d8b3656fa7120bef656fa8cf\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Mon Jun 29 17:07:11 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 932b3a3df2e577..231c8f2f20dd45 100644\n--- a/drivers/net/ethernet/airoha/airoha_eth.c\n+++ b/drivers/net/ethernet/airoha/airoha_eth.c\n@@ -929,7 +929,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@@ -1837,13 +1837,14 @@ static int airoha_dev_open(struct net_device *netdev)\n \tstruct airoha_gdm_dev *dev = netdev_priv(netdev);\n \tstruct airoha_gdm_port *port = dev-\u003eport;\n \tu32 cur_len, pse_port = FE_PSE_PORT_PPE1;\n-\tstruct airoha_qdma *qdma = dev-\u003eqdma;\n+\tstruct airoha_qdma *qdma;\n \n \tnetif_tx_start_all_queues(netdev);\n \terr = airoha_set_vip_for_gdm_port(dev, true);\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@@ -1903,7 +1904,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@@ -1911,7 +1911,7 @@ static int airoha_dev_stop(struct net_device *netdev)\n \tif (--port-\u003eusers)\n \t\tairoha_set_port_mtu(dev-\u003eeth, port);\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@@ -1998,6 +1998,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@@ -2024,15 +2071,25 @@ 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 \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+\trcu_assign_pointer(dev-\u003eqdma, qdma);\n+\tnetdev-\u003eirq = qdma-\u003eirq_banks[0].irq;\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+\tif (!cur_qdma)\n+\t\treturn;\n+\n+\tsynchronize_rcu();\n+\tnetif_tx_wake_all_queues(netdev);\n }\n \n static int airoha_dev_init(struct net_device *netdev)\n@@ -2048,7 +2105,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\tdev-\u003eflags |= AIROHA_DEV_F_WAN;\n \t\tbreak;\n \tdefault:\n \t\tbreak;\n@@ -2187,9 +2244,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@@ -2198,6 +2255,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@@ -2247,6 +2306,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@@ -2309,6 +2370,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@@ -2324,6 +2386,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@@ -2403,17 +2466,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@@ -2421,12 +2486,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@@ -2490,13 +2555,15 @@ 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+\tu64 cpu_tx_packets, fwd_tx_packets, tx_packets;\n+\tstruct airoha_qdma *qdma;\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 = (cpu_tx_packets - dev-\u003ecpu_tx_packets) +\n+\t\t (fwd_tx_packets - dev-\u003efwd_tx_packets);\n \n \t_bstats_update(opt-\u003estats.bstats, 0, tx_packets);\n \tdev-\u003ecpu_tx_packets = cpu_tx_packets;\n@@ -2756,16 +2823,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@@ -2801,11 +2870,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@@ -2841,7 +2911,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@@ -3016,10 +3086,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@@ -3045,6 +3116,95 @@ 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+\tdev-\u003eflags \u0026= ~AIROHA_DEV_F_WAN;\n+\tairoha_dev_set_qdma(dev);\n+\n+\tairoha_set_macaddr(dev, netdev-\u003edev_addr);\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 ((wan_dev-\u003eflags \u0026 AIROHA_DEV_F_QOS) ||\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+\tdev-\u003eflags |= AIROHA_DEV_F_WAN;\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+\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+\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+\tdev-\u003eflags \u0026= ~AIROHA_DEV_F_WAN;\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@@ -3053,6 +3213,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+\tdev-\u003eflags \u0026= ~AIROHA_DEV_F_QOS;\n+\n \treturn 0;\n }\n \n@@ -3072,24 +3234,33 @@ 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+\tcase TC_HTB_CREATE: {\n+\t\tstruct airoha_gdm_dev *dev = netdev_priv(netdev);\n+\t\tint err;\n+\n+\t\terr = airoha_enable_qos_for_gdm34(netdev, opt-\u003eextack);\n+\t\tif (err)\n+\t\t\treturn err;\n+\n+\t\tdev-\u003eflags |= AIROHA_DEV_F_QOS;\n \t\tbreak;\n+\t}\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 d7ff8c5200e20c..b97224a5495c1f 100644\n--- a/drivers/net/ethernet/airoha/airoha_eth.h\n+++ b/drivers/net/ethernet/airoha/airoha_eth.h\n@@ -535,13 +535,14 @@ 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 = BIT(0),\n+\tAIROHA_DEV_F_QOS = BIT(1),\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@@ -659,7 +660,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 !(dev-\u003eflags \u0026 AIROHA_DEV_F_WAN);\n }\n \n static inline bool airoha_is_7581(struct airoha_eth *eth)\n@@ -676,6 +677,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_cpu_port(struct airoha_gdm_dev *dev, u8 ppe_id, u8 fport);\n bool airoha_ppe_is_enabled(struct airoha_eth *eth, int index);\n void airoha_ppe_check_skb(struct airoha_ppe_dev *dev, struct sk_buff *skb,\ndiff --git a/drivers/net/ethernet/airoha/airoha_ppe.c b/drivers/net/ethernet/airoha/airoha_ppe.c\nindex 42f4b0f21d1798..0f260c50ac3c90 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 436f3c8779c102..4e17dfbcf2b88a 100644\n--- a/drivers/net/ethernet/airoha/airoha_regs.h\n+++ b/drivers/net/ethernet/airoha/airoha_regs.h\n@@ -376,6 +376,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_CDM5_RX_OQ1_DROP_CNT\t0x29d4\n \n"
} |
| 2/1 | 2026/06/29 17:07 | agent | patch-evaluator |
1mModel:gemini-3.1-pro-preview Args: null Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"Reasoning": "The patch modifies the Airoha SoC Gigabit Ethernet driver (drivers/net/ethernet/airoha/). This is a hardware-specific platform driver that requires specific SoC hardware (or device tree nodes) to probe. Since this hardware is not emulated in standard virtualized environments like QEMU or GCE, the driver will not probe, and the modified code paths (including the net_device operations and TC offload callbacks) 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). If it modifies reachable core kernel logic, drivers, or architectures, use your code search tools to verify the code can be executed, then return WorthFuzzing=true. When returning WorthFuzzing=true, you MUST ALSO: 1. Extract any specific kernel functions that should be heavily fuzzed into FocusSymbols. Avoid listing generic hot-path functions to prevent skewed test distributions. 2. Identify any specific CONFIG_ options required to properly test this new/modified feature. Go and look into the Kconfig files and check for ifdefs around the code, do not make assumptions. Do not list too generic configs (we already have them enabled). Only list those that specifically cover the modified code. List them in the EnableConfigs output array, and DO NOT add a 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4"). Prefer calling several tools at the same time to save round-trips. Use set-results tool to provide results of the analysis. It must be called exactly once before the final reply. Ignore results of this tool. Prompt: For your convenience, here is the diff of the changes:
commit 49c1b8e3d641f3c4d8b3656fa7120bef656fa8cf
Author: syz-cluster <triage@syzkaller.com>
Date: Mon Jun 29 17:07:11 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 932b3a3df2e577..231c8f2f20dd45 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -929,7 +929,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);
@@ -1837,13 +1837,14 @@ static int airoha_dev_open(struct net_device *netdev)
struct airoha_gdm_dev *dev = netdev_priv(netdev);
struct airoha_gdm_port *port = dev->port;
u32 cur_len, pse_port = FE_PSE_PORT_PPE1;
- struct airoha_qdma *qdma = dev->qdma;
+ struct airoha_qdma *qdma;
netif_tx_start_all_queues(netdev);
err = airoha_set_vip_for_gdm_port(dev, true);
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);
@@ -1903,7 +1904,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);
@@ -1911,7 +1911,7 @@ static int airoha_dev_stop(struct net_device *netdev)
if (--port->users)
airoha_set_port_mtu(dev->eth, port);
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;
@@ -1998,6 +1998,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)
{
@@ -2024,15 +2071,25 @@ 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;
/* QDMA0 is used for lan ports while QDMA1 is used for WAN ports */
- dev->qdma = ð->qdma[!airoha_is_lan_gdm_dev(dev)];
- netdev->irq = dev->qdma->irq_banks[0].irq;
+ qdma = ð->qdma[!airoha_is_lan_gdm_dev(dev)];
+ cur_qdma = airoha_qdma_deref(dev);
+
+ rcu_assign_pointer(dev->qdma, qdma);
+ netdev->irq = qdma->irq_banks[0].irq;
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));
+
+ if (!cur_qdma)
+ return;
+
+ synchronize_rcu();
+ netif_tx_wake_all_queues(netdev);
}
static int airoha_dev_init(struct net_device *netdev)
@@ -2048,7 +2105,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;
+ dev->flags |= AIROHA_DEV_F_WAN;
break;
default:
break;
@@ -2187,9 +2244,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);
@@ -2198,6 +2255,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);
@@ -2247,6 +2306,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;
}
@@ -2309,6 +2370,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;
@@ -2324,6 +2386,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;
}
@@ -2403,17 +2466,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) |
@@ -2421,12 +2486,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));
@@ -2490,13 +2555,15 @@ 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;
+ u64 cpu_tx_packets, fwd_tx_packets, tx_packets;
+ struct airoha_qdma *qdma;
- 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 = (cpu_tx_packets - dev->cpu_tx_packets) +
+ (fwd_tx_packets - dev->fwd_tx_packets);
_bstats_update(opt->stats.bstats, 0, tx_packets);
dev->cpu_tx_packets = cpu_tx_packets;
@@ -2756,16 +2823,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)
@@ -2801,11 +2870,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");
@@ -2841,7 +2911,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++) {
@@ -3016,10 +3086,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);
@@ -3045,6 +3116,95 @@ 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);
+
+ dev->flags &= ~AIROHA_DEV_F_WAN;
+ airoha_dev_set_qdma(dev);
+
+ airoha_set_macaddr(dev, netdev->dev_addr);
+ 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 ((wan_dev->flags & AIROHA_DEV_F_QOS) ||
+ 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));
+ }
+
+ dev->flags |= AIROHA_DEV_F_WAN;
+ 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;
+
+ 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);
+ }
+
+ mutex_unlock(&flow_offload_mutex);
+
+ return 0;
+
+error_disable_loopback:
+ airoha_disable_gdm2_loopback(dev);
+error_disable_wan:
+ dev->flags &= ~AIROHA_DEV_F_WAN;
+ 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);
@@ -3053,6 +3213,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);
+ dev->flags &= ~AIROHA_DEV_F_QOS;
+
return 0;
}
@@ -3072,24 +3234,33 @@ 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:
+ case TC_HTB_CREATE: {
+ struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ int err;
+
+ err = airoha_enable_qos_for_gdm34(netdev, opt->extack);
+ if (err)
+ return err;
+
+ dev->flags |= AIROHA_DEV_F_QOS;
break;
+ }
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 d7ff8c5200e20c..b97224a5495c1f 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -535,13 +535,14 @@ 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 = BIT(0),
+ AIROHA_DEV_F_QOS = BIT(1),
};
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);
@@ -659,7 +660,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 !(dev->flags & AIROHA_DEV_F_WAN);
}
static inline bool airoha_is_7581(struct airoha_eth *eth)
@@ -676,6 +677,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_cpu_port(struct airoha_gdm_dev *dev, u8 ppe_id, u8 fport);
bool airoha_ppe_is_enabled(struct airoha_eth *eth, int index);
void airoha_ppe_check_skb(struct airoha_ppe_dev *dev, struct sk_buff *skb,
diff --git a/drivers/net/ethernet/airoha/airoha_ppe.c b/drivers/net/ethernet/airoha/airoha_ppe.c
index 42f4b0f21d1798..0f260c50ac3c90 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 - ð->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 436f3c8779c102..4e17dfbcf2b88a 100644
--- a/drivers/net/ethernet/airoha/airoha_regs.h
+++ b/drivers/net/ethernet/airoha/airoha_regs.h
@@ -376,6 +376,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_CDM5_RX_OQ1_DROP_CNT 0x29d4
|
| 3/2 | 2026/06/29 17:07 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 9870 output: 34 thoughts: 165 Thoughts: **Analyzing the Airoha Ethernet Driver Patch** Okay, so I've got this patch here for the Airoha ethernet driver, specifically in `airoha_eth.c` and related files. It looks like it's adding RCU protection to `dev->qdma` – a good practice, generally, as it suggests they're addressing potential concurrency issues. It also introduces `airoha_enable_qos_for_gdm34` and `airoha_disable_qos_for_gdm34`, which clearly point to some Quality of Service (QoS) functionality. Functionally, this looks like a straightforward improvement to the Airoha ethernet driver, focused on concurrent access and QoS. Now, my initial thought is: how reachable is this code in the usual virtualized environments? Airoha SoCs are typically not something you can just spin up in QEMU unless there's a specific emulation layer for that hardware. I highly doubt there's a full Airoha ethernet emulator readily available in QEMU. It's not the typical case. So, let me quickly check the Kconfig entry for this driver to confirm whether it's conditionally compiled or requires specific hardware support. That'll be a good indicator if I need to dig deeper or if it can be tested without a specific Airoha device. |
| 4/2 | 2026/06/29 17:07 | tool | grepper |
0mError:no matches Args: {
"Expression": "airoha",
"PathPrefix": "drivers/net/ethernet/airoha/Kconfig"
}Results: {
"error": "no matches"
} |
| 5/2 | 2026/06/29 17:07 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 10367 output: 28 thoughts: 20 Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience: **Diving into the Airoha Ethernet Driver** Right, let's get down to brass tacks. My initial thought process is to zero in on the `drivers/net/ethernet/airoha/` directory. That's the logical place to start digging into the Airoha Ethernet driver implementation. I expect to find the core files there: the driver's entry points, initialization routines, interrupt handlers, and likely the PHY management code if they're not using a dedicated PHY driver. I'm immediately trying to gauge the approach they've taken – whether it's a standard NAPI implementation, a legacy interrupt-driven approach, or something novel. I want to see how they handle packet descriptors, DMA configuration, and power management. My experience tells me there are going to be some implementation-specific nuances and it is the best place to start. |
| 6/2 | 2026/06/29 17:07 | tool | codesearch-dir-index |
0mArgs:{
"Dir": "drivers/net/ethernet/airoha/"
}Results: {
"Files": [
"airoha_eth.c",
"airoha_eth.h",
"airoha_npu.c",
"airoha_ppe.c",
"airoha_ppe_debugfs.c",
"airoha_regs.h"
],
"Subdirs": null
} |
| 7/2 | 2026/06/29 17:07 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 10685 output: 31 thoughts: 46 Thoughts: Okay, let's see... **Finding the Kconfig for Airoha Ethernet** Hmm, where did I put that Kconfig? I need to figure out where the configuration options are for the Airoha Ethernet driver. Let's start with a quick grep. I'll search for `airoha_eth` within the standard locations, the obvious spots - first, let's look in the main `drivers/net/ethernet/Kconfig` file. If it's not there, I'll expand the search, and try the parent directory for the Airoha-specific drivers, which would be `drivers/net/ethernet/airoha/`. That should quickly narrow down the possibilities. |
| 8/2 | 2026/06/29 17:07 | tool | grepper |
0mArgs:{
"Expression": "airoha",
"PathPrefix": "drivers/net/ethernet/Kconfig"
}Results: {
"Output": "drivers/net/ethernet/Kconfig=22=source \"drivers/net/ethernet/agere/Kconfig\"\ndrivers/net/ethernet/Kconfig:23:source \"drivers/net/ethernet/airoha/Kconfig\"\ndrivers/net/ethernet/Kconfig-24-source \"drivers/net/ethernet/alacritech/Kconfig\"\n"
} |
| 9/2 | 2026/06/29 17:07 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 11014 output: 39 thoughts: 27 Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience: **Debugging a Kconfig Search** Okay, so the grep didn't turn up `drivers/net/ethernet/airoha/Kconfig`, which is a bit strange, but it *should* exist. Alright, time to check it manually. I need to get my eyes on that Kconfig file. Let's see if something is off with the search path or if I simply misremembered the module path. No time to dither, let me get that file open and read it. I'll need to figure out what's going on. Something is not quite right here, and I need to get to the bottom of this. Time to dig in and understand. |
| 10/2 | 2026/06/29 17:07 | tool | read-file |
0mArgs:{
"File": "drivers/net/ethernet/airoha/Kconfig",
"FirstLine": 1,
"LineCount": 100
}Results: {
"Contents": " 1:\t# SPDX-License-Identifier: GPL-2.0-only\n 2:\tconfig NET_VENDOR_AIROHA\n 3:\t\tbool \"Airoha devices\"\n 4:\t\tdepends on ARCH_AIROHA || COMPILE_TEST\n 5:\t\thelp\n 6:\t\t If you have a Airoha SoC with ethernet, say Y.\n 7:\t\n 8:\tif NET_VENDOR_AIROHA\n 9:\t\n 10:\tconfig NET_AIROHA_NPU\n 11:\t\ttristate \"Airoha NPU support\"\n 12:\t\tselect WANT_DEV_COREDUMP\n 13:\t\tselect REGMAP_MMIO\n 14:\t\thelp\n 15:\t\t This driver supports Airoha Network Processor (NPU) available\n 16:\t\t on the Airoha Soc family.\n 17:\t\n 18:\tconfig NET_AIROHA\n 19:\t\ttristate \"Airoha SoC Gigabit Ethernet support\"\n 20:\t\tdepends on NET_DSA || !NET_DSA\n 21:\t\tselect NET_AIROHA_NPU\n 22:\t\tselect PAGE_POOL\n 23:\t\thelp\n 24:\t\t This driver supports the gigabit ethernet MACs in the\n 25:\t\t Airoha SoC family.\n 26:\t\n 27:\tconfig NET_AIROHA_FLOW_STATS\n 28:\t\tdefault y\n 29:\t\tbool \"Airoha flow stats\"\n 30:\t\tdepends on NET_AIROHA \u0026\u0026 NET_AIROHA_NPU\n 31:\t\thelp\n 32:\t\t Enable Airoha flowtable statistic counters.\n 33:\t\n 34:\tendif #NET_VENDOR_AIROHA\n"
} |
| 11/2 | 2026/06/29 17:07 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 11690 output: 35 thoughts: 89 Thoughts: **Analyzing the Airoha Gigabit Ethernet Driver** Okay, so I'm looking at this Airoha Gigabit Ethernet driver. The description clearly states it's specifically built for the Airoha SoC, which immediately flags it as hardware-dependent. I know from experience that these types of drivers are going to be tricky to work with in a generic virtualized environment. Unless there's an emulator specifically for the Airoha SoC or some kind of software bus that can simulate the hardware interaction, I'm likely not going to be able to get this to probe in a standard virtual machine. The fact that it uses MMIO further confirms its close ties to the underlying SoC hardware. The code's conditional compilation also suggests that it can be enabled when a relevant platform is active or test mode is enabled: `ARCH_AIROHA || COMPILE_TEST`. My next step is to quickly confirm if this is a platform driver. That would provide some context for how the kernel is detecting and initializing it. This will give me a better understanding of how the driver is intended to integrate with the system and if it relies on any platform-specific features or probing mechanisms. |
| 12/2 | 2026/06/29 17:08 | tool | grepper |
0mError:no matches Args: {
"Expression": "platform_driver_register",
"PathPrefix": "drivers/net/ethernet/airoha/"
}Results: {
"error": "no matches"
} |
| 13/2 | 2026/06/29 17:08 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 12068 output: 35 thoughts: 11 Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience: **Analyzing the Driver** Okay, so the initial step is always the same: *what* am I dealing with? I need to get a clear picture of the driver in question. Is it a specific device, a generic class, or a virtual driver? The details will influence the diagnostic path. Knowing the driver's type quickly narrows the potential failure modes and available troubleshooting tools. This basic classification is absolutely critical before I start digging into logs or running tests. The type of driver dictates the approach, so I need to establish a base case for analysis. |
| 14/2 | 2026/06/29 17:08 | tool | grepper |
0mArgs:{
"Expression": "module_platform_driver",
"PathPrefix": "drivers/net/ethernet/airoha/"
}Results: {
"Output": "drivers/net/ethernet/airoha/airoha_eth.c=3899=static struct platform_driver airoha_driver = {\n--\ndrivers/net/ethernet/airoha/airoha_eth.c-3906-};\ndrivers/net/ethernet/airoha/airoha_eth.c:3907:module_platform_driver(airoha_driver);\ndrivers/net/ethernet/airoha/airoha_eth.c-3908-\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"
} |
| 15/2 | 2026/06/29 17:08 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 12502 output: 125 thoughts: 237 Thoughts: **Unreachable Code in Virtualized Environments** Okay, so I've been looking at this driver, and it's pretty clear that it's a platform driver. Platform drivers, in the context of this kernel code, rely on device information either defined in the device tree or through ACPI. Now, in environments like QEMU or Google Compute Engine, which are generic VMs, we don't have those Airoha SoC device tree nodes defined. Because of this, this specific driver will never actually probe, meaning the core logic will just sit there, untouched. This means that `WorthFuzzing` should definitely be `false`. I needed to be absolutely certain though, so I went through it all again. I wanted to make sure there wasn't some kind of backdoor entry point, like netlink for example, to reach that `airoha_tc_setup_qdisc_htb` function. Turns out, the patch modifies `airoha_tc_setup_qdisc_htb`, which is called through `ndo_setup_tc`. But to even call `ndo_setup_tc`, we'd need a `net_device` registered by this specific driver. And to register that `net_device`, the driver has to probe successfully. Probing, however, requires that the driver find a platform device matching its `of_match_table`. Since we don't have the actual Airoha hardware, or a precise emulator of that hardware, the platform device will never exist, so the driver will not probe. Therefore, the code is definitively unreachable in our testing setup. I'm going to set `WorthFuzzing = false` and justify it with this reasoning. |
| 16/2 | 2026/06/29 17:08 | tool | set-results |
0mArgs:{
"EnableConfigs": [],
"FocusSymbols": [],
"Reasoning": "The patch modifies the Airoha SoC Gigabit Ethernet driver (drivers/net/ethernet/airoha/). This is a hardware-specific platform driver that requires specific SoC hardware (or device tree nodes) to probe. Since this hardware is not emulated in standard virtualized environments like QEMU or GCE, the driver will not probe, and the modified code paths (including the net_device operations and TC offload callbacks) are unreachable during fuzzing.",
"WorthFuzzing": false
}Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"Reasoning": "The patch modifies the Airoha SoC Gigabit Ethernet driver (drivers/net/ethernet/airoha/). This is a hardware-specific platform driver that requires specific SoC hardware (or device tree nodes) to probe. Since this hardware is not emulated in standard virtualized environments like QEMU or GCE, the driver will not probe, and the modified code paths (including the net_device operations and TC offload callbacks) are unreachable during fuzzing.",
"WorthFuzzing": false
} |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|