| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/09/26 22:50 | flow | patch-triage |
7mArgs:null Results: {
"EnableConfigs": [
"PHYLINK",
"USB_NET_DRIVERS",
"USB_USBNET",
"USB_NET_AX8817X",
"USB_LAN78XX",
"NET_DSA",
"NET_DSA_LOOP",
"STMMAC_ETH",
"STMMAC_PCI"
],
"FocusSymbols": [
"phylink_stop",
"phylink_suspend",
"phylink_resume",
"phylink_speed_down",
"stmmac_reconfigure_mtu",
"stmmac_xdp_open",
"stmmac_xdp_release",
"stmmac_reopen",
"stmmac_open",
"stmmac_release"
],
"KMSANReasoning": "The patch refactors datapath lifecycle management, suspend/resume handling, MTU reconfiguration, and phylink PM speed control in the stmmac network driver and phylink subsystem. Specifically:\n1. `priv-\u003edma_conf` is converted from an embedded struct to a dynamically allocated pointer allocated via `kzalloc_obj()`, ensuring all fields are zero-initialized.\n2. Newly added struct members (`datapath`, `hw_suspended` in `stmmac_priv`, and `suspend_speed_down` in `phylink`) are members of zero-initialized structs allocated via `alloc_etherdev_mqs` and `kzalloc`.\n3. All local variables introduced in helper functions (e.g., `stmmac_reconfigure_mtu`, `stmmac_resume_hw`, `stmmac_set_irq_state`, `stmmac_reopen`) are explicitly initialized before use.\n4. No uninitialized memory, padding, or uninitialized kernel buffers are copied to user space or used in conditional logic.\n5. The potential bug risks introduced by this patch involve pointer lifecycle/lifetime management (e.g., UAF or double-free of DMA configurations during error unwinding or reconfiguration), NULL pointer dereferences, locking/NAPI concurrency, or DMA descriptor state handling. These bug classes are effectively caught by standard debugging tools (KASAN, LOCKDEP, and page allocation tracking).\n\nBecause no uninitialized memory usage or info-leak risks are introduced, a dedicated KMSAN fuzzing session is not warranted.",
"NeedsKMSAN": false,
"Reasoning": "The patch modifies both the stmmac network driver and the core phylink subsystem (drivers/net/phy/phylink.c). Phylink is reachable in standard virtualized fuzzing environments via software-emulated USB network adapters (such as lan78xx and asix/ax8817x through USB Raw-Gadget) and DSA loop mock switch devices. The modifications introduce new state management (suspend_speed_down), flush work synchronously under RTNL lock in phylink_stop, and alter speed control transitions across suspend/resume/down paths. In addition, extensive refactoring of the stmmac datapath state machine, DMA descriptor allocation, and MTU rollback logic makes this series worth fuzzing.",
"WorthFuzzing": true
} |
| 1/1 | 2026/09/26 22:50 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit 925615988696cb7f8488eeefff14dfb3ead46259\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Sat Sep 26 22:50:45 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/drivers/net/ethernet/stmicro/stmmac/chain_mode.c b/drivers/net/ethernet/stmicro/stmmac/chain_mode.c\nindex 66025e2509e91..65243c5e539e4 100644\n--- a/drivers/net/ethernet/stmicro/stmmac/chain_mode.c\n+++ b/drivers/net/ethernet/stmicro/stmmac/chain_mode.c\n@@ -48,7 +48,7 @@ static int jumbo_frm(struct stmmac_tx_queue *tx_q, struct sk_buff *skb,\n \n \twhile (len != 0) {\n \t\ttx_q-\u003etx_skbuff[entry] = NULL;\n-\t\tentry = STMMAC_NEXT_ENTRY(entry, priv-\u003edma_conf.dma_tx_size);\n+\t\tentry = STMMAC_NEXT_ENTRY(entry, priv-\u003edma_conf-\u003edma_tx_size);\n \t\tdesc = tx_q-\u003edma_tx + entry;\n \n \t\tif (len \u003e bmax) {\n@@ -137,7 +137,7 @@ static void refill_desc3(struct stmmac_rx_queue *rx_q, struct dma_desc *p)\n \t\t */\n \t\tp-\u003edes3 = cpu_to_le32((unsigned int)(rx_q-\u003edma_rx_phy +\n \t\t\t\t (((rx_q-\u003edirty_rx) + 1) %\n-\t\t\t\t priv-\u003edma_conf.dma_rx_size) *\n+\t\t\t\t priv-\u003edma_conf-\u003edma_rx_size) *\n \t\t\t\t sizeof(struct dma_desc)));\n }\n \n@@ -154,7 +154,7 @@ static void clean_desc3(struct stmmac_tx_queue *tx_q, struct dma_desc *p)\n \t\t */\n \t\tp-\u003edes3 = cpu_to_le32((unsigned int)((tx_q-\u003edma_tx_phy +\n \t\t\t\t ((tx_q-\u003edirty_tx + 1) %\n-\t\t\t\t priv-\u003edma_conf.dma_tx_size))\n+\t\t\t\t priv-\u003edma_conf-\u003edma_tx_size))\n \t\t\t\t * sizeof(struct dma_desc)));\n }\n \ndiff --git a/drivers/net/ethernet/stmicro/stmmac/ring_mode.c b/drivers/net/ethernet/stmicro/stmmac/ring_mode.c\nindex d2f0c321661d9..0299d6a6c32bf 100644\n--- a/drivers/net/ethernet/stmicro/stmmac/ring_mode.c\n+++ b/drivers/net/ethernet/stmicro/stmmac/ring_mode.c\n@@ -52,7 +52,7 @@ static int jumbo_frm(struct stmmac_tx_queue *tx_q, struct sk_buff *skb,\n \t\tstmmac_prepare_tx_desc(priv, desc, 1, bmax, csum,\n \t\t\t\tSTMMAC_RING_MODE, 0, false, skb-\u003elen);\n \t\ttx_q-\u003etx_skbuff[entry] = NULL;\n-\t\tentry = STMMAC_NEXT_ENTRY(entry, priv-\u003edma_conf.dma_tx_size);\n+\t\tentry = STMMAC_NEXT_ENTRY(entry, priv-\u003edma_conf-\u003edma_tx_size);\n \n \t\tif (priv-\u003eextend_desc)\n \t\t\tdesc = (struct dma_desc *)(tx_q-\u003edma_etx + entry);\n@@ -102,7 +102,7 @@ static void refill_desc3(struct stmmac_rx_queue *rx_q, struct dma_desc *p)\n \tstruct stmmac_priv *priv = rx_q-\u003epriv_data;\n \n \t/* Fill DES3 in case of RING mode */\n-\tif (priv-\u003edma_conf.dma_buf_sz == BUF_SIZE_16KiB)\n+\tif (priv-\u003edma_conf-\u003edma_buf_sz == BUF_SIZE_16KiB)\n \t\tp-\u003edes3 = cpu_to_le32(le32_to_cpu(p-\u003edes2) + BUF_SIZE_8KiB);\n }\n \ndiff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h\nindex 7582fca637419..8ebf407895689 100644\n--- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h\n+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h\n@@ -258,6 +258,17 @@ struct stmmac_msi {\n \tchar int_name_tx_irq[MTL_MAX_TX_QUEUES][IFNAMSIZ + 18];\n };\n \n+enum stmmac_datapath_state {\n+\t/* No IRQs or DMA allocations owned by a successful open. */\n+\tSTMMAC_DATAPATH_DOWN,\n+\t/* Resources allocated, NAPI enabled. */\n+\tSTMMAC_DATAPATH_RUNNING,\n+\t/* Resources retained, NAPI and DMA stopped; also after failed resume. */\n+\tSTMMAC_DATAPATH_SUSPENDED,\n+\t/* Failed MTU rollback: rings retained, but no IRQs or running NAPI. */\n+\tSTMMAC_DATAPATH_HALTED,\n+};\n+\n struct stmmac_priv {\n \t/* Frequently used values are kept adjacent for cache effect */\n \tu32 tx_coal_frames[MTL_MAX_TX_QUEUES];\n@@ -280,7 +291,11 @@ struct stmmac_priv {\n \tint (*hwif_quirks)(struct stmmac_priv *priv);\n \tstruct mutex lock;\n \n-\tstruct stmmac_dma_conf dma_conf;\n+\tstruct stmmac_dma_conf *dma_conf;\n+\t/* IRQ/DMA ownership and NAPI state, serialized by RTNL. */\n+\tenum stmmac_datapath_state datapath;\n+\t/* Core sleep sequence completed, independently of datapath ownership. */\n+\tbool hw_suspended;\n \n \t/* Generic channel for NAPI */\n \tstruct stmmac_channel channel[STMMAC_CH_MAX];\n@@ -424,10 +439,6 @@ static inline bool stmmac_xdp_is_enabled(struct stmmac_priv *priv)\n \treturn !!priv-\u003exdp_prog;\n }\n \n-void stmmac_disable_rx_queue(struct stmmac_priv *priv, u32 queue);\n-void stmmac_enable_rx_queue(struct stmmac_priv *priv, u32 queue);\n-void stmmac_disable_tx_queue(struct stmmac_priv *priv, u32 queue);\n-void stmmac_enable_tx_queue(struct stmmac_priv *priv, u32 queue);\n int stmmac_xsk_wakeup(struct net_device *dev, u32 queue, u32 flags);\n struct timespec64 stmmac_calc_tas_basetime(ktime_t old_base_time,\n \t\t\t\t\t ktime_t current_time,\ndiff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c\nindex 1be5310ca766c..994350fcdbfa8 100644\n--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c\n+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c\n@@ -396,8 +396,8 @@ static void stmmac_get_ringparam(struct net_device *netdev,\n \n \tring-\u003erx_max_pending = DMA_MAX_RX_SIZE;\n \tring-\u003etx_max_pending = DMA_MAX_TX_SIZE;\n-\tring-\u003erx_pending = priv-\u003edma_conf.dma_rx_size;\n-\tring-\u003etx_pending = priv-\u003edma_conf.dma_tx_size;\n+\tring-\u003erx_pending = priv-\u003edma_conf-\u003edma_rx_size;\n+\tring-\u003etx_pending = priv-\u003edma_conf-\u003edma_tx_size;\n }\n \n static int stmmac_set_ringparam(struct net_device *netdev,\ndiff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c\nindex 3f34d491c9592..1a552455a633d 100644\n--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c\n+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c\n@@ -79,7 +79,7 @@ static int phyaddr = -1;\n module_param(phyaddr, int, 0444);\n MODULE_PARM_DESC(phyaddr, \"Physical device address\");\n \n-#define STMMAC_TX_THRESH(x)\t((x)-\u003edma_conf.dma_tx_size / 4)\n+#define STMMAC_TX_THRESH(x)\t((x)-\u003edma_conf-\u003edma_tx_size / 4)\n \n /* Limit to make sure XDP TX and slow path can coexist */\n #define STMMAC_XSK_TX_BUDGET_MAX\t256\n@@ -297,7 +297,7 @@ static void stmmac_disable_all_queues(struct stmmac_priv *priv)\n \n \t/* synchronize_rcu() needed for pending XDP buffers to drain */\n \tfor (queue = 0; queue \u003c rx_queues_cnt; queue++) {\n-\t\trx_q = \u0026priv-\u003edma_conf.rx_queue[queue];\n+\t\trx_q = \u0026priv-\u003edma_conf-\u003erx_queue[queue];\n \t\tif (rx_q-\u003exsk_pool) {\n \t\t\tsynchronize_rcu();\n \t\t\tbreak;\n@@ -356,10 +356,10 @@ static void print_pkt(unsigned char *buf, int len)\n \n static inline u32 stmmac_tx_avail(struct stmmac_priv *priv, u32 queue)\n {\n-\tstruct stmmac_tx_queue *tx_q = \u0026priv-\u003edma_conf.tx_queue[queue];\n+\tstruct stmmac_tx_queue *tx_q = \u0026priv-\u003edma_conf-\u003etx_queue[queue];\n \n \treturn CIRC_SPACE(tx_q-\u003ecur_tx, tx_q-\u003edirty_tx,\n-\t\t\t priv-\u003edma_conf.dma_tx_size);\n+\t\t\t priv-\u003edma_conf-\u003edma_tx_size);\n }\n \n static size_t stmmac_get_tx_desc_size(struct stmmac_priv *priv,\n@@ -438,7 +438,7 @@ static void stmmac_set_queue_rx_buf_size(struct stmmac_priv *priv,\n \tif (rx_q-\u003exsk_pool \u0026\u0026 rx_q-\u003ebuf_alloc_num)\n \t\tbuf_size = xsk_pool_get_rx_frame_size(rx_q-\u003exsk_pool);\n \telse\n-\t\tbuf_size = priv-\u003edma_conf.dma_buf_sz;\n+\t\tbuf_size = priv-\u003edma_conf-\u003edma_buf_sz;\n \n \tstmmac_set_dma_bfsize(priv, priv-\u003eioaddr, buf_size, chan);\n }\n@@ -450,10 +450,10 @@ static void stmmac_set_queue_rx_buf_size(struct stmmac_priv *priv,\n */\n static inline u32 stmmac_rx_dirty(struct stmmac_priv *priv, u32 queue)\n {\n-\tstruct stmmac_rx_queue *rx_q = \u0026priv-\u003edma_conf.rx_queue[queue];\n+\tstruct stmmac_rx_queue *rx_q = \u0026priv-\u003edma_conf-\u003erx_queue[queue];\n \n \treturn CIRC_CNT(rx_q-\u003ecur_rx, rx_q-\u003edirty_rx,\n-\t\t\tpriv-\u003edma_conf.dma_rx_size);\n+\t\t\tpriv-\u003edma_conf-\u003edma_rx_size);\n }\n \n static bool stmmac_eee_tx_busy(struct stmmac_priv *priv)\n@@ -463,7 +463,7 @@ static bool stmmac_eee_tx_busy(struct stmmac_priv *priv)\n \n \t/* check if all TX queues have the work finished */\n \tfor (queue = 0; queue \u003c tx_cnt; queue++) {\n-\t\tstruct stmmac_tx_queue *tx_q = \u0026priv-\u003edma_conf.tx_queue[queue];\n+\t\tstruct stmmac_tx_queue *tx_q = \u0026priv-\u003edma_conf-\u003etx_queue[queue];\n \n \t\tif (tx_q-\u003edirty_tx != tx_q-\u003ecur_tx)\n \t\t\treturn true; /* still unfinished work */\n@@ -1629,24 +1629,10 @@ static void stmmac_clear_descriptors(struct stmmac_priv *priv,\n \t\tstmmac_clear_tx_descriptors(priv, dma_conf, queue);\n }\n \n-/**\n- * stmmac_init_rx_buffers - init the RX descriptor buffer.\n- * @priv: driver private structure\n- * @dma_conf: structure to take the dma data\n- * @p: descriptor pointer\n- * @i: descriptor index\n- * @flags: gfp flag\n- * @queue: RX queue index\n- * Description: this function is called to allocate a receive buffer, perform\n- * the DMA mapping and init the descriptor.\n- */\n-static int stmmac_init_rx_buffers(struct stmmac_priv *priv,\n-\t\t\t\t struct stmmac_dma_conf *dma_conf,\n-\t\t\t\t struct dma_desc *p,\n-\t\t\t\t int i, gfp_t flags, u32 queue)\n+static int stmmac_alloc_rx_buffer(struct stmmac_priv *priv,\n+\t\t\t\t struct stmmac_rx_queue *rx_q,\n+\t\t\t\t struct stmmac_rx_buffer *buf)\n {\n-\tstruct stmmac_rx_queue *rx_q = \u0026dma_conf-\u003erx_queue[queue];\n-\tstruct stmmac_rx_buffer *buf = \u0026rx_q-\u003ebuf_pool[i];\n \tgfp_t gfp = (GFP_ATOMIC | __GFP_NOWARN);\n \n \tif (priv-\u003edma_cap.host_dma_width \u003c= 32)\n@@ -1663,19 +1649,49 @@ static int stmmac_init_rx_buffers(struct stmmac_priv *priv,\n \t\tbuf-\u003esec_page = page_pool_alloc_pages(rx_q-\u003epage_pool, gfp);\n \t\tif (!buf-\u003esec_page)\n \t\t\treturn -ENOMEM;\n-\n \t\tbuf-\u003esec_addr = page_pool_get_dma_addr(buf-\u003esec_page);\n-\t\tstmmac_set_desc_sec_addr(priv, p, buf-\u003esec_addr, true);\n-\t} else {\n-\t\tbuf-\u003esec_page = NULL;\n-\t\tstmmac_set_desc_sec_addr(priv, p, buf-\u003esec_addr, false);\n \t}\n \n+\treturn 0;\n+}\n+\n+static void stmmac_init_rx_buffer_desc(struct stmmac_priv *priv,\n+\t\t\t\t struct stmmac_dma_conf *dma_conf,\n+\t\t\t\t struct dma_desc *p,\n+\t\t\t\t struct stmmac_rx_buffer *buf)\n+{\n+\tif (buf-\u003esec_page)\n+\t\tbuf-\u003esec_addr = page_pool_get_dma_addr(buf-\u003esec_page);\n+\tstmmac_set_desc_sec_addr(priv, p, buf-\u003esec_addr, !!buf-\u003esec_page);\n \tbuf-\u003eaddr = page_pool_get_dma_addr(buf-\u003epage) + buf-\u003epage_offset;\n \n \tstmmac_set_desc_addr(priv, p, buf-\u003eaddr);\n \tif (dma_conf-\u003edma_buf_sz == BUF_SIZE_16KiB)\n \t\tstmmac_init_desc3(priv, p);\n+}\n+\n+/**\n+ * stmmac_init_rx_buffers - allocate a receive buffer and init its descriptor\n+ * @priv: driver private structure\n+ * @dma_conf: structure to take the dma data\n+ * @p: descriptor pointer\n+ * @i: descriptor index\n+ * @flags: gfp flag\n+ * @queue: RX queue index\n+ */\n+static int stmmac_init_rx_buffers(struct stmmac_priv *priv,\n+\t\t\t\t struct stmmac_dma_conf *dma_conf,\n+\t\t\t\t struct dma_desc *p,\n+\t\t\t\t int i, gfp_t flags, u32 queue)\n+{\n+\tstruct stmmac_rx_queue *rx_q = \u0026dma_conf-\u003erx_queue[queue];\n+\tstruct stmmac_rx_buffer *buf = \u0026rx_q-\u003ebuf_pool[i];\n+\tint ret;\n+\n+\tret = stmmac_alloc_rx_buffer(priv, rx_q, buf);\n+\tif (ret)\n+\t\treturn ret;\n+\tstmmac_init_rx_buffer_desc(priv, dma_conf, p, buf);\n \n \treturn 0;\n }\n@@ -1761,6 +1777,9 @@ static void dma_free_rx_skbufs(struct stmmac_priv *priv,\n \tstruct stmmac_rx_queue *rx_q = \u0026dma_conf-\u003erx_queue[queue];\n \tint i;\n \n+\tif (!rx_q-\u003ebuf_pool)\n+\t\treturn;\n+\n \tfor (i = 0; i \u003c dma_conf-\u003edma_rx_size; i++)\n \t\tstmmac_free_rx_buffer(priv, rx_q, i);\n }\n@@ -1884,17 +1903,19 @@ static int __init_dma_rx_desc_rings(struct stmmac_priv *priv,\n \trx_q-\u003exsk_pool = stmmac_get_xsk_pool(priv, queue);\n \n \tif (rx_q-\u003exsk_pool) {\n-\t\tWARN_ON(xdp_rxq_info_reg_mem_model(\u0026rx_q-\u003exdp_rxq,\n-\t\t\t\t\t\t MEM_TYPE_XSK_BUFF_POOL,\n-\t\t\t\t\t\t NULL));\n+\t\tret = xdp_rxq_info_reg_mem_model(\u0026rx_q-\u003exdp_rxq,\n+\t\t\t\t\t\t MEM_TYPE_XSK_BUFF_POOL, NULL);\n+\t\tif (ret)\n+\t\t\treturn ret;\n \t\tnetdev_info(priv-\u003edev,\n \t\t\t \"Register MEM_TYPE_XSK_BUFF_POOL RxQ-%d\\n\",\n \t\t\t queue);\n \t\txsk_pool_set_rxq_info(rx_q-\u003exsk_pool, \u0026rx_q-\u003exdp_rxq);\n \t} else {\n-\t\tWARN_ON(xdp_rxq_info_reg_mem_model(\u0026rx_q-\u003exdp_rxq,\n-\t\t\t\t\t\t MEM_TYPE_PAGE_POOL,\n-\t\t\t\t\t\t rx_q-\u003epage_pool));\n+\t\tret = xdp_rxq_info_reg_mem_model(\u0026rx_q-\u003exdp_rxq,\n+\t\t\t\t\t\t MEM_TYPE_PAGE_POOL, rx_q-\u003epage_pool);\n+\t\tif (ret)\n+\t\t\treturn ret;\n \t\tnetdev_info(priv-\u003edev,\n \t\t\t \"Register MEM_TYPE_PAGE_POOL RxQ-%d\\n\",\n \t\t\t queue);\n@@ -1956,6 +1977,8 @@ static int init_dma_rx_desc_rings(struct net_device *dev,\n \t\t\tdma_free_rx_skbufs(priv, dma_conf, queue);\n \n \t\trx_q-\u003ebuf_alloc_num = 0;\n+\t\tif (rx_q-\u003exsk_pool)\n+\t\t\txsk_pool_set_rxq_info(rx_q-\u003exsk_pool, NULL);\n \t\trx_q-\u003exsk_pool = NULL;\n \n \t\tqueue--;\n@@ -2099,6 +2122,9 @@ static void dma_free_tx_skbufs(struct stmmac_priv *priv,\n \n \ttx_q-\u003exsk_frames_done = 0;\n \n+\tif (!tx_q-\u003etx_skbuff_dma || !tx_q-\u003etx_skbuff)\n+\t\treturn;\n+\n \tfor (i = 0; i \u003c dma_conf-\u003edma_tx_size; i++)\n \t\tstmmac_free_tx_buffer(priv, dma_conf, queue, i);\n \n@@ -2119,7 +2145,64 @@ static void stmmac_free_tx_skbufs(struct stmmac_priv *priv)\n \tu8 queue;\n \n \tfor (queue = 0; queue \u003c tx_queue_cnt; queue++)\n-\t\tdma_free_tx_skbufs(priv, \u0026priv-\u003edma_conf, queue);\n+\t\tdma_free_tx_skbufs(priv, priv-\u003edma_conf, queue);\n+}\n+\n+/* NAPI is stopped, but DMA may still be using the old rings. Fill holes in\n+ * the software buffer array without changing any descriptors. If allocation\n+ * fails, the old rings can continue unchanged. Otherwise rollback after a\n+ * reset will not need to allocate buffers.\n+ */\n+static int stmmac_prepare_rx_buffers(struct stmmac_priv *priv)\n+{\n+\tstruct stmmac_dma_conf *dma_conf = priv-\u003edma_conf;\n+\tu32 queue, i;\n+\tint ret;\n+\n+\tfor (queue = 0; queue \u003c priv-\u003eplat-\u003erx_queues_to_use; queue++) {\n+\t\tstruct stmmac_rx_queue *rx_q = \u0026dma_conf-\u003erx_queue[queue];\n+\n+\t\tfor (i = 0; i \u003c dma_conf-\u003edma_rx_size; i++) {\n+\t\t\tret = stmmac_alloc_rx_buffer(priv, rx_q, \u0026rx_q-\u003ebuf_pool[i]);\n+\t\t\tif (ret)\n+\t\t\t\treturn ret;\n+\t\t}\n+\t}\n+\n+\treturn 0;\n+}\n+\n+/* Only after a successful DMA reset, and with all RX buffers prepared. */\n+static void stmmac_reinit_dma_desc(struct stmmac_priv *priv)\n+{\n+\tstruct stmmac_dma_conf *dma_conf = priv-\u003edma_conf;\n+\tu32 queue, i;\n+\n+\tstmmac_free_tx_skbufs(priv);\n+\tstmmac_reset_queues_param(priv);\n+\tinit_dma_tx_desc_rings(priv-\u003edev, dma_conf);\n+\tstmmac_clear_descriptors(priv, dma_conf);\n+\n+\tfor (queue = 0; queue \u003c priv-\u003eplat-\u003erx_queues_to_use; queue++) {\n+\t\tstruct stmmac_rx_queue *rx_q = \u0026dma_conf-\u003erx_queue[queue];\n+\n+\t\tif (rx_q-\u003estate_saved)\n+\t\t\tdev_kfree_skb_any(rx_q-\u003estate.skb);\n+\t\trx_q-\u003estate.skb = NULL;\n+\t\trx_q-\u003estate_saved = 0;\n+\t\trx_q-\u003erx_count_frames = 0;\n+\t\trx_q-\u003ebuf_alloc_num = dma_conf-\u003edma_rx_size;\n+\n+\t\tfor (i = 0; i \u003c dma_conf-\u003edma_rx_size; i++)\n+\t\t\tstmmac_init_rx_buffer_desc(priv, dma_conf,\n+\t\t\t\t\t\t stmmac_get_rx_desc(priv, rx_q, i),\n+\t\t\t\t\t\t \u0026rx_q-\u003ebuf_pool[i]);\n+\n+\t\tif (priv-\u003edescriptor_mode == STMMAC_CHAIN_MODE)\n+\t\t\tstmmac_mode_init(priv, stmmac_get_rx_desc(priv, rx_q, 0),\n+\t\t\t\t\t rx_q-\u003edma_rx_phy, dma_conf-\u003edma_rx_size,\n+\t\t\t\t\t priv-\u003eextend_desc);\n+\t}\n }\n \n /**\n@@ -2137,10 +2220,16 @@ static void __free_dma_rx_desc_resources(struct stmmac_priv *priv,\n \tvoid *addr;\n \n \t/* Release the DMA RX socket buffers */\n-\tif (rx_q-\u003exsk_pool)\n+\tif (rx_q-\u003exsk_pool) {\n \t\tdma_free_rx_xskbufs(priv, dma_conf, queue);\n-\telse\n+\t\txsk_pool_set_rxq_info(rx_q-\u003exsk_pool, NULL);\n+\t} else {\n \t\tdma_free_rx_skbufs(priv, dma_conf, queue);\n+\t}\n+\tif (rx_q-\u003estate_saved)\n+\t\tdev_kfree_skb_any(rx_q-\u003estate.skb);\n+\trx_q-\u003estate.skb = NULL;\n+\trx_q-\u003estate_saved = 0;\n \n \trx_q-\u003ebuf_alloc_num = 0;\n \trx_q-\u003exsk_pool = NULL;\n@@ -2153,7 +2242,8 @@ static void __free_dma_rx_desc_resources(struct stmmac_priv *priv,\n \n \tsize = stmmac_get_rx_desc_size(priv) * dma_conf-\u003edma_rx_size;\n \n-\tdma_free_coherent(priv-\u003edevice, size, addr, rx_q-\u003edma_rx_phy);\n+\tif (addr)\n+\t\tdma_free_coherent(priv-\u003edevice, size, addr, rx_q-\u003edma_rx_phy);\n \n \tif (xdp_rxq_info_is_reg(\u0026rx_q-\u003exdp_rxq))\n \t\txdp_rxq_info_unreg(\u0026rx_q-\u003exdp_rxq);\n@@ -2161,6 +2251,10 @@ static void __free_dma_rx_desc_resources(struct stmmac_priv *priv,\n \tkfree(rx_q-\u003ebuf_pool);\n \tif (rx_q-\u003epage_pool)\n \t\tpage_pool_destroy(rx_q-\u003epage_pool);\n+\trx_q-\u003ebuf_pool = NULL;\n+\trx_q-\u003epage_pool = NULL;\n+\trx_q-\u003edma_erx = NULL;\n+\trx_q-\u003edma_rx = NULL;\n }\n \n static void free_dma_rx_desc_resources(struct stmmac_priv *priv,\n@@ -2201,10 +2295,17 @@ static void __free_dma_tx_desc_resources(struct stmmac_priv *priv,\n \n \tsize = stmmac_get_tx_desc_size(priv, tx_q) * dma_conf-\u003edma_tx_size;\n \n-\tdma_free_coherent(priv-\u003edevice, size, addr, tx_q-\u003edma_tx_phy);\n+\tif (addr)\n+\t\tdma_free_coherent(priv-\u003edevice, size, addr, tx_q-\u003edma_tx_phy);\n \n \tkfree(tx_q-\u003etx_skbuff_dma);\n \tkfree(tx_q-\u003etx_skbuff);\n+\ttx_q-\u003etx_skbuff_dma = NULL;\n+\ttx_q-\u003etx_skbuff = NULL;\n+\ttx_q-\u003edma_etx = NULL;\n+\ttx_q-\u003edma_entx = NULL;\n+\ttx_q-\u003edma_tx = NULL;\n+\ttx_q-\u003exsk_pool = NULL;\n }\n \n static void free_dma_tx_desc_resources(struct stmmac_priv *priv,\n@@ -2411,6 +2512,8 @@ static int alloc_dma_desc_resources(struct stmmac_priv *priv,\n \t\treturn ret;\n \n \tret = alloc_dma_tx_desc_resources(priv, dma_conf);\n+\tif (ret)\n+\t\tfree_dma_rx_desc_resources(priv, dma_conf);\n \n \treturn ret;\n }\n@@ -2609,7 +2712,7 @@ static void stmmac_dma_operation_mode(struct stmmac_priv *priv)\n \n \t/* configure all channels */\n \tfor (chan = 0; chan \u003c rx_channels_count; chan++) {\n-\t\tstruct stmmac_rx_queue *rx_q = \u0026priv-\u003edma_conf.rx_queue[chan];\n+\t\tstruct stmmac_rx_queue *rx_q = \u0026priv-\u003edma_conf-\u003erx_queue[chan];\n \n \t\tqmode = priv-\u003eplat-\u003erx_queues_cfg[chan].mode_to_use;\n \n@@ -2681,7 +2784,7 @@ static const struct xsk_tx_metadata_ops stmmac_xsk_tx_metadata_ops = {\n static bool stmmac_xdp_xmit_zc(struct stmmac_priv *priv, u32 queue, u32 budget)\n {\n \tstruct netdev_queue *nq = netdev_get_tx_queue(priv-\u003edev, queue);\n-\tstruct stmmac_tx_queue *tx_q = \u0026priv-\u003edma_conf.tx_queue[queue];\n+\tstruct stmmac_tx_queue *tx_q = \u0026priv-\u003edma_conf-\u003etx_queue[queue];\n \tstruct stmmac_txq_stats *txq_stats = \u0026priv-\u003exstats.txq_stats[queue];\n \tbool csum = !priv-\u003eplat-\u003etx_queues_cfg[queue].coe_unsupported;\n \tstruct xsk_buff_pool *pool = tx_q-\u003exsk_pool;\n@@ -2770,7 +2873,7 @@ static bool stmmac_xdp_xmit_zc(struct stmmac_priv *priv, u32 queue, u32 budget)\n \t\txsk_tx_metadata_to_compl(meta,\n \t\t\t\t\t \u0026tx_q-\u003etx_skbuff_dma[entry].xsk_meta);\n \n-\t\ttx_q-\u003ecur_tx = STMMAC_NEXT_ENTRY(tx_q-\u003ecur_tx, priv-\u003edma_conf.dma_tx_size);\n+\t\ttx_q-\u003ecur_tx = STMMAC_NEXT_ENTRY(tx_q-\u003ecur_tx, priv-\u003edma_conf-\u003edma_tx_size);\n \t\tentry = tx_q-\u003ecur_tx;\n \t}\n \tu64_stats_update_begin(\u0026txq_stats-\u003enapi_syncp);\n@@ -2818,7 +2921,7 @@ static void stmmac_bump_dma_threshold(struct stmmac_priv *priv, u32 chan)\n static int stmmac_tx_clean(struct stmmac_priv *priv, int budget, u32 queue,\n \t\t\t bool *pending_packets)\n {\n-\tstruct stmmac_tx_queue *tx_q = \u0026priv-\u003edma_conf.tx_queue[queue];\n+\tstruct stmmac_tx_queue *tx_q = \u0026priv-\u003edma_conf-\u003etx_queue[queue];\n \tstruct stmmac_txq_stats *txq_stats = \u0026priv-\u003exstats.txq_stats[queue];\n \tunsigned int bytes_compl = 0, pkts_compl = 0;\n \tunsigned int entry, xmits = 0, count = 0;\n@@ -2831,7 +2934,7 @@ static int stmmac_tx_clean(struct stmmac_priv *priv, int budget, u32 queue,\n \tentry = tx_q-\u003edirty_tx;\n \n \t/* Try to clean all TX complete frame in 1 shot */\n-\twhile ((entry != tx_q-\u003ecur_tx) \u0026\u0026 count \u003c priv-\u003edma_conf.dma_tx_size) {\n+\twhile ((entry != tx_q-\u003ecur_tx) \u0026\u0026 count \u003c priv-\u003edma_conf-\u003edma_tx_size) {\n \t\tstruct xdp_frame *xdpf;\n \t\tstruct sk_buff *skb;\n \t\tstruct dma_desc *p;\n@@ -2938,7 +3041,7 @@ static int stmmac_tx_clean(struct stmmac_priv *priv, int budget, u32 queue,\n \n \t\tstmmac_release_tx_desc(priv, p, priv-\u003edescriptor_mode);\n \n-\t\tentry = STMMAC_NEXT_ENTRY(entry, priv-\u003edma_conf.dma_tx_size);\n+\t\tentry = STMMAC_NEXT_ENTRY(entry, priv-\u003edma_conf-\u003edma_tx_size);\n \t}\n \ttx_q-\u003edirty_tx = entry;\n \n@@ -3006,13 +3109,13 @@ static int stmmac_tx_clean(struct stmmac_priv *priv, int budget, u32 queue,\n */\n static void stmmac_tx_err(struct stmmac_priv *priv, u32 chan)\n {\n-\tstruct stmmac_tx_queue *tx_q = \u0026priv-\u003edma_conf.tx_queue[chan];\n+\tstruct stmmac_tx_queue *tx_q = \u0026priv-\u003edma_conf-\u003etx_queue[chan];\n \n \tnetif_tx_stop_queue(netdev_get_tx_queue(priv-\u003edev, chan));\n \n \tstmmac_stop_tx_dma(priv, chan);\n-\tdma_free_tx_skbufs(priv, \u0026priv-\u003edma_conf, chan);\n-\tstmmac_clear_tx_descriptors(priv, \u0026priv-\u003edma_conf, chan);\n+\tdma_free_tx_skbufs(priv, priv-\u003edma_conf, chan);\n+\tstmmac_clear_tx_descriptors(priv, priv-\u003edma_conf, chan);\n \tstmmac_reset_tx_queue(priv, chan);\n \tstmmac_init_tx_chan(priv, priv-\u003eioaddr, priv-\u003eplat-\u003edma_cfg,\n \t\t\t tx_q-\u003edma_tx_phy, chan);\n@@ -3073,8 +3176,8 @@ static int stmmac_napi_check(struct stmmac_priv *priv, u32 chan, u32 dir)\n {\n \tint status = stmmac_dma_interrupt_status(priv, priv-\u003eioaddr,\n \t\t\t\t\t\t \u0026priv-\u003exstats, chan, dir);\n-\tstruct stmmac_rx_queue *rx_q = \u0026priv-\u003edma_conf.rx_queue[chan];\n-\tstruct stmmac_tx_queue *tx_q = \u0026priv-\u003edma_conf.tx_queue[chan];\n+\tstruct stmmac_rx_queue *rx_q = \u0026priv-\u003edma_conf-\u003erx_queue[chan];\n+\tstruct stmmac_tx_queue *tx_q = \u0026priv-\u003edma_conf-\u003etx_queue[chan];\n \tstruct stmmac_channel *ch = \u0026priv-\u003echannel[chan];\n \tstruct napi_struct *rx_napi;\n \tstruct napi_struct *tx_napi;\n@@ -3255,12 +3358,13 @@ static int stmmac_prereset_configure(struct stmmac_priv *priv)\n /**\n * stmmac_init_dma_engine - DMA init.\n * @priv: driver private structure\n+ * @reinit: rebuild the retained rings after a successful reset\n * Description:\n * It inits the DMA invoking the specific MAC/GMAC callback.\n * Some DMA parameters can be passed from the platform;\n * in case of these are not passed a default is kept for the MAC or GMAC.\n */\n-static int stmmac_init_dma_engine(struct stmmac_priv *priv)\n+static int stmmac_init_dma_engine(struct stmmac_priv *priv, bool reinit)\n {\n \tu8 rx_channels_count = priv-\u003eplat-\u003erx_queues_to_use;\n \tu8 tx_channels_count = priv-\u003eplat-\u003etx_queues_to_use;\n@@ -3280,6 +3384,9 @@ static int stmmac_init_dma_engine(struct stmmac_priv *priv)\n \t\treturn ret;\n \t}\n \n+\tif (reinit)\n+\t\tstmmac_reinit_dma_desc(priv);\n+\n \t/* DMA Configuration */\n \tstmmac_dma_init(priv, priv-\u003eioaddr, priv-\u003eplat-\u003edma_cfg);\n \n@@ -3294,7 +3401,7 @@ static int stmmac_init_dma_engine(struct stmmac_priv *priv)\n \n \t/* DMA RX Channel Configuration */\n \tfor (chan = 0; chan \u003c rx_channels_count; chan++) {\n-\t\trx_q = \u0026priv-\u003edma_conf.rx_queue[chan];\n+\t\trx_q = \u0026priv-\u003edma_conf-\u003erx_queue[chan];\n \n \t\tstmmac_init_rx_chan(priv, priv-\u003eioaddr, priv-\u003eplat-\u003edma_cfg,\n \t\t\t\t rx_q-\u003edma_rx_phy, chan);\n@@ -3305,7 +3412,7 @@ static int stmmac_init_dma_engine(struct stmmac_priv *priv)\n \n \t/* DMA TX Channel Configuration */\n \tfor (chan = 0; chan \u003c tx_channels_count; chan++) {\n-\t\ttx_q = \u0026priv-\u003edma_conf.tx_queue[chan];\n+\t\ttx_q = \u0026priv-\u003edma_conf-\u003etx_queue[chan];\n \n \t\tstmmac_init_tx_chan(priv, priv-\u003eioaddr, priv-\u003eplat-\u003edma_cfg,\n \t\t\t\t tx_q-\u003edma_tx_phy, chan);\n@@ -3318,7 +3425,7 @@ static int stmmac_init_dma_engine(struct stmmac_priv *priv)\n \n static void stmmac_tx_timer_arm(struct stmmac_priv *priv, u32 queue)\n {\n-\tstruct stmmac_tx_queue *tx_q = \u0026priv-\u003edma_conf.tx_queue[queue];\n+\tstruct stmmac_tx_queue *tx_q = \u0026priv-\u003edma_conf-\u003etx_queue[queue];\n \tu32 tx_coal_timer = priv-\u003etx_coal_timer[queue];\n \tstruct stmmac_channel *ch;\n \tstruct napi_struct *napi;\n@@ -3386,7 +3493,7 @@ static void stmmac_init_coalesce(struct stmmac_priv *priv)\n \tu8 chan;\n \n \tfor (chan = 0; chan \u003c tx_channel_count; chan++) {\n-\t\tstruct stmmac_tx_queue *tx_q = \u0026priv-\u003edma_conf.tx_queue[chan];\n+\t\tstruct stmmac_tx_queue *tx_q = \u0026priv-\u003edma_conf-\u003etx_queue[chan];\n \n \t\tpriv-\u003etx_coal_frames[chan] = STMMAC_TX_FRAMES;\n \t\tpriv-\u003etx_coal_timer[chan] = STMMAC_COAL_TX_TIMER;\n@@ -3407,12 +3514,12 @@ static void stmmac_set_rings_length(struct stmmac_priv *priv)\n \t/* set TX ring length */\n \tfor (chan = 0; chan \u003c tx_channels_count; chan++)\n \t\tstmmac_set_tx_ring_len(priv, priv-\u003eioaddr,\n-\t\t\t\t (priv-\u003edma_conf.dma_tx_size - 1), chan);\n+\t\t\t\t (priv-\u003edma_conf-\u003edma_tx_size - 1), chan);\n \n \t/* set RX ring length */\n \tfor (chan = 0; chan \u003c rx_channels_count; chan++)\n \t\tstmmac_set_rx_ring_len(priv, priv-\u003eioaddr,\n-\t\t\t\t (priv-\u003edma_conf.dma_rx_size - 1), chan);\n+\t\t\t\t (priv-\u003edma_conf-\u003edma_rx_size - 1), chan);\n }\n \n /**\n@@ -3621,12 +3728,13 @@ static bool stmmac_tso_channel_permitted(struct stmmac_priv *priv,\n \t\t\t\t\t unsigned int chan)\n {\n \t/* TSO and TBS cannot co-exist */\n-\treturn !(priv-\u003edma_conf.tx_queue[chan].tbs \u0026 STMMAC_TBS_AVAIL);\n+\treturn !(priv-\u003edma_conf-\u003etx_queue[chan].tbs \u0026 STMMAC_TBS_AVAIL);\n }\n \n /**\n * stmmac_hw_setup - setup mac in a usable state.\n * @dev : pointer to the device structure.\n+ * @reinit: rebuild retained descriptor rings after the DMA reset\n * Description:\n * this is the main function to setup the HW in a usable state because the\n * dma engine is reset, the core registers are configured (e.g. AXI,\n@@ -3636,7 +3744,7 @@ static bool stmmac_tso_channel_permitted(struct stmmac_priv *priv,\n * 0 on success and an appropriate (-)ve integer as defined in errno.h\n * file on failure.\n */\n-static int stmmac_hw_setup(struct net_device *dev)\n+static int stmmac_hw_setup(struct net_device *dev, bool reinit)\n {\n \tstruct stmmac_priv *priv = netdev_priv(dev);\n \tu8 rx_cnt = priv-\u003eplat-\u003erx_queues_to_use;\n@@ -3658,7 +3766,7 @@ static int stmmac_hw_setup(struct net_device *dev)\n \tphylink_rx_clk_stop_block(priv-\u003ephylink);\n \n \t/* DMA initialization and SW reset */\n-\tret = stmmac_init_dma_engine(priv);\n+\tret = stmmac_init_dma_engine(priv, reinit);\n \tif (ret \u003c 0) {\n \t\tphylink_rx_clk_stop_unblock(priv-\u003ephylink);\n \t\tnetdev_err(priv-\u003edev, \"%s: DMA engine initialization failed\\n\",\n@@ -3739,7 +3847,7 @@ static int stmmac_hw_setup(struct net_device *dev)\n \n \t/* TBS */\n \tfor (chan = 0; chan \u003c tx_cnt; chan++) {\n-\t\tstruct stmmac_tx_queue *tx_q = \u0026priv-\u003edma_conf.tx_queue[chan];\n+\t\tstruct stmmac_tx_queue *tx_q = \u0026priv-\u003edma_conf-\u003etx_queue[chan];\n \t\tint enable = tx_q-\u003etbs \u0026 STMMAC_TBS_AVAIL;\n \n \t\tstmmac_enable_tbs(priv, priv-\u003eioaddr, enable, chan);\n@@ -3774,8 +3882,7 @@ static void stmmac_free_irq(struct net_device *dev,\n \t\tfor (j = irq_idx - 1; msi \u0026\u0026 j \u003e= 0; j--) {\n \t\t\tif (msi-\u003etx_irq[j] \u003e 0) {\n \t\t\t\tirq_set_affinity_hint(msi-\u003etx_irq[j], NULL);\n-\t\t\t\tfree_irq(msi-\u003etx_irq[j],\n-\t\t\t\t\t \u0026priv-\u003edma_conf.tx_queue[j]);\n+\t\t\t\tfree_irq(msi-\u003etx_irq[j], \u0026priv-\u003echannel[j]);\n \t\t\t}\n \t\t}\n \t\tirq_idx = priv-\u003eplat-\u003erx_queues_to_use;\n@@ -3784,8 +3891,7 @@ static void stmmac_free_irq(struct net_device *dev,\n \t\tfor (j = irq_idx - 1; msi \u0026\u0026 j \u003e= 0; j--) {\n \t\t\tif (msi-\u003erx_irq[j] \u003e 0) {\n \t\t\t\tirq_set_affinity_hint(msi-\u003erx_irq[j], NULL);\n-\t\t\t\tfree_irq(msi-\u003erx_irq[j],\n-\t\t\t\t\t \u0026priv-\u003edma_conf.rx_queue[j]);\n+\t\t\t\tfree_irq(msi-\u003erx_irq[j], \u0026priv-\u003echannel[j]);\n \t\t\t}\n \t\t}\n \n@@ -3797,13 +3903,13 @@ static void stmmac_free_irq(struct net_device *dev,\n \t\t\tfree_irq(msi-\u003esfty_ce_irq, dev);\n \t\tfallthrough;\n \tcase REQ_IRQ_ERR_SFTY_CE:\n-\t\tif (priv-\u003ewol_irq \u003e 0 \u0026\u0026 priv-\u003ewol_irq != dev-\u003eirq)\n-\t\t\tfree_irq(priv-\u003ewol_irq, dev);\n-\t\tfallthrough;\n-\tcase REQ_IRQ_ERR_SFTY:\n \t\tif (priv-\u003esfty_irq \u003e 0 \u0026\u0026 priv-\u003esfty_irq != dev-\u003eirq)\n \t\t\tfree_irq(priv-\u003esfty_irq, dev);\n \t\tfallthrough;\n+\tcase REQ_IRQ_ERR_SFTY:\n+\t\tif (priv-\u003ewol_irq \u003e 0 \u0026\u0026 priv-\u003ewol_irq != dev-\u003eirq)\n+\t\t\tfree_irq(priv-\u003ewol_irq, dev);\n+\t\tfallthrough;\n \tcase REQ_IRQ_ERR_WOL:\n \t\tfree_irq(dev-\u003eirq, dev);\n \t\tfallthrough;\n@@ -3939,7 +4045,7 @@ static int stmmac_request_irq_multi_msi(struct net_device *dev)\n \t\tsprintf(int_name, \"%s:%s-%d\", dev-\u003ename, \"rx\", i);\n \t\tret = request_irq(msi-\u003erx_irq[i],\n \t\t\t\t stmmac_msi_intr_rx,\n-\t\t\t\t 0, int_name, \u0026priv-\u003edma_conf.rx_queue[i]);\n+\t\t\t\t 0, int_name, \u0026priv-\u003echannel[i]);\n \t\tif (unlikely(ret \u003c 0)) {\n \t\t\tnetdev_err(priv-\u003edev,\n \t\t\t\t \"%s: alloc rx-%d MSI %d (error: %d)\\n\",\n@@ -3963,7 +4069,7 @@ static int stmmac_request_irq_multi_msi(struct net_device *dev)\n \t\tsprintf(int_name, \"%s:%s-%d\", dev-\u003ename, \"tx\", i);\n \t\tret = request_irq(msi-\u003etx_irq[i],\n \t\t\t\t stmmac_msi_intr_tx,\n-\t\t\t\t 0, int_name, \u0026priv-\u003edma_conf.tx_queue[i]);\n+\t\t\t\t 0, int_name, \u0026priv-\u003echannel[i]);\n \t\tif (unlikely(ret \u003c 0)) {\n \t\t\tnetdev_err(priv-\u003edev,\n \t\t\t\t \"%s: alloc tx-%d MSI %d (error: %d)\\n\",\n@@ -4050,14 +4156,40 @@ static int stmmac_request_irq(struct net_device *dev)\n \treturn ret;\n }\n \n+/* Balance disable_irq()/enable_irq() for every registered IRQ, including\n+ * shared lines. Unlike freeing and requesting IRQs, this cannot fail.\n+ */\n+static void stmmac_set_irq_state(struct stmmac_priv *priv, bool enable)\n+{\n+\tvoid (*set_state)(unsigned int) = enable ? enable_irq : disable_irq;\n+\tstruct stmmac_msi *msi = priv-\u003emsi;\n+\tint irq = priv-\u003edev-\u003eirq;\n+\tu32 i;\n+\n+\tset_state(irq);\n+\tif (priv-\u003ewol_irq \u003e 0 \u0026\u0026 priv-\u003ewol_irq != irq)\n+\t\tset_state(priv-\u003ewol_irq);\n+\tif (priv-\u003esfty_irq \u003e 0 \u0026\u0026 priv-\u003esfty_irq != irq)\n+\t\tset_state(priv-\u003esfty_irq);\n+\tif (!msi)\n+\t\treturn;\n+\tif (msi-\u003esfty_ce_irq \u003e 0 \u0026\u0026 msi-\u003esfty_ce_irq != irq)\n+\t\tset_state(msi-\u003esfty_ce_irq);\n+\tif (msi-\u003esfty_ue_irq \u003e 0 \u0026\u0026 msi-\u003esfty_ue_irq != irq)\n+\t\tset_state(msi-\u003esfty_ue_irq);\n+\tfor (i = 0; i \u003c priv-\u003eplat-\u003erx_queues_to_use; i++)\n+\t\tif (msi-\u003erx_irq[i] \u003e 0)\n+\t\t\tset_state(msi-\u003erx_irq[i]);\n+\tfor (i = 0; i \u003c priv-\u003eplat-\u003etx_queues_to_use; i++)\n+\t\tif (msi-\u003etx_irq[i] \u003e 0)\n+\t\t\tset_state(msi-\u003etx_irq[i]);\n+}\n+\n /**\n- * stmmac_setup_dma_desc - Generate a dma_conf and allocate DMA queue\n- * @priv: driver private structure\n- * @mtu: MTU to setup the dma queue and buf with\n- * Description: Allocate and generate a dma_conf based on the provided MTU.\n- * Allocate the Tx/Rx DMA queue and init them.\n- * Return value:\n- * the dma_conf allocated struct on success and an appropriate ERR_PTR on failure.\n+ * stmmac_setup_dma_desc - allocate and initialize a DMA configuration\n+ * @priv: driver private structure\n+ * @mtu: MTU to size the receive buffers for\n+ * Return: the allocated configuration, or an ERR_PTR on failure\n */\n static struct stmmac_dma_conf *\n stmmac_setup_dma_desc(struct stmmac_priv *priv, unsigned int mtu)\n@@ -4087,8 +4219,8 @@ stmmac_setup_dma_desc(struct stmmac_priv *priv, unsigned int mtu)\n \t/* Chose the tx/rx size from the already defined one in the\n \t * priv struct. (if defined)\n \t */\n-\tdma_conf-\u003edma_tx_size = priv-\u003edma_conf.dma_tx_size;\n-\tdma_conf-\u003edma_rx_size = priv-\u003edma_conf.dma_rx_size;\n+\tdma_conf-\u003edma_tx_size = priv-\u003edma_conf-\u003edma_tx_size;\n+\tdma_conf-\u003edma_rx_size = priv-\u003edma_conf-\u003edma_rx_size;\n \n \tif (!dma_conf-\u003edma_tx_size)\n \t\tdma_conf-\u003edma_tx_size = DMA_DEFAULT_TX_SIZE;\n@@ -4102,6 +4234,7 @@ stmmac_setup_dma_desc(struct stmmac_priv *priv, unsigned int mtu)\n \n \t\t/* Setup per-TXQ tbs flag before TX descriptor alloc */\n \t\ttx_q-\u003etbs |= tbs_en ? STMMAC_TBS_AVAIL : 0;\n+\t\ttx_q-\u003etbs |= priv-\u003edma_conf-\u003etx_queue[chan].tbs \u0026 STMMAC_TBS_EN;\n \t}\n \n \tret = alloc_dma_desc_resources(priv, dma_conf);\n@@ -4127,6 +4260,37 @@ stmmac_setup_dma_desc(struct stmmac_priv *priv, unsigned int mtu)\n \treturn ERR_PTR(ret);\n }\n \n+/* Finish core sleep state even if the platform resume callback failed. */\n+static int stmmac_resume_hw(struct stmmac_priv *priv)\n+{\n+\tint ret;\n+\n+\tif (!priv-\u003ehw_suspended)\n+\t\treturn 0;\n+\n+\t/* Use the state installed by suspend, not a subsequently changed WoL\n+\t * setting. Clear PMT even when a different device caused the wakeup.\n+\t */\n+\tif (priv-\u003eirq_wake) {\n+\t\tmutex_lock(\u0026priv-\u003elock);\n+\t\tstmmac_pmt(priv, priv-\u003ehw, 0);\n+\t\tmutex_unlock(\u0026priv-\u003elock);\n+\t\tpriv-\u003eirq_wake = 0;\n+\t} else {\n+\t\tret = pinctrl_pm_select_default_state(priv-\u003edevice);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t\tif (priv-\u003emii) {\n+\t\t\tret = stmmac_mdio_reset(priv-\u003emii);\n+\t\t\tif (ret)\n+\t\t\t\treturn ret;\n+\t\t}\n+\t}\n+\tpriv-\u003ehw_suspended = false;\n+\n+\treturn 0;\n+}\n+\n /**\n * __stmmac_open - open entry point of the driver\n * @dev : pointer to the device structure.\n@@ -4144,10 +4308,8 @@ static int __stmmac_open(struct net_device *dev,\n \tu8 chan;\n \tint ret;\n \n-\tfor (int i = 0; i \u003c priv-\u003eplat-\u003etx_queues_to_use; i++)\n-\t\tif (priv-\u003edma_conf.tx_queue[i].tbs \u0026 STMMAC_TBS_EN)\n-\t\t\tdma_conf-\u003etx_queue[i].tbs = priv-\u003edma_conf.tx_queue[i].tbs;\n-\tmemcpy(\u0026priv-\u003edma_conf, dma_conf, sizeof(*dma_conf));\n+\t/* Keep RXQ metadata and timers at their registered addresses. */\n+\tpriv-\u003edma_conf = dma_conf;\n \n \t/* The PHY is suspended when the interface is reopened without\n \t * disconnecting the PHY, e.g. on MTU change. IEEE 802.3 allows PHYs\n@@ -4160,7 +4322,7 @@ static int __stmmac_open(struct net_device *dev,\n \n \tstmmac_reset_queues_param(priv);\n \n-\tret = stmmac_hw_setup(dev);\n+\tret = stmmac_hw_setup(dev, false);\n \tif (ret \u003c 0) {\n \t\tnetdev_err(priv-\u003edev, \"%s: Hw setup failed\\n\", __func__);\n \t\tgoto init_error;\n@@ -4181,6 +4343,7 @@ static int __stmmac_open(struct net_device *dev,\n \tstmmac_enable_all_queues(priv);\n \tnetif_tx_start_all_queues(priv-\u003edev);\n \tstmmac_enable_all_dma_irq(priv);\n+\tpriv-\u003edatapath = STMMAC_DATAPATH_RUNNING;\n \n \treturn 0;\n \n@@ -4188,16 +4351,19 @@ static int __stmmac_open(struct net_device *dev,\n \tphylink_stop(priv-\u003ephylink);\n \n \tfor (chan = 0; chan \u003c priv-\u003eplat-\u003etx_queues_to_use; chan++)\n-\t\thrtimer_cancel(\u0026priv-\u003edma_conf.tx_queue[chan].txtimer);\n+\t\thrtimer_cancel(\u0026priv-\u003edma_conf-\u003etx_queue[chan].txtimer);\n \n \tstmmac_release_ptp(priv);\n init_error:\n+\tstmmac_stop_all_dma(priv);\n+\tstmmac_mac_set(priv, priv-\u003eioaddr, false);\n \treturn ret;\n }\n \n static int stmmac_open(struct net_device *dev)\n {\n \tstruct stmmac_priv *priv = netdev_priv(dev);\n+\tstruct stmmac_dma_conf *old_conf = priv-\u003edma_conf;\n \tstruct stmmac_dma_conf *dma_conf;\n \tint ret;\n \n@@ -4213,6 +4379,10 @@ static int stmmac_open(struct net_device *dev)\n \tif (ret \u003c 0)\n \t\tgoto err_dma_resources;\n \n+\tret = stmmac_resume_hw(priv);\n+\tif (ret)\n+\t\tgoto err_runtime_pm;\n+\n \tret = stmmac_init_phy(dev);\n \tif (ret)\n \t\tgoto err_runtime_pm;\n@@ -4227,7 +4397,7 @@ static int stmmac_open(struct net_device *dev)\n \tif (ret)\n \t\tgoto err_serdes;\n \n-\tkfree(dma_conf);\n+\tkfree(old_conf);\n \n \t/* We may have called phylink_speed_down before */\n \tphylink_speed_up(priv-\u003ephylink);\n@@ -4241,34 +4411,65 @@ static int stmmac_open(struct net_device *dev)\n err_runtime_pm:\n \tpm_runtime_put(priv-\u003edevice);\n err_dma_resources:\n+\tpriv-\u003edma_conf = old_conf;\n \tfree_dma_desc_resources(priv, dma_conf);\n \tkfree(dma_conf);\n \treturn ret;\n }\n \n-static void __stmmac_release(struct net_device *dev)\n+static void stmmac_stop_tx_queues(struct stmmac_priv *priv)\n {\n-\tstruct stmmac_priv *priv = netdev_priv(dev);\n \tu8 chan;\n \n-\t/* Stop and disconnect the PHY */\n-\tphylink_stop(priv-\u003ephylink);\n+\tnetif_tx_disable(priv-\u003edev);\n \n-\tstmmac_disable_all_queues(priv);\n+\t/* A poll function can still arm a timer after napi_complete_done().\n+\t * Drain those poll tails and in-flight transmitters before cancelling\n+\t * the timers, so none can be rearmed after their final cancellation.\n+\t */\n+\tsynchronize_net();\n \n \tfor (chan = 0; chan \u003c priv-\u003eplat-\u003etx_queues_to_use; chan++)\n-\t\thrtimer_cancel(\u0026priv-\u003edma_conf.tx_queue[chan].txtimer);\n+\t\thrtimer_cancel(\u0026priv-\u003edma_conf-\u003etx_queue[chan].txtimer);\n+}\n \n-\tnetif_tx_disable(dev);\n+/* Quiesce NAPI and transmit queues without releasing their resources. */\n+static void stmmac_quiesce(struct stmmac_priv *priv)\n+{\n+\tstmmac_disable_all_queues(priv);\n+\tstmmac_stop_tx_queues(priv);\n+}\n+\n+static void __stmmac_release(struct net_device *dev)\n+{\n+\tstruct stmmac_priv *priv = netdev_priv(dev);\n+\tenum stmmac_datapath_state state = priv-\u003edatapath;\n+\n+\t/* There may be no resources left after detached XDP reconfiguration. */\n+\tif (priv-\u003edatapath == STMMAC_DATAPATH_DOWN)\n+\t\treturn;\n+\n+\tphylink_stop(priv-\u003ephylink);\n+\n+\t/* Suspend retains the resources, but has already stopped activity. */\n+\tif (priv-\u003edatapath == STMMAC_DATAPATH_RUNNING)\n+\t\tstmmac_quiesce(priv);\n+\tpriv-\u003edatapath = STMMAC_DATAPATH_DOWN;\n \n \t/* Free the IRQ lines */\n-\tstmmac_free_irq(dev, REQ_IRQ_ERR_ALL, 0);\n+\tif (state != STMMAC_DATAPATH_HALTED)\n+\t\tstmmac_free_irq(dev, REQ_IRQ_ERR_ALL, 0);\n \n-\t/* Stop TX/RX DMA and clear the descriptors */\n+\t/* TX error IRQs can restart a queue after the first quiescence. */\n+\tstmmac_stop_tx_queues(priv);\n+\n+\t/* Stop TX/RX DMA after draining IRQ handlers which can restart it. */\n \tstmmac_stop_all_dma(priv);\n+\t/* Link resolution need not have reached mac_link_up() yet. */\n+\tstmmac_mac_set(priv, priv-\u003eioaddr, false);\n \n \t/* Release and free the Rx/Tx resources */\n-\tfree_dma_desc_resources(priv, \u0026priv-\u003edma_conf);\n+\tfree_dma_desc_resources(priv, priv-\u003edma_conf);\n \n \tstmmac_release_ptp(priv);\n \n@@ -4285,6 +4486,15 @@ static void __stmmac_release(struct net_device *dev)\n static int stmmac_release(struct net_device *dev)\n {\n \tstruct stmmac_priv *priv = netdev_priv(dev);\n+\tint ret;\n+\n+\t/* Resume may have failed before restoring pins or disabling MAC wake.\n+\t * Complete that cleanup without restarting the link or the datapath.\n+\t * If it fails, keep hw_suspended set so a fresh open can retry it.\n+\t */\n+\tret = stmmac_resume_hw(priv);\n+\tif (ret)\n+\t\tnetdev_err(dev, \"failed to restore hardware sleep state: %d\\n\", ret);\n \n \t/* If the PHY or MAC has WoL enabled, then the PHY will not be\n \t * suspended when phylink_stop() is called below. Set the PHY\n@@ -4298,6 +4508,8 @@ static int stmmac_release(struct net_device *dev)\n \tstmmac_legacy_serdes_power_down(priv);\n \tphylink_disconnect_phy(priv-\u003ephylink);\n \tpm_runtime_put(priv-\u003edevice);\n+\t/* Allow a fresh open after a failed MTU reopen or resume. */\n+\tnetif_device_attach(dev);\n \n \treturn 0;\n }\n@@ -4322,7 +4534,7 @@ static bool stmmac_vlan_insert(struct stmmac_priv *priv, struct sk_buff *skb,\n \t\treturn false;\n \n \tstmmac_set_tx_owner(priv, p);\n-\ttx_q-\u003ecur_tx = STMMAC_NEXT_ENTRY(tx_q-\u003ecur_tx, priv-\u003edma_conf.dma_tx_size);\n+\ttx_q-\u003ecur_tx = STMMAC_NEXT_ENTRY(tx_q-\u003ecur_tx, priv-\u003edma_conf-\u003edma_tx_size);\n \treturn true;\n }\n \n@@ -4342,7 +4554,7 @@ static void stmmac_tso_allocator(struct stmmac_priv *priv, u32 *entry,\n \t\t\t\t dma_addr_t des, int total_len,\n \t\t\t\t bool last_segment, u32 queue)\n {\n-\tstruct stmmac_tx_queue *tx_q = \u0026priv-\u003edma_conf.tx_queue[queue];\n+\tstruct stmmac_tx_queue *tx_q = \u0026priv-\u003edma_conf-\u003etx_queue[queue];\n \tstruct dma_desc *desc;\n \tu32 buff_size;\n \tint tmp_len;\n@@ -4352,7 +4564,7 @@ static void stmmac_tso_allocator(struct stmmac_priv *priv, u32 *entry,\n \twhile (tmp_len \u003e 0) {\n \t\tdma_addr_t curr_addr;\n \n-\t\t*entry = STMMAC_NEXT_ENTRY(*entry, priv-\u003edma_conf.dma_tx_size);\n+\t\t*entry = STMMAC_NEXT_ENTRY(*entry, priv-\u003edma_conf-\u003edma_tx_size);\n \t\tWARN_ON(tx_q-\u003etx_skbuff[*entry]);\n \n \t\tif (tx_q-\u003etbs \u0026 STMMAC_TBS_AVAIL)\n@@ -4376,7 +4588,7 @@ static void stmmac_tso_allocator(struct stmmac_priv *priv, u32 *entry,\n \n static void stmmac_flush_tx_descriptors(struct stmmac_priv *priv, int queue)\n {\n-\tstruct stmmac_tx_queue *tx_q = \u0026priv-\u003edma_conf.tx_queue[queue];\n+\tstruct stmmac_tx_queue *tx_q = \u0026priv-\u003edma_conf-\u003etx_queue[queue];\n \n \t/* The own bit must be the latest setting done when prepare the\n \t * descriptor and then barrier is needed to make sure that\n@@ -4529,7 +4741,7 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)\n \tnfrags = skb_shinfo(skb)-\u003enr_frags;\n \tqueue = skb_get_queue_mapping(skb);\n \n-\ttx_q = \u0026priv-\u003edma_conf.tx_queue[queue];\n+\ttx_q = \u0026priv-\u003edma_conf-\u003etx_queue[queue];\n \ttxq_stats = \u0026priv-\u003exstats.txq_stats[queue];\n \tfirst_tx = tx_q-\u003ecur_tx;\n \n@@ -4567,7 +4779,7 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)\n \t\tstmmac_set_mss(priv, mss_desc, mss);\n \t\ttx_q-\u003emss = mss;\n \t\ttx_q-\u003ecur_tx = STMMAC_NEXT_ENTRY(tx_q-\u003ecur_tx,\n-\t\t\t\t\t\tpriv-\u003edma_conf.dma_tx_size);\n+\t\t\t\t\t\tpriv-\u003edma_conf-\u003edma_tx_size);\n \t\tWARN_ON(tx_q-\u003etx_skbuff[tx_q-\u003ecur_tx]);\n \t}\n \n@@ -4638,7 +4850,7 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)\n \n \t/* Manage tx mitigation */\n \ttx_packets = CIRC_CNT(tx_q-\u003ecur_tx + 1, first_tx,\n-\t\t\t priv-\u003edma_conf.dma_tx_size);\n+\t\t\t priv-\u003edma_conf-\u003edma_tx_size);\n \ttx_q-\u003etx_count_frames += tx_packets;\n \n \tif ((skb_shinfo(skb)-\u003etx_flags \u0026 SKBTX_HW_TSTAMP) \u0026\u0026 priv-\u003ehwts_tx_en)\n@@ -4670,7 +4882,7 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)\n \t * ndo_start_xmit will fill this descriptor the next time it's\n \t * called and stmmac_tx_clean may clean up to this descriptor.\n \t */\n-\ttx_q-\u003ecur_tx = STMMAC_NEXT_ENTRY(tx_q-\u003ecur_tx, priv-\u003edma_conf.dma_tx_size);\n+\ttx_q-\u003ecur_tx = STMMAC_NEXT_ENTRY(tx_q-\u003ecur_tx, priv-\u003edma_conf-\u003edma_tx_size);\n \n \tif (unlikely(stmmac_tx_avail(priv, queue) \u003c= (MAX_SKB_FRAGS + 1))) {\n \t\tnetif_dbg(priv, hw, priv-\u003edev, \"%s: stop transmitted packets\\n\",\n@@ -4700,7 +4912,7 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)\n \t * segment.\n \t */\n \tis_last_segment = CIRC_CNT(tx_q-\u003ecur_tx, first_entry,\n-\t\t\t\t priv-\u003edma_conf.dma_tx_size) == 1;\n+\t\t\t\t priv-\u003edma_conf-\u003edma_tx_size) == 1;\n \n \t/* Complete the first descriptor before granting the DMA */\n \tstmmac_prepare_tso_tx_desc(priv, first, 1, proto_hdr_len, 0, 1,\n@@ -4738,13 +4950,13 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)\n \tfor (;;) {\n \t\tdesc = stmmac_get_tx_desc(priv, tx_q, first_entry);\n \t\tstmmac_release_tx_desc(priv, desc, priv-\u003edescriptor_mode);\n-\t\tstmmac_free_tx_buffer(priv, \u0026priv-\u003edma_conf, queue,\n+\t\tstmmac_free_tx_buffer(priv, priv-\u003edma_conf, queue,\n \t\t\t\t first_entry);\n \t\tif (first_entry == entry)\n \t\t\tbreak;\n \n \t\tfirst_entry = STMMAC_NEXT_ENTRY(first_entry,\n-\t\t\t\t\t\tpriv-\u003edma_conf.dma_tx_size);\n+\t\t\t\t\t\tpriv-\u003edma_conf-\u003edma_tx_size);\n \t}\n error:\n \tdev_err(priv-\u003edevice, \"Tx dma map failed\\n\");\n@@ -4829,7 +5041,7 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)\n \t\treturn NETDEV_TX_BUSY;\n \t}\n \n-\ttx_q = \u0026priv-\u003edma_conf.tx_queue[queue];\n+\ttx_q = \u0026priv-\u003edma_conf-\u003etx_queue[queue];\n \tfirst_tx = tx_q-\u003ecur_tx;\n \n \t/* Check if VLAN can be inserted by HW */\n@@ -4905,7 +5117,7 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)\n \t\tunsigned int frag_size = skb_frag_size(frag);\n \t\tbool last_segment = (i == (nfrags - 1));\n \n-\t\tentry = STMMAC_NEXT_ENTRY(entry, priv-\u003edma_conf.dma_tx_size);\n+\t\tentry = STMMAC_NEXT_ENTRY(entry, priv-\u003edma_conf-\u003edma_tx_size);\n \t\tWARN_ON(tx_q-\u003etx_skbuff[entry]);\n \n \t\tdesc = stmmac_get_tx_desc(priv, tx_q, entry);\n@@ -4935,7 +5147,7 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)\n \t * This approach takes care about the fragments: desc is the first\n \t * element in case of no SG.\n \t */\n-\ttx_packets = CIRC_CNT(entry + 1, first_tx, priv-\u003edma_conf.dma_tx_size);\n+\ttx_packets = CIRC_CNT(entry + 1, first_tx, priv-\u003edma_conf-\u003edma_tx_size);\n \ttx_q-\u003etx_count_frames += tx_packets;\n \n \tif ((skb_shinfo(skb)-\u003etx_flags \u0026 SKBTX_HW_TSTAMP) \u0026\u0026 priv-\u003ehwts_tx_en)\n@@ -4963,7 +5175,7 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)\n \t * ndo_start_xmit will fill this descriptor the next time it's\n \t * called and stmmac_tx_clean may clean up to this descriptor.\n \t */\n-\tentry = STMMAC_NEXT_ENTRY(entry, priv-\u003edma_conf.dma_tx_size);\n+\tentry = STMMAC_NEXT_ENTRY(entry, priv-\u003edma_conf-\u003edma_tx_size);\n \ttx_q-\u003ecur_tx = entry;\n \n \tif (netif_msg_pktdata(priv)) {\n@@ -5078,7 +5290,7 @@ static void stmmac_rx_vlan(struct net_device *dev, struct sk_buff *skb)\n */\n static inline void stmmac_rx_refill(struct stmmac_priv *priv, u32 queue)\n {\n-\tstruct stmmac_rx_queue *rx_q = \u0026priv-\u003edma_conf.rx_queue[queue];\n+\tstruct stmmac_rx_queue *rx_q = \u0026priv-\u003edma_conf-\u003erx_queue[queue];\n \tint dirty = stmmac_rx_dirty(priv, queue);\n \tunsigned int entry = rx_q-\u003edirty_rx;\n \tgfp_t gfp = (GFP_ATOMIC | __GFP_NOWARN);\n@@ -5129,7 +5341,7 @@ static inline void stmmac_rx_refill(struct stmmac_priv *priv, u32 queue)\n \t\tdma_wmb();\n \t\tstmmac_set_rx_owner(priv, p, use_rx_wd);\n \n-\t\tentry = STMMAC_NEXT_ENTRY(entry, priv-\u003edma_conf.dma_rx_size);\n+\t\tentry = STMMAC_NEXT_ENTRY(entry, priv-\u003edma_conf-\u003edma_rx_size);\n \t}\n \trx_q-\u003edirty_rx = entry;\n \tstmmac_set_queue_rx_tail_ptr(priv, rx_q, queue, rx_q-\u003edirty_rx);\n@@ -5157,12 +5369,12 @@ static unsigned int stmmac_rx_buf1_len(struct stmmac_priv *priv,\n \n \t/* First descriptor, not last descriptor and not split header */\n \tif (status \u0026 rx_not_ls)\n-\t\treturn priv-\u003edma_conf.dma_buf_sz;\n+\t\treturn priv-\u003edma_conf-\u003edma_buf_sz;\n \n \tplen = stmmac_get_rx_frame_len(priv, p, coe);\n \n \t/* First descriptor and last descriptor and not split header */\n-\treturn min_t(unsigned int, priv-\u003edma_conf.dma_buf_sz, plen);\n+\treturn min_t(unsigned int, priv-\u003edma_conf-\u003edma_buf_sz, plen);\n }\n \n static unsigned int stmmac_rx_buf2_len(struct stmmac_priv *priv,\n@@ -5192,7 +5404,7 @@ static unsigned int stmmac_rx_buf2_len(struct stmmac_priv *priv,\n \n \t/* Not GMAC4 and not last descriptor */\n \tif (priv-\u003eplat-\u003ecore_type != DWMAC_CORE_GMAC4 \u0026\u0026 (status \u0026 rx_not_ls))\n-\t\treturn priv-\u003edma_conf.dma_buf_sz;\n+\t\treturn priv-\u003edma_conf-\u003edma_buf_sz;\n \n \t/* GMAC4 or last descriptor */\n \tplen = stmmac_get_rx_frame_len(priv, p, coe);\n@@ -5204,7 +5416,7 @@ static int stmmac_xdp_xmit_xdpf(struct stmmac_priv *priv, int queue,\n \t\t\t\tstruct xdp_frame *xdpf, bool dma_map)\n {\n \tstruct stmmac_txq_stats *txq_stats = \u0026priv-\u003exstats.txq_stats[queue];\n-\tstruct stmmac_tx_queue *tx_q = \u0026priv-\u003edma_conf.tx_queue[queue];\n+\tstruct stmmac_tx_queue *tx_q = \u0026priv-\u003edma_conf-\u003etx_queue[queue];\n \tbool csum = !priv-\u003eplat-\u003etx_queues_cfg[queue].coe_unsupported;\n \tunsigned int entry = tx_q-\u003ecur_tx;\n \tenum stmmac_txbuf_type buf_type;\n@@ -5270,7 +5482,7 @@ static int stmmac_xdp_xmit_xdpf(struct stmmac_priv *priv, int queue,\n \n \tstmmac_enable_dma_transmission(priv, priv-\u003eioaddr, queue);\n \n-\tentry = STMMAC_NEXT_ENTRY(entry, priv-\u003edma_conf.dma_tx_size);\n+\tentry = STMMAC_NEXT_ENTRY(entry, priv-\u003edma_conf-\u003edma_tx_size);\n \ttx_q-\u003ecur_tx = entry;\n \n \treturn STMMAC_XDP_TX;\n@@ -5461,7 +5673,7 @@ static void stmmac_dispatch_skb_zc(struct stmmac_priv *priv, u32 queue,\n \n static bool stmmac_rx_refill_zc(struct stmmac_priv *priv, u32 queue, u32 budget)\n {\n-\tstruct stmmac_rx_queue *rx_q = \u0026priv-\u003edma_conf.rx_queue[queue];\n+\tstruct stmmac_rx_queue *rx_q = \u0026priv-\u003edma_conf-\u003erx_queue[queue];\n \tunsigned int entry = rx_q-\u003edirty_rx;\n \tstruct dma_desc *rx_desc = NULL;\n \tbool ret = true;\n@@ -5501,7 +5713,7 @@ static bool stmmac_rx_refill_zc(struct stmmac_priv *priv, u32 queue, u32 budget)\n \t\tdma_wmb();\n \t\tstmmac_set_rx_owner(priv, rx_desc, use_rx_wd);\n \n-\t\tentry = STMMAC_NEXT_ENTRY(entry, priv-\u003edma_conf.dma_rx_size);\n+\t\tentry = STMMAC_NEXT_ENTRY(entry, priv-\u003edma_conf-\u003edma_rx_size);\n \t}\n \n \tif (rx_desc) {\n@@ -5525,7 +5737,7 @@ static struct stmmac_xdp_buff *xsk_buff_to_stmmac_ctx(struct xdp_buff *xdp)\n static int stmmac_rx_zc(struct stmmac_priv *priv, int limit, u32 queue)\n {\n \tstruct stmmac_rxq_stats *rxq_stats = \u0026priv-\u003exstats.rxq_stats[queue];\n-\tstruct stmmac_rx_queue *rx_q = \u0026priv-\u003edma_conf.rx_queue[queue];\n+\tstruct stmmac_rx_queue *rx_q = \u0026priv-\u003edma_conf-\u003erx_queue[queue];\n \tunsigned int count = 0, error = 0, len = 0;\n \tint dirty = stmmac_rx_dirty(priv, queue);\n \tunsigned int next_entry = rx_q-\u003ecur_rx;\n@@ -5542,7 +5754,7 @@ static int stmmac_rx_zc(struct stmmac_priv *priv, int limit, u32 queue)\n \t\tnetdev_dbg(priv-\u003edev, \"%s: descriptor ring:\\n\", __func__);\n \t\tdesc_size = stmmac_get_rx_desc_size(priv);\n \n-\t\tstmmac_display_ring(priv, rx_head, priv-\u003edma_conf.dma_rx_size, true,\n+\t\tstmmac_display_ring(priv, rx_head, priv-\u003edma_conf-\u003edma_rx_size, true,\n \t\t\t\t rx_q-\u003edma_rx_phy, desc_size);\n \t}\n \twhile (count \u003c limit) {\n@@ -5586,7 +5798,7 @@ static int stmmac_rx_zc(struct stmmac_priv *priv, int limit, u32 queue)\n \n \t\t/* Prefetch the next RX descriptor */\n \t\tnext_entry = STMMAC_NEXT_ENTRY(rx_q-\u003ecur_rx,\n-\t\t\t\t\t priv-\u003edma_conf.dma_rx_size);\n+\t\t\t\t\t priv-\u003edma_conf-\u003edma_rx_size);\n \t\tif (unlikely(next_entry == rx_q-\u003edirty_rx))\n \t\t\tbreak;\n \n@@ -5711,7 +5923,7 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue)\n {\n \tu32 rx_errors = 0, rx_dropped = 0, rx_bytes = 0, rx_packets = 0;\n \tstruct stmmac_rxq_stats *rxq_stats = \u0026priv-\u003exstats.rxq_stats[queue];\n-\tstruct stmmac_rx_queue *rx_q = \u0026priv-\u003edma_conf.rx_queue[queue];\n+\tstruct stmmac_rx_queue *rx_q = \u0026priv-\u003edma_conf-\u003erx_queue[queue];\n \tstruct stmmac_channel *ch = \u0026priv-\u003echannel[queue];\n \tunsigned int count = 0, error = 0, len = 0;\n \tint status = 0, coe = priv-\u003ehw-\u003erx_csum;\n@@ -5724,7 +5936,7 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue)\n \tint bufsz;\n \n \tdma_dir = page_pool_get_dma_dir(rx_q-\u003epage_pool);\n-\tbufsz = DIV_ROUND_UP(priv-\u003edma_conf.dma_buf_sz, PAGE_SIZE) * PAGE_SIZE;\n+\tbufsz = DIV_ROUND_UP(priv-\u003edma_conf-\u003edma_buf_sz, PAGE_SIZE) * PAGE_SIZE;\n \n \tif (netif_msg_rx_status(priv)) {\n \t\tvoid *rx_head = stmmac_get_rx_desc(priv, rx_q, 0);\n@@ -5732,7 +5944,7 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue)\n \t\tnetdev_dbg(priv-\u003edev, \"%s: descriptor ring:\\n\", __func__);\n \t\tdesc_size = stmmac_get_rx_desc_size(priv);\n \n-\t\tstmmac_display_ring(priv, rx_head, priv-\u003edma_conf.dma_rx_size, true,\n+\t\tstmmac_display_ring(priv, rx_head, priv-\u003edma_conf-\u003edma_rx_size, true,\n \t\t\t\t rx_q-\u003edma_rx_phy, desc_size);\n \t}\n \twhile (count \u003c limit) {\n@@ -5772,7 +5984,7 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue)\n \t\t\tbreak;\n \n \t\tnext_entry = STMMAC_NEXT_ENTRY(rx_q-\u003ecur_rx,\n-\t\t\t\t\t priv-\u003edma_conf.dma_rx_size);\n+\t\t\t\t\t priv-\u003edma_conf-\u003edma_rx_size);\n \t\tif (unlikely(next_entry == rx_q-\u003edirty_rx))\n \t\t\tbreak;\n \n@@ -5906,7 +6118,7 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue)\n \t\t\t\t\t\tbuf1_len, dma_dir);\n \t\t\tskb_add_rx_frag(skb, skb_shinfo(skb)-\u003enr_frags,\n \t\t\t\t\tbuf-\u003epage, buf-\u003epage_offset, buf1_len,\n-\t\t\t\t\tpriv-\u003edma_conf.dma_buf_sz);\n+\t\t\t\t\tpriv-\u003edma_conf-\u003edma_buf_sz);\n \t\t\tbuf-\u003epage = NULL;\n \t\t}\n \n@@ -5915,7 +6127,7 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue)\n \t\t\t\t\t\tbuf2_len, dma_dir);\n \t\t\tskb_add_rx_frag(skb, skb_shinfo(skb)-\u003enr_frags,\n \t\t\t\t\tbuf-\u003esec_page, 0, buf2_len,\n-\t\t\t\t\tpriv-\u003edma_conf.dma_buf_sz);\n+\t\t\t\t\tpriv-\u003edma_conf-\u003edma_buf_sz);\n \t\t\tbuf-\u003esec_page = NULL;\n \t\t}\n \n@@ -6126,6 +6338,113 @@ static void stmmac_set_rx_mode(struct net_device *dev)\n \tstmmac_set_filter(priv, priv-\u003ehw, dev);\n }\n \n+static int stmmac_reconfigure_mtu(struct net_device *dev, int mtu)\n+{\n+\tstruct stmmac_priv *priv = netdev_priv(dev);\n+\tstruct stmmac_dma_conf *old_conf = priv-\u003edma_conf;\n+\tstruct stmmac_dma_conf *new_conf;\n+\tint old_mtu = dev-\u003emtu;\n+\tint ret, restore_ret;\n+\tu32 chan;\n+\n+\tnew_conf = stmmac_setup_dma_desc(priv, mtu);\n+\tif (IS_ERR(new_conf))\n+\t\treturn PTR_ERR(new_conf);\n+\n+\tnetif_device_detach(dev);\n+\tphylink_stop(priv-\u003ephylink);\n+\tstmmac_quiesce(priv);\n+\ttimer_delete_sync(\u0026priv-\u003eeee_ctrl_timer);\n+\tif (stmmac_fpe_supported(priv))\n+\t\tethtool_mmsv_stop(\u0026priv-\u003efpe_cfg.mmsv);\n+\n+\t/* An IRQ can recover a TX error and restart a queue. Drain handlers\n+\t * before the final TX stop, and keep the registrations for rollback.\n+\t */\n+\tstmmac_set_irq_state(priv, false);\n+\tnetif_tx_disable(dev);\n+\tsynchronize_net();\n+\n+\tret = stmmac_prepare_rx_buffers(priv);\n+\tif (ret)\n+\t\tgoto restart;\n+\n+\tstmmac_stop_all_dma(priv);\n+\tphylink_prepare_resume(priv-\u003ephylink);\n+\n+\t/* MAC receive limits must be programmed for the prospective MTU. */\n+\tWRITE_ONCE(dev-\u003emtu, mtu);\n+\tpriv-\u003edma_conf = new_conf;\n+\tstmmac_reset_queues_param(priv);\n+\tret = stmmac_hw_setup(dev, false);\n+\tif (ret) {\n+\t\tstmmac_stop_all_dma(priv);\n+\t\tstmmac_mac_set(priv, priv-\u003eioaddr, false);\n+\t\tpriv-\u003edma_conf = old_conf;\n+\t\tWRITE_ONCE(dev-\u003emtu, old_mtu);\n+\n+\t\t/* Reuse the retained rings. Reinitialize them only after reset\n+\t\t * has completed, not merely after clearing the DMA enable bits.\n+\t\t */\n+\t\trestore_ret = stmmac_hw_setup(dev, true);\n+\t\tif (restore_ret) {\n+\t\t\tstmmac_stop_all_dma(priv);\n+\t\t\tstmmac_mac_set(priv, priv-\u003eioaddr, false);\n+\t\t\tstmmac_set_irq_state(priv, true);\n+\t\t\tstmmac_free_irq(dev, REQ_IRQ_ERR_ALL, 0);\n+\t\t\tnetif_tx_disable(dev);\n+\t\t\tstmmac_stop_all_dma(priv);\n+\t\t\tpriv-\u003edatapath = STMMAC_DATAPATH_HALTED;\n+\t\t\tnetdev_err(dev, \"MTU rollback failed: %pe; interface remains detached\\n\",\n+\t\t\t\t ERR_PTR(restore_ret));\n+\t\t\tgoto free_new;\n+\t\t}\n+\t} else {\n+\t\t/* Hardware setup completed its reset before using the new rings.\n+\t\t * The old DMA allocations can now be released safely.\n+\t\t */\n+\t\tfree_dma_desc_resources(priv, old_conf);\n+\t\tkfree(old_conf);\n+\t\tfor (chan = 0; chan \u003c priv-\u003eplat-\u003etx_queues_to_use; chan++)\n+\t\t\thrtimer_setup(\u0026new_conf-\u003etx_queue[chan].txtimer,\n+\t\t\t\t stmmac_tx_timer, CLOCK_MONOTONIC,\n+\t\t\t\t HRTIMER_MODE_REL);\n+\t}\n+\n+\t/* Restore timestamping without registering a new PHC or resetting the\n+\t * user's packet timestamp filters. Timestamping can also be used without\n+\t * a registered PHC (CONFIG_PTP_1588_CLOCK=n).\n+\t */\n+\tif ((priv-\u003edma_cap.time_stamp || priv-\u003edma_cap.atime_stamp) \u0026\u0026\n+\t priv-\u003eplat-\u003eclk_ptp_rate) {\n+\t\tunsigned long flags;\n+\n+\t\twrite_lock_irqsave(\u0026priv-\u003eptp_lock, flags);\n+\t\tstmmac_init_tstamp_counter(priv, priv-\u003esystime_flags);\n+\t\tif (priv-\u003eplat-\u003eflags \u0026 STMMAC_FLAG_HWTSTAMP_CORRECT_LATENCY)\n+\t\t\tstmmac_hwtstamp_correct_latency(priv, priv);\n+\t\twrite_unlock_irqrestore(\u0026priv-\u003eptp_lock, flags);\n+\t}\n+\tstmmac_set_rx_mode(dev);\n+\tstmmac_vlan_restore(priv);\n+\n+restart:\n+\tstmmac_enable_all_queues(priv);\n+\tstmmac_enable_all_dma_irq(priv);\n+\tstmmac_set_irq_state(priv, true);\n+\tphylink_start(priv-\u003ephylink);\n+\tnetif_device_attach(dev);\n+\tfor (chan = 0; chan \u003c priv-\u003eplat-\u003etx_queues_to_use; chan++)\n+\t\tstmmac_tx_timer_arm(priv, chan);\n+\tif (!ret)\n+\t\treturn 0;\n+\n+free_new:\n+\tfree_dma_desc_resources(priv, new_conf);\n+\tkfree(new_conf);\n+\treturn ret;\n+}\n+\n /**\n * stmmac_change_mtu - entry point to change MTU size for the device.\n * @dev : device pointer.\n@@ -6141,7 +6460,6 @@ static int stmmac_change_mtu(struct net_device *dev, int new_mtu)\n {\n \tstruct stmmac_priv *priv = netdev_priv(dev);\n \tint txfifosz = priv-\u003eplat-\u003etx_fifo_size;\n-\tstruct stmmac_dma_conf *dma_conf;\n \tconst int mtu = new_mtu;\n \tint ret;\n \n@@ -6161,29 +6479,15 @@ static int stmmac_change_mtu(struct net_device *dev, int new_mtu)\n \tif ((txfifosz \u003c new_mtu) || (new_mtu \u003e BUF_SIZE_16KiB))\n \t\treturn -EINVAL;\n \n-\tif (netif_running(dev)) {\n-\t\tnetdev_dbg(priv-\u003edev, \"restarting interface to change its MTU\\n\");\n-\t\t/* Try to allocate the new DMA conf with the new mtu */\n-\t\tdma_conf = stmmac_setup_dma_desc(priv, mtu);\n-\t\tif (IS_ERR(dma_conf)) {\n-\t\t\tnetdev_err(priv-\u003edev, \"failed allocating new dma conf for new MTU %d\\n\",\n-\t\t\t\t mtu);\n-\t\t\treturn PTR_ERR(dma_conf);\n-\t\t}\n-\n-\t\t__stmmac_release(dev);\n-\n-\t\tret = __stmmac_open(dev, dma_conf);\n-\t\tif (ret) {\n-\t\t\tfree_dma_desc_resources(priv, dma_conf);\n-\t\t\tkfree(dma_conf);\n-\t\t\tnetdev_err(priv-\u003edev, \"failed reopening the interface after MTU change\\n\");\n+\t/* Normal-size frames use the same buffers and MAC receive limits.\n+\t * In particular, do not disturb a live AF_XDP pool: XDP does not\n+\t * support jumbo frames, so it never needs the ring replacement below.\n+\t */\n+\tif (netif_running(dev) \u0026\u0026\n+\t (dev-\u003emtu \u003e ETH_DATA_LEN || mtu \u003e ETH_DATA_LEN)) {\n+\t\tret = stmmac_reconfigure_mtu(dev, mtu);\n+\t\tif (ret)\n \t\t\treturn ret;\n-\t\t}\n-\n-\t\tkfree(dma_conf);\n-\n-\t\tstmmac_set_rx_mode(dev);\n \t}\n \n \tWRITE_ONCE(dev-\u003emtu, mtu);\n@@ -6354,15 +6658,11 @@ static irqreturn_t stmmac_safety_interrupt(int irq, void *dev_id)\n \n static irqreturn_t stmmac_msi_intr_tx(int irq, void *data)\n {\n-\tstruct stmmac_tx_queue *tx_q = (struct stmmac_tx_queue *)data;\n-\tstruct stmmac_dma_conf *dma_conf;\n-\tint chan = tx_q-\u003equeue_index;\n-\tstruct stmmac_priv *priv;\n+\tstruct stmmac_channel *ch = data;\n+\tstruct stmmac_priv *priv = ch-\u003epriv_data;\n+\tint chan = ch-\u003eindex;\n \tint status;\n \n-\tdma_conf = container_of(tx_q, struct stmmac_dma_conf, tx_queue[chan]);\n-\tpriv = container_of(dma_conf, struct stmmac_priv, dma_conf);\n-\n \t/* Check if adapter is up */\n \tif (test_bit(STMMAC_DOWN, \u0026priv-\u003estate))\n \t\treturn IRQ_HANDLED;\n@@ -6381,13 +6681,9 @@ static irqreturn_t stmmac_msi_intr_tx(int irq, void *data)\n \n static irqreturn_t stmmac_msi_intr_rx(int irq, void *data)\n {\n-\tstruct stmmac_rx_queue *rx_q = (struct stmmac_rx_queue *)data;\n-\tstruct stmmac_dma_conf *dma_conf;\n-\tint chan = rx_q-\u003equeue_index;\n-\tstruct stmmac_priv *priv;\n-\n-\tdma_conf = container_of(rx_q, struct stmmac_dma_conf, rx_queue[chan]);\n-\tpriv = container_of(dma_conf, struct stmmac_priv, dma_conf);\n+\tstruct stmmac_channel *ch = data;\n+\tstruct stmmac_priv *priv = ch-\u003epriv_data;\n+\tint chan = ch-\u003eindex;\n \n \t/* Check if adapter is up */\n \tif (test_bit(STMMAC_DOWN, \u0026priv-\u003estate))\n@@ -6425,6 +6721,8 @@ static int stmmac_setup_tc_block_cb(enum tc_setup_type type, void *type_data,\n \n \tif (!tc_cls_can_offload_and_chain0(priv-\u003edev, type_data))\n \t\treturn ret;\n+\tif (!netif_device_present(priv-\u003edev))\n+\t\treturn -ENETDOWN;\n \n \t__stmmac_disable_all_queues(priv);\n \n@@ -6542,45 +6840,49 @@ static int stmmac_rings_status_show(struct seq_file *seq, void *v)\n {\n \tstruct net_device *dev = seq-\u003eprivate;\n \tstruct stmmac_priv *priv = netdev_priv(dev);\n-\tu8 rx_count = priv-\u003eplat-\u003erx_queues_to_use;\n-\tu8 tx_count = priv-\u003eplat-\u003etx_queues_to_use;\n-\tu8 queue;\n+\tu8 rx_count, tx_count, queue;\n \n-\tif ((dev-\u003eflags \u0026 IFF_UP) == 0)\n-\t\treturn 0;\n+\trtnl_lock();\n+\tif (priv-\u003edatapath == STMMAC_DATAPATH_DOWN)\n+\t\tgoto out_unlock;\n+\n+\trx_count = priv-\u003eplat-\u003erx_queues_to_use;\n+\ttx_count = priv-\u003eplat-\u003etx_queues_to_use;\n \n \tfor (queue = 0; queue \u003c rx_count; queue++) {\n-\t\tstruct stmmac_rx_queue *rx_q = \u0026priv-\u003edma_conf.rx_queue[queue];\n+\t\tstruct stmmac_rx_queue *rx_q = \u0026priv-\u003edma_conf-\u003erx_queue[queue];\n \n \t\tseq_printf(seq, \"RX Queue %d:\\n\", queue);\n \n \t\tif (priv-\u003eextend_desc) {\n \t\t\tseq_printf(seq, \"Extended descriptor ring:\\n\");\n \t\t\tsysfs_display_ring((void *)rx_q-\u003edma_erx,\n-\t\t\t\t\t priv-\u003edma_conf.dma_rx_size, 1, seq, rx_q-\u003edma_rx_phy);\n+\t\t\t\t\t priv-\u003edma_conf-\u003edma_rx_size, 1, seq, rx_q-\u003edma_rx_phy);\n \t\t} else {\n \t\t\tseq_printf(seq, \"Descriptor ring:\\n\");\n \t\t\tsysfs_display_ring((void *)rx_q-\u003edma_rx,\n-\t\t\t\t\t priv-\u003edma_conf.dma_rx_size, 0, seq, rx_q-\u003edma_rx_phy);\n+\t\t\t\t\t priv-\u003edma_conf-\u003edma_rx_size, 0, seq, rx_q-\u003edma_rx_phy);\n \t\t}\n \t}\n \n \tfor (queue = 0; queue \u003c tx_count; queue++) {\n-\t\tstruct stmmac_tx_queue *tx_q = \u0026priv-\u003edma_conf.tx_queue[queue];\n+\t\tstruct stmmac_tx_queue *tx_q = \u0026priv-\u003edma_conf-\u003etx_queue[queue];\n \n \t\tseq_printf(seq, \"TX Queue %d:\\n\", queue);\n \n \t\tif (priv-\u003eextend_desc) {\n \t\t\tseq_printf(seq, \"Extended descriptor ring:\\n\");\n \t\t\tsysfs_display_ring((void *)tx_q-\u003edma_etx,\n-\t\t\t\t\t priv-\u003edma_conf.dma_tx_size, 1, seq, tx_q-\u003edma_tx_phy);\n+\t\t\t\t\t priv-\u003edma_conf-\u003edma_tx_size, 1, seq, tx_q-\u003edma_tx_phy);\n \t\t} else if (!(tx_q-\u003etbs \u0026 STMMAC_TBS_AVAIL)) {\n \t\t\tseq_printf(seq, \"Descriptor ring:\\n\");\n \t\t\tsysfs_display_ring((void *)tx_q-\u003edma_tx,\n-\t\t\t\t\t priv-\u003edma_conf.dma_tx_size, 0, seq, tx_q-\u003edma_tx_phy);\n+\t\t\t\t\t priv-\u003edma_conf-\u003edma_tx_size, 0, seq, tx_q-\u003edma_tx_phy);\n \t\t}\n \t}\n \n+out_unlock:\n+\trtnl_unlock();\n \treturn 0;\n }\n DEFINE_SHOW_ATTRIBUTE(stmmac_rings_status);\n@@ -6962,6 +7264,19 @@ static int stmmac_bpf(struct net_device *dev, struct netdev_bpf *bpf)\n {\n \tstruct stmmac_priv *priv = netdev_priv(dev);\n \n+\tif (bpf-\u003ecommand != XDP_SETUP_PROG \u0026\u0026\n+\t bpf-\u003ecommand != XDP_SETUP_XSK_POOL)\n+\t\treturn -EOPNOTSUPP;\n+\n+\t/*\n+\t * Pool removal must succeed even after a failed resume. Release the\n+\t * suspended rings before their pool or XDP buffer layout can change.\n+\t * Leave the interface detached until it is closed and reopened.\n+\t */\n+\tif (priv-\u003edatapath == STMMAC_DATAPATH_SUSPENDED ||\n+\t priv-\u003edatapath == STMMAC_DATAPATH_HALTED)\n+\t\t__stmmac_release(dev);\n+\n \tswitch (bpf-\u003ecommand) {\n \tcase XDP_SETUP_PROG:\n \t\treturn stmmac_xdp_set_prog(priv, bpf-\u003eprog, bpf-\u003eextack);\n@@ -6992,6 +7307,10 @@ static int stmmac_xdp_xmit(struct net_device *dev, int num_frames,\n \tnq = netdev_get_tx_queue(priv-\u003edev, queue);\n \n \t__netif_tx_lock(nq, cpu);\n+\tif (unlikely(!netif_device_present(dev) || netif_tx_queue_stopped(nq))) {\n+\t\t__netif_tx_unlock(nq);\n+\t\treturn -ENETDOWN;\n+\t}\n \t/* Avoids TX time-out as we are sharing with slow path */\n \ttxq_trans_cond_update(nq);\n \n@@ -7015,129 +7334,24 @@ static int stmmac_xdp_xmit(struct net_device *dev, int num_frames,\n \treturn nxmit;\n }\n \n-void stmmac_disable_rx_queue(struct stmmac_priv *priv, u32 queue)\n-{\n-\tstruct stmmac_channel *ch = \u0026priv-\u003echannel[queue];\n-\tunsigned long flags;\n-\n-\tspin_lock_irqsave(\u0026ch-\u003elock, flags);\n-\tstmmac_disable_dma_irq(priv, priv-\u003eioaddr, queue, 1, 0);\n-\tspin_unlock_irqrestore(\u0026ch-\u003elock, flags);\n-\n-\tstmmac_stop_rx_dma(priv, queue);\n-\t__free_dma_rx_desc_resources(priv, \u0026priv-\u003edma_conf, queue);\n-}\n-\n-void stmmac_enable_rx_queue(struct stmmac_priv *priv, u32 queue)\n-{\n-\tstruct stmmac_rx_queue *rx_q = \u0026priv-\u003edma_conf.rx_queue[queue];\n-\tstruct stmmac_channel *ch = \u0026priv-\u003echannel[queue];\n-\tunsigned long flags;\n-\tint ret;\n-\n-\tret = __alloc_dma_rx_desc_resources(priv, \u0026priv-\u003edma_conf, queue);\n-\tif (ret) {\n-\t\tnetdev_err(priv-\u003edev, \"Failed to alloc RX desc.\\n\");\n-\t\treturn;\n-\t}\n-\n-\tret = __init_dma_rx_desc_rings(priv, \u0026priv-\u003edma_conf, queue, GFP_KERNEL);\n-\tif (ret) {\n-\t\t__free_dma_rx_desc_resources(priv, \u0026priv-\u003edma_conf, queue);\n-\t\tnetdev_err(priv-\u003edev, \"Failed to init RX desc.\\n\");\n-\t\treturn;\n-\t}\n-\n-\tstmmac_reset_rx_queue(priv, queue);\n-\tstmmac_clear_rx_descriptors(priv, \u0026priv-\u003edma_conf, queue);\n-\n-\tstmmac_init_rx_chan(priv, priv-\u003eioaddr, priv-\u003eplat-\u003edma_cfg,\n-\t\t\t rx_q-\u003edma_rx_phy, queue);\n-\n-\tstmmac_set_queue_rx_tail_ptr(priv, rx_q, queue, rx_q-\u003ebuf_alloc_num);\n-\n-\tstmmac_set_queue_rx_buf_size(priv, rx_q, queue);\n-\n-\tstmmac_start_rx_dma(priv, queue);\n-\n-\tspin_lock_irqsave(\u0026ch-\u003elock, flags);\n-\tstmmac_enable_dma_irq(priv, priv-\u003eioaddr, queue, 1, 0);\n-\tspin_unlock_irqrestore(\u0026ch-\u003elock, flags);\n-}\n-\n-void stmmac_disable_tx_queue(struct stmmac_priv *priv, u32 queue)\n-{\n-\tstruct stmmac_channel *ch = \u0026priv-\u003echannel[queue];\n-\tunsigned long flags;\n-\n-\tspin_lock_irqsave(\u0026ch-\u003elock, flags);\n-\tstmmac_disable_dma_irq(priv, priv-\u003eioaddr, queue, 0, 1);\n-\tspin_unlock_irqrestore(\u0026ch-\u003elock, flags);\n-\n-\tstmmac_stop_tx_dma(priv, queue);\n-\t__free_dma_tx_desc_resources(priv, \u0026priv-\u003edma_conf, queue);\n-}\n-\n-void stmmac_enable_tx_queue(struct stmmac_priv *priv, u32 queue)\n-{\n-\tstruct stmmac_tx_queue *tx_q = \u0026priv-\u003edma_conf.tx_queue[queue];\n-\tstruct stmmac_channel *ch = \u0026priv-\u003echannel[queue];\n-\tunsigned long flags;\n-\tint ret;\n-\n-\tret = __alloc_dma_tx_desc_resources(priv, \u0026priv-\u003edma_conf, queue);\n-\tif (ret) {\n-\t\tnetdev_err(priv-\u003edev, \"Failed to alloc TX desc.\\n\");\n-\t\treturn;\n-\t}\n-\n-\tret = __init_dma_tx_desc_rings(priv, \u0026priv-\u003edma_conf, queue);\n-\tif (ret) {\n-\t\t__free_dma_tx_desc_resources(priv, \u0026priv-\u003edma_conf, queue);\n-\t\tnetdev_err(priv-\u003edev, \"Failed to init TX desc.\\n\");\n-\t\treturn;\n-\t}\n-\n-\tstmmac_reset_tx_queue(priv, queue);\n-\tstmmac_clear_tx_descriptors(priv, \u0026priv-\u003edma_conf, queue);\n-\n-\tstmmac_init_tx_chan(priv, priv-\u003eioaddr, priv-\u003eplat-\u003edma_cfg,\n-\t\t\t tx_q-\u003edma_tx_phy, queue);\n-\n-\tif (tx_q-\u003etbs \u0026 STMMAC_TBS_AVAIL)\n-\t\tstmmac_enable_tbs(priv, priv-\u003eioaddr, 1, queue);\n-\n-\tstmmac_set_queue_tx_tail_ptr(priv, tx_q, queue, 0);\n-\n-\tstmmac_start_tx_dma(priv, queue);\n-\n-\tspin_lock_irqsave(\u0026ch-\u003elock, flags);\n-\tstmmac_enable_dma_irq(priv, priv-\u003eioaddr, queue, 0, 1);\n-\tspin_unlock_irqrestore(\u0026ch-\u003elock, flags);\n-}\n-\n void stmmac_xdp_release(struct net_device *dev)\n {\n \tstruct stmmac_priv *priv = netdev_priv(dev);\n-\tu8 chan;\n \n-\t/* Ensure tx function is not running */\n-\tnetif_tx_disable(dev);\n-\n-\t/* Disable NAPI process */\n-\tstmmac_disable_all_queues(priv);\n-\n-\tfor (chan = 0; chan \u003c priv-\u003eplat-\u003etx_queues_to_use; chan++)\n-\t\thrtimer_cancel(\u0026priv-\u003edma_conf.tx_queue[chan].txtimer);\n+\tnetif_device_detach(dev);\n+\tphylink_stop(priv-\u003ephylink);\n+\tstmmac_quiesce(priv);\n+\tpriv-\u003edatapath = STMMAC_DATAPATH_DOWN;\n \n \t/* Free the IRQ lines */\n \tstmmac_free_irq(dev, REQ_IRQ_ERR_ALL, 0);\n+\tstmmac_stop_tx_queues(priv);\n \n \t/* Stop TX/RX DMA channels */\n \tstmmac_stop_all_dma(priv);\n \n \t/* Release and free the Rx/Tx resources */\n-\tfree_dma_desc_resources(priv, \u0026priv-\u003edma_conf);\n+\tfree_dma_desc_resources(priv, priv-\u003edma_conf);\n \n \t/* Disable the MAC Rx/Tx */\n \tstmmac_mac_set(priv, priv-\u003eioaddr, false);\n@@ -7146,7 +7360,13 @@ void stmmac_xdp_release(struct net_device *dev)\n \t * watchdogs during reset\n \t */\n \tnetif_trans_update(dev);\n-\tnetif_carrier_off(dev);\n+\n+\tif (stmmac_fpe_supported(priv))\n+\t\tethtool_mmsv_stop(\u0026priv-\u003efpe_cfg.mmsv);\n+\n+\t/* Keep PTP across the immediately following stmmac_xdp_open(). That\n+\t * function releases it if reopening fails, before returning DOWN.\n+\t */\n }\n \n int stmmac_xdp_open(struct net_device *dev)\n@@ -7161,14 +7381,14 @@ int stmmac_xdp_open(struct net_device *dev)\n \tu8 chan;\n \tint ret;\n \n-\tret = alloc_dma_desc_resources(priv, \u0026priv-\u003edma_conf);\n+\tret = alloc_dma_desc_resources(priv, priv-\u003edma_conf);\n \tif (ret \u003c 0) {\n \t\tnetdev_err(dev, \"%s: DMA descriptors allocation failed\\n\",\n \t\t\t __func__);\n \t\tgoto dma_desc_error;\n \t}\n \n-\tret = init_dma_desc_rings(dev, \u0026priv-\u003edma_conf, GFP_KERNEL);\n+\tret = init_dma_desc_rings(dev, priv-\u003edma_conf, GFP_KERNEL);\n \tif (ret \u003c 0) {\n \t\tnetdev_err(dev, \"%s: DMA descriptors initialization failed\\n\",\n \t\t\t __func__);\n@@ -7188,7 +7408,7 @@ int stmmac_xdp_open(struct net_device *dev)\n \n \t/* DMA RX Channel Configuration */\n \tfor (chan = 0; chan \u003c rx_cnt; chan++) {\n-\t\trx_q = \u0026priv-\u003edma_conf.rx_queue[chan];\n+\t\trx_q = \u0026priv-\u003edma_conf-\u003erx_queue[chan];\n \n \t\tstmmac_init_rx_chan(priv, priv-\u003eioaddr, priv-\u003eplat-\u003edma_cfg,\n \t\t\t\t rx_q-\u003edma_rx_phy, chan);\n@@ -7203,13 +7423,16 @@ int stmmac_xdp_open(struct net_device *dev)\n \n \t/* DMA TX Channel Configuration */\n \tfor (chan = 0; chan \u003c tx_cnt; chan++) {\n-\t\ttx_q = \u0026priv-\u003edma_conf.tx_queue[chan];\n+\t\ttx_q = \u0026priv-\u003edma_conf-\u003etx_queue[chan];\n \n \t\tstmmac_init_tx_chan(priv, priv-\u003eioaddr, priv-\u003eplat-\u003edma_cfg,\n \t\t\t\t tx_q-\u003edma_tx_phy, chan);\n \n \t\tstmmac_set_queue_tx_tail_ptr(priv, tx_q, chan, 0);\n \n+\t\tif (tx_q-\u003etbs \u0026 STMMAC_TBS_AVAIL)\n+\t\t\tstmmac_enable_tbs(priv, priv-\u003eioaddr, 1, chan);\n+\n \t\thrtimer_setup(\u0026tx_q-\u003etxtimer, stmmac_tx_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);\n \t}\n \n@@ -7225,27 +7448,28 @@ int stmmac_xdp_open(struct net_device *dev)\n \n \t/* Enable NAPI process*/\n \tstmmac_enable_all_queues(priv);\n-\tnetif_carrier_on(dev);\n-\tnetif_tx_start_all_queues(dev);\n \tstmmac_enable_all_dma_irq(priv);\n+\tpriv-\u003edatapath = STMMAC_DATAPATH_RUNNING;\n+\tphylink_start(priv-\u003ephylink);\n+\tnetif_device_attach(dev);\n \n \treturn 0;\n \n irq_error:\n-\tfor (chan = 0; chan \u003c priv-\u003eplat-\u003etx_queues_to_use; chan++)\n-\t\thrtimer_cancel(\u0026priv-\u003edma_conf.tx_queue[chan].txtimer);\n+\tstmmac_stop_tx_queues(priv);\n+\tstmmac_stop_all_dma(priv);\n+\tstmmac_mac_set(priv, priv-\u003eioaddr, false);\n \n init_error:\n-\tfree_dma_desc_resources(priv, \u0026priv-\u003edma_conf);\n+\tfree_dma_desc_resources(priv, priv-\u003edma_conf);\n dma_desc_error:\n+\tstmmac_release_ptp(priv);\n \treturn ret;\n }\n \n int stmmac_xsk_wakeup(struct net_device *dev, u32 queue, u32 flags)\n {\n \tstruct stmmac_priv *priv = netdev_priv(dev);\n-\tstruct stmmac_rx_queue *rx_q;\n-\tstruct stmmac_tx_queue *tx_q;\n \tstruct stmmac_channel *ch;\n \n \tif (test_bit(STMMAC_DOWN, \u0026priv-\u003estate) ||\n@@ -7259,11 +7483,9 @@ int stmmac_xsk_wakeup(struct net_device *dev, u32 queue, u32 flags)\n \t queue \u003e= priv-\u003eplat-\u003etx_queues_to_use)\n \t\treturn -EINVAL;\n \n-\trx_q = \u0026priv-\u003edma_conf.rx_queue[queue];\n-\ttx_q = \u0026priv-\u003edma_conf.tx_queue[queue];\n \tch = \u0026priv-\u003echannel[queue];\n \n-\tif (!rx_q-\u003exsk_pool \u0026\u0026 !tx_q-\u003exsk_pool)\n+\tif (!test_bit(queue, priv-\u003eaf_xdp_zc_qps))\n \t\treturn -EINVAL;\n \n \tif (!napi_if_scheduled_mark_missed(\u0026ch-\u003erxtx_napi)) {\n@@ -7364,6 +7586,9 @@ static void stmmac_reset_subtask(struct stmmac_priv *priv)\n \tnetdev_err(priv-\u003edev, \"Reset adapter.\\n\");\n \n \trtnl_lock();\n+\tif (!netif_device_present(priv-\u003edev))\n+\t\tgoto out_unlock;\n+\n \tnetif_trans_update(priv-\u003edev);\n \twhile (test_and_set_bit(STMMAC_RESETING, \u0026priv-\u003estate))\n \t\tusleep_range(1000, 2000);\n@@ -7373,6 +7598,7 @@ static void stmmac_reset_subtask(struct stmmac_priv *priv)\n \tdev_open(priv-\u003edev, NULL);\n \tclear_bit(STMMAC_DOWN, \u0026priv-\u003estate);\n \tclear_bit(STMMAC_RESETING, \u0026priv-\u003estate);\n+out_unlock:\n \trtnl_unlock();\n }\n \n@@ -7617,13 +7843,37 @@ static void stmmac_napi_del(struct net_device *dev)\n \t}\n }\n \n-int stmmac_reinit_queues(struct net_device *dev, u8 rx_cnt, u8 tx_cnt)\n+/* Rebuild only the datapath. The administratively-up device still owns its\n+ * PHY attachment and runtime-PM reference, even if this reopen fails.\n+ */\n+static int stmmac_reopen(struct net_device *dev)\n {\n \tstruct stmmac_priv *priv = netdev_priv(dev);\n-\tint ret = 0, i;\n+\tstruct stmmac_dma_conf *old_conf = priv-\u003edma_conf;\n+\tstruct stmmac_dma_conf *dma_conf;\n+\tint ret;\n \n-\tif (netif_running(dev))\n-\t\tstmmac_release(dev);\n+\tdma_conf = stmmac_setup_dma_desc(priv, dev-\u003emtu);\n+\tif (IS_ERR(dma_conf))\n+\t\treturn PTR_ERR(dma_conf);\n+\n+\tret = __stmmac_open(dev, dma_conf);\n+\tif (ret) {\n+\t\tpriv-\u003edma_conf = old_conf;\n+\t\tfree_dma_desc_resources(priv, dma_conf);\n+\t\tkfree(dma_conf);\n+\t\treturn ret;\n+\t}\n+\n+\tkfree(old_conf);\n+\tnetif_device_attach(dev);\n+\treturn 0;\n+}\n+\n+static void stmmac_set_queues(struct net_device *dev, u8 rx_cnt, u8 tx_cnt)\n+{\n+\tstruct stmmac_priv *priv = netdev_priv(dev);\n+\tint i;\n \n \tstmmac_napi_del(dev);\n \n@@ -7635,9 +7885,31 @@ int stmmac_reinit_queues(struct net_device *dev, u8 rx_cnt, u8 tx_cnt)\n \t\t\t\t\t\t\t\t\trx_cnt);\n \n \tstmmac_napi_add(dev);\n+}\n+\n+int stmmac_reinit_queues(struct net_device *dev, u8 rx_cnt, u8 tx_cnt)\n+{\n+\tstruct stmmac_priv *priv = netdev_priv(dev);\n+\tu8 old_rx = priv-\u003eplat-\u003erx_queues_to_use;\n+\tu8 old_tx = priv-\u003eplat-\u003etx_queues_to_use;\n+\tint ret = 0;\n+\n+\tif (netif_running(dev)) {\n+\t\tif (!netif_device_present(dev))\n+\t\t\treturn -ENETDOWN;\n+\t\tnetif_device_detach(dev);\n+\t\t__stmmac_release(dev);\n+\t}\n+\n+\tstmmac_set_queues(dev, rx_cnt, tx_cnt);\n \n \tif (netif_running(dev))\n-\t\tret = stmmac_open(dev);\n+\t\tret = stmmac_reopen(dev);\n+\tif (ret) {\n+\t\tstmmac_set_queues(dev, old_rx, old_tx);\n+\t\tnetdev_err(dev, \"failed reopening after channel change: %pe; interface remains detached\\n\",\n+\t\t\t ERR_PTR(ret));\n+\t}\n \n \treturn ret;\n }\n@@ -7645,16 +7917,28 @@ int stmmac_reinit_queues(struct net_device *dev, u8 rx_cnt, u8 tx_cnt)\n int stmmac_reinit_ringparam(struct net_device *dev, u32 rx_size, u32 tx_size)\n {\n \tstruct stmmac_priv *priv = netdev_priv(dev);\n+\tu32 old_rx = priv-\u003edma_conf-\u003edma_rx_size;\n+\tu32 old_tx = priv-\u003edma_conf-\u003edma_tx_size;\n \tint ret = 0;\n \n-\tif (netif_running(dev))\n-\t\tstmmac_release(dev);\n+\tif (netif_running(dev)) {\n+\t\tif (!netif_device_present(dev))\n+\t\t\treturn -ENETDOWN;\n+\t\tnetif_device_detach(dev);\n+\t\t__stmmac_release(dev);\n+\t}\n \n-\tpriv-\u003edma_conf.dma_rx_size = rx_size;\n-\tpriv-\u003edma_conf.dma_tx_size = tx_size;\n+\tpriv-\u003edma_conf-\u003edma_rx_size = rx_size;\n+\tpriv-\u003edma_conf-\u003edma_tx_size = tx_size;\n \n \tif (netif_running(dev))\n-\t\tret = stmmac_open(dev);\n+\t\tret = stmmac_reopen(dev);\n+\tif (ret) {\n+\t\tpriv-\u003edma_conf-\u003edma_rx_size = old_rx;\n+\t\tpriv-\u003edma_conf-\u003edma_tx_size = old_tx;\n+\t\tnetdev_err(dev, \"failed reopening after ring change: %pe; interface remains detached\\n\",\n+\t\t\t ERR_PTR(ret));\n+\t}\n \n \treturn ret;\n }\n@@ -7816,6 +8100,13 @@ struct plat_stmmacenet_data *stmmac_plat_dat_alloc(struct device *dev)\n }\n EXPORT_SYMBOL_GPL(stmmac_plat_dat_alloc);\n \n+static void stmmac_free_dma_conf(void *data)\n+{\n+\tstruct stmmac_priv *priv = data;\n+\n+\tkfree(priv-\u003edma_conf);\n+}\n+\n static int __stmmac_dvr_probe(struct device *device,\n \t\t\t struct plat_stmmacenet_data *plat_dat,\n \t\t\t struct stmmac_resources *res)\n@@ -7840,6 +8131,13 @@ static int __stmmac_dvr_probe(struct device *device,\n \tpriv = netdev_priv(ndev);\n \tpriv-\u003edevice = device;\n \tpriv-\u003edev = ndev;\n+\t/* Keep ring sizes and per-queue settings even while the device is down. */\n+\tpriv-\u003edma_conf = kzalloc_obj(*priv-\u003edma_conf);\n+\tif (!priv-\u003edma_conf)\n+\t\treturn -ENOMEM;\n+\tret = devm_add_action_or_reset(device, stmmac_free_dma_conf, priv);\n+\tif (ret)\n+\t\treturn ret;\n \n \tfor (i = 0; i \u003c MTL_MAX_RX_QUEUES; i++)\n \t\tu64_stats_init(\u0026priv-\u003exstats.rxq_stats[i].napi_syncp);\n@@ -8193,34 +8491,41 @@ EXPORT_SYMBOL_GPL(stmmac_dvr_remove);\n /**\n * stmmac_suspend - suspend callback\n * @dev: device pointer\n- * Description: this is the function to suspend the device and it is called\n- * by the platform driver to stop the network queue, release the resources,\n- * program the PMT register (for WoL), clean and release driver resources.\n+ * Description: stop network activity and program hardware for system sleep,\n+ * preserving any datapath resources still owned for resume or close.\n */\n int stmmac_suspend(struct device *dev)\n {\n \tstruct net_device *ndev = dev_get_drvdata(dev);\n \tstruct stmmac_priv *priv = netdev_priv(ndev);\n-\tu8 chan;\n \n-\tif (!ndev || !netif_running(ndev))\n+\trtnl_lock();\n+\tif (!netif_running(ndev) || priv-\u003ehw_suspended) {\n+\t\trtnl_unlock();\n \t\tgoto suspend_bsp;\n+\t}\n+\n+\t/* A failed datapath cannot provide a working MAC wake path. It may\n+\t * even have released its wake IRQ. Do not silently suspend without WoL.\n+\t */\n+\tif (priv-\u003ewolopts \u0026\u0026 priv-\u003edatapath != STMMAC_DATAPATH_RUNNING) {\n+\t\tnetdev_err(ndev, \"cannot suspend failed datapath with MAC WoL enabled\\n\");\n+\t\trtnl_unlock();\n+\t\treturn -EBUSY;\n+\t}\n \n \tmutex_lock(\u0026priv-\u003elock);\n \n \tnetif_device_detach(ndev);\n \n-\tstmmac_disable_all_queues(priv);\n-\n-\tfor (chan = 0; chan \u003c priv-\u003eplat-\u003etx_queues_to_use; chan++)\n-\t\thrtimer_cancel(\u0026priv-\u003edma_conf.tx_queue[chan].txtimer);\n+\tif (priv-\u003edatapath == STMMAC_DATAPATH_RUNNING)\n+\t\tstmmac_quiesce(priv);\n \n \tif (priv-\u003eeee_sw_timer_en) {\n \t\tpriv-\u003etx_path_in_lpi_mode = false;\n \t\ttimer_delete_sync(\u0026priv-\u003eeee_ctrl_timer);\n \t}\n \n-\t/* Stop TX/RX DMA */\n \tstmmac_stop_all_dma(priv);\n \n \tstmmac_legacy_serdes_power_down(priv);\n@@ -8236,12 +8541,14 @@ int stmmac_suspend(struct device *dev)\n \n \tmutex_unlock(\u0026priv-\u003elock);\n \n-\trtnl_lock();\n \tphylink_suspend(priv-\u003ephylink, !!priv-\u003ewolopts);\n-\trtnl_unlock();\n+\tif (priv-\u003edatapath == STMMAC_DATAPATH_RUNNING)\n+\t\tpriv-\u003edatapath = STMMAC_DATAPATH_SUSPENDED;\n+\tpriv-\u003ehw_suspended = true;\n \n \tif (stmmac_fpe_supported(priv))\n \t\tethtool_mmsv_stop(\u0026priv-\u003efpe_cfg.mmsv);\n+\trtnl_unlock();\n \n suspend_bsp:\n \tif (priv-\u003eplat-\u003esuspend)\n@@ -8253,7 +8560,7 @@ EXPORT_SYMBOL_GPL(stmmac_suspend);\n \n static void stmmac_reset_rx_queue(struct stmmac_priv *priv, u32 queue)\n {\n-\tstruct stmmac_rx_queue *rx_q = \u0026priv-\u003edma_conf.rx_queue[queue];\n+\tstruct stmmac_rx_queue *rx_q = \u0026priv-\u003edma_conf-\u003erx_queue[queue];\n \n \trx_q-\u003ecur_rx = 0;\n \trx_q-\u003edirty_rx = 0;\n@@ -8261,7 +8568,7 @@ static void stmmac_reset_rx_queue(struct stmmac_priv *priv, u32 queue)\n \n static void stmmac_reset_tx_queue(struct stmmac_priv *priv, u32 queue)\n {\n-\tstruct stmmac_tx_queue *tx_q = \u0026priv-\u003edma_conf.tx_queue[queue];\n+\tstruct stmmac_tx_queue *tx_q = \u0026priv-\u003edma_conf-\u003etx_queue[queue];\n \n \ttx_q-\u003ecur_tx = 0;\n \ttx_q-\u003edirty_tx = 0;\n@@ -8305,35 +8612,35 @@ int stmmac_resume(struct device *dev)\n \t\t\treturn ret;\n \t}\n \n-\tif (!netif_running(ndev))\n-\t\treturn 0;\n+\trtnl_lock();\n+\tif (!netif_running(ndev)) {\n+\t\tret = 0;\n+\t\tgoto out_unlock;\n+\t}\n \n-\t/* Power Down bit, into the PM register, is cleared\n-\t * automatically as soon as a magic packet or a Wake-up frame\n-\t * is received. Anyway, it's better to manually clear\n-\t * this bit because it can generate problems while resuming\n-\t * from another devices (e.g. serial console).\n-\t */\n-\tif (priv-\u003ewolopts) {\n-\t\tmutex_lock(\u0026priv-\u003elock);\n-\t\tstmmac_pmt(priv, priv-\u003ehw, 0);\n-\t\tmutex_unlock(\u0026priv-\u003elock);\n-\t\tpriv-\u003eirq_wake = 0;\n-\t} else {\n-\t\tpinctrl_pm_select_default_state(priv-\u003edevice);\n-\t\t/* reset the phy so that it's ready */\n-\t\tif (priv-\u003emii)\n-\t\t\tstmmac_mdio_reset(priv-\u003emii);\n+\tif (priv-\u003ehw_suspended) {\n+\t\tret = stmmac_resume_hw(priv);\n+\t\tif (ret)\n+\t\t\tgoto out_unlock;\n+\n+\t\t/* Terminate PM speed control without restarting a datapath\n+\t\t * whose IRQs or rings were released before system sleep.\n+\t\t */\n+\t\tif (priv-\u003edatapath != STMMAC_DATAPATH_SUSPENDED)\n+\t\t\tphylink_stop(priv-\u003ephylink);\n+\t}\n+\n+\tif (priv-\u003edatapath != STMMAC_DATAPATH_SUSPENDED) {\n+\t\tret = 0;\n+\t\tgoto out_unlock;\n \t}\n \n \tif (!(priv-\u003eplat-\u003eflags \u0026 STMMAC_FLAG_SERDES_UP_AFTER_PHY_LINKUP)) {\n \t\tret = stmmac_legacy_serdes_power_up(priv);\n \t\tif (ret \u003c 0)\n-\t\t\treturn ret;\n+\t\t\tgoto out_unlock;\n \t}\n \n-\trtnl_lock();\n-\n \t/* Prepare the PHY to resume, ensuring that its clocks which are\n \t * necessary for the MAC DMA reset to complete are running\n \t */\n@@ -8344,15 +8651,12 @@ int stmmac_resume(struct device *dev)\n \tstmmac_reset_queues_param(priv);\n \n \tstmmac_free_tx_skbufs(priv);\n-\tstmmac_clear_descriptors(priv, \u0026priv-\u003edma_conf);\n+\tstmmac_clear_descriptors(priv, priv-\u003edma_conf);\n \n-\tret = stmmac_hw_setup(ndev);\n+\tret = stmmac_hw_setup(ndev, false);\n \tif (ret \u003c 0) {\n \t\tnetdev_err(priv-\u003edev, \"%s: Hw setup failed\\n\", __func__);\n-\t\tstmmac_legacy_serdes_power_down(priv);\n-\t\tmutex_unlock(\u0026priv-\u003elock);\n-\t\trtnl_unlock();\n-\t\treturn ret;\n+\t\tgoto error_stop_dma;\n \t}\n \n \tstmmac_init_timestamping(priv);\n@@ -8374,11 +8678,25 @@ int stmmac_resume(struct device *dev)\n \t * workqueue thread, which will race with initialisation.\n \t */\n \tphylink_resume(priv-\u003ephylink);\n-\trtnl_unlock();\n-\n+\tpriv-\u003edatapath = STMMAC_DATAPATH_RUNNING;\n \tnetif_device_attach(ndev);\n+\trtnl_unlock();\n \n \treturn 0;\n+\n+error_stop_dma:\n+\tstmmac_stop_all_dma(priv);\n+\tstmmac_mac_set(priv, priv-\u003eioaddr, false);\n+\tstmmac_legacy_serdes_power_down(priv);\n+\tmutex_unlock(\u0026priv-\u003elock);\n+\t/*\n+\t * Keep the suspended data path detached. A later resume may retry, or\n+\t * ndo_stop() can release its resources without disabling NAPI again.\n+\t */\n+out_unlock:\n+\trtnl_unlock();\n+\n+\treturn ret;\n }\n EXPORT_SYMBOL_GPL(stmmac_resume);\n \ndiff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c\nindex 6128ed1bd521a..9bd622517389d 100644\n--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c\n+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c\n@@ -958,8 +958,9 @@ static int __maybe_unused stmmac_pltfr_noirq_suspend(struct device *dev)\n \t\treturn 0;\n \n \tif (!priv-\u003ewolopts) {\n-\t\t/* Disable clock in case of PWM is off */\n-\t\tclk_disable_unprepare(priv-\u003eplat-\u003eclk_ptp_ref);\n+\t\t/* A detached datapath may already have released its PTP clock. */\n+\t\tif (priv-\u003edatapath != STMMAC_DATAPATH_DOWN)\n+\t\t\tclk_disable_unprepare(priv-\u003eplat-\u003eclk_ptp_ref);\n \n \t\tret = pm_runtime_force_suspend(dev);\n \t\tif (ret)\n@@ -984,12 +985,14 @@ static int __maybe_unused stmmac_pltfr_noirq_resume(struct device *dev)\n \t\tif (ret)\n \t\t\treturn ret;\n \n-\t\tret = clk_prepare_enable(priv-\u003eplat-\u003eclk_ptp_ref);\n-\t\tif (ret \u003c 0) {\n-\t\t\tnetdev_warn(priv-\u003edev,\n-\t\t\t\t \"failed to enable PTP reference clock: %pe\\n\",\n-\t\t\t\t ERR_PTR(ret));\n-\t\t\treturn ret;\n+\t\tif (priv-\u003edatapath != STMMAC_DATAPATH_DOWN) {\n+\t\t\tret = clk_prepare_enable(priv-\u003eplat-\u003eclk_ptp_ref);\n+\t\t\tif (ret \u003c 0) {\n+\t\t\t\tnetdev_warn(priv-\u003edev,\n+\t\t\t\t\t \"failed to enable PTP reference clock: %pe\\n\",\n+\t\t\t\t\t ERR_PTR(ret));\n+\t\t\t\treturn ret;\n+\t\t\t}\n \t\t}\n \t}\n \ndiff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c\nindex c25dc9f89270c..2511339e55e4e 100644\n--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c\n+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c\n@@ -887,8 +887,8 @@ static int stmmac_test_flowctrl(struct stmmac_priv *priv)\n \t\tstruct stmmac_channel *ch = \u0026priv-\u003echannel[i];\n \t\tu32 tail;\n \n-\t\ttail = priv-\u003edma_conf.rx_queue[i].dma_rx_phy +\n-\t\t\t(priv-\u003edma_conf.dma_rx_size * sizeof(struct dma_desc));\n+\t\ttail = priv-\u003edma_conf-\u003erx_queue[i].dma_rx_phy +\n+\t\t\t(priv-\u003edma_conf-\u003edma_rx_size * sizeof(struct dma_desc));\n \n \t\tstmmac_set_rx_tail_ptr(priv, priv-\u003eioaddr, tail, i);\n \t\tstmmac_start_rx(priv, priv-\u003eioaddr, i);\n@@ -1787,7 +1787,7 @@ static int stmmac_test_arpoffload(struct stmmac_priv *priv)\n static int __stmmac_test_jumbo(struct stmmac_priv *priv, u16 queue)\n {\n \tstruct stmmac_packet_attrs attr = { };\n-\tint size = priv-\u003edma_conf.dma_buf_sz;\n+\tint size = priv-\u003edma_conf-\u003edma_buf_sz;\n \n \tif (!dwmac_is_xmac(priv-\u003eplat-\u003ecore_type))\n \t\tsize -= NET_IP_ALIGN;\n@@ -1873,7 +1873,7 @@ static int stmmac_test_tbs(struct stmmac_priv *priv)\n \n \t/* Find first TBS enabled Queue, if any */\n \tfor (i = 0; i \u003c priv-\u003eplat-\u003etx_queues_to_use; i++)\n-\t\tif (priv-\u003edma_conf.tx_queue[i].tbs \u0026 STMMAC_TBS_AVAIL)\n+\t\tif (priv-\u003edma_conf-\u003etx_queue[i].tbs \u0026 STMMAC_TBS_AVAIL)\n \t\t\tbreak;\n \n \tif (i \u003e= priv-\u003eplat-\u003etx_queues_to_use)\ndiff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c\nindex 42a00446e9b41..bb1dfe2702acd 100644\n--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c\n+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c\n@@ -1197,13 +1197,13 @@ static int tc_setup_etf(struct stmmac_priv *priv,\n \t\treturn -EOPNOTSUPP;\n \tif (qopt-\u003equeue \u003e= priv-\u003eplat-\u003etx_queues_to_use)\n \t\treturn -EINVAL;\n-\tif (!(priv-\u003edma_conf.tx_queue[qopt-\u003equeue].tbs \u0026 STMMAC_TBS_AVAIL))\n+\tif (!(priv-\u003edma_conf-\u003etx_queue[qopt-\u003equeue].tbs \u0026 STMMAC_TBS_AVAIL))\n \t\treturn -EINVAL;\n \n \tif (qopt-\u003eenable)\n-\t\tpriv-\u003edma_conf.tx_queue[qopt-\u003equeue].tbs |= STMMAC_TBS_EN;\n+\t\tpriv-\u003edma_conf-\u003etx_queue[qopt-\u003equeue].tbs |= STMMAC_TBS_EN;\n \telse\n-\t\tpriv-\u003edma_conf.tx_queue[qopt-\u003equeue].tbs \u0026= ~STMMAC_TBS_EN;\n+\t\tpriv-\u003edma_conf-\u003etx_queue[qopt-\u003equeue].tbs \u0026= ~STMMAC_TBS_EN;\n \n \tnetdev_info(priv-\u003edev, \"%s ETF for Queue %d\\n\",\n \t\t qopt-\u003eenable ? \"enabled\" : \"disabled\", qopt-\u003equeue);\ndiff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_xdp.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_xdp.c\nindex d7e4db7224b0c..907ac49a1b764 100644\n--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_xdp.c\n+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_xdp.c\n@@ -9,7 +9,6 @@\n static int stmmac_xdp_enable_pool(struct stmmac_priv *priv,\n \t\t\t\t struct xsk_buff_pool *pool, u16 queue)\n {\n-\tstruct stmmac_channel *ch = \u0026priv-\u003echannel[queue];\n \tbool need_update;\n \tu32 frame_size;\n \tint err;\n@@ -31,25 +30,26 @@ static int stmmac_xdp_enable_pool(struct stmmac_priv *priv,\n \t\treturn err;\n \t}\n \n-\tneed_update = netif_running(priv-\u003edev) \u0026\u0026 stmmac_xdp_is_enabled(priv);\n+\tneed_update = priv-\u003edatapath == STMMAC_DATAPATH_RUNNING \u0026\u0026\n+\t\t stmmac_xdp_is_enabled(priv);\n \n-\tif (need_update) {\n-\t\tnapi_disable(\u0026ch-\u003erx_napi);\n-\t\tnapi_disable(\u0026ch-\u003etx_napi);\n-\t\tstmmac_disable_rx_queue(priv, queue);\n-\t\tstmmac_disable_tx_queue(priv, queue);\n-\t}\n+\tif (need_update)\n+\t\tstmmac_xdp_release(priv-\u003edev);\n \n \tset_bit(queue, priv-\u003eaf_xdp_zc_qps);\n \n \tif (need_update) {\n-\t\tstmmac_enable_rx_queue(priv, queue);\n-\t\tstmmac_enable_tx_queue(priv, queue);\n-\t\tnapi_enable(\u0026ch-\u003erxtx_napi);\n-\n-\t\terr = stmmac_xsk_wakeup(priv-\u003edev, queue, XDP_WAKEUP_RX);\n-\t\tif (err)\n+\t\terr = stmmac_xdp_open(priv-\u003edev);\n+\t\tif (err) {\n+\t\t\tclear_bit(queue, priv-\u003eaf_xdp_zc_qps);\n+\t\t\txsk_pool_dma_unmap(pool, STMMAC_RX_DMA_ATTR);\n+\t\t\tnetdev_err(priv-\u003edev, \"failed reopening after XSK pool attach: %pe; interface remains detached\\n\",\n+\t\t\t\t ERR_PTR(err));\n \t\t\treturn err;\n+\t\t}\n+\n+\t\t/* The pool is installed even if link resolution is still pending. */\n+\t\tnapi_schedule(\u0026priv-\u003echannel[queue].rxtx_napi);\n \t}\n \n \treturn 0;\n@@ -57,9 +57,9 @@ static int stmmac_xdp_enable_pool(struct stmmac_priv *priv,\n \n static int stmmac_xdp_disable_pool(struct stmmac_priv *priv, u16 queue)\n {\n-\tstruct stmmac_channel *ch = \u0026priv-\u003echannel[queue];\n \tstruct xsk_buff_pool *pool;\n \tbool need_update;\n+\tint err;\n \n \tif (queue \u003e= priv-\u003eplat-\u003erx_queues_to_use ||\n \t queue \u003e= priv-\u003eplat-\u003etx_queues_to_use)\n@@ -69,26 +69,24 @@ static int stmmac_xdp_disable_pool(struct stmmac_priv *priv, u16 queue)\n \tif (!pool)\n \t\treturn -EINVAL;\n \n-\tneed_update = netif_running(priv-\u003edev) \u0026\u0026 stmmac_xdp_is_enabled(priv);\n+\tneed_update = priv-\u003edatapath == STMMAC_DATAPATH_RUNNING \u0026\u0026\n+\t\t stmmac_xdp_is_enabled(priv);\n \n-\tif (need_update) {\n-\t\tnapi_disable(\u0026ch-\u003erxtx_napi);\n-\t\tstmmac_disable_rx_queue(priv, queue);\n-\t\tstmmac_disable_tx_queue(priv, queue);\n-\t\tsynchronize_rcu();\n-\t}\n+\tif (need_update)\n+\t\tstmmac_xdp_release(priv-\u003edev);\n \n \txsk_pool_dma_unmap(pool, STMMAC_RX_DMA_ATTR);\n \n \tclear_bit(queue, priv-\u003eaf_xdp_zc_qps);\n \n \tif (need_update) {\n-\t\tstmmac_enable_rx_queue(priv, queue);\n-\t\tstmmac_enable_tx_queue(priv, queue);\n-\t\tnapi_enable(\u0026ch-\u003erx_napi);\n-\t\tnapi_enable(\u0026ch-\u003etx_napi);\n+\t\terr = stmmac_xdp_open(priv-\u003edev);\n+\t\tif (err)\n+\t\t\tnetdev_err(priv-\u003edev, \"failed reopening after XSK pool removal: %pe; interface remains detached\\n\",\n+\t\t\t\t ERR_PTR(err));\n \t}\n \n+\t/* Socket teardown must be able to unmap and free the removed pool. */\n \treturn 0;\n }\n \n@@ -106,8 +104,9 @@ int stmmac_xdp_set_prog(struct stmmac_priv *priv, struct bpf_prog *prog,\n \tstruct bpf_prog *old_prog;\n \tbool need_update;\n \tbool if_running;\n+\tint ret;\n \n-\tif_running = netif_running(dev);\n+\tif_running = priv-\u003edatapath == STMMAC_DATAPATH_RUNNING;\n \n \tif (prog \u0026\u0026 dev-\u003emtu \u003e ETH_DATA_LEN) {\n \t\t/* For now, the driver doesn't support XDP functionality with\n@@ -117,25 +116,41 @@ int stmmac_xdp_set_prog(struct stmmac_priv *priv, struct bpf_prog *prog,\n \t\treturn -EOPNOTSUPP;\n \t}\n \n-\tif (!prog)\n-\t\txdp_features_clear_redirect_target(dev);\n-\n \tneed_update = !!priv-\u003exdp_prog != !!prog;\n \tif (if_running \u0026\u0026 need_update)\n \t\tstmmac_xdp_release(dev);\n \n \told_prog = xchg(\u0026priv-\u003exdp_prog, prog);\n-\tif (old_prog)\n-\t\tbpf_prog_put(old_prog);\n \n \t/* Disable RX SPH for XDP operation */\n \tpriv-\u003esph_active = priv-\u003esph_capable \u0026\u0026 !stmmac_xdp_is_enabled(priv);\n \n-\tif (if_running \u0026\u0026 need_update)\n-\t\tstmmac_xdp_open(dev);\n+\tif (if_running \u0026\u0026 need_update) {\n+\t\tret = stmmac_xdp_open(dev);\n+\t\tif (ret) {\n+\t\t\tnetdev_err(dev, \"failed reopening after XDP change: %pe; interface remains detached\\n\",\n+\t\t\t\t ERR_PTR(ret));\n+\t\t\tif (prog) {\n+\t\t\t\t/* The core retains the old program on error and drops\n+\t\t\t\t * the reference it passed for the proposed program.\n+\t\t\t\t */\n+\t\t\t\txchg(\u0026priv-\u003exdp_prog, old_prog);\n+\t\t\t\tpriv-\u003esph_active = priv-\u003esph_capable \u0026\u0026 !old_prog;\n+\t\t\t\treturn ret;\n+\t\t\t}\n+\t\t\t/* Uninstalling a BPF link must release its program even\n+\t\t\t * if the non-XDP datapath cannot be restarted.\n+\t\t\t */\n+\t\t}\n+\t}\n+\n+\tif (old_prog)\n+\t\tbpf_prog_put(old_prog);\n \n \tif (prog)\n \t\txdp_features_set_redirect_target(dev, false);\n+\telse\n+\t\txdp_features_clear_redirect_target(dev);\n \n \treturn 0;\n }\ndiff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c\nindex 1bbcf46c83566..b7d30ca340316 100644\n--- a/drivers/net/phy/phylink.c\n+++ b/drivers/net/phy/phylink.c\n@@ -78,6 +78,7 @@ struct phylink {\n \n \tbool link_failed;\n \tbool suspend_link_up;\n+\tbool suspend_speed_down;\n \tbool force_major_config;\n \tbool major_config_failed;\n \tbool mac_supports_eee_ops;\n@@ -2498,6 +2499,14 @@ void phylink_start(struct phylink *pl)\n }\n EXPORT_SYMBOL_GPL(phylink_start);\n \n+static void phylink_restore_suspend_speed(struct phylink *pl)\n+{\n+\tif (pl-\u003esuspend_speed_down) {\n+\t\tphylink_speed_up(pl);\n+\t\tpl-\u003esuspend_speed_down = false;\n+\t}\n+}\n+\n /**\n * phylink_stop() - stop a phylink instance\n * @pl: a pointer to a \u0026struct phylink returned from phylink_create()\n@@ -2509,11 +2518,30 @@ EXPORT_SYMBOL_GPL(phylink_start);\n *\n * This will synchronously bring down the link if the link is not already\n * down (in other words, it will trigger a mac_link_down() method call.)\n+ * A suspended instance may be stopped without first calling phylink_resume().\n+ * In particular, closing a device after a failed resume must not restart the\n+ * link or reconfigure the MAC just to finish shutting it down.\n+ * Any PHY advertisement reduced by phylink_suspend() is restored as part\n+ * of this transition.\n+ * If phylink_prepare_resume() powered up an already stopped PHY, suspend\n+ * it again when Wake-on-LAN permits.\n */\n void phylink_stop(struct phylink *pl)\n {\n \tASSERT_RTNL();\n \n+\t/* Also undo PHY speed control when terminating a suspended instance. */\n+\tphylink_restore_suspend_speed(pl);\n+\n+\tif (test_bit(PHYLINK_DISABLE_STOPPED, \u0026pl-\u003ephylink_disable_state)) {\n+\t\t/* A failed MAC resume may have called phylink_prepare_resume()\n+\t\t * and powered the stopped PHY back up to supply its RX clock.\n+\t\t */\n+\t\tif (pl-\u003ephydev)\n+\t\t\tphy_suspend(pl-\u003ephydev);\n+\t\treturn;\n+\t}\n+\n \tif (pl-\u003esfp_bus)\n \t\tsfp_upstream_stop(pl-\u003esfp_bus);\n \tif (pl-\u003ephydev)\n@@ -2526,6 +2554,16 @@ void phylink_stop(struct phylink *pl)\n \n \tphylink_run_resolve_and_disable(pl, PHYLINK_DISABLE_STOPPED);\n \n+\tif (test_bit(PHYLINK_DISABLE_MAC_WOL, \u0026pl-\u003ephylink_disable_state)) {\n+\t\t/* Finish the link-down deferred by MAC WoL, without restarting. */\n+\t\tflush_work(\u0026pl-\u003eresolve);\n+\t\tmutex_lock(\u0026pl-\u003estate_mutex);\n+\t\tif (pl-\u003esuspend_link_up)\n+\t\t\tphylink_link_down(pl);\n+\t\t__clear_bit(PHYLINK_DISABLE_MAC_WOL, \u0026pl-\u003ephylink_disable_state);\n+\t\tmutex_unlock(\u0026pl-\u003estate_mutex);\n+\t}\n+\n \tpl-\u003epcs_state = PCS_STATE_DOWN;\n \n \tphylink_pcs_disable(pl-\u003epcs);\n@@ -2657,8 +2695,10 @@ void phylink_suspend(struct phylink *pl, bool mac_wol)\n \t\tphylink_stop(pl);\n \t}\n \n-\tif (phylink_phy_pm_speed_ctrl(pl))\n+\tif (phylink_phy_pm_speed_ctrl(pl)) {\n \t\tphylink_speed_down(pl, false);\n+\t\tpl-\u003esuspend_speed_down = true;\n+\t}\n }\n EXPORT_SYMBOL_GPL(phylink_suspend);\n \n@@ -2698,8 +2738,7 @@ void phylink_resume(struct phylink *pl)\n {\n \tASSERT_RTNL();\n \n-\tif (phylink_phy_pm_speed_ctrl(pl))\n-\t\tphylink_speed_up(pl);\n+\tphylink_restore_suspend_speed(pl);\n \n \tif (test_bit(PHYLINK_DISABLE_MAC_WOL, \u0026pl-\u003ephylink_disable_state)) {\n \t\t/* Wake-on-Lan enabled, MAC handling */\n@@ -3616,6 +3655,12 @@ int phylink_speed_down(struct phylink *pl, bool sync)\n \n \tASSERT_RTNL();\n \n+\t/* An explicit request takes over from suspend-time speed control.\n+\t * Restore the original advertisement before saving it again, so a\n+\t * repeated speed-down cannot replace it with the reduced advertisement.\n+\t */\n+\tphylink_restore_suspend_speed(pl);\n+\n \tif (!pl-\u003esfp_bus \u0026\u0026 pl-\u003ephydev)\n \t\tret = phy_speed_down(pl-\u003ephydev, sync);\n \n"
} |
| 2/1 | 2026/09/26 22:50 | agent | patch-evaluator |
6mModel:core Args: null Results: {
"EnableConfigs": [
"PHYLINK",
"USB_NET_DRIVERS",
"USB_USBNET",
"USB_NET_AX8817X",
"USB_LAN78XX",
"NET_DSA",
"NET_DSA_LOOP",
"STMMAC_ETH",
"STMMAC_PCI"
],
"FocusSymbols": [
"phylink_stop",
"phylink_suspend",
"phylink_resume",
"phylink_speed_down",
"stmmac_reconfigure_mtu",
"stmmac_xdp_open",
"stmmac_xdp_release",
"stmmac_reopen",
"stmmac_open",
"stmmac_release"
],
"Reasoning": "The patch modifies both the stmmac network driver and the core phylink subsystem (drivers/net/phy/phylink.c). Phylink is reachable in standard virtualized fuzzing environments via software-emulated USB network adapters (such as lan78xx and asix/ax8817x through USB Raw-Gadget) and DSA loop mock switch devices. The modifications introduce new state management (suspend_speed_down), flush work synchronously under RTNL lock in phylink_stop, and alter speed control transitions across suspend/resume/down paths. In addition, extensive refactoring of the stmmac datapath state machine, DMA descriptor allocation, and MTU rollback logic makes this series worth fuzzing.",
"WorthFuzzing": true
}Instruction: You are an expert Linux kernel maintainer and security engineer.
Your job is to review a provided patch series and evaluate whether it warrants fuzzing with syzkaller.
IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on internal assumptions. You must actively use your code access
tools to inspect the actual source code, callers, and surrounding context.
================================================================================
1. CORE TRIAGE PHILOSOPHY
================================================================================
The goal of patch fuzzing is to discover crashes, regressions, exposed latent bugs,
and newly triggered assertions introduced by the patch series.
- REACHABILITY IS THE PRIMARY GATE:
Fuzzing can only discover bugs in code that can actually execute in standard virtualized
environments (GCE or QEMU, utilizing software-emulated devices like USB gadgets, netdev, tun/tap).
If the modified code is structurally unreachable (see Section 2), it MUST NOT be fuzzed,
regardless of whether it adds assertions or complex logic.
- DO NOT BLINDLY TRUST "NO FUNCTIONAL CHANGE" (NFCI) OR "REFACTORING" CLAIMS:
Patch authors routinely label changes as "cleanups", "refactorings", or state
"No functional change intended". Do NOT take these claims at face value.
Code refactorings that rearrange logic, introduce helper functions, or alter state management
in core subsystems frequently introduce subtle semantic shifts or uncover latent kernel bugs.
If reachable executable code is modified or refactored, it MUST be fuzzed.
- NEW OR MODIFIED ASSERTIONS IN REACHABLE CODE MUST BE FUZZED:
When a patch introduces or modifies runtime checks or assertions (e.g., WARN_ON*, VM_WARN_ON*,
BUG_ON*, lockdep_assert*) in reachable code paths, it enforces new or stricter invariants.
Even if the author believes the invariant always holds, fuzzing is essential to verify whether
an unusual sequence of operations can violate it.
================================================================================
2. WHEN TO RETURN WorthFuzzing=false (NEGATIVE CRITERIA)
================================================================================
Return WorthFuzzing=false ONLY IF all modified code falls strictly into one or more of these categories:
- Non-kernel and non-executable changes:
* Modifications to Documentation/, comments, or spelling fixes.
* User-space directories, self-tests, samples, or scripts (e.g., tools/, samples/, scripts/, usr/)
that do not affect the compiled kernel image (vmlinux) or kernel modules.
* Purely decorative logging (e.g., message strings in pr_err, printk, dev_info) or tracepoints
that do not alter control flow or data structures.
* Build system or Kconfig changes that do not alter compiled C logic.
- Structurally unreachable hardware:
* Vendor-specific PCIe switches, SmartNICs, or GPU drivers (e.g., mlxsw, pds_core, qed,
ionic, amdgpu) requiring physical ASIC/PCIe cards not emulated in standard QEMU.
- Unreachable execution paths:
* Driver teardown callbacks (.remove, .shutdown, pci_unregister_driver) executed only during
physical PCI hot-unplug or manual sysfs driver unbinding.
* Code paths exclusive to architectures other than the target architecture.
================================================================================
3. WHEN TO RETURN WorthFuzzing=true (POSITIVE CRITERIA)
================================================================================
Return WorthFuzzing=true whenever the patch touches reachable executable code, including:
- Core Subsystems:
* Any logic modifications in memory management (mm/), synchronization/locking (kernel/locking/),
BPF, scheduler, core networking, VFS, or syscall handling.
- Refactorings and Code Cleanups:
* Any restructuring of reachable data structures, helper abstractions, or algorithm flows.
- Runtime Assertions and Defensive Checks:
* Any introduction or alteration of assertions (WARN_ON*, VM_WARN_ON*, BUG_ON*, etc.) in reachable paths.
- Reachable Drivers and Protocols:
* Drivers accessible via virtual buses (virtio, USB gadget, loopback, netlink, binder, sockets, etc.).
================================================================================
4. EXTRACTING FocusSymbols (PREVENTING DILUTION)
================================================================================
When WorthFuzzing=true, you must extract specific kernel functions into FocusSymbols to guide the fuzzer:
- AVOID UBIQUITOUS LIFECYCLE HOT-PATHS:
Do NOT list generic, ubiquitous functions called by almost every program in the corpus
(including, but not limited to: general memory allocators and deallocators, page fault
and trap handlers, or core synchronization primitives; this is not an exhaustive list).
Listing ubiquitous functions causes the fuzzer to classify thousands of unrelated tests as "focused",
which severely dilutes fuzzing effort away from the actual changes.
- TARGET SPECIFIC FEATURE LOGIC AND ENTRYPOINTS:
List functions that specifically implement the logic being added or altered, or direct API entrypoints
for the subsystem feature under review.
- HANDLING STATIC INLINE FUNCTIONS IN HEADERS (.h):
Compiler-inlined static functions (such as static inlines in mm/*.h or include/linux/*.h) lack
distinct symbol addresses in vmlinux and cannot be targeted directly by symbol coverage filters.
If the changes are primarily in static inline helpers, identify non-static, feature-specific caller
functions in .c files that exercise them (avoiding ubiquitous lifecycle wrappers).
================================================================================
5. IDENTIFYING EnableConfigs
================================================================================
Identify any specific CONFIG_ options required to properly compile and reach the modified code:
- Inspect Kconfig files and #ifdef guards; do not make assumptions.
- Check "depends on" lines in Kconfig to include any non-standard parent subsystem configs needed.
- Strip any 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4").
Prefer calling several tools at the same time to save round-trips.
Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.
Prompt: Target architecture: amd64
For your convenience, here is the diff of the changes:
commit 925615988696cb7f8488eeefff14dfb3ead46259
Author: syz-cluster <triage@syzkaller.com>
Date: Sat Sep 26 22:50:45 2026 +0000
syz-cluster: applied patch under review
diff --git a/drivers/net/ethernet/stmicro/stmmac/chain_mode.c b/drivers/net/ethernet/stmicro/stmmac/chain_mode.c
index 66025e2509e91..65243c5e539e4 100644
--- a/drivers/net/ethernet/stmicro/stmmac/chain_mode.c
+++ b/drivers/net/ethernet/stmicro/stmmac/chain_mode.c
@@ -48,7 +48,7 @@ static int jumbo_frm(struct stmmac_tx_queue *tx_q, struct sk_buff *skb,
while (len != 0) {
tx_q->tx_skbuff[entry] = NULL;
- entry = STMMAC_NEXT_ENTRY(entry, priv->dma_conf.dma_tx_size);
+ entry = STMMAC_NEXT_ENTRY(entry, priv->dma_conf->dma_tx_size);
desc = tx_q->dma_tx + entry;
if (len > bmax) {
@@ -137,7 +137,7 @@ static void refill_desc3(struct stmmac_rx_queue *rx_q, struct dma_desc *p)
*/
p->des3 = cpu_to_le32((unsigned int)(rx_q->dma_rx_phy +
(((rx_q->dirty_rx) + 1) %
- priv->dma_conf.dma_rx_size) *
+ priv->dma_conf->dma_rx_size) *
sizeof(struct dma_desc)));
}
@@ -154,7 +154,7 @@ static void clean_desc3(struct stmmac_tx_queue *tx_q, struct dma_desc *p)
*/
p->des3 = cpu_to_le32((unsigned int)((tx_q->dma_tx_phy +
((tx_q->dirty_tx + 1) %
- priv->dma_conf.dma_tx_size))
+ priv->dma_conf->dma_tx_size))
* sizeof(struct dma_desc)));
}
diff --git a/drivers/net/ethernet/stmicro/stmmac/ring_mode.c b/drivers/net/ethernet/stmicro/stmmac/ring_mode.c
index d2f0c321661d9..0299d6a6c32bf 100644
--- a/drivers/net/ethernet/stmicro/stmmac/ring_mode.c
+++ b/drivers/net/ethernet/stmicro/stmmac/ring_mode.c
@@ -52,7 +52,7 @@ static int jumbo_frm(struct stmmac_tx_queue *tx_q, struct sk_buff *skb,
stmmac_prepare_tx_desc(priv, desc, 1, bmax, csum,
STMMAC_RING_MODE, 0, false, skb->len);
tx_q->tx_skbuff[entry] = NULL;
- entry = STMMAC_NEXT_ENTRY(entry, priv->dma_conf.dma_tx_size);
+ entry = STMMAC_NEXT_ENTRY(entry, priv->dma_conf->dma_tx_size);
if (priv->extend_desc)
desc = (struct dma_desc *)(tx_q->dma_etx + entry);
@@ -102,7 +102,7 @@ static void refill_desc3(struct stmmac_rx_queue *rx_q, struct dma_desc *p)
struct stmmac_priv *priv = rx_q->priv_data;
/* Fill DES3 in case of RING mode */
- if (priv->dma_conf.dma_buf_sz == BUF_SIZE_16KiB)
+ if (priv->dma_conf->dma_buf_sz == BUF_SIZE_16KiB)
p->des3 = cpu_to_le32(le32_to_cpu(p->des2) + BUF_SIZE_8KiB);
}
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h
index 7582fca637419..8ebf407895689 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h
@@ -258,6 +258,17 @@ struct stmmac_msi {
char int_name_tx_irq[MTL_MAX_TX_QUEUES][IFNAMSIZ + 18];
};
+enum stmmac_datapath_state {
+ /* No IRQs or DMA allocations owned by a successful open. */
+ STMMAC_DATAPATH_DOWN,
+ /* Resources allocated, NAPI enabled. */
+ STMMAC_DATAPATH_RUNNING,
+ /* Resources retained, NAPI and DMA stopped; also after failed resume. */
+ STMMAC_DATAPATH_SUSPENDED,
+ /* Failed MTU rollback: rings retained, but no IRQs or running NAPI. */
+ STMMAC_DATAPATH_HALTED,
+};
+
struct stmmac_priv {
/* Frequently used values are kept adjacent for cache effect */
u32 tx_coal_frames[MTL_MAX_TX_QUEUES];
@@ -280,7 +291,11 @@ struct stmmac_priv {
int (*hwif_quirks)(struct stmmac_priv *priv);
struct mutex lock;
- struct stmmac_dma_conf dma_conf;
+ struct stmmac_dma_conf *dma_conf;
+ /* IRQ/DMA ownership and NAPI state, serialized by RTNL. */
+ enum stmmac_datapath_state datapath;
+ /* Core sleep sequence completed, independently of datapath ownership. */
+ bool hw_suspended;
/* Generic channel for NAPI */
struct stmmac_channel channel[STMMAC_CH_MAX];
@@ -424,10 +439,6 @@ static inline bool stmmac_xdp_is_enabled(struct stmmac_priv *priv)
return !!priv->xdp_prog;
}
-void stmmac_disable_rx_queue(struct stmmac_priv *priv, u32 queue);
-void stmmac_enable_rx_queue(struct stmmac_priv *priv, u32 queue);
-void stmmac_disable_tx_queue(struct stmmac_priv *priv, u32 queue);
-void stmmac_enable_tx_queue(struct stmmac_priv *priv, u32 queue);
int stmmac_xsk_wakeup(struct net_device *dev, u32 queue, u32 flags);
struct timespec64 stmmac_calc_tas_basetime(ktime_t old_base_time,
ktime_t current_time,
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c
index 1be5310ca766c..994350fcdbfa8 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c
@@ -396,8 +396,8 @@ static void stmmac_get_ringparam(struct net_device *netdev,
ring->rx_max_pending = DMA_MAX_RX_SIZE;
ring->tx_max_pending = DMA_MAX_TX_SIZE;
- ring->rx_pending = priv->dma_conf.dma_rx_size;
- ring->tx_pending = priv->dma_conf.dma_tx_size;
+ ring->rx_pending = priv->dma_conf->dma_rx_size;
+ ring->tx_pending = priv->dma_conf->dma_tx_size;
}
static int stmmac_set_ringparam(struct net_device *netdev,
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index 3f34d491c9592..1a552455a633d 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -79,7 +79,7 @@ static int phyaddr = -1;
module_param(phyaddr, int, 0444);
MODULE_PARM_DESC(phyaddr, "Physical device address");
-#define STMMAC_TX_THRESH(x) ((x)->dma_conf.dma_tx_size / 4)
+#define STMMAC_TX_THRESH(x) ((x)->dma_conf->dma_tx_size / 4)
/* Limit to make sure XDP TX and slow path can coexist */
#define STMMAC_XSK_TX_BUDGET_MAX 256
@@ -297,7 +297,7 @@ static void stmmac_disable_all_queues(struct stmmac_priv *priv)
/* synchronize_rcu() needed for pending XDP buffers to drain */
for (queue = 0; queue < rx_queues_cnt; queue++) {
- rx_q = &priv->dma_conf.rx_queue[queue];
+ rx_q = &priv->dma_conf->rx_queue[queue];
if (rx_q->xsk_pool) {
synchronize_rcu();
break;
@@ -356,10 +356,10 @@ static void print_pkt(unsigned char *buf, int len)
static inline u32 stmmac_tx_avail(struct stmmac_priv *priv, u32 queue)
{
- struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue];
+ struct stmmac_tx_queue *tx_q = &priv->dma_conf->tx_queue[queue];
return CIRC_SPACE(tx_q->cur_tx, tx_q->dirty_tx,
- priv->dma_conf.dma_tx_size);
+ priv->dma_conf->dma_tx_size);
}
static size_t stmmac_get_tx_desc_size(struct stmmac_priv *priv,
@@ -438,7 +438,7 @@ static void stmmac_set_queue_rx_buf_size(struct stmmac_priv *priv,
if (rx_q->xsk_pool && rx_q->buf_alloc_num)
buf_size = xsk_pool_get_rx_frame_size(rx_q->xsk_pool);
else
- buf_size = priv->dma_conf.dma_buf_sz;
+ buf_size = priv->dma_conf->dma_buf_sz;
stmmac_set_dma_bfsize(priv, priv->ioaddr, buf_size, chan);
}
@@ -450,10 +450,10 @@ static void stmmac_set_queue_rx_buf_size(struct stmmac_priv *priv,
*/
static inline u32 stmmac_rx_dirty(struct stmmac_priv *priv, u32 queue)
{
- struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue];
+ struct stmmac_rx_queue *rx_q = &priv->dma_conf->rx_queue[queue];
return CIRC_CNT(rx_q->cur_rx, rx_q->dirty_rx,
- priv->dma_conf.dma_rx_size);
+ priv->dma_conf->dma_rx_size);
}
static bool stmmac_eee_tx_busy(struct stmmac_priv *priv)
@@ -463,7 +463,7 @@ static bool stmmac_eee_tx_busy(struct stmmac_priv *priv)
/* check if all TX queues have the work finished */
for (queue = 0; queue < tx_cnt; queue++) {
- struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue];
+ struct stmmac_tx_queue *tx_q = &priv->dma_conf->tx_queue[queue];
if (tx_q->dirty_tx != tx_q->cur_tx)
return true; /* still unfinished work */
@@ -1629,24 +1629,10 @@ static void stmmac_clear_descriptors(struct stmmac_priv *priv,
stmmac_clear_tx_descriptors(priv, dma_conf, queue);
}
-/**
- * stmmac_init_rx_buffers - init the RX descriptor buffer.
- * @priv: driver private structure
- * @dma_conf: structure to take the dma data
- * @p: descriptor pointer
- * @i: descriptor index
- * @flags: gfp flag
- * @queue: RX queue index
- * Description: this function is called to allocate a receive buffer, perform
- * the DMA mapping and init the descriptor.
- */
-static int stmmac_init_rx_buffers(struct stmmac_priv *priv,
- struct stmmac_dma_conf *dma_conf,
- struct dma_desc *p,
- int i, gfp_t flags, u32 queue)
+static int stmmac_alloc_rx_buffer(struct stmmac_priv *priv,
+ struct stmmac_rx_queue *rx_q,
+ struct stmmac_rx_buffer *buf)
{
- struct stmmac_rx_queue *rx_q = &dma_conf->rx_queue[queue];
- struct stmmac_rx_buffer *buf = &rx_q->buf_pool[i];
gfp_t gfp = (GFP_ATOMIC | __GFP_NOWARN);
if (priv->dma_cap.host_dma_width <= 32)
@@ -1663,19 +1649,49 @@ static int stmmac_init_rx_buffers(struct stmmac_priv *priv,
buf->sec_page = page_pool_alloc_pages(rx_q->page_pool, gfp);
if (!buf->sec_page)
return -ENOMEM;
-
buf->sec_addr = page_pool_get_dma_addr(buf->sec_page);
- stmmac_set_desc_sec_addr(priv, p, buf->sec_addr, true);
- } else {
- buf->sec_page = NULL;
- stmmac_set_desc_sec_addr(priv, p, buf->sec_addr, false);
}
+ return 0;
+}
+
+static void stmmac_init_rx_buffer_desc(struct stmmac_priv *priv,
+ struct stmmac_dma_conf *dma_conf,
+ struct dma_desc *p,
+ struct stmmac_rx_buffer *buf)
+{
+ if (buf->sec_page)
+ buf->sec_addr = page_pool_get_dma_addr(buf->sec_page);
+ stmmac_set_desc_sec_addr(priv, p, buf->sec_addr, !!buf->sec_page);
buf->addr = page_pool_get_dma_addr(buf->page) + buf->page_offset;
stmmac_set_desc_addr(priv, p, buf->addr);
if (dma_conf->dma_buf_sz == BUF_SIZE_16KiB)
stmmac_init_desc3(priv, p);
+}
+
+/**
+ * stmmac_init_rx_buffers - allocate a receive buffer and init its descriptor
+ * @priv: driver private structure
+ * @dma_conf: structure to take the dma data
+ * @p: descriptor pointer
+ * @i: descriptor index
+ * @flags: gfp flag
+ * @queue: RX queue index
+ */
+static int stmmac_init_rx_buffers(struct stmmac_priv *priv,
+ struct stmmac_dma_conf *dma_conf,
+ struct dma_desc *p,
+ int i, gfp_t flags, u32 queue)
+{
+ struct stmmac_rx_queue *rx_q = &dma_conf->rx_queue[queue];
+ struct stmmac_rx_buffer *buf = &rx_q->buf_pool[i];
+ int ret;
+
+ ret = stmmac_alloc_rx_buffer(priv, rx_q, buf);
+ if (ret)
+ return ret;
+ stmmac_init_rx_buffer_desc(priv, dma_conf, p, buf);
return 0;
}
@@ -1761,6 +1777,9 @@ static void dma_free_rx_skbufs(struct stmmac_priv *priv,
struct stmmac_rx_queue *rx_q = &dma_conf->rx_queue[queue];
int i;
+ if (!rx_q->buf_pool)
+ return;
+
for (i = 0; i < dma_conf->dma_rx_size; i++)
stmmac_free_rx_buffer(priv, rx_q, i);
}
@@ -1884,17 +1903,19 @@ static int __init_dma_rx_desc_rings(struct stmmac_priv *priv,
rx_q->xsk_pool = stmmac_get_xsk_pool(priv, queue);
if (rx_q->xsk_pool) {
- WARN_ON(xdp_rxq_info_reg_mem_model(&rx_q->xdp_rxq,
- MEM_TYPE_XSK_BUFF_POOL,
- NULL));
+ ret = xdp_rxq_info_reg_mem_model(&rx_q->xdp_rxq,
+ MEM_TYPE_XSK_BUFF_POOL, NULL);
+ if (ret)
+ return ret;
netdev_info(priv->dev,
"Register MEM_TYPE_XSK_BUFF_POOL RxQ-%d\n",
queue);
xsk_pool_set_rxq_info(rx_q->xsk_pool, &rx_q->xdp_rxq);
} else {
- WARN_ON(xdp_rxq_info_reg_mem_model(&rx_q->xdp_rxq,
- MEM_TYPE_PAGE_POOL,
- rx_q->page_pool));
+ ret = xdp_rxq_info_reg_mem_model(&rx_q->xdp_rxq,
+ MEM_TYPE_PAGE_POOL, rx_q->page_pool);
+ if (ret)
+ return ret;
netdev_info(priv->dev,
"Register MEM_TYPE_PAGE_POOL RxQ-%d\n",
queue);
@@ -1956,6 +1977,8 @@ static int init_dma_rx_desc_rings(struct net_device *dev,
dma_free_rx_skbufs(priv, dma_conf, queue);
rx_q->buf_alloc_num = 0;
+ if (rx_q->xsk_pool)
+ xsk_pool_set_rxq_info(rx_q->xsk_pool, NULL);
rx_q->xsk_pool = NULL;
queue--;
@@ -2099,6 +2122,9 @@ static void dma_free_tx_skbufs(struct stmmac_priv *priv,
tx_q->xsk_frames_done = 0;
+ if (!tx_q->tx_skbuff_dma || !tx_q->tx_skbuff)
+ return;
+
for (i = 0; i < dma_conf->dma_tx_size; i++)
stmmac_free_tx_buffer(priv, dma_conf, queue, i);
@@ -2119,7 +2145,64 @@ static void stmmac_free_tx_skbufs(struct stmmac_priv *priv)
u8 queue;
for (queue = 0; queue < tx_queue_cnt; queue++)
- dma_free_tx_skbufs(priv, &priv->dma_conf, queue);
+ dma_free_tx_skbufs(priv, priv->dma_conf, queue);
+}
+
+/* NAPI is stopped, but DMA may still be using the old rings. Fill holes in
+ * the software buffer array without changing any descriptors. If allocation
+ * fails, the old rings can continue unchanged. Otherwise rollback after a
+ * reset will not need to allocate buffers.
+ */
+static int stmmac_prepare_rx_buffers(struct stmmac_priv *priv)
+{
+ struct stmmac_dma_conf *dma_conf = priv->dma_conf;
+ u32 queue, i;
+ int ret;
+
+ for (queue = 0; queue < priv->plat->rx_queues_to_use; queue++) {
+ struct stmmac_rx_queue *rx_q = &dma_conf->rx_queue[queue];
+
+ for (i = 0; i < dma_conf->dma_rx_size; i++) {
+ ret = stmmac_alloc_rx_buffer(priv, rx_q, &rx_q->buf_pool[i]);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+/* Only after a successful DMA reset, and with all RX buffers prepared. */
+static void stmmac_reinit_dma_desc(struct stmmac_priv *priv)
+{
+ struct stmmac_dma_conf *dma_conf = priv->dma_conf;
+ u32 queue, i;
+
+ stmmac_free_tx_skbufs(priv);
+ stmmac_reset_queues_param(priv);
+ init_dma_tx_desc_rings(priv->dev, dma_conf);
+ stmmac_clear_descriptors(priv, dma_conf);
+
+ for (queue = 0; queue < priv->plat->rx_queues_to_use; queue++) {
+ struct stmmac_rx_queue *rx_q = &dma_conf->rx_queue[queue];
+
+ if (rx_q->state_saved)
+ dev_kfree_skb_any(rx_q->state.skb);
+ rx_q->state.skb = NULL;
+ rx_q->state_saved = 0;
+ rx_q->rx_count_frames = 0;
+ rx_q->buf_alloc_num = dma_conf->dma_rx_size;
+
+ for (i = 0; i < dma_conf->dma_rx_size; i++)
+ stmmac_init_rx_buffer_desc(priv, dma_conf,
+ stmmac_get_rx_desc(priv, rx_q, i),
+ &rx_q->buf_pool[i]);
+
+ if (priv->descriptor_mode == STMMAC_CHAIN_MODE)
+ stmmac_mode_init(priv, stmmac_get_rx_desc(priv, rx_q, 0),
+ rx_q->dma_rx_phy, dma_conf->dma_rx_size,
+ priv->extend_desc);
+ }
}
/**
@@ -2137,10 +2220,16 @@ static void __free_dma_rx_desc_resources(struct stmmac_priv *priv,
void *addr;
/* Release the DMA RX socket buffers */
- if (rx_q->xsk_pool)
+ if (rx_q->xsk_pool) {
dma_free_rx_xskbufs(priv, dma_conf, queue);
- else
+ xsk_pool_set_rxq_info(rx_q->xsk_pool, NULL);
+ } else {
dma_free_rx_skbufs(priv, dma_conf, queue);
+ }
+ if (rx_q->state_saved)
+ dev_kfree_skb_any(rx_q->state.skb);
+ rx_q->state.skb = NULL;
+ rx_q->state_saved = 0;
rx_q->buf_alloc_num = 0;
rx_q->xsk_pool = NULL;
@@ -2153,7 +2242,8 @@ static void __free_dma_rx_desc_resources(struct stmmac_priv *priv,
size = stmmac_get_rx_desc_size(priv) * dma_conf->dma_rx_size;
- dma_free_coherent(priv->device, size, addr, rx_q->dma_rx_phy);
+ if (addr)
+ dma_free_coherent(priv->device, size, addr, rx_q->dma_rx_phy);
if (xdp_rxq_info_is_reg(&rx_q->xdp_rxq))
xdp_rxq_info_unreg(&rx_q->xdp_rxq);
@@ -2161,6 +2251,10 @@ static void __free_dma_rx_desc_resources(struct stmmac_priv *priv,
kfree(rx_q->buf_pool);
if (rx_q->page_pool)
page_pool_destroy(rx_q->page_pool);
+ rx_q->buf_pool = NULL;
+ rx_q->page_pool = NULL;
+ rx_q->dma_erx = NULL;
+ rx_q->dma_rx = NULL;
}
static void free_dma_rx_desc_resources(struct stmmac_priv *priv,
@@ -2201,10 +2295,17 @@ static void __free_dma_tx_desc_resources(struct stmmac_priv *priv,
size = stmmac_get_tx_desc_size(priv, tx_q) * dma_conf->dma_tx_size;
- dma_free_coherent(priv->device, size, addr, tx_q->dma_tx_phy);
+ if (addr)
+ dma_free_coherent(priv->device, size, addr, tx_q->dma_tx_phy);
kfree(tx_q->tx_skbuff_dma);
kfree(tx_q->tx_skbuff);
+ tx_q->tx_skbuff_dma = NULL;
+ tx_q->tx_skbuff = NULL;
+ tx_q->dma_etx = NULL;
+ tx_q->dma_entx = NULL;
+ tx_q->dma_tx = NULL;
+ tx_q->xsk_pool = NULL;
}
static void free_dma_tx_desc_resources(struct stmmac_priv *priv,
@@ -2411,6 +2512,8 @@ static int alloc_dma_desc_resources(struct stmmac_priv *priv,
return ret;
ret = alloc_dma_tx_desc_resources(priv, dma_conf);
+ if (ret)
+ free_dma_rx_desc_resources(priv, dma_conf);
return ret;
}
@@ -2609,7 +2712,7 @@ static void stmmac_dma_operation_mode(struct stmmac_priv *priv)
/* configure all channels */
for (chan = 0; chan < rx_channels_count; chan++) {
- struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[chan];
+ struct stmmac_rx_queue *rx_q = &priv->dma_conf->rx_queue[chan];
qmode = priv->plat->rx_queues_cfg[chan].mode_to_use;
@@ -2681,7 +2784,7 @@ static const struct xsk_tx_metadata_ops stmmac_xsk_tx_metadata_ops = {
static bool stmmac_xdp_xmit_zc(struct stmmac_priv *priv, u32 queue, u32 budget)
{
struct netdev_queue *nq = netdev_get_tx_queue(priv->dev, queue);
- struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue];
+ struct stmmac_tx_queue *tx_q = &priv->dma_conf->tx_queue[queue];
struct stmmac_txq_stats *txq_stats = &priv->xstats.txq_stats[queue];
bool csum = !priv->plat->tx_queues_cfg[queue].coe_unsupported;
struct xsk_buff_pool *pool = tx_q->xsk_pool;
@@ -2770,7 +2873,7 @@ static bool stmmac_xdp_xmit_zc(struct stmmac_priv *priv, u32 queue, u32 budget)
xsk_tx_metadata_to_compl(meta,
&tx_q->tx_skbuff_dma[entry].xsk_meta);
- tx_q->cur_tx = STMMAC_NEXT_ENTRY(tx_q->cur_tx, priv->dma_conf.dma_tx_size);
+ tx_q->cur_tx = STMMAC_NEXT_ENTRY(tx_q->cur_tx, priv->dma_conf->dma_tx_size);
entry = tx_q->cur_tx;
}
u64_stats_update_begin(&txq_stats->napi_syncp);
@@ -2818,7 +2921,7 @@ static void stmmac_bump_dma_threshold(struct stmmac_priv *priv, u32 chan)
static int stmmac_tx_clean(struct stmmac_priv *priv, int budget, u32 queue,
bool *pending_packets)
{
- struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue];
+ struct stmmac_tx_queue *tx_q = &priv->dma_conf->tx_queue[queue];
struct stmmac_txq_stats *txq_stats = &priv->xstats.txq_stats[queue];
unsigned int bytes_compl = 0, pkts_compl = 0;
unsigned int entry, xmits = 0, count = 0;
@@ -2831,7 +2934,7 @@ static int stmmac_tx_clean(struct stmmac_priv *priv, int budget, u32 queue,
entry = tx_q->dirty_tx;
/* Try to clean all TX complete frame in 1 shot */
- while ((entry != tx_q->cur_tx) && count < priv->dma_conf.dma_tx_size) {
+ while ((entry != tx_q->cur_tx) && count < priv->dma_conf->dma_tx_size) {
struct xdp_frame *xdpf;
struct sk_buff *skb;
struct dma_desc *p;
@@ -2938,7 +3041,7 @@ static int stmmac_tx_clean(struct stmmac_priv *priv, int budget, u32 queue,
stmmac_release_tx_desc(priv, p, priv->descriptor_mode);
- entry = STMMAC_NEXT_ENTRY(entry, priv->dma_conf.dma_tx_size);
+ entry = STMMAC_NEXT_ENTRY(entry, priv->dma_conf->dma_tx_size);
}
tx_q->dirty_tx = entry;
@@ -3006,13 +3109,13 @@ static int stmmac_tx_clean(struct stmmac_priv *priv, int budget, u32 queue,
*/
static void stmmac_tx_err(struct stmmac_priv *priv, u32 chan)
{
- struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[chan];
+ struct stmmac_tx_queue *tx_q = &priv->dma_conf->tx_queue[chan];
netif_tx_stop_queue(netdev_get_tx_queue(priv->dev, chan));
stmmac_stop_tx_dma(priv, chan);
- dma_free_tx_skbufs(priv, &priv->dma_conf, chan);
- stmmac_clear_tx_descriptors(priv, &priv->dma_conf, chan);
+ dma_free_tx_skbufs(priv, priv->dma_conf, chan);
+ stmmac_clear_tx_descriptors(priv, priv->dma_conf, chan);
stmmac_reset_tx_queue(priv, chan);
stmmac_init_tx_chan(priv, priv->ioaddr, priv->plat->dma_cfg,
tx_q->dma_tx_phy, chan);
@@ -3073,8 +3176,8 @@ static int stmmac_napi_check(struct stmmac_priv *priv, u32 chan, u32 dir)
{
int status = stmmac_dma_interrupt_status(priv, priv->ioaddr,
&priv->xstats, chan, dir);
- struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[chan];
- struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[chan];
+ struct stmmac_rx_queue *rx_q = &priv->dma_conf->rx_queue[chan];
+ struct stmmac_tx_queue *tx_q = &priv->dma_conf->tx_queue[chan];
struct stmmac_channel *ch = &priv->channel[chan];
struct napi_struct *rx_napi;
struct napi_struct *tx_napi;
@@ -3255,12 +3358,13 @@ static int stmmac_prereset_configure(struct stmmac_priv *priv)
/**
* stmmac_init_dma_engine - DMA init.
* @priv: driver private structure
+ * @reinit: rebuild the retained rings after a successful reset
* Description:
* It inits the DMA invoking the specific MAC/GMAC callback.
* Some DMA parameters can be passed from the platform;
* in case of these are not passed a default is kept for the MAC or GMAC.
*/
-static int stmmac_init_dma_engine(struct stmmac_priv *priv)
+static int stmmac_init_dma_engine(struct stmmac_priv *priv, bool reinit)
{
u8 rx_channels_count = priv->plat->rx_queues_to_use;
u8 tx_channels_count = priv->plat->tx_queues_to_use;
@@ -3280,6 +3384,9 @@ static int stmmac_init_dma_engine(struct stmmac_priv *priv)
return ret;
}
+ if (reinit)
+ stmmac_reinit_dma_desc(priv);
+
/* DMA Configuration */
stmmac_dma_init(priv, priv->ioaddr, priv->plat->dma_cfg);
@@ -3294,7 +3401,7 @@ static int stmmac_init_dma_engine(struct stmmac_priv *priv)
/* DMA RX Channel Configuration */
for (chan = 0; chan < rx_channels_count; chan++) {
- rx_q = &priv->dma_conf.rx_queue[chan];
+ rx_q = &priv->dma_conf->rx_queue[chan];
stmmac_init_rx_chan(priv, priv->ioaddr, priv->plat->dma_cfg,
rx_q->dma_rx_phy, chan);
@@ -3305,7 +3412,7 @@ static int stmmac_init_dma_engine(struct stmmac_priv *priv)
/* DMA TX Channel Configuration */
for (chan = 0; chan < tx_channels_count; chan++) {
- tx_q = &priv->dma_conf.tx_queue[chan];
+ tx_q = &priv->dma_conf->tx_queue[chan];
stmmac_init_tx_chan(priv, priv->ioaddr, priv->plat->dma_cfg,
tx_q->dma_tx_phy, chan);
@@ -3318,7 +3425,7 @@ static int stmmac_init_dma_engine(struct stmmac_priv *priv)
static void stmmac_tx_timer_arm(struct stmmac_priv *priv, u32 queue)
{
- struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue];
+ struct stmmac_tx_queue *tx_q = &priv->dma_conf->tx_queue[queue];
u32 tx_coal_timer = priv->tx_coal_timer[queue];
struct stmmac_channel *ch;
struct napi_struct *napi;
@@ -3386,7 +3493,7 @@ static void stmmac_init_coalesce(struct stmmac_priv *priv)
u8 chan;
for (chan = 0; chan < tx_channel_count; chan++) {
- struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[chan];
+ struct stmmac_tx_queue *tx_q = &priv->dma_conf->tx_queue[chan];
priv->tx_coal_frames[chan] = STMMAC_TX_FRAMES;
priv->tx_coal_timer[chan] = STMMAC_COAL_TX_TIMER;
@@ -3407,12 +3514,12 @@ static void stmmac_set_rings_length(struct stmmac_priv *priv)
/* set TX ring length */
for (chan = 0; chan < tx_channels_count; chan++)
stmmac_set_tx_ring_len(priv, priv->ioaddr,
- (priv->dma_conf.dma_tx_size - 1), chan);
+ (priv->dma_conf->dma_tx_size - 1), chan);
/* set RX ring length */
for (chan = 0; chan < rx_channels_count; chan++)
stmmac_set_rx_ring_len(priv, priv->ioaddr,
- (priv->dma_conf.dma_rx_size - 1), chan);
+ (priv->dma_conf->dma_rx_size - 1), chan);
}
/**
@@ -3621,12 +3728,13 @@ static bool stmmac_tso_channel_permitted(struct stmmac_priv *priv,
unsigned int chan)
{
/* TSO and TBS cannot co-exist */
- return !(priv->dma_conf.tx_queue[chan].tbs & STMMAC_TBS_AVAIL);
+ return !(priv->dma_conf->tx_queue[chan].tbs & STMMAC_TBS_AVAIL);
}
/**
* stmmac_hw_setup - setup mac in a usable state.
* @dev : pointer to the device structure.
+ * @reinit: rebuild retained descriptor rings after the DMA reset
* Description:
* this is the main function to setup the HW in a usable state because the
* dma engine is reset, the core registers are configured (e.g. AXI,
@@ -3636,7 +3744,7 @@ static bool stmmac_tso_channel_permitted(struct stmmac_priv *priv,
* 0 on success and an appropriate (-)ve integer as defined in errno.h
* file on failure.
*/
-static int stmmac_hw_setup(struct net_device *dev)
+static int stmmac_hw_setup(struct net_device *dev, bool reinit)
{
struct stmmac_priv *priv = netdev_priv(dev);
u8 rx_cnt = priv->plat->rx_queues_to_use;
@@ -3658,7 +3766,7 @@ static int stmmac_hw_setup(struct net_device *dev)
phylink_rx_clk_stop_block(priv->phylink);
/* DMA initialization and SW reset */
- ret = stmmac_init_dma_engine(priv);
+ ret = stmmac_init_dma_engine(priv, reinit);
if (ret < 0) {
phylink_rx_clk_stop_unblock(priv->phylink);
netdev_err(priv->dev, "%s: DMA engine initialization failed\n",
@@ -3739,7 +3847,7 @@ static int stmmac_hw_setup(struct net_device *dev)
/* TBS */
for (chan = 0; chan < tx_cnt; chan++) {
- struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[chan];
+ struct stmmac_tx_queue *tx_q = &priv->dma_conf->tx_queue[chan];
int enable = tx_q->tbs & STMMAC_TBS_AVAIL;
stmmac_enable_tbs(priv, priv->ioaddr, enable, chan);
@@ -3774,8 +3882,7 @@ static void stmmac_free_irq(struct net_device *dev,
for (j = irq_idx - 1; msi && j >= 0; j--) {
if (msi->tx_irq[j] > 0) {
irq_set_affinity_hint(msi->tx_irq[j], NULL);
- free_irq(msi->tx_irq[j],
- &priv->dma_conf.tx_queue[j]);
+ free_irq(msi->tx_irq[j], &priv->channel[j]);
}
}
irq_idx = priv->plat->rx_queues_to_use;
@@ -3784,8 +3891,7 @@ static void stmmac_free_irq(struct net_device *dev,
for (j = irq_idx - 1; msi && j >= 0; j--) {
if (msi->rx_irq[j] > 0) {
irq_set_affinity_hint(msi->rx_irq[j], NULL);
- free_irq(msi->rx_irq[j],
- &priv->dma_conf.rx_queue[j]);
+ free_irq(msi->rx_irq[j], &priv->channel[j]);
}
}
@@ -3797,13 +3903,13 @@ static void stmmac_free_irq(struct net_device *dev,
free_irq(msi->sfty_ce_irq, dev);
fallthrough;
case REQ_IRQ_ERR_SFTY_CE:
- if (priv->wol_irq > 0 && priv->wol_irq != dev->irq)
- free_irq(priv->wol_irq, dev);
- fallthrough;
- case REQ_IRQ_ERR_SFTY:
if (priv->sfty_irq > 0 && priv->sfty_irq != dev->irq)
free_irq(priv->sfty_irq, dev);
fallthrough;
+ case REQ_IRQ_ERR_SFTY:
+ if (priv->wol_irq > 0 && priv->wol_irq != dev->irq)
+ free_irq(priv->wol_irq, dev);
+ fallthrough;
case REQ_IRQ_ERR_WOL:
free_irq(dev->irq, dev);
fallthrough;
@@ -3939,7 +4045,7 @@ static int stmmac_request_irq_multi_msi(struct net_device *dev)
sprintf(int_name, "%s:%s-%d", dev->name, "rx", i);
ret = request_irq(msi->rx_irq[i],
stmmac_msi_intr_rx,
- 0, int_name, &priv->dma_conf.rx_queue[i]);
+ 0, int_name, &priv->channel[i]);
if (unlikely(ret < 0)) {
netdev_err(priv->dev,
"%s: alloc rx-%d MSI %d (error: %d)\n",
@@ -3963,7 +4069,7 @@ static int stmmac_request_irq_multi_msi(struct net_device *dev)
sprintf(int_name, "%s:%s-%d", dev->name, "tx", i);
ret = request_irq(msi->tx_irq[i],
stmmac_msi_intr_tx,
- 0, int_name, &priv->dma_conf.tx_queue[i]);
+ 0, int_name, &priv->channel[i]);
if (unlikely(ret < 0)) {
netdev_err(priv->dev,
"%s: alloc tx-%d MSI %d (error: %d)\n",
@@ -4050,14 +4156,40 @@ static int stmmac_request_irq(struct net_device *dev)
return ret;
}
+/* Balance disable_irq()/enable_irq() for every registered IRQ, including
+ * shared lines. Unlike freeing and requesting IRQs, this cannot fail.
+ */
+static void stmmac_set_irq_state(struct stmmac_priv *priv, bool enable)
+{
+ void (*set_state)(unsigned int) = enable ? enable_irq : disable_irq;
+ struct stmmac_msi *msi = priv->msi;
+ int irq = priv->dev->irq;
+ u32 i;
+
+ set_state(irq);
+ if (priv->wol_irq > 0 && priv->wol_irq != irq)
+ set_state(priv->wol_irq);
+ if (priv->sfty_irq > 0 && priv->sfty_irq != irq)
+ set_state(priv->sfty_irq);
+ if (!msi)
+ return;
+ if (msi->sfty_ce_irq > 0 && msi->sfty_ce_irq != irq)
+ set_state(msi->sfty_ce_irq);
+ if (msi->sfty_ue_irq > 0 && msi->sfty_ue_irq != irq)
+ set_state(msi->sfty_ue_irq);
+ for (i = 0; i < priv->plat->rx_queues_to_use; i++)
+ if (msi->rx_irq[i] > 0)
+ set_state(msi->rx_irq[i]);
+ for (i = 0; i < priv->plat->tx_queues_to_use; i++)
+ if (msi->tx_irq[i] > 0)
+ set_state(msi->tx_irq[i]);
+}
+
/**
- * stmmac_setup_dma_desc - Generate a dma_conf and allocate DMA queue
- * @priv: driver private structure
- * @mtu: MTU to setup the dma queue and buf with
- * Description: Allocate and generate a dma_conf based on the provided MTU.
- * Allocate the Tx/Rx DMA queue and init them.
- * Return value:
- * the dma_conf allocated struct on success and an appropriate ERR_PTR on failure.
+ * stmmac_setup_dma_desc - allocate and initialize a DMA configuration
+ * @priv: driver private structure
+ * @mtu: MTU to size the receive buffers for
+ * Return: the allocated configuration, or an ERR_PTR on failure
*/
static struct stmmac_dma_conf *
stmmac_setup_dma_desc(struct stmmac_priv *priv, unsigned int mtu)
@@ -4087,8 +4219,8 @@ stmmac_setup_dma_desc(struct stmmac_priv *priv, unsigned int mtu)
/* Chose the tx/rx size from the already defined one in the
* priv struct. (if defined)
*/
- dma_conf->dma_tx_size = priv->dma_conf.dma_tx_size;
- dma_conf->dma_rx_size = priv->dma_conf.dma_rx_size;
+ dma_conf->dma_tx_size = priv->dma_conf->dma_tx_size;
+ dma_conf->dma_rx_size = priv->dma_conf->dma_rx_size;
if (!dma_conf->dma_tx_size)
dma_conf->dma_tx_size = DMA_DEFAULT_TX_SIZE;
@@ -4102,6 +4234,7 @@ stmmac_setup_dma_desc(struct stmmac_priv *priv, unsigned int mtu)
/* Setup per-TXQ tbs flag before TX descriptor alloc */
tx_q->tbs |= tbs_en ? STMMAC_TBS_AVAIL : 0;
+ tx_q->tbs |= priv->dma_conf->tx_queue[chan].tbs & STMMAC_TBS_EN;
}
ret = alloc_dma_desc_resources(priv, dma_conf);
@@ -4127,6 +4260,37 @@ stmmac_setup_dma_desc(struct stmmac_priv *priv, unsigned int mtu)
return ERR_PTR(ret);
}
+/* Finish core sleep state even if the platform resume callback failed. */
+static int stmmac_resume_hw(struct stmmac_priv *priv)
+{
+ int ret;
+
+ if (!priv->hw_suspended)
+ return 0;
+
+ /* Use the state installed by suspend, not a subsequently changed WoL
+ * setting. Clear PMT even when a different device caused the wakeup.
+ */
+ if (priv->irq_wake) {
+ mutex_lock(&priv->lock);
+ stmmac_pmt(priv, priv->hw, 0);
+ mutex_unlock(&priv->lock);
+ priv->irq_wake = 0;
+ } else {
+ ret = pinctrl_pm_select_default_state(priv->device);
+ if (ret)
+ return ret;
+ if (priv->mii) {
+ ret = stmmac_mdio_reset(priv->mii);
+ if (ret)
+ return ret;
+ }
+ }
+ priv->hw_suspended = false;
+
+ return 0;
+}
+
/**
* __stmmac_open - open entry point of the driver
* @dev : pointer to the device structure.
@@ -4144,10 +4308,8 @@ static int __stmmac_open(struct net_device *dev,
u8 chan;
int ret;
- for (int i = 0; i < priv->plat->tx_queues_to_use; i++)
- if (priv->dma_conf.tx_queue[i].tbs & STMMAC_TBS_EN)
- dma_conf->tx_queue[i].tbs = priv->dma_conf.tx_queue[i].tbs;
- memcpy(&priv->dma_conf, dma_conf, sizeof(*dma_conf));
+ /* Keep RXQ metadata and timers at their registered addresses. */
+ priv->dma_conf = dma_conf;
/* The PHY is suspended when the interface is reopened without
* disconnecting the PHY, e.g. on MTU change. IEEE 802.3 allows PHYs
@@ -4160,7 +4322,7 @@ static int __stmmac_open(struct net_device *dev,
stmmac_reset_queues_param(priv);
- ret = stmmac_hw_setup(dev);
+ ret = stmmac_hw_setup(dev, false);
if (ret < 0) {
netdev_err(priv->dev, "%s: Hw setup failed\n", __func__);
goto init_error;
@@ -4181,6 +4343,7 @@ static int __stmmac_open(struct net_device *dev,
stmmac_enable_all_queues(priv);
netif_tx_start_all_queues(priv->dev);
stmmac_enable_all_dma_irq(priv);
+ priv->datapath = STMMAC_DATAPATH_RUNNING;
return 0;
@@ -4188,16 +4351,19 @@ static int __stmmac_open(struct net_device *dev,
phylink_stop(priv->phylink);
for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++)
- hrtimer_cancel(&priv->dma_conf.tx_queue[chan].txtimer);
+ hrtimer_cancel(&priv->dma_conf->tx_queue[chan].txtimer);
stmmac_release_ptp(priv);
init_error:
+ stmmac_stop_all_dma(priv);
+ stmmac_mac_set(priv, priv->ioaddr, false);
return ret;
}
static int stmmac_open(struct net_device *dev)
{
struct stmmac_priv *priv = netdev_priv(dev);
+ struct stmmac_dma_conf *old_conf = priv->dma_conf;
struct stmmac_dma_conf *dma_conf;
int ret;
@@ -4213,6 +4379,10 @@ static int stmmac_open(struct net_device *dev)
if (ret < 0)
goto err_dma_resources;
+ ret = stmmac_resume_hw(priv);
+ if (ret)
+ goto err_runtime_pm;
+
ret = stmmac_init_phy(dev);
if (ret)
goto err_runtime_pm;
@@ -4227,7 +4397,7 @@ static int stmmac_open(struct net_device *dev)
if (ret)
goto err_serdes;
- kfree(dma_conf);
+ kfree(old_conf);
/* We may have called phylink_speed_down before */
phylink_speed_up(priv->phylink);
@@ -4241,34 +4411,65 @@ static int stmmac_open(struct net_device *dev)
err_runtime_pm:
pm_runtime_put(priv->device);
err_dma_resources:
+ priv->dma_conf = old_conf;
free_dma_desc_resources(priv, dma_conf);
kfree(dma_conf);
return ret;
}
-static void __stmmac_release(struct net_device *dev)
+static void stmmac_stop_tx_queues(struct stmmac_priv *priv)
{
- struct stmmac_priv *priv = netdev_priv(dev);
u8 chan;
- /* Stop and disconnect the PHY */
- phylink_stop(priv->phylink);
+ netif_tx_disable(priv->dev);
- stmmac_disable_all_queues(priv);
+ /* A poll function can still arm a timer after napi_complete_done().
+ * Drain those poll tails and in-flight transmitters before cancelling
+ * the timers, so none can be rearmed after their final cancellation.
+ */
+ synchronize_net();
for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++)
- hrtimer_cancel(&priv->dma_conf.tx_queue[chan].txtimer);
+ hrtimer_cancel(&priv->dma_conf->tx_queue[chan].txtimer);
+}
- netif_tx_disable(dev);
+/* Quiesce NAPI and transmit queues without releasing their resources. */
+static void stmmac_quiesce(struct stmmac_priv *priv)
+{
+ stmmac_disable_all_queues(priv);
+ stmmac_stop_tx_queues(priv);
+}
+
+static void __stmmac_release(struct net_device *dev)
+{
+ struct stmmac_priv *priv = netdev_priv(dev);
+ enum stmmac_datapath_state state = priv->datapath;
+
+ /* There may be no resources left after detached XDP reconfiguration. */
+ if (priv->datapath == STMMAC_DATAPATH_DOWN)
+ return;
+
+ phylink_stop(priv->phylink);
+
+ /* Suspend retains the resources, but has already stopped activity. */
+ if (priv->datapath == STMMAC_DATAPATH_RUNNING)
+ stmmac_quiesce(priv);
+ priv->datapath = STMMAC_DATAPATH_DOWN;
/* Free the IRQ lines */
- stmmac_free_irq(dev, REQ_IRQ_ERR_ALL, 0);
+ if (state != STMMAC_DATAPATH_HALTED)
+ stmmac_free_irq(dev, REQ_IRQ_ERR_ALL, 0);
- /* Stop TX/RX DMA and clear the descriptors */
+ /* TX error IRQs can restart a queue after the first quiescence. */
+ stmmac_stop_tx_queues(priv);
+
+ /* Stop TX/RX DMA after draining IRQ handlers which can restart it. */
stmmac_stop_all_dma(priv);
+ /* Link resolution need not have reached mac_link_up() yet. */
+ stmmac_mac_set(priv, priv->ioaddr, false);
/* Release and free the Rx/Tx resources */
- free_dma_desc_resources(priv, &priv->dma_conf);
+ free_dma_desc_resources(priv, priv->dma_conf);
stmmac_release_ptp(priv);
@@ -4285,6 +4486,15 @@ static void __stmmac_release(struct net_device *dev)
static int stmmac_release(struct net_device *dev)
{
struct stmmac_priv *priv = netdev_priv(dev);
+ int ret;
+
+ /* Resume may have failed before restoring pins or disabling MAC wake.
+ * Complete that cleanup without restarting the link or the datapath.
+ * If it fails, keep hw_suspended set so a fresh open can retry it.
+ */
+ ret = stmmac_resume_hw(priv);
+ if (ret)
+ netdev_err(dev, "failed to restore hardware sleep state: %d\n", ret);
/* If the PHY or MAC has WoL enabled, then the PHY will not be
* suspended when phylink_stop() is called below. Set the PHY
@@ -4298,6 +4508,8 @@ static int stmmac_release(struct net_device *dev)
stmmac_legacy_serdes_power_down(priv);
phylink_disconnect_phy(priv->phylink);
pm_runtime_put(priv->device);
+ /* Allow a fresh open after a failed MTU reopen or resume. */
+ netif_device_attach(dev);
return 0;
}
@@ -4322,7 +4534,7 @@ static bool stmmac_vlan_insert(struct stmmac_priv *priv, struct sk_buff *skb,
return false;
stmmac_set_tx_owner(priv, p);
- tx_q->cur_tx = STMMAC_NEXT_ENTRY(tx_q->cur_tx, priv->dma_conf.dma_tx_size);
+ tx_q->cur_tx = STMMAC_NEXT_ENTRY(tx_q->cur_tx, priv->dma_conf->dma_tx_size);
return true;
}
@@ -4342,7 +4554,7 @@ static void stmmac_tso_allocator(struct stmmac_priv *priv, u32 *entry,
dma_addr_t des, int total_len,
bool last_segment, u32 queue)
{
- struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue];
+ struct stmmac_tx_queue *tx_q = &priv->dma_conf->tx_queue[queue];
struct dma_desc *desc;
u32 buff_size;
int tmp_len;
@@ -4352,7 +4564,7 @@ static void stmmac_tso_allocator(struct stmmac_priv *priv, u32 *entry,
while (tmp_len > 0) {
dma_addr_t curr_addr;
- *entry = STMMAC_NEXT_ENTRY(*entry, priv->dma_conf.dma_tx_size);
+ *entry = STMMAC_NEXT_ENTRY(*entry, priv->dma_conf->dma_tx_size);
WARN_ON(tx_q->tx_skbuff[*entry]);
if (tx_q->tbs & STMMAC_TBS_AVAIL)
@@ -4376,7 +4588,7 @@ static void stmmac_tso_allocator(struct stmmac_priv *priv, u32 *entry,
static void stmmac_flush_tx_descriptors(struct stmmac_priv *priv, int queue)
{
- struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue];
+ struct stmmac_tx_queue *tx_q = &priv->dma_conf->tx_queue[queue];
/* The own bit must be the latest setting done when prepare the
* descriptor and then barrier is needed to make sure that
@@ -4529,7 +4741,7 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
nfrags = skb_shinfo(skb)->nr_frags;
queue = skb_get_queue_mapping(skb);
- tx_q = &priv->dma_conf.tx_queue[queue];
+ tx_q = &priv->dma_conf->tx_queue[queue];
txq_stats = &priv->xstats.txq_stats[queue];
first_tx = tx_q->cur_tx;
@@ -4567,7 +4779,7 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
stmmac_set_mss(priv, mss_desc, mss);
tx_q->mss = mss;
tx_q->cur_tx = STMMAC_NEXT_ENTRY(tx_q->cur_tx,
- priv->dma_conf.dma_tx_size);
+ priv->dma_conf->dma_tx_size);
WARN_ON(tx_q->tx_skbuff[tx_q->cur_tx]);
}
@@ -4638,7 +4850,7 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
/* Manage tx mitigation */
tx_packets = CIRC_CNT(tx_q->cur_tx + 1, first_tx,
- priv->dma_conf.dma_tx_size);
+ priv->dma_conf->dma_tx_size);
tx_q->tx_count_frames += tx_packets;
if ((skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) && priv->hwts_tx_en)
@@ -4670,7 +4882,7 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
* ndo_start_xmit will fill this descriptor the next time it's
* called and stmmac_tx_clean may clean up to this descriptor.
*/
- tx_q->cur_tx = STMMAC_NEXT_ENTRY(tx_q->cur_tx, priv->dma_conf.dma_tx_size);
+ tx_q->cur_tx = STMMAC_NEXT_ENTRY(tx_q->cur_tx, priv->dma_conf->dma_tx_size);
if (unlikely(stmmac_tx_avail(priv, queue) <= (MAX_SKB_FRAGS + 1))) {
netif_dbg(priv, hw, priv->dev, "%s: stop transmitted packets\n",
@@ -4700,7 +4912,7 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
* segment.
*/
is_last_segment = CIRC_CNT(tx_q->cur_tx, first_entry,
- priv->dma_conf.dma_tx_size) == 1;
+ priv->dma_conf->dma_tx_size) == 1;
/* Complete the first descriptor before granting the DMA */
stmmac_prepare_tso_tx_desc(priv, first, 1, proto_hdr_len, 0, 1,
@@ -4738,13 +4950,13 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
for (;;) {
desc = stmmac_get_tx_desc(priv, tx_q, first_entry);
stmmac_release_tx_desc(priv, desc, priv->descriptor_mode);
- stmmac_free_tx_buffer(priv, &priv->dma_conf, queue,
+ stmmac_free_tx_buffer(priv, priv->dma_conf, queue,
first_entry);
if (first_entry == entry)
break;
first_entry = STMMAC_NEXT_ENTRY(first_entry,
- priv->dma_conf.dma_tx_size);
+ priv->dma_conf->dma_tx_size);
}
error:
dev_err(priv->device, "Tx dma map failed\n");
@@ -4829,7 +5041,7 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
return NETDEV_TX_BUSY;
}
- tx_q = &priv->dma_conf.tx_queue[queue];
+ tx_q = &priv->dma_conf->tx_queue[queue];
first_tx = tx_q->cur_tx;
/* Check if VLAN can be inserted by HW */
@@ -4905,7 +5117,7 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
unsigned int frag_size = skb_frag_size(frag);
bool last_segment = (i == (nfrags - 1));
- entry = STMMAC_NEXT_ENTRY(entry, priv->dma_conf.dma_tx_size);
+ entry = STMMAC_NEXT_ENTRY(entry, priv->dma_conf->dma_tx_size);
WARN_ON(tx_q->tx_skbuff[entry]);
desc = stmmac_get_tx_desc(priv, tx_q, entry);
@@ -4935,7 +5147,7 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
* This approach takes care about the fragments: desc is the first
* element in case of no SG.
*/
- tx_packets = CIRC_CNT(entry + 1, first_tx, priv->dma_conf.dma_tx_size);
+ tx_packets = CIRC_CNT(entry + 1, first_tx, priv->dma_conf->dma_tx_size);
tx_q->tx_count_frames += tx_packets;
if ((skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) && priv->hwts_tx_en)
@@ -4963,7 +5175,7 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
* ndo_start_xmit will fill this descriptor the next time it's
* called and stmmac_tx_clean may clean up to this descriptor.
*/
- entry = STMMAC_NEXT_ENTRY(entry, priv->dma_conf.dma_tx_size);
+ entry = STMMAC_NEXT_ENTRY(entry, priv->dma_conf->dma_tx_size);
tx_q->cur_tx = entry;
if (netif_msg_pktdata(priv)) {
@@ -5078,7 +5290,7 @@ static void stmmac_rx_vlan(struct net_device *dev, struct sk_buff *skb)
*/
static inline void stmmac_rx_refill(struct stmmac_priv *priv, u32 queue)
{
- struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue];
+ struct stmmac_rx_queue *rx_q = &priv->dma_conf->rx_queue[queue];
int dirty = stmmac_rx_dirty(priv, queue);
unsigned int entry = rx_q->dirty_rx;
gfp_t gfp = (GFP_ATOMIC | __GFP_NOWARN);
@@ -5129,7 +5341,7 @@ static inline void stmmac_rx_refill(struct stmmac_priv *priv, u32 queue)
dma_wmb();
stmmac_set_rx_owner(priv, p, use_rx_wd);
- entry = STMMAC_NEXT_ENTRY(entry, priv->dma_conf.dma_rx_size);
+ entry = STMMAC_NEXT_ENTRY(entry, priv->dma_conf->dma_rx_size);
}
rx_q->dirty_rx = entry;
stmmac_set_queue_rx_tail_ptr(priv, rx_q, queue, rx_q->dirty_rx);
@@ -5157,12 +5369,12 @@ static unsigned int stmmac_rx_buf1_len(struct stmmac_priv *priv,
/* First descriptor, not last descriptor and not split header */
if (status & rx_not_ls)
- return priv->dma_conf.dma_buf_sz;
+ return priv->dma_conf->dma_buf_sz;
plen = stmmac_get_rx_frame_len(priv, p, coe);
/* First descriptor and last descriptor and not split header */
- return min_t(unsigned int, priv->dma_conf.dma_buf_sz, plen);
+ return min_t(unsigned int, priv->dma_conf->dma_buf_sz, plen);
}
static unsigned int stmmac_rx_buf2_len(struct stmmac_priv *priv,
@@ -5192,7 +5404,7 @@ static unsigned int stmmac_rx_buf2_len(struct stmmac_priv *priv,
/* Not GMAC4 and not last descriptor */
if (priv->plat->core_type != DWMAC_CORE_GMAC4 && (status & rx_not_ls))
- return priv->dma_conf.dma_buf_sz;
+ return priv->dma_conf->dma_buf_sz;
/* GMAC4 or last descriptor */
plen = stmmac_get_rx_frame_len(priv, p, coe);
@@ -5204,7 +5416,7 @@ static int stmmac_xdp_xmit_xdpf(struct stmmac_priv *priv, int queue,
struct xdp_frame *xdpf, bool dma_map)
{
struct stmmac_txq_stats *txq_stats = &priv->xstats.txq_stats[queue];
- struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue];
+ struct stmmac_tx_queue *tx_q = &priv->dma_conf->tx_queue[queue];
bool csum = !priv->plat->tx_queues_cfg[queue].coe_unsupported;
unsigned int entry = tx_q->cur_tx;
enum stmmac_txbuf_type buf_type;
@@ -5270,7 +5482,7 @@ static int stmmac_xdp_xmit_xdpf(struct stmmac_priv *priv, int queue,
stmmac_enable_dma_transmission(priv, priv->ioaddr, queue);
- entry = STMMAC_NEXT_ENTRY(entry, priv->dma_conf.dma_tx_size);
+ entry = STMMAC_NEXT_ENTRY(entry, priv->dma_conf->dma_tx_size);
tx_q->cur_tx = entry;
return STMMAC_XDP_TX;
@@ -5461,7 +5673,7 @@ static void stmmac_dispatch_skb_zc(struct stmmac_priv *priv, u32 queue,
static bool stmmac_rx_refill_zc(struct stmmac_priv *priv, u32 queue, u32 budget)
{
- struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue];
+ struct stmmac_rx_queue *rx_q = &priv->dma_conf->rx_queue[queue];
unsigned int entry = rx_q->dirty_rx;
struct dma_desc *rx_desc = NULL;
bool ret = true;
@@ -5501,7 +5713,7 @@ static bool stmmac_rx_refill_zc(struct stmmac_priv *priv, u32 queue, u32 budget)
dma_wmb();
stmmac_set_rx_owner(priv, rx_desc, use_rx_wd);
- entry = STMMAC_NEXT_ENTRY(entry, priv->dma_conf.dma_rx_size);
+ entry = STMMAC_NEXT_ENTRY(entry, priv->dma_conf->dma_rx_size);
}
if (rx_desc) {
@@ -5525,7 +5737,7 @@ static struct stmmac_xdp_buff *xsk_buff_to_stmmac_ctx(struct xdp_buff *xdp)
static int stmmac_rx_zc(struct stmmac_priv *priv, int limit, u32 queue)
{
struct stmmac_rxq_stats *rxq_stats = &priv->xstats.rxq_stats[queue];
- struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue];
+ struct stmmac_rx_queue *rx_q = &priv->dma_conf->rx_queue[queue];
unsigned int count = 0, error = 0, len = 0;
int dirty = stmmac_rx_dirty(priv, queue);
unsigned int next_entry = rx_q->cur_rx;
@@ -5542,7 +5754,7 @@ static int stmmac_rx_zc(struct stmmac_priv *priv, int limit, u32 queue)
netdev_dbg(priv->dev, "%s: descriptor ring:\n", __func__);
desc_size = stmmac_get_rx_desc_size(priv);
- stmmac_display_ring(priv, rx_head, priv->dma_conf.dma_rx_size, true,
+ stmmac_display_ring(priv, rx_head, priv->dma_conf->dma_rx_size, true,
rx_q->dma_rx_phy, desc_size);
}
while (count < limit) {
@@ -5586,7 +5798,7 @@ static int stmmac_rx_zc(struct stmmac_priv *priv, int limit, u32 queue)
/* Prefetch the next RX descriptor */
next_entry = STMMAC_NEXT_ENTRY(rx_q->cur_rx,
- priv->dma_conf.dma_rx_size);
+ priv->dma_conf->dma_rx_size);
if (unlikely(next_entry == rx_q->dirty_rx))
break;
@@ -5711,7 +5923,7 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue)
{
u32 rx_errors = 0, rx_dropped = 0, rx_bytes = 0, rx_packets = 0;
struct stmmac_rxq_stats *rxq_stats = &priv->xstats.rxq_stats[queue];
- struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue];
+ struct stmmac_rx_queue *rx_q = &priv->dma_conf->rx_queue[queue];
struct stmmac_channel *ch = &priv->channel[queue];
unsigned int count = 0, error = 0, len = 0;
int status = 0, coe = priv->hw->rx_csum;
@@ -5724,7 +5936,7 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue)
int bufsz;
dma_dir = page_pool_get_dma_dir(rx_q->page_pool);
- bufsz = DIV_ROUND_UP(priv->dma_conf.dma_buf_sz, PAGE_SIZE) * PAGE_SIZE;
+ bufsz = DIV_ROUND_UP(priv->dma_conf->dma_buf_sz, PAGE_SIZE) * PAGE_SIZE;
if (netif_msg_rx_status(priv)) {
void *rx_head = stmmac_get_rx_desc(priv, rx_q, 0);
@@ -5732,7 +5944,7 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue)
netdev_dbg(priv->dev, "%s: descriptor ring:\n", __func__);
desc_size = stmmac_get_rx_desc_size(priv);
- stmmac_display_ring(priv, rx_head, priv->dma_conf.dma_rx_size, true,
+ stmmac_display_ring(priv, rx_head, priv->dma_conf->dma_rx_size, true,
rx_q->dma_rx_phy, desc_size);
}
while (count < limit) {
@@ -5772,7 +5984,7 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue)
break;
next_entry = STMMAC_NEXT_ENTRY(rx_q->cur_rx,
- priv->dma_conf.dma_rx_size);
+ priv->dma_conf->dma_rx_size);
if (unlikely(next_entry == rx_q->dirty_rx))
break;
@@ -5906,7 +6118,7 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue)
buf1_len, dma_dir);
skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags,
buf->page, buf->page_offset, buf1_len,
- priv->dma_conf.dma_buf_sz);
+ priv->dma_conf->dma_buf_sz);
buf->page = NULL;
}
@@ -5915,7 +6127,7 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue)
buf2_len, dma_dir);
skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags,
buf->sec_page, 0, buf2_len,
- priv->dma_conf.dma_buf_sz);
+ priv->dma_conf->dma_buf_sz);
buf->sec_page = NULL;
}
@@ -6126,6 +6338,113 @@ static void stmmac_set_rx_mode(struct net_device *dev)
stmmac_set_filter(priv, priv->hw, dev);
}
+static int stmmac_reconfigure_mtu(struct net_device *dev, int mtu)
+{
+ struct stmmac_priv *priv = netdev_priv(dev);
+ struct stmmac_dma_conf *old_conf = priv->dma_conf;
+ struct stmmac_dma_conf *new_conf;
+ int old_mtu = dev->mtu;
+ int ret, restore_ret;
+ u32 chan;
+
+ new_conf = stmmac_setup_dma_desc(priv, mtu);
+ if (IS_ERR(new_conf))
+ return PTR_ERR(new_conf);
+
+ netif_device_detach(dev);
+ phylink_stop(priv->phylink);
+ stmmac_quiesce(priv);
+ timer_delete_sync(&priv->eee_ctrl_timer);
+ if (stmmac_fpe_supported(priv))
+ ethtool_mmsv_stop(&priv->fpe_cfg.mmsv);
+
+ /* An IRQ can recover a TX error and restart a queue. Drain handlers
+ * before the final TX stop, and keep the registrations for rollback.
+ */
+ stmmac_set_irq_state(priv, false);
+ netif_tx_disable(dev);
+ synchronize_net();
+
+ ret = stmmac_prepare_rx_buffers(priv);
+ if (ret)
+ goto restart;
+
+ stmmac_stop_all_dma(priv);
+ phylink_prepare_resume(priv->phylink);
+
+ /* MAC receive limits must be programmed for the prospective MTU. */
+ WRITE_ONCE(dev->mtu, mtu);
+ priv->dma_conf = new_conf;
+ stmmac_reset_queues_param(priv);
+ ret = stmmac_hw_setup(dev, false);
+ if (ret) {
+ stmmac_stop_all_dma(priv);
+ stmmac_mac_set(priv, priv->ioaddr, false);
+ priv->dma_conf = old_conf;
+ WRITE_ONCE(dev->mtu, old_mtu);
+
+ /* Reuse the retained rings. Reinitialize them only after reset
+ * has completed, not merely after clearing the DMA enable bits.
+ */
+ restore_ret = stmmac_hw_setup(dev, true);
+ if (restore_ret) {
+ stmmac_stop_all_dma(priv);
+ stmmac_mac_set(priv, priv->ioaddr, false);
+ stmmac_set_irq_state(priv, true);
+ stmmac_free_irq(dev, REQ_IRQ_ERR_ALL, 0);
+ netif_tx_disable(dev);
+ stmmac_stop_all_dma(priv);
+ priv->datapath = STMMAC_DATAPATH_HALTED;
+ netdev_err(dev, "MTU rollback failed: %pe; interface remains detached\n",
+ ERR_PTR(restore_ret));
+ goto free_new;
+ }
+ } else {
+ /* Hardware setup completed its reset before using the new rings.
+ * The old DMA allocations can now be released safely.
+ */
+ free_dma_desc_resources(priv, old_conf);
+ kfree(old_conf);
+ for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++)
+ hrtimer_setup(&new_conf->tx_queue[chan].txtimer,
+ stmmac_tx_timer, CLOCK_MONOTONIC,
+ HRTIMER_MODE_REL);
+ }
+
+ /* Restore timestamping without registering a new PHC or resetting the
+ * user's packet timestamp filters. Timestamping can also be used without
+ * a registered PHC (CONFIG_PTP_1588_CLOCK=n).
+ */
+ if ((priv->dma_cap.time_stamp || priv->dma_cap.atime_stamp) &&
+ priv->plat->clk_ptp_rate) {
+ unsigned long flags;
+
+ write_lock_irqsave(&priv->ptp_lock, flags);
+ stmmac_init_tstamp_counter(priv, priv->systime_flags);
+ if (priv->plat->flags & STMMAC_FLAG_HWTSTAMP_CORRECT_LATENCY)
+ stmmac_hwtstamp_correct_latency(priv, priv);
+ write_unlock_irqrestore(&priv->ptp_lock, flags);
+ }
+ stmmac_set_rx_mode(dev);
+ stmmac_vlan_restore(priv);
+
+restart:
+ stmmac_enable_all_queues(priv);
+ stmmac_enable_all_dma_irq(priv);
+ stmmac_set_irq_state(priv, true);
+ phylink_start(priv->phylink);
+ netif_device_attach(dev);
+ for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++)
+ stmmac_tx_timer_arm(priv, chan);
+ if (!ret)
+ return 0;
+
+free_new:
+ free_dma_desc_resources(priv, new_conf);
+ kfree(new_conf);
+ return ret;
+}
+
/**
* stmmac_change_mtu - entry point to change MTU size for the device.
* @dev : device pointer.
@@ -6141,7 +6460,6 @@ static int stmmac_change_mtu(struct net_device *dev, int new_mtu)
{
struct stmmac_priv *priv = netdev_priv(dev);
int txfifosz = priv->plat->tx_fifo_size;
- struct stmmac_dma_conf *dma_conf;
const int mtu = new_mtu;
int ret;
@@ -6161,29 +6479,15 @@ static int stmmac_change_mtu(struct net_device *dev, int new_mtu)
if ((txfifosz < new_mtu) || (new_mtu > BUF_SIZE_16KiB))
return -EINVAL;
- if (netif_running(dev)) {
- netdev_dbg(priv->dev, "restarting interface to change its MTU\n");
- /* Try to allocate the new DMA conf with the new mtu */
- dma_conf = stmmac_setup_dma_desc(priv, mtu);
- if (IS_ERR(dma_conf)) {
- netdev_err(priv->dev, "failed allocating new dma conf for new MTU %d\n",
- mtu);
- return PTR_ERR(dma_conf);
- }
-
- __stmmac_release(dev);
-
- ret = __stmmac_open(dev, dma_conf);
- if (ret) {
- free_dma_desc_resources(priv, dma_conf);
- kfree(dma_conf);
- netdev_err(priv->dev, "failed reopening the interface after MTU change\n");
+ /* Normal-size frames use the same buffers and MAC receive limits.
+ * In particular, do not disturb a live AF_XDP pool: XDP does not
+ * support jumbo frames, so it never needs the ring replacement below.
+ */
+ if (netif_running(dev) &&
+ (dev->mtu > ETH_DATA_LEN || mtu > ETH_DATA_LEN)) {
+ ret = stmmac_reconfigure_mtu(dev, mtu);
+ if (ret)
return ret;
- }
-
- kfree(dma_conf);
-
- stmmac_set_rx_mode(dev);
}
WRITE_ONCE(dev->mtu, mtu);
@@ -6354,15 +6658,11 @@ static irqreturn_t stmmac_safety_interrupt(int irq, void *dev_id)
static irqreturn_t stmmac_msi_intr_tx(int irq, void *data)
{
- struct stmmac_tx_queue *tx_q = (struct stmmac_tx_queue *)data;
- struct stmmac_dma_conf *dma_conf;
- int chan = tx_q->queue_index;
- struct stmmac_priv *priv;
+ struct stmmac_channel *ch = data;
+ struct stmmac_priv *priv = ch->priv_data;
+ int chan = ch->index;
int status;
- dma_conf = container_of(tx_q, struct stmmac_dma_conf, tx_queue[chan]);
- priv = container_of(dma_conf, struct stmmac_priv, dma_conf);
-
/* Check if adapter is up */
if (test_bit(STMMAC_DOWN, &priv->state))
return IRQ_HANDLED;
@@ -6381,13 +6681,9 @@ static irqreturn_t stmmac_msi_intr_tx(int irq, void *data)
static irqreturn_t stmmac_msi_intr_rx(int irq, void *data)
{
- struct stmmac_rx_queue *rx_q = (struct stmmac_rx_queue *)data;
- struct stmmac_dma_conf *dma_conf;
- int chan = rx_q->queue_index;
- struct stmmac_priv *priv;
-
- dma_conf = container_of(rx_q, struct stmmac_dma_conf, rx_queue[chan]);
- priv = container_of(dma_conf, struct stmmac_priv, dma_conf);
+ struct stmmac_channel *ch = data;
+ struct stmmac_priv *priv = ch->priv_data;
+ int chan = ch->index;
/* Check if adapter is up */
if (test_bit(STMMAC_DOWN, &priv->state))
@@ -6425,6 +6721,8 @@ static int stmmac_setup_tc_block_cb(enum tc_setup_type type, void *type_data,
if (!tc_cls_can_offload_and_chain0(priv->dev, type_data))
return ret;
+ if (!netif_device_present(priv->dev))
+ return -ENETDOWN;
__stmmac_disable_all_queues(priv);
@@ -6542,45 +6840,49 @@ static int stmmac_rings_status_show(struct seq_file *seq, void *v)
{
struct net_device *dev = seq->private;
struct stmmac_priv *priv = netdev_priv(dev);
- u8 rx_count = priv->plat->rx_queues_to_use;
- u8 tx_count = priv->plat->tx_queues_to_use;
- u8 queue;
+ u8 rx_count, tx_count, queue;
- if ((dev->flags & IFF_UP) == 0)
- return 0;
+ rtnl_lock();
+ if (priv->datapath == STMMAC_DATAPATH_DOWN)
+ goto out_unlock;
+
+ rx_count = priv->plat->rx_queues_to_use;
+ tx_count = priv->plat->tx_queues_to_use;
for (queue = 0; queue < rx_count; queue++) {
- struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue];
+ struct stmmac_rx_queue *rx_q = &priv->dma_conf->rx_queue[queue];
seq_printf(seq, "RX Queue %d:\n", queue);
if (priv->extend_desc) {
seq_printf(seq, "Extended descriptor ring:\n");
sysfs_display_ring((void *)rx_q->dma_erx,
- priv->dma_conf.dma_rx_size, 1, seq, rx_q->dma_rx_phy);
+ priv->dma_conf->dma_rx_size, 1, seq, rx_q->dma_rx_phy);
} else {
seq_printf(seq, "Descriptor ring:\n");
sysfs_display_ring((void *)rx_q->dma_rx,
- priv->dma_conf.dma_rx_size, 0, seq, rx_q->dma_rx_phy);
+ priv->dma_conf->dma_rx_size, 0, seq, rx_q->dma_rx_phy);
}
}
for (queue = 0; queue < tx_count; queue++) {
- struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue];
+ struct stmmac_tx_queue *tx_q = &priv->dma_conf->tx_queue[queue];
seq_printf(seq, "TX Queue %d:\n", queue);
if (priv->extend_desc) {
seq_printf(seq, "Extended descriptor ring:\n");
sysfs_display_ring((void *)tx_q->dma_etx,
- priv->dma_conf.dma_tx_size, 1, seq, tx_q->dma_tx_phy);
+ priv->dma_conf->dma_tx_size, 1, seq, tx_q->dma_tx_phy);
} else if (!(tx_q->tbs & STMMAC_TBS_AVAIL)) {
seq_printf(seq, "Descriptor ring:\n");
sysfs_display_ring((void *)tx_q->dma_tx,
- priv->dma_conf.dma_tx_size, 0, seq, tx_q->dma_tx_phy);
+ priv->dma_conf->dma_tx_size, 0, seq, tx_q->dma_tx_phy);
}
}
+out_unlock:
+ rtnl_unlock();
return 0;
}
DEFINE_SHOW_ATTRIBUTE(stmmac_rings_status);
@@ -6962,6 +7264,19 @@ static int stmmac_bpf(struct net_device *dev, struct netdev_bpf *bpf)
{
struct stmmac_priv *priv = netdev_priv(dev);
+ if (bpf->command != XDP_SETUP_PROG &&
+ bpf->command != XDP_SETUP_XSK_POOL)
+ return -EOPNOTSUPP;
+
+ /*
+ * Pool removal must succeed even after a failed resume. Release the
+ * suspended rings before their pool or XDP buffer layout can change.
+ * Leave the interface detached until it is closed and reopened.
+ */
+ if (priv->datapath == STMMAC_DATAPATH_SUSPENDED ||
+ priv->datapath == STMMAC_DATAPATH_HALTED)
+ __stmmac_release(dev);
+
switch (bpf->command) {
case XDP_SETUP_PROG:
return stmmac_xdp_set_prog(priv, bpf->prog, bpf->extack);
@@ -6992,6 +7307,10 @@ static int stmmac_xdp_xmit(struct net_device *dev, int num_frames,
nq = netdev_get_tx_queue(priv->dev, queue);
__netif_tx_lock(nq, cpu);
+ if (unlikely(!netif_device_present(dev) || netif_tx_queue_stopped(nq))) {
+ __netif_tx_unlock(nq);
+ return -ENETDOWN;
+ }
/* Avoids TX time-out as we are sharing with slow path */
txq_trans_cond_update(nq);
@@ -7015,129 +7334,24 @@ static int stmmac_xdp_xmit(struct net_device *dev, int num_frames,
return nxmit;
}
-void stmmac_disable_rx_queue(struct stmmac_priv *priv, u32 queue)
-{
- struct stmmac_channel *ch = &priv->channel[queue];
- unsigned long flags;
-
- spin_lock_irqsave(&ch->lock, flags);
- stmmac_disable_dma_irq(priv, priv->ioaddr, queue, 1, 0);
- spin_unlock_irqrestore(&ch->lock, flags);
-
- stmmac_stop_rx_dma(priv, queue);
- __free_dma_rx_desc_resources(priv, &priv->dma_conf, queue);
-}
-
-void stmmac_enable_rx_queue(struct stmmac_priv *priv, u32 queue)
-{
- struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue];
- struct stmmac_channel *ch = &priv->channel[queue];
- unsigned long flags;
- int ret;
-
- ret = __alloc_dma_rx_desc_resources(priv, &priv->dma_conf, queue);
- if (ret) {
- netdev_err(priv->dev, "Failed to alloc RX desc.\n");
- return;
- }
-
- ret = __init_dma_rx_desc_rings(priv, &priv->dma_conf, queue, GFP_KERNEL);
- if (ret) {
- __free_dma_rx_desc_resources(priv, &priv->dma_conf, queue);
- netdev_err(priv->dev, "Failed to init RX desc.\n");
- return;
- }
-
- stmmac_reset_rx_queue(priv, queue);
- stmmac_clear_rx_descriptors(priv, &priv->dma_conf, queue);
-
- stmmac_init_rx_chan(priv, priv->ioaddr, priv->plat->dma_cfg,
- rx_q->dma_rx_phy, queue);
-
- stmmac_set_queue_rx_tail_ptr(priv, rx_q, queue, rx_q->buf_alloc_num);
-
- stmmac_set_queue_rx_buf_size(priv, rx_q, queue);
-
- stmmac_start_rx_dma(priv, queue);
-
- spin_lock_irqsave(&ch->lock, flags);
- stmmac_enable_dma_irq(priv, priv->ioaddr, queue, 1, 0);
- spin_unlock_irqrestore(&ch->lock, flags);
-}
-
-void stmmac_disable_tx_queue(struct stmmac_priv *priv, u32 queue)
-{
- struct stmmac_channel *ch = &priv->channel[queue];
- unsigned long flags;
-
- spin_lock_irqsave(&ch->lock, flags);
- stmmac_disable_dma_irq(priv, priv->ioaddr, queue, 0, 1);
- spin_unlock_irqrestore(&ch->lock, flags);
-
- stmmac_stop_tx_dma(priv, queue);
- __free_dma_tx_desc_resources(priv, &priv->dma_conf, queue);
-}
-
-void stmmac_enable_tx_queue(struct stmmac_priv *priv, u32 queue)
-{
- struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue];
- struct stmmac_channel *ch = &priv->channel[queue];
- unsigned long flags;
- int ret;
-
- ret = __alloc_dma_tx_desc_resources(priv, &priv->dma_conf, queue);
- if (ret) {
- netdev_err(priv->dev, "Failed to alloc TX desc.\n");
- return;
- }
-
- ret = __init_dma_tx_desc_rings(priv, &priv->dma_conf, queue);
- if (ret) {
- __free_dma_tx_desc_resources(priv, &priv->dma_conf, queue);
- netdev_err(priv->dev, "Failed to init TX desc.\n");
- return;
- }
-
- stmmac_reset_tx_queue(priv, queue);
- stmmac_clear_tx_descriptors(priv, &priv->dma_conf, queue);
-
- stmmac_init_tx_chan(priv, priv->ioaddr, priv->plat->dma_cfg,
- tx_q->dma_tx_phy, queue);
-
- if (tx_q->tbs & STMMAC_TBS_AVAIL)
- stmmac_enable_tbs(priv, priv->ioaddr, 1, queue);
-
- stmmac_set_queue_tx_tail_ptr(priv, tx_q, queue, 0);
-
- stmmac_start_tx_dma(priv, queue);
-
- spin_lock_irqsave(&ch->lock, flags);
- stmmac_enable_dma_irq(priv, priv->ioaddr, queue, 0, 1);
- spin_unlock_irqrestore(&ch->lock, flags);
-}
-
void stmmac_xdp_release(struct net_device *dev)
{
struct stmmac_priv *priv = netdev_priv(dev);
- u8 chan;
- /* Ensure tx function is not running */
- netif_tx_disable(dev);
-
- /* Disable NAPI process */
- stmmac_disable_all_queues(priv);
-
- for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++)
- hrtimer_cancel(&priv->dma_conf.tx_queue[chan].txtimer);
+ netif_device_detach(dev);
+ phylink_stop(priv->phylink);
+ stmmac_quiesce(priv);
+ priv->datapath = STMMAC_DATAPATH_DOWN;
/* Free the IRQ lines */
stmmac_free_irq(dev, REQ_IRQ_ERR_ALL, 0);
+ stmmac_stop_tx_queues(priv);
/* Stop TX/RX DMA channels */
stmmac_stop_all_dma(priv);
/* Release and free the Rx/Tx resources */
- free_dma_desc_resources(priv, &priv->dma_conf);
+ free_dma_desc_resources(priv, priv->dma_conf);
/* Disable the MAC Rx/Tx */
stmmac_mac_set(priv, priv->ioaddr, false);
@@ -7146,7 +7360,13 @@ void stmmac_xdp_release(struct net_device *dev)
* watchdogs during reset
*/
netif_trans_update(dev);
- netif_carrier_off(dev);
+
+ if (stmmac_fpe_supported(priv))
+ ethtool_mmsv_stop(&priv->fpe_cfg.mmsv);
+
+ /* Keep PTP across the immediately following stmmac_xdp_open(). That
+ * function releases it if reopening fails, before returning DOWN.
+ */
}
int stmmac_xdp_open(struct net_device *dev)
@@ -7161,14 +7381,14 @@ int stmmac_xdp_open(struct net_device *dev)
u8 chan;
int ret;
- ret = alloc_dma_desc_resources(priv, &priv->dma_conf);
+ ret = alloc_dma_desc_resources(priv, priv->dma_conf);
if (ret < 0) {
netdev_err(dev, "%s: DMA descriptors allocation failed\n",
__func__);
goto dma_desc_error;
}
- ret = init_dma_desc_rings(dev, &priv->dma_conf, GFP_KERNEL);
+ ret = init_dma_desc_rings(dev, priv->dma_conf, GFP_KERNEL);
if (ret < 0) {
netdev_err(dev, "%s: DMA descriptors initialization failed\n",
__func__);
@@ -7188,7 +7408,7 @@ int stmmac_xdp_open(struct net_device *dev)
/* DMA RX Channel Configuration */
for (chan = 0; chan < rx_cnt; chan++) {
- rx_q = &priv->dma_conf.rx_queue[chan];
+ rx_q = &priv->dma_conf->rx_queue[chan];
stmmac_init_rx_chan(priv, priv->ioaddr, priv->plat->dma_cfg,
rx_q->dma_rx_phy, chan);
@@ -7203,13 +7423,16 @@ int stmmac_xdp_open(struct net_device *dev)
/* DMA TX Channel Configuration */
for (chan = 0; chan < tx_cnt; chan++) {
- tx_q = &priv->dma_conf.tx_queue[chan];
+ tx_q = &priv->dma_conf->tx_queue[chan];
stmmac_init_tx_chan(priv, priv->ioaddr, priv->plat->dma_cfg,
tx_q->dma_tx_phy, chan);
stmmac_set_queue_tx_tail_ptr(priv, tx_q, chan, 0);
+ if (tx_q->tbs & STMMAC_TBS_AVAIL)
+ stmmac_enable_tbs(priv, priv->ioaddr, 1, chan);
+
hrtimer_setup(&tx_q->txtimer, stmmac_tx_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
}
@@ -7225,27 +7448,28 @@ int stmmac_xdp_open(struct net_device *dev)
/* Enable NAPI process*/
stmmac_enable_all_queues(priv);
- netif_carrier_on(dev);
- netif_tx_start_all_queues(dev);
stmmac_enable_all_dma_irq(priv);
+ priv->datapath = STMMAC_DATAPATH_RUNNING;
+ phylink_start(priv->phylink);
+ netif_device_attach(dev);
return 0;
irq_error:
- for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++)
- hrtimer_cancel(&priv->dma_conf.tx_queue[chan].txtimer);
+ stmmac_stop_tx_queues(priv);
+ stmmac_stop_all_dma(priv);
+ stmmac_mac_set(priv, priv->ioaddr, false);
init_error:
- free_dma_desc_resources(priv, &priv->dma_conf);
+ free_dma_desc_resources(priv, priv->dma_conf);
dma_desc_error:
+ stmmac_release_ptp(priv);
return ret;
}
int stmmac_xsk_wakeup(struct net_device *dev, u32 queue, u32 flags)
{
struct stmmac_priv *priv = netdev_priv(dev);
- struct stmmac_rx_queue *rx_q;
- struct stmmac_tx_queue *tx_q;
struct stmmac_channel *ch;
if (test_bit(STMMAC_DOWN, &priv->state) ||
@@ -7259,11 +7483,9 @@ int stmmac_xsk_wakeup(struct net_device *dev, u32 queue, u32 flags)
queue >= priv->plat->tx_queues_to_use)
return -EINVAL;
- rx_q = &priv->dma_conf.rx_queue[queue];
- tx_q = &priv->dma_conf.tx_queue[queue];
ch = &priv->channel[queue];
- if (!rx_q->xsk_pool && !tx_q->xsk_pool)
+ if (!test_bit(queue, priv->af_xdp_zc_qps))
return -EINVAL;
if (!napi_if_scheduled_mark_missed(&ch->rxtx_napi)) {
@@ -7364,6 +7586,9 @@ static void stmmac_reset_subtask(struct stmmac_priv *priv)
netdev_err(priv->dev, "Reset adapter.\n");
rtnl_lock();
+ if (!netif_device_present(priv->dev))
+ goto out_unlock;
+
netif_trans_update(priv->dev);
while (test_and_set_bit(STMMAC_RESETING, &priv->state))
usleep_range(1000, 2000);
@@ -7373,6 +7598,7 @@ static void stmmac_reset_subtask(struct stmmac_priv *priv)
dev_open(priv->dev, NULL);
clear_bit(STMMAC_DOWN, &priv->state);
clear_bit(STMMAC_RESETING, &priv->state);
+out_unlock:
rtnl_unlock();
}
@@ -7617,13 +7843,37 @@ static void stmmac_napi_del(struct net_device *dev)
}
}
-int stmmac_reinit_queues(struct net_device *dev, u8 rx_cnt, u8 tx_cnt)
+/* Rebuild only the datapath. The administratively-up device still owns its
+ * PHY attachment and runtime-PM reference, even if this reopen fails.
+ */
+static int stmmac_reopen(struct net_device *dev)
{
struct stmmac_priv *priv = netdev_priv(dev);
- int ret = 0, i;
+ struct stmmac_dma_conf *old_conf = priv->dma_conf;
+ struct stmmac_dma_conf *dma_conf;
+ int ret;
- if (netif_running(dev))
- stmmac_release(dev);
+ dma_conf = stmmac_setup_dma_desc(priv, dev->mtu);
+ if (IS_ERR(dma_conf))
+ return PTR_ERR(dma_conf);
+
+ ret = __stmmac_open(dev, dma_conf);
+ if (ret) {
+ priv->dma_conf = old_conf;
+ free_dma_desc_resources(priv, dma_conf);
+ kfree(dma_conf);
+ return ret;
+ }
+
+ kfree(old_conf);
+ netif_device_attach(dev);
+ return 0;
+}
+
+static void stmmac_set_queues(struct net_device *dev, u8 rx_cnt, u8 tx_cnt)
+{
+ struct stmmac_priv *priv = netdev_priv(dev);
+ int i;
stmmac_napi_del(dev);
@@ -7635,9 +7885,31 @@ int stmmac_reinit_queues(struct net_device *dev, u8 rx_cnt, u8 tx_cnt)
rx_cnt);
stmmac_napi_add(dev);
+}
+
+int stmmac_reinit_queues(struct net_device *dev, u8 rx_cnt, u8 tx_cnt)
+{
+ struct stmmac_priv *priv = netdev_priv(dev);
+ u8 old_rx = priv->plat->rx_queues_to_use;
+ u8 old_tx = priv->plat->tx_queues_to_use;
+ int ret = 0;
+
+ if (netif_running(dev)) {
+ if (!netif_device_present(dev))
+ return -ENETDOWN;
+ netif_device_detach(dev);
+ __stmmac_release(dev);
+ }
+
+ stmmac_set_queues(dev, rx_cnt, tx_cnt);
if (netif_running(dev))
- ret = stmmac_open(dev);
+ ret = stmmac_reopen(dev);
+ if (ret) {
+ stmmac_set_queues(dev, old_rx, old_tx);
+ netdev_err(dev, "failed reopening after channel change: %pe; interface remains detached\n",
+ ERR_PTR(ret));
+ }
return ret;
}
@@ -7645,16 +7917,28 @@ int stmmac_reinit_queues(struct net_device *dev, u8 rx_cnt, u8 tx_cnt)
int stmmac_reinit_ringparam(struct net_device *dev, u32 rx_size, u32 tx_size)
{
struct stmmac_priv *priv = netdev_priv(dev);
+ u32 old_rx = priv->dma_conf->dma_rx_size;
+ u32 old_tx = priv->dma_conf->dma_tx_size;
int ret = 0;
- if (netif_running(dev))
- stmmac_release(dev);
+ if (netif_running(dev)) {
+ if (!netif_device_present(dev))
+ return -ENETDOWN;
+ netif_device_detach(dev);
+ __stmmac_release(dev);
+ }
- priv->dma_conf.dma_rx_size = rx_size;
- priv->dma_conf.dma_tx_size = tx_size;
+ priv->dma_conf->dma_rx_size = rx_size;
+ priv->dma_conf->dma_tx_size = tx_size;
if (netif_running(dev))
- ret = stmmac_open(dev);
+ ret = stmmac_reopen(dev);
+ if (ret) {
+ priv->dma_conf->dma_rx_size = old_rx;
+ priv->dma_conf->dma_tx_size = old_tx;
+ netdev_err(dev, "failed reopening after ring change: %pe; interface remains detached\n",
+ ERR_PTR(ret));
+ }
return ret;
}
@@ -7816,6 +8100,13 @@ struct plat_stmmacenet_data *stmmac_plat_dat_alloc(struct device *dev)
}
EXPORT_SYMBOL_GPL(stmmac_plat_dat_alloc);
+static void stmmac_free_dma_conf(void *data)
+{
+ struct stmmac_priv *priv = data;
+
+ kfree(priv->dma_conf);
+}
+
static int __stmmac_dvr_probe(struct device *device,
struct plat_stmmacenet_data *plat_dat,
struct stmmac_resources *res)
@@ -7840,6 +8131,13 @@ static int __stmmac_dvr_probe(struct device *device,
priv = netdev_priv(ndev);
priv->device = device;
priv->dev = ndev;
+ /* Keep ring sizes and per-queue settings even while the device is down. */
+ priv->dma_conf = kzalloc_obj(*priv->dma_conf);
+ if (!priv->dma_conf)
+ return -ENOMEM;
+ ret = devm_add_action_or_reset(device, stmmac_free_dma_conf, priv);
+ if (ret)
+ return ret;
for (i = 0; i < MTL_MAX_RX_QUEUES; i++)
u64_stats_init(&priv->xstats.rxq_stats[i].napi_syncp);
@@ -8193,34 +8491,41 @@ EXPORT_SYMBOL_GPL(stmmac_dvr_remove);
/**
* stmmac_suspend - suspend callback
* @dev: device pointer
- * Description: this is the function to suspend the device and it is called
- * by the platform driver to stop the network queue, release the resources,
- * program the PMT register (for WoL), clean and release driver resources.
+ * Description: stop network activity and program hardware for system sleep,
+ * preserving any datapath resources still owned for resume or close.
*/
int stmmac_suspend(struct device *dev)
{
struct net_device *ndev = dev_get_drvdata(dev);
struct stmmac_priv *priv = netdev_priv(ndev);
- u8 chan;
- if (!ndev || !netif_running(ndev))
+ rtnl_lock();
+ if (!netif_running(ndev) || priv->hw_suspended) {
+ rtnl_unlock();
goto suspend_bsp;
+ }
+
+ /* A failed datapath cannot provide a working MAC wake path. It may
+ * even have released its wake IRQ. Do not silently suspend without WoL.
+ */
+ if (priv->wolopts && priv->datapath != STMMAC_DATAPATH_RUNNING) {
+ netdev_err(ndev, "cannot suspend failed datapath with MAC WoL enabled\n");
+ rtnl_unlock();
+ return -EBUSY;
+ }
mutex_lock(&priv->lock);
netif_device_detach(ndev);
- stmmac_disable_all_queues(priv);
-
- for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++)
- hrtimer_cancel(&priv->dma_conf.tx_queue[chan].txtimer);
+ if (priv->datapath == STMMAC_DATAPATH_RUNNING)
+ stmmac_quiesce(priv);
if (priv->eee_sw_timer_en) {
priv->tx_path_in_lpi_mode = false;
timer_delete_sync(&priv->eee_ctrl_timer);
}
- /* Stop TX/RX DMA */
stmmac_stop_all_dma(priv);
stmmac_legacy_serdes_power_down(priv);
@@ -8236,12 +8541,14 @@ int stmmac_suspend(struct device *dev)
mutex_unlock(&priv->lock);
- rtnl_lock();
phylink_suspend(priv->phylink, !!priv->wolopts);
- rtnl_unlock();
+ if (priv->datapath == STMMAC_DATAPATH_RUNNING)
+ priv->datapath = STMMAC_DATAPATH_SUSPENDED;
+ priv->hw_suspended = true;
if (stmmac_fpe_supported(priv))
ethtool_mmsv_stop(&priv->fpe_cfg.mmsv);
+ rtnl_unlock();
suspend_bsp:
if (priv->plat->suspend)
@@ -8253,7 +8560,7 @@ EXPORT_SYMBOL_GPL(stmmac_suspend);
static void stmmac_reset_rx_queue(struct stmmac_priv *priv, u32 queue)
{
- struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue];
+ struct stmmac_rx_queue *rx_q = &priv->dma_conf->rx_queue[queue];
rx_q->cur_rx = 0;
rx_q->dirty_rx = 0;
@@ -8261,7 +8568,7 @@ static void stmmac_reset_rx_queue(struct stmmac_priv *priv, u32 queue)
static void stmmac_reset_tx_queue(struct stmmac_priv *priv, u32 queue)
{
- struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue];
+ struct stmmac_tx_queue *tx_q = &priv->dma_conf->tx_queue[queue];
tx_q->cur_tx = 0;
tx_q->dirty_tx = 0;
@@ -8305,35 +8612,35 @@ int stmmac_resume(struct device *dev)
return ret;
}
- if (!netif_running(ndev))
- return 0;
+ rtnl_lock();
+ if (!netif_running(ndev)) {
+ ret = 0;
+ goto out_unlock;
+ }
- /* Power Down bit, into the PM register, is cleared
- * automatically as soon as a magic packet or a Wake-up frame
- * is received. Anyway, it's better to manually clear
- * this bit because it can generate problems while resuming
- * from another devices (e.g. serial console).
- */
- if (priv->wolopts) {
- mutex_lock(&priv->lock);
- stmmac_pmt(priv, priv->hw, 0);
- mutex_unlock(&priv->lock);
- priv->irq_wake = 0;
- } else {
- pinctrl_pm_select_default_state(priv->device);
- /* reset the phy so that it's ready */
- if (priv->mii)
- stmmac_mdio_reset(priv->mii);
+ if (priv->hw_suspended) {
+ ret = stmmac_resume_hw(priv);
+ if (ret)
+ goto out_unlock;
+
+ /* Terminate PM speed control without restarting a datapath
+ * whose IRQs or rings were released before system sleep.
+ */
+ if (priv->datapath != STMMAC_DATAPATH_SUSPENDED)
+ phylink_stop(priv->phylink);
+ }
+
+ if (priv->datapath != STMMAC_DATAPATH_SUSPENDED) {
+ ret = 0;
+ goto out_unlock;
}
if (!(priv->plat->flags & STMMAC_FLAG_SERDES_UP_AFTER_PHY_LINKUP)) {
ret = stmmac_legacy_serdes_power_up(priv);
if (ret < 0)
- return ret;
+ goto out_unlock;
}
- rtnl_lock();
-
/* Prepare the PHY to resume, ensuring that its clocks which are
* necessary for the MAC DMA reset to complete are running
*/
@@ -8344,15 +8651,12 @@ int stmmac_resume(struct device *dev)
stmmac_reset_queues_param(priv);
stmmac_free_tx_skbufs(priv);
- stmmac_clear_descriptors(priv, &priv->dma_conf);
+ stmmac_clear_descriptors(priv, priv->dma_conf);
- ret = stmmac_hw_setup(ndev);
+ ret = stmmac_hw_setup(ndev, false);
if (ret < 0) {
netdev_err(priv->dev, "%s: Hw setup failed\n", __func__);
- stmmac_legacy_serdes_power_down(priv);
- mutex_unlock(&priv->lock);
- rtnl_unlock();
- return ret;
+ goto error_stop_dma;
}
stmmac_init_timestamping(priv);
@@ -8374,11 +8678,25 @@ int stmmac_resume(struct device *dev)
* workqueue thread, which will race with initialisation.
*/
phylink_resume(priv->phylink);
- rtnl_unlock();
-
+ priv->datapath = STMMAC_DATAPATH_RUNNING;
netif_device_attach(ndev);
+ rtnl_unlock();
return 0;
+
+error_stop_dma:
+ stmmac_stop_all_dma(priv);
+ stmmac_mac_set(priv, priv->ioaddr, false);
+ stmmac_legacy_serdes_power_down(priv);
+ mutex_unlock(&priv->lock);
+ /*
+ * Keep the suspended data path detached. A later resume may retry, or
+ * ndo_stop() can release its resources without disabling NAPI again.
+ */
+out_unlock:
+ rtnl_unlock();
+
+ return ret;
}
EXPORT_SYMBOL_GPL(stmmac_resume);
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
index 6128ed1bd521a..9bd622517389d 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
@@ -958,8 +958,9 @@ static int __maybe_unused stmmac_pltfr_noirq_suspend(struct device *dev)
return 0;
if (!priv->wolopts) {
- /* Disable clock in case of PWM is off */
- clk_disable_unprepare(priv->plat->clk_ptp_ref);
+ /* A detached datapath may already have released its PTP clock. */
+ if (priv->datapath != STMMAC_DATAPATH_DOWN)
+ clk_disable_unprepare(priv->plat->clk_ptp_ref);
ret = pm_runtime_force_suspend(dev);
if (ret)
@@ -984,12 +985,14 @@ static int __maybe_unused stmmac_pltfr_noirq_resume(struct device *dev)
if (ret)
return ret;
- ret = clk_prepare_enable(priv->plat->clk_ptp_ref);
- if (ret < 0) {
- netdev_warn(priv->dev,
- "failed to enable PTP reference clock: %pe\n",
- ERR_PTR(ret));
- return ret;
+ if (priv->datapath != STMMAC_DATAPATH_DOWN) {
+ ret = clk_prepare_enable(priv->plat->clk_ptp_ref);
+ if (ret < 0) {
+ netdev_warn(priv->dev,
+ "failed to enable PTP reference clock: %pe\n",
+ ERR_PTR(ret));
+ return ret;
+ }
}
}
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c
index c25dc9f89270c..2511339e55e4e 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c
@@ -887,8 +887,8 @@ static int stmmac_test_flowctrl(struct stmmac_priv *priv)
struct stmmac_channel *ch = &priv->channel[i];
u32 tail;
- tail = priv->dma_conf.rx_queue[i].dma_rx_phy +
- (priv->dma_conf.dma_rx_size * sizeof(struct dma_desc));
+ tail = priv->dma_conf->rx_queue[i].dma_rx_phy +
+ (priv->dma_conf->dma_rx_size * sizeof(struct dma_desc));
stmmac_set_rx_tail_ptr(priv, priv->ioaddr, tail, i);
stmmac_start_rx(priv, priv->ioaddr, i);
@@ -1787,7 +1787,7 @@ static int stmmac_test_arpoffload(struct stmmac_priv *priv)
static int __stmmac_test_jumbo(struct stmmac_priv *priv, u16 queue)
{
struct stmmac_packet_attrs attr = { };
- int size = priv->dma_conf.dma_buf_sz;
+ int size = priv->dma_conf->dma_buf_sz;
if (!dwmac_is_xmac(priv->plat->core_type))
size -= NET_IP_ALIGN;
@@ -1873,7 +1873,7 @@ static int stmmac_test_tbs(struct stmmac_priv *priv)
/* Find first TBS enabled Queue, if any */
for (i = 0; i < priv->plat->tx_queues_to_use; i++)
- if (priv->dma_conf.tx_queue[i].tbs & STMMAC_TBS_AVAIL)
+ if (priv->dma_conf->tx_queue[i].tbs & STMMAC_TBS_AVAIL)
break;
if (i >= priv->plat->tx_queues_to_use)
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c
index 42a00446e9b41..bb1dfe2702acd 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c
@@ -1197,13 +1197,13 @@ static int tc_setup_etf(struct stmmac_priv *priv,
return -EOPNOTSUPP;
if (qopt->queue >= priv->plat->tx_queues_to_use)
return -EINVAL;
- if (!(priv->dma_conf.tx_queue[qopt->queue].tbs & STMMAC_TBS_AVAIL))
+ if (!(priv->dma_conf->tx_queue[qopt->queue].tbs & STMMAC_TBS_AVAIL))
return -EINVAL;
if (qopt->enable)
- priv->dma_conf.tx_queue[qopt->queue].tbs |= STMMAC_TBS_EN;
+ priv->dma_conf->tx_queue[qopt->queue].tbs |= STMMAC_TBS_EN;
else
- priv->dma_conf.tx_queue[qopt->queue].tbs &= ~STMMAC_TBS_EN;
+ priv->dma_conf->tx_queue[qopt->queue].tbs &= ~STMMAC_TBS_EN;
netdev_info(priv->dev, "%s ETF for Queue %d\n",
qopt->enable ? "enabled" : "disabled", qopt->queue);
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_xdp.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_xdp.c
index d7e4db7224b0c..907ac49a1b764 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_xdp.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_xdp.c
@@ -9,7 +9,6 @@
static int stmmac_xdp_enable_pool(struct stmmac_priv *priv,
struct xsk_buff_pool *pool, u16 queue)
{
- struct stmmac_channel *ch = &priv->channel[queue];
bool need_update;
u32 frame_size;
int err;
@@ -31,25 +30,26 @@ static int stmmac_xdp_enable_pool(struct stmmac_priv *priv,
return err;
}
- need_update = netif_running(priv->dev) && stmmac_xdp_is_enabled(priv);
+ need_update = priv->datapath == STMMAC_DATAPATH_RUNNING &&
+ stmmac_xdp_is_enabled(priv);
- if (need_update) {
- napi_disable(&ch->rx_napi);
- napi_disable(&ch->tx_napi);
- stmmac_disable_rx_queue(priv, queue);
- stmmac_disable_tx_queue(priv, queue);
- }
+ if (need_update)
+ stmmac_xdp_release(priv->dev);
set_bit(queue, priv->af_xdp_zc_qps);
if (need_update) {
- stmmac_enable_rx_queue(priv, queue);
- stmmac_enable_tx_queue(priv, queue);
- napi_enable(&ch->rxtx_napi);
-
- err = stmmac_xsk_wakeup(priv->dev, queue, XDP_WAKEUP_RX);
- if (err)
+ err = stmmac_xdp_open(priv->dev);
+ if (err) {
+ clear_bit(queue, priv->af_xdp_zc_qps);
+ xsk_pool_dma_unmap(pool, STMMAC_RX_DMA_ATTR);
+ netdev_err(priv->dev, "failed reopening after XSK pool attach: %pe; interface remains detached\n",
+ ERR_PTR(err));
return err;
+ }
+
+ /* The pool is installed even if link resolution is still pending. */
+ napi_schedule(&priv->channel[queue].rxtx_napi);
}
return 0;
@@ -57,9 +57,9 @@ static int stmmac_xdp_enable_pool(struct stmmac_priv *priv,
static int stmmac_xdp_disable_pool(struct stmmac_priv *priv, u16 queue)
{
- struct stmmac_channel *ch = &priv->channel[queue];
struct xsk_buff_pool *pool;
bool need_update;
+ int err;
if (queue >= priv->plat->rx_queues_to_use ||
queue >= priv->plat->tx_queues_to_use)
@@ -69,26 +69,24 @@ static int stmmac_xdp_disable_pool(struct stmmac_priv *priv, u16 queue)
if (!pool)
return -EINVAL;
- need_update = netif_running(priv->dev) && stmmac_xdp_is_enabled(priv);
+ need_update = priv->datapath == STMMAC_DATAPATH_RUNNING &&
+ stmmac_xdp_is_enabled(priv);
- if (need_update) {
- napi_disable(&ch->rxtx_napi);
- stmmac_disable_rx_queue(priv, queue);
- stmmac_disable_tx_queue(priv, queue);
- synchronize_rcu();
- }
+ if (need_update)
+ stmmac_xdp_release(priv->dev);
xsk_pool_dma_unmap(pool, STMMAC_RX_DMA_ATTR);
clear_bit(queue, priv->af_xdp_zc_qps);
if (need_update) {
- stmmac_enable_rx_queue(priv, queue);
- stmmac_enable_tx_queue(priv, queue);
- napi_enable(&ch->rx_napi);
- napi_enable(&ch->tx_napi);
+ err = stmmac_xdp_open(priv->dev);
+ if (err)
+ netdev_err(priv->dev, "failed reopening after XSK pool removal: %pe; interface remains detached\n",
+ ERR_PTR(err));
}
+ /* Socket teardown must be able to unmap and free the removed pool. */
return 0;
}
@@ -106,8 +104,9 @@ int stmmac_xdp_set_prog(struct stmmac_priv *priv, struct bpf_prog *prog,
struct bpf_prog *old_prog;
bool need_update;
bool if_running;
+ int ret;
- if_running = netif_running(dev);
+ if_running = priv->datapath == STMMAC_DATAPATH_RUNNING;
if (prog && dev->mtu > ETH_DATA_LEN) {
/* For now, the driver doesn't support XDP functionality with
@@ -117,25 +116,41 @@ int stmmac_xdp_set_prog(struct stmmac_priv *priv, struct bpf_prog *prog,
return -EOPNOTSUPP;
}
- if (!prog)
- xdp_features_clear_redirect_target(dev);
-
need_update = !!priv->xdp_prog != !!prog;
if (if_running && need_update)
stmmac_xdp_release(dev);
old_prog = xchg(&priv->xdp_prog, prog);
- if (old_prog)
- bpf_prog_put(old_prog);
/* Disable RX SPH for XDP operation */
priv->sph_active = priv->sph_capable && !stmmac_xdp_is_enabled(priv);
- if (if_running && need_update)
- stmmac_xdp_open(dev);
+ if (if_running && need_update) {
+ ret = stmmac_xdp_open(dev);
+ if (ret) {
+ netdev_err(dev, "failed reopening after XDP change: %pe; interface remains detached\n",
+ ERR_PTR(ret));
+ if (prog) {
+ /* The core retains the old program on error and drops
+ * the reference it passed for the proposed program.
+ */
+ xchg(&priv->xdp_prog, old_prog);
+ priv->sph_active = priv->sph_capable && !old_prog;
+ return ret;
+ }
+ /* Uninstalling a BPF link must release its program even
+ * if the non-XDP datapath cannot be restarted.
+ */
+ }
+ }
+
+ if (old_prog)
+ bpf_prog_put(old_prog);
if (prog)
xdp_features_set_redirect_target(dev, false);
+ else
+ xdp_features_clear_redirect_target(dev);
return 0;
}
diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c
index 1bbcf46c83566..b7d30ca340316 100644
--- a/drivers/net/phy/phylink.c
+++ b/drivers/net/phy/phylink.c
@@ -78,6 +78,7 @@ struct phylink {
bool link_failed;
bool suspend_link_up;
+ bool suspend_speed_down;
bool force_major_config;
bool major_config_failed;
bool mac_supports_eee_ops;
@@ -2498,6 +2499,14 @@ void phylink_start(struct phylink *pl)
}
EXPORT_SYMBOL_GPL(phylink_start);
+static void phylink_restore_suspend_speed(struct phylink *pl)
+{
+ if (pl->suspend_speed_down) {
+ phylink_speed_up(pl);
+ pl->suspend_speed_down = false;
+ }
+}
+
/**
* phylink_stop() - stop a phylink instance
* @pl: a pointer to a &struct phylink returned from phylink_create()
@@ -2509,11 +2518,30 @@ EXPORT_SYMBOL_GPL(phylink_start);
*
* This will synchronously bring down the link if the link is not already
* down (in other words, it will trigger a mac_link_down() method call.)
+ * A suspended instance may be stopped without first calling phylink_resume().
+ * In particular, closing a device after a failed resume must not restart the
+ * link or reconfigure the MAC just to finish shutting it down.
+ * Any PHY advertisement reduced by phylink_suspend() is restored as part
+ * of this transition.
+ * If phylink_prepare_resume() powered up an already stopped PHY, suspend
+ * it again when Wake-on-LAN permits.
*/
void phylink_stop(struct phylink *pl)
{
ASSERT_RTNL();
+ /* Also undo PHY speed control when terminating a suspended instance. */
+ phylink_restore_suspend_speed(pl);
+
+ if (test_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state)) {
+ /* A failed MAC resume may have called phylink_prepare_resume()
+ * and powered the stopped PHY back up to supply its RX clock.
+ */
+ if (pl->phydev)
+ phy_suspend(pl->phydev);
+ return;
+ }
+
if (pl->sfp_bus)
sfp_upstream_stop(pl->sfp_bus);
if (pl->phydev)
@@ -2526,6 +2554,16 @@ void phylink_stop(struct phylink *pl)
phylink_run_resolve_and_disable(pl, PHYLINK_DISABLE_STOPPED);
+ if (test_bit(PHYLINK_DISABLE_MAC_WOL, &pl->phylink_disable_state)) {
+ /* Finish the link-down deferred by MAC WoL, without restarting. */
+ flush_work(&pl->resolve);
+ mutex_lock(&pl->state_mutex);
+ if (pl->suspend_link_up)
+ phylink_link_down(pl);
+ __clear_bit(PHYLINK_DISABLE_MAC_WOL, &pl->phylink_disable_state);
+ mutex_unlock(&pl->state_mutex);
+ }
+
pl->pcs_state = PCS_STATE_DOWN;
phylink_pcs_disable(pl->pcs);
@@ -2657,8 +2695,10 @@ void phylink_suspend(struct phylink *pl, bool mac_wol)
phylink_stop(pl);
}
- if (phylink_phy_pm_speed_ctrl(pl))
+ if (phylink_phy_pm_speed_ctrl(pl)) {
phylink_speed_down(pl, false);
+ pl->suspend_speed_down = true;
+ }
}
EXPORT_SYMBOL_GPL(phylink_suspend);
@@ -2698,8 +2738,7 @@ void phylink_resume(struct phylink *pl)
{
ASSERT_RTNL();
- if (phylink_phy_pm_speed_ctrl(pl))
- phylink_speed_up(pl);
+ phylink_restore_suspend_speed(pl);
if (test_bit(PHYLINK_DISABLE_MAC_WOL, &pl->phylink_disable_state)) {
/* Wake-on-Lan enabled, MAC handling */
@@ -3616,6 +3655,12 @@ int phylink_speed_down(struct phylink *pl, bool sync)
ASSERT_RTNL();
+ /* An explicit request takes over from suspend-time speed control.
+ * Restore the original advertisement before saving it again, so a
+ * repeated speed-down cannot replace it with the reduced advertisement.
+ */
+ phylink_restore_suspend_speed(pl);
+
if (!pl->sfp_bus && pl->phydev)
ret = phy_speed_down(pl->phydev, sync);
|
| 3/2 | 2026/09/26 22:50 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 41282 output: 40 thoughts: 389 Thoughts: **Assessing the Fuzzing Potential of This Patch Series** Okay, let's dive into this patch series and see if it's worth our time to throw Syzkaller at it. The initial step is always to scope the changes. Right away, I see modifications across the `drivers/net/ethernet/stmicro/stmmac` directory and then, wait, `drivers/net/phy/phylink.c`? That's interesting, usually, PHY drivers are fairly self-contained. My immediate thought is to focus on `stmmac` first, given the concentration of changes there. But before I even start digging into the code, I need to consider accessibility. Is this something I can readily hit in a standard virtualized environment like QEMU or Google Compute Engine? Hmm, does QEMU emulate the stmmac? Or is it specific to an SoC Ethernet controller, like the Synopsys DesignWare Ethernet MAC or something from STMicroelectronics? I need to clarify that. Let me check. Does `stmmac` have any PCI glue? That would be a good starting point. I need to quickly grep for PCI within the `stmmac` directory to see if it's even remotely accessible through PCI, which would give me a far easier time to reproduce this within Syzkaller. |
| 4/2 | 2026/09/26 22:50 | tool | grepper |
0mError:no matches Args: {
"Expression": "pci_register_driver",
"PathPrefix": "drivers/net/ethernet/stmicro/stmmac/"
}Results: {
"error": "no matches"
} |
| 5/2 | 2026/09/26 22:50 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 42004 output: 32 thoughts: 21 Thoughts: Here's my thought process, as requested: **Navigating the stmmac Directory** Okay, so I need to get a handle on the STMicroelectronics Ethernet MAC driver. The `stmmac` driver is likely where I'll find all the critical components. I'll need to know what files and subdirectories are present within that area, `drivers/net/ethernet/stmicro/stmmac/`. I want to quickly gain an understanding of the structure and what components they manage. This includes, of course, the configuration files, and likely any firmware handling. My goal is to use this directory and all information within it to build a robust solution. |
| 6/2 | 2026/09/26 22:50 | tool | codesearch-dir-index |
0mArgs:{
"Dir": "drivers/net/ethernet/stmicro/stmmac/"
}Results: {
"Files": [
"chain_mode.c",
"common.h",
"descs.h",
"descs_com.h",
"dwmac-anarion.c",
"dwmac-dwc-qos-eth.c",
"dwmac-eic7700.c",
"dwmac-generic.c",
"dwmac-imx.c",
"dwmac-ingenic.c",
"dwmac-intel-plat.c",
"dwmac-intel.c",
"dwmac-intel.h",
"dwmac-ipq806x.c",
"dwmac-loongson.c",
"dwmac-loongson1.c",
"dwmac-lpc18xx.c",
"dwmac-mediatek.c",
"dwmac-meson.c",
"dwmac-meson8b.c",
"dwmac-motorcomm.c",
"dwmac-nuvoton.c",
"dwmac-qcom-ethqos.c",
"dwmac-renesas-gbeth.c",
"dwmac-rk.c",
"dwmac-rzn1.c",
"dwmac-s32.c",
"dwmac-socfpga.c",
"dwmac-sophgo.c",
"dwmac-spacemit.c",
"dwmac-starfive.c",
"dwmac-sti.c",
"dwmac-stm32.c",
"dwmac-sun55i.c",
"dwmac-sun8i.c",
"dwmac-sunxi.c",
"dwmac-tegra.c",
"dwmac-thead.c",
"dwmac-visconti.c",
"dwmac100.h",
"dwmac1000.h",
"dwmac1000_core.c",
"dwmac1000_dma.c",
"dwmac100_core.c",
"dwmac100_dma.c",
"dwmac4.h",
"dwmac4_core.c",
"dwmac4_descs.c",
"dwmac4_descs.h",
"dwmac4_dma.c",
"dwmac4_dma.h",
"dwmac4_lib.c",
"dwmac5.c",
"dwmac5.h",
"dwmac_dma.h",
"dwmac_lib.c",
"dwxgmac2.h",
"dwxgmac2_core.c",
"dwxgmac2_descs.c",
"dwxgmac2_dma.c",
"dwxlgmac2.h",
"enh_desc.c",
"hwif.c",
"hwif.h",
"mmc.h",
"mmc_core.c",
"norm_desc.c",
"ring_mode.c",
"stmmac.h",
"stmmac_est.c",
"stmmac_est.h",
"stmmac_ethtool.c",
"stmmac_fpe.c",
"stmmac_fpe.h",
"stmmac_hwtstamp.c",
"stmmac_libpci.c",
"stmmac_libpci.h",
"stmmac_main.c",
"stmmac_mdio.c",
"stmmac_pci.c",
"stmmac_pcs.c",
"stmmac_pcs.h",
"stmmac_platform.c",
"stmmac_platform.h",
"stmmac_ptp.c",
"stmmac_ptp.h",
"stmmac_selftests.c",
"stmmac_tc.c",
"stmmac_vlan.c",
"stmmac_vlan.h",
"stmmac_xdp.c",
"stmmac_xdp.h"
],
"Subdirs": null
} |
| 7/2 | 2026/09/26 22:50 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 43163 output: 18 thoughts: 116 Thoughts: **Investigating QEMU Network Emulation with a Focus on PHY Abstraction** Okay, so I need to figure out how network hardware is emulated in QEMU, specifically focusing on the PHY layer. My initial instinct was to jump into `stmmac_pci.c`, but before I dive too deep into that specific driver, I need to understand if the emulation even utilizes it directly. It’s possible QEMU has its own abstracted layer. Hmm... wait a minute! Let me check `drivers/net/phy/phylink.c`. That’s likely where the core PHY abstraction resides. Let's see who is actually *using* that phylink core functionality, because that's where the answer is likely hiding. Phylink is a central piece of the puzzle, a generic PHY abstraction layer within the Linux networking subsystem. But the key is to determine if this abstraction is used by the *virtual* network devices within QEMU, or if the emulated devices bypass this layer entirely. I need to know the calling relationships. Time to check which drivers are actively using `phylink` to get a better handle on the architecture. |
| 8/2 | 2026/09/26 22:50 | tool | grepper |
0mArgs:{
"Expression": "phylink_create"
}Results: {
"Output": "Documentation/networking/sfp-phylink.rst=74=this documentation.\n--\nDocumentation/networking/sfp-phylink.rst-363-\nDocumentation/networking/sfp-phylink.rst:364:\tphylink = phylink_create(\u0026priv-\u003ephylink_config, node, phy_mode, \u0026phylink_ops);\nDocumentation/networking/sfp-phylink.rst-365-\tif (IS_ERR(phylink)) {\n--\ndrivers/net/ethernet/altera/altera_tse_main.c=1128=static int altera_tse_probe(struct platform_device *pdev)\n--\ndrivers/net/ethernet/altera/altera_tse_main.c-1415-\ndrivers/net/ethernet/altera/altera_tse_main.c:1416:\tpriv-\u003ephylink = phylink_create(\u0026priv-\u003ephylink_config,\ndrivers/net/ethernet/altera/altera_tse_main.c-1417-\t\t\t\t of_fwnode_handle(priv-\u003edevice-\u003eof_node),\n--\ndrivers/net/ethernet/atheros/ag71xx.c=1074=static int ag71xx_phylink_setup(struct ag71xx *ag)\n--\ndrivers/net/ethernet/atheros/ag71xx.c-1108-\ndrivers/net/ethernet/atheros/ag71xx.c:1109:\tphylink = phylink_create(\u0026ag-\u003ephylink_config, ag-\u003epdev-\u003edev.fwnode,\ndrivers/net/ethernet/atheros/ag71xx.c-1110-\t\t\t\t ag-\u003ephy_if_mode, \u0026ag71xx_phylink_mac_ops);\n--\ndrivers/net/ethernet/cadence/macb_main.c=1023=static int macb_mii_probe(struct net_device *netdev)\n--\ndrivers/net/ethernet/cadence/macb_main.c-1085-\ndrivers/net/ethernet/cadence/macb_main.c:1086:\tbp-\u003ephylink = phylink_create(\u0026bp-\u003ephylink_config, bp-\u003epdev-\u003edev.fwnode,\ndrivers/net/ethernet/cadence/macb_main.c-1087-\t\t\t\t bp-\u003ephy_interface, \u0026macb_phylink_ops);\n--\ndrivers/net/ethernet/freescale/dpaa/dpaa_eth.c=201=static int dpaa_netdev_init(struct net_device *net_dev,\n--\ndrivers/net/ethernet/freescale/dpaa/dpaa_eth.c-274-\tmac_dev-\u003eupdate_speed = dpaa_eth_cgr_set_speed;\ndrivers/net/ethernet/freescale/dpaa/dpaa_eth.c:275:\tmac_dev-\u003ephylink = phylink_create(\u0026mac_dev-\u003ephylink_config,\ndrivers/net/ethernet/freescale/dpaa/dpaa_eth.c-276-\t\t\t\t\t dev_fwnode(mac_dev-\u003edev),\n--\ndrivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c=560=int dpaa2_mac_connect(struct dpaa2_mac *mac)\n--\ndrivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c-624-\ndrivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c:625:\tphylink = phylink_create(\u0026mac-\u003ephylink_config,\ndrivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c-626-\t\t\t\t dpmac_node, mac-\u003eif_mode,\n--\ndrivers/net/ethernet/freescale/enetc/enetc4_pf.c=923=static int enetc4_link_init(struct enetc_ndev_priv *priv,\n--\ndrivers/net/ethernet/freescale/enetc/enetc4_pf.c-941-\ndrivers/net/ethernet/freescale/enetc/enetc4_pf.c:942:\terr = enetc_phylink_create(priv, node, \u0026enetc_pl_mac_ops);\ndrivers/net/ethernet/freescale/enetc/enetc4_pf.c-943-\tif (err) {\ndrivers/net/ethernet/freescale/enetc/enetc4_pf.c-944-\t\tdev_err(dev, \"Failed to create phylink\\n\");\ndrivers/net/ethernet/freescale/enetc/enetc4_pf.c:945:\t\tgoto err_phylink_create;\ndrivers/net/ethernet/freescale/enetc/enetc4_pf.c-946-\t}\n--\ndrivers/net/ethernet/freescale/enetc/enetc4_pf.c-949-\ndrivers/net/ethernet/freescale/enetc/enetc4_pf.c:950:err_phylink_create:\ndrivers/net/ethernet/freescale/enetc/enetc4_pf.c-951-\tenetc_mdiobus_destroy(pf);\n--\ndrivers/net/ethernet/freescale/enetc/enetc_pf.c=785=static int enetc_pf_probe(struct pci_dev *pdev,\n--\ndrivers/net/ethernet/freescale/enetc/enetc_pf.c-867-\ndrivers/net/ethernet/freescale/enetc/enetc_pf.c:868:\terr = enetc_phylink_create(priv, node, \u0026enetc_mac_phylink_ops);\ndrivers/net/ethernet/freescale/enetc/enetc_pf.c-869-\tif (err)\ndrivers/net/ethernet/freescale/enetc/enetc_pf.c:870:\t\tgoto err_phylink_create;\ndrivers/net/ethernet/freescale/enetc/enetc_pf.c-871-\n--\ndrivers/net/ethernet/freescale/enetc/enetc_pf.c-879-\tenetc_phylink_destroy(priv);\ndrivers/net/ethernet/freescale/enetc/enetc_pf.c:880:err_phylink_create:\ndrivers/net/ethernet/freescale/enetc/enetc_pf.c-881-\tenetc_mdiobus_destroy(pf);\n--\ndrivers/net/ethernet/freescale/enetc/enetc_pf_common.c=419=EXPORT_SYMBOL_GPL(enetc_mdiobus_destroy);\ndrivers/net/ethernet/freescale/enetc/enetc_pf_common.c-420-\ndrivers/net/ethernet/freescale/enetc/enetc_pf_common.c:421:int enetc_phylink_create(struct enetc_ndev_priv *priv, struct device_node *node,\ndrivers/net/ethernet/freescale/enetc/enetc_pf_common.c-422-\t\t\t const struct phylink_mac_ops *ops)\n--\ndrivers/net/ethernet/freescale/enetc/enetc_pf_common.c-461-\tpf-\u003ephylink_config.mac_capabilities = mac_caps;\ndrivers/net/ethernet/freescale/enetc/enetc_pf_common.c:462:\tphylink = phylink_create(\u0026pf-\u003ephylink_config, of_fwnode_handle(node),\ndrivers/net/ethernet/freescale/enetc/enetc_pf_common.c-463-\t\t\t\t pf-\u003eif_mode, ops);\n--\ndrivers/net/ethernet/freescale/enetc/enetc_pf_common.c-472-}\ndrivers/net/ethernet/freescale/enetc/enetc_pf_common.c:473:EXPORT_SYMBOL_GPL(enetc_phylink_create);\ndrivers/net/ethernet/freescale/enetc/enetc_pf_common.c-474-\n--\ndrivers/net/ethernet/freescale/enetc/enetc_pf_common.h=12=void enetc_mdiobus_destroy(struct enetc_pf *pf);\ndrivers/net/ethernet/freescale/enetc/enetc_pf_common.h:13:int enetc_phylink_create(struct enetc_ndev_priv *priv, struct device_node *node,\ndrivers/net/ethernet/freescale/enetc/enetc_pf_common.h-14-\t\t\t const struct phylink_mac_ops *ops);\n--\ndrivers/net/ethernet/freescale/fs_enet/fs_enet-main.c=853=static int fs_enet_probe(struct platform_device *ofdev)\n--\ndrivers/net/ethernet/freescale/fs_enet/fs_enet-main.c-932-\ndrivers/net/ethernet/freescale/fs_enet/fs_enet-main.c:933:\tphylink = phylink_create(\u0026fep-\u003ephylink_config, dev_fwnode(fep-\u003edev),\ndrivers/net/ethernet/freescale/fs_enet/fs_enet-main.c-934-\t\t\t\t phy_mode, \u0026fs_enet_phylink_mac_ops);\n--\ndrivers/net/ethernet/freescale/ucc_geth.c=3419=static int ucc_geth_probe(struct platform_device* ofdev)\n--\ndrivers/net/ethernet/freescale/ucc_geth.c-3573-\ndrivers/net/ethernet/freescale/ucc_geth.c:3574:\tphylink = phylink_create(\u0026ugeth-\u003ephylink_config, dev_fwnode(\u0026dev-\u003edev),\ndrivers/net/ethernet/freescale/ucc_geth.c-3575-\t\t\t\t phy_interface, \u0026ugeth_mac_ops);\n--\ndrivers/net/ethernet/marvell/mvneta.c=5480=static int mvneta_probe(struct platform_device *pdev)\n--\ndrivers/net/ethernet/marvell/mvneta.c-5598-\ndrivers/net/ethernet/marvell/mvneta.c:5599:\tphylink = phylink_create(\u0026pp-\u003ephylink_config, pdev-\u003edev.fwnode,\ndrivers/net/ethernet/marvell/mvneta.c-5600-\t\t\t\t phy_mode, \u0026mvneta_phylink_ops);\n--\ndrivers/net/ethernet/marvell/mvpp2/mvpp2_main.c=6841=static int mvpp2_port_probe(struct platform_device *pdev,\n--\ndrivers/net/ethernet/marvell/mvpp2/mvpp2_main.c-7148-\ndrivers/net/ethernet/marvell/mvpp2/mvpp2_main.c:7149:\t\tphylink = phylink_create(\u0026port-\u003ephylink_config, port_fwnode,\ndrivers/net/ethernet/marvell/mvpp2/mvpp2_main.c-7150-\t\t\t\t\t phy_mode, \u0026mvpp2_phylink_ops);\n--\ndrivers/net/ethernet/marvell/prestera/prestera_main.c=370=static int prestera_port_sfp_bind(struct prestera_port *port)\n--\ndrivers/net/ethernet/marvell/prestera/prestera_main.c-416-\ndrivers/net/ethernet/marvell/prestera/prestera_main.c:417:\t\tphy_link = phylink_create(\u0026port-\u003ephy_config, fwnode,\ndrivers/net/ethernet/marvell/prestera/prestera_main.c-418-\t\t\t\t\t PHY_INTERFACE_MODE_INTERNAL,\n--\ndrivers/net/ethernet/mediatek/mtk_eth_soc.c=4828=static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np)\n--\ndrivers/net/ethernet/mediatek/mtk_eth_soc.c-4984-\ndrivers/net/ethernet/mediatek/mtk_eth_soc.c:4985:\tphylink = phylink_create(\u0026mac-\u003ephylink_config,\ndrivers/net/ethernet/mediatek/mtk_eth_soc.c-4986-\t\t\t\t of_fwnode_handle(mac-\u003eof_node),\n--\ndrivers/net/ethernet/meta/fbnic/fbnic_netdev.c=746=struct net_device *fbnic_netdev_alloc(struct fbnic_dev *fbd)\n--\ndrivers/net/ethernet/meta/fbnic/fbnic_netdev.c-834-\ndrivers/net/ethernet/meta/fbnic/fbnic_netdev.c:835:\tif (fbnic_phylink_create(netdev)) {\ndrivers/net/ethernet/meta/fbnic/fbnic_netdev.c-836-\t\tfree_netdev(netdev);\n--\ndrivers/net/ethernet/meta/fbnic/fbnic_netdev.h=111=int fbnic_phylink_get_fecparam(struct net_device *netdev,\ndrivers/net/ethernet/meta/fbnic/fbnic_netdev.h-112-\t\t\t struct ethtool_fecparam *fecparam);\ndrivers/net/ethernet/meta/fbnic/fbnic_netdev.h:113:int fbnic_phylink_create(struct net_device *netdev);\ndrivers/net/ethernet/meta/fbnic/fbnic_netdev.h-114-void fbnic_phylink_destroy(struct net_device *netdev);\n--\ndrivers/net/ethernet/meta/fbnic/fbnic_phylink.c=177=static const struct phylink_mac_ops fbnic_phylink_mac_ops = {\n--\ndrivers/net/ethernet/meta/fbnic/fbnic_phylink.c-186-/**\ndrivers/net/ethernet/meta/fbnic/fbnic_phylink.c:187: * fbnic_phylink_create - Phylink device creation\ndrivers/net/ethernet/meta/fbnic/fbnic_phylink.c-188- * @netdev: Network Device struct to attach phylink device\n--\ndrivers/net/ethernet/meta/fbnic/fbnic_phylink.c-196- **/\ndrivers/net/ethernet/meta/fbnic/fbnic_phylink.c:197:int fbnic_phylink_create(struct net_device *netdev)\ndrivers/net/ethernet/meta/fbnic/fbnic_phylink.c-198-{\n--\ndrivers/net/ethernet/meta/fbnic/fbnic_phylink.c-231-\ndrivers/net/ethernet/meta/fbnic/fbnic_phylink.c:232:\tphylink = phylink_create(\u0026fbn-\u003ephylink_config, NULL,\ndrivers/net/ethernet/meta/fbnic/fbnic_phylink.c-233-\t\t\t\t fbnic_phylink_select_interface(fbn-\u003eaui),\n--\ndrivers/net/ethernet/microchip/lan743x_main.c=3152=static const struct phylink_mac_ops lan743x_phylink_mac_ops = {\n--\ndrivers/net/ethernet/microchip/lan743x_main.c-3159-\ndrivers/net/ethernet/microchip/lan743x_main.c:3160:static int lan743x_phylink_create(struct lan743x_adapter *adapter)\ndrivers/net/ethernet/microchip/lan743x_main.c-3161-{\n--\ndrivers/net/ethernet/microchip/lan743x_main.c-3211-\ndrivers/net/ethernet/microchip/lan743x_main.c:3212:\tpl = phylink_create(\u0026adapter-\u003ephylink_config, NULL,\ndrivers/net/ethernet/microchip/lan743x_main.c-3213-\t\t\t adapter-\u003ephy_interface, \u0026lan743x_phylink_mac_ops);\n--\ndrivers/net/ethernet/microchip/lan743x_main.c=3691=static int lan743x_pcidev_probe(struct pci_dev *pdev,\n--\ndrivers/net/ethernet/microchip/lan743x_main.c-3750-\ndrivers/net/ethernet/microchip/lan743x_main.c:3751:\tret = lan743x_phylink_create(adapter);\ndrivers/net/ethernet/microchip/lan743x_main.c-3752-\tif (ret \u003c 0) {\n--\ndrivers/net/ethernet/microchip/lan966x/lan966x_main.c=791=static int lan966x_probe_port(struct lan966x *lan966x, u32 p,\n--\ndrivers/net/ethernet/microchip/lan966x/lan966x_main.c-855-\ndrivers/net/ethernet/microchip/lan966x/lan966x_main.c:856:\tphylink = phylink_create(\u0026port-\u003ephylink_config,\ndrivers/net/ethernet/microchip/lan966x/lan966x_main.c-857-\t\t\t\t portnp,\n--\ndrivers/net/ethernet/microchip/sparx5/sparx5_main.c=312=static int sparx5_create_port(struct sparx5 *sparx5,\n--\ndrivers/net/ethernet/microchip/sparx5/sparx5_main.c-388-\ndrivers/net/ethernet/microchip/sparx5/sparx5_main.c:389:\tphylink = phylink_create(\u0026spx5_port-\u003ephylink_config,\ndrivers/net/ethernet/microchip/sparx5/sparx5_main.c-390-\t\t\t\t of_fwnode_handle(config-\u003enode),\n--\ndrivers/net/ethernet/mscc/ocelot_net.c=1774=static const struct phylink_mac_ops ocelot_phylink_ops = {\n--\ndrivers/net/ethernet/mscc/ocelot_net.c-1779-\ndrivers/net/ethernet/mscc/ocelot_net.c:1780:static int ocelot_port_phylink_create(struct ocelot *ocelot, int port,\ndrivers/net/ethernet/mscc/ocelot_net.c-1781-\t\t\t\t struct device_node *portnp)\n--\ndrivers/net/ethernet/mscc/ocelot_net.c-1820-\ndrivers/net/ethernet/mscc/ocelot_net.c:1821:\tphylink = phylink_create(\u0026priv-\u003ephylink_config,\ndrivers/net/ethernet/mscc/ocelot_net.c-1822-\t\t\t\t of_fwnode_handle(portnp),\n--\ndrivers/net/ethernet/mscc/ocelot_net.c=1843=int ocelot_probe_port(struct ocelot *ocelot, int port, struct regmap *target,\n--\ndrivers/net/ethernet/mscc/ocelot_net.c-1879-\ndrivers/net/ethernet/mscc/ocelot_net.c:1880:\terr = ocelot_port_phylink_create(ocelot, port, portnp);\ndrivers/net/ethernet/mscc/ocelot_net.c-1881-\tif (err)\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c=1374=static int stmmac_phylink_setup(struct stmmac_priv *priv)\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-1465-\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c:1466:\tphylink = phylink_create(config, dev_fwnode(priv-\u003edevice),\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-1467-\t\t\t\t priv-\u003eplat-\u003ephy_interface,\n--\ndrivers/net/ethernet/tehuti/tn40_phy.c=45=int tn40_phy_register(struct tn40_priv *priv)\n--\ndrivers/net/ethernet/tehuti/tn40_phy.c-62-\ndrivers/net/ethernet/tehuti/tn40_phy.c:63:\tphylink = phylink_create(config, NULL, PHY_INTERFACE_MODE_XAUI,\ndrivers/net/ethernet/tehuti/tn40_phy.c-64-\t\t\t\t \u0026tn40_mac_ops);\n--\ndrivers/net/ethernet/ti/am65-cpsw-nuss.c=2759=am65_cpsw_nuss_init_port_ndev(struct am65_cpsw_common *common, u32 port_idx)\n--\ndrivers/net/ethernet/ti/am65-cpsw-nuss.c-2843-\ndrivers/net/ethernet/ti/am65-cpsw-nuss.c:2844:\tphylink = phylink_create(\u0026port-\u003eslave.phylink_config,\ndrivers/net/ethernet/ti/am65-cpsw-nuss.c-2845-\t\t\t\t of_fwnode_handle(port-\u003eslave.port_np),\n--\ndrivers/net/ethernet/wangxun/ngbe/ngbe_mdio.c=130=static int ngbe_phylink_init(struct wx *wx)\n--\ndrivers/net/ethernet/wangxun/ngbe/ngbe_mdio.c-149-\ndrivers/net/ethernet/wangxun/ngbe/ngbe_mdio.c:150:\tphylink = phylink_create(config, NULL, phy_mode, \u0026ngbe_mac_ops);\ndrivers/net/ethernet/wangxun/ngbe/ngbe_mdio.c-151-\tif (IS_ERR(phylink))\n--\ndrivers/net/ethernet/wangxun/txgbe/txgbe_aml.c=482=int txgbe_phylink_init_aml(struct txgbe *txgbe)\n--\ndrivers/net/ethernet/wangxun/txgbe/txgbe_aml.c-512-\ndrivers/net/ethernet/wangxun/txgbe/txgbe_aml.c:513:\tphylink = phylink_create(config, NULL, phy_mode, \u0026txgbe_mac_ops_aml);\ndrivers/net/ethernet/wangxun/txgbe/txgbe_aml.c-514-\tif (IS_ERR(phylink))\n--\ndrivers/net/ethernet/wangxun/txgbe/txgbe_phy.c=268=static int txgbe_phylink_init(struct txgbe *txgbe)\n--\ndrivers/net/ethernet/wangxun/txgbe/txgbe_phy.c-292-\ndrivers/net/ethernet/wangxun/txgbe/txgbe_phy.c:293:\tphylink = phylink_create(config, fwnode, phy_mode, \u0026txgbe_mac_ops);\ndrivers/net/ethernet/wangxun/txgbe/txgbe_phy.c-294-\tif (IS_ERR(phylink))\n--\ndrivers/net/ethernet/xilinx/xilinx_axienet_main.c=2795=static int axienet_probe(struct platform_device *pdev)\n--\ndrivers/net/ethernet/xilinx/xilinx_axienet_main.c-3120-\ndrivers/net/ethernet/xilinx/xilinx_axienet_main.c:3121:\tlp-\u003ephylink = phylink_create(\u0026lp-\u003ephylink_config, pdev-\u003edev.fwnode,\ndrivers/net/ethernet/xilinx/xilinx_axienet_main.c-3122-\t\t\t\t lp-\u003ephy_mode,\n--\ndrivers/net/ethernet/xilinx/xilinx_axienet_main.c-3125-\t\tret = PTR_ERR(lp-\u003ephylink);\ndrivers/net/ethernet/xilinx/xilinx_axienet_main.c:3126:\t\tdev_err(\u0026pdev-\u003edev, \"phylink_create error (%i)\\n\", ret);\ndrivers/net/ethernet/xilinx/xilinx_axienet_main.c-3127-\t\tgoto cleanup_mdio;\n--\ndrivers/net/phy/phylink.c=516=static int phylink_validate_mac_and_pcs(struct phylink *pl,\n--\ndrivers/net/phy/phylink.c-531-\tif (pcs) {\ndrivers/net/phy/phylink.c:532:\t\t/* The PCS, if present, must be setup before phylink_create()\ndrivers/net/phy/phylink.c-533-\t\t * has been called. If the ops is not initialised, print an\n--\ndrivers/net/phy/phylink.c=1039=static enum inband_type phylink_get_inband_type(phy_interface_t interface)\n--\ndrivers/net/phy/phylink.c-1071- * phylink_pcs_neg_mode() - helper to determine PCS inband mode\ndrivers/net/phy/phylink.c:1072: * @pl: a pointer to a \u0026struct phylink returned from phylink_create()\ndrivers/net/phy/phylink.c-1073- * @pcs: a pointer to \u0026struct phylink_pcs\n--\ndrivers/net/phy/phylink.c=1771=static int phylink_register_sfp(struct phylink *pl,\n--\ndrivers/net/phy/phylink.c-1795- * phylink_set_fixed_link() - set the fixed link\ndrivers/net/phy/phylink.c:1796: * @pl: a pointer to a \u0026struct phylink returned from phylink_create()\ndrivers/net/phy/phylink.c-1797- * @state: a pointer to a struct phylink_link_state.\n--\ndrivers/net/phy/phylink.c=1833=EXPORT_SYMBOL_GPL(phylink_set_fixed_link);\n--\ndrivers/net/phy/phylink.c-1835-/**\ndrivers/net/phy/phylink.c:1836: * phylink_create() - create a phylink instance\ndrivers/net/phy/phylink.c-1837- * @config: a pointer to the target \u0026struct phylink_config\n--\ndrivers/net/phy/phylink.c-1850- */\ndrivers/net/phy/phylink.c:1851:struct phylink *phylink_create(struct phylink_config *config,\ndrivers/net/phy/phylink.c-1852-\t\t\t const struct fwnode_handle *fwnode,\n--\ndrivers/net/phy/phylink.c-1938-}\ndrivers/net/phy/phylink.c:1939:EXPORT_SYMBOL_GPL(phylink_create);\ndrivers/net/phy/phylink.c-1940-\n--\ndrivers/net/phy/phylink.c-1942- * phylink_destroy() - cleanup and destroy the phylink instance\ndrivers/net/phy/phylink.c:1943: * @pl: a pointer to a \u0026struct phylink returned from phylink_create()\ndrivers/net/phy/phylink.c-1944- *\n--\ndrivers/net/phy/phylink.c=1959=EXPORT_SYMBOL_GPL(phylink_destroy);\n--\ndrivers/net/phy/phylink.c-1962- * phylink_expects_phy() - Determine if phylink expects a phy to be attached\ndrivers/net/phy/phylink.c:1963: * @pl: a pointer to a \u0026struct phylink returned from phylink_create()\ndrivers/net/phy/phylink.c-1964- *\n--\ndrivers/net/phy/phylink.c=2220=static int phylink_attach_phy(struct phylink *pl, struct phy_device *phy,\n--\ndrivers/net/phy/phylink.c-2238- * phylink_connect_phy() - connect a PHY to the phylink instance\ndrivers/net/phy/phylink.c:2239: * @pl: a pointer to a \u0026struct phylink returned from phylink_create()\ndrivers/net/phy/phylink.c-2240- * @phy: a pointer to a \u0026struct phy_device.\n--\ndrivers/net/phy/phylink.c=2272=EXPORT_SYMBOL_GPL(phylink_connect_phy);\n--\ndrivers/net/phy/phylink.c-2275- * phylink_of_phy_connect() - connect the PHY specified in the DT mode.\ndrivers/net/phy/phylink.c:2276: * @pl: a pointer to a \u0026struct phylink returned from phylink_create()\ndrivers/net/phy/phylink.c-2277- * @dn: a pointer to a \u0026struct device_node.\n--\ndrivers/net/phy/phylink.c=2291=EXPORT_SYMBOL_GPL(phylink_of_phy_connect);\n--\ndrivers/net/phy/phylink.c-2294- * phylink_fwnode_phy_connect() - connect the PHY specified in the fwnode.\ndrivers/net/phy/phylink.c:2295: * @pl: a pointer to a \u0026struct phylink returned from phylink_create()\ndrivers/net/phy/phylink.c-2296- * @fwnode: a pointer to a \u0026struct fwnode_handle.\n--\ndrivers/net/phy/phylink.c=2350=EXPORT_SYMBOL_GPL(phylink_fwnode_phy_connect);\n--\ndrivers/net/phy/phylink.c-2354- * instance.\ndrivers/net/phy/phylink.c:2355: * @pl: a pointer to a \u0026struct phylink returned from phylink_create()\ndrivers/net/phy/phylink.c-2356- *\n--\ndrivers/net/phy/phylink.c=2385=static void phylink_link_changed(struct phylink *pl, bool up, const char *what)\n--\ndrivers/net/phy/phylink.c-2394- * phylink_mac_change() - notify phylink of a change in MAC state\ndrivers/net/phy/phylink.c:2395: * @pl: a pointer to a \u0026struct phylink returned from phylink_create()\ndrivers/net/phy/phylink.c-2396- * @up: indicates whether the link is currently up.\n--\ndrivers/net/phy/phylink.c=2427=static irqreturn_t phylink_link_handler(int irq, void *data)\n--\ndrivers/net/phy/phylink.c-2437- * phylink_start() - start a phylink instance\ndrivers/net/phy/phylink.c:2438: * @pl: a pointer to a \u0026struct phylink returned from phylink_create()\ndrivers/net/phy/phylink.c-2439- *\n--\ndrivers/net/phy/phylink.c=2502=static void phylink_restore_suspend_speed(struct phylink *pl)\n--\ndrivers/net/phy/phylink.c-2511- * phylink_stop() - stop a phylink instance\ndrivers/net/phy/phylink.c:2512: * @pl: a pointer to a \u0026struct phylink returned from phylink_create()\ndrivers/net/phy/phylink.c-2513- *\n--\ndrivers/net/phy/phylink.c=2571=EXPORT_SYMBOL_GPL(phylink_stop);\n--\ndrivers/net/phy/phylink.c-2574- * phylink_rx_clk_stop_block() - block PHY ability to stop receive clock in LPI\ndrivers/net/phy/phylink.c:2575: * @pl: a pointer to a \u0026struct phylink returned from phylink_create()\ndrivers/net/phy/phylink.c-2576- *\n--\ndrivers/net/phy/phylink.c=2600=EXPORT_SYMBOL_GPL(phylink_rx_clk_stop_block);\n--\ndrivers/net/phy/phylink.c-2603- * phylink_rx_clk_stop_unblock() - unblock PHY ability to stop receive clock\ndrivers/net/phy/phylink.c:2604: * @pl: a pointer to a \u0026struct phylink returned from phylink_create()\ndrivers/net/phy/phylink.c-2605- *\n--\ndrivers/net/phy/phylink.c=2642=static bool phylink_phy_pm_speed_ctrl(struct phylink *pl)\n--\ndrivers/net/phy/phylink.c-2649- * phylink_suspend() - handle a network device suspend event\ndrivers/net/phy/phylink.c:2650: * @pl: a pointer to a \u0026struct phylink returned from phylink_create()\ndrivers/net/phy/phylink.c-2651- * @mac_wol: true if the MAC needs to receive packets for Wake-on-Lan\n--\ndrivers/net/phy/phylink.c=2703=EXPORT_SYMBOL_GPL(phylink_suspend);\n--\ndrivers/net/phy/phylink.c-2706- * phylink_prepare_resume() - prepare to resume a network device\ndrivers/net/phy/phylink.c:2707: * @pl: a pointer to a \u0026struct phylink returned from phylink_create()\ndrivers/net/phy/phylink.c-2708- *\n--\ndrivers/net/phy/phylink.c=2728=EXPORT_SYMBOL_GPL(phylink_prepare_resume);\n--\ndrivers/net/phy/phylink.c-2731- * phylink_resume() - handle a network device resume event\ndrivers/net/phy/phylink.c:2732: * @pl: a pointer to a \u0026struct phylink returned from phylink_create()\ndrivers/net/phy/phylink.c-2733- *\n--\ndrivers/net/phy/phylink.c=2770=EXPORT_SYMBOL_GPL(phylink_resume);\n--\ndrivers/net/phy/phylink.c-2773- * phylink_ethtool_get_wol() - get the wake on lan parameters for the PHY\ndrivers/net/phy/phylink.c:2774: * @pl: a pointer to a \u0026struct phylink returned from phylink_create()\ndrivers/net/phy/phylink.c-2775- * @wol: a pointer to \u0026struct ethtool_wolinfo to hold the read parameters\n--\ndrivers/net/phy/phylink.c=2807=EXPORT_SYMBOL_GPL(phylink_ethtool_get_wol);\n--\ndrivers/net/phy/phylink.c-2810- * phylink_ethtool_set_wol() - set wake on lan parameters\ndrivers/net/phy/phylink.c:2811: * @pl: a pointer to a \u0026struct phylink returned from phylink_create()\ndrivers/net/phy/phylink.c-2812- * @wol: a pointer to \u0026struct ethtool_wolinfo for the desired parameters\n--\ndrivers/net/phy/phylink.c=2939=static void phylink_get_ksettings(const struct phylink_link_state *state,\n--\ndrivers/net/phy/phylink.c-2954- * phylink_ethtool_ksettings_get() - get the current link settings\ndrivers/net/phy/phylink.c:2955: * @pl: a pointer to a \u0026struct phylink returned from phylink_create()\ndrivers/net/phy/phylink.c-2956- * @kset: a pointer to a \u0026struct ethtool_link_ksettings to hold link settings\n--\ndrivers/net/phy/phylink.c=3007=static bool phylink_validate_pcs_inband_autoneg(struct phylink *pl,\n--\ndrivers/net/phy/phylink.c-3028- * phylink_ethtool_ksettings_set() - set the link settings\ndrivers/net/phy/phylink.c:3029: * @pl: a pointer to a \u0026struct phylink returned from phylink_create()\ndrivers/net/phy/phylink.c-3030- * @kset: a pointer to a \u0026struct ethtool_link_ksettings for the desired modes\n--\ndrivers/net/phy/phylink.c=3195=EXPORT_SYMBOL_GPL(phylink_ethtool_ksettings_set);\n--\ndrivers/net/phy/phylink.c-3198- * phylink_ethtool_nway_reset() - restart negotiation\ndrivers/net/phy/phylink.c:3199: * @pl: a pointer to a \u0026struct phylink returned from phylink_create()\ndrivers/net/phy/phylink.c-3200- *\n--\ndrivers/net/phy/phylink.c=3220=EXPORT_SYMBOL_GPL(phylink_ethtool_nway_reset);\n--\ndrivers/net/phy/phylink.c-3223- * phylink_ethtool_get_pauseparam() - get the current pause parameters\ndrivers/net/phy/phylink.c:3224: * @pl: a pointer to a \u0026struct phylink returned from phylink_create()\ndrivers/net/phy/phylink.c-3225- * @pause: a pointer to a \u0026struct ethtool_pauseparam\n--\ndrivers/net/phy/phylink.c=3236=EXPORT_SYMBOL_GPL(phylink_ethtool_get_pauseparam);\n--\ndrivers/net/phy/phylink.c-3239- * phylink_ethtool_set_pauseparam() - set the current pause parameters\ndrivers/net/phy/phylink.c:3240: * @pl: a pointer to a \u0026struct phylink returned from phylink_create()\ndrivers/net/phy/phylink.c-3241- * @pause: a pointer to a \u0026struct ethtool_pauseparam\n--\ndrivers/net/phy/phylink.c=3322=EXPORT_SYMBOL_GPL(phylink_ethtool_set_pauseparam);\n--\ndrivers/net/phy/phylink.c-3326- * counter\ndrivers/net/phy/phylink.c:3327: * @pl: a pointer to a \u0026struct phylink returned from phylink_create().\ndrivers/net/phy/phylink.c-3328- *\n--\ndrivers/net/phy/phylink.c=3345=EXPORT_SYMBOL_GPL(phylink_get_eee_err);\n--\ndrivers/net/phy/phylink.c-3348- * phylink_ethtool_get_eee() - read the energy efficient ethernet parameters\ndrivers/net/phy/phylink.c:3349: * @pl: a pointer to a \u0026struct phylink returned from phylink_create()\ndrivers/net/phy/phylink.c-3350- * @eee: a pointer to a \u0026struct ethtool_keee for the read parameters\n--\ndrivers/net/phy/phylink.c=3371=EXPORT_SYMBOL_GPL(phylink_ethtool_get_eee);\n--\ndrivers/net/phy/phylink.c-3374- * phylink_ethtool_set_eee() - set the energy efficient ethernet parameters\ndrivers/net/phy/phylink.c:3375: * @pl: a pointer to a \u0026struct phylink returned from phylink_create()\ndrivers/net/phy/phylink.c-3376- * @eee: a pointer to a \u0026struct ethtool_keee for the desired parameters\n--\ndrivers/net/phy/phylink.c=3545=static int phylink_mii_write(struct phylink *pl, unsigned int phy_id,\n--\ndrivers/net/phy/phylink.c-3563- * phylink_mii_ioctl() - generic mii ioctl interface\ndrivers/net/phy/phylink.c:3564: * @pl: a pointer to a \u0026struct phylink returned from phylink_create()\ndrivers/net/phy/phylink.c-3565- * @ifr: a pointer to a \u0026struct ifreq for socket ioctls\n--\ndrivers/net/phy/phylink.c=3638=EXPORT_SYMBOL_GPL(phylink_mii_ioctl);\n--\ndrivers/net/phy/phylink.c-3642- * link partners\ndrivers/net/phy/phylink.c:3643: * @pl: a pointer to a \u0026struct phylink returned from phylink_create()\ndrivers/net/phy/phylink.c-3644- * @sync: perform action synchronously\n--\ndrivers/net/phy/phylink.c=3669=EXPORT_SYMBOL_GPL(phylink_speed_down);\n--\ndrivers/net/phy/phylink.c-3673- * phylink_speed_down()\ndrivers/net/phy/phylink.c:3674: * @pl: a pointer to a \u0026struct phylink returned from phylink_create()\ndrivers/net/phy/phylink.c-3675- *\n--\ndrivers/net/phy/phylink.c=4428=EXPORT_SYMBOL_GPL(phylink_mii_c45_pcs_get_state);\n--\ndrivers/net/phy/phylink.c-4432- *\t\t\t\t which loses state\ndrivers/net/phy/phylink.c:4433: * @pl: a pointer to a \u0026struct phylink returned from phylink_create()\ndrivers/net/phy/phylink.c-4434- *\n--\ndrivers/net/phy/phylink.c=4449=EXPORT_SYMBOL_GPL(phylink_replay_link_begin);\n--\ndrivers/net/phy/phylink.c-4453- *\t\t\t which lost state\ndrivers/net/phy/phylink.c:4454: * @pl: a pointer to a \u0026struct phylink returned from phylink_create()\ndrivers/net/phy/phylink.c-4455- *\n--\ndrivers/net/usb/asix_devices.c=799=static int ax88772_phylink_setup(struct usbnet *dev)\n--\ndrivers/net/usb/asix_devices.c-819-\ndrivers/net/usb/asix_devices.c:820:\tphylink = phylink_create(\u0026priv-\u003ephylink_config, dev-\u003enet-\u003edev.fwnode,\ndrivers/net/usb/asix_devices.c-821-\t\t\t\t phy_if_mode, \u0026ax88772_phylink_mac_ops);\n--\ndrivers/net/usb/lan78xx.c=2799=static int lan78xx_phylink_setup(struct lan78xx_net *dev)\n--\ndrivers/net/usb/lan78xx.c-2838-\ndrivers/net/usb/lan78xx.c:2839:\tphylink = phylink_create(pc, dev-\u003enet-\u003edev.fwnode,\ndrivers/net/usb/lan78xx.c-2840-\t\t\t\t dev-\u003einterface, \u0026lan78xx_phylink_mac_ops);\n--\ninclude/linux/phylink.h=718=int pcs_pre_init(struct phylink_pcs *pcs);\n--\ninclude/linux/phylink.h-721-\ninclude/linux/phylink.h:722:struct phylink *phylink_create(struct phylink_config *,\ninclude/linux/phylink.h-723-\t\t\t const struct fwnode_handle *,\n--\nnet/dsa/port.c=1611=static const struct phylink_mac_ops dsa_port_phylink_mac_ops = {\n--\nnet/dsa/port.c-1616-\nnet/dsa/port.c:1617:int dsa_port_phylink_create(struct dsa_port *dp)\nnet/dsa/port.c-1618-{\n--\nnet/dsa/port.c-1646-\nnet/dsa/port.c:1647:\tpl = phylink_create(\u0026dp-\u003epl_config, of_fwnode_handle(dp-\u003edn), mode,\nnet/dsa/port.c-1648-\t\t\t mac_ops);\n--\nnet/dsa/port.c=1665=static int dsa_shared_port_phylink_register(struct dsa_port *dp)\n--\nnet/dsa/port.c-1673-\nnet/dsa/port.c:1674:\terr = dsa_port_phylink_create(dp);\nnet/dsa/port.c-1675-\tif (err)\n--\nnet/dsa/port.h=100=int dsa_port_mrp_del_ring_role(const struct dsa_port *dp,\nnet/dsa/port.h-101-\t\t\t const struct switchdev_obj_ring_role_mrp *mrp);\nnet/dsa/port.h:102:int dsa_port_phylink_create(struct dsa_port *dp);\nnet/dsa/port.h-103-void dsa_port_phylink_destroy(struct dsa_port *dp);\n--\nnet/dsa/user.c=2654=static int dsa_user_phy_setup(struct net_device *user_dev)\n--\nnet/dsa/user.c-2673-\nnet/dsa/user.c:2674:\tret = dsa_port_phylink_create(dp);\nnet/dsa/user.c-2675-\tif (ret)\n"
} |
| 9/2 | 2026/09/26 22:50 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 55262 output: 42 thoughts: 138 Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience: **Kernel Network Driver Reconnaissance** Alright, let's see. My initial scan throws up some interesting candidates, specifically `drivers/net/usb/asix_devices.c`, `drivers/net/usb/lan78xx.c`, and `drivers/net/ethernet/meta/fbnic/fbnic_netdev.c`. Immediately, I see a potential avenue: USB network devices like Asix and Lan78xx are phylink-based, and that's key. This gives us a foothold, because with the recent advances, these can potentially be emulated via USB raw gadget, and potentially leveraged by syzkaller's USB emulation. Now, before I get too fixated on USB, I need to make sure I'm covering all bases. The `stmmac` driver is another prime target for this. I need to quickly determine if `stmmac` is reachable via PCI, platform, or other means. Let me take a look at `stmmac_pci.c` to see about access. |
| 10/2 | 2026/09/26 22:51 | tool | grepper |
0mArgs:{
"Expression": "MODULE_DEVICE_TABLE\\(pci",
"PathPrefix": "drivers/net/ethernet/stmicro/stmmac/"
}Results: {
"Output": "drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c=1395=static const struct pci_device_id intel_eth_pci_id_table[] = {\n--\ndrivers/net/ethernet/stmicro/stmmac/dwmac-intel.c-1414-};\ndrivers/net/ethernet/stmicro/stmmac/dwmac-intel.c:1415:MODULE_DEVICE_TABLE(pci, intel_eth_pci_id_table);\ndrivers/net/ethernet/stmicro/stmmac/dwmac-intel.c-1416-\n--\ndrivers/net/ethernet/stmicro/stmmac/dwmac-loongson.c=596=static const struct pci_device_id loongson_dwmac_id_table[] = {\n--\ndrivers/net/ethernet/stmicro/stmmac/dwmac-loongson.c-601-};\ndrivers/net/ethernet/stmicro/stmmac/dwmac-loongson.c:602:MODULE_DEVICE_TABLE(pci, loongson_dwmac_id_table);\ndrivers/net/ethernet/stmicro/stmmac/dwmac-loongson.c-603-\n--\ndrivers/net/ethernet/stmicro/stmmac/dwmac-motorcomm.c=367=static const struct pci_device_id dwmac_motorcomm_pci_id_table[] = {\n--\ndrivers/net/ethernet/stmicro/stmmac/dwmac-motorcomm.c-370-};\ndrivers/net/ethernet/stmicro/stmmac/dwmac-motorcomm.c:371:MODULE_DEVICE_TABLE(pci, dwmac_motorcomm_pci_id_table);\ndrivers/net/ethernet/stmicro/stmmac/dwmac-motorcomm.c-372-\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_pci.c=203=static const struct pci_device_id stmmac_id_table[] = {\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_pci.c-209-\ndrivers/net/ethernet/stmicro/stmmac/stmmac_pci.c:210:MODULE_DEVICE_TABLE(pci, stmmac_id_table);\ndrivers/net/ethernet/stmicro/stmmac/stmmac_pci.c-211-\n"
} |
| 11/2 | 2026/09/26 22:51 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 56319 output: 30 thoughts: 1770 Thoughts: **Analyzing Potential `stmmac` Emulation in QEMU and Fuzzing Relevance** Okay, let's break this down. My initial thought was: can I even *probe* or instantiate `stmmac` within QEMU? And, a quick reality check on available QEMU hardware showed that the default emulated devices, particularly on `amd64`, don't readily include an `stmmac` implementation. We're primarily seeing things like e1000, rtl8139, and virtio-net. The syzkaller situation is similar: no PCI device emulation unless QEMU provides it, or if it's using a USB raw gadget. So, my focus shifted to whether QEMU even *emulates* an `stmmac` PCI device. No, it doesn't. Now, could `stmmac` be hanging off of USB somehow? Nope. `stmmac` is firmly in the ethernet domain. But wait a minute! My next thought was to explore the `phylink.c` code. It's time to examine the changes there. My assumption is that `phylink` might be a more general interface that could be used by `stmmac`. The diff showed `phylink_suspend`, `phylink_resume`, `phylink_stop` and `phylink_speed_down` calls, with related fields like `suspend_speed_down` added, and the crucial fact that `phylink_stop` is reachable through the regular interface shutdown path. Okay, this is promising. The key is to understand what can *trigger* `phylink_stop`. Ah, it's interface shutdown! When an interface using `phylink` is brought down – like using `ip link set dev ethX down` – that calls `dev_close()` -> `ndo_stop()` -> which leads to the driver's `phylink_stop()` call. The goal is to analyze whether this path is present within the fuzzing environment: Is `lan78xx` the path? The question now is whether the `lan78xx_stop` function, in particular, calls `phylink_stop`. |
| 12/2 | 2026/09/26 22:51 | tool | grepper |
0mArgs:{
"Expression": "phylink_stop",
"PathPrefix": "drivers/net/usb/"
}Results: {
"Output": "drivers/net/usb/asix_devices.c=946=static int ax88772_stop(struct usbnet *dev)\n--\ndrivers/net/usb/asix_devices.c-949-\ndrivers/net/usb/asix_devices.c:950:\tphylink_stop(priv-\u003ephylink);\ndrivers/net/usb/asix_devices.c-951-\n--\ndrivers/net/usb/lan78xx.c=3538=static int lan78xx_stop(struct net_device *net)\n--\ndrivers/net/usb/lan78xx.c-3558-\ndrivers/net/usb/lan78xx.c:3559:\tphylink_stop(dev-\u003ephylink);\ndrivers/net/usb/lan78xx.c-3560-\n--\ndrivers/net/usb/lan78xx.c=4571=static void lan78xx_disconnect(struct usb_interface *intf)\n--\ndrivers/net/usb/lan78xx.c-4583-\trtnl_lock();\ndrivers/net/usb/lan78xx.c:4584:\tphylink_stop(dev-\u003ephylink);\ndrivers/net/usb/lan78xx.c-4585-\tphylink_disconnect_phy(dev-\u003ephylink);\n"
} |
| 13/2 | 2026/09/26 22:51 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 58937 output: 29 thoughts: 186 Thoughts: Here's my thought process, distilled: **Investigating Potential Fuzzing Targets in the Linux Kernel's Network Stack** Okay, so `ax88772_stop` and `lan78xx_stop` both ultimately hit `phylink_stop`. That's interesting, and worth remembering. And the critical connection here is that `asix` and `lan78xx` are both standard USB Ethernet adapters. And since Linux's USB raw gadget functionality and syzkaller are heavily used for USB fuzzing, it might be relevant to dig deeper. My mind immediately went to whether syzkaller can emulate `asix` or `lan78xx`, or similar network devices. Bingo! Syzkaller *does* have USB descriptions for USB Ethernet adapters. That's a significant lead. This could be a solid target. Now, let's switch gears and consider DSA (Distributed Switch Architecture). Does DSA make use of `phylink` under the hood? It seems like a logical spot for this. Let's see... a quick check on `net/dsa/user.c`... ah-ha! `dsa_user_open` calls `phylink_start(dp->pl)`! And, predictably, `dsa_user_close` calls `phylink_stop(dp->pl)`. This confirms the link! This whole DSA user port thing looks like a promising avenue for further investigation. Time to verify and probe those areas! |
| 14/2 | 2026/09/26 22:51 | tool | grepper |
0mArgs:{
"Expression": "phylink_stop",
"PathPrefix": "net/dsa/"
}Results: {
"Output": "net/dsa/port.c=245=void dsa_port_disable_rt(struct dsa_port *dp)\n--\nnet/dsa/port.c-250-\tif (dp-\u003epl)\nnet/dsa/port.c:251:\t\tphylink_stop(dp-\u003epl);\nnet/dsa/port.c-252-\n--\nnet/dsa/user.c=2726=int dsa_user_suspend(struct net_device *user_dev)\n--\nnet/dsa/user.c-2735-\trtnl_lock();\nnet/dsa/user.c:2736:\tphylink_stop(dp-\u003epl);\nnet/dsa/user.c-2737-\trtnl_unlock();\n"
} |
| 15/2 | 2026/09/26 22:51 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 59675 output: 20 thoughts: 46 Thoughts: Here's my thought process, as if I were analyzing that code: **Deep Dive into Phylink Speed Reduction** Okay, so I'm digging into something related to DSA (Distributed Switch Architecture) networking, specifically concerning speed reduction. My initial focus is on `net/dsa/user.c:2726` and `net/dsa/port.c:251`. I'll cross-reference these to establish a basic context of where speed adjustments are taking place within the driver. But before I start tracing the code, my immediate question is: *Who* is actually initiating this `phylink_speed_down` call? Understanding the caller is key. Knowing the initiating function will give me a clear understanding of the context of why and when we're seeing this speed reduction. Is it user-initiated, hardware triggered, or due to some internal management logic? Knowing the source of the speed reduction is the essential first step to understanding the flow of this code. That's my starting point. |
| 16/2 | 2026/09/26 22:51 | tool | grepper |
0mArgs:{
"Expression": "phylink_speed_down"
}Results: {
"Output": "drivers/net/ethernet/marvell/mvneta.c=3755=static void mvneta_start_dev(struct mvneta_port *pp)\n--\ndrivers/net/ethernet/marvell/mvneta.c-3787-\ndrivers/net/ethernet/marvell/mvneta.c:3788:\t/* We may have called phylink_speed_down before */\ndrivers/net/ethernet/marvell/mvneta.c-3789-\tphylink_speed_up(pp-\u003ephylink);\n--\ndrivers/net/ethernet/marvell/mvneta.c=3796=static void mvneta_stop_dev(struct mvneta_port *pp)\n--\ndrivers/net/ethernet/marvell/mvneta.c-3802-\tif (device_may_wakeup(\u0026pp-\u003edev-\u003edev))\ndrivers/net/ethernet/marvell/mvneta.c:3803:\t\tphylink_speed_down(pp-\u003ephylink, false);\ndrivers/net/ethernet/marvell/mvneta.c-3804-\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c=4363=static int stmmac_open(struct net_device *dev)\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-4401-\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c:4402:\t/* We may have called phylink_speed_down before */\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-4403-\tphylink_speed_up(priv-\u003ephylink);\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c=4486=static int stmmac_release(struct net_device *dev)\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-4503-\tif (device_may_wakeup(priv-\u003edevice))\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c:4504:\t\tphylink_speed_down(priv-\u003ephylink, false);\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-4505-\n--\ndrivers/net/phy/phylink.c=2665=void phylink_suspend(struct phylink *pl, bool mac_wol)\n--\ndrivers/net/phy/phylink.c-2698-\tif (phylink_phy_pm_speed_ctrl(pl)) {\ndrivers/net/phy/phylink.c:2699:\t\tphylink_speed_down(pl, false);\ndrivers/net/phy/phylink.c-2700-\t\tpl-\u003esuspend_speed_down = true;\n--\ndrivers/net/phy/phylink.c=3638=EXPORT_SYMBOL_GPL(phylink_mii_ioctl);\n--\ndrivers/net/phy/phylink.c-3640-/**\ndrivers/net/phy/phylink.c:3641: * phylink_speed_down() - set the non-SFP PHY to lowest speed supported by both\ndrivers/net/phy/phylink.c-3642- * link partners\n--\ndrivers/net/phy/phylink.c-3651- */\ndrivers/net/phy/phylink.c:3652:int phylink_speed_down(struct phylink *pl, bool sync)\ndrivers/net/phy/phylink.c-3653-{\n--\ndrivers/net/phy/phylink.c-3668-}\ndrivers/net/phy/phylink.c:3669:EXPORT_SYMBOL_GPL(phylink_speed_down);\ndrivers/net/phy/phylink.c-3670-\n--\ndrivers/net/phy/phylink.c-3672- * phylink_speed_up() - restore the advertised speeds prior to the call to\ndrivers/net/phy/phylink.c:3673: * phylink_speed_down()\ndrivers/net/phy/phylink.c-3674- * @pl: a pointer to a \u0026struct phylink returned from phylink_create()\n--\ninclude/linux/phylink.h=768=int phylink_mii_ioctl(struct phylink *, struct ifreq *, int);\ninclude/linux/phylink.h:769:int phylink_speed_down(struct phylink *pl, bool sync);\ninclude/linux/phylink.h-770-int phylink_speed_up(struct phylink *pl);\n"
} |
| 17/2 | 2026/09/26 22:51 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 61123 output: 39 thoughts: 43 Thoughts: Here's my summary, as though I'm thinking through this process: **Diving Deeper into the Code Change** Okay, before I make any definitive judgments, I need to understand this patch thoroughly. My first instinct is to dig into the commit history. Let's see... I'll pull up the `git log` for that specific commit. I need to understand the *why* behind this change, not just the *what*. That means I'm looking for the commit message itself, of course. But sometimes, especially with a squashed commit, I might need to trace back to the individual commits that were folded into this one to get the complete picture. The key is understanding the problem this patch solves and the rationale driving the solution. Ultimately, I need to grasp exactly *what* the patch does, what lines of code are changed, and how it impacts the existing codebase. That's the only way to effectively assess its quality and potential implications. |
| 18/2 | 2026/09/26 22:51 | tool | grepper |
0mArgs:{
"Expression": "reconfigure_mtu",
"PathPrefix": "drivers/net/ethernet/stmicro/stmmac/"
}Results: {
"Output": "drivers/net/ethernet/stmicro/stmmac/stmmac_main.c=6334=static void stmmac_set_rx_mode(struct net_device *dev)\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-6340-\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c:6341:static int stmmac_reconfigure_mtu(struct net_device *dev, int mtu)\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-6342-{\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c=6459=static int stmmac_change_mtu(struct net_device *dev, int new_mtu)\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-6487-\t (dev-\u003emtu \u003e ETH_DATA_LEN || mtu \u003e ETH_DATA_LEN)) {\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c:6488:\t\tret = stmmac_reconfigure_mtu(dev, mtu);\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-6489-\t\tif (ret)\n"
} |
| 19/2 | 2026/09/26 22:51 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 61759 output: 42 thoughts: 789 Thoughts: **Analyzing a Recent Commit for Fuzzing Potential** Okay, let's dive into this commit. My initial instinct is to check the commit log, ideally with `git log -1 HEAD`, to get some context. However, it seems I don't have that tool available directly, so I'll have to read the changes themselves. Ah, the commit message provides some clue: "syz-cluster: applied patch under review". Good, that gives a starting point. Now, let's dissect the diff. Immediately, I see modifications to `stmmac` which is interesting. The `priv->dma_conf` is now a pointer, and there are some new additions: an enum `stmmac_datapath_state`, a `hw_suspended` boolean, and several new functions like `stmmac_reconfigure_mtu`, `stmmac_resume_hw`, etc. I also spot changes in MSI IRQ handlers and adjustments within `drivers/net/phy/phylink.c` related to suspend, resume, and speed handling. It looks like some power management or potentially hotplug functionality is being enhanced. But the question is, *can* this be fuzzed? That's what I'm interested in! First, what is `stmmac`? Can its hardware be fuzzed? Is it something we can even *reach* with our fuzzing setup? I need to check the "negative criteria" section of my instructions. The goal is to avoid wasting time on stuff that is impossible to fuzz. Let's see... the instructions say to return `WorthFuzzing=false` if it falls into a few categories: non-kernel and non-executable changes, structurally unreachable hardware, or unreachable execution paths. I see "Structurally unreachable hardware" as the key here. It lists examples like vendor-specific PCIe switches, and GPU drivers. So, let's see if `stmmac` fits this negative category. I know it's a MAC, but is it something that *requires* specialized hardware that can't be emulated in standard QEMU? I need more info: what hardware uses `stmmac`? It looks like it is STMicroelectronics Synopsys DesignWare Ethernet MAC. Okay, that helps. Now, where does this thing run? I need to look into `drivers/net/ethernet/stmicro/stmmac/Kconfig` to see the dependencies. This should give me a better understanding of the platform it runs on. From there, I'll be able to decide if we can use this in our fuzzing. |
| 20/2 | 2026/09/26 22:51 | tool | read-file |
0mArgs:{
"File": "drivers/net/ethernet/stmicro/stmmac/Kconfig",
"FirstLine": 1,
"LineCount": 80
}Results: {
"Contents": " 1:\t# SPDX-License-Identifier: GPL-2.0-only\n 2:\tconfig STMMAC_ETH\n 3:\t\ttristate \"STMicroelectronics Multi-Gigabit Ethernet driver\"\n 4:\t\tdepends on HAS_IOMEM \u0026\u0026 HAS_DMA\n 5:\t\tdepends on PTP_1588_CLOCK_OPTIONAL\n 6:\t\tdepends on ETHTOOL_NETLINK\n 7:\t\tselect MII\n 8:\t\tselect PCS_XPCS\n 9:\t\tselect PAGE_POOL\n 10:\t\tselect PHYLINK\n 11:\t\tselect CRC32\n 12:\t\tselect RESET_CONTROLLER\n 13:\t\tselect NET_DEVLINK\n 14:\t\thelp\n 15:\t\t This is the driver for the Ethernet IPs built around a\n 16:\t\t Synopsys IP Core.\n 17:\t\n 18:\tif STMMAC_ETH\n 19:\t\n 20:\tconfig STMMAC_SELFTESTS\n 21:\t\tbool \"Support for STMMAC Selftests\"\n 22:\t\tdepends on INET\n 23:\t\tdefault n\n 24:\t\thelp\n 25:\t\t This adds support for STMMAC Selftests using ethtool. Enable this\n 26:\t\t feature if you are facing problems with your HW and submit the test\n 27:\t\t results to the netdev Mailing List.\n 28:\t\n 29:\tconfig STMMAC_PLATFORM\n 30:\t\ttristate \"STMMAC Platform bus support\"\n 31:\t\tselect MFD_SYSCON\n 32:\t\tdefault y\n 33:\t\thelp\n 34:\t\t This selects the platform specific bus support for the stmmac driver.\n 35:\t\t This is the driver used on several SoCs:\n 36:\t\t STi, Allwinner, Amlogic Meson, Altera SOCFPGA.\n 37:\t\n 38:\t\t If you have a controller with this interface, say Y or M here.\n 39:\t\n 40:\t\t If unsure, say N.\n 41:\t\n 42:\tif STMMAC_PLATFORM\n 43:\t\n 44:\tconfig DWMAC_DWC_QOS_ETH\n 45:\t\ttristate \"Support for snps,dwc-qos-ethernet.txt DT binding.\"\n 46:\t\tselect CRC32\n 47:\t\tselect MII\n 48:\t\tdepends on OF \u0026\u0026 HAS_DMA\n 49:\t\thelp\n 50:\t\t Support for chips using the snps,dwc-qos-ethernet.txt DT binding.\n 51:\t\n 52:\tconfig DWMAC_GENERIC\n 53:\t\ttristate \"Generic driver for DWMAC\"\n 54:\t\tdefault STMMAC_PLATFORM\n 55:\t\thelp\n 56:\t\t Generic DWMAC driver for platforms that don't require any\n 57:\t\t platform specific code to function or is using platform\n 58:\t\t data for setup.\n 59:\t\n 60:\tconfig DWMAC_ANARION\n 61:\t\ttristate \"Adaptrum Anarion GMAC support\"\n 62:\t\tdefault ARC\n 63:\t\tdepends on OF \u0026\u0026 (ARC || COMPILE_TEST)\n 64:\t\thelp\n 65:\t\t Support for Adaptrum Anarion GMAC Ethernet controller.\n 66:\t\n 67:\t\t This selects the Anarion SoC glue layer support for the stmmac driver.\n 68:\t\n 69:\tconfig DWMAC_EIC7700\n 70:\t\ttristate \"Support for Eswin eic7700 ethernet driver\"\n 71:\t\tdepends on OF \u0026\u0026 HAS_DMA \u0026\u0026 ARCH_ESWIN || COMPILE_TEST\n 72:\t\thelp\n 73:\t\t This driver supports the Eswin EIC7700 Ethernet controller,\n 74:\t\t which integrates Synopsys DesignWare QoS features. It enables\n 75:\t\t high-speed networking with DMA acceleration and is optimized\n 76:\t\t for embedded systems.\n 77:\t\n 78:\tconfig DWMAC_INGENIC\n 79:\t\ttristate \"Ingenic MAC support\"\n 80:\t\tdefault MACH_INGENIC\n"
} |
| 21/2 | 2026/09/26 22:51 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 64230 output: 44 thoughts: 13 Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience: **Diving Back into the stmmac Kconfig** Alright, so we're circling back to the `stmmac` Kconfig, which means it's time to thoroughly vet it. I'm thinking, "Let's make absolutely sure we haven't overlooked any crucial configurations or dependencies." We've probably been looking at the stmmac driver in isolation. Now, the context is the big picture. Let's make sure that we haven't broken any dependencies. We have to make sure that the configuration is complete. We need to cross-reference our changes against the entire configuration file. I need to refresh my understanding of the interactions. I'm mentally running through the various options and their implications, especially concerning any new features or optimizations we've been working on. This isn't just a cursory glance; it's a deep dive. Time to ensure we haven't inadvertently introduced any regressions or conflicts. |
| 22/2 | 2026/09/26 22:51 | tool | read-file |
0mArgs:{
"File": "drivers/net/ethernet/stmicro/stmmac/Kconfig",
"FirstLine": 200,
"LineCount": 80
}Results: {
"Contents": " 200:\t\t SOCs GMAC ethernet controller, ie. S32G2xx, S32G3xx and\n 201:\t\t S32R45.\n 202:\t\n 203:\tconfig DWMAC_SOCFPGA\n 204:\t\ttristate \"SOCFPGA dwmac support\"\n 205:\t\tdefault ARCH_INTEL_SOCFPGA\n 206:\t\tdepends on OF \u0026\u0026 (ARCH_INTEL_SOCFPGA || COMPILE_TEST)\n 207:\t\tselect MFD_SYSCON\n 208:\t\tselect MDIO_REGMAP\n 209:\t\tselect REGMAP_MMIO\n 210:\t\tselect PCS_LYNX\n 211:\t\thelp\n 212:\t\t Support for ethernet controller on Altera SOCFPGA\n 213:\t\n 214:\t\t This selects the Altera SOCFPGA SoC glue layer support\n 215:\t\t for the stmmac device driver. This driver is used for\n 216:\t\t arria5 and cyclone5 FPGA SoCs.\n 217:\t\n 218:\tconfig DWMAC_SOPHGO\n 219:\t\ttristate \"Sophgo dwmac support\"\n 220:\t\tdepends on OF \u0026\u0026 (ARCH_SOPHGO || COMPILE_TEST)\n 221:\t\tdefault m if ARCH_SOPHGO\n 222:\t\thelp\n 223:\t\t Support for ethernet controllers on Sophgo RISC-V SoCs\n 224:\t\n 225:\t\t This selects the Sophgo SoC specific glue layer support\n 226:\t\t for the stmmac device driver. This driver is used for the\n 227:\t\t ethernet controllers on various Sophgo SoCs.\n 228:\t\n 229:\tconfig DWMAC_SPACEMIT\n 230:\t\ttristate \"Spacemit dwmac support\"\n 231:\t\tdepends on OF \u0026\u0026 (ARCH_SPACEMIT || COMPILE_TEST)\n 232:\t\tselect MFD_SYSCON\n 233:\t\tdefault m if ARCH_SPACEMIT\n 234:\t\thelp\n 235:\t\t Support for ethernet controllers on Spacemit RISC-V SoCs\n 236:\t\n 237:\t\t This selects the Spacemit platform specific glue layer support\n 238:\t\t for the stmmac device driver. This driver is used for the\n 239:\t\t Spacemit K3 ethernet controllers.\n 240:\t\n 241:\tconfig DWMAC_STARFIVE\n 242:\t\ttristate \"StarFive dwmac support\"\n 243:\t\tdepends on OF \u0026\u0026 (ARCH_STARFIVE || COMPILE_TEST)\n 244:\t\tselect MFD_SYSCON\n 245:\t\tdefault m if ARCH_STARFIVE\n 246:\t\thelp\n 247:\t\t Support for ethernet controllers on StarFive RISC-V SoCs\n 248:\t\n 249:\t\t This selects the StarFive platform specific glue layer support\n 250:\t\t for the stmmac device driver. This driver is used for the\n 251:\t\t StarFive JH7100 and JH7110 ethernet controllers.\n 252:\t\n 253:\tconfig DWMAC_STI\n 254:\t\ttristate \"STi GMAC support\"\n 255:\t\tdefault ARCH_STI\n 256:\t\tdepends on OF \u0026\u0026 (ARCH_STI || COMPILE_TEST)\n 257:\t\tselect MFD_SYSCON\n 258:\t\thelp\n 259:\t\t Support for ethernet controller on STi SOCs.\n 260:\t\n 261:\t\t This selects STi SoC glue layer support for the stmmac\n 262:\t\t device driver. This driver is used on for the STi series\n 263:\t\t SOCs GMAC ethernet controller.\n 264:\t\n 265:\tconfig DWMAC_STM32\n 266:\t\ttristate \"STM32 DWMAC support\"\n 267:\t\tdefault ARCH_STM32\n 268:\t\tdepends on OF \u0026\u0026 HAS_IOMEM \u0026\u0026 (ARCH_STM32 || COMPILE_TEST)\n 269:\t\tselect MFD_SYSCON\n 270:\t\thelp\n 271:\t\t Support for ethernet controller on STM32 SOCs.\n 272:\t\n 273:\t\t This selects STM32 SoC glue layer support for the stmmac\n 274:\t\t device driver. This driver is used on for the STM32 series\n 275:\t\t SOCs GMAC ethernet controller.\n 276:\t\n 277:\tconfig DWMAC_SUNXI\n 278:\t\ttristate \"Allwinner GMAC support\"\n 279:\t\tdefault ARCH_SUNXI\n"
} |
| 23/2 | 2026/09/26 22:51 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 65744 output: 40 thoughts: 11 Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience, and delivered as a first-person summary: **Digging into `STMMAC_PCI`** Right, let's dive into this `STMMAC_PCI` topic. My initial thought process is to understand the context. I'm assuming we're dealing with an STMicroelectronics Gigabit Ethernet MAC connected via a PCI bus, but specifics are always key. I need to quickly determine the specific aspect or problem that's driving this investigation. Am I troubleshooting an issue, optimizing performance, or perhaps integrating this MAC into a new system? The initial step is always understanding the scope. I need to quickly recall my knowledge of this driver and architecture. My immediate questions focus on the configuration and interaction with the PCI bus itself. I'm thinking of interrupt handling, DMA setup, and potential shared memory regions. Are there known performance bottlenecks or compatibility quirks associated with this particular combination of STMMAC and PCI? I'm anticipating the need to check specific registers, driver code sections, and possibly hardware documentation to establish a clear picture. I'm mentally formulating a plan to gather data, perhaps a quick `lspci` check, then a look at the driver source and config files. The goal is to rapidly form a hypothesis based on an initial triage, and then hone in on the core of the matter. |
| 24/2 | 2026/09/26 22:51 | tool | grepper |
0mArgs:{
"Expression": "config STMMAC_PCI",
"PathPrefix": "drivers/net/ethernet/stmicro/stmmac/"
}Results: {
"Output": "drivers/net/ethernet/stmicro/stmmac/Kconfig=398=config DWMAC_MOTORCOMM\n--\ndrivers/net/ethernet/stmicro/stmmac/Kconfig-406-\ndrivers/net/ethernet/stmicro/stmmac/Kconfig:407:config STMMAC_PCI\ndrivers/net/ethernet/stmicro/stmmac/Kconfig-408-\ttristate \"STMMAC PCI bus support\"\n"
} |
| 25/2 | 2026/09/26 22:51 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 66208 output: 44 thoughts: 11 Thoughts: Okay, I'm ready. Let's do this. I'll summarize the text as if the thoughts described are my own, starting with the specified line number. I'll assume I'm an expert in the field. **Deep Dive at Line 407** Alright, let's zero in on line 407. Based on the context, I'm expecting a specific argument or data point to be presented here. I'm mentally prepared to dissect it, looking for the underlying assumptions, methodologies employed, and potential weaknesses. My immediate reaction, as I read it, is to assess its relevance to the broader argument and my existing understanding. Is it a confirmation, a contradiction, or something entirely new? What are the implications if this point holds true? If it's a critical piece of evidence, I'm on high alert for corroborating sources. If it seems shaky, I'll be looking for potential biases or methodological flaws that I can bring to light. My focus will be on the nuances and any inconsistencies. Is it overgeneralized? Under-specified? Does the author adequately address counterarguments, and if so, how convincingly? I'm already formulating questions in my mind, such as "How does this fit within the larger context, what are other works that would refute or reinforce this?" Once I understand it completely, I'll be evaluating its significance and making a judgment on its validity and impact. |
| 26/2 | 2026/09/26 22:51 | tool | read-file |
0mArgs:{
"File": "drivers/net/ethernet/stmicro/stmmac/Kconfig",
"FirstLine": 400,
"LineCount": 40
}Results: {
"Contents": " 400:\t\tdepends on PCI\n 401:\t\tselect MOTORCOMM_PHY\n 402:\t\tselect STMMAC_LIBPCI\n 403:\t\thelp\n 404:\t\t This enables glue driver for Motorcomm DWMAC-based PCI Ethernet\n 405:\t\t controllers. Currently only YT6801 is supported.\n 406:\t\n 407:\tconfig STMMAC_PCI\n 408:\t\ttristate \"STMMAC PCI bus support\"\n 409:\t\tdepends on PCI\n 410:\t\tdepends on COMMON_CLK\n 411:\t\tselect STMMAC_LIBPCI\n 412:\t\thelp\n 413:\t\t This selects the platform specific bus support for the stmmac driver.\n 414:\t\t This driver was tested on XLINX XC2V3000 FF1152AMT0221\n 415:\t\t D1215994A VIRTEX FPGA board and SNPS QoS IPK Prototyping Kit.\n 416:\t\n 417:\t\t If you have a controller with this interface, say Y or M here.\n 418:\t\n 419:\t\t If unsure, say N.\n 420:\tendif # STMMAC_ETH\n"
} |
| 27/2 | 2026/09/26 22:51 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 66896 output: 18 thoughts: 1244 Thoughts: **Analyzing Fuzzing Scope for PHY-Link Layer** Okay, let's break this down systematically. The criteria for *not* fuzzing this code is pretty specific: it has to be structurally unreachable hardware, things like vendor-specific PCIe switches, SmartNICs, or GPU drivers that require specific hardware not emulated in standard QEMU. Right, so is the code path I'm looking at even *reachable*? Let's start with `drivers/net/phy/phylink.c`. Clearly, this is the PHY-link core, and it's *not* a vendor-specific piece of hardware. Therefore, the question is what was modified in `phylink.c` and is it reachable? I see modifications to `phylink_stop`, `phylink_restore_suspend_speed`, `phylink_suspend`, `phylink_resume`, and `phylink_speed_down`. Now, can syzkaller even touch those functions? We need to consider how syzkaller interacts with the network stack. Does syzkaller target USB network devices like `ax88772` (asix) or `lan78xx`? I need to check syzkaller's support for USB networking. Okay, I recall syzkaller using `/dev/raw-gadget` and `/dev/gadget` emulation, or `syz_usb_connect`. So, when a USB device with the appropriate VID/PID connects, the kernel probes and binds `asix` or `lan78xx`. Then the network interface gets registered. After that, userland (syzkaller) can do things like `ioctl(SIOCSIFFLAGS)` to bring the device up and down – essentially triggering `dev_open` and `dev_close`. Now, the crucial part: when `dev_close()` is called on that interface, the `ax88772_stop()` (or `lan78xx_stop()`) function is invoked. And *that* calls `phylink_stop(priv->phylink)`. That's the entry point! So the code in `phylink_stop()` *is* reachable. What about DSA? Is it possible to create DSA devices in userspace? I seem to remember `drivers/net/dsa/dsa_loop.c`. Let me check that quickly, yep, it's a mock DSA driver, perfect! So, to summarize: The changed code path is reachable through `ax88772_stop()` or `lan78xx_stop()` when the syzkaller fuzzer closes the network interface, therefore, it should be fuzzed. |
| 28/2 | 2026/09/26 22:51 | tool | grepper |
0mArgs:{
"Expression": "dsa_loop"
}Results: {
"Output": "Documentation/networking/dsa/dsa.rst=159=all attached switch ports must be down while doing this).\nDocumentation/networking/dsa/dsa.rst-160-\nDocumentation/networking/dsa/dsa.rst:161:It is desirable that all tagging protocols are testable with the ``dsa_loop``\nDocumentation/networking/dsa/dsa.rst-162-mockup driver, which can be attached to any network interface. The goal is that\n--\ndrivers/net/dsa/Makefile=3=bcm-sf2-objs\t\t\t:= bcm_sf2.o bcm_sf2_cfp.o\ndrivers/net/dsa/Makefile:4:obj-$(CONFIG_NET_DSA_LOOP)\t+= dsa_loop.o\ndrivers/net/dsa/Makefile-5-obj-$(CONFIG_NET_DSA_KS8995) \t+= ks8995.o\n--\ndrivers/net/dsa/dsa_loop.c-24-\ndrivers/net/dsa/dsa_loop.c:25:struct dsa_loop_vlan {\ndrivers/net/dsa/dsa_loop.c-26-\tu16 members;\n--\ndrivers/net/dsa/dsa_loop.c-29-\ndrivers/net/dsa/dsa_loop.c:30:struct dsa_loop_mib_entry {\ndrivers/net/dsa/dsa_loop.c-31-\tchar name[ETH_GSTRING_LEN];\n--\ndrivers/net/dsa/dsa_loop.c-34-\ndrivers/net/dsa/dsa_loop.c:35:enum dsa_loop_mib_counters {\ndrivers/net/dsa/dsa_loop.c-36-\tDSA_LOOP_PHY_READ_OK,\n--\ndrivers/net/dsa/dsa_loop.c-42-\ndrivers/net/dsa/dsa_loop.c:43:struct dsa_loop_port {\ndrivers/net/dsa/dsa_loop.c:44:\tstruct dsa_loop_mib_entry mib[__DSA_LOOP_CNT_MAX];\ndrivers/net/dsa/dsa_loop.c-45-\tu16 pvid;\n--\ndrivers/net/dsa/dsa_loop.c-48-\ndrivers/net/dsa/dsa_loop.c:49:struct dsa_loop_priv {\ndrivers/net/dsa/dsa_loop.c-50-\tstruct mii_bus\t*bus;\ndrivers/net/dsa/dsa_loop.c-51-\tunsigned int\tport_base;\ndrivers/net/dsa/dsa_loop.c:52:\tstruct dsa_loop_vlan vlans[VLAN_N_VID];\ndrivers/net/dsa/dsa_loop.c-53-\tstruct net_device *netdev;\ndrivers/net/dsa/dsa_loop.c:54:\tstruct dsa_loop_port ports[DSA_MAX_PORTS];\ndrivers/net/dsa/dsa_loop.c-55-};\ndrivers/net/dsa/dsa_loop.c-56-\ndrivers/net/dsa/dsa_loop.c:57:struct dsa_loop_pdata {\ndrivers/net/dsa/dsa_loop.c-58-\t/* Must be first, such that dsa_register_switch() can access this\n--\ndrivers/net/dsa/dsa_loop.c-66-\ndrivers/net/dsa/dsa_loop.c:67:static struct dsa_loop_mib_entry dsa_loop_mibs[] = {\ndrivers/net/dsa/dsa_loop.c-68-\t[DSA_LOOP_PHY_READ_OK]\t= { \"phy_read_ok\", },\n--\ndrivers/net/dsa/dsa_loop.c=75=static struct mdio_device *switch_mdiodev;\ndrivers/net/dsa/dsa_loop.c-76-\ndrivers/net/dsa/dsa_loop.c:77:enum dsa_loop_devlink_resource_id {\ndrivers/net/dsa/dsa_loop.c-78-\tDSA_LOOP_DEVLINK_PARAM_ID_NONE, /* DEVLINK_RESOURCE_ID_PARENT_TOP */\n--\ndrivers/net/dsa/dsa_loop.c-81-\ndrivers/net/dsa/dsa_loop.c:82:static u64 dsa_loop_devlink_vtu_get(void *priv)\ndrivers/net/dsa/dsa_loop.c-83-{\ndrivers/net/dsa/dsa_loop.c:84:\tstruct dsa_loop_priv *ps = priv;\ndrivers/net/dsa/dsa_loop.c-85-\tunsigned int i, count = 0;\ndrivers/net/dsa/dsa_loop.c:86:\tstruct dsa_loop_vlan *vl;\ndrivers/net/dsa/dsa_loop.c-87-\n--\ndrivers/net/dsa/dsa_loop.c-96-\ndrivers/net/dsa/dsa_loop.c:97:static int dsa_loop_setup_devlink_resources(struct dsa_switch *ds)\ndrivers/net/dsa/dsa_loop.c-98-{\ndrivers/net/dsa/dsa_loop.c-99-\tstruct devlink_resource_size_params size_params;\ndrivers/net/dsa/dsa_loop.c:100:\tstruct dsa_loop_priv *ps = ds-\u003epriv;\ndrivers/net/dsa/dsa_loop.c-101-\tint err;\n--\ndrivers/net/dsa/dsa_loop.c-115-\t\t\t\t\t DSA_LOOP_DEVLINK_PARAM_ID_VTU,\ndrivers/net/dsa/dsa_loop.c:116:\t\t\t\t\t dsa_loop_devlink_vtu_get, ps);\ndrivers/net/dsa/dsa_loop.c-117-\n--\ndrivers/net/dsa/dsa_loop.c-124-\ndrivers/net/dsa/dsa_loop.c:125:static enum dsa_tag_protocol dsa_loop_get_protocol(struct dsa_switch *ds,\ndrivers/net/dsa/dsa_loop.c-126-\t\t\t\t\t\t int port,\n--\ndrivers/net/dsa/dsa_loop.c-133-\ndrivers/net/dsa/dsa_loop.c:134:static int dsa_loop_setup(struct dsa_switch *ds)\ndrivers/net/dsa/dsa_loop.c-135-{\ndrivers/net/dsa/dsa_loop.c:136:\tstruct dsa_loop_priv *ps = ds-\u003epriv;\ndrivers/net/dsa/dsa_loop.c-137-\tunsigned int i;\n--\ndrivers/net/dsa/dsa_loop.c-139-\tfor (i = 0; i \u003c ds-\u003enum_ports; i++)\ndrivers/net/dsa/dsa_loop.c:140:\t\tmemcpy(ps-\u003eports[i].mib, dsa_loop_mibs,\ndrivers/net/dsa/dsa_loop.c:141:\t\t sizeof(dsa_loop_mibs));\ndrivers/net/dsa/dsa_loop.c-142-\n--\ndrivers/net/dsa/dsa_loop.c-144-\ndrivers/net/dsa/dsa_loop.c:145:\treturn dsa_loop_setup_devlink_resources(ds);\ndrivers/net/dsa/dsa_loop.c-146-}\ndrivers/net/dsa/dsa_loop.c-147-\ndrivers/net/dsa/dsa_loop.c:148:static void dsa_loop_teardown(struct dsa_switch *ds)\ndrivers/net/dsa/dsa_loop.c-149-{\n--\ndrivers/net/dsa/dsa_loop.c-152-\ndrivers/net/dsa/dsa_loop.c:153:static int dsa_loop_get_sset_count(struct dsa_switch *ds, int port, int sset)\ndrivers/net/dsa/dsa_loop.c-154-{\n--\ndrivers/net/dsa/dsa_loop.c-160-\ndrivers/net/dsa/dsa_loop.c:161:static void dsa_loop_get_strings(struct dsa_switch *ds, int port,\ndrivers/net/dsa/dsa_loop.c-162-\t\t\t\t u32 stringset, uint8_t *data)\ndrivers/net/dsa/dsa_loop.c-163-{\ndrivers/net/dsa/dsa_loop.c:164:\tstruct dsa_loop_priv *ps = ds-\u003epriv;\ndrivers/net/dsa/dsa_loop.c-165-\tunsigned int i;\n--\ndrivers/net/dsa/dsa_loop.c-173-\ndrivers/net/dsa/dsa_loop.c:174:static void dsa_loop_get_ethtool_stats(struct dsa_switch *ds, int port,\ndrivers/net/dsa/dsa_loop.c-175-\t\t\t\t uint64_t *data)\ndrivers/net/dsa/dsa_loop.c-176-{\ndrivers/net/dsa/dsa_loop.c:177:\tstruct dsa_loop_priv *ps = ds-\u003epriv;\ndrivers/net/dsa/dsa_loop.c-178-\tunsigned int i;\n--\ndrivers/net/dsa/dsa_loop.c-183-\ndrivers/net/dsa/dsa_loop.c:184:static int dsa_loop_phy_read(struct dsa_switch *ds, int port, int regnum)\ndrivers/net/dsa/dsa_loop.c-185-{\ndrivers/net/dsa/dsa_loop.c:186:\tstruct dsa_loop_priv *ps = ds-\u003epriv;\ndrivers/net/dsa/dsa_loop.c-187-\tstruct mii_bus *bus = ps-\u003ebus;\n--\ndrivers/net/dsa/dsa_loop.c-198-\ndrivers/net/dsa/dsa_loop.c:199:static int dsa_loop_phy_write(struct dsa_switch *ds, int port,\ndrivers/net/dsa/dsa_loop.c-200-\t\t\t int regnum, u16 value)\ndrivers/net/dsa/dsa_loop.c-201-{\ndrivers/net/dsa/dsa_loop.c:202:\tstruct dsa_loop_priv *ps = ds-\u003epriv;\ndrivers/net/dsa/dsa_loop.c-203-\tstruct mii_bus *bus = ps-\u003ebus;\n--\ndrivers/net/dsa/dsa_loop.c-214-\ndrivers/net/dsa/dsa_loop.c:215:static int dsa_loop_port_bridge_join(struct dsa_switch *ds, int port,\ndrivers/net/dsa/dsa_loop.c-216-\t\t\t\t struct dsa_bridge bridge,\n--\ndrivers/net/dsa/dsa_loop.c-225-\ndrivers/net/dsa/dsa_loop.c:226:static void dsa_loop_port_bridge_leave(struct dsa_switch *ds, int port,\ndrivers/net/dsa/dsa_loop.c-227-\t\t\t\t struct dsa_bridge bridge)\n--\ndrivers/net/dsa/dsa_loop.c-232-\ndrivers/net/dsa/dsa_loop.c:233:static void dsa_loop_port_stp_state_set(struct dsa_switch *ds, int port,\ndrivers/net/dsa/dsa_loop.c-234-\t\t\t\t\tu8 state)\n--\ndrivers/net/dsa/dsa_loop.c-239-\ndrivers/net/dsa/dsa_loop.c:240:static int dsa_loop_port_vlan_filtering(struct dsa_switch *ds, int port,\ndrivers/net/dsa/dsa_loop.c-241-\t\t\t\t\tbool vlan_filtering,\n--\ndrivers/net/dsa/dsa_loop.c-249-\ndrivers/net/dsa/dsa_loop.c:250:static int dsa_loop_port_vlan_add(struct dsa_switch *ds, int port,\ndrivers/net/dsa/dsa_loop.c-251-\t\t\t\t const struct switchdev_obj_port_vlan *vlan,\n--\ndrivers/net/dsa/dsa_loop.c-255-\tbool pvid = vlan-\u003eflags \u0026 BRIDGE_VLAN_INFO_PVID;\ndrivers/net/dsa/dsa_loop.c:256:\tstruct dsa_loop_priv *ps = ds-\u003epriv;\ndrivers/net/dsa/dsa_loop.c-257-\tstruct mii_bus *bus = ps-\u003ebus;\ndrivers/net/dsa/dsa_loop.c:258:\tstruct dsa_loop_vlan *vl;\ndrivers/net/dsa/dsa_loop.c-259-\n--\ndrivers/net/dsa/dsa_loop.c-282-\ndrivers/net/dsa/dsa_loop.c:283:static int dsa_loop_port_vlan_del(struct dsa_switch *ds, int port,\ndrivers/net/dsa/dsa_loop.c-284-\t\t\t\t const struct switchdev_obj_port_vlan *vlan)\n--\ndrivers/net/dsa/dsa_loop.c-286-\tbool untagged = vlan-\u003eflags \u0026 BRIDGE_VLAN_INFO_UNTAGGED;\ndrivers/net/dsa/dsa_loop.c:287:\tstruct dsa_loop_priv *ps = ds-\u003epriv;\ndrivers/net/dsa/dsa_loop.c-288-\tu16 pvid = ps-\u003eports[port].pvid;\ndrivers/net/dsa/dsa_loop.c-289-\tstruct mii_bus *bus = ps-\u003ebus;\ndrivers/net/dsa/dsa_loop.c:290:\tstruct dsa_loop_vlan *vl;\ndrivers/net/dsa/dsa_loop.c-291-\n--\ndrivers/net/dsa/dsa_loop.c-310-\ndrivers/net/dsa/dsa_loop.c:311:static int dsa_loop_port_change_mtu(struct dsa_switch *ds, int port,\ndrivers/net/dsa/dsa_loop.c-312-\t\t\t\t int new_mtu)\ndrivers/net/dsa/dsa_loop.c-313-{\ndrivers/net/dsa/dsa_loop.c:314:\tstruct dsa_loop_priv *priv = ds-\u003epriv;\ndrivers/net/dsa/dsa_loop.c-315-\n--\ndrivers/net/dsa/dsa_loop.c-320-\ndrivers/net/dsa/dsa_loop.c:321:static int dsa_loop_port_max_mtu(struct dsa_switch *ds, int port)\ndrivers/net/dsa/dsa_loop.c-322-{\n--\ndrivers/net/dsa/dsa_loop.c-325-\ndrivers/net/dsa/dsa_loop.c:326:static void dsa_loop_phylink_get_caps(struct dsa_switch *dsa, int port,\ndrivers/net/dsa/dsa_loop.c-327-\t\t\t\t struct phylink_config *config)\n--\ndrivers/net/dsa/dsa_loop.c-333-\ndrivers/net/dsa/dsa_loop.c:334:static const struct dsa_switch_ops dsa_loop_driver = {\ndrivers/net/dsa/dsa_loop.c:335:\t.get_tag_protocol\t= dsa_loop_get_protocol,\ndrivers/net/dsa/dsa_loop.c:336:\t.setup\t\t\t= dsa_loop_setup,\ndrivers/net/dsa/dsa_loop.c:337:\t.teardown\t\t= dsa_loop_teardown,\ndrivers/net/dsa/dsa_loop.c:338:\t.get_strings\t\t= dsa_loop_get_strings,\ndrivers/net/dsa/dsa_loop.c:339:\t.get_ethtool_stats\t= dsa_loop_get_ethtool_stats,\ndrivers/net/dsa/dsa_loop.c:340:\t.get_sset_count\t\t= dsa_loop_get_sset_count,\ndrivers/net/dsa/dsa_loop.c:341:\t.get_ethtool_phy_stats\t= dsa_loop_get_ethtool_stats,\ndrivers/net/dsa/dsa_loop.c:342:\t.phy_read\t\t= dsa_loop_phy_read,\ndrivers/net/dsa/dsa_loop.c:343:\t.phy_write\t\t= dsa_loop_phy_write,\ndrivers/net/dsa/dsa_loop.c:344:\t.port_bridge_join\t= dsa_loop_port_bridge_join,\ndrivers/net/dsa/dsa_loop.c:345:\t.port_bridge_leave\t= dsa_loop_port_bridge_leave,\ndrivers/net/dsa/dsa_loop.c:346:\t.port_stp_state_set\t= dsa_loop_port_stp_state_set,\ndrivers/net/dsa/dsa_loop.c:347:\t.port_vlan_filtering\t= dsa_loop_port_vlan_filtering,\ndrivers/net/dsa/dsa_loop.c:348:\t.port_vlan_add\t\t= dsa_loop_port_vlan_add,\ndrivers/net/dsa/dsa_loop.c:349:\t.port_vlan_del\t\t= dsa_loop_port_vlan_del,\ndrivers/net/dsa/dsa_loop.c:350:\t.port_change_mtu\t= dsa_loop_port_change_mtu,\ndrivers/net/dsa/dsa_loop.c:351:\t.port_max_mtu\t\t= dsa_loop_port_max_mtu,\ndrivers/net/dsa/dsa_loop.c:352:\t.phylink_get_caps\t= dsa_loop_phylink_get_caps,\ndrivers/net/dsa/dsa_loop.c-353-};\ndrivers/net/dsa/dsa_loop.c-354-\ndrivers/net/dsa/dsa_loop.c:355:static int dsa_loop_drv_probe(struct mdio_device *mdiodev)\ndrivers/net/dsa/dsa_loop.c-356-{\ndrivers/net/dsa/dsa_loop.c:357:\tstruct dsa_loop_pdata *pdata = mdiodev-\u003edev.platform_data;\ndrivers/net/dsa/dsa_loop.c:358:\tstruct dsa_loop_priv *ps;\ndrivers/net/dsa/dsa_loop.c-359-\tstruct dsa_switch *ds;\n--\ndrivers/net/dsa/dsa_loop.c-382-\tds-\u003edev = \u0026mdiodev-\u003edev;\ndrivers/net/dsa/dsa_loop.c:383:\tds-\u003eops = \u0026dsa_loop_driver;\ndrivers/net/dsa/dsa_loop.c-384-\tds-\u003epriv = ps;\n--\ndrivers/net/dsa/dsa_loop.c-396-\ndrivers/net/dsa/dsa_loop.c:397:static void dsa_loop_drv_remove(struct mdio_device *mdiodev)\ndrivers/net/dsa/dsa_loop.c-398-{\ndrivers/net/dsa/dsa_loop.c-399-\tstruct dsa_switch *ds = dev_get_drvdata(\u0026mdiodev-\u003edev);\ndrivers/net/dsa/dsa_loop.c:400:\tstruct dsa_loop_priv *ps;\ndrivers/net/dsa/dsa_loop.c-401-\n--\ndrivers/net/dsa/dsa_loop.c-410-\ndrivers/net/dsa/dsa_loop.c:411:static void dsa_loop_drv_shutdown(struct mdio_device *mdiodev)\ndrivers/net/dsa/dsa_loop.c-412-{\n--\ndrivers/net/dsa/dsa_loop.c-422-\ndrivers/net/dsa/dsa_loop.c:423:static struct mdio_driver dsa_loop_drv = {\ndrivers/net/dsa/dsa_loop.c-424-\t.mdiodrv.driver\t= {\n--\ndrivers/net/dsa/dsa_loop.c-426-\t},\ndrivers/net/dsa/dsa_loop.c:427:\t.probe\t= dsa_loop_drv_probe,\ndrivers/net/dsa/dsa_loop.c:428:\t.remove\t= dsa_loop_drv_remove,\ndrivers/net/dsa/dsa_loop.c:429:\t.shutdown = dsa_loop_drv_shutdown,\ndrivers/net/dsa/dsa_loop.c-430-};\ndrivers/net/dsa/dsa_loop.c-431-\ndrivers/net/dsa/dsa_loop.c:432:static int dsa_loop_bus_match(struct device *dev,\ndrivers/net/dsa/dsa_loop.c-433-\t\t\t const struct device_driver *drv)\ndrivers/net/dsa/dsa_loop.c-434-{\ndrivers/net/dsa/dsa_loop.c:435:\treturn drv == \u0026dsa_loop_drv.mdiodrv.driver;\ndrivers/net/dsa/dsa_loop.c-436-}\ndrivers/net/dsa/dsa_loop.c-437-\ndrivers/net/dsa/dsa_loop.c:438:static void dsa_loop_phydevs_unregister(void)\ndrivers/net/dsa/dsa_loop.c-439-{\n--\ndrivers/net/dsa/dsa_loop.c-445-\ndrivers/net/dsa/dsa_loop.c:446:static int __init dsa_loop_create_switch_mdiodev(void)\ndrivers/net/dsa/dsa_loop.c-447-{\ndrivers/net/dsa/dsa_loop.c:448:\tstatic struct dsa_loop_pdata dsa_loop_pdata = {\ndrivers/net/dsa/dsa_loop.c-449-\t\t.cd = {\n--\ndrivers/net/dsa/dsa_loop.c-470-\ndrivers/net/dsa/dsa_loop.c:471:\tswitch_mdiodev-\u003ebus_match = dsa_loop_bus_match;\ndrivers/net/dsa/dsa_loop.c:472:\tswitch_mdiodev-\u003edev.platform_data = \u0026dsa_loop_pdata;\ndrivers/net/dsa/dsa_loop.c-473-\n--\ndrivers/net/dsa/dsa_loop.c-481-\ndrivers/net/dsa/dsa_loop.c:482:static int __init dsa_loop_init(void)\ndrivers/net/dsa/dsa_loop.c-483-{\n--\ndrivers/net/dsa/dsa_loop.c-486-\ndrivers/net/dsa/dsa_loop.c:487:\tret = dsa_loop_create_switch_mdiodev();\ndrivers/net/dsa/dsa_loop.c-488-\tif (ret)\n--\ndrivers/net/dsa/dsa_loop.c-493-\ndrivers/net/dsa/dsa_loop.c:494:\tret = mdio_driver_register(\u0026dsa_loop_drv);\ndrivers/net/dsa/dsa_loop.c-495-\tif (ret) {\ndrivers/net/dsa/dsa_loop.c:496:\t\tdsa_loop_phydevs_unregister();\ndrivers/net/dsa/dsa_loop.c-497-\t\tmdio_device_remove(switch_mdiodev);\n--\ndrivers/net/dsa/dsa_loop.c-502-}\ndrivers/net/dsa/dsa_loop.c:503:module_init(dsa_loop_init);\ndrivers/net/dsa/dsa_loop.c-504-\ndrivers/net/dsa/dsa_loop.c:505:static void __exit dsa_loop_exit(void)\ndrivers/net/dsa/dsa_loop.c-506-{\ndrivers/net/dsa/dsa_loop.c:507:\tmdio_driver_unregister(\u0026dsa_loop_drv);\ndrivers/net/dsa/dsa_loop.c:508:\tdsa_loop_phydevs_unregister();\ndrivers/net/dsa/dsa_loop.c-509-\tmdio_device_remove(switch_mdiodev);\n--\ndrivers/net/dsa/dsa_loop.c-511-}\ndrivers/net/dsa/dsa_loop.c:512:module_exit(dsa_loop_exit);\ndrivers/net/dsa/dsa_loop.c-513-\n"
} |
| 29/2 | 2026/09/26 22:51 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 75389 output: 31 thoughts: 843 Thoughts: **Analyzing `phylink_stop` and Potential RTNL Locking Issues**
Okay, so I'm looking at this `dsa_loop` driver, and it looks like it's a software mockup for switch ports, creating them, and `dsa_user_open` / `dsa_user_close` are interacting with these ports, ultimately using `phylink_start` / `phylink_stop`. I need to figure out what's going on with PM/suspend/resume in the context of `phylink`.
First, a quick check: can syzkaller be used to test PM, suspend, and resume scenarios? Yes! It can trigger these states through sysfs or ioctls. But, more importantly, I need to focus on what happens during shutdown.
Now, focusing on `phylink_stop`, it's called on the close of *any* netdev using phylink. That includes things like USB net devices and, critically, even these mock switches like `dsa_loop`. The important thing here is: `phylink_stop` is called on *every* close! This happens on `ifconfig eth0 down` or `ip link set dev eth0 down` as well, through the netdev's `ndo_stop()`.
I notice that the recent patch included this specific section within `phylink_stop`:
```c
if (test_bit(PHYLINK_DISABLE_MAC_WOL, &pl->phylink_disable_state)) {
/* Finish the link-down deferred by MAC WoL, without restarting. */
flush_work(&pl->resolve);
mutex_lock(&pl->state_mutex);
if (pl->suspend_link_up)
phylink_link_down(pl);
__clear_bit(PHYLINK_DISABLE_MAC_WOL, &pl->phylink_disable_state);
mutex_unlock(&pl->state_mutex);
}
```
My key concern arises from the `flush_work(&pl->resolve);` call, *while* holding `ASSERT_RTNL()`! My immediate thought is, could the `pl->resolve` work handler *also* acquire `RTNL`? That could be a recipe for some serious deadlock potential. So, now, I need to investigate exactly what the `pl->resolve` work does and what the locking situation is there. I need to know if this work queue function can acquire the RTNL lock, because if it does, it could cause issues.
|
| 30/2 | 2026/09/26 22:52 | tool | grepper |
0mArgs:{
"Expression": "resolve",
"PathPrefix": "drivers/net/phy/phylink.c"
}Results: {
"Output": "drivers/net/phy/phylink.c=41=struct phylink {\n--\ndrivers/net/phy/phylink.c-70-\tstruct mutex state_mutex;\ndrivers/net/phy/phylink.c:71:\t/* Serialize updates to pl-\u003ephydev with phylink_resolve() */\ndrivers/net/phy/phylink.c-72-\tstruct mutex phydev_mutex;\n--\ndrivers/net/phy/phylink.c-74-\tunsigned int phy_ib_mode;\ndrivers/net/phy/phylink.c:75:\tstruct work_struct resolve;\ndrivers/net/phy/phylink.c-76-\tunsigned int pcs_neg_mode;\n--\ndrivers/net/phy/phylink.c=854=static void phylink_apply_manual_flow(struct phylink *pl,\n--\ndrivers/net/phy/phylink.c-866-\ndrivers/net/phy/phylink.c:867:static void phylink_resolve_an_pause(struct phylink_link_state *state)\ndrivers/net/phy/phylink.c-868-{\n--\ndrivers/net/phy/phylink.c-871-\tif (state-\u003eduplex == DUPLEX_FULL) {\ndrivers/net/phy/phylink.c:872:\t\tlinkmode_resolve_pause(state-\u003eadvertising,\ndrivers/net/phy/phylink.c-873-\t\t\t\t state-\u003elp_advertising,\n--\ndrivers/net/phy/phylink.c=1442=static void phylink_get_fixed_state(struct phylink *pl,\n--\ndrivers/net/phy/phylink.c-1451-\tstate-\u003epause = MLO_PAUSE_NONE;\ndrivers/net/phy/phylink.c:1452:\tphylink_resolve_an_pause(state);\ndrivers/net/phy/phylink.c-1453-}\n--\ndrivers/net/phy/phylink.c=1611=static bool phylink_link_is_up(struct phylink *pl)\n--\ndrivers/net/phy/phylink.c-1615-\ndrivers/net/phy/phylink.c:1616:static void phylink_resolve(struct work_struct *w)\ndrivers/net/phy/phylink.c-1617-{\ndrivers/net/phy/phylink.c:1618:\tstruct phylink *pl = container_of(w, struct phylink, resolve);\ndrivers/net/phy/phylink.c-1619-\tstruct phylink_link_state link_state;\n--\ndrivers/net/phy/phylink.c-1649-\t\t/* The PCS may have a latching link-fail indicator. If the link\ndrivers/net/phy/phylink.c:1650:\t\t * was up, bring the link down and re-trigger the resolve.\ndrivers/net/phy/phylink.c-1651-\t\t * Otherwise, re-read the PCS state to get the current status\n--\ndrivers/net/phy/phylink.c-1669-\t\t\t/* If the interface has changed, force a link down\ndrivers/net/phy/phylink.c:1670:\t\t\t * event if the link isn't already down, and re-resolve.\ndrivers/net/phy/phylink.c-1671-\t\t\t */\n--\ndrivers/net/phy/phylink.c-1728-\t\tpl-\u003elink_failed = false;\ndrivers/net/phy/phylink.c:1729:\t\tqueue_work(system_power_efficient_wq, \u0026pl-\u003eresolve);\ndrivers/net/phy/phylink.c-1730-\t}\n--\ndrivers/net/phy/phylink.c-1736-\ndrivers/net/phy/phylink.c:1737:static void phylink_run_resolve(struct phylink *pl)\ndrivers/net/phy/phylink.c-1738-{\ndrivers/net/phy/phylink.c-1739-\tif (!pl-\u003ephylink_disable_state)\ndrivers/net/phy/phylink.c:1740:\t\tqueue_work(system_power_efficient_wq, \u0026pl-\u003eresolve);\ndrivers/net/phy/phylink.c-1741-}\ndrivers/net/phy/phylink.c-1742-\ndrivers/net/phy/phylink.c:1743:static void phylink_run_resolve_and_disable(struct phylink *pl, int bit)\ndrivers/net/phy/phylink.c-1744-{\n--\ndrivers/net/phy/phylink.c-1748-\tif (state == 0) {\ndrivers/net/phy/phylink.c:1749:\t\tqueue_work(system_power_efficient_wq, \u0026pl-\u003eresolve);\ndrivers/net/phy/phylink.c:1750:\t\tflush_work(\u0026pl-\u003eresolve);\ndrivers/net/phy/phylink.c-1751-\t}\n--\ndrivers/net/phy/phylink.c-1753-\ndrivers/net/phy/phylink.c:1754:static void phylink_enable_and_run_resolve(struct phylink *pl, int bit)\ndrivers/net/phy/phylink.c-1755-{\ndrivers/net/phy/phylink.c-1756-\tclear_bit(bit, \u0026pl-\u003ephylink_disable_state);\ndrivers/net/phy/phylink.c:1757:\tphylink_run_resolve(pl);\ndrivers/net/phy/phylink.c-1758-}\n--\ndrivers/net/phy/phylink.c=1760=static void phylink_fixed_poll(struct timer_list *t)\n--\ndrivers/net/phy/phylink.c-1765-\ndrivers/net/phy/phylink.c:1766:\tphylink_run_resolve(pl);\ndrivers/net/phy/phylink.c-1767-}\n--\ndrivers/net/phy/phylink.c=1851=struct phylink *phylink_create(struct phylink_config *config,\n--\ndrivers/net/phy/phylink.c-1871-\tmutex_init(\u0026pl-\u003estate_mutex);\ndrivers/net/phy/phylink.c:1872:\tINIT_WORK(\u0026pl-\u003eresolve, phylink_resolve);\ndrivers/net/phy/phylink.c-1873-\n--\ndrivers/net/phy/phylink.c=1950=void phylink_destroy(struct phylink *pl)\n--\ndrivers/net/phy/phylink.c-1955-\ndrivers/net/phy/phylink.c:1956:\tcancel_work_sync(\u0026pl-\u003eresolve);\ndrivers/net/phy/phylink.c-1957-\tkfree(pl);\n--\ndrivers/net/phy/phylink.c=1978=static void phylink_phy_change(struct phy_device *phydev, bool up)\n--\ndrivers/net/phy/phylink.c-2003-\ndrivers/net/phy/phylink.c:2004:\tphylink_run_resolve(pl);\ndrivers/net/phy/phylink.c-2005-\n--\ndrivers/net/phy/phylink.c=2359=void phylink_disconnect_phy(struct phylink *pl)\n--\ndrivers/net/phy/phylink.c-2378-\tif (phy) {\ndrivers/net/phy/phylink.c:2379:\t\tflush_work(\u0026pl-\u003eresolve);\ndrivers/net/phy/phylink.c-2380-\t\tphy_disconnect(phy);\n--\ndrivers/net/phy/phylink.c=2385=static void phylink_link_changed(struct phylink *pl, bool up, const char *what)\n--\ndrivers/net/phy/phylink.c-2388-\t\tpl-\u003elink_failed = true;\ndrivers/net/phy/phylink.c:2389:\tphylink_run_resolve(pl);\ndrivers/net/phy/phylink.c-2390-\tphylink_dbg(pl, \"%s link %s\\n\", what, up ? \"up\" : \"down\");\n--\ndrivers/net/phy/phylink.c=2427=static irqreturn_t phylink_link_handler(int irq, void *data)\n--\ndrivers/net/phy/phylink.c-2430-\ndrivers/net/phy/phylink.c:2431:\tphylink_run_resolve(pl);\ndrivers/net/phy/phylink.c-2432-\n--\ndrivers/net/phy/phylink.c=2444=void phylink_start(struct phylink *pl)\n--\ndrivers/net/phy/phylink.c-2471-\ndrivers/net/phy/phylink.c:2472:\tphylink_enable_and_run_resolve(pl, PHYLINK_DISABLE_STOPPED);\ndrivers/net/phy/phylink.c-2473-\n--\ndrivers/net/phy/phylink.c=2529=void phylink_stop(struct phylink *pl)\n--\ndrivers/net/phy/phylink.c-2554-\ndrivers/net/phy/phylink.c:2555:\tphylink_run_resolve_and_disable(pl, PHYLINK_DISABLE_STOPPED);\ndrivers/net/phy/phylink.c-2556-\n--\ndrivers/net/phy/phylink.c-2558-\t\t/* Finish the link-down deferred by MAC WoL, without restarting. */\ndrivers/net/phy/phylink.c:2559:\t\tflush_work(\u0026pl-\u003eresolve);\ndrivers/net/phy/phylink.c-2560-\t\tmutex_lock(\u0026pl-\u003estate_mutex);\n--\ndrivers/net/phy/phylink.c=2665=void phylink_suspend(struct phylink *pl, bool mac_wol)\n--\ndrivers/net/phy/phylink.c-2675-\ndrivers/net/phy/phylink.c:2676:\t\t/* Stop the resolver bringing the link up */\ndrivers/net/phy/phylink.c-2677-\t\t__set_bit(PHYLINK_DISABLE_MAC_WOL, \u0026pl-\u003ephylink_disable_state);\n--\ndrivers/net/phy/phylink.c-2682-\t\t\t * but one would hope all packets have been sent. This\ndrivers/net/phy/phylink.c:2683:\t\t\t * also means phylink_resolve() will do nothing.\ndrivers/net/phy/phylink.c-2684-\t\t\t */\n--\ndrivers/net/phy/phylink.c=2737=void phylink_resume(struct phylink *pl)\n--\ndrivers/net/phy/phylink.c-2751-\t\t\t * the true link state will be printed when we run a\ndrivers/net/phy/phylink.c:2752:\t\t\t * resolve.\ndrivers/net/phy/phylink.c-2753-\t\t\t */\n--\ndrivers/net/phy/phylink.c-2763-\ndrivers/net/phy/phylink.c:2764:\t\t/* Re-enable and re-resolve the link parameters */\ndrivers/net/phy/phylink.c:2765:\t\tphylink_enable_and_run_resolve(pl, PHYLINK_DISABLE_MAC_WOL);\ndrivers/net/phy/phylink.c-2766-\t} else {\n--\ndrivers/net/phy/phylink.c=3032=int phylink_ethtool_ksettings_set(struct phylink *pl,\n--\ndrivers/net/phy/phylink.c-3052-\t\t * - link_config.interface will be updated by the PHY calling\ndrivers/net/phy/phylink.c:3053:\t\t * back via phylink_phy_change() and a subsequent resolve.\ndrivers/net/phy/phylink.c-3054-\t\t * - initial link configuration for PHY mode comes from the\n--\ndrivers/net/phy/phylink.c=3243=int phylink_ethtool_set_pauseparam(struct phylink *pl,\n--\ndrivers/net/phy/phylink.c-3303-\t * cause phylib to renegotiate (if AN is enabled) which will in turn\ndrivers/net/phy/phylink.c:3304:\t * call our phylink_phy_change() and trigger a resolve. Note that\ndrivers/net/phy/phylink.c-3305-\t * we can't hold our state mutex while calling phy_set_asym_pause().\n--\ndrivers/net/phy/phylink.c-3311-\t/* If the manual pause settings changed, make sure we trigger a\ndrivers/net/phy/phylink.c:3312:\t * resolve to update their state; we can not guarantee that the\ndrivers/net/phy/phylink.c-3313-\t * link will cycle.\n--\ndrivers/net/phy/phylink.c-3316-\t\tpl-\u003elink_failed = true;\ndrivers/net/phy/phylink.c:3317:\t\tphylink_run_resolve(pl);\ndrivers/net/phy/phylink.c-3318-\t}\n--\ndrivers/net/phy/phylink.c=3960=static void phylink_sfp_link_down(void *upstream)\n--\ndrivers/net/phy/phylink.c-3965-\ndrivers/net/phy/phylink.c:3966:\tphylink_run_resolve_and_disable(pl, PHYLINK_DISABLE_LINK);\ndrivers/net/phy/phylink.c-3967-}\n--\ndrivers/net/phy/phylink.c=3969=static void phylink_sfp_link_up(void *upstream)\n--\ndrivers/net/phy/phylink.c-3974-\ndrivers/net/phy/phylink.c:3975:\tphylink_enable_and_run_resolve(pl, PHYLINK_DISABLE_LINK);\ndrivers/net/phy/phylink.c-3976-}\n--\ndrivers/net/phy/phylink.c=4027=static struct {\n--\ndrivers/net/phy/phylink.c-4042-\ndrivers/net/phy/phylink.c:4043:void phylink_resolve_c73(struct phylink_link_state *state)\ndrivers/net/phy/phylink.c-4044-{\n--\ndrivers/net/phy/phylink.c-4061-\ndrivers/net/phy/phylink.c:4062:\tphylink_resolve_an_pause(state);\ndrivers/net/phy/phylink.c-4063-}\ndrivers/net/phy/phylink.c:4064:EXPORT_SYMBOL_GPL(phylink_resolve_c73);\ndrivers/net/phy/phylink.c-4065-\ndrivers/net/phy/phylink.c=4066=static void phylink_decode_c37_word(struct phylink_link_state *state,\n--\ndrivers/net/phy/phylink.c-4086-\ndrivers/net/phy/phylink.c:4087:\tphylink_resolve_an_pause(state);\ndrivers/net/phy/phylink.c-4088-}\n--\ndrivers/net/phy/phylink.c=4443=void phylink_replay_link_begin(struct phylink *pl)\n--\ndrivers/net/phy/phylink.c-4446-\ndrivers/net/phy/phylink.c:4447:\tphylink_run_resolve_and_disable(pl, PHYLINK_DISABLE_REPLAY);\ndrivers/net/phy/phylink.c-4448-}\n--\ndrivers/net/phy/phylink.c=4466=void phylink_replay_link_end(struct phylink *pl)\n--\ndrivers/net/phy/phylink.c-4475-\tpl-\u003eforce_major_config = true;\ndrivers/net/phy/phylink.c:4476:\tphylink_enable_and_run_resolve(pl, PHYLINK_DISABLE_REPLAY);\ndrivers/net/phy/phylink.c:4477:\tflush_work(\u0026pl-\u003eresolve);\ndrivers/net/phy/phylink.c-4478-}\n"
} |
| 31/2 | 2026/09/26 22:52 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 80949 output: 50 thoughts: 101 Thoughts: **Examining phylink_stop and its Implications** Okay, so I'm looking into the `drivers/net/phy/phylink.c` file. Specifically, I'm focusing on `phylink_stop`. It's pretty clear this is a key function, widely used across netdev drivers whenever an interface that utilizes phylink is being stopped. I need to understand its role and potential impact within the broader network stack. Now, let's pivot. The `stmmac` driver is a likely target for vulnerability research, so I should examine it too. The question is, can `stmmac` even be reached in standard fuzzing setups? I need to know if it's accessible during common testing and if the fuzzer can actually trigger it. Alright, let's zoom in on `stmmac_pci.c`. The first step here is identifying the specific PCI device IDs that `stmmac_pci` actually matches. Knowing those IDs is crucial for understanding its hardware footprint and establishing the possible attack surface. That should help me figure out the drivers' interaction with phylink when the device is stopped. |
| 32/2 | 2026/09/26 22:52 | tool | read-file |
0mArgs:{
"File": "drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c",
"FirstLine": 180,
"LineCount": 40
}Results: {
"Contents": " 180:\t\tplat-\u003esuspend = stmmac_pci_plat_suspend;\n 181:\t\tplat-\u003eresume = stmmac_pci_plat_resume;\n 182:\t\n 183:\t\treturn stmmac_dvr_probe(\u0026pdev-\u003edev, plat, \u0026res);\n 184:\t}\n 185:\t\n 186:\t/**\n 187:\t * stmmac_pci_remove\n 188:\t *\n 189:\t * @pdev: platform device pointer\n 190:\t * Description: this function calls the main to free the net resources.\n 191:\t */\n 192:\tstatic void stmmac_pci_remove(struct pci_dev *pdev)\n 193:\t{\n 194:\t\tstmmac_dvr_remove(\u0026pdev-\u003edev);\n 195:\t}\n 196:\t\n 197:\t/* synthetic ID, no official vendor */\n 198:\t#define PCI_VENDOR_ID_STMMAC\t\t0x0700\n 199:\t\n 200:\t#define PCI_DEVICE_ID_STMMAC_STMMAC\t\t0x1108\n 201:\t#define PCI_DEVICE_ID_SYNOPSYS_GMAC5_ID\t\t0x7102\n 202:\t\n 203:\tstatic const struct pci_device_id stmmac_id_table[] = {\n 204:\t\t{ PCI_DEVICE_DATA(STMMAC, STMMAC, \u0026stmmac_pci_info) },\n 205:\t\t{ PCI_DEVICE_DATA(STMICRO, MAC, \u0026stmmac_pci_info) },\n 206:\t\t{ PCI_DEVICE_DATA(SYNOPSYS, GMAC5_ID, \u0026snps_gmac5_pci_info) },\n 207:\t\t{}\n 208:\t};\n 209:\t\n 210:\tMODULE_DEVICE_TABLE(pci, stmmac_id_table);\n 211:\t\n 212:\tstatic struct pci_driver stmmac_pci_driver = {\n 213:\t\t.name = STMMAC_RESOURCE_NAME,\n 214:\t\t.id_table = stmmac_id_table,\n 215:\t\t.probe = stmmac_pci_probe,\n 216:\t\t.remove = stmmac_pci_remove,\n 217:\t\t.driver = {\n 218:\t\t\t.pm = \u0026stmmac_simple_pm_ops,\n 219:\t\t},\n"
} |
| 33/2 | 2026/09/26 22:52 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 82036 output: 43 thoughts: 764 Thoughts: **Analyzing Patch Reachability for Fuzzing** Okay, let's break this down. First, I'm noting the device IDs: 0x0700:0x1108, STMICRO:MAC, and SYNOPSYS:0x7102. I recall that these are typically associated with specific hardware configurations, likely from the Intel family and some other chip vendors. These aren't emulated in standard QEMU; we have the usual suspects there: e1000, e1000e, vmxnet3, and so on. Platform devices are more for ARM/SoC architectures, which is a different beast entirely. Then the key question arises: what about *`phylink.c`*? I need to re-read the triage rules meticulously, specifically sections 1 and 2. Remember, the primary gate is reachability. Fuzzing only makes sense for code that *executes* in standard virtualized environments. If the code is unreachable, forget it. Section 2 is where the crucial negative criteria are defined. It says I can only return `WorthFuzzing=false` if *all* the modified code falls into the "unreachable" categories. So, are we talking about vendor-specific PCIe switches, or drivers that require physical ASICs or cards not emulated? Or paths exclusive to other architectures? I have to carefully evaluate the phrasing, because it demands that *all* the modified code must be unreachable for the triage to return false. Here is the trick, is all modified code in unreachable hardware? The code in `phylink.c` is the key. `drivers/net/phy/phylink.c` is modified, but it's *not* in `stmicro/stmmac`. The location in `drivers/net/phy/` indicates that it's a generic networking framework – part of the core PHY and netdev abstraction. It is NOT structurally unreachable. So the question becomes: can phylink code be triggered from a QEMU environment? To determine this, let's explore where `phylink_create` is called. The calls to `phylink_create` are our clue. I'm seeing that `drivers/net/usb/asix_devices.c` calls phylink. Let's see how `asix_devices.c` interacts with `phylink` by inspecting the code. |
| 34/2 | 2026/09/26 22:52 | tool | grepper |
0mArgs:{
"Expression": "ax88772_phylink_setup",
"PathPrefix": "drivers/net/usb/asix_devices.c"
}Results: {
"Output": "drivers/net/usb/asix_devices.c=793=static const struct phylink_mac_ops ax88772_phylink_mac_ops = {\n--\ndrivers/net/usb/asix_devices.c-798-\ndrivers/net/usb/asix_devices.c:799:static int ax88772_phylink_setup(struct usbnet *dev)\ndrivers/net/usb/asix_devices.c-800-{\n--\ndrivers/net/usb/asix_devices.c=829=static int ax88772_bind(struct usbnet *dev, struct usb_interface *intf)\n--\ndrivers/net/usb/asix_devices.c-920-\ndrivers/net/usb/asix_devices.c:921:\tret = ax88772_phylink_setup(dev);\ndrivers/net/usb/asix_devices.c-922-\tif (ret)\n"
} |
| 35/2 | 2026/09/26 22:52 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 83612 output: 40 thoughts: 156 Thoughts: Okay, let me break this down. **Diving into the `ax88772` Driver and its Phylink Setup** Right, so I'm looking at the `ax88772_bind` function. Standard stuff – it's the core of the Asix AX88772 USB ethernet adapter driver. We know syzkaller hits this code path pretty regularly when it's probing the device, presumably via USB raw-gadget or some form of emulation. The key thing here is the call to `ax88772_phylink_setup(dev)` that happens as part of the binding process. This is where things get interesting from a networking and phylink perspective. Now, inside `ax88772_phylink_setup`, it seems like `phylink_create(...)` gets called. This means the driver is setting up a phylink instance, which is pretty standard for managing the physical layer interface. Then, when the device is actually opened – this would be triggered by things like `SIOCSIFFLAGS` socket ioctls and `dev_open` – we jump to the `usbnet_open` function. That function then calls `info->open(dev)` which is a driver-specific open function. |
| 36/2 | 2026/09/26 22:52 | tool | grepper |
0mError:no matches Args: {
"Expression": "ax88772_open",
"PathPrefix": "drivers/net/usb/asix_devices.c"
}Results: {
"error": "no matches"
} |
| 37/2 | 2026/09/26 22:52 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 84094 output: 40 thoughts: 21 Thoughts: Here's my take on those thoughts, tailored for an expert audience: **Investigating AX88772 Callback Functionality** Alright, let's dive in. My immediate thought is to check the `open` and `stop` callbacks, specifically within the context of the ax88772 driver. I need to understand how the driver handles initialization and de-initialization, the vital parts for smooth operation and resource management. I'm assuming these callbacks are where the critical hardware setup and teardown are managed. This initial focus is the logical starting point for understanding the device's lifecycle within the system. My brain is already running through potential scenarios regarding resource allocation, interrupt handling, and power management based on how those callbacks are structured. What are the specific functions? Are they blocking, or interrupt-driven? These initial questions will shape my subsequent analysis. |
| 38/2 | 2026/09/26 22:52 | tool | grepper |
0mArgs:{
"Expression": "ax88772_stop",
"PathPrefix": "drivers/net/usb/asix_devices.c"
}Results: {
"Output": "drivers/net/usb/asix_devices.c=829=static int ax88772_bind(struct usbnet *dev, struct usb_interface *intf)\n--\ndrivers/net/usb/asix_devices.c-945-\ndrivers/net/usb/asix_devices.c:946:static int ax88772_stop(struct usbnet *dev)\ndrivers/net/usb/asix_devices.c-947-{\n--\ndrivers/net/usb/asix_devices.c=1366=static const struct driver_info ax88772_info = {\n--\ndrivers/net/usb/asix_devices.c-1370-\t.reset = ax88772_reset,\ndrivers/net/usb/asix_devices.c:1371:\t.stop = ax88772_stop,\ndrivers/net/usb/asix_devices.c-1372-\t.flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_MULTI_PACKET,\n--\ndrivers/net/usb/asix_devices.c=1377=static const struct driver_info ax88772b_info = {\n--\ndrivers/net/usb/asix_devices.c-1381-\t.reset = ax88772_reset,\ndrivers/net/usb/asix_devices.c:1382:\t.stop = ax88772_stop,\ndrivers/net/usb/asix_devices.c-1383-\t.flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_MULTI_PACKET,\n--\ndrivers/net/usb/asix_devices.c=1389=static const struct driver_info lxausb_t1l_info = {\n--\ndrivers/net/usb/asix_devices.c-1393-\t.reset = ax88772_reset,\ndrivers/net/usb/asix_devices.c:1394:\t.stop = ax88772_stop,\ndrivers/net/usb/asix_devices.c-1395-\t.flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_MULTI_PACKET,\n"
} |
| 39/2 | 2026/09/26 22:52 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 84898 output: 40 thoughts: 38 Thoughts: Here's my analysis of the provided snippet, presented as a first-person expert perspective: **Diving into the Driver's Shutdown Sequence** Okay, so `ax88772_stop` is the function that handles the NIC's shutdown process, which is critical for proper resource release and avoiding potential issues. The first thing that jumps out is the direct call to `phylink_stop(priv->phylink)`. Aha! It's delegating the PHY-specific shutdown to the `phylink` layer. This makes perfect sense; `phylink` abstracts the PHY details, and `ax88772_stop` can focus on the NIC-specific parts of the shutdown procedure. That means the `phylink` layer is where the PHY control and power management is going to happen for the device. I need to understand that call's role here, as `phylink` is responsible for low level operations like disabling link and speed settings. Now I'll need to dig into `ax88772_stop` function to understand what else is happening within it, and how it interacts with the `phylink` subsystem. This is a good design, separating the PHY interaction from the general NIC shutdown logic. Next step: let's see what happens within `ax88772_stop`... |
| 40/2 | 2026/09/26 22:52 | tool | read-file |
0mArgs:{
"File": "drivers/net/usb/asix_devices.c",
"FirstLine": 945,
"LineCount": 20
}Results: {
"Contents": " 945:\t\n 946:\tstatic int ax88772_stop(struct usbnet *dev)\n 947:\t{\n 948:\t\tstruct asix_common_private *priv = dev-\u003edriver_priv;\n 949:\t\n 950:\t\tphylink_stop(priv-\u003ephylink);\n 951:\t\n 952:\t\treturn 0;\n 953:\t}\n 954:\t\n 955:\tstatic void ax88772_unbind(struct usbnet *dev, struct usb_interface *intf)\n 956:\t{\n 957:\t\tstruct asix_common_private *priv = dev-\u003edriver_priv;\n 958:\t\n 959:\t\trtnl_lock();\n 960:\t\tphylink_disconnect_phy(priv-\u003ephylink);\n 961:\t\trtnl_unlock();\n 962:\t\tphylink_destroy(priv-\u003ephylink);\n 963:\t\tax88772_mdio_unregister(priv);\n 964:\t\tasix_rx_fixup_common_free(dev-\u003edriver_priv);\n"
} |
| 41/2 | 2026/09/26 22:52 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 85566 output: 41 thoughts: 105 Thoughts: Okay, let me break this down. **Investigating Network Driver Shutdown Procedures** Alright, fascinating! My attention is immediately drawn to the `usbnet_stop` function. So, when this is called, specifically as the `ndo_stop` callback within the `usbnet` driver framework, it subsequently invokes `info->stop(dev)`. This seems pretty standard for managing the network interface device. Now, my mind goes to the `ax88772` driver, a common USB-Ethernet adapter. I'm noting that `ax88772_stop()` (the specific implementation for that device) calls `phylink_stop(priv->phylink)`. That's important; it's delegating the PHY-layer shutdown to the phylink subsystem. I'd expect it is important to check the PHY linkage as the driver moves through the shutdown sequence. Just to be thorough, I'm also going to examine the `lan78xx.c` driver. Let's see how `lan78xx_stop()` handles the network interface shutdown. This will allow me to check for potential similarities and differences in the PHY-layer management, especially in how `phylink` is handled. I am eager to see if this is being implemented in a similar way. |
| 42/2 | 2026/09/26 22:52 | tool | read-file |
0mArgs:{
"File": "drivers/net/usb/lan78xx.c",
"FirstLine": 3555,
"LineCount": 20
}Results: {
"Contents": "3555:\t\t\t \"stop stats: rx/tx %lu/%lu, errs %lu/%lu\\n\",\n3556:\t\t\t net-\u003estats.rx_packets, net-\u003estats.tx_packets,\n3557:\t\t\t net-\u003estats.rx_errors, net-\u003estats.tx_errors);\n3558:\t\n3559:\t\tphylink_stop(dev-\u003ephylink);\n3560:\t\n3561:\t\tusb_kill_urb(dev-\u003eurb_intr);\n3562:\t\n3563:\t\t/* deferred work (task, timer, softirq) must also stop.\n3564:\t\t * can't flush_scheduled_work() until we drop rtnl (later),\n3565:\t\t * else workers could deadlock; so make workers a NOP.\n3566:\t\t */\n3567:\t\tclear_bit(EVENT_TX_HALT, \u0026dev-\u003eflags);\n3568:\t\tclear_bit(EVENT_RX_HALT, \u0026dev-\u003eflags);\n3569:\t\tclear_bit(EVENT_PHY_INT_ACK, \u0026dev-\u003eflags);\n3570:\t\tclear_bit(EVENT_STAT_UPDATE, \u0026dev-\u003eflags);\n3571:\t\n3572:\t\tcancel_delayed_work_sync(\u0026dev-\u003ewq);\n3573:\t\n3574:\t\tusb_autopm_put_interface(dev-\u003eintf);\n"
} |
| 43/2 | 2026/09/26 22:52 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 86356 output: 37 thoughts: 83 Thoughts: Here's my thinking process: **Diving into the LAN78XX Driver** Okay, so `lan78xx_stop` is, under the hood, the `ndo_stop` method defined within the `lan78xx_netdev_ops` structure. Got it. That's good to know, confirming the expected network device operation. Now, `lan78xx_suspend` is calling `phylink_suspend` with the `dev->phylink` object. Hmm, that's interesting and points towards the driver's power management strategy, likely using Phylink for the underlying PHY. Alright, time to dig deeper. It seems like the next logical step is to confirm the calls to `phylink_resume` are happening as well. I need to make sure the driver properly handles the resume path, ensuring the PHY is brought back up after a suspend. The interplay between the LAN78XX driver and Phylink is important here and I need to review both calls. I should make sure there aren't any missing pieces. |
| 44/2 | 2026/09/26 22:52 | tool | grepper |
0mArgs:{
"Expression": "phylink_suspend",
"PathPrefix": "drivers/net/usb/lan78xx.c"
}Results: {
"Output": "drivers/net/usb/lan78xx.c=5115=static int lan78xx_suspend(struct usb_interface *intf, pm_message_t message)\n--\ndrivers/net/usb/lan78xx.c-5142-\t\trtnl_lock();\ndrivers/net/usb/lan78xx.c:5143:\t\tphylink_suspend(dev-\u003ephylink, false);\ndrivers/net/usb/lan78xx.c-5144-\t\trtnl_unlock();\n"
} |
| 45/2 | 2026/09/26 22:52 | llm | patch-evaluator |
1mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 86842 output: 39 thoughts: 1525 Thoughts: **Analyzing Phylink Stop Logic and Potential Issues** Okay, so I've been looking into the `phylink` driver and how it interacts with suspend, resume, and stop operations, specifically focusing on the new logic added to `phylink_stop`. It looks like `lan78xx` and likely other drivers, like the asix / ax88772, use `phylink` to manage the PHY link. My initial observation is that `phylink_stop` now handles some crucial edge cases, particularly those related to suspended instances. The key change involves `phylink_restore_suspend_speed(pl)` and the conditional block dealing with `PHYLINK_DISABLE_STOPPED`. If this bit is set, `phylink_stop` now attempts to suspend the PHY again via `phy_suspend(pl->phydev)`, and returns early! Also, `phylink_speed_down` also calls `phylink_restore_suspend_speed`. The important bit is that if `phylink_stop` is called with the `PHYLINK_DISABLE_STOPPED` flag set, it's *supposed* to handle cases where the PHY might have been inadvertently powered up during a failed resume, possibly to provide an RX clock. This is interesting as it highlights a potential for a race condition or incorrect state if the device has already been stopped, or is in an unexpected state. Also, it may cause a crash if `pl->phydev` doesn't exist. My main concern is that `dev_close` could trigger `phylink_stop` directly. Now, the question becomes: what happens if `phylink_stop` is called multiple times, or is called under unusual circumstances? We need to verify what happens when `pl->phydev` is already suspended or not started. What is going to happen if it is called after a PHY disconnect? Furthermore, there is a second conditional block that handles `PHYLINK_DISABLE_MAC_WOL`, that calls `flush_work(&pl->resolve)` *while holding the RTNL lock*. The code attempts to complete a link-down deferred by MAC WoL. But, what does `phylink_resolve` do? Will `phylink_resolve` deadlock with other locks? I need to check `phylink_resolve` and make sure it is safe. |
| 46/2 | 2026/09/26 22:53 | tool | read-file |
0mArgs:{
"File": "drivers/net/phy/phylink.c",
"FirstLine": 1615,
"LineCount": 80
}Results: {
"Contents": "1615:\t\n1616:\tstatic void phylink_resolve(struct work_struct *w)\n1617:\t{\n1618:\t\tstruct phylink *pl = container_of(w, struct phylink, resolve);\n1619:\t\tstruct phylink_link_state link_state;\n1620:\t\tbool mac_config = false;\n1621:\t\tbool retrigger = false;\n1622:\t\tstruct phy_device *phy;\n1623:\t\tbool cur_link_state;\n1624:\t\n1625:\t\tmutex_lock(\u0026pl-\u003ephydev_mutex);\n1626:\t\tphy = pl-\u003ephydev;\n1627:\t\tif (phy)\n1628:\t\t\tmutex_lock(\u0026phy-\u003elock);\n1629:\t\tmutex_lock(\u0026pl-\u003estate_mutex);\n1630:\t\tcur_link_state = phylink_link_is_up(pl);\n1631:\t\n1632:\t\tif (pl-\u003ephylink_disable_state) {\n1633:\t\t\tpl-\u003elink_failed = false;\n1634:\t\t\tlink_state = pl-\u003elink_config;\n1635:\t\t\tlink_state.link = false;\n1636:\t\t} else if (pl-\u003elink_failed) {\n1637:\t\t\tlink_state = pl-\u003elink_config;\n1638:\t\t\tlink_state.link = false;\n1639:\t\t\tretrigger = true;\n1640:\t\t} else if (pl-\u003eact_link_an_mode == MLO_AN_FIXED) {\n1641:\t\t\tphylink_get_fixed_state(pl, \u0026link_state);\n1642:\t\t\tmac_config = link_state.link;\n1643:\t\t} else if (pl-\u003eact_link_an_mode == MLO_AN_PHY) {\n1644:\t\t\tlink_state = pl-\u003ephy_state;\n1645:\t\t\tmac_config = link_state.link;\n1646:\t\t} else {\n1647:\t\t\tphylink_mac_pcs_get_state(pl, \u0026link_state);\n1648:\t\n1649:\t\t\t/* The PCS may have a latching link-fail indicator. If the link\n1650:\t\t\t * was up, bring the link down and re-trigger the resolve.\n1651:\t\t\t * Otherwise, re-read the PCS state to get the current status\n1652:\t\t\t * of the link.\n1653:\t\t\t */\n1654:\t\t\tif (!link_state.link) {\n1655:\t\t\t\tif (cur_link_state)\n1656:\t\t\t\t\tretrigger = true;\n1657:\t\t\t\telse\n1658:\t\t\t\t\tphylink_mac_pcs_get_state(pl, \u0026link_state);\n1659:\t\t\t}\n1660:\t\n1661:\t\t\t/* If we have a phy, the \"up\" state is the union of both the\n1662:\t\t\t * PHY and the MAC\n1663:\t\t\t */\n1664:\t\t\tif (phy)\n1665:\t\t\t\tlink_state.link \u0026= pl-\u003ephy_state.link;\n1666:\t\n1667:\t\t\t/* Only update if the PHY link is up */\n1668:\t\t\tif (phy \u0026\u0026 pl-\u003ephy_state.link) {\n1669:\t\t\t\t/* If the interface has changed, force a link down\n1670:\t\t\t\t * event if the link isn't already down, and re-resolve.\n1671:\t\t\t\t */\n1672:\t\t\t\tif (link_state.interface != pl-\u003ephy_state.interface) {\n1673:\t\t\t\t\tretrigger = true;\n1674:\t\t\t\t\tlink_state.link = false;\n1675:\t\t\t\t}\n1676:\t\n1677:\t\t\t\tlink_state.interface = pl-\u003ephy_state.interface;\n1678:\t\n1679:\t\t\t\t/* If we are doing rate matching, then the link\n1680:\t\t\t\t * speed/duplex comes from the PHY\n1681:\t\t\t\t */\n1682:\t\t\t\tif (pl-\u003ephy_state.rate_matching) {\n1683:\t\t\t\t\tlink_state.rate_matching =\n1684:\t\t\t\t\t\tpl-\u003ephy_state.rate_matching;\n1685:\t\t\t\t\tlink_state.speed = pl-\u003ephy_state.speed;\n1686:\t\t\t\t\tlink_state.duplex = pl-\u003ephy_state.duplex;\n1687:\t\t\t\t}\n1688:\t\n1689:\t\t\t\t/* If we have a PHY, we need to update with the PHY\n1690:\t\t\t\t * flow control bits.\n1691:\t\t\t\t */\n1692:\t\t\t\tlink_state.pause = pl-\u003ephy_state.pause;\n1693:\t\t\t\tmac_config = true;\n1694:\t\t\t}\n"
} |
| 47/2 | 2026/09/26 22:53 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 90149 output: 39 thoughts: 13 Thoughts: Okay, here's my interpretation of "Let's read further down in `phylink_resolve`" in first-person, assuming I'm a subject-matter expert: **Diving Deeper into `phylink_resolve`** Alright, that initial overview is helpful, but I need to get into the nitty-gritty. "Let's read further down in `phylink_resolve`." It's time to stop skimming and really dissect the function. I'm not just looking for a general understanding anymore; I need to see how it's *actually* handling the link resolution. I want to understand the control flow, error handling, and any edge cases it's designed to address. The devil's in the details, and that's where the real insights will be. I need to get past the high-level explanations and see the low-level logic, the actual mechanics of this process. |
| 48/2 | 2026/09/26 22:53 | tool | read-file |
0mArgs:{
"File": "drivers/net/phy/phylink.c",
"FirstLine": 1695,
"LineCount": 45
}Results: {
"Contents": "1695:\t\t}\n1696:\t\n1697:\t\tif (pl-\u003eact_link_an_mode != MLO_AN_FIXED)\n1698:\t\t\tphylink_apply_manual_flow(pl, \u0026link_state);\n1699:\t\n1700:\t\tif ((mac_config \u0026\u0026 link_state.interface != pl-\u003elink_config.interface) ||\n1701:\t\t pl-\u003eforce_major_config) {\n1702:\t\t\t/* The interface has changed or a forced major configuration\n1703:\t\t\t * was requested, so force the link down and then reconfigure.\n1704:\t\t\t */\n1705:\t\t\tif (cur_link_state) {\n1706:\t\t\t\tphylink_link_down(pl);\n1707:\t\t\t\tcur_link_state = false;\n1708:\t\t\t}\n1709:\t\t\tphylink_major_config(pl, false, \u0026link_state);\n1710:\t\t\tpl-\u003elink_config.interface = link_state.interface;\n1711:\t\t\tpl-\u003eforce_major_config = false;\n1712:\t\t}\n1713:\t\n1714:\t\t/* If configuration of the interface failed, force the link down\n1715:\t\t * until we get a successful configuration.\n1716:\t\t */\n1717:\t\tif (pl-\u003emajor_config_failed)\n1718:\t\t\tlink_state.link = false;\n1719:\t\n1720:\t\tif (link_state.link != cur_link_state) {\n1721:\t\t\tpl-\u003eold_link_state = link_state.link;\n1722:\t\t\tif (!link_state.link)\n1723:\t\t\t\tphylink_link_down(pl);\n1724:\t\t\telse\n1725:\t\t\t\tphylink_link_up(pl, link_state);\n1726:\t\t}\n1727:\t\tif (!link_state.link \u0026\u0026 retrigger) {\n1728:\t\t\tpl-\u003elink_failed = false;\n1729:\t\t\tqueue_work(system_power_efficient_wq, \u0026pl-\u003eresolve);\n1730:\t\t}\n1731:\t\tmutex_unlock(\u0026pl-\u003estate_mutex);\n1732:\t\tif (phy)\n1733:\t\t\tmutex_unlock(\u0026phy-\u003elock);\n1734:\t\tmutex_unlock(\u0026pl-\u003ephydev_mutex);\n1735:\t}\n1736:\t\n1737:\tstatic void phylink_run_resolve(struct phylink *pl)\n1738:\t{\n1739:\t\tif (!pl-\u003ephylink_disable_state)\n"
} |
| 49/2 | 2026/09/26 22:53 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 91086 output: 37 thoughts: 873 Thoughts: **Reaching Phylink: A Fuzzing Perspective** Okay, so I'm looking at this `phylink_resolve` function and the mutexes it holds: `phydev_mutex`, `phy->lock`, and `state_mutex`. That's important context. Then, I see this bit of code within `phylink_stop`: it looks like it handles a scenario where MAC WoL has disabled the link. The crucial part here is the conditional logic and the potential call to `phylink_link_down`. I need to understand the control flow, specifically how `PHYLINK_DISABLE_MAC_WOL` is set and cleared, and the implications of `flush_work(&pl->resolve)`. That's where the interesting stuff happens. My initial thought is, "Can I reach `phylink`?" I've got to determine if the changes I'm looking at are actually executed code. I need to understand what is calling those functions, to understand whether those functions are worth fuzzing. My mind immediately goes to the drivers. I'm thinking USB network drivers, like the `ax88772` and `lan78xx` (as I know that they use `phylink` and have been used in the past). These are reachable through USB emulation, either with a USB gadget or Syzkaller's USB device emulation. If I can get those devices bound and opened/closed, I know `phylink_start` and `phylink_stop` are executed, and those are important entry points for fuzzing. Now, I'm also thinking of other drivers. What about `stmmac`? Can it be reached in QEMU? I've got to consider if any virtualized environment supports it, but QEMU is usually the starting point. Is there any DWMAC / STMMAC emulation? I need to check QEMU upstream. It looks like it doesn't normally emulate these. On `amd64`, where QEMU often runs, I'm thinking about whether `stmmac` can be loaded or used. Is it possible to load a driver with platform devices or dummy devices? That's what I need to check. The `dwmac-generic.c` doesn't help because there is no device tree on amd64. What about PCI? `stmmac_pci.c` matches specific vendor IDs (Synopsys or Intel). Standard QEMU doesn't emulate these PCI IDs. So `stmmac` itself is almost certainly not reachable on standard amd64 QEMU/GCE without specific hardware. But then, I need to evaluate this based on the criteria for "WorthFuzzing." I'm looking for reachable executable code. And "phylink" is modified. It's a generic subsystem in `drivers/net/phy/` and, importantly, it's reachable via USB gadget/network devices like the ASIX AX88772 and LAN78xx. That's the key. So, I need to verify that `CONFIG_USB_NET_AX8817X`, `CONFIG_USB_NET_AX88179_178A`, or `CONFIG_USB_LAN78XX` are enabled in Syzkaller configs and if they use `phylink`. I'll check `drivers/net/usb/Kconfig` to confirm which of these are present. |
| 50/2 | 2026/09/26 22:53 | tool | grepper |
0mArgs:{
"Expression": "config USB_NET_AX8817X",
"PathPrefix": "drivers/net/usb/"
}Results: {
"Output": "drivers/net/usb/Kconfig=132=config USB_USBNET\n--\ndrivers/net/usb/Kconfig-165-\ndrivers/net/usb/Kconfig:166:config USB_NET_AX8817X\ndrivers/net/usb/Kconfig-167-\ttristate \"ASIX AX88xxx Based USB 2.0 Ethernet Adapters\"\n"
} |
| 51/2 | 2026/09/26 22:53 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 92841 output: 36 thoughts: 27 Thoughts: Here's my take on those thoughts, tailored for an expert audience: **Kconfig Dive for AX8817X USB Support** Okay, so I need to understand the configuration options for the AX8817X USB Ethernet adapter. My first move is to jump into `drivers/net/usb/Kconfig`. That's where I'd expect to find the relevant configuration symbols and dependencies. I'm looking specifically for `USB_NET_AX8817X`. That symbol should tell the story – is this driver built-in or modular? Are there any prerequisites or conflicting options? What are the implications of enabling or disabling it, perhaps some relevant dependencies? This is my primary entry point for dissecting the configuration landscape of this particular USB network interface. I expect it's going to be a straightforward look, but still worth the effort to ensure the proper functionality of the system. |
| 52/2 | 2026/09/26 22:53 | tool | read-file |
0mArgs:{
"File": "drivers/net/usb/Kconfig",
"FirstLine": 165,
"LineCount": 20
}Results: {
"Contents": " 165:\t\n 166:\tconfig USB_NET_AX8817X\n 167:\t\ttristate \"ASIX AX88xxx Based USB 2.0 Ethernet Adapters\"\n 168:\t\tdepends on USB_USBNET\n 169:\t\tselect CRC32\n 170:\t\tselect PHYLINK\n 171:\t\tselect AX88796B_PHY\n 172:\t\timply NET_SELFTESTS\n 173:\t\tdefault y\n 174:\t\thelp\n 175:\t\t This option adds support for ASIX AX88xxx based USB 2.0\n 176:\t\t 10/100 Ethernet adapters.\n 177:\t\n 178:\t\t This driver should work with at least the following devices:\n 179:\t\t * Aten UC210T\n 180:\t\t * ASIX AX88172\n 181:\t\t * Billionton Systems, USB2AR\n 182:\t\t * Billionton Systems, GUSB2AM-1G-B\n 183:\t\t * Buffalo LUA-U2-KTX\n 184:\t\t * Corega FEther USB2-TX\n"
} |
| 53/2 | 2026/09/26 22:53 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 93416 output: 33 thoughts: 32 Thoughts: Here's my thought process, as if I were considering this myself: **Network Driver Configuration - USB Ethernet Decisions** Okay, so `USB_NET_AX8817X` is picking `PHYLINK`. That's not a surprise, the ASIX AX88179/178A commonly use an external PHY and PHYLINK is the mechanism for the kernel to talk to it. Good to see that's being handled correctly. Now, let's just make sure I'm not missing anything. I should quickly check `USB_LAN78XX` and see how it’s configured. I need to refresh my understanding of how Microchip's LAN78XX ethernet bridge driver handles its PHY connectivity to make sure I'm not missing any conflicts or overlapping logic. Making sure I have a clear mental model of how different USB Ethernet drivers make those decisions is important for overall system stability and performance. |
| 54/2 | 2026/09/26 22:54 | tool | grepper |
0mArgs:{
"Expression": "config USB_LAN78XX",
"PathPrefix": "drivers/net/usb/"
}Results: {
"Output": "drivers/net/usb/Kconfig=99=config USB_RTL8152\n--\ndrivers/net/usb/Kconfig-112-\ndrivers/net/usb/Kconfig:113:config USB_LAN78XX\ndrivers/net/usb/Kconfig-114-\ttristate \"Microchip LAN78XX Based USB Ethernet Adapters\"\n"
} |
| 55/2 | 2026/09/26 22:54 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 93782 output: 36 thoughts: 12 Thoughts: Okay, here's my interpretation of "Let's read `USB_LAN78XX`," summarized in the first person, assuming I'm a subject matter expert: **Diving into USB_LAN78XX** Alright, time to get into it. `USB_LAN78XX`. Just the name itself tells me we're dealing with a USB-to-Ethernet device, likely based on a LAN78xx chipset, right? Knowing the family, I'm already anticipating some specific architectural considerations. I'm going to want to understand the driver implementation, the interrupt handling, and how it's managing the data transfer between the USB host and the Ethernet PHY. First, I'll need to look at the initialization sequence, see how the USB device is enumerated, and what endpoints are being used. Are they using bulk transfers for data, isochronous for real-time applications, or perhaps a combination? The descriptor parsing will be key to understanding the supported features, like what Ethernet speed is supported and if jumbo frames are implemented. I'm also interested in how the driver handles power management – does it support USB suspend/resume properly? Then, the data path. I'm going to look for the key data structures involved: how the driver allocates buffers for incoming and outgoing Ethernet frames, how it interacts with the USB controller hardware, and if there are any optimizations in place like DMA. I'll need to trace the data flow through the driver, from packet reception from the PHY, through the USB interface, and up to the network stack. Conversely, from the stack down to the PHY for transmission. Interrupt handling is crucial. The LAN78xx devices are known for their interrupt-driven operation. So, I need to see how the interrupts are enabled, how they're handled, and how the driver ensures interrupt latency doesn't become a bottleneck. We're talking real-time performance here, so efficient interrupt handling is paramount. Are they masking and unmasking interrupts correctly, or is there a chance of interrupt storms? Finally, I'll pay close attention to the error handling and debugging mechanisms. What happens when there's a USB transfer error, an Ethernet link problem, or a buffer overflow? Are there any specific error codes that are used? Are there any diagnostic interfaces available, like debug registers or statistical counters that might be useful? In essence, I'm expecting to delve into a well-defined interaction between USB and Ethernet, and I want to see how this particular driver has balanced performance, power efficiency, and robustness. Let's see what's in here! |
| 56/2 | 2026/09/26 22:54 | tool | read-file |
0mArgs:{
"File": "drivers/net/usb/Kconfig",
"FirstLine": 112,
"LineCount": 20
}Results: {
"Contents": " 112:\t\n 113:\tconfig USB_LAN78XX\n 114:\t\ttristate \"Microchip LAN78XX Based USB Ethernet Adapters\"\n 115:\t\tselect MII\n 116:\t\tselect PHYLINK\n 117:\t\tselect MICROCHIP_PHY\n 118:\t\tselect CRC32\n 119:\t\timply NET_SELFTESTS\n 120:\t\thelp\n 121:\t\t This option adds support for Microchip LAN78XX based USB 2\n 122:\t\t \u0026 USB 3 10/100/1000 Ethernet adapters.\n 123:\t\t LAN7800 : USB 3 to 10/100/1000 Ethernet adapter\n 124:\t\t LAN7850 : USB 2 to 10/100/1000 Ethernet adapter\n 125:\t\t LAN7801 : USB 3 to 10/100/1000 Ethernet adapter (MAC only)\n 126:\t\n 127:\t\t Proper PHY driver is required for LAN7801.\n 128:\t\n 129:\t\t To compile this driver as a module, choose M here: the\n 130:\t\t module will be called lan78xx.\n 131:\t\n"
} |
| 57/2 | 2026/09/26 22:54 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 94730 output: 30 thoughts: 65 Thoughts: Here's my summary, as I see it: **Digging into PHYLINK Dependencies** Okay, so I'm looking at these network driver configurations, `USB_NET_AX8817X` and `USB_LAN78XX`, and it's immediately jumping out that they both *depend* on `PHYLINK`. That means, if either of those drivers are enabled in the build, `PHYLINK` *has* to be enabled as well. That's a key piece of information. Since I'm working with these configurations, and trying to understand their dependencies, it's time to go straight to the source. Let's dive into `drivers/net/phy/Kconfig` and see exactly *what* `PHYLINK` is all about, and what other dependencies it might have. This should tell me more about its purpose in the network stack. |
| 58/2 | 2026/09/26 22:54 | tool | grepper |
0mArgs:{
"Expression": "config PHYLINK",
"PathPrefix": "drivers/net/phy/"
}Results: {
"Output": "drivers/net/phy/Kconfig-5-\ndrivers/net/phy/Kconfig:6:config PHYLINK\ndrivers/net/phy/Kconfig-7-\ttristate\n"
} |
| 59/2 | 2026/09/26 22:54 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 95074 output: 34 thoughts: 14 Thoughts: Here's my analysis of the `drivers/net/phy/Kconfig` file: **Diving into the PHY Kconfig File** Okay, let's get down to it. I'm going to start by taking a close look at `drivers/net/phy/Kconfig`. This is where all the configuration options related to Physical Layer (PHY) devices for network interfaces are defined, right? I expect to see the standard Kconfig structure: various `config` symbols, maybe some `menuconfig` blocks to group related options logically, dependencies based on other hardware or driver choices, and hopefully decent help text to explain each option clearly. I'm specifically looking for how the drivers for various PHYs are integrated into the overall network stack configuration. It is likely, the config file will contain conditional dependencies. I'm especially interested in how the various PHY driver options are structured. Are they organized by PHY vendor, interface type (e.g., SGMII, RGMII), specific PHY model, or some combination? I expect to also find options for supporting different features of the PHY, like link speed negotiation, autonegotiation control, and power management. My expectation is that the Kconfig file contains the structure that defines which PHY drivers can be selected, and how they relate to the underlying network interface drivers. Let's see how well it's all put together. |
| 60/2 | 2026/09/26 22:54 | tool | read-file |
0mArgs:{
"File": "drivers/net/phy/Kconfig",
"FirstLine": 1,
"LineCount": 25
}Results: {
"Contents": " 1:\t# SPDX-License-Identifier: GPL-2.0-only\n 2:\t#\n 3:\t# PHY Layer Configuration\n 4:\t#\n 5:\t\n 6:\tconfig PHYLINK\n 7:\t\ttristate\n 8:\t\tselect PHYLIB\n 9:\t\tselect SWPHY\n 10:\t\thelp\n 11:\t\t PHYlink models the link between the PHY and MAC, allowing fixed\n 12:\t\t configuration links, PHYs, and Serdes links with MAC level\n 13:\t\t autonegotiation modes.\n 14:\t\n 15:\tmenuconfig PHYLIB\n 16:\t\ttristate \"PHY Device support and infrastructure\"\n 17:\t\thelp\n 18:\t\t Ethernet controllers are usually attached to PHY\n 19:\t\t devices. This option provides infrastructure for\n 20:\t\t managing PHY devices.\n 21:\t\n 22:\tif PHYLIB\n 23:\t\n 24:\tconfig SWPHY\n 25:\t\tbool\n"
} |
| 61/2 | 2026/09/26 22:54 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 95678 output: 28 thoughts: 170 Thoughts: Okay, let's see. First, I need to double-check something. **Exploring Phylink Usage Across Drivers** I'm trying to get a comprehensive understanding of where `phylink` is being utilized within this system. I initially focused on the immediate context, but now I'm thinking, "Hold on, let's cast a wider net". My first instinct is to see if any other drivers directly utilize `phylink`. And then, what about `fbnic`? That's the Meta NIC driver, right? Ah, but that's a PCIe NIC, so likely less relevant to the current investigation. My mind then wanders. What about `dsa_loop`? That rings a bell. I need to make sure I'm considering all relevant scenarios. I'm thinking, "Let's check the code." Is it CONFIG'd? Okay, `CONFIG_NET_DSA_LOOP`. Good, good. I’ll dive into `dsa_loop`. The critical question is, does `dsa_loop` itself make use of `phylink` internally? I can't just assume. Looking at `dsa_loop.c`... ah, yes! `dsa_loop_phylink_get_caps`... it's in the `dsa_loop_driver` structure! That's a good find. And now I’m also remembering the DSA core, that is `net/dsa/user.c` and `net/dsa/port.c`. Yes, I'm pretty sure DSA core does use `phylink`! I can almost *feel* it. I'm going to look into `net/dsa/Kconfig`. Does DSA select PHYLINK? I need to verify that dependency explicitly. |
| 62/2 | 2026/09/26 22:54 | tool | grepper |
0mArgs:{
"Expression": "PHYLINK",
"PathPrefix": "net/dsa/"
}Results: {
"Output": "net/dsa/Kconfig=3=menuconfig NET_DSA\n--\nnet/dsa/Kconfig-9-\tselect NET_SWITCHDEV\nnet/dsa/Kconfig:10:\tselect PHYLINK\nnet/dsa/Kconfig-11-\tselect NET_DEVLINK\n--\nnet/dsa/port.c=1617=int dsa_port_phylink_create(struct dsa_port *dp)\n--\nnet/dsa/port.c-1649-\tif (IS_ERR(pl)) {\nnet/dsa/port.c:1650:\t\tpr_err(\"error creating PHYLINK: %ld\\n\", PTR_ERR(pl));\nnet/dsa/port.c-1651-\t\treturn PTR_ERR(pl);\n--\nnet/dsa/port.c=1665=static int dsa_shared_port_phylink_register(struct dsa_port *dp)\n--\nnet/dsa/port.c-1671-\tdp-\u003epl_config.dev = ds-\u003edev;\nnet/dsa/port.c:1672:\tdp-\u003epl_config.type = PHYLINK_DEV;\nnet/dsa/port.c-1673-\n--\nnet/dsa/user.c=2654=static int dsa_user_phy_setup(struct net_device *user_dev)\n--\nnet/dsa/user.c-2662-\tdp-\u003epl_config.dev = \u0026user_dev-\u003edev;\nnet/dsa/user.c:2663:\tdp-\u003epl_config.type = PHYLINK_NETDEV;\nnet/dsa/user.c-2664-\nnet/dsa/user.c-2665-\t/* The get_fixed_state callback takes precedence over polling the\nnet/dsa/user.c:2666:\t * link GPIO in PHYLINK (see phylink_get_fixed_state). Only set\nnet/dsa/user.c-2667-\t * this if the switch provides such a callback.\n"
} |
| 63/2 | 2026/09/26 22:54 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 96781 output: 23 thoughts: 850 Thoughts: **Analyzing Reachability and Focus Symbols for Phylink and stmmac** Okay, so `NET_DSA` selects `PHYLINK`, and that software switch driver `NET_DSA_LOOP` in `drivers/net/dsa/dsa_loop.c` creates switch ports. Good to know. And USB devices like `USB_NET_AX8817X` and `USB_LAN78XX` also use `PHYLINK`, which is relevant since those USB devices are standard emulation targets in syzkaller. Now, I've got to understand the impact of the changes in `phylink.c`. Let's see... the modified functions are `phylink_stop`, `phylink_suspend`, `phylink_resume`, `phylink_speed_down`, and a static function `phylink_restore_suspend_speed`. Hold on, there are changes related to `stmmac` too? Why is `phylink.c` being changed along with `stmmac`? Aha! `stmmac` was calling those `phylink` functions: `phylink_speed_down`, `phylink_speed_up`, `phylink_suspend`, `phylink_resume`, and `phylink_stop`. The patch series refactored the `stmmac` datapath lifecycle, and that's why `phylink` was modified to handle scenarios like stopping a suspended instance, deferred link down, restoring the suspend speed etc. Makes sense. Because `phylink` is shared infrastructure, these changes affect every device using it when those functions are called. `phylink_stop` is going to impact all those devices using it. And also those devices where speed down/up or suspend/resume occur. But, are those `phylink` functions ubiquitous? No, they're specific to network devices that use `phylink` like the USB net drivers, and DSA user ports. That's a key distinction. Ubiquitous functions would be things like `kmalloc` or `mutex_lock`. So, `phylink_stop`, `phylink_speed_down`, `phylink_suspend`, and `phylink_resume` are definitely non-ubiquitous. Now, regarding the `stmmac` functions... should they be included in `FocusSymbols`? And here's the critical question: If `stmmac` isn't reachable on amd64 in QEMU, should we include its functions at all, or only the `phylink` functions? Or both? What does Section 4 say? It emphasizes avoiding ubiquitous lifecycle hot-paths and targeting specific feature logic/entrypoints. If `stmmac` is *not* reachable on amd64 in QEMU, then including its symbols is a waste of resources. The fuzzer will either target unreachable code, or get zero coverage on those symbols. But if we focus on the `phylink` symbols (`phylink_stop`, `phylink_speed_down`, `phylink_suspend`, `phylink_resume`), the fuzzer will be directed to the *reachable* code that's been modified! Let's just make sure, let's check if there is ANY chance `stmmac` is reachable on amd64. Let's see if there's any virtual or mock driver, or a platform device registered at boot. |
| 64/2 | 2026/09/26 22:54 | tool | grepper |
0mError:no matches Args: {
"Expression": "platform_device_register.*stmmac"
}Results: {
"error": "no matches"
} |
| 65/2 | 2026/09/26 22:54 | llm | patch-evaluator |
1mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 98397 output: 40 thoughts: 118 Thoughts: **Investigating stmmac Device Binding in the Absence of Device Tree** Okay, so I need to figure out how these `stmmac` drivers are getting bound, especially given that I'm on a system (like QEMU's standard x86/amd64 setup) where Device Tree isn't in play. My first thought is to dig into `stmmac_platform.c`. Let's see how that platform driver is registered – ah, there it is: `module_platform_driver(stmmac_pltfr_driver);`. Classic platform driver setup. Now, I should look inside that file, specifically `drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c`, to check for any `of_device_id` tables, that are common for binding with the correct SoC compat strings, but since I'm *not* seeing a DT, I need to consider alternate methods. ACPI has to be my next best bet; it's the standard for this kind of hardware discovery in a non-DT environment. So, let's explore whether or not ACPI is utilized for `stmmac`. |
| 66/2 | 2026/09/26 22:55 | tool | grepper |
0mError:no matches Args: {
"Expression": "acpi_device_id",
"PathPrefix": "drivers/net/ethernet/stmicro/stmmac/"
}Results: {
"error": "no matches"
} |
| 67/2 | 2026/09/26 22:55 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 98820 output: 32 thoughts: 1228 Thoughts: **WorthFuzzing Analysis: Verdict - True** Okay, so here's the breakdown of this patch and why I'm confident it's fuzz-worthy. Initially, I saw the `stmmac` involvement and thought, "Hold on, that's potentially unreachable hardware on amd64 in a standard QEMU setup." The `stmmac` driver, in this context, only seems to bind to PCI devices, and the specific IDs mentioned aren't present in the emulated environment. So, on its own, I'd lean towards "false" based on that "structurally unreachable hardware" criterion, as the various `stmmac_pci_driver`, `intel_eth_pci_driver`, and the others wouldn't be triggered. However, the key is the modification to `drivers/net/phy/phylink.c`. *That's* where things get interesting, and importantly, reachable. `phylink.c` is not some obscure piece of hardware-specific code. It's core networking, part of the PHY framework. This framework is leveraged by USB network drivers, specifically the `USB_NET_AX8817X` and `USB_LAN78XX` drivers. These drivers *are* tested in syzkaller via USB raw gadget, which I've seen in action, and also the DSA loopback mockup (`CONFIG_NET_DSA_LOOP`). This means this code is executable. The patch itself modifies crucial functions within `phylink.c`: `phylink_stop`, `phylink_speed_down`, `phylink_suspend`, and `phylink_resume`. These functions are directly involved in PHY link management, including suspend/resume cycles. This suggests that the changes are not limited to just unreachable hardware; they are directly impacting code that's used for devices that can be fuzzed. Now, let's circle back to the "WorthFuzzing" criteria. I have to apply the negative *and* positive criteria. The crucial line is "Return WorthFuzzing=false ONLY IF *all* modified code falls strictly into one or more of these categories". In this case, not *all* the modified code is unreachable. `phylink.c` is part of core subsystems (core networking), and reachable drivers (USB ethernet devices, DSA). Section 3 states "Return WorthFuzzing=true whenever the patch touches reachable executable code, including: Core Subsystems...Reachable Drivers...". Bingo! This covers it. Therefore, based on the reachability of `phylink.c` and the modifications to code within it, I'm confident in setting `WorthFuzzing=true`. For good measure, let's verify what is needed. The dependency is `CONFIG_PHYLINK`. Is this directly selectable, or selected by drivers? I checked the `Kconfig` and confirmed that `PHYLINK` is `tristate` but not directly selectable. The drivers that need it must select it. So, I must specify the drivers as well, to make sure it is enabled. I've validated that `USB_NET_AX8817X` (which depends on `USB_USBNET`, that selects `PHYLINK`), `USB_LAN78XX` and `NET_DSA_LOOP` (depends on `NET_DSA` which selects `PHYLINK`) all select `PHYLINK`. So, from a fuzzing perspective, this looks like a good target: the code changes are impactful, and the affected code is reachable through standard USB network drivers that are fuzzed. |
| 68/2 | 2026/09/26 22:55 | tool | grepper |
0mArgs:{
"Expression": "config USB_USBNET",
"PathPrefix": "drivers/net/usb/"
}Results: {
"Output": "drivers/net/usb/Kconfig=113=config USB_LAN78XX\n--\ndrivers/net/usb/Kconfig-131-\ndrivers/net/usb/Kconfig:132:config USB_USBNET\ndrivers/net/usb/Kconfig-133-\ttristate \"Multi-purpose USB Networking Framework\"\n"
} |
| 69/2 | 2026/09/26 22:55 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 100943 output: 33 thoughts: 108 Thoughts: **Examining USB Ethernet Driver Configuration** Okay, let's dive in. I need to understand the configuration options for a `USB_LAN78XX` device. First, I'll consult the kernel's Kconfig files, specifically within `drivers/net/usb/Kconfig`. Ah, there it is: `config USB_LAN78XX` – a tristate option for Microchip LAN78XX based USB Ethernet Adapters. Good, straightforward. This tells me the general scope. Now, let's see what this driver inherently depends on. It's selecting `MII`, which makes sense for the Media Independent Interface, essential for physical layer communication. It's also selecting `PHYLINK` and `MICROCHIP_PHY`, solidifying its dependency on the PHY connection and a specific Microchip PHY driver, respectively. Finally, there's `CRC32`, which indicates checksum calculations are enabled, making it more robust. Seems pretty standard for a USB-Ethernet driver. I'm also curious about another aspect, so let's briefly look into `NET_DSA_LOOP`. Let's see what's in `drivers/net/dsa/Kconfig`. |
| 70/2 | 2026/09/26 22:55 | tool | grepper |
0mArgs:{
"Expression": "config NET_DSA_LOOP",
"PathPrefix": "drivers/net/dsa/"
}Results: {
"Output": "drivers/net/dsa/Kconfig=7=config NET_DSA_BCM_SF2\n--\ndrivers/net/dsa/Kconfig-18-\ndrivers/net/dsa/Kconfig:19:config NET_DSA_LOOP\ndrivers/net/dsa/Kconfig-20-\ttristate \"DSA mock-up Ethernet switch chip support\"\n"
} |
| 71/2 | 2026/09/26 22:55 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 101439 output: 36 thoughts: 15 Thoughts: Okay, here's my interpretation of that thought process, tailored for an expert audience, and written in the first person: **Analyzing the DSA Kconfig Configuration File** Alright, let's dive into this. "drivers/net/dsa/Kconfig"...right, that's the heart of the Distributed Switch Architecture configuration within the kernel. I need to refresh my mental map of what's currently enabled/disabled, what options are available, and how they're interconnected. My goal is to understand how we define and expose the various DSA features and drivers to the user, and identify any potential new options to add. What are the key dependencies? What are the implications of enabling a particular switch type, and how does it cascade through the configuration? I need to quickly grasp the logical flow and the overall structure of the config file and ensure that my prior knowledge is still relevant. |
| 72/2 | 2026/09/26 22:55 | tool | read-file |
0mArgs:{
"File": "drivers/net/dsa/Kconfig",
"FirstLine": 18,
"LineCount": 20
}Results: {
"Contents": " 18:\t\n 19:\tconfig NET_DSA_LOOP\n 20:\t\ttristate \"DSA mock-up Ethernet switch chip support\"\n 21:\t\tselect NET_DSA_TAG_NONE\n 22:\t\tselect FIXED_PHY\n 23:\t\thelp\n 24:\t\t This enables support for a fake mock-up switch chip which\n 25:\t\t exercises the DSA APIs.\n 26:\t\n 27:\tsource \"drivers/net/dsa/hirschmann/Kconfig\"\n 28:\t\n 29:\tsource \"drivers/net/dsa/lantiq/Kconfig\"\n 30:\t\n 31:\tconfig NET_DSA_MT7530\n 32:\t\ttristate \"MediaTek MT7530 and MT7531 Ethernet switch support\"\n 33:\t\tselect NET_DSA_TAG_MTK\n 34:\t\tselect REGMAP_IRQ\n 35:\t\timply NET_DSA_MT7530_MDIO\n 36:\t\timply NET_DSA_MT7530_MMIO\n 37:\t\thelp\n"
} |
| 73/2 | 2026/09/26 22:55 | llm | patch-evaluator-compressor |
0mModel:gemini-3.7-flash Args: null Results: null Reply: ### Workspace & Patch Summary
The patch series applies changes across two main components:
1. `drivers/net/ethernet/stmicro/stmmac/*`: Major refactoring of driver lifecycle, datapath state tracking, MTU reconfiguration rollback, DMA descriptor allocation/deallocation lifecycle, and interrupt handlers.
2. `drivers/net/phy/phylink.c`: Core PHY link management changes to `phylink_stop()`, `phylink_suspend()`, `phylink_resume()`, `phylink_speed_down()`, and helper `phylink_restore_suspend_speed()`.
#### Key Structural Changes in `stmmac`:
- `struct stmmac_priv`: Changed `dma_conf` from embedded struct (`struct stmmac_dma_conf dma_conf`) to pointer (`struct stmmac_dma_conf *dma_conf`).
- Added datapath state machine:
```c
enum stmmac_datapath_state {
STMMAC_DATAPATH_DOWN,
STMMAC_DATAPATH_RUNNING,
STMMAC_DATAPATH_SUSPENDED,
STMMAC_DATAPATH_HALTED,
};
```
- Added field `bool hw_suspended;` to `struct stmmac_priv`.
- Extracted and added new functions:
- `stmmac_alloc_rx_buffer()`
- `stmmac_init_rx_buffer_desc()`
- `stmmac_prepare_rx_buffers()`
- `stmmac_reinit_dma_desc()`
- `stmmac_set_irq_state()`
- `stmmac_resume_hw()`
- `stmmac_stop_tx_queues()`
- `stmmac_quiesce()`
- `stmmac_reconfigure_mtu()`
- `stmmac_reopen()`
- `stmmac_set_queues()`
- MSI IRQ handlers (`stmmac_msi_intr_tx`, `stmmac_msi_intr_rx`) now receive `struct stmmac_channel *` instead of `struct stmmac_tx_queue *` / `struct stmmac_rx_queue *`.
#### Key Changes in `phylink.c`:
- Added `bool suspend_speed_down` to `struct phylink`.
- In `phylink_stop()`: Added handling for `PHYLINK_DISABLE_MAC_WOL` which calls `flush_work(&pl->resolve)` under RTNL lock, as well as `phylink_restore_suspend_speed(pl)`.
- In `phylink_suspend()` / `phylink_resume()` / `phylink_speed_down()`: Managed `suspend_speed_down` state and restored speed advertisements cleanly.
---
### Approaches Tried and Results
1. **`stmmac` Hardware Reachability**:
- Checked if `stmmac` is reachable via PCI or platform drivers.
- Found `CONFIG_STMMAC_PCI` with `PCI_DEVICE_ID_STMMAC_STMMAC` (0x0700:0x1108), `PCI_DEVICE_ID_SYNOPSYS_GMAC5_ID` (0x7102), and Intel glue (`dwmac-intel.c`).
- Standard QEMU does not emulate Synopsys DWMAC / STMMAC hardware by default on x86_64.
2. **Core Subsystem / Virtual Device Reachability via `phylink.c`**:
- Analyzed callers of `phylink` to check if `phylink.c` is exercised through virtual buses / emulated devices.
- Found USB network devices using phylink:
- `drivers/net/usb/asix_devices.c` (`ax88772_phylink_setup`, `ax88772_stop` -> `phylink_stop`)
- `drivers/net/usb/lan78xx.c` (`lan78xx_phylink_setup`, `lan78xx_stop` -> `phylink_stop`, `lan78xx_disconnect` -> `phylink_stop`)
- Found DSA mock / virtual drivers using phylink:
- `net/dsa/user.c` (`dsa_user_close` -> `phylink_stop`)
- `drivers/net/dsa/dsa_loop.c` (mock switch driver attached via MDIO loopback, uses `dsa_loop_phylink_get_caps`)
- Reaching `phylink_stop` via standard `ip link set <dev> down` / `close()` on emulated USB net devices (via syzkaller USB raw-gadget / `syz_usb_connect`) or DSA loop devices is directly achievable in virtualized test environments.
3. **Concurrency and Locking Review in `phylink.c`**:
- `phylink_stop()` contains `flush_work(&pl->resolve)` under `ASSERT_RTNL()`. Verified `phylink_resolve()` work handler execution and locking requirements (`state_mutex`, `phydev_mutex`).
---
### Current Theories & Decisions
- **WorthFuzzing Decision**: `true`.
- Even if `stmmac` driver itself targets specialized hardware, the patch includes non-trivial changes to core networking PHY management (`drivers/net/phy/phylink.c`), which is reachable via software-emulated devices (USB gadget netdevs like `asix`/`lan78xx` and DSA virtual devices).
- Furthermore, `phylink_stop()` introduces synchronous work flushing (`flush_work(&pl->resolve)`) under RTNL, and state changes to speed control during suspend/resume/down operations that require fuzz testing against race conditions, deadlocks, and unexpected interface transition ordering.
---
### Critical File Paths, Configs, and Symbols
#### Files:
- `drivers/net/phy/phylink.c`
- `drivers/net/ethernet/stmicro/stmmac/stmmac_main.c`
- `drivers/net/ethernet/stmicro/stmmac/stmmac_xdp.c`
- `drivers/net/ethernet/stmicro/stmmac/stmmac.h`
#### Required Kconfig Symbols (stripped of `CONFIG_`):
- `PHYLINK`
- `NET_DSA`
- `NET_DSA_LOOP`
- `USB_NET_DRIVERS`
- `USB_LAN78XX`
- `USB_NET_AX88179_178A`
- `USB_NET_ASIX`
- `STMMAC_ETH`
- `STMMAC_PCI`
#### Target FocusSymbols:
- `phylink_stop`
- `phylink_suspend`
- `phylink_resume`
- `phylink_speed_down`
- `stmmac_reconfigure_mtu`
- `stmmac_xdp_open`
- `stmmac_xdp_release`
- `stmmac_reopen`
- `stmmac_open`
- `stmmac_release`
---
### Watchouts
- Do not loop on inspecting individual SoC platform glue drivers for `stmmac` (e.g. `dwmac-sunxi`, `dwmac-stm32`).
- Target focus must remain on `phylink_*` entry points and core `stmmac_*` datapath functions.Tokens: input: 78208 output: 1715 thoughts: 1744 |
| 74/2 | 2026/09/26 22:56 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 56720 output: 41 thoughts: 0 |
| 75/2 | 2026/09/26 22:56 | tool | grepper |
0mArgs:{
"Expression": "CONFIG_STMMAC_ETH",
"PathPrefix": "drivers/net/ethernet/stmicro/stmmac/"
}Results: {
"Output": "drivers/net/ethernet/stmicro/stmmac/Makefile-1-# SPDX-License-Identifier: GPL-2.0\ndrivers/net/ethernet/stmicro/stmmac/Makefile:2:obj-$(CONFIG_STMMAC_ETH) += stmmac.o\ndrivers/net/ethernet/stmicro/stmmac/Makefile-3-stmmac-objs:= stmmac_main.o stmmac_ethtool.o stmmac_mdio.o ring_mode.o\t\\\n"
} |
| 76/2 | 2026/09/26 22:56 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 56895 output: 42 thoughts: 0 |
| 77/2 | 2026/09/26 22:56 | tool | read-file |
0mArgs:{
"File": "drivers/net/ethernet/stmicro/stmmac/Kconfig",
"FirstLine": 1,
"LineCount": 40
}Results: {
"Contents": " 1:\t# SPDX-License-Identifier: GPL-2.0-only\n 2:\tconfig STMMAC_ETH\n 3:\t\ttristate \"STMicroelectronics Multi-Gigabit Ethernet driver\"\n 4:\t\tdepends on HAS_IOMEM \u0026\u0026 HAS_DMA\n 5:\t\tdepends on PTP_1588_CLOCK_OPTIONAL\n 6:\t\tdepends on ETHTOOL_NETLINK\n 7:\t\tselect MII\n 8:\t\tselect PCS_XPCS\n 9:\t\tselect PAGE_POOL\n 10:\t\tselect PHYLINK\n 11:\t\tselect CRC32\n 12:\t\tselect RESET_CONTROLLER\n 13:\t\tselect NET_DEVLINK\n 14:\t\thelp\n 15:\t\t This is the driver for the Ethernet IPs built around a\n 16:\t\t Synopsys IP Core.\n 17:\t\n 18:\tif STMMAC_ETH\n 19:\t\n 20:\tconfig STMMAC_SELFTESTS\n 21:\t\tbool \"Support for STMMAC Selftests\"\n 22:\t\tdepends on INET\n 23:\t\tdefault n\n 24:\t\thelp\n 25:\t\t This adds support for STMMAC Selftests using ethtool. Enable this\n 26:\t\t feature if you are facing problems with your HW and submit the test\n 27:\t\t results to the netdev Mailing List.\n 28:\t\n 29:\tconfig STMMAC_PLATFORM\n 30:\t\ttristate \"STMMAC Platform bus support\"\n 31:\t\tselect MFD_SYSCON\n 32:\t\tdefault y\n 33:\t\thelp\n 34:\t\t This selects the platform specific bus support for the stmmac driver.\n 35:\t\t This is the driver used on several SoCs:\n 36:\t\t STi, Allwinner, Amlogic Meson, Altera SOCFPGA.\n 37:\t\n 38:\t\t If you have a controller with this interface, say Y or M here.\n 39:\t\n 40:\t\t If unsure, say N.\n"
} |
| 78/2 | 2026/09/26 22:56 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 57466 output: 344 thoughts: 732 Thoughts: **My Evaluation of This Patch Series**
Okay, let's break this down systematically. First, **reachability**. The key question is whether this code touches executable parts of the kernel. Absolutely, it does! We have two key areas to consider:
* `drivers/net/phy/phylink.c`: This is a fundamental network infrastructure component. Importantly, it's used by virtual or emulated network drivers, like those for USB network adapters (`lan78xx`, `asix`, `ax88772`). Syzkaller, with its Raw-Gadget, dummy_hcd, and USB emulation capabilities, can definitely exercise this code path. Furthermore, DSA drivers (`net/dsa/user.c`, `drivers/net/dsa/dsa_loop.c`) and their associated mock switch driver also use phylink.
* The changes to `phylink.c` aren't cosmetic; they introduce *significant* modifications. We're talking about new state tracking (`suspend_speed_down`), changes to lifecycle functions like `phylink_stop()`, `phylink_suspend()`, `phylink_resume()`, `phylink_speed_down()`, and critically, the introduction of work flushing (`flush_work(&pl->resolve)`) under the RTNL lock. This is a big red flag for potential concurrency issues.
* While the `stmmac` driver itself might only be compiled in specific configurations (`STMMAC_ETH`, `STMMAC_PCI`), the fact that phylink is *definitely* reachable via USB and DSA means that's less of a concern.
Therefore, reachability is confirmed. I'm setting `WorthFuzzing = true`.
Next, the **EnableConfigs** are important. To make sure this code is built and tested, we need the following:
* `PHYLINK`
* `USB_NET_DRIVERS`
* `USB_USBNET`
* `USB_NET_AX8817X`
* `USB_LAN78XX`
* `NET_DSA`
* `NET_DSA_LOOP`
* `STMMAC_ETH`
* `STMMAC_PCI`
* `STMMAC_PLATFORM`
The **FocusSymbols** are our direct targets. Based on the code changes:
* In `phylink.c`, we have modifications to:
* `phylink_stop`
* `phylink_suspend`
* `phylink_resume`
* `phylink_speed_down`
* In `stmmac_main.c` we also see new functions to consider:
* `stmmac_reconfigure_mtu`
* `stmmac_xdp_open`
* `stmmac_xdp_release`
* `stmmac_reopen`
* `stmmac_open`
* `stmmac_release`
* `stmmac_hw_setup`
* `stmmac_init_dma_engine`
Finally, the **Reasoning** behind this decision: This patch is definitely worth fuzzing because it touches core phylink lifecycle state and locking (especially the `flush_work` within the RTNL lock in `phylink_stop`). It also includes important logic for suspend/resume and speed control. The accessibility via software-emulated USB network devices (like `lan78xx`, `ax8817x`) and DSA mock devices makes it an ideal target. The changes to MTU rollback, datapath state, and DMA descriptor handling within `stmmac` adds further motivation.
Okay, let's call `set-results`.
|
| 79/2 | 2026/09/26 22:56 | tool | set-results |
0mArgs:{
"EnableConfigs": [
"PHYLINK",
"USB_NET_DRIVERS",
"USB_USBNET",
"USB_NET_AX8817X",
"USB_LAN78XX",
"NET_DSA",
"NET_DSA_LOOP",
"STMMAC_ETH",
"STMMAC_PCI"
],
"FocusSymbols": [
"phylink_stop",
"phylink_suspend",
"phylink_resume",
"phylink_speed_down",
"stmmac_reconfigure_mtu",
"stmmac_xdp_open",
"stmmac_xdp_release",
"stmmac_reopen",
"stmmac_open",
"stmmac_release"
],
"Reasoning": "The patch modifies both the stmmac network driver and the core phylink subsystem (drivers/net/phy/phylink.c). Phylink is reachable in standard virtualized fuzzing environments via software-emulated USB network adapters (such as lan78xx and asix/ax8817x through USB Raw-Gadget) and DSA loop mock switch devices. The modifications introduce new state management (suspend_speed_down), flush work synchronously under RTNL lock in phylink_stop, and alter speed control transitions across suspend/resume/down paths. In addition, extensive refactoring of the stmmac datapath state machine, DMA descriptor allocation, and MTU rollback logic makes this series worth fuzzing.",
"WorthFuzzing": true
}Results: {
"EnableConfigs": [
"PHYLINK",
"USB_NET_DRIVERS",
"USB_USBNET",
"USB_NET_AX8817X",
"USB_LAN78XX",
"NET_DSA",
"NET_DSA_LOOP",
"STMMAC_ETH",
"STMMAC_PCI"
],
"FocusSymbols": [
"phylink_stop",
"phylink_suspend",
"phylink_resume",
"phylink_speed_down",
"stmmac_reconfigure_mtu",
"stmmac_xdp_open",
"stmmac_xdp_release",
"stmmac_reopen",
"stmmac_open",
"stmmac_release"
],
"Reasoning": "The patch modifies both the stmmac network driver and the core phylink subsystem (drivers/net/phy/phylink.c). Phylink is reachable in standard virtualized fuzzing environments via software-emulated USB network adapters (such as lan78xx and asix/ax8817x through USB Raw-Gadget) and DSA loop mock switch devices. The modifications introduce new state management (suspend_speed_down), flush work synchronously under RTNL lock in phylink_stop, and alter speed control transitions across suspend/resume/down paths. In addition, extensive refactoring of the stmmac datapath state machine, DMA descriptor allocation, and MTU rollback logic makes this series worth fuzzing.",
"WorthFuzzing": true
} |
| 80/1 | 2026/09/26 22:56 | action | If |
1mArgs:{
"WorthFuzzing": true
}Results: null |
| 81/2 | 2026/09/26 22:56 | agent | kmsan-evaluator |
1mModel:core Args: null Results: {
"KMSANReasoning": "The patch refactors datapath lifecycle management, suspend/resume handling, MTU reconfiguration, and phylink PM speed control in the stmmac network driver and phylink subsystem. Specifically:\n1. `priv-\u003edma_conf` is converted from an embedded struct to a dynamically allocated pointer allocated via `kzalloc_obj()`, ensuring all fields are zero-initialized.\n2. Newly added struct members (`datapath`, `hw_suspended` in `stmmac_priv`, and `suspend_speed_down` in `phylink`) are members of zero-initialized structs allocated via `alloc_etherdev_mqs` and `kzalloc`.\n3. All local variables introduced in helper functions (e.g., `stmmac_reconfigure_mtu`, `stmmac_resume_hw`, `stmmac_set_irq_state`, `stmmac_reopen`) are explicitly initialized before use.\n4. No uninitialized memory, padding, or uninitialized kernel buffers are copied to user space or used in conditional logic.\n5. The potential bug risks introduced by this patch involve pointer lifecycle/lifetime management (e.g., UAF or double-free of DMA configurations during error unwinding or reconfiguration), NULL pointer dereferences, locking/NAPI concurrency, or DMA descriptor state handling. These bug classes are effectively caught by standard debugging tools (KASAN, LOCKDEP, and page allocation tracking).\n\nBecause no uninitialized memory usage or info-leak risks are introduced, a dedicated KMSAN fuzzing session is not warranted.",
"NeedsKMSAN": false
}Instruction: You are an expert Linux kernel security engineer specializing in kernel memory
error detectors (KASAN and KMSAN). Your job is to review the provided patch series and
determine if the code changes justify spawning a dedicated KMSAN (KernelMemorySanitizer)
fuzzing session in addition to standard KASAN fuzzing.
CRITICAL DISTINCTION BETWEEN KASAN AND KMSAN:
- Standard KASAN kernel builds (upstream-apparmor-kasan.config) already enable
a comprehensive suite of debugging tools and sanitizers, including KASAN
(out-of-bounds accesses, use-after-free, double free, invalid free), LOCKDEP
(locking bugs and deadlocks), UB-sanitizers, and memory corruption checks.
- KMSAN (KernelMemorySanitizer) detects reads of UNINITIALIZED memory (stack, heap,
or page allocations) and kernel-to-user memory info-leaks.
Rule: THERE IS NO SENSE IN RUNNING A KMSAN SESSION IF A BUG CAN BE CAUGHT BY KASAN,
LOCKDEP, OR OTHER STANDARD BUG DETECTORS.
A dedicated KMSAN fuzzing session incurs significant resource costs. You must ONLY
set NeedsKMSAN=true if the code changes introduce or expose UNINITIALIZED MEMORY risks
that are detected ONLY by KMSAN.
Look holistically at the patch series and surrounding code. Even if no direct
uninitialized field accesses or new buffer allocations are added in the diff itself,
a patch may alter control flow, bounds checking, or data length calculations in ways
that change how the rest of the code operates on existing buffers (e.g. allowing
uninitialized stack/heap memory to be read, copied to user space, or used in control
flow). Do not hesitate to use your code access tools to inspect the surrounding code,
called functions, and callers.
Set NeedsKMSAN=true ONLY IF the patch introduces or modifies:
1. Kernel structures sent to user space (via copy_to_user, put_user, netlink skb
attributes, ioctl output arguments, socket options, or BPF buffers) where fields
or structure padding might not be fully initialized/zeroed.
2. Conditional logic or branching that depends on potentially uninitialized variables
or struct fields.
3. Allocation or initialization of complex data structures where uninitialized fields
could be read later in reachable code paths.
4. Bounds checks, lengths, or logic in a way that allows surrounding code to access
uninitialized bytes of existing buffers.
Set NeedsKMSAN=false IF:
- The code changes primarily risk out-of-bounds access, array overflows, NULL pointer
dereferences, locking deadlocks, or use-after-free bugs (these are already caught
by KASAN, LOCKDEP, or standard bug detectors).
- All stack/heap structures touched or introduced by the patch are fully zeroed
or initialized (e.g. using = {0}, memset, kzalloc) before being read or copied.
- The patch does not introduce any risk of uninitialized memory usage or info-leaks.
Use your code access tools to inspect the surrounding code if necessary, then provide
detailed KMSANReasoning contrasting KASAN vs KMSAN applicability for this patch.
Prefer calling several tools at the same time to save round-trips.
Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.
Prompt: Target architecture: amd64
For your convenience, here is the diff of the changes:
commit 925615988696cb7f8488eeefff14dfb3ead46259
Author: syz-cluster <triage@syzkaller.com>
Date: Sat Sep 26 22:50:45 2026 +0000
syz-cluster: applied patch under review
diff --git a/drivers/net/ethernet/stmicro/stmmac/chain_mode.c b/drivers/net/ethernet/stmicro/stmmac/chain_mode.c
index 66025e2509e91..65243c5e539e4 100644
--- a/drivers/net/ethernet/stmicro/stmmac/chain_mode.c
+++ b/drivers/net/ethernet/stmicro/stmmac/chain_mode.c
@@ -48,7 +48,7 @@ static int jumbo_frm(struct stmmac_tx_queue *tx_q, struct sk_buff *skb,
while (len != 0) {
tx_q->tx_skbuff[entry] = NULL;
- entry = STMMAC_NEXT_ENTRY(entry, priv->dma_conf.dma_tx_size);
+ entry = STMMAC_NEXT_ENTRY(entry, priv->dma_conf->dma_tx_size);
desc = tx_q->dma_tx + entry;
if (len > bmax) {
@@ -137,7 +137,7 @@ static void refill_desc3(struct stmmac_rx_queue *rx_q, struct dma_desc *p)
*/
p->des3 = cpu_to_le32((unsigned int)(rx_q->dma_rx_phy +
(((rx_q->dirty_rx) + 1) %
- priv->dma_conf.dma_rx_size) *
+ priv->dma_conf->dma_rx_size) *
sizeof(struct dma_desc)));
}
@@ -154,7 +154,7 @@ static void clean_desc3(struct stmmac_tx_queue *tx_q, struct dma_desc *p)
*/
p->des3 = cpu_to_le32((unsigned int)((tx_q->dma_tx_phy +
((tx_q->dirty_tx + 1) %
- priv->dma_conf.dma_tx_size))
+ priv->dma_conf->dma_tx_size))
* sizeof(struct dma_desc)));
}
diff --git a/drivers/net/ethernet/stmicro/stmmac/ring_mode.c b/drivers/net/ethernet/stmicro/stmmac/ring_mode.c
index d2f0c321661d9..0299d6a6c32bf 100644
--- a/drivers/net/ethernet/stmicro/stmmac/ring_mode.c
+++ b/drivers/net/ethernet/stmicro/stmmac/ring_mode.c
@@ -52,7 +52,7 @@ static int jumbo_frm(struct stmmac_tx_queue *tx_q, struct sk_buff *skb,
stmmac_prepare_tx_desc(priv, desc, 1, bmax, csum,
STMMAC_RING_MODE, 0, false, skb->len);
tx_q->tx_skbuff[entry] = NULL;
- entry = STMMAC_NEXT_ENTRY(entry, priv->dma_conf.dma_tx_size);
+ entry = STMMAC_NEXT_ENTRY(entry, priv->dma_conf->dma_tx_size);
if (priv->extend_desc)
desc = (struct dma_desc *)(tx_q->dma_etx + entry);
@@ -102,7 +102,7 @@ static void refill_desc3(struct stmmac_rx_queue *rx_q, struct dma_desc *p)
struct stmmac_priv *priv = rx_q->priv_data;
/* Fill DES3 in case of RING mode */
- if (priv->dma_conf.dma_buf_sz == BUF_SIZE_16KiB)
+ if (priv->dma_conf->dma_buf_sz == BUF_SIZE_16KiB)
p->des3 = cpu_to_le32(le32_to_cpu(p->des2) + BUF_SIZE_8KiB);
}
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h
index 7582fca637419..8ebf407895689 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h
@@ -258,6 +258,17 @@ struct stmmac_msi {
char int_name_tx_irq[MTL_MAX_TX_QUEUES][IFNAMSIZ + 18];
};
+enum stmmac_datapath_state {
+ /* No IRQs or DMA allocations owned by a successful open. */
+ STMMAC_DATAPATH_DOWN,
+ /* Resources allocated, NAPI enabled. */
+ STMMAC_DATAPATH_RUNNING,
+ /* Resources retained, NAPI and DMA stopped; also after failed resume. */
+ STMMAC_DATAPATH_SUSPENDED,
+ /* Failed MTU rollback: rings retained, but no IRQs or running NAPI. */
+ STMMAC_DATAPATH_HALTED,
+};
+
struct stmmac_priv {
/* Frequently used values are kept adjacent for cache effect */
u32 tx_coal_frames[MTL_MAX_TX_QUEUES];
@@ -280,7 +291,11 @@ struct stmmac_priv {
int (*hwif_quirks)(struct stmmac_priv *priv);
struct mutex lock;
- struct stmmac_dma_conf dma_conf;
+ struct stmmac_dma_conf *dma_conf;
+ /* IRQ/DMA ownership and NAPI state, serialized by RTNL. */
+ enum stmmac_datapath_state datapath;
+ /* Core sleep sequence completed, independently of datapath ownership. */
+ bool hw_suspended;
/* Generic channel for NAPI */
struct stmmac_channel channel[STMMAC_CH_MAX];
@@ -424,10 +439,6 @@ static inline bool stmmac_xdp_is_enabled(struct stmmac_priv *priv)
return !!priv->xdp_prog;
}
-void stmmac_disable_rx_queue(struct stmmac_priv *priv, u32 queue);
-void stmmac_enable_rx_queue(struct stmmac_priv *priv, u32 queue);
-void stmmac_disable_tx_queue(struct stmmac_priv *priv, u32 queue);
-void stmmac_enable_tx_queue(struct stmmac_priv *priv, u32 queue);
int stmmac_xsk_wakeup(struct net_device *dev, u32 queue, u32 flags);
struct timespec64 stmmac_calc_tas_basetime(ktime_t old_base_time,
ktime_t current_time,
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c
index 1be5310ca766c..994350fcdbfa8 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c
@@ -396,8 +396,8 @@ static void stmmac_get_ringparam(struct net_device *netdev,
ring->rx_max_pending = DMA_MAX_RX_SIZE;
ring->tx_max_pending = DMA_MAX_TX_SIZE;
- ring->rx_pending = priv->dma_conf.dma_rx_size;
- ring->tx_pending = priv->dma_conf.dma_tx_size;
+ ring->rx_pending = priv->dma_conf->dma_rx_size;
+ ring->tx_pending = priv->dma_conf->dma_tx_size;
}
static int stmmac_set_ringparam(struct net_device *netdev,
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index 3f34d491c9592..1a552455a633d 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -79,7 +79,7 @@ static int phyaddr = -1;
module_param(phyaddr, int, 0444);
MODULE_PARM_DESC(phyaddr, "Physical device address");
-#define STMMAC_TX_THRESH(x) ((x)->dma_conf.dma_tx_size / 4)
+#define STMMAC_TX_THRESH(x) ((x)->dma_conf->dma_tx_size / 4)
/* Limit to make sure XDP TX and slow path can coexist */
#define STMMAC_XSK_TX_BUDGET_MAX 256
@@ -297,7 +297,7 @@ static void stmmac_disable_all_queues(struct stmmac_priv *priv)
/* synchronize_rcu() needed for pending XDP buffers to drain */
for (queue = 0; queue < rx_queues_cnt; queue++) {
- rx_q = &priv->dma_conf.rx_queue[queue];
+ rx_q = &priv->dma_conf->rx_queue[queue];
if (rx_q->xsk_pool) {
synchronize_rcu();
break;
@@ -356,10 +356,10 @@ static void print_pkt(unsigned char *buf, int len)
static inline u32 stmmac_tx_avail(struct stmmac_priv *priv, u32 queue)
{
- struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue];
+ struct stmmac_tx_queue *tx_q = &priv->dma_conf->tx_queue[queue];
return CIRC_SPACE(tx_q->cur_tx, tx_q->dirty_tx,
- priv->dma_conf.dma_tx_size);
+ priv->dma_conf->dma_tx_size);
}
static size_t stmmac_get_tx_desc_size(struct stmmac_priv *priv,
@@ -438,7 +438,7 @@ static void stmmac_set_queue_rx_buf_size(struct stmmac_priv *priv,
if (rx_q->xsk_pool && rx_q->buf_alloc_num)
buf_size = xsk_pool_get_rx_frame_size(rx_q->xsk_pool);
else
- buf_size = priv->dma_conf.dma_buf_sz;
+ buf_size = priv->dma_conf->dma_buf_sz;
stmmac_set_dma_bfsize(priv, priv->ioaddr, buf_size, chan);
}
@@ -450,10 +450,10 @@ static void stmmac_set_queue_rx_buf_size(struct stmmac_priv *priv,
*/
static inline u32 stmmac_rx_dirty(struct stmmac_priv *priv, u32 queue)
{
- struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue];
+ struct stmmac_rx_queue *rx_q = &priv->dma_conf->rx_queue[queue];
return CIRC_CNT(rx_q->cur_rx, rx_q->dirty_rx,
- priv->dma_conf.dma_rx_size);
+ priv->dma_conf->dma_rx_size);
}
static bool stmmac_eee_tx_busy(struct stmmac_priv *priv)
@@ -463,7 +463,7 @@ static bool stmmac_eee_tx_busy(struct stmmac_priv *priv)
/* check if all TX queues have the work finished */
for (queue = 0; queue < tx_cnt; queue++) {
- struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue];
+ struct stmmac_tx_queue *tx_q = &priv->dma_conf->tx_queue[queue];
if (tx_q->dirty_tx != tx_q->cur_tx)
return true; /* still unfinished work */
@@ -1629,24 +1629,10 @@ static void stmmac_clear_descriptors(struct stmmac_priv *priv,
stmmac_clear_tx_descriptors(priv, dma_conf, queue);
}
-/**
- * stmmac_init_rx_buffers - init the RX descriptor buffer.
- * @priv: driver private structure
- * @dma_conf: structure to take the dma data
- * @p: descriptor pointer
- * @i: descriptor index
- * @flags: gfp flag
- * @queue: RX queue index
- * Description: this function is called to allocate a receive buffer, perform
- * the DMA mapping and init the descriptor.
- */
-static int stmmac_init_rx_buffers(struct stmmac_priv *priv,
- struct stmmac_dma_conf *dma_conf,
- struct dma_desc *p,
- int i, gfp_t flags, u32 queue)
+static int stmmac_alloc_rx_buffer(struct stmmac_priv *priv,
+ struct stmmac_rx_queue *rx_q,
+ struct stmmac_rx_buffer *buf)
{
- struct stmmac_rx_queue *rx_q = &dma_conf->rx_queue[queue];
- struct stmmac_rx_buffer *buf = &rx_q->buf_pool[i];
gfp_t gfp = (GFP_ATOMIC | __GFP_NOWARN);
if (priv->dma_cap.host_dma_width <= 32)
@@ -1663,19 +1649,49 @@ static int stmmac_init_rx_buffers(struct stmmac_priv *priv,
buf->sec_page = page_pool_alloc_pages(rx_q->page_pool, gfp);
if (!buf->sec_page)
return -ENOMEM;
-
buf->sec_addr = page_pool_get_dma_addr(buf->sec_page);
- stmmac_set_desc_sec_addr(priv, p, buf->sec_addr, true);
- } else {
- buf->sec_page = NULL;
- stmmac_set_desc_sec_addr(priv, p, buf->sec_addr, false);
}
+ return 0;
+}
+
+static void stmmac_init_rx_buffer_desc(struct stmmac_priv *priv,
+ struct stmmac_dma_conf *dma_conf,
+ struct dma_desc *p,
+ struct stmmac_rx_buffer *buf)
+{
+ if (buf->sec_page)
+ buf->sec_addr = page_pool_get_dma_addr(buf->sec_page);
+ stmmac_set_desc_sec_addr(priv, p, buf->sec_addr, !!buf->sec_page);
buf->addr = page_pool_get_dma_addr(buf->page) + buf->page_offset;
stmmac_set_desc_addr(priv, p, buf->addr);
if (dma_conf->dma_buf_sz == BUF_SIZE_16KiB)
stmmac_init_desc3(priv, p);
+}
+
+/**
+ * stmmac_init_rx_buffers - allocate a receive buffer and init its descriptor
+ * @priv: driver private structure
+ * @dma_conf: structure to take the dma data
+ * @p: descriptor pointer
+ * @i: descriptor index
+ * @flags: gfp flag
+ * @queue: RX queue index
+ */
+static int stmmac_init_rx_buffers(struct stmmac_priv *priv,
+ struct stmmac_dma_conf *dma_conf,
+ struct dma_desc *p,
+ int i, gfp_t flags, u32 queue)
+{
+ struct stmmac_rx_queue *rx_q = &dma_conf->rx_queue[queue];
+ struct stmmac_rx_buffer *buf = &rx_q->buf_pool[i];
+ int ret;
+
+ ret = stmmac_alloc_rx_buffer(priv, rx_q, buf);
+ if (ret)
+ return ret;
+ stmmac_init_rx_buffer_desc(priv, dma_conf, p, buf);
return 0;
}
@@ -1761,6 +1777,9 @@ static void dma_free_rx_skbufs(struct stmmac_priv *priv,
struct stmmac_rx_queue *rx_q = &dma_conf->rx_queue[queue];
int i;
+ if (!rx_q->buf_pool)
+ return;
+
for (i = 0; i < dma_conf->dma_rx_size; i++)
stmmac_free_rx_buffer(priv, rx_q, i);
}
@@ -1884,17 +1903,19 @@ static int __init_dma_rx_desc_rings(struct stmmac_priv *priv,
rx_q->xsk_pool = stmmac_get_xsk_pool(priv, queue);
if (rx_q->xsk_pool) {
- WARN_ON(xdp_rxq_info_reg_mem_model(&rx_q->xdp_rxq,
- MEM_TYPE_XSK_BUFF_POOL,
- NULL));
+ ret = xdp_rxq_info_reg_mem_model(&rx_q->xdp_rxq,
+ MEM_TYPE_XSK_BUFF_POOL, NULL);
+ if (ret)
+ return ret;
netdev_info(priv->dev,
"Register MEM_TYPE_XSK_BUFF_POOL RxQ-%d\n",
queue);
xsk_pool_set_rxq_info(rx_q->xsk_pool, &rx_q->xdp_rxq);
} else {
- WARN_ON(xdp_rxq_info_reg_mem_model(&rx_q->xdp_rxq,
- MEM_TYPE_PAGE_POOL,
- rx_q->page_pool));
+ ret = xdp_rxq_info_reg_mem_model(&rx_q->xdp_rxq,
+ MEM_TYPE_PAGE_POOL, rx_q->page_pool);
+ if (ret)
+ return ret;
netdev_info(priv->dev,
"Register MEM_TYPE_PAGE_POOL RxQ-%d\n",
queue);
@@ -1956,6 +1977,8 @@ static int init_dma_rx_desc_rings(struct net_device *dev,
dma_free_rx_skbufs(priv, dma_conf, queue);
rx_q->buf_alloc_num = 0;
+ if (rx_q->xsk_pool)
+ xsk_pool_set_rxq_info(rx_q->xsk_pool, NULL);
rx_q->xsk_pool = NULL;
queue--;
@@ -2099,6 +2122,9 @@ static void dma_free_tx_skbufs(struct stmmac_priv *priv,
tx_q->xsk_frames_done = 0;
+ if (!tx_q->tx_skbuff_dma || !tx_q->tx_skbuff)
+ return;
+
for (i = 0; i < dma_conf->dma_tx_size; i++)
stmmac_free_tx_buffer(priv, dma_conf, queue, i);
@@ -2119,7 +2145,64 @@ static void stmmac_free_tx_skbufs(struct stmmac_priv *priv)
u8 queue;
for (queue = 0; queue < tx_queue_cnt; queue++)
- dma_free_tx_skbufs(priv, &priv->dma_conf, queue);
+ dma_free_tx_skbufs(priv, priv->dma_conf, queue);
+}
+
+/* NAPI is stopped, but DMA may still be using the old rings. Fill holes in
+ * the software buffer array without changing any descriptors. If allocation
+ * fails, the old rings can continue unchanged. Otherwise rollback after a
+ * reset will not need to allocate buffers.
+ */
+static int stmmac_prepare_rx_buffers(struct stmmac_priv *priv)
+{
+ struct stmmac_dma_conf *dma_conf = priv->dma_conf;
+ u32 queue, i;
+ int ret;
+
+ for (queue = 0; queue < priv->plat->rx_queues_to_use; queue++) {
+ struct stmmac_rx_queue *rx_q = &dma_conf->rx_queue[queue];
+
+ for (i = 0; i < dma_conf->dma_rx_size; i++) {
+ ret = stmmac_alloc_rx_buffer(priv, rx_q, &rx_q->buf_pool[i]);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+/* Only after a successful DMA reset, and with all RX buffers prepared. */
+static void stmmac_reinit_dma_desc(struct stmmac_priv *priv)
+{
+ struct stmmac_dma_conf *dma_conf = priv->dma_conf;
+ u32 queue, i;
+
+ stmmac_free_tx_skbufs(priv);
+ stmmac_reset_queues_param(priv);
+ init_dma_tx_desc_rings(priv->dev, dma_conf);
+ stmmac_clear_descriptors(priv, dma_conf);
+
+ for (queue = 0; queue < priv->plat->rx_queues_to_use; queue++) {
+ struct stmmac_rx_queue *rx_q = &dma_conf->rx_queue[queue];
+
+ if (rx_q->state_saved)
+ dev_kfree_skb_any(rx_q->state.skb);
+ rx_q->state.skb = NULL;
+ rx_q->state_saved = 0;
+ rx_q->rx_count_frames = 0;
+ rx_q->buf_alloc_num = dma_conf->dma_rx_size;
+
+ for (i = 0; i < dma_conf->dma_rx_size; i++)
+ stmmac_init_rx_buffer_desc(priv, dma_conf,
+ stmmac_get_rx_desc(priv, rx_q, i),
+ &rx_q->buf_pool[i]);
+
+ if (priv->descriptor_mode == STMMAC_CHAIN_MODE)
+ stmmac_mode_init(priv, stmmac_get_rx_desc(priv, rx_q, 0),
+ rx_q->dma_rx_phy, dma_conf->dma_rx_size,
+ priv->extend_desc);
+ }
}
/**
@@ -2137,10 +2220,16 @@ static void __free_dma_rx_desc_resources(struct stmmac_priv *priv,
void *addr;
/* Release the DMA RX socket buffers */
- if (rx_q->xsk_pool)
+ if (rx_q->xsk_pool) {
dma_free_rx_xskbufs(priv, dma_conf, queue);
- else
+ xsk_pool_set_rxq_info(rx_q->xsk_pool, NULL);
+ } else {
dma_free_rx_skbufs(priv, dma_conf, queue);
+ }
+ if (rx_q->state_saved)
+ dev_kfree_skb_any(rx_q->state.skb);
+ rx_q->state.skb = NULL;
+ rx_q->state_saved = 0;
rx_q->buf_alloc_num = 0;
rx_q->xsk_pool = NULL;
@@ -2153,7 +2242,8 @@ static void __free_dma_rx_desc_resources(struct stmmac_priv *priv,
size = stmmac_get_rx_desc_size(priv) * dma_conf->dma_rx_size;
- dma_free_coherent(priv->device, size, addr, rx_q->dma_rx_phy);
+ if (addr)
+ dma_free_coherent(priv->device, size, addr, rx_q->dma_rx_phy);
if (xdp_rxq_info_is_reg(&rx_q->xdp_rxq))
xdp_rxq_info_unreg(&rx_q->xdp_rxq);
@@ -2161,6 +2251,10 @@ static void __free_dma_rx_desc_resources(struct stmmac_priv *priv,
kfree(rx_q->buf_pool);
if (rx_q->page_pool)
page_pool_destroy(rx_q->page_pool);
+ rx_q->buf_pool = NULL;
+ rx_q->page_pool = NULL;
+ rx_q->dma_erx = NULL;
+ rx_q->dma_rx = NULL;
}
static void free_dma_rx_desc_resources(struct stmmac_priv *priv,
@@ -2201,10 +2295,17 @@ static void __free_dma_tx_desc_resources(struct stmmac_priv *priv,
size = stmmac_get_tx_desc_size(priv, tx_q) * dma_conf->dma_tx_size;
- dma_free_coherent(priv->device, size, addr, tx_q->dma_tx_phy);
+ if (addr)
+ dma_free_coherent(priv->device, size, addr, tx_q->dma_tx_phy);
kfree(tx_q->tx_skbuff_dma);
kfree(tx_q->tx_skbuff);
+ tx_q->tx_skbuff_dma = NULL;
+ tx_q->tx_skbuff = NULL;
+ tx_q->dma_etx = NULL;
+ tx_q->dma_entx = NULL;
+ tx_q->dma_tx = NULL;
+ tx_q->xsk_pool = NULL;
}
static void free_dma_tx_desc_resources(struct stmmac_priv *priv,
@@ -2411,6 +2512,8 @@ static int alloc_dma_desc_resources(struct stmmac_priv *priv,
return ret;
ret = alloc_dma_tx_desc_resources(priv, dma_conf);
+ if (ret)
+ free_dma_rx_desc_resources(priv, dma_conf);
return ret;
}
@@ -2609,7 +2712,7 @@ static void stmmac_dma_operation_mode(struct stmmac_priv *priv)
/* configure all channels */
for (chan = 0; chan < rx_channels_count; chan++) {
- struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[chan];
+ struct stmmac_rx_queue *rx_q = &priv->dma_conf->rx_queue[chan];
qmode = priv->plat->rx_queues_cfg[chan].mode_to_use;
@@ -2681,7 +2784,7 @@ static const struct xsk_tx_metadata_ops stmmac_xsk_tx_metadata_ops = {
static bool stmmac_xdp_xmit_zc(struct stmmac_priv *priv, u32 queue, u32 budget)
{
struct netdev_queue *nq = netdev_get_tx_queue(priv->dev, queue);
- struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue];
+ struct stmmac_tx_queue *tx_q = &priv->dma_conf->tx_queue[queue];
struct stmmac_txq_stats *txq_stats = &priv->xstats.txq_stats[queue];
bool csum = !priv->plat->tx_queues_cfg[queue].coe_unsupported;
struct xsk_buff_pool *pool = tx_q->xsk_pool;
@@ -2770,7 +2873,7 @@ static bool stmmac_xdp_xmit_zc(struct stmmac_priv *priv, u32 queue, u32 budget)
xsk_tx_metadata_to_compl(meta,
&tx_q->tx_skbuff_dma[entry].xsk_meta);
- tx_q->cur_tx = STMMAC_NEXT_ENTRY(tx_q->cur_tx, priv->dma_conf.dma_tx_size);
+ tx_q->cur_tx = STMMAC_NEXT_ENTRY(tx_q->cur_tx, priv->dma_conf->dma_tx_size);
entry = tx_q->cur_tx;
}
u64_stats_update_begin(&txq_stats->napi_syncp);
@@ -2818,7 +2921,7 @@ static void stmmac_bump_dma_threshold(struct stmmac_priv *priv, u32 chan)
static int stmmac_tx_clean(struct stmmac_priv *priv, int budget, u32 queue,
bool *pending_packets)
{
- struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue];
+ struct stmmac_tx_queue *tx_q = &priv->dma_conf->tx_queue[queue];
struct stmmac_txq_stats *txq_stats = &priv->xstats.txq_stats[queue];
unsigned int bytes_compl = 0, pkts_compl = 0;
unsigned int entry, xmits = 0, count = 0;
@@ -2831,7 +2934,7 @@ static int stmmac_tx_clean(struct stmmac_priv *priv, int budget, u32 queue,
entry = tx_q->dirty_tx;
/* Try to clean all TX complete frame in 1 shot */
- while ((entry != tx_q->cur_tx) && count < priv->dma_conf.dma_tx_size) {
+ while ((entry != tx_q->cur_tx) && count < priv->dma_conf->dma_tx_size) {
struct xdp_frame *xdpf;
struct sk_buff *skb;
struct dma_desc *p;
@@ -2938,7 +3041,7 @@ static int stmmac_tx_clean(struct stmmac_priv *priv, int budget, u32 queue,
stmmac_release_tx_desc(priv, p, priv->descriptor_mode);
- entry = STMMAC_NEXT_ENTRY(entry, priv->dma_conf.dma_tx_size);
+ entry = STMMAC_NEXT_ENTRY(entry, priv->dma_conf->dma_tx_size);
}
tx_q->dirty_tx = entry;
@@ -3006,13 +3109,13 @@ static int stmmac_tx_clean(struct stmmac_priv *priv, int budget, u32 queue,
*/
static void stmmac_tx_err(struct stmmac_priv *priv, u32 chan)
{
- struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[chan];
+ struct stmmac_tx_queue *tx_q = &priv->dma_conf->tx_queue[chan];
netif_tx_stop_queue(netdev_get_tx_queue(priv->dev, chan));
stmmac_stop_tx_dma(priv, chan);
- dma_free_tx_skbufs(priv, &priv->dma_conf, chan);
- stmmac_clear_tx_descriptors(priv, &priv->dma_conf, chan);
+ dma_free_tx_skbufs(priv, priv->dma_conf, chan);
+ stmmac_clear_tx_descriptors(priv, priv->dma_conf, chan);
stmmac_reset_tx_queue(priv, chan);
stmmac_init_tx_chan(priv, priv->ioaddr, priv->plat->dma_cfg,
tx_q->dma_tx_phy, chan);
@@ -3073,8 +3176,8 @@ static int stmmac_napi_check(struct stmmac_priv *priv, u32 chan, u32 dir)
{
int status = stmmac_dma_interrupt_status(priv, priv->ioaddr,
&priv->xstats, chan, dir);
- struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[chan];
- struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[chan];
+ struct stmmac_rx_queue *rx_q = &priv->dma_conf->rx_queue[chan];
+ struct stmmac_tx_queue *tx_q = &priv->dma_conf->tx_queue[chan];
struct stmmac_channel *ch = &priv->channel[chan];
struct napi_struct *rx_napi;
struct napi_struct *tx_napi;
@@ -3255,12 +3358,13 @@ static int stmmac_prereset_configure(struct stmmac_priv *priv)
/**
* stmmac_init_dma_engine - DMA init.
* @priv: driver private structure
+ * @reinit: rebuild the retained rings after a successful reset
* Description:
* It inits the DMA invoking the specific MAC/GMAC callback.
* Some DMA parameters can be passed from the platform;
* in case of these are not passed a default is kept for the MAC or GMAC.
*/
-static int stmmac_init_dma_engine(struct stmmac_priv *priv)
+static int stmmac_init_dma_engine(struct stmmac_priv *priv, bool reinit)
{
u8 rx_channels_count = priv->plat->rx_queues_to_use;
u8 tx_channels_count = priv->plat->tx_queues_to_use;
@@ -3280,6 +3384,9 @@ static int stmmac_init_dma_engine(struct stmmac_priv *priv)
return ret;
}
+ if (reinit)
+ stmmac_reinit_dma_desc(priv);
+
/* DMA Configuration */
stmmac_dma_init(priv, priv->ioaddr, priv->plat->dma_cfg);
@@ -3294,7 +3401,7 @@ static int stmmac_init_dma_engine(struct stmmac_priv *priv)
/* DMA RX Channel Configuration */
for (chan = 0; chan < rx_channels_count; chan++) {
- rx_q = &priv->dma_conf.rx_queue[chan];
+ rx_q = &priv->dma_conf->rx_queue[chan];
stmmac_init_rx_chan(priv, priv->ioaddr, priv->plat->dma_cfg,
rx_q->dma_rx_phy, chan);
@@ -3305,7 +3412,7 @@ static int stmmac_init_dma_engine(struct stmmac_priv *priv)
/* DMA TX Channel Configuration */
for (chan = 0; chan < tx_channels_count; chan++) {
- tx_q = &priv->dma_conf.tx_queue[chan];
+ tx_q = &priv->dma_conf->tx_queue[chan];
stmmac_init_tx_chan(priv, priv->ioaddr, priv->plat->dma_cfg,
tx_q->dma_tx_phy, chan);
@@ -3318,7 +3425,7 @@ static int stmmac_init_dma_engine(struct stmmac_priv *priv)
static void stmmac_tx_timer_arm(struct stmmac_priv *priv, u32 queue)
{
- struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue];
+ struct stmmac_tx_queue *tx_q = &priv->dma_conf->tx_queue[queue];
u32 tx_coal_timer = priv->tx_coal_timer[queue];
struct stmmac_channel *ch;
struct napi_struct *napi;
@@ -3386,7 +3493,7 @@ static void stmmac_init_coalesce(struct stmmac_priv *priv)
u8 chan;
for (chan = 0; chan < tx_channel_count; chan++) {
- struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[chan];
+ struct stmmac_tx_queue *tx_q = &priv->dma_conf->tx_queue[chan];
priv->tx_coal_frames[chan] = STMMAC_TX_FRAMES;
priv->tx_coal_timer[chan] = STMMAC_COAL_TX_TIMER;
@@ -3407,12 +3514,12 @@ static void stmmac_set_rings_length(struct stmmac_priv *priv)
/* set TX ring length */
for (chan = 0; chan < tx_channels_count; chan++)
stmmac_set_tx_ring_len(priv, priv->ioaddr,
- (priv->dma_conf.dma_tx_size - 1), chan);
+ (priv->dma_conf->dma_tx_size - 1), chan);
/* set RX ring length */
for (chan = 0; chan < rx_channels_count; chan++)
stmmac_set_rx_ring_len(priv, priv->ioaddr,
- (priv->dma_conf.dma_rx_size - 1), chan);
+ (priv->dma_conf->dma_rx_size - 1), chan);
}
/**
@@ -3621,12 +3728,13 @@ static bool stmmac_tso_channel_permitted(struct stmmac_priv *priv,
unsigned int chan)
{
/* TSO and TBS cannot co-exist */
- return !(priv->dma_conf.tx_queue[chan].tbs & STMMAC_TBS_AVAIL);
+ return !(priv->dma_conf->tx_queue[chan].tbs & STMMAC_TBS_AVAIL);
}
/**
* stmmac_hw_setup - setup mac in a usable state.
* @dev : pointer to the device structure.
+ * @reinit: rebuild retained descriptor rings after the DMA reset
* Description:
* this is the main function to setup the HW in a usable state because the
* dma engine is reset, the core registers are configured (e.g. AXI,
@@ -3636,7 +3744,7 @@ static bool stmmac_tso_channel_permitted(struct stmmac_priv *priv,
* 0 on success and an appropriate (-)ve integer as defined in errno.h
* file on failure.
*/
-static int stmmac_hw_setup(struct net_device *dev)
+static int stmmac_hw_setup(struct net_device *dev, bool reinit)
{
struct stmmac_priv *priv = netdev_priv(dev);
u8 rx_cnt = priv->plat->rx_queues_to_use;
@@ -3658,7 +3766,7 @@ static int stmmac_hw_setup(struct net_device *dev)
phylink_rx_clk_stop_block(priv->phylink);
/* DMA initialization and SW reset */
- ret = stmmac_init_dma_engine(priv);
+ ret = stmmac_init_dma_engine(priv, reinit);
if (ret < 0) {
phylink_rx_clk_stop_unblock(priv->phylink);
netdev_err(priv->dev, "%s: DMA engine initialization failed\n",
@@ -3739,7 +3847,7 @@ static int stmmac_hw_setup(struct net_device *dev)
/* TBS */
for (chan = 0; chan < tx_cnt; chan++) {
- struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[chan];
+ struct stmmac_tx_queue *tx_q = &priv->dma_conf->tx_queue[chan];
int enable = tx_q->tbs & STMMAC_TBS_AVAIL;
stmmac_enable_tbs(priv, priv->ioaddr, enable, chan);
@@ -3774,8 +3882,7 @@ static void stmmac_free_irq(struct net_device *dev,
for (j = irq_idx - 1; msi && j >= 0; j--) {
if (msi->tx_irq[j] > 0) {
irq_set_affinity_hint(msi->tx_irq[j], NULL);
- free_irq(msi->tx_irq[j],
- &priv->dma_conf.tx_queue[j]);
+ free_irq(msi->tx_irq[j], &priv->channel[j]);
}
}
irq_idx = priv->plat->rx_queues_to_use;
@@ -3784,8 +3891,7 @@ static void stmmac_free_irq(struct net_device *dev,
for (j = irq_idx - 1; msi && j >= 0; j--) {
if (msi->rx_irq[j] > 0) {
irq_set_affinity_hint(msi->rx_irq[j], NULL);
- free_irq(msi->rx_irq[j],
- &priv->dma_conf.rx_queue[j]);
+ free_irq(msi->rx_irq[j], &priv->channel[j]);
}
}
@@ -3797,13 +3903,13 @@ static void stmmac_free_irq(struct net_device *dev,
free_irq(msi->sfty_ce_irq, dev);
fallthrough;
case REQ_IRQ_ERR_SFTY_CE:
- if (priv->wol_irq > 0 && priv->wol_irq != dev->irq)
- free_irq(priv->wol_irq, dev);
- fallthrough;
- case REQ_IRQ_ERR_SFTY:
if (priv->sfty_irq > 0 && priv->sfty_irq != dev->irq)
free_irq(priv->sfty_irq, dev);
fallthrough;
+ case REQ_IRQ_ERR_SFTY:
+ if (priv->wol_irq > 0 && priv->wol_irq != dev->irq)
+ free_irq(priv->wol_irq, dev);
+ fallthrough;
case REQ_IRQ_ERR_WOL:
free_irq(dev->irq, dev);
fallthrough;
@@ -3939,7 +4045,7 @@ static int stmmac_request_irq_multi_msi(struct net_device *dev)
sprintf(int_name, "%s:%s-%d", dev->name, "rx", i);
ret = request_irq(msi->rx_irq[i],
stmmac_msi_intr_rx,
- 0, int_name, &priv->dma_conf.rx_queue[i]);
+ 0, int_name, &priv->channel[i]);
if (unlikely(ret < 0)) {
netdev_err(priv->dev,
"%s: alloc rx-%d MSI %d (error: %d)\n",
@@ -3963,7 +4069,7 @@ static int stmmac_request_irq_multi_msi(struct net_device *dev)
sprintf(int_name, "%s:%s-%d", dev->name, "tx", i);
ret = request_irq(msi->tx_irq[i],
stmmac_msi_intr_tx,
- 0, int_name, &priv->dma_conf.tx_queue[i]);
+ 0, int_name, &priv->channel[i]);
if (unlikely(ret < 0)) {
netdev_err(priv->dev,
"%s: alloc tx-%d MSI %d (error: %d)\n",
@@ -4050,14 +4156,40 @@ static int stmmac_request_irq(struct net_device *dev)
return ret;
}
+/* Balance disable_irq()/enable_irq() for every registered IRQ, including
+ * shared lines. Unlike freeing and requesting IRQs, this cannot fail.
+ */
+static void stmmac_set_irq_state(struct stmmac_priv *priv, bool enable)
+{
+ void (*set_state)(unsigned int) = enable ? enable_irq : disable_irq;
+ struct stmmac_msi *msi = priv->msi;
+ int irq = priv->dev->irq;
+ u32 i;
+
+ set_state(irq);
+ if (priv->wol_irq > 0 && priv->wol_irq != irq)
+ set_state(priv->wol_irq);
+ if (priv->sfty_irq > 0 && priv->sfty_irq != irq)
+ set_state(priv->sfty_irq);
+ if (!msi)
+ return;
+ if (msi->sfty_ce_irq > 0 && msi->sfty_ce_irq != irq)
+ set_state(msi->sfty_ce_irq);
+ if (msi->sfty_ue_irq > 0 && msi->sfty_ue_irq != irq)
+ set_state(msi->sfty_ue_irq);
+ for (i = 0; i < priv->plat->rx_queues_to_use; i++)
+ if (msi->rx_irq[i] > 0)
+ set_state(msi->rx_irq[i]);
+ for (i = 0; i < priv->plat->tx_queues_to_use; i++)
+ if (msi->tx_irq[i] > 0)
+ set_state(msi->tx_irq[i]);
+}
+
/**
- * stmmac_setup_dma_desc - Generate a dma_conf and allocate DMA queue
- * @priv: driver private structure
- * @mtu: MTU to setup the dma queue and buf with
- * Description: Allocate and generate a dma_conf based on the provided MTU.
- * Allocate the Tx/Rx DMA queue and init them.
- * Return value:
- * the dma_conf allocated struct on success and an appropriate ERR_PTR on failure.
+ * stmmac_setup_dma_desc - allocate and initialize a DMA configuration
+ * @priv: driver private structure
+ * @mtu: MTU to size the receive buffers for
+ * Return: the allocated configuration, or an ERR_PTR on failure
*/
static struct stmmac_dma_conf *
stmmac_setup_dma_desc(struct stmmac_priv *priv, unsigned int mtu)
@@ -4087,8 +4219,8 @@ stmmac_setup_dma_desc(struct stmmac_priv *priv, unsigned int mtu)
/* Chose the tx/rx size from the already defined one in the
* priv struct. (if defined)
*/
- dma_conf->dma_tx_size = priv->dma_conf.dma_tx_size;
- dma_conf->dma_rx_size = priv->dma_conf.dma_rx_size;
+ dma_conf->dma_tx_size = priv->dma_conf->dma_tx_size;
+ dma_conf->dma_rx_size = priv->dma_conf->dma_rx_size;
if (!dma_conf->dma_tx_size)
dma_conf->dma_tx_size = DMA_DEFAULT_TX_SIZE;
@@ -4102,6 +4234,7 @@ stmmac_setup_dma_desc(struct stmmac_priv *priv, unsigned int mtu)
/* Setup per-TXQ tbs flag before TX descriptor alloc */
tx_q->tbs |= tbs_en ? STMMAC_TBS_AVAIL : 0;
+ tx_q->tbs |= priv->dma_conf->tx_queue[chan].tbs & STMMAC_TBS_EN;
}
ret = alloc_dma_desc_resources(priv, dma_conf);
@@ -4127,6 +4260,37 @@ stmmac_setup_dma_desc(struct stmmac_priv *priv, unsigned int mtu)
return ERR_PTR(ret);
}
+/* Finish core sleep state even if the platform resume callback failed. */
+static int stmmac_resume_hw(struct stmmac_priv *priv)
+{
+ int ret;
+
+ if (!priv->hw_suspended)
+ return 0;
+
+ /* Use the state installed by suspend, not a subsequently changed WoL
+ * setting. Clear PMT even when a different device caused the wakeup.
+ */
+ if (priv->irq_wake) {
+ mutex_lock(&priv->lock);
+ stmmac_pmt(priv, priv->hw, 0);
+ mutex_unlock(&priv->lock);
+ priv->irq_wake = 0;
+ } else {
+ ret = pinctrl_pm_select_default_state(priv->device);
+ if (ret)
+ return ret;
+ if (priv->mii) {
+ ret = stmmac_mdio_reset(priv->mii);
+ if (ret)
+ return ret;
+ }
+ }
+ priv->hw_suspended = false;
+
+ return 0;
+}
+
/**
* __stmmac_open - open entry point of the driver
* @dev : pointer to the device structure.
@@ -4144,10 +4308,8 @@ static int __stmmac_open(struct net_device *dev,
u8 chan;
int ret;
- for (int i = 0; i < priv->plat->tx_queues_to_use; i++)
- if (priv->dma_conf.tx_queue[i].tbs & STMMAC_TBS_EN)
- dma_conf->tx_queue[i].tbs = priv->dma_conf.tx_queue[i].tbs;
- memcpy(&priv->dma_conf, dma_conf, sizeof(*dma_conf));
+ /* Keep RXQ metadata and timers at their registered addresses. */
+ priv->dma_conf = dma_conf;
/* The PHY is suspended when the interface is reopened without
* disconnecting the PHY, e.g. on MTU change. IEEE 802.3 allows PHYs
@@ -4160,7 +4322,7 @@ static int __stmmac_open(struct net_device *dev,
stmmac_reset_queues_param(priv);
- ret = stmmac_hw_setup(dev);
+ ret = stmmac_hw_setup(dev, false);
if (ret < 0) {
netdev_err(priv->dev, "%s: Hw setup failed\n", __func__);
goto init_error;
@@ -4181,6 +4343,7 @@ static int __stmmac_open(struct net_device *dev,
stmmac_enable_all_queues(priv);
netif_tx_start_all_queues(priv->dev);
stmmac_enable_all_dma_irq(priv);
+ priv->datapath = STMMAC_DATAPATH_RUNNING;
return 0;
@@ -4188,16 +4351,19 @@ static int __stmmac_open(struct net_device *dev,
phylink_stop(priv->phylink);
for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++)
- hrtimer_cancel(&priv->dma_conf.tx_queue[chan].txtimer);
+ hrtimer_cancel(&priv->dma_conf->tx_queue[chan].txtimer);
stmmac_release_ptp(priv);
init_error:
+ stmmac_stop_all_dma(priv);
+ stmmac_mac_set(priv, priv->ioaddr, false);
return ret;
}
static int stmmac_open(struct net_device *dev)
{
struct stmmac_priv *priv = netdev_priv(dev);
+ struct stmmac_dma_conf *old_conf = priv->dma_conf;
struct stmmac_dma_conf *dma_conf;
int ret;
@@ -4213,6 +4379,10 @@ static int stmmac_open(struct net_device *dev)
if (ret < 0)
goto err_dma_resources;
+ ret = stmmac_resume_hw(priv);
+ if (ret)
+ goto err_runtime_pm;
+
ret = stmmac_init_phy(dev);
if (ret)
goto err_runtime_pm;
@@ -4227,7 +4397,7 @@ static int stmmac_open(struct net_device *dev)
if (ret)
goto err_serdes;
- kfree(dma_conf);
+ kfree(old_conf);
/* We may have called phylink_speed_down before */
phylink_speed_up(priv->phylink);
@@ -4241,34 +4411,65 @@ static int stmmac_open(struct net_device *dev)
err_runtime_pm:
pm_runtime_put(priv->device);
err_dma_resources:
+ priv->dma_conf = old_conf;
free_dma_desc_resources(priv, dma_conf);
kfree(dma_conf);
return ret;
}
-static void __stmmac_release(struct net_device *dev)
+static void stmmac_stop_tx_queues(struct stmmac_priv *priv)
{
- struct stmmac_priv *priv = netdev_priv(dev);
u8 chan;
- /* Stop and disconnect the PHY */
- phylink_stop(priv->phylink);
+ netif_tx_disable(priv->dev);
- stmmac_disable_all_queues(priv);
+ /* A poll function can still arm a timer after napi_complete_done().
+ * Drain those poll tails and in-flight transmitters before cancelling
+ * the timers, so none can be rearmed after their final cancellation.
+ */
+ synchronize_net();
for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++)
- hrtimer_cancel(&priv->dma_conf.tx_queue[chan].txtimer);
+ hrtimer_cancel(&priv->dma_conf->tx_queue[chan].txtimer);
+}
- netif_tx_disable(dev);
+/* Quiesce NAPI and transmit queues without releasing their resources. */
+static void stmmac_quiesce(struct stmmac_priv *priv)
+{
+ stmmac_disable_all_queues(priv);
+ stmmac_stop_tx_queues(priv);
+}
+
+static void __stmmac_release(struct net_device *dev)
+{
+ struct stmmac_priv *priv = netdev_priv(dev);
+ enum stmmac_datapath_state state = priv->datapath;
+
+ /* There may be no resources left after detached XDP reconfiguration. */
+ if (priv->datapath == STMMAC_DATAPATH_DOWN)
+ return;
+
+ phylink_stop(priv->phylink);
+
+ /* Suspend retains the resources, but has already stopped activity. */
+ if (priv->datapath == STMMAC_DATAPATH_RUNNING)
+ stmmac_quiesce(priv);
+ priv->datapath = STMMAC_DATAPATH_DOWN;
/* Free the IRQ lines */
- stmmac_free_irq(dev, REQ_IRQ_ERR_ALL, 0);
+ if (state != STMMAC_DATAPATH_HALTED)
+ stmmac_free_irq(dev, REQ_IRQ_ERR_ALL, 0);
- /* Stop TX/RX DMA and clear the descriptors */
+ /* TX error IRQs can restart a queue after the first quiescence. */
+ stmmac_stop_tx_queues(priv);
+
+ /* Stop TX/RX DMA after draining IRQ handlers which can restart it. */
stmmac_stop_all_dma(priv);
+ /* Link resolution need not have reached mac_link_up() yet. */
+ stmmac_mac_set(priv, priv->ioaddr, false);
/* Release and free the Rx/Tx resources */
- free_dma_desc_resources(priv, &priv->dma_conf);
+ free_dma_desc_resources(priv, priv->dma_conf);
stmmac_release_ptp(priv);
@@ -4285,6 +4486,15 @@ static void __stmmac_release(struct net_device *dev)
static int stmmac_release(struct net_device *dev)
{
struct stmmac_priv *priv = netdev_priv(dev);
+ int ret;
+
+ /* Resume may have failed before restoring pins or disabling MAC wake.
+ * Complete that cleanup without restarting the link or the datapath.
+ * If it fails, keep hw_suspended set so a fresh open can retry it.
+ */
+ ret = stmmac_resume_hw(priv);
+ if (ret)
+ netdev_err(dev, "failed to restore hardware sleep state: %d\n", ret);
/* If the PHY or MAC has WoL enabled, then the PHY will not be
* suspended when phylink_stop() is called below. Set the PHY
@@ -4298,6 +4508,8 @@ static int stmmac_release(struct net_device *dev)
stmmac_legacy_serdes_power_down(priv);
phylink_disconnect_phy(priv->phylink);
pm_runtime_put(priv->device);
+ /* Allow a fresh open after a failed MTU reopen or resume. */
+ netif_device_attach(dev);
return 0;
}
@@ -4322,7 +4534,7 @@ static bool stmmac_vlan_insert(struct stmmac_priv *priv, struct sk_buff *skb,
return false;
stmmac_set_tx_owner(priv, p);
- tx_q->cur_tx = STMMAC_NEXT_ENTRY(tx_q->cur_tx, priv->dma_conf.dma_tx_size);
+ tx_q->cur_tx = STMMAC_NEXT_ENTRY(tx_q->cur_tx, priv->dma_conf->dma_tx_size);
return true;
}
@@ -4342,7 +4554,7 @@ static void stmmac_tso_allocator(struct stmmac_priv *priv, u32 *entry,
dma_addr_t des, int total_len,
bool last_segment, u32 queue)
{
- struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue];
+ struct stmmac_tx_queue *tx_q = &priv->dma_conf->tx_queue[queue];
struct dma_desc *desc;
u32 buff_size;
int tmp_len;
@@ -4352,7 +4564,7 @@ static void stmmac_tso_allocator(struct stmmac_priv *priv, u32 *entry,
while (tmp_len > 0) {
dma_addr_t curr_addr;
- *entry = STMMAC_NEXT_ENTRY(*entry, priv->dma_conf.dma_tx_size);
+ *entry = STMMAC_NEXT_ENTRY(*entry, priv->dma_conf->dma_tx_size);
WARN_ON(tx_q->tx_skbuff[*entry]);
if (tx_q->tbs & STMMAC_TBS_AVAIL)
@@ -4376,7 +4588,7 @@ static void stmmac_tso_allocator(struct stmmac_priv *priv, u32 *entry,
static void stmmac_flush_tx_descriptors(struct stmmac_priv *priv, int queue)
{
- struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue];
+ struct stmmac_tx_queue *tx_q = &priv->dma_conf->tx_queue[queue];
/* The own bit must be the latest setting done when prepare the
* descriptor and then barrier is needed to make sure that
@@ -4529,7 +4741,7 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
nfrags = skb_shinfo(skb)->nr_frags;
queue = skb_get_queue_mapping(skb);
- tx_q = &priv->dma_conf.tx_queue[queue];
+ tx_q = &priv->dma_conf->tx_queue[queue];
txq_stats = &priv->xstats.txq_stats[queue];
first_tx = tx_q->cur_tx;
@@ -4567,7 +4779,7 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
stmmac_set_mss(priv, mss_desc, mss);
tx_q->mss = mss;
tx_q->cur_tx = STMMAC_NEXT_ENTRY(tx_q->cur_tx,
- priv->dma_conf.dma_tx_size);
+ priv->dma_conf->dma_tx_size);
WARN_ON(tx_q->tx_skbuff[tx_q->cur_tx]);
}
@@ -4638,7 +4850,7 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
/* Manage tx mitigation */
tx_packets = CIRC_CNT(tx_q->cur_tx + 1, first_tx,
- priv->dma_conf.dma_tx_size);
+ priv->dma_conf->dma_tx_size);
tx_q->tx_count_frames += tx_packets;
if ((skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) && priv->hwts_tx_en)
@@ -4670,7 +4882,7 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
* ndo_start_xmit will fill this descriptor the next time it's
* called and stmmac_tx_clean may clean up to this descriptor.
*/
- tx_q->cur_tx = STMMAC_NEXT_ENTRY(tx_q->cur_tx, priv->dma_conf.dma_tx_size);
+ tx_q->cur_tx = STMMAC_NEXT_ENTRY(tx_q->cur_tx, priv->dma_conf->dma_tx_size);
if (unlikely(stmmac_tx_avail(priv, queue) <= (MAX_SKB_FRAGS + 1))) {
netif_dbg(priv, hw, priv->dev, "%s: stop transmitted packets\n",
@@ -4700,7 +4912,7 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
* segment.
*/
is_last_segment = CIRC_CNT(tx_q->cur_tx, first_entry,
- priv->dma_conf.dma_tx_size) == 1;
+ priv->dma_conf->dma_tx_size) == 1;
/* Complete the first descriptor before granting the DMA */
stmmac_prepare_tso_tx_desc(priv, first, 1, proto_hdr_len, 0, 1,
@@ -4738,13 +4950,13 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
for (;;) {
desc = stmmac_get_tx_desc(priv, tx_q, first_entry);
stmmac_release_tx_desc(priv, desc, priv->descriptor_mode);
- stmmac_free_tx_buffer(priv, &priv->dma_conf, queue,
+ stmmac_free_tx_buffer(priv, priv->dma_conf, queue,
first_entry);
if (first_entry == entry)
break;
first_entry = STMMAC_NEXT_ENTRY(first_entry,
- priv->dma_conf.dma_tx_size);
+ priv->dma_conf->dma_tx_size);
}
error:
dev_err(priv->device, "Tx dma map failed\n");
@@ -4829,7 +5041,7 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
return NETDEV_TX_BUSY;
}
- tx_q = &priv->dma_conf.tx_queue[queue];
+ tx_q = &priv->dma_conf->tx_queue[queue];
first_tx = tx_q->cur_tx;
/* Check if VLAN can be inserted by HW */
@@ -4905,7 +5117,7 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
unsigned int frag_size = skb_frag_size(frag);
bool last_segment = (i == (nfrags - 1));
- entry = STMMAC_NEXT_ENTRY(entry, priv->dma_conf.dma_tx_size);
+ entry = STMMAC_NEXT_ENTRY(entry, priv->dma_conf->dma_tx_size);
WARN_ON(tx_q->tx_skbuff[entry]);
desc = stmmac_get_tx_desc(priv, tx_q, entry);
@@ -4935,7 +5147,7 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
* This approach takes care about the fragments: desc is the first
* element in case of no SG.
*/
- tx_packets = CIRC_CNT(entry + 1, first_tx, priv->dma_conf.dma_tx_size);
+ tx_packets = CIRC_CNT(entry + 1, first_tx, priv->dma_conf->dma_tx_size);
tx_q->tx_count_frames += tx_packets;
if ((skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) && priv->hwts_tx_en)
@@ -4963,7 +5175,7 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
* ndo_start_xmit will fill this descriptor the next time it's
* called and stmmac_tx_clean may clean up to this descriptor.
*/
- entry = STMMAC_NEXT_ENTRY(entry, priv->dma_conf.dma_tx_size);
+ entry = STMMAC_NEXT_ENTRY(entry, priv->dma_conf->dma_tx_size);
tx_q->cur_tx = entry;
if (netif_msg_pktdata(priv)) {
@@ -5078,7 +5290,7 @@ static void stmmac_rx_vlan(struct net_device *dev, struct sk_buff *skb)
*/
static inline void stmmac_rx_refill(struct stmmac_priv *priv, u32 queue)
{
- struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue];
+ struct stmmac_rx_queue *rx_q = &priv->dma_conf->rx_queue[queue];
int dirty = stmmac_rx_dirty(priv, queue);
unsigned int entry = rx_q->dirty_rx;
gfp_t gfp = (GFP_ATOMIC | __GFP_NOWARN);
@@ -5129,7 +5341,7 @@ static inline void stmmac_rx_refill(struct stmmac_priv *priv, u32 queue)
dma_wmb();
stmmac_set_rx_owner(priv, p, use_rx_wd);
- entry = STMMAC_NEXT_ENTRY(entry, priv->dma_conf.dma_rx_size);
+ entry = STMMAC_NEXT_ENTRY(entry, priv->dma_conf->dma_rx_size);
}
rx_q->dirty_rx = entry;
stmmac_set_queue_rx_tail_ptr(priv, rx_q, queue, rx_q->dirty_rx);
@@ -5157,12 +5369,12 @@ static unsigned int stmmac_rx_buf1_len(struct stmmac_priv *priv,
/* First descriptor, not last descriptor and not split header */
if (status & rx_not_ls)
- return priv->dma_conf.dma_buf_sz;
+ return priv->dma_conf->dma_buf_sz;
plen = stmmac_get_rx_frame_len(priv, p, coe);
/* First descriptor and last descriptor and not split header */
- return min_t(unsigned int, priv->dma_conf.dma_buf_sz, plen);
+ return min_t(unsigned int, priv->dma_conf->dma_buf_sz, plen);
}
static unsigned int stmmac_rx_buf2_len(struct stmmac_priv *priv,
@@ -5192,7 +5404,7 @@ static unsigned int stmmac_rx_buf2_len(struct stmmac_priv *priv,
/* Not GMAC4 and not last descriptor */
if (priv->plat->core_type != DWMAC_CORE_GMAC4 && (status & rx_not_ls))
- return priv->dma_conf.dma_buf_sz;
+ return priv->dma_conf->dma_buf_sz;
/* GMAC4 or last descriptor */
plen = stmmac_get_rx_frame_len(priv, p, coe);
@@ -5204,7 +5416,7 @@ static int stmmac_xdp_xmit_xdpf(struct stmmac_priv *priv, int queue,
struct xdp_frame *xdpf, bool dma_map)
{
struct stmmac_txq_stats *txq_stats = &priv->xstats.txq_stats[queue];
- struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue];
+ struct stmmac_tx_queue *tx_q = &priv->dma_conf->tx_queue[queue];
bool csum = !priv->plat->tx_queues_cfg[queue].coe_unsupported;
unsigned int entry = tx_q->cur_tx;
enum stmmac_txbuf_type buf_type;
@@ -5270,7 +5482,7 @@ static int stmmac_xdp_xmit_xdpf(struct stmmac_priv *priv, int queue,
stmmac_enable_dma_transmission(priv, priv->ioaddr, queue);
- entry = STMMAC_NEXT_ENTRY(entry, priv->dma_conf.dma_tx_size);
+ entry = STMMAC_NEXT_ENTRY(entry, priv->dma_conf->dma_tx_size);
tx_q->cur_tx = entry;
return STMMAC_XDP_TX;
@@ -5461,7 +5673,7 @@ static void stmmac_dispatch_skb_zc(struct stmmac_priv *priv, u32 queue,
static bool stmmac_rx_refill_zc(struct stmmac_priv *priv, u32 queue, u32 budget)
{
- struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue];
+ struct stmmac_rx_queue *rx_q = &priv->dma_conf->rx_queue[queue];
unsigned int entry = rx_q->dirty_rx;
struct dma_desc *rx_desc = NULL;
bool ret = true;
@@ -5501,7 +5713,7 @@ static bool stmmac_rx_refill_zc(struct stmmac_priv *priv, u32 queue, u32 budget)
dma_wmb();
stmmac_set_rx_owner(priv, rx_desc, use_rx_wd);
- entry = STMMAC_NEXT_ENTRY(entry, priv->dma_conf.dma_rx_size);
+ entry = STMMAC_NEXT_ENTRY(entry, priv->dma_conf->dma_rx_size);
}
if (rx_desc) {
@@ -5525,7 +5737,7 @@ static struct stmmac_xdp_buff *xsk_buff_to_stmmac_ctx(struct xdp_buff *xdp)
static int stmmac_rx_zc(struct stmmac_priv *priv, int limit, u32 queue)
{
struct stmmac_rxq_stats *rxq_stats = &priv->xstats.rxq_stats[queue];
- struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue];
+ struct stmmac_rx_queue *rx_q = &priv->dma_conf->rx_queue[queue];
unsigned int count = 0, error = 0, len = 0;
int dirty = stmmac_rx_dirty(priv, queue);
unsigned int next_entry = rx_q->cur_rx;
@@ -5542,7 +5754,7 @@ static int stmmac_rx_zc(struct stmmac_priv *priv, int limit, u32 queue)
netdev_dbg(priv->dev, "%s: descriptor ring:\n", __func__);
desc_size = stmmac_get_rx_desc_size(priv);
- stmmac_display_ring(priv, rx_head, priv->dma_conf.dma_rx_size, true,
+ stmmac_display_ring(priv, rx_head, priv->dma_conf->dma_rx_size, true,
rx_q->dma_rx_phy, desc_size);
}
while (count < limit) {
@@ -5586,7 +5798,7 @@ static int stmmac_rx_zc(struct stmmac_priv *priv, int limit, u32 queue)
/* Prefetch the next RX descriptor */
next_entry = STMMAC_NEXT_ENTRY(rx_q->cur_rx,
- priv->dma_conf.dma_rx_size);
+ priv->dma_conf->dma_rx_size);
if (unlikely(next_entry == rx_q->dirty_rx))
break;
@@ -5711,7 +5923,7 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue)
{
u32 rx_errors = 0, rx_dropped = 0, rx_bytes = 0, rx_packets = 0;
struct stmmac_rxq_stats *rxq_stats = &priv->xstats.rxq_stats[queue];
- struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue];
+ struct stmmac_rx_queue *rx_q = &priv->dma_conf->rx_queue[queue];
struct stmmac_channel *ch = &priv->channel[queue];
unsigned int count = 0, error = 0, len = 0;
int status = 0, coe = priv->hw->rx_csum;
@@ -5724,7 +5936,7 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue)
int bufsz;
dma_dir = page_pool_get_dma_dir(rx_q->page_pool);
- bufsz = DIV_ROUND_UP(priv->dma_conf.dma_buf_sz, PAGE_SIZE) * PAGE_SIZE;
+ bufsz = DIV_ROUND_UP(priv->dma_conf->dma_buf_sz, PAGE_SIZE) * PAGE_SIZE;
if (netif_msg_rx_status(priv)) {
void *rx_head = stmmac_get_rx_desc(priv, rx_q, 0);
@@ -5732,7 +5944,7 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue)
netdev_dbg(priv->dev, "%s: descriptor ring:\n", __func__);
desc_size = stmmac_get_rx_desc_size(priv);
- stmmac_display_ring(priv, rx_head, priv->dma_conf.dma_rx_size, true,
+ stmmac_display_ring(priv, rx_head, priv->dma_conf->dma_rx_size, true,
rx_q->dma_rx_phy, desc_size);
}
while (count < limit) {
@@ -5772,7 +5984,7 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue)
break;
next_entry = STMMAC_NEXT_ENTRY(rx_q->cur_rx,
- priv->dma_conf.dma_rx_size);
+ priv->dma_conf->dma_rx_size);
if (unlikely(next_entry == rx_q->dirty_rx))
break;
@@ -5906,7 +6118,7 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue)
buf1_len, dma_dir);
skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags,
buf->page, buf->page_offset, buf1_len,
- priv->dma_conf.dma_buf_sz);
+ priv->dma_conf->dma_buf_sz);
buf->page = NULL;
}
@@ -5915,7 +6127,7 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue)
buf2_len, dma_dir);
skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags,
buf->sec_page, 0, buf2_len,
- priv->dma_conf.dma_buf_sz);
+ priv->dma_conf->dma_buf_sz);
buf->sec_page = NULL;
}
@@ -6126,6 +6338,113 @@ static void stmmac_set_rx_mode(struct net_device *dev)
stmmac_set_filter(priv, priv->hw, dev);
}
+static int stmmac_reconfigure_mtu(struct net_device *dev, int mtu)
+{
+ struct stmmac_priv *priv = netdev_priv(dev);
+ struct stmmac_dma_conf *old_conf = priv->dma_conf;
+ struct stmmac_dma_conf *new_conf;
+ int old_mtu = dev->mtu;
+ int ret, restore_ret;
+ u32 chan;
+
+ new_conf = stmmac_setup_dma_desc(priv, mtu);
+ if (IS_ERR(new_conf))
+ return PTR_ERR(new_conf);
+
+ netif_device_detach(dev);
+ phylink_stop(priv->phylink);
+ stmmac_quiesce(priv);
+ timer_delete_sync(&priv->eee_ctrl_timer);
+ if (stmmac_fpe_supported(priv))
+ ethtool_mmsv_stop(&priv->fpe_cfg.mmsv);
+
+ /* An IRQ can recover a TX error and restart a queue. Drain handlers
+ * before the final TX stop, and keep the registrations for rollback.
+ */
+ stmmac_set_irq_state(priv, false);
+ netif_tx_disable(dev);
+ synchronize_net();
+
+ ret = stmmac_prepare_rx_buffers(priv);
+ if (ret)
+ goto restart;
+
+ stmmac_stop_all_dma(priv);
+ phylink_prepare_resume(priv->phylink);
+
+ /* MAC receive limits must be programmed for the prospective MTU. */
+ WRITE_ONCE(dev->mtu, mtu);
+ priv->dma_conf = new_conf;
+ stmmac_reset_queues_param(priv);
+ ret = stmmac_hw_setup(dev, false);
+ if (ret) {
+ stmmac_stop_all_dma(priv);
+ stmmac_mac_set(priv, priv->ioaddr, false);
+ priv->dma_conf = old_conf;
+ WRITE_ONCE(dev->mtu, old_mtu);
+
+ /* Reuse the retained rings. Reinitialize them only after reset
+ * has completed, not merely after clearing the DMA enable bits.
+ */
+ restore_ret = stmmac_hw_setup(dev, true);
+ if (restore_ret) {
+ stmmac_stop_all_dma(priv);
+ stmmac_mac_set(priv, priv->ioaddr, false);
+ stmmac_set_irq_state(priv, true);
+ stmmac_free_irq(dev, REQ_IRQ_ERR_ALL, 0);
+ netif_tx_disable(dev);
+ stmmac_stop_all_dma(priv);
+ priv->datapath = STMMAC_DATAPATH_HALTED;
+ netdev_err(dev, "MTU rollback failed: %pe; interface remains detached\n",
+ ERR_PTR(restore_ret));
+ goto free_new;
+ }
+ } else {
+ /* Hardware setup completed its reset before using the new rings.
+ * The old DMA allocations can now be released safely.
+ */
+ free_dma_desc_resources(priv, old_conf);
+ kfree(old_conf);
+ for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++)
+ hrtimer_setup(&new_conf->tx_queue[chan].txtimer,
+ stmmac_tx_timer, CLOCK_MONOTONIC,
+ HRTIMER_MODE_REL);
+ }
+
+ /* Restore timestamping without registering a new PHC or resetting the
+ * user's packet timestamp filters. Timestamping can also be used without
+ * a registered PHC (CONFIG_PTP_1588_CLOCK=n).
+ */
+ if ((priv->dma_cap.time_stamp || priv->dma_cap.atime_stamp) &&
+ priv->plat->clk_ptp_rate) {
+ unsigned long flags;
+
+ write_lock_irqsave(&priv->ptp_lock, flags);
+ stmmac_init_tstamp_counter(priv, priv->systime_flags);
+ if (priv->plat->flags & STMMAC_FLAG_HWTSTAMP_CORRECT_LATENCY)
+ stmmac_hwtstamp_correct_latency(priv, priv);
+ write_unlock_irqrestore(&priv->ptp_lock, flags);
+ }
+ stmmac_set_rx_mode(dev);
+ stmmac_vlan_restore(priv);
+
+restart:
+ stmmac_enable_all_queues(priv);
+ stmmac_enable_all_dma_irq(priv);
+ stmmac_set_irq_state(priv, true);
+ phylink_start(priv->phylink);
+ netif_device_attach(dev);
+ for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++)
+ stmmac_tx_timer_arm(priv, chan);
+ if (!ret)
+ return 0;
+
+free_new:
+ free_dma_desc_resources(priv, new_conf);
+ kfree(new_conf);
+ return ret;
+}
+
/**
* stmmac_change_mtu - entry point to change MTU size for the device.
* @dev : device pointer.
@@ -6141,7 +6460,6 @@ static int stmmac_change_mtu(struct net_device *dev, int new_mtu)
{
struct stmmac_priv *priv = netdev_priv(dev);
int txfifosz = priv->plat->tx_fifo_size;
- struct stmmac_dma_conf *dma_conf;
const int mtu = new_mtu;
int ret;
@@ -6161,29 +6479,15 @@ static int stmmac_change_mtu(struct net_device *dev, int new_mtu)
if ((txfifosz < new_mtu) || (new_mtu > BUF_SIZE_16KiB))
return -EINVAL;
- if (netif_running(dev)) {
- netdev_dbg(priv->dev, "restarting interface to change its MTU\n");
- /* Try to allocate the new DMA conf with the new mtu */
- dma_conf = stmmac_setup_dma_desc(priv, mtu);
- if (IS_ERR(dma_conf)) {
- netdev_err(priv->dev, "failed allocating new dma conf for new MTU %d\n",
- mtu);
- return PTR_ERR(dma_conf);
- }
-
- __stmmac_release(dev);
-
- ret = __stmmac_open(dev, dma_conf);
- if (ret) {
- free_dma_desc_resources(priv, dma_conf);
- kfree(dma_conf);
- netdev_err(priv->dev, "failed reopening the interface after MTU change\n");
+ /* Normal-size frames use the same buffers and MAC receive limits.
+ * In particular, do not disturb a live AF_XDP pool: XDP does not
+ * support jumbo frames, so it never needs the ring replacement below.
+ */
+ if (netif_running(dev) &&
+ (dev->mtu > ETH_DATA_LEN || mtu > ETH_DATA_LEN)) {
+ ret = stmmac_reconfigure_mtu(dev, mtu);
+ if (ret)
return ret;
- }
-
- kfree(dma_conf);
-
- stmmac_set_rx_mode(dev);
}
WRITE_ONCE(dev->mtu, mtu);
@@ -6354,15 +6658,11 @@ static irqreturn_t stmmac_safety_interrupt(int irq, void *dev_id)
static irqreturn_t stmmac_msi_intr_tx(int irq, void *data)
{
- struct stmmac_tx_queue *tx_q = (struct stmmac_tx_queue *)data;
- struct stmmac_dma_conf *dma_conf;
- int chan = tx_q->queue_index;
- struct stmmac_priv *priv;
+ struct stmmac_channel *ch = data;
+ struct stmmac_priv *priv = ch->priv_data;
+ int chan = ch->index;
int status;
- dma_conf = container_of(tx_q, struct stmmac_dma_conf, tx_queue[chan]);
- priv = container_of(dma_conf, struct stmmac_priv, dma_conf);
-
/* Check if adapter is up */
if (test_bit(STMMAC_DOWN, &priv->state))
return IRQ_HANDLED;
@@ -6381,13 +6681,9 @@ static irqreturn_t stmmac_msi_intr_tx(int irq, void *data)
static irqreturn_t stmmac_msi_intr_rx(int irq, void *data)
{
- struct stmmac_rx_queue *rx_q = (struct stmmac_rx_queue *)data;
- struct stmmac_dma_conf *dma_conf;
- int chan = rx_q->queue_index;
- struct stmmac_priv *priv;
-
- dma_conf = container_of(rx_q, struct stmmac_dma_conf, rx_queue[chan]);
- priv = container_of(dma_conf, struct stmmac_priv, dma_conf);
+ struct stmmac_channel *ch = data;
+ struct stmmac_priv *priv = ch->priv_data;
+ int chan = ch->index;
/* Check if adapter is up */
if (test_bit(STMMAC_DOWN, &priv->state))
@@ -6425,6 +6721,8 @@ static int stmmac_setup_tc_block_cb(enum tc_setup_type type, void *type_data,
if (!tc_cls_can_offload_and_chain0(priv->dev, type_data))
return ret;
+ if (!netif_device_present(priv->dev))
+ return -ENETDOWN;
__stmmac_disable_all_queues(priv);
@@ -6542,45 +6840,49 @@ static int stmmac_rings_status_show(struct seq_file *seq, void *v)
{
struct net_device *dev = seq->private;
struct stmmac_priv *priv = netdev_priv(dev);
- u8 rx_count = priv->plat->rx_queues_to_use;
- u8 tx_count = priv->plat->tx_queues_to_use;
- u8 queue;
+ u8 rx_count, tx_count, queue;
- if ((dev->flags & IFF_UP) == 0)
- return 0;
+ rtnl_lock();
+ if (priv->datapath == STMMAC_DATAPATH_DOWN)
+ goto out_unlock;
+
+ rx_count = priv->plat->rx_queues_to_use;
+ tx_count = priv->plat->tx_queues_to_use;
for (queue = 0; queue < rx_count; queue++) {
- struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue];
+ struct stmmac_rx_queue *rx_q = &priv->dma_conf->rx_queue[queue];
seq_printf(seq, "RX Queue %d:\n", queue);
if (priv->extend_desc) {
seq_printf(seq, "Extended descriptor ring:\n");
sysfs_display_ring((void *)rx_q->dma_erx,
- priv->dma_conf.dma_rx_size, 1, seq, rx_q->dma_rx_phy);
+ priv->dma_conf->dma_rx_size, 1, seq, rx_q->dma_rx_phy);
} else {
seq_printf(seq, "Descriptor ring:\n");
sysfs_display_ring((void *)rx_q->dma_rx,
- priv->dma_conf.dma_rx_size, 0, seq, rx_q->dma_rx_phy);
+ priv->dma_conf->dma_rx_size, 0, seq, rx_q->dma_rx_phy);
}
}
for (queue = 0; queue < tx_count; queue++) {
- struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue];
+ struct stmmac_tx_queue *tx_q = &priv->dma_conf->tx_queue[queue];
seq_printf(seq, "TX Queue %d:\n", queue);
if (priv->extend_desc) {
seq_printf(seq, "Extended descriptor ring:\n");
sysfs_display_ring((void *)tx_q->dma_etx,
- priv->dma_conf.dma_tx_size, 1, seq, tx_q->dma_tx_phy);
+ priv->dma_conf->dma_tx_size, 1, seq, tx_q->dma_tx_phy);
} else if (!(tx_q->tbs & STMMAC_TBS_AVAIL)) {
seq_printf(seq, "Descriptor ring:\n");
sysfs_display_ring((void *)tx_q->dma_tx,
- priv->dma_conf.dma_tx_size, 0, seq, tx_q->dma_tx_phy);
+ priv->dma_conf->dma_tx_size, 0, seq, tx_q->dma_tx_phy);
}
}
+out_unlock:
+ rtnl_unlock();
return 0;
}
DEFINE_SHOW_ATTRIBUTE(stmmac_rings_status);
@@ -6962,6 +7264,19 @@ static int stmmac_bpf(struct net_device *dev, struct netdev_bpf *bpf)
{
struct stmmac_priv *priv = netdev_priv(dev);
+ if (bpf->command != XDP_SETUP_PROG &&
+ bpf->command != XDP_SETUP_XSK_POOL)
+ return -EOPNOTSUPP;
+
+ /*
+ * Pool removal must succeed even after a failed resume. Release the
+ * suspended rings before their pool or XDP buffer layout can change.
+ * Leave the interface detached until it is closed and reopened.
+ */
+ if (priv->datapath == STMMAC_DATAPATH_SUSPENDED ||
+ priv->datapath == STMMAC_DATAPATH_HALTED)
+ __stmmac_release(dev);
+
switch (bpf->command) {
case XDP_SETUP_PROG:
return stmmac_xdp_set_prog(priv, bpf->prog, bpf->extack);
@@ -6992,6 +7307,10 @@ static int stmmac_xdp_xmit(struct net_device *dev, int num_frames,
nq = netdev_get_tx_queue(priv->dev, queue);
__netif_tx_lock(nq, cpu);
+ if (unlikely(!netif_device_present(dev) || netif_tx_queue_stopped(nq))) {
+ __netif_tx_unlock(nq);
+ return -ENETDOWN;
+ }
/* Avoids TX time-out as we are sharing with slow path */
txq_trans_cond_update(nq);
@@ -7015,129 +7334,24 @@ static int stmmac_xdp_xmit(struct net_device *dev, int num_frames,
return nxmit;
}
-void stmmac_disable_rx_queue(struct stmmac_priv *priv, u32 queue)
-{
- struct stmmac_channel *ch = &priv->channel[queue];
- unsigned long flags;
-
- spin_lock_irqsave(&ch->lock, flags);
- stmmac_disable_dma_irq(priv, priv->ioaddr, queue, 1, 0);
- spin_unlock_irqrestore(&ch->lock, flags);
-
- stmmac_stop_rx_dma(priv, queue);
- __free_dma_rx_desc_resources(priv, &priv->dma_conf, queue);
-}
-
-void stmmac_enable_rx_queue(struct stmmac_priv *priv, u32 queue)
-{
- struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue];
- struct stmmac_channel *ch = &priv->channel[queue];
- unsigned long flags;
- int ret;
-
- ret = __alloc_dma_rx_desc_resources(priv, &priv->dma_conf, queue);
- if (ret) {
- netdev_err(priv->dev, "Failed to alloc RX desc.\n");
- return;
- }
-
- ret = __init_dma_rx_desc_rings(priv, &priv->dma_conf, queue, GFP_KERNEL);
- if (ret) {
- __free_dma_rx_desc_resources(priv, &priv->dma_conf, queue);
- netdev_err(priv->dev, "Failed to init RX desc.\n");
- return;
- }
-
- stmmac_reset_rx_queue(priv, queue);
- stmmac_clear_rx_descriptors(priv, &priv->dma_conf, queue);
-
- stmmac_init_rx_chan(priv, priv->ioaddr, priv->plat->dma_cfg,
- rx_q->dma_rx_phy, queue);
-
- stmmac_set_queue_rx_tail_ptr(priv, rx_q, queue, rx_q->buf_alloc_num);
-
- stmmac_set_queue_rx_buf_size(priv, rx_q, queue);
-
- stmmac_start_rx_dma(priv, queue);
-
- spin_lock_irqsave(&ch->lock, flags);
- stmmac_enable_dma_irq(priv, priv->ioaddr, queue, 1, 0);
- spin_unlock_irqrestore(&ch->lock, flags);
-}
-
-void stmmac_disable_tx_queue(struct stmmac_priv *priv, u32 queue)
-{
- struct stmmac_channel *ch = &priv->channel[queue];
- unsigned long flags;
-
- spin_lock_irqsave(&ch->lock, flags);
- stmmac_disable_dma_irq(priv, priv->ioaddr, queue, 0, 1);
- spin_unlock_irqrestore(&ch->lock, flags);
-
- stmmac_stop_tx_dma(priv, queue);
- __free_dma_tx_desc_resources(priv, &priv->dma_conf, queue);
-}
-
-void stmmac_enable_tx_queue(struct stmmac_priv *priv, u32 queue)
-{
- struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue];
- struct stmmac_channel *ch = &priv->channel[queue];
- unsigned long flags;
- int ret;
-
- ret = __alloc_dma_tx_desc_resources(priv, &priv->dma_conf, queue);
- if (ret) {
- netdev_err(priv->dev, "Failed to alloc TX desc.\n");
- return;
- }
-
- ret = __init_dma_tx_desc_rings(priv, &priv->dma_conf, queue);
- if (ret) {
- __free_dma_tx_desc_resources(priv, &priv->dma_conf, queue);
- netdev_err(priv->dev, "Failed to init TX desc.\n");
- return;
- }
-
- stmmac_reset_tx_queue(priv, queue);
- stmmac_clear_tx_descriptors(priv, &priv->dma_conf, queue);
-
- stmmac_init_tx_chan(priv, priv->ioaddr, priv->plat->dma_cfg,
- tx_q->dma_tx_phy, queue);
-
- if (tx_q->tbs & STMMAC_TBS_AVAIL)
- stmmac_enable_tbs(priv, priv->ioaddr, 1, queue);
-
- stmmac_set_queue_tx_tail_ptr(priv, tx_q, queue, 0);
-
- stmmac_start_tx_dma(priv, queue);
-
- spin_lock_irqsave(&ch->lock, flags);
- stmmac_enable_dma_irq(priv, priv->ioaddr, queue, 0, 1);
- spin_unlock_irqrestore(&ch->lock, flags);
-}
-
void stmmac_xdp_release(struct net_device *dev)
{
struct stmmac_priv *priv = netdev_priv(dev);
- u8 chan;
- /* Ensure tx function is not running */
- netif_tx_disable(dev);
-
- /* Disable NAPI process */
- stmmac_disable_all_queues(priv);
-
- for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++)
- hrtimer_cancel(&priv->dma_conf.tx_queue[chan].txtimer);
+ netif_device_detach(dev);
+ phylink_stop(priv->phylink);
+ stmmac_quiesce(priv);
+ priv->datapath = STMMAC_DATAPATH_DOWN;
/* Free the IRQ lines */
stmmac_free_irq(dev, REQ_IRQ_ERR_ALL, 0);
+ stmmac_stop_tx_queues(priv);
/* Stop TX/RX DMA channels */
stmmac_stop_all_dma(priv);
/* Release and free the Rx/Tx resources */
- free_dma_desc_resources(priv, &priv->dma_conf);
+ free_dma_desc_resources(priv, priv->dma_conf);
/* Disable the MAC Rx/Tx */
stmmac_mac_set(priv, priv->ioaddr, false);
@@ -7146,7 +7360,13 @@ void stmmac_xdp_release(struct net_device *dev)
* watchdogs during reset
*/
netif_trans_update(dev);
- netif_carrier_off(dev);
+
+ if (stmmac_fpe_supported(priv))
+ ethtool_mmsv_stop(&priv->fpe_cfg.mmsv);
+
+ /* Keep PTP across the immediately following stmmac_xdp_open(). That
+ * function releases it if reopening fails, before returning DOWN.
+ */
}
int stmmac_xdp_open(struct net_device *dev)
@@ -7161,14 +7381,14 @@ int stmmac_xdp_open(struct net_device *dev)
u8 chan;
int ret;
- ret = alloc_dma_desc_resources(priv, &priv->dma_conf);
+ ret = alloc_dma_desc_resources(priv, priv->dma_conf);
if (ret < 0) {
netdev_err(dev, "%s: DMA descriptors allocation failed\n",
__func__);
goto dma_desc_error;
}
- ret = init_dma_desc_rings(dev, &priv->dma_conf, GFP_KERNEL);
+ ret = init_dma_desc_rings(dev, priv->dma_conf, GFP_KERNEL);
if (ret < 0) {
netdev_err(dev, "%s: DMA descriptors initialization failed\n",
__func__);
@@ -7188,7 +7408,7 @@ int stmmac_xdp_open(struct net_device *dev)
/* DMA RX Channel Configuration */
for (chan = 0; chan < rx_cnt; chan++) {
- rx_q = &priv->dma_conf.rx_queue[chan];
+ rx_q = &priv->dma_conf->rx_queue[chan];
stmmac_init_rx_chan(priv, priv->ioaddr, priv->plat->dma_cfg,
rx_q->dma_rx_phy, chan);
@@ -7203,13 +7423,16 @@ int stmmac_xdp_open(struct net_device *dev)
/* DMA TX Channel Configuration */
for (chan = 0; chan < tx_cnt; chan++) {
- tx_q = &priv->dma_conf.tx_queue[chan];
+ tx_q = &priv->dma_conf->tx_queue[chan];
stmmac_init_tx_chan(priv, priv->ioaddr, priv->plat->dma_cfg,
tx_q->dma_tx_phy, chan);
stmmac_set_queue_tx_tail_ptr(priv, tx_q, chan, 0);
+ if (tx_q->tbs & STMMAC_TBS_AVAIL)
+ stmmac_enable_tbs(priv, priv->ioaddr, 1, chan);
+
hrtimer_setup(&tx_q->txtimer, stmmac_tx_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
}
@@ -7225,27 +7448,28 @@ int stmmac_xdp_open(struct net_device *dev)
/* Enable NAPI process*/
stmmac_enable_all_queues(priv);
- netif_carrier_on(dev);
- netif_tx_start_all_queues(dev);
stmmac_enable_all_dma_irq(priv);
+ priv->datapath = STMMAC_DATAPATH_RUNNING;
+ phylink_start(priv->phylink);
+ netif_device_attach(dev);
return 0;
irq_error:
- for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++)
- hrtimer_cancel(&priv->dma_conf.tx_queue[chan].txtimer);
+ stmmac_stop_tx_queues(priv);
+ stmmac_stop_all_dma(priv);
+ stmmac_mac_set(priv, priv->ioaddr, false);
init_error:
- free_dma_desc_resources(priv, &priv->dma_conf);
+ free_dma_desc_resources(priv, priv->dma_conf);
dma_desc_error:
+ stmmac_release_ptp(priv);
return ret;
}
int stmmac_xsk_wakeup(struct net_device *dev, u32 queue, u32 flags)
{
struct stmmac_priv *priv = netdev_priv(dev);
- struct stmmac_rx_queue *rx_q;
- struct stmmac_tx_queue *tx_q;
struct stmmac_channel *ch;
if (test_bit(STMMAC_DOWN, &priv->state) ||
@@ -7259,11 +7483,9 @@ int stmmac_xsk_wakeup(struct net_device *dev, u32 queue, u32 flags)
queue >= priv->plat->tx_queues_to_use)
return -EINVAL;
- rx_q = &priv->dma_conf.rx_queue[queue];
- tx_q = &priv->dma_conf.tx_queue[queue];
ch = &priv->channel[queue];
- if (!rx_q->xsk_pool && !tx_q->xsk_pool)
+ if (!test_bit(queue, priv->af_xdp_zc_qps))
return -EINVAL;
if (!napi_if_scheduled_mark_missed(&ch->rxtx_napi)) {
@@ -7364,6 +7586,9 @@ static void stmmac_reset_subtask(struct stmmac_priv *priv)
netdev_err(priv->dev, "Reset adapter.\n");
rtnl_lock();
+ if (!netif_device_present(priv->dev))
+ goto out_unlock;
+
netif_trans_update(priv->dev);
while (test_and_set_bit(STMMAC_RESETING, &priv->state))
usleep_range(1000, 2000);
@@ -7373,6 +7598,7 @@ static void stmmac_reset_subtask(struct stmmac_priv *priv)
dev_open(priv->dev, NULL);
clear_bit(STMMAC_DOWN, &priv->state);
clear_bit(STMMAC_RESETING, &priv->state);
+out_unlock:
rtnl_unlock();
}
@@ -7617,13 +7843,37 @@ static void stmmac_napi_del(struct net_device *dev)
}
}
-int stmmac_reinit_queues(struct net_device *dev, u8 rx_cnt, u8 tx_cnt)
+/* Rebuild only the datapath. The administratively-up device still owns its
+ * PHY attachment and runtime-PM reference, even if this reopen fails.
+ */
+static int stmmac_reopen(struct net_device *dev)
{
struct stmmac_priv *priv = netdev_priv(dev);
- int ret = 0, i;
+ struct stmmac_dma_conf *old_conf = priv->dma_conf;
+ struct stmmac_dma_conf *dma_conf;
+ int ret;
- if (netif_running(dev))
- stmmac_release(dev);
+ dma_conf = stmmac_setup_dma_desc(priv, dev->mtu);
+ if (IS_ERR(dma_conf))
+ return PTR_ERR(dma_conf);
+
+ ret = __stmmac_open(dev, dma_conf);
+ if (ret) {
+ priv->dma_conf = old_conf;
+ free_dma_desc_resources(priv, dma_conf);
+ kfree(dma_conf);
+ return ret;
+ }
+
+ kfree(old_conf);
+ netif_device_attach(dev);
+ return 0;
+}
+
+static void stmmac_set_queues(struct net_device *dev, u8 rx_cnt, u8 tx_cnt)
+{
+ struct stmmac_priv *priv = netdev_priv(dev);
+ int i;
stmmac_napi_del(dev);
@@ -7635,9 +7885,31 @@ int stmmac_reinit_queues(struct net_device *dev, u8 rx_cnt, u8 tx_cnt)
rx_cnt);
stmmac_napi_add(dev);
+}
+
+int stmmac_reinit_queues(struct net_device *dev, u8 rx_cnt, u8 tx_cnt)
+{
+ struct stmmac_priv *priv = netdev_priv(dev);
+ u8 old_rx = priv->plat->rx_queues_to_use;
+ u8 old_tx = priv->plat->tx_queues_to_use;
+ int ret = 0;
+
+ if (netif_running(dev)) {
+ if (!netif_device_present(dev))
+ return -ENETDOWN;
+ netif_device_detach(dev);
+ __stmmac_release(dev);
+ }
+
+ stmmac_set_queues(dev, rx_cnt, tx_cnt);
if (netif_running(dev))
- ret = stmmac_open(dev);
+ ret = stmmac_reopen(dev);
+ if (ret) {
+ stmmac_set_queues(dev, old_rx, old_tx);
+ netdev_err(dev, "failed reopening after channel change: %pe; interface remains detached\n",
+ ERR_PTR(ret));
+ }
return ret;
}
@@ -7645,16 +7917,28 @@ int stmmac_reinit_queues(struct net_device *dev, u8 rx_cnt, u8 tx_cnt)
int stmmac_reinit_ringparam(struct net_device *dev, u32 rx_size, u32 tx_size)
{
struct stmmac_priv *priv = netdev_priv(dev);
+ u32 old_rx = priv->dma_conf->dma_rx_size;
+ u32 old_tx = priv->dma_conf->dma_tx_size;
int ret = 0;
- if (netif_running(dev))
- stmmac_release(dev);
+ if (netif_running(dev)) {
+ if (!netif_device_present(dev))
+ return -ENETDOWN;
+ netif_device_detach(dev);
+ __stmmac_release(dev);
+ }
- priv->dma_conf.dma_rx_size = rx_size;
- priv->dma_conf.dma_tx_size = tx_size;
+ priv->dma_conf->dma_rx_size = rx_size;
+ priv->dma_conf->dma_tx_size = tx_size;
if (netif_running(dev))
- ret = stmmac_open(dev);
+ ret = stmmac_reopen(dev);
+ if (ret) {
+ priv->dma_conf->dma_rx_size = old_rx;
+ priv->dma_conf->dma_tx_size = old_tx;
+ netdev_err(dev, "failed reopening after ring change: %pe; interface remains detached\n",
+ ERR_PTR(ret));
+ }
return ret;
}
@@ -7816,6 +8100,13 @@ struct plat_stmmacenet_data *stmmac_plat_dat_alloc(struct device *dev)
}
EXPORT_SYMBOL_GPL(stmmac_plat_dat_alloc);
+static void stmmac_free_dma_conf(void *data)
+{
+ struct stmmac_priv *priv = data;
+
+ kfree(priv->dma_conf);
+}
+
static int __stmmac_dvr_probe(struct device *device,
struct plat_stmmacenet_data *plat_dat,
struct stmmac_resources *res)
@@ -7840,6 +8131,13 @@ static int __stmmac_dvr_probe(struct device *device,
priv = netdev_priv(ndev);
priv->device = device;
priv->dev = ndev;
+ /* Keep ring sizes and per-queue settings even while the device is down. */
+ priv->dma_conf = kzalloc_obj(*priv->dma_conf);
+ if (!priv->dma_conf)
+ return -ENOMEM;
+ ret = devm_add_action_or_reset(device, stmmac_free_dma_conf, priv);
+ if (ret)
+ return ret;
for (i = 0; i < MTL_MAX_RX_QUEUES; i++)
u64_stats_init(&priv->xstats.rxq_stats[i].napi_syncp);
@@ -8193,34 +8491,41 @@ EXPORT_SYMBOL_GPL(stmmac_dvr_remove);
/**
* stmmac_suspend - suspend callback
* @dev: device pointer
- * Description: this is the function to suspend the device and it is called
- * by the platform driver to stop the network queue, release the resources,
- * program the PMT register (for WoL), clean and release driver resources.
+ * Description: stop network activity and program hardware for system sleep,
+ * preserving any datapath resources still owned for resume or close.
*/
int stmmac_suspend(struct device *dev)
{
struct net_device *ndev = dev_get_drvdata(dev);
struct stmmac_priv *priv = netdev_priv(ndev);
- u8 chan;
- if (!ndev || !netif_running(ndev))
+ rtnl_lock();
+ if (!netif_running(ndev) || priv->hw_suspended) {
+ rtnl_unlock();
goto suspend_bsp;
+ }
+
+ /* A failed datapath cannot provide a working MAC wake path. It may
+ * even have released its wake IRQ. Do not silently suspend without WoL.
+ */
+ if (priv->wolopts && priv->datapath != STMMAC_DATAPATH_RUNNING) {
+ netdev_err(ndev, "cannot suspend failed datapath with MAC WoL enabled\n");
+ rtnl_unlock();
+ return -EBUSY;
+ }
mutex_lock(&priv->lock);
netif_device_detach(ndev);
- stmmac_disable_all_queues(priv);
-
- for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++)
- hrtimer_cancel(&priv->dma_conf.tx_queue[chan].txtimer);
+ if (priv->datapath == STMMAC_DATAPATH_RUNNING)
+ stmmac_quiesce(priv);
if (priv->eee_sw_timer_en) {
priv->tx_path_in_lpi_mode = false;
timer_delete_sync(&priv->eee_ctrl_timer);
}
- /* Stop TX/RX DMA */
stmmac_stop_all_dma(priv);
stmmac_legacy_serdes_power_down(priv);
@@ -8236,12 +8541,14 @@ int stmmac_suspend(struct device *dev)
mutex_unlock(&priv->lock);
- rtnl_lock();
phylink_suspend(priv->phylink, !!priv->wolopts);
- rtnl_unlock();
+ if (priv->datapath == STMMAC_DATAPATH_RUNNING)
+ priv->datapath = STMMAC_DATAPATH_SUSPENDED;
+ priv->hw_suspended = true;
if (stmmac_fpe_supported(priv))
ethtool_mmsv_stop(&priv->fpe_cfg.mmsv);
+ rtnl_unlock();
suspend_bsp:
if (priv->plat->suspend)
@@ -8253,7 +8560,7 @@ EXPORT_SYMBOL_GPL(stmmac_suspend);
static void stmmac_reset_rx_queue(struct stmmac_priv *priv, u32 queue)
{
- struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue];
+ struct stmmac_rx_queue *rx_q = &priv->dma_conf->rx_queue[queue];
rx_q->cur_rx = 0;
rx_q->dirty_rx = 0;
@@ -8261,7 +8568,7 @@ static void stmmac_reset_rx_queue(struct stmmac_priv *priv, u32 queue)
static void stmmac_reset_tx_queue(struct stmmac_priv *priv, u32 queue)
{
- struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue];
+ struct stmmac_tx_queue *tx_q = &priv->dma_conf->tx_queue[queue];
tx_q->cur_tx = 0;
tx_q->dirty_tx = 0;
@@ -8305,35 +8612,35 @@ int stmmac_resume(struct device *dev)
return ret;
}
- if (!netif_running(ndev))
- return 0;
+ rtnl_lock();
+ if (!netif_running(ndev)) {
+ ret = 0;
+ goto out_unlock;
+ }
- /* Power Down bit, into the PM register, is cleared
- * automatically as soon as a magic packet or a Wake-up frame
- * is received. Anyway, it's better to manually clear
- * this bit because it can generate problems while resuming
- * from another devices (e.g. serial console).
- */
- if (priv->wolopts) {
- mutex_lock(&priv->lock);
- stmmac_pmt(priv, priv->hw, 0);
- mutex_unlock(&priv->lock);
- priv->irq_wake = 0;
- } else {
- pinctrl_pm_select_default_state(priv->device);
- /* reset the phy so that it's ready */
- if (priv->mii)
- stmmac_mdio_reset(priv->mii);
+ if (priv->hw_suspended) {
+ ret = stmmac_resume_hw(priv);
+ if (ret)
+ goto out_unlock;
+
+ /* Terminate PM speed control without restarting a datapath
+ * whose IRQs or rings were released before system sleep.
+ */
+ if (priv->datapath != STMMAC_DATAPATH_SUSPENDED)
+ phylink_stop(priv->phylink);
+ }
+
+ if (priv->datapath != STMMAC_DATAPATH_SUSPENDED) {
+ ret = 0;
+ goto out_unlock;
}
if (!(priv->plat->flags & STMMAC_FLAG_SERDES_UP_AFTER_PHY_LINKUP)) {
ret = stmmac_legacy_serdes_power_up(priv);
if (ret < 0)
- return ret;
+ goto out_unlock;
}
- rtnl_lock();
-
/* Prepare the PHY to resume, ensuring that its clocks which are
* necessary for the MAC DMA reset to complete are running
*/
@@ -8344,15 +8651,12 @@ int stmmac_resume(struct device *dev)
stmmac_reset_queues_param(priv);
stmmac_free_tx_skbufs(priv);
- stmmac_clear_descriptors(priv, &priv->dma_conf);
+ stmmac_clear_descriptors(priv, priv->dma_conf);
- ret = stmmac_hw_setup(ndev);
+ ret = stmmac_hw_setup(ndev, false);
if (ret < 0) {
netdev_err(priv->dev, "%s: Hw setup failed\n", __func__);
- stmmac_legacy_serdes_power_down(priv);
- mutex_unlock(&priv->lock);
- rtnl_unlock();
- return ret;
+ goto error_stop_dma;
}
stmmac_init_timestamping(priv);
@@ -8374,11 +8678,25 @@ int stmmac_resume(struct device *dev)
* workqueue thread, which will race with initialisation.
*/
phylink_resume(priv->phylink);
- rtnl_unlock();
-
+ priv->datapath = STMMAC_DATAPATH_RUNNING;
netif_device_attach(ndev);
+ rtnl_unlock();
return 0;
+
+error_stop_dma:
+ stmmac_stop_all_dma(priv);
+ stmmac_mac_set(priv, priv->ioaddr, false);
+ stmmac_legacy_serdes_power_down(priv);
+ mutex_unlock(&priv->lock);
+ /*
+ * Keep the suspended data path detached. A later resume may retry, or
+ * ndo_stop() can release its resources without disabling NAPI again.
+ */
+out_unlock:
+ rtnl_unlock();
+
+ return ret;
}
EXPORT_SYMBOL_GPL(stmmac_resume);
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
index 6128ed1bd521a..9bd622517389d 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
@@ -958,8 +958,9 @@ static int __maybe_unused stmmac_pltfr_noirq_suspend(struct device *dev)
return 0;
if (!priv->wolopts) {
- /* Disable clock in case of PWM is off */
- clk_disable_unprepare(priv->plat->clk_ptp_ref);
+ /* A detached datapath may already have released its PTP clock. */
+ if (priv->datapath != STMMAC_DATAPATH_DOWN)
+ clk_disable_unprepare(priv->plat->clk_ptp_ref);
ret = pm_runtime_force_suspend(dev);
if (ret)
@@ -984,12 +985,14 @@ static int __maybe_unused stmmac_pltfr_noirq_resume(struct device *dev)
if (ret)
return ret;
- ret = clk_prepare_enable(priv->plat->clk_ptp_ref);
- if (ret < 0) {
- netdev_warn(priv->dev,
- "failed to enable PTP reference clock: %pe\n",
- ERR_PTR(ret));
- return ret;
+ if (priv->datapath != STMMAC_DATAPATH_DOWN) {
+ ret = clk_prepare_enable(priv->plat->clk_ptp_ref);
+ if (ret < 0) {
+ netdev_warn(priv->dev,
+ "failed to enable PTP reference clock: %pe\n",
+ ERR_PTR(ret));
+ return ret;
+ }
}
}
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c
index c25dc9f89270c..2511339e55e4e 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c
@@ -887,8 +887,8 @@ static int stmmac_test_flowctrl(struct stmmac_priv *priv)
struct stmmac_channel *ch = &priv->channel[i];
u32 tail;
- tail = priv->dma_conf.rx_queue[i].dma_rx_phy +
- (priv->dma_conf.dma_rx_size * sizeof(struct dma_desc));
+ tail = priv->dma_conf->rx_queue[i].dma_rx_phy +
+ (priv->dma_conf->dma_rx_size * sizeof(struct dma_desc));
stmmac_set_rx_tail_ptr(priv, priv->ioaddr, tail, i);
stmmac_start_rx(priv, priv->ioaddr, i);
@@ -1787,7 +1787,7 @@ static int stmmac_test_arpoffload(struct stmmac_priv *priv)
static int __stmmac_test_jumbo(struct stmmac_priv *priv, u16 queue)
{
struct stmmac_packet_attrs attr = { };
- int size = priv->dma_conf.dma_buf_sz;
+ int size = priv->dma_conf->dma_buf_sz;
if (!dwmac_is_xmac(priv->plat->core_type))
size -= NET_IP_ALIGN;
@@ -1873,7 +1873,7 @@ static int stmmac_test_tbs(struct stmmac_priv *priv)
/* Find first TBS enabled Queue, if any */
for (i = 0; i < priv->plat->tx_queues_to_use; i++)
- if (priv->dma_conf.tx_queue[i].tbs & STMMAC_TBS_AVAIL)
+ if (priv->dma_conf->tx_queue[i].tbs & STMMAC_TBS_AVAIL)
break;
if (i >= priv->plat->tx_queues_to_use)
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c
index 42a00446e9b41..bb1dfe2702acd 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c
@@ -1197,13 +1197,13 @@ static int tc_setup_etf(struct stmmac_priv *priv,
return -EOPNOTSUPP;
if (qopt->queue >= priv->plat->tx_queues_to_use)
return -EINVAL;
- if (!(priv->dma_conf.tx_queue[qopt->queue].tbs & STMMAC_TBS_AVAIL))
+ if (!(priv->dma_conf->tx_queue[qopt->queue].tbs & STMMAC_TBS_AVAIL))
return -EINVAL;
if (qopt->enable)
- priv->dma_conf.tx_queue[qopt->queue].tbs |= STMMAC_TBS_EN;
+ priv->dma_conf->tx_queue[qopt->queue].tbs |= STMMAC_TBS_EN;
else
- priv->dma_conf.tx_queue[qopt->queue].tbs &= ~STMMAC_TBS_EN;
+ priv->dma_conf->tx_queue[qopt->queue].tbs &= ~STMMAC_TBS_EN;
netdev_info(priv->dev, "%s ETF for Queue %d\n",
qopt->enable ? "enabled" : "disabled", qopt->queue);
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_xdp.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_xdp.c
index d7e4db7224b0c..907ac49a1b764 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_xdp.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_xdp.c
@@ -9,7 +9,6 @@
static int stmmac_xdp_enable_pool(struct stmmac_priv *priv,
struct xsk_buff_pool *pool, u16 queue)
{
- struct stmmac_channel *ch = &priv->channel[queue];
bool need_update;
u32 frame_size;
int err;
@@ -31,25 +30,26 @@ static int stmmac_xdp_enable_pool(struct stmmac_priv *priv,
return err;
}
- need_update = netif_running(priv->dev) && stmmac_xdp_is_enabled(priv);
+ need_update = priv->datapath == STMMAC_DATAPATH_RUNNING &&
+ stmmac_xdp_is_enabled(priv);
- if (need_update) {
- napi_disable(&ch->rx_napi);
- napi_disable(&ch->tx_napi);
- stmmac_disable_rx_queue(priv, queue);
- stmmac_disable_tx_queue(priv, queue);
- }
+ if (need_update)
+ stmmac_xdp_release(priv->dev);
set_bit(queue, priv->af_xdp_zc_qps);
if (need_update) {
- stmmac_enable_rx_queue(priv, queue);
- stmmac_enable_tx_queue(priv, queue);
- napi_enable(&ch->rxtx_napi);
-
- err = stmmac_xsk_wakeup(priv->dev, queue, XDP_WAKEUP_RX);
- if (err)
+ err = stmmac_xdp_open(priv->dev);
+ if (err) {
+ clear_bit(queue, priv->af_xdp_zc_qps);
+ xsk_pool_dma_unmap(pool, STMMAC_RX_DMA_ATTR);
+ netdev_err(priv->dev, "failed reopening after XSK pool attach: %pe; interface remains detached\n",
+ ERR_PTR(err));
return err;
+ }
+
+ /* The pool is installed even if link resolution is still pending. */
+ napi_schedule(&priv->channel[queue].rxtx_napi);
}
return 0;
@@ -57,9 +57,9 @@ static int stmmac_xdp_enable_pool(struct stmmac_priv *priv,
static int stmmac_xdp_disable_pool(struct stmmac_priv *priv, u16 queue)
{
- struct stmmac_channel *ch = &priv->channel[queue];
struct xsk_buff_pool *pool;
bool need_update;
+ int err;
if (queue >= priv->plat->rx_queues_to_use ||
queue >= priv->plat->tx_queues_to_use)
@@ -69,26 +69,24 @@ static int stmmac_xdp_disable_pool(struct stmmac_priv *priv, u16 queue)
if (!pool)
return -EINVAL;
- need_update = netif_running(priv->dev) && stmmac_xdp_is_enabled(priv);
+ need_update = priv->datapath == STMMAC_DATAPATH_RUNNING &&
+ stmmac_xdp_is_enabled(priv);
- if (need_update) {
- napi_disable(&ch->rxtx_napi);
- stmmac_disable_rx_queue(priv, queue);
- stmmac_disable_tx_queue(priv, queue);
- synchronize_rcu();
- }
+ if (need_update)
+ stmmac_xdp_release(priv->dev);
xsk_pool_dma_unmap(pool, STMMAC_RX_DMA_ATTR);
clear_bit(queue, priv->af_xdp_zc_qps);
if (need_update) {
- stmmac_enable_rx_queue(priv, queue);
- stmmac_enable_tx_queue(priv, queue);
- napi_enable(&ch->rx_napi);
- napi_enable(&ch->tx_napi);
+ err = stmmac_xdp_open(priv->dev);
+ if (err)
+ netdev_err(priv->dev, "failed reopening after XSK pool removal: %pe; interface remains detached\n",
+ ERR_PTR(err));
}
+ /* Socket teardown must be able to unmap and free the removed pool. */
return 0;
}
@@ -106,8 +104,9 @@ int stmmac_xdp_set_prog(struct stmmac_priv *priv, struct bpf_prog *prog,
struct bpf_prog *old_prog;
bool need_update;
bool if_running;
+ int ret;
- if_running = netif_running(dev);
+ if_running = priv->datapath == STMMAC_DATAPATH_RUNNING;
if (prog && dev->mtu > ETH_DATA_LEN) {
/* For now, the driver doesn't support XDP functionality with
@@ -117,25 +116,41 @@ int stmmac_xdp_set_prog(struct stmmac_priv *priv, struct bpf_prog *prog,
return -EOPNOTSUPP;
}
- if (!prog)
- xdp_features_clear_redirect_target(dev);
-
need_update = !!priv->xdp_prog != !!prog;
if (if_running && need_update)
stmmac_xdp_release(dev);
old_prog = xchg(&priv->xdp_prog, prog);
- if (old_prog)
- bpf_prog_put(old_prog);
/* Disable RX SPH for XDP operation */
priv->sph_active = priv->sph_capable && !stmmac_xdp_is_enabled(priv);
- if (if_running && need_update)
- stmmac_xdp_open(dev);
+ if (if_running && need_update) {
+ ret = stmmac_xdp_open(dev);
+ if (ret) {
+ netdev_err(dev, "failed reopening after XDP change: %pe; interface remains detached\n",
+ ERR_PTR(ret));
+ if (prog) {
+ /* The core retains the old program on error and drops
+ * the reference it passed for the proposed program.
+ */
+ xchg(&priv->xdp_prog, old_prog);
+ priv->sph_active = priv->sph_capable && !old_prog;
+ return ret;
+ }
+ /* Uninstalling a BPF link must release its program even
+ * if the non-XDP datapath cannot be restarted.
+ */
+ }
+ }
+
+ if (old_prog)
+ bpf_prog_put(old_prog);
if (prog)
xdp_features_set_redirect_target(dev, false);
+ else
+ xdp_features_clear_redirect_target(dev);
return 0;
}
diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c
index 1bbcf46c83566..b7d30ca340316 100644
--- a/drivers/net/phy/phylink.c
+++ b/drivers/net/phy/phylink.c
@@ -78,6 +78,7 @@ struct phylink {
bool link_failed;
bool suspend_link_up;
+ bool suspend_speed_down;
bool force_major_config;
bool major_config_failed;
bool mac_supports_eee_ops;
@@ -2498,6 +2499,14 @@ void phylink_start(struct phylink *pl)
}
EXPORT_SYMBOL_GPL(phylink_start);
+static void phylink_restore_suspend_speed(struct phylink *pl)
+{
+ if (pl->suspend_speed_down) {
+ phylink_speed_up(pl);
+ pl->suspend_speed_down = false;
+ }
+}
+
/**
* phylink_stop() - stop a phylink instance
* @pl: a pointer to a &struct phylink returned from phylink_create()
@@ -2509,11 +2518,30 @@ EXPORT_SYMBOL_GPL(phylink_start);
*
* This will synchronously bring down the link if the link is not already
* down (in other words, it will trigger a mac_link_down() method call.)
+ * A suspended instance may be stopped without first calling phylink_resume().
+ * In particular, closing a device after a failed resume must not restart the
+ * link or reconfigure the MAC just to finish shutting it down.
+ * Any PHY advertisement reduced by phylink_suspend() is restored as part
+ * of this transition.
+ * If phylink_prepare_resume() powered up an already stopped PHY, suspend
+ * it again when Wake-on-LAN permits.
*/
void phylink_stop(struct phylink *pl)
{
ASSERT_RTNL();
+ /* Also undo PHY speed control when terminating a suspended instance. */
+ phylink_restore_suspend_speed(pl);
+
+ if (test_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state)) {
+ /* A failed MAC resume may have called phylink_prepare_resume()
+ * and powered the stopped PHY back up to supply its RX clock.
+ */
+ if (pl->phydev)
+ phy_suspend(pl->phydev);
+ return;
+ }
+
if (pl->sfp_bus)
sfp_upstream_stop(pl->sfp_bus);
if (pl->phydev)
@@ -2526,6 +2554,16 @@ void phylink_stop(struct phylink *pl)
phylink_run_resolve_and_disable(pl, PHYLINK_DISABLE_STOPPED);
+ if (test_bit(PHYLINK_DISABLE_MAC_WOL, &pl->phylink_disable_state)) {
+ /* Finish the link-down deferred by MAC WoL, without restarting. */
+ flush_work(&pl->resolve);
+ mutex_lock(&pl->state_mutex);
+ if (pl->suspend_link_up)
+ phylink_link_down(pl);
+ __clear_bit(PHYLINK_DISABLE_MAC_WOL, &pl->phylink_disable_state);
+ mutex_unlock(&pl->state_mutex);
+ }
+
pl->pcs_state = PCS_STATE_DOWN;
phylink_pcs_disable(pl->pcs);
@@ -2657,8 +2695,10 @@ void phylink_suspend(struct phylink *pl, bool mac_wol)
phylink_stop(pl);
}
- if (phylink_phy_pm_speed_ctrl(pl))
+ if (phylink_phy_pm_speed_ctrl(pl)) {
phylink_speed_down(pl, false);
+ pl->suspend_speed_down = true;
+ }
}
EXPORT_SYMBOL_GPL(phylink_suspend);
@@ -2698,8 +2738,7 @@ void phylink_resume(struct phylink *pl)
{
ASSERT_RTNL();
- if (phylink_phy_pm_speed_ctrl(pl))
- phylink_speed_up(pl);
+ phylink_restore_suspend_speed(pl);
if (test_bit(PHYLINK_DISABLE_MAC_WOL, &pl->phylink_disable_state)) {
/* Wake-on-Lan enabled, MAC handling */
@@ -3616,6 +3655,12 @@ int phylink_speed_down(struct phylink *pl, bool sync)
ASSERT_RTNL();
+ /* An explicit request takes over from suspend-time speed control.
+ * Restore the original advertisement before saving it again, so a
+ * repeated speed-down cannot replace it with the reduced advertisement.
+ */
+ phylink_restore_suspend_speed(pl);
+
if (!pl->sfp_bus && pl->phydev)
ret = phy_speed_down(pl->phydev, sync);
|
| 82/3 | 2026/09/26 22:56 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 40494 output: 20 thoughts: 898 Thoughts: **KMSAN Fuzzing Session Assessment** Okay, so I need to decide if this patch series warrants a dedicated KMSAN fuzzing session. The rule is pretty clear: I only flag `NeedsKMSAN=true` if the code changes introduce or expose *uninitialized memory* risks that KMSAN is uniquely positioned to detect. KASAN, LOCKDEP, and the like handle other memory safety issues. So, it's about catching those reads of uninitialized memory on the stack, heap, or in page allocations, and also kernel-to-user info-leaks. I need to focus on these scenarios: changes to kernel structures sent to userspace where uninitialized fields are possible, conditional logic reliant on uninitialized variables, the allocation/initialization of complex data structures with potential uninitialized fields, and bounds checks that might inadvertently expose uninitialized data in existing buffers. If it's just about out-of-bounds accesses, use-after-free, deadlocks, etc., KMSAN isn't the right tool. If *everything* is fully zeroed or initialized, then it's a no-go for KMSAN. Alright, the patch touches a bunch of files within the `stmmac` driver: `chain_mode.c`, `ring_mode.c`, `stmmac.h`, `stmmac_ethtool.c`, `stmmac_main.c`, `stmmac_platform.c`, `stmmac_selftests.c`, `stmmac_tc.c`, `stmmac_xdp.c`, and `phylink.c`. Lots of network-related code. My first glance suggests that this might have the potential for uninitialized memory usage. I need to dig in. The main change seems to be in the `stmmac_priv` structure. It's now using a pointer for `dma_conf` and a new `datapath` enum and `hw_suspended` boolean. In `__stmmac_dvr_probe`, the `dma_conf` is now allocated via `kzalloc_obj`, or `kzalloc` which I'll have to investigate. The interesting part is that `kzalloc_obj` uses a pointer dereference. I need to understand that call to `kzalloc_obj(*priv->dma_conf)` and ensure the intended size is properly used. I also need to make sure the structure is properly initialized. If there's an error and `kzalloc` fails, that's already handled, so that's good. The new `datapath` enum and `hw_suspended` bool look fine. I'll need to double check how `dma_conf` is used later on in the code and what it is initialized to, and if it is sent to userspace. |
| 83/3 | 2026/09/26 22:56 | tool | grepper |
0mArgs:{
"Expression": "#define kzalloc_obj"
}Results: {
"Output": "include/linux/slab.h=1073=void *kmalloc_nolock(size_t size, gfp_t gfp_flags, int node);\n--\ninclude/linux/slab.h-1154-/* All kzalloc aliases for kmalloc_(obj|objs|flex). */\ninclude/linux/slab.h:1155:#define kzalloc_obj(P, ...) \\\ninclude/linux/slab.h-1156-\t__alloc_objs(kzalloc, default_gfp(__VA_ARGS__), typeof(P), 1)\ninclude/linux/slab.h:1157:#define kzalloc_objs(P, COUNT, ...) \\\ninclude/linux/slab.h-1158-\t__alloc_objs(kzalloc, default_gfp(__VA_ARGS__), typeof(P), COUNT)\n--\ntools/include/linux/slab.h=200=static inline unsigned int kmem_cache_sheaf_size(struct slab_sheaf *sheaf)\n--\ntools/include/linux/slab.h-210-\ntools/include/linux/slab.h:211:#define kzalloc_obj(P, ...) \\\ntools/include/linux/slab.h-212-\t__alloc_objs(kzalloc, default_gfp(__VA_ARGS__), typeof(P), 1)\n"
} |
| 84/3 | 2026/09/26 22:56 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 42349 output: 51 thoughts: 76 Thoughts: Here's my take on the memory allocation process, as I understand it, given the context: **Memory Initialization and DMA Descriptor Setup** Okay, so `kzalloc_obj` uses `kzalloc`. That immediately tells me the memory allocated is zeroed, which is crucial for safety and preventing potential leaks or unexpected behavior. Makes sense. Now, let's dive into the details. I need to understand how the DMA descriptors are set up in this particular driver. I'll focus on `stmmac_setup_dma_desc`, specifically within `drivers/net/ethernet/stmicro/stmmac/stmmac_main.c`. That's where the rubber meets the road, so to speak, in terms of initializing the descriptors. I want to see *how* the driver prepares the DMA engine for efficient data transfers. Let's see what they're doing. |
| 85/3 | 2026/09/26 22:57 | tool | grepper |
0mArgs:{
"Expression": "stmmac_setup_dma_desc",
"PathPrefix": "drivers/net/ethernet/stmicro/stmmac/stmmac_main.c"
}Results: {
"Output": "drivers/net/ethernet/stmicro/stmmac/stmmac_main.c=4162=static void stmmac_set_irq_state(struct stmmac_priv *priv, bool enable)\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-4188-/**\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c:4189: * stmmac_setup_dma_desc - allocate and initialize a DMA configuration\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-4190- * @priv: driver private structure\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c=4194=static struct stmmac_dma_conf *\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c:4195:stmmac_setup_dma_desc(struct stmmac_priv *priv, unsigned int mtu)\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-4196-{\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c=4363=static int stmmac_open(struct net_device *dev)\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-4373-\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c:4374:\tdma_conf = stmmac_setup_dma_desc(priv, dev-\u003emtu);\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-4375-\tif (IS_ERR(dma_conf))\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c=6341=static int stmmac_reconfigure_mtu(struct net_device *dev, int mtu)\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-6349-\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c:6350:\tnew_conf = stmmac_setup_dma_desc(priv, mtu);\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-6351-\tif (IS_ERR(new_conf))\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c=7849=static int stmmac_reopen(struct net_device *dev)\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-7855-\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c:7856:\tdma_conf = stmmac_setup_dma_desc(priv, dev-\u003emtu);\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-7857-\tif (IS_ERR(dma_conf))\n"
} |
| 86/3 | 2026/09/26 22:57 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 43467 output: 50 thoughts: 48 Thoughts: Okay, let's dive into this. **Examining the DMA Descriptor Setup in `stmmac_main.c`** Alright, I need to get a handle on how this DMA descriptor setup is being handled. I know this is a core piece of the STMMAC driver, and I want to refresh my memory on the specifics of the `stmmac_setup_dma_desc` function, so I'm heading to `drivers/net/ethernet/stmicro/stmmac/stmmac_main.c` around line 4194. I'm assuming that function is key for configuring the descriptors, but I need to confirm the details. I will review the code to ensure I have a robust understanding of the data structures involved, the sequence of operations, and any potential optimizations or error handling within that crucial section. |
| 87/3 | 2026/09/26 22:57 | tool | read-file |
0mArgs:{
"File": "drivers/net/ethernet/stmicro/stmmac/stmmac_main.c",
"FirstLine": 4190,
"LineCount": 60
}Results: {
"Contents": "4190:\t * @priv: driver private structure\n4191:\t * @mtu: MTU to size the receive buffers for\n4192:\t * Return: the allocated configuration, or an ERR_PTR on failure\n4193:\t */\n4194:\tstatic struct stmmac_dma_conf *\n4195:\tstmmac_setup_dma_desc(struct stmmac_priv *priv, unsigned int mtu)\n4196:\t{\n4197:\t\tstruct stmmac_dma_conf *dma_conf;\n4198:\t\tint bfsize, len, ret;\n4199:\t\tu8 chan;\n4200:\t\n4201:\t\tdma_conf = kzalloc_obj(*dma_conf);\n4202:\t\tif (!dma_conf) {\n4203:\t\t\tnetdev_err(priv-\u003edev, \"%s: DMA conf allocation failed\\n\",\n4204:\t\t\t\t __func__);\n4205:\t\t\treturn ERR_PTR(-ENOMEM);\n4206:\t\t}\n4207:\t\n4208:\t\tlen = mtu + ETH_HLEN + 2 * VLAN_HLEN + ETH_FCS_LEN;\n4209:\t\n4210:\t\t/* Returns 0 or BUF_SIZE_16KiB if len \u003e 8KiB and dwmac4 or ring mode */\n4211:\t\tbfsize = stmmac_set_16kib_bfsize(priv, len);\n4212:\t\tif (bfsize \u003c 0)\n4213:\t\t\tbfsize = 0;\n4214:\t\n4215:\t\tif (bfsize \u003c BUF_SIZE_16KiB)\n4216:\t\t\tbfsize = stmmac_set_bfsize(len);\n4217:\t\n4218:\t\tdma_conf-\u003edma_buf_sz = bfsize;\n4219:\t\t/* Chose the tx/rx size from the already defined one in the\n4220:\t\t * priv struct. (if defined)\n4221:\t\t */\n4222:\t\tdma_conf-\u003edma_tx_size = priv-\u003edma_conf-\u003edma_tx_size;\n4223:\t\tdma_conf-\u003edma_rx_size = priv-\u003edma_conf-\u003edma_rx_size;\n4224:\t\n4225:\t\tif (!dma_conf-\u003edma_tx_size)\n4226:\t\t\tdma_conf-\u003edma_tx_size = DMA_DEFAULT_TX_SIZE;\n4227:\t\tif (!dma_conf-\u003edma_rx_size)\n4228:\t\t\tdma_conf-\u003edma_rx_size = DMA_DEFAULT_RX_SIZE;\n4229:\t\n4230:\t\t/* Earlier check for TBS */\n4231:\t\tfor (chan = 0; chan \u003c priv-\u003eplat-\u003etx_queues_to_use; chan++) {\n4232:\t\t\tstruct stmmac_tx_queue *tx_q = \u0026dma_conf-\u003etx_queue[chan];\n4233:\t\t\tint tbs_en = priv-\u003eplat-\u003etx_queues_cfg[chan].tbs_en;\n4234:\t\n4235:\t\t\t/* Setup per-TXQ tbs flag before TX descriptor alloc */\n4236:\t\t\ttx_q-\u003etbs |= tbs_en ? STMMAC_TBS_AVAIL : 0;\n4237:\t\t\ttx_q-\u003etbs |= priv-\u003edma_conf-\u003etx_queue[chan].tbs \u0026 STMMAC_TBS_EN;\n4238:\t\t}\n4239:\t\n4240:\t\tret = alloc_dma_desc_resources(priv, dma_conf);\n4241:\t\tif (ret \u003c 0) {\n4242:\t\t\tnetdev_err(priv-\u003edev, \"%s: DMA descriptors allocation failed\\n\",\n4243:\t\t\t\t __func__);\n4244:\t\t\tgoto alloc_error;\n4245:\t\t}\n4246:\t\n4247:\t\tret = init_dma_desc_rings(priv-\u003edev, dma_conf, GFP_KERNEL);\n4248:\t\tif (ret \u003c 0) {\n4249:\t\t\tnetdev_err(priv-\u003edev, \"%s: DMA descriptors initialization failed\\n\",\n"
} |
| 88/3 | 2026/09/26 22:57 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 44865 output: 50 thoughts: 731 Thoughts: **Analyzing DMA Configuration in stmmac Driver** Okay, let's break this down. I see a change in how the DMA configuration (`dma_conf`) is handled within the `stmmac` driver. Initially, I'm observing the allocation of `dma_conf` within `stmmac_setup_dma_desc`, specifically with `dma_conf = kzalloc_obj(*dma_conf);`. My first thought is to connect this to `stmmac_xdp_open`. In the `stmmac_xdp_open` function, I see that the driver attempts to allocate and initialize DMA descriptor rings using `priv->dma_conf`. The question then becomes, where did `priv->dma_conf` get initialized? Now I'm looking further back, I see a significant detail in `__stmmac_dvr_probe`: `priv->dma_conf = kzalloc_obj(*priv->dma_conf);`. That's where it's being allocated, it seems. And I need to remember the historical context... I recall that *before* this patch, `struct stmmac_dma_conf dma_conf;` was embedded directly within the `struct stmmac_priv`. Because `stmmac_priv` was allocated using `alloc_etherdev_mqs` (which uses `kzalloc`), the `dma_conf` struct was effectively zero-initialized. Now it's a pointer. This explains the `kzalloc_obj` call. So, we're allocating memory for `priv->dma_conf` after the driver has probed. The key question now is, what happens during the lifecycle of the device – during open, close, and MTU changes? Let's trace it through `stmmac_open`. I see that `stmmac_open` calls `stmmac_setup_dma_desc`, which is likely where the *current* DMA configuration is determined. Critically, it then stores this configuration in `dma_conf`. Notice the key lines within the `stmmac_open` function: ```c struct stmmac_dma_conf *old_conf = priv->dma_conf; struct stmmac_dma_conf *dma_conf; ... dma_conf = stmmac_setup_dma_desc(priv, dev->mtu); if (IS_ERR(dma_conf)) return PTR_ERR(dma_conf); ... ret = __stmmac_open(dev, dma_conf); ... kfree(old_conf); ... err_dma_resources: priv->dma_conf = old_conf; free_dma_desc_resources(priv, dma_conf); kfree(dma_conf); ``` Okay, so `stmmac_open` *replaces* the existing `priv->dma_conf` (which is stored in `old_conf`), calculates a *new* configuration, and sets `priv->dma_conf` to point to the new configuration. After successfully opening, the old configuration (`old_conf`) is freed. In error cases, the new configuration is freed and `priv->dma_conf` is restored to the previous value. I must dig further into what `__stmmac_open` does next. Now, looking inside `__stmmac_open`, I see that the calculated `dma_conf` is assigned to `priv->dma_conf`, ensuring that the new descriptor ring configuration is active. This confirms the replacement. It looks like the old `dma_conf` from `stmmac_open` is passed to `__stmmac_open` where it then sets `priv->dma_conf`. Now the remaining question: what's going on during release/close? Time to inspect `__stmmac_release` and `stmmac_release`. It is important to know that the new `dma_conf` (allocated by `stmmac_open` and then passed to `__stmmac_open`) is cleaned up when the device is closed, as the code shows. |
| 89/3 | 2026/09/26 22:57 | tool | read-file |
0mArgs:{
"File": "drivers/net/ethernet/stmicro/stmmac/stmmac_main.c",
"FirstLine": 4440,
"LineCount": 80
}Results: {
"Contents": "4440:\t\tstmmac_stop_tx_queues(priv);\n4441:\t}\n4442:\t\n4443:\tstatic void __stmmac_release(struct net_device *dev)\n4444:\t{\n4445:\t\tstruct stmmac_priv *priv = netdev_priv(dev);\n4446:\t\tenum stmmac_datapath_state state = priv-\u003edatapath;\n4447:\t\n4448:\t\t/* There may be no resources left after detached XDP reconfiguration. */\n4449:\t\tif (priv-\u003edatapath == STMMAC_DATAPATH_DOWN)\n4450:\t\t\treturn;\n4451:\t\n4452:\t\tphylink_stop(priv-\u003ephylink);\n4453:\t\n4454:\t\t/* Suspend retains the resources, but has already stopped activity. */\n4455:\t\tif (priv-\u003edatapath == STMMAC_DATAPATH_RUNNING)\n4456:\t\t\tstmmac_quiesce(priv);\n4457:\t\tpriv-\u003edatapath = STMMAC_DATAPATH_DOWN;\n4458:\t\n4459:\t\t/* Free the IRQ lines */\n4460:\t\tif (state != STMMAC_DATAPATH_HALTED)\n4461:\t\t\tstmmac_free_irq(dev, REQ_IRQ_ERR_ALL, 0);\n4462:\t\n4463:\t\t/* TX error IRQs can restart a queue after the first quiescence. */\n4464:\t\tstmmac_stop_tx_queues(priv);\n4465:\t\n4466:\t\t/* Stop TX/RX DMA after draining IRQ handlers which can restart it. */\n4467:\t\tstmmac_stop_all_dma(priv);\n4468:\t\t/* Link resolution need not have reached mac_link_up() yet. */\n4469:\t\tstmmac_mac_set(priv, priv-\u003eioaddr, false);\n4470:\t\n4471:\t\t/* Release and free the Rx/Tx resources */\n4472:\t\tfree_dma_desc_resources(priv, priv-\u003edma_conf);\n4473:\t\n4474:\t\tstmmac_release_ptp(priv);\n4475:\t\n4476:\t\tif (stmmac_fpe_supported(priv))\n4477:\t\t\tethtool_mmsv_stop(\u0026priv-\u003efpe_cfg.mmsv);\n4478:\t}\n4479:\t\n4480:\t/**\n4481:\t * stmmac_release - close entry point of the driver\n4482:\t * @dev : device pointer.\n4483:\t * Description:\n4484:\t * This is the stop entry point of the driver.\n4485:\t */\n4486:\tstatic int stmmac_release(struct net_device *dev)\n4487:\t{\n4488:\t\tstruct stmmac_priv *priv = netdev_priv(dev);\n4489:\t\tint ret;\n4490:\t\n4491:\t\t/* Resume may have failed before restoring pins or disabling MAC wake.\n4492:\t\t * Complete that cleanup without restarting the link or the datapath.\n4493:\t\t * If it fails, keep hw_suspended set so a fresh open can retry it.\n4494:\t\t */\n4495:\t\tret = stmmac_resume_hw(priv);\n4496:\t\tif (ret)\n4497:\t\t\tnetdev_err(dev, \"failed to restore hardware sleep state: %d\\n\", ret);\n4498:\t\n4499:\t\t/* If the PHY or MAC has WoL enabled, then the PHY will not be\n4500:\t\t * suspended when phylink_stop() is called below. Set the PHY\n4501:\t\t * to its slowest speed to save power.\n4502:\t\t */\n4503:\t\tif (device_may_wakeup(priv-\u003edevice))\n4504:\t\t\tphylink_speed_down(priv-\u003ephylink, false);\n4505:\t\n4506:\t\t__stmmac_release(dev);\n4507:\t\n4508:\t\tstmmac_legacy_serdes_power_down(priv);\n4509:\t\tphylink_disconnect_phy(priv-\u003ephylink);\n4510:\t\tpm_runtime_put(priv-\u003edevice);\n4511:\t\t/* Allow a fresh open after a failed MTU reopen or resume. */\n4512:\t\tnetif_device_attach(dev);\n4513:\t\n4514:\t\treturn 0;\n4515:\t}\n4516:\t\n4517:\tstatic bool stmmac_vlan_insert(struct stmmac_priv *priv, struct sk_buff *skb,\n4518:\t\t\t\t struct stmmac_tx_queue *tx_q)\n4519:\t{\n"
} |
| 90/3 | 2026/09/26 22:57 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 47869 output: 40 thoughts: 1602 Thoughts: **Analyzing the STM32 Ethernet Driver Patch** Okay, let's break this down. First, I need to understand the memory management around the `stmmac_dma_conf` structure. It looks like `__stmmac_release` frees the *descriptors* inside `priv->dma_conf`, but not the `priv->dma_conf` structure itself. Interesting. Then, during `stmmac_open`, a new `dma_conf` is allocated with `stmmac_setup_dma_desc`, and then the *old* `priv->dma_conf` is freed with `kfree(old_conf)`. If `stmmac_open` fails, the old `priv->dma_conf` is restored, and the new `dma_conf` is freed. And finally, when the device is removed, `stmmac_free_dma_conf` is called to free `priv->dma_conf`. Looks like a carefully crafted lifecycle to me. I see that `dma_conf` is zeroed by `kzalloc` in both `stmmac_setup_dma_desc` and within `__stmmac_dvr_probe`, meaning that all fields should be initialized, but what if they aren't initialized? Now I need to see if this patch causes any issues here. Since I don't have direct access to git log, I'll use the provided information. This is a patch under review from `syz-cluster`, so it's likely a bug fix or improvement identified by syzkaller. Now, let's understand the scope of the patch itself. The patch modifies `drivers/net/ethernet/stmicro/stmmac/stmmac_main.c`: * It changes `priv->dma_conf` from an inline struct to a heap pointer. This suggests a shift in how the structure is managed. * New fields `datapath` and `hw_suspended` are introduced, which indicates state management. * Functions related to RX buffer allocation and initialization (`stmmac_alloc_rx_buffer`, `stmmac_init_rx_buffer_desc`, `stmmac_prepare_rx_buffers`, `stmmac_reinit_dma_desc`) are touched, as well as MTU reconfiguration (`stmmac_reconfigure_mtu`). The MTU changes look specifically designed to reduce the need for down/up operations. * Changes to MSI IRQ handling and interrupt routines, probably related to improved interrupt handling. * Suspend/resume and datapath state tracking have been improved. * XDP pool handling, and phylink PM speed control restoration are also included. Now, the important part: KMSAN. I need to check for potential issues related to uninitialized memory reads or kernel-to-user information leaks. Specifically, I'm concerned about what happens to values in `priv->dma_conf` before they are initialized, if ever. Looking at `stmmac_ethtool.c`, I see a critical piece: `stmmac_get_ringparam`. It reads `priv->dma_conf->dma_rx_size` and `priv->dma_conf->dma_tx_size`. These values were previously part of the struct inline with `priv`, now they are pointers. Where are these initialized? They're initialized in `stmmac_setup_dma_desc` and in `__stmmac_dvr_probe`. In `__stmmac_dvr_probe`: `priv->dma_conf = kzalloc_obj(*priv->dma_conf)`. So the struct is zero-initialized *before* `stmmac_open` is called. So before `stmmac_open` gets called, the `dma_rx_size` and `dma_tx_size` will be zero. This looks safe now. It's properly initialized with `kzalloc`. The KMSAN check is passed! Now, time to analyze other changes and ensure they are all KMSAN compliant! |
| 91/3 | 2026/09/26 22:57 | tool | grepper |
0mArgs:{
"Expression": "dma_rx_size",
"PathPrefix": "drivers/net/ethernet/stmicro/stmmac/"
}Results: {
"Output": "drivers/net/ethernet/stmicro/stmmac/chain_mode.c=129=static void refill_desc3(struct stmmac_rx_queue *rx_q, struct dma_desc *p)\n--\ndrivers/net/ethernet/stmicro/stmmac/chain_mode.c-139-\t\t\t\t (((rx_q-\u003edirty_rx) + 1) %\ndrivers/net/ethernet/stmicro/stmmac/chain_mode.c:140:\t\t\t\t priv-\u003edma_conf-\u003edma_rx_size) *\ndrivers/net/ethernet/stmicro/stmmac/chain_mode.c-141-\t\t\t\t sizeof(struct dma_desc)));\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac.h=218=struct stmmac_dma_conf {\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac.h-222-\tstruct stmmac_rx_queue rx_queue[MTL_MAX_RX_QUEUES];\ndrivers/net/ethernet/stmicro/stmmac/stmmac.h:223:\tunsigned int dma_rx_size;\ndrivers/net/ethernet/stmicro/stmmac/stmmac.h-224-\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c=390=static void stmmac_get_ringparam(struct net_device *netdev,\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c-398-\tring-\u003etx_max_pending = DMA_MAX_TX_SIZE;\ndrivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c:399:\tring-\u003erx_pending = priv-\u003edma_conf-\u003edma_rx_size;\ndrivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c-400-\tring-\u003etx_pending = priv-\u003edma_conf-\u003edma_tx_size;\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c=451=static inline u32 stmmac_rx_dirty(struct stmmac_priv *priv, u32 queue)\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-455-\treturn CIRC_CNT(rx_q-\u003ecur_rx, rx_q-\u003edirty_rx,\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c:456:\t\t\tpriv-\u003edma_conf-\u003edma_rx_size);\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-457-}\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c=1476=static void stmmac_display_rx_rings(struct stmmac_priv *priv,\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-1493-\t\t/* Display RX ring */\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c:1494:\t\tstmmac_display_ring(priv, head_rx, dma_conf-\u003edma_rx_size, true,\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-1495-\t\t\t\t rx_q-\u003edma_rx_phy, desc_size);\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c=1565=static void stmmac_clear_rx_descriptors(struct stmmac_priv *priv,\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-1573-\t/* Clear the RX descriptors */\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c:1574:\tfor (i = 0; i \u003c dma_conf-\u003edma_rx_size; i++) {\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-1575-\t\tdesc = stmmac_get_rx_desc(priv, rx_q, i);\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-1578-\t\t\t\t priv-\u003edescriptor_mode,\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c:1579:\t\t\t\t (i == dma_conf-\u003edma_rx_size - 1),\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-1580-\t\t\t\t dma_conf-\u003edma_buf_sz);\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c=1773=static void dma_free_rx_skbufs(struct stmmac_priv *priv,\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-1782-\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c:1783:\tfor (i = 0; i \u003c dma_conf-\u003edma_rx_size; i++)\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-1784-\t\tstmmac_free_rx_buffer(priv, rx_q, i);\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c=1787=static int stmmac_alloc_rx_buffers(struct stmmac_priv *priv,\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-1793-\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c:1794:\tfor (i = 0; i \u003c dma_conf-\u003edma_rx_size; i++) {\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-1795-\t\tstruct dma_desc *p;\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c=1817=static void dma_free_rx_xskbufs(struct stmmac_priv *priv,\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-1823-\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c:1824:\tfor (i = 0; i \u003c dma_conf-\u003edma_rx_size; i++) {\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-1825-\t\tstruct stmmac_rx_buffer *buf = \u0026rx_q-\u003ebuf_pool[i];\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c=1835=static int stmmac_alloc_rx_buffers_zc(struct stmmac_priv *priv,\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-1847-\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c:1848:\tfor (i = 0; i \u003c dma_conf-\u003edma_rx_size; i++) {\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-1849-\t\tstruct stmmac_rx_buffer *buf;\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c=1887=static int __init_dma_rx_desc_rings(struct stmmac_priv *priv,\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-1942-\t\tstmmac_mode_init(priv, des, rx_q-\u003edma_rx_phy,\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c:1943:\t\t\t\t dma_conf-\u003edma_rx_size, priv-\u003eextend_desc);\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-1944-\t}\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c=2156=static int stmmac_prepare_rx_buffers(struct stmmac_priv *priv)\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-2164-\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c:2165:\t\tfor (i = 0; i \u003c dma_conf-\u003edma_rx_size; i++) {\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-2166-\t\t\tret = stmmac_alloc_rx_buffer(priv, rx_q, \u0026rx_q-\u003ebuf_pool[i]);\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c=2176=static void stmmac_reinit_dma_desc(struct stmmac_priv *priv)\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-2193-\t\trx_q-\u003erx_count_frames = 0;\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c:2194:\t\trx_q-\u003ebuf_alloc_num = dma_conf-\u003edma_rx_size;\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-2195-\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c:2196:\t\tfor (i = 0; i \u003c dma_conf-\u003edma_rx_size; i++)\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-2197-\t\t\tstmmac_init_rx_buffer_desc(priv, dma_conf,\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-2202-\t\t\tstmmac_mode_init(priv, stmmac_get_rx_desc(priv, rx_q, 0),\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c:2203:\t\t\t\t\t rx_q-\u003edma_rx_phy, dma_conf-\u003edma_rx_size,\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-2204-\t\t\t\t\t priv-\u003eextend_desc);\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c=2214=static void __free_dma_rx_desc_resources(struct stmmac_priv *priv,\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-2242-\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c:2243:\tsize = stmmac_get_rx_desc_size(priv) * dma_conf-\u003edma_rx_size;\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-2244-\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c=2332=static int __alloc_dma_rx_desc_resources(struct stmmac_priv *priv,\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-2354-\tpp_params.flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV;\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c:2355:\tpp_params.pool_size = dma_conf-\u003edma_rx_size;\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-2356-\tpp_params.order = order_base_2(num_pages);\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-2374-\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c:2375:\trx_q-\u003ebuf_pool = kzalloc_objs(*rx_q-\u003ebuf_pool, dma_conf-\u003edma_rx_size);\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-2376-\tif (!rx_q-\u003ebuf_pool)\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-2378-\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c:2379:\tsize = stmmac_get_rx_desc_size(priv) * dma_conf-\u003edma_rx_size;\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-2380-\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c=3508=static void stmmac_set_rings_length(struct stmmac_priv *priv)\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-3521-\t\tstmmac_set_rx_ring_len(priv, priv-\u003eioaddr,\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c:3522:\t\t\t\t (priv-\u003edma_conf-\u003edma_rx_size - 1), chan);\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-3523-}\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c=4195=stmmac_setup_dma_desc(struct stmmac_priv *priv, unsigned int mtu)\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-4222-\tdma_conf-\u003edma_tx_size = priv-\u003edma_conf-\u003edma_tx_size;\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c:4223:\tdma_conf-\u003edma_rx_size = priv-\u003edma_conf-\u003edma_rx_size;\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-4224-\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-4226-\t\tdma_conf-\u003edma_tx_size = DMA_DEFAULT_TX_SIZE;\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c:4227:\tif (!dma_conf-\u003edma_rx_size)\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c:4228:\t\tdma_conf-\u003edma_rx_size = DMA_DEFAULT_RX_SIZE;\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-4229-\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c=5291=static inline void stmmac_rx_refill(struct stmmac_priv *priv, u32 queue)\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-5343-\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c:5344:\t\tentry = STMMAC_NEXT_ENTRY(entry, priv-\u003edma_conf-\u003edma_rx_size);\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-5345-\t}\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c=5674=static bool stmmac_rx_refill_zc(struct stmmac_priv *priv, u32 queue, u32 budget)\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-5715-\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c:5716:\t\tentry = STMMAC_NEXT_ENTRY(entry, priv-\u003edma_conf-\u003edma_rx_size);\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-5717-\t}\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c=5737=static int stmmac_rx_zc(struct stmmac_priv *priv, int limit, u32 queue)\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-5756-\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c:5757:\t\tstmmac_display_ring(priv, rx_head, priv-\u003edma_conf-\u003edma_rx_size, true,\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-5758-\t\t\t\t rx_q-\u003edma_rx_phy, desc_size);\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-5800-\t\tnext_entry = STMMAC_NEXT_ENTRY(rx_q-\u003ecur_rx,\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c:5801:\t\t\t\t\t priv-\u003edma_conf-\u003edma_rx_size);\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-5802-\t\tif (unlikely(next_entry == rx_q-\u003edirty_rx))\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c=5922=static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue)\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-5946-\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c:5947:\t\tstmmac_display_ring(priv, rx_head, priv-\u003edma_conf-\u003edma_rx_size, true,\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-5948-\t\t\t\t rx_q-\u003edma_rx_phy, desc_size);\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-5986-\t\tnext_entry = STMMAC_NEXT_ENTRY(rx_q-\u003ecur_rx,\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c:5987:\t\t\t\t\t priv-\u003edma_conf-\u003edma_rx_size);\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-5988-\t\tif (unlikely(next_entry == rx_q-\u003edirty_rx))\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c=6839=static int stmmac_rings_status_show(struct seq_file *seq, void *v)\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-6859-\t\t\tsysfs_display_ring((void *)rx_q-\u003edma_erx,\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c:6860:\t\t\t\t\t priv-\u003edma_conf-\u003edma_rx_size, 1, seq, rx_q-\u003edma_rx_phy);\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-6861-\t\t} else {\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-6863-\t\t\tsysfs_display_ring((void *)rx_q-\u003edma_rx,\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c:6864:\t\t\t\t\t priv-\u003edma_conf-\u003edma_rx_size, 0, seq, rx_q-\u003edma_rx_phy);\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-6865-\t\t}\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c=7917=int stmmac_reinit_ringparam(struct net_device *dev, u32 rx_size, u32 tx_size)\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-7919-\tstruct stmmac_priv *priv = netdev_priv(dev);\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c:7920:\tu32 old_rx = priv-\u003edma_conf-\u003edma_rx_size;\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-7921-\tu32 old_tx = priv-\u003edma_conf-\u003edma_tx_size;\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-7930-\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c:7931:\tpriv-\u003edma_conf-\u003edma_rx_size = rx_size;\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-7932-\tpriv-\u003edma_conf-\u003edma_tx_size = tx_size;\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-7936-\tif (ret) {\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c:7937:\t\tpriv-\u003edma_conf-\u003edma_rx_size = old_rx;\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-7938-\t\tpriv-\u003edma_conf-\u003edma_tx_size = old_tx;\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c=817=static int stmmac_test_flowctrl(struct stmmac_priv *priv)\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c-890-\t\ttail = priv-\u003edma_conf-\u003erx_queue[i].dma_rx_phy +\ndrivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c:891:\t\t\t(priv-\u003edma_conf-\u003edma_rx_size * sizeof(struct dma_desc));\ndrivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c-892-\n"
} |
| 92/3 | 2026/09/26 22:57 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 56291 output: 50 thoughts: 466 Thoughts: **Analyzing DMA Configuration Initialization and Potential Race Conditions** Okay, let me break this down. In the probe function, `priv->dma_conf` is initialized using `kzalloc_obj`, which means all its members start at zero. Now, when `stmmac_open` or, equivalently, `stmmac_setup_dma_desc`, is called, it copies values like `dma_tx_size` and `dma_rx_size` from `priv->dma_conf` to a local `dma_conf` struct. Critically, there's a check: if `dma_tx_size` or `dma_rx_size` are zero, they're assigned default values ( `DMA_DEFAULT_TX_SIZE` and `DMA_DEFAULT_RX_SIZE`). Here's the tricky part, and it is a point to examine closely. What happens if someone calls ethtool's `stmmac_get_ringparam` before the interface is actually opened? If the interface isn't opened, `priv->dma_conf->dma_rx_size` will still be zero. Before the patch, what was its value? I need to check back and think this through. Aha! Before the patch, `priv->dma_conf` was embedded inside `priv`, which was allocated with `alloc_etherdev_mqs`. That would have zero-initialized the whole thing! So, `priv->dma_conf.dma_rx_size` *was* zero before the open operation, too. Now, since `priv->dma_conf` is allocated with `kzalloc_obj` in `__stmmac_dvr_probe`, it's still zero-initialized in the new situation too. Now I need to shift my attention to `stmmac_alloc_rx_buffer` and `stmmac_init_rx_buffer_desc`. Let's look at `stmmac_main.c` around line 1630. This should provide some insight into how those zero values might be utilized or handled. |
| 93/3 | 2026/09/26 22:57 | tool | read-file |
0mArgs:{
"File": "drivers/net/ethernet/stmicro/stmmac/stmmac_main.c",
"FirstLine": 1630,
"LineCount": 80
}Results: {
"Contents": "1630:\t}\n1631:\t\n1632:\tstatic int stmmac_alloc_rx_buffer(struct stmmac_priv *priv,\n1633:\t\t\t\t\t struct stmmac_rx_queue *rx_q,\n1634:\t\t\t\t\t struct stmmac_rx_buffer *buf)\n1635:\t{\n1636:\t\tgfp_t gfp = (GFP_ATOMIC | __GFP_NOWARN);\n1637:\t\n1638:\t\tif (priv-\u003edma_cap.host_dma_width \u003c= 32)\n1639:\t\t\tgfp |= GFP_DMA32;\n1640:\t\n1641:\t\tif (!buf-\u003epage) {\n1642:\t\t\tbuf-\u003epage = page_pool_alloc_pages(rx_q-\u003epage_pool, gfp);\n1643:\t\t\tif (!buf-\u003epage)\n1644:\t\t\t\treturn -ENOMEM;\n1645:\t\t\tbuf-\u003epage_offset = stmmac_rx_offset(priv);\n1646:\t\t}\n1647:\t\n1648:\t\tif (priv-\u003esph_active \u0026\u0026 !buf-\u003esec_page) {\n1649:\t\t\tbuf-\u003esec_page = page_pool_alloc_pages(rx_q-\u003epage_pool, gfp);\n1650:\t\t\tif (!buf-\u003esec_page)\n1651:\t\t\t\treturn -ENOMEM;\n1652:\t\t\tbuf-\u003esec_addr = page_pool_get_dma_addr(buf-\u003esec_page);\n1653:\t\t}\n1654:\t\n1655:\t\treturn 0;\n1656:\t}\n1657:\t\n1658:\tstatic void stmmac_init_rx_buffer_desc(struct stmmac_priv *priv,\n1659:\t\t\t\t\t struct stmmac_dma_conf *dma_conf,\n1660:\t\t\t\t\t struct dma_desc *p,\n1661:\t\t\t\t\t struct stmmac_rx_buffer *buf)\n1662:\t{\n1663:\t\tif (buf-\u003esec_page)\n1664:\t\t\tbuf-\u003esec_addr = page_pool_get_dma_addr(buf-\u003esec_page);\n1665:\t\tstmmac_set_desc_sec_addr(priv, p, buf-\u003esec_addr, !!buf-\u003esec_page);\n1666:\t\tbuf-\u003eaddr = page_pool_get_dma_addr(buf-\u003epage) + buf-\u003epage_offset;\n1667:\t\n1668:\t\tstmmac_set_desc_addr(priv, p, buf-\u003eaddr);\n1669:\t\tif (dma_conf-\u003edma_buf_sz == BUF_SIZE_16KiB)\n1670:\t\t\tstmmac_init_desc3(priv, p);\n1671:\t}\n1672:\t\n1673:\t/**\n1674:\t * stmmac_init_rx_buffers - allocate a receive buffer and init its descriptor\n1675:\t * @priv: driver private structure\n1676:\t * @dma_conf: structure to take the dma data\n1677:\t * @p: descriptor pointer\n1678:\t * @i: descriptor index\n1679:\t * @flags: gfp flag\n1680:\t * @queue: RX queue index\n1681:\t */\n1682:\tstatic int stmmac_init_rx_buffers(struct stmmac_priv *priv,\n1683:\t\t\t\t\t struct stmmac_dma_conf *dma_conf,\n1684:\t\t\t\t\t struct dma_desc *p,\n1685:\t\t\t\t\t int i, gfp_t flags, u32 queue)\n1686:\t{\n1687:\t\tstruct stmmac_rx_queue *rx_q = \u0026dma_conf-\u003erx_queue[queue];\n1688:\t\tstruct stmmac_rx_buffer *buf = \u0026rx_q-\u003ebuf_pool[i];\n1689:\t\tint ret;\n1690:\t\n1691:\t\tret = stmmac_alloc_rx_buffer(priv, rx_q, buf);\n1692:\t\tif (ret)\n1693:\t\t\treturn ret;\n1694:\t\tstmmac_init_rx_buffer_desc(priv, dma_conf, p, buf);\n1695:\t\n1696:\t\treturn 0;\n1697:\t}\n1698:\t\n1699:\t/**\n1700:\t * stmmac_free_rx_buffer - free RX dma buffers\n1701:\t * @priv: private structure\n1702:\t * @rx_q: RX queue\n1703:\t * @i: buffer index.\n1704:\t */\n1705:\tstatic void stmmac_free_rx_buffer(struct stmmac_priv *priv,\n1706:\t\t\t\t\t struct stmmac_rx_queue *rx_q,\n1707:\t\t\t\t\t int i)\n1708:\t{\n1709:\t\tstruct stmmac_rx_buffer *buf = \u0026rx_q-\u003ebuf_pool[i];\n"
} |
| 94/3 | 2026/09/26 22:57 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 58617 output: 45 thoughts: 1057 Thoughts: **Analyzing Potential Resource Leaks and Initialization Issues in STMMAC Driver** Okay, let's break this down. I'm looking at the `stmmac_alloc_rx_buffer` function and immediately a few things stand out. Specifically, the interaction between buffer allocation and the `priv->sph_active` flag. If `priv->sph_active` is true, a secondary page, `buf->sec_page`, is allocated. However, what happens if the allocation fails *after* a primary page (`buf->page`) has been successfully allocated in the same function call? We get an `-ENOMEM` error, but is everything cleaned up? Looking at `stmmac_free_rx_buffer` confirms my suspicions. The function checks and deallocates both `buf->page` *and* `buf->sec_page`, which is good. But consider the scenario I just described: a partial allocation. If `buf->sec_page` allocation failed earlier, `stmmac_free_rx_buffer` handles that too. This seems okay, although the error path needs to be validated. Now, let's look at `stmmac_init_rx_buffer_desc`. This function is responsible for initializing the descriptor, and that's where things get interesting in light of the `sph_active` conditional. I noticed the recent change, and immediately thought to myself "wait, what was the *before* state?" Thankfully, I've got the diff! The older code explicitly set `buf->sec_page = NULL` when `!priv->sph_active`. I remember `rx_q->buf_pool` is allocated with `kzalloc_objs`, which means `buf->sec_addr` is initialized to zero by default. The critical change then is how `sec_addr` is set now. The new code snippet: ```c if (buf->sec_page) buf->sec_addr = page_pool_get_dma_addr(buf->sec_page); stmmac_set_desc_sec_addr(priv, p, buf->sec_addr, !!buf->sec_page); ``` If `buf->sec_page` is `NULL`, which is the case when we're not using the SPH feature or on an earlier failed allocation, `buf->sec_addr` remains zero. This is expected. But, this `!!buf->sec_page` check is important. If the flag is false, then I need to confirm what `stmmac_set_desc_sec_addr` actually does with that data. It's likely zeroing out that descriptor field to prevent memory leaks, but I'll have to check to be sure. I'm going to quickly look at `stmmac_set_desc_sec_addr` to make sure it handles the `sec_addr` zero case properly. I have to confirm that a zero `sec_addr` correctly indicates that the secondary buffer isn't active, and that no stale DMA mappings are left lingering around. |
| 95/3 | 2026/09/26 22:57 | tool | grepper |
0mArgs:{
"Expression": "stmmac_set_desc_sec_addr",
"PathPrefix": "drivers/net/ethernet/stmicro/stmmac/"
}Results: {
"Output": "drivers/net/ethernet/stmicro/stmmac/hwif.h=39=struct stmmac_desc_ops {\n--\ndrivers/net/ethernet/stmicro/stmmac/hwif.h-151-\tstmmac_do_void_callback(__priv, desc, get_rx_header_len, __args)\ndrivers/net/ethernet/stmicro/stmmac/hwif.h:152:#define stmmac_set_desc_sec_addr(__priv, __args...) \\\ndrivers/net/ethernet/stmicro/stmmac/hwif.h-153-\tstmmac_do_void_callback(__priv, desc, set_sec_addr, __args)\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c=1658=static void stmmac_init_rx_buffer_desc(struct stmmac_priv *priv,\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-1664-\t\tbuf-\u003esec_addr = page_pool_get_dma_addr(buf-\u003esec_page);\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c:1665:\tstmmac_set_desc_sec_addr(priv, p, buf-\u003esec_addr, !!buf-\u003esec_page);\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-1666-\tbuf-\u003eaddr = page_pool_get_dma_addr(buf-\u003epage) + buf-\u003epage_offset;\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c=5291=static inline void stmmac_rx_refill(struct stmmac_priv *priv, u32 queue)\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-5325-\t\tif (priv-\u003esph_active)\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c:5326:\t\t\tstmmac_set_desc_sec_addr(priv, p, buf-\u003esec_addr, true);\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-5327-\t\telse\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c:5328:\t\t\tstmmac_set_desc_sec_addr(priv, p, buf-\u003esec_addr, false);\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-5329-\t\tstmmac_refill_desc3(priv, rx_q, p);\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c=5674=static bool stmmac_rx_refill_zc(struct stmmac_priv *priv, u32 queue, u32 budget)\n--\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-5699-\t\tstmmac_set_desc_addr(priv, rx_desc, dma_addr);\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c:5700:\t\tstmmac_set_desc_sec_addr(priv, rx_desc, 0, false);\ndrivers/net/ethernet/stmicro/stmmac/stmmac_main.c-5701-\t\tstmmac_refill_desc3(priv, rx_q, rx_desc);\n"
} |
| 96/3 | 2026/09/26 22:57 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 61282 output: 355 thoughts: 5279 Thoughts: **KMSAN Assessment of STMMAC Driver Refactoring and MTU/Queue Reconfiguration** Alright, let's break down this code. My job here is to determine if these changes warrant the use of KMSAN. My analysis is going to be from the perspective of potential memory safety issues, with a focus on uninitialized memory reads or leaks. First, I examined the `stmmac_reconfigure_mtu` function and the related `stmmac_prepare_rx_buffers` and `stmmac_reinit_dma_desc` functions. The core logic involves changing the MTU, reallocating DMA descriptors, and reinitializing the RX buffers. It appears that the new MTU is correctly set and the old configuration is correctly restored in case of any failures. The `stmmac_prepare_rx_buffers` function appears to allocate the RX buffer pages before any changes to the descriptors are done in `stmmac_hw_setup`. I checked initialization and how `priv->dma_conf` is updated. Critical variables such as `new_conf`, `old_conf`, `ret`, `old_mtu`, and `restore_ret` are all initialized before their use. The `stmmac_reinit_dma_desc` function is reinitializing all the descriptors, and `rx_q->buf_pool[i]` is correctly initialized in prior steps. Next, I looked at `stmmac_rings_status_show`. It involves debug output, but crucially, all variables used in this function are initialized before they are used. The code appears to be well-protected from data races with RTNL locking and a check for `priv->datapath` state, ensuring it won't access stale data. I then investigated `phylink.c`, specifically the new `phylink_restore_suspend_speed` function. I tracked the `suspend_speed_down` flag, ensuring it is properly initialized to `false` in `phylink_create` and correctly updated/reset in `phylink_suspend` and `phylink_restore_suspend_speed` functions, avoiding uninitialized access. I carefully examined `stmmac_set_irq_state`, `stmmac_resume_hw`, `stmmac_reopen`, `stmmac_reinit_queues`, and `stmmac_reinit_ringparam`. These functions are involved in the driver's state transition and resource management. All variables and paths are considered and initialized correctly. The code utilizes `kzalloc_obj` to allocate and initialize complex structs. Finally, I considered the KMSAN criteria. This patch doesn't introduce data being sent to user space that could be uninitialized. There are no conditional branches on uninitialized variables. Complex data structures, such as `stmmac_dma_conf`, are correctly initialized by `kzalloc_obj`. The bounds checking and lengths of buffers are not modified to potentially access uninitialized data. This analysis shows that the primary risks introduced by these changes are memory management bugs, synchronization issues, device state bugs, or concurrency bugs, all of which are caught by KASAN, LOCKDEP, or standard kernel debugging tools. The changes are primarily refactorings and bug fixes in the STMMAC network driver lifecycle, suspend/resume, XDP reconfig, and MTU change paths, and phylink speed restore. All new and existing dynamically allocated objects are initialized with `kzalloc_obj`, zeroing out the memory. There are no uninitialized memory reads or infoleaks. Thus, the condition for `NeedsKMSAN=false` is met. |
| 97/3 | 2026/09/26 22:57 | tool | set-results |
0mArgs:{
"KMSANReasoning": "The patch refactors datapath lifecycle management, suspend/resume handling, MTU reconfiguration, and phylink PM speed control in the stmmac network driver and phylink subsystem. Specifically:\n1. `priv-\u003edma_conf` is converted from an embedded struct to a dynamically allocated pointer allocated via `kzalloc_obj()`, ensuring all fields are zero-initialized.\n2. Newly added struct members (`datapath`, `hw_suspended` in `stmmac_priv`, and `suspend_speed_down` in `phylink`) are members of zero-initialized structs allocated via `alloc_etherdev_mqs` and `kzalloc`.\n3. All local variables introduced in helper functions (e.g., `stmmac_reconfigure_mtu`, `stmmac_resume_hw`, `stmmac_set_irq_state`, `stmmac_reopen`) are explicitly initialized before use.\n4. No uninitialized memory, padding, or uninitialized kernel buffers are copied to user space or used in conditional logic.\n5. The potential bug risks introduced by this patch involve pointer lifecycle/lifetime management (e.g., UAF or double-free of DMA configurations during error unwinding or reconfiguration), NULL pointer dereferences, locking/NAPI concurrency, or DMA descriptor state handling. These bug classes are effectively caught by standard debugging tools (KASAN, LOCKDEP, and page allocation tracking).\n\nBecause no uninitialized memory usage or info-leak risks are introduced, a dedicated KMSAN fuzzing session is not warranted.",
"NeedsKMSAN": false
}Results: {
"KMSANReasoning": "The patch refactors datapath lifecycle management, suspend/resume handling, MTU reconfiguration, and phylink PM speed control in the stmmac network driver and phylink subsystem. Specifically:\n1. `priv-\u003edma_conf` is converted from an embedded struct to a dynamically allocated pointer allocated via `kzalloc_obj()`, ensuring all fields are zero-initialized.\n2. Newly added struct members (`datapath`, `hw_suspended` in `stmmac_priv`, and `suspend_speed_down` in `phylink`) are members of zero-initialized structs allocated via `alloc_etherdev_mqs` and `kzalloc`.\n3. All local variables introduced in helper functions (e.g., `stmmac_reconfigure_mtu`, `stmmac_resume_hw`, `stmmac_set_irq_state`, `stmmac_reopen`) are explicitly initialized before use.\n4. No uninitialized memory, padding, or uninitialized kernel buffers are copied to user space or used in conditional logic.\n5. The potential bug risks introduced by this patch involve pointer lifecycle/lifetime management (e.g., UAF or double-free of DMA configurations during error unwinding or reconfiguration), NULL pointer dereferences, locking/NAPI concurrency, or DMA descriptor state handling. These bug classes are effectively caught by standard debugging tools (KASAN, LOCKDEP, and page allocation tracking).\n\nBecause no uninitialized memory usage or info-leak risks are introduced, a dedicated KMSAN fuzzing session is not warranted.",
"NeedsKMSAN": false
} |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|