Skip to contentSkip to conversation

7.2.8-rc1 review

PATCH In review Greg Kroah-Hartman 16 participants 454 messages Sep 23 – Sep 25 Stable branch Page 2 of 4

lore
Share & tools
lore
author maintainer
#128 lore raw

PATCH 125/438 ntfs: ignore interrupted inode reads as corruption

In reply to Greg Kroah-Hartman

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

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

From: Namjae Jeon <[email protected]>

[ Upstream commit 8c5dc7587fdd45f957af81a9adc1e16863f300fc ]

Do not mark the volume in error or report an inode as corrupt when reading it was interrupted by a signal. -EINTR and -ERESTARTSYS indicate a transient read failure rather than on-disk NTFS corruption.

Fixes 115380f9a2f9 ("ntfs: update mft operations")
---
 fs/ntfs/inode.c | 21 +++++++++++++--------
 fs/ntfs/mft.c   |  3 ++-
 2 files changed, 15 insertions(+), 9 deletions(-)
fs/ntfs/inode.c +13 -8
index b6e161d51dce2..cfb753be4f6b1 100644
@@ -1232,7 +1232,8 @@ static int ntfs_read_locked_inode(struct inode *vi)
 	if (m)
 		unmap_mft_record(ni);
 err_out:
-	if (err != -EOPNOTSUPP && err != -ENOMEM && vol_err == true) {
+	if (err != -EOPNOTSUPP && err != -ENOMEM &&
+	    err != -EINTR && err != -ERESTARTSYS && vol_err == true) {
 		ntfs_error(vol->sb,
 			"Failed with error code %i.  Marking corrupt inode 0x%llx as bad.  Run chkdsk.",
 			err, ni->mft_no);
@@ -1458,12 +1459,13 @@ static int ntfs_read_locked_attr_inode(struct inode *base_vi, struct inode *vi)
 		ntfs_attr_put_search_ctx(ctx);
 	unmap_mft_record(base_ni);
 err_out:
-	if (err != -ENOENT)
+	if (err != -ENOENT && err != -EINTR && err != -ERESTARTSYS)
 		ntfs_error(vol->sb,
 			"Failed with error code %i while reading attribute inode (mft_no 0x%llx, type 0x%x, name_len %i).  Marking corrupt inode and base inode 0x%llx as bad.  Run chkdsk.",
 			err, ni->mft_no, ni->type, ni->name_len,
 			base_ni->mft_no);
-	if (err != -ENOENT && err != -ENOMEM)
+	if (err != -ENOENT && err != -ENOMEM &&
+	    err != -EINTR && err != -ERESTARTSYS)
 		NVolSetErrors(vol);
 	return err;
 }
@@ -1667,8 +1669,9 @@ static int ntfs_read_locked_index_inode(struct inode *base_vi, struct inode *vi)
 	/* Get the index bitmap attribute inode. */
 	bvi = ntfs_attr_iget(base_vi, AT_BITMAP, ni->name, ni->name_len);
 	if (IS_ERR(bvi)) {
-		ntfs_error(vi->i_sb, "Failed to get bitmap attribute.");
 		err = PTR_ERR(bvi);
+		if (err != -EINTR && err != -ERESTARTSYS)
+			ntfs_error(vi->i_sb, "Failed to get bitmap attribute.");
 		goto unm_err_out;
 	}
 	bni = NTFS_I(bvi);
@@ -1712,10 +1715,12 @@ static int ntfs_read_locked_index_inode(struct inode *base_vi, struct inode *vi)
 	if (m)
 		unmap_mft_record(base_ni);
 err_out:
-	ntfs_error(vi->i_sb,
-		"Failed with error code %i while reading index inode (mft_no 0x%llx, name_len %i.",
-		err, ni->mft_no, ni->name_len);
-	if (err != -EOPNOTSUPP && err != -ENOMEM)
+	if (err != -EINTR && err != -ERESTARTSYS)
+		ntfs_error(vi->i_sb,
+			"Failed with error code %i while reading index inode (mft_no 0x%llx, name_len %i.",
+			err, ni->mft_no, ni->name_len);
+	if (err != -EOPNOTSUPP && err != -ENOMEM &&
+	    err != -EINTR && err != -ERESTARTSYS)
 		NVolSetErrors(vol);
 	return err;
 }
fs/ntfs/mft.c +2 -1
index 60f10bb0004fd..8dcbc93a8ccac 100644
@@ -213,7 +213,8 @@ struct mft_record *map_mft_record(struct ntfs_inode *ni)
 		return m;
 
 	atomic_dec(&ni->count);
-	ntfs_error(ni->vol->sb, "Failed with error code %lu.", -PTR_ERR(m));
+	if (PTR_ERR(m) != -EINTR && PTR_ERR(m) != -ERESTARTSYS)
+		ntfs_error(ni->vol->sb, "Failed with error code %lu.", -PTR_ERR(m));
 	return m;
 }
 
Signature · 1 line
2.53.0
author maintainer
#129 lore raw

PATCH 126/438 phy: mediatek: phy-mtk-hdmi-mt8195: Fix PLL calc divisor overflow

In reply to Greg Kroah-Hartman

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

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

From: AngeloGioacchino Del Regno <[email protected]>

[ Upstream commit de7f29a1fe1dc2864d8a47f8c39d508442cae167 ]

When trying to calculate a PLL rate for target display resolutions above 2560x1440, 24bpp, 30Hz, the pixel clock value will be more than 32-bits long but the division to finally calculate the digital clock divider is being done with div_u64(), which expects a 32bit unsigned divisor.

Fix the overflow by using div64_u64() instead.

---
 drivers/phy/mediatek/phy-mtk-hdmi-mt8195.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/phy/mediatek/phy-mtk-hdmi-mt8195.c +1 -1
index 1426a2db984d5..e6ee8e0800222 100644
@@ -290,7 +290,7 @@ static int mtk_hdmi_pll_calc(struct mtk_hdmi_phy *hdmi_phy, struct clk_hw *hw,
 	posdiv2 = 1;
 
 	/* Digital clk divider, max /32 */
-	digital_div = div_u64(ns_hdmipll_ck, posdiv1 * posdiv2 * pixel_clk);
+	digital_div = div64_u64(ns_hdmipll_ck, posdiv1 * posdiv2 * pixel_clk);
 	if (!(digital_div <= 32 && digital_div >= 1))
 		return -EINVAL;
 
Signature · 1 line
2.53.0
author maintainer
#130 lore raw

PATCH 127/438 phy: mediatek: phy-mtk-hdmi-mt8195: Fix TMDS clk bit ratio setting

In reply to Greg Kroah-Hartman

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

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

From: AngeloGioacchino Del Regno <[email protected]>

[ Upstream commit 486a70ef848264dcf9a57f0bb0452848db9537de ]

The comment in the mtk_phy_tmds_clk_ratio() function clearly and correctly explains that the TMDS ratio has to be 1/10 for data rates under 3.4Gbps, and 1/40 over that.

Unfortunately though, the TXC_DIV register setting was wrong, as in value 3 means to divide by 8 and, in order to achieve the in spec 1/40 (tmds) data rate, this has to divide by 4 instead!

Add definitions for the TXC_DIV register values clearly explaining the meanings (DIV2, DIV4, DIV8), and program the correct, DIV 4, value to the register in mtk_phy_tmds_clk_ratio().

This fixes out of spec clocking and, with this change, SoCs using the MT8195 class HDMI PHYs can now successfully be configured to output 3840x2160@60Hz over HDMI.

---
 drivers/phy/mediatek/phy-mtk-hdmi-mt8195.c | 2 +-
 drivers/phy/mediatek/phy-mtk-hdmi-mt8195.h | 3 +++
 2 files changed, 4 insertions(+), 1 deletion(-)
drivers/phy/mediatek/phy-mtk-hdmi-mt8195.c +1 -1
index e6ee8e0800222..a4bc1268946d8 100644
@@ -36,7 +36,7 @@ mtk_phy_tmds_clk_ratio(struct mtk_hdmi_phy *hdmi_phy, bool enable)
 	 * clock bit ratio 1:40, under 3.4Gbps, clock bit ratio 1:10
 	 */
 	if (enable)
-		mtk_phy_update_field(regs + HDMI20_CLK_CFG, REG_TXC_DIV, 3);
+		mtk_phy_update_field(regs + HDMI20_CLK_CFG, REG_TXC_DIV, VAL_TXC_DIV4);
 	else
 		mtk_phy_clear_bits(regs + HDMI20_CLK_CFG, REG_TXC_DIV);
 }
drivers/phy/mediatek/phy-mtk-hdmi-mt8195.h +3 -0
index e26caaf4d104c..58800d7659ca0 100644
@@ -17,6 +17,9 @@
 
 #define HDMI20_CLK_CFG 0x70
 #define REG_TXC_DIV GENMASK(31, 30)
+#define VAL_TXC_DIV2 1
+#define VAL_TXC_DIV4 2
+#define VAL_TXC_DIV8 3
 
 #define HDMI_1_CFG_0 0x00
 #define RG_HDMITX21_DRV_IBIAS_CLK GENMASK(10, 5)
Signature · 1 line
2.53.0
author maintainer
#131 lore raw

PATCH 128/438 ALSA: pcm: set timer->private_data before registering the PCM timer

In reply to Greg Kroah-Hartman

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

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

From: Nguyen Ngoc Thang <[email protected]>

[ Upstream commit 1e713f9bb2ac583521f06b0eb4e22440b1e3d078 ]

snd_pcm_timer_init() calls snd_device_register() to link the new struct snd_timer into the global timer list while it still carries hw.c_resolution = snd_pcm_timer_resolution (and hw.start/hw.stop), and only afterwards sets timer->private_data = substream.

Once the timer is on the list under register_mutex, a concurrent reader can already reach it through the same mutex and invoke these callbacks. /proc/asound/timers does this via c_resolution(), and snd_timer_open()+snd_timer_start() reach start()/stop() the same way. All three dereference timer->private_data, which for this brief window is NULL, giving a NULL-pointer dereference:

  substream = timer->private_data;
  return substream->runtime ? ...   // substream is NULL

Move the private_data/private_free assignment before snd_device_register() so the timer is never visible on the list without its private_data set. On the snd_device_register() failure path, private_free() (snd_pcm_timer_free()) can now run, but it only does substream->timer = NULL, which is already NULL at that point since substream->timer is set to the new timer just once, after a successful registration -- so the failure path stays safe.

---
 sound/core/pcm_timer.c | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)
sound/core/pcm_timer.c +5 -2
index ab0e5bd70f8fa..18bedd66435dc 100644
@@ -111,12 +111,15 @@ void snd_pcm_timer_init(struct snd_pcm_substream *substream)
 			snd_pcm_direction_name(substream->stream),
 			tid.card, tid.device, tid.subdevice);
 	timer->hw = snd_pcm_timer;
+	/* Set before registering: a concurrent reader can invoke our hw
+	 * callbacks as soon as the timer is on the global list.
+	 */
+	timer->private_data = substream;
+	timer->private_free = snd_pcm_timer_free;
 	if (snd_device_register(timer->card, timer) < 0) {
 		snd_device_free(timer->card, timer);
 		return;
 	}
-	timer->private_data = substream;
-	timer->private_free = snd_pcm_timer_free;
 	substream->timer = timer;
 }
 
Signature · 1 line
2.53.0
author maintainer
#132 lore raw

PATCH 129/438 drm/msm/dp: skip PUSH_IDLE when the link was never enabled

In reply to Greg Kroah-Hartman

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

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

From: Jesse Casco <[email protected]>

[ Upstream commit e249a6e2a130c08bb4d8b0a55cbe29754307e5c9 ]

msm_dp_display_atomic_enable() returns early when link training fails, leaving ->power_on false and the main link down. msm_dp_display_atomic_disable() nevertheless writes DP_STATE_CTRL_PUSH_IDLE and waits for an idle-pattern completion that cannot arrive, so every failed enable is followed by "PUSH_IDLE pattern timedout".

Every other step of the teardown is already gated on that flag:
msm_dp_display_disable(), called from .atomic_post_disable(), returns early on !power_on. The PUSH_IDLE write is the only one that is not, so the controller's runtime-PM reference is then dropped without the link having been taken down.

On glymur (Snapdragon X2 Elite) the consequence is not a warning. The SoC does not survive it: TrustZone force-stops the SOCCP and ADSP remote processors and the machine resets silently about 50 ms later, with no oops and no panic. On an ASUS Zenbook A16 (UX3607OA), whose eDP panel does not currently train, this reproduces without any compositor or GPU involvement:

# eDP enable has already failed with "Failed link training (rc=-104)" echo 1 > /sys/class/graphics/fb0/blank

  [535.645455] === marker ===
  [535.694833] qcom_q6v5_pas d00000.remoteproc: fatal error received: \
                 sys_m_smsm.c:512:TZ force stop
  [535.694875] remoteproc remoteproc0: crash detected in soccp: type fatal error
  [535.728857] qcom_q6v5_pas 6800000.remoteproc: fatal error received: \
                 sys_m_smsm.c:783:err fatal notification received from TZ
  <SoC reset>

Gate the PUSH_IDLE write on ->power_on so the disable path is consistent with the rest of the teardown. With this applied the same sequence is harmless and the machine stays up; without it, it resets every time.

The unconditional write dates back to the original DP driver (c943b4948b58 ("drm/msm/dp: add displayPort driver support")), but the surrounding code has been restructured several times since, so no Fixes:
tag is offered.

Note that the eDP link-training failure that exposes this on the A16 is a separate problem in the glymur eDP PHY and is reported separately; this change is about not damaging the machine when training fails, for whatever reason.

Tested on ASUS Zenbook A16 (UX3607OA), Snapdragon X2 Elite Extreme, on linux-next next-20260803 and next-20260807. The machine has since been running next-20260807 with this patch as its daily driver.

Assisted-by Anthropic:Claude-Opus-5

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

---
 drivers/gpu/drm/msm/dp/dp_display.c | 14 ++++++++++++++
 1 file changed, 14 insertions(+)
drivers/gpu/drm/msm/dp/dp_display.c +14 -0
index 79e2b171e269a..a2f799cf50f63 100644
@@ -1412,6 +1412,20 @@ void msm_dp_bridge_atomic_disable(struct drm_bridge *drm_bridge,
 
 	msm_dp_display = container_of(dp, struct msm_dp_display_private, msm_dp_display);
 
+	/*
+	 * If .atomic_enable() bailed out - link training failure is the common
+	 * case - the mainlink was never brought up and ->power_on stayed false.
+	 * Driving the PUSH_IDLE pattern into a controller that was never
+	 * enabled times out, and .atomic_post_disable() then drops the
+	 * controller's runtime-PM reference without tearing the PHY back down,
+	 * because msm_dp_display_disable() returns early on !power_on.  On
+	 * glymur (Snapdragon X2 Elite) that combination is answered by a
+	 * TrustZone-level SOCCP/ADSP force-stop and a silent SoC reset.
+	 * There is nothing to push idle, so leave it alone.
+	 */
+	if (!dp->power_on)
+		return;
+
 	msm_dp_ctrl_push_idle(msm_dp_display->ctrl);
 }
 
Signature · 1 line
2.53.0
author maintainer
#133 lore raw

PATCH 130/438 drm/msm/dp: fix link bandwidth check when wide bus is enabled

In reply to Greg Kroah-Hartman

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

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

From: William Bright <[email protected]>

[ Upstream commit 58995b11dfb7dda095d23f22fa4dc79b923b5adf ]

msm_dp_display_mode_valid() halves the pixel clock when either YUV420 or wide bus is in use, then uses that halved value both for the controller pixel clock limit and for the DP link bandwidth check.

Only YUV420 halves the data crossing the link. Wide bus widens the internal DPU to DP interface to two pixels per clock, halving the controller clock. Every pixel is still transmitted, so the link bandwidth requirement remains.

As a result, modes needing up to twice the available link bandwidth pass validation. On the IMDT QCS8550 SBC (rev5 with CYPD6125), where DP runs over USB-C alt mode where only two lanes are available, 3840x2160@60 was accepted despite needing 9.6 Gbps against the 8.64 Gbps the link can carry.

Use a separate link pixel clock that is only halved for YUV420 for the bandwidth calculation, leaving the wide bus halving to apply solely to the controller pixel clock limit. With this, 4k@60 is correctly rejected and 4k@30 selected instead.

Fixes df9cf852ca30 ("drm/msm/dp: account for widebus and yuv420 during mode validation")
Assisted-by Claude:claude-opus-5

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

---
 drivers/gpu/drm/msm/dp/dp_display.c | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)
drivers/gpu/drm/msm/dp/dp_display.c +5 -2
index a2f799cf50f63..22eade11c6150 100644
@@ -698,6 +698,7 @@ enum drm_mode_status msm_dp_bridge_mode_valid(struct drm_bridge *bridge,
 	u32 mode_rate_khz = 0, supported_rate_khz = 0, mode_bpp = 0;
 	struct msm_dp *dp;
 	int mode_pclk_khz = mode->clock;
+	int link_pclk_khz;
 	bool is_yuv_420;
 
 	dp = to_dp_bridge(bridge)->msm_dp_display;
@@ -719,6 +720,8 @@ enum drm_mode_status msm_dp_bridge_mode_valid(struct drm_bridge *bridge,
 	if (is_yuv_420 && !msm_dp_display->panel->vsc_sdp_supported)
 		return MODE_NO_420;
 
+	link_pclk_khz = is_yuv_420 ? mode_pclk_khz / 2 : mode_pclk_khz;
+
 	if (is_yuv_420 || msm_dp_display->wide_bus_supported)
 		mode_pclk_khz /= 2;
 
@@ -730,9 +733,9 @@ enum drm_mode_status msm_dp_bridge_mode_valid(struct drm_bridge *bridge,
 		mode_bpp = default_bpp;
 
 	mode_bpp = msm_dp_panel_get_mode_bpp(msm_dp_display->panel,
-			mode_bpp, mode_pclk_khz);
+			mode_bpp, link_pclk_khz);
 
-	mode_rate_khz = mode_pclk_khz * mode_bpp;
+	mode_rate_khz = link_pclk_khz * mode_bpp;
 	supported_rate_khz = link_info->num_lanes * link_info->rate * 8;
 
 	if (mode_rate_khz > supported_rate_khz)
Signature · 1 line
2.53.0
author maintainer
#134 lore raw

PATCH 131/438 ASoC: Rename snd_soc_dai_link_ch_map.ch_mask to cpu_ch_mask

In reply to Greg Kroah-Hartman

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

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

From: Richard Fitzgerald <[email protected]>

[ Upstream commit 4d855d747521505b54457c96bc73577bf74b2374 ]

Rename the ch_mask member of snd_soc_dai_link_ch_map to cpu_ch_mask, as that is what it is used for.

The CPU and codec channel masks are not necessarily the same, and are quite likely different. SoundWire and I2S/TDM both support assigning different sample slots to each codec, so for example channel 0 on each codec could map to different channels at the CPU. So it's quite normal that the channel mask at the CPU end is different for each codec, but the codec channel masks are the same for each codec.

Stable-dep-of: 6b382bdfe26a ("ASoC: soc-pcm: Apply snd_soc_dai_link_ch_map.codec_ch_mask to codec params")

---
 include/sound/soc.h                 | 2 +-
 sound/soc/sdw_utils/soc_sdw_utils.c | 2 +-
 sound/soc/soc-pcm.c                 | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)
include/sound/soc.h +1 -1
index 10ad80f930c2e..300ce20fc7b70 100644
@@ -698,7 +698,7 @@ struct snd_soc_dai_link_component {
 struct snd_soc_dai_link_ch_map {
 	unsigned int cpu;
 	unsigned int codec;
-	unsigned int ch_mask;
+	unsigned int cpu_ch_mask;
 };
 
 struct snd_soc_dai_link {
sound/soc/sdw_utils/soc_sdw_utils.c +1 -1
index d8db8fc5313ee..ce2a9c28fe1a5 100644
@@ -1541,7 +1541,7 @@ int asoc_sdw_hw_params(struct snd_pcm_substream *substream,
 	 * ASoC will set the corresponding channel numbers for each cpu dai.
 	 */
 	for_each_link_ch_maps(rtd->dai_link, i, ch_maps)
-		ch_maps->ch_mask = ch_mask << (i * step);
+		ch_maps->cpu_ch_mask = ch_mask << (i * step);
 
 	return 0;
 }
sound/soc/soc-pcm.c +1 -1
index 0e49290a8c903..cb64ced211494 100644
@@ -1264,7 +1264,7 @@ static int __soc_pcm_hw_params(struct snd_pcm_substream *substream,
 		 */
 		for_each_rtd_ch_maps(rtd, j, ch_maps)
 			if (ch_maps->cpu == i)
-				ch_mask |= ch_maps->ch_mask;
+				ch_mask |= ch_maps->cpu_ch_mask;
 
 		/* fixup cpu channel number */
 		if (ch_mask)
Signature · 1 line
2.53.0
author maintainer
#135 lore raw

PATCH 132/438 ASoC: Add codec_ch_mask to snd_soc_dai_link_ch_map

In reply to Greg Kroah-Hartman

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

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

From: Richard Fitzgerald <[email protected]>

[ Upstream commit 88b14c0d0bab5c0f3e7c641f274e3c70210c0e36 ]

Add a codec_ch_mask member to snd_soc_dai_link_ch_map.

The CPU and codec channel masks are not necessarily the same, and are quite likely different. SoundWire and I2S/TDM both support assigning different sample slots to each codec, so for example channel 0 on each codec could map to different channels at the CPU.

It is also possible for one TX channel to map to multiple RX channels. So it isn't _always_ safe to assume that the total number of set bits in the CPU ch_mask is the same as the total number of enabled channels on the codec.

For example consider this mapping on a capture stream:

CPU0 CODEC0 cpu_ch_mask = 0x03
CPU1 CODEC0 cpu_ch_mask = 0x03

This could be either four TX channels on the codec split across two receiving CPUs, or two TX channels on the codec duplicated to two CPUs.

Stable-dep-of: 6b382bdfe26a ("ASoC: soc-pcm: Apply snd_soc_dai_link_ch_map.codec_ch_mask to codec params")

---
 include/sound/soc.h | 1 +
 1 file changed, 1 insertion(+)
include/sound/soc.h +1 -0
index 300ce20fc7b70..1d2772758fb69 100644
@@ -699,6 +699,7 @@ struct snd_soc_dai_link_ch_map {
 	unsigned int cpu;
 	unsigned int codec;
 	unsigned int cpu_ch_mask;
+	unsigned int codec_ch_mask;
 };
 
 struct snd_soc_dai_link {
Signature · 1 line
2.53.0
author maintainer
#136 lore raw

PATCH 133/438 ASoC: soc-pcm: Apply snd_soc_dai_link_ch_map.codec_ch_mask to codec params

In reply to Greg Kroah-Hartman

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

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

From: Richard Fitzgerald <[email protected]>

[ Upstream commit 6b382bdfe26a2232091bf743e454e6794295783e ]

In __soc_pcm_hw_params() if there is a snd_soc_dai_link_ch_map with non-zero codec_ch_mask, use that channel mask to restrict which channels are enabled on the codec. But only if there isn't a TDM mask.

It is possible that a snd_soc_dai_link_ch_map could include the same codec multiple times on different CPUs so the for_each_rtd_ch_maps() loop accumulates the channel masks for all entries of that codec.

If a TDM mask was also set, it takes priority and is used instead of any possible snd_soc_dai_link_ch_map entries. (They cannot be ANDed together because the bit positions are indicating different things: TDM is a bit for each TDM slot, codec_ch_mask is a bit for each codec channel.)

This fixes a problem of incorrect TX channels enabled on the codec when multiple codecs are aggregated on a single capture link. For example:

- Two CPUs with six 4-channel codecs.
- The machine driver chooses to assign one channel from each codec to one channel on the CPU
- But the codec hw_params() would be passed a channel count of 6, which
(a) is more channels than the codec has and (b) allows enabling channels that should not be driving the audio bus.

Fixes ac950278b087 ("ASoC: add N cpus to M codecs dai link support")
---
 sound/soc/soc-pcm.c | 16 ++++++++++++----
 1 file changed, 12 insertions(+), 4 deletions(-)
sound/soc/soc-pcm.c +12 -4
index cb64ced211494..3137c091bdb88 100644
@@ -1206,7 +1206,9 @@ static int __soc_pcm_hw_params(struct snd_pcm_substream *substream,
 		goto out;
 
 	for_each_rtd_codec_dais(rtd, i, codec_dai) {
-		unsigned int tdm_mask = snd_soc_dai_tdm_mask_get(codec_dai, substream->stream);
+		unsigned int ch_mask = snd_soc_dai_tdm_mask_get(codec_dai, substream->stream);
+		struct snd_soc_dai_link_ch_map *ch_maps;
+		int j;
 
 		/*
 		 * Skip CODECs which don't support the current stream type,
@@ -1228,9 +1230,15 @@ static int __soc_pcm_hw_params(struct snd_pcm_substream *substream,
 		/* copy params for each codec */
 		tmp_params = *params;
 
-		/* fixup params based on TDM slot masks */
-		if (tdm_mask)
-			soc_pcm_codec_params_fixup(&tmp_params, tdm_mask);
+		/* fixup params based on TDM or ch_map masks */
+		if (!ch_mask) {
+			for_each_rtd_ch_maps(rtd, j, ch_maps)
+				if (ch_maps->codec == i)
+					ch_mask |= ch_maps->codec_ch_mask;
+		}
+
+		if (ch_mask)
+			soc_pcm_codec_params_fixup(&tmp_params, ch_mask);
 
 		ret = snd_soc_dai_hw_params(codec_dai, substream,
 					    &tmp_params);
Signature · 1 line
2.53.0
author maintainer
#137 lore raw

PATCH 134/438 spi: spi-qpic-snand: avoid writing QPIC_EBI2_ECC_BUF_CFG register

In reply to Greg Kroah-Hartman

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

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

From: Gabor Juhos <[email protected]>

[ Upstream commit 930a7312c946bf4731721cadd82bb9a2ada496ca ]

The description of commit bfb34eced559 ("mtd: rawnand: qcom: avoid writing to obsolete register") says this:

"QPIC_EBI2_ECC_BUF_CFG register got obsolete from QPIC V2.0 onwards. Avoid writing this register if QPIC version is V2.0 or newer."

Although the referenced commit is related to the 'qcom-nandc' driver, however the hardware supported by the current driver is also based on QPIC v2.0 so we should avoid writing that register here as well.

Remove the register writing code to avoid undefined behaviour.

---
 drivers/spi/spi-qpic-snand.c | 4 ----
 1 file changed, 4 deletions(-)
drivers/spi/spi-qpic-snand.c +0 -4
index b6c58d9cfe143..8388c637eee00 100644
@@ -740,8 +740,6 @@ static int qcom_spi_read_cw_raw(struct qcom_nand_controller *snandc, u8 *data_bu
 
 	qcom_write_reg_dma(snandc, &snandc->regs->addr0, NAND_ADDR0, 2, 0);
 	qcom_write_reg_dma(snandc, &snandc->regs->cfg0, NAND_DEV0_CFG0, 3, 0);
-	qcom_write_reg_dma(snandc, &snandc->regs->ecc_buf_cfg, NAND_EBI2_ECC_BUF_CFG, 1, 0);
-
 	qcom_write_reg_dma(snandc, &snandc->regs->erased_cw_detect_cfg_clr,
 			   NAND_ERASED_CW_DETECT_CFG, 1, 0);
 	qcom_write_reg_dma(snandc, &snandc->regs->erased_cw_detect_cfg_set,
@@ -1008,8 +1006,6 @@ static void qcom_spi_config_page_write(struct qcom_nand_controller *snandc)
 {
 	qcom_write_reg_dma(snandc, &snandc->regs->addr0, NAND_ADDR0, 2, 0);
 	qcom_write_reg_dma(snandc, &snandc->regs->cfg0, NAND_DEV0_CFG0, 3, 0);
-	qcom_write_reg_dma(snandc, &snandc->regs->ecc_buf_cfg, NAND_EBI2_ECC_BUF_CFG,
-			   1, NAND_BAM_NEXT_SGL);
 }
 
 static void qcom_spi_config_cw_write(struct qcom_nand_controller *snandc)
Signature · 1 line
2.53.0
author maintainer
#138 lore raw

PATCH 135/438 Input: trackpoint - fix the inertia attribute name in the ABI document

In reply to Greg Kroah-Hartman

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

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

From: Karl Mehltretter <[email protected]>

[ Upstream commit 45b0037899704caf9078be2be4de69361ca7d933 ]

The attribute is created as "inertia" (TRACKPOINT_INT_ATTR(inertia, ...) in drivers/input/mouse/trackpoint.c); the ABI file spells the path "intertia". The description below it already says inertia.

Fix the spelling.

Fixes aebb47d4e7a9 ("Input: trackpoint: document sysfs interface")
Assisted-by LLM
---
 Documentation/ABI/testing/sysfs-devices-platform-trackpoint | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
Documentation/ABI/testing/sysfs-devices-platform-trackpoint +1 -1
index df11901a6b3df..7954434b0434a 100644
@@ -5,7 +5,7 @@ Contact:	[email protected]
 Description:
 		(RW) Trackpoint sensitivity.
 
-What:		/sys/devices/platform/i8042/.../intertia
+What:		/sys/devices/platform/i8042/.../inertia
 Date:		Aug, 2005
 KernelVersion:	2.6.14
 Contact:	[email protected]
Signature · 1 line
2.53.0
author maintainer
#139 lore raw

PATCH 136/438 objtool: Validate disassembler headers in libopcodes probe

In reply to Greg Kroah-Hartman

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

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

From: Ulises Mendez Martinez <[email protected]>

[ Upstream commit 7e61560628d17ea6b1d8ee370f6d42694cff8758 ]

commit 3f2de814c059 ("objtool: Fix libopcodes linking with static libraries") tested for libopcodes availability by linking a test snippet with a forward declaration of disassemble_init_for_target().

However, testing symbol linkage with an extern declaration only verifies the presence of the library (.so/.a) and bypasses checking for development headers (binutils-dev). On systems where libopcodes is present without development headers installed, the probe succeeds, enabling BUILD_DISAS. Subsequent compilation of objtool then fails:

fatal error: 'bfd.h' file not found

    113 | #include <bfd.h>

Additionally, the probe invokes $(HOSTCC) without $(HOSTCFLAGS), ignoring any sysroot or include flags specified for the host compiler.

Fix this by including <bfd.h> and <dis-asm.h> directly in the test snippet, passing $(HOSTCFLAGS) so host compiler options are respected, and defining PACKAGE="objtool" to satisfy the configuration check in <bfd.h>.

Fixes 3f2de814c059 ("objtool: Fix libopcodes linking with static libraries")
Fixes 436326bc525d ("objtool: fix build failure due to missing libopcodes check")
Assisted-by Antigravity:Gemini-Next
---
 tools/objtool/Makefile | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)
tools/objtool/Makefile +5 -3
index a4484fd22a96d..4cc2e756af840 100644
@@ -89,9 +89,11 @@ LIBOPCODES_LIBS := $(shell \
 		    "-lopcodes -lbfd" \
 		    "-lopcodes -lbfd -liberty" \
 		    "-lopcodes -lbfd -liberty -lz"; do \
-		echo 'extern void disassemble_init_for_target(void *);' \
-		     'int main(void) { disassemble_init_for_target(0); return 0; }' | \
-			$(HOSTCC) -xc - -o /dev/null $$libs 2>/dev/null && \
+		printf '%s\n' \
+		       '$(pound)include <bfd.h>' \
+		       '$(pound)include <dis-asm.h>' \
+		       'int main(void) { disassemble_init_for_target(0); return 0; }' | \
+			$(HOSTCC) $(HOSTCFLAGS) -DPACKAGE='"objtool"' -xc - -o /dev/null $$libs 2>/dev/null && \
 			echo "$$libs" && break; \
 	done)
 
Signature · 1 line
2.53.0
author maintainer
#140 lore raw

PATCH 137/438 gpio: virtuser: skip free_irq when no IRQ is installed

In reply to Greg Kroah-Hartman

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

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

From: Runyu Xiao <[email protected]>

[ Upstream commit 50fd0ada8d37587223001600933270b59cb30e19 ]

Disabling interrupt monitoring uses atomic_xchg() to clear the stored IRQ. When monitoring is already disabled, atomic_xchg() returns 0. It must not be passed to free_irq().

The bug is reproducible on an x86_64 QEMU guest with CONFIG_GPIO_VIRTUSER=y and CONFIG_GPIO_SIM=y. Configure a live gpio-virtuser device through configfs. Its input lookup must refer to a live gpio-sim bank, such as key gpio-sim-test with offset 0. The consumer's dev_name attribute is shown as <dev> below; then run:

echo 0 > /sys/kernel/debug/gpio-virtuser/<dev>/gpiod:input:0/interrupts

On an unpatched kernel, this reaches gpio_virtuser_interrupts_set() with ld->irq still at its initial value 0, and free_irq() reports:

Trying to free already-free IRQ 0

The same reproducer completes without the warning on the patched kernel.

Fixes 91581c4b3f29 ("gpio: virtuser: new virtual testing driver for the GPIO API")
Assisted-by LLM
---
 drivers/gpio/gpio-virtuser.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/gpio/gpio-virtuser.c +2 -1
index 7d0d366be37a4..d5876c19cfefa 100644
@@ -692,7 +692,8 @@ static int gpio_virtuser_interrupts_set(void *data, u64 val)
 		atomic_set(&ld->irq, irq);
 	} else {
 		irq = atomic_xchg(&ld->irq, 0);
-		free_irq(irq, ld);
+		if (irq)
+			free_irq(irq, ld);
 	}
 
 	return 0;
Signature · 1 line
2.53.0
author maintainer
#141 lore raw

PATCH 138/438 ALSA: usb: 6fire: Avoid embedded URBs

In reply to Greg Kroah-Hartman

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

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

From: Takashi Iwai <[email protected]>

[ Upstream commit 9fe49dbc023e82dfaee7b245997d820d01742a9a ]

The USB 6fire driver uses URBs embedded in different structs for PCM, MIDI and communication, and this is basically a buggy implementation nowadays; since a URB is managed with a refcount, this may lead to a UAF when the URB is released asynchronously.

For addressing the problem, this patch converts those embedded URBs to ones that are properly allocated via usb_alloc_urb(). The pcm_urb.packets[] is gone, as it's allocated by usb_alloc_urb(), hence it's found in urb.iso_frame_desc[] instead.

The conversions are rather straightforward; each embedded struct urb is changed to a pointer, and its callers are updated accordingly. The resource for those structs are released in the common destructor functions (usb6fire_comm_free(), etc), which are called at both the init error path and the disconnect.

No functional changes, only compile-tested.

Stable-dep-of: 1589afe2d099 ("ALSA: 6fire: fix OOB write from device-reported iso length")

---
 sound/usb/6fire/comm.c |  42 +++++++++-----
 sound/usb/6fire/comm.h |   2 +-
 sound/usb/6fire/midi.c |  43 +++++++++-----
 sound/usb/6fire/midi.h |   2 +-
 sound/usb/6fire/pcm.c  | 128 ++++++++++++++++++++++++-----------------
 sound/usb/6fire/pcm.h  |   5 +-
 6 files changed, 136 insertions(+), 86 deletions(-)
sound/usb/6fire/comm.c +28 -14
index d3b7cab856997..510d310824e91 100644
@@ -21,7 +21,6 @@ enum {
 static void usb6fire_comm_init_urb(struct comm_runtime *rt, struct urb *urb,
 		u8 *buffer, void *context, void(*handler)(struct urb *urb))
 {
-	usb_init_urb(urb);
 	urb->transfer_buffer = buffer;
 	urb->pipe = usb_sndintpipe(rt->chip->dev, COMM_EP);
 	urb->complete = handler;
@@ -142,6 +141,19 @@ static int usb6fire_comm_write16(struct comm_runtime *rt, u8 request,
 	return ret;
 }
 
+static void usb6fire_comm_free(struct comm_runtime *rt)
+{
+	if (!rt)
+		return;
+
+	if (rt->chip)
+		rt->chip->comm = NULL;
+
+	usb_free_urb(rt->receiver);
+	kfree(rt->receiver_buffer);
+	kfree(rt);
+}
+
 int usb6fire_comm_init(struct sfire_chip *chip)
 {
 	struct comm_runtime *rt = kzalloc_obj(struct comm_runtime);
@@ -153,14 +165,18 @@ int usb6fire_comm_init(struct sfire_chip *chip)
 
 	rt->receiver_buffer = kzalloc(COMM_RECEIVER_BUFSIZE, GFP_KERNEL);
 	if (!rt->receiver_buffer) {
-		kfree(rt);
-		return -ENOMEM;
+		ret = -ENOMEM;
+		goto error;
 	}
 
-	urb = &rt->receiver;
+	urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!urb) {
+		ret = -ENOMEM;
+		goto error;
+	}
+	rt->receiver = urb;
 	rt->serial = 1;
 	rt->chip = chip;
-	usb_init_urb(urb);
 	rt->init_urb = usb6fire_comm_init_urb;
 	rt->write8 = usb6fire_comm_write8;
 	rt->write16 = usb6fire_comm_write16;
@@ -175,13 +191,15 @@ int usb6fire_comm_init(struct sfire_chip *chip)
 	urb->interval = 1;
 	ret = usb_submit_urb(urb, GFP_KERNEL);
 	if (ret < 0) {
-		kfree(rt->receiver_buffer);
-		kfree(rt);
 		dev_err(&chip->dev->dev, "cannot create comm data receiver.");
-		return ret;
+		goto error;
 	}
 	chip->comm = rt;
 	return 0;
+
+ error:
+	usb6fire_comm_free(rt);
+	return ret;
 }
 
 void usb6fire_comm_abort(struct sfire_chip *chip)
@@ -189,14 +207,10 @@ void usb6fire_comm_abort(struct sfire_chip *chip)
 	struct comm_runtime *rt = chip->comm;
 
 	if (rt)
-		usb_poison_urb(&rt->receiver);
+		usb_poison_urb(rt->receiver);
 }
 
 void usb6fire_comm_destroy(struct sfire_chip *chip)
 {
-	struct comm_runtime *rt = chip->comm;
-
-	kfree(rt->receiver_buffer);
-	kfree(rt);
-	chip->comm = NULL;
+	usb6fire_comm_free(chip->comm);
 }
sound/usb/6fire/comm.h +1 -1
index 2447d7ecf1798..89976f510f6c2 100644
@@ -19,7 +19,7 @@ enum /* settings for comm */
 struct comm_runtime {
 	struct sfire_chip *chip;
 
-	struct urb receiver;
+	struct urb *receiver;
 	u8 *receiver_buffer;
 
 	u8 serial; /* urb serial */
sound/usb/6fire/midi.c +30 -13

86 lines not on this page · raw message

sound/usb/6fire/midi.h +1 -1
index 47640c845903b..8716ab8a863ae 100644
@@ -22,7 +22,7 @@ struct midi_runtime {
 	spinlock_t in_lock;
 	spinlock_t out_lock;
 	struct snd_rawmidi_substream *out;
-	struct urb out_urb;
+	struct urb *out_urb;
 	u8 out_serial; /* serial number of out packet */
 	u8 *out_buffer;
 	int buffer_offset;
sound/usb/6fire/pcm.c +75 -53

267 lines not on this page · raw message

sound/usb/6fire/pcm.h +1 -4
index 5a092dfd69f5a..b586fe220fd11 100644
@@ -24,10 +24,7 @@ enum /* settings for pcm */
 struct pcm_urb {
 	struct sfire_chip *chip;
 
-	/* BEGIN DO NOT SEPARATE */
-	struct urb instance;
-	struct usb_iso_packet_descriptor packets[PCM_N_PACKETS_PER_URB];
-	/* END DO NOT SEPARATE */
+	struct urb *instance;
 	u8 *buffer;
 
 	struct pcm_urb *peer;
Signature · 1 line
2.53.0
author maintainer
#142 lore raw

PATCH 139/438 ALSA: 6fire: fix OOB write from device-reported iso length

In reply to Greg Kroah-Hartman

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

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

From: Xiang Mei <[email protected]>

[ Upstream commit 1589afe2d099d3e817873bc474676968d7080410 ]

usb6fire_pcm_in_urb_handler() sizes each outgoing isochronous packet as (actual_length - 4) / (in_n_analog << 2) * (out_n_analog << 2) + 4, where actual_length is the unsigned length the device reported for the matching IN packet. A packet completed with status 0 and actual_length < 4 wraps the subtraction to 0x7fffffec; a zero-length isochronous packet is legal on the bus, and the preceding loop rejects only non-zero status. The sum reaches memset() on out_urb->buffer, a 4832-byte object from kcalloc(PCM_MAX_PACKET_SIZE, PCM_N_PACKETS_PER_URB).

Even without the wrap the result is out of bounds: at 88.2/96 kHz the 4-in/6-out scaling turns a full 420-byte IN packet into 628, so eight packets span 5024 bytes of that buffer. usb_submit_urb() rejects an over-long descriptor only after the memset() and the usb6fire_pcm_playback() copy of user PCM data have run.

Guard the subtraction as the sibling usb6fire_pcm_capture() already does, and limit the frame count to what fits in rt->out_packet_size, the OUT endpoint's wMaxPacketSize. This bounds total_length by the buffer size while keeping each packet length aligned to a whole output frame.

  BUG: KASAN: out-of-bounds in usb6fire_pcm_in_urb_handler (sound/usb/6fire/pcm.c:338)
  Write of size 18446744073709551456 at addr ffff88802a3d0000 by task vhci_rx/5018
  Call Trace:
   dump_stack_lvl (lib/dump_stack.c:94 lib/dump_stack.c:120)
   print_report (mm/kasan/report.c:378 mm/kasan/report.c:482)
   kasan_report (mm/kasan/report.c:595)
   kasan_check_range (mm/kasan/generic.c:186 mm/kasan/generic.c:200)
   __asan_memset (mm/kasan/shadow.c:84)
   usb6fire_pcm_in_urb_handler (sound/usb/6fire/pcm.c:338)
   __usb_hcd_giveback_urb (drivers/usb/core/hcd.c:1657)
   usb_hcd_giveback_urb (drivers/usb/core/hcd.c:1741)
   vhci_rx_loop (drivers/usb/usbip/vhci_rx.c:107 drivers/usb/usbip/vhci_rx.c:242)
   kthread (kernel/kthread.c:436)
   ret_from_fork (arch/x86/kernel/process.c:158)
   ret_from_fork_asm (arch/x86/entry/entry_64.S:245)

  Allocated by task 10:
   __kmalloc_cache_noprof (mm/slub.c:5563)
   usb6fire_pcm_init (sound/usb/6fire/pcm.c:560 sound/usb/6fire/pcm.c:595)
   usb6fire_chip_probe (sound/usb/6fire/chip.c:133)
   usb_probe_interface (drivers/usb/core/driver.c:399)

  The buggy address belongs to the object at ffff88802a3d0000
   which belongs to the cache kmalloc-8k of size 8192
  The buggy address is located 0 bytes inside of
   4832-byte region [ffff88802a3d0000, ffff88802a3d12e0)
  Kernel panic - not syncing: Fatal exception in interrupt
---
 sound/usb/6fire/pcm.c | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)
sound/usb/6fire/pcm.c +10 -2
index 21789db6657d3..0285d79ace0fc 100644
@@ -335,11 +335,19 @@ static void usb6fire_pcm_in_urb_handler(struct urb *usb_urb)
 
 	/* setup out urb structure */
 	for (i = 0; i < PCM_N_PACKETS_PER_URB; i++) {
+		unsigned int frames = 0;
+
 		isoc_out = &out_urb->instance->iso_frame_desc[i];
 		isoc_in = &in_urb->instance->iso_frame_desc[i];
+		if (isoc_in->actual_length > 4)
+			frames = (isoc_in->actual_length - 4)
+					/ (rt->in_n_analog << 2);
+		frames = min_t(unsigned int, frames,
+				       (rt->out_packet_size - 4)
+				       / (rt->out_n_analog << 2));
+
 		isoc_out->offset = total_length;
-		isoc_out->length = (isoc_in->actual_length - 4) / (rt->in_n_analog << 2)
-				* (rt->out_n_analog << 2) + 4;
+		isoc_out->length = frames * (rt->out_n_analog << 2) + 4;
 		isoc_out->status = 0;
 		total_length += isoc_out->length;
 	}
Signature · 1 line
2.53.0
author maintainer
#143 lore raw

PATCH 140/438 ksmbd: fix malformed procfs status output

In reply to Greg Kroah-Hartman

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

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

From: Namjae Jeon <[email protected]>

[ Upstream commit 1f7dd03a88a8405143aab195f6fa9ed2243494b1 ]

The ksmbd procfs monitoring files produce misleading or malformed output. The constant-name helper uses a bitwise test for enum values. This omits zero-valued constants and can print multiple names for one lease state. It also unconditionally emits a newline, splitting entries in the open-file table across two lines. Session capabilities are printed as numeric flag values even though a table of descriptive names is available.

Use exact matching for enum values. Print flag names as a comma-separated list, preserving unknown bits as hexadecimal values. Let callers control line termination so each open-file entry remains on one line. Print common session properties once, and report signing and encryption independently.

Adjust client and open-file column widths for IPv6 addresses and 64-bit file IDs, and fix the misspelled OPLOCK_EXCLUSIVE name. Also expose and maintain the total request count alongside the per-command counters.

Stable-dep-of: d7fd1f98607f ("ksmbd: follow SMB2 session expiration semantics")

---
 fs/smb/server/connection.c        | 12 ++---
 fs/smb/server/mgmt/user_session.c | 75 +++++++++++++------------------
 fs/smb/server/misc.h              |  7 +--
 fs/smb/server/proc.c              | 38 ++++++++++++++++
 fs/smb/server/stats.h             |  4 +-
 fs/smb/server/vfs_cache.c         | 16 ++++---
 6 files changed, 89 insertions(+), 63 deletions(-)
fs/smb/server/connection.c +6 -6
index 39ac777fd4527..cef279b8fd27e 100644
@@ -35,9 +35,9 @@ static int proc_show_clients(struct seq_file *m, void *v)
 	struct timespec64 now, t;
 	int i;
 
-	seq_printf(m, "#%-20s %-10s %-10s %-10s %-10s %-10s\n",
-			"<name>", "<dialect>", "<credits>", "<open files>",
-			"<requests>", "<last active>");
+	seq_printf(m, "#%-40s %-10s %-10s %-12s %-10s %s\n",
+		   "<client>", "<dialect>", "<credits>", "<open files>",
+		   "<requests>", "<last active>");
 
 	down_read(&conn_list_lock);
 	hash_for_each(conn_list, i, conn, hlist) {
@@ -46,11 +46,11 @@ static int proc_show_clients(struct seq_file *m, void *v)
 		t = timespec64_sub(now, t);
 #if IS_ENABLED(CONFIG_IPV6)
 		if (!conn->inet_addr)
-			seq_printf(m, "%-20pI6c", &conn->inet6_addr);
+			seq_printf(m, " %-40pI6c", &conn->inet6_addr);
 		else
 #endif
-			seq_printf(m, "%-20pI4", &conn->inet_addr);
-		seq_printf(m, "   0x%-10x %-10u %-12d %-10d %ptT\n",
+			seq_printf(m, " %-40pI4", &conn->inet_addr);
+		seq_printf(m, " 0x%-8x %-10u %-12d %-10d %ptT\n",
 			   conn->dialect,
 			   conn->total_credits,
 			   atomic_read(&conn->stats.open_files_count),
fs/smb/server/mgmt/user_session.c +30 -45

103 lines not on this page · raw message

fs/smb/server/misc.h +2 -5
index c7b063f571a7a..1faaddd0f5f75 100644
@@ -53,11 +53,8 @@ void ksmbd_proc_show_flag_names(struct seq_file *m,
 				const struct ksmbd_const_name *table,
 				int count,
 				unsigned int flags);
-void ksmbd_proc_show_const_name(struct seq_file *m,
-				const char *format,
-				const struct ksmbd_const_name *table,
-				int count,
-				unsigned int const_value);
+const char *ksmbd_proc_const_name(const struct ksmbd_const_name *table,
+				  int count, unsigned int const_value);
 #else
 static inline int ksmbd_proc_init(void) { return 0; }
 static inline void ksmbd_proc_cleanup(void) {}
fs/smb/server/proc.c +38 -0
index 5c9f3f314dbe1..13742a2404a58 100644
@@ -27,6 +27,42 @@ struct proc_dir_entry *ksmbd_proc_create(const char *name,
 			   show, v);
 }
 
+void ksmbd_proc_show_flag_names(struct seq_file *m,
+				const struct ksmbd_const_name *table,
+				int count, unsigned int flags)
+{
+	unsigned int remaining = flags;
+	bool separator = false;
+	int i;
+
+	for (i = 0; i < count; i++) {
+		unsigned int flag = table[i].const_value;
+
+		if (!flag || (remaining & flag) != flag)
+			continue;
+		seq_printf(m, "%s%s", separator ? "," : "", table[i].name);
+		separator = true;
+		remaining &= ~flag;
+	}
+
+	if (remaining)
+		seq_printf(m, "%s0x%08x", separator ? "," : "", remaining);
+	else if (!separator)
+		seq_puts(m, "none");
+}
+
+const char *ksmbd_proc_const_name(const struct ksmbd_const_name *table,
+				  int count, unsigned int const_value)
+{
+	int i;
+
+	for (i = 0; i < count; i++) {
+		if (table[i].const_value == const_value)
+			return table[i].name;
+	}
+	return NULL;
+}
+
 struct ksmbd_const_smb2_process_req {
 	unsigned int const_value;
 	const char *name;
@@ -71,6 +107,8 @@ static int proc_show_ksmbd_stats(struct seq_file *m, void *v)
 		   ksmbd_counter_sum(KSMBD_COUNTER_SESSIONS));
 	seq_printf(m, "tree connects: %lld\n",
 		   ksmbd_counter_sum(KSMBD_COUNTER_TREE_CONNS));
+	seq_printf(m, "requests: %lld\n",
+		   ksmbd_counter_sum(KSMBD_COUNTER_REQUESTS));
 	seq_printf(m, "read bytes: %lld\n",
 		   ksmbd_counter_sum(KSMBD_COUNTER_READ_BYTES));
 	seq_printf(m, "written bytes: %lld\n",
fs/smb/server/stats.h +3 -1
index b60c30c690770..08ee66f91eaab 100644
@@ -52,8 +52,10 @@ static inline void ksmbd_counter_sub(int type, s64 value)
 
 static inline void ksmbd_counter_inc_reqs(unsigned int cmd)
 {
-	if (cmd < KSMBD_COUNTER_MAX_REQS)
+	if (cmd < KSMBD_COUNTER_MAX_REQS) {
+		percpu_counter_inc(&ksmbd_counters.counters[KSMBD_COUNTER_REQUESTS]);
 		percpu_counter_inc(&ksmbd_counters.counters[KSMBD_COUNTER_FIRST_REQ + cmd]);
+	}
 }
 
 static inline s64 ksmbd_counter_sum(int type)
fs/smb/server/vfs_cache.c +10 -6
index a23bd9b1a68bc..1b5f4efb6ae01 100644
@@ -66,7 +66,7 @@ static const struct ksmbd_const_name ksmbd_lease_const_names[] = {
 static const struct ksmbd_const_name ksmbd_oplock_const_names[] = {
 	{SMB2_OPLOCK_LEVEL_NONE, "OPLOCK_NONE"},
 	{SMB2_OPLOCK_LEVEL_II, "OPLOCK_II"},
-	{SMB2_OPLOCK_LEVEL_EXCLUSIVE, "OPLOCK_EXECL"},
+	{SMB2_OPLOCK_LEVEL_EXCLUSIVE, "OPLOCK_EXCLUSIVE"},
 	{SMB2_OPLOCK_LEVEL_BATCH, "OPLOCK_BATCH"},
 };
 
@@ -76,14 +76,14 @@ static int proc_show_files(struct seq_file *m, void *v)
 	unsigned int id;
 	struct oplock_info *opinfo;
 
-	seq_printf(m, "#%-10s %-10s %-10s %-10s %-15s %-10s %-10s %s\n",
+	seq_printf(m, "#%-10s %-18s %-18s %-10s %-16s %-10s %-10s %s\n",
 		   "<tree id>", "<pid>", "<vid>", "<refcnt>",
 		   "<oplock>", "<daccess>", "<saccess>",
 		   "<name>");
 
 	read_lock(&global_ft.lock);
 	idr_for_each_entry(global_ft.idr, fp, id) {
-		seq_printf(m, "%#-10x %#-10llx %#-10llx %#-10x",
+		seq_printf(m, " %#-10x %#-18llx %#-18llx %#-10x",
 			   fp->tcon ? fp->tcon->id : 0,
 			   fp->persistent_id,
 			   fp->volatile_id,
@@ -93,6 +93,7 @@ static int proc_show_files(struct seq_file *m, void *v)
 		opinfo = rcu_dereference(fp->f_opinfo);
 		if (opinfo) {
 			const struct ksmbd_const_name *const_names;
+			const char *name;
 			int count;
 			unsigned int level;
 
@@ -106,11 +107,14 @@ static int proc_show_files(struct seq_file *m, void *v)
 				level = opinfo->level;
 			}
 			rcu_read_unlock();
-			ksmbd_proc_show_const_name(m, " %-15s",
-						   const_names, count, level);
+			name = ksmbd_proc_const_name(const_names, count, level);
+			if (name)
+				seq_printf(m, " %-16s", name);
+			else
+				seq_printf(m, " 0x%-14x", level);
 		} else {
 			rcu_read_unlock();
-			seq_printf(m, " %-15s", " ");
+			seq_printf(m, " %-16s", " ");
 		}
 
 		seq_printf(m, " %#010x %#010x %s\n",
Signature · 1 line
2.53.0
author maintainer
#144 lore raw

PATCH 141/438 ksmbd: extend procfs server statistics

In reply to Greg Kroah-Hartman

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

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

From: Namjae Jeon <[email protected]>

[ Upstream commit 4c670ccd5790816fc0f5714d5ee3c67dd5a9c67a ]

The server proc entry does not expose configured limits or enough outcome data to distinguish protocol errors from transport stalls.

Report the server state, listener and signing configuration, connection limits, timeout values, current client and open-file totals, IPC activity, and durable scavenger state. Classify processed SMB2 response statuses by NTSTATUS severity and provide counters for common error groups while retaining the per-command counters.

Stable-dep-of: d7fd1f98607f ("ksmbd: follow SMB2 session expiration semantics")

---
 fs/smb/server/proc.c       | 121 +++++++++++++++++++++++++++++++++----
 fs/smb/server/server.c     |   7 ++-
 fs/smb/server/smb_common.h |   2 +-
 fs/smb/server/stats.h      |  53 +++++++++++++++-
 fs/smb/server/vfs_cache.c  |  10 +++
 fs/smb/server/vfs_cache.h  |   1 +
 6 files changed, 176 insertions(+), 18 deletions(-)
fs/smb/server/proc.c +108 -13
index 13742a2404a58..826353ed05538 100644
@@ -11,10 +11,12 @@
 #include <linux/seq_file.h>
 
 #include "misc.h"
+#include "connection.h"
 #include "server.h"
 #include "stats.h"
 #include "smb_common.h"
 #include "smb2pdu.h"
+#include "vfs_cache.h"
 
 static struct proc_dir_entry *ksmbd_proc_fs;
 struct ksmbd_counters ksmbd_counters;
@@ -90,34 +92,127 @@ static const struct ksmbd_const_smb2_process_req smb2_process_req[KSMBD_COUNTER_
 	{le16_to_cpu(SMB2_OPLOCK_BREAK), "SMB2_OPLOCK_BREAK"},
 };
 
+static const char *ksmbd_server_state_string(void)
+{
+	switch (READ_ONCE(server_conf.state)) {
+	case SERVER_STATE_STARTING_UP:
+		return "starting";
+	case SERVER_STATE_RUNNING:
+		return "running";
+	case SERVER_STATE_RESETTING:
+		return "resetting";
+	case SERVER_STATE_SHUTTING_DOWN:
+		return "shutdown";
+	default:
+		return "unknown";
+	}
+}
+
+static const char *ksmbd_signing_mode_string(void)
+{
+	switch (server_conf.signing) {
+	case KSMBD_CONFIG_OPT_DISABLED:
+		return "disabled";
+	case KSMBD_CONFIG_OPT_MANDATORY:
+		return "mandatory";
+	case KSMBD_CONFIG_OPT_AUTO:
+		return "auto";
+	default:
+		return "unknown";
+	}
+}
+
+static void proc_show_runtime_totals(struct seq_file *m)
+{
+	struct ksmbd_conn *conn;
+	unsigned int clients = 0;
+	unsigned int open_files = 0;
+	int i;
+
+	down_read(&conn_list_lock);
+	hash_for_each(conn_list, i, conn, hlist) {
+		clients++;
+		open_files += atomic_read(&conn->stats.open_files_count);
+	}
+	up_read(&conn_list_lock);
+
+	seq_printf(m, "clients:\t%u\n", clients);
+	seq_printf(m, "open_files:\t%u\n", open_files);
+}
+
 static int proc_show_ksmbd_stats(struct seq_file *m, void *v)
 {
 	int i;
 
 	seq_puts(m, "Server\n");
-	seq_printf(m, "name: %s\n", ksmbd_server_string());
-	seq_printf(m, "netbios: %s\n", ksmbd_netbios_name());
-	seq_printf(m, "work group: %s\n", ksmbd_work_group());
-	seq_printf(m, "min protocol: %s\n", ksmbd_get_protocol_string(server_conf.min_protocol));
-	seq_printf(m, "max protocol: %s\n", ksmbd_get_protocol_string(server_conf.max_protocol));
-	seq_printf(m, "flags: 0x%08x\n", server_conf.flags);
-	seq_printf(m, "share_fake_fscaps: 0x%08x\n",
+	seq_printf(m, "state:\t%s\n", ksmbd_server_state_string());
+	seq_printf(m, "name:\t%s\n", ksmbd_server_string());
+	seq_printf(m, "netbios:\t%s\n", ksmbd_netbios_name());
+	seq_printf(m, "work_group:\t%s\n", ksmbd_work_group());
+	seq_printf(m, "min_protocol:\t%s\n", ksmbd_get_protocol_string(server_conf.min_protocol));
+	seq_printf(m, "max_protocol:\t%s\n", ksmbd_get_protocol_string(server_conf.max_protocol));
+	seq_printf(m, "flags:\t0x%08x\n", server_conf.flags);
+	seq_printf(m, "tcp_port:\t%u\n", server_conf.tcp_port);
+	seq_printf(m, "signing:\t%s\n", ksmbd_signing_mode_string());
+	seq_printf(m, "signing_enforced:\t%s\n",
+		   server_conf.enforced_signing ? "yes" : "no");
+	seq_printf(m, "bind_interfaces_only:\t%s\n",
+		   server_conf.bind_interfaces_only ? "yes" : "no");
+	seq_printf(m, "max_connections:\t%u\n", server_conf.max_connections);
+	seq_printf(m, "max_connections_per_ip:\t%u\n",
+		   server_conf.max_ip_connections);
+	seq_printf(m, "max_inflight_requests:\t%u\n",
+		   server_conf.max_inflight_req);
+	seq_printf(m, "deadtime_seconds:\t%lu\n", server_conf.deadtime / HZ);
+	seq_printf(m, "ipc_timeout_seconds:\t%u\n", server_conf.ipc_timeout / HZ);
+	if (server_conf.ipc_last_active)
+		seq_printf(m, "ipc_last_active_seconds:\t%lu\n",
+			   jiffies_to_msecs(jiffies - server_conf.ipc_last_active) /
+			   MSEC_PER_SEC);
+	else
+		seq_puts(m, "ipc_last_active_seconds:\tnever\n");
+	seq_printf(m, "durable_scavenger:\t%s\n",
+		   ksmbd_durable_scavenger_active() ? "running" : "stopped");
+	seq_printf(m, "share_fake_fscaps:\t0x%08x\n",
 		   server_conf.share_fake_fscaps);
-	seq_printf(m, "sessions: %lld\n",
+	proc_show_runtime_totals(m);
+	seq_printf(m, "sessions:\t%lld\n",
 		   ksmbd_counter_sum(KSMBD_COUNTER_SESSIONS));
-	seq_printf(m, "tree connects: %lld\n",
+	seq_printf(m, "tree_connects:\t%lld\n",
 		   ksmbd_counter_sum(KSMBD_COUNTER_TREE_CONNS));
-	seq_printf(m, "requests: %lld\n",
+	seq_printf(m, "requests:\t%lld\n",
 		   ksmbd_counter_sum(KSMBD_COUNTER_REQUESTS));
-	seq_printf(m, "read bytes: %lld\n",
+	seq_printf(m, "read_bytes:\t%lld\n",
 		   ksmbd_counter_sum(KSMBD_COUNTER_READ_BYTES));
-	seq_printf(m, "written bytes: %lld\n",
+	seq_printf(m, "written_bytes:\t%lld\n",
 		   ksmbd_counter_sum(KSMBD_COUNTER_WRITE_BYTES));
 
 	seq_puts(m, "\nSMB2\n");
 	for (i = 0; i < KSMBD_COUNTER_MAX_REQS; i++)
-		seq_printf(m, "%-20s:\t%lld\n", smb2_process_req[i].name,
+		seq_printf(m, "%s:\t%lld\n", smb2_process_req[i].name,
 			   ksmbd_counter_sum(KSMBD_COUNTER_FIRST_REQ + i));
+
+	seq_puts(m, "\nSMB2 status\n");
+	seq_printf(m, "success:\t%lld\n",
+		   ksmbd_counter_sum(KSMBD_COUNTER_STATUS_SUCCESS));
+	seq_printf(m, "informational:\t%lld\n",
+		   ksmbd_counter_sum(KSMBD_COUNTER_STATUS_INFORMATIONAL));
+	seq_printf(m, "warning:\t%lld\n",
+		   ksmbd_counter_sum(KSMBD_COUNTER_STATUS_WARNING));
+	seq_printf(m, "error:\t%lld\n",
+		   ksmbd_counter_sum(KSMBD_COUNTER_STATUS_ERROR));
+	seq_printf(m, "access_denied:\t%lld\n",
+		   ksmbd_counter_sum(KSMBD_COUNTER_ERROR_ACCESS_DENIED));
+	seq_printf(m, "not_found:\t%lld\n",
+		   ksmbd_counter_sum(KSMBD_COUNTER_ERROR_NOT_FOUND));
+	seq_printf(m, "invalid_parameter:\t%lld\n",
+		   ksmbd_counter_sum(KSMBD_COUNTER_ERROR_INVALID_PARAMETER));
+	seq_printf(m, "sharing_violation:\t%lld\n",
+		   ksmbd_counter_sum(KSMBD_COUNTER_ERROR_SHARING_VIOLATION));
+	seq_printf(m, "not_supported:\t%lld\n",
+		   ksmbd_counter_sum(KSMBD_COUNTER_ERROR_NOT_SUPPORTED));
+	seq_printf(m, "other:\t%lld\n",
+		   ksmbd_counter_sum(KSMBD_COUNTER_ERROR_OTHER));
 	return 0;
 }
 
fs/smb/server/server.c +5 -2
index 0b080972e9085..26c424e6682ea 100644
@@ -156,8 +156,11 @@ static int __process_request(struct ksmbd_work *work, struct ksmbd_conn *conn,
 	}
 
 	ret = cmds->proc(work);
-	if (conn->ops->inc_reqs)
-		conn->ops->inc_reqs(command);
+	if (conn->ops->inc_reqs) {
+		struct smb2_hdr *rsp = ksmbd_resp_buf_curr(work);
+
+		conn->ops->inc_reqs(command, rsp->Status);
+	}
 
 	if (ret < 0)
 		ksmbd_debug(CONN, "Failed to process %u [%d]\n", command, ret);
fs/smb/server/smb_common.h +1 -1
index b090b56743c4c..7b9c5cfcb63b0 100644
@@ -135,7 +135,7 @@ struct file_id_both_directory_info {
 
 struct smb_version_ops {
 	u16 (*get_cmd_val)(struct ksmbd_work *swork);
-	void (*inc_reqs)(unsigned int cmd);
+	void (*inc_reqs)(unsigned int cmd, __le32 status);
 	int (*init_rsp_hdr)(struct ksmbd_work *swork);
 	void (*set_rsp_status)(struct ksmbd_work *swork, __le32 err);
 	int (*allocate_rsp_buf)(struct ksmbd_work *work);
fs/smb/server/stats.h +51 -2

78 lines not on this page · raw message

fs/smb/server/vfs_cache.c +10 -0
index 1b5f4efb6ae01..5eae49ed1c799 100644
@@ -140,6 +140,16 @@ static bool durable_scavenger_running;
 static DEFINE_MUTEX(durable_scavenger_lock);
 static wait_queue_head_t dh_wq;
 
+bool ksmbd_durable_scavenger_active(void)
+{
+	bool active;
+
+	mutex_lock(&durable_scavenger_lock);
+	active = durable_scavenger_running;
+	mutex_unlock(&durable_scavenger_lock);
+	return active;
+}
+
 void ksmbd_set_fd_limit(unsigned long limit)
 {
 	limit = min(limit, get_max_files());
fs/smb/server/vfs_cache.h +1 -0
index d6a9cd4b6d563..1c6fc4349b883 100644
@@ -209,6 +209,7 @@ unsigned int ksmbd_open_durable_fd(struct ksmbd_file *fp);
 struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, struct file *filp);
 void ksmbd_launch_ksmbd_durable_scavenger(void);
 void ksmbd_stop_durable_scavenger(void);
+bool ksmbd_durable_scavenger_active(void);
 void ksmbd_close_tree_conn_fds(struct ksmbd_work *work);
 void ksmbd_close_session_fds(struct ksmbd_work *work);
 void ksmbd_wake_session_blocked_works(struct ksmbd_session *sess);
Signature · 1 line
2.53.0
author maintainer
#145 lore raw

PATCH 142/438 ksmbd: follow SMB2 session expiration semantics

In reply to Greg Kroah-Hartman

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

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

From: Namjae Jeon <[email protected]>

[ Upstream commit d7fd1f98607f2cd358e583f9548bdf0173090a86 ]

ksmbd_session_register() destroys valid sessions after ten seconds of inactivity whenever a client starts another SessionSetup exchange. This confuses Session.IdleTime with Session.ExpirationTime. Windows can create additional authenticated sessions on an existing connection, so deleting the older session invalidates its tree connects and makes mapped drives fail with STATUS_NETWORK_NAME_DELETED.

The session expiration rules require the server to change a valid session to expired only after its credential expiration time passes. A valid or expired session otherwise keeps its connection from being scavenged. Connections that have not negotiated a dialect, have no sessions, or have only InProgress sessions are disconnected after an implementation-specific timeout. Use the Windows-compatible 45 second value and run the expiration check periodically for both TCP and SMB Direct.

Keep zero as an infinite credential expiration time, and count each Valid-to-Expired transition. Set expired sessions to InProgress when they reauthenticate. If authentication fails, remove the session from the global and per-connection tables immediately, including SMB3 multichannel connections.

Retain protection against abandoned SessionId-zero exchanges by allowing only one InProgress authentication per connection. Additional exchanges fail with STATUS_INSUFFICIENT_RESOURCES, and stale InProgress sessions are reaped after the same 45 second setup timeout. This bounds the original unauthenticated memory-exhaustion path without evicting established sessions.

Fixes ea174a918939 ("ksmbd: destroy expired sessions")
---
 fs/smb/server/connection.c        |  72 +++++++++++++++++
 fs/smb/server/connection.h        |   3 +
 fs/smb/server/mgmt/user_session.c | 124 ++++++++++++++++++++++++++++--
 fs/smb/server/mgmt/user_session.h |   8 +-
 fs/smb/server/proc.c              |   2 +
 fs/smb/server/server.c            |   2 +-
 fs/smb/server/smb2pdu.c           |  37 ++++++---
 fs/smb/server/smb2pdu.h           |   2 -
 fs/smb/server/stats.h             |   1 +
 9 files changed, 227 insertions(+), 24 deletions(-)
fs/smb/server/connection.c +72 -0
index cef279b8fd27e..4f042a8a2fdbd 100644
@@ -22,6 +22,8 @@
 static DEFINE_MUTEX(init_lock);
 
 static struct ksmbd_conn_ops default_conn_ops;
+static struct delayed_work session_expiration_work;
+static bool stopping_session_expiration_work;
 
 DEFINE_HASHTABLE(conn_list, CONN_HASH_BITS);
 DECLARE_RWSEM(conn_list_lock);
@@ -84,18 +86,28 @@ static void delete_proc_clients(void) {}
 
 static struct workqueue_struct *ksmbd_conn_wq;
 
+static void ksmbd_session_expiration_worker(struct work_struct *work);
+
 int ksmbd_conn_wq_init(void)
 {
 	ksmbd_conn_wq = alloc_workqueue("ksmbd-conn-release",
 					WQ_UNBOUND | WQ_MEM_RECLAIM, 0);
 	if (!ksmbd_conn_wq)
 		return -ENOMEM;
+
+	WRITE_ONCE(stopping_session_expiration_work, false);
+	INIT_DELAYED_WORK(&session_expiration_work,
+			  ksmbd_session_expiration_worker);
+	queue_delayed_work(ksmbd_conn_wq, &session_expiration_work,
+			   KSMBD_SESSION_EXPIRATION_INTERVAL);
 	return 0;
 }
 
 void ksmbd_conn_wq_destroy(void)
 {
 	if (ksmbd_conn_wq) {
+		WRITE_ONCE(stopping_session_expiration_work, true);
+		cancel_delayed_work_sync(&session_expiration_work);
 		destroy_workqueue(ksmbd_conn_wq);
 		ksmbd_conn_wq = NULL;
 	}
@@ -205,6 +217,7 @@ struct ksmbd_conn *ksmbd_conn_alloc(void)
 		return NULL;
 
 	conn->need_neg = true;
+	conn->creation_time = jiffies;
 	ksmbd_conn_set_new(conn);
 	conn->local_nls = load_nls("utf8");
 	if (!conn->local_nls)
@@ -487,6 +500,20 @@ bool ksmbd_conn_alive(struct ksmbd_conn *conn)
 	if (kthread_should_stop())
 		return false;
 
+	/*
+	 * Stale connections that have not completed NEGOTIATE and SESSION_SETUP
+	 * must be disconnected. Do not race a request that is currently
+	 * completing authentication.
+	 */
+	if (!atomic_read(&conn->req_running) &&
+	    time_after(jiffies, conn->creation_time +
+		       KSMBD_UNAUTHENTICATED_CONN_TIMEOUT) &&
+	    (READ_ONCE(conn->need_neg) ||
+	     !ksmbd_conn_has_valid_or_expired_session(conn))) {
+		ksmbd_debug(CONN, "Connection setup timed out\n");
+		return false;
+	}
+
 	if (atomic_read(&conn->stats.open_files_count) > 0)
 		return true;
 
@@ -504,6 +531,51 @@ bool ksmbd_conn_alive(struct ksmbd_conn *conn)
 	return true;
 }
 
+static void ksmbd_session_expiration_worker(struct work_struct *work)
+{
+	struct ksmbd_conn *conn, *target;
+	int bkt;
+
+	if (!ksmbd_server_running())
+		goto reschedule;
+
+	ksmbd_expire_sessions();
+
+	/*
+	 * An old connection without a Valid or Expired session must be
+	 * disconnected. Process one connection at a time without holding
+	 * conn_list_lock across transport shutdown.
+	 */
+again:
+	target = NULL;
+	down_read(&conn_list_lock);
+	hash_for_each(conn_list, bkt, conn, hlist) {
+		if (ksmbd_conn_exiting(conn) || ksmbd_conn_releasing(conn) ||
+		    atomic_read(&conn->req_running) ||
+		    time_before_eq(jiffies, conn->creation_time +
+				   KSMBD_UNAUTHENTICATED_CONN_TIMEOUT) ||
+		    (!READ_ONCE(conn->need_neg) &&
+		     ksmbd_conn_has_valid_or_expired_session(conn)))
+			continue;
+
+		target = ksmbd_conn_get(conn);
+		break;
+	}
+	up_read(&conn_list_lock);
+
+	if (target) {
+		ksmbd_debug(CONN, "Connection setup timed out\n");
+		ksmbd_conn_abort(target);
+		ksmbd_conn_put(target);
+		goto again;
+	}
+
+reschedule:
+	if (!READ_ONCE(stopping_session_expiration_work))
+		queue_delayed_work(ksmbd_conn_wq, &session_expiration_work,
+				   KSMBD_SESSION_EXPIRATION_INTERVAL);
+}
+
 /* "+2" for BCC field (ByteCount, 2 bytes) */
 #define SMB1_MIN_SUPPORTED_PDU_SIZE (sizeof(struct smb_hdr) + 2)
 #define SMB2_MIN_SUPPORTED_PDU_SIZE (sizeof(struct smb2_pdu))
fs/smb/server/connection.h +3 -0
index 242a9757eb0db..909f73c6e3c14 100644
@@ -67,6 +67,7 @@ struct ksmbd_conn {
 	struct rw_semaphore		session_lock;
 	/* smb session 1 per user */
 	struct xarray			sessions;
+	unsigned long			creation_time;
 	unsigned long			last_active;
 	/* How many request are running currently */
 	atomic_t			req_running;
@@ -160,6 +161,8 @@ struct ksmbd_transport {
 
 #define KSMBD_TCP_RECV_TIMEOUT	(7 * HZ)
 #define KSMBD_TCP_SEND_TIMEOUT	(5 * HZ)
+#define KSMBD_SESSION_EXPIRATION_INTERVAL	(5 * HZ)
+#define KSMBD_UNAUTHENTICATED_CONN_TIMEOUT	(45 * HZ)
 #define KSMBD_TCP_PEER_SOCKADDR(c)	((struct sockaddr *)&((c)->peer_addr))
 
 #define CONN_HASH_BITS	12
fs/smb/server/mgmt/user_session.c +116 -8

170 lines not on this page · raw message

fs/smb/server/mgmt/user_session.h +7 -1
index 3e52d4cc13247..217258551d6d0 100644
@@ -72,6 +72,8 @@ struct ksmbd_session {
 	struct rw_semaphore		rpc_lock;
 };
 
+#define KSMBD_MAX_CHANNELS	32
+
 static inline int test_session_flag(struct ksmbd_session *sess, int bit)
 {
 	return sess->flags & bit;
@@ -98,7 +100,11 @@ bool is_ksmbd_session_in_connection(struct ksmbd_conn *conn,
 				     unsigned long long id);
 int ksmbd_session_register(struct ksmbd_conn *conn,
 			   struct ksmbd_session *sess);
-void ksmbd_sessions_deregister(struct ksmbd_conn *conn);
+void ksmbd_session_unregister(struct ksmbd_conn *conn,
+			      struct ksmbd_session *sess);
+void ksmbd_conn_sessions_cleanup(struct ksmbd_conn *conn);
+bool ksmbd_conn_has_valid_or_expired_session(struct ksmbd_conn *conn);
+void ksmbd_expire_sessions(void);
 struct ksmbd_session *__session_lookup(unsigned long long id);
 struct ksmbd_session *ksmbd_session_lookup_all(struct ksmbd_conn *conn,
 					       unsigned long long id);
fs/smb/server/proc.c +2 -0
index 826353ed05538..19f0f2cfbf543 100644
@@ -178,6 +178,8 @@ static int proc_show_ksmbd_stats(struct seq_file *m, void *v)
 	proc_show_runtime_totals(m);
 	seq_printf(m, "sessions:\t%lld\n",
 		   ksmbd_counter_sum(KSMBD_COUNTER_SESSIONS));
+	seq_printf(m, "session_timeouts:\t%lld\n",
+		   ksmbd_counter_sum(KSMBD_COUNTER_SESSION_TIMEOUTS));
 	seq_printf(m, "tree_connects:\t%lld\n",
 		   ksmbd_counter_sum(KSMBD_COUNTER_TREE_CONNS));
 	seq_printf(m, "requests:\t%lld\n",
fs/smb/server/server.c +1 -1
index 26c424e6682ea..e113b23d96f94 100644
@@ -403,7 +403,7 @@ static int ksmbd_server_process_request(struct ksmbd_conn *conn)
 
 static int ksmbd_server_terminate_conn(struct ksmbd_conn *conn)
 {
-	ksmbd_sessions_deregister(conn);
+	ksmbd_conn_sessions_cleanup(conn);
 	destroy_lease_table(conn);
 	return 0;
 }
fs/smb/server/smb2pdu.c +25 -12
index 184b28072a6f0..3cb39dc7b149c 100644
@@ -83,8 +83,6 @@ struct channel *lookup_chann_list(struct ksmbd_session *sess, struct ksmbd_conn
 	return chann;
 }
 
-#define KSMBD_MAX_CHANNELS	32
-
 static int register_session_channel(struct ksmbd_session *sess,
 				    struct ksmbd_conn *conn,
 				    const char *sess_key)
@@ -914,8 +912,14 @@ static bool smb2_session_expired_cmd_allowed(struct ksmbd_work *work,
 
 static bool smb2_session_kerberos_expired(struct ksmbd_session *sess)
 {
-	return sess->kerberos_expiry &&
-		ktime_get_real_seconds() >= sess->kerberos_expiry;
+	if (!sess->kerberos_expiry ||
+	    ktime_get_real_seconds() < sess->kerberos_expiry)
+		return false;
+
+	if (cmpxchg(&sess->state, SMB2_SESSION_VALID,
+		    SMB2_SESSION_EXPIRED) == SMB2_SESSION_VALID)
+		ksmbd_counter_inc(KSMBD_COUNTER_SESSION_TIMEOUTS);
+	return true;
 }
 
 /**
@@ -950,9 +954,8 @@ int smb2_check_user_session(struct ksmbd_work *work)
 		if (!work->next_smb2_rcv_hdr_off && sess_id)
 			work->sess = ksmbd_session_lookup_all_states(conn, sess_id);
 		if (work->sess) {
-			if (smb2_session_kerberos_expired(work->sess)) {
-				work->sess->state = SMB2_SESSION_EXPIRED;
-			} else if (work->sess->state != SMB2_SESSION_VALID) {
+			if (!smb2_session_kerberos_expired(work->sess) &&
+			    work->sess->state != SMB2_SESSION_VALID) {
 				ksmbd_user_session_put(work->sess);
 				work->sess = NULL;
 			}
@@ -977,8 +980,7 @@ int smb2_check_user_session(struct ksmbd_work *work)
 					sess_id, work->sess->id);
 			return -EINVAL;
 		}
-		if (smb2_session_kerberos_expired(work->sess))
-			work->sess->state = SMB2_SESSION_EXPIRED;
+		smb2_session_kerberos_expired(work->sess);
 		if (work->sess->state != SMB2_SESSION_VALID) {
 			pr_err("compound request on a non-valid session (state %d)\n",
 					work->sess->state);
@@ -995,7 +997,6 @@ int smb2_check_user_session(struct ksmbd_work *work)
 	work->sess = ksmbd_session_lookup_all_states(conn, sess_id);
 	if (work->sess) {
 		if (smb2_session_kerberos_expired(work->sess)) {
-			work->sess->state = SMB2_SESSION_EXPIRED;
 			return smb2_session_expired_cmd_allowed(work, cmd) ?
 				1 : -EKEYEXPIRED;
 		}
@@ -2192,7 +2193,7 @@ int smb2_sess_setup(struct ksmbd_work *work)
 	struct ksmbd_conn *conn = work->conn;
 	struct smb2_sess_setup_req *req;
 	struct smb2_sess_setup_rsp *rsp;
-	struct ksmbd_session *sess;
+	struct ksmbd_session *sess = NULL;
 	struct negotiate_message *negblob;
 	unsigned int negblob_len, negblob_off;
 	int rc = 0;
@@ -2333,6 +2334,9 @@ int smb2_sess_setup(struct ksmbd_work *work)
 			goto out_err;
 		}
 
+		if (work->session_setup_reauth)
+			WRITE_ONCE(sess->state, SMB2_SESSION_IN_PROGRESS);
+
 		conn->binding = false;
 	}
 	work->sess = sess;
@@ -2444,6 +2448,14 @@ int smb2_sess_setup(struct ksmbd_work *work)
 	}
 
 	if (rc < 0) {
+		bool setup_in_progress = sess &&
+			READ_ONCE(sess->state) == SMB2_SESSION_IN_PROGRESS &&
+			!(req->Flags & SMB2_SESSION_REQ_FLAG_BINDING);
+
+		/* Authentication errors must not leave the new session published. */
+		if (setup_in_progress)
+			ksmbd_session_unregister(conn, sess);
+
 		if (sess && conn->dialect == SMB311_PROT_ID &&
 		    (req->Flags & SMB2_SESSION_REQ_FLAG_BINDING)) {
 			struct preauth_session *preauth_sess;
@@ -2477,7 +2489,8 @@ int smb2_sess_setup(struct ksmbd_work *work)
 			 * For binding requests, session belongs to another
 			 * connection. Do not expire it.
 			 */
-			if (!(req->Flags & SMB2_SESSION_REQ_FLAG_BINDING)) {
+			if (!(req->Flags & SMB2_SESSION_REQ_FLAG_BINDING) &&
+			    !setup_in_progress) {
 				sess->last_active = jiffies;
 				sess->kerberos_expiry = 0;
 				sess->state = SMB2_SESSION_EXPIRED;
fs/smb/server/smb2pdu.h +0 -2
index 1836259be8971..2a8a2bc9a8a13 100644
@@ -61,8 +61,6 @@ struct preauth_integrity_info {
 #define SMB2_SESSION_IN_PROGRESS	BIT(0)
 #define SMB2_SESSION_VALID		BIT(1)
 
-#define SMB2_SESSION_TIMEOUT		(10 * HZ)
-
 /* Apple Defined Contexts */
 #define SMB2_CREATE_AAPL		"AAPL"
 
fs/smb/server/stats.h +1 -0
index bc864efa0d46b..8b32b8b4e8be4 100644
@@ -15,6 +15,7 @@
 
 enum {
 	KSMBD_COUNTER_SESSIONS = 0,
+	KSMBD_COUNTER_SESSION_TIMEOUTS,
 	KSMBD_COUNTER_TREE_CONNS,
 	KSMBD_COUNTER_REQUESTS,
 	KSMBD_COUNTER_STATUS_SUCCESS,
Signature · 1 line
2.53.0
author maintainer
#146 lore raw

PATCH 143/438 wifi: virt_wifi: dont transfer operstate before register

In reply to Greg Kroah-Hartman

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

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

From: Zihan Xi <[email protected]>

[ Upstream commit e5c8d7acd31b27057ea42cd405d0b3ece097bc89 ]

virt_wifi_newlink() calls netif_stacked_transfer_operstate() before register_netdevice(). If the lower device is dormant, that queues the new netdev on lweventlist while it is still uninitialized. If registration fails after that, for example because of an invalid name such as "bad/name", free_netdev() immediately frees the object. A later linkwatch_fire_event() then use-after-frees the list entry.

Move the transfer to after netdev_upper_dev_link(), as macvlan and ipvlan already do.

---
 drivers/net/wireless/virtual/virt_wifi.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/net/wireless/virtual/virt_wifi.c +2 -1
index b69a4650fba85..48afc2432f93a 100644
@@ -558,7 +558,6 @@ static int virt_wifi_newlink(struct net_device *dev,
 	}
 
 	eth_hw_addr_inherit(dev, priv->lowerdev);
-	netif_stacked_transfer_operstate(priv->lowerdev, dev);
 
 	dev->ieee80211_ptr = kzalloc_obj(*dev->ieee80211_ptr);
 
@@ -584,6 +583,8 @@ static int virt_wifi_newlink(struct net_device *dev,
 		goto unregister_netdev;
 	}
 
+	netif_stacked_transfer_operstate(priv->lowerdev, dev);
+
 	dev->priv_destructor = virt_wifi_net_device_destructor;
 	priv->being_deleted = false;
 	priv->is_connected = false;
Signature · 1 line
2.53.0
author maintainer
#147 lore raw

PATCH 144/438 drm/xe/mmio_gem: forbid VMA split

In reply to Greg Kroah-Hartman

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

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

From: Ilia Levi <[email protected]>

[ Upstream commit 247a82da6f563dcfd9074a68f99a0c0997d0679c ]

The fault handler assumes it always operates on a VMA spanning the entire GEM object. This does not hold when the VMA has been split, e.g. by a partial munmap or mprotect. In that case the handler may map wrong physical pages or cause SIGBUS.

Handle this by forbidding VMA split, as partial unmaps are not deemed useful for MMIO GEMs.

Fixes 1ffcf8b8ae8a ("drm/xe: Support for mmap-ing mmio regions")
(cherry picked from commit f3391a0b12d7bf826a0b21600d2f294f3dce4c14)
---
 drivers/gpu/drm/xe/xe_mmio_gem.c | 10 ++++++++++
 1 file changed, 10 insertions(+)
drivers/gpu/drm/xe/xe_mmio_gem.c +10 -0
index 8c803ef233cc4..f15a6a84af159 100644
@@ -39,10 +39,20 @@ struct xe_mmio_gem {
 	phys_addr_t phys_addr;
 };
 
+static int xe_mmio_gem_vm_may_split(struct vm_area_struct *area, unsigned long addr)
+{
+	/*
+	 * Forbid splitting. Together with VM_DONTEXPAND, this keeps the VMA
+	 * matching the GEM object exactly.
+	 */
+	return -EINVAL;
+}
+
 static const struct vm_operations_struct vm_ops = {
 	.open = drm_gem_vm_open,
 	.close = drm_gem_vm_close,
 	.fault = xe_mmio_gem_vm_fault,
+	.may_split = xe_mmio_gem_vm_may_split,
 };
 
 static const struct drm_gem_object_funcs xe_mmio_gem_funcs = {
Signature · 1 line
2.53.0
author maintainer
#148 lore raw

PATCH 145/438 drm/xe/mmio_gem: use write-back mapping for dummy page

In reply to Greg Kroah-Hartman

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

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

From: Ilia Levi <[email protected]>

[ Upstream commit 819f189265a5955da743e78e20a713a038982e89 ]

Currently vmf_insert_pfn() maps the dummy page as UC, inheriting the VMA's page protection which was set for the real MMIO region. This conflicts with the direct map's WB mapping of the same page, creating a cache type alias which is architecturally undefined on some platforms.

Use vmf_insert_pfn_prot() with a WB pgprot instead. Also simplify to fault in the requested page instead of the whole VMA.

Fixes 1ffcf8b8ae8a ("drm/xe: Support for mmap-ing mmio regions")
Assisted-by GitHub-Copilot:claude-opus-4.6
(cherry picked from commit 1e8e28e35df0e77ae1b22fc091c1f422f62fa5e9)
---
 drivers/gpu/drm/xe/xe_mmio_gem.c | 19 +++++--------------
 1 file changed, 5 insertions(+), 14 deletions(-)
drivers/gpu/drm/xe/xe_mmio_gem.c +5 -14
index f15a6a84af159..418a24bdfa4ed 100644
@@ -172,14 +172,13 @@ static void xe_mmio_gem_release_dummy_page(struct drm_device *dev, void *res)
 	__free_page((struct page *)res);
 }
 
-static vm_fault_t xe_mmio_gem_vm_fault_dummy_page(struct vm_area_struct *vma)
+static vm_fault_t xe_mmio_gem_vm_fault_dummy_page(struct vm_fault *vmf)
 {
+	struct vm_area_struct *vma = vmf->vma;
 	struct drm_gem_object *base = vma->vm_private_data;
 	struct drm_device *dev = base->dev;
-	vm_fault_t ret = VM_FAULT_NOPAGE;
 	struct page *page;
 	unsigned long pfn;
-	unsigned long i;
 
 	page = alloc_page(GFP_KERNEL | __GFP_ZERO);
 	if (!page)
@@ -190,16 +189,8 @@ static vm_fault_t xe_mmio_gem_vm_fault_dummy_page(struct vm_area_struct *vma)
 
 	pfn = page_to_pfn(page);
 
-	/* Map the entire VMA to the same dummy page */
-	for (i = 0; i < base->size; i += PAGE_SIZE) {
-		unsigned long addr = vma->vm_start + i;
-
-		ret = vmf_insert_pfn(vma, addr, pfn);
-		if (ret & VM_FAULT_ERROR)
-			break;
-	}
-
-	return ret;
+	return vmf_insert_pfn_prot(vma, vmf->address, pfn,
+				   vm_get_page_prot(vma->vm_flags));
 }
 
 static vm_fault_t xe_mmio_gem_vm_fault(struct vm_fault *vmf)
@@ -219,7 +210,7 @@ static vm_fault_t xe_mmio_gem_vm_fault(struct vm_fault *vmf)
 		 * It is assumed the userspace will receive the notification via some
 		 * other channel (e.g. drm uevent).
 		 */
-		return xe_mmio_gem_vm_fault_dummy_page(vma);
+		return xe_mmio_gem_vm_fault_dummy_page(vmf);
 	}
 
 	for (i = 0; i < base->size; i += PAGE_SIZE) {
Signature · 1 line
2.53.0
author maintainer
#149 lore raw

PATCH 146/438 drm/xe/mmio_gem: Revoke drm_vma_node on xe_mmio_gem destroy

In reply to Greg Kroah-Hartman

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

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

From: Shuicheng Lin <[email protected]>

[ Upstream commit 37fcbd7b2f8996d783932dab11bc668e169b0de6 ]

xe_mmio_gem_create() calls drm_vma_node_allow() but nothing ever calls drm_vma_node_revoke(). The drm_vma_offset_file rb-tree entry allocated by drm_vma_node_allow() is not freed by drm_gem_object_release(), so it is leaked on every create/destroy cycle.

Add a struct drm_file * parameter to xe_mmio_gem_destroy() and call drm_vma_node_revoke() from there, mirroring the drm_vma_node_allow() call in xe_mmio_gem_create().

Fixes 1ffcf8b8ae8a ("drm/xe: Support for mmap-ing mmio regions")
Assisted-by Claude:claude-opus-4.6
(cherry picked from commit 32f0cb250598456d812fb7ca57a040282858323d)
---
 drivers/gpu/drm/xe/xe_mmio_gem.c | 4 +++-
 drivers/gpu/drm/xe/xe_mmio_gem.h | 2 +-
 2 files changed, 4 insertions(+), 2 deletions(-)
drivers/gpu/drm/xe/xe_mmio_gem.c +3 -1
index 418a24bdfa4ed..ad3c9fa05223d 100644
@@ -138,14 +138,16 @@ static void xe_mmio_gem_free(struct drm_gem_object *base)
 /**
  * xe_mmio_gem_destroy - Destroy the GEM object that exposes an MMIO region
  * @gem: the GEM object to destroy
+ * @file: DRM file descriptor previously passed to xe_mmio_gem_create()
  *
  * This function releases resources associated with the GEM object created by
  * xe_mmio_gem_create().
  *
  * See: "Exposing MMIO regions to userspace"
  */
-void xe_mmio_gem_destroy(struct xe_mmio_gem *gem)
+void xe_mmio_gem_destroy(struct xe_mmio_gem *gem, struct drm_file *file)
 {
+	drm_vma_node_revoke(&gem->base.vma_node, file);
 	xe_mmio_gem_free(&gem->base);
 }
 
drivers/gpu/drm/xe/xe_mmio_gem.h +1 -1
index 4b76d5586ebb8..80d7795f07c8e 100644
@@ -15,6 +15,6 @@ struct xe_mmio_gem;
 struct xe_mmio_gem *xe_mmio_gem_create(struct xe_device *xe, struct drm_file *file,
 				       phys_addr_t phys_addr, size_t size);
 u64 xe_mmio_gem_mmap_offset(struct xe_mmio_gem *gem);
-void xe_mmio_gem_destroy(struct xe_mmio_gem *gem);
+void xe_mmio_gem_destroy(struct xe_mmio_gem *gem, struct drm_file *file);
 
 #endif /* _XE_MMIO_GEM_H_ */
Signature · 1 line
2.53.0
author maintainer
#150 lore raw

PATCH 147/438 drm/xe/shrinker: Return the freed page count through a parameter

In reply to Greg Kroah-Hartman

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

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

From: Shuicheng Lin <[email protected]>

[ Upstream commit 3c90e42a01426262f0cd166bc01b45c05562640d ]

__xe_shrinker_walk() and xe_shrinker_walk() return either the number of pages freed or a negative error, so the two cannot be reported at once. On error the pages already freed are dropped, and since xe_shrinker_scan() only accumulates non-negative returns while *scanned is updated by pointer, the shrinker tells mm that it scanned without freeing.

Accumulate the count into a caller-provided counter and return only the status, so an error no longer discards what the walk had freed.

Fixes 00c8efc3180f ("drm/xe: Add a shrinker for xe bos")
Assisted-by Claude:claude-opus-5
Cc Matthew Brost <[email protected]>
(cherry picked from commit d7aac1a0235a6ce41e30cec385e2db8c33dad12d)
---
 drivers/gpu/drm/xe/xe_shrinker.c | 62 ++++++++++++++------------------
 1 file changed, 26 insertions(+), 36 deletions(-)
drivers/gpu/drm/xe/xe_shrinker.c +26 -36
index 83374cd576608..89445cd202380 100644
@@ -54,13 +54,14 @@ xe_shrinker_mod_pages(struct xe_shrinker *shrinker, long shrinkable, long purgea
 	write_unlock(&shrinker->lock);
 }
 
-static s64 __xe_shrinker_walk(struct xe_device *xe,
+static int __xe_shrinker_walk(struct xe_device *xe,
 			      struct ttm_operation_ctx *ctx,
 			      const struct xe_bo_shrink_flags flags,
-			      unsigned long to_scan, unsigned long *scanned)
+			      unsigned long to_scan, unsigned long *scanned,
+			      unsigned long *freed)
 {
 	unsigned int mem_type;
-	s64 freed = 0, lret;
+	s64 lret;
 
 	for (mem_type = XE_PL_SYSTEM; mem_type <= XE_PL_TT; ++mem_type) {
 		struct ttm_resource_manager *man = ttm_manager_type(&xe->ttm, mem_type);
@@ -82,7 +83,7 @@ static s64 __xe_shrinker_walk(struct xe_device *xe,
 			if (lret < 0)
 				return lret;
 
-			freed += lret;
+			*freed += lret;
 			if (*scanned >= to_scan)
 				break;
 		}
@@ -90,7 +91,7 @@ static s64 __xe_shrinker_walk(struct xe_device *xe,
 		xe_assert(xe, !IS_ERR(ttm_bo));
 	}
 
-	return freed;
+	return 0;
 }
 
 /*
@@ -99,40 +100,35 @@ static s64 __xe_shrinker_walk(struct xe_device *xe,
  * add writeback. This avoids stalls and explicit writebacks with light or
  * moderate memory pressure.
  */
-static s64 xe_shrinker_walk(struct xe_device *xe,
+static int xe_shrinker_walk(struct xe_device *xe,
 			    struct ttm_operation_ctx *ctx,
 			    const struct xe_bo_shrink_flags flags,
-			    unsigned long to_scan, unsigned long *scanned)
+			    unsigned long to_scan, unsigned long *scanned,
+			    unsigned long *freed)
 {
 	bool no_wait_gpu = true;
 	struct xe_bo_shrink_flags save_flags = flags;
-	s64 lret, freed;
+	int ret;
 
 	swap(no_wait_gpu, ctx->no_wait_gpu);
 	save_flags.writeback = false;
-	lret = __xe_shrinker_walk(xe, ctx, save_flags, to_scan, scanned);
+	ret = __xe_shrinker_walk(xe, ctx, save_flags, to_scan, scanned, freed);
 	swap(no_wait_gpu, ctx->no_wait_gpu);
-	if (lret < 0 || *scanned >= to_scan)
-		return lret;
+	if (ret || *scanned >= to_scan)
+		return ret;
 
-	freed = lret;
 	if (!ctx->no_wait_gpu) {
-		lret = __xe_shrinker_walk(xe, ctx, save_flags, to_scan, scanned);
-		if (lret < 0)
-			return lret;
-		freed += lret;
-		if (*scanned >= to_scan)
-			return freed;
+		ret = __xe_shrinker_walk(xe, ctx, save_flags, to_scan, scanned,
+					 freed);
+		if (ret || *scanned >= to_scan)
+			return ret;
 	}
 
-	if (flags.writeback) {
-		lret = __xe_shrinker_walk(xe, ctx, flags, to_scan, scanned);
-		if (lret < 0)
-			return lret;
-		freed += lret;
-	}
+	if (flags.writeback)
+		ret = __xe_shrinker_walk(xe, ctx, flags, to_scan, scanned,
+					 freed);
 
-	return freed;
+	return ret;
 }
 
 static unsigned long
@@ -214,7 +210,6 @@ static unsigned long xe_shrinker_scan(struct shrinker *shrink, struct shrink_con
 	bool runtime_pm;
 	bool purgeable;
 	bool can_backup = !!(sc->gfp_mask & __GFP_FS);
-	s64 lret;
 
 	nr_to_scan = sc->nr_to_scan;
 
@@ -225,12 +220,9 @@ static unsigned long xe_shrinker_scan(struct shrinker *shrink, struct shrink_con
 	/* Might need runtime PM. Try to wake early if it looks like it. */
 	runtime_pm = xe_shrinker_runtime_pm_get(shrinker, false, nr_to_scan, can_backup);
 
-	if (purgeable && nr_scanned < nr_to_scan) {
-		lret = xe_shrinker_walk(shrinker->xe, &ctx, shrink_flags,
-					nr_to_scan, &nr_scanned);
-		if (lret >= 0)
-			freed += lret;
-	}
+	if (purgeable && nr_scanned < nr_to_scan)
+		xe_shrinker_walk(shrinker->xe, &ctx, shrink_flags,
+				 nr_to_scan, &nr_scanned, &freed);
 
 	sc->nr_scanned = nr_scanned;
 	if (nr_scanned >= nr_to_scan || !can_backup)
@@ -242,10 +234,8 @@ static unsigned long xe_shrinker_scan(struct shrinker *shrink, struct shrink_con
 
 	shrink_flags.purge = false;
 
-	lret = xe_shrinker_walk(shrinker->xe, &ctx, shrink_flags,
-				nr_to_scan, &nr_scanned);
-	if (lret >= 0)
-		freed += lret;
+	xe_shrinker_walk(shrinker->xe, &ctx, shrink_flags,
+			 nr_to_scan, &nr_scanned, &freed);
 
 	sc->nr_scanned = nr_scanned;
 out:
Signature · 1 line
2.53.0
author maintainer
#151 lore raw

PATCH 148/438 drm/xe/shrinker: Take a runtime PM ref before shrinking non-system memory

In reply to Greg Kroah-Hartman

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

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

From: Shuicheng Lin <[email protected]>

[ Upstream commit 985862be16c7e4da808c51f393d631fb60c0be5c ]

__xe_shrinker_walk() walks the SYSTEM and TT LRUs without a runtime PM reference. Shrinking a bo outside system memory invalidates its GPU mappings, which needs the device resumed, so while it is runtime suspended the page table zap trips an assert and the TLB invalidation returns -ENODEV:

  WARNING: drivers/gpu/drm/xe/xe_bo.c:770 at xe_bo_move_notify+0x1fc/0x450 [xe]
   xe_bo_shrink+0x20f/0x2b0 [xe]
   __xe_shrinker_walk+0x174/0x410 [xe]
   xe_shrinker_scan+0x10c/0x1e0 [xe]
   do_shrink_slab+0x176/0x7e0
   drop_caches_sysctl_handler+0x9c/0xf0

Take a reference before walking a memory type other than XE_PL_SYSTEM and stop there if it cannot be acquired. Reuse the shrinker's existing acquire path, which resumes the device directly where reclaim allows that and otherwise queues the PM worker for a later scan. Stop the walk once the scan target is met, so a satisfied scan does not wake the device. System memory is still reclaimed while the device is suspended.

Gate this on xe_device_is_l2_flush_optimized(), the same condition under which xe_bo_trigger_rebind() issues the invalidation for a non-fault-mode vm, so reclaim is unaffected elsewhere. The System CCS copy already has its own reference in xe_bo_shrink().

Only a non-fault-mode vm can reach this, since a fault-mode vm requires LR mode and that holds a runtime PM reference for the vm's lifetime.

Reproduced with igt@xe_madvise@dontneed-before-exec while the GPU is runtime suspended.

v2: simplify needs_rpm check. (Matt)
retarget Fixes tag since the issue occurs with the non-fault-mode path added by 4e7ebff69aed.
v3: handle this in xe_shrinker.c instead of xe_bo.c (Thomas)
v4: stop the walk once the scan target is met. (Sashiko)
v5: rebase on the freed page accounting fix. (Sashiko)
v6: reuse the shrinker acquire path so runtime pm can be resumed
directly instead of always queueing a worker. (Thomas)
v7: replace xe_pm_runtime_put() with xe_shrinker_runtime_pm_put(). (Thomas)

Fixes 4e7ebff69aed ("drm/xe/xe3p_lpg: flush shrinker bo cachelines manually")
Assisted-by Claude:claude-opus-5
Cc Tejas Upadhyay <[email protected]>
Cc Matthew Brost <[email protected]>
(cherry picked from commit 628f92b28bf4c371c10207daf6fc4caee0c0db2e)
---
 drivers/gpu/drm/xe/xe_shrinker.c | 78 +++++++++++++++++++++-----------
 1 file changed, 51 insertions(+), 27 deletions(-)
drivers/gpu/drm/xe/xe_shrinker.c +51 -27
index 89445cd202380..deb4378c1ec15 100644
@@ -54,13 +54,39 @@ xe_shrinker_mod_pages(struct xe_shrinker *shrinker, long shrinkable, long purgea
 	write_unlock(&shrinker->lock);
 }
 
-static int __xe_shrinker_walk(struct xe_device *xe,
+static bool __xe_shrinker_runtime_pm_get(struct xe_shrinker *shrinker)
+{
+	struct xe_device *xe = shrinker->xe;
+
+	if (xe_pm_runtime_get_if_active(xe))
+		return true;
+
+	if (xe_rpm_reclaim_safe(xe) && !ttm_bo_shrink_avoid_wait()) {
+		xe_pm_runtime_get(xe);
+		return true;
+	}
+
+	queue_work(xe->unordered_wq, &shrinker->pm_worker);
+
+	return false;
+}
+
+static void xe_shrinker_runtime_pm_put(struct xe_shrinker *shrinker, bool runtime_pm)
+{
+	if (runtime_pm)
+		xe_pm_runtime_put(shrinker->xe);
+}
+
+static int __xe_shrinker_walk(struct xe_shrinker *shrinker,
 			      struct ttm_operation_ctx *ctx,
 			      const struct xe_bo_shrink_flags flags,
 			      unsigned long to_scan, unsigned long *scanned,
 			      unsigned long *freed)
 {
+	struct xe_device *xe = shrinker->xe;
 	unsigned int mem_type;
+	bool rpm = false;
+	int ret = 0;
 	s64 lret;
 
 	for (mem_type = XE_PL_SYSTEM; mem_type <= XE_PL_TT; ++mem_type) {
@@ -75,23 +101,35 @@ static int __xe_shrinker_walk(struct xe_device *xe,
 		if (!man || !man->use_tt)
 			continue;
 
+		if (mem_type != XE_PL_SYSTEM && !rpm &&
+		    xe_device_is_l2_flush_optimized(xe)) {
+			if (!__xe_shrinker_runtime_pm_get(shrinker))
+				break;
+			rpm = true;
+		}
+
 		ttm_bo_lru_for_each_reserved_guarded(&curs, man, &arg, ttm_bo) {
 			if (!ttm_bo_shrink_suitable(ttm_bo, ctx))
 				continue;
 
 			lret = xe_bo_shrink(ctx, ttm_bo, flags, scanned);
-			if (lret < 0)
-				return lret;
+			if (lret < 0) {
+				ret = lret;
+				goto out;
+			}
 
 			*freed += lret;
 			if (*scanned >= to_scan)
-				break;
+				goto out;
 		}
 		/* Trylocks should never error, just fail. */
 		xe_assert(xe, !IS_ERR(ttm_bo));
 	}
 
-	return 0;
+out:
+	xe_shrinker_runtime_pm_put(shrinker, rpm);
+
+	return ret;
 }
 
 /*
@@ -100,7 +138,7 @@ static int __xe_shrinker_walk(struct xe_device *xe,
  * add writeback. This avoids stalls and explicit writebacks with light or
  * moderate memory pressure.
  */
-static int xe_shrinker_walk(struct xe_device *xe,
+static int xe_shrinker_walk(struct xe_shrinker *shrinker,
 			    struct ttm_operation_ctx *ctx,
 			    const struct xe_bo_shrink_flags flags,
 			    unsigned long to_scan, unsigned long *scanned,
@@ -112,20 +150,21 @@ static int xe_shrinker_walk(struct xe_device *xe,
 
 	swap(no_wait_gpu, ctx->no_wait_gpu);
 	save_flags.writeback = false;
-	ret = __xe_shrinker_walk(xe, ctx, save_flags, to_scan, scanned, freed);
+	ret = __xe_shrinker_walk(shrinker, ctx, save_flags, to_scan, scanned,
+				 freed);
 	swap(no_wait_gpu, ctx->no_wait_gpu);
 	if (ret || *scanned >= to_scan)
 		return ret;
 
 	if (!ctx->no_wait_gpu) {
-		ret = __xe_shrinker_walk(xe, ctx, save_flags, to_scan, scanned,
+		ret = __xe_shrinker_walk(shrinker, ctx, save_flags, to_scan, scanned,
 					 freed);
 		if (ret || *scanned >= to_scan)
 			return ret;
 	}
 
 	if (flags.writeback)
-		ret = __xe_shrinker_walk(xe, ctx, flags, to_scan, scanned,
+		ret = __xe_shrinker_walk(shrinker, ctx, flags, to_scan, scanned,
 					 freed);
 
 	return ret;
@@ -176,22 +215,7 @@ static bool xe_shrinker_runtime_pm_get(struct xe_shrinker *shrinker, bool force,
 			return false;
 	}
 
-	if (!xe_pm_runtime_get_if_active(xe)) {
-		if (xe_rpm_reclaim_safe(xe) && !ttm_bo_shrink_avoid_wait()) {
-			xe_pm_runtime_get(xe);
-			return true;
-		}
-		queue_work(xe->unordered_wq, &shrinker->pm_worker);
-		return false;
-	}
-
-	return true;
-}
-
-static void xe_shrinker_runtime_pm_put(struct xe_shrinker *shrinker, bool runtime_pm)
-{
-	if (runtime_pm)
-		xe_pm_runtime_put(shrinker->xe);
+	return __xe_shrinker_runtime_pm_get(shrinker);
 }
 
 static unsigned long xe_shrinker_scan(struct shrinker *shrink, struct shrink_control *sc)
@@ -221,7 +245,7 @@ static unsigned long xe_shrinker_scan(struct shrinker *shrink, struct shrink_con
 	runtime_pm = xe_shrinker_runtime_pm_get(shrinker, false, nr_to_scan, can_backup);
 
 	if (purgeable && nr_scanned < nr_to_scan)
-		xe_shrinker_walk(shrinker->xe, &ctx, shrink_flags,
+		xe_shrinker_walk(shrinker, &ctx, shrink_flags,
 				 nr_to_scan, &nr_scanned, &freed);
 
 	sc->nr_scanned = nr_scanned;
@@ -234,7 +258,7 @@ static unsigned long xe_shrinker_scan(struct shrinker *shrink, struct shrink_con
 
 	shrink_flags.purge = false;
 
-	xe_shrinker_walk(shrinker->xe, &ctx, shrink_flags,
+	xe_shrinker_walk(shrinker, &ctx, shrink_flags,
 			 nr_to_scan, &nr_scanned, &freed);
 
 	sc->nr_scanned = nr_scanned;
Signature · 1 line
2.53.0
author maintainer
#152 lore raw

PATCH 149/438 drm/vc4: Use managed KMS polling to fix UAF on unbind

In reply to Greg Kroah-Hartman

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

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

From: Karl Mehltretter <[email protected]>

[ Upstream commit 073a30d75f309812ed61af134f24ffef4107b13a ]

vc4_kms_load() calls drm_kms_helper_poll_init() but the driver provides no matching drm_kms_helper_poll_fini(). The output poll work stays scheduled after unbind and runs on the freed drm_device:

  # modprobe vc4; rmmod vc4; sleep 10
  BUG: KASAN: slab-use-after-free in delayed_work_timer_fn
  BUG: KASAN: slab-use-after-free in drm_client_dev_hotplug [drm]
  Workqueue: events output_poll_execute [drm_kms_helper]
  Allocated by task 171: __devm_drm_dev_alloc
  Freed by task 262 (rmmod): drm_dev_put / component_del

Use drmm_kms_helper_poll_init() so polling is finalized with the device, as other drivers do.

Fixes c8b75bca92cb ("drm/vc4: Add KMS support for Raspberry Pi.")
Assisted-by Claude:claude-fable-5
---
 drivers/gpu/drm/vc4/vc4_kms.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/gpu/drm/vc4/vc4_kms.c +1 -1
index b17e73bce3841..82a7f2ac4c7e2 100644
@@ -1188,7 +1188,7 @@ int vc4_kms_load(struct drm_device *dev)
 
 	drm_mode_config_reset(dev);
 
-	drm_kms_helper_poll_init(dev);
+	drmm_kms_helper_poll_init(dev);
 
 	return 0;
 }
Signature · 1 line
2.53.0
author maintainer
#153 lore raw

PATCH 150/438 ALSA: hda: trace PCM open only after assigning a stream

In reply to Greg Kroah-Hartman

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

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

From: Slavin Liu <[email protected]>

[ Upstream commit c9e6e5f38bf75276605f1952b22285f5f3abcaff ]

Stream assignment can fail when hardware streams are exhausted. Move the tracepoint after the NULL check because its payload accesses the assigned stream tag.

Detected by static analysis and reviewed with AI-assisted source auditing.

Fixes 184865085b88 ("ALSA: hda - rename hda_intel_trace.h to hda_controller_trace.h")
Assisted-by LLM
---
 sound/hda/common/controller.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
sound/hda/common/controller.c +1 -1
index afec5c5546ec7..18dae022b324f 100644
@@ -586,11 +586,11 @@ static int azx_pcm_open(struct snd_pcm_substream *substream)
 	snd_hda_codec_pcm_get(apcm->info);
 	mutex_lock(&chip->open_mutex);
 	azx_dev = azx_assign_device(chip, substream);
-	trace_azx_pcm_open(chip, azx_dev);
 	if (azx_dev == NULL) {
 		err = -EBUSY;
 		goto unlock;
 	}
+	trace_azx_pcm_open(chip, azx_dev);
 	runtime->private_data = azx_dev;
 
 	runtime->hw = azx_pcm_hw;
Signature · 1 line
2.53.0
author maintainer
#154 lore raw

PATCH 151/438 wifi: ath11k: cleanup arsta in ath11k_mac_peer_cleanup_all()

In reply to Greg Kroah-Hartman

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

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

From: Nicolas Escande <[email protected]>

[ Upstream commit 820b8cff81c796ba20573e04722ab62500713f97 ]

When mac80211 removes a sta, it calls .sta_state() which in turn calls ath11k_mac_station_remove(). In that function we clean up both peers & arsta related resources.

But when the firmware crashes, ath11k calls ieee80211_restart_hw(), which assumes that all driver related resources are cleaned up beforehand. This cleanup is supposedly done by ath11k_mac_peer_cleanup_all() but does not in fact free arsta->rx_stats / tx_stats.

Extract the arsta cleanup from ath11k_mac_station_remove() into a new ath11k_mac_station_cleanup() and call it from both there and ath11k_mac_peer_cleanup_all().

This should handle kmemleaks reports like:

	unreferenced object 0xffffff801ae66400 (size 1024):
	  comm "hostapd", pid 1306, jiffies 4295011565
	  hex dump (first 32 bytes):
	    00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
	    00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
	  backtrace (crc d61c08ec):
	    kmemleak_alloc+0x3c/0x50
	    __kmalloc_cache_noprof+0x2b0/0x3e0
	    ath11k_mac_op_sta_state+0x1dc/0xb10
	    drv_sta_state+0xac/0x6f8
	    sta_info_insert_rcu+0x314/0x5e0
	    sta_info_insert+0x14/0x38
	    ieee80211_add_station+0x10c/0x1a0
	    nl80211_new_station+0x3e8/0x680
	    genl_family_rcv_msg_doit+0xc0/0x120
	    genl_rcv_msg+0x1b4/0x258
	    netlink_rcv_skb+0x4c/0x108
	    genl_rcv+0x38/0x60
	    netlink_unicast+0x190/0x278
	    netlink_sendmsg+0x15c/0x370
	    ____sys_sendmsg+0x120/0x290
	    ___sys_sendmsg+0x70/0xa0

Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.9.0.1-01977-QCAHKSWPL_SILICONZ-1

---
 drivers/net/wireless/ath/ath11k/mac.c | 25 ++++++++++++++++++-------
 1 file changed, 18 insertions(+), 7 deletions(-)
drivers/net/wireless/ath/ath11k/mac.c +18 -7
index 2d55cdc4d165d..ae91b57c8422f 100644
@@ -873,6 +873,22 @@ static int ath11k_mac_set_kickout(struct ath11k_vif *arvif)
 	return 0;
 }
 
+static void ath11k_mac_station_cleanup(struct ieee80211_sta *sta)
+{
+	struct ath11k_sta *arsta;
+
+	if (!sta)
+		return;
+
+	arsta = ath11k_sta_to_arsta(sta);
+
+	kfree(arsta->tx_stats);
+	arsta->tx_stats = NULL;
+
+	kfree(arsta->rx_stats);
+	arsta->rx_stats = NULL;
+}
+
 void ath11k_mac_peer_cleanup_all(struct ath11k *ar)
 {
 	struct ath11k_peer *peer, *tmp;
@@ -885,6 +901,7 @@ void ath11k_mac_peer_cleanup_all(struct ath11k *ar)
 	list_for_each_entry_safe(peer, tmp, &ab->peers, list) {
 		ath11k_peer_rx_tid_cleanup(ar, peer);
 		ath11k_peer_rhash_delete(ab, peer);
+		ath11k_mac_station_cleanup(peer->sta);
 		list_del(&peer->list);
 		kfree(peer);
 	}
@@ -9892,7 +9909,6 @@ static int ath11k_mac_station_remove(struct ath11k *ar,
 {
 	struct ath11k_base *ab = ar->ab;
 	struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif);
-	struct ath11k_sta *arsta = ath11k_sta_to_arsta(sta);
 	int ret;
 
 	if (ab->hw_params.vdev_start_delay &&
@@ -9916,12 +9932,7 @@ static int ath11k_mac_station_remove(struct ath11k *ar,
 			   sta->addr, arvif->vdev_id);
 
 	ath11k_mac_dec_num_stations(arvif, sta);
-
-	kfree(arsta->tx_stats);
-	arsta->tx_stats = NULL;
-
-	kfree(arsta->rx_stats);
-	arsta->rx_stats = NULL;
+	ath11k_mac_station_cleanup(sta);
 
 	return ret;
 }
Signature · 1 line
2.53.0
author maintainer
#155 lore raw

PATCH 152/438 btrfs: tree-checker: print dev extent offset in error message

In reply to Greg Kroah-Hartman

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

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

From: Filipe Manana <[email protected]>

[ Upstream commit a1167d9420474ab9ed9efca99d86aeb6217c0265 ]

If a dev extent's offset is not sector size aligned, the error message is printing the dev extent's objectid instead of the offset. This is a copy paste error, as before this check we check the objectid field.

Fixes 008e2512dc56 ("btrfs: tree-checker: add dev extent item checks")
---
 fs/btrfs/tree-checker.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
fs/btrfs/tree-checker.c +1 -1
index 0ce91396b517f..1326bbc1322dc 100644
@@ -2100,7 +2100,7 @@ static int check_dev_extent_item(const struct extent_buffer *leaf,
 				 sectorsize))) {
 		generic_err(leaf, slot,
 			    "invalid dev extent chunk offset, has %llu not aligned to %u",
-			    btrfs_dev_extent_chunk_objectid(leaf, de),
+			    btrfs_dev_extent_chunk_offset(leaf, de),
 			    sectorsize);
 		return -EUCLEAN;
 	}
Signature · 1 line
2.53.0
author maintainer
#156 lore raw

PATCH 153/438 drm/msm/dsi: round the byte clock rate after reparenting to the PHY PLL

In reply to Greg Kroah-Hartman

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

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

From: Dmitry Baryshkov <[email protected]>

[ Upstream commit 2028280686f4fa78e2f1f6dede4b6c1fd782b9e3 ]

DSI 6G v2.9 hosts (SM8650, SM8750, Kaanapali, etc.) reparent the byte and pixel RCGs to the DSI PHY PLL at runtime from dsi_link_clk_set_rate_6g_v2_9(), after the PHY has been enabled. However dsi_calc_clk_rate_6g() runs earlier, in order to compute the bit clock request for the PHY. At that point the byte RCG still has its reset parent (XO), so clk_round_rate() returns a bogus rate, which then ends up in the PHY bit clock request and the PLL gets programmed to a wrong frequency, breaking the panel.

Move the rounding to dsi_link_clk_set_rate_6g(), which is called after the RCGs have been reparented to the PLL. Storing the rounded rate at this point still makes later link_clk_set_rate() calls no-ops in the CCF. Derive the byte interface clock rate from the rounded byte clock rate, otherwise it would keep requesting the idealized rate and retrigger the PLL on every transfer.

Fixes 6cd33b6f4155 ("drm/msm/dsi: round 6G byte clock rate to the PLL-achievable value")
Assisted-by LLM

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

---
 drivers/gpu/drm/msm/dsi/dsi_host.c | 36 ++++++++++++++++--------------
 1 file changed, 19 insertions(+), 17 deletions(-)
drivers/gpu/drm/msm/dsi/dsi_host.c +19 -17
index 2685cc3d03590..89face2670187 100644
@@ -129,7 +129,7 @@ struct msm_dsi_host {
 	struct clk *dsi_pll_pixel_clk;
 
 	unsigned long byte_clk_rate;
-	unsigned long byte_intf_clk_rate;
+	bool byte_intf_clk_div_2;
 	unsigned long pixel_clk_rate;
 	unsigned long esc_clk_rate;
 
@@ -381,8 +381,20 @@ int msm_dsi_runtime_resume(struct device *dev)
 
 int dsi_link_clk_set_rate_6g(struct msm_dsi_host *msm_host)
 {
+	unsigned long byte_intf_clk_rate;
+	long rounded_byte_clk_rate;
 	int ret;
 
+	rounded_byte_clk_rate = clk_round_rate(msm_host->byte_clk,
+					       msm_host->byte_clk_rate);
+	if (rounded_byte_clk_rate < 0) {
+		pr_err("%s: failed to round byte clock rate, %ld\n",
+		       __func__, rounded_byte_clk_rate);
+		return rounded_byte_clk_rate;
+	}
+
+	msm_host->byte_clk_rate = rounded_byte_clk_rate;
+
 	DBG("Set clk rates: pclk=%lu, byteclk=%lu",
 	    msm_host->pixel_clk_rate, msm_host->byte_clk_rate);
 
@@ -400,7 +412,11 @@ int dsi_link_clk_set_rate_6g(struct msm_dsi_host *msm_host)
 	}
 
 	if (msm_host->byte_intf_clk) {
-		ret = clk_set_rate(msm_host->byte_intf_clk, msm_host->byte_intf_clk_rate);
+		byte_intf_clk_rate = msm_host->byte_clk_rate;
+		if (msm_host->byte_intf_clk_div_2)
+			byte_intf_clk_rate /= 2;
+
+		ret = clk_set_rate(msm_host->byte_intf_clk, byte_intf_clk_rate);
 		if (ret) {
 			pr_err("%s: Failed to set rate byte intf clk, %d\n",
 			       __func__, ret);
@@ -668,24 +684,12 @@ static void dsi_calc_pclk(struct msm_dsi_host *msm_host, bool is_bonded_dsi)
 
 int dsi_calc_clk_rate_6g(struct msm_dsi_host *msm_host, bool is_bonded_dsi)
 {
-	long rounded_byte_clk_rate;
-
 	if (!msm_host->mode) {
 		pr_err("%s: mode not set\n", __func__);
 		return -EINVAL;
 	}
 
 	dsi_calc_pclk(msm_host, is_bonded_dsi);
-
-	rounded_byte_clk_rate = clk_round_rate(msm_host->byte_clk,
-					       msm_host->byte_clk_rate);
-	if (rounded_byte_clk_rate < 0) {
-		pr_err("%s: failed to round byte clock rate, %ld\n",
-		       __func__, rounded_byte_clk_rate);
-		return rounded_byte_clk_rate;
-	}
-
-	msm_host->byte_clk_rate = rounded_byte_clk_rate;
 	msm_host->esc_clk_rate = clk_get_rate(msm_host->esc_clk);
 	return 0;
 }
@@ -2500,9 +2504,7 @@ int msm_dsi_host_power_on(struct mipi_dsi_host *host,
 		goto unlock_ret;
 	}
 
-	msm_host->byte_intf_clk_rate = msm_host->byte_clk_rate;
-	if (phy_shared_timings->byte_intf_clk_div_2)
-		msm_host->byte_intf_clk_rate /= 2;
+	msm_host->byte_intf_clk_div_2 = phy_shared_timings->byte_intf_clk_div_2;
 
 	msm_dsi_sfpb_config(msm_host, true);
 
Signature · 1 line
2.53.0
author maintainer
#157 lore raw

PATCH 154/438 seg6: set IPSKB_L3SLAVE from IP6SKB_L3SLAVE on IPIP decapsulation

In reply to Greg Kroah-Hartman

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

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

From: Andrea Mayer <[email protected]>

[ Upstream commit 7616242a2b37883f7322aaa1d2bd6cd0fed28315 ]

When an SRv6 packet arrives on an interface enslaved to a VRF, vrf_ip6_rcv() sets IP6SKB_L3SLAVE in IP6CB, but decap_and_validate() has never set IPSKB_L3SLAVE in IPCB. The bit stayed clear in the common case, and with CONFIG_IPV6_MIP6 the leftover frag_max_size of a reassembled outer packet could even set it, with no VRF involved. Commit 44930446dde4 ("ipv6: seg6: clear IPv4 control block on IPIP decapsulation") then made the unreliable bit reliably clear.

The effect of the missing flag is visible with End.DX4 when a delivery to a local address of the node reaches the socket lookup. For example, a UDP socket bound to the enslaved ingress interface does not receive any of the decapsulated packets, while an unbound socket outside the VRF does.
This contradicts Documentation/networking/vrf.rst: by default the scope of an unbound UDP or TCP socket is limited to the default VRF.

Set IPSKB_L3SLAVE for IPv4 in decap_and_validate(), which already does the same for IPv6. The socket lookup then matches the decapsulated packet like any other packet received on that enslaved interface. Such a packet matches an unbound UDP or TCP socket only when udp_l3mdev_accept or tcp_l3mdev_accept is set.

---
 net/ipv6/seg6_local.c | 3 +++
 1 file changed, 3 insertions(+)
net/ipv6/seg6_local.c +3 -0
index 7b52122201858..d1070aec7b72b 100644
@@ -257,10 +257,13 @@ static bool decap_and_validate(struct sk_buff *skb, int proto)
 		return false;
 
 	if (proto == IPPROTO_IPIP) {
+		bool l3slave = ipv6_l3mdev_skb(IP6CB(skb)->flags);
 		int iif = IP6CB(skb)->iif;
 
 		memset(IPCB(skb), 0, sizeof(*IPCB(skb)));
 		IPCB(skb)->iif = iif;
+		if (l3slave)
+			IPCB(skb)->flags |= IPSKB_L3SLAVE;
 	} else if (proto == IPPROTO_IPV6) {
 		bool l3slave = ipv6_l3mdev_skb(IP6CB(skb)->flags);
 		int iif = IP6CB(skb)->iif;
Signature · 1 line
2.53.0
author maintainer
#158 lore raw

PATCH 155/438 net: dsa: mxl862xx: disable the stats poll on teardown

In reply to Greg Kroah-Hartman

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

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

From: Daniel Golle <[email protected]>

[ Upstream commit 9e92ad4630f5dd1838ce6bbe6b1bd2c73d34de36 ]

mxl862xx_setup() arms the stats poll before mxl862xx_setup_mdio(), and nothing stops it until dsa_register_switch() has returned an error to mxl862xx_probe(). DSA frees the dsa_port list before it returns, so a poll that fires once .setup or a later step of dsa_tree_setup() has failed walks freed ports. On shutdown the user ports stay registered, and the WORK_STOPPED flag test in mxl862xx_get_stats64() is not atomic with the cancel in mxl862xx_shutdown(), so a re-arm that read the flag before it was set queues the poll after cancel_delayed_work_sync() has returned.

Arm the poll once .setup has succeeded and stop it from a .teardown op, which DSA calls on unregister and after a failed registration, in both cases before it frees the ports. Use disable_delayed_work_sync() there and in shutdown(): it drains a running poll as the cancel did and turns every later attempt to queue the work into a no-op, so the re-arm cannot bring the poll back. remove() and the probe error path only set WORK_STOPPED, which crc_err_work tests before it walks the ports.

---
 drivers/net/dsa/mxl862xx/mxl862xx.c | 23 ++++++++++++++++-------
 1 file changed, 16 insertions(+), 7 deletions(-)
drivers/net/dsa/mxl862xx/mxl862xx.c +16 -7
index cfa7e3e269a28..e05ad52cd297e 100644
@@ -685,10 +685,22 @@ static int mxl862xx_setup(struct dsa_switch *ds)
 	if (ret)
 		return ret;
 
+	ret = mxl862xx_setup_mdio(ds);
+	if (ret)
+		return ret;
+
 	schedule_delayed_work(&priv->stats_work,
 			      MXL862XX_STATS_POLL_INTERVAL);
 
-	return mxl862xx_setup_mdio(ds);
+	return 0;
+}
+
+static void mxl862xx_teardown(struct dsa_switch *ds)
+{
+	struct mxl862xx_priv *priv = ds->priv;
+
+	set_bit(MXL862XX_FLAG_WORK_STOPPED, &priv->flags);
+	disable_delayed_work_sync(&priv->stats_work);
 }
 
 static int mxl862xx_port_state(struct dsa_switch *ds, int port, bool enable)
@@ -2047,9 +2059,7 @@ static void mxl862xx_get_stats64(struct dsa_switch *ds, int port,
 
 	spin_unlock_bh(&priv->ports[port].stats_lock);
 
-	/* Trigger a fresh poll so the next read sees up-to-date counters.
-	 * No-op if the work is already pending, running, or teardown started.
-	 */
+	/* Trigger a fresh poll so the next read sees up-to-date counters. */
 	if (!test_bit(MXL862XX_FLAG_WORK_STOPPED, &priv->flags))
 		schedule_delayed_work(&priv->stats_work, 0);
 }
@@ -2057,6 +2067,7 @@ static void mxl862xx_get_stats64(struct dsa_switch *ds, int port,
 static const struct dsa_switch_ops mxl862xx_switch_ops = {
 	.get_tag_protocol = mxl862xx_get_tag_protocol,
 	.setup = mxl862xx_setup,
+	.teardown = mxl862xx_teardown,
 	.port_setup = mxl862xx_port_setup,
 	.port_teardown = mxl862xx_port_teardown,
 	.phylink_get_caps = mxl862xx_phylink_get_caps,
@@ -2131,7 +2142,6 @@ static int mxl862xx_probe(struct mdio_device *mdiodev)
 	err = dsa_register_switch(ds);
 	if (err) {
 		set_bit(MXL862XX_FLAG_WORK_STOPPED, &priv->flags);
-		cancel_delayed_work_sync(&priv->stats_work);
 		mxl862xx_host_shutdown(priv);
 		for (i = 0; i < MXL862XX_MAX_PORTS; i++)
 			cancel_work_sync(&priv->ports[i].host_flood_work);
@@ -2152,7 +2162,6 @@ static void mxl862xx_remove(struct mdio_device *mdiodev)
 	priv = ds->priv;
 
 	set_bit(MXL862XX_FLAG_WORK_STOPPED, &priv->flags);
-	cancel_delayed_work_sync(&priv->stats_work);
 
 	dsa_unregister_switch(ds);
 
@@ -2181,7 +2190,7 @@ static void mxl862xx_shutdown(struct mdio_device *mdiodev)
 	dsa_switch_shutdown(ds);
 
 	set_bit(MXL862XX_FLAG_WORK_STOPPED, &priv->flags);
-	cancel_delayed_work_sync(&priv->stats_work);
+	disable_delayed_work_sync(&priv->stats_work);
 
 	mxl862xx_host_shutdown(priv);
 
Signature · 1 line
2.53.0
author maintainer
#159 lore raw

PATCH 156/438 net: bcmgenet: restore the hardware filters on open

In reply to Greg Kroah-Hartman

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

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

From: Nicolai Buchwitz <[email protected]>

[ Upstream commit 23ca4ddc4fce2c233a49e9fd34d4b5b02bd7324e ]

bcmgenet_hfb_init() runs INIT_LIST_HEAD() on priv->rxnfc_list, which drops every rule off the list, and bcmgenet_open() calls it on each ifup. Every rule the user configured is silently lost:

  # ethtool -N eth0 flow-type ether dst $MAC action 0
  Added rule with ID 0
  # ethtool -n eth0 | grep -c Filter:
  1
  # ip link set eth0 down && ip link set eth0 up
  # ethtool -n eth0 | grep -c Filter:
  0

Initialise the lists once at probe and restore the rules on open, as bcmgenet_resume() already does.

---
 .../net/ethernet/broadcom/genet/bcmgenet.c    | 19 +++++++++++++------
 1 file changed, 13 insertions(+), 6 deletions(-)
drivers/net/ethernet/broadcom/genet/bcmgenet.c +13 -6
index a2305e6428d1f..b916080f4ff17 100644
@@ -749,8 +749,17 @@ static void bcmgenet_hfb_init(struct bcmgenet_priv *priv)
 		INIT_LIST_HEAD(&priv->rxnfc_rules[i].list);
 		priv->rxnfc_rules[i].state = BCMGENET_RXNFC_STATE_UNUSED;
 	}
+}
+
+static void bcmgenet_hfb_restore(struct bcmgenet_priv *priv)
+{
+	struct bcmgenet_rxnfc_rule *rule;
 
 	bcmgenet_hfb_clear(priv);
+
+	list_for_each_entry(rule, &priv->rxnfc_list, list)
+		if (rule->state != BCMGENET_RXNFC_STATE_UNUSED)
+			bcmgenet_hfb_create_rxnfc_filter(priv, rule);
 }
 
 static int bcmgenet_begin(struct net_device *dev)
@@ -3376,8 +3385,8 @@ static int bcmgenet_open(struct net_device *dev)
 
 	bcmgenet_set_hw_addr(priv, dev->dev_addr);
 
-	/* HFB init */
-	bcmgenet_hfb_init(priv);
+	/* Restore the filters, the MAC was reset above */
+	bcmgenet_hfb_restore(priv);
 
 	/* Reinitialize TDMA and RDMA and SW housekeeping */
 	ret = bcmgenet_init_dma(priv, true);
@@ -4075,6 +4084,7 @@ static int bcmgenet_probe(struct platform_device *pdev)
 
 	/* Mii wait queue */
 	init_waitqueue_head(&priv->wq);
+	bcmgenet_hfb_init(priv);
 	INIT_WORK(&priv->bcmgenet_irq_work, bcmgenet_irq_task);
 
 	priv->clk_wol = devm_clk_get_optional(&priv->pdev->dev, "enet-wol");
@@ -4272,10 +4282,7 @@ static int bcmgenet_resume(struct device *d)
 	bcmgenet_set_hw_addr(priv, dev->dev_addr);
 
 	/* Restore hardware filters */
-	bcmgenet_hfb_clear(priv);
-	list_for_each_entry(rule, &priv->rxnfc_list, list)
-		if (rule->state != BCMGENET_RXNFC_STATE_UNUSED)
-			bcmgenet_hfb_create_rxnfc_filter(priv, rule);
+	bcmgenet_hfb_restore(priv);
 
 	/* Reinitialize TDMA and RDMA and SW housekeeping */
 	ret = bcmgenet_init_dma(priv, false);
Signature · 1 line
2.53.0
author maintainer
#160 lore raw

PATCH 157/438 sysctl: Check range in proc_dointvec_ms_jiffies_minmax

In reply to Greg Kroah-Hartman

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

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

From: Kuniyuki Iwashima <[email protected]>

[ Upstream commit 82431877d837a6c2593efd8e66ba28b35a220ca4 ]

Add the range check to do_proc_int_conv_ms_jiffies_minmax that commit d174174c6776 ("sysctl: replace SYSCTL_INT_CONV_CUSTOM macro with functions") incorrectly removed.

Fixes d174174c6776 ("sysctl: replace SYSCTL_INT_CONV_CUSTOM macro with functions")
---
 kernel/time/jiffies.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/time/jiffies.c +1 -1
index d51428867a339..f4c43b9716f89 100644
@@ -181,7 +181,7 @@ static int do_proc_int_conv_ms_jiffies_minmax(bool *negp, ulong *u_ptr,
 					      int *k_ptr, int dir,
 					      const struct ctl_table *tbl)
 {
-	return proc_int_conv(negp, u_ptr, k_ptr, dir, tbl, false,
+	return proc_int_conv(negp, u_ptr, k_ptr, dir, tbl, true,
 			     sysctl_u2k_int_conv_ms, sysctl_k2u_int_conv_ms);
 }
 
Signature · 1 line
2.53.0
author maintainer
#161 lore raw

PATCH 158/438 ipv4: icmp: reject RTN_UNREACHABLE input routes in icmp_route_lookup

In reply to Greg Kroah-Hartman

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

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

From: Dong Chenchen <[email protected]>

[ Upstream commit 2998147b59c9df0a51477c7a6b3d1f0ba3127dd4 ]

When the forward output route cannot be used in icmp_route_lookup(), it enters the "reverse path" and calls ip_route_input() on fl4_dec.daddr, the original packet's source address.

ip_route_input() only returns an error for truly invalid packets. For unreachable addresses it will succeed and return an input route whose dst.output is set to ip_rt_bug(). The existing check only rejects RTN_LOCAL routes, so the RTN_UNREACHABLE route types can still be returned and later used for output, syzkaller triggering a WARN_ON_ONCE() in ip_rt_bug() as bellow:

 ------------[ cut here ]------------
 WARNING: net/ipv4/route.c:1273 at ip_rt_bug+0x14/0x20
 RIP: 0010:ip_rt_bug+0x14/0x20
 Call Trace:
  ip_push_pending_frames+0xfa/0x100
  __icmp_send+0x905/0xf10
  ip_options_compile+0xc0/0xd0
  ip_rcv_finish_core+0x321/0xae0
  ip_rcv+0x1de/0x260
  __netif_receive_skb_one_core+0x11a/0x130
  netif_receive_skb+0x7b/0x260
  tun_get_user+0x11bf/0x1c10
 ------------[ cut here ]------------

Reject input route that is RTN_UNREACHABLE to fix it. The net warning is only printed for RTN_LOCAL, as RTN_UNREACHABLE is not the result of a race condition.

---
 net/ipv4/icmp.c | 17 ++++++++++-------
 1 file changed, 10 insertions(+), 7 deletions(-)
net/ipv4/icmp.c +10 -7
index 0caedfc7ca92f..90c0e22c29bea 100644
@@ -581,16 +581,19 @@ static struct rtable *icmp_route_lookup(struct net *net, struct flowi4 *fl4,
 		skb_dstref_restore(skb_in, orefdst);
 
 		/*
-		 * At this point, fl4_dec.daddr should NOT be local (we
-		 * checked fl4_dec.saddr above). However, a race condition
-		 * may occur if the address is added to the interface
-		 * concurrently. In that case, ip_route_input() returns a
-		 * LOCAL route with dst.output=ip_rt_bug, which must not
-		 * be used for output.
+		 * fl4_dec.daddr is not expected to be local here, but it can be
+		 * added to an interface concurrently, in which case
+		 * ip_route_input() returns a LOCAL route. It can also fail to
+		 * build a forwarding route towards fl4_dec.daddr, for example,
+		 * when forwarding is disabled, and return an UNREACHABLE route.
+		 * Both cases will result in a route with dst.output=ip_rt_bug,
+		 * which must not be used for output.
 		 */
-		if (!err && rt2 && rt2->rt_type == RTN_LOCAL) {
+		if (!err && rt2 && rt2->rt_type == RTN_LOCAL)
 			net_warn_ratelimited("detected local route for %pI4 during ICMP sending, src %pI4\n",
 					     &fl4_dec.daddr, &fl4_dec.saddr);
+		if (!err && rt2 &&
+		    (rt2->rt_type == RTN_LOCAL || rt2->rt_type == RTN_UNREACHABLE)) {
 			dst_release(&rt2->dst);
 			err = -EINVAL;
 		}
Signature · 1 line
2.53.0
author maintainer
#162 lore raw

PATCH 159/438 net: fddi: skfp: fix NULL deref when setting the MAC address while down

In reply to Greg Kroah-Hartman

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

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

From: Hohyun Sim <[email protected]>

[ Upstream commit 7c8810c2e69c3d9ca6df870b40ae9218e50b4fb1 ]

skfp_ctl_set_mac_address() calls ResetAdapter() unconditionally, without checking netif_running(). ResetAdapter() first calls card_stop(), which sets smc->hw.hw_state to STOPPED, and then mac_drv_clear_tx_queue(), which walks the two transmit queues:

	for (i = QUEUE_S; i <= QUEUE_A0; i++) {
		queue = smc->hw.fp.tx[i] ;
		...
		t = queue->tx_curr_get ;

smc->hw.fp.tx[] is only populated by init_tx(), which is reached from skfp_open() through init_smt() -> init_fddi_driver() -> init_fplus() -> init_mac() -> init_tx(). The private area is allocated and zeroed by alloc_fddidev(), so on an interface that has never been brought up both queue pointers are still NULL. The hw_state test at the top of mac_drv_clear_tx_queue() does not catch this, because card_stop() has just set STOPPED; the function proceeds into the loop and dereferences NULL. ResetAdapter() does call init_smt() itself, but only after the queues have been cleared.

Setting the MAC address on a down interface therefore oopses:

ip link set dev fddi0 address 02:00:00:00:00:01

  BUG: KASAN: null-ptr-deref in mac_drv_clear_tx_queue+0x68/0x2c0 [skfp]
  Read of size 8 at addr 0000000000000010 by task ip/302
  Call Trace:
   <TASK>
   mac_drv_clear_tx_queue+0x68/0x2c0 [skfp 6c01d4bab63c36978bd0a7d7e90837adb44cc37b]
   ResetAdapter+0x29/0x100 [skfp 6c01d4bab63c36978bd0a7d7e90837adb44cc37b]
   skfp_ctl_set_mac_address+0x57/0x80 [skfp 6c01d4bab63c36978bd0a7d7e90837adb44cc37b]
   netif_set_mac_address+0x1e4/0x2c0
   do_setlink+0x684/0x2680
   </TASK>

Address 0x10 is the offset of tx_curr_get, the third pointer in struct s_smt_tx_queue, on 64-bit. mac_drv_clear_rx_queue(), which ResetAdapter() calls immediately afterwards, dereferences smc->hw.fp.rx[QUEUE_R1] in the same way behind the same ineffective hw_state test; the transmit queue merely crashes first. Both are covered by the guard below.

Skip the adapter reset when the interface is down. dev_addr_set() is left unconditional, so the new address is still recorded in dev->dev_addr. Nothing is lost by not resetting the adapter here:
skfp_open() deliberately re-reads the factory address on every open,

	read_address(smc, NULL);
	eth_hw_addr_set(dev, smc->hw.fddi_canon_addr.a);

and the comment above it states this is done to discard exactly such an address override across a close/open cycle. An address set while the interface is down could not have survived the following open even before this change, so the guard removes no working behaviour. Guarding the hardware side of ndo_set_mac_address() with netif_running() is established practice; skge_set_mac_address() has done so since commit 2eb3e621c4e0 ("skge: set mac address bonding fix").

Guarding the reset as a whole, rather than NULL-checking the queues, is also what the rest of the driver expects. After a previous open/close the queue pointers are stale but non-NULL, so there is no crash, yet ResetAdapter() goes on to call smt_online() and STI_FBI() ("Enable Board Interrupts") while skfp_close() has already called free_irq() - the adapter would be brought back online with no handler installed. The only other ResetAdapter() caller is skfp_interrupt(), which by construction runs only while the device is open.

Found by automated driver testing against an emulated SysKonnect FDDI adapter under a KASAN-enabled 7.0.0 kernel. Triggering it requires CAP_NET_ADMIN.

Fixes 1da177e4c3f4 ("Linux-2.6.12-rc2")
Assisted-by LLM KASAN
---
 drivers/net/fddi/skfp/skfddi.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/net/fddi/skfp/skfddi.c +2 -1
index a273362c9e703..feea7baa48168 100644
@@ -928,7 +928,8 @@ static int skfp_ctl_set_mac_address(struct net_device *dev, void *addr)
 
 	dev_addr_set(dev, p_sockaddr->sa_data);
 	spin_lock_irqsave(&bp->DriverLock, Flags);
-	ResetAdapter(smc);
+	if (netif_running(dev))
+		ResetAdapter(smc);
 	spin_unlock_irqrestore(&bp->DriverLock, Flags);
 
 	return 0;		/* always return zero */
Signature · 1 line
2.53.0
author maintainer
#163 lore raw

PATCH 160/438 wifi: brcmfmac: fix lost 802.1x TX completion wakeup

In reply to Greg Kroah-Hartman

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

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

From: Karl Mehltretter <[email protected]>

[ Upstream commit 621d90169cef6c8da5b6134db5c0c4e23cdd09ce ]

brcmf_txfinalize() decrements pend_8021x_cnt before a lockless waitqueue_active() check. atomic_dec() does not order the decrement against the check.

The waiter can therefore observe a nonzero count while the waker observes an empty queue, losing the final wakeup and delaying key installation until the 950 ms timeout.

Add smp_mb__after_atomic() to order the decrement before the queue check. wait_event_timeout() provides the matching barrier. LKMM confirms that this forbids the lost-wakeup outcome.

Fixes 21fff75d2fb6 ("brcmfmac: use wait_event_timeout for 8021x pending count")
Assisted-by Claude:claude-fable-5
---
 drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c | 2 ++
 1 file changed, 2 insertions(+)
drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c +2 -0
index dad6f4563d146..d2ae679856067 100644
@@ -555,6 +555,8 @@ void brcmf_txfinalize(struct brcmf_if *ifp, struct sk_buff *txp, bool success)
 
 	if (type == ETH_P_PAE) {
 		atomic_dec(&ifp->pend_8021x_cnt);
+		/* Order the decrement before waitqueue_active() */
+		smp_mb__after_atomic();
 		if (waitqueue_active(&ifp->pend_8021x_wait))
 			wake_up(&ifp->pend_8021x_wait);
 	}
Signature · 1 line
2.53.0
author maintainer
#164 lore raw

PATCH 161/438 net: bridge: mst: move switchdev call outside rcu

In reply to Greg Kroah-Hartman

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

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

From: Nikolay Aleksandrov <[email protected]>

[ Upstream commit 18a6fe05fb6e18de29fa90d388bb34044114b3d8 ]

This is a follow-up of one of sashiko's pre-existing bug reports. br_mst_set_state() calls switchdev_port_attr_set() for nonzero MSTIs while holding rcu_read_lock() which invokes the blocking switchdev notifier chain and may sleep. Nonzero MSTI changes come from netlink with rtnl held. Move the switchdev call before entering the rcu section and assert that rtnl is held.

The call cannot be deferred because netlink needs its error and extack. Also DSA reads the old bridge MST state during the callback and checks it. A deferred callback will be late and will see the updated state.

---
 net/bridge/br_mst.c | 20 ++++++++++++--------
 1 file changed, 12 insertions(+), 8 deletions(-)
net/bridge/br_mst.c +12 -8
index 43a300ae6bfaf..1654efd3045b0 100644
@@ -107,21 +107,24 @@ int br_mst_set_state(struct net_bridge_port *p, u16 msti, u8 state,
 	struct net_bridge_vlan *v;
 	int err = 0;
 
-	rcu_read_lock();
-	vg = nbp_vlan_group_rcu(p);
-	if (!vg)
-		goto out;
-
 	/* MSTI 0 (CST) state changes are notified via the regular
-	 * SWITCHDEV_ATTR_ID_PORT_STP_STATE.
+	 * SWITCHDEV_ATTR_ID_PORT_STP_STATE. All other MSTIs are handled via
+	 * netlink with RTNL held
 	 */
 	if (msti) {
+		ASSERT_RTNL();
+
 		err = switchdev_port_attr_set(p->dev, &attr, extack);
 		if (err && err != -EOPNOTSUPP)
 			goto out;
+		err = 0;
 	}
 
-	err = 0;
+	rcu_read_lock();
+	vg = nbp_vlan_group_rcu(p);
+	if (!vg)
+		goto out_rcu_unlock;
+
 	list_for_each_entry_rcu(v, &vg->vlan_list, vlist) {
 		if (v->brvlan->msti != msti)
 			continue;
@@ -129,8 +132,9 @@ int br_mst_set_state(struct net_bridge_port *p, u16 msti, u8 state,
 		br_mst_vlan_set_state(vg, v, state);
 	}
 
-out:
+out_rcu_unlock:
 	rcu_read_unlock();
+out:
 	return err;
 }
 
Signature · 1 line
2.53.0
author maintainer
#165 lore raw

PATCH 162/438 tcp: Dont call skb_clone_and_charge_r() for close()d listener in tcp_v6_do_rcv().

In reply to Greg Kroah-Hartman

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

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

From: Kuniyuki Iwashima <[email protected]>

[ Upstream commit 8e759cd1f6444a946bd1fd2b2b29eea582eea1d5 ]

tcp_v6_do_rcv() no longer calls skb_clone_and_charge_r() for TCP_LISTEN since commit 073d89808c06 ("net: fix data-races around sk->sk_forward_alloc").

However, there is still a small race window between tcp_v6_rcv() and tcp_v6_do_rcv(), where concurrent close() changes TCP_LISTEN to TCP_CLOSE, causing skb_clone_and_charge_r() to be called locklessly and resulting in the splat below. [0]

Let's avoid calling skb_clone_and_charge_r() for TCP_CLOSE as well.

This is fine for non-listeners because tcp_rcv_state_process() drops skb for TCP_CLOSE and opt_skb was freed immediately anyway.

[0]:
sk->sk_forward_alloc

WARNING: net/ipv4/af_inet.c:162 at inet_sock_destruct+0x64d/0x810 net/ipv4/af_inet.c:162, CPU#1: ksoftirqd/1/28
Modules linked in:
CPU: 1 UID: 0 PID: 28 Comm: ksoftirqd/1 Not tainted 7.2.0 #17 PREEMPT(full)
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.17.0-debian-1.17.0-1 04/01/2014
RIP: 0010:inet_sock_destruct+0x64d/0x810 net/ipv4/af_inet.c:162
Code: 3d 49 ff e9 06 fd ff ff e8 d0 5b 83 f8 90 0f 0b 90 e9 35 fe ff ff e8 c2 5b 83 f8 90 0f 0b 90 e9 c5 fe ff ff e8 b4 5b 83 f8 90 <0f> 0b 90 e9 04 ff ff ff e8 a6 5b 83 f8 90 0f 0b 90 e9 65 fe ff ff
RSP: 0018:ffffc90000677bb8 EFLAGS: 00010246
RAX: 0000000000000000 RBX: ffff8880117bde80 RCX: ffffffff8957eb41
RDX: ffff88801dad5d00 RSI: ffffffff8957ec3c RDI: 0000000000000005
RBP: 00000000fffff000 R08: ffffffff8957eb41 R09: 00000000fffff000
R10: 0000000000000005 R11: 0000000000000000 R12: dffffc0000000000
R13: ffff8880117bdf10 R14: ffffffff81c08eb7 R15: 0000000000000003
FS:  0000000000000000(0000) GS:ffff8880d7ae5000(0000) knlGS:0000000000000000
CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007f93a1021138 CR3: 00000000207a9000 CR4: 0000000000350ef0
Call Trace:
 <TASK>
 __sk_destruct+0x82/0xae0 net/core/sock.c:2356
 rcu_do_batch kernel/rcu/tree.c:2645 [inline]
 rcu_core+0x59c/0x1100 kernel/rcu/tree.c:2897
 handle_softirqs+0x1e4/0x9b0 kernel/softirq.c:622
 run_ksoftirqd kernel/softirq.c:1076 [inline]
 run_ksoftirqd+0x38/0x60 kernel/softirq.c:1068
 smpboot_thread_fn+0x458/0xc80 kernel/smpboot.c:160
 kthread+0x396/0x4a0 kernel/kthread.c:436
 ret_from_fork+0x8e0/0xe40 arch/x86/kernel/process.c:158
 ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245
 </TASK>
---
 net/ipv6/tcp_ipv6.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
net/ipv6/tcp_ipv6.c +2 -1
index df9c29eb5c1f4..7fa4ed2fd4f16 100644
@@ -1604,7 +1604,8 @@ int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb)
 	   by tcp. Feel free to propose better solution.
 					       --ANK (980728)
 	 */
-	if (np->rxopt.all && sk->sk_state != TCP_LISTEN)
+	if (np->rxopt.all &&
+	    !((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_CLOSE)))
 		opt_skb = skb_clone_and_charge_r(skb, sk);
 
 	if (sk->sk_state == TCP_ESTABLISHED) { /* Fast path */
Signature · 1 line
2.53.0
author maintainer
#166 lore raw

PATCH 163/438 tcp: do not let tcp_rmem be set below 4096

In reply to Greg Kroah-Hartman

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

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

From: Eric Dumazet <[email protected]>

[ Upstream commit 83a945a529d6e002dd7339c532288a931f463dba ]

We can hit a division by zero crash in tcp_rcvbuf_grow() and tcp_rcv_space_adjust():

divide error: 0000 [#1] PREEMPT SMP

RIP: 0010:tcp_rcvbuf_grow+0x187/0x450 net/ipv4/tcp_input.c:939

...
grow = div_u64(((u64)rcvwin << 1) * (newval - oldval), oldval);

The division uses oldval = tp->rcvq_space.space as divisor. When tp->rcvq_space.space is zero, this leads to a divide-by-zero exception.

tp->rcvq_space.space is initialized in tcp_init_buffer_space():

    tp->rcvq_space.space = min3(tp->rcv_ssthresh, tp->rcv_wnd,
                                (u32)TCP_INIT_CWND * tp->advmss);

If tcp_rmem[1] is configured to very small values (such as 1), sk->sk_rcvbuf is initialized to 1. Then tcp_full_space(sk), which computes (sk->sk_rcvbuf * scaling_ratio) >> 8, truncates to 0. This sets tp->window_clamp = 0, tp->rcv_ssthresh = 0, and tp->rcvq_space.space = 0. Later, when data arrives and DRS is invoked, tcp_rcvbuf_grow() divides by oldval == 0.

Back in 2015, commit b1cb59cf2efe ("net: sysctl_net_core: check SNDBUF and RCVBUF for min length") ensured that net.core.rmem_default and net.core.rmem_max cannot be set below SOCK_MIN_RCVBUF. Similarly, SO_RCVBUF setsockopt enforces max_t(int, val * 2, SOCK_MIN_RCVBUF).

However, net.ipv4.tcp_rmem still had .extra1 = SYSCTL_ONE, allowing arbitrarily small values.

Because SOCK_MIN_RCVBUF depends on sizeof(struct sk_buff) and cacheline alignment, its value varies across architectures and configuration options. Using a fixed constant of 4096 ensures a predictable, architecture- independent lower bound that is safely above SOCK_MIN_RCVBUF everywhere and matches the documented 4K default.

Fix this by setting tcp_rmem.extra1 to 4096 and updating the documentation.

---
 Documentation/networking/ip-sysctl.rst | 2 ++
 net/ipv4/sysctl_net_ipv4.c             | 4 +++-
 2 files changed, 5 insertions(+), 1 deletion(-)
Documentation/networking/ip-sysctl.rst +2 -0
index b05829e44d8fc..f7af0286341c9 100644
@@ -874,6 +874,8 @@ tcp_rmem - vector of 3 INTEGERs: min, default, max
 	case this value is ignored.
 	Default: between 131072 and 32MB, depending on RAM size.
 
+	Each of the three values cannot be set below 4096.
+
 tcp_sack - BOOLEAN
 	Enable select acknowledgments (SACKS).
 
net/ipv4/sysctl_net_ipv4.c +3 -1
index ca1180dba1dea..e921cb381b53c 100644
@@ -51,6 +51,8 @@ static int tcp_ecn_mode_max = 5;
 static u32 icmp_errors_extension_mask_all =
 	GENMASK_U8(ICMP_ERR_EXT_COUNT - 1, 0);
 
+static int tcp_min_rcvbuf = 4096;
+
 /* obsolete */
 static int sysctl_tcp_low_latency __read_mostly;
 
@@ -1462,7 +1464,7 @@ static struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(init_net.ipv4.sysctl_tcp_rmem),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ONE,
+		.extra1		= &tcp_min_rcvbuf,
 	},
 	{
 		.procname	= "tcp_comp_sack_delay_ns",
Signature · 1 line
2.53.0
author maintainer
#167 lore raw

PATCH 164/438 ksmbd: return buffer overflow for partial filesystem info

In reply to Greg Kroah-Hartman

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

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

From: Namjae Jeon <[email protected]>

[ Upstream commit 0ecd35fac4b4f2828490689b46039744d201dcb0 ]

The query-info buffer check returns STATUS_INFO_LENGTH_MISMATCH for every output buffer smaller than the complete response. Variable-length filesystem information instead requires STATUS_BUFFER_OVERFLOW when the fixed portion fits but the complete data does not.

Pass the fixed size for each filesystem information class to the buffer checker. Keep INFO_LENGTH_MISMATCH for buffers below that size, and return BUFFER_OVERFLOW with a response truncated to the requested length for larger partial buffers.

This fixes smb2.getinfo.qfs_buffercheck.

Stable-dep-of: 9fa26285ae70 ("ksmbd: keep compound responses on query info errors")

---
 fs/smb/server/smb2pdu.c | 26 +++++++++++++++++++++++++-
 1 file changed, 25 insertions(+), 1 deletion(-)
fs/smb/server/smb2pdu.c +25 -1
index 3cb39dc7b149c..cd533dbdc2b23 100644
@@ -5622,21 +5622,30 @@ int smb2_query_dir(struct ksmbd_work *work)
 /**
  * buffer_check_err() - helper function to check buffer errors
  * @reqOutputBufferLength:	max buffer length expected in command response
+ * @fixed_len:			minimum fixed response length
  * @rsp:		query info response buffer contains output buffer length
  * @rsp_org:		base response buffer pointer in case of chained response
  *
  * Return:	0 on success, otherwise error
  */
 static int buffer_check_err(int reqOutputBufferLength,
+			    unsigned int fixed_len,
 			    struct smb2_query_info_rsp *rsp,
 			    void *rsp_org)
 {
-	if (reqOutputBufferLength < le32_to_cpu(rsp->OutputBufferLength)) {
+	unsigned int output_len = le32_to_cpu(rsp->OutputBufferLength);
+
+	if (reqOutputBufferLength < fixed_len) {
 		pr_err("Invalid Buffer Size Requested\n");
 		rsp->hdr.Status = STATUS_INFO_LENGTH_MISMATCH;
 		*(__be32 *)rsp_org = cpu_to_be32(sizeof(struct smb2_hdr));
 		return -EINVAL;
 	}
+
+	if (reqOutputBufferLength < output_len) {
+		rsp->hdr.Status = STATUS_BUFFER_OVERFLOW;
+		rsp->OutputBufferLength = cpu_to_le32(reqOutputBufferLength);
+	}
 	return 0;
 }
 
@@ -5699,11 +5708,13 @@ static int smb2_get_info_file_pipe(struct ksmbd_session *sess,
 	case FILE_STANDARD_INFORMATION:
 		get_standard_info_pipe(rsp, rsp_org);
 		rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength),
+				      le32_to_cpu(rsp->OutputBufferLength),
 				      rsp, rsp_org);
 		break;
 	case FILE_INTERNAL_INFORMATION:
 		get_internal_info_pipe(rsp, id, rsp_org);
 		rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength),
+				      le32_to_cpu(rsp->OutputBufferLength),
 				      rsp, rsp_org);
 		break;
 	default:
@@ -6524,6 +6535,7 @@ static int smb2_get_info_file(struct ksmbd_work *work,
 	}
 	if (!rc)
 		rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength),
+				      le32_to_cpu(rsp->OutputBufferLength),
 				      rsp, work->response_buf);
 	ksmbd_fd_put(work, fp);
 
@@ -6545,6 +6557,7 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work,
 	struct kstatfs stfs;
 	struct path path;
 	int rc = 0, len;
+	unsigned int fixed_len = 0;
 
 	if (!share->path)
 		return -EIO;
@@ -6579,6 +6592,7 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work,
 			info->DeviceCharacteristics |=
 				cpu_to_le32(FILE_READ_ONLY_DEVICE);
 		rsp->OutputBufferLength = cpu_to_le32(8);
+		fixed_len = 8;
 		break;
 	}
 	case FS_ATTRIBUTE_INFORMATION:
@@ -6631,6 +6645,7 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work,
 		info->FileSystemNameLen = cpu_to_le32(len);
 		sz = sizeof(FILE_SYSTEM_ATTRIBUTE_INFO) + len;
 		rsp->OutputBufferLength = cpu_to_le32(sz);
+		fixed_len = 16;
 		break;
 	}
 	case FS_VOLUME_INFORMATION:
@@ -6658,6 +6673,7 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work,
 		info->SupportsObjects = 0;
 		sz = sizeof(struct filesystem_vol_info) + len;
 		rsp->OutputBufferLength = cpu_to_le32(sz);
+		fixed_len = 24;
 		break;
 	}
 	case FS_SIZE_INFORMATION:
@@ -6670,6 +6686,7 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work,
 		info->SectorsPerAllocationUnit = cpu_to_le32(1);
 		info->BytesPerSector = cpu_to_le32(stfs.f_bsize);
 		rsp->OutputBufferLength = cpu_to_le32(24);
+		fixed_len = 24;
 		break;
 	}
 	case FS_FULL_SIZE_INFORMATION:
@@ -6685,6 +6702,7 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work,
 		info->SectorsPerAllocationUnit = cpu_to_le32(1);
 		info->BytesPerSector = cpu_to_le32(stfs.f_bsize);
 		rsp->OutputBufferLength = cpu_to_le32(32);
+		fixed_len = 32;
 		break;
 	}
 	case FS_OBJECT_ID_INFORMATION:
@@ -6705,6 +6723,7 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work,
 		info->extended_info.rel_date = 0;
 		memcpy(info->extended_info.version_string, "1.1.0", strlen("1.1.0"));
 		rsp->OutputBufferLength = cpu_to_le32(64);
+		fixed_len = 64;
 		break;
 	}
 	case FS_SECTOR_SIZE_INFORMATION:
@@ -6726,6 +6745,7 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work,
 		info->ByteOffsetForSectorAlignment = 0;
 		info->ByteOffsetForPartitionAlignment = 0;
 		rsp->OutputBufferLength = cpu_to_le32(28);
+		fixed_len = 28;
 		break;
 	}
 	case FS_CONTROL_INFORMATION:
@@ -6746,6 +6766,7 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work,
 		info->DefaultQuotaLimit = cpu_to_le64(SMB2_NO_FID);
 		info->Padding = 0;
 		rsp->OutputBufferLength = cpu_to_le32(48);
+		fixed_len = 48;
 		break;
 	}
 	case FS_POSIX_INFORMATION:
@@ -6766,6 +6787,7 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work,
 			info->TotalFileNodes = cpu_to_le64(stfs.f_files);
 			info->FreeFileNodes = cpu_to_le64(stfs.f_ffree);
 			rsp->OutputBufferLength = cpu_to_le32(56);
+			fixed_len = 56;
 		}
 		break;
 	}
@@ -6774,6 +6796,7 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work,
 		return -EOPNOTSUPP;
 	}
 	rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength),
+			      fixed_len,
 			      rsp, work->response_buf);
 	path_put(&path);
 
@@ -6875,6 +6898,7 @@ static int smb2_get_info_sec(struct ksmbd_work *work,
 
 	rsp->OutputBufferLength = cpu_to_le32(secdesclen);
 	rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength),
+			      le32_to_cpu(rsp->OutputBufferLength),
 			      rsp, work->response_buf);
 	if (rc)
 		goto err_out;
Signature · 1 line
2.53.0
author maintainer
#168 lore raw

PATCH 165/438 ksmbd: fix partial file information responses

In reply to Greg Kroah-Hartman

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

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

From: Namjae Jeon <[email protected]>

[ Upstream commit 6b8b79226bc3e0ac3fdd4e91836241af712e8cd1 ]

Variable-length file information handlers use the client output length while constructing the response. FILE_ALL_INFORMATION can consequently return -EINVAL before the common buffer check, while stream information can stop building the complete result too early.

Build the complete response within the available server response buffer and apply the client output length only when selecting the final status and transmitted length. Use the protocol-defined fixed sizes for all, alternate-name, and stream information to distinguish STATUS_INFO_LENGTH_MISMATCH from STATUS_BUFFER_OVERFLOW.

This fixes smb2.getinfo.qfile_buffercheck.

Stable-dep-of: 9fa26285ae70 ("ksmbd: keep compound responses on query info errors")

---
 fs/smb/server/smb2pdu.c | 31 ++++++++++++++++++++-----------
 1 file changed, 20 insertions(+), 11 deletions(-)
fs/smb/server/smb2pdu.c +20 -11
index cd533dbdc2b23..f6b9a0ad922ff 100644
@@ -5997,7 +5997,6 @@ static int get_file_all_info(struct ksmbd_work *work,
 	char *filename;
 	u64 time;
 	int ret, buf_free_len, filename_len;
-	struct smb2_query_info_req *req = ksmbd_req_buf_next(work);
 
 	if (!(fp->daccess & FILE_READ_ATTRIBUTES_LE)) {
 		ksmbd_debug(SMB, "no right to read the attributes : 0x%x\n",
@@ -6010,10 +6009,9 @@ static int get_file_all_info(struct ksmbd_work *work,
 		return PTR_ERR(filename);
 
 	filename_len = strlen(filename);
-	buf_free_len = smb2_calc_max_out_buf_len(work,
+	buf_free_len = smb2_resp_buf_len(work,
 			offsetof(struct smb2_query_info_rsp, Buffer) +
-			offsetof(struct smb2_file_all_info, FileName),
-			le32_to_cpu(req->OutputBufferLength));
+			offsetof(struct smb2_file_all_info, FileName));
 	if (buf_free_len < (filename_len + 1) * 2) {
 		kfree(filename);
 		return -EINVAL;
@@ -6104,7 +6102,6 @@ static int get_file_stream_info(struct ksmbd_work *work,
 	ssize_t xattr_list_len;
 	int nbytes = 0, streamlen, stream_name_len, next, idx = 0;
 	int buf_free_len;
-	struct smb2_query_info_req *req = ksmbd_req_buf_next(work);
 	int ret;
 
 	ret = vfs_getattr(&fp->filp->f_path, &stat, STATX_BASIC_STATS,
@@ -6114,10 +6111,8 @@ static int get_file_stream_info(struct ksmbd_work *work,
 
 	file_info = (struct smb2_file_stream_info *)rsp->Buffer;
 
-	buf_free_len =
-		smb2_calc_max_out_buf_len(work,
-				offsetof(struct smb2_query_info_rsp, Buffer),
-				le32_to_cpu(req->OutputBufferLength));
+	buf_free_len = smb2_resp_buf_len(work,
+			offsetof(struct smb2_query_info_rsp, Buffer));
 	if (buf_free_len < 0)
 		goto out;
 
@@ -6430,6 +6425,7 @@ static int smb2_get_info_file(struct ksmbd_work *work,
 	struct ksmbd_file *fp;
 	int fileinfoclass = 0;
 	int rc = 0;
+	unsigned int fixed_len;
 	unsigned int id = KSMBD_NO_FID, pid = KSMBD_NO_FID;
 
 	if (test_share_config_flag(work->tcon->share_conf,
@@ -6533,10 +6529,23 @@ static int smb2_get_info_file(struct ksmbd_work *work,
 			    fileinfoclass);
 		rc = -EOPNOTSUPP;
 	}
-	if (!rc)
+	if (!rc) {
+		fixed_len = le32_to_cpu(rsp->OutputBufferLength);
+		switch (fileinfoclass) {
+		case FILE_ALL_INFORMATION:
+			fixed_len = FILE_ALL_INFORMATION_SIZE;
+			break;
+		case FILE_ALTERNATE_NAME_INFORMATION:
+			fixed_len = FILE_ALTERNATE_NAME_INFORMATION_SIZE;
+			break;
+		case FILE_STREAM_INFORMATION:
+			fixed_len = FILE_STREAM_INFORMATION_SIZE;
+			break;
+		}
 		rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength),
-				      le32_to_cpu(rsp->OutputBufferLength),
+				      fixed_len,
 				      rsp, work->response_buf);
+	}
 	ksmbd_fd_put(work, fp);
 
 iov_pin_out:
Signature · 1 line
2.53.0
author maintainer
#169 lore raw

PATCH 166/438 ksmbd: keep compound responses on query info errors

In reply to Greg Kroah-Hartman

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

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

From: Namjae Jeon <[email protected]>

[ Upstream commit 9fa26285ae70ac2d3d1b47459a6b4463ab053e1c ]

Do not reset the RFC1002 length of the complete response when a query info buffer is too small. The current command will add its error response through ksmbd_iov_pin_rsp(), while resetting the base length can truncate earlier responses in a compound request.

This lets ksmbd return the earlier responses and the query-info error response together. Remove the now-unused rsp_org parameter from the pipe query-info helpers.

Fixes e2b76ab8b5c9 ("ksmbd: add support for read compound")
---
 fs/smb/server/smb2pdu.c | 31 ++++++++++++-------------------
 1 file changed, 12 insertions(+), 19 deletions(-)
fs/smb/server/smb2pdu.c +12 -19
index f6b9a0ad922ff..5312ae3fc5023 100644
@@ -5624,21 +5624,18 @@ int smb2_query_dir(struct ksmbd_work *work)
  * @reqOutputBufferLength:	max buffer length expected in command response
  * @fixed_len:			minimum fixed response length
  * @rsp:		query info response buffer contains output buffer length
- * @rsp_org:		base response buffer pointer in case of chained response
  *
  * Return:	0 on success, otherwise error
  */
 static int buffer_check_err(int reqOutputBufferLength,
 			    unsigned int fixed_len,
-			    struct smb2_query_info_rsp *rsp,
-			    void *rsp_org)
+			    struct smb2_query_info_rsp *rsp)
 {
 	unsigned int output_len = le32_to_cpu(rsp->OutputBufferLength);
 
 	if (reqOutputBufferLength < fixed_len) {
 		pr_err("Invalid Buffer Size Requested\n");
 		rsp->hdr.Status = STATUS_INFO_LENGTH_MISMATCH;
-		*(__be32 *)rsp_org = cpu_to_be32(sizeof(struct smb2_hdr));
 		return -EINVAL;
 	}
 
@@ -5649,8 +5646,7 @@ static int buffer_check_err(int reqOutputBufferLength,
 	return 0;
 }
 
-static void get_standard_info_pipe(struct smb2_query_info_rsp *rsp,
-				   void *rsp_org)
+static void get_standard_info_pipe(struct smb2_query_info_rsp *rsp)
 {
 	struct smb2_file_standard_info *sinfo;
 
@@ -5665,8 +5661,7 @@ static void get_standard_info_pipe(struct smb2_query_info_rsp *rsp,
 		cpu_to_le32(sizeof(struct smb2_file_standard_info));
 }
 
-static void get_internal_info_pipe(struct smb2_query_info_rsp *rsp, u64 num,
-				   void *rsp_org)
+static void get_internal_info_pipe(struct smb2_query_info_rsp *rsp, u64 num)
 {
 	struct smb2_file_internal_info *file_info;
 
@@ -5680,8 +5675,7 @@ static void get_internal_info_pipe(struct smb2_query_info_rsp *rsp, u64 num,
 
 static int smb2_get_info_file_pipe(struct ksmbd_session *sess,
 				   struct smb2_query_info_req *req,
-				   struct smb2_query_info_rsp *rsp,
-				   void *rsp_org)
+				   struct smb2_query_info_rsp *rsp)
 {
 	u64 id;
 	int rc;
@@ -5706,16 +5700,16 @@ static int smb2_get_info_file_pipe(struct ksmbd_session *sess,
 
 	switch (req->FileInfoClass) {
 	case FILE_STANDARD_INFORMATION:
-		get_standard_info_pipe(rsp, rsp_org);
+		get_standard_info_pipe(rsp);
 		rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength),
 				      le32_to_cpu(rsp->OutputBufferLength),
-				      rsp, rsp_org);
+				      rsp);
 		break;
 	case FILE_INTERNAL_INFORMATION:
-		get_internal_info_pipe(rsp, id, rsp_org);
+		get_internal_info_pipe(rsp, id);
 		rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength),
 				      le32_to_cpu(rsp->OutputBufferLength),
-				      rsp, rsp_org);
+				      rsp);
 		break;
 	default:
 		ksmbd_debug(SMB, "smb2_info_file_pipe for %u not supported\n",
@@ -6431,8 +6425,7 @@ static int smb2_get_info_file(struct ksmbd_work *work,
 	if (test_share_config_flag(work->tcon->share_conf,
 				   KSMBD_SHARE_FLAG_PIPE)) {
 		/* smb2 info file called for pipe */
-		rc = smb2_get_info_file_pipe(work->sess, req, rsp,
-					       work->response_buf);
+		rc = smb2_get_info_file_pipe(work->sess, req, rsp);
 		goto iov_pin_out;
 	}
 
@@ -6544,7 +6537,7 @@ static int smb2_get_info_file(struct ksmbd_work *work,
 		}
 		rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength),
 				      fixed_len,
-				      rsp, work->response_buf);
+				      rsp);
 	}
 	ksmbd_fd_put(work, fp);
 
@@ -6806,7 +6799,7 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work,
 	}
 	rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength),
 			      fixed_len,
-			      rsp, work->response_buf);
+			      rsp);
 	path_put(&path);
 
 	if (!rc)
@@ -6908,7 +6901,7 @@ static int smb2_get_info_sec(struct ksmbd_work *work,
 	rsp->OutputBufferLength = cpu_to_le32(secdesclen);
 	rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength),
 			      le32_to_cpu(rsp->OutputBufferLength),
-			      rsp, work->response_buf);
+			      rsp);
 	if (rc)
 		goto err_out;
 
Signature · 1 line
2.53.0
author maintainer
#170 lore raw

PATCH 167/438 dmaengine: mmp_pdma: fix wrong sg length in mmp_pdma_prep_slave_sg()

In reply to Greg Kroah-Hartman

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

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

From: Baineng Shou <[email protected]>

[ Upstream commit 075bc7b1d3dde5ed43fbaabbc1a69f09b7fc3a47 ]

In mmp_pdma_prep_slave_sg(), for_each_sg() iterates the scatterlist putting each entry into 'sg', but the entry length is read from 'sgl' (the list head) instead of 'sg' (the current entry):

    for_each_sg(sgl, sg, sg_len, i) {
        addr = sg_dma_address(sg);
        avail = sg_dma_len(sgl);   /* should be 'sg' */

Consequently 'avail' is always the length of the first entry. For multi-sg lists this causes out-of-bounds reads when a later entry is shorter than the first, and silent data loss when it is longer. Single-sg or uniformly-sized lists happen to mask the issue.

---
 drivers/dma/mmp_pdma.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/dma/mmp_pdma.c +1 -1
index 78e3e07e681df..ed520737882bc 100644
@@ -712,7 +712,7 @@ mmp_pdma_prep_slave_sg(struct dma_chan *dchan, struct scatterlist *sgl,
 
 	for_each_sg(sgl, sg, sg_len, i) {
 		addr = sg_dma_address(sg);
-		avail = sg_dma_len(sgl);
+		avail = sg_dma_len(sg);
 
 		do {
 			len = min_t(size_t, avail, PDMA_MAX_DESC_BYTES);
Signature · 1 line
2.53.0
author maintainer
#171 lore raw

PATCH 168/438 Bluetooth: hci_core: Fix queuing tx_work after workqueue is drained

In reply to Greg Kroah-Hartman

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

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

From: ThangNN99 <[email protected]>

[ Upstream commit 6610c6fe4b8936c232048e6049bf77c70a6f759c ]

hci_send_acl(), hci_send_sco() and hci_send_iso() queue hdev->tx_work unconditionally. They can run from the L2CAP/SCO/ISO socket send path while hci_dev_close_sync() is draining hdev->workqueue (HCIDEVDOWN racing with a socket write). Since that queue_work() is not chained work from the tx_work worker itself, __queue_work() sees the queue marked __WQ_DRAINING, warns "cannot queue %ps on wq %s", and drops the work:

  WARNING: CPU: 1 PID: 5985 at kernel/workqueue.c:2352 __queue_work
  Call Trace:
   queue_work_on
   l2cap_chan_send
   l2cap_sock_sendmsg
   ...

hci_dev_close_sync() already sets HCI_CMD_DRAIN_WORKQUEUE before draining, but only hci_cmd_work() and handle_cmd_cnt_and_timer() check it before queuing. Route the tx_work producers through the same guard via a shared hci_sched_tx() helper.

Fixes 525daaea459f ("Bluetooth: hci_sync: Set HCI_CMD_DRAIN_WORKQUEUE during device close")
Reported-by [email protected]
---
 net/bluetooth/hci_core.c | 17 ++++++++++++++---
 1 file changed, 14 insertions(+), 3 deletions(-)
net/bluetooth/hci_core.c +14 -3
index f747492da5f66..86a9677ed7025 100644
@@ -3234,6 +3234,17 @@ static void hci_queue_acl(struct hci_chan *chan, struct sk_buff_head *queue,
 	bt_dev_dbg(hdev, "chan %p queued %d", chan, skb_queue_len(queue));
 }
 
+/* Queue hdev->tx_work, unless hdev->workqueue is being drained by
+ * hci_dev_close_sync(), which would otherwise WARN and drop the work.
+ */
+static void hci_sched_tx(struct hci_dev *hdev)
+{
+	rcu_read_lock();
+	if (!hci_dev_test_flag(hdev, HCI_CMD_DRAIN_WORKQUEUE))
+		queue_work(hdev->workqueue, &hdev->tx_work);
+	rcu_read_unlock();
+}
+
 void hci_send_acl(struct hci_chan *chan, struct sk_buff *skb, __u16 flags)
 {
 	struct hci_dev *hdev = chan->conn->hdev;
@@ -3242,7 +3253,7 @@ void hci_send_acl(struct hci_chan *chan, struct sk_buff *skb, __u16 flags)
 
 	hci_queue_acl(chan, &chan->data_q, skb, flags);
 
-	queue_work(hdev->workqueue, &hdev->tx_work);
+	hci_sched_tx(hdev);
 }
 
 /* Send SCO data */
@@ -3267,7 +3278,7 @@ void hci_send_sco(struct hci_conn *conn, struct sk_buff *skb)
 	bt_dev_dbg(hdev, "hcon %p queued %d", conn,
 		   skb_queue_len(&conn->data_q));
 
-	queue_work(hdev->workqueue, &hdev->tx_work);
+	hci_sched_tx(hdev);
 }
 
 /* Send ISO data */
@@ -3338,7 +3349,7 @@ void hci_send_iso(struct hci_conn *conn, struct sk_buff *skb)
 
 	hci_queue_iso(conn, &conn->data_q, skb);
 
-	queue_work(hdev->workqueue, &hdev->tx_work);
+	hci_sched_tx(hdev);
 }
 
 /* ---- HCI TX task (outgoing data) ---- */
Signature · 1 line
2.53.0
author maintainer
#172 lore raw

PATCH 169/438 Bluetooth: btintel_pcie: validate TX skb length in send_sync

In reply to Greg Kroah-Hartman

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

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

From: Chandrashekar Devegowda <[email protected]>

[ Upstream commit 4b837ebd0ea21ae5cc26f02dc042edc6fe7b46b9 ]

btintel_pcie_prepare_tx() copies skb->len bytes into a fixed BTINTEL_PCIE_BUFFER_SIZE (4096) DMA slot via an unchecked memcpy. Oversized packets are currently rejected only in btintel_pcie_send_frame(); any future caller of btintel_pcie_send_sync() would silently overflow the DMA buffer.

Add the bounds check in btintel_pcie_send_sync() itself, right before skb_push() and the DMA copy.

Assisted-by Copilot:claude-sonnet-5 code-review code-generation
Fixes 6e65a09f9275 ("Bluetooth: btintel_pcie: Add *setup* function to download firmware")
---
 drivers/bluetooth/btintel_pcie.c | 6 ++++++
 1 file changed, 6 insertions(+)
drivers/bluetooth/btintel_pcie.c +6 -0
index c64e96fe88976..aad466e453013 100644
@@ -404,6 +404,12 @@ static int btintel_pcie_send_sync(struct btintel_pcie_data *data,
 	if (tfd_index > txq->count)
 		return -ERANGE;
 
+	if (skb->len > BTINTEL_PCIE_BUFFER_SIZE - BTINTEL_PCIE_HCI_TYPE_LEN) {
+		bt_dev_err(hdev, "TX skb too large (%u > %u)", skb->len,
+			   BTINTEL_PCIE_BUFFER_SIZE - BTINTEL_PCIE_HCI_TYPE_LEN);
+		return -EMSGSIZE;
+	}
+
 	/* Firmware raises alive interrupt on HCI_OP_RESET or
 	 * BTINTEL_HCI_OP_RESET
 	 */
Signature · 1 line
2.53.0
author maintainer
#173 lore raw

PATCH 170/438 Bluetooth: coredump: Quiesce dump work on unregister

In reply to Greg Kroah-Hartman

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

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

From: Weiming Shi <[email protected]>

[ Upstream commit d236517c264e41dc09833c708ef23bccb7a91219 ]

hci_devcd_handle_pkt_init() arms dump_timeout and coredump producers queue dump_rx without holding an hdev reference. Unregister leaves both works live, so disconnecting during an active dump lets them access hdev after hci_release_dev() frees it.

Shut down coredump processing during unregister. Close the producer gate under dump_q.lock before disabling both works, then free the active buffer and queued packets under hci_dev_lock. Serializing the gate with enqueue prevents controller-specific workers from adding packets after the final purge.

---
 include/net/bluetooth/coredump.h |  2 +
 net/bluetooth/coredump.c         | 65 +++++++++++++++++++++-----------
 net/bluetooth/hci_core.c         |  1 +
 3 files changed, 47 insertions(+), 21 deletions(-)
include/net/bluetooth/coredump.h +2 -0
index 72f51b587a046..00c12c7ac042f 100644
@@ -61,6 +61,7 @@ struct hci_devcoredump {
 #ifdef CONFIG_DEV_COREDUMP
 
 void hci_devcd_reset(struct hci_dev *hdev);
+void hci_devcd_shutdown(struct hci_dev *hdev);
 void hci_devcd_rx(struct work_struct *work);
 void hci_devcd_timeout(struct work_struct *work);
 
@@ -75,6 +76,7 @@ int hci_devcd_abort(struct hci_dev *hdev);
 #else
 
 static inline void hci_devcd_reset(struct hci_dev *hdev) {}
+static inline void hci_devcd_shutdown(struct hci_dev *hdev) {}
 static inline void hci_devcd_rx(struct work_struct *work) {}
 static inline void hci_devcd_timeout(struct work_struct *work) {}
 
net/bluetooth/coredump.c +44 -21

106 lines not on this page · raw message

net/bluetooth/hci_core.c +1 -0
index 86a9677ed7025..3ea5ea7267d9c 100644
@@ -2670,6 +2670,7 @@ void hci_unregister_dev(struct hci_dev *hdev)
 	disable_work_sync(&hdev->error_reset);
 	disable_delayed_work_sync(&hdev->cmd_timer);
 	disable_delayed_work_sync(&hdev->ncmd_timer);
+	hci_devcd_shutdown(hdev);
 
 	hci_cmd_sync_clear(hdev);
 
Signature · 1 line
2.53.0
author maintainer
#174 lore raw

PATCH 171/438 Bluetooth: put the peers on-air address on air when we cannot resolve

In reply to Greg Kroah-Hartman

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

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

From: Radek Podgorny <[email protected]>

[ Upstream commit 4914c499896121ae8b9d5b90f0abc5c8287ff396 ]

An identity address only reaches a peer that is advertising an RPA if the controller resolves it on our behalf. Where it cannot, the host has to put the peer's on-air address on air itself.

hci_connect_le() still swaps the caller's identity address for the peer's cached RPA before creating the connection, but __hci_conn_add() resolves the RPA back to the identity address when it stores it, so the identity is what goes out. Storing the identity is right when the controller translates it on the way to the radio; without LL Privacy, or with this peer absent from the resolving list, nothing does.

A peer advertising an RPA cannot answer its identity address, so the attempt burns a full create-connection timeout. That is not merely a slow
connect: a controller without extended scanning cannot scan while it is initiating, so every dead attempt also takes the scanner off the air for the whole timeout.

Measured on a CYW43438, which reports neither LL Privacy nor extended advertising (LE features 3f 00 00 08 00 00 00 00), against a peer advertising a resolvable private address the host holds the IRK for, with the connection requested on the peer's identity address:

before: LE Create Connection to the identity address, public type

          1.61s -> 22.07s, then LE Create Connection Cancel
          LE Connection Complete: Unknown Connection Identifier (0x02)

after: LE Create Connection to the peer's RPA, random type
LE Connection Complete: Success

Advertising reports reaching the host per second, same window, same five unrelated devices on the adapter:

before 1s:2 [nothing from 2s through 21s] 22s:5 23s:3
after 0s:11 1s:5 2s:2 3s:5 4s:3 5s:4 ... 21s:2 22s:1 23s:2

One dead connect costs twenty seconds of scanning for every device on the adapter, not just the one being dialled.

Keep the RPA in conn->dst unless the controller will translate the identity address: address resolution enabled and the peer's identity actually programmed into the resolving list. Testing ll_privacy_capable() alone would not be enough: it reports the feature bit, not whether resolution is switched on and not whether this peer is in the list. Resolution is cleared with the other volatile flags on power-off and switched off again while suspend pauses scanning, and a peer's IRK is only programmed along the accept list path, so a direct-connect target, a peer without HCI_CONN_FLAG_ADDRESS_RESOLUTION, and one that did not fit in a full list are all absent from it.

With the peer programmed, the identity address stays in conn->dst and the controller translates it: measured on an Intel controller, the host dials the identity and LE Enhanced Connection Complete reports Resolved Public with the peer's RPA in the separate peer resolvable private address field. With the peer absent from the list the same setup dials the RPA itself.

Everything downstream already copes with an RPA in conn->dst: it is what every outgoing LE connection stored before 14b06c3a88f7, the connection complete event names the address that was dialled, and le_conn_complete_evt() resolves it back to the identity once the link is up. ISO links keep the unconditional conversion: they are created from an existing ACL or a periodic sync and never dial this address themselves.

Keeping the RPA is only right while the peer is still using it, which is why the preceding patch drops the cached RPA as soon as the peer is seen advertising its identity address. Without that, a peer that turns privacy off would be dialled on the address it abandoned rather than the one it is answering on.

Fixes 14b06c3a88f7 ("Bluetooth: HCI: Always use the identity address when initializing a connection")
Assisted-by Claude:claude-opus-5
Assisted-by Claude:claude-fable-5
---
 net/bluetooth/hci_conn.c | 13 +++++++++++++
 1 file changed, 13 insertions(+)
net/bluetooth/hci_conn.c +13 -0
index 8de98af2fb581..c9466cb2c7c00 100644
@@ -1023,6 +1023,19 @@ static struct hci_conn *__hci_conn_add(struct hci_dev *hdev, int type,
 		if (!hdev->le_mtu && hdev->acl_mtu < HCI_MIN_LE_MTU)
 			return ERR_PTR(-ECONNREFUSED);
 		irk = hci_get_irk(hdev, dst, dst_type);
+		/* An identity address only reaches a peer advertising an RPA
+		 * if the controller translates it. Unless address resolution
+		 * is enabled and this peer is programmed into the resolving
+		 * list, keep the RPA the peer is on air with;
+		 * le_conn_complete_evt() resolves it back once the link is
+		 * up.
+		 */
+		if (irk &&
+		    (!hci_dev_test_flag(hdev, HCI_LL_RPA_RESOLUTION) ||
+		     !hci_bdaddr_list_lookup_with_irk(&hdev->le_resolv_list,
+						      &irk->bdaddr,
+						      irk->addr_type)))
+			irk = NULL;
 		break;
 	case SCO_LINK:
 	case ESCO_LINK:
Signature · 1 line
2.53.0
author maintainer
#175 lore raw

PATCH 172/438 Bluetooth: hci_qca: Do not write to the serial port after it is closed

In reply to Greg Kroah-Hartman

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

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

From: Ibrahim Abdelkader <[email protected]>

[ Upstream commit 4e93c65f87825e1e012bce56615320aeb123815d ]

hci_uart_close() closes the serdev port if HCI_QUIRK_NON_PERSISTENT_SETUP is set (for example, for the WCN399x family). A failed hci_dev_open_sync() following a successful qca_setup() calls hdev->close() but not hdev->shutdown(), so the port is closed while power->vregs_on is left true. qca_serdev_remove() then passes its power->vregs_on test and calls qca_power_off(), which writes to the closed port unconditionally.

Seen on a WCN3988 by unbinding the driver after a controller failure. The trace below is from a 7.0.0 based kernel, where qca_power_off() was still named qca_power_shutdown():

  Unable to handle kernel NULL pointer dereference at virtual address
  0000000000000038
  Call trace:
   tty_set_termios+0x50/0x238 (P)
   ttyport_set_baudrate+0x84/0xc0
   serdev_device_set_baudrate+0x24/0x40
   qca_power_shutdown+0x158/0x1fc [hci_uart]
   qca_serdev_remove+0x54/0x68 [hci_uart]
   serdev_drv_remove+0x1c/0x2c
   device_remove+0x4c/0x80
   device_release_driver_internal+0x1cc/0x224
   device_driver_detach+0x18/0x24
   unbind_store+0xb4/0xc0

Check HCI_UART_PROTO_READY, which hci_uart_close() clears in the same place it closes the port, before writing to it. The regulator disable is left unconditional so the controller is still powered down.

The dangling serport->tty that turns this into a use-after-free is addressed in a separate patch.

Fixes fa9ad876b8e0 ("Bluetooth: hci_qca: Add support for Qualcomm Bluetooth chip wcn3990")
---
 drivers/bluetooth/hci_qca.c | 14 ++++++++++----
 1 file changed, 10 insertions(+), 4 deletions(-)
drivers/bluetooth/hci_qca.c +10 -4
index 1222f97800f4a..875093e61c681 100644
@@ -2228,8 +2228,8 @@ static void qca_power_off(struct hci_uart *hu)
 	bool sw_ctrl_state;
 	struct qca_power *power;
 
-	/* From this point we go into power off state. But serial port is
-	 * still open, stop queueing the IBS data and flush all the buffered
+	/* From this point we go into power off state. But serial port may
+	 * still be open, stop queueing the IBS data and flush all the buffered
 	 * data in skb's.
 	 */
 	spin_lock_irqsave(&qca->hci_ibs_lock, flags);
@@ -2251,8 +2251,14 @@ static void qca_power_off(struct hci_uart *hu)
 	case QCA_WCN3990:
 	case QCA_WCN3991:
 	case QCA_WCN3998:
-		host_set_baudrate(hu, 2400);
-		qca_send_power_pulse(hu, false);
+		/* Both of these write to the serial port which may have
+		 * already been closed by hci_uart_close(), which closes
+		 * the port if HCI_QUIRK_NON_PERSISTENT_SETUP is set.
+		 */
+		if (test_bit(HCI_UART_PROTO_READY, &hu->flags)) {
+			host_set_baudrate(hu, 2400);
+			qca_send_power_pulse(hu, false);
+		}
 		break;
 	default:
 		break;
Signature · 1 line
2.53.0
author maintainer
#176 lore raw

PATCH 173/438 Bluetooth: ISO: Fix parent socket leak in iso_conn_ready()

In reply to Greg Kroah-Hartman

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

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

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

[ Upstream commit ca18ee413a7cb6f09885778039225e58bae0d607 ]

iso_get_sock() returns the parent socket with a reference held, which is dropped by sock_put() once the child socket has been set up. The error path taken when iso_sock_alloc() fails only calls release_sock() and returns, leaking the reference and thus the parent socket itself.

Drop the reference on that path as well.

Fixes fa224d0c094a ("Bluetooth: ISO: Reassociate a socket with an active BIS")
---
 net/bluetooth/iso.c | 1 +
 1 file changed, 1 insertion(+)
net/bluetooth/iso.c +1 -0
index 75bfd5938b2ea..4de332b8901f2 100644
@@ -2289,6 +2289,7 @@ static void iso_conn_ready(struct iso_conn *conn)
 				    BTPROTO_ISO, GFP_ATOMIC, 0);
 		if (!sk) {
 			release_sock(parent);
+			sock_put(parent);
 			return;
 		}
 
Signature · 1 line
2.53.0
author maintainer
#177 lore raw

PATCH 174/438 Bluetooth: ISO: set BT_LISTEN before requesting a BIG sync

In reply to Greg Kroah-Hartman

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

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

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

[ Upstream commit 296e7f3c5071cc02dc22e1566e759179fa1792ae ]

A BIS connection is matched to its parent socket by looking for a socket in BT_LISTEN state with the same BIG handle:

iso_conn_ready()

    if (test_bit(HCI_CONN_BIG_SYNC, &hcon->flags))
            parent = iso_get_sock(hdev, &hcon->src, &hcon->dst,
                                  BT_LISTEN, iso_match_big_hcon, hcon);

The socket was only moved to BT_LISTEN after iso_conn_big_sync() returned, while the LE BIG Create Sync command has already been queued by then. If the BIG sync is established before the state is updated, which is easy to hit with an emulated controller as the command may complete in a few hundred microseconds, no parent is found and the BIS connections are never notified to the listening socket.

The user space is then left waiting for connections that never arrive, e.g. bluetoothd never completes a MediaTransport1.Acquire of a Broadcast Sink transport.

Move the socket to BT_LISTEN before requesting the BIG sync, so the state is visible by the time the command is queued, and restore the previous state if the request could not be started. Since the socket is briefly visible as a listening socket, child sockets may have been queued in the meantime, so drain the accept queue before restoring the
state: the cleanup paths of BT_CONNECT2/BT_CONNECTED don't do it and the children would be left with a dangling parent pointer.

Fixes fbdc4bc47268 ("Bluetooth: ISO: Use defer setup to separate PA sync and BIG sync")
---
 net/bluetooth/iso.c | 52 +++++++++++++++++++++++++++++++++++----------
 1 file changed, 41 insertions(+), 11 deletions(-)
net/bluetooth/iso.c +41 -11
index 4de332b8901f2..eb99653f33f91 100644
@@ -819,19 +819,24 @@ static void iso_sock_destruct(struct sock *sk)
 	skb_queue_purge(&sk->sk_error_queue);
 }
 
-static void iso_sock_cleanup_listen(struct sock *parent)
+/* Close not yet accepted channels */
+static void iso_sock_flush_accept_q(struct sock *parent)
 {
 	struct sock *sk;
 
-	BT_DBG("parent %p", parent);
-
-	/* Close not yet accepted channels */
 	while ((sk = bt_accept_dequeue(parent, NULL))) {
 		iso_sock_close(sk);
 		iso_sock_kill(sk);
 		/* Drop the reference handed back by bt_accept_dequeue(). */
 		sock_put(sk);
 	}
+}
+
+static void iso_sock_cleanup_listen(struct sock *parent)
+{
+	BT_DBG("parent %p", parent);
+
+	iso_sock_flush_accept_q(parent);
 
 	/* If listening socket has a hcon, properly disconnect it */
 	if (iso_pi(parent)->conn && iso_pi(parent)->conn->hcon) {
@@ -1737,6 +1742,13 @@ static int iso_sock_recvmsg(struct socket *sock, struct msghdr *msg,
 		switch (sk->sk_state) {
 		case BT_CONNECT2:
 			if (test_bit(BT_SK_PA_SYNC, &pi->flags)) {
+				/* Move to BT_LISTEN before requesting the BIG
+				 * sync: the BIS connections are matched to a
+				 * parent socket in BT_LISTEN state, and they
+				 * may be notified before the request returns.
+				 */
+				sk->sk_state = BT_LISTEN;
+
 				release_sock(sk);
 				err = iso_conn_big_sync(sk);
 				lock_sock(sk);
@@ -1745,12 +1757,20 @@ static int iso_sock_recvmsg(struct socket *sock, struct msghdr *msg,
 				 * connection may have been torn down
 				 * meanwhile and iso_chan_del() may have
 				 * already moved the socket to BT_CLOSED.
-				 * Only move on to BT_LISTEN if the BIG sync
-				 * was actually started and nothing else has
-				 * changed the state.
+				 * Only move back if the BIG sync could not be
+				 * started and nothing else has changed the
+				 * state.
 				 */
-				if (!err && sk->sk_state == BT_CONNECT2)
-					sk->sk_state = BT_LISTEN;
+				if (err && sk->sk_state == BT_LISTEN) {
+					/* Discard any child socket that may
+					 * have been queued while the socket
+					 * was in BT_LISTEN, as the cleanup of
+					 * BT_CONNECT2 doesn't drain the
+					 * accept queue.
+					 */
+					iso_sock_flush_accept_q(sk);
+					sk->sk_state = BT_CONNECT2;
+				}
 			} else {
 				iso_conn_defer_accept(pi->conn->hcon);
 				sk->sk_state = BT_CONFIG;
@@ -1760,12 +1780,22 @@ static int iso_sock_recvmsg(struct socket *sock, struct msghdr *msg,
 			break;
 		case BT_CONNECTED:
 			if (test_bit(BT_SK_PA_SYNC, &iso_pi(sk)->flags)) {
+				/* As above, the BIS connections may be
+				 * notified before the request returns.
+				 */
+				sk->sk_state = BT_LISTEN;
+
 				release_sock(sk);
 				err = iso_conn_big_sync(sk);
 				lock_sock(sk);
 
-				if (!err && sk->sk_state == BT_CONNECTED)
-					sk->sk_state = BT_LISTEN;
+				if (err && sk->sk_state == BT_LISTEN) {
+					/* As above, don't leave any child
+					 * socket behind in the accept queue.
+					 */
+					iso_sock_flush_accept_q(sk);
+					sk->sk_state = BT_CONNECTED;
+				}
 				early_ret = true;
 			}
 
Signature · 1 line
2.53.0
author maintainer
#178 lore raw

PATCH 175/438 Bluetooth: btmtk: fix wrong status for short WMT FUNC_CTRL events

In reply to Greg Kroah-Hartman

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

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

From: Chris Lu <[email protected]>

[ Upstream commit 78b6abd6c7a7591aacdae657f813214dae4fcd3b ]

A too-short BTMTK_WMT_FUNC_CTRL event (WMT header only, no trailing 2-byte status word) is always treated as BTMTK_WMT_ON_UNDONE. This short form is how firmware acks a plain enable/disable request, and the actual result is carried in the header's own flag byte (0 = success), not a separate status word. Decode it from there instead of assuming failure.

Verified setup on MT7920, MT7921, MT7922 and MT7925: no regression.

Fixes e3ac0d9f1a20 ("Bluetooth: btmtk: accept too short WMT FUNC_CTRL events")
Assisted-by Claude:claude-opus-5
---
 drivers/bluetooth/btmtk.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)
drivers/bluetooth/btmtk.c +6 -1
index 26d525acd6590..7ea8bcd8a7eca 100644
@@ -721,7 +721,12 @@ static int btmtk_usb_hci_wmt_sync(struct hci_dev *hdev,
 	case BTMTK_WMT_FUNC_CTRL:
 		if (!skb_pull_data(data->evt_skb,
 				   sizeof(wmt_evt_funcc->status))) {
-			status = BTMTK_WMT_ON_UNDONE;
+			/* A plain enable/disable request is acked with just
+			 * the WMT header and no trailing status word; the
+			 * result is carried in the header's own flag byte.
+			 */
+			status = wmt_evt->whdr.flag ? BTMTK_WMT_ON_UNDONE :
+						       BTMTK_WMT_ON_DONE;
 			break;
 		}
 
Signature · 1 line
2.53.0
author maintainer
#179 lore raw

PATCH 176/438 Bluetooth: btmtksdio, btmtkuart: validate WMT event length before struct access

In reply to Greg Kroah-Hartman

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

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

From: Chris Lu <[email protected]>

[ Upstream commit 8879e3e0a84a86954c855caceead4867e74a9a27 ]

btmtksdio.c and btmtkuart.c cast a received WMT event straight to struct btmtk_hci_wmt_evt and read its op/flag fields without checking the event is long enough to contain them, unlike btmtk.c. The FUNC_CTRL case then further casts to struct btmtk_hci_wmt_evt_funcc and reads its 2-byte status field, again without a length check. Firmware that sends a short or malformed WMT event makes both drivers read past the end of the received SKB.

Mirror btmtk.c: validate the base WMT header with skb_pull_data() before touching any of its fields, and when a FUNC_CTRL event turns out to be the short, header-only form (a plain enable/disable ack with no status word), decode the result from the header's own flag byte instead (0 = success, otherwise failure).

Verified setup on MT7920, MT7921, MT7922 and MT7925: no regression.

Fixes 9aebfd4a2200 ("Bluetooth: mediatek: add support for MediaTek MT7663S and MT7668S SDIO devices")
Fixes e0b67035a90b ("Bluetooth: mediatek: update the common setup between MT7622 and other devices")
Assisted-by Claude:claude-opus-5
---
 drivers/bluetooth/btmtksdio.c | 20 +++++++++++++++++++-
 drivers/bluetooth/btmtkuart.c | 20 +++++++++++++++++++-
 2 files changed, 38 insertions(+), 2 deletions(-)
drivers/bluetooth/btmtksdio.c +19 -1
index 94aa60d9cc207..7fab678925d19 100644
@@ -217,7 +217,14 @@ static int mtk_hci_wmt_sync(struct hci_dev *hdev,
 	}
 
 	/* Parse and handle the return WMT event */
-	wmt_evt = (struct btmtk_hci_wmt_evt *)bdev->evt_skb->data;
+	wmt_evt = skb_pull_data(bdev->evt_skb, sizeof(*wmt_evt));
+	if (!wmt_evt) {
+		bt_dev_err(hdev, "WMT event too short (%u bytes)",
+			   bdev->evt_skb->len);
+		err = -EINVAL;
+		goto err_free_skb;
+	}
+
 	if (wmt_evt->whdr.op != hdr->op) {
 		bt_dev_err(hdev, "Wrong op received %d expected %d",
 			   wmt_evt->whdr.op, hdr->op);
@@ -233,6 +240,17 @@ static int mtk_hci_wmt_sync(struct hci_dev *hdev,
 			status = BTMTK_WMT_PATCH_DONE;
 		break;
 	case BTMTK_WMT_FUNC_CTRL:
+		if (!skb_pull_data(bdev->evt_skb,
+				   sizeof(wmt_evt_funcc->status))) {
+			/* A plain enable/disable request is acked with just
+			 * the WMT header and no trailing status word; the
+			 * result is carried in the header's own flag byte.
+			 */
+			status = wmt_evt->whdr.flag ? BTMTK_WMT_ON_UNDONE :
+						       BTMTK_WMT_ON_DONE;
+			break;
+		}
+
 		wmt_evt_funcc = (struct btmtk_hci_wmt_evt_funcc *)wmt_evt;
 		if (be16_to_cpu(wmt_evt_funcc->status) == 0x404)
 			status = BTMTK_WMT_ON_DONE;
drivers/bluetooth/btmtkuart.c +19 -1
index 27aa48ff3ac2c..4af6fbbbd302a 100644
@@ -151,7 +151,14 @@ static int mtk_hci_wmt_sync(struct hci_dev *hdev,
 	}
 
 	/* Parse and handle the return WMT event */
-	wmt_evt = (struct btmtk_hci_wmt_evt *)bdev->evt_skb->data;
+	wmt_evt = skb_pull_data(bdev->evt_skb, sizeof(*wmt_evt));
+	if (!wmt_evt) {
+		bt_dev_err(hdev, "WMT event too short (%u bytes)",
+			   bdev->evt_skb->len);
+		err = -EINVAL;
+		goto err_free_wc;
+	}
+
 	if (wmt_evt->whdr.op != hdr->op) {
 		bt_dev_err(hdev, "Wrong op received %d expected %d",
 			   wmt_evt->whdr.op, hdr->op);
@@ -167,6 +174,17 @@ static int mtk_hci_wmt_sync(struct hci_dev *hdev,
 			status = BTMTK_WMT_PATCH_DONE;
 		break;
 	case BTMTK_WMT_FUNC_CTRL:
+		if (!skb_pull_data(bdev->evt_skb,
+				   sizeof(wmt_evt_funcc->status))) {
+			/* A plain enable/disable request is acked with just
+			 * the WMT header and no trailing status word; the
+			 * result is carried in the header's own flag byte.
+			 */
+			status = wmt_evt->whdr.flag ? BTMTK_WMT_ON_UNDONE :
+						       BTMTK_WMT_ON_DONE;
+			break;
+		}
+
 		wmt_evt_funcc = (struct btmtk_hci_wmt_evt_funcc *)wmt_evt;
 		if (be16_to_cpu(wmt_evt_funcc->status) == 0x404)
 			status = BTMTK_WMT_ON_DONE;
Signature · 1 line
2.53.0
author maintainer
#180 lore raw

PATCH 177/438 Bluetooth: btmtksdio: Fix PM runtime reference leak in shutdown

In reply to Greg Kroah-Hartman

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

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

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

[ Upstream commit 7b60ee5f46f2ee329de661f7c68b6818d8136220 ]

In btmtksdio_shutdown(), pm_runtime_get_sync() is called at the beginning of the function. However, if sending the WMT function control command fails later, the driver returns early.

It bypasses the corresponding pm_runtime_put_noidle() and pm_runtime_disable() calls, leaking the PM usage counter and leaving PM runtime enabled indefinitely.

Fall through to execute the PM runtime cleanup block even if WMT errors.

Fixes 7f3c563c575e ("Bluetooth: btmtksdio: Add runtime PM support to SDIO based Bluetooth")
---
 drivers/bluetooth/btmtksdio.c | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)
drivers/bluetooth/btmtksdio.c +1 -3
index 7fab678925d19..a15ae6598c665 100644
@@ -1262,10 +1262,8 @@ static int btmtksdio_shutdown(struct hci_dev *hdev)
 	wmt_params.status = NULL;
 
 	err = mtk_hci_wmt_sync(hdev, &wmt_params);
-	if (err < 0) {
+	if (err < 0)
 		bt_dev_err(hdev, "Failed to send wmt func ctrl (%d)", err);
-		return err;
-	}
 
 ignore_wmt_cmd:
 	pm_runtime_put_noidle(bdev->dev);
Signature · 1 line
2.53.0
author maintainer
#181 lore raw

PATCH 178/438 Bluetooth: btintel_pcie: fix off-by-one bounds check in RX submit

In reply to Greg Kroah-Hartman

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

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

From: Sai Teja Aluvala <[email protected]>

[ Upstream commit 2ea5a87a5a7ae58cb2662b8a7d06f209383e1765 ]

btintel_pcie_submit_rx() used frbd_index > rxq->count to guard the FRBD array access, allowing frbd_index == rxq->count to pass through and index one element past the end of the array. Change the check to

Quoted 1 line

= rxq->count so every out-of-range index is rejected.

This issue was reported by Claude Mythos.

Fixes c2b636b3f788 (Bluetooth: btintel_pcie: Add support for PCIe transport)
---
 drivers/bluetooth/btintel_pcie.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/bluetooth/btintel_pcie.c +1 -1
index aad466e453013..3262e950a6bd8 100644
@@ -508,7 +508,7 @@ static int btintel_pcie_submit_rx(struct btintel_pcie_data *data)
 
 	frbd_index = data->ia.tr_hia[BTINTEL_PCIE_RXQ_NUM];
 
-	if (frbd_index > rxq->count)
+	if (frbd_index >= rxq->count)
 		return -ERANGE;
 
 	/* Prepare for RX submit. It updates the FRBD with the address of DMA
Signature · 1 line
2.53.0
author maintainer
#182 lore raw

PATCH 179/438 Bluetooth: RFCOMM: avoid socket lock inversion in listener cleanup

In reply to Greg Kroah-Hartman

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

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

From: Juan Perdomo <[email protected]>

[ Upstream commit 801fb950cae7048eb7d83b18857d1ca37b8cd5a4 ]

rfcomm_sock_cleanup_listen() closes unaccepted child sockets through rfcomm_sock_close(), which takes the child socket lock before rfcomm_dlc_close() acquires rfcomm_mutex. The RFCOMM worker takes these locks in reverse order while handling connections and DLC state changes, so lockdep reports a possible deadlock.

Close dequeued children without taking their socket lock. The accept queue owns a reference to each child, and bt_accept_dequeue() locks the child while unlinking it and clearing its parent pointer.

Dropping the child lock makes it important to prevent a concurrent rfcomm_connect_ind() from enqueueing a new child after cleanup observes an empty queue. Set a listening socket to BT_CLOSED while its lock is still held, before dropping the lock and draining the queue. The state check in rfcomm_connect_ind() then rejects new children once cleanup starts.

Reported-by [email protected]
Fixes b7ce436a5d79 ("Bluetooth: switch to lock_sock in RFCOMM")
---
 net/bluetooth/rfcomm/sock.c | 13 ++++++++++---
 1 file changed, 10 insertions(+), 3 deletions(-)
net/bluetooth/rfcomm/sock.c +10 -3
index feb302a491fa0..24b42483b3c0c 100644
@@ -241,9 +241,7 @@ static void __rfcomm_sock_close(struct sock *sk)
  */
 static void rfcomm_sock_close(struct sock *sk)
 {
-	lock_sock(sk);
 	__rfcomm_sock_close(sk);
-	release_sock(sk);
 }
 
 static void rfcomm_sock_init(struct sock *sk, struct sock *parent)
@@ -904,6 +902,7 @@ static int rfcomm_sock_compat_ioctl(struct socket *sock, unsigned int cmd, unsig
 static int rfcomm_sock_shutdown(struct socket *sock, int how)
 {
 	struct sock *sk = sock->sk;
+	bool cleanup_listen = false;
 	int err = 0;
 
 	BT_DBG("sock %p, sk %p", sock, sk);
@@ -914,9 +913,17 @@ static int rfcomm_sock_shutdown(struct socket *sock, int how)
 	lock_sock(sk);
 	if (!sk->sk_shutdown) {
 		sk->sk_shutdown = SHUTDOWN_MASK;
+		if (sk->sk_state == BT_LISTEN) {
+			/* Block new children before cleaning up without sk lock. */
+			sk->sk_state = BT_CLOSED;
+			cleanup_listen = true;
+		}
 
 		release_sock(sk);
-		__rfcomm_sock_close(sk);
+		if (cleanup_listen)
+			rfcomm_sock_cleanup_listen(sk);
+		else
+			__rfcomm_sock_close(sk);
 		lock_sock(sk);
 
 		if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime &&
Signature · 1 line
2.53.0
author maintainer
#183 lore raw

PATCH 180/438 af_unix: Unify scc_index when finalising SCC in __unix_walk_scc().

In reply to Greg Kroah-Hartman

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

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

From: Kuniyuki Iwashima <[email protected]>

[ Upstream commit 4a4263dfeabad72f95e8ab6e15146861fa4144dd ]

Commit bfdb01283ee8 ("af_unix: Assign a unique index to SCC.") changed Tarjan's algorithm to update lowlink with lowlink, which is called lowpoint (unix_vertex.scc_index).

unix_vertex_dead() assumes all vertices in an SCC share the same lowpoint, but this is not always true if an SCC has two or more back edges, depending on the order of DFS.

For example, the graph below has two back edges from B to A and from C to B.

A --> B --> C
^ | ^ |
----' ----'

If DFS walks through A -> B -> C -> B (-> C -> B) -> A (-> B -> A), each index and scc_index will be updated as follows.

A --> B --> C C = (3, 3) (index, scc_index)

                   B = (2, 2)
                   A = (1, 1)
  A ... B ... C    C = (3, 2)<-.
         ^    |    B = (2, 2) -'
         `----'    A = (1, 1)
  A ... B ... C    C = (3, 2)
  ^    | .    .    B = (2, 1)<-.
  `----'  ....     A = (1, 1) -'

Then, unix_vertex_dead() thinks that B is passed to another SCC with scc_index 2, and the SCC is not garbage-collected.

This does not happen if DFS walks in a different order below or starts from B.

1 3
A --> B --> C
^ | ^ |
----' ----'
2 4

Let's unify scc_index across the SCC when finalising it.

Note that updating v->index was previously done in unix_scc_dead(), when called from __unix_walk_scc(), just to save one loop. Since __unix_walk_scc() now iterates over the SCC anyway, the update is moved back to __unix_walk_scc() and 'fast' argument is dropped.

---
 net/unix/garbage.c | 17 ++++++++++-------
 1 file changed, 10 insertions(+), 7 deletions(-)
net/unix/garbage.c +10 -7
index 9fcaaf55cba5d..da774f56ca648 100644
@@ -374,7 +374,7 @@ static bool unix_vertex_dead(struct unix_vertex *vertex)
 static LIST_HEAD(unix_visited_vertices);
 static unsigned long unix_vertex_grouped_index = UNIX_VERTEX_INDEX_MARK2;
 
-static bool unix_scc_dead(struct list_head *scc, bool fast)
+static bool unix_scc_dead(struct list_head *scc)
 {
 	struct unix_vertex *vertex;
 	bool scc_dead = true;
@@ -386,10 +386,6 @@ static bool unix_scc_dead(struct list_head *scc, bool fast)
 		/* Don't restart DFS from this vertex. */
 		list_move_tail(&vertex->entry, &unix_visited_vertices);
 
-		/* Mark vertex as off-stack for __unix_walk_scc(). */
-		if (!fast)
-			vertex->index = unix_vertex_grouped_index;
-
 		if (scc_dead)
 			scc_dead = unix_vertex_dead(vertex);
 	}
@@ -521,6 +517,7 @@ static unsigned long __unix_walk_scc(struct unix_vertex *vertex,
 	}
 
 	if (vertex->index == vertex->scc_index) {
+		struct unix_vertex *v;
 		struct list_head scc;
 
 		/* SCC finalised.
@@ -530,7 +527,13 @@ static unsigned long __unix_walk_scc(struct unix_vertex *vertex,
 		 */
 		__list_cut_position(&scc, &vertex_stack, &vertex->scc_entry);
 
-		if (unix_scc_dead(&scc, false)) {
+		list_for_each_entry_reverse(v, &scc, scc_entry) {
+			/* Mark vertex as off-stack and assign a unique ID. */
+			v->index = unix_vertex_grouped_index;
+			v->scc_index = vertex->scc_index;
+		}
+
+		if (unix_scc_dead(&scc)) {
 			unix_collect_skb(&scc, hitlist);
 		} else {
 			if (unix_vertex_max_scc_index < vertex->scc_index)
@@ -588,7 +591,7 @@ static void unix_walk_scc_fast(struct sk_buff_head *hitlist)
 		vertex = list_first_entry(&unix_unvisited_vertices, typeof(*vertex), entry);
 		list_add(&scc, &vertex->scc_entry);
 
-		if (unix_scc_dead(&scc, true)) {
+		if (unix_scc_dead(&scc)) {
 			cyclic_sccs--;
 			unix_collect_skb(&scc, hitlist);
 		}
Signature · 1 line
2.53.0
author maintainer
#184 lore raw

PATCH 181/438 net: stmmac: fix TSO header length truncation

In reply to Greg Kroah-Hartman

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

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

From: Lorenzo Bianconi <[email protected]>

[ Upstream commit 15989abd74f16f44bf953d056b95f1d2fda9b0cd ]

stmmac_tso_xmit() stores the protocol header length returned by stmmac_tso_header_size() in a u8. stmmac_tso_valid_packet() admits headers up to 1023 bytes, so a header longer than 255 bytes wraps modulo 256 (486 becomes 230, 256 becomes 0).

A TCP over IPv6 socket carrying a few hundred bytes of sticky destination/hop-by-hop options makes skb_tcp_all_headers() exceed 255 while staying below the 1023-byte limit, so such an skb reaches stmmac_tso_xmit().

Widen proto_hdr_len to unsigned int, which is sufficient since the value is bounded by the hardware limit, and adjust the debug print specifier accordingly.

---
 drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +3 -3
index f801caf65fff1..5f717f02c3c01 100644
@@ -4513,16 +4513,16 @@ static int stmmac_tso_get_num_desc(struct stmmac_tx_queue *tx_q,
  */
 static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
 {
+	unsigned int first_entry, entry, tx_packets, proto_hdr_len;
 	struct dma_desc *desc, *first, *mss_desc = NULL;
 	struct stmmac_priv *priv = netdev_priv(dev);
-	unsigned int first_entry, entry, tx_packets;
 	struct stmmac_txq_stats *txq_stats;
 	int i, first_tx, nfrags, ndesc;
 	struct stmmac_tx_queue *tx_q;
 	bool set_ic, is_last_segment;
 	u32 pay_len, mss, queue;
-	u8 proto_hdr_len, hdr;
 	dma_addr_t des;
+	u8 hdr;
 
 	nfrags = skb_shinfo(skb)->nr_frags;
 	queue = skb_get_queue_mapping(skb);
@@ -4570,7 +4570,7 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
 	}
 
 	if (netif_msg_tx_queued(priv)) {
-		pr_info("%s: hdrlen %d, hdr_len %d, pay_len %d, mss %d\n",
+		pr_info("%s: hdrlen %d, hdr_len %u, pay_len %d, mss %d\n",
 			__func__, hdr, proto_hdr_len, pay_len, mss);
 		pr_info("\tskb->len %d, skb->data_len %d\n", skb->len,
 			skb->data_len);
Signature · 1 line
2.53.0
author maintainer
#185 lore raw

PATCH 182/438 pppoatm: ensure a writable skb header and linear data

In reply to Greg Kroah-Hartman

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

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

From: Eric Dumazet <[email protected]>

[ Upstream commit ecc7253683a3c55caa868ce0ee530fcb0044bd3c ]

In pppoatm_send(), LLC encapsulation checks whether there is sufficient headroom for the 4-byte LLC header, but does not ensure that the skb header is writable.

Normal transmit packets passing through ppp_start_xmit() have their header unshared via skb_cow_head(). However, packets can also reach pppoatm_send() via PPP channel bridging (PPPIOCBRIDGECHAN) without going through ppp_start_xmit().

Use skb_cow_head() to ensure both sufficient headroom and a writable header before pushing the LLC header.

While at it:
- Call pskb_may_pull(skb, 1) before inspecting skb->data[0] to prevent out-of-bounds reads on zero-length or non-linear frames (e.g. from bridging).
- Defer SC_COMP_PROT protocol compression until after pppoatm_may_send() succeeds. This eliminates the temporary skb allocation on admission failure and completely removes the fragile "undo" heuristic at the nospace label, avoiding any risk of reading uninitialized headroom or performing an unbalanced skb_push().

Fixes 4cf476ced45d ("ppp: add PPPIOCBRIDGECHAN and PPPIOCUNBRIDGECHAN ioctls")
---
 net/atm/pppoatm.c | 42 +++++++++++++++++-------------------------
 1 file changed, 17 insertions(+), 25 deletions(-)
net/atm/pppoatm.c +17 -25
index 6da52d12df68e..5214786e61d11 100644
@@ -292,10 +292,13 @@ static int pppoatm_send(struct ppp_channel *chan, struct sk_buff *skb)
 	struct atm_vcc *vcc;
 	int ret;
 
+	if (!pskb_may_pull(skb, 1)) {
+		kfree_skb(skb);
+		return DROP_PACKET;
+	}
+
 	ATM_SKB(skb)->vcc = pvcc->atmvcc;
 	pr_debug("(skb=0x%p, vcc=0x%p)\n", skb, pvcc->atmvcc);
-	if (skb->data[0] == '\0' && (pvcc->flags & SC_COMP_PROT))
-		(void) skb_pull(skb, 1);
 
 	vcc = ATM_SKB(skb)->vcc;
 	bh_lock_sock(sk_atm(vcc));
@@ -317,23 +320,13 @@ static int pppoatm_send(struct ppp_channel *chan, struct sk_buff *skb)
 
 	switch (pvcc->encaps) {		/* LLC encapsulation needed */
 	case e_llc:
-		if (skb_headroom(skb) < LLC_LEN) {
-			struct sk_buff *n;
-			n = skb_realloc_headroom(skb, LLC_LEN);
-			if (n != NULL &&
-			    !pppoatm_may_send(pvcc, n->truesize)) {
-				kfree_skb(n);
-				goto nospace;
-			}
-			consume_skb(skb);
-			skb = n;
-			if (skb == NULL) {
-				bh_unlock_sock(sk_atm(vcc));
-				return DROP_PACKET;
-			}
-		} else if (!pppoatm_may_send(pvcc, skb->truesize))
+		if (skb_cow_head(skb, LLC_LEN)) {
+			bh_unlock_sock(sk_atm(vcc));
+			kfree_skb(skb);
+			return DROP_PACKET;
+		}
+		if (!pppoatm_may_send(pvcc, skb->truesize))
 			goto nospace;
-		memcpy(skb_push(skb, LLC_LEN), pppllc, LLC_LEN);
 		break;
 	case e_vc:
 		if (!pppoatm_may_send(pvcc, skb->truesize))
@@ -346,6 +339,12 @@ static int pppoatm_send(struct ppp_channel *chan, struct sk_buff *skb)
 		return 1;
 	}
 
+	if (skb->data[0] == '\0' && (pvcc->flags & SC_COMP_PROT))
+		skb_pull(skb, 1);
+
+	if (pvcc->encaps == e_llc)
+		memcpy(skb_push(skb, LLC_LEN), pppllc, LLC_LEN);
+
 	atm_account_tx(vcc, skb);
 	pr_debug("atm_skb(%p)->vcc(%p)->dev(%p)\n",
 		 skb, ATM_SKB(skb)->vcc, ATM_SKB(skb)->vcc->dev);
@@ -355,13 +354,6 @@ static int pppoatm_send(struct ppp_channel *chan, struct sk_buff *skb)
 	return ret;
 nospace:
 	bh_unlock_sock(sk_atm(vcc));
-	/*
-	 * We don't have space to send this SKB now, but we might have
-	 * already applied SC_COMP_PROT compression, so may need to undo
-	 */
-	if ((pvcc->flags & SC_COMP_PROT) && skb_headroom(skb) > 0 &&
-	    skb->data[-1] == '\0')
-		(void) skb_push(skb, 1);
 	return 0;
 }
 
Signature · 1 line
2.53.0
author maintainer
#186 lore raw

PATCH 183/438 drop_monitor: synchronize tracepoint unregistration on error path

In reply to Greg Kroah-Hartman

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

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

From: Eric Dumazet <[email protected]>

[ Upstream commit 6a038ef2b57922b6d9ca98ddac0df0681849b704 ]

If register_trace_napi_poll() fails in net_dm_trace_on_set(), unregister_trace_kfree_skb() is called to roll back the kfree_skb tracepoint registration.

However, tracepoint_synchronize_unregister() is omitted before calling cancel_work_sync() and module_put(). An in-flight probe executing concurrently on another CPU could call schedule_work() after cancel_work_sync() has already returned, leaving a pending work item scheduled after the module reference is dropped. If the module is then unloaded, executing the work item triggers a kernel panic.

Add tracepoint_synchronize_unregister() after unregister_trace_kfree_skb() in the error path, matching net_dm_trace_off_set() and net_dm_hw_probe_unregister().

Fixes 7c747838a558 ("drop_monitor: Split tracing enable / disable to different functions")
---
 net/core/drop_monitor.c | 1 +
 1 file changed, 1 insertion(+)
net/core/drop_monitor.c +1 -0
index abaf108ac4db8..018d19e3a71de 100644
@@ -1173,6 +1173,7 @@ static int net_dm_trace_on_set(struct netlink_ext_ack *extack)
 
 err_unregister_trace:
 	unregister_trace_kfree_skb(ops->kfree_skb_probe, NULL);
+	tracepoint_synchronize_unregister();
 err_module_put:
 	for_each_possible_cpu(cpu) {
 		struct per_cpu_dm_data *data = &per_cpu(dm_cpu_data, cpu);
Signature · 1 line
2.53.0
author maintainer
#187 lore raw

PATCH 184/438 drop_monitor: use timer_shutdown_sync() to prevent timer rearming during teardown

In reply to Greg Kroah-Hartman

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

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

From: Eric Dumazet <[email protected]>

[ Upstream commit c391a40f71886b28c082b47270f0e856fa3e1150 ]

In drop_monitor teardown paths (net_dm_trace_off_set(), net_dm_hw_monitor_stop(), and error unwind paths in net_dm_trace_on_set() and net_dm_hw_monitor_start()), per-CPU timers are stopped using timer_delete_sync() followed by cancel_work_sync().

However, there is a circular dependency between send_timer and dm_alert_work:
1) sched_send_work() (timer callback) schedules dm_alert_work.
2) send_dm_alert() / net_dm_hw_summary_work() calls reset_per_cpu_data() or net_dm_hw_reset_per_cpu_data().
3) If memory allocation fails under memory pressure in the reset function, it re-arms the timer via mod_timer(&data->send_timer, ...).

If dm_alert_work is running concurrently while timer_delete_sync() executes on another CPU, an allocation failure in the worker will re-arm the timer after timer_delete_sync() has already returned. Once cancel_work_sync() completes and module_put() is called, the timer remains active in the timer wheel. If the module is then unloaded, the timer will fire and execute sched_send_work() in freed memory, triggering a kernel panic / use-after-free.

Switch from timer_delete_sync() to timer_shutdown_sync(). This guarantees that any in-flight timer handler has finished and prevents subsequent re-arming attempts from running workers from succeeding. When monitoring is restarted later, timer_setup() is invoked, which cleanly re-initializes the timer.

Fixes 9398e9c0b1d4 ("drop_monitor: Perform cleanup upon probe registration failure")
---
 net/core/drop_monitor.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)
net/core/drop_monitor.c +4 -4
index 018d19e3a71de..873155ca72432 100644
@@ -1083,7 +1083,7 @@ static int net_dm_hw_monitor_start(struct netlink_ext_ack *extack)
 		struct per_cpu_dm_data *hw_data = &per_cpu(dm_hw_cpu_data, cpu);
 		struct sk_buff *skb;
 
-		timer_delete_sync(&hw_data->send_timer);
+		timer_shutdown_sync(&hw_data->send_timer);
 		cancel_work_sync(&hw_data->dm_alert_work);
 		while ((skb = __skb_dequeue(&hw_data->drop_queue))) {
 			struct devlink_trap_metadata *hw_metadata;
@@ -1117,7 +1117,7 @@ static void net_dm_hw_monitor_stop(struct netlink_ext_ack *extack)
 		struct per_cpu_dm_data *hw_data = &per_cpu(dm_hw_cpu_data, cpu);
 		struct sk_buff *skb;
 
-		timer_delete_sync(&hw_data->send_timer);
+		timer_shutdown_sync(&hw_data->send_timer);
 		cancel_work_sync(&hw_data->dm_alert_work);
 		while ((skb = __skb_dequeue(&hw_data->drop_queue))) {
 			struct devlink_trap_metadata *hw_metadata;
@@ -1179,7 +1179,7 @@ static int net_dm_trace_on_set(struct netlink_ext_ack *extack)
 		struct per_cpu_dm_data *data = &per_cpu(dm_cpu_data, cpu);
 		struct sk_buff *skb;
 
-		timer_delete_sync(&data->send_timer);
+		timer_shutdown_sync(&data->send_timer);
 		cancel_work_sync(&data->dm_alert_work);
 		while ((skb = __skb_dequeue(&data->drop_queue)))
 			consume_skb(skb);
@@ -1207,7 +1207,7 @@ static void net_dm_trace_off_set(void)
 		struct per_cpu_dm_data *data = &per_cpu(dm_cpu_data, cpu);
 		struct sk_buff *skb;
 
-		timer_delete_sync(&data->send_timer);
+		timer_shutdown_sync(&data->send_timer);
 		cancel_work_sync(&data->dm_alert_work);
 		while ((skb = __skb_dequeue(&data->drop_queue)))
 			consume_skb(skb);
Signature · 1 line
2.53.0
author maintainer
#188 lore raw

PATCH 185/438 drop_monitor: use raw_cpu_ptr() in tracepoint probes

In reply to Greg Kroah-Hartman

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

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

From: Eric Dumazet <[email protected]>

[ Upstream commit c19b7d35086b7d240f1ca3088b0079d2bd39ffb9 ]

syzbot reported a preemption warning in sk_skb_reason_drop():

 BUG: using smp_processor_id() in preemptible [00000000] code: syz.0.17/5917
 caller is net_dm_packet_trace_kfree_skb_hit+0x119/0x350 net/core/drop_monitor.c:519

In net_dm_packet_trace_kfree_skb_hit(), data = this_cpu_ptr(&dm_cpu_data) is evaluated before spin_lock_irqsave(&data->drop_queue.lock, flags). When kfree_skb() is called from preemptible context (e.g. process context during close() on /dev/net/tun), preemption is enabled, triggering the CONFIG_DEBUG_PREEMPT warning in smp_processor_id().

The same pattern exists in net_dm_hw_trap_summary_probe() and net_dm_hw_trap_packet_probe() for dm_hw_cpu_data.

This is a false positive because each per-cpu structure is protected by its own spinlock. If the task migrates to another CPU right after reading the per-cpu pointer, the lock still safely synchronizes access to that queue.

Use raw_cpu_ptr() instead of this_cpu_ptr() to silence CONFIG_DEBUG_PREEMPT without disturbing interrupt state or breaking PREEMPT_RT locking semantics.

Fixes ca30707dee2b ("drop_monitor: Add packet alert mode")
Fixes 5855357cd40e ("drop_monitor: Prepare probe functions for devlink tracepoint")
Reported-by [email protected]
---
 net/core/drop_monitor.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)
net/core/drop_monitor.c +3 -3
index 873155ca72432..795c15dd1771a 100644
@@ -448,7 +448,7 @@ net_dm_hw_trap_summary_probe(void *ignore, const struct devlink *devlink,
 	if (metadata->trap_type == DEVLINK_TRAP_TYPE_CONTROL)
 		return;
 
-	hw_data = this_cpu_ptr(&dm_hw_cpu_data);
+	hw_data = raw_cpu_ptr(&dm_hw_cpu_data);
 	raw_spin_lock_irqsave(&hw_data->lock, flags);
 	hw_entries = hw_data->hw_entries;
 
@@ -516,7 +516,7 @@ static void net_dm_packet_trace_kfree_skb_hit(void *ignore,
 	 */
 	nskb->tstamp = tstamp;
 
-	data = this_cpu_ptr(&dm_cpu_data);
+	data = raw_cpu_ptr(&dm_cpu_data);
 
 	spin_lock_irqsave(&data->drop_queue.lock, flags);
 	if (skb_queue_len(&data->drop_queue) < net_dm_queue_len)
@@ -983,7 +983,7 @@ net_dm_hw_trap_packet_probe(void *ignore, const struct devlink *devlink,
 	NET_DM_SKB_CB(nskb)->hw_metadata = n_hw_metadata;
 	nskb->tstamp = tstamp;
 
-	hw_data = this_cpu_ptr(&dm_hw_cpu_data);
+	hw_data = raw_cpu_ptr(&dm_hw_cpu_data);
 
 	spin_lock_irqsave(&hw_data->drop_queue.lock, flags);
 	if (skb_queue_len(&hw_data->drop_queue) < net_dm_queue_len)
Signature · 1 line
2.53.0
author maintainer
#189 lore raw

PATCH 186/438 drop_monitor: fix out-of-bounds write in reset_per_cpu_data()

In reply to Greg Kroah-Hartman

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

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

From: Eric Dumazet <[email protected]>

[ Upstream commit 439f392084f8f7f59ab9d47a9579185accefe1d8 ]

In reset_per_cpu_data(), al is computed as:

    al = sizeof(struct net_dm_alert_msg);
    al += dm_hit_limit * sizeof(struct net_dm_drop_point);
    al += sizeof(struct nlattr);

    skb = genlmsg_new(al, GFP_KERNEL);
    ...
    nla = nla_reserve(skb, NLA_UNSPEC, sizeof(struct net_dm_alert_msg));
    ...
    msg = nla_data(nla);
    memset(msg, 0, al);

Because al includes sizeof(struct nlattr) (the 4-byte attribute header), genlmsg_new() allocates al bytes of tailroom starting at nla. However, msg points to nla_data(nla), which is located sizeof(struct nlattr) bytes past nla. Calling memset(msg, 0, al) therefore writes al bytes starting from msg, exceeding the allocated buffer by sizeof(struct nlattr) (4 bytes) and corrupting skb_shared_info.

Fix this by letting al represent only the payload length, allocating the skb with genlmsg_new(nla_total_size(al), GFP_KERNEL), and zeroing al bytes from msg.

Fixes 683703a26e46 ("drop_monitor: Update netlink protocol to include netlink attribute header in alert message")
---
 net/core/drop_monitor.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)
net/core/drop_monitor.c +1 -2
index 795c15dd1771a..edc660778408e 100644
@@ -141,9 +141,8 @@ static struct sk_buff *reset_per_cpu_data(struct per_cpu_dm_data *data)
 
 	al = sizeof(struct net_dm_alert_msg);
 	al += dm_hit_limit * sizeof(struct net_dm_drop_point);
-	al += sizeof(struct nlattr);
 
-	skb = genlmsg_new(al, GFP_KERNEL);
+	skb = genlmsg_new(nla_total_size(al), GFP_KERNEL);
 
 	if (!skb)
 		goto err;
Signature · 1 line
2.53.0
author maintainer
#190 lore raw

PATCH 187/438 net: stmmac: do not overwrite phc_index when no PTP clock is registered

In reply to Greg Kroah-Hartman

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

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

From: Lorenzo Bianconi <[email protected]>

[ Upstream commit f0ef4b1eaed000a304726a43091588e8426ba08a ]

stmmac_get_ts_info() reports phc_index as 0 when hardware timestamping is supported but no PTP clock has been registered yet (e.g. while the interface is down). Zero is a valid PHC index and would make userspace resolve the wrong clock; the absence of a clock should be reported as -1.

The ethtool core already initializes phc_index to -1 before invoking the get_ts_info callback (ethtool_init_tsinfo()), so just drop the erroneous assignment.

---
 drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c | 2 --
 1 file changed, 2 deletions(-)
drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c +0 -2
index 92585d27ab883..2a4882f2a498d 100644
@@ -1013,8 +1013,6 @@ static int stmmac_get_ts_info(struct net_device *dev,
 
 		if (priv->ptp_clock)
 			info->phc_index = ptp_clock_index(priv->ptp_clock);
-		else
-			info->phc_index = 0;
 
 		info->tx_types = (1 << HWTSTAMP_TX_OFF) | (1 << HWTSTAMP_TX_ON);
 
Signature · 1 line
2.53.0
author maintainer
#191 lore raw

PATCH 188/438 net: bridge: vlan: fix bugs caused by switchdev deletion errors

In reply to Greg Kroah-Hartman

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

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

From: Nikolay Aleksandrov <[email protected]>

[ Upstream commit 2842ce397dd09882530b42f7fdb0c855767eb24e ]

Allowing switchdev to prevent vlan deletion and error out in __vlan_del could cause multiple different issues - inconsistent state, memory leaks when flushing, NULL pointer dereference on bridge error when flushing. It doesn't make sense to allow it to stop __vlan_del, so log the error and continue with software vlan deletion. This is also consistent with 8021q behaviour.

Fixes bf361ad38165 ("net: bridge: check __vlan_vid_del for error")
Fixes 5454f5c28eca ("net: bridge: vlan: check for errors from __vlan_del in __vlan_flush")
Fixes 2594e9064a57 ("bridge: vlan: add per-vlan struct and move to rhashtables")
Fixes 9c86ce2c1ae3 ("net: bridge: Notify about bridge VLANs")
---
 net/bridge/br_vlan.c | 31 ++++++++++++++-----------------
 1 file changed, 14 insertions(+), 17 deletions(-)
net/bridge/br_vlan.c +14 -17
index 1e0e436629ece..92b3cb621a264 100644
@@ -387,12 +387,12 @@ static int __vlan_add(struct net_bridge_vlan *v, u16 flags,
 	goto out;
 }
 
-static int __vlan_del(struct net_bridge_vlan *v)
+static void __vlan_del(struct net_bridge_vlan *v)
 {
 	struct net_bridge_vlan *masterv = v;
 	struct net_bridge_vlan_group *vg;
 	struct net_bridge_port *p = NULL;
-	int err = 0;
+	int err;
 
 	if (br_vlan_is_master(v)) {
 		vg = br_vlan_group(v->br);
@@ -406,12 +406,16 @@ static int __vlan_del(struct net_bridge_vlan *v)
 	if (p) {
 		err = __vlan_vid_del(p->dev, p->br, v);
 		if (err)
-			goto out;
+			br_warn(p->br,
+				"port %u(%s) failed to delete vlan %u from switchdev: %pe\n",
+				(unsigned int)p->port_no, p->dev->name,
+				v->vid, ERR_PTR(err));
 	} else {
 		err = br_switchdev_port_vlan_del(v->br->dev, v->vid);
 		if (err && err != -EOPNOTSUPP)
-			goto out;
-		err = 0;
+			br_warn(v->br,
+				"failed to delete bridge vlan %u from switchdev: %pe\n",
+				v->vid, ERR_PTR(err));
 	}
 
 	if (br_vlan_should_use(v)) {
@@ -431,8 +435,6 @@ static int __vlan_del(struct net_bridge_vlan *v)
 	}
 
 	br_vlan_put_master(masterv);
-out:
-	return err;
 }
 
 static void __vlan_group_free(struct net_bridge_vlan_group *vg)
@@ -449,7 +451,6 @@ static void __vlan_flush(const struct net_bridge *br,
 {
 	struct net_bridge_vlan *vlan, *tmp;
 	u16 v_start = 0, v_end = 0;
-	int err;
 
 	__vlan_delete_pvid(vg, vg->pvid);
 	list_for_each_entry_safe(vlan, tmp, &vg->vlan_list, vlist) {
@@ -463,13 +464,7 @@ static void __vlan_flush(const struct net_bridge *br,
 		}
 		v_end = vlan->vid;
 
-		err = __vlan_del(vlan);
-		if (err) {
-			br_err(br,
-			       "port %u(%s) failed to delete vlan %d: %pe\n",
-			       (unsigned int) p->port_no, p->dev->name,
-			       vlan->vid, ERR_PTR(err));
-		}
+		__vlan_del(vlan);
 	}
 
 	/* notify about the last/whole vlan range */
@@ -837,8 +832,9 @@ int br_vlan_delete(struct net_bridge *br, u16 vid)
 	br_fdb_delete_by_port(br, NULL, vid, 0);
 
 	vlan_tunnel_info_del(vg, v);
+	__vlan_del(v);
 
-	return __vlan_del(v);
+	return 0;
 }
 
 void br_vlan_flush(struct net_bridge *br)
@@ -1368,8 +1364,9 @@ int nbp_vlan_delete(struct net_bridge_port *port, u16 vid)
 		return -ENOENT;
 	br_fdb_find_delete_local(port->br, port, port->dev->dev_addr, vid);
 	br_fdb_delete_by_port(port->br, port, vid, 0);
+	__vlan_del(v);
 
-	return __vlan_del(v);
+	return 0;
 }
 
 void nbp_vlan_flush(struct net_bridge_port *port)
Signature · 1 line
2.53.0
author maintainer
#192 lore raw

PATCH 189/438 netlink: do not free nlk->groups while lockless readers can use it

In reply to Greg Kroah-Hartman

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

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

From: Eric Dumazet <[email protected]>

[ Upstream commit ceac0de741bfb47ca255eee075257b3bb31f0651 ]

netlink_realloc_groups() uses krealloc() under netlink_table_grab(). Whenever NLGRPSZ(groups) lands in a different kmalloc bucket, the old bitmap is freed immediately.

Two readers of nlk->groups / nlk->ngroups do not hold the netlink table lock:

1) sk_diag_dump_groups(). Hashed (bound) sockets are dumped from the rhashtable walk in __netlink_diag_dump(), which only holds RCU. Only the mc_list part of the dump takes nl_table_lock.

2) netlink_native_seq_show() (/proc/net/netlink), whose walk has been lockless since commit 21e4902aea80 ("netlink: Lockless lookup with RCU grace period in socket release").

Both can read a freed buffer, and sk_diag_dump_groups() can also read past the end of the old (smaller) buffer if it happens to load the old @groups pointer together with the new @ngroups value, copying the result into a NETLINK_DIAG_GROUPS attribute.

This is the same class of bug that commit f773608026ee ("netlink:
access nlk groups safely in netlink bind and getname") fixed for bind() and getname(); these two readers were missed. Simply grabbing the table lock in sk_diag_dump_groups() is not an option, because it is also called with nl_table_lock already held from the mc_list section of the dump.

Make the lockless readers safe instead:

- Allocate a new bitmap and free the old one after an RCU grace period, instead of relying on the implicit kfree() done by krealloc().

- Publish @groups before @ngroups, both with release semantics, and have the lockless readers load @ngroups first. A reader can then never pair the new (bigger) size with the old (smaller) buffer, and a reader picking up the new pointer while still seeing the old size is guaranteed to see the initialized bitmap.

netlink_realloc_groups() is called from process context (bind() and setsockopt()), so kfree_rcu_mightsleep() can be used, once the table has been released.

Fixes 21e4902aea80 ("netlink: Lockless lookup with RCU grace period in socket release")
Fixes ad202074320c ("netlink: Use rhashtable walk interface in diag dump")
---
 net/netlink/af_netlink.c | 42 ++++++++++++++++++++++++++++++++--------
 net/netlink/diag.c       | 20 +++++++++++++++----
 2 files changed, 50 insertions(+), 12 deletions(-)
net/netlink/af_netlink.c +34 -8
index 5202fe0b08671..7288de715341a 100644
@@ -922,9 +922,9 @@ netlink_update_subscriptions(struct sock *sk, unsigned int subscriptions)
 
 static int netlink_realloc_groups(struct sock *sk)
 {
+	unsigned long *new_groups, *old_groups = NULL;
 	struct netlink_sock *nlk = nlk_sk(sk);
 	unsigned int groups;
-	unsigned long *new_groups;
 	int err = 0;
 
 	netlink_table_grab();
@@ -938,18 +938,37 @@ static int netlink_realloc_groups(struct sock *sk)
 	if (nlk->ngroups >= groups)
 		goto out_unlock;
 
-	new_groups = krealloc(nlk->groups, NLGRPSZ(groups), GFP_ATOMIC);
-	if (new_groups == NULL) {
+	/* Can not use krealloc(), because the old buffer might be freed
+	 * immediately, while lockless readers (netlink diag dump and
+	 * /proc/net/netlink) can still be looking at it.
+	 */
+	new_groups = kzalloc(NLGRPSZ(groups), GFP_ATOMIC);
+	if (!new_groups) {
 		err = -ENOMEM;
 		goto out_unlock;
 	}
-	memset((char *)new_groups + NLGRPSZ(nlk->ngroups), 0,
-	       NLGRPSZ(groups) - NLGRPSZ(nlk->ngroups));
+	old_groups = nlk->groups;
+	if (old_groups)
+		memcpy(new_groups, old_groups, NLGRPSZ(nlk->ngroups));
+
+	/* Publish the new bitmap and its content: pairs with the address
+	 * dependency in lockless readers, which can pick up the new pointer
+	 * while still seeing the old (smaller) nlk->ngroups.
+	 */
+	smp_store_release(&nlk->groups, new_groups);
+
+	/* Then publish the new size: pairs with smp_load_acquire() from
+	 * lockless readers, so that they can not read NLGRPSZ(new ngroups)
+	 * bytes from the old buffer.
+	 */
+	smp_store_release(&nlk->ngroups, groups);
 
-	nlk->groups = new_groups;
-	nlk->ngroups = groups;
  out_unlock:
 	netlink_table_ungrab();
+
+	if (old_groups)
+		kfree_rcu_mightsleep(old_groups);
+
 	return err;
 }
 
@@ -2705,12 +2724,19 @@ static int netlink_native_seq_show(struct seq_file *seq, void *v)
 	} else {
 		struct sock *s = v;
 		struct netlink_sock *nlk = nlk_sk(s);
+		const unsigned long *groups;
+
+		/* Lockless read : netlink_realloc_groups() can change
+		 * nlk->groups under us. The old buffer is freed after an
+		 * RCU grace period, and this walk is RCU protected.
+		 */
+		groups = READ_ONCE(nlk->groups);
 
 		seq_printf(seq, "%pK %-3d %-10u %08x %-8d %-8d %-5d %-8d %-8u %-8llu\n",
 			   s,
 			   s->sk_protocol,
 			   nlk->portid,
-			   nlk->groups ? (u32)nlk->groups[0] : 0,
+			   groups ? (u32)groups[0] : 0,
 			   sk_rmem_alloc_get(s),
 			   sk_wmem_alloc_get(s),
 			   READ_ONCE(nlk->cb_running),
net/netlink/diag.c +16 -4
index 0b3e021bd0ed2..7979bd9b26060 100644
@@ -12,12 +12,24 @@
 static int sk_diag_dump_groups(struct sock *sk, struct sk_buff *nlskb)
 {
 	struct netlink_sock *nlk = nlk_sk(sk);
-
-	if (nlk->groups == NULL)
+	unsigned long *groups;
+	unsigned int ngroups;
+
+	/* Hashed sockets are dumped from the rhashtable walk, which only
+	 * holds rcu_read_lock(), while netlink_realloc_groups() can replace
+	 * nlk->groups and nlk->ngroups at any time.
+	 *
+	 * Read nlk->ngroups first : this pairs with smp_store_release()
+	 * from netlink_realloc_groups(), so that we can not use the new
+	 * (bigger) size with the old (smaller) buffer. The old buffer is
+	 * freed after an RCU grace period.
+	 */
+	ngroups = smp_load_acquire(&nlk->ngroups);
+	groups = READ_ONCE(nlk->groups);
+	if (!groups)
 		return 0;
 
-	return nla_put(nlskb, NETLINK_DIAG_GROUPS, NLGRPSZ(nlk->ngroups),
-		       nlk->groups);
+	return nla_put(nlskb, NETLINK_DIAG_GROUPS, NLGRPSZ(ngroups), groups);
 }
 
 static int sk_diag_put_flags(struct sock *sk, struct sk_buff *skb)
Signature · 1 line
2.53.0
author maintainer
#193 lore raw

PATCH 190/438 KVM: PPC: Book3S HV: fix use-after-free in kvmhv_emulate_tlbie_all_lpid()

In reply to Greg Kroah-Hartman

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

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

From: Amit Machhiwal <[email protected]>

[ Upstream commit 51938dfa8a51a4f85328413fca9b6e21f9d2d088 ]

kvmhv_emulate_tlbie_all_lpid() iterates the nested-guest IDR and drops mmu_lock before calling kvmhv_emulate_tlbie_lpid(), but does not hold a reference on the kvm_nested_guest pointer obtained from the IDR. A concurrent vCPU issuing a single-LPID tlbie (is=2, ric=2) can race through kvmhv_flush_nested() -> kvmhv_remove_nested() -> idr_remove / --refcnt -> kvmhv_release_nested() -> kfree(gp) in that window, leaving the iterating vCPU with a dangling pointer. The subsequent mutex_lock(&gp->tlb_lock) and accesses to gp->shadow_pgtable, gp->shadow_lpid and gp->l1_host all touch freed memory. The free path is fully L1-controlled.

Fix this by incrementing gp->refcnt inside the loop before dropping mmu_lock, mirroring what kvmhv_get_nested() does, and releasing the reference with kvmhv_put_nested() after the per-guest work completes. This is the same get/put discipline already used at every other call site that drops mmu_lock while holding a nested-guest pointer.

Fixes e3b6b4661527 ("KVM: PPC: Book3S HV: Implement H_TLB_INVALIDATE hcall")
---
 arch/powerpc/kvm/book3s_hv_nested.c | 2 ++
 1 file changed, 2 insertions(+)
arch/powerpc/kvm/book3s_hv_nested.c +2 -0
index 22e6166622556..a6ff42d7666c1 100644
@@ -1204,8 +1204,10 @@ static void kvmhv_emulate_tlbie_all_lpid(struct kvm_vcpu *vcpu, int ric)
 
 	spin_lock(&kvm->mmu_lock);
 	idr_for_each_entry(&kvm->arch.kvm_nested_guest_idr, gp, lpid) {
+		++gp->refcnt;
 		spin_unlock(&kvm->mmu_lock);
 		kvmhv_emulate_tlbie_lpid(vcpu, gp, ric);
+		kvmhv_put_nested(gp);
 		spin_lock(&kvm->mmu_lock);
 	}
 	spin_unlock(&kvm->mmu_lock);
Signature · 1 line
2.53.0
author maintainer
#194 lore raw

PATCH 191/438 KVM: PPC: Book3S HV: fix secure device page leak on uv_page_in() failure

In reply to Greg Kroah-Hartman

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

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

From: Amit Machhiwal <[email protected]>

[ Upstream commit 0a416ee20bcccddf91ca5b63696a23b9d11d73aa ]

In kvmppc_svm_page_in(), if uv_page_in() fails after kvmppc_uvmem_get_page() has succeeded, the secure device page is never released. kvmppc_uvmem_get_page() sets a bit in kvmppc_uvmem_bitmap, allocates a kvmppc_uvmem_page_pvt struct, marks the GFN as KVMPPC_GFN_UVMEM_PFN, and calls zone_device_page_init() which sets refcount=1 and locks the page. The subsequent goto out_finalize skips the *mig.dst assignment, so migrate_vma_finalize() is a no-op for the page, and none of those resources are ever reclaimed.

Each occurrence permanently consumes one entry from the firmware-bounded secure memory pool (kvmppc_uvmem_bitmap), leaks pvt, and leaves the GFN marked as secure — making it unusable for the lifetime of the VM.

The twin __kvmppc_svm_page_out() already handles the analogous uv_page_out() failure correctly with unlock_page(dpage); __free_page(dpage). Apply the same pattern here: unlock_page() followed by put_page(), which chains through free_zone_device_folio() into kvmppc_uvmem_folio_free() to clear the bitmap bit, free pvt, and reset the GFN state.

Reachable whenever uv_page_in() returns an error (e.g. UV pool exhaustion) on any POWER9/10 + Ultravisor/PEF system.

Fixes ca9f4942670c ("KVM: PPC: Book3S HV: Support for running secure guests")
---
 arch/powerpc/kvm/book3s_hv_uvmem.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)
arch/powerpc/kvm/book3s_hv_uvmem.c +4 -1
index 5fbb95d90e996..463aef870c4eb 100644
@@ -779,8 +779,11 @@ static int kvmppc_svm_page_in(struct vm_area_struct *vma,
 		if (spage) {
 			ret = uv_page_in(kvm->arch.lpid, pfn << page_shift,
 					gpa, 0, page_shift);
-			if (ret)
+			if (ret) {
+				unlock_page(dpage);
+				put_page(dpage);
 				goto out_finalize;
+			}
 		}
 	}
 
Signature · 1 line
2.53.0
author maintainer
#195 lore raw

PATCH 192/438 powerpc/iommu: Fix the overflow validation in iommu_tce_check_ioba

In reply to Greg Kroah-Hartman

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

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

From: Shivaprasad G Bhat <[email protected]>

[ Upstream commit 0b271f7d7f5ed45bc498a03ce0aa9cfd8402fc71 ]

The commit b1af23d836f8 ("KVM: PPC: iommu: Unify TCE checking") unified IOBA parameter checking across KVM and VFIO into iommu_tce_check_ioba(). While doing so, the passed in argument npages is ignored and constant value '1' is used leaving out a possible overflow as the callers can legitimately be using npages > 1 for H_STUFF_TCE or H_PUT_TCE_INDIRECT cases.

Fix this by accounting for 'npages', checking for arithmetic overflow, and verifying that the entire requested range (ioba - offset + npages) does not exceed the table capacity 'size'.

---
 arch/powerpc/kernel/iommu.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
arch/powerpc/kernel/iommu.c +1 -1
index ee1b5cb557c9a..1ae8384637b5f 100644
@@ -1076,7 +1076,7 @@ int iommu_tce_check_ioba(unsigned long page_shift,
 	if (ioba < offset)
 		return -EINVAL;
 
-	if ((ioba + 1) > (offset + size))
+	if ((ioba + npages < ioba) || (ioba - offset + npages > size))
 		return -EINVAL;
 
 	return 0;
Signature · 1 line
2.53.0
author maintainer
#196 lore raw

PATCH 193/438 Revert "drm/i915/display: Clear SEL_FETCH_PLANE_CTL on plane disable"

In reply to Greg Kroah-Hartman

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

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

From: Nemesa Garg <[email protected]>

[ Upstream commit a26204be587c57bd5c54fa513be26c4fd7bf252d ]

This reverts commit 7f1172a2ac0d7e50850785e2e65789c8aac8411a.

This commit replaced the crtc_state->enable_psr2_sel_fetch guard in icl_plane_disable_sel_fetch_arm() and i9xx_cursor_disable_sel_fetch_arm() with HAS_PSR2_SEL_FETCH(). This is a display version check and says nothing about the pipe, so every plane and cursor disable on a display 12+ platform started writing SEL_FETCH_PLANE_CTL() / SEL_FETCH_CUR_CTL(), including on pipes that do not implement them. It shows up as an unclaimed register access on pipes driving HDMI where selective fetch was never enabled.

The stale selective fetch enable bit that commit addressed is handled in the next patch.

Fixes 7f1172a2ac0d ("drm/i915/display: Clear SEL_FETCH_PLANE_CTL on plane disable")
(cherry picked from commit d393529394167e0f5f706657eebe84d8529ce4fc)
---
 drivers/gpu/drm/i915/display/intel_cursor.c       | 15 +++++----------
 .../gpu/drm/i915/display/skl_universal_plane.c    | 15 +++++----------
 2 files changed, 10 insertions(+), 20 deletions(-)
drivers/gpu/drm/i915/display/intel_cursor.c +5 -10
index 18ef7e64821be..52347668f27d6 100644
@@ -530,18 +530,13 @@ static int i9xx_check_cursor(struct intel_crtc_state *crtc_state,
 }
 
 static void i9xx_cursor_disable_sel_fetch_arm(struct intel_dsb *dsb,
-					      struct intel_plane *plane)
+					      struct intel_plane *plane,
+					      const struct intel_crtc_state *crtc_state)
 {
 	struct intel_display *display = to_intel_display(plane);
 	enum pipe pipe = plane->pipe;
 
-	/*
-	 * Clear this whenever the hardware has selective fetch, not just when
-	 * the current state uses it. The cursor may have been enabled with
-	 * selective fetch earlier and had its enable bit orphaned when the
-	 * feature was switched off.
-	 */
-	if (!HAS_PSR2_SEL_FETCH(display))
+	if (!crtc_state->enable_psr2_sel_fetch)
 		return;
 
 	intel_de_write_dsb(display, dsb, SEL_FETCH_CUR_CTL(pipe), 0);
@@ -591,7 +586,7 @@ static void i9xx_cursor_update_sel_fetch_arm(struct intel_dsb *dsb,
 		if (crtc_state->enable_psr2_su_region_et)
 			wa_16021440873(dsb, plane, crtc_state, plane_state);
 		else
-			i9xx_cursor_disable_sel_fetch_arm(dsb, plane);
+			i9xx_cursor_disable_sel_fetch_arm(dsb, plane, crtc_state);
 	}
 }
 
@@ -700,7 +695,7 @@ static void i9xx_cursor_update_arm(struct intel_dsb *dsb,
 	if (plane_state)
 		i9xx_cursor_update_sel_fetch_arm(dsb, plane, crtc_state, plane_state);
 	else
-		i9xx_cursor_disable_sel_fetch_arm(dsb, plane);
+		i9xx_cursor_disable_sel_fetch_arm(dsb, plane, crtc_state);
 
 	if (plane->cursor.base != base ||
 	    plane->cursor.size != fbc_ctl ||
drivers/gpu/drm/i915/display/skl_universal_plane.c +5 -10
index 4369c8eeaac25..164b7d61c9a31 100644
@@ -879,18 +879,13 @@ skl_plane_disable_arm(struct intel_dsb *dsb,
 }
 
 static void icl_plane_disable_sel_fetch_arm(struct intel_dsb *dsb,
-					    struct intel_plane *plane)
+					    struct intel_plane *plane,
+					    const struct intel_crtc_state *crtc_state)
 {
 	struct intel_display *display = to_intel_display(plane);
 	enum pipe pipe = plane->pipe;
 
-	/*
-	 * Clear this whenever the hardware has selective fetch, not just when
-	 * the current state uses it. The plane may have been enabled with
-	 * selective fetch earlier and had its enable bit orphaned when the
-	 * feature was switched off.
-	 */
-	if (!HAS_PSR2_SEL_FETCH(display))
+	if (!crtc_state->enable_psr2_sel_fetch)
 		return;
 
 	intel_de_write_dsb(display, dsb, SEL_FETCH_PLANE_CTL(pipe, plane->id), 0);
@@ -926,7 +921,7 @@ icl_plane_disable_arm(struct intel_dsb *dsb,
 
 	skl_write_plane_wm(dsb, plane, crtc_state);
 
-	icl_plane_disable_sel_fetch_arm(dsb, plane);
+	icl_plane_disable_sel_fetch_arm(dsb, plane, crtc_state);
 
 	if (plane_has_normalizer(plane))
 		intel_de_write_dsb(display, dsb,
@@ -1646,7 +1641,7 @@ static void icl_plane_update_sel_fetch_arm(struct intel_dsb *dsb,
 		intel_de_write_dsb(display, dsb, SEL_FETCH_PLANE_CTL(pipe, plane->id),
 				   SEL_FETCH_PLANE_CTL_ENABLE);
 	else
-		icl_plane_disable_sel_fetch_arm(dsb, plane);
+		icl_plane_disable_sel_fetch_arm(dsb, plane, crtc_state);
 }
 
 static void
Signature · 1 line
2.53.0
author maintainer
#197 lore raw

PATCH 194/438 futex: Also allocate private hash on vfork()

In reply to Greg Kroah-Hartman

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

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

From: Peter Zijlstra <[email protected]>

[ Upstream commit b61b6f95d6722ddbbbd09e689fa41b55fd36f9a5 ]

As Jann demonstrated, it is entirely feasible to access the mm through vfork(). Therefore we need to allocate a private hash on vfork() as well as any other CLONE_VM user.

Specifically, it must be avoided to have (private) futex waiters before allocating the private hash.

Fixes ee9dce44362b ("futex: Drop CLONE_THREAD requirement for private default hash alloc")
---
 kernel/fork.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
kernel/fork.c +2 -2
index 3340dbaf2bb4a..2c88568b0463d 100644
@@ -1977,9 +1977,9 @@ static bool need_futex_hash_allocate_default(u64 clone_flags)
 {
 	/*
 	 * Allocate a default futex hash for any sibling that will
-	 * share the parent's mm, except vfork.
+	 * share the parent's mm.
 	 */
-	return (clone_flags & (CLONE_VM | CLONE_VFORK)) == CLONE_VM;
+	return clone_flags & CLONE_VM;
 }
 
 /*
Signature · 1 line
2.53.0
author maintainer
#198 lore raw

PATCH 195/438 drm/xe/i2c: Disable IRQ on unbind

In reply to Greg Kroah-Hartman

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

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

From: Raag Jadav <[email protected]>

[ Upstream commit f0e9f963a3d209d7dc7ddd61116118ab5da2797d ]

Currently, struct xe_i2c is freed before SGUnit IRQ is disabled in unbind path, leaving a potential UAF in case I2C IRQ is hit during this small window. Explicitly disable I2C IRQ in xe_i2c_remove() and fix this.

Fixes 0bb78ce09926 ("drm/xe/i2c: Wire up reset/postinstall for I2C IRQ")
(cherry picked from commit 8ba5c8b8ab3fd362267c11df2cd5a90ee46f6e24)
---
 drivers/gpu/drm/xe/xe_i2c.c | 3 +++
 1 file changed, 3 insertions(+)
drivers/gpu/drm/xe/xe_i2c.c +3 -0
index 5504cd9dd3596..6fce3de272965 100644
@@ -277,8 +277,10 @@ void xe_i2c_pm_resume(struct xe_device *xe, bool d3cold)
 static void xe_i2c_remove(void *data)
 {
 	struct xe_i2c *i2c = data;
+	struct xe_device *xe = tile_to_xe(i2c->mmio->tile);
 	unsigned int i;
 
+	xe_i2c_irq_reset(xe);
 	xe_amc_exit(i2c);
 
 	for (i = 0; i < XE_I2C_MAX_CLIENTS; i++) {
@@ -288,6 +290,7 @@ static void xe_i2c_remove(void *data)
 
 	bus_unregister_notifier(&i2c_bus_type, &i2c->bus_notifier);
 	xe_i2c_unregister_adapter(i2c);
+	xe->i2c = NULL;
 }
 
 /**
Signature · 1 line
2.53.0
author maintainer
#199 lore raw

PATCH 196/438 btrfs: handle lack of space when cleaning up verity items

In reply to Greg Kroah-Hartman

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

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

From: Daniel Linjama <[email protected]>

[ Upstream commit 76bf149cd0298544631e756670b89c399c7acbca ]

When enable_verity() hits the qgroup limit, rollback_verity() needs its own metadata reservation. When the qgroup limit or lack of space refuses the rollback, the whole filesystem is forced read-only even though the qgroup limit was for one subvolume only. Also orphan cleanup at the next mount fails the same way, so the leftover items are never removed: with -EDQUOT the subvolume stays unreachable, and with -ENOSPC on a full filesystem the next read-write mount fails.

Start transactions with btrfs_start_transaction_fallback_global_rsv() in btrfs_orphan_cleanup(), drop_verity_items() and rollback_verity(). Those calls only delete items and free the space in the end, so they may use the global reserve and skip the qgroup limit, which avoids -ENOSPC and -EDQUOT.

Fixes 146054090b08 ("btrfs: initial fsverity support")
---
 fs/btrfs/inode.c  |  3 ++-
 fs/btrfs/verity.c | 18 ++++++++++++++++--
 2 files changed, 18 insertions(+), 3 deletions(-)
fs/btrfs/inode.c +2 -1
index 188b4ac9889fc..67f39347acc05 100644
@@ -3876,7 +3876,8 @@ int btrfs_orphan_cleanup(struct btrfs_root *root)
 				if (ret)
 					goto out;
 			}
-			trans = btrfs_start_transaction(root, 1);
+			/* Only deletes the orphan. */
+			trans = btrfs_start_transaction_fallback_global_rsv(root, 1);
 			if (IS_ERR(trans)) {
 				ret = PTR_ERR(trans);
 				goto out;
fs/btrfs/verity.c +16 -2
index 1133a56c0568b..1f05525386c93 100644
@@ -93,6 +93,20 @@ static loff_t merkle_file_pos(const struct inode *inode)
 	return rounded;
 }
 
+/*
+ * Start a transaction for removing verity items or the verity orphan.
+ *
+ * Like unlink, this only deletes items and frees space in the end, so the
+ * reservation may come from the global reserve when the filesystem is full
+ * (-ENOSPC) and is not subject to the qgroup limit (-EDQUOT). Otherwise a
+ * failed enable could never be cleaned up in either situation.
+ */
+static struct btrfs_trans_handle *start_verity_cleanup_trans(struct btrfs_root *root,
+							     unsigned int num_items)
+{
+	return btrfs_start_transaction_fallback_global_rsv(root, num_items);
+}
+
 /*
  * Drop all the items for this inode with this key_type.
  *
@@ -120,7 +134,7 @@ static int drop_verity_items(struct btrfs_inode *inode, u8 key_type)
 
 	while (1) {
 		/* 1 for the item being dropped */
-		trans = btrfs_start_transaction(root, 1);
+		trans = start_verity_cleanup_trans(root, 1);
 		if (IS_ERR(trans))
 			return PTR_ERR(trans);
 
@@ -466,7 +480,7 @@ static int rollback_verity(struct btrfs_inode *inode)
 	 * 1 for updating the inode flag
 	 * 1 for deleting the orphan
 	 */
-	trans = btrfs_start_transaction(root, 2);
+	trans = start_verity_cleanup_trans(root, 2);
 	if (IS_ERR(trans)) {
 		ret = PTR_ERR(trans);
 		trans = NULL;
Signature · 1 line
2.53.0
author maintainer
#200 lore raw

PATCH 197/438 ASoC: ux500: Parenthesize MSP_{RX,TX}_CLKPOL_BIT() arguments

In reply to Greg Kroah-Hartman

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

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

[ Upstream commit 11fc0048a6930f4fca44fe3bd16a0023e78846a2 ]

arm allmodconfig fails to build with gcc:

In file included from sound/soc/ux500/ux500_msp_i2s.c:20:
sound/soc/ux500/ux500_msp_i2s.h:151:38: error: suggest parentheses around arithmetic in operand of '^' [-Werror=parentheses] sound/soc/ux500/ux500_msp_i2s.c:204:21: note: in expansion of macro 'MSP_TX_CLKPOL_BIT'
cc1: all warnings being treated as errors

The macros never parenthesized their argument:

  #define MSP_TX_CLKPOL_BIT(n)  ((n & TCKPOL_MASK) << TCKPOL_SHIFT)

That went unnoticed while every caller passed a plain variable, but configure_protocol() now passes an XOR expression, which binds as "a ^ (b & MASK)" rather than "(a ^ b) & MASK", and gcc rightly complains.

No functional change: tx_clk_pol and rx_clk_pol only ever hold MSP_FALLING_EDGE (0) or MSP_RISING_EDGE (1), and bclk_inverted is a bool, so masking before or after the XOR gives the same 0/1 result. Parenthesize the argument anyway - it fixes the build and stops the macros from silently mis-evaluating a future composite argument.

---
 sound/soc/ux500/ux500_msp_i2s.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
sound/soc/ux500/ux500_msp_i2s.h +2 -2
index 2bf2699bdc49f..c66ef455e1380 100644
@@ -147,8 +147,8 @@ enum msp_direction {
 #define RCKPOL_MASK		BIT(0)
 #define TCKPOL_MASK		BIT(0)
 #define SPICKM_MASK		(BIT(1) | BIT(0))
-#define MSP_RX_CLKPOL_BIT(n)     ((n & RCKPOL_MASK) << RCKPOL_SHIFT)
-#define MSP_TX_CLKPOL_BIT(n)     ((n & TCKPOL_MASK) << TCKPOL_SHIFT)
+#define MSP_RX_CLKPOL_BIT(n)     (((n) & RCKPOL_MASK) << RCKPOL_SHIFT)
+#define MSP_TX_CLKPOL_BIT(n)     (((n) & TCKPOL_MASK) << TCKPOL_SHIFT)
 
 #define P1ELEN_SHIFT		0
 #define P1FLEN_SHIFT		3
Signature · 1 line
2.53.0
author maintainer
#201 lore raw

PATCH 198/438 ASoC: hdmi-codec: Report a change when the channel status moves

In reply to Greg Kroah-Hartman

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

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

From: HyeongJun An <[email protected]>

[ Upstream commit c17ae8c26eac16ad244daef44044d714f68a2ddc ]

The put() callback of "IEC958 Playback Default" stores all 24 channel status bytes and then returns 0. The core notifies userspace only on a positive return, so a write that changes what the get() callback hands back is never announced, and a mixer holding the control open keeps showing the old value.

Compare the stored bytes and return 1 when they move, the way snd_hda_spdif_default_put() does.

The same shape is in img-spdif-out and uniperif_player.

No board with this codec was to hand. The change is a comparison of driver state with no hardware behaviour in it, and mixer-test counts the missing notification as event_missing.

Fixes 7a8e1d44211e ("ASoC: hdmi-codec: Add iec958 controls")
Assisted-by Claude:claude-opus-5
---
 sound/soc/codecs/hdmi-codec.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)
sound/soc/codecs/hdmi-codec.c +5 -1
index 13ae9e83bc21f..2db9cf8ec6e96 100644
@@ -425,10 +425,14 @@ static int hdmi_codec_iec958_default_put(struct snd_kcontrol *kcontrol,
 	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
 	struct hdmi_codec_priv *hcp = snd_soc_component_get_drvdata(component);
 
+	if (!memcmp(hcp->iec_status, ucontrol->value.iec958.status,
+		    sizeof(hcp->iec_status)))
+		return 0;
+
 	memcpy(hcp->iec_status, ucontrol->value.iec958.status,
 	       sizeof(hcp->iec_status));
 
-	return 0;
+	return 1;
 }
 
 static int hdmi_codec_iec958_mask_get(struct snd_kcontrol *kcontrol,
Signature · 1 line
2.53.0
author maintainer
#202 lore raw

PATCH 199/438 ASoC: cs-amp-lib: Prevent NULL pointer if efi variable is zero length

In reply to Greg Kroah-Hartman

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

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

From: Richard Fitzgerald <[email protected]>

[ Upstream commit 3482062c786ce4233f8ed3224d824184f53ec154 ]

In cs_amp_alloc_get_efi_variable() the first call to cs_amp_get_efi_variable() might return EFI_SUCCESS if the variable exists with zero length. Trap this and return -ENOENT to prevent returning an unexpected NULL pointer.

The first cs_amp_get_efi_variable() call was assumed to return EFI_BUFFER_TOO_SMALL if the variable existed, but if instead it returned EFI_SUCCESS this would be converted to 0 by cs_amp_convert_efi_status() and then be returned as a NULL pointer.

Fixes 00fd40bc7acec ("ASoC: cs-amp-lib: Support Dell SSIDExV2 UEFI variable")
---
 sound/soc/codecs/cs-amp-lib.c | 2 ++
 1 file changed, 2 insertions(+)
sound/soc/codecs/cs-amp-lib.c +2 -0
index 371e99205b58e..58b06e6878b97 100644
@@ -314,6 +314,8 @@ static void *cs_amp_alloc_get_efi_variable(efi_char16_t *name,
 	unsigned long size = 0;
 
 	status = cs_amp_get_efi_variable(name, guid, NULL, &size, NULL);
+	if (status == EFI_SUCCESS)
+		return ERR_PTR(-ENOENT);
 	if (status != EFI_BUFFER_TOO_SMALL)
 		return ERR_PTR(cs_amp_convert_efi_status(status));
 
Signature · 1 line
2.53.0
author maintainer Applied
#203 lore raw

PATCH 200/438 ASoC: amd: acp: bounds-check SoundWire link ID in machine drivers

In reply to Greg Kroah-Hartman

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

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

From: Vijendar Mukunda <[email protected]>

[ Upstream commit 29218a4d11a31a8157389bc2b9e62dd768d7ea42 ]

Add a bounds check in create_sdw_dailink() to validate that the SoundWire link ID derived from link_mask does not exceed the maximum supported by the platform. If the link ID is out of range or link_mask is zero, log an error and return -EINVAL to prevent accessing invalid CPU pin ID tables.

Applied to both acp-sdw-sof-mach.c and acp-sdw-legacy-mach.c.

Fixes 6d8348ddc56e ("ASoC: amd: acp: refactor SoundWire machine driver code")
---
 sound/soc/amd/acp/acp-sdw-legacy-mach.c | 10 ++++++++++
 sound/soc/amd/acp/acp-sdw-sof-mach.c    |  9 +++++++++
 2 files changed, 19 insertions(+)
sound/soc/amd/acp/acp-sdw-legacy-mach.c +10 -0
index e8b6819cc4b45..1a6be664be279 100644
@@ -205,6 +205,16 @@ static int create_sdw_dailink(struct snd_soc_card *card,
 			return -EINVAL;
 		}
 
+		if (!soc_end->link_mask) {
+			dev_err(dev, "invalid zero link_mask\n");
+			return -EINVAL;
+		}
+		if ((ffs(soc_end->link_mask) - 1) >= amd_ctx->max_sdw_links) {
+			dev_err(dev, "link_id %d exceeds max_sdw_links %d\n",
+				ffs(soc_end->link_mask) - 1, amd_ctx->max_sdw_links);
+			return -EINVAL;
+		}
+
 		switch (amd_ctx->acp_rev) {
 		case ACP63_PCI_REV:
 			ret = get_acp63_cpu_pin_id(ffs(soc_end->link_mask - 1),
sound/soc/amd/acp/acp-sdw-sof-mach.c +9 -0
index a423853f3a97d..f66228435984a 100644
@@ -121,6 +121,15 @@ static int create_sdw_dailink(struct snd_soc_card *card,
 			return -EINVAL;
 		}
 
+		if (!sof_end->link_mask) {
+			dev_err(dev, "invalid zero link_mask\n");
+			return -EINVAL;
+		}
+		if ((ffs(sof_end->link_mask) - 1) >= amd_ctx->max_sdw_links) {
+			dev_err(dev, "link_id %d exceeds max_sdw_links %d\n",
+				ffs(sof_end->link_mask) - 1, amd_ctx->max_sdw_links);
+			return -EINVAL;
+		}
 		switch (amd_ctx->acp_rev) {
 		case ACP63_PCI_REV:
 			ret = get_acp63_cpu_pin_id(ffs(sof_end->link_mask - 1),
Signature · 1 line
2.53.0
author maintainer
#204 lore raw

PATCH 201/438 ASoC: sdw_utils: tidyup .count_sidecar

In reply to Greg Kroah-Hartman

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

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

From: Kuninori Morimoto <[email protected]>

[ Upstream commit c97f0bf5f705b16d150f2b0d5ce0ee24eee4f68a ]

count_sidecar() is not using *card. Tidyup it.

Current code makes old style / new style conversion difficult. To make future conversions easier to understand, this patch clean up the code a little. but no functional change.

Stable-dep-of: 0b7d55d3a912 ("ASoC: amd: acp: refactor codec config count in SOF SoundWire machine driver")

---
 include/sound/soc_sdw_utils.h                | 28 ++++++++++----------
 sound/soc/sdw_utils/soc_sdw_bridge_cs35l56.c |  4 +--
 sound/soc/sdw_utils/soc_sdw_utils.c          |  2 +-
 3 files changed, 16 insertions(+), 18 deletions(-)
include/sound/soc_sdw_utils.h +14 -14
index 79c21966220b2..443d63dc6ea37 100644
@@ -44,6 +44,18 @@
 
 struct asoc_sdw_codec_info;
 
+struct asoc_sdw_mc_private {
+	struct snd_soc_card card;
+	struct snd_soc_jack sdw_headset;
+	struct device *headset_codec_dev; /* only one headset per card */
+	struct device *amp_dev1, *amp_dev2;
+	bool append_dai_type;
+	bool ignore_internal_dmic;
+	void *private;
+	unsigned long mc_quirk;
+	int codec_info_list_count;
+};
+
 struct asoc_sdw_dai_info {
 	const bool direction[2]; /* playback & capture support */
 	const char *codec_name;
@@ -88,25 +100,13 @@ struct asoc_sdw_codec_info {
 
 	int (*codec_card_late_probe)(struct snd_soc_card *card);
 
-	int  (*count_sidecar)(struct snd_soc_card *card,
+	int  (*count_sidecar)(struct asoc_sdw_mc_private *ctx,
 			      int *num_dais, int *num_devs);
 	int  (*add_sidecar)(struct snd_soc_card *card,
 			    struct snd_soc_dai_link **dai_links,
 			    struct snd_soc_codec_conf **codec_conf);
 };
 
-struct asoc_sdw_mc_private {
-	struct snd_soc_card card;
-	struct snd_soc_jack sdw_headset;
-	struct device *headset_codec_dev; /* only one headset per card */
-	struct device *amp_dev1, *amp_dev2;
-	bool append_dai_type;
-	bool ignore_internal_dmic;
-	void *private;
-	unsigned long mc_quirk;
-	int codec_info_list_count;
-};
-
 struct asoc_sdw_endpoint {
 	struct list_head list;
 
@@ -235,7 +235,7 @@ int asoc_sdw_es9356_amp_init(struct snd_soc_card *card,
 int asoc_sdw_es9356_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link);
 
 /* CS AMP support */
-int asoc_sdw_bridge_cs35l56_count_sidecar(struct snd_soc_card *card,
+int asoc_sdw_bridge_cs35l56_count_sidecar(struct asoc_sdw_mc_private *ctx,
 					  int *num_dais, int *num_devs);
 int asoc_sdw_bridge_cs35l56_add_sidecar(struct snd_soc_card *card,
 					struct snd_soc_dai_link **dai_links,
sound/soc/sdw_utils/soc_sdw_bridge_cs35l56.c +1 -3
index e0e32a279787c..129a437ae397e 100644
@@ -99,11 +99,9 @@ static const struct snd_soc_dai_link bridge_dai_template = {
 	SND_SOC_DAILINK_REG(asoc_sdw_bridge_dai),
 };
 
-int asoc_sdw_bridge_cs35l56_count_sidecar(struct snd_soc_card *card,
+int asoc_sdw_bridge_cs35l56_count_sidecar(struct asoc_sdw_mc_private *ctx,
 					  int *num_dais, int *num_devs)
 {
-	struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
-
 	if (ctx->mc_quirk & SOC_SDW_SIDECAR_AMPS) {
 		(*num_dais)++;
 		(*num_devs) += ARRAY_SIZE(bridge_cs35l56_name_prefixes);
sound/soc/sdw_utils/soc_sdw_utils.c +1 -1
index ce2a9c28fe1a5..982be8a286133 100644
@@ -2045,7 +2045,7 @@ int asoc_sdw_parse_sdw_endpoints(struct snd_soc_card *card,
 			ctx->ignore_internal_dmic |= codec_info->ignore_internal_dmic;
 
 			if (codec_info->count_sidecar && codec_info->add_sidecar) {
-				ret = codec_info->count_sidecar(card, &num_dais, num_devs);
+				ret = codec_info->count_sidecar(ctx, &num_dais, num_devs);
 				if (ret)
 					return ret;
 
Signature · 1 line
2.53.0
author maintainer
#205 lore raw

PATCH 202/438 ASoC: sdw_utils: tidyup asoc_sdw_parse_sdw_endpoints()

In reply to Greg Kroah-Hartman

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

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

From: Kuninori Morimoto <[email protected]>

[ Upstream commit a1332be2a07090cf422507ec812ce2b9ba0a558a ]

We can avoid to use *card. Tidyup it.

Current code makes old style / new style conversion difficult. To make future conversions easier to understand, this patch clean up the code a little. but no functional change.

Stable-dep-of: 0b7d55d3a912 ("ASoC: amd: acp: refactor codec config count in SOF SoundWire machine driver")

---
 include/sound/soc_sdw_utils.h           | 3 ++-
 sound/soc/amd/acp/acp-sdw-legacy-mach.c | 2 +-
 sound/soc/amd/acp/acp-sdw-sof-mach.c    | 2 +-
 sound/soc/intel/boards/sof_sdw.c        | 2 +-
 sound/soc/sdw_utils/soc_sdw_utils.c     | 5 ++---
 5 files changed, 7 insertions(+), 7 deletions(-)
include/sound/soc_sdw_utils.h +2 -1
index 443d63dc6ea37..9b28e9aef4f1d 100644
@@ -182,7 +182,8 @@ struct asoc_sdw_dailink *asoc_sdw_find_dailink(struct asoc_sdw_dailink *dailinks
 					       const struct snd_soc_acpi_endpoint *new);
 int asoc_sdw_get_dai_type(u32 type);
 
-int asoc_sdw_parse_sdw_endpoints(struct snd_soc_card *card,
+int asoc_sdw_parse_sdw_endpoints(struct device *dev,
+				 struct asoc_sdw_mc_private *ctx,
 				 struct snd_soc_aux_dev *soc_aux,
 				 struct asoc_sdw_dailink *soc_dais,
 				 struct asoc_sdw_endpoint *soc_ends,
sound/soc/amd/acp/acp-sdw-legacy-mach.c +1 -1
index 1a6be664be279..fa31bb848d9df 100644
@@ -442,7 +442,7 @@ static int soc_card_dai_links_create(struct snd_soc_card *card)
 	if (!soc_aux)
 		return -ENOMEM;
 
-	ret = asoc_sdw_parse_sdw_endpoints(card, soc_aux, soc_dais, soc_ends, &num_confs);
+	ret = asoc_sdw_parse_sdw_endpoints(dev, ctx, soc_aux, soc_dais, soc_ends, &num_confs);
 	if (ret < 0)
 		return ret;
 
sound/soc/amd/acp/acp-sdw-sof-mach.c +1 -1
index f66228435984a..5d0818ca918a0 100644
@@ -312,7 +312,7 @@ static int sof_card_dai_links_create(struct snd_soc_card *card)
 	if (!sof_aux)
 		return -ENOMEM;
 
-	ret = asoc_sdw_parse_sdw_endpoints(card, sof_aux, sof_dais, sof_ends, &num_devs);
+	ret = asoc_sdw_parse_sdw_endpoints(dev, ctx, sof_aux, sof_dais, sof_ends, &num_devs);
 	if (ret < 0)
 		return ret;
 
sound/soc/intel/boards/sof_sdw.c +1 -1
index c527d575d1ed6..1013e07052a37 100644
@@ -1288,7 +1288,7 @@ static int sof_card_dai_links_create(struct snd_soc_card *card)
 		goto err_dai;
 	}
 
-	ret = asoc_sdw_parse_sdw_endpoints(card, sof_aux, sof_dais, sof_ends, &num_confs);
+	ret = asoc_sdw_parse_sdw_endpoints(dev, ctx, sof_aux, sof_dais, sof_ends, &num_confs);
 	if (ret < 0)
 		goto err_end;
 
sound/soc/sdw_utils/soc_sdw_utils.c +2 -3
index 982be8a286133..befcad08135e7 100644
@@ -1976,14 +1976,13 @@ static int is_sdca_endpoint_present(struct device *dev,
 	return ret;
 }
 
-int asoc_sdw_parse_sdw_endpoints(struct snd_soc_card *card,
+int asoc_sdw_parse_sdw_endpoints(struct device *dev,
+				 struct asoc_sdw_mc_private *ctx,
 				 struct snd_soc_aux_dev *soc_aux,
 				 struct asoc_sdw_dailink *soc_dais,
 				 struct asoc_sdw_endpoint *soc_ends,
 				 int *num_devs)
 {
-	struct device *dev = card->dev;
-	struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
 	struct snd_soc_acpi_mach *mach = dev_get_platdata(dev);
 	struct snd_soc_acpi_mach_params *mach_params = &mach->mach_params;
 	const struct snd_soc_acpi_link_adr *adr_link;
Signature · 1 line
2.53.0
author maintainer
#206 lore raw

PATCH 203/438 ASoC: amd: acp: refactor codec config count in SOF SoundWire machine driver

In reply to Greg Kroah-Hartman

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

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

From: Vijendar Mukunda <[email protected]>

[ Upstream commit 0b7d55d3a91200f2b1ed710f525a944b0a7d6369 ]

num_devs was used both as the endpoint count and as the output for asoc_sdw_parse_sdw_endpoints(), which overwrites it with the codec configuration count. Introduce a separate num_confs variable to hold the codec conf count so the two values remain distinct across codec_conf allocation and card->num_configs assignment.

Fixes 6d8348ddc56e ("ASoC: amd: acp: refactor SoundWire machine driver code")
---
 sound/soc/amd/acp/acp-sdw-sof-mach.c | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)
sound/soc/amd/acp/acp-sdw-sof-mach.c +5 -3
index 5d0818ca918a0..f58ef8953dbc2 100644
@@ -286,6 +286,7 @@ static int sof_card_dai_links_create(struct snd_soc_card *card)
 	int num_devs = 0;
 	int num_ends = 0;
 	int num_aux = 0;
+	int num_confs;
 	int num_links;
 	int be_id = 0;
 	int ret;
@@ -296,6 +297,7 @@ static int sof_card_dai_links_create(struct snd_soc_card *card)
 		return ret;
 	}
 
+	num_confs = num_ends;
 	/* One per DAI link, worst case is a DAI link for every endpoint */
 	struct asoc_sdw_dailink *sof_dais __free(kfree) =
 		kzalloc_objs(*sof_dais, num_ends);
@@ -312,7 +314,7 @@ static int sof_card_dai_links_create(struct snd_soc_card *card)
 	if (!sof_aux)
 		return -ENOMEM;
 
-	ret = asoc_sdw_parse_sdw_endpoints(dev, ctx, sof_aux, sof_dais, sof_ends, &num_devs);
+	ret = asoc_sdw_parse_sdw_endpoints(dev, ctx, sof_aux, sof_dais, sof_ends, &num_confs);
 	if (ret < 0)
 		return ret;
 
@@ -324,7 +326,7 @@ static int sof_card_dai_links_create(struct snd_soc_card *card)
 
 	dev_dbg(dev, "sdw %d, dmic %d", sdw_be_num, dmic_num);
 
-	codec_conf = devm_kcalloc(dev, num_devs, sizeof(*codec_conf), GFP_KERNEL);
+	codec_conf = devm_kcalloc(dev, num_confs, sizeof(*codec_conf), GFP_KERNEL);
 	if (!codec_conf)
 		return -ENOMEM;
 
@@ -335,7 +337,7 @@ static int sof_card_dai_links_create(struct snd_soc_card *card)
 		return -ENOMEM;
 
 	card->codec_conf = codec_conf;
-	card->num_configs = num_devs;
+	card->num_configs = num_confs;
 	card->dai_link = dai_links;
 	card->num_links = num_links;
 	card->aux_dev = sof_aux;
Signature · 1 line
2.53.0
author maintainer
#207 lore raw

PATCH 204/438 ASoC: amd: acp: fix ffs() operator precedence for SoundWire link ID

In reply to Greg Kroah-Hartman

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

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

From: Vijendar Mukunda <[email protected]>

[ Upstream commit 27098aaf28b96ab4e6891709062c343566d4882b ]

ffs(link_mask - 1) computes ffs on (link_mask - 1) instead of subtracting 1 from the result of ffs(link_mask). For a typical power-of-2 link_mask this returns the wrong link ID, causing cpu_pin_id lookup to select the incorrect SoundWire manager.

Fix the operator precedence to ffs(link_mask) - 1 in both acp-sdw-sof-mach.c and acp-sdw-legacy-mach.c.

Fixes 6d8348ddc56e ("ASoC: amd: acp: refactor SoundWire machine driver code")
---
 sound/soc/amd/acp/acp-sdw-legacy-mach.c | 4 ++--
 sound/soc/amd/acp/acp-sdw-sof-mach.c    | 4 ++--
 2 files changed, 4 insertions(+), 4 deletions(-)
sound/soc/amd/acp/acp-sdw-legacy-mach.c +2 -2
index fa31bb848d9df..9e3d5d3ed1d75 100644
@@ -217,7 +217,7 @@ static int create_sdw_dailink(struct snd_soc_card *card,
 
 		switch (amd_ctx->acp_rev) {
 		case ACP63_PCI_REV:
-			ret = get_acp63_cpu_pin_id(ffs(soc_end->link_mask - 1),
+			ret = get_acp63_cpu_pin_id(ffs(soc_end->link_mask) - 1,
 						   *be_id, &cpu_pin_id, dev);
 			if (ret)
 				return ret;
@@ -225,7 +225,7 @@ static int create_sdw_dailink(struct snd_soc_card *card,
 		case ACP70_PCI_REV:
 		case ACP71_PCI_REV:
 		case ACP72_PCI_REV:
-			ret = get_acp70_cpu_pin_id(ffs(soc_end->link_mask - 1),
+			ret = get_acp70_cpu_pin_id(ffs(soc_end->link_mask) - 1,
 						   *be_id, &cpu_pin_id, dev);
 			if (ret)
 				return ret;
sound/soc/amd/acp/acp-sdw-sof-mach.c +2 -2
index f58ef8953dbc2..13885ca20168b 100644
@@ -132,7 +132,7 @@ static int create_sdw_dailink(struct snd_soc_card *card,
 		}
 		switch (amd_ctx->acp_rev) {
 		case ACP63_PCI_REV:
-			ret = get_acp63_cpu_pin_id(ffs(sof_end->link_mask - 1),
+			ret = get_acp63_cpu_pin_id(ffs(sof_end->link_mask) - 1,
 						   *be_id, &cpu_pin_id, dev);
 			if (ret)
 				return ret;
@@ -140,7 +140,7 @@ static int create_sdw_dailink(struct snd_soc_card *card,
 		case ACP70_PCI_REV:
 		case ACP71_PCI_REV:
 		case ACP72_PCI_REV:
-			ret = get_acp70_cpu_pin_id(ffs(sof_end->link_mask - 1),
+			ret = get_acp70_cpu_pin_id(ffs(sof_end->link_mask) - 1,
 						   *be_id, &cpu_pin_id, dev);
 			if (ret)
 				return ret;
Signature · 1 line
2.53.0
author maintainer
#208 lore raw

PATCH 205/438 net/sched: codel: bound the dropping loop per dequeue call

In reply to Greg Kroah-Hartman

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

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

From: Jamal Hadi Salim <[email protected]>

[ Upstream commit 7f4a5ec6258fd7c92633ec4b0493fc51166d9398 ]

The CoDel control law schedules the next drop one interval/sqrt(count) after the previous drop, using the configured interval (codel_params.interval). For very small intervals the scheduled step rounds down to zero, so the dropping loop in codel_dequeue() never advances and drains the entire backlog under the qdisc lock in one call - an unprivileged user can trigger a soft lockup this way.

Fix in the shared codel code used by both codel and fq_codel:

1. Make the control-law step at least 1 tick so the dropping loop always moves forward.

2. Cap the dropping loop at CODEL_MAX_DROPS_PER_DEQUEUE (256) drops per codel_dequeue() call, resyncing drop_next to now when the cap is hit: the catch-up owed to the loop grows with the idle gap and the backlog, which no interval threshold can bound. This is a deliberate behaviour change after long idle gaps.

The cap applies to fq_codel (4b549a2ef4be) and the mac80211 TXQ path (fixed interval, cap only).

The target sojourn delay (codel_params.target) is not validated: it does not feed the control law, so a sub-tick value is aggressive rather than deadlock-prone.

Conditions to recreate the bug:
- tc qdisc add dev lo root handle 1: tbf rate 1kbit burst 2kb limit 1000000
- tc qdisc add dev lo parent 1:1 handle 10: codel interval 2us target 1ms noecn limit 1000000 (same for fq_codel)
- unpatched kernel: tc accepts it; a UDP flood under the 1kbit tbf
soft-lockups (watchdog: BUG: soft lockup) while one
codel_dequeue() call drops the backlog under the qdisc lock
- patched kernel: same setup, at most 256 drops per dequeue call,
no soft lockup

Testing: claim reproducer and interval 2us/3us variants run clean; tdc qdisc category passes (see the selftests patch).

---
 include/net/codel.h      |  5 +++++
 include/net/codel_impl.h | 16 +++++++++++++++-
 2 files changed, 20 insertions(+), 1 deletion(-)
include/net/codel.h +5 -0
index aa80f744826cd..183d43c2bd434 100644
@@ -140,6 +140,11 @@ struct codel_vars {
 /* needed shift to get a Q0.32 number from rec_inv_sqrt */
 #define REC_INV_SQRT_SHIFT (32 - REC_INV_SQRT_BITS)
 
+/* Cap on drops per codel_dequeue() call: the loop's work depends on the
+ * idle gap and backlog, both outside our control; resync when exceeded.
+ */
+#define CODEL_MAX_DROPS_PER_DEQUEUE 256
+
 /**
  * struct codel_stats - contains codel shared variables and stats
  * @maxpacket:	largest packet we've seen so far
include/net/codel_impl.h +15 -1
index 2c1f0ec309e9f..8f26132d45b7f 100644
@@ -93,12 +93,17 @@ static void codel_Newton_step(struct codel_vars *vars)
  * CoDel control_law is t + interval/sqrt(count)
  * We maintain in rec_inv_sqrt the reciprocal value of sqrt(count) to avoid
  * both sqrt() and divide operation.
+ *
+ * Clamp the increment to at least 1 tick: a very small interval (or a
+ * large count) can truncate it to zero, stalling the dropping loop.
  */
 static codel_time_t codel_control_law(codel_time_t t,
 				      codel_time_t interval,
 				      u32 rec_inv_sqrt)
 {
-	return t + reciprocal_scale(interval, rec_inv_sqrt << REC_INV_SQRT_SHIFT);
+	return t + max_t(u32, 1,
+			 reciprocal_scale(interval,
+					  rec_inv_sqrt << REC_INV_SQRT_SHIFT));
 }
 
 static bool codel_should_drop(const struct sk_buff *skb,
@@ -154,6 +159,7 @@ static struct sk_buff *codel_dequeue(void *ctx,
 				     codel_skb_dequeue_t dequeue_func)
 {
 	struct sk_buff *skb = dequeue_func(vars, ctx);
+	unsigned int drops = 0;
 	codel_time_t now;
 	bool drop;
 
@@ -180,6 +186,14 @@ static struct sk_buff *codel_dequeue(void *ctx,
 			 */
 			while (vars->dropping &&
 			       codel_time_after_eq(now, vars->drop_next)) {
+				if (++drops > CODEL_MAX_DROPS_PER_DEQUEUE) {
+					/* fell far behind the schedule */
+					WRITE_ONCE(vars->drop_next,
+						   codel_control_law(now,
+								     params->interval,
+								     vars->rec_inv_sqrt));
+					break;
+				}
 				/* dont care of possible wrap
 				 * since there is no more divide.
 				 */
Signature · 1 line
2.53.0
author maintainer
#209 lore raw

PATCH 206/438 net: do not advance stack index from dev_fwd_path()

In reply to Greg Kroah-Hartman

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

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

From: Pablo Neira Ayuso <[email protected]>

[ Upstream commit 5deda60c56eeeab25beb10cf4d48e07587076b11 ]

Update stack index from dev_fill_forward_path() instead, once the forward path slot has been populated.

Stable-dep-of: 150dba2c69e9 ("net: remove WARN_ON_ONCE() from the dev_fill_forward_path() loop check")

---
 net/core/dev.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)
net/core/dev.c +4 -4
index 74dd9e25f4ff7..e68463e7310a0 100644
@@ -742,12 +742,10 @@ EXPORT_SYMBOL_GPL(dev_fill_metadata_dst);
 
 static struct net_device_path *dev_fwd_path(struct net_device_path_stack *stack)
 {
-	int k = stack->num_paths++;
-
-	if (k >= NET_DEVICE_PATH_STACK_MAX)
+	if (stack->num_paths + 1 > NET_DEVICE_PATH_STACK_MAX)
 		return NULL;
 
-	return &stack->path[k];
+	return &stack->path[stack->num_paths];
 }
 
 int dev_fill_forward_path(const struct net_device *dev, const u8 *daddr,
@@ -773,6 +771,7 @@ int dev_fill_forward_path(const struct net_device *dev, const u8 *daddr,
 		if (ret < 0)
 			return -1;
 
+		stack->num_paths++;
 		if (WARN_ON_ONCE(last_dev == ctx.dev))
 			return -1;
 	}
@@ -785,6 +784,7 @@ int dev_fill_forward_path(const struct net_device *dev, const u8 *daddr,
 		return -1;
 	path->type = DEV_PATH_ETHERNET;
 	path->dev = ctx.dev;
+	stack->num_paths++;
 
 	return ret;
 }
Signature · 1 line
2.53.0
author maintainer
#210 lore raw

PATCH 207/438 net: pass dst via net_device_path in dev_fill_forward_path()

In reply to Greg Kroah-Hartman

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

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

From: Pablo Neira Ayuso <[email protected]>

[ Upstream commit 0ad8404e776698de4dac5cc0df3be68d344e741c ]

Add dst_entry to tunnel device path, this will allow us to remove a duplicated route lookup.

This is a preparation patch to retrieve the tunnel route directly from the .fill_forward_path. This new dst_entry in the tunnel will be used by a follow up patch.

Since dst_release() works fine on NULL interface, this is still noop until the flowtable starts using this.

Add a new dev_fill_forward_path_release() function to drop the refcount on the tunnel device route and use it in case of error out. Export it so to drop the refcount on the tunnel route at a later stage.

Adjust existing drivers that recycle dev_fill_forward_path() to call dev_fill_forward_path_release() for safety reasons.

Stable-dep-of: 150dba2c69e9 ("net: remove WARN_ON_ONCE() from the dev_fill_forward_path() loop check")

---
 drivers/net/ethernet/airoha/airoha_ppe.c      | 10 ++++--
 .../net/ethernet/mediatek/mtk_ppe_offload.c   | 10 ++++--
 include/linux/netdevice.h                     |  2 ++
 net/core/dev.c                                | 36 ++++++++++++++++---
 4 files changed, 47 insertions(+), 11 deletions(-)
drivers/net/ethernet/airoha/airoha_ppe.c +7 -3
index f6396925722d0..c8fd95e800937 100644
@@ -293,14 +293,18 @@ static int airoha_ppe_get_wdma_info(struct net_device *dev, const u8 *addr,
 		return err;
 
 	path = &stack.path[stack.num_paths - 1];
-	if (path->type != DEV_PATH_MTK_WDMA)
-		return -EINVAL;
+	if (path->type != DEV_PATH_MTK_WDMA) {
+		err = -EINVAL;
+		goto err_out;
+	}
 
 	info->idx = path->mtk_wdma.wdma_idx;
 	info->bss = path->mtk_wdma.bss;
 	info->wcid = path->mtk_wdma.wcid;
+err_out:
+	dev_fill_forward_path_release(&stack);
 
-	return 0;
+	return err;
 }
 
 static int airoha_get_dsa_port(struct net_device **dev)
drivers/net/ethernet/mediatek/mtk_ppe_offload.c +7 -3
index cc8c4ef8038f3..771d9118f94ad 100644
@@ -108,16 +108,20 @@ mtk_flow_get_wdma_info(struct net_device *dev, const u8 *addr, struct mtk_wdma_i
 		return err;
 
 	path = &stack.path[stack.num_paths - 1];
-	if (path->type != DEV_PATH_MTK_WDMA)
-		return -1;
+	if (path->type != DEV_PATH_MTK_WDMA) {
+		err = -EINVAL;
+		goto err_out;
+	}
 
 	info->wdma_idx = path->mtk_wdma.wdma_idx;
 	info->queue = path->mtk_wdma.queue;
 	info->bss = path->mtk_wdma.bss;
 	info->wcid = path->mtk_wdma.wcid;
 	info->amsdu = path->mtk_wdma.amsdu;
+err_out:
+	dev_fill_forward_path_release(&stack);
 
-	return 0;
+	return err;
 }
 
 
include/linux/netdevice.h +2 -0
index 8840b126979ff..d325027b03705 100644
@@ -894,6 +894,7 @@ struct net_device_path {
 			u8		h_dest[ETH_ALEN];
 		} encap;
 		struct {
+			struct dst_entry *dst;
 			union {
 				struct in_addr	src_v4;
 				struct in6_addr	src_v6;
@@ -3424,6 +3425,7 @@ int dev_get_iflink(const struct net_device *dev);
 int dev_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb);
 int dev_fill_forward_path(const struct net_device *dev, const u8 *daddr,
 			  struct net_device_path_stack *stack);
+void dev_fill_forward_path_release(struct net_device_path_stack *stack);
 struct net_device *dev_get_by_name(struct net *net, const char *name);
 struct net_device *dev_get_by_name_rcu(struct net *net, const char *name);
 struct net_device *__dev_get_by_name(struct net *net, const char *name);
net/core/dev.c +31 -5
index e68463e7310a0..2b493f4c2f0cb 100644
@@ -748,6 +748,27 @@ static struct net_device_path *dev_fwd_path(struct net_device_path_stack *stack)
 	return &stack->path[stack->num_paths];
 }
 
+void dev_fill_forward_path_release(struct net_device_path_stack *stack)
+{
+	struct net_device_path *path;
+	int k;
+
+	if (stack->num_paths == 0)
+		return;
+
+	for (k = stack->num_paths - 1; k >= 0; k--) {
+		path = &stack->path[k];
+		switch (path->type) {
+		case DEV_PATH_TUN:
+			dst_release(path->tun.dst);
+			break;
+		default:
+			break;
+		}
+	}
+}
+EXPORT_SYMBOL_GPL(dev_fill_forward_path_release);
+
 int dev_fill_forward_path(const struct net_device *dev, const u8 *daddr,
 			  struct net_device_path_stack *stack)
 {
@@ -764,16 +785,16 @@ int dev_fill_forward_path(const struct net_device *dev, const u8 *daddr,
 		last_dev = ctx.dev;
 		path = dev_fwd_path(stack);
 		if (!path)
-			return -1;
+			goto err_out;
 
 		memset(path, 0, sizeof(struct net_device_path));
 		ret = ctx.dev->netdev_ops->ndo_fill_forward_path(&ctx, path);
 		if (ret < 0)
-			return -1;
+			goto err_out;
 
 		stack->num_paths++;
 		if (WARN_ON_ONCE(last_dev == ctx.dev))
-			return -1;
+			goto err_out;
 	}
 
 	if (!ctx.dev)
@@ -781,12 +802,17 @@ int dev_fill_forward_path(const struct net_device *dev, const u8 *daddr,
 
 	path = dev_fwd_path(stack);
 	if (!path)
-		return -1;
+		goto err_out;
+
 	path->type = DEV_PATH_ETHERNET;
 	path->dev = ctx.dev;
 	stack->num_paths++;
 
-	return ret;
+	return 0;
+err_out:
+	dev_fill_forward_path_release(stack);
+
+	return -1;
 }
 EXPORT_SYMBOL_GPL(dev_fill_forward_path);
 
Signature · 1 line
2.53.0
author maintainer
#211 lore raw

PATCH 208/438 net: pass net_device_path_ctx to dev_fill_forward_path()

In reply to Greg Kroah-Hartman

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

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

From: Lorenzo Bianconi <[email protected]>

[ Upstream commit 95133a416809c7e822da4023b7f4193ef2620796 ]

Refactor dev_fill_forward_path() to take a struct net_device_path_ctx pointer instead of a (dev, daddr) pair, so the caller can build and populate the context up front and keep it after the forward path walk.

This allows additional fields (e.g. vlan and ether_type) to be carried in the context and shared with ndo_fill_forward_path implementations, instead of being reconstructed on the stack inside the core helper.

Update the mtk_ppe_offload, airoha_ppe and nf_flow_table_path callers to allocate and fill the context before invoking dev_fill_forward_path(). The network topology resolution behaviour is unchanged.

This is a preliminary patch to enable HW flowtable offload for IPv4 over IPv6 tunnels.

Stable-dep-of: 150dba2c69e9 ("net: remove WARN_ON_ONCE() from the dev_fill_forward_path() loop check")

---
 drivers/net/ethernet/airoha/airoha_ppe.c       |  7 ++++++-
 .../net/ethernet/mediatek/mtk_ppe_offload.c    |  7 ++++++-
 include/linux/netdevice.h                      |  2 +-
 net/core/dev.c                                 | 18 +++++++-----------
 net/netfilter/nf_flow_table_path.c             |  7 ++++++-
 5 files changed, 26 insertions(+), 15 deletions(-)
drivers/net/ethernet/airoha/airoha_ppe.c +6 -1
index c8fd95e800937..bc2b4deafd83a 100644
@@ -280,14 +280,19 @@ static int airoha_ppe_get_wdma_info(struct net_device *dev, const u8 *addr,
 				    struct airoha_wdma_info *info)
 {
 	struct net_device_path_stack stack;
+	struct net_device_path_ctx ctx = {
+		.dev = dev,
+	};
 	struct net_device_path *path;
 	int err;
 
 	if (!dev)
 		return -ENODEV;
 
+	ether_addr_copy(ctx.daddr, addr);
+
 	rcu_read_lock();
-	err = dev_fill_forward_path(dev, addr, &stack);
+	err = dev_fill_forward_path(&ctx, &stack);
 	rcu_read_unlock();
 	if (err)
 		return err;
drivers/net/ethernet/mediatek/mtk_ppe_offload.c +6 -1
index 771d9118f94ad..99b28aaa7cc47 100644
@@ -92,6 +92,9 @@ static int
 mtk_flow_get_wdma_info(struct net_device *dev, const u8 *addr, struct mtk_wdma_info *info)
 {
 	struct net_device_path_stack stack;
+	struct net_device_path_ctx ctx = {
+		.dev = dev,
+	};
 	struct net_device_path *path;
 	int err;
 
@@ -101,8 +104,10 @@ mtk_flow_get_wdma_info(struct net_device *dev, const u8 *addr, struct mtk_wdma_i
 	if (!IS_ENABLED(CONFIG_NET_MEDIATEK_SOC_WED))
 		return -1;
 
+	ether_addr_copy(ctx.daddr, addr);
+
 	rcu_read_lock();
-	err = dev_fill_forward_path(dev, addr, &stack);
+	err = dev_fill_forward_path(&ctx, &stack);
 	rcu_read_unlock();
 	if (err)
 		return err;
include/linux/netdevice.h +1 -1
index d325027b03705..6a86a211fff57 100644
@@ -3423,7 +3423,7 @@ void dev_remove_offload(struct packet_offload *po);
 
 int dev_get_iflink(const struct net_device *dev);
 int dev_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb);
-int dev_fill_forward_path(const struct net_device *dev, const u8 *daddr,
+int dev_fill_forward_path(struct net_device_path_ctx *ctx,
 			  struct net_device_path_stack *stack);
 void dev_fill_forward_path_release(struct net_device_path_stack *stack);
 struct net_device *dev_get_by_name(struct net *net, const char *name);
net/core/dev.c +7 -11
index 2b493f4c2f0cb..679e75d3699ae 100644
@@ -769,35 +769,31 @@ void dev_fill_forward_path_release(struct net_device_path_stack *stack)
 }
 EXPORT_SYMBOL_GPL(dev_fill_forward_path_release);
 
-int dev_fill_forward_path(const struct net_device *dev, const u8 *daddr,
+int dev_fill_forward_path(struct net_device_path_ctx *ctx,
 			  struct net_device_path_stack *stack)
 {
 	const struct net_device *last_dev;
-	struct net_device_path_ctx ctx = {
-		.dev	= dev,
-	};
 	struct net_device_path *path;
 	int ret = 0;
 
-	memcpy(ctx.daddr, daddr, sizeof(ctx.daddr));
 	stack->num_paths = 0;
-	while (ctx.dev && ctx.dev->netdev_ops->ndo_fill_forward_path) {
-		last_dev = ctx.dev;
+	while (ctx->dev && ctx->dev->netdev_ops->ndo_fill_forward_path) {
+		last_dev = ctx->dev;
 		path = dev_fwd_path(stack);
 		if (!path)
 			goto err_out;
 
 		memset(path, 0, sizeof(struct net_device_path));
-		ret = ctx.dev->netdev_ops->ndo_fill_forward_path(&ctx, path);
+		ret = ctx->dev->netdev_ops->ndo_fill_forward_path(ctx, path);
 		if (ret < 0)
 			goto err_out;
 
 		stack->num_paths++;
-		if (WARN_ON_ONCE(last_dev == ctx.dev))
+		if (WARN_ON_ONCE(last_dev == ctx->dev))
 			goto err_out;
 	}
 
-	if (!ctx.dev)
+	if (!ctx->dev)
 		return ret;
 
 	path = dev_fwd_path(stack);
@@ -805,7 +801,7 @@ int dev_fill_forward_path(const struct net_device *dev, const u8 *daddr,
 		goto err_out;
 
 	path->type = DEV_PATH_ETHERNET;
-	path->dev = ctx.dev;
+	path->dev = ctx->dev;
 	stack->num_paths++;
 
 	return 0;
net/netfilter/nf_flow_table_path.c +6 -1
index 98c03b487f521..b9df7453f7620 100644
@@ -50,6 +50,9 @@ static int nft_dev_fill_forward_path(const struct nf_flow_route *route,
 {
 	const void *daddr = &ct->tuplehash[!dir].tuple.src.u3;
 	struct net_device *dev = dst_cache->dev;
+	struct net_device_path_ctx ctx = {
+		.dev = dev,
+	};
 	struct neighbour *n;
 	u8 nud_state;
 
@@ -72,7 +75,9 @@ static int nft_dev_fill_forward_path(const struct nf_flow_route *route,
 		return -1;
 
 out:
-	return dev_fill_forward_path(dev, ha, stack);
+	ether_addr_copy(ctx.daddr, ha);
+
+	return dev_fill_forward_path(&ctx, stack);
 }
 
 struct nft_forward_info {
Signature · 1 line
2.53.0
author maintainer
#212 lore raw

PATCH 209/438 net: remove WARN_ON_ONCE() from the dev_fill_forward_path() loop check

In reply to Greg Kroah-Hartman

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

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

From: Farhad Alemi <[email protected]>

[ Upstream commit 150dba2c69e93302af24a0c868eebe4871e2e107 ]

ipip_fill_forward_path() and ip6_tnl_fill_forward_path() look up the route to the tunnel's remote endpoint and set ctx->dev to its device, which is the tunnel itself when that route resolves back to the tunnel. dev_fill_forward_path() then makes no progress and trips WARN_ON_ONCE(last_dev == ctx->dev) as soon as a flowtable tries to offload a flow through the tunnel. That routing loop is a configuration any CAP_NET_ADMIN user can set up, and ip_tunnel_xmit() and ip6_tnl_xmit() already treat it as a tx error, so remove the warning and just fail the walk, as commit 008e7a7c293b ("net: remove WARN_ON_ONCE when accessing forward path array") did for the path stack overflow.

---
 net/core/dev.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
net/core/dev.c +1 -1
index 679e75d3699ae..ff25e4c71588e 100644
@@ -789,7 +789,7 @@ int dev_fill_forward_path(struct net_device_path_ctx *ctx,
 			goto err_out;
 
 		stack->num_paths++;
-		if (WARN_ON_ONCE(last_dev == ctx->dev))
+		if (last_dev == ctx->dev)
 			goto err_out;
 	}
 
Signature · 1 line
2.53.0
author maintainer
#213 lore raw

PATCH 210/438 net: netsec: fix device_node reference leak on phy_np

In reply to Greg Kroah-Hartman

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

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

From: Yige Jiang <[email protected]>

[ Upstream commit 5ae916fabca141b79b32e2e57f3c915c0f1e1b2e ]

netsec_of_probe() takes a reference on the PHY device_node with of_parse_phandle() and stores it in priv->phy_np, but the driver never drops it. One device_node reference is leaked per probe, on the success path as well as on every error path reached after netsec_of_probe().

Neither consumer takes ownership. of_mdio_parse_addr() is a static inline taking a const struct device_node * that only reads the "reg" property. of_phy_connect() borrows as well: of_phy_get_and_connect() in drivers/net/mdio/of_mdio.c brackets its own call with of_node_get() at :364 and of_node_put() at :373, which would be a double put if of_phy_connect() consumed the reference.

The node is still in use at netsec_netdev_open() time, where it is passed to of_phy_connect(), so it has device lifetime. Release it at the probe error label, which every failure path after the acquire funnels through, and in netsec_remove(). Both releases precede free_netdev(), since priv is netdev_priv(ndev). The ACPI probe path leaves priv->phy_np NULL and of_node_put(NULL) is a no-op.

There is no end-user visible symptom on currently supported platforms:
a device_node is only freed once OF_DYNAMIC is enabled and the node has been detached, so on a static device tree the imbalance is inert. It is observable as a refcount that grows across bind/unbind cycles, and would matter under device tree overlays.

Found by static analysis of reference acquire/release pairing rather than from a runtime report. No reproducer was produced and the change has not been runtime tested; it is compile-tested only (arm64, CONFIG_SNI_NETSEC=m via COMPILE_TEST).

Fixes 533dd11a12f6 ("net: socionext: Add Synquacer NetSec driver")
---
 drivers/net/ethernet/socionext/netsec.c | 2 ++
 1 file changed, 2 insertions(+)
drivers/net/ethernet/socionext/netsec.c +2 -0
index d14a6584473c8..79a0a324c921d 100644
@@ -2149,6 +2149,7 @@ static int netsec_probe(struct platform_device *pdev)
 	pm_runtime_put_sync(&pdev->dev);
 	pm_runtime_disable(&pdev->dev);
 free_ndev:
+	of_node_put(priv->phy_np);
 	free_netdev(ndev);
 	dev_err(&pdev->dev, "init failed\n");
 
@@ -2166,6 +2167,7 @@ static void netsec_remove(struct platform_device *pdev)
 	netif_napi_del(&priv->napi);
 
 	pm_runtime_disable(&pdev->dev);
+	of_node_put(priv->phy_np);
 	free_netdev(priv->ndev);
 }
 
Signature · 1 line
2.53.0
author maintainer
#214 lore raw

PATCH 211/438 eth: fbnic: ring the doorbell if a burst ends in a drop

In reply to Greg Kroah-Hartman

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

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

From: Jakub Kicinski <[email protected]>

[ Upstream commit 490599ab23134962a6d18a024e84541d77bdb999 ]

fbnic_tx_map() skips the doorbell write, and the completion request, for every packet handed to it with xmit_more set, counting on the packet which ends the burst to publish them all. When that packet is dropped instead - skb_put_padto(), skb_cow_head() or a DMA mapping failure - nothing rings. The descriptors of the preceding packets stay invisible to the HW until the next transmit on that queue, which for a burst-then-idle workload may never come.

Remember the meta descriptor of the last packet left without a doorbell and flush it from the error paths. The completion request has to be set on that descriptor rather than simply writing the tail, otherwise the HW would transmit the packets but never report a head, and the ring would fill up and stall for good.

This is very similar to Joe's recent series of fixes for bnxt. Not seen in real life, reproduced under QEMU with failure injection.

---
 drivers/net/ethernet/meta/fbnic/fbnic_txrx.c | 42 +++++++++++++++-----
 drivers/net/ethernet/meta/fbnic/fbnic_txrx.h |  9 ++++-
 2 files changed, 40 insertions(+), 11 deletions(-)
drivers/net/ethernet/meta/fbnic/fbnic_txrx.c +33 -9
index 401f8b8ae1cae..e7918d3f6aba9 100644
@@ -311,6 +311,29 @@ fbnic_rx_csum(u64 rcd, struct sk_buff *skb, struct fbnic_ring *rcq,
 	}
 }
 
+static void fbnic_tx_doorbell(struct fbnic_ring *ring, __le64 *meta)
+{
+	*meta |= cpu_to_le64(FBNIC_TWD_FLAG_REQ_COMPLETION);
+	ring->deferred_meta = -1;
+
+	/* Force DMA writes to flush before writing to tail */
+	dma_wmb();
+
+	writel(ring->tail, ring->doorbell);
+}
+
+/* Packets handed to us with xmit_more set are left in the ring without a
+ * doorbell, and without a completion request, in the expectation that the
+ * packet ending the burst will ring for all of them. If that packet gets
+ * dropped instead we have to ring here, otherwise the descriptors sit in
+ * the ring until the next transmit, which may never come.
+ */
+static void fbnic_tx_flush_doorbell(struct fbnic_ring *ring)
+{
+	if (ring->deferred_meta >= 0)
+		fbnic_tx_doorbell(ring, &ring->desc[ring->deferred_meta]);
+}
+
 static bool
 fbnic_tx_map(struct fbnic_ring *ring, struct sk_buff *skb, __le64 *meta)
 {
@@ -378,14 +401,10 @@ fbnic_tx_map(struct fbnic_ring *ring, struct sk_buff *skb, __le64 *meta)
 	/* Verify there is room for another packet */
 	fbnic_maybe_stop_tx(skb->dev, ring, FBNIC_MAX_SKB_DESC);
 
-	if (fbnic_tx_sent_queue(skb, ring)) {
-		*meta |= cpu_to_le64(FBNIC_TWD_FLAG_REQ_COMPLETION);
-
-		/* Force DMA writes to flush before writing to tail */
-		dma_wmb();
-
-		writel(tail, ring->doorbell);
-	}
+	if (fbnic_tx_sent_queue(skb, ring))
+		fbnic_tx_doorbell(ring, meta);
+	else
+		ring->deferred_meta = meta - ring->desc;
 
 	return false;
 dma_error:
@@ -425,8 +444,10 @@ fbnic_xmit_frame_ring(struct sk_buff *skb, struct fbnic_ring *ring)
 	 * otherwise try next time
 	 */
 	desc_needed = skb_shinfo(skb)->nr_frags + 10;
-	if (fbnic_maybe_stop_tx(skb->dev, ring, desc_needed))
+	if (fbnic_maybe_stop_tx(skb->dev, ring, desc_needed)) {
+		fbnic_tx_flush_doorbell(ring);
 		return NETDEV_TX_BUSY;
+	}
 
 	*meta = cpu_to_le64(FBNIC_TWD_FLAG_DEST_MAC);
 
@@ -447,6 +468,8 @@ fbnic_xmit_frame_ring(struct sk_buff *skb, struct fbnic_ring *ring)
 err_free:
 	dev_kfree_skb_any(skb);
 err_count:
+	fbnic_tx_flush_doorbell(ring);
+
 	u64_stats_update_begin(&ring->stats.syncp);
 	ring->stats.dropped++;
 	u64_stats_update_end(&ring->stats.syncp);
@@ -2491,6 +2514,7 @@ static void fbnic_enable_twq0(struct fbnic_ring *twq)
 	fbnic_ring_wr32(twq, FBNIC_QUEUE_TWQ0_CTL, FBNIC_QUEUE_TWQ_CTL_RESET);
 	twq->tail = 0;
 	twq->head = 0;
+	twq->deferred_meta = -1;
 
 	/* Store descriptor ring address and size */
 	fbnic_ring_wr32(twq, FBNIC_QUEUE_TWQ0_BAL, lower_32_bits(twq->dma));
drivers/net/ethernet/meta/fbnic/fbnic_txrx.h +7 -2
index e03c9d2c38dca..f5899446dcc51 100644
@@ -128,9 +128,14 @@ struct fbnic_ring {
 		/* Rx BDQs only */
 		struct page_pool *page_pool;
 
-		/* Deferred_head is used to cache the head for TWQ1 if
+		/* TWQ0 only, index of the meta descriptor of the last packet
+		 * placed in the ring without ringing the doorbell, -1 if the
+		 * doorbell is in sync with the tail.
+		 */
+		s32 deferred_meta;
+
+		/* TCQ only, used to cache the head for TWQ1 if
 		 * an attempt is made to clean TWQ1 with zero napi_budget.
-		 * We do not use it for any other ring.
 		 */
 		s32 deferred_head;
 	};
Signature · 1 line
2.53.0
author maintainer
#215 lore raw

PATCH 212/438 net: lock the socket in sock_gettstamp()

In reply to Greg Kroah-Hartman

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

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

From: Eric Dumazet <[email protected]>

[ Upstream commit 9ed55f3dbef4f4adfe65eb03b0c35c53229a8490 ]

sk->sk_flags must only be changed while holding the socket lock, because sock_set_flag() and sock_reset_flag() use non atomic operations (__set_bit() and __clear_bit()).

sock_gettstamp() is one of the last places where a bit of sk->sk_flags is changed from a syscall without owning the socket lock, through sock_enable_timestamp(sk, SOCK_TIMESTAMP).

sk_set_memalloc() and sk_clear_memalloc() also change sk->sk_flags without the socket lock, but their callers (nbd, iscsi_tcp, nvme-tcp, sunrpc, wireguard) need a careful audit, this will be addressed in a separate patch.

Jungwoo Lee and Wongi Lee reported an UDP socket use-after-free caused by this bug: a SIOCGSTAMPNS_NEW ioctl racing with bind() can cancel the SOCK_RCU_FREE bit that udp_lib_get_port() just set, because both threads perform a read-modify-write on the same word.

  CPU 0 (bind)                        CPU 1 (SIOCGSTAMPNS_NEW)
  --------------------------------    ----------------------------
  read sk_flags = F                   read sk_flags = F
  compute F | BIT(SOCK_RCU_FREE)      compute F | BIT(SOCK_TIMESTAMP)

store F | BIT(SOCK_RCU_FREE)
sk_add_node_rcu(sk, ...)
store F | BIT(SOCK_TIMESTAMP)

After the lost update, SOCK_RCU_FREE is clear while the socket is visible to lockless UDP receive lookups. sk_destruct() then frees the socket immediately instead of waiting for a RCU grace period, while the receive path still holds a reference-less pointer to it:

 BUG: KASAN: slab-use-after-free in ipv4_pktinfo_prepare+0x30/0x410
 Read of size 8 at addr ffff888008806610 by task exploit/207
 CPU: 0 UID: 1000 PID: 207 Comm: exploit Not tainted 6.12.95+ #1
  ipv4_pktinfo_prepare+0x30/0x410
  udp_queue_rcv_one_skb+0x51c/0x1180
  udp_unicast_rcv_skb+0x109/0x350
  ip_protocol_deliver_rcu+0x14b/0x310
  ip_local_deliver_finish+0x29d/0x390
  ip_local_deliver+0x24d/0x2a0

Only grab the socket lock when SOCK_TIMESTAMP has to be set, to keep the common case lockless.

---
 net/core/sock.c | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)
net/core/sock.c +8 -1
index 1ad41904db25b..5c1a1d1950752 100644
@@ -3908,7 +3908,14 @@ int sock_gettstamp(struct socket *sock, void __user *userstamp,
 	struct sock *sk = sock->sk;
 	struct timespec64 ts;
 
-	sock_enable_timestamp(sk, SOCK_TIMESTAMP);
+	/* sk->sk_flags must only be changed under the socket lock,
+	 * because sock_set_flag() uses non atomic operations.
+	 */
+	if (!sock_flag(sk, SOCK_TIMESTAMP)) {
+		lock_sock(sk);
+		sock_enable_timestamp(sk, SOCK_TIMESTAMP);
+		release_sock(sk);
+	}
 	ts = ktime_to_timespec64(sock_read_timestamp(sk));
 	if (ts.tv_sec == -1)
 		return -ENOENT;
Signature · 1 line
2.53.0
author maintainer
#216 lore raw

PATCH 213/438 net: ethernet: cortina: Ack RX overrun interrupt correctly

In reply to Greg Kroah-Hartman

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

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

From: Linus Walleij <[email protected]>

[ Upstream commit 1dd85662fee6e2ac580b1c4f9a0c0a7ae6e31f0e ]

The RX overrun interrupt is reported in interrupt status register 4, but gmac_irq() acknowledges it using the RX descriptor error bit from status register 0. For GMAC0 this writes the GMAC1 overrun bit, while for GMAC1 the shift leaves no bit in the 32-bit register.

Acknowledge the same per-port RX overrun bit that was detected.

Fixes 4d5ae32f5e1e ("net: ethernet: Add a driver for Gemini gigabit ethernet")
---
 drivers/net/ethernet/cortina/gemini.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/ethernet/cortina/gemini.c +1 -1
index f08de623e6f7c..2dd2fa801829c 100644
@@ -1798,7 +1798,7 @@ static irqreturn_t gmac_irq(int irq, void *data)
 
 	if (val & (GMAC0_RX_OVERRUN_INT_BIT << (netdev->dev_id * 8))) {
 		spin_lock(&geth->irq_lock);
-		writel(GMAC0_RXDERR_INT_BIT << (netdev->dev_id * 8),
+		writel(GMAC0_RX_OVERRUN_INT_BIT << (netdev->dev_id * 8),
 		       geth->base + GLOBAL_INTERRUPT_STATUS_4_REG);
 		u64_stats_update_begin(&port->ir_stats_syncp);
 		++port->stats.rx_fifo_errors;
Signature · 1 line
2.53.0
author maintainer
#217 lore raw

PATCH 214/438 net: stmmac: propagate FPE preemption-class mapping errors

In reply to Greg Kroah-Hartman

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

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

From: Lorenzo Bianconi <[email protected]>

[ Upstream commit 90e4b849dfa6fc8e6c050bcfe1b331b69c015d28 ]

stmmac_fpe_map_preemption_class() dispatches through the stmmac_do_void_callback() helper, which forces the callback's return value to 0 whenever the op pointer is populated. As a result the -EINVAL returned by dwmac5_fpe_map_preemption_class() (e.g. when a preemptible TC owns more than one TXQ under SP scheduling) is silently swallowed by every caller.

Switch the dispatch macro to stmmac_do_callback() so the callback's real result is propagated, and honour it in the taprio and mqprio qdisc offload.

Note that the taprio "if (ret)" check in tc_taprio_configure() used to be dead code and now becomes live: a preemptible TC spanning more than one TXQ under SP scheduling cannot be programmed in hardware, so a taprio or mqprio configuration that previously returned success while leaving the preemption-class register unprogrammed now fails with -EINVAL. For taprio, the failure also runs the disable path, tearing down the schedule that was just installed; this is the intended behaviour.

---
 drivers/net/ethernet/stmicro/stmmac/hwif.h    |  2 +-
 .../net/ethernet/stmicro/stmmac/stmmac_tc.c   | 19 +++++++++----------
 2 files changed, 10 insertions(+), 11 deletions(-)
drivers/net/ethernet/stmicro/stmmac/hwif.h +1 -1
index 04dafec021b4f..9314bcb85c221 100644
@@ -494,7 +494,7 @@ struct stmmac_ops {
 #define stmmac_set_arp_offload(__priv, __args...) \
 	stmmac_do_void_callback(__priv, mac, set_arp_offload, __args)
 #define stmmac_fpe_map_preemption_class(__priv, __args...) \
-	stmmac_do_void_callback(__priv, mac, fpe_map_preemption_class, __args)
+	stmmac_do_callback(__priv, mac, fpe_map_preemption_class, __args)
 
 /* PTP and HW Timer helpers */
 struct stmmac_hwtimestamp {
drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c +9 -10
index 14cabe76e53ec..5398616fcdfea 100644
@@ -970,7 +970,7 @@ static int tc_taprio_configure(struct stmmac_priv *priv,
 	struct netlink_ext_ack *extack = qopt->mqprio.extack;
 	struct timespec64 time, current_time, qopt_time;
 	ktime_t current_time_ns;
-	int i, ret = 0;
+	int err, i, ret = 0;
 	u64 ctr;
 
 	if (qopt->base_time < 0)
@@ -1120,9 +1120,9 @@ static int tc_taprio_configure(struct stmmac_priv *priv,
 		mutex_unlock(&priv->est_lock);
 	}
 
-	stmmac_fpe_map_preemption_class(priv, priv->dev, extack, 0);
+	err = stmmac_fpe_map_preemption_class(priv, priv->dev, extack, 0);
 
-	return ret;
+	return qopt->cmd == TAPRIO_CMD_DESTROY ? err : ret;
 }
 
 static void tc_taprio_stats(struct stmmac_priv *priv,
@@ -1237,14 +1237,15 @@ static int tc_query_caps(struct stmmac_priv *priv,
 	}
 }
 
-static void stmmac_reset_tc_mqprio(struct net_device *ndev,
-				   struct netlink_ext_ack *extack)
+static int stmmac_reset_tc_mqprio(struct net_device *ndev,
+				  struct netlink_ext_ack *extack)
 {
 	struct stmmac_priv *priv = netdev_priv(ndev);
 
 	netdev_reset_tc(ndev);
 	netif_set_real_num_tx_queues(ndev, priv->plat->tx_queues_to_use);
-	stmmac_fpe_map_preemption_class(priv, ndev, extack, 0);
+
+	return stmmac_fpe_map_preemption_class(priv, ndev, extack, 0);
 }
 
 static int tc_setup_dwmac510_mqprio(struct stmmac_priv *priv,
@@ -1257,10 +1258,8 @@ static int tc_setup_dwmac510_mqprio(struct stmmac_priv *priv,
 	u32 num_tc = qopt->num_tc;
 	int err;
 
-	if (!num_tc) {
-		stmmac_reset_tc_mqprio(ndev, extack);
-		return 0;
-	}
+	if (!num_tc)
+		return stmmac_reset_tc_mqprio(ndev, extack);
 
 	err = netdev_set_num_tc(ndev, num_tc);
 	if (err)
Signature · 1 line
2.53.0
author maintainer
#218 lore raw

PATCH 215/438 net: prevent torn reads in netdev_tc_txq

In reply to Greg Kroah-Hartman

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

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

From: Eric Dumazet <[email protected]>

[ Upstream commit 21ef2d065ad3f0cfbf2ae51260bf962a9fa2c643 ]

netdev_set_tc_queue() (and related helpers/drivers such as netdev_bind_sb_channel_queue(), netdev_reset_tc(), and netdev_unbind_sb_channel()) perform separate 16-bit writes to dev->tc_to_txq[tc].count and dev->tc_to_txq[tc].offset.

Furthermore, memset() in netdev_reset_tc() and netdev_unbind_sb_channel() provides no guarantee of performing full 32-bit word stores.

Concurrent lockless readers (e.g. skb_tx_hash(), netdev_txq_to_tc(), ixgbe_select_queue(), taprio, mqprio, FPE drivers) can observe torn values where offset and count belong to inconsistent configurations.

Redefine struct netdev_tc_txq to embed count and offset inside a union with a u32 combined field, allowing atomic manipulation via READ_ONCE() and WRITE_ONCE().

Update all lockless readers and writers across the kernel to use READ_ONCE() and WRITE_ONCE() on the combined field.

Stable-dep-of: 02fffd1939f6 ("net: stmmac: preserve real_num_tx_queues on mqprio setup failure")

---
 drivers/net/ethernet/intel/igc/igc_tsn.c      |  6 ++-
 drivers/net/ethernet/intel/ixgbe/ixgbe_main.c |  7 +--
 .../net/ethernet/mellanox/mlx5/core/en_main.c |  2 +-
 drivers/net/ethernet/sfc/falcon/tx.c          |  8 +++-
 drivers/net/ethernet/sfc/siena/tx.c           |  8 +++-
 .../net/ethernet/stmicro/stmmac/stmmac_fpe.c  | 14 ++++--
 include/linux/netdevice.h                     |  9 +++-
 net/core/dev.c                                | 46 +++++++++++++------
 net/sched/sch_mqprio.c                        |  4 +-
 net/sched/sch_mqprio_lib.c                    |  7 ++-
 net/sched/sch_taprio.c                        | 26 ++++++-----
 11 files changed, 94 insertions(+), 43 deletions(-)
drivers/net/ethernet/intel/igc/igc_tsn.c +4 -2
index 52de2bcbadbec..0c08650d3bb2d 100644
@@ -183,13 +183,15 @@ static u32 igc_fpe_map_preempt_tc_to_queue(const struct igc_adapter *adapter,
 	u32 i, queue = 0;
 
 	for (i = 0; i < dev->num_tc; i++) {
+		struct netdev_tc_txq res;
 		u32 offset, count;
 
 		if (!(preemptible_tcs & BIT(i)))
 			continue;
 
-		offset = dev->tc_to_txq[i].offset;
-		count = dev->tc_to_txq[i].count;
+		res.combined = READ_ONCE(dev->tc_to_txq[i].combined);
+		offset = res.offset;
+		count = res.count;
 		queue |= GENMASK(offset + count - 1, offset);
 	}
 
drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +4 -3
index 8873a8cc4a185..f91856498eb2d 100644
@@ -9273,10 +9273,11 @@ static u16 ixgbe_select_queue(struct net_device *dev, struct sk_buff *skb,
 	if (sb_dev) {
 		u8 tc = netdev_get_prio_tc_map(dev, skb->priority);
 		struct net_device *vdev = sb_dev;
+		struct netdev_tc_txq res;
 
-		txq = vdev->tc_to_txq[tc].offset;
-		txq += reciprocal_scale(skb_get_hash(skb),
-					vdev->tc_to_txq[tc].count);
+		res.combined = READ_ONCE(vdev->tc_to_txq[tc].combined);
+		txq = res.offset;
+		txq += reciprocal_scale(skb_get_hash(skb), res.count);
 
 		return txq;
 	}
drivers/net/ethernet/mellanox/mlx5/core/en_main.c +1 -1
index f0407a850ea82..8634c970cd002 100644
@@ -3247,7 +3247,7 @@ static int mlx5e_update_tc_and_tx_queues(struct mlx5e_priv *priv)
 	old_num_txqs = netdev->real_num_tx_queues;
 	old_ntc = netdev->num_tc ? : 1;
 	for (i = 0; i < ARRAY_SIZE(old_tc_to_txq); i++)
-		old_tc_to_txq[i] = netdev->tc_to_txq[i];
+		old_tc_to_txq[i].combined = READ_ONCE(netdev->tc_to_txq[i].combined);
 
 	nch = priv->channels.params.num_channels;
 	ntc = priv->channels.params.mqprio.num_tc;
drivers/net/ethernet/sfc/falcon/tx.c +6 -2
index 9e18aaf44badd..e4d47d26a87a0 100644
@@ -439,8 +439,12 @@ int ef4_setup_tc(struct net_device *net_dev, enum tc_setup_type type,
 		return 0;
 
 	for (tc = 0; tc < num_tc; tc++) {
-		net_dev->tc_to_txq[tc].offset = tc * efx->n_tx_channels;
-		net_dev->tc_to_txq[tc].count = efx->n_tx_channels;
+		struct netdev_tc_txq res = {
+			.offset = tc * efx->n_tx_channels,
+			.count = efx->n_tx_channels,
+		};
+
+		WRITE_ONCE(net_dev->tc_to_txq[tc].combined, res.combined);
 	}
 
 	if (num_tc > net_dev->num_tc) {
drivers/net/ethernet/sfc/siena/tx.c +6 -2
index 91e87594ed1ea..1ce98f8fdaf81 100644
@@ -380,8 +380,12 @@ int efx_siena_setup_tc(struct net_device *net_dev, enum tc_setup_type type,
 		return 0;
 
 	for (tc = 0; tc < num_tc; tc++) {
-		net_dev->tc_to_txq[tc].offset = tc * efx->n_tx_channels;
-		net_dev->tc_to_txq[tc].count = efx->n_tx_channels;
+		struct netdev_tc_txq res = {
+			.offset = tc * efx->n_tx_channels,
+			.count = efx->n_tx_channels,
+		};
+
+		WRITE_ONCE(net_dev->tc_to_txq[tc].combined, res.combined);
 	}
 
 	net_dev->num_tc = num_tc;
drivers/net/ethernet/stmicro/stmmac/stmmac_fpe.c +10 -4
index c54c702243517..c889204a7aa5d 100644
@@ -217,8 +217,11 @@ int dwmac5_fpe_map_preemption_class(struct net_device *ndev,
 	 * and is direct one-to-one mapping."
 	 */
 	for (u32 tc = 0; tc < num_tc; tc++) {
-		count = ndev->tc_to_txq[tc].count;
-		offset = ndev->tc_to_txq[tc].offset;
+		struct netdev_tc_txq res;
+
+		res.combined = READ_ONCE(ndev->tc_to_txq[tc].combined);
+		count = res.count;
+		offset = res.offset;
 
 		if (pclass & BIT(tc))
 			preemptible_txqs |= GENMASK(offset + count - 1, offset);
@@ -275,8 +278,11 @@ int dwxgmac3_fpe_map_preemption_class(struct net_device *ndev,
 	 * any of the scheduling algorithms."
 	 */
 	for (u32 tc = 0; tc < num_tc; tc++) {
-		count = ndev->tc_to_txq[tc].count;
-		offset = ndev->tc_to_txq[tc].offset;
+		struct netdev_tc_txq res;
+
+		res.combined = READ_ONCE(ndev->tc_to_txq[tc].combined);
+		count = res.count;
+		offset = res.offset;
 
 		if (pclass & BIT(tc))
 			preemptible_txqs |= GENMASK(offset + count - 1, offset);
include/linux/netdevice.h +7 -2
index 6a86a211fff57..14bcdcd44d1d5 100644
@@ -832,8 +832,13 @@ struct xps_dev_maps {
 #define TC_BITMASK	15
 /* HW offloaded queuing disciplines txq count and offset maps */
 struct netdev_tc_txq {
-	u16 count;
-	u16 offset;
+	union {
+		struct {
+			u16 count;
+			u16 offset;
+		};
+		u32 combined;
+	};
 };
 
 #if defined(CONFIG_FCOE) || defined(CONFIG_FCOE_MODULE)
net/core/dev.c +33 -13

114 lines not on this page · raw message

net/sched/sch_mqprio.c +3 -1
index ae991fc25b43f..6ced7008ef5c8 100644
@@ -679,12 +679,14 @@ static int mqprio_dump_class_stats(struct Qdisc *sch, unsigned long cl,
 	rcu_read_lock();
 	if (cl >= TC_H_MIN_PRIORITY) {
 		struct net_device *dev = qdisc_dev(sch);
-		struct netdev_tc_txq tc = dev->tc_to_txq[cl & TC_BITMASK];
+		struct netdev_tc_txq tc;
 		struct gnet_stats_queue qstats = {0};
 		struct gnet_stats_basic_sync bstats;
 		u32 qlen = 0;
 		int i;
 
+		tc.combined = READ_ONCE(dev->tc_to_txq[cl & TC_BITMASK].combined);
+
 		gnet_stats_basic_sync_init(&bstats);
 
 		for (i = tc.offset; i < tc.offset + tc.count; i++) {
net/sched/sch_mqprio_lib.c +5 -2
index b3a5572c167b7..b60e130c70781 100644
@@ -108,8 +108,11 @@ void mqprio_qopt_reconstruct(struct net_device *dev, struct tc_mqprio_qopt *qopt
 	memcpy(qopt->prio_tc_map, dev->prio_tc_map, sizeof(qopt->prio_tc_map));
 
 	for (tc = 0; tc < num_tc; tc++) {
-		qopt->count[tc] = dev->tc_to_txq[tc].count;
-		qopt->offset[tc] = dev->tc_to_txq[tc].offset;
+		struct netdev_tc_txq res;
+
+		res.combined = READ_ONCE(dev->tc_to_txq[tc].combined);
+		qopt->count[tc] = res.count;
+		qopt->offset[tc] = res.offset;
 	}
 }
 EXPORT_SYMBOL_GPL(mqprio_qopt_reconstruct);
net/sched/sch_taprio.c +15 -11
index 299234a5f0fe6..7d5fe93a4c124 100644
@@ -762,12 +762,13 @@ static struct sk_buff *taprio_dequeue_from_txq(struct Qdisc *sch, int txq,
 
 static void taprio_next_tc_txq(struct net_device *dev, int tc, int *txq)
 {
-	int offset = dev->tc_to_txq[tc].offset;
-	int count = dev->tc_to_txq[tc].count;
+	struct netdev_tc_txq res;
+
+	res.combined = READ_ONCE(dev->tc_to_txq[tc].combined);
 
 	(*txq)++;
-	if (*txq == offset + count)
-		*txq = offset;
+	if (*txq == res.offset + res.count)
+		*txq = res.offset;
 }
 
 /* Prioritize higher traffic classes, and select among TXQs belonging to the
@@ -1441,15 +1442,14 @@ static u32 tc_map_to_queue_mask(struct net_device *dev, u32 tc_mask)
 	u32 i, queue_mask = 0;
 
 	for (i = 0; i < dev->num_tc; i++) {
-		u32 offset, count;
+		struct netdev_tc_txq res;
 
 		if (!(tc_mask & BIT(i)))
 			continue;
 
-		offset = dev->tc_to_txq[i].offset;
-		count = dev->tc_to_txq[i].count;
+		res.combined = READ_ONCE(dev->tc_to_txq[i].combined);
 
-		queue_mask |= GENMASK(offset + count - 1, offset);
+		queue_mask |= GENMASK(res.offset + res.count - 1, res.offset);
 	}
 
 	return queue_mask;
@@ -1802,10 +1802,14 @@ static int taprio_mqprio_cmp(const struct net_device *dev,
 	if (!mqprio || mqprio->num_tc != dev->num_tc)
 		return -1;
 
-	for (i = 0; i < mqprio->num_tc; i++)
-		if (dev->tc_to_txq[i].count != mqprio->count[i] ||
-		    dev->tc_to_txq[i].offset != mqprio->offset[i])
+	for (i = 0; i < mqprio->num_tc; i++) {
+		struct netdev_tc_txq res;
+
+		res.combined = READ_ONCE(dev->tc_to_txq[i].combined);
+		if (res.count != mqprio->count[i] ||
+		    res.offset != mqprio->offset[i])
 			return -1;
+	}
 
 	for (i = 0; i <= TC_BITMASK; i++)
 		if (dev->prio_tc_map[i] != mqprio->prio_tc_map[i])
Signature · 1 line
2.53.0
author maintainer
#219 lore raw

PATCH 216/438 net: stmmac: preserve real_num_tx_queues on mqprio setup failure

In reply to Greg Kroah-Hartman

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

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

From: Lorenzo Bianconi <[email protected]>

[ Upstream commit 02fffd1939f6b45892f61822459953ce95e42948 ]

With the FPE preemption-class mapping error now propagated from stmmac_fpe_map_preemption_class(), tc_setup_dwmac510_mqprio() can fail on the mapping step. The error path used to call stmmac_reset_tc_mqprio(), which resets the number of real TX queues to priv->plat->tx_queues_to_use (the platform maximum), overwriting the value that was active before the offload was attempted (for example a lower count left over from a previous mqprio configuration).

The issue can be triggered using the following configuration:

# First mqprio config lowers the hw queue count below the platform

  # default (e.g. 8 TX queues).
  $tc qdisc add dev eth0 root handle 1: mqprio queues 2@0 2@2

# Replace mqprio configuration with a second one that fails FPE

  # preemption-class mapping. stmmac driver resets the real_num_tx_queues
  # to the platform maximum, losing the previous configuration.
  $tc qdisc replace dev eth0 root handle 2: mqprio queues 2@0 2@2 fp E P

Save ndev->real_num_tx_queues before lowering it and restore it, together with the TC-to-queue and priority-to-TC mappings, when the FPE preemption-class mapping fails, instead of resetting the queue count to the platform maximum.

Note that a failed setup makes the qdisc layer run mqprio_destroy() on the new qdisc. Because priv->hw_offload is only assigned after ndo_setup_tc() succeeds, mqprio_destroy() calls netdev_set_num_tc(dev, 0), so dev->num_tc ends up 0 regardless of the driver-side restore and the previous qdisc is not reactivated. The restore is still needed to keep real_num_tx_queues and to avoid leaving the failed configuration's TC-to-queue and priority-to-TC mappings in place.

---
 .../net/ethernet/stmicro/stmmac/stmmac_tc.c   | 82 ++++++++++++++-----
 1 file changed, 62 insertions(+), 20 deletions(-)
drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c +62 -20
index 5398616fcdfea..42a00446e9b41 100644
@@ -1237,6 +1237,30 @@ static int tc_query_caps(struct stmmac_priv *priv,
 	}
 }
 
+static int stmmac_set_ndev_tcs(struct net_device *ndev, u8 ntc,
+			       struct netdev_tc_txq *tc_to_txq)
+{
+	int i, err;
+
+	netdev_reset_tc(ndev);
+	if (!ntc)
+		return 0;
+
+	err = netdev_set_num_tc(ndev, ntc);
+	if (err)
+		return err;
+
+	for (i = 0; i < ntc; i++) {
+		u16 count, offset;
+
+		count = tc_to_txq[i].count;
+		offset = tc_to_txq[i].offset;
+		netdev_set_tc_queue(ndev, i, count, offset);
+	}
+
+	return 0;
+}
+
 static int stmmac_reset_tc_mqprio(struct net_device *ndev,
 				  struct netlink_ext_ack *extack)
 {
@@ -1251,43 +1275,61 @@ static int stmmac_reset_tc_mqprio(struct net_device *ndev,
 static int tc_setup_dwmac510_mqprio(struct stmmac_priv *priv,
 				    struct tc_mqprio_qopt_offload *mqprio)
 {
+	unsigned int ndev_num_tx_queues, num_tx_queues = 0;
+	struct netdev_tc_txq ndev_tc_to_txq[TC_MAX_QUEUE];
+	struct netdev_tc_txq tc_to_txq[TC_MAX_QUEUE] = {};
 	struct netlink_ext_ack *extack = mqprio->extack;
 	struct tc_mqprio_qopt *qopt = &mqprio->qopt;
-	u32 offset, count, num_stack_tx_queues = 0;
 	struct net_device *ndev = priv->dev;
-	u32 num_tc = qopt->num_tc;
-	int err;
+	u8 ndev_prio_tc_map[TC_BITMASK + 1];
+	int i, err, ndev_ntc;
 
-	if (!num_tc)
+	if (!qopt->num_tc)
 		return stmmac_reset_tc_mqprio(ndev, extack);
 
-	err = netdev_set_num_tc(ndev, num_tc);
-	if (err)
-		return err;
-
-	for (u32 tc = 0; tc < num_tc; tc++) {
-		offset = qopt->offset[tc];
-		count = qopt->count[tc];
-		num_stack_tx_queues += count;
+	if (qopt->num_tc > ARRAY_SIZE(tc_to_txq))
+		return -EINVAL;
 
-		err = netdev_set_tc_queue(ndev, tc, count, offset);
-		if (err)
-			goto err_reset_tc;
+	/* save current tc values for reset */
+	ndev_ntc = netdev_get_num_tc(ndev);
+	for (i = 0; i < ARRAY_SIZE(ndev->tc_to_txq); i++)
+		ndev_tc_to_txq[i].combined =
+			READ_ONCE(ndev->tc_to_txq[i].combined);
+	for (i = 0; i < ARRAY_SIZE(ndev_prio_tc_map); i++)
+		ndev_prio_tc_map[i] = READ_ONCE(ndev->prio_tc_map[i]);
+
+	for (i = 0; i < qopt->num_tc; i++) {
+		tc_to_txq[i] = (struct netdev_tc_txq) {
+			.count = qopt->count[i],
+			.offset = qopt->offset[i],
+		};
+		num_tx_queues += qopt->count[i];
 	}
 
-	err = netif_set_real_num_tx_queues(ndev, num_stack_tx_queues);
+	err = stmmac_set_ndev_tcs(ndev, qopt->num_tc, tc_to_txq);
+	if (err)
+		goto error_reset_tc;
+
+	ndev_num_tx_queues = ndev->real_num_tx_queues;
+	err = netif_set_real_num_tx_queues(ndev, num_tx_queues);
 	if (err)
-		goto err_reset_tc;
+		goto error_reset_tc;
 
 	err = stmmac_fpe_map_preemption_class(priv, ndev, extack,
 					      mqprio->preemptible_tcs);
 	if (err)
-		goto err_reset_tc;
+		goto error_reset_num_tx_queues;
 
 	return 0;
 
-err_reset_tc:
-	stmmac_reset_tc_mqprio(ndev, extack);
+error_reset_num_tx_queues:
+	if (netif_set_real_num_tx_queues(ndev, ndev_num_tx_queues))
+		netdev_warn(ndev, "Failed to restore %u TX queues\n",
+			    ndev_num_tx_queues);
+error_reset_tc:
+	stmmac_set_ndev_tcs(ndev, ndev_ntc, ndev_tc_to_txq);
+	for (i = 0; i < ARRAY_SIZE(ndev_prio_tc_map); i++)
+		netdev_set_prio_tc_map(ndev, i, ndev_prio_tc_map[i]);
 
 	return err;
 }
Signature · 1 line
2.53.0
author maintainer
#220 lore raw

PATCH 217/438 net: psp: avoid conflicts with skb->decrypted and sk_validate_xmit_skb()

In reply to Greg Kroah-Hartman

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

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

From: Daniel Zahka <[email protected]>

[ Upstream commit a41f24c612c3f5139a3143307eb85bbcf1bd4d07 ]

PSP conflicts with TLS ULP in its usage of both skb->decrypted and sk->sk_validate_xmit_skb().

Make PSP mutually exclusive with TLS ULP, the only other user of either of these. As other users of skb->decrypted come along, they can be added to sk_has_decrypt_user(). It would make sense to also assert that sk->sk_validate_xmit_skb() is also NULL in both of these setup paths for similar future proofing, but the PSP listener/sk_clone() path is still broken and it could be seen as a regression to not allow rx assoc to run on a child of a listener socket with PSP tx assoc state.

Include all TCP ULPs in the sk_has_decrypt_user() check, even though TLS is the only one that conflicts with PSP via the decrypted bit. This is intentional because PSP was not designed to be used with ULPs. It is best to close off surface area that may make bugs reachable, until someone wishes to design and test an actual user of PSP with ULPs.

Fixes 6b46ca260e22 ("net: psp: add socket security association code")
---
 include/net/sock.h | 2 ++
 net/core/sock.c    | 7 +++++++
 net/ipv4/tcp_ulp.c | 4 ++++
 net/psp/psp_sock.c | 4 ++++
 4 files changed, 17 insertions(+)
include/net/sock.h +2 -0
index 51185222aac29..60ea55dc18854 100644
@@ -2312,6 +2312,8 @@ static inline void sk_gso_disable(struct sock *sk)
 	sk->sk_route_caps &= ~NETIF_F_GSO_MASK;
 }
 
+bool sk_has_decrypt_user(const struct sock *sk);
+
 static inline int skb_do_copy_data_nocache(struct sock *sk, struct sk_buff *skb,
 					   struct iov_iter *from, char *to,
 					   int copy, int offset)
net/core/sock.c +7 -0
index 5c1a1d1950752..4395509ab9dcd 100644
@@ -142,6 +142,7 @@
 
 #include <trace/events/sock.h>
 
+#include <net/psp.h>
 #include <net/tcp.h>
 #include <net/busy_poll.h>
 #include <net/phonet/phonet.h>
@@ -2667,6 +2668,12 @@ void sk_setup_caps(struct sock *sk, struct dst_entry *dst)
 }
 EXPORT_SYMBOL_GPL(sk_setup_caps);
 
+bool sk_has_decrypt_user(const struct sock *sk)
+{
+	return psp_sk_assoc(sk) ||
+	       (sk_is_inet(sk) && inet_csk_has_ulp(sk)); /* for tls */
+}
+
 /*
  *	Simple resource managers for sockets.
  */
net/ipv4/tcp_ulp.c +4 -0
index 2aa442128630e..b58045df101e5 100644
@@ -136,6 +136,10 @@ static int __tcp_set_ulp(struct sock *sk, const struct tcp_ulp_ops *ulp_ops)
 	if (icsk->icsk_ulp_ops)
 		goto out_err;
 
+	err = -EINVAL;
+	if (sk_has_decrypt_user(sk))
+		goto out_err;
+
 	if (sk->sk_socket)
 		clear_bit(SOCK_SUPPORT_ZC, &sk->sk_socket->flags);
 
net/psp/psp_sock.c +4 -0
index 07dc4cf741f33..ce423d14ad267 100644
@@ -143,6 +143,10 @@ int psp_sock_assoc_set_rx(struct sock *sk, struct psp_assoc *pas,
 		NL_SET_ERR_MSG(extack, "Socket already has PSP state");
 		err = -EBUSY;
 		goto exit_unlock;
+	} else if (sk_has_decrypt_user(sk)) {
+		NL_SET_ERR_MSG(extack, "Socket has incompatible state");
+		err = -EINVAL;
+		goto exit_unlock;
 	}
 
 	refcount_inc(&pas->refcnt);
Signature · 1 line
2.53.0
author maintainer
#221 lore raw

PATCH 218/438 x86/kprobes: Fix crash when probing CS CALL instructions

In reply to Greg Kroah-Hartman

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

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

From: Jinke Han <[email protected]>

[ Upstream commit a5f7a5bb3b7f28ba7e4fa246775b29a0e5537255 ]

When using eBPF to probe CS CALL instructions within a function, a crash can be triggered.

The eBPF tool probes offset 257 of the __hrtimer_run_queues() function:

<__hrtimer_run_queues+249>:  nopl   0x0(%rax,%rax,1)
<__hrtimer_run_queues+254>:  mov    %r14,%rdi
<__hrtimer_run_queues+257>:  cs call <__x86_indirect_thunk_r12>
<__hrtimer_run_queues+263>:  mov    %eax,%r12d
<__hrtimer_run_queues+266>:  xchg   %ax,%ax
<__hrtimer_run_queues+268>:  mov    %r13,%rdi

Which triggers this crash:

  BUG: unable to handle page fault for address: 00000000000f41c9
  #PF: supervisor write access in kernel mode
  #PF: error_code(0x0002) - not-present page
  PGD 0 P4D 0
  Oops: 0002 [#1] SMP NOPTI
  CPU: 1 PID: 0 Comm: swapper/1 Kdump: loaded Tainted: P
  RIP: 0010:__hrtimer_run_queues+0x106/0x230

Note that __hrtimer_run_queues+0x106 is __hrtimer_run_queues+262, which is at the 6th byte of the above CS CALL instruction. Since the CS CALL instruction occupies 6 bytes, the exception occurred in the middle of that call instruction.

The root cause is that when using eBPF tools to probe in the middle of a function, a kprobe with INT3 is used as the underlying implementation.

During single-step emulation of the original CALL instruction, int3_emulate_call() assumes that the probed CALL instruction is 5 bytes long. However, the actual CS-prefixed CALL instruction occupies 6 bytes, so it constructs an incorrect exception return address. When the CPU returns from the kprobe handler, the next instruction to be executed is at the address of the last byte of that CS CALL instruction. Coincidentally, starting from that address, the CPU fetches and decodes a completely different instruction, which ultimately triggers a kernel crash.

Fix the issue by using the actual instruction length obtained from the instruction decoder when constructing the exception return address, rather than relying on the hardcoded CALL_INSN_SIZE macro.

[ mingo: Refined the changelog ]

---
 arch/x86/include/asm/text-patching.h | 4 ++--
 arch/x86/kernel/alternative.c        | 6 ++++--
 arch/x86/kernel/kprobes/core.c       | 5 ++---
 3 files changed, 8 insertions(+), 7 deletions(-)
arch/x86/include/asm/text-patching.h +2 -2
index f2d142a0a862e..ea09381070e82 100644
@@ -164,9 +164,9 @@ unsigned long int3_emulate_pop(struct pt_regs *regs)
 }
 
 static __always_inline
-void int3_emulate_call(struct pt_regs *regs, unsigned long func)
+void int3_emulate_call(struct pt_regs *regs, unsigned long ip, unsigned long func)
 {
-	int3_emulate_push(regs, regs->ip - INT3_INSN_SIZE + CALL_INSN_SIZE);
+	int3_emulate_push(regs, ip);
 	int3_emulate_jmp(regs, func);
 }
 
arch/x86/kernel/alternative.c +4 -2
index b0b576ab2fa6e..f24d7e911d3e5 100644
@@ -2319,6 +2319,7 @@ int3_exception_notify(struct notifier_block *self, unsigned long val, void *data
 	unsigned long selftest = (unsigned long)&int3_selftest_asm;
 	struct die_args *args = data;
 	struct pt_regs *regs = args->regs;
+	unsigned long ip;
 
 	OPTIMIZER_HIDE_VAR(selftest);
 
@@ -2331,7 +2332,8 @@ int3_exception_notify(struct notifier_block *self, unsigned long val, void *data
 	if (regs->ip - INT3_INSN_SIZE != selftest)
 		return NOTIFY_DONE;
 
-	int3_emulate_call(regs, (unsigned long)&int3_selftest_callee);
+	ip = regs->ip - INT3_INSN_SIZE + CALL_INSN_SIZE;
+	int3_emulate_call(regs, ip, (unsigned long)&int3_selftest_callee);
 	return NOTIFY_STOP;
 }
 
@@ -2929,7 +2931,7 @@ noinstr int smp_text_poke_int3_handler(struct pt_regs *regs)
 		break;
 
 	case CALL_INSN_OPCODE:
-		int3_emulate_call(regs, (long)ip + tpl->disp);
+		int3_emulate_call(regs, (long)ip, (long)ip + tpl->disp);
 		break;
 
 	case JMP32_INSN_OPCODE:
arch/x86/kernel/kprobes/core.c +2 -3
index c1fac3a9fecc2..03d2710ec3021 100644
@@ -510,10 +510,9 @@ NOKPROBE_SYMBOL(kprobe_emulate_ret);
 
 static void kprobe_emulate_call(struct kprobe *p, struct pt_regs *regs)
 {
-	unsigned long func = regs->ip - INT3_INSN_SIZE + p->ainsn.size;
+	unsigned long ip = regs->ip - INT3_INSN_SIZE + p->ainsn.size;
 
-	func += p->ainsn.rel32;
-	int3_emulate_call(regs, func);
+	int3_emulate_call(regs, ip, ip + p->ainsn.rel32);
 }
 NOKPROBE_SYMBOL(kprobe_emulate_call);
 
Signature · 1 line
2.53.0
author maintainer
#222 lore raw

PATCH 219/438 net: macb: fix ordering around PTP timestamp read

In reply to Greg Kroah-Hartman

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

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

From: James Clark <[email protected]>

[ Upstream commit 9ca4ba24259183ce15665be86b2956cd896c4687 ]

PTP_SYS_OFFSET_EXTENDED returns system timestamps that do not correctly bracket the PHC register read on MACB/GEM. On a Raspberry Pi 5, the returned interval can be as short as 37 ns, while an ordered register read takes approximately 1 us. This biases the midpoint used by phc2sys, causing CLOCK_REALTIME to run approximately 0.5 us ahead when synchronized to the PHC.

gem_tsu_get_time() reads the nanoseconds register using the driver's relaxed MMIO accessor. On weakly ordered systems, the subsequent system timestamp can be taken before the register read completes. The internal smp_rmb() in the pre-timestamp path also does not guarantee ordering against the subsequent MMIO read.

Add rmb() before and after the bracketed nanoseconds read in both the normal and seconds rollover paths so the system timestamps bracket the PHC read. Adding the post-read barrier increases the minimum interval on the same Raspberry Pi 5 to approximately 1 us.

---
 drivers/net/ethernet/cadence/macb_ptp.c | 9 +++++++++
 1 file changed, 9 insertions(+)
drivers/net/ethernet/cadence/macb_ptp.c +9 -0
index 6d91663899886..14ae57fa00cba 100644
@@ -50,7 +50,12 @@ static int gem_tsu_get_time(struct ptp_clock_info *ptp, struct timespec64 *ts,
 
 	spin_lock_irqsave(&bp->tsu_clk_lock, flags);
 	ptp_read_system_prets(sts);
+	/* explicit barriers are needed because gem_readl() is relaxed */
+	if (sts)
+		rmb();
 	first = gem_readl(bp, TN);
+	if (sts)
+		rmb();
 	ptp_read_system_postts(sts);
 	secl = gem_readl(bp, TSL);
 	sech = gem_readl(bp, TSH);
@@ -62,7 +67,11 @@ static int gem_tsu_get_time(struct ptp_clock_info *ptp, struct timespec64 *ts,
 		 * (assume all done within 1s)
 		 */
 		ptp_read_system_prets(sts);
+		if (sts)
+			rmb();
 		ts->tv_nsec = gem_readl(bp, TN);
+		if (sts)
+			rmb();
 		ptp_read_system_postts(sts);
 		secl = gem_readl(bp, TSL);
 		sech = gem_readl(bp, TSH);
Signature · 1 line
2.53.0
author maintainer
#223 lore raw

PATCH 220/438 net: mvpp2: prevent buffer overflow in page_pool allocation

In reply to Greg Kroah-Hartman

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

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

From: Dmitriy Okunev <[email protected]>

[ Upstream commit 14cb1e7702e5cb3c58888f6aed498381a73927d2 ]

The per‑processor buffering scheme is supported only if the number of pools (nrxqs * 2) does not exceed MVPP2_BM_MAX_POOLS (8). This is already checked in mvpp2_probe() during the initial activation of percpu_pools.

However, mvpp2_change_mtu() may later call mvpp2_bm_switch_buffers(priv, true) without this check, which can lead to an out-of-bounds access in the priv->page_pool array in mvpp2_bm_init(). The array is sized to hold MVPP2_PORT_MAX_RXQ entries, and mvpp2_get_nrxqs() may return exactly that value. The per-CPU scheme then doubles it to nrxqs * 2, exceeding the array bounds.

Check that the hardware version is MVPP22 or newer and that the number of pools (nrxqs * 2) does not exceed MVPP2_BM_MAX_POOLS before switching to per-CPU mode.

Found by Linux Verification Center (linuxtesting.org) with SVACE.

---
 drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c +2 -1
index ccc24a1301f22..848ee655c6ea6 100644
@@ -5086,7 +5086,8 @@ static int mvpp2_change_mtu(struct net_device *dev, int mtu)
 			netdev_warn(dev, "mtu %d too high, switching to shared buffers", mtu);
 			mvpp2_bm_switch_buffers(priv, false);
 		}
-	} else {
+	} else if (priv->hw_version >= MVPP22 &&
+		   mvpp2_get_nrxqs(priv) * 2 <= MVPP2_BM_MAX_POOLS) {
 		bool jumbo = false;
 		int i;
 
Signature · 1 line
2.53.0
author maintainer
#224 lore raw

PATCH 221/438 net: skbuff: do not leave stale header offsets after pskb_carve()

In reply to Greg Kroah-Hartman

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

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

From: Eric Dumazet <[email protected]>

[ Upstream commit a5117e1eccac6ee3bd4aed7cacf8ebcb6b3eb309 ]

pskb_carve_inside_header() and pskb_carve_inside_nonlinear() remove the first bytes of a packet and reallocate skb->head.

All the headers that were present before the operation are gone, but both functions call skb_headers_offset_update(skb, 0), which is a no-op : skb->mac_header, skb->network_header, skb->transport_header and skb->csum_start keep their old values and now describe bytes which are no longer there.

Both helpers size the new head from the old skb_end_offset(), so the stale offsets still land inside the new allocation. They point past skb_tail_pointer() though, to bytes that were never initialized.

pskb_carve_inside_nonlinear() is the worst case, because it leaves a zombie skb with an empty linear part (skb->data == skb_tail_pointer(skb), skb_headlen(skb) == 0), while skb_mac_header_was_set() is still true and skb->mac_header is way ahead of skb->data.

The only user of pskb_extract() is rds_tcp_data_recv(), and the carved skb is queued on tinc->ti_skb_list. When the RDS incoming message is released, rds_tcp_inc_free() calls skb_queue_purge(), which frees the skbs with SKB_DROP_REASON_QUEUE_PURGE. This is visible from drop_monitor, which then tries to pull back to the (bogus) mac header :

skbuff: __skb_pull(len=234)
skb len=6968 data_len=6968 headroom=0 headlen=0 tailroom=0 end-tail=384 mac=(234,14) mac_len=14 net=(248,40) trans=288 shinfo(txflags=0 nr_frags=1 gso(size=1428 type=16 segs=5)) csum(0x100120 start=288 offset=16 ip_summed=3 complete_sw=0 valid=1 level=0) hash(0x7b446c6c sw=0 l4=1) proto=0x86dd pkttype=0 iif=60
kernel BUG at ./include/linux/skbuff.h:2847!

Add skb_carve_reset_headers() to mark the mac and transport headers as not set, reset the network header, clear skb->mac_len, and drop a now meaningless CHECKSUM_PARTIAL (csum_start no longer describes anything).

Invalidate the inner offsets as well. Unlike mac_header and transport_header they have no "unset" sentinel, so a leftover non-zero value still looks like a real header. Zero skb->inner_mac_header, skb->inner_network_header, skb->inner_transport_header, skb->inner_protocol and skb->encapsulation, so that all the header state is invalidated in one place.

v2: fixed an inaccurate changelog. The stale offsets stay inside the
new skb->head, which is never smaller than the old one, they simply point past skb_tail_pointer() to bytes that are gone. Thanks to Xuanqiang Luo for insisting on this.
Also invalidate the inner header state, as suggested by the netdev AI review :
https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260911114922.621937-1-edumazet%40google.com

---
 net/core/skbuff.c | 32 ++++++++++++++++++++++++++++++--
 1 file changed, 30 insertions(+), 2 deletions(-)
net/core/skbuff.c +30 -2
index 0e964c67a7213..c485be081aeaa 100644
@@ -6820,6 +6820,34 @@ struct sk_buff *alloc_skb_with_frags(unsigned long header_len,
 }
 EXPORT_SYMBOL(alloc_skb_with_frags);
 
+/* pskb_carve_inside_header() and pskb_carve_inside_nonlinear()
+ * remove the first bytes of a packet and reallocate skb->head.
+ *
+ * Whatever headers were present before the operation are gone,
+ * we must not leave stale offsets, otherwise users of this skb
+ * (skb_dump(), drop_monitor, taps, ...) would read or pull garbage.
+ */
+static void skb_carve_reset_headers(struct sk_buff *skb)
+{
+	skb_unset_mac_header(skb);
+	skb_unset_transport_header(skb);
+	skb_reset_network_header(skb);
+	skb->mac_len = 0;
+
+	/* Inner offsets have no "unset" marker, zero them so that
+	 * skb_inner_network_header_was_set() becomes false and no
+	 * consumer mistakes them for a real (and long gone) header.
+	 */
+	skb->inner_mac_header = 0;
+	skb->inner_network_header = 0;
+	skb->inner_transport_header = 0;
+	skb->inner_protocol = 0;
+	skb->encapsulation = 0;
+
+	if (skb->ip_summed == CHECKSUM_PARTIAL)
+		skb->ip_summed = CHECKSUM_NONE;
+}
+
 /* carve out the first off bytes from skb when off < headlen */
 static int pskb_carve_inside_header(struct sk_buff *skb, const u32 off,
 				    const int headlen, gfp_t gfp_mask)
@@ -6875,7 +6903,7 @@ static int pskb_carve_inside_header(struct sk_buff *skb, const u32 off,
 	skb->head_frag = 0;
 	skb_set_end_offset(skb, size);
 	skb_set_tail_pointer(skb, skb_headlen(skb));
-	skb_headers_offset_update(skb, 0);
+	skb_carve_reset_headers(skb);
 	skb->cloned = 0;
 	skb->hdr_len = 0;
 	skb->nohdr = 0;
@@ -7015,7 +7043,7 @@ static int pskb_carve_inside_nonlinear(struct sk_buff *skb, const u32 off,
 	skb->data = data;
 	skb_set_end_offset(skb, size);
 	skb_reset_tail_pointer(skb);
-	skb_headers_offset_update(skb, 0);
+	skb_carve_reset_headers(skb);
 	skb->cloned   = 0;
 	skb->hdr_len  = 0;
 	skb->nohdr    = 0;
Signature · 1 line
2.53.0
author maintainer
#225 lore raw

PATCH 222/438 drm/amdgpu: check ras and obj before dereference

In reply to Greg Kroah-Hartman

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

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

From: Dmitriy Chumachenko <[email protected]>

[ Upstream commit 723d4dc628d764b19cf9efca14b82cca5ff020c9 ]

nbio_v7_9_handle_ras_controller_intr_no_bifring() dereferences ras and obj without checking either for NULL. Both amdgpu_ras_get_context() and amdgpu_ras_find_obj() can return NULL, e.g. during the window between adev->nbio.ras being set (early in amdgpu_ras_init(), by design, to enable the fatal-error interrupt as soon as possible) and the PCIE_BIF ras object actually being created in RAS late_init. Any interrupt in that window crashes in hard-IRQ context.

This is analogous to commit d190b459b2a4 ("drm/amdgpu: the warning dereferencing obj for nbio_v7_4"), which fixed the same issue in the nbio_v7_4 handler.

Found by Linux Verification Center (linuxtesting.org) with SVACE.

Fixes 7692e1ee2446 ("drm/amdgpu: add RAS fatal error handler for NBIO v7.9")
(cherry picked from commit c7071767a50a32ed727cf800ac84372429e3b4b3)
---
 drivers/gpu/drm/amd/amdgpu/nbio_v7_9.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/gpu/drm/amd/amdgpu/nbio_v7_9.c +1 -1
index bdfd2917e3cab..def02993b7cfd 100644
@@ -535,7 +535,7 @@ static void nbio_v7_9_handle_ras_controller_intr_no_bifring(struct amdgpu_device
 						RAS_CNTLR_INTERRUPT_CLEAR, 1);
 		WREG32_SOC15(NBIO, 0, regBIF_BX0_BIF_DOORBELL_INT_CNTL, bif_doorbell_intr_cntl);
 
-		if (!ras->disable_ras_err_cnt_harvest) {
+		if (ras && !ras->disable_ras_err_cnt_harvest && obj) {
 			/*
 			 * clear error status after ras_controller_intr
 			 * according to hw team and count ue number
Signature · 1 line
2.53.0
author maintainer
#226 lore raw

PATCH 223/438 drm/amd/display: fix MALL hysteresis timer underflow at high refresh rates

In reply to Greg Kroah-Hartman

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

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

From: Francis Marlou Pacaro <[email protected]>

[ Upstream commit 2ac2fe765ef475f409616ac0b57c4a3922749b0f ]

dcn30_apply_idle_power_optimizations() derives the MALL frame cache hysteresis timer with

	tmr_delay = (uint32_t)(div_u64(..., denom) - 64LL);

div_u64() returns a u64, so when the quotient is smaller than 64 the subtraction wraps instead of going negative and tmr_delay ends up huge. The loop that follows tries to squeeze it into the 6 bit register field by doubling denom, but that only makes the quotient smaller, so tmr_delay can never converge. tmr_scale is bumped past 3 and the function gives up with

	/* Delay exceeds range of hysteresis timer */
	ASSERT(false);

even though the requested delay is too *short* to encode, not too long.

With mall_additional_timer_percent left at its default of 0, the quotient drops below 64 once the refresh rate used for the calculation goes above ~243 Hz. Every DCN 3.0 display above that loses MALL static screen entirely and splats a WARN once per boot. Reproduced on Navi 23 (RX 6600) driving 1920x1080, resetting /sys/kernel/debug/clear_warn_once between modes:

	refresh   MALL       ASSERT
	144 Hz    enabled    no
	240 Hz    enabled    no
	280 Hz    skipped    yes
	360 Hz    skipped    yes

Commit 3bb68cec4db8 ("drm/amd/display: Add Overflow check to skip MALL") already covered the other end of the range, where a large stutter period makes the delay too long to encode. Cover the short end by clamping to 0, which selects the shortest hysteresis the register can express, 65.28us * 64 = ~4.18ms. That is marginally longer than what the formula asks for at these refresh rates, and erring long is the safe direction:
it only delays MALL entry, it can never enter early.

The numerator does not change between iterations, only denom does, so compute it once and keep both call sites inside 100 columns.

The genuinely out of range case at very low refresh rates still reaches the ASSERT, which is where it belongs.

Fixes 52f2e83e2fe5 ("drm/amdgpu/display: add MALL support (v2)")
(cherry picked from commit 387550e53e1405f1f960b62b22f8783db17c8e1d)
---
 .../gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c | 13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)
drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c +7 -6
index 2d3587a31bc8b..2ecd3a4068c03 100644
@@ -1058,10 +1058,12 @@ bool dcn30_apply_idle_power_optimizations(struct dc *dc, bool enable)
 				 */
 				unsigned int denom = refresh_hz * 6528;
 				unsigned int stutter_period = dc->current_state->perf_params.stutter_period_us;
+				uint64_t num = (1000000LL + 2 * stutter_period * refresh_hz) *
+					(100LL + dc->debug.mall_additional_timer_percent);
+				uint64_t tmr_ticks;
 
-				tmr_delay = (uint32_t)(div_u64(((1000000LL + 2 * stutter_period * refresh_hz) *
-						(100LL + dc->debug.mall_additional_timer_percent) + denom - 1),
-						denom) - 64LL);
+				tmr_ticks = div_u64(num + denom - 1, denom);
+				tmr_delay = tmr_ticks > 64 ? (uint32_t)(tmr_ticks - 64) : 0;
 
 				/* In some cases the stutter period is really big (tiny modes) in these
 				 * cases MALL cant be enabled, So skip these cases to avoid a ASSERT()
@@ -1083,9 +1085,8 @@ bool dcn30_apply_idle_power_optimizations(struct dc *dc, bool enable)
 					}
 
 					denom *= 2;
-					tmr_delay = (uint32_t)(div_u64(((1000000LL + 2 * stutter_period * refresh_hz) *
-							(100LL + dc->debug.mall_additional_timer_percent) + denom - 1),
-							denom) - 64LL);
+					tmr_ticks = div_u64(num + denom - 1, denom);
+					tmr_delay = tmr_ticks > 64 ? (uint32_t)(tmr_ticks - 64) : 0;
 				}
 
 				/* Copy HW cursor */
Signature · 1 line
2.53.0
author maintainer
#227 lore raw

PATCH 224/438 btrfs: abort transaction on failure to update inode for hole punching and reflinking

In reply to Greg Kroah-Hartman

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

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

From: Filipe Manana <[email protected]>

[ Upstream commit 97fcd34aa9fd73cefe3120ac9a82ca9d7763922f ]

If we fail to update the inode we error out without aborting the transaction, which can result in a persistent inconsistency if after the failure the transaction is committed, as we have dropped file extent items from a range and either punched a hole or insert a new file extent item for that range (for reflinks).

So add the missing transaction abort.

Fixes 2aaa66558172 ("Btrfs: add hole punching")
---
 fs/btrfs/file.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
fs/btrfs/file.c +3 -1
index f949805f2c3ef..d892683927149 100644
@@ -2513,8 +2513,10 @@ int btrfs_replace_file_extents(struct btrfs_inode *inode,
 					      inode_set_ctime_current(&inode->vfs_inode));
 
 		ret = btrfs_update_inode(trans, inode);
-		if (ret)
+		if (unlikely(ret)) {
+			btrfs_abort_transaction(trans, ret);
 			break;
+		}
 
 		btrfs_end_transaction(trans);
 		btrfs_btree_balance_dirty(fs_info);
Signature · 1 line
2.53.0
author maintainer
#228 lore raw

PATCH 225/438 btrfs: check if there is space for chunk item when validating sys chunk array

In reply to Greg Kroah-Hartman

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

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

From: Filipe Manana <[email protected]>

[ Upstream commit aeab4c62875748ecfd390a47ac1d91ea7c9a6abb ]

We checked if have enough remaining space for a key before dereferencing a key, but we then dereference a chunk item, to get the number of stripes, without checking if there is space for the item. So add a check to see if there is enough space for a chunk item before dereferencing the item to extract the stripe count.

Fixes 2a9bb78cfd36 ("btrfs: validate system chunk array at btrfs_validate_super()")
---
 fs/btrfs/disk-io.c | 4 ++++
 1 file changed, 4 insertions(+)
fs/btrfs/disk-io.c +4 -0
index de6e23b88b406..12b771bda43aa 100644
@@ -2358,6 +2358,10 @@ static int validate_sys_chunk_array(const struct btrfs_fs_info *fs_info,
 				  key.type, cur);
 			return -EUCLEAN;
 		}
+
+		if (unlikely(cur + sizeof(*chunk) > sys_array_size))
+			goto short_read;
+
 		chunk = (struct btrfs_chunk *)(sb->sys_chunk_array + cur);
 		num_stripes = btrfs_stack_chunk_num_stripes(chunk);
 		if (unlikely(cur + btrfs_chunk_item_size(num_stripes) > sys_array_size))
Signature · 1 line
2.53.0
author maintainer
#229 lore raw

PATCH 226/438 perf: Fix null pointer access in is_include_guest_event()

In reply to Greg Kroah-Hartman

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

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

From: Vinay Belgaumkar <[email protected]>

[ Upstream commit 88aed0422f39b22406f35f1e758cea25e7bbcfb5 ]

A typical module unload occurring event when there is an active perf connection leads to freeing of the pmu pointer. The call log is something like:
..
__pmu_detach_event
pmu_detach_event
pmu_detach_events
perf_pmu_unregister
..

__pmu_detach_event() sets event->pmu to null. When the perf connection finally is closed, the following stack trace is observed:

 Oops: general protection fault, kernel NULL pointer dereference
 ...
 RIP: 0010:_free_event+0x3e/0x370
 ...
 Call Trace:
 ...
 perf_event_release_kernel+0x260/0x2d0
 perf_release+0x12/0x20

A call to mediated_pmu_unaccount_event() inside _free_event() is the root cause of this crash. Adding a check inside is_include_guest_event() ensures we don't accidentally access a null pmu ptr. In addition to this, we will now call mediated_pmu_unaccount_event() before clearing the pmu ptr so that nr_include_guest_events counts are maintained correctly.

Fixes eff95e170275 ("perf: Add APIs to create/release mediated guest vPMUs")
Assisted-by Claude:Claude-Sonnet-5
---
 kernel/events/core.c | 4 ++++
 1 file changed, 4 insertions(+)
kernel/events/core.c +4 -0
index bd8c1acf59e2e..60e60809bedc7 100644
@@ -6350,6 +6350,9 @@ static DEFINE_MUTEX(perf_mediated_pmu_mutex);
 /* !exclude_guest event of PMU with PERF_PMU_CAP_MEDIATED_VPMU */
 static inline bool is_include_guest_event(struct perf_event *event)
 {
+	if (!event->pmu)
+		return false;
+
 	if ((event->pmu->capabilities & PERF_PMU_CAP_MEDIATED_VPMU) &&
 	    !event->attr.exclude_guest)
 		return true;
@@ -12977,6 +12980,7 @@ static void __pmu_detach_event(struct pmu *pmu, struct perf_event *event,
 	exclusive_event_destroy(event);
 	module_put(pmu->module);
 
+	mediated_pmu_unaccount_event(event);
 	event->pmu = NULL; /* force fault instead of UAF */
 }
 
Signature · 1 line
2.53.0
author maintainer
#230 lore raw

PATCH 227/438 sched/core: Avoid false migration warning for proxy donors

In reply to Greg Kroah-Hartman

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

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

From: Andrea Righi <[email protected]>

[ Upstream commit fe3c73d7bc769e7afc252f867a3421fe168b898d ]

Proxy execution can move a blocked donor's scheduling context to the lock owner's CPU even when the donor is migration-disabled. The donor does not execute there, and its original execution CPU remains recorded in wake_cpu.

set_task_cpu() warns unconditionally for migration-disabled tasks, so a subsequent proxy migration or the wakeup path returning the donor home triggers a false positive: moving a blocked scheduling context does not violate the migration-disabled execution context.

For example, creating a mutex owner on CPU1 and a migration-disabled waiter on CPU0 can trigger the following warning:

proxy_migrate_repro: donor blocking on CPU0 with migration disabled
proxy_migrate_repro: donor moved from CPU0 to CPU1

  WARNING: kernel/sched/core.c:3389 at set_task_cpu+0x1d3/0x280
  ...
  Call Trace:
   try_to_wake_up+0x43f/0x780
   __mutex_unlock_slowpath+0x330/0x540
   owner_fn+0x9f/0xc0 [proxy_migrate_repro]
  ...
  proxy_migrate_repro: donor woke on CPU0, task_cpu=0
  proxy_migrate_repro: completed

Exclude blocked proxy donors from the warning. The proxy wakeup path restores an executable placement before clearing the blocked state.

Fixes b049b81bdff6 ("sched: Handle blocked-waiter migration (and return migration)")
---
 kernel/sched/core.c | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)
kernel/sched/core.c +8 -1
index 35d647c8c3683..3d6c55598726f 100644
@@ -3352,6 +3352,8 @@ void relax_compatible_cpus_allowed_ptr(struct task_struct *p)
 void set_task_cpu(struct task_struct *p, unsigned int new_cpu)
 {
 	unsigned int state = READ_ONCE(p->__state);
+	bool proxy_migrated = sched_proxy_exec() && p->is_blocked &&
+			      task_cpu(p) != p->wake_cpu;
 
 	/*
 	 * We should never call set_task_cpu() on a blocked task,
@@ -3387,7 +3389,12 @@ void set_task_cpu(struct task_struct *p, unsigned int new_cpu)
 	 */
 	WARN_ON_ONCE(!cpu_online(new_cpu));
 
-	WARN_ON_ONCE(is_migration_disabled(p));
+	/*
+	 * Proxy execution can move a blocked task's scheduling context to any
+	 * CPU without moving its migration-disabled execution context. The
+	 * wakeup path will return the task to a CPU where it can execute.
+	 */
+	WARN_ON_ONCE(is_migration_disabled(p) && !proxy_migrated);
 
 	trace_sched_migrate_task(p, new_cpu);
 
Signature · 1 line
2.53.0
author maintainer
#231 lore raw

PATCH 228/438 x86/fred: Reconstruct the #GP context for rejected INT instructions

In reply to Greg Kroah-Hartman

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

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

From: Matthew Schwartz <[email protected]>

[ Upstream commit 93f53499d0b945e8ae447f497faf743d60069f61 ]

FRED event delivery does not use the IDT, so the gate DPL check that rejects a user INT n falls to software (Intel FRED specification [1], section 8.3). fred_intx() rejects the same vectors as IDT delivery, but reports a zero error code and the IP after the INT. This breaks the signal ABI. Wine uses the error code to recognize INT 0x2d, so the changed context turns a handled breakpoint into an access violation in Elden Ring.

Rewind IP using the instruction length in the augmented SS and synthesize the IDT selector error code, (vector << 3) | 2. Set RF in the saved flags, as the CPU does for a #GP fault. Section 5.2.1 defines the saved vector, instruction length and RF state. The supplied length handles prefixes without reading user memory. Limit the changes to already-rejected software interrupts, preserving the accepted INT3, INT4 and enabled INT80 paths and hardware exceptions. With IA32 emulation disabled, INT 0x80 now reports the same #GP as the DPL 0 gate IDT installs there. The rewound IP also stops fixup_iopl_exception() from inspecting the byte after the INT.

Also clear the software event flag. Section 6.2.3 specifies that ERETU with this flag and TF set traps before executing any user instruction. A tracer that suppresses SIGSEGV and resumes with TF set expects the next instruction to run first, as after IRET. The sigreturn path clears the same flag for this reason in prevent_single_step_upon_eretu().

[1] Intel Flexible Return and Event Delivery (FRED) Specification, revision 9.0 (346446-009US), sections 5.2.1, 6.2.3 and 8.3.

---
 arch/x86/entry/entry_fred.c | 11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)
arch/x86/entry/entry_fred.c +10 -1
index fb3594ddf731f..854899bfec5d9 100644
@@ -10,6 +10,7 @@
 #include <asm/desc.h>
 #include <asm/fred.h>
 #include <asm/idtentry.h>
+#include <asm/processor-flags.h>
 #include <asm/syscall.h>
 #include <asm/trapnr.h>
 #include <asm/traps.h>
@@ -71,7 +72,15 @@ static noinstr void fred_intx(struct pt_regs *regs)
 #endif
 
 	default:
-		return exc_general_protection(regs, 0);
+		/*
+		 * Reconstruct the #GP fault state that IDT delivery would produce.
+		 * Clear the software event flag so ERETU with TF set does not trap
+		 * before the resumed instruction. See prevent_single_step_upon_eretu().
+		 */
+		regs->ip -= regs->fred_ss.insnlen;
+		regs->flags |= X86_EFLAGS_RF;
+		regs->fred_ss.swevent = 0;
+		return exc_general_protection(regs, (regs->fred_ss.vector << 3) | 2);
 	}
 }
 
Signature · 1 line
2.53.0
author maintainer
#232 lore raw

PATCH 229/438 Input: eeti_ts - publish the OF module alias

In reply to Greg Kroah-Hartman

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

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

From: hpp.iscas <[email protected]>

[ Upstream commit a52ae68a937efc353251aec27fc995ff66cbe1ca ]

The EETI driver matches eeti,exc3000-i2c Device Tree clients, but only publishes the legacy eeti_ts I2C ID. The I2C core emits an OF modalias for a Device Tree client.

Publish the existing OF match table within its CONFIG_OF guard.

Fixes e32d7f1b246c ("Input: eeti - add device tree matching table")
---
 drivers/input/touchscreen/eeti_ts.c | 1 +
 1 file changed, 1 insertion(+)
drivers/input/touchscreen/eeti_ts.c +1 -0
index b12602bc368ef..b870a939508bb 100644
@@ -276,6 +276,7 @@ static const struct of_device_id of_eeti_ts_match[] = {
 	{ .compatible = "eeti,exc3000-i2c", },
 	{ }
 };
+MODULE_DEVICE_TABLE(of, of_eeti_ts_match);
 #endif
 
 static struct i2c_driver eeti_ts_driver = {
Signature · 1 line
2.53.0
author maintainer
#233 lore raw

PATCH 230/438 hwmon: (gpio-fan) return IRQ_HANDLED from the shared alarm IRQ handler

In reply to Greg Kroah-Hartman

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

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

From: Cong Nguyen <[email protected]>

[ Upstream commit bdf5f731957de48acada392f28e82bc019713adb ]

fan_alarm_irq_handler() always schedules alarm_work but returns IRQ_NONE, so the kernel treats every alarm interrupt as unhandled. On a shared line that risks the whole line being disabled as spurious.

v1 just fixed that, but it was still IRQF_SHARED, and always returning IRQ_HANDLED there defeats spurious-interrupt detection for the line -- if the interrupt ever fires without a real event, nothing catches it, and a fault could spin the CPU in the handler.

Sashiko flagged this in v1, and Guenter confirmed: this interrupt must not be shared. So v2 drops IRQF_SHARED too.

---
 drivers/hwmon/gpio-fan.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/hwmon/gpio-fan.c +2 -2
index df8bd97076050..3f78375eeb448 100644
@@ -68,7 +68,7 @@ static irqreturn_t fan_alarm_irq_handler(int irq, void *dev_id)
 
 	schedule_work(&fan_data->alarm_work);
 
-	return IRQ_NONE;
+	return IRQ_HANDLED;
 }
 
 static ssize_t fan1_alarm_show(struct device *dev,
@@ -103,7 +103,7 @@ static int fan_alarm_init(struct gpio_fan_data *fan_data)
 
 	irq_set_irq_type(alarm_irq, IRQ_TYPE_EDGE_BOTH);
 	return devm_request_irq(dev, alarm_irq, fan_alarm_irq_handler,
-				IRQF_SHARED, "GPIO fan alarm", fan_data);
+				0, "GPIO fan alarm", fan_data);
 }
 
 /*
Signature · 1 line
2.53.0
author maintainer
#234 lore raw

PATCH 231/438 hwmon: (hp-wmi-sensors) Improve raw WMI string handling

In reply to Greg Kroah-Hartman

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

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

From: James Seo <[email protected]>

[ Upstream commit 92b68492eae701e5b0e9d142ffe229921af7b1fa ]

Commit c9ba59258094 ("hwmon: (hp-wmi-sensors) Fix failure to load on EliteDesk 800 G6") left out some logic for recognizing raw WMI strings in check_numeric_sensor_wobj(). This issue was reported by a user along with an incomplete and unsuitable proposed solution [1].

Add the missing logic and properly remedy the issue. Also slightly refactor how raw WMI strings are recognized elsewhere to make the intent that they should be treated as regular ACPI strings clearer.

---
 drivers/hwmon/hp-wmi-sensors.c | 30 ++++++++++++++++++++++--------
 1 file changed, 22 insertions(+), 8 deletions(-)
drivers/hwmon/hp-wmi-sensors.c +22 -8
index 03c684ba83bd6..cb2eb9bd278a2 100644
@@ -526,14 +526,12 @@ static int check_wobj(const union acpi_object *wobj,
 	for (prop = 0; prop <= last_prop; prop++) {
 		type = elements[prop].type;
 		valid_type = property_map[prop];
-		if (type != valid_type) {
-			if (type == ACPI_TYPE_BUFFER &&
-			    valid_type == ACPI_TYPE_STRING &&
-			    is_raw_wmi_string(elements[prop].buffer.pointer,
-					      elements[prop].buffer.length))
-				continue;
+		if (type == ACPI_TYPE_BUFFER &&
+		    is_raw_wmi_string(elements[prop].buffer.pointer,
+				      elements[prop].buffer.length))
+			type = ACPI_TYPE_STRING;
+		if (type != valid_type)
 			return -EINVAL;
-		}
 	}
 
 	return 0;
@@ -579,6 +577,7 @@ static int check_numeric_sensor_wobj(const union acpi_object *wobj,
 	int prop = HP_WMI_PROPERTY_NAME;
 	acpi_object_type valid_type;
 	union acpi_object *elements;
+	union acpi_object *element;
 	u32 elem_count;
 	int last_prop;
 	bool is_new;
@@ -602,13 +601,20 @@ static int check_numeric_sensor_wobj(const union acpi_object *wobj,
 	    elem_count > HP_WMI_MAX_PROPERTIES)
 		return -EINVAL;
 
-	type = elements[HP_WMI_PROPERTY_SIZE].type;
+	element = &elements[HP_WMI_PROPERTY_SIZE];
+	type = element->type;
 	switch (type) {
 	case ACPI_TYPE_INTEGER:
 		is_new = true;
 		last_prop = HP_WMI_PROPERTY_RATE_UNITS;
 		break;
 
+	case ACPI_TYPE_BUFFER:
+		if (!is_raw_wmi_string(element->buffer.pointer,
+				       element->buffer.length))
+			return -EINVAL;
+		fallthrough;
+
 	case ACPI_TYPE_STRING:
 		is_new = false;
 		last_prop = HP_WMI_PROPERTY_CURRENT_READING;
@@ -631,6 +637,10 @@ static int check_numeric_sensor_wobj(const union acpi_object *wobj,
 	for (i = 0; i < elem_count && prop <= last_prop; i++, prop++) {
 		type = elements[i].type;
 		valid_type = hp_wmi_property_map[prop];
+		if (type == ACPI_TYPE_BUFFER &&
+		    is_raw_wmi_string(elements[i].buffer.pointer,
+				      elements[i].buffer.length))
+			type = ACPI_TYPE_STRING;
 		if (type != valid_type)
 			return -EINVAL;
 
@@ -651,6 +661,10 @@ static int check_numeric_sensor_wobj(const union acpi_object *wobj,
 			/* PossibleStates[0] has already been type-checked. */
 			for (j = 0; i + 1 < elem_count && j + 1 < count; j++) {
 				type = elements[++i].type;
+				if (type == ACPI_TYPE_BUFFER &&
+				    is_raw_wmi_string(elements[i].buffer.pointer,
+						      elements[i].buffer.length))
+					type = ACPI_TYPE_STRING;
 				if (type != valid_type)
 					return -EINVAL;
 			}
Signature · 1 line
2.53.0
author maintainer
#235 lore raw

PATCH 232/438 watchdog: da9062: fix suspend/resume handling of HW_RUNNING watchdog

In reply to Greg Kroah-Hartman

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

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

From: Li Jun <[email protected]>

[ Upstream commit 5071122bf5a628494db16d98d253f622a5aab074 ]

da9062_wdt_suspend() and da9062_wdt_resume() only check watchdog_active(), when the watchdog is left running by the driver sets WDOG_HW_RUNNING in da9062_wdt_probe() but userspace never opens the device, so WDOG_ACTIVE remains cleared, the wdt_disable() will not be executed in da9062_wdt_suspend. In this case, the suspend callback is a no-op and the watchdog keeps counting during system suspend, leading to an unexpected system reset.
Check WDOG_HW_RUNNING and wdt->wdd,can fix this issue.

Fixes f6c98b08381c7 ("watchdog: da9062: add power management ops")

Cs: [email protected]

---
 drivers/watchdog/da9062_wdt.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/watchdog/da9062_wdt.c +2 -2
index 426962547df16..4d558652e9e71 100644
@@ -256,7 +256,7 @@ static int __maybe_unused da9062_wdt_suspend(struct device *dev)
 	if (!wdt->use_sw_pm)
 		return 0;
 
-	if (watchdog_active(wdd))
+	if (watchdog_active(wdd) || watchdog_hw_running(wdd))
 		return da9062_wdt_stop(wdd);
 
 	return 0;
@@ -270,7 +270,7 @@ static int __maybe_unused da9062_wdt_resume(struct device *dev)
 	if (!wdt->use_sw_pm)
 		return 0;
 
-	if (watchdog_active(wdd))
+	if (watchdog_active(wdd) || watchdog_hw_running(wdd))
 		return da9062_wdt_start(wdd);
 
 	return 0;
Signature · 1 line
2.53.0
author maintainer
#236 lore raw

PATCH 233/438 Input: xpad - add support for Victrix Pro BFG Controller

In reply to Greg Kroah-Hartman

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

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

From: Erich Sartison <[email protected]>

commit 971fa7ea8621e123feb9c8d7dc61be1c656bd945 upstream.

The controller doesn't currently work via USB-cable.

---
 drivers/input/joystick/xpad.c |    1 +
 1 file changed, 1 insertion(+)
drivers/input/joystick/xpad.c +1 -0
@@ -227,6 +227,7 @@ static const struct xpad_device {
 	{ 0x0e6f, 0x0213, "Afterglow Gamepad for Xbox 360", 0, XTYPE_XBOX360 },
 	{ 0x0e6f, 0x021f, "Rock Candy Gamepad for Xbox 360", 0, XTYPE_XBOX360 },
 	{ 0x0e6f, 0x0246, "Rock Candy Gamepad for Xbox One 2015", 0, XTYPE_XBOXONE },
+	{ 0x0e6f, 0x024c, "PDP Victrix Pro BFG Wired Controller for Xbox", 0, XTYPE_XBOXONE },
 	{ 0x0e6f, 0x02a0, "PDP Xbox One Controller", 0, XTYPE_XBOXONE },
 	{ 0x0e6f, 0x02a1, "PDP Xbox One Controller", 0, XTYPE_XBOXONE },
 	{ 0x0e6f, 0x02a2, "PDP Wired Controller for Xbox One - Crimson Red", 0, XTYPE_XBOXONE },
author maintainer
#237 lore raw

PATCH 234/438 Input: xpad - add support for Azeron devices

In reply to Greg Kroah-Hartman

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

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

From: Roberts Kursitis <[email protected]>

commit cba76c0f47af1a389d718c5bb69e75cbd67bba98 upstream.

Azeron controllers (Cyro, Cyborg, Classic/Compact, Cyro Lefty, Cyborg II and Keyzen) present a standard Xbox 360 controller interface, so they work with the existing xpad driver once their USB IDs are added.

The 0x16d0 vendor ID is a shared block, but this is safe because xpad only binds interfaces that match the Xbox 360 signature.

Tested with an Azeron Keyzen.

---
 drivers/input/joystick/xpad.c |    7 +++++++
 1 file changed, 7 insertions(+)
drivers/input/joystick/xpad.c +7 -0
@@ -293,6 +293,12 @@ static const struct xpad_device {
 	{ 0x1689, 0xfd00, "Razer Onza Tournament Edition", 0, XTYPE_XBOX360 },
 	{ 0x1689, 0xfd01, "Razer Onza Classic Edition", 0, XTYPE_XBOX360 },
 	{ 0x1689, 0xfe00, "Razer Sabertooth", 0, XTYPE_XBOX360 },
+	{ 0x16d0, 0x1103, "Azeron Cyro", 0, XTYPE_XBOX360 },
+	{ 0x16d0, 0x113c, "Azeron Cyborg", 0, XTYPE_XBOX360 },
+	{ 0x16d0, 0x1192, "Azeron Classic/Compact", 0, XTYPE_XBOX360 },
+	{ 0x16d0, 0x1212, "Azeron Cyro Lefty", 0, XTYPE_XBOX360 },
+	{ 0x16d0, 0x12f7, "Azeron Cyborg II", 0, XTYPE_XBOX360 },
+	{ 0x16d0, 0x13ea, "Azeron Keyzen", 0, XTYPE_XBOX360 },
 	{ 0x17ef, 0x6182, "Lenovo Legion Controller for Windows", 0, XTYPE_XBOX360 },
 	{ 0x1949, 0x041a, "Amazon Game Controller", 0, XTYPE_XBOX360 },
 	{ 0x1a86, 0xe310, "Legion Go S", 0, XTYPE_XBOX360 },
@@ -535,6 +541,7 @@ static const struct usb_device_id xpad_t
 	XPAD_XBOX360_VENDOR(0x15e4),		/* Numark Xbox 360 controllers */
 	XPAD_XBOX360_VENDOR(0x162e),		/* Joytech Xbox 360 controllers */
 	XPAD_XBOX360_VENDOR(0x1689),		/* Razer Onza */
+	XPAD_XBOX360_VENDOR(0x16d0),		/* Azeron controllers */
 	XPAD_XBOX360_VENDOR(0x17ef),		/* Lenovo */
 	XPAD_XBOX360_VENDOR(0x1949),		/* Amazon controllers */
 	XPAD_XBOX360_VENDOR(0x1a86),		/* Nanjing Qinheng Microelectronics (WCH) */
author maintainer
#238 lore raw

PATCH 235/438 Input: xpad - fix PDP Marvel Xbox 360 controller

In reply to Greg Kroah-Hartman

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

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

From: Jeremy Nyberg <[email protected]>

commit 7bc369cb3d3f3656eb77285628ee264264d28ad4 upstream.

The PDP Marvel Xbox 360 controller with USB ID 0e6f:0147 is incorrectly classified as an Xbox One controller.

With the current XTYPE_XBOXONE classification, the controller is detected but produces no input, while its four player LEDs continue blinking indefinitely.

Classify USB ID 0e6f:0147 as an Xbox 360 controller instead.

Tested on a PDP Marvel Xbox 360 controller with USB ID 0e6f:0147. All inputs register correctly and the player LED indicates the current player.

Fixes c225370e01b8 ("Input: xpad - sync supported devices with 360Controller")
---
 drivers/input/joystick/xpad.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/input/joystick/xpad.c +1 -1
@@ -215,7 +215,7 @@ static const struct xpad_device {
 	{ 0x0e6f, 0x0139, "Afterglow Prismatic Wired Controller", 0, XTYPE_XBOXONE },
 	{ 0x0e6f, 0x013a, "PDP Xbox One Controller", 0, XTYPE_XBOXONE },
 	{ 0x0e6f, 0x0146, "Rock Candy Wired Controller for Xbox One", 0, XTYPE_XBOXONE },
-	{ 0x0e6f, 0x0147, "PDP Marvel Xbox One Controller", 0, XTYPE_XBOXONE },
+	{ 0x0e6f, 0x0147, "PDP Marvel Xbox 360 Controller", 0, XTYPE_XBOX360 },
 	{ 0x0e6f, 0x015c, "PDP Xbox One Arcade Stick", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOXONE },
 	{ 0x0e6f, 0x015d, "PDP Mirror's Edge Official Wired Controller for Xbox One", 0, XTYPE_XBOXONE },
 	{ 0x0e6f, 0x0161, "PDP Xbox One Controller", 0, XTYPE_XBOXONE },
author maintainer
#239 lore raw

PATCH 236/438 9p: Fix v9fs_issue_write() to update i_size and remote_i_size

In reply to Greg Kroah-Hartman

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

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

From: David Howells <[email protected]>

commit c60ae98c5aa64021751b38ab1313b19d620bf640 upstream.

Fix v9fs_issue_write() to update i_size and remote_i_size to the new size of the server file if we made it larger, using the start fpos and the count returned by p9_client_write() to calculate the new minimum file size.

This assumes that if the 9P server makes a short write (say it hits ENOSPC), a reduced count is returned.

Fixes 5fb70e7275a6 ("netfs, 9p: Implement helpers for new write code")

Message-ID: <[email protected]>

---
 fs/9p/vfs_addr.c |   28 +++++++++++++++++++++++++++-
 1 file changed, 27 insertions(+), 1 deletion(-)
fs/9p/vfs_addr.c +27 -1
@@ -54,11 +54,37 @@ static void v9fs_begin_writeback(struct
 static void v9fs_issue_write(struct netfs_io_subrequest *subreq)
 {
 	struct p9_fid *fid = subreq->rreq->netfs_priv;
+	struct inode *inode = subreq->rreq->inode;
+	struct netfs_inode *ictx = netfs_inode(inode);
 	int err, len;
 
 	len = p9_client_write(fid, subreq->start, &subreq->io_iter, &err);
-	if (len > 0)
+	if (len > 0) {
+		uoff_t end = subreq->start + len, i_size, remote, zp;
+		bool set = false;
+
+		spin_lock(&inode->i_lock);
+
+		/* We can read the sizes directly as we hold i_lock. */
+		i_size = inode->i_size;
+		remote = ictx->_remote_i_size;
+		zp = ictx->_zero_point;
+
+		if (end > i_size) {
+			i_size = end;
+			set = true;
+		}
+		if (end > remote) {
+			remote = end;
+			set = true;
+		}
+
+		if (set)
+			netfs_write_sizes(inode, i_size, remote, zp);
+		spin_unlock(&inode->i_lock);
+
 		__set_bit(NETFS_SREQ_MADE_PROGRESS, &subreq->flags);
+	}
 	netfs_write_subrequest_terminated(subreq, len ?: err);
 }
 
author maintainer
#240 lore raw

PATCH 237/438 ALSA: core: Fix potential UAF after asynchronous card release

In reply to Greg Kroah-Hartman

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

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

From: Takashi Iwai <[email protected]>

commit fd95e68df6fe66344161a1329cbe5e5805e7b704 upstream.

Usually a sound driver releases the resources assigned to the card via snd_card_free(), and it synchronizes with the whole release procedure. However, when the card is released asynchronously via snd_card_free_when_closed() like USB-audio driver, the situation is slightly different; although the snd_card_disconnect() call at the disconnection guarantees that any newer accesses will be gated, the in-flight tasks might be still accessing to the underlying card->dev device even after the disconnection, which would cause a use-after-free in the end, as reported by fuzzers.

For addressing the bug above, this patch takes the refcount of card->dev at initialization of the card object, and releases at its
destructor. This assures the availability of the card->dev in its
whole lifecycle.

---
 sound/core/init.c |    3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
sound/core/init.c +2 -1
@@ -310,7 +310,7 @@ static int snd_card_init(struct snd_card
 			kfree(card); /* manually free here, as no destructor called */
 		return err;
 	}
-	card->dev = parent;
+	card->dev = get_device(parent);
 	card->number = idx;
 	WARN_ON(IS_MODULE(CONFIG_SND) && !module);
 	card->module = module;
@@ -601,6 +601,7 @@ static int snd_card_do_free(struct snd_c
 		dev_warn(card->dev, "unable to free card info\n");
 		/* Not fatal error */
 	}
+	put_device(card->dev);
 	if (card->release_completion)
 		complete(card->release_completion);
 	if (!managed)
author maintainer
#241 lore raw

PATCH 238/438 ALSA: usb-audio: Clamp implicit feedback packet count to URB capacity

In reply to Greg Kroah-Hartman

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

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

From: Xiang Mei <[email protected]>

commit 76a986c980bb502c7688d605ac7a67fd257a9a1b upstream.

data_ep_set_params() allocates each data URB for exactly u->packets isochronous frames, so urb->iso_frame_desc[] has u->packets slots and ctx->packets is the driver's only record of that limit. For an implicit feedback sink, snd_usb_queue_pending_output_urbs() overwrites it with the sync source's packet count, which is calculated independently from the capture endpoint's parameters. When that count is larger, prepare_playback_urb() and prepare_silent_urb() can write iso_frame_desc[] past the allocation; their existing bounds limit payload bytes, not the descriptor index.

The reproducer uses a high-speed UAC2 device declaring bInterval 1 for implicit feedback capture (8 packets) and bInterval 4 for playback (1 packet). On the first capture completion after the stream starts, it accesses seven descriptors spanning 112 bytes beyond the one-packet URB:

  BUG: KASAN: slab-out-of-bounds in prepare_playback_urb (sound/usb/pcm.c:1560)
  Write of size 4 at addr ffff88801e696ad0 by task vhci_rx/178
   prepare_playback_urb (sound/usb/pcm.c:1560)
   prepare_outbound_urb (sound/usb/endpoint.c:340)
   snd_usb_queue_pending_output_urbs (sound/usb/endpoint.c:501)
   snd_complete_urb (sound/usb/endpoint.c:1834)
   __usb_hcd_giveback_urb (drivers/usb/core/hcd.c:1657)
   usb_hcd_giveback_urb (drivers/usb/core/hcd.c:1741)
   vhci_rx_loop (drivers/usb/usbip/vhci_rx.c:107)
   kthread (kernel/kthread.c:436)
  The buggy address belongs to the object at ffff88801e696a00
   which belongs to the cache kmalloc-256 of size 256
  The buggy address is located 0 bytes to the right of
   allocated 208-byte region [ffff88801e696a00, ffff88801e696ad0)

Record the allocated packet count per endpoint and clamp both the adopted count and the packet-size copy to it. Fold the Format Type II delimiter into urb_packs before the allocation loop so the recorded limit matches every URB.

Fixes cf044e441902 ("ALSA: usb-audio: Update the number of packets properly at receiving")
Reported-by [email protected]
Assisted-by Claude:claude-opus-5
---
 sound/usb/card.h     |    1 +
 sound/usb/endpoint.c |   12 +++++++-----
 2 files changed, 8 insertions(+), 5 deletions(-)
sound/usb/card.h +1 -0
@@ -116,6 +116,7 @@ struct snd_usb_endpoint {
 	unsigned int phase;		/* phase accumulator */
 	unsigned int maxpacksize;	/* max packet size in bytes */
 	unsigned int maxframesize;      /* max packet size in frames */
+	unsigned int max_urb_packs;	/* packets allocated per data URB */
 	unsigned int max_urb_frames;	/* max URB size in frames */
 	unsigned int curpacksize;	/* current packet size in bytes (for capture) */
 	unsigned int curframesize;      /* current packet size in frames (for capture) */
sound/usb/endpoint.c +7 -5
@@ -492,9 +492,10 @@ int snd_usb_queue_pending_output_urbs(st
 
 		/* copy over the length information */
 		if (implicit_fb) {
-			ctx->packets = packet->packets;
+			ctx->packets = min_t(int, packet->packets,
+					     ep->max_urb_packs);
 			memcpy(ctx->packet_size, packet->packet_size,
-			       packet->packets * sizeof(packet->packet_size[0]));
+			       ctx->packets * sizeof(packet->packet_size[0]));
 		}
 
 		/* call the data handler to fill in playback data */
@@ -1239,15 +1240,16 @@ static int data_ep_set_params(struct snd
 		ep->nurbs = min(max_urbs, urbs_per_period * ep->cur_buffer_periods);
 	}
 
+	if (fmt->fmt_type == UAC_FORMAT_TYPE_II)
+		urb_packs++; /* for transfer delimiter */
+	ep->max_urb_packs = urb_packs;
+
 	/* allocate and initialize data urbs */
 	for (i = 0; i < ep->nurbs; i++) {
 		struct snd_urb_ctx *u = &ep->urb[i];
 		u->index = i;
 		u->ep = ep;
 		u->packets = urb_packs;
-
-		if (fmt->fmt_type == UAC_FORMAT_TYPE_II)
-			u->packets++; /* for transfer delimiter */
 		u->buffer_size = maxsize * u->packets;
 		u->urb = usb_alloc_urb(u->packets, GFP_KERNEL);
 		if (!u->urb)
author maintainer
#242 lore raw

PATCH 239/438 ALSA: virtio: reset device before deleting virtqueues

In reply to Greg Kroah-Hartman

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

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

From: Yuho Choi <[email protected]>

commit 6c05d00af307560e6a9f1631d6270d3df5aa2272 upstream.

virtsnd_remove() and virtsnd_freeze() delete the virtqueues before resetting the device. del_vqs() frees the vring backing, but does not provide a generic device quiesce operation. In particular, modern virtio-pci keeps enabled queues active until the device is reset.

Reset the device before deleting the virtqueues so it can no longer access the vring memory when that memory is released. This also covers probe failures after DRIVER_OK, which unwind through virtsnd_remove().

Fixes de3a9980d8c3 ("ALSA: virtio: add virtio sound driver")
Fixes 575483e90a32 ("ALSA: virtio: introduce device suspend/resume support")
---
 sound/virtio/virtio_card.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
sound/virtio/virtio_card.c +2 -2
@@ -354,8 +354,8 @@ static void virtsnd_remove(struct virtio
 	if (snd->card)
 		snd_card_free(snd->card);
 
-	vdev->config->del_vqs(vdev);
 	virtio_reset_device(vdev);
+	vdev->config->del_vqs(vdev);
 
 	for (i = 0; snd->substreams && i < snd->nsubstreams; ++i) {
 		struct virtio_pcm_substream *vss = &snd->substreams[i];
@@ -383,8 +383,8 @@ static int virtsnd_freeze(struct virtio_
 	virtsnd_disable_event_vq(snd);
 	virtsnd_ctl_msg_cancel_all(snd);
 
-	vdev->config->del_vqs(vdev);
 	virtio_reset_device(vdev);
+	vdev->config->del_vqs(vdev);
 
 	for (i = 0; i < snd->nsubstreams; ++i)
 		cancel_work_sync(&snd->substreams[i].elapsed_period);
author maintainer
#243 lore raw

PATCH 240/438 ASoC: codecs: rt712-sdca-dmic: fix uninitialized stream_config->type

In reply to Greg Kroah-Hartman

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

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

From: Jiangshan Yi <[email protected]>

commit 03a5699a0a04309c597683967aaaf25d1e555ea2 upstream.

stream_config is not initialized before being passed to sdw_stream_add_slave(). The type field may contain garbage and is later copied to stream->type by sdw_config_stream().

Zero-initialize stream_config so type defaults to SDW_STREAM_PCM.

While at it, use snd_sdw_params_to_config() helper instead of open-coding the same logic.

---
 sound/soc/codecs/rt712-sdca-dmic.c |   14 +++++---------
 1 file changed, 5 insertions(+), 9 deletions(-)
sound/soc/codecs/rt712-sdca-dmic.c +5 -9
@@ -13,6 +13,7 @@
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
+#include <sound/sdw.h>
 #include <sound/tlv.h>
 #include "rt712-sdca.h"
 #include "rt712-sdca-dmic.h"
@@ -632,10 +633,10 @@ static int rt712_sdca_dmic_hw_params(str
 {
 	struct snd_soc_component *component = dai->component;
 	struct rt712_sdca_dmic_priv *rt712 = snd_soc_component_get_drvdata(component);
-	struct sdw_stream_config stream_config;
+	struct sdw_stream_config stream_config = {0};
 	struct sdw_port_config port_config;
 	struct sdw_stream_runtime *sdw_stream;
-	int retval, num_channels;
+	int retval;
 	unsigned int sampling_rate;
 
 	dev_dbg(dai->dev, "%s %s", __func__, dai->name);
@@ -647,13 +648,8 @@ static int rt712_sdca_dmic_hw_params(str
 	if (!rt712->slave)
 		return -EINVAL;
 
-	stream_config.frame_rate = params_rate(params);
-	stream_config.ch_count = params_channels(params);
-	stream_config.bps = snd_pcm_format_width(params_format(params));
-	stream_config.direction = SDW_DATA_DIR_TX;
-
-	num_channels = params_channels(params);
-	port_config.ch_mask = GENMASK(num_channels - 1, 0);
+	/* SoundWire specific configuration */
+	snd_sdw_params_to_config(substream, params, &stream_config, &port_config);
 	port_config.num = 2;
 
 	retval = sdw_stream_add_slave(rt712->slave, &stream_config,
author maintainer
#244 lore raw

PATCH 241/438 cgroup: Avoid iteration of dying tasks with zero refcount

In reply to Greg Kroah-Hartman

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

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

From: Michal Koutný <[email protected]>

commit 057dac23d329d5c5ed62352f2659a39fd46c6d4a upstream.

The commit 260fbcb92bbea ("cgroup: Move dying_tasks cleanup from cgroup_task_release() to cgroup_task_free()") extended the lifetime of tasks on the dying_tasks list.
The iterators have provision to go through dying_tasks because of dying threadgroup leaders or explicit CSS_TASK_ITER_WITH_DEAD, however, it was expected that such tasks can obtain a new reference (that is possible before cgroup_task_release()/put_task_struct_rcu_user()). The tasks after cgroup_task_release() and before cgroup_task_free() are subject to race when they may or may not have ->usage count > 0.

The race window is between css_task_iter_next() invocations when css_set_lock is released and we may arrive at a new ->task_pos. The iterator should not attempt to resurrect tasks whose ->usage count dropped to zero. (When that happens, __put_task_struct_rcu_cb() is already imminent and the returned task_struct would could be used after free.)

As for the fix, we cannot simply check the signal->live count of a task on the dying list because that won't distinguish regular zombies waiting to be reaped from RCU remnant tasks that are going to be free'd. Therefore add an extra check to rule out ->usage==0 tasks from any iteration.

The repeat: loop in css_task_iter_advance() doesn't consider ->usage count, so add a new loop to css_task_iter_next() to skip de-used tasks on the dying_list.

Rough illustration of the possible race

R (reader of cgroup.procs) T (thread) L (group leader)
--------------------------------- -------------------------------- --------------------------------

                                                                      L exits, signal->live > 0
                                                                      cgroup_task_dead(L)
                                                                        css_set_skip_task_iters() // skips only cset->tasks
                                                                        list_add_tail(&L->cg_list, &cset->dying_tasks)
  css_task_iter_next()
    take css_set_lock
    css_task_iter_advance()
      leader && signal->live != 0
      => it->task_pos = &L->cg_list
    release css_set_lock
                                     T exits
                                     --signal->live == 0
				     cgroup_task_dead(T) // css_set_lock
                                     release_task(T)
                                       cgroup_task_release(T)
                                       release_task(L) // zap_leader
                                         cgroup_task_release(L)
                                         put_task_struct_rcu_user(L)
                                         ...RCU...
                                         put_task_struct(L)
                                           L->usage = 0
                                           /* L still on dying_tasks */
                                           ...RCU...
                                           __put_task_struct(L)
  css_task_iter_next() // another iteration
    take css_set_lock
    it->task_pos = &L->cg_list
    get_task_struct(L)
      => addition on 0
    drop css_set_lock
                                           cgroup_task_free(L)
                                             css_set_skip_task_iters() // dying skip comes too late
                                           free_task(L)
  cgroup_procs_show()
    task_pid_vnr(L)
---
 kernel/cgroup/cgroup.c |    7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)
kernel/cgroup/cgroup.c +5 -2
@@ -5228,10 +5228,13 @@ struct task_struct *css_task_iter_next(s
 	if (it->flags & CSS_TASK_ITER_SKIPPED)
 		css_task_iter_advance(it);
 
-	if (it->task_pos) {
+	while (it->task_pos && !it->cur_task) {
 		it->cur_task = list_entry(it->task_pos, struct task_struct,
 					  cg_list);
-		get_task_struct(it->cur_task);
+		/* a task on dying_tasks with zero refcount is only valid for
+		 * RCU readers, not even interesting for
+		 * CSS_TASK_ITER_WITH_DEAD, find another one */
+		it->cur_task = tryget_task_struct(it->cur_task);
 		css_task_iter_advance(it);
 	}
 
author maintainer
#245 lore raw

PATCH 242/438 ata: libahci: clear PxCLBU and PxFBU for AHCI_HFLAG_32BIT_ONLY

In reply to Greg Kroah-Hartman

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

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

From: Niklas Cassel <[email protected]>

commit 82e47533221d4746947b74d2e79a478c36c6433a upstream.

A user reported that commit 105c42566a55 ("ata: ahci: force 32-bit DMA for JMicron JMB582/JMB585") made the JMicron JMB585 unusable on his board.

The failure is seen as soon as the ahci driver is probed, and booting with iommu=off does not solve the problem.

Looking at the AHCI specification, PxCLBU and PxFBU are both read only '0' for HBAs that do not support 64-bit addressing.

For HBAs that do support 64-bit addressing, the registers are read write, with a reset value that is Implementation Specific.

When using the AHCI_HFLAG_32BIT_ONLY flag, the HBA does support 64-bit addressing, and a 32-bit DMA mask is set by simply clearing HOST_CAP_64. Thus, in this case, we need to explicitly clear the registers to 0.

---
 drivers/ata/libahci.c |   15 ++++++++++++++-
 1 file changed, 14 insertions(+), 1 deletion(-)
drivers/ata/libahci.c +14 -1
@@ -744,15 +744,28 @@ void ahci_start_fis_rx(struct ata_port *
 	struct ahci_port_priv *pp = ap->private_data;
 	u32 tmp;
 
-	/* set FIS registers */
+	/*
+	 * On HBAs that only support 32-bit addressing PxCLBU is read only '0'.
+	 * When applying the AHCI_HFLAG_32BIT_ONLY quirk, PxCLBU is RW, and the
+	 * reset value is Implementation Specific, so we need to clear it to 0.
+	 */
 	if (hpriv->cap & HOST_CAP_64)
 		writel((pp->cmd_slot_dma >> 16) >> 16,
 		       port_mmio + PORT_LST_ADDR_HI);
+	else if (hpriv->flags & AHCI_HFLAG_32BIT_ONLY)
+		writel(0, port_mmio + PORT_LST_ADDR_HI);
 	writel(pp->cmd_slot_dma & 0xffffffff, port_mmio + PORT_LST_ADDR);
 
+	/*
+	 * On HBAs that only support 32-bit addressing PxFBU is read only '0'.
+	 * When applying the AHCI_HFLAG_32BIT_ONLY quirk, PxFBU is RW, and the
+	 * reset value is Implementation Specific, so we need to clear it to 0.
+	 */
 	if (hpriv->cap & HOST_CAP_64)
 		writel((pp->rx_fis_dma >> 16) >> 16,
 		       port_mmio + PORT_FIS_ADDR_HI);
+	else if (hpriv->flags & AHCI_HFLAG_32BIT_ONLY)
+		writel(0, port_mmio + PORT_FIS_ADDR_HI);
 	writel(pp->rx_fis_dma & 0xffffffff, port_mmio + PORT_FIS_ADDR);
 
 	/* enable FIS reception */
author maintainer
#246 lore raw

PATCH 243/438 ata: libahci_platform: Fix device reference leak in ahci_platform_get_resources()

In reply to Greg Kroah-Hartman

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

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

From: Wentao Liang <[email protected]>

commit 0d1cb83337f13af082afb68b28d3fdfe29cde7fb upstream.

of_find_device_by_node() takes a reference on the port platform device, which is only used to look up its port regulator and is never released, neither on success nor on the error paths. Drop the reference with put_device() once the regulator has been obtained, which covers both the success and error paths.

---
 drivers/ata/libahci_platform.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/ata/libahci_platform.c +1 -1
@@ -620,10 +620,10 @@ struct ahci_host_priv *ahci_platform_get
 			of_platform_device_create(child, NULL, NULL);
 
 			port_dev = of_find_device_by_node(child);
-
 			if (port_dev) {
 				rc = ahci_platform_get_regulator(hpriv, port,
 								&port_dev->dev);
+				put_device(&port_dev->dev);
 				if (rc == -EPROBE_DEFER)
 					goto err_out;
 			}
author maintainer
#247 lore raw

PATCH 244/438 cifs: Fix server use-after-free in cifs_chan_skip_or_disable()

In reply to Greg Kroah-Hartman

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

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

From: Wentao Liang <[email protected]>

commit 717e0a25036b6c92cecace30913b2d874a4c22b8 upstream.

When a secondary channel is no longer supported by the server, cifs_chan_skip_or_disable() drops the channel reference with cifs_put_tcp_session() and then continues to use the server pointer by calling cifs_signal_cifsd_for_reconnect() on it and reading its primary_server pointer. cifs_put_tcp_session() can drop the last reference of the channel and tear it down, so both the channel and the primary server (whose reference is also dropped by cifs_put_tcp_session()) can be freed before they are signaled for reconnect.

Signal the channel and the primary server and capture the primary server pointer before dropping the channel reference with cifs_put_tcp_session().

Fixes f591062bdbf4 ("cifs: handle servers that still advertise multichannel after disabling")
---
 fs/smb/client/smb2pdu.c |   13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)
fs/smb/client/smb2pdu.c +7 -6
@@ -189,18 +189,19 @@ cifs_chan_skip_or_disable(struct cifs_se
 		spin_unlock(&ses->chan_lock);
 
 		/*
-		 * the above reference of server by channel
-		 * needs to be dropped without holding chan_lock
-		 * as cifs_put_tcp_session takes a higher lock
-		 * i.e. cifs_tcp_ses_lock
+		 * signal the channel and its primary server to
+		 * reconnect before dropping the above reference of
+		 * server by channel, which is done without holding
+		 * chan_lock as cifs_put_tcp_session takes a higher
+		 * lock i.e. cifs_tcp_ses_lock
 		 */
-		cifs_put_tcp_session(server, from_reconnect);
-
 		cifs_signal_cifsd_for_reconnect(server, false);
 
 		/* mark primary server as needing reconnect */
 		pserver = server->primary_server;
 		cifs_signal_cifsd_for_reconnect(pserver, false);
+
+		cifs_put_tcp_session(server, from_reconnect);
 skip_terminate:
 		return -EHOSTDOWN;
 	}
author maintainer
#248 lore raw

PATCH 245/438 exec: Cleanup POSIX timers right after de_thread()

In reply to Greg Kroah-Hartman

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

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

From: Hyunwoo Kim <[email protected]>

commit acb03d3881818581052924a9bbbe92b8741ed448 upstream.

A per-thread CPU timer holds a reference to the PID of the thread it is attached to and, while it is armed, its node is queued in that thread's posix_cputimers. The task is looked up by that PID.

When a non-leader thread exec()s, de_thread() changes which task owns that PID. pid_task(timer->it.cpu.pid, PIDTYPE_PID) then returns NULL, but the node is still queued on tsk, which is alive. timer_lock_sighand() takes a failed lookup to mean that the node is already dequeued, so it has nothing to undo.

begin_new_exec() calls posix_cpu_timers_exit(me) right after exec_task_namespaces() and that removes the leftover node, so the state normally stays invisible. But bprm->point_of_no_return is set before de_thread(), so if unshare_files(), set_mm_exe_file(), exec_mmap() or exec_task_namespaces() fails, the task dies before it gets there. exit_itimers() then frees the k_itimer while its node is still queued, and reaping tsk later erases that freed node from the rbtree.

In short:

the non-leader thread B the parent

timer_create(CLOCK_THREAD_CPUTIME_ID)
timer_settime()
arm_timer() // the node is queued on B
execve()
de_thread(B)
exchange_tids(B, leader) // B's PID now belongs to the leader release_task(leader)
__exit_signal(leader)
posix_cpu_timers_exit(leader) // cleans leader's queue, not B's __unhash_process(leader) // that PID has no task anymore
exec_mmap()
mmap_read_lock_killable(old_mm)
kill(B, SIGKILL)
// -EINTR
get_signal()
do_exit()
exit_itimers()
posix_timer_delete()
posix_cpu_timer_del()
posix_timer_unhash_and_free() // freed while still queued
wait4()
release_task(B)
posix_cpu_timers_exit(B)
cleanup_timerqueue()
timerqueue_del() // use-after-free

Move the POSIX timer cleanup right after de_thread() before any of the later failure conditions brings the task into do_exit().

[ tglx: Move the cleanup right after de_thread() ]

---
 fs/exec.c |   29 +++++++++++++++++++++--------
 1 file changed, 21 insertions(+), 8 deletions(-)
fs/exec.c +21 -8
@@ -1104,6 +1104,17 @@ void __set_task_comm(struct task_struct
 	perf_event_comm(tsk, exec);
 }
 
+static void posixtimer_exec(struct task_struct *me)
+{
+#ifdef CONFIG_POSIX_TIMERS
+	spin_lock_irq(&me->sighand->siglock);
+	posix_cpu_timers_exit(me);
+	spin_unlock_irq(&me->sighand->siglock);
+	exit_itimers(me);
+	flush_itimer_signals();
+#endif
+}
+
 /*
  * Calling this is the point of no return. None of the failures will be
  * seen by userspace since either the process is already taking a fatal
@@ -1137,6 +1148,16 @@ int begin_new_exec(struct linux_binprm *
 	retval = de_thread(me);
 	if (retval)
 		goto out;
+
+	/*
+	 * This must be done here to ensure that POSIX CPU timers which were
+	 * armed on the current task are dequeued from me::posix_cputimers.
+	 * Otherwise in case of a TID switch the deletion of the related POSIX
+	 * timer would not remove an enqueued timer because the TID lookup
+	 * of the old TID fails.
+	 */
+	posixtimer_exec(me);
+
 	/* see the comment in check_unsafe_exec() */
 	current->fs->in_exec = 0;
 	/*
@@ -1191,14 +1212,6 @@ int begin_new_exec(struct linux_binprm *
 	if (retval)
 		goto out_unlock;
 
-#ifdef CONFIG_POSIX_TIMERS
-	spin_lock_irq(&me->sighand->siglock);
-	posix_cpu_timers_exit(me);
-	spin_unlock_irq(&me->sighand->siglock);
-	exit_itimers(me);
-	flush_itimer_signals();
-#endif
-
 	/*
 	 * Make the signal table private.
 	 */
author maintainer
#249 lore raw

PATCH 246/438 fs/dax: check zero or empty entry before converting xarray entry

In reply to Greg Kroah-Hartman

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

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

From: Seunguk Shin <[email protected]>

commit 8e2b8614039853e68d5338e37821e8bcee9fc05f upstream.

Calling dax_to_folio() with empty entry causes kernel panic below when booting a VM with DAX enabled storage.

This patch checks empty entry before calling dax_to_folio() on dax_associate_entry(), dax_disassociate_entry(), and dax_busy_page().

Commit 98c183a4fccf ("fs/dax: don't disassociate zero page entries") added guards in the associate and disassociate paths, but the guards still come after dax_to_folio(), and dax_busy_page() still has the same problem.

[    0.737679] EXT4-fs (pmem0p1): mounted filesystem 79676804-7c8b-491a-b2a6-9bae3c72af70 ro with ordered data mode. Quota mode: disabled.
[    0.737891] VFS: Mounted root (ext4 filesystem) readonly on device 259:1.
[    0.739119] devtmpfs: mounted
[    0.739476] Freeing unused kernel memory: 1920K
[    0.740156] Run /sbin/init as init process
[    0.740229]   with arguments:
[    0.740286]     /sbin/init
[    0.740321]   with environment:
[    0.740369]     HOME=/
[    0.740400]     TERM=linux
[    0.743162] Unable to handle kernel paging request at virtual address fffffdffbf000008
[    0.743285] Mem abort info:
[    0.743316]   ESR = 0x0000000096000006
[    0.743371]   EC = 0x25: DABT (current EL), IL = 32 bits
[    0.743444]   SET = 0, FnV = 0
[    0.743489]   EA = 0, S1PTW = 0
[    0.743545]   FSC = 0x06: level 2 translation fault
[    0.743610] Data abort info:
[    0.743656]   ISV = 0, ISS = 0x00000006, ISS2 = 0x00000000
[    0.743720]   CM = 0, WnR = 0, TnD = 0, TagAccess = 0
[    0.743785]   GCS = 0, Overlay = 0, DirtyBit = 0, Xs = 0
[    0.743848] swapper pgtable: 4k pages, 48-bit VAs, pgdp=00000000b9d17000
[    0.743931] [fffffdffbf000008] pgd=10000000bfa3d403, p4d=10000000bfa3d403, pud=1000000040bfe403, pmd=0000000000000000
[    0.744070] Internal error: Oops: 0000000096000006 [#1]  SMP
[    0.748888] CPU: 0 UID: 0 PID: 1 Comm: init Not tainted 6.18.4 #1 NONE
[    0.749421] pstate: 004000c5 (nzcv daIF +PAN -UAO -TCO -DIT -SSBS BTYPE=--)
[    0.749969] pc : dax_disassociate_entry.constprop.0+0x20/0x50
[    0.750444] lr : dax_insert_entry+0xcc/0x408
[    0.750802] sp : ffff80008000b9e0
[    0.751083] x29: ffff80008000b9e0 x28: 0000000000000000 x27: 0000000000000000
[    0.751682] x26: 0000000001963d01 x25: ffff0000004f7d90 x24: 0000000000000000
[    0.752264] x23: 0000000000000000 x22: ffff80008000bcc8 x21: 0000000000000011
[    0.752836] x20: ffff80008000ba90 x19: 0000000001963d01 x18: 0000000000000000
[    0.753407] x17: 0000000000000000 x16: 0000000000000000 x15: 0000000000000000
[    0.753970] x14: ffffbf3154b9ae70 x13: 0000000000000000 x12: ffffbf3154b9ae70
[    0.754548] x11: ffffffffffffffff x10: 0000000000000000 x9 : 0000000000000000
[    0.755122] x8 : 000000000000000d x7 : 000000000000001f x6 : 0000000000000000
[    0.755707] x5 : 0000000000000000 x4 : 0000000000000000 x3 : fffffdffc0000000
[    0.756287] x2 : 0000000000000008 x1 : 0000000040000000 x0 : fffffdffbf000000
[    0.756871] Call trace:
[    0.757107]  dax_disassociate_entry.constprop.0+0x20/0x50 (P)
[    0.757592]  dax_iomap_pte_fault+0x4fc/0x808
[    0.757951]  dax_iomap_fault+0x28/0x30
[    0.758258]  ext4_dax_huge_fault+0x80/0x2dc
[    0.758594]  ext4_dax_fault+0x10/0x3c
[    0.758892]  __do_fault+0x38/0x12c
[    0.759175]  __handle_mm_fault+0x530/0xcf0
[    0.759518]  handle_mm_fault+0xe4/0x230
[    0.759833]  do_page_fault+0x17c/0x4dc
[    0.760144]  do_translation_fault+0x30/0x38
[    0.760483]  do_mem_abort+0x40/0x8c
[    0.760771]  el0_ia+0x4c/0x170
[    0.761032]  el0t_64_sync_handler+0xd8/0xdc
[    0.761371]  el0t_64_sync+0x168/0x16c
[    0.761677] Code: f9453021 f2dfbfe3 cb813080 8b001860 (f9400401)
[    0.762168] ---[ end trace 0000000000000000 ]---
[    0.762550] note: init[1] exited with irqs disabled
[    0.762631] Kernel panic - not syncing: Attempted to kill init! exitcode=0x0000000b
---
 fs/dax.c |    9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)
fs/dax.c +6 -3
@@ -480,11 +480,12 @@ static void dax_associate_entry(void *en
 				unsigned long address, bool shared)
 {
 	unsigned long size = dax_entry_size(entry), index;
-	struct folio *folio = dax_to_folio(entry);
+	struct folio *folio;
 
 	if (dax_is_zero_entry(entry) || dax_is_empty_entry(entry))
 		return;
 
+	folio = dax_to_folio(entry);
 	index = linear_page_index(vma, address & ~(size - 1));
 	if (shared && (folio->mapping || dax_folio_is_shared(folio))) {
 		if (folio->mapping)
@@ -505,21 +506,23 @@ static void dax_associate_entry(void *en
 static void dax_disassociate_entry(void *entry, struct address_space *mapping,
 				bool trunc)
 {
-	struct folio *folio = dax_to_folio(entry);
+	struct folio *folio;
 
 	if (dax_is_zero_entry(entry) || dax_is_empty_entry(entry))
 		return;
 
+	folio = dax_to_folio(entry);
 	dax_folio_put(folio);
 }
 
 static struct page *dax_busy_page(void *entry)
 {
-	struct folio *folio = dax_to_folio(entry);
+	struct folio *folio;
 
 	if (dax_is_zero_entry(entry) || dax_is_empty_entry(entry))
 		return NULL;
 
+	folio = dax_to_folio(entry);
 	if (folio_ref_count(folio) - folio_mapcount(folio))
 		return &folio->page;
 	else
author maintainer
#250 lore raw

PATCH 247/438 PCI: imx6: Move clock enable after core reset assertion

In reply to Greg Kroah-Hartman

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

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

From: Richard Zhu <[email protected]>

commit c9dc7d730319ad64b51570c5387f1fee7b07b510 upstream.

Commit 610fa91d9863 ("PCI: imx6: Assert PERST# before enabling regulators") inadvertently moved clock enablement before core reset assertion, breaking PCI device initialization on i.MX6Q Apalis platforms with ASM1061/ASM1062 SATA controllers connected:

imx6q-pcie 1ffc000.pcie: host bridge /soc/pcie@1ffc000 ranges:
imx6q-pcie 1ffc000.pcie: IO 0x0001f80000..0x0001f8ffff -> 0x0000000000
imx6q-pcie 1ffc000.pcie: MEM 0x0001000000..0x0001efffff -> 0x0001000000
imx6q-pcie 1ffc000.pcie: config reg[1] 0x01f00000 == cpu 0x01f00000 imx6q-pcie 1ffc000.pcie: iATU: unroll F, 4 ob, 4 ib, align 64K, limit 4G imx6q-pcie 1ffc000.pcie: Link: Only Gen1 is enabled
imx6q-pcie 1ffc000.pcie: Link failed to come up. LTSSM: POLL_CONFIG imx6q-pcie 1ffc000.pcie: probe with driver imx6q-pcie failed with error -110

NOTE: It is not 100% clear if the issue is specific to the ASM1061/ASM1062 device or on the specific power-up sequence (reset vs cold-power-on).

To fix this regression, restore the original sequence where clocks are enabled after asserting core reset and configuring the controller type.

---
 drivers/pci/controller/dwc/pci-imx6.c |   12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)
drivers/pci/controller/dwc/pci-imx6.c +6 -6
@@ -1388,12 +1388,6 @@ static int imx_pcie_host_init(struct dw_
 		goto err_pwrctrl_destroy;
 	}
 
-	ret = imx_pcie_clk_enable(imx_pcie);
-	if (ret) {
-		dev_err(dev, "unable to enable pcie clocks: %d\n", ret);
-		goto err_pwrctrl_power_off;
-	}
-
 	if (pp->bridge && imx_check_flag(imx_pcie, IMX_PCIE_FLAG_HAS_LUT)) {
 		pp->bridge->enable_device = imx_pcie_enable_device;
 		pp->bridge->disable_device = imx_pcie_disable_device;
@@ -1409,6 +1403,12 @@ static int imx_pcie_host_init(struct dw_
 
 	imx_pcie_configure_type(imx_pcie);
 
+	ret = imx_pcie_clk_enable(imx_pcie);
+	if (ret) {
+		dev_err(dev, "unable to enable pcie clocks: %d\n", ret);
+		goto err_pwrctrl_power_off;
+	}
+
 	if (imx_pcie->phy) {
 		ret = phy_init(imx_pcie->phy);
 		if (ret) {
author maintainer
#251 lore raw

PATCH 248/438 phy: renesas: rcar-gen3-usb2: Avoid long delay in atomic context

In reply to Greg Kroah-Hartman

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

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

From: Claudiu Beznea <[email protected]>

commit 48e97c59a49c9e90270f5e3d221db253b76de5da upstream.

The OTG PHY initialization sequence needs to wait for 20 ms at a specific step, as described in commit 72c0339c115b ("phy: renesas:
rcar-gen3-usb2: follow the hardware manual procedure").

Commit 55a387ebb921 ("phy: renesas: rcar-gen3-usb2: Lock around hardware registers and driver data") tried to address various problems in the rcar-gen3-usb2 driver and converted the mutex protecting HW register accesses to a spin lock, leaving, however, a long delay in the critical section protected by the spin lock. This may become a problem, especially on RT kernels.

To address this, release the spin lock before sleeping for 20 ms as required by the HW manual and reacquire it afterwards. To avoid other threads entering the critical section and configuring the HW while the software is waiting for the OTG initialization to complete, introduce the otg_initializing variable alongside the otg_init_done wait queue. Any other thread trying to configure the HW while the OTG PHY initialization is in progress waits for the wait queue instead of immediately returning errors to PHY users. The IRQs were also disabled while waiting for the OTG PHY initialization to complete, as the interrupt handler may also apply HW settings.

The OTG can only be initialized once. It is initialized by the first PHY that calls struct phy_ops::rcar_gen3_phy_usb2_init().

To avoid failures when multiple PHYs call struct phy_ops::rcar_gen3_phy_usb2_init() simultaneously, and the PHY responsible for initializing the OTG either fails or deinit quiqly and another PHY takes over the PHY init role), the code waiting for the channel->otg_init_done wait queue retries up to NUM_OF_PHYS times.

---
 drivers/phy/renesas/phy-rcar-gen3-usb2.c |  310 ++++++++++++++++++++++++++-----
 1 file changed, 265 insertions(+), 45 deletions(-)
drivers/phy/renesas/phy-rcar-gen3-usb2.c +265 -45
@@ -27,6 +27,7 @@
 #include <linux/reset.h>
 #include <linux/string.h>
 #include <linux/usb/of.h>
+#include <linux/wait.h>
 #include <linux/workqueue.h>
 
 /******* USB2.0 Host registers (original offset is +0x200) *******/
@@ -106,6 +107,13 @@
 /*  RZ/G2L specific */
 #define USB2_LINECTRL1_USB2_IDMON	BIT(0)
 
+/*
+ * The OTG initialization is expected to finish in 20ms. Choose a large enough
+ * timeout to avoid waiters exit prematurely the waiting section under heavy
+ * CPU load.
+ */
+#define USB2_OTG_INIT_TIMEOUT		msecs_to_jiffies(120)
+
 #define NUM_OF_PHYS			4
 enum rcar_gen3_phy_index {
 	PHY_INDEX_BOTH_HC,
@@ -138,12 +146,20 @@ struct rcar_gen3_chan {
 	struct rcar_gen3_phy rphys[NUM_OF_PHYS];
 	struct regulator *vbus;
 	struct work_struct work;
+	wait_queue_head_t otg_init_done;
 	spinlock_t lock;	/* protects access to hardware and driver data structure. */
 	enum usb_dr_mode dr_mode;
 	bool extcon_host;
 	bool is_otg_channel;
 	bool uses_otg_pins;
 	bool otg_internal_reg;
+	/*
+	 * The OTG can be initialized only once and needs to release the spinlock
+	 * and wait for 20 ms due to hardware constraints. If a thread executes
+	 * PHY configuration code while the OTG PHY is waiting for the 20 ms, the
+	 * thread will have to wait for the OTG PHY initialization to complete.
+	 */
+	bool otg_initializing;
 };
 
 struct rcar_gen3_phy_drv_data {
@@ -392,26 +408,58 @@ static ssize_t role_store(struct device
 	struct rcar_gen3_chan *ch = dev_get_drvdata(dev);
 	bool is_b_device;
 	enum phy_mode cur_mode, new_mode;
+	int retries = NUM_OF_PHYS;
+	unsigned long flags;
+	int ret = -EIO;
 
-	guard(spinlock_irqsave)(&ch->lock);
+	spin_lock_irqsave(&ch->lock, flags);
 
-	if (!ch->is_otg_channel || !rcar_gen3_is_any_otg_rphy_initialized(ch))
-		return -EIO;
+	if (!ch->is_otg_channel)
+		goto unlock;
+
+	while (retries-- && ch->otg_initializing) {
+		spin_unlock_irqrestore(&ch->lock, flags);
+
+		ret = wait_event_timeout(ch->otg_init_done, !ch->otg_initializing,
+					 USB2_OTG_INIT_TIMEOUT);
+		ret = ret ? 0 : -ETIMEDOUT;
+		if (ret && !retries)
+			goto exit;
+
+		spin_lock_irqsave(&ch->lock, flags);
+	}
+
+	/* If another thread started a new initialization just return -EBUSY. */
+	if (ch->otg_initializing) {
+		ret = -EBUSY;
+		goto unlock;
+	} else {
+		ret = 0;
+	}
+
+	if (!rcar_gen3_is_any_otg_rphy_initialized(ch)) {
+		ret = -EIO;
+		goto unlock;
+	}
 
-	if (sysfs_streq(buf, "host"))
+	if (sysfs_streq(buf, "host")) {
 		new_mode = PHY_MODE_USB_HOST;
-	else if (sysfs_streq(buf, "peripheral"))
+	} else if (sysfs_streq(buf, "peripheral")) {
 		new_mode = PHY_MODE_USB_DEVICE;
-	else
-		return -EINVAL;
+	} else {
+		ret = -EINVAL;
+		goto unlock;
+	}
 
 	/* is_b_device: true is B-Device. false is A-Device. */
 	is_b_device = rcar_gen3_check_id(ch);
 	cur_mode = rcar_gen3_get_phy_mode(ch);
 
 	/* If current and new mode is the same, this returns the error */
-	if (cur_mode == new_mode)
-		return -EINVAL;
+	if (cur_mode == new_mode) {
+		ret = -EINVAL;
+		goto unlock;
+	}
 
 	if (new_mode == PHY_MODE_USB_HOST) { /* And is_host must be false */
 		if (!is_b_device)	/* A-Peripheral */
@@ -425,7 +473,10 @@ static ssize_t role_store(struct device
 			rcar_gen3_init_for_peri(ch);
 	}
 
-	return count;
+unlock:
+	spin_unlock_irqrestore(&ch->lock, flags);
+exit:
+	return ret ?: count;
 }
 
 static ssize_t role_show(struct device *dev, struct device_attribute *attr,
@@ -441,14 +492,11 @@ static ssize_t role_show(struct device *
 }
 static DEVICE_ATTR_RW(role);
 
-static void rcar_gen3_init_otg(struct rcar_gen3_chan *ch)
+static void rcar_gen3_init_otg_phase0(struct rcar_gen3_chan *ch)
 {
 	void __iomem *usb2_base = ch->base;
 	u32 val;
 
-	if (!ch->is_otg_channel || rcar_gen3_is_any_otg_rphy_initialized(ch))
-		return;
-
 	/* Should not use functions of read-modify-write a register */
 	val = readl(usb2_base + USB2_LINECTRL1);
 	val = (val & ~USB2_LINECTRL1_DP_RPD) | USB2_LINECTRL1_DPRPD_EN |
@@ -471,7 +519,11 @@ static void rcar_gen3_init_otg(struct rc
 			writel(val | USB2_ADPCTRL_IDPULLUP, usb2_base + USB2_ADPCTRL);
 		}
 	}
-	mdelay(20);
+}
+
+static void rcar_gen3_init_otg_phase1(struct rcar_gen3_chan *ch)
+{
+	void __iomem *usb2_base = ch->base;
 
 	writel(0xffffffff, usb2_base + USB2_OBINTSTA);
 	writel(ch->phy_data->obint_enable_bits, usb2_base + USB2_OBINTEN);
@@ -502,6 +554,7 @@ static irqreturn_t rcar_gen3_phy_usb2_ir
 	void __iomem *usb2_base = ch->base;
 	struct device *dev = ch->dev;
 	irqreturn_t ret = IRQ_NONE;
+	unsigned long flags;
 	u32 status;
 
 	pm_runtime_get_noresume(dev);
@@ -509,33 +562,102 @@ static irqreturn_t rcar_gen3_phy_usb2_ir
 	if (pm_runtime_suspended(dev))
 		goto rpm_put;
 
-	scoped_guard(spinlock, &ch->lock) {
-		status = readl(usb2_base + USB2_OBINTSTA);
-		if (status & ch->phy_data->obint_enable_bits) {
-			dev_vdbg(dev, "%s: %08x\n", __func__, status);
-			if (ch->phy_data->vblvl_ctrl)
-				writel(USB2_OBINTSTA_CLEAR, usb2_base + USB2_OBINTSTA);
-			else
-				writel(ch->phy_data->obint_enable_bits, usb2_base + USB2_OBINTSTA);
-			rcar_gen3_device_recognition(ch);
-			rcar_gen3_configure_vblvl_ctrl(ch);
-			ret = IRQ_HANDLED;
-		}
+	spin_lock_irqsave(&ch->lock, flags);
+
+	status = readl(usb2_base + USB2_OBINTSTA);
+	if (status & ch->phy_data->obint_enable_bits) {
+		dev_vdbg(dev, "%s: %08x\n", __func__, status);
+		if (ch->phy_data->vblvl_ctrl)
+			writel(USB2_OBINTSTA_CLEAR, usb2_base + USB2_OBINTSTA);
+		else
+			writel(ch->phy_data->obint_enable_bits, usb2_base + USB2_OBINTSTA);
+
+		ret = IRQ_HANDLED;
+
+		/* This should not happen! */
+		if (ch->otg_initializing)
+			goto unlock;
+
+		rcar_gen3_device_recognition(ch);
+		rcar_gen3_configure_vblvl_ctrl(ch);
 	}
 
+unlock:
+	spin_unlock_irqrestore(&ch->lock, flags);
 rpm_put:
 	pm_runtime_put_noidle(dev);
 	return ret;
 }
 
+static void rcar_gen3_phy_usb2_irqs_mask_all(struct rcar_gen3_chan *channel,
+					     u32 *masked_irqs_bits)
+{
+	u32 val, bitmask = USB2_INT_ENABLE_UCOM_INTEN;
+	void __iomem *usb2_base = channel->base;
+
+	for (unsigned int i = 0; i < NUM_OF_PHYS; i++)
+		bitmask |= channel->rphys[i].int_enable_bits;
+
+	val = readl(usb2_base + USB2_INT_ENABLE);
+	*masked_irqs_bits = val & bitmask;
+	val &= ~bitmask;
+	writel(val, usb2_base + USB2_INT_ENABLE);
+
+	/*
+	 * Don't report channel->phy_data->obint_enable_bits IRQs. These are
+	 * unmasked anyway in rcar_gen3_init_otg_phase1().
+	 */
+	val = readl(usb2_base + USB2_OBINTEN);
+	val &= ~channel->phy_data->obint_enable_bits;
+	writel(val, usb2_base + USB2_OBINTEN);
+}
+
+static void rcar_gen3_phy_usb2_irqs_unmask(struct rcar_gen3_chan *channel,
+					   u32 irqs_bits)
+{
+	u32 val, bitmask = USB2_INT_ENABLE_UCOM_INTEN;
+	void __iomem *usb2_base = channel->base;
+
+	for (unsigned int i = 0; i < NUM_OF_PHYS; i++)
+		bitmask |= channel->rphys[i].int_enable_bits;
+
+	val = readl(usb2_base + USB2_INT_ENABLE);
+	val &= ~bitmask;
+	val |= irqs_bits;
+	writel(val, usb2_base + USB2_INT_ENABLE);
+}
+
 static int rcar_gen3_phy_usb2_init(struct phy *p)
 {
 	struct rcar_gen3_phy *rphy = phy_get_drvdata(p);
 	struct rcar_gen3_chan *channel = rphy->ch;
 	void __iomem *usb2_base = channel->base;
+	int retries = NUM_OF_PHYS;
+	unsigned long flags;
 	u32 val;
+	int ret;
+
+	spin_lock_irqsave(&channel->lock, flags);
 
-	guard(spinlock_irqsave)(&channel->lock);
+	while (retries-- && channel->otg_initializing) {
+		spin_unlock_irqrestore(&channel->lock, flags);
+
+		ret = wait_event_timeout(channel->otg_init_done, !channel->otg_initializing,
+					 USB2_OTG_INIT_TIMEOUT);
+		ret = ret ? 0 : -ETIMEDOUT;
+		if (ret && !retries)
+			return ret;
+
+		spin_lock_irqsave(&channel->lock, flags);
+	}
+
+	/* If another thread started a new initialization just return -EBUSY. */
+	if (channel->otg_initializing) {
+		ret = -EBUSY;
+		goto unlock;
+	} else {
+		ret = 0;
+	}
 
 	/* Initialize USB2 part */
 	val = readl(usb2_base + USB2_INT_ENABLE);
@@ -548,8 +670,23 @@ static int rcar_gen3_phy_usb2_init(struc
 	}
 
 	/* Initialize otg part (only if we initialize a PHY with IRQs). */
-	if (rphy->int_enable_bits)
-		rcar_gen3_init_otg(channel);
+	if (rphy->int_enable_bits && channel->is_otg_channel &&
+	    !rcar_gen3_is_any_otg_rphy_initialized(channel)) {
+		u32 masked_irq_bits = 0;
+
+		rcar_gen3_init_otg_phase0(channel);
+		rcar_gen3_phy_usb2_irqs_mask_all(channel, &masked_irq_bits);
+		channel->otg_initializing = true;
+		spin_unlock_irqrestore(&channel->lock, flags);
+
+		fsleep(20000);
+
+		spin_lock_irqsave(&channel->lock, flags);
+		rcar_gen3_phy_usb2_irqs_unmask(channel, masked_irq_bits);
+		rcar_gen3_init_otg_phase1(channel);
+		channel->otg_initializing = false;
+		wake_up_all(&channel->otg_init_done);
+	}
 
 	if (channel->phy_data->vblvl_ctrl) {
 		/* SIDDQ mode release */
@@ -568,7 +705,10 @@ static int rcar_gen3_phy_usb2_init(struc
 
 	rphy->initialized = true;
 
-	return 0;
+unlock:
+	spin_unlock_irqrestore(&channel->lock, flags);
+
+	return ret;
 }
 
 static int rcar_gen3_phy_usb2_exit(struct phy *p)
@@ -576,9 +716,32 @@ static int rcar_gen3_phy_usb2_exit(struc
 	struct rcar_gen3_phy *rphy = phy_get_drvdata(p);
 	struct rcar_gen3_chan *channel = rphy->ch;
 	void __iomem *usb2_base = channel->base;
+	int retries = NUM_OF_PHYS;
+	unsigned long flags;
 	u32 val;
+	int ret;
+
+	spin_lock_irqsave(&channel->lock, flags);
+
+	while (retries-- && channel->otg_initializing) {
+		spin_unlock_irqrestore(&channel->lock, flags);
+
+		ret = wait_event_timeout(channel->otg_init_done, !channel->otg_initializing,
+					 USB2_OTG_INIT_TIMEOUT);
+		ret = ret ? 0 : -ETIMEDOUT;
+		if (ret && !retries)
+			return ret;
+
+		spin_lock_irqsave(&channel->lock, flags);
+	}
 
-	guard(spinlock_irqsave)(&channel->lock);
+	/* If another thread started a new initialization just return -EBUSY. */
+	if (channel->otg_initializing) {
+		ret = -EBUSY;
+		goto unlock;
+	} else {
+		ret = 0;
+	}
 
 	rphy->initialized = false;
 
@@ -588,7 +751,9 @@ static int rcar_gen3_phy_usb2_exit(struc
 		val &= ~USB2_INT_ENABLE_UCOM_INTEN;
 	writel(val, usb2_base + USB2_INT_ENABLE);
 
-	return 0;
+unlock:
+	spin_unlock_irqrestore(&channel->lock, flags);
+	return ret;
 }
 
 static int rcar_gen3_phy_usb2_power_on(struct phy *p)
@@ -596,8 +761,10 @@ static int rcar_gen3_phy_usb2_power_on(s
 	struct rcar_gen3_phy *rphy = phy_get_drvdata(p);
 	struct rcar_gen3_chan *channel = rphy->ch;
 	void __iomem *usb2_base = channel->base;
+	int retries = NUM_OF_PHYS;
+	unsigned long flags;
 	u32 val;
-	int ret = 0;
+	int ret;
 
 	if (channel->vbus && !channel->otg_internal_reg) {
 		ret = regulator_enable(channel->vbus);
@@ -605,7 +772,27 @@ static int rcar_gen3_phy_usb2_power_on(s
 			return ret;
 	}
 
-	guard(spinlock_irqsave)(&channel->lock);
+	spin_lock_irqsave(&channel->lock, flags);
+
+	while (retries-- && channel->otg_initializing) {
+		spin_unlock_irqrestore(&channel->lock, flags);
+
+		ret = wait_event_timeout(channel->otg_init_done, !channel->otg_initializing,
+					 USB2_OTG_INIT_TIMEOUT);
+		ret = ret ? 0 : -ETIMEDOUT;
+		if (ret && !retries)
+			goto disable_regulator;
+
+		spin_lock_irqsave(&channel->lock, flags);
+	}
+
+	/* If another thread started a new initialization just return -EBUSY. */
+	if (channel->otg_initializing) {
+		ret = -EBUSY;
+		goto unlock;
+	} else {
+		ret = 0;
+	}
 
 	if (!rcar_gen3_are_all_rphys_power_off(channel))
 		goto out;
@@ -620,27 +807,59 @@ out:
 	/* The powered flag should be set for any other phys anyway */
 	rphy->powered = true;
 
-	return 0;
+unlock:
+	spin_unlock_irqrestore(&channel->lock, flags);
+
+disable_regulator:
+	if (ret && channel->vbus && !channel->otg_internal_reg)
+		regulator_disable(channel->vbus);
+
+	return ret;
 }
 
 static int rcar_gen3_phy_usb2_power_off(struct phy *p)
 {
 	struct rcar_gen3_phy *rphy = phy_get_drvdata(p);
 	struct rcar_gen3_chan *channel = rphy->ch;
-	int ret = 0;
+	int retries = NUM_OF_PHYS;
+	unsigned long flags;
+	int ret;
 
-	scoped_guard(spinlock_irqsave, &channel->lock) {
-		rphy->powered = false;
+	spin_lock_irqsave(&channel->lock, flags);
 
-		if (rcar_gen3_are_all_rphys_power_off(channel)) {
-			u32 val = readl(channel->base + USB2_USBCTR);
+	while (retries-- && channel->otg_initializing) {
+		spin_unlock_irqrestore(&channel->lock, flags);
 
-			val |= USB2_USBCTR_PLL_RST;
-			writel(val, channel->base + USB2_USBCTR);
-		}
+		ret = wait_event_timeout(channel->otg_init_done, !channel->otg_initializing,
+					 USB2_OTG_INIT_TIMEOUT);
+		ret = ret ? 0 : -ETIMEDOUT;
+		if (ret && !retries)
+			return ret;
+
+		spin_lock_irqsave(&channel->lock, flags);
+	}
+
+	/* If another thread started a new initialization just return -EBUSY. */
+	if (channel->otg_initializing) {
+		ret = -EBUSY;
+		goto unlock;
+	} else {
+		ret = 0;
+	}
+
+	rphy->powered = false;
+
+	if (rcar_gen3_are_all_rphys_power_off(channel)) {
+		u32 val = readl(channel->base + USB2_USBCTR);
+
+		val |= USB2_USBCTR_PLL_RST;
+		writel(val, channel->base + USB2_USBCTR);
 	}
 
-	if (channel->vbus && !channel->otg_internal_reg)
+unlock:
+	spin_unlock_irqrestore(&channel->lock, flags);
+
+	if (!ret && channel->vbus && !channel->otg_internal_reg)
 		ret = regulator_disable(channel->vbus);
 
 	return ret;
@@ -1018,6 +1237,7 @@ static int rcar_gen3_phy_usb2_probe(stru
 		return ret;
 
 	spin_lock_init(&channel->lock);
+	init_waitqueue_head(&channel->otg_init_done);
 	for (i = 0; i < NUM_OF_PHYS; i++) {
 		channel->rphys[i].phy = devm_phy_create(dev, NULL,
 							channel->phy_data->phy_usb2_ops);