Skip to contentSkip to conversation

6.12.110-rc1 review

PATCH Applied Greg Kroah-Hartman 23 participants 1,469 messages Sep 12 – Sep 22 Page 7 of 11

Subsystems (7)
Stable branch Documentation VFS SCSI +3 more subsystems
lore
Share & tools
lore
author maintainer
#842 lore raw

PATCH 806/1376 wifi: mt76: mt7996: fix capability of EHT-MCS 15 in MRU

In reply to Greg Kroah-Hartman

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

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

From: Shayne Chen <[email protected]>

[ Upstream commit 29e889c4ada83c69d10a3937f5ae2934306e2e3d ]

According to the definition in IEEE Std 802.11be-2024, Table 9-417r:
- If 80 MHz is not supported, bit 1-3 are set to 0.
- If 160 MHz is not supported, bit 2-3 are set to 0.
- If 320 MHz is not supported, bit 3 is set to 0.

Fixes 348533eb968d ("wifi: mt76: mt7996: add EHT capability init")
---
 drivers/net/wireless/mediatek/mt76/mt7996/init.c | 16 ++++++++++------
 1 file changed, 10 insertions(+), 6 deletions(-)
drivers/net/wireless/mediatek/mt76/mt7996/init.c +10 -6
index fc2d46b10b720..d3d63f07d7b05 100644
@@ -1181,7 +1181,6 @@ mt7996_init_eht_caps(struct mt7996_phy *phy, enum nl80211_band band,
 	struct ieee80211_sta_eht_cap *eht_cap = &data->eht_cap;
 	struct ieee80211_eht_cap_elem_fixed *eht_cap_elem = &eht_cap->eht_cap_elem;
 	struct ieee80211_eht_mcs_nss_supp *eht_nss = &eht_cap->eht_mcs_nss_supp;
-	enum nl80211_chan_width width = phy->mt76->chandef.width;
 	int nss = hweight8(phy->mt76->antenna_mask);
 	int sts = hweight16(phy->mt76->chainmask);
 	u8 val;
@@ -1250,11 +1249,16 @@ mt7996_init_eht_caps(struct mt7996_phy *phy, enum nl80211_band band,
 		u8_encode_bits(u8_get_bits(1, GENMASK(1, 0)),
 			       IEEE80211_EHT_PHY_CAP5_MAX_NUM_SUPP_EHT_LTF_MASK);
 
-	val = width == NL80211_CHAN_WIDTH_320 ? 0xf :
-	      width == NL80211_CHAN_WIDTH_160 ? 0x7 :
-	      width == NL80211_CHAN_WIDTH_80 ? 0x3 : 0x1;
-	eht_cap_elem->phy_cap_info[6] =
-		u8_encode_bits(val, IEEE80211_EHT_PHY_CAP6_MCS15_SUPP_MASK);
+	eht_cap_elem->phy_cap_info[6] = IEEE80211_EHT_PHY_CAP6_MCS15_SUPP_MASK;
+	if (band != NL80211_BAND_6GHZ) {
+		eht_cap_elem->phy_cap_info[6] &=
+			~IEEE80211_EHT_PHY_CAP6_MCS15_SUPP_320MHZ;
+
+		if (band != NL80211_BAND_5GHZ)
+			eht_cap_elem->phy_cap_info[6] &=
+				~(IEEE80211_EHT_PHY_CAP6_MCS15_SUPP_160MHZ |
+				  IEEE80211_EHT_PHY_CAP6_MCS15_SUPP_80MHZ);
+	}
 
 	val = u8_encode_bits(nss, IEEE80211_EHT_MCS_NSS_RX) |
 	      u8_encode_bits(nss, IEEE80211_EHT_MCS_NSS_TX);
Signature · 1 line
2.53.0
author maintainer
#843 lore raw

PATCH 807/1376 wifi: mt76: fix non-AQL packet accounting for MLO stations

In reply to Greg Kroah-Hartman

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

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

From: Michael-CY Lee <[email protected]>

[ Upstream commit 8ae659743ba936b22ecb4620815887728e2820d6 ]

__mt76_tx_queue_skb() overrides the wcid passed by the driver with sta->drv_priv, so the wcid might incorrectly be changed after TX, causing wcid->non_aql_packets to be counted on the wrong wcid. For example, on the AP side, if a station's setup link is the 5G link and the station uses 2G to transmit a frame, the value of non_aql_packets is increased on the 5G wcid but decreased on the 2G wcid. Once the inflated counter exceeds MT_MAX_NON_AQL_PKT, the TX scheduler permanently refuses to service the station.

Drop the reassignment and account on the wcid used for transmission. This also records the actual wcid in the queue entry.

Fixes e1378e5228aa ("mt76: rely on AQL for burst size limits on tx queueing")
---
 drivers/net/wireless/mediatek/mt76/tx.c | 4 ----
 1 file changed, 4 deletions(-)
drivers/net/wireless/mediatek/mt76/tx.c +0 -4
index 634b6dacd1e0d..1184357d933d2 100644
@@ -313,10 +313,6 @@ __mt76_tx_queue_skb(struct mt76_phy *phy, int qid, struct sk_buff *skb,
 	if (idx < 0 || !sta)
 		return idx;
 
-	wcid = (struct mt76_wcid *)sta->drv_priv;
-	if (!wcid->sta)
-		return idx;
-
 	q->entry[idx].wcid = wcid->idx;
 
 	if (!non_aql)
Signature · 1 line
2.53.0
author maintainer
#844 lore raw

PATCH 808/1376 wifi: mt76: mt7996: validate RX band_idx before dereferencing phys[]

In reply to Greg Kroah-Hartman

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

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

From: Felix Fietkau <[email protected]>

[ Upstream commit 2243778a5fae8329ab5f18e7adcd7e03b911a1b7 ]

band_idx comes from a 2-bit descriptor field (0-3) and was used directly to index dev->mt76.phys[] (size __MT_MAX_BAND == 3) and dereference the result. A corrupt or reserved descriptor value could index out of bounds or hit a NULL phy on parts with fewer bands. Reject invalid band indices, mirroring mt7996_rx_get_wcid().

Fixes 98686cd21624 ("wifi: mt76: mt7996: add driver for MediaTek Wi-Fi 7 (802.11be) devices")
---
 drivers/net/wireless/mediatek/mt76/mt7996/mac.c | 6 ++++++
 1 file changed, 6 insertions(+)
drivers/net/wireless/mediatek/mt76/mt7996/mac.c +6 -0
index 54bbc871c0bdc..116050a987a61 100644
@@ -454,7 +454,13 @@ mt7996_mac_fill_rx(struct mt7996_dev *dev, enum mt76_rxq_id q,
 	memset(status, 0, sizeof(*status));
 
 	band_idx = FIELD_GET(MT_RXD1_NORMAL_BAND_IDX, rxd1);
+	if (!mt7996_band_valid(dev, band_idx))
+		return -EINVAL;
+
 	mphy = dev->mt76.phys[band_idx];
+	if (!mphy)
+		return -EINVAL;
+
 	phy = mphy->priv;
 	status->phy_idx = mphy->band_idx;
 
Signature · 1 line
2.53.0
author maintainer
#845 lore raw

PATCH 809/1376 wifi: mt76: mt7996: set MT76_MCU_RESET before waking MCU waiters on full reset

In reply to Greg Kroah-Hartman

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

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

From: Felix Fietkau <[email protected]>

[ Upstream commit 6469ae71e7e5d0132c934972f628f346ad0379cd ]

mt7996_mac_full_reset() called wake_up(&dev->mt76.mcu.wait) without first setting MT76_MCU_RESET. The MCU response wait condition only checks the response queue and that bit, so the wake-up released nobody: a thread blocked in an MCU command against the dead firmware (typically holding dev->mt76.mutex) stayed asleep until its multi-second timeout, stalling recovery. Set the bit before the wake-up, as mt7915 does.

Fixes 27015b6fbcca ("wifi: mt76: mt7996: enable full system reset support")
---
 drivers/net/wireless/mediatek/mt76/mt7996/mac.c | 1 +
 1 file changed, 1 insertion(+)
drivers/net/wireless/mediatek/mt76/mt7996/mac.c +1 -0
index 116050a987a61..24e0346ea3d88 100644
@@ -1798,6 +1798,7 @@ mt7996_mac_full_reset(struct mt7996_dev *dev)
 	phy3 = mt7996_phy3(dev);
 	dev->recovery.hw_full_reset = true;
 
+	set_bit(MT76_MCU_RESET, &dev->mphy.state);
 	wake_up(&dev->mt76.mcu.wait);
 	ieee80211_stop_queues(mt76_hw(dev));
 	if (phy2)
Signature · 1 line
2.53.0
author maintainer
#846 lore raw

PATCH 810/1376 wifi: mt76: mt7915: clear wcid mask under mutex after RCU pointer clear

In reply to Greg Kroah-Hartman

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

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

From: Felix Fietkau <[email protected]>

[ Upstream commit 6486e11a6e2f679597af2d5bb48c3b07a2b2a7ba ]

mt7915_remove_interface() cleared the wcid mask bit with no lock held and before clearing the RCU wcid pointer. The mask is a non-atomic RMW shared with the allocators, which all run under dev->mt76.mutex; on DBDC the two wiphys share one mt76_dev, so this raced add_interface/sta_add on the other band and could leak or double-hand-out a wcid. Clearing the bit before the RCU pointer also let a concurrent allocation reuse the index and publish its wcid, which the subsequent NULL assignment then wiped. Move the clear into the existing mutex section, after the RCU pointer is cleared.

Fixes f3049b88b2b3 ("wifi: mt76: mt7915: allocate vif wcid in the same range as stations")
---
 drivers/net/wireless/mediatek/mt76/mt7915/main.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/wireless/mediatek/mt76/mt7915/main.c +1 -1
index 9b0fde73b8945..4875f51472ed8 100644
@@ -296,7 +296,6 @@ static void mt7915_remove_interface(struct ieee80211_hw *hw,
 
 	mt7915_mcu_add_bss_info(phy, vif, false);
 	mt7915_mcu_add_sta(dev, vif, NULL, CONN_STATE_DISCONNECT, false);
-	mt76_wcid_mask_clear(dev->mt76.wcid_mask, mvif->sta.wcid.idx);
 
 	mutex_lock(&dev->mt76.mutex);
 	mt76_testmode_reset(phy->mt76, true);
@@ -312,6 +311,7 @@ static void mt7915_remove_interface(struct ieee80211_hw *hw,
 	mutex_lock(&dev->mt76.mutex);
 	dev->mt76.vif_mask &= ~BIT_ULL(mvif->mt76.idx);
 	phy->omac_mask &= ~BIT_ULL(mvif->mt76.omac_idx);
+	mt76_wcid_mask_clear(dev->mt76.wcid_mask, mvif->sta.wcid.idx);
 	mutex_unlock(&dev->mt76.mutex);
 
 	spin_lock_bh(&dev->mt76.sta_poll_lock);
Signature · 1 line
2.53.0
author maintainer
#847 lore raw

PATCH 811/1376 wifi: mt76: mt7915: avoid nss underflow in mt7915_mcu_get_sta_nss

In reply to Greg Kroah-Hartman

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

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

From: Felix Fietkau <[email protected]>

[ Upstream commit 4a2f4be532e3ea4e2b536e411793a05aaa51af25 ]

If a peer's VHT/HE MCS map has no supported spatial stream (all fields 0x3), the loop exits with nss == 0 and the function returned (u8)-1 (255), which was then written into the firmware sta_rec_bf beamforming fields. Clamp the result to 0.

Fixes 89029a85482c ("mt76: mt7915: add Tx beamformer support")
---
 drivers/net/wireless/mediatek/mt76/mt7915/mcu.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/wireless/mediatek/mt76/mt7915/mcu.c +1 -1
index 32e9c0960657a..dcebe55c9010e 100644
@@ -51,7 +51,7 @@ mt7915_mcu_get_sta_nss(u16 mcs_map)
 			break;
 	}
 
-	return nss - 1;
+	return nss ? nss - 1 : 0;
 }
 
 static void
Signature · 1 line
2.53.0
author maintainer
#848 lore raw

PATCH 812/1376 wifi: mt76: mt7996: dont report a zero TX bitrate

In reply to Greg Kroah-Hartman

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

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

From: Felix Fietkau <[email protected]>

[ Upstream commit d4d92ccded678c92c390003926097ddbb6516bc7 ]

mt7996_sta_statistics() set NL80211_STA_INFO_TX_BITRATE unconditionally after the block that already sets it, so a station with no rate info yet was reported to userspace with a valid-but-zero TX rate. Drop the redundant unconditional assignments; the in-block ones are sufficient.

Fixes b34f346b917e ("wifi: mt76: mt7996: drop return in mt7996_sta_statistics")
---
 drivers/net/wireless/mediatek/mt76/mt7996/main.c | 2 --
 1 file changed, 2 deletions(-)
drivers/net/wireless/mediatek/mt76/mt7996/main.c +0 -2
index 4d11083b86c09..bdb5e82d9f4e7 100644
@@ -1014,8 +1014,6 @@ static void mt7996_sta_statistics(struct ieee80211_hw *hw,
 		sinfo->txrate.flags = txrate->flags;
 		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE);
 	}
-	sinfo->txrate.flags = txrate->flags;
-	sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE);
 
 	sinfo->tx_failed = msta->wcid.stats.tx_failed;
 	sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_FAILED);
Signature · 1 line
2.53.0
author maintainer
#849 lore raw

PATCH 813/1376 wifi: mt76: mt7915: write RX header translation bit to the correct register

In reply to Greg Kroah-Hartman

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

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

From: Felix Fietkau <[email protected]>

[ Upstream commit 236145737480c4c0c515e09061d0d5f77cf52d3f ]

MT_MDP_DCR0_RX_HDR_TRANS_EN is a field of MT_MDP_DCR0, but monitor-mode handling applied it to the per-band MT_DMA_DCR0 register instead. As a result RX header translation was never disabled in the MDP when entering monitor mode, and an undocumented bit of MT_DMA_DCR0 was toggled. Target MT_MDP_DCR0, matching the mt7996 driver.

Fixes b2491018587a ("wifi: mt76: mt7915: fix monitor mode issues")
---
 drivers/net/wireless/mediatek/mt76/mt7915/main.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/wireless/mediatek/mt76/mt7915/main.c +1 -1
index 4875f51472ed8..529926c287bce 100644
@@ -490,7 +490,7 @@ static int mt7915_config(struct ieee80211_hw *hw, u32 changed)
 
 		mt76_rmw_field(dev, MT_DMA_DCR0(band), MT_DMA_DCR0_RXD_G5_EN,
 			       enabled);
-		mt76_rmw_field(dev, MT_DMA_DCR0(band), MT_MDP_DCR0_RX_HDR_TRANS_EN,
+		mt76_rmw_field(dev, MT_MDP_DCR0, MT_MDP_DCR0_RX_HDR_TRANS_EN,
 			       !dev->monitor_mask);
 		mt76_testmode_reset(phy->mt76, true);
 		mt76_wr(dev, MT_WF_RFCR(band), rxfilter);
Signature · 1 line
2.53.0
author maintainer
#850 lore raw

PATCH 814/1376 wifi: mt76: fix stranded frames in mt76_txq_schedule_pending

In reply to Greg Kroah-Hartman

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

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

From: Felix Fietkau <[email protected]>

[ Upstream commit 422dd2db28ae27c35a586acd9ad482f30000c090 ]

A wcid is added to phy->tx_list whenever either tx_pending or tx_offchannel becomes non-empty, but the requeue check after a partial schedule required BOTH queues to be non-empty. When mt76_txq_schedule_pending_wcid() returns -1 (queue stopped or MT76_RESET) it leaves frames in tx_pending while tx_offchannel is empty, so the wcid is dropped from every scheduling list and its frames stall until the next mt76_tx() for that wcid or wcid cleanup. This strands EAPOL/mgmt/nullfunc frames under momentary queue-full or across scan/channel-switch, causing association and 4-way-handshake timeouts.

Requeue when either queue still holds frames, matching the enqueue condition.

Fixes 0b3be9d1d34e ("wifi: mt76: add separate tx scheduling queue for off-channel tx")
---
 drivers/net/wireless/mediatek/mt76/tx.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/net/wireless/mediatek/mt76/tx.c +2 -2
index 1184357d933d2..1a5acb2bcfb3e 100644
@@ -664,8 +664,8 @@ static void mt76_txq_schedule_pending(struct mt76_phy *phy)
 			ret = mt76_txq_schedule_pending_wcid(phy, wcid, &wcid->tx_pending);
 		spin_lock(&phy->tx_lock);
 
-		if (!skb_queue_empty(&wcid->tx_pending) &&
-		    !skb_queue_empty(&wcid->tx_offchannel) &&
+		if ((!skb_queue_empty(&wcid->tx_pending) ||
+		     !skb_queue_empty(&wcid->tx_offchannel)) &&
 		    list_empty(&wcid->tx_list))
 			list_add_tail(&wcid->tx_list, &phy->tx_list);
 	}
Signature · 1 line
2.53.0
author maintainer
#851 lore raw

PATCH 815/1376 wifi: mt76: mt7915: poll the correct SLP CTRL register for the second adie

In reply to Greg Kroah-Hartman

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

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

From: Felix Fietkau <[email protected]>

[ Upstream commit dd59a6126a8f1bd52bf6bd057bf0c8307f76a74b ]

The clock enable path for the second adie sets MT_ADIE_SLP_CTRL_CK0(1) but polled the busy bit of MT_ADIE_SLP_CTRL_CK0(0), so dual-adie bring-up could proceed before the adie1 clock was stable.

Fixes 99ad32a4ca3a ("mt76: mt7915: add support for MT7986")
---
 drivers/net/wireless/mediatek/mt76/mt7915/soc.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/wireless/mediatek/mt76/mt7915/soc.c +1 -1
index 90a6f61d10892..c0372f693cd49 100644
@@ -913,7 +913,7 @@ static void mt7986_wmac_clock_enable(struct mt7915_dev *dev, u32 adie_type)
 
 		read_poll_timeout(mt76_rr, cur, !(cur & MT_SLP_CTRL_BSY_MASK),
 				  USEC_PER_MSEC, 50 * USEC_PER_MSEC, false,
-				  dev, MT_ADIE_SLP_CTRL_CK0(0));
+				  dev, MT_ADIE_SLP_CTRL_CK0(1));
 	}
 	mt76_wmac_spi_unlock(dev);
 
Signature · 1 line
2.53.0
author maintainer
#852 lore raw

PATCH 816/1376 wifi: mt76: check txfree done event on the WED hw path

In reply to Greg Kroah-Hartman

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

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

From: Rex Lu <[email protected]>

[ Upstream commit 3310e71a74b176d3613dfb42b6bc630d99e90cbb ]

Check the txfree done event DW1 bit 15 when WED is enabled, to avoid the driver reading a txfree done event before WED has finished reading it. No need to check this flag on WED v2, otherwise SER will occur.

The bit position was previously defined as MT_DMA_CTL_BURST, which is unused; rename it to match its function on the txfree ring.

Fixes 83eafc9251d6 ("wifi: mt76: mt7996: add wed tx support")
---
 drivers/net/wireless/mediatek/mt76/dma.c | 9 +++++++++
 drivers/net/wireless/mediatek/mt76/dma.h | 2 +-
 2 files changed, 10 insertions(+), 1 deletion(-)
drivers/net/wireless/mediatek/mt76/dma.c +9 -0
index 8940f8bb7bb50..7989edea8bc09 100644
@@ -490,6 +490,15 @@ mt76_dma_dequeue(struct mt76_dev *dev, struct mt76_queue *q, bool flush,
 			q->desc[idx].ctrl |= cpu_to_le32(MT_DMA_CTL_DMA_DONE);
 		else if (!(q->desc[idx].ctrl & cpu_to_le32(MT_DMA_CTL_DMA_DONE)))
 			return NULL;
+#ifdef CONFIG_NET_MEDIATEK_SOC_WED
+		/* on WED v3 the M_DONE bit signals that WED is done reading
+		 * the txfree descriptor; WED v2 does not set it
+		 */
+		else if (dev->mmio.wed.version > 2 &&
+			 mt76_queue_is_wed_tx_free(q) &&
+			 !(q->desc[idx].ctrl & cpu_to_le32(MT_DMA_CTL_M_DONE)))
+			return NULL;
+#endif
 	}
 
 	q->tail = (q->tail + 1) % q->ndesc;
drivers/net/wireless/mediatek/mt76/dma.h +1 -1
index e3ddc7a837579..2e20d855f3ecf 100644
@@ -11,7 +11,7 @@
 
 #define MT_DMA_CTL_SD_LEN1		GENMASK(13, 0)
 #define MT_DMA_CTL_LAST_SEC1		BIT(14)
-#define MT_DMA_CTL_BURST		BIT(15)
+#define MT_DMA_CTL_M_DONE		BIT(15)
 #define MT_DMA_CTL_SD_LEN0		GENMASK(29, 16)
 #define MT_DMA_CTL_LAST_SEC0		BIT(30)
 #define MT_DMA_CTL_DMA_DONE		BIT(31)
Signature · 1 line
2.53.0
author maintainer
#853 lore raw

PATCH 817/1376 wifi: mt76: mt7996: bound TLV walk in mt7996_mcu_get_chip_config

In reply to Greg Kroah-Hartman

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

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

From: Felix Fietkau <[email protected]>

[ Upstream commit 44af52467e72094351a362bf69effd52f1d9c186 ]

The response TLV loop advanced by tlv->len without a minimum, so a theoretical firmware response containing a zero-length TLV could spin forever, hanging the CPU during device probe.
The u32 payload was also read without bounds checking. Reject a short fixed field, stop on a TLV whose length underruns the header or overruns the skb.

Fixes 5d33053be609 ("wifi: mt76: mt7996: add variants support")
---
 drivers/net/wireless/mediatek/mt76/mt7996/mcu.c | 16 +++++++++++++---
 1 file changed, 13 insertions(+), 3 deletions(-)
drivers/net/wireless/mediatek/mt76/mt7996/mcu.c +13 -3
index a5693efee74b8..e3f5b2b2e3674 100644
@@ -3651,21 +3651,31 @@ int mt7996_mcu_get_chip_config(struct mt7996_dev *dev, u32 *cap)
 		return ret;
 
 	/* fixed field */
+	if (skb->len < 4) {
+		dev_kfree_skb(skb);
+		return -EINVAL;
+	}
 	skb_pull(skb, 4);
 
 	buf = skb->data;
-	while (buf - skb->data < skb->len) {
+	while (buf - skb->data + sizeof(struct tlv) <= skb->len) {
 		struct tlv *tlv = (struct tlv *)buf;
+		u16 tlv_len = le16_to_cpu(tlv->len);
+
+		if (tlv_len < sizeof(*tlv) ||
+		    tlv_len > skb->len - (buf - skb->data))
+			break;
 
 		switch (le16_to_cpu(tlv->tag)) {
 		case UNI_EVENT_CHIP_CONFIG_EFUSE_VERSION:
-			*cap = le32_to_cpu(*(__le32 *)(buf + sizeof(*tlv)));
+			if (tlv_len >= sizeof(*tlv) + sizeof(__le32))
+				*cap = le32_to_cpu(*(__le32 *)(buf + sizeof(*tlv)));
 			break;
 		default:
 			break;
 		}
 
-		buf += le16_to_cpu(tlv->len);
+		buf += tlv_len;
 	}
 
 	dev_kfree_skb(skb);
Signature · 1 line
2.53.0
author maintainer
#854 lore raw

PATCH 818/1376 wifi: mt76: mt7915: unwind state on add_interface failure

In reply to Greg Kroah-Hartman

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

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

From: Felix Fietkau <[email protected]>

[ Upstream commit 2fb6480c52f611338e1b0abe5e6219be1fc9ab75 ]

When mt76_wcid_alloc() fails, mt7915_add_interface() returned without clearing the vif_mask/omac_mask bits it had already set, without removing the firmware dev info added earlier, and without clearing a monitor_vif pointer to the vif mac80211 is about to free. mac80211 does not call remove_interface() for a failed add, so the indices and firmware dev entry leaked permanently and testmode could dereference the stale monitor_vif. Add a proper error unwind.

Fixes b619e01380ee ("mt76: fix MBSS index condition in DBDC mode")
---
 drivers/net/wireless/mediatek/mt76/mt7915/main.c | 12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)
drivers/net/wireless/mediatek/mt76/mt7915/main.c +11 -1
index 529926c287bce..b065a8938dd5d 100644
@@ -248,7 +248,7 @@ static int mt7915_add_interface(struct ieee80211_hw *hw,
 	idx = mt76_wcid_alloc(dev->mt76.wcid_mask, mt7915_wtbl_size(dev));
 	if (idx < 0) {
 		ret = -ENOSPC;
-		goto out;
+		goto err;
 	}
 
 	INIT_LIST_HEAD(&mvif->sta.rc_list);
@@ -279,7 +279,17 @@ static int mt7915_add_interface(struct ieee80211_hw *hw,
 	mt7915_mcu_add_sta(dev, vif, NULL, CONN_STATE_PORT_SECURE, true);
 	rcu_assign_pointer(dev->mt76.wcid[idx], &mvif->sta.wcid);
 
+	mutex_unlock(&dev->mt76.mutex);
+
+	return 0;
+
+err:
+	dev->mt76.vif_mask &= ~BIT_ULL(mvif->mt76.idx);
+	phy->omac_mask &= ~BIT_ULL(mvif->mt76.omac_idx);
+	mt7915_mcu_add_dev_info(phy, vif, false);
 out:
+	if (phy->monitor_vif == vif)
+		phy->monitor_vif = NULL;
 	mutex_unlock(&dev->mt76.mutex);
 
 	return ret;
Signature · 1 line
2.53.0
author maintainer
#855 lore raw

PATCH 819/1376 wifi: mt76: mt7996: reserve space for the CSA-abort countdown TLV

In reply to Greg Kroah-Hartman

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

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

From: Felix Fietkau <[email protected]>

[ Upstream commit 50c66bab321140c49aa2ed779a3ec9d2f085b458 ]

When a CSA countdown is active, mt7996_mcu_beacon_cntdwn() emits two bss_bcn_cntdwn_tlv entries (the CSA countdown and the CCA-abort BCC), but MT7996_BEACON_UPDATE_SIZE only reserved one. With MBSSID enabled and a near-maximum beacon template the extra 8 bytes could push the offload command past MT7996_MAX_BSS_OFFLOAD_SIZE and trigger skb_over_panic(). Reserve room for both countdown TLVs.

Fixes 98686cd21624 ("wifi: mt76: mt7996: add driver for MediaTek Wi-Fi 7 (802.11be) devices")
---
 drivers/net/wireless/mediatek/mt76/mt7996/mcu.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/wireless/mediatek/mt76/mt7996/mcu.h +1 -1
index ded2fe7210680..ce01f540972bf 100644
@@ -804,7 +804,7 @@ enum {
 #define MT7996_BEACON_UPDATE_SIZE	(sizeof(struct bss_req_hdr) +		\
 					 sizeof(struct bss_bcn_content_tlv) +	\
 					 4 + MT_TXD_SIZE +			\
-					 sizeof(struct bss_bcn_cntdwn_tlv) +	\
+					 sizeof(struct bss_bcn_cntdwn_tlv) * 2 +	\
 					 sizeof(struct bss_bcn_mbss_tlv))
 #define MT7996_MAX_BSS_OFFLOAD_SIZE	(MT7996_MAX_BEACON_SIZE +		\
 					 MT7996_BEACON_UPDATE_SIZE)
Signature · 1 line
2.53.0
author maintainer
#856 lore raw

PATCH 820/1376 wifi: mt76: mt7915: use little-endian for bss_info_ra wire fields

In reply to Greg Kroah-Hartman

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

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

From: Felix Fietkau <[email protected]>

[ Upstream commit 04280d0a56be4264720e7b205daaa332c715e5ec ]

train_up_high_thres, train_up_rule_rssi and low_traffic_thres were declared as host-native short in a firmware-facing TLV and assigned host-order constants, so on a big-endian host the firmware received byte-swapped rate-adaptation thresholds. Declare them __le16 and convert with cpu_to_le16().

Fixes e57b7901469f ("mt76: add mac80211 driver for MT7915 PCIe-based chipsets")
---
 drivers/net/wireless/mediatek/mt76/mt7915/mcu.c | 6 +++---
 drivers/net/wireless/mediatek/mt76/mt7915/mcu.h | 6 +++---
 2 files changed, 6 insertions(+), 6 deletions(-)
drivers/net/wireless/mediatek/mt76/mt7915/mcu.c +3 -3
index dcebe55c9010e..276cbc7693fef 100644
@@ -532,9 +532,9 @@ mt7915_mcu_bss_ra_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
 	ra->rx_streams = max_nss;
 	ra->algo = 4;
 	ra->train_up_rule = 2;
-	ra->train_up_high_thres = 110;
-	ra->train_up_rule_rssi = -70;
-	ra->low_traffic_thres = 2;
+	ra->train_up_high_thres = cpu_to_le16(110);
+	ra->train_up_rule_rssi = cpu_to_le16(-70);
+	ra->low_traffic_thres = cpu_to_le16(2);
 	ra->phy_cap = cpu_to_le32(0xfdf);
 	ra->interval = cpu_to_le32(500);
 	ra->fast_interval = cpu_to_le32(100);
drivers/net/wireless/mediatek/mt76/mt7915/mcu.h +3 -3
index b7e26c2409444..9e57a369a423f 100644
@@ -318,9 +318,9 @@ struct bss_info_ra {
 	u8 antenna_idx;
 	u8 train_up_rule;
 	u8 rsv[3];
-	unsigned short train_up_high_thres;
-	short train_up_rule_rssi;
-	unsigned short low_traffic_thres;
+	__le16 train_up_high_thres;
+	__le16 train_up_rule_rssi;
+	__le16 low_traffic_thres;
 	__le16 max_phyrate;
 	__le32 phy_cap;
 	__le32 interval;
Signature · 1 line
2.53.0
author maintainer
#857 lore raw

PATCH 821/1376 wifi: mt76: only consume the WO drop bit on WED v2 devices

In reply to Greg Kroah-Hartman

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

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

From: Felix Fietkau <[email protected]>

[ Upstream commit 1df54335590bb025c3bd706a9ba9c6e73a1d3000 ]

The RX path is handled by the WO MCU only on WED v2 hardware. On WED v3 the same buf1 bit does not carry drop information, so evaluating it there causes spurious RX drops.

Fixes e4d2b8bcac11 ("wifi: mt76: drop the incorrect scatter and gather frame")
---
 drivers/net/wireless/mediatek/mt76/dma.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)
drivers/net/wireless/mediatek/mt76/dma.c +6 -1
index 7989edea8bc09..e02741c5892e6 100644
@@ -459,8 +459,13 @@ mt76_dma_get_buf(struct mt76_dev *dev, struct mt76_queue *q, int idx,
 		t->ptr = NULL;
 
 		mt76_put_rxwi(dev, t);
-		if (drop)
+#ifdef CONFIG_NET_MEDIATEK_SOC_WED
+		/* the WO MCU owns the RX path only on WED v2, on newer
+		 * versions this buf1 bit carries no drop information
+		 */
+		if (drop && dev->mmio.wed.version == 2)
 			*drop |= !!(buf1 & MT_DMA_CTL_WO_DROP);
+#endif
 	} else {
 		dma_sync_single_for_cpu(dev->dma_dev, e->dma_addr[0],
 				SKB_WITH_OVERHEAD(q->buf_size),
Signature · 1 line
2.53.0
author maintainer
#858 lore raw

PATCH 822/1376 ACPI: processor: idle: Optimize ACPI idle driver registration

In reply to Greg Kroah-Hartman

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

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

From: Huisong Li <[email protected]>

[ Upstream commit 7a8c994cbb2db3c5335cee35fd486557f5aaf7e1 ]

Currently, the ACPI idle driver is registered from within a CPU hotplug callback. Although this didn't cause any functional issues, this is questionable and confusing. And it is better to register the cpuidle driver when all of the CPUs have been brought up.

So add a new function to initialize acpi_idle_driver based on the power management information of an available CPU and register cpuidle driver in acpi_processor_driver_init().

This commit has four changes under the commit 7a8c994cbb2d (ACPI:
processor: idle: Optimize ACPI idle driver registration):

1) move acpi_processor_register_idle_driver() ahead of the driver_register().
2) add acpi_processor_cstate_first_run_checks() before calling acpi_processor_get_power_info().
3) squash the commit 9d68320b2bca (ACPI: processor: idle: Fix function defined but not used warning) into this change.
4) use for_each_possible_cpu(cpu) to scan all possible cpus.

[ rjw: New comment edits, changelog tweak ]

Stable-dep-of: 06f32dd67e6b ("ACPI: processor: Unregister cpufreq notifier on init failure")

---
 drivers/acpi/processor_driver.c | 10 ++++-
 drivers/acpi/processor_idle.c   | 66 +++++++++++++++++++++------------
 include/acpi/processor.h        |  2 +
 3 files changed, 54 insertions(+), 24 deletions(-)
drivers/acpi/processor_driver.c +9 -1
index 3b281bc1e73c3..93970583157df 100644
@@ -259,9 +259,11 @@ static int __init acpi_processor_driver_init(void)
 		acpi_processor_ignore_ppc_init();
 	}
 
+	acpi_processor_register_idle_driver();
+
 	result = driver_register(&acpi_processor_driver);
 	if (result < 0)
-		return result;
+		goto unregister_idle_drv;
 
 	result = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
 				   "acpi/cpu-drv:online",
@@ -280,8 +282,13 @@ static int __init acpi_processor_driver_init(void)
 	 */
 	acpi_processor_init_invariance_cppc();
 	return 0;
+
 err:
 	driver_unregister(&acpi_processor_driver);
+
+unregister_idle_drv:
+	acpi_processor_unregister_idle_driver();
+
 	return result;
 }
 
@@ -299,6 +306,7 @@ static void __exit acpi_processor_driver_exit(void)
 	cpuhp_remove_state_nocalls(hp_online);
 	cpuhp_remove_state_nocalls(CPUHP_ACPI_CPUDRV_DEAD);
 	driver_unregister(&acpi_processor_driver);
+	acpi_processor_unregister_idle_driver();
 }
 
 module_init(acpi_processor_driver_init);
drivers/acpi/processor_idle.c +43 -23

96 lines not on this page · raw message

include/acpi/processor.h +2 -0
index a17e97e634a68..960ce42386cc7 100644
@@ -418,6 +418,8 @@ int acpi_processor_power_init(struct acpi_processor *pr);
 int acpi_processor_power_exit(struct acpi_processor *pr);
 int acpi_processor_power_state_has_changed(struct acpi_processor *pr);
 int acpi_processor_hotplug(struct acpi_processor *pr);
+void acpi_processor_register_idle_driver(void);
+void acpi_processor_unregister_idle_driver(void);
 #else
 static inline int acpi_processor_power_init(struct acpi_processor *pr)
 {
Signature · 1 line
2.53.0
author maintainer
#859 lore raw

PATCH 823/1376 ACPI: processor: Unregister cpufreq notifier on init failure

In reply to Greg Kroah-Hartman

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

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

From: Can Peng <[email protected]>

[ Upstream commit 06f32dd67e6b23a05bef0d8183c5335af91c0c3b ]

acpi_processor_driver_init() registers the cpufreq policy notifier before registering the ACPI processor driver and setting up CPU hotplug state.

If driver_register() or cpuhp_setup_state() fails, the error path only unregisters the ACPI processor driver and the idle driver. The cpufreq notifier remains registered even though initialization failed.

Mirror the module exit path on the init failure path and unregister the cpufreq notifier when it has been registered.

Fixes c0e0421a60bf ("ACPI: processor: Reorder acpi_processor_driver_init()")
---
 drivers/acpi/processor_driver.c | 6 ++++++
 1 file changed, 6 insertions(+)
drivers/acpi/processor_driver.c +6 -0
index 93970583157df..d6d9c57dd555f 100644
@@ -289,6 +289,12 @@ static int __init acpi_processor_driver_init(void)
 unregister_idle_drv:
 	acpi_processor_unregister_idle_driver();
 
+	if (acpi_processor_cpufreq_init) {
+		cpufreq_unregister_notifier(&acpi_processor_notifier_block,
+					    CPUFREQ_POLICY_NOTIFIER);
+		acpi_processor_cpufreq_init = false;
+	}
+
 	return result;
 }
 
Signature · 1 line
2.53.0
author maintainer
#860 lore raw

PATCH 824/1376 perf: arm_spe: Make wakeup range check overflow safe

In reply to Greg Kroah-Hartman

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

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

From: Leo Yan <[email protected]>

[ Upstream commit fcc5eaea2d234162dfb8258372dd897bc2a1b862 ]

The current code checks whether the wakeup point is in the current writable range by comparing it with handle->head + handle->size.

The perf AUX head is a monotonically increasing index, so that addition can overflow when head is close to ULONG_MAX. In that case, a wakeup point which is still inside the free space range can be missed.

Use unsigned subtraction to compare the distance from head to wakeup against the handle->size. This can dismiss the issue when addition overflow.

This is unlikely to happen in practice, but the change makes the watermark check logically correct.

Fixes d5d9696b0380 ("drivers/perf: Add support for ARMv8.2 Statistical Profiling Extension")
---
 drivers/perf/arm_spe_pmu.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/perf/arm_spe_pmu.c +1 -1
index 2fce871a1882d..5902ca3f057a1 100644
@@ -467,7 +467,7 @@ static u64 __arm_spe_pmu_next_off(struct perf_output_handle *handle)
 	 * the page boundary following it. Keep the tail boundary if
 	 * that's lower.
 	 */
-	if (handle->wakeup < (handle->head + handle->size) && head <= wakeup)
+	if ((handle->wakeup - handle->head) < handle->size && head <= wakeup)
 		limit = min(limit, round_up(wakeup, PAGE_SIZE));
 
 	if (limit > head)
Signature · 1 line
2.53.0
author maintainer
#861 lore raw

PATCH 825/1376 drm/msm/dpu: Drop sneaky dev_pm_opp_set_rate(0)

In reply to Greg Kroah-Hartman

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

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

From: Konrad Dybcio <[email protected]>

[ Upstream commit 811c38907eab0f66c22c5e5708e6f8eab14d76fa ]

dev_pm_opp_set_rate(0) removes the vote specified in required-opps but does not actually park the clock, making it run without the necessary power backing. Prevent that from happening when _dpu_core_perf_get_core_clk_rate() returns 0.

Fixes 25fdd5933e4c ("drm/msm: Add SDM845 DPU support")

Patchwork: https://patchwork.freedesktop.org/patch/742779/

---
 drivers/gpu/drm/msm/disp/dpu1/dpu_core_perf.c | 4 ++++
 1 file changed, 4 insertions(+)
drivers/gpu/drm/msm/disp/dpu1/dpu_core_perf.c +4 -0
index 260accc151d4b..d29df73c02c5f 100644
@@ -383,6 +383,10 @@ int dpu_core_perf_crtc_update(struct drm_crtc *crtc,
 
 		trace_dpu_core_perf_update_clk(kms->dev, !crtc->enabled, clk_rate);
 
+		/* If we're going offline, PM callbacks will disable the clocks instead */
+		if (!clk_rate)
+			return 0;
+
 		clk_rate = min(clk_rate, kms->perf.max_core_clk_rate);
 		ret = dev_pm_opp_set_rate(&kms->pdev->dev, clk_rate);
 		if (ret) {
Signature · 1 line
2.53.0
author maintainer
#862 lore raw

PATCH 826/1376 drm/msm/dsi: Drop dev_pm_opp_set_rate(0)

In reply to Greg Kroah-Hartman

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

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

From: Konrad Dybcio <[email protected]>

[ Upstream commit 06b7ba206561619bb34116f49e0ef26b867ce3aa ]

dev_pm_opp_set_rate(0) removes the vote specified in required-opps but does not actually park the clock, making it run without the necessary power backing. Drop the explicit call to it.

Every call site of ops->link_clk_disable() is followed by pm_runtime_put(), so the power vote will be rescinded if deemed safe.

Fixes 32d3e0feccfe ("drm/msm: dsi: Use OPP API to set clk/perf state")

Patchwork: https://patchwork.freedesktop.org/patch/742783/

---
 drivers/gpu/drm/msm/dsi/dsi_host.c | 2 --
 1 file changed, 2 deletions(-)
drivers/gpu/drm/msm/dsi/dsi_host.c +0 -2
index ab97de31af5ff..77173183509f1 100644
@@ -499,8 +499,6 @@ int dsi_link_clk_enable_v2(struct msm_dsi_host *msm_host)
 
 void dsi_link_clk_disable_6g(struct msm_dsi_host *msm_host)
 {
-	/* Drop the performance state vote */
-	dev_pm_opp_set_rate(&msm_host->pdev->dev, 0);
 	clk_disable_unprepare(msm_host->esc_clk);
 	clk_disable_unprepare(msm_host->pixel_clk);
 	clk_disable_unprepare(msm_host->byte_intf_clk);
Signature · 1 line
2.53.0
author maintainer
#863 lore raw

PATCH 827/1376 wifi: ath11k: fix leak in ath11k_service_ready_ext_event()

In reply to Greg Kroah-Hartman

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

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

From: Jeff Johnson <[email protected]>

[ Upstream commit 0293be2212d319d59589082461abf2a9b626cd1c ]

Currently, during ath11k_service_ready_ext_event() processing, svc_rdy_ext.mac_phy_caps can be allocated during TLV parsing. This is a temporary allocation that is freed on the success path, but not on the error path. If parsing succeeds far enough to allocate mac_phy_caps and then fails on a later TLV, the allocation leaks. So free the allocation on the error path.

Compile tested only.

---
 drivers/net/wireless/ath/ath11k/wmi.c | 1 +
 1 file changed, 1 insertion(+)
drivers/net/wireless/ath/ath11k/wmi.c +1 -0
index 540faacee41d8..a184a7e3452c0 100644
@@ -5077,6 +5077,7 @@ static int ath11k_service_ready_ext_event(struct ath11k_base *ab,
 	return 0;
 
 err:
+	kfree(svc_rdy_ext.mac_phy_caps);
 	ath11k_wmi_free_dbring_caps(ab);
 	return ret;
 }
Signature · 1 line
2.53.0
author maintainer
#864 lore raw

PATCH 828/1376 regulator: core: use system_freezable_wq for init complete work

In reply to Greg Kroah-Hartman

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

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

From: Joy Zou <[email protected]>

[ Upstream commit 03eab318cedd6ae34ecd34533cd986edf5237164 ]

schedule_delayed_work() uses system_wq, which is non-freezable, allowing regulator_init_complete_work to run concurrently with system suspend. This work fires ~30s after boot to disable unused regulators via I2C. When it races with PM suspend, the I2C adapter may already be suspended, triggering a -ESHUTDOWN warning in __i2c_transfer():

WARNING: ... at __i2c_transfer+0x36c/0x3c8

  Call trace:
   __i2c_transfer
   i2c_transfer
   regmap_i2c_write
   _regmap_update_bits
   regulator_disable_regmap
   _regulator_do_disable
   regulator_late_cleanup
   regulator_init_complete_work_function
   process_one_work

Switch to system_freezable_wq so the work is frozen before any device is suspended, eliminating the race.

Fixes 55576cf18537 ("regulator: Defer init completion for a while after late_initcall")
---
 drivers/regulator/core.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)
drivers/regulator/core.c +4 -2
index 264c8ab434f07..6c571edfaab97 100644
@@ -27,6 +27,7 @@
 #include <linux/regulator/driver.h>
 #include <linux/regulator/machine.h>
 #include <linux/module.h>
+#include <linux/workqueue.h>
 
 #define CREATE_TRACE_POINTS
 #include <trace/events/regulator.h>
@@ -6467,8 +6468,9 @@ static int __init regulator_init_complete(void)
 	 * we'd only do this on systems that need it, and a kernel
 	 * command line option might be useful.
 	 */
-	schedule_delayed_work(&regulator_init_complete_work,
-			      msecs_to_jiffies(30000));
+	queue_delayed_work(system_freezable_wq,
+			   &regulator_init_complete_work,
+			   msecs_to_jiffies(30000));
 
 	return 0;
 }
Signature · 1 line
2.53.0
author maintainer
#865 lore raw

PATCH 829/1376 perf machine: Fix NULL parent dereference in fork event processing

In reply to Greg Kroah-Hartman

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

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

From: Arnaldo Carvalho de Melo <[email protected]>

[ Upstream commit 73ac546bd6ba8ed4dc8d7a90fcb9bb8236de1568 ]

machine__process_fork_event() calls machine__findnew_thread() for the parent thread, which can return NULL on allocation failure. The code then dereferences parent via thread__pid(parent) without a NULL check when validating whether the parent PID matches. The later NULL check at thread__fork() does not prevent this earlier dereference.

Add a NULL guard before accessing the parent thread.

Fixes 5cb73340d92a ("perf tools: Make fork event processing more resilient")
Cc Adrian Hunter <[email protected]>
Assisted-by Claude:claude-opus-4.6
---
 tools/perf/util/machine.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
tools/perf/util/machine.c +2 -1
index 20fd742984e3c..8341737172279 100644
@@ -1843,7 +1843,8 @@ int machine__process_fork_event(struct machine *machine, union perf_event *event
 	 * (fork) event that would have removed the thread was lost. Assume the
 	 * latter case and continue on as best we can.
 	 */
-	if (thread__pid(parent) != (pid_t)event->fork.ppid) {
+	if (parent != NULL &&
+	    thread__pid(parent) != (pid_t)event->fork.ppid) {
 		dump_printf("removing erroneous parent thread %d/%d\n",
 			    thread__pid(parent), thread__tid(parent));
 		machine__remove_thread(machine, parent);
Signature · 1 line
2.53.0
author maintainer
#866 lore raw

PATCH 830/1376 perf machine: Guard against NULL strlist in machines__findnew()

In reply to Greg Kroah-Hartman

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

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

From: Arnaldo Carvalho de Melo <[email protected]>

[ Upstream commit e27b96d0a34e1dc87affecf221a99fee8f6c5afc ]

The static 'seen' strlist caches guestmount paths that have already been reported as inaccessible, to avoid repeating the error message. If strlist__new() fails (OOM), 'seen' stays NULL and the next call dereferences it via strlist__has_entry() and strlist__add().

Guard both calls so that on allocation failure the error message is still printed (just not deduplicated) instead of crashing.

Fixes c80c3c269011 ("perf kvm: Limit repetitive guestmount message to once per directory")
Cc David Ahern <[email protected]>
Assisted-by Claude:claude-opus-4.6
---
 tools/perf/util/machine.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)
tools/perf/util/machine.c +3 -2
index 8341737172279..9bd5dd9ddc81c 100644
@@ -296,9 +296,10 @@ struct machine *machines__findnew(struct machines *machines, pid_t pid)
 			if (!seen)
 				seen = strlist__new(NULL, NULL);
 
-			if (!strlist__has_entry(seen, path)) {
+			if (!seen || !strlist__has_entry(seen, path)) {
 				pr_err("Can't access file %s\n", path);
-				strlist__add(seen, path);
+				if (seen)
+					strlist__add(seen, path);
 			}
 			machine = NULL;
 			goto out;
Signature · 1 line
2.53.0
author maintainer
#867 lore raw

PATCH 831/1376 perf machine: Use snprintf() for guestmount path construction

In reply to Greg Kroah-Hartman

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

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

From: Arnaldo Carvalho de Melo <[email protected]>

[ Upstream commit fe63d3bca288c5bb983304efd5fc3a5ff3183403 ]

machines__findnew() and machines__create_guest_kernel_maps() use sprintf() to build paths by prepending symbol_conf.guestmount. Both write into PATH_MAX stack buffers, but guestmount comes from user configuration and is not length-checked. A guestmount path at or near PATH_MAX causes a stack buffer overflow.

Switch to snprintf() with sizeof() to prevent overflow. The subsequent access()/fopen() calls will fail on a truncated path.

Fixes a1645ce12adb6c9c ("perf: 'perf kvm' tool for monitoring guest performance from host")
Cc Zhang, Yanmin <[email protected]>
Assisted-by Claude:claude-opus-4.6

Stable-dep-of: cc6abe0012bf ("perf machine: Check snprintf truncation in machines__findnew()")

---
 tools/perf/util/machine.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)
tools/perf/util/machine.c +4 -4
index 9bd5dd9ddc81c..03fd3dcd4c986 100644
@@ -289,7 +289,7 @@ struct machine *machines__findnew(struct machines *machines, pid_t pid)
 	if ((pid != HOST_KERNEL_ID) &&
 	    (pid != DEFAULT_GUEST_KERNEL_ID) &&
 	    (symbol_conf.guestmount)) {
-		sprintf(path, "%s/%d", symbol_conf.guestmount, pid);
+		snprintf(path, sizeof(path), "%s/%d", symbol_conf.guestmount, pid);
 		if (access(path, R_OK)) {
 			static struct strlist *seen;
 
@@ -1215,9 +1215,9 @@ int machines__create_guest_kernel_maps(struct machines *machines)
 					 namelist[i]->d_name);
 				continue;
 			}
-			sprintf(path, "%s/%s/proc/kallsyms",
-				symbol_conf.guestmount,
-				namelist[i]->d_name);
+			snprintf(path, sizeof(path), "%s/%s/proc/kallsyms",
+				 symbol_conf.guestmount,
+				 namelist[i]->d_name);
 			ret = access(path, R_OK);
 			if (ret) {
 				pr_debug("Can't access file %s\n", path);
Signature · 1 line
2.53.0
author maintainer
#868 lore raw

PATCH 832/1376 perf machine: Check snprintf truncation in machines__findnew()

In reply to Greg Kroah-Hartman

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

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

From: Arnaldo Carvalho de Melo <[email protected]>

[ Upstream commit cc6abe0012bf8c04af8275266f8ed7c55ba4a5fb ]

The guestmount path is built with snprintf() into a PATH_MAX buffer without checking the return value. If symbol_conf.guestmount is long enough to cause truncation, the truncated path could match a different directory, causing the wrong guest to be associated with the pid.

Check for truncation and bail out early.

Fixes a1645ce12adb ("perf: 'perf kvm' tool for monitoring guest performance from host")
Cc Zhang, Yanmin <[email protected]>
Assisted-by Claude:claude-opus-4.6
---
 tools/perf/util/machine.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)
tools/perf/util/machine.c +6 -1
index 03fd3dcd4c986..ea89e7499032a 100644
@@ -289,7 +289,12 @@ struct machine *machines__findnew(struct machines *machines, pid_t pid)
 	if ((pid != HOST_KERNEL_ID) &&
 	    (pid != DEFAULT_GUEST_KERNEL_ID) &&
 	    (symbol_conf.guestmount)) {
-		snprintf(path, sizeof(path), "%s/%d", symbol_conf.guestmount, pid);
+		if (snprintf(path, sizeof(path), "%s/%d",
+			     symbol_conf.guestmount, pid) >= (int)sizeof(path)) {
+			pr_err("Guest path too long for pid %d\n", pid);
+			machine = NULL;
+			goto out;
+		}
 		if (access(path, R_OK)) {
 			static struct strlist *seen;
 
Signature · 1 line
2.53.0
author maintainer
#869 lore raw

PATCH 833/1376 perf machine: Dont abort guest map creation on first inaccessible dir

In reply to Greg Kroah-Hartman

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

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

From: Arnaldo Carvalho de Melo <[email protected]>

[ Upstream commit b687e1a418fb819ef83c362d84c216a6a841e3b0 ]

machines__create_guest_kernel_maps() jumps to the failure label when one guest directory's kallsyms file fails access(), skipping all remaining valid guest directories. An inaccessible directory is not fatal — other guests may still be reachable.

Replace 'goto failure' with 'continue' so the loop processes all directories, and remove the now-unreferenced failure label.

Fixes a1645ce12adb ("perf: 'perf kvm' tool for monitoring guest performance from host")
Cc Zhang, Yanmin <[email protected]>
Assisted-by Claude:claude-opus-4.6
---
 tools/perf/util/machine.c | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)
tools/perf/util/machine.c +2 -4
index ea89e7499032a..073ab47bd5678 100644
@@ -1223,14 +1223,12 @@ int machines__create_guest_kernel_maps(struct machines *machines)
 			snprintf(path, sizeof(path), "%s/%s/proc/kallsyms",
 				 symbol_conf.guestmount,
 				 namelist[i]->d_name);
-			ret = access(path, R_OK);
-			if (ret) {
+			if (access(path, R_OK)) {
 				pr_debug("Can't access file %s\n", path);
-				goto failure;
+				continue;
 			}
 			machines__create_kernel_maps(machines, pid);
 		}
-failure:
 		free(namelist);
 	}
 
Signature · 1 line
2.53.0
author maintainer
#870 lore raw

PATCH 834/1376 perf machine: Reset errno before strtol in guest kernel map creation

In reply to Greg Kroah-Hartman

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

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

From: Arnaldo Carvalho de Melo <[email protected]>

[ Upstream commit 29ec46e43f6ca7d6a6651db724d4ffd820f46e8b ]

machines__create_guest_kernel_maps() checks errno == ERANGE after strtol() to detect overflow, but does not clear errno first. A stale ERANGE from an earlier library call (e.g. scandir internals) causes valid numeric directory names to be incorrectly skipped.

Set errno = 0 before strtol() so only the current conversion can trigger the ERANGE check.

Fixes a1645ce12adb ("perf: 'perf kvm' tool for monitoring guest performance from host")
Cc Zhang, Yanmin <[email protected]>
Assisted-by Claude:claude-opus-4.6
---
 tools/perf/util/machine.c | 1 +
 1 file changed, 1 insertion(+)
tools/perf/util/machine.c +1 -0
index 073ab47bd5678..1cef434b3c8dc 100644
@@ -1212,6 +1212,7 @@ int machines__create_guest_kernel_maps(struct machines *machines)
 				/* Filter out . and .. */
 				continue;
 			}
+			errno = 0;
 			pid = (pid_t)strtol(namelist[i]->d_name, &endp, 10);
 			if ((*endp != '\0') ||
 			    (endp == namelist[i]->d_name) ||
Signature · 1 line
2.53.0
author maintainer
#871 lore raw

PATCH 835/1376 perf machine: Free scandir entries in guest kernel map creation

In reply to Greg Kroah-Hartman

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

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

From: Arnaldo Carvalho de Melo <[email protected]>

[ Upstream commit f53bf58dcd11e1cb088d3b91a035fef77062094b ]

machines__create_guest_kernel_maps() calls scandir() which allocates both the namelist array and each individual dirent entry. The code frees the namelist array but not the individual entries, leaking memory proportional to the number of directories under guestmount.

Free each namelist[i] after it is no longer needed.

Fixes a1645ce12adb ("perf: 'perf kvm' tool for monitoring guest performance from host")
Cc Zhang, Yanmin <[email protected]>
Assisted-by Claude:claude-opus-4.6
---
 tools/perf/util/machine.c | 4 ++++
 1 file changed, 4 insertions(+)
tools/perf/util/machine.c +4 -0
index 1cef434b3c8dc..a958184d3bb38 100644
@@ -1210,6 +1210,7 @@ int machines__create_guest_kernel_maps(struct machines *machines)
 		for (i = 0; i < items; i++) {
 			if (!isdigit(namelist[i]->d_name[0])) {
 				/* Filter out . and .. */
+				free(namelist[i]);
 				continue;
 			}
 			errno = 0;
@@ -1219,6 +1220,7 @@ int machines__create_guest_kernel_maps(struct machines *machines)
 			    (errno == ERANGE)) {
 				pr_debug("invalid directory (%s). Skipping.\n",
 					 namelist[i]->d_name);
+				free(namelist[i]);
 				continue;
 			}
 			snprintf(path, sizeof(path), "%s/%s/proc/kallsyms",
@@ -1226,9 +1228,11 @@ int machines__create_guest_kernel_maps(struct machines *machines)
 				 namelist[i]->d_name);
 			if (access(path, R_OK)) {
 				pr_debug("Can't access file %s\n", path);
+				free(namelist[i]);
 				continue;
 			}
 			machines__create_kernel_maps(machines, pid);
+			free(namelist[i]);
 		}
 		free(namelist);
 	}
Signature · 1 line
2.53.0
author maintainer
#872 lore raw

PATCH 836/1376 perf machine: Check snprintf truncation for guest kallsyms path

In reply to Greg Kroah-Hartman

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

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

From: Arnaldo Carvalho de Melo <[email protected]>

[ Upstream commit d04ef71492fad7230d474efe33d05f4c0563d409 ]

machines__create_guest_kernel_maps() builds the guest kallsyms path with snprintf() without checking the return value. A truncated path could pass the access() check if a prefix directory happens to contain a file named "kallsyms", leading to the wrong file being used for symbol resolution.

Check for truncation and skip the directory.

Fixes a1645ce12adb ("perf: 'perf kvm' tool for monitoring guest performance from host")
Cc Zhang, Yanmin <[email protected]>
Assisted-by Claude:claude-opus-4.6
---
 tools/perf/util/machine.c | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)
tools/perf/util/machine.c +8 -3
index a958184d3bb38..77050eff8fcb5 100644
@@ -1223,9 +1223,14 @@ int machines__create_guest_kernel_maps(struct machines *machines)
 				free(namelist[i]);
 				continue;
 			}
-			snprintf(path, sizeof(path), "%s/%s/proc/kallsyms",
-				 symbol_conf.guestmount,
-				 namelist[i]->d_name);
+			if (snprintf(path, sizeof(path), "%s/%s/proc/kallsyms",
+				     symbol_conf.guestmount,
+				     namelist[i]->d_name) >= (int)sizeof(path)) {
+				pr_debug("Guest kallsyms path too long for %s. Skipping.\n",
+					 namelist[i]->d_name);
+				free(namelist[i]);
+				continue;
+			}
 			if (access(path, R_OK)) {
 				pr_debug("Can't access file %s\n", path);
 				free(namelist[i]);
Signature · 1 line
2.53.0
author maintainer
#873 lore raw

PATCH 837/1376 bpf, x86: Fix trampoline stack size for 128-bit arguments

In reply to Greg Kroah-Hartman

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

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

From: Yonghong Song <[email protected]>

[ Upstream commit 814cba835ef648e0c5eb79505c96c0493b29eea6 ]

btf_distill_func_proto() accepts a function argument up to 16 bytes, so a 128-bit scalar such as __int128 reaches the x86 trampoline with arg_size == 16. But the current implementation assumes an __int128 argument only needs one register, so the register save area is under-allocated and save_args() overwrites adjacent stack slots.

Compute the register count from arg_size for all arguments to fix it.

Fixes a9c5ad31fbdc ("bpf: x86: Support in-register struct arguments in trampoline programs")
---
 arch/x86/net/bpf_jit_comp.c | 7 ++-----
 1 file changed, 2 insertions(+), 5 deletions(-)
arch/x86/net/bpf_jit_comp.c +2 -5
index 5e1518218f1de..ca882b8d70734 100644
@@ -2948,11 +2948,8 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *rw_im
 	WARN_ON_ONCE((flags & BPF_TRAMP_F_INDIRECT) &&
 		     (flags & ~(BPF_TRAMP_F_INDIRECT | BPF_TRAMP_F_RET_FENTRY_RET)));
 
-	/* extra registers for struct arguments */
-	for (i = 0; i < m->nr_args; i++) {
-		if (m->arg_flags[i] & BTF_FMODEL_STRUCT_ARG)
-			nr_regs += (m->arg_size[i] + 7) / 8 - 1;
-	}
+	for (i = 0; i < m->nr_args; i++)
+		nr_regs += (m->arg_size[i] + 7) / 8 - 1;
 
 	/* x86-64 supports up to MAX_BPF_FUNC_ARGS arguments. 1-6
 	 * are passed through regs, the remains are through stack.
Signature · 1 line
2.53.0
author maintainer
#874 lore raw

PATCH 838/1376 wifi: mt76: mt7915: unlink TWT flow if the MCU rejects the agreement

In reply to Greg Kroah-Hartman

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

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

From: Felix Fietkau <[email protected]>

[ Upstream commit 16a04441eab0dcd4d7126a6f66b370adbf28f96d ]

The flow is added to dev->twt_list before sending the agreement to the firmware, but the error path leaves it linked while flowid_mask is never set. The flow slot can then be reused and memset while still on the list, corrupting twt_list, and station removal leaves a dangling entry behind that mt7915_mac_twt_sched_list_add() later walks.

Fixes 3782b69d03e7 ("mt76: mt7915: introduce mt7915_mac_add_twt_setup routine")
---
 drivers/net/wireless/mediatek/mt76/mt7915/mac.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/net/wireless/mediatek/mt76/mt7915/mac.c +3 -1
index b058a38a88cf2..bd8bd74f0336d 100644
@@ -2371,8 +2371,10 @@ void mt7915_mac_add_twt_setup(struct ieee80211_hw *hw,
 	}
 	flow->tsf = le64_to_cpu(twt_agrt->twt);
 
-	if (mt7915_mcu_twt_agrt_update(dev, msta->vif, flow, MCU_TWT_AGRT_ADD))
+	if (mt7915_mcu_twt_agrt_update(dev, msta->vif, flow, MCU_TWT_AGRT_ADD)) {
+		list_del(&flow->list);
 		goto unlock;
+	}
 
 	setup_cmd = TWT_SETUP_CMD_ACCEPT;
 	dev->twt.table_mask |= BIT(table_id);
Signature · 1 line
2.53.0
author maintainer
#875 lore raw

PATCH 839/1376 wifi: mt76: mt7915: fix double hif2 init on the non-WED path

In reply to Greg Kroah-Hartman

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

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

From: Felix Fietkau <[email protected]>

[ Upstream commit 3ae8ad277e2819a281b0e36b55633c8515c16ce7 ]

mt7915_pci_init_hif2() was called unconditionally and again inside the WED-inactive branch. The helper increments the global hif_idx, writes the PCIe RECOG_ID register and takes a get_device() reference via mt7915_pci_get_hif2(), while removal only drops one reference. On non-WED dual-hif hardware this double-incremented hif_idx, wrote RECOG_ID twice and leaked a device reference. Only the call inside the WED-inactive branch is correct; drop the unconditional one. hif2 is already initialised to NULL.

Fixes cacdd67812c6 ("mt76: mt7915: add mt7915_mmio_probe() as a common probing function")
---
 drivers/net/wireless/mediatek/mt76/mt7915/pci.c | 1 -
 1 file changed, 1 deletion(-)
drivers/net/wireless/mediatek/mt76/mt7915/pci.c +0 -1
index 07b0a5766eab7..5a0c9eeb2c4e2 100644
@@ -135,7 +135,6 @@ static int mt7915_pci_probe(struct pci_dev *pdev,
 
 	mdev = &dev->mt76;
 	mt7915_wfsys_reset(dev);
-	hif2 = mt7915_pci_init_hif2(pdev);
 
 	ret = mt7915_mmio_wed_init(dev, pdev, true, &irq);
 	if (ret < 0)
Signature · 1 line
2.53.0
author maintainer
#876 lore raw

PATCH 840/1376 wifi: mt76: mt7915: fix ext PHY use-after-free on register error path

In reply to Greg Kroah-Hartman

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

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

From: Felix Fietkau <[email protected]>

[ Upstream commit 15b960014f24dce5388d4a2e7274e6490cb3c421 ]

After mt7915_register_ext_phy() succeeded, a failure of the main PHY mt7915_init_debugfs() or mt7915_coredump_register() unwound through free_phy2, which called ieee80211_free_hw() on the ext PHY hw while it was still registered with mac80211, since mt76_unregister_device() only unregisters the main hw. Unregister the ext PHY (thermal + phy + hw) first and skip the redundant free.

Fixes 7b8e1ae886e4 ("mt76: mt7915: rework hardware/phy initialization")
---
 drivers/net/wireless/mediatek/mt76/mt7915/init.c | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)
drivers/net/wireless/mediatek/mt76/mt7915/init.c +7 -2
index b2220ff7d6758..96871012bed3b 100644
@@ -1253,14 +1253,19 @@ int mt7915_register_device(struct mt7915_dev *dev)
 
 	ret = mt7915_init_debugfs(&dev->phy);
 	if (ret)
-		goto unreg_thermal;
+		goto unreg_ext_phy;
 
 	ret = mt7915_coredump_register(dev);
 	if (ret)
-		goto unreg_thermal;
+		goto unreg_ext_phy;
 
 	return 0;
 
+unreg_ext_phy:
+	if (phy2) {
+		mt7915_unregister_ext_phy(dev);
+		phy2 = NULL;
+	}
 unreg_thermal:
 	mt7915_unregister_thermal(&dev->phy);
 unreg_dev:
Signature · 1 line
2.53.0
author maintainer
#877 lore raw

PATCH 841/1376 wifi: mt76: mt7915: release hif2 reference on probe IRQ failure

In reply to Greg Kroah-Hartman

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

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

From: Felix Fietkau <[email protected]>

[ Upstream commit 8370aebd26a9dfa2e0de665e3ab504c0e97ee730 ]

The hif2 reference obtained by mt7915_pci_init_hif2() is only released on error paths that key off dev->hif2, which is not assigned until after the IRQ setup. If pci_alloc_irq_vectors() or the primary devm_request_irq() fails, the reference leaks. Drop it explicitly on those paths via mt7915_put_hif2().

Fixes f68d67623dec ("mt76: mt7915: add Wireless Ethernet Dispatch support")
---
 drivers/net/wireless/mediatek/mt76/mt7915/pci.c | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)
drivers/net/wireless/mediatek/mt76/mt7915/pci.c +6 -2
index 5a0c9eeb2c4e2..c24f1b12f064f 100644
@@ -144,16 +144,20 @@ static int mt7915_pci_probe(struct pci_dev *pdev,
 		hif2 = mt7915_pci_init_hif2(pdev);
 
 		ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES);
-		if (ret < 0)
+		if (ret < 0) {
+			mt7915_put_hif2(hif2);
 			goto free_device;
+		}
 
 		irq = pdev->irq;
 	}
 
 	ret = devm_request_irq(mdev->dev, irq, mt7915_irq_handler,
 			       IRQF_SHARED, KBUILD_MODNAME, dev);
-	if (ret)
+	if (ret) {
+		mt7915_put_hif2(hif2);
 		goto free_wed_or_irq_vector;
+	}
 
 	/* master switch of PCIe tnterrupt enable */
 	mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0xff);
Signature · 1 line
2.53.0
author maintainer
#878 lore raw

PATCH 842/1376 wifi: mt76: mt7996: fix reg addr remap when addr is 0

In reply to Greg Kroah-Hartman

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

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

From: StanleyYP Wang <[email protected]>

[ Upstream commit eb906eeff2d1e84b628dc210dada325269c71383 ]

When addr is less than the hardcoded threshold in __mt7996_reg_addr, it indicates that remapping is unnecessary.
Currently, the flow remaps address 0x0 to MT_HIF_REMAP_BASE_L2, which is incorrect.
To address this, modify __mt7996_reg_addr to return INVALID_REG_ADDR if the address is not below the hardcoded value or is not present in the mt7996_reg_map array.
Additionally, update the remap condition to check if addr is equal to INVALID_REG_ADDR.

Fixes 3687854d3e7e ("wifi: mt76: mt7996: add locking for accessing mapped registers")
---
 drivers/net/wireless/mediatek/mt76/mt7996/mmio.c | 12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)
drivers/net/wireless/mediatek/mt76/mt7996/mmio.c +7 -5
index bffee73b780cb..01d4b54a1acc6 100644
@@ -17,6 +17,8 @@
 static bool wed_enable;
 module_param(wed_enable, bool, 0644);
 
+#define INVALID_REG_ADDR	0xffffffff
+
 static const struct __base mt7996_reg_base[] = {
 	[WF_AGG_BASE]		= { { 0x820e2000, 0x820f2000, 0x830e2000 } },
 	[WF_ARB_BASE]		= { { 0x820e3000, 0x820f3000, 0x830e3000 } },
@@ -183,7 +185,7 @@ static u32 __mt7996_reg_addr(struct mt7996_dev *dev, u32 addr)
 		return dev->reg.map[i].mapped + ofs;
 	}
 
-	return 0;
+	return INVALID_REG_ADDR;
 }
 
 static u32 __mt7996_reg_remap_addr(struct mt7996_dev *dev, u32 addr)
@@ -212,7 +214,7 @@ void mt7996_memcpy_fromio(struct mt7996_dev *dev, void *buf, u32 offset,
 {
 	u32 addr = __mt7996_reg_addr(dev, offset);
 
-	if (addr) {
+	if (addr != INVALID_REG_ADDR) {
 		memcpy_fromio(buf, dev->mt76.mmio.regs + addr, len);
 		return;
 	}
@@ -228,7 +230,7 @@ static u32 mt7996_rr(struct mt76_dev *mdev, u32 offset)
 	struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76);
 	u32 addr = __mt7996_reg_addr(dev, offset), val;
 
-	if (addr)
+	if (addr != INVALID_REG_ADDR)
 		return dev->bus_ops->rr(mdev, addr);
 
 	spin_lock_bh(&dev->reg_lock);
@@ -243,7 +245,7 @@ static void mt7996_wr(struct mt76_dev *mdev, u32 offset, u32 val)
 	struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76);
 	u32 addr = __mt7996_reg_addr(dev, offset);
 
-	if (addr) {
+	if (addr != INVALID_REG_ADDR) {
 		dev->bus_ops->wr(mdev, addr, val);
 		return;
 	}
@@ -258,7 +260,7 @@ static u32 mt7996_rmw(struct mt76_dev *mdev, u32 offset, u32 mask, u32 val)
 	struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76);
 	u32 addr = __mt7996_reg_addr(dev, offset);
 
-	if (addr)
+	if (addr != INVALID_REG_ADDR)
 		return dev->bus_ops->rmw(mdev, addr, mask, val);
 
 	spin_lock_bh(&dev->reg_lock);
Signature · 1 line
2.53.0
author maintainer
#879 lore raw

PATCH 843/1376 wifi: mt76: mt7915: fix chainmask handling for non-dbdc phys on band 1

In reply to Greg Kroah-Hartman

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

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

From: Felix Fietkau <[email protected]>

[ Upstream commit ea891799eccc9e43ebba0dc157d4b197ad6c1a0e ]

On single-adie mt7986 the only phy is bound to band 1, but its chainmask is stored unshifted, because dev->chainshift is still zero while the eeprom is parsed for the main phy. mt7915_set_antenna() on the other hand shifts by chainshift * band_idx, so the representation of the chainmask changed as soon as the antenna configuration was touched. Until then, mt7915_mcu_set_chan_info() passed rx_path = 0 to the firmware, since shifting the unshifted mask down clears all bits.

Keep the unshifted form for that case and add helpers for the band local chainmask, so that only the band 1 phy of a dbdc device uses the shifted form.

Fixes 3eb50cc90534 ("wifi: mt76: mt7915: rely on band_idx of mt76_phy")
---
 .../net/wireless/mediatek/mt76/mt7915/eeprom.c |  2 +-
 .../net/wireless/mediatek/mt76/mt7915/main.c   |  6 +++---
 .../net/wireless/mediatek/mt76/mt7915/mcu.c    |  2 +-
 .../net/wireless/mediatek/mt76/mt7915/mt7915.h | 18 ++++++++++++++++++
 .../wireless/mediatek/mt76/mt7915/testmode.c   |  5 +----
 5 files changed, 24 insertions(+), 9 deletions(-)
drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c +1 -1
index b038568e0be91..93956d4020414 100644
@@ -257,7 +257,7 @@ void mt7915_eeprom_parse_hw_cap(struct mt7915_dev *dev,
 	nss = min_t(u8, min_t(u8, nss_max, nss), path);
 
 	mphy->chainmask = BIT(path) - 1;
-	if (band)
+	if (band && dev->dbdc_support)
 		mphy->chainmask <<= dev->chainshift;
 	mphy->antenna_mask = BIT(nss) - 1;
 	dev->chainmask |= mphy->chainmask;
drivers/net/wireless/mediatek/mt76/mt7915/main.c +3 -3
index b065a8938dd5d..0e3ca0eb9f082 100644
@@ -1072,7 +1072,7 @@ mt7915_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant)
 	struct mt7915_dev *dev = mt7915_hw_dev(hw);
 	struct mt7915_phy *phy = mt7915_hw_phy(hw);
 	int max_nss = hweight8(hw->wiphy->available_antennas_tx);
-	u8 chainshift = dev->chainshift;
+	u8 shift = mt7915_band_chainshift(phy);
 	u8 band = phy->mt76->band_idx;
 
 	if (!tx_ant || tx_ant != rx_ant || ffs(tx_ant) > max_nss)
@@ -1085,9 +1085,9 @@ mt7915_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant)
 	/* handle a variant of mt7916/mt7981 which has 3T3R but nss2 on 5 GHz band */
 	if ((is_mt7916(&dev->mt76) || is_mt7981(&dev->mt76)) &&
 	    band && hweight8(tx_ant) == max_nss)
-		phy->mt76->chainmask = (dev->chainmask >> chainshift) << chainshift;
+		phy->mt76->chainmask = (dev->chainmask >> shift) << shift;
 	else
-		phy->mt76->chainmask = tx_ant << (chainshift * band);
+		phy->mt76->chainmask = tx_ant << shift;
 
 	mt76_set_stream_caps(phy->mt76, true);
 	mt7915_set_stream_vht_txbf_caps(phy);
drivers/net/wireless/mediatek/mt76/mt7915/mcu.c +1 -1
index 276cbc7693fef..343866dfc112b 100644
@@ -2758,7 +2758,7 @@ int mt7915_mcu_set_chan_info(struct mt7915_phy *phy, int cmd)
 		.center_ch = ieee80211_frequency_to_channel(freq1),
 		.bw = mt76_connac_chan_bw(chandef),
 		.tx_path_num = hweight16(phy->mt76->chainmask),
-		.rx_path = phy->mt76->chainmask >> (dev->chainshift * band),
+		.rx_path = mt7915_band_chainmask(phy),
 		.band_idx = band,
 		.channel_band = ch_band[chandef->chan->band],
 	};
drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h +18 -0
index f49d6b2f81e42..623b3077ed6f6 100644
@@ -383,6 +383,24 @@ mt7915_ext_phy(struct mt7915_dev *dev)
 	return phy->priv;
 }
 
+/* without dbdc, the chainmask is stored unshifted, even if the phy is
+ * bound to band 1
+ */
+static inline u8 mt7915_band_chainshift(struct mt7915_phy *phy)
+{
+	struct mt7915_dev *dev = phy->dev;
+
+	if (!dev->dbdc_support)
+		return 0;
+
+	return phy->mt76->band_idx * dev->chainshift;
+}
+
+static inline u16 mt7915_band_chainmask(struct mt7915_phy *phy)
+{
+	return phy->mt76->chainmask >> mt7915_band_chainshift(phy);
+}
+
 static inline u32 mt7915_check_adie(struct mt7915_dev *dev, bool sku)
 {
 	u32 mask = sku ? MT_CONNINFRA_SKU_MASK : MT_ADIE_TYPE_MASK;
drivers/net/wireless/mediatek/mt76/mt7915/testmode.c +1 -4
index d534fff5c952b..2a9e5d9ff101c 100644
@@ -694,9 +694,7 @@ mt7915_tm_set_params(struct mt76_phy *mphy, struct nlattr **tb,
 {
 	struct mt76_testmode_data *td = &mphy->test;
 	struct mt7915_phy *phy = mphy->priv;
-	struct mt7915_dev *dev = phy->dev;
-	u32 chainmask = mphy->chainmask, changed = 0;
-	bool ext_phy = phy != &dev->phy;
+	u32 chainmask = mt7915_band_chainmask(phy), changed = 0;
 	int i;
 
 	BUILD_BUG_ON(NUM_TM_CHANGED >= 32);
@@ -705,7 +703,6 @@ mt7915_tm_set_params(struct mt76_phy *mphy, struct nlattr **tb,
 	    td->state == MT76_TM_STATE_OFF)
 		return 0;
 
-	chainmask = ext_phy ? chainmask >> dev->chainshift : chainmask;
 	if (td->tx_antenna_mask > chainmask)
 		return -EINVAL;
 
Signature · 1 line
2.53.0
author maintainer
#880 lore raw

PATCH 844/1376 wifi: mt76: mt7915: report RX chain signal for all RX paths

In reply to Greg Kroah-Hartman

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

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

From: Felix Fietkau <[email protected]>

[ Upstream commit b53c44fe65792608f58028c7b0953e610ad652ee ]

status->chains was set from the antenna mask, which is derived from the number of spatial streams, while the chain_signal array is filled from all RCPI fields. On boards where the number of RX paths exceeds the stream count, e.g. the 3T3R mt7916/mt7981 variant with 2 streams on the 5 GHz band, the RSSI of the extra chains was never reported.

Use the band local RX path chainmask instead.

Fixes e57b7901469f ("mt76: add mac80211 driver for MT7915 PCIe-based chipsets")
---
 drivers/net/wireless/mediatek/mt76/mt7915/mac.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/wireless/mediatek/mt76/mt7915/mac.c +1 -1
index bd8bd74f0336d..f165e61ed28f7 100644
@@ -444,7 +444,7 @@ mt7915_mac_fill_rx(struct mt7915_dev *dev, struct sk_buff *skb,
 		if (v0 & MT_PRXV_HT_AD_CODE)
 			status->enc_flags |= RX_ENC_FLAG_LDPC;
 
-		status->chains = mphy->antenna_mask;
+		status->chains = mt7915_band_chainmask(phy);
 		status->chain_signal[0] = to_rssi(MT_PRXV_RCPI0, v1);
 		status->chain_signal[1] = to_rssi(MT_PRXV_RCPI1, v1);
 		status->chain_signal[2] = to_rssi(MT_PRXV_RCPI2, v1);
Signature · 1 line
2.53.0
author maintainer
#881 lore raw

PATCH 845/1376 wifi: mt76: mt7925: advertise EHT 320MHz capabilities for 6GHz band

In reply to Greg Kroah-Hartman

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

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

From: Javier Tia <[email protected]>

[ Upstream commit 77833c57a33450c0409e4b90d7721d255ea23a9e ]

mt7925_init_eht_caps() only populates EHT MCS/NSS maps for BW <= 80 and BW = 160, but never sets BW = 320. This means iw phy shows no 320MHz MCS map entries even though the hardware supports 320MHz operation in the 6GHz band.

Add the missing 320MHz capability bits for 6GHz:
- PHY_CAP0: IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ
- PHY_CAP1: beamformee SS for 320MHz
- PHY_CAP2: sounding dimensions for 320MHz
- PHY_CAP6: MCS15 support for 320MHz width
- MCS/NSS: populate bw._320 maps for 6GHz band

Introduce is_320mhz_supported() to gate 320MHz on MT7927 only, since MT7925 does not support 320MHz operation.

Stable-dep-of: 404c4e564f6b ("wifi: mt76: mt7925: Fix EHT Beamformee SS subfields to meet 802.11be minimum")

---
 .../net/wireless/mediatek/mt76/mt76_connac.h  |  5 +++++
 .../net/wireless/mediatek/mt76/mt7925/main.c  | 22 ++++++++++++++++++-
 2 files changed, 26 insertions(+), 1 deletion(-)
drivers/net/wireless/mediatek/mt76/mt76_connac.h +5 -0
index 445d0f0ab7795..deb3c27ddc317 100644
@@ -177,6 +177,11 @@ static inline bool is_mt7925(struct mt76_dev *dev)
 	return mt76_chip(dev) == 0x7925;
 }
 
+static inline bool is_320mhz_supported(struct mt76_dev *dev)
+{
+	return mt76_chip(dev) == 0x7927;
+}
+
 static inline bool is_mt7920(struct mt76_dev *dev)
 {
 	return mt76_chip(dev) == 0x7920;
drivers/net/wireless/mediatek/mt76/mt7925/main.c +21 -1
index 626e68905ffa0..c219460549679 100644
@@ -178,6 +178,10 @@ mt7925_init_eht_caps(struct mt792x_phy *phy, enum nl80211_band band,
 		IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMER |
 		IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMEE;
 
+	if (band == NL80211_BAND_6GHZ && is_320mhz_supported(&phy->dev->mt76))
+		eht_cap_elem->phy_cap_info[0] |=
+			IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ;
+
 	eht_cap_elem->phy_cap_info[0] |=
 		u8_encode_bits(u8_get_bits(sts - 1, BIT(0)),
 			       IEEE80211_EHT_PHY_CAP0_BEAMFORMEE_SS_80MHZ_MASK);
@@ -188,10 +192,20 @@ mt7925_init_eht_caps(struct mt792x_phy *phy, enum nl80211_band band,
 		u8_encode_bits(sts - 1,
 			       IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_160MHZ_MASK);
 
+	if (band == NL80211_BAND_6GHZ && is_320mhz_supported(&phy->dev->mt76))
+		eht_cap_elem->phy_cap_info[1] |=
+			u8_encode_bits(sts - 1,
+				       IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_320MHZ_MASK);
+
 	eht_cap_elem->phy_cap_info[2] =
 		u8_encode_bits(sts - 1, IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_80MHZ_MASK) |
 		u8_encode_bits(sts - 1, IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_160MHZ_MASK);
 
+	if (band == NL80211_BAND_6GHZ && is_320mhz_supported(&phy->dev->mt76))
+		eht_cap_elem->phy_cap_info[2] |=
+			u8_encode_bits(sts - 1,
+				       IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_320MHZ_MASK);
+
 	eht_cap_elem->phy_cap_info[3] =
 		IEEE80211_EHT_PHY_CAP3_NG_16_SU_FEEDBACK |
 		IEEE80211_EHT_PHY_CAP3_NG_16_MU_FEEDBACK |
@@ -212,7 +226,8 @@ mt7925_init_eht_caps(struct mt792x_phy *phy, enum nl80211_band band,
 		u8_encode_bits(u8_get_bits(0x11, GENMASK(1, 0)),
 			       IEEE80211_EHT_PHY_CAP5_MAX_NUM_SUPP_EHT_LTF_MASK);
 
-	val = width == NL80211_CHAN_WIDTH_160 ? 0x7 :
+	val = width == NL80211_CHAN_WIDTH_320 ? 0xf :
+	      width == NL80211_CHAN_WIDTH_160 ? 0x7 :
 	      width == NL80211_CHAN_WIDTH_80 ? 0x3 : 0x1;
 	eht_cap_elem->phy_cap_info[6] =
 		u8_encode_bits(u8_get_bits(0x11, GENMASK(4, 2)),
@@ -234,6 +249,11 @@ mt7925_init_eht_caps(struct mt792x_phy *phy, enum nl80211_band band,
 	eht_nss->bw._160.rx_tx_mcs9_max_nss = val;
 	eht_nss->bw._160.rx_tx_mcs11_max_nss = val;
 	eht_nss->bw._160.rx_tx_mcs13_max_nss = val;
+	if (band == NL80211_BAND_6GHZ && is_320mhz_supported(&phy->dev->mt76)) {
+		eht_nss->bw._320.rx_tx_mcs9_max_nss = val;
+		eht_nss->bw._320.rx_tx_mcs11_max_nss = val;
+		eht_nss->bw._320.rx_tx_mcs13_max_nss = val;
+	}
 }
 
 int mt7925_init_mlo_caps(struct mt792x_phy *phy)
Signature · 1 line
2.53.0
author maintainer
#882 lore raw

PATCH 846/1376 wifi: mt76: mt7925: Fix EHT Beamformee SS subfields to meet 802.11be minimum

In reply to Greg Kroah-Hartman

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

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

From: shengwei.lu <[email protected]>

[ Upstream commit 404c4e564f6b1eeffd10bf2b2d3b86620f5794c3 ]

Per IEEE 802.11be, the Beamformee SS <= 80/160/320 MHz 3-bit subfields in the EHT PHY Capabilities are encoded as (Nss - 1) and are required to be >= 3 (i.e. at least 4 SS receive capability) whenever SU Beamformee is advertised.

MT7925 is a 2x2 STA (sts = 2), so directly filling (sts - 1) = 1 violates the spec minimum. Clamp the encoded value to 3 when sts <= 3, otherwise use (sts - 1). This is applied consistently to the BEAMFORMEE_SS <= 80 MHz (split across phy_cap_info[0]/[1]), <= 160 MHz and <= 320 MHz (6 GHz only) subfields.

Fixes c948b5da6bbe ("wifi: mt76: mt7925: add Mediatek Wi-Fi7 driver for mt7925 chips")
---
 drivers/net/wireless/mediatek/mt76/mt7925/main.c | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)
drivers/net/wireless/mediatek/mt76/mt7925/main.c +6 -4
index c219460549679..bcc5ce829169f 100644
@@ -182,19 +182,21 @@ mt7925_init_eht_caps(struct mt792x_phy *phy, enum nl80211_band band,
 		eht_cap_elem->phy_cap_info[0] |=
 			IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ;
 
+	val = (sts > 3) ? sts - 1 : 3;
+
 	eht_cap_elem->phy_cap_info[0] |=
-		u8_encode_bits(u8_get_bits(sts - 1, BIT(0)),
+		u8_encode_bits(u8_get_bits(val, BIT(0)),
 			       IEEE80211_EHT_PHY_CAP0_BEAMFORMEE_SS_80MHZ_MASK);
 
 	eht_cap_elem->phy_cap_info[1] =
-		u8_encode_bits(u8_get_bits(sts - 1, GENMASK(2, 1)),
+		u8_encode_bits(u8_get_bits(val, GENMASK(2, 1)),
 			       IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_80MHZ_MASK) |
-		u8_encode_bits(sts - 1,
+		u8_encode_bits(val,
 			       IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_160MHZ_MASK);
 
 	if (band == NL80211_BAND_6GHZ && is_320mhz_supported(&phy->dev->mt76))
 		eht_cap_elem->phy_cap_info[1] |=
-			u8_encode_bits(sts - 1,
+			u8_encode_bits(val,
 				       IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_320MHZ_MASK);
 
 	eht_cap_elem->phy_cap_info[2] =
Signature · 1 line
2.53.0
author maintainer
#883 lore raw

PATCH 847/1376 wifi: ath10k: snoc: use memcpy_fromio() for MSA ramdump

In reply to Greg Kroah-Hartman

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

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

From: Linghui Wu <[email protected]>

[ Upstream commit 4f25071afe9218aaae1c63fbf75e229aa6405319 ]

On WCN3990/SNOC the MSA region is mapped with devm_memremap(MEMREMAP_WT). On arm64 such a mapping is not Normal-cacheable, so unaligned accesses to it are not permitted. ath10k_msa_dump_memory() copies the region with a plain memcpy(), whose optimized __pi_memcpy_generic implementation issues wide/unaligned loads. This triggers an alignment fault (FSC=0x21) Oops in ath10k_snoc_fw_crashed_dump() while collecting the devcoredump:

  Unable to handle kernel paging request ... FSC=0x21: alignment fault
  pc : __pi_memcpy_generic
  lr : ath10k_snoc_fw_crashed_dump [ath10k_snoc]

The Oops both leaves the firmware RAM dump buffer zeroed (no dump is captured) and crashes the kernel, which in turn breaks modem SSR recovery.

Use memcpy_fromio(), which only performs accesses that are valid for such a device-memory mapping. The generic memcpy_fromio() implementation aligns the source before issuing word-sized reads and stores the destination with put_unaligned(), so it is also safe for the coherent DMA allocation used on the non-reserved-memory path. ath11k and ath12k use the same pattern when copying target memory into crash dumps, so call it unconditionally here too.
The MEMREMAP_WT pointer is a plain void *, so an explicit __iomem cast is needed; use __force to keep sparse happy.

Tested-on: WCN3990 hw1.0 SNOC WLAN.HL.3.3.7.c5-00107-QCAHLSWMTPL-1

---
 drivers/net/wireless/ath/ath10k/snoc.c | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)
drivers/net/wireless/ath/ath10k/snoc.c +7 -2
index 59f7ccb33fde3..ad215de068e1c 100644
@@ -5,6 +5,7 @@
 
 #include <linux/bits.h>
 #include <linux/clk.h>
+#include <linux/io.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/of.h>
@@ -1455,11 +1456,15 @@ static void ath10k_msa_dump_memory(struct ath10k *ar,
 	hdr->length = cpu_to_le32(ar->msa.mem_size);
 
 	if (current_region->len < ar->msa.mem_size) {
-		memcpy(buf, ar->msa.vaddr, current_region->len);
+		memcpy_fromio(buf,
+			      (const void __iomem __force *)ar->msa.vaddr,
+			      current_region->len);
 		ath10k_warn(ar, "msa dump length is less than msa size %x, %x\n",
 			    current_region->len, ar->msa.mem_size);
 	} else {
-		memcpy(buf, ar->msa.vaddr, ar->msa.mem_size);
+		memcpy_fromio(buf,
+			      (const void __iomem __force *)ar->msa.vaddr,
+			      ar->msa.mem_size);
 	}
 }
 
Signature · 1 line
2.53.0
author maintainer
#884 lore raw

PATCH 848/1376 iommu/arm-smmu-v3: Convert to use atomic poll timeout

In reply to Greg Kroah-Hartman

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

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

From: Pranjal Shrivastava <[email protected]>

[ Upstream commit eced8058c82a3a81ae480a6546e2da32100dddfa ]

The arm_smmu_write_reg_sync() helper is currently implemented using readl_relaxed_poll_timeout() (that relies on usleep_range() internally) which becomes a critical issue when used in the gerror irq handler.

If the SMMU hits a gerror and enters Service Failure Mode (GERROR_SFM_ERR), the gerror handler calls arm_smmu_device_disable() in hard-irq context. This becomes a problem as arm_smmu_device_disable() inevitably calls arm_smmu_write_reg_sync() which might attempt to sleep inside a hard-irq context.

Fix this by converting the arm_smmu_write_reg_sync to use the readl_relaxed_poll_timeout_atomic() polling helper.

(Discovered while running Sashiko locally on another patch series).

Fixes 48ec83bcbcf5 ("iommu/arm-smmu: Add initial driver support for ARM SMMUv3 devices")
---
 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +3 -2
index 0a8a6cf1f0330..28331d9118b63 100644
@@ -3774,8 +3774,9 @@ static int arm_smmu_write_reg_sync(struct arm_smmu_device *smmu, u32 val,
 	u32 reg;
 
 	writel_relaxed(val, smmu->base + reg_off);
-	return readl_relaxed_poll_timeout(smmu->base + ack_off, reg, reg == val,
-					  1, ARM_SMMU_POLL_TIMEOUT_US);
+	return readl_relaxed_poll_timeout_atomic(smmu->base + ack_off, reg,
+						reg == val, 1,
+						ARM_SMMU_POLL_TIMEOUT_US);
 }
 
 /* GBPA is "special" */
Signature · 1 line
2.53.0
author maintainer
#885 lore raw

PATCH 849/1376 perf/cxlpmu: Fix 64-bit write to 32-bit HDM filter register

In reply to Greg Kroah-Hartman

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

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

From: Davidlohr Bueso <[email protected]>

[ Upstream commit ea434e8fd3a539e9c53285b10d3c7e539e228591 ]

The HDM decoder filter configuration register is 32 bits wide, but the driver programs it with a 64-bit writeq(). The filter value never exceeds 32 bits, so the upper half of the write is always zero and lands in the adjacent Filter ID 1 (Channel/Rank/Bank) configuration register at offset+4.

Fixes 5d7107c72796 ("perf: CXL Performance Monitoring Unit driver")
---
 drivers/perf/cxl_pmu.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/perf/cxl_pmu.c +1 -1
index 24244096f5141..ce97062a66190 100644
@@ -632,7 +632,7 @@ static void cxl_pmu_event_start(struct perf_event *event, int flags)
 			cfg = cxl_pmu_config2_get_hdm_decoder(event);
 		else
 			cfg = GENMASK(31, 0); /* No filtering if 0xFFFF_FFFF */
-		writeq(cfg, base + CXL_PMU_FILTER_CFG_REG(hwc->idx, 0));
+		writel(cfg, base + CXL_PMU_FILTER_CFG_REG(hwc->idx, 0));
 	}
 
 	cfg = readq(base + CXL_PMU_COUNTER_CFG_REG(hwc->idx));
Signature · 1 line
2.53.0
author maintainer
#886 lore raw

PATCH 850/1376 wifi: mac80211: send TWT teardown to peer after setup TX failure

In reply to Greg Kroah-Hartman

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

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

From: Zhao Li <[email protected]>

[ Upstream commit a28fcce6ee74be8a4526e6cfa16dc7786d62a784 ]

When an AP's TWT Setup response is not acknowledged, ieee80211_s1g_tx_twt_setup_fail() asks the driver to tear down the local agreement and sends a TWT teardown action as the peer notification. It uses the response SA as the destination, but ieee80211_s1g_send_twt_setup() built that response with SA set to the AP's address. The teardown is therefore queued with DA, SA and BSSID all set to the AP address and never reaches the station.

The in-tree driver callbacks update local hardware state and emit no action frame. The station receives no notification that mac80211 asked the driver to remove the agreement and can keep following the TWT schedule, leaving the peers' power-save state desynchronized.

Address the teardown to the response DA, the station to which the failed response was sent. This also matches the station lookup the transmit status path already performs on the same frame.

Fixes f5a4c24e689f ("mac80211: introduce individual TWT support in AP mode")
Assisted-by Codex:gpt-5.6-sol
Assisted-by Kimi:K3
---
 net/mac80211/s1g.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
net/mac80211/s1g.c +1 -1
index 2a23e431cbb5a..657f281adf10a 100644
@@ -147,7 +147,7 @@ ieee80211_s1g_tx_twt_setup_fail(struct ieee80211_sub_if_data *sdata,
 
 	drv_twt_teardown_request(sdata->local, sdata, &sta->sta, flowid);
 
-	ieee80211_s1g_send_twt_teardown(sdata, mgmt->sa, sdata->vif.addr,
+	ieee80211_s1g_send_twt_teardown(sdata, mgmt->da, sdata->vif.addr,
 					flowid);
 }
 
Signature · 1 line
2.53.0
author maintainer
#887 lore raw

PATCH 851/1376 wifi: zd1211rw: reject secondary interfaces to prevent conflicts

In reply to Greg Kroah-Hartman

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

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

From: Slawomir Stepien <[email protected]>

[ Upstream commit 0e4532ec658606f76f62eb277e7a933919d36cbb ]

The zd1211rw driver is designed for single-function Wi-Fi dongles and hardcodes its USB endpoints. When a malformed USB device exposes multiple interfaces that match the driver's device ID, the driver blindly binds to all of them.

During probe(), the driver calls usb_reset_device(), which iterates over all interfaces and invokes the pre_reset() callback for each bound interface. Since multiple interfaces are bound to zd1211rw, pre_reset() is called sequentially for each instance, acquiring their respective &mac->chip.mutex. Because all instances initialize their mutexes with the same lock class, lockdep detects a task acquiring a lock of the same class it already holds and flags it as a possible recursive deadlock:

WARNING: possible recursive locking detected kworker/0:1/11 is trying to acquire lock:
ffff88810371dde0 (&chip->mutex){+.+.}-{4:4}, at:
zd_chip_disable_rxtx+0x20/0x50 drivers/net/wireless/zydas/zd1211rw/zd_chip.c:1465

but task is already holding lock:
ffff8881138ddde0 (&chip->mutex){+.+.}-{4:4}, at: pre_reset+0x28c/0x380 drivers/net/wireless/zydas/zd1211rw/zd_usb.c:1505

Fix this by explicitly rejecting secondary interfaces (bInterfaceNumber !=
0) during probe(). This ensures that only a single instance of the driver binds to the device, eliminating the recursive locking scenario.

---
 drivers/net/wireless/zydas/zd1211rw/zd_usb.c | 8 ++++++++
 1 file changed, 8 insertions(+)
drivers/net/wireless/zydas/zd1211rw/zd_usb.c +8 -0
index 9ae10f65f2af0..8b8abcfbbfc8f 100644
@@ -1352,6 +1352,14 @@ static int probe(struct usb_interface *intf, const struct usb_device_id *id)
 	struct zd_usb *usb;
 	struct ieee80211_hw *hw = NULL;
 
+	/*
+	 * ZD1211 devices are single-function. Reject secondary interfaces
+	 * to prevent multiple instances from conflicting on hardcoded endpoints
+	 * and triggering recursive locking warnings.
+	 */
+	if (intf->cur_altsetting->desc.bInterfaceNumber != 0)
+		return -ENODEV;
+
 	print_id(udev);
 
 	if (id->driver_info & DEVICE_INSTALLER)
Signature · 1 line
2.53.0
author maintainer
#888 lore raw

PATCH 852/1376 wifi: mac80211: skip unused probe response countdown offsets

In reply to Greg Kroah-Hartman

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

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

From: Zhao Li <[email protected]>

[ Upstream commit fd2bf5e718108c00732eb07fd94a5d8830f62a9f ]

mac80211 copies cfg80211's variable-length countdown offset list into a zero-initialized fixed-size array, leaving unused entries at zero. The beacon branch already skips those zero entries, but the AP probe-response branch writes through them unconditionally.

When a probe-response template has no countdown offset, the write through an unused zero entry overwrites resp->data[0], corrupting the first byte of the template. cfg80211 already bounds explicitly supplied non-zero offsets in nl80211_parse_counter_offsets(), so this is a zero-sentinel bug, not an out-of-bounds write.

Skip zero probe-response offsets, matching the beacon path.

Fixes af296bdb8da4 ("mac80211: move csa counters from sdata to beacon/presp")
Assisted-by Codex:gpt-5
Assisted-by Claude:opus-4.8
---
 net/mac80211/tx.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
net/mac80211/tx.c +2 -1
index 7e23916c9189b..8fc30a773c583 100644
@@ -5051,7 +5051,8 @@ static void ieee80211_set_beacon_cntdwn(struct ieee80211_sub_if_data *sdata,
 		if (sdata->vif.type == NL80211_IFTYPE_AP && resp) {
 			u16 *resp_offsets = resp->cntdwn_counter_offsets;
 
-			resp->data[resp_offsets[i]] = count;
+			if (resp_offsets[i])
+				resp->data[resp_offsets[i]] = count;
 		}
 	}
 }
Signature · 1 line
2.53.0
author maintainer
#889 lore raw

PATCH 853/1376 wifi: mac80211: disconnect on CSA to channel 0

In reply to Greg Kroah-Hartman

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

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

From: Johannes Berg <[email protected]>

[ Upstream commit cf57f0a674cc3e3cda1a789359cc1238b61b9d7d ]

The refactor for the CSA parsing erroneously equates channel zero and no information present, leading it to ignore a CSA on an AP that advertises a switch to that (invalid) channel. This leads to not disconnecting, which we should. For Intel devices, this can lead to a firmware crash.

Fix this by using an int type for the channel number as well as the opclass, and using a (negative) value that cannot be encoded in the element to indicate it's not present.

---
 net/mac80211/spectmgmt.c | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)
net/mac80211/spectmgmt.c +6 -5
index 073ff9e0f3970..1bdb241e1fd5e 100644
@@ -228,7 +228,7 @@ int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
 {
 	enum nl80211_band new_band = current_band;
 	int new_freq;
-	u8 new_chan_no = 0, new_op_class = 0;
+	int new_chan_no = -1, new_op_class = -1;
 	struct ieee80211_channel *new_chan;
 	struct cfg80211_chan_def new_chandef = {};
 	const struct ieee80211_sec_chan_offs_ie *sec_chan_offs;
@@ -257,7 +257,7 @@ int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
 		new_op_class = ext_chansw_elem->new_operating_class;
 
 		if (!ieee80211_operating_class_to_band(new_op_class, &new_band)) {
-			new_op_class = 0;
+			new_op_class = -1;
 			if (!unprot_action)
 				sdata_info(sdata,
 					   "cannot understand ECSA IE operating class, %d, ignoring\n",
@@ -269,14 +269,14 @@ int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
 		}
 	}
 
-	if (!new_op_class && elems->ch_switch_ie) {
+	if (new_op_class < 0 && elems->ch_switch_ie) {
 		new_chan_no = elems->ch_switch_ie->new_ch_num;
 		csa_ie->count = elems->ch_switch_ie->count;
 		csa_ie->mode = elems->ch_switch_ie->mode;
 	}
 
 	/* nothing here we understand */
-	if (!new_chan_no)
+	if (new_chan_no < 0)
 		return 1;
 
 	/* Mesh Channel Switch Parameters Element */
@@ -360,7 +360,8 @@ int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
 				get_unaligned_le16(bwi->info.optional);
 	} else if (!wide_bw_chansw_ie || !wbcs_elem_to_chandef(wide_bw_chansw_ie,
 							       &new_chandef)) {
-		if (!ieee80211_operating_class_to_chandef(new_op_class, new_chan,
+		if (new_op_class < 0 ||
+		    !ieee80211_operating_class_to_chandef(new_op_class, new_chan,
 							  &new_chandef))
 			new_chandef = csa_ie->chanreq.oper;
 	}
Signature · 1 line
2.53.0
author maintainer
#890 lore raw

PATCH 854/1376 firmware: google: Add bounds checks in coreboot_table_populate()

In reply to Greg Kroah-Hartman

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

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

From: Titouan Ameline de Cadeville <[email protected]>

[ Upstream commit 7b1a1af4556a4f95ef273e91435fe804cbfcd223 ]

coreboot_table_populate() iterates over firmware-provided table entries with no validation that the entries stay within the mapped memory region. A corrupt table with a large entry->size advances ptr_entry past the mapped region, causing an out-of-bounds read on the next iteration.

Add a check before dereferencing ptr_entry to ensure the entry header is readable, and a second check after reading entry->size to ensure the full entry stays within the mapped region.

Pass len from coreboot_table_probe() into coreboot_table_populate() to make the mapped region size available for validation.

Stable-dep-of: a58a57a1076f ("firmware: coreboot: Validate table bounds")

---
 drivers/firmware/google/coreboot_table.c | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)
drivers/firmware/google/coreboot_table.c +9 -2
index 208652a8087cd..0cf613944951e 100644
@@ -101,16 +101,20 @@ void coreboot_driver_unregister(struct coreboot_driver *driver)
 }
 EXPORT_SYMBOL(coreboot_driver_unregister);
 
-static int coreboot_table_populate(struct device *dev, void *ptr)
+static int coreboot_table_populate(struct device *dev, void *ptr, resource_size_t len)
 {
 	int i, ret;
 	void *ptr_entry;
 	struct coreboot_device *device;
 	struct coreboot_table_entry *entry;
 	struct coreboot_table_header *header = ptr;
+	void *ptr_end;
 
+	ptr_end = ptr + len;
 	ptr_entry = ptr + header->header_bytes;
 	for (i = 0; i < header->table_entries; i++) {
+		if (ptr_entry + sizeof(*entry) > ptr_end)
+			return -EINVAL;
 		entry = ptr_entry;
 
 		if (entry->size < sizeof(*entry)) {
@@ -118,6 +122,9 @@ static int coreboot_table_populate(struct device *dev, void *ptr)
 			return -EINVAL;
 		}
 
+		if (ptr_entry + entry->size > ptr_end)
+			return -EINVAL;
+
 		device = kzalloc(sizeof(device->dev) + entry->size, GFP_KERNEL);
 		if (!device)
 			return -ENOMEM;
@@ -183,7 +190,7 @@ static int coreboot_table_probe(struct platform_device *pdev)
 	if (!ptr)
 		return -ENOMEM;
 
-	ret = coreboot_table_populate(dev, ptr);
+	ret = coreboot_table_populate(dev, ptr, len);
 
 	memunmap(ptr);
 
Signature · 1 line
2.53.0
author maintainer
#891 lore raw

PATCH 855/1376 firmware: coreboot: Validate table bounds

In reply to Greg Kroah-Hartman

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

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

From: Laxman Acharya Padhya <[email protected]>

[ Upstream commit a58a57a1076f8c5dae0327e3710899478c3be901 ]

The existing coreboot_table_populate() bounds checks limit individual entries to the mapped length. However, coreboot_table_probe() replaces the platform resource length with header and table sizes supplied by firmware before mapping the full table.

A malformed table can overflow the 32-bit size addition or advertise an extent beyond the resource, causing the driver to map and parse memory outside the resource. A resource shorter than the fixed header is also mapped as though it contained a complete header.

Reject resources shorter than the fixed header. After validating the signature, require a complete header, calculate the advertised extent with overflow checking, and reject extents beyond the resource before remapping the table.

Fixes d384d6f43d1e ("firmware: google memconsole: Add coreboot support")
---
 drivers/firmware/google/coreboot_table.c | 17 +++++++++++++----
 1 file changed, 13 insertions(+), 4 deletions(-)
drivers/firmware/google/coreboot_table.c +13 -4
index 0cf613944951e..3a7b9b432b9a2 100644
@@ -159,6 +159,7 @@ static int coreboot_table_populate(struct device *dev, void *ptr, resource_size_
 static int coreboot_table_probe(struct platform_device *pdev)
 {
 	resource_size_t len;
+	resource_size_t table_span;
 	struct coreboot_table_header *header;
 	struct resource *res;
 	struct device *dev = &pdev->dev;
@@ -170,7 +171,7 @@ static int coreboot_table_probe(struct platform_device *pdev)
 		return -EINVAL;
 
 	len = resource_size(res);
-	if (!res->start || !len)
+	if (!res->start || len < sizeof(*header))
 		return -EINVAL;
 
 	/* Check just the header first to make sure things are sane */
@@ -178,19 +179,27 @@ static int coreboot_table_probe(struct platform_device *pdev)
 	if (!header)
 		return -ENOMEM;
 
-	len = header->header_bytes + header->table_bytes;
 	ret = strncmp(header->signature, "LBIO", sizeof(header->signature));
+
+	if (!ret &&
+	    (header->header_bytes < sizeof(*header) ||
+	     check_add_overflow((resource_size_t)header->header_bytes,
+				(resource_size_t)header->table_bytes,
+				&table_span) ||
+	     table_span > len))
+		ret = -EINVAL;
+
 	memunmap(header);
 	if (ret) {
 		dev_warn(dev, "coreboot table missing or corrupt!\n");
 		return -ENODEV;
 	}
 
-	ptr = memremap(res->start, len, MEMREMAP_WB);
+	ptr = memremap(res->start, table_span, MEMREMAP_WB);
 	if (!ptr)
 		return -ENOMEM;
 
-	ret = coreboot_table_populate(dev, ptr, len);
+	ret = coreboot_table_populate(dev, ptr, table_span);
 
 	memunmap(ptr);
 
Signature · 1 line
2.53.0
author maintainer
#892 lore raw

PATCH 856/1376 powerpc/smp: add NULL guard for cause_ipi in smp_muxed_ipi_message_pass

In reply to Greg Kroah-Hartman

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

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

From: Gou Hao <[email protected]>

[ Upstream commit 5aabc192702defb8950e7c81b05c3f4ca8ee43ec ]

smp_muxed_ipi_message_pass() calls smp_ops->cause_ipi() without checking whether it has been set.

On platforms using muxed IPI (e.g. powernv/pseries), smp_ops->cause_ipi is initialized to NULL in the static smp_ops and only assigned during the platform smp_probe() handler. If the IPI subsystem fails to initialize -- for example when xive_init_ipis() fails and xive_smp_probe() returns an error -- the probe handler returns early and cause_ipi is never set. Any subsequent IPI send (e.g. arch_smp_send_reschedule()) would dereference the NULL pointer.

Add a NULL check to avoid the crash in that situation.

Fixes 23d72bfd8f9f ("powerpc: Consolidate ipi message mux and demux")
---
 arch/powerpc/kernel/smp.c | 3 +++
 1 file changed, 3 insertions(+)
arch/powerpc/kernel/smp.c +3 -0
index 09b2ccf387118..6bf939d15f1f5 100644
@@ -288,6 +288,9 @@ void smp_muxed_ipi_set_message(int cpu, int msg)
 
 void smp_muxed_ipi_message_pass(int cpu, int msg)
 {
+	if (!smp_ops->cause_ipi)
+		return;
+
 	smp_muxed_ipi_set_message(cpu, msg);
 
 	/*
Signature · 1 line
2.53.0
author maintainer
#893 lore raw

PATCH 857/1376 powerpc/irq: Fix missing r2 clobber in PCREL inline assembly

In reply to Greg Kroah-Hartman

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

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

From: Saket Kumar Bhaskar <[email protected]>

[ Upstream commit 00be69070d91d2be978e752bb117a0a4db0e1281 ]

In CONFIG_PPC_KERNEL_PCREL mode, r2 is no longer reserved for the TOC pointer and is available as a caller-saved register [0].

Both call_do_irq() and call_do_softirq() use inline assembly to call functions with stack switching, but fail to list r2 in their clobber lists. This causes the compiler to assume r2 is preserved across these calls, leading to register corruption when the called functions (__do_irq and __do_softirq) clobber r2.

As a result of this kernel crash during interrupt handling is seen and the kernel fails to boot:

BUG: Unable to handle kernel data access on write at 0xc000000404697638
Faulting instruction address: 0xc0000000000181ec
Oops: Kernel access of bad area, sig: 11 [#1]
NIP [c0000000000181ec] __do_IRQ+0x6c/0xc0

With older GCC, the compiler would conservatively allocate callee-saved registers (like r31) for values spanning function calls, accidentally avoiding the bug:

<__do_IRQ>:
00 00 00 60 nop

a6 02 08 7c 	mflr    r0
f8 ff e1 fb 	std     r31,-8(r1)
f0 ff c1 fb 	std     r30,-16(r1)
2d 03 10 06 	pla     r31,53297316

...

3d e8 ff 4b 	bl      c0000000000165ac <__do_irq>
00 00 21 e8 	ld      r1,0(r1)
28 00 4d e9 	ld      r10,40(r13)
40 00 21 38 	addi    r1,r1,64
2a f9 aa 7f 	stdx    r29,r10,r31

With newer GCC 14, the compiler uses r2 for such values, exposing the missing clobber specification:

<__do_IRQ>:

00 00 00 60     nop
a6 02 08 7c     mflr    r0
f0 ff c1 fb     std     r30,-16(r1)
f8 ff e1 fb     std     r31,-8(r1)
29 02 10 06     pla     r2,36252592     # c0000000022aadc0 <__irq_regs>

...

85 dc ff 4b 	bl      c000000000015ee0 <__do_irq>
00 00 21 e8 	ld      r1,0(r1)
28 00 2d e9 	ld      r9,40(r13)
30 00 21 38 	addi    r1,r1,48
2a 11 c9 7f 	stdx    r30,r9,r2

Fix this by adding r2 to the clobber list for both call_do_irq() and call_do_softirq() when CONFIG_PPC_KERNEL_PCREL is enabled.

[0]: https://www.mail-archive.com/[email protected]/msg313226.html

---
 arch/powerpc/kernel/irq.c | 16 ++++++++++++----
 1 file changed, 12 insertions(+), 4 deletions(-)
arch/powerpc/kernel/irq.c +12 -4
index 2e1600a8bbbbf..7ce69e7d6b11a 100644
@@ -217,8 +217,12 @@ static __always_inline void call_do_softirq(const void *sp)
 		   [sp] "b" (sp), [offset] "i" (THREAD_SIZE - STACK_FRAME_MIN_SIZE),
 		   [callee] "i" (__do_softirq)
 		 : // Clobbers
-		   "lr", "xer", "ctr", "memory", "cr0", "cr1", "cr5", "cr6",
-		   "cr7", "r0", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10",
+		   "lr", "xer", "ctr", "memory", "cr0", "cr1", "cr5", "cr6", "cr7", "r0",
+		  /* r2 may be clobbered by the callee when using PCREL mode in the ELFv2 ABI. */
+#ifdef CONFIG_PPC_KERNEL_PCREL
+		   "r2",
+#endif
+		   "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10",
 		   "r11", "r12"
 	);
 }
@@ -275,8 +279,12 @@ static __always_inline void call_do_irq(struct pt_regs *regs, void *sp)
 		   [sp] "b" (sp), [offset] "i" (THREAD_SIZE - STACK_FRAME_MIN_SIZE),
 		   [callee] "i" (__do_irq)
 		 : // Clobbers
-		   "lr", "xer", "ctr", "memory", "cr0", "cr1", "cr5", "cr6",
-		   "cr7", "r0", "r4", "r5", "r6", "r7", "r8", "r9", "r10",
+		   "lr", "xer", "ctr", "memory", "cr0", "cr1", "cr5", "cr6", "cr7", "r0",
+		  /* r2 may be clobbered by the callee when using PCREL mode in the ELFv2 ABI. */
+#ifdef CONFIG_PPC_KERNEL_PCREL
+		   "r2",
+#endif
+		   "r4", "r5", "r6", "r7", "r8", "r9", "r10",
 		   "r11", "r12"
 	);
 }
Signature · 1 line
2.53.0
author maintainer
#894 lore raw

PATCH 858/1376 MIPS: ptrace: Fix syscall skipping via PTRACE_SYSCALL

In reply to Greg Kroah-Hartman

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

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

From: Thomas Bogendoerfer <[email protected]>

[ Upstream commit 5475c03fa25f31cfd5f8c7e552f8d10347bbaad9 ]

If tracer wanted to skip a syscall return value was always overwritten with -ENOSYS. Fix this by checking against original syscall number and only return -ENOSYS, if it is negative.

Fixes b6318a903d06 ("MIPS/ptrace: Pick up ptrace/seccomp changed syscalls")
---
 arch/mips/kernel/ptrace.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)
arch/mips/kernel/ptrace.c +5 -1
index 61503a36067e9..2016dd5e8fae0 100644
@@ -1319,8 +1319,12 @@ long arch_ptrace(struct task_struct *child, long request,
  */
 asmlinkage long syscall_trace_enter(struct pt_regs *regs)
 {
+	long syscall;
+
 	user_exit();
 
+	syscall = current_thread_info()->syscall;
+
 	if (test_thread_flag(TIF_SYSCALL_TRACE)) {
 		if (ptrace_report_syscall_entry(regs))
 			return -1;
@@ -1356,7 +1360,7 @@ asmlinkage long syscall_trace_enter(struct pt_regs *regs)
 	 * Negative syscall numbers are mistaken for rejected syscalls, but
 	 * won't have had the return value set appropriately, so we do so now.
 	 */
-	if (current_thread_info()->syscall < 0)
+	if (syscall < 0)
 		syscall_set_return_value(current, regs, -ENOSYS, 0);
 	return current_thread_info()->syscall;
 }
Signature · 1 line
2.53.0
author maintainer
#895 lore raw

PATCH 859/1376 serial: amba-pl011: unprepare console clock on unregister

In reply to Greg Kroah-Hartman

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

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

From: Karl Mehltretter <[email protected]>

[ Upstream commit 7f93da9d78d433c37836d85de475c5d884ad58ed ]

pl011_console_setup() calls clk_prepare() on the UART clock, but the console provides no matching teardown, so the clock is never unprepared when the console is unregistered -- via the sysfs "console" attribute or a driver unbind. Each re-registration prepares the clock again, leaking one prepare reference per cycle.

Even where preparing the clock has no hardware effect, the stale reference leaves the clock framework's prepare count unbalanced. For providers with prepare/unprepare operations or runtime-PM integration, it may also retain resources after the console is unregistered.

Add a console .exit() callback that clk_unprepare()s the clock, balancing the clk_prepare() in pl011_console_setup().

Fixes 4b4851c65d92 ("clk: amba-pl011: convert to clk_prepare()/clk_unprepare()")
Assisted-by Claude:claude-fable-5
---
 drivers/tty/serial/amba-pl011.c | 10 ++++++++++
 1 file changed, 10 insertions(+)
drivers/tty/serial/amba-pl011.c +10 -0
index 8934fbdb0cf98..3e1932a3bfbbd 100644
@@ -2434,6 +2434,15 @@ static int pl011_console_setup(struct console *co, char *options)
 	return uart_set_options(&uap->port, co, baud, parity, bits, flow);
 }
 
+static int pl011_console_exit(struct console *co)
+{
+	struct uart_amba_port *uap = amba_ports[co->index];
+
+	clk_unprepare(uap->clk);
+
+	return 0;
+}
+
 /**
  *	pl011_console_match - non-standard console matching
  *	@co:	  registering console
@@ -2500,6 +2509,7 @@ static struct console amba_console = {
 	.write		= pl011_console_write,
 	.device		= uart_console_device,
 	.setup		= pl011_console_setup,
+	.exit		= pl011_console_exit,
 	.match		= pl011_console_match,
 	.flags		= CON_PRINTBUFFER | CON_ANYTIME,
 	.index		= -1,
Signature · 1 line
2.53.0
author maintainer
#896 lore raw

PATCH 860/1376 tty: clear cdev pointer after cdev_add() failure

In reply to Greg Kroah-Hartman

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

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

From: Karl Mehltretter <[email protected]>

[ Upstream commit 6645856f0df3aeecd45519cb611415b4b89c2223 ]

tty_cdev_add() drops the cdev reference when cdev_add() fails, but leaves driver->cdevs[index] pointing to freed memory. tty_unregister_device() later passes that stale pointer to cdev_del(), causing a use-after-free.

Clear the slot after dropping the reference.

Fixes c1a752ba2d6b ("tty: don't leak cdev in tty_cdev_add()")
Assisted-by Claude:claude-fable-5
---
 drivers/tty/tty_io.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/tty/tty_io.c +3 -1
index dcb1769c3625c..5d372fd7423e3 100644
@@ -3181,8 +3181,10 @@ static int tty_cdev_add(struct tty_driver *driver, dev_t dev,
 	driver->cdevs[index]->ops = &tty_fops;
 	driver->cdevs[index]->owner = driver->owner;
 	err = cdev_add(driver->cdevs[index], dev, count);
-	if (err)
+	if (err) {
 		kobject_put(&driver->cdevs[index]->kobj);
+		driver->cdevs[index] = NULL;
+	}
 	return err;
 }
 
Signature · 1 line
2.53.0
author maintainer
#897 lore raw

PATCH 861/1376 HID: i2c-hid: Refactor _DSM helper and add i2c-hid-acpi-prp0001 driver

In reply to Greg Kroah-Hartman

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

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

From: 谢致邦 (XIE Zhibang) <[email protected]>

[ Upstream commit fd7c67d05fb695b1deb07f9e213dd7e80e3a8427 ]

Move the _DSM call that gets the HID descriptor address from i2c-hid-acpi.c into i2c-hid-acpi.h as a static inline so both the ACPI and the new PRP0001 driver can use it. While refactoring, move the blacklist check and the _DSM call to the top of probe() to avoid a pointless alloc when the device is blacklisted or does not implement the _DSM.

Some devices, for example the Lenovo KaiTian N60d and Inspur CP300L3, are declared with _HID "PRP0001" and _DSD compatible "hid-over-i2c" but lack "hid-descr-addr" from the _DSD and provide the HID descriptor address only through an ACPI _DSM. The OF driver fails to probe them because it requires hid-descr-addr. Add a new driver that handles these devices by calling the shared _DSM helper.

Fixes b33752c30023 ("HID: i2c-hid: Reorganize so ACPI and OF are separate modules")
---
 drivers/hid/i2c-hid/Makefile               |   2 +-
 drivers/hid/i2c-hid/i2c-hid-acpi-prp0001.c | 104 +++++++++++++++++++++
 drivers/hid/i2c-hid/i2c-hid-acpi.c         |  52 +++--------
 drivers/hid/i2c-hid/i2c-hid-acpi.h         |  33 +++++++
 4 files changed, 152 insertions(+), 39 deletions(-)
 create mode 100644 drivers/hid/i2c-hid/i2c-hid-acpi-prp0001.c
 create mode 100644 drivers/hid/i2c-hid/i2c-hid-acpi.h
drivers/hid/i2c-hid/Makefile +1 -1
index 55bd5e0f35af3..38d5d827f3ce4 100644
@@ -8,7 +8,7 @@ obj-$(CONFIG_I2C_HID_CORE)			+= i2c-hid.o
 i2c-hid-objs					=  i2c-hid-core.o
 i2c-hid-$(CONFIG_DMI)				+= i2c-hid-dmi-quirks.o
 
-obj-$(CONFIG_I2C_HID_ACPI)			+= i2c-hid-acpi.o
+obj-$(CONFIG_I2C_HID_ACPI)			+= i2c-hid-acpi.o i2c-hid-acpi-prp0001.o
 obj-$(CONFIG_I2C_HID_OF)			+= i2c-hid-of.o
 obj-$(CONFIG_I2C_HID_OF_ELAN)			+= i2c-hid-of-elan.o
 obj-$(CONFIG_I2C_HID_OF_GOODIX)			+= i2c-hid-of-goodix.o
drivers/hid/i2c-hid/i2c-hid-acpi-prp0001.c new file +104 -0

104 lines not on this page · raw message

drivers/hid/i2c-hid/i2c-hid-acpi.c +14 -38

86 lines not on this page · raw message

drivers/hid/i2c-hid/i2c-hid-acpi.h new file +33 -0
new file mode 100644
index 0000000000000..0bbed1853313d
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef _I2C_HID_ACPI_H
+#define _I2C_HID_ACPI_H
+
+#include <linux/acpi.h>
+#include <linux/uuid.h>
+
+static inline int i2c_hid_acpi_get_descriptor(struct acpi_device *adev)
+{
+	/* HID I²C Device: 3cdff6f7-4267-4555-ad05-b30a3d8938de */
+	static const guid_t i2c_hid_guid =
+		GUID_INIT(0x3CDFF6F7, 0x4267, 0x4555,
+			  0xAD, 0x05, 0xB3, 0x0A, 0x3D, 0x89, 0x38, 0xDE);
+
+	acpi_handle handle = acpi_device_handle(adev);
+	union acpi_object *obj;
+	u16 addr;
+
+	obj = acpi_evaluate_dsm_typed(handle, &i2c_hid_guid,
+				      1, 1, NULL, ACPI_TYPE_INTEGER);
+	if (!obj) {
+		acpi_handle_err(handle,
+				"Error _DSM call to get HID descriptor address failed\n");
+		return -ENODEV;
+	}
+
+	addr = obj->integer.value;
+	ACPI_FREE(obj);
+	return addr;
+}
+
+#endif
Signature · 1 line
2.53.0
author maintainer
#898 lore raw

PATCH 862/1376 HID: synchronize input before cleaning up a failed probe

In reply to Greg Kroah-Hartman

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

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

From: Yousef Alhouseen <[email protected]>

[ Upstream commit 207853d46f7ef2e28042344a1468da8754c3ddbf ]

hid_device_io_start() allows reports to run concurrently with probe. If the probe subsequently fails, __hid_device_probe() releases driver resources and clears hdev->driver without first excluding those report callbacks.

For example, a report may enter hidraw_report_event() while the failure path frees the associated hidraw object, leading to a use-after-free when the report takes the object's list lock.

Stop input before performing failed-probe cleanup. This reacquires driver_input_lock and waits for any report callback already in progress.

Fixes c849a6143bec ("HID: Separate struct hid_device's driver_lock into two locks.")
Reported-by [email protected]
---
 drivers/hid/hid-core.c | 2 ++
 1 file changed, 2 insertions(+)
drivers/hid/hid-core.c +2 -0
index 5c33e1a0aef3c..df2e43b27388d 100644
@@ -2780,6 +2780,8 @@ static int __hid_device_probe(struct hid_device *hdev, struct hid_driver *hdrv)
 	 */
 
 	if (ret) {
+		if (hdev->io_started)
+			hid_device_io_stop(hdev);
 		devres_release_group(&hdev->dev, hdev->devres_group_id);
 		hid_close_report(hdev);
 		hdev->driver = NULL;
Signature · 1 line
2.53.0
author maintainer
#899 lore raw

PATCH 863/1376 HID: i2c-hid: Fix "(null)" output when reading report descriptor fails

In reply to Greg Kroah-Hartman

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

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

From: Ai Chao <[email protected]>

[ Upstream commit 8da0f0951deec9f0728ed2d9c54ded1c344b7542 ]

When i2c-hid fails to read the HID report descriptor during device initialization, the error message prints as:

hid (null): reading report descriptor failed

The HID device name is set in hid_add_device() after calling hdev->ll_driver->parse(), so when i2c_hid_parse() fails and calls hid_err(), the device name has not been set yet, resulting in "(null)" output.

Use dev_err(&client->dev, ...) instead of hid_err(hid, ...) because the I2C client device is fully initialized with a proper name, providing meaningful error messages for debugging.

Before: hid (null): reading report descriptor failed
After:  i2c_hid i2c-TPD0001:00: reading report descriptor failed
Fixes 4a200c3b9a40 ("HID: i2c-hid: introduce HID over i2c specification implementation")
---
 drivers/hid/i2c-hid/i2c-hid-core.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/hid/i2c-hid/i2c-hid-core.c +1 -1
index 8ce0535fc42d6..0e1adfa9b2931 100644
@@ -784,7 +784,7 @@ static int i2c_hid_parse(struct hid_device *hid)
 					    ihid->hdesc.wReportDescRegister,
 					    rdesc, rsize);
 		if (ret) {
-			hid_err(hid, "reading report descriptor failed\n");
+			dev_err(&client->dev, "reading report descriptor failed\n");
 			goto out;
 		}
 	}
Signature · 1 line
2.53.0
author maintainer
#900 lore raw

PATCH 864/1376 HID: i2c-hid: goodix: Disable VDD on VDDIO enable failure

In reply to Greg Kroah-Hartman

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

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

From: Chao Huang <[email protected]>

[ Upstream commit 8e2c560faea0220664169f7be4b498915ea1469f ]

If enabling VDDIO fails after VDD has been enabled, the power-up path returns without disabling VDD. This leaves the regulator enabled and its enable count unbalanced.

Disable VDD before returning the VDDIO error.

Fixes eb16f59e8e58 ("HID: i2c-hid: goodix: Add mainboard-vddio-supply")
---
 drivers/hid/i2c-hid/i2c-hid-of-goodix.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/hid/i2c-hid/i2c-hid-of-goodix.c +3 -1
index f1597ad67e7c8..f4dbcd1d1d472 100644
@@ -51,8 +51,10 @@ static int goodix_i2c_hid_power_up(struct i2chid_ops *ops)
 		return ret;
 
 	ret = regulator_enable(ihid_goodix->vddio);
-	if (ret)
+	if (ret) {
+		regulator_disable(ihid_goodix->vdd);
 		return ret;
+	}
 
 	if (ihid_goodix->timings->post_power_delay_ms)
 		msleep(ihid_goodix->timings->post_power_delay_ms);
Signature · 1 line
2.53.0
author maintainer
#901 lore raw

PATCH 865/1376 HID: lg4ff: validate report length before fixed offsets

In reply to Greg Kroah-Hartman

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

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

From: Jiancheng Huang <[email protected]>

[ Upstream commit be00988cce4ed44db1e61231d0ab71a64bab44cd ]

lg4ff_raw_event() rewrites fixed report offsets when combined pedals are enabled. It currently assumes that each product report contains every source and destination byte used by the rewrite.

Return without rewriting a short report before each product-specific access. Apply the same bound to the computed offset path.

Fixes c832f86effbc ("HID: hid-logitech: Add combined pedal support Logitech wheels")
Assisted-by Codex:gpt-5.6-luna
---
 drivers/hid/hid-lg4ff.c | 8 ++++++++
 1 file changed, 8 insertions(+)
drivers/hid/hid-lg4ff.c +8 -0
index e3fcf1353fb3b..71f168eff29b1 100644
@@ -336,6 +336,8 @@ int lg4ff_raw_event(struct hid_device *hdev, struct hid_report *report,
 	if (entry->wdata.combine) {
 		switch (entry->wdata.product_id) {
 		case USB_DEVICE_ID_LOGITECH_WHEEL:
+			if (size < 7)
+				return 0;
 			rd[5] = rd[3];
 			rd[6] = 0x7F;
 			return 1;
@@ -343,10 +345,14 @@ int lg4ff_raw_event(struct hid_device *hdev, struct hid_report *report,
 		case USB_DEVICE_ID_LOGITECH_WINGMAN_FFG:
 		case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL:
 		case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2:
+			if (size < 6)
+				return 0;
 			rd[4] = rd[3];
 			rd[5] = 0x7F;
 			return 1;
 		case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:
+			if (size < 7)
+				return 0;
 			rd[5] = rd[4];
 			rd[6] = 0x7F;
 			return 1;
@@ -366,6 +372,8 @@ int lg4ff_raw_event(struct hid_device *hdev, struct hid_report *report,
 		}
 
 		/* Compute a combined axis when wheel does not supply it */
+		if (size <= offset + 1)
+			return 0;
 		rd[offset] = (0xFF + rd[offset] - rd[offset+1]) >> 1;
 		rd[offset+1] = 0x7F;
 		return 1;
Signature · 1 line
2.53.0
author maintainer
#902 lore raw

PATCH 866/1376 perf thread-stack: Fix heap buffer overflow on branch stack wrap copy

In reply to Greg Kroah-Hartman

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

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

From: Arnaldo Carvalho de Melo <[email protected]>

[ Upstream commit ab9c84d1cd59e6b3b73de34982a35a76e3a9b032 ]

thread_stack__br_sample() copies the wrap-around portion of the branch stack ring buffer with:

  nr = min(ts->br_stack_pos, sz);
  memcpy(be, &src->entries[0], bsz * ts->br_stack_pos);

'nr' is correctly bounded to min(br_stack_pos, sz) but the memcpy uses the unbounded ts->br_stack_pos directly. When br_stack_pos exceeds the remaining destination space 'sz', this writes past the destination buffer.

Use 'nr' (the bounded value) in the memcpy size, matching the pattern of the first memcpy in the same function.

Fixes 86d67180b920 ("perf thread-stack: Add branch stack support")
Assisted-by Claude:claude-opus-4.6
---
 tools/perf/util/thread-stack.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
tools/perf/util/thread-stack.c +1 -1
index c6a0a27b12c2a..47d5922efdee3 100644
@@ -642,7 +642,7 @@ void thread_stack__br_sample(struct thread *thread, int cpu,
 		sz -= nr;
 		be = &dst->entries[nr];
 		nr = min(ts->br_stack_pos, sz);
-		memcpy(be, &src->entries[0], bsz * ts->br_stack_pos);
+		memcpy(be, &src->entries[0], bsz * nr);
 	}
 }
 
Signature · 1 line
2.53.0
author maintainer
#903 lore raw

PATCH 867/1376 perf auxtrace: Fix queue grow overflow and old array leak

In reply to Greg Kroah-Hartman

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

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

From: Arnaldo Carvalho de Melo <[email protected]>

[ Upstream commit 96fcc9ea5f18c083a1fa73da23afef7e953f7dca ]

auxtrace_queues__grow() has two bugs:

1. When idx is UINT_MAX, the caller passes new_nr_queues = idx + 1 = 0. The function skips growing (since any nr_queues >= 0), returns success, and the caller accesses queue_array[UINT_MAX] — an OOB heap write. Fix by rejecting new_nr_queues == 0 up front.

2. The function allocates a new queue_array via calloc and copies elements from the old array, but never frees the old array. Fix by saving the old pointer and freeing it after the copy.

Fixes e502789302a6ece9 ("perf auxtrace: Add helpers for queuing AUX area tracing data")
Assisted-by Claude:claude-opus-4.6
---
 tools/perf/util/auxtrace.c | 15 ++++++++++-----
 1 file changed, 10 insertions(+), 5 deletions(-)
tools/perf/util/auxtrace.c +10 -5
index ca8682966fae4..0927cfb42b7c5 100644
@@ -237,8 +237,12 @@ static int auxtrace_queues__grow(struct auxtrace_queues *queues,
 {
 	unsigned int nr_queues = queues->nr_queues;
 	struct auxtrace_queue *queue_array;
+	struct auxtrace_queue *old_array = queues->queue_array;
 	unsigned int i;
 
+	if (!new_nr_queues)
+		return -EINVAL;
+
 	if (!nr_queues)
 		nr_queues = AUXTRACE_INIT_NR_QUEUES;
 
@@ -253,16 +257,17 @@ static int auxtrace_queues__grow(struct auxtrace_queues *queues,
 		return -ENOMEM;
 
 	for (i = 0; i < queues->nr_queues; i++) {
-		list_splice_tail(&queues->queue_array[i].head,
+		list_splice_tail(&old_array[i].head,
 				 &queue_array[i].head);
-		queue_array[i].tid = queues->queue_array[i].tid;
-		queue_array[i].cpu = queues->queue_array[i].cpu;
-		queue_array[i].set = queues->queue_array[i].set;
-		queue_array[i].priv = queues->queue_array[i].priv;
+		queue_array[i].tid = old_array[i].tid;
+		queue_array[i].cpu = old_array[i].cpu;
+		queue_array[i].set = old_array[i].set;
+		queue_array[i].priv = old_array[i].priv;
 	}
 
 	queues->nr_queues = nr_queues;
 	queues->queue_array = queue_array;
+	free(old_array);
 
 	return 0;
 }
Signature · 1 line
2.53.0
author maintainer
#904 lore raw

PATCH 868/1376 perf intel-pt: Fix off-by-one in auxtrace_info minimum size check

In reply to Greg Kroah-Hartman

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

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

From: Arnaldo Carvalho de Melo <[email protected]>

[ Upstream commit c4362d5e1a5ed4ce2098798f655a636c4340fa20 ]

min_sz is set to sizeof(u64) * INTEL_PT_PER_CPU_MMAPS, but the code accesses auxtrace_info->priv[INTEL_PT_PER_CPU_MMAPS], which requires at least INTEL_PT_PER_CPU_MMAPS + 1 elements. A file with exactly min_sz bytes of priv data passes the size check but the access reads one u64 past the validated region.

Use (INTEL_PT_PER_CPU_MMAPS + 1) to ensure the highest accessed index is within bounds.

Fixes 90e457f7be087005 ("perf tools: Add Intel PT support")
Assisted-by Claude:claude-opus-4.6
---
 tools/perf/util/intel-pt.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
tools/perf/util/intel-pt.c +1 -1
index 61f10578e1212..fa4eb009a9636 100644
@@ -4359,7 +4359,7 @@ int intel_pt_process_auxtrace_info(union perf_event *event,
 				   struct perf_session *session)
 {
 	struct perf_record_auxtrace_info *auxtrace_info = &event->auxtrace_info;
-	size_t min_sz = sizeof(u64) * INTEL_PT_PER_CPU_MMAPS;
+	size_t min_sz = sizeof(u64) * (INTEL_PT_PER_CPU_MMAPS + 1);
 	struct intel_pt *pt;
 	void *info_end;
 	__u64 *info;
Signature · 1 line
2.53.0
author maintainer
#905 lore raw

PATCH 869/1376 perf intel-bts: Fix off-by-one in auxtrace_info minimum size check

In reply to Greg Kroah-Hartman

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

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

From: Arnaldo Carvalho de Melo <[email protected]>

[ Upstream commit b9fb8225951ce27e62a2235a71f3ab01137aaec3 ]

Same pattern as the Intel PT fix: min_sz is set to sizeof(u64) * INTEL_BTS_SNAPSHOT_MODE, but the code accesses auxtrace_info->priv[INTEL_BTS_SNAPSHOT_MODE], which requires at least INTEL_BTS_SNAPSHOT_MODE + 1 elements.

Use (INTEL_BTS_SNAPSHOT_MODE + 1) to ensure the highest accessed index is within bounds.

Fixes d0170af7004dce9c ("perf tools: Add Intel BTS support")
Assisted-by Claude:claude-opus-4.6
---
 tools/perf/util/intel-bts.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
tools/perf/util/intel-bts.c +1 -1
index 27d9b5c9fec8b..056dde772eabf 100644
@@ -829,7 +829,7 @@ int intel_bts_process_auxtrace_info(union perf_event *event,
 				    struct perf_session *session)
 {
 	struct perf_record_auxtrace_info *auxtrace_info = &event->auxtrace_info;
-	size_t min_sz = sizeof(u64) * INTEL_BTS_SNAPSHOT_MODE;
+	size_t min_sz = sizeof(u64) * (INTEL_BTS_SNAPSHOT_MODE + 1);
 	struct intel_bts *bts;
 	int err;
 
Signature · 1 line
2.53.0
author maintainer
#906 lore raw

PATCH 870/1376 iio: light: tsl2772: fix ALS calibscale readback

In reply to Greg Kroah-Hartman

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

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

From: Yuanshen Cao <[email protected]>

[ Upstream commit ac75550ab5b5d73649bffea245c2075fd9249bd0 ]

The read_raw() implementation uses IIO_LIGHT to distinguish between the ambient light and proximity channels when handling IIO_CHAN_INFO_CALIBSCALE.

However, the ALS channel is registered as IIO_INTENSITY, while write_raw() correctly writes to IIO_INTENSITY. As a result, reading in_intensity0_calibscale incorrectly returns the proximity gain instead of the ALS gain.

This causes the following user-visible behavior:
- Writing in_intensity0_calibscale appears to have no effect because the readback reports the proximity gain.
- Writing in_proximity0_calibscale causes both in_proximity0_calibscale and in_intensity0_calibscale to report the same value.

Fix this by checking for IIO_INTENSITY in read_raw(), matching the channel definition and the existing write_raw() implementation.

Fixes 3c97c08b5735 ("staging: iio: add TAOS tsl2x7x driver")
---
 drivers/iio/light/tsl2772.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/iio/light/tsl2772.c +1 -1
index cab468a82b616..fffed029c24dd 100644
@@ -1263,7 +1263,7 @@ static int tsl2772_read_raw(struct iio_dev *indio_dev,
 		}
 		break;
 	case IIO_CHAN_INFO_CALIBSCALE:
-		if (chan->type == IIO_LIGHT)
+		if (chan->type == IIO_INTENSITY)
 			*val = tsl2772_als_gain[chip->settings.als_gain];
 		else
 			*val = tsl2772_prox_gain[chip->settings.prox_gain];
Signature · 1 line
2.53.0
author maintainer
#907 lore raw

PATCH 871/1376 iio: light: isl29028: return zero in write_raw() on success

In reply to Greg Kroah-Hartman

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

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

From: Sang-Heon Jeon <[email protected]>

[ Upstream commit 55b75622829779223b9e32aa9600a8651d3e2df4 ]

isl29028_write_raw() returns the value of pm_runtime_put_autosuspend(), which is 1 if the device is already runtime suspended.

In that case write() on the sysfs attribute returns 1 instead of the number of bytes written. Make isl29028_write_raw() always return zero on success.

Fixes 2db5054ac28d ("staging: iio: isl29028: add runtime power management support")
---
 drivers/iio/light/isl29028.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/iio/light/isl29028.c +1 -1
index 95bfb3ffa519a..a23b2af287d5e 100644
@@ -414,7 +414,7 @@ static int isl29028_write_raw(struct iio_dev *indio_dev,
 	if (ret < 0)
 		return ret;
 
-	return ret;
+	return 0;
 }
 
 static int isl29028_read_raw(struct iio_dev *indio_dev,
Signature · 1 line
2.53.0
author maintainer
#908 lore raw

PATCH 872/1376 iio: light: tsl2583: return zero in write_raw() on success

In reply to Greg Kroah-Hartman

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

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

From: Sang-Heon Jeon <[email protected]>

[ Upstream commit 42e8791841e0677418a3ccc97fa5c22a1455f417 ]

tsl2583_write_raw() returns the value of pm_runtime_put_autosuspend(), which is 1 if the device is already runtime suspended.

In that case write() on the sysfs attribute returns 1 instead of the number of bytes written. Make tsl2583_write_raw() always return zero on success.

Fixes 371894f5d1a0 ("iio: tsl2583: add runtime power management support")
---
 drivers/iio/light/tsl2583.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/iio/light/tsl2583.c +1 -1
index 02ad11611b9c5..dfcb8228ada1d 100644
@@ -800,7 +800,7 @@ static int tsl2583_write_raw(struct iio_dev *indio_dev,
 	if (ret < 0)
 		return ret;
 
-	return ret;
+	return 0;
 }
 
 static const struct iio_info tsl2583_info = {
Signature · 1 line
2.53.0
author maintainer
#909 lore raw

PATCH 873/1376 net: stmmac: Skip PHY attach if custom PCS is in use

In reply to Greg Kroah-Hartman

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

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

From: Zxyan Zhu <[email protected]>

[ Upstream commit af4d934164457f0578bf90e92ae0fcc5348260cb ]

When a platform provides a custom PCS via the pcs_init callback, the MAC's phylink_pcs is already configured. In this case, no traditional PHY device is needed.

Without this, stmmac_init_phy() falls through to the no-phy-node path and errors out with "no phy found" when the DT has no phy-handle for such interfaces.

Skip the PHY attach when priv->hw->phylink_pcs is set and phy_addr is invalid.

Fixes f0ef433fc264 ("net: stmmac: introduce pcs_init/pcs_exit stmmac operations")
---
 drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 4 ++++
 1 file changed, 4 insertions(+)
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +4 -0
index 44fd0fadc137d..3074e322a2cc2 100644
@@ -1183,6 +1183,10 @@ static int stmmac_init_phy(struct net_device *dev)
 		struct phy_device *phydev;
 
 		if (addr < 0) {
+			/* If a custom PCS is in use, no PHY is needed */
+			if (priv->hw->phylink_pcs)
+				return 0;
+
 			netdev_err(priv->dev, "no phy found\n");
 			return -ENODEV;
 		}
Signature · 1 line
2.53.0
author maintainer
#910 lore raw

PATCH 874/1376 phonet: pep: do not write beyond optlen in getsockopt

In reply to Greg Kroah-Hartman

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

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

From: Breno Leitao <[email protected]>

[ Upstream commit 77e5eb0e192aec6710c03ca8144582fd2af36ca4 ]

pep_getsockopt() clamps the reported length to the caller's buffer with min_t(), but then stores the value with put_user(val, (int __user *) optval), which always writes sizeof(int) bytes. A getsockopt() call with an optlen smaller than sizeof(int) thus reports the clamped length yet writes a full int, one to three bytes past the user buffer.

Write the value with copy_to_user() bounded by len, so at most optlen bytes are copied, matching the length reported back to userspace.

---
 net/phonet/pep.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
net/phonet/pep.c +1 -1
index 2a8dee4a327e4..4a350c4379004 100644
@@ -1116,7 +1116,7 @@ static int pep_getsockopt(struct sock *sk, int level, int optname,
 	len = min_t(unsigned int, sizeof(int), len);
 	if (put_user(len, optlen))
 		return -EFAULT;
-	if (put_user(val, (int __user *) optval))
+	if (copy_to_user(optval, &val, len))
 		return -EFAULT;
 	return 0;
 }
Signature · 1 line
2.53.0
author maintainer
#911 lore raw

PATCH 875/1376 blk-cgroup: skip dying blkg in blkcg_activate_policy()

In reply to Greg Kroah-Hartman

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

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

From: Zheng Qixing <[email protected]>

[ Upstream commit 5e9220389920f33b6a804d50c548cd0cd1b04634 ]

When switching IO schedulers on a block device, blkcg_activate_policy() can race with concurrent blkcg deletion, leading to a use-after-free in rcu_accelerate_cbs.

T1: T2:

                                  blkg_destroy
                                  kill(&blkg->refcnt) // blkg->refcnt=1->0
                                  blkg_release // call_rcu(__blkg_release)
                                  ...
                                  blkg_free_workfn
                                  ->pd_free_fn(pd)

elv_iosched_store
elevator_switch
...
iterate blkg list
blkg_get(blkg) // blkg->refcnt=0->1

                                  list_del_init(&blkg->q_node)

blkg_put(pinned_blkg) // blkg->refcnt=1->0
blkg_release // call_rcu again
rcu_accelerate_cbs // uaf

Fix this by checking hlist_unhashed(&blkg->blkcg_node) before getting a reference to the blkg. This is the same check used in blkg_destroy() to detect if a blkg has already been destroyed. If the blkg is already unhashed, skip processing it since it's being destroyed.

Fixes f1c006f1c685 ("blk-cgroup: synchronize pd_free_fn() from blkg_free_workfn() and blkcg_deactivate_policy()")
---
 block/blk-cgroup.c | 2 ++
 1 file changed, 2 insertions(+)
block/blk-cgroup.c +2 -0
index d95104f0fcdcf..8e7e2db4d1c27 100644
@@ -1573,6 +1573,8 @@ int blkcg_activate_policy(struct gendisk *disk, const struct blkcg_policy *pol)
 
 		if (blkg->pd[pol->plid])
 			continue;
+		if (hlist_unhashed(&blkg->blkcg_node))
+			continue;
 
 		/* If prealloc matches, use it; otherwise try GFP_NOWAIT */
 		if (blkg == pinned_blkg) {
Signature · 1 line
2.53.0
author maintainer
#912 lore raw

PATCH 876/1376 block/blk-stat: drain per-cpu callback stats over possible CPUs

In reply to Greg Kroah-Hartman

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

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

From: Tao Cui <[email protected]>

[ Upstream commit 9d617828cfc4d9a4d385daa2cd61f9db0592c53f ]

blk_stat_timer_fn() sums and resets a callback's per-cpu buckets using for_each_online_cpu(). A CPU that goes offline with pending samples is skipped, so its samples are neither accumulated into the window nor cleared; they sit in the bucket until the CPU comes back online, at which point the stale values are flushed into whatever window is then running.

This silently corrupts the latency picture that consumers (notably writeback throttling via wbt, and blk-mq latency tracking) base decisions on around CPU hotplug: under-counting while the CPU is offline, then a burst of stale data on re-online.

Fixes 34dbad5d26e2 ("blk-stat: convert to callback-based statistics reporting")
---
 block/blk-stat.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
block/blk-stat.c +1 -1
index eaf60097bbe17..0149abb59b76b 100644
@@ -83,7 +83,7 @@ static void blk_stat_timer_fn(struct timer_list *t)
 	for (bucket = 0; bucket < cb->buckets; bucket++)
 		blk_rq_stat_init(&cb->stat[bucket]);
 
-	for_each_online_cpu(cpu) {
+	for_each_possible_cpu(cpu) {
 		struct blk_rq_stat *cpu_stat;
 
 		cpu_stat = per_cpu_ptr(cb->cpu_stat, cpu);
Signature · 1 line
2.53.0
author maintainer
#913 lore raw

PATCH 877/1376 block/blk-iocost: collect per-cpu latency stats over possible CPUs

In reply to Greg Kroah-Hartman

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

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

From: Tao Cui <[email protected]>

[ Upstream commit 4e050c5b92c1600415b2cd452583e543036f3d73 ]

ioc_lat_stat() walks ioc->pcpu_stat with for_each_online_cpu() to compute missed-ppm and rq_wait deltas. An offlined CPU is skipped, so its delta is dropped from the period and its last_* watermark is not advanced; on re-online the next collection sees a delta spanning the whole offline interval, corrupting the latency/vrate picture.

---
 block/blk-iocost.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
block/blk-iocost.c +1 -1
index a5894ec9696e7..61a347f504d73 100644
@@ -1603,7 +1603,7 @@ static void ioc_lat_stat(struct ioc *ioc, u32 *missed_ppm_ar, u32 *rq_wait_pct_p
 	u64 rq_wait_ns = 0;
 	int cpu, rw;
 
-	for_each_online_cpu(cpu) {
+	for_each_possible_cpu(cpu) {
 		struct ioc_pcpu_stat *stat = per_cpu_ptr(ioc->pcpu_stat, cpu);
 		u64 this_rq_wait_ns;
 
Signature · 1 line
2.53.0
author maintainer
#914 lore raw

PATCH 878/1376 block/kyber-iosched: flush per-cpu latency buckets over possible CPUs

In reply to Greg Kroah-Hartman

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

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

From: Tao Cui <[email protected]>

[ Upstream commit 482fc257de95ab181688e9d1dfcc6b6a58857b1e ]

kyber_timer_fn() sums the per-cpu latency histograms with for_each_online_cpu(). A CPU that goes offline mid-interval leaves its bucket un-flushed; the samples are lost from the current decision and re-appear (stale) when the CPU is onlined again.

Fixes 6e25cb01ea20 ("kyber: implement improved heuristics")
---
 block/kyber-iosched.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
block/kyber-iosched.c +1 -1
index ccfefa6a36696..869e83054c48f 100644
@@ -279,7 +279,7 @@ static void kyber_timer_fn(struct timer_list *t)
 	bool bad = false;
 
 	/* Sum all of the per-cpu latency histograms. */
-	for_each_online_cpu(cpu) {
+	for_each_possible_cpu(cpu) {
 		struct kyber_cpu_latency *cpu_latency;
 
 		cpu_latency = per_cpu_ptr(kqd->cpu_latency, cpu);
Signature · 1 line
2.53.0
author maintainer
#915 lore raw

PATCH 879/1376 ublk: check for ublk_unmap_io() returning 0

In reply to Greg Kroah-Hartman

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

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

From: Caleb Sander Mateos <[email protected]>

[ Upstream commit 24fd3706178f1ae5501fd1ff9036e170ed0665ba ]

If the userspace ublk server passes an unmapped address as the data buffer for a completed ublk read, ublk_unmap_io() will return 0 indicating no bytes could be copied. Currently, this will result in calling blk_update_request() with nr_bytes=0, which doesn't seem supported. Fail the I/O with BLK_STS_IOERR in this case instead.

Fixes 71f28f3136af ("ublk_drv: add io_uring based userspace block driver")
---
 drivers/block/ublk_drv.c | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)
drivers/block/ublk_drv.c +7 -1
index df177ea0a47e6..aecac3b6749bb 100644
@@ -1072,8 +1072,14 @@ static inline void __ublk_complete_rq(struct request *req)
 	 *
 	 * Re-read simply for this unlikely case.
 	 */
-	if (unlikely(unmapped_bytes < io->res))
+	if (unlikely(unmapped_bytes < io->res)) {
+		if (unlikely(!unmapped_bytes)) {
+			res = BLK_STS_IOERR;
+			goto exit;
+		}
+
 		io->res = unmapped_bytes;
+	}
 
 	/*
 	 * Run bio->bi_end_io() with softirqs disabled. If the final fput
Signature · 1 line
2.53.0
author maintainer
#916 lore raw

PATCH 880/1376 o2hb_region_dev_store(): avoid goto around fdget()/fdput()

In reply to Greg Kroah-Hartman

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

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

From: Al Viro <[email protected]>

[ Upstream commit 554ceb7a5e14435725ac59a42bf0708f95721405 ]

Preparation for CLASS(fd) conversion.

Stable-dep-of: 688bc88e2046 ("ocfs2/cluster: keep heartbeat local node stable")

---
 fs/ocfs2/cluster/heartbeat.c | 11 +++++------
 1 file changed, 5 insertions(+), 6 deletions(-)
fs/ocfs2/cluster/heartbeat.c +5 -6
index 6914d615ec2dd..5743dff6af4ea 100644
@@ -1778,23 +1778,23 @@ static ssize_t o2hb_region_dev_store(struct config_item *item,
 	int live_threshold;
 
 	if (reg->hr_bdev_file)
-		goto out;
+		return -EINVAL;
 
 	/* We can't heartbeat without having had our node number
 	 * configured yet. */
 	if (o2nm_this_node() == O2NM_MAX_NODES)
-		goto out;
+		return -EINVAL;
 
 	fd = simple_strtol(p, &p, 0);
 	if (!p || (*p && (*p != '\n')))
-		goto out;
+		return -EINVAL;
 
 	if (fd < 0 || fd >= INT_MAX)
-		goto out;
+		return -EINVAL;
 
 	f = fdget(fd);
 	if (fd_file(f) == NULL)
-		goto out;
+		return -EINVAL;
 
 	if (reg->hr_blocks == 0 || reg->hr_start_block == 0 ||
 	    reg->hr_block_bytes == 0)
@@ -1916,7 +1916,6 @@ static ssize_t o2hb_region_dev_store(struct config_item *item,
 	}
 out2:
 	fdput(f);
-out:
 	return ret;
 }
 
Signature · 1 line
2.53.0
author maintainer
#917 lore raw

PATCH 881/1376 ocfs2/cluster: keep heartbeat local node stable

In reply to Greg Kroah-Hartman

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

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

From: Cen Zhang <[email protected]>

[ Upstream commit 688bc88e2046dd6ce81ce18079b5254cb8dadc0e ]

o2nm_node_local_store() handles local=0 by stopping o2net and setting cl_local_node to O2NM_INVALID_NODE_NUM, but it leaves cl_has_local set. That stale state makes o2nm_this_node() return 255, blocks a later local=1 attempt with -EBUSY, and can feed 255 to heartbeat users that call o2nm_this_node() dynamically.

Clearing cl_has_local is required when the local node is reset. But heartbeat threads can still be running at that point. They pin the local node config item at startup, yet o2hb_do_disk_heartbeat() and thread teardown re-read o2nm_this_node() for the local slot and for o2nm_undepend_this_node(). Once local=0 has cleared the live local-node state, those dynamic reads return O2NM_MAX_NODES, which is also the invalid node number 255.

Store the local node number in the heartbeat region when the region starts. Use that stable node for heartbeat slot writes/checks, negotiation messages, and the final configfs undepend. Stop the heartbeat loop when the current local node no longer matches the stored node, and clear cl_has_local together with cl_local_node in the local=0 path so nodemanager state matches node removal.

Validation reproduced this kernel report:
KASAN slab-out-of-bounds in o2hb_do_disk_heartbeat+0x372/0xb30

RIP: 0010:memset+0xf/0x20
Read of size 8
Call trace:
  dump_stack_lvl+0x66/0xa0
  print_report+0xd0/0x630
  o2hb_do_disk_heartbeat+0x372/0xb30 (fs/ocfs2/cluster/heartbeat.c:1079)
  srso_alias_return_thunk+0x5/0xfbef5
  __virt_addr_valid+0x188/0x2f0
  kasan_report+0xe4/0x120
  o2hb_do_disk_heartbeat+0x5/0xb30 (fs/ocfs2/cluster/heartbeat.c:1079)
  o2hb_thread+0x14e/0x770
  kthread_affine_node+0x139/0x180
  lockdep_hardirqs_on_prepare+0xda/0x190
  trace_hardirqs_on+0x18/0x130
  kthread+0x19d/0x1e0
  ret_from_fork+0x37a/0x4d0
  __switch_to+0x2d5/0x6f0
  ret_from_fork_asm+0x1a/0x30
Fixes a7f6a5fb4bde ("[PATCH] OCFS2: The Second Oracle Cluster Filesystem")
Assisted-by Codex:gpt-5.5
Cc Mark Fasheh <[email protected]>
Cc Joel Becker <[email protected]>
Cc Junxiao Bi <[email protected]>
Cc Changwei Ge <[email protected]>
Cc Jun Piao <[email protected]>
Cc Heming Zhao <[email protected]>
---
 fs/ocfs2/cluster/heartbeat.c   | 43 +++++++++++++++++++++++-----------
 fs/ocfs2/cluster/nodemanager.c | 19 +++++++++++----
 fs/ocfs2/cluster/nodemanager.h |  2 ++
 3 files changed, 46 insertions(+), 18 deletions(-)
fs/ocfs2/cluster/heartbeat.c +29 -14
index 5743dff6af4ea..ea44ea6efbc6e 100644
@@ -210,6 +210,7 @@ struct o2hb_region {
 
 	/* protected by the hr_callback_sem */
 	struct task_struct 	*hr_task;
+	u8			hr_node_num;
 
 	unsigned int		hr_blocks;
 	unsigned long long	hr_start_block;
@@ -357,12 +358,12 @@ static void o2hb_disarm_timeout(struct o2hb_region *reg)
 	cancel_delayed_work_sync(&reg->hr_nego_timeout_work);
 }
 
-static int o2hb_send_nego_msg(int key, int type, u8 target)
+static int o2hb_send_nego_msg(int key, int type, u8 target, u8 node_num)
 {
 	struct o2hb_nego_msg msg;
 	int status, ret;
 
-	msg.node_num = o2nm_this_node();
+	msg.node_num = node_num;
 again:
 	ret = o2net_send_message(type, key, &msg, sizeof(msg),
 			target, &status);
@@ -380,8 +381,10 @@ static void o2hb_nego_timeout(struct work_struct *work)
 	unsigned long live_node_bitmap[BITS_TO_LONGS(O2NM_MAX_NODES)];
 	int master_node, i, ret;
 	struct o2hb_region *reg;
+	u8 node_num;
 
 	reg = container_of(work, struct o2hb_region, hr_nego_timeout_work.work);
+	node_num = reg->hr_node_num;
 	/* don't negotiate timeout if last hb failed since it is very
 	 * possible io failed. Should let write timeout fence self.
 	 */
@@ -392,10 +395,10 @@ static void o2hb_nego_timeout(struct work_struct *work)
 	/* lowest node as master node to make negotiate decision. */
 	master_node = find_first_bit(live_node_bitmap, O2NM_MAX_NODES);
 
-	if (master_node == o2nm_this_node()) {
+	if (master_node == node_num) {
 		if (!test_bit(master_node, reg->hr_nego_node_bitmap)) {
 			printk(KERN_NOTICE "o2hb: node %d hb write hung for %ds on region %s (%pg).\n",
-				o2nm_this_node(), O2HB_NEGO_TIMEOUT_MS/1000,
+				node_num, O2HB_NEGO_TIMEOUT_MS / 1000,
 				config_item_name(&reg->hr_item), reg_bdev(reg));
 			set_bit(master_node, reg->hr_nego_node_bitmap);
 		}
@@ -424,7 +427,7 @@ static void o2hb_nego_timeout(struct work_struct *work)
 
 			mlog(ML_HEARTBEAT, "send NEGO_APPROVE msg to node %d\n", i);
 			ret = o2hb_send_nego_msg(reg->hr_key,
-					O2HB_NEGO_APPROVE_MSG, i);
+					O2HB_NEGO_APPROVE_MSG, i, node_num);
 			if (ret)
 				mlog(ML_ERROR, "send NEGO_APPROVE msg to node %d fail %d\n",
 					i, ret);
@@ -432,10 +435,10 @@ static void o2hb_nego_timeout(struct work_struct *work)
 	} else {
 		/* negotiate timeout with master node. */
 		printk(KERN_NOTICE "o2hb: node %d hb write hung for %ds on region %s (%pg), negotiate timeout with node %d.\n",
-			o2nm_this_node(), O2HB_NEGO_TIMEOUT_MS/1000, config_item_name(&reg->hr_item),
+			node_num, O2HB_NEGO_TIMEOUT_MS / 1000, config_item_name(&reg->hr_item),
 			reg_bdev(reg), master_node);
 		ret = o2hb_send_nego_msg(reg->hr_key, O2HB_NEGO_TIMEOUT_MSG,
-				master_node);
+				master_node, node_num);
 		if (ret)
 			mlog(ML_ERROR, "send NEGO_TIMEOUT msg to node %d fail %d\n",
 				master_node, ret);
@@ -608,7 +611,9 @@ static int o2hb_issue_node_write(struct o2hb_region *reg,
 
 	o2hb_bio_wait_init(write_wc);
 
-	slot = o2nm_this_node();
+	slot = reg->hr_node_num;
+	if (slot >= O2NM_MAX_NODES)
+		return -EINVAL;
 
 	bio = o2hb_setup_one_bio(reg, write_wc, &slot, slot+1,
 				 REQ_OP_WRITE | REQ_SYNC);
@@ -677,8 +682,12 @@ static int o2hb_check_own_slot(struct o2hb_region *reg)
 	struct o2hb_disk_slot *slot;
 	struct o2hb_disk_heartbeat_block *hb_block;
 	char *errstr;
+	u8 node_num = reg->hr_node_num;
+
+	if (node_num >= O2NM_MAX_NODES)
+		return 0;
 
-	slot = &reg->hr_slots[o2nm_this_node()];
+	slot = &reg->hr_slots[node_num];
 	/* Don't check on our 1st timestamp */
 	if (!slot->ds_last_time)
 		return 0;
@@ -719,7 +728,10 @@ static inline void o2hb_prepare_block(struct o2hb_region *reg,
 	struct o2hb_disk_slot *slot;
 	struct o2hb_disk_heartbeat_block *hb_block;
 
-	node_num = o2nm_this_node();
+	node_num = reg->hr_node_num;
+	if (node_num >= O2NM_MAX_NODES)
+		return;
+
 	slot = &reg->hr_slots[node_num];
 
 	hb_block = (struct o2hb_disk_heartbeat_block *)slot->ds_raw_block;
@@ -1213,7 +1225,7 @@ static int o2hb_thread(void *data)
 	set_user_nice(current, MIN_NICE);
 
 	/* Pin node */
-	ret = o2nm_depend_this_node();
+	ret = o2nm_depend_node(reg->hr_node_num);
 	if (ret) {
 		mlog(ML_ERROR, "Node has been deleted, ret = %d\n", ret);
 		reg->hr_node_deleted = 1;
@@ -1222,7 +1234,8 @@ static int o2hb_thread(void *data)
 	}
 
 	while (!kthread_should_stop() &&
-	       !reg->hr_unclean_stop && !reg->hr_aborted_start) {
+	       !reg->hr_unclean_stop && !reg->hr_aborted_start &&
+	       o2nm_this_node() == reg->hr_node_num) {
 		/* We track the time spent inside
 		 * o2hb_do_disk_heartbeat so that we avoid more than
 		 * hr_timeout_ms between disk writes. On busy systems
@@ -1271,7 +1284,7 @@ static int o2hb_thread(void *data)
 	}
 
 	/* Unpin node */
-	o2nm_undepend_this_node();
+	o2nm_undepend_node(reg->hr_node_num);
 
 	mlog(ML_HEARTBEAT|ML_KTHREAD, "o2hb thread exiting\n");
 
@@ -1782,7 +1795,8 @@ static ssize_t o2hb_region_dev_store(struct config_item *item,
 
 	/* We can't heartbeat without having had our node number
 	 * configured yet. */
-	if (o2nm_this_node() == O2NM_MAX_NODES)
+	reg->hr_node_num = o2nm_this_node();
+	if (reg->hr_node_num == O2NM_MAX_NODES)
 		return -EINVAL;
 
 	fd = simple_strtol(p, &p, 0);
@@ -2017,6 +2031,7 @@ static struct config_item *o2hb_heartbeat_group_make_item(struct config_group *g
 		ret = -ENAMETOOLONG;
 		goto free;
 	}
+	reg->hr_node_num = O2NM_MAX_NODES;
 
 	spin_lock(&o2hb_live_lock);
 	reg->hr_region_num = 0;
fs/ocfs2/cluster/nodemanager.c +15 -4
index 5fffbed779da7..46e0c9ba8a4ff 100644
@@ -366,6 +366,7 @@ static ssize_t o2nm_node_local_store(struct config_item *item, const char *page,
 	if (!tmp && cluster->cl_has_local &&
 	    cluster->cl_local_node == node->nd_num) {
 		o2net_stop_listening(node);
+		cluster->cl_has_local = 0;
 		cluster->cl_local_node = O2NM_INVALID_NODE_NUM;
 	}
 
@@ -787,12 +788,12 @@ void o2nm_undepend_item(struct config_item *item)
 	configfs_undepend_item(item);
 }
 
-int o2nm_depend_this_node(void)
+int o2nm_depend_node(u8 node_num)
 {
 	int ret = 0;
 	struct o2nm_node *local_node;
 
-	local_node = o2nm_get_node_by_num(o2nm_this_node());
+	local_node = o2nm_get_node_by_num(node_num);
 	if (!local_node) {
 		ret = -EINVAL;
 		goto out;
@@ -805,17 +806,27 @@ int o2nm_depend_this_node(void)
 	return ret;
 }
 
-void o2nm_undepend_this_node(void)
+void o2nm_undepend_node(u8 node_num)
 {
 	struct o2nm_node *local_node;
 
-	local_node = o2nm_get_node_by_num(o2nm_this_node());
+	local_node = o2nm_get_node_by_num(node_num);
 	BUG_ON(!local_node);
 
 	o2nm_undepend_item(&local_node->nd_item);
 	o2nm_node_put(local_node);
 }
 
+int o2nm_depend_this_node(void)
+{
+	return o2nm_depend_node(o2nm_this_node());
+}
+
+void o2nm_undepend_this_node(void)
+{
+	o2nm_undepend_node(o2nm_this_node());
+}
+
 
 static void __exit exit_o2nm(void)
 {
fs/ocfs2/cluster/nodemanager.h +2 -0
index 2f72f56996bd9..ca3483fb54504 100644
@@ -66,6 +66,8 @@ void o2nm_node_put(struct o2nm_node *node);
 int o2nm_depend_item(struct config_item *item);
 int o2nm_depend_item_unlocked(struct config_item *item);
 void o2nm_undepend_item(struct config_item *item);
+int o2nm_depend_node(u8 node_num);
+void o2nm_undepend_node(u8 node_num);
 int o2nm_depend_this_node(void);
 void o2nm_undepend_this_node(void);
 
Signature · 1 line
2.53.0
author maintainer
#918 lore raw

PATCH 882/1376 lib/string: fix memchr_inv() for large ranges

In reply to Greg Kroah-Hartman

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

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

From: Bradley Morgan <[email protected]>

[ Upstream commit c04cffb8c51618538f0c05c478a931eb6e1a806b ]

memchr_inv() takes a size_t length but counts 8 byte words in an unsigned int. At 32GiB that count wraps, so the scan can quietly miss most of the range.

Use size_t for the word count.

Fixes 798248206b59 ("lib/string.c: introduce memchr_inv()")
Cc Akinbou Mita <[email protected]>
Cc Andy Shevchenko <[email protected]>
Cc Christoph Lameer <[email protected]>
Cc Joern Engel <[email protected]>
Cc Kees Cook <[email protected]>
Cc Pekka Enberg <[email protected]>
---
 lib/string.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
lib/string.c +2 -1
index e657809fa7189..8d117fed31264 100644
@@ -828,7 +828,8 @@ void *memchr_inv(const void *start, int c, size_t bytes)
 {
 	u8 value = c;
 	u64 value64;
-	unsigned int words, prefix;
+	size_t words;
+	unsigned int prefix;
 
 	if (bytes <= 16)
 		return check_bytes8(start, value, bytes);
Signature · 1 line
2.53.0
author maintainer
#919 lore raw

PATCH 883/1376 pps: dont try to wait for negative timeouts in PPS_FETCH

In reply to Greg Kroah-Hartman

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

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

From: Calvin Owens <[email protected]>

[ Upstream commit 45217e98987a87ff2372386dbf82fd5325db28ea ]

If userspace passes a negative timeout to PPS_FETCH, it triggers a kernel splat from schedule_timeout():

    schedule_timeout: wrong timeout value fffffffffff0bfb4
    CPU: 17 UID: 0 PID: 4720 Comm: a.out Not tainted 7.1.0-rc5-x86-kvm-00150-g331d97e36b37 #1 PREEMPT_RT
    Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-20240910_120124-localhost 04/01/2014
    Call Trace:
     <TASK>
     dump_stack_lvl+0x4b/0x70
     schedule_timeout+0xb7/0xe0
     pps_cdev_pps_fetch.isra.0+0x93/0x150
     pps_cdev_ioctl+0x70/0x310
     __x64_sys_ioctl+0x7b/0xc0
     do_syscall_64+0xb6/0xfc0
     entry_SYSCALL_64_after_hwframe+0x4b/0x53

Here is a trivial reproducer that works with the PPS_CLIENT_KTIMER test device enabled in the kernel:

    #include <stdlib.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include <sys/ioctl.h>
    #include <linux/pps.h>
    #include <err.h>

    int main() {
        struct pps_fdata fdata;
        int fd;

        fd = open("/dev/pps0", O_RDWR);
        if (fd == -1)
            err(1, "Failed to open /dev/pps0");

        fdata.timeout.sec = -1;
        fdata.timeout.nsec = 0;

        if (ioctl(fd, PPS_FETCH, &fdata))
            err(2, "PPS_FETCH failed");

        close(fd);
        return 0;
    }

Sashiko imagines this to be some sort of security problem, which is obviously really silly. But I think it is still worth fixing, so buggy userspace code can't trigger the splat.

Silence the splat by using timespec64_to_jiffies(), which hard limits the timeout to LONG_MAX jiffies. To be safe, explicitly preserve the -ETIMEDOUT return value userspace sees today if it passes a negative timeout.

If you really squint, this is still a slight behavior change in that there are "denormalized" combinations of tv_sec and tv_nsec which used to work but will now return -ETIMEDOUT. I can't imagine anybody will care about that...

---
 drivers/pps/pps.c | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)
drivers/pps/pps.c +8 -2
index c6b8b64782761..2e04636f43805 100644
@@ -63,13 +63,19 @@ static int pps_cdev_pps_fetch(struct pps_device *pps, struct pps_fdata *fdata)
 		err = wait_event_interruptible(pps->queue,
 				ev != pps->last_ev);
 	else {
+		struct timespec64 ts;
 		unsigned long ticks;
 
 		dev_dbg(&pps->dev, "timeout %lld.%09d\n",
 				(long long) fdata->timeout.sec,
 				fdata->timeout.nsec);
-		ticks = fdata->timeout.sec * HZ;
-		ticks += fdata->timeout.nsec / (NSEC_PER_SEC / HZ);
+
+		if (fdata->timeout.sec < 0)
+			return -ETIMEDOUT;
+
+		ts.tv_sec = fdata->timeout.sec;
+		ts.tv_nsec = fdata->timeout.nsec;
+		ticks = timespec64_to_jiffies(&ts);
 
 		if (ticks != 0) {
 			err = wait_event_interruptible_timeout(
Signature · 1 line
2.53.0
author maintainer
#920 lore raw

PATCH 884/1376 pps: clients: gpio: Bypass edges direction check when not needed

In reply to Greg Kroah-Hartman

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

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

From: Bastien Curutchet <[email protected]>

[ Upstream commit a01f6287c244f35eeec11ca932d09061181eed8c ]

In the IRQ handler, the GPIO's state is read to verify the direction of the edge that triggered the interruption before generating the PPS event. If a pulse is too short, the GPIO line can reach back its original state before this verification and the PPS event is lost.

This check is needed when info->capture_clear is set because it needs interruptions on both rising and falling edges. When info->capture_clear is not set, interruption is triggered by one edge only so this check can be omitted.

Add a warning if irq_handler is left without triggering any PPS event. Bypass the edge's direction verification when info->capture_clear is not set.

Stable-dep-of: b899e0279f90 ("pps-gpio: remove dead capture_clear code")

---
 drivers/pps/clients/pps-gpio.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)
drivers/pps/clients/pps-gpio.c +5 -1
index 1412f8af15f27..027759e2d563c 100644
@@ -52,7 +52,9 @@ static irqreturn_t pps_gpio_irq_handler(int irq, void *data)
 
 	info = data;
 
-	rising_edge = gpiod_get_value(info->gpio_pin);
+	/* Small trick to bypass the check on edge's direction when capture_clear is unset */
+	rising_edge = info->capture_clear ?
+		      gpiod_get_value(info->gpio_pin) : !info->assert_falling_edge;
 	if ((rising_edge && !info->assert_falling_edge) ||
 			(!rising_edge && info->assert_falling_edge))
 		pps_event(info->pps, &ts, PPS_CAPTUREASSERT, data);
@@ -60,6 +62,8 @@ static irqreturn_t pps_gpio_irq_handler(int irq, void *data)
 			((rising_edge && info->assert_falling_edge) ||
 			(!rising_edge && !info->assert_falling_edge)))
 		pps_event(info->pps, &ts, PPS_CAPTURECLEAR, data);
+	else
+		dev_warn_ratelimited(&info->pps->dev, "IRQ did not trigger any PPS event\n");
 
 	return IRQ_HANDLED;
 }
Signature · 1 line
2.53.0
author maintainer
#921 lore raw

PATCH 885/1376 pps: pps-gpio: split IRQ handler into hardirq timestamper + threaded handler

In reply to Greg Kroah-Hartman

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

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

From: Michael Byczkowski <[email protected]>

[ Upstream commit 93781560b2fdd26fa8499d64db8a95a07e1dc902 ]

Split the pps-gpio interrupt handler into a primary (hardirq) handler that captures the PPS timestamp at interrupt entry, and a threaded handler that processes the event. This produces the same two-part handler structure on both PREEMPT_RT and non-RT kernels.

On non-RT kernels the threaded portion runs immediately after the primary, with no behavioral change compared to the previous single-handler implementation.

On PREEMPT_RT, where interrupt handlers are force-threaded by default, the previous single-handler implementation captured the timestamp inside the threaded portion, after IRQ-thread scheduling delay. With the split, the timestamp is captured in true hardirq context as it is on non-RT kernels, eliminating a significant source of PPS jitter on RT systems.

Stable-dep-of: b899e0279f90 ("pps-gpio: remove dead capture_clear code")

---
 drivers/pps/clients/pps-gpio.c | 37 +++++++++++++++++++++++-----------
 1 file changed, 25 insertions(+), 12 deletions(-)
drivers/pps/clients/pps-gpio.c +25 -12
index 027759e2d563c..99b4ca57a3ab4 100644
@@ -35,33 +35,44 @@ struct pps_gpio_device_data {
 	bool capture_clear;
 	unsigned int echo_active_ms;	/* PPS echo active duration */
 	unsigned long echo_timeout;	/* timer timeout value in jiffies */
+	struct pps_event_time ts;	/* timestamp captured in hardirq */
 };
 
 /*
  * Report the PPS event
  */
 
-static irqreturn_t pps_gpio_irq_handler(int irq, void *data)
+/*
+ * Primary hardirq handler -- runs in hardirq context even on PREEMPT_RT.
+ * Only captures the timestamp; all other work is deferred to the thread.
+ */
+static irqreturn_t pps_gpio_irq_hardirq(int irq, void *data)
 {
-	const struct pps_gpio_device_data *info;
-	struct pps_event_time ts;
-	int rising_edge;
+	struct pps_gpio_device_data *info = data;
+
+	pps_get_ts(&info->ts);
 
-	/* Get the time stamp first */
-	pps_get_ts(&ts);
+	return IRQ_WAKE_THREAD;
+}
 
-	info = data;
+/*
+ * Threaded handler -- processes the PPS event using the timestamp
+ * captured in hardirq context above.
+ */
+static irqreturn_t pps_gpio_irq_thread(int irq, void *data)
+{
+	struct pps_gpio_device_data *info = data;
+	int rising_edge;
 
-	/* Small trick to bypass the check on edge's direction when capture_clear is unset */
 	rising_edge = info->capture_clear ?
 		      gpiod_get_value(info->gpio_pin) : !info->assert_falling_edge;
 	if ((rising_edge && !info->assert_falling_edge) ||
 			(!rising_edge && info->assert_falling_edge))
-		pps_event(info->pps, &ts, PPS_CAPTUREASSERT, data);
+		pps_event(info->pps, &info->ts, PPS_CAPTUREASSERT, data);
 	else if (info->capture_clear &&
 			((rising_edge && info->assert_falling_edge) ||
 			(!rising_edge && !info->assert_falling_edge)))
-		pps_event(info->pps, &ts, PPS_CAPTURECLEAR, data);
+		pps_event(info->pps, &info->ts, PPS_CAPTURECLEAR, data);
 	else
 		dev_warn_ratelimited(&info->pps->dev, "IRQ did not trigger any PPS event\n");
 
@@ -210,8 +221,10 @@ static int pps_gpio_probe(struct platform_device *pdev)
 	}
 
 	/* register IRQ interrupt handler */
-	ret = request_irq(data->irq, pps_gpio_irq_handler,
-			  get_irqf_trigger_flags(data), data->info.name, data);
+	ret = request_threaded_irq(data->irq,
+			  pps_gpio_irq_hardirq, pps_gpio_irq_thread,
+			  get_irqf_trigger_flags(data) | IRQF_ONESHOT,
+			  data->info.name, data);
 	if (ret) {
 		pps_unregister_source(data->pps);
 		dev_err(dev, "failed to acquire IRQ %d\n", data->irq);
Signature · 1 line
2.53.0
author maintainer
#922 lore raw

PATCH 886/1376 pps-gpio: remove dead capture_clear code

In reply to Greg Kroah-Hartman

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

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

From: Calvin Owens <[email protected]>

[ Upstream commit b899e0279f90c3ce4099d68b989dd27861cc5c4f ]

The capture_clear field is never set, and all code conditional on it being set has been unreachable since the platform data logic was removed from pps-gpio in ee89646619ba ("pps: clients: gpio: Get rid of legacy platform data").

I think the only logical thing to do here is to remove it all, since no in-tree code ever actually used it in the first place, and it has been completely dead code for over five years (since v5.13).

Sashiko asked some questions about the gpiod_get_value() call which caused me to look deeper and figure this out, but it did not actually notice capture_clear is never set.

---
 drivers/pps/clients/pps-gpio.c | 37 ++++------------------------------
 1 file changed, 4 insertions(+), 33 deletions(-)
drivers/pps/clients/pps-gpio.c +4 -33
index 99b4ca57a3ab4..96e169a35cedb 100644
@@ -32,7 +32,6 @@ struct pps_gpio_device_data {
 	struct gpio_desc *echo_pin;
 	struct timer_list echo_timer;	/* timer to reset echo active state */
 	bool assert_falling_edge;
-	bool capture_clear;
 	unsigned int echo_active_ms;	/* PPS echo active duration */
 	unsigned long echo_timeout;	/* timer timeout value in jiffies */
 	struct pps_event_time ts;	/* timestamp captured in hardirq */
@@ -62,19 +61,8 @@ static irqreturn_t pps_gpio_irq_hardirq(int irq, void *data)
 static irqreturn_t pps_gpio_irq_thread(int irq, void *data)
 {
 	struct pps_gpio_device_data *info = data;
-	int rising_edge;
-
-	rising_edge = info->capture_clear ?
-		      gpiod_get_value(info->gpio_pin) : !info->assert_falling_edge;
-	if ((rising_edge && !info->assert_falling_edge) ||
-			(!rising_edge && info->assert_falling_edge))
-		pps_event(info->pps, &info->ts, PPS_CAPTUREASSERT, data);
-	else if (info->capture_clear &&
-			((rising_edge && info->assert_falling_edge) ||
-			(!rising_edge && !info->assert_falling_edge)))
-		pps_event(info->pps, &info->ts, PPS_CAPTURECLEAR, data);
-	else
-		dev_warn_ratelimited(&info->pps->dev, "IRQ did not trigger any PPS event\n");
+
+	pps_event(info->pps, &info->ts, PPS_CAPTUREASSERT, data);
 
 	return IRQ_HANDLED;
 }
@@ -90,11 +78,6 @@ static void pps_gpio_echo(struct pps_device *pps, int event, void *data)
 		if (pps->params.mode & PPS_ECHOASSERT)
 			gpiod_set_value(info->echo_pin, 1);
 		break;
-
-	case PPS_CAPTURECLEAR:
-		if (pps->params.mode & PPS_ECHOCLEAR)
-			gpiod_set_value(info->echo_pin, 1);
-		break;
 	}
 
 	/* fire the timer */
@@ -156,15 +139,8 @@ static int pps_gpio_setup(struct device *dev)
 static unsigned long
 get_irqf_trigger_flags(const struct pps_gpio_device_data *data)
 {
-	unsigned long flags = data->assert_falling_edge ?
-		IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING;
-
-	if (data->capture_clear) {
-		flags |= ((flags & IRQF_TRIGGER_RISING) ?
-				IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING);
-	}
-
-	return flags;
+	return data->assert_falling_edge ? IRQF_TRIGGER_FALLING :
+					   IRQF_TRIGGER_RISING;
 }
 
 static int pps_gpio_probe(struct platform_device *pdev)
@@ -197,9 +173,6 @@ static int pps_gpio_probe(struct platform_device *pdev)
 	/* initialize PPS specific parts of the bookkeeping data structure. */
 	data->info.mode = PPS_CAPTUREASSERT | PPS_OFFSETASSERT |
 		PPS_ECHOASSERT | PPS_CANWAIT | PPS_TSFMT_TSPEC;
-	if (data->capture_clear)
-		data->info.mode |= PPS_CAPTURECLEAR | PPS_OFFSETCLEAR |
-			PPS_ECHOCLEAR;
 	data->info.owner = THIS_MODULE;
 	snprintf(data->info.name, PPS_MAX_NAME_LEN - 1, "%s.%d",
 		 pdev->name, pdev->id);
@@ -211,8 +184,6 @@ static int pps_gpio_probe(struct platform_device *pdev)
 
 	/* register PPS source */
 	pps_default_params = PPS_CAPTUREASSERT | PPS_OFFSETASSERT;
-	if (data->capture_clear)
-		pps_default_params |= PPS_CAPTURECLEAR | PPS_OFFSETCLEAR;
 	data->pps = pps_register_source(&data->info, pps_default_params);
 	if (IS_ERR(data->pps)) {
 		dev_err(dev, "failed to register IRQ %d as PPS source\n",
Signature · 1 line
2.53.0
author maintainer
#923 lore raw

PATCH 887/1376 rapidio: clear mport->net when rio_add_net() fails

In reply to Greg Kroah-Hartman

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

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

From: Guangshuo Li <[email protected]>

[ Upstream commit b74030fbf187b43c1f85b66a7082e9946511cb5d ]

rio_alloc_net() stores the newly allocated rio_net in mport->net before rio_scan_alloc_net() registers the device.

If rio_add_net() fails, rio_scan_alloc_net() drops the device reference with put_device(), which releases the rio_net through the device release callback. However, mport->net is left pointing at the freed object.

A later mport unregister path can then dereference the dangling mport->net pointer and may try to free the same rio_net again.

Clear mport->net in the rio_add_net() failure path, matching the cleanup done for the destID table allocation failure path.

Fixes e842f9a1edf3 ("rapidio: add check for rio_add_net() in rio_scan_alloc_net()")
Cc Alexandre Bounine <[email protected]>
Cc Matt Porter <[email protected]>
Cc Yang yingliang <[email protected]>
---
 drivers/rapidio/rio-scan.c | 1 +
 1 file changed, 1 insertion(+)
drivers/rapidio/rio-scan.c +1 -0
index dcd6619a4b027..3cc25d0534513 100644
@@ -874,6 +874,7 @@ static struct rio_net *rio_scan_alloc_net(struct rio_mport *mport,
 		net->dev.release = rio_scan_release_dev;
 		if (rio_add_net(net)) {
 			put_device(&net->dev);
+			mport->net = NULL;
 			net = NULL;
 		}
 	}
Signature · 1 line
2.53.0
author maintainer
#924 lore raw

PATCH 888/1376 fat: release buffer head after rebuilding parent

In reply to Greg Kroah-Hartman

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

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

From: Yichong Chen <[email protected]>

[ Upstream commit 83e98dbf19ab64e8528e101e20f8d50e1aaa68a8 ]

fat_scan_logstart() leaves the matching directory entry's buffer head in sinfo.bh for the caller to release, just like fat_scan().

fat_rebuild_parent() uses the directory entry to rebuild the parent inode for the nostale_ro NFS export path, but does not release sinfo.bh after a successful scan. Release it once fat_build_inode() has consumed the directory entry data.

Fixes f1e6fb0ab451 ("fat (exportfs): rebuild directory-inode if fat_dget()")
Cc Christian Brauner <[email protected]>
Cc Amit Sahrawat <[email protected]>
Cc chenyichong <[email protected]>
Cc Namjae Jeon <[email protected]>
Cc Ravishankar N <[email protected]>
---
 fs/fat/nfs.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
fs/fat/nfs.c +3 -1
index 509eea96a457d..6e1b371711edd 100644
@@ -250,8 +250,10 @@ struct inode *fat_rebuild_parent(struct super_block *sb, int parent_logstart)
 		MSDOS_I(dummy_grand_parent)->i_pos = -1;
 	}
 
-	if (!fat_scan_logstart(dummy_grand_parent, clus_to_match, &sinfo))
+	if (!fat_scan_logstart(dummy_grand_parent, clus_to_match, &sinfo)) {
 		parent = fat_build_inode(sb, sinfo.de, sinfo.i_pos);
+		brelse(sinfo.bh);
+	}
 
 	brelse(parent_bh);
 	iput(dummy_grand_parent);
Signature · 1 line
2.53.0
author maintainer
#925 lore raw

PATCH 889/1376 riscv: dts: sophgo: cv180x: Allow the DMA multiplexer to set channel number for DMA controller

In reply to Greg Kroah-Hartman

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

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

From: Inochi Amaoto <[email protected]>

[ Upstream commit 5011466bade64483bb52bc4a926719d6794e6dab ]

Change the DMA controller compatible to the sophgo,cv1800b-axi-dma, which supports setting DMA channel number in DMA phandle args.

This dts change does not break backward compatibility as a fallback compatiable string is added.

---
 arch/riscv/boot/dts/sophgo/cv18xx.dtsi | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
arch/riscv/boot/dts/sophgo/cv18xx.dtsi +1 -1
index b8063ba6d6d7f..e519a72c093d1 100644
@@ -298,7 +298,7 @@ sdhci0: mmc@4310000 {
 		};
 
 		dmac: dma-controller@4330000 {
-			compatible = "snps,axi-dma-1.01a";
+			compatible = "sophgo,cv1800b-axi-dma", "snps,axi-dma-1.01a";
 			reg = <0x04330000 0x1000>;
 			interrupts = <29 IRQ_TYPE_LEVEL_HIGH>;
 			clocks = <&clk CLK_SDMA_AXI>, <&clk CLK_SDMA_AXI>;
Signature · 1 line
2.53.0
#926 lore raw

Re: [PATCH 6.12 0889/1376] riscv: dts: sophgo: cv180x: Allow the DMA multiplexer to set channel number for DMA controller

Hi Greg/Sasha,

Quoted 31 lines · Greg Kroah-Hartman

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

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

From: Inochi Amaoto <[email protected]>

[ Upstream commit 5011466bade64483bb52bc4a926719d6794e6dab ]

Change the DMA controller compatible to the sophgo,cv1800b-axi-dma, which supports setting DMA channel number in DMA phandle args.

This dts change does not break backward compatibility as a fallback compatiable string is added.

---
  arch/riscv/boot/dts/sophgo/cv18xx.dtsi | 2 +-
  1 file changed, 1 insertion(+), 1 deletion(-)
arch/riscv/boot/dts/sophgo/cv18xx.dtsi +0 -0
index b8063ba6d6d7f..e519a72c093d1 100644

arch/riscv/boot/dts/sophgo/cv18xx.dtsi sdhci0: mmc@4310000 { · line 301

Quoted 5 lines
arch/riscv/boot/dts/sophgo/cv18xx.dtsi +1 -1
@@ -298,7 +298,7 @@ sdhci0: mmc@4310000 {
  		};
 
  		dmac: dma-controller@4330000 {
-			compatible = "snps,axi-dma-1.01a";
+			compatible = "sophgo,cv1800b-axi-dma", "snps,axi-dma-1.01a";

I ran an AI-assisted backport review, and I have verified the report. It looks valid to me.

The patch adds the Sophgo CV1800B DMA compatible, but 6.12 lacks the matching driver support from b49c70273801. The generic fallback therefore matches and still calls dma_get_any_slave_channel(), so the phandle channel number is not honored. The branch also lacks the CV1800B mux driver db7d07b5add4 and its syscon/mux DTS integration from 126a1b3c61cb.

Upstream driver code after b49c70273801:

    if (dw->hdata->use_handshake_as_channel_number)
            dchan = dma_get_slave_channel(&chan->vc.chan);
    else
            dchan = dma_get_any_slave_channel(&dw->dma);

Downstream 6.12.y code:

    compatible = "sophgo,cv1800b-axi-dma",
                 "snps,axi-dma-1.01a";
    dchan = dma_get_any_slave_channel(&dw->dma);

Downstream falls back to the generic match and ignores the channel ID.

Maybe we should be dropping this or reverting this.

thanks,
Harshit

Quoted 3 lines
arch/riscv/boot/dts/sophgo/cv18xx.dtsi +0 -0
  			reg = <0x04330000 0x1000>;
  			interrupts = <29 IRQ_TYPE_LEVEL_HIGH>;
  			clocks = <&clk CLK_SDMA_AXI>, <&clk CLK_SDMA_AXI>;
#927 lore raw

Re: [PATCH 6.12 0889/1376] riscv: dts: sophgo: cv180x: Allow the DMA multiplexer to set channel number for DMA controller

Quoted 63 lines · Harshit Mogalapalli

Hi Greg/Sasha,

Quoted 31 lines · Greg Kroah-Hartman

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

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

From: Inochi Amaoto <[email protected]>

[ Upstream commit 5011466bade64483bb52bc4a926719d6794e6dab ]

Change the DMA controller compatible to the sophgo,cv1800b-axi-dma, which supports setting DMA channel number in DMA phandle args.

This dts change does not break backward compatibility as a fallback compatiable string is added.

---
  arch/riscv/boot/dts/sophgo/cv18xx.dtsi | 2 +-
  1 file changed, 1 insertion(+), 1 deletion(-)
arch/riscv/boot/dts/sophgo/cv18xx.dtsi +0 -0
index b8063ba6d6d7f..e519a72c093d1 100644

arch/riscv/boot/dts/sophgo/cv18xx.dtsi sdhci0: mmc@4310000 { · line 300

Quoted 4 lines
arch/riscv/boot/dts/sophgo/cv18xx.dtsi +1 -1
@@ -298,7 +298,7 @@ sdhci0: mmc@4310000 {
  		};
  		dmac: dma-controller@4330000 {
-			compatible = "snps,axi-dma-1.01a";
+			compatible = "sophgo,cv1800b-axi-dma", "snps,axi-dma-1.01a";

I ran an AI-assisted backport review, and I have verified the report. It looks valid to me.

The patch adds the Sophgo CV1800B DMA compatible, but 6.12 lacks the matching driver support from b49c70273801. The generic fallback therefore matches and still calls dma_get_any_slave_channel(), so the phandle channel number is not honored. The branch also lacks the CV1800B mux driver db7d07b5add4 and its syscon/mux DTS integration from 126a1b3c61cb.

Upstream driver code after b49c70273801:

    if (dw->hdata->use_handshake_as_channel_number)
            dchan = dma_get_slave_channel(&chan->vc.chan);
    else
            dchan = dma_get_any_slave_channel(&dw->dma);

Downstream 6.12.y code:

    compatible = "sophgo,cv1800b-axi-dma",
                 "snps,axi-dma-1.01a";
    dchan = dma_get_any_slave_channel(&dw->dma);
Quoted 11 lines

Downstream falls back to the generic match and ignores the channel ID.

Maybe we should be dropping this or reverting this.

thanks,
Harshit

Quoted 3 lines
arch/riscv/boot/dts/sophgo/cv18xx.dtsi +0 -0
  			reg = <0x04330000 0x1000>;
  			interrupts = <29 IRQ_TYPE_LEVEL_HIGH>;
  			clocks = <&clk CLK_SDMA_AXI>, <&clk CLK_SDMA_AXI>;

I think this fix does not change something as we already have the fallback compatiable, even if we does not backport the driver fix. As this does not change the behavior, I think this could be kept.

Regards,
Inochi

author maintainer
#928 lore raw

PATCH 890/1376 drm/omap: dsi: Do not copy isr table

In reply to Greg Kroah-Hartman

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

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

From: Andreas Kemnade <[email protected]>

[ Upstream commit 97c03b32b28a9f7f13f768f2b06e1eaafe850e66 ]

To be able to unregister stuff from isrs, the corresponding table was copied. Nobody seems to unregister stuff that way, so it does not help. But there are stack-allocated objects passed to these isrs giving chances of UAF of these objects if irqs are unregistered while they are handled, so better do not copy that table.

Fixes 4ae2ddddf44cd ("OMAP: DSS2: DSI: Add ISR support")
---
 drivers/gpu/drm/omapdrm/dss/dsi.c | 7 +------
 drivers/gpu/drm/omapdrm/dss/dsi.h | 2 --
 2 files changed, 1 insertion(+), 8 deletions(-)
drivers/gpu/drm/omapdrm/dss/dsi.c +1 -6
index ea63c64d3a1ab..b9b013ba80e2e 100644
@@ -455,15 +455,10 @@ static irqreturn_t omap_dsi_irq_handler(int irq, void *arg)
 		del_timer(&dsi->te_timer);
 #endif
 
-	/* make a copy and unlock, so that isrs can unregister
-	 * themselves */
-	memcpy(&dsi->isr_tables_copy, &dsi->isr_tables,
-		sizeof(dsi->isr_tables));
+	dsi_handle_isrs(&dsi->isr_tables, irqstatus, vcstatus, ciostatus);
 
 	spin_unlock(&dsi->irq_lock);
 
-	dsi_handle_isrs(&dsi->isr_tables_copy, irqstatus, vcstatus, ciostatus);
-
 	dsi_handle_irq_errors(dsi, irqstatus, vcstatus, ciostatus);
 
 	dsi_collect_irq_stats(dsi, irqstatus, vcstatus, ciostatus);
drivers/gpu/drm/omapdrm/dss/dsi.h +0 -2
index 601707c0ecc4e..2b25247ea8935 100644
@@ -379,8 +379,6 @@ struct dsi_data {
 
 	spinlock_t irq_lock;
 	struct dsi_isr_tables isr_tables;
-	/* space for a copy used by the interrupt handler */
-	struct dsi_isr_tables isr_tables_copy;
 
 	int update_vc;
 #ifdef DSI_PERF_MEASURE
Signature · 1 line
2.53.0
author maintainer
#929 lore raw

PATCH 891/1376 arm64: dts: qcom: agatti: Add missing CX power domain to DISPCC

In reply to Greg Kroah-Hartman

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

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

From: Imran Shaik <[email protected]>

[ Upstream commit 26d7b23caa4b1d8208e28c5981a85cf83e658e7c ]

Add the missing power-domains property to associate DISPCC with CX rail. This is to ensure the genpd performance state votes on the GDSC to get propagated to the CX rail and to avoid the rail under-voltage conditions.

---
 arch/arm64/boot/dts/qcom/qcm2290.dtsi | 1 +
 1 file changed, 1 insertion(+)
arch/arm64/boot/dts/qcom/qcm2290.dtsi +1 -0
index dbc78b2d5095a..d50b36143ebda 100644
@@ -1798,6 +1798,7 @@ dispcc: clock-controller@5f00000 {
 				      "gcc_disp_gpll0_div_clk_src",
 				      "dsi0_phy_pll_out_byteclk",
 				      "dsi0_phy_pll_out_dsiclk";
+			power-domains = <&rpmpd QCM2290_VDDCX>;
 			#power-domain-cells = <1>;
 			#clock-cells = <1>;
 			#reset-cells = <1>;
Signature · 1 line
2.53.0
author maintainer
#930 lore raw

PATCH 892/1376 remoteproc: Move resource table data structure to its own header

In reply to Greg Kroah-Hartman

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

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

From: Mukesh Ojha <[email protected]>

[ Upstream commit 0590420c2f90de497d342c9a41a618f46f4d09ab ]

The resource table data structure has traditionally been associated with the remoteproc framework, where the resource table is included as a section within the remote processor firmware binary. However, it is also possible to obtain the resource table through other means—such as from a reserved memory region populated by the boot firmware, statically maintained driver data, or via a secure SMC call—when it is not embedded in the firmware.

There are multiple Qualcomm remote processors (e.g., Venus, Iris, GPU, etc.) in the upstream kernel that do not use the remoteproc framework to manage their lifecycle for various reasons.

When Linux is running at EL2, similar to the Qualcomm PAS driver (qcom_q6v5_pas.c), client drivers for subsystems like video and GPU may also want to use the resource table SMC call to retrieve and map resources before they are used by the remote processor.

In such cases, the resource table data structure is no longer tightly coupled with the remoteproc headers. Client drivers that do not use the remoteproc framework should still be able to parse the resource table obtained through alternative means. Therefore, there is a need to decouple the resource table definitions from the remoteproc headers.

Stable-dep-of: bb840ea69347 ("remoteproc: fix OOB read via signed offset in rsc_table_for_each_entry()")

---
 include/linux/remoteproc.h | 269 +-------------------------------
 include/linux/rsc_table.h  | 306 +++++++++++++++++++++++++++++++++++++
 2 files changed, 307 insertions(+), 268 deletions(-)
 create mode 100644 include/linux/rsc_table.h
include/linux/remoteproc.h +1 -268
index 023fa91bd2a48..de98462d58889 100644
@@ -43,274 +43,7 @@
 #include <linux/completion.h>
 #include <linux/idr.h>
 #include <linux/of.h>
-
-/**
- * struct resource_table - firmware resource table header
- * @ver: version number
- * @num: number of resource entries
- * @reserved: reserved (must be zero)
- * @offset: array of offsets pointing at the various resource entries
- *
- * A resource table is essentially a list of system resources required
- * by the remote processor. It may also include configuration entries.
- * If needed, the remote processor firmware should contain this table
- * as a dedicated ".resource_table" ELF section.
- *
- * Some resources entries are mere announcements, where the host is informed
- * of specific remoteproc configuration. Other entries require the host to
- * do something (e.g. allocate a system resource). Sometimes a negotiation
- * is expected, where the firmware requests a resource, and once allocated,
- * the host should provide back its details (e.g. address of an allocated
- * memory region).
- *
- * The header of the resource table, as expressed by this structure,
- * contains a version number (should we need to change this format in the
- * future), the number of available resource entries, and their offsets
- * in the table.
- *
- * Immediately following this header are the resource entries themselves,
- * each of which begins with a resource entry header (as described below).
- */
-struct resource_table {
-	u32 ver;
-	u32 num;
-	u32 reserved[2];
-	u32 offset[];
-} __packed;
-
-/**
- * struct fw_rsc_hdr - firmware resource entry header
- * @type: resource type
- * @data: resource data
- *
- * Every resource entry begins with a 'struct fw_rsc_hdr' header providing
- * its @type. The content of the entry itself will immediately follow
- * this header, and it should be parsed according to the resource type.
- */
-struct fw_rsc_hdr {
-	u32 type;
-	u8 data[];
-} __packed;
-
-/**
- * enum fw_resource_type - types of resource entries
- *
- * @RSC_CARVEOUT:   request for allocation of a physically contiguous
- *		    memory region.
- * @RSC_DEVMEM:     request to iommu_map a memory-based peripheral.
- * @RSC_TRACE:	    announces the availability of a trace buffer into which
- *		    the remote processor will be writing logs.
- * @RSC_VDEV:       declare support for a virtio device, and serve as its
- *		    virtio header.
- * @RSC_LAST:       just keep this one at the end of standard resources
- * @RSC_VENDOR_START:	start of the vendor specific resource types range
- * @RSC_VENDOR_END:	end of the vendor specific resource types range
- *
- * For more details regarding a specific resource type, please see its
- * dedicated structure below.
- *
- * Please note that these values are used as indices to the rproc_handle_rsc
- * lookup table, so please keep them sane. Moreover, @RSC_LAST is used to
- * check the validity of an index before the lookup table is accessed, so
- * please update it as needed.
- */
-enum fw_resource_type {
-	RSC_CARVEOUT		= 0,
-	RSC_DEVMEM		= 1,
-	RSC_TRACE		= 2,
-	RSC_VDEV		= 3,
-	RSC_LAST		= 4,
-	RSC_VENDOR_START	= 128,
-	RSC_VENDOR_END		= 512,
-};
-
-#define FW_RSC_ADDR_ANY (-1)
-
-/**
- * struct fw_rsc_carveout - physically contiguous memory request
- * @da: device address
- * @pa: physical address
- * @len: length (in bytes)
- * @flags: iommu protection flags
- * @reserved: reserved (must be zero)
- * @name: human-readable name of the requested memory region
- *
- * This resource entry requests the host to allocate a physically contiguous
- * memory region.
- *
- * These request entries should precede other firmware resource entries,
- * as other entries might request placing other data objects inside
- * these memory regions (e.g. data/code segments, trace resource entries, ...).
- *
- * Allocating memory this way helps utilizing the reserved physical memory
- * (e.g. CMA) more efficiently, and also minimizes the number of TLB entries
- * needed to map it (in case @rproc is using an IOMMU). Reducing the TLB
- * pressure is important; it may have a substantial impact on performance.
- *
- * If the firmware is compiled with static addresses, then @da should specify
- * the expected device address of this memory region. If @da is set to
- * FW_RSC_ADDR_ANY, then the host will dynamically allocate it, and then
- * overwrite @da with the dynamically allocated address.
- *
- * We will always use @da to negotiate the device addresses, even if it
- * isn't using an iommu. In that case, though, it will obviously contain
- * physical addresses.
- *
- * Some remote processors needs to know the allocated physical address
- * even if they do use an iommu. This is needed, e.g., if they control
- * hardware accelerators which access the physical memory directly (this
- * is the case with OMAP4 for instance). In that case, the host will
- * overwrite @pa with the dynamically allocated physical address.
- * Generally we don't want to expose physical addresses if we don't have to
- * (remote processors are generally _not_ trusted), so we might want to
- * change this to happen _only_ when explicitly required by the hardware.
- *
- * @flags is used to provide IOMMU protection flags, and @name should
- * (optionally) contain a human readable name of this carveout region
- * (mainly for debugging purposes).
- */
-struct fw_rsc_carveout {
-	u32 da;
-	u32 pa;
-	u32 len;
-	u32 flags;
-	u32 reserved;
-	u8 name[32];
-} __packed;
-
-/**
- * struct fw_rsc_devmem - iommu mapping request
- * @da: device address
- * @pa: physical address
- * @len: length (in bytes)
- * @flags: iommu protection flags
- * @reserved: reserved (must be zero)
- * @name: human-readable name of the requested region to be mapped
- *
- * This resource entry requests the host to iommu map a physically contiguous
- * memory region. This is needed in case the remote processor requires
- * access to certain memory-based peripherals; _never_ use it to access
- * regular memory.
- *
- * This is obviously only needed if the remote processor is accessing memory
- * via an iommu.
- *
- * @da should specify the required device address, @pa should specify
- * the physical address we want to map, @len should specify the size of
- * the mapping and @flags is the IOMMU protection flags. As always, @name may
- * (optionally) contain a human readable name of this mapping (mainly for
- * debugging purposes).
- *
- * Note: at this point we just "trust" those devmem entries to contain valid
- * physical addresses, but this isn't safe and will be changed: eventually we
- * want remoteproc implementations to provide us ranges of physical addresses
- * the firmware is allowed to request, and not allow firmwares to request
- * access to physical addresses that are outside those ranges.
- */
-struct fw_rsc_devmem {
-	u32 da;
-	u32 pa;
-	u32 len;
-	u32 flags;
-	u32 reserved;
-	u8 name[32];
-} __packed;
-
-/**
- * struct fw_rsc_trace - trace buffer declaration
- * @da: device address
- * @len: length (in bytes)
- * @reserved: reserved (must be zero)
- * @name: human-readable name of the trace buffer
- *
- * This resource entry provides the host information about a trace buffer
- * into which the remote processor will write log messages.
- *
- * @da specifies the device address of the buffer, @len specifies
- * its size, and @name may contain a human readable name of the trace buffer.
- *
- * After booting the remote processor, the trace buffers are exposed to the
- * user via debugfs entries (called trace0, trace1, etc..).
- */
-struct fw_rsc_trace {
-	u32 da;
-	u32 len;
-	u32 reserved;
-	u8 name[32];
-} __packed;
-
-/**
- * struct fw_rsc_vdev_vring - vring descriptor entry
- * @da: device address
- * @align: the alignment between the consumer and producer parts of the vring
- * @num: num of buffers supported by this vring (must be power of two)
- * @notifyid: a unique rproc-wide notify index for this vring. This notify
- * index is used when kicking a remote processor, to let it know that this
- * vring is triggered.
- * @pa: physical address
- *
- * This descriptor is not a resource entry by itself; it is part of the
- * vdev resource type (see below).
- *
- * Note that @da should either contain the device address where
- * the remote processor is expecting the vring, or indicate that
- * dynamically allocation of the vring's device address is supported.
- */
-struct fw_rsc_vdev_vring {
-	u32 da;
-	u32 align;
-	u32 num;
-	u32 notifyid;
-	u32 pa;
-} __packed;
-
-/**
- * struct fw_rsc_vdev - virtio device header
- * @id: virtio device id (as in virtio_ids.h)
- * @notifyid: a unique rproc-wide notify index for this vdev. This notify
- * index is used when kicking a remote processor, to let it know that the
- * status/features of this vdev have changes.
- * @dfeatures: specifies the virtio device features supported by the firmware
- * @gfeatures: a place holder used by the host to write back the
- * negotiated features that are supported by both sides.
- * @config_len: the size of the virtio config space of this vdev. The config
- * space lies in the resource table immediate after this vdev header.
- * @status: a place holder where the host will indicate its virtio progress.
- * @num_of_vrings: indicates how many vrings are described in this vdev header
- * @reserved: reserved (must be zero)
- * @vring: an array of @num_of_vrings entries of 'struct fw_rsc_vdev_vring'.
- *
- * This resource is a virtio device header: it provides information about
- * the vdev, and is then used by the host and its peer remote processors
- * to negotiate and share certain virtio properties.
- *
- * By providing this resource entry, the firmware essentially asks remoteproc
- * to statically allocate a vdev upon registration of the rproc (dynamic vdev
- * allocation is not yet supported).
- *
- * Note:
- * 1. unlike virtualization systems, the term 'host' here means
- *    the Linux side which is running remoteproc to control the remote
- *    processors. We use the name 'gfeatures' to comply with virtio's terms,
- *    though there isn't really any virtualized guest OS here: it's the host
- *    which is responsible for negotiating the final features.
- *    Yeah, it's a bit confusing.
- *
- * 2. immediately following this structure is the virtio config space for
- *    this vdev (which is specific to the vdev; for more info, read the virtio
- *    spec). The size of the config space is specified by @config_len.
- */
-struct fw_rsc_vdev {
-	u32 id;
-	u32 notifyid;
-	u32 dfeatures;
-	u32 gfeatures;
-	u32 config_len;
-	u8 status;
-	u8 num_of_vrings;
-	u8 reserved[2];
-	struct fw_rsc_vdev_vring vring[];
-} __packed;
+#include <linux/rsc_table.h>
 
 struct rproc;
 
include/linux/rsc_table.h new file +306 -0

306 lines not on this page · raw message

Signature · 1 line
2.53.0
author maintainer
#931 lore raw

PATCH 893/1376 remoteproc: use rsc_table_for_each_entry() in rproc_handle_resources()

In reply to Greg Kroah-Hartman

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

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

From: Mukesh Ojha <[email protected]>

[ Upstream commit 49abb5d6e1ac8169cdfc0c3aa4408e0d90ee5696 ]

Replace the open-coded resource table iteration loop in rproc_handle_resources() with the rsc_table_for_each_entry() helper.

The remoteproc-specific dispatch logic (vendor resource handling via rproc_handle_rsc(), RSC_LAST bounds check, handler table lookup) is moved into a local callback rproc_handle_rsc_entry(), keeping the iteration mechanics in one canonical place.

The callback receives the payload offset within the table so that handlers which write back into the resource table (e.g. rproc_handle_carveout() recording a dynamically allocated address via rsc_offset) continue to work correctly.

No functional change.

Stable-dep-of: bb840ea69347 ("remoteproc: fix OOB read via signed offset in rsc_table_for_each_entry()")

---
 drivers/remoteproc/remoteproc_core.c | 81 +++++++++++++---------------
 include/linux/rsc_table.h            | 53 ++++++++++++++++++
 2 files changed, 91 insertions(+), 43 deletions(-)
drivers/remoteproc/remoteproc_core.c +38 -43
index 86dd92067da82..0c11a0b529086 100644
@@ -1016,60 +1016,55 @@ static rproc_handle_resource_t rproc_loading_handlers[RSC_LAST] = {
 	[RSC_VDEV] = rproc_handle_vdev,
 };
 
-/* handle firmware resource entries before booting the remote processor */
-static int rproc_handle_resources(struct rproc *rproc,
-				  rproc_handle_resource_t handlers[RSC_LAST])
+struct rproc_rsc_cb_data {
+	struct rproc *rproc;
+	rproc_handle_resource_t *handlers;
+};
+
+static int rproc_handle_rsc_entry(u32 type, void *rsc, int offset,
+				  int avail, void *data)
 {
+	struct rproc_rsc_cb_data *d = data;
+	struct rproc *rproc = d->rproc;
 	struct device *dev = &rproc->dev;
 	rproc_handle_resource_t handler;
-	int ret = 0, i;
-
-	if (!rproc->table_ptr)
-		return 0;
+	int ret;
 
-	for (i = 0; i < rproc->table_ptr->num; i++) {
-		int offset = rproc->table_ptr->offset[i];
-		struct fw_rsc_hdr *hdr = (void *)rproc->table_ptr + offset;
-		int avail = rproc->table_sz - offset - sizeof(*hdr);
-		void *rsc = (void *)hdr + sizeof(*hdr);
+	dev_dbg(dev, "rsc: type %d\n", type);
 
-		/* make sure table isn't truncated */
-		if (avail < 0) {
-			dev_err(dev, "rsc table is truncated\n");
-			return -EINVAL;
-		}
-
-		dev_dbg(dev, "rsc: type %d\n", hdr->type);
+	if (type >= RSC_VENDOR_START && type <= RSC_VENDOR_END) {
+		ret = rproc_handle_rsc(rproc, type, rsc, offset, avail);
+		if (ret == RSC_HANDLED)
+			return 0;
+		if (ret < 0)
+			return ret;
+		dev_warn(dev, "unsupported vendor resource %d\n", type);
+		return 0;
+	}
 
-		if (hdr->type >= RSC_VENDOR_START &&
-		    hdr->type <= RSC_VENDOR_END) {
-			ret = rproc_handle_rsc(rproc, hdr->type, rsc,
-					       offset + sizeof(*hdr), avail);
-			if (ret == RSC_HANDLED)
-				continue;
-			else if (ret < 0)
-				break;
+	if (type >= RSC_LAST) {
+		dev_warn(dev, "unsupported resource %d\n", type);
+		return 0;
+	}
 
-			dev_warn(dev, "unsupported vendor resource %d\n",
-				 hdr->type);
-			continue;
-		}
+	handler = d->handlers[type];
+	if (!handler)
+		return 0;
 
-		if (hdr->type >= RSC_LAST) {
-			dev_warn(dev, "unsupported resource %d\n", hdr->type);
-			continue;
-		}
+	return handler(rproc, rsc, offset, avail);
+}
 
-		handler = handlers[hdr->type];
-		if (!handler)
-			continue;
+/* handle firmware resource entries before booting the remote processor */
+static int rproc_handle_resources(struct rproc *rproc,
+				  rproc_handle_resource_t handlers[RSC_LAST])
+{
+	struct rproc_rsc_cb_data d = { .rproc = rproc, .handlers = handlers };
 
-		ret = handler(rproc, rsc, offset + sizeof(*hdr), avail);
-		if (ret)
-			break;
-	}
+	if (!rproc->table_ptr)
+		return 0;
 
-	return ret;
+	return rsc_table_for_each_entry(rproc->table_ptr, rproc->table_sz,
+					&rproc->dev, rproc_handle_rsc_entry, &d);
 }
 
 static int rproc_prepare_subdevices(struct rproc *rproc)
include/linux/rsc_table.h +53 -0

57 lines not on this page · raw message

Signature · 1 line
2.53.0
author maintainer
#932 lore raw

PATCH 894/1376 remoteproc: fix OOB read via signed offset in rsc_table_for_each_entry()

In reply to Greg Kroah-Hartman

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

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

From: Mukesh Ojha <[email protected]>

[ Upstream commit bb840ea69347aff7bde5a208e7b5b180669a7656 ]

table->offset[i] is a u32 from firmware, but was stored into a signed int. A crafted offset like 0xFFFFFFF0 becomes -16, placing hdr 16 bytes before the table buffer. The subsequent avail check was bypassed because the negative int was promoted to a large size_t in the expression "table_sz - offset - sizeof(*hdr)", yielding a large positive avail and letting the out-of-bounds hdr->type read proceed undetected.

Store the offset as u32 and validate it with unsigned comparisons before any pointer arithmetic.

Fixes fd2c15ec1dd3 ("remoteproc: resource table overhaul")
---
 include/linux/rsc_table.h | 17 +++++++++++------
 1 file changed, 11 insertions(+), 6 deletions(-)
include/linux/rsc_table.h +11 -6
index c6d6d553d8f11..4cef11a2e3a2c 100644
@@ -337,17 +337,22 @@ static inline int rsc_table_for_each_entry(struct resource_table *table,
 	int i, ret;
 
 	for (i = 0; i < table->num; i++) {
-		int offset = table->offset[i];
-		struct fw_rsc_hdr *hdr = (void *)table + offset;
-		int avail = table_sz - offset - sizeof(*hdr);
-		int rsc_offset = offset + sizeof(*hdr);
-		void *rsc = (void *)hdr + sizeof(*hdr);
+		u32 offset = table->offset[i];
+		struct fw_rsc_hdr *hdr;
+		int avail, rsc_offset;
+		void *rsc;
 
-		if (avail < 0) {
+		if (offset < sizeof(*table) || offset >= table_sz ||
+		    table_sz - offset < sizeof(*hdr)) {
 			dev_err(dev, "rsc table is truncated\n");
 			return -EINVAL;
 		}
 
+		hdr = (void *)table + offset;
+		avail = table_sz - offset - sizeof(*hdr);
+		rsc_offset = offset + sizeof(*hdr);
+		rsc = (void *)hdr + sizeof(*hdr);
+
 		ret = cb(hdr->type, rsc, rsc_offset, avail, data);
 		if (ret)
 			return ret;
Signature · 1 line
2.53.0
author maintainer
#933 lore raw

PATCH 895/1376 bpf, cgroup: Fix invalid storage access after __cgroup_bpf_attach failed

In reply to Greg Kroah-Hartman

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

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

From: Pu Lehui <[email protected]>

[ Upstream commit 6655c409707ec8ce9ce0850ffe4fe02331fd4d9c ]

A potential invalid storage access issue can occur after replacing a cgroup bpf prog.

This occurs in the following scenario:
1. prog1 with storage is attached to a cgroup in multi-attach mode.
2. prog1 is replaced with prog2 using BPF_F_REPLACE in multi-attach mode, but fails midway (e.g. in bpf_trampoline_link_cgroup_shim or update_effective_progs).
3. A new prog3 is attached to the cgroup in multi-attach mode.

The reason is that __cgroup_bpf_attach overwrites pl->storage with the new storage prior to attachment completion. When attachment fails midway, the cleanup path calls bpf_cgroup_storages_free(new_storage) to free the newly allocated storage, but fails to restore pl->storage back to old_storage.

Consequently, the still-active prog1 holds invalid or dangling storage pointers, leading to an invalid memory access when prog1 executes and calls bpf_get_local_storage. Additionally, original pl->flags and cgrp->bpf.flags[atype] are left unrestored.

Fix this by saving old_pl_flags, old_storage, and old_flags prior to the update, and properly restoring all of them in the cleanup path on error.

Fixes 7d9c3427894f ("bpf: Make cgroup storages shared between programs on the same cgroup")
---
 kernel/bpf/cgroup.c | 8 ++++++++
 1 file changed, 8 insertions(+)
kernel/bpf/cgroup.c +8 -0
index ad054a4c782ea..7b8b4e09131dd 100644
@@ -646,8 +646,10 @@ static int __cgroup_bpf_attach(struct cgroup *cgrp,
 	struct bpf_prog *old_prog = NULL;
 	struct bpf_cgroup_storage *storage[MAX_BPF_CGROUP_STORAGE_TYPE] = {};
 	struct bpf_cgroup_storage *new_storage[MAX_BPF_CGROUP_STORAGE_TYPE] = {};
+	struct bpf_cgroup_storage *old_storage[MAX_BPF_CGROUP_STORAGE_TYPE] = {};
 	struct bpf_prog *new_prog = prog ? : link->link.prog;
 	enum cgroup_bpf_attach_type atype;
+	u32 old_flags, old_pl_flags;
 	struct bpf_prog_list *pl;
 	struct hlist_head *progs;
 	int err;
@@ -693,6 +695,8 @@ static int __cgroup_bpf_attach(struct cgroup *cgrp,
 
 	if (pl) {
 		old_prog = pl->prog;
+		old_pl_flags = pl->flags;
+		bpf_cgroup_storages_assign(old_storage, pl->storage);
 	} else {
 		struct hlist_node *last = NULL;
 
@@ -716,6 +720,7 @@ static int __cgroup_bpf_attach(struct cgroup *cgrp,
 	pl->link = link;
 	pl->flags = flags;
 	bpf_cgroup_storages_assign(pl->storage, storage);
+	old_flags = cgrp->bpf.flags[atype];
 	cgrp->bpf.flags[atype] = saved_flags;
 
 	if (type == BPF_LSM_CGROUP) {
@@ -746,12 +751,15 @@ static int __cgroup_bpf_attach(struct cgroup *cgrp,
 	if (old_prog) {
 		pl->prog = old_prog;
 		pl->link = NULL;
+		pl->flags = old_pl_flags;
+		bpf_cgroup_storages_assign(pl->storage, old_storage);
 	}
 	bpf_cgroup_storages_free(new_storage);
 	if (!old_prog) {
 		hlist_del(&pl->node);
 		kfree(pl);
 	}
+	cgrp->bpf.flags[atype] = old_flags;
 	return err;
 }
 
Signature · 1 line
2.53.0
author maintainer
#934 lore raw

PATCH 896/1376 selftests/mm: add new test cases to the migration test

In reply to Greg Kroah-Hartman

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

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

From: Donet Tom <[email protected]>

[ Upstream commit 901083d8f5c55dc125834bbc5ac1720014e66758 ]

Added three new test cases to the migration tests:

1. Shared anon THP migration test
This test will mmap shared anon memory, madvise it to MADV_HUGEPAGE, then do migration entry testing. One thread will move pages back and forth between nodes whilst other threads try and access them.

2. Private anon hugetlb migration test
This test will mmap private anon hugetlb memory and then do the migration entry testing.

3. Shared anon hugetlb migration test
This test will mmap shared anon hugetlb memory and then do the migration entry testing.

Test results
============

 # ./tools/testing/selftests/mm/migration
 TAP version 13
 1..6
 # Starting 6 tests from 1 test cases.
 #  RUN           migration.private_anon ...
 #            OK  migration.private_anon
 ok 1 migration.private_anon
 #  RUN           migration.shared_anon ...
 #            OK  migration.shared_anon
 ok 2 migration.shared_anon
 #  RUN           migration.private_anon_thp ...
 #            OK  migration.private_anon_thp
 ok 3 migration.private_anon_thp
 #  RUN           migration.shared_anon_thp ...
 #            OK  migration.shared_anon_thp
 ok 4 migration.shared_anon_thp
 #  RUN           migration.private_anon_htlb ...
 #            OK  migration.private_anon_htlb
 ok 5 migration.private_anon_htlb
 #  RUN           migration.shared_anon_htlb ...
 #            OK  migration.shared_anon_htlb
 ok 6 migration.shared_anon_htlb
 # PASSED: 6 / 6 tests passed.
 # Totals: pass:6 fail:0 xfail:0 xpass:0 skip:0 error:0
 #

Stable-dep-of: 15828a150c58 ("selftests/mm: fix ksm NUMA merge test for systems with memoryless NUMA nodes")

---
 tools/testing/selftests/mm/migration.c | 99 ++++++++++++++++++++++++++
 1 file changed, 99 insertions(+)
tools/testing/selftests/mm/migration.c +99 -0
index c883ef420d3ba..cfd01e951e89c 100644
@@ -205,4 +205,103 @@ TEST_F_TIMEOUT(migration, private_anon_thp, 2*RUNTIME)
 		ASSERT_EQ(pthread_cancel(self->threads[i]), 0);
 }
 
+/*
+ * migration test with shared anon THP page
+ */
+
+TEST_F_TIMEOUT(migration, shared_anon_thp, 2*RUNTIME)
+{
+	pid_t pid;
+	uint64_t *ptr;
+	int i;
+
+	if (self->nthreads < 2 || self->n1 < 0 || self->n2 < 0)
+		SKIP(return, "Not enough threads or NUMA nodes available");
+
+	ptr = mmap(NULL, 2 * TWOMEG, PROT_READ | PROT_WRITE,
+		MAP_SHARED | MAP_ANONYMOUS, -1, 0);
+	ASSERT_NE(ptr, MAP_FAILED);
+
+	ptr = (uint64_t *) ALIGN((uintptr_t) ptr, TWOMEG);
+	ASSERT_EQ(madvise(ptr, TWOMEG, MADV_HUGEPAGE), 0);
+
+	memset(ptr, 0xde, TWOMEG);
+	for (i = 0; i < self->nthreads - 1; i++) {
+		pid = fork();
+		if (!pid) {
+			prctl(PR_SET_PDEATHSIG, SIGHUP);
+			/* Parent may have died before prctl so check now. */
+			if (getppid() == 1)
+				kill(getpid(), SIGHUP);
+			access_mem(ptr);
+		} else {
+			self->pids[i] = pid;
+		}
+	}
+
+	ASSERT_EQ(migrate(ptr, self->n1, self->n2), 0);
+	for (i = 0; i < self->nthreads - 1; i++)
+		ASSERT_EQ(kill(self->pids[i], SIGTERM), 0);
+}
+
+/*
+ * migration test with private anon hugetlb page
+ */
+TEST_F_TIMEOUT(migration, private_anon_htlb, 2*RUNTIME)
+{
+	uint64_t *ptr;
+	int i;
+
+	if (self->nthreads < 2 || self->n1 < 0 || self->n2 < 0)
+		SKIP(return, "Not enough threads or NUMA nodes available");
+
+	ptr = mmap(NULL, TWOMEG, PROT_READ | PROT_WRITE,
+		MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB, -1, 0);
+	ASSERT_NE(ptr, MAP_FAILED);
+
+	memset(ptr, 0xde, TWOMEG);
+	for (i = 0; i < self->nthreads - 1; i++)
+		if (pthread_create(&self->threads[i], NULL, access_mem, ptr))
+			perror("Couldn't create thread");
+
+	ASSERT_EQ(migrate(ptr, self->n1, self->n2), 0);
+	for (i = 0; i < self->nthreads - 1; i++)
+		ASSERT_EQ(pthread_cancel(self->threads[i]), 0);
+}
+
+/*
+ * migration test with shared anon hugetlb page
+ */
+TEST_F_TIMEOUT(migration, shared_anon_htlb, 2*RUNTIME)
+{
+	pid_t pid;
+	uint64_t *ptr;
+	int i;
+
+	if (self->nthreads < 2 || self->n1 < 0 || self->n2 < 0)
+		SKIP(return, "Not enough threads or NUMA nodes available");
+
+	ptr = mmap(NULL, TWOMEG, PROT_READ | PROT_WRITE,
+		MAP_SHARED | MAP_ANONYMOUS | MAP_HUGETLB, -1, 0);
+	ASSERT_NE(ptr, MAP_FAILED);
+
+	memset(ptr, 0xde, TWOMEG);
+	for (i = 0; i < self->nthreads - 1; i++) {
+		pid = fork();
+		if (!pid) {
+			prctl(PR_SET_PDEATHSIG, SIGHUP);
+			/* Parent may have died before prctl so check now. */
+			if (getppid() == 1)
+				kill(getpid(), SIGHUP);
+			access_mem(ptr);
+		} else {
+			self->pids[i] = pid;
+		}
+	}
+
+	ASSERT_EQ(migrate(ptr, self->n1, self->n2), 0);
+	for (i = 0; i < self->nthreads - 1; i++)
+		ASSERT_EQ(kill(self->pids[i], SIGTERM), 0);
+}
+
 TEST_HARNESS_MAIN
Signature · 1 line
2.53.0
author maintainer
#935 lore raw

PATCH 897/1376 ksm_tests: skip hugepage test when Transparent Hugepages are disabled

In reply to Greg Kroah-Hartman

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

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

From: Li Wang <[email protected]>

[ Upstream commit fdc3bc3497946c6b416a17628907581657102e60 ]

Some systems (e.g. minimal or real-time kernels) may not enable Transparent Hugepages (THP), causing MADV_HUGEPAGE to return EINVAL. This patch introduces a runtime check using the existing THP sysfs interface and skips the hugepage merging test (-H) when THP is not available.

To avoid those failures:

# -----------------------------

  # running ./ksm_tests -H -s 100
  # -----------------------------
  # ksm_tests: MADV_HUGEPAGE: Invalid argument
  # [FAIL]
  not ok 1 ksm_tests -H -s 100 # exit=2

# --------------------

  # running ./khugepaged
  # --------------------
  # Reading PMD pagesize failed# [FAIL]
  not ok 1 khugepaged # exit=1

# --------------------

  # running ./soft-dirty
  # --------------------
  # TAP version 13
  # 1..15
  # ok 1 Test test_simple
  # ok 2 Test test_vma_reuse dirty bit of allocated page
  # ok 3 Test test_vma_reuse dirty bit of reused address page
  # Bail out! Reading PMD pagesize failed# Planned tests != run tests (15 != 3)
  # # Totals: pass:3 fail:0 xfail:0 xpass:0 skip:0 error:0
  # [FAIL]
  not ok 1 soft-dirty # exit=1
  # SUMMARY: PASS=0 SKIP=0 FAIL=1

# -------------------

  # running ./migration
  # -------------------
  # TAP version 13
  # 1..3
  # # Starting 3 tests from 1 test cases.
  # #  RUN           migration.private_anon ...
  # #            OK  migration.private_anon
  # ok 1 migration.private_anon
  # #  RUN           migration.shared_anon ...
  # #            OK  migration.shared_anon
  # ok 2 migration.shared_anon
  # #  RUN           migration.private_anon_thp ...
  # # migration.c:196:private_anon_thp:Expected madvise(ptr, TWOMEG, MADV_HUGEPAGE) (-1) == 0 (0)
  # # private_anon_thp: Test terminated by assertion
  # #          FAIL  migration.private_anon_thp
  # not ok 3 migration.private_anon_thp
  # # FAILED: 2 / 3 tests passed.
  # # Totals: pass:2 fail:1 xfail:0 xpass:0 skip:0 error:0
  # [FAIL]
  not ok 1 migration # exit=1

It's true that CONFIG_TRANSPARENT_HUGEPAGE=y is explicitly enabled in tools/testing/selftests/mm/config, so ideally the runtime environment should also support THP.

However, in practice, we've found that on some systems:

- THP is disabled at boot time (transparent_hugepage=never)
- Or manually disabled via sysfs
- Or unavailable in RT kernels, containers, or minimal CI environments

In these cases, the test will fail with EINVAL on madvise(MADV_HUGEPAGE), even though the kernel config is correct.

To make the test suite more robust and avoid false negatives, this patch adds a runtime check for /sys/kernel/mm/transparent_hugepage/enabled.

If THP is not available, the hugepage test (-H) is skipped with a clear message.

Cc Aruna Ramakrishna <[email protected]>
Cc Bagas Sanjaya <[email protected]>
Cc Catalin Marinas <[email protected]>
Cc Dave Hansen <[email protected]>
Cc David Hildenbrand <[email protected]>
Cc Joey Gouly <[email protected]>
Cc Johannes Weiner <[email protected]>
Cc Keith Lucas <[email protected]>
Cc Ryan Roberts <[email protected]>
Cc Shuah Khan <[email protected]>
Cc Lorenzo Stoakes <[email protected]>

Stable-dep-of: 15828a150c58 ("selftests/mm: fix ksm NUMA merge test for systems with memoryless NUMA nodes")

---
 tools/testing/selftests/mm/khugepaged.c   |  5 +++++
 tools/testing/selftests/mm/ksm_tests.c    |  6 ++++++
 tools/testing/selftests/mm/migration.c    |  8 ++++++++
 tools/testing/selftests/mm/soft-dirty.c   |  9 ++++++++-
 tools/testing/selftests/mm/thp_settings.c | 11 +++++++++++
 tools/testing/selftests/mm/thp_settings.h |  2 ++
 6 files changed, 40 insertions(+), 1 deletion(-)
tools/testing/selftests/mm/khugepaged.c +5 -0
index 8a4d34cce36b2..6822eb7ea58e8 100644
@@ -1190,6 +1190,11 @@ int main(int argc, char **argv)
 		.read_ahead_kb = 0,
 	};
 
+	if (!thp_is_enabled()) {
+		printf("Transparent Hugepages not available\n");
+		return KSFT_SKIP;
+	}
+
 	parse_test_type(argc, argv);
 
 	setbuf(stdout, NULL);
tools/testing/selftests/mm/ksm_tests.c +6 -0
index b748c48908d9d..faa3d053994df 100644
@@ -15,6 +15,7 @@
 #include "../kselftest.h"
 #include <include/vdso/time64.h>
 #include "vm_util.h"
+#include "thp_settings.h"
 
 #define KSM_SYSFS_PATH "/sys/kernel/mm/ksm/"
 #define KSM_FP(s) (KSM_SYSFS_PATH s)
@@ -555,6 +556,11 @@ static int ksm_merge_hugepages_time(int merge_type, int mapping, int prot,
 	unsigned long scan_time_ns;
 	int pagemap_fd, n_normal_pages, n_huge_pages;
 
+	if (!thp_is_enabled()) {
+		printf("Transparent Hugepages not available\n");
+		return KSFT_SKIP;
+	}
+
 	map_size *= MB;
 	size_t len = map_size;
 
tools/testing/selftests/mm/migration.c +8 -0
index cfd01e951e89c..7ad742c8c1325 100644
@@ -5,6 +5,8 @@
  */
 
 #include "../kselftest_harness.h"
+#include "thp_settings.h"
+
 #include <strings.h>
 #include <pthread.h>
 #include <numa.h>
@@ -186,6 +188,9 @@ TEST_F_TIMEOUT(migration, private_anon_thp, 2*RUNTIME)
 	uint64_t *ptr;
 	int i;
 
+	if (!thp_is_enabled())
+		SKIP(return, "Transparent Hugepages not available");
+
 	if (self->nthreads < 2 || self->n1 < 0 || self->n2 < 0)
 		SKIP(return, "Not enough threads or NUMA nodes available");
 
@@ -215,6 +220,9 @@ TEST_F_TIMEOUT(migration, shared_anon_thp, 2*RUNTIME)
 	uint64_t *ptr;
 	int i;
 
+	if (!thp_is_enabled())
+		SKIP(return, "Transparent Hugepages not available");
+
 	if (self->nthreads < 2 || self->n1 < 0 || self->n2 < 0)
 		SKIP(return, "Not enough threads or NUMA nodes available");
 
tools/testing/selftests/mm/soft-dirty.c +8 -1
index 7b91df12ce5b9..ca65ff3096b25 100644
@@ -6,8 +6,10 @@
 #include <stdint.h>
 #include <malloc.h>
 #include <sys/mman.h>
+
 #include "../kselftest.h"
 #include "vm_util.h"
+#include "thp_settings.h"
 
 #define PAGEMAP_FILE_PATH "/proc/self/pagemap"
 #define TEST_ITERATIONS 10000
@@ -78,8 +80,13 @@ static void test_hugepage(int pagemap_fd, int pagesize)
 {
 	char *map;
 	int i, ret;
-	size_t hpage_len = read_pmd_pagesize();
 
+	if (!thp_is_enabled()) {
+		ksft_test_result_skip("Transparent Hugepages not available\n");
+		return;
+	}
+
+	size_t hpage_len = read_pmd_pagesize();
 	if (!hpage_len)
 		ksft_exit_fail_msg("Reading PMD pagesize failed");
 
tools/testing/selftests/mm/thp_settings.c +11 -0
index 577eaab6266fd..38f70155bfbf1 100644
@@ -381,3 +381,14 @@ unsigned long thp_shmem_supported_orders(void)
 {
 	return __thp_supported_orders(true);
 }
+
+bool thp_is_enabled(void)
+{
+	if (access(THP_SYSFS, F_OK) != 0)
+		return false;
+
+	int mode = thp_read_string("enabled", thp_enabled_strings);
+
+	/* THP is considered enabled if it's either "always" or "madvise" */
+	return mode == 1 || mode == 3;
+}
tools/testing/selftests/mm/thp_settings.h +2 -0
index 876235a23460c..079a58a662b0e 100644
@@ -84,4 +84,6 @@ void thp_set_read_ahead_path(char *path);
 unsigned long thp_supported_orders(void);
 unsigned long thp_shmem_supported_orders(void);
 
+bool thp_is_enabled(void);
+
 #endif /* __THP_SETTINGS_H__ */
Signature · 1 line
2.53.0
author maintainer
#936 lore raw

PATCH 898/1376 selftests/mm: ksm_tests: use kselftest framework

In reply to Greg Kroah-Hartman

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

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

From: Mike Rapoport (Microsoft) <[email protected]>

[ Upstream commit 4cc68d5f52de9614e2db70f43c52f012d1ebb5ad ]

Convert ksm_tests to use kselftest framework for reporting and tracking successful and failing runs.

Cc Baolin Wang <[email protected]>
Cc Barry Song <[email protected]>
Cc David Hildenbrand <[email protected]>
Cc Dev Jain <[email protected]>
Cc Jason Gunthorpe <[email protected]>
Cc John Hubbard <[email protected]>
Cc Lance Yang <[email protected]>
Cc Leon Romanovsky <[email protected]>
Cc Liam Howlett <[email protected]>
Cc Li Wang <[email protected]>
Cc Lorenzo Stoakes <[email protected]>
Cc Michal Hocko <[email protected]>
Cc Nico Pache <[email protected]>
Cc Peter Xu <[email protected]>
Cc Ryan Roberts <[email protected]>
Cc Shuah Khan <[email protected]>
Cc Suren Baghdasaryan <[email protected]>
Cc Vlastimil Babka <[email protected]>
Cc Zi Yan <[email protected]>

Stable-dep-of: 15828a150c58 ("selftests/mm: fix ksm NUMA merge test for systems with memoryless NUMA nodes")

---
 tools/testing/selftests/mm/ksm_tests.c | 180 +++++++++++--------------
 1 file changed, 81 insertions(+), 99 deletions(-)
tools/testing/selftests/mm/ksm_tests.c +81 -99
index faa3d053994df..d99a6bff6344c 100644
@@ -203,12 +203,12 @@ static void  *allocate_memory(void *ptr, int prot, int mapping, char data, size_
 	void *map_ptr = mmap(ptr, map_size, PROT_WRITE, mapping, -1, 0);
 
 	if (!map_ptr) {
-		perror("mmap");
+		ksft_perror("mmap");
 		return NULL;
 	}
 	memset(map_ptr, data, map_size);
 	if (mprotect(map_ptr, map_size, prot)) {
-		perror("mprotect");
+		ksft_perror("mprotect");
 		munmap(map_ptr, map_size);
 		return NULL;
 	}
@@ -229,11 +229,11 @@ static int ksm_do_scan(int scan_count, struct timespec start_time, int timeout)
 		if (ksm_read_sysfs(KSM_FP("full_scans"), &cur_scan))
 			return 1;
 		if (clock_gettime(CLOCK_MONOTONIC_RAW, &cur_time)) {
-			perror("clock_gettime");
+			ksft_perror("clock_gettime");
 			return 1;
 		}
 		if ((cur_time.tv_sec - start_time.tv_sec) > timeout) {
-			printf("Scan time limit exceeded\n");
+			ksft_print_msg("Scan time limit exceeded\n");
 			return 1;
 		}
 	}
@@ -246,12 +246,12 @@ static int ksm_merge_pages(int merge_type, void *addr, size_t size,
 {
 	if (merge_type == KSM_MERGE_MADVISE) {
 		if (madvise(addr, size, MADV_MERGEABLE)) {
-			perror("madvise");
+			ksft_perror("madvise");
 			return 1;
 		}
 	} else if (merge_type == KSM_MERGE_PRCTL) {
 		if (prctl(PR_SET_MEMORY_MERGE, 1, 0, 0, 0)) {
-			perror("prctl");
+			ksft_perror("prctl");
 			return 1;
 		}
 	}
@@ -270,7 +270,7 @@ static int ksm_unmerge_pages(void *addr, size_t size,
 			     struct timespec start_time, int timeout)
 {
 	if (madvise(addr, size, MADV_UNMERGEABLE)) {
-		perror("madvise");
+		ksft_perror("madvise");
 		return 1;
 	}
 	return 0;
@@ -352,7 +352,7 @@ static int check_ksm_merge(int merge_type, int mapping, int prot,
 	struct timespec start_time;
 
 	if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) {
-		perror("clock_gettime");
+		ksft_perror("clock_gettime");
 		return KSFT_FAIL;
 	}
 
@@ -366,7 +366,6 @@ static int check_ksm_merge(int merge_type, int mapping, int prot,
 
 	/* verify that the right number of pages are merged */
 	if (assert_ksm_pages_count(page_count)) {
-		printf("OK\n");
 		munmap(map_ptr, page_size * page_count);
 		if (merge_type == KSM_MERGE_PRCTL)
 			prctl(PR_SET_MEMORY_MERGE, 0, 0, 0, 0);
@@ -374,7 +373,6 @@ static int check_ksm_merge(int merge_type, int mapping, int prot,
 	}
 
 err_out:
-	printf("Not OK\n");
 	munmap(map_ptr, page_size * page_count);
 	return KSFT_FAIL;
 }
@@ -386,7 +384,7 @@ static int check_ksm_unmerge(int merge_type, int mapping, int prot, int timeout,
 	int page_count = 2;
 
 	if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) {
-		perror("clock_gettime");
+		ksft_perror("clock_gettime");
 		return KSFT_FAIL;
 	}
 
@@ -408,13 +406,11 @@ static int check_ksm_unmerge(int merge_type, int mapping, int prot, int timeout,
 
 	/* check that unmerging was successful and 0 pages are currently merged */
 	if (assert_ksm_pages_count(0)) {
-		printf("OK\n");
 		munmap(map_ptr, page_size * page_count);
 		return KSFT_PASS;
 	}
 
 err_out:
-	printf("Not OK\n");
 	munmap(map_ptr, page_size * page_count);
 	return KSFT_FAIL;
 }
@@ -426,7 +422,7 @@ static int check_ksm_zero_page_merge(int merge_type, int mapping, int prot, long
 	struct timespec start_time;
 
 	if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) {
-		perror("clock_gettime");
+		ksft_perror("clock_gettime");
 		return KSFT_FAIL;
 	}
 
@@ -453,12 +449,10 @@ static int check_ksm_zero_page_merge(int merge_type, int mapping, int prot, long
 	else if (!use_zero_pages && !assert_ksm_pages_count(page_count))
 		goto err_out;
 
-	printf("OK\n");
 	munmap(map_ptr, page_size * page_count);
 	return KSFT_PASS;
 
 err_out:
-	printf("Not OK\n");
 	munmap(map_ptr, page_size * page_count);
 	return KSFT_FAIL;
 }
@@ -493,16 +487,16 @@ static int check_ksm_numa_merge(int merge_type, int mapping, int prot, int timeo
 	int first_node;
 
 	if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) {
-		perror("clock_gettime");
+		ksft_perror("clock_gettime");
 		return KSFT_FAIL;
 	}
 
 	if (numa_available() < 0) {
-		perror("NUMA support not enabled");
+		ksft_print_msg("NUMA support not enabled\n");
 		return KSFT_SKIP;
 	}
 	if (numa_num_configured_nodes() <= 1) {
-		printf("At least 2 NUMA nodes must be available\n");
+		ksft_print_msg("At least 2 NUMA nodes must be available\n");
 		return KSFT_SKIP;
 	}
 	if (ksm_write_sysfs(KSM_FP("merge_across_nodes"), merge_across_nodes))
@@ -513,7 +507,7 @@ static int check_ksm_numa_merge(int merge_type, int mapping, int prot, int timeo
 	numa1_map_ptr = numa_alloc_onnode(page_size, first_node);
 	numa2_map_ptr = numa_alloc_onnode(page_size, get_next_mem_node(first_node));
 	if (!numa1_map_ptr || !numa2_map_ptr) {
-		perror("numa_alloc_onnode");
+		ksft_perror("numa_alloc_onnode");
 		return KSFT_FAIL;
 	}
 
@@ -538,13 +532,11 @@ static int check_ksm_numa_merge(int merge_type, int mapping, int prot, int timeo
 
 	numa_free(numa1_map_ptr, page_size);
 	numa_free(numa2_map_ptr, page_size);
-	printf("OK\n");
 	return KSFT_PASS;
 
 err_out:
 	numa_free(numa1_map_ptr, page_size);
 	numa_free(numa2_map_ptr, page_size);
-	printf("Not OK\n");
 	return KSFT_FAIL;
 }
 
@@ -557,7 +549,7 @@ static int ksm_merge_hugepages_time(int merge_type, int mapping, int prot,
 	int pagemap_fd, n_normal_pages, n_huge_pages;
 
 	if (!thp_is_enabled()) {
-		printf("Transparent Hugepages not available\n");
+		ksft_print_msg("Transparent Hugepages not available\n");
 		return KSFT_SKIP;
 	}
 
@@ -587,36 +579,35 @@ static int ksm_merge_hugepages_time(int merge_type, int mapping, int prot,
 		else
 			n_huge_pages++;
 	}
-	printf("Number of normal pages:    %d\n", n_normal_pages);
-	printf("Number of huge pages:    %d\n", n_huge_pages);
+	ksft_print_msg("Number of normal pages:    %d\n", n_normal_pages);
+	ksft_print_msg("Number of huge pages:    %d\n", n_huge_pages);
 
 	memset(map_ptr, '*', len);
 
 	if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) {
-		perror("clock_gettime");
+		ksft_perror("clock_gettime");
 		goto err_out;
 	}
 	if (ksm_merge_pages(merge_type, map_ptr, map_size, start_time, timeout))
 		goto err_out;
 	if (clock_gettime(CLOCK_MONOTONIC_RAW, &end_time)) {
-		perror("clock_gettime");
+		ksft_perror("clock_gettime");
 		goto err_out;
 	}
 
 	scan_time_ns = (end_time.tv_sec - start_time.tv_sec) * NSEC_PER_SEC +
 		       (end_time.tv_nsec - start_time.tv_nsec);
 
-	printf("Total size:    %lu MiB\n", map_size / MB);
-	printf("Total time:    %ld.%09ld s\n", scan_time_ns / NSEC_PER_SEC,
+	ksft_print_msg("Total size:    %lu MiB\n", map_size / MB);
+	ksft_print_msg("Total time:    %ld.%09ld s\n", scan_time_ns / NSEC_PER_SEC,
 	       scan_time_ns % NSEC_PER_SEC);
-	printf("Average speed:  %.3f MiB/s\n", (map_size / MB) /
+	ksft_print_msg("Average speed:  %.3f MiB/s\n", (map_size / MB) /
 					       ((double)scan_time_ns / NSEC_PER_SEC));
 
 	munmap(map_ptr_orig, len + HPAGE_SIZE);
 	return KSFT_PASS;
 
 err_out:
-	printf("Not OK\n");
 	munmap(map_ptr_orig, len + HPAGE_SIZE);
 	return KSFT_FAIL;
 }
@@ -634,30 +625,29 @@ static int ksm_merge_time(int merge_type, int mapping, int prot, int timeout, si
 		return KSFT_FAIL;
 
 	if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) {
-		perror("clock_gettime");
+		ksft_perror("clock_gettime");
 		goto err_out;
 	}
 	if (ksm_merge_pages(merge_type, map_ptr, map_size, start_time, timeout))
 		goto err_out;
 	if (clock_gettime(CLOCK_MONOTONIC_RAW, &end_time)) {
-		perror("clock_gettime");
+		ksft_perror("clock_gettime");
 		goto err_out;
 	}
 
 	scan_time_ns = (end_time.tv_sec - start_time.tv_sec) * NSEC_PER_SEC +
 		       (end_time.tv_nsec - start_time.tv_nsec);
 
-	printf("Total size:    %lu MiB\n", map_size / MB);
-	printf("Total time:    %ld.%09ld s\n", scan_time_ns / NSEC_PER_SEC,
+	ksft_print_msg("Total size:    %lu MiB\n", map_size / MB);
+	ksft_print_msg("Total time:    %ld.%09ld s\n", scan_time_ns / NSEC_PER_SEC,
 	       scan_time_ns % NSEC_PER_SEC);
-	printf("Average speed:  %.3f MiB/s\n", (map_size / MB) /
+	ksft_print_msg("Average speed:  %.3f MiB/s\n", (map_size / MB) /
 					       ((double)scan_time_ns / NSEC_PER_SEC));
 
 	munmap(map_ptr, map_size);
 	return KSFT_PASS;
 
 err_out:
-	printf("Not OK\n");
 	munmap(map_ptr, map_size);
 	return KSFT_FAIL;
 }
@@ -674,37 +664,36 @@ static int ksm_unmerge_time(int merge_type, int mapping, int prot, int timeout,
 	if (!map_ptr)
 		return KSFT_FAIL;
 	if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) {
-		perror("clock_gettime");
+		ksft_perror("clock_gettime");
 		goto err_out;
 	}
 	if (ksm_merge_pages(merge_type, map_ptr, map_size, start_time, timeout))
 		goto err_out;
 
 	if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) {
-		perror("clock_gettime");
+		ksft_perror("clock_gettime");
 		goto err_out;
 	}
 	if (ksm_unmerge_pages(map_ptr, map_size, start_time, timeout))
 		goto err_out;
 	if (clock_gettime(CLOCK_MONOTONIC_RAW, &end_time)) {
-		perror("clock_gettime");
+		ksft_perror("clock_gettime");
 		goto err_out;
 	}
 
 	scan_time_ns = (end_time.tv_sec - start_time.tv_sec) * NSEC_PER_SEC +
 		       (end_time.tv_nsec - start_time.tv_nsec);
 
-	printf("Total size:    %lu MiB\n", map_size / MB);
-	printf("Total time:    %ld.%09ld s\n", scan_time_ns / NSEC_PER_SEC,
+	ksft_print_msg("Total size:    %lu MiB\n", map_size / MB);
+	ksft_print_msg("Total time:    %ld.%09ld s\n", scan_time_ns / NSEC_PER_SEC,
 	       scan_time_ns % NSEC_PER_SEC);
-	printf("Average speed:  %.3f MiB/s\n", (map_size / MB) /
+	ksft_print_msg("Average speed:  %.3f MiB/s\n", (map_size / MB) /
 					       ((double)scan_time_ns / NSEC_PER_SEC));
 
 	munmap(map_ptr, map_size);
 	return KSFT_PASS;
 
 err_out:
-	printf("Not OK\n");
 	munmap(map_ptr, map_size);
 	return KSFT_FAIL;
 }
@@ -723,24 +712,24 @@ static int ksm_cow_time(int merge_type, int mapping, int prot, int timeout, size
 		return KSFT_FAIL;
 
 	if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) {
-		perror("clock_gettime");
+		ksft_perror("clock_gettime");
 		return KSFT_FAIL;
 	}
 	for (size_t i = 0; i < page_count - 1; i = i + 2)
 		memset(map_ptr + page_size * i, '-', 1);
 	if (clock_gettime(CLOCK_MONOTONIC_RAW, &end_time)) {
-		perror("clock_gettime");
+		ksft_perror("clock_gettime");
 		return KSFT_FAIL;
 	}
 
 	cow_time_ns = (end_time.tv_sec - start_time.tv_sec) * NSEC_PER_SEC +
 		       (end_time.tv_nsec - start_time.tv_nsec);
 
-	printf("Total size:    %lu MiB\n\n", (page_size * page_count) / MB);
-	printf("Not merged pages:\n");
-	printf("Total time:     %ld.%09ld s\n", cow_time_ns / NSEC_PER_SEC,
+	ksft_print_msg("Total size:    %lu MiB\n\n", (page_size * page_count) / MB);
+	ksft_print_msg("Not merged pages:\n");
+	ksft_print_msg("Total time:     %ld.%09ld s\n", cow_time_ns / NSEC_PER_SEC,
 	       cow_time_ns % NSEC_PER_SEC);
-	printf("Average speed:  %.3f MiB/s\n\n", ((page_size * (page_count / 2)) / MB) /
+	ksft_print_msg("Average speed:  %.3f MiB/s\n\n", ((page_size * (page_count / 2)) / MB) /
 					       ((double)cow_time_ns / NSEC_PER_SEC));
 
 	/* Create 2000 pairs of duplicate pages */
@@ -752,30 +741,29 @@ static int ksm_cow_time(int merge_type, int mapping, int prot, int timeout, size
 		goto err_out;
 
 	if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) {
-		perror("clock_gettime");
+		ksft_perror("clock_gettime");
 		goto err_out;
 	}
 	for (size_t i = 0; i < page_count - 1; i = i + 2)
 		memset(map_ptr + page_size * i, '-', 1);
 	if (clock_gettime(CLOCK_MONOTONIC_RAW, &end_time)) {
-		perror("clock_gettime");
+		ksft_perror("clock_gettime");
 		goto err_out;
 	}
 
 	cow_time_ns = (end_time.tv_sec - start_time.tv_sec) * NSEC_PER_SEC +
 		       (end_time.tv_nsec - start_time.tv_nsec);
 
-	printf("Merged pages:\n");
-	printf("Total time:     %ld.%09ld s\n", cow_time_ns / NSEC_PER_SEC,
+	ksft_print_msg("Merged pages:\n");
+	ksft_print_msg("Total time:     %ld.%09ld s\n", cow_time_ns / NSEC_PER_SEC,
 	       cow_time_ns % NSEC_PER_SEC);
-	printf("Average speed:  %.3f MiB/s\n", ((page_size * (page_count / 2)) / MB) /
+	ksft_print_msg("Average speed:  %.3f MiB/s\n", ((page_size * (page_count / 2)) / MB) /
 					       ((double)cow_time_ns / NSEC_PER_SEC));
 
 	munmap(map_ptr, page_size * page_count);
 	return KSFT_PASS;
 
 err_out:
-	printf("Not OK\n");
 	munmap(map_ptr, page_size * page_count);
 	return KSFT_FAIL;
 }
@@ -793,6 +781,10 @@ int main(int argc, char *argv[])
 	bool use_zero_pages = KSM_USE_ZERO_PAGES_DEFAULT;
 	bool merge_across_nodes = KSM_MERGE_ACROSS_NODES_DEFAULT;
 	long size_MB = 0;
+	const char *test_descr = "KSM merging";
+
+	ksft_print_header();
+	ksft_set_plan(1);
 
 	while ((opt = getopt(argc, argv, "dha:p:l:z:m:s:t:MUZNPCHD")) != -1) {
 		switch (opt) {
@@ -801,17 +793,13 @@ int main(int argc, char *argv[])
 			break;
 		case 'p':
 			page_count = atol(optarg);
-			if (page_count <= 0) {
-				printf("The number of pages must be greater than 0\n");
-				return KSFT_FAIL;
-			}
+			if (page_count <= 0)
+				ksft_exit_fail_msg("The number of pages must be greater than 0\n");
 			break;
 		case 'l':
 			ksm_scan_limit_sec = atoi(optarg);
-			if (ksm_scan_limit_sec <= 0) {
-				printf("Timeout value must be greater than 0\n");
-				return KSFT_FAIL;
-			}
+			if (ksm_scan_limit_sec <= 0)
+				ksft_exit_fail_msg("Timeout value must be greater than 0\n");
 			break;
 		case 'h':
 			print_help();
@@ -833,19 +821,15 @@ int main(int argc, char *argv[])
 			break;
 		case 's':
 			size_MB = atoi(optarg);
-			if (size_MB <= 0) {
-				printf("Size must be greater than 0\n");
-				return KSFT_FAIL;
-			}
+			if (size_MB <= 0)
+				ksft_exit_fail_msg("Size must be greater than 0\n");
 			break;
 		case 't':
 			{
 				int tmp = atoi(optarg);
 
-				if (tmp < 0 || tmp > KSM_MERGE_LAST) {
-					printf("Invalid merge type\n");
-					return KSFT_FAIL;
-				}
+				if (tmp < 0 || tmp > KSM_MERGE_LAST)
+					ksft_exit_fail_msg("Invalid merge type\n");
 				merge_type = tmp;
 			}
 			break;
@@ -873,82 +857,80 @@ int main(int argc, char *argv[])
 			test_name = KSM_COW_TIME;
 			break;
 		default:
-			return KSFT_FAIL;
+			ksft_exit_fail_msg("Unknown option\n");
 		}
 	}
 
 	if (prot == 0)
 		prot = str_to_prot(KSM_PROT_STR_DEFAULT);
 
-	if (access(KSM_SYSFS_PATH, F_OK)) {
-		printf("Config KSM not enabled\n");
-		return KSFT_SKIP;
-	}
+	if (access(KSM_SYSFS_PATH, F_OK))
+		ksft_exit_skip("Config KSM not enabled\n");
 
-	if (ksm_save_def(&ksm_sysfs_old)) {
-		printf("Cannot save default tunables\n");
-		return KSFT_FAIL;
-	}
+	if (ksm_save_def(&ksm_sysfs_old))
+		ksft_exit_fail_msg("Cannot save default tunables\n");
 
 	if (ksm_write_sysfs(KSM_FP("run"), 2) ||
 	    ksm_write_sysfs(KSM_FP("sleep_millisecs"), 0) ||
 	    numa_available() ? 0 :
 		ksm_write_sysfs(KSM_FP("merge_across_nodes"), 1) ||
 	    ksm_write_sysfs(KSM_FP("pages_to_scan"), page_count))
-		return KSFT_FAIL;
+		ksft_exit_fail_msg("Cannot set up KSM tunables\n");
 
 	switch (test_name) {
 	case CHECK_KSM_MERGE:
+		test_descr = "KSM merging";
 		ret = check_ksm_merge(merge_type, MAP_PRIVATE | MAP_ANONYMOUS, prot, page_count,
 				      ksm_scan_limit_sec, page_size);
 		break;
 	case CHECK_KSM_UNMERGE:
+		test_descr = "KSM unmerging";
 		ret = check_ksm_unmerge(merge_type, MAP_PRIVATE | MAP_ANONYMOUS, prot,
 					ksm_scan_limit_sec, page_size);
 		break;
 	case CHECK_KSM_ZERO_PAGE_MERGE:
+		test_descr = "KSM zero page merging";
 		ret = check_ksm_zero_page_merge(merge_type, MAP_PRIVATE | MAP_ANONYMOUS, prot,
 						page_count, ksm_scan_limit_sec, use_zero_pages,
 						page_size);
 		break;
 	case CHECK_KSM_NUMA_MERGE:
+		test_descr = "KSM NUMA merging";
 		ret = check_ksm_numa_merge(merge_type, MAP_PRIVATE | MAP_ANONYMOUS, prot,
 					ksm_scan_limit_sec, merge_across_nodes, page_size);
 		break;
 	case KSM_MERGE_TIME:
-		if (size_MB == 0) {
-			printf("Option '-s' is required.\n");
-			return KSFT_FAIL;
-		}
+		if (size_MB == 0)
+			ksft_exit_fail_msg("Option '-s' is required\n");
+		test_descr = "KSM merge time";
 		ret = ksm_merge_time(merge_type, MAP_PRIVATE | MAP_ANONYMOUS, prot,
 				ksm_scan_limit_sec, size_MB);
 		break;
 	case KSM_MERGE_TIME_HUGE_PAGES:
-		if (size_MB == 0) {
-			printf("Option '-s' is required.\n");
-			return KSFT_FAIL;
-		}
+		if (size_MB == 0)
+			ksft_exit_fail_msg("Option '-s' is required\n");
+		test_descr = "KSM merge time with huge pages";
 		ret = ksm_merge_hugepages_time(merge_type, MAP_PRIVATE | MAP_ANONYMOUS, prot,
 				ksm_scan_limit_sec, size_MB);
 		break;
 	case KSM_UNMERGE_TIME:
-		if (size_MB == 0) {
-			printf("Option '-s' is required.\n");
-			return KSFT_FAIL;
-		}
+		if (size_MB == 0)
+			ksft_exit_fail_msg("Option '-s' is required\n");
+		test_descr = "KSM unmerge time";
 		ret = ksm_unmerge_time(merge_type, MAP_PRIVATE | MAP_ANONYMOUS, prot,
 				       ksm_scan_limit_sec, size_MB);
 		break;
 	case KSM_COW_TIME:
+		test_descr = "KSM COW time";
 		ret = ksm_cow_time(merge_type, MAP_PRIVATE | MAP_ANONYMOUS, prot,
 				ksm_scan_limit_sec, page_size);
 		break;
 	}
 
-	if (ksm_restore(&ksm_sysfs_old)) {
-		printf("Cannot restore default tunables\n");
-		return KSFT_FAIL;
-	}
+	if (ksm_restore(&ksm_sysfs_old))
+		ksft_print_msg("Cannot restore default tunables\n");
+
+	ksft_test_result_report(ret, "%s\n", test_descr);
 
-	return ret;
+	ksft_finished();
 }
Signature · 1 line
2.53.0
author maintainer
#937 lore raw

PATCH 899/1376 selftests/mm: fix ksm NUMA merge test for systems with memoryless NUMA nodes

In reply to Greg Kroah-Hartman

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

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

From: Sayali Patil <[email protected]>

[ Upstream commit 15828a150c5806869b7feb9e38ad2c0284fbf18a ]

The KSM NUMA merge test allocates identical pages on different NUMA nodes and verifies KSM behavior with merge_across_nodes enabled and disabled.

On systems with memoryless NUMA nodes, for example:
#numactl -H

      available: 2 nodes (0,4)
      .....
      node 0 cpus: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
      node 0 size: 14825 MB
      node 0 free: 1382 MB
      node 4 cpus:
      node 4 size: 0 MB
      node 4 free: 0 MB

the test may attempt to allocate memory on a node without memory, causing numa_alloc_onnode() to fail and resulting in a spurious test failure.

The test currently checks numa_num_configured_nodes() to determine whether sufficient NUMA nodes are available. However, configured nodes do not necessarily have memory.

Reuse the existing get_first_mem_node() and get_next_mem_node() helpers to locate NUMA nodes that actually contain memory, and skip the test when fewer than two such nodes are available.

Before patch:
---------------------------
running ./ksm_tests -N -m 1
---------------------------
mbind: Invalid argument
ok 1 KSM NUMA merging
Totals: pass:1 fail:0 xfail:0 xpass:0 skip:0 error:0 [PASS]
ok 1 ksm_tests -N -m 1
---------------------------
running ./ksm_tests -N -m 0
---------------------------
mbind: Invalid argument
not ok 1 KSM NUMA merging
Totals: pass:0 fail:1 xfail:0 xpass:0 skip:0 error:0 [FAIL]
not ok 2 ksm_tests -N -m 0 # exit=1

After patch:
---------------------------
running ./ksm_tests -N -m 1
---------------------------
At least 2 NUMA nodes with memory must be available ok 1
SKIP KSM NUMA merging
Totals: pass:0 fail:0 xfail:0 xpass:0 skip:1 error:0 [PASS]
ok 1 ksm_tests -N -m 1
---------------------------
running ./ksm_tests -N -m 0
---------------------------
At least 2 NUMA nodes with memory must be available ok 1
SKIP KSM NUMA merging
Totals: pass:0 fail:0 xfail:0 xpass:0 skip:1 error:0 [PASS]
ok 2 ksm_tests -N -m 0

Fixes e3820ab252dd ("selftest/vm: fix ksm selftest to run with different NUMA topologies")
Cc Dev Jain <[email protected]>
Cc Liam Howlett <[email protected]>
Cc Miaohe Lin <[email protected]>
Cc Michal Hocko <[email protected]>
Cc Oscar Salvador <[email protected]>
Cc "Ritesh Harjani (IBM)" <[email protected]>
Cc Shuah Khan <[email protected]>
Cc Zi Yan <[email protected]>
---
 tools/testing/selftests/mm/ksm_tests.c | 16 +++++++++-------
 1 file changed, 9 insertions(+), 7 deletions(-)
tools/testing/selftests/mm/ksm_tests.c +9 -7
index d99a6bff6344c..57f9cba1b6802 100644
@@ -468,9 +468,9 @@ static int get_next_mem_node(int node)
 		mem_node = i % (max_node + 1);
 		node_size = numa_node_size(mem_node, NULL);
 		if (node_size > 0)
-			break;
+			return mem_node;
 	}
-	return mem_node;
+	return -ENODEV;
 }
 
 static int get_first_mem_node(void)
@@ -483,8 +483,8 @@ static int check_ksm_numa_merge(int merge_type, int mapping, int prot, int timeo
 {
 	void *numa1_map_ptr, *numa2_map_ptr;
 	struct timespec start_time;
+	int first_node, second_node;
 	int page_count = 2;
-	int first_node;
 
 	if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) {
 		ksft_perror("clock_gettime");
@@ -495,17 +495,19 @@ static int check_ksm_numa_merge(int merge_type, int mapping, int prot, int timeo
 		ksft_print_msg("NUMA support not enabled\n");
 		return KSFT_SKIP;
 	}
-	if (numa_num_configured_nodes() <= 1) {
-		ksft_print_msg("At least 2 NUMA nodes must be available\n");
+	first_node = get_first_mem_node();
+	second_node = get_next_mem_node(first_node);
+
+	if (second_node < 0) {
+		ksft_print_msg("At least 2 NUMA nodes with memory must be available\n");
 		return KSFT_SKIP;
 	}
 	if (ksm_write_sysfs(KSM_FP("merge_across_nodes"), merge_across_nodes))
 		return KSFT_FAIL;
 
 	/* allocate 2 pages in 2 different NUMA nodes and fill them with the same data */
-	first_node = get_first_mem_node();
 	numa1_map_ptr = numa_alloc_onnode(page_size, first_node);
-	numa2_map_ptr = numa_alloc_onnode(page_size, get_next_mem_node(first_node));
+	numa2_map_ptr = numa_alloc_onnode(page_size, second_node);
 	if (!numa1_map_ptr || !numa2_map_ptr) {
 		ksft_perror("numa_alloc_onnode");
 		return KSFT_FAIL;
Signature · 1 line
2.53.0
author maintainer
#938 lore raw

PATCH 900/1376 selftests/mm: fix ternary operator precedence in ksm_tests

In reply to Greg Kroah-Hartman

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

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

From: Sayali Patil <[email protected]>

[ Upstream commit 4e1fbffb3333626682a011db7c4b4e9e40ba96d4 ]

The KSM selftest uses conditional expressions to skip accesses to merge_across_nodes on systems without NUMA support. However, the ternary operator is combined with logical OR without parentheses:

a || numa_available() ? 0 : b || c

Due to operator precedence rules, this is parsed as:

(a || numa_available()) ? 0 : (b || c)

instead of the intended:

a || (numa_available() ? 0 : b) || c

Add parentheses around the conditional expressions to ensure the correct evaluation order.

Fixes 9aa1af954db0 ("selftests: vm: check numa_available() before operating "merge_across_nodes" in ksm_tests")
Cc Dev Jain <[email protected]>
Cc Liam Howlett <[email protected]>
Cc Miaohe Lin <[email protected]>
Cc Michal Hocko <[email protected]>
Cc Oscar Salvador <[email protected]>
Cc "Ritesh Harjani (IBM)" <[email protected]>
Cc Shuah Khan <[email protected]>
Cc Zi Yan <[email protected]>
---
 tools/testing/selftests/mm/ksm_tests.c | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)
tools/testing/selftests/mm/ksm_tests.c +6 -6
index 57f9cba1b6802..ecb4b1057c6b3 100644
@@ -316,8 +316,8 @@ static bool assert_ksm_pages_count(long dupl_page_count)
 static int ksm_save_def(struct ksm_sysfs *ksm_sysfs)
 {
 	if (ksm_read_sysfs(KSM_FP("max_page_sharing"), &ksm_sysfs->max_page_sharing) ||
-	    numa_available() ? 0 :
-		ksm_read_sysfs(KSM_FP("merge_across_nodes"), &ksm_sysfs->merge_across_nodes) ||
+	    (numa_available() ? 0 :
+		ksm_read_sysfs(KSM_FP("merge_across_nodes"), &ksm_sysfs->merge_across_nodes)) ||
 	    ksm_read_sysfs(KSM_FP("sleep_millisecs"), &ksm_sysfs->sleep_millisecs) ||
 	    ksm_read_sysfs(KSM_FP("pages_to_scan"), &ksm_sysfs->pages_to_scan) ||
 	    ksm_read_sysfs(KSM_FP("run"), &ksm_sysfs->run) ||
@@ -332,8 +332,8 @@ static int ksm_save_def(struct ksm_sysfs *ksm_sysfs)
 static int ksm_restore(struct ksm_sysfs *ksm_sysfs)
 {
 	if (ksm_write_sysfs(KSM_FP("max_page_sharing"), ksm_sysfs->max_page_sharing) ||
-	    numa_available() ? 0 :
-		ksm_write_sysfs(KSM_FP("merge_across_nodes"), ksm_sysfs->merge_across_nodes) ||
+	    (numa_available() ? 0 :
+		ksm_write_sysfs(KSM_FP("merge_across_nodes"), ksm_sysfs->merge_across_nodes)) ||
 	    ksm_write_sysfs(KSM_FP("pages_to_scan"), ksm_sysfs->pages_to_scan) ||
 	    ksm_write_sysfs(KSM_FP("run"), ksm_sysfs->run) ||
 	    ksm_write_sysfs(KSM_FP("sleep_millisecs"), ksm_sysfs->sleep_millisecs) ||
@@ -874,8 +874,8 @@ int main(int argc, char *argv[])
 
 	if (ksm_write_sysfs(KSM_FP("run"), 2) ||
 	    ksm_write_sysfs(KSM_FP("sleep_millisecs"), 0) ||
-	    numa_available() ? 0 :
-		ksm_write_sysfs(KSM_FP("merge_across_nodes"), 1) ||
+	    (numa_available() ? 0 :
+		ksm_write_sysfs(KSM_FP("merge_across_nodes"), 1)) ||
 	    ksm_write_sysfs(KSM_FP("pages_to_scan"), page_count))
 		ksft_exit_fail_msg("Cannot set up KSM tunables\n");
 
Signature · 1 line
2.53.0
author maintainer
#939 lore raw

PATCH 901/1376 arm64: dts: qcom: qcs8550-aim300: Fix swapped USB QMP PHY vdda-phy/vdda-pll supplies

In reply to Greg Kroah-Hartman

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

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

From: Manivannan Sadhasivam <[email protected]>

[ Upstream commit c0ea3b6339881c2a5fff0830557b1279d9681036 ]

The QMP PHY expects the vdda-phy supply to be around 0.88V and the vdda-pll supply to be 1.2V. But these two supplies are swapped for the USB QMP PHY on this board, feeding 1.2V to vdda-phy and 0.88V to vdda-pll.

Fix it by swapping the two supplies back.

Fixes 0b12da4e28d8 ("arm64: dts: qcom: add base AIM300 dtsi")
Assisted-by Claude:opus-4-8
---
 arch/arm64/boot/dts/qcom/qcs8550-aim300.dtsi | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
arch/arm64/boot/dts/qcom/qcs8550-aim300.dtsi +2 -2
index e6ac529e6b721..3ead078246f48 100644
@@ -396,8 +396,8 @@ &usb_1_hsphy {
 };
 
 &usb_dp_qmpphy {
-	vdda-phy-supply = <&vreg_l3e_1p2>;
-	vdda-pll-supply = <&vreg_l3f_0p88>;
+	vdda-phy-supply = <&vreg_l3f_0p88>;
+	vdda-pll-supply = <&vreg_l3e_1p2>;
 };
 
 &xo_board {
Signature · 1 line
2.53.0
author maintainer
#940 lore raw

PATCH 902/1376 arm64: dts: qcom: sm7225-fairphone-fp4: Fix swapped USB QMP PHY vdda-phy/vdda-pll supplies

In reply to Greg Kroah-Hartman

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

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

From: Manivannan Sadhasivam <[email protected]>

[ Upstream commit a3b3a060b696d964c769a7d9dc5835c79e4fe964 ]

The QMP PHY expects the vdda-phy supply to be around 0.88V and the vdda-pll supply to be 1.2V. But these two supplies are swapped for the USB QMP PHY on this board, feeding 1.2V to vdda-phy and 0.9V to vdda-pll.

Fix it by swapping the two supplies back.

Fixes 4cbea668767d ("arm64: dts: qcom: sm7225: Add device tree for Fairphone 4")
Assisted-by Claude:opus-4-8
---
 arch/arm64/boot/dts/qcom/sm7225-fairphone-fp4.dts | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
arch/arm64/boot/dts/qcom/sm7225-fairphone-fp4.dts +2 -2
index 2a7c5a33b5dda..dfa3b111ae10f 100644
@@ -1143,8 +1143,8 @@ &usb_1_hsphy {
 };
 
 &usb_1_qmpphy {
-	vdda-phy-supply = <&vreg_l22a>;
-	vdda-pll-supply = <&vreg_l16a>;
+	vdda-phy-supply = <&vreg_l16a>;
+	vdda-pll-supply = <&vreg_l22a>;
 
 	status = "okay";
 };
Signature · 1 line
2.53.0
author maintainer
#941 lore raw

PATCH 903/1376 scripts/tags.sh: Prevent binary files appearing in cscope.files

In reply to Greg Kroah-Hartman

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

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

From: Sergei Litvin <[email protected]>

[ Upstream commit a9b93c34625a27bed5dc0f80ee2a359ceb955172 ]

When executing the command make COMPILED_SOURCE=1 cscope, the resulting cscope.files file contains filenames with the extensions *.rlib, *.rmeta, and *.so.

To fix this, modify the regular expression in the all_compiled_sources() function so that only files with the extensions *.h, *.c, *.S, and *.rs are accepted.

The issue has been introduced by commit 4f491bb6ea2a ("scripts/tags.sh:
collect compiled source precisely") which implemented the parsing of compiled sources from *.cmd files instead of using the "find" command.

Fixes 4f491bb6ea2a ("scripts/tags.sh: collect compiled source precisely")
[nsc: cleaned-up commit message line breaks and removed cc trailers]
---
 scripts/tags.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
scripts/tags.sh +1 -1
index 191e0461d6d5b..4c194ebf7a70d 100755
@@ -100,7 +100,7 @@ all_compiled_sources()
 	{
 		echo include/generated/autoconf.h
 		find $ignore -name "*.cmd" -exec \
-			grep -Poh '(?<=^  )\S+|(?<== )\S+[^\\](?=$)' {} \+ |
+			grep -Poh '(?<=^  )\S+\.([chS]|rs)(?=\s)|(?<== )\S+\.(?1)(?=$)' {} \+ |
 		awk '!a[$0]++'
 	} | xargs realpath -esq $([ -z "$KBUILD_ABS_SRCTREE" ] && echo --relative-to=.) |
 	sort -u
Signature · 1 line
2.53.0
author maintainer
#942 lore raw

PATCH 904/1376 modpost: prevent leak when early return no suffix .o in read_symbols()

In reply to Greg Kroah-Hartman

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

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

From: Robertus Diawan Chris <[email protected]>

[ Upstream commit 9a5b76027ed91680e10f90111d9cba300f96d1ab ]

The allocation for elf info symsearch and hdr from parse_elf() haven't been released when return because of modname didn't have suffix ".o". And it seems like the suffix ".o" check did not depends on parse_elf() to succeed first. So, move the suffix ".o" check before checking parse_elf() result to prevent resource leak when the modname didn't have suffix ".o" and return early.

This is reported by Coverity Scan as "Resource leak".

---
 scripts/mod/modpost.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)
scripts/mod/modpost.c +3 -3
index 07e8a4eb71d04..f17350bf3ddb4 100644
@@ -1567,14 +1567,14 @@ static void read_symbols(const char *modname)
 	struct elf_info info = { };
 	Elf_Sym *sym;
 
-	if (!parse_elf(&info, modname))
-		return;
-
 	if (!strends(modname, ".o")) {
 		error("%s: filename must be suffixed with .o\n", modname);
 		return;
 	}
 
+	if (!parse_elf(&info, modname))
+		return;
+
 	/* strip trailing .o */
 	mod = new_module(modname, strlen(modname) - strlen(".o"));
 
Signature · 1 line
2.53.0
author maintainer
#943 lore raw

PATCH 905/1376 RDMA/erdma: Hold CQ references when processing EQ events

In reply to Greg Kroah-Hartman

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

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

From: Cheng Xu <[email protected]>

[ Upstream commit 98df2aee1459ee1c62c70cbe9b370d2a532aea36 ]

EQ handlers look up CQs from dev->cq_xa and invoke CQ completion or error callbacks outside the xarray lock. erdma_destroy_cq() can erase the CQ from the xarray and free its queue buffer and doorbell record while a previously scheduled EQ handler is still using the CQ.

Add a CQ refcount and take a reference under the xarray lock with refcount_inc_not_zero(). Remove the CQ from the xarray before dropping the destroy-path reference, then wait for in-flight EQ users before releasing CQ resources.

Fixes 155055771704 ("RDMA/erdma: Add verbs implementation")
---
 drivers/infiniband/hw/erdma/erdma_eq.c    |  6 ++++--
 drivers/infiniband/hw/erdma/erdma_verbs.c | 12 +++++++++--
 drivers/infiniband/hw/erdma/erdma_verbs.h | 25 +++++++++++++++++++++--
 3 files changed, 37 insertions(+), 6 deletions(-)
drivers/infiniband/hw/erdma/erdma_eq.c +4 -2
index 5610e7f4c6bf7..6bffbc1b543be 100644
@@ -52,7 +52,7 @@ void erdma_aeq_event_handler(struct erdma_dev *dev)
 		if (FIELD_GET(ERDMA_AEQE_HDR_TYPE_MASK,
 			      le32_to_cpu(aeqe->hdr)) == ERDMA_AE_TYPE_CQ_ERR) {
 			cqn = le32_to_cpu(aeqe->event_data0);
-			cq = find_cq_by_cqn(dev, cqn);
+			cq = erdma_cq_get_by_cqn(dev, cqn);
 			if (!cq)
 				continue;
 
@@ -62,6 +62,7 @@ void erdma_aeq_event_handler(struct erdma_dev *dev)
 			if (cq->ibcq.event_handler)
 				cq->ibcq.event_handler(&event,
 						       cq->ibcq.cq_context);
+			erdma_cq_put(cq);
 		} else {
 			qpn = le32_to_cpu(aeqe->event_data0);
 			qp = find_qp_by_qpn(dev, qpn);
@@ -157,7 +158,7 @@ void erdma_ceq_completion_handler(struct erdma_eq_cb *ceq_cb)
 		poll_cnt++;
 		cqn = FIELD_GET(ERDMA_CEQE_HDR_CQN_MASK, READ_ONCE(*ceqe));
 
-		cq = find_cq_by_cqn(dev, cqn);
+		cq = erdma_cq_get_by_cqn(dev, cqn);
 		if (!cq)
 			continue;
 
@@ -166,6 +167,7 @@ void erdma_ceq_completion_handler(struct erdma_eq_cb *ceq_cb)
 
 		if (cq->ibcq.comp_handler)
 			cq->ibcq.comp_handler(&cq->ibcq, cq->ibcq.cq_context);
+		erdma_cq_put(cq);
 	}
 
 	notify_eq(&ceq_cb->eq);
drivers/infiniband/hw/erdma/erdma_verbs.c +10 -2
index 3a65cbe5839ad..c9d454813b1f0 100644
@@ -1278,6 +1278,7 @@ int erdma_destroy_cq(struct ib_cq *ibcq, struct ib_udata *udata)
 	struct erdma_dev *dev = to_edev(ibcq->device);
 	struct erdma_ucontext *ctx = rdma_udata_to_drv_context(
 		udata, struct erdma_ucontext, ibucontext);
+	unsigned long flags;
 	int err;
 	struct erdma_cmdq_destroy_cq_req req;
 
@@ -1292,6 +1293,13 @@ int erdma_destroy_cq(struct ib_cq *ibcq, struct ib_udata *udata)
 				       "failed to destroy CQ %u: %d\n",
 				       cq->cqn, err);
 
+	xa_lock_irqsave(&dev->cq_xa, flags);
+	__xa_erase(&dev->cq_xa, cq->cqn);
+	xa_unlock_irqrestore(&dev->cq_xa, flags);
+
+	erdma_cq_put(cq);
+	wait_for_completion(&cq->free);
+
 	if (rdma_is_kernel_res(&cq->ibcq.res)) {
 		dma_free_coherent(&dev->pdev->dev, cq->depth << CQE_SHIFT,
 				  cq->kern_cq.qbuf, cq->kern_cq.qbuf_dma_addr);
@@ -1302,8 +1310,6 @@ int erdma_destroy_cq(struct ib_cq *ibcq, struct ib_udata *udata)
 		put_mtt_entries(dev, &cq->user_cq.qbuf_mem);
 	}
 
-	xa_erase(&dev->cq_xa, cq->cqn);
-
 	return 0;
 }
 
@@ -1932,6 +1938,8 @@ int erdma_create_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr,
 	cq->ibcq.cqe = depth;
 	cq->depth = depth;
 	cq->assoc_eqn = attr->comp_vector + 1;
+	refcount_set(&cq->refcount, 1);
+	init_completion(&cq->free);
 
 	ret = xa_alloc_cyclic(&dev->cq_xa, &cq->cqn, cq,
 			      XA_LIMIT(1, dev->attrs.max_cq - 1),
drivers/infiniband/hw/erdma/erdma_verbs.h +23 -2
index f9408ccc8bad0..22ceb5fe7ca2a 100644
@@ -7,6 +7,9 @@
 #ifndef __ERDMA_VERBS_H__
 #define __ERDMA_VERBS_H__
 
+#include <linux/completion.h>
+#include <linux/refcount.h>
+
 #include "erdma.h"
 
 /* RDMA Capability. */
@@ -341,6 +344,8 @@ struct erdma_cq {
 
 	u32 depth;
 	u32 assoc_eqn;
+	refcount_t refcount;
+	struct completion free;
 
 	union {
 		struct erdma_kcq_info kern_cq;
@@ -355,9 +360,25 @@ static inline struct erdma_qp *find_qp_by_qpn(struct erdma_dev *dev, int id)
 	return (struct erdma_qp *)xa_load(&dev->qp_xa, id);
 }
 
-static inline struct erdma_cq *find_cq_by_cqn(struct erdma_dev *dev, int id)
+static inline struct erdma_cq *erdma_cq_get_by_cqn(struct erdma_dev *dev,
+						   int id)
+{
+	struct erdma_cq *cq;
+	unsigned long flags;
+
+	xa_lock_irqsave(&dev->cq_xa, flags);
+	cq = xa_load(&dev->cq_xa, id);
+	if (cq && !refcount_inc_not_zero(&cq->refcount))
+		cq = NULL;
+	xa_unlock_irqrestore(&dev->cq_xa, flags);
+
+	return cq;
+}
+
+static inline void erdma_cq_put(struct erdma_cq *cq)
 {
-	return (struct erdma_cq *)xa_load(&dev->cq_xa, id);
+	if (refcount_dec_and_test(&cq->refcount))
+		complete(&cq->free);
 }
 
 void erdma_qp_get(struct erdma_qp *qp);
Signature · 1 line
2.53.0
author maintainer
#944 lore raw

PATCH 906/1376 RDMA/erdma: Hold QP references for AE and CM processing

In reply to Greg Kroah-Hartman

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

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

From: Cheng Xu <[email protected]>

[ Upstream commit a52eeff32024f190b3bdc99088c7becccd4fa60b ]

AE QP fatal events and iWARP CM paths load QPs from dev->qp_xa and then use or reference them outside the xarray lock. erdma_destroy_qp() can drop the destroy-path reference and free QP resources while such a lookup is in flight.

Add erdma_qp_get_by_qpn() to acquire a kref under the xarray lock with kref_get_unless_zero(). Remove the QP from the xarray before dropping the destroy-path reference so no new lookup can acquire it while destruction waits for existing users.

Fixes 155055771704 ("RDMA/erdma: Add verbs implementation")
---
 drivers/infiniband/hw/erdma/erdma_cm.c    |  6 ++----
 drivers/infiniband/hw/erdma/erdma_eq.c    |  3 ++-
 drivers/infiniband/hw/erdma/erdma_verbs.c |  6 +++++-
 drivers/infiniband/hw/erdma/erdma_verbs.h | 15 +++++++++++++++
 4 files changed, 24 insertions(+), 6 deletions(-)
drivers/infiniband/hw/erdma/erdma_cm.c +2 -4
index e0acc185e7193..f0c8ebf62a057 100644
@@ -1021,10 +1021,9 @@ int erdma_connect(struct iw_cm_id *id, struct iw_cm_conn_param *params)
 	if (laddr->sa_family != AF_INET || raddr->sa_family != AF_INET)
 		return -EAFNOSUPPORT;
 
-	qp = find_qp_by_qpn(dev, params->qpn);
+	qp = erdma_qp_get_by_qpn(dev, params->qpn);
 	if (!qp)
 		return -ENOENT;
-	erdma_qp_get(qp);
 
 	ret = sock_create(AF_INET, SOCK_STREAM, IPPROTO_TCP, &s);
 	if (ret < 0)
@@ -1154,10 +1153,9 @@ int erdma_accept(struct iw_cm_id *id, struct iw_cm_conn_param *params)
 		return -ECONNRESET;
 	}
 
-	qp = find_qp_by_qpn(dev, params->qpn);
+	qp = erdma_qp_get_by_qpn(dev, params->qpn);
 	if (!qp)
 		return -ENOENT;
-	erdma_qp_get(qp);
 
 	down_write(&qp->state_lock);
 	if (qp->attrs.iwarp.state > ERDMA_QPS_IWARP_RTR) {
drivers/infiniband/hw/erdma/erdma_eq.c +2 -1
index 6bffbc1b543be..e4161e85422c6 100644
@@ -65,7 +65,7 @@ void erdma_aeq_event_handler(struct erdma_dev *dev)
 			erdma_cq_put(cq);
 		} else {
 			qpn = le32_to_cpu(aeqe->event_data0);
-			qp = find_qp_by_qpn(dev, qpn);
+			qp = erdma_qp_get_by_qpn(dev, qpn);
 			if (!qp)
 				continue;
 
@@ -75,6 +75,7 @@ void erdma_aeq_event_handler(struct erdma_dev *dev)
 			if (qp->ibqp.event_handler)
 				qp->ibqp.event_handler(&event,
 						       qp->ibqp.qp_context);
+			erdma_qp_put(qp);
 		}
 	}
 
drivers/infiniband/hw/erdma/erdma_verbs.c +5 -1
index c9d454813b1f0..057efff532b4f 100644
@@ -1321,6 +1321,7 @@ int erdma_destroy_qp(struct ib_qp *ibqp, struct ib_udata *udata)
 		udata, struct erdma_ucontext, ibucontext);
 	struct erdma_cmdq_destroy_qp_req req;
 	union erdma_mod_qp_params params;
+	unsigned long flags;
 	int err;
 
 	down_write(&qp->state_lock);
@@ -1348,6 +1349,10 @@ int erdma_destroy_qp(struct ib_qp *ibqp, struct ib_udata *udata)
 				       "failed to destroy QP %u: %d\n",
 				       QP_ID(qp), err);
 
+	xa_lock_irqsave(&dev->qp_xa, flags);
+	__xa_erase(&dev->qp_xa, QP_ID(qp));
+	xa_unlock_irqrestore(&dev->qp_xa, flags);
+
 	erdma_qp_put(qp);
 	wait_for_completion(&qp->safe_free);
 
@@ -1361,7 +1366,6 @@ int erdma_destroy_qp(struct ib_qp *ibqp, struct ib_udata *udata)
 
 	if (qp->cep)
 		erdma_cep_put(qp->cep);
-	xa_erase(&dev->qp_xa, QP_ID(qp));
 
 	return 0;
 }
drivers/infiniband/hw/erdma/erdma_verbs.h +15 -0
index 22ceb5fe7ca2a..a752bf7c4c1ec 100644
@@ -360,6 +360,21 @@ static inline struct erdma_qp *find_qp_by_qpn(struct erdma_dev *dev, int id)
 	return (struct erdma_qp *)xa_load(&dev->qp_xa, id);
 }
 
+static inline struct erdma_qp *erdma_qp_get_by_qpn(struct erdma_dev *dev,
+						   int id)
+{
+	struct erdma_qp *qp;
+	unsigned long flags;
+
+	xa_lock_irqsave(&dev->qp_xa, flags);
+	qp = xa_load(&dev->qp_xa, id);
+	if (qp && !kref_get_unless_zero(&qp->ref))
+		qp = NULL;
+	xa_unlock_irqrestore(&dev->qp_xa, flags);
+
+	return qp;
+}
+
 static inline struct erdma_cq *erdma_cq_get_by_cqn(struct erdma_dev *dev,
 						   int id)
 {
Signature · 1 line
2.53.0
author maintainer
#945 lore raw

PATCH 907/1376 RDMA/srp: fix heap information leak on a truncated SRP_CRED_REQ

In reply to Greg Kroah-Hartman

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

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

From: Yehyeong Lee <[email protected]>

[ Upstream commit 961ac0f0c5e414abdd6b33fae84b311d9fde0bd0 ]

srp_recv_done() passes wc->byte_len to srp_process_rsp(). It passes nothing to srp_process_cred_req() and srp_process_aer_req(), which read fixed-size fields from the receive buffer without checking that those fields were received.

The buffer size is max_ti_iu_len, which comes from the login response and is not validated. A target that advertises 8 and then sends an 8-byte SRP_CRED_REQ makes the initiator read req->tag from beyond the end of the buffer. req->tag is copied into the SRP_CRED_RSP and sent back, so those bytes reach the target. SRP_AER_REQ behaves the same way and also reads req->lun.

The leak is 8 bytes per response. max_ti_iu_len also decides which slab cache the buffer comes from. With 8 the buffer is a kmalloc-8 object and the read is entirely outside it:

  BUG: KASAN: slab-out-of-bounds in srp_recv_done+0x172b/0x1aa0
  Read of size 8 at addr ffff888104714da8 by task kworker/u8:3/50
   which belongs to the cache kmalloc-8 of size 8
  The buggy address is located 0 bytes to the right of
   allocated 8-byte region [ffff888104714da0, ffff888104714da8)

Without KASAN the returned bytes are whatever is next in the slab. One run returned ".strtab".

rsp->data[3] in srp_process_rsp() has the same problem: only resp_data_len is checked before it is read.

Drop a request that is shorter than the structure being parsed, and check byte_len before the tsk_mgmt read.

Fixes bb12588a38e6 ("IB/srp: Implement SRP_CRED_REQ and SRP_AER_REQ")
---
 drivers/infiniband/ulp/srp/ib_srp.c | 45 +++++++++++++++++++----------
 1 file changed, 30 insertions(+), 15 deletions(-)
drivers/infiniband/ulp/srp/ib_srp.c +30 -15
index ce1505a8ee8d8..13eddbe0841aa 100644
@@ -1943,7 +1943,8 @@ static void srp_process_rsp(struct srp_rdma_ch *ch, struct srp_rsp *rsp,
 		ch->req_lim += be32_to_cpu(rsp->req_lim_delta);
 		if (rsp->tag == ch->tsk_mgmt_tag) {
 			ch->tsk_mgmt_status = -1;
-			if (be32_to_cpu(rsp->resp_data_len) >= 4)
+			if (be32_to_cpu(rsp->resp_data_len) >= 4 &&
+			    byte_len >= sizeof(*rsp) + 4)
 				ch->tsk_mgmt_status = rsp->data[3];
 			complete(&ch->tsk_mgmt_done);
 		} else {
@@ -2043,13 +2044,20 @@ static int srp_response_common(struct srp_rdma_ch *ch, s32 req_delta,
 }
 
 static void srp_process_cred_req(struct srp_rdma_ch *ch,
-				 struct srp_cred_req *req)
+				 struct srp_cred_req *req, u32 byte_len)
 {
-	struct srp_cred_rsp rsp = {
-		.opcode = SRP_CRED_RSP,
-		.tag = req->tag,
-	};
-	s32 delta = be32_to_cpu(req->req_lim_delta);
+	struct srp_cred_rsp rsp = { .opcode = SRP_CRED_RSP };
+	s32 delta;
+
+	if (byte_len < sizeof(*req)) {
+		shost_printk(KERN_ERR, ch->target->scsi_host, PFX
+			     "dropping truncated SRP_CRED_REQ (%u bytes received, %zu expected)\n",
+			     byte_len, sizeof(*req));
+		return;
+	}
+
+	rsp.tag = req->tag;
+	delta = be32_to_cpu(req->req_lim_delta);
 
 	if (srp_response_common(ch, delta, &rsp, sizeof(rsp)))
 		shost_printk(KERN_ERR, ch->target->scsi_host, PFX
@@ -2057,14 +2065,21 @@ static void srp_process_cred_req(struct srp_rdma_ch *ch,
 }
 
 static void srp_process_aer_req(struct srp_rdma_ch *ch,
-				struct srp_aer_req *req)
+				struct srp_aer_req *req, u32 byte_len)
 {
 	struct srp_target_port *target = ch->target;
-	struct srp_aer_rsp rsp = {
-		.opcode = SRP_AER_RSP,
-		.tag = req->tag,
-	};
-	s32 delta = be32_to_cpu(req->req_lim_delta);
+	struct srp_aer_rsp rsp = { .opcode = SRP_AER_RSP };
+	s32 delta;
+
+	if (byte_len < sizeof(*req)) {
+		shost_printk(KERN_ERR, target->scsi_host, PFX
+			     "dropping truncated SRP_AER_REQ (%u bytes received, %zu expected)\n",
+			     byte_len, sizeof(*req));
+		return;
+	}
+
+	rsp.tag = req->tag;
+	delta = be32_to_cpu(req->req_lim_delta);
 
 	shost_printk(KERN_ERR, target->scsi_host, PFX
 		     "ignoring AER for LUN %llu\n", scsilun_to_int(&req->lun));
@@ -2106,11 +2121,11 @@ static void srp_recv_done(struct ib_cq *cq, struct ib_wc *wc)
 		break;
 
 	case SRP_CRED_REQ:
-		srp_process_cred_req(ch, iu->buf);
+		srp_process_cred_req(ch, iu->buf, wc->byte_len);
 		break;
 
 	case SRP_AER_REQ:
-		srp_process_aer_req(ch, iu->buf);
+		srp_process_aer_req(ch, iu->buf, wc->byte_len);
 		break;
 
 	case SRP_T_LOGOUT:
Signature · 1 line
2.53.0
author maintainer
#946 lore raw

PATCH 908/1376 ARM: 9481/2: breakpoint: CFI breakpoints only on demand

In reply to Greg Kroah-Hartman

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

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

From: Linus Walleij <[email protected]>

[ Upstream commit 8ed9bff906cf8036531d1559f10e82733a52b41f ]

This removes the stub hw_breakpoint_cfi_handler() from ARM, making it not steal breakpoint type 0x03 (ARM_ENTRY_CFI_BREAKPOINT) unless CFI is actively used in the kernel.

When not instrumenting with CFI, or when a breakpoint is issued in userspace, we fall through to return 1 from hw_breakpoint_pending() "unhandled fault" so userspace can make use of this breakpoint.

Tested with LKDTM and this command line:
echo CFI_FORWARD_PROTO > /sys/kernel/debug/provoke-crash/DIRECT still works as expected.

Fixes c3f89986fde7 ("ARM: 9391/2: hw_breakpoint: Handle CFI breakpoints")
---
 arch/arm/kernel/hw_breakpoint.c | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)
arch/arm/kernel/hw_breakpoint.c +6 -5
index a12efd0f43e81..6819d4c1f3521 100644
@@ -929,10 +929,6 @@ static void hw_breakpoint_cfi_handler(struct pt_regs *regs)
 		break;
 	}
 }
-#else
-static void hw_breakpoint_cfi_handler(struct pt_regs *regs)
-{
-}
 #endif
 
 /*
@@ -964,9 +960,14 @@ static int hw_breakpoint_pending(unsigned long addr, unsigned int fsr,
 	case ARM_ENTRY_SYNC_WATCHPOINT:
 		watchpoint_handler(addr, fsr, regs);
 		break;
+#ifdef CONFIG_CFI
 	case ARM_ENTRY_CFI_BREAKPOINT:
-		hw_breakpoint_cfi_handler(regs);
+		if (user_mode(regs))
+			ret = 1; /* Don't handle userspace BKPT */
+		else
+			hw_breakpoint_cfi_handler(regs);
 		break;
+#endif
 	default:
 		ret = 1; /* Unhandled fault. */
 	}
Signature · 1 line
2.53.0
#947 lore raw

Re: [PATCH 6.12 0908/1376] ARM: 9481/2: breakpoint: CFI breakpoints only on demand

Quoted 48 lines · Greg Kroah-Hartman

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

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

From: Linus Walleij <[email protected]>

[ Upstream commit 8ed9bff906cf8036531d1559f10e82733a52b41f ]

This removes the stub hw_breakpoint_cfi_handler() from ARM, making it not steal breakpoint type 0x03 (ARM_ENTRY_CFI_BREAKPOINT) unless CFI is actively used in the kernel.

When not instrumenting with CFI, or when a breakpoint is issued in userspace, we fall through to return 1 from hw_breakpoint_pending() "unhandled fault" so userspace can make use of this breakpoint.

Tested with LKDTM and this command line:
echo CFI_FORWARD_PROTO > /sys/kernel/debug/provoke-crash/DIRECT still works as expected.

Fixes c3f89986fde7 ("ARM: 9391/2: hw_breakpoint: Handle CFI breakpoints")
---
  arch/arm/kernel/hw_breakpoint.c | 11 ++++++-----
  1 file changed, 6 insertions(+), 5 deletions(-)
arch/arm/kernel/hw_breakpoint.c +0 -4
index a12efd0f43e81..6819d4c1f3521 100644
@@ -929,10 +929,6 @@ static void hw_breakpoint_cfi_handler(struct pt_regs *regs)
  		break;
  	}
  }
-#else
-static void hw_breakpoint_cfi_handler(struct pt_regs *regs)
-{
-}
  #endif
 
  /*

arch/arm/kernel/hw_breakpoint.c static int hw_breakpoint_pending(unsigned long addr, unsigned int fsr, · line 968

Quoted 10 lines
arch/arm/kernel/hw_breakpoint.c +5 -1
@@ -964,9 +960,14 @@ static int hw_breakpoint_pending(unsigned long addr, unsigned int fsr,
  	case ARM_ENTRY_SYNC_WATCHPOINT:
  		watchpoint_handler(addr, fsr, regs);
  		break;
+#ifdef CONFIG_CFI
  	case ARM_ENTRY_CFI_BREAKPOINT:
-		hw_breakpoint_cfi_handler(regs);
+		if (user_mode(regs))
+			ret = 1; /* Don't handle userspace BKPT */
+		else
+			hw_breakpoint_cfi_handler(regs);

Hi Greg/Sasha,

I ran an AI-assisted backport review, and I have verified the report. It looks valid to me.

The new ARM_ENTRY_CFI_BREAKPOINT case is under CONFIG_CFI, but this 6.12 tree defines and uses CONFIG_CFI_CLANG. CONFIG_CFI was introduced by a later upstream rename and is absent here. The handler itself still builds under CONFIG_CFI_CLANG, so a CFI-enabled destination omits the only dispatch case which can call it.

Upstream source code after the later Kconfig rename:

    #ifdef CONFIG_CFI
    case ARM_ENTRY_CFI_BREAKPOINT:

Downstream 6.12.y handler code:

#ifdef CONFIG_CFI_CLANG
static void hw_breakpoint_cfi_handler(struct pt_regs *regs)

Maybe we should change that guard from CONFIG_CFI to CONFIG_CFI_CLANG before retaining the backport. Thoughts?

thanks,
Harshit

Quoted 5 lines
arch/arm/kernel/hw_breakpoint.c +1 -0
  		break;
+#endif
  	default:
  		ret = 1; /* Unhandled fault. */
  	}
author maintainer
#948 lore raw

PATCH 909/1376 ARM: 9485/1: mm: acquire mmap write lock around show_pte() for user faults

In reply to Greg Kroah-Hartman

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

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

From: Xie Yuanbin <[email protected]>

[ Upstream commit 1039bffd6ae9c75b42b7d148d6c1106134107b66 ]

When CONFIG_DEBUG_USER=y, and cmdline "user_debug=31" is set, a user fault may trigger show_pte() without any lock.
If another thread in the same process concurrently calls munmap(), the page table pages may be freed while show_pte() is still traversing them, causing a use-after-free in show_pte().

If CONFIG_ARM_LPAE=y, this may cause a kernel panic if the pages table of PMD are freed when show_pte() is running.

Acquire mmap_write_lock() around show_pte() for user faults to fix the contention.

For user faults, additionally restrict that show_pte() is called only when the addr is a user-space address (addr < TASK_SIZE). This is because the lock of tsk->mm only protects the virtual memory of user address space, furthermore, dumping the page tables of a kernel-space address for user faults is unnecessary and may have security implications.

Keep everything unchanged for kernel faults, because the kernel is already in the "oops" state, acquiring a lock may risk a deadlock.

Fixes 6d021b724481 ("ARM: dump pgd, pmd and pte states on unhandled data abort faults")
---
 arch/arm/mm/fault.c | 16 ++++++++++++++--
 1 file changed, 14 insertions(+), 2 deletions(-)
arch/arm/mm/fault.c +14 -2
index 16b5a7d214808..6763a7c660780 100644
@@ -205,7 +205,11 @@ __do_user_fault(unsigned long addr, unsigned int fsr, unsigned int sig,
 		pr_err("8<--- cut here ---\n");
 		pr_err("%s: unhandled page fault (%d) at 0x%08lx, code 0x%03x\n",
 		       tsk->comm, sig, addr, fsr);
-		show_pte(KERN_ERR, tsk->mm, addr);
+		if (likely(addr < TASK_SIZE)) {
+			mmap_write_lock(tsk->mm);
+			show_pte(KERN_ERR, tsk->mm, addr);
+			mmap_write_unlock(tsk->mm);
+		}
 		show_regs(regs);
 	}
 #endif
@@ -649,7 +653,15 @@ do_DataAbort(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
 	pr_alert("8<--- cut here ---\n");
 	pr_alert("Unhandled fault: %s (0x%03x) at 0x%08lx\n",
 		inf->name, fsr, addr);
-	show_pte(KERN_ALERT, current->mm, addr);
+	if (likely(user_mode(regs))) {
+		if (addr < TASK_SIZE) {
+			mmap_write_lock(current->mm);
+			show_pte(KERN_ALERT, current->mm, addr);
+			mmap_write_unlock(current->mm);
+		}
+	} else {
+		show_pte(KERN_ALERT, current->mm, addr);
+	}
 
 	arm_notify_die("", regs, inf->sig, inf->code, (void __user *)addr,
 		       fsr, 0);
Signature · 1 line
2.53.0
#949 lore raw

Re: [PATCH 6.12 0909/1376] ARM: 9485/1: mm: acquire mmap write lock around show_pte() for user faults

Quoted 48 lines · Greg Kroah-Hartman

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

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

From: Xie Yuanbin <[email protected]>

[ Upstream commit 1039bffd6ae9c75b42b7d148d6c1106134107b66 ]

When CONFIG_DEBUG_USER=y, and cmdline "user_debug=31" is set, a user fault may trigger show_pte() without any lock.
If another thread in the same process concurrently calls munmap(), the page table pages may be freed while show_pte() is still traversing them, causing a use-after-free in show_pte().

If CONFIG_ARM_LPAE=y, this may cause a kernel panic if the pages table of PMD are freed when show_pte() is running.

Acquire mmap_write_lock() around show_pte() for user faults to fix the contention.

For user faults, additionally restrict that show_pte() is called only when the addr is a user-space address (addr < TASK_SIZE). This is because the lock of tsk->mm only protects the virtual memory of user address space, furthermore, dumping the page tables of a kernel-space address for user faults is unnecessary and may have security implications.

Keep everything unchanged for kernel faults, because the kernel is already in the "oops" state, acquiring a lock may risk a deadlock.

Fixes 6d021b724481 ("ARM: dump pgd, pmd and pte states on unhandled data abort faults")
---
  arch/arm/mm/fault.c | 16 ++++++++++++++--
  1 file changed, 14 insertions(+), 2 deletions(-)
arch/arm/mm/fault.c +0 -0
index 16b5a7d214808..6763a7c660780 100644

arch/arm/mm/fault.c __do_user_fault(unsigned long addr, unsigned int fsr, unsigned int sig, · line 211

Quoted 8 lines
arch/arm/mm/fault.c +4 -1
@@ -205,7 +205,11 @@ __do_user_fault(unsigned long addr, unsigned int fsr, unsigned int sig,
  		pr_err("8<--- cut here ---\n");
  		pr_err("%s: unhandled page fault (%d) at 0x%08lx, code 0x%03x\n",
  		       tsk->comm, sig, addr, fsr);
-		show_pte(KERN_ERR, tsk->mm, addr);
+		if (likely(addr < TASK_SIZE)) {
+			mmap_write_lock(tsk->mm);
+			show_pte(KERN_ERR, tsk->mm, addr);
+			mmap_write_unlock(tsk->mm);

Hi Greg/Sasha,

I ran an AI-assisted backport review, and I have verified the report. It looks valid to me.

Upstream prerequisite code:

    local_irq_enable();
    mmap_write_lock(tsk->mm);

Downstream 6.12.y code:

    if (likely(addr < TASK_SIZE)) {
            mmap_write_lock(tsk->mm);
            show_pte(KERN_ERR, tsk->mm, addr);
    }

The two new mmap_write_lock() calls are sleepable. Upstream first enabled interrupts in both affected user-fault paths, but 6.12 lacks those changes. The backport can therefore call down_write() with IRQs disabled, producing atomic-sleep diagnostics or a stall if the mmap lock is contended.

The missing commits are 59e4f3b45b96a24fc9b7a89e5f8a2168b30f95af and e79ca91165d4fd18549c536abdb86101e889052f. I suggest applying adapted equivalents of both before retaining this patch. Thoughts?

thanks,
Harshit

Quoted 21 lines
arch/arm/mm/fault.c +10 -1
+		}
  		show_regs(regs);
  	}
  #endif
@@ -649,7 +653,15 @@ do_DataAbort(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
  	pr_alert("8<--- cut here ---\n");
  	pr_alert("Unhandled fault: %s (0x%03x) at 0x%08lx\n",
  		inf->name, fsr, addr);
-	show_pte(KERN_ALERT, current->mm, addr);
+	if (likely(user_mode(regs))) {
+		if (addr < TASK_SIZE) {
+			mmap_write_lock(current->mm);
+			show_pte(KERN_ALERT, current->mm, addr);
+			mmap_write_unlock(current->mm);
+		}
+	} else {
+		show_pte(KERN_ALERT, current->mm, addr);
+	}
 
  	arm_notify_die("", regs, inf->sig, inf->code, (void __user *)addr,
  		       fsr, 0);
maintainer
#950 lore raw

Re: [PATCH 6.12 0909/1376] ARM: 9485/1: mm: acquire mmap write lock around show_pte() for user faults

Quoted 3 lines

The missing commits are 59e4f3b45b96a24fc9b7a89e5f8a2168b30f95af and e79ca91165d4fd18549c536abdb86101e889052f. I suggest applying adapted equivalents of both before retaining this patch. Thoughts?

e79ca91165d4 ("ARM: 9484/1: enable interrupts when unhandled user faults are triggered") is already queued for 5.15 and 5.10.

59e4f3b45b96 ("ARM: ensure interrupts are enabled in __do_user_fault()") cannot go in on its own on those trees. Without 7733bc7d299d ("ARM: fix hash_name() fault") and fd2dee1c6e22 ("ARM: fix branch predictor hardening") it leaves harden_branch_predictor() running preemptible, which is a Spectre v2 regression.

Xie Yuanbin has posted a 5.15 and 5.10 series that carries both prerequisites, so I would rather take that once he resends it.

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

PATCH 910/1376 ocfs2: synchronize heartbeat callbacks with o2net teardown

In reply to Greg Kroah-Hartman

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

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

From: Cen Zhang <[email protected]>

[ Upstream commit 3e326f3bf16506873777444608e8b715aab74a7a ]

Patch series "ocfs2: harden heartbeat teardown races".

This series fixes two OCFS2 heartbeat/o2net teardown races found by KASAN.

This patch (of 2):

Heartbeat callbacks stay registered while configfs local-node teardown enters o2net_stop_listening(). A node-down event can still run through o2net_disconnect_node() and o2net_set_nn_state() while teardown is destroying o2net_wq, so the later queue/flush operations can hit a dead workqueue. KASAN has caught this as a slab-use-after-free in __queue_work() with the call chain:

KASAN slab-use-after-free in __queue_work+0x56/0xa90

Read of size 4
Call trace:
  dump_stack_lvl+0x66/0xa0
  print_report+0xce/0x630
  __queue_work+0x56/0xa90
  srso_alias_return_thunk+0x5/0xfbef5
  __virt_addr_valid+0x19f/0x330
  kasan_report+0xe0/0x110
  __queue_delayed_work+0x58/0x1e0
  queue_delayed_work_on+0xb4/0xc0
  o2net_set_nn_state+0x467/0x840
  o2net_disconnect_node+0x7b/0xe0
  o2net_hb_node_down_cb+0x54/0x60
  o2hb_run_event_list+0x236/0x2d0
  o2hb_check_slot+0xad4/0xbc0
  lock_release+0xc8/0x290
  o2hb_check_slot+0x9ea/0xbc0
  trace_hardirqs_on+0x18/0x130
  o2hb_do_disk_heartbeat+0x646/0xb30 (fs/ocfs2/cluster/heartbeat.c:1079)
  __lock_acquire+0x466/0x2260
  lockdep_hardirqs_on_prepare+0xea/0x1a0
  ktime_get_with_offset+0xe9/0x230
  o2hb_thread+0x14e/0x770
  kthread+0x1ad/0x1f0
  ret_from_fork+0x3c9/0x540
  __switch_to+0x2e9/0x730
  ret_from_fork_asm+0x1a/0x30
Allocated by task stack:
  kasan_save_stack+0x33/0x60
  kasan_save_track+0x14/0x30
  __kasan_kmalloc+0xaa/0xb0
  __kmalloc_noprof+0x292/0x760
  __alloc_workqueue+0x736/0xc60
  alloc_workqueue_noprof+0xb1/0x110
  o2net_start_listening+0xe5/0x430
  o2nm_node_local_store+0x184/0x310
  configfs_write_iter+0x18a/0x210
  vfs_write+0x469/0x810
  ksys_write+0xd2/0x170
  do_syscall_64+0x115/0x6a0 (arch/x86/entry/syscall_64.c:87)
  entry_SYSCALL_64_after_hwframe+0x77/0x7f
Freed by task stack:
  kasan_save_stack+0x33/0x60
  kasan_save_track+0x14/0x30
  kasan_save_free_info+0x3b/0x60
  __kasan_slab_free+0x5f/0x80
  kfree+0x313/0x590
  rcu_core+0x4f4/0x1320
  handle_softirqs+0x156/0x660

queue_delayed_work_on
o2net_set_nn_state
o2net_disconnect_node
o2net_hb_node_down_cb
o2hb_run_event_list

Keep heartbeat callbacks registered so quorum state still tracks node state, but stop them from driving o2net reconnect/disconnect work once local teardown starts. Mark the transport offline before destroying o2net_wq, wait for any in-flight heartbeat callback to finish, and delay bring-up replay until the new local node is published through o2nm_this_node().

The replay also has to stay serialized with heartbeat callback delivery. Otherwise a live-node snapshot can be copied, a real hb_down callback can install -ENOTCONN for a peer, and the stale replay can call o2net_hb_node_up() for that same peer and queue reconnect work even though heartbeat is already down.

The buggy scenario involves two paths, with each column showing the order within that path:

local-node teardown: heartbeat node-down callback:
1. configfs local-off enters 1. o2hb_run_event_list() invokes
o2net_stop_listening(). o2net_hb_node_down_cb().
2. teardown heads for 2. the callback reaches
destroy_workqueue(o2net_wq). o2net_disconnect_node() and

                                        o2net_set_nn_state().

3. teardown destroys and NULLs 3. the callback flushes or queues
o2net_wq. work through o2net_wq.

Fixes 98211489d414 ("[PATCH] OCFS2: The Second Oracle Cluster Filesystem")
Assisted-by Codex:gpt-5.5
Cc Changwei Ge <[email protected]>
Cc Heming Zhao <[email protected]>
Cc Joel Becker <[email protected]>
Cc Jun Piao <[email protected]>
Cc Junxiao Bi <[email protected]>
Cc Mark Fasheh <[email protected]>
---
 fs/ocfs2/cluster/heartbeat.c   | 43 ++++++++++++++----
 fs/ocfs2/cluster/heartbeat.h   |  5 ++
 fs/ocfs2/cluster/nodemanager.c |  4 ++
 fs/ocfs2/cluster/tcp.c         | 83 +++++++++++++++++++++++++++-------
 fs/ocfs2/cluster/tcp.h         |  1 +
 5 files changed, 109 insertions(+), 27 deletions(-)
fs/ocfs2/cluster/heartbeat.c +33 -10
index ea44ea6efbc6e..6170e202e56e3 100644
@@ -1476,13 +1476,38 @@ void o2hb_init(void)
 	o2hb_debug_init();
 }
 
-/* if we're already in a callback then we're already serialized by the sem */
-static void o2hb_fill_node_map_from_callback(unsigned long *map,
-					     unsigned int bits)
+static void __o2hb_fill_node_map(unsigned long *map, unsigned int bits)
 {
 	bitmap_copy(map, o2hb_live_node_bitmap, bits);
 }
 
+void o2hb_callback_read_lock(void)
+{
+	down_read(&o2hb_callback_sem);
+}
+
+void o2hb_callback_read_unlock(void)
+{
+	up_read(&o2hb_callback_sem);
+}
+
+void o2hb_synchronize_callbacks(void)
+{
+	down_write(&o2hb_callback_sem);
+	up_write(&o2hb_callback_sem);
+}
+
+/*
+ * Callers must already hold o2hb_callback_sem for read or write so the copy
+ * stays serialized with callback delivery.
+ */
+void o2hb_fill_node_map_locked(unsigned long *map, unsigned int bits)
+{
+	spin_lock(&o2hb_live_lock);
+	__o2hb_fill_node_map(map, bits);
+	spin_unlock(&o2hb_live_lock);
+}
+
 /*
  * get a map of all nodes that are heartbeating in any regions
  */
@@ -1490,11 +1515,9 @@ void o2hb_fill_node_map(unsigned long *map, unsigned int bits)
 {
 	/* callers want to serialize this map and callbacks so that they
 	 * can trust that they don't miss nodes coming to the party */
-	down_read(&o2hb_callback_sem);
-	spin_lock(&o2hb_live_lock);
-	o2hb_fill_node_map_from_callback(map, bits);
-	spin_unlock(&o2hb_live_lock);
-	up_read(&o2hb_callback_sem);
+	o2hb_callback_read_lock();
+	o2hb_fill_node_map_locked(map, bits);
+	o2hb_callback_read_unlock();
 }
 EXPORT_SYMBOL_GPL(o2hb_fill_node_map);
 
@@ -2566,7 +2589,7 @@ int o2hb_check_node_heartbeating_no_sem(u8 node_num)
 	unsigned long testing_map[BITS_TO_LONGS(O2NM_MAX_NODES)];
 
 	spin_lock(&o2hb_live_lock);
-	o2hb_fill_node_map_from_callback(testing_map, O2NM_MAX_NODES);
+	__o2hb_fill_node_map(testing_map, O2NM_MAX_NODES);
 	spin_unlock(&o2hb_live_lock);
 	if (!test_bit(node_num, testing_map)) {
 		mlog(ML_HEARTBEAT,
@@ -2583,7 +2606,7 @@ int o2hb_check_node_heartbeating_from_callback(u8 node_num)
 {
 	unsigned long testing_map[BITS_TO_LONGS(O2NM_MAX_NODES)];
 
-	o2hb_fill_node_map_from_callback(testing_map, O2NM_MAX_NODES);
+	o2hb_fill_node_map_locked(testing_map, O2NM_MAX_NODES);
 	if (!test_bit(node_num, testing_map)) {
 		mlog(ML_HEARTBEAT,
 		     "node (%u) does not have heartbeating enabled.\n",
fs/ocfs2/cluster/heartbeat.h +5 -0
index 8ef8c1b9eeb76..2ca2b657583c0 100644
@@ -58,6 +58,11 @@ int o2hb_register_callback(const char *region_uuid,
 			   struct o2hb_callback_func *hc);
 void o2hb_unregister_callback(const char *region_uuid,
 			      struct o2hb_callback_func *hc);
+void o2hb_callback_read_lock(void);
+void o2hb_callback_read_unlock(void);
+void o2hb_synchronize_callbacks(void);
+void o2hb_fill_node_map_locked(unsigned long *map,
+			       unsigned int bits);
 void o2hb_fill_node_map(unsigned long *map,
 			unsigned int bits);
 void o2hb_exit(void);
fs/ocfs2/cluster/nodemanager.c +4 -0
index 46e0c9ba8a4ff..b65fc6eeae6e0 100644
@@ -325,6 +325,7 @@ static ssize_t o2nm_node_local_store(struct config_item *item, const char *page,
 	struct o2nm_node *node = to_o2nm_node(item);
 	struct o2nm_cluster *cluster;
 	unsigned long tmp;
+	bool starting = false;
 	char *p = (char *)page;
 	ssize_t ret;
 
@@ -361,6 +362,7 @@ static ssize_t o2nm_node_local_store(struct config_item *item, const char *page,
 		ret = o2net_start_listening(node);
 		if (ret)
 			goto out;
+		starting = true;
 	}
 
 	if (!tmp && cluster->cl_has_local &&
@@ -374,6 +376,8 @@ static ssize_t o2nm_node_local_store(struct config_item *item, const char *page,
 	if (node->nd_local) {
 		cluster->cl_has_local = tmp;
 		cluster->cl_local_node = node->nd_num;
+		if (starting)
+			o2net_complete_start_listening(node);
 	}
 
 	ret = count;
fs/ocfs2/cluster/tcp.c +66 -17

137 lines not on this page · raw message

fs/ocfs2/cluster/tcp.h +1 -0
index a75b551d31c7b..2e86d42b5faf9 100644
@@ -96,6 +96,7 @@ struct o2nm_node;
 int o2net_register_hb_callbacks(void);
 void o2net_unregister_hb_callbacks(void);
 int o2net_start_listening(struct o2nm_node *node);
+void o2net_complete_start_listening(struct o2nm_node *node);
 void o2net_stop_listening(struct o2nm_node *node);
 void o2net_disconnect_node(struct o2nm_node *node);
 int o2net_num_connected_peers(void);
Signature · 1 line
2.53.0
author maintainer
#952 lore raw

PATCH 911/1376 clk: rockchip: rk3576: fix source muxes for SPI0..SPI4

In reply to Greg Kroah-Hartman

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

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

From: Alexey Charkov <[email protected]>

[ Upstream commit 586ff159ec02533c17dd928641529cb0b52e9c62 ]

The TRM defines available source muxes for SPI0..SPI4 as
- b00: clk_gpll_div6_src
- b01: clk_gpll_div8_src
- b10: clk_cpll_div10_src
- b11: clk_xin_osc0_func

Which doesn't match what the current clock driver implements, making it impossible to derive some SPI clock rates such as 37.125 MHz (which requires clk_gpll_div8_src as the source mux).

Add a correct mux definition per TRM and point SPI0..SPI4 clocks at it.

Fixes cc40f5baa91b ("clk: rockchip: Add clock controller for the RK3576")
---
 drivers/clk/rockchip/clk-rk3576.c | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)
drivers/clk/rockchip/clk-rk3576.c +6 -5
index 595e010341f73..0746d2d07095b 100644
@@ -313,6 +313,7 @@ PNAME(mux_100m_24m_lclk0_p)		= { "clk_cpll_div10", "xin24m", "lclk_asrc_src_0" }
 PNAME(mux_100m_24m_lclk1_p)		= { "clk_cpll_div10", "xin24m", "lclk_asrc_src_1" };
 PNAME(mux_150m_100m_50m_24m_p)		= { "clk_gpll_div8", "clk_cpll_div10", "clk_cpll_div20", "xin24m" };
 PNAME(mux_200m_100m_50m_24m_p)		= { "clk_gpll_div6", "clk_cpll_div10", "clk_cpll_div20", "xin24m" };
+PNAME(mux_200m_150m_100m_24m_p)		= { "clk_gpll_div6", "clk_gpll_div8", "clk_cpll_div10", "xin24m" };
 PNAME(mux_400m_200m_100m_24m_p)		= { "clk_gpll_div3", "clk_gpll_div6", "clk_cpll_div10", "xin24m" };
 PNAME(mux_500m_250m_100m_24m_p)		= { "clk_cpll_div2", "clk_cpll_div4", "clk_cpll_div10", "xin24m" };
 PNAME(mux_600m_400m_300m_24m_p)		= { "clk_gpll_div2", "clk_gpll_div3", "clk_gpll_div4", "xin24m" };
@@ -702,19 +703,19 @@ static struct rockchip_clk_branch rk3576_clk_branches[] __initdata = {
 			RK3576_CLKGATE_CON(16), 0, GFLAGS),
 	GATE(PCLK_SPI4, "pclk_spi4", "pclk_bus_root", 0,
 			RK3576_CLKGATE_CON(16), 1, GFLAGS),
-	COMPOSITE_NODIV(CLK_SPI0, "clk_spi0", mux_200m_100m_50m_24m_p, 0,
+	COMPOSITE_NODIV(CLK_SPI0, "clk_spi0", mux_200m_150m_100m_24m_p, 0,
 			RK3576_CLKSEL_CON(70), 13, 2, MFLAGS,
 			RK3576_CLKGATE_CON(16), 2, GFLAGS),
-	COMPOSITE_NODIV(CLK_SPI1, "clk_spi1", mux_200m_100m_50m_24m_p, 0,
+	COMPOSITE_NODIV(CLK_SPI1, "clk_spi1", mux_200m_150m_100m_24m_p, 0,
 			RK3576_CLKSEL_CON(71), 0, 2, MFLAGS,
 			RK3576_CLKGATE_CON(16), 3, GFLAGS),
-	COMPOSITE_NODIV(CLK_SPI2, "clk_spi2", mux_200m_100m_50m_24m_p, 0,
+	COMPOSITE_NODIV(CLK_SPI2, "clk_spi2", mux_200m_150m_100m_24m_p, 0,
 			RK3576_CLKSEL_CON(71), 2, 2, MFLAGS,
 			RK3576_CLKGATE_CON(16), 4, GFLAGS),
-	COMPOSITE_NODIV(CLK_SPI3, "clk_spi3", mux_200m_100m_50m_24m_p, 0,
+	COMPOSITE_NODIV(CLK_SPI3, "clk_spi3", mux_200m_150m_100m_24m_p, 0,
 			RK3576_CLKSEL_CON(71), 4, 2, MFLAGS,
 			RK3576_CLKGATE_CON(16), 5, GFLAGS),
-	COMPOSITE_NODIV(CLK_SPI4, "clk_spi4", mux_200m_100m_50m_24m_p, 0,
+	COMPOSITE_NODIV(CLK_SPI4, "clk_spi4", mux_200m_150m_100m_24m_p, 0,
 			RK3576_CLKSEL_CON(71), 6, 2, MFLAGS,
 			RK3576_CLKGATE_CON(16), 6, GFLAGS),
 	GATE(PCLK_WDT0, "pclk_wdt0", "pclk_bus_root", 0,
Signature · 1 line
2.53.0
author maintainer
#953 lore raw

PATCH 912/1376 drm/sun4i: vi scaler: Fix coefficient selection

In reply to Greg Kroah-Hartman

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

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

From: Jernej Skrabec <[email protected]>

[ Upstream commit 5c31990b21f0b535732deb2b658b78b07464f56c ]

Currently, vertical coefficients are selected based on horizontal scaling, which is wrong. Additionally, chroma coefficients should be selected based on format subsampling.

Fix all that.

---
 drivers/gpu/drm/sun4i/sun8i_vi_scaler.c | 18 ++++++++++++------
 1 file changed, 12 insertions(+), 6 deletions(-)
drivers/gpu/drm/sun4i/sun8i_vi_scaler.c +12 -6
index 7ba75011adf9f..328b84df2ee5e 100644
@@ -891,20 +891,26 @@ static void sun8i_vi_scaler_set_coeff(struct regmap *map, u32 base,
 			     lan3coefftab32_left[offset + i]);
 		regmap_write(map, SUN8I_SCALER_VSU_YHCOEFF1(base, i),
 			     lan3coefftab32_right[offset + i]);
+	}
+	offset = sun8i_vi_scaler_coef_index(vstep) *
+			SUN8I_VI_SCALER_COEFF_COUNT;
+	for (i = 0; i < SUN8I_VI_SCALER_COEFF_COUNT; i++)
+		regmap_write(map, SUN8I_SCALER_VSU_YVCOEFF(base, i),
+			     lan2coefftab32[offset + i]);
+
+	offset = sun8i_vi_scaler_coef_index(hstep / format->hsub) *
+			SUN8I_VI_SCALER_COEFF_COUNT;
+	for (i = 0; i < SUN8I_VI_SCALER_COEFF_COUNT; i++) {
 		regmap_write(map, SUN8I_SCALER_VSU_CHCOEFF0(base, i),
 			     ch_left[offset + i]);
 		regmap_write(map, SUN8I_SCALER_VSU_CHCOEFF1(base, i),
 			     ch_right[offset + i]);
 	}
-
-	offset = sun8i_vi_scaler_coef_index(hstep) *
+	offset = sun8i_vi_scaler_coef_index(vstep / format->vsub) *
 			SUN8I_VI_SCALER_COEFF_COUNT;
-	for (i = 0; i < SUN8I_VI_SCALER_COEFF_COUNT; i++) {
-		regmap_write(map, SUN8I_SCALER_VSU_YVCOEFF(base, i),
-			     lan2coefftab32[offset + i]);
+	for (i = 0; i < SUN8I_VI_SCALER_COEFF_COUNT; i++)
 		regmap_write(map, SUN8I_SCALER_VSU_CVCOEFF(base, i),
 			     cy[offset + i]);
-	}
 }
 
 void sun8i_vi_scaler_enable(struct sun8i_mixer *mixer, int layer, bool enable)
Signature · 1 line
2.53.0
author maintainer
#954 lore raw

PATCH 913/1376 of: property: add of_graph_get_next_port()

In reply to Greg Kroah-Hartman

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

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

From: Kuninori Morimoto <[email protected]>

[ Upstream commit 02ac5f9d6caec96071103f7c62b5117526e47b64 ]

We have endpoint base functions
- of_graph_get_next_endpoint()
- of_graph_get_endpoint_count()
- for_each_endpoint_of_node()

Here, for_each_endpoint_of_node() loop finds each endpoints

	ports {
		port@0 {
(1)			endpoint {...};
		};
		port@1 {
(2)			endpoint {...};
		};
		...
	};

In above case, it finds endpoint as (1) -> (2) -> ...

Basically, user/driver knows which port is used for what, but not in all cases. For example on flexible/generic driver case, how many ports are used is not fixed.

For example Sound Generic Card driver which is very flexible/generic and used from many venders can't know how many ports are used, and used for what, because it depends on each vender SoC and/or its used board.

And more, the port can have multi endpoints. For example Generic Sound Card case, it supports many type of connection between CPU / Codec, and some of them uses multi endpoint in one port. see below.

	ports {
(A)		port@0 {
(1)			endpoint@0 {...};
(2)			endpoint@1 {...};
		};
(B)		port@1 {
(3)			endpoint {...};
		};
		...
	};

Generic Sound Card want to handle each connection via "port" base instead of "endpoint" base. But, it is very difficult to handle each "port" via existing for_each_endpoint_of_node(). Because getting each "port" via of_get_parent() from each "endpoint" doesn't work. For example in above case, both (1) (2) endpoint has same "port" (= A).

Add "port" base functions.

Stable-dep-of: 62bac3202042 ("drm/sun4i: tcon-top: Keep mixer routes distinct")

---
 drivers/of/property.c    | 54 ++++++++++++++++++++++++++++++++++++++++
 include/linux/of_graph.h | 28 +++++++++++++++++++++
 2 files changed, 82 insertions(+)
drivers/of/property.c +54 -0
index 906a33ae717f7..7c8fb5dd4cb19 100644
@@ -630,6 +630,43 @@ struct device_node *of_graph_get_port_by_id(struct device_node *parent, u32 id)
 }
 EXPORT_SYMBOL(of_graph_get_port_by_id);
 
+/**
+ * of_graph_get_next_port() - get next port node.
+ * @parent: pointer to the parent device node, or parent ports node
+ * @prev: previous port node, or NULL to get first
+ *
+ * Parent device node can be used as @parent whether device node has ports node
+ * or not. It will work same as ports@0 node.
+ *
+ * Return: A 'port' node pointer with refcount incremented. Refcount
+ * of the passed @prev node is decremented.
+ */
+struct device_node *of_graph_get_next_port(const struct device_node *parent,
+					   struct device_node *prev)
+{
+	if (!parent)
+		return NULL;
+
+	if (!prev) {
+		struct device_node *node __free(device_node) =
+			of_get_child_by_name(parent, "ports");
+
+		if (node)
+			parent = node;
+
+		return of_get_child_by_name(parent, "port");
+	}
+
+	do {
+		prev = of_get_next_child(parent, prev);
+		if (!prev)
+			break;
+	} while (!of_node_name_eq(prev, "port"));
+
+	return prev;
+}
+EXPORT_SYMBOL(of_graph_get_next_port);
+
 /**
  * of_graph_get_next_endpoint() - get next endpoint node
  * @parent: pointer to the parent device node
@@ -823,6 +860,23 @@ unsigned int of_graph_get_endpoint_count(const struct device_node *np)
 }
 EXPORT_SYMBOL(of_graph_get_endpoint_count);
 
+/**
+ * of_graph_get_port_count() - get the number of port in a device or ports node
+ * @np: pointer to the device or ports node
+ *
+ * Return: count of port of this device or ports node
+ */
+unsigned int of_graph_get_port_count(struct device_node *np)
+{
+	unsigned int num = 0;
+
+	for_each_of_graph_port(np, port)
+		num++;
+
+	return num;
+}
+EXPORT_SYMBOL(of_graph_get_port_count);
+
 /**
  * of_graph_get_remote_node() - get remote parent device_node for given port/endpoint
  * @node: pointer to parent device_node containing graph port/endpoint
include/linux/of_graph.h +28 -0
index a4bea62bfa290..44518f3583a44 100644
@@ -11,6 +11,7 @@
 #ifndef __LINUX_OF_GRAPH_H
 #define __LINUX_OF_GRAPH_H
 
+#include <linux/cleanup.h>
 #include <linux/types.h>
 #include <linux/errno.h>
 
@@ -37,14 +38,29 @@ struct of_endpoint {
 	for (child = of_graph_get_next_endpoint(parent, NULL); child != NULL; \
 	     child = of_graph_get_next_endpoint(parent, child))
 
+/**
+ * for_each_of_graph_port - iterate over every port in a device or ports node
+ * @parent: parent device or ports node containing port
+ * @child: loop variable pointing to the current port node
+ *
+ * When breaking out of the loop, and continue to use the @child, you need to
+ * use return_ptr(@child) or no_free_ptr(@child) not to call __free() for it.
+ */
+#define for_each_of_graph_port(parent, child)			\
+	for (struct device_node *child __free(device_node) = of_graph_get_next_port(parent, NULL);\
+	     child != NULL; child = of_graph_get_next_port(parent, child))
+
 #ifdef CONFIG_OF
 bool of_graph_is_present(const struct device_node *node);
 int of_graph_parse_endpoint(const struct device_node *node,
 				struct of_endpoint *endpoint);
 unsigned int of_graph_get_endpoint_count(const struct device_node *np);
+unsigned int of_graph_get_port_count(struct device_node *np);
 struct device_node *of_graph_get_port_by_id(struct device_node *node, u32 id);
 struct device_node *of_graph_get_next_endpoint(const struct device_node *parent,
 					struct device_node *previous);
+struct device_node *of_graph_get_next_port(const struct device_node *parent,
+					   struct device_node *port);
 struct device_node *of_graph_get_endpoint_by_regs(
 		const struct device_node *parent, int port_reg, int reg);
 struct device_node *of_graph_get_remote_endpoint(
@@ -73,6 +89,11 @@ static inline unsigned int of_graph_get_endpoint_count(const struct device_node
 	return 0;
 }
 
+static inline unsigned int of_graph_get_port_count(struct device_node *np)
+{
+	return 0;
+}
+
 static inline struct device_node *of_graph_get_port_by_id(
 					struct device_node *node, u32 id)
 {
@@ -86,6 +107,13 @@ static inline struct device_node *of_graph_get_next_endpoint(
 	return NULL;
 }
 
+static inline struct device_node *of_graph_get_next_port(
+					const struct device_node *parent,
+					struct device_node *previous)
+{
+	return NULL;
+}
+
 static inline struct device_node *of_graph_get_endpoint_by_regs(
 		const struct device_node *parent, int port_reg, int reg)
 {
Signature · 1 line
2.53.0
author maintainer
#955 lore raw

PATCH 914/1376 of: property: add of_graph_get_next_port_endpoint()

In reply to Greg Kroah-Hartman

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

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

From: Kuninori Morimoto <[email protected]>

[ Upstream commit 58fe47d6ac7413172e1fa88324fb1b4c0eb2c0a2 ]

We already have of_graph_get_next_endpoint(), but it is not intuitive to use in some case.

(X) node {
(Y) ports {
(P0) port@0 { endpoint { remote-endpoint = ...; };};
(P10) port@1 { endpoint { remote-endpoint = ...; };
(P11) endpoint { remote-endpoint = ...; };};

(P2)			port@2 { endpoint { remote-endpoint = ...; };};
		};
	};

For example, if I want to handle port@1's 2 endpoints (= P10, P11), I want to use like below

	P10 = of_graph_get_next_endpoint(port1, NULL);
	P11 = of_graph_get_next_endpoint(port1, P10);

But 1st one will be error, because of_graph_get_next_endpoint() requested 1st parameter is "node" (X) or "ports" (Y), not but "port". Below works well, but it will get P0

	P0 = of_graph_get_next_endpoint(node,  NULL);
	P0 = of_graph_get_next_endpoint(ports, NULL);

In other words, we can't handle P10/P11 directly via of_graph_get_next_endpoint().

There is another non intuitive behavior on of_graph_get_next_endpoint(). In case of if I could get P10 pointer for some way, and if I want to handle port@1 things by loop, I would like use it like below

	/*
	 * "ep" is now P10, and handle port1 things here,
	 * but we don't know how many endpoints port1 have.
	 *
	 * Because "ep" is non NULL now, we can use port1
	 * as of_graph_get_next_endpoint(port1, xxx)
	 */
	do {
		/* do something for port1 specific things here */
	} while (ep = of_graph_get_next_endpoint(port1, ep))

But it also not worked as I expected.
I expect it will be P10 -> P11 -> NULL,
but it will be P10 -> P11 -> P2, because
of_graph_get_next_endpoint() will fetch "endpoint" beyond the "port". It is not useful for generic driver.

To handle endpoint more intuitive, create of_graph_get_next_port_endpoint()

	of_graph_get_next_port_endpoint(port1, NULL); // P10
	of_graph_get_next_port_endpoint(port1, P10);  // P11
	of_graph_get_next_port_endpoint(port1, P11);  // NULL

Stable-dep-of: 62bac3202042 ("drm/sun4i: tcon-top: Keep mixer routes distinct")

---
 drivers/of/property.c    | 27 +++++++++++++++++++++++++++
 include/linux/of_graph.h | 21 +++++++++++++++++++++
 2 files changed, 48 insertions(+)
drivers/of/property.c +27 -0
index 7c8fb5dd4cb19..0b1d28b249bdb 100644
@@ -667,6 +667,33 @@ struct device_node *of_graph_get_next_port(const struct device_node *parent,
 }
 EXPORT_SYMBOL(of_graph_get_next_port);
 
+/**
+ * of_graph_get_next_port_endpoint() - get next endpoint node in port.
+ * If it reached to end of the port, it will return NULL.
+ * @port: pointer to the target port node
+ * @prev: previous endpoint node, or NULL to get first
+ *
+ * Return: An 'endpoint' node pointer with refcount incremented. Refcount
+ * of the passed @prev node is decremented.
+ */
+struct device_node *of_graph_get_next_port_endpoint(const struct device_node *port,
+						    struct device_node *prev)
+{
+	while (1) {
+		prev = of_get_next_child(port, prev);
+		if (!prev)
+			break;
+		if (WARN(!of_node_name_eq(prev, "endpoint"),
+			 "non endpoint node is used (%pOF)", prev))
+			continue;
+
+		break;
+	}
+
+	return prev;
+}
+EXPORT_SYMBOL(of_graph_get_next_port_endpoint);
+
 /**
  * of_graph_get_next_endpoint() - get next endpoint node
  * @parent: pointer to the parent device node
include/linux/of_graph.h +21 -0
index 44518f3583a44..a692d9d979a64 100644
@@ -50,6 +50,18 @@ struct of_endpoint {
 	for (struct device_node *child __free(device_node) = of_graph_get_next_port(parent, NULL);\
 	     child != NULL; child = of_graph_get_next_port(parent, child))
 
+/**
+ * for_each_of_graph_port_endpoint - iterate over every endpoint in a port node
+ * @parent: parent port node
+ * @child: loop variable pointing to the current endpoint node
+ *
+ * When breaking out of the loop, and continue to use the @child, you need to
+ * use return_ptr(@child) or no_free_ptr(@child) not to call __free() for it.
+ */
+#define for_each_of_graph_port_endpoint(parent, child)			\
+	for (struct device_node *child __free(device_node) = of_graph_get_next_port_endpoint(parent, NULL);\
+	     child != NULL; child = of_graph_get_next_port_endpoint(parent, child))
+
 #ifdef CONFIG_OF
 bool of_graph_is_present(const struct device_node *node);
 int of_graph_parse_endpoint(const struct device_node *node,
@@ -61,6 +73,8 @@ struct device_node *of_graph_get_next_endpoint(const struct device_node *parent,
 					struct device_node *previous);
 struct device_node *of_graph_get_next_port(const struct device_node *parent,
 					   struct device_node *port);
+struct device_node *of_graph_get_next_port_endpoint(const struct device_node *port,
+						    struct device_node *prev);
 struct device_node *of_graph_get_endpoint_by_regs(
 		const struct device_node *parent, int port_reg, int reg);
 struct device_node *of_graph_get_remote_endpoint(
@@ -114,6 +128,13 @@ static inline struct device_node *of_graph_get_next_port(
 	return NULL;
 }
 
+static inline struct device_node *of_graph_get_next_port_endpoint(
+					const struct device_node *parent,
+					struct device_node *previous)
+{
+	return NULL;
+}
+
 static inline struct device_node *of_graph_get_endpoint_by_regs(
 		const struct device_node *parent, int port_reg, int reg)
 {
Signature · 1 line
2.53.0
author maintainer
#956 lore raw

PATCH 915/1376 drm/sun4i: tcon: Set output mux for DSI and LVDS

In reply to Greg Kroah-Hartman

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

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

From: Jernej Skrabec <[email protected]>

[ Upstream commit 9c90199b39637ad94253c4fd8e7b1333ca1d3e0d ]

DSI and LVDS skip output mux setup, so TCON TOP cannot route the selected mixer. Configure them like other channel 0 outputs.

In practice this matters for D1, where channel 0 TCONs are fed through TCON TOP. The remaining set_mux implementations only handle TMDS and return an error for other encoder types, as before.

---
 drivers/gpu/drm/sun4i/sun4i_tcon.c | 2 ++
 1 file changed, 2 insertions(+)
drivers/gpu/drm/sun4i/sun4i_tcon.c +2 -0
index a1a2c845ade0c..1b710013b9cb3 100644
@@ -717,9 +717,11 @@ void sun4i_tcon_mode_set(struct sun4i_tcon *tcon,
 	case DRM_MODE_ENCODER_DSI:
 		/* DSI is tied to special case of CPU interface */
 		sun4i_tcon0_mode_set_cpu(tcon, encoder, mode);
+		sun4i_tcon_set_mux(tcon, 0, encoder);
 		break;
 	case DRM_MODE_ENCODER_LVDS:
 		sun4i_tcon0_mode_set_lvds(tcon, encoder, mode);
+		sun4i_tcon_set_mux(tcon, 0, encoder);
 		break;
 	case DRM_MODE_ENCODER_NONE:
 		sun4i_tcon0_mode_set_rgb(tcon, encoder, mode);
Signature · 1 line
2.53.0
author maintainer
#957 lore raw

PATCH 916/1376 drm/sun4i: tcon: Drop TCON TOP device reference

In reply to Greg Kroah-Hartman

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

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

From: Jernej Skrabec <[email protected]>

[ Upstream commit 8208832a38ff3d2560eb8a77a9d7a2f17d8ebcdc ]

of_find_device_by_node() takes a device reference. Drop it after mux configuration succeeds.

---
 drivers/gpu/drm/sun4i/sun4i_tcon.c | 19 +++++++++----------
 1 file changed, 9 insertions(+), 10 deletions(-)
drivers/gpu/drm/sun4i/sun4i_tcon.c +9 -10
index 1b710013b9cb3..901e84de5aef9 100644
@@ -1409,7 +1409,7 @@ static int sun8i_r40_tcon_tv_set_mux(struct sun4i_tcon *tcon,
 {
 	struct device_node *port, *remote;
 	struct platform_device *pdev;
-	int id, ret;
+	int id, ret = 0;
 
 	/* find TCON TOP platform device and TCON id */
 
@@ -1432,21 +1432,20 @@ static int sun8i_r40_tcon_tv_set_mux(struct sun4i_tcon *tcon,
 	if (IS_ENABLED(CONFIG_DRM_SUN8I_TCON_TOP) &&
 	    encoder->encoder_type == DRM_MODE_ENCODER_TMDS) {
 		ret = sun8i_tcon_top_set_hdmi_src(&pdev->dev, id);
-		if (ret) {
-			put_device(&pdev->dev);
-			return ret;
-		}
+		if (ret)
+			goto out_put_device;
 	}
 
 	if (IS_ENABLED(CONFIG_DRM_SUN8I_TCON_TOP)) {
 		ret = sun8i_tcon_top_de_config(&pdev->dev, tcon->id, id);
-		if (ret) {
-			put_device(&pdev->dev);
-			return ret;
-		}
+		if (ret)
+			goto out_put_device;
 	}
 
-	return 0;
+out_put_device:
+	put_device(&pdev->dev);
+
+	return ret;
 }
 
 static const struct sun4i_tcon_quirks sun4i_a10_quirks = {
Signature · 1 line
2.53.0
author maintainer
#958 lore raw

PATCH 917/1376 drm/sun4i: hdmi: Dont leak sync polarity bits into packet control

In reply to Greg Kroah-Hartman

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

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

From: Jernej Skrabec <[email protected]>

[ Upstream commit f5c3b1b0d228624786d22973a20c908c84e1a576 ]

sun4i_hdmi_enable() keeps using the same variable after it programmed the video timing polarity register with it. The leftover TX_CLK, HSYNC and VSYNC bits are then ORed into the packet control register, where each nibble selects the packet type sent in one slot.

As a result, slot 0 selects packet type 3 instead of the AVI infoframe whenever the mode has positive HSYNC polarity, and the TX_CLK bits set nibbles which the driver never programs.

Assign the packet types instead of ORing them into the stale value.

---
 drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c +1 -1
index b3649449de302..a48bef891f05a 100644
@@ -146,7 +146,7 @@ static void sun4i_hdmi_enable(struct drm_encoder *encoder,
 
 	drm_atomic_helper_connector_hdmi_update_infoframes(connector, state);
 
-	val |= SUN4I_HDMI_PKT_CTRL_TYPE(0, SUN4I_HDMI_PKT_AVI);
+	val = SUN4I_HDMI_PKT_CTRL_TYPE(0, SUN4I_HDMI_PKT_AVI);
 	val |= SUN4I_HDMI_PKT_CTRL_TYPE(1, SUN4I_HDMI_PKT_END);
 	writel(val, hdmi->base + SUN4I_HDMI_PKT_CTRL_REG(0));
 
Signature · 1 line
2.53.0
author maintainer
#959 lore raw

PATCH 918/1376 drm/sun4i: crtc: Propagate layer initialization error

In reply to Greg Kroah-Hartman

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

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

From: Jernej Skrabec <[email protected]>

[ Upstream commit 7061ff05ed4a3cf16e83f7e3ad09cbd212508a32 ]

sun4i_crtc_init() returns plain NULL when layer initialization fails, while all its other error paths return an error pointer. The only caller, sun4i_tcon_bind(), checks the result with IS_ERR() and happily continues with tcon->crtc set to NULL. sun4i_rgb_init() and sun4i_lvds_init() then dereference it in drm_crtc_mask(), which oopses.

Return the error pointer instead.

---
 drivers/gpu/drm/sun4i/sun4i_crtc.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/gpu/drm/sun4i/sun4i_crtc.c +1 -1
index 18e74047b0f56..3e90aeb68c480 100644
@@ -208,7 +208,7 @@ struct sun4i_crtc *sun4i_crtc_init(struct drm_device *drm,
 	planes = sunxi_engine_layers_init(drm, engine);
 	if (IS_ERR(planes)) {
 		dev_err(drm->dev, "Couldn't create the planes\n");
-		return NULL;
+		return ERR_CAST(planes);
 	}
 
 	/* find primary and cursor planes for drm_crtc_init_with_planes */
Signature · 1 line
2.53.0
author maintainer
#960 lore raw

PATCH 919/1376 drm/sun4i: tcon: Drop remote endpoint reference

In reply to Greg Kroah-Hartman

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

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

From: Jernej Skrabec <[email protected]>

[ Upstream commit 3f77e4072630e2301efdbe521e7fca10311043ec ]

sun4i_tcon_of_get_id_from_port() never drops the reference taken by of_graph_get_remote_endpoint(). The function is not only called during bind, but also on every mode set through sun8i_r40_tcon_tv_set_mux(), so the leak accumulates.

---
 drivers/gpu/drm/sun4i/sun4i_tcon.c | 1 +
 1 file changed, 1 insertion(+)
drivers/gpu/drm/sun4i/sun4i_tcon.c +1 -0
index 901e84de5aef9..ab4409dcbf302 100644
@@ -972,6 +972,7 @@ static int sun4i_tcon_of_get_id_from_port(struct device_node *port)
 			continue;
 
 		ret = of_property_read_u32(remote, "reg", &reg);
+		of_node_put(remote);
 		if (ret)
 			continue;
 
Signature · 1 line
2.53.0
author maintainer
#961 lore raw

PATCH 920/1376 drm/sun4i: dw-hdmi: Drop TCON TOP port reference

In reply to Greg Kroah-Hartman

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

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

From: Jernej Skrabec <[email protected]>

[ Upstream commit d2a242e5688a17b79c89cd966cd31820c5096d80 ]

When the HDMI controller is fed by TCON TOP, the port node used to enumerate the possible CRTCs is never released.

---
 drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c | 2 ++
 1 file changed, 2 insertions(+)
drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c +2 -0
index 4727dfaa8fb98..af9be9ba8b96a 100644
@@ -83,6 +83,8 @@ static u32 sun8i_dw_hdmi_find_possible_crtcs(struct drm_device *drm,
 				of_node_put(remote_port);
 			}
 		}
+
+		of_node_put(port);
 	} else {
 		crtcs = drm_of_find_possible_crtcs(drm, node);
 	}
Signature · 1 line
2.53.0
author maintainer
#962 lore raw

PATCH 921/1376 drm/sun4i: hdmi-phy: Fix H6 8-bit MPLL config at 594 MHz

In reply to Greg Kroah-Hartman

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

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

From: Jernej Skrabec <[email protected]>

[ Upstream commit 0ba6deddaae74f0539c0303bfb5f860adbe1a68b ]

The 8-bit entry of the last MPLL row (594 MHz) doesn't lock reliably on H6. 4K@60 RGB/YUV444, which is the mode that reaches this entry, doesn't come up.

Align the value with the vendor driver. Other entries are left alone, they are used by lower pixel clocks which work fine.

Tested with 4K@60 on a LG TV.

---
 drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c +1 -1
index 4fa69c463dc46..489ea94693ffa 100644
@@ -91,7 +91,7 @@ static const struct dw_hdmi_mpll_config sun50i_h6_mpll_cfg[] = {
 		},
 	},  {
 		594000000, {
-			{ 0x1a40, 0x0003 },
+			{ 0x1a7c, 0x0003 },
 			{ 0x3b4c, 0x0003 },
 			{ 0x5a64, 0x0003 },
 		},
Signature · 1 line
2.53.0
author maintainer
#963 lore raw

PATCH 922/1376 cpufreq: imx6q: fix devres accumulation across driver rebind

In reply to Greg Kroah-Hartman

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

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

From: Karl Mehltretter <[email protected]>

[ Upstream commit 22c23c72c3b21fa3ec3db5070dfc0582794e0ef9 ]

imx6_soc_volt is allocated with devm_kcalloc(cpu_dev, ...), where cpu_dev is the CPU device from get_cpu_device(0). That device is never unbound, so its devres list is never released, and imx6q_cpufreq_remove() does not free the array either. Every probe therefore adds an allocation that stays for the lifetime of the system.

Allocate against the platform device instead. Its devres is released when the driver is unbound, which is exactly the lifetime the array wants:
imx6q_set_target() reads it, and nothing may reach that after cpufreq_unregister_driver().

That makes the array actually go away on unbind, so also clear the file-scope pointer in remove and on the failed-probe path, rather than leave it pointing at memory devres is about to release.

Tested by rebinding the driver on qemu's mcimx6ul-evk.

Fixes b4573d1d657a ("cpufreq: imx6q: correct VDDSOC/PU voltage scaling when cpufreq is changed")
Assisted-by Claude:claude-opus-5
---
 drivers/cpufreq/imx6q-cpufreq.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/cpufreq/imx6q-cpufreq.c +3 -1
index 33c1df7f683e4..853534f0fec88 100644
@@ -401,7 +401,7 @@ static int imx6q_cpufreq_probe(struct platform_device *pdev)
 	}
 
 	/* Make imx6_soc_volt array's size same as arm opp number */
-	imx6_soc_volt = devm_kcalloc(cpu_dev, num, sizeof(*imx6_soc_volt),
+	imx6_soc_volt = devm_kcalloc(&pdev->dev, num, sizeof(*imx6_soc_volt),
 				     GFP_KERNEL);
 	if (imx6_soc_volt == NULL) {
 		ret = -ENOMEM;
@@ -486,6 +486,7 @@ static int imx6q_cpufreq_probe(struct platform_device *pdev)
 	return 0;
 
 free_freq_table:
+	imx6_soc_volt = NULL;
 	dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table);
 out_free_opp:
 	dev_pm_opp_of_remove_table(cpu_dev);
@@ -507,6 +508,7 @@ static int imx6q_cpufreq_probe(struct platform_device *pdev)
 static void imx6q_cpufreq_remove(struct platform_device *pdev)
 {
 	cpufreq_unregister_driver(&imx6q_cpufreq_driver);
+	imx6_soc_volt = NULL;
 	dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table);
 	dev_pm_opp_of_remove_table(cpu_dev);
 	regulator_put(arm_reg);
Signature · 1 line
2.53.0
author maintainer
#964 lore raw

PATCH 923/1376 cpufreq: imx6q: fix out-of-bounds write when probed more than once

In reply to Greg Kroah-Hartman

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

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

From: Karl Mehltretter <[email protected]>

[ Upstream commit 8c3afcf27fa4582c1ab912503dc8a4ebb8dc0f82 ]

imx6_soc_volt is allocated fresh on every probe, sized to the number of ARM OPPs:

	imx6_soc_volt = devm_kcalloc(cpu_dev, num, sizeof(*imx6_soc_volt),
				     GFP_KERNEL);

but it is filled through soc_opp_count, which has static storage and is never reset. A second bind after an unbind keeps indexing from where the first one stopped, and writes past the end of the new array.

Unbinding and rebinding the driver on qemu's mcimx6ul-evk, under KASAN:

  BUG: KASAN: slab-out-of-bounds in imx6q_cpufreq_probe+0x3b0/0xa34
  Write of size 4 at addr c5e90480 by task binder/73
   imx6q_cpufreq_probe from platform_probe+0x88/0xe4
   platform_probe from really_probe+0x108/0x384
   bind_store from kernfs_fop_write_iter+0x1b4/0x28c

The write lands one u32 past the end of the allocation.

soc_opp_count is only read a few lines below the loop that fills it, so it never needed static storage. Make it a local.

Fixes b4573d1d657a ("cpufreq: imx6q: correct VDDSOC/PU voltage scaling when cpufreq is changed")
Assisted-by Claude:claude-opus-5
---
 drivers/cpufreq/imx6q-cpufreq.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/cpufreq/imx6q-cpufreq.c +1 -1
index 853534f0fec88..51520307c4b04 100644
@@ -55,7 +55,6 @@ static unsigned int max_freq;
 static unsigned int transition_latency;
 
 static u32 *imx6_soc_volt;
-static u32 soc_opp_count;
 
 static int imx6q_set_target(struct cpufreq_policy *policy, unsigned int index)
 {
@@ -331,6 +330,7 @@ static int imx6q_cpufreq_probe(struct platform_device *pdev)
 	const struct property *prop;
 	const __be32 *val;
 	u32 nr, i, j;
+	u32 soc_opp_count = 0;
 
 	cpu_dev = get_cpu_device(0);
 	if (!cpu_dev) {
Signature · 1 line
2.53.0
author maintainer
#965 lore raw

PATCH 924/1376 IB/isert: delay the final Login Response until the session is registered

In reply to Greg Kroah-Hartman

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

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

From: Yehyeong Lee <[email protected]>

[ Upstream commit 464f5afa92d071a226f88424803b0fcf88093ede ]

isert_put_login_tx() puts the final Login Response on the wire before __transport_register_session(), which iscsi_post_login_handler() reaches only after iscsi_target_do_login() returns. An initiator that issues a SCSI command as soon as it sees that response can have it executed against an se_session whose se_tpg is still NULL, and the ib-comp-wq worker oopses on the NULL dereference.

 Oops: general protection fault, probably for non-canonical address 0xdffffc000000000f: 0000 [#1] SMP KASAN NOPTI
 KASAN: null-ptr-deref in range [0x0000000000000078-0x000000000000007f]
 CPU: 0 UID: 0 PID: 178 Comm: kworker/0:1H Not tainted 7.2.0-rc5-V2CTL-gf5098b6bae76 #10 PREEMPT(lazy)
 Hardware name: QEMU Ubuntu 24.04 PC v2 (i440FX + PIIX, arch_caps fix, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
 Workqueue: ib-comp-wq ib_cq_poll_work
 RIP: 0010:target_submit+0xbe/0x390
 Code: fa 48 c1 ea 03 80 3c 02 00 0f 85 89 02 00 00 48 b8 00 00 00 00 00 fc ff df 4d 8b 64 24 18 49 8d 7c 24 78 48 89 fa 48 c1 ea 03 <80> 3c 02 00 0f 85 5a 02 00 00 48 8d 7b 78 4d 8b 6c 24 78 48 b8 00
 RSP: 0018:ffff8881058cfa78 EFLAGS: 00010206
 RAX: dffffc0000000000 RBX: ffff88810c78c6f0 RCX: ffffffff964bb363
 RDX: 000000000000000f RSI: 00000000fffffe00 RDI: 0000000000000078
 RBP: 1ffff11020b19f52 R08: 0000000000000001 R09: ffffed1020b19f52
 R10: 0000000000000003 R11: ffff88810596c000 R12: 0000000000000000
 R13: ffff88810c61b000 R14: ffff88810c6a3400 R15: ffff88810c61b044
 FS:  0000000000000000(0000) GS:ffff8881822b2000(0000) knlGS:0000000000000000
 CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
 CR2: 00007f1f1b83c000 CR3: 000000006fe72001 CR4: 0000000000770ef0
 PKRU: 55555554
 Call Trace:
  <TASK>
  ? __pfx__raw_spin_lock_bh+0x10/0x10
  ? __pfx_target_submit+0x10/0x10
  ? mutex_lock+0x81/0xe0
  ? __pfx_mutex_lock+0x10/0x10
  ? iscsit_execute_cmd+0x650/0x850
  iscsit_sequence_cmd+0x186/0x3d0
  iscsit_process_scsi_cmd+0x87/0x300
  isert_recv_done+0x1002/0x2390
  ? __pfx_isert_recv_done+0x10/0x10
  ? rxe_poll_cq+0x253/0x3d0
  ? finish_task_switch.isra.0+0x1dc/0xa70
  __ib_process_cq+0xe1/0x390
  ib_cq_poll_work+0x46/0x150
  process_one_work+0x633/0x1030
  ? assign_work+0x11d/0x370
  worker_thread+0x45b/0xd10
  ? __pfx_worker_thread+0x10/0x10
  ? __pfx_worker_thread+0x10/0x10
  kthread+0x2c6/0x3b0
  ? recalc_sigpending+0x15c/0x1e0
  ? __pfx_kthread+0x10/0x10
  ret_from_fork+0x36e/0x5a0
  ? __pfx_ret_from_fork+0x10/0x10
  ? __switch_to+0x572/0xdd0
  ? __pfx_kthread+0x10/0x10
  ret_from_fork_asm+0x1a/0x30
  </TASK>
 Modules linked in:
 ---[ end trace 0000000000000000 ]---

Delay the final Login Response instead. isert_get_rx_pdu() runs from iscsi_target_rx_thread() after conn->rx_login_comp, completed by iscsi_post_login_handler() after __transport_register_session(); iscsi-TCP and cxgbit already take PDUs from that thread, isert alone does not. The buffers are still posted first, so the initiator's first command does not meet an empty receive queue and nothing depends on RNR flow control, and the header and payload live in isert_conn, not in the struct iscsi_login that iscsi_target_nego_release() frees first.

Over rxe, 400 login cycles per run, the oops appeared in 10 of 20 unpatched runs and in none of 20 runs with this patch. An initiator that never waits is handled by the next patch.

Not tested: iWARP, discovery sessions over iSER, and real HCAs.

Fixes b8d26b3be8b3 ("iser-target: Add iSCSI Extensions for RDMA (iSER) target driver")
---
 drivers/infiniband/ulp/isert/ib_isert.c | 20 ++++++++++++++++++--
 drivers/infiniband/ulp/isert/ib_isert.h |  1 +
 2 files changed, 19 insertions(+), 2 deletions(-)
drivers/infiniband/ulp/isert/ib_isert.c +18 -2
index 1fcaf7df8b398..d0e3d1cf5b41b 100644
@@ -59,6 +59,8 @@ static void isert_recv_done(struct ib_cq *cq, struct ib_wc *wc);
 static void isert_send_done(struct ib_cq *cq, struct ib_wc *wc);
 static void isert_login_recv_done(struct ib_cq *cq, struct ib_wc *wc);
 static void isert_login_send_done(struct ib_cq *cq, struct ib_wc *wc);
+static void isert_unmap_tx_desc(struct iser_tx_desc *tx_desc,
+				struct ib_device *ib_dev);
 
 static int isert_sg_tablesize_set(const char *val, const struct kernel_param *kp)
 {
@@ -498,6 +500,8 @@ isert_connect_release(struct isert_conn *isert_conn)
 	if (isert_conn->qp)
 		isert_destroy_qp(isert_conn);
 
+	isert_unmap_tx_desc(&isert_conn->login_tx_desc, device->ib_device);
+
 	if (isert_conn->login_desc)
 		isert_free_login_buf(isert_conn);
 
@@ -958,14 +962,17 @@ isert_put_login_tx(struct iscsit_conn *conn, struct iscsi_login *login,
 			mutex_lock(&isert_conn->mutex);
 			isert_conn->state = ISER_CONN_FULL_FEATURE;
 			mutex_unlock(&isert_conn->mutex);
-			goto post_send;
+
+			/* Sent from isert_get_rx_pdu() after registration. */
+			isert_conn->login_rsp_pending = true;
+			return 0;
 		}
 
 		ret = isert_login_post_recv(isert_conn);
 		if (ret)
 			return ret;
 	}
-post_send:
+
 	ret = isert_login_post_send(isert_conn, tx_desc);
 	if (ret)
 		return ret;
@@ -2625,8 +2632,17 @@ static void isert_free_conn(struct iscsit_conn *conn)
 
 static void isert_get_rx_pdu(struct iscsit_conn *conn)
 {
+	struct isert_conn *isert_conn = conn->context;
 	struct completion comp;
 
+	/* The session is registered by now; see isert_put_login_tx(). */
+	if (isert_conn->login_rsp_pending) {
+		isert_conn->login_rsp_pending = false;
+		if (isert_login_post_send(isert_conn,
+					  &isert_conn->login_tx_desc))
+			return;
+	}
+
 	init_completion(&comp);
 
 	wait_for_completion_interruptible(&comp);
drivers/infiniband/ulp/isert/ib_isert.h +1 -0
index 0b2dfd6e7e270..0bac5aa66c802 100644
@@ -178,6 +178,7 @@ struct isert_conn {
 	struct completion	login_comp;
 	struct completion	login_req_comp;
 	struct iser_tx_desc	login_tx_desc;
+	bool			login_rsp_pending;
 	struct rdma_cm_id	*cm_id;
 	struct ib_qp		*qp;
 	struct ib_cq		*cq;
Signature · 1 line
2.53.0
author maintainer
#966 lore raw

PATCH 925/1376 IB/isert: post the full-feature receive buffers after session registration

In reply to Greg Kroah-Hartman

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

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

From: Yehyeong Lee <[email protected]>

[ Upstream commit 5247dde9daac7e107853b6fea043f7f47be033f7 ]

isert_put_login_tx() posts the full-feature receive buffers before __transport_register_session() runs, so an initiator that does not wait for the final Login Response can still have a SCSI command executed against an se_session whose se_tpg is NULL - the same oops as the previous patch, at target_submit+0xbe.

Post them from isert_get_rx_pdu(), which the previous patch already uses to send that response, and post them before that send: the receive queue is filled at the moment the initiator is told it may use it. Allocating there keeps the existing property that a memory allocation failure cannot happen once the final Login Response is on the wire.

The receive queue is already empty between the final Login Request and isert_post_recvm(); this moves the second point later, from a median of 92 us to 172 us over 1200 logins. Only an initiator that sends before it has been told to can reach that window, and on IB and RoCE its send is retried there until the buffers appear - isert_rdma_accept() asks for rnr_retry_count = 7. iWARP has no RNR flow control, so there the same send terminates the connection instead.

Measured over rxe, 400 login cycles per run, with an initiator that does not wait: an instrumented build counted no entries to isert_recv_done() before the buffers are posted in 10 runs, where that initiator oopsed 8 of 10 unpatched runs and 5 of 10 with only the previous patch.

Not tested: iWARP, discovery sessions over iSER, and real HCAs.

Fixes b8d26b3be8b3 ("iser-target: Add iSCSI Extensions for RDMA (iSER) target driver")
---
 drivers/infiniband/ulp/isert/ib_isert.c | 41 ++++++++++++-------------
 1 file changed, 20 insertions(+), 21 deletions(-)
drivers/infiniband/ulp/isert/ib_isert.c +20 -21
index d0e3d1cf5b41b..6483a55170cd1 100644
@@ -949,21 +949,7 @@ isert_put_login_tx(struct iscsit_conn *conn, struct iscsi_login *login,
 	}
 	if (!login->login_failed) {
 		if (login->login_complete) {
-			ret = isert_alloc_rx_descriptors(isert_conn);
-			if (ret)
-				return ret;
-
-			ret = isert_post_recvm(isert_conn,
-					       ISERT_QP_MAX_RECV_DTOS);
-			if (ret)
-				return ret;
-
-			/* Now we are in FULL_FEATURE phase */
-			mutex_lock(&isert_conn->mutex);
-			isert_conn->state = ISER_CONN_FULL_FEATURE;
-			mutex_unlock(&isert_conn->mutex);
-
-			/* Sent from isert_get_rx_pdu() after registration. */
+			/* Posted and sent from isert_get_rx_pdu(). */
 			isert_conn->login_rsp_pending = true;
 			return 0;
 		}
@@ -2635,13 +2621,26 @@ static void isert_get_rx_pdu(struct iscsit_conn *conn)
 	struct isert_conn *isert_conn = conn->context;
 	struct completion comp;
 
+	/* The login timeout timer can fail the login after isert_put_login_tx(). */
+	if (!isert_conn->login_rsp_pending)
+		return;
+
+	isert_conn->login_rsp_pending = false;
+
 	/* The session is registered by now; see isert_put_login_tx(). */
-	if (isert_conn->login_rsp_pending) {
-		isert_conn->login_rsp_pending = false;
-		if (isert_login_post_send(isert_conn,
-					  &isert_conn->login_tx_desc))
-			return;
-	}
+	if (isert_alloc_rx_descriptors(isert_conn))
+		return;
+
+	if (isert_post_recvm(isert_conn, ISERT_QP_MAX_RECV_DTOS))
+		return;
+
+	/* Now we are in FULL_FEATURE phase */
+	mutex_lock(&isert_conn->mutex);
+	isert_conn->state = ISER_CONN_FULL_FEATURE;
+	mutex_unlock(&isert_conn->mutex);
+
+	if (isert_login_post_send(isert_conn, &isert_conn->login_tx_desc))
+		return;
 
 	init_completion(&comp);
 
Signature · 1 line
2.53.0
author maintainer
#967 lore raw

PATCH 926/1376 RDMA/siw: Fix use-after-free in siw_accept()

In reply to Greg Kroah-Hartman

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

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

From: Shuangpeng Bai <[email protected]>

[ Upstream commit a9394971825933074032794a5feee5211509c774 ]

siw_accept() looks up the QP supplied by userspace. If that QP is already in RTS, the function jumps to error cleanup before associating the incoming CEP with it.

The cleanup tests whether qp->cep is non-NULL and assumes the current call installed the association. However, qp->cep can point to the CEP of an existing connection. The cleanup then drops a reference from the incoming cep, not qp->cep. Once the incoming endpoint loses its remaining references, this can free it before the subsequent cep->qp store, causing a use-after-free. It also clears the existing QP association.

Only release the association reference when qp->cep is the incoming CEP. This preserves an existing association and avoids accessing the freed endpoint.

---
 drivers/infiniband/sw/siw/siw_cm.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/infiniband/sw/siw/siw_cm.c +1 -1
index 49ff121f77fe4..bb7d909639071 100644
@@ -1701,7 +1701,7 @@ int siw_accept(struct iw_cm_id *id, struct iw_cm_conn_param *params)
 	cep->state = SIW_EPSTATE_CLOSED;
 
 	siw_free_cm_id(cep);
-	if (qp->cep) {
+	if (qp->cep == cep) {
 		siw_cep_put(cep);
 		qp->cep = NULL;
 	}
Signature · 1 line
2.53.0
author maintainer
#968 lore raw

PATCH 927/1376 module: replace use of system_wq with system_dfl_wq

In reply to Greg Kroah-Hartman

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

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

From: Marco Crivellari <[email protected]>

[ Upstream commit 581ac2d4a58b81669cc6abf645a558bce5cf14ab ]

Currently if a user enqueues a work item using schedule_delayed_work() the used wq is "system_wq" (per-cpu wq) while queue_delayed_work() use WORK_CPU_UNBOUND (used when a cpu is not specified). The same applies to schedule_work() that is using system_wq and queue_work(), that makes use again of WORK_CPU_UNBOUND.

This lack of consistency cannot be addressed without refactoring the API.

This continues the effort to refactor workqueue APIs, which began with the introduction of new workqueues and a new alloc_workqueue flag in:

commit 128ea9f6ccfb ("workqueue: Add system_percpu_wq and system_dfl_wq") commit 930c2ea566af ("workqueue: Add new WQ_PERCPU flag")

Switch to using system_dfl_wq, the new unbound workqueue, because the users do not benefit from a per-cpu workqueue.

Stable-dep-of: 5eecb11b543f ("module/dups: Fix use-after-free in kmod_dup_req lifetime handling")

---
 kernel/module/dups.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
kernel/module/dups.c +2 -2
index bd2149fbe1173..0b633f2edda6b 100644
@@ -113,7 +113,7 @@ static void kmod_dup_request_complete(struct work_struct *work)
 	 * let this linger forever as this is just a boot optimization for
 	 * possible abuses of vmalloc() incurred by finit_module() thrashing.
 	 */
-	queue_delayed_work(system_wq, &kmod_req->delete_work, 60 * HZ);
+	queue_delayed_work(system_dfl_wq, &kmod_req->delete_work, 60 * HZ);
 }
 
 bool kmod_dup_request_exists_wait(char *module_name, bool wait, int *dup_ret)
@@ -240,7 +240,7 @@ void kmod_dup_request_announce(char *module_name, int ret)
 	 * There is no rush. But we also don't want to hold the
 	 * caller up forever or introduce any boot delays.
 	 */
-	queue_work(system_wq, &kmod_req->complete_work);
+	queue_work(system_dfl_wq, &kmod_req->complete_work);
 
 out:
 	mutex_unlock(&kmod_dup_mutex);
Signature · 1 line
2.53.0
author maintainer
#969 lore raw

PATCH 928/1376 module: use strscpy() to copy module names in stats and dup tracking

In reply to Greg Kroah-Hartman

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

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

From: Naveen Kumar Chaudhary <[email protected]>

[ Upstream commit 93c29ebd1622fb0670701e1c1b3a978a5cac08b7 ]

Both try_add_failed_module() and kmod_dup_request_exists_wait() use memcpy() with strlen() to copy module names into fixed-size char[MODULE_NAME_LEN] buffers. Neither performs a bounds check on the copy. Current callers always pass names originating from mod->name (itself char[MODULE_NAME_LEN]), so this is not exploitable today. However both functions accept a plain const char * with no documented length contract, making them latent buffer overflows if a future caller passes a longer string.

Replace memcpy() with strscpy() in both sites, which bounds the copy to MODULE_NAME_LEN and always NUL-terminates.

Stable-dep-of: 5eecb11b543f ("module/dups: Fix use-after-free in kmod_dup_req lifetime handling")

---
 kernel/module/dups.c  | 2 +-
 kernel/module/stats.c | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)
kernel/module/dups.c +1 -1
index 0b633f2edda6b..6ecc42193de24 100644
@@ -129,7 +129,7 @@ bool kmod_dup_request_exists_wait(char *module_name, bool wait, int *dup_ret)
 	if (!new_kmod_req)
 		return false;
 
-	memcpy(new_kmod_req->name, module_name, strlen(module_name));
+	strscpy(new_kmod_req->name, module_name);
 	INIT_WORK(&new_kmod_req->complete_work, kmod_dup_request_complete);
 	INIT_DELAYED_WORK(&new_kmod_req->delete_work, kmod_dup_request_delete);
 	init_completion(&new_kmod_req->first_req_done);
kernel/module/stats.c +1 -1
index 3ba0e98b3c910..2a4e2f6708965 100644
@@ -253,7 +253,7 @@ int try_add_failed_module(const char *name, enum fail_dup_mod_reason reason)
 	mod_fail = kzalloc(sizeof(*mod_fail), GFP_KERNEL);
 	if (!mod_fail)
 		return -ENOMEM;
-	memcpy(mod_fail->name, name, strlen(name));
+	strscpy(mod_fail->name, name);
 	__set_bit(reason, &mod_fail->dup_fail_mask);
 	atomic_long_inc(&mod_fail->count);
 	list_add_rcu(&mod_fail->list, &dup_failed_modules);
Signature · 1 line
2.53.0
author maintainer
#970 lore raw

PATCH 929/1376 module/dups: Inform duplicate requests about the result directly

In reply to Greg Kroah-Hartman

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

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

From: Petr Pavlu <[email protected]>

[ Upstream commit d258ed8a86bb46bbbbc84fb914478259a1e694a4 ]

When kmod_dup_request_announce() announces the completion of a request_module() call to duplicate waiters, it queues a work item to invoke kmod_dup_request_complete(), and only that function calls complete_all().

This adds an arbitrary delay that is unnecessary and provides little benefit. Call complete_all() directly from kmod_dup_request_announce() instead.

Stable-dep-of: 5eecb11b543f ("module/dups: Fix use-after-free in kmod_dup_req lifetime handling")

---
 kernel/module/dups.c | 43 +++++++++----------------------------------
 1 file changed, 9 insertions(+), 34 deletions(-)
kernel/module/dups.c +9 -34
index 6ecc42193de24..7b9b08031d7d2 100644
@@ -48,7 +48,6 @@ struct kmod_dup_req {
 	struct list_head list;
 	char name[MODULE_NAME_LEN];
 	struct completion first_req_done;
-	struct work_struct complete_work;
 	struct delayed_work delete_work;
 	int dup_ret;
 };
@@ -93,29 +92,6 @@ static void kmod_dup_request_delete(struct work_struct *work)
 	kfree(kmod_req);
 }
 
-static void kmod_dup_request_complete(struct work_struct *work)
-{
-	struct kmod_dup_req *kmod_req;
-
-	kmod_req = container_of(work, struct kmod_dup_req, complete_work);
-
-	/*
-	 * This will ensure that the kernel will let all the waiters get
-	 * informed its time to check the return value. It's time to
-	 * go home.
-	 */
-	complete_all(&kmod_req->first_req_done);
-
-	/*
-	 * Now that we have allowed prior request_module() calls to go on
-	 * with life, let's schedule deleting this entry. We don't have
-	 * to do it right away, but we *eventually* want to do it so to not
-	 * let this linger forever as this is just a boot optimization for
-	 * possible abuses of vmalloc() incurred by finit_module() thrashing.
-	 */
-	queue_delayed_work(system_dfl_wq, &kmod_req->delete_work, 60 * HZ);
-}
-
 bool kmod_dup_request_exists_wait(char *module_name, bool wait, int *dup_ret)
 {
 	struct kmod_dup_req *kmod_req, *new_kmod_req;
@@ -130,7 +106,6 @@ bool kmod_dup_request_exists_wait(char *module_name, bool wait, int *dup_ret)
 		return false;
 
 	strscpy(new_kmod_req->name, module_name);
-	INIT_WORK(&new_kmod_req->complete_work, kmod_dup_request_complete);
 	INIT_DELAYED_WORK(&new_kmod_req->delete_work, kmod_dup_request_delete);
 	init_completion(&new_kmod_req->first_req_done);
 
@@ -230,17 +205,17 @@ void kmod_dup_request_announce(char *module_name, int ret)
 
 	kmod_req->dup_ret = ret;
 
+	/* Inform all duplicate waiters to check the return value. */
+	complete_all(&kmod_req->first_req_done);
+
 	/*
-	 * If we complete() here we may allow duplicate threads
-	 * to continue before the first one that submitted the
-	 * request. We're in no rush also, given that each and
-	 * every bounce back to userspace is slow we avoid that
-	 * with a slight delay here. So queueue up the completion
-	 * and let duplicates suffer, just wait a tad bit longer.
-	 * There is no rush. But we also don't want to hold the
-	 * caller up forever or introduce any boot delays.
+	 * Now that we have allowed prior request_module() calls to go on
+	 * with life, let's schedule deleting this entry. We don't have
+	 * to do it right away, but we *eventually* want to do it so to not
+	 * let this linger forever as this is just a boot optimization for
+	 * possible abuses of vmalloc() incurred by finit_module() thrashing.
 	 */
-	queue_work(system_dfl_wq, &kmod_req->complete_work);
+	queue_delayed_work(system_dfl_wq, &kmod_req->delete_work, 60 * HZ);
 
 out:
 	mutex_unlock(&kmod_dup_mutex);
Signature · 1 line
2.53.0
author maintainer
#971 lore raw

PATCH 930/1376 module/dups: Fix use-after-free in kmod_dup_req lifetime handling

In reply to Greg Kroah-Hartman

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

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

From: Petr Pavlu <[email protected]>

[ Upstream commit 5eecb11b543f9f417bcf0dea239ff99c6af65dbd ]

The kmod dups code uses RCU to ensure that a kmod_dup_req instance is freed only after it is no longer referenced. When releasing an instance, the kmod_dup_request_delete() function removes the kmod_dup_req from the dup_kmod_reqs list, waits via synchronize_rcu() and finally frees it. However, this doesn't work correctly because parallel users referencing the instance in kmod_dup_request_exists_wait() don't enter an RCU read-side critical section. This can result in a use-after-free.

The kmod_dup_request_exists_wait() function may need to hold a valid reference to a kmod_dup_req instance across a blocking wait until the corresponding modprobe command completes. This makes it unsuitable for RCU.

Fix the issue by changing the lifecycle management of kmod_dup_req to use reference counting.

Fixes 8660484ed1cf ("module: add debugging auto-load duplicate module support")
---
 kernel/module/dups.c | 56 +++++++++++++++++++++++++++++++-------------
 1 file changed, 40 insertions(+), 16 deletions(-)
kernel/module/dups.c +40 -16
index 7b9b08031d7d2..c21b675c239b7 100644
@@ -30,6 +30,7 @@
 #include <linux/ptrace.h>
 #include <linux/async.h>
 #include <linux/uaccess.h>
+#include <linux/refcount.h>
 
 #include "internal.h"
 
@@ -38,13 +39,12 @@
 static bool enable_dups_trace = IS_ENABLED(CONFIG_MODULE_DEBUG_AUTOLOAD_DUPS_TRACE);
 module_param(enable_dups_trace, bool_enable_only, 0644);
 
-/*
- * Protects dup_kmod_reqs list, adds / removals with RCU.
- */
+/* A mutex-protected list of active kmod requests. */
 static DEFINE_MUTEX(kmod_dup_mutex);
 static LIST_HEAD(dup_kmod_reqs);
 
 struct kmod_dup_req {
+	refcount_t refcount;
 	struct list_head list;
 	char name[MODULE_NAME_LEN];
 	struct completion first_req_done;
@@ -52,12 +52,24 @@ struct kmod_dup_req {
 	int dup_ret;
 };
 
+static void get_kmod_req(struct kmod_dup_req *kmod_req)
+{
+	refcount_inc(&kmod_req->refcount);
+}
+
+static void put_kmod_req(struct kmod_dup_req *kmod_req)
+{
+	if (refcount_dec_and_test(&kmod_req->refcount))
+		kfree(kmod_req);
+}
+
 static struct kmod_dup_req *kmod_dup_request_lookup(char *module_name)
 {
 	struct kmod_dup_req *kmod_req;
 
-	list_for_each_entry_rcu(kmod_req, &dup_kmod_reqs, list,
-				lockdep_is_held(&kmod_dup_mutex)) {
+	lockdep_assert_held(&kmod_dup_mutex);
+
+	list_for_each_entry(kmod_req, &dup_kmod_reqs, list) {
 		if (strlen(kmod_req->name) == strlen(module_name) &&
 		    !memcmp(kmod_req->name, module_name, strlen(module_name))) {
 			return kmod_req;
@@ -86,10 +98,10 @@ static void kmod_dup_request_delete(struct work_struct *work)
 	 * just returning 0.
 	 */
 	mutex_lock(&kmod_dup_mutex);
-	list_del_rcu(&kmod_req->list);
-	synchronize_rcu();
+	list_del(&kmod_req->list);
 	mutex_unlock(&kmod_dup_mutex);
-	kfree(kmod_req);
+
+	put_kmod_req(kmod_req);
 }
 
 bool kmod_dup_request_exists_wait(char *module_name, bool wait, int *dup_ret)
@@ -105,6 +117,7 @@ bool kmod_dup_request_exists_wait(char *module_name, bool wait, int *dup_ret)
 	if (!new_kmod_req)
 		return false;
 
+	refcount_set(&new_kmod_req->refcount, 1);
 	strscpy(new_kmod_req->name, module_name);
 	INIT_DELAYED_WORK(&new_kmod_req->delete_work, kmod_dup_request_delete);
 	init_completion(&new_kmod_req->first_req_done);
@@ -136,10 +149,12 @@ bool kmod_dup_request_exists_wait(char *module_name, bool wait, int *dup_ret)
 		 * keep tab on duplicates later.
 		 */
 		pr_debug("New request_module() for %s\n", module_name);
-		list_add_rcu(&new_kmod_req->list, &dup_kmod_reqs);
+		list_add(&new_kmod_req->list, &dup_kmod_reqs);
 		mutex_unlock(&kmod_dup_mutex);
 		return false;
 	}
+
+	get_kmod_req(kmod_req);
 	mutex_unlock(&kmod_dup_mutex);
 
 	/* We are dealing with a duplicate request now */
@@ -169,7 +184,7 @@ bool kmod_dup_request_exists_wait(char *module_name, bool wait, int *dup_ret)
 		 * calls bail out right away.
 		 */
 		*dup_ret = 0;
-		return true;
+		goto out;
 	}
 
 	/*
@@ -184,12 +199,14 @@ bool kmod_dup_request_exists_wait(char *module_name, bool wait, int *dup_ret)
 					TASK_KILLABLE);
 	if (ret) {
 		*dup_ret = ret;
-		return true;
+		goto out;
 	}
 
 	/* Now the duplicate request has the same exact return value as the first request */
 	*dup_ret = kmod_req->dup_ret;
 
+out:
+	put_kmod_req(kmod_req);
 	return true;
 }
 
@@ -199,15 +216,25 @@ void kmod_dup_request_announce(char *module_name, int ret)
 
 	mutex_lock(&kmod_dup_mutex);
 
+	/*
+	 * Look for a kmod_dup_req previously added in
+	 * kmod_dup_request_exists_wait(). Note that a request_module_nowait()
+	 * without its own kmod_dup_req entry can announce a result of
+	 * a concurrent request_module() call.
+	 */
 	kmod_req = kmod_dup_request_lookup(module_name);
-	if (!kmod_req)
-		goto out;
+	if (!kmod_req || completion_done(&kmod_req->first_req_done)) {
+		mutex_unlock(&kmod_dup_mutex);
+		return;
+	}
 
 	kmod_req->dup_ret = ret;
 
 	/* Inform all duplicate waiters to check the return value. */
 	complete_all(&kmod_req->first_req_done);
 
+	mutex_unlock(&kmod_dup_mutex);
+
 	/*
 	 * Now that we have allowed prior request_module() calls to go on
 	 * with life, let's schedule deleting this entry. We don't have
@@ -216,7 +243,4 @@ void kmod_dup_request_announce(char *module_name, int ret)
 	 * possible abuses of vmalloc() incurred by finit_module() thrashing.
 	 */
 	queue_delayed_work(system_dfl_wq, &kmod_req->delete_work, 60 * HZ);
-
-out:
-	mutex_unlock(&kmod_dup_mutex);
 }
Signature · 1 line
2.53.0
author maintainer
#972 lore raw

PATCH 931/1376 RDMA/erdma: restrict the driver to little-endian systems

In reply to Greg Kroah-Hartman

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

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

From: Leon Romanovsky <[email protected]>

[ Upstream commit a12d9145145b21c50531afb6e3f711b1f34e1465 ]

The eRDMA device interface requires explicit byte ordering, but several DMA-visible values that should be little-endian remain native-endian. Command request payloads are copied verbatim, data-path SQE headers are written without cpu_to_le64(), and kernel doorbell records are assigned plain u64 values. The command completion path also reads a little-endian SQE header without conversion.

These paths are byte-swapped on big-endian kernels and can break command processing during probe. Since complete big-endian support requires converting every device-visible structure, depend on !CPU_BIG_ENDIAN.

---
 drivers/infiniband/hw/erdma/Kconfig | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/infiniband/hw/erdma/Kconfig +1 -1
index 267fc1f3c42af..745e5551773f5 100644
@@ -1,7 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0-only
 config INFINIBAND_ERDMA
 	tristate "Alibaba Elastic RDMA Adapter (ERDMA) support"
-	depends on PCI_MSI && 64BIT
+	depends on PCI_MSI && 64BIT && !CPU_BIG_ENDIAN
 	depends on INFINIBAND_ADDR_TRANS
 	depends on INFINIBAND_USER_ACCESS
 	help
Signature · 1 line
2.53.0
author maintainer
#973 lore raw

PATCH 932/1376 wifi: mac80211: skip default WMM setup for AP_VLAN links

In reply to Greg Kroah-Hartman

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

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

From: Felix Fietkau <[email protected]>

[ Upstream commit 4d8cfff012aaa971d50b01823b1015c1073c3ff6 ]

AP_VLAN interfaces are never passed to the driver, so setting default WMM parameters on their links trips the check-sdata-in-driver warning in drv_conf_tx(), as well as in the BSS_CHANGED_QOS link info notification. Skip it, matching the existing AP_VLAN handling in this function.

Fixes 2259d14499d1 ("wifi: mac80211: set default WMM parameters on all links")
---
 net/mac80211/link.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
net/mac80211/link.c +2 -1
index 29813394bbff1..7e249e2f93bfa 100644
@@ -340,7 +340,8 @@ static int ieee80211_vif_update_links(struct ieee80211_sub_if_data *sdata,
 		link = links[link_id];
 		ieee80211_link_init(sdata, link_id, &link->data, &link->conf);
 		ieee80211_link_setup(&link->data);
-		ieee80211_set_wmm_default(&link->data, true, non_sta);
+		if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN)
+			ieee80211_set_wmm_default(&link->data, true, non_sta);
 	}
 
 	if (new_links == 0)
Signature · 1 line
2.53.0
author maintainer
#974 lore raw

PATCH 933/1376 arm64: hibernate: mask DAIF before restoring hibernated kernel

In reply to Greg Kroah-Hartman

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

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

From: Ada Couprie Diaz <[email protected]>

[ Upstream commit 684bde100117931f4c51c644a95f42f2dab041bc ]

The arm64 hibernate code manages the exception masking in an unsound way, leading to potential crashes and/or warnings during resume.

When a hibernation image is saved in swsusp_arch_suspend(), all DAIF exceptions are masked (by virtue of local_daif_save()), and the suspended image is saved assuming that all DAIF exceptions will remain masked when the image is restored.

When a hibernation image is resumed by swsusp_arch_resume(), only interrupts are masked (by virtue of local_irq_disable() in resume_target_kernel()). When pseudo-NMI is enabled the DAIF.IF bits will be clear, and regardless of pseudo-NMI the DAIF.DA bits will be clear.

This means that there are two problems:

(1) It is possible to take Debug, SError, or pseudo-NMI exceptions during the resume process. This is unsafe, as during the resume process both the old ane new kernels will tranisently be in an inconsistent state, and swsusp_arch_suspend_exit() won't retain an executable mapping of any exception vectors.

Any exception taken here will be fatal and silent.

(2) When re-entering the resumed kernel, some DAIF bits will be clear unexpectedly. This permits Debug, SError, or pseudo-NMI exceptions to be taken for a short period while the resumed kernel is not yet in a consistent state.

This is detected by CONFIG_ARM64_DEBUG_PRIORITY_MASKING.

Avoid these issues by masking all DAIF exceptions during resume.

Fixes 82869ac57b5d ("arm64: kernel: Add support for hibernate/suspend-to-disk")
---
 arch/arm64/kernel/hibernate.c | 12 ++++++++++++
 1 file changed, 12 insertions(+)
arch/arm64/kernel/hibernate.c +12 -0
index 945983735815c..7b9f82ccdf0e4 100644
@@ -459,9 +459,21 @@ int __nocfi swsusp_arch_resume(void)
 	if (el2_reset_needed())
 		__hyp_set_vectors(el2_vectors);
 
+	/*
+	 * It is necessary to mask all DAIF exceptions here as:
+	 *
+	 * - The copy of swsusp_arch_suspend_exit() in the hibernation
+	 *   text cannot handle taking any exceptions.
+	 *
+	 * - The suspended kernel masked all DAIF exceptions in
+	 *   swsusp_arch_resume(), and expects to be re-entered in the
+	 *   same state : with all DAIF exceptions masked.
+	 */
+	local_daif_save();
 	hibernate_exit(virt_to_phys(tmp_pg_dir), resume_hdr.ttbr1_el1,
 		       resume_hdr.reenter_kernel, restore_pblist,
 		       resume_hdr.__hyp_stub_vectors, virt_to_phys(zero_page));
+	unreachable();
 
 	return 0;
 }
Signature · 1 line
2.53.0
author maintainer
#975 lore raw

PATCH 934/1376 arm64: hibernate: Restore DAIF state on error

In reply to Greg Kroah-Hartman

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

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

From: Vladimir Murzin <[email protected]>

[ Upstream commit 541549827889d0380fd73f8aacb5de6ef7a5a1ac ]

Sashiko AI has reported that if swsusp_mte_save_tags() for some reason fails we return from swsusp_arch_suspend() with DAIF being masked - that is not what we'd expect. Restore the saved DAIF state before returning from the error path.

Fixes ee11f332af96 ("arm64: mte: Save tags when hibernating")
---
 arch/arm64/kernel/hibernate.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
arch/arm64/kernel/hibernate.c +3 -1
index 7b9f82ccdf0e4..44d31b019efd5 100644
@@ -342,8 +342,10 @@ int swsusp_arch_suspend(void)
 		crash_prepare_suspend();
 
 		ret = swsusp_mte_save_tags();
-		if (ret)
+		if (ret) {
+			local_daif_restore(flags);
 			return ret;
+		}
 
 		sleep_cpu = smp_processor_id();
 		ret = swsusp_save();
Signature · 1 line
2.53.0
author maintainer
#976 lore raw

PATCH 935/1376 mfd: rave-sp: validate received frame payload lengths

In reply to Greg Kroah-Hartman

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

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

From: Pengpeng Hou <[email protected]>

[ Upstream commit 0be718b5451bd83865d6e2a8d750ca7886c4772a ]

A received RAVE-SP frame contains protocol data followed by a variant-specific one- or two-byte checksum. rave_sp_receive_frame() derives a checksum pointer before proving that the frame contains the checksum, then passes the checksum-inclusive length to handlers that index the command, acknowledgment ID and event-data bytes or derive a reply payload length.

Name those protocol field offsets, prove the checksum extent before deriving the protocol-data length, pass only that data length to the handlers, and require the complete event or reply prefix before consuming it.

---
 drivers/mfd/rave-sp.c | 64 +++++++++++++++++++++++++++++--------------
 1 file changed, 44 insertions(+), 20 deletions(-)
drivers/mfd/rave-sp.c +44 -20
index c1b78d127a261..05d26d92df03e 100644
@@ -63,6 +63,12 @@
 #define RAVE_SP_TX_BUFFER_SIZE				\
 	(RAVE_SP_STX_ETX_SIZE + 2 * RAVE_SP_RX_BUFFER_SIZE)
 
+enum rave_sp_frame_offset {
+	RAVE_SP_FRAME_CODE_OFFSET,
+	RAVE_SP_FRAME_ACK_ID_OFFSET,
+	RAVE_SP_FRAME_DATA_OFFSET,
+};
+
 /**
  * enum rave_sp_deframer_state - Possible state for de-framer
  *
@@ -352,7 +358,7 @@ int rave_sp_exec(struct rave_sp *sp,
 	int command, ret = 0;
 	u8 ackid;
 
-	command = sp->variant->cmd.translate(data[0]);
+	command = sp->variant->cmd.translate(data[RAVE_SP_FRAME_CODE_OFFSET]);
 	if (command < 0)
 		return command;
 
@@ -366,8 +372,8 @@ int rave_sp_exec(struct rave_sp *sp,
 	sp->reply = &reply;
 	mutex_unlock(&sp->reply_lock);
 
-	data[0] = command;
-	data[1] = ackid;
+	data[RAVE_SP_FRAME_CODE_OFFSET] = command;
+	data[RAVE_SP_FRAME_ACK_ID_OFFSET] = ackid;
 
 	rave_sp_write(sp, data, data_size);
 
@@ -388,16 +394,23 @@ EXPORT_SYMBOL_GPL(rave_sp_exec);
 static void rave_sp_receive_event(struct rave_sp *sp,
 				  const unsigned char *data, size_t length)
 {
-	u8 cmd[] = {
-		[0] = rave_sp_reply_code(data[0]),
-		[1] = data[1],
-	};
+	unsigned long action;
+	u8 cmd[RAVE_SP_FRAME_DATA_OFFSET];
+
+	if (length < RAVE_SP_FRAME_DATA_OFFSET + 1) {
+		dev_warn(&sp->serdev->dev, "Dropping short event frame\n");
+		return;
+	}
+
+	cmd[RAVE_SP_FRAME_CODE_OFFSET] =
+		rave_sp_reply_code(data[RAVE_SP_FRAME_CODE_OFFSET]);
+	cmd[RAVE_SP_FRAME_ACK_ID_OFFSET] = data[RAVE_SP_FRAME_ACK_ID_OFFSET];
 
 	rave_sp_write(sp, cmd, sizeof(cmd));
 
-	blocking_notifier_call_chain(&sp->event_notifier_list,
-				     rave_sp_action_pack(data[0], data[2]),
-				     NULL);
+	action = rave_sp_action_pack(data[RAVE_SP_FRAME_CODE_OFFSET],
+				     data[RAVE_SP_FRAME_DATA_OFFSET]);
+	blocking_notifier_call_chain(&sp->event_notifier_list, action, NULL);
 }
 
 static void rave_sp_receive_reply(struct rave_sp *sp,
@@ -405,27 +418,35 @@ static void rave_sp_receive_reply(struct rave_sp *sp,
 {
 	struct device *dev = &sp->serdev->dev;
 	struct rave_sp_reply *reply;
-	const  size_t payload_length = length - 2;
+	size_t payload_length;
+
+	if (length < RAVE_SP_FRAME_DATA_OFFSET) {
+		dev_warn(dev, "Dropping short reply frame\n");
+		return;
+	}
+	payload_length = length - RAVE_SP_FRAME_DATA_OFFSET;
 
 	mutex_lock(&sp->reply_lock);
 	reply = sp->reply;
 
 	if (reply) {
-		if (reply->code == data[0] && reply->ackid == data[1] &&
+		if (reply->code == data[RAVE_SP_FRAME_CODE_OFFSET] &&
+		    reply->ackid == data[RAVE_SP_FRAME_ACK_ID_OFFSET] &&
 		    payload_length >= reply->length) {
 			/*
 			 * We are relying on memcpy(dst, src, 0) to be a no-op
 			 * when handling commands that have a no-payload reply
 			 */
-			memcpy(reply->data, &data[2], reply->length);
+			memcpy(reply->data, &data[RAVE_SP_FRAME_DATA_OFFSET],
+			       reply->length);
 			complete(&reply->received);
 			sp->reply = NULL;
 		} else {
 			dev_err(dev, "Ignoring incorrect reply\n");
 			dev_dbg(dev, "Code:   expected = 0x%08x received = 0x%08x\n",
-				reply->code, data[0]);
+				reply->code, data[RAVE_SP_FRAME_CODE_OFFSET]);
 			dev_dbg(dev, "ACK ID: expected = 0x%08x received = 0x%08x\n",
-				reply->ackid, data[1]);
+				reply->ackid, data[RAVE_SP_FRAME_ACK_ID_OFFSET]);
 			dev_dbg(dev, "Length: expected = %zu received = %zu\n",
 				reply->length, payload_length);
 		}
@@ -439,10 +460,10 @@ static void rave_sp_receive_frame(struct rave_sp *sp,
 				  size_t length)
 {
 	const size_t checksum_length = sp->variant->checksum->length;
-	const size_t payload_length  = length - checksum_length;
-	const u8 *crc_reported       = &data[payload_length];
 	struct device *dev           = &sp->serdev->dev;
 	u8 crc_calculated[RAVE_SP_CHECKSUM_SIZE];
+	const u8 *crc_reported;
+	size_t payload_length;
 
 	if (unlikely(checksum_length > sizeof(crc_calculated))) {
 		dev_warn(dev, "Checksum too long, dropping\n");
@@ -457,6 +478,9 @@ static void rave_sp_receive_frame(struct rave_sp *sp,
 		return;
 	}
 
+	payload_length = length - checksum_length;
+	crc_reported = &data[payload_length];
+
 	sp->variant->checksum->subroutine(data, payload_length,
 					  crc_calculated);
 
@@ -465,10 +489,10 @@ static void rave_sp_receive_frame(struct rave_sp *sp,
 		return;
 	}
 
-	if (rave_sp_id_is_event(data[0]))
-		rave_sp_receive_event(sp, data, length);
+	if (rave_sp_id_is_event(data[RAVE_SP_FRAME_CODE_OFFSET]))
+		rave_sp_receive_event(sp, data, payload_length);
 	else
-		rave_sp_receive_reply(sp, data, length);
+		rave_sp_receive_reply(sp, data, payload_length);
 }
 
 static size_t rave_sp_receive_buf(struct serdev_device *serdev,
Signature · 1 line
2.53.0
author maintainer
#977 lore raw

PATCH 936/1376 mfd: iqs62x: Reject zero-length firmware records

In reply to Greg Kroah-Hartman

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

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

From: Pengpeng Hou <[email protected]>

[ Upstream commit 08ea045e0b82cbcadb7a2efc43a23561489a00f2 ]

struct iqs62x_fw_rec includes the first data byte in its fixed-size header, so the parser advances by len - 1 bytes after that header. A zero len makes the size_t cursor update move back by one byte, so the next record overlaps the current record instead of following a valid declared extent.

Reject zero-length records and express the remaining-size check without an offset addition.

---
 drivers/mfd/iqs62x.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/mfd/iqs62x.c +1 -1
index ee017617d1d1b..412ae7777f729 100644
@@ -237,7 +237,7 @@ static int iqs62x_firmware_parse(struct iqs62x_core *iqs62x,
 		fw_rec = (struct iqs62x_fw_rec *)(fw->data + pos);
 		pos += sizeof(*fw_rec);
 
-		if (pos + fw_rec->len - 1 > fw->size) {
+		if (!fw_rec->len || fw_rec->len - 1 > fw->size - pos) {
 			ret = -EINVAL;
 			break;
 		}
Signature · 1 line
2.53.0
author maintainer
#978 lore raw

PATCH 937/1376 drm/amdgpu/gfx6: Fixup emit_cntxcntl()

In reply to Greg Kroah-Hartman

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

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

From: Timur Kristóf <[email protected]>

[ Upstream commit 1edb323406aaf05739804d2faa2561f2d43bcd09 ]

Set bits on dword 2 like GFX7-8 except load_global_uconfig which doesn't exist on GFX6.

Emit VS_PARTIAL_FLUSH before VGT_FLUSH like GFX7-8.

For reference see old PAL which explains the bit fields in this register and that load_global_uconfig doesn't exist on GFX6 and also see gfx_v7_ring_emit_cntxcntl() for the GFX7 code which this commit follows.

Fixes 2cd46ad22383 ("drm/amdgpu: add graphic pipeline implementation for si v8")
---
 drivers/gpu/drm/amd/amdgpu/gfx_v6_0.c | 28 ++++++++++++++++++++-------
 1 file changed, 21 insertions(+), 7 deletions(-)
drivers/gpu/drm/amd/amdgpu/gfx_v6_0.c +21 -7
index 90c426ee877b6..0c08247fb5474 100644
@@ -1859,11 +1859,13 @@ static int gfx_v6_0_ring_test_ring(struct amdgpu_ring *ring)
 	return r;
 }
 
-static void gfx_v6_0_ring_emit_vgt_flush(struct amdgpu_ring *ring)
+static void gfx_v6_0_ring_emit_event_write(struct amdgpu_ring *ring,
+					   uint32_t event_type,
+					   uint32_t event_index)
 {
 	amdgpu_ring_write(ring, PACKET3(PACKET3_EVENT_WRITE, 0));
-	amdgpu_ring_write(ring, EVENT_TYPE(VGT_FLUSH) |
-		EVENT_INDEX(0));
+	amdgpu_ring_write(ring, EVENT_TYPE(event_type) |
+				EVENT_INDEX(event_index));
 }
 
 static void gfx_v6_0_ring_emit_fence(struct amdgpu_ring *ring, u64 addr,
@@ -3000,10 +3002,22 @@ static uint64_t gfx_v6_0_get_gpu_clock_counter(struct amdgpu_device *adev)
 
 static void gfx_v6_ring_emit_cntxcntl(struct amdgpu_ring *ring, uint32_t flags)
 {
-	if (flags & AMDGPU_HAVE_CTX_SWITCH)
-		gfx_v6_0_ring_emit_vgt_flush(ring);
+	u32 dw2 = 0x80000000; /* set load_enable otherwise this package is just NOPs */
+
+	if (flags & AMDGPU_HAVE_CTX_SWITCH) {
+		gfx_v6_0_ring_emit_event_write(ring, VS_PARTIAL_FLUSH, 4);
+		gfx_v6_0_ring_emit_event_write(ring, VGT_FLUSH, 0);
+
+		/* set load_global_config (load_global_uconfig doesn't exist on GFX6) */
+		dw2 |= 0x1;
+		/* set load_cs_sh_regs */
+		dw2 |= 0x01000000;
+		/* set load_per_context_state & load_gfx_sh_regs */
+		dw2 |= 0x10002;
+	}
+
 	amdgpu_ring_write(ring, PACKET3(PACKET3_CONTEXT_CONTROL, 1));
-	amdgpu_ring_write(ring, 0x80000000);
+	amdgpu_ring_write(ring, dw2);
 	amdgpu_ring_write(ring, 0);
 }
 
@@ -3539,7 +3553,7 @@ static const struct amdgpu_ring_funcs gfx_v6_0_ring_funcs_gfx = {
 		14 + 14 + 14 + /* gfx_v6_0_ring_emit_fence x3 for user fence, vm fence */
 		7 + 4 + /* gfx_v6_0_ring_emit_pipeline_sync */
 		SI_FLUSH_GPU_TLB_NUM_WREG * 5 + 7 + 6 + /* gfx_v6_0_ring_emit_vm_flush */
-		3 + 2 + /* gfx_v6_ring_emit_cntxcntl including vgt flush */
+		3 + 2 + 2 + /* gfx_v6_ring_emit_cntxcntl including VGT flush */
 		5, /* SURFACE_SYNC */
 	.emit_ib_size = 6, /* gfx_v6_0_ring_emit_ib */
 	.emit_ib = gfx_v6_0_ring_emit_ib,
Signature · 1 line
2.53.0
author maintainer
#979 lore raw

PATCH 938/1376 ext4: fix spurious message about orphan cleanup on RO fs

In reply to Greg Kroah-Hartman

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

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

From: Jan Kara <[email protected]>

[ Upstream commit 5aa98f874c013bcce9bb84ffded2f0ef886e4e33 ]

When orphan_file feature is enabled, ext4_orphan_cleanup() was always walking through the orphan file looking for orphan inodes. This is mostly harmless but for read-only filesystem it results in spurious "orphan cleanup on readonly fs" message and in other cornercases it could result in similar somewhat misleading messages. Skip orphan cleanup if the orphan file is empty to avoid confusing messages.

---
 fs/ext4/orphan.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
fs/ext4/orphan.c +1 -1
index 7a5893dc18114..13cbf94c7cc2e 100644
@@ -388,7 +388,7 @@ void ext4_orphan_cleanup(struct super_block *sb, struct ext4_super_block *es)
 	struct ext4_orphan_info *oi = &EXT4_SB(sb)->s_orphan_info;
 	int inodes_per_ob = ext4_inodes_per_orphan_block(sb);
 
-	if (!es->s_last_orphan && !oi->of_blocks) {
+	if (!es->s_last_orphan && ext4_orphan_file_empty(sb)) {
 		ext4_debug("no orphan inodes to clean up\n");
 		return;
 	}
Signature · 1 line
2.53.0
author maintainer
#980 lore raw

PATCH 939/1376 arm64: dts: ti: k3-am64: Fix MDIO clock reference for ICSSG0 node

In reply to Greg Kroah-Hartman

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

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

From: Meghana Malladi <[email protected]>

[ Upstream commit 197df050a10fe216971b45a79f07195c86a02236 ]

MDIO clock index changed from 62:3 to 81:0 to match proper clock definition in the SoC device tree. Clock Id 81:0 belongs to ICSSG0 core clock, where as 62 belongs to EQEP2 device.

See: https://software-dl.ti.com/tisci/esd/latest/5_soc_doc/am64x/clocks.html

Fixes c9087e3898a1d0 ("arm64: dts: ti: k3-am64-main: Add ICSSG nodes")
---
 arch/arm64/boot/dts/ti/k3-am64-main.dtsi | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
arch/arm64/boot/dts/ti/k3-am64-main.dtsi +1 -1
index 7eae18399caa6..7c6ba86cf8eed 100644
@@ -1349,7 +1349,7 @@ tx_pru0_1: txpru@c000 {
 		icssg0_mdio: mdio@32400 {
 			compatible = "ti,davinci_mdio";
 			reg = <0x32400 0x100>;
-			clocks = <&k3_clks 62 3>;
+			clocks = <&k3_clks 81 0>;
 			clock-names = "fck";
 			#address-cells = <1>;
 			#size-cells = <0>;
Signature · 1 line
2.53.0
author maintainer
#981 lore raw

PATCH 940/1376 phy: sunplus: fix error handling in sp_uphy_init()

In reply to Greg Kroah-Hartman

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

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

From: Felix Gu <[email protected]>

[ Upstream commit 8b2683bc4cc18c581b7cd24f227cbe61abca7f4d ]

Fix the error paths of sp_uphy_init() to undo exactly what each stage
did: return directly if clk_prepare_enable() fails, release only the clock if reset_control_deassert() fails, and jump to err_reset if update_disc_vol() fails so the clock and reset are not leaked.

Fixes 99d9ccd97385 ("phy: usb: Add USB2.0 phy driver for Sunplus SP7021")
---
 drivers/phy/sunplus/phy-sunplus-usb2.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)
drivers/phy/sunplus/phy-sunplus-usb2.c +3 -3
index 637a5fbae6d9a..0ad4c7160d17c 100644
@@ -116,11 +116,11 @@ static int sp_uphy_init(struct phy *phy)
 
 	ret = clk_prepare_enable(usbphy->phy_clk);
 	if (ret)
-		goto err_clk;
+		return ret;
 
 	ret = reset_control_deassert(usbphy->rstc);
 	if (ret)
-		goto err_reset;
+		goto err_clk;
 
 	/* Default value modification */
 	writel(HIGH_MASK_BITS | 0x4002, usbphy->moon4_regs + UPHY_CONTROL0);
@@ -129,7 +129,7 @@ static int sp_uphy_init(struct phy *phy)
 	/* disconnect voltage */
 	ret = update_disc_vol(usbphy);
 	if (ret < 0)
-		return ret;
+		goto err_reset;
 
 	/* board uphy 0 internal register modification for tid certification */
 	val = readl(usbphy->phy_regs + CONFIG9);
Signature · 1 line
2.53.0
author maintainer
#982 lore raw

PATCH 941/1376 phy: rockchip: phy-rockchip-inno-csidphy: fix rk1808 hsfreq table

In reply to Greg Kroah-Hartman

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

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

From: Gerald Loacker <[email protected]>

[ Upstream commit 4fae43e33a7c10951fbdc6baf409d51bd66aaefb ]

The rk1808 hsfreq table capped at 2499 Mbps, preventing a data rate of exactly 2500 Mbps. Extend the final entry to 2500 Mbps to support this rate.

This is essential for RK3588 reusing this array and fully supporting rates up to 2500 Mbps.

---
 drivers/phy/rockchip/phy-rockchip-inno-csidphy.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/phy/rockchip/phy-rockchip-inno-csidphy.c +1 -1
index 279e19e7546b6..2c1f8e3645df1 100644
@@ -157,7 +157,7 @@ static const struct hsfreq_range rk1808_mipidphy_hsfreq_ranges[] = {
 	{ 299, 0x06}, { 399, 0x08}, { 499, 0x0b}, { 599, 0x0e},
 	{ 699, 0x10}, { 799, 0x12}, { 999, 0x16}, {1199, 0x1e},
 	{1399, 0x23}, {1599, 0x2d}, {1799, 0x32}, {1999, 0x37},
-	{2199, 0x3c}, {2399, 0x41}, {2499, 0x46}
+	{2199, 0x3c}, {2399, 0x41}, {2500, 0x46}
 };
 
 static const struct hsfreq_range rk3326_mipidphy_hsfreq_ranges[] = {
Signature · 1 line
2.53.0
author maintainer
#983 lore raw

PATCH 942/1376 perf trace-event: Fix buffer overflow in read_string()

In reply to Greg Kroah-Hartman

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

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

From: Tanushree Shah <[email protected]>

[ Upstream commit 1121a7af1833f8b5723f1e32685b461614353d5d ]

read_string() writes into buf[BUFSIZ] one byte at a time without checking 'size' against the buffer bound before each write. A string longer than BUFSIZ in the input overflows the stack buffer.

Add a bounds check before each write to prevent overflow. On overflow the function returns NULL, matching its other error paths.

Fixes 9215545e99d8 ("perf: Convert perf tracing data into a tracing_data event")
---
 tools/perf/util/trace-event-read.c | 5 +++++
 1 file changed, 5 insertions(+)
tools/perf/util/trace-event-read.c +5 -0
index 1162c49b80825..72379cb8f5fd3 100644
@@ -127,6 +127,11 @@ static char *read_string(void)
 			}
 		}
 
+		if (size >= (int)sizeof(buf) - 1) {
+			pr_debug("string too long (max %zu bytes)", sizeof(buf) - 1);
+			goto out;
+		}
+
 		buf[size++] = c;
 
 		if (!c)
Signature · 1 line
2.53.0
author maintainer
#984 lore raw

PATCH 943/1376 drm/amdgpu/gfx6: Fixup emitting SWITCH_BUFFER packets

In reply to Greg Kroah-Hartman

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

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

From: Timur Kristóf <[email protected]>

[ Upstream commit 7aac4242a11991d530eee3e141d6e445d5f11c22 ]

Implement the emit_switch_buffer() function instead of emitting them duing emit_ib, emit_pipeline_sync and emit_vm_flush.

Note that it isn't necessary to emit these in both emit_pipeline_sync() and emit_vm_flush() because amdgpu_vm_flush() already calls these when calling either of those functions.

Fixes 2cd46ad22383 ("drm/amdgpu: add graphic pipeline implementation for si v8")
---
 drivers/gpu/drm/amd/amdgpu/gfx_v6_0.c | 38 ++++++++++-----------------
 1 file changed, 14 insertions(+), 24 deletions(-)
drivers/gpu/drm/amd/amdgpu/gfx_v6_0.c +14 -24
index 0c08247fb5474..2e517f4d75879 100644
@@ -1904,12 +1904,6 @@ static void gfx_v6_0_ring_emit_ib(struct amdgpu_ring *ring,
 	unsigned vmid = AMDGPU_JOB_GET_VMID(job);
 	u32 header, control = 0;
 
-	/* insert SWITCH_BUFFER packet before first IB in the ring frame */
-	if (flags & AMDGPU_HAVE_CTX_SWITCH) {
-		amdgpu_ring_write(ring, PACKET3(PACKET3_SWITCH_BUFFER, 0));
-		amdgpu_ring_write(ring, 0);
-	}
-
 	if (ib->flags & AMDGPU_IB_FLAG_CE)
 		header = PACKET3(PACKET3_INDIRECT_BUFFER_CONST, 2);
 	else
@@ -2344,14 +2338,6 @@ static void gfx_v6_0_ring_emit_pipeline_sync(struct amdgpu_ring *ring)
 	amdgpu_ring_write(ring, seq);
 	amdgpu_ring_write(ring, 0xffffffff);
 	amdgpu_ring_write(ring, 4); /* poll interval */
-
-	if (usepfp) {
-		/* synce CE with ME to prevent CE fetch CEIB before context switch done */
-		amdgpu_ring_write(ring, PACKET3(PACKET3_SWITCH_BUFFER, 0));
-		amdgpu_ring_write(ring, 0);
-		amdgpu_ring_write(ring, PACKET3(PACKET3_SWITCH_BUFFER, 0));
-		amdgpu_ring_write(ring, 0);
-	}
 }
 
 static void gfx_v6_0_ring_emit_vm_flush(struct amdgpu_ring *ring,
@@ -2375,12 +2361,6 @@ static void gfx_v6_0_ring_emit_vm_flush(struct amdgpu_ring *ring,
 		/* sync PFP to ME, otherwise we might get invalid PFP reads */
 		amdgpu_ring_write(ring, PACKET3(PACKET3_PFP_SYNC_ME, 0));
 		amdgpu_ring_write(ring, 0x0);
-
-		/* synce CE with ME to prevent CE fetch CEIB before context switch done */
-		amdgpu_ring_write(ring, PACKET3(PACKET3_SWITCH_BUFFER, 0));
-		amdgpu_ring_write(ring, 0);
-		amdgpu_ring_write(ring, PACKET3(PACKET3_SWITCH_BUFFER, 0));
-		amdgpu_ring_write(ring, 0);
 	}
 }
 
@@ -3000,6 +2980,12 @@ static uint64_t gfx_v6_0_get_gpu_clock_counter(struct amdgpu_device *adev)
 	return clock;
 }
 
+static void gfx_v6_0_ring_emit_sb(struct amdgpu_ring *ring)
+{
+	amdgpu_ring_write(ring, PACKET3(PACKET3_SWITCH_BUFFER, 0));
+	amdgpu_ring_write(ring, 0);
+}
+
 static void gfx_v6_ring_emit_cntxcntl(struct amdgpu_ring *ring, uint32_t flags)
 {
 	u32 dw2 = 0x80000000; /* set load_enable otherwise this package is just NOPs */
@@ -3551,11 +3537,12 @@ static const struct amdgpu_ring_funcs gfx_v6_0_ring_funcs_gfx = {
 	.emit_frame_size =
 		5 + 5 + /* hdp flush / invalidate */
 		14 + 14 + 14 + /* gfx_v6_0_ring_emit_fence x3 for user fence, vm fence */
-		7 + 4 + /* gfx_v6_0_ring_emit_pipeline_sync */
-		SI_FLUSH_GPU_TLB_NUM_WREG * 5 + 7 + 6 + /* gfx_v6_0_ring_emit_vm_flush */
+		7 + /* gfx_v6_0_ring_emit_pipeline_sync */
+		SI_FLUSH_GPU_TLB_NUM_WREG * 5 + 7 + 2 + /* gfx_v6_0_ring_emit_vm_flush */
+		3 * 2 + /* gfx_v6_0_ring_emit_sb x3 (from amdgpu_vm_flush, amdgpu_ib_schedule) */
 		3 + 2 + 2 + /* gfx_v6_ring_emit_cntxcntl including VGT flush */
 		5, /* SURFACE_SYNC */
-	.emit_ib_size = 6, /* gfx_v6_0_ring_emit_ib */
+	.emit_ib_size = 4, /* gfx_v6_0_ring_emit_ib */
 	.emit_ib = gfx_v6_0_ring_emit_ib,
 	.emit_fence = gfx_v6_0_ring_emit_fence,
 	.emit_pipeline_sync = gfx_v6_0_ring_emit_pipeline_sync,
@@ -3563,6 +3550,7 @@ static const struct amdgpu_ring_funcs gfx_v6_0_ring_funcs_gfx = {
 	.test_ring = gfx_v6_0_ring_test_ring,
 	.test_ib = gfx_v6_0_ring_test_ib,
 	.insert_nop = amdgpu_ring_insert_nop,
+	.emit_switch_buffer = gfx_v6_0_ring_emit_sb,
 	.emit_cntxcntl = gfx_v6_ring_emit_cntxcntl,
 	.emit_wreg = gfx_v6_0_ring_emit_wreg,
 	.emit_mem_sync = gfx_v6_0_emit_mem_sync,
@@ -3580,8 +3568,9 @@ static const struct amdgpu_ring_funcs gfx_v6_0_ring_funcs_compute = {
 		7 + /* gfx_v6_0_ring_emit_pipeline_sync */
 		SI_FLUSH_GPU_TLB_NUM_WREG * 5 + 7 + /* gfx_v6_0_ring_emit_vm_flush */
 		14 + 14 + 14 + /* gfx_v6_0_ring_emit_fence x3 for user fence, vm fence */
+		3 * 2 + /* gfx_v6_0_ring_emit_sb x3 (from amdgpu_vm_flush, amdgpu_ib_schedule) */
 		5, /* SURFACE_SYNC */
-	.emit_ib_size = 6, /* gfx_v6_0_ring_emit_ib */
+	.emit_ib_size = 4, /* gfx_v6_0_ring_emit_ib */
 	.emit_ib = gfx_v6_0_ring_emit_ib,
 	.emit_fence = gfx_v6_0_ring_emit_fence,
 	.emit_pipeline_sync = gfx_v6_0_ring_emit_pipeline_sync,
@@ -3589,6 +3578,7 @@ static const struct amdgpu_ring_funcs gfx_v6_0_ring_funcs_compute = {
 	.test_ring = gfx_v6_0_ring_test_ring,
 	.test_ib = gfx_v6_0_ring_test_ib,
 	.insert_nop = amdgpu_ring_insert_nop,
+	.emit_switch_buffer = gfx_v6_0_ring_emit_sb,
 	.emit_wreg = gfx_v6_0_ring_emit_wreg,
 	.emit_mem_sync = gfx_v6_0_emit_mem_sync,
 };
Signature · 1 line
2.53.0
author maintainer
#985 lore raw

PATCH 944/1376 drm/amdgpu/gfx6: Use PFP on the compute queues too

In reply to Greg Kroah-Hartman

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

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

From: Timur Kristóf <[email protected]>

[ Upstream commit 60f20946cd318518ddc2c0da12103c666b2b9564 ]

On GFX6, the compute rings use the same CP path as the graphics ring. The only difference is that they don't support draw commands. (As opposed to GFX7 and newer which have a separate command parser that is called MEC for compute queues.)

This means that we have to take into consideration that the PFP also exists on compute queues on GFX6:

Use PFP for register writes on both graphics and compute queues.

In the pipeline sync, use the PFP to wait for the previous fence (and not the ME) to prevent the PFP from starting to execute the next submission while the ME is still in the previous submission.

After a VM flush, emit PFP_SYNC_ME on compute queues as well.

Fixes 2cd46ad22383 ("drm/amdgpu: add graphic pipeline implementation for si v8")
---
 drivers/gpu/drm/amd/amdgpu/gfx_v6_0.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)
drivers/gpu/drm/amd/amdgpu/gfx_v6_0.c +4 -4
index 2e517f4d75879..9b294ef10fd7d 100644
@@ -2325,7 +2325,7 @@ static int gfx_v6_0_cp_resume(struct amdgpu_device *adev)
 
 static void gfx_v6_0_ring_emit_pipeline_sync(struct amdgpu_ring *ring)
 {
-	int usepfp = (ring->funcs->type == AMDGPU_RING_TYPE_GFX);
+	int usepfp = 1;
 	uint32_t seq = ring->fence_drv.sync_seq;
 	uint64_t addr = ring->fence_drv.gpu_addr;
 
@@ -2343,7 +2343,7 @@ static void gfx_v6_0_ring_emit_pipeline_sync(struct amdgpu_ring *ring)
 static void gfx_v6_0_ring_emit_vm_flush(struct amdgpu_ring *ring,
 					unsigned vmid, uint64_t pd_addr)
 {
-	int usepfp = (ring->funcs->type == AMDGPU_RING_TYPE_GFX);
+	int usepfp = 1;
 
 	amdgpu_gmc_emit_flush_gpu_tlb(ring, vmid, pd_addr);
 
@@ -2367,7 +2367,7 @@ static void gfx_v6_0_ring_emit_vm_flush(struct amdgpu_ring *ring,
 static void gfx_v6_0_ring_emit_wreg(struct amdgpu_ring *ring,
 				    uint32_t reg, uint32_t val)
 {
-	int usepfp = (ring->funcs->type == AMDGPU_RING_TYPE_GFX);
+	int usepfp = 1;
 
 	amdgpu_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3));
 	amdgpu_ring_write(ring, (WRITE_DATA_ENGINE_SEL(usepfp) |
@@ -3566,7 +3566,7 @@ static const struct amdgpu_ring_funcs gfx_v6_0_ring_funcs_compute = {
 	.emit_frame_size =
 		5 + 5 + /* hdp flush / invalidate */
 		7 + /* gfx_v6_0_ring_emit_pipeline_sync */
-		SI_FLUSH_GPU_TLB_NUM_WREG * 5 + 7 + /* gfx_v6_0_ring_emit_vm_flush */
+		SI_FLUSH_GPU_TLB_NUM_WREG * 5 + 7 + 2 + /* gfx_v6_0_ring_emit_vm_flush */
 		14 + 14 + 14 + /* gfx_v6_0_ring_emit_fence x3 for user fence, vm fence */
 		3 * 2 + /* gfx_v6_0_ring_emit_sb x3 (from amdgpu_vm_flush, amdgpu_ib_schedule) */
 		5, /* SURFACE_SYNC */
Signature · 1 line
2.53.0
author maintainer
#986 lore raw

PATCH 945/1376 scsi: qla2xxx: Remove redundant VPD flash read in sysfs read path

In reply to Greg Kroah-Hartman

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

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

From: Manish Rangankar <[email protected]>

[ Upstream commit 5cbc49d5c4cd20c18041e86958103045216d2190 ]

qla2x00_sysfs_read_vpd() called ha->isp_ops->read_optrom() a second time after releasing optrom_mutex. The repeated read is redundant and, unlike the first, runs without optrom_mutex held, exposing flash access to concurrent optrom operations. Drop the duplicate call.

Fixes 5fa8774c7f38 ("scsi: qla2xxx: Add 28xx flash primary/secondary status/image mechanism")
---
 drivers/scsi/qla2xxx/qla_attr.c | 1 -
 1 file changed, 1 deletion(-)
drivers/scsi/qla2xxx/qla_attr.c +0 -1
index a8b62bf31be20..a5c031f92b0a8 100644
@@ -580,7 +580,6 @@ qla2x00_sysfs_read_vpd(struct file *filp, struct kobject *kobj,
 	ha->isp_ops->read_optrom(vha, ha->vpd, faddr, ha->vpd_size);
 	mutex_unlock(&ha->optrom_mutex);
 
-	ha->isp_ops->read_optrom(vha, ha->vpd, faddr, ha->vpd_size);
 skip:
 	return memory_read_from_buffer(buf, count, &off, ha->vpd, ha->vpd_size);
 }
Signature · 1 line
2.53.0
author maintainer
#987 lore raw

PATCH 946/1376 firmware_loader: do not queue completed sysfs fallback requests

In reply to Greg Kroah-Hartman

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

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

From: Mukesh Ojha <[email protected]>

[ Upstream commit b48373c901951fad1a26bd7c33ad91172b3945b5 ]

fw_load_sysfs_fallback() calls device_add() before adding the fw_priv to pending_fw_head. device_add() publishes the fallback loading interface, so a userspace helper which discovers the device by scanning sysfs can write 0 to the loading attribute and complete the request before it is queued as pending.

In that interleaving firmware_loading_store() calls fw_state_done() while pending_list still points to itself, so it cannot remove an entry from pending_fw_head. The subsequent unconditional list_add() then queues an already-completed fw_priv. Once the request is released, pending_fw_head can retain a pointer to freed memory and the next fallback request can fault while validating the list.

Only in-flight fallback requests need suspend or reboot abort handling. If the request is already DONE after device_add(), return success from the fallback path without sending another uevent, waiting again, or queueing it as pending. This preserves the invariant that pending_fw_head contains only active fallback requests.

Fixes 75d95e2e39b2 ("firmware_loader: fix use-after-free in firmware_fallback_sysfs")
---
 drivers/base/firmware_loader/fallback.c | 10 ++++++++++
 1 file changed, 10 insertions(+)
drivers/base/firmware_loader/fallback.c +10 -0
index 3ef0b312ae719..00d6b6e594616 100644
@@ -95,6 +95,16 @@ static int fw_load_sysfs_fallback(struct fw_sysfs *fw_sysfs, long timeout)
 		retval = -EINTR;
 		goto out;
 	}
+
+	/*
+	 * device_add() exposes the loading interface before pending_list is
+	 * linked into pending_fw_head, so fw_state_done() may run first.
+	 */
+	if (fw_state_is_done(fw_priv)) {
+		mutex_unlock(&fw_lock);
+		goto out;
+	}
+
 	list_add(&fw_priv->pending_list, &pending_fw_head);
 	mutex_unlock(&fw_lock);
 
Signature · 1 line
2.53.0
author maintainer
#988 lore raw

PATCH 947/1376 pinctrl: rockchip: Reset the pin count when recalculating SoC data

In reply to Greg Kroah-Hartman

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

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

From: Simon Glass <[email protected]>

[ Upstream commit 5b695c191cc85f0fd62eec885b55d01468dc9f2c ]

rockchip_pinctrl_get_soc_data() mutates the static per-SoC data. The iomux and drive offsets are recalculated idempotently, since a rerun anchors at the values calculated before, but the total pin count only
accumulates: each run adds every bank's pins again. When the probe is deferred and runs a second time, nr_pins doubles and every bank's pin_base shifts, so later pin lookups resolve to the wrong bank and the wrong registers.

Reset the pin count at the start of the calculation, so that a rerun produces the same values.

This is verified on a Luckfox Pico Mini B (RV1103, with the pending RV1106 series applied) by forcing the probe to defer once: without this patch the second probe calculates nr_pins=304 instead of 152 and no GPIO bank comes up; with it the recalculation matches the first run and all banks work.

Fixes d3e5116119bd ("pinctrl: add pinctrl driver for Rockchip SoCs")
Assisted-by Claude:claude-opus-5
---
 drivers/pinctrl/pinctrl-rockchip.c | 10 ++++++++++
 1 file changed, 10 insertions(+)
drivers/pinctrl/pinctrl-rockchip.c +10 -0
index 894dde9446844..bd3cfeffaac10 100644
@@ -3409,6 +3409,16 @@ static struct rockchip_pin_ctrl *rockchip_pinctrl_get_soc_data(
 	pmu_offs = ctrl->pmu_mux_offset;
 	drv_pmu_offs = ctrl->pmu_drv_offset;
 	drv_grf_offs = ctrl->grf_drv_offset;
+
+	/*
+	 * This function mutates the static per-SoC data. Most of it is
+	 * idempotent: recalculated iomux and drv offsets anchor at the
+	 * values calculated by a previous run. The pin count is not, so
+	 * reset it here; otherwise it accumulates when the probe runs
+	 * again after a probe deferral, shifting every bank's pin_base.
+	 */
+	ctrl->nr_pins = 0;
+
 	bank = ctrl->pin_banks;
 	for (i = 0; i < ctrl->nr_banks; ++i, ++bank) {
 		int bank_pins = 0;
Signature · 1 line
2.53.0
author maintainer
#989 lore raw

PATCH 948/1376 hugetlbfs: release subpool on fill_super failure

In reply to Greg Kroah-Hartman

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

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

From: Yichong Chen <[email protected]>

[ Upstream commit 308ab73e97c87bd0e142b11758faab7f88d82854 ]

hugetlbfs_fill_super() allocates a hugepage subpool when size or min_size mount options are specified. hugepage_new_subpool() may also reserve huge pages for min_size.

If root dentry creation fails after the subpool is created, the failure path frees the subpool with kfree(). This bypasses hugepage_put_subpool() and can leave min_size reservations charged.

Use hugepage_put_subpool() on the failure path, matching the normal put_super path.

Fixes 7ca02d0ae586 ("hugetlbfs: accept subpool min_size mount option and setup accordingly")
Cc David Hildenbrand <[email protected]>
Cc Muchun Song <[email protected]>
Cc Oscar Salvador <[email protected]>
---
 fs/hugetlbfs/inode.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
fs/hugetlbfs/inode.c +2 -1
index b0c3b4399a793..734eb08bafc92 100644
@@ -1506,7 +1506,8 @@ hugetlbfs_fill_super(struct super_block *sb, struct fs_context *fc)
 		goto out_free;
 	return 0;
 out_free:
-	kfree(sbinfo->spool);
+	if (sbinfo->spool)
+		hugepage_put_subpool(sbinfo->spool);
 	kfree(sbinfo);
 	return -ENOMEM;
 }
Signature · 1 line
2.53.0
author maintainer
#990 lore raw

PATCH 949/1376 soc: fsl: qe: check platform_driver_register() in qe_ic_of_init()

In reply to Greg Kroah-Hartman

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

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

From: Linkai Gong <[email protected]>

[ Upstream commit fbdba2a67fa7c0e7570bcbf9f28b782d6100270d ]

qe_ic_of_init() ignored the return value of platform_driver_register() and always returned success. Propagate the error to the initcall.

Fixes be7ecbd240b2 ("soc: fsl: qe: convert QE interrupt controller to platform_device")
---
 drivers/soc/fsl/qe/qe_ic.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)
drivers/soc/fsl/qe/qe_ic.c +1 -2
index bbae3d39c7bed..f17de6000ff24 100644
@@ -481,7 +481,6 @@ static struct platform_driver qe_ic_driver =
 
 static int __init qe_ic_of_init(void)
 {
-	platform_driver_register(&qe_ic_driver);
-	return 0;
+	return platform_driver_register(&qe_ic_driver);
 }
 subsys_initcall(qe_ic_of_init);
Signature · 1 line
2.53.0