Skip to contentSkip to conversation

6.18.51-rc1 review

PATCH In review Greg Kroah-Hartman 11 participants 598 messages Sep 9 – Sep 12 Page 3 of 5

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

PATCH 277/583 media: cec: disable delayed work before freeing an interrupted transmit

In reply to Greg Kroah-Hartman

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

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

From: Biren Pandya <[email protected]>

commit 0fbd5c2327020858c45b2d1c65775d64cdeca523 upstream.

cec_transmit_msg_fh() drops adap->lock to wait for a blocking transmit in wait_for_completion_killable(). If that wait is interrupted by a signal, cancel_delayed_work_sync() can run before the CEC kthread arms the reply timeout via schedule_delayed_work(&data->work) in cec_transmit_done_ts(). The work is then armed after the cancel, and the data is freed with its delayed_work still pending:

ODEBUG: free active (active state 0) object: ... hint: cec_wait_timeout

Use disable_delayed_work_sync(): it cancels the work and disables it, so the later schedule_delayed_work() becomes a no-op and the work cannot be re-armed. The data is freed right after, so it need not be re-enabled.

---
 drivers/media/cec/core/cec-adap.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/media/cec/core/cec-adap.c +1 -1
@@ -965,7 +965,7 @@ int cec_transmit_msg_fh(struct cec_adapt
 	 */
 	mutex_unlock(&adap->lock);
 	err = wait_for_completion_killable(&data->c);
-	cancel_delayed_work_sync(&data->work);
+	disable_delayed_work_sync(&data->work);
 	mutex_lock(&adap->lock);
 
 	if (err)
author maintainer
#278 lore raw

PATCH 278/583 media: cec: extron-da-hd-4k-plus: add sanity check

In reply to Greg Kroah-Hartman

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

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

From: Hans Verkuil <[email protected]>

commit abac9820b26b5cfcb01eb79efe2abdd0ac7e07c3 upstream.

Add check to prevent overflowing msg.msg[] in case the incoming data is malformed.

Fixes 056f2821b631 ("media: cec: extron-da-hd-4k-plus: add the Extron DA HD 4K Plus CEC driver")
---
 drivers/media/cec/usb/extron-da-hd-4k-plus/extron-da-hd-4k-plus.c |    3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/media/cec/usb/extron-da-hd-4k-plus/extron-da-hd-4k-plus.c +2 -1
@@ -656,7 +656,8 @@ static void extron_process_received(stru
 	if (!port || port->disconnected)
 		return;
 
-	if (len < 5 || (len - 2) % 3 || data[len - 2] != '*')
+	if (len < 5 || ((len - 2) / 3 > sizeof(msg.msg)) ||
+	    (len - 2) % 3 || data[len - 2] != '*')
 		goto malformed;
 
 	while (*data != '*') {
author maintainer
#279 lore raw

PATCH 279/583 media: cec: meson: ao-cec-g12a: name the CEC core regmap to avoid debugfs clash

In reply to Greg Kroah-Hartman

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

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

From: Yi Ding <[email protected]>

commit 172c5a7d81deb922ddedd1bc920751c7fed6c43c upstream.

The driver registers two regmaps on the same platform device: an MMIO regmap for the AO CEC registers, and an indirect regmap (using reg_read()/reg_write() callbacks) for the CEC controller core registers. Neither regmap_config sets a .name, so both default their debugfs directory to the device name and collide:

debugfs: 'ff800280.cec' already exists in 'regmap'

Because of the clash the second regmap's debugfs directory fails to register, so its registers can no longer be inspected via debugfs.

Give the indirect CEC core regmap a distinct name. The two debugfs directories then become "<dev>.cec" and "<dev>.cec-core". This only affects debugfs naming; register access is unchanged.

Tested on an ODROID-N2 (Amlogic S922X): the warning is gone and both /sys/kernel/debug/regmap/ff800280.cec and ff800280.cec-core are present.

Fixes b7778c46683c ("media: platform: meson: Add Amlogic Meson G12A AO CEC Controller driver")
Assisted-by Claude:claude-opus-4-8
---
 drivers/media/cec/platform/meson/ao-cec-g12a.c |    1 +
 1 file changed, 1 insertion(+)
drivers/media/cec/platform/meson/ao-cec-g12a.c +1 -0
@@ -405,6 +405,7 @@ static int meson_ao_cec_g12a_write(void
 }
 
 static const struct regmap_config meson_ao_cec_g12a_cec_regmap_conf = {
+	.name = "core",
 	.reg_bits = 8,
 	.val_bits = 8,
 	.reg_read = meson_ao_cec_g12a_read,
author maintainer
#280 lore raw

PATCH 280/583 media: cec: Serialize exclusive follower delivery

In reply to Greg Kroah-Hartman

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

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

From: Ruoyu Wang <[email protected]>

commit 1924d0788caa6c66fd320dd4704fae99487fd2c7 upstream.

cec_receive_notify() reads the exclusive follower pointer without the adapter lock. Serialize the no-follower check and message delivery against mode changes and release.

Fixes 9881fe0ca187 ("[media] cec: add HDMI CEC framework (adapter)")
---
 drivers/media/cec/core/cec-adap.c |    8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)
drivers/media/cec/core/cec-adap.c +7 -1
@@ -2210,9 +2210,13 @@ static int cec_receive_notify(struct cec
 		 * Unprocessed messages are aborted if userspace isn't doing
 		 * any processing either.
 		 */
+		mutex_lock(&adap->lock);
 		if (!is_broadcast && !is_reply && !adap->follower_cnt &&
-		    !adap->cec_follower && msg->msg[1] != CEC_MSG_FEATURE_ABORT)
+		    !adap->cec_follower && msg->msg[1] != CEC_MSG_FEATURE_ABORT) {
+			mutex_unlock(&adap->lock);
 			return cec_feature_abort(adap, msg);
+		}
+		mutex_unlock(&adap->lock);
 		break;
 	}
 
@@ -2225,10 +2229,12 @@ skip_processing:
 	 * Send to the exclusive follower if there is one, otherwise send
 	 * to all followers.
 	 */
+	mutex_lock(&adap->lock);
 	if (adap->cec_follower)
 		cec_queue_msg_fh(adap->cec_follower, msg);
 	else
 		cec_queue_msg_followers(adap, msg);
+	mutex_unlock(&adap->lock);
 	return 0;
 }
 
author maintainer
#281 lore raw

PATCH 281/583 media: cedrus: fix memory leak in cedrus_init_ctrls()

In reply to Greg Kroah-Hartman

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

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

From: Dawei Feng <[email protected]>

commit 9df2fbe563194da1967a5db083442186c1323efe upstream.

In cedrus_init_ctrls(), the V4L2 control handler is initialized before allocating memory for ctx->ctrls. If this allocation fails, the function returns -ENOMEM without freeing the previously allocated handler resources, leading to a memory leak.

Fix this by calling v4l2_ctrl_handler_free() on the ctx->ctrls allocation failure path.

The bug was first flagged by an experimental analysis tool we are developing for kernel memory-management bugs while analyzing v6.13-rc1. The tool is still under development and is not yet publicly available. Manual inspection confirms that the bug is still present in v7.1.1.

An x86_64 allyesconfig build showed no new warnings. As we do not have an Allwinner SoC or board with a Cedrus VPU available to test with, no runtime testing was able to be performed.

---
 drivers/staging/media/sunxi/cedrus/cedrus.c |    4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/staging/media/sunxi/cedrus/cedrus.c +3 -1
@@ -285,8 +285,10 @@ static int cedrus_init_ctrls(struct cedr
 	ctrl_size = sizeof(ctrl) * CEDRUS_CONTROLS_COUNT + 1;
 
 	ctx->ctrls = kzalloc(ctrl_size, GFP_KERNEL);
-	if (!ctx->ctrls)
+	if (!ctx->ctrls) {
+		v4l2_ctrl_handler_free(hdl);
 		return -ENOMEM;
+	}
 
 	j = 0;
 	for (i = 0; i < CEDRUS_CONTROLS_COUNT; i++) {
author maintainer
#282 lore raw

PATCH 282/583 media: cobalt: Avoid freeing ALSA private data twice

In reply to Greg Kroah-Hartman

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

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

From: Ruoyu Wang <[email protected]>

commit 3a7d6b9c4cb5ac18cbd3f1c7f8c7b159c42ba0b1 upstream.

snd_cobalt_card_create() stores cobsc in sc->private_data and installs snd_cobalt_card_private_free() as sc->private_free. From that point, snd_card_free(sc) releases cobsc through the ALSA card cleanup path.

If cobalt_alsa_init() fails after snd_cobalt_card_create(), the err_exit_free path calls snd_card_free(sc) and then kfree(cobsc). That second free releases the same object again.

Remove the explicit kfree(cobsc) and leave ownership with the ALSA card.

This issue was found by a static analysis checker and confirmed by manual source review.

Fixes 85756a069c55 ("[media] cobalt: add new driver")
---
 drivers/media/pci/cobalt/cobalt-alsa-main.c |    1 -
 1 file changed, 1 deletion(-)
drivers/media/pci/cobalt/cobalt-alsa-main.c +0 -1
@@ -135,7 +135,6 @@ int cobalt_alsa_init(struct cobalt_strea
 err_exit_free:
 	if (sc != NULL)
 		snd_card_free(sc);
-	kfree(cobsc);
 err_exit:
 	return ret;
 }
author maintainer
#283 lore raw

PATCH 283/583 media: cx231xx: reject geometry changes while the VBI queue is busy

In reply to Greg Kroah-Hartman

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

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

From: Bryam Vargas <[email protected]>

commit 627a121c15fe05a541f44d86016294b80bada75d upstream.

vidioc_s_fmt_vid_cap() and vidioc_s_std() change the device-wide dev->width / dev->norm but only refuse the change when the *video* queue (dev->vidq) is busy. The VBI queue (dev->vbiq) shares that same geometry:
cx231xx_init_vbi_isoc() latches dma_q->lines_per_field from dev->norm, the VBI videobuf2 plane is sized from dev->width / dev->norm in vbi_queue_setup() and vbi_buf_prepare(), and cx231xx_do_vbi_copy() then recomputes the destination offset from the *live* dev->width and the latched lines_per_field on every URB completion:

	offset = lines_completed * (dev->width << 1) + ...;
	if (dma_q->current_field == 2)
		offset += dev->width * 2 * dma_q->lines_per_field;
	memcpy(plane + offset, p_buffer, lencopy);

Because the VBI node shares video_ioctl_ops with the video node, an application can size a small VBI plane (REQBUFS/QBUF with a small width, or with the NTSC standard), then enlarge dev->width (or switch dev->norm to PAL) through the video node while the VBI stream is running -- the change is allowed because only dev->vidq is checked -- and let the device deliver a field-2 VBI payload. cx231xx_do_vbi_copy() now computes the offset with the larger geometry and memcpy()s past the end of the smaller plane that was already allocated, a heap out-of-bounds write whose offset is attacker-chosen and whose contents come from the device. The per-field guard in cx231xx_copy_vbi_line() does not help: it bounds the copy against the latched lines_per_field, not the plane's real capacity, and vb2 does not re-run buf_prepare() for an already prepared buffer.

Refuse the format/standard change when the VBI queue is busy as well, so the geometry cannot change underneath an allocated VBI buffer.

Fixes 7c617138b825 ("media: cx231xx: convert to the vb2 framework")
---
 drivers/media/usb/cx231xx/cx231xx-video.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/media/usb/cx231xx/cx231xx-video.c +2 -2
@@ -898,7 +898,7 @@ static int vidioc_s_fmt_vid_cap(struct f
 	if (rc)
 		return rc;
 
-	if (vb2_is_busy(&dev->vidq)) {
+	if (vb2_is_busy(&dev->vidq) || vb2_is_busy(&dev->vbiq)) {
 		dev_err(dev->dev, "%s: queue busy\n", __func__);
 		return -EBUSY;
 	}
@@ -933,7 +933,7 @@ static int vidioc_s_std(struct file *fil
 	if (dev->norm == norm)
 		return 0;
 
-	if (vb2_is_busy(&dev->vidq))
+	if (vb2_is_busy(&dev->vidq) || vb2_is_busy(&dev->vbiq))
 		return -EBUSY;
 
 	dev->norm = norm;
author maintainer
#284 lore raw

PATCH 284/583 media: cx23885: cancel NetUP CI work before teardown

In reply to Greg Kroah-Hartman

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

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

From: Fan Wu <[email protected]>

commit 4e143d662ca94888b494b2427fc9e34494eb933a upstream.

netup_ci_exit() frees a netup_ci_state while its work item, netup_read_ci_status(), may still be pending or running on the system workqueue. The worker obtains the state with container_of() and dereferences it, so it must not outlive the state.

netup_ci_init() queues the initial status read, and CI GPIO interrupts subsequently queue the same work from netup_ci_slot_status(). During remove, cx23885_finidev() calls free_irq() before the CI device is unregistered. free_irq() prevents further IRQ handlers from running, but does not drain work queued previously, so the worker can run after netup_ci_exit() frees the state.

Call cancel_work_sync() before dvb_ca_en50221_release() and kfree().

This issue was found by an in-house static analysis tool.

Fixes c184dcd28233 ("V4L/DVB (10798): Add CIMax(R) SP2 Common Interface code for NetUP Dual DVB-S2 CI card")
Assisted-by Codex:gpt-5.6
---
 drivers/media/pci/cx23885/cimax2.c |    1 +
 1 file changed, 1 insertion(+)
drivers/media/pci/cx23885/cimax2.c +1 -0
@@ -528,6 +528,7 @@ void netup_ci_exit(struct cx23885_tsport
 	if (NULL == state->ca.data)
 		return;
 
+	cancel_work_sync(&state->work);
 	dvb_ca_en50221_release(&state->ca);
 	kfree(state);
 }
author maintainer
#285 lore raw

PATCH 285/583 media: em28xx: defer audio-only extension registration

In reply to Greg Kroah-Hartman

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

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

From: Diego Fernando Mancera Gomez <[email protected]>

commit 95f76f51937fdfb0fc1e14cae606b1ef574a56f3 upstream.

The audio-only path registers extensions while probing the primary device. For a dual-TS board, this happens before dev_next is created. The duplicate device inherits is_audio_only and is then independently inserted into em28xx_devlist.

The list is intended to contain only primary devices: extension operations reach the secondary device through dev_next. The independently linked secondary can be freed during disconnect while its list node remains reachable, resulting in a use-after-free.

Defer audio-only extension registration to the module-request work item. It runs only after probing has completed construction of the optional secondary device, so only the primary is registered and extension callbacks reach the secondary through dev_next.

---
 drivers/media/usb/em28xx/em28xx-cards.c |    3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)
drivers/media/usb/em28xx/em28xx-cards.c +1 -2
@@ -3448,6 +3448,7 @@ static void request_module_async(struct
 	 * intf. Don't register extensions twice on those devices.
 	 */
 	if (dev->is_audio_only) {
+		em28xx_init_extension(dev);
 #if defined(CONFIG_MODULES) && defined(MODULE)
 		request_module("em28xx-alsa");
 #endif
@@ -3676,8 +3677,6 @@ static int em28xx_init_dev(struct em28xx
 			retval = -ENODEV;
 			goto err_deinit_media;
 		}
-		em28xx_init_extension(dev);
-
 		return 0;
 	}
 
author maintainer
#286 lore raw

PATCH 286/583 media: em28xx: fix use-after-free of dev_next->devlist on disconnect

In reply to Greg Kroah-Hartman

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

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

From: Jiangong.Han <[email protected]>

commit 826915b6b65e2d3251e7248ea54289a22d748c84 upstream.

When a device with has_dual_ts=1 is probed and the is_audio_only path is taken, both dev and dev->dev_next are added to the global em28xx_devlist via em28xx_init_extension(). However, during disconnect, em28xx_close_extension(dev) only calls list_del(&dev->devlist), leaving dev->dev_next->devlist still linked in the global list. When dev_next is subsequently freed via kref_put(), its devlist entry becomes a dangling pointer in em28xx_devlist. The next device probe that calls em28xx_init_extension() triggers a list corruption BUG when list_add_tail detects the freed node.

This bug was exposed by commit a368ecde8a50 ("USB: core: Fix duplicate endpoint bug by clearing reserved bits in the descriptor") which clears reserved bits in bEndpointAddress during endpoint parsing. This causes fuzzed endpoint addresses like 0xf3 to be normalized to 0x83, which em28xx interprets as a vendor audio endpoint, enabling the is_audio_only + has_dual_ts code path that was previously unreachable with such descriptors.

Fix this by removing dev->dev_next->devlist from the global list in em28xx_close_extension() before the device is freed.

Fixes f410b4093fdd ("media: em28xx: split up em28xx_dvb_init to reduce stack size")
Reported-by [email protected]
---
 drivers/media/usb/em28xx/em28xx-core.c |    2 ++
 1 file changed, 2 insertions(+)
drivers/media/usb/em28xx/em28xx-core.c +2 -0
@@ -1134,6 +1134,8 @@ void em28xx_close_extension(struct em28x
 			ops->fini(dev);
 		}
 	}
+	if (dev->dev_next)
+		list_del(&dev->dev_next->devlist);
 	list_del(&dev->devlist);
 	mutex_unlock(&em28xx_devlist_mutex);
 }
author maintainer
#287 lore raw

PATCH 287/583 media: go7007: defer the ALSA v4l2 put until card release

In reply to Greg Kroah-Hartman

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

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

From: Shuangpeng Bai <[email protected]>

commit 1bd456afeb8a515137e567967069fce6f8fcd23e upstream.

go7007_snd_init() already takes a v4l2_device reference for the ALSA side, but go7007_snd_remove() drops it immediately after calling snd_card_free_when_closed().

That is too early when a userspace process still has the capture PCM open. The ALSA card and its PCM callbacks remain alive until the last file is closed, so the release path can still reach struct go7007 through pcm->private_data and call go7007_snd_hw_free() after the V4L2 release path has freed the object.

Move the matching v4l2_device_put() to the ALSA card private_free callback so the existing ALSA reference covers the whole deferred card lifetime.

Fixes d5d3a7cc127d ("[media] go7007: fix unregister/disconnect handling")
---
 drivers/media/usb/go7007/snd-go7007.c |   10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)
drivers/media/usb/go7007/snd-go7007.c +9 -1
@@ -195,6 +195,13 @@ static const struct snd_device_ops go700
 	.dev_free	= go7007_snd_free,
 };
 
+static void go7007_snd_card_free(struct snd_card *card)
+{
+	struct go7007 *go = card->private_data;
+
+	v4l2_device_put(&go->v4l2_dev);
+}
+
 int go7007_snd_init(struct go7007 *go)
 {
 	static int dev;
@@ -245,6 +252,8 @@ int go7007_snd_init(struct go7007 *go)
 	gosnd->substream = NULL;
 	go->snd_context = gosnd;
 	v4l2_device_get(&go->v4l2_dev);
+	gosnd->card->private_data = go;
+	gosnd->card->private_free = go7007_snd_card_free;
 	++dev;
 
 	return 0;
@@ -263,7 +272,6 @@ int go7007_snd_remove(struct go7007 *go)
 
 	snd_card_disconnect(gosnd->card);
 	snd_card_free_when_closed(gosnd->card);
-	v4l2_device_put(&go->v4l2_dev);
 	return 0;
 }
 EXPORT_SYMBOL(go7007_snd_remove);
author maintainer
#288 lore raw

PATCH 288/583 media: i2c: alvium: Fix: Correct name of register in alvium_set_ctrl_auto_exposure

In reply to Greg Kroah-Hartman

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

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

From: Martin Hecht <[email protected]>

commit 58ca8a0bff9e78841a39863474b96e59ef60da19 upstream.

Write value for auto-exposure into correct register REG_BCRM_EXPOSURE_AUTO_RW instead of wrong register REG_BCRM_WHITE_BALANCE_AUTO_RW.

Fixes 0a7af872915e ("media: i2c: Add support for alvium camera")
---
 drivers/media/i2c/alvium-csi2.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/media/i2c/alvium-csi2.c +1 -1
@@ -1290,7 +1290,7 @@ static int alvium_set_ctrl_auto_exposure
 	struct device *dev = &alvium->i2c_client->dev;
 	int ret;
 
-	ret = alvium_write_hshake(alvium, REG_BCRM_WHITE_BALANCE_AUTO_RW,
+	ret = alvium_write_hshake(alvium, REG_BCRM_EXPOSURE_AUTO_RW,
 				  on ? 0x02 : 0x00);
 	if (ret) {
 		dev_err(dev, "Fail to set autoexposure reg\n");
author maintainer
#289 lore raw

PATCH 289/583 media: i2c: imx415: Release runtime PM reference on VBLANK error

In reply to Greg Kroah-Hartman

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

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

From: Narasimharao Vadlamudi <[email protected]>

commit bea3001e0f32527a291444e527e84a7ea9b546d4 upstream.

The VBLANK path returned immediately when programming VMAX failed after pm_runtime_get_if_in_use() had taken a runtime PM reference. Break out of the switch instead so the common pm_runtime_put() path is used.

Fixes 3bcae55ab96a ("media: i2c: imx415: Add read/write control of VBLANK")
---
 drivers/media/i2c/imx415.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/media/i2c/imx415.c +1 -1
@@ -720,7 +720,7 @@ static int imx415_s_ctrl(struct v4l2_ctr
 		ret = cci_write(sensor->regmap, IMX415_VMAX,
 				format->height + ctrl->val, NULL);
 		if (ret)
-			return ret;
+			break;
 		/*
 		 * Exposure is set based on VMAX which has just changed, so
 		 * program exposure register as well
author maintainer
#290 lore raw

PATCH 290/583 media: i2c: imx415: Return test pattern write errors

In reply to Greg Kroah-Hartman

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

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

From: Narasimharao Vadlamudi <[email protected]>

commit 8cd5735b88d76dda80b089031747b6f18ee0bca2 upstream.

imx415_set_testpattern() accumulates failures from cci_write(), but drops the value and always returns success. Return the accumulated error so V4L2 reports failures to userspace.

Fixes d5df1c7f3f83 ("media: i2c: imx415: Convert to new CCI register access helpers")
---
 drivers/media/i2c/imx415.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/media/i2c/imx415.c +1 -1
@@ -686,7 +686,7 @@ static int imx415_set_testpattern(struct
 		cci_write(sensor->regmap, IMX415_DIG_CLP_MODE, 0x01, &ret);
 		cci_write(sensor->regmap, IMX415_WRJ_OPEN, 0x01, &ret);
 	}
-	return 0;
+	return ret;
 }
 
 static int imx415_s_ctrl(struct v4l2_ctrl *ctrl)
author maintainer
#291 lore raw

PATCH 291/583 media: i2c: ov02a10: fix endpoint parsing use-after-free

In reply to Greg Kroah-Hartman

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

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

From: Biren Pandya <[email protected]>

commit 94971ba0592ca77ec99b292226a4b398763315b8 upstream.

The ov02a10_check_hwcfg() function calls fwnode_handle_put(ep) immediately after allocating and parsing the endpoint. However, it subsequently calls fwnode_property_read_u32() using the same 'ep' handle, leading to a potential use-after-free.

Additionally, reading the optional 'ovti,mipi-clock-voltage' property used to overwrite the 'ret' variable. If the property was missing, 'ret' would become negative, and this failure code would be incorrectly returned at the end of the function, causing probe to fail entirely.

Fix the use-after-free by moving fwnode_property_read_u32() before the endpoint is parsed and freed. Avoid the error leak by not assigning the result of fwnode_property_read_u32() to 'ret'.

Fixes 91807efbe8ec ("media: i2c: add OV02A10 image sensor driver")
---
 drivers/media/i2c/ov02a10.c |   12 +++++-------
 1 file changed, 5 insertions(+), 7 deletions(-)
drivers/media/i2c/ov02a10.c +5 -7
@@ -820,18 +820,16 @@ static int ov02a10_check_hwcfg(struct de
 	if (!ep)
 		return -ENXIO;
 
+	/* Optional indication of MIPI clock voltage unit */
+	if (!fwnode_property_read_u32(ep, "ovti,mipi-clock-voltage",
+				      &clk_volt))
+		ov02a10->mipi_clock_voltage = clk_volt;
+
 	ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg);
 	fwnode_handle_put(ep);
 	if (ret)
 		return ret;
 
-	/* Optional indication of MIPI clock voltage unit */
-	ret = fwnode_property_read_u32(ep, "ovti,mipi-clock-voltage",
-				       &clk_volt);
-
-	if (!ret)
-		ov02a10->mipi_clock_voltage = clk_volt;
-
 	for (i = 0; i < ARRAY_SIZE(link_freq_menu_items); i++) {
 		for (j = 0; j < bus_cfg.nr_of_link_frequencies; j++) {
 			if (link_freq_menu_items[i] ==
author maintainer
#292 lore raw

PATCH 292/583 media: i2c: ov7740: fix use-after-destroy in remove

In reply to Greg Kroah-Hartman

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

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

From: Biren Pandya <[email protected]>

commit 5d1b3dea5a44124bab6c14a2d71b977dabed54e7 upstream.

The ov7740_remove() function had a severe teardown order bug where it destroyed the driver's mutex before freeing the V4L2 control handler which relies on that mutex, leading to a use-after-destroy kernel panic. Furthermore, the driver explicitly called v4l2_ctrl_handler_free() and mutex_destroy() sequentially, but then called ov7740_free_controls() which invokes both of them a second time, resulting in a double-free.

This patch fixes the issue by unregistering the subdevice first, and relying exclusively on ov7740_free_controls() to safely tear down the mutex and control handler in the correct order.

Fixes 39c5c4471b8d ("media: i2c: Add the ov7740 image sensor driver")
---
 drivers/media/i2c/ov7740.c |    4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)
drivers/media/i2c/ov7740.c +1 -3
@@ -1116,10 +1116,8 @@ static void ov7740_remove(struct i2c_cli
 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
 	struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev);
 
-	mutex_destroy(&ov7740->mutex);
-	v4l2_ctrl_handler_free(ov7740->subdev.ctrl_handler);
-	media_entity_cleanup(&ov7740->subdev.entity);
 	v4l2_async_unregister_subdev(sd);
+	media_entity_cleanup(&ov7740->subdev.entity);
 	ov7740_free_controls(ov7740);
 
 	pm_runtime_get_sync(&client->dev);
author maintainer
#293 lore raw

PATCH 293/583 media: imx355: Avoid calling imx355_power_off twice in error path

In reply to Greg Kroah-Hartman

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

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

From: Dave Stevenson <[email protected]>

commit ee737bc3ccae7dc713ccaa83ffa46080c6031b3e upstream.

If v4l2_async_register_subdev_sensor failed, then the sensor had already been powered down by pm_runtime_idle, but the error path then also explicitly called imx355_power_off as well. That left an imbalance in the regulator and clock calls.

Call pm_runtime_idle only after v4l2_async_register_subdev_sensor succeeds to avoid this.

Fixes efa5fe19c0a9 ("media: imx355: Enable runtime PM before registering async sub-device")
---
 drivers/media/i2c/imx355.c |    3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/media/i2c/imx355.c +2 -1
@@ -1736,12 +1736,13 @@ static int imx355_probe(struct i2c_clien
 	 */
 	pm_runtime_set_active(imx355->dev);
 	pm_runtime_enable(imx355->dev);
-	pm_runtime_idle(imx355->dev);
 
 	ret = v4l2_async_register_subdev_sensor(&imx355->sd);
 	if (ret < 0)
 		goto error_media_entity_runtime_pm;
 
+	pm_runtime_idle(imx355->dev);
+
 	return 0;
 
 error_media_entity_runtime_pm:
author maintainer
#294 lore raw

PATCH 294/583 media: intel/ipu6: fix async notifier cleanup leak on parse error

In reply to Greg Kroah-Hartman

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

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

From: Cong Nguyen <[email protected]>

commit abb1f808ceab5a3275f8a6b4e37cff17f9f781c1 upstream.

isys_notifier_init() calls v4l2_async_nf_init() and then adds fwnode remote subdevs in a loop with v4l2_async_nf_add_fwnode_remote(). If an endpoint parse or add fails partway through the loop, it jumps to err_parse and returns without calling v4l2_async_nf_cleanup(), leaking every v4l2_async_connection already added to the notifier's waiting list.

The register-failure path just below already cleans up correctly, and the caller only tears the notifier down (isys_notifier_cleanup()) once isys_notifier_init() has returned success. Clean up the notifier on the parse error path too.

Fixes f50c4ca0a820 ("media: intel/ipu6: add the main input system driver")
Assisted-by Claude:claude-opus-4
---
 drivers/media/pci/intel/ipu6/ipu6-isys.c |    1 +
 1 file changed, 1 insertion(+)
drivers/media/pci/intel/ipu6/ipu6-isys.c +1 -0
@@ -761,6 +761,7 @@ static int isys_notifier_init(struct ipu
 
 err_parse:
 		fwnode_handle_put(ep);
+		v4l2_async_nf_cleanup(&isys->notifier);
 		return ret;
 	}
 
author maintainer
#295 lore raw

PATCH 295/583 media: iris: Enumerate cap->bus_info to differentiate between encoder and decoder

In reply to Greg Kroah-Hartman

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

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

From: Bryan O'Donoghue <[email protected]>

commit 94ef75095d5e76df848a2e5508d71a4532a6ce23 upstream.

commit 66c744e28b69 ("media: venus: assign unique bus_info strings for encoder and decoder") introduced the naming convention plat:node-addr:video-codec{enc|dec}. Right now Iris does not replicate this naming convention.

When we do v4l2-ctrl --list -devices we see:
Iris Decoder (platform:aa00000.video-codec):

	/dev/video0
	/dev/video1

Enumerate the bus_info field of the capabilities structure for namespace parity and appropriate differentiation:
Iris Decoder (plat:aa00000.video-codec:dec):

	/dev/video0

Iris Encoder (plat:aa00000.video-codec:enc):

	/dev/video1
Fixes 5ad964ad5656 ("media: iris: Initialize and deinitialize encoder instance structure")
---
 drivers/media/platform/qcom/iris/iris_vidc.c |   11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)
drivers/media/platform/qcom/iris/iris_vidc.c +9 -2
@@ -450,14 +450,21 @@ static int iris_enum_frameintervals(stru
 
 static int iris_querycap(struct file *filp, void *fh, struct v4l2_capability *cap)
 {
+	struct iris_core *core = video_drvdata(filp);
 	struct iris_inst *inst = iris_get_inst(filp);
+	char *info;
 
 	strscpy(cap->driver, IRIS_DRV_NAME, sizeof(cap->driver));
 
-	if (inst->domain == DECODER)
+	if (inst->domain == DECODER) {
 		strscpy(cap->card, "Iris Decoder", sizeof(cap->card));
-	else
+		info = "dec";
+	} else {
 		strscpy(cap->card, "Iris Encoder", sizeof(cap->card));
+		info = "enc";
+	}
+	snprintf(cap->bus_info, sizeof(cap->bus_info),
+		 "plat:%s:%s", dev_name(core->dev), info);
 
 	return 0;
 }
author maintainer
#296 lore raw

PATCH 296/583 media: meson: vdec: fix NULL pointer deref in vdec_try_fmt_common

In reply to Greg Kroah-Hartman

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

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

From: Christian Hewitt <[email protected]>

commit 20aa934ace6917262ff579a73ec018d06a7bad1c upstream.

When VIDIOC_TRY_FMT is called with an unsupported pixel format on the OUTPUT queue, vdec_try_fmt_common() falls back to V4L2_PIX_FMT_MPEG2. However, if a distro has locally patched MPEG2 support out (as it has been broken for some time) the platform format table does not contain MPEG2 so find_format() returns NULL and the subsequent dereference of fmt_out->max_width triggers a NULL pointer dereference.

Fix this by falling back to the first format in the platform's format array instead of hardcoding V4L2_PIX_FMT_MPEG2. This is always valid since every platform defines at least one format.

Fixes 3e7f51bd9607 ("media: meson: add v4l2 m2m video decoder driver")
---
 drivers/staging/media/meson/vdec/vdec.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/staging/media/meson/vdec/vdec.c +2 -2
@@ -504,8 +504,8 @@ vdec_try_fmt_common(struct amvdec_sessio
 	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
 		fmt_out = find_format(fmts, size, pixmp->pixelformat);
 		if (!fmt_out) {
-			pixmp->pixelformat = V4L2_PIX_FMT_MPEG2;
-			fmt_out = find_format(fmts, size, pixmp->pixelformat);
+			pixmp->pixelformat = fmts[0].pixfmt;
+			fmt_out = &fmts[0];
 		}
 		break;
 	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
author maintainer
#297 lore raw

PATCH 297/583 media: platform: mtk-mdp3: Fix SCP device refcounting

In reply to Greg Kroah-Hartman

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

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

From: Guangshuo Li <[email protected]>

commit 55793e4665b7f15151e6f5ab51ca980e73abed5d upstream.

mdp_probe() first tries to get the SCP handle with scp_get(). When that fails, it falls back to looking up the SCP platform device with __get_pdev_by_id() and then reads its driver data.

The fallback lookup returns the platform device with a reference, just like scp_get() does. However, the fallback path currently drops that reference immediately after platform_get_drvdata(). The driver later still calls scp_put(mdp->scp) unconditionally from the probe error path and from mdp_video_device_release(), which drops the SCP device reference again.

Keep the fallback reference until the existing scp_put() call, so that the fallback path follows the same ownership rules as the scp_get() path.

Fixes 8f6f3aa21517 ("media: platform: mtk-mdp3: fix device leaks at probe")
---
 drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c |    1 -
 1 file changed, 1 deletion(-)
drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c +0 -1
@@ -292,7 +292,6 @@ static int mdp_probe(struct platform_dev
 			goto err_destroy_clock_wq;
 		}
 		mdp->scp = platform_get_drvdata(mm_pdev);
-		put_device(&mm_pdev->dev);
 	}
 
 	mdp->rproc_handle = scp_get_rproc(mdp->scp);
author maintainer
#298 lore raw

PATCH 298/583 media: platform: mtk-mdp3: fix NULL deref on failed SCP lookup

In reply to Greg Kroah-Hartman

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

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

From: Johan Hovold <[email protected]>

commit 90368323fb244da0504e3da37a182f8e89bcc3b9 upstream.

Add the missing sanity check after looking up the SCP to avoid dereferencing a NULL-pointer in case its driver has not yet been bound.

Fixes 61890ccaefaf ("media: platform: mtk-mdp3: add MediaTek MDP3 driver")
Cc Moudy Ho <[email protected]>
---
 drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c |    5 +++++
 1 file changed, 5 insertions(+)
drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c +5 -0
@@ -294,6 +294,11 @@ static int mdp_probe(struct platform_dev
 		mdp->scp = platform_get_drvdata(mm_pdev);
 	}
 
+	if (!mdp->scp) {
+		ret = -EPROBE_DEFER;
+		goto err_destroy_clock_wq;
+	}
+
 	mdp->rproc_handle = scp_get_rproc(mdp->scp);
 	dev_dbg(&pdev->dev, "MDP rproc_handle: %p", mdp->rproc_handle);
 
author maintainer
#299 lore raw

PATCH 299/583 media: nxp: imx8-isi: Fix stream ID validation bypass in crossbar routing

In reply to Greg Kroah-Hartman

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

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

From: Guoniu Zhou <[email protected]>

commit 795bd2863591f2fee33fab8f10cf9c383c94342e upstream.

The crossbar routing validation has a critical bug where it validates the wrong routing table, allowing userspace to bypass validation entirely.

The __mxc_isi_crossbar_set_routing() function is called to validate and apply a new routing table from userspace. However, the validation loop iterates over state->routing (the currently active routing table) instead of the routing parameter (the new table being validated):

    for_each_active_route(&state->routing, route) {

This means userspace can submit any invalid routing configuration and it will pass validation as long as the currently active routing is valid. This is a security issue as it allows userspace to configure routes that violate hardware constraints, potentially causing undefined hardware behavior.

Fix by validating the routing table that will actually be applied.

---
 drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c +1 -1
@@ -107,7 +107,7 @@ static int __mxc_isi_crossbar_set_routin
 		return ret;
 
 	/* The memory input can be routed to the first pipeline only. */
-	for_each_active_route(&state->routing, route) {
+	for_each_active_route(routing, route) {
 		if (route->sink_pad == xbar->num_sinks - 1 &&
 		    route->source_pad != xbar->num_sinks) {
 			dev_dbg(xbar->isi->dev,
author maintainer
#300 lore raw

PATCH 300/583 media: nxp: imx8-isi: Correct color map between V4L2 and ISI

In reply to Greg Kroah-Hartman

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

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

From: Guoniu Zhou <[email protected]>

commit 4640ec1cb0121473867e7a6850c7449340dcd428 upstream.

Fix the ISI input format for the color map V4L2_PIX_FMT_XBGR32 in memory-to-memory mode.

---
 drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c +1 -1
@@ -151,7 +151,7 @@ static const struct mxc_isi_format_info
 		.fourcc		= V4L2_PIX_FMT_XBGR32,
 		.type		= MXC_ISI_VIDEO_CAP | MXC_ISI_VIDEO_M2M_OUT
 				| MXC_ISI_VIDEO_M2M_CAP,
-		.isi_in_format	= CHNL_MEM_RD_CTRL_IMG_TYPE_XBGR8,
+		.isi_in_format	= CHNL_MEM_RD_CTRL_IMG_TYPE_XRGB8,
 		.isi_out_format	= CHNL_IMG_CTRL_FORMAT_XRGB888,
 		.mem_planes	= 1,
 		.color_planes	= 1,
author maintainer
#301 lore raw

PATCH 301/583 media: nxp: imx8-isi: Use BIT_ULL() for 64-bit stream masks

In reply to Greg Kroah-Hartman

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

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

From: Guoniu Zhou <[email protected]>

commit 77e60a2c5d824ad2d493f53dc17137ae065753fe upstream.

Use BIT_ULL() instead of BIT() for u64 stream masks to avoid incorrect results on 32-bit architectures when stream IDs are 32 or greater.

---
 drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c +2 -2
@@ -145,10 +145,10 @@ mxc_isi_crossbar_xlate_streams(struct mx
 	 */
 	for_each_active_route(&state->routing, route) {
 		if (route->source_pad != source_pad ||
-		    !(source_streams & BIT(route->source_stream)))
+		    !(source_streams & BIT_ULL(route->source_stream)))
 			continue;
 
-		sink_streams |= BIT(route->sink_stream);
+		sink_streams |= BIT_ULL(route->sink_stream);
 		sink_pad = route->sink_pad;
 	}
 
author maintainer
#302 lore raw

PATCH 302/583 media: rc: sunxi-cir: Unregister rc device on probe failure

In reply to Greg Kroah-Hartman

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

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

From: Myeonghun Pak <[email protected]>

commit 479aa6fa8c50f1052f1451326ef7d4d586d340c3 upstream.

After rc_register_device() succeeds, later probe failures must undo the registration with rc_unregister_device(). The current error path jumps to the allocation cleanup label and only calls rc_free_device(), leaving the rc device registration and resources created by rc_register_device() behind.

Add a registered-device unwind label for the IRQ lookup, IRQ request, and hardware initialization failure paths. Keep rc_free_device() for failures before rc_register_device() succeeds.

Fixes b4e3e59fb59c ("[media] rc: add sunxi-ir driver")
---
 drivers/media/rc/sunxi-cir.c |    9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)
drivers/media/rc/sunxi-cir.c +6 -3
@@ -344,22 +344,25 @@ static int sunxi_ir_probe(struct platfor
 	ir->irq = platform_get_irq(pdev, 0);
 	if (ir->irq < 0) {
 		ret = ir->irq;
-		goto exit_free_dev;
+		goto exit_unregister_dev;
 	}
 
 	ret = devm_request_irq(dev, ir->irq, sunxi_ir_irq, 0, SUNXI_IR_DEV, ir);
 	if (ret) {
 		dev_err(dev, "failed request irq\n");
-		goto exit_free_dev;
+		goto exit_unregister_dev;
 	}
 
 	ret = sunxi_ir_hw_init(dev);
 	if (ret)
-		goto exit_free_dev;
+		goto exit_unregister_dev;
 
 	dev_info(dev, "initialized sunXi IR driver\n");
 	return 0;
 
+exit_unregister_dev:
+	rc_unregister_device(ir->rc);
+
 exit_free_dev:
 	rc_free_device(ir->rc);
 
author maintainer
#303 lore raw

PATCH 303/583 media: rkvdec: Propagate platform_get_irq() errors

In reply to Greg Kroah-Hartman

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

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

From: Narasimharao Vadlamudi <[email protected]>

commit c37aca64206fafe938119e801a3fd10a537a051f upstream.

platform_get_irq() returns a positive IRQ number on success and a negative error code on failure. It no longer returns zero. The driver currently returns -ENXIO for all failures, which loses useful errors such as -EPROBE_DEFER.

Return the error from platform_get_irq() directly.

---
 drivers/media/platform/rockchip/rkvdec/rkvdec.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/media/platform/rockchip/rkvdec/rkvdec.c +2 -2
@@ -1160,8 +1160,8 @@ static int rkvdec_probe(struct platform_
 	vb2_dma_contig_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32));
 
 	irq = platform_get_irq(pdev, 0);
-	if (irq <= 0)
-		return -ENXIO;
+	if (irq < 0)
+		return irq;
 
 	ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
 					rkvdec_irq_handler, IRQF_ONESHOT,
author maintainer
#304 lore raw

PATCH 304/583 media: rtl2832_sdr: use vb2_video_unregister_device() on remove to fix DMA leak

In reply to Greg Kroah-Hartman

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

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

From: Valery Borovsky <[email protected]>

commit dabb047c62668f280998e29117c55e41aabac336 upstream.

rtl2832_sdr_remove() runs on USB disconnect and clears dev->udev to NULL before any pending streaming teardown has run. When user space later closes its file descriptor, vb2 calls rtl2832_sdr_stop_streaming() which in turn calls rtl2832_sdr_free_stream_bufs(). That helper releases each coherent buffer with:

    usb_free_coherent(dev->udev, dev->buf_size,
                      dev->buf_list[dev->buf_num],
                      dev->dma_addr[dev->buf_num]);

usb_free_coherent() returns immediately when its dev argument is NULL, so every DMA stream buffer that was live at disconnect is silently leaked. The URBs allocated in rtl2832_sdr_alloc_urbs() outlive the device for the same reason.

The rtl2832_sdr driver uses vb2_fop_release() in its file_operations, so replace video_unregister_device(&dev->vdev) with vb2_video_unregister_device(&dev->vdev) and move it before clearing dev->udev. vb2_video_unregister_device() releases the vb2 queue, which synchronously runs rtl2832_sdr_stop_streaming() if streaming is active, so URBs and coherent DMA stream buffers are freed while dev->udev is still valid.

vb2_video_unregister_device() locks vdev->queue->lock (vb_queue_lock) internally, and stop_streaming() locks v4l2_lock, so the previous outer mutex_lock(&dev->vb_queue_lock) / mutex_lock(&dev->v4l2_lock) pair around the unregister sequence would self-deadlock and has been removed. A short v4l2_lock critical section around dev->udev = NULL remains so any ioctl path that still holds the file descriptor sees coherent state.

Issue identified by automated review of the INV-003 series at
https://sashiko.dev/

Fixes 771138920eaf ("[media] rtl2832_sdr: Realtek RTL2832 SDR driver module")
---
 drivers/media/dvb-frontends/rtl2832_sdr.c |   18 +++++++++++++-----
 1 file changed, 13 insertions(+), 5 deletions(-)
drivers/media/dvb-frontends/rtl2832_sdr.c +13 -5
@@ -1477,14 +1477,22 @@ static void rtl2832_sdr_remove(struct pl
 
 	dev_dbg(&pdev->dev, "\n");
 
-	mutex_lock(&dev->vb_queue_lock);
+	/*
+	 * vb2_video_unregister_device() releases the vb2 queue, which
+	 * triggers rtl2832_sdr_stop_streaming() if streaming is active.
+	 * stop_streaming() uses dev->udev to free URBs and coherent DMA
+	 * stream buffers via usb_free_coherent(), so it must run before
+	 * dev->udev is cleared. vb2_video_unregister_device() locks
+	 * vb_queue_lock internally and stop_streaming() locks v4l2_lock,
+	 * so neither may be held by the caller.
+	 */
+	v4l2_device_disconnect(&dev->v4l2_dev);
+	vb2_video_unregister_device(&dev->vdev);
+
 	mutex_lock(&dev->v4l2_lock);
-	/* No need to keep the urbs around after disconnection */
 	dev->udev = NULL;
-	v4l2_device_disconnect(&dev->v4l2_dev);
-	video_unregister_device(&dev->vdev);
 	mutex_unlock(&dev->v4l2_lock);
-	mutex_unlock(&dev->vb_queue_lock);
+
 	v4l2_device_put(&dev->v4l2_dev);
 	module_put(pdev->dev.parent->driver->owner);
 }
author maintainer
#305 lore raw

PATCH 305/583 media: rtl2832_sdr: release URBs and stream buffers on start_streaming() failure

In reply to Greg Kroah-Hartman

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

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

From: Valery Borovsky <[email protected]>

commit fe50cdaebf12cd32ff9a44d92bfd6fbc2300dbd4 upstream.

rtl2832_sdr_start_streaming() calls rtl2832_sdr_alloc_stream_bufs(), rtl2832_sdr_alloc_urbs() and rtl2832_sdr_submit_urbs() in sequence and shares a single err: label that only unlocks the mutex and returns. When alloc_urbs() succeeds but submit_urbs() fails, or when alloc_urbs() itself returns -ENOMEM after alloc_stream_bufs() has already succeeded, the URBs and/or the coherent DMA stream buffers stay allocated while streaming reports failure to vb2. Two latent defects follow on the next VIDIOC_STREAMON:

1) rtl2832_sdr_alloc_stream_bufs() unconditionally resets dev->buf_num to 0 and overwrites dev->buf_list[]/dev->dma_addr[], permanently leaking the coherent DMA memory allocated by the previous attempt.

2) rtl2832_sdr_alloc_urbs() never resets dev->urbs_initialized and only increments it. After a second successful pass urbs_initialized can exceed MAX_BULK_BUFS, so the subsequent rtl2832_sdr_free_urbs() walks from urbs_initialized - 1 down to 0 and reads past the end of dev->urb_list[], passing garbage pointers to usb_free_urb().

Mirror the teardown that stop_streaming() already performs: on the error path call rtl2832_sdr_free_urbs() and rtl2832_sdr_free_stream_bufs() before unlocking. Both helpers are idempotent (free_urbs kills and zeros urbs_initialized; free_stream_bufs is gated on URB_BUF and clears the buf_num counter), so partial-failure paths and the no-allocation paths remain safe.

Issue identified by automated review of the INV-003 series at
https://sashiko.dev/

Fixes 771138920eaf ("[media] rtl2832_sdr: Realtek RTL2832 SDR driver module")
---
 drivers/media/dvb-frontends/rtl2832_sdr.c |    3 +++
 1 file changed, 3 insertions(+)
drivers/media/dvb-frontends/rtl2832_sdr.c +3 -0
@@ -906,9 +906,12 @@ static int rtl2832_sdr_start_streaming(s
 		goto err;
 
 	mutex_unlock(&dev->v4l2_lock);
+
 	return 0;
 
 err:
+	rtl2832_sdr_free_urbs(dev);
+	rtl2832_sdr_free_stream_bufs(dev);
 	rtl2832_sdr_cleanup_queued_bufs(dev, VB2_BUF_STATE_QUEUED);
 	mutex_unlock(&dev->v4l2_lock);
 
author maintainer
#306 lore raw

PATCH 306/583 media: rzg2l-cru: Align bytesperline to hardware DMA stride requirement

In reply to Greg Kroah-Hartman

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

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

From: Tommaso Merciai <[email protected]>

commit 56c29fa3ee666197516a231e75aed789ae9c530d upstream.

The RZ/G3E CRU programs the line stride via the AMnIS register, whose IS field encodes the value in units of 128 bytes. If bytesperline is not a multiple of 128, the division truncates and the hardware uses a wrong stride, causing horizontal banding.

Commit ace92ccef0c9 ("media: platform: rzg2l-cru: Use v4l2_fill_pixfmt()") replaced the open-coded aligned calculation with v4l2_fill_pixfmt(), which sets no alignment, reintroducing the issue.

Round bytesperline up to RZG2L_CRU_STRIDE_ALIGN and recompute sizeimage when info->has_stride is set. RZ/G2L has no AMnIS register and keeps the values from v4l2_fill_pixfmt() unchanged.

Fixes ace92ccef0c9 ("media: platform: rzg2l-cru: Use v4l2_fill_pixfmt()")
---
 drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c |    5 +++++
 1 file changed, 5 insertions(+)
drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c +5 -0
@@ -934,6 +934,11 @@ static void rzg2l_cru_format_align(struc
 
 	v4l2_fill_pixfmt(pix, pix->pixelformat, pix->width, pix->height);
 
+	if (info->has_stride) {
+		pix->bytesperline = ALIGN(pix->bytesperline, RZG2L_CRU_STRIDE_ALIGN);
+		pix->sizeimage = pix->bytesperline * pix->height;
+	}
+
 	dev_dbg(cru->dev, "Format %ux%u bpl: %u size: %u\n",
 		pix->width, pix->height, pix->bytesperline, pix->sizeimage);
 }
author maintainer
#307 lore raw

PATCH 307/583 media: s2255: bound JPEG frame size before copying into the buffer

In reply to Greg Kroah-Hartman

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

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

From: HyeongJun An <[email protected]>

commit e504cc888f42999dd76b6a43788c422610f2aad2 upstream.

s2255_fillbuff() memcpy()s vc->jpg_size bytes of a captured JPEG/MJPEG frame into the vb2 plane. vc->jpg_size is taken verbatim from the S2255_MARKER_FRAME header the device sends (pdword[4] in save_frame()) and, unlike the frame payload length just above it, is never bounded:

	payload = le32_to_cpu(pdword[3]);
	if (payload > vc->req_image_size)	/* payload is checked ... */
		return -EINVAL;
	vc->pkt_size = payload;
	vc->jpg_size = le32_to_cpu(pdword[4]);	/* ... jpg_size is not */

A malicious or malfunctioning device can therefore report a jpg_size larger than the destination vb2 plane, and the memcpy() writes past it. jpg_size is a signed int, so a value with the top bit set also turns into a huge length.

Reject a frame whose jpg_size is negative or exceeds the plane size before copying it.

Fixes 38f993ad8b1f ("V4L/DVB (8125): This driver adds support for the Sensoray 2255 devices.")
Assisted-by Claude:claude-opus-4-8
---
 drivers/media/usb/s2255/s2255drv.c |    6 ++++++
 1 file changed, 6 insertions(+)
drivers/media/usb/s2255/s2255drv.c +6 -0
@@ -617,6 +617,12 @@ static void s2255_fillbuff(struct s2255_
 			break;
 		case V4L2_PIX_FMT_JPEG:
 		case V4L2_PIX_FMT_MJPEG:
+			if (jpgsize < 0 ||
+			    jpgsize > vb2_plane_size(&buf->vb.vb2_buf, 0)) {
+				dprintk(dev, 1, "bad JPEG frame size %d\n",
+					jpgsize);
+				break;
+			}
 			vb2_set_plane_payload(&buf->vb.vb2_buf, 0, jpgsize);
 			memcpy(vbuf, tmpbuf, jpgsize);
 			break;
author maintainer
#308 lore raw

PATCH 308/583 media: s2255: check firmware size before reading trailing marker

In reply to Greg Kroah-Hartman

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

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

From: Lei Huang <[email protected]>

commit 330f2936ab768c7215322a476f033143e8891d28 upstream.

s2255_probe() reads a 4-byte marker and version from the last 8 bytes of the firmware blob (fw->data[fw_size - 8] and [fw_size - 4]). If the firmware file is shorter than 8 bytes, fw_size - 8 underflows and the access reads out of bounds. Validate the firmware size before indexing.

Fixes 14d962602c8b ("V4L/DVB (8752): s2255drv: firmware improvement patch")
---
 drivers/media/usb/s2255/s2255drv.c |    5 +++++
 1 file changed, 5 insertions(+)
drivers/media/usb/s2255/s2255drv.c +5 -0
@@ -2287,6 +2287,11 @@ static int s2255_probe(struct usb_interf
 	}
 	/* check the firmware is valid */
 	fw_size = dev->fw_data->fw->size;
+	if (fw_size < 8) {
+		dev_err(&interface->dev, "Firmware invalid: too small.\n");
+		retval = -ENODEV;
+		goto errorFWMARKER;
+	}
 	pdata = (__le32 *) &dev->fw_data->fw->data[fw_size - 8];
 
 	if (*pdata != S2255_FW_MARKER) {
author maintainer
#309 lore raw

PATCH 309/583 media: saa7164: fix cleanup on resource allocation failure

In reply to Greg Kroah-Hartman

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

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

From: Guangshuo Li <[email protected]>

commit 28e84c6e2e6753ed238ea097b2842a32a6a6879b upstream.

saa7164_dev_setup() adds the device to the global saa7164_devlist before requesting the PCI BAR memory regions.

If get_resources() fails, saa7164_dev_setup() decrements the device count and returns an error, but leaves the device on saa7164_devlist. The probe error path then frees the device, leaving a dangling entry on the global list.

Reuse the existing MMIO mapping error path to remove the device from saa7164_devlist and decrement the device count before returning.

Also release BAR0 if it was successfully requested but the BAR2 request fails.

Fixes 443c1228d505 ("V4L/DVB (12923): SAA7164: Add support for the NXP SAA7164 silicon")
---
 drivers/media/pci/saa7164/saa7164-core.c |    8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)
drivers/media/pci/saa7164/saa7164-core.c +5 -3
@@ -878,6 +878,9 @@ static int get_resources(struct saa7164_
 		if (request_mem_region(pci_resource_start(dev->pci, 2),
 			pci_resource_len(dev->pci, 2), dev->name))
 			return 0;
+
+		release_mem_region(pci_resource_start(dev->pci, 0),
+				   pci_resource_len(dev->pci, 0));
 	}
 
 	printk(KERN_ERR "%s: can't get MMIO memory @ 0x%llx or 0x%llx\n",
@@ -1000,8 +1003,7 @@ static int saa7164_dev_setup(struct saa7
 		       dev->name, dev->pci->subsystem_vendor,
 		       dev->pci->subsystem_device);
 
-		saa7164_devcount--;
-		return -ENODEV;
+		goto err_devlist;
 	}
 
 	/* PCI/e allocations */
@@ -1039,7 +1041,7 @@ err_ioremap_bar2:
 	iounmap(dev->lmmio);
 err_ioremap_bar0:
 	release_resources(dev);
-
+err_devlist:
 	scoped_guard(mutex, &devlist) {
 		list_del(&dev->devlist);
 	}
author maintainer
#310 lore raw

PATCH 310/583 media: tda18250: fix possible integer overflow

In reply to Greg Kroah-Hartman

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

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

From: Ilya Krutskih <[email protected]>

commit 6dd8e257f7cafda7fbf10d81b3c55c9bba4825f4 upstream.

Integer overflow may occur, when variable exp equals to zero. Result of shift 1 << (exp - 1) may then leads to undefined behavior.

Fixes 148abd3b5b14 ("media: tda18250: support for new silicon tuner")
---
 drivers/media/tuners/tda18250.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/media/tuners/tda18250.c +2 -2
@@ -440,8 +440,8 @@ static int tda18250_pll_calc(struct dvb_
 		goto err;
 
 	exp = (uval & 0x70) >> 4;
-	if (exp > 5)
-		exp = 0;
+	if (exp == 0 || exp > 5)
+		exp = 1;
 	lopd = 1 << (exp - 1);
 	scale = uval & 0x0f;
 	fvco = lopd * scale * ((c->frequency / 1000) + dev->if_frequency);
author maintainer
#311 lore raw

PATCH 311/583 media: v4l2-async: avoid deleting unlinked ASC entry on link error

In reply to Greg Kroah-Hartman

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

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

From: Xu Rao <[email protected]>

commit 47d82b605351c0e04f6365e42c8ffe2fcfdba615 upstream.

v4l2_async_match_notify() creates ancillary media links before adding asc->asc_subdev_entry to sd->asc_list.

If ancillary link creation fails, the function jumps to err_call_unbind while asc_subdev_entry has not been linked yet. Async connections are zero-allocated, so the list entry still has NULL next and prev pointers on this path. Calling list_del() on it can therefore dereference NULL instead of returning the original link creation error.

Do not delete asc_subdev_entry from err_call_unbind. There is no list insertion to undo on this path; the bound callback and sub-device registration are the operations that need to be rolled back.

Fixes 28a1295795d8 ("media: v4l: async: Allow multiple connections between entities")
---
 drivers/media/v4l2-core/v4l2-async.c |    1 -
 1 file changed, 1 deletion(-)
drivers/media/v4l2-core/v4l2-async.c +0 -1
@@ -392,7 +392,6 @@ static int v4l2_async_match_notify(struc
 
 err_call_unbind:
 	v4l2_async_nf_call_unbind(notifier, sd, asc);
-	list_del(&asc->asc_subdev_entry);
 
 err_unregister_subdev:
 	if (registered)
author maintainer
#312 lore raw

PATCH 312/583 media: v4l2-ctrls: Allow unknown HDR10 white point and luminance

In reply to Greg Kroah-Hartman

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

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

From: Ming Qian <[email protected]>

commit 49af0c7cad889f7dabe5cf080b019392066122a3 upstream.

SMPTE ST 2086 defines the nominal ranges for mastering display chromaticity and luminance values. Its Annex A also documents that CTA 861-G uses zero maximum and minimum luminance values to signal that the corresponding values are unknown, and the xy chromaticity coordinate (0, 0) to signal that the white point chromaticity is unknown.

The V4L2 HDR10 mastering display compound control currently rejects these values. Consequently, an unknown white point or luminance value prevents the entire compound control from being updated, making the other valid mastering display metadata unavailable to userspace.

Accept (0, 0) as an unknown white point and zero as an unknown maximum or minimum mastering luminance. Continue to reject partially zero white point coordinates and non-zero values outside the nominal ranges. Display primary validation remains unchanged.

Document the newly accepted unknown values in the V4L2 userspace API.

Fixes 1ad0de78e794 ("media: v4l: Add HDR10 static metadata controls")
---
 Documentation/userspace-api/media/v4l/ext-ctrls-colorimetry.rst |   12 ++
 drivers/media/v4l2-core/v4l2-ctrls-core.c                       |   49 ++++++----
 2 files changed, 44 insertions(+), 17 deletions(-)
Documentation/userspace-api/media/v4l/ext-ctrls-colorimetry.rst +11 -1
@@ -79,15 +79,25 @@ Colorimetry Control IDs
       - ``white_point_x``
       - Specifies the normalized x chromaticity coordinate of the white
         point of the mastering display in increments of 0.00002.
+        When both ``white_point_x`` and ``white_point_y`` are zero,
+        the white point chromaticity is unknown. If either coordinate is
+        non-zero, both coordinates shall be within their valid ranges.
     * - __u16
       - ``white_point_y``
       - Specifies the normalized y chromaticity coordinate of the white
         point of the mastering display in increments of 0.00002.
+        When both ``white_point_x`` and ``white_point_y`` are zero,
+        the white point chromaticity is unknown. If either coordinate is
+        non-zero, both coordinates shall be within their valid ranges.
     * - __u32
       - ``max_luminance``
       - Specifies the nominal maximum display luminance of the mastering
         display in units of 0.0001 cd/m\ :sup:`2`.
+        A value of zero indicates that the nominal maximum display
+        luminance is unknown.
     * - __u32
       - ``min_luminance``
-      - specifies the nominal minimum display luminance of the mastering
+      - Specifies the nominal minimum display luminance of the mastering
         display in units of 0.0001 cd/m\ :sup:`2`.
+        A value of zero indicates that the nominal minimum display
+        luminance is unknown.
drivers/media/v4l2-core/v4l2-ctrls-core.c +33 -16

57 lines not on this page · raw message

author maintainer
#313 lore raw

PATCH 313/583 media: v4l2-fwnode: Fix fwnode leak in v4l2_fwnode_parse_link

In reply to Greg Kroah-Hartman

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

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

From: Biren Pandya <[email protected]>

commit a6e86efd7f85e519bf48417f41923f8bd51f1597 upstream.

In v4l2_fwnode_parse_link(), the remote endpoint fwnode reference is acquired using fwnode_graph_get_remote_endpoint(). This reference is properly released in the error paths, but it is leaked on the success path.

Add the missing fwnode_handle_put() before returning 0 to prevent the reference leak.

Fixes ca50c197bd96 ("[media] v4l: fwnode: Support generic fwnode for parsing standardised properties")
[Sakari Ailus: Fix subject prefix and coding style a little.]
---
 drivers/media/v4l2-core/v4l2-fwnode.c |    2 ++
 1 file changed, 2 insertions(+)
drivers/media/v4l2-core/v4l2-fwnode.c +2 -0
@@ -623,6 +623,8 @@ int v4l2_fwnode_parse_link(struct fwnode
 	if (!link->remote_node)
 		goto err_put_remote_endpoint;
 
+	fwnode_handle_put(fwnode);
+
 	return 0;
 
 err_put_remote_endpoint:
author maintainer
#314 lore raw

PATCH 314/583 media: venus: fix payload size returned by parse_caps() and parse_alloc_mode()

In reply to Greg Kroah-Hartman

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

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

From: Mohammed EL Kadiri <[email protected]>

commit a51cea23e409278f6e2ea072280aba93cc1dd75e upstream.

parse_caps() and parse_alloc_mode() return only the size of their fixed header fields, excluding the flexible array payload. hfi_parser() uses this return value to advance through the firmware response buffer, so underreporting causes parser desynchronization.

Return the full consumed size (header + entries), matching the correct pattern used by parse_profile_level().

Fixes 9edaaa8e3e15 ("media: venus: hfi_parser: refactor hfi packet parsing logic")
---
 drivers/media/platform/qcom/venus/hfi_parser.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/media/platform/qcom/venus/hfi_parser.c +2 -2
@@ -85,7 +85,7 @@ parse_alloc_mode(struct venus_core *core
 		type++;
 	}
 
-	return sizeof(*mode);
+	return mode->num_entries * sizeof(u32) + sizeof(*mode);
 }
 
 static void fill_profile_level(struct hfi_plat_caps *cap, const void *data,
@@ -146,7 +146,7 @@ parse_caps(struct venus_core *core, u32
 	for_each_codec(core->caps, ARRAY_SIZE(core->caps), codecs, domain,
 		       fill_caps, caps_arr, num_caps);
 
-	return sizeof(*caps);
+	return num_caps * sizeof(*cap) + sizeof(u32);
 }
 
 static void fill_raw_fmts(struct hfi_plat_caps *cap, const void *fmts,
author maintainer
#315 lore raw

PATCH 315/583 media: venus: fix payload size calculation in parse_raw_formats()

In reply to Greg Kroah-Hartman

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

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

From: Mohammed EL Kadiri <[email protected]>

commit bd595b745eb770e80347c31ffc25351046935305 upstream.

The consumed size is computed after the loop using the num_planes value from the last iteration for all entries. When entries have different plane counts, this produces an incorrect total.

Accumulate the actual size during the loop instead.

Fixes 9edaaa8e3e15 ("media: venus: hfi_parser: refactor hfi packet parsing logic")
---
 drivers/media/platform/qcom/venus/hfi_parser.c |    5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)
drivers/media/platform/qcom/venus/hfi_parser.c +2 -3
@@ -171,7 +171,7 @@ parse_raw_formats(struct venus_core *cor
 	u32 entries = fmt->format_entries;
 	unsigned int i = 0;
 	u32 num_planes = 0;
-	u32 size;
+	u32 size = 2 * sizeof(u32);
 
 	while (entries) {
 		num_planes = pinfo->num_planes;
@@ -186,6 +186,7 @@ parse_raw_formats(struct venus_core *cor
 		if (pinfo->num_planes > MAX_PLANES)
 			break;
 
+		size += sizeof(*constr) * num_planes + 2 * sizeof(u32);
 		pinfo = (void *)pinfo + sizeof(*constr) * num_planes +
 			2 * sizeof(u32);
 		entries--;
@@ -193,8 +194,6 @@ parse_raw_formats(struct venus_core *cor
 
 	for_each_codec(core->caps, ARRAY_SIZE(core->caps), codecs, domain,
 		       fill_raw_fmts, rawfmts, i);
-	size = fmt->format_entries * (sizeof(*constr) * num_planes + 2 * sizeof(u32))
-		+ 2 * sizeof(u32);
 
 	return size;
 }
author maintainer
#316 lore raw

PATCH 316/583 media: video-i2c: fix kthread error pointer left in kthread_vid_cap on failure

In reply to Greg Kroah-Hartman

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

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

From: Uday Khare <[email protected]>

commit 76e379754ba618989f6215be608d5c04774a611d upstream.

kthread_run() returns an ERR_PTR on failure, not NULL. When start_streaming() fails, data->kthread_vid_cap is left holding this error pointer instead of being cleared.

This causes two subsequent bugs:
1. A future call to start_streaming() sees a non-NULL kthread_vid_cap and returns 0 (success) immediately, without actually starting the capture thread.
2. A call to stop_streaming() checks 'kthread_vid_cap == NULL' which is false for an error pointer, and proceeds to call kthread_stop() on the error pointer, leading to a kernel crash.

Fix this by resetting kthread_vid_cap to NULL on failure before jumping to the error path.

Fixes 5cebaac60974 ("media: video-i2c: add video-i2c driver")
---
 drivers/media/i2c/video-i2c.c |    8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)
drivers/media/i2c/video-i2c.c +6 -2
@@ -522,8 +522,12 @@ static int start_streaming(struct vb2_qu
 	data->kthread_vid_cap = kthread_run(video_i2c_thread_vid_cap, data,
 					    "%s-vid-cap", data->v4l2_dev.name);
 	ret = PTR_ERR_OR_ZERO(data->kthread_vid_cap);
-	if (!ret)
-		return 0;
+	if (ret) {
+		data->kthread_vid_cap = NULL;
+		goto error_rpm_put;
+	}
+
+	return 0;
 
 error_rpm_put:
 	pm_runtime_put_autosuspend(dev);
author maintainer
#317 lore raw

PATCH 317/583 media: vimc: fix pixel format lookup in enum_framesizes

In reply to Greg Kroah-Hartman

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

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

From: Arash Golgol <[email protected]>

commit ad4c65fa30cfb00e2e06adae9a8eb407086eaa66 upstream.

vimc_capture_enum_framesizes() looks up the requested format using vimc_pix_map_by_code(), which searches the pix map table by media bus code (MEDIA_BUS_FMT_*).

However, v4l2_frmsizeenum::pixel_format holds a V4L2 pixel format (V4L2_PIX_FMT_*), not a media bus code, so valid pixel formats end up being rejected with -EINVAL.

Fix this by using vimc_pix_map_by_pixelformat() instead, which performs the lookup by pixel format as the ioctl expects.

Fixes 09c41a23a2e2 ("media: Revert "media: vimc: propagate pixel format in the stream"")
---
 drivers/media/test-drivers/vimc/vimc-capture.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/media/test-drivers/vimc/vimc-capture.c +2 -2
@@ -175,8 +175,8 @@ static int vimc_capture_enum_framesizes(
 	if (fsize->index)
 		return -EINVAL;
 
-	/* Only accept code in the pix map table */
-	vpix = vimc_pix_map_by_code(fsize->pixel_format);
+	/* Only accept pixel_format in the pix map table */
+	vpix = vimc_pix_map_by_pixelformat(fsize->pixel_format);
 	if (!vpix)
 		return -EINVAL;
 
author maintainer
#318 lore raw

PATCH 318/583 media: zoran: Avoid freeing a registered video_device twice

In reply to Greg Kroah-Hartman

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

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

From: Ruoyu Wang <[email protected]>

commit 0735e0b5a96761a9ce277a238e834008ad92a0a5 upstream.

zoran_init_video_device() installs zoran_vdev_release() as the video_device release callback through zoran_template. After video_register_device() succeeds, video_unregister_device() drops the registered video_device reference and the V4L2 core eventually invokes that release callback, which kfree()s the video_device.

zoran_exit_video_devices() called video_unregister_device() and then kfree(zr->video_dev), so device teardown could free the same video_device twice.

Remove the direct kfree() and clear the cached pointer after unregistering. The pre-registration failure path keeps its manual free because the video_device was not registered there.

This issue was found by a static analysis checker and confirmed by manual source review.

Fixes 82e3a496eb56 ("media: staging: media: zoran: move videodev alloc")
---
 drivers/media/pci/zoran/zoran_card.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/media/pci/zoran/zoran_card.c +1 -1
@@ -885,7 +885,7 @@ static int zoran_init_video_device(struc
 static void zoran_exit_video_devices(struct zoran *zr)
 {
 	video_unregister_device(zr->video_dev);
-	kfree(zr->video_dev);
+	zr->video_dev = NULL;
 }
 
 static int zoran_init_video_devices(struct zoran *zr)
author maintainer
#319 lore raw

PATCH 319/583 media: qcom: iris: fix state-change debug log printing stale value

In reply to Greg Kroah-Hartman

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

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

From: Dikshita Agarwal <[email protected]>

commit 460d3257a6dffc7f0bf58009daeb7e0b6eb2d9d3 upstream.

The state‑change debug log in iris_inst_change_state() always prints the same value for the old and new state, rendering it useless for debugging. This happens because the state is updated before the log is emitted.

Log the transition before updating the state so the previous value is preserved, consistent with the existing sub‑state handling.

---
 drivers/media/platform/qcom/iris/iris_state.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/media/platform/qcom/iris/iris_state.c +1 -1
index d14472414750..e991f34916ec 100644
@@ -60,9 +60,9 @@ int iris_inst_change_state(struct iris_inst *inst,
 		return -EINVAL;
 
 change_state:
-	inst->state = request_state;
 	dev_dbg(inst->core->dev, "state changed from %x to %x\n",
 		inst->state, request_state);
+	inst->state = request_state;
 
 	return 0;
 }
Signature · 1 line
2.55.0
author maintainer
#320 lore raw

PATCH 320/583 media: qcom: iris: use disable_irq() during power-off

In reply to Greg Kroah-Hartman

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

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

From: Hungyu Lin <[email protected]>

commit b9c2215bdedc9c532a7e9d57ec49ee1b6381f863 upstream.

The IRQ is registered as a threaded IRQ.

Using disable_irq_nosync() in iris_vpu_power_off() does not wait for an already queued threaded IRQ handler to complete before returning.

As a result, a threaded IRQ handler may still run after the VPU has been powered down and access hardware registers after power-off.

Replace disable_irq_nosync() with disable_irq() so the power-off path waits for any in-flight threaded IRQ handler to complete before returning.

---
 drivers/media/platform/qcom/iris/iris_vpu_common.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/media/platform/qcom/iris/iris_vpu_common.c +1 -1
@@ -272,7 +272,7 @@ void iris_vpu_power_off(struct iris_core
 	iris_unset_icc_bw(core);
 
 	if (!iris_vpu_watchdog(core, core->intr_status))
-		disable_irq_nosync(core->irq);
+		disable_irq(core->irq);
 }
 
 int iris_vpu_power_on_controller(struct iris_core *core)
author maintainer
#321 lore raw

PATCH 321/583 media: chips-media: wave5: Guard bit depth check with initial_info_obtained

In reply to Greg Kroah-Hartman

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

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

From: Jackson Lee <[email protected]>

commit 1551386934ad43d934c3bb7317929207e1edcd6a upstream.

When CAPTURE STREAMON is called before the VPU has completed sequence initialization (initial_info_obtained == false), the initial_info fields contain uninitialized data. The driver checks
luma_bitdepth and rejects anything other than 8-bit, so garbage values (e.g. 15) cause STREAMON to fail spuriously.

This is reproducible with the following multi-threaded test scenario:
1. Allocate 2 CAPTURE buffers.
2. Call STREAMON on the CAPTURE queue.
3. Call DQBUF, which blocks waiting for a decoded frame.
4. A second thread calls STREAMOFF on the CAPTURE queue.
5. The blocked DQBUF should be released, allowing graceful termination.

At step 2, STREAMON reads uninitialized luma_bitdepth and rejects the stream, causing the test to fail.

Fix this by checking initial_info_obtained before accessing the bit depth fields, so the validation is only performed when the sequence info has actually been parsed by the VPU.

Fixes 035371c9e509 ("media: chips-media: wave5: Fix timeout while testing 10bit hevc fluster")
---
 drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c |    3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c +2 -1
@@ -1352,6 +1352,7 @@ static int wave5_vpu_dec_start_streaming
 	} else if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
 		struct dec_initial_info *initial_info =
 			&inst->codec_info->dec_info.initial_info;
+		struct dec_info *p_dec_info = &inst->codec_info->dec_info;
 
 		if (inst->state == VPU_INST_STATE_STOP)
 			ret = switch_state(inst, VPU_INST_STATE_INIT_SEQ);
@@ -1359,6 +1360,7 @@ static int wave5_vpu_dec_start_streaming
 			goto return_buffers;
 
 		if (inst->state == VPU_INST_STATE_INIT_SEQ &&
+		    p_dec_info->initial_info_obtained &&
 		    inst->dev->product_code == WAVE521C_CODE) {
 			if (initial_info->luma_bitdepth != 8) {
 				dev_info(inst->dev->dev, "%s: no support for %d bit depth",
@@ -1367,7 +1369,6 @@ static int wave5_vpu_dec_start_streaming
 				goto return_buffers;
 			}
 		}
-
 	}
 	pm_runtime_put_autosuspend(inst->dev->dev);
 	return ret;
author maintainer
#322 lore raw

PATCH 322/583 media: chips-media: wave5: Set inst->std during default format initialization

In reply to Greg Kroah-Hartman

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

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

From: Jackson Lee <[email protected]>

commit cfcefc5a996f6a00b310c963b5f811430f28a30a upstream.

When the encoder is opened, wave5_set_default_format() sets up the default capture format (e.g. H.264) but does not initialize inst->std. As a result, inst->std remains zero, which does not match any valid encoder codec.

If STREAMON is called before the user explicitly calls S_FMT on the capture queue — as v4l2-compliance does in testBlockingDQBuf — the codec/product check in wave5_vpu_enc_init_seq() fails with "Unsupported encoder-codec & product combination" because inst->std is neither W_HEVC_ENC nor W_AVC_ENC, returning -EOPNOTSUPP.

Fix this by setting inst->std via wave5_to_vpu_std() in wave5_set_default_format(), so that the codec type is always consistent with the default capture pixel format from the moment the instance is opened.

Fixes 9707a6254a8a ("media: chips-media: wave5: Add the v4l2 layer")
---
 drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c |    6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)
drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c +4 -2
@@ -1458,7 +1458,8 @@ static const struct vb2_ops wave5_vpu_en
 	.stop_streaming = wave5_vpu_enc_stop_streaming,
 };
 
-static void wave5_set_default_format(struct v4l2_pix_format_mplane *src_fmt,
+static void wave5_set_default_format(struct vpu_instance *inst,
+				     struct v4l2_pix_format_mplane *src_fmt,
 				     struct v4l2_pix_format_mplane *dst_fmt)
 {
 	src_fmt->pixelformat = enc_fmt_list[VPU_FMT_TYPE_RAW][0].v4l2_pix_fmt;
@@ -1470,6 +1471,7 @@ static void wave5_set_default_format(str
 	wave5_update_pix_fmt(dst_fmt, VPU_FMT_TYPE_CODEC,
 			     W5_DEF_ENC_PIC_WIDTH, W5_DEF_ENC_PIC_HEIGHT,
 			     &enc_frmsize[VPU_FMT_TYPE_CODEC]);
+	inst->std = wave5_to_vpu_std(dst_fmt->pixelformat, inst->type);
 }
 
 static int wave5_vpu_enc_queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq)
@@ -1731,7 +1733,7 @@ static int wave5_vpu_open_enc(struct fil
 	inst->v4l2_fh.ctrl_handler = v4l2_ctrl_hdl;
 	v4l2_ctrl_handler_setup(v4l2_ctrl_hdl);
 
-	wave5_set_default_format(&inst->src_fmt, &inst->dst_fmt);
+	wave5_set_default_format(inst, &inst->src_fmt, &inst->dst_fmt);
 	inst->conf_win.width = inst->dst_fmt.width;
 	inst->conf_win.height = inst->dst_fmt.height;
 	inst->colorspace = V4L2_COLORSPACE_REC709;
author maintainer
#323 lore raw

PATCH 323/583 scsi: qla2xxx: Zero SFP DMA buffer in FRU/I2C bsg handlers

In reply to Greg Kroah-Hartman

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

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

From: Nilesh Javali <[email protected]>

commit b47d4a1547d9ef21b2e9d1a739fe2204d4be05dc upstream.

The FRU and I2C bsg handlers stage their transfer in a DMA_POOL_SIZE (256-byte) bounce buffer obtained from dma_pool_alloc(), which does not zero the allocation. They initialize only a few leading bytes before handing the buffer to qla2x00_write_sfp().

qla2x00_write_sfp() can override the transfer length with a user-supplied value:

	if (len == 1)
		opt |= BIT_0;
	if (opt & BIT_0)
		len = *sfp;

*sfp is the first byte of the (user-controlled) payload, so len can grow up to 255. The device then DMA-reads len bytes from the 256-byte pool buffer. Since only a small prefix was written
(e.g. MAX_FRU_SIZE == 36 bytes for a FRU version, one byte for a FRU status register), the hardware reads past the initialized region and writes up to ~219 bytes of stale DMA-pool heap memory to the device flash.

Allocate the buffer with dma_pool_zalloc() in all five FRU/I2C handlers so any bytes beyond the initialized data are zero rather than stale heap contents.

Fixes 697a4bc69159 ("[SCSI] qla2xxx: Provide method for updating I2C attached VPD.")
Fixes 9ebb5d9c69f1 ("[SCSI] qla2xxx: Add I2C BSG interface.")
---
 drivers/scsi/qla2xxx/qla_bsg.c |   10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)
drivers/scsi/qla2xxx/qla_bsg.c +5 -5
@@ -1565,7 +1565,7 @@ qla2x00_update_fru_versions(struct bsg_j
 	struct qla_image_version *image;
 	uint32_t count;
 	dma_addr_t sfp_dma;
-	void *sfp = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
+	void *sfp = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
 
 	if (!sfp) {
 		bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
@@ -1616,7 +1616,7 @@ qla2x00_read_fru_status(struct bsg_job *
 	uint8_t bsg[DMA_POOL_SIZE];
 	struct qla_status_reg *sr = (void *)bsg;
 	dma_addr_t sfp_dma;
-	uint8_t *sfp = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
+	uint8_t *sfp = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
 
 	if (!sfp) {
 		bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
@@ -1667,7 +1667,7 @@ qla2x00_write_fru_status(struct bsg_job
 	uint8_t bsg[DMA_POOL_SIZE];
 	struct qla_status_reg *sr = (void *)bsg;
 	dma_addr_t sfp_dma;
-	uint8_t *sfp = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
+	uint8_t *sfp = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
 
 	if (!sfp) {
 		bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
@@ -1714,7 +1714,7 @@ qla2x00_write_i2c(struct bsg_job *bsg_jo
 	uint8_t bsg[DMA_POOL_SIZE];
 	struct qla_i2c_access *i2c = (void *)bsg;
 	dma_addr_t sfp_dma;
-	uint8_t *sfp = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
+	uint8_t *sfp = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
 
 	if (!sfp) {
 		bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
@@ -1760,7 +1760,7 @@ qla2x00_read_i2c(struct bsg_job *bsg_job
 	uint8_t bsg[DMA_POOL_SIZE];
 	struct qla_i2c_access *i2c = (void *)bsg;
 	dma_addr_t sfp_dma;
-	uint8_t *sfp = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
+	uint8_t *sfp = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
 
 	if (!sfp) {
 		bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
author maintainer
#324 lore raw

PATCH 324/583 scsi: qla2xxx: Bound i2c->length in I2C bsg handlers

In reply to Greg Kroah-Hartman

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

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

From: Nilesh Javali <[email protected]>

commit 0918ee2c0eeb4d7f45b82b3dc11e65c2d9b7ad59 upstream.

struct qla_i2c_access carries a 16-bit length field alongside a fixed 64-byte buffer:

	struct qla_i2c_access {
		uint16_t device, offset, option, length;
		uint8_t  buffer[0x40];
	} __packed;

qla2x00_write_i2c() and qla2x00_read_i2c() use the user-supplied i2c->length without any bounds check. i2c is overlaid on a 256-byte on-stack buffer and sfp is a 256-byte DMA-pool buffer, so a length up to 65535 overruns both:

- write: memcpy(sfp, i2c->buffer, i2c->length) over-reads the stack and over-writes the sfp heap buffer, and qla2x00_write_sfp() then DMAs i2c->length bytes out of the 256-byte buffer.
- read: qla2x00_read_sfp() DMAs i2c->length bytes into the 256-byte sfp, then memcpy(i2c->buffer, sfp, i2c->length) overflows the 64-byte buffer inside the on-stack array.

A caller holding CAP_SYS_RAWIO can use this to corrupt the heap and the kernel stack. Reject requests whose length exceeds the buffer before any copy or DMA transfer in both handlers.

---
 drivers/scsi/qla2xxx/qla_bsg.c |   12 ++++++++++++
 1 file changed, 12 insertions(+)
drivers/scsi/qla2xxx/qla_bsg.c +12 -0
@@ -1725,6 +1725,12 @@ qla2x00_write_i2c(struct bsg_job *bsg_jo
 	sg_copy_to_buffer(bsg_job->request_payload.sg_list,
 	    bsg_job->request_payload.sg_cnt, i2c, sizeof(*i2c));
 
+	if (i2c->length > sizeof(i2c->buffer)) {
+		bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
+		    EXT_STATUS_INVALID_PARAM;
+		goto dealloc;
+	}
+
 	memcpy(sfp, i2c->buffer, i2c->length);
 	rval = qla2x00_write_sfp(vha, sfp_dma, sfp,
 	    i2c->device, i2c->offset, i2c->length, i2c->option);
@@ -1771,6 +1777,12 @@ qla2x00_read_i2c(struct bsg_job *bsg_job
 	sg_copy_to_buffer(bsg_job->request_payload.sg_list,
 	    bsg_job->request_payload.sg_cnt, i2c, sizeof(*i2c));
 
+	if (i2c->length > sizeof(i2c->buffer)) {
+		bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
+		    EXT_STATUS_INVALID_PARAM;
+		goto dealloc;
+	}
+
 	rval = qla2x00_read_sfp(vha, sfp_dma, sfp,
 		i2c->device, i2c->offset, i2c->length, i2c->option);
 
author maintainer
#325 lore raw

PATCH 325/583 scsi: qla2xxx: edif: Fix NULL pointer deref in RX SA delete check

In reply to Greg Kroah-Hartman

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

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

From: Nilesh Javali <[email protected]>

commit c20ee380ca59c5a8646750c4849969a815924e2e upstream.

qla_chk_edif_rx_sa_delete_pending() obtains the SCSI command via GET_CMD_SP(sp) and immediately dereferences cmd->sc_data_direction. That command pointer can be NULL: the firmware may post a status completion for a command that has already been returned or aborted. The caller qla2x00_status_entry() acknowledges this on the very same status path, re-fetching GET_CMD_SP(sp) and bailing out with the "Command already returned" message when it is NULL -- but that check runs only after qla_chk_edif_rx_sa_delete_pending() has already dereferenced the pointer, so a NULL cmd crashes the kernel in interrupt context.

Return early when cmd is NULL, before touching cmd->sc_data_direction.

---
 drivers/scsi/qla2xxx/qla_edif.c |    3 +++
 1 file changed, 3 insertions(+)
drivers/scsi/qla2xxx/qla_edif.c +3 -0
@@ -3492,6 +3492,9 @@ void qla_chk_edif_rx_sa_delete_pending(s
 	struct scsi_cmnd *cmd = GET_CMD_SP(sp);
 	uint32_t handle;
 
+	if (!cmd)
+		return;
+
 	handle = (uint32_t)LSW(sts24->handle);
 
 	/* find out if this status iosb is for a scsi read */
author maintainer
#326 lore raw

PATCH 326/583 scsi: qla2xxx: Fix Name Server logout detection on FWI2 adapters

In reply to Greg Kroah-Hartman

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

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

From: Nilesh Javali <[email protected]>

commit eb137255fd7aa834c4d639ae7b5e9e8ecf3a4fb2 upstream.

In the CS_PORT_LOGGED_OUT case of qla2x00_chk_ms_status(), the FWI2-capable branch compared ms_pkt->loop_id.extended against NPH_SNS to decide whether the Name Server had logged out. On FWI2 and later adapters the response is a ct_entry_24xx / ct_entry_24xx_ext, where loop_id.extended (via the legacy ms_iocb_entry_t view) aliases offset 8, which is comp_status, not nport_handle (offset 10). As this code runs under CS_PORT_LOGGED_OUT, the field read back 0x29 (CS_PORT_LOGGED_OUT) and the comparison against NPH_SNS (0x7fc) was always false.

As a result the driver never recognized a Name Server logout on FWI2/ 29xx adapters: it returned the generic QLA_FUNCTION_FAILED instead of QLA_NOT_LOGGED_IN and skipped setting LOOP_RESYNC_NEEDED / LOCAL_LOOP_UPDATE, so the fabric rediscovery triggered by an SNS logout did not happen.

Read nport_handle from the ct_entry_24xx layout (offset 10) instead. nport_handle is at the same offset in ct_entry_24xx and ct_entry_24xx_ext, so a single cast covers 24xx-class and 29xx. The non-FWI2 branch keeps using loop_id.extended, which is correct for the ms_iocb_entry_t response on those adapters.

---
 drivers/scsi/qla2xxx/qla_gs.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/scsi/qla2xxx/qla_gs.c +2 -2
@@ -157,8 +157,8 @@ qla2x00_chk_ms_status(scsi_qla_host_t *v
 			break;
 		case CS_PORT_LOGGED_OUT:
 			if (IS_FWI2_CAPABLE(ha)) {
-				if (le16_to_cpu(ms_pkt->loop_id.extended) ==
-				    NPH_SNS)
+				if (le16_to_cpu(((struct ct_entry_24xx *)
+				    ms_pkt)->nport_handle) == NPH_SNS)
 					lid_is_sns = true;
 			} else {
 				if (le16_to_cpu(ms_pkt->loop_id.extended) ==
author maintainer
#327 lore raw

PATCH 327/583 scsi: qla2xxx: Hold vport reference in qla24xx_report_id_acquisition()

In reply to Greg Kroah-Hartman

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

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

From: Nilesh Javali <[email protected]>

commit 793cedee296fd819bfadc2a7ec4d52faf9c09a0a upstream.

In the format 1 path, the virtual port is located on ha->vp_list while holding vport_slock, but the lock is dropped before vp is used:
qla_update_host_map() is called and VP_IDX_ACQUIRED/REGISTER_FC4_NEEDED/ REGISTER_FDMI_NEEDED are set on vp. No reference is taken across that window, so a concurrent qla24xx_deallocate_vp_id() can tear the vport down and free it, leading to a use-after-free.

Take a vport reference (vref_count) under vport_slock when the matching vp is found, and drop it after the last use of
vp. qla24xx_deallocate_vp_id() waits for vref_count to reach zero before unlinking and freeing the vport, so the pointer stays valid. This matches the reference idiom already used by the other ha->vp_list traversals.

---
 drivers/scsi/qla2xxx/qla_mbx.c |    3 +++
 1 file changed, 3 insertions(+)
drivers/scsi/qla2xxx/qla_mbx.c +3 -0
@@ -4146,6 +4146,7 @@ qla24xx_report_id_acquisition(scsi_qla_h
 			list_for_each_entry(vp, &ha->vp_list, list) {
 				if (rptid_entry->vp_idx == vp->vp_idx) {
 					found = 1;
+					atomic_inc(&vp->vref_count);
 					break;
 				}
 			}
@@ -4163,6 +4164,8 @@ qla24xx_report_id_acquisition(scsi_qla_h
 			set_bit(VP_IDX_ACQUIRED, &vp->vp_flags);
 			set_bit(REGISTER_FC4_NEEDED, &vp->dpc_flags);
 			set_bit(REGISTER_FDMI_NEEDED, &vp->dpc_flags);
+
+			atomic_dec(&vp->vref_count);
 		}
 		set_bit(VP_DPC_NEEDED, &vha->dpc_flags);
 		qla2xxx_wake_dpc(vha);
author maintainer
#328 lore raw

PATCH 328/583 scsi: qla2xxx: Initialize NVMe abort_work once at submission

In reply to Greg Kroah-Hartman

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

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

From: Nilesh Javali <[email protected]>

commit 7e85f6dbc85616de2172bce8eaf84b387a723cd1 upstream.

qla_nvme_fcp_abort() and qla_nvme_ls_abort() ran INIT_WORK() on priv->abort_work immediately before schedule_work(). INIT_WORK() reinitializes the work_struct, resetting its list head and clearing the pending bit. If an abort is issued more than once for the same command (for example, concurrent transport teardown and a timeout-driven abort), the second INIT_WORK() reinitializes a work item that is already queued, which can corrupt the workqueue list and lead to crashes or a looping worker.

Initialize priv->abort_work once at command submission, next to the existing per-command spin_lock_init(&priv->cmd_lock), and leave only schedule_work() in the abort paths. schedule_work() already does nothing when the work item is still pending, so a repeated abort no longer disturbs an in-flight work item. The command is not returned to the transport until the final kref_put()/release callback runs after abort_work has completed, so the work item is idle before priv is reused and the single submission-time INIT_WORK() is safe.

---
 drivers/scsi/qla2xxx/qla_nvme.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/scsi/qla2xxx/qla_nvme.c +2 -2
@@ -463,7 +463,6 @@ static void qla_nvme_ls_abort(struct nvm
 	}
 	spin_unlock_irqrestore(&priv->cmd_lock, flags);
 
-	INIT_WORK(&priv->abort_work, qla_nvme_abort_work);
 	schedule_work(&priv->abort_work);
 }
 
@@ -501,6 +500,7 @@ static int qla_nvme_ls_req(struct nvme_f
 	priv->sp = sp;
 	kref_init(&sp->cmd_kref);
 	spin_lock_init(&priv->cmd_lock);
+	INIT_WORK(&priv->abort_work, qla_nvme_abort_work);
 	nvme = &sp->u.iocb_cmd;
 	priv->fd = fd;
 	nvme->u.nvme.desc = fd;
@@ -545,7 +545,6 @@ static void qla_nvme_fcp_abort(struct nv
 	}
 	spin_unlock_irqrestore(&priv->cmd_lock, flags);
 
-	INIT_WORK(&priv->abort_work, qla_nvme_abort_work);
 	schedule_work(&priv->abort_work);
 }
 
@@ -811,6 +810,7 @@ static int qla_nvme_post_cmd(struct nvme
 
 	kref_init(&sp->cmd_kref);
 	spin_lock_init(&priv->cmd_lock);
+	INIT_WORK(&priv->abort_work, qla_nvme_abort_work);
 	sp->priv = priv;
 	priv->sp = sp;
 	sp->type = SRB_NVME_CMD;
author maintainer
#329 lore raw

PATCH 329/583 scsi: qla2xxx: Check entry_status in qla24xx_modify_vp_config()

In reply to Greg Kroah-Hartman

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

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

From: Nilesh Javali <[email protected]>

commit 9101c51649f5b6773a97bf5271785c948589ea1d upstream.

The Modify VP Config completion handler labelled its first error branch "error status" but tested vpmod->comp_status instead of vpmod->entry_status. Because CS_COMPLETE is 0, the following "comp_status != CS_COMPLETE" branch duplicated that test and was dead code, and entry_status was never examined at all.

When firmware rejects the IOCB early it sets entry_status while leaving comp_status zero. As the IOCB is allocated with dma_pool_zalloc(), both comp_status branches evaluate false and the handler falls through to the success path, calling fc_vport_set_state(FC_VPORT_INITIALIZING) for a configuration the firmware never accepted. This can leave the virtual port enabled on top of an invalid config and surface later as login timeouts or follow-on firmware errors.

Test entry_status in the first branch, matching qla_ctrlvp_completed() and the login/logout/abort/reset IOCB handlers; the comp_status branch then becomes the live completion-status check.

---
 drivers/scsi/qla2xxx/qla_mbx.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/scsi/qla2xxx/qla_mbx.c +2 -2
@@ -4276,10 +4276,10 @@ qla24xx_modify_vp_config(scsi_qla_host_t
 	if (rval != QLA_SUCCESS) {
 		ql_dbg(ql_dbg_mbx, vha, 0x10bd,
 		    "Failed to issue VP config IOCB (%x).\n", rval);
-	} else if (vpmod->comp_status != 0) {
+	} else if (vpmod->entry_status != 0) {
 		ql_dbg(ql_dbg_mbx, vha, 0x10be,
 		    "Failed to complete IOCB -- error status (%x).\n",
-		    vpmod->comp_status);
+		    vpmod->entry_status);
 		rval = QLA_FUNCTION_FAILED;
 	} else if (vpmod->comp_status != cpu_to_le16(CS_COMPLETE)) {
 		ql_dbg(ql_dbg_mbx, vha, 0x10bf,
author maintainer
#330 lore raw

PATCH 330/583 scsi: qla2xxx: Bound image count in qla2x00_update_fru_versions()

In reply to Greg Kroah-Hartman

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

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

From: Nilesh Javali <[email protected]>

commit de62cf265dbe309f34f144a6cdbca9240317727e upstream.

qla2x00_update_fru_versions() copies the user-supplied BSG request into a fixed 256-byte stack buffer (bsg[DMA_POOL_SIZE]) and then iterates list->count times over the qla_image_version array embedded in that buffer, advancing the image pointer each iteration. count is taken directly from user input with no upper bound, while only (DMA_POOL_SIZE
- sizeof(list->count)) / sizeof(struct qla_image_version) = 6 entries actually fit. A larger count walks the image pointer off the end of the stack buffer, reading adjacent kernel stack memory and sending it to the device via qla2x00_write_sfp().

Reject requests whose declared count does not fit in the buffer.

---
 drivers/scsi/qla2xxx/qla_bsg.c |    7 +++++++
 1 file changed, 7 insertions(+)
drivers/scsi/qla2xxx/qla_bsg.c +7 -0
@@ -1578,6 +1578,13 @@ qla2x00_update_fru_versions(struct bsg_j
 
 	image = list->version;
 	count = list->count;
+
+	if (struct_size(list, version, count) > sizeof(bsg)) {
+		bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
+		    EXT_STATUS_INVALID_PARAM;
+		goto dealloc;
+	}
+
 	while (count--) {
 		memcpy(sfp, &image->field_info, sizeof(image->field_info));
 		rval = qla2x00_write_sfp(vha, sfp_dma, sfp,
author maintainer
#331 lore raw

PATCH 331/583 scsi: qla2xxx: Hold qpair lock when sending NVMe LS reject

In reply to Greg Kroah-Hartman

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

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

From: Nilesh Javali <[email protected]>

commit f743488e4a203049f27ec5d8cd0caccc483af01e upstream.

qla_nvme_ls_reject_iocb() allocates from and advances the request ring through __qla2x00_alloc_iocbs() (which assumes the hardware_lock is held) and qla2x00_start_iocbs() (which advances the ring and rings the request-in doorbell), but takes no lock itself. Two of its callers invoke it without the producer lock held:

- qla_nvme_xmt_ls_rsp(), the NVMe-FC .xmt_ls_rsp transport callback, on its error path, and

- qla2xxx_process_purls_pkt(), run from the purex work/DPC context.

Both use ha->base_qpair, whose qp_lock_ptr is hardware_lock, so they can run concurrently with normal I/O submission on the base ring and corrupt the ring producer state, leading to duplicated or dropped commands. The third caller, qla2xxx_process_purls_iocb(), runs inside qla24xx_process_response_queue() with the qpair lock already held and is safe; that is also why the lock cannot be taken inside the helper itself (it would recursively re-acquire hardware_lock on the response path).

Take qp_lock_ptr around the two unlocked callers and document the helper as caller-locked. Both run in process context, so spin_lock_irqsave() is used and nothing in the locked region sleeps.

Fixes 875386b98857 ("scsi: qla2xxx: Add Unsolicited LS Request and Response Support for NVMe")
---
 drivers/scsi/qla2xxx/qla_nvme.c |   10 ++++++++++
 1 file changed, 10 insertions(+)
drivers/scsi/qla2xxx/qla_nvme.c +10 -0
@@ -374,6 +374,7 @@ static int qla_nvme_xmt_ls_rsp(struct nv
 	srb_t *sp;
 	int rval = QLA_FUNCTION_FAILED;
 	uint8_t cnt = 0;
+	unsigned long flags;
 
 	if (!fcport || fcport->deleted)
 		goto out;
@@ -440,7 +441,9 @@ out:
 	a.vp_idx = vha->vp_idx;
 	a.nport_handle = uctx->nport_handle;
 	a.xchg_address = uctx->exchange_address;
+	spin_lock_irqsave(ha->base_qpair->qp_lock_ptr, flags);
 	qla_nvme_ls_reject_iocb(vha, ha->base_qpair, &a, true);
+	spin_unlock_irqrestore(ha->base_qpair->qp_lock_ptr, flags);
 	kfree(uctx);
 	return rval;
 }
@@ -1127,6 +1130,10 @@ static void qla_nvme_lsrjt_pt_iocb(struc
 	lsrjt_iocb->rx_byte_count = 0;
 }
 
+/*
+ * Allocates from and advances the request ring, so the caller must hold
+ * qp->qp_lock_ptr (the response-queue caller already holds it).
+ */
 static int
 qla_nvme_ls_reject_iocb(struct scsi_qla_host *vha, struct qla_qpair *qp,
 			struct qla_nvme_lsrjt_pt_arg *a, bool is_xchg_terminate)
@@ -1183,6 +1190,7 @@ qla2xxx_process_purls_pkt(struct scsi_ql
 {
 	struct qla_nvme_unsol_ctx *uctx = item->purls_context;
 	struct qla_nvme_lsrjt_pt_arg a;
+	unsigned long flags;
 	int ret = 1;
 
 #if (IS_ENABLED(CONFIG_NVME_FC))
@@ -1195,7 +1203,9 @@ qla2xxx_process_purls_pkt(struct scsi_ql
 		a.vp_idx = vha->vp_idx;
 		a.nport_handle = uctx->nport_handle;
 		a.xchg_address = uctx->exchange_address;
+		spin_lock_irqsave(vha->hw->base_qpair->qp_lock_ptr, flags);
 		qla_nvme_ls_reject_iocb(vha, vha->hw->base_qpair, &a, true);
+		spin_unlock_irqrestore(vha->hw->base_qpair->qp_lock_ptr, flags);
 		list_del(&uctx->elem);
 		kfree(uctx);
 	}
author maintainer
#332 lore raw

PATCH 332/583 scsi: qla2xxx: Clamp MSI-X derived queue counts to avoid truncation

In reply to Greg Kroah-Hartman

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

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

From: Nilesh Javali <[email protected]>

commit ebfd35c64433821bd5619a6d07ccc2df8b5b1de3 upstream.

ha->msix_count is u16, but ha->max_req_queues, ha->max_rsp_queues and ha->max_qpairs are u8. Deriving the queue count as "ha->max_req_queues = ha->msix_count - 1" therefore truncates: a board (or a misconfigured/malicious hot-plugged device) advertising 257 MSI-X vectors yields msix_count - 1 == 256, which truncates to 0. An MSI-X count of 1 zeroes it as well, and in target mode the subsequent "ha->max_req_queues--" then underflows 0 to 255.

When the count is 0, qla2x00_alloc_queues() calls kzalloc_objs(struct req_que *, 0), which returns ZERO_SIZE_PTR. That is not NULL, so the allocation check passes and the following "ha->req_q_map[0] = req" dereferences ZERO_SIZE_PTR, corrupting memory or crashing the kernel.

Add qla_calc_queue_count() to clamp the derived value into [1, QLA_MAX_QUEUES - 1] so it always fits in u8 and is never zero, and use it at all three derivation sites (qla25xx_iospace_config(), qla83xx_iospace_config() and qla24xx_enable_msix()). Also guard the target-mode decrement so it cannot reintroduce a zero (which would in turn underflow max_qpairs).

---
 drivers/scsi/qla2xxx/qla_inline.h |   13 +++++++++++++
 drivers/scsi/qla2xxx/qla_isr.c    |    4 ++--
 drivers/scsi/qla2xxx/qla_os.c     |    6 +++---
 3 files changed, 18 insertions(+), 5 deletions(-)
drivers/scsi/qla2xxx/qla_inline.h +13 -0
@@ -54,6 +54,19 @@ qla2x00_debounce_register(volatile __le1
 	return (first);
 }
 
+static inline u8
+qla_calc_queue_count(u16 msix_count)
+{
+	/*
+	 * Request/response queues are bounded by the MSI-X vector count less
+	 * the mailbox vector.  These counters are u8, so a board advertising
+	 * e.g. 257 vectors would truncate msix_count - 1 (256) to 0 and hand
+	 * kzalloc_objs() a zero count (ZERO_SIZE_PTR), faulting on the first
+	 * ha->req_q_map[0] store.  Clamp into [1, QLA_MAX_QUEUES - 1].
+	 */
+	return clamp_t(u16, msix_count - 1, 1, QLA_MAX_QUEUES - 1);
+}
+
 static inline void
 qla2x00_poll(struct rsp_que *rsp)
 {
drivers/scsi/qla2xxx/qla_isr.c +2 -2
@@ -4548,10 +4548,10 @@ qla24xx_enable_msix(struct qla_hw_data *
 		ha->msix_count = ret;
 		/* Recalculate queue values */
 		if (ha->mqiobase && (ql2xmqsupport || ql2xnvmeenable)) {
-			ha->max_req_queues = ha->msix_count - 1;
+			ha->max_req_queues = qla_calc_queue_count(ha->msix_count);
 
 			/* ATIOQ needs 1 vector. That's 1 less QPair */
-			if (QLA_TGT_MODE_ENABLED())
+			if (QLA_TGT_MODE_ENABLED() && ha->max_req_queues > 1)
 				ha->max_req_queues--;
 
 			ha->max_rsp_queues = ha->max_req_queues;
drivers/scsi/qla2xxx/qla_os.c +3 -3
@@ -2127,7 +2127,7 @@ skip_pio:
 		ha->msix_count = msix + 1;
 		/* Max queues are bounded by available msix vectors */
 		/* MB interrupt uses 1 vector */
-		ha->max_req_queues = ha->msix_count - 1;
+		ha->max_req_queues = qla_calc_queue_count(ha->msix_count);
 		ha->max_rsp_queues = ha->max_req_queues;
 		/* Queue pairs is the max value minus the base queue pair */
 		ha->max_qpairs = ha->max_rsp_queues - 1;
@@ -2213,10 +2213,10 @@ qla83xx_iospace_config(struct qla_hw_dat
 		 */
 		if (ql2xmqsupport || ql2xnvmeenable) {
 			/* MB interrupt uses 1 vector */
-			ha->max_req_queues = ha->msix_count - 1;
+			ha->max_req_queues = qla_calc_queue_count(ha->msix_count);
 
 			/* ATIOQ needs 1 vector. That's 1 less QPair */
-			if (QLA_TGT_MODE_ENABLED())
+			if (QLA_TGT_MODE_ENABLED() && ha->max_req_queues > 1)
 				ha->max_req_queues--;
 
 			ha->max_rsp_queues = ha->max_req_queues;
author maintainer
#333 lore raw

PATCH 333/583 scsi: qla2xxx: Serialize flash version read in reset handler

In reply to Greg Kroah-Hartman

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

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

From: Nilesh Javali <[email protected]>

commit f606ed93de0c4f1e7e3618779e9fad731455314a upstream.

The "update cache versions without reset" sysfs reset operation (0x20261) calls get_flash_version(), which reads hardware flash registers, without holding ha->optrom_mutex. The VPD update path serializes the same call under optrom_mutex, so this reset path can interleave its flash register accesses with a concurrent VPD or optrom flash operation and corrupt the reads.

Hold ha->optrom_mutex across the get_flash_version() call to match the VPD update path.

---
 drivers/scsi/qla2xxx/qla_attr.c |    2 ++
 1 file changed, 2 insertions(+)
drivers/scsi/qla2xxx/qla_attr.c +2 -0
@@ -816,7 +816,9 @@ qla2x00_sysfs_write_reset(struct file *f
 			    "Unable to allocate memory for VPD information update.\n");
 			return -ENOMEM;
 		}
+		mutex_lock(&ha->optrom_mutex);
 		ha->isp_ops->get_flash_version(vha, tmp_data);
+		mutex_unlock(&ha->optrom_mutex);
 		vfree(tmp_data);
 		break;
 	}
author maintainer
#334 lore raw

PATCH 334/583 scsi: qla2xxx: Fix cs84xx use-after-free on host teardown

In reply to Greg Kroah-Hartman

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

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

From: Nilesh Javali <[email protected]>

commit 33d102102d925357c5fd172dd6672a27d74b3215 upstream.

qla84xx_put_chip() drops the last reference to ha->cs84xx and frees it via __qla84xx_chip_release() without clearing ha->cs84xx. During teardown it ran before scsi_remove_host(), which is what removes the 84xx_fw_version host sysfs attribute. A concurrent read of that attribute in the window between the two calls executes qla24xx_84xx_fw_version_show(), which dereferences the freed ha->cs84xx, resulting in a use-after-free.

Move qla84xx_put_chip() to after scsi_remove_host() in both qla2x00_remove_one() and qla2x00_disable_board_on_pci_error(). Once scsi_remove_host() returns, the sysfs attribute is gone and kernfs has drained any in-flight show(), so no reader can touch cs84xx; the put still runs before the host and ha are freed.

Fixes fe1b806f4f71 ("[SCSI] qla2xxx: Refactor shutdown code so some functionality can be reused.")
---
 drivers/scsi/qla2xxx/qla_os.c |    8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)
drivers/scsi/qla2xxx/qla_os.c +4 -4
@@ -3933,8 +3933,6 @@ qla2x00_remove_one(struct pci_dev *pdev)
 
 	qla2x00_dfs_remove(base_vha);
 
-	qla84xx_put_chip(base_vha);
-
 	/* Disable timer */
 	if (base_vha->timer_active)
 		qla2x00_stop_timer(base_vha);
@@ -3959,6 +3957,8 @@ qla2x00_remove_one(struct pci_dev *pdev)
 
 	scsi_remove_host(base_vha->host);
 
+	qla84xx_put_chip(base_vha);
+
 	qla2x00_free_device(base_vha);
 
 	qla2x00_clear_drv_active(ha);
@@ -6830,8 +6830,6 @@ qla2x00_disable_board_on_pci_error(struc
 
 	qla2x00_dfs_remove(base_vha);
 
-	qla84xx_put_chip(base_vha);
-
 	if (base_vha->timer_active)
 		qla2x00_stop_timer(base_vha);
 
@@ -6849,6 +6847,8 @@ qla2x00_disable_board_on_pci_error(struc
 
 	scsi_remove_host(base_vha->host);
 
+	qla84xx_put_chip(base_vha);
+
 	base_vha->flags.init_done = 0;
 	qla25xx_delete_queues(base_vha);
 	qla2x00_free_fcports(base_vha);
author maintainer
#335 lore raw

PATCH 335/583 scsi: qla2xxx: Fix FCE trace use-after-free during firmware dump

In reply to Greg Kroah-Hartman

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

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

From: Nilesh Javali <[email protected]>

commit 53298efcbbb0f0438366d45cb7ed7e6d93dd5531 upstream.

qla2x00_free_fce_trace() freed and cleared ha->fce while holding only fce_mutex. The firmware-dump consumers qla27xx_fwdt_entry_t264() and qla25xx_copy_fce() read ha->fce (NULL check followed by a copy of the buffer) under hardware_lock and never take fce_mutex. A debugfs FCE disable could therefore free the DMA buffer between a dump's NULL check and its copy, resulting in a use-after-free.

Unpublish ha->fce under hardware_lock, then release the lock and free the DMA buffer (dma_free_coherent() may sleep). A concurrent dump either completes its check and copy with the buffer still valid, or observes ha->fce == NULL and skips it.

Fixes 841df27d619e ("scsi: qla2xxx: Move FCE Trace buffer allocation to user control")
---
 drivers/scsi/qla2xxx/qla_init.c |   20 ++++++++++++++++++--
 1 file changed, 18 insertions(+), 2 deletions(-)
drivers/scsi/qla2xxx/qla_init.c +18 -2
@@ -3764,11 +3764,27 @@ int qla2x00_alloc_fce_trace(scsi_qla_hos
 
 void qla2x00_free_fce_trace(struct qla_hw_data *ha)
 {
-	if (!ha->fce)
+	void *fce;
+	dma_addr_t fce_dma;
+	unsigned long flags;
+
+	/*
+	 * Unpublish ha->fce under hardware_lock so a firmware dump in
+	 * progress (which reads ha->fce under the same lock) cannot race
+	 * with the buffer being freed.
+	 */
+	spin_lock_irqsave(&ha->hardware_lock, flags);
+	if (!ha->fce) {
+		spin_unlock_irqrestore(&ha->hardware_lock, flags);
 		return;
-	dma_free_coherent(&ha->pdev->dev, FCE_SIZE, ha->fce, ha->fce_dma);
+	}
+	fce = ha->fce;
+	fce_dma = ha->fce_dma;
 	ha->fce = NULL;
 	ha->fce_dma = 0;
+	spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+	dma_free_coherent(&ha->pdev->dev, FCE_SIZE, fce, fce_dma);
 }
 
 static void
author maintainer
#336 lore raw

PATCH 336/583 scsi: qla2xxx: Zero mailbox struct in qla2x00_get_firmware_state()

In reply to Greg Kroah-Hartman

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

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

From: Nilesh Javali <[email protected]>

commit 9efaa782845b4d5fb3e01242be0d06ebc7428d8f upstream.

The mbx_cmd_t is allocated on the stack but left uninitialized. qla2x00_mailbox_command() has several early-return paths (PCI permanent failure, device failed, EEH busy, ISP abort pending, mailbox access timeout, purge mbox) that return without writing the input mailbox registers back into mcp->mb[]. qla2x00_get_firmware_state() then unconditionally copies mcp->mb[1..6] (and mb[12]) into the caller's states[] array regardless of the return value.

On such a failure the copied values are uninitialized kernel stack memory, which is then exposed to userspace via the fw_state and mpi_fw_state sysfs handlers. Zero the mailbox struct so a failed query yields deterministic zeroed state instead of leaking stack contents.

---
 drivers/scsi/qla2xxx/qla_mbx.c |    2 ++
 1 file changed, 2 insertions(+)
drivers/scsi/qla2xxx/qla_mbx.c +2 -0
@@ -2214,6 +2214,8 @@ qla2x00_get_firmware_state(scsi_qla_host
 	if (!ha->flags.fw_started)
 		return QLA_FUNCTION_FAILED;
 
+	memset(&mc, 0, sizeof(mc));
+
 	mcp->mb[0] = MBC_GET_FIRMWARE_STATE;
 	mcp->out_mb = MBX_0;
 	if (IS_FWI2_CAPABLE(vha->hw))
author maintainer
#337 lore raw

PATCH 337/583 scsi: qla2xxx: Fix FCE trace enable parsing in debugfs

In reply to Greg Kroah-Hartman

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

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

From: Nilesh Javali <[email protected]>

commit b7368687e3d11f51392d3c4774ec0263d5fbf31f upstream.

qla2x00_dfs_fce_write() called kstrtoul() with a NULL result pointer, so a successful parse would dereference NULL and oops. Worse, the int return value (0 on success, negative errno on failure) was assigned to the unsigned long enable flag, inverting the intended logic: a valid number was treated as "disable" while a parse failure enabled FCE.

Parse the value into enable and propagate parse errors to userspace.

Fixes 841df27d619e ("scsi: qla2xxx: Move FCE Trace buffer allocation to user control")
---
 drivers/scsi/qla2xxx/qla_dfs.c |    4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/scsi/qla2xxx/qla_dfs.c +3 -1
@@ -510,7 +510,9 @@ qla2x00_dfs_fce_write(struct file *file,
 		return PTR_ERR(buf);
 	}
 
-	enable = kstrtoul(buf, 0, 0);
+	rc = kstrtoul(buf, 0, &enable);
+	if (rc)
+		goto out_free;
 	rc = count;
 
 	mutex_lock(&ha->fce_mutex);
author maintainer
#338 lore raw

PATCH 338/583 scsi: qla2xxx: Dont query firmware state while chip is down

In reply to Greg Kroah-Hartman

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

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

From: Nilesh Javali <[email protected]>

commit e0cebe20dcffbed9c078fe30e2d18cd5046d9eff upstream.

qla2x00_fw_state_show() initializes rval to QLA_FUNCTION_FAILED and jumps to the out: label when the chip is down or EEH is busy. The out: block then re-issued qla2x00_get_firmware_state() because rval != QLA_SUCCESS, defeating the chip-down/EEH-busy guards and issuing a mailbox command (outside optrom_mutex) during ISP reset or PCI error recovery, which can hang the adapter. It also turned a normal in-lock mailbox failure into a second unsynchronized mailbox attempt.

Make the out: fallback only mark the firmware state as unknown. The mailbox is now issued at most once, inside optrom_mutex, and only when the chip is up and not EEH-busy.

---
 drivers/scsi/qla2xxx/qla_attr.c |    4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)
drivers/scsi/qla2xxx/qla_attr.c +1 -3
@@ -1662,10 +1662,8 @@ qla2x00_fw_state_show(struct device *dev
 	rval = qla2x00_get_firmware_state(vha, state);
 	mutex_unlock(&vha->hw->optrom_mutex);
 out:
-	if (rval != QLA_SUCCESS) {
+	if (rval != QLA_SUCCESS)
 		memset(state, -1, sizeof(state));
-		rval = qla2x00_get_firmware_state(vha, state);
-	}
 
 	return scnprintf(buf, PAGE_SIZE, "0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n",
 	    state[0], state[1], state[2], state[3], state[4], state[5]);
author maintainer
#339 lore raw

PATCH 339/583 scsi: qla2xxx: Reject non-SCSI SRB on status IOCB fast path

In reply to Greg Kroah-Hartman

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

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

From: Nilesh Javali <[email protected]>

commit 0f41d07d72f2245208c45374ca8d0a1846cad667 upstream.

qla2x00_status_entry() filters out non-TYPE_SRB entries and the SRB_NVME_CMD, SRB_BIDI_CMD and SRB_TM_CMD types, then falls through to a SCSI fast path that assumes the command is an SRB_SCSI_CMD. The first thing on that path, qla_chk_edif_rx_sa_delete_pending(), and the subsequent handling both evaluate GET_CMD_SP(sp), i.e. sp->u.scmd.cmd.

The srb u union overlays the SCSI command pointer with other command layouts (bsg_job, iocb_cmd). If firmware delivers an unexpected STATUS_TYPE IOCB for a non-SCSI handle, sp->u.scmd.cmd can read as a non-NULL garbage pointer, bypassing the NULL checks in qla_chk_edif_rx_sa_delete_pending() and at the cp == NULL test, and leading to a wild pointer dereference.

Reject any SRB whose type is not SRB_SCSI_CMD before entering the fast path. The outstanding_cmds slot is left untouched so a genuinely non-SCSI command still completes through its proper handler.

---
 drivers/scsi/qla2xxx/qla_dbg.c |    2 +-
 drivers/scsi/qla2xxx/qla_isr.c |    8 ++++++++
 2 files changed, 9 insertions(+), 1 deletion(-)
drivers/scsi/qla2xxx/qla_dbg.c +1 -1
@@ -16,7 +16,7 @@
  * |                              |                    | 0x2127-0x2128  |
  * | Queue Command and IO tracing |       0x3074       | 0x300b         |
  * |                              |                    | 0x3027-0x3028  |
- * |                              |                    | 0x303d-0x3041  |
+ * |                              |                    | 0x303e-0x3041  |
  * |                              |                    | 0x302e,0x3033  |
  * |                              |                    | 0x3036,0x3038  |
  * |                              |                    | 0x303a		|
drivers/scsi/qla2xxx/qla_isr.c +8 -0
@@ -3406,6 +3406,14 @@ qla2x00_status_entry(scsi_qla_host_t *vh
 		return;
 	}
 
+	/* Everything below is the SCSI fast path; reject other SRB types. */
+	if (sp->type != SRB_SCSI_CMD) {
+		ql_dbg(ql_dbg_io, vha, 0x303d,
+		    "Unexpected SRB type %x for status IOCB, sp %p.\n",
+		    sp->type, sp);
+		return;
+	}
+
 	/* Fast path completion. */
 	qla_chk_edif_rx_sa_delete_pending(vha, sp, sts24);
 	sp->qpair->cmd_completion_cnt++;
author maintainer
#340 lore raw

PATCH 340/583 scsi: qla2xxx: Fix response queue over-consumption in __qla_consume_iocb()

In reply to Greg Kroah-Hartman

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

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

From: Nilesh Javali <[email protected]>

commit 3ba019bdd89d931499d9476456b5d9c7ab7fa753 upstream.

qla24xx_process_response_queue() advances ring_ptr past the head IOCB before dispatching, so by the time __qla_consume_iocb() runs, ring_ptr already points at the first continuation IOCB. The function however looped purex->entry_count times starting at ring_ptr. As entry_count includes the head, this consumed one entry too many: it stamped RESPONSE_PROCESSED on the next, unrelated IOCB and advanced the ring past it, silently dropping a legitimate firmware response. The head IOCB's signature was also never marked.

Mark the head processed and account for it, then consume only the entry_count - 1 continuation IOCBs, matching __qla_copy_purex_to_buffer().

Fixes fac2807946c1 ("scsi: qla2xxx: edif: Add extraction of auth_els from the wire")
---
 drivers/scsi/qla2xxx/qla_isr.c |   11 +++++++++++
 1 file changed, 11 insertions(+)
drivers/scsi/qla2xxx/qla_isr.c +11 -0
@@ -205,6 +205,17 @@ void __qla_consume_iocb(struct scsi_qla_
 	struct purex_entry_24xx *purex = *pkt;
 
 	entry_count_remaining = purex->entry_count;
+
+	/*
+	 * The caller already advanced ring_ptr past the head IOCB, so mark
+	 * the head processed and account for it here, then consume only the
+	 * continuation IOCBs that follow.
+	 */
+	((response_t *)purex)->signature = RESPONSE_PROCESSED;
+	/* flush signature */
+	wmb();
+	--entry_count_remaining;
+
 	while (entry_count_remaining > 0) {
 		new_pkt = rsp_q->ring_ptr;
 		*pkt = new_pkt;
author maintainer
#341 lore raw

PATCH 341/583 scsi: qla2xxx: Quiesce response IRQ before freeing request queue

In reply to Greg Kroah-Hartman

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

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

From: Nilesh Javali <[email protected]>

commit 505753ec2594c6af09a601f0dd60be7d840c1d2d upstream.

qla2xxx_delete_qpair() deletes the request queue before the response queue. qla25xx_delete_req_que() frees the request queue memory (kfree(req) in qla25xx_free_req_que()), but the response-queue MSI-X is only released later, in qla25xx_free_rsp_que(). In that window the response interrupt can still fire, qla2xxx_msix_rsp_q() queues qpair->q_work, and qla_do_work() -> qla24xx_process_response_queue() dereferences the now-freed rsp->req (LOGINOUT/CT/ELS entries and the status path), a use-after-free.

The cancel_work_sync() added for the qpair teardown lives in the response free path, which runs after the request queue is already freed, so it does not protect rsp->req.

Release the response-queue interrupt and flush qpair->q_work before deleting the request queue, so no late completion can reach the freed request queue. Clearing have_irq makes the subsequent qla25xx_free_rsp_que() skip its free_irq(), and the firmware queue-delete order (request then response) is preserved; the request-delete mailbox completes on the default vector and is unaffected by dropping the qpair response interrupt early.

---
 drivers/scsi/qla2xxx/qla_init.c |   17 +++++++++++++++++
 1 file changed, 17 insertions(+)
drivers/scsi/qla2xxx/qla_init.c +17 -0
@@ -9849,11 +9849,28 @@ int qla2xxx_delete_qpair(struct scsi_qla
 {
 	int ret = QLA_FUNCTION_FAILED;
 	struct qla_hw_data *ha = qpair->hw;
+	struct rsp_que *rsp = qpair->rsp;
 
 	qpair->delete_in_progress = 1;
 
 	qla_free_buf_pool(qpair);
 
+	/*
+	 * The response-queue interrupt schedules qla_do_work(), which
+	 * dereferences qpair->rsp->req.  Release the interrupt and flush
+	 * any pending work before the request queue is freed below so a
+	 * late completion cannot touch the freed request queue.  The
+	 * firmware queue-delete order (request then response) is kept.
+	 */
+	if (rsp && rsp->msix && rsp->msix->have_irq) {
+		free_irq(rsp->msix->vector, rsp->msix->handle);
+		rsp->msix->have_irq = 0;
+		rsp->msix->in_use = 0;
+		rsp->msix->handle = NULL;
+	}
+	if (rsp && ha->wq)
+		cancel_work_sync(&qpair->q_work);
+
 	ret = qla25xx_delete_req_que(vha, qpair->req);
 	if (ret != QLA_SUCCESS)
 		goto fail;
author maintainer
#342 lore raw

PATCH 342/583 scsi: qla2xxx: Avoid double completion in async IOCB timeout

In reply to Greg Kroah-Hartman

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

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

From: Nilesh Javali <[email protected]>

commit bb45bc4bd53c95a7bf6f782577b5ede94c0f8aa8 upstream.

qla2x00_async_iocb_timeout() tries to abort a timed-out async IOCB. When qla24xx_async_abort_cmd() fails, both the SRB_LOGIN_CMD path and the SRB_CTRL_VP/default path scan outstanding_cmds[] for the SRB and then call sp->done(sp, QLA_FUNCTION_TIMEOUT) unconditionally, without checking whether the SRB was actually found and removed.

If the response ISR completes the same handle first, it removes the SRB under qp_lock_ptr and runs sp->done() -> complete(sp->comp). The submitter qla24xx_control_vp() wakes from wait_for_completion(), clears sp->comp, drops its reference and returns, reclaiming the on-stack completion. The timer reference keeps the SRB alive across the timeout handler, but not the submitter's stack. The timeout then issues a second sp->done() -> qla_ctrlvp_sp_done(), which evaluates "if (sp->comp) complete(sp->comp)"; with the pointer loaded before the submitter's NULL store, complete() writes into the freed stack frame, a use-after-free.

Track whether this path removed the SRB from outstanding_cmds and only call sp->done() when it did, so the command is completed exactly once by whichever path owns it. This mirrors the sp_found guard already used in qla24xx_abort_iocb_timeout().

Fixes f6145e86d21f ("scsi: qla2xxx: Fix race between switch cmd completion and timeout")
---
 drivers/scsi/qla2xxx/qla_init.c |   24 +++++++++++++++++++++---
 1 file changed, 21 insertions(+), 3 deletions(-)
drivers/scsi/qla2xxx/qla_init.c +21 -3
@@ -228,7 +228,7 @@ qla2x00_async_iocb_timeout(void *data)
 	srb_t *sp = data;
 	fc_port_t *fcport = sp->fcport;
 	struct srb_iocb *lio = &sp->u.iocb_cmd;
-	int rc, h;
+	int rc, h, found;
 	unsigned long flags;
 
 	if (fcport) {
@@ -251,6 +251,7 @@ qla2x00_async_iocb_timeout(void *data)
 			lio->u.logio.data[1] =
 				lio->u.logio.flags & SRB_LOGIN_RETRIED ?
 				QLA_LOGIO_LOGIN_RETRIED : 0;
+			found = 0;
 			spin_lock_irqsave(sp->qpair->qp_lock_ptr, flags);
 			for (h = 1; h < sp->qpair->req->num_outstanding_cmds;
 			    h++) {
@@ -258,11 +259,19 @@ qla2x00_async_iocb_timeout(void *data)
 				    sp) {
 					sp->qpair->req->outstanding_cmds[h] =
 					    NULL;
+					found = 1;
 					break;
 				}
 			}
 			spin_unlock_irqrestore(sp->qpair->qp_lock_ptr, flags);
-			sp->done(sp, QLA_FUNCTION_TIMEOUT);
+			/*
+			 * Only complete the command if this path removed it
+			 * from outstanding_cmds.  Otherwise the ISR already
+			 * completed it and a second sp->done() would race the
+			 * submitter's freeing of the on-stack completion.
+			 */
+			if (found)
+				sp->done(sp, QLA_FUNCTION_TIMEOUT);
 		}
 		break;
 	case SRB_LOGOUT_CMD:
@@ -275,6 +284,7 @@ qla2x00_async_iocb_timeout(void *data)
 	default:
 		rc = qla24xx_async_abort_cmd(sp, false);
 		if (rc) {
+			found = 0;
 			spin_lock_irqsave(sp->qpair->qp_lock_ptr, flags);
 			for (h = 1; h < sp->qpair->req->num_outstanding_cmds;
 			    h++) {
@@ -282,11 +292,19 @@ qla2x00_async_iocb_timeout(void *data)
 				    sp) {
 					sp->qpair->req->outstanding_cmds[h] =
 					    NULL;
+					found = 1;
 					break;
 				}
 			}
 			spin_unlock_irqrestore(sp->qpair->qp_lock_ptr, flags);
-			sp->done(sp, QLA_FUNCTION_TIMEOUT);
+			/*
+			 * Only complete the command if this path removed it
+			 * from outstanding_cmds.  Otherwise the ISR already
+			 * completed it and a second sp->done() would race the
+			 * submitter's freeing of the on-stack completion.
+			 */
+			if (found)
+				sp->done(sp, QLA_FUNCTION_TIMEOUT);
 		}
 		break;
 	}
author maintainer
#343 lore raw

PATCH 343/583 scsi: qla2xxx: Bound rsp_info_len to avoid OOB sense-data read

In reply to Greg Kroah-Hartman

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

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

From: Nilesh Javali <[email protected]>

commit ca6d880d6c70cb7946e7b3e05d7285f271b6d99e upstream.

In qla2x00_status_entry(), the FWI2 status path advances sense_data and shrinks par_sense_len by rsp_info_len:

	if (IS_FWI2_CAPABLE(ha)) {
		sense_data += rsp_info_len;
		par_sense_len -= rsp_info_len;
	}

rsp_info_len is a 32-bit value taken directly from the target's FCP response (sf.rsp_data_len), while par_sense_len is the IOCB data area size (28 bytes for 24xx, 60 bytes for 29xx). A hostile or buggy target reporting an rsp_info_len larger than par_sense_len makes the unsigned subtraction underflow to a huge value and advances sense_data out of bounds.

The underflowed par_sense_len then defeats the cap in qla2x00_handle_sense():

	if (sense_len > par_sense_len)
		sense_len = par_sense_len;
	memcpy(cp->sense_buffer, sense_data, sense_len);

so the memcpy reads up to SCSI_SENSE_BUFFERSIZE bytes from the out-of-bounds sense_data pointer, leaking adjacent response-ring/heap memory into the command's sense buffer.

Clamp rsp_info_len to par_sense_len before the subtraction so par_sense_len can never underflow and sense_data stays within the IOCB data area. The fix sits before the comp_status switch, covering both qla2x00_handle_sense() call sites.

---
 drivers/scsi/qla2xxx/qla_isr.c |   12 ++++++++++++
 1 file changed, 12 insertions(+)
drivers/scsi/qla2xxx/qla_isr.c +12 -0
@@ -3482,6 +3482,18 @@ qla2x00_status_entry(scsi_qla_host_t *vh
 	if (scsi_status & SS_RESPONSE_INFO_LEN_VALID) {
 		/* Sense data lies beyond any FCP RESPONSE data. */
 		if (IS_FWI2_CAPABLE(ha)) {
+			/*
+			 * A hostile or buggy target may report an
+			 * rsp_info_len larger than the IOCB data area.
+			 * Clamp it so the par_sense_len subtraction cannot
+			 * underflow and walk sense_data out of bounds.
+			 */
+			if (rsp_info_len > par_sense_len) {
+				ql_log(ql_log_warn, fcport->vha, 0x3107,
+				       "Truncating bogus rsp_info_len 0x%x to 0x%x.\n",
+				       rsp_info_len, par_sense_len);
+				rsp_info_len = par_sense_len;
+			}
 			sense_data += rsp_info_len;
 			par_sense_len -= rsp_info_len;
 		}
author maintainer
#344 lore raw

PATCH 344/583 scsi: qla2xxx: Avoid req_q_map double-read in qla2x00_error_entry()

In reply to Greg Kroah-Hartman

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

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

From: Nilesh Javali <[email protected]>

commit deb8abde83a799d2501f3977f6d6051000253f5e upstream.

qla2x00_error_entry() reads ha->req_q_map[que] twice: once for the NULL check and again when assigning it to req. The map slot is cleared by qla25xx_free_req_que() (ha->req_q_map[que_id] = NULL under mq_lock) during queue teardown, while the response-queue interrupt that drives qla2x00_error_entry() is still registered (the IRQ is released later in qla25xx_free_rsp_que()). If the slot is set to NULL between the two reads, req becomes NULL and is dereferenced.

Read the slot once into req and NULL-check the local before use. mq_lock is a mutex and cannot be taken from interrupt context, so the single read plus local check is the appropriate fix for the reported NULL dereference.

Fixes a6fe35c052c4 ("[SCSI] qla2xxx: Avoid invalid request queue dereference for bad response packets.")
---
 drivers/scsi/qla2xxx/qla_isr.c |    4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/scsi/qla2xxx/qla_isr.c +3 -1
@@ -3809,10 +3809,12 @@ qla2x00_error_entry(scsi_qla_host_t *vha
 	    "iocb type %xh with error status %xh, handle %xh, rspq id %d\n",
 	    pkt->entry_type, pkt->entry_status, pkt->handle, rsp->id);
 
-	if (que >= ha->max_req_queues || !ha->req_q_map[que])
+	if (que >= ha->max_req_queues)
 		goto fatal;
 
 	req = ha->req_q_map[que];
+	if (!req)
+		goto fatal;
 
 	if (pkt->entry_status & RF_BUSY)
 		res = DID_BUS_BUSY << 16;
author maintainer
#345 lore raw

PATCH 345/583 scsi: qla2xxx: Fix NVMe abort reference leak on repeated abort

In reply to Greg Kroah-Hartman

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

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

From: Nilesh Javali <[email protected]>

commit 06b5b2a5d499323f1c3256ead35798e8e3d15e60 upstream.

qla_nvme_ls_abort() and qla_nvme_fcp_abort() take a command reference with kref_get_unless_zero() and then call schedule_work() on priv->abort_work, ignoring its return value. qla_nvme_abort_work() runs once and drops exactly one reference via kref_put(&sp->cmd_kref, sp->put_fn).

Since the per-abort INIT_WORK() was moved to submission time, schedule_work() now returns false when the work is already pending, for example on a concurrent transport teardown and timeout-driven abort of the same command. In that case the reference taken for the second abort is never released because the work still executes only once, leaking a reference. The command is then never returned to the NVMe-FC transport, which can hang the port.

Drop the reference when schedule_work() returns false, so each kref_get_unless_zero() is balanced regardless of whether the work was newly queued. The held reference keeps priv->sp valid for the put.

Fixes 7e85f6dbc856 ("scsi: qla2xxx: Initialize NVMe abort_work once at submission")
---
 drivers/scsi/qla2xxx/qla_nvme.c |    6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)
drivers/scsi/qla2xxx/qla_nvme.c +4 -2
@@ -466,7 +466,8 @@ static void qla_nvme_ls_abort(struct nvm
 	}
 	spin_unlock_irqrestore(&priv->cmd_lock, flags);
 
-	schedule_work(&priv->abort_work);
+	if (!schedule_work(&priv->abort_work))
+		kref_put(&priv->sp->cmd_kref, priv->sp->put_fn);
 }
 
 static int qla_nvme_ls_req(struct nvme_fc_local_port *lport,
@@ -548,7 +549,8 @@ static void qla_nvme_fcp_abort(struct nv
 	}
 	spin_unlock_irqrestore(&priv->cmd_lock, flags);
 
-	schedule_work(&priv->abort_work);
+	if (!schedule_work(&priv->abort_work))
+		kref_put(&priv->sp->cmd_kref, priv->sp->put_fn);
 }
 
 static inline int qla2x00_start_nvme_mq(srb_t *sp)
author maintainer
#346 lore raw

PATCH 346/583 scsi: qla2xxx: Drop vport reference under lock in report ID acquisition

In reply to Greg Kroah-Hartman

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

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

From: Nilesh Javali <[email protected]>

commit 1154b16439ffc562f9461494c4508c63446eb684 upstream.

qla24xx_report_id_acquisition() format-1 handling takes the vport reference under vport_slock but drops it outside the lock, after setting vp->vp_flags and vp->dpc_flags:

	set_bit(VP_IDX_ACQUIRED, &vp->vp_flags);
	set_bit(REGISTER_FC4_NEEDED, &vp->dpc_flags);
	set_bit(REGISTER_FDMI_NEEDED, &vp->dpc_flags);

	atomic_dec(&vp->vref_count);

Neither set_bit() nor atomic_dec() imply a memory barrier, so on a weakly ordered architecture the decrement can become visible before the flag stores. qla24xx_deallocate_vp_id() polls vref_count under vport_slock and unlinks the vport once it reads zero, after which qla24xx_vport_delete() frees it via scsi_host_put(). The poller could therefore observe vref_count == 0 early and tear the vport down while the pending vp_flags/ dpc_flags stores land on freed memory.

Drop the reference under vport_slock, as is done for the matching increment and by every other vref_count user. The unlock release pairs with the deallocate poller's lock acquire so the flag stores are ordered before vref_count == 0 can be observed.

Fixes 793cedee296f ("scsi: qla2xxx: Hold vport reference in qla24xx_report_id_acquisition()")
---
 drivers/scsi/qla2xxx/qla_mbx.c |    2 ++
 1 file changed, 2 insertions(+)
drivers/scsi/qla2xxx/qla_mbx.c +2 -0
@@ -4167,7 +4167,9 @@ qla24xx_report_id_acquisition(scsi_qla_h
 			set_bit(REGISTER_FC4_NEEDED, &vp->dpc_flags);
 			set_bit(REGISTER_FDMI_NEEDED, &vp->dpc_flags);
 
+			spin_lock_irqsave(&ha->vport_slock, flags);
 			atomic_dec(&vp->vref_count);
+			spin_unlock_irqrestore(&ha->vport_slock, flags);
 		}
 		set_bit(VP_DPC_NEEDED, &vha->dpc_flags);
 		qla2xxx_wake_dpc(vha);
author maintainer
#347 lore raw

PATCH 347/583 scsi: qla2xxx: Hold vport_slock for host map update in report ID acquisition

In reply to Greg Kroah-Hartman

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

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

From: Nilesh Javali <[email protected]>

commit 7944039ba9cb5c3a935d17c91004e3b8649ff58e upstream.

qla24xx_report_id_acquisition() format-1 handling drops vport_slock after taking the vport reference and then calls qla_update_host_map() without the lock. That reaches qla_update_vp_map(), which mutates the ha->host_map btree via btree_insert32()/btree_update32()/btree_remove32() and is documented to require vport_slock to be held by the caller. Running it unlocked can race concurrent host_map updates and corrupt the btree.

The format-2 path in the same function already wraps its host_map update (SET_AL_PA) in vport_slock; the format-1 path is the lone outlier.

Hold vport_slock across the format-1 qla_update_host_map() call to honor the documented locking contract. The vref_count taken in the loop keeps the vport valid, so this only adds the missing host_map serialization.

---
 drivers/scsi/qla2xxx/qla_mbx.c |    2 ++
 1 file changed, 2 insertions(+)
drivers/scsi/qla2xxx/qla_mbx.c +2 -0
@@ -4157,7 +4157,9 @@ qla24xx_report_id_acquisition(scsi_qla_h
 			if (!found)
 				return;
 
+			spin_lock_irqsave(&ha->vport_slock, flags);
 			qla_update_host_map(vp, id);
+			spin_unlock_irqrestore(&ha->vport_slock, flags);
 
 			/*
 			 * Cannot configure here as we are still sitting on the
author maintainer
#348 lore raw

PATCH 348/583 scsi: qla2xxx: Use coherent DMA buffer for D_Port diagnostics

In reply to Greg Kroah-Hartman

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

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

From: Nilesh Javali <[email protected]>

commit 7c4f3f50d83af4545efaa99b3d0d46fb8d52031e upstream.

qla26xx_dport_diagnostics() streaming-maps the caller's result buffer with dma_map_single(). The bsg path passes &dd->buf from the __packed struct qla_dport_diag, where buf lands at a 2-byte offset and shares cachelines with the surrounding options/unused fields. Mapping such a misaligned sub-buffer violates the DMA API requirement that streaming buffers be cacheline aligned and not share a cacheline with other data, and can corrupt data on non-DMA-coherent architectures.

Allocate a dedicated DMA-coherent buffer inside qla26xx_dport_diagnostics() for the mailbox command and copy the result back into the caller's buffer. This removes the streaming map of the misaligned sub-buffer entirely; the caller's buffer is now only a plain CPU buffer, so its packing no longer matters.

---
 drivers/scsi/qla2xxx/qla_mbx.c |   15 +++++++--------
 1 file changed, 7 insertions(+), 8 deletions(-)
drivers/scsi/qla2xxx/qla_mbx.c +7 -8
@@ -6442,6 +6442,7 @@ qla26xx_dport_diagnostics(scsi_qla_host_
 	mbx_cmd_t mc;
 	mbx_cmd_t *mcp = &mc;
 	dma_addr_t dd_dma;
+	void *dd;
 
 	if (!IS_QLA83XX(vha->hw) && !IS_QLA27XX(vha->hw) &&
 	    !IS_QLA28XX(vha->hw))
@@ -6450,15 +6451,12 @@ qla26xx_dport_diagnostics(scsi_qla_host_
 	ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x119f,
 	    "Entered %s.\n", __func__);
 
-	dd_dma = dma_map_single(&vha->hw->pdev->dev,
-	    dd_buf, size, DMA_FROM_DEVICE);
-	if (dma_mapping_error(&vha->hw->pdev->dev, dd_dma)) {
-		ql_log(ql_log_warn, vha, 0x1194, "Failed to map dma buffer.\n");
+	dd = dma_alloc_coherent(&vha->hw->pdev->dev, size, &dd_dma, GFP_KERNEL);
+	if (!dd) {
+		ql_log(ql_log_warn, vha, 0x1194, "Failed to allocate dma buffer.\n");
 		return QLA_MEMORY_ALLOC_FAILED;
 	}
 
-	memset(dd_buf, 0, size);
-
 	mcp->mb[0] = MBC_DPORT_DIAGNOSTICS;
 	mcp->mb[1] = options;
 	mcp->mb[2] = MSW(LSD(dd_dma));
@@ -6480,8 +6478,9 @@ qla26xx_dport_diagnostics(scsi_qla_host_
 		    "Done %s.\n", __func__);
 	}
 
-	dma_unmap_single(&vha->hw->pdev->dev, dd_dma,
-	    size, DMA_FROM_DEVICE);
+	memcpy(dd_buf, dd, size);
+
+	dma_free_coherent(&vha->hw->pdev->dev, size, dd, dd_dma);
 
 	return rval;
 }
author maintainer
#349 lore raw

PATCH 349/583 scsi: qla2xxx: Zero-init bsg stack buffers to avoid info leak

In reply to Greg Kroah-Hartman

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

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

From: Nilesh Javali <[email protected]>

commit b93d3bb3afe1b44489927de1eb4e66e8536a5935 upstream.

Several bsg handlers stage their request/reply in an uninitialized 256-byte on-stack buffer (uint8_t bsg[DMA_POOL_SIZE]) and fill it via sg_copy_to_buffer(), which only copies as many bytes as the user-supplied request payload. When the request is shorter than the structure, the remainder of the buffer is left holding stale stack data.

qla2x00_read_fru_status() and qla2x00_read_i2c() then copy the full structure back to the reply payload with sg_copy_from_buffer(), leaking the uninitialized stack bytes to user space. The write/update paths do not copy the buffer back, but can feed uninitialized fields to the device.

Zero the stack buffer at declaration in all five handlers, mirroring the heap kzalloc() approach, so short requests can no longer expose stale memory.

Fixes 697a4bc69159 ("[SCSI] qla2xxx: Provide method for updating I2C attached VPD.")
Fixes 9ebb5d9c69f1 ("[SCSI] qla2xxx: Add I2C BSG interface.")
---
 drivers/scsi/qla2xxx/qla_bsg.c |   10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)
drivers/scsi/qla2xxx/qla_bsg.c +5 -5
@@ -1560,7 +1560,7 @@ qla2x00_update_fru_versions(struct bsg_j
 	scsi_qla_host_t *vha = shost_priv(host);
 	struct qla_hw_data *ha = vha->hw;
 	int rval = 0;
-	uint8_t bsg[DMA_POOL_SIZE];
+	uint8_t bsg[DMA_POOL_SIZE] = {};
 	struct qla_image_version_list *list = (void *)bsg;
 	struct qla_image_version *image;
 	uint32_t count;
@@ -1620,7 +1620,7 @@ qla2x00_read_fru_status(struct bsg_job *
 	scsi_qla_host_t *vha = shost_priv(host);
 	struct qla_hw_data *ha = vha->hw;
 	int rval = 0;
-	uint8_t bsg[DMA_POOL_SIZE];
+	uint8_t bsg[DMA_POOL_SIZE] = {};
 	struct qla_status_reg *sr = (void *)bsg;
 	dma_addr_t sfp_dma;
 	uint8_t *sfp = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
@@ -1671,7 +1671,7 @@ qla2x00_write_fru_status(struct bsg_job
 	scsi_qla_host_t *vha = shost_priv(host);
 	struct qla_hw_data *ha = vha->hw;
 	int rval = 0;
-	uint8_t bsg[DMA_POOL_SIZE];
+	uint8_t bsg[DMA_POOL_SIZE] = {};
 	struct qla_status_reg *sr = (void *)bsg;
 	dma_addr_t sfp_dma;
 	uint8_t *sfp = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
@@ -1718,7 +1718,7 @@ qla2x00_write_i2c(struct bsg_job *bsg_jo
 	scsi_qla_host_t *vha = shost_priv(host);
 	struct qla_hw_data *ha = vha->hw;
 	int rval = 0;
-	uint8_t bsg[DMA_POOL_SIZE];
+	uint8_t bsg[DMA_POOL_SIZE] = {};
 	struct qla_i2c_access *i2c = (void *)bsg;
 	dma_addr_t sfp_dma;
 	uint8_t *sfp = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
@@ -1770,7 +1770,7 @@ qla2x00_read_i2c(struct bsg_job *bsg_job
 	scsi_qla_host_t *vha = shost_priv(host);
 	struct qla_hw_data *ha = vha->hw;
 	int rval = 0;
-	uint8_t bsg[DMA_POOL_SIZE];
+	uint8_t bsg[DMA_POOL_SIZE] = {};
 	struct qla_i2c_access *i2c = (void *)bsg;
 	dma_addr_t sfp_dma;
 	uint8_t *sfp = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
author maintainer
#350 lore raw

PATCH 350/583 scsi: qla2xxx: Skip NVMe LS reject IOCB when FW not started

In reply to Greg Kroah-Hartman

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

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

From: Nilesh Javali <[email protected]>

commit f7e46ebffc5781aab3f1f5a5d4350addbb5833f4 upstream.

qla_nvme_xmt_ls_rsp() bails out to the out: label when firmware is not started (!ha->flags.fw_started), but the out: path unconditionally calls qla_nvme_ls_reject_iocb(), which ends in qla2x00_start_iocbs() and an unconditional doorbell write to the request queue in-pointer register. This rings the firmware doorbell and queues an IOCB that stopped or resetting firmware cannot consume, and touches MMIO during the reset/EEH window where fw_started is also clear.

Only emit the LS reject IOCB (and ring the doorbell) when fw_started is set; otherwise just clean up and return. The post-allocation failure cases (SRB alloc / qla2x00_start_sp() failure) run with firmware started and still send the reject. Apply the same guard to the reject emission in qla2xxx_process_purls_pkt().

Fixes 875386b98857 ("scsi: qla2xxx: Add Unsolicited LS Request and Response Support for NVMe")
---
 drivers/scsi/qla2xxx/qla_nvme.c |   19 +++++++++++++------
 1 file changed, 13 insertions(+), 6 deletions(-)
drivers/scsi/qla2xxx/qla_nvme.c +13 -6
@@ -441,9 +441,11 @@ out:
 	a.vp_idx = vha->vp_idx;
 	a.nport_handle = uctx->nport_handle;
 	a.xchg_address = uctx->exchange_address;
-	spin_lock_irqsave(ha->base_qpair->qp_lock_ptr, flags);
-	qla_nvme_ls_reject_iocb(vha, ha->base_qpair, &a, true);
-	spin_unlock_irqrestore(ha->base_qpair->qp_lock_ptr, flags);
+	if (ha->flags.fw_started) {
+		spin_lock_irqsave(ha->base_qpair->qp_lock_ptr, flags);
+		qla_nvme_ls_reject_iocb(vha, ha->base_qpair, &a, true);
+		spin_unlock_irqrestore(ha->base_qpair->qp_lock_ptr, flags);
+	}
 	kfree(uctx);
 	return rval;
 }
@@ -1205,9 +1207,14 @@ qla2xxx_process_purls_pkt(struct scsi_ql
 		a.vp_idx = vha->vp_idx;
 		a.nport_handle = uctx->nport_handle;
 		a.xchg_address = uctx->exchange_address;
-		spin_lock_irqsave(vha->hw->base_qpair->qp_lock_ptr, flags);
-		qla_nvme_ls_reject_iocb(vha, vha->hw->base_qpair, &a, true);
-		spin_unlock_irqrestore(vha->hw->base_qpair->qp_lock_ptr, flags);
+		if (vha->hw->flags.fw_started) {
+			spin_lock_irqsave(vha->hw->base_qpair->qp_lock_ptr,
+					  flags);
+			qla_nvme_ls_reject_iocb(vha, vha->hw->base_qpair, &a,
+						true);
+			spin_unlock_irqrestore(vha->hw->base_qpair->qp_lock_ptr,
+					       flags);
+		}
 		list_del(&uctx->elem);
 		kfree(uctx);
 	}
author maintainer
#351 lore raw

PATCH 351/583 f2fs: return symlink writeback errors

In reply to Greg Kroah-Hartman

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

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

From: Wenjie Qi <[email protected]>

commit a2c73a7a677afdaa8b16d775188f9ef5cfbfd8b2 upstream.

F2FS writes long symlink data with page_symlink() and then flushes the symlink mapping to reduce the chance of exposing a broken symlink.

That flush result is currently ignored. If the writeback fails, symlink() still returns success even though the symlink is not durable and the same operation can already surface -EIO through syncfs().

Return the writeback error to userspace and skip the dirsync flush once the symlink data flush has failed.

Fixes d0cae97cb600 ("f2fs: flush symlink path to avoid broken symlink after POR")
---
 fs/f2fs/namei.c |   11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)
fs/f2fs/namei.c +6 -5
@@ -681,15 +681,16 @@ err_out:
 	 * performance regression.
 	 */
 	if (!err) {
-		filemap_write_and_wait_range(inode->i_mapping, 0,
-							disk_link.len - 1);
+		err = filemap_write_and_wait_range(inode->i_mapping, 0,
+						   disk_link.len - 1);
 
-		if (IS_DIRSYNC(dir))
+		if (!err && IS_DIRSYNC(dir))
 			f2fs_sync_fs(sbi->sb, 1);
-	} else {
-		f2fs_unlink(dir, dentry);
 	}
 
+	if (err)
+		f2fs_unlink(dir, dentry);
+
 	f2fs_balance_fs(sbi, true);
 	goto out_free_encrypted_link;
 
author maintainer
#352 lore raw

PATCH 352/583 f2fs: reject overlapping move range after len expansion

In reply to Greg Kroah-Hartman

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

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

From: Hao-Qun Huang <[email protected]>

commit 28c1ef094e7c86977d9bf570dc0362fc54e36437 upstream.

F2FS_IOC_MOVE_RANGE treats a zero length as a request to move data from pos_in to EOF. However, the same-file overlap check runs before that expansion, so a request with len == 0 bypasses the overlap rejection added for same-file moves.

For example, with a four-block file, moving from block 0 to block 1 with len == 0 is accepted by the old check because pos_in + len is still pos_in at that point. The code then expands len to cover the rest of the file and calls __exchange_data_block() on overlapping source and destination ranges in the same inode, which is the data-corruption case the overlap check was meant to reject.

Move the overlap check after the source range has been validated and len == 0 has been expanded, so it sees the effective length. This is a no-op for non-zero len (the value is unchanged there) and keeps the existing early return for identical positions.

Fixes d95fd91c1ac1 ("f2fs: exclude special cases for f2fs_move_file_range")
Assisted-by Claude:claude-fable-5
---
 fs/f2fs/file.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
fs/f2fs/file.c +2 -2
@@ -3119,8 +3119,6 @@ static int f2fs_move_file_range(struct f
 	if (src == dst) {
 		if (pos_in == pos_out)
 			return 0;
-		if (pos_out > pos_in && pos_out < pos_in + len)
-			return -EINVAL;
 	}
 
 	inode_lock(src);
@@ -3146,6 +3144,8 @@ static int f2fs_move_file_range(struct f
 		goto out_unlock;
 	if (len == 0)
 		olen = len = src->i_size - pos_in;
+	if (src == dst && pos_out > pos_in && pos_out < pos_in + len)
+		goto out_unlock;
 	if (pos_in + len == src->i_size)
 		len = ALIGN(src->i_size, F2FS_BLKSIZE) - pos_in;
 	if (len == 0) {
author maintainer
#353 lore raw

PATCH 353/583 f2fs: only redirty pinned folios in redirty_blocks

In reply to Greg Kroah-Hartman

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

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

From: Wenjie Qi <[email protected]>

commit 85171332742e741ccd6f401c69b6e0d698119e72 upstream.

redirty_blocks() pins folios with read_cache_folio() and then walks the same range again with filemap_lock_folio() to redirty them and drop the references it took.

Commit 5951fee46bef ("f2fs: Use a folio in redirty_blocks()") changed the second pass to a do/while loop. If read_cache_folio() fails before anything is pinned, page_idx does not advance but the cleanup loop still runs once.

If readahead has already populated the failed folio in page cache, that extra iteration finds it and folio_put_refs(folio, 2) drops one reference too many. Later drop_caches or reclaim can then report "BUG: Bad page state".

Only redirty the range that was pinned successfully.

Fixes 5951fee46bef ("f2fs: Use a folio in redirty_blocks()")
Assisted-by Codex:gpt-5.5
---
 fs/f2fs/file.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
fs/f2fs/file.c +2 -2
@@ -4441,7 +4441,7 @@ static int redirty_blocks(struct inode *
 		page_idx = folio_next_index(folio);
 	} while (page_len < len);
 
-	do {
+	while (redirty_idx < page_idx) {
 		folio = filemap_lock_folio(mapping, redirty_idx);
 
 		/* It will never fail, when folio has pinned above */
@@ -4454,7 +4454,7 @@ static int redirty_blocks(struct inode *
 		redirty_idx = folio_next_index(folio);
 		folio_unlock(folio);
 		folio_put_refs(folio, 2);
-	} while (redirty_idx < page_idx);
+	}
 
 	return ret;
 }
author maintainer
#354 lore raw

PATCH 354/583 f2fs: fix to avoid move_range and defragment on device_alias file

In reply to Greg Kroah-Hartman

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

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

From: Chao Yu <[email protected]>

commit 1b4db09b7f10895dc1a0bc32704c7de05188fb1d upstream.

It's forbidden to migrate blocks of device alias file.

Fixes 128d333f0dff ("f2fs: introduce device aliasing file")
---
 fs/f2fs/file.c |    6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)
fs/f2fs/file.c +5 -1
@@ -3059,6 +3059,9 @@ static int f2fs_ioc_defragment(struct fi
 	if (f2fs_readonly(sbi->sb))
 		return -EROFS;
 
+	if (IS_DEVICE_ALIASING(inode))
+		return -EOPNOTSUPP;
+
 	if (copy_from_user(&range, (struct f2fs_defragment __user *)arg,
 							sizeof(range)))
 		return -EFAULT;
@@ -3110,7 +3113,8 @@ static int f2fs_move_file_range(struct f
 	if (!S_ISREG(src->i_mode) || !S_ISREG(dst->i_mode))
 		return -EINVAL;
 
-	if (IS_ENCRYPTED(src) || IS_ENCRYPTED(dst))
+	if (IS_ENCRYPTED(src) || IS_ENCRYPTED(dst) ||
+		IS_DEVICE_ALIASING(src) || IS_DEVICE_ALIASING(dst))
 		return -EOPNOTSUPP;
 
 	if (pos_out < 0 || pos_in < 0)
author maintainer
#355 lore raw

PATCH 355/583 f2fs: dirty directory inodes on mtime/ctime update

In reply to Greg Kroah-Hartman

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

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

From: Joanne Chang <[email protected]>

commit 9ec09d5f4b317a417c8655c14056f70cbe71eb6c upstream.

Xfstests generic/547 sometimes fail with mismatched directory metadata before and after a power failure. This happens because when a directory entry is added, renamed, or deleted, its mtime and ctime are updated and the inode is marked dirty via f2fs_mark_inode_dirty_sync(dir, sync=false). The sync=false flag means the dirty inode is not added to the global DIRTY_META list. Therefore, subsequent checkpoints skip flushing these updated directory blocks, causing directory timestamps to revert to stale values after a sudden power failure.

Address this by changing the dirtying parameter to sync=true during directory entry mutations and renames. This forces F2FS to immediately queue the updated directory blocks on the global DIRTY_META list, ensuring timestamps are committed to checkpoints.

Fixes 7c45729a4d6d ("f2fs: keep dirty inodes selectively for checkpoint")
---
 fs/f2fs/dir.c    |    6 +++---
 fs/f2fs/inline.c |    2 +-
 fs/f2fs/namei.c  |    6 +++---
 3 files changed, 7 insertions(+), 7 deletions(-)
fs/f2fs/dir.c +3 -3
@@ -453,7 +453,7 @@ void f2fs_set_link(struct inode *dir, st
 	folio_mark_dirty(folio);
 
 	inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir));
-	f2fs_mark_inode_dirty_sync(dir, false);
+	f2fs_mark_inode_dirty_sync(dir, true);
 	f2fs_folio_put(folio, true);
 }
 
@@ -608,7 +608,7 @@ void f2fs_update_parent_metadata(struct
 		clear_inode_flag(inode, FI_NEW_INODE);
 	}
 	inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir));
-	f2fs_mark_inode_dirty_sync(dir, false);
+	f2fs_mark_inode_dirty_sync(dir, true);
 
 	if (F2FS_I(dir)->i_current_depth != current_depth)
 		f2fs_i_depth_write(dir, current_depth);
@@ -920,7 +920,7 @@ void f2fs_delete_entry(struct f2fs_dir_e
 	f2fs_folio_put(folio, true);
 
 	inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir));
-	f2fs_mark_inode_dirty_sync(dir, false);
+	f2fs_mark_inode_dirty_sync(dir, true);
 
 	if (inode)
 		f2fs_drop_nlink(dir, inode);
fs/f2fs/inline.c +1 -1
@@ -720,7 +720,7 @@ void f2fs_delete_inline_entry(struct f2f
 	f2fs_folio_put(folio, true);
 
 	inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir));
-	f2fs_mark_inode_dirty_sync(dir, false);
+	f2fs_mark_inode_dirty_sync(dir, true);
 
 	if (inode)
 		f2fs_drop_nlink(dir, inode);
fs/f2fs/namei.c +3 -3
@@ -1047,7 +1047,7 @@ static int f2fs_rename(struct mnt_idmap
 	f2fs_up_write(&F2FS_I(old_inode)->i_sem);
 
 	inode_set_ctime_current(old_inode);
-	f2fs_mark_inode_dirty_sync(old_inode, false);
+	f2fs_mark_inode_dirty_sync(old_inode, true);
 
 	f2fs_delete_entry(old_entry, old_folio, old_dir, NULL);
 	old_folio = NULL;
@@ -1216,7 +1216,7 @@ static int f2fs_cross_rename(struct inod
 		f2fs_i_links_write(old_dir, old_nlink > 0);
 		f2fs_up_write(&F2FS_I(old_dir)->i_sem);
 	}
-	f2fs_mark_inode_dirty_sync(old_dir, false);
+	f2fs_mark_inode_dirty_sync(old_dir, true);
 
 	/* update directory entry info of new dir inode */
 	f2fs_set_link(new_dir, new_entry, new_folio, old_inode);
@@ -1235,7 +1235,7 @@ static int f2fs_cross_rename(struct inod
 		f2fs_i_links_write(new_dir, new_nlink > 0);
 		f2fs_up_write(&F2FS_I(new_dir)->i_sem);
 	}
-	f2fs_mark_inode_dirty_sync(new_dir, false);
+	f2fs_mark_inode_dirty_sync(new_dir, true);
 
 	if (F2FS_OPTION(sbi).fsync_mode == FSYNC_MODE_STRICT) {
 		f2fs_add_ino_entry(sbi, old_dir->i_ino, TRANS_DIR_INO);
author maintainer
#356 lore raw

PATCH 356/583 f2fs: use the mount idmap for the owner check in f2fs_xattr_advise_set()

In reply to Greg Kroah-Hartman

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

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

From: Zhan Xusheng <[email protected]>

commit a54ffce4637acb0db8e695188a6c7f99f14c3576 upstream.

f2fs_xattr_advise_set() calls inode_owner_or_capable() with &nop_mnt_idmap before allowing the "system.advise" xattr to be set, instead of the idmap that the VFS passes to the ->set() handler.

f2fs supports idmapped mounts, so on such a mount this checks the caller's fsuid against the unmapped on-disk owner rather than the mapped owner: the actual owner can be wrongly denied with -EPERM and an unrelated caller wrongly allowed. Pass the handler's idmap instead.

Fixes 01beba7957a2 ("fs: port inode_owner_or_capable() to mnt_idmap")
Acked-by Christian Brauner (Amutable) <braurg>
---
 fs/f2fs/xattr.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
fs/f2fs/xattr.c +1 -1
@@ -118,7 +118,7 @@ static int f2fs_xattr_advise_set(const s
 	unsigned char old_advise = F2FS_I(inode)->i_advise;
 	unsigned char new_advise;
 
-	if (!inode_owner_or_capable(&nop_mnt_idmap, inode))
+	if (!inode_owner_or_capable(idmap, inode))
 		return -EPERM;
 	if (value == NULL)
 		return -EINVAL;
author maintainer
#357 lore raw

PATCH 357/583 f2fs: return writeback error from collapse range

In reply to Greg Kroah-Hartman

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

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

From: Wenjie Qi <[email protected]>

commit f8a4108800254d6f7b2755515fbbd9d0caac561e upstream.

f2fs_collapse_range() writes back pages moved by f2fs_do_collapse(), but ignores the return value. If writeback fails, the ioctl can still truncate page cache, shrink blocks, and report success.

Return the error before truncating page cache or updating the file size.

Fixes b4ace3370324 ("f2fs: support FALLOC_FL_COLLAPSE_RANGE")
Assisted-by Codex:gpt-5.5
---
 fs/f2fs/file.c |    5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)
fs/f2fs/file.c +4 -1
@@ -1599,11 +1599,14 @@ static int f2fs_collapse_range(struct in
 
 	/* write out all moved pages, if possible */
 	filemap_invalidate_lock(inode->i_mapping);
-	filemap_write_and_wait_range(inode->i_mapping, offset, LLONG_MAX);
+	ret = filemap_write_and_wait_range(inode->i_mapping, offset, LLONG_MAX);
+	if (ret)
+		goto out_unlock;
 	truncate_pagecache(inode, offset);
 
 	new_size = i_size_read(inode) - len;
 	ret = f2fs_truncate_blocks(inode, new_size, true);
+out_unlock:
 	filemap_invalidate_unlock(inode->i_mapping);
 	if (!ret)
 		f2fs_i_size_write(inode, new_size);
author maintainer
#358 lore raw

PATCH 358/583 f2fs: fix to avoid potential section-unaligned pinfile

In reply to Greg Kroah-Hartman

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

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

From: Chao Yu <[email protected]>

commit d0a481fad5c7a3a56ecf54a099651216869f4d0a upstream.

Blocks of pinfile may not aligned to section size due to wrong use on pinfile, result in heavy overhead of GC, let avoid this by adding additional check condition in f2fs_setattr().

- truncate -s 8mb pinfile
: random checkpoint may persist filesize w/ inode
- fallocate -o 0 -l 8mb pinfile
- f2fs_fallocate
- f2fs_expand_inode_data
- f2fs_allocate_pinning_section
- f2fs_map_blocks
- f2fs_map_lock
- __allocate_data_block
- file_need_truncate
: w/ FADVISE_TRUNC_BIT, we can expect unaligned mapping can be
truncated while open() if f2fs is not umount abnormally
- f2fs_map_unlock
: following f2fs checkpoint and sudden power-cut

- mount
- open pinfile
- f2fs_file_open
- finish_preallocate_blocks
- truncate_setsize
: filesize is 8mb
- f2fs_truncate
: can only truncate block outside filesize, rather than truncating
unaligned blocks inside filesize

Fixes f5a53edcf01e ("f2fs: support aligned pinned file")
Cc Daeho Jeong <[email protected]>
---
 fs/f2fs/file.c |   28 +++++++++++++++++-----------
 1 file changed, 17 insertions(+), 11 deletions(-)
fs/f2fs/file.c +17 -11
@@ -1094,17 +1094,23 @@ int f2fs_setattr(struct mnt_idmap *idmap
 			!IS_ALIGNED(attr->ia_size,
 			F2FS_BLK_TO_BYTES(fi->i_cluster_size)))
 			return -EINVAL;
-		/*
-		 * To prevent scattered pin block generation, we don't allow
-		 * smaller/equal size unaligned truncation for pinned file.
-		 * We only support overwrite IO to pinned file, so don't
-		 * care about larger size truncation.
-		 */
-		if (f2fs_is_pinned_file(inode) &&
-			attr->ia_size <= i_size_read(inode) &&
-			!IS_ALIGNED(attr->ia_size,
-			F2FS_BLK_TO_BYTES(CAP_BLKS_PER_SEC(sbi))))
-			return -EINVAL;
+
+		if (f2fs_is_pinned_file(inode)) {
+			/*
+			 * It may break section-aligned fallocate recovery
+			 * mechanism, so do not allow larger size truncation.
+			 */
+			if (attr->ia_size > i_size_read(inode))
+				return -EINVAL;
+			/*
+			 * To prevent scattered pin block generation, we don't
+			 * allow smaller/equal size unaligned truncation for
+			 * pinned file.
+			 */
+			else if (!IS_ALIGNED(attr->ia_size,
+				F2FS_BLK_TO_BYTES(CAP_BLKS_PER_SEC(sbi))))
+				return -EINVAL;
+		}
 	}
 
 	if (is_quota_modification(idmap, inode, attr)) {
author maintainer
#359 lore raw

PATCH 359/583 f2fs: fix dentry folio leak in find_in_level

In reply to Greg Kroah-Hartman

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

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

From: Guanghui Yang <[email protected]>

commit cca7d3e30bf30333314e31bc70b9a739f1342167 upstream.

find_in_level() gets a dentry folio with f2fs_find_data_folio() before calling find_in_block(). If find_in_block() returns an error, the function stores the error in res_folio and breaks out of the loop without dropping the dentry folio.

This leaks the folio reference on the find_in_block() error path. Drop the dentry folio before returning the error to the caller.

Fixes 7ad08a58bf67 ("f2fs: Handle casefolding with Encryption")
---
 fs/f2fs/dir.c |    1 +
 1 file changed, 1 insertion(+)
fs/f2fs/dir.c +1 -0
@@ -313,6 +313,7 @@ start_find_bucket:
 
 		de = find_in_block(dir, dentry_folio, fname, &max_slots, use_hash);
 		if (IS_ERR(de)) {
+			f2fs_folio_put(dentry_folio, false);
 			*res_folio = ERR_CAST(de);
 			de = NULL;
 			break;
author maintainer
#360 lore raw

PATCH 360/583 f2fs: avoid NULL checkpoint thread access in sysfs

In reply to Greg Kroah-Hartman

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

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

From: Wenjie Qi <[email protected]>

commit 5cb33b00c8fbb6e8f1fa3d281c3036d5f7c7c41f upstream.

checkpoint_merge can be enabled even when no checkpoint merge thread is running. A read-only mount is one case: f2fs does not start f2fs_issue_ckpt there, but ckpt_thread_ioprio is still writable through sysfs.

The ckpt_thread_ioprio store path updates the saved ioprio value and, when checkpoint_merge is enabled, calls set_task_ioprio() for the checkpoint thread. If cprc->f2fs_issue_ckpt is NULL, that dereferences a NULL task pointer.

Protect ckpt_thread_ioprio sysfs writes with s_umount as well, so the checkpoint thread cannot disappear under the store path while updating its ioprio.

Fixes e65920661708 ("f2fs: add ckpt_thread_ioprio sysfs node")
---
 fs/f2fs/sysfs.c |    7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)
fs/f2fs/sysfs.c +4 -3
@@ -547,7 +547,7 @@ out:
 			return -EINVAL;
 
 		cprc->ckpt_thread_ioprio = IOPRIO_PRIO_VALUE(class, level);
-		if (test_opt(sbi, MERGE_CHECKPOINT)) {
+		if (cprc->f2fs_issue_ckpt) {
 			ret = set_task_ioprio(cprc->f2fs_issue_ckpt,
 					cprc->ckpt_thread_ioprio);
 			if (ret)
@@ -962,13 +962,14 @@ static ssize_t f2fs_sbi_store(struct f2f
 	ssize_t ret;
 	bool gc_entry = (!strcmp(a->attr.name, "gc_urgent") ||
 					a->struct_type == GC_THREAD);
+	bool thread_entry = !strcmp(a->attr.name, "ckpt_thread_ioprio");
 
-	if (gc_entry) {
+	if (gc_entry || thread_entry) {
 		if (!down_read_trylock(&sbi->sb->s_umount))
 			return -EAGAIN;
 	}
 	ret = __sbi_store(a, sbi, buf, count);
-	if (gc_entry)
+	if (gc_entry || thread_entry)
 		up_read(&sbi->sb->s_umount);
 
 	return ret;
author maintainer
#361 lore raw

PATCH 361/583 f2fs: fix to migrate all curseg types during free_segment_range

In reply to Greg Kroah-Hartman

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

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

From: Daeho Jeong <[email protected]>

commit 8ec06f50ddd8d201bd7e55b896ae28ed9d4cb7d1 upstream.

In free_segment_range(), the curseg evacuation loop only iterates up to NR_CURSEG_PERSIST_TYPE (0..5), missing non-persistent in-memory curseg types such as CURSEG_COLD_DATA_PINNED and CURSEG_ALL_DATA_ATGC.

Even though these in-memory curseg types are not saved in the on-disk checkpoint header, they still occupy active physical segments at runtime. If an active in-memory curseg happens to be allocated within the segment range being truncated during filesystem shrink, failing to evacuate it will cause subsequent writes to the curseg attempting out-of-bounds I/O on the truncated storage range.

Fix this by expanding the curseg evacuation loop upper bound to NR_CURSEG_TYPE to ensure all active curseg types are safely migrated out of the target range.

Fixes d0b9e42ab615 ("f2fs: introduce inmem curseg")
---
 fs/f2fs/gc.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
fs/f2fs/gc.c +1 -1
@@ -2192,7 +2192,7 @@ static int free_segment_range(struct f2f
 	mutex_unlock(&DIRTY_I(sbi)->seglist_lock);
 
 	/* Move out cursegs from the target range */
-	for (type = CURSEG_HOT_DATA; type < NR_CURSEG_PERSIST_TYPE; type++) {
+	for (type = CURSEG_HOT_DATA; type < NR_CURSEG_TYPE; type++) {
 		err = f2fs_allocate_segment_for_resize(sbi, type, start, end);
 		if (err)
 			goto out;
author maintainer
#362 lore raw

PATCH 362/583 f2fs: fix to avoid potential deadloop in f2fs_fsync_node_pages()

In reply to Greg Kroah-Hartman

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

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

From: Chao Yu <[email protected]>

commit ce366bfa821ec81dd45bde547ee31e659306cc61 upstream.

There is potential deadloop in race condition:

Thread A Thread B
- fsync
- f2fs_do_sync_file
- f2fs_fsync_node_pages
- last_fsync_dnode
- folio_get(last_folio)
- f2fs_setattr
- f2fs_truncate
- f2fs_truncate_blocks
- f2fs_do_truncate_blocks
- f2fs_truncate_inode_blocks
- truncate_dnode
- truncate_node
- invalidate_mapping_pages
- folio->mapping = NULL
- is_node_folio alwasy return false
- atomic && !marked is always true,
then goto retry

Fixes 608514deba38 ("f2fs: set fsync mark only for the last dnode")
---
 fs/f2fs/node.c |    5 +++++
 1 file changed, 5 insertions(+)
fs/f2fs/node.c +5 -0
@@ -1986,6 +1986,11 @@ continue_unlock:
 		f2fs_debug(sbi, "Retry to write fsync mark: ino=%u, idx=%lx",
 			   ino, last_folio->index);
 		folio_lock(last_folio);
+		if (unlikely(!is_node_folio(last_folio))) {
+			f2fs_folio_put(last_folio, true);
+			ret = -EAGAIN;
+			goto out;
+		}
 		f2fs_folio_wait_writeback(last_folio, NODE, true, true);
 		folio_mark_dirty(last_folio);
 		folio_unlock(last_folio);
author maintainer
#363 lore raw

PATCH 363/583 f2fs: fix i_size when pinned fallocate partially fails

In reply to Greg Kroah-Hartman

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

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

From: Zhan Xusheng <[email protected]>

commit 0f448bb3767ef6119f5cdeabcae3f10d6e75aed6 upstream.

From: Zhan Xusheng <[email protected]>

Commit 4275b59673eb ("f2fs: fix to round down start offset of fallocate for pin file") moved the allocation loop's start down to a section boundary, but the error path still converts @expanded against @pg_start, which holds the unrounded start.

@pg_start exists for that conversion: commit 88f2cfc5fa90 ("f2fs: fix to update last i_size if fallocate partially succeeds") added it as an immutable base because map.m_lblk moves every round. Each round now maps exactly sec_blks blocks starting from rounddown(pg_start, sec_blks), so pg_start + expanded overshoots the last allocated block by pg_start % sec_blks, and a partial failure leaves i_size covering a tail that was never allocated. Nothing corrects that afterwards either, since file_dont_truncate() has already cleared FADVISE_TRUNC_BIT.

It needs a start offset that is not section aligned plus a fallocate that hits ENOSPC partway, so the error path runs with expanded > 0. On an 80 MiB image with 2 MiB sections:

truncate -s 80M img
mkfs.f2fs -s 1 -f img
mount -o loop img /mnt
touch /mnt/pinned
f2fs_io pinfile set /mnt/pinned
# 2093056 = block 511, so pg_start % sec_blks = 511 f2fs_io fallocate 0 2093056 536870912 /mnt/pinned stat -c %s /mnt/pinned
filefrag -v /mnt/pinned

The last extent ends at block 10737 either way. Before, i_size is 46075904, block 11249, so 511 blocks of it were never allocated, and filefrag does not mark the last extent eof. After, i_size is 43982848, block 10738, and eof is back. A kernel from before that commit also shows no overshoot.

Keep @pg_start pointing at where allocation actually begins.

Fixes 4275b59673eb ("f2fs: fix to round down start offset of fallocate for pin file")
---
 fs/f2fs/file.c |    5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)
fs/f2fs/file.c +3 -2
@@ -1907,8 +1907,9 @@ static int f2fs_expand_inode_data(struct
 		block_t sec_len;
 
 		if (map.m_lblk % sec_blks) {
-			map.m_lblk = rounddown(map.m_lblk, sec_blks);
-			map.m_len = pg_end - map.m_lblk;
+			pg_start = rounddown(map.m_lblk, sec_blks);
+			map.m_lblk = pg_start;
+			map.m_len = pg_end - pg_start;
 			if (off_end)
 				map.m_len++;
 		}
author maintainer
#364 lore raw

PATCH 364/583 f2fs: fix to off-by-one issue in f2fs_zero_post_eof_page()

In reply to Greg Kroah-Hartman

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

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

From: Chao Yu <[email protected]>

commit b2205d3cfd6c76fd5c5443ee9fdb498cfb0e1c66 upstream.

Otherwise, it will drop one more page after new_size which is not necessary.

Fixes ba8dac350faf ("f2fs: fix to zero post-eof page")
---
 fs/f2fs/file.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
fs/f2fs/file.c +1 -1
@@ -49,7 +49,7 @@ static void f2fs_zero_post_eof_page(stru
 	if (lock)
 		filemap_invalidate_lock(inode->i_mapping);
 	/* zero or drop pages only in range of [old_size, new_size] */
-	truncate_inode_pages_range(inode->i_mapping, old_size, new_size);
+	truncate_inode_pages_range(inode->i_mapping, old_size, new_size - 1);
 	if (lock)
 		filemap_invalidate_unlock(inode->i_mapping);
 }
author maintainer
#365 lore raw

PATCH 365/583 f2fs: fix to clear dirty flag on folio in error path

In reply to Greg Kroah-Hartman

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

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

From: Chao Yu <[email protected]>

commit 5b86eab84ac8e9289b5afc52ef88ab18ba5bacab upstream.

If node block is corrupted due to chksum mismatch or inconsistent footer info, it needs to drop clear flag of node folio, in order to persist inconsistent node data to storage.

Fixes b42b179bda9f ("f2fs: fix to do checksum even if inode page is uptodate")
---
 fs/f2fs/node.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
fs/f2fs/node.c +1 -1
@@ -1596,7 +1596,7 @@ page_hit:
 	if (!err)
 		return folio;
 out_err:
-	folio_clear_uptodate(folio);
+	clear_node_folio_dirty(folio);
 out_put_err:
 	/* ENOENT comes from read_node_folio which is not an error. */
 	if (err != -ENOENT)
author maintainer
#366 lore raw

PATCH 366/583 f2fs: fix valid block count leak on data block allocation failure

In reply to Greg Kroah-Hartman

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

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

From: Chen Changcheng <[email protected]>

commit 0f9af07ecc1ab486038373db6ae0436c5d674b19 upstream.

In __allocate_data_block(), when allocating a new data block (dn->data_blkaddr == NULL_ADDR), inc_valid_block_count() is called first to increment total_valid_block_count and i_blocks. If the subsequent f2fs_allocate_data_block() fails, the function returns the error directly without rolling back the already-incremented block counts, causing a permanent leak.

Fix this by calling dec_valid_block_count() to undo the increment before returning the error. The condition old_blkaddr == NULL_ADDR precisely identifies the case where inc_valid_block_count() was called.

Fixes 7d009e048d7c ("f2fs: fix to handle segment allocation failure correctly")
---
 fs/f2fs/data.c |    5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)
fs/f2fs/data.c +4 -1
@@ -1453,8 +1453,11 @@ static int __allocate_data_block(struct
 	old_blkaddr = dn->data_blkaddr;
 	err = f2fs_allocate_data_block(sbi, NULL, old_blkaddr,
 				&dn->data_blkaddr, &sum, seg_type, NULL);
-	if (err)
+	if (err) {
+		if (old_blkaddr == NULL_ADDR)
+			dec_valid_block_count(sbi, dn->inode, count);
 		return err;
+	}
 
 	if (GET_SEGNO(sbi, old_blkaddr) != NULL_SEGNO)
 		f2fs_invalidate_internal_cache(sbi, old_blkaddr, 1);
author maintainer
#367 lore raw

PATCH 367/583 f2fs: fix to pass folio->index to f2fs_sanity_check_node_footer()

In reply to Greg Kroah-Hartman

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

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

From: Chao Yu <[email protected]>

commit 7e188e9f9437ab47c3237d609f1b26348d6fea1a upstream.

Otherwise in f2fs_sanity_check_node_footer(), it will check the same nid incorrectly.

Fixes 0a736109c9d2 ("f2fs: fix to do sanity check on node footer in __write_node_folio()")
---
 fs/f2fs/node.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
fs/f2fs/node.c +1 -1
@@ -1767,7 +1767,7 @@ static bool __write_node_folio(struct fo
 	/* get old block addr of this node page */
 	nid = nid_of_node(folio);
 
-	if (f2fs_sanity_check_node_footer(sbi, folio, nid,
+	if (f2fs_sanity_check_node_footer(sbi, folio, folio->index,
 					NODE_TYPE_REGULAR, false)) {
 		f2fs_handle_critical_error(sbi, STOP_CP_REASON_CORRUPTED_NID);
 		goto redirty_out;
author maintainer
#368 lore raw

PATCH 368/583 f2fs: fix to zero post-EOF data when extending file size

In reply to Greg Kroah-Hartman

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

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

From: Chao Yu <[email protected]>

commit 5eced87b7d19dbc76ebdddaf322046f9ac582fcb upstream.

generic/794 4s ... - output mismatch (see /share/git/fstests/results//generic/794.out.bad)

#    --- tests/generic/794.out   2026-06-12 08:46:32.766426241 +0800
#    +++ /share/git/fstests/results//generic/794.out.bad 2026-07-05 18:32:55.000000000 +0800
#    @@ -1,4 +1,16 @@
#     QA output created by 794
#     append_write
#    +FAIL: non-zero data in gap [4080,4096) after shutdown+remount
#    +000000 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a  >ZZZZZZZZZZZZZZZZ<
#    +*
#    +001000

# truncate_up
# ...
# (Run 'diff -u /share/git/fstests/tests/generic/794.out /share/git/fstests/results//generic/794.out.bad' to see the entire diff)
Ran: generic/794
Failures: generic/794
Failed 1 of 1 tests

Steps of generic/794:
1. write 4096 bytes to file w/ 0x5a
2. use fiemap to get PBA of first block in file
3. truncate file to 4080
4. umount; write 4096 bytes to file w/ 0x5a directly via PBA; mount
5. extend filesize via
a) append 4096 from offset 4096, or
b) truncate 8192, or
c) fallocate 4096 from offset 4096
6. verify the gap is zeroed in memory [4080,4096)
7. sync range 4096 from offset 4096; shutdown -f (flush meta before shutdown)
8. umount; mount; verify [4080,4096) is zeroed or not.

When extending file size (e.g. via truncate, fallocate, or write) across an unaligned EOF boundary, we need to ensure that post-EOF data in the partial page is zeroed out in pagecache and marked dirty, then writeback the cache to persist zeroed data before committing inode w/ updated i_size.

This help to prevent stale disk data beyond the previous EOF from being exposed after remounting or crash recovery.

Since f2fs is a LFS filesystem, we only support direct write via PBA in pinfile, and pinfile has section-aligned filesize, so in Android, there should no problem, but for other usage in different environment, let's fix this w/ fsync_mode=strict mount option.

---
 fs/f2fs/file.c |  100 +++++++++++++++++++++++++++++++++++++++++++++++----------
 1 file changed, 84 insertions(+), 16 deletions(-)
fs/f2fs/file.c +84 -16
@@ -35,16 +35,52 @@
 #include <trace/events/f2fs.h>
 #include <uapi/linux/f2fs.h>
 
-static void f2fs_zero_post_eof_page(struct inode *inode,
-					loff_t new_size, bool lock)
+static int fill_zero(struct inode *inode, pgoff_t index,
+					loff_t start, loff_t len);
+
+static int do_zero_post_eof_page(struct inode *inode, loff_t new_size)
+{
+	loff_t old_size = i_size_read(inode);
+	unsigned int offset, len;
+	pgoff_t index;
+	int err;
+
+	offset = old_size & (PAGE_SIZE - 1);
+
+	if (!offset)
+		return 0;
+
+	len = min_t(loff_t, PAGE_SIZE - offset, new_size - old_size);
+	index = old_size >> PAGE_SHIFT;
+
+	if (f2fs_has_inline_data(inode)) {
+		/* data post eof should be always zero */
+		if (new_size <= MAX_INLINE_DATA(inode))
+			return 0;
+		err = f2fs_convert_inline_inode(inode);
+		if (err)
+			return err;
+	}
+
+	err = fill_zero(inode, index, offset, len);
+	if (err)
+		return err;
+	return filemap_write_and_wait_range(inode->i_mapping,
+				old_size, old_size + len - 1);
+}
+
+static int f2fs_zero_post_eof_page(struct inode *inode,
+					loff_t new_size, bool lock, bool writeback)
 {
 	loff_t old_size = i_size_read(inode);
+	bool strict =
+		F2FS_OPTION(F2FS_I_SB(inode)).fsync_mode == FSYNC_MODE_STRICT;
 
 	if (old_size >= new_size)
-		return;
+		return 0;
 
-	if (mapping_empty(inode->i_mapping))
-		return;
+	if (!strict && mapping_empty(inode->i_mapping))
+		return 0;
 
 	if (lock)
 		filemap_invalidate_lock(inode->i_mapping);
@@ -52,6 +88,16 @@ static void f2fs_zero_post_eof_page(stru
 	truncate_inode_pages_range(inode->i_mapping, old_size, new_size - 1);
 	if (lock)
 		filemap_invalidate_unlock(inode->i_mapping);
+
+	if (!writeback || !strict)
+		return 0;
+	/*
+	 * In fsync_mode=strict, when we expand an unaligned EOF size, we
+	 * should zero post EOF data and writeback the data immediately,
+	 * so that it can avoid exposing stale data after metadata flush
+	 * and POR.
+	 */
+	return do_zero_post_eof_page(inode, new_size);
 }
 
 static vm_fault_t f2fs_filemap_fault(struct vm_fault *vmf)
@@ -122,7 +168,10 @@ static vm_fault_t f2fs_vm_page_mkwrite(s
 
 	f2fs_bug_on(sbi, f2fs_has_inline_data(inode));
 
-	f2fs_zero_post_eof_page(inode, (folio->index + 1) << PAGE_SHIFT, true);
+	err = f2fs_zero_post_eof_page(inode,
+		(folio->index + 1) << PAGE_SHIFT, true, false);
+	if (err)
+		goto out_pagefault;
 
 	file_update_time(vmf->vma->vm_file);
 	filemap_invalidate_lock_shared(inode->i_mapping);
@@ -179,7 +228,7 @@ static vm_fault_t f2fs_vm_page_mkwrite(s
 
 out_sem:
 	filemap_invalidate_unlock_shared(inode->i_mapping);
-
+out_pagefault:
 	sb_end_pagefault(inode->i_sb);
 out:
 	ret = vmf_fs_error(err);
@@ -1160,8 +1209,12 @@ int f2fs_setattr(struct mnt_idmap *idmap
 		f2fs_down_write(&fi->i_gc_rwsem[WRITE]);
 		filemap_invalidate_lock(inode->i_mapping);
 
-		if (attr->ia_size > old_size)
-			f2fs_zero_post_eof_page(inode, attr->ia_size, false);
+		if (attr->ia_size > old_size) {
+			err = f2fs_zero_post_eof_page(inode,
+				attr->ia_size, false, true);
+			if (err)
+				goto err_out;
+		}
 		truncate_setsize(inode, attr->ia_size);
 
 		if (attr->ia_size <= old_size)
@@ -1170,6 +1223,7 @@ int f2fs_setattr(struct mnt_idmap *idmap
 		 * do not trim all blocks after i_size if target size is
 		 * larger than i_size.
 		 */
+err_out:
 		filemap_invalidate_unlock(inode->i_mapping);
 		f2fs_up_write(&fi->i_gc_rwsem[WRITE]);
 		if (err)
@@ -1280,7 +1334,9 @@ static int f2fs_punch_hole(struct inode
 	if (ret)
 		return ret;
 
-	f2fs_zero_post_eof_page(inode, offset + len, true);
+	ret = f2fs_zero_post_eof_page(inode, offset + len, true, false);
+	if (ret)
+		return ret;
 
 	pg_start = ((unsigned long long) offset) >> PAGE_SHIFT;
 	pg_end = ((unsigned long long) offset + len) >> PAGE_SHIFT;
@@ -1565,7 +1621,9 @@ static int f2fs_do_collapse(struct inode
 	f2fs_down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
 	filemap_invalidate_lock(inode->i_mapping);
 
-	f2fs_zero_post_eof_page(inode, offset + len, false);
+	ret = f2fs_zero_post_eof_page(inode, offset + len, false, false);
+	if (ret)
+		goto out_unlock;
 
 	f2fs_lock_op(sbi);
 	f2fs_drop_extent_tree(inode);
@@ -1573,6 +1631,7 @@ static int f2fs_do_collapse(struct inode
 	ret = __exchange_data_block(inode, inode, end, start, nrpages - end, true);
 	f2fs_unlock_op(sbi);
 
+out_unlock:
 	filemap_invalidate_unlock(inode->i_mapping);
 	f2fs_up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
 	return ret;
@@ -1694,7 +1753,9 @@ static int f2fs_zero_range(struct inode
 	if (ret)
 		return ret;
 
-	f2fs_zero_post_eof_page(inode, offset + len, true);
+	ret = f2fs_zero_post_eof_page(inode, offset + len, true, false);
+	if (ret)
+		return ret;
 
 	pg_start = ((unsigned long long) offset) >> PAGE_SHIFT;
 	pg_end = ((unsigned long long) offset + len) >> PAGE_SHIFT;
@@ -1828,7 +1889,9 @@ static int f2fs_insert_range(struct inod
 	f2fs_down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
 	filemap_invalidate_lock(mapping);
 
-	f2fs_zero_post_eof_page(inode, offset + len, false);
+	ret = f2fs_zero_post_eof_page(inode, offset + len, false, false);
+	if (ret)
+		goto out_unlock;
 	truncate_pagecache(inode, offset);
 
 	while (!ret && idx > pg_start) {
@@ -1844,6 +1907,7 @@ static int f2fs_insert_range(struct inod
 					idx + delta, nr, false);
 		f2fs_unlock_op(sbi);
 	}
+out_unlock:
 	filemap_invalidate_unlock(mapping);
 	f2fs_up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
 	if (ret)
@@ -1886,7 +1950,9 @@ static int f2fs_expand_inode_data(struct
 	if (err)
 		return err;
 
-	f2fs_zero_post_eof_page(inode, offset + len, true);
+	err = f2fs_zero_post_eof_page(inode, offset + len, true, true);
+	if (err)
+		return err;
 
 	f2fs_balance_fs(sbi, true);
 
@@ -4947,8 +5013,10 @@ static ssize_t f2fs_write_checks(struct
 	if (err)
 		return err;
 
-	f2fs_zero_post_eof_page(inode,
-		iocb->ki_pos + iov_iter_count(from), true);
+	err = f2fs_zero_post_eof_page(inode,
+		iocb->ki_pos + iov_iter_count(from), true, true);
+	if (err)
+		return err;
 	return count;
 }
 
author maintainer
#369 lore raw

PATCH 369/583 drm/amdgpu: Fix init ordering in amdgpu_vram_mgr_init()

In reply to Greg Kroah-Hartman

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

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

From: Thomas Hellström <[email protected]>

commit e773798e14ac0aea54ca9676083b91f445e5bc59 upstream.

drmm_cgroup_register_region() is called before INIT_LIST_HEAD() and gpu_buddy_init() in amdgpu_vram_mgr_init(). If it fails, the function returns early and bypasses those initializations.

Since adev->mman.initialized is set to true before amdgpu_vram_mgr_init() is called, a failure triggers amdgpu_ttm_fini(), which calls amdgpu_vram_mgr_fini(), which then:

- Calls list_for_each_entry_safe() on reservations_pending and reserved_pages, whose list_head::next pointers are zero-initialized (NULL). The loop does not recognize them as empty and dereferences NULL.

- Calls gpu_buddy_fini(), which iterates free_trees[] unconditionally via for_each_free_tree(). Since mm->free_trees is NULL (never allocated), this dereferences NULL.

Both result in a kernel panic on the module load error path.

Fix by moving drmm_cgroup_register_region() to after the list and buddy allocator are fully initialized, so the teardown path is safe to run.

---
 drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c |    7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)
drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c +4 -3
@@ -918,9 +918,6 @@ int amdgpu_vram_mgr_init(struct amdgpu_d
 	struct ttm_resource_manager *man = &mgr->manager;
 	int err;
 
-	man->cg = drmm_cgroup_register_region(adev_to_drm(adev), "vram", adev->gmc.real_vram_size);
-	if (IS_ERR(man->cg))
-		return PTR_ERR(man->cg);
 	ttm_resource_manager_init(man, &adev->mman.bdev,
 				  adev->gmc.real_vram_size);
 
@@ -935,6 +932,10 @@ int amdgpu_vram_mgr_init(struct amdgpu_d
 	if (err)
 		return err;
 
+	man->cg = drmm_cgroup_register_region(adev_to_drm(adev), "vram", adev->gmc.real_vram_size);
+	if (IS_ERR(man->cg))
+		return PTR_ERR(man->cg);
+
 	ttm_set_driver_manager(&adev->mman.bdev, TTM_PL_VRAM, &mgr->manager);
 	ttm_resource_manager_set_used(man, true);
 	return 0;
author maintainer
#370 lore raw

PATCH 370/583 drm/xe/vram: report FLAT_CCS base misalignment

In reply to Greg Kroah-Hartman

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

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

From: Matthew Auld <[email protected]>

commit 0e68c74e44da81a4599c52437ee1f63a2c234470 upstream.

So we can easily check if a machine had the CCS bug, when looking back over bug reports where we have the same machine with newer kernel.

Example print for a machine with the CCS bug:

FLAT_CCS base:27bbff800, aligned:no

v2 (Matt B):
- Unconditionally print the base + alignment

Fixes 37173392741c ("drm/xe/vram: fix ccs offset calculation")
Cc Thomas Hellström <[email protected]>
Cc Matthew Brost <[email protected]>
Cc Rodrigo Vivi <[email protected]>
(cherry picked from commit d00b7f4f03bbeb2efad872f1686130e18c2b4141)
---
 drivers/gpu/drm/xe/xe_vram.c |    3 +++
 1 file changed, 3 insertions(+)
drivers/gpu/drm/xe/xe_vram.c +3 -0
@@ -207,6 +207,9 @@ static inline u64 get_flat_ccs_offset(st
 		offset |= offset_lo << 6; /* HW view bits 31:6 */
 		offset *= num_enabled; /* convert to SW view */
 
+		drm_info(&xe->drm, "FLAT_CCS base:%llx, aligned:%s\n", offset,
+			 str_yes_no(IS_ALIGNED(offset, SZ_128K)));
+
 		/*
 		 * Everything below this offset is handed to the VRAM
 		 * allocator, so it has to be the *first* address the
author maintainer
#371 lore raw

PATCH 371/583 drm/panthor: harden firmware build-info bounds checks

In reply to Greg Kroah-Hartman

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

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

From: Osama Abdelkader <[email protected]>

commit 8321b093fa6c297b80586460ce6914d9655df170 upstream.

panthor_fw_read_build_info() checks whether the metadata range fits in the firmware image with hdr.meta_start + hdr.meta_size. Both fields are u32, so the addition can wrap and let an out-of-bounds range pass validation.

The function also reads the "git_sha: " prefix without first checking that the metadata is long enough, and meta_size == 0 can underflow the NULL terminator index.

Use subtraction-based bounds checking and reject metadata that is too short to contain the expected prefix and trailing NULL byte.

---
 drivers/gpu/drm/panthor/panthor_fw.c |    3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/gpu/drm/panthor/panthor_fw.c +2 -1
@@ -657,7 +657,8 @@ static int panthor_fw_read_build_info(st
 		return ret;
 
 	if (hdr.meta_start > fw->size ||
-	    hdr.meta_start + hdr.meta_size > fw->size) {
+	    hdr.meta_size > fw->size - hdr.meta_start ||
+	    hdr.meta_size <= header_len) {
 		drm_err(&ptdev->base, "Firmware build info corrupt\n");
 		/* We don't need the build info, so continue */
 		return 0;
author maintainer
#372 lore raw

PATCH 372/583 drm/panthor: fix firmware control interface bounds checks

In reply to Greg Kroah-Hartman

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

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

From: Osama Abdelkader <[email protected]>

commit 6a47f9fd2d970674ed9dedc52fc7ab76fd015785 upstream.

panthor_init_cs_iface() and panthor_init_csg_iface() validate firmware control interface offsets with 32-bit arithmetic and the size of the host wrapper structures. The offsets are derived from firmware-provided strides, so the arithmetic can wrap before the bounds check, and the host wrapper size is not the size of the firmware control interface being mapped.

Use 64-bit arithmetic for the computed offsets and validate against the actual firmware control interface structure sizes with subtraction-based bounds checks. Also validate that the shared section is large enough for the global control interface before using it.

---
 drivers/gpu/drm/panthor/panthor_fw.c |   19 +++++++++++++------
 1 file changed, 13 insertions(+), 6 deletions(-)
drivers/gpu/drm/panthor/panthor_fw.c +13 -6
@@ -843,14 +843,15 @@ static int panthor_init_cs_iface(struct
 	struct panthor_fw_csg_iface *csg_iface = panthor_fw_get_csg_iface(ptdev, csg_idx);
 	struct panthor_fw_cs_iface *cs_iface = &ptdev->fw->iface.streams[csg_idx][cs_idx];
 	u64 shared_section_sz = panthor_kernel_bo_size(ptdev->fw->shared_section->mem);
-	u32 iface_offset = CSF_GROUP_CONTROL_OFFSET +
-			   (csg_idx * glb_iface->control->group_stride) +
+	u64 iface_offset = CSF_GROUP_CONTROL_OFFSET +
+			   ((u64)csg_idx * glb_iface->control->group_stride) +
 			   CSF_STREAM_CONTROL_OFFSET +
-			   (cs_idx * csg_iface->control->stream_stride);
+			   ((u64)cs_idx * csg_iface->control->stream_stride);
 	struct panthor_fw_cs_iface *first_cs_iface =
 		panthor_fw_get_cs_iface(ptdev, 0, 0);
 
-	if (iface_offset + sizeof(*cs_iface) >= shared_section_sz)
+	if (iface_offset > shared_section_sz ||
+	    sizeof(*cs_iface->control) > shared_section_sz - iface_offset)
 		return -EINVAL;
 
 	spin_lock_init(&cs_iface->lock);
@@ -900,10 +901,12 @@ static int panthor_init_csg_iface(struct
 	struct panthor_fw_global_iface *glb_iface = panthor_fw_get_glb_iface(ptdev);
 	struct panthor_fw_csg_iface *csg_iface = &ptdev->fw->iface.groups[csg_idx];
 	u64 shared_section_sz = panthor_kernel_bo_size(ptdev->fw->shared_section->mem);
-	u32 iface_offset = CSF_GROUP_CONTROL_OFFSET + (csg_idx * glb_iface->control->group_stride);
+	u64 iface_offset = CSF_GROUP_CONTROL_OFFSET +
+			   ((u64)csg_idx * glb_iface->control->group_stride);
 	unsigned int i;
 
-	if (iface_offset + sizeof(*csg_iface) >= shared_section_sz)
+	if (iface_offset > shared_section_sz ||
+	    sizeof(*csg_iface->control) > shared_section_sz - iface_offset)
 		return -EINVAL;
 
 	spin_lock_init(&csg_iface->lock);
@@ -955,11 +958,15 @@ static u32 panthor_get_instr_features(st
 static int panthor_fw_init_ifaces(struct panthor_device *ptdev)
 {
 	struct panthor_fw_global_iface *glb_iface = &ptdev->fw->iface.global;
+	u64 shared_section_sz = panthor_kernel_bo_size(ptdev->fw->shared_section->mem);
 	unsigned int i;
 
 	if (!ptdev->fw->shared_section->mem->kmap)
 		return -EINVAL;
 
+	if (sizeof(*glb_iface->control) > shared_section_sz)
+		return -EINVAL;
+
 	spin_lock_init(&glb_iface->lock);
 	glb_iface->control = ptdev->fw->shared_section->mem->kmap;
 
author maintainer
#373 lore raw

PATCH 373/583 drm/bridge: dw-hdmi: fix i2c adapter leak on probe failure

In reply to Greg Kroah-Hartman

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

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

From: Johan Hovold <[email protected]>

commit 09b195a7bb23df56269cd2a95d01ba3a5533af13 upstream.

Make sure to drop the i2c adapter device and module references before returning when detecting a malformed devicetree during probe.

Fixes 80e2f97968b5 ("drm: bridge: dw-hdmi: Switch to regmap for register access")
Cc Neil Armstrong <[email protected]>
---
 drivers/gpu/drm/bridge/synopsys/dw-hdmi.c |    3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +2 -1
@@ -3385,7 +3385,8 @@ struct dw_hdmi *dw_hdmi_probe(struct pla
 			break;
 		default:
 			dev_err(dev, "reg-io-width must be 1 or 4\n");
-			return ERR_PTR(-EINVAL);
+			ret = -EINVAL;
+			goto err_res;
 		}
 
 		iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
author maintainer
#374 lore raw

PATCH 374/583 drm/panel-edp: fix i2c adapter leak on probe failure

In reply to Greg Kroah-Hartman

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

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

From: Johan Hovold <[email protected]>

commit e2a9e291275a74e309a21cbb1def6296a72d6aed upstream.

Make sure to drop the i2c adapter reference on probe failure (e.g. probe deferral) and on driver unbind also if a devicetree redundantly uses the 'ddc-i2c-bus' property to point to the aux ddc bus.

---
 drivers/gpu/drm/panel/panel-edp.c |   19 +++++++++++++------
 1 file changed, 13 insertions(+), 6 deletions(-)
drivers/gpu/drm/panel/panel-edp.c +13 -6
@@ -831,6 +831,13 @@ exit:
 	return 0;
 }
 
+static void panel_edp_put_adapter(void *_adap)
+{
+	struct i2c_adapter *adap = _adap;
+
+	put_device(&adap->dev);
+}
+
 static int panel_edp_probe(struct device *dev, const struct panel_desc *desc,
 			   struct drm_dp_aux *aux)
 {
@@ -878,6 +885,11 @@ static int panel_edp_probe(struct device
 
 		if (!panel->ddc)
 			return -EPROBE_DEFER;
+
+		err = devm_add_action_or_reset(dev, panel_edp_put_adapter,
+					       panel->ddc);
+		if (err)
+			return err;
 	} else if (aux) {
 		panel->ddc = &aux->ddc;
 	}
@@ -889,7 +901,7 @@ static int panel_edp_probe(struct device
 
 	err = drm_panel_of_backlight(&panel->base);
 	if (err)
-		goto err_finished_ddc_init;
+		return err;
 
 	/*
 	 * We use runtime PM for prepare / unprepare since those power the panel
@@ -936,9 +948,6 @@ static int panel_edp_probe(struct device
 err_finished_pm_runtime:
 	pm_runtime_dont_use_autosuspend(dev);
 	pm_runtime_disable(dev);
-err_finished_ddc_init:
-	if (panel->ddc && (!panel->aux || panel->ddc != &panel->aux->ddc))
-		put_device(&panel->ddc->dev);
 
 	return err;
 }
@@ -982,8 +991,6 @@ static void panel_edp_remove(struct devi
 
 	pm_runtime_dont_use_autosuspend(dev);
 	pm_runtime_disable(dev);
-	if (panel->ddc && (!panel->aux || panel->ddc != &panel->aux->ddc))
-		put_device(&panel->ddc->dev);
 
 	drm_edid_free(panel->drm_edid);
 	panel->drm_edid = NULL;
author maintainer
#375 lore raw

PATCH 375/583 drm: fix race between partial drm_dev_register() failure and ioctl

In reply to Greg Kroah-Hartman

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

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

From: Danilo Krummrich <[email protected]>

commit eb197f7d60f00d0f5b1b3505dfc86a7e36045a3e upstream.

If drm_dev_register() fails after registering a minor (e.g. render minor registered, primary minor fails), userspace could have opened the first minor and entered a drm_dev_enter() critical section. Since the unplugged flag was never set, the ioctl proceeds while the error path tears down device resources.

Fix this by introducing drm_dev_synchronize_unplug(), which sets the unplugged flag and waits for the SRCU barrier, ensuring all in-flight drm_dev_enter() critical sections complete before cleanup proceeds; call it on the error path of drm_dev_register().

---
 drivers/gpu/drm/drm_drv.c |   34 +++++++++++++++++++++++++---------
 1 file changed, 25 insertions(+), 9 deletions(-)
drivers/gpu/drm/drm_drv.c +25 -9
@@ -472,6 +472,22 @@ void drm_dev_exit(int idx)
 }
 EXPORT_SYMBOL(drm_dev_exit);
 
+/*
+ * Mark the device as unplugged and wait for any in-flight drm_dev_enter()
+ * critical sections to complete.
+ */
+static void drm_dev_synchronize_unplug(struct drm_device *dev)
+{
+	/*
+	 * After synchronizing any critical read section is guaranteed to see
+	 * the new value of ->unplugged, and any critical section which might
+	 * still have seen the old value of ->unplugged is guaranteed to have
+	 * finished.
+	 */
+	dev->unplugged = true;
+	synchronize_srcu(&drm_unplug_srcu);
+}
+
 /**
  * drm_dev_unplug - unplug a DRM device
  * @dev: DRM device
@@ -484,15 +500,7 @@ EXPORT_SYMBOL(drm_dev_exit);
  */
 void drm_dev_unplug(struct drm_device *dev)
 {
-	/*
-	 * After synchronizing any critical read section is guaranteed to see
-	 * the new value of ->unplugged, and any critical section which might
-	 * still have seen the old value of ->unplugged is guaranteed to have
-	 * finished.
-	 */
-	dev->unplugged = true;
-	synchronize_srcu(&drm_unplug_srcu);
-
+	drm_dev_synchronize_unplug(dev);
 	drm_dev_unregister(dev);
 
 	/* Clear all CPU mappings pointing to this device */
@@ -1087,6 +1095,7 @@ int drm_dev_register(struct drm_device *
 		goto err_minors;
 
 	dev->registered = true;
+	dev->unplugged = false;
 
 	if (driver->load) {
 		ret = driver->load(dev, flags);
@@ -1113,6 +1122,13 @@ err_unload:
 	if (dev->driver->unload)
 		dev->driver->unload(dev);
 err_minors:
+	/*
+	 * If a minor was registered before the failure, userspace could have
+	 * opened it and entered a drm_dev_enter() critical section. Ensure all
+	 * such sections complete before we clean up.
+	 */
+	drm_dev_synchronize_unplug(dev);
+
 	remove_compat_control_link(dev);
 	drm_minor_unregister(dev, DRM_MINOR_ACCEL);
 	drm_minor_unregister(dev, DRM_MINOR_PRIMARY);
author maintainer
#376 lore raw

PATCH 376/583 drm/i915: Guard against NULL driver_data in i915_pci_probe()

In reply to Greg Kroah-Hartman

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

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

From: Deepanshu Kartikey <[email protected]>

commit 3785d40831ba5601296283e0197e10e089392757 upstream.

pci_match_device() can return the dummy pci_device_id_any entry when a device is force-bound via sysfs driver_override, in which case ->driver_data is unset (NULL). i915_pci_probe() casts it to struct intel_device_info * unconditionally and dereferences intel_info->require_force_probe, causing a NULL-ptr-deref.

---
 drivers/gpu/drm/i915/i915_pci.c |    3 +++
 1 file changed, 3 insertions(+)
drivers/gpu/drm/i915/i915_pci.c +3 -0
@@ -958,6 +958,9 @@ static int i915_pci_probe(struct pci_dev
 		(struct intel_device_info *) ent->driver_data;
 	int err;
 
+	if (!intel_info)
+		return -ENODEV;
+
 	if (intel_info->require_force_probe && !id_forced(pdev->device)) {
 		dev_info(&pdev->dev,
 			 "Your graphics device %04x is not properly supported by i915 in this\n"
author maintainer
#377 lore raw

PATCH 377/583 drm/ssd130x: fix column and row end address in partial updates for ssd132x

In reply to Greg Kroah-Hartman

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

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

From: Amit Barzilai <[email protected]>

commit 99e9c09358195454ecd200b9c6aba6b7d209fad4 upstream.

On partial screen updates, SSD132X controllers expect to get the rectangle addresses as arguments of the "Set Column Address" and "Set Row Address" commands. Each command expects the start address and end address of the row/column in absolute format, however the end addresses were being sent in a relative format (relative to the start address).

The relative end addresses work only when the start address is 0. In those situations, there is no value difference between relative and absolute addresses.

---
 drivers/gpu/drm/solomon/ssd130x.c |    5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)
drivers/gpu/drm/solomon/ssd130x.c +3 -2
@@ -863,12 +863,13 @@ static int ssd132x_update_rect(struct ss
 	 */
 
 	/* Set column start and end */
-	ret = ssd130x_write_cmd(ssd130x, 3, SSD132X_SET_COL_RANGE, x / segment_width, columns - 1);
+	ret = ssd130x_write_cmd(ssd130x, 3, SSD132X_SET_COL_RANGE, x / segment_width,
+				x / segment_width + columns - 1);
 	if (ret < 0)
 		return ret;
 
 	/* Set row start and end */
-	ret = ssd130x_write_cmd(ssd130x, 3, SSD132X_SET_ROW_RANGE, y, rows - 1);
+	ret = ssd130x_write_cmd(ssd130x, 3, SSD132X_SET_ROW_RANGE, y, y + rows - 1);
 	if (ret < 0)
 		return ret;
 
author maintainer
#378 lore raw

PATCH 378/583 drm/sun4i: fix refcount leak in sun4i_backend_init_sat()

In reply to Greg Kroah-Hartman

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

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

From: Wentao Liang <[email protected]>

commit f7a56ff6240e6fd0cb36a3c0a911a1cd54789ce2 upstream.

When sun4i_backend_init_sat() calls reset_control_deassert() it increments the deassert_count of the reset controller, and must pair that with a reset_control_assert() call to decrement it. In the error path where clk_prepare_enable() fails, the function returns immediately without calling reset_control_assert(), leaking the reference count. Other error paths, like the devm_clk_get() failure, correctly jump to the err_assert_reset label which performs the missing assert.

Fix the leak by using the existing err_assert_reset label in the clk_prepare_enable error path instead of returning directly.

---
 drivers/gpu/drm/sun4i/sun4i_backend.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/gpu/drm/sun4i/sun4i_backend.c +1 -1
@@ -685,7 +685,7 @@ static int sun4i_backend_init_sat(struct
 	ret = clk_prepare_enable(backend->sat_clk);
 	if (ret) {
 		dev_err(dev, "Couldn't enable the SAT clock\n");
-		return ret;
+		goto err_assert_reset;
 	}
 
 	return 0;
author maintainer
#379 lore raw

PATCH 379/583 drm/ssd130x: fix column and row end address in partial updates in ssd133x

In reply to Greg Kroah-Hartman

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

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

From: Amit Barzilai <[email protected]>

commit b7fcb70162acd7f15ed20bc64a14c150db34256f upstream.

On partial screen updates, SSD133X controllers expect to get the rectangle addresses as arguments of the "Set Column Address" and "Set Row Address" commands. Each command expects the start address and end address of the row/column in absolute format, however the end addresses were being sent in a relative format (relative to the start address).

The relative end addresses work only when the start address is 0. In those situations, there is no value difference between relative and absolute addresses.

---
 drivers/gpu/drm/solomon/ssd130x.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/gpu/drm/solomon/ssd130x.c +2 -2
@@ -915,12 +915,12 @@ static int ssd133x_update_rect(struct ss
 	 */
 
 	/* Set column start and end */
-	ret = ssd130x_write_cmd(ssd130x, 3, SSD133X_SET_COL_RANGE, x, columns - 1);
+	ret = ssd130x_write_cmd(ssd130x, 3, SSD133X_SET_COL_RANGE, x, x + columns - 1);
 	if (ret < 0)
 		return ret;
 
 	/* Set row start and end */
-	ret = ssd130x_write_cmd(ssd130x, 3, SSD133X_SET_ROW_RANGE, y, rows - 1);
+	ret = ssd130x_write_cmd(ssd130x, 3, SSD133X_SET_ROW_RANGE, y, y + rows - 1);
 	if (ret < 0)
 		return ret;
 
author maintainer
#380 lore raw

PATCH 380/583 drm/nouveau/disp/r535: Add scanline position support + head state support

In reply to Greg Kroah-Hartman

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

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

From: Lyude Paul <[email protected]>

commit 804cb093b245c752f15d17186e0d404f10303593 upstream.

That's right! It looks like this never actually got finished, something which I just noticed today when I saw this fun message spamming one of my test machine's kernel logs when enabling display debug output for nouveau:

[drm:drm_crtc_vblank_helper_get_vblank_timestamp_internal] crtc 0 : scanoutpos query failed.

So it looks like we've been falling back to DRM's core fallback for a while now, whoops.

So, while it seems that we do have the option of doing this through GSP - that doesn't seem like a great idea. Mainly because reading this from GSP would involve a lot more latency then we should have for vblank handling due to the RPC communication. So instead of implementing that, just use gv100_head_state and gv100_head_rgpos for implementing .state and .rgpos. It seems to work perfectly fine!

Fixes 9e9944449023 ("drm/nouveau/disp/r535: initial support")
Cc Ben Skeggs <[email protected]>
Cc Dave Airlie <[email protected]>
Cc Timur Tabi <[email protected]>
Cc Ben Skeggs <[email protected]>
Cc James Jones <[email protected]>
Cc Faith Ekstrand <[email protected]>
Cc Suraj Kandpal <[email protected]>
Cc Lyude Paul <[email protected]>
Cc Aaron Kling <[email protected]>
Cc Danilo Krummrich <[email protected]>
Cc Zhang Enpei <[email protected]>
Cc <[email protected]> # v6.7+
---
 drivers/gpu/drm/nouveau/nvkm/engine/disp/gv100.c       |    4 ++--
 drivers/gpu/drm/nouveau/nvkm/engine/disp/head.h        |    2 ++
 drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/disp.c |    8 ++------
 3 files changed, 6 insertions(+), 8 deletions(-)
drivers/gpu/drm/nouveau/nvkm/engine/disp/gv100.c +2 -2
@@ -253,7 +253,7 @@ gv100_head_vblank_get(struct nvkm_head *
 	nvkm_mask(device, 0x611d80 + (head->id * 4), 0x00000004, 0x00000004);
 }
 
-static void
+void
 gv100_head_rgpos(struct nvkm_head *head, u16 *hline, u16 *vline)
 {
 	struct nvkm_device *device = head->disp->engine.subdev.device;
@@ -263,7 +263,7 @@ gv100_head_rgpos(struct nvkm_head *head,
 	*hline = nvkm_rd32(device, 0x616334 + hoff) & 0x0000ffff;
 }
 
-static void
+void
 gv100_head_state(struct nvkm_head *head, struct nvkm_head_state *state)
 {
 	struct nvkm_device *device = head->disp->engine.subdev.device;
drivers/gpu/drm/nouveau/nvkm/engine/disp/head.h +2 -0
@@ -53,6 +53,8 @@ void gf119_head_rgclk(struct nvkm_head *
 
 int gv100_head_cnt(struct nvkm_disp *, unsigned long *);
 int gv100_head_new(struct nvkm_disp *, int id);
+void gv100_head_state(struct nvkm_head *head, struct nvkm_head_state *state);
+void gv100_head_rgpos(struct nvkm_head *head, u16 *hline, u16 *vline);
 
 #define HEAD_MSG(h,l,f,a...) do {                                              \
 	struct nvkm_head *_h = (h);                                            \
drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/disp.c +2 -6
@@ -625,14 +625,10 @@ r535_head_vblank_get(struct nvkm_head *h
 	nvkm_mask(device, 0x611d80 + (head->id * 4), 0x00000002, 0x00000002);
 }
 
-static void
-r535_head_state(struct nvkm_head *head, struct nvkm_head_state *state)
-{
-}
-
 static const struct nvkm_head_func
 r535_head = {
-	.state = r535_head_state,
+	.state = gv100_head_state,
+	.rgpos = gv100_head_rgpos,
 	.vblank_get = r535_head_vblank_get,
 	.vblank_put = r535_head_vblank_put,
 };
author maintainer
#381 lore raw

PATCH 381/583 drm/hibmc: Fix list of formats on the primary plane

In reply to Greg Kroah-Hartman

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

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

From: Thomas Zimmermann <[email protected]>

commit 0e682e136c466ae37c62f18099f591c096260ee0 upstream.

Remove all formats from the primary plane that are unsupported for various reasons.

* Formats with alpha channel: planes should not announce alpha channels unless they support transparency. There's no transparency support in the primary plane's implementation.

* Formats with BGR order. The common format is in RGB channel order. There's no BGR support in the primary plane's implementation.

* RGB888: atomic_update programs the format from cpp[0] * 8 / 16. For RGB888's cpp value of 3 this returns 1.5; rounded to 1. Programming the value of 1 to HIBMC_CRT_DISP_CTL_FORMAT sets up RGB565. Hence, the output is distorted. This can be tested by booting with video=1024x768-24.

Removing all unsupported formats leaves XRGB8888 and RGB565. Both of which are supported and work correctly.

Fixes da52605eea8f ("drm/hisilicon/hibmc: Add support for display engine")
Cc Rongrong Zou <[email protected]>
Cc Sean Paul <[email protected]>
Cc Xinliang Liu <[email protected]>
Cc Dmitry Baryshkov <[email protected]>
Cc Yongbang Shi <[email protected]>
Cc Baihan Li <[email protected]>
Cc <[email protected]> # v4.10+
---
 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c |    6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)
drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c +2 -4
@@ -153,10 +153,8 @@ static void hibmc_plane_atomic_update(st
 }
 
 static const u32 channel_formats1[] = {
-	DRM_FORMAT_RGB565, DRM_FORMAT_BGR565, DRM_FORMAT_RGB888,
-	DRM_FORMAT_BGR888, DRM_FORMAT_XRGB8888, DRM_FORMAT_XBGR8888,
-	DRM_FORMAT_RGBA8888, DRM_FORMAT_BGRA8888, DRM_FORMAT_ARGB8888,
-	DRM_FORMAT_ABGR8888
+	DRM_FORMAT_XRGB8888,
+	DRM_FORMAT_RGB565,
 };
 
 static const struct drm_plane_funcs hibmc_plane_funcs = {
author maintainer
#382 lore raw

PATCH 382/583 drm/hibmc: Use drm_atomic_helper_check_plane_state()

In reply to Greg Kroah-Hartman

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

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

From: Thomas Zimmermann <[email protected]>

commit 715c5db68bdbd4a524b79ebf20fb61e880fffea0 upstream.

Call drm_atomic_helper_check_plane_state() from the primary plane's atomic-check helper and replace the custom implementation.

All plane's implementations of atomic_check should call the shared _check_plane_state() helper first. It adjusts the plane state for correct positioning, rotation and scaling of the plane. Do this even if the plane's CRTC has been disabled by setting the parameter can_update_disabled. The original code returned early in this case, but it's safe to so and cleaner to have all plane state initialized.

As we don't set can_position, drm_atomic_helper_check_plane_state()'s visibility check tests if the plane covers all of the CRTC. This is a small change from the original code, which tested if the plane is exactly the size of the CRTC. With the new test, the plane still has to cover all of the CRTC, but can be larger than the CRTC's size. A later patch can fully implement this feature in hibmc.

If the plane is disabled, the helper clears the visibility flag in the plane state. On errors or if the plane is not visible, the atomic-check helper can return early. Implement all this in hibmc and drop the custom code that does some of it.

v2:
- extend the commit description (Yongbang)

Fixes da52605eea8f ("drm/hisilicon/hibmc: Add support for display engine")
Cc Rongrong Zou <[email protected]>
Cc Sean Paul <[email protected]>
Cc Xinliang Liu <[email protected]>
Cc Dmitry Baryshkov <[email protected]>
Cc Baihan Li <[email protected]>
Cc Yongbang Shi <[email protected]>
Cc <[email protected]> # v4.10+
---
 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c |   50 ++++++++-----------------
 1 file changed, 16 insertions(+), 34 deletions(-)
drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c +16 -34
@@ -72,46 +72,28 @@ static int hibmc_get_best_clock_idx(cons
 static int hibmc_plane_atomic_check(struct drm_plane *plane,
 				    struct drm_atomic_state *state)
 {
-	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
-										 plane);
-	struct drm_framebuffer *fb = new_plane_state->fb;
-	struct drm_crtc *crtc = new_plane_state->crtc;
-	struct drm_crtc_state *crtc_state;
-	u32 src_w = new_plane_state->src_w >> 16;
-	u32 src_h = new_plane_state->src_h >> 16;
-
-	if (!crtc || !fb)
-		return 0;
-
-	crtc_state = drm_atomic_get_crtc_state(state, crtc);
-	if (IS_ERR(crtc_state))
-		return PTR_ERR(crtc_state);
-
-	if (src_w != new_plane_state->crtc_w || src_h != new_plane_state->crtc_h) {
-		drm_dbg_atomic(plane->dev, "scale not support\n");
-		return -EINVAL;
-	}
-
-	if (new_plane_state->crtc_x < 0 || new_plane_state->crtc_y < 0) {
-		drm_dbg_atomic(plane->dev, "crtc_x/y of drm_plane state is invalid\n");
-		return -EINVAL;
-	}
-
-	if (!crtc_state->enable)
+	struct drm_plane_state *new_plane_state =
+		drm_atomic_get_new_plane_state(state, plane);
+	struct drm_crtc_state *new_crtc_state = NULL;
+	int ret;
+
+	if (new_plane_state->crtc)
+		new_crtc_state = drm_atomic_get_new_crtc_state(state, new_plane_state->crtc);
+
+	ret = drm_atomic_helper_check_plane_state(new_plane_state, new_crtc_state,
+						  DRM_PLANE_NO_SCALING,
+						  DRM_PLANE_NO_SCALING,
+						  false, true);
+	if (ret)
+		return ret;
+	else if (!new_plane_state->visible)
 		return 0;
 
-	if (new_plane_state->crtc_x + new_plane_state->crtc_w >
-	    crtc_state->adjusted_mode.hdisplay ||
-	    new_plane_state->crtc_y + new_plane_state->crtc_h >
-	    crtc_state->adjusted_mode.vdisplay) {
-		drm_dbg_atomic(plane->dev, "visible portion of plane is invalid\n");
-		return -EINVAL;
-	}
-
 	if (new_plane_state->fb->pitches[0] % 128 != 0) {
 		drm_dbg_atomic(plane->dev, "wrong stride with 128-byte aligned\n");
 		return -EINVAL;
 	}
+
 	return 0;
 }
 
author maintainer
#383 lore raw

PATCH 383/583 drm/amd/display: avoid divide-by-zero in __is_lut_linear()

In reply to Greg Kroah-Hartman

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

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

From: Harry Wentland <[email protected]>

commit 4f40873f8a4107df2b9c8e68c947c4fd0cd519d2 upstream.

__is_lut_linear() computes the expected value of each entry with

	expected = i * MAX_DRM_LUT_VALUE / (size - 1);

If it is ever called with a single-entry LUT, size - 1 is zero and the kernel takes a divide error (#DE). A LUT with fewer than two entries cannot describe a linear mapping anyway, so return false early instead of dividing by zero.

---
 drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c |    6 ++++++
 1 file changed, 6 insertions(+)
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c +6 -0
@@ -360,6 +360,12 @@ static bool __is_lut_linear(const struct
 	uint32_t expected;
 	int delta;
 
+	/* A LUT with fewer than two entries can't be interpolated and would
+	 * divide by zero below (size - 1); it can't be treated as linear.
+	 */
+	if (size < 2)
+		return false;
+
 	for (i = 0; i < size; i++) {
 		/* All color values should equal */
 		if ((lut[i].red != lut[i].green) || (lut[i].green != lut[i].blue))
author maintainer
#384 lore raw

PATCH 384/583 drm/amd/display: fix dc_lock leak on GPU reset error paths

In reply to Greg Kroah-Hartman

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

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

From: Linkai Gong <[email protected]>

commit 92a9eebd2a1f892fe482154d83f9f1626bc73d3b upstream.

On GPU reset, dm_suspend() takes dc_lock and leaves it for dm_resume() to drop. If amdgpu_dm_commit_zero_streams() or dm_dmub_hw_init() fails, the function returns with the lock still held. The matching resume path is then skipped, so every later dc_lock take hangs.

Release the cached DC state and unlock before returning the error.

Fixes 3cf7a0bc87f0 ("drm/amd/display: Catch failures for amdgpu_dm_commit_zero_streams()")
Fixes 2b6943df5413 ("drm/amd/display: Pass up errors for reset GPU that fails to init HW")
---
 drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c |    6 ++++++
 1 file changed, 6 insertions(+)
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +6 -0
@@ -3261,6 +3261,9 @@ static int dm_suspend(struct amdgpu_ip_b
 		res = amdgpu_dm_commit_zero_streams(dm->dc);
 		if (res != DC_OK) {
 			drm_err(adev_to_drm(adev), "Failed to commit zero streams: %d\n", res);
+			dc_state_release(dm->cached_dc_state);
+			dm->cached_dc_state = NULL;
+			mutex_unlock(&dm->dc_lock);
 			return -EINVAL;
 		}
 
@@ -3505,6 +3508,9 @@ static int dm_resume(struct amdgpu_ip_bl
 		r = dm_dmub_hw_init(adev);
 		if (r) {
 			drm_err(adev_to_drm(adev), "DMUB interface failed to initialize: status=%d\n", r);
+			dc_state_release(dm->cached_dc_state);
+			dm->cached_dc_state = NULL;
+			mutex_unlock(&dm->dc_lock);
 			return r;
 		}
 
author maintainer
#385 lore raw

PATCH 385/583 drm/amd/display: validate plane degamma LUT size for private color prop

In reply to Greg Kroah-Hartman

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

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

From: Harry Wentland <[email protected]>

commit e4c3ab59021e7c146a84b6671f0d530972bd58b4 upstream.

Unlike the CRTC degamma path, which is guarded by amdgpu_dm_verify_lut_sizes(), the per-plane degamma LUT size was never validated before use. __set_dm_plane_degamma() passed the user-supplied size straight into __is_lut_linear() and, for a non-linear LUT, into __set_input_tf() -> __drm_lut_to_dc_gamma(), the latter always iterating MAX_COLOR_LUT_ENTRIES entries regardless of the actual LUT size.

A malformed AMD_PLANE_DEGAMMA_LUT blob (e.g. a single entry) could thus trigger a divide-by-zero in __is_lut_linear() or an out-of-bounds read in __drm_lut_to_dc_gamma(). Reject any plane degamma LUT whose size does not match MAX_COLOR_LUT_ENTRIES, mirroring the invariant the code already asserts a few lines below (and which the CRTC path enforces).

The AMD_PLANE_DEGAMMA_LUT property is only exposed on builds with AMD_PRIVATE_COLOR defined.

Fixes 980f8710075a ("drm/amd/display: add plane degamma TF and LUT support")
---
 drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c |    7 +++++++
 1 file changed, 7 insertions(+)
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c +7 -0
@@ -1149,6 +1149,13 @@ __set_dm_plane_degamma(struct drm_plane_
 	degamma_lut = __extract_blob_lut(dm_plane_state->degamma_lut,
 					 &degamma_size);
 
+	if (degamma_lut && degamma_size != MAX_COLOR_LUT_ENTRIES) {
+		drm_dbg(plane_state->state->dev,
+			"Invalid Plane Degamma LUT size. Should be %u but got %u.\n",
+			MAX_COLOR_LUT_ENTRIES, degamma_size);
+		return -EINVAL;
+	}
+
 	has_degamma_lut = degamma_lut &&
 			  !__is_lut_linear(degamma_lut, degamma_size);
 
author maintainer
#386 lore raw

PATCH 386/583 drm/amdgpu/vcn: fix integer overflow in dec_msg buffer count check

In reply to Greg Kroah-Hartman

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

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

From: David (Ming Qiang) Wu <[email protected]>

commit 4d7390530853eb7befda9cc786e4c86e8ad7ac9e upstream.

If the supplied msg[2] (num_buffers) is 0x3FFFFFFF, the expression 6 + num_buffers * 4 wraps to 2 and the bounds check passes, letting the parser loop far past the end of the message BO. Triggering it additionally requires a ~4GiB mapping so that msg[1] survives the earlier "header does not fit in BO" check.

Rewrite the test in division form, which is overflow-free by construction. Also update the message to reflect that msg is invalid.

Fixes b193019860d6 ("drm/amdgpu/vcn3: Prevent OOB reads when parsing dec msg")
Fixes 0a78f2bac142 ("drm/amdgpu/vcn4: Prevent OOB reads when parsing dec msg")
---
 drivers/gpu/drm/amd/amdgpu/vcn_v3_0.c |   10 +++++++---
 drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c |   10 +++++++---
 2 files changed, 14 insertions(+), 6 deletions(-)
drivers/gpu/drm/amd/amdgpu/vcn_v3_0.c +7 -3
@@ -1963,9 +1963,13 @@ static int vcn_v3_0_dec_msg(struct amdgp
 	len_dw = msg[1] / 4;
 	num_buffers = msg[2];
 
-	/* Verify that all indices fit within the claimed length. Each index is 4 DWORDs */
-	if (num_buffers > len_dw || 6 + num_buffers * 4 > len_dw) {
-		DRM_ERROR("VCN message has too many buffers!\n");
+	/* Verify that all indices fit within the claimed length.
+	 * There are 6 dwords in the header before the first buffer.
+	 * Each buffer has 4 dwords. Any trailing dwords after the
+	 * last buffer are ignored.
+	 */
+	if (len_dw < 6 || num_buffers > (len_dw - 6) / 4) {
+		DRM_ERROR("Invalid VCN message!\n");
 		r = -EINVAL;
 		goto out;
 	}
drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c +7 -3
@@ -1879,9 +1879,13 @@ static int vcn_v4_0_dec_msg(struct amdgp
 	len_dw = msg[1] / 4;
 	num_buffers = msg[2];
 
-	/* Verify that all indices fit within the claimed length. Each index is 4 DWORDs */
-	if (num_buffers > len_dw || 6 + num_buffers * 4 > len_dw) {
-		DRM_ERROR("VCN message has too many buffers!\n");
+	/* Verify that all indices fit within the claimed length.
+	 * There are 6 dwords in the header before the first buffer.
+	 * Each buffer has 4 dwords. Any trailing dwords after the
+	 * last buffer are ignored.
+	 */
+	if (len_dw < 6 || num_buffers > (len_dw - 6) / 4) {
+		DRM_ERROR("Invalid VCN message!\n");
 		r = -EINVAL;
 		goto out;
 	}
author maintainer
#387 lore raw

PATCH 387/583 drm/gud: NUL-terminate TV mode names read from the device

In reply to Greg Kroah-Hartman

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

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

From: Deepanshu Kartikey <[email protected]>

commit 500cb24cd61bad8a2747ddfc49b7034899c82d94 upstream.

gud_connector_add_tv_mode() reads a buffer of fixed-size mode names from the USB device and passes pointers into it to drm_mode_create_tv_properties_legacy(), which calls strlen() on each one. Nothing guarantees the device NUL-terminates a name, so strlen() can run past the end of a slot and, for the last mode, past the end of the allocation.

Terminate each name at the end of its slot before use.

---
 drivers/gpu/drm/gud/gud_connector.c |    9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)
drivers/gpu/drm/gud/gud_connector.c +7 -2
@@ -396,8 +396,13 @@ static int gud_connector_add_tv_mode(str
 	}
 
 	num_modes = ret / GUD_CONNECTOR_TV_MODE_NAME_LEN;
-	for (i = 0; i < num_modes; i++)
-		modes[i] = &buf[i * GUD_CONNECTOR_TV_MODE_NAME_LEN];
+	for (i = 0; i < num_modes; i++) {
+		char *mode = &buf[i * GUD_CONNECTOR_TV_MODE_NAME_LEN];
+
+		/* The device is not trusted to NUL-terminate the name */
+		mode[GUD_CONNECTOR_TV_MODE_NAME_LEN - 1] = '\0';
+		modes[i] = mode;
+	}
 
 	ret = drm_mode_create_tv_properties_legacy(connector->dev, num_modes, modes);
 free:
author maintainer
#388 lore raw

PATCH 388/583 drm/gud: validate TV mode names before creating enum property

In reply to Greg Kroah-Hartman

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

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

From: Tao Yu <[email protected]>

commit da1ea35fea67ad841f4ada28dd61b41be65e5437 upstream.

The GUD protocol returns TV mode names as fixed-size GUD_CONNECTOR_TV_MODE_NAME_LEN entries and requires each name to be NUL-terminated.

gud_connector_add_tv_mode() currently passes each fixed-size entry directly to drm_mode_create_tv_properties_legacy(), which eventually reaches drm_property_add_enum() and strlen(). If a device returns an entry without a terminating NUL byte, strlen() reads past the end of the slot and can run beyond the allocated buffer, triggering an out-of-bounds read.

Validate that each returned TV mode name contains a NUL terminator within its fixed-size slot before passing it to the DRM property code. If a malformed entry is found, reject the device response with -EIO.

This fixes the out-of-bounds read without changing the handling of valid devices, and avoids silently truncating malformed protocol data.

---
 drivers/gpu/drm/gud/gud_connector.c |    7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)
drivers/gpu/drm/gud/gud_connector.c +5 -2
@@ -399,8 +399,11 @@ static int gud_connector_add_tv_mode(str
 	for (i = 0; i < num_modes; i++) {
 		char *mode = &buf[i * GUD_CONNECTOR_TV_MODE_NAME_LEN];
 
-		/* The device is not trusted to NUL-terminate the name */
-		mode[GUD_CONNECTOR_TV_MODE_NAME_LEN - 1] = '\0';
+		if (!memchr(mode, '\0', GUD_CONNECTOR_TV_MODE_NAME_LEN)) {
+			ret = -EIO;
+			goto free;
+		}
+
 		modes[i] = mode;
 	}
 
author maintainer
#389 lore raw

PATCH 389/583 drm/msm/dsi: round 6G byte clock rate to the PLL-achievable value

In reply to Greg Kroah-Hartman

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

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

From: Kavan Smith <[email protected]>

commit 6cd33b6f4155efc20485929fd0b56bb704641db9 upstream.

MSM8916 runtime DSI commands still go through msm_dsi_host_xfer_prepare(), which re-applies the link clock rate before enabling the link clocks. That is fine in principle, but on DSI 6G the requested byte clock rate often does not exactly match the DSI PHY PLL's realizable rate. For example, the driver can request 56250000 Hz while the PLL actually runs at 56246337 Hz.

Because the requested and actual rates differ slightly, every later link_clk_set_rate() call is treated as a real clock change and re-locks the PLL. On a video-mode panel without an internal timing generator, such as samsung,s6d7aa0 / lsl080al03 on MSM8916, that live-clock glitch makes the panel lose pixel lock and visibly corrupts scanout on each runtime DCS command, including backlight writes.

Fix this by rounding the computed 6G byte clock rate up front, before it is stored in msm_host->byte_clk_rate and reused by later transfers. Once the host carries the PLL-achievable rate instead of the idealized one, repeated link_clk_set_rate() calls become no-ops in the common clock framework and no longer re-lock the PLL.

This keeps the normal transfer callback sequencing intact, preserves the OPP vote path in link_clk_set_rate(), and matches the fix direction suggested in the original 2018 discussion.

Fixes 6b16f05aa39f ("drm/msm/dsi: Split clk rate setting and enable")

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

---
 drivers/gpu/drm/msm/dsi/dsi_host.c |   12 ++++++++++++
 1 file changed, 12 insertions(+)
drivers/gpu/drm/msm/dsi/dsi_host.c +12 -0
@@ -670,12 +670,24 @@ static void dsi_calc_pclk(struct msm_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;
 }
author maintainer
#390 lore raw

PATCH 390/583 drm: Fix drm_crtc_commit leak if signaled when PAGE_FLIP_EVENT is used

In reply to Greg Kroah-Hartman

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

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

From: Thadeu Lima de Souza Cascardo <[email protected]>

commit 4d4be202165e832d74849b4a68e289a2a377039c upstream.

Commit 1c6ceeee6ebb ("drm/atomic: Fix memleak on ERESTARTSYS during non-blocking commits") fixed a very similar issue when the event was allocated by drm_atomic_helper_setup_commit() itself.

However, if the event is allocated in prepare_signaling(), it will also be set to NULL in complete_signaling(), which prevents drm_crtc_commit from being put in __drm_atomic_helper_crtc_destroy_state().

Dropping the reference when the event is set to NULL at complete_signaling() fixes the leak.

The leak can be reproduced by sending a signal to the thread using DRM_MODE_PAGE_FLIP_EVENT and using a sw_sync fence to cause the atomic ioctl to block at drm_atomic_helper_wait_for_fences(). It happened both with amdgpu and vkms.

---
 drivers/gpu/drm/drm_atomic_uapi.c |    2 ++
 1 file changed, 2 insertions(+)
drivers/gpu/drm/drm_atomic_uapi.c +2 -0
@@ -1346,6 +1346,8 @@ static void complete_signaling(struct dr
 		 * to prevent a double free in drm_atomic_state_clear.
 		 */
 		if (event && (event->base.fence || event->base.file_priv)) {
+			if (crtc_state->commit && crtc_state->commit->abort_completion)
+				drm_crtc_commit_put(crtc_state->commit);
 			drm_event_cancel_free(dev, &event->base);
 			crtc_state->event = NULL;
 		}
author maintainer
#391 lore raw

PATCH 391/583 drm/amdgpu: check thunderbolt before switcheroo registration

In reply to Greg Kroah-Hartman

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

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

From: Yang Wang <[email protected]>

commit 8587d48d694da5aca580f92461658ec14470592b upstream.

Introduce a helper to consolidate the vga_switcheroo registration condition used by the init and fini paths.

Keep the explicit pci_is_thunderbolt_attached() check, as dev_is_removable() does not provide equivalent coverage for Thunderbolt-attached GPUs. This ensures such devices remain excluded from switcheroo registration while preserving the existing PX and Apple gmux handling.

---
 drivers/gpu/drm/amd/amdgpu/amdgpu_device.c |   14 ++++++++++----
 1 file changed, 10 insertions(+), 4 deletions(-)
drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +10 -4
@@ -4463,6 +4463,14 @@ static void amdgpu_device_set_mcbp(struc
 		dev_info(adev->dev, "MCBP is enabled\n");
 }
 
+static bool
+amdgpu_device_should_register_switcheroo(struct amdgpu_device *adev, bool px)
+{
+	return !pci_is_thunderbolt_attached(adev->pdev) &&
+	       (px || (!dev_is_removable(&adev->pdev->dev) &&
+		       apple_gmux_detect(NULL, NULL)));
+}
+
 /**
  * amdgpu_device_init - initialize the driver
  *
@@ -4940,8 +4948,7 @@ fence_driver_init:
 
 	px = amdgpu_device_supports_px(adev);
 
-	if (px || (!dev_is_removable(&adev->pdev->dev) &&
-				apple_gmux_detect(NULL, NULL)))
+	if (amdgpu_device_should_register_switcheroo(adev, px))
 		vga_switcheroo_register_client(adev->pdev,
 					       &amdgpu_switcheroo_ops, px);
 
@@ -5117,8 +5124,7 @@ void amdgpu_device_fini_sw(struct amdgpu
 
 	px = amdgpu_device_supports_px(adev);
 
-	if (px || (!dev_is_removable(&adev->pdev->dev) &&
-				apple_gmux_detect(NULL, NULL)))
+	if (amdgpu_device_should_register_switcheroo(adev, px))
 		vga_switcheroo_unregister_client(adev->pdev);
 
 	if (px)
author maintainer
#392 lore raw

PATCH 392/583 drm/amdgpu: clamp the isolation index for rings outside a partition

In reply to Greg Kroah-Hartman

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

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

From: Xiang Liu <[email protected]>

commit b30900566642ceb2c9e12b56c2afec28d0fd91a0 upstream.

adev->isolation[] has one slot per partition, but a ring that is not assigned to one keeps AMDGPU_XCP_NO_PARTITION, which is ~0, so indexing the array with it is out of bounds. SDMA submissions hit this on both the isolation enforcement and the VM flush path and trip UBSAN.

Fall back to the first slot the way the cleaner shader path already does, and stop taking the address before the ring type check that makes it relevant.

---
 drivers/gpu/drm/amd/amdgpu/amdgpu_device.c |    5 ++++-
 drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c     |    4 +++-
 2 files changed, 7 insertions(+), 2 deletions(-)
drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +4 -1
@@ -7556,8 +7556,8 @@ struct dma_fence *amdgpu_device_enforce_
 						  struct amdgpu_ring *ring,
 						  struct amdgpu_job *job)
 {
-	struct amdgpu_isolation *isolation = &adev->isolation[ring->xcp_id];
 	struct drm_sched_fence *f = job->base.s_fence;
+	struct amdgpu_isolation *isolation;
 	struct dma_fence *dep;
 	void *owner;
 	int r;
@@ -7570,6 +7570,9 @@ struct dma_fence *amdgpu_device_enforce_
 	    ring->funcs->type != AMDGPU_RING_TYPE_COMPUTE)
 		return NULL;
 
+	isolation = &adev->isolation[ring->xcp_id == AMDGPU_XCP_NO_PARTITION ?
+				     0 : ring->xcp_id];
+
 	/*
 	 * All submissions where enforce isolation is false are handled as if
 	 * they come from a single client. Use ~0l as the owner to distinct it
drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c +3 -1
@@ -768,7 +768,9 @@ int amdgpu_vm_flush(struct amdgpu_ring *
 		    bool need_pipe_sync)
 {
 	struct amdgpu_device *adev = ring->adev;
-	struct amdgpu_isolation *isolation = &adev->isolation[ring->xcp_id];
+	struct amdgpu_isolation *isolation =
+		&adev->isolation[ring->xcp_id == AMDGPU_XCP_NO_PARTITION ?
+				 0 : ring->xcp_id];
 	unsigned vmhub = ring->vm_hub;
 	struct amdgpu_vmid_mgr *id_mgr = &adev->vm_manager.id_mgr[vmhub];
 	struct amdgpu_vmid *id = &id_mgr->ids[job->vmid];
author maintainer
#393 lore raw

PATCH 393/583 drm/amdgpu: Disable runtime PM for externally attached dGPUs

In reply to Greg Kroah-Hartman

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

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

From: Yang Wang <[email protected]>

commit c52feb4365396b6a881b5e8a95540517ffabb3b7 upstream.

pci_is_thunderbolt_attached() requires an upstream PCI bridge with is_thunderbolt set from an Intel Thunderbolt VSEC. This does not cover the affected ASM4242 USB4 PCI hierarchy:

00:02.2

    \- 0f:00.0 [1b21:2421]
       +- 10:01.0 [1b21:2423] -> 45:00.0 -> 46:00.0
       |  -> 47:00.0 -> 48:00.0 -> 49:00.0 [1002:7590]
       \- 10:03.0 -> 76:00.0 [1b21:2425] USB4 Host Router

The host router is outside the GPU upstream bridge chain, leaving no ancestor with is_thunderbolt set. PCI core propagates DEVICE_REMOVABLE below the external-facing PCIe tunnel. Disable Runtime PM when either pci_is_thunderbolt_attached() or dev_is_removable() is true.

---
 drivers/gpu/drm/amd/amdgpu/amdgpu_device.c |    7 +++++++
 1 file changed, 7 insertions(+)
drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +7 -0
@@ -468,6 +468,13 @@ void amdgpu_device_detect_runtime_pm_mod
 	int bamaco_support;
 
 	adev->pm.rpm_mode = AMDGPU_RUNPM_NONE;
+	if (pci_is_thunderbolt_attached(adev->pdev) ||
+	    dev_is_removable(&adev->pdev->dev)) {
+		dev_info(adev->dev,
+			 "Runtime PM disabled for externally attached device\n");
+		return;
+	}
+
 	bamaco_support = amdgpu_device_supports_baco(adev);
 
 	switch (amdgpu_runtime_pm) {
author maintainer
#394 lore raw

PATCH 394/583 drm/amdgpu: fix autosuspend cleanup during removal

In reply to Greg Kroah-Hartman

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

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

From: Guangshuo Li <[email protected]>

commit ef5fcf2a6c320676bf8be2dadac93d9023b468b7 upstream.

amdgpu_pci_probe() calls pm_runtime_use_autosuspend(), but amdgpu_pci_remove() does not call the matching pm_runtime_dont_use_autosuspend().

If the autosuspend delay is set to a negative value while autosuspend is enabled, the runtime PM core increments usage_count to prevent runtime suspend. Without calling pm_runtime_dont_use_autosuspend() during teardown, this reference is not dropped and usage_count remains unbalanced.

The documentation for pm_runtime_use_autosuspend() also notes that it is important to undo it with pm_runtime_dont_use_autosuspend() at driver exit time, unless runtime PM was initially enabled with devm_pm_runtime_enable().

Add the missing pm_runtime_dont_use_autosuspend() call to the remove path.

This issue was found by manual code inspection.

---
 drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c |    1 +
 1 file changed, 1 insertion(+)
drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c +1 -0
@@ -2539,6 +2539,7 @@ amdgpu_pci_remove(struct pci_dev *pdev)
 	if (adev->pm.rpm_mode != AMDGPU_RUNPM_NONE) {
 		pm_runtime_get_sync(dev->dev);
 		pm_runtime_forbid(dev->dev);
+		pm_runtime_dont_use_autosuspend(dev->dev);
 	}
 
 	amdgpu_driver_unload_kms(dev);
author maintainer
#395 lore raw

PATCH 395/583 drm/amdgpu: Skip accessing psp rum time db for APUs

In reply to Greg Kroah-Hartman

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

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

From: Kanala Ramalingeswara Reddy <[email protected]>

commit a26301203a196a991527f7b1ab884d4dd0e7c95e upstream.

Psp runtime DB is for dGPUs only.

(cherry picked from commit dce8195027f146467c9378efb2bb1b0859cb735e)
---
 drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c |    6 ++++++
 1 file changed, 6 insertions(+)
drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c +6 -0
@@ -372,6 +372,12 @@ static bool psp_get_runtime_db_entry(str
 	bool ret = false;
 	int i;
 
+	/*
+	 * Runtime DB is for dGPUs only.
+	 */
+	if (adev->flags & AMD_IS_APU)
+		return false;
+
 	if (amdgpu_ip_version(adev, MP0_HWIP, 0) == IP_VERSION(13, 0, 6) ||
 	    amdgpu_ip_version(adev, MP0_HWIP, 0) == IP_VERSION(13, 0, 12) ||
 	    amdgpu_ip_version(adev, MP0_HWIP, 0) == IP_VERSION(13, 0, 14))
author maintainer
#396 lore raw

PATCH 396/583 drm/amdgpu: update the fw version for gfx11 userqueues

In reply to Greg Kroah-Hartman

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

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

From: Sunil Khatri <[email protected]>

commit c748dd03df33360549ad60cdccee13570e9c0f90 upstream.

Update to the latest stable fw versions where userqueues is working as it is expected with major fixes.

(cherry picked from commit d50201b891604ab97f305d4a20d888ba93305b48)
---
 drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c |    8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)
drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c +4 -4
@@ -1616,10 +1616,10 @@ static int gfx_v11_0_sw_init(struct amdg
 	case IP_VERSION(11, 0, 2):
 	case IP_VERSION(11, 0, 3):
 		if (!adev->gfx.disable_uq &&
-		    adev->gfx.me_fw_version  >= 2420 &&
-		    adev->gfx.pfp_fw_version >= 2580 &&
-		    adev->gfx.mec_fw_version >= 2650 &&
-		    adev->mes.fw_version[0] >= 120) {
+		    adev->gfx.me_fw_version  >= 3090 &&
+		    adev->gfx.pfp_fw_version >= 3190 &&
+		    adev->gfx.mec_fw_version >= 3450 &&
+		    adev->mes.fw_version[0] >= 147) {
 			adev->userq_funcs[AMDGPU_HW_IP_GFX] = &userq_mes_funcs;
 			adev->userq_funcs[AMDGPU_HW_IP_COMPUTE] = &userq_mes_funcs;
 		}
author maintainer
#397 lore raw

PATCH 397/583 drm/amdgpu: update the fw version for gfx12 userqueues

In reply to Greg Kroah-Hartman

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

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

From: Sunil Khatri <[email protected]>

commit 49a74a2388528c1a2e96f01114c4513e635605fe upstream.

Update to the latest stable fw versions where userqueues is working as it is expected with major fixes.

(cherry picked from commit 69fa36e3ac92f2544ee7a1b719ec212b8247a2da)
---
 drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c |    8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)
drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c +4 -4
@@ -1421,10 +1421,10 @@ static int gfx_v12_0_sw_init(struct amdg
 	case IP_VERSION(12, 0, 0):
 	case IP_VERSION(12, 0, 1):
 		if (!adev->gfx.disable_uq &&
-		    adev->gfx.me_fw_version  >= 2780 &&
-		    adev->gfx.pfp_fw_version >= 2840 &&
-		    adev->gfx.mec_fw_version >= 3050 &&
-		    adev->mes.fw_version[0] >= 123) {
+		    adev->gfx.me_fw_version  >= 3090 &&
+		    adev->gfx.pfp_fw_version >= 3190 &&
+		    adev->gfx.mec_fw_version >= 3450 &&
+		    adev->mes.fw_version[0] >= 147) {
 			adev->userq_funcs[AMDGPU_HW_IP_GFX] = &userq_mes_funcs;
 			adev->userq_funcs[AMDGPU_HW_IP_COMPUTE] = &userq_mes_funcs;
 		}
author maintainer
#398 lore raw

PATCH 398/583 drm/amdgpu: use AMDGPU_GPU_PAGE_SHIFT instead of PAGE_SHIFT

In reply to Greg Kroah-Hartman

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

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

From: Sunil Khatri <[email protected]>

commit d6e16df7df4d2c39e2b04b355d0434fb90e2d62c upstream.

For different address types the variable PAGE_SHIFT might not work well and it's better to use the GPU specific one

(cherry picked from commit 3494b77d10375e0f9ab784e9b20763339844b55b)
---
 drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c +1 -1
@@ -2054,7 +2054,7 @@ int amdgpu_vm_bo_clear_mappings(struct a
 			after->start = eaddr + 1;
 			after->last = tmp->last;
 			after->offset = tmp->offset;
-			after->offset += (after->start - tmp->start) << PAGE_SHIFT;
+			after->offset += (after->start - tmp->start) << AMDGPU_GPU_PAGE_SHIFT;
 			after->flags = tmp->flags;
 			after->bo_va = tmp->bo_va;
 			list_add(&after->list, &tmp->bo_va->invalids);
author maintainer
#399 lore raw

PATCH 399/583 drm/amdkfd: Add TLB flush after MES queue eviction/suspension

In reply to Greg Kroah-Hartman

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

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

From: Priya Hosur <[email protected]>

commit 94e25cb6ab7f4f025bcdcd8ea79fda30f12843a4 upstream.

MES (Micro Engine Scheduler) does not perform heavy-weight TLB invalidation after unmapping queues, unlike HWS which does this automatically. This causes a race condition where in-flight DMA descriptors can access memory that has been unmapped, leading to page faults and GPU queue hangs during SVM page migration.

The issue manifests as KFDSVMRangeTest.MultiThreadMigrationTest failures on gfx1151 (Strix Point) with XNACK mode 1 enabled - the GPU compute queue hangs with packets submitted but never consumed.

Add kfd_flush_tlb() calls after MES queue removal in two locations:
- evict_process_queues_cpsch(): after all queues removed during eviction
- suspend_queues(): after debug/criu queue suspension (with mem_fence barrier)

This ensures all in-flight memory accesses from unmapped queues are flushed before memory is freed or migrated.

(cherry picked from commit f5c4f88e0f9c45a8fb9dfac0c1df726c95e41b77)
---
 drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c |   13 ++++++++++++-
 1 file changed, 12 insertions(+), 1 deletion(-)
drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c +12 -1
@@ -1233,6 +1233,14 @@ static int evict_process_queues_cpsch(st
 		}
 	}
 
+	/*
+	 * Heavy-weight TLB flush after MES removes queues to ensure
+	 * in-flight memory accesses complete before memory is freed/migrated.
+	 * HWS does this automatically, MES does not.
+	 */
+	if (dqm->dev->kfd->shared_resources.enable_mes)
+		kfd_flush_tlb(pdd);
+
 	if (!dqm->dev->kfd->shared_resources.enable_mes) {
 		pdd->last_evict_timestamp = get_jiffies_64();
 		retval = execute_queues_cpsch(dqm,
@@ -3457,8 +3465,11 @@ int suspend_queues(struct kfd_process *p
 		if (!per_device_suspended) {
 			dqm_unlock(dqm);
 			mutex_unlock(&p->event_mutex);
-			if (total_suspended)
+			if (total_suspended) {
 				amdgpu_amdkfd_debug_mem_fence(dqm->dev->adev);
+				/* Heavy-weight TLB flush after MES suspends queues */
+				kfd_flush_tlb(pdd);
+			}
 			continue;
 		}
 
author maintainer
#400 lore raw

PATCH 400/583 drm/amdkfd: Fix error path at svm_migrate_copy_to_ram

In reply to Greg Kroah-Hartman

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

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

From: Xiaogang Chen <[email protected]>

commit 960c4a8069bfd352c48cc88592618f1ebe24c69e upstream.

If page migration from device to sys ram fails for some reasons driver needs release and unlock allocated system pages. To do that driver should use page physical address, or pfn, then get struct page*. Current driver uses dma address(for adev) that is not correct with IOMMU enabled, or even in general.

The patch releases and unlocks allocated system pages based on where migration failed by struct page* of sys ram pages. Also dma_unmap correspodent system ram pages at error path.

---
 drivers/gpu/drm/amd/amdkfd/kfd_migrate.c |   45 ++++++++++++++++++++-----------
 1 file changed, 30 insertions(+), 15 deletions(-)
drivers/gpu/drm/amd/amdkfd/kfd_migrate.c +30 -15
@@ -252,15 +252,6 @@ svm_migrate_get_sys_page(struct vm_area_
 	return page;
 }
 
-static void svm_migrate_put_sys_page(unsigned long addr)
-{
-	struct page *page;
-
-	page = pfn_to_page(addr >> PAGE_SHIFT);
-	unlock_page(page);
-	put_page(page);
-}
-
 static unsigned long svm_migrate_successful_pages(struct migrate_vma *migrate)
 {
 	unsigned long mpages = 0;
@@ -584,9 +575,10 @@ svm_migrate_copy_to_ram(struct amdgpu_de
 			dma_addr_t *scratch, u64 npages)
 {
 	struct device *dev = adev->dev;
-	u64 *src;
+	struct page *dpage = NULL;
 	dma_addr_t *dst;
-	struct page *dpage;
+	u64 *src;
+
 	u64 i = 0, j;
 	u64 addr;
 	int r = 0;
@@ -640,6 +632,7 @@ svm_migrate_copy_to_ram(struct amdgpu_de
 		r = dma_mapping_error(dev, dst[i]);
 		if (r) {
 			dev_err(adev->dev, "%s: fail %d dma_map_page\n", __func__, r);
+			dst[i] = 0;
 			goto out_oom;
 		}
 
@@ -647,17 +640,39 @@ svm_migrate_copy_to_ram(struct amdgpu_de
 				     dst[i] >> PAGE_SHIFT, page_to_pfn(dpage));
 
 		migrate->dst[i] = migrate_pfn(page_to_pfn(dpage));
+
+		dpage = NULL;
 		j++;
 	}
 
-	r = svm_migrate_copy_memory_gart(adev, dst + i - j, src + i - j, j,
-					 FROM_VRAM_TO_RAM, mfence);
-
+	if (j > 0)
+		r = svm_migrate_copy_memory_gart(adev, dst + i - j, src + i - j, j,
+						 FROM_VRAM_TO_RAM, mfence);
 out_oom:
 	if (r) {
 		pr_debug("failed %d copy to ram\n", r);
+
+		/* first release current dpage when dma_map_page fail */
+		if (dpage) {
+			unlock_page(dpage);
+			put_page(dpage);
+		}
+
+		/* release previous allocated sys pages and unmap dma address */
 		while (i--) {
-			svm_migrate_put_sys_page(dst[i]);
+
+			if (dst[i]) {
+				dma_unmap_page(dev, dst[i], PAGE_SIZE,
+					       DMA_BIDIRECTIONAL);
+				dst[i] = 0;
+			}
+
+			dpage = migrate_pfn_to_page(migrate->dst[i]);
+			if (!dpage)
+				continue;
+
+			unlock_page(dpage);
+			put_page(dpage);
 			migrate->dst[i] = 0;
 		}
 	}
author maintainer
#401 lore raw

PATCH 401/583 drm/amdkfd: Fix the case that vm range is hole at svm_migrate_copy_to_vram

In reply to Greg Kroah-Hartman

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

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

From: Xiaogang Chen <[email protected]>

commit 520e345ffe05aabef1db82beda4288afb1757ff2 upstream.

When migration vm range is hole at cpu side(MIGRATE_PFN_MIGRATE set + MIGRATE_PFN_VALID unset) driver still allocates device pages. There is no dma map of src pages and migration. j is 0 and svm_migrate_copy_memory_gart() will return an uninitialized r. That can trigger out_free_vram_pages to drop all VRAM just set up.

Initialize r and only call the last svm_migrate_copy_memory_gart if j > 0.

Current code postponed the last page to the final copy. This patch flushes on the last page when reach to the end of current drm_buddy_block; avoids another svm_migrate_copy_memory_gart.

---
 drivers/gpu/drm/amd/amdkfd/kfd_migrate.c |   11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)
drivers/gpu/drm/amd/amdkfd/kfd_migrate.c +7 -4
@@ -132,7 +132,7 @@ svm_migrate_copy_memory_gart(struct amdg
 	u64 gart_s, gart_d;
 	struct dma_fence *next;
 	u64 size;
-	int r;
+	int r = 0;
 
 	mutex_lock(&adev->mman.gtt_window_lock);
 
@@ -278,7 +278,7 @@ svm_migrate_copy_to_vram(struct kfd_node
 	dma_addr_t *src;
 	u64 *dst;
 	u64 i, j;
-	int r;
+	int r = 0;
 
 	pr_debug("svms 0x%p [0x%lx 0x%lx 0x%llx]\n", prange->svms, prange->start,
 		 prange->last, ttm_res_offset);
@@ -304,6 +304,7 @@ svm_migrate_copy_to_vram(struct kfd_node
 					      DMA_BIDIRECTIONAL);
 			r = dma_mapping_error(dev, src[i]);
 			if (r) {
+				src[i] = 0;
 				dev_err(dev, "%s: fail %d dma_map_page\n",
 					__func__, r);
 				goto out_free_vram_pages;
@@ -328,7 +329,8 @@ svm_migrate_copy_to_vram(struct kfd_node
 		pr_debug_ratelimited("dma mapping src to 0x%llx, pfn 0x%lx\n",
 				     src[i] >> PAGE_SHIFT, page_to_pfn(spage));
 
-		if (j >= (cursor.size >> PAGE_SHIFT) - 1 && i < npages - 1) {
+		/* accumulated j + 1 pages reach end of current drm_buddy_block */
+		if (j + 1 >= (cursor.size >> PAGE_SHIFT)) {
 			r = svm_migrate_copy_memory_gart(adev, src + i - j,
 							 dst + i - j, j + 1,
 							 FROM_RAM_TO_VRAM,
@@ -342,7 +344,8 @@ svm_migrate_copy_to_vram(struct kfd_node
 		}
 	}
 
-	r = svm_migrate_copy_memory_gart(adev, src + i - j, dst + i - j, j,
+	if (j > 0)
+		r = svm_migrate_copy_memory_gart(adev, src + i - j, dst + i - j, j,
 					 FROM_RAM_TO_VRAM, mfence);
 
 out_free_vram_pages:
author maintainer
#402 lore raw

PATCH 402/583 drm/amdkfd: guard against NULL restore_mqd in CRIU queue restore

In reply to Greg Kroah-Hartman

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

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

From: Vladimir Marioukhine <[email protected]>

commit 6aa530642f95d5c48aa336416f94a35e7949b647 upstream.

Both create_queue_cpsch() and create_queue_nocpsch() unconditionally call mqd_mgr->restore_mqd() when a CRIU restore is in progress (qd != NULL), with no NULL guard. On any system where restore_mqd is not implemented for the given queue type, a user holding CAP_CHECKPOINT_RESTORE can trigger a kernel NULL pointer dereference and panic the machine by issuing KFD_IOC_CRIU_OP_RESTORE with a crafted queue restore object. Note that checkpoint_mqd is likewise unimplemented on GFX12, so no legitimate CRIU image can reach this path — only a hand-crafted restore payload.

Add a NULL guard for restore_mqd immediately after mqd_mgr is resolved, unwinding via the existing error labels and returning -EOPNOTSUPP if the callback is not implemented. This mirrors the existing checkpoint_mqd guard in checkpoint_mqd().

Fixes 48f0bdf4e38e ("drm/amdkfd: Added MQD manager files for GFX12.")
---
 drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c |   10 ++++++++++
 1 file changed, 10 insertions(+)
drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c +10 -0
@@ -653,6 +653,11 @@ static int create_queue_nocpsch(struct d
 
 	mqd_mgr = dqm->mqd_mgrs[get_mqd_type_from_queue_type(
 			q->properties.type)];
+	if (qd && !mqd_mgr->restore_mqd) {
+		pr_debug("restore_mqd not implemented for this GPU\n");
+		retval = -EOPNOTSUPP;
+		goto deallocate_vmid;
+	}
 	if (q->properties.type == KFD_QUEUE_TYPE_COMPUTE) {
 		retval = allocate_hqd(dqm, q);
 		if (retval)
@@ -2002,6 +2007,11 @@ static int create_queue_cpsch(struct dev
 
 	mqd_mgr = dqm->mqd_mgrs[get_mqd_type_from_queue_type(
 			q->properties.type)];
+	if (qd && !mqd_mgr->restore_mqd) {
+		pr_debug("restore_mqd not implemented for this GPU\n");
+		retval = -EOPNOTSUPP;
+		goto out_deallocate_doorbell;
+	}
 
 	if (q->properties.type == KFD_QUEUE_TYPE_SDMA ||
 		q->properties.type == KFD_QUEUE_TYPE_SDMA_XGMI)
author maintainer
#403 lore raw

PATCH 403/583 drm/amdkfd: Reject zero-sized AQL queue allocations after size halving

In reply to Greg Kroah-Hartman

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

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

From: Sunday Clement <[email protected]>

commit 40ba09e11188d1b7f79d51fc28aca5ea45e0c138 upstream.

KFD_IOC_ALLOC_MEMORY_OF_GPU with flag KFD_IOC_ALLOC_MEM_FLAGS_AQL_QUEUE_MEM and size=1 triggers the AQL wraparound workaround (size >>= 1), reducing size to 0. The resulting zero passes through PAGE_ALIGN(0) = 0 without validation, bypassing the per-process VRAM quota check in reserve_mem_limit() (vram_used + 0 > vram_available is always false).

The fix adds post-halving zero-size validation in the primary allocation path (amdgpu_amdkfd_gpuvm.c). The check happens after size halving but before reserve_mem_limit(), and uses err_alignment_size error path to properly clean up the allocated kgd_mem structure and mutex.

---
 drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c |    7 +++++++
 drivers/gpu/drm/amd/amdkfd/kfd_chardev.c         |    3 ++-
 2 files changed, 9 insertions(+), 1 deletion(-)
drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c +7 -0
@@ -1777,6 +1777,12 @@ int amdgpu_amdkfd_gpuvm_alloc_memory_of_
 		size >>= 1;
 	aligned_size = PAGE_ALIGN(size);
 
+	/* reject AQL queue with size < 2 */
+	if (!aligned_size) {
+		ret = -EINVAL;
+		goto err_alignment_size;
+	}
+
 	(*mem)->alloc_flags = flags;
 
 	amdgpu_sync_create(&(*mem)->sync);
@@ -1868,6 +1874,7 @@ err_bo_create:
 	amdgpu_amdkfd_unreserve_mem_limit(adev, aligned_size, flags, xcp_id);
 err_reserve_limit:
 	amdgpu_sync_free(&(*mem)->sync);
+err_alignment_size:
 	mutex_destroy(&(*mem)->lock);
 	if (gobj)
 		drm_gem_object_put(gobj);
drivers/gpu/drm/amd/amdkfd/kfd_chardev.c +2 -1
@@ -1162,7 +1162,8 @@ static int kfd_ioctl_alloc_memory_of_gpu
 
 		if (flags & KFD_IOC_ALLOC_MEM_FLAGS_AQL_QUEUE_MEM)
 			size >>= 1;
-		atomic64_add(PAGE_ALIGN(size), &pdd->vram_usage);
+		size = PAGE_ALIGN(size);
+		atomic64_add(size, &pdd->vram_usage);
 	}
 
 	mutex_unlock(&p->mutex);
author maintainer
#404 lore raw

PATCH 404/583 drm/sysfb: simpledrm: Improve framebuffer-size validation

In reply to Greg Kroah-Hartman

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

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

From: Thomas Zimmermann <[email protected]>

commit 03f1a3545b721fa7fdadd00080e237519a286a97 upstream.

Validate the framebuffer size from the firmware against the limitations of struct drm_display_mode. The type only stores sizes in 16-bit fields. Fail probing on errors.

v2:
- remove unused function simplefb_get_validated_int0() (Sashiko)

Fixes 11e8f5fd223b ("drm: Add simpledrm driver")
Cc <[email protected]> # v5.14+
Fixes 11e8f5fd223b ("drm: Add simpledrm driver")
---
 drivers/gpu/drm/sysfb/simpledrm.c |   15 ++++-----------
 1 file changed, 4 insertions(+), 11 deletions(-)
drivers/gpu/drm/sysfb/simpledrm.c +4 -11
@@ -44,13 +44,6 @@ simplefb_get_validated_int(struct drm_de
 	return drm_sysfb_get_validated_int(dev, name, value, INT_MAX);
 }
 
-static int
-simplefb_get_validated_int0(struct drm_device *dev, const char *name,
-			    uint32_t value)
-{
-	return drm_sysfb_get_validated_int0(dev, name, value, INT_MAX);
-}
-
 static const struct drm_format_info *
 simplefb_get_validated_format(struct drm_device *dev, const char *format_name)
 {
@@ -84,14 +77,14 @@ static int
 simplefb_get_width_pd(struct drm_device *dev,
 		      const struct simplefb_platform_data *pd)
 {
-	return simplefb_get_validated_int0(dev, "width", pd->width);
+	return drm_sysfb_get_validated_int0(dev, "width", pd->width, U16_MAX);
 }
 
 static int
 simplefb_get_height_pd(struct drm_device *dev,
 		       const struct simplefb_platform_data *pd)
 {
-	return simplefb_get_validated_int0(dev, "height", pd->height);
+	return drm_sysfb_get_validated_int0(dev, "height", pd->height, U16_MAX);
 }
 
 static int
@@ -140,7 +133,7 @@ simplefb_get_width_of(struct drm_device
 
 	if (ret)
 		return ret;
-	return simplefb_get_validated_int0(dev, "width", width);
+	return drm_sysfb_get_validated_int0(dev, "width", width, U16_MAX);
 }
 
 static int
@@ -151,7 +144,7 @@ simplefb_get_height_of(struct drm_device
 
 	if (ret)
 		return ret;
-	return simplefb_get_validated_int0(dev, "height", height);
+	return drm_sysfb_get_validated_int0(dev, "height", height, U16_MAX);
 }
 
 static int
author maintainer
#405 lore raw

PATCH 405/583 drm/sysfb: simpledrm: Improve panel-size validation

In reply to Greg Kroah-Hartman

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

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

From: Thomas Zimmermann <[email protected]>

commit 3a75a0761914d01c7362adf1f906cc1d1762c189 upstream.

Validate the panel size from the device-tree node against the limitations of struct drm_display_mode. The type only stores sizes in 16-bit fields. Fail transparently on errors; do not warn.

v3:
- move comments to a more prominent place (Thierry) v2:
- only use initialized values in debugging output (Sashiko)

Fixes 2a6d731a8f16 ("drm/simpledrm: Allow physical width and height configuration via panel node")
Cc Rayyan Ansari <[email protected]>
Cc <[email protected]> # v6.4+
---
 drivers/gpu/drm/sysfb/simpledrm.c |   49 +++++++++++++++++++++++++++++++++++---
 1 file changed, 46 insertions(+), 3 deletions(-)
drivers/gpu/drm/sysfb/simpledrm.c +46 -3
@@ -189,6 +189,39 @@ simplefb_get_memory_of(struct drm_device
 	return res;
 }
 
+static int __simplefb_get_panel_size_mm_of(struct drm_device *dev,
+					   struct device_node *of_panel_node,
+					   const char *name)
+{
+	int ret;
+	u32 value;
+
+	ret = of_property_read_u32(of_panel_node, name, &value);
+	if (ret) {
+		drm_dbg(dev, "simplefb: cannot parse panel %s: error %d\n",
+			name, ret);
+		return ret;
+	} else if (value > U16_MAX) {
+		drm_dbg(dev, "simplefb: panel %s of %u exceeds maximum value\n",
+			name, value);
+		return -EINVAL;
+	}
+
+	return value;
+}
+
+static int simplefb_get_panel_width_mm_of(struct drm_device *dev,
+					  struct device_node *of_panel_node)
+{
+	return __simplefb_get_panel_size_mm_of(dev, of_panel_node, "width-mm");
+}
+
+static int simplefb_get_panel_height_mm_of(struct drm_device *dev,
+					   struct device_node *of_panel_node)
+{
+	return __simplefb_get_panel_size_mm_of(dev, of_panel_node, "height-mm");
+}
+
 /*
  * Simple Framebuffer device
  */
@@ -590,7 +623,7 @@ static struct simpledrm_device *simpledr
 	struct drm_sysfb_device *sysfb;
 	struct drm_device *dev;
 	int width, height, stride;
-	int width_mm = 0, height_mm = 0;
+	u16 width_mm = 0, height_mm = 0;
 	struct device_node *panel_node;
 	const struct drm_format_info *format;
 	struct resource *res, *mem = NULL;
@@ -654,8 +687,18 @@ static struct simpledrm_device *simpledr
 			return ERR_CAST(mem);
 		panel_node = of_parse_phandle(of_node, "panel", 0);
 		if (panel_node) {
-			simplefb_read_u32_of(dev, panel_node, "width-mm", &width_mm);
-			simplefb_read_u32_of(dev, panel_node, "height-mm", &height_mm);
+			/*
+			 * Ignore errors from parsing the physical panel
+			 * size. Using the pre-initialized sizes of 0 will
+			 * make drm_sysfb_mode() calculate a default physical
+			 * size based on a resolution of 96 dpi.
+			 */
+			ret = simplefb_get_panel_width_mm_of(dev, panel_node);
+			if (ret > 0)
+				width_mm = ret;
+			ret = simplefb_get_panel_height_mm_of(dev, panel_node);
+			if (ret > 0)
+				height_mm = ret;
 			of_node_put(panel_node);
 		}
 	} else {
author maintainer
#406 lore raw

PATCH 406/583 drm/sysfb: simpledrm: Improve stride validation

In reply to Greg Kroah-Hartman

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

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

From: Thomas Zimmermann <[email protected]>

commit df6533f11688aa30be3bb883c7637f4ffdbb7cbd upstream.

Validate the computed stride against the maximum value INT_MAX.

---
 drivers/gpu/drm/sysfb/simpledrm.c |   10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)
drivers/gpu/drm/sysfb/simpledrm.c +8 -2
@@ -706,9 +706,15 @@ static struct simpledrm_device *simpledr
 		return ERR_PTR(-ENODEV);
 	}
 	if (!stride) {
-		stride = drm_format_info_min_pitch(format, 0, width);
-		if (drm_WARN_ON(dev, !stride))
+		u64 pitch = drm_format_info_min_pitch(format, 0, width);
+
+		if (drm_WARN_ON(dev, !pitch)) {
+			return ERR_PTR(-EINVAL); /* driver bug */
+		} else if (pitch > INT_MAX) {
+			drm_warn(dev, "stride of %llu exceeds maximum\n", pitch);
 			return ERR_PTR(-EINVAL);
+		}
+		stride = pitch;
 	}
 
 	sysfb->fb_mode = drm_sysfb_mode(width, height, width_mm, height_mm);
author maintainer
#407 lore raw

PATCH 407/583 drm/sysfb: ofdrm: Fix integer overflow in fb_size calculation

In reply to Greg Kroah-Hartman

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

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

From: Shixiong Ou <[email protected]>

commit c6f48e59ece0123f6a11527ad4d89b21c2d65b87 upstream.

The framebuffer size calculation fb_size = linebytes * height can overflow when both values are large (e.g., 46341 * 46341 > INT_MAX). Since linebytes and height are both int types, the multiplication is performed as int * int, which results in undefined behavior on overflow.

Use check_mul_overflow() to detect and prevent this overflow, consistent with the approach used in simpledrm.c and corebootdrm.c.

Fixes c8a17756c425 ("drm/ofdrm: Add ofdrm for Open Firmware framebuffers")
Cc <[email protected]> # v6.2+
---
 drivers/gpu/drm/sysfb/ofdrm.c |    6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)
drivers/gpu/drm/sysfb/ofdrm.c +5 -1
@@ -2,6 +2,7 @@
 
 #include <linux/aperture.h>
 #include <linux/of_address.h>
+#include <linux/overflow.h>
 #include <linux/pci.h>
 #include <linux/platform_device.h>
 
@@ -910,7 +911,10 @@ static struct ofdrm_device *ofdrm_device
 			return ERR_PTR(-EINVAL);
 	}
 
-	fb_size = linebytes * height;
+	if (check_mul_overflow(linebytes, height, &fb_size)) {
+		drm_err(dev, "framebuffer size exceeds maximum\n");
+		return ERR_PTR(-EINVAL);
+	}
 
 	/*
 	 * Try to figure out the address of the framebuffer. Unfortunately, Open
author maintainer
#408 lore raw

PATCH 408/583 drm/sysfb: ofdrm: Fix is_avivo() constant comparison bug

In reply to Greg Kroah-Hartman

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

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

From: Shixiong Ou <[email protected]>

commit 958f35cbb8955ca3fa439cd9f2092cb42414aa8c upstream.

The is_avivo() function has a logic error where it compares a constant to another constant instead of checking the device parameter:

(PCI_VENDOR_ID_ATI_R600 >= 0x9400)

---
 drivers/gpu/drm/sysfb/ofdrm.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/gpu/drm/sysfb/ofdrm.c +1 -1
@@ -236,7 +236,7 @@ static bool is_avivo(u32 vendor, u32 dev
 	/* This will match most R5xx */
 	return (vendor == PCI_VENDOR_ID_ATI) &&
 	       ((device >= PCI_VENDOR_ID_ATI_R520 && device < 0x7800) ||
-		(PCI_VENDOR_ID_ATI_R600 >= 0x9400));
+		(device >= PCI_VENDOR_ID_ATI_R600));
 }
 
 static enum ofdrm_model display_get_model_of(struct drm_device *dev, struct device_node *of_node)
author maintainer
#409 lore raw

PATCH 409/583 drm/nouveau/gsp: use per-version DP_CONFIG_STREAM params on r570 firmware

In reply to Greg Kroah-Hartman

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

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

From: Mohamed Ahmed <[email protected]>

commit 39fd4b742720c68da8695ee1ffa85c5fea4f8e11 upstream.

NVIDIA removed the deprecated actualPclkHz/linkClkFreqHz fields and the whole Legacy{activeCnt, activeFrac, activePolarity, mvidWarEnabled, MvidWarParams} block from the SST sub-struct of NV0073_CTRL_CMD_DP_CONFIG_STREAM_PARAMS between the 535 and 570 releases (compared in OpenRM tags 535.113.01 vs 570.144), shrinking the struct.

Everything nouveau writes sits at identical offsets in both layouts except the trailing SST.bEnableAudioOverRightPanel (written as zero), but the size is wrong on r570, which means r535_sor_dp_sst() and r535_sor_dp_vcpi() are sent with an incorrect size.

Route the .sst/.vcpi IOR functions through nvkm_rm_api_disp the same way bl_ctrl and dp.get_caps/set_indexed_link_rates already are. Keep the existing implementation for r535 and add an r570 implementation built against the 570.144 layout, which already exists in r570/nvrm/disp.h but was unused until now. Also add the NV0073_CTRL_CMD_DP_CONFIG_STREAM define that was missing from the layout.

Other DP controls sent through shared r535 code did not change layout between the tags.

---
 drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/disp.c      |   33 ++++--
 drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/disp.c      |   64 ++++++++++++
 drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/nvrm/disp.h |    2 
 drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/rm.h             |    5 
 4 files changed, 97 insertions(+), 7 deletions(-)
drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/disp.c +26 -7
@@ -400,16 +400,16 @@ r535_sor_dp_audio(struct nvkm_ior *sor,
 		r535_sor_dp_audio_mute(sor, false);
 }
 
-static void
-r535_sor_dp_vcpi(struct nvkm_ior *sor, int head, u8 slot, u8 slot_nr, u16 pbn, u16 aligned_pbn)
+static int
+r535_dp_vcpi(struct nvkm_ior *sor, int head, u8 slot, u8 slot_nr, u16 pbn, u16 aligned_pbn)
 {
 	struct nvkm_disp *disp = sor->disp;
 	struct NV0073_CTRL_CMD_DP_CONFIG_STREAM_PARAMS *ctrl;
 
 	ctrl = nvkm_gsp_rm_ctrl_get(&disp->rm.objcom,
 				    NV0073_CTRL_CMD_DP_CONFIG_STREAM, sizeof(*ctrl));
-	if (WARN_ON(IS_ERR(ctrl)))
-		return;
+	if (IS_ERR(ctrl))
+		return PTR_ERR(ctrl);
 
 	ctrl->subDeviceInstance = 0;
 	ctrl->head = head;
@@ -429,12 +429,20 @@ r535_sor_dp_vcpi(struct nvkm_ior *sor, i
 	ctrl->MST.sendACT = 0;
 	ctrl->MST.singleHeadMSTPipeline = 0;
 	ctrl->MST.bEnableAudioOverRightPanel = 0;
-	WARN_ON(nvkm_gsp_rm_ctrl_wr(&disp->rm.objcom, ctrl));
+	return nvkm_gsp_rm_ctrl_wr(&disp->rm.objcom, ctrl);
+}
+
+static void
+r535_sor_dp_vcpi(struct nvkm_ior *sor, int head, u8 slot, u8 slot_nr, u16 pbn, u16 aligned_pbn)
+{
+	const struct nvkm_rm_api *rmapi = sor->disp->engine.subdev.device->gsp->rm->api;
+
+	WARN_ON(rmapi->disp->dp.vcpi(sor, head, slot, slot_nr, pbn, aligned_pbn));
 }
 
 static int
-r535_sor_dp_sst(struct nvkm_ior *sor, int head, bool ef,
-		u32 watermark, u32 hblanksym, u32 vblanksym)
+r535_dp_sst(struct nvkm_ior *sor, int head, bool ef,
+	    u32 watermark, u32 hblanksym, u32 vblanksym)
 {
 	struct nvkm_disp *disp = sor->disp;
 	struct NV0073_CTRL_CMD_DP_CONFIG_STREAM_PARAMS *ctrl;
@@ -461,6 +469,15 @@ r535_sor_dp_sst(struct nvkm_ior *sor, in
 	return nvkm_gsp_rm_ctrl_wr(&disp->rm.objcom, ctrl);
 }
 
+static int
+r535_sor_dp_sst(struct nvkm_ior *sor, int head, bool ef,
+		u32 watermark, u32 hblanksym, u32 vblanksym)
+{
+	const struct nvkm_rm_api *rmapi = sor->disp->engine.subdev.device->gsp->rm->api;
+
+	return rmapi->disp->dp.sst(sor, head, ef, watermark, hblanksym, vblanksym);
+}
+
 static const struct nvkm_ior_func_dp
 r535_sor_dp = {
 	.sst = r535_sor_dp_sst,
@@ -1781,6 +1798,8 @@ r535_disp = {
 	.dp = {
 		.get_caps = r535_dp_get_caps,
 		.set_indexed_link_rates = r535_dp_set_indexed_link_rates,
+		.sst = r535_dp_sst,
+		.vcpi = r535_dp_vcpi,
 	},
 	.chan = {
 		.set_pushbuf = r535_disp_chan_set_pushbuf,
drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/disp.c +64 -0

82 lines not on this page · raw message

drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/nvrm/disp.h +2 -0
@@ -256,6 +256,8 @@ typedef struct NV0073_CTRL_DP_CTRL_PARAM
     NvU32 eightLaneDpcdBaseAddr;
 } NV0073_CTRL_DP_CTRL_PARAMS;
 
+#define NV0073_CTRL_CMD_DP_CONFIG_STREAM                   (0x731362U) /* finn: Evaluated from "(FINN_NV04_DISPLAY_COMMON_DP_INTERFACE_ID << 8) | NV0073_CTRL_CMD_DP_CONFIG_STREAM_PARAMS_MESSAGE_ID" */
+
 typedef struct NV0073_CTRL_CMD_DP_CONFIG_STREAM_PARAMS {
     NvU32  subDeviceInstance;
     NvU32  head;
drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/rm.h +5 -0
@@ -6,6 +6,7 @@
 #ifndef __NVKM_RM_H__
 #define __NVKM_RM_H__
 #include "handles.h"
+struct nvkm_ior;
 struct nvkm_outp;
 struct r535_gr;
 
@@ -93,6 +94,10 @@ struct nvkm_rm_api {
 		struct {
 			int (*get_caps)(struct nvkm_disp *, int *link_bw, bool *mst, bool *wm);
 			int (*set_indexed_link_rates)(struct nvkm_outp *);
+			int (*sst)(struct nvkm_ior *, int head, bool ef,
+				   u32 watermark, u32 hblanksym, u32 vblanksym);
+			int (*vcpi)(struct nvkm_ior *, int head,
+				    u8 slot, u8 slot_nr, u16 pbn, u16 aligned_pbn);
 		} dp;
 
 		struct {
author maintainer
#410 lore raw

PATCH 410/583 drm/nouveau: unsubscribe the channel-kill event before the fence context

In reply to Greg Kroah-Hartman

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

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

From: Marek Czernohous <[email protected]>

commit 511585987d27d8cb668acebd399fc4deda23404c upstream.

nouveau_channel_del() tears the fence context down first and only drops the channel-kill subscription later, in the middle of the nvif object teardown:

	if (chan->fence)
		nouveau_fence(chan->cli->drm)->context_del(chan);
	...
	nvif_object_dtor(&chan->vram);
	nvif_event_dtor(&chan->kill);

The subscribed handler is nouveau_channel_killed(), which calls nouveau_channel_kill() and from there nouveau_fence_context_kill() on chan->fence. A kill event delivered in that window takes fctx->lock and walks fctx->pending on a fence context that context_del() has already freed.

Nothing reaches this below Fermi today, because the subscription is gated on FERMI_CHANNEL_GPFIFO and nothing kills a channel there. On Fermi and newer the window is real but narrow, since a kill has to land exactly while the channel is being destroyed. That is reason enough on its own, which is why this carries a Fixes: tag. The last patch in this series subscribes Tesla channels as well; nothing kills those today, so it does not widen the exposure now, but it is the groundwork for a recovery path that would, and the ordering is better fixed before that lands than alongside it.

Drop the subscription before anything it depends on is torn down.

Fixes ea13e5abf807 ("drm/nouveau: signal pending fences when channel has been killed")
Assisted-by Claude:claude-opus-5
Fixes ea13e5abf807 ("drm/nouveau: signal pending fences when channel has been killed")
---
 drivers/gpu/drm/nouveau/nouveau_chan.c |    9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)
drivers/gpu/drm/nouveau/nouveau_chan.c +8 -1
@@ -90,6 +90,14 @@ nouveau_channel_del(struct nouveau_chann
 {
 	struct nouveau_channel *chan = *pchan;
 	if (chan) {
+		/*
+		 * Drop the kill-event subscription first.  Its handler
+		 * dereferences chan->fence, which the fence context teardown
+		 * below frees, so leaving it armed across the teardown leaves
+		 * a window for a use-after-free.
+		 */
+		nvif_event_dtor(&chan->kill);
+
 		if (chan->fence)
 			nouveau_fence(chan->cli->drm)->context_del(chan);
 
@@ -100,7 +108,6 @@ nouveau_channel_del(struct nouveau_chann
 		nvif_object_dtor(&chan->nvsw);
 		nvif_object_dtor(&chan->gart);
 		nvif_object_dtor(&chan->vram);
-		nvif_event_dtor(&chan->kill);
 		nvif_object_dtor(&chan->user);
 		nvif_mem_dtor(&chan->mem_userd);
 		nouveau_vma_del(&chan->sema.vma);
author maintainer
#411 lore raw

PATCH 411/583 drm/nouveau: Use write-combined maps for coherent

In reply to Greg Kroah-Hartman

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

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

From: Faith Ekstrand <[email protected]>

commit 774b73428e6eabb4f0382aeeb76e569c7b106a29 upstream.

On Tegra devices, uncached maps translate to device memory, causing unaligned accesses by userspace resulting in a SIGBUS. Instead, use write-combined maps to ensure proper access.

This would also affect discrete cards on any Arm device. It was determined that discrete cards regardless of cpu arch should use write-combined maps for coherent anyways. Thus this change is made for all gpu types.

---
 drivers/gpu/drm/nouveau/nouveau_sgdma.c |    4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)
drivers/gpu/drm/nouveau/nouveau_sgdma.c +1 -3
@@ -72,9 +72,7 @@ nouveau_sgdma_create_ttm(struct ttm_buff
 	struct nouveau_sgdma_be *nvbe;
 	enum ttm_caching caching;
 
-	if (nvbo->force_coherent)
-		caching = ttm_uncached;
-	else if (drm->agp.bridge)
+	if (nvbo->force_coherent || drm->agp.bridge)
 		caching = ttm_write_combined;
 	else
 		caching = ttm_cached;
author maintainer
#412 lore raw

PATCH 412/583 drm/nouveau/uvmm: fix NULL deref unwinding an OP_MAP_SPARSE op

In reply to Greg Kroah-Hartman

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

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

From: Zhenhao Wan <[email protected]>

commit 412a6ceb56d501ef2f8202e26ab4b5d4dfbca566 upstream.

Each bind_job_op is zeroed by kzalloc_obj() in bind_job_op_from_uop(), and the OP_MAP_SPARSE case in nouveau_uvmm_bind_job_submit() only creates a region, so op->ops stays NULL for a successfully processed sparse map.

If a later op in the same job fails, the reverse unwind loop revisits that op and calls drm_gpuva_ops_free(&uvmm->base, op->ops) unconditionally. drm_gpuva_ops_free() dereferences its argument right away (list_for_each_entry_safe on &ops->list), so a NULL op->ops oopses. The path is reachable by any render-node fd holder, since NOUVEAU_VM_BIND is DRM_RENDER_ALLOW.

Guard the free with IS_ERR_OR_NULL(), as nouveau_uvmm_bind_job_cleanup() already does for the identical free.

Fixes b88baab82871 ("drm/nouveau: implement new VM_BIND uAPI")
Assisted-by Claude:claude-opus-5
---
 drivers/gpu/drm/nouveau/nouveau_uvmm.c |    3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/gpu/drm/nouveau/nouveau_uvmm.c +2 -1
@@ -1421,7 +1421,8 @@ unwind:
 			break;
 		}
 
-		drm_gpuva_ops_free(&uvmm->base, op->ops);
+		if (!IS_ERR_OR_NULL(op->ops))
+			drm_gpuva_ops_free(&uvmm->base, op->ops);
 		op->ops = NULL;
 		op->reg = NULL;
 	}
author maintainer
#413 lore raw

PATCH 413/583 drm/nouveau/uvmm: fix premature region free on failed OP_UNMAP_SPARSE

In reply to Greg Kroah-Hartman

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

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

From: Zhenhao Wan <[email protected]>

commit ccf930812f23b8259ef64fd3394d53b093e4651a upstream.

In nouveau_uvmm_bind_job_submit()'s OP_UNMAP_SPARSE arm, op->reg is set from nouveau_uvma_region_find(), which only looks the region up and takes no reference; a region's sole reference is its membership in uvmm->region_mt. Two failure paths leave op->reg set: the -ENOENT check when the region is busy, and the drm_gpuvm_sm_unmap_ops_create() failure. The sibling nouveau_uvmm_sm_unmap_prepare() failure just below clears op->reg; these two do not.

unwind_continue steps back one op, so the failing op is skipped by the unwind loop and its op->reg stays set. nouveau_uvmm_bind_job_cleanup() then enters its if (op->reg) branch and calls nouveau_uvma_region_remove() and nouveau_uvma_region_put() on it, dropping the tree's sole reference and freeing a region this job never created. The comment above the cleanup loop documents the broken invariant: op->reg must be NULL on submit failure.

This frees a live region on an unrelated failure, reachable single-job when drm_gpuvm_sm_unmap_ops_create() returns -ENOMEM; if another job owns the same region, its cleanup then removes and puts the freed region, a use-after-free. Clear op->reg on both failure paths.

Fixes b88baab82871 ("drm/nouveau: implement new VM_BIND uAPI")
Assisted-by Claude:claude-opus-5
---
 drivers/gpu/drm/nouveau/nouveau_uvmm.c |    2 ++
 1 file changed, 2 insertions(+)
drivers/gpu/drm/nouveau/nouveau_uvmm.c +2 -0
@@ -1251,6 +1251,7 @@ nouveau_uvmm_bind_job_submit(struct nouv
 							   op->va.range);
 			if (!op->reg || op->reg->dirty) {
 				ret = -ENOENT;
+				op->reg = NULL;
 				goto unwind_continue;
 			}
 
@@ -1259,6 +1260,7 @@ nouveau_uvmm_bind_job_submit(struct nouv
 								op->va.range);
 			if (IS_ERR(op->ops)) {
 				ret = PTR_ERR(op->ops);
+				op->reg = NULL;
 				goto unwind_continue;
 			}
 
author maintainer
#414 lore raw

PATCH 414/583 drm/nouveau/uvmm: clear the dirty flag when unwinding an OP_UNMAP_SPARSE

In reply to Greg Kroah-Hartman

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

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

From: Zhenhao Wan <[email protected]>

commit 38a62306c4266bcb3cd89e33c7111ee33096ebb3 upstream.

A successful OP_UNMAP_SPARSE marks its region dirty with nouveau_uvma_region_dirty() and defers the teardown to nouveau_uvmm_bind_job_cleanup(); it does not remove the region from uvmm->region_mt.

If a later op in the job fails, the unwind path never clears reg->dirty (set in one place, cleared nowhere) and sets op->reg = NULL, so cleanup skips the teardown. The region is left in the tree with dirty set and its completion never signalled. Later binds over that range then fail permanently -- -ENOENT or -EINVAL from the dirty checks, or an unkillable wait_for_completion() in bind_validate_region() -- for the lifetime of the uvmm.

Clear reg->dirty when the unwind reverts the sparse unmap, restoring the region to the state it was found in.

Fixes b88baab82871 ("drm/nouveau: implement new VM_BIND uAPI")
Assisted-by Claude:claude-opus-5
---
 drivers/gpu/drm/nouveau/nouveau_uvmm.c |    1 +
 1 file changed, 1 insertion(+)
drivers/gpu/drm/nouveau/nouveau_uvmm.c +1 -0
@@ -1407,6 +1407,7 @@ unwind:
 						    op->va.range);
 			break;
 		case OP_UNMAP_SPARSE:
+			op->reg->dirty = false;
 			__nouveau_uvma_region_insert(uvmm, op->reg);
 			nouveau_uvmm_sm_unmap_prepare_unwind(uvmm, &op->new,
 							     op->ops);
author maintainer
#415 lore raw

PATCH 415/583 drm/nouveau/disp: move GSP head-timing ISR and vblank helpers to tu102.c

In reply to Greg Kroah-Hartman

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

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

From: Mohamed Ahmed <[email protected]>

commit c6659e0ffc19b4ef0b3273c185cb8409a154eada upstream.

The GSP-RM display code in rm/r535/disp.c owns a handful of direct MMIO
routines: the head-timing (vblank) interrupt handler and the per-head vblank enable/disable. They program display registers, not RM, so they belong with the rest of the per-chip register code in engine/disp/.

Move them to tu102.c (Turing is the first GSP-capable generation) as tu102_disp_intr() and tu102_head_vblank_get()/put(), exported for rm/r535/disp.c, which keeps calling them by name for now. No functional change.

---
 drivers/gpu/drm/nouveau/nvkm/engine/disp/head.h        |    3 
 drivers/gpu/drm/nouveau/nvkm/engine/disp/priv.h        |    1 
 drivers/gpu/drm/nouveau/nvkm/engine/disp/tu102.c       |   50 ++++++++++++++++
 drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/disp.c |   52 -----------------
 4 files changed, 57 insertions(+), 49 deletions(-)
drivers/gpu/drm/nouveau/nvkm/engine/disp/head.h +3 -0
@@ -56,6 +56,9 @@ int gv100_head_new(struct nvkm_disp *, i
 void gv100_head_state(struct nvkm_head *head, struct nvkm_head_state *state);
 void gv100_head_rgpos(struct nvkm_head *head, u16 *hline, u16 *vline);
 
+void tu102_head_vblank_get(struct nvkm_head *);
+void tu102_head_vblank_put(struct nvkm_head *);
+
 #define HEAD_MSG(h,l,f,a...) do {                                              \
 	struct nvkm_head *_h = (h);                                            \
 	nvkm_##l(&_h->disp->engine.subdev, "head-%d: "f"\n", _h->id, ##a);     \
drivers/gpu/drm/nouveau/nvkm/engine/disp/priv.h +1 -0
@@ -72,6 +72,7 @@ int gv100_disp_wndw_cnt(struct nvkm_disp
 int gv100_disp_caps_new(const struct nvkm_oclass *, void *, u32, struct nvkm_object **);
 
 int tu102_disp_init(struct nvkm_disp *);
+irqreturn_t tu102_disp_intr(struct nvkm_inth *);
 
 void nv50_disp_dptmds_war_2(struct nvkm_disp *, struct dcb_output *);
 void nv50_disp_dptmds_war_3(struct nvkm_disp *, struct dcb_output *);
drivers/gpu/drm/nouveau/nvkm/engine/disp/tu102.c +50 -0

56 lines not on this page · raw message

drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/disp.c +3 -49

74 lines not on this page · raw message

author maintainer
#416 lore raw

PATCH 416/583 drm/nouveau/disp: move the GSP HDMI GCP AVMute write to engine/disp

In reply to Greg Kroah-Hartman

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

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

From: Mohamed Ahmed <[email protected]>

commit eb1ffc3dc72d379a41e367a44b99fb61a15bf8ba upstream.

r535_sor_hdmi_audio() pairs two RM controls (a SET_OD_PACKET carrying the same General Control Packet, and the audio mute-stream toggle) with a direct write of the GCP AVMute bit through the SF GCP unit. The controls are RM and stay, but the direct write is register programming and moves next to the other per-chip display code as tu102_sor_hdmi_gcp(). No functional change.

---
 drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h         |    1 +
 drivers/gpu/drm/nouveau/nvkm/engine/disp/tu102.c       |   15 +++++++++++++++
 drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/disp.c |    9 +--------
 3 files changed, 17 insertions(+), 8 deletions(-)
drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h +1 -0
@@ -194,6 +194,7 @@ void gv100_sor_dp_audio_sym(struct nvkm_
 void gv100_sor_dp_watermark(struct nvkm_ior *, int, u8);
 extern const struct nvkm_ior_func_hda gv100_sor_hda;
 
+void tu102_sor_hdmi_gcp(struct nvkm_ior *, int, bool);
 void tu102_sor_dp_vcpi(struct nvkm_ior *, int, u8, u8, u16, u16);
 
 int nv50_pior_cnt(struct nvkm_disp *, unsigned long *);
drivers/gpu/drm/nouveau/nvkm/engine/disp/tu102.c +15 -0
@@ -30,6 +30,21 @@
 
 #include <nvif/class.h>
 
+/* General Control Packet: bracket an audio enable/disable with AVMute
+ * through the legacy GCP SF unit. Used by the GSP-RM path, which sends the
+ * equivalent packet via RM as well but keeps the direct write in sync.
+ */
+void
+tu102_sor_hdmi_gcp(struct nvkm_ior *sor, int head, bool enable)
+{
+	struct nvkm_device *device = sor->disp->engine.subdev.device;
+	const u32 hdmi = head * 0x400;
+
+	nvkm_mask(device, 0x6f00c0 + hdmi, 0x00000001, 0x00000000);
+	nvkm_wr32(device, 0x6f00cc + hdmi, !enable ? 0x00000001 : 0x00000010);
+	nvkm_mask(device, 0x6f00c0 + hdmi, 0x00000001, 0x00000001);
+}
+
 void
 tu102_sor_dp_vcpi(struct nvkm_ior *sor, int head, u8 slot, u8 slot_nr, u16 pbn, u16 aligned)
 {
drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/disp.c +1 -8
@@ -562,16 +562,9 @@ r535_sor_hdmi_ctrl_audio(struct nvkm_out
 static void
 r535_sor_hdmi_audio(struct nvkm_ior *sor, int head, bool enable)
 {
-	struct nvkm_device *device = sor->disp->engine.subdev.device;
-	const u32 hdmi = head * 0x400;
-
 	r535_sor_hdmi_ctrl_audio(sor->asy.outp, enable);
 	r535_sor_hdmi_ctrl_audio_mute(sor->asy.outp, !enable);
-
-	/* General Control (GCP). */
-	nvkm_mask(device, 0x6f00c0 + hdmi, 0x00000001, 0x00000000);
-	nvkm_wr32(device, 0x6f00cc + hdmi, !enable ? 0x00000001 : 0x00000010);
-	nvkm_mask(device, 0x6f00c0 + hdmi, 0x00000001, 0x00000001);
+	tu102_sor_hdmi_gcp(sor, head, enable);
 }
 
 static void
author maintainer
#417 lore raw

PATCH 417/583 drm/nouveau/disp: route GSP-RM display MMIO through nvkm_disp_func hooks

In reply to Greg Kroah-Hartman

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

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

From: Mohamed Ahmed <[email protected]>

commit 9886aad51f4b5e7082209a153e404bcd8101356c upstream.

The GSP-RM display code in rm/r535/disp.c borrows a few register-programming routines from engine/disp (the head-timing interrupt handler, vblank enables, armed head state and scanout position readback, the AVI/VSI infoframe writers and the GCP AVMute write) and so far picked them by name, which means it has to know which chip it runs on the moment a generation changes any of them.

Give nvkm_disp_func a .gsp table that each chip fills with exactly those hooks, add tu102_gsp_disp (TU1xx) and ga102_gsp_disp (GA10x onwards) carrying the current functions, hand them to r535_disp_new() instead of the full hardware tables, and make rm/r535/disp.c call through the hooks. The head hooks are a whole nvkm_head_func, so r535_head goes away and the chip's own table is handed to nvkm_head_new_(). r535_sor_hdmi gets infoframe forwarders, r535_sor_hdmi_audio() calls the GCP hook, and the interrupt handler comes from the table. The tables are per chip even though the two currently coincide, so a generation that changes a hook only touches its own file.
rm/r535/disp.c no longer contains chip-specific register code, and a new display generation only has to provide its own table. No functional change.

---
 drivers/gpu/drm/nouveau/nvkm/engine/disp/ga102.c       |   13 ++++++-
 drivers/gpu/drm/nouveau/nvkm/engine/disp/head.h        |    1 
 drivers/gpu/drm/nouveau/nvkm/engine/disp/priv.h        |   14 +++++++
 drivers/gpu/drm/nouveau/nvkm/engine/disp/tu102.c       |   21 ++++++++++-
 drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/disp.c |   31 +++++++++--------
 5 files changed, 65 insertions(+), 15 deletions(-)
drivers/gpu/drm/nouveau/nvkm/engine/disp/ga102.c +12 -1
@@ -144,12 +144,23 @@ ga102_disp = {
 	},
 };
 
+static const struct nvkm_disp_func
+ga102_gsp_disp = {
+	.uevent = &gv100_disp_chan_uevent,
+	.ramht_size = 0x2000,
+	.gsp.intr = tu102_disp_intr,
+	.gsp.head = &tu102_gsp_head,
+	.gsp.hdmi_gcp = tu102_sor_hdmi_gcp,
+	.gsp.hdmi_infoframe_avi = gv100_sor_hdmi_infoframe_avi,
+	.gsp.hdmi_infoframe_vsi = gv100_sor_hdmi_infoframe_vsi,
+};
+
 int
 ga102_disp_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst,
 	       struct nvkm_disp **pdisp)
 {
 	if (nvkm_gsp_rm(device->gsp))
-		return r535_disp_new(&ga102_disp, device, type, inst, pdisp);
+		return r535_disp_new(&ga102_gsp_disp, device, type, inst, pdisp);
 
 	return nvkm_disp_new_(&ga102_disp, device, type, inst, pdisp);
 }
drivers/gpu/drm/nouveau/nvkm/engine/disp/head.h +1 -0
@@ -58,6 +58,7 @@ void gv100_head_rgpos(struct nvkm_head *
 
 void tu102_head_vblank_get(struct nvkm_head *);
 void tu102_head_vblank_put(struct nvkm_head *);
+extern const struct nvkm_head_func tu102_gsp_head;
 
 #define HEAD_MSG(h,l,f,a...) do {                                              \
 	struct nvkm_head *_h = (h);                                            \
drivers/gpu/drm/nouveau/nvkm/engine/disp/priv.h +14 -0
@@ -5,6 +5,8 @@
 #include <engine/disp.h>
 #include <core/enum.h>
 struct nvkm_head;
+struct nvkm_head_func;
+struct nvkm_ior;
 struct nvkm_outp;
 struct dcb_output;
 
@@ -34,6 +36,18 @@ struct nvkm_disp_func {
 		int (*new)(struct nvkm_disp *, int id);
 	} wndw, head, dac, sor, pior;
 
+	/* Register programming that the GSP-RM display path (rm/r535) needs from
+	 * the chip, everything else on that path goes through RM. The hooks are
+	 * called unconditionally and the head table is handed to nvkm_head_new_().
+	 */
+	struct {
+		irqreturn_t (*intr)(struct nvkm_inth *);
+		const struct nvkm_head_func *head;
+		void (*hdmi_gcp)(struct nvkm_ior *, int head, bool enable);
+		void (*hdmi_infoframe_avi)(struct nvkm_ior *, int head, void *data, u32 size);
+		void (*hdmi_infoframe_vsi)(struct nvkm_ior *, int head, void *data, u32 size);
+	} gsp;
+
 	u16 ramht_size;
 
 	struct nvkm_sclass root;
drivers/gpu/drm/nouveau/nvkm/engine/disp/tu102.c +20 -1
@@ -140,6 +140,14 @@ tu102_head_vblank_get(struct nvkm_head *
 	nvkm_mask(device, 0x611d80 + (head->id * 4), 0x00000002, 0x00000002);
 }
 
+const struct nvkm_head_func
+tu102_gsp_head = {
+	.state = gv100_head_state,
+	.rgpos = gv100_head_rgpos,
+	.vblank_get = tu102_head_vblank_get,
+	.vblank_put = tu102_head_vblank_put,
+};
+
 static void
 tu102_disp_intr_head_timing(struct nvkm_disp *disp, int head)
 {
@@ -295,12 +303,23 @@ tu102_disp = {
 	},
 };
 
+static const struct nvkm_disp_func
+tu102_gsp_disp = {
+	.uevent = &gv100_disp_chan_uevent,
+	.ramht_size = 0x2000,
+	.gsp.intr = tu102_disp_intr,
+	.gsp.head = &tu102_gsp_head,
+	.gsp.hdmi_gcp = tu102_sor_hdmi_gcp,
+	.gsp.hdmi_infoframe_avi = gv100_sor_hdmi_infoframe_avi,
+	.gsp.hdmi_infoframe_vsi = gv100_sor_hdmi_infoframe_vsi,
+};
+
 int
 tu102_disp_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst,
 	       struct nvkm_disp **pdisp)
 {
 	if (nvkm_gsp_rm(device->gsp))
-		return r535_disp_new(&tu102_disp, device, type, inst, pdisp);
+		return r535_disp_new(&tu102_gsp_disp, device, type, inst, pdisp);
 
 	return nvkm_disp_new_(&tu102_disp, device, type, inst, pdisp);
 }
drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/disp.c +18 -13
@@ -564,7 +564,19 @@ r535_sor_hdmi_audio(struct nvkm_ior *sor
 {
 	r535_sor_hdmi_ctrl_audio(sor->asy.outp, enable);
 	r535_sor_hdmi_ctrl_audio_mute(sor->asy.outp, !enable);
-	tu102_sor_hdmi_gcp(sor, head, enable);
+	sor->disp->func->gsp.hdmi_gcp(sor, head, enable);
+}
+
+static void
+r535_sor_hdmi_infoframe_avi(struct nvkm_ior *sor, int head, void *data, u32 size)
+{
+	sor->disp->func->gsp.hdmi_infoframe_avi(sor, head, data, size);
+}
+
+static void
+r535_sor_hdmi_infoframe_vsi(struct nvkm_ior *sor, int head, void *data, u32 size)
+{
+	sor->disp->func->gsp.hdmi_infoframe_vsi(sor, head, data, size);
 }
 
 static void
@@ -592,8 +604,8 @@ r535_sor_hdmi = {
 	.ctrl = r535_sor_hdmi_ctrl,
 	.scdc = r535_sor_hdmi_scdc,
 	/*TODO: SF_USER -> KMS. */
-	.infoframe_avi = gv100_sor_hdmi_infoframe_avi,
-	.infoframe_vsi = gv100_sor_hdmi_infoframe_vsi,
+	.infoframe_avi = r535_sor_hdmi_infoframe_avi,
+	.infoframe_vsi = r535_sor_hdmi_infoframe_vsi,
 	.audio = r535_sor_hdmi_audio,
 };
 
@@ -618,14 +630,6 @@ r535_sor_cnt(struct nvkm_disp *disp, uns
 	return 4;
 }
 
-static const struct nvkm_head_func
-r535_head = {
-	.state = gv100_head_state,
-	.rgpos = gv100_head_rgpos,
-	.vblank_get = tu102_head_vblank_get,
-	.vblank_put = tu102_head_vblank_put,
-};
-
 static struct nvkm_conn *
 r535_conn_new(struct nvkm_disp *disp, u32 id)
 {
@@ -1623,7 +1627,7 @@ r535_disp_oneinit(struct nvkm_disp *disp
 		nvkm_gsp_rm_ctrl_done(&disp->rm.objcom, ctrl);
 
 		for_each_set_bit(i, &disp->head.mask, disp->head.nr) {
-			ret = nvkm_head_new_(&r535_head, disp, i);
+			ret = nvkm_head_new_(disp->func->gsp.head, disp, i);
 			if (ret)
 				return ret;
 		}
@@ -1672,7 +1676,7 @@ r535_disp_oneinit(struct nvkm_disp *disp
 		return ret;
 
 	ret = nvkm_inth_add(&device->vfn->intr, ret, NVKM_INTR_PRIO_NORMAL, &disp->engine.subdev,
-			    tu102_disp_intr, &disp->engine.subdev.inth);
+			    disp->func->gsp.intr, &disp->engine.subdev.inth);
 	if (ret)
 		return ret;
 
@@ -1704,6 +1708,7 @@ r535_disp_new(const struct nvkm_disp_fun
 	rm->uevent = hw->uevent;
 	rm->sor.cnt = r535_sor_cnt;
 	rm->sor.new = r535_sor_new;
+	rm->gsp = hw->gsp;
 	rm->ramht_size = hw->ramht_size;
 
 	rm->root.oclass = gpu->disp.class.root;
author maintainer
#418 lore raw

PATCH 418/583 drm/nouveau/disp: fix HDMI vendor infoframes on GB20x

In reply to Greg Kroah-Hartman

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

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

From: Mohamed Ahmed <[email protected]>

commit 92f09dcb4e8473ab25764e950994ab7b6abce6dd upstream.

The GSP path reuses the GV100 direct-MMIO infoframe writers on every chip. On GB20x that is only half right as while the legacy SF AVI unit is unchanged, the legacy VSI unit at 0x6f0100 was removed, so gv100_sor_hdmi_infoframe_vsi() writes into a reserved area and no vendor infoframe ever reaches the HW. This affects HDMI-VIC signalling which can impact some 4K modes for legacy HDMI 1.4 sinks.

GB20x (NVDisplay 5.0+) reorganised the SF HDMI packet units. Per NVIDIA's published C971/CA71 DISP_SF_USER class headers, only three legacy units remain (AVI at +0x000, GCP at +0x040, ACR at +0x080), and vendor infoframes must instead be sent through the shared generic infoframe units at +0x130, whose 9-dword packet slots are loaded through the shared data port at +0x3f0/+0x3f4.

Add a VSI writer using the same programming sequence OpenRM uses on these chips (nvhdmipkt_C971.c, programAdvancedInfoframeC971()): disable the unit and wait for it to idle, clear the SENT status, write the packet through the data port with a zero inserted in HB3 after the three header bytes, then enable the unit for every-frame transmission during vblank. Generic unit 1 is used for the VSI, matching the slot assignment in NVIDIA's nvkms (NVHDMIPKT_TYPE_SHARED_GENERIC2, unit 0 is reserved for extended metadata packets and unit 2 for the HDR DRM infoframe, if those are wired up later).

GB20x so far shared GA10x's display entry point. Give it its own, gb202_disp_new(), with a gb202_gsp_disp table that supplies the VSI writer to the GSP path and otherwise carries the same hooks as GA10x. The following fixes fill in the rest of the GB20x differences there.

---
 drivers/gpu/drm/nouveau/include/nvkm/engine/disp.h |    1 
 drivers/gpu/drm/nouveau/nvkm/engine/device/base.c  |   10 +-
 drivers/gpu/drm/nouveau/nvkm/engine/disp/Kbuild    |    1 
 drivers/gpu/drm/nouveau/nvkm/engine/disp/gb202.c   |   88 +++++++++++++++++++++
 4 files changed, 95 insertions(+), 5 deletions(-)
 create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/disp/gb202.c
drivers/gpu/drm/nouveau/include/nvkm/engine/disp.h +1 -0
@@ -87,4 +87,5 @@ int gp102_disp_new(struct nvkm_device *,
 int gv100_disp_new(struct nvkm_device *, enum nvkm_subdev_type, int inst, struct nvkm_disp **);
 int tu102_disp_new(struct nvkm_device *, enum nvkm_subdev_type, int inst, struct nvkm_disp **);
 int ga102_disp_new(struct nvkm_device *, enum nvkm_subdev_type, int inst, struct nvkm_disp **);
+int gb202_disp_new(struct nvkm_device *, enum nvkm_subdev_type, int inst, struct nvkm_disp **);
 #endif
drivers/gpu/drm/nouveau/nvkm/engine/device/base.c +5 -5
@@ -2844,7 +2844,7 @@ nv1b2_chipset = {
 	.pci      = { 0x00000001, gh100_pci_new },
 	.timer    = { 0x00000001, gk20a_timer_new },
 	.vfn      = { 0x00000001, ga100_vfn_new },
-	.disp     = { 0x00000001, ga102_disp_new },
+	.disp     = { 0x00000001, gb202_disp_new },
 	.fifo     = { 0x00000001, ga102_fifo_new },
 };
 
@@ -2860,7 +2860,7 @@ nv1b3_chipset = {
 	.pci      = { 0x00000001, gh100_pci_new },
 	.timer    = { 0x00000001, gk20a_timer_new },
 	.vfn      = { 0x00000001, ga100_vfn_new },
-	.disp     = { 0x00000001, ga102_disp_new },
+	.disp     = { 0x00000001, gb202_disp_new },
 	.fifo     = { 0x00000001, ga102_fifo_new },
 };
 
@@ -2876,7 +2876,7 @@ nv1b5_chipset = {
 	.pci      = { 0x00000001, gh100_pci_new },
 	.timer    = { 0x00000001, gk20a_timer_new },
 	.vfn      = { 0x00000001, ga100_vfn_new },
-	.disp     = { 0x00000001, ga102_disp_new },
+	.disp     = { 0x00000001, gb202_disp_new },
 	.fifo     = { 0x00000001, ga102_fifo_new },
 };
 
@@ -2892,7 +2892,7 @@ nv1b6_chipset = {
 	.pci      = { 0x00000001, gh100_pci_new },
 	.timer    = { 0x00000001, gk20a_timer_new },
 	.vfn      = { 0x00000001, ga100_vfn_new },
-	.disp     = { 0x00000001, ga102_disp_new },
+	.disp     = { 0x00000001, gb202_disp_new },
 	.fifo     = { 0x00000001, ga102_fifo_new },
 };
 
@@ -2908,7 +2908,7 @@ nv1b7_chipset = {
 	.pci      = { 0x00000001, gh100_pci_new },
 	.timer    = { 0x00000001, gk20a_timer_new },
 	.vfn      = { 0x00000001, ga100_vfn_new },
-	.disp     = { 0x00000001, ga102_disp_new },
+	.disp     = { 0x00000001, gb202_disp_new },
 	.fifo     = { 0x00000001, ga102_fifo_new },
 };
 
drivers/gpu/drm/nouveau/nvkm/engine/disp/Kbuild +1 -0
@@ -27,6 +27,7 @@ nvkm-y += nvkm/engine/disp/gp102.o
 nvkm-y += nvkm/engine/disp/gv100.o
 nvkm-y += nvkm/engine/disp/tu102.o
 nvkm-y += nvkm/engine/disp/ga102.o
+nvkm-y += nvkm/engine/disp/gb202.o
 
 nvkm-y += nvkm/engine/disp/udisp.o
 nvkm-y += nvkm/engine/disp/uconn.o
drivers/gpu/drm/nouveau/nvkm/engine/disp/gb202.c new file +88 -0

88 lines not on this page · raw message

author maintainer
#419 lore raw

PATCH 419/583 drm/nouveau/disp: fix HDMI GCP AVMute register offsets on GB20x

In reply to Greg Kroah-Hartman

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

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

From: Mohamed Ahmed <[email protected]>

commit 764deff8450c9a83e335c17c32ea258ec25bb71e upstream.

The GSP path brackets audio enablement with a General Control Packet AVMute toggle. r535_sor_hdmi_audio() calls the gsp.hdmi_gcp hook, which every chip so far serves with tu102_sor_hdmi_gcp() and the legacy GCP unit at 0x6f00c0/0x6f00cc. On GB20x the SF packet units were compacted and the old generic and VSI units are gone (ACR keeps slot 2) and the GCP unit moved from slot 3 to slot 1 (control 0x6f0040 and subpack 0x6f004c from NVIDIA's published clc971.h. The same offsets are also used by OpenRM's hdmiWriteGeneralCtrlPacketC871() on these chips). The old addresses are reserved on GB20x, so the AVMute writes were silent no-ops and mitigated only by the equivalent GCP r535_sor_hdmi_audio() already sends through the SET_OD_PACKET RM control.

Add a GB20x GCP writer using the new offsets and hook it into gb202_gsp_disp, keeping the direct MMIO path in sync with the hardware as on earlier chips.

Only SB0 (the AVMute bit) is written. On NVD5.0 the subpack register also carries SB1_CTRL (bit 24), which selects where the deep-color CD/PP fields are generated (hardware or from the driver, with the default being HW). hdmiWriteGeneralCtrlPacketC871() likewise writes only SB0-SB2.

---
 .../gpu/drm/nouveau/nvkm/engine/disp/gb202.c  | 20 ++++++++++++++++++-
 1 file changed, 19 insertions(+), 1 deletion(-)
drivers/gpu/drm/nouveau/nvkm/engine/disp/gb202.c +19 -1
index 1e40de83e2bb..face801af080 100644
@@ -65,6 +65,24 @@ gb202_sor_hdmi_infoframe_vsi(struct nvkm_ior *ior, int head, void *data, u32 siz
 	nvkm_wr32(device, 0x6f03f8 + hoff, 0x00000002);
 }
 
+/* General Control Packet AVMute bracket. The GCP unit moved to slot 1 on
+ * NVD5.0. Only SB0 (the AVMute bit) is ours to write so we must not do a
+ * full write here: SB1 carries the deep-color CD/PP fields, and SB1_CTRL
+ * (bit 24, new with clc871.h) controls where their generation happens (HW
+ * or driver) on these chips, with the default being HW.
+ */
+static void
+gb202_sor_hdmi_gcp(struct nvkm_ior *sor, int head, bool enable)
+{
+	struct nvkm_device *device = sor->disp->engine.subdev.device;
+	const u32 hdmi = head * 0x400;
+
+	nvkm_mask(device, 0x6f0040 + hdmi, 0x00000001, 0x00000000);
+	nvkm_mask(device, 0x6f004c + hdmi, 0x000000ff, !enable ? 0x00000001 :
+								 0x00000010);
+	nvkm_mask(device, 0x6f0040 + hdmi, 0x00000001, 0x00000001);
+}
+
 /* GB20x is GSP-only. This table supplies the register programming the
  * GSP-RM display path needs from the chip.
  */
@@ -74,7 +92,7 @@ gb202_gsp_disp = {
 	.ramht_size = 0x2000,
 	.gsp.intr = tu102_disp_intr,
 	.gsp.head = &tu102_gsp_head,
-	.gsp.hdmi_gcp = tu102_sor_hdmi_gcp,
+	.gsp.hdmi_gcp = gb202_sor_hdmi_gcp,
 	/* The legacy AVI unit is unchanged on GB20x. */
 	.gsp.hdmi_infoframe_avi = gv100_sor_hdmi_infoframe_avi,
 	.gsp.hdmi_infoframe_vsi = gb202_sor_hdmi_infoframe_vsi,
Signature · 1 line
2.55.0
author maintainer
#420 lore raw

PATCH 420/583 drm/nouveau/disp: fix head state readback on GB20x

In reply to Greg Kroah-Hartman

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

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

From: Mohamed Ahmed <[email protected]>

commit 9421dfe912e55360e6b9301a110acb00df7e7320 upstream.

The GSP path reads armed head state and the RG scanout position through gv100_head_state() and gv100_head_rgpos() on every generation. gv100_head_state() reads the core channel's state mirror at a 0x400 per-head stride, which NVD5.0 (GB20x) doubled. Per NVIDIA's published CA7D class header every HEAD_SET method sits at 0x2000 + head * 0x800, while the mirror bases are unchanged (assembly at 0x680000, armed at +0x8000, per OpenRM's v03_00 channel-user-base HAL which is still used on DISPv0502).

Add gb202_head_state(), the same readback at the 0x800 stride, and a gb202_gsp_head table to supply it.
gv100_head_rgpos() is kept. The RG registers keep their per-head 0x800 stride on NVD5.0, and OpenRM's kdispReadRgLineCountAndFrameCount_v03_00 still reads NV_PDISP_RG_DPCA on DISPv0502.

---
 .../gpu/drm/nouveau/nvkm/engine/disp/gb202.c  | 51 ++++++++++++++++++-
 1 file changed, 50 insertions(+), 1 deletion(-)
drivers/gpu/drm/nouveau/nvkm/engine/disp/gb202.c +50 -1
index face801af080..765c42039a47 100644
@@ -83,6 +83,55 @@ gb202_sor_hdmi_gcp(struct nvkm_ior *sor, int head, bool enable)
 	nvkm_mask(device, 0x6f0040 + hdmi, 0x00000001, 0x00000001);
 }
 
+/* Same core-channel state mirror as gv100_head_state() (assembly at 0x680000,
+ * armed at +0x8000, per-head method offsets unchanged), but NVD5.0 spaces
+ * heads 0x800 apart (see NVCA7D_HEAD_SET_*(a) in clca7d.h).
+ */
+static void
+gb202_head_state(struct nvkm_head *head, struct nvkm_head_state *state)
+{
+	struct nvkm_device *device = head->disp->engine.subdev.device;
+	const u32 hoff = (state == &head->arm) * 0x8000 + head->id * 0x800;
+	u32 data;
+
+	data = nvkm_rd32(device, 0x682064 + hoff);
+	state->vtotal = (data & 0xffff0000) >> 16;
+	state->htotal = (data & 0x0000ffff);
+	data = nvkm_rd32(device, 0x682068 + hoff);
+	state->vsynce = (data & 0xffff0000) >> 16;
+	state->hsynce = (data & 0x0000ffff);
+	data = nvkm_rd32(device, 0x68206c + hoff);
+	state->vblanke = (data & 0xffff0000) >> 16;
+	state->hblanke = (data & 0x0000ffff);
+	data = nvkm_rd32(device, 0x682070 + hoff);
+	state->vblanks = (data & 0xffff0000) >> 16;
+	state->hblanks = (data & 0x0000ffff);
+	/* Bit 31 is ADJ1000DIV1001, not a HERTZ bit. We don't have enough bits
+	 * to add the full clock in hz on Blackwell (35 bits), but state->hz
+	 * is unused and obsolete under GSP so this is fine.
+	 */
+	state->hz = nvkm_rd32(device, 0x68200c + hoff) & 0x7fffffff;
+
+	data = nvkm_rd32(device, 0x682004 + hoff);
+	switch ((data & 0x000000f0) >> 4) {
+	case 5: state->or.depth = 30; break;
+	case 4: state->or.depth = 24; break;
+	case 1: state->or.depth = 18; break;
+	default:
+		state->or.depth = 18;
+		WARN_ON(1);
+		break;
+	}
+}
+
+static const struct nvkm_head_func
+gb202_gsp_head = {
+	.state = gb202_head_state,
+	.rgpos = gv100_head_rgpos,
+	.vblank_get = tu102_head_vblank_get,
+	.vblank_put = tu102_head_vblank_put,
+};
+
 /* GB20x is GSP-only. This table supplies the register programming the
  * GSP-RM display path needs from the chip.
  */
@@ -91,7 +140,7 @@ gb202_gsp_disp = {
 	.uevent = &gv100_disp_chan_uevent,
 	.ramht_size = 0x2000,
 	.gsp.intr = tu102_disp_intr,
-	.gsp.head = &tu102_gsp_head,
+	.gsp.head = &gb202_gsp_head,
 	.gsp.hdmi_gcp = gb202_sor_hdmi_gcp,
 	/* The legacy AVI unit is unchanged on GB20x. */
 	.gsp.hdmi_infoframe_avi = gv100_sor_hdmi_infoframe_avi,
Signature · 1 line
2.55.0
author maintainer
#421 lore raw

PATCH 421/583 drm/nouveau/gsp: fix vblank interrupts on GB20x

In reply to Greg Kroah-Hartman

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

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

From: Mohamed Ahmed <[email protected]>

commit 5bb489b333237c1bf63a891a4362986253a0060a upstream.

The GSP path programs per-head timing (vblank) interrupts the same way on every generation. NVD5.0 (GB20x) reworked the FE interrupt frontend around four message-based kernel vectors (high latency, low latency, PMU, and GSP) and moved RM head-timing interrupts to the dedicated low-latency vector:

- The enable is NV_PDISP_FE_RM_INTR_EN1_HEAD_TIMING, 0x611ef0 + head*4 (570.144 kernel_head_0501.c, renamed kernel_head_0502.c from 575.51.02 on, and v05_01 dev_disp.h).

- The vector is reported as a separate interrupt table entry, MC_ENGINE_IDX_DISP_LOW (intr_gb202.c, intrCacheDispIntrVectors).

- The vector must be re-armed through NV_PDISP_FE_INTR_RETRIGGER(1) at 0x611f34 after servicing (kdispServiceInterrupt -> kdispIntrRetrigger_v05_01).

The event latch (0x611800), per-head status (0x611c00), and dispatch summary (0x611ec0) the interrupt handler uses are unchanged on GB20x (kheadReadPendingVblank_v03_00 and kheadResetPendingLastData_v03_00 remain for DISPv0502+).

On GB20x the old code enables head timing onto the legacy vector, leaves its handler there, and never re-arms the message-based vectors. Page flips still complete (nv50 sends those events from the commit path), so the desktop looks fine while DRM vblank waits and vblank sequence queries are affected.

Supply GB20x vblank enables and an interrupt handler that re-arms the vector after servicing through gb202_gsp_disp, translate the low-latency interrupt table entry as a second NVKM_ENGINE_DISP instance, and add a gsp.intr_low_latency flag so r535_disp_oneinit() attaches the handler to that instance. GB20x was the last cross-file user of the TU1xx vblank enables, so make those static and drop their head.h prototypes.

---
 .../gpu/drm/nouveau/nvkm/engine/disp/gb202.c  | 42 +++++++++++++++++--
 .../gpu/drm/nouveau/nvkm/engine/disp/head.h   |  2 -
 .../gpu/drm/nouveau/nvkm/engine/disp/priv.h   |  2 +
 .../gpu/drm/nouveau/nvkm/engine/disp/tu102.c  |  4 +-
 .../nouveau/nvkm/subdev/gsp/rm/r535/disp.c    | 10 ++++-
 .../drm/nouveau/nvkm/subdev/gsp/rm/r570/gsp.c |  9 ++++
 6 files changed, 61 insertions(+), 8 deletions(-)
drivers/gpu/drm/nouveau/nvkm/engine/disp/gb202.c +39 -3
index 765c42039a47..d0360610f9fa 100644
@@ -124,12 +124,46 @@ gb202_head_state(struct nvkm_head *head, struct nvkm_head_state *state)
 	}
 }
 
+/* NVD5.0 (GB20x and later) moved the RM head-timing interrupt enable to
+ * the low-latency vector's EN1 block. The event latch is unchanged.
+ */
+static void
+gb202_head_vblank_put(struct nvkm_head *head)
+{
+	struct nvkm_device *device = head->disp->engine.subdev.device;
+
+	nvkm_mask(device, 0x611ef0 + (head->id * 4), 0x00000002, 0x00000000);
+}
+
+static void
+gb202_head_vblank_get(struct nvkm_head *head)
+{
+	struct nvkm_device *device = head->disp->engine.subdev.device;
+
+	nvkm_wr32(device, 0x611800 + (head->id * 4), 0x00000002);
+	nvkm_mask(device, 0x611ef0 + (head->id * 4), 0x00000002, 0x00000002);
+}
+
+static irqreturn_t
+gb202_disp_intr(struct nvkm_inth *inth)
+{
+	struct nvkm_disp *disp = container_of(inth, typeof(*disp), engine.subdev.inth);
+	irqreturn_t ret = tu102_disp_intr(inth);
+
+	/* The FE interrupt vectors are message-based on NVD5.0. Re-arm the
+	 * low-latency vector so it fires again for any event that latched
+	 * while we were servicing.
+	 */
+	nvkm_wr32(disp->engine.subdev.device, 0x611f34, 0x00000001);
+	return ret;
+}
+
 static const struct nvkm_head_func
 gb202_gsp_head = {
 	.state = gb202_head_state,
 	.rgpos = gv100_head_rgpos,
-	.vblank_get = tu102_head_vblank_get,
-	.vblank_put = tu102_head_vblank_put,
+	.vblank_get = gb202_head_vblank_get,
+	.vblank_put = gb202_head_vblank_put,
 };
 
 /* GB20x is GSP-only. This table supplies the register programming the
@@ -139,7 +173,9 @@ static const struct nvkm_disp_func
 gb202_gsp_disp = {
 	.uevent = &gv100_disp_chan_uevent,
 	.ramht_size = 0x2000,
-	.gsp.intr = tu102_disp_intr,
+	/* Head timing arrives on the dedicated low-latency vector. */
+	.gsp.intr = gb202_disp_intr,
+	.gsp.intr_low_latency = true,
 	.gsp.head = &gb202_gsp_head,
 	.gsp.hdmi_gcp = gb202_sor_hdmi_gcp,
 	/* The legacy AVI unit is unchanged on GB20x. */
drivers/gpu/drm/nouveau/nvkm/engine/disp/head.h +0 -2
index 784521c2aca1..5976498da909 100644
@@ -56,8 +56,6 @@ int gv100_head_new(struct nvkm_disp *, int id);
 void gv100_head_state(struct nvkm_head *head, struct nvkm_head_state *state);
 void gv100_head_rgpos(struct nvkm_head *head, u16 *hline, u16 *vline);
 
-void tu102_head_vblank_get(struct nvkm_head *);
-void tu102_head_vblank_put(struct nvkm_head *);
 extern const struct nvkm_head_func tu102_gsp_head;
 
 #define HEAD_MSG(h,l,f,a...) do {                                              \
drivers/gpu/drm/nouveau/nvkm/engine/disp/priv.h +2 -0
index a9dbda67a7d4..fde321dbd7c8 100644
@@ -42,6 +42,8 @@ struct nvkm_disp_func {
 	 */
 	struct {
 		irqreturn_t (*intr)(struct nvkm_inth *);
+		/* Head-timing interrupts arrive on a second DISP vector. */
+		bool intr_low_latency;
 		const struct nvkm_head_func *head;
 		void (*hdmi_gcp)(struct nvkm_ior *, int head, bool enable);
 		void (*hdmi_infoframe_avi)(struct nvkm_ior *, int head, void *data, u32 size);
drivers/gpu/drm/nouveau/nvkm/engine/disp/tu102.c +2 -2
index 948b1d2f954c..f6c163072ff6 100644
@@ -123,7 +123,7 @@ tu102_sor_new(struct nvkm_disp *disp, int id)
  * enables to us. These program the RM head-timing line (bit 1 of the
  * per-head enable, not the bit nvkm's own gv100 path uses).
  */
-void
+static void
 tu102_head_vblank_put(struct nvkm_head *head)
 {
 	struct nvkm_device *device = head->disp->engine.subdev.device;
@@ -131,7 +131,7 @@ tu102_head_vblank_put(struct nvkm_head *head)
 	nvkm_mask(device, 0x611d80 + (head->id * 4), 0x00000002, 0x00000000);
 }
 
-void
+static void
 tu102_head_vblank_get(struct nvkm_head *head)
 {
 	struct nvkm_device *device = head->disp->engine.subdev.device;
drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/disp.c +9 -1
index 7d1d4ee2af79..f5f22173fc2c 100644
@@ -1671,7 +1671,15 @@ r535_disp_oneinit(struct nvkm_disp *disp)
 	if (ret)
 		return ret;
 
-	ret = nvkm_gsp_intr_stall(gsp, disp->engine.subdev.type, disp->engine.subdev.inst);
+	/* Chips that raise head-timing interrupts on a separate low-latency
+	 * vector report it as a second DISP interrupt table entry, exposed
+	 * as instance 1 by the RM engine-index translation (see
+	 * r570_gsp_xlat_mc_engine_idx()). Their high-latency vector
+	 * (instance 0) is left unhandled as no event nouveau enables is
+	 * routed to it, and without a handler it stays masked.
+	 */
+	ret = nvkm_gsp_intr_stall(gsp, disp->engine.subdev.type,
+				  disp->func->gsp.intr_low_latency ? 1 : disp->engine.subdev.inst);
 	if (ret < 0)
 		return ret;
 
drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/gsp.c +9 -0
index 996941c668ba..1488771c63fc 100644
@@ -44,6 +44,15 @@ r570_gsp_xlat_mc_engine_idx(u32 mc_engine_idx, enum nvkm_subdev_type *ptype, int
 		*ptype = NVKM_ENGINE_DISP;
 		*pinst = 0;
 		return true;
+	case MC_ENGINE_IDX_DISP_LOW:
+		/* GB20x+ report a separate low-latency display vector, used
+		 * for head-timing interrupts. Expose it as a second DISP
+		 * interrupt instance. r535_disp_oneinit() attaches the
+		 * handler to it when the chip's gsp.intr_low_latency is set.
+		 */
+		*ptype = NVKM_ENGINE_DISP;
+		*pinst = 1;
+		return true;
 	case MC_ENGINE_IDX_CE0 ... MC_ENGINE_IDX_CE19:
 		*ptype = NVKM_ENGINE_CE;
 		*pinst = mc_engine_idx - MC_ENGINE_IDX_CE0;
Signature · 1 line
2.55.0
author maintainer
#422 lore raw

PATCH 422/583 ksmbd: fix use-after-free in oplock break notification

In reply to Greg Kroah-Hartman

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

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

From: Abdifatah Suruur <[email protected]>

commit 0e753899627b5e28a9fea8bca98262a6f65a2452 upstream.

smb2_oplock_break_noti() reads opinfo->conn without any lock and dereferences it after two allocations which may sleep. When the durable handle owning the oplock is disconnected, session_fd_check() clears opinfo->conn and drops its conn reference under ci->m_lock, and the last ksmbd_conn_put() frees the connection. A break triggered by another connection that races with the teardown can then resurrect the freed connection: ksmbd_conn_get() is a plain atomic_inc, and the queued break work later dereferences the stale conn via ksmbd_conn_write(), a use-after-free reachable by any authenticated client holding a durable batch oplock.

Thread the caller's inode into the notification path instead of taking a new reference on it. Every caller of oplock_break() already holds a live ksmbd_file (or an explicit ksmbd_inode_lookup_lock() reference, in the parent lease break paths) on the inode that owns the break target's oplock list, so ci cannot be freed during the call, and its lock can be taken without dereferencing opinfo->o_fp, which a concurrent close may free. Select and pin the connection under ci->m_lock, the same lock session_fd_check() and ksmbd_reopen_durable_fd() use to update opinfo->conn, so a concurrent detach either loses the race to the clear or keeps the connection alive until the notification work releases it. Transfer the reference to the work item and release it on allocation failures.

Fixes b003086d7696 ("ksmbd: fix NULL-deref of opinfo->conn in oplock/lease break notifiers")
---
 fs/smb/server/oplock.c |   61 +++++++++++++++++++++++++++++++++++++++----------
 1 file changed, 49 insertions(+), 12 deletions(-)
fs/smb/server/oplock.c +49 -12
@@ -708,31 +708,67 @@ out:
 	ksmbd_conn_put(conn);
 }
 
+/*
+ * Select and pin the connection used for an oplock break before doing any
+ * allocations which may sleep.  The caller of oplock_break() holds a live
+ * reference on ci (a file being opened, a file being operated on, or an
+ * explicit ksmbd_inode_lookup_lock() reference in the parent lease break
+ * paths), so the inode cannot be freed during the call.
+ *
+ * opinfo->conn is cleared under ci->m_lock by session_fd_check() when the
+ * durable handle owning the oplock is disconnected, reassigned by
+ * ksmbd_reopen_durable_fd() under the same lock, and the last
+ * ksmbd_conn_put() of the old connection frees it.  Holding the read lock
+ * excludes both writers, so the connection cannot be freed while it is
+ * selected.
+ */
+static struct ksmbd_conn *smb2_oplock_break_conn_get(struct oplock_info *opinfo,
+						     struct ksmbd_inode *ci)
+{
+	struct ksmbd_conn *conn;
+
+	down_read(&ci->m_lock);
+	conn = READ_ONCE(opinfo->conn);
+	if (conn && !ksmbd_conn_releasing(conn))
+		conn = ksmbd_conn_get(conn);
+	else
+		conn = NULL;
+	up_read(&ci->m_lock);
+
+	return conn;
+}
+
 /**
  * smb2_oplock_break_noti() - send smb2 exclusive/batch to level2 oplock
  *		break command from server to client
  * @opinfo:		oplock info object
+ * @ci:		inode owning the break target's oplock list, pinned by
+ *		the caller
  *
  * Return:      0 on success, otherwise error
  */
-static int smb2_oplock_break_noti(struct oplock_info *opinfo)
+static int smb2_oplock_break_noti(struct oplock_info *opinfo,
+				  struct ksmbd_inode *ci)
 {
 	struct ksmbd_conn *conn;
 	struct oplock_break_info *br_info;
 	int ret = 0;
 	struct ksmbd_work *work;
 
-	conn = READ_ONCE(opinfo->conn);
+	conn = smb2_oplock_break_conn_get(opinfo, ci);
 	if (!conn)
 		return 0;
 
 	work = ksmbd_alloc_work_struct();
-	if (!work)
+	if (!work) {
+		ksmbd_conn_put(conn);
 		return -ENOMEM;
+	}
 
 	br_info = kmalloc(sizeof(struct oplock_break_info), KSMBD_DEFAULT_GFP);
 	if (!br_info) {
 		ksmbd_free_work_struct(work);
+		ksmbd_conn_put(conn);
 		return -ENOMEM;
 	}
 
@@ -741,7 +777,8 @@ static int smb2_oplock_break_noti(struct
 	br_info->open_trunc = opinfo->open_trunc;
 
 	work->request_buf = (char *)br_info;
-	work->conn = ksmbd_conn_get(conn);
+	/* Transfer the reference acquired by smb2_oplock_break_conn_get(). */
+	work->conn = conn;
 	work->sess = opinfo->sess;
 
 	ksmbd_conn_r_count_inc(conn);
@@ -890,8 +927,8 @@ static void wait_lease_breaking(struct o
 	}
 }
 
-static int oplock_break(struct oplock_info *brk_opinfo, int req_op_level,
-			struct ksmbd_work *in_work)
+static int oplock_break(struct oplock_info *brk_opinfo, struct ksmbd_inode *ci,
+			int req_op_level, struct ksmbd_work *in_work)
 {
 	int err = 0;
 
@@ -957,7 +994,7 @@ static int oplock_break(struct oplock_in
 	if (brk_opinfo->is_lease)
 		err = smb2_lease_break_noti(brk_opinfo);
 	else
-		err = smb2_oplock_break_noti(brk_opinfo);
+		err = smb2_oplock_break_noti(brk_opinfo, ci);
 
 	ksmbd_debug(OPLOCK, "oplock granted = %d\n", brk_opinfo->level);
 	if (brk_opinfo->op_state == OPLOCK_CLOSING)
@@ -1137,7 +1174,7 @@ void smb_send_parent_lease_break_noti(st
 				continue;
 			}
 
-			oplock_break(opinfo, SMB2_OPLOCK_LEVEL_NONE, NULL);
+			oplock_break(opinfo, p_ci, SMB2_OPLOCK_LEVEL_NONE, NULL);
 			opinfo_put(opinfo);
 		}
 	}
@@ -1178,7 +1215,7 @@ void smb_lazy_parent_lease_break_close(s
 				continue;
 			}
 
-			oplock_break(opinfo, SMB2_OPLOCK_LEVEL_NONE, NULL);
+			oplock_break(opinfo, p_ci, SMB2_OPLOCK_LEVEL_NONE, NULL);
 			opinfo_put(opinfo);
 		}
 	}
@@ -1280,7 +1317,7 @@ int smb_grant_oplock(struct ksmbd_work *
 		goto op_break_not_needed;
 	}
 
-	err = oplock_break(prev_opinfo, SMB2_OPLOCK_LEVEL_II, work);
+	err = oplock_break(prev_opinfo, fp->f_ci, SMB2_OPLOCK_LEVEL_II, work);
 	opinfo_put(prev_opinfo);
 	if (err == -ENOENT)
 		goto set_lev;
@@ -1366,7 +1403,7 @@ static void smb_break_all_write_oplock(s
 	}
 
 	brk_opinfo->open_trunc = is_trunc;
-	oplock_break(brk_opinfo, SMB2_OPLOCK_LEVEL_II, work);
+	oplock_break(brk_opinfo, fp->f_ci, SMB2_OPLOCK_LEVEL_II, work);
 	opinfo_put(brk_opinfo);
 }
 
@@ -1430,7 +1467,7 @@ void smb_break_all_levII_oplock(struct k
 			    SMB2_LEASE_KEY_SIZE))
 			goto next;
 		brk_op->open_trunc = is_trunc;
-		oplock_break(brk_op, SMB2_OPLOCK_LEVEL_NONE, NULL);
+		oplock_break(brk_op, ci, SMB2_OPLOCK_LEVEL_NONE, NULL);
 next:
 		opinfo_put(brk_op);
 	}
author maintainer
#423 lore raw

PATCH 423/583 afs: Fix leak of ungot volume

In reply to Greg Kroah-Hartman

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

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

From: David Howells <[email protected]>

commit d672c276f685a540ed2b2a8bafaed4650a89022c upstream.

Fix afs_lookup_volume_rcu() so that it doesn't leak a dying volume if afs_try_get_volume() fails.

---
 fs/afs/callback.c |    1 +
 1 file changed, 1 insertion(+)
fs/afs/callback.c +1 -0
@@ -138,6 +138,7 @@ static struct afs_volume *afs_lookup_vol
 
 		if (volume && afs_try_get_volume(volume, afs_volume_trace_get_callback))
 			break;
+		volume = NULL;
 		if (!need_seqretry(&cell->volume_lock, seq))
 			break;
 	}
author maintainer
#424 lore raw

PATCH 424/583 usb: xhci: add tracing for PORTSC register writes

In reply to Greg Kroah-Hartman

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

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

From: Niklas Neronin <[email protected]>

[ Upstream commit 829738e59f1fad90ef7b63d6a1a4de9d5d22544a ]

Introduce a dedicated write function for the USB Port Register Set (PORTSC) that includes tracing capabilities for values written to the PORTSC register. This enhancement minimizes code duplication and improves debugging.

The PORTSC register is part of the Host Controller USB Port Register Set, comprising 4 x 32-bit registers. As the first register, PORTSC is accessed directly via 'port->addr'. Future commits will introduce a dedicated Port register struct to further streamline access.
By adding the xhci_portsc_writel() function prior to these changes, we significantly reduce the number of same line modifications required.

Stable-dep-of: 78203d5b54a4 ("usb: xhci: bail out of setup if the controller is inaccessible")

---
 drivers/usb/host/xhci-debugfs.c |    2 +-
 drivers/usb/host/xhci-hub.c     |   33 ++++++++++++++++-----------------
 drivers/usb/host/xhci-pci.c     |    2 +-
 drivers/usb/host/xhci-trace.h   |    5 +++++
 drivers/usb/host/xhci.c         |    9 ++++++++-
 drivers/usb/host/xhci.h         |    1 +
 6 files changed, 32 insertions(+), 20 deletions(-)
drivers/usb/host/xhci-debugfs.c +1 -1
@@ -367,7 +367,7 @@ static ssize_t xhci_port_write(struct fi
 		portsc = xhci_port_state_to_neutral(portsc);
 		portsc &= ~PORT_PLS_MASK;
 		portsc |= PORT_LINK_STROBE | XDEV_COMP_MODE;
-		writel(portsc, port->addr);
+		xhci_portsc_writel(port, portsc);
 		spin_unlock_irqrestore(&xhci->lock, flags);
 	} else {
 		return -EINVAL;
drivers/usb/host/xhci-hub.c +16 -17
@@ -570,7 +570,7 @@ static void xhci_disable_port(struct xhc
 	portsc = xhci_port_state_to_neutral(portsc);
 
 	/* Write 1 to disable the port */
-	writel(portsc | PORT_PE, port->addr);
+	xhci_portsc_writel(port, portsc | PORT_PE);
 
 	portsc = readl(port->addr);
 	xhci_dbg(xhci, "disable port %d-%d, portsc: 0x%x\n",
@@ -578,7 +578,7 @@ static void xhci_disable_port(struct xhc
 }
 
 static void xhci_clear_port_change_bit(struct xhci_hcd *xhci, u16 wValue,
-		u16 wIndex, __le32 __iomem *addr, u32 port_status)
+		u16 wIndex, struct xhci_port *port, u32 port_status)
 {
 	char *port_change_bit;
 	u32 status;
@@ -621,8 +621,8 @@ static void xhci_clear_port_change_bit(s
 		return;
 	}
 	/* Change bits are all write 1 to clear */
-	writel(port_status | status, addr);
-	port_status = readl(addr);
+	xhci_portsc_writel(port, port_status | status);
+	port_status = readl(port->addr);
 
 	xhci_dbg(xhci, "clear port%d %s change, portsc: 0x%x\n",
 		 wIndex + 1, port_change_bit, port_status);
@@ -659,11 +659,11 @@ static void xhci_set_port_power(struct x
 
 	if (on) {
 		/* Power on */
-		writel(temp | PORT_POWER, port->addr);
+		xhci_portsc_writel(port, temp | PORT_POWER);
 		readl(port->addr);
 	} else {
 		/* Power off */
-		writel(temp & ~PORT_POWER, port->addr);
+		xhci_portsc_writel(port, temp & ~PORT_POWER);
 	}
 
 	spin_unlock_irqrestore(&xhci->lock, *flags);
@@ -805,7 +805,7 @@ void xhci_set_link_state(struct xhci_hcd
 	temp = xhci_port_state_to_neutral(portsc);
 	temp &= ~PORT_PLS_MASK;
 	temp |= PORT_LINK_STROBE | link_state;
-	writel(temp, port->addr);
+	xhci_portsc_writel(port, temp);
 
 	xhci_dbg(xhci, "Set port %d-%d link state, portsc: 0x%x, write 0x%x",
 		 port->rhub->hcd->self.busnum, port->hcd_portnum + 1,
@@ -835,7 +835,7 @@ static void xhci_set_remote_wake_mask(st
 	else
 		temp &= ~PORT_WKOC_E;
 
-	writel(temp, port->addr);
+	xhci_portsc_writel(port, temp);
 }
 
 /* Test and clear port RWC bit */
@@ -848,7 +848,7 @@ void xhci_test_and_clear_bit(struct xhci
 	if (temp & port_bit) {
 		temp = xhci_port_state_to_neutral(temp);
 		temp |= port_bit;
-		writel(temp, port->addr);
+		xhci_portsc_writel(port, temp);
 	}
 }
 
@@ -1371,7 +1371,7 @@ int xhci_hub_control(struct usb_hcd *hcd
 				temp |= PORT_CSC | PORT_PEC | PORT_WRC |
 					PORT_OCC | PORT_RC | PORT_PLC |
 					PORT_CEC;
-				writel(temp | PORT_PE, port->addr);
+				xhci_portsc_writel(port, temp | PORT_PE);
 				temp = readl(port->addr);
 				break;
 			}
@@ -1500,7 +1500,7 @@ int xhci_hub_control(struct usb_hcd *hcd
 			break;
 		case USB_PORT_FEAT_RESET:
 			temp = (temp | PORT_RESET);
-			writel(temp, port->addr);
+			xhci_portsc_writel(port, temp);
 
 			temp = readl(port->addr);
 			xhci_dbg(xhci, "set port reset, actual port %d-%d status  = 0x%x\n",
@@ -1514,7 +1514,7 @@ int xhci_hub_control(struct usb_hcd *hcd
 			break;
 		case USB_PORT_FEAT_BH_PORT_RESET:
 			temp |= PORT_WR;
-			writel(temp, port->addr);
+			xhci_portsc_writel(port, temp);
 			temp = readl(port->addr);
 			break;
 		case USB_PORT_FEAT_U1_TIMEOUT:
@@ -1603,8 +1603,7 @@ int xhci_hub_control(struct usb_hcd *hcd
 		case USB_PORT_FEAT_C_ENABLE:
 		case USB_PORT_FEAT_C_PORT_LINK_STATE:
 		case USB_PORT_FEAT_C_PORT_CONFIG_ERROR:
-			xhci_clear_port_change_bit(xhci, wValue, wIndex,
-					port->addr, temp);
+			xhci_clear_port_change_bit(xhci, wValue, wIndex, port, temp);
 			break;
 		case USB_PORT_FEAT_ENABLE:
 			xhci_disable_port(xhci, port);
@@ -1829,7 +1828,7 @@ retry:
 				spin_lock_irqsave(&xhci->lock, flags);
 			}
 		}
-		writel(portsc_buf[port_index], ports[port_index]->addr);
+		xhci_portsc_writel(ports[port_index], portsc_buf[port_index]);
 	}
 	hcd->state = HC_STATE_SUSPENDED;
 	bus_state->next_statechange = jiffies + msecs_to_jiffies(10);
@@ -1863,7 +1862,7 @@ static bool xhci_port_missing_cas_quirk(
 	/* clear wakeup/change bits, and do a warm port reset */
 	portsc &= ~(PORT_RWC_BITS | PORT_CEC | PORT_WAKE_BITS);
 	portsc |= PORT_WR;
-	writel(portsc, port->addr);
+	xhci_portsc_writel(port, portsc);
 	/* flush write */
 	readl(port->addr);
 	return true;
@@ -1942,7 +1941,7 @@ int xhci_bus_resume(struct usb_hcd *hcd)
 			}
 		/* disable wake for all ports, write new link state if needed */
 		portsc &= ~(PORT_RWC_BITS | PORT_CEC | PORT_WAKE_BITS);
-		writel(portsc, ports[port_index]->addr);
+		xhci_portsc_writel(ports[port_index], portsc);
 	}
 
 	/* USB2 specific resume signaling delay and U0 link state transition */
drivers/usb/host/xhci-pci.c +1 -1
@@ -920,7 +920,7 @@ static int xhci_pci_poweroff_late(struct
 		xhci_dbg(xhci, "port %d-%d in U3 without wakeup, disable it\n",
 			 port->rhub->hcd->self.busnum, port->hcd_portnum + 1);
 		portsc = xhci_port_state_to_neutral(portsc);
-		writel(portsc | PORT_PE, port->addr);
+		xhci_portsc_writel(port, portsc | PORT_PE);
 	}
 
 	return 0;
drivers/usb/host/xhci-trace.h +5 -0
@@ -575,6 +575,11 @@ DEFINE_EVENT(xhci_log_portsc, xhci_hub_s
 	     TP_ARGS(port, portsc)
 );
 
+DEFINE_EVENT(xhci_log_portsc, xhci_portsc_writel,
+	     TP_PROTO(struct xhci_port *port, u32 portsc),
+	     TP_ARGS(port, portsc)
+);
+
 DECLARE_EVENT_CLASS(xhci_log_doorbell,
 	TP_PROTO(u32 slot, u32 doorbell),
 	TP_ARGS(slot, doorbell),
drivers/usb/host/xhci.c +8 -1
@@ -41,6 +41,13 @@ static unsigned long long quirks;
 module_param(quirks, ullong, S_IRUGO);
 MODULE_PARM_DESC(quirks, "Bit flags for quirks to be enabled as default");
 
+void xhci_portsc_writel(struct xhci_port *port, u32 val)
+{
+	trace_xhci_portsc_writel(port, val);
+	writel(val, port->addr);
+}
+EXPORT_SYMBOL_GPL(xhci_portsc_writel);
+
 static bool td_on_ring(struct xhci_td *td, struct xhci_ring *ring)
 {
 	struct xhci_segment *seg;
@@ -909,7 +916,7 @@ static void xhci_disable_hub_port_wake(s
 			t2 |= PORT_CSC;
 
 		if (t1 != t2) {
-			writel(t2, rhub->ports[i]->addr);
+			xhci_portsc_writel(rhub->ports[i], t2);
 			xhci_dbg(xhci, "config port %d-%d wake bits, portsc: 0x%x, write: 0x%x\n",
 				 rhub->hcd->self.busnum, i + 1, portsc, t2);
 		}
drivers/usb/host/xhci.h +1 -0
@@ -1962,6 +1962,7 @@ void xhci_update_erst_dequeue(struct xhc
 void xhci_add_interrupter(struct xhci_hcd *xhci, unsigned int intr_num);
 int xhci_usb_endpoint_maxp(struct usb_device *udev,
 			   struct usb_host_endpoint *host_ep);
+void xhci_portsc_writel(struct xhci_port *port, u32 val);
 
 /* xHCI roothub code */
 void xhci_set_link_state(struct xhci_hcd *xhci, struct xhci_port *port,
author maintainer
#425 lore raw

PATCH 425/583 usb: xhci: add helper to read PORTSC register

In reply to Greg Kroah-Hartman

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

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

From: Niklas Neronin <[email protected]>

[ Upstream commit 511afe80b82d2b21086e459906e6c97b4eaeed20 ]

Add a dedicated helper function to read the USB Port Status and Control (PORTSC) register. This complements xhci_portsc_writel() and improves code clarity by providing a clear counterpart for reading the register.

Stable-dep-of: 78203d5b54a4 ("usb: xhci: bail out of setup if the controller is inaccessible")

---
 drivers/usb/host/xhci-debugfs.c |    4 +-
 drivers/usb/host/xhci-hub.c     |   68 ++++++++++++++++++++--------------------
 drivers/usb/host/xhci-pci.c     |    2 -
 drivers/usb/host/xhci-ring.c    |    2 -
 drivers/usb/host/xhci-tegra.c   |   12 +++----
 drivers/usb/host/xhci.c         |   14 +++++---
 drivers/usb/host/xhci.h         |    1 
 7 files changed, 55 insertions(+), 48 deletions(-)
drivers/usb/host/xhci-debugfs.c +2 -2
@@ -329,7 +329,7 @@ static int xhci_portsc_show(struct seq_f
 	u32			portsc;
 	char			str[XHCI_MSG_MAX];
 
-	portsc = readl(port->addr);
+	portsc = xhci_portsc_readl(port);
 	seq_printf(s, "%s\n", xhci_decode_portsc(str, portsc));
 
 	return 0;
@@ -359,7 +359,7 @@ static ssize_t xhci_port_write(struct fi
 			return count;
 		spin_lock_irqsave(&xhci->lock, flags);
 		/* compliance mode can only be enabled on ports in RxDetect */
-		portsc = readl(port->addr);
+		portsc = xhci_portsc_readl(port);
 		if ((portsc & PORT_PLS_MASK) != XDEV_RXDETECT) {
 			spin_unlock_irqrestore(&xhci->lock, flags);
 			return -EPERM;
drivers/usb/host/xhci-hub.c +34 -34

265 lines not on this page · raw message

drivers/usb/host/xhci-pci.c +1 -1
@@ -899,7 +899,7 @@ static int xhci_pci_poweroff_late(struct
 
 	for (i = 0; i < HCS_MAX_PORTS(xhci->hcs_params1); i++) {
 		port = &xhci->hw_ports[i];
-		portsc = readl(port->addr);
+		portsc = xhci_portsc_readl(port);
 
 		if ((portsc & PORT_PLS_MASK) != XDEV_U3)
 			continue;
drivers/usb/host/xhci-ring.c +1 -1
@@ -2051,7 +2051,7 @@ static void handle_port_status(struct xh
 	hcd = port->rhub->hcd;
 	bus_state = &port->rhub->bus_state;
 	hcd_portnum = port->hcd_portnum;
-	portsc = readl(port->addr);
+	portsc = xhci_portsc_readl(port);
 
 	xhci_dbg(xhci, "Port change event, %d-%d, id %d, portsc: 0x%x\n",
 		 hcd->self.busnum, hcd_portnum + 1, port_id, portsc);
drivers/usb/host/xhci-tegra.c +6 -6
@@ -2025,7 +2025,7 @@ static bool xhci_hub_ports_suspended(str
 	u32 value;
 
 	for (i = 0; i < hub->num_ports; i++) {
-		value = readl(hub->ports[i]->addr);
+		value = xhci_portsc_readl(hub->ports[i]);
 		if ((value & PORT_PE) == 0)
 			continue;
 
@@ -2151,7 +2151,7 @@ static void tegra_xhci_enable_phy_sleepw
 			if (!is_host_mode_phy(tegra, i, j))
 				continue;
 
-			portsc = readl(rhub->ports[index]->addr);
+			portsc = xhci_portsc_readl(rhub->ports[index]);
 			speed = tegra_xhci_portsc_to_speed(tegra, portsc);
 			tegra_xusb_padctl_enable_phy_sleepwalk(padctl, phy, speed);
 			tegra_xusb_padctl_enable_phy_wake(padctl, phy);
@@ -2246,7 +2246,7 @@ static int tegra_xusb_enter_elpg(struct
 	for (i = 0; i < xhci->usb2_rhub.num_ports; i++) {
 		if (!xhci->usb2_rhub.ports[i])
 			continue;
-		portsc = readl(xhci->usb2_rhub.ports[i]->addr);
+		portsc = xhci_portsc_readl(xhci->usb2_rhub.ports[i]);
 		tegra->lp0_utmi_pad_mask &= ~BIT(i);
 		if (((portsc & PORT_PLS_MASK) == XDEV_U3) || ((portsc & DEV_SPEED_MASK) == XDEV_FS))
 			tegra->lp0_utmi_pad_mask |= BIT(i);
@@ -2784,7 +2784,7 @@ static int tegra_xhci_hub_control(struct
 		while (i--) {
 			if (!test_bit(i, &bus_state->resuming_ports))
 				continue;
-			portsc = readl(ports[i]->addr);
+			portsc = xhci_portsc_readl(ports[i]);
 			if ((portsc & PORT_PLS_MASK) == XDEV_RESUME)
 				tegra_phy_xusb_utmi_pad_power_on(
 					tegra_xusb_get_phy(tegra, "usb2", (int) i));
@@ -2802,7 +2802,7 @@ static int tegra_xhci_hub_control(struct
 			if (!index || index > rhub->num_ports)
 				return -EPIPE;
 			ports = rhub->ports;
-			portsc = readl(ports[port]->addr);
+			portsc = xhci_portsc_readl(ports[port]);
 			if (portsc & PORT_CONNECT)
 				tegra_phy_xusb_utmi_pad_power_on(phy);
 		}
@@ -2821,7 +2821,7 @@ static int tegra_xhci_hub_control(struct
 
 		if ((type_req == ClearPortFeature) && (value == USB_PORT_FEAT_C_CONNECTION)) {
 			ports = rhub->ports;
-			portsc = readl(ports[port]->addr);
+			portsc = xhci_portsc_readl(ports[port]);
 			if (!(portsc & PORT_CONNECT)) {
 				/* We don't suspend the PAD while HNP role swap happens on the OTG
 				 * port
drivers/usb/host/xhci.c +10 -4
@@ -48,6 +48,12 @@ void xhci_portsc_writel(struct xhci_port
 }
 EXPORT_SYMBOL_GPL(xhci_portsc_writel);
 
+u32 xhci_portsc_readl(struct xhci_port *port)
+{
+	return readl(port->addr);
+}
+EXPORT_SYMBOL_GPL(xhci_portsc_readl);
+
 static bool td_on_ring(struct xhci_td *td, struct xhci_ring *ring)
 {
 	struct xhci_segment *seg;
@@ -380,7 +386,7 @@ static void compliance_mode_recovery(str
 		return;
 
 	for (i = 0; i < rhub->num_ports; i++) {
-		temp = readl(rhub->ports[i]->addr);
+		temp = xhci_portsc_readl(rhub->ports[i]);
 		if ((temp & PORT_PLS_MASK) == USB_SS_PORT_LS_COMP_MOD) {
 			/*
 			 * Compliance Mode Detected. Letting USB Core
@@ -903,7 +909,7 @@ static void xhci_disable_hub_port_wake(s
 	spin_lock_irqsave(&xhci->lock, flags);
 
 	for (i = 0; i < rhub->num_ports; i++) {
-		portsc = readl(rhub->ports[i]->addr);
+		portsc = xhci_portsc_readl(rhub->ports[i]);
 		t1 = xhci_port_state_to_neutral(portsc);
 		t2 = t1;
 
@@ -943,7 +949,7 @@ static bool xhci_pending_portevent(struc
 	port_index = xhci->usb2_rhub.num_ports;
 	ports = xhci->usb2_rhub.ports;
 	while (port_index--) {
-		portsc = readl(ports[port_index]->addr);
+		portsc = xhci_portsc_readl(ports[port_index]);
 		if (portsc & PORT_CHANGE_MASK ||
 		    (portsc & PORT_PLS_MASK) == XDEV_RESUME)
 			return true;
@@ -951,7 +957,7 @@ static bool xhci_pending_portevent(struc
 	port_index = xhci->usb3_rhub.num_ports;
 	ports = xhci->usb3_rhub.ports;
 	while (port_index--) {
-		portsc = readl(ports[port_index]->addr);
+		portsc = xhci_portsc_readl(ports[port_index]);
 		if (portsc & (PORT_CHANGE_MASK | PORT_CAS) ||
 		    (portsc & PORT_PLS_MASK) == XDEV_RESUME)
 			return true;
drivers/usb/host/xhci.h +1 -0
@@ -1963,6 +1963,7 @@ void xhci_add_interrupter(struct xhci_hc
 int xhci_usb_endpoint_maxp(struct usb_device *udev,
 			   struct usb_host_endpoint *host_ep);
 void xhci_portsc_writel(struct xhci_port *port, u32 val);
+u32 xhci_portsc_readl(struct xhci_port *port);
 
 /* xHCI roothub code */
 void xhci_set_link_state(struct xhci_hcd *xhci, struct xhci_port *port,
author maintainer
#426 lore raw

PATCH 426/583 usb: xhci: add USB Port Register Set struct

In reply to Greg Kroah-Hartman

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

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

From: Niklas Neronin <[email protected]>

[ Upstream commit 377a91594e008848363641d07f51d2e48f4bdde5 ]

Introduce a new struct for the Host Controller USB Port Register Set to enhance readability and maintainability.

The Host Controller Operational Registers (struct 'xhci_op_regs') span from offset 0x0 to 0x3FF and consist of fixed fields. Following these fixed fields are the Host Controller USB Port Register Sets, which are dynamic and repeat from 1 to MaxPorts, as defined by HCSPARAMS1.

Currently, the struct 'xhci_op_regs' includes:
__le32 port_status_base; The first PORTSC
__le32 port_power_base; The first PORTPMSC
__le32 port_link_base; The first PORTLI
__le32 reserved5; The first PORTHLPMC, not reserved
__le32 reserved6[NUM_PORT_REGS*254]; Port registers 2 to MaxPorts

Replace this with the simpler:
struct xhci_port_regs port_regs[]; Port registers 1 to MaxPorts

Host Controller USB Port Register Set:
| Offset | Mnemonic | Register Name
--------------------------------------------------------------------------

| 0x0		| PORTSC	| Port Status and Control
| 0x4		| PORTPMSC	| Port Power Management Status and Control
| 0x8		| PORTLI	| Port Link Info
| 0xC		| PORTHLPMC	| Port Hardware LPM Control

Stable-dep-of: 78203d5b54a4 ("usb: xhci: bail out of setup if the controller is inaccessible")

---
 drivers/usb/host/xhci-mem.c |    3 +--
 drivers/usb/host/xhci.h     |   36 ++++++++++++++++--------------------
 2 files changed, 17 insertions(+), 22 deletions(-)
drivers/usb/host/xhci-mem.c +1 -2
@@ -2201,8 +2201,7 @@ static int xhci_setup_port_arrays(struct
 		return -ENOMEM;
 
 	for (i = 0; i < num_ports; i++) {
-		xhci->hw_ports[i].addr = &xhci->op_regs->port_status_base +
-			NUM_PORT_REGS * i;
+		xhci->hw_ports[i].addr = &xhci->op_regs->port_regs[i].portsc;
 		xhci->hw_ports[i].hw_portnum = i;
 
 		init_completion(&xhci->hw_ports[i].rexit_done);
drivers/usb/host/xhci.h +16 -20
@@ -66,14 +66,25 @@ struct xhci_cap_regs {
 	/* Reserved up to (CAPLENGTH - 0x1C) */
 };
 
-/* Number of registers per port */
-#define	NUM_PORT_REGS	4
-
 #define PORTSC		0
 #define PORTPMSC	1
 #define PORTLI		2
 #define PORTHLPMC	3
 
+/*
+ * struct xhci_port_regs - Host Controller USB Port Register Set. xHCI spec 5.4.8
+ * @portsc:	Port Status and Control
+ * @portpmsc:	Port Power Management Status and Control
+ * @portli:	Port Link Info
+ * @porthlmpc:	Port Hardware LPM Control
+ */
+struct xhci_port_regs {
+	__le32	portsc;
+	__le32	portpmsc;
+	__le32	portli;
+	__le32	porthlmpc;
+};
+
 /**
  * struct xhci_op_regs - xHCI Host Controller Operational Registers.
  * @command:		USBCMD - xHC command register
@@ -85,16 +96,7 @@ struct xhci_cap_regs {
  * @cmd_ring:		CRP - 64-bit Command Ring Pointer
  * @dcbaa_ptr:		DCBAAP - 64-bit Device Context Base Address Array Pointer
  * @config_reg:		CONFIG - Configure Register
- * @port_status_base:	PORTSCn - base address for Port Status and Control
- * 			Each port has a Port Status and Control register,
- * 			followed by a Port Power Management Status and Control
- * 			register, a Port Link Info register, and a reserved
- * 			register.
- * @port_power_base:	PORTPMSCn - base address for
- * 			Port Power Management Status and Control
- * @port_link_base:	PORTLIn - base address for Port Link Info (current
- * 			Link PM state and control) for USB 2.1 and USB 3.0
- * 			devices.
+ * @port_regs:		Port Register Sets, from 1 to MaxPorts (defined by HCSPARAMS1).
  */
 struct xhci_op_regs {
 	__le32	command;
@@ -110,13 +112,7 @@ struct xhci_op_regs {
 	__le32	config_reg;
 	/* rsvd: offset 0x3C-3FF */
 	__le32	reserved4[241];
-	/* port 1 registers, which serve as a base address for other ports */
-	__le32	port_status_base;
-	__le32	port_power_base;
-	__le32	port_link_base;
-	__le32	reserved5;
-	/* registers for ports 2-255 */
-	__le32	reserved6[NUM_PORT_REGS*254];
+	struct xhci_port_regs port_regs[];
 };
 
 /* USBCMD - USB command - command bitmasks */
author maintainer
#427 lore raw

PATCH 427/583 usb: xhci: implement USB Port Register Set struct

In reply to Greg Kroah-Hartman

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

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

From: Niklas Neronin <[email protected]>

[ Upstream commit f2469d89a70cc6eb3d8141770995a3b251afa6ff ]

Previously, each port's 'addr' field pointed to the base of the Host Controller USB Port Register Set, and specific registers were accessed using macros such as (port->addr + PORTPMSC).

This patch replaces the raw '__le32 __iomem *addr' pointer with a typed 'struct xhci_port_regs __iomem *port_reg' pointer. With this change, individual registers can be accessed directly through the structure fields:

Before:
port->addr
port->addr + PORTPMSC
port->addr + PORTLI
port->addr + PORTHLPMC

After:
port->port_reg->portsc
port->port_reg->portpmsc
port->port_reg->portli
port->port_reg->porthlpmc

This improves code readability and makes register access more intuitive by using named struct members instead of pointer arithmetic and macros.

Stable-dep-of: 78203d5b54a4 ("usb: xhci: bail out of setup if the controller is inaccessible")

---
 drivers/usb/host/xhci-hub.c |   16 ++++++++--------
 drivers/usb/host/xhci-mem.c |    2 +-
 drivers/usb/host/xhci.c     |   29 ++++++++++++++---------------
 drivers/usb/host/xhci.h     |    7 +------
 4 files changed, 24 insertions(+), 30 deletions(-)
drivers/usb/host/xhci-hub.c +8 -8
@@ -683,9 +683,9 @@ static void xhci_port_set_test_mode(stru
 
 	/* xhci only supports test mode for usb2 ports */
 	port = xhci->usb2_rhub.ports[wIndex];
-	temp = readl(port->addr + PORTPMSC);
+	temp = readl(&port->port_reg->portpmsc);
 	temp |= test_mode << PORT_TEST_MODE_SHIFT;
-	writel(temp, port->addr + PORTPMSC);
+	writel(temp, &port->port_reg->portpmsc);
 	xhci->test_mode = test_mode;
 	if (test_mode == USB_TEST_FORCE_ENABLE)
 		xhci_start(xhci);
@@ -1288,7 +1288,7 @@ int xhci_hub_control(struct usb_hcd *hcd
 				retval = -EINVAL;
 				break;
 			}
-			port_li = readl(port->addr + PORTLI);
+			port_li = readl(&port->port_reg->portli);
 			status = xhci_get_ext_port_status(temp, port_li);
 			put_unaligned_le32(status, &buf[4]);
 		}
@@ -1520,18 +1520,18 @@ int xhci_hub_control(struct usb_hcd *hcd
 		case USB_PORT_FEAT_U1_TIMEOUT:
 			if (hcd->speed < HCD_USB3)
 				goto error;
-			temp = readl(port->addr + PORTPMSC);
+			temp = readl(&port->port_reg->portpmsc);
 			temp &= ~PORT_U1_TIMEOUT_MASK;
 			temp |= PORT_U1_TIMEOUT(timeout);
-			writel(temp, port->addr + PORTPMSC);
+			writel(temp, &port->port_reg->portpmsc);
 			break;
 		case USB_PORT_FEAT_U2_TIMEOUT:
 			if (hcd->speed < HCD_USB3)
 				goto error;
-			temp = readl(port->addr + PORTPMSC);
+			temp = readl(&port->port_reg->portpmsc);
 			temp &= ~PORT_U2_TIMEOUT_MASK;
 			temp |= PORT_U2_TIMEOUT(timeout);
-			writel(temp, port->addr + PORTPMSC);
+			writel(temp, &port->port_reg->portpmsc);
 			break;
 		case USB_PORT_FEAT_TEST:
 			/* 4.19.6 Port Test Modes (USB2 Test Mode) */
@@ -1962,7 +1962,7 @@ int xhci_bus_resume(struct usb_hcd *hcd)
 
 	/* poll for U0 link state complete, both USB2 and USB3 */
 	for_each_set_bit(port_index, &bus_state->bus_suspended, BITS_PER_LONG) {
-		sret = xhci_handshake(ports[port_index]->addr, PORT_PLC,
+		sret = xhci_handshake(&ports[port_index]->port_reg->portsc, PORT_PLC,
 				      PORT_PLC, 10 * 1000);
 		if (sret) {
 			xhci_warn(xhci, "port %d-%d resume PLC timeout\n",
drivers/usb/host/xhci-mem.c +1 -1
@@ -2201,7 +2201,7 @@ static int xhci_setup_port_arrays(struct
 		return -ENOMEM;
 
 	for (i = 0; i < num_ports; i++) {
-		xhci->hw_ports[i].addr = &xhci->op_regs->port_regs[i].portsc;
+		xhci->hw_ports[i].port_reg = &xhci->op_regs->port_regs[i];
 		xhci->hw_ports[i].hw_portnum = i;
 
 		init_completion(&xhci->hw_ports[i].rexit_done);
drivers/usb/host/xhci.c +14 -15
@@ -44,13 +44,13 @@ MODULE_PARM_DESC(quirks, "Bit flags for
 void xhci_portsc_writel(struct xhci_port *port, u32 val)
 {
 	trace_xhci_portsc_writel(port, val);
-	writel(val, port->addr);
+	writel(val, &port->port_reg->portsc);
 }
 EXPORT_SYMBOL_GPL(xhci_portsc_writel);
 
 u32 xhci_portsc_readl(struct xhci_port *port)
 {
-	return readl(port->addr);
+	return readl(&port->port_reg->portsc);
 }
 EXPORT_SYMBOL_GPL(xhci_portsc_readl);
 
@@ -4667,7 +4667,7 @@ static int xhci_set_usb2_hardware_lpm(st
 {
 	struct xhci_hcd	*xhci = hcd_to_xhci(hcd);
 	struct xhci_port **ports;
-	__le32 __iomem	*pm_addr, *hlpm_addr;
+	struct xhci_port_regs __iomem *port_reg;
 	u32		pm_val, hlpm_val, field;
 	unsigned int	port_num;
 	unsigned long	flags;
@@ -4692,9 +4692,8 @@ static int xhci_set_usb2_hardware_lpm(st
 
 	ports = xhci->usb2_rhub.ports;
 	port_num = udev->portnum - 1;
-	pm_addr = ports[port_num]->addr + PORTPMSC;
-	pm_val = readl(pm_addr);
-	hlpm_addr = ports[port_num]->addr + PORTHLPMC;
+	port_reg = ports[port_num]->port_reg;
+	pm_val = readl(&port_reg->portpmsc);
 
 	xhci_dbg(xhci, "%s port %d USB2 hardware LPM\n",
 		 str_enable_disable(enable), port_num + 1);
@@ -4723,30 +4722,30 @@ static int xhci_set_usb2_hardware_lpm(st
 			spin_lock_irqsave(&xhci->lock, flags);
 
 			hlpm_val = xhci_calculate_usb2_hw_lpm_params(udev);
-			writel(hlpm_val, hlpm_addr);
+			writel(hlpm_val, &port_reg->porthlmpc);
 			/* flush write */
-			readl(hlpm_addr);
+			readl(&port_reg->porthlmpc);
 		} else {
 			hird = xhci_calculate_hird_besl(xhci, udev);
 		}
 
 		pm_val &= ~PORT_HIRD_MASK;
 		pm_val |= PORT_HIRD(hird) | PORT_RWE | PORT_L1DS(udev->slot_id);
-		writel(pm_val, pm_addr);
-		pm_val = readl(pm_addr);
+		writel(pm_val, &port_reg->portpmsc);
+		pm_val = readl(&port_reg->portpmsc);
 		pm_val |= PORT_HLE;
-		writel(pm_val, pm_addr);
+		writel(pm_val, &port_reg->portpmsc);
 		/* flush write */
-		readl(pm_addr);
+		readl(&port_reg->portpmsc);
 	} else {
 		pm_val &= ~(PORT_HLE | PORT_RWE | PORT_HIRD_MASK | PORT_L1DS_MASK);
-		writel(pm_val, pm_addr);
+		writel(pm_val, &port_reg->portpmsc);
 		/* flush write */
-		readl(pm_addr);
+		readl(&port_reg->portpmsc);
 		if (udev->usb2_hw_lpm_besl_capable) {
 			spin_unlock_irqrestore(&xhci->lock, flags);
 			xhci_change_max_exit_latency(xhci, udev, 0);
-			readl_poll_timeout(ports[port_num]->addr, pm_val,
+			readl_poll_timeout(&ports[port_num]->port_reg->portsc, pm_val,
 					   (pm_val & PORT_PLS_MASK) == XDEV_U0,
 					   100, 10000);
 			return 0;
drivers/usb/host/xhci.h +1 -6
@@ -66,11 +66,6 @@ struct xhci_cap_regs {
 	/* Reserved up to (CAPLENGTH - 0x1C) */
 };
 
-#define PORTSC		0
-#define PORTPMSC	1
-#define PORTLI		2
-#define PORTHLPMC	3
-
 /*
  * struct xhci_port_regs - Host Controller USB Port Register Set. xHCI spec 5.4.8
  * @portsc:	Port Status and Control
@@ -1470,7 +1465,7 @@ struct xhci_port_cap {
 };
 
 struct xhci_port {
-	__le32 __iomem		*addr;
+	struct xhci_port_regs __iomem	*port_reg;
 	int			hw_portnum;
 	int			hcd_portnum;
 	struct xhci_hub		*rhub;
author maintainer
#428 lore raw

PATCH 428/583 usb: xhci: use cached HCSPARAMS1 value

In reply to Greg Kroah-Hartman

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

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

From: Niklas Neronin <[email protected]>

[ Upstream commit 70651cc3f5a4c7cec529f121e36ea3b45ea84778 ]

The Structural Parameters 1 (HCSPARAMS1) register is read and cached in 'xhci->hcs_params1' during host controller initialization. Since this register is read-only and its value remains constant for the lifetime of the controller, re-reading it later is unnecessary.

Replace subsequent register reads with the cached 'xhci->hcs_params1' value to avoid redundant MMIO access.

Stable-dep-of: 78203d5b54a4 ("usb: xhci: bail out of setup if the controller is inaccessible")

---
 drivers/usb/host/xhci.c |    3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)
drivers/usb/host/xhci.c +1 -2
@@ -4253,8 +4253,7 @@ int xhci_alloc_dev(struct usb_hcd *hcd,
 		xhci_err(xhci, "Error while assigning device slot ID: %s\n",
 			 xhci_trb_comp_code_string(command->status));
 		xhci_err(xhci, "Max number of devices this xHCI host supports is %u.\n",
-				HCS_MAX_SLOTS(
-					readl(&xhci->cap_regs->hcs_params1)));
+			 HCS_MAX_SLOTS(xhci->hcs_params1));
 		xhci_free_command(xhci, command);
 		return 0;
 	}