Skip to contentSkip to conversation

7.2.7-rc1 review

PATCH Applied Greg Kroah-Hartman 15 participants 748 messages Sep 17 – Sep 19 Page 4 of 6

Subsystems (5)
lore
Share & tools
lore
author maintainer
#361 lore raw

PATCH 361/733 net: macb: reject an unknown link speed in the taprio setup

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Aleksei Sviridkin <[email protected]>

[ Upstream commit 2b6c0e25a3d713c4032e45f212bdd9e14c50f8a0 ]

speed is a u32, so SPEED_UNKNOWN arrives as 0xffffffff and passes the "speed <= 0" check, which only ever catches zero. That is what an autonegotiating link reports while it is down: the limit derived from the speed collapses to a nanosecond at most and the first entry fails with a misleading "exceeds hardware limit". Zero stays covered, it is what an interface that was never opened reports, and enst_max_hw_interval() divides by it. Say which case it was in the error.

Fixes 89934dbf169e ("net: macb: Add TAPRIO traffic scheduling support")
Assisted-by LLM
---
 drivers/net/ethernet/cadence/macb_main.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/net/ethernet/cadence/macb_main.c +2 -2
index 09fd83782ae3f..55ed40a0a6062 100644
@@ -4360,8 +4360,8 @@ static int macb_taprio_setup_replace(struct net_device *netdev,
 	}
 
 	speed = kset.base.speed;
-	if (unlikely(speed <= 0)) {
-		netdev_err(netdev, "Invalid speed: %d\n", speed);
+	if (unlikely(speed == SPEED_UNKNOWN || !speed)) {
+		netdev_err(netdev, "Invalid speed %d, link-down?\n", speed);
 		return -EINVAL;
 	}
 
Signature · 1 line
2.53.0
author maintainer
#362 lore raw

PATCH 362/733 net: ethernet: cortina: Fix budget accounting

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Linus Walleij <[email protected]>

[ Upstream commit a0de06d0da78a3db53de65dfd7452cc6d111f703 ]

The gmac_rx() function returns the remaining NAPI budget, but its caller treats the return value as the number of packets received. An idle poll therefore reports a full budget and remains scheduled.

Return the number of received packets instead. Preserve the existing free queue refill accounting by adding that count directly; continuing to subtract it from the budget would invert the refill behavior.

---
 drivers/net/ethernet/cortina/gemini.c | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)
drivers/net/ethernet/cortina/gemini.c +5 -3
index 4c762229ce420..1d9824d1716cf 100644
@@ -1450,6 +1450,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget)
 	unsigned int frame_len, frag_len;
 	struct gmac_rxdesc *rx = NULL;
 	struct gmac_queue_page *gpage;
+	unsigned int received = 0;
 	union gmac_rxdesc_0 word0;
 	union gmac_rxdesc_1 word1;
 	union gmac_rxdesc_3 word3;
@@ -1545,7 +1546,8 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget)
 			napi_gro_frags(&port->napi);
 			skb = NULL;
 			frag_nr = 0;
-			--budget;
+			budget--;
+			received++;
 		}
 		continue;
 
@@ -1565,7 +1567,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget)
 	port->rx_skb = skb;
 	port->rx_frag_nr = frag_nr;
 	writew(r, ptr_reg);
-	return budget;
+	return received;
 }
 
 static int gmac_napi_poll(struct napi_struct *napi, int budget)
@@ -1586,7 +1588,7 @@ static int gmac_napi_poll(struct napi_struct *napi, int budget)
 		++port->rx_napi_exits;
 	}
 
-	port->freeq_refill += (budget - received);
+	port->freeq_refill += received;
 	if (port->freeq_refill > freeq_threshold) {
 		port->freeq_refill -= freeq_threshold;
 		geth_fill_freeq(geth, true);
Signature · 1 line
2.53.0
author maintainer
#363 lore raw

PATCH 363/733 net: ethernet: cortina: Finish RX updates before NAPI completion

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Linus Walleij <[email protected]>

[ Upstream commit baa26841cb9a2cdc7e0e99d6854a4e3359bf7393 ]

napi_complete_done() releases ownership of the NAPI instance, but the Gemini poll keeps the RX statistics writer section open and updates the free queue after calling it. A new poll can therefore start while the old writer is still active.

Finish the statistics and free queue updates before releasing ownership. Only re-enable RX interrupts when napi_complete_done() reports successful completion.

Fixes 4d5ae32f5e1e ("net: ethernet: Add a driver for Gemini gigabit ethernet")
Assisted-by LLM
---
 drivers/net/ethernet/cortina/gemini.c | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)
drivers/net/ethernet/cortina/gemini.c +6 -6
index 1d9824d1716cf..6502220362cbd 100644
@@ -1581,12 +1581,10 @@ static int gmac_napi_poll(struct napi_struct *napi, int budget)
 	u64_stats_update_begin(&port->rx_stats_syncp);
 
 	received = gmac_rx(napi->dev, budget);
-	if (received < budget) {
-		napi_gro_flush(napi, false);
-		napi_complete_done(napi, received);
-		gmac_enable_rx_irq(napi->dev, 1);
+	if (received < budget)
 		++port->rx_napi_exits;
-	}
+
+	u64_stats_update_end(&port->rx_stats_syncp);
 
 	port->freeq_refill += received;
 	if (port->freeq_refill > freeq_threshold) {
@@ -1594,7 +1592,9 @@ static int gmac_napi_poll(struct napi_struct *napi, int budget)
 		geth_fill_freeq(geth, true);
 	}
 
-	u64_stats_update_end(&port->rx_stats_syncp);
+	if (received < budget && napi_complete_done(napi, received))
+		gmac_enable_rx_irq(napi->dev, 1);
+
 	return received;
 }
 
Signature · 1 line
2.53.0
author maintainer
#364 lore raw

PATCH 364/733 net: ethernet: cortina: Count dropped frames as NAPI work

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Linus Walleij <[email protected]>

[ Upstream commit b856c552f556bc0341c1dbe0bf88e630fd1dc4b7 ]

The RX loop only consumes budget when it successfully delivers a frame. Error paths keep consuming descriptors without reducing the budget, so a stream of bad frames can process the entire receive ring in one poll.

Move the budget accounting to a common end-of-frame path. This counts each completed frame as NAPI work whether it was delivered or dropped, matching the behavior of the vendor driver.

Fixes 4d5ae32f5e1e ("net: ethernet: Add a driver for Gemini gigabit ethernet")
Assisted-by LLM
---
 drivers/net/ethernet/cortina/gemini.c | 15 ++++++++++-----
 1 file changed, 10 insertions(+), 5 deletions(-)
drivers/net/ethernet/cortina/gemini.c +10 -5
index 6502220362cbd..33e9763b32fe8 100644
@@ -1501,7 +1501,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget)
 				skb = NULL;
 				frag_nr = 0;
 			}
-			continue;
+			goto next_desc;
 		}
 		page = gpage->page;
 
@@ -1523,7 +1523,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget)
 
 		} else if (!skb) {
 			put_page(page);
-			continue;
+			goto next_desc;
 		}
 
 		if (word3.bits32 & EOF_BIT)
@@ -1546,10 +1546,8 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget)
 			napi_gro_frags(&port->napi);
 			skb = NULL;
 			frag_nr = 0;
-			budget--;
-			received++;
 		}
-		continue;
+		goto next_desc;
 
 err_drop:
 		if (skb) {
@@ -1562,6 +1560,13 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget)
 			put_page(page);
 
 		port->stats.rx_dropped++;
+
+next_desc:
+		/* Final or single-descriptor fragment, advance things */
+		if (word3.bits32 & EOF_BIT) {
+			budget--;
+			received++;
+		}
 	}
 
 	port->rx_skb = skb;
Signature · 1 line
2.53.0
author maintainer
#365 lore raw

PATCH 365/733 net: ethernet: cortina: Count RX drops once per frame

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Linus Walleij <[email protected]>

[ Upstream commit 6520198c430c81bcc367f0dd5e32f2fb740b9d51 ]

The absence of a partial skb means either that the driver is not assembling a frame or that the current frame was already dropped. Consequently, repeated descriptor errors can increment rx_dropped more than once, while an orphaned descriptor chain can reach EOF without being counted at all.

Track the dropping state across NAPI polls. Clear it at frame boundaries and route mapping failures and orphaned continuations through the common drop path so each discarded frame is counted exactly once.

Fixes 4d5ae32f5e1e ("net: ethernet: Add a driver for Gemini gigabit ethernet")
Assisted-by LLM
---
 drivers/net/ethernet/cortina/gemini.c | 41 +++++++++++++++------------
 1 file changed, 23 insertions(+), 18 deletions(-)
drivers/net/ethernet/cortina/gemini.c +23 -18
index 33e9763b32fe8..9ba8524fa3710 100644
@@ -124,6 +124,7 @@ struct gemini_ethernet_port {
 	unsigned int		rx_coalesce_nsecs;
 	struct sk_buff		*rx_skb;
 	unsigned int		rx_frag_nr;
+	bool			rx_dropping;
 
 	unsigned int		freeq_refill;
 	struct gmac_txq		txq[TX_QUEUE_NUM];
@@ -1451,6 +1452,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget)
 	struct gmac_rxdesc *rx = NULL;
 	struct gmac_queue_page *gpage;
 	unsigned int received = 0;
+	bool dropping = port->rx_dropping;
 	union gmac_rxdesc_0 word0;
 	union gmac_rxdesc_1 word1;
 	union gmac_rxdesc_3 word3;
@@ -1472,6 +1474,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget)
 	w = rw.bits.wptr;
 
 	while (budget && w != r) {
+		page = NULL;
 		rx = port->rxq_ring + r;
 		word0 = rx->word0;
 		word1 = rx->word1;
@@ -1485,6 +1488,16 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget)
 		frame_len = word1.bits.byte_count;
 		page_offs = mapping & ~PAGE_MASK;
 
+		if (word3.bits32 & SOF_BIT) {
+			if (skb) {
+				napi_free_frags(&port->napi);
+				port->stats.rx_dropped++;
+				skb = NULL;
+				frag_nr = 0;
+			}
+			dropping = false;
+		}
+
 		if (!mapping) {
 			netdev_err(netdev,
 				   "rxq[%u]: HW BUG: zero DMA desc\n", r);
@@ -1495,24 +1508,11 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget)
 		gpage = gmac_get_queue_page(geth, port, mapping + PAGE_SIZE);
 		if (!gpage) {
 			dev_err(geth->dev, "could not find mapping\n");
-			port->stats.rx_dropped++;
-			if (skb) {
-				napi_free_frags(&port->napi);
-				skb = NULL;
-				frag_nr = 0;
-			}
-			goto next_desc;
+			goto err_drop;
 		}
 		page = gpage->page;
 
 		if (word3.bits32 & SOF_BIT) {
-			if (skb) {
-				napi_free_frags(&port->napi);
-				port->stats.rx_dropped++;
-				skb = NULL;
-				frag_nr = 0;
-			}
-
 			skb = gmac_skb_if_good_frame(port, word0, frame_len);
 			if (!skb)
 				goto err_drop;
@@ -1522,8 +1522,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget)
 			frag_nr = 0;
 
 		} else if (!skb) {
-			put_page(page);
-			goto next_desc;
+			goto err_drop;
 		}
 
 		if (word3.bits32 & EOF_BIT)
@@ -1556,21 +1555,26 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget)
 			frag_nr = 0;
 		}
 
-		if (mapping)
+		if (page)
 			put_page(page);
 
-		port->stats.rx_dropped++;
+		if (!dropping) {
+			port->stats.rx_dropped++;
+			dropping = true;
+		}
 
 next_desc:
 		/* Final or single-descriptor fragment, advance things */
 		if (word3.bits32 & EOF_BIT) {
 			budget--;
 			received++;
+			dropping = false;
 		}
 	}
 
 	port->rx_skb = skb;
 	port->rx_frag_nr = frag_nr;
+	port->rx_dropping = dropping;
 	writew(r, ptr_reg);
 	return received;
 }
@@ -1900,6 +1904,7 @@ static int gmac_stop(struct net_device *netdev)
 	napi_disable(&port->napi);
 	port->rx_skb = NULL;
 	port->rx_frag_nr = 0;
+	port->rx_dropping = false;
 
 	gmac_enable_irq(netdev, 0);
 	gmac_cleanup_rxq(netdev);
Signature · 1 line
2.53.0
author maintainer
#366 lore raw

PATCH 366/733 net: ethernet: cortina: Count RX descriptors for freeq refill

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Linus Walleij <[email protected]>

[ Upstream commit e89e88ad41d9f31c829c2af39c48313e8e48d5b0 ]

The software free queue provides one buffer fragment for every descriptor moved to an RX queue. The refill heuristic instead advances by NAPI work, which counts frames. A fragmented or discarded frame can consume several queue entries while adding only one to the refill count.

Count the RX descriptors as they are consumed and report that separately from NAPI work. Use the descriptor count to drive free queue refills.

Fixes 4d5ae32f5e1e ("net: ethernet: Add a driver for Gemini gigabit ethernet")
Assisted-by LLM
---
 drivers/net/ethernet/cortina/gemini.c | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)
drivers/net/ethernet/cortina/gemini.c +8 -3
index 9ba8524fa3710..f08de623e6f7c 100644
@@ -1440,7 +1440,8 @@ static struct sk_buff *gmac_skb_if_good_frame(struct gemini_ethernet_port *port,
 	return skb;
 }
 
-static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget)
+static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget,
+			    unsigned int *freeq_consumed)
 {
 	struct gemini_ethernet_port *port = netdev_priv(netdev);
 	unsigned short m = (1 << port->rxq_order) - 1;
@@ -1448,6 +1449,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget)
 	void __iomem *ptr_reg = port->rxq_rwptr;
 	unsigned int frag_nr = port->rx_frag_nr;
 	struct sk_buff *skb = port->rx_skb;
+	unsigned int consumed = 0;
 	unsigned int frame_len, frag_len;
 	struct gmac_rxdesc *rx = NULL;
 	struct gmac_queue_page *gpage;
@@ -1483,6 +1485,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget)
 
 		r++;
 		r &= m;
+		consumed++;
 
 		frag_len = word0.bits.buffer_size;
 		frame_len = word1.bits.byte_count;
@@ -1575,6 +1578,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget)
 	port->rx_skb = skb;
 	port->rx_frag_nr = frag_nr;
 	port->rx_dropping = dropping;
+	*freeq_consumed = consumed;
 	writew(r, ptr_reg);
 	return received;
 }
@@ -1584,18 +1588,19 @@ static int gmac_napi_poll(struct napi_struct *napi, int budget)
 	struct gemini_ethernet_port *port = netdev_priv(napi->dev);
 	struct gemini_ethernet *geth = port->geth;
 	unsigned int freeq_threshold;
+	unsigned int freeq_consumed;
 	unsigned int received;
 
 	freeq_threshold = 1 << (geth->freeq_order - 1);
 	u64_stats_update_begin(&port->rx_stats_syncp);
 
-	received = gmac_rx(napi->dev, budget);
+	received = gmac_rx(napi->dev, budget, &freeq_consumed);
 	if (received < budget)
 		++port->rx_napi_exits;
 
 	u64_stats_update_end(&port->rx_stats_syncp);
 
-	port->freeq_refill += received;
+	port->freeq_refill += freeq_consumed;
 	if (port->freeq_refill > freeq_threshold) {
 		port->freeq_refill -= freeq_threshold;
 		geth_fill_freeq(geth, true);
Signature · 1 line
2.53.0
author maintainer
#367 lore raw

PATCH 367/733 drm/adp: Drop the select of the nonexistent CONFIG_DRM_KMS_DMA_HELPER

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Karl Mehltretter <[email protected]>

[ Upstream commit fedf002d7d08bee36693aacd1ade2ba39351ea91 ]

There is no Kconfig symbol CONFIG_DRM_KMS_DMA_HELPER. The former CONFIG_DRM_KMS_CMA_HELPER was removed by commit 09717af7d13d ("drm:
Remove CONFIG_DRM_KMS_CMA_HELPER option") before this driver was added, so the select does nothing. The driver already selects CONFIG_DRM_GEM_DMA_HELPER, which is what it needs.

Remove the dead line.

Fixes 332122eba628 ("drm: adp: Add Apple Display Pipe driver")
Assisted-by LLM
---
 drivers/gpu/drm/adp/Kconfig | 1 -
 1 file changed, 1 deletion(-)
drivers/gpu/drm/adp/Kconfig +0 -1
index 9fcc27eb200db..acfa21ee06d22 100644
@@ -6,7 +6,6 @@ config DRM_ADP
 	select DRM_KMS_HELPER
 	select DRM_BRIDGE_CONNECTOR
 	select DRM_DISPLAY_HELPER
-	select DRM_KMS_DMA_HELPER
 	select DRM_GEM_DMA_HELPER
 	select DRM_PANEL_BRIDGE
 	select VIDEOMODE_HELPERS
Signature · 1 line
2.53.0
author maintainer
#368 lore raw

PATCH 368/733 drm/logicvc: Drop the select of the nonexistent CONFIG_DRM_KMS_DMA_HELPER

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Karl Mehltretter <[email protected]>

[ Upstream commit f97802dd98b27e45c04293f9926f07642578b23f ]

CONFIG_DRM_KMS_CMA_HELPER was removed by commit 09717af7d13d ("drm:
Remove CONFIG_DRM_KMS_CMA_HELPER option"). When commit 6bcfe8eaeef0 ("drm/fb: rename FB CMA helpers to FB DMA helpers") later renamed the select in this Kconfig to CONFIG_DRM_KMS_DMA_HELPER, no symbol of that name existed, and git log -S finds no Kconfig file that has defined one since. The select is silently ignored. The driver already selects CONFIG_DRM_GEM_DMA_HELPER, which is what it needs.

Remove the dead line.

Fixes 6bcfe8eaeef0 ("drm/fb: rename FB CMA helpers to FB DMA helpers")
Assisted-by LLM
---
 drivers/gpu/drm/logicvc/Kconfig | 1 -
 1 file changed, 1 deletion(-)
drivers/gpu/drm/logicvc/Kconfig +0 -1
index 579a358ed5cf3..11aae1626199b 100644
@@ -4,7 +4,6 @@ config DRM_LOGICVC
 	depends on OF || COMPILE_TEST
 	select DRM_CLIENT_SELECTION
 	select DRM_KMS_HELPER
-	select DRM_KMS_DMA_HELPER
 	select DRM_GEM_DMA_HELPER
 	select REGMAP
 	select REGMAP_MMIO
Signature · 1 line
2.53.0
author maintainer
#369 lore raw

PATCH 369/733 s390/debug: Fix NULL pointer dereference in debug_set_level()

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Mikhail Zaslonko <[email protected]>

[ Upstream commit b1eb31d533cdfcae1011ed53850d52f36afe5774 ]

Commit a2cec6863709 ("s390/debug: Add s390dbf kernel parameter") incorrectly removed a null-id check from debug_set_level(), introducing a possible NULL pointer dereference for debug-API users that put debug_register() results unchecked into debug_set_level().

Fix this by moving the check from the internal _debug_set_level() variant back to the external debug_set_level() wrapper.

Fixes a2cec6863709 ("s390/debug: Add s390dbf kernel parameter")
---
 arch/s390/kernel/debug.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)
arch/s390/kernel/debug.c +3 -3
index 14d2b58ad0930..e06abf1dbc218 100644
@@ -1074,9 +1074,6 @@ static void _debug_set_level(debug_info_t *id, int new_level)
 {
 	unsigned long flags;
 
-	if (!id)
-		return;
-
 	if (new_level == DEBUG_OFF_LEVEL) {
 		pr_info("%s: switched off\n", id->name);
 	} else if ((new_level > DEBUG_MAX_LEVEL) || (new_level < 0)) {
@@ -1101,6 +1098,9 @@ static void _debug_set_level(debug_info_t *id, int new_level)
  */
 void debug_set_level(debug_info_t *id, int new_level)
 {
+	if (!id)
+		return;
+
 	/* Level specified via kernel parameter takes precedence */
 	debug_get_param(id->name, &new_level, NULL);
 
Signature · 1 line
2.53.0
author maintainer
#370 lore raw

PATCH 370/733 ALSA: hda: Report a change when only the channel status bytes move

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: HyeongJun An <[email protected]>

[ Upstream commit 7284788743121ec8bed556b00f830dc52ad9955d ]

The put() callback of "IEC958 Playback Default" returns whether the converted register value moved. The convert_from_spdif_status() helper reads part of the first two channel status bytes and none of the last two, while the get() callback returns all four. So a write that lands only in the bits it does not read changes what userspace reads back and reports no change. Of the 31 bits above the mode bit, 20 are such bits in consumer mode and 29 in professional mode. The core notifies only on a positive return.

Toggling status[2] bit 0 on an HDA HDMI codec moves the read-back from 04 00 00 00 to 04 00 01 00 with no event. Toggling the non-audio bit in status[0] gives one.

Compare the stored status as well, the way the ac97 code does. The write to the codec stays gated on the converted value.

Fixes 1da177e4c3f4 ("Linux-2.6.12-rc2")
Assisted-by Claude:claude-opus-5
---
 sound/hda/common/codec.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
sound/hda/common/codec.c +3 -1
index ef533770179b4..33dbe21fa165c 100644
@@ -2271,6 +2271,7 @@ static int snd_hda_spdif_default_put(struct snd_kcontrol *kcontrol,
 	int idx = kcontrol->private_value;
 	struct hda_spdif_out *spdif;
 	hda_nid_t nid;
+	unsigned int old_status;
 	unsigned short val;
 	int change;
 
@@ -2279,6 +2280,7 @@ static int snd_hda_spdif_default_put(struct snd_kcontrol *kcontrol,
 	guard(mutex)(&codec->spdif_mutex);
 	spdif = snd_array_elem(&codec->spdif_out, idx);
 	nid = spdif->nid;
+	old_status = spdif->status;
 	spdif->status = ucontrol->value.iec958.status[0] |
 		((unsigned int)ucontrol->value.iec958.status[1] << 8) |
 		((unsigned int)ucontrol->value.iec958.status[2] << 16) |
@@ -2289,7 +2291,7 @@ static int snd_hda_spdif_default_put(struct snd_kcontrol *kcontrol,
 	spdif->ctls = val;
 	if (change && nid != (u16)-1)
 		set_dig_out_convert(codec, nid, val & 0xff, (val >> 8) & 0xff);
-	return change;
+	return change || spdif->status != old_status;
 }
 
 #define snd_hda_spdif_out_switch_info	snd_ctl_boolean_mono_info
Signature · 1 line
2.53.0
author maintainer
#371 lore raw

PATCH 371/733 platform/x86: x86-android-tablets: fix gpio_secondary_fwnode_init() not working

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Hans de Goede <[email protected]>

[ Upstream commit dd519eb8f66eaa205bbbdcb753588138a1d18414 ]

acpi_bus_find_device_by_name() call returns a pointer to the device object on the ACPI bus, aka the ACPI companion device.

gpio_secondary_fwnode_init() then continues with setting the secondary fwnode on this device. But this is not the actual physical device for the GPIO controller (e.g. the GPIO controller platform bus device).

This mismatch is causing GPIO lookups by secondary fwnode to not work.

Modify gpio_secondary_fwnode_init() to instead set the secondary fwnode of the first physical device associated with the ACPI companion device.

This fixes the GPIO lookups not working.

Fixes 1448c2d2ca5c ("platform/x86: x86-android-tablets: enable fwnode matching of GPIO chips")
---
 drivers/platform/x86/x86-android-tablets/core.c | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)
drivers/platform/x86/x86-android-tablets/core.c +9 -2
index 722c0ae4ecd12..7751261f4b7cf 100644
@@ -381,6 +381,7 @@ static int gpio_secondary_fwnode_init(struct device *parent)
 {
 	const struct software_node *const *swnode;
 	struct fwnode_handle *fwnode;
+	struct device *phys_dev;
 	int ret;
 
 	if (!gpiochip_node_group)
@@ -408,9 +409,15 @@ static int gpio_secondary_fwnode_init(struct device *parent)
 		if (WARN_ON(!fwnode))
 			return -ENOENT;
 
-		set_secondary_fwnode(dev, fwnode);
+		phys_dev = acpi_get_first_physical_node(to_acpi_device(dev));
+		if (!phys_dev)
+			return dev_err_probe(parent, -ENODEV,
+					     "No physical device for ACPI GPIO dev: %pfwP\n",
+					     fwnode);
 
-		ret = devm_add_action_or_reset(parent, gpio_secondary_unset, get_device(dev));
+		set_secondary_fwnode(phys_dev, fwnode);
+
+		ret = devm_add_action_or_reset(parent, gpio_secondary_unset, get_device(phys_dev));
 		if (ret)
 			return ret;
 	}
Signature · 1 line
2.53.0
author maintainer
#372 lore raw

PATCH 372/733 idpf: account for VLAN header when parsing RSC packet header

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Joshua Hay <[email protected]>

[ Upstream commit cc6d60ef92278a31ffc2e94966a0921b9646af18 ]

While parsing the header of a Receive Side Coalesced (RSC) packet, check if a VLAN tag is present and adjust the header parsing accordingly. Otherwise, Rx TCP traffic is completely broken for any VLAN interface whose underlying interface has RSC (rx-gro-hw) enabled.

We only need to worry about one VLAN header since Rx packets with multiple VLAN headers are not candidates for RSC.

Fixes 3a8845af66edb ("idpf: add RX splitq napi poll support")
---
 drivers/net/ethernet/intel/idpf/idpf_txrx.c | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)
drivers/net/ethernet/intel/idpf/idpf_txrx.c +7 -3
index 91ca75e454630..0b4e3d075fb05 100644
@@ -3299,6 +3299,7 @@ static int idpf_rx_rsc(struct idpf_rx_queue *rxq, struct sk_buff *skb,
 		       struct libeth_rx_pt decoded)
 {
 	u16 rsc_segments, rsc_seg_len;
+	u16 l3_start = 0;
 	bool ipv4, ipv6;
 	int len;
 
@@ -3321,7 +3322,10 @@ static int idpf_rx_rsc(struct idpf_rx_queue *rxq, struct sk_buff *skb,
 	NAPI_GRO_CB(skb)->count = rsc_segments;
 	skb_shinfo(skb)->gso_size = rsc_seg_len;
 
-	skb_reset_network_header(skb);
+	if (unlikely(eth_type_vlan(skb->protocol)))
+		l3_start = VLAN_HLEN;
+
+	skb_set_network_header(skb, l3_start);
 
 	if (ipv4) {
 		struct iphdr *ipv4h = ip_hdr(skb);
@@ -3329,7 +3333,7 @@ static int idpf_rx_rsc(struct idpf_rx_queue *rxq, struct sk_buff *skb,
 		skb_shinfo(skb)->gso_type = SKB_GSO_TCPV4;
 
 		/* Reset and set transport header offset in skb */
-		skb_set_transport_header(skb, sizeof(struct iphdr));
+		skb_set_transport_header(skb, l3_start + sizeof(struct iphdr));
 		len = skb->len - skb_transport_offset(skb);
 
 		/* Compute the TCP pseudo header checksum*/
@@ -3339,7 +3343,7 @@ static int idpf_rx_rsc(struct idpf_rx_queue *rxq, struct sk_buff *skb,
 		struct ipv6hdr *ipv6h = ipv6_hdr(skb);
 
 		skb_shinfo(skb)->gso_type = SKB_GSO_TCPV6;
-		skb_set_transport_header(skb, sizeof(struct ipv6hdr));
+		skb_set_transport_header(skb, l3_start + sizeof(struct ipv6hdr));
 		len = skb->len - skb_transport_offset(skb);
 		tcp_hdr(skb)->check =
 			~tcp_v6_check(len, &ipv6h->saddr, &ipv6h->daddr, 0);
Signature · 1 line
2.53.0
author maintainer
#373 lore raw

PATCH 373/733 ice: add missing xa_destroy for sched_node_ids

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Jacob Keller <[email protected]>

[ Upstream commit 53432c4c3e869076350aef319534431af8ba99c1 ]

Commit 16dfa49406bc ("ice: Introduce new parameters in ice_sched_node") added a sched_node_ids xarray to the port info structure, but never called xa_destroy on it.

Since xarrays can allocate internal memory, this can result in a memory leak even if every element in the xarray has been removed.

The xarray is currently embedded in the port_info structure. This appears to have been done because its use is within functions that take the port_info as a primary argument.

However, this complicates managing the lifecycle of the field. The port_info structure is allocated in ice_init_hw() using devm, and it is not released until the devm cleanup when the driver is unloaded.

The ice_init_hw() function is called in many places, including devlink reload, and possibly during DDP load after updating the Tx scheduler layout.

Adding a call of xa_destroy to the ice_deinit_hw() causes Sashiko to raise multiple concerns due to potential ordering issues and possible ways that port_info could be a dangling reference.

To handle this, move the sched_node_ids out of port_info and into the hw structure. All users of the array already have a pointer to hw anyways, and there is only one sched_node_ids per adapter. While here, remove the overly verbose comment explaining the nature of the sched_node_ids xarray.

Add the missing xa_destroy to the cleanup path and to ice_deinit_hw(), ensuring that we properly release the xarray memory.

This was caught by Sashiko during development of unrelated code.

Fixes 16dfa49406bc ("ice: Introduce new parameters in ice_sched_node")
---
 drivers/net/ethernet/intel/ice/ice_common.c | 9 ++++++---
 drivers/net/ethernet/intel/ice/ice_sched.c  | 4 ++--
 drivers/net/ethernet/intel/ice/ice_type.h   | 2 +-
 3 files changed, 9 insertions(+), 6 deletions(-)
drivers/net/ethernet/intel/ice/ice_common.c +6 -3
index ef1ce106f81b5..04633103e3e61 100644
@@ -1051,14 +1051,13 @@ int ice_init_hw(struct ice_hw *hw)
 
 	hw->evb_veb = true;
 
-	/* init xarray for identifying scheduling nodes uniquely */
-	xa_init_flags(&hw->port_info->sched_node_ids, XA_FLAGS_ALLOC);
+	xa_init_flags(&hw->sched_node_ids, XA_FLAGS_ALLOC);
 
 	/* Query the allocated resources for Tx scheduler */
 	status = ice_sched_query_res_alloc(hw);
 	if (status) {
 		ice_debug(hw, ICE_DBG_SCHED, "Failed to get scheduler allocated resources\n");
-		goto err_unroll_alloc;
+		goto err_unroll_xarray;
 	}
 	ice_sched_get_psm_clk_freq(hw);
 
@@ -1146,6 +1145,8 @@ int ice_init_hw(struct ice_hw *hw)
 	ice_cleanup_fltr_mgmt_struct(hw);
 err_unroll_sched:
 	ice_sched_cleanup_all(hw);
+err_unroll_xarray:
+	xa_destroy(&hw->sched_node_ids);
 err_unroll_alloc:
 	devm_kfree(ice_hw_to_dev(hw), hw->port_info);
 err_unroll_cqinit:
@@ -1186,6 +1187,8 @@ void ice_deinit_hw(struct ice_hw *hw)
 
 	/* Clear VSI contexts if not already cleared */
 	ice_clear_all_vsi_ctx(hw);
+
+	xa_destroy(&hw->sched_node_ids);
 }
 
 /**
drivers/net/ethernet/intel/ice/ice_sched.c +2 -2
index fff0c1afdb414..ffa18d86729a0 100644
@@ -371,7 +371,7 @@ void ice_free_sched_node(struct ice_port_info *pi, struct ice_sched_node *node)
 
 	devm_kfree(ice_hw_to_dev(hw), node->children);
 	kfree(node->name);
-	xa_erase(&pi->sched_node_ids, node->id);
+	xa_erase(&hw->sched_node_ids, node->id);
 	devm_kfree(ice_hw_to_dev(hw), node);
 }
 
@@ -977,7 +977,7 @@ ice_sched_add_elems(struct ice_port_info *pi, struct ice_sched_node *tc_node,
 		if (!new_node->name)
 			return -ENOMEM;
 
-		status = xa_alloc(&pi->sched_node_ids, &new_node->id, NULL, XA_LIMIT(0, UINT_MAX),
+		status = xa_alloc(&hw->sched_node_ids, &new_node->id, NULL, XA_LIMIT(0, UINT_MAX),
 				  GFP_KERNEL);
 		if (status) {
 			ice_debug(hw, ICE_DBG_SCHED, "xa_alloc failed for sched node status =%d\n",
drivers/net/ethernet/intel/ice/ice_type.h +1 -1
index d9a5c1aae7c23..cf147a2127071 100644
@@ -765,7 +765,6 @@ struct ice_port_info {
 	/* List contain profile ID(s) and other params per layer */
 	struct list_head rl_prof_list[ICE_AQC_TOPO_MAX_LEVEL_NUM];
 	struct ice_qos_cfg qos_cfg;
-	struct xarray sched_node_ids;
 	u8 is_vf:1;
 	u8 is_custom_tx_enabled:1;
 };
@@ -930,6 +929,7 @@ struct ice_hw {
 	u8 sw_entry_point_layer;
 	u16 max_children[ICE_AQC_TOPO_MAX_LEVEL_NUM];
 	struct list_head agg_list;	/* lists all aggregator */
+	struct xarray sched_node_ids;
 
 	struct ice_vsi_ctx *vsi_ctx[ICE_MAX_VSI];
 	u8 evb_veb;		/* true for VEB, false for VEPA */
Signature · 1 line
2.53.0
author maintainer
#374 lore raw

PATCH 374/733 eth: ice: dont dereference pointers from TP_printk()

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Jakub Kicinski <[email protected]>

[ Upstream commit b8bf9bfda5f62e11444e483c2b4aaff90c5cfc6b ]

After forwarding net-next during the v7.3 merge window we started seeing:

TRACE EVENT ERROR: Event ice_tx_dim_work has double dereference in TP_printk: REC->q_vector->tx.tx_ring->q_index

  WARNING: kernel/trace/trace_events.c:420 at test_double_dereference.cold+0x39/0x4b

this is due to extra checks added in tracing subsystem in
commit b5cc230af5e5 ("tracing: Warn when an event dereferences a pointer in TP_printk()").

Printing happens long after the event was recorded, by which point the pointers may be invalid (the ring or the dim instance). Copy the eight scalars into the event instead.

Fixes 3089cf6d3caa ("ice: add tracepoints")
---
 drivers/net/ethernet/intel/ice/ice_trace.h | 64 ++++++++++++++--------
 1 file changed, 42 insertions(+), 22 deletions(-)
drivers/net/ethernet/intel/ice/ice_trace.h +42 -22
index 4f35ef8d6b299..7568c917cdbe1 100644
@@ -63,23 +63,33 @@
 DECLARE_EVENT_CLASS(ice_rx_dim_template,
 		    TP_PROTO(struct ice_q_vector *q_vector, struct dim *dim),
 		    TP_ARGS(q_vector, dim),
-		    TP_STRUCT__entry(__field(struct ice_q_vector *, q_vector)
-				     __field(struct dim *, dim)
+		    TP_STRUCT__entry(__field(u16, q_index)
+				     __field(u8, state)
+				     __field(u8, profile_ix)
+				     __field(u8, tune_state)
+				     __field(u8, steps_right)
+				     __field(u8, steps_left)
+				     __field(u8, tired)
 				     __string(devname, q_vector->rx.rx_ring->netdev->name)),
 
-		    TP_fast_assign(__entry->q_vector = q_vector;
-				   __entry->dim = dim;
+		    TP_fast_assign(__entry->q_index = q_vector->rx.rx_ring->q_index;
+				   __entry->state = dim->state;
+				   __entry->profile_ix = dim->profile_ix;
+				   __entry->tune_state = dim->tune_state;
+				   __entry->steps_right = dim->steps_right;
+				   __entry->steps_left = dim->steps_left;
+				   __entry->tired = dim->tired;
 				   __assign_str(devname);),
 
 		    TP_printk("netdev: %s Rx-Q: %d dim-state: %d dim-profile: %d dim-tune: %d dim-st-right: %d dim-st-left: %d dim-tired: %d",
 			      __get_str(devname),
-			      __entry->q_vector->rx.rx_ring->q_index,
-			      __entry->dim->state,
-			      __entry->dim->profile_ix,
-			      __entry->dim->tune_state,
-			      __entry->dim->steps_right,
-			      __entry->dim->steps_left,
-			      __entry->dim->tired)
+			      __entry->q_index,
+			      __entry->state,
+			      __entry->profile_ix,
+			      __entry->tune_state,
+			      __entry->steps_right,
+			      __entry->steps_left,
+			      __entry->tired)
 );
 
 DEFINE_EVENT(ice_rx_dim_template, ice_rx_dim_work,
@@ -90,23 +100,33 @@ DEFINE_EVENT(ice_rx_dim_template, ice_rx_dim_work,
 DECLARE_EVENT_CLASS(ice_tx_dim_template,
 		    TP_PROTO(struct ice_q_vector *q_vector, struct dim *dim),
 		    TP_ARGS(q_vector, dim),
-		    TP_STRUCT__entry(__field(struct ice_q_vector *, q_vector)
-				     __field(struct dim *, dim)
+		    TP_STRUCT__entry(__field(u16, q_index)
+				     __field(u8, state)
+				     __field(u8, profile_ix)
+				     __field(u8, tune_state)
+				     __field(u8, steps_right)
+				     __field(u8, steps_left)
+				     __field(u8, tired)
 				     __string(devname, q_vector->tx.tx_ring->netdev->name)),
 
-		    TP_fast_assign(__entry->q_vector = q_vector;
-				   __entry->dim = dim;
+		    TP_fast_assign(__entry->q_index = q_vector->tx.tx_ring->q_index;
+				   __entry->state = dim->state;
+				   __entry->profile_ix = dim->profile_ix;
+				   __entry->tune_state = dim->tune_state;
+				   __entry->steps_right = dim->steps_right;
+				   __entry->steps_left = dim->steps_left;
+				   __entry->tired = dim->tired;
 				   __assign_str(devname);),
 
 		    TP_printk("netdev: %s Tx-Q: %d dim-state: %d dim-profile: %d dim-tune: %d dim-st-right: %d dim-st-left: %d dim-tired: %d",
 			      __get_str(devname),
-			      __entry->q_vector->tx.tx_ring->q_index,
-			      __entry->dim->state,
-			      __entry->dim->profile_ix,
-			      __entry->dim->tune_state,
-			      __entry->dim->steps_right,
-			      __entry->dim->steps_left,
-			      __entry->dim->tired)
+			      __entry->q_index,
+			      __entry->state,
+			      __entry->profile_ix,
+			      __entry->tune_state,
+			      __entry->steps_right,
+			      __entry->steps_left,
+			      __entry->tired)
 );
 
 DEFINE_EVENT(ice_tx_dim_template, ice_tx_dim_work,
Signature · 1 line
2.53.0
author maintainer
#375 lore raw

PATCH 375/733 Bluetooth: Properly disable remote wakeup for MT7922/MT7925 on Ryzen platform

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Rong Zhang <[email protected]>

[ Upstream commit dcaf83ead130d3067862599089b0999b3da140a4 ]

It is reported that a remote wakeup could cause MT7922/MT7925's btusb interface completely unresponsive. Resetting the xHCI root hub doesn't help at all, and recovering from such a state needs a power cycle.

All reports seen to be relevant to Ryzen-based laptops. These NICs are usually used as OEM components thanks to some sort of reference designs. Their popularity on other platforms is unclear. While there is still a chance that the quirk may exist on other platforms, be cautious and only apply the quirk to direct children of Ryzen platforms's root hubs for the time being. In most cases the root hub is on the SoC or PCH, which needs the quirk. Unfortunately, this can't distinguish root hubs on PCIe add-in cards. Such roughness should be acceptable, as PCIe USB controller add-in cards are less commonly used nowadays. On the other hand, applying the quirk doesn't hurt any functionalities either, as the device can still be used as a wakeup source if desired. Theoretically, we could retrieve the root hub's PCI vendor ID with some hierarchy magic, but that's too intrusive...

Meanwhile, though device_set_wakeup_capable(false) is the correct fix for other NICs with fake remote wakeup capabilities, doing so for MT7922/MT7925 effectively prevents it from being used as wakeup sources as per userspace requests. Hence, return -EBUSY on runtime suspend to prevent the interface from being autosuspended while it's still opened, which has the same effect as device_set_wakeup_capable(false), since disabling remote wakeup simply causes the USB core to gate runtime autosuspend as well due to needs_remote_wakeup == 1. The interface can be safely autosuspended as long as remote wakeup is disabled, i.e., after closing the HCI device.

Specifically, the interface may still take the advantage of remote wakeup in order to wake up the system from sleep if userspace has enabled it as a wakeup source.

Fixes e31d761628ad ("Bluetooth: btmtk: Disable remote wakeup for MT7922/MT7925")
---
 drivers/bluetooth/btmtk.c | 10 ------
 drivers/bluetooth/btusb.c | 73 ++++++++++++++++++++++++++++++++++++---
 2 files changed, 69 insertions(+), 14 deletions(-)
drivers/bluetooth/btmtk.c +0 -10
index c0ed51567ed4d..9589caff925de 100644
@@ -1374,16 +1374,6 @@ int btmtk_usb_setup(struct hci_dev *hdev)
 		break;
 	case 0x7922:
 	case 0x7925:
-		/*
-		 * A remote wakeup could cause the device completely unresponsive, and
-		 * recovering from such a state needs a power cycle.
-		 *
-		 * Since the remote wakeup capability is super broken, just disable it
-		 * to get rid of the troubles. The device can still be autosuspended
-		 * when the bluetooth interface is closed.
-		 */
-		device_set_wakeup_capable(&btmtk_data->udev->dev, false);
-		fallthrough;
 	case 0x7961:
 	case 0x7902:
 	case 0x6639:
drivers/bluetooth/btusb.c +69 -4

109 lines not on this page · raw message

Signature · 1 line
2.53.0
author maintainer
#376 lore raw

PATCH 376/733 Bluetooth: btusb: Fix UAF of btusb_data by rx_work

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Luiz Augusto von Dentz <[email protected]>

[ Upstream commit 1c12c3117639e78940959d956519c758c57d0849 ]

btusb_close() and btusb_flush() cancel data->rx_work with the asynchronous cancel_delayed_work(), so if btusb_rx_work() is already running on another CPU it keeps running after the cancel returns.

btusb_disconnect() calls hci_unregister_dev(), which invokes btusb_close(), and then frees the btusb_data. A still running btusb_rx_work() then dereferences the freed data:

	while ((skb = skb_dequeue(&data->acl_q)))
		data->recv_acl(data->hdev, skb);

Use cancel_delayed_work_sync() instead. In btusb_close() the cancel also has to happen after btusb_stop_traffic(), otherwise an URB completion racing with the cancel can requeue the work right after it has been waited for.

Fixes 800fe5ec302e ("Bluetooth: btusb: Add support for queuing during polling interval")
---
 drivers/bluetooth/btusb.c | 14 ++++++++++----
 1 file changed, 10 insertions(+), 4 deletions(-)
drivers/bluetooth/btusb.c +10 -4
index ed4bf9a478087..53baf56b9dd96 100644
@@ -2074,18 +2074,24 @@ static int btusb_close(struct hci_dev *hdev)
 
 	BT_DBG("%s", hdev->name);
 
-	cancel_delayed_work(&data->rx_work);
 	cancel_work_sync(&data->work);
 	cancel_work_sync(&data->waker);
 
-	skb_queue_purge(&data->acl_q);
-
 	clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
 	clear_bit(BTUSB_BULK_RUNNING, &data->flags);
 	clear_bit(BTUSB_INTR_RUNNING, &data->flags);
 	clear_bit(BTUSB_DIAG_RUNNING, &data->flags);
 
 	btusb_stop_traffic(data);
+
+	/* rx_work must only be canceled once the URBs that can rearm it are
+	 * gone, and it must be canceled synchronously since btusb_disconnect()
+	 * frees the btusb_data it dereferences right after hci_unregister_dev().
+	 */
+	cancel_delayed_work_sync(&data->rx_work);
+
+	skb_queue_purge(&data->acl_q);
+
 	btusb_free_frags(data);
 
 	err = usb_autopm_get_interface(data->intf);
@@ -2111,7 +2117,7 @@ static int btusb_flush(struct hci_dev *hdev)
 
 	BT_DBG("%s", hdev->name);
 
-	cancel_delayed_work(&data->rx_work);
+	cancel_delayed_work_sync(&data->rx_work);
 
 	skb_queue_purge(&data->acl_q);
 
Signature · 1 line
2.53.0
author maintainer
#377 lore raw

PATCH 377/733 Bluetooth: btintel_pcie: validate packet_len before skb_put_data

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Kiran K <[email protected]>

[ Upstream commit 6436e1b5331b1aebf905c13e0880a37032719b75 ]

btintel_pcie_submit_rx_work() reads packet_len from rfh_hdr without checking if it exceeds the RX buffer size. An oversized packet_len can lead to an out-of-bounds read in skb_put_data().

Validate packet_len to ensure it is non-zero and does not exceed BTINTEL_PCIE_BUFFER_SIZE - sizeof(*rfh_hdr), logging an error when invalid.

This issue was reported by Claude Mythos. It can be simulated either by using customized firmware configured to return an invalid packet_len or by modifying rfh_hdr->packet_len in the driver before calling btintel_pcie_submit_rx_work().

Fixes c2b636b3f788 ("Bluetooth: btintel_pcie: Add support for PCIe transport")
---
 drivers/bluetooth/btintel_pcie.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/bluetooth/btintel_pcie.c +3 -1
index 6b6258d03dd84..da46fb39d53ea 100644
@@ -1537,7 +1537,9 @@ static int btintel_pcie_submit_rx_work(struct btintel_pcie_data *data, u8 status
 	rfh_hdr = buf;
 
 	len = rfh_hdr->packet_len;
-	if (len <= 0) {
+	if (len == 0 || len > BTINTEL_PCIE_BUFFER_SIZE - sizeof(*rfh_hdr)) {
+		bt_dev_err(data->hdev, "Invalid packet_len %d (max %zu)", len,
+			   BTINTEL_PCIE_BUFFER_SIZE - sizeof(*rfh_hdr));
 		ret = -EINVAL;
 		goto resubmit;
 	}
Signature · 1 line
2.53.0
author maintainer
#378 lore raw

PATCH 378/733 Bluetooth: btintel_pcie: fix tx_handle bounds off-by-one

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Kiran K <[email protected]>

[ Upstream commit 3dd1b41f96aad08444be1b7626c89de2b9f2abd4 ]

Valid indices into txq->urbd0s/tfds/bufs are 0..txq->count-1, so tfd_index == txq->count is already out of range. Change the guard in btintel_pcie_msix_tx_handle() from '> txq->count' to '>= txq->count'.

This issue was reported by Claude Mythos.

Fixes c2b636b3f788 ("Bluetooth: btintel_pcie: Add support for PCIe transport")
---
 drivers/bluetooth/btintel_pcie.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/bluetooth/btintel_pcie.c +1 -1
index da46fb39d53ea..c64e96fe88976 100644
@@ -1099,7 +1099,7 @@ static void btintel_pcie_msix_tx_handle(struct btintel_pcie_data *data)
 
 		urbd0 = &txq->urbd0s[cr_tia];
 
-		if (urbd0->tfd_index > txq->count)
+		if (urbd0->tfd_index >= txq->count)
 			return;
 
 		cr_tia = (cr_tia + 1) % txq->count;
Signature · 1 line
2.53.0
author maintainer
#379 lore raw

PATCH 379/733 Bluetooth: btmtk: Declare MT7920 (MT7961 1a) Bluetooth firmware

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Ivan Hu <[email protected]>

[ Upstream commit 3d8a8e81ea8ad8813d4c82a12ba53ecb597b217d ]

btmtk_fw_get_filename() constructs the firmware name at runtime, so for the MT7920 variant (dev_id 0x7961 with fw_flavor set) it requests "mediatek/BT_RAM_CODE_MT7961_1a_2_hdr.bin" without ever declaring it via MODULE_FIRMWARE(). Tools that select firmware from module metadata (e.g. "modinfo -F firmware") therefore omit this blob, so request_firmware() fails and Bluetooth does not initialise on MT7920, even though the file is present in linux-firmware.

Declare it with MODULE_FIRMWARE(), as the mt76 driver already does for the corresponding MT7920 wifi firmware.

Fixes 1cb63d80fff6 ("Bluetooth: btusb: Add support Mediatek MT7920")
---
 drivers/bluetooth/btmtk.c | 1 +
 drivers/bluetooth/btmtk.h | 1 +
 2 files changed, 2 insertions(+)
drivers/bluetooth/btmtk.c +1 -0
index 9589caff925de..26d525acd6590 100644
@@ -1577,5 +1577,6 @@ MODULE_FIRMWARE(FIRMWARE_MT7663);
 MODULE_FIRMWARE(FIRMWARE_MT7668);
 MODULE_FIRMWARE(FIRMWARE_MT7922);
 MODULE_FIRMWARE(FIRMWARE_MT7961);
+MODULE_FIRMWARE(FIRMWARE_MT7920);
 MODULE_FIRMWARE(FIRMWARE_MT7925);
 MODULE_FIRMWARE(FIRMWARE_MT7927);
drivers/bluetooth/btmtk.h +1 -0
index c83c24897c954..bc26148ec5442 100644
@@ -7,6 +7,7 @@
 #define FIRMWARE_MT7922		"mediatek/BT_RAM_CODE_MT7922_1_1_hdr.bin"
 #define FIRMWARE_MT7902		"mediatek/BT_RAM_CODE_MT7902_1_1_hdr.bin"
 #define FIRMWARE_MT7961		"mediatek/BT_RAM_CODE_MT7961_1_2_hdr.bin"
+#define FIRMWARE_MT7920		"mediatek/BT_RAM_CODE_MT7961_1a_2_hdr.bin"
 #define FIRMWARE_MT7925		"mediatek/mt7925/BT_RAM_CODE_MT7925_1_1_hdr.bin"
 #define FIRMWARE_MT7927		"mediatek/mt7927/BT_RAM_CODE_MT6639_2_1_hdr.bin"
 
Signature · 1 line
2.53.0
author maintainer
#380 lore raw

PATCH 380/733 Bluetooth: hci_sysfs: Fix NULL pointer dereference in device_del()

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Krystian Kaniewski <[email protected]>

[ Upstream commit 9b851b09b392da68bd715601f10a5adb2d8d19b8 ]

A NULL pointer dereference in klist_put() occurs when a child device (such as a BNEP network device in bnep_session) is concurrently being unregistered while hci_conn_del_sysfs() reparents child devices.

This is caused by a race condition between hci_conn_del_sysfs() and concurrent child device unregistration (e.g. bnep_session calling unregister_netdev()). During device unregistration, device_del() snapshots a non-NULL parent pointer. Concurrently, hci_conn_del_sysfs() finds the child device using device_find_any_child() and calls device_move() to reparent it to NULL, which removes the node from its parent's klist and clears knode_parent. Subsequently, device_del() calls klist_del(&dev->p->knode_parent) using the stale parent snapshot, causing klist_put() to dereference knode_klist(n)->put on an already removed node, resulting in a NULL pointer dereference.

This race was introduced by commit 27aabf27fd01 ("Bluetooth: fix use-after-free in device_for_each_child()"), which replaced device_find_child(..., __match_tty) with device_find_any_child() in hci_conn_del_sysfs(). That change was intended to avoid a use-after-free where conn->dev outlived its parent hdev->dev when child devices held references to conn->dev, because conn->dev only held a reference to hdev->dev while registered in sysfs.

Fix the issue properly by taking an explicit reference to the parent device with get_device(&hdev->dev) in hci_conn_init_sysfs() and dropping it with put_device(parent) in bt_link_release() when the conn device is freed. This ensures that hdev->dev remains valid for the entire lifecycle of conn->dev, resolving the underlying use-after-free. With the parent reference held properly, restore the __match_tty filter in hci_conn_del_sysfs() so that device_move() is only invoked on persistent RFCOMM TTY devices as originally intended, eliminating the race condition with unregistering network devices.

Fixes 27aabf27fd01 ("Bluetooth: fix use-after-free in device_for_each_child()")
Assisted-by Gemini:gemini-3.7-flash syzbot
Reported-by [email protected]
---
 net/bluetooth/hci_sysfs.c | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)
net/bluetooth/hci_sysfs.c +15 -2
index 8957ce7c21b76..c2065abf753e0 100644
@@ -13,7 +13,10 @@ static const struct class bt_class = {
 static void bt_link_release(struct device *dev)
 {
 	struct hci_conn *conn = to_hci_conn(dev);
+	struct device *parent = dev->parent;
+
 	kfree(conn);
+	put_device(parent);
 }
 
 static const struct device_type bt_link = {
@@ -21,6 +24,16 @@ static const struct device_type bt_link = {
 	.release = bt_link_release,
 };
 
+/*
+ * The rfcomm tty device will possibly retain even when conn
+ * is down, and sysfs doesn't support move zombie device,
+ * so we should move the device before conn device is destroyed.
+ */
+static int __match_tty(struct device *dev, const void *data)
+{
+	return !strncmp(dev_name(dev), "rfcomm", 6);
+}
+
 void hci_conn_init_sysfs(struct hci_conn *conn)
 {
 	struct hci_dev *hdev = conn->hdev;
@@ -29,7 +42,7 @@ void hci_conn_init_sysfs(struct hci_conn *conn)
 
 	conn->dev.type = &bt_link;
 	conn->dev.class = &bt_class;
-	conn->dev.parent = &hdev->dev;
+	conn->dev.parent = get_device(&hdev->dev);
 
 	device_initialize(&conn->dev);
 }
@@ -69,7 +82,7 @@ void hci_conn_del_sysfs(struct hci_conn *conn)
 	while (1) {
 		struct device *dev;
 
-		dev = device_find_any_child(&conn->dev);
+		dev = device_find_child(&conn->dev, NULL, __match_tty);
 		if (!dev)
 			break;
 		device_move(dev, NULL, DPM_ORDER_DEV_LAST);
Signature · 1 line
2.53.0
author maintainer
#381 lore raw

PATCH 381/733 Bluetooth: btusb: mediatek: Fix leaked runtime PM reference in reset

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Jiajia Liu <[email protected]>

[ Upstream commit e486a891c412d9d82ee865987f4eead6196e1f96 ]

MT7925 on HP Pro Mini 260 sometimes timed out during reloading driver and reset usb device. btusb_suspend is not called again after closing bluetooth interface.

usbcore: registered new interface driver btusb
Bluetooth: hci0: HW/SW Version: 0x00000000, Build Time: 20260605184935
Bluetooth: hci0: Execution of wmt command timed out
Bluetooth: hci0: Failed to send wmt patch dwnld (-110)
Bluetooth: hci0: Failed to set up firmware (-110)
usb 3-10: reset high-speed USB device number 4 using xhci_hcd
Bluetooth: hci0: HW/SW Version: 0x00000000, Build Time: 20260605184935
Bluetooth: hci0: Device setup in 1856545 usecs
Bluetooth: hci0: AOSP extensions version v1.00
Bluetooth: hci0: AOSP quality report is supported
Bluetooth: MGMT ver 1.23

btusb_mtk_reset calls usb_autopm_get_interface to resume the device before driving the hardware reset, but never calls the matching usb_autopm_put_interface. Every hardware reset therefore leaks a PM usage reference of the interface, preventing the device from being runtime suspended again until it is unbound.

Add the BTUSB_RESET flag. It is set before usb_queue_reset_device and is cleared in btusb_disconnect, which drops the reference as well. If the flag is already set when a new reset is requested, drop one reference.

Also clear BTMTK_HW_RESET_ACTIVE if usb_autopm_get_interface fails, otherwise no further reset could ever be attempted.

Fixes 25b6d7593a3a ("Bluetooth: btmtk: introduce btmtk reset work")
Assisted-by Claude:qwen3.8-max
---
 drivers/bluetooth/btusb.c | 14 +++++++++++++-
 1 file changed, 13 insertions(+), 1 deletion(-)
drivers/bluetooth/btusb.c +13 -1
index 53baf56b9dd96..f91a828f4b23b 100644
@@ -970,6 +970,7 @@ struct qca_dump_info {
 #define BTUSB_ALT6_CONTINUOUS_TX	16
 #define BTUSB_HW_SSR_ACTIVE	17
 #define BTUSB_WAKEUP_BROKEN	18
+#define BTUSB_RESET		19
 
 struct btusb_data {
 	struct hci_dev       *hdev;
@@ -2911,8 +2912,11 @@ static int btusb_mtk_reset(struct hci_dev *hdev, void *rst_data)
 	}
 
 	err = usb_autopm_get_interface(data->intf);
-	if (err < 0)
+	if (err < 0) {
+		bt_dev_err(hdev, "Failed usb_autopm_get_interface: %d", err);
+		clear_bit(BTMTK_HW_RESET_ACTIVE, &btmtk_data->flags);
 		return err;
+	}
 
 	/* Release MediaTek ISO data interface */
 	btusb_mtk_release_iso_intf(hdev);
@@ -2935,6 +2939,11 @@ static int btusb_mtk_reset(struct hci_dev *hdev, void *rst_data)
 
 	err = btmtk_usb_subsys_reset(hdev, btmtk_data->dev_id);
 
+	if (test_and_set_bit(BTUSB_RESET, &data->flags)) {
+		bt_dev_err(hdev, "last usb reset failed? Resetting again");
+		usb_autopm_put_interface_no_suspend(data->intf);
+	}
+
 	usb_queue_reset_device(data->intf);
 	clear_bit(BTMTK_HW_RESET_ACTIVE, &btmtk_data->flags);
 
@@ -4572,6 +4581,9 @@ static void btusb_disconnect(struct usb_interface *intf)
 	if (data->reset_gpio)
 		gpiod_put(data->reset_gpio);
 
+	if (test_and_clear_bit(BTUSB_RESET, &data->flags))
+		usb_autopm_put_interface_no_suspend(data->intf);
+
 	if (intf == data->intf) {
 		if (data->isoc)
 			usb_driver_release_interface(&btusb_driver, data->isoc);
Signature · 1 line
2.53.0
author maintainer
#382 lore raw

PATCH 382/733 Bluetooth: btusb: Fix leaked runtime PM reference in btusb_reset

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Jiajia Liu <[email protected]>

[ Upstream commit c93922dd316b7273a8667d29084632066fa8a2d3 ]

btusb_reset calls usb_autopm_get_interface to resume the device before queuing a reset of it, but never calls the matching usb_autopm_put_interface.

usb_queue_reset_device ends up in usb_reset_device(), and since btusb provides no pre_reset/post_reset callbacks the interface is merely unbound and rebound: the interface device object survives this cycle, and so does its PM usage count, which is not cleared when the driver is unbound.

As a result every reset permanently leaks a PM usage reference, preventing the interface from being runtime suspended again until it is unbound.

Set BTUSB_RESET flag before usb_queue_reset_device so that btusb_disconnect drops the reference. If the flag is already set, drop one reference.

Fixes c9209b269afd ("Bluetooth: btusb: Introduce generic USB reset")
Assisted-by Claude:qwen3.8-max
---
 drivers/bluetooth/btusb.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/bluetooth/btusb.c +3 -1
index f91a828f4b23b..84f55f67523e0 100644
@@ -1047,13 +1047,15 @@ static void btusb_reset(struct hci_dev *hdev)
 	int err;
 
 	data = hci_get_drvdata(hdev);
-	/* This is not an unbalanced PM reference since the device will reset */
 	err = usb_autopm_get_interface(data->intf);
 	if (err) {
 		bt_dev_err(hdev, "Failed usb_autopm_get_interface: %d", err);
 		return;
 	}
 
+	if (test_and_set_bit(BTUSB_RESET, &data->flags))
+		usb_autopm_put_interface_no_suspend(data->intf);
+
 	bt_dev_err(hdev, "Resetting usb device.");
 	usb_queue_reset_device(data->intf);
 }
Signature · 1 line
2.53.0
author maintainer
#383 lore raw

PATCH 383/733 net: mana: Clear RDMA teardown and suspend state in mana_rdma_probe()

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Long Li <[email protected]>

[ Upstream commit f6d61fe4c19cf448e5cba6d8767b4e6966f58606 ]

mana_rdma_remove() sets gd->rdma_teardown to stop mana_rdma_service_handle() from acting on servicing events, but nothing ever clears it. A hardware service reset (GDMA_EQE_HWC_RESET_REQUEST) goes through mana_gd_suspend() -> mana_rdma_remove() and mana_gd_resume() -> mana_rdma_probe(), so from the first reset onwards every GDMA_EQE_HWC_SOC_SERVICE event returns early and RDMA suspend/resume servicing is silently dropped for the life of the device.

gd->is_suspended has the same problem: it is set when servicing removes the adev and is cleared only by a matching resume. A reset while RDMA is suspended re-adds the adev but leaves is_suspended set, so a later resume event calls add_adev() on top of a live gd->adev and leaks it. This is currently masked by the rdma_teardown bug.

Clear both in mana_rdma_probe(). On the reset path mana_rdma_remove() has closed the gate and drained the service workqueue, so clear is_suspended first and re-open the gate with smp_store_release(), paired with smp_load_acquire() in the handler, so the handler cannot observe an open gate with a stale is_suspended. On the initial probe path the gate was never closed and both flags are already clear.

This does not order gd->adev, which add_adev() publishes afterwards. A servicing event arriving in that window is still dropped, as it is in mainline today on the initial probe path; closing it needs probe and the handler to be serialized and is left to a separate change.

Fixes 505cc26bcae0 ("net: mana: Add support for auxiliary device servicing events")
---
 drivers/net/ethernet/microsoft/mana/mana_en.c | 18 +++++++++++++++++-
 1 file changed, 17 insertions(+), 1 deletion(-)
drivers/net/ethernet/microsoft/mana/mana_en.c +17 -1
index 92bb55935c1c4..b2a6f6369bc3c 100644
@@ -3728,7 +3728,8 @@ static void mana_rdma_service_handle(struct work_struct *work)
 	struct device *dev = gd->gdma_context->dev;
 	int ret;
 
-	if (READ_ONCE(gd->rdma_teardown))
+	/* Pairs with the smp_store_release() in mana_rdma_probe(). */
+	if (smp_load_acquire(&gd->rdma_teardown))
 		goto out;
 
 	switch (serv_work->event) {
@@ -4024,6 +4025,21 @@ int mana_rdma_probe(struct gdma_dev *gd)
 	if (err)
 		return err;
 
+	/* Clear the state left by a previous mana_rdma_remove() so servicing
+	 * events are handled again after a reset cycle.
+	 */
+	gd->is_suspended = false;
+
+	/* Publish is_suspended before re-opening the gate, so the handler
+	 * cannot observe an open gate with a stale is_suspended.  Pairs
+	 * with the smp_load_acquire() in mana_rdma_service_handle().  This
+	 * matters on the reset path, where mana_rdma_remove() closed the
+	 * gate and drained the workqueue; on the initial probe path the
+	 * gate was never closed and both flags are already clear.  It does
+	 * not order gd->adev, which add_adev() publishes below.
+	 */
+	smp_store_release(&gd->rdma_teardown, false);
+
 	err = add_adev(gd, "rdma");
 	if (err)
 		mana_gd_deregister_device(gd);
Signature · 1 line
2.53.0
author maintainer
#384 lore raw

PATCH 384/733 ipv6: null-check fib6_node before accessing in __ip6_del_rt_siblings()

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Naman Gulati <[email protected]>

[ Upstream commit cdca92eddc025fdb90071be97738f7d55a65f8dd ]

syzbot reported a null-ptr-deref in __ip6_del_rt_siblings() [0].

The stack trace hinted towards a null dereference of rt->fib6_node when fn->leaf is accessed in __ip6_del_rt_siblings(). With RTNL_FLAG_DOIT_UNLOCKED set, inet6_rtm_delroute() operations run concurrently without acquiring the RTNL lock. In ip6_route_del(), the route lookup happens under rcu_read_lock() without acquiring table->tb6_lock.

Between ip6_route_del() looking up the route and __ip6_del_rt_siblings() acquiring table->tb6_lock, another thread can modify the routing table. For example, when an ECMP route is replaced via RTM_NEWROUTE with NLM_F_REPLACE, fib6_add_rt2node() unlinks all old siblings and sets iter->fib6_node = NULL. A reproducer was found that triggers this [1].

Add a check to ensure rt->fib6_node is non-null before accessing it.

[0]

KASAN: null-ptr-deref in range [0x0000000000000020-0x0000000000000027]
RIP: 0010:__ip6_del_rt_siblings+0x31e/0x7c0 net/ipv6/route.c:4056
Call Trace:
 <TASK>
 ip6_route_del+0x1054/0x1110 net/ipv6/route.c:4232
 inet6_rtm_delroute+0x5d7/0x6d0 net/ipv6/route.c:5669
 rtnetlink_rcv_msg+0x802/0xc00 net/core/rtnetlink.c:7132
 netlink_rcv_skb+0x226/0x4a0 net/netlink/af_netlink.c:2556
 netlink_unicast_kernel net/netlink/af_netlink.c:1319 [inline]
 netlink_unicast+0x7f5/0x990 net/netlink/af_netlink.c:1345
 netlink_sendmsg+0x813/0xb40 net/netlink/af_netlink.c:1900
 sock_sendmsg_nosec+0x13a/0x180 net/socket.c:800
 __sock_sendmsg net/socket.c:815 [inline]
 ____sys_sendmsg+0x565/0x870 net/socket.c:2713
 ___sys_sendmsg+0x2a5/0x360 net/socket.c:2767
 __sys_sendmsg net/socket.c:2799 [inline]
 __do_sys_sendmsg net/socket.c:2804 [inline]
 __se_sys_sendmsg net/socket.c:2802 [inline]
 __x64_sys_sendmsg+0x1b7/0x290 net/socket.c:2802
 do_syscall_x64 arch/x86/entry/syscall_64.c:61 [inline]
 do_syscall_64+0x166/0x520 arch/x86/entry/syscall_64.c:84
 entry_SYSCALL_64_after_hwframe+0x77/0x7f
 </TASK>

[1] https://gist.github.com/NamanGulati/0766a1159b6ca61928faaf87425ff899

---
 net/ipv6/route.c | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)
net/ipv6/route.c +6 -3
index dc9060af5bb1e..ee707e48f4efa 100644
@@ -4010,6 +4010,7 @@ static int __ip6_del_rt_siblings(struct fib6_info *rt, struct fib6_config *cfg)
 	struct net *net = info->nl_net;
 	struct sk_buff *skb = NULL;
 	struct fib6_table *table;
+	struct fib6_node *fn;
 	int err = -ENOENT;
 
 	if (rt == net->ipv6.fib6_null_entry)
@@ -4017,9 +4018,13 @@ static int __ip6_del_rt_siblings(struct fib6_info *rt, struct fib6_config *cfg)
 	table = rt->fib6_table;
 	spin_lock_bh(&table->tb6_lock);
 
+	fn = rcu_dereference_protected(rt->fib6_node,
+				       lockdep_is_held(&table->tb6_lock));
+	if (!fn)
+		goto out_unlock;
+
 	if (rt->fib6_nsiblings && cfg->fc_delete_all_nh) {
 		struct fib6_info *sibling, *next_sibling;
-		struct fib6_node *fn;
 
 		/* prefer to send a single notification with all hops */
 		skb = nlmsg_new(rt6_nlmsg_size(rt), GFP_ATOMIC);
@@ -4041,8 +4046,6 @@ static int __ip6_del_rt_siblings(struct fib6_info *rt, struct fib6_config *cfg)
 		 * and emit a replace or delete notification, respectively.
 		 */
 		info->skip_notify_kernel = 1;
-		fn = rcu_dereference_protected(rt->fib6_node,
-					    lockdep_is_held(&table->tb6_lock));
 		if (rcu_access_pointer(fn->leaf) == rt) {
 			struct fib6_info *last_sibling, *replace_rt;
 
Signature · 1 line
2.53.0
author maintainer
#385 lore raw

PATCH 385/733 net: macb: destroy the phylink instance on the probe error path

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Nicolai Buchwitz <[email protected]>

[ Upstream commit 7d059f390750152b9bd69df934198651b94fc26d ]

macb_mii_init() creates a phylink instance on both of its success paths, but the probe unwind frees the netdev without destroying it, so a failing macb_alloc_tieoff() or register_netdev() leaks the instance.

Destroy it at err_out_unregister_mdio, which is only reachable once macb_mii_init() has succeeded, so bp->phylink is valid there.

---
 drivers/net/ethernet/cadence/macb_main.c | 1 +
 1 file changed, 1 insertion(+)
drivers/net/ethernet/cadence/macb_main.c +1 -0
index 55ed40a0a6062..404aebe01ed01 100644
@@ -5968,6 +5968,7 @@ static int macb_probe(struct platform_device *pdev)
 		mdiobus_unregister(bp->mii_bus);
 		mdiobus_free(bp->mii_bus);
 	}
+	phylink_destroy(bp->phylink);
 
 err_out_phy_exit:
 	phy_exit(bp->phy);
Signature · 1 line
2.53.0
author maintainer
#386 lore raw

PATCH 386/733 net: macb: put the "mdio" child node reference on success

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Nicolai Buchwitz <[email protected]>

[ Upstream commit 382a373d9ea7a6ac4de9c022385b6217f65ae3cc ]

macb_mii_init() holds the reference returned by of_get_child_by_name() for macb_mdiobus_register() and drops it only on the error paths, so every successful probe leaks a node reference. On a CM5, overlay removal after four bind cycles reports

OF: ERROR: memory leak, expected refcount 1 instead of 5

Drop the reference after registration, where __mdiobus_register() has already taken its own for the lifetime of the bus.

Fixes 8a6631f1cece ("net: macb: avoid redundant lookup for "mdio" child node in MDIO setup")
---
 drivers/net/ethernet/cadence/macb_main.c | 2 ++
 1 file changed, 2 insertions(+)
drivers/net/ethernet/cadence/macb_main.c +2 -0
index 404aebe01ed01..9a59dd0df61a9 100644
@@ -1164,6 +1164,8 @@ static int macb_mii_init(struct macb *bp)
 	if (err)
 		goto err_out_unregister_bus;
 
+	of_node_put(mdio_np);
+
 	return 0;
 
 err_out_unregister_bus:
Signature · 1 line
2.53.0
author maintainer
#387 lore raw

PATCH 387/733 nstree: check listing permission before taking a namespace reference

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Norbert Szetei <[email protected]>

[ Upstream commit 56ea4e86832d8abe8930394473566c194d189f85 ]

legitimize_ns() takes a reference on the candidate namespace before may_list_ns() has decided whether the caller may see it. The __free(ns_put) cleanup on the denied path can drop the last reference to a mount namespace while we still hold the rcu read lock, and put_mnt_ns() may sleep there. This is the same problem commit 2ec2aff3c8e2 ("ns: make sure reference are dropped outside of rcu lock") fixed for the put_user() path. Neither ns_requested() nor may_list_ns() needs a reference, both only look at the namespace type and at the caller's own namespaces, so do the checks first and take the reference last.

Splat:

Voluntary context switch within RCU read-side critical section!

  WARNING: kernel/rcu/tree_plugin.h:332 at rcu_note_context_switch+0x238/0x2a0, CPU#5: a/3442
  CPU: 5 UID: 1000 PID: 3442 Comm: a Not tainted 7.0.0-30-generic #30-Ubuntu PREEMPT(lazy)
  RIP: 0010:rcu_note_context_switch+0x238/0x2a0
  Call Trace:
   <TASK>
   __schedule+0xcf/0x650
   schedule+0x27/0x90
   schedule_preempt_disabled+0x15/0x30
   __mutex_lock.constprop.0+0x550/0xaf0
   __mutex_lock_slowpath+0x13/0x20
   mutex_lock+0x3b/0x50
   exp_funnel_lock+0xb2/0x260
   synchronize_rcu_expedited+0xe7/0x220
   namespace_unlock+0x26a/0x320
   put_mnt_ns+0xd3/0x120
   mntns_put+0xe/0x20
   do_listns+0x13e/0x560
   __do_sys_listns+0x126/0x2d0
   __x64_sys_listns+0x20/0x30
   x64_sys_call+0x2366/0x2390
   do_syscall_64+0x105/0x5a0
   entry_SYSCALL_64_after_hwframe+0x76/0x7e
   </TASK>
---
 kernel/nstree.c | 10 ++--------
 1 file changed, 2 insertions(+), 8 deletions(-)
kernel/nstree.c +2 -8
index 6d12e5900ac01..831f279d174a3 100644
@@ -533,19 +533,13 @@ DEFINE_FREE(ns_put, struct ns_common *, if (!IS_ERR_OR_NULL(_T)) ns_put(_T))
 static inline struct ns_common *__must_check legitimize_ns(const struct klistns *kls,
 							   struct ns_common *candidate)
 {
-	struct ns_common *ns __free(ns_put) = NULL;
-
 	if (!ns_requested(kls, candidate))
 		return NULL;
 
-	ns = ns_get_unless_inactive(candidate);
-	if (!ns)
-		return NULL;
-
-	if (!may_list_ns(kls, ns))
+	if (!may_list_ns(kls, candidate))
 		return NULL;
 
-	return no_free_ptr(ns);
+	return ns_get_unless_inactive(candidate);
 }
 
 static ssize_t do_listns_userns(struct klistns *kls)
Signature · 1 line
2.53.0
author maintainer
#388 lore raw

PATCH 388/733 drm/xe: Guard page-fault worker with runtime PM check

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Varun Gupta <[email protected]>

[ Upstream commit 20fce5b34b21a995839743b4917a1edd2fd503ba ]

During VM teardown, the VM's runtime PM reference is dropped asynchronously, allowing the device to autosuspend while stale page faults belonging to the now-dead VM are still queued. When the page-fault worker later tries to ack one of these, it calls into guc_ct_send_locked() on an already-suspended device, tripping:

  Assertion !xe_pm_runtime_suspended(xe) failed!
  WARNING at xe_device.c:1267 xe_device_assert_mem_access+0x11c/0x140 [xe]

A live VM/exec queue always holds a PM reference while it has outstanding work, so if the device is suspended at ack time, the owning context is already gone and the fault is stale.

Take a runtime PM reference across the entire pagefault queue worker to safely deliver acks for torn-down VMs.

v3:
- Move PM ref to the generic xe_pagefault_queue_work using guard(xe_pm_runtime)(xe) instead of tracking it in the GuC backend(Matt Brost).

v2:
- Hold PM ref across the entire batch (begin/end) instead of per-ack. This prevents the device from autosuspending mid-batch, which would leave write_only acks written but the end flush skipped, and skip counter++, desyncing the cadence check.(Himal)
- Add a comment explaining stale faults.(Himal)

Fixes f289f7807119 ("drm/xe: Add xe_guc_pagefault layer")
(cherry picked from commit fcc2431d2213dc4d04250c4f1ae87d9c3ae0d455)
[Rodrigo: Added xe_device struct for compatibility while cherry-picking]
---
 drivers/gpu/drm/xe/xe_pagefault.c       | 10 ++++++++++
 drivers/gpu/drm/xe/xe_pagefault_types.h |  3 +++
 2 files changed, 13 insertions(+)
drivers/gpu/drm/xe/xe_pagefault.c +10 -0
index dbf8f71d3328a..a4986df8328d6 100644
@@ -16,6 +16,7 @@
 #include "xe_hw_engine.h"
 #include "xe_pagefault.h"
 #include "xe_pagefault_types.h"
+#include "xe_pm.h"
 #include "xe_svm.h"
 #include "xe_trace_bo.h"
 #include "xe_vm.h"
@@ -292,9 +293,17 @@ static void xe_pagefault_queue_work(struct work_struct *w)
 {
 	struct xe_pagefault_queue *pf_queue =
 		container_of(w, typeof(*pf_queue), worker);
+	struct xe_device *xe = pf_queue->xe;
 	struct xe_pagefault pf;
 	unsigned long threshold;
 
+	/*
+	 * A live VM holds a PM reference, but a torn-down VM does not.
+	 * Guard the entire worker loop to safely drain stale faults and
+	 * prevent autosuspends from desyncing batched CT flushes.
+	 */
+	guard(xe_pm_runtime)(xe);
+
 #define USM_QUEUE_MAX_RUNTIME_MS      20
 	threshold = jiffies + msecs_to_jiffies(USM_QUEUE_MAX_RUNTIME_MS);
 
@@ -365,6 +374,7 @@ static int xe_pagefault_queue_init(struct xe_device *xe,
 	drm_dbg(&xe->drm, "xe_pagefault_entry_size=%d, total_num_eus=%d, pf_queue->size=%u",
 		xe_pagefault_entry_size(), total_num_eus, pf_queue->size);
 
+	pf_queue->xe = xe;
 	spin_lock_init(&pf_queue->lock);
 	INIT_WORK(&pf_queue->worker, xe_pagefault_queue_work);
 
drivers/gpu/drm/xe/xe_pagefault_types.h +3 -0
index c4ee625b93ddd..f63a12aa0d4f7 100644
@@ -8,6 +8,7 @@
 
 #include <linux/workqueue.h>
 
+struct xe_device;
 struct xe_gt;
 struct xe_pagefault;
 
@@ -118,6 +119,8 @@ struct xe_pagefault {
  * queue to absorb the device’s worst-case number of outstanding faults.
  */
 struct xe_pagefault_queue {
+	/** @xe: Back-pointer to the Xe device */
+	struct xe_device *xe;
 	/**
 	 * @data: Data in queue containing struct xe_pagefault, protected by
 	 * @lock
Signature · 1 line
2.53.0
author maintainer
#389 lore raw

PATCH 389/733 mptcp: remove unneeded READ_ONCE() annotation

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Paolo Abeni <[email protected]>

[ Upstream commit caa4a79f74f32084ce28aee8653bc04df745970d ]

The subflow->fully_established flag is always written under the subflow socket lock. Reading such value under the same lock does not require any ONCE annotation.

---
 net/mptcp/options.c  | 4 ++--
 net/mptcp/protocol.c | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)
net/mptcp/options.c +2 -2
index 1057d500577b0..6e208b4e024a6 100644
@@ -529,7 +529,7 @@ static bool mptcp_established_options_mp(struct sock *sk, struct sk_buff *skb,
 		return false;
 
 	/* MPC/MPJ needed only on 3rd ack packet, DATA_FIN and TCP shutdown take precedence */
-	if (READ_ONCE(subflow->fully_established) || snd_data_fin_enable ||
+	if (subflow->fully_established || snd_data_fin_enable ||
 	    subflow->snd_isn != TCP_SKB_CB(skb)->seq ||
 	    sk->sk_state != TCP_ESTABLISHED)
 		return false;
@@ -980,7 +980,7 @@ static bool check_fully_established(struct mptcp_sock *msk, struct sock *ssk,
 	/* here we can process OoO, in-window pkts, only in-sequence 4th ack
 	 * will make the subflow fully established
 	 */
-	if (likely(READ_ONCE(subflow->fully_established))) {
+	if (likely(subflow->fully_established)) {
 		/* on passive sockets, check for 3rd ack retransmission
 		 * note that msk is always set by subflow_syn_recv_sock()
 		 * for mp_join subflows
net/mptcp/protocol.c +1 -1
index 8adf699894e32..5c6923fdcad08 100644
@@ -3743,7 +3743,7 @@ static void schedule_3rdack_retransmission(struct sock *ssk)
 	struct tcp_sock *tp = tcp_sk(ssk);
 	unsigned long timeout;
 
-	if (READ_ONCE(mptcp_subflow_ctx(ssk)->fully_established))
+	if (mptcp_subflow_ctx(ssk)->fully_established)
 		return;
 
 	/* reschedule with a timeout above RTT, as we must look only for drop */
Signature · 1 line
2.53.0
author maintainer
#390 lore raw

PATCH 390/733 watchdog: fix hrtimer start when pretimeout is zero

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: David Arcari <[email protected]>

[ Upstream commit 0fa37512eb747e4ffdcf367274f9e72845f1bca4 ]

Per the watchdog API, a pretimeout value of 0 disables the feature. However, watchdog_hrtimer_pretimeout_start() fails to verify if the pretimeout is non-zero before arming the timer.

This omission inadvertently starts the software pretimeout timer, which could result in the pretimeout handler executing incorrectly when the watchdog timeout is reached.

Fix this by adding a check for wdd->pretimeout before calling hrtimer_start(), ensuring the disabled state is respected.

Fixes 7b7d2fdc8c3e ("watchdog: Add hrtimer-based pretimeout feature")
---
 drivers/watchdog/watchdog_hrtimer_pretimeout.c | 1 +
 1 file changed, 1 insertion(+)
drivers/watchdog/watchdog_hrtimer_pretimeout.c +1 -0
index fbc7eecd8b203..49a05ea60c979 100644
@@ -30,6 +30,7 @@ void watchdog_hrtimer_pretimeout_init(struct watchdog_device *wdd)
 void watchdog_hrtimer_pretimeout_start(struct watchdog_device *wdd)
 {
 	if (!(wdd->info->options & WDIOF_PRETIMEOUT) &&
+	    wdd->pretimeout &&
 	    !watchdog_pretimeout_invalid(wdd, wdd->pretimeout))
 		hrtimer_start(&wdd->wd_data->pretimeout_timer,
 			      ktime_set(wdd->timeout - wdd->pretimeout, 0),
Signature · 1 line
2.53.0
author maintainer
#391 lore raw

PATCH 391/733 watchdog: msc313e: Avoid division by zero

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Tzung-Bi Shih <[email protected]>

[ Upstream commit 3c73a37f5e40972ce26d8eeb98e8b938d719b069 ]

clk_get_rate() could return 0. Avoid a division by zero panic.

Fixes e9800b799464 ("watchdog: Add Mstar MSC313e WDT driver")
---
 drivers/watchdog/msc313e_wdt.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)
drivers/watchdog/msc313e_wdt.c +5 -1
index f69d66971c41d..c3018b9701641 100644
@@ -97,6 +97,7 @@ static int msc313e_wdt_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
 	struct msc313e_wdt_priv *priv;
+	unsigned long rate;
 
 	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
 	if (!priv)
@@ -116,7 +117,10 @@ static int msc313e_wdt_probe(struct platform_device *pdev)
 	priv->wdev.ops = &msc313e_wdt_ops,
 	priv->wdev.parent = dev;
 	priv->wdev.min_timeout = MSC313E_WDT_MIN_TIMEOUT;
-	priv->wdev.max_timeout = U32_MAX / clk_get_rate(priv->clk);
+	rate = clk_get_rate(priv->clk);
+	if (!rate)
+		return -EINVAL;
+	priv->wdev.max_timeout = U32_MAX / rate;
 	priv->wdev.timeout = MSC313E_WDT_DEFAULT_TIMEOUT;
 
 	/* If the period is non-zero the WDT is running */
Signature · 1 line
2.53.0
author maintainer
#392 lore raw

PATCH 392/733 watchdog: msc313e: Fix clock leak and spurious timer in settimeout()

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Tzung-Bi Shih <[email protected]>

[ Upstream commit 3db30f315935c2fb0d95f46b7a593b5b4d3ec3d0 ]

msc313e_wdt_settimeout() unconditionally calls msc313e_wdt_start() which introduces two severe bugs:

1. If the watchdog is already active, calling start() again will increase the reference count of the clock again. However stop() is only called once, the reference count is unbalance.
2. If the watchdog is stopped, calling settimeout() will start the hardware timer accidentally.

Factor out the register-writing logic into a helper function. Only call it in settimeout() if the watchdog is running. Otherwise, simply update wdev->timeout.

Fixes e9800b799464 ("watchdog: Add Mstar MSC313e WDT driver")
---
 drivers/watchdog/msc313e_wdt.c | 22 ++++++++++++++++------
 1 file changed, 16 insertions(+), 6 deletions(-)
drivers/watchdog/msc313e_wdt.c +16 -6
index c3018b9701641..8ce24df8e338b 100644
@@ -31,20 +31,26 @@ struct msc313e_wdt_priv {
 	struct clk *clk;
 };
 
+static void msc313e_wdt_set_hw_timeout(struct msc313e_wdt_priv *priv,
+				       unsigned int timeout)
+{
+	u32 t = timeout * clk_get_rate(priv->clk);
+
+	writew(t & 0xffff, priv->base + REG_WDT_MAX_PRD_L);
+	writew((t >> 16) & 0xffff, priv->base + REG_WDT_MAX_PRD_H);
+	writew(1, priv->base + REG_WDT_CLR);
+}
+
 static int msc313e_wdt_start(struct watchdog_device *wdev)
 {
 	struct msc313e_wdt_priv *priv = watchdog_get_drvdata(wdev);
-	u32 timeout;
 	int err;
 
 	err = clk_prepare_enable(priv->clk);
 	if (err)
 		return err;
 
-	timeout = wdev->timeout * clk_get_rate(priv->clk);
-	writew(timeout & 0xffff, priv->base + REG_WDT_MAX_PRD_L);
-	writew((timeout >> 16) & 0xffff, priv->base + REG_WDT_MAX_PRD_H);
-	writew(1, priv->base + REG_WDT_CLR);
+	msc313e_wdt_set_hw_timeout(priv, wdev->timeout);
 	return 0;
 }
 
@@ -69,9 +75,13 @@ static int msc313e_wdt_stop(struct watchdog_device *wdev)
 
 static int msc313e_wdt_settimeout(struct watchdog_device *wdev, unsigned int new_time)
 {
+	struct msc313e_wdt_priv *priv = watchdog_get_drvdata(wdev);
+
 	wdev->timeout = new_time;
 
-	return msc313e_wdt_start(wdev);
+	if (watchdog_hw_running(wdev) || watchdog_active(wdev))
+		msc313e_wdt_set_hw_timeout(priv, wdev->timeout);
+	return 0;
 }
 
 static const struct watchdog_info msc313e_wdt_ident = {
Signature · 1 line
2.53.0
author maintainer
#393 lore raw

PATCH 393/733 watchdog: msc313e: Enable clock before accessing hardware registers

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Tzung-Bi Shih <[email protected]>

[ Upstream commit 3db2df24e7f11fb117718f6abe326628d91bc500 ]

msc313e_wdt_probe() reads from hardware registers without ensuring the required clock is enabled. Furthermore, if the bootloader leaves the watchdog running, msc313e_wdt_probe() sets WDOG_HW_RUNNING without increasing the clock's reference count.

While the clock is currently supplied as a fixed clock by the device tree (xtal_div2 in arch/arm/boot/dts/sigmastar/mstar-v7.dtsi) which masks the physical issue, this still violates the API usage.

Call clk_prepare_enable() before reading WDT registers. If the WDT is running, leave the clock enabled so the CCF reference counter is balanced.

Fixes ffd264bd152c ("watchdog: msc313e: Check if the WDT was running at boot")
---
 drivers/watchdog/msc313e_wdt.c | 23 +++++++++++++++++++++--
 1 file changed, 21 insertions(+), 2 deletions(-)
drivers/watchdog/msc313e_wdt.c +21 -2
index 8ce24df8e338b..7c45935667818 100644
@@ -108,6 +108,7 @@ static int msc313e_wdt_probe(struct platform_device *pdev)
 	struct device *dev = &pdev->dev;
 	struct msc313e_wdt_priv *priv;
 	unsigned long rate;
+	int ret;
 
 	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
 	if (!priv)
@@ -133,9 +134,21 @@ static int msc313e_wdt_probe(struct platform_device *pdev)
 	priv->wdev.max_timeout = U32_MAX / rate;
 	priv->wdev.timeout = MSC313E_WDT_DEFAULT_TIMEOUT;
 
+	ret = clk_prepare_enable(priv->clk);
+	if (ret)
+		return ret;
+
 	/* If the period is non-zero the WDT is running */
-	if (readw(priv->base + REG_WDT_MAX_PRD_L) | (readw(priv->base + REG_WDT_MAX_PRD_H) << 16))
+	if (readw(priv->base + REG_WDT_MAX_PRD_L) | (readw(priv->base + REG_WDT_MAX_PRD_H) << 16)) {
 		set_bit(WDOG_HW_RUNNING, &priv->wdev.status);
+		/*
+		 * Keep the clock enabled. The watchdog core will skip the next
+		 * start() and a future stop() will balance the CCF reference
+		 * count.
+		 */
+	} else {
+		clk_disable_unprepare(priv->clk);
+	}
 
 	watchdog_set_drvdata(&priv->wdev, priv);
 	platform_set_drvdata(pdev, priv);
@@ -144,7 +157,13 @@ static int msc313e_wdt_probe(struct platform_device *pdev)
 	watchdog_stop_on_reboot(&priv->wdev);
 	watchdog_stop_on_unregister(&priv->wdev);
 
-	return devm_watchdog_register_device(dev, &priv->wdev);
+	ret = devm_watchdog_register_device(dev, &priv->wdev);
+
+	/* If the WDT is running and anything goes wrong, disable the clock. */
+	if (ret && test_bit(WDOG_HW_RUNNING, &priv->wdev.status))
+		clk_disable_unprepare(priv->clk);
+
+	return ret;
 }
 
 static int __maybe_unused msc313e_wdt_suspend(struct device *dev)
Signature · 1 line
2.53.0
author maintainer
#394 lore raw

PATCH 394/733 watchdog: msc313e: Fix spurious reset on suspend

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Tzung-Bi Shih <[email protected]>

[ Upstream commit 4f6817c9eff4aa1078e16652d82e4b7ef4ffae3e ]

If the hardware watchdog was started by the bootloader and the device is suspended before userspace opens it, the ping worker (from watchdog core) is frozen and the active hardware timer continues running. This leads to a spurious system reset.

Check both watchdog_active() and watchdog_hw_running() when deciding whether to start or stop the watchdog during suspend and resume.

Additionally, call watchdog_stop_ping_on_suspend() to ensure the ping worker be correctly paused and restarted during suspend and resume.

Fixes ffd264bd152c ("watchdog: msc313e: Check if the WDT was running at boot")
---
 drivers/watchdog/msc313e_wdt.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)
drivers/watchdog/msc313e_wdt.c +3 -2
index 7c45935667818..c7d558fefc860 100644
@@ -156,6 +156,7 @@ static int msc313e_wdt_probe(struct platform_device *pdev)
 	watchdog_init_timeout(&priv->wdev, timeout, dev);
 	watchdog_stop_on_reboot(&priv->wdev);
 	watchdog_stop_on_unregister(&priv->wdev);
+	watchdog_stop_ping_on_suspend(&priv->wdev);
 
 	ret = devm_watchdog_register_device(dev, &priv->wdev);
 
@@ -170,7 +171,7 @@ static int __maybe_unused msc313e_wdt_suspend(struct device *dev)
 {
 	struct msc313e_wdt_priv *priv = dev_get_drvdata(dev);
 
-	if (watchdog_active(&priv->wdev))
+	if (watchdog_active(&priv->wdev) || watchdog_hw_running(&priv->wdev))
 		msc313e_wdt_stop(&priv->wdev);
 
 	return 0;
@@ -180,7 +181,7 @@ static int __maybe_unused msc313e_wdt_resume(struct device *dev)
 {
 	struct msc313e_wdt_priv *priv = dev_get_drvdata(dev);
 
-	if (watchdog_active(&priv->wdev))
+	if (watchdog_active(&priv->wdev) || watchdog_hw_running(&priv->wdev))
 		msc313e_wdt_start(&priv->wdev);
 
 	return 0;
Signature · 1 line
2.53.0
author maintainer
#395 lore raw

PATCH 395/733 watchdog: msc313e: Fix undefined behavior

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Tzung-Bi Shih <[email protected]>

[ Upstream commit ab390021b2a3bb4cc875f28a6f76d13de90d7457 ]

readw() returns a u16. Left shifting a u16 by 16 bits yields undefined behavior.

Cast to u32 explicitly before the shift.

Fixes ffd264bd152c ("watchdog: msc313e: Check if the WDT was running at boot")
---
 drivers/watchdog/msc313e_wdt.c | 12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)
drivers/watchdog/msc313e_wdt.c +11 -1
index c7d558fefc860..e28261c7a8d4c 100644
@@ -31,6 +31,16 @@ struct msc313e_wdt_priv {
 	struct clk *clk;
 };
 
+static u32 msc313e_wdt_get_hw_timeout(struct msc313e_wdt_priv *priv)
+{
+	u16 low, high;
+
+	low = readw(priv->base + REG_WDT_MAX_PRD_L);
+	high = readw(priv->base + REG_WDT_MAX_PRD_H);
+
+	return ((u32)high << 16) | low;
+}
+
 static void msc313e_wdt_set_hw_timeout(struct msc313e_wdt_priv *priv,
 				       unsigned int timeout)
 {
@@ -139,7 +149,7 @@ static int msc313e_wdt_probe(struct platform_device *pdev)
 		return ret;
 
 	/* If the period is non-zero the WDT is running */
-	if (readw(priv->base + REG_WDT_MAX_PRD_L) | (readw(priv->base + REG_WDT_MAX_PRD_H) << 16)) {
+	if (msc313e_wdt_get_hw_timeout(priv)) {
 		set_bit(WDOG_HW_RUNNING, &priv->wdev.status);
 		/*
 		 * Keep the clock enabled. The watchdog core will skip the next
Signature · 1 line
2.53.0
author maintainer
#396 lore raw

PATCH 396/733 watchdog: msc313e: Sync timeout value if WDT was running at boot

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Tzung-Bi Shih <[email protected]>

[ Upstream commit 01504d14e47b34779911250dd308a03f6ef681c2 ]

If WDT was running at boot, the hardware timeout might be set to values other than the final software timeout.

To be consistent, set the hardware timeout to match the final software timeout (i.e., after watchdog_init_timeout()) if WDT was running.

Fixes ffd264bd152c ("watchdog: msc313e: Check if the WDT was running at boot")
---
 drivers/watchdog/msc313e_wdt.c | 17 +++++++++--------
 1 file changed, 9 insertions(+), 8 deletions(-)
drivers/watchdog/msc313e_wdt.c +9 -8
index e28261c7a8d4c..4a5cce2a16b18 100644
@@ -144,12 +144,21 @@ static int msc313e_wdt_probe(struct platform_device *pdev)
 	priv->wdev.max_timeout = U32_MAX / rate;
 	priv->wdev.timeout = MSC313E_WDT_DEFAULT_TIMEOUT;
 
+	watchdog_set_drvdata(&priv->wdev, priv);
+	platform_set_drvdata(pdev, priv);
+
+	watchdog_init_timeout(&priv->wdev, timeout, dev);
+	watchdog_stop_on_reboot(&priv->wdev);
+	watchdog_stop_on_unregister(&priv->wdev);
+	watchdog_stop_ping_on_suspend(&priv->wdev);
+
 	ret = clk_prepare_enable(priv->clk);
 	if (ret)
 		return ret;
 
 	/* If the period is non-zero the WDT is running */
 	if (msc313e_wdt_get_hw_timeout(priv)) {
+		msc313e_wdt_set_hw_timeout(priv, priv->wdev.timeout);
 		set_bit(WDOG_HW_RUNNING, &priv->wdev.status);
 		/*
 		 * Keep the clock enabled. The watchdog core will skip the next
@@ -160,14 +169,6 @@ static int msc313e_wdt_probe(struct platform_device *pdev)
 		clk_disable_unprepare(priv->clk);
 	}
 
-	watchdog_set_drvdata(&priv->wdev, priv);
-	platform_set_drvdata(pdev, priv);
-
-	watchdog_init_timeout(&priv->wdev, timeout, dev);
-	watchdog_stop_on_reboot(&priv->wdev);
-	watchdog_stop_on_unregister(&priv->wdev);
-	watchdog_stop_ping_on_suspend(&priv->wdev);
-
 	ret = devm_watchdog_register_device(dev, &priv->wdev);
 
 	/* If the WDT is running and anything goes wrong, disable the clock. */
Signature · 1 line
2.53.0
author maintainer
#397 lore raw

PATCH 397/733 net: dsa: lantiq_gswip: fix GSWIP_MDIO_PHY_FCONTX_EN value

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Jan Havran (Advantech Czech) <[email protected]>

[ Upstream commit 59fb389ad6bf50916189e56dafcd225ab977f874 ]

Per the GSW145 data sheet, the FCONTX (bits 8:7) and FCONRX (bits 6:5) flow-control fields of the PHY_ADDR_n register both encode 00 = AUTO, 01 = EN, 10 = reserved, 11 = DIS. GSWIP_MDIO_PHY_FCONTX_EN was 0x0100, i.e. field value 10 (the reserved encoding), instead of 0x0080 (01 = EN); FCONRX_EN is already 0x0020 (01). Enabling tx flow control therefore wrote the reserved value.

Set FCONTX_EN to 0x0080. The register is shared by all supported parts.

---
 drivers/net/dsa/lantiq/lantiq_gswip.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/dsa/lantiq/lantiq_gswip.h +1 -1
index bc3686faad0d6..0b75be14dc109 100644
@@ -42,7 +42,7 @@
 #define  GSWIP_MDIO_PHY_FDUP_DIS	0x0600
 #define  GSWIP_MDIO_PHY_FCONTX_MASK	0x0180
 #define  GSWIP_MDIO_PHY_FCONTX_AUTO	0x0000
-#define  GSWIP_MDIO_PHY_FCONTX_EN	0x0100
+#define  GSWIP_MDIO_PHY_FCONTX_EN	0x0080
 #define  GSWIP_MDIO_PHY_FCONTX_DIS	0x0180
 #define  GSWIP_MDIO_PHY_FCONRX_MASK	0x0060
 #define  GSWIP_MDIO_PHY_FCONRX_AUTO	0x0000
Signature · 1 line
2.53.0
author maintainer
#398 lore raw

PATCH 398/733 net: ks8851: Fix receiver error in 100BASE-TX mode following software power-down

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Marek Vasut <[email protected]>

[ Upstream commit 66ef5adb75446627f8b6c26cd04f2adc86d4de56 ]

KSZ8851 errata sheet DS80000716D-page 4 Module 3 [1] states that, when issuing a software power-down (PMECR[1:0] = 10) followed by a power-on (PMECR[1:0] = 00), the receiver circuit can fail to start properly preventing communication. The Transmitter will still send data, but no data will be received.

The errata sheet also includes a workaround, which states that, it is recommended that the software power-down feature not be used.

Implement that workaround and drop the entry into software power-down mode. The ks8851_write_mac_addr() calls entry into normal power-on mode at the very beginning of the function, therefore dropping the second call to enter software power-down mode is sufficient here. The ks8851_net_stop() can only be called after ks8851_net_start() was already called, and ks8851_net_start() also makes the MAC enter normal power-on mode, therefore it is also fine to drop the call to enter software power-down mode from ks8851_net_stop().

This will lead to a slight increase in power consumption, but it also fixes a sporadic reliability problem on at least KSZ8851-16MLL, which is where the problem was reported and this fix was tested.

[1] https://ww1.microchip.com/downloads/en/DeviceDoc/80000716D.pdf

---
 drivers/net/ethernet/micrel/ks8851_common.c | 8 +-------
 1 file changed, 1 insertion(+), 7 deletions(-)
drivers/net/ethernet/micrel/ks8851_common.c +1 -7
index 4afbb40bc0e4a..d49f281c78676 100644
@@ -143,9 +143,6 @@ static int ks8851_write_mac_addr(struct net_device *dev)
 		ks8851_wrreg16(ks, KS_MAR(i), val);
 	}
 
-	if (!netif_running(dev))
-		ks8851_set_powermode(ks, PMECR_PM_SOFTDOWN);
-
 	ks8851_unlock(ks);
 
 	return 0;
@@ -478,8 +475,7 @@ static int ks8851_net_open(struct net_device *dev)
  * @dev: The device being closed.
  *
  * Called to close down a network device which has been active. Cancel any
- * work, shutdown the RX and TX process and then place the chip into a low
- * power state whilst it is not being used.
+ * work and shutdown the RX and TX process.
  */
 static int ks8851_net_stop(struct net_device *dev)
 {
@@ -506,8 +502,6 @@ static int ks8851_net_stop(struct net_device *dev)
 	/* shutdown TX process */
 	ks8851_wrreg16(ks, KS_TXCR, 0x0000);
 
-	/* set powermode to soft power down to save power */
-	ks8851_set_powermode(ks, PMECR_PM_SOFTDOWN);
 	ks8851_unlock(ks);
 
 	/* ensure any queued tx buffers are dumped */
Signature · 1 line
2.53.0
author maintainer
#399 lore raw

PATCH 399/733 hwmon: (corsair-cpro) Create debugfs entries after hwmon registration

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Linmao Li <[email protected]>

[ Upstream commit 508baf1713f32f287bfb4f85d759403ec8ba35a3 ]

ccp_debugfs_init() registers debugfs files whose private data is the devm allocated ccp. It runs before hwmon_device_register_with_info(), so when that registration fails, ccp_probe() returns with the files still in place. The HID core then frees ccp, and ccp_remove() is not called for a failed probe, so nothing removes them later either. Reading one of the files dereferences the freed pointer.

Create the debugfs entries only after the hwmon device has been registered, so no failing path can leave them behind.

The two version queries stay where they are. They send USB commands without holding ccp->mutex, which is only safe as long as nothing else can call send_usb_cmd(); once the hwmon device is registered its callbacks can do so concurrently. Only the debugfs creation moves, and it is told which queries succeeded.

---
 drivers/hwmon/corsair-cpro.c | 20 +++++++++++++-------
 1 file changed, 13 insertions(+), 7 deletions(-)
drivers/hwmon/corsair-cpro.c +13 -7
index 8354a002f4c5e..56de0fe0f544c 100644
@@ -566,21 +566,18 @@ static int bootloader_show(struct seq_file *seqf, void *unused)
 }
 DEFINE_SHOW_ATTRIBUTE(bootloader);
 
-static void ccp_debugfs_init(struct ccp_device *ccp)
+static void ccp_debugfs_init(struct ccp_device *ccp, bool fw_valid, bool bl_valid)
 {
 	char name[32];
-	int ret;
 
 	scnprintf(name, sizeof(name), "corsaircpro-%s", dev_name(&ccp->hdev->dev));
 	ccp->debugfs = debugfs_create_dir(name, NULL);
 
-	ret = get_fw_version(ccp);
-	if (!ret)
+	if (fw_valid)
 		debugfs_create_file("firmware_version", 0444,
 				    ccp->debugfs, ccp, &firmware_fops);
 
-	ret = get_bl_version(ccp);
-	if (!ret)
+	if (bl_valid)
 		debugfs_create_file("bootloader_version", 0444,
 				    ccp->debugfs, ccp, &bootloader_fops);
 }
@@ -588,6 +585,7 @@ static void ccp_debugfs_init(struct ccp_device *ccp)
 static int ccp_probe(struct hid_device *hdev, const struct hid_device_id *id)
 {
 	struct ccp_device *ccp;
+	bool fw_valid, bl_valid;
 	int ret;
 
 	ccp = devm_kzalloc(&hdev->dev, sizeof(*ccp), GFP_KERNEL);
@@ -632,7 +630,13 @@ static int ccp_probe(struct hid_device *hdev, const struct hid_device_id *id)
 	if (ret)
 		goto out_hw_close;
 
-	ccp_debugfs_init(ccp);
+	/*
+	 * Query the versions before registering the hwmon device: they send
+	 * USB commands without holding ccp->mutex, which is only safe while
+	 * nothing else can call send_usb_cmd().
+	 */
+	fw_valid = !get_fw_version(ccp);
+	bl_valid = !get_bl_version(ccp);
 
 	ccp->hwmon_dev = hwmon_device_register_with_info(&hdev->dev, "corsaircpro",
 							 ccp, &ccp_chip_info, NULL);
@@ -641,6 +645,8 @@ static int ccp_probe(struct hid_device *hdev, const struct hid_device_id *id)
 		goto out_hw_close;
 	}
 
+	ccp_debugfs_init(ccp, fw_valid, bl_valid);
+
 	return 0;
 
 out_hw_close:
Signature · 1 line
2.53.0
author maintainer
#400 lore raw

PATCH 400/733 hwmon: (gpio-fan) take fan_data->lock in gpio_fan_shutdown()

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Cong Nguyen <[email protected]>

[ Upstream commit bb2424c3502cc72292eedade46960c331d5f28fb ]

set_fan_speed() writes the control GPIOs one bit at a time. Every other caller locks around it; gpio_fan_shutdown() doesn't. If it races a locked caller, the GPIO writes can interleave and leave the fan at a speed neither caller asked for.

Fixes b95579cd8795 ("hwmon: (gpio-fan) Add a shutdown handler to poweroff the fans")
Assisted-by Claude:claude-opus-4
---
 drivers/hwmon/gpio-fan.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)
drivers/hwmon/gpio-fan.c +4 -1
index 084828e1e2817..10c91bc33b020 100644
@@ -606,8 +606,11 @@ static void gpio_fan_shutdown(struct platform_device *pdev)
 {
 	struct gpio_fan_data *fan_data = platform_get_drvdata(pdev);
 
-	if (fan_data->gpios)
+	if (fan_data->gpios) {
+		mutex_lock(&fan_data->lock);
 		set_fan_speed(fan_data, 0);
+		mutex_unlock(&fan_data->lock);
+	}
 }
 
 static int gpio_fan_runtime_suspend(struct device *dev)
Signature · 1 line
2.53.0
author maintainer
#401 lore raw

PATCH 401/733 hwmon: (aspeed-pwm-tacho) Propagate reset deassert errors

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Pengpeng Hou <[email protected]>

[ Upstream commit 09a9e1746a87845d7d8e2b4e23bb613306effdff ]

aspeed_pwm_tacho_probe() installs its reset cleanup action and configures the
controller after an unchecked reset deassertion.

Stop probing when the reset controller rejects the transition, before the hwmon device becomes visible.

Fixes 18c514cc0e02 ("hwmon: (aspeed-pwm-tacho) Deassert reset in probe")
---
 drivers/hwmon/aspeed-pwm-tacho.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/hwmon/aspeed-pwm-tacho.c +3 -1
index 1c5945d4ba377..bfce589c3fb1f 100644
@@ -934,7 +934,9 @@ static int aspeed_pwm_tacho_probe(struct platform_device *pdev)
 			"missing or invalid reset controller device tree entry");
 		return PTR_ERR(priv->rst);
 	}
-	reset_control_deassert(priv->rst);
+	ret = reset_control_deassert(priv->rst);
+	if (ret)
+		return ret;
 
 	ret = devm_add_action_or_reset(dev, aspeed_pwm_tacho_remove, priv);
 	if (ret)
Signature · 1 line
2.53.0
author maintainer
#402 lore raw

PATCH 402/733 hwmon: (corsair-cpro) Remove debugfs entries when probe fails

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Linmao Li <[email protected]>

[ Upstream commit 4ee875c423c66c45d7ef7bbff403cd0e3971e0a2 ]

ccp_debugfs_init() registers debugfs files whose private data is the devm allocated ccp. If hwmon_device_register_with_info() fails right after it, ccp_probe() returns without removing them: the HID core then frees ccp, and ccp_remove() is not called for a failed probe, so the files stay behind. Reading one of them dereferences the freed pointer.

Remove the debugfs entries on that error path. debugfs_remove_recursive() waits for readers already inside the show callbacks, so ccp is no longer reachable through debugfs by the time probe returns.

---
 drivers/hwmon/corsair-cpro.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/hwmon/corsair-cpro.c +3 -1
index 56de0fe0f544c..c096451526132 100644
@@ -642,13 +642,15 @@ static int ccp_probe(struct hid_device *hdev, const struct hid_device_id *id)
 							 ccp, &ccp_chip_info, NULL);
 	if (IS_ERR(ccp->hwmon_dev)) {
 		ret = PTR_ERR(ccp->hwmon_dev);
-		goto out_hw_close;
+		goto out_debugfs_remove;
 	}
 
 	ccp_debugfs_init(ccp, fw_valid, bl_valid);
 
 	return 0;
 
+out_debugfs_remove:
+	debugfs_remove_recursive(ccp->debugfs);
 out_hw_close:
 	hid_hw_close(hdev);
 	hid_device_io_stop(hdev);
Signature · 1 line
2.53.0
author maintainer
#403 lore raw

PATCH 403/733 hwmon: (nct6694) do not expose enable on DTIN temperature channels

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Ali Ahmet Memis <[email protected]>

[ Upstream commit c88a6338ae485e4d6210cc74cdb7664d6476c925 ]

The driver registers 26 temperature channels, all advertising HWMON_T_ENABLE, and indexes the enable bitmap with the raw channel:

	data->hwmon_en.tin_en[channel / 8] |= BIT(channel % 8);

tin_en is two bytes and only covers the 5 THR and 5 TDP channels (index 0-9). The 16 DTIN channels (index 10-25) are enabled by the firmware and were never meant to carry an enable bit. Because the control structure is packed, writing temp17_enable and above indexes past tin_en into the fin_en bytes that follow it, so it toggles fan enable state instead; nct6694_hwmon_init() then sends the whole structure back to the device, and reads report fan state as temperature state. It stays within the structure, so this is not a memory safety problem, but on a board that uses the fan channels it is not harmless.

Give the DTIN channels a temperature config without HWMON_T_ENABLE so the core never creates their enable attribute. The enable path is then reachable only for the first 10 channels, which stay within tin_en, and fin_en is left alone. The DTIN input and limit attributes are unchanged.

---
 drivers/hwmon/nct6694-hwmon.c | 35 +++++++++++++++++++----------------
 1 file changed, 19 insertions(+), 16 deletions(-)
drivers/hwmon/nct6694-hwmon.c +19 -16
index 6dcf22ca5018a..9a9a4db434c4f 100644
@@ -159,6 +159,9 @@ static inline s8 temp_to_reg(long val)
 #define NCT6694_HWMON_TEMP_CONFIG (HWMON_T_INPUT | HWMON_T_ENABLE |	\
 				   HWMON_T_MAX | HWMON_T_MAX_HYST |	\
 				   HWMON_T_MAX_ALARM)
+#define NCT6694_HWMON_DTIN_CONFIG (HWMON_T_INPUT |			\
+				   HWMON_T_MAX | HWMON_T_MAX_HYST |	\
+				   HWMON_T_MAX_ALARM)
 #define NCT6694_HWMON_FAN_CONFIG (HWMON_F_INPUT | HWMON_F_ENABLE |	\
 				  HWMON_F_MIN | HWMON_F_MIN_ALARM)
 #define NCT6694_HWMON_PWM_CONFIG (HWMON_PWM_INPUT | HWMON_PWM_ENABLE |	\
@@ -193,22 +196,22 @@ static const struct hwmon_channel_info *nct6694_info[] = {
 			   NCT6694_HWMON_TEMP_CONFIG,	/* TDP2 */
 			   NCT6694_HWMON_TEMP_CONFIG,	/* TDP3 */
 			   NCT6694_HWMON_TEMP_CONFIG,	/* TDP4 */
-			   NCT6694_HWMON_TEMP_CONFIG,	/* DTIN0 */
-			   NCT6694_HWMON_TEMP_CONFIG,	/* DTIN1 */
-			   NCT6694_HWMON_TEMP_CONFIG,	/* DTIN2 */
-			   NCT6694_HWMON_TEMP_CONFIG,	/* DTIN3 */
-			   NCT6694_HWMON_TEMP_CONFIG,	/* DTIN4 */
-			   NCT6694_HWMON_TEMP_CONFIG,	/* DTIN5 */
-			   NCT6694_HWMON_TEMP_CONFIG,	/* DTIN6 */
-			   NCT6694_HWMON_TEMP_CONFIG,	/* DTIN7 */
-			   NCT6694_HWMON_TEMP_CONFIG,	/* DTIN8 */
-			   NCT6694_HWMON_TEMP_CONFIG,	/* DTIN9 */
-			   NCT6694_HWMON_TEMP_CONFIG,	/* DTIN10 */
-			   NCT6694_HWMON_TEMP_CONFIG,	/* DTIN11 */
-			   NCT6694_HWMON_TEMP_CONFIG,	/* DTIN12 */
-			   NCT6694_HWMON_TEMP_CONFIG,	/* DTIN13 */
-			   NCT6694_HWMON_TEMP_CONFIG,	/* DTIN14 */
-			   NCT6694_HWMON_TEMP_CONFIG),	/* DTIN15 */
+			   NCT6694_HWMON_DTIN_CONFIG,	/* DTIN0 */
+			   NCT6694_HWMON_DTIN_CONFIG,	/* DTIN1 */
+			   NCT6694_HWMON_DTIN_CONFIG,	/* DTIN2 */
+			   NCT6694_HWMON_DTIN_CONFIG,	/* DTIN3 */
+			   NCT6694_HWMON_DTIN_CONFIG,	/* DTIN4 */
+			   NCT6694_HWMON_DTIN_CONFIG,	/* DTIN5 */
+			   NCT6694_HWMON_DTIN_CONFIG,	/* DTIN6 */
+			   NCT6694_HWMON_DTIN_CONFIG,	/* DTIN7 */
+			   NCT6694_HWMON_DTIN_CONFIG,	/* DTIN8 */
+			   NCT6694_HWMON_DTIN_CONFIG,	/* DTIN9 */
+			   NCT6694_HWMON_DTIN_CONFIG,	/* DTIN10 */
+			   NCT6694_HWMON_DTIN_CONFIG,	/* DTIN11 */
+			   NCT6694_HWMON_DTIN_CONFIG,	/* DTIN12 */
+			   NCT6694_HWMON_DTIN_CONFIG,	/* DTIN13 */
+			   NCT6694_HWMON_DTIN_CONFIG,	/* DTIN14 */
+			   NCT6694_HWMON_DTIN_CONFIG),	/* DTIN15 */
 
 	HWMON_CHANNEL_INFO(fan,
 			   NCT6694_HWMON_FAN_CONFIG,	/* FIN0 */
Signature · 1 line
2.53.0
author maintainer
#404 lore raw

PATCH 404/733 octeontx2-pf: reset HTB scheduler topology before freeing queues

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Ratheesh Kannoth <[email protected]>

[ Upstream commit ef39fca8508597fa565cf2be72a884a712fb98af ]

HTB offload programs NIX_AF_TLxX_TOPOLOGY on QoS-allocated scheduler queues via otx2_qos_txschq_set_parent_topology(), but teardown freed those queues without clearing TOPOLOGY. The AF only restores PARENT and SCHEDULE on free, so PRIO_ANCHOR/RR_PRIO settings can survive in the shared scheduler pool and affect later allocations.

Add otx2_qos_reset_schq_topology() and otx2_qos_free_hw_schq() to zero TL4 through TL2 TOPOLOGY before each schq is returned to the AF during hierarchy teardown and cfg rollback. Skip the aggregation level (TL1):
it is a per-tx-link queue shared by the PF, default Tx hierarchy and VFs, and is not freed back to the AF by nix_txschq_free_one().

---
 .../net/ethernet/marvell/octeontx2/nic/qos.c  | 60 +++++++++++++++++--
 1 file changed, 55 insertions(+), 5 deletions(-)
drivers/net/ethernet/marvell/octeontx2/nic/qos.c +55 -5
index 69c0911e28e91..f160b1618efa2 100644
@@ -235,13 +235,63 @@ static int otx2_qos_txschq_set_parent_topology(struct otx2_nic *pfvf,
 	return rc;
 }
 
+static int otx2_qos_reset_schq_topology(struct otx2_nic *pfvf, u16 lvl,
+					u16 schq)
+{
+	struct mbox *mbox = &pfvf->mbox;
+	struct nix_txschq_config *cfg;
+	int rc;
+
+	if (lvl < NIX_TXSCH_LVL_TL4 || lvl >= NIX_TXSCH_LVL_TL1)
+		return 0;
+
+	mutex_lock(&mbox->lock);
+
+	cfg = otx2_mbox_alloc_msg_nix_txschq_cfg(mbox);
+	if (!cfg) {
+		mutex_unlock(&mbox->lock);
+		return -ENOMEM;
+	}
+
+	cfg->lvl = lvl;
+	cfg->num_regs = 1;
+
+	if (lvl == NIX_TXSCH_LVL_TL4)
+		cfg->reg[0] = NIX_AF_TL4X_TOPOLOGY(schq);
+	else if (lvl == NIX_TXSCH_LVL_TL3)
+		cfg->reg[0] = NIX_AF_TL3X_TOPOLOGY(schq);
+	else if (lvl == NIX_TXSCH_LVL_TL2)
+		cfg->reg[0] = NIX_AF_TL2X_TOPOLOGY(schq);
+
+	cfg->regval[0] = 0;
+
+	rc = otx2_sync_mbox_msg(mbox);
+
+	mutex_unlock(&mbox->lock);
+
+	return rc;
+}
+
+static void otx2_qos_free_hw_schq(struct otx2_nic *pfvf, u16 lvl, u16 schq)
+{
+	int err;
+
+	err = otx2_qos_reset_schq_topology(pfvf, lvl, schq);
+	if (err)
+		netdev_warn(pfvf->netdev,
+			    "QoS: failed to reset topology for schq %u at level %u: %d\n",
+			    schq, lvl, err);
+
+	otx2_txschq_free_one(pfvf, lvl, schq);
+}
+
 static void otx2_qos_free_hw_node_schq(struct otx2_nic *pfvf,
 				       struct otx2_qos_node *parent)
 {
 	struct otx2_qos_node *node;
 
 	list_for_each_entry_reverse(node, &parent->child_schq_list, list)
-		otx2_txschq_free_one(pfvf, node->level, node->schq);
+		otx2_qos_free_hw_schq(pfvf, node->level, node->schq);
 }
 
 static void otx2_qos_free_hw_node(struct otx2_nic *pfvf,
@@ -252,7 +302,7 @@ static void otx2_qos_free_hw_node(struct otx2_nic *pfvf,
 	list_for_each_entry_safe(node, tmp, &parent->child_list, list) {
 		otx2_qos_free_hw_node(pfvf, node);
 		otx2_qos_free_hw_node_schq(pfvf, node);
-		otx2_txschq_free_one(pfvf, node->level, node->schq);
+		otx2_qos_free_hw_schq(pfvf, node->level, node->schq);
 	}
 }
 
@@ -266,7 +316,7 @@ static void otx2_qos_free_hw_cfg(struct otx2_nic *pfvf,
 	otx2_qos_free_hw_node_schq(pfvf, node);
 
 	/* free node hw mappings */
-	otx2_txschq_free_one(pfvf, node->level, node->schq);
+	otx2_qos_free_hw_schq(pfvf, node->level, node->schq);
 
 	mutex_unlock(&pfvf->qos.qos_lock);
 }
@@ -913,7 +963,7 @@ static void otx2_qos_free_cfg(struct otx2_nic *pfvf, struct otx2_qos_cfg *cfg)
 	for (lvl = 0; lvl < NIX_TXSCH_LVL_CNT; lvl++) {
 		for (idx = 0; idx < cfg->schq[lvl]; idx++) {
 			schq = cfg->schq_list[lvl][idx];
-			otx2_txschq_free_one(pfvf, lvl, schq);
+			otx2_qos_free_hw_schq(pfvf, lvl, schq);
 		}
 	}
 
@@ -921,7 +971,7 @@ static void otx2_qos_free_cfg(struct otx2_nic *pfvf, struct otx2_qos_cfg *cfg)
 		for (idx = 0; idx < cfg->schq_contig[lvl]; idx++) {
 			if (cfg->schq_index_used[lvl][idx]) {
 				schq = cfg->schq_contig_list[lvl][idx];
-				otx2_txschq_free_one(pfvf, lvl, schq);
+				otx2_qos_free_hw_schq(pfvf, lvl, schq);
 			}
 		}
 	}
Signature · 1 line
2.53.0
author maintainer
#405 lore raw

PATCH 405/733 vxlan: initialize _md in vxlan_xmit_one()

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Eric Dumazet <[email protected]>

[ Upstream commit be83178bfc44588f6e3adb827ed874c683193466 ]

If a VXLAN device is configured with both VXLAN_F_COLLECT_METADATA and VXLAN_F_GBP, and a packet is transmitted through it using an external ip_tunnel_info that lacks the IP_TUNNEL_VXLAN_OPT_BIT flag, md is left pointing to the uninitialized _md stack variable:

                if (test_bit(IP_TUNNEL_VXLAN_OPT_BIT, info->key.tun_flags)) {
                        if (info->options_len < sizeof(*md))
                                goto drop;
                        md = ip_tunnel_info_opts(info);
                }

Because IP_TUNNEL_VXLAN_OPT_BIT is not set, md is not updated and remains pointing to _md. Later, vxlan_build_skb() is called with md, which eventually calls vxlan_build_gbp_hdr():

if (vxflags & VXLAN_F_GBP)
vxlan_build_gbp_hdr(vxh, md);

Inside vxlan_build_gbp_hdr(), md->gbp is read:

        if (!md->gbp)
                return;
        gbp = (struct vxlanhdr_gbp *)vxh;
        ...
        if (md->gbp & VXLAN_GBP_DONT_LEARN)
                gbp->dont_learn = 1;

If the stack contains garbage, this causes:
1) VXLAN_HF_GBP flag to be spuriously set in the VXLAN header.
2) gbp->dont_learn and gbp->policy_applied to be set from stack bits.
3) gbp->policy_id to receive 16 bits of uninitialized kernel stack data, leaking it onto the wire.

Fix this by zero-initializing _md. If IP_TUNNEL_VXLAN_OPT_BIT is not present, md->gbp remains 0, and vxlan_build_gbp_hdr() returns early without modifying the VXLAN header.

---
 drivers/net/vxlan/vxlan_core.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/vxlan/vxlan_core.c +1 -1
index 1d76e1dbc41f2..e045e1ee9e594 100644
@@ -2373,7 +2373,7 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
 	struct ip_tunnel_key key;
 	struct vxlan_dev *vxlan = netdev_priv(dev);
 	const struct iphdr *old_iph;
-	struct vxlan_metadata _md;
+	struct vxlan_metadata _md = {};
 	struct vxlan_metadata *md = &_md;
 	unsigned int pkt_len = skb->len;
 	__be16 src_port = 0, dst_port;
Signature · 1 line
2.53.0
author maintainer
#406 lore raw

PATCH 406/733 ppp_synctty: ensure a writeable skb header

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Qingfang Deng <[email protected]>

[ Upstream commit 8aaeb56aff2a557a88f83ae866da2c91ad247e59 ]

ppp_sync_txmunge() checks headroom before prepending the address and control bytes, but does not ensure that the skb header is writable. A received skb can reach this function through PPP channel bridging without passing through ppp_start_xmit(), which calls skb_cow_head().

For example, a PPPoE frame may share its buffer with a clone queued to an AF_PACKET socket. If it is bridged to a synchronous tty channel, the address/control bytes can overwrite data still visible to that socket.

Use skb_cow_head() to ensure both sufficient headroom and a writable header.

Fixes 4cf476ced45d ("ppp: add PPPIOCBRIDGECHAN and PPPIOCUNBRIDGECHAN ioctls")
---
 drivers/net/ppp/ppp_synctty.c | 14 +++-----------
 1 file changed, 3 insertions(+), 11 deletions(-)
drivers/net/ppp/ppp_synctty.c +3 -11
index 0208e752ef1eb..1b64e610f9609 100644
@@ -457,17 +457,9 @@ ppp_sync_txmunge(struct syncppp *ap, struct sk_buff *skb)
 
 	/* prepend address/control fields if necessary */
 	if ((ap->flags & SC_COMP_AC) == 0 || islcp) {
-		if (skb_headroom(skb) < 2) {
-			struct sk_buff *npkt = dev_alloc_skb(skb->len + 2);
-			if (npkt == NULL) {
-				kfree_skb(skb);
-				return NULL;
-			}
-			skb_reserve(npkt,2);
-			skb_copy_from_linear_data(skb,
-				      skb_put(npkt, skb->len), skb->len);
-			consume_skb(skb);
-			skb = npkt;
+		if (skb_cow_head(skb, 2)) {
+			kfree_skb(skb);
+			return NULL;
 		}
 		skb_push(skb,2);
 		skb->data[0] = PPP_ALLSTATIONS;
Signature · 1 line
2.53.0
author maintainer
#407 lore raw

PATCH 407/733 net: phylink: initialise link_state before a forced major config

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Aleksei Sviridkin <[email protected]>

[ Upstream commit 113998aa372f4869bf62cfc75c28a2849e8487be ]

phylink_resolve() leaves link_state on the stack unpopulated on its disable and link-failed branches, which set only link_state.link. phylink_apply_manual_flow() then reads the struct's advertising on every mode but MLO_AN_FIXED, and has done so since long before force_major_config existed.

force_major_config turns that into a write to the hardware. It is the only trigger for the major-config block that does not require mac_config, so phylink_major_config() programs the MAC for whatever the stack held, a zeroed interface is PHY_INTERFACE_MODE_NA, and the write-back stores it in pl->link_config.interface.

phylink_replay_link_end() is the only in-tree setter, and sja1105_static_config_reload() calls it for every port that has a phylink instance, regardless of admin state. On a stopped port phylink_run_resolve() no-ops, so the flag outlives the call. The next resolve consumes it whatever branch it takes; an unpopulated branch is where that does damage.

Found while developing a series that attaches a late PHY from a delayed work item and sets this flag there, so the PHY attached after its port was already up. The link stayed down until the port was cycled 29 minutes later. With this patch on the same board the same attach programs the MAC for 2500base-x rather than unknown, and the PHY's interrupt fires without a port bounce where it had stayed at zero throughout the failure.

Fixes 96969b132bf1 ("net: phylink: introduce helpers for replaying link callbacks")
---
 drivers/net/phy/phylink.c | 2 ++
 1 file changed, 2 insertions(+)
drivers/net/phy/phylink.c +2 -0
index 7397169236fdf..27fd9aa2027a4 100644
@@ -1630,8 +1630,10 @@ static void phylink_resolve(struct work_struct *w)
 
 	if (pl->phylink_disable_state) {
 		pl->link_failed = false;
+		link_state = pl->link_config;
 		link_state.link = false;
 	} else if (pl->link_failed) {
+		link_state = pl->link_config;
 		link_state.link = false;
 		retrigger = true;
 	} else if (pl->act_link_an_mode == MLO_AN_FIXED) {
Signature · 1 line
2.53.0
author maintainer
#408 lore raw

PATCH 408/733 net: stmmac: initialize ptp_lock at probe time

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Lorenzo Bianconi <[email protected]>

[ Upstream commit 0338c68e22abd2ee509ec2e32508a50896618c32 ]

priv->ptp_lock is only initialized in stmmac_ptp_register(), which runs during __stmmac_open(). However, the lock is also used while the interface is down and has never been opened: tc_taprio_configure() invokes the PTP gettime64() callback to compute the EST base time when offloading a TAPRIO schedule, and stmmac_get_time() takes priv->ptp_lock. Using an uninitialized rwlock is undefined behaviour. Move the rwlock_init() to __stmmac_dvr_probe(), together with the other private locks, so that ptp_lock is always valid regardless of the interface state.

---
 drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 1 +
 drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c  | 1 -
 2 files changed, 1 insertion(+), 1 deletion(-)
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +1 -0
index 1e88575fdddbb..aa05160c22e35 100644
@@ -8032,6 +8032,7 @@ static int __stmmac_dvr_probe(struct device *device,
 	stmmac_napi_add(ndev);
 
 	mutex_init(&priv->lock);
+	rwlock_init(&priv->ptp_lock);
 
 	stmmac_fpe_init(priv);
 
drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c +0 -1
index 960249960004c..3bfcc9760dce7 100644
@@ -365,7 +365,6 @@ void stmmac_ptp_register(struct stmmac_priv *priv)
 	if (priv->plat->crosststamp)
 		priv->ptp_clock_ops.getcrosststamp = stmmac_getcrosststamp;
 
-	rwlock_init(&priv->ptp_lock);
 	mutex_init(&priv->aux_ts_lock);
 
 	priv->ptp_clock = ptp_clock_register(&priv->ptp_clock_ops,
Signature · 1 line
2.53.0
author maintainer
#409 lore raw

PATCH 409/733 net/mlx5e: Move representor vnic reporter to eswitch devlink port

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Carolina Jubran <[email protected]>

[ Upstream commit 7f26a5e8040b4957ef4dbdfcde6cc7ba2db53937 ]

The representor vnic devlink health reporter is created and destroyed along the representor netdev (un)load path, which is not serialized by the devlink instance lock. Destroying the reporter from there triggers a devl_assert_locked() splat on driver unbind:

  WARNING: net/devlink/core.c:259 at devl_assert_locked+0x54/0x70, CPU#2: bash/3758
  Modules linked in: mlx5_vdpa vringh vdpa mlx5_ib mlx5_fwctl mlx5_core ...
  CPU: 2 UID: 0 PID: 3758 Comm: bash Tainted: G        W           6.19.0+ #1 PREEMPT
  Tainted: [W]=WARN
  Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), ...
  RIP: 0010:devl_assert_locked+0x54/0x70
  Call Trace:
   <TASK>
   devl_health_reporter_destroy+0x3a/0x1b0
   mlx5e_vport_rep_unload+0x12d/0x2b0 [mlx5_core]
   mlx5_eswitch_unregister_vport_reps+0x1b8/0x220 [mlx5_core]
   ? __esw_offloads_unload_rep+0x190/0x190 [mlx5_core]
   ? kernfs_remove_by_name_ns+0xc3/0xf0
   device_release_driver_internal+0x3b2/0x560
   unbind_store+0xce/0xf0

Move the reporter's lifecycle to the eswitch devlink port (un)register paths, which are already serialized by the devlink instance lock, and store the handle on mlx5_devlink_port. Use the port's mlx5_vport as the reporter priv since the diagnose callback only needs a device handle and a vport number, and mlx5_vport carries both and is initialized before any representor driver probes.

---
 .../net/ethernet/mellanox/mlx5/core/en_rep.c  | 52 +------------------
 .../net/ethernet/mellanox/mlx5/core/en_rep.h  |  1 -
 .../mellanox/mlx5/core/esw/devlink_port.c     | 37 +++++++++++++
 .../net/ethernet/mellanox/mlx5/core/eswitch.h |  1 +
 4 files changed, 39 insertions(+), 52 deletions(-)
drivers/net/ethernet/mellanox/mlx5/core/en_rep.c +1 -51
index 3d544fe4e6f73..eb3160bfbb29d 100644
@@ -56,7 +56,6 @@
 #include "lib/vxlan.h"
 #define CREATE_TRACE_POINTS
 #include "diag/en_rep_tracepoint.h"
-#include "diag/reporter_vnic.h"
 #include "en_accel/ipsec.h"
 #include "en/tc/int_port.h"
 #include "en/ptp.h"
@@ -1435,51 +1434,6 @@ static unsigned int mlx5e_ul_rep_stats_grps_num(struct mlx5e_priv *priv)
 	return ARRAY_SIZE(mlx5e_ul_rep_stats_grps);
 }
 
-static int
-mlx5e_rep_vnic_reporter_diagnose(struct devlink_health_reporter *reporter,
-				 struct devlink_fmsg *fmsg,
-				 struct netlink_ext_ack *extack)
-{
-	struct mlx5e_rep_priv *rpriv = devlink_health_reporter_priv(reporter);
-	struct mlx5_eswitch_rep *rep = rpriv->rep;
-
-	mlx5_reporter_vnic_diagnose_counters(rep->esw->dev, fmsg, rep->vport,
-					     true);
-	return 0;
-}
-
-static const struct devlink_health_reporter_ops mlx5_rep_vnic_reporter_ops = {
-	.name = "vnic",
-	.diagnose = mlx5e_rep_vnic_reporter_diagnose,
-};
-
-static void mlx5e_rep_vnic_reporter_create(struct mlx5e_priv *priv,
-					   struct devlink_port *dl_port)
-{
-	struct mlx5e_rep_priv *rpriv = priv->ppriv;
-	struct devlink_health_reporter *reporter;
-
-	reporter = devl_port_health_reporter_create(dl_port,
-						    &mlx5_rep_vnic_reporter_ops,
-						    rpriv);
-	if (IS_ERR(reporter)) {
-		mlx5_core_err(priv->mdev,
-			      "Failed to create representor vnic reporter, err = %pe\n",
-			      reporter);
-		return;
-	}
-
-	rpriv->rep_vnic_reporter = reporter;
-}
-
-static void mlx5e_rep_vnic_reporter_destroy(struct mlx5e_priv *priv)
-{
-	struct mlx5e_rep_priv *rpriv = priv->ppriv;
-
-	if (!IS_ERR_OR_NULL(rpriv->rep_vnic_reporter))
-		devl_health_reporter_destroy(rpriv->rep_vnic_reporter);
-}
-
 static const struct mlx5e_profile mlx5e_rep_profile = {
 	.init			= mlx5e_init_rep,
 	.cleanup		= mlx5e_cleanup_rep,
@@ -1603,10 +1557,8 @@ mlx5e_vport_vf_rep_load(struct mlx5_core_dev *dev, struct mlx5_eswitch_rep *rep)
 
 	dl_port = mlx5_esw_offloads_devlink_port(dev->priv.eswitch,
 						 rpriv->rep->vport);
-	if (!IS_ERR(dl_port)) {
+	if (!IS_ERR(dl_port))
 		SET_NETDEV_DEVLINK_PORT(netdev, dl_port);
-		mlx5e_rep_vnic_reporter_create(priv, dl_port);
-	}
 
 	err = register_netdev(netdev);
 	if (err) {
@@ -1619,7 +1571,6 @@ mlx5e_vport_vf_rep_load(struct mlx5_core_dev *dev, struct mlx5_eswitch_rep *rep)
 	return 0;
 
 err_detach_netdev:
-	mlx5e_rep_vnic_reporter_destroy(priv);
 	mlx5e_detach_netdev(netdev_priv(netdev));
 err_cleanup_profile:
 	priv->profile->cleanup(priv);
@@ -1677,7 +1628,6 @@ mlx5e_vport_rep_unload(struct mlx5_eswitch_rep *rep)
 	}
 
 	unregister_netdev(netdev);
-	mlx5e_rep_vnic_reporter_destroy(priv);
 	mlx5e_detach_netdev(priv);
 	priv->profile->cleanup(priv);
 	mlx5e_destroy_netdev(netdev);
drivers/net/ethernet/mellanox/mlx5/core/en_rep.h +0 -1
index 70640fa1ad7bc..bcd7b4e814d06 100644
@@ -118,7 +118,6 @@ struct mlx5e_rep_priv {
 	struct rtnl_link_stats64 prev_vf_vport_stats;
 	struct mlx5_flow_handle *send_to_vport_meta_rule;
 	struct rhashtable tc_ht;
-	struct devlink_health_reporter *rep_vnic_reporter;
 };
 
 static inline
drivers/net/ethernet/mellanox/mlx5/core/esw/devlink_port.c +37 -0
index 6e50311faa27f..2e7eac4bca3dd 100644
@@ -4,6 +4,26 @@
 #include <linux/mlx5/driver.h>
 #include "eswitch.h"
 #include "devlink.h"
+#include "diag/reporter_vnic.h"
+
+static int
+mlx5_esw_rep_vnic_reporter_diagnose(struct devlink_health_reporter *reporter,
+				    struct devlink_fmsg *fmsg,
+				    struct netlink_ext_ack *extack)
+{
+	struct mlx5_vport *vport = devlink_health_reporter_priv(reporter);
+
+	mlx5_reporter_vnic_diagnose_counters(vport->dev, fmsg, vport->vport,
+					     true);
+
+	return 0;
+}
+
+static const
+struct devlink_health_reporter_ops mlx5_esw_rep_vnic_reporter_ops = {
+	.name = "vnic",
+	.diagnose = mlx5_esw_rep_vnic_reporter_diagnose,
+};
 
 static void
 mlx5_esw_get_port_parent_id(struct mlx5_core_dev *dev, struct netdev_phys_item_id *ppid)
@@ -217,6 +237,7 @@ static void mlx5_esw_devlink_port_res_unregister(struct devlink_port *dl_port)
 
 int mlx5_esw_offloads_devlink_port_register(struct mlx5_eswitch *esw, struct mlx5_vport *vport)
 {
+	struct devlink_health_reporter *reporter;
 	struct mlx5_core_dev *dev = esw->dev;
 	const struct devlink_port_ops *ops;
 	struct mlx5_devlink_port *dl_port;
@@ -252,6 +273,16 @@ int mlx5_esw_offloads_devlink_port_register(struct mlx5_eswitch *esw, struct mlx
 		mlx5_core_dbg(dev, "Failed to register port resources: %d\n",
 			      err);
 
+	reporter = devl_port_health_reporter_create(
+		&dl_port->dl_port, &mlx5_esw_rep_vnic_reporter_ops,
+		vport);
+	if (IS_ERR(reporter))
+		mlx5_core_err(dev,
+			      "Failed to create vnic health reporter for vport %d: %pe\n",
+			      vport_num, reporter);
+	else
+		dl_port->vnic_reporter = reporter;
+
 	return 0;
 
 rate_err:
@@ -266,6 +297,12 @@ void mlx5_esw_offloads_devlink_port_unregister(struct mlx5_vport *vport)
 	if (!vport->dl_port)
 		return;
 	dl_port = vport->dl_port;
+
+	if (dl_port->vnic_reporter) {
+		devl_health_reporter_destroy(dl_port->vnic_reporter);
+		dl_port->vnic_reporter = NULL;
+	}
+
 	mlx5_esw_devlink_port_res_unregister(&dl_port->dl_port);
 
 	mlx5_esw_qos_vport_update_parent(vport, NULL, NULL);
drivers/net/ethernet/mellanox/mlx5/core/eswitch.h +1 -0
index fea72b1dedab9..e8817d45763a6 100644
@@ -189,6 +189,7 @@ struct mlx5_vport;
 struct mlx5_devlink_port {
 	struct devlink_port dl_port;
 	struct mlx5_vport *vport;
+	struct devlink_health_reporter *vnic_reporter;
 };
 
 static inline void mlx5_devlink_port_init(struct mlx5_devlink_port *dl_port,
Signature · 1 line
2.53.0
author maintainer
#410 lore raw

PATCH 410/733 powerpc/entry: Fix double accounting of user time on interrupt entry

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Aboorva Devarajan <[email protected]>

[ Upstream commit 11ae2e1dc58304a48816fc8ca4afa8f2ef9d1bdf ]

Since the switch to generic entry, an interrupt from user mode accounts user time twice: once in arch_interrupt_enter_prepare() and again in arch_enter_from_user_mode(), which irqentry_enter() invokes for the same interrupt:

	arch_interrupt_enter_prepare()
	  account_cpu_user_entry()		/* first */
	irqentry_enter()
	  arch_enter_from_user_mode()
	    account_cpu_user_entry()		/* second */

The second call charges the same interval again, because account_cpu_user_entry() accumulates the time spent in user mode since the last return to user space.

The two calls come from the GENERIC_ENTRY preparation series, where each step was a no-op on its own. Commit 09a9d3a8499d ("powerpc: introduce arch_enter_from_user_mode") added the hook with the user-time accounting in it, but nothing called it yet. Commit 893082ac769b ("powerpc: Prepare for IRQ entry exit") copied interrupt_enter_prepare() verbatim into entry-common.h as arch_interrupt_enter_prepare(); that copy was equally unused, as handlers still called interrupt_enter_prepare().

Commit bee25f97ad24 ("powerpc: Enable GENERIC_ENTRY feature") made both live. On the syscall side it did the full conversion:
system_call_exception() now accounts once through the hook via syscall_enter_from_user_mode(), rather than calling account_cpu_user_entry() directly. On the interrupt side it switched the handler macros to arch_interrupt_enter_prepare() followed by irqentry_enter(), which also runs the hook, but the accounting in arch_interrupt_enter_prepare() was not removed to match. The double accounting starts with that commit.

With CONFIG_VIRT_CPU_ACCOUNTING_NATIVE=y this roughly doubles the reported user time of any workload that takes interrupts. The other accounting modes compile account_cpu_user_entry() to an empty stub, so they are not affected.

Remove the accounting from arch_interrupt_enter_prepare() and rely on arch_enter_from_user_mode(), which already runs for both syscalls and interrupts. The duplicate account_stolen_time() call is removed the same way.

On a pseries LPAR a busy loop reports 6s user time in 3s elapsed (~210% CPU) before the fix, and 3s (~105% CPU) after it:

  $ python3 -c 'while True: pass' &
  $ sleep 3; ps -p $! -o etime,time,pcpu

ELAPSED TIME %CPU

  Before      00:03 00:00:06   210
  After       00:03 00:00:03   105

A 50% load reports ~70% usr / 30% idle before the fix, and ~49% usr / 51% idle after it:

  $ taskset -c 6 stress-ng --cpu 1 --cpu-load 50 &
  $ mpstat -P 6 1
            CPU    %usr   %idle
  Before      6   69.74   30.26
  After       6   48.51   50.50
---
 arch/powerpc/include/asm/entry-common.h | 2 --
 1 file changed, 2 deletions(-)
arch/powerpc/include/asm/entry-common.h +0 -2
index 80b07750b531b..8e91489fdf2bb 100644
@@ -222,8 +222,6 @@ static inline void arch_interrupt_enter_prepare(struct pt_regs *regs)
 
 	if (user_mode(regs)) {
 		kuap_lock();
-		account_cpu_user_entry();
-		account_stolen_time();
 	} else {
 		kuap_save_and_lock(regs);
 		/*
Signature · 1 line
2.53.0
author maintainer
#411 lore raw

PATCH 411/733 selftests/powerpc/tm: Fix tcheck() reading uninitialised CR value

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Thibault Ferrante <[email protected]>

[ Upstream commit ed28b16eab705071d28edaace47189c2eb3aa108 ]

tcheck() is used to check the current transaction state (active, suspended, doomed) via the "tcheck" instruction, which writes its result into CR field 0. The inline asm declared a GPR output operand for this result but never actually moved the CR into it.

Every caller (tcheck_doomed(), tcheck_active(), tcheck_suspended(), tcheck_transactional()) has effectively been testing bits of an unrelated, arbitrary register value since this helper was introduced.
The "& 4" mask discards the TDOOMED and TS_lsb (suspended) bits before they ever reach the callers, so tcheck_doomed() and tcheck_suspended() can never return true, and tcheck_transactional() degrades to being equivalent to tcheck_active().

Fix tcheck() to actually move CR into the output register with mfcr, and widen the mask from "& 4" to "& 0xf" so the full CR0 nibble (TDOOMED | TS_msb | TS_lsb | reserved) is preserved for the callers.

This bug has been present since tcheck() was introduced.

---
 tools/testing/selftests/powerpc/tm/tm.h | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)
tools/testing/selftests/powerpc/tm/tm.h +6 -2
index c03c6e7788767..6024ce4ba6ffd 100644
@@ -105,8 +105,12 @@ static inline bool failure_is_nesting(void)
 static inline int tcheck(void)
 {
 	long cr;
-	asm volatile ("tcheck 0" : "=r"(cr) : : "cr0");
-	return (cr >> 28) & 4;
+	asm volatile("tcheck 0;"
+		     "mfcr %0;"
+		     : "=r"(cr)
+		     :
+		     : "cr0");
+	return (cr >> 28) & 0xf;
 }
 
 static inline bool tcheck_doomed(void)
Signature · 1 line
2.53.0
author maintainer
#412 lore raw

PATCH 412/733 drm/i915/dp: Gate UHBR SST SDP splitting on sink capability

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Mitul Golani <[email protected]>

[ Upstream commit a894f97318366d12102c15937aa6b63c21aa82b5 ]

SDP splitting for 128b/132b (UHBR) SST audio must only be enabled when the sink advertises support for it. Previously sdp_split_enable was set for every UHBR SST stream carrying audio, regardless of sink capability.

In MST mode SDP splitting is inherently supported, so the sink capability check (DP_SST_SPLIT_SDP_CAP) is applied only to the SST path.

Fixes 8853750dbad8 ("drm/i915: Enable SDP split for DP2.0")
Assisted-by Claude:claude-opus-4-8
(cherry picked from commit b37921c9f533ca936c5b5a484c1299680c570a7e)
---
 .../drm/i915/display/intel_display_types.h    |  2 +
 drivers/gpu/drm/i915/display/intel_dp.c       | 44 ++++++++++++++++---
 2 files changed, 40 insertions(+), 6 deletions(-)
drivers/gpu/drm/i915/display/intel_display_types.h +2 -0
index c21e0c0ef0b12..96422641ae441 100644
@@ -1947,6 +1947,8 @@ struct intel_dp {
 
 	bool colorimetry_support;
 
+	bool sst_split_sdp_support;
+
 	struct {
 		enum transcoder transcoder;
 		struct mutex lock;
drivers/gpu/drm/i915/display/intel_dp.c +38 -6

93 lines not on this page · raw message

Signature · 1 line
2.53.0
author maintainer
#413 lore raw

PATCH 413/733 perf/core: Allow list_del during perf_event_overflow()

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Thomas Richter <[email protected]>

[ Upstream commit 59e63416f5153e7d58652c616fbdcb7d5e01fff7 ]

A PMU might use perf_sched_cb_inc() and perf_sched_cb_dec() interface to get the PMU call back function pmu::sched_task invoked at schedule in and schedule out. This is achieved by walking along the list anchored by sched_cb_list.

The following scenario might lead to a list corruption.

perf_pmu_sched_task()

      for_each_list_entry(..., &sched_cb_list)
      +--> __perf_pmu_sched_task()
           +--> event->pmu->sched_task())
                +--> PMU_push_sample()
                     +--> perf_event_overflow()
                          +--> __perf_event_overflow()
                               +--> pmu->stop()
                                    +--> perf_sched_cb_dec()
                                         remove entry from sched_cb_list
                                         while list node in use.

This happens when ioctl(fd, PERF_EVENT_IOC_REFRESH, xxx) has been invoked and perf_event::event_limit hits zero.

Prevent the list corruption and convert for_each_list_entry() to for_each_list_entry_safe().

---
 kernel/events/core.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
kernel/events/core.c +2 -2
index ed3b62b0f948f..bd8c1acf59e2e 100644
@@ -3925,13 +3925,13 @@ static void perf_pmu_sched_task(struct task_struct *prev,
 				bool sched_in)
 {
 	struct perf_cpu_context *cpuctx = this_cpu_ptr(&perf_cpu_context);
-	struct perf_cpu_pmu_context *cpc;
+	struct perf_cpu_pmu_context *cpc, *cpc2;
 
 	/* cpuctx->task_ctx will be handled in perf_event_context_sched_in/out */
 	if (prev == next || cpuctx->task_ctx)
 		return;
 
-	list_for_each_entry(cpc, this_cpu_ptr(&sched_cb_list), sched_cb_entry)
+	list_for_each_entry_safe(cpc, cpc2, this_cpu_ptr(&sched_cb_list), sched_cb_entry)
 		__perf_pmu_sched_task(cpc, sched_in ? next : prev, sched_in);
 }
 
Signature · 1 line
2.53.0
author maintainer
#414 lore raw

PATCH 414/733 perf/x86/intel: Correct pt_regs->flags update for PEBS path

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Dapeng Mi <[email protected]>

[ Upstream commit 88aa1223bfffb1a0a98c639e9e1f71058f0d9178 ]

pt_regs->flags holds the saved CPU FLAGS register. In the PEBS path, it was incorrectly set to PERF_EFLAGS_EXACT instead of being populated from the PEBS flags snapshot.

Update pt_regs->flags from PEBS GPR flags if GPRs group is present.

Fixes c22497f5838c ("perf/x86/intel: Support adaptive PEBS v4")
---
 arch/x86/events/intel/ds.c | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)
arch/x86/events/intel/ds.c +9 -1
index 91a093d8cf2e7..e71cca7bbe77b 100644
@@ -2445,7 +2445,7 @@ static inline void __setup_pebs_basic_group(struct perf_event *event,
 {
 	/* The ip in basic is EventingIP */
 	set_linear_ip(regs, ip);
-	regs->flags = PERF_EFLAGS_EXACT;
+	regs->flags |= PERF_EFLAGS_EXACT;
 	setup_pebs_time(event, data, tsc);
 
 	if (sample_type & PERF_SAMPLE_WEIGHT_STRUCT)
@@ -2457,9 +2457,17 @@ static inline void __setup_pebs_gpr_group(struct perf_event *event,
 					  struct pebs_gprs *gprs,
 					  u64 sample_type)
 {
+	/*
+	 * Update flags with PEBS data. PERF_EFLAGS_EXACT must be set
+	 * in previous basic group handling.
+	 */
+	regs->flags = gprs->flags | PERF_EFLAGS_EXACT;
+
 	if (event->attr.precise_ip < 2) {
 		set_linear_ip(regs, gprs->ip);
 		regs->flags &= ~PERF_EFLAGS_EXACT;
+	} else if (regs->flags & X86_VM_MASK) {
+		regs->flags ^= (PERF_EFLAGS_VM | X86_VM_MASK);
 	}
 
 	if (sample_type & (PERF_SAMPLE_REGS_INTR | PERF_SAMPLE_REGS_USER))
Signature · 1 line
2.53.0
author maintainer
#415 lore raw

PATCH 415/733 perf/x86/intel: Prevent drain_pebs() reentry

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Dapeng Mi <[email protected]>

[ Upstream commit a56c03a397e2cd0c4cf8da96dcd6214f7d0e7d8c ]

The PEBS buffer is shared by all events on a CPU, so drain_pebs() must not be reentered. If so, one instance may observe stale buffer state and potentially access out-of-bound memory.

Most invocations happen in NMI context, which naturally prevents reentry. However, drain_pebs() is also reachable from process context via intel_pmu_drain_pebs_buffer().

In those paths, the PMU is often already disabled, but not guaranteed. For example, __intel_pmu_pebs_disable() only disables the target counter, so other active counters can still raise a PMI and interrupt an in-flight drain_pebs(). Here is an example,

__perf_addr_filters_adjust()
perf_event_stop()

    __perf_event_stop()
      x86_pmu_stop() (event->pmu->stop)
        intel_pmu_disable_event()
          intel_pmu_pebs_disable()
            __intel_pmu_pebs_disable()
              intel_pmu_drain_large_pebs()
                intel_pmu_drain_pebs_buffer()

Introduce __intel_pmu_quiesce() and __intel_pmu_resume() helpers and use them in intel_pmu_drain_large_pebs() to disable the full PMU around the intel_pmu_drain_pebs_buffer() call, preventing reentry.

Also add a warning in intel_pmu_drain_pebs_buffer() when the full PMU is not disabled.

Fixes b752ea0c28e3 ("perf/x86/intel/ds: Flush PEBS DS when changing PEBS_DATA_CFG")
---
 arch/x86/events/intel/core.c | 33 ++++++++++++++++++++++++---------
 arch/x86/events/intel/ds.c   |  8 +++++++-
 arch/x86/events/perf_event.h |  3 +++
 3 files changed, 34 insertions(+), 10 deletions(-)
arch/x86/events/intel/core.c +24 -9
index 5116b15438a21..24c1f40595449 100644
@@ -3125,6 +3125,27 @@ static void intel_pmu_del_event(struct perf_event *event)
 		this_cpu_ptr(&cpu_hw_events)->n_late_setup--;
 }
 
+int __intel_pmu_quiesce(void)
+{
+	struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
+	int pmu_enabled = cpuc->enabled;
+
+	cpuc->enabled = 0;
+	if (pmu_enabled)
+		intel_pmu_disable_all();
+
+	return pmu_enabled;
+}
+
+void __intel_pmu_resume(int pmu_enabled)
+{
+	struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
+
+	cpuc->enabled = pmu_enabled;
+	if (pmu_enabled)
+		intel_pmu_enable_all(0);
+}
+
 static int icl_set_topdown_event_period(struct perf_event *event)
 {
 	struct hw_perf_event *hwc = &event->hw;
@@ -3316,16 +3337,13 @@ static void intel_pmu_read_event(struct perf_event *event)
 	if (event->hw.flags & (PERF_X86_EVENT_AUTO_RELOAD | PERF_X86_EVENT_TOPDOWN) ||
 	    is_pebs_counter_event_group(event)) {
 		struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
-		bool pmu_enabled = cpuc->enabled;
+		int pmu_enabled;
 
 		/* Only need to call update_topdown_event() once for group read. */
 		if (is_metric_event(event) && (cpuc->txn_flags & PERF_PMU_TXN_READ))
 			return;
 
-		cpuc->enabled = 0;
-		if (pmu_enabled)
-			intel_pmu_disable_all();
-
+		pmu_enabled = __intel_pmu_quiesce();
 		/*
 		 * If the PEBS counters snapshotting is enabled,
 		 * the topdown event is available in PEBS records.
@@ -3334,10 +3352,7 @@ static void intel_pmu_read_event(struct perf_event *event)
 			static_call(intel_pmu_update_topdown_event)(event, NULL);
 		else
 			intel_pmu_drain_pebs_buffer();
-
-		cpuc->enabled = pmu_enabled;
-		if (pmu_enabled)
-			intel_pmu_enable_all(0);
+		__intel_pmu_resume(pmu_enabled);
 
 		return;
 	}
arch/x86/events/intel/ds.c +7 -1
index e71cca7bbe77b..3dcd7dfc92b1c 100644
@@ -1242,8 +1242,11 @@ int intel_pmu_drain_bts_buffer(void)
 
 void intel_pmu_drain_pebs_buffer(void)
 {
+	struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
 	struct perf_sample_data data;
 
+	WARN_ON_ONCE(cpuc->enabled);
+
 	static_call(x86_pmu_drain_pebs)(NULL, &data);
 }
 
@@ -1877,8 +1880,11 @@ static void intel_pmu_pebs_via_pt_enable(struct perf_event *event)
 static inline void intel_pmu_drain_large_pebs(struct cpu_hw_events *cpuc)
 {
 	if (cpuc->n_pebs == cpuc->n_large_pebs &&
-	    cpuc->n_pebs != cpuc->n_pebs_via_pt)
+	    cpuc->n_pebs != cpuc->n_pebs_via_pt) {
+		int enabled = __intel_pmu_quiesce();
 		intel_pmu_drain_pebs_buffer();
+		__intel_pmu_resume(enabled);
+	}
 }
 
 static void __intel_pmu_pebs_enable(struct perf_event *event)
arch/x86/events/perf_event.h +3 -0
index a8afea8d38f0c..680220d311a71 100644
@@ -1644,6 +1644,9 @@ static __always_inline void __intel_pmu_lbr_disable(void)
 	wrmsrq(MSR_IA32_DEBUGCTLMSR, debugctl);
 }
 
+extern int __intel_pmu_quiesce(void);
+extern void __intel_pmu_resume(int pmu_enabled);
+
 int intel_pmu_save_and_restart(struct perf_event *event);
 
 struct event_constraint *
Signature · 1 line
2.53.0
author maintainer
#416 lore raw

PATCH 416/733 sched/eevdf: Fix augmented max_slice

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Vincent Guittot <[email protected]>

[ Upstream commit 9a8bc9bb4c3fb3218b4f151f98a722fbeb5b5c34 ]

Similarly to se->min_slice, init se->max_slice with se->slice before enqueueing the entity so the augmented callback computes it correctly at parent level.

---
 kernel/sched/fair.c | 2 ++
 1 file changed, 2 insertions(+)
kernel/sched/fair.c +2 -0
index 4c7bb4b7018e2..83fe5319e2ccd 100644
@@ -1042,6 +1042,8 @@ static void __enqueue_entity(struct cfs_rq *cfs_rq, struct sched_entity *se)
 	sum_w_vruntime_add(cfs_rq, se);
 	se->min_vruntime = se->vruntime;
 	se->min_slice = se->slice;
+	se->max_slice = se->slice;
+
 	rb_add_augmented_cached(&se->run_node, &cfs_rq->tasks_timeline,
 				__entity_less, &min_vruntime_cb);
 }
Signature · 1 line
2.53.0
author maintainer
#417 lore raw

PATCH 417/733 sched/eevdf: Fix rb augmented with multi fields

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Vincent Guittot <[email protected]>

[ Upstream commit 51b0e68cfa0ac69e3c3ea9d6753af7e15dfaab22 ]

The eevdf rb tree maintains 3 augmented fields but only one is currently copied when balancing the tree.

Add a more generic define that can be used when there are several augmented fields. In this case, we provide a function that takes care of copying all fields.

---
 include/linux/rbtree_augmented.h | 35 +++++++++++++++++++++++++-------
 kernel/sched/fair.c              | 12 +++++++++--
 2 files changed, 38 insertions(+), 9 deletions(-)
include/linux/rbtree_augmented.h +28 -7
index 6dbc5a1bf6a8c..d2fa1c41bfd2b 100644
@@ -87,18 +87,18 @@ rb_add_augmented_cached(struct rb_node *node, struct rb_root_cached *tree,
 }
 
 /*
- * Template for declaring augmented rbtree callbacks (generic case)
+ * Template for declaring augmented rbtree callbacks (generic multi fields)
  *
  * RBSTATIC:    'static' or empty
  * RBNAME:      name of the rb_augment_callbacks structure
  * RBSTRUCT:    struct type of the tree nodes
  * RBFIELD:     name of struct rb_node field within RBSTRUCT
- * RBAUGMENTED: name of field within RBSTRUCT holding data for subtree
- * RBCOMPUTE:   name of function that recomputes the RBAUGMENTED data
+ * RBCOPY:	name of function that copies the RBAUGMENTED datas
+ * RBCOMPUTE:   name of function that recomputes the RBAUGMENTED datas
  */
 
-#define RB_DECLARE_CALLBACKS(RBSTATIC, RBNAME,				\
-			     RBSTRUCT, RBFIELD, RBAUGMENTED, RBCOMPUTE)	\
+#define RB_DECLARE_CALLBACKS_MULTI(RBSTATIC, RBNAME,			\
+			     RBSTRUCT, RBFIELD, RBCOPY, RBCOMPUTE)	\
 static inline void							\
 RBNAME ## _propagate(struct rb_node *rb, struct rb_node *stop)		\
 {									\
@@ -114,14 +114,14 @@ RBNAME ## _copy(struct rb_node *rb_old, struct rb_node *rb_new)		\
 {									\
 	RBSTRUCT *old = rb_entry(rb_old, RBSTRUCT, RBFIELD);		\
 	RBSTRUCT *new = rb_entry(rb_new, RBSTRUCT, RBFIELD);		\
-	new->RBAUGMENTED = old->RBAUGMENTED;				\
+	RBCOPY(new, old);						\
 }									\
 static void								\
 RBNAME ## _rotate(struct rb_node *rb_old, struct rb_node *rb_new)	\
 {									\
 	RBSTRUCT *old = rb_entry(rb_old, RBSTRUCT, RBFIELD);		\
 	RBSTRUCT *new = rb_entry(rb_new, RBSTRUCT, RBFIELD);		\
-	new->RBAUGMENTED = old->RBAUGMENTED;				\
+	RBCOPY(new, old);						\
 	RBCOMPUTE(old, false);						\
 }									\
 RBSTATIC const struct rb_augment_callbacks RBNAME = {			\
@@ -130,6 +130,27 @@ RBSTATIC const struct rb_augment_callbacks RBNAME = {			\
 	.rotate = RBNAME ## _rotate					\
 };
 
+/*
+ * Template for declaring augmented rbtree callbacks (generic single field)
+ *
+ * RBSTATIC:    'static' or empty
+ * RBNAME:      name of the rb_augment_callbacks structure
+ * RBSTRUCT:    struct type of the tree nodes
+ * RBFIELD:     name of struct rb_node field within RBSTRUCT
+ * RBAUGMENTED: name of field within RBSTRUCT holding data for subtree
+ * RBCOMPUTE:   name of function that recomputes the RBAUGMENTED data
+ */
+
+#define RB_DECLARE_CALLBACKS(RBSTATIC, RBNAME,				\
+			     RBSTRUCT, RBFIELD, RBAUGMENTED, RBCOMPUTE)	\
+static inline void							\
+RBNAME ## _copy_single(RBSTRUCT *new, RBSTRUCT *old)			\
+{									\
+	new->RBAUGMENTED = old->RBAUGMENTED;				\
+}									\
+RB_DECLARE_CALLBACKS_MULTI(RBSTATIC, RBNAME,				\
+		     RBSTRUCT, RBFIELD, RBNAME ## _copy_single, RBCOMPUTE)
+
 /*
  * Template for declaring augmented rbtree callbacks,
  * computing RBAUGMENTED scalar as max(RBCOMPUTE(node)) for all subtree nodes.
kernel/sched/fair.c +10 -2
index 83fe5319e2ccd..a30d539a3f054 100644
@@ -1004,6 +1004,13 @@ static inline void __max_slice_update(struct sched_entity *se, struct rb_node *n
 	}
 }
 
+static inline void min_vruntime_copy(struct sched_entity *new, struct sched_entity *old)
+{
+	new->min_vruntime = old->min_vruntime;
+	new->min_slice = old->min_slice;
+	new->max_slice = old->max_slice;
+}
+
 /*
  * se->min_vruntime = min(se->vruntime, {left,right}->min_vruntime)
  */
@@ -1031,8 +1038,9 @@ static inline bool min_vruntime_update(struct sched_entity *se, bool exit)
 	       se->max_slice == old_max_slice;
 }
 
-RB_DECLARE_CALLBACKS(static, min_vruntime_cb, struct sched_entity,
-		     run_node, min_vruntime, min_vruntime_update);
+
+RB_DECLARE_CALLBACKS_MULTI(static, min_vruntime_cb, struct sched_entity,
+		     run_node, min_vruntime_copy, min_vruntime_update);
 
 /*
  * Enqueue an entity into the rb-tree:
Signature · 1 line
2.53.0
author maintainer
#418 lore raw

PATCH 418/733 sched: Account cgroup CPU time to the execution context

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Hui Su <[email protected]>

[ Upstream commit c23810313bdf6b02f39a1f2a1464c4b18bd39e31 ]

Proxy execution separates the scheduling context from the execution context. Commit aa4f74dfd42b ("sched: Fix runtime accounting w/ split exec & sched contexts") made per-task and thread-group runtime accounting follow the task that actually executes, while cgroup CPU usage is charged to the donor.

When the donor and execution task belong to different cgroups, this makes a task's execution time count against a different cgroup from the one the task belongs to.

Cgroup CPU usage should follow the execution context, matching the per-task, thread-group, and cgroup user/system accounting. Keep scheduling state associated with the donor, but charge cgroup CPU usage to rq->curr.

A reproducer with the donor and execution task in separate cgroups showed the execution task accumulating runtime while cgroup CPU usage was charged to the donor's cgroup. With this change, the execution task's cgroup accumulates the CPU usage instead. The same behavior was verified with an RT donor and with legacy cpuacct accounting.

---
 kernel/sched/fair.c | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)
kernel/sched/fair.c +1 -3
index a30d539a3f054..bb8a5f358ee19 100644
@@ -1373,7 +1373,6 @@ static s64 update_se(struct rq *rq, struct sched_entity *se)
 
 	se->exec_start = now;
 	if (entity_is_task(se)) {
-		struct task_struct *donor = task_of(se);
 		struct task_struct *running = rq->curr;
 		/*
 		 * If se is a task, we account the time against the running
@@ -1386,8 +1385,7 @@ static s64 update_se(struct rq *rq, struct sched_entity *se)
 		account_group_exec_runtime(running, delta_exec);
 		account_mm_sched(rq, running, delta_exec);
 
-		/* cgroup time is always accounted against the donor */
-		cgroup_account_cputime(donor, delta_exec);
+		cgroup_account_cputime(running, delta_exec);
 	} else {
 		/* If not task, account the time against donor se  */
 		se->sum_exec_runtime += delta_exec;
Signature · 1 line
2.53.0
author maintainer
#419 lore raw

PATCH 419/733 sched/core: Call wq_worker_tick() for the execution context

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Hui Su <[email protected]>

[ Upstream commit f5741d2b34519d387edf6e9798fc7030c20a35f3 ]

wq_worker_tick() accounts CPU time and detects CPU-intensive work for the kworker that is actually running. With proxy execution, rq->donor is the scheduling context while rq->curr is the execution context.

Calling the hook with rq->donor can skip workqueue accounting when a kworker is executing on behalf of a donor task. It can also account a blocked kworker when the donor is a worker but rq->curr is the task actually executing. The former can delay WORKER_CPU_INTENSIVE handling and pool concurrency management, which can delay pending kernel work and userspace operations depending on it.

Use rq->curr for the workqueue tick hook while retaining rq->donor for scheduler accounting.

Fixes af0c8b2bf67b ("sched: Split scheduler and execution contexts")
---
 kernel/sched/core.c | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)
kernel/sched/core.c +5 -4
index d0992ecda4c72..35d647c8c3683 100644
@@ -5780,8 +5780,8 @@ void sched_tick(void)
 {
 	int cpu = smp_processor_id();
 	struct rq *rq = cpu_rq(cpu);
-	/* accounting goes to the donor task */
-	struct task_struct *donor;
+	/* scheduler accounting goes to the donor task */
+	struct task_struct *curr, *donor;
 	struct rq_flags rf;
 	unsigned long hw_pressure;
 	u64 resched_latency;
@@ -5792,6 +5792,7 @@ void sched_tick(void)
 	sched_clock_tick();
 
 	rq_lock(rq, &rf);
+	curr = rq->curr;
 	donor = rq->donor;
 
 	psi_account_irqtime(rq, donor, NULL);
@@ -5817,8 +5818,8 @@ void sched_tick(void)
 
 	perf_event_task_tick();
 
-	if (donor->flags & PF_WQ_WORKER)
-		wq_worker_tick(donor);
+	if (curr->flags & PF_WQ_WORKER)
+		wq_worker_tick(curr);
 
 	if (!scx_switched_all()) {
 		rq->idle_balance = idle_cpu(cpu);
Signature · 1 line
2.53.0
author maintainer
#420 lore raw

PATCH 420/733 net/sched: cls_route: free emptied bucket on filter move

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Victor Nogueira <[email protected]>

[ Upstream commit 1853f30cf5c84971f99788a76207c6f745380896 ]

route4_change can move an existing filter to a different top-level
bucket: route4_set_parms recomputes the handle from TCA_ROUTE4_TO/ FROM/IIF, and the handle-mismatch check is gated on the 'new' flag, so for an existing filter the new handle may differ from the old one and land in a different bucket. When this happens, the filter is unlinked from the old bucket, but the bucket itself is never freed once it goes empty. The stale empty bucket remains in head->table[], causing route4_delete to report *last=false even after the last live filter is gone. That pins the empty tcf_proto and causes a leak.

Fix this by refcounting the filters linked to a bucket and freeing the bucket when the count drops to zero. The existing scan in route4_delete goes away with it.

The count is updated at all sites that link or unlink a filter during add, change and delete, and the bucket is dropped from head->table[] as soon as it reaches zero.

Conditions to recreate the bug:
CONFIG_NET_CLS_ROUTE4=y, CONFIG_NET_SCH_INGRESS=y, CONFIG_NET_CLS_ACT=y.

tc qdisc replace dev lo clsact
tc filter add dev lo ingress protocol ip pref 100 route from 1 to 1 tc filter change dev lo ingress protocol ip pref 100 handle 0x10001 \
route from 1 to 2
tc filter del dev lo ingress protocol ip pref 100 handle 0x10002 \
route from 1 to 2
tc filter show dev lo ingress | grep -c 'pref 100 route chain 0 '

Fixes 1e052be69d04 ("net_sched: destroy proto tp when all filters are gone")
---
 net/sched/cls_route.c | 45 +++++++++++++++++++++----------------------
 1 file changed, 22 insertions(+), 23 deletions(-)
net/sched/cls_route.c +22 -23
index 0d1324c905837..17b0ebb766626 100644
@@ -11,6 +11,7 @@
 #include <linux/kernel.h>
 #include <linux/string.h>
 #include <linux/errno.h>
+#include <linux/refcount.h>
 #include <linux/skbuff.h>
 #include <net/dst.h>
 #include <net/route.h>
@@ -41,6 +42,7 @@ struct route4_head {
 struct route4_bucket {
 	/* 16 FROM buckets + 16 IIF buckets + 1 wildcard bucket */
 	struct route4_filter __rcu	*ht[16 + 16 + 1];
+	refcount_t			filters_ref;
 	struct rcu_head			rcu;
 };
 
@@ -336,7 +338,7 @@ static int route4_delete(struct tcf_proto *tp, void *arg, bool *last,
 	struct route4_filter *nf;
 	struct route4_bucket *b;
 	unsigned int h = 0;
-	int i, h1;
+	int h1;
 
 	if (!head || !f)
 		return -EINVAL;
@@ -362,23 +364,14 @@ static int route4_delete(struct tcf_proto *tp, void *arg, bool *last,
 			tcf_exts_get_net(&f->exts);
 			tcf_queue_work(&f->rwork, route4_delete_filter_work);
 
-			/* Strip RTNL protected tree */
-			for (i = 0; i <= 32; i++) {
-				struct route4_filter *rt;
-
-				rt = rtnl_dereference(b->ht[i]);
-				if (rt)
-					goto out;
+			if (refcount_dec_and_test(&b->filters_ref)) {
+				RCU_INIT_POINTER(head->table[to_hash(h)], NULL);
+				kfree_rcu(b, rcu);
 			}
-
-			/* OK, session has no flows */
-			RCU_INIT_POINTER(head->table[to_hash(h)], NULL);
-			kfree_rcu(b, rcu);
 			break;
 		}
 	}
 
-out:
 	*last = true;
 	for (h1 = 0; h1 <= 256; h1++) {
 		if (rcu_access_pointer(head->table[h1])) {
@@ -459,6 +452,7 @@ static int route4_set_parms(struct net *net, struct tcf_proto *tp,
 		if (b == NULL)
 			return -ENOBUFS;
 
+		refcount_set(&b->filters_ref, 1);
 		rcu_assign_pointer(head->table[h1], b);
 	} else {
 		unsigned int h2 = from_hash(nhandle >> 16);
@@ -468,6 +462,8 @@ static int route4_set_parms(struct net *net, struct tcf_proto *tp,
 		     fp = rtnl_dereference(fp->next))
 			if (fp->handle == f->handle)
 				return -EEXIST;
+
+		refcount_inc(&b->filters_ref);
 	}
 
 	if (tb[TCA_ROUTE4_TO])
@@ -500,7 +496,7 @@ static int route4_change(struct net *net, struct sk_buff *in_skb,
 	struct route4_filter *fold, *f1, *pfp, *f = NULL;
 	struct route4_bucket *b;
 	struct nlattr *tb[TCA_ROUTE4_MAX + 1];
-	unsigned int h, th;
+	unsigned int h;
 	int err;
 	bool new = true;
 
@@ -560,17 +556,20 @@ static int route4_change(struct net *net, struct sk_buff *in_skb,
 	rcu_assign_pointer(*fp, f);
 
 	if (fold) {
-		th = to_hash(fold->handle);
+		b = fold->bkt;
 		h = from_hash(fold->handle >> 16);
-		b = rtnl_dereference(head->table[th]);
-		if (b) {
-			fp = &b->ht[h];
-			for (pfp = rtnl_dereference(*fp); pfp;
-			     fp = &pfp->next, pfp = rtnl_dereference(*fp)) {
-				if (pfp == fold) {
-					rcu_assign_pointer(*fp, fold->next);
-					break;
+		fp = &b->ht[h];
+		for (pfp = rtnl_dereference(*fp); pfp;
+		     fp = &pfp->next, pfp = rtnl_dereference(*fp)) {
+			if (pfp == fold) {
+				rcu_assign_pointer(*fp, fold->next);
+				if (refcount_dec_and_test(&b->filters_ref)) {
+					unsigned int th = to_hash(fold->handle);
+
+					RCU_INIT_POINTER(head->table[th], NULL);
+					kfree_rcu(b, rcu);
 				}
+				break;
 			}
 		}
 	}
Signature · 1 line
2.53.0
author maintainer
#421 lore raw

PATCH 421/733 net/sched: cls_route: Reject handle aliasing

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Victor Nogueira <[email protected]>

[ Upstream commit b74a8455a2f271f54695b6a8ec1f113824a46c0e ]

route4_set_parms() rejects a duplicate by scanning the destination chain for f->handle, but f->handle is the handle the filter has before the update, not the one it is about to be linked under. The comparison and the insertion therefore use different handles, which causes breakage.

When a change moves the filter to a chain that already holds nhandle, the scan looks for the old handle instead, misses the collision and links a second filter with the same handle:

tc filter add dev lo ingress protocol ip pref 100 \
route from 1 to 1 classid 1:1 action ok
tc filter add dev lo ingress protocol ip pref 100 \
route from 2 to 2 classid 1:2 action drop
tc filter change dev lo ingress protocol ip pref 100 handle 0x10001 \
route from 2 to 2 classid 1:1 action ok
tc filter show dev lo ingress
... fh 0x00020002 flowid 1:2 to 2 from 2
... fh 0x00020002 flowid 1:1 to 2 from 2

The newcomer is appended after the incumbent, and both end up with the same f->id. route4_get() returns the first match, so the second filter can no longer be addressed by handle, and route4_classify() stops at the first filter whose f->id matches. The second filter is dumped but is effectively dead.

Fix this by comparing against nhandle.

---
 net/sched/cls_route.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)
net/sched/cls_route.c +5 -1
index 17b0ebb766626..9710b77d379c4 100644
@@ -460,8 +460,12 @@ static int route4_set_parms(struct net *net, struct tcf_proto *tp,
 		for (fp = rtnl_dereference(b->ht[h2]);
 		     fp;
 		     fp = rtnl_dereference(fp->next))
-			if (fp->handle == f->handle)
+			if (fp->handle == nhandle) {
+				NL_SET_ERR_MSG_FMT(extack,
+						   "Handle %x is already in use",
+						   nhandle);
 				return -EEXIST;
+			}
 
 		refcount_inc(&b->filters_ref);
 	}
Signature · 1 line
2.53.0
author maintainer
#422 lore raw

PATCH 422/733 net/sched: cls_route: Fix in-place replace

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Victor Nogueira <[email protected]>

[ Upstream commit 41e85e54e5649a1617698438b0ce64c6f9d83d69 ]

Building on the previous patch, route4_set_parms rejects a duplicate by scanning the destination chain for nhandle, but the scan doesn't exclude the older version it is replacing, so an in-place replace will match the older version's handle and fail.

Fix this by passing the older filter as a parameter to route4_set_parms (replacing "new") and skipping it in the scan.

Excluding the older version is not enough on its own. nhandle is built out of TCA_ROUTE4_TO, TCA_ROUTE4_FROM and TCA_ROUTE4_IIF alone, while the 0x7F00 bits, which only tell apart filters sharing one key, are folded in on the create path. Letting the replace through would therefore rename the filter it replaces: replacing handle 0x10101 stored it back as 0x10001, and a sibling at 0x10201 could then no longer be replaced at all, since its own nhandle collided with the renamed filter.

	tc filter add ... handle 0x10101 route from 1 to 1 classid 1:1
	tc filter add ... handle 0x10201 route from 1 to 1 classid 1:2
	tc filter replace ... handle 0x10101 route from 1 to 1 classid 1:9
	... fh 0x00010001 flowid 1:9 to 1 from 1
	... fh 0x00010201 flowid 1:2 to 1 from 1
	tc filter replace ... handle 0x10201 route from 1 to 1 classid 1:8
	Error: Handle 10001 is already in use.

So carry those bits over when the key the request builds is the key the older filter already has. An in-place replace then keeps the handle userspace named the filter by, while a request that does change the key still renames it, as it did before.

---
 net/sched/cls_route.c | 27 +++++++++++++--------------
 1 file changed, 13 insertions(+), 14 deletions(-)
net/sched/cls_route.c +13 -14
index 9710b77d379c4..0f211f030fd9a 100644
@@ -393,8 +393,9 @@ static const struct nla_policy route4_policy[TCA_ROUTE4_MAX + 1] = {
 static int route4_set_parms(struct net *net, struct tcf_proto *tp,
 			    unsigned long base, struct route4_filter *f,
 			    u32 handle, struct route4_head *head,
-			    struct nlattr **tb, struct nlattr *est, int new,
-			    u32 flags, struct netlink_ext_ack *extack)
+			    struct nlattr **tb, struct nlattr *est,
+			    struct route4_filter *fold, u32 flags,
+			    struct netlink_ext_ack *extack)
 {
 	u32 id = 0, to = 0, nhandle = 0x8000;
 	struct route4_filter *fp;
@@ -407,7 +408,7 @@ static int route4_set_parms(struct net *net, struct tcf_proto *tp,
 		return err;
 
 	if (tb[TCA_ROUTE4_TO]) {
-		if (new && handle & 0x8000) {
+		if (!fold && handle & 0x8000) {
 			NL_SET_ERR_MSG(extack, "Invalid handle");
 			return -EINVAL;
 		}
@@ -430,14 +431,14 @@ static int route4_set_parms(struct net *net, struct tcf_proto *tp,
 	} else
 		nhandle |= 0xFFFF << 16;
 
-	if (handle && new) {
+	if (handle && (!fold || nhandle == (handle & ~0x7F00)))
 		nhandle |= handle & 0x7F00;
-		if (nhandle != handle) {
-			NL_SET_ERR_MSG_FMT(extack,
-					   "Handle mismatch constructed: %x (expected: %x)",
-					   handle, nhandle);
-			return -EINVAL;
-		}
+
+	if (handle && !fold && nhandle != handle) {
+		NL_SET_ERR_MSG_FMT(extack,
+				   "Handle mismatch constructed: %x (expected: %x)",
+				   handle, nhandle);
+		return -EINVAL;
 	}
 
 	if (!nhandle) {
@@ -460,7 +461,7 @@ static int route4_set_parms(struct net *net, struct tcf_proto *tp,
 		for (fp = rtnl_dereference(b->ht[h2]);
 		     fp;
 		     fp = rtnl_dereference(fp->next))
-			if (fp->handle == nhandle) {
+			if (fp != fold && fp->handle == nhandle) {
 				NL_SET_ERR_MSG_FMT(extack,
 						   "Handle %x is already in use",
 						   nhandle);
@@ -502,7 +503,6 @@ static int route4_change(struct net *net, struct sk_buff *in_skb,
 	struct nlattr *tb[TCA_ROUTE4_MAX + 1];
 	unsigned int h;
 	int err;
-	bool new = true;
 
 	if (!handle) {
 		NL_SET_ERR_MSG(extack, "Creating with handle of 0 is invalid");
@@ -539,11 +539,10 @@ static int route4_change(struct net *net, struct sk_buff *in_skb,
 
 		f->tp = fold->tp;
 		f->bkt = fold->bkt;
-		new = false;
 	}
 
 	err = route4_set_parms(net, tp, base, f, handle, head, tb,
-			       tca[TCA_RATE], new, flags, extack);
+			       tca[TCA_RATE], fold, flags, extack);
 	if (err < 0)
 		goto errout;
 
Signature · 1 line
2.53.0
author maintainer
#423 lore raw

PATCH 423/733 net/sched: cls_api: Dont replay RTM_GETCHAIN in tc_ctl_chain().

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Kuniyuki Iwashima <[email protected]>

[ Upstream commit dff39930ad5e53d202bfdfb14687d1d2fd753b4d ]

If a netlink socket sends RTM_GETCHAIN requests repeatedly without recv()ing the responses, tc_ctl_chain() hogs CPU and triggers Hung Task splat. [0]

As caught in the stack trace, netlink_attachskb() could confuse tc_ctl_chain() by returning -EAGAIN when the userspace netlink socket's receive buffer is full.

The replay: label exists since commit 32a4f5ecd738 ("net: sched:
introduce chain object to uapi") but was not used initially.

Since commit 9f407f1768d3 ("net: sched: introduce chain templates"), the label is needed for RTM_NEWCHAIN because tcf_proto_lookup_ops() may release RTNL to call request_module().

However, the replay logic is unnecessary for RTM_GETCHAIN.

Let's apply the replay logic only for RTM_NEWCHAIN.

[0]:

INFO: task repro:1018 is blocked on a mutex likely owned by task repro:1022.
task:repro           state:R  running task     stack:14096 pid:1022  tgid:1014  ppid:961    task_flags:0x400040 flags:0x00080000
Call Trace:
 <TASK>
 ? clockevents_program_event (kernel/time/clockevents.c:372)
 ? pskb_expand_head (net/core/skbuff.c:615)
 ? skb_release_data (net/core/skbuff.c:1122)
 ? netlink_attachskb (./include/linux/skbuff.h:1323 ./include/linux/skbuff.h:1332 net/netlink/af_netlink.c:1232)
 ? __netlink_lookup (./include/linux/rcupdate.h:882 ./include/linux/rhashtable.h:711 net/netlink/af_netlink.c:499)
 ? tc_chain_notify (net/sched/cls_api.c:3045)
 ? tc_chain_notify (./include/linux/skbuff.h:1384 net/sched/cls_api.c:3041)
 ? netlink_unicast (net/netlink/af_netlink.c:1335)
 ? rtnl_unicast (./include/net/netlink.h:1198 net/core/rtnetlink.c:985)
 ? tc_ctl_chain (net/sched/cls_api.c:3242)
 ? rtnetlink_rcv_msg (net/core/rtnetlink.c:7146)
 ? netlink_unicast (net/netlink/af_netlink.c:1354)
 ? __pfx_rtnetlink_rcv_msg (net/core/rtnetlink.c:7177)
 ? netlink_rcv_skb (net/netlink/af_netlink.c:2556)
 ? netlink_unicast (net/netlink/af_netlink.c:1319)
 ? netlink_sendmsg (net/netlink/af_netlink.c:1900)
 ? __sock_sendmsg (net/socket.c:800)
 ? __sys_sendto (net/socket.c:2281)
 ? __x64_sys_sendto (net/socket.c:2288 net/socket.c:2284 net/socket.c:2284)
 ? do_syscall_64 (arch/x86/entry/syscall_64.c:61 arch/x86/entry/syscall_64.c:84)
 ? entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121)
 </TASK>
---
 net/sched/cls_api.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
net/sched/cls_api.c +1 -1
index 9966766661d50..c47d2ee13641d 100644
@@ -3254,7 +3254,7 @@ static int tc_ctl_chain(struct sk_buff *skb, struct nlmsghdr *n,
 	tcf_chain_put(chain);
 errout_block:
 	tcf_block_release(q, block, true);
-	if (err == -EAGAIN)
+	if (err == -EAGAIN && n->nlmsg_type == RTM_NEWCHAIN)
 		/* Replay the request. */
 		goto replay;
 	return err;
Signature · 1 line
2.53.0
author maintainer
#424 lore raw

PATCH 424/733 net: sun4i-emac: fix missing of_node_put() for phy_node

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Li Youhong <[email protected]>

[ Upstream commit af406abfecad2f48d8f1fc646d3994f0982bac62 ]

of_parse_phandle() returns a node pointer with an elevated refcount. Add the missing of_node_put() on the probe error path after register_netdev() fails and in emac_remove().

Fixes 492205050d77 ("net: Add EMAC ethernet driver found on Allwinner A10 SoC's")
---
 drivers/net/ethernet/allwinner/sun4i-emac.c | 2 ++
 1 file changed, 2 insertions(+)
drivers/net/ethernet/allwinner/sun4i-emac.c +2 -0
index a297874f4a819..9382b4b329bdf 100644
@@ -1067,6 +1067,7 @@ static int emac_probe(struct platform_device *pdev)
 	return 0;
 
 out_release_sram:
+	of_node_put(db->phy_node);
 	sunxi_sram_release(&pdev->dev);
 out_clk_disable_unprepare:
 	clk_disable_unprepare(db->clk);
@@ -1094,6 +1095,7 @@ static void emac_remove(struct platform_device *pdev)
 	}
 
 	unregister_netdev(ndev);
+	of_node_put(db->phy_node);
 	sunxi_sram_release(&pdev->dev);
 	clk_disable_unprepare(db->clk);
 	irq_dispose_mapping(ndev->irq);
Signature · 1 line
2.53.0
author maintainer
#425 lore raw

PATCH 425/733 net: hinic: fix mailbox segment buffer overflow

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Aamir Ahmed <[email protected]>

[ Upstream commit 5d4d985957434867bbe85e4fa5e638f3e48ad522 ]

check_mbox_seq_id_and_seg_len() validates that seq_id does not exceed SEQ_ID_MAX_VAL (42) and seg_len does not exceed MBOX_SEG_LEN (48). However, this allows the last segment (seq_id=42) to carry a full 48-byte payload, writing to offset 42*48=2016 for 48 bytes (ending at byte 2064). The receive buffer is only MBOX_MAX_BUF_SZ (2048) bytes, resulting in a 16-byte heap buffer overflow.

The hinic3 driver already handles this correctly by defining MBOX_LAST_SEG_MAX_LEN and rejecting the last segment when it exceeds the remaining buffer space. Apply the same fix to the hinic driver.

---
 drivers/net/ethernet/huawei/hinic/hinic_hw_mbox.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/net/ethernet/huawei/hinic/hinic_hw_mbox.c +3 -1
index 2784127327e64..6e67a6c9578e1 100644
@@ -128,6 +128,7 @@ enum hinic_mbox_tx_status {
 
 #define SEQ_ID_START_VAL			0
 #define SEQ_ID_MAX_VAL				42
+#define MBOX_LAST_SEG_MAX_LEN			(MBOX_MAX_BUF_SZ - SEQ_ID_MAX_VAL * MBOX_SEG_LEN)
 
 #define NO_DMA_ATTRIBUTE_VAL			0
 
@@ -372,7 +373,8 @@ recv_pf_from_vf_mbox_handler(struct hinic_mbox_func_to_func *func_to_func,
 static bool check_mbox_seq_id_and_seg_len(struct hinic_recv_mbox *recv_mbox,
 					  u8 seq_id, u8 seg_len)
 {
-	if (seq_id > SEQ_ID_MAX_VAL || seg_len > MBOX_SEG_LEN)
+	if (seq_id > SEQ_ID_MAX_VAL || seg_len > MBOX_SEG_LEN ||
+	    (seq_id == SEQ_ID_MAX_VAL && seg_len > MBOX_LAST_SEG_MAX_LEN))
 		return false;
 
 	if (seq_id == 0) {
Signature · 1 line
2.53.0
author maintainer
#426 lore raw

PATCH 426/733 net: dsa: mt7530: add EN7528 support

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Ahmed Naseef <[email protected]>

[ Upstream commit cf23fcc9437e5c383d9f282197d580a5a3fd6e6e ]

The EcoNet EN7528 SoC integrates an MT7530 switch (the chip revision register reads 0x7530), memory-mapped in the SoC register space and reached through the same MMIO glue used for the built-in switches of the MediaTek MT7988 and Airoha EN7581/AN7583 SoCs. Its reset sequence and its PHY indirect access registers are the same as on those switches, so add an ID_EN7528 variant bound with the "econet,en7528-switch" compatible, reusing mt7988_setup() and the indirect PHY accessors.

The switch core, however, is an MT7530 and not an MT7531 derivative: the CPU port to trap frames to is set through the MT7530-style CPU_EN / CPU_PORT fields of the MFC register rather than the MT7531 CFC register, so add it to the MT7530 handling in mt753x_conduit_state_change(). For the same reason the MT7530 mirror and force-mode register layouts already apply to it as the default of the MT753X_*() macros.

The four user ports (1-4) are connected to integrated Gigabit PHYs at MDIO addresses 9-12 of the switch internal MDIO bus. The CPU port (port
6) is connected to the SoC Ethernet MAC at a fixed 1000 Mbps full duplex link, so the port capabilities cannot be shared with the MT7988 and EN7581 switches, whose CPU ports run at 10 Gbps.

The LAN GPHYs advertise EEE by default, but negotiating EEE with some link partners results in an unstable link with dropped frames. Leave the LPI capabilities empty for the EN7528 so that phylink disables EEE on these PHYs and refuses to enable it from userspace.

Stable-dep-of: 3c18e3c9a54e ("net: dsa: mt7530: populate lpi_interfaces to fix EEE support")

---
 drivers/net/dsa/mt7530-mmio.c |  1 +
 drivers/net/dsa/mt7530.c      | 58 ++++++++++++++++++++++++++++++-----
 drivers/net/dsa/mt7530.h      |  1 +
 3 files changed, 52 insertions(+), 8 deletions(-)
drivers/net/dsa/mt7530-mmio.c +1 -0
index 119fdd863d917..fd68b1cd06306 100644
@@ -12,6 +12,7 @@
 static const struct of_device_id mt7988_of_match[] = {
 	{ .compatible = "airoha,an7583-switch", .data = &mt753x_table[ID_AN7583], },
 	{ .compatible = "airoha,en7581-switch", .data = &mt753x_table[ID_EN7581], },
+	{ .compatible = "econet,en7528-switch", .data = &mt753x_table[ID_EN7528], },
 	{ .compatible = "mediatek,mt7988-switch", .data = &mt753x_table[ID_MT7988], },
 	{ /* sentinel */ },
 };
drivers/net/dsa/mt7530.c +50 -8

86 lines not on this page · raw message

drivers/net/dsa/mt7530.h +1 -0
index dd33b0df3419e..5f1e841f42c0e 100644
@@ -21,6 +21,7 @@ enum mt753x_id {
 	ID_MT7988 = 3,
 	ID_EN7581 = 4,
 	ID_AN7583 = 5,
+	ID_EN7528 = 6,
 };
 
 #define	NUM_TRGMII_CTRL			5
Signature · 1 line
2.53.0
author maintainer
#427 lore raw

PATCH 427/733 net: dsa: mt7530: populate lpi_interfaces to fix EEE support

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Aleksei Sviridkin <[email protected]>

[ Upstream commit 3c18e3c9a54e1239b72849502ca4737604bfbb46 ]

phylink_create() decides once and for all that a MAC supports managed EEE, and it requires the tx_lpi ops plus non-empty lpi_capabilities and lpi_interfaces. mt753x_phylink_get_caps() leaves lpi_interfaces empty.

So ever since the conversion to phylink managed EEE, ethtool has answered "Not supported" on every mt753x port, and phy_disable_eee() has locked userspace out of turning EEE on. That undoes what commit 06dfcd4098cf ("net: dsa: mt7530: fix enabling EEE on MT7531 switch on all boards") arranged: EEE off by default, but reachable with ethtool.

Leave the speeds above 1 Gbps out of both bitmaps. PMCR folds SPEED_2500 and SPEED_10000 onto PMCR_FORCE_SPEED_1000, so PMCR_FORCE_EEE1G would govern LPI on such a link, and that is unvalidated rather than known unsupported: MediaTek's SDK driver sets the EEE force bits for 100 Mbps and 1 Gbps only, and the unit of the wakeup timers is undocumented with the port clock at 2.5 times the rate.

LPI stays off until userspace enables it, but the EEE advertisement of a PHY that advertises it out of reset comes back, since phylink stops force-clearing it.

Fixes 9cf21773f535 ("net: dsa: mt7530: convert to phylink managed EEE")
---
 drivers/net/dsa/mt7530.c | 16 ++++++++++++----
 1 file changed, 12 insertions(+), 4 deletions(-)
drivers/net/dsa/mt7530.c +12 -4
index 55131bfd11f6d..56ee8dc34f518 100644
@@ -3118,23 +3118,31 @@ static void mt753x_phylink_get_caps(struct dsa_switch *ds, int port,
 
 	config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE;
 
+	priv->info->mac_port_get_caps(ds, port, config);
+
 	/* The EN7528 GPHYs report EEE capability, but negotiating EEE with
 	 * common link partners (e.g. Realtek GbE NICs) results in an unstable
 	 * link with dropped frames. Leave the LPI capabilities empty so that
 	 * phylink disables EEE on these PHYs and refuses to enable it from
 	 * userspace.
 	 */
-	if (priv->id != ID_EN7528) {
+	if (priv->id != ID_EN7528 &&
+	    config->mac_capabilities & (MAC_100FD | MAC_1000FD)) {
 		u32 eeecr = mt7530_read(priv, MT753X_PMEEECR_P(port));
 
-		config->lpi_capabilities = MAC_100FD | MAC_1000FD | MAC_2500FD;
+		/* LPI above 1 Gbps is not supported */
+		config->lpi_capabilities = config->mac_capabilities &
+					   (MAC_100FD | MAC_1000FD);
+		phy_interface_copy(config->lpi_interfaces,
+				   config->supported_interfaces);
+		__clear_bit(PHY_INTERFACE_MODE_2500BASEX,
+			    config->lpi_interfaces);
+
 		/* tx_lpi_timer should be in microseconds. The time units for
 		 * LPI threshold are unspecified.
 		 */
 		config->lpi_timer_default = FIELD_GET(LPI_THRESH_MASK, eeecr);
 	}
-
-	priv->info->mac_port_get_caps(ds, port, config);
 }
 
 static int mt753x_pcs_validate(struct phylink_pcs *pcs,
Signature · 1 line
2.53.0
author maintainer
#428 lore raw

PATCH 428/733 net: ethernet: mtk_eth_soc: populate lpi_interfaces to fix EEE support

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Aleksei Sviridkin <[email protected]>

[ Upstream commit d876c9cb2d16ed259449fe9da08c37a5cb81d724 ]

phylink_create() decides once and for all that a MAC supports managed EEE, and it requires the tx_lpi ops plus non-empty lpi_capabilities and lpi_interfaces. mtk_add_mac() leaves lpi_interfaces empty.

So ever since EEE support was added, ethtool has answered "Not supported" on every MAC that uses mtk_phylink_ops, and phy_disable_eee() has locked userspace out of turning EEE on. MT7628 is unaffected, as rt5350_phylink_ops has no tx_lpi methods.

Leave 2.5 Gbps out of both bitmaps, and the xGMII modes that mtk_mac_enable_tx_lpi() already refuses. MAC_MCR folds SPEED_2500 onto MAC_MCR_SPEED_1000, so MAC_MCR_EEE1G would govern LPI on such a link, and that is unvalidated rather than known unsupported: MediaTek's SDK driver sets the EEE force bits for 100 Mbps and 1 Gbps only, and the unit of the wakeup timers is undocumented with the port clock at 2.5 times the rate.

mtk_mac_enable_tx_lpi() programs wake-up times taken from MT7531's reset values, and the SoC's own field has no reset value to fall back on. Only MT7981 has been seen to exit LPI cleanly with them, so the LPI interfaces sit behind a new MTK_GMAC_EEE capability that only MT7981 sets; every other SoC keeps the current behaviour until it has been confirmed.

LPI stays off until userspace enables it, but the EEE advertisement of a PHY that advertises it out of reset comes back, since phylink stops force-clearing it.

Fixes 952d7325362f ("net: ethernet: mediatek: add EEE support")
---
 drivers/net/ethernet/mediatek/mtk_eth_soc.c | 18 +++++++++++++++---
 drivers/net/ethernet/mediatek/mtk_eth_soc.h |  4 +++-
 2 files changed, 18 insertions(+), 4 deletions(-)
drivers/net/ethernet/mediatek/mtk_eth_soc.c +15 -3
index 351444fb48716..b2473df74dff0 100644
@@ -4827,7 +4827,7 @@ static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np)
 	phy_interface_t phy_mode;
 	struct phylink *phylink;
 	struct mtk_mac *mac;
-	int id, err;
+	int id, err, i;
 	int txqs = 1;
 	u32 val;
 
@@ -4906,8 +4906,8 @@ static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np)
 	mac->phylink_config.type = PHYLINK_NETDEV;
 	mac->phylink_config.mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE |
 		MAC_10 | MAC_100 | MAC_1000 | MAC_2500FD;
-	mac->phylink_config.lpi_capabilities = MAC_100FD | MAC_1000FD |
-		MAC_2500FD;
+	/* LPI above 1 Gbps is not supported */
+	mac->phylink_config.lpi_capabilities = MAC_100FD | MAC_1000FD;
 	mac->phylink_config.lpi_timer_default = 1000;
 
 	/* MT7623 gmac0 is now missing its speed-specific PLL configuration
@@ -4965,6 +4965,18 @@ static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np)
 		__set_bit(PHY_INTERFACE_MODE_INTERNAL,
 			  mac->phylink_config.supported_interfaces);
 
+	/* LPI wake-up timing is only verified on MTK_GMAC_EEE SoCs */
+	if (MTK_HAS_CAPS(eth->soc->caps, MTK_GMAC_EEE)) {
+		phy_interface_copy(mac->phylink_config.lpi_interfaces,
+				   mac->phylink_config.supported_interfaces);
+		__clear_bit(PHY_INTERFACE_MODE_2500BASEX,
+			    mac->phylink_config.lpi_interfaces);
+		for (i = 0; i < PHY_INTERFACE_MODE_MAX; i++)
+			if (mtk_interface_mode_is_xgmii(eth, i))
+				__clear_bit(i,
+					    mac->phylink_config.lpi_interfaces);
+	}
+
 	phylink = phylink_create(&mac->phylink_config,
 				 of_fwnode_handle(mac->of_node),
 				 phy_mode, mac_ops);
drivers/net/ethernet/mediatek/mtk_eth_soc.h +3 -1
index 0168e2fbc6197..88a9b3b23bea5 100644
@@ -994,6 +994,7 @@ enum mkt_eth_capabilities {
 	MTK_U3_COPHY_V2_BIT,
 	MTK_SRAM_BIT,
 	MTK_36BIT_DMA_BIT,
+	MTK_GMAC_EEE_BIT,
 
 	/* MUX BITS*/
 	MTK_ETH_MUX_GDM1_TO_GMAC1_ESW_BIT,
@@ -1034,6 +1035,7 @@ enum mkt_eth_capabilities {
 #define MTK_U3_COPHY_V2		BIT_ULL(MTK_U3_COPHY_V2_BIT)
 #define MTK_SRAM		BIT_ULL(MTK_SRAM_BIT)
 #define MTK_36BIT_DMA	BIT_ULL(MTK_36BIT_DMA_BIT)
+#define MTK_GMAC_EEE		BIT_ULL(MTK_GMAC_EEE_BIT)
 
 #define MTK_ETH_MUX_GDM1_TO_GMAC1_ESW		\
 	BIT_ULL(MTK_ETH_MUX_GDM1_TO_GMAC1_ESW_BIT)
@@ -1117,7 +1119,7 @@ enum mkt_eth_capabilities {
 #define MT7981_CAPS  (MTK_GMAC1_SGMII | MTK_GMAC2_SGMII | MTK_GMAC2_GEPHY | \
 		      MTK_MUX_GMAC12_TO_GEPHY_SGMII | MTK_QDMA | \
 		      MTK_MUX_U3_GMAC2_TO_QPHY | MTK_U3_COPHY_V2 | \
-		      MTK_RSTCTRL_PPE1 | MTK_SRAM)
+		      MTK_RSTCTRL_PPE1 | MTK_SRAM | MTK_GMAC_EEE)
 
 #define MT7986_CAPS  (MTK_GMAC1_SGMII | MTK_GMAC2_SGMII | \
 		      MTK_MUX_GMAC12_TO_GEPHY_SGMII | MTK_QDMA | \
Signature · 1 line
2.53.0
author maintainer
#429 lore raw

PATCH 429/733 net: phy: mediatek-ge: disable EEE on the MT7530 PHY

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Vladislav Karmanov <[email protected]>

[ Upstream commit ccbe7540e4aad0d1c3acc249697350b93ccb8025 ]

The MT7530 internal GE PHY advertises EEE by hardware default, but its EEE support is defective: with EEE advertised, some link partners fail to establish a stable link. On a 2-pair (4-wire) cable where both ends advertise gigabit, 1000BASE-T training cannot succeed, and instead of falling back to 100 Mbps the port loops, so no link or DHCP lease is ever obtained. MediaTek confirms the hardware is the root cause (Landen Chao, 2021): "EEE of the 10-year-old MT7530 internal gephy has many IOT problems, so it is recommended to disable its EEE."

mtk_gephy_config_init() used to clear the EEE advertisement early, but commit af3b4b0e59de ("net: phy: mediatek-ge: do not disable EEE advertisement") removed that on the rationale that the DSA subdriver already performs an early disable. That holds for MT7531, whose mt7531_setup() clears MDIO_AN_EEE_ADV on each switch PHY, but not for the MT7530 PHY: neither the MT7621 integrated switch nor the dedicated MT7530 IC ever had such a loop, so removing it left those boards without any working early EEE disable and the link flapping came back.

Since the broken hardware is the PHY, fix it in the PHY driver so it covers all users of this PHY, integrated in a switch or standalone:

- clear MDIO_AN_EEE_ADV in probe(), as early as possible, before

    anything can negotiate EEE with the link partner;

- clear it again in config_init() and call phy_disable_eee() there. config_init() is what phy_init_hw() replays after a PHY reset, when the register is back at its EEE-advertising hardware default, and it runs after of_set_phy_eee_broken() in phy_probe(), so the eee_disabled_modes mask survives and neither phylib nor userspace can re-enable EEE. dp83867 disables broken EEE from config_init() the same way.

Auto-negotiation then falls back to a stable 100 Mbps link instead of looping at gigabit. Tested on ASUS RT-AX53U (MT7621): with a 2-pair cable on the WAN port, a single clean 100 Mbps link comes up and a DHCP lease is obtained, where the unpatched driver loops.

Fixes af3b4b0e59de ("net: phy: mediatek-ge: do not disable EEE advertisement")
---
 drivers/net/phy/mediatek/mtk-ge.c | 29 +++++++++++++++++++++++++++++
 1 file changed, 29 insertions(+)
drivers/net/phy/mediatek/mtk-ge.c +29 -0
index 73d9b72f9d9e2..96d8ac5154e5e 100644
@@ -62,10 +62,38 @@ static void mtk_gephy_config_init(struct phy_device *phydev)
 		       FIELD_PREP(MTK_MCC_NEARECHO_OFFSET_MASK, 0x3));
 }
 
+static int mt7530_phy_probe(struct phy_device *phydev)
+{
+	/* The MT7530 internal GE PHY has broken EEE: with EEE advertised,
+	 * some link partners fail to establish a stable link (on a 2-pair
+	 * cable, 1000BASE-T training fails and the port loops instead of
+	 * falling back). MediaTek recommends disabling EEE on this PHY.
+	 * Clear the advertisement as early as possible, before anything
+	 * can negotiate EEE with the link partner.
+	 */
+	return phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, 0);
+}
+
 static int mt7530_phy_config_init(struct phy_device *phydev)
 {
+	int ret;
+
 	mtk_gephy_config_init(phydev);
 
+	/* The probe() clear alone is not durable: phy_init_hw() replays only
+	 * ->config_init after a PHY reset, with the register back at its
+	 * EEE-advertising hardware default, and phy_probe() zeroes
+	 * eee_disabled_modes (of_set_phy_eee_broken()) after ->probe already
+	 * ran. Clear the advertisement again and mark EEE disabled, so that
+	 * neither phylib nor userspace can re-enable it; dp83867 disables
+	 * broken EEE from config_init() the same way.
+	 */
+	ret = phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, 0);
+	if (ret)
+		return ret;
+
+	phy_disable_eee(phydev);
+
 	/* Increase post_update_timer */
 	phy_write_paged(phydev, MTK_PHY_PAGE_EXTENDED_3,
 			MTK_PHY_RG_LPI_PCS_DSP_CTRL_REG11, 0x4b);
@@ -100,6 +128,7 @@ static struct phy_driver mtk_gephy_driver[] = {
 	{
 		PHY_ID_MATCH_EXACT(MTK_GPHY_ID_MT7530),
 		.name		= "MediaTek MT7530 PHY",
+		.probe		= mt7530_phy_probe,
 		.config_init	= mt7530_phy_config_init,
 		/* Interrupts are handled by the switch, not the PHY
 		 * itself.
Signature · 1 line
2.53.0
author maintainer
#430 lore raw

PATCH 430/733 net: net_failover: Fix the deadlock in net_failover_slave_name_change()

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Faicker Mo <[email protected]>

[ Upstream commit 985a663bf00799c1daf1c5789efa6406958780c8 ]

This is a sibling fix of commit
b84c5632c7b3 ("net: net_failover: Fix the deadlock in slave register"). There is netdev_lock_ops() in the upper callers, so using netif_open() instead of dev_open().

Call Trace:
 __schedule+0x2bb/0x650
 schedule+0x27/0xb0
 schedule_preempt_disabled+0x15/0x30
 __mutex_lock.constprop.0+0x550/0xaf0
 __mutex_lock_slowpath+0x13/0x20
 mutex_lock+0x3b/0x50
 dev_open+0x3b/0xe0
 net_failover_slave_name_change+0x22/0x40
 failover_event+0xd4/0x1e0
 notifier_call_chain+0x62/0xf0
 raw_notifier_call_chain+0x16/0x30
 call_netdevice_notifiers_info+0x50/0x80
 netif_change_name+0x200/0x330
 do_setlink.isra.0+0xb12/0xdf0
 ? security_capable+0x9a/0x1e0
 ? ns_capable+0x31/0x60
 rtnl_setlink+0x302/0x670
 ? netlink_recvmsg+0x296/0x340
 ? security_capable+0x9a/0x1e0
 ? __pfx_rtnl_setlink+0x10/0x10
 rtnetlink_rcv_msg+0x384/0x460
 ? __pfx_rtnetlink_rcv_msg+0x10/0x10
 netlink_rcv_skb+0x61/0x120
 rtnetlink_rcv+0x15/0x30
 netlink_unicast+0x28f/0x3c0
 netlink_sendmsg+0x216/0x450
 __sys_sendto+0x222/0x230
 __x64_sys_sendto+0x24/0x40
 x64_sys_call+0x1d5d/0x2390
 do_syscall_64+0x105/0x5a0
 ? do_syscall_64+0x140/0x5a0
 ? exc_page_fault+0x94/0x1e0
 entry_SYSCALL_64_after_hwframe+0x76/0x7e
Fixes 7e4d784f5810 ("net: hold netdev instance lock during rtnetlink operations")
---
 drivers/net/net_failover.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/net_failover.c +1 -1
index 3f7d31033bae8..1b5213e870703 100644
@@ -675,7 +675,7 @@ static int net_failover_slave_name_change(struct net_device *slave_dev,
 	/* We need to bring up the slave after the rename by udev in case
 	 * open failed with EBUSY when it was registered.
 	 */
-	dev_open(slave_dev, NULL);
+	netif_open(slave_dev, NULL);
 
 	return 0;
 }
Signature · 1 line
2.53.0
author maintainer
#431 lore raw

PATCH 431/733 octeontx2-af: fix PF/CGX debugfs PCI bus lookup

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Ratheesh Kannoth <[email protected]>

[ Upstream commit 4f4b743c2d2bbc336cb164d9d3d2ed6956ad8437 ]

rvu_dbg_rvu_pf_cgx_map_display() locates each RVU PF PCI device via pci_get_domain_bus_and_slot() when printing the PF-to-CGX map. It assumed PF0 always sits on PCI bus 1 and derived other PF bus numbers as pf + 1, but the AF device can be enumerated on a different bus.

Use rvu->pdev->bus->number as the base bus instead, so each PF lookup uses pf + start on systems where RVU functions are on contiguous buses but do not start at bus 1.

---
 .../net/ethernet/marvell/octeontx2/af/rvu_debugfs.c    | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)
drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c +8 -2
index 22ee996768796..904374baae6f3 100644
@@ -829,19 +829,25 @@ static int rvu_dbg_rvu_pf_cgx_map_display(struct seq_file *filp, void *unused)
 	int pf, domain, blkid;
 	u8 cgx_id, lmac_id;
 	u16 pcifunc;
+	u8 start;
 
-	domain = 2;
+	domain = pci_domain_nr(rvu->pdev->bus);
 	mac_ops = get_mac_ops(rvu_first_cgx_pdata(rvu));
 	/* There can be no CGX devices at all */
 	if (!mac_ops)
 		return 0;
 	seq_printf(filp, "PCI dev\t\tRVU PF Func\tNIX block\t%s\tLMAC\tCHAN\n",
 		   mac_ops->name);
+
+	/* All the PF devices are on contiguous PCI bus numbers, but the PF0(AF)
+	 * may not start from 1 always. Hence get domain and bus from PCI device.
+	 */
+	start = rvu->pdev->bus->number;
 	for (pf = 0; pf < rvu->hw->total_pfs; pf++) {
 		if (!is_pf_cgxmapped(rvu, pf))
 			continue;
 
-		pdev =  pci_get_domain_bus_and_slot(domain, pf + 1, 0);
+		pdev =  pci_get_domain_bus_and_slot(domain, pf + start, 0);
 		if (!pdev)
 			continue;
 
Signature · 1 line
2.53.0
author maintainer
#432 lore raw

PATCH 432/733 net: phy: dp83867: handle the active-high LED polarity mode

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Donggeun Yoo <[email protected]>

[ Upstream commit 36a45facedd5c8e73bfb2403f8b0dbff05124c9c ]

Commit a274465cc3be ("net: phy: support 'active-high' property for PHY LEDs") added PHY_LED_ACTIVE_HIGH and made of_phy_led() set the matching bit in the modes mask when a LED node carries the 'active-high' property. dp83867 was not part of that series.

dp83867_led_polarity_set() only recognizes PHY_LED_ACTIVE_LOW, so PHY_LED_ACTIVE_HIGH falls through to the default case and returns -EINVAL. of_phy_led() propagates the error, of_phy_leds() drops the LEDs registered so far and passes it on, and phy_probe() fails. A device tree marking a DP83867 LED as 'active-high', which leds/common.yaml allows and ethernet-phy.yaml references for led@N nodes, thus stops the PHY from probing.

Active high is what the function programs when no polarity mode is requested at all, so the initial value of polarity already satisfies the request and only the case label is missing.

The same series updated mxl-gpy in commit eb89c79c1b8f ("net: phy:
mxl-gpy: correctly describe LED polarity") and aquantia in commit 9d55e68b19f2 ("net: phy: aquantia: correctly describe LED polarity override").

Fixes a274465cc3be ("net: phy: support 'active-high' property for PHY LEDs")
---
 drivers/net/phy/dp83867.c | 3 +++
 1 file changed, 3 insertions(+)
drivers/net/phy/dp83867.c +3 -0
index 88255e92b4cdb..61a941aa02d94 100644
@@ -1150,6 +1150,9 @@ static int dp83867_led_polarity_set(struct phy_device *phydev, int index,
 		case PHY_LED_ACTIVE_LOW:
 			polarity = 0;
 			break;
+		case PHY_LED_ACTIVE_HIGH:
+			polarity = DP83867_LED_POLARITY(index);
+			break;
 		default:
 			return -EINVAL;
 		}
Signature · 1 line
2.53.0
author maintainer
#433 lore raw

PATCH 433/733 net: mana: restore the XDP program pointer when pre-allocation fails

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Long Li <[email protected]>

[ Upstream commit 4c46beb807efcc93f5899ebe1f5958248eb296c6 ]

mana_xdp_set() publishes the new program into apc->bpf_prog before it allocates anything, because mana_pre_alloc_rxbufs() sizes the buffers from it via mana_get_rxbuf_cfg(). When that allocation fails the function returns the error directly, skipping the err_dealloc_rxbuffs label which is the only place that restores the previous pointer.

The attach is reported as failed, so the BPF core drops the reference it held for the caller and the program can be freed, while apc->bpf_prog still points at it. The next consumer of mana_xdp_get() - typically mana_chn_setxdp() from mana_alloc_queues() on the following ifup, or after a TX timeout reset - then calls bpf_prog_add() on freed memory.

This is reachable from an ordinary "ip link set dev ethX xdp obj ..." whenever the per-queue RX buffer pre-allocation cannot be satisfied.

Restore the previous program on that error path.

Fixes 730ff06d3f5c ("net: mana: Use page pool fragments for RX buffers instead of full pages to improve memory efficiency.")
---
 drivers/net/ethernet/microsoft/mana/mana_bpf.c | 1 +
 1 file changed, 1 insertion(+)
drivers/net/ethernet/microsoft/mana/mana_bpf.c +1 -0
index 53308e139cbe9..5c9961ee9747a 100644
@@ -208,6 +208,7 @@ static int mana_xdp_set(struct net_device *ndev, struct bpf_prog *prog,
 		if (err) {
 			NL_SET_ERR_MSG_MOD(extack,
 					   "XDP: Insufficient memory for tx/rx re-config");
+			apc->bpf_prog = old_prog;
 			return err;
 		}
 
Signature · 1 line
2.53.0
author maintainer
#434 lore raw

PATCH 434/733 net/rds: fix tcp stream corruption with large pages

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Greg Marsden <[email protected]>

[ Upstream commit 2ac09b5353fe6858411fdc8c6efa60d832e20f13 ]

rds_message_map_pages() assigns PAGE_SIZE bytes to every scatterlist entry, even when total_len ends in a partial page. The RDS congestion map is defined as 8192 bytes, so on systems with PAGE_SIZE greater than 8192 the scatterlist maps bytes beyond the end of the congestion map. RDS-TCP transmits the SG contents according to those lengths, so the extra bytes become part of the TCP RDS stream and are interpreted as subsequent RDS message headers, corrupting the stream.

Limit the final scatterlist mapping to the number of bytes remaining. This has no effect on systems with a 4K page size and allows RDS-TCP to be used on systems with 16K and larger page sizes.

The RDS selftest, which previously hung on 16K pages, now passes.

---
 net/rds/message.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
net/rds/message.c +3 -1
index 7feb0eb6537db..9cbf045fe0031 100644
@@ -405,7 +405,9 @@ struct rds_message *rds_message_map_pages(unsigned long *page_addrs, unsigned in
 	for (i = 0; i < rm->data.op_nents; ++i) {
 		sg_set_page(&rm->data.op_sg[i],
 				virt_to_page((void *)page_addrs[i]),
-				PAGE_SIZE, 0);
+				i == rm->data.op_nents - 1
+					? total_len - (i * PAGE_SIZE)
+					: PAGE_SIZE, 0);
 	}
 
 	return rm;
Signature · 1 line
2.53.0
author maintainer
#435 lore raw

PATCH 435/733 net: stmmac: fix TX descriptor availability check for TSO traffic

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Lorenzo Bianconi <[email protected]>

[ Upstream commit 5e38d732ec67a5b1f9a56e6c73add480c4b6030a ]

stmmac_tso_xmit() estimates the number of free TX descriptors required by a TSO skb as:

	(skb->len - proto_hdr_len) / TSO_MAX_BUFF_SIZE + 1

which assumes the payload is split into TSO_MAX_BUFF_SIZE chunks. This underestimates the descriptors actually consumed by stmmac_tso_allocator(), since each fragment is mapped individually and so it needs at least one descriptor regardless of its size. Moreover, one descriptor is used for the L2/L3/L4 headers and, when the MSS changes, one more is consumed for the MSS context descriptor.
For a highly fragmented TSO skb the check can therefore pass even when the ring has too few free slots. stmmac_tso_allocator() then writes past the available descriptors, overwriting descriptors still owned by the DMA engine, corrupting the TX ring.
Add stmmac_tso_get_num_desc() to compute the exact number of descriptors needed for the header, the linear payload and each fragment, plus the MSS context descriptor when required, and use it in the availability check.

---
 .../net/ethernet/stmicro/stmmac/stmmac_main.c | 31 +++++++++++++++----
 1 file changed, 25 insertions(+), 6 deletions(-)
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +25 -6
index aa05160c22e35..f801caf65fff1 100644
@@ -4454,6 +4454,26 @@ static bool stmmac_tso_valid_packet(struct sk_buff *skb)
 	       header_len + gso_size < 16383;
 }
 
+static int stmmac_tso_get_num_desc(struct stmmac_tx_queue *tx_q,
+				   struct sk_buff *skb, u32 pay_len)
+{
+	int i, ndesc = 1;
+
+	/* head payload */
+	ndesc += DIV_ROUND_UP(pay_len, TSO_MAX_BUFF_SIZE);
+	/* frag payload */
+	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+		const skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+
+		ndesc += DIV_ROUND_UP(skb_frag_size(frag),
+				      TSO_MAX_BUFF_SIZE);
+	}
+	/* MSS update requires a new descriptor */
+	ndesc += !!(skb_shinfo(skb)->gso_size != tx_q->mss);
+
+	return ndesc;
+}
+
 /**
  *  stmmac_tso_xmit - Tx entry point of the driver for oversized frames (TSO)
  *  @skb : the socket buffer
@@ -4497,10 +4517,10 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
 	struct stmmac_priv *priv = netdev_priv(dev);
 	unsigned int first_entry, entry, tx_packets;
 	struct stmmac_txq_stats *txq_stats;
+	int i, first_tx, nfrags, ndesc;
 	struct stmmac_tx_queue *tx_q;
 	bool set_ic, is_last_segment;
 	u32 pay_len, mss, queue;
-	int i, first_tx, nfrags;
 	u8 proto_hdr_len, hdr;
 	dma_addr_t des;
 
@@ -4513,14 +4533,15 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
 
 	/* Compute header lengths */
 	proto_hdr_len = stmmac_tso_header_size(skb);
+	pay_len = skb_headlen(skb) - proto_hdr_len; /* no frags */
+
 	if (skb_shinfo(skb)->gso_type & SKB_GSO_UDP_L4)
 		hdr = sizeof(struct udphdr);
 	else
 		hdr = tcp_hdrlen(skb);
 
-	/* Desc availability based on threshold should be enough safe */
-	if (unlikely(stmmac_tx_avail(priv, queue) <
-		(((skb->len - proto_hdr_len) / TSO_MAX_BUFF_SIZE + 1)))) {
+	ndesc = stmmac_tso_get_num_desc(tx_q, skb, pay_len);
+	if (unlikely(stmmac_tx_avail(priv, queue) < ndesc)) {
 		if (!netif_tx_queue_stopped(netdev_get_tx_queue(dev, queue))) {
 			netif_tx_stop_queue(netdev_get_tx_queue(priv->dev,
 								queue));
@@ -4532,8 +4553,6 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
 		return NETDEV_TX_BUSY;
 	}
 
-	pay_len = skb_headlen(skb) - proto_hdr_len; /* no frags */
-
 	mss = skb_shinfo(skb)->gso_size;
 
 	/* set new MSS value if needed */
Signature · 1 line
2.53.0
author maintainer
#436 lore raw

PATCH 436/733 net: hsr: enable promiscuous mode on interlink port with fwd offload

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: MD Danish Anwar <[email protected]>

[ Upstream commit a2dc179481d18f6df7274522571b64dd50f31e81 ]

hsr_portdev_setup() skips promiscuous mode on non-master ports when hsr->fwd_offloaded is set. fwd_offloaded is derived only from the ring slaves' NETIF_F_HW_HSR_FWD bit, so this also skips it for the interlink port, which never gets forwarding offload. Without promiscuous mode, the interlink NIC drops unicast frames addressed to hsr_dev's MAC (e.g. SAN traffic to the RedBox), breaking RedBox whenever the ring is HW-offloaded.

---
 net/hsr/hsr_slave.c | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)
net/hsr/hsr_slave.c +7 -4
index 01c73b4b50ddd..a546f70f9cc8b 100644
@@ -149,9 +149,12 @@ static int hsr_portdev_setup(struct hsr_priv *hsr, struct net_device *dev,
 	int res;
 
 	/* Don't use promiscuous mode for offload since L2 frame forward
-	 * happens at the offloaded hardware.
+	 * happens at the offloaded hardware. The interlink port never
+	 * gets forwarding offload (RedBox forwarding to/from it is done
+	 * by this driver), so it still needs promiscuous mode to receive
+	 * frames addressed to hsr_dev's MAC rather than its own.
 	 */
-	if (!port->hsr->fwd_offloaded) {
+	if (!port->hsr->fwd_offloaded || port->type == HSR_PT_INTERLINK) {
 		res = dev_set_promiscuity(dev, 1);
 		if (res)
 			return res;
@@ -176,7 +179,7 @@ static int hsr_portdev_setup(struct hsr_priv *hsr, struct net_device *dev,
 fail_rx_handler:
 	netdev_upper_dev_unlink(dev, hsr_dev);
 fail_upper_dev_link:
-	if (!port->hsr->fwd_offloaded)
+	if (!port->hsr->fwd_offloaded || port->type == HSR_PT_INTERLINK)
 		dev_set_promiscuity(dev, -1);
 
 	return res;
@@ -240,7 +243,7 @@ void hsr_del_port(struct hsr_port *port)
 		netdev_update_features(master->dev);
 		dev_set_mtu(master->dev, hsr_get_max_mtu(hsr));
 		netdev_rx_handler_unregister(port->dev);
-		if (!port->hsr->fwd_offloaded)
+		if (!port->hsr->fwd_offloaded || port->type == HSR_PT_INTERLINK)
 			dev_set_promiscuity(port->dev, -1);
 		if (port->type == HSR_PT_SLAVE_A || port->type == HSR_PT_SLAVE_B)
 			vlan_vids_del_by_dev(port->dev, master->dev);
Signature · 1 line
2.53.0
author maintainer
#437 lore raw

PATCH 437/733 openvswitch: fix wrong flag value in get_ipv6_ext_hdrs()

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Eelco Chaudron <[email protected]>

[ Upstream commit e184a4a6f423550a25adce867036cdb1ff471745 ]

The ESP and AH cases in get_ipv6_ext_hdrs() used IPPROTO_FRAGMENT instead of OFPIEH12_FRAG when checking for out-of-order extension headers, causing the fragment header to not be recognised as a valid predecessor.

The original code used IPPROTO_FRAGMENT (44) as a bitmask constant where OFPIEH12_FRAG (1 << 4 = 16) was intended. IPPROTO_FRAGMENT encodes bits 2, 3 and 5 (OFPIEH12_AUTH | OFPIEH12_DEST | OFPIEH12_ROUTER), but not bit 4 (OFPIEH12_FRAG). This caused incorrect OFPIEH12_UNSEQ verdicts in both the ESP and AH arms: the ESP arm failed to whitelist OFPIEH12_FRAG, while the AH arm accidentally whitelisted OFPIEH12_AUTH.

With the fix, a packet with two AH headers now also gets OFPIEH12_UNSEQ in addition to OFPIEH12_UNREP, matching the ESP arm which already sets UNSEQ on a repeat, which is the intended behavior.

---
 net/openvswitch/flow.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
net/openvswitch/flow.c +2 -2
index 46c1d66aad8c3..868d9fdf3afdf 100644
@@ -288,7 +288,7 @@ static void get_ipv6_ext_hdrs(struct sk_buff *skb, struct ipv6hdr *nh,
 			if (*ext_hdrs & OFPIEH12_ESP)
 				*ext_hdrs |= OFPIEH12_UNREP;
 			if ((*ext_hdrs & ~(OFPIEH12_HOP | OFPIEH12_DEST |
-					   OFPIEH12_ROUTER | IPPROTO_FRAGMENT |
+					   OFPIEH12_ROUTER | OFPIEH12_FRAG |
 					   OFPIEH12_AUTH | OFPIEH12_UNREP)) ||
 			    dest_options_header_count >= 2) {
 				*ext_hdrs |= OFPIEH12_UNSEQ;
@@ -301,7 +301,7 @@ static void get_ipv6_ext_hdrs(struct sk_buff *skb, struct ipv6hdr *nh,
 				*ext_hdrs |= OFPIEH12_UNREP;
 			if ((*ext_hdrs &
 			     ~(OFPIEH12_HOP | OFPIEH12_DEST | OFPIEH12_ROUTER |
-			       IPPROTO_FRAGMENT | OFPIEH12_UNREP)) ||
+			       OFPIEH12_FRAG | OFPIEH12_UNREP)) ||
 			    dest_options_header_count >= 2) {
 				*ext_hdrs |= OFPIEH12_UNSEQ;
 			}
Signature · 1 line
2.53.0
author maintainer
#438 lore raw

PATCH 438/733 block: Fix start and length check added to iov_iter_extract_bvecs()

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: David Howells <[email protected]>

[ Upstream commit b0d8d56b7c93ed767eb4f2be9988e7b9dc023566 ]

Commit 14b007e17881 added an address check using iter_iov_addr() and a length check using iter_iov_len() to iov_iter_extract_bvecs(), but these cannot be used so and are unsafe in this circumstance as the functions have hardwired assumptions about the iterator type. They should only be used with ITER_UBUF or ITER_IOVEC-type iterators; they shouldn't be used with ITER_BVEC, ITER_KVEC, ITER_FOLIOQ, ITER_XARRAY or ITER_DISCARD iterators.

This proves to be a problem for cachefiles as an iterator of type ITER_FOLIOQ is passed and iter_iov_addr() and iter_iov_len() both malfunction because iter->__iov in iter_iov() is not pointing to an iovec array.

Fix this by using iov_iter_alignment() instead.

Fixes 14b007e17881 ("block: validate user space vectors during extraction")
cc Hannes Reinecke <[email protected]>
cc Christoph Hellwig <[email protected]>
cc Jens Axboe <[email protected]>
cc Alexander Viro <[email protected]>
cc Paulo Alcantara <[email protected]>
---
 lib/iov_iter.c | 18 ++++++++++++++++--
 1 file changed, 16 insertions(+), 2 deletions(-)
lib/iov_iter.c +16 -2
index 34a52e9ba9e1b..5238731910917 100644
@@ -1920,15 +1920,29 @@ ssize_t iov_iter_extract_bvecs(struct iov_iter *iter, struct bio_vec *bv,
 		unsigned short max_vecs, unsigned mem_align_mask,
 		iov_iter_extraction_t extraction_flags)
 {
-	unsigned long start = (unsigned long)iter_iov_addr(iter);
 	unsigned short entries_left = max_vecs - *nr_vecs;
 	unsigned short nr_pages, i = 0;
 	size_t left, offset, len;
 	struct page **pages;
 	ssize_t size;
 
-	if ((start | iter_iov_len(iter)) & mem_align_mask)
+	/*
+	 * DMA engines typically have both memory address and length alignment
+	 * requirements, so check these against the alignment mask.  For UBUF,
+	 * IOVEC and KVEC, only the current segment will be extracted from; for
+	 * everything else we might extract from multiple segments, so we need
+	 * to check those too.
+	 */
+	if (likely(iter_is_ubuf(iter) ||
+		   iter_is_iovec(iter) ||
+		   iov_iter_is_kvec(iter))) {
+		unsigned long start = (unsigned long)iter_iov_addr(iter);
+
+		if ((start | iter_iov_len(iter)) & mem_align_mask)
+			return -EINVAL;
+	} else if (iov_iter_alignment(iter) & mem_align_mask) {
 		return -EINVAL;
+	}
 
 	/*
 	 * Move page array up in the allocated memory for the bio vecs as far as
Signature · 1 line
2.53.0
author maintainer
#439 lore raw

PATCH 439/733 sunvdc: unmap LDC cookies when the descriptor send fails

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Stian Halseth <[email protected]>

[ Upstream commit 0c6da21fa35e03fc74f09895433ccd6d4a9c3530 ]

__send_request() maps the request's pages into the LDC channel's map table (ldc_map_sg()), fills in the descriptor and marks it VIO_DESC_READY before ringing the doorbell via __vdc_tx_trigger(). When the trigger fails, the error path only prints a message: the descriptor stays READY and the cookies are never unmapped. The mapping is normally released in vdc_end_one() when the peer completes the descriptor - but a descriptor whose doorbell was never sent will never complete, and since dr->prod is not advanced on failure, the reset path (vdc_requeue_inflight(), which walks [cons, prod)) never visits it either. The map table entries are leaked permanently.

Since commit a11f6ca9aef9 ("sunvdc: Do not spin in an infinite loop when vio_ldc_send() returns EAGAIN") trigger failures occur in practice under load, so every resulting I/O error also leaks one request's worth of entries from the fixed-size (8192 entries per channel) map table. Because the allocator hands out contiguous ranges, fragmentation makes large multi-segment requests fail first as the table drains, until ldc_map_sg() fails permanently and the disk is dead until reboot.

It also makes any retry-based recovery unusable: requeuing the request on -EAGAIN remaps the pages on every attempt, overwriting desc->cookies and orphaning the previous mapping, so the table drains at the retry rate. This is the memory exhaustion observed when the requeue approach was first tested in October 2025.

Roll back on failure: unmap the cookies, mark the descriptor FREE again and clear the request entry. If the trigger failed with -ENOTCONN, __vdc_tx_trigger() has already reset the port, which tears down and reallocates both the dring and the LDC channel including its map table - nothing to roll back, and the stale descriptor must not be touched.

Fixes a11f6ca9aef9 ("sunvdc: Do not spin in an infinite loop when vio_ldc_send() returns EAGAIN")
---
 drivers/block/sunvdc.c | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)
drivers/block/sunvdc.c +17 -0
index 020bd9f1a7b6a..24ad56536ed60 100644
@@ -525,6 +525,23 @@ static int __send_request(struct request *req)
 	err = __vdc_tx_trigger(port);
 	if (err < 0) {
 		printk(KERN_ERR PFX "vdc_tx_trigger() failure, err=%d\n", err);
+		/*
+		 * If the port was reset (-ENOTCONN), the dring and the
+		 * LDC channel including all of its mappings are already
+		 * torn down and reallocated - there is nothing to undo
+		 * and @desc must not be touched.
+		 *
+		 * For any other failure the descriptor was never handed
+		 * to the peer: unmap the cookies and free the descriptor
+		 * again, so that a later retry of the request does not
+		 * leak LDC map table entries.
+		 */
+		if (err != -ENOTCONN) {
+			ldc_unmap(port->vio.lp, desc->cookies,
+				  desc->ncookies);
+			desc->hdr.state = VIO_DESC_FREE;
+			rqe->req = NULL;
+		}
 	} else {
 		port->req_id++;
 		dr->prod = vio_dring_next(dr, dr->prod);
Signature · 1 line
2.53.0
#440 lore raw

Re: [PATCH 7.2 439/733] sunvdc: unmap LDC cookies when the descriptor send fails

Hi,

No objection to this patch, but please also queue its companion

5067d4ba7139 ("sunvdc: fix -EIO issue due to lack of retries")

which was applied to mainline together with this one. The same applies to the 6.18 and 6.12 queues, which carry this patch as well.

With both applied, the stable trees match mainline behavior.

Order matters: 0c6da21fa35e must go first (it already is, in the queues).

Best regards
Stian Halseth

Quoted 82 lines · Greg Kroah-Hartman

7.2-stable review patch.  If anyone has any objections, please let me know.

------------------

From: Stian Halseth <[email protected]>

[ Upstream commit 0c6da21fa35e03fc74f09895433ccd6d4a9c3530 ]

__send_request() maps the request's pages into the LDC channel's map table (ldc_map_sg()), fills in the descriptor and marks it VIO_DESC_READY before ringing the doorbell via __vdc_tx_trigger(). When the trigger fails, the error path only prints a message: the descriptor stays READY and the cookies are never unmapped. The mapping is normally released in vdc_end_one() when the peer completes the descriptor - but a descriptor whose doorbell was never sent will never complete, and since dr->prod is not advanced on failure, the reset path (vdc_requeue_inflight(), which walks [cons, prod)) never visits it either. The map table entries are leaked permanently.

Since commit a11f6ca9aef9 ("sunvdc: Do not spin in an infinite loop when vio_ldc_send() returns EAGAIN") trigger failures occur in practice under load, so every resulting I/O error also leaks one request's worth of entries from the fixed-size (8192 entries per channel) map table. Because the allocator hands out contiguous ranges, fragmentation makes large multi-segment requests fail first as the table drains, until ldc_map_sg() fails permanently and the disk is dead until reboot.

It also makes any retry-based recovery unusable: requeuing the request on -EAGAIN remaps the pages on every attempt, overwriting desc->cookies and orphaning the previous mapping, so the table drains at the retry rate. This is the memory exhaustion observed when the requeue approach was first tested in October 2025.

Roll back on failure: unmap the cookies, mark the descriptor FREE again and clear the request entry. If the trigger failed with -ENOTCONN, __vdc_tx_trigger() has already reset the port, which tears down and reallocates both the dring and the LDC channel including its map table - nothing to roll back, and the stale descriptor must not be touched.

Fixes a11f6ca9aef9 ("sunvdc: Do not spin in an infinite loop when

vio_ldc_send() returns EAGAIN")

---
 drivers/block/sunvdc.c | 17 +++++++++++++++++

 1 file changed, 17 insertions(+)
drivers/block/sunvdc.c +17 -0
index 020bd9f1a7b6a..24ad56536ed60 100644
@@ -525,6 +525,23 @@ static int __send_request(struct request *req)
   err = __vdc_tx_trigger(port);
   if (err < 0) {
   printk(KERN_ERR PFX "vdc_tx_trigger() failure, err=%d\n", err);
+ /*
+ * If the port was reset (-ENOTCONN), the dring and the
+ * LDC channel including all of its mappings are already
+ * torn down and reallocated - there is nothing to undo
+ * and @desc must not be touched.
+ *
+ * For any other failure the descriptor was never handed
+ * to the peer: unmap the cookies and free the descriptor
+ * again, so that a later retry of the request does not
+ * leak LDC map table entries.
+ */
+ if (err != -ENOTCONN) {
+ ldc_unmap(port->vio.lp, desc->cookies,
+   desc->ncookies);
+ desc->hdr.state = VIO_DESC_FREE;
+ rqe->req = NULL;
+ }
   } else {
   port->req_id++;
   dr->prod = vio_dring_next(dr, dr->prod);
maintainer Applied
#441 lore raw

Re: [PATCH 7.2 439/733] sunvdc: unmap LDC cookies when the descriptor send fails

Quoted 6 lines

No objection to this patch, but please also queue its companion

5067d4ba7139 ("sunvdc: fix -EIO issue due to lack of retries")

which was applied to mainline together with this one. The same applies to the 6.18 and 6.12 queues, which carry this patch as well.

Queued for 7.2, 6.18, 6.12, 6.6, 6.1, 5.15 and 5.10, thanks.

Signature · 2 lines
Thanks,
Sasha
author maintainer
#442 lore raw

PATCH 440/733 ublk: clear force_abort in ublk_queue_reset_io_flags()

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Yang Xiuwei <[email protected]>

[ Upstream commit 8a14be55bdc6d5a25cd7b0ac5d4d884fcc727b49 ]

Quiesce sets ubq->force_abort for batch I/O. Recovery never clears it, so batch fetch keeps failing with -ENODEV and the device stays QUIESCED.

Fixes a4d883755399 ("ublk: add UBLK_U_IO_FETCH_IO_CMDS for batch I/O processing")
---
 drivers/block/ublk_drv.c | 1 +
 1 file changed, 1 insertion(+)
drivers/block/ublk_drv.c +1 -0
index b71283588ea77..d879e8492d59c 100644
@@ -3040,6 +3040,7 @@ static void ublk_queue_reset_io_flags(struct ublk_queue *ubq)
 	ubq->canceling = false;
 	spin_unlock(&ubq->cancel_lock);
 	ubq->fail_io = false;
+	ubq->force_abort = false;
 }
 
 /* device can only be started after all IOs are ready */
Signature · 1 line
2.53.0
author maintainer
#443 lore raw

PATCH 441/733 erofs: add missing buf->off in erofs_bread()

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Binglei Wang <[email protected]>

[ Upstream commit 135d84c66f85426299db01a09d93a79a87af18ba ]

erofs_bread() locates the target folio with

index = (buf->off + offset) >> PAGE_SHIFT;

but computes the in-folio offset without taking buf->off into account:

return buf->base + (offset & ~PAGE_MASK);

If buf->off is not page-aligned, the returned pointer misses the in-page component of buf->off, so callers end up fetching data from a wrong offset.

buf->off is set to sbi->dif0.fsoff in erofs_init_metabuf(), and fsoff can be specified via the "fsoffset=" mount option, which only requires block-size alignment. Therefore, on an image with a sub-page block size (e.g. 512 bytes), a non-page-aligned fsoff (e.g. 512) triggers the issue, since 512 is a multiple of the block size but not of PAGE_SIZE.

It can be reproduced by mounting an image that is placed at a non-page-aligned offset:

mkfs.erofs -b512 -zlz4hc sub.erofs src/

 # prepend 512 bytes of padding to the image
 mount -t erofs -o loop,fsoffset=512 padded.erofs /mnt

which fails with

erofs (device loop0): cannot find valid erofs superblock

because the on-disk superblock (at offset 1024 within the image, i.e. 1536 within the padded file) is read from a wrong in-folio offset. With this fixed, the very same image mounts successfully and its file contents match those read from the unpadded image.

Fix it by including buf->off in the in-folio offset calculation, so that it is consistent with the folio index calculation.

Fixes c36ec00d7f67 ("erofs: add 'fsoffset' mount option to specify filesystem offset")
---
 fs/erofs/data.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
fs/erofs/data.c +1 -1
index 9aa48c8d67d12..b6cdffd0d8936 100644
@@ -61,7 +61,7 @@ void *erofs_bread(struct erofs_buf *buf, erofs_off_t offset, bool need_kmap)
 		return NULL;
 	if (!buf->base)
 		buf->base = kmap_local_page(buf->page);
-	return buf->base + (offset & ~PAGE_MASK);
+	return buf->base + ((buf->off + offset) & ~PAGE_MASK);
 }
 
 int erofs_init_metabuf(struct erofs_buf *buf, struct super_block *sb,
Signature · 1 line
2.53.0
author maintainer
#444 lore raw

PATCH 442/733 tracing: Fix ring_buffer_read_page_size() kernel-doc

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Karl Mehltretter <[email protected]>

[ Upstream commit 7e645147dfba67edb3ed3090a1ed1d89df77fc27 ]

ring_buffer_read_page_size() takes a parameter named rpage, but its kernel-doc describes page. As a result, kernel-doc reports rpage as undescribed and page as an excess parameter description.

Rename the documentation entry to match the function.

Fixes dae8dda341d2 ("tracing: Fix subbuf resize races with trace_pipe_raw readers")
Assisted-by LLM
---
 kernel/trace/ring_buffer.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/trace/ring_buffer.c +1 -1
index 66760542fce10..f958538c649ae 100644
@@ -7368,7 +7368,7 @@ EXPORT_SYMBOL_GPL(ring_buffer_read_page_data);
 
 /**
  * ring_buffer_read_page_size - get size of the read page.
- * @page:  the page to get the size from
+ * @rpage:  the page to get the size from
  *
  * Returns size of the page in bytes.
  */
Signature · 1 line
2.53.0
author maintainer
#445 lore raw

PATCH 443/733 scripts/mksysmap: drop the MODULE_INFO() symbols from kallsyms

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Lorenzo Stoakes (ARM) <[email protected]>

[ Upstream commit 281b61d408d4c39544583e393c6707af0ef5ee50 ]

Commit 3e86e4d74c04 ("kbuild: keep .modinfo section in vmlinux.unstripped") keeps .modinfo symbols out of System.map and kallsyms, which assumes unique IDs have a format like '__UNIQUE_ID_modinfo123'.

However, commit afb026b6d35c ("compiler: Tweak __UNIQUE_ID() naming"), sent in the same cycle, changes this to '__UNIQUE_ID_modinfo_123'.

As a result this regexp has never matched and every kernel since v6.18 has carried one kallsyms entries for every MODULE_INFO() declaration in the kernel whether the modules are compiled or not.

That's 5,810 entries for an x86 defconfig build and 15,200 for arm64.

On x86 defconfig that is 113 KiB of kallsyms tables and 32 KiB of bzImage, and every lookup walks past them.

Fix the pattern.

Fixes 3e86e4d74c04 ("kbuild: keep .modinfo section in vmlinux.unstripped")
Assisted-by LLM
---
 scripts/mksysmap | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
scripts/mksysmap +1 -1
index c4531eacde202..56a8b8bbdb373 100755
@@ -83,7 +83,7 @@
 / _SDA2_BASE_$/d
 
 # MODULE_INFO()
-/ __UNIQUE_ID_modinfo[0-9]*$/d
+/ __UNIQUE_ID_modinfo_[0-9]*$/d
 
 # ---------------------------------------------------------------------------
 # Ignored patterns
Signature · 1 line
2.53.0
author maintainer
#446 lore raw

PATCH 444/733 scripts/mksysmap: fix escape of $ in the __pi_ pattern

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Lorenzo Stoakes (ARM) <[email protected]>

[ Upstream commit 59351365ac271b5e0eb180f211c531476a36221f ]

Commit b18b047002b7 ("kbuild: change scripts/mksysmap into sed script") converted scripts/mksysmap from a shell script to a sed script.

However an error was made - escaping of '$' required \\ escaping in shell but only \ in a sed script.

This was mostly corrected in commit 7a6c355b55c0 ("scripts/mksysmap: Fix escape chars '$'"), but this fix missed arm64 PIE namespace local symbols like __pi_$x and __pi_$d which appear in System.map and /proc/kallsyms:

$ grep __pi_\\$ /proc/kallsyms | sort -u
0000000000000000 d __pi_$d
0000000000000000 t __pi_$x

Fix the escaping properly.

Fixes b18b047002b7 ("kbuild: change scripts/mksysmap into sed script")
Assisted-by LLM
---
 scripts/mksysmap | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
scripts/mksysmap +1 -1
index 56a8b8bbdb373..856b26ba2ac02 100755
@@ -35,7 +35,7 @@
 / __efistub_/d
 
 # arm64 local symbols in PIE namespace
-/ __pi_\\$/d
+/ __pi_\$/d
 / __pi_\.L/d
 
 # arm64 local symbols in non-VHE KVM namespace
Signature · 1 line
2.53.0
author maintainer
#447 lore raw

PATCH 445/733 tracing/remotes: Account for ring buffer page header in size calculation

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Vincent Donnefort <[email protected]>

[ Upstream commit 442ffa742daa65a0e8fe003abe9fbe472366e4de ]

trace_buffer_desc_size() and trace_remote_alloc_buffer() undercount the required pages because every ring buffer page contains a header (BUF_PAGE_HDR_SIZE). Account for that header to ensure allocated remote ring buffers aren't smaller than requested by the user.

The newly introduced helper __calc_nr_pages_ring_buffer_desc() can return a value that overflows the descriptor nr_pages field (32 bits).

Fixes 2e67fabd8b77 ("ring-buffer: Introduce ring-buffer remotes")
---
 include/linux/ring_buffer.h | 15 +++++++++++++--
 kernel/trace/trace_remote.c |  2 +-
 2 files changed, 14 insertions(+), 3 deletions(-)
include/linux/ring_buffer.h +13 -2
index afc7daa6ee7dc..11bffb6a142d1 100644
@@ -3,8 +3,9 @@
 #define _LINUX_RING_BUFFER_H
 
 #include <linux/mm.h>
-#include <linux/seq_file.h>
 #include <linux/poll.h>
+#include <linux/ring_buffer_types.h>
+#include <linux/seq_file.h>
 
 #include <uapi/linux/trace_mmap.h>
 
@@ -279,9 +280,19 @@ static inline struct ring_buffer_desc *__first_ring_buffer_desc(struct trace_buf
 	return (struct ring_buffer_desc *)(&desc->__data[0]);
 }
 
+/*
+ * Returns the number of pages for a ring_buffer_desc. The caller must ensure it
+ * does not overflow ring_buffer_desc::nr_page_va.
+ */
+static inline unsigned long __calc_nr_pages_ring_buffer_desc(size_t size)
+{
+	/* Takes into account the reader page */
+	return max(DIV_ROUND_UP(size, PAGE_SIZE - BUF_PAGE_HDR_SIZE), 2UL) + 1;
+}
+
 static inline size_t trace_buffer_desc_size(size_t buffer_size, unsigned int nr_cpus)
 {
-	unsigned int nr_pages = max(DIV_ROUND_UP(buffer_size, PAGE_SIZE), 2UL) + 1;
+	unsigned long nr_pages = __calc_nr_pages_ring_buffer_desc(buffer_size);
 	struct ring_buffer_desc *rbdesc;
 
 	return size_add(offsetof(struct trace_buffer_desc, __data),
kernel/trace/trace_remote.c +1 -1
index e6724f947170d..0e8ca62301040 100644
@@ -980,7 +980,7 @@ int trace_remote_alloc_buffer(struct trace_buffer_desc *desc, size_t desc_size,
 			      const struct cpumask *cpumask)
 {
 	size_t min_desc_size = trace_buffer_desc_size(buffer_size, cpumask_weight(cpumask));
-	unsigned int nr_pages = max(DIV_ROUND_UP(buffer_size, PAGE_SIZE), 2UL) + 1;
+	unsigned int nr_pages = __calc_nr_pages_ring_buffer_desc(buffer_size);
 	struct ring_buffer_desc *rb_desc;
 	int cpu, ret = -ENOMEM;
 
Signature · 1 line
2.53.0
author maintainer
#448 lore raw

PATCH 446/733 tracing/remotes: Catch nr_page_va overflow in ring_buffer_desc sizing

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Vincent Donnefort <[email protected]>

[ Upstream commit d059d8bf2c9b5d563d15e7552d73e17d7535013a ]

The number of pages per remote ring buffer is capped by ring_buffer_desc::nr_page_va (32 bits). A buffer_size large enough to overflow that field would silently allocate a descriptor smaller than what was asked for.

Return SIZE_MAX from trace_buffer_desc_size() on nr_page_va overflow.

Fixes 2e67fabd8b77 ("ring-buffer: Introduce ring-buffer remotes")
---
 include/linux/ring_buffer.h | 4 ++++
 kernel/trace/trace_remote.c | 6 +++++-
 2 files changed, 9 insertions(+), 1 deletion(-)
include/linux/ring_buffer.h +4 -0
index 11bffb6a142d1..eac3e9080c3c0 100644
@@ -295,6 +295,10 @@ static inline size_t trace_buffer_desc_size(size_t buffer_size, unsigned int nr_
 	unsigned long nr_pages = __calc_nr_pages_ring_buffer_desc(buffer_size);
 	struct ring_buffer_desc *rbdesc;
 
+	/* Capped by ring_buffer_desc::nr_page_va */
+	if (nr_pages > UINT_MAX)
+		return SIZE_MAX;
+
 	return size_add(offsetof(struct trace_buffer_desc, __data),
 			size_mul(nr_cpus, struct_size(rbdesc, page_va, nr_pages)));
 }
kernel/trace/trace_remote.c +5 -1
index 0e8ca62301040..c79cd21347a03 100644
@@ -980,9 +980,12 @@ int trace_remote_alloc_buffer(struct trace_buffer_desc *desc, size_t desc_size,
 			      const struct cpumask *cpumask)
 {
 	size_t min_desc_size = trace_buffer_desc_size(buffer_size, cpumask_weight(cpumask));
-	unsigned int nr_pages = __calc_nr_pages_ring_buffer_desc(buffer_size);
 	struct ring_buffer_desc *rb_desc;
 	int cpu, ret = -ENOMEM;
+	unsigned int nr_pages;
+
+	if (min_desc_size == SIZE_MAX)
+		return -E2BIG;
 
 	if (desc_size < min_desc_size)
 		return -EINVAL;
@@ -991,6 +994,7 @@ int trace_remote_alloc_buffer(struct trace_buffer_desc *desc, size_t desc_size,
 	desc->struct_len = min_desc_size;
 
 	rb_desc = __first_ring_buffer_desc(desc);
+	nr_pages = __calc_nr_pages_ring_buffer_desc(buffer_size);
 
 	for_each_cpu(cpu, cpumask) {
 		unsigned int id;
Signature · 1 line
2.53.0
author maintainer
#449 lore raw

PATCH 447/733 scsi: target: iscsi: Fix hang for aborted WRITE_PENDING commands

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Maurizio Lombardi <[email protected]>

[ Upstream commit d5869dae5080e976d4b03cc33eb7ceb527f242bf ]

When a LUN_RESET aborts a WRITE command that is in the TRANSPORT_WRITE_PENDING state, the target core sets CMD_T_ABORTED and waits for the frontend to finish processing.

If the initiator subsequently sends the remaining dataout PDUs, __iscsit_check_dataout_hdr() catches the payload, stops the dataout timer if the sequence is final and finally dumps the data. However, the iSCSI target doesn't trigger the completion process for these aborted commands. Because of this, the abort path hangs indefinitely in target_put_cmd_and_wait(), leading to a deadlocked target worker thread.

Fix this by explicitly calling target_complete_cmd() when the final dataout PDU is received for an aborted WRITE command. target_complete_cmd() detects the CMD_T_ABORTED flag and cleanly routes the command into target_abort_work, allowing the abort completion to successfully unblock.

---
 drivers/target/iscsi/iscsi_target.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/target/iscsi/iscsi_target.c +3 -1
index 62ada3a52210e..124ff269b8e75 100644
@@ -1533,8 +1533,10 @@ __iscsit_check_dataout_hdr(struct iscsit_conn *conn, void *buf,
 		 */
 		if (se_cmd->transport_state & CMD_T_ABORTED) {
 			if (hdr->flags & ISCSI_FLAG_CMD_FINAL &&
-			    --cmd->outstanding_r2ts < 1)
+			    --cmd->outstanding_r2ts < 1) {
 				iscsit_stop_dataout_timer(cmd);
+				target_complete_cmd(se_cmd, SAM_STAT_TASK_ABORTED);
+			}
 
 			return iscsit_dump_data_payload(conn, payload_length, 1);
 		}
Signature · 1 line
2.53.0
author maintainer
#450 lore raw

PATCH 448/733 configfs: pin the symlink targets dirent instead of chasing ->ci_dentry

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Vasileios Almpanis <[email protected]>

commit a7c1290eef60711c10289c056ad32ed1f2b47b12 upstream.

create_link() reads the target's configfs_dirent from item->ci_dentry->d_fsdata, relying on the item reference taken by get_target(). That reference pins the item, not its dentry: the dentry is pinned by DCACHE_PERSISTENT, which configfs_remove_dir() releases via simple_rmdir() while the item is still alive. A symlink racing with rmdir of its target can therefore find ->ci_dentry freed and its dirent released, triggering WARN_ON(!atomic_read(&sd->s_count)) in configfs_get().

Take the dirent in get_target() as well, under ->d_lock and atomically with the item reference, and pass it down to create_link(). A hashed dentry has not been killed yet, so its ->d_fsdata reference keeps the dirent alive there.

---
 fs/configfs/symlink.c |   24 ++++++++++++++++++++----
 1 file changed, 20 insertions(+), 4 deletions(-)
fs/configfs/symlink.c +20 -4
@@ -76,9 +76,9 @@ static int configfs_get_target_path(stru
 
 static int create_link(struct config_item *parent_item,
 		       struct config_item *item,
+		       struct configfs_dirent *target_sd,
 		       struct dentry *dentry)
 {
-	struct configfs_dirent *target_sd = item->ci_dentry->d_fsdata;
 	char *body;
 	int ret;
 
@@ -115,6 +115,7 @@ static int create_link(struct config_ite
 
 
 static int get_target(const char *symname, struct config_item **target,
+		      struct configfs_dirent **target_sd,
 		      struct super_block *sb)
 {
 	struct path path __free(path_put) = {};
@@ -125,7 +126,20 @@ static int get_target(const char *symnam
 		return ret;
 	if (path.dentry->d_sb != sb)
 		return -EPERM;
-	*target = configfs_get_config_item(path.dentry);
+	/*
+	 * A hashed dentry guarantees that neither the item nor the dirent
+	 * have been released yet, as removals unhash before dropping.
+	 * Grab both references here. An item reference alone would not keep
+	 * ->ci_dentry alive.
+	 */
+	spin_lock(&path.dentry->d_lock);
+	if (!d_unhashed(path.dentry)) {
+		struct configfs_dirent *sd = path.dentry->d_fsdata;
+
+		*target = config_item_get(sd->s_element);
+		*target_sd = configfs_get(sd);
+	}
+	spin_unlock(&path.dentry->d_lock);
 	if (!*target)
 		return -ENOENT;
 	return 0;
@@ -139,6 +153,7 @@ int configfs_symlink(struct mnt_idmap *i
 	struct configfs_dirent *sd;
 	struct config_item *parent_item;
 	struct config_item *target_item = NULL;
+	struct configfs_dirent *target_sd = NULL;
 	const struct config_item_type *type;
 
 	sd = dentry->d_parent->d_fsdata;
@@ -182,7 +197,7 @@ int configfs_symlink(struct mnt_idmap *i
 	 *  AV, a thoroughly annoyed bastard.
 	 */
 	inode_unlock(dir);
-	ret = get_target(symname, &target_item, dentry->d_sb);
+	ret = get_target(symname, &target_item, &target_sd, dentry->d_sb);
 	inode_lock(dir);
 	if (ret)
 		goto out_put;
@@ -196,13 +211,14 @@ int configfs_symlink(struct mnt_idmap *i
 		ret = type->ct_item_ops->allow_link(parent_item, target_item);
 	if (!ret) {
 		mutex_lock(&configfs_symlink_mutex);
-		ret = create_link(parent_item, target_item, dentry);
+		ret = create_link(parent_item, target_item, target_sd, dentry);
 		mutex_unlock(&configfs_symlink_mutex);
 		if (ret && type->ct_item_ops->drop_link)
 			type->ct_item_ops->drop_link(parent_item,
 						     target_item);
 	}
 
+	configfs_put(target_sd);
 	config_item_put(target_item);
 
 out_put:
author maintainer
#451 lore raw

PATCH 449/733 configfs: unhash the dentry before dropping the item in rmdir

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Vasileios Almpanis <[email protected]>

commit f06c2d26d1999d37e93299db0ecead04ca7d0b9f upstream.

configfs_get_config_item() treats a hashed dentry as proof that sd->s_element is a live config_item. configfs_rmdir() breaks that:
simple_rmdir() leaves the dentry hashed, the last reference to the item is dropped right after, and the dentry is only unhashed by d_delete() once ->rmdir() has returned. configfs_symlink() resolves its target holding no lock on it, so get_target() can land in that window:

  BUG: KASAN: slab-use-after-free in config_item_get+0x26/0x90
   get_target fs/configfs/symlink.c:128 [inline]
   configfs_symlink+0x4ab/0x1030 fs/configfs/symlink.c:185

Unhash in configfs_remove_dir(), while the item is still guaranteed to be there. A reference obtained just before that stays harmless, as create_link() rechecks CONFIGFS_USET_DROPPING, already set by configfs_detach_prep(). Both configfs_unregister_subsystem() paths d_drop() after detaching, so this only makes rmdir match them.

---
 fs/configfs/dir.c |    9 +++++++++
 1 file changed, 9 insertions(+)
fs/configfs/dir.c +9 -0
@@ -416,6 +416,15 @@ static void configfs_remove_dir(struct d
 	if (d_really_is_positive(d)) {
 		if (unlikely(simple_rmdir(d_inode(parent), d)))
 			pr_warn("remove_dir (%pd): attributes remain", d);
+		else
+			/*
+			 * configfs_get_config_item() takes a hashed dentry as
+			 * proof that ->s_element is still alive.  Our caller
+			 * is about to drop the last reference to the item and
+			 * the VFS will not unhash until after we return, so
+			 * unhash it here.
+			 */
+			d_drop(d);
 	}
 
 	pr_debug(" o %pd removing done (%d)\n", d, d_count(d));
author maintainer
#452 lore raw

PATCH 450/733 powerpc/ps3: Fix repository.c build failure

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Thorsten Blum <[email protected]>

commit 5ba79d37403d86082ab4083b0f51ec3008a942cb upstream.

GCC fails to build ps3_defconfig with the following errors:

arch/powerpc/platforms/ps3/repository.c: In function ‘make_first_field.constprop’:
arch/powerpc/platforms/ps3/repository.c:78:9: error: ‘strnlen’ specified bound 8 exceeds source size 3 [-Werror=stringop-overread]

     78 |         memcpy((char *)&n, text, strnlen(text, sizeof(n)));
        |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  arch/powerpc/platforms/ps3/repository.c: In function ‘make_first_field.constprop’:
  arch/powerpc/platforms/ps3/repository.c:78:9: error: ‘strnlen’ specified bound 8 exceeds source size 4 [-Werror=stringop-overread]
     78 |         memcpy((char *)&n, text, strnlen(text, sizeof(n)));
        |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The current use of strnlen(text, sizeof(n)) triggers -Wstringop-overread when text is a short string literal that is smaller than sizeof(n), such as "bi" or "bus". Use strlen(text) instead and clamp the copy length to sizeof(n) before memcpy().

Drop the redundant char * cast while at it.

---
 arch/powerpc/platforms/ps3/repository.c |    5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)
arch/powerpc/platforms/ps3/repository.c +4 -1
@@ -6,6 +6,8 @@
  *  Copyright 2006 Sony Corp.
  */
 
+#include <linux/minmax.h>
+
 #include <asm/lv1call.h>
 
 #include "platform.h"
@@ -74,8 +76,9 @@ static void _dump_node(unsigned int lpar
 static u64 make_first_field(const char *text, u64 index)
 {
 	u64 n = 0;
+	size_t len = min(strlen(text), sizeof(n));
 
-	memcpy((char *)&n, text, strnlen(text, sizeof(n)));
+	memcpy(&n, text, len);
 	return PS3_VENDOR_ID_NONE + (n >> 32) + index;
 }
 
author maintainer
#453 lore raw

PATCH 451/733 powerpc/eeh: Fix recursive locking on devices without EEH sensitive driver

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Shivaprasad G Bhat <[email protected]>

commit c5e68706527968282e49de205cc2b935823cb88a upstream.

The commit 1010b4c012b0 ("powerpc/eeh: Make EEH driver device hotplug safe") refactored the EEH code such that the pci_rescan_remove_lock is held at the beginning of eeh_handle_normal_event() and the eeh_reset_device() is called with that lock being held. Looks like the commit missed to remove the existing lock/unlock inside eeh_rmv_device() which is no longer necessary. This is causing the eehd to hang on the lock which it actually holds when that code path is taken.

[<0>] 0xc00000011c78f870

[<0>] __switch_to+0xfc/0x1a0
[<0>] pci_lock_rescan_remove+0x30/0x44
[<0>] eeh_rmv_device+0x290/0x2e0
[<0>] eeh_pe_dev_traverse+0x80/0x130
[<0>] eeh_reset_device+0xcc/0x23c
[<0>] eeh_handle_normal_event+0x830/0xa80
[<0>] eeh_event_handler+0xf8/0x190
[<0>] kthread+0x194/0x1b0
[<0>] start_kernel_thread+0x14/0x18

The issue is seen for cases where the errors are detected on the PHB directly AND|OR for devices where the driver error_detected() returns PCI_ERS_RESULT_NEED_RESET, and driver being not EEH sensitive(i.e no error handlers like slot_reset(), resume() etc defined).

---
 arch/powerpc/kernel/eeh_driver.c |    2 --
 1 file changed, 2 deletions(-)
arch/powerpc/kernel/eeh_driver.c +0 -2
@@ -533,9 +533,7 @@ static void eeh_rmv_device(struct eeh_de
 		if (rmv_data)
 			list_add(&edev->rmv_entry, &rmv_data->removed_vf_list);
 	} else {
-		pci_lock_rescan_remove();
 		pci_stop_and_remove_bus_device(dev);
-		pci_unlock_rescan_remove();
 	}
 }
 
author maintainer
#454 lore raw

PATCH 452/733 powerpc: pci-ioda: Fix the stale irq chip reference

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Shivaprasad G Bhat <[email protected]>

commit d96171d911e3b89ca2957c04264019cf2f96287b upstream.

The commit f0ac60e6e311 ("powerpc/powernv/pci: Switch to use msi_create_parent_irq_domain()") removed the legacy MSI irq chip pnv_pci_msi_irq_chip but left behind the static definition of it and its reference in is_pnv_opal_msi().

The KVM IRQ bypass for vfio devices is broken because the comparision in is_pnv_opal_msi() fails on the comparision with stale unused variable showing the below errors in dmesg.

kvmppc_set_passthru_irq_hv: Could not assign IRQ map for (X,Y) kvmppc_set_passthru_irq (irq X, gsi Y) fails: -2
vfio-pci A:B:C.D irq bypass producer (eventfd Z) registration fails: -2

The patch removes the stale variable definition and fixes the is_pnv_opal_msi() by comparing against the chip name prefix.

---
 arch/powerpc/platforms/powernv/pci-ioda.c |    6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)
arch/powerpc/platforms/powernv/pci-ioda.c +2 -4
@@ -1623,15 +1623,13 @@ int64_t pnv_opal_pci_msi_eoi(struct irq_
 	return opal_pci_msi_eoi(phb->opal_id, d->parent_data->hwirq);
 }
 
-static struct irq_chip pnv_pci_msi_irq_chip;
-
 /*
  * Returns true iff chip is something that we could call
  * pnv_opal_pci_msi_eoi for.
  */
 bool is_pnv_opal_msi(struct irq_chip *chip)
 {
-	return chip == &pnv_pci_msi_irq_chip;
+	return chip && chip->name && str_has_prefix(chip->name, "PNV-");
 }
 EXPORT_SYMBOL_GPL(is_pnv_opal_msi);
 
@@ -1728,7 +1726,7 @@ static const struct msi_parent_ops pnv_m
 	.chip_flags		= MSI_CHIP_FLAG_SET_EOI,
 	.bus_select_token	= DOMAIN_BUS_NEXUS,
 	.bus_select_mask	= MATCH_PCI_MSI,
-	.prefix			= "PNV-",
+	.prefix			= "PNV-", /* Note: is_pnv_opal_msi() uses this */
 	.init_dev_msi_info	= pnv_init_dev_msi_info,
 };
 
author maintainer
#455 lore raw

PATCH 453/733 x86/amd_node: Avoid divide by zero on virtualized systems

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Jason Andryuk <[email protected]>

commit 72bd92bd8190d7869ecb462649ca40f297822a33 upstream.

On a virtualized system, the number of nodes does not have a relationship to the number of roots. A Xen PVH dom0 can calculate roots_per_node as 0, which crashes with a divide by zero in:

if (count++ % roots_per_node)

because the underlying topology code on Xen ends up making num_nodes 2 and num_roots 1 and the integer division result is 0.

The issue is seen with Xen, but it could affect other systems.

Set roots_per_node to 1 in this case. Print a firmware bug when this is performed for non-virtualized systems.

[ bp: Massage commit message. ]

---
 arch/x86/kernel/amd_node.c |    5 +++++
 1 file changed, 5 insertions(+)
arch/x86/kernel/amd_node.c +5 -0
@@ -287,6 +287,11 @@ static int __init amd_smn_init(void)
 		return -ENOMEM;
 
 	roots_per_node = num_roots / num_nodes;
+	if (!roots_per_node) {
+		if (!cpu_feature_enabled(X86_FEATURE_HYPERVISOR))
+			pr_warn(FW_BUG "Error detecting roots per node.\n");
+		roots_per_node = 1;
+	}
 
 	count = 0;
 	node = 0;
author maintainer
#456 lore raw

PATCH 454/733 x86/MCE/AMD: Fix inverted interrupt enablement during storm handling

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Jasjeet Rangi <[email protected]>

commit d2929113b15bfc06793b852aeba3d2db6d79fcc9 upstream.

mce_amd_handle_storm() currently does the opposite of what storm handling needs: it enables thresholding interrupts when a storm is detected and disables them when the storm subsides.

Flip the "on" function argument before passing it to threshold_restart_bank() as it should have been done.

To clarify: "on" to mce_handle_storm() means, the storm is on now when "on" is true, and off when "on" is false.

[ bp: Simplify. ]

---
 arch/x86/kernel/cpu/mce/amd.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
arch/x86/kernel/cpu/mce/amd.c +1 -1
@@ -864,7 +864,7 @@ static void amd_deferred_error_interrupt
 
 void mce_amd_handle_storm(unsigned int bank, bool on)
 {
-	threshold_restart_bank(bank, on);
+	threshold_restart_bank(bank, !on);
 }
 
 static void amd_reset_thr_limit(unsigned int bank)
author maintainer
#457 lore raw

PATCH 455/733 x86/amd_node: Fix potential NULL pointer dereference

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Jason Andryuk <[email protected]>

commit aefdbd574a362dcf7569bada6d72f64a006b9fb9 upstream.

amd_smn_read/write() are exported functions around __amd_smn_rw(), so they are always available even if amd_smn_init() fails. In that case, 'amd_roots' is NULL and __amd_smn_rw() will access uninitialized memory.

Then, commit:

83518453074d ("x86/amd_node: Add SMN offsets to exclusive region access")

added the 'smn_exclusive' flag, which indicated the calls to pci_request_config_region_exclusive() succeeded, to prevent concurrent userspace access.

Commit:

0a4b61d9c2e4 ("x86/amd_node: Fix AMD root device caching")

re-ordered initialization so pci_request_config_region_exclusive() is called earlier and a failure exits amd_smn_init() before allocating 'amd_roots'. The setting of 'smn_exclusive' moved to the end of amd_smn_init(), after 'amd_roots' is allocated. It became redundant and can be removed.

Replace 'smn_exclusive' with directly checking 'amd_roots', to fix a potential NULL pointer dereference and to simplify the logic.

[ bp: Reorg commit message, touchup comment. ]
[ mingo: Rebase & further touchups. ]

---
 arch/x86/kernel/amd_node.c |   14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)
arch/x86/kernel/amd_node.c +8 -6
@@ -38,7 +38,6 @@ static struct pci_dev **amd_roots;
 
 /* Protect the PCI config register pairs used for SMN. */
 static DEFINE_MUTEX(smn_mutex);
-static bool smn_exclusive;
 
 #define SMN_INDEX_OFFSET	0x60
 #define SMN_DATA_OFFSET		0x64
@@ -91,11 +90,16 @@ static int __amd_smn_rw(u8 i_off, u8 d_o
 	if (node >= amd_num_nodes())
 		return err;
 
-	root = amd_roots[node];
-	if (!root)
+	/*
+	 * Uninitialized amd_roots indicates pci_request_config_region_exclusive()
+	 * didn't run or failed and thus the kernel cannot rely on having
+	 * exclusive access to SMN registers so prevent that.
+	 */
+	if (!amd_roots)
 		return err;
 
-	if (!smn_exclusive)
+	root = amd_roots[node];
+	if (!root)
 		return err;
 
 	guard(mutex)(&smn_mutex);
@@ -313,8 +317,6 @@ static int __init amd_smn_init(void)
 		debugfs_create_file("value",	0600, debugfs_dir, NULL, &smn_value_fops);
 	}
 
-	smn_exclusive = true;
-
 	return 0;
 }
 
author maintainer
#458 lore raw

PATCH 456/733 crypto: x86/aria - add missing vzeroupper in AVX2 code

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Eric Biggers <[email protected]>

commit ac53977611428db3bc0b4ac0225e19c3e08ae50b upstream.

Since the AVX2 optimized ARIA code uses YMM registers, execute vzeroupper before returning from it. This is needed to avoid degrading the performance of any later SSE code that may happen to be executed.

Fixes 37d8d3ae7a58 ("crypto: x86/aria - implement aria-avx2")
Cc Taehee Yoo <[email protected]>
---
 arch/x86/crypto/aria-aesni-avx2-asm_64.S |    6 ++++++
 1 file changed, 6 insertions(+)
arch/x86/crypto/aria-aesni-avx2-asm_64.S +6 -0
@@ -982,6 +982,7 @@ SYM_TYPED_FUNC_START(aria_aesni_avx2_enc
 		     %ymm8, %ymm9, %ymm10, %ymm11, %ymm12, %ymm13, %ymm14,
 		     %ymm15, %rax);
 
+	vzeroupper;
 	FRAME_END
 	RET;
 SYM_FUNC_END(aria_aesni_avx2_encrypt_32way)
@@ -1007,6 +1008,7 @@ SYM_TYPED_FUNC_START(aria_aesni_avx2_dec
 		     %ymm8, %ymm9, %ymm10, %ymm11, %ymm12, %ymm13, %ymm14,
 		     %ymm15, %rax);
 
+	vzeroupper;
 	FRAME_END
 	RET;
 SYM_FUNC_END(aria_aesni_avx2_decrypt_32way)
@@ -1209,6 +1211,7 @@ SYM_TYPED_FUNC_START(aria_aesni_avx2_ctr
 		     %ymm8, %ymm9, %ymm10, %ymm11, %ymm12, %ymm13, %ymm14,
 		     %ymm15, %r10);
 
+	vzeroupper;
 	FRAME_END
 	RET;
 SYM_FUNC_END(aria_aesni_avx2_ctr_crypt_32way)
@@ -1359,6 +1362,7 @@ SYM_TYPED_FUNC_START(aria_aesni_avx2_gfn
 		     %ymm8, %ymm9, %ymm10, %ymm11, %ymm12, %ymm13, %ymm14,
 		     %ymm15, %rax);
 
+	vzeroupper;
 	FRAME_END
 	RET;
 SYM_FUNC_END(aria_aesni_avx2_gfni_encrypt_32way)
@@ -1384,6 +1388,7 @@ SYM_TYPED_FUNC_START(aria_aesni_avx2_gfn
 		     %ymm8, %ymm9, %ymm10, %ymm11, %ymm12, %ymm13, %ymm14,
 		     %ymm15, %rax);
 
+	vzeroupper;
 	FRAME_END
 	RET;
 SYM_FUNC_END(aria_aesni_avx2_gfni_decrypt_32way)
@@ -1428,6 +1433,7 @@ SYM_TYPED_FUNC_START(aria_aesni_avx2_gfn
 		     %ymm8, %ymm9, %ymm10, %ymm11, %ymm12, %ymm13, %ymm14,
 		     %ymm15, %r10);
 
+	vzeroupper;
 	FRAME_END
 	RET;
 SYM_FUNC_END(aria_aesni_avx2_gfni_ctr_crypt_32way)
author maintainer
#459 lore raw

PATCH 457/733 crypto: x86/aria - add missing vzeroupper in AVX-512 code

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Eric Biggers <[email protected]>

commit 60892a384aa1e65d0e703e1c513417bdf0c80777 upstream.

Since the AVX-512 optimized ARIA code uses ZMM registers, execute vzeroupper before returning from it. This is needed to avoid degrading the performance of any later SSE code that may happen to be executed.

Fixes c970d42001f2 ("crypto: x86/aria - implement aria-avx512")
Cc Taehee Yoo <[email protected]>
---
 arch/x86/crypto/aria-gfni-avx512-asm_64.S |    3 +++
 1 file changed, 3 insertions(+)
arch/x86/crypto/aria-gfni-avx512-asm_64.S +3 -0
@@ -800,6 +800,7 @@ SYM_TYPED_FUNC_START(aria_gfni_avx512_en
 		     %zmm9, %zmm8, %zmm11, %zmm10, %zmm12, %zmm13, %zmm14,
 		     %zmm15, %rax);
 
+	vzeroupper;
 	FRAME_END
 	RET;
 SYM_FUNC_END(aria_gfni_avx512_encrypt_64way)
@@ -825,6 +826,7 @@ SYM_TYPED_FUNC_START(aria_gfni_avx512_de
 		     %zmm9, %zmm8, %zmm11, %zmm10, %zmm12, %zmm13, %zmm14,
 		     %zmm15, %rax);
 
+	vzeroupper;
 	FRAME_END
 	RET;
 SYM_FUNC_END(aria_gfni_avx512_decrypt_64way)
@@ -966,6 +968,7 @@ SYM_TYPED_FUNC_START(aria_gfni_avx512_ct
 		     %zmm9, %zmm8, %zmm11, %zmm10, %zmm12, %zmm13, %zmm14,
 		     %zmm15, %r10);
 
+	vzeroupper;
 	FRAME_END
 	RET;
 SYM_FUNC_END(aria_gfni_avx512_ctr_crypt_64way)
author maintainer
#460 lore raw

PATCH 458/733 x86/amd_node: Fix PCI device reference counting in amd_smn_init()

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Yazen Ghannam <[email protected]>

commit 27600805e62f800bacf990354632eae4e487d34c upstream.

The local "root" pointer is a temporary variable used during the device search. Therefore, refcount related to the search iterators should be cleaned up after the search is complete.

Use the __free() cleanup macro to ensure the refcount is decremented when the temporary pointer goes out of scope.

Additionally, increment the refcount when caching a root pointer. This ensures the in-use refcount is separate from the temporary search refcounting.

Finally, drop the redundant "root = NULL" before the second search loop. The pci_get_class() iterator always decrements the refcount of its "from" argument, so the first loop can only fall through with "root" already NULL.

---
 arch/x86/kernel/amd_node.c |    6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)
arch/x86/kernel/amd_node.c +2 -4
@@ -251,7 +251,7 @@ __setup("amd_smn_debugfs_enable", amd_sm
 static int __init amd_smn_init(void)
 {
 	u16 count, num_roots, roots_per_node, node, num_nodes;
-	struct pci_dev *root;
+	struct pci_dev *root __free(pci_dev_put) = NULL;
 
 	if (!cpu_feature_enabled(X86_FEATURE_ZEN))
 		return 0;
@@ -262,7 +262,6 @@ static int __init amd_smn_init(void)
 		return 0;
 
 	num_roots = 0;
-	root = NULL;
 	while ((root = get_next_root(root))) {
 		pci_dbg(root, "Reserving PCI config space\n");
 
@@ -299,14 +298,13 @@ static int __init amd_smn_init(void)
 
 	count = 0;
 	node = 0;
-	root = NULL;
 	while (node < num_nodes && (root = get_next_root(root))) {
 		/* Use one root for each node and skip the rest. */
 		if (count++ % roots_per_node)
 			continue;
 
 		pci_dbg(root, "is root for AMD node %u\n", node);
-		amd_roots[node++] = root;
+		amd_roots[node++] = pci_dev_get(root);
 	}
 
 	if (enable_dfs) {
author maintainer
#461 lore raw

PATCH 459/733 x86/mm: Fix user-space data loss with MADV_FREE and THP

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Vernon Yang <[email protected]>

commit f7491d7c81db0e7c304a7bd757a76d2fbeaff80e upstream.

Some of users of Polars (a data analytics library) have lost production data from this bug. They seem to have just the right combination of huge pages, MADV_FREE and heavy reclaim pressure.

pmd_modify() masks the old value with (_HPAGE_CHG_MASK & ~_PAGE_DIRTY), silently discarding the hardware dirty bit. The subsequent pmd_mksaveddirty() call is supposed to transfer _PAGE_DIRTY into _PAGE_SAVED_DIRTY when write-protecting, but the dirty bit was already stripped from the value, so there is nothing left to transfer.

Contrast with pte_modify(), which keeps _PAGE_DIRTY_BITS in its mask, and pud_modify(), which keeps _HPAGE_CHG_MASK untouched: pmd_modify() is the odd one out. Any pmd_modify() on a writable, dirty PMD loses the dirty state.

One visible consequence is data loss with MADV_FREE on PMD-mapped THP:

    memset(buf, 0x5A, size);          // PMD-mapped THP, PMD dirty
    madvise(buf, size, MADV_FREE);    // PMD cleaned but left writable,
                                      // folio marked lazyfree
    memset(buf, 0x5A, size);          // hardware sets _PAGE_DIRTY again
    mprotect(buf, size, PROT_READ);   // pmd_modify() drops the dirty bit
    mprotect(buf, size, PROT_READ|PROT_WRITE);
    // ... memory pressure ...

Reclaim (e.g. under memcg pressure) then finds the lazyfree folio with no dirty bit set anywhere and frees it in __discard_anon_folio_pmd_locked(), even though the data was rewritten after MADV_FREE; subsequent reads fault in fresh zero pages. NUMA hinting alone can trigger the same loss, as do_huge_pmd_numa_page() restores the PMD through pmd_modify() as well.

PMD-mapped file THPs are affected too: mprotect()/NUMA hinting dropping the dirty bit means rewritten data is never written back.

Fix it by keeping _PAGE_DIRTY in the preserved mask, exactly like pte_modify() and pud_modify() do. The existing pmd_mksaveddirty()/pmd_clear_saveddirty() pair then performs the hardware-dirty <-> saved-dirty transition based on the write bit, preserving the shadow-stack encoding rules.

---
 arch/x86/include/asm/pgtable.h |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
arch/x86/include/asm/pgtable.h +1 -1
@@ -806,7 +806,7 @@ static inline pmd_t pmd_modify(pmd_t pmd
 	pmdval_t val = pmd_val(pmd), oldval = val;
 	pmd_t pmd_result;
 
-	val &= (_HPAGE_CHG_MASK & ~_PAGE_DIRTY);
+	val &= _HPAGE_CHG_MASK;
 	val |= check_pgprot(newprot) & ~_HPAGE_CHG_MASK;
 	val = flip_protnone_guard(oldval, val, PHYSICAL_PMD_PAGE_MASK);
 
author maintainer
#462 lore raw

PATCH 460/733 x86/cfi: Fix FineIBT hash offset in cfi_get_func_hash()

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Soheil Hassas Yeganeh <[email protected]>

commit 5a5d26f2cfe13467166219f6bf58099326912ddb upstream.

The switch of the FineIBT preamble from "subl $hash, %r10d" to the shorter "subl $hash, %eax" moved the hash immediate from offset 7 to offset 5 of the preamble. fineibt_preamble_hash was updated to match, but the open-coded offset in cfi_get_func_hash() was missed and it still reads the hash at offset 7.

cfi_get_func_hash() is used by the BPF JIT to give a struct_ops trampoline the CFI hash of the stub function it stands in for. With FineIBT the trampoline now gets the upper half of the real hash followed by the first two bytes of the next instruction, so the first indirect call from the kernel into a struct_ops program, tcp_init_congestion_control() calling ->init() of a BPF congestion control for example, fails the FineIBT check and the kernel dies with a CFI failure.

Move the FineIBT preamble template and its offset defines above cfi_get_func_hash() and use fineibt_preamble_hash there, so every reader of the preamble shares one definition of its layout. The CFI_FINEIBT arm is only built with CONFIG_FINEIBT, the only configuration in which cfi_mode can take that value. cfi_get_func_arity() does not need the same treatment: the __bhi_args call whose displacement it reads still ends at the function address.

---
 arch/x86/kernel/alternative.c |   72 ++++++++++++++++++++++--------------------
 1 file changed, 38 insertions(+), 34 deletions(-)
arch/x86/kernel/alternative.c +38 -34
@@ -1207,6 +1207,41 @@ static bool cfi_debug __ro_after_init;
 bool cfi_bhi __ro_after_init = false;
 #endif
 
+#ifdef CONFIG_FINEIBT
+/*
+ * <fineibt_preamble_start>:
+ *  0:   f3 0f 1e fa             endbr64
+ *  4:   2d 78 56 34 12          sub    $0x12345678, %eax
+ *  9:   2e 0f 85 03 00 00 00    jne,pn 13 <fineibt_preamble_start+0x13>
+ * 10:   0f 1f 40 d6             nopl   -0x2a(%rax)
+ *
+ * Note that the JNE target is the 0xD6 byte inside the NOPL, this decodes as
+ * UDB on x86_64 and raises #UD.
+ */
+asm(	".pushsection .rodata				\n"
+	"fineibt_preamble_start:			\n"
+	"	endbr64					\n"
+	"	subl	$0x12345678, %eax		\n"
+	"fineibt_preamble_bhi:				\n"
+	"	cs jne.d32 fineibt_preamble_start+0x13	\n"
+	"#fineibt_func:					\n"
+	"	nopl	-42(%rax)			\n"
+	"fineibt_preamble_end:				\n"
+	".popsection\n"
+);
+
+extern u8 fineibt_preamble_start[];
+extern u8 fineibt_preamble_bhi[];
+extern u8 fineibt_preamble_end[];
+
+#define fineibt_preamble_size (fineibt_preamble_end - fineibt_preamble_start)
+#define fineibt_preamble_bhi  (fineibt_preamble_bhi - fineibt_preamble_start)
+#define fineibt_preamble_ud   0x13
+#define fineibt_preamble_hash 5
+
+#define fineibt_prefix_size (fineibt_preamble_size - ENDBR_INSN_SIZE)
+#endif /* CONFIG_FINEIBT */
+
 #ifdef CONFIG_CFI
 u32 cfi_get_func_hash(void *func)
 {
@@ -1214,9 +1249,11 @@ u32 cfi_get_func_hash(void *func)
 
 	func -= cfi_get_offset();
 	switch (cfi_mode) {
+#ifdef CONFIG_FINEIBT
 	case CFI_FINEIBT:
-		func += 7;
+		func += fineibt_preamble_hash;
 		break;
+#endif
 	case CFI_KCFI:
 		func += 1;
 		break;
@@ -1359,39 +1396,6 @@ early_param("cfi", cfi_parse_cmdline);
  */
 
 /*
- * <fineibt_preamble_start>:
- *  0:   f3 0f 1e fa             endbr64
- *  4:   2d 78 56 34 12          sub    $0x12345678, %eax
- *  9:   2e 0f 85 03 00 00 00    jne,pn 13 <fineibt_preamble_start+0x13>
- * 10:   0f 1f 40 d6             nopl   -0x2a(%rax)
- *
- * Note that the JNE target is the 0xD6 byte inside the NOPL, this decodes as
- * UDB on x86_64 and raises #UD.
- */
-asm(	".pushsection .rodata				\n"
-	"fineibt_preamble_start:			\n"
-	"	endbr64					\n"
-	"	subl	$0x12345678, %eax		\n"
-	"fineibt_preamble_bhi:				\n"
-	"	cs jne.d32 fineibt_preamble_start+0x13	\n"
-	"#fineibt_func:					\n"
-	"	nopl	-42(%rax)			\n"
-	"fineibt_preamble_end:				\n"
-	".popsection\n"
-);
-
-extern u8 fineibt_preamble_start[];
-extern u8 fineibt_preamble_bhi[];
-extern u8 fineibt_preamble_end[];
-
-#define fineibt_preamble_size (fineibt_preamble_end - fineibt_preamble_start)
-#define fineibt_preamble_bhi  (fineibt_preamble_bhi - fineibt_preamble_start)
-#define fineibt_preamble_ud   0x13
-#define fineibt_preamble_hash 5
-
-#define fineibt_prefix_size (fineibt_preamble_size - ENDBR_INSN_SIZE)
-
-/*
  * <fineibt_caller_start>:
  *  0:   b8 78 56 34 12          mov    $0x12345678, %eax
  *  5:   4d 8d 5b f0             lea    -0x10(%r11), %r11
author maintainer
#463 lore raw

PATCH 461/733 x86/mm/pat: Acquire init_mm write lock on collapse to avoid UAF

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Lorenzo Stoakes (ARM) <[email protected]>

commit a1c7570cedd03372812a5b693732880867babbca upstream.

x86 implements page attribute modification using its Change Page Attributes (CPA) mechanism.

This tracks properties of ranges such as cache mode through x86 page attributes, and as part of that logic manipulates kernel page tables.

Since commit:

41d88484c71c ("x86/mm/pat: restore large ROX pages after fragmentation")

ranges of kernel page table entries can be collapsed into huge page table entries as part of this logic.

As part of this collapse, it frees the page tables which the collapsed entries previously pointed to, and it does so without any relevant locks being held to preclude concurrent kernel page table walkers.

The only way this code can be reached is if CPA_COLLAPSE is specified, and this is only set in set_memory_rox() via:

set_memory_rox()
-> change_page_attr_set_clr()
-> cpa_flush()
-> cpa_collapse_large_pages()

Notable users of this are execmem and BPF when manipulating executable mappings.

However, this is problematic for ptdump as it walks ranges it does not own and thus runs the risk of a use-after-free on page tables freed underneath it.

In addition, concurrent CPA collapse operations are possible which can also cause races.

Resolve the issue by acquiring the mmap write lock on init_mm across the whole operation.

It is safe to acquire a sleeping lock as all the callers invoke set_memory_rox() from process context and in any case, change_page_attr_set_clr() calls vm_unmap_alias() which ultimately takes a mutex, disallowing atomic context here.

---
 arch/x86/mm/pat/set_memory.c |   15 ++++++++++++++-
 include/linux/mmap_lock.h    |    2 ++
 2 files changed, 16 insertions(+), 1 deletion(-)
arch/x86/mm/pat/set_memory.c +14 -1
@@ -22,6 +22,7 @@
 #include <linux/cc_platform.h>
 #include <linux/set_memory.h>
 #include <linux/memregion.h>
+#include <linux/cleanup.h>
 
 #include <asm/e820/api.h>
 #include <asm/processor.h>
@@ -410,7 +411,7 @@ static void __cpa_flush_tlb(void *data)
 
 static int collapse_large_pages(unsigned long addr, struct list_head *pgtables);
 
-static void cpa_collapse_large_pages(struct cpa_data *cpa)
+static void __cpa_collapse_large_pages(struct cpa_data *cpa)
 {
 	unsigned long start, addr, end;
 	struct ptdesc *ptdesc, *tmp;
@@ -448,6 +449,18 @@ static void cpa_collapse_large_pages(str
 	spin_unlock(&cpa_lock);
 }
 
+static void cpa_collapse_large_pages(struct cpa_data *cpa)
+{
+	/*
+	 * Take the mmap write lock on init_mm to:
+	 * - Avoid a use-after-free if raced by ptdump (which takes its own
+	 *   write lock on init_mm).
+	 * - Serialise concurrent CPA walkers.
+	 */
+	scoped_guard(mmap_write_lock, &init_mm)
+		__cpa_collapse_large_pages(cpa);
+}
+
 static void cpa_flush(struct cpa_data *cpa, int cache)
 {
 	unsigned int i;
include/linux/mmap_lock.h +2 -0
@@ -622,6 +622,8 @@ static inline void mmap_read_unlock(stru
 DEFINE_GUARD(mmap_read_lock, struct mm_struct *,
 	     mmap_read_lock(_T), mmap_read_unlock(_T))
 DEFINE_GUARD_COND(mmap_read_lock, _try, mmap_read_trylock(_T))
+DEFINE_GUARD(mmap_write_lock, struct mm_struct *,
+	     mmap_write_lock(_T), mmap_write_unlock(_T))
 
 static inline void mmap_read_unlock_non_owner(struct mm_struct *mm)
 {
author maintainer
#464 lore raw

PATCH 462/733 x86/alternatives: Exclude text poking against change_page_attr()

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Pedro Falcato <[email protected]>

commit 1587d3394e254639cc36516256031334095e6ef3 upstream.

Quoted 1 line

From time to time, the following BUG can be observed

in the x86 alternatives patching code [0]:

Quoted 12 lines

kernel BUG at arch/x86/kernel/alternative.c:2576!

Oops: invalid opcode: 0000 [#1] SMP NOPTI
CPU: 0 UID: 0 PID: 355 Comm: (udev-worker) Not tainted 7.1.3-1-default #1 PREEMPT(full) openSUSE Tumbleweed  8c1795b03ec64f997e57a8ad38b1161e3b98da64
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS unknown 02/02/2022
RIP: 0010:__text_poke+0x2aa/0x450
Call Trace:
 <TASK>
 smp_text_poke_batch_finish+0x2a7/0x320
 __static_call_transform+0xb7/0x220
 arch_static_call_transform+0x5b/0xb0
 __static_call_init+0xe9/0x270
 static_call_module_notify+0x11f/0x150
Quoted 8 lines
 notifier_call_chain+0x61/0xe0
 blocking_notifier_call_chain_robust+0x63/0xc0
 load_module+0x1c92/0x20c0
 init_module_from_file+0xd8/0x140
 idempotent_init_module+0x100/0x2f0
 __x64_sys_finit_module+0x71/0xe0
 do_syscall_64+0xe1/0x610
 entry_SYSCALL_64_after_hwframe+0x76/0x7e

which matches the following BUG_ON() in alternative.c:

	/*
	 * If something went wrong, crash and burn since recovery paths are not
	 * implemented.
	 */
	BUG_ON(!pages[0] || (cross_page_boundary && !pages[1]));

This can happen if vmalloc_to_page() fails, for any reason. Such can happen if text poking races with CPA, which can possibly result in the collapsing of page tables (or breaking of PMD hugepages). It is not a problem for most users of vmalloc_to_page() (they solely own the vmalloc'd range) but, when CONFIG_ARCH_HAS_EXECMEM_ROX=y, various modules own a single execmem vmalloc range, and can call set_memory_*() in parallel on it. This can happen to race against __text_poke and cause havoc in vmalloc_to_page().

Fix it by excluding against CPA using the init_mm mmap read lock.

[ dhansen: Fix up SoB ordering. The actual code flow here was:
Pedro=>Lorenzo=>Mike=>Me which is reflected in the SoB chain now. I *believe* Mike simply picked up Lorenzo's update to Pedro's post from the Link ]

---
 arch/x86/kernel/alternative.c |   39 ++++++++++++++++++++++++++++++++++++---
 1 file changed, 36 insertions(+), 3 deletions(-)
arch/x86/kernel/alternative.c +36 -3
@@ -6,6 +6,9 @@
 #include <linux/vmalloc.h>
 #include <linux/memory.h>
 #include <linux/execmem.h>
+#include <linux/cleanup.h>
+#include <linux/kgdb.h>
+#include <linux/mmap_lock.h>
 
 #include <asm/text-patching.h>
 #include <asm/insn.h>
@@ -2547,6 +2550,38 @@ static void text_poke_memset(void *dst,
 
 typedef void text_poke_f(void *dst, const void *src, size_t len);
 
+static void __poke_vmalloc_pages(struct page **pages, void *addr,
+				 bool cross_page_boundary)
+{
+	pages[0] = vmalloc_to_page(addr);
+	if (cross_page_boundary)
+		pages[1] = vmalloc_to_page(addr + PAGE_SIZE);
+}
+
+static void poke_vmalloc_pages(struct page **pages, void *addr,
+			       bool cross_page_boundary)
+{
+	if (in_dbg_master()) {
+		/*
+		 * If called from kgdb cannot sleep, but all other CPUs stopped
+		 * anyway so safe to proceed without locks
+		 */
+		__poke_vmalloc_pages(pages, addr, cross_page_boundary);
+	} else {
+		/*
+		 * execmem ROX ranges are shared between modules and can be
+		 * collapsed to huge PMD entries, and this collapse can happen
+		 * concurrently with a racing set_memory_rox().
+		 *
+		 * Prevent vmalloc_to_page() from racing by acquiring an
+		 * init_mm read lock which pairs with the init_mm write lock in
+		 * cpa_collapse_large_pages().
+		 */
+		guard(mmap_read_lock)(&init_mm);
+		__poke_vmalloc_pages(pages, addr, cross_page_boundary);
+	}
+}
+
 static void *__text_poke(text_poke_f func, void *addr, const void *src, size_t len)
 {
 	bool cross_page_boundary = offset_in_page(addr) + len > PAGE_SIZE;
@@ -2564,9 +2599,7 @@ static void *__text_poke(text_poke_f fun
 	BUG_ON(!after_bootmem);
 
 	if (!core_kernel_text((unsigned long)addr)) {
-		pages[0] = vmalloc_to_page(addr);
-		if (cross_page_boundary)
-			pages[1] = vmalloc_to_page(addr + PAGE_SIZE);
+		poke_vmalloc_pages(pages, addr, cross_page_boundary);
 	} else {
 		pages[0] = virt_to_page(addr);
 		WARN_ON(!PageReserved(pages[0]));
author maintainer
#465 lore raw

PATCH 463/733 mm/slab: take n->list_lock in __slab_try_return_freelist() to avoid race

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Harry Yoo (Meta) <[email protected]>

commit 4a724bcf5d703e18957397914d79156fa2cf1174 upstream.

Commit ba7425312607 ("mm, slab: add an optimistic __slab_try_return_freelist()") incorrectly assumed that nobody has freed an object to the slab as long as slab->freelist is NULL and cmpxchg succeeds.

However, as reported by Hyunwoo Kim [1], other CPUs might have freed an object to the slab, insert the slab to the partial list, then allocated an object from the slab, and be in the middle of removing the slab from the list under n->list_lock.

Since __refill_objects_node() puts the slab back on pc.slabs outside n->list_lock, it might insert the slab into that list while the slab is concurrently being removed from n->partial. This led to a list corruption [1]:

list_add corruption. next->prev should be prev
(ffff888100000248), but was dead000000000122.
(next=ffffea000416e410).
kernel BUG at lib/list_debug.c:29!

  Oops: invalid opcode: 0000 [#1] SMP NOPTI
  CPU: 1 UID: 65534 PID: 144 Comm: poc Not tainted
  7.2.0-16172-gcf72cbb39da8-dirty #1 PREEMPT(lazy)
  RIP: 0010:__list_add_valid_or_report+0x80/0xd0
  ...
  Call Trace:
   alloc_from_new_slab+0x183/0x300
   ___slab_alloc+0x31c/0x890
   __kmalloc_noprof+0x3d4/0x800
   lsm_blob_alloc+0x2d/0x50
   security_msg_msg_alloc+0x26/0x90
   load_msg+0x1aa/0x210
   do_msgsnd+0x91/0x800
   do_syscall_64+0x109/0x5d0
   entry_SYSCALL_64_after_hwframe+0x77/0x7f
  ...
  Kernel panic - not syncing: Fatal exception

This is a classic ABA problem where cmpxchg succeeds but the state has changed since __refill_objects_node() took the freelist from the slab.

As Vlastimil Babka mentioned [2], it should be rare to return more than one slab (due to the racy read of slab->counters in get_partial_node_bulk()). Therefore, instead of introducing additional complexity, acquire and release n->list_lock twice in the worst case.

Return the slab directly to the partial list and hold n->list_lock across the cmpxchg and add_partial(). This is similar to the initial version of commit ba7425312607 [3]. This is enough to avoid the race as the list manipulation is serialized by n->list_lock. While at it, bring back unlikely() hint now that the condition is unlikely.

---
 mm/slub.c |   20 +++++++++++++-------
 1 file changed, 13 insertions(+), 7 deletions(-)
mm/slub.c +13 -7
@@ -5612,10 +5612,12 @@ static noinline void free_to_partial_lis
  *
  * Fail if the slab isn't full anymore due to a concurrent free.
  */
-static bool __slab_try_return_freelist(struct kmem_cache *s, struct slab *slab,
-				       void *head, int cnt)
+static bool __slab_try_return_freelist(struct kmem_cache *s,
+				       struct kmem_cache_node *n,
+				       struct slab *slab, void *head, int cnt)
 {
 	struct freelist_counters old, new;
+	unsigned long flags;
 
 	old.freelist = slab->freelist;
 	old.counters = slab->counters;
@@ -5627,9 +5629,15 @@ static bool __slab_try_return_freelist(s
 	new.counters = old.counters;
 	new.inuse -= cnt;
 
-	if (!slab_update_freelist(s, slab, &old, &new, "__slab_try_return_freelist"))
+	spin_lock_irqsave(&n->list_lock, flags);
+
+	if (!slab_update_freelist(s, slab, &old, &new, "__slab_try_return_freelist")) {
+		spin_unlock_irqrestore(&n->list_lock, flags);
 		return false;
+	}
 
+	add_partial(n, slab, ADD_TO_TAIL);
+	spin_unlock_irqrestore(&n->list_lock, flags);
 	return true;
 }
 
@@ -7202,10 +7210,8 @@ __refill_objects_node(struct kmem_cache
 			void *head = object;
 			void *tail;
 
-			if (__slab_try_return_freelist(s, slab, head, count)) {
-				list_add(&slab->slab_list, &pc.slabs);
+			if (__slab_try_return_freelist(s, n, slab, head, count))
 				break;
-			}
 
 			do {
 				tail = object;
@@ -7218,7 +7224,7 @@ __refill_objects_node(struct kmem_cache
 			break;
 	}
 
-	if (!list_empty(&pc.slabs)) {
+	if (unlikely(!list_empty(&pc.slabs))) {
 		spin_lock_irqsave(&n->list_lock, flags);
 
 		list_for_each_entry(slab, &pc.slabs, slab_list)
author maintainer
#466 lore raw

PATCH 464/733 ipv6: fix fib6 walker UAF on seq stop

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Zihan Xi <[email protected]>

commit 19b4ed644d68098cc62ab612727f40d30f43476c upstream.

ipv6_route_iter_active() treats a walker in FWS_U at the table root as already unlinked. fib6_del_route() can move a still-linked walker into that same state when the current leaf is the last route at the root, so ipv6_route_native_seq_stop() skips fib6_walker_unlink(). The seq private object can then be freed while it remains on net->ipv6.fib6_walkers. A later route deletion walks the dangling list and uses the freed walker.

Use the list head as membership state and reinitialize it when unlinking. Keep the existing w->node check so a never-started iterator with a zeroed private object is not treated as linked.

The same stop helper is used by /proc/net/ipv6_route and by the BPF ipv6_route iterator. The BPF show path only widens the race.

---
 net/ipv6/ip6_fib.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
net/ipv6/ip6_fib.c +2 -2
@@ -85,7 +85,7 @@ static void fib6_walker_link(struct net
 static void fib6_walker_unlink(struct net *net, struct fib6_walker *w)
 {
 	write_lock_bh(&net->ipv6.fib6_walker_lock);
-	list_del(&w->lh);
+	list_del_init(&w->lh);
 	write_unlock_bh(&net->ipv6.fib6_walker_lock);
 }
 
@@ -2758,7 +2758,7 @@ static void *ipv6_route_seq_start(struct
 static bool ipv6_route_iter_active(struct ipv6_route_iter *iter)
 {
 	struct fib6_walker *w = &iter->w;
-	return w->node && !(w->state == FWS_U && w->node == w->root);
+	return w->node && !list_empty(&w->lh);
 }
 
 static void ipv6_route_native_seq_stop(struct seq_file *seq, void *v)
author maintainer
#467 lore raw

PATCH 465/733 ipmr: account multicast table and route memory

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Zihan Xi <[email protected]>

commit b7ee18725f2292ab554aa96a101ae42d45f008bd upstream.

A netadmin in a user+net namespace can create many IPv4 and IPv6 multicast routing tables with MRT_TABLE and MRT6_TABLE. Each unseen id allocates an mr_table via the shared mr_table_alloc(), links it into the per-net list, and leaves it until netns teardown. Those objects were not charged to memcg, so the host unreclaimable slab grows with the table count.

Account mr_table allocations with GFP_KERNEL_ACCOUNT and mark the IPv4/IPv6 MFC caches SLAB_ACCOUNT. This matches the established handling of IP addresses, routes and alternate interface names.

Unresolved MFC entries are still allocated from softIRQ with GFP_ATOMIC and are not charged. They expire after 10 seconds and are bounded by the socket receive queue; see commit 0079ad8e8dc3 ("ipmr: remove hard code cache_resolve_queue_len limit").

Fixes f0ad0860d01e ("ipv4: ipmr: support multiple tables")
Fixes d1db275dd3f6 ("ipv6: ip6mr: support multiple tables")
---
 net/ipv4/ipmr.c      |    3 ++-
 net/ipv4/ipmr_base.c |    2 +-
 net/ipv6/ip6mr.c     |    2 +-
 3 files changed, 4 insertions(+), 3 deletions(-)
net/ipv4/ipmr.c +2 -1
@@ -3376,7 +3376,8 @@ int __init ip_mr_init(void)
 {
 	int err;
 
-	mrt_cachep = KMEM_CACHE(mfc_cache, SLAB_HWCACHE_ALIGN | SLAB_PANIC);
+	mrt_cachep = KMEM_CACHE(mfc_cache,
+				SLAB_HWCACHE_ALIGN | SLAB_PANIC | SLAB_ACCOUNT);
 
 	err = register_pernet_subsys(&ipmr_net_ops);
 	if (err)
net/ipv4/ipmr_base.c +1 -1
@@ -52,7 +52,7 @@ mr_table_alloc(struct net *net, u32 id,
 	struct mr_table *mrt;
 	int err;
 
-	mrt = kzalloc_obj(*mrt);
+	mrt = kzalloc_obj(*mrt, GFP_KERNEL_ACCOUNT);
 	if (!mrt)
 		return ERR_PTR(-ENOMEM);
 	mrt->id = id;
net/ipv6/ip6mr.c +1 -1
@@ -1427,7 +1427,7 @@ int __init ip6_mr_init(void)
 {
 	int err;
 
-	mrt_cachep = KMEM_CACHE(mfc6_cache, SLAB_HWCACHE_ALIGN);
+	mrt_cachep = KMEM_CACHE(mfc6_cache, SLAB_HWCACHE_ALIGN | SLAB_ACCOUNT);
 	if (!mrt_cachep)
 		return -ENOMEM;
 
author maintainer
#468 lore raw

PATCH 466/733 reboot: fix cad_pid use-after-free race

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Cen Zhang (Microsoft) <[email protected]>

commit 5a88f78df753993469dab4d1831f8fb4256a9468 upstream.

cad_pid is a single kernel-wide struct pid pointer. proc_do_cad_pid() reads it and passes it to pid_vnr() without protecting the lifetime of the referenced struct pid. A concurrent writer can replace cad_pid and drop the final reference to the old struct pid after the reader has loaded the pointer but before pid_vnr() has finished dereferencing it, causing a use-after-free.

kill_cad_pid() has the same lifetime race when it passes cad_pid to kill_pid().

At the time this issue was reported, an unprivileged user could reach the sysctl through user and PID namespaces because cad_pid was registered in pid_table[]. Moving cad_pid back to the global reboot sysctl table corrected that namespace and permission mismatch, but did not fix the underlying lifetime race.

Fix this by treating cad_pid as an RCU-protected pointer at both read sites and by waiting for a grace period before dropping the old reference on the write side.

call_rcu(&old_pid->rcu, ...) cannot be used here because free_pid() also queues pid->rcu; queueing the same rcu_head twice can corrupt the RCU callback list.

Original KASAN crash stack:

  kernel/pid.c:545 pid_nr_ns()        # reads freed pid->level
  kernel/pid.c:556 pid_vnr()          # calls pid_nr_ns()
  kernel/pid.c:775 proc_do_cad_pid()  # calls pid_vnr(cad_pid)
---
 include/linux/sched.h        |    2 +-
 include/linux/sched/signal.h |    5 +----
 init/main.c                  |    2 +-
 kernel/reboot.c              |   19 +++++++++++++++----
 kernel/signal.c              |   12 ++++++++++++
 5 files changed, 30 insertions(+), 10 deletions(-)
include/linux/sched.h +1 -1
@@ -1775,7 +1775,7 @@ static inline bool is_lazy_mmu_mode_acti
 }
 #endif
 
-extern struct pid *cad_pid;
+extern struct pid __rcu *cad_pid;
 
 /*
  * Per process flags
include/linux/sched/signal.h +1 -4
@@ -562,10 +562,7 @@ static inline sigset_t *sigmask_to_save(
 	return res;
 }
 
-static inline int kill_cad_pid(int sig, int priv)
-{
-	return kill_pid(cad_pid, sig, priv);
-}
+int kill_cad_pid(int sig, int priv);
 
 /* These can be the second arg to send_sig_info/send_group_sig_info.  */
 #define SEND_SIG_NOINFO ((struct kernel_siginfo *) 0)
init/main.c +1 -1
@@ -1636,7 +1636,7 @@ static noinline void __init kernel_init_
 	 */
 	set_mems_allowed(node_states[N_MEMORY]);
 
-	cad_pid = get_pid(task_pid(current));
+	rcu_assign_pointer(cad_pid, get_pid(task_pid(current)));
 
 	smp_prepare_cpus(setup_max_cpus);
 
kernel/reboot.c +15 -4
@@ -13,7 +13,9 @@
 #include <linux/kexec.h>
 #include <linux/kmod.h>
 #include <linux/kmsg_dump.h>
+#include <linux/rcupdate.h>
 #include <linux/reboot.h>
+#include <linux/sched/signal.h>
 #include <linux/suspend.h>
 #include <linux/syscalls.h>
 #include <linux/syscore_ops.h>
@@ -24,8 +26,7 @@
  */
 
 static int C_A_D = 1;
-struct pid *cad_pid;
-EXPORT_SYMBOL(cad_pid);
+struct pid __rcu *cad_pid;
 
 #if defined(CONFIG_ARM)
 #define DEFAULT_REBOOT_MODE		= REBOOT_HARD
@@ -1371,10 +1372,14 @@ static int proc_do_cad_pid(const struct
 {
 	struct ctl_table tmp_table = *table;
 	struct pid *new_pid;
+	struct pid *old_pid;
 	pid_t tmp_pid;
 	int r;
 
-	tmp_pid = pid_vnr(cad_pid);
+	rcu_read_lock();
+	tmp_pid = pid_vnr(rcu_dereference(cad_pid));
+	rcu_read_unlock();
+
 	tmp_table.data = &tmp_pid;
 
 	r = proc_dointvec(&tmp_table, write, buffer, lenp, ppos);
@@ -1385,7 +1390,13 @@ static int proc_do_cad_pid(const struct
 	if (!new_pid)
 		return -ESRCH;
 
-	put_pid(xchg(&cad_pid, new_pid));
+	old_pid = unrcu_pointer(xchg(&cad_pid, RCU_INITIALIZER(new_pid)));
+	/*
+	 * Wait for cad_pid readers before put_pid().  We cannot use
+	 * call_rcu() here because free_pid() already owns pid->rcu.
+	 */
+	synchronize_rcu();
+	put_pid(old_pid);
 	return 0;
 }
 
kernel/signal.c +12 -0
@@ -1903,6 +1903,18 @@ int kill_pid(struct pid *pid, int sig, i
 }
 EXPORT_SYMBOL(kill_pid);
 
+int kill_cad_pid(int sig, int priv)
+{
+	int ret;
+
+	rcu_read_lock();
+	ret = kill_pid(rcu_dereference(cad_pid), sig, priv);
+	rcu_read_unlock();
+
+	return ret;
+}
+EXPORT_SYMBOL(kill_cad_pid);
+
 #ifdef CONFIG_POSIX_TIMERS
 /*
  * These functions handle POSIX timer signals. POSIX timers use
author maintainer
#469 lore raw

PATCH 467/733 ftrace: Use rcu_assign_pointer() for tmp_ops filter hash

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Leon Hwang <[email protected]>

commit b4dcc18b97913888e8d009624e07c8014ce41b84 upstream.

tmp_ops.func_hash->filter_hash is annotated __rcu, but update_ftrace_direct_mod() assigns hash to it directly. Sparse reports an address-space mismatch.

Use rcu_assign_pointer() for the assignment.

---
 kernel/trace/ftrace.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/trace/ftrace.c +1 -1
index 53d5db60bfa5..673a54fdf392 100644
@@ -6675,7 +6675,7 @@ int update_ftrace_direct_mod(struct ftrace_ops *ops, struct ftrace_hash *hash, b
 
 	/* Enable the tmp_ops to have the same functions as the hash object. */
 	ftrace_ops_init(&tmp_ops);
-	tmp_ops.func_hash->filter_hash = hash;
+	rcu_assign_pointer(tmp_ops.func_hash->filter_hash, hash);
 
 	err = register_ftrace_function_nolock(&tmp_ops);
 	if (err)
Signature · 1 line
2.55.0
author maintainer
#470 lore raw

PATCH 468/733 ftrace: fork: Initialize function graph state before copy_exec_state()

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Jérémy Jean <[email protected]>

commit 08cacffeef8f64f1a222c93467ca84f24a46c953 upstream.

dup_task_struct() copies the parent's task_struct, including ret_stack. ftrace_graph_init_task() clears the copied function graph state, but it currently runs after copy_exec_state().

For non-CLONE_VM forks, copy_exec_state() allocates a new task_exec_state. If that allocation fails, copy_process() reaches bad_fork_free and free_task() calls ftrace_graph_exit_task(). Since the child still carries the parent's ret_stack pointer, the unwind frees the parent's active function graph return stack. The parent subsequently accesses freed memory from function_graph_enter_regs().

KASAN reports:

[   22.190920] ==================================================================
[   22.195899] BUG: KASAN: slab-use-after-free in function_graph_enter_regs+0xa76/0xb90
[   22.200747] Write of size 8 at addr ff110000054dc0a8 by task repro/1
[   22.205134]
[   22.210770] CPU: 0 UID: 0 PID: 1 Comm: repro Not tainted 7.2.0-07732-g9328b3b03bdc-dirty #3 PREEMPT(lazy)
[   22.212576] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
[   22.213750] Call Trace:
[   22.215271]  <TASK>
[   22.216242]  ? ftrace_stub_direct_tramp+0x10/0x10
[   22.217774]  dump_stack_lvl+0x4e/0x70
[   22.220531]  print_report+0x157/0x4b4
[   22.223202]  ? fixup_red_left+0x9/0x30
[   22.224407]  ? complete_report_info+0x83/0x110
[   22.226679]  ? function_graph_enter_regs+0xa76/0xb90
[   22.228084]  kasan_report+0xce/0x100
[   22.230109]  ? function_graph_enter_regs+0xa76/0xb90
[   22.232860]  ? stack_trace_save+0x4/0xd0
[   22.234156]  function_graph_enter_regs+0xa76/0xb90
[   22.236090]  ? kasan_save_stack+0x30/0x50
[   22.237752]  ? __pfx_function_graph_enter_regs+0x10/0x10
[   22.238694]  ? ring_buffer_lock_reserve+0x345/0xf80
[   22.239628]  ? stack_trace_save+0x4/0xd0
[   22.242121]  ? stack_trace_save+0x4/0xd0
[   22.243588]  ftrace_graph_func+0xda/0x160
[   22.245362]  ? ftrace_stub_direct_tramp+0x10/0x10
[   22.246520]  0xffffffffa0000095
[   22.250528]  ? stack_trace_save+0x9/0xd0
[   22.251757]  ? ring_buffer_unlock_commit+0x11d/0x5c0
[   22.253152]  stack_trace_save+0x9/0xd0
[   22.254264]  kasan_save_stack+0x30/0x50
[   22.273631]  kasan_save_track+0x14/0x30
[   22.276763]  kasan_save_free_info+0x3b/0x70
[   22.278296]  __kasan_slab_free+0x43/0x70
[   22.280157]  kmem_cache_free+0xbf/0x3b0
[   22.282963]  ? ftrace_stub_direct_tramp+0x10/0x10
[   22.284001]  free_task+0xa2/0x160
[   22.285699]  ? ftrace_stub_direct_tramp+0x10/0x10
[   22.286752]  copy_process+0x2aae/0x7bc0

Initialize the child function graph state immediately after dup_task_struct(), before the first fallible operation.

Fixes 6b1c66c9cca9 ("exec_state: relocate dumpable information")
Assisted-by Codex:gpt-5
---
 kernel/fork.c |    7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)
kernel/fork.c +5 -2
@@ -2114,6 +2114,11 @@ __latent_entropy struct task_struct *cop
 	p = dup_task_struct(current, node);
 	if (!p)
 		goto fork_out;
+	/*
+	 * Must run before the first fallible op, so error paths never
+	 * free the parent's ret_stack.
+	 */
+	ftrace_graph_init_task(p);
 	retval = copy_exec_state(clone_flags, p);
 	if (retval)
 		goto bad_fork_free;
@@ -2140,8 +2145,6 @@ __latent_entropy struct task_struct *cop
 	 */
 	p->clear_child_tid = (clone_flags & CLONE_CHILD_CLEARTID) ? args->child_tid : NULL;
 
-	ftrace_graph_init_task(p);
-
 	rt_mutex_init_task(p);
 	raw_spin_lock_init(&p->blocked_lock);
 
author maintainer
#471 lore raw

PATCH 469/733 tracing: Fix memory corruption from the histogram stacktrace modifier

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Donggeun Yoo <[email protected]>

commit a5e70ba87ca8ebc79b4e63de302d03b0625fe153 upstream.

parse_field() sets HIST_FIELD_FL_STACKTRACE from the ".stacktrace" modifier before it looks the field name up, and nothing afterwards checks that the name resolved to a field which holds a stacktrace. create_hist_field() picks HIST_FIELD_FN_STACK on the strength of the field pointer alone, which reads a __data_loc word from the record and follows its low 16 bits as an offset into the same record. event_hist_trigger() takes the first word there as an entry count and copies that many longs into a 31 entry array:

	n_entries = *stack;
	memcpy(entries, ++stack, n_entries * sizeof(unsigned long));

Neither end of that copy is bounded, and the count is whatever the event holds at the offset, so any field will do:

  # cd /sys/kernel/tracing/events/sched/sched_process_fork
  # echo 'hist:keys=parent_pid.stacktrace' > trigger
  # (true)
  BUG: kernel NULL pointer dereference, address: 0000000000000008
  RIP: 0010:rb_insert_color+0x18/0x130
   timerqueue_linked_add+0x7e/0xd0
   enqueue_hrtimer+0x39/0xb0
   __hrtimer_run_queues+0x10f/0x1f0
   </IRQ>
  RIP: 0010:memcpy+0xc/0x30
   event_hist_trigger+0x165/0x690

The timer interrupt landed on the rbtree the copy had already run over. No debug options are needed for this; KASAN reports the same write as an out-of-bounds read of 13835058055416381440 bytes.

Documentation/trace/histogram.rst already states the rule, "must be a long[] type", so enforce it once the name has been resolved. Names which resolve to no field at all, "hitcount.stacktrace" and the common_* pseudo-fields, are refused for the same reason: they hold no stacktrace to read.

---
 kernel/trace/trace_events_hist.c |   12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)
kernel/trace/trace_events_hist.c +10 -2
@@ -2325,6 +2325,7 @@ parse_field(struct hist_trigger_data *hi
 	struct ftrace_event_field *field = NULL;
 	char *field_name, *modifier, *str;
 	struct trace_array *tr = file->tr;
+	bool stack_modifier = false;
 
 	modifier = str = kstrdup(field_str, GFP_KERNEL);
 	if (!modifier)
@@ -2347,9 +2348,10 @@ parse_field(struct hist_trigger_data *hi
 			*flags |= HIST_FIELD_FL_EXECNAME;
 		else if (strcmp(modifier, "syscall") == 0)
 			*flags |= HIST_FIELD_FL_SYSCALL;
-		else if (strcmp(modifier, "stacktrace") == 0)
+		else if (strcmp(modifier, "stacktrace") == 0) {
 			*flags |= HIST_FIELD_FL_STACKTRACE;
-		else if (strcmp(modifier, "log2") == 0)
+			stack_modifier = true;
+		} else if (strcmp(modifier, "log2") == 0)
 			*flags |= HIST_FIELD_FL_LOG2;
 		else if (strcmp(modifier, "usecs") == 0)
 			*flags |= HIST_FIELD_FL_TIMESTAMP_USECS;
@@ -2420,6 +2422,12 @@ parse_field(struct hist_trigger_data *hi
 			}
 		}
 	}
+
+	if (stack_modifier &&
+	    (!field || field->filter_type != FILTER_STACKTRACE)) {
+		hist_err(tr, HIST_ERR_BAD_FIELD_MODIFIER, errpos(field_str));
+		field = ERR_PTR(-EINVAL);
+	}
  out:
 	kfree(str);
 
author maintainer
#472 lore raw

PATCH 470/733 tracing: Restore :mod: trailer after parsing in ftrace_set_clr_event()

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Thomas Weißschuh <[email protected]>

commit 911002e99e15f640f1fdc6d276206beaef59e790 upstream.

While ftrace_set_clr_event() modifies its input buffer during parsing, before returning to the caller the buffer is supposed to be restored to its original state.

This works correctly for the colon between the subsystem and event but not the colon at the beginning of :mod:.

Restore the colon, so the :mod: trailer is not stripped after ftrace_set_clr_event().

---
 kernel/trace/trace_events.c |    2 ++
 1 file changed, 2 insertions(+)
kernel/trace/trace_events.c +2 -0
@@ -1462,6 +1462,8 @@ int ftrace_set_clr_event(struct trace_ar
 	/* Put back the colon to allow this to be called again */
 	if (buf)
 		*(buf - 1) = ':';
+	if (mod)
+		*(mod - 5) = ':';
 
 	return ret;
 }
author maintainer
#473 lore raw

PATCH 471/733 tracing: Set the trace clock before registering the histogram trigger

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Donggeun Yoo <[email protected]>

commit 6ede78d0563a2a3ae3e46f9c07cedb5d79645429 upstream.

hist_register_trigger() puts the trigger on the global named_triggers list in cmd_ops->init(), and only then sets the trace clock:

	if (data->cmd_ops->init) {
		ret = data->cmd_ops->init(data);
		if (ret < 0)
			goto out;
	}

	if (hist_data->enable_timestamps) {
		ret = tracing_set_clock(file->tr, hist_data->attrs->clock);
		if (ret) {
			hist_err(tr, HIST_ERR_SET_CLOCK_FAIL, errpos(clock));
			goto out;
		}

The clock string is not checked anywhere before that call, so a named trigger using common_timestamp with an unknown clock fails after it has already become findable. event_hist_trigger_parse() then frees it without taking it off the list, and the next lookup by name reads the freed object:

~# cd /sys/kernel/tracing/events/sched/sched_switch
~# echo 'hist:name=foo:keys=common_pid:ts=common_timestamp:clock=bogus' > trigger
bash: echo: write error: Invalid argument
~# echo 'hist:name=foo:keys=common_pid' > trigger

  BUG: KASAN: slab-use-after-free in find_named_trigger+0xac/0xc0
  Read of size 8 at addr ffff88800915d760 by task init/1
   find_named_trigger+0xac/0xc0
   hist_register_trigger+0xc1/0x900
   event_hist_trigger_parse+0x3146/0x6af0
   event_trigger_write+0xce/0x160
  Freed by task 63:
   kfree+0x154/0x420
   trigger_kthread_fn+0xfd/0x160

Set the clock before the trigger is registered, so that nothing which can fail runs after it is published, the way commit 6f86bdeab633 ("tracing: Fix bad hist from corrupting named_triggers list") moved the registration below the rest of the setup.

tracing_set_filter_buffering() is reference counted, so the init failure path has to drop the reference that the clock block now takes first.

Fixes a4072fe85ba3 ("tracing: Add a clock attribute for hist triggers")
---
 kernel/trace/trace_events_hist.c |   15 +++++++++------
 1 file changed, 9 insertions(+), 6 deletions(-)
kernel/trace/trace_events_hist.c +9 -6
@@ -6631,12 +6631,6 @@ static int hist_register_trigger(char *g
 		data->cmd_ops = cmd_ops;
 	}
 
-	if (data->cmd_ops->init) {
-		ret = data->cmd_ops->init(data);
-		if (ret < 0)
-			goto out;
-	}
-
 	if (hist_data->enable_timestamps) {
 		char *clock = hist_data->attrs->clock;
 
@@ -6649,6 +6643,15 @@ static int hist_register_trigger(char *g
 		tracing_set_filter_buffering(file->tr, true);
 	}
 
+	if (data->cmd_ops->init) {
+		ret = data->cmd_ops->init(data);
+		if (ret < 0) {
+			if (hist_data->enable_timestamps)
+				tracing_set_filter_buffering(file->tr, false);
+			goto out;
+		}
+	}
+
 	if (named_data) {
 		remove_hist_vars(hist_data);
 		destroy_hist_data(hist_data);
author maintainer
#474 lore raw

PATCH 472/733 tracing: Fix memory corruption from a "STACKTRACE" histogram key

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Donggeun Yoo <[email protected]>

commit 7f711e62355bb3123a2ca2f97a2facbfebc678c6 upstream.

"cpu", "CPU", "stacktrace" and "STACKTRACE" are generic fields, defined with an offset and a size of zero so that the filter code can match them by name. parse_field() maps them onto their common_* equivalents for backward compatibility, but unlike the common_* names it hands the placeholder back to the caller instead of NULL.

create_hist_field() takes a non-NULL field as a promise that the record carries a stacktrace and picks HIST_FIELD_FN_STACK, so the __data_loc word is read from offset 0, that is from common_type, and its low 16 bits are followed as an offset into the record. What is found there becomes the length of an unbounded memcpy. Pick an event whose id is small enough that the offset stays inside its own record and the length is a kernel text address:

  # cd /sys/kernel/tracing
  # echo 'hist:keys=STACKTRACE' > events/ftrace/print/trigger
  # echo hello > trace_marker
  Oops: general protection fault, probably for non-canonical address
  RIP: 0010:rb_next+0x23/0x60
   </IRQ>
  RIP: 0010:memcpy+0xc/0x30
   event_hist_trigger+0x2e7/0x12c0
  Kernel panic - not syncing: Fatal exception in interrupt

Leave the field NULL, which is what the comment above the branch says the code does and what common_stacktrace already does. FILTER_CPU and FILTER_COMM are left alone, their create_hist_field() branches never look at the field.

Fixes 4b512860bdbd ("tracing: Rename stacktrace field to common_stacktrace")
---
 kernel/trace/trace_events_hist.c |    1 +
 1 file changed, 1 insertion(+)
kernel/trace/trace_events_hist.c +1 -0
@@ -2412,6 +2412,7 @@ parse_field(struct hist_trigger_data *hi
 				*flags |= HIST_FIELD_FL_CPU;
 			} else if (field && field->filter_type == FILTER_STACKTRACE) {
 				*flags |= HIST_FIELD_FL_STACKTRACE;
+				field = NULL;
 			} else if (field && field->filter_type == FILTER_COMM) {
 				*flags |= HIST_FIELD_FL_COMM | HIST_FIELD_FL_STRING;
 			} else {
author maintainer
#475 lore raw

PATCH 473/733 tracing: Take trace_array reference when opening a tracer options file

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Steven Rostedt <[email protected]>

commit ed0aff60f83a9bdc2f6556376ac79c96b3ce7e80 upstream.

When a tracer option file is opened, it is passed a descriptor that points to an element on the trace_array's topts array. This element has information to find the trace array and other information. It uses this element to take a reference of the trace_array so that the trace_array does not get removed while this file is opened.

Unfortunately, there's a race condition where the element itself could be freed by the removal of the instance the trace_array represents causing a use-after-free as this element that is used to find the trace_array to increment its reference counter is also freed when the instance is removed.

To solve this, add a trace_array_tracer_options_get() helper function that will take the address of the element that is passed to the open function by the inode->i_private pointer and search all the trace_arrays under a lock to find the one that the element's address is in the range of the trace_arrays topts array elements. When a match happens, that trace_array's reference would be increased.

Note, there's a race where if an admin was deleting and creating trace instances at the same time and the memory of the old trace_array's array matched the memory of the new trace_array that it could in theory open the option from the wrong trace array. But we do not care because it would be stupid to perform that kind of action. As long as the only thing that can happen is that the option from the wrong trace array is used and doesn't crash the kernel it will only make the user confused. But if they are doing something stupid like this, they are already confused, so no harm done.

---
 kernel/trace/trace.c |   46 +++++++++++++++++++++++++++++++++++++++++++++-
 kernel/trace/trace.h |    1 +
 2 files changed, 46 insertions(+), 1 deletion(-)
kernel/trace/trace.c +45 -1
@@ -7715,12 +7715,55 @@ trace_options_write(struct file *filp, c
 	return cnt;
 }
 
+static bool tr_option_match(struct trace_array *tr, void *topt)
+{
+	for (int i = 0; i < tr->nr_topts; i++) {
+		struct trace_options *tr_topts = &tr->topts[i];
+
+		if (topt >= (void *)&tr_topts->topts[0] &&
+		    topt < (void *)&tr_topts->topts[tr_topts->nr_topts])
+			return true;
+	}
+	return false;
+}
+
+/*
+ * The topt is the address of a trace_array->topts[] element that holds the
+ * the tracer options descriptor. But since the trace_array reference has not
+ * been taken yet, it cannot be dereferenced as it could have been freed by
+ * a rmdir of the instance the trace_array represents.
+ *
+ * Search the list of trace_arrays and compare the topt to the address of
+ * the entire trace_array topts array for each trace_array in the list.
+ * If one is matched, then take the reference and return it. If not, the
+ * trace_array no longer exits.
+ */
+static int trace_array_tracer_options_get(void *topt)
+{
+	struct trace_array *tr;
+	int ret;
+
+	ret = security_locked_down(LOCKDOWN_TRACEFS);
+	if (ret)
+		return ret;
+
+	if (tracing_disabled)
+		return -ENODEV;
+
+	guard(mutex)(&trace_types_lock);
+	list_for_each_entry(tr, &ftrace_trace_arrays, list) {
+		if (tr_option_match(tr, topt))
+			return __trace_array_get(tr);
+	}
+	return -ENODEV;
+}
+
 static int tracing_open_options(struct inode *inode, struct file *filp)
 {
 	struct trace_option_dentry *topt = inode->i_private;
 	int ret;
 
-	ret = tracing_check_open_get_tr(topt->tr);
+	ret = trace_array_tracer_options_get(topt);
 	if (ret)
 		return ret;
 
@@ -7982,6 +8025,7 @@ create_trace_option_files(struct trace_a
 	tr->topts = tr_topts;
 	tr->topts[tr->nr_topts].tracer = tracer;
 	tr->topts[tr->nr_topts].topts = topts;
+	tr->topts[tr->nr_topts].nr_topts = cnt;
 	tr->nr_topts++;
 
 	for (cnt = 0; opts[cnt].name; cnt++) {
kernel/trace/trace.h +1 -0
@@ -227,6 +227,7 @@ struct array_buffer {
 struct trace_options {
 	struct tracer			*tracer;
 	struct trace_option_dentry	*topts;
+	int				nr_topts;
 };
 
 struct trace_pid_list *trace_pid_list_alloc(void);
author maintainer
#476 lore raw

PATCH 474/733 tracing: Dont dereference trace_event_file in deferred trigger free

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Ali Ahmet Memiş <[email protected]>

commit bcfe2816e6ec46c3f4c58aa4264476665ddb3f69 upstream.

The enable_event trigger defers trace_event_put_ref() to the trigger free kthread, but the trace_event_file can already be freed when the instance is removed.

Keep the trace_event_call directly in enable_trigger_data so the deferred free does not access the freed trace_event_file.

---
 kernel/trace/trace.h                |    1 +
 kernel/trace/trace_events_trigger.c |    4 +++-
 2 files changed, 4 insertions(+), 1 deletion(-)
kernel/trace/trace.h +1 -0
@@ -1953,6 +1953,7 @@ struct event_trigger_data {
 
 struct enable_trigger_data {
 	struct trace_event_file		*file;
+	struct trace_event_call		*call;
 	bool				enable;
 	bool				hist;
 };
kernel/trace/trace_events_trigger.c +3 -1
@@ -1728,7 +1728,8 @@ static void enable_trigger_private_data_
 {
 	struct enable_trigger_data *enable_data = data->private_data;
 
-	trace_event_put_ref(enable_data->file->event_call);
+	/* The file may already be freed here, only the call is kept alive */
+	trace_event_put_ref(enable_data->call);
 	kfree(enable_data);
 }
 
@@ -1801,6 +1802,7 @@ int event_enable_trigger_parse(struct ev
 	enable_data->hist = hist;
 	enable_data->enable = enable;
 	enable_data->file = event_enable_file;
+	enable_data->call = event_enable_file->event_call;
 
 	trigger_data = trigger_data_alloc(cmd_ops, cmd, param, enable_data);
 	if (!trigger_data) {
author maintainer
#477 lore raw

PATCH 475/733 rust: num: seal Integer

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Younes Akhouayri <[email protected]>

commit c6709d5e14072d0e3d02f291daee46a199e5dad3 upstream.

Bounded relies on Integer implementations to describe primitive integer semantics correctly. In particular, it uses Integer::BITS and Signedness to justify unchecked operations.

Integer is currently safe and externally implementable, so an implementation can violate those assumptions and make safe Bounded operations reach undefined behavior.

For example, an Integer implementation for a u8 wrapper can report BITS = 16. Safe code can then cast a Bounded<u16, 9> containing 256 to that wrapper. Its TryFrom<u16> implementation returns Err, and Bounded::cast() calls unwrap_unchecked() on it, causing undefined behavior.

Seal Integer so only the primitive implementations provided by the kernel crate can satisfy it.

---
 rust/kernel/num.rs |    9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)
rust/kernel/num.rs +8 -1
@@ -13,9 +13,14 @@ pub enum Unsigned {}
 /// Designates signed primitive types.
 pub enum Signed {}
 
+mod private {
+    pub trait Sealed {}
+}
+
 /// Describes core properties of integer types.
 pub trait Integer:
-    Sized
+    private::Sealed
+    + Sized
     + Copy
     + Clone
     + PartialEq
@@ -54,6 +59,8 @@ pub trait Integer:
 macro_rules! impl_integer {
     ($($type:ty: $signedness:ty), *) => {
         $(
+        impl private::Sealed for $type {}
+
         impl Integer for $type {
             type Signedness = $signedness;
 
author maintainer
#478 lore raw

PATCH 476/733 rust: pin-init: use irrefutable pattern for `stack_pin_init`

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Gary Guo <[email protected]>

commit b6b9e6d4abe87b16ab55990b887c6fad8e7a01af upstream.

In Rust 1.100.0, Infallible will become an alias of !. The let binding in stack_pin_init will thus become unreachable and produce an "unreachable expression" warning for the subsequent match, and thus will fail a -Dwarnings build. For this macro, all we need to know is that the error type is uninhabited, so replace this with an irrefutable pattern instead.

[ The error looks like (dummy reproducer):

error: unreachable expression
--> rust/kernel/sync.rs:177:5
|
177 | pin_init::stack_pin_init!(let num = 42u32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
| unreachable expression
| any code following this expression is unreachable
|
= note: -D unreachable-code implied by -D warnings
= help: to override -D warnings add #[allow(unreachable_code)]
= note: this error originates in the macro pin_init::stack_pin_init (in Nightly builds, run with -Z macro-backtrace for more info)

- Miguel ]

Cc [email protected] # Needed in 7.1.y and later (for 6.12.y and 6.18.y a custom one is needed).
[ Reworded for typos. - Miguel ]
---
 rust/pin-init/src/lib.rs |    8 +-------
 1 file changed, 1 insertion(+), 7 deletions(-)
rust/pin-init/src/lib.rs +1 -7
@@ -493,13 +493,7 @@ macro_rules! stack_pin_init {
     (let $var:ident $(: $t:ty)? = $val:expr) => {
         let val = $val;
         let mut $var = ::core::pin::pin!($crate::__internal::StackInit$(::<$t>)?::uninit());
-        let mut $var = match $crate::__internal::StackInit::init($var, val) {
-            Ok(res) => res,
-            Err(x) => {
-                let x: ::core::convert::Infallible = x;
-                match x {}
-            }
-        };
+        let Ok(mut $var) = $crate::__internal::StackInit::init($var, val);
     };
 }
 
author maintainer
#479 lore raw

PATCH 477/733 rust: allow `clippy::as_underscore` in the generated bindings

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: John Hubbard <[email protected]>

commit 2ac74c6db40adaa29c50cbb281ae9a6f63de18e1 upstream.

A CLIPPY=1 build emitted about 15000 as _ conversion warnings, all of them in bindgen's generated output and none in hand-written code.

[ The lint messages look like:

error: using as _ conversion
--> rust/bindings/bindings_generated.rs:18947:9
|
18947 | self._bitfield_1.get_const::<0usize, 16u8>() as u32 as _
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
| |
| help: consider giving the type explicitly: u32
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.98.0/index.html#as_underscore
= note: -D clippy::as-underscore implied by -D warnings
= help: to override -D warnings add #[allow(clippy::as_underscore)]

- Miguel ]

bindgen 0.73 returns each bitfield read through a trailing as _, and 0.72 returns it through a transmute, which the lint ignores. The bindings and uapi crates allow clippy::all over the generated code. That group does not cover clippy::as_underscore, a restriction lint.

Allow clippy::as_underscore by name in the bindings and uapi crates.

Assisted-by LLM
Cc [email protected] # Needed in 6.12.y and later (Rust is pinned in older LTSs).
[ Removed CI sentence. - Miguel ]
---
 rust/bindings/lib.rs |    1 +
 rust/uapi/lib.rs     |    1 +
 2 files changed, 2 insertions(+)
rust/bindings/lib.rs +1 -0
@@ -22,6 +22,7 @@
 #![feature(cfi_encoding)]
 
 #[allow(dead_code)]
+#[allow(clippy::as_underscore)]
 #[allow(clippy::cast_lossless)]
 #[allow(clippy::ptr_as_ptr)]
 #[allow(clippy::ref_as_ptr)]
rust/uapi/lib.rs +1 -0
@@ -10,6 +10,7 @@
 #![no_std]
 #![allow(
     clippy::all,
+    clippy::as_underscore,
     clippy::cast_lossless,
     clippy::ptr_as_ptr,
     clippy::ref_as_ptr,
author maintainer
#480 lore raw

PATCH 478/733 rust: allow `unknown_lints` in generated bindings for Rust < 1.88

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Miguel Ojeda <[email protected]>

commit f4c3e38111fd84c2c7ae5785755f4a4d476e1cba upstream.

Starting with bindgen 0.73.2 [1], #[allow(unnecessary_transmutes)] are used, even when --rust-target 1.85 is passed.

However, the lint was introduced in Rust 1.88.0. Thus building with older Rust versions warns like:

    error: unknown lint: `unnecessary_transmutes`
         --> rust/uapi/uapi_generated.rs:26294:13
          |
    26294 |     #[allow(unnecessary_transmutes)]
          |             ^^^^^^^^^^^^^^^^^^^^^^
          |
          = note: `-D unknown-lints` implied by `-D warnings`
          = help: to override `-D warnings` add `#[allow(unknown_lints)]`

Thus allow unknown_lints in the generated bindings -- only when building with older Rust versions.

I have asked upstream if this is intentional [1], i.e. if we are supposed to always allow unknown lints in case bindgen uses such attributes, or whether it is an oversight.

[ Emilio said it wasn't intentional -- we will work around it for now
on the kernel side. - Miguel ]

Cc [email protected] # Needed in 6.12.y and later (Rust is pinned in older LTSs).
Cc Emilio Cobos Álvarez <[email protected]>
Assisted-by LLM

[ Removed the cfg for allow(unnecessary_transmutes) as suggested by
Gary. - Miguel ]

---
 rust/bindings/lib.rs |    3 ++-
 rust/uapi/lib.rs     |    3 ++-
 2 files changed, 4 insertions(+), 2 deletions(-)
rust/bindings/lib.rs +2 -1
@@ -27,7 +27,8 @@
 #[allow(clippy::ptr_as_ptr)]
 #[allow(clippy::ref_as_ptr)]
 #[allow(clippy::undocumented_unsafe_blocks)]
-#[cfg_attr(CONFIG_RUSTC_HAS_UNNECESSARY_TRANSMUTES, allow(unnecessary_transmutes))]
+#[cfg_attr(not(CONFIG_RUSTC_HAS_UNNECESSARY_TRANSMUTES), allow(unknown_lints))]
+#[allow(unnecessary_transmutes)]
 #[cfg_attr(
     CONFIG_RUSTC_HAS_SUSPICIOUS_RUNTIME_SYMBOL_DEFINITIONS,
     allow(suspicious_runtime_symbol_definitions)
rust/uapi/lib.rs +2 -1
@@ -24,7 +24,8 @@
     unreachable_pub,
     unsafe_op_in_unsafe_fn
 )]
-#![cfg_attr(CONFIG_RUSTC_HAS_UNNECESSARY_TRANSMUTES, allow(unnecessary_transmutes))]
+#![cfg_attr(not(CONFIG_RUSTC_HAS_UNNECESSARY_TRANSMUTES), allow(unknown_lints))]
+#![allow(unnecessary_transmutes)]
 #![cfg_attr(
     CONFIG_RUSTC_HAS_SUSPICIOUS_RUNTIME_SYMBOL_DEFINITIONS,
     allow(suspicious_runtime_symbol_definitions)
author maintainer
#481 lore raw

PATCH 479/733 drm/panic: clean new `clippy::needless_range_loop` lint for Rust 1.100.0

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Miguel Ojeda <[email protected]>

commit 97f8cb91a8c5658fe2ae6f5c2ff6e95474a5eb2f upstream.

Starting with Rust 1.100.0 (expected 2026-11-12), Clippy warns:

    warning: the loop variable `i` is only used to index `self.decimals`
       --> drivers/gpu/drm/drm_panic_qr.rs:410:18
        |
    410 |         for i in 0..len {
        |                  ^^^^^^
        |
    note: for this index operation
       --> drivers/gpu/drm/drm_panic_qr.rs:411:13
        |
    411 |             self.decimals[i] = (chunk % 10) as u8;
        |             ^^^^^^^^^^^^^^^^
        = help: for further information visit https://rust-lang.github.io/rust-clippy/main/index.html#needless_range_loop
        = note: `-W clippy::needless-range-loop` implied by `-W clippy::all`
        = help: to override `-W clippy::all` add `#[allow(clippy::needless_range_loop)]`
    help: consider using an iterator
        |
    410 -         for i in 0..len {
    410 +         for <item> in self.decimals.iter_mut().take(len) {
        |

The lint did not trigger here before because it could not handle arrays behind a field access such as self.decimals -- Clippy was improved to catch those cases [1][2].

Thus clean the warning by iterating over a slice rather than using take() so that an out-of-range len still triggers the same bounds check as the indexed loop.

---
 drivers/gpu/drm/drm_panic_qr.rs |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/gpu/drm/drm_panic_qr.rs +2 -2
@@ -407,8 +407,8 @@ impl DecFifo {
         for i in (0..self.len).rev() {
             self.decimals[i + len] = self.decimals[i];
         }
-        for i in 0..len {
-            self.decimals[i] = (chunk % 10) as u8;
+        for decimal in &mut self.decimals[..len] {
+            *decimal = (chunk % 10) as u8;
             chunk = div10(chunk);
         }
         self.len += len;
author maintainer
#482 lore raw

PATCH 480/733 ALSA: ctxfi: Fix CA20K2 S/PDIF passthrough

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Roman Prucha <[email protected]>

commit b26a7a80e6bbf8dd17dacb127d12435d79375cf2 upstream.

dao_rsc_init() encodes the DAIO configuration as

    conf = (desc->msr & 0x7) | (desc->passthru << 3);

S/PDIF passthrough uses msr=1 and passthru=1, resulting in conf=9.

daio_mgr_dao_init() masks conf with 0xf, but handles only values 1, 2, 4 and 8 when programming ATXCTL_NUC. As a result, conf=9 falls through to the default case and leaves NUC at its previous setting.

On a Creative X-Fi Titanium HD SB1270 (CA20K2), this breaks AC3 IEC61937 passthrough when snd_ctxfi runs with reference_rate=48000,multiple=2. The receiver detects a non-audio stream but cannot decode the AC3 payload.

With the unmodified driver, multiple=1 makes the same stream work. Handle conf=9 through the same NUC=0 path as conf=1.

The change was runtime tested on the SB1270 with multiple=2 using IEC958 stereo PCM, pre-encoded AC3 IEC61937 passthrough and ALSA A52 live 5.1 encoding.

Fixes 26a9630c72eb ("ALSA: ctxfi: cthw20k2: fix mask on conf to allow 4 bits")
Assisted-by LLM
---
 sound/pci/ctxfi/cthw20k2.c |    1 +
 1 file changed, 1 insertion(+)
sound/pci/ctxfi/cthw20k2.c +1 -0
@@ -994,6 +994,7 @@ static int daio_mgr_dao_init(struct hw *
 		/* S/PDIF output */
 		switch ((conf & 0xf)) {
 		case 1:
+		case 9:
 			set_field(&ctl->txctl[idx], ATXCTL_NUC, 0);
 			break;
 		case 2:
author maintainer
#483 lore raw

PATCH 481/733 ALSA: us122l: Prevent write upgrades for read mappings

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Kazuki Hanai <[email protected]>

commit 71c610aeb1770302ac9c9e0b9a4ecd37f1311928 upstream.

The hwdep mmap callback rejects read-buffer mappings that are initially writable, but leaves VM_MAYWRITE set on mappings created with PROT_READ. A process that can open the hwdep node O_RDWR can later use mprotect() to make the mapping writable.

The read allocation begins with struct usb_stream. Its read_size member is used by the fault handler to decide which pages belong to the read buffer. The read VMA intentionally remains expandable because pcm_usb_stream uses mremap() after reading that size. Changing read_size first can therefore map and access pages beyond the allocation. The same member is also consumed by usb_stream_free(), where changing it can make free_pages_exact() release pages outside the allocation.

Clear VM_MAYWRITE for read-buffer mappings after rejecting an initially writable VMA. This keeps the separate output-buffer mapping writable while preventing later permission upgrades.

---
 sound/usb/usx2y/us122l.c |    7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)
sound/usb/usx2y/us122l.c +5 -2
@@ -180,8 +180,11 @@ static int usb_stream_hwdep_mmap(struct
 	guard(mutex)(&us122l->mutex);
 	s = us122l->sk.s;
 	read = offset < s->read_size;
-	if (read && area->vm_flags & VM_WRITE)
-		return -EPERM;
+	if (read) {
+		if (area->vm_flags & VM_WRITE)
+			return -EPERM;
+		vm_flags_clear(area, VM_MAYWRITE);
+	}
 	/* if userspace tries to mmap beyond end of our buffer, fail */
 	if (size > PAGE_ALIGN(read ? s->read_size : s->write_size)) {
 		dev_warn(hw->card->dev, "%s: size %lu > %u\n", __func__,
author maintainer
#484 lore raw

PATCH 482/733 ALSA: usbusx2y: fix in04_last array size mismatch with in04_buf

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Tristan Madani <[email protected]>

commit 861111a14740e12c36d363e9830f8daa734279c9 upstream.

The in04_last array in struct usx2ydev is declared as char[24], but in04_buf is allocated as sizeof(struct us428_ctls) which is 21 bytes. In i_usx2y_in04_int(), when ctl_snapshot_last == -2 (initialization path):

    memcpy(usx2y->in04_last, usx2y->in04_buf, sizeof(usx2y->in04_last));

This copies 24 bytes from a 21-byte slab allocation, reading 3 bytes past the end of the source object.

Introduce a USX2Y_IN04_SIZE constant defined as sizeof(struct us428_ctls) and use it consistently for the in04_last array, the in04_buf allocation, the URB transfer length, and the comparison loop, replacing the bare 24 and 21 literals throughout.

---
 sound/usb/usx2y/usbusx2y.c |    6 +++---
 sound/usb/usx2y/usbusx2y.h |    4 +++-
 2 files changed, 6 insertions(+), 4 deletions(-)
sound/usb/usx2y/usbusx2y.c +3 -3
@@ -196,7 +196,7 @@ static void i_usx2y_in04_int(struct urb
 			memcpy(usx2y->in04_last, usx2y->in04_buf, sizeof(usx2y->in04_last));
 			us428ctls->ctl_snapshot_last = -1;
 		} else {
-			for (i = 0; i < 21; i++) {
+			for (i = 0; i < USX2Y_IN04_SIZE; i++) {
 				if (usx2y->in04_last[i] != ((char *)usx2y->in04_buf)[i]) {
 					if (diff < 0)
 						diff = i;
@@ -305,7 +305,7 @@ int usx2y_in04_init(struct usx2ydev *usx
 		goto error;
 	}
 
-	usx2y->in04_buf = kmalloc(21, GFP_KERNEL);
+	usx2y->in04_buf = kmalloc(USX2Y_IN04_SIZE, GFP_KERNEL);
 	if (!usx2y->in04_buf) {
 		err = -ENOMEM;
 		goto error;
@@ -313,7 +313,7 @@ int usx2y_in04_init(struct usx2ydev *usx
 
 	init_waitqueue_head(&usx2y->in04_wait_queue);
 	usb_fill_int_urb(usx2y->in04_urb, usx2y->dev, usb_rcvintpipe(usx2y->dev, 0x4),
-			 usx2y->in04_buf, 21,
+			 usx2y->in04_buf, USX2Y_IN04_SIZE,
 			 i_usx2y_in04_int, usx2y,
 			 10);
 	if (usb_urb_ep_type_check(usx2y->in04_urb)) {
sound/usb/usx2y/usbusx2y.h +3 -1
@@ -5,6 +5,8 @@
 #include "../midi.h"
 #include "usbus428ctldefs.h"
 
+#define USX2Y_IN04_SIZE	sizeof(struct us428_ctls)
+
 #define NRURBS	        2
 
 /* Default value used for nr of packs per urb.
@@ -55,7 +57,7 @@ struct usx2ydev {
 	int			stride;
 	struct urb		*in04_urb;
 	void			*in04_buf;
-	char			in04_last[24];
+	char			in04_last[USX2Y_IN04_SIZE];
 	unsigned int		in04_int_calls;
 	struct snd_usx2y_urb_seq	*us04;
 	wait_queue_head_t	in04_wait_queue;
author maintainer
#485 lore raw

PATCH 483/733 ALSA: usbusx2y: validate URB actual_length in interrupt callback

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Tristan Madani <[email protected]>

commit 8f5ef203abda9dd36b2af473c7b737d544f807bd upstream.

i_usx2y_in04_int() processes the interrupt URB data without checking urb->actual_length. A short transfer from a malfunctioning device would cause the handler to process uninitialized heap data from the kmalloc-allocated in04_buf, which is then copied to the mmap-accessible ctl_snapshot[] array.

Fix by using kzalloc() for in04_buf to zero-initialize the buffer, and adding an actual_length check to skip processing on short transfers while still resubmitting the URB.

---
 sound/usb/usx2y/usbusx2y.c |    6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)
sound/usb/usx2y/usbusx2y.c +5 -1
@@ -189,6 +189,9 @@ static void i_usx2y_in04_int(struct urb
 		return;
 	}
 
+	if (urb->actual_length < USX2Y_IN04_SIZE)
+		goto resubmit;
+
 	if (us428ctls) {
 		diff = -1;
 		if (us428ctls->ctl_snapshot_last == -2) {
@@ -253,6 +256,7 @@ static void i_usx2y_in04_int(struct urb
 	if (err)
 		dev_err(&urb->dev->dev, "in04_int() usb_submit_urb err=%i\n", err);
 
+resubmit:
 	urb->dev = usx2y->dev;
 	usb_submit_urb(urb, GFP_ATOMIC);
 }
@@ -305,7 +309,7 @@ int usx2y_in04_init(struct usx2ydev *usx
 		goto error;
 	}
 
-	usx2y->in04_buf = kmalloc(USX2Y_IN04_SIZE, GFP_KERNEL);
+	usx2y->in04_buf = kzalloc(USX2Y_IN04_SIZE, GFP_KERNEL);
 	if (!usx2y->in04_buf) {
 		err = -ENOMEM;
 		goto error;
author maintainer
#486 lore raw

PATCH 484/733 Revert "riscv: Reset pmm when PR_TAGGED_ADDR_ENABLE is not set"

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Samuel Holland <[email protected]>

commit 2d2184ac90365a4af3274e23c98d469f09f91749 upstream.

This reverts commit 3033b2b1e3949274f33a140e2a97571b5a307298.

The reverted patch is userspace-visible behavior change, not a bug fix. The two variables here (pmm and pmlen) control two independent features:
pmm is the _hardware_ pointer masking mode that applies while executing in userspace. pmlen is the shift amount that the _kernel_ uses when untagging addresses; PMLEN_0 means no untagging occurs, so the kernel does not accept tagged addresses in syscall arguments.

It is valid (as documented and tested by the self test) to enable pointer masking without enabling the tagged address ABI. This separation is necessary to allow userspace to create an execution environment similar to what the kernel supports on arm64 by default, where TBI is enabled but the tagged address ABI is not. (On arm64, there is no equivalent to PR_PMLEN_MASK because TBI is always enabled.)

Fixes 3033b2b1e394 ("riscv: Reset pmm when PR_TAGGED_ADDR_ENABLE is not set")
---
 arch/riscv/kernel/process.c |    4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)
arch/riscv/kernel/process.c +1 -3
@@ -349,10 +349,8 @@ long set_tagged_addr_ctrl(struct task_st
 	if (arg & PR_TAGGED_ADDR_ENABLE && (tagged_addr_disabled || !pmlen))
 		return -EINVAL;
 
-	if (!(arg & PR_TAGGED_ADDR_ENABLE)) {
+	if (!(arg & PR_TAGGED_ADDR_ENABLE))
 		pmlen = PMLEN_0;
-		pmm = ENVCFG_PMM_PMLEN_0;
-	}
 
 	if (mmap_write_lock_killable(mm))
 		return -EINTR;
author maintainer
#487 lore raw

PATCH 485/733 riscv: hwprobe: initialize pair->value in hwprobe_one_pair()

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Andy Chiu <[email protected]>

commit d0fc6fab20460add1f27402cd8b945d094a56b21 upstream.

The vendor-extension handlers reached from hwprobe_one_pair() (hwprobe_isa_vendor_ext_thead_0() and friends) only OR the present bits into pair->value via VENDOR_EXTENSION_SUPPORTED() and clear their own missing bits; they assume the caller has already zeroed pair->value.

That holds for hwprobe_get_values() (it zeroes each pair) and hwprobe_get_cpus() (it re-initializes its scratch pair per key), but not for complete_hwprobe_vdso_data(), which reuses a single pair across all keys without re-zeroing. A vendor key therefore inherits stale bits from the previously probed key, and the wrong value is cached in the vDSO all_cpu_hwprobe_values[] and handed to userspace on the fast patih.

Zero pair->value once at the top of hwprobe_one_pair() so every handler starts from a clean value regardless of the caller, and drop the now redundant zeroing in the *_BLOCK_SIZE cases. hwprobe_isa_ext0() keeps its own zeroing because hwprobe_ext0_has() calls it directly, bypassing hwprobe_one_pair().

---
 arch/riscv/kernel/sys_hwprobe.c |    5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)
arch/riscv/kernel/sys_hwprobe.c +2 -3
@@ -295,6 +295,8 @@ static u64 hwprobe_vec_misaligned(const
 static void hwprobe_one_pair(struct riscv_hwprobe *pair,
 			     const struct cpumask *cpus)
 {
+	pair->value = 0;
+
 	switch (pair->key) {
 	case RISCV_HWPROBE_KEY_MVENDORID:
 	case RISCV_HWPROBE_KEY_MARCHID:
@@ -329,17 +331,14 @@ static void hwprobe_one_pair(struct risc
 		break;
 
 	case RISCV_HWPROBE_KEY_ZICBOZ_BLOCK_SIZE:
-		pair->value = 0;
 		if (hwprobe_ext0_has(cpus, RISCV_HWPROBE_EXT_ZICBOZ))
 			pair->value = riscv_cboz_block_size;
 		break;
 	case RISCV_HWPROBE_KEY_ZICBOM_BLOCK_SIZE:
-		pair->value = 0;
 		if (hwprobe_ext0_has(cpus, RISCV_HWPROBE_EXT_ZICBOM))
 			pair->value = riscv_cbom_block_size;
 		break;
 	case RISCV_HWPROBE_KEY_ZICBOP_BLOCK_SIZE:
-		pair->value = 0;
 		if (hwprobe_ext0_has(cpus, RISCV_HWPROBE_EXT_ZICBOP))
 			pair->value = riscv_cbop_block_size;
 		break;
author maintainer
#488 lore raw

PATCH 486/733 dm/amdgpu: fix malformed link_settings debugfs output

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Harry Wentland <[email protected]>

commit 622b4e8505aa7453a53d17fa3a288871f270fc8b upstream.

[Why]
dp_link_settings_read() passed strlen() of each format string as the size argument to snprintf() and then advanced rd_buf_ptr by that same fixed amount. The format-string length has no relation to the formatted output length, so snprintf() truncated each field at a NUL it wrote inside the buffer while the pointer was advanced past it. The result is a buffer peppered with embedded NUL bytes and fields that are silently cut short, so the data read back from the debugfs node does not reflect the actual link settings.

[How]
Use scnprintf() with the real remaining buffer size (rd_buf_size - (rd_buf_ptr - rd_buf)) and advance rd_buf_ptr by its return value, which is the number of characters actually written. This both bounds each write to the space left in rd_buf and keeps the output a single, properly terminated string. The now-unused str_len local is removed.

Fixes 41db5f1931ec ("drm/amd/display: set-read link rate and lane count through debugfs")
Assisted-by Copilot:claude-opus-4.8
(cherry picked from commit 43b9f0f18693c7f7b75613f3aeae25fa2b4e2f76)
---
 drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c |   20 +++++---------
 1 file changed, 8 insertions(+), 12 deletions(-)
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c +8 -12
@@ -196,7 +196,6 @@ static ssize_t dp_link_settings_read(str
 	char *rd_buf_ptr = NULL;
 	const uint32_t rd_buf_size = 100;
 	uint32_t result = 0;
-	uint8_t str_len = 0;
 	int r;
 
 	if (*pos & 3 || size & 3)
@@ -208,29 +207,26 @@ static ssize_t dp_link_settings_read(str
 
 	rd_buf_ptr = rd_buf;
 
-	str_len = strlen("Current:  %d  0x%x  %d  ");
-	snprintf(rd_buf_ptr, str_len, "Current:  %d  0x%x  %d  ",
+	rd_buf_ptr += scnprintf(rd_buf_ptr, rd_buf_size - (rd_buf_ptr - rd_buf),
+			"Current:  %d  0x%x  %d  ",
 			link->cur_link_settings.lane_count,
 			link->cur_link_settings.link_rate,
 			link->cur_link_settings.link_spread);
-	rd_buf_ptr += str_len;
 
-	str_len = strlen("Verified:  %d  0x%x  %d  ");
-	snprintf(rd_buf_ptr, str_len, "Verified:  %d  0x%x  %d  ",
+	rd_buf_ptr += scnprintf(rd_buf_ptr, rd_buf_size - (rd_buf_ptr - rd_buf),
+			"Verified:  %d  0x%x  %d  ",
 			link->verified_link_cap.lane_count,
 			link->verified_link_cap.link_rate,
 			link->verified_link_cap.link_spread);
-	rd_buf_ptr += str_len;
 
-	str_len = strlen("Reported:  %d  0x%x  %d  ");
-	snprintf(rd_buf_ptr, str_len, "Reported:  %d  0x%x  %d  ",
+	rd_buf_ptr += scnprintf(rd_buf_ptr, rd_buf_size - (rd_buf_ptr - rd_buf),
+			"Reported:  %d  0x%x  %d  ",
 			link->reported_link_cap.lane_count,
 			link->reported_link_cap.link_rate,
 			link->reported_link_cap.link_spread);
-	rd_buf_ptr += str_len;
 
-	str_len = strlen("Preferred:  %d  0x%x  %d  ");
-	snprintf(rd_buf_ptr, str_len, "Preferred:  %d  0x%x  %d\n",
+	rd_buf_ptr += scnprintf(rd_buf_ptr, rd_buf_size - (rd_buf_ptr - rd_buf),
+			"Preferred:  %d  0x%x  %d\n",
 			link->preferred_link_setting.lane_count,
 			link->preferred_link_setting.link_rate,
 			link->preferred_link_setting.link_spread);
author maintainer
#489 lore raw

PATCH 487/733 drm/amdgpu: skip gfx switch_power_profile during GPU reset

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Prike Liang <[email protected]>

commit 1f1d43418d61c8511779e0f63a954a78b9433b43 upstream.

During resume from GPU reset, the gfx idle work may invoke switch_power_profile before the reset completes. This causes the following assert error because the register access occurs without first releasing the GPU reset semaphore:

[ 1576.768935] CR2: 0000559ea133ead0 CR3: 00000002e6c42000 CR4: 0000000000350ef0
[ 1576.768940] Call Trace:
[ 1576.768944]  <TASK>
[ 1576.768953]  amdgpu_device_rreg+0x21/0x50 [amdgpu]
[ 1576.769158]  smu_msg_v1_send_msg+0x1a4/0x6e0 [amdgpu]
[ 1576.769437]  smu_cmn_send_smc_msg_with_params_ext+0xba/0x120 [amdgpu]
[ 1576.769721]  smu_cmn_send_smc_msg_with_param+0x33/0x40 [amdgpu]
[ 1576.769993]  smu_v13_0_0_set_power_profile_mode+0x192/0x2b0 [amdgpu]
[ 1576.770267]  smu_bump_power_profile_mode+0x5d/0x80 [amdgpu]
[ 1576.770538]  smu_switch_power_profile+0xa4/0xf0 [amdgpu]
[ 1576.770839]  amdgpu_dpm_switch_power_profile+0x6f/0x90 [amdgpu]
[ 1576.771210]  amdgpu_gfx_profile_idle_work_handler+0xe9/0x130 [amdgpu]
[ 1576.771460]  process_one_work+0x23e/0x6f0
[ 1576.771491]  worker_thread+0x1c4/0x380
[ 1576.771506]  kthread+0x10c/0x150
[ 1576.771512]  ? __pfx_worker_thread+0x10/0x10
[ 1576.771518]  ? __pfx_kthread+0x10/0x10
[ 1576.771530]  ret_from_fork+0x314/0x390
[ 1576.771537]  ? __pfx_kthread+0x10/0x10
[ 1576.771546]  ret_from_fork_asm+0x1a/0x30
(cherry picked from commit d93b1ff538ce9750c01e0dd0aa62575579c0fc08)
---
 drivers/gpu/drm/amd/pm/amdgpu_dpm.c |    6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)
drivers/gpu/drm/amd/pm/amdgpu_dpm.c +4 -2
@@ -348,7 +348,8 @@ int amdgpu_dpm_switch_power_profile(stru
 	const struct amd_pm_funcs *pp_funcs = adev->powerplay.pp_funcs;
 	int ret = 0;
 
-	if (amdgpu_sriov_vf(adev))
+	if (amdgpu_sriov_vf(adev) ||
+		amdgpu_in_reset(adev))
 		return 0;
 
 	if (pp_funcs && pp_funcs->switch_power_profile) {
@@ -367,7 +368,8 @@ int amdgpu_dpm_pause_power_profile(struc
 	const struct amd_pm_funcs *pp_funcs = adev->powerplay.pp_funcs;
 	int ret = 0;
 
-	if (amdgpu_sriov_vf(adev))
+	if (amdgpu_sriov_vf(adev) ||
+		amdgpu_in_reset(adev))
 		return 0;
 
 	if (pp_funcs && pp_funcs->pause_power_profile) {
author maintainer
#490 lore raw

PATCH 488/733 drm/amdgpu: skip the VMID 0 flush for VRAM

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Arunpravin Paneer Selvam <[email protected]>

commit 87ceb8cba73d0b3c4025ff42495bccd8164acaed upstream.

Clear-on-release only runs on VRAM, which amdgpu_ttm_map_buffer() reaches via its direct MC address without programming a GART window, yet the wipe still forces a VMID 0 flush. On GFX11 (e.g. Navi33) that spurious SDMA flush can wedge the engine; only flush when a GART window is actually used.

v2: Let amdgpu_ttm_map_buffer() return whether the VMID 0 flush is needed,
and drive the clear and copy paths from that. (Christian)
v3: Make the vm_needs_flush output parameter mandatory instead of
allowing NULL. (Christian)

Fixes a68c7eaa7a8f ("drm/amdgpu: Enable clear page functionality")
Cc Christian König <[email protected]>
(cherry picked from commit a306e406e570b74318ff7d80e5b07b540ca1d3a9)
---
 drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c |   20 ++++++++++++++------
 1 file changed, 14 insertions(+), 6 deletions(-)
drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c +14 -6
@@ -190,6 +190,8 @@ amdgpu_ttm_job_submit(struct amdgpu_devi
  * @tmz: if we should setup a TMZ enabled mapping
  * @size: in number of bytes to map, out number of bytes mapped
  * @addr: resulting address inside the MC address space
+ * @vm_needs_flush: out, set true if a GART window was programmed (VMID 0 flush
+ *		    needed) or false for a direct address
  *
  * Setup one of the GART windows to access a specific piece of memory or return
  * the physical address for local memory.
@@ -199,7 +201,8 @@ static int amdgpu_ttm_map_buffer(struct
 				 struct ttm_resource *mem,
 				 struct amdgpu_res_cursor *mm_cur,
 				 unsigned int window,
-				 bool tmz, uint64_t *size, uint64_t *addr)
+				 bool tmz, uint64_t *size, uint64_t *addr,
+				 bool *vm_needs_flush)
 {
 	struct amdgpu_device *adev = amdgpu_ttm_adev(bo->bdev);
 	unsigned int offset, num_pages, num_dw, num_bytes;
@@ -220,9 +223,12 @@ static int amdgpu_ttm_map_buffer(struct
 	if (!tmz && mem->start != AMDGPU_BO_INVALID_OFFSET) {
 		*addr = amdgpu_ttm_domain_start(adev, mem->mem_type) +
 			mm_cur->start;
+		*vm_needs_flush = false;
 		return 0;
 	}
 
+	/* A GART window is programmed below, so its VMID 0 TLB needs a flush */
+	*vm_needs_flush = true;
 
 	/*
 	 * If start begins at an offset inside the page, then adjust the size
@@ -322,6 +328,7 @@ static int amdgpu_ttm_copy_mem_to_mem(st
 	while (src_mm.remaining) {
 		uint64_t from, to, cur_size, tiling_flags;
 		uint32_t num_type, data_format, max_com, write_compress_disable;
+		bool src_vm_flush, dst_vm_flush;
 		struct dma_fence *next;
 
 		/* Never copy more than 256MiB at once to avoid a timeout */
@@ -329,12 +336,12 @@ static int amdgpu_ttm_copy_mem_to_mem(st
 
 		/* Map src to window 0 and dst to window 1. */
 		r = amdgpu_ttm_map_buffer(entity, src->bo, src->mem, &src_mm,
-					  0, tmz, &cur_size, &from);
+					  0, tmz, &cur_size, &from, &src_vm_flush);
 		if (r)
 			goto error;
 
 		r = amdgpu_ttm_map_buffer(entity, dst->bo, dst->mem, &dst_mm,
-					  1, tmz, &cur_size, &to);
+					  1, tmz, &cur_size, &to, &dst_vm_flush);
 		if (r)
 			goto error;
 
@@ -362,7 +369,7 @@ static int amdgpu_ttm_copy_mem_to_mem(st
 		}
 
 		r = amdgpu_copy_buffer(adev, entity, from, to, cur_size, resv,
-				       &next, true, copy_flags);
+				       &next, src_vm_flush || dst_vm_flush, copy_flags);
 		if (r)
 			goto error;
 
@@ -2580,6 +2587,7 @@ int amdgpu_ttm_clear_buffer(struct amdgp
 	struct amdgpu_device *adev = amdgpu_ttm_adev(bo->tbo.bdev);
 	struct dma_fence *fence = NULL;
 	struct amdgpu_res_cursor dst;
+	bool vm_needs_flush = false;
 	int r;
 
 	if (!entity)
@@ -2601,13 +2609,13 @@ int amdgpu_ttm_clear_buffer(struct amdgp
 		cur_size = min(dst.size, 256ULL << 20);
 
 		r = amdgpu_ttm_map_buffer(entity, &bo->tbo, bo->tbo.resource, &dst,
-					  0, false, &cur_size, &to);
+					  0, false, &cur_size, &to, &vm_needs_flush);
 		if (r)
 			goto error;
 
 		r = amdgpu_ttm_fill_mem(adev, entity,
 					0, to, cur_size, resv,
-					&next, true, k_job_id);
+					&next, vm_needs_flush, k_job_id);
 		if (r)
 			goto error;
 
author maintainer
#491 lore raw

PATCH 489/733 watchdog: sunxi_wdt: preserve boot-enabled watchdog

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: James Hilliard <[email protected]>

commit aab55360fa11a2c054798a484ac67ad606f563e4 upstream.

sunxi_wdt_probe() unconditionally stops the watchdog even when firmware left it running. This opens an unprotected interval during boot and prevents CONFIG_WATCHDOG_HANDLE_BOOT_ENABLED from taking over the active watchdog.

Detect an enabled watchdog and decode its programmed interval. Preserve representable timeouts, and round the 0.5-second interval up to the minimum representable one-second timeout. Use the configured timeout for reserved interval encodings. Set the Linux reset mode and ping the watchdog without clearing its enable bit, then mark it hardware-running before registration so the watchdog core services it until userspace takes control. Leave disabled watchdogs untouched.

---
 drivers/watchdog/sunxi_wdt.c |   45 ++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 44 insertions(+), 1 deletion(-)
drivers/watchdog/sunxi_wdt.c +44 -1
@@ -128,6 +128,38 @@ static int sunxi_wdt_ping(struct watchdo
 	return 0;
 }
 
+static bool sunxi_wdt_is_running(struct watchdog_device *wdt_dev)
+{
+	struct sunxi_wdt_dev *sunxi_wdt = watchdog_get_drvdata(wdt_dev);
+	const struct sunxi_wdt_reg *regs = sunxi_wdt->wdt_regs;
+
+	return readl(sunxi_wdt->wdt_base + regs->wdt_mode) & WDT_MODE_EN;
+}
+
+static unsigned int sunxi_wdt_get_timeout(struct watchdog_device *wdt_dev)
+{
+	struct sunxi_wdt_dev *sunxi_wdt = watchdog_get_drvdata(wdt_dev);
+	const struct sunxi_wdt_reg *regs = sunxi_wdt->wdt_regs;
+	unsigned int timeout;
+	u32 interval;
+
+	interval = readl(sunxi_wdt->wdt_base + regs->wdt_mode);
+	interval >>= regs->wdt_timeout_shift;
+	interval &= WDT_TIMEOUT_MASK;
+	/* Round the 0.5-second interval up to the minimum representable timeout. */
+	if (!interval)
+		return WDT_MIN_TIMEOUT;
+
+	for (timeout = WDT_MIN_TIMEOUT;
+	     timeout < ARRAY_SIZE(wdt_timeout_map); timeout++) {
+		if (wdt_timeout_map[timeout] == interval)
+			return timeout;
+	}
+
+	/* Reserved interval encoding. */
+	return 0;
+}
+
 static int sunxi_wdt_set_timeout(struct watchdog_device *wdt_dev,
 		unsigned int timeout)
 {
@@ -259,6 +291,7 @@ static int sunxi_wdt_probe(struct platfo
 {
 	struct device *dev = &pdev->dev;
 	struct sunxi_wdt_dev *sunxi_wdt;
+	unsigned int running_timeout;
 	int err;
 
 	sunxi_wdt = devm_kzalloc(dev, sizeof(*sunxi_wdt), GFP_KERNEL);
@@ -286,7 +319,17 @@ static int sunxi_wdt_probe(struct platfo
 
 	watchdog_set_drvdata(&sunxi_wdt->wdt_dev, sunxi_wdt);
 
-	sunxi_wdt_stop(&sunxi_wdt->wdt_dev);
+	if (sunxi_wdt_is_running(&sunxi_wdt->wdt_dev)) {
+		running_timeout = sunxi_wdt_get_timeout(&sunxi_wdt->wdt_dev);
+		if (running_timeout)
+			sunxi_wdt->wdt_dev.timeout = running_timeout;
+
+		err = sunxi_wdt_start(&sunxi_wdt->wdt_dev);
+		if (err)
+			return err;
+
+		set_bit(WDOG_HW_RUNNING, &sunxi_wdt->wdt_dev.status);
+	}
 
 	watchdog_stop_on_reboot(&sunxi_wdt->wdt_dev);
 	err = devm_watchdog_register_device(dev, &sunxi_wdt->wdt_dev);
author maintainer
#492 lore raw

PATCH 490/733 virtio_mmio: disable IRQ wake before free_irq

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Xiong Weimin <[email protected]>

commit d14d693adb055e98ca705822ba6daebc18602d9a upstream.

When the DT node has "wakeup-source", vm_find_vqs() calls enable_irq_wake() on the shared IRQ, but vm_del_vqs() freed that IRQ without a matching disable_irq_wake(). That leaves a wake reference behind and can warn on later free_irq()/request_irq() cycles.

Record whether enable_irq_wake() succeeded, and disable it in vm_del_vqs() before free_irq().

Fixes 02213273f72a ("virtio_mmio: add support to set IRQ of a virtio device as wakeup source")

Message-ID: <[email protected]>

---
 drivers/virtio/virtio_mmio.c |   16 +++++++++++++---
 1 file changed, 13 insertions(+), 3 deletions(-)
drivers/virtio/virtio_mmio.c +13 -3
@@ -87,6 +87,9 @@ struct virtio_mmio_device {
 
 	void __iomem *base;
 	unsigned long version;
+
+	/* True if enable_irq_wake() succeeded for the shared IRQ. */
+	bool wake_irq_enabled;
 };
 
 /* Configuration interface */
@@ -329,11 +332,17 @@ static void vm_del_vqs(struct virtio_dev
 {
 	struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
 	struct virtqueue *vq, *n;
+	int irq = platform_get_irq(vm_dev->pdev, 0);
 
 	list_for_each_entry_safe(vq, n, &vdev->vqs, list)
 		vm_del_vq(vq);
 
-	free_irq(platform_get_irq(vm_dev->pdev, 0), vm_dev);
+	if (vm_dev->wake_irq_enabled) {
+		disable_irq_wake(irq);
+		vm_dev->wake_irq_enabled = false;
+	}
+
+	free_irq(irq, vm_dev);
 }
 
 static void vm_synchronize_cbs(struct virtio_device *vdev)
@@ -460,8 +469,9 @@ static int vm_find_vqs(struct virtio_dev
 	if (err)
 		return err;
 
-	if (of_property_read_bool(vm_dev->pdev->dev.of_node, "wakeup-source"))
-		enable_irq_wake(irq);
+	if (of_property_read_bool(vm_dev->pdev->dev.of_node, "wakeup-source") &&
+	    !enable_irq_wake(irq))
+		vm_dev->wake_irq_enabled = true;
 
 	for (i = 0; i < nvqs; ++i) {
 		struct virtqueue_info *vqi = &vqs_info[i];
author maintainer
#493 lore raw

PATCH 491/733 ufs: create the root dentry after loading cylinder metadata

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Ali Ahmet Memis <[email protected]>

commit 55a4c98abb9694b067c6a031d11501f06b6b523c upstream.

ufs_fill_super() installed sb->s_root before it loaded the cylinder group structures for a writable mount:

	sb->s_root = d_make_root(inode);
	...
	if (!sb_rdonly(sb))
		if (!ufs_read_cylinder_structures(sb))
			goto failed;

When ufs_read_cylinder_structures() failed, the error path freed the in-core superblock information and set sb->s_fs_info to NULL while sb->s_root stayed installed. get_tree_bdev() then reached deactivate_locked_super(), and because s_root was present, generic_shutdown_super() called sync_filesystem() and the put_super operation. Both dereference UFS_SB(sb), which is now NULL, so a mount that fails only while reading the cylinder groups oopses during teardown. A crafted image whose first cylinder group cannot be read reaches this path.

Load the cylinder group metadata first and create the root dentry last, so the superblock is published to the VFS only once it is fully set up. ufs_setup_cstotal() and ufs_read_cylinder_structures() take only the super_block and do not use the root inode, so the reordering is safe.

---
 fs/ufs/super.c |   17 +++++++++--------
 1 file changed, 9 insertions(+), 8 deletions(-)
fs/ufs/super.c +9 -8
@@ -1199,6 +1199,15 @@ magic_found:
 	sb->s_maxbytes = ufs_max_bytes(sb);
 	sb->s_max_links = UFS_LINK_MAX;
 
+	ufs_setup_cstotal(sb);
+	/*
+	 * Read cylinder group structures
+	 */
+	if (!sb_rdonly(sb))
+		if (!ufs_read_cylinder_structures(sb))
+			goto failed;
+
+	/* create the root dentry last, once UFS_SB(sb) is fully set up */
 	inode = ufs_iget(sb, UFS_ROOTINO);
 	if (IS_ERR(inode)) {
 		ret = PTR_ERR(inode);
@@ -1210,14 +1219,6 @@ magic_found:
 		goto failed;
 	}
 
-	ufs_setup_cstotal(sb);
-	/*
-	 * Read cylinder group structures
-	 */
-	if (!sb_rdonly(sb))
-		if (!ufs_read_cylinder_structures(sb))
-			goto failed;
-
 	UFSD("EXIT\n");
 	return 0;
 
author maintainer
#494 lore raw

PATCH 492/733 ufs: validate cylinder group metadata before caching it

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Ali Ahmet Memis <[email protected]>

commit c9d263be26806d388129fab8c6904bed197fc6af upstream.

ufs_read_cylinder() copies the cylinder group index and the rotor positions straight from the on-disk group and caches them without any check:

	ucpi->c_cgx    = fs32_to_cpu(sb, ucg->cg_cgx);
	ucpi->c_rotor  = fs32_to_cpu(sb, ucg->cg_rotor);
	ucpi->c_frotor = fs32_to_cpu(sb, ucg->cg_frotor);
	ucpi->c_irotor = fs32_to_cpu(sb, ucg->cg_irotor);

They are then used as indices during allocation and free:

- c_cgx indexes the cylinder summary array as UFS_SB(sb)->fs_cs(ucpi->c_cgx), so a value past s_ncg writes a 32 bit count outside the s_csp allocation.

- c_frotor becomes a bitmap scan start, start = c_frotor >> 3, and then length = ((s_fpg + 7) >> 3) - start. A start beyond the block bitmap wraps the unsigned length to a huge value, so ubh_scanc() walks far past the cylinder group buffers. c_irotor drives the inode bitmap the same way.

A crafted image can set any of these freely, turning an ordinary allocation into an out of bounds access.

Reject a cylinder group whose recorded index does not match the group being read, or whose rotors fall outside the group, before the metadata is cached. Valid filesystems keep cg_cgx equal to the group number and the rotors within the group, so only malformed images are rejected.

---
 fs/ufs/cylinder.c |   10 ++++++++++
 1 file changed, 10 insertions(+)
fs/ufs/cylinder.c +10 -0
@@ -68,6 +68,16 @@ static bool ufs_read_cylinder(struct sup
 	ucpi->c_clustersumoff = fs32_to_cpu(sb, ucg->cg_u.cg_44.cg_clustersumoff);
 	ucpi->c_clusteroff = fs32_to_cpu(sb, ucg->cg_u.cg_44.cg_clusteroff);
 	ucpi->c_nclusterblks = fs32_to_cpu(sb, ucg->cg_u.cg_44.cg_nclusterblks);
+
+	/* these on-disk values become array and bitmap indices */
+	if (ucpi->c_cgx != cgno ||
+	    ucpi->c_rotor >= uspi->s_fpg ||
+	    ucpi->c_frotor >= uspi->s_fpg ||
+	    ucpi->c_irotor >= uspi->s_ipg) {
+		ufs_error(sb, __func__,
+			  "inconsistent metadata in cylinder group %u\n", cgno);
+		goto failed;
+	}
 	UFSD("EXIT\n");
 	return true;
 	
author maintainer
#495 lore raw

PATCH 493/733 tunnels: Drop stale dst when building an ICMP error for PMTUD

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Ido Schimmel <[email protected]>

commit b58d749633203d92c265317b45fccee555090352 upstream.

Bridged UDP tunnels such as VXLAN and GENEVE build an ICMP error packet around an overlay packet if the packet is going to exceed the underlay path MTU. The ICMP error packet is then injected back into the Rx path with the source and destination addresses swapped, so that it will be delivered to the overlay source.

If the overlay packet was routed to the UDP tunnel or locally generated, then it is already carrying a valid dst entry and this entry is not dropped when transforming the packet to an ICMP error packet. This causes the IP layer to reuse the dst entry, leading to the ICMP error packet being dropped or routed out of the UDP tunnel interface in case of forwarding.

Prior to the blamed commit this could not happen, as skb_tunnel_check_pmtu() did not build ICMP errors for PACKET_HOST packets. Such packets were instead encapsulated and, unless the DF bit was set in the outer header, fragmented by the underlay.

Fix this by making sure that the ICMP error packet does not have a valid dst entry, thereby forcing the IP layer to perform a route lookup.

Adjust the bridged PMTU exception selftests accordingly. When the local sender in ns_a pings the overlay destination with a deadline (-w), ping exits on the first socket error before any reply is received and returns a non-zero exit code. The test therefore only passed because the ICMP error was never delivered. Use a packet count (-c) like the ns_c line above it, so that the ICMP error counts against the packet budget and the exit code depends on whether echo replies were received. This passes with and without the fix.

---
 net/ipv4/ip_tunnel_core.c           |    6 ++++++
 tools/testing/selftests/net/pmtu.sh |    2 +-
 2 files changed, 7 insertions(+), 1 deletion(-)
net/ipv4/ip_tunnel_core.c +6 -0
@@ -268,6 +268,9 @@ static int iptunnel_pmtud_build_icmp(str
 	eth_header(skb, skb->dev, ntohs(eh.h_proto), eh.h_source, eh.h_dest, 0);
 	skb_reset_mac_header(skb);
 
+	if (skb_valid_dst(skb))
+		skb_dst_drop(skb);
+
 	return skb->len;
 }
 
@@ -371,6 +374,9 @@ static int iptunnel_pmtud_build_icmpv6(s
 	eth_header(skb, skb->dev, ntohs(eh.h_proto), eh.h_source, eh.h_dest, 0);
 	skb_reset_mac_header(skb);
 
+	if (skb_valid_dst(skb))
+		skb_dst_drop(skb);
+
 	return skb->len;
 }
 
tools/testing/selftests/net/pmtu.sh +1 -1
@@ -1457,7 +1457,7 @@ test_pmtu_ipvX_over_bridged_vxlanY_or_ge
 	mtu "${ns_b}" ${type}_b $((${ll_mtu} + 1000))
 
 	run_cmd ${ns_c} ${ping} -q -M want -i 0.1 -c 10 -s $((${ll_mtu} + 500)) ${dst} || return 1
-	run_cmd ${ns_a} ${ping} -q -M want -i 0.1 -w 1  -s $((${ll_mtu} + 500)) ${dst} || return 1
+	run_cmd ${ns_a} ${ping} -q -M want -i 0.1 -c 10 -s $((${ll_mtu} + 500)) ${dst} || return 1
 
 	# Check that exceptions were created
 	pmtu="$(route_get_dst_pmtu_from_exception "${ns_c}" ${dst})"
author maintainer
#496 lore raw

PATCH 494/733 tick/broadcast: Plug clockevents replacement race

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Thomas Gleixner <[email protected]>

commit 113a9796effe3376d2ec5aabcca1fef4fef4cd62 upstream.

朱恺乾 reported and decoded the following race condition when a broadcast device is replaced:

CPUA CPUB
__tick_broadcast_oneshot_control()

   bc = tick_broadcast_device.evtdev;
					tick_install_broadcast_device(dev)
        				clockevents_exchange_device(cur, dev)
					   shutdown(cur);
					   detach(cur);
					   cur->handler = noop;
					   tick_broadcast_device.evtdev = dev;

tick_broadcast_set_event(bc, next_event); <- FAIL: arms a detached device.

If the original broadcast device has a restricted interrupt affinity mask and the last CPU in that mask goes offline then the BUG() in tick_cleanup_dead_cpu() triggers because the clockevent device is not in detached state.

The reason for this is that tick_install_broadcast_device() is not serialized vs. tick broadcast operations.

The obvious cure is to serialize tick_install_broadcast_device() with tick_broadcast_lock against a concurrent tick broadcast operation.

That requires to split clockevents_exchange_device() into two parts, one which does the exchange, shutdown and detach operation and the other which drops the module reference count. This is required because the module reference cannot be dropped while holding tick_broadcast_lock.

Let clockevents_exchange_device() do both operations as before, but let the broadcast device code take the two step approach and do the device exchange under tick_broadcast_lock and drop the module reference count after releasing it.

---
 kernel/time/clockevents.c    |   33 ++++++++++++++++++++-------------
 kernel/time/tick-broadcast.c |   36 ++++++++++++++++++++++--------------
 kernel/time/tick-internal.h  |    2 ++
 3 files changed, 44 insertions(+), 27 deletions(-)
kernel/time/clockevents.c +20 -13
@@ -615,34 +615,41 @@ void clockevents_handle_noop(struct cloc
 {
 }
 
-/**
- * clockevents_exchange_device - release and request clock devices
- * @old:	device to release (can be NULL)
- * @new:	device to request (can be NULL)
- *
- * Called from various tick functions with clockevents_lock held and
- * interrupts disabled.
- */
-void clockevents_exchange_device(struct clock_event_device *old,
-				 struct clock_event_device *new)
+void __clockevents_exchange_device(struct clock_event_device *old,
+				   struct clock_event_device *new)
 {
 	/*
 	 * Caller releases a clock event device. We queue it into the
 	 * released list and do a notify add later.
 	 */
 	if (old) {
-		module_put(old->owner);
 		clockevents_switch_state(old, CLOCK_EVT_STATE_DETACHED);
 		list_move(&old->list, &clockevents_released);
 	}
 
 	if (new) {
-		BUG_ON(!clockevent_state_detached(new));
+		WARN_ON(!clockevent_state_detached(new));
 		clockevents_shutdown(new);
 	}
 }
 
 /**
+ * clockevents_exchange_device - release and request clock devices
+ * @old:	device to release (can be NULL)
+ * @new:	device to request (can be NULL)
+ *
+ * Called from various tick functions with clockevents_lock held and
+ * interrupts disabled.
+ */
+void clockevents_exchange_device(struct clock_event_device *old,
+				 struct clock_event_device *new)
+{
+	__clockevents_exchange_device(old, new);
+	if (old)
+		module_put(old->owner);
+}
+
+/**
  * clockevents_suspend - suspend clock devices
  */
 void clockevents_suspend(void)
@@ -699,7 +706,7 @@ void tick_offline_cpu(unsigned int cpu)
 		if (cpumask_test_cpu(cpu, dev->cpumask) &&
 		    cpumask_weight(dev->cpumask) == 1 &&
 		    !tick_is_broadcast_device(dev)) {
-			BUG_ON(!clockevent_state_detached(dev));
+			WARN_ON(!clockevent_state_detached(dev));
 			list_del(&dev->list);
 		}
 	}
kernel/time/tick-broadcast.c +22 -14
@@ -165,23 +165,31 @@ static bool tick_set_oneshot_wakeup_devi
  */
 void tick_install_broadcast_device(struct clock_event_device *dev, int cpu)
 {
-	struct clock_event_device *cur = tick_broadcast_device.evtdev;
+	struct clock_event_device *cur;
 
-	if (tick_set_oneshot_wakeup_device(dev, cpu))
-		return;
+	scoped_guard(raw_spinlock_irqsave, &tick_broadcast_lock) {
 
-	if (!tick_check_broadcast_device(cur, dev))
-		return;
+		if (tick_set_oneshot_wakeup_device(dev, cpu))
+			return;
 
-	if (!try_module_get(dev->owner))
-		return;
+		cur = tick_broadcast_device.evtdev;
+		if (!tick_check_broadcast_device(cur, dev))
+			return;
 
-	clockevents_exchange_device(cur, dev);
+		if (!try_module_get(dev->owner))
+			return;
+
+		__clockevents_exchange_device(cur, dev);
+		if (cur)
+			cur->event_handler = clockevents_handle_noop;
+		WRITE_ONCE(tick_broadcast_device.evtdev, dev);
+		if (!cpumask_empty(tick_broadcast_mask))
+			tick_broadcast_start_periodic(dev);
+	}
+
+	/* Module release must be outside of the lock */
 	if (cur)
-		cur->event_handler = clockevents_handle_noop;
-	tick_broadcast_device.evtdev = dev;
-	if (!cpumask_empty(tick_broadcast_mask))
-		tick_broadcast_start_periodic(dev);
+		module_put(cur->owner);
 
 	if (!(dev->features & CLOCK_EVT_FEAT_ONESHOT))
 		return;
@@ -1218,7 +1226,7 @@ int tick_broadcast_oneshot_active(void)
  */
 bool tick_broadcast_oneshot_available(void)
 {
-	struct clock_event_device *bc = tick_broadcast_device.evtdev;
+	struct clock_event_device *bc = READ_ONCE(tick_broadcast_device.evtdev);
 
 	return bc ? bc->features & CLOCK_EVT_FEAT_ONESHOT : false;
 }
@@ -1226,7 +1234,7 @@ bool tick_broadcast_oneshot_available(vo
 #else
 int __tick_broadcast_oneshot_control(enum tick_broadcast_state state)
 {
-	struct clock_event_device *bc = tick_broadcast_device.evtdev;
+	struct clock_event_device *bc = READ_ONCE(tick_broadcast_device.evtdev);
 
 	if (!bc || (bc->features & CLOCK_EVT_FEAT_HRTIMER))
 		return -EBUSY;
kernel/time/tick-internal.h +2 -0
@@ -54,6 +54,8 @@ static inline void clockevent_set_state(
 }
 
 extern void clockevents_shutdown(struct clock_event_device *dev);
+extern void __clockevents_exchange_device(struct clock_event_device *old,
+					  struct clock_event_device *new);
 extern void clockevents_exchange_device(struct clock_event_device *old,
 					struct clock_event_device *new);
 extern void clockevents_switch_state(struct clock_event_device *dev,
author maintainer
#497 lore raw

PATCH 495/733 tools/bootconfig: Fix integer overflow and truncation in size checks

In reply to Greg Kroah-Hartman

7.2-stable review patch. If anyone has any objections, please let me know.

------------------

From: Masami Hiramatsu (Google) <[email protected]>

commit 462d0b066b613103f579793031429db2ca23abc0 upstream.

Sashiko reported that on 32-bit systems, if an attacker crafts size in the bootconfig footer such that adding BOOTCONFIG_FOOTER_SIZE wraps around (for instance, if size is 0xFFFFFFFF), the size check in load_xbc_from_initrd() can be bypassed:

    if (stat.st_size < size + BOOTCONFIG_FOOTER_SIZE) {
        pr_err("bootconfig size is too big\n");
        return -E2BIG;
    }

Furthermore, on 64-bit systems with an initrd > 4.29 GB, comparing a corrupted 32-bit size (e.g. 0xFFFFFFFF) against
stat.st_size - BOOTCONFIG_FOOTER_SIZE can also bypass the check if size is not bounded. Similarly, load_xbc_file() passes 64-bit stat.st_size directly into the 32-bit int size parameter of load_xbc_fd(), truncating large standalone files (>= 2GB).

In both cases, passing 0xFFFFFFFF to load_xbc_fd() truncates to -1, resulting in malloc(0), an integer overflow in read(), and an out-of-bounds null-byte write.

Fix this by:
1. Rejecting size > XBC_DATA_MAX or
size > stat.st_size - BOOTCONFIG_FOOTER_SIZE in load_xbc_from_initrd().
2. Rejecting stat.st_size > XBC_DATA_MAX in load_xbc_file() before passing it to load_xbc_fd().
3. Checking size < 0 || size > XBC_DATA_MAX defensively in load_xbc_fd().

---
 tools/bootconfig/main.c |   13 ++++++++++++-
 1 file changed, 12 insertions(+), 1 deletion(-)
tools/bootconfig/main.c +12 -1
@@ -140,6 +140,9 @@ static int load_xbc_fd(int fd, char **bu
 {
 	int ret;
 
+	if (size < 0 || size > XBC_DATA_MAX)
+		return -EINVAL;
+
 	*buf = malloc(size + 1);
 	if (!*buf)
 		return -ENOMEM;
@@ -168,6 +171,13 @@ static int load_xbc_file(const char *pat
 		return ret;
 	}
 
+	if (stat.st_size > XBC_DATA_MAX) {
+		pr_err("%s size is too big\n", path);
+		ret = -E2BIG;
+		close(fd);
+		return ret;
+	}
+
 	ret = load_xbc_fd(fd, buf, stat.st_size);
 
 	close(fd);
@@ -218,7 +228,8 @@ static int load_xbc_from_initrd(int fd,
 	csum = le32toh(csum);
 
 	/* Wrong size error  */
-	if (stat.st_size < size + BOOTCONFIG_FOOTER_SIZE) {
+	if (size > XBC_DATA_MAX ||
+	    size > stat.st_size - BOOTCONFIG_FOOTER_SIZE) {
 		pr_err("bootconfig size is too big\n");
 		return -E2BIG;
 	}