--- /dev/null
+From stable+bounces-287896-greg=kroah.com@vger.kernel.org Wed Jul 22 17:03:27 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 10:48:33 -0400
+Subject: afs: Annotate struct afs_addr_list with __counted_by
+To: stable@vger.kernel.org
+Cc: Kees Cook <keescook@chromium.org>, David Howells <dhowells@redhat.com>, Marc Dionne <marc.dionne@auristor.com>, linux-afs@lists.infradead.org, "Gustavo A. R. Silva" <gustavoars@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260722144836.1601242-1-sashal@kernel.org>
+
+From: Kees Cook <keescook@chromium.org>
+
+[ Upstream commit 2d26302bdff1300029a67931739f5770eac22493 ]
+
+Prepare for the coming implementation by GCC and Clang of the __counted_by
+attribute. Flexible array members annotated with __counted_by can have
+their accesses bounds-checked at run-time checking via CONFIG_UBSAN_BOUNDS
+(for array indexing) and CONFIG_FORTIFY_SOURCE (for strcpy/memcpy-family
+functions).
+
+As found with Coccinelle[1], add __counted_by for struct afs_addr_list.
+
+[1] https://github.com/kees/kernel-tools/blob/trunk/coccinelle/examples/counted_by.cocci
+
+Cc: David Howells <dhowells@redhat.com>
+Cc: Marc Dionne <marc.dionne@auristor.com>
+Cc: linux-afs@lists.infradead.org
+Reviewed-by: "Gustavo A. R. Silva" <gustavoars@kernel.org>
+Link: https://lore.kernel.org/r/20230915201449.never.649-kees@kernel.org
+Signed-off-by: Kees Cook <keescook@chromium.org>
+Stable-dep-of: dc175389b18c ("rxrpc: serialize kernel accept preallocation with socket teardown")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/afs/internal.h | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/fs/afs/internal.h
++++ b/fs/afs/internal.h
+@@ -87,7 +87,7 @@ struct afs_addr_list {
+ enum dns_lookup_status status:8;
+ unsigned long failed; /* Mask of addrs that failed locally/ICMP */
+ unsigned long responded; /* Mask of addrs that responded */
+- struct sockaddr_rxrpc addrs[];
++ struct sockaddr_rxrpc addrs[] __counted_by(max_addrs);
+ #define AFS_MAX_ADDRESSES ((unsigned int)(sizeof(unsigned long) * 8))
+ };
+
--- /dev/null
+From stable+bounces-287897-greg=kroah.com@vger.kernel.org Wed Jul 22 16:50:51 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 10:48:34 -0400
+Subject: afs: Turn the afs_addr_list address array into an array of structs
+To: stable@vger.kernel.org
+Cc: David Howells <dhowells@redhat.com>, Marc Dionne <marc.dionne@auristor.com>, linux-afs@lists.infradead.org, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260722144836.1601242-2-sashal@kernel.org>
+
+From: David Howells <dhowells@redhat.com>
+
+[ Upstream commit 07f3502b33a260f873e35708d2fa693eb52225cb ]
+
+Turn the afs_addr_list address array into an array of structs, thereby
+allowing per-address (such as RTT) info to be added.
+
+Signed-off-by: David Howells <dhowells@redhat.com>
+cc: Marc Dionne <marc.dionne@auristor.com>
+cc: linux-afs@lists.infradead.org
+Stable-dep-of: dc175389b18c ("rxrpc: serialize kernel accept preallocation with socket teardown")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/afs/addr_list.c | 10 +++++-----
+ fs/afs/fs_probe.c | 6 +++---
+ fs/afs/internal.h | 6 +++++-
+ fs/afs/proc.c | 4 ++--
+ fs/afs/rotate.c | 2 +-
+ fs/afs/rxrpc.c | 4 ++--
+ fs/afs/server.c | 4 ++--
+ fs/afs/vl_alias.c | 4 ++--
+ fs/afs/vl_probe.c | 6 +++---
+ fs/afs/vl_rotate.c | 2 +-
+ 10 files changed, 26 insertions(+), 22 deletions(-)
+
+--- a/fs/afs/addr_list.c
++++ b/fs/afs/addr_list.c
+@@ -45,7 +45,7 @@ struct afs_addr_list *afs_alloc_addrlist
+ alist->max_addrs = nr;
+
+ for (i = 0; i < nr; i++) {
+- struct sockaddr_rxrpc *srx = &alist->addrs[i];
++ struct sockaddr_rxrpc *srx = &alist->addrs[i].srx;
+ srx->srx_family = AF_RXRPC;
+ srx->srx_service = service;
+ srx->transport_type = SOCK_DGRAM;
+@@ -281,7 +281,7 @@ void afs_merge_fs_addr4(struct afs_addr_
+ return;
+
+ for (i = 0; i < alist->nr_ipv4; i++) {
+- struct sockaddr_in *a = &alist->addrs[i].transport.sin;
++ struct sockaddr_in *a = &alist->addrs[i].srx.transport.sin;
+ u32 a_addr = ntohl(a->sin_addr.s_addr);
+ u16 a_port = ntohs(a->sin_port);
+
+@@ -298,7 +298,7 @@ void afs_merge_fs_addr4(struct afs_addr_
+ alist->addrs + i,
+ sizeof(alist->addrs[0]) * (alist->nr_addrs - i));
+
+- srx = &alist->addrs[i];
++ srx = &alist->addrs[i].srx;
+ srx->srx_family = AF_RXRPC;
+ srx->transport_type = SOCK_DGRAM;
+ srx->transport_len = sizeof(srx->transport.sin);
+@@ -321,7 +321,7 @@ void afs_merge_fs_addr6(struct afs_addr_
+ return;
+
+ for (i = alist->nr_ipv4; i < alist->nr_addrs; i++) {
+- struct sockaddr_in6 *a = &alist->addrs[i].transport.sin6;
++ struct sockaddr_in6 *a = &alist->addrs[i].srx.transport.sin6;
+ u16 a_port = ntohs(a->sin6_port);
+
+ diff = memcmp(xdr, &a->sin6_addr, 16);
+@@ -338,7 +338,7 @@ void afs_merge_fs_addr6(struct afs_addr_
+ alist->addrs + i,
+ sizeof(alist->addrs[0]) * (alist->nr_addrs - i));
+
+- srx = &alist->addrs[i];
++ srx = &alist->addrs[i].srx;
+ srx->srx_family = AF_RXRPC;
+ srx->transport_type = SOCK_DGRAM;
+ srx->transport_len = sizeof(srx->transport.sin6);
+--- a/fs/afs/fs_probe.c
++++ b/fs/afs/fs_probe.c
+@@ -153,12 +153,12 @@ responded:
+ if (call->service_id == YFS_FS_SERVICE) {
+ server->probe.is_yfs = true;
+ set_bit(AFS_SERVER_FL_IS_YFS, &server->flags);
+- alist->addrs[index].srx_service = call->service_id;
++ alist->addrs[index].srx.srx_service = call->service_id;
+ } else {
+ server->probe.not_yfs = true;
+ if (!server->probe.is_yfs) {
+ clear_bit(AFS_SERVER_FL_IS_YFS, &server->flags);
+- alist->addrs[index].srx_service = call->service_id;
++ alist->addrs[index].srx.srx_service = call->service_id;
+ }
+ cap0 = ntohl(call->tmp);
+ if (cap0 & AFS3_VICED_CAPABILITY_64BITFILES)
+@@ -182,7 +182,7 @@ out:
+ spin_unlock(&server->probe_lock);
+
+ _debug("probe %pU [%u] %pISpc rtt=%u ret=%d",
+- &server->uuid, index, &alist->addrs[index].transport,
++ &server->uuid, index, &alist->addrs[index].srx.transport,
+ rtt_us, ret);
+
+ return afs_done_one_fs_probe(call->net, server);
+--- a/fs/afs/internal.h
++++ b/fs/afs/internal.h
+@@ -87,7 +87,9 @@ struct afs_addr_list {
+ enum dns_lookup_status status:8;
+ unsigned long failed; /* Mask of addrs that failed locally/ICMP */
+ unsigned long responded; /* Mask of addrs that responded */
+- struct sockaddr_rxrpc addrs[] __counted_by(max_addrs);
++ struct {
++ struct sockaddr_rxrpc srx;
++ } addrs[] __counted_by(max_addrs);
+ #define AFS_MAX_ADDRESSES ((unsigned int)(sizeof(unsigned long) * 8))
+ };
+
+@@ -972,6 +974,8 @@ extern void afs_put_addrlist(struct afs_
+ extern struct afs_vlserver_list *afs_parse_text_addrs(struct afs_net *,
+ const char *, size_t, char,
+ unsigned short, unsigned short);
++bool afs_addr_list_same(const struct afs_addr_list *a,
++ const struct afs_addr_list *b);
+ extern struct afs_vlserver_list *afs_dns_query(struct afs_cell *, time64_t *);
+ extern bool afs_iterate_addresses(struct afs_addr_cursor *);
+ extern int afs_end_cursor(struct afs_addr_cursor *);
+--- a/fs/afs/proc.c
++++ b/fs/afs/proc.c
+@@ -307,7 +307,7 @@ static int afs_proc_cell_vlservers_show(
+ for (i = 0; i < alist->nr_addrs; i++)
+ seq_printf(m, " %c %pISpc\n",
+ alist->preferred == i ? '>' : '-',
+- &alist->addrs[i].transport);
++ &alist->addrs[i].srx.transport);
+ }
+ seq_printf(m, " info: fl=%lx rtt=%d\n", vlserver->flags, vlserver->rtt);
+ seq_printf(m, " probe: fl=%x e=%d ac=%d out=%d\n",
+@@ -399,7 +399,7 @@ static int afs_proc_servers_show(struct
+ alist->version, alist->responded, alist->failed);
+ for (i = 0; i < alist->nr_addrs; i++)
+ seq_printf(m, " [%x] %pISpc%s\n",
+- i, &alist->addrs[i].transport,
++ i, &alist->addrs[i].srx.transport,
+ alist->preferred == i ? "*" : "");
+ return 0;
+ }
+--- a/fs/afs/rotate.c
++++ b/fs/afs/rotate.c
+@@ -409,7 +409,7 @@ iterate_address:
+
+ _debug("address [%u] %u/%u %pISp",
+ op->index, op->ac.index, op->ac.alist->nr_addrs,
+- &op->ac.alist->addrs[op->ac.index].transport);
++ &op->ac.alist->addrs[op->ac.index].srx.transport);
+
+ _leave(" = t");
+ return true;
+--- a/fs/afs/rxrpc.c
++++ b/fs/afs/rxrpc.c
+@@ -302,7 +302,7 @@ static void afs_notify_end_request_tx(st
+ */
+ void afs_make_call(struct afs_addr_cursor *ac, struct afs_call *call, gfp_t gfp)
+ {
+- struct sockaddr_rxrpc *srx = &ac->alist->addrs[ac->index];
++ struct sockaddr_rxrpc *srx = &ac->alist->addrs[ac->index].srx;
+ struct rxrpc_call *rxcall;
+ struct msghdr msg;
+ struct kvec iov[1];
+@@ -474,7 +474,7 @@ static void afs_log_error(struct afs_cal
+ max = m + 1;
+ pr_notice("kAFS: Peer reported %s failure on %s [%pISp]\n",
+ msg, call->type->name,
+- &call->alist->addrs[call->addr_ix].transport);
++ &call->alist->addrs[call->addr_ix].srx.transport);
+ }
+ }
+
+--- a/fs/afs/server.c
++++ b/fs/afs/server.c
+@@ -43,7 +43,7 @@ struct afs_server *afs_find_server(struc
+ hlist_for_each_entry_rcu(server, &net->fs_addresses6, addr6_link) {
+ alist = rcu_dereference(server->addresses);
+ for (i = alist->nr_ipv4; i < alist->nr_addrs; i++) {
+- b = &alist->addrs[i].transport.sin6;
++ b = &alist->addrs[i].srx.transport.sin6;
+ diff = ((u16 __force)a->sin6_port -
+ (u16 __force)b->sin6_port);
+ if (diff == 0)
+@@ -59,7 +59,7 @@ struct afs_server *afs_find_server(struc
+ hlist_for_each_entry_rcu(server, &net->fs_addresses4, addr4_link) {
+ alist = rcu_dereference(server->addresses);
+ for (i = 0; i < alist->nr_ipv4; i++) {
+- b = &alist->addrs[i].transport.sin;
++ b = &alist->addrs[i].srx.transport.sin;
+ diff = ((u16 __force)a->sin_port -
+ (u16 __force)b->sin_port);
+ if (diff == 0)
+--- a/fs/afs/vl_alias.c
++++ b/fs/afs/vl_alias.c
+@@ -94,8 +94,8 @@ static int afs_compare_fs_alists(const s
+ lb = rcu_dereference(server_b->addresses);
+
+ while (a < la->nr_addrs && b < lb->nr_addrs) {
+- const struct sockaddr_rxrpc *srx_a = &la->addrs[a];
+- const struct sockaddr_rxrpc *srx_b = &lb->addrs[b];
++ const struct sockaddr_rxrpc *srx_a = &la->addrs[a].srx;
++ const struct sockaddr_rxrpc *srx_b = &lb->addrs[b].srx;
+ int diff = afs_compare_addrs(srx_a, srx_b);
+
+ if (diff < 0) {
+--- a/fs/afs/vl_probe.c
++++ b/fs/afs/vl_probe.c
+@@ -106,12 +106,12 @@ responded:
+ if (call->service_id == YFS_VL_SERVICE) {
+ server->probe.flags |= AFS_VLSERVER_PROBE_IS_YFS;
+ set_bit(AFS_VLSERVER_FL_IS_YFS, &server->flags);
+- alist->addrs[index].srx_service = call->service_id;
++ alist->addrs[index].srx.srx_service = call->service_id;
+ } else {
+ server->probe.flags |= AFS_VLSERVER_PROBE_NOT_YFS;
+ if (!(server->probe.flags & AFS_VLSERVER_PROBE_IS_YFS)) {
+ clear_bit(AFS_VLSERVER_FL_IS_YFS, &server->flags);
+- alist->addrs[index].srx_service = call->service_id;
++ alist->addrs[index].srx.srx_service = call->service_id;
+ }
+ }
+
+@@ -131,7 +131,7 @@ out:
+ spin_unlock(&server->probe_lock);
+
+ _debug("probe [%u][%u] %pISpc rtt=%u ret=%d",
+- server_index, index, &alist->addrs[index].transport, rtt_us, ret);
++ server_index, index, &alist->addrs[index].srx.transport, rtt_us, ret);
+
+ afs_done_one_vl_probe(server, have_result);
+ }
+--- a/fs/afs/vl_rotate.c
++++ b/fs/afs/vl_rotate.c
+@@ -249,7 +249,7 @@ iterate_address:
+
+ _debug("VL address %d/%d", vc->ac.index, vc->ac.alist->nr_addrs);
+
+- _leave(" = t %pISpc", &vc->ac.alist->addrs[vc->ac.index].transport);
++ _leave(" = t %pISpc", &vc->ac.alist->addrs[vc->ac.index].srx.transport);
+ return true;
+
+ next_server:
--- /dev/null
+From stable+bounces-287801-greg=kroah.com@vger.kernel.org Wed Jul 22 13:02:56 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 07:01:17 -0400
+Subject: ALSA: hda: conexant: Remove mic bias threshold override
+To: stable@vger.kernel.org
+Cc: Zhang Heng <zhangheng@kylinos.cn>, Takashi Iwai <tiwai@suse.de>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260722110117.1006143-1-sashal@kernel.org>
+
+From: Zhang Heng <zhangheng@kylinos.cn>
+
+[ Upstream commit f52524da7084c1a54683ae9fbc73e93fff19dd64 ]
+
+Remove the mic bias current comparator threshold override (NID 0x1c,
+verb 0x320, value 0x010) from Conexant codec driver.
+
+This override was originally intended to support volume up/down controls on
+headsets with inline remote controls, but it causes microphone detection
+failures on some headsets with impedance less than 1k ohm.
+
+After consulting with the vendor's engineers, it was confirmed that this
+setting is board-specific and should be handled by BIOS/firmware rather
+than the generic codec driver, especially since inline remote support
+is not currently implemented.
+
+Fixes: 7aeb25908648 ("ALSA: hda/conexant: Fix headset auto detect fail in cx8070 and SN6140")
+Cc: stable@vger.kernel.org
+Signed-off-by: Zhang Heng <zhangheng@kylinos.cn>
+Link: https://patch.msgid.link/20260713100329.306892-1-zhangheng@kylinos.cn
+Signed-off-by: Takashi Iwai <tiwai@suse.de>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ sound/pci/hda/patch_conexant.c | 3 ---
+ 1 file changed, 3 deletions(-)
+
+--- a/sound/pci/hda/patch_conexant.c
++++ b/sound/pci/hda/patch_conexant.c
+@@ -168,9 +168,6 @@ static void cx_fixup_headset_recog(struc
+ {
+ unsigned int mic_present;
+
+- /* fix some headset type recognize fail issue, such as EDIFIER headset */
+- /* set micbias output current comparator threshold from 66% to 55%. */
+- snd_hda_codec_write(codec, 0x1c, 0, 0x320, 0x010);
+ /* set OFF voltage for DFET from -1.2V to -0.8V, set headset micbias register
+ * value adjustment trim from 2.2K ohms to 2.0K ohms.
+ */
--- /dev/null
+From stable+bounces-287811-greg=kroah.com@vger.kernel.org Wed Jul 22 13:31:18 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 07:24:21 -0400
+Subject: ALSA: hda: Fix cached processing coefficient verbs
+To: stable@vger.kernel.org
+Cc: Xu Rao <raoxu@uniontech.com>, Takashi Iwai <tiwai@suse.de>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260722112421.1114485-1-sashal@kernel.org>
+
+From: Xu Rao <raoxu@uniontech.com>
+
+[ Upstream commit f67be28fdf8b5d31ac1cc1152bb17250f9f8f513 ]
+
+Intel HD Audio defines Coefficient Index and Processing Coefficient as
+separate audio widget controls in the Audio Widget Verb Definitions:
+Coefficient Index selects the coefficient slot, while Processing
+Coefficient accesses the value at the selected slot.
+
+hda_reg_read_coef() selects the slot with AC_VERB_SET_COEF_INDEX, but
+then uses AC_VERB_GET_COEF_INDEX for the value read. That reads back the
+selected index instead of the coefficient value. hda_reg_write_coef()
+has the same issue and builds the value write from AC_VERB_GET_COEF_INDEX
+instead of AC_VERB_SET_PROC_COEF.
+
+This only affects the regmap coefficient cache path used by codecs that
+set codec->cache_coef. Direct coefficient helpers already use the normal
+SET_COEF_INDEX followed by GET_PROC_COEF or SET_PROC_COEF sequence, which
+is likely why this has not been noticed widely.
+
+Use AC_VERB_GET_PROC_COEF for cached coefficient reads and
+AC_VERB_SET_PROC_COEF for cached coefficient writes.
+
+Fixes: 40ba66a702b8 ("ALSA: hda - Add cache support for COEF read/write")
+Cc: stable@vger.kernel.org
+Signed-off-by: Xu Rao <raoxu@uniontech.com>
+Link: https://patch.msgid.link/DB9023BF2920BA99+20260707132419.1731342-1-raoxu@uniontech.com
+Signed-off-by: Takashi Iwai <tiwai@suse.de>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ sound/hda/hdac_regmap.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/sound/hda/hdac_regmap.c
++++ b/sound/hda/hdac_regmap.c
+@@ -214,7 +214,7 @@ static int hda_reg_read_coef(struct hdac
+ err = snd_hdac_exec_verb(codec, verb, 0, NULL);
+ if (err < 0)
+ return err;
+- verb = (reg & ~0xfffff) | (AC_VERB_GET_COEF_INDEX << 8);
++ verb = (reg & ~0xfffff) | (AC_VERB_GET_PROC_COEF << 8);
+ return snd_hdac_exec_verb(codec, verb, 0, val);
+ }
+
+@@ -232,7 +232,7 @@ static int hda_reg_write_coef(struct hda
+ err = snd_hdac_exec_verb(codec, verb, 0, NULL);
+ if (err < 0)
+ return err;
+- verb = (reg & ~0xfffff) | (AC_VERB_GET_COEF_INDEX << 8) |
++ verb = (reg & ~0xfffff) | (AC_VERB_SET_PROC_COEF << 8) |
+ (val & 0xffff);
+ return snd_hdac_exec_verb(codec, verb, 0, NULL);
+ }
--- /dev/null
+From stable+bounces-288180-greg=kroah.com@vger.kernel.org Thu Jul 23 04:53:11 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 22:53:02 -0400
+Subject: ASoC: mediatek: mt8183: Check runtime resume during probe
+To: stable@vger.kernel.org
+Cc: "Cássio Gabriel" <cassiogabrielcontato@gmail.com>, "Mark Brown" <broonie@kernel.org>, "Sasha Levin" <sashal@kernel.org>
+Message-ID: <20260723025302.2872512-1-sashal@kernel.org>
+
+From: Cássio Gabriel <cassiogabrielcontato@gmail.com>
+
+[ Upstream commit f0334fbfd107682d0c95f3f71e25f6127038e2b9 ]
+
+The MT8183 AFE probe uses pm_runtime_get_sync() before reading hardware
+defaults into the regmap cache, but does not check whether runtime resume
+failed. If regmap_reinit_cache() then fails, the temporary runtime PM
+usage count is also not released.
+
+Use pm_runtime_resume_and_get() so resume failures abort probe without
+leaking a usage count, and release the temporary reference before
+handling the regmap cache result.
+
+Fixes: a94aec035a12 ("ASoC: mediatek: mt8183: add platform driver")
+Cc: stable@vger.kernel.org
+Signed-off-by: Cássio Gabriel <cassiogabrielcontato@gmail.com>
+Link: https://patch.msgid.link/20260527-asoc-mt8183-probe-cleanup-v1-2-4f4f5593c8d1@gmail.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ sound/soc/mediatek/mt8183/mt8183-afe-pcm.c | 12 ++++++++----
+ 1 file changed, 8 insertions(+), 4 deletions(-)
+
+--- a/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c
++++ b/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c
+@@ -1149,17 +1149,21 @@ static int mt8183_afe_pcm_dev_probe(stru
+
+ /* enable clock for regcache get default value from hw */
+ afe_priv->pm_runtime_bypass_reg_ctl = true;
+- pm_runtime_get_sync(&pdev->dev);
++ ret = pm_runtime_resume_and_get(dev);
++ if (ret) {
++ afe_priv->pm_runtime_bypass_reg_ctl = false;
++ goto err_pm_disable;
++ }
+
+ ret = regmap_reinit_cache(afe->regmap, &mt8183_afe_regmap_config);
++ pm_runtime_put_sync(dev);
++ afe_priv->pm_runtime_bypass_reg_ctl = false;
++
+ if (ret) {
+ dev_err(dev, "regmap_reinit_cache fail, ret %d\n", ret);
+ goto err_pm_disable;
+ }
+
+- pm_runtime_put_sync(&pdev->dev);
+- afe_priv->pm_runtime_bypass_reg_ctl = false;
+-
+ regcache_cache_only(afe->regmap, true);
+ regcache_mark_dirty(afe->regmap);
+
--- /dev/null
+From stable+bounces-288169-greg=kroah.com@vger.kernel.org Thu Jul 23 04:29:25 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 22:29:11 -0400
+Subject: ASoC: mediatek: mt8192-afe-pcm: Convert to devm_pm_runtime_enable()
+To: stable@vger.kernel.org
+Cc: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>, Chen-Yu Tsai <wenst@chromium.org>, Mark Brown <broonie@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260723022915.2845450-2-sashal@kernel.org>
+
+From: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+
+[ Upstream commit 7aaaa22de56ce0dae15fd9f42a69a1d1a7a6e078 ]
+
+Switch from pm_runtime_enable() to devm_pm_runtime_enable(), allowing
+to remove all gotos from the probe function.
+
+Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+Reviewed-by: Chen-Yu Tsai <wenst@chromium.org>
+Link: https://msgid.link/r/20240313110147.1267793-2-angelogioacchino.delregno@collabora.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Stable-dep-of: e24d5dde56a5 ("ASoC: mediatek: mt8192: Check runtime resume during probe")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ sound/soc/mediatek/mt8192/mt8192-afe-pcm.c | 46 ++++++++++-------------------
+ 1 file changed, 17 insertions(+), 29 deletions(-)
+
+--- a/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c
++++ b/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c
+@@ -2233,21 +2233,20 @@ static int mt8192_afe_pcm_dev_probe(stru
+ return ret;
+ }
+
+- pm_runtime_enable(&pdev->dev);
+- if (!pm_runtime_enabled(&pdev->dev))
+- goto err_pm_disable;
++ ret = devm_pm_runtime_enable(&pdev->dev);
++ if (ret)
++ return ret;
+
+ /* regmap init */
+ afe->regmap = syscon_node_to_regmap(dev->parent->of_node);
+ if (IS_ERR(afe->regmap)) {
+ dev_err(dev, "could not get regmap from parent\n");
+- ret = PTR_ERR(afe->regmap);
+- goto err_pm_disable;
++ return PTR_ERR(afe->regmap);
+ }
+ ret = regmap_attach_dev(dev, afe->regmap, &mt8192_afe_regmap_config);
+ if (ret) {
+ dev_warn(dev, "regmap_attach_dev fail, ret %d\n", ret);
+- goto err_pm_disable;
++ return ret;
+ }
+
+ /* enable clock for regcache get default value from hw */
+@@ -2257,7 +2256,7 @@ static int mt8192_afe_pcm_dev_probe(stru
+ ret = regmap_reinit_cache(afe->regmap, &mt8192_afe_regmap_config);
+ if (ret) {
+ dev_err(dev, "regmap_reinit_cache fail, ret %d\n", ret);
+- goto err_pm_disable;
++ return ret;
+ }
+
+ pm_runtime_put_sync(&pdev->dev);
+@@ -2270,10 +2269,8 @@ static int mt8192_afe_pcm_dev_probe(stru
+ afe->memif_size = MT8192_MEMIF_NUM;
+ afe->memif = devm_kcalloc(dev, afe->memif_size, sizeof(*afe->memif),
+ GFP_KERNEL);
+- if (!afe->memif) {
+- ret = -ENOMEM;
+- goto err_pm_disable;
+- }
++ if (!afe->memif)
++ return -ENOMEM;
+
+ for (i = 0; i < afe->memif_size; i++) {
+ afe->memif[i].data = &memif_data[i];
+@@ -2287,26 +2284,22 @@ static int mt8192_afe_pcm_dev_probe(stru
+ afe->irqs_size = MT8192_IRQ_NUM;
+ afe->irqs = devm_kcalloc(dev, afe->irqs_size, sizeof(*afe->irqs),
+ GFP_KERNEL);
+- if (!afe->irqs) {
+- ret = -ENOMEM;
+- goto err_pm_disable;
+- }
++ if (!afe->irqs)
++ return -ENOMEM;
+
+ for (i = 0; i < afe->irqs_size; i++)
+ afe->irqs[i].irq_data = &irq_data[i];
+
+ /* request irq */
+ irq_id = platform_get_irq(pdev, 0);
+- if (irq_id < 0) {
+- ret = irq_id;
+- goto err_pm_disable;
+- }
++ if (irq_id < 0)
++ return irq_id;
+
+ ret = devm_request_irq(dev, irq_id, mt8192_afe_irq_handler,
+ IRQF_TRIGGER_NONE, "asys-isr", (void *)afe);
+ if (ret) {
+ dev_err(dev, "could not request_irq for Afe_ISR_Handle\n");
+- goto err_pm_disable;
++ return ret;
+ }
+
+ /* init sub_dais */
+@@ -2317,7 +2310,7 @@ static int mt8192_afe_pcm_dev_probe(stru
+ if (ret) {
+ dev_warn(afe->dev, "dai register i %d fail, ret %d\n",
+ i, ret);
+- goto err_pm_disable;
++ return ret;
+ }
+ }
+
+@@ -2326,7 +2319,7 @@ static int mt8192_afe_pcm_dev_probe(stru
+ if (ret) {
+ dev_warn(afe->dev, "mtk_afe_combine_sub_dai fail, ret %d\n",
+ ret);
+- goto err_pm_disable;
++ return ret;
+ }
+
+ /* others */
+@@ -2345,7 +2338,7 @@ static int mt8192_afe_pcm_dev_probe(stru
+ &mt8192_afe_component, NULL, 0);
+ if (ret) {
+ dev_warn(dev, "err_platform\n");
+- goto err_pm_disable;
++ return ret;
+ }
+
+ ret = devm_snd_soc_register_component(&pdev->dev,
+@@ -2354,15 +2347,10 @@ static int mt8192_afe_pcm_dev_probe(stru
+ afe->num_dai_drivers);
+ if (ret) {
+ dev_warn(dev, "err_dai_component\n");
+- goto err_pm_disable;
++ return ret;
+ }
+
+ return 0;
+-
+-err_pm_disable:
+- pm_runtime_disable(&pdev->dev);
+-
+- return ret;
+ }
+
+ static void mt8192_afe_pcm_dev_remove(struct platform_device *pdev)
--- /dev/null
+From stable+bounces-288172-greg=kroah.com@vger.kernel.org Thu Jul 23 04:29:46 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 22:29:14 -0400
+Subject: ASoC: mediatek: mt8192-afe-pcm: Simplify probe() with local dev variable
+To: stable@vger.kernel.org
+Cc: Tang Bin <tangbin@cmss.chinamobile.com>, Mark Brown <broonie@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260723022915.2845450-5-sashal@kernel.org>
+
+From: Tang Bin <tangbin@cmss.chinamobile.com>
+
+[ Upstream commit 01981565c764c554cc96e2d30a71c42975171416 ]
+
+Simplify the function mt8192_afe_pcm_dev_probe() by
+using local 'dev' instead of '&pdev->dev'.
+
+Signed-off-by: Tang Bin <tangbin@cmss.chinamobile.com>
+Link: https://patch.msgid.link/20241025080026.2393-1-tangbin@cmss.chinamobile.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Stable-dep-of: e24d5dde56a5 ("ASoC: mediatek: mt8192: Check runtime resume during probe")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ sound/soc/mediatek/mt8192/mt8192-afe-pcm.c | 19 +++++++++----------
+ 1 file changed, 9 insertions(+), 10 deletions(-)
+
+--- a/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c
++++ b/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c
+@@ -2164,27 +2164,26 @@ static int mt8192_afe_pcm_dev_probe(stru
+ {
+ struct mtk_base_afe *afe;
+ struct mt8192_afe_private *afe_priv;
+- struct device *dev;
++ struct device *dev = &pdev->dev;
+ struct reset_control *rstc;
+ int i, ret, irq_id;
+
+- ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(34));
++ ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(34));
+ if (ret)
+ return ret;
+
+- afe = devm_kzalloc(&pdev->dev, sizeof(*afe), GFP_KERNEL);
++ afe = devm_kzalloc(dev, sizeof(*afe), GFP_KERNEL);
+ if (!afe)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, afe);
+
+- afe->platform_priv = devm_kzalloc(&pdev->dev, sizeof(*afe_priv),
++ afe->platform_priv = devm_kzalloc(dev, sizeof(*afe_priv),
+ GFP_KERNEL);
+ if (!afe->platform_priv)
+ return -ENOMEM;
+ afe_priv = afe->platform_priv;
+
+- afe->dev = &pdev->dev;
+- dev = afe->dev;
++ afe->dev = dev;
+
+ ret = of_reserved_mem_device_init(dev);
+ if (ret) {
+@@ -2212,7 +2211,7 @@ static int mt8192_afe_pcm_dev_probe(stru
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to trigger audio reset\n");
+
+- ret = devm_pm_runtime_enable(&pdev->dev);
++ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return ret;
+
+@@ -2228,13 +2227,13 @@ static int mt8192_afe_pcm_dev_probe(stru
+
+ /* enable clock for regcache get default value from hw */
+ afe_priv->pm_runtime_bypass_reg_ctl = true;
+- pm_runtime_get_sync(&pdev->dev);
++ pm_runtime_get_sync(dev);
+
+ ret = regmap_reinit_cache(afe->regmap, &mt8192_afe_regmap_config);
+ if (ret)
+ return dev_err_probe(dev, ret, "regmap_reinit_cache fail\n");
+
+- pm_runtime_put_sync(&pdev->dev);
++ pm_runtime_put_sync(dev);
+ afe_priv->pm_runtime_bypass_reg_ctl = false;
+
+ regcache_cache_only(afe->regmap, true);
+@@ -2301,7 +2300,7 @@ static int mt8192_afe_pcm_dev_probe(stru
+ afe->runtime_suspend = mt8192_afe_runtime_suspend;
+
+ /* register platform */
+- ret = devm_snd_soc_register_component(&pdev->dev,
++ ret = devm_snd_soc_register_component(dev,
+ &mtk_afe_pcm_platform,
+ afe->dai_drivers,
+ afe->num_dai_drivers);
--- /dev/null
+From stable+bounces-288170-greg=kroah.com@vger.kernel.org Thu Jul 23 04:29:23 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 22:29:12 -0400
+Subject: ASoC: mediatek: mt8192-afe-pcm: Simplify with dev_err_probe()
+To: stable@vger.kernel.org
+Cc: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>, Chen-Yu Tsai <wenst@chromium.org>, Mark Brown <broonie@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260723022915.2845450-3-sashal@kernel.org>
+
+From: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+
+[ Upstream commit 324c603a4efca7d1045e0bf3477ca54970eac72c ]
+
+Simplify the probe function by switching error prints to return
+dev_err_probe(), lowering the lines count; while at it, also
+beautify some messages and change some others' level from warn
+to error.
+
+Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+Reviewed-by: Chen-Yu Tsai <wenst@chromium.org>
+Link: https://msgid.link/r/20240313110147.1267793-3-angelogioacchino.delregno@collabora.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Stable-dep-of: e24d5dde56a5 ("ASoC: mediatek: mt8192: Check runtime resume during probe")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ sound/soc/mediatek/mt8192/mt8192-afe-pcm.c | 65 +++++++++--------------------
+ 1 file changed, 22 insertions(+), 43 deletions(-)
+
+--- a/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c
++++ b/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c
+@@ -2221,17 +2221,12 @@ static int mt8192_afe_pcm_dev_probe(stru
+
+ /* reset controller to reset audio regs before regmap cache */
+ rstc = devm_reset_control_get_exclusive(dev, "audiosys");
+- if (IS_ERR(rstc)) {
+- ret = PTR_ERR(rstc);
+- dev_err(dev, "could not get audiosys reset:%d\n", ret);
+- return ret;
+- }
++ if (IS_ERR(rstc))
++ return dev_err_probe(dev, PTR_ERR(rstc), "could not get audiosys reset\n");
+
+ ret = reset_control_reset(rstc);
+- if (ret) {
+- dev_err(dev, "failed to trigger audio reset:%d\n", ret);
+- return ret;
+- }
++ if (ret)
++ return dev_err_probe(dev, ret, "failed to trigger audio reset\n");
+
+ ret = devm_pm_runtime_enable(&pdev->dev);
+ if (ret)
+@@ -2239,25 +2234,21 @@ static int mt8192_afe_pcm_dev_probe(stru
+
+ /* regmap init */
+ afe->regmap = syscon_node_to_regmap(dev->parent->of_node);
+- if (IS_ERR(afe->regmap)) {
+- dev_err(dev, "could not get regmap from parent\n");
+- return PTR_ERR(afe->regmap);
+- }
++ if (IS_ERR(afe->regmap))
++ return dev_err_probe(dev, PTR_ERR(afe->regmap),
++ "could not get regmap from parent");
++
+ ret = regmap_attach_dev(dev, afe->regmap, &mt8192_afe_regmap_config);
+- if (ret) {
+- dev_warn(dev, "regmap_attach_dev fail, ret %d\n", ret);
+- return ret;
+- }
++ if (ret)
++ return dev_err_probe(dev, ret, "regmap_attach_dev fail\n");
+
+ /* enable clock for regcache get default value from hw */
+ afe_priv->pm_runtime_bypass_reg_ctl = true;
+ pm_runtime_get_sync(&pdev->dev);
+
+ ret = regmap_reinit_cache(afe->regmap, &mt8192_afe_regmap_config);
+- if (ret) {
+- dev_err(dev, "regmap_reinit_cache fail, ret %d\n", ret);
+- return ret;
+- }
++ if (ret)
++ return dev_err_probe(dev, ret, "regmap_reinit_cache fail\n");
+
+ pm_runtime_put_sync(&pdev->dev);
+ afe_priv->pm_runtime_bypass_reg_ctl = false;
+@@ -2297,30 +2288,22 @@ static int mt8192_afe_pcm_dev_probe(stru
+
+ ret = devm_request_irq(dev, irq_id, mt8192_afe_irq_handler,
+ IRQF_TRIGGER_NONE, "asys-isr", (void *)afe);
+- if (ret) {
+- dev_err(dev, "could not request_irq for Afe_ISR_Handle\n");
+- return ret;
+- }
++ if (ret)
++ return dev_err_probe(dev, ret, "could not request_irq for Afe_ISR_Handle\n");
+
+ /* init sub_dais */
+ INIT_LIST_HEAD(&afe->sub_dais);
+
+ for (i = 0; i < ARRAY_SIZE(dai_register_cbs); i++) {
+ ret = dai_register_cbs[i](afe);
+- if (ret) {
+- dev_warn(afe->dev, "dai register i %d fail, ret %d\n",
+- i, ret);
+- return ret;
+- }
++ if (ret)
++ return dev_err_probe(afe->dev, ret, "dai %d register fail", i);
+ }
+
+ /* init dai_driver and component_driver */
+ ret = mtk_afe_combine_sub_dai(afe);
+- if (ret) {
+- dev_warn(afe->dev, "mtk_afe_combine_sub_dai fail, ret %d\n",
+- ret);
+- return ret;
+- }
++ if (ret)
++ return dev_err_probe(afe->dev, ret, "mtk_afe_combine_sub_dai fail\n");
+
+ /* others */
+ afe->mtk_afe_hardware = &mt8192_afe_hardware;
+@@ -2336,19 +2319,15 @@ static int mt8192_afe_pcm_dev_probe(stru
+ /* register platform */
+ ret = devm_snd_soc_register_component(&pdev->dev,
+ &mt8192_afe_component, NULL, 0);
+- if (ret) {
+- dev_warn(dev, "err_platform\n");
+- return ret;
+- }
++ if (ret)
++ return dev_err_probe(dev, ret, "Couldn't register AFE component\n");
+
+ ret = devm_snd_soc_register_component(&pdev->dev,
+ &mt8192_afe_pcm_component,
+ afe->dai_drivers,
+ afe->num_dai_drivers);
+- if (ret) {
+- dev_warn(dev, "err_dai_component\n");
+- return ret;
+- }
++ if (ret)
++ return dev_err_probe(dev, ret, "Couldn't register AFE-PCM component\n");
+
+ return 0;
+ }
--- /dev/null
+From stable+bounces-288173-greg=kroah.com@vger.kernel.org Thu Jul 23 04:29:56 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 22:29:15 -0400
+Subject: ASoC: mediatek: mt8192: Check runtime resume during probe
+To: stable@vger.kernel.org
+Cc: "Cássio Gabriel" <cassiogabrielcontato@gmail.com>, "Mark Brown" <broonie@kernel.org>, "Sasha Levin" <sashal@kernel.org>
+Message-ID: <20260723022915.2845450-6-sashal@kernel.org>
+
+From: Cássio Gabriel <cassiogabrielcontato@gmail.com>
+
+[ Upstream commit e24d5dde56a50946020b134fa8448869093db76a ]
+
+The MT8192 AFE probe enables runtime PM temporarily while reinitializing
+the regmap cache from hardware, but it uses pm_runtime_get_sync()
+without checking the return value. If runtime resume fails, probe keeps
+going without the device necessarily being accessible, and
+pm_runtime_get_sync() may leave the PM usage count incremented.
+
+The regmap_reinit_cache() failure path also returns before dropping the
+temporary PM reference and before clearing pm_runtime_bypass_reg_ctl.
+
+Use pm_runtime_resume_and_get() so resume failures do not leak a usage
+count, and clear the temporary bypass flag after dropping the probe PM
+reference on all regmap_reinit_cache() outcomes.
+
+Fixes: 125ab5d588b0 ("ASoC: mediatek: mt8192: add platform driver")
+Cc: stable@vger.kernel.org
+Signed-off-by: Cássio Gabriel <cassiogabrielcontato@gmail.com>
+Link: https://patch.msgid.link/20260527-asoc-mt8192-probe-cleanup-v1-2-1bb834d05b72@gmail.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ sound/soc/mediatek/mt8192/mt8192-afe-pcm.c | 12 ++++++++----
+ 1 file changed, 8 insertions(+), 4 deletions(-)
+
+--- a/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c
++++ b/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c
+@@ -2227,15 +2227,19 @@ static int mt8192_afe_pcm_dev_probe(stru
+
+ /* enable clock for regcache get default value from hw */
+ afe_priv->pm_runtime_bypass_reg_ctl = true;
+- pm_runtime_get_sync(dev);
++ ret = pm_runtime_resume_and_get(dev);
++ if (ret) {
++ afe_priv->pm_runtime_bypass_reg_ctl = false;
++ return dev_err_probe(dev, ret, "failed to resume device\n");
++ }
+
+ ret = regmap_reinit_cache(afe->regmap, &mt8192_afe_regmap_config);
+- if (ret)
+- return dev_err_probe(dev, ret, "regmap_reinit_cache fail\n");
+-
+ pm_runtime_put_sync(dev);
+ afe_priv->pm_runtime_bypass_reg_ctl = false;
+
++ if (ret)
++ return dev_err_probe(dev, ret, "regmap_reinit_cache fail\n");
++
+ regcache_cache_only(afe->regmap, true);
+ regcache_mark_dirty(afe->regmap);
+
--- /dev/null
+From stable+bounces-288168-greg=kroah.com@vger.kernel.org Thu Jul 23 04:29:22 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 22:29:10 -0400
+Subject: ASoC: mediatek: mt8195: Remove afe-dai component and rework codec link
+To: stable@vger.kernel.org
+Cc: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>, Mark Brown <broonie@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260723022915.2845450-1-sashal@kernel.org>
+
+From: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+
+[ Upstream commit e70b8dd26711704b1ff1f1b4eb3d048ba69e29da ]
+
+Remove the extra 'mt8195-afe-pcm-dai' component, register the DAI
+drivers to the main AFE component, and rework the DAI linking between
+the headset codec (RT5682/RT5682S) and the TDM interface in the probe
+function to stop assigning name, relying on the of_node of the codec.
+Also replace the COMP_DUMMY codec entry with a COMP_EMPTY for the
+ETDM2_IN and remove it entirely from ETDM1_OUT to fix the registration
+flow for this sound card.
+
+While at it, since we also need to swap the codec init function from
+ETDM2_IN to ETDM1_OUT, remove the static assignment of both `ops` and
+`init` for both, as we now assign these dynamically during probe.
+
+Fixes: 13f58267cda3 ("ASoC: soc.h: don't create dummy Component via COMP_DUMMY()")
+Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+Link: https://msgid.link/r/20240110105757.539089-1-angelogioacchino.delregno@collabora.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Stable-dep-of: e24d5dde56a5 ("ASoC: mediatek: mt8192: Check runtime resume during probe")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ sound/soc/mediatek/mt8195/mt8195-afe-pcm.c | 33 -----------------------
+ sound/soc/mediatek/mt8195/mt8195-mt6359.c | 41 +++++++++++++++++++----------
+ 2 files changed, 29 insertions(+), 45 deletions(-)
+
+--- a/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c
++++ b/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c
+@@ -1795,10 +1795,6 @@ static const struct snd_kcontrol_new mt8
+ MT8195_AFE_IRQ_28),
+ };
+
+-static const struct snd_soc_component_driver mt8195_afe_pcm_dai_component = {
+- .name = "mt8195-afe-pcm-dai",
+-};
+-
+ static const struct mtk_base_memif_data memif_data[MT8195_AFE_MEMIF_NUM] = {
+ [MT8195_AFE_MEMIF_DL2] = {
+ .name = "DL2",
+@@ -3037,7 +3033,6 @@ static int mt8195_afe_pcm_dev_probe(stru
+ struct device *dev = &pdev->dev;
+ struct reset_control *rstc;
+ int i, irq_id, ret;
+- struct snd_soc_component *component;
+
+ ret = of_reserved_mem_device_init(dev);
+ if (ret)
+@@ -3170,36 +3165,12 @@ static int mt8195_afe_pcm_dev_probe(stru
+
+ /* register component */
+ ret = devm_snd_soc_register_component(dev, &mt8195_afe_component,
+- NULL, 0);
++ afe->dai_drivers, afe->num_dai_drivers);
+ if (ret) {
+ dev_warn(dev, "err_platform\n");
+ goto err_pm_put;
+ }
+
+- component = devm_kzalloc(dev, sizeof(*component), GFP_KERNEL);
+- if (!component) {
+- ret = -ENOMEM;
+- goto err_pm_put;
+- }
+-
+- ret = snd_soc_component_initialize(component,
+- &mt8195_afe_pcm_dai_component,
+- dev);
+- if (ret)
+- goto err_pm_put;
+-
+-#ifdef CONFIG_DEBUG_FS
+- component->debugfs_prefix = "pcm";
+-#endif
+-
+- ret = snd_soc_add_component(component,
+- afe->dai_drivers,
+- afe->num_dai_drivers);
+- if (ret) {
+- dev_warn(dev, "err_dai_component\n");
+- goto err_pm_put;
+- }
+-
+ ret = regmap_multi_reg_write(afe->regmap, mt8195_afe_reg_defaults,
+ ARRAY_SIZE(mt8195_afe_reg_defaults));
+ if (ret)
+@@ -3224,8 +3195,6 @@ err_pm_put:
+
+ static void mt8195_afe_pcm_dev_remove(struct platform_device *pdev)
+ {
+- snd_soc_unregister_component(&pdev->dev);
+-
+ pm_runtime_disable(&pdev->dev);
+ if (!pm_runtime_status_suspended(&pdev->dev))
+ mt8195_afe_runtime_suspend(&pdev->dev);
+--- a/sound/soc/mediatek/mt8195/mt8195-mt6359.c
++++ b/sound/soc/mediatek/mt8195/mt8195-mt6359.c
+@@ -934,12 +934,11 @@ SND_SOC_DAILINK_DEFS(ETDM1_IN_BE,
+
+ SND_SOC_DAILINK_DEFS(ETDM2_IN_BE,
+ DAILINK_COMP_ARRAY(COMP_CPU("ETDM2_IN")),
+- DAILINK_COMP_ARRAY(COMP_DUMMY()),
++ DAILINK_COMP_ARRAY(COMP_EMPTY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+ SND_SOC_DAILINK_DEFS(ETDM1_OUT_BE,
+ DAILINK_COMP_ARRAY(COMP_CPU("ETDM1_OUT")),
+- DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+ SND_SOC_DAILINK_DEFS(ETDM2_OUT_BE,
+@@ -1237,8 +1236,6 @@ static struct snd_soc_dai_link mt8195_mt
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS,
+ .dpcm_capture = 1,
+- .init = mt8195_rt5682_init,
+- .ops = &mt8195_rt5682_etdm_ops,
+ .be_hw_params_fixup = mt8195_etdm_hw_params_fixup,
+ SND_SOC_DAILINK_REG(ETDM2_IN_BE),
+ },
+@@ -1249,7 +1246,6 @@ static struct snd_soc_dai_link mt8195_mt
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS,
+ .dpcm_playback = 1,
+- .ops = &mt8195_rt5682_etdm_ops,
+ .be_hw_params_fixup = mt8195_etdm_hw_params_fixup,
+ SND_SOC_DAILINK_REG(ETDM1_OUT_BE),
+ },
+@@ -1381,7 +1377,7 @@ static int mt8195_mt6359_dev_probe(struc
+ struct snd_soc_dai_link *dai_link;
+ struct mtk_soc_card_data *soc_card_data;
+ struct mt8195_mt6359_priv *mach_priv;
+- struct device_node *platform_node, *adsp_node, *dp_node, *hdmi_node;
++ struct device_node *platform_node, *adsp_node, *codec_node, *dp_node, *hdmi_node;
+ struct mt8195_card_data *card_data;
+ int is5682s = 0;
+ int init6359 = 0;
+@@ -1401,8 +1397,12 @@ static int mt8195_mt6359_dev_probe(struc
+ if (!card->name)
+ card->name = card_data->name;
+
+- if (strstr(card->name, "_5682s"))
++ if (strstr(card->name, "_5682s")) {
++ codec_node = of_find_compatible_node(NULL, NULL, "realtek,rt5682s");
+ is5682s = 1;
++ } else
++ codec_node = of_find_compatible_node(NULL, NULL, "realtek,rt5682i");
++
+ soc_card_data = devm_kzalloc(&pdev->dev, sizeof(*card_data), GFP_KERNEL);
+ if (!soc_card_data)
+ return -ENOMEM;
+@@ -1488,12 +1488,27 @@ static int mt8195_mt6359_dev_probe(struc
+ dai_link->codecs->dai_name = "i2s-hifi";
+ dai_link->init = mt8195_hdmi_codec_init;
+ }
+- } else if (strcmp(dai_link->name, "ETDM1_OUT_BE") == 0 ||
+- strcmp(dai_link->name, "ETDM2_IN_BE") == 0) {
+- dai_link->codecs->name =
+- is5682s ? RT5682S_DEV0_NAME : RT5682_DEV0_NAME;
+- dai_link->codecs->dai_name =
+- is5682s ? RT5682S_CODEC_DAI : RT5682_CODEC_DAI;
++ } else if (strcmp(dai_link->name, "ETDM1_OUT_BE") == 0) {
++ if (!codec_node) {
++ dev_err(&pdev->dev, "Codec not found!\n");
++ } else {
++ dai_link->codecs->of_node = codec_node;
++ dai_link->codecs->name = NULL;
++ dai_link->codecs->dai_name =
++ is5682s ? RT5682S_CODEC_DAI : RT5682_CODEC_DAI;
++ dai_link->init = mt8195_rt5682_init;
++ dai_link->ops = &mt8195_rt5682_etdm_ops;
++ }
++ } else if (strcmp(dai_link->name, "ETDM2_IN_BE") == 0) {
++ if (!codec_node) {
++ dev_err(&pdev->dev, "Codec not found!\n");
++ } else {
++ dai_link->codecs->of_node = codec_node;
++ dai_link->codecs->name = NULL;
++ dai_link->codecs->dai_name =
++ is5682s ? RT5682S_CODEC_DAI : RT5682_CODEC_DAI;
++ dai_link->ops = &mt8195_rt5682_etdm_ops;
++ }
+ } else if (strcmp(dai_link->name, "DL_SRC_BE") == 0 ||
+ strcmp(dai_link->name, "UL_SRC1_BE") == 0 ||
+ strcmp(dai_link->name, "UL_SRC2_BE") == 0) {
--- /dev/null
+From stable+bounces-288171-greg=kroah.com@vger.kernel.org Thu Jul 23 04:29:24 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 22:29:13 -0400
+Subject: ASoC: mediatek: Use common mtk_afe_pcm_platform with common probe cb
+To: stable@vger.kernel.org
+Cc: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>, Alexandre Mergnat <amergnat@baylibre.com>, Mark Brown <broonie@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260723022915.2845450-4-sashal@kernel.org>
+
+From: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+
+[ Upstream commit 1d2a6b3b5189b6c3e51f29e050aa9b2337b774e8 ]
+
+Since the mtk-afe-platform-driver generic mtk_afe_pcm_platform now has
+a common .probe() callback, there is no reason to keep duplicating this
+function over and over in the SoC specific AFE-PCM drivers: switch over
+to register with the common bits instead.
+
+Note that MT8186 was left out of this because it is registering some
+extra sinegen controls in the AFE-PCM probe callback and needs extra
+cleanups to be able to use the common bits.
+
+Reviewed-by: Alexandre Mergnat <amergnat@baylibre.com>
+Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+Link: https://lore.kernel.org/r/20240416071410.75620-13-angelogioacchino.delregno@collabora.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Stable-dep-of: e24d5dde56a5 ("ASoC: mediatek: mt8192: Check runtime resume during probe")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ sound/soc/mediatek/mt6797/mt6797-afe-pcm.c | 14 +-------------
+ sound/soc/mediatek/mt7986/mt7986-afe-pcm.c | 14 +-------------
+ sound/soc/mediatek/mt8183/mt8183-afe-pcm.c | 14 +-------------
+ sound/soc/mediatek/mt8188/mt8188-afe-pcm.c | 21 +--------------------
+ sound/soc/mediatek/mt8192/mt8192-afe-pcm.c | 25 ++-----------------------
+ sound/soc/mediatek/mt8195/mt8195-afe-pcm.c | 21 +--------------------
+ 6 files changed, 7 insertions(+), 102 deletions(-)
+
+--- a/sound/soc/mediatek/mt6797/mt6797-afe-pcm.c
++++ b/sound/soc/mediatek/mt6797/mt6797-afe-pcm.c
+@@ -704,18 +704,6 @@ static int mt6797_afe_runtime_resume(str
+ return 0;
+ }
+
+-static int mt6797_afe_component_probe(struct snd_soc_component *component)
+-{
+- return mtk_afe_add_sub_dai_control(component);
+-}
+-
+-static const struct snd_soc_component_driver mt6797_afe_component = {
+- .name = AFE_PCM_NAME,
+- .probe = mt6797_afe_component_probe,
+- .pointer = mtk_afe_pcm_pointer,
+- .pcm_construct = mtk_afe_pcm_new,
+-};
+-
+ static int mt6797_dai_memif_register(struct mtk_base_afe *afe)
+ {
+ struct mtk_base_afe_dai *dai;
+@@ -852,7 +840,7 @@ static int mt6797_afe_pcm_dev_probe(stru
+ pm_runtime_get_sync(&pdev->dev);
+
+ /* register component */
+- ret = devm_snd_soc_register_component(dev, &mt6797_afe_component,
++ ret = devm_snd_soc_register_component(dev, &mtk_afe_pcm_platform,
+ NULL, 0);
+ if (ret) {
+ dev_warn(dev, "err_platform\n");
+--- a/sound/soc/mediatek/mt7986/mt7986-afe-pcm.c
++++ b/sound/soc/mediatek/mt7986/mt7986-afe-pcm.c
+@@ -429,18 +429,6 @@ static int mt7986_afe_runtime_resume(str
+ return 0;
+ }
+
+-static int mt7986_afe_component_probe(struct snd_soc_component *component)
+-{
+- return mtk_afe_add_sub_dai_control(component);
+-}
+-
+-static const struct snd_soc_component_driver mt7986_afe_component = {
+- .name = AFE_PCM_NAME,
+- .probe = mt7986_afe_component_probe,
+- .pointer = mtk_afe_pcm_pointer,
+- .pcm_construct = mtk_afe_pcm_new,
+-};
+-
+ static int mt7986_dai_memif_register(struct mtk_base_afe *afe)
+ {
+ struct mtk_base_afe_dai *dai;
+@@ -573,7 +561,7 @@ static int mt7986_afe_pcm_dev_probe(stru
+
+ /* register component */
+ ret = devm_snd_soc_register_component(&pdev->dev,
+- &mt7986_afe_component,
++ &mtk_afe_pcm_platform,
+ NULL, 0);
+ if (ret)
+ return dev_err_probe(dev, ret, "Cannot register AFE component\n");
+--- a/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c
++++ b/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c
+@@ -1043,18 +1043,6 @@ skip_regmap:
+ return 0;
+ }
+
+-static int mt8183_afe_component_probe(struct snd_soc_component *component)
+-{
+- return mtk_afe_add_sub_dai_control(component);
+-}
+-
+-static const struct snd_soc_component_driver mt8183_afe_component = {
+- .name = AFE_PCM_NAME,
+- .probe = mt8183_afe_component_probe,
+- .pointer = mtk_afe_pcm_pointer,
+- .pcm_construct = mtk_afe_pcm_new,
+-};
+-
+ static int mt8183_dai_memif_register(struct mtk_base_afe *afe)
+ {
+ struct mtk_base_afe_dai *dai;
+@@ -1250,7 +1238,7 @@ static int mt8183_afe_pcm_dev_probe(stru
+
+ /* register component */
+ ret = devm_snd_soc_register_component(&pdev->dev,
+- &mt8183_afe_component,
++ &mtk_afe_pcm_platform,
+ NULL, 0);
+ if (ret) {
+ dev_warn(dev, "err_platform\n");
+--- a/sound/soc/mediatek/mt8188/mt8188-afe-pcm.c
++++ b/sound/soc/mediatek/mt8188/mt8188-afe-pcm.c
+@@ -3027,25 +3027,6 @@ skip_regmap:
+ return 0;
+ }
+
+-static int mt8188_afe_component_probe(struct snd_soc_component *component)
+-{
+- struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component);
+- int ret;
+-
+- snd_soc_component_init_regmap(component, afe->regmap);
+-
+- ret = mtk_afe_add_sub_dai_control(component);
+-
+- return ret;
+-}
+-
+-static const struct snd_soc_component_driver mt8188_afe_component = {
+- .name = AFE_PCM_NAME,
+- .pointer = mtk_afe_pcm_pointer,
+- .pcm_construct = mtk_afe_pcm_new,
+- .probe = mt8188_afe_component_probe,
+-};
+-
+ static int init_memif_priv_data(struct mtk_base_afe *afe)
+ {
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+@@ -3347,7 +3328,7 @@ static int mt8188_afe_pcm_dev_probe(stru
+ }
+
+ /* register component */
+- ret = devm_snd_soc_register_component(dev, &mt8188_afe_component,
++ ret = devm_snd_soc_register_component(dev, &mtk_afe_pcm_platform,
+ afe->dai_drivers, afe->num_dai_drivers);
+ if (ret) {
+ dev_warn(dev, "err_platform\n");
+--- a/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c
++++ b/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c
+@@ -2126,22 +2126,6 @@ skip_regmap:
+ return 0;
+ }
+
+-static int mt8192_afe_component_probe(struct snd_soc_component *component)
+-{
+- return mtk_afe_add_sub_dai_control(component);
+-}
+-
+-static const struct snd_soc_component_driver mt8192_afe_component = {
+- .name = AFE_PCM_NAME,
+- .probe = mt8192_afe_component_probe,
+- .pointer = mtk_afe_pcm_pointer,
+- .pcm_construct = mtk_afe_pcm_new,
+-};
+-
+-static const struct snd_soc_component_driver mt8192_afe_pcm_component = {
+- .name = "mt8192-afe-pcm-dai",
+-};
+-
+ static int mt8192_dai_memif_register(struct mtk_base_afe *afe)
+ {
+ struct mtk_base_afe_dai *dai;
+@@ -2318,16 +2302,11 @@ static int mt8192_afe_pcm_dev_probe(stru
+
+ /* register platform */
+ ret = devm_snd_soc_register_component(&pdev->dev,
+- &mt8192_afe_component, NULL, 0);
+- if (ret)
+- return dev_err_probe(dev, ret, "Couldn't register AFE component\n");
+-
+- ret = devm_snd_soc_register_component(&pdev->dev,
+- &mt8192_afe_pcm_component,
++ &mtk_afe_pcm_platform,
+ afe->dai_drivers,
+ afe->num_dai_drivers);
+ if (ret)
+- return dev_err_probe(dev, ret, "Couldn't register AFE-PCM component\n");
++ return dev_err_probe(dev, ret, "Couldn't register AFE component\n");
+
+ return 0;
+ }
+--- a/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c
++++ b/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c
+@@ -2944,25 +2944,6 @@ skip_regmap:
+ return 0;
+ }
+
+-static int mt8195_afe_component_probe(struct snd_soc_component *component)
+-{
+- struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component);
+- int ret = 0;
+-
+- snd_soc_component_init_regmap(component, afe->regmap);
+-
+- ret = mtk_afe_add_sub_dai_control(component);
+-
+- return ret;
+-}
+-
+-static const struct snd_soc_component_driver mt8195_afe_component = {
+- .name = AFE_PCM_NAME,
+- .pointer = mtk_afe_pcm_pointer,
+- .pcm_construct = mtk_afe_pcm_new,
+- .probe = mt8195_afe_component_probe,
+-};
+-
+ static int init_memif_priv_data(struct mtk_base_afe *afe)
+ {
+ struct mt8195_afe_private *afe_priv = afe->platform_priv;
+@@ -3164,7 +3145,7 @@ static int mt8195_afe_pcm_dev_probe(stru
+ }
+
+ /* register component */
+- ret = devm_snd_soc_register_component(dev, &mt8195_afe_component,
++ ret = devm_snd_soc_register_component(dev, &mtk_afe_pcm_platform,
+ afe->dai_drivers, afe->num_dai_drivers);
+ if (ret) {
+ dev_warn(dev, "err_platform\n");
--- /dev/null
+From stable+bounces-290234-greg=kroah.com@vger.kernel.org Wed Jul 29 04:40:14 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Jul 2026 22:40:07 -0400
+Subject: ata: libata-core: Reject an invalid concurrent positioning ranges count
+To: stable@vger.kernel.org
+Cc: Bryam Vargas <hexlabsecurity@proton.me>, Damien Le Moal <dlemoal@kernel.org>, Niklas Cassel <cassel@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260729024007.1127363-1-sashal@kernel.org>
+
+From: Bryam Vargas <hexlabsecurity@proton.me>
+
+[ Upstream commit 533a0b940f901c15e5cbbd4b5d66e871c209e8ce ]
+
+ata_dev_config_cpr() takes the number of range descriptors from buf[0]
+of the concurrent positioning ranges log (up to 255), which the device
+reports independently of the log size in the GPL directory. The count is
+then walked at a fixed 32-byte stride in two places with no bound: the
+log read here, and the INQUIRY VPD page B9h emitter, which writes one
+descriptor per range into the fixed 2048-byte ata_scsi_rbuf. A device
+reporting a count larger than its own log overflows the read buffer (up
+to 7704 bytes past a 512-byte slab), and a count above 62 overflows the
+response buffer on the emit side.
+
+Bound the count once, on probe, against both the log the device returned
+and the number of descriptors the VPD B9h response buffer can hold
+(ATA_DEV_MAX_CPR, derived from the rbuf size). Reject an out-of-range
+count with a warning; this keeps the emitter in bounds with no separate
+change there.
+
+Suggested-by: Damien Le Moal <dlemoal@kernel.org>
+Fixes: fe22e1c2f705 ("libata: support concurrent positioning ranges log")
+Fixes: c745dfc541e7 ("libata: fix reading concurrent positioning ranges log")
+Cc: stable@vger.kernel.org
+Signed-off-by: Bryam Vargas <hexlabsecurity@proton.me>
+Reviewed-by: Niklas Cassel <cassel@kernel.org>
+Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
+[ adapted `kzalloc_flex()` allocation to `kzalloc(struct_size(...), GFP_KERNEL)` and adjusted context offsets. ]
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/ata/libata-core.c | 18 ++++++++++++++++++
+ drivers/ata/libata-scsi.c | 2 --
+ drivers/ata/libata.h | 9 +++++++++
+ 3 files changed, 27 insertions(+), 2 deletions(-)
+
+--- a/drivers/ata/libata-core.c
++++ b/drivers/ata/libata-core.c
+@@ -2794,6 +2794,24 @@ static void ata_dev_config_cpr(struct at
+ if (!nr_cpr)
+ goto out;
+
++ /*
++ * The device reports the number of CPR descriptors independently of the
++ * log size, and that count is also used to emit VPD page B9h into the
++ * fixed-size rbuf. Reject a count larger than what that buffer can hold
++ * (ATA_DEV_MAX_CPR) or larger than the log the device actually returned.
++ */
++ if (nr_cpr > ATA_DEV_MAX_CPR) {
++ ata_dev_warn(dev,
++ "Too many concurrent positioning ranges\n");
++ goto out;
++ }
++
++ if (buf_len < 64 + (size_t)nr_cpr * 32) {
++ ata_dev_warn(dev,
++ "Invalid number of concurrent positioning ranges\n");
++ goto out;
++ }
++
+ cpr_log = kzalloc(struct_size(cpr_log, cpr, nr_cpr), GFP_KERNEL);
+ if (!cpr_log)
+ goto out;
+--- a/drivers/ata/libata-scsi.c
++++ b/drivers/ata/libata-scsi.c
+@@ -37,8 +37,6 @@
+ #include "libata.h"
+ #include "libata-transport.h"
+
+-#define ATA_SCSI_RBUF_SIZE 2048
+-
+ static DEFINE_SPINLOCK(ata_scsi_rbuf_lock);
+ static u8 ata_scsi_rbuf[ATA_SCSI_RBUF_SIZE];
+
+--- a/drivers/ata/libata.h
++++ b/drivers/ata/libata.h
+@@ -111,6 +111,15 @@ static inline void ata_acpi_bind_dev(str
+ #endif
+
+ /* libata-scsi.c */
++#define ATA_SCSI_RBUF_SIZE 2048
++
++/*
++ * Maximum number of concurrent positioning ranges (CPR) supported. The ACS
++ * specifications allow up to 255, but we limit this to the number of CPR
++ * descriptors that fit in the rbuf buffer used to emit VPD page B9h.
++ */
++#define ATA_DEV_MAX_CPR min(255, ((ATA_SCSI_RBUF_SIZE - 64) / 32))
++
+ extern struct ata_device *ata_scsi_find_dev(struct ata_port *ap,
+ const struct scsi_device *scsidev);
+ extern int ata_scsi_add_hosts(struct ata_host *host,
--- /dev/null
+From stable+bounces-287679-greg=kroah.com@vger.kernel.org Wed Jul 22 04:46:12 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 21 Jul 2026 20:51:15 -0400
+Subject: audit: fix recursive locking deadlock in audit_dupe_exe()
+To: stable@vger.kernel.org
+Cc: Ricardo Robaina <rrobaina@redhat.com>, stable@kernel.org, Waiman Long <longman@redhat.com>, Richard Guy Briggs <rgb@redhat.com>, Nathan Chancellor <nathan@kernel.org>, Paul Moore <paul@paul-moore.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260722005116.605624-4-sashal@kernel.org>
+
+From: Ricardo Robaina <rrobaina@redhat.com>
+
+[ Upstream commit 81905b5acbe77284734438df3fbec1158e6429a3 ]
+
+A deadlock occurs in the audit subsystem when duplicating
+executable-related rules.
+
+When a file is moved (e.g., via do_renameat2()), the VFS layer locks
+the parent directory (I_MUTEX_PARENT), which synchronously triggers an
+fsnotify_move event. If an existing executable audit rule matches the
+file being moved, the audit subsystem catches this event and calls
+audit_dupe_exe() to duplicate the watch and update the rule. Then,
+audit_alloc_mark() would call kern_path_parent() to resolve the path,
+leading to a blind attempt to acquire the exact same I_MUTEX_PARENT lock
+already held by the task, resulting in the following recursive locking
+deadlock:
+
+ ============================================
+ WARNING: possible recursive locking detected
+ 6.12.0-55.27.1.el10_0.x86_64+debug #1 Not tainted
+ --------------------------------------------
+ mv/5099 is trying to acquire lock:
+ ffff888132845358 (&inode->i_sb->s_type->i_mutex_dir_key/1){+.+.}-{3:3},
+ at: __kern_path_locked+0x10a/0x2f0
+
+ but task is already holding lock:
+ ffff888132846b58 (&inode->i_sb->s_type->i_mutex_dir_key/1){+.+.}-{3:3},
+ at: lock_two_directories+0x13f/0x2b0
+
+ other info that might help us debug this:
+ Possible unsafe locking scenario:
+
+ CPU0
+ ----
+ lock(&inode->i_sb->s_type->i_mutex_dir_key/1);
+ lock(&inode->i_sb->s_type->i_mutex_dir_key/1);
+
+ *** DEADLOCK ***
+
+ May be due to missing lock nesting notation
+
+ 6 locks held by mv/5099:
+ #0: ffff888112a9c440 (sb_writers#13)
+ at: do_renameat2+0x34c/0xbc0
+ #1: ffff888112a9c790 (&type->s_vfs_rename_key#3)
+ at: do_renameat2+0x415/0xbc0
+ #2: ffff888132846b58 (&inode->i_sb->s_type->i_mutex_dir_key/1)
+ at: lock_two_directories+0x13f/0x2b0
+ #3: ffff888132845358 (&inode->i_sb->s_type->i_mutex_dir_key/5)
+ at: lock_two_directories+0x175/0x2b0
+ #4: ffffffffb3a1fb10 (&fsnotify_mark_srcu)
+ at: fsnotify+0x454/0x28a0
+ #5: ffffffffaf886230 (audit_filter_mutex)
+ at: audit_update_watch+0x36/0x11e0
+
+ stack backtrace:
+ Call Trace:
+ <TASK>
+ dump_stack_lvl+0x6f/0xb0
+ print_deadlock_bug.cold+0xbd/0xca
+ validate_chain+0x83a/0xf00
+ __lock_acquire+0xcac/0x1d20
+ lock_acquire.part.0+0x11b/0x360
+ down_write_nested+0x9f/0x230
+ __kern_path_locked+0x10a/0x2f0
+ kern_path_locked+0x26/0x40
+ audit_alloc_mark+0xfb/0x4f0
+ audit_dupe_exe+0x6c/0xe0
+ audit_dupe_rule+0x6c2/0xc00
+ audit_update_watch+0x4cc/0x11e0
+ audit_watch_handle_event+0x12c/0x1b0
+ send_to_group+0x5d0/0x8b0
+ fsnotify+0x615/0x28a0
+ fsnotify_move+0x1d8/0x630
+ vfs_rename+0xdcd/0x1df0
+ do_renameat2+0x9d4/0xbc0
+ __x64_sys_renameat+0x192/0x260
+ do_syscall_64+0x92/0x180
+ entry_SYSCALL_64_after_hwframe+0x76/0x7e
+ RIP: 0033:0x7f0491fe8c4e
+ Code: 0f 1f 40 00 48 8b 15 c1 e1 16 00 f7 d8 64 89 02 b8 ff ff ff ff
+ c3 66 0f 1f 44 00 00 f3 0f 1e fa 49 89 ca b8 08 01 00 00 0f 05 <48>
+ 3d 00 f0 ff ff 77 0a c3 66 0f 1f 84 00 00 00 00 00 48 8b 15 89
+ RSP: 002b:00007ffc7210bf38 EFLAGS: 00000246 ORIG_RAX: 0000000000000108
+ RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 00007f0491fe8c4e
+ RDX: 0000000000000003 RSI: 00007ffc7210e6c8 RDI: 00000000ffffff9c
+ RBP: 0000000000000000 R08: 0000000000000000 R09: 0000000000000001
+ R10: 00005575eb2dae2a R11: 0000000000000246 R12: 00005575eb2dae2a
+ R13: 00007ffc7210e6c8 R14: 0000000000000003 R15: 00000000ffffff9c
+ </TASK>
+
+The aforementioned deadlock can be consistently reproduced by running
+the script below:
+
+ audit-dupe-exe-deadlock.sh
+ --------------------------
+ #!/bin/bash
+ auditctl -D
+ mkdir -p /tmp/foo
+ touch /tmp/file
+ auditctl -a always,exit -F exe=/tmp/file -F path=/tmp/file -S all -k dr
+ mv /tmp/file /tmp/foo/file
+ rm -Rf /tmp/foo
+
+This patch fixes the issue by introducing struct audit_watch_ctx to pass
+the fsnotify event context down to audit_alloc_mark(). By utilizing the
+already-resolved directory inode provided by the event, we bypass the
+kern_path_parent() path resolution entirely, safely avoiding the
+recursive lock. Furthermore, it explicitly allows duplicate fsnotify
+marks (allow_dups = 1) during the rename update, allowing the new rule's
+mark to safely coexist with the old rule's mark until the old rule is
+freed.
+
+P.S.: This issue was identified and reproduced during a comprehensive
+code coverage analysis of the audit subsystem. The full report is
+available at the link below:
+
+https://people.redhat.com/rrobaina/audit-code-coverage-analysis.pdf
+
+P.P.S: With the permission of both Ricardo and Nathan, I've squashed a
+fixup patch from Nathan that addresses a compile time error when
+CONFIG_AUDITSYSCALL=n.
+
+Cc: stable@kernel.org
+Fixes: 34d99af52ad4 ("audit: implement audit by executable")
+Acked-by: Waiman Long <longman@redhat.com>
+Acked-by: Richard Guy Briggs <rgb@redhat.com>
+Signed-off-by: Nathan Chancellor <nathan@kernel.org>
+Signed-off-by: Ricardo Robaina <rrobaina@redhat.com>
+[PM: move link metadata into the msg, apply fix from NC]
+Signed-off-by: Paul Moore <paul@paul-moore.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ kernel/audit.h | 17 ++++++++++++-----
+ kernel/audit_fsnotify.c | 32 +++++++++++++++++++++++---------
+ kernel/audit_watch.c | 25 +++++++++++++++++--------
+ kernel/auditfilter.c | 9 +++++----
+ 4 files changed, 57 insertions(+), 26 deletions(-)
+
+--- a/kernel/audit.h
++++ b/kernel/audit.h
+@@ -250,8 +250,13 @@ extern int audit_del_rule(struct audit_e
+ extern void audit_free_rule_rcu(struct rcu_head *head);
+ extern struct list_head audit_filter_list[];
+
+-extern struct audit_entry *audit_dupe_rule(struct audit_krule *old);
++struct audit_watch_ctx {
++ struct inode *dir;
++ struct inode *child;
++};
+
++extern struct audit_entry *audit_dupe_rule(struct audit_krule *old,
++ struct audit_watch_ctx *ctx);
+ extern void audit_log_d_path_exe(struct audit_buffer *ab,
+ struct mm_struct *mm);
+
+@@ -274,13 +279,15 @@ extern char *audit_watch_path(struct aud
+ extern int audit_watch_compare(struct audit_watch *watch, u64 ino, dev_t dev);
+
+ extern struct audit_fsnotify_mark *audit_alloc_mark(struct audit_krule *krule,
+- char *pathname, int len);
++ char *pathname, int len,
++ struct audit_watch_ctx *ctx);
+ extern char *audit_mark_path(struct audit_fsnotify_mark *mark);
+ extern void audit_remove_mark(struct audit_fsnotify_mark *audit_mark);
+ extern void audit_remove_mark_rule(struct audit_krule *krule);
+ extern int audit_mark_compare(struct audit_fsnotify_mark *mark, u64 ino,
+ dev_t dev);
+-extern int audit_dupe_exe(struct audit_krule *new, struct audit_krule *old);
++extern int audit_dupe_exe(struct audit_krule *new, struct audit_krule *old,
++ struct audit_watch_ctx *ctx);
+ extern int audit_exe_compare(struct task_struct *tsk,
+ struct audit_fsnotify_mark *mark);
+
+@@ -311,13 +318,13 @@ extern struct list_head *audit_killed_tr
+ #define audit_watch_path(w) ""
+ #define audit_watch_compare(w, i, d) 0
+
+-#define audit_alloc_mark(k, p, l) (ERR_PTR(-EINVAL))
++#define audit_alloc_mark(k, p, l, c) (ERR_PTR(-EINVAL))
+ #define audit_mark_path(m) ""
+ #define audit_remove_mark(m) do { } while (0)
+ #define audit_remove_mark_rule(k) do { } while (0)
+ #define audit_mark_compare(m, i, d) 0
+ #define audit_exe_compare(t, m) (-EINVAL)
+-#define audit_dupe_exe(n, o) (-EINVAL)
++#define audit_dupe_exe(n, o, c) (-EINVAL)
+
+ #define audit_remove_tree_rule(rule) BUG()
+ #define audit_add_tree_rule(rule) -EINVAL
+--- a/kernel/audit_fsnotify.c
++++ b/kernel/audit_fsnotify.c
+@@ -71,19 +71,30 @@ static void audit_update_mark(struct aud
+ audit_mark->ino = inode ? inode->i_ino : AUDIT_INO_UNSET;
+ }
+
+-struct audit_fsnotify_mark *audit_alloc_mark(struct audit_krule *krule, char *pathname, int len)
++struct audit_fsnotify_mark *audit_alloc_mark(struct audit_krule *krule, char *pathname,
++ int len, struct audit_watch_ctx *ctx)
+ {
+ struct audit_fsnotify_mark *audit_mark;
+ struct path path;
+ struct dentry *dentry;
+- int ret;
++ struct inode *dir, *child;
++ int ret, allow_dups;
+
+ if (pathname[0] != '/' || pathname[len-1] == '/')
+ return ERR_PTR(-EINVAL);
+
+- dentry = kern_path_parent(pathname, &path);
+- if (IS_ERR(dentry))
+- return ERR_CAST(dentry); /* returning an error */
++ if (!ctx) {
++ dentry = kern_path_parent(pathname, &path);
++ if (IS_ERR(dentry))
++ return ERR_CAST(dentry); /* returning an error */
++ dir = d_inode(path.dentry);
++ child = d_inode(dentry);
++ allow_dups = 0;
++ } else {
++ dir = ctx->dir;
++ child = ctx->child;
++ allow_dups = 1;
++ }
+
+ audit_mark = kzalloc(sizeof(*audit_mark), GFP_KERNEL);
+ if (unlikely(!audit_mark)) {
+@@ -94,18 +105,21 @@ struct audit_fsnotify_mark *audit_alloc_
+ fsnotify_init_mark(&audit_mark->mark, audit_fsnotify_group);
+ audit_mark->mark.mask = AUDIT_FS_EVENTS;
+ audit_mark->path = pathname;
+- audit_update_mark(audit_mark, dentry->d_inode);
+ audit_mark->rule = krule;
+
+- ret = fsnotify_add_inode_mark(&audit_mark->mark, path.dentry->d_inode, 0);
++ audit_update_mark(audit_mark, child);
++ ret = fsnotify_add_inode_mark(&audit_mark->mark, dir, allow_dups);
++
+ if (ret < 0) {
+ audit_mark->path = NULL;
+ fsnotify_put_mark(&audit_mark->mark);
+ audit_mark = ERR_PTR(ret);
+ }
+ out:
+- dput(dentry);
+- path_put(&path);
++ if (!ctx) {
++ dput(dentry);
++ path_put(&path);
++ }
+ return audit_mark;
+ }
+
+--- a/kernel/audit_watch.c
++++ b/kernel/audit_watch.c
+@@ -244,7 +244,8 @@ static void audit_watch_log_rule_change(
+ /* Update inode info in audit rules based on filesystem event. */
+ static void audit_update_watch(struct audit_parent *parent,
+ const struct qstr *dname, dev_t dev,
+- u64 ino, unsigned int invalidating)
++ u64 ino, unsigned int invalidating,
++ struct audit_watch_ctx *ctx)
+ {
+ struct audit_watch *owatch, *nwatch, *nextw;
+ struct audit_krule *r, *nextr;
+@@ -280,7 +281,7 @@ static void audit_update_watch(struct au
+ list_del(&oentry->rule.rlist);
+ list_del_rcu(&oentry->list);
+
+- nentry = audit_dupe_rule(&oentry->rule);
++ nentry = audit_dupe_rule(&oentry->rule, ctx);
+ if (IS_ERR(nentry)) {
+ list_del(&oentry->rule.list);
+ audit_panic("error updating watch, removing");
+@@ -479,10 +480,17 @@ static int audit_watch_handle_event(stru
+ if (WARN_ON_ONCE(inode_mark->group != audit_watch_group))
+ return 0;
+
+- if (mask & (FS_CREATE|FS_MOVED_TO) && inode)
+- audit_update_watch(parent, dname, inode->i_sb->s_dev, inode->i_ino, 0);
+- else if (mask & (FS_DELETE|FS_MOVED_FROM))
+- audit_update_watch(parent, dname, AUDIT_DEV_UNSET, AUDIT_INO_UNSET, 1);
++ if (mask & (FS_CREATE|FS_MOVED_TO) && inode) {
++ struct audit_watch_ctx ctx = { .dir = dir, .child = inode };
++
++ audit_update_watch(parent, dname, inode->i_sb->s_dev, inode->i_ino, 0,
++ &ctx);
++ } else if (mask & (FS_DELETE|FS_MOVED_FROM)) {
++ struct audit_watch_ctx ctx = { .dir = dir, .child = NULL };
++
++ audit_update_watch(parent, dname, AUDIT_DEV_UNSET, AUDIT_INO_UNSET, 1,
++ &ctx);
++ }
+ else if (mask & (FS_DELETE_SELF|FS_UNMOUNT|FS_MOVE_SELF))
+ audit_remove_parent_watches(parent);
+
+@@ -505,7 +513,8 @@ static int __init audit_watch_init(void)
+ }
+ device_initcall(audit_watch_init);
+
+-int audit_dupe_exe(struct audit_krule *new, struct audit_krule *old)
++int audit_dupe_exe(struct audit_krule *new, struct audit_krule *old,
++ struct audit_watch_ctx *ctx)
+ {
+ struct audit_fsnotify_mark *audit_mark;
+ char *pathname;
+@@ -514,7 +523,7 @@ int audit_dupe_exe(struct audit_krule *n
+ if (!pathname)
+ return -ENOMEM;
+
+- audit_mark = audit_alloc_mark(new, pathname, strlen(pathname));
++ audit_mark = audit_alloc_mark(new, pathname, strlen(pathname), ctx);
+ if (IS_ERR(audit_mark)) {
+ kfree(pathname);
+ return PTR_ERR(audit_mark);
+--- a/kernel/auditfilter.c
++++ b/kernel/auditfilter.c
+@@ -590,7 +590,7 @@ static struct audit_entry *audit_data_to
+ err = PTR_ERR(str);
+ goto exit_free;
+ }
+- audit_mark = audit_alloc_mark(&entry->rule, str, f_val);
++ audit_mark = audit_alloc_mark(&entry->rule, str, f_val, NULL);
+ if (IS_ERR(audit_mark)) {
+ kfree(str);
+ err = PTR_ERR(audit_mark);
+@@ -818,7 +818,8 @@ static inline int audit_dupe_lsm_field(s
+ * rule with the new rule in the filterlist, then free the old rule.
+ * The rlist element is undefined; list manipulations are handled apart from
+ * the initial copy. */
+-struct audit_entry *audit_dupe_rule(struct audit_krule *old)
++struct audit_entry *audit_dupe_rule(struct audit_krule *old,
++ struct audit_watch_ctx *ctx)
+ {
+ u32 fcount = old->field_count;
+ struct audit_entry *entry;
+@@ -877,7 +878,7 @@ struct audit_entry *audit_dupe_rule(stru
+ new->filterkey = fk;
+ break;
+ case AUDIT_EXE:
+- err = audit_dupe_exe(new, old);
++ err = audit_dupe_exe(new, old, ctx);
+ break;
+ }
+ if (err) {
+@@ -1408,7 +1409,7 @@ static int update_lsm_rule(struct audit_
+ if (!security_audit_rule_known(r))
+ return 0;
+
+- nentry = audit_dupe_rule(r);
++ nentry = audit_dupe_rule(r, NULL);
+ if (entry->rule.exe)
+ audit_remove_mark(entry->rule.exe);
+ if (IS_ERR(nentry)) {
--- /dev/null
+From stable+bounces-287678-greg=kroah.com@vger.kernel.org Wed Jul 22 02:54:17 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 21 Jul 2026 20:51:14 -0400
+Subject: audit: use 'unsigned int' instead of 'unsigned'
+To: stable@vger.kernel.org
+Cc: Ricardo Robaina <rrobaina@redhat.com>, Paul Moore <paul@paul-moore.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260722005116.605624-3-sashal@kernel.org>
+
+From: Ricardo Robaina <rrobaina@redhat.com>
+
+[ Upstream commit 8b226771014beab1292081151a99530886ce54b4 ]
+
+Address checkpatch.pl warning below, across the audit subsystem:
+
+ WARNING: Prefer 'unsigned int' to bare use of 'unsigned'
+
+Minor cleanup, no functional changes.
+
+Signed-off-by: Ricardo Robaina <rrobaina@redhat.com>
+Signed-off-by: Paul Moore <paul@paul-moore.com>
+Stable-dep-of: 81905b5acbe7 ("audit: fix recursive locking deadlock in audit_dupe_exe()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ include/linux/audit.h | 4 ++--
+ include/linux/audit_arch.h | 12 ++++++------
+ kernel/audit.c | 2 +-
+ kernel/audit.h | 2 +-
+ kernel/audit_tree.c | 2 +-
+ kernel/auditfilter.c | 8 ++++----
+ kernel/auditsc.c | 2 +-
+ lib/compat_audit.c | 12 ++++++------
+ 8 files changed, 22 insertions(+), 22 deletions(-)
+
+--- a/include/linux/audit.h
++++ b/include/linux/audit.h
+@@ -122,8 +122,8 @@ enum audit_nfcfgop {
+ AUDIT_NFT_OP_INVALID,
+ };
+
+-extern int __init audit_register_class(int class, unsigned *list);
+-extern int audit_classify_syscall(int abi, unsigned syscall);
++extern int __init audit_register_class(int class, unsigned int *list);
++extern int audit_classify_syscall(int abi, unsigned int syscall);
+ extern int audit_classify_arch(int arch);
+
+ /* audit_names->type values */
+--- a/include/linux/audit_arch.h
++++ b/include/linux/audit_arch.h
+@@ -21,13 +21,13 @@ enum auditsc_class_t {
+ AUDITSC_NVALS /* count */
+ };
+
+-extern int audit_classify_compat_syscall(int abi, unsigned syscall);
++extern int audit_classify_compat_syscall(int abi, unsigned int syscall);
+
+ /* only for compat system calls */
+-extern unsigned compat_write_class[];
+-extern unsigned compat_read_class[];
+-extern unsigned compat_dir_class[];
+-extern unsigned compat_chattr_class[];
+-extern unsigned compat_signal_class[];
++extern unsigned int compat_write_class[];
++extern unsigned int compat_read_class[];
++extern unsigned int compat_dir_class[];
++extern unsigned int compat_chattr_class[];
++extern unsigned int compat_signal_class[];
+
+ #endif
+--- a/kernel/audit.c
++++ b/kernel/audit.c
+@@ -1989,7 +1989,7 @@ static void audit_log_vformat(struct aud
+ * here and AUDIT_BUFSIZ is at least 1024, then we can
+ * log everything that printk could have logged. */
+ avail = audit_expand(ab,
+- max_t(unsigned, AUDIT_BUFSIZ, 1+len-avail));
++ max_t(unsigned int, AUDIT_BUFSIZ, 1+len-avail));
+ if (!avail)
+ goto out_va_end;
+ len = vsnprintf(skb_tail_pointer(skb), avail, fmt, args2);
+--- a/kernel/audit.h
++++ b/kernel/audit.h
+@@ -227,7 +227,7 @@ static inline int audit_hash_ino(u64 ino
+ /* Indicates that audit should log the full pathname. */
+ #define AUDIT_NAME_FULL -1
+
+-extern int audit_match_class(int class, unsigned syscall);
++extern int audit_match_class(int class, unsigned int syscall);
+ extern int audit_comparator(const u32 left, const u32 op, const u32 right);
+ extern int audit_uid_comparator(kuid_t left, u32 op, kuid_t right);
+ extern int audit_gid_comparator(kgid_t left, u32 op, kgid_t right);
+--- a/kernel/audit_tree.c
++++ b/kernel/audit_tree.c
+@@ -33,7 +33,7 @@ struct audit_chunk {
+ struct audit_node {
+ struct list_head list;
+ struct audit_tree *owner;
+- unsigned index; /* index; upper bit indicates 'will prune' */
++ unsigned int index; /* index; upper bit indicates 'will prune' */
+ } owners[];
+ };
+
+--- a/kernel/auditfilter.c
++++ b/kernel/auditfilter.c
+@@ -165,13 +165,13 @@ static inline int audit_to_inode(struct
+
+ static __u32 *classes[AUDIT_SYSCALL_CLASSES];
+
+-int __init audit_register_class(int class, unsigned *list)
++int __init audit_register_class(int class, unsigned int *list)
+ {
+ __u32 *p = kcalloc(AUDIT_BITMASK_SIZE, sizeof(__u32), GFP_KERNEL);
+ if (!p)
+ return -ENOMEM;
+ while (*list != ~0U) {
+- unsigned n = *list++;
++ unsigned int n = *list++;
+ if (n >= AUDIT_BITMASK_SIZE * 32 - AUDIT_SYSCALL_CLASSES) {
+ kfree(p);
+ return -EINVAL;
+@@ -186,7 +186,7 @@ int __init audit_register_class(int clas
+ return 0;
+ }
+
+-int audit_match_class(int class, unsigned syscall)
++int audit_match_class(int class, unsigned int syscall)
+ {
+ if (unlikely(syscall >= AUDIT_BITMASK_SIZE * 32))
+ return 0;
+@@ -237,7 +237,7 @@ static int audit_match_signal(struct aud
+ /* Common user-space to kernel rule translation. */
+ static inline struct audit_entry *audit_to_entry_common(struct audit_rule_data *rule)
+ {
+- unsigned listnr;
++ unsigned int listnr;
+ struct audit_entry *entry;
+ int i, err;
+
+--- a/kernel/auditsc.c
++++ b/kernel/auditsc.c
+@@ -150,7 +150,7 @@ static const struct audit_nfcfgop_tab au
+
+ static int audit_match_perm(struct audit_context *ctx, int mask)
+ {
+- unsigned n;
++ unsigned int n;
+
+ if (unlikely(!ctx))
+ return 0;
+--- a/lib/compat_audit.c
++++ b/lib/compat_audit.c
+@@ -4,32 +4,32 @@
+ #include <linux/audit_arch.h>
+ #include <asm/unistd32.h>
+
+-unsigned compat_dir_class[] = {
++unsigned int compat_dir_class[] = {
+ #include <asm-generic/audit_dir_write.h>
+ ~0U
+ };
+
+-unsigned compat_read_class[] = {
++unsigned int compat_read_class[] = {
+ #include <asm-generic/audit_read.h>
+ ~0U
+ };
+
+-unsigned compat_write_class[] = {
++unsigned int compat_write_class[] = {
+ #include <asm-generic/audit_write.h>
+ ~0U
+ };
+
+-unsigned compat_chattr_class[] = {
++unsigned int compat_chattr_class[] = {
+ #include <asm-generic/audit_change_attr.h>
+ ~0U
+ };
+
+-unsigned compat_signal_class[] = {
++unsigned int compat_signal_class[] = {
+ #include <asm-generic/audit_signal.h>
+ ~0U
+ };
+
+-int audit_classify_compat_syscall(int abi, unsigned syscall)
++int audit_classify_compat_syscall(int abi, unsigned int syscall)
+ {
+ switch (syscall) {
+ #ifdef __NR_open
--- /dev/null
+From stable+bounces-287677-greg=kroah.com@vger.kernel.org Wed Jul 22 04:46:03 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 21 Jul 2026 20:51:13 -0400
+Subject: audit: widen ino fields to u64
+To: stable@vger.kernel.org
+Cc: Jeff Layton <jlayton@kernel.org>, Paul Moore <paul@paul-moore.com>, Christian Brauner <brauner@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260722005116.605624-2-sashal@kernel.org>
+
+From: Jeff Layton <jlayton@kernel.org>
+
+[ Upstream commit 125dfa218134df7cc112667e92984de9d8cd0bf6 ]
+
+inode->i_ino is being widened from unsigned long to u64. The audit
+subsystem uses unsigned long ino in struct fields, function parameters,
+and local variables that store inode numbers from arbitrary filesystems.
+On 32-bit platforms this truncates inode numbers that exceed 32 bits,
+which will cause incorrect audit log entries and broken watch/mark
+comparisons.
+
+Widen all audit ino fields, parameters, and locals to u64, and update
+the inode format string from %lu to %llu to match.
+
+Signed-off-by: Jeff Layton <jlayton@kernel.org>
+Link: https://patch.msgid.link/20260304-iino-u64-v3-2-2257ad83d372@kernel.org
+Acked-by: Paul Moore <paul@paul-moore.com>
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Stable-dep-of: 81905b5acbe7 ("audit: fix recursive locking deadlock in audit_dupe_exe()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ include/linux/audit.h | 2 +-
+ kernel/audit.h | 9 ++++-----
+ kernel/audit_fsnotify.c | 2 +-
+ kernel/audit_watch.c | 10 +++++-----
+ kernel/auditsc.c | 4 ++--
+ 5 files changed, 13 insertions(+), 14 deletions(-)
+
+--- a/include/linux/audit.h
++++ b/include/linux/audit.h
+@@ -16,7 +16,7 @@
+ #include <uapi/linux/netfilter/nf_tables.h>
+ #include <uapi/linux/fanotify.h>
+
+-#define AUDIT_INO_UNSET ((unsigned long)-1)
++#define AUDIT_INO_UNSET ((u64)-1)
+ #define AUDIT_DEV_UNSET ((dev_t)-1)
+
+ struct audit_sig_info {
+--- a/kernel/audit.h
++++ b/kernel/audit.h
+@@ -75,7 +75,7 @@ struct audit_names {
+ int name_len; /* number of chars to log */
+ bool hidden; /* don't log this record */
+
+- unsigned long ino;
++ u64 ino;
+ dev_t dev;
+ umode_t mode;
+ kuid_t uid;
+@@ -219,9 +219,9 @@ extern int auditd_test_task(struct task_
+ #define AUDIT_INODE_BUCKETS 32
+ extern struct list_head audit_inode_hash[AUDIT_INODE_BUCKETS];
+
+-static inline int audit_hash_ino(u32 ino)
++static inline int audit_hash_ino(u64 ino)
+ {
+- return (ino & (AUDIT_INODE_BUCKETS-1));
++ return ((u32)ino & (AUDIT_INODE_BUCKETS-1));
+ }
+
+ /* Indicates that audit should log the full pathname. */
+@@ -271,8 +271,7 @@ extern int audit_to_watch(struct audit_k
+ extern int audit_add_watch(struct audit_krule *krule, struct list_head **list);
+ extern void audit_remove_watch_rule(struct audit_krule *krule);
+ extern char *audit_watch_path(struct audit_watch *watch);
+-extern int audit_watch_compare(struct audit_watch *watch, unsigned long ino,
+- dev_t dev);
++extern int audit_watch_compare(struct audit_watch *watch, u64 ino, dev_t dev);
+
+ extern struct audit_fsnotify_mark *audit_alloc_mark(struct audit_krule *krule,
+ char *pathname, int len);
+--- a/kernel/audit_fsnotify.c
++++ b/kernel/audit_fsnotify.c
+@@ -25,7 +25,7 @@
+ */
+ struct audit_fsnotify_mark {
+ dev_t dev; /* associated superblock device */
+- unsigned long ino; /* associated inode number */
++ u64 ino; /* associated inode number */
+ char *path; /* insertion path */
+ struct fsnotify_mark mark; /* fsnotify mark on the inode */
+ struct audit_krule *rule;
+--- a/kernel/audit_watch.c
++++ b/kernel/audit_watch.c
+@@ -37,7 +37,7 @@ struct audit_watch {
+ refcount_t count; /* reference count */
+ dev_t dev; /* associated superblock device */
+ char *path; /* insertion path */
+- unsigned long ino; /* associated inode number */
++ u64 ino; /* associated inode number */
+ struct audit_parent *parent; /* associated parent */
+ struct list_head wlist; /* entry in parent->watches list */
+ struct list_head rules; /* anchor for krule->rlist */
+@@ -125,7 +125,7 @@ char *audit_watch_path(struct audit_watc
+ return watch->path;
+ }
+
+-int audit_watch_compare(struct audit_watch *watch, unsigned long ino, dev_t dev)
++int audit_watch_compare(struct audit_watch *watch, u64 ino, dev_t dev)
+ {
+ return (watch->ino != AUDIT_INO_UNSET) &&
+ (watch->ino == ino) &&
+@@ -285,7 +285,7 @@ static void audit_update_watch(struct au
+ list_del(&oentry->rule.list);
+ audit_panic("error updating watch, removing");
+ } else {
+- int h = audit_hash_ino((u32)ino);
++ int h = audit_hash_ino(ino);
+
+ /*
+ * nentry->rule.watch == oentry->rule.watch so
+@@ -439,7 +439,7 @@ int audit_add_watch(struct audit_krule *
+
+ audit_add_to_parent(krule, parent);
+
+- h = audit_hash_ino((u32)watch->ino);
++ h = audit_hash_ino(watch->ino);
+ *list = &audit_inode_hash[h];
+ error:
+ path_put(&parent_path);
+@@ -527,7 +527,7 @@ int audit_dupe_exe(struct audit_krule *n
+ int audit_exe_compare(struct task_struct *tsk, struct audit_fsnotify_mark *mark)
+ {
+ struct file *exe_file;
+- unsigned long ino;
++ u64 ino;
+ dev_t dev;
+
+ /* only do exe filtering if we are recording @current events/records */
+--- a/kernel/auditsc.c
++++ b/kernel/auditsc.c
+@@ -884,7 +884,7 @@ static int audit_filter_inode_name(struc
+ struct audit_names *n,
+ struct audit_context *ctx)
+ {
+- int h = audit_hash_ino((u32)n->ino);
++ int h = audit_hash_ino(n->ino);
+ struct list_head *list = &audit_inode_hash[h];
+
+ return __audit_filter_op(tsk, ctx, list, n, ctx->major);
+@@ -1549,7 +1549,7 @@ static void audit_log_name(struct audit_
+ audit_log_format(ab, " name=(null)");
+
+ if (n->ino != AUDIT_INO_UNSET)
+- audit_log_format(ab, " inode=%lu dev=%02x:%02x mode=%#ho ouid=%u ogid=%u rdev=%02x:%02x",
++ audit_log_format(ab, " inode=%llu dev=%02x:%02x mode=%#ho ouid=%u ogid=%u rdev=%02x:%02x",
+ n->ino,
+ MAJOR(n->dev),
+ MINOR(n->dev),
--- /dev/null
+From stable+bounces-290112-greg=kroah.com@vger.kernel.org Tue Jul 28 20:22:36 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Jul 2026 14:06:41 -0400
+Subject: bootconfig: do not put quotes on cmdline items unless necessary
+To: stable@vger.kernel.org
+Cc: Rasmus Villemoes <linux@rasmusvillemoes.dk>, Masami Hiramatsu <mhiramat@kernel.org>, Andrew Morton <akpm@linux-foundation.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260728180643.289803-1-sashal@kernel.org>
+
+From: Rasmus Villemoes <linux@rasmusvillemoes.dk>
+
+[ Upstream commit 212f863fa8811c780abacc1d0404c573fdc0a2de ]
+
+When trying to migrate to using bootconfig to embed the kernel's and
+PID1's command line with the kernel image itself, and so allowing changing
+that without modifying the bootloader, I noticed that /proc/cmdline
+changed from e.g.
+
+ console=ttymxc0,115200n8 cma=128M quiet -- --log-level=notice
+
+to
+
+ console="ttymxc0,115200n8" cma="128M" quiet -- --log-level="notice"
+
+The kernel parameters are parsed just fine, and the quotes are indeed
+stripped from the actual argv[] given to PID1. However, the quoting
+doesn't really serve any purpose and looks excessive, and might confuse
+some (naive) userspace tool trying to parse /proc/cmdline. So do not
+quote the value unless it contains whitespace.
+
+Link: https://lkml.kernel.org/r/20240320101952.62135-1-linux@rasmusvillemoes.dk
+Signed-off-by: Rasmus Villemoes <linux@rasmusvillemoes.dk>
+Cc: Masami Hiramatsu <mhiramat@kernel.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Stable-dep-of: dec4d8118c17 ("bootconfig: fix NULL-pointer arithmetic in xbc_snprint_cmdline()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ init/main.c | 12 +++++++++---
+ 1 file changed, 9 insertions(+), 3 deletions(-)
+
+--- a/init/main.c
++++ b/init/main.c
+@@ -325,7 +325,7 @@ static int __init xbc_snprint_cmdline(ch
+ {
+ struct xbc_node *knode, *vnode;
+ char *end = buf + size;
+- const char *val;
++ const char *val, *q;
+ int ret;
+
+ xbc_node_for_each_key_value(root, knode, val) {
+@@ -343,8 +343,14 @@ static int __init xbc_snprint_cmdline(ch
+ continue;
+ }
+ xbc_array_for_each_value(vnode, val) {
+- ret = snprintf(buf, rest(buf, end), "%s=\"%s\" ",
+- xbc_namebuf, val);
++ /*
++ * For prettier and more readable /proc/cmdline, only
++ * quote the value when necessary, i.e. when it contains
++ * whitespace.
++ */
++ q = strpbrk(val, " \t\r\n") ? "\"" : "";
++ ret = snprintf(buf, rest(buf, end), "%s=%s%s%s ",
++ xbc_namebuf, q, val, q);
+ if (ret < 0)
+ return ret;
+ buf += ret;
--- /dev/null
+From stable+bounces-290114-greg=kroah.com@vger.kernel.org Tue Jul 28 20:29:44 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Jul 2026 14:06:43 -0400
+Subject: bootconfig: fix NULL-pointer arithmetic in xbc_snprint_cmdline()
+To: stable@vger.kernel.org
+Cc: Breno Leitao <leitao@debian.org>, "Masami Hiramatsu (Google)" <mhiramat@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260728180643.289803-3-sashal@kernel.org>
+
+From: Breno Leitao <leitao@debian.org>
+
+[ Upstream commit dec4d8118c179b3d12bca7e609054c6011c4f2ce ]
+
+xbc_snprint_cmdline() is meant to be called twice: first with
+buf=NULL, size=0 to probe the rendered length, then with a real
+buffer to fill it (the standard snprintf() two-pass pattern). The
+probe call makes the function compute "buf + size" (NULL + 0) and,
+on every iteration, advance "buf += ret" from that NULL base and
+pass the result back into snprintf().
+
+Pointer arithmetic on a NULL pointer is undefined behavior. It is
+harmless in the in-kernel callers today, but the follow-up patches
+run this same code in the userspace tools/bootconfig parser at kernel
+build time, where host UBSan / FORTIFY_SOURCE abort the build.
+
+Track a running written length (size_t) instead of mutating @buf, and
+only form "buf + len" when @buf is non-NULL. snprintf(NULL, 0, ...)
+is itself well defined and returns the would-be length, so the
+two-pass "probe then fill" usage returns identical byte counts.
+
+Link: https://lore.kernel.org/all/20260626-bootconfig_using_tools-v7-1-24ab72139c29@debian.org/
+
+Fixes: 51887d03aca1 ("bootconfig: init: Allow admin to use bootconfig for kernel command line")
+Cc: stable@vger.kernel.org
+Signed-off-by: Breno Leitao <leitao@debian.org>
+Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ lib/bootconfig.c | 23 ++++++++++++++++-------
+ 1 file changed, 16 insertions(+), 7 deletions(-)
+
+--- a/lib/bootconfig.c
++++ b/lib/bootconfig.c
+@@ -426,10 +426,18 @@ static char xbc_namebuf[XBC_KEYLEN_MAX]
+ int __init xbc_snprint_cmdline(char *buf, size_t size, struct xbc_node *root)
+ {
+ struct xbc_node *knode, *vnode;
+- char *end = buf + size;
+ const char *val, *q;
++ size_t len = 0;
+ int ret;
+
++ /*
++ * Track the running written length rather than advancing @buf, so we
++ * never form "buf + size" or "buf += ret" while @buf is NULL (the
++ * size-probe call passes buf=NULL, size=0). NULL pointer arithmetic
++ * is undefined behavior and trips host UBSan / FORTIFY_SOURCE when
++ * this renderer runs at kernel build time. snprintf(NULL, 0, ...)
++ * itself is well defined and returns the would-be length.
++ */
+ xbc_node_for_each_key_value(root, knode, val) {
+ ret = xbc_node_compose_key_after(root, knode,
+ xbc_namebuf, XBC_KEYLEN_MAX);
+@@ -438,10 +446,11 @@ int __init xbc_snprint_cmdline(char *buf
+
+ vnode = xbc_node_get_child(knode);
+ if (!vnode) {
+- ret = snprintf(buf, rest(buf, end), "%s ", xbc_namebuf);
++ ret = snprintf(buf ? buf + len : NULL, rest(len, size),
++ "%s ", xbc_namebuf);
+ if (ret < 0)
+ return ret;
+- buf += ret;
++ len += ret;
+ continue;
+ }
+ xbc_array_for_each_value(vnode, val) {
+@@ -451,15 +460,15 @@ int __init xbc_snprint_cmdline(char *buf
+ * whitespace.
+ */
+ q = strpbrk(val, " \t\r\n") ? "\"" : "";
+- ret = snprintf(buf, rest(buf, end), "%s=%s%s%s ",
+- xbc_namebuf, q, val, q);
++ ret = snprintf(buf ? buf + len : NULL, rest(len, size),
++ "%s=%s%s%s ", xbc_namebuf, q, val, q);
+ if (ret < 0)
+ return ret;
+- buf += ret;
++ len += ret;
+ }
+ }
+
+- return buf - (end - size);
++ return len;
+ }
+ #undef rest
+
--- /dev/null
+From stable+bounces-290113-greg=kroah.com@vger.kernel.org Tue Jul 28 20:13:24 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Jul 2026 14:06:42 -0400
+Subject: bootconfig: move xbc_snprint_cmdline() to lib/bootconfig.c
+To: stable@vger.kernel.org
+Cc: Breno Leitao <leitao@debian.org>, "Masami Hiramatsu (Google)" <mhiramat@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260728180643.289803-2-sashal@kernel.org>
+
+From: Breno Leitao <leitao@debian.org>
+
+[ Upstream commit 5a643e4623238e14b03d75ca0d4eda0645720cee ]
+
+Move xbc_snprint_cmdline() from init/main.c to lib/bootconfig.c so the
+function (and its xbc_namebuf scratch buffer) becomes part of the shared
+parser library. tools/bootconfig already compiles lib/bootconfig.c
+directly, which lets a follow-up patch reuse the same renderer in the
+userspace tool to convert a bootconfig file into a flat cmdline string
+at build time.
+
+No functional change.
+
+Link: https://lore.kernel.org/all/20260508-bootconfig_using_tools-v1-1-1132219aa773@debian.org/
+
+Signed-off-by: Breno Leitao <leitao@debian.org>
+Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
+Stable-dep-of: dec4d8118c17 ("bootconfig: fix NULL-pointer arithmetic in xbc_snprint_cmdline()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ include/linux/bootconfig.h | 3 ++
+ init/main.c | 45 ------------------------------------
+ lib/bootconfig.c | 56 +++++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 59 insertions(+), 45 deletions(-)
+
+--- a/include/linux/bootconfig.h
++++ b/include/linux/bootconfig.h
+@@ -264,6 +264,9 @@ static inline struct xbc_node * __init x
+ int __init xbc_node_compose_key_after(struct xbc_node *root,
+ struct xbc_node *node, char *buf, size_t size);
+
++/* Render key/value pairs under @root as a flat cmdline string */
++int __init xbc_snprint_cmdline(char *buf, size_t size, struct xbc_node *root);
++
+ /**
+ * xbc_node_compose_key() - Compose full key string of the XBC node
+ * @node: An XBC node.
+--- a/init/main.c
++++ b/init/main.c
+@@ -316,51 +316,6 @@ static void * __init get_boot_config_fro
+
+ #ifdef CONFIG_BOOT_CONFIG
+
+-static char xbc_namebuf[XBC_KEYLEN_MAX] __initdata;
+-
+-#define rest(dst, end) ((end) > (dst) ? (end) - (dst) : 0)
+-
+-static int __init xbc_snprint_cmdline(char *buf, size_t size,
+- struct xbc_node *root)
+-{
+- struct xbc_node *knode, *vnode;
+- char *end = buf + size;
+- const char *val, *q;
+- int ret;
+-
+- xbc_node_for_each_key_value(root, knode, val) {
+- ret = xbc_node_compose_key_after(root, knode,
+- xbc_namebuf, XBC_KEYLEN_MAX);
+- if (ret < 0)
+- return ret;
+-
+- vnode = xbc_node_get_child(knode);
+- if (!vnode) {
+- ret = snprintf(buf, rest(buf, end), "%s ", xbc_namebuf);
+- if (ret < 0)
+- return ret;
+- buf += ret;
+- continue;
+- }
+- xbc_array_for_each_value(vnode, val) {
+- /*
+- * For prettier and more readable /proc/cmdline, only
+- * quote the value when necessary, i.e. when it contains
+- * whitespace.
+- */
+- q = strpbrk(val, " \t\r\n") ? "\"" : "";
+- ret = snprintf(buf, rest(buf, end), "%s=%s%s%s ",
+- xbc_namebuf, q, val, q);
+- if (ret < 0)
+- return ret;
+- buf += ret;
+- }
+- }
+-
+- return buf - (end - size);
+-}
+-#undef rest
+-
+ /* Make an extra command line under given key word */
+ static char * __init xbc_make_cmdline(const char *key)
+ {
+--- a/lib/bootconfig.c
++++ b/lib/bootconfig.c
+@@ -407,6 +407,62 @@ const char * __init xbc_node_find_next_k
+ return ""; /* No value key */
+ }
+
++static char xbc_namebuf[XBC_KEYLEN_MAX] __initdata;
++
++#define rest(dst, end) ((end) > (dst) ? (end) - (dst) : 0)
++
++/**
++ * xbc_snprint_cmdline() - Render bootconfig keys under @root as a cmdline string
++ * @buf: Destination buffer (may be NULL when @size is 0 to query the length)
++ * @size: Size of @buf in bytes
++ * @root: Subtree root whose key=value pairs should be rendered
++ *
++ * Walk all key/value pairs under @root and emit them as a space-separated
++ * cmdline string into @buf. Values containing whitespace are quoted with
++ * double quotes. Returns the number of bytes that would be written if @buf
++ * were large enough (matching snprintf semantics), or a negative errno on
++ * failure.
++ */
++int __init xbc_snprint_cmdline(char *buf, size_t size, struct xbc_node *root)
++{
++ struct xbc_node *knode, *vnode;
++ char *end = buf + size;
++ const char *val, *q;
++ int ret;
++
++ xbc_node_for_each_key_value(root, knode, val) {
++ ret = xbc_node_compose_key_after(root, knode,
++ xbc_namebuf, XBC_KEYLEN_MAX);
++ if (ret < 0)
++ return ret;
++
++ vnode = xbc_node_get_child(knode);
++ if (!vnode) {
++ ret = snprintf(buf, rest(buf, end), "%s ", xbc_namebuf);
++ if (ret < 0)
++ return ret;
++ buf += ret;
++ continue;
++ }
++ xbc_array_for_each_value(vnode, val) {
++ /*
++ * For prettier and more readable /proc/cmdline, only
++ * quote the value when necessary, i.e. when it contains
++ * whitespace.
++ */
++ q = strpbrk(val, " \t\r\n") ? "\"" : "";
++ ret = snprintf(buf, rest(buf, end), "%s=%s%s%s ",
++ xbc_namebuf, q, val, q);
++ if (ret < 0)
++ return ret;
++ buf += ret;
++ }
++ }
++
++ return buf - (end - size);
++}
++#undef rest
++
+ /* XBC parse and tree build */
+
+ static int __init xbc_init_node(struct xbc_node *node, char *data, uint32_t flag)
--- /dev/null
+From stable+bounces-279054-greg=kroah.com@vger.kernel.org Tue Jul 21 17:55:51 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 21 Jul 2026 11:48:28 -0400
+Subject: bpf: Reject BPF_MAP_TYPE_INODE_STORAGE creation if BPF LSM is uninitialized
+To: stable@vger.kernel.org
+Cc: Matt Bobrowski <mattbobrowski@google.com>, oxsignal <awo@kakao.com>, Daniel Borkmann <daniel@iogearbox.net>, Emil Tsalapatis <emil@etsalapatis.com>, Amery Hung <ameryhung@gmail.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260721154828.4004522-1-sashal@kernel.org>
+
+From: Matt Bobrowski <mattbobrowski@google.com>
+
+[ Upstream commit a6f0643e4f63cfaa0d5d4a69de4f132eac4b8fe4 ]
+
+When CONFIG_BPF_LSM=y is set, BPF inode storage maps
+(BPF_MAP_TYPE_INODE_STORAGE) are compiled into the kernel. However,
+if the BPF LSM is not explicitly enabled at boot time (e.g. omitted
+from the "lsm=" boot parameter), lsm_prepare() is never executed for
+the BPF LSM.
+
+Consequently, the BPF inode security blob offset
+(bpf_lsm_blob_sizes.lbs_inode) is never initialized and remains at
+its default compiled size of 8 bytes instead of being updated to a
+valid offset past the reserved struct rcu_head (typically 16 bytes
+or more).
+
+When a privileged user creates and updates a BPF_MAP_TYPE_INODE_STORAGE
+map, bpf_inode() evaluates inode->i_security + 8. This erroneously
+aliases the struct rcu_head.func callback pointer at the beginning
+of the inode->i_security blob. During subsequent map element cleanup
+or inode destruction, writing NULL to owner_storage clears the queued
+RCU callback pointer. When rcu_do_batch() later executes the queued
+callback, it attempts an instruction fetch at address 0x0, triggering
+an immediate kernel panic.
+
+Fix this by introducing a global bpf_lsm_initialized boolean flag
+marked with __ro_after_init. Set this flag to true inside bpf_lsm_init()
+when the LSM framework successfully registers the BPF LSM. Gate map
+allocation in inode_storage_map_alloc() on this flag, returning
+-EOPNOTSUPP if the BPF LSM is in turn uninitialized.
+
+This fail-fast approach prevents userspace from allocating inode
+storage maps when the supporting BPF LSM infrastructure is absent,
+avoiding zombie map states.
+
+Fixes: 8ea636848aca ("bpf: Implement bpf_local_storage for inodes")
+Reported-by: oxsignal <awo@kakao.com>
+Signed-off-by: Matt Bobrowski <mattbobrowski@google.com>
+Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
+Reviewed-by: Emil Tsalapatis <emil@etsalapatis.com>
+Reviewed-by: Amery Hung <ameryhung@gmail.com>
+Link: https://lore.kernel.org/bpf/20260628201103.3624525-1-mattbobrowski@google.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ include/linux/bpf_lsm.h | 4 ++++
+ kernel/bpf/bpf_inode_storage.c | 9 +++++++++
+ security/bpf/hooks.c | 3 +++
+ 3 files changed, 16 insertions(+)
+
+--- a/include/linux/bpf_lsm.h
++++ b/include/linux/bpf_lsm.h
+@@ -13,6 +13,8 @@
+
+ #ifdef CONFIG_BPF_LSM
+
++extern bool bpf_lsm_initialized __ro_after_init;
++
+ #define LSM_HOOK(RET, DEFAULT, NAME, ...) \
+ RET bpf_lsm_##NAME(__VA_ARGS__);
+ #include <linux/lsm_hook_defs.h>
+@@ -47,6 +49,8 @@ void bpf_lsm_find_cgroup_shim(const stru
+
+ #else /* !CONFIG_BPF_LSM */
+
++#define bpf_lsm_initialized false
++
+ static inline bool bpf_lsm_is_sleepable_hook(u32 btf_id)
+ {
+ return false;
+--- a/kernel/bpf/bpf_inode_storage.c
++++ b/kernel/bpf/bpf_inode_storage.c
+@@ -191,6 +191,15 @@ static int notsupp_get_next_key(struct b
+
+ static struct bpf_map *inode_storage_map_alloc(union bpf_attr *attr)
+ {
++ /*
++ * Do not allow allocation of BPF_MAP_TYPE_INODE_STORAGE if the BPF LSM
++ * was not initialized by the LSM framework at boot. Without proper
++ * initialization, the BPF inode security blob offset remains unprepared,
++ * causing bpf_inode() to calculate an invalid memory offset and corrupt
++ * inode->i_security.
++ */
++ if (!bpf_lsm_initialized)
++ return ERR_PTR(-EOPNOTSUPP);
+ return bpf_local_storage_map_alloc(attr, &inode_cache, false);
+ }
+
+--- a/security/bpf/hooks.c
++++ b/security/bpf/hooks.c
+@@ -6,6 +6,8 @@
+ #include <linux/lsm_hooks.h>
+ #include <linux/bpf_lsm.h>
+
++bool bpf_lsm_initialized __ro_after_init;
++
+ static struct security_hook_list bpf_lsm_hooks[] __ro_after_init = {
+ #define LSM_HOOK(RET, DEFAULT, NAME, ...) \
+ LSM_HOOK_INIT(NAME, bpf_lsm_##NAME),
+@@ -18,6 +20,7 @@ static struct security_hook_list bpf_lsm
+ static int __init bpf_lsm_init(void)
+ {
+ security_add_hooks(bpf_lsm_hooks, ARRAY_SIZE(bpf_lsm_hooks), "bpf");
++ bpf_lsm_initialized = true;
+ pr_info("LSM support for eBPF active\n");
+ return 0;
+ }
--- /dev/null
+From stable+bounces-289647-greg=kroah.com@vger.kernel.org Mon Jul 27 21:23:42 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 15:17:17 -0400
+Subject: dm-verity: avoid double increment of &use_bh_wq_enabled
+To: stable@vger.kernel.org
+Cc: Mikulas Patocka <mpatocka@redhat.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260727191717.1692026-1-sashal@kernel.org>
+
+From: Mikulas Patocka <mpatocka@redhat.com>
+
+[ Upstream commit 72e9ec2fe32b00994f41719cf77423fca67d48b2 ]
+
+verity_parse_opt_args is called twice, first with the only_modifier_opts,
+first with only_modifier_opts == true and then with only_modifier_opts ==
+false. Thus, the static branch &use_bh_wq_enabled was incremented twice
+and the destructor verity_dtr would only decrement it once.
+
+Fix tihs bug by only incrementing it on the first call, on the second
+call, when v->use_bh_wq is true, do nothing.
+
+Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
+Assisted-by: Claude:claude-opus-4-6
+Cc: stable@vger.kernel.org
+Fixes: df326e7a0699 ("dm verity: allow optional args to alter primary args handling")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/md/dm-verity-target.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/drivers/md/dm-verity-target.c
++++ b/drivers/md/dm-verity-target.c
+@@ -1172,6 +1172,8 @@ static int verity_parse_opt_args(struct
+ continue;
+
+ } else if (!strcasecmp(arg_name, DM_VERITY_OPT_TASKLET_VERIFY)) {
++ if (v->use_tasklet)
++ continue;
+ v->use_tasklet = true;
+ static_branch_inc(&use_tasklet_enabled);
+ continue;
--- /dev/null
+From stable+bounces-289649-greg=kroah.com@vger.kernel.org Mon Jul 27 21:29:53 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 15:22:11 -0400
+Subject: dm-verity: make error counter atomic
+To: stable@vger.kernel.org
+Cc: Mikulas Patocka <mpatocka@redhat.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260727192211.1722315-1-sashal@kernel.org>
+
+From: Mikulas Patocka <mpatocka@redhat.com>
+
+[ Upstream commit 8ec4d9c5a5cf4b61fc087f871465b1f79b393325 ]
+
+The error counter "v->corrupted_errs" was not atomic, thus it could be
+subject to race conditions. The call to
+dm_audit_log_target("max-corrupted-errors") may be skipped due to the
+races.
+
+Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
+Assisted-by: Claude:claude-opus-4.6
+Fixes: 65ff5b7ddf05 ("dm verity: add error handling modes for corrupted blocks")
+Cc: stable@vger.kernel.org
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/md/dm-verity-target.c | 12 +++++++-----
+ drivers/md/dm-verity.h | 2 +-
+ 2 files changed, 8 insertions(+), 6 deletions(-)
+
+--- a/drivers/md/dm-verity-target.c
++++ b/drivers/md/dm-verity-target.c
+@@ -227,14 +227,16 @@ static int verity_handle_err(struct dm_v
+ char *envp[] = { verity_env, NULL };
+ const char *type_str = "";
+ struct mapped_device *md = dm_table_get_md(v->ti->table);
++ int ce;
+
+ /* Corruption should be visible in device status in all modes */
+ v->hash_failed = true;
+
+- if (v->corrupted_errs >= DM_VERITY_MAX_CORRUPTED_ERRS)
+- goto out;
+-
+- v->corrupted_errs++;
++ ce = atomic_read(&v->corrupted_errs);
++ do {
++ if (ce >= DM_VERITY_MAX_CORRUPTED_ERRS)
++ goto out;
++ } while (!atomic_try_cmpxchg(&v->corrupted_errs, &ce, ce + 1));
+
+ switch (type) {
+ case DM_VERITY_BLOCK_TYPE_DATA:
+@@ -250,7 +252,7 @@ static int verity_handle_err(struct dm_v
+ DMERR_LIMIT("%s: %s block %llu is corrupted", v->data_dev->name,
+ type_str, block);
+
+- if (v->corrupted_errs == DM_VERITY_MAX_CORRUPTED_ERRS) {
++ if (ce + 1 == DM_VERITY_MAX_CORRUPTED_ERRS) {
+ DMERR("%s: reached maximum errors", v->data_dev->name);
+ dm_audit_log_target(DM_MSG_PREFIX, "max-corrupted-errors", v->ti, 0);
+ }
+--- a/drivers/md/dm-verity.h
++++ b/drivers/md/dm-verity.h
+@@ -58,7 +58,7 @@ struct dm_verity {
+ unsigned int digest_size; /* digest size for the current hash algorithm */
+ unsigned int ahash_reqsize;/* the size of temporary space for crypto */
+ enum verity_mode mode; /* mode for handling verification errors */
+- unsigned int corrupted_errs;/* Number of errors for corrupted blocks */
++ atomic_t corrupted_errs;/* Number of errors for corrupted blocks */
+
+ struct workqueue_struct *verify_wq;
+
--- /dev/null
+From stable+bounces-281369-greg=kroah.com@vger.kernel.org Tue Jul 21 21:47:00 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 21 Jul 2026 14:28:03 -0400
+Subject: dma-buf/udmabuf: skip redundant cpu sync to fix cacheline EEXIST warning
+To: stable@vger.kernel.org
+Cc: Mikhail Gavrilov <mikhail.v.gavrilov@gmail.com>, Vivek Kasireddy <vivek.kasireddy@intel.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260721182803.332607-2-sashal@kernel.org>
+
+From: Mikhail Gavrilov <mikhail.v.gavrilov@gmail.com>
+
+[ Upstream commit 504e2b4ab97a51d56d966cd36d0997ad30b65b2d ]
+
+When CONFIG_DMA_API_DEBUG_SG is enabled, importing a udmabuf into a DRM
+driver (e.g. amdgpu for video playback in GNOME Videos / Showtime)
+triggers a spurious warning:
+
+ DMA-API: amdgpu 0000:03:00.0: cacheline tracking EEXIST, \
+ overlapping mappings aren't supported
+ WARNING: kernel/dma/debug.c:619 at add_dma_entry+0x473/0x5f0
+
+The call chain is:
+
+ amdgpu_cs_ioctl
+ -> amdgpu_ttm_backend_bind
+ -> dma_buf_map_attachment
+ -> [udmabuf] map_udmabuf -> get_sg_table
+ -> dma_map_sgtable(dev, sg, direction, 0) // attrs=0
+ -> debug_dma_map_sg -> add_dma_entry -> EEXIST
+
+This happens because udmabuf builds a per-page scatter-gather list via
+sg_set_folio(). When begin_cpu_udmabuf() has already created an sg
+table mapped for the misc device, and an importer such as amdgpu maps
+the same pages for its own device via map_udmabuf(), the DMA debug
+infrastructure sees two active mappings whose physical addresses share
+cacheline boundaries and warns about the overlap.
+
+The DMA_ATTR_SKIP_CPU_SYNC flag suppresses this check in
+add_dma_entry() because it signals that no CPU cache maintenance is
+performed at map/unmap time, making the cacheline overlap harmless.
+
+All other major dma-buf exporters already pass this flag:
+ - drm_gem_map_dma_buf() passes DMA_ATTR_SKIP_CPU_SYNC
+ - amdgpu_dma_buf_map() passes DMA_ATTR_SKIP_CPU_SYNC
+
+The CPU sync at map/unmap time is also redundant for udmabuf:
+begin_cpu_udmabuf() and end_cpu_udmabuf() already perform explicit
+cache synchronization via dma_sync_sgtable_for_cpu/device() when CPU
+access is requested through the dma-buf interface.
+
+Pass DMA_ATTR_SKIP_CPU_SYNC to dma_map_sgtable() and
+dma_unmap_sgtable() in udmabuf to suppress the spurious warning and
+skip the redundant sync.
+
+Fixes: 284562e1f348 ("udmabuf: implement begin_cpu_access/end_cpu_access hooks")
+Cc: stable@vger.kernel.org
+Signed-off-by: Mikhail Gavrilov <mikhail.v.gavrilov@gmail.com>
+Acked-by: Vivek Kasireddy <vivek.kasireddy@intel.com>
+Signed-off-by: Vivek Kasireddy <vivek.kasireddy@intel.com>
+Link: https://patch.msgid.link/20260331061657.79983-1-mikhail.v.gavrilov@gmail.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/dma-buf/udmabuf.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/dma-buf/udmabuf.c
++++ b/drivers/dma-buf/udmabuf.c
+@@ -102,7 +102,7 @@ static struct sg_table *get_sg_table(str
+ if (ret < 0)
+ goto err_alloc;
+
+- ret = dma_map_sgtable(dev, sg, direction, 0);
++ ret = dma_map_sgtable(dev, sg, direction, DMA_ATTR_SKIP_CPU_SYNC);
+ if (ret < 0)
+ goto err_map;
+ return sg;
+@@ -117,7 +117,7 @@ err_alloc:
+ static void put_sg_table(struct device *dev, struct sg_table *sg,
+ enum dma_data_direction direction)
+ {
+- dma_unmap_sgtable(dev, sg, direction, 0);
++ dma_unmap_sgtable(dev, sg, direction, DMA_ATTR_SKIP_CPU_SYNC);
+ sg_free_table(sg);
+ kfree(sg);
+ }
--- /dev/null
+From stable+bounces-289284-greg=kroah.com@vger.kernel.org Sun Jul 26 15:35:25 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 26 Jul 2026 09:35:15 -0400
+Subject: dma: dw-edma: Fix build warning in dw_edma_pcie_probe()
+To: stable@vger.kernel.org
+Cc: Abinash Singh <abinashlalotra@gmail.com>, Abinash Singh <abinashsinghlalotra@gmail.com>, Manivannan Sadhasivam <mani@kernel.org>, Vinod Koul <vkoul@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260726133517.364299-1-sashal@kernel.org>
+
+From: Abinash Singh <abinashlalotra@gmail.com>
+
+[ Upstream commit 3df63fa8f2afd051848e37ef1b8299dee28d4f87 ]
+
+The function dw_edma_pcie_probe() in dw-edma-pcie.c triggered a
+frame size warning:
+ld.lld:warning:
+ drivers/dma/dw-edma/dw-edma-pcie.c:162:0: stack frame size (1040) exceeds limit (1024) in function 'dw_edma_pcie_probe'
+
+This patch reduces the stack usage by dynamically allocating the
+`vsec_data` structure using kmalloc(), rather than placing it on
+the stack. This eliminates the overflow warning and improves kernel
+robustness.
+
+Signed-off-by: Abinash Singh <abinashsinghlalotra@gmail.com>
+Acked-by: Manivannan Sadhasivam <mani@kernel.org>
+Link: https://lore.kernel.org/r/20250705160055.808165-1-abinashsinghlalotra@gmail.com
+Signed-off-by: Vinod Koul <vkoul@kernel.org>
+Stable-dep-of: 11d7cfe0c119 ("dmaengine: dw-edma-pcie: Reject devices without driver data")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/dma/dw-edma/dw-edma-pcie.c | 62 +++++++++++++++++++------------------
+ 1 file changed, 33 insertions(+), 29 deletions(-)
+
+--- a/drivers/dma/dw-edma/dw-edma-pcie.c
++++ b/drivers/dma/dw-edma/dw-edma-pcie.c
+@@ -160,12 +160,16 @@ static int dw_edma_pcie_probe(struct pci
+ const struct pci_device_id *pid)
+ {
+ struct dw_edma_pcie_data *pdata = (void *)pid->driver_data;
+- struct dw_edma_pcie_data vsec_data;
++ struct dw_edma_pcie_data *vsec_data __free(kfree) = NULL;
+ struct device *dev = &pdev->dev;
+ struct dw_edma_chip *chip;
+ int err, nr_irqs;
+ int i, mask;
+
++ vsec_data = kmalloc(sizeof(*vsec_data), GFP_KERNEL);
++ if (!vsec_data)
++ return -ENOMEM;
++
+ /* Enable PCI device */
+ err = pcim_enable_device(pdev);
+ if (err) {
+@@ -173,23 +177,23 @@ static int dw_edma_pcie_probe(struct pci
+ return err;
+ }
+
+- memcpy(&vsec_data, pdata, sizeof(struct dw_edma_pcie_data));
++ memcpy(vsec_data, pdata, sizeof(struct dw_edma_pcie_data));
+
+ /*
+ * Tries to find if exists a PCIe Vendor-Specific Extended Capability
+ * for the DMA, if one exists, then reconfigures it.
+ */
+- dw_edma_pcie_get_vsec_dma_data(pdev, &vsec_data);
++ dw_edma_pcie_get_vsec_dma_data(pdev, vsec_data);
+
+ /* Mapping PCI BAR regions */
+- mask = BIT(vsec_data.rg.bar);
+- for (i = 0; i < vsec_data.wr_ch_cnt; i++) {
+- mask |= BIT(vsec_data.ll_wr[i].bar);
+- mask |= BIT(vsec_data.dt_wr[i].bar);
+- }
+- for (i = 0; i < vsec_data.rd_ch_cnt; i++) {
+- mask |= BIT(vsec_data.ll_rd[i].bar);
+- mask |= BIT(vsec_data.dt_rd[i].bar);
++ mask = BIT(vsec_data->rg.bar);
++ for (i = 0; i < vsec_data->wr_ch_cnt; i++) {
++ mask |= BIT(vsec_data->ll_wr[i].bar);
++ mask |= BIT(vsec_data->dt_wr[i].bar);
++ }
++ for (i = 0; i < vsec_data->rd_ch_cnt; i++) {
++ mask |= BIT(vsec_data->ll_rd[i].bar);
++ mask |= BIT(vsec_data->dt_rd[i].bar);
+ }
+ err = pcim_iomap_regions(pdev, mask, pci_name(pdev));
+ if (err) {
+@@ -212,7 +216,7 @@ static int dw_edma_pcie_probe(struct pci
+ return -ENOMEM;
+
+ /* IRQs allocation */
+- nr_irqs = pci_alloc_irq_vectors(pdev, 1, vsec_data.irqs,
++ nr_irqs = pci_alloc_irq_vectors(pdev, 1, vsec_data->irqs,
+ PCI_IRQ_MSI | PCI_IRQ_MSIX);
+ if (nr_irqs < 1) {
+ pci_err(pdev, "fail to alloc IRQ vector (number of IRQs=%u)\n",
+@@ -223,22 +227,22 @@ static int dw_edma_pcie_probe(struct pci
+ /* Data structure initialization */
+ chip->dev = dev;
+
+- chip->mf = vsec_data.mf;
++ chip->mf = vsec_data->mf;
+ chip->nr_irqs = nr_irqs;
+ chip->ops = &dw_edma_pcie_plat_ops;
+
+- chip->ll_wr_cnt = vsec_data.wr_ch_cnt;
+- chip->ll_rd_cnt = vsec_data.rd_ch_cnt;
++ chip->ll_wr_cnt = vsec_data->wr_ch_cnt;
++ chip->ll_rd_cnt = vsec_data->rd_ch_cnt;
+
+- chip->reg_base = pcim_iomap_table(pdev)[vsec_data.rg.bar];
++ chip->reg_base = pcim_iomap_table(pdev)[vsec_data->rg.bar];
+ if (!chip->reg_base)
+ return -ENOMEM;
+
+ for (i = 0; i < chip->ll_wr_cnt; i++) {
+ struct dw_edma_region *ll_region = &chip->ll_region_wr[i];
+ struct dw_edma_region *dt_region = &chip->dt_region_wr[i];
+- struct dw_edma_block *ll_block = &vsec_data.ll_wr[i];
+- struct dw_edma_block *dt_block = &vsec_data.dt_wr[i];
++ struct dw_edma_block *ll_block = &vsec_data->ll_wr[i];
++ struct dw_edma_block *dt_block = &vsec_data->dt_wr[i];
+
+ ll_region->vaddr.io = pcim_iomap_table(pdev)[ll_block->bar];
+ if (!ll_region->vaddr.io)
+@@ -262,8 +266,8 @@ static int dw_edma_pcie_probe(struct pci
+ for (i = 0; i < chip->ll_rd_cnt; i++) {
+ struct dw_edma_region *ll_region = &chip->ll_region_rd[i];
+ struct dw_edma_region *dt_region = &chip->dt_region_rd[i];
+- struct dw_edma_block *ll_block = &vsec_data.ll_rd[i];
+- struct dw_edma_block *dt_block = &vsec_data.dt_rd[i];
++ struct dw_edma_block *ll_block = &vsec_data->ll_rd[i];
++ struct dw_edma_block *dt_block = &vsec_data->dt_rd[i];
+
+ ll_region->vaddr.io = pcim_iomap_table(pdev)[ll_block->bar];
+ if (!ll_region->vaddr.io)
+@@ -295,31 +299,31 @@ static int dw_edma_pcie_probe(struct pci
+ pci_dbg(pdev, "Version:\tUnknown (0x%x)\n", chip->mf);
+
+ pci_dbg(pdev, "Registers:\tBAR=%u, off=0x%.8lx, sz=0x%zx bytes, addr(v=%p)\n",
+- vsec_data.rg.bar, vsec_data.rg.off, vsec_data.rg.sz,
++ vsec_data->rg.bar, vsec_data->rg.off, vsec_data->rg.sz,
+ chip->reg_base);
+
+
+ for (i = 0; i < chip->ll_wr_cnt; i++) {
+ pci_dbg(pdev, "L. List:\tWRITE CH%.2u, BAR=%u, off=0x%.8lx, sz=0x%zx bytes, addr(v=%p, p=%pa)\n",
+- i, vsec_data.ll_wr[i].bar,
+- vsec_data.ll_wr[i].off, chip->ll_region_wr[i].sz,
++ i, vsec_data->ll_wr[i].bar,
++ vsec_data->ll_wr[i].off, chip->ll_region_wr[i].sz,
+ chip->ll_region_wr[i].vaddr.io, &chip->ll_region_wr[i].paddr);
+
+ pci_dbg(pdev, "Data:\tWRITE CH%.2u, BAR=%u, off=0x%.8lx, sz=0x%zx bytes, addr(v=%p, p=%pa)\n",
+- i, vsec_data.dt_wr[i].bar,
+- vsec_data.dt_wr[i].off, chip->dt_region_wr[i].sz,
++ i, vsec_data->dt_wr[i].bar,
++ vsec_data->dt_wr[i].off, chip->dt_region_wr[i].sz,
+ chip->dt_region_wr[i].vaddr.io, &chip->dt_region_wr[i].paddr);
+ }
+
+ for (i = 0; i < chip->ll_rd_cnt; i++) {
+ pci_dbg(pdev, "L. List:\tREAD CH%.2u, BAR=%u, off=0x%.8lx, sz=0x%zx bytes, addr(v=%p, p=%pa)\n",
+- i, vsec_data.ll_rd[i].bar,
+- vsec_data.ll_rd[i].off, chip->ll_region_rd[i].sz,
++ i, vsec_data->ll_rd[i].bar,
++ vsec_data->ll_rd[i].off, chip->ll_region_rd[i].sz,
+ chip->ll_region_rd[i].vaddr.io, &chip->ll_region_rd[i].paddr);
+
+ pci_dbg(pdev, "Data:\tREAD CH%.2u, BAR=%u, off=0x%.8lx, sz=0x%zx bytes, addr(v=%p, p=%pa)\n",
+- i, vsec_data.dt_rd[i].bar,
+- vsec_data.dt_rd[i].off, chip->dt_region_rd[i].sz,
++ i, vsec_data->dt_rd[i].bar,
++ vsec_data->dt_rd[i].off, chip->dt_region_rd[i].sz,
+ chip->dt_region_rd[i].vaddr.io, &chip->dt_region_rd[i].paddr);
+ }
+
--- /dev/null
+From stable+bounces-289285-greg=kroah.com@vger.kernel.org Sun Jul 26 15:35:25 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 26 Jul 2026 09:35:16 -0400
+Subject: dmaengine: dw-edma: Fix confusing cleanup.h syntax
+To: stable@vger.kernel.org
+Cc: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>, Manivannan Sadhasivam <mani@kernel.org>, Vinod Koul <vkoul@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260726133517.364299-2-sashal@kernel.org>
+
+From: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
+
+[ Upstream commit f9ef8dedee34e2d7828d5a6a0643cd969aaa8437 ]
+
+Initializing automatic __free variables to NULL without need (e.g.
+branches with different allocations), followed by actual allocation is
+in contrary to explicit coding rules guiding cleanup.h:
+
+"Given that the "__free(...) = NULL" pattern for variables defined at
+the top of the function poses this potential interdependency problem the
+recommendation is to always define and assign variables in one statement
+and not group variable definitions at the top of the function when
+__free() is used."
+
+Code does not have a bug, but is less readable and uses discouraged
+coding practice, so fix that by moving declaration to the place of
+assignment.
+
+Signed-off-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
+Reviewed-by: Manivannan Sadhasivam <mani@kernel.org>
+Link: https://patch.msgid.link/20251208020729.4654-2-krzysztof.kozlowski@oss.qualcomm.com
+Signed-off-by: Vinod Koul <vkoul@kernel.org>
+Stable-dep-of: 11d7cfe0c119 ("dmaengine: dw-edma-pcie: Reject devices without driver data")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/dma/dw-edma/dw-edma-pcie.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/dma/dw-edma/dw-edma-pcie.c
++++ b/drivers/dma/dw-edma/dw-edma-pcie.c
+@@ -160,13 +160,13 @@ static int dw_edma_pcie_probe(struct pci
+ const struct pci_device_id *pid)
+ {
+ struct dw_edma_pcie_data *pdata = (void *)pid->driver_data;
+- struct dw_edma_pcie_data *vsec_data __free(kfree) = NULL;
+ struct device *dev = &pdev->dev;
+ struct dw_edma_chip *chip;
+ int err, nr_irqs;
+ int i, mask;
+
+- vsec_data = kmalloc(sizeof(*vsec_data), GFP_KERNEL);
++ struct dw_edma_pcie_data *vsec_data __free(kfree) =
++ kmalloc(sizeof(*vsec_data), GFP_KERNEL);
+ if (!vsec_data)
+ return -ENOMEM;
+
--- /dev/null
+From stable+bounces-289286-greg=kroah.com@vger.kernel.org Sun Jul 26 15:35:25 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 26 Jul 2026 09:35:17 -0400
+Subject: dmaengine: dw-edma-pcie: Reject devices without driver data
+To: stable@vger.kernel.org
+Cc: Koichiro Den <den@valinux.co.jp>, Frank Li <Frank.Li@nxp.com>, Vinod Koul <vkoul@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260726133517.364299-3-sashal@kernel.org>
+
+From: Koichiro Den <den@valinux.co.jp>
+
+[ Upstream commit 11d7cfe0c119691b2dafbb699bbca90258c678aa ]
+
+dw_edma_pcie_probe() treats the PCI device ID driver_data as the
+template for the controller layout and copies it unconditionally. A
+device bound dynamically via sysfs can match the driver without that
+data, which leads to a NULL pointer dereference.
+
+Reject such matches before enabling the device.
+
+Fixes: 41aaff2a2ac0 ("dmaengine: Add Synopsys eDMA IP PCIe glue-logic")
+Cc: stable@vger.kernel.org
+Signed-off-by: Koichiro Den <den@valinux.co.jp>
+Reviewed-by: Frank Li <Frank.Li@nxp.com>
+Link: https://patch.msgid.link/20260521142153.2957432-3-den@valinux.co.jp
+Signed-off-by: Vinod Koul <vkoul@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/dma/dw-edma/dw-edma-pcie.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/drivers/dma/dw-edma/dw-edma-pcie.c
++++ b/drivers/dma/dw-edma/dw-edma-pcie.c
+@@ -165,6 +165,9 @@ static int dw_edma_pcie_probe(struct pci
+ int err, nr_irqs;
+ int i, mask;
+
++ if (!pdata)
++ return -ENODEV;
++
+ struct dw_edma_pcie_data *vsec_data __free(kfree) =
+ kmalloc(sizeof(*vsec_data), GFP_KERNEL);
+ if (!vsec_data)
--- /dev/null
+From stable+bounces-287937-greg=kroah.com@vger.kernel.org Wed Jul 22 18:09:58 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 12:07:57 -0400
+Subject: fbcon: Rename struct fbcon_ops to struct fbcon_par
+To: stable@vger.kernel.org
+Cc: Thomas Zimmermann <tzimmermann@suse.de>, Sam Ravnborg <sam@ravnborg.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260722160758.1740441-1-sashal@kernel.org>
+
+From: Thomas Zimmermann <tzimmermann@suse.de>
+
+[ Upstream commit a6adbbc4c32a016146e117b1e9e5242724a75e10 ]
+
+The type struct fbcon_ops contains fbcon state and callbacks. As the
+callbacks will be removed from struct fbcon_ops, rename the data type
+to struct fbcon_par. Also rename the variables from ops to par.
+
+The _par postfix ("private access registers") is used throughout the
+fbdev subsystem for per-driver state. The fbcon pointer within struct
+fb_info is also named fbcon_par. Hence, the new naming fits existing
+practice.
+
+v2:
+- rename struct fbcon_ops to struct fbcon_par
+- fix build for CONFIG_FB_TILEBITTING=n (kernel test robot)
+- fix indention
+
+Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
+Reviewed-by: Sam Ravnborg <sam@ravnborg.org>
+Link: https://lore.kernel.org/r/20250909124616.143365-3-tzimmermann@suse.de
+Stable-dep-of: 84202754fb17 ("fbcon: Use correct type for vc_resize() return value")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/video/fbdev/core/bitblit.c | 122 ++++-----
+ drivers/video/fbdev/core/fbcon.c | 417 +++++++++++++++-----------------
+ drivers/video/fbdev/core/fbcon.h | 6
+ drivers/video/fbdev/core/fbcon_ccw.c | 148 +++++------
+ drivers/video/fbdev/core/fbcon_cw.c | 148 +++++------
+ drivers/video/fbdev/core/fbcon_rotate.c | 44 +--
+ drivers/video/fbdev/core/fbcon_rotate.h | 6
+ drivers/video/fbdev/core/fbcon_ud.c | 164 ++++++------
+ drivers/video/fbdev/core/softcursor.c | 20 -
+ drivers/video/fbdev/core/tileblit.c | 28 +-
+ 10 files changed, 547 insertions(+), 556 deletions(-)
+
+--- a/drivers/video/fbdev/core/bitblit.c
++++ b/drivers/video/fbdev/core/bitblit.c
+@@ -261,10 +261,10 @@ static void bit_cursor(struct vc_data *v
+ int fg, int bg)
+ {
+ struct fb_cursor cursor;
+- struct fbcon_ops *ops = info->fbcon_par;
++ struct fbcon_par *par = info->fbcon_par;
+ unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
+ int w = DIV_ROUND_UP(vc->vc_font.width, 8), c;
+- int y = real_y(ops->p, vc->state.y);
++ int y = real_y(par->p, vc->state.y);
+ int attribute, use_sw = vc->vc_cursor_type & CUR_SW;
+ int err = 1;
+ char *src;
+@@ -278,10 +278,10 @@ static void bit_cursor(struct vc_data *v
+ attribute = get_attribute(info, c);
+ src = vc->vc_font.data + ((c & charmask) * (w * vc->vc_font.height));
+
+- if (ops->cursor_state.image.data != src ||
+- ops->cursor_reset) {
+- ops->cursor_state.image.data = src;
+- cursor.set |= FB_CUR_SETIMAGE;
++ if (par->cursor_state.image.data != src ||
++ par->cursor_reset) {
++ par->cursor_state.image.data = src;
++ cursor.set |= FB_CUR_SETIMAGE;
+ }
+
+ if (attribute) {
+@@ -290,46 +290,46 @@ static void bit_cursor(struct vc_data *v
+ dst = kmalloc_array(w, vc->vc_font.height, GFP_ATOMIC);
+ if (!dst)
+ return;
+- kfree(ops->cursor_data);
+- ops->cursor_data = dst;
++ kfree(par->cursor_data);
++ par->cursor_data = dst;
+ update_attr(dst, src, attribute, vc);
+ src = dst;
+ }
+
+- if (ops->cursor_state.image.fg_color != fg ||
+- ops->cursor_state.image.bg_color != bg ||
+- ops->cursor_reset) {
+- ops->cursor_state.image.fg_color = fg;
+- ops->cursor_state.image.bg_color = bg;
++ if (par->cursor_state.image.fg_color != fg ||
++ par->cursor_state.image.bg_color != bg ||
++ par->cursor_reset) {
++ par->cursor_state.image.fg_color = fg;
++ par->cursor_state.image.bg_color = bg;
+ cursor.set |= FB_CUR_SETCMAP;
+ }
+
+- if ((ops->cursor_state.image.dx != (vc->vc_font.width * vc->state.x)) ||
+- (ops->cursor_state.image.dy != (vc->vc_font.height * y)) ||
+- ops->cursor_reset) {
+- ops->cursor_state.image.dx = vc->vc_font.width * vc->state.x;
+- ops->cursor_state.image.dy = vc->vc_font.height * y;
++ if ((par->cursor_state.image.dx != (vc->vc_font.width * vc->state.x)) ||
++ (par->cursor_state.image.dy != (vc->vc_font.height * y)) ||
++ par->cursor_reset) {
++ par->cursor_state.image.dx = vc->vc_font.width * vc->state.x;
++ par->cursor_state.image.dy = vc->vc_font.height * y;
+ cursor.set |= FB_CUR_SETPOS;
+ }
+
+- if (ops->cursor_state.image.height != vc->vc_font.height ||
+- ops->cursor_state.image.width != vc->vc_font.width ||
+- ops->cursor_reset) {
+- ops->cursor_state.image.height = vc->vc_font.height;
+- ops->cursor_state.image.width = vc->vc_font.width;
++ if (par->cursor_state.image.height != vc->vc_font.height ||
++ par->cursor_state.image.width != vc->vc_font.width ||
++ par->cursor_reset) {
++ par->cursor_state.image.height = vc->vc_font.height;
++ par->cursor_state.image.width = vc->vc_font.width;
+ cursor.set |= FB_CUR_SETSIZE;
+ }
+
+- if (ops->cursor_state.hot.x || ops->cursor_state.hot.y ||
+- ops->cursor_reset) {
+- ops->cursor_state.hot.x = cursor.hot.y = 0;
++ if (par->cursor_state.hot.x || par->cursor_state.hot.y ||
++ par->cursor_reset) {
++ par->cursor_state.hot.x = cursor.hot.y = 0;
+ cursor.set |= FB_CUR_SETHOT;
+ }
+
+ if (cursor.set & FB_CUR_SETSIZE ||
+- vc->vc_cursor_type != ops->p->cursor_shape ||
+- ops->cursor_state.mask == NULL ||
+- ops->cursor_reset) {
++ vc->vc_cursor_type != par->p->cursor_shape ||
++ par->cursor_state.mask == NULL ||
++ par->cursor_reset) {
+ char *mask = kmalloc_array(w, vc->vc_font.height, GFP_ATOMIC);
+ int cur_height, size, i = 0;
+ u8 msk = 0xff;
+@@ -337,13 +337,13 @@ static void bit_cursor(struct vc_data *v
+ if (!mask)
+ return;
+
+- kfree(ops->cursor_state.mask);
+- ops->cursor_state.mask = mask;
++ kfree(par->cursor_state.mask);
++ par->cursor_state.mask = mask;
+
+- ops->p->cursor_shape = vc->vc_cursor_type;
++ par->p->cursor_shape = vc->vc_cursor_type;
+ cursor.set |= FB_CUR_SETSHAPE;
+
+- switch (CUR_SIZE(ops->p->cursor_shape)) {
++ switch (CUR_SIZE(par->p->cursor_shape)) {
+ case CUR_NONE:
+ cur_height = 0;
+ break;
+@@ -374,26 +374,26 @@ static void bit_cursor(struct vc_data *v
+
+ switch (mode) {
+ case CM_ERASE:
+- ops->cursor_state.enable = 0;
++ par->cursor_state.enable = 0;
+ break;
+ case CM_DRAW:
+ case CM_MOVE:
+ default:
+- ops->cursor_state.enable = (use_sw) ? 0 : 1;
++ par->cursor_state.enable = (use_sw) ? 0 : 1;
+ break;
+ }
+
+ cursor.image.data = src;
+- cursor.image.fg_color = ops->cursor_state.image.fg_color;
+- cursor.image.bg_color = ops->cursor_state.image.bg_color;
+- cursor.image.dx = ops->cursor_state.image.dx;
+- cursor.image.dy = ops->cursor_state.image.dy;
+- cursor.image.height = ops->cursor_state.image.height;
+- cursor.image.width = ops->cursor_state.image.width;
+- cursor.hot.x = ops->cursor_state.hot.x;
+- cursor.hot.y = ops->cursor_state.hot.y;
+- cursor.mask = ops->cursor_state.mask;
+- cursor.enable = ops->cursor_state.enable;
++ cursor.image.fg_color = par->cursor_state.image.fg_color;
++ cursor.image.bg_color = par->cursor_state.image.bg_color;
++ cursor.image.dx = par->cursor_state.image.dx;
++ cursor.image.dy = par->cursor_state.image.dy;
++ cursor.image.height = par->cursor_state.image.height;
++ cursor.image.width = par->cursor_state.image.width;
++ cursor.hot.x = par->cursor_state.hot.x;
++ cursor.hot.y = par->cursor_state.hot.y;
++ cursor.mask = par->cursor_state.mask;
++ cursor.enable = par->cursor_state.enable;
+ cursor.image.depth = 1;
+ cursor.rop = ROP_XOR;
+
+@@ -403,31 +403,31 @@ static void bit_cursor(struct vc_data *v
+ if (err)
+ soft_cursor(info, &cursor);
+
+- ops->cursor_reset = 0;
++ par->cursor_reset = 0;
+ }
+
+ static int bit_update_start(struct fb_info *info)
+ {
+- struct fbcon_ops *ops = info->fbcon_par;
++ struct fbcon_par *par = info->fbcon_par;
+ int err;
+
+- err = fb_pan_display(info, &ops->var);
+- ops->var.xoffset = info->var.xoffset;
+- ops->var.yoffset = info->var.yoffset;
+- ops->var.vmode = info->var.vmode;
++ err = fb_pan_display(info, &par->var);
++ par->var.xoffset = info->var.xoffset;
++ par->var.yoffset = info->var.yoffset;
++ par->var.vmode = info->var.vmode;
+ return err;
+ }
+
+-void fbcon_set_bitops(struct fbcon_ops *ops)
++void fbcon_set_bitops(struct fbcon_par *par)
+ {
+- ops->bmove = bit_bmove;
+- ops->clear = bit_clear;
+- ops->putcs = bit_putcs;
+- ops->clear_margins = bit_clear_margins;
+- ops->cursor = bit_cursor;
+- ops->update_start = bit_update_start;
+- ops->rotate_font = NULL;
++ par->bmove = bit_bmove;
++ par->clear = bit_clear;
++ par->putcs = bit_putcs;
++ par->clear_margins = bit_clear_margins;
++ par->cursor = bit_cursor;
++ par->update_start = bit_update_start;
++ par->rotate_font = NULL;
+
+- if (ops->rotate)
+- fbcon_set_rotate(ops);
++ if (par->rotate)
++ fbcon_set_rotate(par);
+ }
+--- a/drivers/video/fbdev/core/fbcon.c
++++ b/drivers/video/fbdev/core/fbcon.c
+@@ -198,27 +198,27 @@ static struct device *fbcon_device;
+ #ifdef CONFIG_FRAMEBUFFER_CONSOLE_ROTATION
+ static inline void fbcon_set_rotation(struct fb_info *info)
+ {
+- struct fbcon_ops *ops = info->fbcon_par;
++ struct fbcon_par *par = info->fbcon_par;
+
+ if (!(info->flags & FBINFO_MISC_TILEBLITTING) &&
+- ops->p->con_rotate < 4)
+- ops->rotate = ops->p->con_rotate;
++ par->p->con_rotate < 4)
++ par->rotate = par->p->con_rotate;
+ else
+- ops->rotate = 0;
++ par->rotate = 0;
+ }
+
+ static void fbcon_rotate(struct fb_info *info, u32 rotate)
+ {
+- struct fbcon_ops *ops= info->fbcon_par;
++ struct fbcon_par *par = info->fbcon_par;
+ struct fb_info *fb_info;
+
+- if (!ops || ops->currcon == -1)
++ if (!par || par->currcon == -1)
+ return;
+
+- fb_info = fbcon_info_from_console(ops->currcon);
++ fb_info = fbcon_info_from_console(par->currcon);
+
+ if (info == fb_info) {
+- struct fbcon_display *p = &fb_display[ops->currcon];
++ struct fbcon_display *p = &fb_display[par->currcon];
+
+ if (rotate < 4)
+ p->con_rotate = rotate;
+@@ -231,12 +231,12 @@ static void fbcon_rotate(struct fb_info
+
+ static void fbcon_rotate_all(struct fb_info *info, u32 rotate)
+ {
+- struct fbcon_ops *ops = info->fbcon_par;
++ struct fbcon_par *par = info->fbcon_par;
+ struct vc_data *vc;
+ struct fbcon_display *p;
+ int i;
+
+- if (!ops || ops->currcon < 0 || rotate > 3)
++ if (!par || par->currcon < 0 || rotate > 3)
+ return;
+
+ for (i = first_fb_vc; i <= last_fb_vc; i++) {
+@@ -254,9 +254,9 @@ static void fbcon_rotate_all(struct fb_i
+ #else
+ static inline void fbcon_set_rotation(struct fb_info *info)
+ {
+- struct fbcon_ops *ops = info->fbcon_par;
++ struct fbcon_par *par = info->fbcon_par;
+
+- ops->rotate = FB_ROTATE_UR;
++ par->rotate = FB_ROTATE_UR;
+ }
+
+ static void fbcon_rotate(struct fb_info *info, u32 rotate)
+@@ -272,17 +272,17 @@ static void fbcon_rotate_all(struct fb_i
+
+ static int fbcon_get_rotate(struct fb_info *info)
+ {
+- struct fbcon_ops *ops = info->fbcon_par;
++ struct fbcon_par *par = info->fbcon_par;
+
+- return (ops) ? ops->rotate : 0;
++ return (par) ? par->rotate : 0;
+ }
+
+ static inline int fbcon_is_inactive(struct vc_data *vc, struct fb_info *info)
+ {
+- struct fbcon_ops *ops = info->fbcon_par;
++ struct fbcon_par *par = info->fbcon_par;
+
+ return (info->state != FBINFO_STATE_RUNNING ||
+- vc->vc_mode != KD_TEXT || ops->graphics);
++ vc->vc_mode != KD_TEXT || par->graphics);
+ }
+
+ static int get_color(struct vc_data *vc, struct fb_info *info,
+@@ -354,7 +354,7 @@ static int get_color(struct vc_data *vc,
+
+ static void fb_flashcursor(struct work_struct *work)
+ {
+- struct fbcon_ops *ops = container_of(work, struct fbcon_ops, cursor_work.work);
++ struct fbcon_par *par = container_of(work, struct fbcon_par, cursor_work.work);
+ struct fb_info *info;
+ struct vc_data *vc = NULL;
+ int c;
+@@ -369,10 +369,10 @@ static void fb_flashcursor(struct work_s
+ return;
+
+ /* protected by console_lock */
+- info = ops->info;
++ info = par->info;
+
+- if (ops->currcon != -1)
+- vc = vc_cons[ops->currcon].d;
++ if (par->currcon != -1)
++ vc = vc_cons[par->currcon].d;
+
+ if (!vc || !con_is_visible(vc) ||
+ fbcon_info_from_console(vc->vc_num) != info ||
+@@ -382,30 +382,30 @@ static void fb_flashcursor(struct work_s
+ }
+
+ c = scr_readw((u16 *) vc->vc_pos);
+- mode = (!ops->cursor_flash || ops->cursor_state.enable) ?
++ mode = (!par->cursor_flash || par->cursor_state.enable) ?
+ CM_ERASE : CM_DRAW;
+- ops->cursor(vc, info, mode, get_color(vc, info, c, 1),
++ par->cursor(vc, info, mode, get_color(vc, info, c, 1),
+ get_color(vc, info, c, 0));
+ console_unlock();
+
+- queue_delayed_work(system_power_efficient_wq, &ops->cursor_work,
+- ops->cur_blink_jiffies);
++ queue_delayed_work(system_power_efficient_wq, &par->cursor_work,
++ par->cur_blink_jiffies);
+ }
+
+ static void fbcon_add_cursor_work(struct fb_info *info)
+ {
+- struct fbcon_ops *ops = info->fbcon_par;
++ struct fbcon_par *par = info->fbcon_par;
+
+ if (!fbcon_cursor_noblink)
+- queue_delayed_work(system_power_efficient_wq, &ops->cursor_work,
+- ops->cur_blink_jiffies);
++ queue_delayed_work(system_power_efficient_wq, &par->cursor_work,
++ par->cur_blink_jiffies);
+ }
+
+ static void fbcon_del_cursor_work(struct fb_info *info)
+ {
+- struct fbcon_ops *ops = info->fbcon_par;
++ struct fbcon_par *par = info->fbcon_par;
+
+- cancel_delayed_work_sync(&ops->cursor_work);
++ cancel_delayed_work_sync(&par->cursor_work);
+ }
+
+ #ifndef MODULE
+@@ -559,7 +559,7 @@ static void fbcon_prepare_logo(struct vc
+ int cols, int rows, int new_cols, int new_rows)
+ {
+ /* Need to make room for the logo */
+- struct fbcon_ops *ops = info->fbcon_par;
++ struct fbcon_par *par = info->fbcon_par;
+ int cnt, erase = vc->vc_video_erase_char, step;
+ unsigned short *save = NULL, *r, *q;
+ int logo_height;
+@@ -575,7 +575,7 @@ static void fbcon_prepare_logo(struct vc
+ */
+ if (fb_get_color_depth(&info->var, &info->fix) == 1)
+ erase &= ~0x400;
+- logo_height = fb_prepare_logo(info, ops->rotate);
++ logo_height = fb_prepare_logo(info, par->rotate);
+ logo_lines = DIV_ROUND_UP(logo_height, vc->vc_font.height);
+ q = (unsigned short *) (vc->vc_origin +
+ vc->vc_size_row * rows);
+@@ -648,15 +648,15 @@ static void fbcon_prepare_logo(struct vc
+ #ifdef CONFIG_FB_TILEBLITTING
+ static void set_blitting_type(struct vc_data *vc, struct fb_info *info)
+ {
+- struct fbcon_ops *ops = info->fbcon_par;
++ struct fbcon_par *par = info->fbcon_par;
+
+- ops->p = &fb_display[vc->vc_num];
++ par->p = &fb_display[vc->vc_num];
+
+ if ((info->flags & FBINFO_MISC_TILEBLITTING))
+ fbcon_set_tileops(vc, info);
+ else {
+ fbcon_set_rotation(info);
+- fbcon_set_bitops(ops);
++ fbcon_set_bitops(par);
+ }
+ }
+
+@@ -673,12 +673,12 @@ static int fbcon_invalid_charcount(struc
+ #else
+ static void set_blitting_type(struct vc_data *vc, struct fb_info *info)
+ {
+- struct fbcon_ops *ops = info->fbcon_par;
++ struct fbcon_par *par = info->fbcon_par;
+
+ info->flags &= ~FBINFO_MISC_TILEBLITTING;
+- ops->p = &fb_display[vc->vc_num];
++ par->p = &fb_display[vc->vc_num];
+ fbcon_set_rotation(info);
+- fbcon_set_bitops(ops);
++ fbcon_set_bitops(par);
+ }
+
+ static int fbcon_invalid_charcount(struct fb_info *info, unsigned charcount)
+@@ -698,13 +698,13 @@ static void fbcon_release(struct fb_info
+ module_put(info->fbops->owner);
+
+ if (info->fbcon_par) {
+- struct fbcon_ops *ops = info->fbcon_par;
++ struct fbcon_par *par = info->fbcon_par;
+
+ fbcon_del_cursor_work(info);
+- kfree(ops->cursor_state.mask);
+- kfree(ops->cursor_data);
+- kfree(ops->cursor_src);
+- kfree(ops->fontbuffer);
++ kfree(par->cursor_state.mask);
++ kfree(par->cursor_data);
++ kfree(par->cursor_src);
++ kfree(par->fontbuffer);
+ kfree(info->fbcon_par);
+ info->fbcon_par = NULL;
+ }
+@@ -712,7 +712,7 @@ static void fbcon_release(struct fb_info
+
+ static int fbcon_open(struct fb_info *info)
+ {
+- struct fbcon_ops *ops;
++ struct fbcon_par *par;
+
+ if (!try_module_get(info->fbops->owner))
+ return -ENODEV;
+@@ -726,16 +726,16 @@ static int fbcon_open(struct fb_info *in
+ }
+ unlock_fb_info(info);
+
+- ops = kzalloc(sizeof(struct fbcon_ops), GFP_KERNEL);
+- if (!ops) {
++ par = kzalloc(sizeof(*par), GFP_KERNEL);
++ if (!par) {
+ fbcon_release(info);
+ return -ENOMEM;
+ }
+
+- INIT_DELAYED_WORK(&ops->cursor_work, fb_flashcursor);
+- ops->info = info;
+- info->fbcon_par = ops;
+- ops->cur_blink_jiffies = HZ / 5;
++ INIT_DELAYED_WORK(&par->cursor_work, fb_flashcursor);
++ par->info = info;
++ info->fbcon_par = par;
++ par->cur_blink_jiffies = HZ / 5;
+
+ return 0;
+ }
+@@ -782,12 +782,12 @@ static void con2fb_release_oldinfo(struc
+ static void con2fb_init_display(struct vc_data *vc, struct fb_info *info,
+ int unit, int show_logo)
+ {
+- struct fbcon_ops *ops = info->fbcon_par;
++ struct fbcon_par *par = info->fbcon_par;
+ int ret;
+
+- ops->currcon = fg_console;
++ par->currcon = fg_console;
+
+- if (info->fbops->fb_set_par && !ops->initialized) {
++ if (info->fbops->fb_set_par && !par->initialized) {
+ ret = info->fbops->fb_set_par(info);
+
+ if (ret)
+@@ -796,8 +796,8 @@ static void con2fb_init_display(struct v
+ "error code %d\n", ret);
+ }
+
+- ops->initialized = true;
+- ops->graphics = 0;
++ par->initialized = true;
++ par->graphics = 0;
+ fbcon_set_disp(info, &info->var, unit);
+
+ if (show_logo) {
+@@ -934,7 +934,7 @@ static const char *fbcon_startup(void)
+ struct vc_data *vc = vc_cons[fg_console].d;
+ const struct font_desc *font = NULL;
+ struct fb_info *info = NULL;
+- struct fbcon_ops *ops;
++ struct fbcon_par *par;
+ int rows, cols;
+
+ /*
+@@ -954,10 +954,10 @@ static const char *fbcon_startup(void)
+ if (fbcon_open(info))
+ return NULL;
+
+- ops = info->fbcon_par;
+- ops->currcon = -1;
+- ops->graphics = 1;
+- ops->cur_rotate = -1;
++ par = info->fbcon_par;
++ par->currcon = -1;
++ par->graphics = 1;
++ par->cur_rotate = -1;
+
+ p->con_rotate = initial_rotation;
+ if (p->con_rotate == -1)
+@@ -980,8 +980,8 @@ static const char *fbcon_startup(void)
+ vc->vc_font.charcount = font->charcount;
+ }
+
+- cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres);
+- rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
++ cols = FBCON_SWAP(par->rotate, info->var.xres, info->var.yres);
++ rows = FBCON_SWAP(par->rotate, info->var.yres, info->var.xres);
+ cols /= vc->vc_font.width;
+ rows /= vc->vc_font.height;
+ vc_resize(vc, cols, rows);
+@@ -999,7 +999,7 @@ static const char *fbcon_startup(void)
+ static void fbcon_init(struct vc_data *vc, bool init)
+ {
+ struct fb_info *info;
+- struct fbcon_ops *ops;
++ struct fbcon_par *par;
+ struct vc_data **default_mode = vc->vc_display_fg;
+ struct vc_data *svc = *default_mode;
+ struct fbcon_display *t, *p = &fb_display[vc->vc_num];
+@@ -1074,8 +1074,8 @@ static void fbcon_init(struct vc_data *v
+ if (!*vc->uni_pagedict_loc)
+ con_copy_unimap(vc, svc);
+
+- ops = info->fbcon_par;
+- ops->cur_blink_jiffies = msecs_to_jiffies(vc->vc_cur_blink_ms);
++ par = info->fbcon_par;
++ par->cur_blink_jiffies = msecs_to_jiffies(vc->vc_cur_blink_ms);
+
+ p->con_rotate = initial_rotation;
+ if (p->con_rotate == -1)
+@@ -1087,8 +1087,8 @@ static void fbcon_init(struct vc_data *v
+
+ cols = vc->vc_cols;
+ rows = vc->vc_rows;
+- new_cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres);
+- new_rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
++ new_cols = FBCON_SWAP(par->rotate, info->var.xres, info->var.yres);
++ new_rows = FBCON_SWAP(par->rotate, info->var.yres, info->var.xres);
+ new_cols /= vc->vc_font.width;
+ new_rows /= vc->vc_font.height;
+
+@@ -1100,7 +1100,7 @@ static void fbcon_init(struct vc_data *v
+ * We need to do it in fbcon_init() to prevent screen corruption.
+ */
+ if (con_is_visible(vc) && vc->vc_mode == KD_TEXT) {
+- if (info->fbops->fb_set_par && !ops->initialized) {
++ if (info->fbops->fb_set_par && !par->initialized) {
+ ret = info->fbops->fb_set_par(info);
+
+ if (ret)
+@@ -1109,10 +1109,10 @@ static void fbcon_init(struct vc_data *v
+ "error code %d\n", ret);
+ }
+
+- ops->initialized = true;
++ par->initialized = true;
+ }
+
+- ops->graphics = 0;
++ par->graphics = 0;
+
+ #ifdef CONFIG_FRAMEBUFFER_CONSOLE_LEGACY_ACCELERATION
+ if ((info->flags & FBINFO_HWACCEL_COPYAREA) &&
+@@ -1136,12 +1136,12 @@ static void fbcon_init(struct vc_data *v
+ if (logo)
+ fbcon_prepare_logo(vc, info, cols, rows, new_cols, new_rows);
+
+- if (ops->rotate_font && ops->rotate_font(info, vc)) {
+- ops->rotate = FB_ROTATE_UR;
++ if (par->rotate_font && par->rotate_font(info, vc)) {
++ par->rotate = FB_ROTATE_UR;
+ set_blitting_type(vc, info);
+ }
+
+- ops->p = &fb_display[fg_console];
++ par->p = &fb_display[fg_console];
+ }
+
+ static void fbcon_free_font(struct fbcon_display *p)
+@@ -1179,7 +1179,7 @@ static void fbcon_deinit(struct vc_data
+ {
+ struct fbcon_display *p = &fb_display[vc->vc_num];
+ struct fb_info *info;
+- struct fbcon_ops *ops;
++ struct fbcon_par *par;
+ int idx;
+
+ fbcon_free_font(p);
+@@ -1194,15 +1194,15 @@ static void fbcon_deinit(struct vc_data
+ if (!info)
+ goto finished;
+
+- ops = info->fbcon_par;
++ par = info->fbcon_par;
+
+- if (!ops)
++ if (!par)
+ goto finished;
+
+ if (con_is_visible(vc))
+ fbcon_del_cursor_work(info);
+
+- ops->initialized = false;
++ par->initialized = false;
+ finished:
+
+ fbcon_free_font(p);
+@@ -1249,7 +1249,7 @@ static void __fbcon_clear(struct vc_data
+ unsigned int height, unsigned int width)
+ {
+ struct fb_info *info = fbcon_info_from_console(vc->vc_num);
+- struct fbcon_ops *ops = info->fbcon_par;
++ struct fbcon_par *par = info->fbcon_par;
+ int fg, bg;
+ struct fbcon_display *p = &fb_display[vc->vc_num];
+ u_int y_break;
+@@ -1264,7 +1264,7 @@ static void __fbcon_clear(struct vc_data
+ vc->vc_top = 0;
+ /*
+ * If the font dimensions are not an integral of the display
+- * dimensions then the ops->clear below won't end up clearing
++ * dimensions then the par->clear below won't end up clearing
+ * the margins. Call clear_margins here in case the logo
+ * bitmap stretched into the margin area.
+ */
+@@ -1278,11 +1278,10 @@ static void __fbcon_clear(struct vc_data
+ y_break = p->vrows - p->yscroll;
+ if (sy < y_break && sy + height - 1 >= y_break) {
+ u_int b = y_break - sy;
+- ops->clear(vc, info, real_y(p, sy), sx, b, width, fg, bg);
+- ops->clear(vc, info, real_y(p, sy + b), sx, height - b,
+- width, fg, bg);
++ par->clear(vc, info, real_y(p, sy), sx, b, width, fg, bg);
++ par->clear(vc, info, real_y(p, sy + b), sx, height - b, width, fg, bg);
+ } else
+- ops->clear(vc, info, real_y(p, sy), sx, height, width, fg, bg);
++ par->clear(vc, info, real_y(p, sy), sx, height, width, fg, bg);
+ }
+
+ static void fbcon_clear(struct vc_data *vc, unsigned int sy, unsigned int sx,
+@@ -1296,10 +1295,10 @@ static void fbcon_putcs(struct vc_data *
+ {
+ struct fb_info *info = fbcon_info_from_console(vc->vc_num);
+ struct fbcon_display *p = &fb_display[vc->vc_num];
+- struct fbcon_ops *ops = info->fbcon_par;
++ struct fbcon_par *par = info->fbcon_par;
+
+ if (!fbcon_is_inactive(vc, info))
+- ops->putcs(vc, info, s, count, real_y(p, ypos), xpos,
++ par->putcs(vc, info, s, count, real_y(p, ypos), xpos,
+ get_color(vc, info, scr_readw(s), 1),
+ get_color(vc, info, scr_readw(s), 0));
+ }
+@@ -1315,19 +1314,19 @@ static void fbcon_putc(struct vc_data *v
+ static void fbcon_clear_margins(struct vc_data *vc, int bottom_only)
+ {
+ struct fb_info *info = fbcon_info_from_console(vc->vc_num);
+- struct fbcon_ops *ops = info->fbcon_par;
++ struct fbcon_par *par = info->fbcon_par;
+
+ if (!fbcon_is_inactive(vc, info))
+- ops->clear_margins(vc, info, margin_color, bottom_only);
++ par->clear_margins(vc, info, margin_color, bottom_only);
+ }
+
+ static void fbcon_cursor(struct vc_data *vc, int mode)
+ {
+ struct fb_info *info = fbcon_info_from_console(vc->vc_num);
+- struct fbcon_ops *ops = info->fbcon_par;
++ struct fbcon_par *par = info->fbcon_par;
+ int c = scr_readw((u16 *) vc->vc_pos);
+
+- ops->cur_blink_jiffies = msecs_to_jiffies(vc->vc_cur_blink_ms);
++ par->cur_blink_jiffies = msecs_to_jiffies(vc->vc_cur_blink_ms);
+
+ if (fbcon_is_inactive(vc, info) || vc->vc_deccm != 1)
+ return;
+@@ -1337,12 +1336,12 @@ static void fbcon_cursor(struct vc_data
+ else
+ fbcon_add_cursor_work(info);
+
+- ops->cursor_flash = (mode == CM_ERASE) ? 0 : 1;
++ par->cursor_flash = (mode == CM_ERASE) ? 0 : 1;
+
+- if (!ops->cursor)
++ if (!par->cursor)
+ return;
+
+- ops->cursor(vc, info, mode, get_color(vc, info, c, 1),
++ par->cursor(vc, info, mode, get_color(vc, info, c, 1),
+ get_color(vc, info, c, 0));
+ }
+
+@@ -1356,7 +1355,7 @@ static void fbcon_set_disp(struct fb_inf
+ struct fbcon_display *p, *t;
+ struct vc_data **default_mode, *vc;
+ struct vc_data *svc;
+- struct fbcon_ops *ops = info->fbcon_par;
++ struct fbcon_par *par = info->fbcon_par;
+ int rows, cols;
+ unsigned long ret = 0;
+
+@@ -1389,7 +1388,7 @@ static void fbcon_set_disp(struct fb_inf
+ var->yoffset = info->var.yoffset;
+ var->xoffset = info->var.xoffset;
+ fb_set_var(info, var);
+- ops->var = info->var;
++ par->var = info->var;
+ vc->vc_can_do_color = (fb_get_color_depth(&info->var, &info->fix)!=1);
+ vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800;
+ if (vc->vc_font.charcount == 256) {
+@@ -1405,8 +1404,8 @@ static void fbcon_set_disp(struct fb_inf
+ if (!*vc->uni_pagedict_loc)
+ con_copy_unimap(vc, svc);
+
+- cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres);
+- rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
++ cols = FBCON_SWAP(par->rotate, info->var.xres, info->var.yres);
++ rows = FBCON_SWAP(par->rotate, info->var.yres, info->var.xres);
+ cols /= vc->vc_font.width;
+ rows /= vc->vc_font.height;
+ ret = vc_resize(vc, cols, rows);
+@@ -1418,16 +1417,16 @@ static void fbcon_set_disp(struct fb_inf
+ static __inline__ void ywrap_up(struct vc_data *vc, int count)
+ {
+ struct fb_info *info = fbcon_info_from_console(vc->vc_num);
+- struct fbcon_ops *ops = info->fbcon_par;
++ struct fbcon_par *par = info->fbcon_par;
+ struct fbcon_display *p = &fb_display[vc->vc_num];
+
+ p->yscroll += count;
+ if (p->yscroll >= p->vrows) /* Deal with wrap */
+ p->yscroll -= p->vrows;
+- ops->var.xoffset = 0;
+- ops->var.yoffset = p->yscroll * vc->vc_font.height;
+- ops->var.vmode |= FB_VMODE_YWRAP;
+- ops->update_start(info);
++ par->var.xoffset = 0;
++ par->var.yoffset = p->yscroll * vc->vc_font.height;
++ par->var.vmode |= FB_VMODE_YWRAP;
++ par->update_start(info);
+ scrollback_max += count;
+ if (scrollback_max > scrollback_phys_max)
+ scrollback_max = scrollback_phys_max;
+@@ -1437,16 +1436,16 @@ static __inline__ void ywrap_up(struct v
+ static __inline__ void ywrap_down(struct vc_data *vc, int count)
+ {
+ struct fb_info *info = fbcon_info_from_console(vc->vc_num);
+- struct fbcon_ops *ops = info->fbcon_par;
++ struct fbcon_par *par = info->fbcon_par;
+ struct fbcon_display *p = &fb_display[vc->vc_num];
+
+ p->yscroll -= count;
+ if (p->yscroll < 0) /* Deal with wrap */
+ p->yscroll += p->vrows;
+- ops->var.xoffset = 0;
+- ops->var.yoffset = p->yscroll * vc->vc_font.height;
+- ops->var.vmode |= FB_VMODE_YWRAP;
+- ops->update_start(info);
++ par->var.xoffset = 0;
++ par->var.yoffset = p->yscroll * vc->vc_font.height;
++ par->var.vmode |= FB_VMODE_YWRAP;
++ par->update_start(info);
+ scrollback_max -= count;
+ if (scrollback_max < 0)
+ scrollback_max = 0;
+@@ -1457,19 +1456,19 @@ static __inline__ void ypan_up(struct vc
+ {
+ struct fb_info *info = fbcon_info_from_console(vc->vc_num);
+ struct fbcon_display *p = &fb_display[vc->vc_num];
+- struct fbcon_ops *ops = info->fbcon_par;
++ struct fbcon_par *par = info->fbcon_par;
+
+ p->yscroll += count;
+ if (p->yscroll > p->vrows - vc->vc_rows) {
+- ops->bmove(vc, info, p->vrows - vc->vc_rows,
++ par->bmove(vc, info, p->vrows - vc->vc_rows,
+ 0, 0, 0, vc->vc_rows, vc->vc_cols);
+ p->yscroll -= p->vrows - vc->vc_rows;
+ }
+
+- ops->var.xoffset = 0;
+- ops->var.yoffset = p->yscroll * vc->vc_font.height;
+- ops->var.vmode &= ~FB_VMODE_YWRAP;
+- ops->update_start(info);
++ par->var.xoffset = 0;
++ par->var.yoffset = p->yscroll * vc->vc_font.height;
++ par->var.vmode &= ~FB_VMODE_YWRAP;
++ par->update_start(info);
+ fbcon_clear_margins(vc, 1);
+ scrollback_max += count;
+ if (scrollback_max > scrollback_phys_max)
+@@ -1480,7 +1479,7 @@ static __inline__ void ypan_up(struct vc
+ static __inline__ void ypan_up_redraw(struct vc_data *vc, int t, int count)
+ {
+ struct fb_info *info = fbcon_info_from_console(vc->vc_num);
+- struct fbcon_ops *ops = info->fbcon_par;
++ struct fbcon_par *par = info->fbcon_par;
+ struct fbcon_display *p = &fb_display[vc->vc_num];
+
+ p->yscroll += count;
+@@ -1490,10 +1489,10 @@ static __inline__ void ypan_up_redraw(st
+ fbcon_redraw_move(vc, p, t + count, vc->vc_rows - count, t);
+ }
+
+- ops->var.xoffset = 0;
+- ops->var.yoffset = p->yscroll * vc->vc_font.height;
+- ops->var.vmode &= ~FB_VMODE_YWRAP;
+- ops->update_start(info);
++ par->var.xoffset = 0;
++ par->var.yoffset = p->yscroll * vc->vc_font.height;
++ par->var.vmode &= ~FB_VMODE_YWRAP;
++ par->update_start(info);
+ fbcon_clear_margins(vc, 1);
+ scrollback_max += count;
+ if (scrollback_max > scrollback_phys_max)
+@@ -1505,19 +1504,19 @@ static __inline__ void ypan_down(struct
+ {
+ struct fb_info *info = fbcon_info_from_console(vc->vc_num);
+ struct fbcon_display *p = &fb_display[vc->vc_num];
+- struct fbcon_ops *ops = info->fbcon_par;
++ struct fbcon_par *par = info->fbcon_par;
+
+ p->yscroll -= count;
+ if (p->yscroll < 0) {
+- ops->bmove(vc, info, 0, 0, p->vrows - vc->vc_rows,
++ par->bmove(vc, info, 0, 0, p->vrows - vc->vc_rows,
+ 0, vc->vc_rows, vc->vc_cols);
+ p->yscroll += p->vrows - vc->vc_rows;
+ }
+
+- ops->var.xoffset = 0;
+- ops->var.yoffset = p->yscroll * vc->vc_font.height;
+- ops->var.vmode &= ~FB_VMODE_YWRAP;
+- ops->update_start(info);
++ par->var.xoffset = 0;
++ par->var.yoffset = p->yscroll * vc->vc_font.height;
++ par->var.vmode &= ~FB_VMODE_YWRAP;
++ par->update_start(info);
+ fbcon_clear_margins(vc, 1);
+ scrollback_max -= count;
+ if (scrollback_max < 0)
+@@ -1528,7 +1527,7 @@ static __inline__ void ypan_down(struct
+ static __inline__ void ypan_down_redraw(struct vc_data *vc, int t, int count)
+ {
+ struct fb_info *info = fbcon_info_from_console(vc->vc_num);
+- struct fbcon_ops *ops = info->fbcon_par;
++ struct fbcon_par *par = info->fbcon_par;
+ struct fbcon_display *p = &fb_display[vc->vc_num];
+
+ p->yscroll -= count;
+@@ -1538,10 +1537,10 @@ static __inline__ void ypan_down_redraw(
+ fbcon_redraw_move(vc, p, t, vc->vc_rows - count, t + count);
+ }
+
+- ops->var.xoffset = 0;
+- ops->var.yoffset = p->yscroll * vc->vc_font.height;
+- ops->var.vmode &= ~FB_VMODE_YWRAP;
+- ops->update_start(info);
++ par->var.xoffset = 0;
++ par->var.yoffset = p->yscroll * vc->vc_font.height;
++ par->var.vmode &= ~FB_VMODE_YWRAP;
++ par->update_start(info);
+ fbcon_clear_margins(vc, 1);
+ scrollback_max -= count;
+ if (scrollback_max < 0)
+@@ -1590,7 +1589,7 @@ static void fbcon_redraw_blit(struct vc_
+ unsigned short *d = (unsigned short *)
+ (vc->vc_origin + vc->vc_size_row * line);
+ unsigned short *s = d + offset;
+- struct fbcon_ops *ops = info->fbcon_par;
++ struct fbcon_par *par = info->fbcon_par;
+
+ while (count--) {
+ unsigned short *start = s;
+@@ -1603,8 +1602,8 @@ static void fbcon_redraw_blit(struct vc_
+
+ if (c == scr_readw(d)) {
+ if (s > start) {
+- ops->bmove(vc, info, line + ycount, x,
+- line, x, 1, s-start);
++ par->bmove(vc, info, line + ycount, x,
++ line, x, 1, s - start);
+ x += s - start + 1;
+ start = s + 1;
+ } else {
+@@ -1619,8 +1618,7 @@ static void fbcon_redraw_blit(struct vc_
+ d++;
+ } while (s < le);
+ if (s > start)
+- ops->bmove(vc, info, line + ycount, x, line, x, 1,
+- s-start);
++ par->bmove(vc, info, line + ycount, x, line, x, 1, s - start);
+ console_conditional_schedule();
+ if (ycount > 0)
+ line++;
+@@ -1691,7 +1689,7 @@ static void fbcon_bmove_rec(struct vc_da
+ int dy, int dx, int height, int width, u_int y_break)
+ {
+ struct fb_info *info = fbcon_info_from_console(vc->vc_num);
+- struct fbcon_ops *ops = info->fbcon_par;
++ struct fbcon_par *par = info->fbcon_par;
+ u_int b;
+
+ if (sy < y_break && sy + height > y_break) {
+@@ -1725,8 +1723,7 @@ static void fbcon_bmove_rec(struct vc_da
+ }
+ return;
+ }
+- ops->bmove(vc, info, real_y(p, sy), sx, real_y(p, dy), dx,
+- height, width);
++ par->bmove(vc, info, real_y(p, sy), sx, real_y(p, dy), dx, height, width);
+ }
+
+ static void fbcon_bmove(struct vc_data *vc, int sy, int sx, int dy, int dx,
+@@ -1953,15 +1950,13 @@ static void updatescrollmode_accel(struc
+ struct vc_data *vc)
+ {
+ #ifdef CONFIG_FRAMEBUFFER_CONSOLE_LEGACY_ACCELERATION
+- struct fbcon_ops *ops = info->fbcon_par;
++ struct fbcon_par *par = info->fbcon_par;
+ int cap = info->flags;
+ u16 t = 0;
+- int ypan = FBCON_SWAP(ops->rotate, info->fix.ypanstep,
+- info->fix.xpanstep);
+- int ywrap = FBCON_SWAP(ops->rotate, info->fix.ywrapstep, t);
+- int yres = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
+- int vyres = FBCON_SWAP(ops->rotate, info->var.yres_virtual,
+- info->var.xres_virtual);
++ int ypan = FBCON_SWAP(par->rotate, info->fix.ypanstep, info->fix.xpanstep);
++ int ywrap = FBCON_SWAP(par->rotate, info->fix.ywrapstep, t);
++ int yres = FBCON_SWAP(par->rotate, info->var.yres, info->var.xres);
++ int vyres = FBCON_SWAP(par->rotate, info->var.yres_virtual, info->var.xres_virtual);
+ int good_pan = (cap & FBINFO_HWACCEL_YPAN) &&
+ divides(ypan, vc->vc_font.height) && vyres > yres;
+ int good_wrap = (cap & FBINFO_HWACCEL_YWRAP) &&
+@@ -1994,11 +1989,10 @@ static void updatescrollmode(struct fbco
+ struct fb_info *info,
+ struct vc_data *vc)
+ {
+- struct fbcon_ops *ops = info->fbcon_par;
++ struct fbcon_par *par = info->fbcon_par;
+ int fh = vc->vc_font.height;
+- int yres = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
+- int vyres = FBCON_SWAP(ops->rotate, info->var.yres_virtual,
+- info->var.xres_virtual);
++ int yres = FBCON_SWAP(par->rotate, info->var.yres, info->var.xres);
++ int vyres = FBCON_SWAP(par->rotate, info->var.yres_virtual, info->var.xres_virtual);
+
+ p->vrows = vyres/fh;
+ if (yres > (fh * (vc->vc_rows + 1)))
+@@ -2017,7 +2011,7 @@ static int fbcon_resize(struct vc_data *
+ unsigned int height, unsigned int user)
+ {
+ struct fb_info *info = fbcon_info_from_console(vc->vc_num);
+- struct fbcon_ops *ops = info->fbcon_par;
++ struct fbcon_par *par = info->fbcon_par;
+ struct fbcon_display *p = &fb_display[vc->vc_num];
+ struct fb_var_screeninfo var = info->var;
+ int x_diff, y_diff, virt_w, virt_h, virt_fw, virt_fh;
+@@ -2040,12 +2034,10 @@ static int fbcon_resize(struct vc_data *
+ return -EINVAL;
+ }
+
+- virt_w = FBCON_SWAP(ops->rotate, width, height);
+- virt_h = FBCON_SWAP(ops->rotate, height, width);
+- virt_fw = FBCON_SWAP(ops->rotate, vc->vc_font.width,
+- vc->vc_font.height);
+- virt_fh = FBCON_SWAP(ops->rotate, vc->vc_font.height,
+- vc->vc_font.width);
++ virt_w = FBCON_SWAP(par->rotate, width, height);
++ virt_h = FBCON_SWAP(par->rotate, height, width);
++ virt_fw = FBCON_SWAP(par->rotate, vc->vc_font.width, vc->vc_font.height);
++ virt_fh = FBCON_SWAP(par->rotate, vc->vc_font.height, vc->vc_font.width);
+ var.xres = virt_w * virt_fw;
+ var.yres = virt_h * virt_fh;
+ x_diff = info->var.xres - var.xres;
+@@ -2071,7 +2063,7 @@ static int fbcon_resize(struct vc_data *
+ fb_set_var(info, &var);
+ }
+ var_to_display(p, &info->var, info);
+- ops->var = info->var;
++ par->var = info->var;
+ }
+ updatescrollmode(p, info, vc);
+ return 0;
+@@ -2080,13 +2072,13 @@ static int fbcon_resize(struct vc_data *
+ static bool fbcon_switch(struct vc_data *vc)
+ {
+ struct fb_info *info, *old_info = NULL;
+- struct fbcon_ops *ops;
++ struct fbcon_par *par;
+ struct fbcon_display *p = &fb_display[vc->vc_num];
+ struct fb_var_screeninfo var;
+ int i, ret, prev_console;
+
+ info = fbcon_info_from_console(vc->vc_num);
+- ops = info->fbcon_par;
++ par = info->fbcon_par;
+
+ if (logo_shown >= 0) {
+ struct vc_data *conp2 = vc_cons[logo_shown].d;
+@@ -2097,7 +2089,7 @@ static bool fbcon_switch(struct vc_data
+ logo_shown = FBCON_LOGO_CANSHOW;
+ }
+
+- prev_console = ops->currcon;
++ prev_console = par->currcon;
+ if (prev_console != -1)
+ old_info = fbcon_info_from_console(prev_console);
+ /*
+@@ -2110,9 +2102,9 @@ static bool fbcon_switch(struct vc_data
+ */
+ fbcon_for_each_registered_fb(i) {
+ if (fbcon_registered_fb[i]->fbcon_par) {
+- struct fbcon_ops *o = fbcon_registered_fb[i]->fbcon_par;
++ struct fbcon_par *par = fbcon_registered_fb[i]->fbcon_par;
+
+- o->currcon = vc->vc_num;
++ par->currcon = vc->vc_num;
+ }
+ }
+ memset(&var, 0, sizeof(struct fb_var_screeninfo));
+@@ -2126,7 +2118,7 @@ static bool fbcon_switch(struct vc_data
+ info->var.activate = var.activate;
+ var.vmode |= info->var.vmode & ~FB_VMODE_MASK;
+ fb_set_var(info, &var);
+- ops->var = info->var;
++ par->var = info->var;
+
+ if (old_info != NULL && (old_info != info ||
+ info->flags & FBINFO_MISC_ALWAYS_SETPAR)) {
+@@ -2144,16 +2136,16 @@ static bool fbcon_switch(struct vc_data
+ }
+
+ if (fbcon_is_inactive(vc, info) ||
+- ops->blank_state != FB_BLANK_UNBLANK)
++ par->blank_state != FB_BLANK_UNBLANK)
+ fbcon_del_cursor_work(info);
+ else
+ fbcon_add_cursor_work(info);
+
+ set_blitting_type(vc, info);
+- ops->cursor_reset = 1;
++ par->cursor_reset = 1;
+
+- if (ops->rotate_font && ops->rotate_font(info, vc)) {
+- ops->rotate = FB_ROTATE_UR;
++ if (par->rotate_font && par->rotate_font(info, vc)) {
++ par->rotate = FB_ROTATE_UR;
+ set_blitting_type(vc, info);
+ }
+
+@@ -2184,8 +2176,8 @@ static bool fbcon_switch(struct vc_data
+ scrollback_current = 0;
+
+ if (!fbcon_is_inactive(vc, info)) {
+- ops->var.xoffset = ops->var.yoffset = p->yscroll = 0;
+- ops->update_start(info);
++ par->var.xoffset = par->var.yoffset = p->yscroll = 0;
++ par->update_start(info);
+ }
+
+ fbcon_set_palette(vc, color_table);
+@@ -2194,7 +2186,7 @@ static bool fbcon_switch(struct vc_data
+ if (logo_shown == FBCON_LOGO_DRAW) {
+
+ logo_shown = fg_console;
+- fb_show_logo(info, ops->rotate);
++ fb_show_logo(info, par->rotate);
+ update_region(vc,
+ vc->vc_origin + vc->vc_size_row * vc->vc_top,
+ vc->vc_size_row * (vc->vc_bottom -
+@@ -2222,27 +2214,27 @@ static void fbcon_generic_blank(struct v
+ static int fbcon_blank(struct vc_data *vc, int blank, int mode_switch)
+ {
+ struct fb_info *info = fbcon_info_from_console(vc->vc_num);
+- struct fbcon_ops *ops = info->fbcon_par;
++ struct fbcon_par *par = info->fbcon_par;
+
+ if (mode_switch) {
+ struct fb_var_screeninfo var = info->var;
+
+- ops->graphics = 1;
++ par->graphics = 1;
+
+ if (!blank) {
+ var.activate = FB_ACTIVATE_NOW | FB_ACTIVATE_FORCE |
+ FB_ACTIVATE_KD_TEXT;
+ fb_set_var(info, &var);
+- ops->graphics = 0;
+- ops->var = info->var;
++ par->graphics = 0;
++ par->var = info->var;
+ }
+ }
+
+ if (!fbcon_is_inactive(vc, info)) {
+- if (ops->blank_state != blank) {
+- ops->blank_state = blank;
++ if (par->blank_state != blank) {
++ par->blank_state = blank;
+ fbcon_cursor(vc, blank ? CM_ERASE : CM_DRAW);
+- ops->cursor_flash = (!blank);
++ par->cursor_flash = (!blank);
+
+ if (fb_blank(info, blank))
+ fbcon_generic_blank(vc, info, blank);
+@@ -2253,7 +2245,7 @@ static int fbcon_blank(struct vc_data *v
+ }
+
+ if (mode_switch || fbcon_is_inactive(vc, info) ||
+- ops->blank_state != FB_BLANK_UNBLANK)
++ par->blank_state != FB_BLANK_UNBLANK)
+ fbcon_del_cursor_work(info);
+ else
+ fbcon_add_cursor_work(info);
+@@ -2264,10 +2256,10 @@ static int fbcon_blank(struct vc_data *v
+ static int fbcon_debug_enter(struct vc_data *vc)
+ {
+ struct fb_info *info = fbcon_info_from_console(vc->vc_num);
+- struct fbcon_ops *ops = info->fbcon_par;
++ struct fbcon_par *par = info->fbcon_par;
+
+- ops->save_graphics = ops->graphics;
+- ops->graphics = 0;
++ par->save_graphics = par->graphics;
++ par->graphics = 0;
+ if (info->fbops->fb_debug_enter)
+ info->fbops->fb_debug_enter(info);
+ fbcon_set_palette(vc, color_table);
+@@ -2277,9 +2269,9 @@ static int fbcon_debug_enter(struct vc_d
+ static int fbcon_debug_leave(struct vc_data *vc)
+ {
+ struct fb_info *info = fbcon_info_from_console(vc->vc_num);
+- struct fbcon_ops *ops = info->fbcon_par;
++ struct fbcon_par *par = info->fbcon_par;
+
+- ops->graphics = ops->save_graphics;
++ par->graphics = par->save_graphics;
+ if (info->fbops->fb_debug_leave)
+ info->fbops->fb_debug_leave(info);
+ return 0;
+@@ -2415,7 +2407,7 @@ static int fbcon_do_set_font(struct vc_d
+ const u8 * data, int userfont)
+ {
+ struct fb_info *info = fbcon_info_from_console(vc->vc_num);
+- struct fbcon_ops *ops = info->fbcon_par;
++ struct fbcon_par *par = info->fbcon_par;
+ struct fbcon_display *p = &fb_display[vc->vc_num];
+ int resize, ret, old_userfont, old_width, old_height, old_charcount;
+ u8 *old_data = vc->vc_font.data;
+@@ -2442,8 +2434,8 @@ static int fbcon_do_set_font(struct vc_d
+ if (resize) {
+ int cols, rows;
+
+- cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres);
+- rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
++ cols = FBCON_SWAP(par->rotate, info->var.xres, info->var.yres);
++ rows = FBCON_SWAP(par->rotate, info->var.yres, info->var.xres);
+ cols /= w;
+ rows /= h;
+ ret = vc_resize(vc, cols, rows);
+@@ -2676,11 +2668,11 @@ static void fbcon_invert_region(struct v
+ void fbcon_suspended(struct fb_info *info)
+ {
+ struct vc_data *vc = NULL;
+- struct fbcon_ops *ops = info->fbcon_par;
++ struct fbcon_par *par = info->fbcon_par;
+
+- if (!ops || ops->currcon < 0)
++ if (!par || par->currcon < 0)
+ return;
+- vc = vc_cons[ops->currcon].d;
++ vc = vc_cons[par->currcon].d;
+
+ /* Clear cursor, restore saved data */
+ fbcon_cursor(vc, CM_ERASE);
+@@ -2689,27 +2681,27 @@ void fbcon_suspended(struct fb_info *inf
+ void fbcon_resumed(struct fb_info *info)
+ {
+ struct vc_data *vc;
+- struct fbcon_ops *ops = info->fbcon_par;
++ struct fbcon_par *par = info->fbcon_par;
+
+- if (!ops || ops->currcon < 0)
++ if (!par || par->currcon < 0)
+ return;
+- vc = vc_cons[ops->currcon].d;
++ vc = vc_cons[par->currcon].d;
+
+ update_screen(vc);
+ }
+
+ static void fbcon_modechanged(struct fb_info *info)
+ {
+- struct fbcon_ops *ops = info->fbcon_par;
++ struct fbcon_par *par = info->fbcon_par;
+ struct vc_data *vc;
+ struct fbcon_display *p;
+ int rows, cols;
+
+- if (!ops || ops->currcon < 0)
++ if (!par || par->currcon < 0)
+ return;
+- vc = vc_cons[ops->currcon].d;
++ vc = vc_cons[par->currcon].d;
+ if (vc->vc_mode != KD_TEXT ||
+- fbcon_info_from_console(ops->currcon) != info)
++ fbcon_info_from_console(par->currcon) != info)
+ return;
+
+ p = &fb_display[vc->vc_num];
+@@ -2717,8 +2709,8 @@ static void fbcon_modechanged(struct fb_
+
+ if (con_is_visible(vc)) {
+ var_to_display(p, &info->var, info);
+- cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres);
+- rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
++ cols = FBCON_SWAP(par->rotate, info->var.xres, info->var.yres);
++ rows = FBCON_SWAP(par->rotate, info->var.yres, info->var.xres);
+ cols /= vc->vc_font.width;
+ rows /= vc->vc_font.height;
+ vc_resize(vc, cols, rows);
+@@ -2727,8 +2719,8 @@ static void fbcon_modechanged(struct fb_
+ scrollback_current = 0;
+
+ if (!fbcon_is_inactive(vc, info)) {
+- ops->var.xoffset = ops->var.yoffset = p->yscroll = 0;
+- ops->update_start(info);
++ par->var.xoffset = par->var.yoffset = p->yscroll = 0;
++ par->update_start(info);
+ }
+
+ fbcon_set_palette(vc, color_table);
+@@ -2738,12 +2730,12 @@ static void fbcon_modechanged(struct fb_
+
+ static void fbcon_set_all_vcs(struct fb_info *info)
+ {
+- struct fbcon_ops *ops = info->fbcon_par;
++ struct fbcon_par *par = info->fbcon_par;
+ struct vc_data *vc;
+ struct fbcon_display *p;
+ int i, rows, cols, fg = -1;
+
+- if (!ops || ops->currcon < 0)
++ if (!par || par->currcon < 0)
+ return;
+
+ for (i = first_fb_vc; i <= last_fb_vc; i++) {
+@@ -2760,8 +2752,8 @@ static void fbcon_set_all_vcs(struct fb_
+ p = &fb_display[vc->vc_num];
+ set_blitting_type(vc, info);
+ var_to_display(p, &info->var, info);
+- cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres);
+- rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
++ cols = FBCON_SWAP(par->rotate, info->var.xres, info->var.yres);
++ rows = FBCON_SWAP(par->rotate, info->var.yres, info->var.xres);
+ cols /= vc->vc_font.width;
+ rows /= vc->vc_font.height;
+ vc_resize(vc, cols, rows);
+@@ -2784,13 +2776,13 @@ EXPORT_SYMBOL(fbcon_update_vcs);
+ /* let fbcon check if it supports a new screen resolution */
+ int fbcon_modechange_possible(struct fb_info *info, struct fb_var_screeninfo *var)
+ {
+- struct fbcon_ops *ops = info->fbcon_par;
++ struct fbcon_par *par = info->fbcon_par;
+ struct vc_data *vc;
+ unsigned int i;
+
+ WARN_CONSOLE_UNLOCKED();
+
+- if (!ops)
++ if (!par)
+ return 0;
+
+ /* prevent setting a screen size which is smaller than font size */
+@@ -3088,15 +3080,14 @@ int fbcon_fb_registered(struct fb_info *
+
+ void fbcon_fb_blanked(struct fb_info *info, int blank)
+ {
+- struct fbcon_ops *ops = info->fbcon_par;
++ struct fbcon_par *par = info->fbcon_par;
+ struct vc_data *vc;
+
+- if (!ops || ops->currcon < 0)
++ if (!par || par->currcon < 0)
+ return;
+
+- vc = vc_cons[ops->currcon].d;
+- if (vc->vc_mode != KD_TEXT ||
+- fbcon_info_from_console(ops->currcon) != info)
++ vc = vc_cons[par->currcon].d;
++ if (vc->vc_mode != KD_TEXT || fbcon_info_from_console(par->currcon) != info)
+ return;
+
+ if (con_is_visible(vc)) {
+@@ -3105,7 +3096,7 @@ void fbcon_fb_blanked(struct fb_info *in
+ else
+ do_unblank_screen(0);
+ }
+- ops->blank_state = blank;
++ par->blank_state = blank;
+ }
+
+ void fbcon_new_modelist(struct fb_info *info)
+@@ -3296,7 +3287,7 @@ static ssize_t show_cursor_blink(struct
+ struct device_attribute *attr, char *buf)
+ {
+ struct fb_info *info;
+- struct fbcon_ops *ops;
++ struct fbcon_par *par;
+ int idx, blink = -1;
+
+ console_lock();
+@@ -3306,12 +3297,12 @@ static ssize_t show_cursor_blink(struct
+ goto err;
+
+ info = fbcon_registered_fb[idx];
+- ops = info->fbcon_par;
++ par = info->fbcon_par;
+
+- if (!ops)
++ if (!par)
+ goto err;
+
+- blink = delayed_work_pending(&ops->cursor_work);
++ blink = delayed_work_pending(&par->cursor_work);
+ err:
+ console_unlock();
+ return sysfs_emit(buf, "%d\n", blink);
+--- a/drivers/video/fbdev/core/fbcon.h
++++ b/drivers/video/fbdev/core/fbcon.h
+@@ -50,7 +50,7 @@ struct fbcon_display {
+ const struct fb_videomode *mode;
+ };
+
+-struct fbcon_ops {
++struct fbcon_par {
+ void (*bmove)(struct vc_data *vc, struct fb_info *info, int sy,
+ int sx, int dy, int dx, int height, int width);
+ void (*clear)(struct vc_data *vc, struct fb_info *info, int sy,
+@@ -185,7 +185,7 @@ static inline u_short fb_scrollmode(stru
+ #ifdef CONFIG_FB_TILEBLITTING
+ extern void fbcon_set_tileops(struct vc_data *vc, struct fb_info *info);
+ #endif
+-extern void fbcon_set_bitops(struct fbcon_ops *ops);
++extern void fbcon_set_bitops(struct fbcon_par *par);
+ extern int soft_cursor(struct fb_info *info, struct fb_cursor *cursor);
+
+ #define FBCON_ATTRIBUTE_UNDERLINE 1
+@@ -224,7 +224,7 @@ static inline int get_attribute(struct f
+ (i == FB_ROTATE_UR || i == FB_ROTATE_UD) ? _r : _v; })
+
+ #ifdef CONFIG_FRAMEBUFFER_CONSOLE_ROTATION
+-extern void fbcon_set_rotate(struct fbcon_ops *ops);
++extern void fbcon_set_rotate(struct fbcon_par *par);
+ #else
+ #define fbcon_set_rotate(x) do {} while(0)
+ #endif /* CONFIG_FRAMEBUFFER_CONSOLE_ROTATION */
+--- a/drivers/video/fbdev/core/fbcon_ccw.c
++++ b/drivers/video/fbdev/core/fbcon_ccw.c
+@@ -63,9 +63,9 @@ static void ccw_update_attr(u8 *dst, u8
+ static void ccw_bmove(struct vc_data *vc, struct fb_info *info, int sy,
+ int sx, int dy, int dx, int height, int width)
+ {
+- struct fbcon_ops *ops = info->fbcon_par;
++ struct fbcon_par *par = info->fbcon_par;
+ struct fb_copyarea area;
+- u32 vyres = GETVYRES(ops->p, info);
++ u32 vyres = GETVYRES(par->p, info);
+
+ area.sx = sy * vc->vc_font.height;
+ area.sy = vyres - ((sx + width) * vc->vc_font.width);
+@@ -80,9 +80,9 @@ static void ccw_bmove(struct vc_data *vc
+ static void ccw_clear(struct vc_data *vc, struct fb_info *info, int sy,
+ int sx, int height, int width, int fg, int bg)
+ {
+- struct fbcon_ops *ops = info->fbcon_par;
++ struct fbcon_par *par = info->fbcon_par;
+ struct fb_fillrect region;
+- u32 vyres = GETVYRES(ops->p, info);
++ u32 vyres = GETVYRES(par->p, info);
+
+ region.color = bg;
+ region.dx = sy * vc->vc_font.height;
+@@ -99,13 +99,13 @@ static inline void ccw_putcs_aligned(str
+ u32 d_pitch, u32 s_pitch, u32 cellsize,
+ struct fb_image *image, u8 *buf, u8 *dst)
+ {
+- struct fbcon_ops *ops = info->fbcon_par;
++ struct fbcon_par *par = info->fbcon_par;
+ u16 charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
+ u32 idx = (vc->vc_font.height + 7) >> 3;
+ u8 *src;
+
+ while (cnt--) {
+- src = ops->fontbuffer + (scr_readw(s--) & charmask)*cellsize;
++ src = par->fontbuffer + (scr_readw(s--) & charmask) * cellsize;
+
+ if (attr) {
+ ccw_update_attr(buf, src, attr, vc);
+@@ -130,7 +130,7 @@ static void ccw_putcs(struct vc_data *vc
+ int fg, int bg)
+ {
+ struct fb_image image;
+- struct fbcon_ops *ops = info->fbcon_par;
++ struct fbcon_par *par = info->fbcon_par;
+ u32 width = (vc->vc_font.height + 7)/8;
+ u32 cellsize = width * vc->vc_font.width;
+ u32 maxcnt = info->pixmap.size/cellsize;
+@@ -139,9 +139,9 @@ static void ccw_putcs(struct vc_data *vc
+ u32 cnt, pitch, size;
+ u32 attribute = get_attribute(info, scr_readw(s));
+ u8 *dst, *buf = NULL;
+- u32 vyres = GETVYRES(ops->p, info);
++ u32 vyres = GETVYRES(par->p, info);
+
+- if (!ops->fontbuffer)
++ if (!par->fontbuffer)
+ return;
+
+ image.fg_color = fg;
+@@ -221,28 +221,28 @@ static void ccw_cursor(struct vc_data *v
+ int fg, int bg)
+ {
+ struct fb_cursor cursor;
+- struct fbcon_ops *ops = info->fbcon_par;
++ struct fbcon_par *par = info->fbcon_par;
+ unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
+ int w = (vc->vc_font.height + 7) >> 3, c;
+- int y = real_y(ops->p, vc->state.y);
++ int y = real_y(par->p, vc->state.y);
+ int attribute, use_sw = vc->vc_cursor_type & CUR_SW;
+ int err = 1, dx, dy;
+ char *src;
+- u32 vyres = GETVYRES(ops->p, info);
++ u32 vyres = GETVYRES(par->p, info);
+
+- if (!ops->fontbuffer)
++ if (!par->fontbuffer)
+ return;
+
+ cursor.set = 0;
+
+ c = scr_readw((u16 *) vc->vc_pos);
+ attribute = get_attribute(info, c);
+- src = ops->fontbuffer + ((c & charmask) * (w * vc->vc_font.width));
++ src = par->fontbuffer + ((c & charmask) * (w * vc->vc_font.width));
+
+- if (ops->cursor_state.image.data != src ||
+- ops->cursor_reset) {
+- ops->cursor_state.image.data = src;
+- cursor.set |= FB_CUR_SETIMAGE;
++ if (par->cursor_state.image.data != src ||
++ par->cursor_reset) {
++ par->cursor_state.image.data = src;
++ cursor.set |= FB_CUR_SETIMAGE;
+ }
+
+ if (attribute) {
+@@ -251,49 +251,49 @@ static void ccw_cursor(struct vc_data *v
+ dst = kmalloc_array(w, vc->vc_font.width, GFP_ATOMIC);
+ if (!dst)
+ return;
+- kfree(ops->cursor_data);
+- ops->cursor_data = dst;
++ kfree(par->cursor_data);
++ par->cursor_data = dst;
+ ccw_update_attr(dst, src, attribute, vc);
+ src = dst;
+ }
+
+- if (ops->cursor_state.image.fg_color != fg ||
+- ops->cursor_state.image.bg_color != bg ||
+- ops->cursor_reset) {
+- ops->cursor_state.image.fg_color = fg;
+- ops->cursor_state.image.bg_color = bg;
++ if (par->cursor_state.image.fg_color != fg ||
++ par->cursor_state.image.bg_color != bg ||
++ par->cursor_reset) {
++ par->cursor_state.image.fg_color = fg;
++ par->cursor_state.image.bg_color = bg;
+ cursor.set |= FB_CUR_SETCMAP;
+ }
+
+- if (ops->cursor_state.image.height != vc->vc_font.width ||
+- ops->cursor_state.image.width != vc->vc_font.height ||
+- ops->cursor_reset) {
+- ops->cursor_state.image.height = vc->vc_font.width;
+- ops->cursor_state.image.width = vc->vc_font.height;
++ if (par->cursor_state.image.height != vc->vc_font.width ||
++ par->cursor_state.image.width != vc->vc_font.height ||
++ par->cursor_reset) {
++ par->cursor_state.image.height = vc->vc_font.width;
++ par->cursor_state.image.width = vc->vc_font.height;
+ cursor.set |= FB_CUR_SETSIZE;
+ }
+
+ dx = y * vc->vc_font.height;
+ dy = vyres - ((vc->state.x + 1) * vc->vc_font.width);
+
+- if (ops->cursor_state.image.dx != dx ||
+- ops->cursor_state.image.dy != dy ||
+- ops->cursor_reset) {
+- ops->cursor_state.image.dx = dx;
+- ops->cursor_state.image.dy = dy;
++ if (par->cursor_state.image.dx != dx ||
++ par->cursor_state.image.dy != dy ||
++ par->cursor_reset) {
++ par->cursor_state.image.dx = dx;
++ par->cursor_state.image.dy = dy;
+ cursor.set |= FB_CUR_SETPOS;
+ }
+
+- if (ops->cursor_state.hot.x || ops->cursor_state.hot.y ||
+- ops->cursor_reset) {
+- ops->cursor_state.hot.x = cursor.hot.y = 0;
++ if (par->cursor_state.hot.x || par->cursor_state.hot.y ||
++ par->cursor_reset) {
++ par->cursor_state.hot.x = cursor.hot.y = 0;
+ cursor.set |= FB_CUR_SETHOT;
+ }
+
+ if (cursor.set & FB_CUR_SETSIZE ||
+- vc->vc_cursor_type != ops->p->cursor_shape ||
+- ops->cursor_state.mask == NULL ||
+- ops->cursor_reset) {
++ vc->vc_cursor_type != par->p->cursor_shape ||
++ par->cursor_state.mask == NULL ||
++ par->cursor_reset) {
+ char *tmp, *mask = kmalloc_array(w, vc->vc_font.width,
+ GFP_ATOMIC);
+ int cur_height, size, i = 0;
+@@ -309,13 +309,13 @@ static void ccw_cursor(struct vc_data *v
+ return;
+ }
+
+- kfree(ops->cursor_state.mask);
+- ops->cursor_state.mask = mask;
++ kfree(par->cursor_state.mask);
++ par->cursor_state.mask = mask;
+
+- ops->p->cursor_shape = vc->vc_cursor_type;
++ par->p->cursor_shape = vc->vc_cursor_type;
+ cursor.set |= FB_CUR_SETSHAPE;
+
+- switch (CUR_SIZE(ops->p->cursor_shape)) {
++ switch (CUR_SIZE(par->p->cursor_shape)) {
+ case CUR_NONE:
+ cur_height = 0;
+ break;
+@@ -350,26 +350,26 @@ static void ccw_cursor(struct vc_data *v
+
+ switch (mode) {
+ case CM_ERASE:
+- ops->cursor_state.enable = 0;
++ par->cursor_state.enable = 0;
+ break;
+ case CM_DRAW:
+ case CM_MOVE:
+ default:
+- ops->cursor_state.enable = (use_sw) ? 0 : 1;
++ par->cursor_state.enable = (use_sw) ? 0 : 1;
+ break;
+ }
+
+ cursor.image.data = src;
+- cursor.image.fg_color = ops->cursor_state.image.fg_color;
+- cursor.image.bg_color = ops->cursor_state.image.bg_color;
+- cursor.image.dx = ops->cursor_state.image.dx;
+- cursor.image.dy = ops->cursor_state.image.dy;
+- cursor.image.height = ops->cursor_state.image.height;
+- cursor.image.width = ops->cursor_state.image.width;
+- cursor.hot.x = ops->cursor_state.hot.x;
+- cursor.hot.y = ops->cursor_state.hot.y;
+- cursor.mask = ops->cursor_state.mask;
+- cursor.enable = ops->cursor_state.enable;
++ cursor.image.fg_color = par->cursor_state.image.fg_color;
++ cursor.image.bg_color = par->cursor_state.image.bg_color;
++ cursor.image.dx = par->cursor_state.image.dx;
++ cursor.image.dy = par->cursor_state.image.dy;
++ cursor.image.height = par->cursor_state.image.height;
++ cursor.image.width = par->cursor_state.image.width;
++ cursor.hot.x = par->cursor_state.hot.x;
++ cursor.hot.y = par->cursor_state.hot.y;
++ cursor.mask = par->cursor_state.mask;
++ cursor.enable = par->cursor_state.enable;
+ cursor.image.depth = 1;
+ cursor.rop = ROP_XOR;
+
+@@ -379,32 +379,32 @@ static void ccw_cursor(struct vc_data *v
+ if (err)
+ soft_cursor(info, &cursor);
+
+- ops->cursor_reset = 0;
++ par->cursor_reset = 0;
+ }
+
+ static int ccw_update_start(struct fb_info *info)
+ {
+- struct fbcon_ops *ops = info->fbcon_par;
++ struct fbcon_par *par = info->fbcon_par;
+ u32 yoffset;
+- u32 vyres = GETVYRES(ops->p, info);
++ u32 vyres = GETVYRES(par->p, info);
+ int err;
+
+- yoffset = (vyres - info->var.yres) - ops->var.xoffset;
+- ops->var.xoffset = ops->var.yoffset;
+- ops->var.yoffset = yoffset;
+- err = fb_pan_display(info, &ops->var);
+- ops->var.xoffset = info->var.xoffset;
+- ops->var.yoffset = info->var.yoffset;
+- ops->var.vmode = info->var.vmode;
++ yoffset = (vyres - info->var.yres) - par->var.xoffset;
++ par->var.xoffset = par->var.yoffset;
++ par->var.yoffset = yoffset;
++ err = fb_pan_display(info, &par->var);
++ par->var.xoffset = info->var.xoffset;
++ par->var.yoffset = info->var.yoffset;
++ par->var.vmode = info->var.vmode;
+ return err;
+ }
+
+-void fbcon_rotate_ccw(struct fbcon_ops *ops)
++void fbcon_rotate_ccw(struct fbcon_par *par)
+ {
+- ops->bmove = ccw_bmove;
+- ops->clear = ccw_clear;
+- ops->putcs = ccw_putcs;
+- ops->clear_margins = ccw_clear_margins;
+- ops->cursor = ccw_cursor;
+- ops->update_start = ccw_update_start;
++ par->bmove = ccw_bmove;
++ par->clear = ccw_clear;
++ par->putcs = ccw_putcs;
++ par->clear_margins = ccw_clear_margins;
++ par->cursor = ccw_cursor;
++ par->update_start = ccw_update_start;
+ }
+--- a/drivers/video/fbdev/core/fbcon_cw.c
++++ b/drivers/video/fbdev/core/fbcon_cw.c
+@@ -48,9 +48,9 @@ static void cw_update_attr(u8 *dst, u8 *
+ static void cw_bmove(struct vc_data *vc, struct fb_info *info, int sy,
+ int sx, int dy, int dx, int height, int width)
+ {
+- struct fbcon_ops *ops = info->fbcon_par;
++ struct fbcon_par *par = info->fbcon_par;
+ struct fb_copyarea area;
+- u32 vxres = GETVXRES(ops->p, info);
++ u32 vxres = GETVXRES(par->p, info);
+
+ area.sx = vxres - ((sy + height) * vc->vc_font.height);
+ area.sy = sx * vc->vc_font.width;
+@@ -65,9 +65,9 @@ static void cw_bmove(struct vc_data *vc,
+ static void cw_clear(struct vc_data *vc, struct fb_info *info, int sy,
+ int sx, int height, int width, int fg, int bg)
+ {
+- struct fbcon_ops *ops = info->fbcon_par;
++ struct fbcon_par *par = info->fbcon_par;
+ struct fb_fillrect region;
+- u32 vxres = GETVXRES(ops->p, info);
++ u32 vxres = GETVXRES(par->p, info);
+
+ region.color = bg;
+ region.dx = vxres - ((sy + height) * vc->vc_font.height);
+@@ -84,13 +84,13 @@ static inline void cw_putcs_aligned(stru
+ u32 d_pitch, u32 s_pitch, u32 cellsize,
+ struct fb_image *image, u8 *buf, u8 *dst)
+ {
+- struct fbcon_ops *ops = info->fbcon_par;
++ struct fbcon_par *par = info->fbcon_par;
+ u16 charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
+ u32 idx = (vc->vc_font.height + 7) >> 3;
+ u8 *src;
+
+ while (cnt--) {
+- src = ops->fontbuffer + (scr_readw(s++) & charmask)*cellsize;
++ src = par->fontbuffer + (scr_readw(s++) & charmask) * cellsize;
+
+ if (attr) {
+ cw_update_attr(buf, src, attr, vc);
+@@ -115,7 +115,7 @@ static void cw_putcs(struct vc_data *vc,
+ int fg, int bg)
+ {
+ struct fb_image image;
+- struct fbcon_ops *ops = info->fbcon_par;
++ struct fbcon_par *par = info->fbcon_par;
+ u32 width = (vc->vc_font.height + 7)/8;
+ u32 cellsize = width * vc->vc_font.width;
+ u32 maxcnt = info->pixmap.size/cellsize;
+@@ -124,9 +124,9 @@ static void cw_putcs(struct vc_data *vc,
+ u32 cnt, pitch, size;
+ u32 attribute = get_attribute(info, scr_readw(s));
+ u8 *dst, *buf = NULL;
+- u32 vxres = GETVXRES(ops->p, info);
++ u32 vxres = GETVXRES(par->p, info);
+
+- if (!ops->fontbuffer)
++ if (!par->fontbuffer)
+ return;
+
+ image.fg_color = fg;
+@@ -204,28 +204,28 @@ static void cw_cursor(struct vc_data *vc
+ int fg, int bg)
+ {
+ struct fb_cursor cursor;
+- struct fbcon_ops *ops = info->fbcon_par;
++ struct fbcon_par *par = info->fbcon_par;
+ unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
+ int w = (vc->vc_font.height + 7) >> 3, c;
+- int y = real_y(ops->p, vc->state.y);
++ int y = real_y(par->p, vc->state.y);
+ int attribute, use_sw = vc->vc_cursor_type & CUR_SW;
+ int err = 1, dx, dy;
+ char *src;
+- u32 vxres = GETVXRES(ops->p, info);
++ u32 vxres = GETVXRES(par->p, info);
+
+- if (!ops->fontbuffer)
++ if (!par->fontbuffer)
+ return;
+
+ cursor.set = 0;
+
+ c = scr_readw((u16 *) vc->vc_pos);
+ attribute = get_attribute(info, c);
+- src = ops->fontbuffer + ((c & charmask) * (w * vc->vc_font.width));
++ src = par->fontbuffer + ((c & charmask) * (w * vc->vc_font.width));
+
+- if (ops->cursor_state.image.data != src ||
+- ops->cursor_reset) {
+- ops->cursor_state.image.data = src;
+- cursor.set |= FB_CUR_SETIMAGE;
++ if (par->cursor_state.image.data != src ||
++ par->cursor_reset) {
++ par->cursor_state.image.data = src;
++ cursor.set |= FB_CUR_SETIMAGE;
+ }
+
+ if (attribute) {
+@@ -234,49 +234,49 @@ static void cw_cursor(struct vc_data *vc
+ dst = kmalloc_array(w, vc->vc_font.width, GFP_ATOMIC);
+ if (!dst)
+ return;
+- kfree(ops->cursor_data);
+- ops->cursor_data = dst;
++ kfree(par->cursor_data);
++ par->cursor_data = dst;
+ cw_update_attr(dst, src, attribute, vc);
+ src = dst;
+ }
+
+- if (ops->cursor_state.image.fg_color != fg ||
+- ops->cursor_state.image.bg_color != bg ||
+- ops->cursor_reset) {
+- ops->cursor_state.image.fg_color = fg;
+- ops->cursor_state.image.bg_color = bg;
++ if (par->cursor_state.image.fg_color != fg ||
++ par->cursor_state.image.bg_color != bg ||
++ par->cursor_reset) {
++ par->cursor_state.image.fg_color = fg;
++ par->cursor_state.image.bg_color = bg;
+ cursor.set |= FB_CUR_SETCMAP;
+ }
+
+- if (ops->cursor_state.image.height != vc->vc_font.width ||
+- ops->cursor_state.image.width != vc->vc_font.height ||
+- ops->cursor_reset) {
+- ops->cursor_state.image.height = vc->vc_font.width;
+- ops->cursor_state.image.width = vc->vc_font.height;
++ if (par->cursor_state.image.height != vc->vc_font.width ||
++ par->cursor_state.image.width != vc->vc_font.height ||
++ par->cursor_reset) {
++ par->cursor_state.image.height = vc->vc_font.width;
++ par->cursor_state.image.width = vc->vc_font.height;
+ cursor.set |= FB_CUR_SETSIZE;
+ }
+
+ dx = vxres - ((y * vc->vc_font.height) + vc->vc_font.height);
+ dy = vc->state.x * vc->vc_font.width;
+
+- if (ops->cursor_state.image.dx != dx ||
+- ops->cursor_state.image.dy != dy ||
+- ops->cursor_reset) {
+- ops->cursor_state.image.dx = dx;
+- ops->cursor_state.image.dy = dy;
++ if (par->cursor_state.image.dx != dx ||
++ par->cursor_state.image.dy != dy ||
++ par->cursor_reset) {
++ par->cursor_state.image.dx = dx;
++ par->cursor_state.image.dy = dy;
+ cursor.set |= FB_CUR_SETPOS;
+ }
+
+- if (ops->cursor_state.hot.x || ops->cursor_state.hot.y ||
+- ops->cursor_reset) {
+- ops->cursor_state.hot.x = cursor.hot.y = 0;
++ if (par->cursor_state.hot.x || par->cursor_state.hot.y ||
++ par->cursor_reset) {
++ par->cursor_state.hot.x = cursor.hot.y = 0;
+ cursor.set |= FB_CUR_SETHOT;
+ }
+
+ if (cursor.set & FB_CUR_SETSIZE ||
+- vc->vc_cursor_type != ops->p->cursor_shape ||
+- ops->cursor_state.mask == NULL ||
+- ops->cursor_reset) {
++ vc->vc_cursor_type != par->p->cursor_shape ||
++ par->cursor_state.mask == NULL ||
++ par->cursor_reset) {
+ char *tmp, *mask = kmalloc_array(w, vc->vc_font.width,
+ GFP_ATOMIC);
+ int cur_height, size, i = 0;
+@@ -292,13 +292,13 @@ static void cw_cursor(struct vc_data *vc
+ return;
+ }
+
+- kfree(ops->cursor_state.mask);
+- ops->cursor_state.mask = mask;
++ kfree(par->cursor_state.mask);
++ par->cursor_state.mask = mask;
+
+- ops->p->cursor_shape = vc->vc_cursor_type;
++ par->p->cursor_shape = vc->vc_cursor_type;
+ cursor.set |= FB_CUR_SETSHAPE;
+
+- switch (CUR_SIZE(ops->p->cursor_shape)) {
++ switch (CUR_SIZE(par->p->cursor_shape)) {
+ case CUR_NONE:
+ cur_height = 0;
+ break;
+@@ -333,26 +333,26 @@ static void cw_cursor(struct vc_data *vc
+
+ switch (mode) {
+ case CM_ERASE:
+- ops->cursor_state.enable = 0;
++ par->cursor_state.enable = 0;
+ break;
+ case CM_DRAW:
+ case CM_MOVE:
+ default:
+- ops->cursor_state.enable = (use_sw) ? 0 : 1;
++ par->cursor_state.enable = (use_sw) ? 0 : 1;
+ break;
+ }
+
+ cursor.image.data = src;
+- cursor.image.fg_color = ops->cursor_state.image.fg_color;
+- cursor.image.bg_color = ops->cursor_state.image.bg_color;
+- cursor.image.dx = ops->cursor_state.image.dx;
+- cursor.image.dy = ops->cursor_state.image.dy;
+- cursor.image.height = ops->cursor_state.image.height;
+- cursor.image.width = ops->cursor_state.image.width;
+- cursor.hot.x = ops->cursor_state.hot.x;
+- cursor.hot.y = ops->cursor_state.hot.y;
+- cursor.mask = ops->cursor_state.mask;
+- cursor.enable = ops->cursor_state.enable;
++ cursor.image.fg_color = par->cursor_state.image.fg_color;
++ cursor.image.bg_color = par->cursor_state.image.bg_color;
++ cursor.image.dx = par->cursor_state.image.dx;
++ cursor.image.dy = par->cursor_state.image.dy;
++ cursor.image.height = par->cursor_state.image.height;
++ cursor.image.width = par->cursor_state.image.width;
++ cursor.hot.x = par->cursor_state.hot.x;
++ cursor.hot.y = par->cursor_state.hot.y;
++ cursor.mask = par->cursor_state.mask;
++ cursor.enable = par->cursor_state.enable;
+ cursor.image.depth = 1;
+ cursor.rop = ROP_XOR;
+
+@@ -362,32 +362,32 @@ static void cw_cursor(struct vc_data *vc
+ if (err)
+ soft_cursor(info, &cursor);
+
+- ops->cursor_reset = 0;
++ par->cursor_reset = 0;
+ }
+
+ static int cw_update_start(struct fb_info *info)
+ {
+- struct fbcon_ops *ops = info->fbcon_par;
+- u32 vxres = GETVXRES(ops->p, info);
++ struct fbcon_par *par = info->fbcon_par;
++ u32 vxres = GETVXRES(par->p, info);
+ u32 xoffset;
+ int err;
+
+- xoffset = vxres - (info->var.xres + ops->var.yoffset);
+- ops->var.yoffset = ops->var.xoffset;
+- ops->var.xoffset = xoffset;
+- err = fb_pan_display(info, &ops->var);
+- ops->var.xoffset = info->var.xoffset;
+- ops->var.yoffset = info->var.yoffset;
+- ops->var.vmode = info->var.vmode;
++ xoffset = vxres - (info->var.xres + par->var.yoffset);
++ par->var.yoffset = par->var.xoffset;
++ par->var.xoffset = xoffset;
++ err = fb_pan_display(info, &par->var);
++ par->var.xoffset = info->var.xoffset;
++ par->var.yoffset = info->var.yoffset;
++ par->var.vmode = info->var.vmode;
+ return err;
+ }
+
+-void fbcon_rotate_cw(struct fbcon_ops *ops)
++void fbcon_rotate_cw(struct fbcon_par *par)
+ {
+- ops->bmove = cw_bmove;
+- ops->clear = cw_clear;
+- ops->putcs = cw_putcs;
+- ops->clear_margins = cw_clear_margins;
+- ops->cursor = cw_cursor;
+- ops->update_start = cw_update_start;
++ par->bmove = cw_bmove;
++ par->clear = cw_clear;
++ par->putcs = cw_putcs;
++ par->clear_margins = cw_clear_margins;
++ par->cursor = cw_cursor;
++ par->update_start = cw_update_start;
+ }
+--- a/drivers/video/fbdev/core/fbcon_rotate.c
++++ b/drivers/video/fbdev/core/fbcon_rotate.c
+@@ -20,35 +20,35 @@
+
+ static int fbcon_rotate_font(struct fb_info *info, struct vc_data *vc)
+ {
+- struct fbcon_ops *ops = info->fbcon_par;
++ struct fbcon_par *par = info->fbcon_par;
+ int len, err = 0;
+ int s_cellsize, d_cellsize, i;
+ const u8 *src;
+ u8 *dst;
+
+- if (vc->vc_font.data == ops->fontdata &&
+- ops->p->con_rotate == ops->cur_rotate)
++ if (vc->vc_font.data == par->fontdata &&
++ par->p->con_rotate == par->cur_rotate)
+ goto finished;
+
+- src = ops->fontdata = vc->vc_font.data;
+- ops->cur_rotate = ops->p->con_rotate;
++ src = par->fontdata = vc->vc_font.data;
++ par->cur_rotate = par->p->con_rotate;
+ len = vc->vc_font.charcount;
+ s_cellsize = ((vc->vc_font.width + 7)/8) *
+ vc->vc_font.height;
+ d_cellsize = s_cellsize;
+
+- if (ops->rotate == FB_ROTATE_CW ||
+- ops->rotate == FB_ROTATE_CCW)
++ if (par->rotate == FB_ROTATE_CW ||
++ par->rotate == FB_ROTATE_CCW)
+ d_cellsize = ((vc->vc_font.height + 7)/8) *
+ vc->vc_font.width;
+
+ if (info->fbops->fb_sync)
+ info->fbops->fb_sync(info);
+
+- if (ops->fd_size < d_cellsize * len) {
+- kfree(ops->fontbuffer);
+- ops->fontbuffer = NULL;
+- ops->fd_size = 0;
++ if (par->fd_size < d_cellsize * len) {
++ kfree(par->fontbuffer);
++ par->fontbuffer = NULL;
++ par->fd_size = 0;
+
+ dst = kmalloc_array(len, d_cellsize, GFP_KERNEL);
+
+@@ -57,14 +57,14 @@ static int fbcon_rotate_font(struct fb_i
+ goto finished;
+ }
+
+- ops->fd_size = d_cellsize * len;
+- ops->fontbuffer = dst;
++ par->fd_size = d_cellsize * len;
++ par->fontbuffer = dst;
+ }
+
+- dst = ops->fontbuffer;
+- memset(dst, 0, ops->fd_size);
++ dst = par->fontbuffer;
++ memset(dst, 0, par->fd_size);
+
+- switch (ops->rotate) {
++ switch (par->rotate) {
+ case FB_ROTATE_UD:
+ for (i = len; i--; ) {
+ rotate_ud(src, dst, vc->vc_font.width,
+@@ -96,19 +96,19 @@ finished:
+ return err;
+ }
+
+-void fbcon_set_rotate(struct fbcon_ops *ops)
++void fbcon_set_rotate(struct fbcon_par *par)
+ {
+- ops->rotate_font = fbcon_rotate_font;
++ par->rotate_font = fbcon_rotate_font;
+
+- switch(ops->rotate) {
++ switch (par->rotate) {
+ case FB_ROTATE_CW:
+- fbcon_rotate_cw(ops);
++ fbcon_rotate_cw(par);
+ break;
+ case FB_ROTATE_UD:
+- fbcon_rotate_ud(ops);
++ fbcon_rotate_ud(par);
+ break;
+ case FB_ROTATE_CCW:
+- fbcon_rotate_ccw(ops);
++ fbcon_rotate_ccw(par);
+ break;
+ }
+ }
+--- a/drivers/video/fbdev/core/fbcon_rotate.h
++++ b/drivers/video/fbdev/core/fbcon_rotate.h
+@@ -90,7 +90,7 @@ static inline void rotate_ccw(const char
+ }
+ }
+
+-extern void fbcon_rotate_cw(struct fbcon_ops *ops);
+-extern void fbcon_rotate_ud(struct fbcon_ops *ops);
+-extern void fbcon_rotate_ccw(struct fbcon_ops *ops);
++extern void fbcon_rotate_cw(struct fbcon_par *par);
++extern void fbcon_rotate_ud(struct fbcon_par *par);
++extern void fbcon_rotate_ccw(struct fbcon_par *par);
+ #endif
+--- a/drivers/video/fbdev/core/fbcon_ud.c
++++ b/drivers/video/fbdev/core/fbcon_ud.c
+@@ -48,10 +48,10 @@ static void ud_update_attr(u8 *dst, u8 *
+ static void ud_bmove(struct vc_data *vc, struct fb_info *info, int sy,
+ int sx, int dy, int dx, int height, int width)
+ {
+- struct fbcon_ops *ops = info->fbcon_par;
++ struct fbcon_par *par = info->fbcon_par;
+ struct fb_copyarea area;
+- u32 vyres = GETVYRES(ops->p, info);
+- u32 vxres = GETVXRES(ops->p, info);
++ u32 vyres = GETVYRES(par->p, info);
++ u32 vxres = GETVXRES(par->p, info);
+
+ area.sy = vyres - ((sy + height) * vc->vc_font.height);
+ area.sx = vxres - ((sx + width) * vc->vc_font.width);
+@@ -66,10 +66,10 @@ static void ud_bmove(struct vc_data *vc,
+ static void ud_clear(struct vc_data *vc, struct fb_info *info, int sy,
+ int sx, int height, int width, int fg, int bg)
+ {
+- struct fbcon_ops *ops = info->fbcon_par;
++ struct fbcon_par *par = info->fbcon_par;
+ struct fb_fillrect region;
+- u32 vyres = GETVYRES(ops->p, info);
+- u32 vxres = GETVXRES(ops->p, info);
++ u32 vyres = GETVYRES(par->p, info);
++ u32 vxres = GETVXRES(par->p, info);
+
+ region.color = bg;
+ region.dy = vyres - ((sy + height) * vc->vc_font.height);
+@@ -86,13 +86,13 @@ static inline void ud_putcs_aligned(stru
+ u32 d_pitch, u32 s_pitch, u32 cellsize,
+ struct fb_image *image, u8 *buf, u8 *dst)
+ {
+- struct fbcon_ops *ops = info->fbcon_par;
++ struct fbcon_par *par = info->fbcon_par;
+ u16 charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
+ u32 idx = vc->vc_font.width >> 3;
+ u8 *src;
+
+ while (cnt--) {
+- src = ops->fontbuffer + (scr_readw(s--) & charmask)*cellsize;
++ src = par->fontbuffer + (scr_readw(s--) & charmask) * cellsize;
+
+ if (attr) {
+ ud_update_attr(buf, src, attr, vc);
+@@ -119,7 +119,7 @@ static inline void ud_putcs_unaligned(st
+ struct fb_image *image, u8 *buf,
+ u8 *dst)
+ {
+- struct fbcon_ops *ops = info->fbcon_par;
++ struct fbcon_par *par = info->fbcon_par;
+ u16 charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
+ u32 shift_low = 0, mod = vc->vc_font.width % 8;
+ u32 shift_high = 8;
+@@ -127,7 +127,7 @@ static inline void ud_putcs_unaligned(st
+ u8 *src;
+
+ while (cnt--) {
+- src = ops->fontbuffer + (scr_readw(s--) & charmask)*cellsize;
++ src = par->fontbuffer + (scr_readw(s--) & charmask) * cellsize;
+
+ if (attr) {
+ ud_update_attr(buf, src, attr, vc);
+@@ -152,7 +152,7 @@ static void ud_putcs(struct vc_data *vc,
+ int fg, int bg)
+ {
+ struct fb_image image;
+- struct fbcon_ops *ops = info->fbcon_par;
++ struct fbcon_par *par = info->fbcon_par;
+ u32 width = (vc->vc_font.width + 7)/8;
+ u32 cellsize = width * vc->vc_font.height;
+ u32 maxcnt = info->pixmap.size/cellsize;
+@@ -161,10 +161,10 @@ static void ud_putcs(struct vc_data *vc,
+ u32 mod = vc->vc_font.width % 8, cnt, pitch, size;
+ u32 attribute = get_attribute(info, scr_readw(s));
+ u8 *dst, *buf = NULL;
+- u32 vyres = GETVYRES(ops->p, info);
+- u32 vxres = GETVXRES(ops->p, info);
++ u32 vyres = GETVYRES(par->p, info);
++ u32 vxres = GETVXRES(par->p, info);
+
+- if (!ops->fontbuffer)
++ if (!par->fontbuffer)
+ return;
+
+ image.fg_color = fg;
+@@ -251,29 +251,29 @@ static void ud_cursor(struct vc_data *vc
+ int fg, int bg)
+ {
+ struct fb_cursor cursor;
+- struct fbcon_ops *ops = info->fbcon_par;
++ struct fbcon_par *par = info->fbcon_par;
+ unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
+ int w = (vc->vc_font.width + 7) >> 3, c;
+- int y = real_y(ops->p, vc->state.y);
++ int y = real_y(par->p, vc->state.y);
+ int attribute, use_sw = vc->vc_cursor_type & CUR_SW;
+ int err = 1, dx, dy;
+ char *src;
+- u32 vyres = GETVYRES(ops->p, info);
+- u32 vxres = GETVXRES(ops->p, info);
++ u32 vyres = GETVYRES(par->p, info);
++ u32 vxres = GETVXRES(par->p, info);
+
+- if (!ops->fontbuffer)
++ if (!par->fontbuffer)
+ return;
+
+ cursor.set = 0;
+
+ c = scr_readw((u16 *) vc->vc_pos);
+ attribute = get_attribute(info, c);
+- src = ops->fontbuffer + ((c & charmask) * (w * vc->vc_font.height));
++ src = par->fontbuffer + ((c & charmask) * (w * vc->vc_font.height));
+
+- if (ops->cursor_state.image.data != src ||
+- ops->cursor_reset) {
+- ops->cursor_state.image.data = src;
+- cursor.set |= FB_CUR_SETIMAGE;
++ if (par->cursor_state.image.data != src ||
++ par->cursor_reset) {
++ par->cursor_state.image.data = src;
++ cursor.set |= FB_CUR_SETIMAGE;
+ }
+
+ if (attribute) {
+@@ -282,49 +282,49 @@ static void ud_cursor(struct vc_data *vc
+ dst = kmalloc_array(w, vc->vc_font.height, GFP_ATOMIC);
+ if (!dst)
+ return;
+- kfree(ops->cursor_data);
+- ops->cursor_data = dst;
++ kfree(par->cursor_data);
++ par->cursor_data = dst;
+ ud_update_attr(dst, src, attribute, vc);
+ src = dst;
+ }
+
+- if (ops->cursor_state.image.fg_color != fg ||
+- ops->cursor_state.image.bg_color != bg ||
+- ops->cursor_reset) {
+- ops->cursor_state.image.fg_color = fg;
+- ops->cursor_state.image.bg_color = bg;
++ if (par->cursor_state.image.fg_color != fg ||
++ par->cursor_state.image.bg_color != bg ||
++ par->cursor_reset) {
++ par->cursor_state.image.fg_color = fg;
++ par->cursor_state.image.bg_color = bg;
+ cursor.set |= FB_CUR_SETCMAP;
+ }
+
+- if (ops->cursor_state.image.height != vc->vc_font.height ||
+- ops->cursor_state.image.width != vc->vc_font.width ||
+- ops->cursor_reset) {
+- ops->cursor_state.image.height = vc->vc_font.height;
+- ops->cursor_state.image.width = vc->vc_font.width;
++ if (par->cursor_state.image.height != vc->vc_font.height ||
++ par->cursor_state.image.width != vc->vc_font.width ||
++ par->cursor_reset) {
++ par->cursor_state.image.height = vc->vc_font.height;
++ par->cursor_state.image.width = vc->vc_font.width;
+ cursor.set |= FB_CUR_SETSIZE;
+ }
+
+ dy = vyres - ((y * vc->vc_font.height) + vc->vc_font.height);
+ dx = vxres - ((vc->state.x * vc->vc_font.width) + vc->vc_font.width);
+
+- if (ops->cursor_state.image.dx != dx ||
+- ops->cursor_state.image.dy != dy ||
+- ops->cursor_reset) {
+- ops->cursor_state.image.dx = dx;
+- ops->cursor_state.image.dy = dy;
++ if (par->cursor_state.image.dx != dx ||
++ par->cursor_state.image.dy != dy ||
++ par->cursor_reset) {
++ par->cursor_state.image.dx = dx;
++ par->cursor_state.image.dy = dy;
+ cursor.set |= FB_CUR_SETPOS;
+ }
+
+- if (ops->cursor_state.hot.x || ops->cursor_state.hot.y ||
+- ops->cursor_reset) {
+- ops->cursor_state.hot.x = cursor.hot.y = 0;
++ if (par->cursor_state.hot.x || par->cursor_state.hot.y ||
++ par->cursor_reset) {
++ par->cursor_state.hot.x = cursor.hot.y = 0;
+ cursor.set |= FB_CUR_SETHOT;
+ }
+
+ if (cursor.set & FB_CUR_SETSIZE ||
+- vc->vc_cursor_type != ops->p->cursor_shape ||
+- ops->cursor_state.mask == NULL ||
+- ops->cursor_reset) {
++ vc->vc_cursor_type != par->p->cursor_shape ||
++ par->cursor_state.mask == NULL ||
++ par->cursor_reset) {
+ char *mask = kmalloc_array(w, vc->vc_font.height, GFP_ATOMIC);
+ int cur_height, size, i = 0;
+ u8 msk = 0xff;
+@@ -332,13 +332,13 @@ static void ud_cursor(struct vc_data *vc
+ if (!mask)
+ return;
+
+- kfree(ops->cursor_state.mask);
+- ops->cursor_state.mask = mask;
++ kfree(par->cursor_state.mask);
++ par->cursor_state.mask = mask;
+
+- ops->p->cursor_shape = vc->vc_cursor_type;
++ par->p->cursor_shape = vc->vc_cursor_type;
+ cursor.set |= FB_CUR_SETSHAPE;
+
+- switch (CUR_SIZE(ops->p->cursor_shape)) {
++ switch (CUR_SIZE(par->p->cursor_shape)) {
+ case CUR_NONE:
+ cur_height = 0;
+ break;
+@@ -373,26 +373,26 @@ static void ud_cursor(struct vc_data *vc
+
+ switch (mode) {
+ case CM_ERASE:
+- ops->cursor_state.enable = 0;
++ par->cursor_state.enable = 0;
+ break;
+ case CM_DRAW:
+ case CM_MOVE:
+ default:
+- ops->cursor_state.enable = (use_sw) ? 0 : 1;
++ par->cursor_state.enable = (use_sw) ? 0 : 1;
+ break;
+ }
+
+ cursor.image.data = src;
+- cursor.image.fg_color = ops->cursor_state.image.fg_color;
+- cursor.image.bg_color = ops->cursor_state.image.bg_color;
+- cursor.image.dx = ops->cursor_state.image.dx;
+- cursor.image.dy = ops->cursor_state.image.dy;
+- cursor.image.height = ops->cursor_state.image.height;
+- cursor.image.width = ops->cursor_state.image.width;
+- cursor.hot.x = ops->cursor_state.hot.x;
+- cursor.hot.y = ops->cursor_state.hot.y;
+- cursor.mask = ops->cursor_state.mask;
+- cursor.enable = ops->cursor_state.enable;
++ cursor.image.fg_color = par->cursor_state.image.fg_color;
++ cursor.image.bg_color = par->cursor_state.image.bg_color;
++ cursor.image.dx = par->cursor_state.image.dx;
++ cursor.image.dy = par->cursor_state.image.dy;
++ cursor.image.height = par->cursor_state.image.height;
++ cursor.image.width = par->cursor_state.image.width;
++ cursor.hot.x = par->cursor_state.hot.x;
++ cursor.hot.y = par->cursor_state.hot.y;
++ cursor.mask = par->cursor_state.mask;
++ cursor.enable = par->cursor_state.enable;
+ cursor.image.depth = 1;
+ cursor.rop = ROP_XOR;
+
+@@ -402,36 +402,36 @@ static void ud_cursor(struct vc_data *vc
+ if (err)
+ soft_cursor(info, &cursor);
+
+- ops->cursor_reset = 0;
++ par->cursor_reset = 0;
+ }
+
+ static int ud_update_start(struct fb_info *info)
+ {
+- struct fbcon_ops *ops = info->fbcon_par;
++ struct fbcon_par *par = info->fbcon_par;
+ int xoffset, yoffset;
+- u32 vyres = GETVYRES(ops->p, info);
+- u32 vxres = GETVXRES(ops->p, info);
++ u32 vyres = GETVYRES(par->p, info);
++ u32 vxres = GETVXRES(par->p, info);
+ int err;
+
+- xoffset = vxres - info->var.xres - ops->var.xoffset;
+- yoffset = vyres - info->var.yres - ops->var.yoffset;
++ xoffset = vxres - info->var.xres - par->var.xoffset;
++ yoffset = vyres - info->var.yres - par->var.yoffset;
+ if (yoffset < 0)
+ yoffset += vyres;
+- ops->var.xoffset = xoffset;
+- ops->var.yoffset = yoffset;
+- err = fb_pan_display(info, &ops->var);
+- ops->var.xoffset = info->var.xoffset;
+- ops->var.yoffset = info->var.yoffset;
+- ops->var.vmode = info->var.vmode;
++ par->var.xoffset = xoffset;
++ par->var.yoffset = yoffset;
++ err = fb_pan_display(info, &par->var);
++ par->var.xoffset = info->var.xoffset;
++ par->var.yoffset = info->var.yoffset;
++ par->var.vmode = info->var.vmode;
+ return err;
+ }
+
+-void fbcon_rotate_ud(struct fbcon_ops *ops)
++void fbcon_rotate_ud(struct fbcon_par *par)
+ {
+- ops->bmove = ud_bmove;
+- ops->clear = ud_clear;
+- ops->putcs = ud_putcs;
+- ops->clear_margins = ud_clear_margins;
+- ops->cursor = ud_cursor;
+- ops->update_start = ud_update_start;
++ par->bmove = ud_bmove;
++ par->clear = ud_clear;
++ par->putcs = ud_putcs;
++ par->clear_margins = ud_clear_margins;
++ par->cursor = ud_cursor;
++ par->update_start = ud_update_start;
+ }
+--- a/drivers/video/fbdev/core/softcursor.c
++++ b/drivers/video/fbdev/core/softcursor.c
+@@ -21,7 +21,7 @@
+
+ int soft_cursor(struct fb_info *info, struct fb_cursor *cursor)
+ {
+- struct fbcon_ops *ops = info->fbcon_par;
++ struct fbcon_par *par = info->fbcon_par;
+ unsigned int scan_align = info->pixmap.scan_align - 1;
+ unsigned int buf_align = info->pixmap.buf_align - 1;
+ unsigned int i, size, dsize, s_pitch, d_pitch;
+@@ -34,19 +34,19 @@ int soft_cursor(struct fb_info *info, st
+ s_pitch = (cursor->image.width + 7) >> 3;
+ dsize = s_pitch * cursor->image.height;
+
+- if (dsize + sizeof(struct fb_image) != ops->cursor_size) {
+- kfree(ops->cursor_src);
+- ops->cursor_size = dsize + sizeof(struct fb_image);
+-
+- ops->cursor_src = kmalloc(ops->cursor_size, GFP_ATOMIC);
+- if (!ops->cursor_src) {
+- ops->cursor_size = 0;
++ if (dsize + sizeof(struct fb_image) != par->cursor_size) {
++ kfree(par->cursor_src);
++ par->cursor_size = dsize + sizeof(struct fb_image);
++
++ par->cursor_src = kmalloc(par->cursor_size, GFP_ATOMIC);
++ if (!par->cursor_src) {
++ par->cursor_size = 0;
+ return -ENOMEM;
+ }
+ }
+
+- src = ops->cursor_src + sizeof(struct fb_image);
+- image = (struct fb_image *)ops->cursor_src;
++ src = par->cursor_src + sizeof(struct fb_image);
++ image = (struct fb_image *)par->cursor_src;
+ *image = cursor->image;
+ d_pitch = (s_pitch + scan_align) & ~scan_align;
+
+--- a/drivers/video/fbdev/core/tileblit.c
++++ b/drivers/video/fbdev/core/tileblit.c
+@@ -151,34 +151,34 @@ static void tile_cursor(struct vc_data *
+
+ static int tile_update_start(struct fb_info *info)
+ {
+- struct fbcon_ops *ops = info->fbcon_par;
++ struct fbcon_par *par = info->fbcon_par;
+ int err;
+
+- err = fb_pan_display(info, &ops->var);
+- ops->var.xoffset = info->var.xoffset;
+- ops->var.yoffset = info->var.yoffset;
+- ops->var.vmode = info->var.vmode;
++ err = fb_pan_display(info, &par->var);
++ par->var.xoffset = info->var.xoffset;
++ par->var.yoffset = info->var.yoffset;
++ par->var.vmode = info->var.vmode;
+ return err;
+ }
+
+ void fbcon_set_tileops(struct vc_data *vc, struct fb_info *info)
+ {
+ struct fb_tilemap map;
+- struct fbcon_ops *ops = info->fbcon_par;
++ struct fbcon_par *par = info->fbcon_par;
+
+- ops->bmove = tile_bmove;
+- ops->clear = tile_clear;
+- ops->putcs = tile_putcs;
+- ops->clear_margins = tile_clear_margins;
+- ops->cursor = tile_cursor;
+- ops->update_start = tile_update_start;
++ par->bmove = tile_bmove;
++ par->clear = tile_clear;
++ par->putcs = tile_putcs;
++ par->clear_margins = tile_clear_margins;
++ par->cursor = tile_cursor;
++ par->update_start = tile_update_start;
+
+- if (ops->p) {
++ if (par->p) {
+ map.width = vc->vc_font.width;
+ map.height = vc->vc_font.height;
+ map.depth = 1;
+ map.length = vc->vc_font.charcount;
+- map.data = ops->p->fontdata;
++ map.data = par->p->fontdata;
+ info->tileops->fb_settile(info, &map);
+ }
+ }
--- /dev/null
+From stable+bounces-287934-greg=kroah.com@vger.kernel.org Wed Jul 22 18:28:28 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 12:07:58 -0400
+Subject: fbcon: Use correct type for vc_resize() return value
+To: stable@vger.kernel.org
+Cc: Jiacheng Yu <yujiacheng3@huawei.com>, Thomas Zimmermann <tzimmermann@suse.de>, Helge Deller <deller@gmx.de>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260722160758.1740441-2-sashal@kernel.org>
+
+From: Jiacheng Yu <yujiacheng3@huawei.com>
+
+[ Upstream commit 84202754fb1727dc3ee87f47104e4162ecc8ba3a ]
+
+The return value of vc_resize() is int, but fbcon_set_disp() stores it
+in an unsigned long variable. While the !ret check happens to work
+correctly by coincidence (negative values become large positive values),
+the types should match. Use int instead.
+
+Eliminates the following W=3 warning:
+
+ drivers/video/fbdev/core/fbcon.c: In function 'fbcon_set_disp':
+ drivers/video/fbdev/core/fbcon.c:1494:14: warning: implicit conversion from 'int' to 'unsigned long' [-Wconversion]
+
+Fixes: af0db3c1f898 ("fbdev: Fix vmalloc out-of-bounds write in fast_imageblit")
+Cc: stable@vger.kernel.org # v6.17+
+Signed-off-by: Jiacheng Yu <yujiacheng3@huawei.com>
+Reviewed-by: Thomas Zimmermann <tzimmermann@suse.de>
+Signed-off-by: Helge Deller <deller@gmx.de>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/video/fbdev/core/fbcon.c | 3 +--
+ 1 file changed, 1 insertion(+), 2 deletions(-)
+
+--- a/drivers/video/fbdev/core/fbcon.c
++++ b/drivers/video/fbdev/core/fbcon.c
+@@ -1356,8 +1356,7 @@ static void fbcon_set_disp(struct fb_inf
+ struct vc_data **default_mode, *vc;
+ struct vc_data *svc;
+ struct fbcon_par *par = info->fbcon_par;
+- int rows, cols;
+- unsigned long ret = 0;
++ int rows, cols, ret;
+
+ p = &fb_display[unit];
+
--- /dev/null
+From stable+bounces-288126-greg=kroah.com@vger.kernel.org Thu Jul 23 01:53:30 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 19:47:09 -0400
+Subject: fbdev: efifb: fix memory leak in efifb_probe()
+To: stable@vger.kernel.org
+Cc: Abdun Nihaal <nihaal@cse.iitm.ac.in>, Thomas Zimmermann <tzimmermann@suse.de>, Helge Deller <deller@gmx.de>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260722234709.2613278-2-sashal@kernel.org>
+
+From: Abdun Nihaal <nihaal@cse.iitm.ac.in>
+
+[ Upstream commit 9b6eaf101656958397a6012bf43f6e2e42c9e5cb ]
+
+Since commit 73ce73c30ba9 ("fbdev: Transfer video= option strings to
+caller; clarify ownership") the string returned from fb_get_options()
+is expected to be freed by the caller, but the string is not freed in
+efifb_probe(). Fix that by freeing the option string after setup.
+
+Fixes: 73ce73c30ba9 ("fbdev: Transfer video= option strings to caller; clarify ownership")
+Cc: stable@vger.kernel.org
+Signed-off-by: Abdun Nihaal <nihaal@cse.iitm.ac.in>
+Reviewed-by: Thomas Zimmermann <tzimmermann@suse.de>
+Signed-off-by: Helge Deller <deller@gmx.de>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/video/fbdev/efifb.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/video/fbdev/efifb.c
++++ b/drivers/video/fbdev/efifb.c
+@@ -375,6 +375,7 @@ static int efifb_probe(struct platform_d
+ if (fb_get_options("efifb", &option))
+ return -ENODEV;
+ efifb_setup(si, option);
++ kfree(option);
+
+ /* We don't get linelength from UGA Draw Protocol, only from
+ * EFI Graphics Protocol. So if it's not in DMI, and it's not
--- /dev/null
+From stable+bounces-288125-greg=kroah.com@vger.kernel.org Thu Jul 23 01:53:27 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 19:47:08 -0400
+Subject: fbdev/efifb: Replace references to global screen_info by local pointer
+To: stable@vger.kernel.org
+Cc: Thomas Zimmermann <tzimmermann@suse.de>, Sui Jingfeng <sui.jingfeng@linux.dev>, Javier Martinez Canillas <javierm@redhat.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260722234709.2613278-1-sashal@kernel.org>
+
+From: Thomas Zimmermann <tzimmermann@suse.de>
+
+[ Upstream commit 8505e70821ade706f98f7114a2cbcd51326f7b82 ]
+
+Get the global screen_info's address once and access the data via
+this pointer. Limits the use of global state.
+
+v3:
+ * use const screen_info in several places (Sui)
+ * fix build for deferred takeover (kernel test robot)
+
+Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
+Tested-by: Sui Jingfeng <sui.jingfeng@linux.dev>
+Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>
+Link: https://patchwork.freedesktop.org/patch/msgid/20231206135153.2599-2-tzimmermann@suse.de
+Stable-dep-of: 9b6eaf101656 ("fbdev: efifb: fix memory leak in efifb_probe()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/video/fbdev/efifb.c | 124 ++++++++++++++++++++++----------------------
+ 1 file changed, 64 insertions(+), 60 deletions(-)
+
+--- a/drivers/video/fbdev/efifb.c
++++ b/drivers/video/fbdev/efifb.c
+@@ -108,7 +108,7 @@ static int efifb_setcolreg(unsigned regn
+ */
+ #if defined CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER && \
+ defined CONFIG_ACPI_BGRT
+-static void efifb_copy_bmp(u8 *src, u32 *dst, int width, struct screen_info *si)
++static void efifb_copy_bmp(u8 *src, u32 *dst, int width, const struct screen_info *si)
+ {
+ u8 r, g, b;
+
+@@ -130,7 +130,7 @@ static void efifb_copy_bmp(u8 *src, u32
+ * resolution still fits, it will be displayed very close to the right edge of
+ * the display looking quite bad. This function checks for this.
+ */
+-static bool efifb_bgrt_sanity_check(struct screen_info *si, u32 bmp_width)
++static bool efifb_bgrt_sanity_check(const struct screen_info *si, u32 bmp_width)
+ {
+ /*
+ * All x86 firmwares horizontally center the image (the yoffset
+@@ -141,16 +141,15 @@ static bool efifb_bgrt_sanity_check(stru
+ return bgrt_tab.image_offset_x == expected_xoffset;
+ }
+ #else
+-static bool efifb_bgrt_sanity_check(struct screen_info *si, u32 bmp_width)
++static bool efifb_bgrt_sanity_check(const struct screen_info *si, u32 bmp_width)
+ {
+ return true;
+ }
+ #endif
+
+-static void efifb_show_boot_graphics(struct fb_info *info)
++static void efifb_show_boot_graphics(struct fb_info *info, const struct screen_info *si)
+ {
+ u32 bmp_width, bmp_height, bmp_pitch, dst_x, y, src_y;
+- struct screen_info *si = &screen_info;
+ struct bmp_file_header *file_header;
+ struct bmp_dib_header *dib_header;
+ void *bgrt_image = NULL;
+@@ -247,7 +246,8 @@ error:
+ pr_warn("efifb: Ignoring BGRT: unexpected or invalid BMP data\n");
+ }
+ #else
+-static inline void efifb_show_boot_graphics(struct fb_info *info) {}
++static inline void efifb_show_boot_graphics(struct fb_info *info, const struct screen_info *si)
++{ }
+ #endif
+
+ /*
+@@ -282,7 +282,7 @@ static const struct fb_ops efifb_ops = {
+ .fb_setcolreg = efifb_setcolreg,
+ };
+
+-static int efifb_setup(char *options)
++static int efifb_setup(struct screen_info *si, char *options)
+ {
+ char *this_opt;
+
+@@ -290,16 +290,16 @@ static int efifb_setup(char *options)
+ while ((this_opt = strsep(&options, ",")) != NULL) {
+ if (!*this_opt) continue;
+
+- efifb_setup_from_dmi(&screen_info, this_opt);
++ efifb_setup_from_dmi(si, this_opt);
+
+ if (!strncmp(this_opt, "base:", 5))
+- screen_info.lfb_base = simple_strtoul(this_opt+5, NULL, 0);
++ si->lfb_base = simple_strtoul(this_opt+5, NULL, 0);
+ else if (!strncmp(this_opt, "stride:", 7))
+- screen_info.lfb_linelength = simple_strtoul(this_opt+7, NULL, 0) * 4;
++ si->lfb_linelength = simple_strtoul(this_opt+7, NULL, 0) * 4;
+ else if (!strncmp(this_opt, "height:", 7))
+- screen_info.lfb_height = simple_strtoul(this_opt+7, NULL, 0);
++ si->lfb_height = simple_strtoul(this_opt+7, NULL, 0);
+ else if (!strncmp(this_opt, "width:", 6))
+- screen_info.lfb_width = simple_strtoul(this_opt+6, NULL, 0);
++ si->lfb_width = simple_strtoul(this_opt+6, NULL, 0);
+ else if (!strcmp(this_opt, "nowc"))
+ mem_flags &= ~EFI_MEMORY_WC;
+ else if (!strcmp(this_opt, "nobgrt"))
+@@ -310,15 +310,15 @@ static int efifb_setup(char *options)
+ return 0;
+ }
+
+-static inline bool fb_base_is_valid(void)
++static inline bool fb_base_is_valid(struct screen_info *si)
+ {
+- if (screen_info.lfb_base)
++ if (si->lfb_base)
+ return true;
+
+- if (!(screen_info.capabilities & VIDEO_CAPABILITY_64BIT_BASE))
++ if (!(si->capabilities & VIDEO_CAPABILITY_64BIT_BASE))
+ return false;
+
+- if (screen_info.ext_lfb_base)
++ if (si->ext_lfb_base)
+ return true;
+
+ return false;
+@@ -329,7 +329,10 @@ static ssize_t name##_show(struct device
+ struct device_attribute *attr, \
+ char *buf) \
+ { \
+- return sprintf(buf, fmt "\n", (screen_info.lfb_##name)); \
++ struct screen_info *si = dev_get_platdata(dev); \
++ if (!si) \
++ return -ENODEV; \
++ return sprintf(buf, fmt "\n", (si->lfb_##name)); \
+ } \
+ static DEVICE_ATTR_RO(name)
+
+@@ -356,6 +359,7 @@ static u64 bar_offset;
+
+ static int efifb_probe(struct platform_device *dev)
+ {
++ struct screen_info *si = &screen_info;
+ struct fb_info *info;
+ struct efifb_par *par;
+ int err, orientation;
+@@ -365,48 +369,48 @@ static int efifb_probe(struct platform_d
+ char *option = NULL;
+ efi_memory_desc_t md;
+
+- if (screen_info.orig_video_isVGA != VIDEO_TYPE_EFI || pci_dev_disabled)
++ if (si->orig_video_isVGA != VIDEO_TYPE_EFI || pci_dev_disabled)
+ return -ENODEV;
+
+ if (fb_get_options("efifb", &option))
+ return -ENODEV;
+- efifb_setup(option);
++ efifb_setup(si, option);
+
+ /* We don't get linelength from UGA Draw Protocol, only from
+ * EFI Graphics Protocol. So if it's not in DMI, and it's not
+ * passed in from the user, we really can't use the framebuffer.
+ */
+- if (!screen_info.lfb_linelength)
++ if (!si->lfb_linelength)
+ return -ENODEV;
+
+- if (!screen_info.lfb_depth)
+- screen_info.lfb_depth = 32;
+- if (!screen_info.pages)
+- screen_info.pages = 1;
+- if (!fb_base_is_valid()) {
++ if (!si->lfb_depth)
++ si->lfb_depth = 32;
++ if (!si->pages)
++ si->pages = 1;
++ if (!fb_base_is_valid(si)) {
+ printk(KERN_DEBUG "efifb: invalid framebuffer address\n");
+ return -ENODEV;
+ }
+ printk(KERN_INFO "efifb: probing for efifb\n");
+
+ /* just assume they're all unset if any are */
+- if (!screen_info.blue_size) {
+- screen_info.blue_size = 8;
+- screen_info.blue_pos = 0;
+- screen_info.green_size = 8;
+- screen_info.green_pos = 8;
+- screen_info.red_size = 8;
+- screen_info.red_pos = 16;
+- screen_info.rsvd_size = 8;
+- screen_info.rsvd_pos = 24;
++ if (!si->blue_size) {
++ si->blue_size = 8;
++ si->blue_pos = 0;
++ si->green_size = 8;
++ si->green_pos = 8;
++ si->red_size = 8;
++ si->red_pos = 16;
++ si->rsvd_size = 8;
++ si->rsvd_pos = 24;
+ }
+
+- efifb_fix.smem_start = screen_info.lfb_base;
++ efifb_fix.smem_start = si->lfb_base;
+
+- if (screen_info.capabilities & VIDEO_CAPABILITY_64BIT_BASE) {
++ if (si->capabilities & VIDEO_CAPABILITY_64BIT_BASE) {
+ u64 ext_lfb_base;
+
+- ext_lfb_base = (u64)(unsigned long)screen_info.ext_lfb_base << 32;
++ ext_lfb_base = (u64)(unsigned long)si->ext_lfb_base << 32;
+ efifb_fix.smem_start |= ext_lfb_base;
+ }
+
+@@ -417,10 +421,10 @@ static int efifb_probe(struct platform_d
+ efifb_fix.smem_start = bar_resource->start + bar_offset;
+ }
+
+- efifb_defined.bits_per_pixel = screen_info.lfb_depth;
+- efifb_defined.xres = screen_info.lfb_width;
+- efifb_defined.yres = screen_info.lfb_height;
+- efifb_fix.line_length = screen_info.lfb_linelength;
++ efifb_defined.bits_per_pixel = si->lfb_depth;
++ efifb_defined.xres = si->lfb_width;
++ efifb_defined.yres = si->lfb_height;
++ efifb_fix.line_length = si->lfb_linelength;
+
+ /* size_vmode -- that is the amount of memory needed for the
+ * used video mode, i.e. the minimum amount of
+@@ -430,7 +434,7 @@ static int efifb_probe(struct platform_d
+ /* size_total -- all video memory we have. Used for
+ * entries, ressource allocation and bounds
+ * checking. */
+- size_total = screen_info.lfb_size;
++ size_total = si->lfb_size;
+ if (size_total < size_vmode)
+ size_total = size_vmode;
+
+@@ -505,14 +509,14 @@ static int efifb_probe(struct platform_d
+ goto err_release_fb;
+ }
+
+- efifb_show_boot_graphics(info);
++ efifb_show_boot_graphics(info, si);
+
+ pr_info("efifb: framebuffer at 0x%lx, using %dk, total %dk\n",
+ efifb_fix.smem_start, size_remap/1024, size_total/1024);
+ pr_info("efifb: mode is %dx%dx%d, linelength=%d, pages=%d\n",
+ efifb_defined.xres, efifb_defined.yres,
+ efifb_defined.bits_per_pixel, efifb_fix.line_length,
+- screen_info.pages);
++ si->pages);
+
+ efifb_defined.xres_virtual = efifb_defined.xres;
+ efifb_defined.yres_virtual = efifb_fix.smem_len /
+@@ -526,26 +530,26 @@ static int efifb_probe(struct platform_d
+ efifb_defined.left_margin = (efifb_defined.xres / 8) & 0xf8;
+ efifb_defined.hsync_len = (efifb_defined.xres / 8) & 0xf8;
+
+- efifb_defined.red.offset = screen_info.red_pos;
+- efifb_defined.red.length = screen_info.red_size;
+- efifb_defined.green.offset = screen_info.green_pos;
+- efifb_defined.green.length = screen_info.green_size;
+- efifb_defined.blue.offset = screen_info.blue_pos;
+- efifb_defined.blue.length = screen_info.blue_size;
+- efifb_defined.transp.offset = screen_info.rsvd_pos;
+- efifb_defined.transp.length = screen_info.rsvd_size;
++ efifb_defined.red.offset = si->red_pos;
++ efifb_defined.red.length = si->red_size;
++ efifb_defined.green.offset = si->green_pos;
++ efifb_defined.green.length = si->green_size;
++ efifb_defined.blue.offset = si->blue_pos;
++ efifb_defined.blue.length = si->blue_size;
++ efifb_defined.transp.offset = si->rsvd_pos;
++ efifb_defined.transp.length = si->rsvd_size;
+
+ pr_info("efifb: %s: "
+ "size=%d:%d:%d:%d, shift=%d:%d:%d:%d\n",
+ "Truecolor",
+- screen_info.rsvd_size,
+- screen_info.red_size,
+- screen_info.green_size,
+- screen_info.blue_size,
+- screen_info.rsvd_pos,
+- screen_info.red_pos,
+- screen_info.green_pos,
+- screen_info.blue_pos);
++ si->rsvd_size,
++ si->red_size,
++ si->green_size,
++ si->blue_size,
++ si->rsvd_pos,
++ si->red_pos,
++ si->green_pos,
++ si->blue_pos);
+
+ efifb_fix.ypanstep = 0;
+ efifb_fix.ywrapstep = 0;
--- /dev/null
+From stable+bounces-289740-greg=kroah.com@vger.kernel.org Tue Jul 28 01:34:46 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 19:34:36 -0400
+Subject: firmware_loader: introduce __free() cleanup hanler
+To: stable@vger.kernel.org
+Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>, Luis Chamberalin <mcgrof@kernel.org>, Greg Kroah-Hartman <gregkh@linuxfoundation.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260727233437.2425385-1-sashal@kernel.org>
+
+From: Dmitry Torokhov <dmitry.torokhov@gmail.com>
+
+[ Upstream commit 8dde8fa0cc3edce73c050b9882d06c1a575f6402 ]
+
+Define cleanup handler using facilities from linux/cleanup.h to simplify
+error handling in code using firmware loader. This will allow writing code
+like this:
+
+int driver_update_firmware(...)
+{
+ const struct firmware *fw_entry __free(firmware) = NULL;
+ int error;
+
+ ...
+ error = request_firmware(&fw_entry, fw_name, dev);
+ if (error) {
+ dev_err(dev, "failed to request firmware %s: %d",
+ fw_name, error);
+ return error;
+ }
+
+ error = check_firmware_valid(fw_entry);
+ if (error)
+ return error;
+
+ guard(mutex)(&instance->lock);
+
+ error = use_firmware(instance, fw);
+ if (error)
+ return error;
+
+ return 0;
+}
+
+Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
+Acked-by: Luis Chamberalin <mcgrof@kernel.org>
+Link: https://lore.kernel.org/r/ZaeQw7VXhnirX4pQ@google.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Stable-dep-of: d48795b5cd68 ("Input: ims-pcu - fix firmware leak in async update")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ include/linux/firmware.h | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/include/linux/firmware.h
++++ b/include/linux/firmware.h
+@@ -4,6 +4,7 @@
+
+ #include <linux/types.h>
+ #include <linux/compiler.h>
++#include <linux/cleanup.h>
+ #include <linux/gfp.h>
+
+ #define FW_ACTION_NOUEVENT 0
+@@ -196,4 +197,6 @@ static inline void firmware_upload_unreg
+
+ int firmware_request_cache(struct device *device, const char *name);
+
++DEFINE_FREE(firmware, struct firmware *, release_firmware(_T))
++
+ #endif
--- /dev/null
+From stable+bounces-282091-greg=kroah.com@vger.kernel.org Wed Jul 22 00:10:20 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 21 Jul 2026 14:59:09 -0400
+Subject: fpga: dfl-afu: validate DMA mapping length in afu_dma_map_region()
+To: stable@vger.kernel.org
+Cc: Sebastian Alba Vives <sebasjosue84@gmail.com>, Xu Yilun <yilun.xu@intel.com>, Xu Yilun <yilun.xu@linux.intel.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260721185909.397655-1-sashal@kernel.org>
+
+From: Sebastian Alba Vives <sebasjosue84@gmail.com>
+
+[ Upstream commit fc3b071a7c8dc0f5d56defddf6e6fd5aaa3e1e27 ]
+
+afu_ioctl_dma_map() accepts a 64-bit length from userspace via
+DFL_FPGA_PORT_DMA_MAP ioctl without an upper bound check. The value
+is passed to afu_dma_pin_pages() where npages is derived as
+length >> PAGE_SHIFT and passed to pin_user_pages_fast() which takes
+int nr_pages, causing implicit truncation if length is very large.
+
+Validate map.length at the ioctl entry point before calling
+afu_dma_map_region(), rejecting values whose page count exceeds
+INT_MAX.
+
+Fixes: fa8dda1edef9 ("fpga: dfl: afu: add DFL_FPGA_PORT_DMA_MAP/UNMAP ioctls support")
+Cc: stable@vger.kernel.org
+Signed-off-by: Sebastian Alba Vives <sebasjosue84@gmail.com>
+Reviewed-by: Xu Yilun <yilun.xu@intel.com>
+Link: https://lore.kernel.org/r/20260518190742.61426-3-sebasjosue84@gmail.com
+Signed-off-by: Xu Yilun <yilun.xu@linux.intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/fpga/dfl-afu-main.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/drivers/fpga/dfl-afu-main.c
++++ b/drivers/fpga/dfl-afu-main.c
+@@ -720,6 +720,9 @@ afu_ioctl_dma_map(struct dfl_feature_pla
+ if (map.argsz < minsz || map.flags)
+ return -EINVAL;
+
++ if (map.length >> PAGE_SHIFT > (u64)INT_MAX)
++ return -EINVAL;
++
+ ret = afu_dma_map_region(pdata, map.user_addr, map.length, &map.iova);
+ if (ret)
+ return ret;
--- /dev/null
+From stable+bounces-290844-greg=kroah.com@vger.kernel.org Wed Jul 29 19:14:11 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 29 Jul 2026 12:57:53 -0400
+Subject: fs/resctrl: Fix double-add of pseudo-locked region's RMID to free list
+To: stable@vger.kernel.org
+Cc: Reinette Chatre <reinette.chatre@intel.com>, "Borislav Petkov (AMD)" <bp@alien8.de>, stable@kernel.org, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260729165753.3661352-1-sashal@kernel.org>
+
+From: Reinette Chatre <reinette.chatre@intel.com>
+
+[ Upstream commit b9f089723aee892efc77c349ae47a6b452b293c4 ]
+
+A pseudo-locked group's RMID is freed when it is created. On unmount
+rmdir_all_sub() unconditionally frees all RMID of all groups, resulting
+in a double-free of the pseudo-locked group's RMID. The consequence of this
+is that the original free results in the pseudo-locked group's RMID being
+added to the rmid_free_lru linked list and the second free then attempts
+to add the same RMID entry to the rmid_free_lru again.
+
+Do not double-free a pseudo-locked group's RMID.
+
+Fixes: e0bdfe8e36f3 ("x86/intel_rdt: Support creation/removal of pseudo-locked region")
+Signed-off-by: Reinette Chatre <reinette.chatre@intel.com>
+Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de>
+Cc: <stable@kernel.org>
+Link: https://patch.msgid.link/551432dd7e624a862b8e58314c38aaba0afff3e9.1783377598.git.reinette.chatre@intel.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ arch/x86/kernel/cpu/resctrl/rdtgroup.c | 12 +++++++-----
+ 1 file changed, 7 insertions(+), 5 deletions(-)
+
+--- a/arch/x86/kernel/cpu/resctrl/rdtgroup.c
++++ b/arch/x86/kernel/cpu/resctrl/rdtgroup.c
+@@ -2744,10 +2744,6 @@ static void rmdir_all_sub(void)
+ if (rdtgrp == &rdtgroup_default)
+ continue;
+
+- if (rdtgrp->mode == RDT_MODE_PSEUDO_LOCKSETUP ||
+- rdtgrp->mode == RDT_MODE_PSEUDO_LOCKED)
+- rdtgroup_pseudo_lock_remove(rdtgrp);
+-
+ /*
+ * Give any CPUs back to the default group. We cannot copy
+ * cpu_online_mask because a CPU might have executed the
+@@ -2756,7 +2752,13 @@ static void rmdir_all_sub(void)
+ cpumask_or(&rdtgroup_default.cpu_mask,
+ &rdtgroup_default.cpu_mask, &rdtgrp->cpu_mask);
+
+- free_rmid(rdtgrp->mon.rmid);
++ if (rdtgrp->mode == RDT_MODE_PSEUDO_LOCKSETUP ||
++ rdtgrp->mode == RDT_MODE_PSEUDO_LOCKED) {
++ rdtgroup_pseudo_lock_remove(rdtgrp);
++ } else {
++ /* Pseudo-locked group's RMID is freed during setup. */
++ free_rmid(rdtgrp->mon.rmid);
++ }
+
+ kernfs_remove(rdtgrp->kn);
+ list_del(&rdtgrp->rdtgroup_list);
--- /dev/null
+From stable+bounces-290054-greg=kroah.com@vger.kernel.org Tue Jul 28 18:32:41 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Jul 2026 11:52:28 -0400
+Subject: gpio: mt7621: avoid corruption of shared interrupt trigger state
+To: stable@vger.kernel.org
+Cc: Sergio Paracuellos <sergio.paracuellos@gmail.com>, Sashiko <sashiko-bot@kernel.org>, Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260728155228.8665-1-sashal@kernel.org>
+
+From: Sergio Paracuellos <sergio.paracuellos@gmail.com>
+
+[ Upstream commit 1781172526d1092323af443fa03f00e6de560401 ]
+
+The bank-shared fields like 'rising' and 'falling' are modified using
+non-atomic read-modify-write operations. Since every gpio chip instance
+represents an entire bank of 32 pins, if 'mediatek_gpio_irq_type()' is
+called concurrently for different IRQs on the same bank a possible overwrite
+of each other's configuration is possible. Thus, protect this state with
+'gpio_generic_lock_irqsave' lock in the same way it is handled in irp_chip
+'mediatek_gpio_irq_mask()' and 'mediatek_gpio_irq_unmask()' callbacks.
+
+Cc: stable@vger.kernel.org
+Reported-by: Sashiko <sashiko-bot@kernel.org>
+Fixes: 4ba9c3afda41 ("gpio: mt7621: Add a driver for MT7621")
+Signed-off-by: Sergio Paracuellos <sergio.paracuellos@gmail.com>
+Link: https://patch.msgid.link/20260626060112.2498324-2-sergio.paracuellos@gmail.com
+Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
+[ Changed `guard(gpio_generic_lock_irqsave)(&rg->chip)` to `guard(spinlock_irqsave)(&rg->lock)` as the generic GPIO chip lock does not exist in this tree. ]
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/gpio/gpio-mt7621.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/drivers/gpio/gpio-mt7621.c
++++ b/drivers/gpio/gpio-mt7621.c
+@@ -156,6 +156,8 @@ mediatek_gpio_irq_type(struct irq_data *
+ int pin = d->hwirq;
+ u32 mask = BIT(pin);
+
++ guard(spinlock_irqsave)(&rg->lock);
++
+ if (type == IRQ_TYPE_PROBE) {
+ if ((rg->rising | rg->falling |
+ rg->hlevel | rg->llevel) & mask)
--- /dev/null
+From stable+bounces-289949-greg=kroah.com@vger.kernel.org Tue Jul 28 14:19:41 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Jul 2026 08:11:25 -0400
+Subject: gpio: tegra: do not call pinctrl for GPIO direction
+To: stable@vger.kernel.org
+Cc: Runyu Xiao <runyu.xiao@seu.edu.cn>, Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260728121125.3703669-2-sashal@kernel.org>
+
+From: Runyu Xiao <runyu.xiao@seu.edu.cn>
+
+[ Upstream commit d3e91a95b2b0fc6336dbf3ec90d831a1654d2720 ]
+
+tegra_gpio_direction_input() and tegra_gpio_direction_output() already
+program the GPIO controller direction registers directly. The additional
+pinctrl_gpio_direction_input/output() calls do not add a Tegra pinctrl
+operation, because the Tegra pinmux ops provide GPIO request/free
+handling but no gpio_set_direction hook.
+
+The extra call still enters the pinctrl core and takes pctldev->mutex.
+Shared GPIO users can call the direction path while holding their
+per-line spinlock, so this otherwise redundant pinctrl direction call can
+sleep in an atomic context.
+
+This was found by our static analysis tool and then confirmed by manual
+review of tegra_gpio_probe(), the Tegra GPIO direction callbacks and the
+Tegra pinctrl ops. The reviewed path has a default non-sleeping
+struct gpio_chip while the direction callback still enters the pinctrl
+mutex path.
+
+A directed runtime validation kept the same non-sleeping chip registration
+and drove:
+
+ gpio_shared_proxy_direction_output()
+ gpiod_direction_output_raw_commit()
+ tegra_gpio_direction_output()
+ pinctrl_gpio_direction_output()
+
+Lockdep reported a sleep-in-atomic warning with the shared GPIO spinlock
+held and pinctrl_get_device_gpio_range() plus tegra_gpio_direction_output()
+on the stack.
+
+Do not mark the whole chip as can_sleep to paper over this: can_sleep
+describes whether get()/set() may sleep, and Tegra value access is MMIO.
+Remove the redundant pinctrl direction calls and keep pinctrl involvement
+in the existing request/free path.
+
+Fixes: 11da90541283 ("gpio: tegra: Fix offset of pinctrl calls")
+Cc: stable@vger.kernel.org
+Signed-off-by: Runyu Xiao <runyu.xiao@seu.edu.cn>
+Link: https://patch.msgid.link/20260619152439.1239561-1-runyu.xiao@seu.edu.cn
+Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/gpio/gpio-tegra.c | 18 ++----------------
+ 1 file changed, 2 insertions(+), 16 deletions(-)
+
+--- a/drivers/gpio/gpio-tegra.c
++++ b/drivers/gpio/gpio-tegra.c
+@@ -174,18 +174,11 @@ static int tegra_gpio_direction_input(st
+ unsigned int offset)
+ {
+ struct tegra_gpio_info *tgi = gpiochip_get_data(chip);
+- int ret;
+
+ tegra_gpio_mask_write(tgi, GPIO_MSK_OE(tgi, offset), offset, 0);
+ tegra_gpio_enable(tgi, offset);
+
+- ret = pinctrl_gpio_direction_input(chip, offset);
+- if (ret < 0)
+- dev_err(tgi->dev,
+- "Failed to set pinctrl input direction of GPIO %d: %d",
+- chip->base + offset, ret);
+-
+- return ret;
++ return 0;
+ }
+
+ static int tegra_gpio_direction_output(struct gpio_chip *chip,
+@@ -193,19 +186,12 @@ static int tegra_gpio_direction_output(s
+ int value)
+ {
+ struct tegra_gpio_info *tgi = gpiochip_get_data(chip);
+- int ret;
+
+ tegra_gpio_set(chip, offset, value);
+ tegra_gpio_mask_write(tgi, GPIO_MSK_OE(tgi, offset), offset, 1);
+ tegra_gpio_enable(tgi, offset);
+
+- ret = pinctrl_gpio_direction_output(chip, offset);
+- if (ret < 0)
+- dev_err(tgi->dev,
+- "Failed to set pinctrl output direction of GPIO %d: %d",
+- chip->base + offset, ret);
+-
+- return ret;
++ return 0;
+ }
+
+ static int tegra_gpio_get_direction(struct gpio_chip *chip,
--- /dev/null
+From stable+bounces-283225-greg=kroah.com@vger.kernel.org Tue Jul 21 22:02:49 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 21 Jul 2026 15:48:28 -0400
+Subject: i2c: davinci: Unregister cpufreq notifier on probe failure
+To: stable@vger.kernel.org
+Cc: Haoxiang Li <haoxiang_li2024@163.com>, Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>, Andi Shyti <andi.shyti@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260721194828.416617-1-sashal@kernel.org>
+
+From: Haoxiang Li <haoxiang_li2024@163.com>
+
+[ Upstream commit e43f32816a1b1fe5a86279411626fe3a9be56d45 ]
+
+davinci_i2c_probe() registers a cpufreq transition notifier before adding
+the I2C adapter. If i2c_add_numbered_adapter() fails, the probe error path
+releases the device resources without unregistering the notifier.
+
+Add a dedicated error path to unregister the cpufreq notifier after
+i2c_add_numbered_adapter() fails.
+
+Fixes: 82c0de11b734 ("i2c: davinci: Add cpufreq support")
+Signed-off-by: Haoxiang Li <haoxiang_li2024@163.com>
+Cc: <stable@vger.kernel.org> # v2.6.36+
+Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
+Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
+Link: https://lore.kernel.org/r/20260610030513.2651018-1-haoxiang_li2024@163.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/i2c/busses/i2c-davinci.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+--- a/drivers/i2c/busses/i2c-davinci.c
++++ b/drivers/i2c/busses/i2c-davinci.c
+@@ -866,13 +866,15 @@ static int davinci_i2c_probe(struct plat
+ adap->nr = pdev->id;
+ r = i2c_add_numbered_adapter(adap);
+ if (r)
+- goto err_unuse_clocks;
++ goto err_cpufreq;
+
+ pm_runtime_mark_last_busy(dev->dev);
+ pm_runtime_put_autosuspend(dev->dev);
+
+ return 0;
+
++err_cpufreq:
++ i2c_davinci_cpufreq_deregister(dev);
+ err_unuse_clocks:
+ pm_runtime_dont_use_autosuspend(dev->dev);
+ pm_runtime_put_sync(dev->dev);
--- /dev/null
+From stable+bounces-287786-greg=kroah.com@vger.kernel.org Wed Jul 22 12:36:09 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 06:30:42 -0400
+Subject: i2c: i801: fix hardware state machine corruption in error path
+To: stable@vger.kernel.org
+Cc: Mingyu Wang <25181214217@stu.xidian.edu.cn>, Andi Shyti <andi.shyti@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260722103042.891373-1-sashal@kernel.org>
+
+From: Mingyu Wang <25181214217@stu.xidian.edu.cn>
+
+[ Upstream commit 10dd1a736d557e310a77117832874729a0175d57 ]
+
+A severe livelock and subsequent Hung Task panic were observed in the
+i2c-i801 driver during concurrent Fuzzing. The crash is caused by an
+unconditional hardware register cleanup in the error handling path of
+i801_access().
+
+When i801_check_pre() fails (e.g., returning -EBUSY because the SMBus
+controller is actively used by BIOS/ACPI), the kernel does not actually
+acquire the hardware ownership. However, the code jumps to the 'out'
+label and executes:
+
+ iowrite8(SMBHSTSTS_INUSE_STS | STATUS_FLAGS, SMBHSTSTS(priv));
+
+This forcefully clears the INUSE_STS lock and resets the hardware status
+flags without owning the controller. Doing so interrupts ongoing BIOS/ACPI
+transactions and totally corrupts the SMBus hardware state machine.
+
+Consequently, all subsequent i801_access() calls fail at the pre-check
+stage, triggering an endless stream of "SMBus is busy, can't use it!"
+error logs. Over a slow serial console, this printk flood monopolizes
+the CPU (Console Livelock), starving other processes trying to acquire
+the mmap_lock down_read semaphore, ultimately triggering the hung task
+watchdog.
+
+Fix this by moving the 'out' label below the hardware register cleanup.
+If i801_check_pre() fails, we safely bypass the iowrite8() and only
+release the software locks (pm_runtime and mutex), strictly adhering to
+the rule of not releasing resources that were never acquired.
+
+Fixes: 1f760b87e54c ("i2c: i801: Call i801_check_pre() from i801_access()")
+Signed-off-by: Mingyu Wang <25181214217@stu.xidian.edu.cn>
+Cc: <stable@vger.kernel.org> # v6.3+
+Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
+Link: https://lore.kernel.org/r/20260512093534.348655-1-w15303746062@163.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/i2c/busses/i2c-i801.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/i2c/busses/i2c-i801.c
++++ b/drivers/i2c/busses/i2c-i801.c
+@@ -918,13 +918,13 @@ static s32 i801_access(struct i2c_adapte
+ */
+ if (hwpec)
+ outb_p(inb_p(SMBAUXCTL(priv)) & ~SMBAUXCTL_CRC, SMBAUXCTL(priv));
+-out:
+ /*
+ * Unlock the SMBus device for use by BIOS/ACPI,
+ * and clear status flags if not done already.
+ */
+ outb_p(SMBHSTSTS_INUSE_STS | STATUS_FLAGS, SMBHSTSTS(priv));
+
++out:
+ pm_runtime_mark_last_busy(&priv->pci_dev->dev);
+ pm_runtime_put_autosuspend(&priv->pci_dev->dev);
+ mutex_unlock(&priv->acpi_lock);
--- /dev/null
+From stable+bounces-289485-greg=kroah.com@vger.kernel.org Mon Jul 27 14:36:35 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 08:28:46 -0400
+Subject: i2c: imx: fix locked bus on SMBus block-read of 0 (atomic)
+To: stable@vger.kernel.org
+Cc: Vincent Jardin <vjardin@free.fr>, Oleksij Rempel <o.rempel@pengutronix.de>, Carlos Song <carlos.song@nxp.com>, Stefan Eichenberger <eichest@gmail.com>, Andi Shyti <andi.shyti@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260727122846.4156347-2-sashal@kernel.org>
+
+From: Vincent Jardin <vjardin@free.fr>
+
+[ Upstream commit cb2fc37857693b55909fb77dc2c87cfbc1cdc476 ]
+
+SMBus 3.1 6.5.7 allows a Block Read byte count of 0, but the atomic
+(polling) path rejects it as -EPROTO. Worse, it returns without a
+NACK+STOP: the next receive cycle has already started, so the target
+keeps holding SDA and the bus stays stuck until a power cycle for
+this i2c controller.
+
+Reading I2DR to obtain the count likewise arms the next byte on the
+count > I2C_SMBUS_BLOCK_MAX path, which also returned -EPROTO directly
+and left the bus held.
+
+Handle both: NACK the in-flight dummy byte (TXAK) and extend msgs->len so
+the existing last-byte handling emits STOP; the dummy byte is discarded.
+A count of 0 is a valid empty block read; a count above
+I2C_SMBUS_BLOCK_MAX is still reported as -EPROTO, but only after the bus
+has been released.
+
+The interrupt-driven path has the same flaw from a later commit and is
+fixed separately, as it carries a different Fixes: tag and stable range.
+
+Fixes: 8e8782c71595 ("i2c: imx: add SMBus block read support")
+Signed-off-by: Vincent Jardin <vjardin@free.fr>
+Cc: <stable@vger.kernel.org> # v3.16+
+Acked-by: Oleksij Rempel <o.rempel@pengutronix.de>
+Acked-by: Carlos Song <carlos.song@nxp.com>
+Reviewed-by: Stefan Eichenberger <eichest@gmail.com>
+Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
+Link: https://lore.kernel.org/r/20260713-for-upstream-i2c-lx2160-fix-v1-v3-1-073ac9e103a5@free.fr
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/i2c/busses/i2c-imx.c | 19 ++++++++++++++++---
+ 1 file changed, 16 insertions(+), 3 deletions(-)
+
+--- a/drivers/i2c/busses/i2c-imx.c
++++ b/drivers/i2c/busses/i2c-imx.c
+@@ -1184,6 +1184,7 @@ static int i2c_imx_read(struct imx_i2c_s
+ int i, result;
+ unsigned int temp;
+ int block_data = msgs->flags & I2C_M_RECV_LEN;
++ int block_err = 0;
+
+ result = i2c_imx_prepare_read(i2c_imx, msgs, atomic, false);
+ if (result)
+@@ -1205,8 +1206,20 @@ static int i2c_imx_read(struct imx_i2c_s
+ */
+ if ((!i) && block_data) {
+ len = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR);
+- if ((len == 0) || (len > I2C_SMBUS_BLOCK_MAX))
+- return -EPROTO;
++ if ((len == 0) || (len > I2C_SMBUS_BLOCK_MAX)) {
++ /*
++ * SMBus 3.1 6.5.7: support count byte of 0.
++ * I2C_SMBUS_BLOCK_MAX case should not hold the SDA either.
++ */
++ if (len > I2C_SMBUS_BLOCK_MAX)
++ block_err = -EPROTO;
++ temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
++ temp |= I2CR_TXAK;
++ imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
++ msgs->buf[0] = 0;
++ msgs->len = 2;
++ continue;
++ }
+ dev_dbg(&i2c_imx->adapter.dev,
+ "<%s> read length: 0x%X\n",
+ __func__, len);
+@@ -1254,7 +1267,7 @@ static int i2c_imx_read(struct imx_i2c_s
+ "<%s> read byte: B%d=0x%X\n",
+ __func__, i, msgs->buf[i]);
+ }
+- return 0;
++ return block_err;
+ }
+
+ static int i2c_imx_atomic_read(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs,
--- /dev/null
+From stable+bounces-289484-greg=kroah.com@vger.kernel.org Mon Jul 27 14:40:52 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 08:28:45 -0400
+Subject: i2c: imx: separate atomic, dma and non-dma use case
+To: stable@vger.kernel.org
+Cc: Stefan Eichenberger <stefan.eichenberger@toradex.com>, Frank Li <Frank.Li@nxp.com>, Oleksij Rempel <o.rempel@pengutronix.de>, Andi Shyti <andi.shyti@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260727122846.4156347-1-sashal@kernel.org>
+
+From: Stefan Eichenberger <stefan.eichenberger@toradex.com>
+
+[ Upstream commit b460b15b3cc23ef3639cc51043bf8b2a70ca1878 ]
+
+Separate the atomic, dma and non-dma use case as a preparation step for
+moving the non-dma use case to the isr to avoid rescheduling while a
+transfer is in progress.
+
+Signed-off-by: Stefan Eichenberger <stefan.eichenberger@toradex.com>
+Reviewed-by: Frank Li <Frank.Li@nxp.com>
+Acked-by: Oleksij Rempel <o.rempel@pengutronix.de>
+Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
+Stable-dep-of: cb2fc3785769 ("i2c: imx: fix locked bus on SMBus block-read of 0 (atomic)")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/i2c/busses/i2c-imx.c | 107 ++++++++++++++++++++++++++++---------------
+ 1 file changed, 70 insertions(+), 37 deletions(-)
+
+--- a/drivers/i2c/busses/i2c-imx.c
++++ b/drivers/i2c/busses/i2c-imx.c
+@@ -1011,6 +1011,43 @@ static int i2c_imx_dma_write(struct imx_
+ return i2c_imx_acked(i2c_imx);
+ }
+
++static int i2c_imx_prepare_read(struct imx_i2c_struct *i2c_imx,
++ struct i2c_msg *msgs, bool atomic,
++ bool use_dma)
++{
++ int result;
++ unsigned int temp = 0;
++
++ /* write slave address */
++ imx_i2c_write_reg(i2c_8bit_addr_from_msg(msgs), i2c_imx, IMX_I2C_I2DR);
++ result = i2c_imx_trx_complete(i2c_imx, atomic);
++ if (result)
++ return result;
++ result = i2c_imx_acked(i2c_imx);
++ if (result)
++ return result;
++
++ dev_dbg(&i2c_imx->adapter.dev, "<%s> setup bus\n", __func__);
++
++ /* setup bus to read data */
++ temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
++ temp &= ~I2CR_MTX;
++
++ /*
++ * Reset the I2CR_TXAK flag initially for SMBus block read since the
++ * length is unknown
++ */
++ if (msgs->len - 1)
++ temp &= ~I2CR_TXAK;
++ if (use_dma)
++ temp |= I2CR_DMAEN;
++
++ imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
++ imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR); /* dummy read */
++
++ return 0;
++}
++
+ static int i2c_imx_dma_read(struct imx_i2c_struct *i2c_imx,
+ struct i2c_msg *msgs, bool is_lastmsg)
+ {
+@@ -1021,6 +1058,11 @@ static int i2c_imx_dma_read(struct imx_i
+ struct imx_i2c_dma *dma = i2c_imx->dma;
+ struct device *dev = &i2c_imx->adapter.dev;
+
++ result = i2c_imx_prepare_read(i2c_imx, msgs, false, true);
++ if (result)
++ return result;
++
++ dev_dbg(&i2c_imx->adapter.dev, "<%s> read data\n", __func__);
+
+ dma->chan_using = dma->chan_rx;
+ dma->dma_transfer_dir = DMA_DEV_TO_MEM;
+@@ -1131,50 +1173,24 @@ static int i2c_imx_write(struct imx_i2c_
+ return 0;
+ }
+
++static int i2c_imx_atomic_write(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs)
++{
++ return i2c_imx_write(i2c_imx, msgs, true);
++}
++
+ static int i2c_imx_read(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs,
+ bool is_lastmsg, bool atomic)
+ {
+ int i, result;
+ unsigned int temp;
+ int block_data = msgs->flags & I2C_M_RECV_LEN;
+- int use_dma = i2c_imx->dma && msgs->flags & I2C_M_DMA_SAFE &&
+- msgs->len >= DMA_THRESHOLD && !block_data;
+-
+- dev_dbg(&i2c_imx->adapter.dev,
+- "<%s> write slave address: addr=0x%x\n",
+- __func__, i2c_8bit_addr_from_msg(msgs));
+
+- /* write slave address */
+- imx_i2c_write_reg(i2c_8bit_addr_from_msg(msgs), i2c_imx, IMX_I2C_I2DR);
+- result = i2c_imx_trx_complete(i2c_imx, atomic);
+- if (result)
+- return result;
+- result = i2c_imx_acked(i2c_imx);
++ result = i2c_imx_prepare_read(i2c_imx, msgs, atomic, false);
+ if (result)
+ return result;
+
+- dev_dbg(&i2c_imx->adapter.dev, "<%s> setup bus\n", __func__);
+-
+- /* setup bus to read data */
+- temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
+- temp &= ~I2CR_MTX;
+-
+- /*
+- * Reset the I2CR_TXAK flag initially for SMBus block read since the
+- * length is unknown
+- */
+- if ((msgs->len - 1) || block_data)
+- temp &= ~I2CR_TXAK;
+- if (use_dma)
+- temp |= I2CR_DMAEN;
+- imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
+- imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR); /* dummy read */
+-
+ dev_dbg(&i2c_imx->adapter.dev, "<%s> read data\n", __func__);
+
+- if (use_dma)
+- return i2c_imx_dma_read(i2c_imx, msgs, is_lastmsg);
+-
+ /* read data */
+ for (i = 0; i < msgs->len; i++) {
+ u8 len = 0;
+@@ -1241,6 +1257,12 @@ static int i2c_imx_read(struct imx_i2c_s
+ return 0;
+ }
+
++static int i2c_imx_atomic_read(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs,
++ bool is_lastmsg)
++{
++ return i2c_imx_read(i2c_imx, msgs, is_lastmsg, true);
++}
++
+ static int i2c_imx_xfer_common(struct i2c_adapter *adapter,
+ struct i2c_msg *msgs, int num, bool atomic)
+ {
+@@ -1248,6 +1270,7 @@ static int i2c_imx_xfer_common(struct i2
+ int result;
+ bool is_lastmsg = false;
+ struct imx_i2c_struct *i2c_imx = i2c_get_adapdata(adapter);
++ int use_dma = 0;
+
+ /* Start I2C transfer */
+ result = i2c_imx_start(i2c_imx, atomic);
+@@ -1300,15 +1323,25 @@ static int i2c_imx_xfer_common(struct i2
+ (temp & I2SR_SRW ? 1 : 0), (temp & I2SR_IIF ? 1 : 0),
+ (temp & I2SR_RXAK ? 1 : 0));
+ #endif
++
++ use_dma = i2c_imx->dma && msgs[i].len >= DMA_THRESHOLD &&
++ msgs[i].flags & I2C_M_DMA_SAFE;
+ if (msgs[i].flags & I2C_M_RD) {
+- result = i2c_imx_read(i2c_imx, &msgs[i], is_lastmsg, atomic);
++ int block_data = msgs->flags & I2C_M_RECV_LEN;
++
++ if (atomic)
++ result = i2c_imx_atomic_read(i2c_imx, &msgs[i], is_lastmsg);
++ else if (use_dma && !block_data)
++ result = i2c_imx_dma_read(i2c_imx, &msgs[i], is_lastmsg);
++ else
++ result = i2c_imx_read(i2c_imx, &msgs[i], is_lastmsg, false);
+ } else {
+- if (!atomic &&
+- i2c_imx->dma && msgs[i].len >= DMA_THRESHOLD &&
+- msgs[i].flags & I2C_M_DMA_SAFE)
++ if (atomic)
++ result = i2c_imx_atomic_write(i2c_imx, &msgs[i]);
++ else if (use_dma)
+ result = i2c_imx_dma_write(i2c_imx, &msgs[i]);
+ else
+- result = i2c_imx_write(i2c_imx, &msgs[i], atomic);
++ result = i2c_imx_write(i2c_imx, &msgs[i], false);
+ }
+ if (result)
+ goto fail0;
--- /dev/null
+From stable+bounces-289741-greg=kroah.com@vger.kernel.org Tue Jul 28 01:34:46 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 19:34:37 -0400
+Subject: Input: ims-pcu - fix firmware leak in async update
+To: stable@vger.kernel.org
+Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>, Sashiko bot <sashiko-bot@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260727233437.2425385-2-sashal@kernel.org>
+
+From: Dmitry Torokhov <dmitry.torokhov@gmail.com>
+
+[ Upstream commit d48795b5cd6828d36b707e8d62fc9e5c90e004ab ]
+
+The firmware object was not being released if validation failed.
+Use __free(firmware) to ensure the firmware is always released.
+
+Fixes: 628329d52474 ("Input: add IMS Passenger Control Unit driver")
+Cc: stable@vger.kernel.org
+Reported-by: Sashiko bot <sashiko-bot@kernel.org>
+Assisted-by: Gemini:gemini-3.1-pro
+Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/input/misc/ims-pcu.c | 5 ++---
+ 1 file changed, 2 insertions(+), 3 deletions(-)
+
+--- a/drivers/input/misc/ims-pcu.c
++++ b/drivers/input/misc/ims-pcu.c
+@@ -964,9 +964,10 @@ out:
+ return retval;
+ }
+
+-static void ims_pcu_process_async_firmware(const struct firmware *fw,
++static void ims_pcu_process_async_firmware(const struct firmware *_fw,
+ void *context)
+ {
++ const struct firmware *fw __free(firmware) = _fw;
+ struct ims_pcu *pcu = context;
+ int error;
+
+@@ -987,8 +988,6 @@ static void ims_pcu_process_async_firmwa
+ ims_pcu_handle_firmware_update(pcu, fw);
+ mutex_unlock(&pcu->cmd_mutex);
+
+- release_firmware(fw);
+-
+ out:
+ complete(&pcu->async_firmware_done);
+ }
--- /dev/null
+From stable+bounces-279630-greg=kroah.com@vger.kernel.org Tue Jul 21 19:24:14 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 21 Jul 2026 13:12:57 -0400
+Subject: iommu/amd: Don't split flush for amd_iommu_domain_flush_all()
+To: stable@vger.kernel.org
+Cc: Weinan Liu <wnliu@google.com>, Josef Bacik <josef@toxicpanda.com>, Jason Gunthorpe <jgg@nvidia.com>, Wei Wang <wei.w.wang@hotmail.com>, Samiullah Khawaja <skhawaja@google.com>, Suravee Suthikulpanit <suravee.suthikulpanit@amd.com>, Vasant Hegde <vasant.hegde@amd.com>, Joerg Roedel <joerg.roedel@amd.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260721171257.107653-1-sashal@kernel.org>
+
+From: Weinan Liu <wnliu@google.com>
+
+[ Upstream commit 69fe699afe1afcb730164b86c228483c2da05f94 ]
+
+We have observed multiple full invalidations occurring during device
+detach when we are done using the vfio-device.
+
+blocked_domain_attach_device()
+ -> detach_device()
+ -> amd_iommu_domain_flush_all()
+ -> amd_iommu_domain_flush_pages(..., CMD_INV_IOMMU_ALL_PAGES_ADDRESS)
+
+ while (size != 0) {
+
+ -> __domain_flush_pages( flush_size /* power of 2 flush_size */)
+ -> domain_flush_pages_v1()
+ -> build_inv_iommu_pages()
+ -> build_inv_address()
+
+ }
+
+build_inv_address() will trigger a full invalidation if the chunk
+size > (1 << 51). Consequently, the guest will issue multiple full
+invalidations for a single call to amd_iommu_domain_flush_all()
+
+Without this patch, we will see 10 time instead of 1 time full
+invalidations for every amd_iommu_domain_flush_all().
+
+Cc: stable@vger.kernel.org
+Fixes: a270be1b3fdf ("iommu/amd: Use only natural aligned flushes in a VM")
+Suggested-by: Josef Bacik <josef@toxicpanda.com>
+Suggested-by: Jason Gunthorpe <jgg@nvidia.com>
+Signed-off-by: Weinan Liu <wnliu@google.com>
+Reviewed-by: Wei Wang <wei.w.wang@hotmail.com>
+Reviewed-by: Jason Gunthorpe <jgg@nvidia.com>
+Reviewed-by: Samiullah Khawaja <skhawaja@google.com>
+Reviewed-by: Suravee Suthikulpanit <suravee.suthikulpanit@amd.com>
+Reviewed-by: Vasant Hegde <vasant.hegde@amd.com>
+Signed-off-by: Joerg Roedel <joerg.roedel@amd.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/iommu/amd/iommu.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- a/drivers/iommu/amd/iommu.c
++++ b/drivers/iommu/amd/iommu.c
+@@ -1441,7 +1441,8 @@ static void __domain_flush_pages(struct
+ static void domain_flush_pages(struct protection_domain *domain,
+ u64 address, size_t size, int pde)
+ {
+- if (likely(!amd_iommu_np_cache)) {
++ if (likely(!amd_iommu_np_cache) ||
++ size >= (1ULL<<52)) {
+ __domain_flush_pages(domain, address, size, pde);
+ return;
+ }
--- /dev/null
+From stable+bounces-290122-greg=kroah.com@vger.kernel.org Tue Jul 28 20:23:29 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Jul 2026 14:07:42 -0400
+Subject: ipmi: fix refcount leak in i_ipmi_request()
+To: stable@vger.kernel.org
+Cc: Wentao Liang <vulab@iscas.ac.cn>, Corey Minyard <corey@minyard.net>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260728180742.291994-1-sashal@kernel.org>
+
+From: Wentao Liang <vulab@iscas.ac.cn>
+
+[ Upstream commit a3f3859cecacb64f18fd446271ece9a3b3f2d4de ]
+
+When a caller provides a `supplied_recv` message to i_ipmi_request(),
+the function increments the user's `nr_msgs` reference count. If an
+error occurs later, the out_err cleanup path only frees the recv_msg
+if the function allocated it itself (i.e., !supplied_recv). In the
+supplied_recv case the cleanup is skipped, leaving the reference count
+elevated. The caller ipmi_request_supply_msgs() does not release the
+supplied_recv on error, so the reference is permanently leaked.
+
+Fix this by explicitly reverting the reference count operations when a
+supplied recv_msg with a valid user pointer is present in the error
+path: decrement nr_msgs and drop the user's kref.
+
+Cc: stable@vger.kernel.org
+Fixes: b52da4054ee0 ("ipmi: Rework user message limit handling")
+Signed-off-by: Wentao Liang <vulab@iscas.ac.cn>
+Message-ID: <20260603120634.3758747-1-vulab@iscas.ac.cn>
+Signed-off-by: Corey Minyard <corey@minyard.net>
+[ changed `free_ipmi_user` to `free_user` in the two added `kref_put()` calls ]
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/char/ipmi/ipmi_msghandler.c | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+--- a/drivers/char/ipmi/ipmi_msghandler.c
++++ b/drivers/char/ipmi/ipmi_msghandler.c
+@@ -2331,6 +2331,10 @@ static int i_ipmi_request(struct ipmi_us
+ if (smi_msg == NULL) {
+ if (!supplied_recv)
+ ipmi_free_recv_msg(recv_msg);
++ else if (recv_msg->user) {
++ atomic_dec(&recv_msg->user->nr_msgs);
++ kref_put(&recv_msg->user->refcount, free_user);
++ }
+ return -ENOMEM;
+ }
+ }
+@@ -2373,6 +2377,10 @@ out_err:
+ ipmi_free_smi_msg(smi_msg);
+ if (!supplied_recv)
+ ipmi_free_recv_msg(recv_msg);
++ else if (recv_msg->user) {
++ atomic_dec(&recv_msg->user->nr_msgs);
++ kref_put(&recv_msg->user->refcount, free_user);
++ }
+ } else {
+ dev_dbg(intf->si_dev, "Send: %*ph\n",
+ smi_msg->data_size, smi_msg->data);
--- /dev/null
+From stable+bounces-291165-greg=kroah.com@vger.kernel.org Thu Jul 30 14:53:52 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 30 Jul 2026 08:15:40 -0400
+Subject: KVM: Introduce vcpu->wants_to_run
+To: stable@vger.kernel.org
+Cc: David Matlack <dmatlack@google.com>, Sean Christopherson <seanjc@google.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260730121541.2209429-1-sashal@kernel.org>
+
+From: David Matlack <dmatlack@google.com>
+
+[ Upstream commit a6816314af5749cd88944bfdceb270c627cdf348 ]
+
+Introduce vcpu->wants_to_run to indicate when a vCPU is in its core run
+loop, i.e. when the vCPU is running the KVM_RUN ioctl and immediate_exit
+was not set.
+
+Replace all references to vcpu->run->immediate_exit with
+!vcpu->wants_to_run to avoid TOCTOU races with userspace. For example, a
+malicious userspace could invoked KVM_RUN with immediate_exit=true and
+then after KVM reads it to set wants_to_run=false, flip it to false.
+This would result in the vCPU running in KVM_RUN with
+wants_to_run=false. This wouldn't cause any real bugs today but is a
+dangerous landmine.
+
+Signed-off-by: David Matlack <dmatlack@google.com>
+Link: https://lore.kernel.org/r/20240503181734.1467938-2-dmatlack@google.com
+Signed-off-by: Sean Christopherson <seanjc@google.com>
+Stable-dep-of: e800decd9c0a ("KVM: x86: Only reset TSC Deadline Timer in apic_timer_expired on KVM_RUN")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ arch/arm64/kvm/arm.c | 2 +-
+ arch/mips/kvm/mips.c | 2 +-
+ arch/powerpc/kvm/powerpc.c | 2 +-
+ arch/riscv/kvm/vcpu.c | 2 +-
+ arch/s390/kvm/kvm-s390.c | 2 +-
+ arch/x86/kvm/x86.c | 4 ++--
+ include/linux/kvm_host.h | 1 +
+ virt/kvm/kvm_main.c | 3 +++
+ 8 files changed, 11 insertions(+), 7 deletions(-)
+
+--- a/arch/arm64/kvm/arm.c
++++ b/arch/arm64/kvm/arm.c
+@@ -915,7 +915,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_v
+
+ vcpu_load(vcpu);
+
+- if (run->immediate_exit) {
++ if (!vcpu->wants_to_run) {
+ ret = -EINTR;
+ goto out;
+ }
+--- a/arch/mips/kvm/mips.c
++++ b/arch/mips/kvm/mips.c
+@@ -436,7 +436,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_v
+ vcpu->mmio_needed = 0;
+ }
+
+- if (vcpu->run->immediate_exit)
++ if (!vcpu->wants_to_run)
+ goto out;
+
+ lose_fpu(1);
+--- a/arch/powerpc/kvm/powerpc.c
++++ b/arch/powerpc/kvm/powerpc.c
+@@ -1858,7 +1858,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_v
+
+ kvm_sigset_activate(vcpu);
+
+- if (run->immediate_exit)
++ if (!vcpu->wants_to_run)
+ r = -EINTR;
+ else
+ r = kvmppc_vcpu_run(vcpu);
+--- a/arch/riscv/kvm/vcpu.c
++++ b/arch/riscv/kvm/vcpu.c
+@@ -704,7 +704,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_v
+ return ret;
+ }
+
+- if (run->immediate_exit) {
++ if (!vcpu->wants_to_run) {
+ kvm_vcpu_srcu_read_unlock(vcpu);
+ return -EINTR;
+ }
+--- a/arch/s390/kvm/kvm-s390.c
++++ b/arch/s390/kvm/kvm-s390.c
+@@ -5048,7 +5048,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_v
+ if (vcpu->kvm->arch.pv.dumping)
+ return -EINVAL;
+
+- if (kvm_run->immediate_exit)
++ if (!vcpu->wants_to_run)
+ return -EINTR;
+
+ if (kvm_run->kvm_valid_regs & ~KVM_SYNC_S390_VALID_FIELDS ||
+--- a/arch/x86/kvm/x86.c
++++ b/arch/x86/kvm/x86.c
+@@ -11224,7 +11224,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_v
+
+ kvm_vcpu_srcu_read_lock(vcpu);
+ if (unlikely(vcpu->arch.mp_state == KVM_MP_STATE_UNINITIALIZED)) {
+- if (kvm_run->immediate_exit) {
++ if (!vcpu->wants_to_run) {
+ r = -EINTR;
+ goto out;
+ }
+@@ -11302,7 +11302,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_v
+ WARN_ON_ONCE(vcpu->mmio_needed);
+ }
+
+- if (kvm_run->immediate_exit) {
++ if (!vcpu->wants_to_run) {
+ r = -EINTR;
+ goto out;
+ }
+--- a/include/linux/kvm_host.h
++++ b/include/linux/kvm_host.h
+@@ -378,6 +378,7 @@ struct kvm_vcpu {
+ bool dy_eligible;
+ } spin_loop;
+ #endif
++ bool wants_to_run;
+ bool preempted;
+ bool ready;
+ struct kvm_vcpu_arch arch;
+--- a/virt/kvm/kvm_main.c
++++ b/virt/kvm/kvm_main.c
+@@ -4160,7 +4160,10 @@ static long kvm_vcpu_ioctl(struct file *
+ synchronize_rcu();
+ put_pid(oldpid);
+ }
++ vcpu->wants_to_run = !READ_ONCE(vcpu->run->immediate_exit);
+ r = kvm_arch_vcpu_ioctl_run(vcpu);
++ vcpu->wants_to_run = false;
++
+ trace_kvm_userspace_exit(vcpu->run->exit_reason, r);
+ break;
+ }
--- /dev/null
+From stable+bounces-291164-greg=kroah.com@vger.kernel.org Thu Jul 30 14:53:37 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 30 Jul 2026 08:15:41 -0400
+Subject: KVM: x86: Only reset TSC Deadline Timer in apic_timer_expired on KVM_RUN
+To: stable@vger.kernel.org
+Cc: Venkatesh Srinivas <venkateshs@chromium.org>, David Matlack <dmatlack@google.com>, Sean Christopherson <seanjc@google.com>, Jim Mattson <jmattson@google.com>, James Houghton <jthoughton@google.com>, Chao Gao <chao.gao@intel.com>, Paolo Bonzini <pbonzini@redhat.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260730121541.2209429-2-sashal@kernel.org>
+
+From: Venkatesh Srinivas <venkateshs@chromium.org>
+
+[ Upstream commit e800decd9c0ac4349bcd8f8f9b29fd21fe93165e ]
+
+On Intel platforms with a VMX preemption timer and APICv, if a VMM
+calls KVM_GET_LAPIC before KVM_GET_MSRS to save the vCPU state, it is
+possible to lose a pending timer interrupt.
+
+If the thread running these ioctls is migrated to another core after
+calling KVM_GET_LAPIC but before KVM_GET_MSRS and the guest is using
+their LAPIC timer in TSC-deadline mode, not only does the save LAPIC
+state not carry the pending interrupt, the TSCDEADLINE MSR will be
+zeroed.
+
+After migration across CPUs, KVM_GET_MSRS calls vcpu_load, posting the
+interrupt and clearing the MSR:
+vcpu_load() ->
+ kvm_arch_vcpu_load() ->
+ kvm_lapic_restart_hv_timer() ->
+ start_hv_timer() ->
+ apic_timer_expired() ->
+ kvm_apic_inject_pending_timer_irqs()
+ . post interrupt into the LAPIC state
+ . clear IA32_TSCDEADLINE
+
+The saved LAPIC state will be missing the pending interrupt and the saved
+MSR will be zero. Oops.
+
+Fix by only posting an interrupt when we're attempting to enter the guest
+(vcpu->wants_to_run == true), not for vcpu_load from other paths.
+
+Assisted-by: gemini:gemini-3.1-pro-preview
+Debugged-by: David Matlack <dmatlack@google.com>
+Debugged-by: Sean Christopherson <seanjc@google.com>
+Debugged-by: Jim Mattson <jmattson@google.com>
+Debugged-by: James Houghton <jthoughton@google.com>
+Signed-off-by: Venkatesh Srinivas <venkateshs@chromium.org>
+Message-ID: <20260715234234.15382-2-venkateshs@chromium.org>
+Reviewed-by: James Houghton <jthoughton@google.com>
+Reviewed-by: Chao Gao <chao.gao@intel.com>
+Cc: stable@vger.kernel.org
+Fixes: ae95f566b3d2 ("KVM: X86: TSCDEADLINE MSR emulation fastpath", 2020-05-15)
+Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ arch/x86/kvm/lapic.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/arch/x86/kvm/lapic.c
++++ b/arch/x86/kvm/lapic.c
+@@ -1912,7 +1912,7 @@ static void apic_timer_expired(struct kv
+ if (apic_lvtt_tscdeadline(apic) || ktimer->hv_timer_in_use)
+ ktimer->expired_tscdeadline = ktimer->tscdeadline;
+
+- if (!from_timer_fn && apic->apicv_active) {
++ if (!from_timer_fn && apic->apicv_active && vcpu->wants_to_run) {
+ WARN_ON(kvm_get_running_vcpu() != vcpu);
+ kvm_apic_inject_pending_timer_irqs(apic);
+ return;
--- /dev/null
+From stable+bounces-289818-greg=kroah.com@vger.kernel.org Tue Jul 28 05:13:09 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 23:02:02 -0400
+Subject: locking/rt: Fix the incorrect RCU protection in rt_spin_unlock()
+To: stable@vger.kernel.org
+Cc: Thomas Gleixner <tglx@kernel.org>, syzbot+000c800a02097aaa10ed@syzkaller.appspotmail.com, Jann Horn <jannh@google.com>, Sebastian Andrzej Siewior <bigeasy@linutronix.de>, Al Viro <viro@zeniv.linux.org.uk>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260728030202.3123520-1-sashal@kernel.org>
+
+From: Thomas Gleixner <tglx@kernel.org>
+
+[ Upstream commit 89038cc87d80c77e7aa6f42a64b2573b74af339f ]
+
+rt_spin_unlock() releases the RCU protection before unlocking the
+lock. That opens the door for the following UAF scenario:
+
+ T1 T2
+ spin_lock(&p->lock); rcu_read_lock();
+ invalidate(p); p = rcu_dereference(ptr);
+ rcu_assign_pointer(ptr, NULL); if (!p) return;
+ spin_unlock(&p->lock); spin_lock(&p->lock)
+ lock(&lock->lock);
+ rcu_read_lock();
+ kfree_rcu(p); rcu_read_unlock();
+ ....
+ spin_unlock(&p->lock)
+ rcu_read_unlock(); // Ends grace period
+ rcu_do_batch()
+ kfree(p);
+ UAF -> rt_mutex_cmpxchg_release(&lock->lock...)
+
+Regular spinlocks keep preemption disabled accross the unlock operation,
+which provides full RCU protection, but the RT substitution fails to
+resemble that. Same applies for the rwlock substitution.
+
+Move the rcu_read_unlock() invocation past the unlock operations to match
+the non-RT semantics. This makes it asymmetric vs. rt_xxx_lock(), but
+that's harmless as the caller needs to hold RCU read lock across the lock
+operation. The migrate_enable() call stays before the unlock operation
+because there is no per CPU operation in the unlock path which would
+require migration to be kept disabled.
+
+Fixes: 0f383b6dc96e ("locking/spinlock: Provide RT variant")
+Reported-by: syzbot+000c800a02097aaa10ed@syzkaller.appspotmail.com
+Decoded-by: Jann Horn <jannh@google.com>
+Signed-off-by: Thomas Gleixner <tglx@kernel.org>
+Reviewed-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+Acked-by: Al Viro <viro@zeniv.linux.org.uk>
+Cc: stable@vger.kernel.org
+Link: https://patch.msgid.link/87jyrud75z.ffs@fw13
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ kernel/locking/spinlock_rt.c | 27 ++++++++++++++++++++++++---
+ 1 file changed, 24 insertions(+), 3 deletions(-)
+
+--- a/kernel/locking/spinlock_rt.c
++++ b/kernel/locking/spinlock_rt.c
+@@ -77,10 +77,27 @@ void __sched rt_spin_unlock(spinlock_t *
+ {
+ spin_release(&lock->dep_map, _RET_IP_);
+ migrate_enable();
+- rcu_read_unlock();
+
+ if (unlikely(!rt_mutex_cmpxchg_release(&lock->lock, current, NULL)))
+ rt_mutex_slowunlock(&lock->lock);
++
++ /*
++ * This must be last to prevent the following UAF:
++ *
++ * T1 T2
++ * spin_lock(&p->lock); rcu_read_lock();
++ * invalidate(p); p = rcu_dereference(ptr);
++ * rcu_assign_pointer(ptr, NULL); if (!p) return;
++ * spin_unlock(&p->lock); spin_lock(&p->lock);
++ * kfree_rcu(p); rcu_read_unlock();
++ * ....
++ * spin_unlock(&p->lock)
++ * rcu_read_unlock(); // Ends grace period
++ * rcu_do_batch()
++ * kfree(p);
++ * UAF -> rt_mutex_cmpxchg_release(&p->lock.lock...)
++ */
++ rcu_read_unlock();
+ }
+ EXPORT_SYMBOL(rt_spin_unlock);
+
+@@ -255,17 +272,21 @@ void __sched rt_read_unlock(rwlock_t *rw
+ {
+ rwlock_release(&rwlock->dep_map, _RET_IP_);
+ migrate_enable();
+- rcu_read_unlock();
+ rwbase_read_unlock(&rwlock->rwbase, TASK_RTLOCK_WAIT);
++
++ /* This must be last. See comment in rt_spin_unlock() */
++ rcu_read_unlock();
+ }
+ EXPORT_SYMBOL(rt_read_unlock);
+
+ void __sched rt_write_unlock(rwlock_t *rwlock)
+ {
+ rwlock_release(&rwlock->dep_map, _RET_IP_);
+- rcu_read_unlock();
+ migrate_enable();
+ rwbase_write_unlock(&rwlock->rwbase);
++
++ /* This must be last. See comment in rt_spin_unlock() */
++ rcu_read_unlock();
+ }
+ EXPORT_SYMBOL(rt_write_unlock);
+
--- /dev/null
+From stable+bounces-288352-greg=kroah.com@vger.kernel.org Thu Jul 23 16:21:24 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 23 Jul 2026 10:19:19 -0400
+Subject: lsm: infrastructure management of the sock security
+To: stable@vger.kernel.org
+Cc: Casey Schaufler <casey@schaufler-ca.com>, Paul Moore <paul@paul-moore.com>, Kees Cook <keescook@chromium.org>, John Johansen <john.johansen@canonical.com>, Stephen Smalley <stephen.smalley.work@gmail.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260723141920.3218519-1-sashal@kernel.org>
+
+From: Casey Schaufler <casey@schaufler-ca.com>
+
+[ Upstream commit 2aff9d20d50ac45dd13a013ef5231f4fb8912356 ]
+
+Move management of the sock->sk_security blob out
+of the individual security modules and into the security
+infrastructure. Instead of allocating the blobs from within
+the modules the modules tell the infrastructure how much
+space is required, and the space is allocated there.
+
+Acked-by: Paul Moore <paul@paul-moore.com>
+Reviewed-by: Kees Cook <keescook@chromium.org>
+Reviewed-by: John Johansen <john.johansen@canonical.com>
+Acked-by: Stephen Smalley <stephen.smalley.work@gmail.com>
+Signed-off-by: Casey Schaufler <casey@schaufler-ca.com>
+[PM: subject tweak]
+Signed-off-by: Paul Moore <paul@paul-moore.com>
+Stable-dep-of: 56acfeb10019 ("selinux: avoid sk_socket dereference in selinux_sctp_bind_connect()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ include/linux/lsm_hooks.h | 1
+ security/apparmor/include/net.h | 6 ++
+ security/apparmor/lsm.c | 38 ++++--------------
+ security/apparmor/net.c | 2
+ security/security.c | 36 ++++++++++++++++-
+ security/selinux/hooks.c | 80 +++++++++++++++++---------------------
+ security/selinux/include/objsec.h | 5 ++
+ security/selinux/netlabel.c | 23 +++++-----
+ security/smack/smack.h | 5 ++
+ security/smack/smack_lsm.c | 70 +++++++++++++++------------------
+ security/smack/smack_netfilter.c | 4 -
+ 11 files changed, 146 insertions(+), 124 deletions(-)
+
+--- a/include/linux/lsm_hooks.h
++++ b/include/linux/lsm_hooks.h
+@@ -61,6 +61,7 @@ struct lsm_blob_sizes {
+ int lbs_file;
+ int lbs_backing_file;
+ int lbs_inode;
++ int lbs_sock;
+ int lbs_superblock;
+ int lbs_ipc;
+ int lbs_msg_msg;
+--- a/security/apparmor/include/net.h
++++ b/security/apparmor/include/net.h
+@@ -51,8 +51,12 @@ struct aa_sk_ctx {
+ struct aa_label *peer;
+ };
+
+-#define SK_CTX(X) ((X)->sk_security)
+ #define SOCK_ctx(X) SOCK_INODE(X)->i_security
++static inline struct aa_sk_ctx *aa_sock(const struct sock *sk)
++{
++ return sk->sk_security + apparmor_blob_sizes.lbs_sock;
++}
++
+ #define DEFINE_AUDIT_NET(NAME, OP, SK, F, T, P) \
+ struct lsm_network_audit NAME ## _net = { .sk = (SK), \
+ .family = (F)}; \
+--- a/security/apparmor/lsm.c
++++ b/security/apparmor/lsm.c
+@@ -851,32 +851,14 @@ static int apparmor_task_kill(struct tas
+ }
+
+ /**
+- * apparmor_sk_alloc_security - allocate and attach the sk_security field
+- */
+-static int apparmor_sk_alloc_security(struct sock *sk, int family, gfp_t flags)
+-{
+- struct aa_sk_ctx *ctx;
+-
+- ctx = kzalloc(sizeof(*ctx), flags);
+- if (!ctx)
+- return -ENOMEM;
+-
+- SK_CTX(sk) = ctx;
+-
+- return 0;
+-}
+-
+-/**
+ * apparmor_sk_free_security - free the sk_security field
+ */
+ static void apparmor_sk_free_security(struct sock *sk)
+ {
+- struct aa_sk_ctx *ctx = SK_CTX(sk);
++ struct aa_sk_ctx *ctx = aa_sock(sk);
+
+- SK_CTX(sk) = NULL;
+ aa_put_label(ctx->label);
+ aa_put_label(ctx->peer);
+- kfree(ctx);
+ }
+
+ /**
+@@ -885,8 +867,8 @@ static void apparmor_sk_free_security(st
+ static void apparmor_sk_clone_security(const struct sock *sk,
+ struct sock *newsk)
+ {
+- struct aa_sk_ctx *ctx = SK_CTX(sk);
+- struct aa_sk_ctx *new = SK_CTX(newsk);
++ struct aa_sk_ctx *ctx = aa_sock(sk);
++ struct aa_sk_ctx *new = aa_sock(newsk);
+
+ if (new->label)
+ aa_put_label(new->label);
+@@ -940,7 +922,7 @@ static int apparmor_socket_post_create(s
+ label = aa_get_current_label();
+
+ if (sock->sk) {
+- struct aa_sk_ctx *ctx = SK_CTX(sock->sk);
++ struct aa_sk_ctx *ctx = aa_sock(sock->sk);
+
+ aa_put_label(ctx->label);
+ ctx->label = aa_get_label(label);
+@@ -1139,7 +1121,7 @@ static int apparmor_socket_shutdown(stru
+ */
+ static int apparmor_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
+ {
+- struct aa_sk_ctx *ctx = SK_CTX(sk);
++ struct aa_sk_ctx *ctx = aa_sock(sk);
+
+ if (!skb->secmark)
+ return 0;
+@@ -1159,7 +1141,7 @@ static int apparmor_socket_sock_rcv_skb(
+
+ static struct aa_label *sk_peer_label(struct sock *sk)
+ {
+- struct aa_sk_ctx *ctx = SK_CTX(sk);
++ struct aa_sk_ctx *ctx = aa_sock(sk);
+
+ if (ctx->peer)
+ return ctx->peer;
+@@ -1240,7 +1222,7 @@ static int apparmor_socket_getpeersec_dg
+ */
+ static void apparmor_sock_graft(struct sock *sk, struct socket *parent)
+ {
+- struct aa_sk_ctx *ctx = SK_CTX(sk);
++ struct aa_sk_ctx *ctx = aa_sock(sk);
+
+ if (!ctx->label)
+ ctx->label = aa_get_current_label();
+@@ -1250,7 +1232,7 @@ static void apparmor_sock_graft(struct s
+ static int apparmor_inet_conn_request(const struct sock *sk, struct sk_buff *skb,
+ struct request_sock *req)
+ {
+- struct aa_sk_ctx *ctx = SK_CTX(sk);
++ struct aa_sk_ctx *ctx = aa_sock(sk);
+
+ if (!skb->secmark)
+ return 0;
+@@ -1267,6 +1249,7 @@ struct lsm_blob_sizes apparmor_blob_size
+ .lbs_cred = sizeof(struct aa_label *),
+ .lbs_file = sizeof(struct aa_file_ctx),
+ .lbs_task = sizeof(struct aa_task_ctx),
++ .lbs_sock = sizeof(struct aa_sk_ctx),
+ };
+
+ static struct security_hook_list apparmor_hooks[] __ro_after_init = {
+@@ -1305,7 +1288,6 @@ static struct security_hook_list apparmo
+ LSM_HOOK_INIT(getprocattr, apparmor_getprocattr),
+ LSM_HOOK_INIT(setprocattr, apparmor_setprocattr),
+
+- LSM_HOOK_INIT(sk_alloc_security, apparmor_sk_alloc_security),
+ LSM_HOOK_INIT(sk_free_security, apparmor_sk_free_security),
+ LSM_HOOK_INIT(sk_clone_security, apparmor_sk_clone_security),
+
+@@ -1864,7 +1846,7 @@ static unsigned int apparmor_ip_postrout
+ if (sk == NULL)
+ return NF_ACCEPT;
+
+- ctx = SK_CTX(sk);
++ ctx = aa_sock(sk);
+ if (!apparmor_secmark_check(ctx->label, OP_SENDMSG, AA_MAY_SEND,
+ skb->secmark, sk))
+ return NF_ACCEPT;
+--- a/security/apparmor/net.c
++++ b/security/apparmor/net.c
+@@ -152,7 +152,7 @@ static int aa_label_sk_perm(const struct
+ const char *op, u32 request,
+ struct sock *sk)
+ {
+- struct aa_sk_ctx *ctx = SK_CTX(sk);
++ struct aa_sk_ctx *ctx = aa_sock(sk);
+ int error = 0;
+
+ AA_BUG(!label);
+--- a/security/security.c
++++ b/security/security.c
+@@ -30,6 +30,7 @@
+ #include <linux/string.h>
+ #include <linux/msg.h>
+ #include <net/flow.h>
++#include <net/sock.h>
+
+ /* How many LSMs were built into the kernel? */
+ #define LSM_COUNT (__end_lsm_info - __start_lsm_info)
+@@ -212,6 +213,7 @@ static void __init lsm_set_blob_sizes(st
+ lsm_set_blob_size(&needed->lbs_inode, &blob_sizes.lbs_inode);
+ lsm_set_blob_size(&needed->lbs_ipc, &blob_sizes.lbs_ipc);
+ lsm_set_blob_size(&needed->lbs_msg_msg, &blob_sizes.lbs_msg_msg);
++ lsm_set_blob_size(&needed->lbs_sock, &blob_sizes.lbs_sock);
+ lsm_set_blob_size(&needed->lbs_superblock, &blob_sizes.lbs_superblock);
+ lsm_set_blob_size(&needed->lbs_task, &blob_sizes.lbs_task);
+ lsm_set_blob_size(&needed->lbs_xattr_count,
+@@ -381,6 +383,7 @@ static void __init ordered_lsm_init(void
+ init_debug("inode blob size = %d\n", blob_sizes.lbs_inode);
+ init_debug("ipc blob size = %d\n", blob_sizes.lbs_ipc);
+ init_debug("msg_msg blob size = %d\n", blob_sizes.lbs_msg_msg);
++ init_debug("sock blob size = %d\n", blob_sizes.lbs_sock);
+ init_debug("superblock blob size = %d\n", blob_sizes.lbs_superblock);
+ init_debug("task blob size = %d\n", blob_sizes.lbs_task);
+ init_debug("xattr slots = %d\n", blob_sizes.lbs_xattr_count);
+@@ -4560,6 +4563,28 @@ int security_socket_getpeersec_dgram(str
+ EXPORT_SYMBOL(security_socket_getpeersec_dgram);
+
+ /**
++ * lsm_sock_alloc - allocate a composite sock blob
++ * @sock: the sock that needs a blob
++ * @priority: allocation mode
++ *
++ * Allocate the sock blob for all the modules
++ *
++ * Returns 0, or -ENOMEM if memory can't be allocated.
++ */
++static int lsm_sock_alloc(struct sock *sock, gfp_t priority)
++{
++ if (blob_sizes.lbs_sock == 0) {
++ sock->sk_security = NULL;
++ return 0;
++ }
++
++ sock->sk_security = kzalloc(blob_sizes.lbs_sock, priority);
++ if (sock->sk_security == NULL)
++ return -ENOMEM;
++ return 0;
++}
++
++/**
+ * security_sk_alloc() - Allocate and initialize a sock's LSM blob
+ * @sk: sock
+ * @family: protocol family
+@@ -4572,7 +4597,14 @@ EXPORT_SYMBOL(security_socket_getpeersec
+ */
+ int security_sk_alloc(struct sock *sk, int family, gfp_t priority)
+ {
+- return call_int_hook(sk_alloc_security, 0, sk, family, priority);
++ int rc = lsm_sock_alloc(sk, priority);
++
++ if (unlikely(rc))
++ return rc;
++ rc = call_int_hook(sk_alloc_security, 0, sk, family, priority);
++ if (unlikely(rc))
++ security_sk_free(sk);
++ return rc;
+ }
+
+ /**
+@@ -4584,6 +4616,8 @@ int security_sk_alloc(struct sock *sk, i
+ void security_sk_free(struct sock *sk)
+ {
+ call_void_hook(sk_free_security, sk);
++ kfree(sk->sk_security);
++ sk->sk_security = NULL;
+ }
+
+ /**
+--- a/security/selinux/hooks.c
++++ b/security/selinux/hooks.c
+@@ -4695,7 +4695,7 @@ static int socket_sockcreate_sid(const s
+
+ static int sock_has_perm(struct sock *sk, u32 perms)
+ {
+- struct sk_security_struct *sksec = sk->sk_security;
++ struct sk_security_struct *sksec = selinux_sock(sk);
+ struct common_audit_data ad;
+ struct lsm_network_audit net;
+
+@@ -4748,7 +4748,7 @@ static int selinux_socket_post_create(st
+ isec->initialized = LABEL_INITIALIZED;
+
+ if (sock->sk) {
+- sksec = sock->sk->sk_security;
++ sksec = selinux_sock(sock->sk);
+ sksec->sclass = sclass;
+ sksec->sid = sid;
+ /* Allows detection of the first association on this socket */
+@@ -4764,8 +4764,8 @@ static int selinux_socket_post_create(st
+ static int selinux_socket_socketpair(struct socket *socka,
+ struct socket *sockb)
+ {
+- struct sk_security_struct *sksec_a = socka->sk->sk_security;
+- struct sk_security_struct *sksec_b = sockb->sk->sk_security;
++ struct sk_security_struct *sksec_a = selinux_sock(socka->sk);
++ struct sk_security_struct *sksec_b = selinux_sock(sockb->sk);
+
+ sksec_a->peer_sid = sksec_b->sid;
+ sksec_b->peer_sid = sksec_a->sid;
+@@ -4780,7 +4780,7 @@ static int selinux_socket_socketpair(str
+ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen)
+ {
+ struct sock *sk = sock->sk;
+- struct sk_security_struct *sksec = sk->sk_security;
++ struct sk_security_struct *sksec = selinux_sock(sk);
+ u16 family;
+ int err;
+
+@@ -4920,7 +4920,7 @@ static int selinux_socket_connect_helper
+ struct sockaddr *address, int addrlen)
+ {
+ struct sock *sk = sock->sk;
+- struct sk_security_struct *sksec = sk->sk_security;
++ struct sk_security_struct *sksec = selinux_sock(sk);
+ int err;
+
+ err = sock_has_perm(sk, SOCKET__CONNECT);
+@@ -5115,9 +5115,9 @@ static int selinux_socket_unix_stream_co
+ struct sock *other,
+ struct sock *newsk)
+ {
+- struct sk_security_struct *sksec_sock = sock->sk_security;
+- struct sk_security_struct *sksec_other = other->sk_security;
+- struct sk_security_struct *sksec_new = newsk->sk_security;
++ struct sk_security_struct *sksec_sock = selinux_sock(sock);
++ struct sk_security_struct *sksec_other = selinux_sock(other);
++ struct sk_security_struct *sksec_new = selinux_sock(newsk);
+ struct common_audit_data ad;
+ struct lsm_network_audit net;
+ int err;
+@@ -5146,8 +5146,8 @@ static int selinux_socket_unix_stream_co
+ static int selinux_socket_unix_may_send(struct socket *sock,
+ struct socket *other)
+ {
+- struct sk_security_struct *ssec = sock->sk->sk_security;
+- struct sk_security_struct *osec = other->sk->sk_security;
++ struct sk_security_struct *ssec = selinux_sock(sock->sk);
++ struct sk_security_struct *osec = selinux_sock(other->sk);
+ struct common_audit_data ad;
+ struct lsm_network_audit net;
+
+@@ -5184,7 +5184,7 @@ static int selinux_sock_rcv_skb_compat(s
+ u16 family)
+ {
+ int err = 0;
+- struct sk_security_struct *sksec = sk->sk_security;
++ struct sk_security_struct *sksec = selinux_sock(sk);
+ u32 sk_sid = sksec->sid;
+ struct common_audit_data ad;
+ struct lsm_network_audit net;
+@@ -5213,7 +5213,7 @@ static int selinux_sock_rcv_skb_compat(s
+ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
+ {
+ int err, peerlbl_active, secmark_active;
+- struct sk_security_struct *sksec = sk->sk_security;
++ struct sk_security_struct *sksec = selinux_sock(sk);
+ u16 family = sk->sk_family;
+ u32 sk_sid = sksec->sid;
+ struct common_audit_data ad;
+@@ -5281,7 +5281,7 @@ static int selinux_socket_getpeersec_str
+ int err = 0;
+ char *scontext = NULL;
+ u32 scontext_len;
+- struct sk_security_struct *sksec = sock->sk->sk_security;
++ struct sk_security_struct *sksec = selinux_sock(sock->sk);
+ u32 peer_sid = SECSID_NULL;
+
+ if (sksec->sclass == SECCLASS_UNIX_STREAM_SOCKET ||
+@@ -5339,34 +5339,27 @@ out:
+
+ static int selinux_sk_alloc_security(struct sock *sk, int family, gfp_t priority)
+ {
+- struct sk_security_struct *sksec;
+-
+- sksec = kzalloc(sizeof(*sksec), priority);
+- if (!sksec)
+- return -ENOMEM;
++ struct sk_security_struct *sksec = selinux_sock(sk);
+
+ sksec->peer_sid = SECINITSID_UNLABELED;
+ sksec->sid = SECINITSID_UNLABELED;
+ sksec->sclass = SECCLASS_SOCKET;
+ selinux_netlbl_sk_security_reset(sksec);
+- sk->sk_security = sksec;
+
+ return 0;
+ }
+
+ static void selinux_sk_free_security(struct sock *sk)
+ {
+- struct sk_security_struct *sksec = sk->sk_security;
++ struct sk_security_struct *sksec = selinux_sock(sk);
+
+- sk->sk_security = NULL;
+ selinux_netlbl_sk_security_free(sksec);
+- kfree(sksec);
+ }
+
+ static void selinux_sk_clone_security(const struct sock *sk, struct sock *newsk)
+ {
+- struct sk_security_struct *sksec = sk->sk_security;
+- struct sk_security_struct *newsksec = newsk->sk_security;
++ struct sk_security_struct *sksec = selinux_sock(sk);
++ struct sk_security_struct *newsksec = selinux_sock(newsk);
+
+ newsksec->sid = sksec->sid;
+ newsksec->peer_sid = sksec->peer_sid;
+@@ -5380,7 +5373,7 @@ static void selinux_sk_getsecid(const st
+ if (!sk)
+ *secid = SECINITSID_ANY_SOCKET;
+ else {
+- const struct sk_security_struct *sksec = sk->sk_security;
++ const struct sk_security_struct *sksec = selinux_sock(sk);
+
+ *secid = sksec->sid;
+ }
+@@ -5390,7 +5383,7 @@ static void selinux_sock_graft(struct so
+ {
+ struct inode_security_struct *isec =
+ inode_security_novalidate(SOCK_INODE(parent));
+- struct sk_security_struct *sksec = sk->sk_security;
++ struct sk_security_struct *sksec = selinux_sock(sk);
+
+ if (sk->sk_family == PF_INET || sk->sk_family == PF_INET6 ||
+ sk->sk_family == PF_UNIX)
+@@ -5407,7 +5400,7 @@ static int selinux_sctp_process_new_asso
+ {
+ struct sock *sk = asoc->base.sk;
+ u16 family = sk->sk_family;
+- struct sk_security_struct *sksec = sk->sk_security;
++ struct sk_security_struct *sksec = selinux_sock(sk);
+ struct common_audit_data ad;
+ struct lsm_network_audit net;
+ int err;
+@@ -5462,7 +5455,7 @@ static int selinux_sctp_process_new_asso
+ static int selinux_sctp_assoc_request(struct sctp_association *asoc,
+ struct sk_buff *skb)
+ {
+- struct sk_security_struct *sksec = asoc->base.sk->sk_security;
++ struct sk_security_struct *sksec = selinux_sock(asoc->base.sk);
+ u32 conn_sid;
+ int err;
+
+@@ -5495,7 +5488,7 @@ static int selinux_sctp_assoc_request(st
+ static int selinux_sctp_assoc_established(struct sctp_association *asoc,
+ struct sk_buff *skb)
+ {
+- struct sk_security_struct *sksec = asoc->base.sk->sk_security;
++ struct sk_security_struct *sksec = selinux_sock(asoc->base.sk);
+
+ if (!selinux_policycap_extsockclass())
+ return 0;
+@@ -5594,8 +5587,8 @@ static int selinux_sctp_bind_connect(str
+ static void selinux_sctp_sk_clone(struct sctp_association *asoc, struct sock *sk,
+ struct sock *newsk)
+ {
+- struct sk_security_struct *sksec = sk->sk_security;
+- struct sk_security_struct *newsksec = newsk->sk_security;
++ struct sk_security_struct *sksec = selinux_sock(sk);
++ struct sk_security_struct *newsksec = selinux_sock(newsk);
+
+ /* If policy does not support SECCLASS_SCTP_SOCKET then call
+ * the non-sctp clone version.
+@@ -5611,8 +5604,8 @@ static void selinux_sctp_sk_clone(struct
+
+ static int selinux_mptcp_add_subflow(struct sock *sk, struct sock *ssk)
+ {
+- struct sk_security_struct *ssksec = ssk->sk_security;
+- struct sk_security_struct *sksec = sk->sk_security;
++ struct sk_security_struct *ssksec = selinux_sock(ssk);
++ struct sk_security_struct *sksec = selinux_sock(sk);
+
+ ssksec->sclass = sksec->sclass;
+ ssksec->sid = sksec->sid;
+@@ -5627,7 +5620,7 @@ static int selinux_mptcp_add_subflow(str
+ static int selinux_inet_conn_request(const struct sock *sk, struct sk_buff *skb,
+ struct request_sock *req)
+ {
+- struct sk_security_struct *sksec = sk->sk_security;
++ struct sk_security_struct *sksec = selinux_sock(sk);
+ int err;
+ u16 family = req->rsk_ops->family;
+ u32 connsid;
+@@ -5648,7 +5641,7 @@ static int selinux_inet_conn_request(con
+ static void selinux_inet_csk_clone(struct sock *newsk,
+ const struct request_sock *req)
+ {
+- struct sk_security_struct *newsksec = newsk->sk_security;
++ struct sk_security_struct *newsksec = selinux_sock(newsk);
+
+ newsksec->sid = req->secid;
+ newsksec->peer_sid = req->peer_secid;
+@@ -5665,7 +5658,7 @@ static void selinux_inet_csk_clone(struc
+ static void selinux_inet_conn_established(struct sock *sk, struct sk_buff *skb)
+ {
+ u16 family = sk->sk_family;
+- struct sk_security_struct *sksec = sk->sk_security;
++ struct sk_security_struct *sksec = selinux_sock(sk);
+
+ /* handle mapped IPv4 packets arriving via IPv6 sockets */
+ if (family == PF_INET6 && skb->protocol == htons(ETH_P_IP))
+@@ -5746,7 +5739,7 @@ static int selinux_tun_dev_attach_queue(
+ static int selinux_tun_dev_attach(struct sock *sk, void *security)
+ {
+ struct tun_security_struct *tunsec = security;
+- struct sk_security_struct *sksec = sk->sk_security;
++ struct sk_security_struct *sksec = selinux_sock(sk);
+
+ /* we don't currently perform any NetLabel based labeling here and it
+ * isn't clear that we would want to do so anyway; while we could apply
+@@ -5869,7 +5862,7 @@ static unsigned int selinux_ip_output(vo
+ return NF_ACCEPT;
+
+ /* standard practice, label using the parent socket */
+- sksec = sk->sk_security;
++ sksec = selinux_sock(sk);
+ sid = sksec->sid;
+ } else
+ sid = SECINITSID_KERNEL;
+@@ -5892,7 +5885,7 @@ static unsigned int selinux_ip_postroute
+ sk = skb_to_full_sk(skb);
+ if (sk == NULL)
+ return NF_ACCEPT;
+- sksec = sk->sk_security;
++ sksec = selinux_sock(sk);
+
+ ad_net_init_from_iif(&ad, &net, state->out->ifindex, state->pf);
+ if (selinux_parse_skb(skb, &ad, NULL, 0, &proto))
+@@ -5981,7 +5974,7 @@ static unsigned int selinux_ip_postroute
+ u32 skb_sid;
+ struct sk_security_struct *sksec;
+
+- sksec = sk->sk_security;
++ sksec = selinux_sock(sk);
+ if (selinux_skb_peerlbl_sid(skb, family, &skb_sid))
+ return NF_DROP;
+ /* At this point, if the returned skb peerlbl is SECSID_NULL
+@@ -6010,7 +6003,7 @@ static unsigned int selinux_ip_postroute
+ } else {
+ /* Locally generated packet, fetch the security label from the
+ * associated socket. */
+- struct sk_security_struct *sksec = sk->sk_security;
++ struct sk_security_struct *sksec = selinux_sock(sk);
+ peer_sid = sksec->sid;
+ secmark_perm = PACKET__SEND;
+ }
+@@ -6053,7 +6046,7 @@ static int selinux_netlink_send(struct s
+ unsigned int data_len = skb->len;
+ unsigned char *data = skb->data;
+ struct nlmsghdr *nlh;
+- struct sk_security_struct *sksec = sk->sk_security;
++ struct sk_security_struct *sksec = selinux_sock(sk);
+ u16 sclass = sksec->sclass;
+ u32 perm;
+
+@@ -7008,6 +7001,7 @@ struct lsm_blob_sizes selinux_blob_sizes
+ .lbs_inode = sizeof(struct inode_security_struct),
+ .lbs_ipc = sizeof(struct ipc_security_struct),
+ .lbs_msg_msg = sizeof(struct msg_security_struct),
++ .lbs_sock = sizeof(struct sk_security_struct),
+ .lbs_superblock = sizeof(struct superblock_security_struct),
+ .lbs_xattr_count = SELINUX_INODE_INIT_XATTRS,
+ };
+--- a/security/selinux/include/objsec.h
++++ b/security/selinux/include/objsec.h
+@@ -205,4 +205,9 @@ static inline struct superblock_security
+ return superblock->s_security + selinux_blob_sizes.lbs_superblock;
+ }
+
++static inline struct sk_security_struct *selinux_sock(const struct sock *sock)
++{
++ return sock->sk_security + selinux_blob_sizes.lbs_sock;
++}
++
+ #endif /* _SELINUX_OBJSEC_H_ */
+--- a/security/selinux/netlabel.c
++++ b/security/selinux/netlabel.c
+@@ -17,6 +17,7 @@
+ #include <linux/gfp.h>
+ #include <linux/ip.h>
+ #include <linux/ipv6.h>
++#include <linux/lsm_hooks.h>
+ #include <net/sock.h>
+ #include <net/netlabel.h>
+ #include <net/ip.h>
+@@ -68,7 +69,7 @@ static int selinux_netlbl_sidlookup_cach
+ static struct netlbl_lsm_secattr *selinux_netlbl_sock_genattr(struct sock *sk)
+ {
+ int rc;
+- struct sk_security_struct *sksec = sk->sk_security;
++ struct sk_security_struct *sksec = selinux_sock(sk);
+ struct netlbl_lsm_secattr *secattr;
+
+ if (sksec->nlbl_secattr != NULL)
+@@ -100,7 +101,7 @@ static struct netlbl_lsm_secattr *selinu
+ const struct sock *sk,
+ u32 sid)
+ {
+- struct sk_security_struct *sksec = sk->sk_security;
++ struct sk_security_struct *sksec = selinux_sock(sk);
+ struct netlbl_lsm_secattr *secattr = sksec->nlbl_secattr;
+
+ if (secattr == NULL)
+@@ -240,7 +241,7 @@ int selinux_netlbl_skbuff_setsid(struct
+ * being labeled by it's parent socket, if it is just exit */
+ sk = skb_to_full_sk(skb);
+ if (sk != NULL) {
+- struct sk_security_struct *sksec = sk->sk_security;
++ struct sk_security_struct *sksec = selinux_sock(sk);
+
+ if (sksec->nlbl_state != NLBL_REQSKB)
+ return 0;
+@@ -277,7 +278,7 @@ int selinux_netlbl_sctp_assoc_request(st
+ {
+ int rc;
+ struct netlbl_lsm_secattr secattr;
+- struct sk_security_struct *sksec = asoc->base.sk->sk_security;
++ struct sk_security_struct *sksec = selinux_sock(asoc->base.sk);
+ struct sockaddr_in addr4;
+ struct sockaddr_in6 addr6;
+
+@@ -356,7 +357,7 @@ inet_conn_request_return:
+ */
+ void selinux_netlbl_inet_csk_clone(struct sock *sk, u16 family)
+ {
+- struct sk_security_struct *sksec = sk->sk_security;
++ struct sk_security_struct *sksec = selinux_sock(sk);
+
+ if (family == PF_INET)
+ sksec->nlbl_state = NLBL_LABELED;
+@@ -374,8 +375,8 @@ void selinux_netlbl_inet_csk_clone(struc
+ */
+ void selinux_netlbl_sctp_sk_clone(struct sock *sk, struct sock *newsk)
+ {
+- struct sk_security_struct *sksec = sk->sk_security;
+- struct sk_security_struct *newsksec = newsk->sk_security;
++ struct sk_security_struct *sksec = selinux_sock(sk);
++ struct sk_security_struct *newsksec = selinux_sock(newsk);
+
+ newsksec->nlbl_state = sksec->nlbl_state;
+ }
+@@ -393,7 +394,7 @@ void selinux_netlbl_sctp_sk_clone(struct
+ int selinux_netlbl_socket_post_create(struct sock *sk, u16 family)
+ {
+ int rc;
+- struct sk_security_struct *sksec = sk->sk_security;
++ struct sk_security_struct *sksec = selinux_sock(sk);
+ struct netlbl_lsm_secattr *secattr;
+
+ if (family != PF_INET && family != PF_INET6)
+@@ -507,7 +508,7 @@ int selinux_netlbl_socket_setsockopt(str
+ {
+ int rc = 0;
+ struct sock *sk = sock->sk;
+- struct sk_security_struct *sksec = sk->sk_security;
++ struct sk_security_struct *sksec = selinux_sock(sk);
+ struct netlbl_lsm_secattr secattr;
+
+ if (selinux_netlbl_option(level, optname) &&
+@@ -545,7 +546,7 @@ static int selinux_netlbl_socket_connect
+ struct sockaddr *addr)
+ {
+ int rc;
+- struct sk_security_struct *sksec = sk->sk_security;
++ struct sk_security_struct *sksec = selinux_sock(sk);
+ struct netlbl_lsm_secattr *secattr;
+
+ /* connected sockets are allowed to disconnect when the address family
+@@ -584,7 +585,7 @@ static int selinux_netlbl_socket_connect
+ int selinux_netlbl_socket_connect_locked(struct sock *sk,
+ struct sockaddr *addr)
+ {
+- struct sk_security_struct *sksec = sk->sk_security;
++ struct sk_security_struct *sksec = selinux_sock(sk);
+
+ if (sksec->nlbl_state != NLBL_REQSKB &&
+ sksec->nlbl_state != NLBL_CONNLABELED)
+--- a/security/smack/smack.h
++++ b/security/smack/smack.h
+@@ -361,6 +361,11 @@ static inline struct superblock_smack *s
+ return superblock->s_security + smack_blob_sizes.lbs_superblock;
+ }
+
++static inline struct socket_smack *smack_sock(const struct sock *sock)
++{
++ return sock->sk_security + smack_blob_sizes.lbs_sock;
++}
++
+ /*
+ * Is the directory transmuting?
+ */
+--- a/security/smack/smack_lsm.c
++++ b/security/smack/smack_lsm.c
+@@ -1572,7 +1572,7 @@ static int smack_inode_getsecurity(struc
+ if (sock == NULL || sock->sk == NULL)
+ return -EOPNOTSUPP;
+
+- ssp = sock->sk->sk_security;
++ ssp = smack_sock(sock->sk);
+
+ if (strcmp(name, XATTR_SMACK_IPIN) == 0)
+ isp = ssp->smk_in;
+@@ -1960,7 +1960,7 @@ static int smack_file_receive(struct fil
+
+ if (inode->i_sb->s_magic == SOCKFS_MAGIC) {
+ sock = SOCKET_I(inode);
+- ssp = sock->sk->sk_security;
++ ssp = smack_sock(sock->sk);
+ tsp = smack_cred(current_cred());
+ /*
+ * If the receiving process can't write to the
+@@ -2380,11 +2380,7 @@ static void smack_task_to_inode(struct t
+ static int smack_sk_alloc_security(struct sock *sk, int family, gfp_t gfp_flags)
+ {
+ struct smack_known *skp = smk_of_current();
+- struct socket_smack *ssp;
+-
+- ssp = kzalloc(sizeof(struct socket_smack), gfp_flags);
+- if (ssp == NULL)
+- return -ENOMEM;
++ struct socket_smack *ssp = smack_sock(sk);
+
+ /*
+ * Sockets created by kernel threads receive web label.
+@@ -2398,11 +2394,10 @@ static int smack_sk_alloc_security(struc
+ }
+ ssp->smk_packet = NULL;
+
+- sk->sk_security = ssp;
+-
+ return 0;
+ }
+
++#ifdef SMACK_IPV6_PORT_LABELING
+ /**
+ * smack_sk_free_security - Free a socket blob
+ * @sk: the socket
+@@ -2411,7 +2406,6 @@ static int smack_sk_alloc_security(struc
+ */
+ static void smack_sk_free_security(struct sock *sk)
+ {
+-#ifdef SMACK_IPV6_PORT_LABELING
+ struct smk_port_label *spp;
+
+ if (sk->sk_family == PF_INET6) {
+@@ -2424,9 +2418,8 @@ static void smack_sk_free_security(struc
+ }
+ rcu_read_unlock();
+ }
+-#endif
+- kfree(sk->sk_security);
+ }
++#endif
+
+ /**
+ * smack_sk_clone_security - Copy security context
+@@ -2437,8 +2430,8 @@ static void smack_sk_free_security(struc
+ */
+ static void smack_sk_clone_security(const struct sock *sk, struct sock *newsk)
+ {
+- struct socket_smack *ssp_old = sk->sk_security;
+- struct socket_smack *ssp_new = newsk->sk_security;
++ struct socket_smack *ssp_old = smack_sock(sk);
++ struct socket_smack *ssp_new = smack_sock(newsk);
+
+ *ssp_new = *ssp_old;
+ }
+@@ -2556,7 +2549,7 @@ static struct smack_known *smack_ipv6hos
+ */
+ static int smack_netlbl_add(struct sock *sk)
+ {
+- struct socket_smack *ssp = sk->sk_security;
++ struct socket_smack *ssp = smack_sock(sk);
+ struct smack_known *skp = ssp->smk_out;
+ int rc;
+
+@@ -2588,7 +2581,7 @@ static int smack_netlbl_add(struct sock
+ */
+ static void smack_netlbl_delete(struct sock *sk)
+ {
+- struct socket_smack *ssp = sk->sk_security;
++ struct socket_smack *ssp = smack_sock(sk);
+
+ /*
+ * Take the label off the socket if one is set.
+@@ -2620,7 +2613,7 @@ static int smk_ipv4_check(struct sock *s
+ struct smack_known *skp;
+ int rc = 0;
+ struct smack_known *hkp;
+- struct socket_smack *ssp = sk->sk_security;
++ struct socket_smack *ssp = smack_sock(sk);
+ struct smk_audit_info ad;
+
+ rcu_read_lock();
+@@ -2695,7 +2688,7 @@ static void smk_ipv6_port_label(struct s
+ {
+ struct sock *sk = sock->sk;
+ struct sockaddr_in6 *addr6;
+- struct socket_smack *ssp = sock->sk->sk_security;
++ struct socket_smack *ssp = smack_sock(sock->sk);
+ struct smk_port_label *spp;
+ unsigned short port = 0;
+
+@@ -2783,7 +2776,7 @@ static int smk_ipv6_port_check(struct so
+ int act)
+ {
+ struct smk_port_label *spp;
+- struct socket_smack *ssp = sk->sk_security;
++ struct socket_smack *ssp = smack_sock(sk);
+ struct smack_known *skp = NULL;
+ unsigned short port;
+ struct smack_known *object;
+@@ -2886,7 +2879,7 @@ static int smack_inode_setsecurity(struc
+ if (sock == NULL || sock->sk == NULL)
+ return -EOPNOTSUPP;
+
+- ssp = sock->sk->sk_security;
++ ssp = smack_sock(sock->sk);
+
+ if (strcmp(name, XATTR_SMACK_IPIN) == 0)
+ ssp->smk_in = skp;
+@@ -2934,7 +2927,7 @@ static int smack_socket_post_create(stru
+ * Sockets created by kernel threads receive web label.
+ */
+ if (unlikely(current->flags & PF_KTHREAD)) {
+- ssp = sock->sk->sk_security;
++ ssp = smack_sock(sock->sk);
+ ssp->smk_in = &smack_known_web;
+ ssp->smk_out = &smack_known_web;
+ }
+@@ -2959,8 +2952,8 @@ static int smack_socket_post_create(stru
+ static int smack_socket_socketpair(struct socket *socka,
+ struct socket *sockb)
+ {
+- struct socket_smack *asp = socka->sk->sk_security;
+- struct socket_smack *bsp = sockb->sk->sk_security;
++ struct socket_smack *asp = smack_sock(socka->sk);
++ struct socket_smack *bsp = smack_sock(sockb->sk);
+
+ asp->smk_packet = bsp->smk_out;
+ bsp->smk_packet = asp->smk_out;
+@@ -3025,7 +3018,7 @@ static int smack_socket_connect(struct s
+ if (__is_defined(SMACK_IPV6_SECMARK_LABELING))
+ rsp = smack_ipv6host_label(sip);
+ if (rsp != NULL) {
+- struct socket_smack *ssp = sock->sk->sk_security;
++ struct socket_smack *ssp = smack_sock(sock->sk);
+
+ rc = smk_ipv6_check(ssp->smk_out, rsp, sip,
+ SMK_CONNECTING);
+@@ -3763,9 +3756,9 @@ static int smack_unix_stream_connect(str
+ {
+ struct smack_known *skp;
+ struct smack_known *okp;
+- struct socket_smack *ssp = sock->sk_security;
+- struct socket_smack *osp = other->sk_security;
+- struct socket_smack *nsp = newsk->sk_security;
++ struct socket_smack *ssp = smack_sock(sock);
++ struct socket_smack *osp = smack_sock(other);
++ struct socket_smack *nsp = smack_sock(newsk);
+ struct smk_audit_info ad;
+ int rc = 0;
+ #ifdef CONFIG_AUDIT
+@@ -3817,8 +3810,8 @@ static int smack_unix_stream_connect(str
+ */
+ static int smack_unix_may_send(struct socket *sock, struct socket *other)
+ {
+- struct socket_smack *ssp = sock->sk->sk_security;
+- struct socket_smack *osp = other->sk->sk_security;
++ struct socket_smack *ssp = smack_sock(sock->sk);
++ struct socket_smack *osp = smack_sock(other->sk);
+ struct smk_audit_info ad;
+ int rc;
+
+@@ -3855,7 +3848,7 @@ static int smack_socket_sendmsg(struct s
+ struct sockaddr_in6 *sap = (struct sockaddr_in6 *) msg->msg_name;
+ #endif
+ #ifdef SMACK_IPV6_SECMARK_LABELING
+- struct socket_smack *ssp = sock->sk->sk_security;
++ struct socket_smack *ssp = smack_sock(sock->sk);
+ struct smack_known *rsp;
+ #endif
+ int rc = 0;
+@@ -4067,7 +4060,7 @@ static struct smack_known *smack_from_ne
+ netlbl_secattr_init(&secattr);
+
+ if (sk)
+- ssp = sk->sk_security;
++ ssp = smack_sock(sk);
+
+ if (netlbl_skbuff_getattr(skb, family, &secattr) == 0) {
+ skp = smack_from_secattr(&secattr, ssp);
+@@ -4089,7 +4082,7 @@ static struct smack_known *smack_from_ne
+ */
+ static int smack_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
+ {
+- struct socket_smack *ssp = sk->sk_security;
++ struct socket_smack *ssp = smack_sock(sk);
+ struct smack_known *skp = NULL;
+ int rc = 0;
+ struct smk_audit_info ad;
+@@ -4193,7 +4186,7 @@ static int smack_socket_getpeersec_strea
+ u32 slen = 1;
+ int rc = 0;
+
+- ssp = sock->sk->sk_security;
++ ssp = smack_sock(sock->sk);
+ if (ssp->smk_packet != NULL) {
+ rcp = ssp->smk_packet->smk_known;
+ slen = strlen(rcp) + 1;
+@@ -4243,7 +4236,7 @@ static int smack_socket_getpeersec_dgram
+
+ switch (family) {
+ case PF_UNIX:
+- ssp = sock->sk->sk_security;
++ ssp = smack_sock(sock->sk);
+ s = ssp->smk_out->smk_secid;
+ break;
+ case PF_INET:
+@@ -4292,7 +4285,7 @@ static void smack_sock_graft(struct sock
+ (sk->sk_family != PF_INET && sk->sk_family != PF_INET6))
+ return;
+
+- ssp = sk->sk_security;
++ ssp = smack_sock(sk);
+ ssp->smk_in = skp;
+ ssp->smk_out = skp;
+ /* cssp->smk_packet is already set in smack_inet_csk_clone() */
+@@ -4312,7 +4305,7 @@ static int smack_inet_conn_request(const
+ {
+ u16 family = sk->sk_family;
+ struct smack_known *skp;
+- struct socket_smack *ssp = sk->sk_security;
++ struct socket_smack *ssp = smack_sock(sk);
+ struct sockaddr_in addr;
+ struct iphdr *hdr;
+ struct smack_known *hskp;
+@@ -4398,7 +4391,7 @@ static int smack_inet_conn_request(const
+ static void smack_inet_csk_clone(struct sock *sk,
+ const struct request_sock *req)
+ {
+- struct socket_smack *ssp = sk->sk_security;
++ struct socket_smack *ssp = smack_sock(sk);
+ struct smack_known *skp;
+
+ if (req->peer_secid != 0) {
+@@ -4968,6 +4961,7 @@ struct lsm_blob_sizes smack_blob_sizes _
+ .lbs_inode = sizeof(struct inode_smack),
+ .lbs_ipc = sizeof(struct smack_known *),
+ .lbs_msg_msg = sizeof(struct smack_known *),
++ .lbs_sock = sizeof(struct socket_smack),
+ .lbs_superblock = sizeof(struct superblock_smack),
+ .lbs_xattr_count = SMACK_INODE_INIT_XATTRS,
+ };
+@@ -5084,7 +5078,9 @@ static struct security_hook_list smack_h
+ LSM_HOOK_INIT(socket_getpeersec_stream, smack_socket_getpeersec_stream),
+ LSM_HOOK_INIT(socket_getpeersec_dgram, smack_socket_getpeersec_dgram),
+ LSM_HOOK_INIT(sk_alloc_security, smack_sk_alloc_security),
++#ifdef SMACK_IPV6_PORT_LABELING
+ LSM_HOOK_INIT(sk_free_security, smack_sk_free_security),
++#endif
+ LSM_HOOK_INIT(sk_clone_security, smack_sk_clone_security),
+ LSM_HOOK_INIT(sock_graft, smack_sock_graft),
+ LSM_HOOK_INIT(inet_conn_request, smack_inet_conn_request),
+--- a/security/smack/smack_netfilter.c
++++ b/security/smack/smack_netfilter.c
+@@ -26,8 +26,8 @@ static unsigned int smack_ip_output(void
+ struct socket_smack *ssp;
+ struct smack_known *skp;
+
+- if (sk && sk->sk_security) {
+- ssp = sk->sk_security;
++ if (sk) {
++ ssp = smack_sock(sk);
+ skp = ssp->smk_out;
+ skb->secmark = skp->smk_secid;
+ }
--- /dev/null
+From stable+bounces-289088-greg=kroah.com@vger.kernel.org Sat Jul 25 12:55:29 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 25 Jul 2026 06:55:21 -0400
+Subject: mm/mm_init: fix pageblock migratetype for ZONE_DEVICE compound pages
+To: stable@vger.kernel.org
+Cc: Muchun Song <songmuchun@bytedance.com>, "Mike Rapoport (Microsoft)" <rppt@kernel.org>, Oscar Salvador <osalvador@suse.de>, "David Hildenbrand (Arm)" <david@kernel.org>, "Liam R. Howlett" <liam@infradead.org>, "Aneesh Kumar K.V" <aneesh.kumar@linux.ibm.com>, Joao Martins <joao.m.martins@oracle.com>, Lorenzo Stoakes <ljs@kernel.org>, Madhavan Srinivasan <maddy@linux.ibm.com>, Michael Ellerman <mpe@ellerman.id.au>, Michal Hocko <mhocko@suse.com>, Nicholas Piggin <npiggin@gmail.com>, Suren Baghdasaryan <surenb@google.com>, Vlastimil Babka <vbabka@kernel.org>, Andrew Morton <akpm@linux-foundation.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260725105521.1855856-1-sashal@kernel.org>
+
+From: Muchun Song <songmuchun@bytedance.com>
+
+[ Upstream commit 94405c6136839f7c462249c8b4b957bcb9527a9d ]
+
+The memmap_init_zone_device() function only initializes the migratetype of
+the first pageblock of a compound page. If the compound page size exceeds
+pageblock_nr_pages (e.g., 1GB hugepages with 2MB pageblocks), subsequent
+pageblocks in the compound page remain uninitialized.
+
+Move the migratetype initialization out of __init_zone_device_page() and
+into a separate pageblock_migratetype_init_range() function. This
+iterates over the entire PFN range of the memory, ensuring that all
+pageblocks are correctly initialized.
+
+Also remove the stale confusing comment about MEMINIT_HOTPLUG above the
+migratetype setting since it is an obsolete relic from commit 966cf44f637e
+("mm: defer ZONE_DEVICE page initialization to the point where we init
+pgmap") and no longer makes sense here.
+
+Link: https://lore.kernel.org/20260428081855.1249045-6-songmuchun@bytedance.com
+Fixes: c4386bd8ee3a ("mm/memremap: add ZONE_DEVICE support for compound pages")
+Signed-off-by: Muchun Song <songmuchun@bytedance.com>
+Reviewed-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
+Reviewed-by: Oscar Salvador <osalvador@suse.de>
+Acked-by: David Hildenbrand (Arm) <david@kernel.org>
+Acked-by: Liam R. Howlett <liam@infradead.org>
+Cc: "Aneesh Kumar K.V" <aneesh.kumar@linux.ibm.com>
+Cc: Joao Martins <joao.m.martins@oracle.com>
+Cc: Lorenzo Stoakes <ljs@kernel.org>
+Cc: Madhavan Srinivasan <maddy@linux.ibm.com>
+Cc: Michael Ellerman <mpe@ellerman.id.au>
+Cc: Michal Hocko <mhocko@suse.com>
+Cc: Nicholas Piggin <npiggin@gmail.com>
+Cc: Suren Baghdasaryan <surenb@google.com>
+Cc: Vlastimil Babka <vbabka@kernel.org>
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ mm/mm_init.c | 34 +++++++++++++++++++---------------
+ 1 file changed, 19 insertions(+), 15 deletions(-)
+
+--- a/mm/mm_init.c
++++ b/mm/mm_init.c
+@@ -649,6 +649,20 @@ static inline void fixup_hashdist(void)
+ static inline void fixup_hashdist(void) {}
+ #endif /* CONFIG_NUMA */
+
++#ifdef CONFIG_ZONE_DEVICE
++static __meminit void pageblock_migratetype_init_range(unsigned long pfn,
++ unsigned long nr_pages, int migratetype)
++{
++ const unsigned long end = pfn + nr_pages;
++
++ for (pfn = pageblock_align(pfn); pfn < end; pfn += pageblock_nr_pages) {
++ set_pageblock_migratetype(pfn_to_page(pfn), migratetype);
++ if (IS_ALIGNED(pfn, PAGES_PER_SECTION))
++ cond_resched();
++ }
++}
++#endif
++
+ #ifdef CONFIG_DEFERRED_STRUCT_PAGE_INIT
+ static inline void pgdat_set_deferred_range(pg_data_t *pgdat)
+ {
+@@ -993,21 +1007,6 @@ static void __ref __init_zone_device_pag
+ page->zone_device_data = NULL;
+
+ /*
+- * Mark the block movable so that blocks are reserved for
+- * movable at startup. This will force kernel allocations
+- * to reserve their blocks rather than leaking throughout
+- * the address space during boot when many long-lived
+- * kernel allocations are made.
+- *
+- * Please note that MEMINIT_HOTPLUG path doesn't clear memmap
+- * because this is done early in section_activate()
+- */
+- if (pageblock_aligned(pfn)) {
+- set_pageblock_migratetype(page, MIGRATE_MOVABLE);
+- cond_resched();
+- }
+-
+- /*
+ * ZONE_DEVICE pages are released directly to the driver page allocator
+ * which will set the page count to 1 when allocating the page.
+ */
+@@ -1098,6 +1097,9 @@ void __ref memmap_init_zone_device(struc
+
+ __init_zone_device_page(page, pfn, zone_idx, nid, pgmap);
+
++ if (IS_ALIGNED(pfn, PAGES_PER_SECTION))
++ cond_resched();
++
+ if (pfns_per_compound == 1)
+ continue;
+
+@@ -1105,6 +1107,8 @@ void __ref memmap_init_zone_device(struc
+ compound_nr_pages(pfn, altmap, pgmap));
+ }
+
++ pageblock_migratetype_init_range(start_pfn, nr_pages, MIGRATE_MOVABLE);
++
+ pr_debug("%s initialised %lu pages in %ums\n", __func__,
+ nr_pages, jiffies_to_msecs(jiffies - start));
+ }
--- /dev/null
+From stable+bounces-289774-greg=kroah.com@vger.kernel.org Tue Jul 28 02:59:39 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 20:58:03 -0400
+Subject: mmc: vub300: fix use-after-free on disconnect
+To: stable@vger.kernel.org
+Cc: Johan Hovold <johan@kernel.org>, Binbin Zhou <zhoubinbin@loongson.cn>, Ulf Hansson <ulf.hansson@linaro.org>
+Message-ID: <20260728005806.2694893-1-sashal@kernel.org>
+
+From: Johan Hovold <johan@kernel.org>
+
+The vub300 driver maintains an explicit reference count for the
+controller and its driver data and the last reference can in theory be
+dropped after the driver has been unbound.
+
+This specifically means that the controller allocation must not be
+device managed as that can lead to use-after-free.
+
+Note that the lifetime is currently also incorrectly tied the parent USB
+device rather than interface, which can lead to memory leaks if the
+driver is unbound without its device being physically disconnected (e.g.
+on probe deferral).
+
+Fix both issues by reverting to non-managed allocation of the controller.
+
+Fixes: dcfdd698dc52 ("mmc: vub300: Use devm_mmc_alloc_host() helper")
+Cc: stable@vger.kernel.org # 6.17+
+Cc: Binbin Zhou <zhoubinbin@loongson.cn>
+Signed-off-by: Johan Hovold <johan@kernel.org>
+Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
+(cherry picked from commit 8f4d20a710225ec7a565f6a0459862d3b1f32330)
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/mmc/host/vub300.c | 12 ++++++------
+ 1 file changed, 6 insertions(+), 6 deletions(-)
+
+--- a/drivers/mmc/host/vub300.c
++++ b/drivers/mmc/host/vub300.c
+@@ -2279,7 +2279,7 @@ static int vub300_probe(struct usb_inter
+ dev_err(&vub300->udev->dev,
+ "Could not find two sets of bulk-in/out endpoint pairs\n");
+ retval = -EINVAL;
+- goto error5;
++ goto err_free_host;
+ }
+ retval =
+ usb_control_msg(vub300->udev, usb_rcvctrlpipe(vub300->udev, 0),
+@@ -2288,14 +2288,14 @@ static int vub300_probe(struct usb_inter
+ 0x0000, 0x0000, &vub300->hc_info,
+ sizeof(vub300->hc_info), 1000);
+ if (retval < 0)
+- goto error5;
++ goto err_free_host;
+ retval =
+ usb_control_msg(vub300->udev, usb_sndctrlpipe(vub300->udev, 0),
+ SET_ROM_WAIT_STATES,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ firmware_rom_wait_states, 0x0000, NULL, 0, 1000);
+ if (retval < 0)
+- goto error5;
++ goto err_free_host;
+ dev_info(&vub300->udev->dev,
+ "operating_mode = %s %s %d MHz %s %d byte USB packets\n",
+ (mmc->caps & MMC_CAP_SDIO_IRQ) ? "IRQs" : "POLL",
+@@ -2310,7 +2310,7 @@ static int vub300_probe(struct usb_inter
+ 0x0000, 0x0000, &vub300->system_port_status,
+ sizeof(vub300->system_port_status), 1000);
+ if (retval < 0) {
+- goto error5;
++ goto err_free_host;
+ } else if (sizeof(vub300->system_port_status) == retval) {
+ vub300->card_present =
+ (0x0001 & vub300->system_port_status.port_flags) ? 1 : 0;
+@@ -2318,7 +2318,7 @@ static int vub300_probe(struct usb_inter
+ (0x0010 & vub300->system_port_status.port_flags) ? 1 : 0;
+ } else {
+ retval = -EINVAL;
+- goto error5;
++ goto err_free_host;
+ }
+ usb_set_intfdata(interface, vub300);
+ INIT_DELAYED_WORK(&vub300->pollwork, vub300_pollwork_thread);
+@@ -2348,7 +2348,7 @@ static int vub300_probe(struct usb_inter
+ return 0;
+ error6:
+ del_timer_sync(&vub300->inactivity_timer);
+-error5:
++err_free_host:
+ mmc_free_host(mmc);
+ /*
+ * and hence also frees vub300
--- /dev/null
+From stable+bounces-289776-greg=kroah.com@vger.kernel.org Tue Jul 28 02:59:45 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 20:58:05 -0400
+Subject: mmc: vub300: fix use-after-free on probe failure
+To: stable@vger.kernel.org
+Cc: Guangshuo Li <lgs201920130244@gmail.com>, Johan Hovold <johan@kernel.org>, Ulf Hansson <ulfh@kernel.org>
+Message-ID: <20260728005806.2694893-3-sashal@kernel.org>
+
+From: Guangshuo Li <lgs201920130244@gmail.com>
+
+The vub300 driver lifetime-manages its controller state using
+vub300->kref, with vub300_delete() freeing the mmc host when the last
+reference is dropped. The probe error path after the inactivity timer has
+been armed still bypasses that lifetime rule, however, and falls through
+to mmc_free_host() directly if mmc_add_host() fails.
+
+The race window is between arming the inactivity timer and reaching the
+probe error unwind after mmc_add_host() fails:
+
+ probe thread timer/workqueue
+ ------------ ---------------
+ kref_init(&vub300->kref) ref = 1
+ kref_get(&vub300->kref) ref = 2, timer ref
+ add_timer(inactivity_timer) fires after one second
+ |
+ | race window
+ |<---------------------------------------------------->
+ |
+ mmc_add_host(mmc)
+ inactivity timer fires
+ vub300_queue_dead_work()
+ kref_get() ref = 3
+ queue_work(deadwork)
+ mmc_add_host() fails
+ timer_delete_sync()
+ mmc_free_host(mmc)
+ frees vub300
+ deadwork runs
+ use-after-free
+
+The inactivity timeout is one second, so this would require
+mmc_add_host() to both fail and take more than one second to do so. This
+is unlikely to happen in practice, but the error path is still wrong.
+
+timer_delete_sync() only waits for the timer callback itself. It does
+not flush deadwork that the callback may already have queued. As a
+result, queued deadwork can still hold a kref while the probe error path
+directly frees the backing mmc host, including the vub300 storage.
+
+Fix this by using the same lifetime mechanism as disconnect. Clear
+vub300->interface so that the timer callback and any queued deadwork
+return early and drop their references, then drop the initial probe
+reference and return without falling through to err_free_host.
+
+Fixes: 0613ad2401f8 ("mmc: vub300: fix return value check of mmc_add_host()")
+Signed-off-by: Guangshuo Li <lgs201920130244@gmail.com>
+Reviewed-by: Johan Hovold <johan@kernel.org>
+Cc: stable@vger.kernel.org
+Signed-off-by: Ulf Hansson <ulfh@kernel.org>
+(cherry picked from commit a3b5f242997a3be7404112fd48784881560aea57)
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/mmc/host/vub300.c | 10 +++++++---
+ 1 file changed, 7 insertions(+), 3 deletions(-)
+
+--- a/drivers/mmc/host/vub300.c
++++ b/drivers/mmc/host/vub300.c
+@@ -2343,12 +2343,16 @@ static int vub300_probe(struct usb_inter
+ interface_to_InterfaceNumber(interface));
+ retval = mmc_add_host(mmc);
+ if (retval)
+- goto err_delete_timer;
++ goto err_stop_io;
+
+ return 0;
+
+-err_delete_timer:
+- timer_delete_sync(&vub300->inactivity_timer);
++err_stop_io:
++ vub300->interface = NULL;
++ kref_put(&vub300->kref, vub300_delete);
++
++ return retval;
++
+ err_free_host:
+ mmc_free_host(mmc);
+ /*
--- /dev/null
+From stable+bounces-289775-greg=kroah.com@vger.kernel.org Tue Jul 28 02:59:41 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 20:58:04 -0400
+Subject: mmc: vub300: rename probe error labels
+To: stable@vger.kernel.org
+Cc: Johan Hovold <johan@kernel.org>, Ulf Hansson <ulf.hansson@linaro.org>
+Message-ID: <20260728005806.2694893-2-sashal@kernel.org>
+
+From: Johan Hovold <johan@kernel.org>
+
+Error labels should be named after what they do.
+
+Rename the probe error labels.
+
+Signed-off-by: Johan Hovold <johan@kernel.org>
+Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
+(cherry picked from commit 5b8b35d6f4fa758dd5e8ae18526ea1c73f6787e0)
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/mmc/host/vub300.c | 20 +++++++++++---------
+ 1 file changed, 11 insertions(+), 9 deletions(-)
+
+--- a/drivers/mmc/host/vub300.c
++++ b/drivers/mmc/host/vub300.c
+@@ -2114,19 +2114,19 @@ static int vub300_probe(struct usb_inter
+ command_out_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!command_out_urb) {
+ retval = -ENOMEM;
+- goto error0;
++ goto err_put_udev;
+ }
+ command_res_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!command_res_urb) {
+ retval = -ENOMEM;
+- goto error1;
++ goto err_free_out_urb;
+ }
+ /* this also allocates memory for our VUB300 mmc host device */
+ mmc = mmc_alloc_host(sizeof(struct vub300_mmc_host), &udev->dev);
+ if (!mmc) {
+ retval = -ENOMEM;
+ dev_err(&udev->dev, "not enough memory for the mmc_host\n");
+- goto error4;
++ goto err_free_res_urb;
+ }
+ /* MMC core transfer sizes tunable parameters */
+ mmc->caps = 0;
+@@ -2343,23 +2343,25 @@ static int vub300_probe(struct usb_inter
+ interface_to_InterfaceNumber(interface));
+ retval = mmc_add_host(mmc);
+ if (retval)
+- goto error6;
++ goto err_delete_timer;
+
+ return 0;
+-error6:
+- del_timer_sync(&vub300->inactivity_timer);
++
++err_delete_timer:
++ timer_delete_sync(&vub300->inactivity_timer);
+ err_free_host:
+ mmc_free_host(mmc);
+ /*
+ * and hence also frees vub300
+ * which is contained at the end of struct mmc
+ */
+-error4:
++err_free_res_urb:
+ usb_free_urb(command_res_urb);
+-error1:
++err_free_out_urb:
+ usb_free_urb(command_out_urb);
+-error0:
++err_put_udev:
+ usb_put_dev(udev);
++
+ return retval;
+ }
+
--- /dev/null
+From stable+bounces-289096-greg=kroah.com@vger.kernel.org Sat Jul 25 14:15:04 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 25 Jul 2026 08:14:47 -0400
+Subject: mtd: maps: vmu-flash: fix fault in unaligned fixup
+To: stable@vger.kernel.org
+Cc: Florian Fuchs <fuchsfl@gmail.com>, Miquel Raynal <miquel.raynal@bootlin.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260725121447.2570457-1-sashal@kernel.org>
+
+From: Florian Fuchs <fuchsfl@gmail.com>
+
+[ Upstream commit 79d1661502c6e4b6f626185cef72cf2fa78116e1 ]
+
+Use kzalloc_obj() / kzalloc_objs() to allocate the memcard structs,
+instead of kmalloc_obj() / kmalloc_objs() to prevent access to
+uninitialized data.
+
+Fixes runtime error: Fault in unaligned fixup: 0000 [#1] at
+mtd_get_fact_prot_info.
+
+Fixes: 47a72688fae7 ("mtd: flash mapping support for Dreamcast VMU.")
+Cc: stable@vger.kernel.org
+Signed-off-by: Florian Fuchs <fuchsfl@gmail.com>
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/mtd/maps/vmu-flash.c | 8 +++-----
+ 1 file changed, 3 insertions(+), 5 deletions(-)
+
+--- a/drivers/mtd/maps/vmu-flash.c
++++ b/drivers/mtd/maps/vmu-flash.c
+@@ -610,7 +610,7 @@ static int vmu_connect(struct maple_devi
+
+ basic_flash_data = be32_to_cpu(mdev->devinfo.function_data[c - 1]);
+
+- card = kmalloc(sizeof(struct memcard), GFP_KERNEL);
++ card = kzalloc_obj(struct memcard);
+ if (!card) {
+ error = -ENOMEM;
+ goto fail_nomem;
+@@ -628,15 +628,13 @@ static int vmu_connect(struct maple_devi
+ * Not sure there are actually any multi-partition devices in the
+ * real world, but the hardware supports them, so, so will we
+ */
+- card->parts = kmalloc_array(card->partitions, sizeof(struct vmupart),
+- GFP_KERNEL);
++ card->parts = kzalloc_objs(struct vmupart, card->partitions);
+ if (!card->parts) {
+ error = -ENOMEM;
+ goto fail_partitions;
+ }
+
+- card->mtd = kmalloc_array(card->partitions, sizeof(struct mtd_info),
+- GFP_KERNEL);
++ card->mtd = kzalloc_objs(struct mtd_info, card->partitions);
+ if (!card->mtd) {
+ error = -ENOMEM;
+ goto fail_mtd_info;
--- /dev/null
+From stable+bounces-289264-greg=kroah.com@vger.kernel.org Sun Jul 26 14:38:52 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 26 Jul 2026 08:38:42 -0400
+Subject: mtd: rawnand: Add a helper for calculating a page index
+To: stable@vger.kernel.org
+Cc: Miquel Raynal <miquel.raynal@bootlin.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260726123844.64620-1-sashal@kernel.org>
+
+From: Miquel Raynal <miquel.raynal@bootlin.com>
+
+[ Upstream commit df9803bf5a91e3599f12b53c94722f2c4e144a86 ]
+
+For LUN crossing boundaries, it is handy to know what is the index of
+the last page in a LUN. This helper will soon be reused. At the same
+time I rename page_per_lun to ppl in the calling function to clarify the
+lines.
+
+Cc: stable@vger.kernel.org # v6.7
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Link: https://lore.kernel.org/linux-mtd/20240223115545.354541-3-miquel.raynal@bootlin.com
+Stable-dep-of: 8e4531667d71 ("mtd: rawnand: Pause continuous reads at block boundaries")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/mtd/nand/raw/nand_base.c | 16 +++++++++++-----
+ 1 file changed, 11 insertions(+), 5 deletions(-)
+
+--- a/drivers/mtd/nand/raw/nand_base.c
++++ b/drivers/mtd/nand/raw/nand_base.c
+@@ -1212,19 +1212,25 @@ static int nand_lp_exec_read_page_op(str
+ return nand_exec_op(chip, &op);
+ }
+
++static unsigned int rawnand_last_page_of_lun(unsigned int pages_per_lun, unsigned int lun)
++{
++ /* lun is expected to be very small */
++ return (lun * pages_per_lun) + pages_per_lun - 1;
++}
++
+ static void rawnand_cap_cont_reads(struct nand_chip *chip)
+ {
+ struct nand_memory_organization *memorg;
+- unsigned int pages_per_lun, first_lun, last_lun;
++ unsigned int ppl, first_lun, last_lun;
+
+ memorg = nanddev_get_memorg(&chip->base);
+- pages_per_lun = memorg->pages_per_eraseblock * memorg->eraseblocks_per_lun;
+- first_lun = chip->cont_read.first_page / pages_per_lun;
+- last_lun = chip->cont_read.last_page / pages_per_lun;
++ ppl = memorg->pages_per_eraseblock * memorg->eraseblocks_per_lun;
++ first_lun = chip->cont_read.first_page / ppl;
++ last_lun = chip->cont_read.last_page / ppl;
+
+ /* Prevent sequential cache reads across LUN boundaries */
+ if (first_lun != last_lun)
+- chip->cont_read.pause_page = first_lun * pages_per_lun + pages_per_lun - 1;
++ chip->cont_read.pause_page = rawnand_last_page_of_lun(ppl, first_lun);
+ else
+ chip->cont_read.pause_page = chip->cont_read.last_page;
+ }
--- /dev/null
+From stable+bounces-289265-greg=kroah.com@vger.kernel.org Sun Jul 26 14:38:57 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 26 Jul 2026 08:38:43 -0400
+Subject: mtd: rawnand: Ensure all continuous terms are always in sync
+To: stable@vger.kernel.org
+Cc: Miquel Raynal <miquel.raynal@bootlin.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260726123844.64620-2-sashal@kernel.org>
+
+From: Miquel Raynal <miquel.raynal@bootlin.com>
+
+[ Upstream commit 6fb075fca63c3486612986eeff84ed4179644038 ]
+
+While crossing a LUN boundary, it is probably safer (and clearer) to
+keep all members of the continuous read structure aligned, including the
+pause page (which is the last page of the lun or the last page of the
+continuous read). Once these members properly in sync, we can use the
+rawnand_cap_cont_reads() helper everywhere to "prepare" the next
+continuous read if there is one.
+
+Fixes: bbcd80f53a5e ("mtd: rawnand: Prevent crossing LUN boundaries during sequential reads")
+Cc: stable@vger.kernel.org
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Link: https://lore.kernel.org/linux-mtd/20240223115545.354541-4-miquel.raynal@bootlin.com
+Stable-dep-of: 8e4531667d71 ("mtd: rawnand: Pause continuous reads at block boundaries")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/mtd/nand/raw/nand_base.c | 23 ++++++++++++++---------
+ 1 file changed, 14 insertions(+), 9 deletions(-)
+
+--- a/drivers/mtd/nand/raw/nand_base.c
++++ b/drivers/mtd/nand/raw/nand_base.c
+@@ -1233,6 +1233,15 @@ static void rawnand_cap_cont_reads(struc
+ chip->cont_read.pause_page = rawnand_last_page_of_lun(ppl, first_lun);
+ else
+ chip->cont_read.pause_page = chip->cont_read.last_page;
++
++ if (chip->cont_read.first_page == chip->cont_read.pause_page) {
++ chip->cont_read.first_page++;
++ chip->cont_read.pause_page = min(chip->cont_read.last_page,
++ rawnand_last_page_of_lun(ppl, first_lun + 1));
++ }
++
++ if (chip->cont_read.first_page >= chip->cont_read.last_page)
++ chip->cont_read.ongoing = false;
+ }
+
+ static int nand_lp_exec_cont_read_page_op(struct nand_chip *chip, unsigned int page,
+@@ -1299,12 +1308,11 @@ static int nand_lp_exec_cont_read_page_o
+ if (!chip->cont_read.ongoing)
+ return 0;
+
+- if (page == chip->cont_read.pause_page &&
+- page != chip->cont_read.last_page) {
+- chip->cont_read.first_page = chip->cont_read.pause_page + 1;
+- rawnand_cap_cont_reads(chip);
+- } else if (page == chip->cont_read.last_page) {
++ if (page == chip->cont_read.last_page) {
+ chip->cont_read.ongoing = false;
++ } else if (page == chip->cont_read.pause_page) {
++ chip->cont_read.first_page++;
++ rawnand_cap_cont_reads(chip);
+ }
+
+ return 0;
+@@ -3512,10 +3520,7 @@ static void rawnand_cont_read_skip_first
+ return;
+
+ chip->cont_read.first_page++;
+- if (chip->cont_read.first_page == chip->cont_read.pause_page)
+- chip->cont_read.first_page++;
+- if (chip->cont_read.first_page >= chip->cont_read.last_page)
+- chip->cont_read.ongoing = false;
++ rawnand_cap_cont_reads(chip);
+ }
+
+ /**
--- /dev/null
+From stable+bounces-289266-greg=kroah.com@vger.kernel.org Sun Jul 26 14:38:56 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 26 Jul 2026 08:38:44 -0400
+Subject: mtd: rawnand: Pause continuous reads at block boundaries
+To: stable@vger.kernel.org
+Cc: Miquel Raynal <miquel.raynal@bootlin.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260726123844.64620-3-sashal@kernel.org>
+
+From: Miquel Raynal <miquel.raynal@bootlin.com>
+
+[ Upstream commit 8e4531667d718e2e9b193928cf9b2497fa0d01ef ]
+
+Some chips do not support sequential cached reads past block
+boundaries, like Winbond. In practice when using UBI, this should very
+rarely happen, but let's make sure it never happens.
+
+Cc: stable@vger.kernel.org
+Fixes: 003fe4b9545b ("mtd: rawnand: Support for sequential cache reads")
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/mtd/nand/raw/nand_base.c | 24 ++++++++++++------------
+ 1 file changed, 12 insertions(+), 12 deletions(-)
+
+--- a/drivers/mtd/nand/raw/nand_base.c
++++ b/drivers/mtd/nand/raw/nand_base.c
+@@ -1212,32 +1212,32 @@ static int nand_lp_exec_read_page_op(str
+ return nand_exec_op(chip, &op);
+ }
+
+-static unsigned int rawnand_last_page_of_lun(unsigned int pages_per_lun, unsigned int lun)
++static unsigned int rawnand_last_page_of_block(unsigned int ppb, unsigned int block)
+ {
+- /* lun is expected to be very small */
+- return (lun * pages_per_lun) + pages_per_lun - 1;
++ /* block is expected to be very small */
++ return (block * ppb) + ppb - 1;
+ }
+
+ static void rawnand_cap_cont_reads(struct nand_chip *chip)
+ {
+ struct nand_memory_organization *memorg;
+- unsigned int ppl, first_lun, last_lun;
++ unsigned int ppb, first_block, last_block;
+
+ memorg = nanddev_get_memorg(&chip->base);
+- ppl = memorg->pages_per_eraseblock * memorg->eraseblocks_per_lun;
+- first_lun = chip->cont_read.first_page / ppl;
+- last_lun = chip->cont_read.last_page / ppl;
+-
+- /* Prevent sequential cache reads across LUN boundaries */
+- if (first_lun != last_lun)
+- chip->cont_read.pause_page = rawnand_last_page_of_lun(ppl, first_lun);
++ ppb = memorg->pages_per_eraseblock;
++ first_block = chip->cont_read.first_page / ppb;
++ last_block = chip->cont_read.last_page / ppb;
++
++ /* Prevent sequential cache reads across block boundaries */
++ if (first_block != last_block)
++ chip->cont_read.pause_page = rawnand_last_page_of_block(ppb, first_block);
+ else
+ chip->cont_read.pause_page = chip->cont_read.last_page;
+
+ if (chip->cont_read.first_page == chip->cont_read.pause_page) {
+ chip->cont_read.first_page++;
+ chip->cont_read.pause_page = min(chip->cont_read.last_page,
+- rawnand_last_page_of_lun(ppl, first_lun + 1));
++ rawnand_last_page_of_block(ppb, first_block + 1));
+ }
+
+ if (chip->cont_read.first_page >= chip->cont_read.last_page)
--- /dev/null
+From stable+bounces-289994-greg=kroah.com@vger.kernel.org Tue Jul 28 15:37:43 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Jul 2026 09:30:33 -0400
+Subject: net: ip6_tunnel: require CAP_NET_ADMIN in the device netns for changelink
+To: stable@vger.kernel.org
+Cc: Maoyi Xie <maoyixie.tju@gmail.com>, Xiao Liang <shaw.leon@gmail.com>, Kuniyuki Iwashima <kuniyu@google.com>, Jakub Kicinski <kuba@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260728133033.3965038-1-sashal@kernel.org>
+
+From: Maoyi Xie <maoyixie.tju@gmail.com>
+
+[ Upstream commit 2496fa0b7d180b3ad356b514e7ff93bb14e6140a ]
+
+ip6_tnl_changelink() operates on at most two netns, dev_net(dev) and the
+tunnel link netns t->net. They differ once the device is created in or
+moved to a netns other than the one the request runs in. The rtnl
+changelink path checks CAP_NET_ADMIN only against dev_net(dev), so a
+caller privileged there but not in t->net can rewrite a tunnel that
+lives in t->net.
+
+Gate ip6_tnl_changelink() on rtnl_dev_link_net_capable() at its top,
+before any attribute is parsed.
+
+Reported-by: Xiao Liang <shaw.leon@gmail.com>
+Closes: https://lore.kernel.org/netdev/CABAhCOSzP1vaThGV35_VnsRCb=87_CPjPVsTHbq905k8A+BuUg@mail.gmail.com/
+Fixes: 0bd8762824e7 ("ip6tnl: add x-netns support")
+Cc: stable@vger.kernel.org
+Signed-off-by: Maoyi Xie <maoyixie.tju@gmail.com>
+Reviewed-by: Kuniyuki Iwashima <kuniyu@google.com>
+Link: https://patch.msgid.link/20260612085941.3158249-5-maoyixie.tju@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/ipv6/ip6_tunnel.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/net/ipv6/ip6_tunnel.c
++++ b/net/ipv6/ip6_tunnel.c
+@@ -2055,6 +2055,9 @@ static int ip6_tnl_changelink(struct net
+ struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id);
+ struct ip_tunnel_encap ipencap;
+
++ if (!rtnl_dev_link_net_capable(dev, net))
++ return -EPERM;
++
+ if (dev == ip6n->fb_tnl_dev)
+ return -EINVAL;
+
--- /dev/null
+From stable+bounces-290251-greg=kroah.com@vger.kernel.org Wed Jul 29 05:20:34 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Jul 2026 23:20:20 -0400
+Subject: net: ipa: fix SMEM state handle leaks in SMP2P init
+To: stable@vger.kernel.org
+Cc: Haoxiang Li <haoxiang_li2024@163.com>, Larysa Zaremba <larysa.zaremba@intel.com>, Alex Elder <elder@riscstar.com>, Jakub Kicinski <kuba@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260729032020.1223984-1-sashal@kernel.org>
+
+From: Haoxiang Li <haoxiang_li2024@163.com>
+
+[ Upstream commit 96ca1e658ae459276292bd6d971ab5d8c7e0379a ]
+
+ipa_smp2p_init() acquires two Qualcomm SMEM state handles with
+qcom_smem_state_get(). However, neither the init error paths
+nor ipa_smp2p_exit() release them.
+
+Release both handles with qcom_smem_state_put() in the init
+error paths and in ipa_smp2p_exit().
+
+Fixes: 530f9216a953 ("soc: qcom: ipa: AP/modem communications")
+Cc: stable@vger.kernel.org
+Signed-off-by: Haoxiang Li <haoxiang_li2024@163.com>
+Reviewed-by: Larysa Zaremba <larysa.zaremba@intel.com>
+Reviewed-by: Alex Elder <elder@riscstar.com>
+Link: https://patch.msgid.link/20260624065955.2822765-1-haoxiang_li2024@163.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+[ kzalloc_obj() context line kept as kzalloc(sizeof(*smp2p), GFP_KERNEL) since ipa_smp2p.c was not yet converted in this tree ]
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/net/ipa/ipa_smp2p.c | 30 ++++++++++++++++++++++--------
+ 1 file changed, 22 insertions(+), 8 deletions(-)
+
+--- a/drivers/net/ipa/ipa_smp2p.c
++++ b/drivers/net/ipa/ipa_smp2p.c
+@@ -233,19 +233,27 @@ int ipa_smp2p_init(struct ipa *ipa, bool
+ &valid_bit);
+ if (IS_ERR(valid_state))
+ return PTR_ERR(valid_state);
+- if (valid_bit >= 32) /* BITS_PER_U32 */
+- return -EINVAL;
++ if (valid_bit >= 32) { /* BITS_PER_U32 */
++ ret = -EINVAL;
++ goto err_valid_state_put;
++ }
+
+ enabled_state = qcom_smem_state_get(dev, "ipa-clock-enabled",
+ &enabled_bit);
+- if (IS_ERR(enabled_state))
+- return PTR_ERR(enabled_state);
+- if (enabled_bit >= 32) /* BITS_PER_U32 */
+- return -EINVAL;
++ if (IS_ERR(enabled_state)) {
++ ret = PTR_ERR(enabled_state);
++ goto err_valid_state_put;
++ }
++ if (enabled_bit >= 32) { /* BITS_PER_U32 */
++ ret = -EINVAL;
++ goto err_enabled_state_put;
++ }
+
+ smp2p = kzalloc(sizeof(*smp2p), GFP_KERNEL);
+- if (!smp2p)
+- return -ENOMEM;
++ if (!smp2p) {
++ ret = -ENOMEM;
++ goto err_enabled_state_put;
++ }
+
+ smp2p->ipa = ipa;
+
+@@ -290,6 +298,10 @@ err_null_smp2p:
+ ipa->smp2p = NULL;
+ mutex_destroy(&smp2p->mutex);
+ kfree(smp2p);
++err_enabled_state_put:
++ qcom_smem_state_put(enabled_state);
++err_valid_state_put:
++ qcom_smem_state_put(valid_state);
+
+ return ret;
+ }
+@@ -306,6 +318,8 @@ void ipa_smp2p_exit(struct ipa *ipa)
+ ipa_smp2p_power_release(ipa);
+ ipa->smp2p = NULL;
+ mutex_destroy(&smp2p->mutex);
++ qcom_smem_state_put(smp2p->enabled_state);
++ qcom_smem_state_put(smp2p->valid_state);
+ kfree(smp2p);
+ }
+
--- /dev/null
+From stable+bounces-290235-greg=kroah.com@vger.kernel.org Wed Jul 29 04:40:20 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Jul 2026 22:40:09 -0400
+Subject: net: macb: drop in-flight Tx SKBs on close
+To: stable@vger.kernel.org
+Cc: "Théo Lebrun" <theo.lebrun@bootlin.com>, "Nicolai Buchwitz" <nb@tipi-net.de>, "Paolo Abeni" <pabeni@redhat.com>, "Sasha Levin" <sashal@kernel.org>
+Message-ID: <20260729024009.1127546-1-sashal@kernel.org>
+
+From: Théo Lebrun <theo.lebrun@bootlin.com>
+
+[ Upstream commit 27f575836cfebbf872dec020428742b10650a955 ]
+
+The MACB driver has since forever leaked the outgoing SKBs that
+have not yet been marked as completed. They live in queue->tx_skb
+which gets freed without remorse nor checking.
+
+macb_free_consistent() gets called in a few codepaths, but only close will
+trigger the added expressions. In macb_open() and macb_alloc_consistent()
+failure cases, queues' tx_skb just got allocated and are empty.
+
+Fixes: 89e5785fc8a6 ("[PATCH] Atmel MACB ethernet driver")
+Cc: stable@vger.kernel.org
+Reviewed-by: Nicolai Buchwitz <nb@tipi-net.de>
+Signed-off-by: Théo Lebrun <theo.lebrun@bootlin.com>
+Link: https://patch.msgid.link/20260702-macb-drop-tx-v4-1-1c833eebdbc8@bootlin.com
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+[ adjusted context to free each queue's Tx/Rx rings individually since 6.12 lacks the single-dma_alloc_coherent refactor ]
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/net/ethernet/cadence/macb_main.c | 22 ++++++++++++++++++++--
+ 1 file changed, 20 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/ethernet/cadence/macb_main.c
++++ b/drivers/net/ethernet/cadence/macb_main.c
+@@ -2582,8 +2582,26 @@ static void macb_free_consistent(struct
+ bp->macbgem_ops.mog_free_rx_buffers(bp);
+
+ for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) {
+- kfree(queue->tx_skb);
+- queue->tx_skb = NULL;
++ if (queue->tx_skb) {
++ unsigned int dropped = 0, tail;
++
++ for (tail = queue->tx_tail; tail != queue->tx_head;
++ tail++) {
++ if (macb_tx_skb(queue, tail)->skb)
++ dropped++;
++ macb_tx_unmap(bp, macb_tx_skb(queue, tail), 0);
++ }
++
++ queue->stats.tx_dropped += dropped;
++ bp->dev->stats.tx_dropped += dropped;
++
++ kfree(queue->tx_skb);
++ queue->tx_skb = NULL;
++ }
++
++ queue->tx_head = 0;
++ queue->tx_tail = 0;
++
+ if (queue->tx_ring) {
+ size = TX_RING_BYTES(bp) + bp->tx_bd_rd_prefetch;
+ dma_free_coherent(&bp->pdev->dev, size,
--- /dev/null
+From stable+bounces-289837-greg=kroah.com@vger.kernel.org Tue Jul 28 05:27:25 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 23:25:58 -0400
+Subject: net: mana: Validate the packet length reported by the NIC
+To: stable@vger.kernel.org
+Cc: Dexuan Cui <decui@microsoft.com>, Haiyang Zhang <haiyangz@microsoft.com>, Paolo Abeni <pabeni@redhat.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260728032558.3151321-1-sashal@kernel.org>
+
+From: Dexuan Cui <decui@microsoft.com>
+
+[ Upstream commit 2e2a83b4998af4384e677d3b2ac08565274279bf ]
+
+Validate the packet length reported in the RX CQE before passing it
+to skb processing. The CQE is supplied by the NIC device and should
+not be blindly trusted.
+
+Cc: stable@vger.kernel.org
+Reviewed-by: Haiyang Zhang <haiyangz@microsoft.com>
+Signed-off-by: Dexuan Cui <decui@microsoft.com>
+Fixes: ca9c54d2d6a5 ("net: mana: Add a driver for Microsoft Azure Network Adapter (MANA)")
+Link: https://patch.msgid.link/20260702041237.617719-2-decui@microsoft.com
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/net/ethernet/microsoft/mana/mana_en.c | 13 +++++++++++++
+ 1 file changed, 13 insertions(+)
+
+--- a/drivers/net/ethernet/microsoft/mana/mana_en.c
++++ b/drivers/net/ethernet/microsoft/mana/mana_en.c
+@@ -1739,6 +1739,19 @@ static void mana_process_rx_cqe(struct m
+ rxbuf_oob = &rxq->rx_oobs[curr];
+ WARN_ON_ONCE(rxbuf_oob->wqe_inf.wqe_size_in_bu != 1);
+
++ if (unlikely(pktlen > rxq->datasize)) {
++ /* Increase it even if mana_rx_skb() isn't called. */
++ rxq->rx_cq.work_done++;
++
++ ++ndev->stats.rx_dropped;
++ netdev_warn_once(ndev,
++ "Dropped oversized RX packet: len=%u, datasize=%u\n",
++ pktlen, rxq->datasize);
++
++ /* Reuse the RX buffer since rxbuf_oob is unchanged. */
++ goto drop;
++ }
++
+ mana_refill_rx_oob(dev, rxq, rxbuf_oob, &old_buf, &old_fp);
+
+ /* Unsuccessful refill will have old_buf == NULL.
--- /dev/null
+From stable+bounces-288375-greg=kroah.com@vger.kernel.org Thu Jul 23 17:09:04 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 23 Jul 2026 10:53:18 -0400
+Subject: netfilter: nf_conntrack_sip: remove net variable shadowing
+To: stable@vger.kernel.org
+Cc: Florian Westphal <fw@strlen.de>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260723145319.3272168-1-sashal@kernel.org>
+
+From: Florian Westphal <fw@strlen.de>
+
+[ Upstream commit 7970d6aaf710db166de98c5356a260089896fae5 ]
+
+net is already set, derived from nf_conn.
+I don't see how the device could be living in a different netns
+than the conntrack entry.
+
+Remove the extra variable and re-use existing one.
+
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Stable-dep-of: e5e24a365a5e ("netfilter: nf_conntrack_sip: validate skb_dst() before accessing it")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/netfilter/nf_conntrack_sip.c | 3 +--
+ 1 file changed, 1 insertion(+), 2 deletions(-)
+
+--- a/net/netfilter/nf_conntrack_sip.c
++++ b/net/netfilter/nf_conntrack_sip.c
+@@ -948,9 +948,8 @@ static int set_expected_rtp_rtcp(struct
+ saddr = &ct->tuplehash[!dir].tuple.src.u3;
+ } else if (sip_external_media) {
+ struct net_device *dev = skb_dst(skb)->dev;
+- struct net *net = dev_net(dev);
+- struct flowi fl;
+ struct dst_entry *dst = NULL;
++ struct flowi fl;
+
+ memset(&fl, 0, sizeof(fl));
+
--- /dev/null
+From stable+bounces-288376-greg=kroah.com@vger.kernel.org Thu Jul 23 17:03:48 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 23 Jul 2026 10:53:19 -0400
+Subject: netfilter: nf_conntrack_sip: validate skb_dst() before accessing it
+To: stable@vger.kernel.org
+Cc: Pablo Neira Ayuso <pablo@netfilter.org>, Ren Wei <n05ec@lzu.edu.cn>, Florian Westphal <fw@strlen.de>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260723145319.3272168-2-sashal@kernel.org>
+
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+
+[ Upstream commit e5e24a365a5e024efef63cc49abb345fbd4852c5 ]
+
+tc ingress and openvswitch do not guarantee routing information to be
+available. These subsystems use the conntrack helper infrastructure, and
+the SIP helper relies on the skb_dst() to be present if
+sip_external_media is set to 1 (which is disabled by default as a module
+parameter).
+
+This effectively disables the sip_external_media toggle for these
+subsystems without resulting in a crash.
+
+Fixes: cae3a2627520 ("openvswitch: Allow attaching helpers to ct action")
+Fixes: b57dc7c13ea9 ("net/sched: Introduce action ct")
+Cc: stable@vger.kernel.org
+Reported-by: Ren Wei <n05ec@lzu.edu.cn>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/netfilter/nf_conntrack_sip.c | 7 +++++--
+ 1 file changed, 5 insertions(+), 2 deletions(-)
+
+--- a/net/netfilter/nf_conntrack_sip.c
++++ b/net/netfilter/nf_conntrack_sip.c
+@@ -947,7 +947,6 @@ static int set_expected_rtp_rtcp(struct
+ return NF_ACCEPT;
+ saddr = &ct->tuplehash[!dir].tuple.src.u3;
+ } else if (sip_external_media) {
+- struct net_device *dev = skb_dst(skb)->dev;
+ struct dst_entry *dst = NULL;
+ struct flowi fl;
+
+@@ -969,7 +968,11 @@ static int set_expected_rtp_rtcp(struct
+ * through the same interface as the signalling peer.
+ */
+ if (dst) {
+- bool external_media = (dst->dev == dev);
++ const struct dst_entry *this_dst = skb_dst(skb);
++ bool external_media = false;
++
++ if (this_dst && dst->dev == this_dst->dev)
++ external_media = true;
+
+ dst_release(dst);
+ if (external_media)
--- /dev/null
+From stable+bounces-288525-greg=kroah.com@vger.kernel.org Thu Jul 23 21:59:53 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 23 Jul 2026 15:58:41 -0400
+Subject: netfilter: nf_tables: remove register tracking infrastructure
+To: stable@vger.kernel.org
+Cc: Florian Westphal <fw@strlen.de>, Jakub Kicinski <kuba@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260723195842.3683768-2-sashal@kernel.org>
+
+From: Florian Westphal <fw@strlen.de>
+
+[ Upstream commit 6b94d081f81dd524626f7aab2b98a9de335edb72 ]
+
+This facility was disabled in commit
+9e539c5b6d9c ("netfilter: nf_tables: disable expression reduction infra"),
+because not all nft_exprs guarantee they will update the destination
+register: some may set NFT_BREAK instead to cancel evaluation of the
+rule.
+
+This has been dead code ever since.
+There are no plans to salvage this at this time, so remove this.
+
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Link: https://patch.msgid.link/20260224205048.4718-10-fw@strlen.de
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Stable-dep-of: d07955dd34ec ("netfilter: nft_fib: reject fib expression on the netdev egress hook")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ include/net/netfilter/nf_tables.h | 32 ---------
+ include/net/netfilter/nft_fib.h | 2
+ include/net/netfilter/nft_meta.h | 3
+ net/bridge/netfilter/nft_meta_bridge.c | 20 -----
+ net/bridge/netfilter/nft_reject_bridge.c | 1
+ net/ipv4/netfilter/nft_dup_ipv4.c | 1
+ net/ipv4/netfilter/nft_fib_ipv4.c | 2
+ net/ipv4/netfilter/nft_reject_ipv4.c | 1
+ net/ipv6/netfilter/nft_dup_ipv6.c | 1
+ net/ipv6/netfilter/nft_fib_ipv6.c | 2
+ net/ipv6/netfilter/nft_reject_ipv6.c | 1
+ net/netfilter/nf_tables_api.c | 67 -------------------
+ net/netfilter/nft_bitwise.c | 104 -------------------------------
+ net/netfilter/nft_byteorder.c | 11 ---
+ net/netfilter/nft_cmp.c | 3
+ net/netfilter/nft_compat.c | 10 --
+ net/netfilter/nft_connlimit.c | 1
+ net/netfilter/nft_counter.c | 1
+ net/netfilter/nft_ct.c | 46 -------------
+ net/netfilter/nft_dup_netdev.c | 1
+ net/netfilter/nft_dynset.c | 1
+ net/netfilter/nft_exthdr.c | 34 ----------
+ net/netfilter/nft_fib.c | 42 ------------
+ net/netfilter/nft_fib_inet.c | 1
+ net/netfilter/nft_fib_netdev.c | 1
+ net/netfilter/nft_flow_offload.c | 1
+ net/netfilter/nft_fwd_netdev.c | 2
+ net/netfilter/nft_hash.c | 36 ----------
+ net/netfilter/nft_immediate.c | 12 ---
+ net/netfilter/nft_last.c | 1
+ net/netfilter/nft_limit.c | 2
+ net/netfilter/nft_log.c | 1
+ net/netfilter/nft_lookup.c | 12 ---
+ net/netfilter/nft_masq.c | 3
+ net/netfilter/nft_meta.c | 45 -------------
+ net/netfilter/nft_nat.c | 2
+ net/netfilter/nft_numgen.c | 22 ------
+ net/netfilter/nft_objref.c | 2
+ net/netfilter/nft_osf.c | 25 -------
+ net/netfilter/nft_payload.c | 47 --------------
+ net/netfilter/nft_queue.c | 2
+ net/netfilter/nft_quota.c | 1
+ net/netfilter/nft_range.c | 1
+ net/netfilter/nft_redir.c | 3
+ net/netfilter/nft_reject_inet.c | 1
+ net/netfilter/nft_reject_netdev.c | 1
+ net/netfilter/nft_rt.c | 1
+ net/netfilter/nft_socket.c | 26 -------
+ net/netfilter/nft_synproxy.c | 1
+ net/netfilter/nft_tproxy.c | 1
+ net/netfilter/nft_tunnel.c | 26 -------
+ net/netfilter/nft_xfrm.c | 27 --------
+ 52 files changed, 693 deletions(-)
+
+--- a/include/net/netfilter/nf_tables.h
++++ b/include/net/netfilter/nf_tables.h
+@@ -123,17 +123,6 @@ struct nft_regs {
+ };
+ };
+
+-struct nft_regs_track {
+- struct {
+- const struct nft_expr *selector;
+- const struct nft_expr *bitwise;
+- u8 num_reg;
+- } regs[NFT_REG32_NUM];
+-
+- const struct nft_expr *cur;
+- const struct nft_expr *last;
+-};
+-
+ /* Store/load an u8, u16 or u64 integer to/from the u32 data register.
+ *
+ * Note, when using concatenations, register allocation happens at 32-bit
+@@ -420,8 +409,6 @@ int nft_expr_clone(struct nft_expr *dst,
+ void nft_expr_destroy(const struct nft_ctx *ctx, struct nft_expr *expr);
+ int nft_expr_dump(struct sk_buff *skb, unsigned int attr,
+ const struct nft_expr *expr, bool reset);
+-bool nft_expr_reduce_bitwise(struct nft_regs_track *track,
+- const struct nft_expr *expr);
+
+ struct nft_set_ext;
+
+@@ -933,7 +920,6 @@ struct nft_offload_ctx;
+ * @destroy_clone: destruction clone function
+ * @dump: function to dump parameters
+ * @validate: validate expression, called during loop detection
+- * @reduce: reduce expression
+ * @gc: garbage collection expression
+ * @offload: hardware offload expression
+ * @offload_action: function to report true/false to allocate one slot or not in the flow
+@@ -967,8 +953,6 @@ struct nft_expr_ops {
+ bool reset);
+ int (*validate)(const struct nft_ctx *ctx,
+ const struct nft_expr *expr);
+- bool (*reduce)(struct nft_regs_track *track,
+- const struct nft_expr *expr);
+ bool (*gc)(struct net *net,
+ const struct nft_expr *expr);
+ int (*offload)(struct nft_offload_ctx *ctx,
+@@ -1848,20 +1832,4 @@ static inline u64 nft_net_tstamp(const s
+ return nft_pernet(net)->tstamp;
+ }
+
+-#define __NFT_REDUCE_READONLY 1UL
+-#define NFT_REDUCE_READONLY (void *)__NFT_REDUCE_READONLY
+-
+-void nft_reg_track_update(struct nft_regs_track *track,
+- const struct nft_expr *expr, u8 dreg, u8 len);
+-void nft_reg_track_cancel(struct nft_regs_track *track, u8 dreg, u8 len);
+-void __nft_reg_track_cancel(struct nft_regs_track *track, u8 dreg);
+-
+-static inline bool nft_reg_track_cmp(struct nft_regs_track *track,
+- const struct nft_expr *expr, u8 dreg)
+-{
+- return track->regs[dreg].selector &&
+- track->regs[dreg].selector->ops == expr->ops &&
+- track->regs[dreg].num_reg == 0;
+-}
+-
+ #endif /* _NET_NF_TABLES_H */
+--- a/include/net/netfilter/nft_fib.h
++++ b/include/net/netfilter/nft_fib.h
+@@ -36,6 +36,4 @@ void nft_fib6_eval(const struct nft_expr
+ void nft_fib_store_result(void *reg, const struct nft_fib *priv,
+ const struct net_device *dev);
+
+-bool nft_fib_reduce(struct nft_regs_track *track,
+- const struct nft_expr *expr);
+ #endif
+--- a/include/net/netfilter/nft_meta.h
++++ b/include/net/netfilter/nft_meta.h
+@@ -43,9 +43,6 @@ void nft_meta_set_destroy(const struct n
+ int nft_meta_set_validate(const struct nft_ctx *ctx,
+ const struct nft_expr *expr);
+
+-bool nft_meta_get_reduce(struct nft_regs_track *track,
+- const struct nft_expr *expr);
+-
+ struct nft_inner_tun_ctx;
+ void nft_meta_inner_eval(const struct nft_expr *expr,
+ struct nft_regs *regs, const struct nft_pktinfo *pkt,
+--- a/net/bridge/netfilter/nft_meta_bridge.c
++++ b/net/bridge/netfilter/nft_meta_bridge.c
+@@ -104,7 +104,6 @@ static const struct nft_expr_ops nft_met
+ .eval = nft_meta_bridge_get_eval,
+ .init = nft_meta_bridge_get_init,
+ .dump = nft_meta_get_dump,
+- .reduce = nft_meta_get_reduce,
+ };
+
+ static void nft_meta_bridge_set_eval(const struct nft_expr *expr,
+@@ -151,24 +150,6 @@ static int nft_meta_bridge_set_init(cons
+ return 0;
+ }
+
+-static bool nft_meta_bridge_set_reduce(struct nft_regs_track *track,
+- const struct nft_expr *expr)
+-{
+- int i;
+-
+- for (i = 0; i < NFT_REG32_NUM; i++) {
+- if (!track->regs[i].selector)
+- continue;
+-
+- if (track->regs[i].selector->ops != &nft_meta_bridge_get_ops)
+- continue;
+-
+- __nft_reg_track_cancel(track, i);
+- }
+-
+- return false;
+-}
+-
+ static int nft_meta_bridge_set_validate(const struct nft_ctx *ctx,
+ const struct nft_expr *expr)
+ {
+@@ -193,7 +174,6 @@ static const struct nft_expr_ops nft_met
+ .init = nft_meta_bridge_set_init,
+ .destroy = nft_meta_set_destroy,
+ .dump = nft_meta_set_dump,
+- .reduce = nft_meta_bridge_set_reduce,
+ .validate = nft_meta_bridge_set_validate,
+ };
+
+--- a/net/bridge/netfilter/nft_reject_bridge.c
++++ b/net/bridge/netfilter/nft_reject_bridge.c
+@@ -184,7 +184,6 @@ static const struct nft_expr_ops nft_rej
+ .init = nft_reject_init,
+ .dump = nft_reject_dump,
+ .validate = nft_reject_bridge_validate,
+- .reduce = NFT_REDUCE_READONLY,
+ };
+
+ static struct nft_expr_type nft_reject_bridge_type __read_mostly = {
+--- a/net/ipv4/netfilter/nft_dup_ipv4.c
++++ b/net/ipv4/netfilter/nft_dup_ipv4.c
+@@ -76,7 +76,6 @@ static const struct nft_expr_ops nft_dup
+ .eval = nft_dup_ipv4_eval,
+ .init = nft_dup_ipv4_init,
+ .dump = nft_dup_ipv4_dump,
+- .reduce = NFT_REDUCE_READONLY,
+ };
+
+ static const struct nla_policy nft_dup_ipv4_policy[NFTA_DUP_MAX + 1] = {
+--- a/net/ipv4/netfilter/nft_fib_ipv4.c
++++ b/net/ipv4/netfilter/nft_fib_ipv4.c
+@@ -157,7 +157,6 @@ static const struct nft_expr_ops nft_fib
+ .init = nft_fib_init,
+ .dump = nft_fib_dump,
+ .validate = nft_fib_validate,
+- .reduce = nft_fib_reduce,
+ };
+
+ static const struct nft_expr_ops nft_fib4_ops = {
+@@ -167,7 +166,6 @@ static const struct nft_expr_ops nft_fib
+ .init = nft_fib_init,
+ .dump = nft_fib_dump,
+ .validate = nft_fib_validate,
+- .reduce = nft_fib_reduce,
+ };
+
+ static const struct nft_expr_ops *
+--- a/net/ipv4/netfilter/nft_reject_ipv4.c
++++ b/net/ipv4/netfilter/nft_reject_ipv4.c
+@@ -45,7 +45,6 @@ static const struct nft_expr_ops nft_rej
+ .init = nft_reject_init,
+ .dump = nft_reject_dump,
+ .validate = nft_reject_validate,
+- .reduce = NFT_REDUCE_READONLY,
+ };
+
+ static struct nft_expr_type nft_reject_ipv4_type __read_mostly = {
+--- a/net/ipv6/netfilter/nft_dup_ipv6.c
++++ b/net/ipv6/netfilter/nft_dup_ipv6.c
+@@ -74,7 +74,6 @@ static const struct nft_expr_ops nft_dup
+ .eval = nft_dup_ipv6_eval,
+ .init = nft_dup_ipv6_init,
+ .dump = nft_dup_ipv6_dump,
+- .reduce = NFT_REDUCE_READONLY,
+ };
+
+ static const struct nla_policy nft_dup_ipv6_policy[NFTA_DUP_MAX + 1] = {
+--- a/net/ipv6/netfilter/nft_fib_ipv6.c
++++ b/net/ipv6/netfilter/nft_fib_ipv6.c
+@@ -226,7 +226,6 @@ static const struct nft_expr_ops nft_fib
+ .init = nft_fib_init,
+ .dump = nft_fib_dump,
+ .validate = nft_fib_validate,
+- .reduce = nft_fib_reduce,
+ };
+
+ static const struct nft_expr_ops nft_fib6_ops = {
+@@ -236,7 +235,6 @@ static const struct nft_expr_ops nft_fib
+ .init = nft_fib_init,
+ .dump = nft_fib_dump,
+ .validate = nft_fib_validate,
+- .reduce = nft_fib_reduce,
+ };
+
+ static const struct nft_expr_ops *
+--- a/net/ipv6/netfilter/nft_reject_ipv6.c
++++ b/net/ipv6/netfilter/nft_reject_ipv6.c
+@@ -46,7 +46,6 @@ static const struct nft_expr_ops nft_rej
+ .init = nft_reject_init,
+ .dump = nft_reject_dump,
+ .validate = nft_reject_validate,
+- .reduce = NFT_REDUCE_READONLY,
+ };
+
+ static struct nft_expr_type nft_reject_ipv6_type __read_mostly = {
+--- a/net/netfilter/nf_tables_api.c
++++ b/net/netfilter/nf_tables_api.c
+@@ -754,58 +754,6 @@ static int nft_delflowtable(struct nft_c
+ return 0;
+ }
+
+-static void __nft_reg_track_clobber(struct nft_regs_track *track, u8 dreg)
+-{
+- int i;
+-
+- for (i = track->regs[dreg].num_reg; i > 0; i--)
+- __nft_reg_track_cancel(track, dreg - i);
+-}
+-
+-static void __nft_reg_track_update(struct nft_regs_track *track,
+- const struct nft_expr *expr,
+- u8 dreg, u8 num_reg)
+-{
+- track->regs[dreg].selector = expr;
+- track->regs[dreg].bitwise = NULL;
+- track->regs[dreg].num_reg = num_reg;
+-}
+-
+-void nft_reg_track_update(struct nft_regs_track *track,
+- const struct nft_expr *expr, u8 dreg, u8 len)
+-{
+- unsigned int regcount;
+- int i;
+-
+- __nft_reg_track_clobber(track, dreg);
+-
+- regcount = DIV_ROUND_UP(len, NFT_REG32_SIZE);
+- for (i = 0; i < regcount; i++, dreg++)
+- __nft_reg_track_update(track, expr, dreg, i);
+-}
+-EXPORT_SYMBOL_GPL(nft_reg_track_update);
+-
+-void nft_reg_track_cancel(struct nft_regs_track *track, u8 dreg, u8 len)
+-{
+- unsigned int regcount;
+- int i;
+-
+- __nft_reg_track_clobber(track, dreg);
+-
+- regcount = DIV_ROUND_UP(len, NFT_REG32_SIZE);
+- for (i = 0; i < regcount; i++, dreg++)
+- __nft_reg_track_cancel(track, dreg);
+-}
+-EXPORT_SYMBOL_GPL(nft_reg_track_cancel);
+-
+-void __nft_reg_track_cancel(struct nft_regs_track *track, u8 dreg)
+-{
+- track->regs[dreg].selector = NULL;
+- track->regs[dreg].bitwise = NULL;
+- track->regs[dreg].num_reg = 0;
+-}
+-EXPORT_SYMBOL_GPL(__nft_reg_track_cancel);
+-
+ /*
+ * Tables
+ */
+@@ -9638,16 +9586,9 @@ void nf_tables_trans_destroy_flush_work(
+ }
+ EXPORT_SYMBOL_GPL(nf_tables_trans_destroy_flush_work);
+
+-static bool nft_expr_reduce(struct nft_regs_track *track,
+- const struct nft_expr *expr)
+-{
+- return false;
+-}
+-
+ static int nf_tables_commit_chain_prepare(struct net *net, struct nft_chain *chain)
+ {
+ const struct nft_expr *expr, *last;
+- struct nft_regs_track track = {};
+ unsigned int size, data_size;
+ void *data, *data_boundary;
+ struct nft_rule_dp *prule;
+@@ -9684,15 +9625,7 @@ static int nf_tables_commit_chain_prepar
+ return -ENOMEM;
+
+ size = 0;
+- track.last = nft_expr_last(rule);
+ nft_rule_for_each_expr(expr, last, rule) {
+- track.cur = expr;
+-
+- if (nft_expr_reduce(&track, expr)) {
+- expr = track.cur;
+- continue;
+- }
+-
+ if (WARN_ON_ONCE(data + size + expr->ops->size > data_boundary))
+ return -ENOMEM;
+
+--- a/net/netfilter/nft_bitwise.c
++++ b/net/netfilter/nft_bitwise.c
+@@ -402,61 +402,12 @@ static int nft_bitwise_offload(struct nf
+ return 0;
+ }
+
+-static bool nft_bitwise_reduce(struct nft_regs_track *track,
+- const struct nft_expr *expr)
+-{
+- const struct nft_bitwise *priv = nft_expr_priv(expr);
+- const struct nft_bitwise *bitwise;
+- unsigned int regcount;
+- u8 dreg;
+- int i;
+-
+- if (!track->regs[priv->sreg].selector)
+- return false;
+-
+- bitwise = nft_expr_priv(track->regs[priv->dreg].selector);
+- if (track->regs[priv->sreg].selector == track->regs[priv->dreg].selector &&
+- track->regs[priv->sreg].num_reg == 0 &&
+- track->regs[priv->dreg].bitwise &&
+- track->regs[priv->dreg].bitwise->ops == expr->ops &&
+- priv->sreg == bitwise->sreg &&
+- priv->sreg2 == bitwise->sreg2 &&
+- priv->dreg == bitwise->dreg &&
+- priv->op == bitwise->op &&
+- priv->len == bitwise->len &&
+- !memcmp(&priv->mask, &bitwise->mask, sizeof(priv->mask)) &&
+- !memcmp(&priv->xor, &bitwise->xor, sizeof(priv->xor)) &&
+- !memcmp(&priv->data, &bitwise->data, sizeof(priv->data))) {
+- track->cur = expr;
+- return true;
+- }
+-
+- if (track->regs[priv->sreg].bitwise ||
+- track->regs[priv->sreg].num_reg != 0) {
+- nft_reg_track_cancel(track, priv->dreg, priv->len);
+- return false;
+- }
+-
+- if (priv->sreg != priv->dreg) {
+- nft_reg_track_update(track, track->regs[priv->sreg].selector,
+- priv->dreg, priv->len);
+- }
+-
+- dreg = priv->dreg;
+- regcount = DIV_ROUND_UP(priv->len, NFT_REG32_SIZE);
+- for (i = 0; i < regcount; i++, dreg++)
+- track->regs[dreg].bitwise = expr;
+-
+- return false;
+-}
+-
+ static const struct nft_expr_ops nft_bitwise_ops = {
+ .type = &nft_bitwise_type,
+ .size = NFT_EXPR_SIZE(sizeof(struct nft_bitwise)),
+ .eval = nft_bitwise_eval,
+ .init = nft_bitwise_init,
+ .dump = nft_bitwise_dump,
+- .reduce = nft_bitwise_reduce,
+ .offload = nft_bitwise_offload,
+ };
+
+@@ -559,48 +510,12 @@ static int nft_bitwise_fast_offload(stru
+ return 0;
+ }
+
+-static bool nft_bitwise_fast_reduce(struct nft_regs_track *track,
+- const struct nft_expr *expr)
+-{
+- const struct nft_bitwise_fast_expr *priv = nft_expr_priv(expr);
+- const struct nft_bitwise_fast_expr *bitwise;
+-
+- if (!track->regs[priv->sreg].selector)
+- return false;
+-
+- bitwise = nft_expr_priv(track->regs[priv->dreg].selector);
+- if (track->regs[priv->sreg].selector == track->regs[priv->dreg].selector &&
+- track->regs[priv->dreg].bitwise &&
+- track->regs[priv->dreg].bitwise->ops == expr->ops &&
+- priv->sreg == bitwise->sreg &&
+- priv->dreg == bitwise->dreg &&
+- priv->mask == bitwise->mask &&
+- priv->xor == bitwise->xor) {
+- track->cur = expr;
+- return true;
+- }
+-
+- if (track->regs[priv->sreg].bitwise) {
+- nft_reg_track_cancel(track, priv->dreg, NFT_REG32_SIZE);
+- return false;
+- }
+-
+- if (priv->sreg != priv->dreg) {
+- track->regs[priv->dreg].selector =
+- track->regs[priv->sreg].selector;
+- }
+- track->regs[priv->dreg].bitwise = expr;
+-
+- return false;
+-}
+-
+ const struct nft_expr_ops nft_bitwise_fast_ops = {
+ .type = &nft_bitwise_type,
+ .size = NFT_EXPR_SIZE(sizeof(struct nft_bitwise_fast_expr)),
+ .eval = NULL, /* inlined */
+ .init = nft_bitwise_fast_init,
+ .dump = nft_bitwise_fast_dump,
+- .reduce = nft_bitwise_fast_reduce,
+ .offload = nft_bitwise_fast_offload,
+ };
+
+@@ -637,22 +552,3 @@ struct nft_expr_type nft_bitwise_type __
+ .maxattr = NFTA_BITWISE_MAX,
+ .owner = THIS_MODULE,
+ };
+-
+-bool nft_expr_reduce_bitwise(struct nft_regs_track *track,
+- const struct nft_expr *expr)
+-{
+- const struct nft_expr *last = track->last;
+- const struct nft_expr *next;
+-
+- if (expr == last)
+- return false;
+-
+- next = nft_expr_next(expr);
+- if (next->ops == &nft_bitwise_ops)
+- return nft_bitwise_reduce(track, next);
+- else if (next->ops == &nft_bitwise_fast_ops)
+- return nft_bitwise_fast_reduce(track, next);
+-
+- return false;
+-}
+-EXPORT_SYMBOL_GPL(nft_expr_reduce_bitwise);
+--- a/net/netfilter/nft_byteorder.c
++++ b/net/netfilter/nft_byteorder.c
+@@ -177,23 +177,12 @@ nla_put_failure:
+ return -1;
+ }
+
+-static bool nft_byteorder_reduce(struct nft_regs_track *track,
+- const struct nft_expr *expr)
+-{
+- struct nft_byteorder *priv = nft_expr_priv(expr);
+-
+- nft_reg_track_cancel(track, priv->dreg, priv->len);
+-
+- return false;
+-}
+-
+ static const struct nft_expr_ops nft_byteorder_ops = {
+ .type = &nft_byteorder_type,
+ .size = NFT_EXPR_SIZE(sizeof(struct nft_byteorder)),
+ .eval = nft_byteorder_eval,
+ .init = nft_byteorder_init,
+ .dump = nft_byteorder_dump,
+- .reduce = nft_byteorder_reduce,
+ };
+
+ struct nft_expr_type nft_byteorder_type __read_mostly = {
+--- a/net/netfilter/nft_cmp.c
++++ b/net/netfilter/nft_cmp.c
+@@ -190,7 +190,6 @@ static const struct nft_expr_ops nft_cmp
+ .eval = nft_cmp_eval,
+ .init = nft_cmp_init,
+ .dump = nft_cmp_dump,
+- .reduce = NFT_REDUCE_READONLY,
+ .offload = nft_cmp_offload,
+ };
+
+@@ -282,7 +281,6 @@ const struct nft_expr_ops nft_cmp_fast_o
+ .eval = NULL, /* inlined */
+ .init = nft_cmp_fast_init,
+ .dump = nft_cmp_fast_dump,
+- .reduce = NFT_REDUCE_READONLY,
+ .offload = nft_cmp_fast_offload,
+ };
+
+@@ -376,7 +374,6 @@ const struct nft_expr_ops nft_cmp16_fast
+ .eval = NULL, /* inlined */
+ .init = nft_cmp16_fast_init,
+ .dump = nft_cmp16_fast_dump,
+- .reduce = NFT_REDUCE_READONLY,
+ .offload = nft_cmp16_fast_offload,
+ };
+
+--- a/net/netfilter/nft_compat.c
++++ b/net/netfilter/nft_compat.c
+@@ -794,14 +794,6 @@ static const struct nfnetlink_subsystem
+
+ static struct nft_expr_type nft_match_type;
+
+-static bool nft_match_reduce(struct nft_regs_track *track,
+- const struct nft_expr *expr)
+-{
+- const struct xt_match *match = expr->ops->data;
+-
+- return strcmp(match->name, "comment") == 0;
+-}
+-
+ static const struct nft_expr_ops *
+ nft_match_select_ops(const struct nft_ctx *ctx,
+ const struct nlattr * const tb[])
+@@ -844,7 +836,6 @@ nft_match_select_ops(const struct nft_ct
+ ops->dump = nft_match_dump;
+ ops->validate = nft_match_validate;
+ ops->data = match;
+- ops->reduce = nft_match_reduce;
+
+ matchsize = NFT_EXPR_SIZE(XT_ALIGN(match->matchsize));
+ if (matchsize > NFT_MATCH_LARGE_THRESH) {
+@@ -933,7 +924,6 @@ nft_target_select_ops(const struct nft_c
+ ops->destroy = nft_target_destroy;
+ ops->dump = nft_target_dump;
+ ops->data = target;
+- ops->reduce = NFT_REDUCE_READONLY;
+
+ if (family == NFPROTO_BRIDGE) {
+ ops->eval = nft_target_eval_bridge;
+--- a/net/netfilter/nft_connlimit.c
++++ b/net/netfilter/nft_connlimit.c
+@@ -247,7 +247,6 @@ static const struct nft_expr_ops nft_con
+ .destroy_clone = nft_connlimit_destroy_clone,
+ .dump = nft_connlimit_dump,
+ .gc = nft_connlimit_gc,
+- .reduce = NFT_REDUCE_READONLY,
+ };
+
+ static struct nft_expr_type nft_connlimit_type __read_mostly = {
+--- a/net/netfilter/nft_counter.c
++++ b/net/netfilter/nft_counter.c
+@@ -301,7 +301,6 @@ static const struct nft_expr_ops nft_cou
+ .destroy_clone = nft_counter_destroy,
+ .dump = nft_counter_dump,
+ .clone = nft_counter_clone,
+- .reduce = NFT_REDUCE_READONLY,
+ .offload = nft_counter_offload,
+ .offload_stats = nft_counter_offload_stats,
+ };
+--- a/net/netfilter/nft_ct.c
++++ b/net/netfilter/nft_ct.c
+@@ -679,29 +679,6 @@ nla_put_failure:
+ return -1;
+ }
+
+-static bool nft_ct_get_reduce(struct nft_regs_track *track,
+- const struct nft_expr *expr)
+-{
+- const struct nft_ct *priv = nft_expr_priv(expr);
+- const struct nft_ct *ct;
+-
+- if (!nft_reg_track_cmp(track, expr, priv->dreg)) {
+- nft_reg_track_update(track, expr, priv->dreg, priv->len);
+- return false;
+- }
+-
+- ct = nft_expr_priv(track->regs[priv->dreg].selector);
+- if (priv->key != ct->key) {
+- nft_reg_track_update(track, expr, priv->dreg, priv->len);
+- return false;
+- }
+-
+- if (!track->regs[priv->dreg].bitwise)
+- return true;
+-
+- return nft_expr_reduce_bitwise(track, expr);
+-}
+-
+ static int nft_ct_set_dump(struct sk_buff *skb,
+ const struct nft_expr *expr, bool reset)
+ {
+@@ -736,27 +713,8 @@ static const struct nft_expr_ops nft_ct_
+ .init = nft_ct_get_init,
+ .destroy = nft_ct_get_destroy,
+ .dump = nft_ct_get_dump,
+- .reduce = nft_ct_get_reduce,
+ };
+
+-static bool nft_ct_set_reduce(struct nft_regs_track *track,
+- const struct nft_expr *expr)
+-{
+- int i;
+-
+- for (i = 0; i < NFT_REG32_NUM; i++) {
+- if (!track->regs[i].selector)
+- continue;
+-
+- if (track->regs[i].selector->ops != &nft_ct_get_ops)
+- continue;
+-
+- __nft_reg_track_cancel(track, i);
+- }
+-
+- return false;
+-}
+-
+ #ifdef CONFIG_RETPOLINE
+ static const struct nft_expr_ops nft_ct_get_fast_ops = {
+ .type = &nft_ct_type,
+@@ -765,7 +723,6 @@ static const struct nft_expr_ops nft_ct_
+ .init = nft_ct_get_init,
+ .destroy = nft_ct_get_destroy,
+ .dump = nft_ct_get_dump,
+- .reduce = nft_ct_set_reduce,
+ };
+ #endif
+
+@@ -776,7 +733,6 @@ static const struct nft_expr_ops nft_ct_
+ .init = nft_ct_set_init,
+ .destroy = nft_ct_set_destroy,
+ .dump = nft_ct_set_dump,
+- .reduce = nft_ct_set_reduce,
+ };
+
+ #ifdef CONFIG_NF_CONNTRACK_ZONES
+@@ -787,7 +743,6 @@ static const struct nft_expr_ops nft_ct_
+ .init = nft_ct_set_init,
+ .destroy = nft_ct_set_destroy,
+ .dump = nft_ct_set_dump,
+- .reduce = nft_ct_set_reduce,
+ };
+ #endif
+
+@@ -857,7 +812,6 @@ static const struct nft_expr_ops nft_not
+ .type = &nft_notrack_type,
+ .size = NFT_EXPR_SIZE(0),
+ .eval = nft_notrack_eval,
+- .reduce = NFT_REDUCE_READONLY,
+ };
+
+ static struct nft_expr_type nft_notrack_type __read_mostly = {
+--- a/net/netfilter/nft_dup_netdev.c
++++ b/net/netfilter/nft_dup_netdev.c
+@@ -80,7 +80,6 @@ static const struct nft_expr_ops nft_dup
+ .eval = nft_dup_netdev_eval,
+ .init = nft_dup_netdev_init,
+ .dump = nft_dup_netdev_dump,
+- .reduce = NFT_REDUCE_READONLY,
+ .offload = nft_dup_netdev_offload,
+ .offload_action = nft_dup_netdev_offload_action,
+ };
+--- a/net/netfilter/nft_dynset.c
++++ b/net/netfilter/nft_dynset.c
+@@ -430,7 +430,6 @@ static const struct nft_expr_ops nft_dyn
+ .activate = nft_dynset_activate,
+ .deactivate = nft_dynset_deactivate,
+ .dump = nft_dynset_dump,
+- .reduce = NFT_REDUCE_READONLY,
+ };
+
+ struct nft_expr_type nft_dynset_type __read_mostly = {
+--- a/net/netfilter/nft_exthdr.c
++++ b/net/netfilter/nft_exthdr.c
+@@ -701,40 +701,12 @@ static int nft_exthdr_dump_strip(struct
+ return nft_exthdr_dump_common(skb, priv);
+ }
+
+-static bool nft_exthdr_reduce(struct nft_regs_track *track,
+- const struct nft_expr *expr)
+-{
+- const struct nft_exthdr *priv = nft_expr_priv(expr);
+- const struct nft_exthdr *exthdr;
+-
+- if (!nft_reg_track_cmp(track, expr, priv->dreg)) {
+- nft_reg_track_update(track, expr, priv->dreg, priv->len);
+- return false;
+- }
+-
+- exthdr = nft_expr_priv(track->regs[priv->dreg].selector);
+- if (priv->type != exthdr->type ||
+- priv->op != exthdr->op ||
+- priv->flags != exthdr->flags ||
+- priv->offset != exthdr->offset ||
+- priv->len != exthdr->len) {
+- nft_reg_track_update(track, expr, priv->dreg, priv->len);
+- return false;
+- }
+-
+- if (!track->regs[priv->dreg].bitwise)
+- return true;
+-
+- return nft_expr_reduce_bitwise(track, expr);
+-}
+-
+ static const struct nft_expr_ops nft_exthdr_ipv6_ops = {
+ .type = &nft_exthdr_type,
+ .size = NFT_EXPR_SIZE(sizeof(struct nft_exthdr)),
+ .eval = nft_exthdr_ipv6_eval,
+ .init = nft_exthdr_init,
+ .dump = nft_exthdr_dump,
+- .reduce = nft_exthdr_reduce,
+ };
+
+ static const struct nft_expr_ops nft_exthdr_ipv4_ops = {
+@@ -743,7 +715,6 @@ static const struct nft_expr_ops nft_ext
+ .eval = nft_exthdr_ipv4_eval,
+ .init = nft_exthdr_ipv4_init,
+ .dump = nft_exthdr_dump,
+- .reduce = nft_exthdr_reduce,
+ };
+
+ static const struct nft_expr_ops nft_exthdr_tcp_ops = {
+@@ -752,7 +723,6 @@ static const struct nft_expr_ops nft_ext
+ .eval = nft_exthdr_tcp_eval,
+ .init = nft_exthdr_init,
+ .dump = nft_exthdr_dump,
+- .reduce = nft_exthdr_reduce,
+ };
+
+ static const struct nft_expr_ops nft_exthdr_tcp_set_ops = {
+@@ -761,7 +731,6 @@ static const struct nft_expr_ops nft_ext
+ .eval = nft_exthdr_tcp_set_eval,
+ .init = nft_exthdr_tcp_set_init,
+ .dump = nft_exthdr_dump_set,
+- .reduce = NFT_REDUCE_READONLY,
+ };
+
+ static const struct nft_expr_ops nft_exthdr_tcp_strip_ops = {
+@@ -770,7 +739,6 @@ static const struct nft_expr_ops nft_ext
+ .eval = nft_exthdr_tcp_strip_eval,
+ .init = nft_exthdr_tcp_strip_init,
+ .dump = nft_exthdr_dump_strip,
+- .reduce = NFT_REDUCE_READONLY,
+ };
+
+ static const struct nft_expr_ops nft_exthdr_sctp_ops = {
+@@ -779,7 +747,6 @@ static const struct nft_expr_ops nft_ext
+ .eval = nft_exthdr_sctp_eval,
+ .init = nft_exthdr_init,
+ .dump = nft_exthdr_dump,
+- .reduce = nft_exthdr_reduce,
+ };
+
+ static const struct nft_expr_ops nft_exthdr_dccp_ops = {
+@@ -788,7 +755,6 @@ static const struct nft_expr_ops nft_ext
+ .eval = nft_exthdr_dccp_eval,
+ .init = nft_exthdr_dccp_init,
+ .dump = nft_exthdr_dump,
+- .reduce = nft_exthdr_reduce,
+ };
+
+ static const struct nft_expr_ops *
+--- a/net/netfilter/nft_fib.c
++++ b/net/netfilter/nft_fib.c
+@@ -168,47 +168,5 @@ void nft_fib_store_result(void *reg, con
+ }
+ EXPORT_SYMBOL_GPL(nft_fib_store_result);
+
+-bool nft_fib_reduce(struct nft_regs_track *track,
+- const struct nft_expr *expr)
+-{
+- const struct nft_fib *priv = nft_expr_priv(expr);
+- unsigned int len = NFT_REG32_SIZE;
+- const struct nft_fib *fib;
+-
+- switch (priv->result) {
+- case NFT_FIB_RESULT_OIF:
+- break;
+- case NFT_FIB_RESULT_OIFNAME:
+- if (priv->flags & NFTA_FIB_F_PRESENT)
+- len = NFT_REG32_SIZE;
+- else
+- len = IFNAMSIZ;
+- break;
+- case NFT_FIB_RESULT_ADDRTYPE:
+- break;
+- default:
+- WARN_ON_ONCE(1);
+- break;
+- }
+-
+- if (!nft_reg_track_cmp(track, expr, priv->dreg)) {
+- nft_reg_track_update(track, expr, priv->dreg, len);
+- return false;
+- }
+-
+- fib = nft_expr_priv(track->regs[priv->dreg].selector);
+- if (priv->result != fib->result ||
+- priv->flags != fib->flags) {
+- nft_reg_track_update(track, expr, priv->dreg, len);
+- return false;
+- }
+-
+- if (!track->regs[priv->dreg].bitwise)
+- return true;
+-
+- return false;
+-}
+-EXPORT_SYMBOL_GPL(nft_fib_reduce);
+-
+ MODULE_LICENSE("GPL");
+ MODULE_AUTHOR("Florian Westphal <fw@strlen.de>");
+--- a/net/netfilter/nft_fib_inet.c
++++ b/net/netfilter/nft_fib_inet.c
+@@ -49,7 +49,6 @@ static const struct nft_expr_ops nft_fib
+ .init = nft_fib_init,
+ .dump = nft_fib_dump,
+ .validate = nft_fib_validate,
+- .reduce = nft_fib_reduce,
+ };
+
+ static struct nft_expr_type nft_fib_inet_type __read_mostly = {
+--- a/net/netfilter/nft_fib_netdev.c
++++ b/net/netfilter/nft_fib_netdev.c
+@@ -58,7 +58,6 @@ static const struct nft_expr_ops nft_fib
+ .init = nft_fib_init,
+ .dump = nft_fib_dump,
+ .validate = nft_fib_validate,
+- .reduce = nft_fib_reduce,
+ };
+
+ static struct nft_expr_type nft_fib_netdev_type __read_mostly = {
+--- a/net/netfilter/nft_flow_offload.c
++++ b/net/netfilter/nft_flow_offload.c
+@@ -481,7 +481,6 @@ static const struct nft_expr_ops nft_flo
+ .destroy = nft_flow_offload_destroy,
+ .validate = nft_flow_offload_validate,
+ .dump = nft_flow_offload_dump,
+- .reduce = NFT_REDUCE_READONLY,
+ };
+
+ static struct nft_expr_type nft_flow_offload_type __read_mostly = {
+--- a/net/netfilter/nft_fwd_netdev.c
++++ b/net/netfilter/nft_fwd_netdev.c
+@@ -228,7 +228,6 @@ static const struct nft_expr_ops nft_fwd
+ .init = nft_fwd_neigh_init,
+ .dump = nft_fwd_neigh_dump,
+ .validate = nft_fwd_validate,
+- .reduce = NFT_REDUCE_READONLY,
+ };
+
+ static const struct nft_expr_ops nft_fwd_netdev_ops = {
+@@ -238,7 +237,6 @@ static const struct nft_expr_ops nft_fwd
+ .init = nft_fwd_netdev_init,
+ .dump = nft_fwd_netdev_dump,
+ .validate = nft_fwd_validate,
+- .reduce = NFT_REDUCE_READONLY,
+ .offload = nft_fwd_netdev_offload,
+ .offload_action = nft_fwd_netdev_offload_action,
+ };
+--- a/net/netfilter/nft_hash.c
++++ b/net/netfilter/nft_hash.c
+@@ -165,16 +165,6 @@ nla_put_failure:
+ return -1;
+ }
+
+-static bool nft_jhash_reduce(struct nft_regs_track *track,
+- const struct nft_expr *expr)
+-{
+- const struct nft_jhash *priv = nft_expr_priv(expr);
+-
+- nft_reg_track_cancel(track, priv->dreg, sizeof(u32));
+-
+- return false;
+-}
+-
+ static int nft_symhash_dump(struct sk_buff *skb,
+ const struct nft_expr *expr, bool reset)
+ {
+@@ -195,30 +185,6 @@ nla_put_failure:
+ return -1;
+ }
+
+-static bool nft_symhash_reduce(struct nft_regs_track *track,
+- const struct nft_expr *expr)
+-{
+- struct nft_symhash *priv = nft_expr_priv(expr);
+- struct nft_symhash *symhash;
+-
+- if (!nft_reg_track_cmp(track, expr, priv->dreg)) {
+- nft_reg_track_update(track, expr, priv->dreg, sizeof(u32));
+- return false;
+- }
+-
+- symhash = nft_expr_priv(track->regs[priv->dreg].selector);
+- if (priv->offset != symhash->offset ||
+- priv->modulus != symhash->modulus) {
+- nft_reg_track_update(track, expr, priv->dreg, sizeof(u32));
+- return false;
+- }
+-
+- if (!track->regs[priv->dreg].bitwise)
+- return true;
+-
+- return false;
+-}
+-
+ static struct nft_expr_type nft_hash_type;
+ static const struct nft_expr_ops nft_jhash_ops = {
+ .type = &nft_hash_type,
+@@ -226,7 +192,6 @@ static const struct nft_expr_ops nft_jha
+ .eval = nft_jhash_eval,
+ .init = nft_jhash_init,
+ .dump = nft_jhash_dump,
+- .reduce = nft_jhash_reduce,
+ };
+
+ static const struct nft_expr_ops nft_symhash_ops = {
+@@ -235,7 +200,6 @@ static const struct nft_expr_ops nft_sym
+ .eval = nft_symhash_eval,
+ .init = nft_symhash_init,
+ .dump = nft_symhash_dump,
+- .reduce = nft_symhash_reduce,
+ };
+
+ static const struct nft_expr_ops *
+--- a/net/netfilter/nft_immediate.c
++++ b/net/netfilter/nft_immediate.c
+@@ -320,17 +320,6 @@ static bool nft_immediate_offload_action
+ return false;
+ }
+
+-static bool nft_immediate_reduce(struct nft_regs_track *track,
+- const struct nft_expr *expr)
+-{
+- const struct nft_immediate_expr *priv = nft_expr_priv(expr);
+-
+- if (priv->dreg != NFT_REG_VERDICT)
+- nft_reg_track_cancel(track, priv->dreg, priv->dlen);
+-
+- return false;
+-}
+-
+ static const struct nft_expr_ops nft_imm_ops = {
+ .type = &nft_imm_type,
+ .size = NFT_EXPR_SIZE(sizeof(struct nft_immediate_expr)),
+@@ -341,7 +330,6 @@ static const struct nft_expr_ops nft_imm
+ .destroy = nft_immediate_destroy,
+ .dump = nft_immediate_dump,
+ .validate = nft_immediate_validate,
+- .reduce = nft_immediate_reduce,
+ .offload = nft_immediate_offload,
+ .offload_action = nft_immediate_offload_action,
+ };
+--- a/net/netfilter/nft_last.c
++++ b/net/netfilter/nft_last.c
+@@ -125,7 +125,6 @@ static const struct nft_expr_ops nft_las
+ .destroy = nft_last_destroy,
+ .clone = nft_last_clone,
+ .dump = nft_last_dump,
+- .reduce = NFT_REDUCE_READONLY,
+ };
+
+ struct nft_expr_type nft_last_type __read_mostly = {
+--- a/net/netfilter/nft_limit.c
++++ b/net/netfilter/nft_limit.c
+@@ -243,7 +243,6 @@ static const struct nft_expr_ops nft_lim
+ .destroy = nft_limit_pkts_destroy,
+ .clone = nft_limit_pkts_clone,
+ .dump = nft_limit_pkts_dump,
+- .reduce = NFT_REDUCE_READONLY,
+ };
+
+ static void nft_limit_bytes_eval(const struct nft_expr *expr,
+@@ -299,7 +298,6 @@ static const struct nft_expr_ops nft_lim
+ .dump = nft_limit_bytes_dump,
+ .clone = nft_limit_bytes_clone,
+ .destroy = nft_limit_bytes_destroy,
+- .reduce = NFT_REDUCE_READONLY,
+ };
+
+ static const struct nft_expr_ops *
+--- a/net/netfilter/nft_log.c
++++ b/net/netfilter/nft_log.c
+@@ -235,7 +235,6 @@ static const struct nft_expr_ops nft_log
+ .init = nft_log_init,
+ .destroy = nft_log_destroy,
+ .dump = nft_log_dump,
+- .reduce = NFT_REDUCE_READONLY,
+ };
+
+ static struct nft_expr_type nft_log_type __read_mostly = {
+--- a/net/netfilter/nft_lookup.c
++++ b/net/netfilter/nft_lookup.c
+@@ -232,17 +232,6 @@ static int nft_lookup_validate(const str
+ return 0;
+ }
+
+-static bool nft_lookup_reduce(struct nft_regs_track *track,
+- const struct nft_expr *expr)
+-{
+- const struct nft_lookup *priv = nft_expr_priv(expr);
+-
+- if (priv->set->flags & NFT_SET_MAP)
+- nft_reg_track_cancel(track, priv->dreg, priv->set->dlen);
+-
+- return false;
+-}
+-
+ static const struct nft_expr_ops nft_lookup_ops = {
+ .type = &nft_lookup_type,
+ .size = NFT_EXPR_SIZE(sizeof(struct nft_lookup)),
+@@ -253,7 +242,6 @@ static const struct nft_expr_ops nft_loo
+ .destroy = nft_lookup_destroy,
+ .dump = nft_lookup_dump,
+ .validate = nft_lookup_validate,
+- .reduce = nft_lookup_reduce,
+ };
+
+ struct nft_expr_type nft_lookup_type __read_mostly = {
+--- a/net/netfilter/nft_masq.c
++++ b/net/netfilter/nft_masq.c
+@@ -143,7 +143,6 @@ static const struct nft_expr_ops nft_mas
+ .destroy = nft_masq_ipv4_destroy,
+ .dump = nft_masq_dump,
+ .validate = nft_masq_validate,
+- .reduce = NFT_REDUCE_READONLY,
+ };
+
+ static struct nft_expr_type nft_masq_ipv4_type __read_mostly = {
+@@ -171,7 +170,6 @@ static const struct nft_expr_ops nft_mas
+ .destroy = nft_masq_ipv6_destroy,
+ .dump = nft_masq_dump,
+ .validate = nft_masq_validate,
+- .reduce = NFT_REDUCE_READONLY,
+ };
+
+ static struct nft_expr_type nft_masq_ipv6_type __read_mostly = {
+@@ -213,7 +211,6 @@ static const struct nft_expr_ops nft_mas
+ .destroy = nft_masq_inet_destroy,
+ .dump = nft_masq_dump,
+ .validate = nft_masq_validate,
+- .reduce = NFT_REDUCE_READONLY,
+ };
+
+ static struct nft_expr_type nft_masq_inet_type __read_mostly = {
+--- a/net/netfilter/nft_meta.c
++++ b/net/netfilter/nft_meta.c
+@@ -742,60 +742,16 @@ static int nft_meta_get_offload(struct n
+ return 0;
+ }
+
+-bool nft_meta_get_reduce(struct nft_regs_track *track,
+- const struct nft_expr *expr)
+-{
+- const struct nft_meta *priv = nft_expr_priv(expr);
+- const struct nft_meta *meta;
+-
+- if (!nft_reg_track_cmp(track, expr, priv->dreg)) {
+- nft_reg_track_update(track, expr, priv->dreg, priv->len);
+- return false;
+- }
+-
+- meta = nft_expr_priv(track->regs[priv->dreg].selector);
+- if (priv->key != meta->key ||
+- priv->dreg != meta->dreg) {
+- nft_reg_track_update(track, expr, priv->dreg, priv->len);
+- return false;
+- }
+-
+- if (!track->regs[priv->dreg].bitwise)
+- return true;
+-
+- return nft_expr_reduce_bitwise(track, expr);
+-}
+-EXPORT_SYMBOL_GPL(nft_meta_get_reduce);
+-
+ static const struct nft_expr_ops nft_meta_get_ops = {
+ .type = &nft_meta_type,
+ .size = NFT_EXPR_SIZE(sizeof(struct nft_meta)),
+ .eval = nft_meta_get_eval,
+ .init = nft_meta_get_init,
+ .dump = nft_meta_get_dump,
+- .reduce = nft_meta_get_reduce,
+ .validate = nft_meta_get_validate,
+ .offload = nft_meta_get_offload,
+ };
+
+-static bool nft_meta_set_reduce(struct nft_regs_track *track,
+- const struct nft_expr *expr)
+-{
+- int i;
+-
+- for (i = 0; i < NFT_REG32_NUM; i++) {
+- if (!track->regs[i].selector)
+- continue;
+-
+- if (track->regs[i].selector->ops != &nft_meta_get_ops)
+- continue;
+-
+- __nft_reg_track_cancel(track, i);
+- }
+-
+- return false;
+-}
+-
+ static const struct nft_expr_ops nft_meta_set_ops = {
+ .type = &nft_meta_type,
+ .size = NFT_EXPR_SIZE(sizeof(struct nft_meta)),
+@@ -803,7 +759,6 @@ static const struct nft_expr_ops nft_met
+ .init = nft_meta_set_init,
+ .destroy = nft_meta_set_destroy,
+ .dump = nft_meta_set_dump,
+- .reduce = nft_meta_set_reduce,
+ .validate = nft_meta_set_validate,
+ };
+
+--- a/net/netfilter/nft_nat.c
++++ b/net/netfilter/nft_nat.c
+@@ -320,7 +320,6 @@ static const struct nft_expr_ops nft_nat
+ .destroy = nft_nat_destroy,
+ .dump = nft_nat_dump,
+ .validate = nft_nat_validate,
+- .reduce = NFT_REDUCE_READONLY,
+ };
+
+ static struct nft_expr_type nft_nat_type __read_mostly = {
+@@ -351,7 +350,6 @@ static const struct nft_expr_ops nft_nat
+ .destroy = nft_nat_destroy,
+ .dump = nft_nat_dump,
+ .validate = nft_nat_validate,
+- .reduce = NFT_REDUCE_READONLY,
+ };
+
+ static struct nft_expr_type nft_inet_nat_type __read_mostly = {
+--- a/net/netfilter/nft_numgen.c
++++ b/net/netfilter/nft_numgen.c
+@@ -84,16 +84,6 @@ err:
+ return err;
+ }
+
+-static bool nft_ng_inc_reduce(struct nft_regs_track *track,
+- const struct nft_expr *expr)
+-{
+- const struct nft_ng_inc *priv = nft_expr_priv(expr);
+-
+- nft_reg_track_cancel(track, priv->dreg, NFT_REG32_SIZE);
+-
+- return false;
+-}
+-
+ static int nft_ng_dump(struct sk_buff *skb, enum nft_registers dreg,
+ u32 modulus, enum nft_ng_types type, u32 offset)
+ {
+@@ -178,16 +168,6 @@ static int nft_ng_random_dump(struct sk_
+ priv->offset);
+ }
+
+-static bool nft_ng_random_reduce(struct nft_regs_track *track,
+- const struct nft_expr *expr)
+-{
+- const struct nft_ng_random *priv = nft_expr_priv(expr);
+-
+- nft_reg_track_cancel(track, priv->dreg, NFT_REG32_SIZE);
+-
+- return false;
+-}
+-
+ static struct nft_expr_type nft_ng_type;
+ static const struct nft_expr_ops nft_ng_inc_ops = {
+ .type = &nft_ng_type,
+@@ -196,7 +176,6 @@ static const struct nft_expr_ops nft_ng_
+ .init = nft_ng_inc_init,
+ .destroy = nft_ng_inc_destroy,
+ .dump = nft_ng_inc_dump,
+- .reduce = nft_ng_inc_reduce,
+ };
+
+ static const struct nft_expr_ops nft_ng_random_ops = {
+@@ -205,7 +184,6 @@ static const struct nft_expr_ops nft_ng_
+ .eval = nft_ng_random_eval,
+ .init = nft_ng_random_init,
+ .dump = nft_ng_random_dump,
+- .reduce = nft_ng_random_reduce,
+ };
+
+ static const struct nft_expr_ops *
+--- a/net/netfilter/nft_objref.c
++++ b/net/netfilter/nft_objref.c
+@@ -123,7 +123,6 @@ static const struct nft_expr_ops nft_obj
+ .deactivate = nft_objref_deactivate,
+ .dump = nft_objref_dump,
+ .validate = nft_objref_validate,
+- .reduce = NFT_REDUCE_READONLY,
+ };
+
+ struct nft_objref_map {
+@@ -246,7 +245,6 @@ static const struct nft_expr_ops nft_obj
+ .destroy = nft_objref_map_destroy,
+ .dump = nft_objref_map_dump,
+ .validate = nft_objref_map_validate,
+- .reduce = NFT_REDUCE_READONLY,
+ };
+
+ static const struct nft_expr_ops *
+--- a/net/netfilter/nft_osf.c
++++ b/net/netfilter/nft_osf.c
+@@ -136,30 +136,6 @@ static int nft_osf_validate(const struct
+ return nft_chain_validate_hooks(ctx->chain, hooks);
+ }
+
+-static bool nft_osf_reduce(struct nft_regs_track *track,
+- const struct nft_expr *expr)
+-{
+- struct nft_osf *priv = nft_expr_priv(expr);
+- struct nft_osf *osf;
+-
+- if (!nft_reg_track_cmp(track, expr, priv->dreg)) {
+- nft_reg_track_update(track, expr, priv->dreg, NFT_OSF_MAXGENRELEN);
+- return false;
+- }
+-
+- osf = nft_expr_priv(track->regs[priv->dreg].selector);
+- if (priv->flags != osf->flags ||
+- priv->ttl != osf->ttl) {
+- nft_reg_track_update(track, expr, priv->dreg, NFT_OSF_MAXGENRELEN);
+- return false;
+- }
+-
+- if (!track->regs[priv->dreg].bitwise)
+- return true;
+-
+- return false;
+-}
+-
+ static struct nft_expr_type nft_osf_type;
+ static const struct nft_expr_ops nft_osf_op = {
+ .eval = nft_osf_eval,
+@@ -168,7 +144,6 @@ static const struct nft_expr_ops nft_osf
+ .dump = nft_osf_dump,
+ .type = &nft_osf_type,
+ .validate = nft_osf_validate,
+- .reduce = nft_osf_reduce,
+ };
+
+ static struct nft_expr_type nft_osf_type __read_mostly = {
+--- a/net/netfilter/nft_payload.c
++++ b/net/netfilter/nft_payload.c
+@@ -250,31 +250,6 @@ nla_put_failure:
+ return -1;
+ }
+
+-static bool nft_payload_reduce(struct nft_regs_track *track,
+- const struct nft_expr *expr)
+-{
+- const struct nft_payload *priv = nft_expr_priv(expr);
+- const struct nft_payload *payload;
+-
+- if (!nft_reg_track_cmp(track, expr, priv->dreg)) {
+- nft_reg_track_update(track, expr, priv->dreg, priv->len);
+- return false;
+- }
+-
+- payload = nft_expr_priv(track->regs[priv->dreg].selector);
+- if (priv->base != payload->base ||
+- priv->offset != payload->offset ||
+- priv->len != payload->len) {
+- nft_reg_track_update(track, expr, priv->dreg, priv->len);
+- return false;
+- }
+-
+- if (!track->regs[priv->dreg].bitwise)
+- return true;
+-
+- return nft_expr_reduce_bitwise(track, expr);
+-}
+-
+ static bool nft_payload_offload_mask(struct nft_offload_reg *reg,
+ u32 priv_len, u32 field_len)
+ {
+@@ -578,7 +553,6 @@ static const struct nft_expr_ops nft_pay
+ .eval = nft_payload_eval,
+ .init = nft_payload_init,
+ .dump = nft_payload_dump,
+- .reduce = nft_payload_reduce,
+ .offload = nft_payload_offload,
+ };
+
+@@ -588,7 +562,6 @@ const struct nft_expr_ops nft_payload_fa
+ .eval = nft_payload_eval,
+ .init = nft_payload_init,
+ .dump = nft_payload_dump,
+- .reduce = nft_payload_reduce,
+ .offload = nft_payload_offload,
+ };
+
+@@ -1008,32 +981,12 @@ nla_put_failure:
+ return -1;
+ }
+
+-static bool nft_payload_set_reduce(struct nft_regs_track *track,
+- const struct nft_expr *expr)
+-{
+- int i;
+-
+- for (i = 0; i < NFT_REG32_NUM; i++) {
+- if (!track->regs[i].selector)
+- continue;
+-
+- if (track->regs[i].selector->ops != &nft_payload_ops &&
+- track->regs[i].selector->ops != &nft_payload_fast_ops)
+- continue;
+-
+- __nft_reg_track_cancel(track, i);
+- }
+-
+- return false;
+-}
+-
+ static const struct nft_expr_ops nft_payload_set_ops = {
+ .type = &nft_payload_type,
+ .size = NFT_EXPR_SIZE(sizeof(struct nft_payload_set)),
+ .eval = nft_payload_set_eval,
+ .init = nft_payload_set_init,
+ .dump = nft_payload_set_dump,
+- .reduce = nft_payload_set_reduce,
+ };
+
+ static const struct nft_expr_ops *
+--- a/net/netfilter/nft_queue.c
++++ b/net/netfilter/nft_queue.c
+@@ -191,7 +191,6 @@ static const struct nft_expr_ops nft_que
+ .init = nft_queue_init,
+ .dump = nft_queue_dump,
+ .validate = nft_queue_validate,
+- .reduce = NFT_REDUCE_READONLY,
+ };
+
+ static const struct nft_expr_ops nft_queue_sreg_ops = {
+@@ -201,7 +200,6 @@ static const struct nft_expr_ops nft_que
+ .init = nft_queue_sreg_init,
+ .dump = nft_queue_sreg_dump,
+ .validate = nft_queue_validate,
+- .reduce = NFT_REDUCE_READONLY,
+ };
+
+ static const struct nft_expr_ops *
+--- a/net/netfilter/nft_quota.c
++++ b/net/netfilter/nft_quota.c
+@@ -265,7 +265,6 @@ static const struct nft_expr_ops nft_quo
+ .destroy = nft_quota_destroy,
+ .clone = nft_quota_clone,
+ .dump = nft_quota_dump,
+- .reduce = NFT_REDUCE_READONLY,
+ };
+
+ static struct nft_expr_type nft_quota_type __read_mostly = {
+--- a/net/netfilter/nft_range.c
++++ b/net/netfilter/nft_range.c
+@@ -138,7 +138,6 @@ static const struct nft_expr_ops nft_ran
+ .eval = nft_range_eval,
+ .init = nft_range_init,
+ .dump = nft_range_dump,
+- .reduce = NFT_REDUCE_READONLY,
+ };
+
+ struct nft_expr_type nft_range_type __read_mostly = {
+--- a/net/netfilter/nft_redir.c
++++ b/net/netfilter/nft_redir.c
+@@ -146,7 +146,6 @@ static const struct nft_expr_ops nft_red
+ .destroy = nft_redir_ipv4_destroy,
+ .dump = nft_redir_dump,
+ .validate = nft_redir_validate,
+- .reduce = NFT_REDUCE_READONLY,
+ };
+
+ static struct nft_expr_type nft_redir_ipv4_type __read_mostly = {
+@@ -174,7 +173,6 @@ static const struct nft_expr_ops nft_red
+ .destroy = nft_redir_ipv6_destroy,
+ .dump = nft_redir_dump,
+ .validate = nft_redir_validate,
+- .reduce = NFT_REDUCE_READONLY,
+ };
+
+ static struct nft_expr_type nft_redir_ipv6_type __read_mostly = {
+@@ -203,7 +201,6 @@ static const struct nft_expr_ops nft_red
+ .destroy = nft_redir_inet_destroy,
+ .dump = nft_redir_dump,
+ .validate = nft_redir_validate,
+- .reduce = NFT_REDUCE_READONLY,
+ };
+
+ static struct nft_expr_type nft_redir_inet_type __read_mostly = {
+--- a/net/netfilter/nft_reject_inet.c
++++ b/net/netfilter/nft_reject_inet.c
+@@ -79,7 +79,6 @@ static const struct nft_expr_ops nft_rej
+ .init = nft_reject_init,
+ .dump = nft_reject_dump,
+ .validate = nft_reject_inet_validate,
+- .reduce = NFT_REDUCE_READONLY,
+ };
+
+ static struct nft_expr_type nft_reject_inet_type __read_mostly = {
+--- a/net/netfilter/nft_reject_netdev.c
++++ b/net/netfilter/nft_reject_netdev.c
+@@ -158,7 +158,6 @@ static const struct nft_expr_ops nft_rej
+ .init = nft_reject_init,
+ .dump = nft_reject_dump,
+ .validate = nft_reject_netdev_validate,
+- .reduce = NFT_REDUCE_READONLY,
+ };
+
+ static struct nft_expr_type nft_reject_netdev_type __read_mostly = {
+--- a/net/netfilter/nft_rt.c
++++ b/net/netfilter/nft_rt.c
+@@ -195,7 +195,6 @@ static const struct nft_expr_ops nft_rt_
+ .init = nft_rt_get_init,
+ .dump = nft_rt_get_dump,
+ .validate = nft_rt_validate,
+- .reduce = NFT_REDUCE_READONLY,
+ };
+
+ struct nft_expr_type nft_rt_type __read_mostly = {
+--- a/net/netfilter/nft_socket.c
++++ b/net/netfilter/nft_socket.c
+@@ -249,31 +249,6 @@ static int nft_socket_dump(struct sk_buf
+ return 0;
+ }
+
+-static bool nft_socket_reduce(struct nft_regs_track *track,
+- const struct nft_expr *expr)
+-{
+- const struct nft_socket *priv = nft_expr_priv(expr);
+- const struct nft_socket *socket;
+-
+- if (!nft_reg_track_cmp(track, expr, priv->dreg)) {
+- nft_reg_track_update(track, expr, priv->dreg, priv->len);
+- return false;
+- }
+-
+- socket = nft_expr_priv(track->regs[priv->dreg].selector);
+- if (priv->key != socket->key ||
+- priv->dreg != socket->dreg ||
+- priv->level != socket->level) {
+- nft_reg_track_update(track, expr, priv->dreg, priv->len);
+- return false;
+- }
+-
+- if (!track->regs[priv->dreg].bitwise)
+- return true;
+-
+- return nft_expr_reduce_bitwise(track, expr);
+-}
+-
+ static int nft_socket_validate(const struct nft_ctx *ctx,
+ const struct nft_expr *expr)
+ {
+@@ -296,7 +271,6 @@ static const struct nft_expr_ops nft_soc
+ .init = nft_socket_init,
+ .dump = nft_socket_dump,
+ .validate = nft_socket_validate,
+- .reduce = nft_socket_reduce,
+ };
+
+ static struct nft_expr_type nft_socket_type __read_mostly = {
+--- a/net/netfilter/nft_synproxy.c
++++ b/net/netfilter/nft_synproxy.c
+@@ -290,7 +290,6 @@ static const struct nft_expr_ops nft_syn
+ .dump = nft_synproxy_dump,
+ .type = &nft_synproxy_type,
+ .validate = nft_synproxy_validate,
+- .reduce = NFT_REDUCE_READONLY,
+ };
+
+ static struct nft_expr_type nft_synproxy_type __read_mostly = {
+--- a/net/netfilter/nft_tproxy.c
++++ b/net/netfilter/nft_tproxy.c
+@@ -331,7 +331,6 @@ static const struct nft_expr_ops nft_tpr
+ .init = nft_tproxy_init,
+ .destroy = nft_tproxy_destroy,
+ .dump = nft_tproxy_dump,
+- .reduce = NFT_REDUCE_READONLY,
+ .validate = nft_tproxy_validate,
+ };
+
+--- a/net/netfilter/nft_tunnel.c
++++ b/net/netfilter/nft_tunnel.c
+@@ -124,31 +124,6 @@ nla_put_failure:
+ return -1;
+ }
+
+-static bool nft_tunnel_get_reduce(struct nft_regs_track *track,
+- const struct nft_expr *expr)
+-{
+- const struct nft_tunnel *priv = nft_expr_priv(expr);
+- const struct nft_tunnel *tunnel;
+-
+- if (!nft_reg_track_cmp(track, expr, priv->dreg)) {
+- nft_reg_track_update(track, expr, priv->dreg, priv->len);
+- return false;
+- }
+-
+- tunnel = nft_expr_priv(track->regs[priv->dreg].selector);
+- if (priv->key != tunnel->key ||
+- priv->dreg != tunnel->dreg ||
+- priv->mode != tunnel->mode) {
+- nft_reg_track_update(track, expr, priv->dreg, priv->len);
+- return false;
+- }
+-
+- if (!track->regs[priv->dreg].bitwise)
+- return true;
+-
+- return false;
+-}
+-
+ static struct nft_expr_type nft_tunnel_type;
+ static const struct nft_expr_ops nft_tunnel_get_ops = {
+ .type = &nft_tunnel_type,
+@@ -156,7 +131,6 @@ static const struct nft_expr_ops nft_tun
+ .eval = nft_tunnel_get_eval,
+ .init = nft_tunnel_get_init,
+ .dump = nft_tunnel_get_dump,
+- .reduce = nft_tunnel_get_reduce,
+ };
+
+ static struct nft_expr_type nft_tunnel_type __read_mostly = {
+--- a/net/netfilter/nft_xfrm.c
++++ b/net/netfilter/nft_xfrm.c
+@@ -258,32 +258,6 @@ static int nft_xfrm_validate(const struc
+ return nft_chain_validate_hooks(ctx->chain, hooks);
+ }
+
+-static bool nft_xfrm_reduce(struct nft_regs_track *track,
+- const struct nft_expr *expr)
+-{
+- const struct nft_xfrm *priv = nft_expr_priv(expr);
+- const struct nft_xfrm *xfrm;
+-
+- if (!nft_reg_track_cmp(track, expr, priv->dreg)) {
+- nft_reg_track_update(track, expr, priv->dreg, priv->len);
+- return false;
+- }
+-
+- xfrm = nft_expr_priv(track->regs[priv->dreg].selector);
+- if (priv->key != xfrm->key ||
+- priv->dreg != xfrm->dreg ||
+- priv->dir != xfrm->dir ||
+- priv->spnum != xfrm->spnum) {
+- nft_reg_track_update(track, expr, priv->dreg, priv->len);
+- return false;
+- }
+-
+- if (!track->regs[priv->dreg].bitwise)
+- return true;
+-
+- return nft_expr_reduce_bitwise(track, expr);
+-}
+-
+ static struct nft_expr_type nft_xfrm_type;
+ static const struct nft_expr_ops nft_xfrm_get_ops = {
+ .type = &nft_xfrm_type,
+@@ -292,7 +266,6 @@ static const struct nft_expr_ops nft_xfr
+ .init = nft_xfrm_get_init,
+ .dump = nft_xfrm_get_dump,
+ .validate = nft_xfrm_validate,
+- .reduce = nft_xfrm_reduce,
+ };
+
+ static struct nft_expr_type nft_xfrm_type __read_mostly = {
--- /dev/null
+From stable+bounces-288523-greg=kroah.com@vger.kernel.org Thu Jul 23 22:10:39 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 23 Jul 2026 15:58:40 -0400
+Subject: netfilter: nf_tables: Remove unused nft_reduce_is_readonly()
+To: stable@vger.kernel.org
+Cc: Yue Haibing <yuehaibing@huawei.com>, Simon Horman <horms@kernel.org>, Pablo Neira Ayuso <pablo@netfilter.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260723195842.3683768-1-sashal@kernel.org>
+
+From: Yue Haibing <yuehaibing@huawei.com>
+
+[ Upstream commit bf6788742b8d6c73de441e088a71de7154f0d4aa ]
+
+Since commit 9e539c5b6d9c ("netfilter: nf_tables: disable expression
+reduction infra") this is unused.
+
+Signed-off-by: Yue Haibing <yuehaibing@huawei.com>
+Reviewed-by: Simon Horman <horms@kernel.org>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Stable-dep-of: d07955dd34ec ("netfilter: nft_fib: reject fib expression on the netdev egress hook")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ include/net/netfilter/nf_tables.h | 5 -----
+ 1 file changed, 5 deletions(-)
+
+--- a/include/net/netfilter/nf_tables.h
++++ b/include/net/netfilter/nf_tables.h
+@@ -1851,11 +1851,6 @@ static inline u64 nft_net_tstamp(const s
+ #define __NFT_REDUCE_READONLY 1UL
+ #define NFT_REDUCE_READONLY (void *)__NFT_REDUCE_READONLY
+
+-static inline bool nft_reduce_is_readonly(const struct nft_expr *expr)
+-{
+- return expr->ops->reduce == NFT_REDUCE_READONLY;
+-}
+-
+ void nft_reg_track_update(struct nft_regs_track *track,
+ const struct nft_expr *expr, u8 dreg, u8 len);
+ void nft_reg_track_cancel(struct nft_regs_track *track, u8 dreg, u8 len);
--- /dev/null
+From stable+bounces-288524-greg=kroah.com@vger.kernel.org Thu Jul 23 22:10:45 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 23 Jul 2026 15:58:42 -0400
+Subject: netfilter: nft_fib: reject fib expression on the netdev egress hook
+To: stable@vger.kernel.org
+Cc: Theodor Arsenij Larionov-Trichkine <theodorlarionov@gmail.com>, Florian Westphal <fw@strlen.de>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260723195842.3683768-3-sashal@kernel.org>
+
+From: Theodor Arsenij Larionov-Trichkine <theodorlarionov@gmail.com>
+
+[ Upstream commit d07955dd34ecae17d35d8c7d0a273a3fba653a8c ]
+
+A fib expression in a netdev egress base chain dereferences nft_in(pkt),
+NULL on the transmit path, causing a NULL pointer dereference at eval.
+nft_fib_validate() masks the hook with NF_INET_* values, but netdev hook
+numbers are a separate enum that aliases them (NF_NETDEV_EGRESS ==
+NF_INET_LOCAL_IN), so an egress chain passes validation and then faults.
+
+Add nft_fib_netdev_validate() that limits each result/flag to the netdev
+hook where the device it reads exists: the input-device cases (OIF,
+OIFNAME, ADDRTYPE with F_IIF) to ingress, the output-device case (ADDRTYPE
+with F_OIF) to egress, ADDRTYPE with no device flag to both. Also restrict
+nft_fib_validate() to NFPROTO_IPV4/IPV6/INET so its NF_INET_* masks are
+not applied to another family's hooks.
+
+Fixes: 42df6e1d221d ("netfilter: Introduce egress hook")
+Cc: stable@vger.kernel.org
+Link: https://lore.kernel.org/netfilter-devel/ajxsjcDOnwllMfoR@strlen.de/
+Signed-off-by: Theodor Arsenij Larionov-Trichkine <theodorlarionov@gmail.com>
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/netfilter/nft_fib.c | 9 +++++++++
+ net/netfilter/nft_fib_netdev.c | 29 ++++++++++++++++++++++++++++-
+ 2 files changed, 37 insertions(+), 1 deletion(-)
+
+--- a/net/netfilter/nft_fib.c
++++ b/net/netfilter/nft_fib.c
+@@ -31,6 +31,15 @@ int nft_fib_validate(const struct nft_ct
+ const struct nft_fib *priv = nft_expr_priv(expr);
+ unsigned int hooks;
+
++ switch (ctx->family) {
++ case NFPROTO_IPV4:
++ case NFPROTO_IPV6:
++ case NFPROTO_INET:
++ break;
++ default:
++ return -EOPNOTSUPP;
++ }
++
+ switch (priv->result) {
+ case NFT_FIB_RESULT_OIF:
+ case NFT_FIB_RESULT_OIFNAME:
+--- a/net/netfilter/nft_fib_netdev.c
++++ b/net/netfilter/nft_fib_netdev.c
+@@ -50,6 +50,33 @@ static void nft_fib_netdev_eval(const st
+ regs->verdict.code = NFT_BREAK;
+ }
+
++static int nft_fib_netdev_validate(const struct nft_ctx *ctx,
++ const struct nft_expr *expr)
++{
++ const struct nft_fib *priv = nft_expr_priv(expr);
++ unsigned int hooks;
++
++ switch (priv->result) {
++ case NFT_FIB_RESULT_OIF:
++ case NFT_FIB_RESULT_OIFNAME:
++ hooks = (1 << NF_NETDEV_INGRESS);
++ break;
++ case NFT_FIB_RESULT_ADDRTYPE:
++ if (priv->flags & NFTA_FIB_F_IIF)
++ hooks = (1 << NF_NETDEV_INGRESS);
++ else if (priv->flags & NFTA_FIB_F_OIF)
++ hooks = (1 << NF_NETDEV_EGRESS);
++ else
++ hooks = (1 << NF_NETDEV_INGRESS) |
++ (1 << NF_NETDEV_EGRESS);
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ return nft_chain_validate_hooks(ctx->chain, hooks);
++}
++
+ static struct nft_expr_type nft_fib_netdev_type;
+ static const struct nft_expr_ops nft_fib_netdev_ops = {
+ .type = &nft_fib_netdev_type,
+@@ -57,7 +84,7 @@ static const struct nft_expr_ops nft_fib
+ .eval = nft_fib_netdev_eval,
+ .init = nft_fib_init,
+ .dump = nft_fib_dump,
+- .validate = nft_fib_validate,
++ .validate = nft_fib_netdev_validate,
+ };
+
+ static struct nft_expr_type nft_fib_netdev_type __read_mostly = {
--- /dev/null
+From stable+bounces-288345-greg=kroah.com@vger.kernel.org Thu Jul 23 16:20:55 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 23 Jul 2026 10:18:55 -0400
+Subject: netfilter: nft_set_pipapo: don't leak bad clone into future transaction
+To: stable@vger.kernel.org
+Cc: Florian Westphal <fw@strlen.de>, Seesee <cjc000013@gmail.com>, Stefano Brivio <sbrivio@redhat.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260723141855.3217594-8-sashal@kernel.org>
+
+From: Florian Westphal <fw@strlen.de>
+
+[ Upstream commit 47e65eff50691f0a5b79d325e28d83ec1da43bcf ]
+
+On memory allocation failure the cloned nft_pipapo_match can enter a bad
+state:
+ - some fields can have their lookup tables resized while others did
+ not
+ - bits might have been toggled
+ - scratch map can be undersized which also means m->bsize_max can be
+ lower than what is required
+
+This means that the next insertion in the same batch can trigger
+out-of-bounds writes.
+
+Furthermore, a failure in the first can result in the bad clone to
+leak into the next transaction because the abort callback is never
+executed in this case (the upper layer saw an error and no attempt to
+allocate a transactional request was made).
+
+Record a state for the nft_pipapo_match structure:
+- NEW (pristine clone)
+- MOD (modified clone with good state)
+- ERR (potentially bogus content)
+
+Then make it so that deletes and insertions fail when the clone
+entered ERR state.
+
+In case the very first insert attempt results in an error, free the
+clone right away.
+
+Fixes: 3c4287f62044 ("nf_tables: Add set type for arbitrary concatenation of ranges")
+Cc: stable@vger.kernel.org
+Reported-and-tested-by: Seesee <cjc000013@gmail.com>
+Reviewed-by: Stefano Brivio <sbrivio@redhat.com>
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/netfilter/nft_set_pipapo.c | 34 +++++++++++++++++++++++++++++-----
+ net/netfilter/nft_set_pipapo.h | 8 ++++++++
+ 2 files changed, 37 insertions(+), 5 deletions(-)
+
+--- a/net/netfilter/nft_set_pipapo.c
++++ b/net/netfilter/nft_set_pipapo.c
+@@ -342,6 +342,8 @@
+ #include "nft_set_pipapo_avx2.h"
+ #include "nft_set_pipapo.h"
+
++static void nft_pipapo_abort(const struct nft_set *set);
++
+ /**
+ * pipapo_refill() - For each set bit, set bits from selected mapping table item
+ * @map: Bitmap to be scanned for set bits
+@@ -1278,7 +1280,7 @@ static int nft_pipapo_insert(const struc
+ const u8 *start_p, *end_p;
+ int i, bsize_max, err = 0;
+
+- if (!m)
++ if (!m || m->state == NFT_PIPAPO_CLONE_ERR)
+ return -ENOMEM;
+
+ if (nft_set_ext_exists(ext, NFT_SET_EXT_KEY_END))
+@@ -1351,8 +1353,10 @@ static int nft_pipapo_insert(const struc
+ else
+ ret = pipapo_expand(f, start, end, f->groups * f->bb);
+
+- if (ret < 0)
+- return ret;
++ if (ret < 0) {
++ err = ret;
++ goto abort;
++ }
+
+ if (f->bsize > bsize_max)
+ bsize_max = f->bsize;
+@@ -1368,7 +1372,7 @@ static int nft_pipapo_insert(const struc
+
+ err = pipapo_realloc_scratch(m, bsize_max);
+ if (err)
+- return err;
++ goto abort;
+
+ m->bsize_max = bsize_max;
+ } else {
+@@ -1379,7 +1383,26 @@ static int nft_pipapo_insert(const struc
+
+ pipapo_map(m, rulemap, e);
+
++ m->state = NFT_PIPAPO_CLONE_MOD;
+ return 0;
++abort:
++ DEBUG_NET_WARN_ON_ONCE(m->state == NFT_PIPAPO_CLONE_ERR);
++
++ /* Two rollback cases:
++ * 1) no previous changes. nft_pipapo_abort is not
++ * guaranteed to be invoked (there might be no further
++ * add/delete requests coming after this).
++ *
++ * 2) we had previous changes: there are transaction
++ * records pointing to this set. Leave the rollback to
++ * the transaction handling.
++ */
++ if (m->state == NFT_PIPAPO_CLONE_NEW)
++ nft_pipapo_abort(set); /* releases m */
++ else
++ m->state = NFT_PIPAPO_CLONE_ERR;
++
++ return err;
+ }
+
+ /**
+@@ -1455,6 +1478,7 @@ static struct nft_pipapo_match *pipapo_c
+ dst++;
+ }
+
++ new->state = NFT_PIPAPO_CLONE_NEW;
+ return new;
+
+ out_mt:
+@@ -1891,7 +1915,7 @@ static void *nft_pipapo_deactivate(const
+ /* removal must occur on priv->clone, if we are low on memory
+ * we have no choice and must fail the removal request.
+ */
+- if (!m)
++ if (!m || m->state == NFT_PIPAPO_CLONE_ERR)
+ return NULL;
+
+ e = pipapo_get(net, set, m, (const u8 *)elem->key.val.data,
+--- a/net/netfilter/nft_set_pipapo.h
++++ b/net/netfilter/nft_set_pipapo.h
+@@ -142,9 +142,16 @@ struct nft_pipapo_scratch {
+ unsigned long map[];
+ };
+
++enum nft_pipapo_clone_state {
++ NFT_PIPAPO_CLONE_NEW,
++ NFT_PIPAPO_CLONE_MOD,
++ NFT_PIPAPO_CLONE_ERR,
++};
++
+ /**
+ * struct nft_pipapo_match - Data used for lookup and matching
+ * @field_count Amount of fields in set
++ * @state: add/delete state; used from control plane
+ * @scratch: Preallocated per-CPU maps for partial matching results
+ * @bsize_max: Maximum lookup table bucket size of all fields, in longs
+ * @rcu Matching data is swapped on commits
+@@ -152,6 +159,7 @@ struct nft_pipapo_scratch {
+ */
+ struct nft_pipapo_match {
+ int field_count;
++ enum nft_pipapo_clone_state state:8;
+ struct nft_pipapo_scratch * __percpu *scratch;
+ size_t bsize_max;
+ struct rcu_head rcu;
--- /dev/null
+From stable+bounces-288340-greg=kroah.com@vger.kernel.org Thu Jul 23 16:28:39 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 23 Jul 2026 10:18:50 -0400
+Subject: netfilter: nft_set_pipapo: make pipapo_clone helper return NULL
+To: stable@vger.kernel.org
+Cc: Florian Westphal <fw@strlen.de>, Stefano Brivio <sbrivio@redhat.com>, Pablo Neira Ayuso <pablo@netfilter.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260723141855.3217594-3-sashal@kernel.org>
+
+From: Florian Westphal <fw@strlen.de>
+
+[ Upstream commit 80efd2997fb9343a0283cf3cac5524a4595c8ff4 ]
+
+Currently it returns an error pointer, but the only possible failure
+is ENOMEM.
+
+After a followup patch, we'd need to discard the errno code, i.e.
+
+x = pipapo_clone()
+if (IS_ERR(x))
+ return NULL
+
+or make more changes to fix up callers to expect IS_ERR() code
+from set->ops->deactivate().
+
+So simplify this and make it return ptr-or-null.
+
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Reviewed-by: Stefano Brivio <sbrivio@redhat.com>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Stable-dep-of: 47e65eff5069 ("netfilter: nft_set_pipapo: don't leak bad clone into future transaction")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/netfilter/nft_set_pipapo.c | 14 +++++++-------
+ 1 file changed, 7 insertions(+), 7 deletions(-)
+
+--- a/net/netfilter/nft_set_pipapo.c
++++ b/net/netfilter/nft_set_pipapo.c
+@@ -1358,7 +1358,7 @@ static int nft_pipapo_insert(const struc
+ * pipapo_clone() - Clone matching data to create new working copy
+ * @old: Existing matching data
+ *
+- * Return: copy of matching data passed as 'old', error pointer on failure
++ * Return: copy of matching data passed as 'old' or NULL.
+ */
+ static struct nft_pipapo_match *pipapo_clone(struct nft_pipapo_match *old)
+ {
+@@ -1368,7 +1368,7 @@ static struct nft_pipapo_match *pipapo_c
+
+ new = kmalloc(struct_size(new, f, old->field_count), GFP_KERNEL_ACCOUNT);
+ if (!new)
+- return ERR_PTR(-ENOMEM);
++ return NULL;
+
+ new->field_count = old->field_count;
+ new->bsize_max = old->bsize_max;
+@@ -1444,7 +1444,7 @@ out_scratch:
+ free_percpu(new->scratch);
+ kfree(new);
+
+- return ERR_PTR(-ENOMEM);
++ return NULL;
+ }
+
+ /**
+@@ -1798,7 +1798,7 @@ static void nft_pipapo_commit(struct nft
+ return;
+
+ new_clone = pipapo_clone(priv->clone);
+- if (IS_ERR(new_clone))
++ if (!new_clone)
+ return;
+
+ priv->dirty = false;
+@@ -1824,7 +1824,7 @@ static void nft_pipapo_abort(const struc
+ m = rcu_dereference_protected(priv->match, nft_pipapo_transaction_mutex_held(set));
+
+ new_clone = pipapo_clone(m);
+- if (IS_ERR(new_clone))
++ if (!new_clone)
+ return;
+
+ priv->dirty = false;
+@@ -2265,8 +2265,8 @@ static int nft_pipapo_init(const struct
+
+ /* Create an initial clone of matching data for next insertion */
+ priv->clone = pipapo_clone(m);
+- if (IS_ERR(priv->clone)) {
+- err = PTR_ERR(priv->clone);
++ if (!priv->clone) {
++ err = -ENOMEM;
+ goto out_free;
+ }
+
--- /dev/null
+From stable+bounces-288342-greg=kroah.com@vger.kernel.org Thu Jul 23 16:28:43 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 23 Jul 2026 10:18:52 -0400
+Subject: netfilter: nft_set_pipapo: merge deactivate helper into caller
+To: stable@vger.kernel.org
+Cc: Florian Westphal <fw@strlen.de>, Stefano Brivio <sbrivio@redhat.com>, Pablo Neira Ayuso <pablo@netfilter.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260723141855.3217594-5-sashal@kernel.org>
+
+From: Florian Westphal <fw@strlen.de>
+
+[ Upstream commit c5444786d0ea2417a5e2cee7bd67137fc8bad687 ]
+
+Its the only remaining call site so there is no need for this to
+be separated anymore.
+
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Reviewed-by: Stefano Brivio <sbrivio@redhat.com>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Stable-dep-of: 47e65eff5069 ("netfilter: nft_set_pipapo: don't leak bad clone into future transaction")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/netfilter/nft_set_pipapo.c | 39 +++++++++------------------------------
+ 1 file changed, 9 insertions(+), 30 deletions(-)
+
+--- a/net/netfilter/nft_set_pipapo.c
++++ b/net/netfilter/nft_set_pipapo.c
+@@ -1854,52 +1854,31 @@ static void nft_pipapo_activate(const st
+ }
+
+ /**
+- * pipapo_deactivate() - Check that element is in set, mark as inactive
++ * nft_pipapo_deactivate() - Search for element and make it inactive
+ * @net: Network namespace
+ * @set: nftables API set representation
+- * @data: Input key data
+- * @ext: nftables API extension pointer, used to check for end element
+- *
+- * This is a convenience function that can be called from both
+- * nft_pipapo_deactivate() and nft_pipapo_flush(), as they are in fact the same
+- * operation.
++ * @elem: nftables API element representation containing key data
+ *
+ * Return: deactivated element if found, NULL otherwise.
+ */
+-static void *pipapo_deactivate(const struct net *net, const struct nft_set *set,
+- const u8 *data, const struct nft_set_ext *ext)
++static void *nft_pipapo_deactivate(const struct net *net,
++ const struct nft_set *set,
++ const struct nft_set_elem *elem)
+ {
+ struct nft_pipapo_elem *e;
+
+- e = pipapo_get(net, set, data, nft_genmask_next(net),
+- nft_net_tstamp(net), GFP_KERNEL);
++ e = pipapo_get(net, set, (const u8 *)elem->key.val.data,
++ nft_genmask_next(net), nft_net_tstamp(net), GFP_KERNEL);
+ if (IS_ERR(e))
+ return NULL;
+
+ nft_set_elem_change_active(net, set, &e->ext);
+
+- return e;
+-}
+-
+-/**
+- * nft_pipapo_deactivate() - Call pipapo_deactivate() to make element inactive
+- * @net: Network namespace
+- * @set: nftables API set representation
+- * @elem: nftables API element representation containing key data
+- *
+- * Return: deactivated element if found, NULL otherwise.
+- */
+-static void *nft_pipapo_deactivate(const struct net *net,
+- const struct nft_set *set,
+- const struct nft_set_elem *elem)
+-{
+- const struct nft_set_ext *ext = nft_set_elem_ext(set, elem->priv);
+-
+- return pipapo_deactivate(net, set, (const u8 *)elem->key.val.data, ext);
++ return &e->priv;
+ }
+
+ /**
+- * nft_pipapo_flush() - Call pipapo_deactivate() to make element inactive
++ * nft_pipapo_flush() - make element inactive
+ * @net: Network namespace
+ * @set: nftables API set representation
+ * @elem: nftables API element representation containing key data
--- /dev/null
+From stable+bounces-288343-greg=kroah.com@vger.kernel.org Thu Jul 23 16:28:45 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 23 Jul 2026 10:18:54 -0400
+Subject: netfilter: nft_set_pipapo: move cloning of match info to insert/removal path
+To: stable@vger.kernel.org
+Cc: Florian Westphal <fw@strlen.de>, Stefano Brivio <sbrivio@redhat.com>, Pablo Neira Ayuso <pablo@netfilter.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260723141855.3217594-7-sashal@kernel.org>
+
+From: Florian Westphal <fw@strlen.de>
+
+[ Upstream commit 3f1d886cc7c3525d4dbeee24bfa9bb3fe0d48ddc ]
+
+This set type keeps two copies of the sets' content,
+ priv->match (live version, used to match from packet path)
+ priv->clone (work-in-progress version of the 'future' priv->match).
+
+All additions and removals are done on priv->clone. When transaction
+completes, priv->clone becomes priv->match and a new clone is allocated
+for use by next transaction.
+
+Problem is that the cloning requires GFP_KERNEL allocations but we
+cannot fail at either commit or abort time.
+
+This patch defers the clone until we get an insertion or removal
+request. This allows us to handle OOM situations correctly.
+
+This also allows to remove ->dirty in a followup change:
+
+If ->clone exists, ->dirty is always true
+If ->clone is NULL, ->dirty is always false, no elements were added
+or removed (except catchall elements which are external to the specific
+set backend).
+
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Reviewed-by: Stefano Brivio <sbrivio@redhat.com>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Stable-dep-of: 47e65eff5069 ("netfilter: nft_set_pipapo: don't leak bad clone into future transaction")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/netfilter/nft_set_pipapo.c | 70 ++++++++++++++++++++++++++++-------------
+ 1 file changed, 49 insertions(+), 21 deletions(-)
+
+--- a/net/netfilter/nft_set_pipapo.c
++++ b/net/netfilter/nft_set_pipapo.c
+@@ -1230,6 +1230,29 @@ static bool nft_pipapo_transaction_mutex
+ #endif
+ }
+
++static struct nft_pipapo_match *pipapo_clone(struct nft_pipapo_match *old);
++
++/**
++ * pipapo_maybe_clone() - Build clone for pending data changes, if not existing
++ * @set: nftables API set representation
++ *
++ * Return: newly created or existing clone, if any. NULL on allocation failure
++ */
++static struct nft_pipapo_match *pipapo_maybe_clone(const struct nft_set *set)
++{
++ struct nft_pipapo *priv = nft_set_priv(set);
++ struct nft_pipapo_match *m;
++
++ if (priv->clone)
++ return priv->clone;
++
++ m = rcu_dereference_protected(priv->match,
++ nft_pipapo_transaction_mutex_held(set));
++ priv->clone = pipapo_clone(m);
++
++ return priv->clone;
++}
++
+ /**
+ * nft_pipapo_insert() - Validate and insert ranged elements
+ * @net: Network namespace
+@@ -1247,14 +1270,17 @@ static int nft_pipapo_insert(const struc
+ union nft_pipapo_map_bucket rulemap[NFT_PIPAPO_MAX_FIELDS];
+ const u8 *start = (const u8 *)elem->key.val.data, *end;
+ struct nft_pipapo_elem *e = elem->priv, *dup;
++ struct nft_pipapo_match *m = pipapo_maybe_clone(set);
+ struct nft_pipapo *priv = nft_set_priv(set);
+- struct nft_pipapo_match *m = priv->clone;
+ u8 genmask = nft_genmask_next(net);
+ u64 tstamp = nft_net_tstamp(net);
+ struct nft_pipapo_field *f;
+ const u8 *start_p, *end_p;
+ int i, bsize_max, err = 0;
+
++ if (!m)
++ return -ENOMEM;
++
+ if (nft_set_ext_exists(ext, NFT_SET_EXT_KEY_END))
+ end = (const u8 *)nft_set_ext_key_end(ext)->data;
+ else
+@@ -1791,7 +1817,10 @@ static void pipapo_reclaim_match(struct
+ static void nft_pipapo_commit(struct nft_set *set)
+ {
+ struct nft_pipapo *priv = nft_set_priv(set);
+- struct nft_pipapo_match *new_clone, *old;
++ struct nft_pipapo_match *old;
++
++ if (!priv->clone)
++ return;
+
+ if (time_after_eq(jiffies, priv->last_gc + nft_set_gc_interval(set)))
+ pipapo_gc_scan(set, priv->clone);
+@@ -1799,40 +1828,29 @@ static void nft_pipapo_commit(struct nft
+ if (!priv->dirty)
+ return;
+
+- new_clone = pipapo_clone(priv->clone);
+- if (!new_clone)
+- return;
+-
++ old = rcu_replace_pointer(priv->match, priv->clone,
++ nft_pipapo_transaction_mutex_held(set));
++ priv->clone = NULL;
+ priv->dirty = false;
+
+- old = rcu_access_pointer(priv->match);
+- rcu_assign_pointer(priv->match, priv->clone);
+ if (old)
+ call_rcu(&old->rcu, pipapo_reclaim_match);
+
+- priv->clone = new_clone;
+-
+ pipapo_gc_queue(set);
+ }
+
+ static void nft_pipapo_abort(const struct nft_set *set)
+ {
+ struct nft_pipapo *priv = nft_set_priv(set);
+- struct nft_pipapo_match *new_clone, *m;
+
+ if (!priv->dirty)
+ return;
+
+- m = rcu_dereference_protected(priv->match, nft_pipapo_transaction_mutex_held(set));
+-
+- new_clone = pipapo_clone(m);
+- if (!new_clone)
++ if (!priv->clone)
+ return;
+-
+ priv->dirty = false;
+-
+ pipapo_free_match(priv->clone);
+- priv->clone = new_clone;
++ priv->clone = NULL;
+ }
+
+ /**
+@@ -1867,10 +1885,15 @@ static void *nft_pipapo_deactivate(const
+ const struct nft_set *set,
+ const struct nft_set_elem *elem)
+ {
+- const struct nft_pipapo *priv = nft_set_priv(set);
+- struct nft_pipapo_match *m = priv->clone;
++ struct nft_pipapo_match *m = pipapo_maybe_clone(set);
+ struct nft_pipapo_elem *e;
+
++ /* removal must occur on priv->clone, if we are low on memory
++ * we have no choice and must fail the removal request.
++ */
++ if (!m)
++ return NULL;
++
+ e = pipapo_get(net, set, m, (const u8 *)elem->key.val.data,
+ nft_genmask_next(net), nft_net_tstamp(net), GFP_KERNEL);
+ if (IS_ERR(e))
+@@ -2153,7 +2176,12 @@ static void nft_pipapo_walk(const struct
+
+ switch (iter->type) {
+ case NFT_ITER_UPDATE:
+- m = priv->clone;
++ m = pipapo_maybe_clone(set);
++ if (!m) {
++ iter->err = -ENOMEM;
++ return;
++ }
++
+ nft_pipapo_do_walk(ctx, set, m, iter);
+ break;
+ case NFT_ITER_READ:
--- /dev/null
+From stable+bounces-288338-greg=kroah.com@vger.kernel.org Thu Jul 23 16:28:34 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 23 Jul 2026 10:18:49 -0400
+Subject: netfilter: nft_set_pipapo: move prove_locking helper around
+To: stable@vger.kernel.org
+Cc: Florian Westphal <fw@strlen.de>, Stefano Brivio <sbrivio@redhat.com>, Pablo Neira Ayuso <pablo@netfilter.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260723141855.3217594-2-sashal@kernel.org>
+
+From: Florian Westphal <fw@strlen.de>
+
+[ Upstream commit a590f4760922acaa2d2b55a88004a38eecdd6412 ]
+
+Preparation patch, the helper will soon get called from insert
+function too.
+
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Reviewed-by: Stefano Brivio <sbrivio@redhat.com>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Stable-dep-of: 47e65eff5069 ("netfilter: nft_set_pipapo: don't leak bad clone into future transaction")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/netfilter/nft_set_pipapo.c | 22 +++++++++++-----------
+ 1 file changed, 11 insertions(+), 11 deletions(-)
+
+--- a/net/netfilter/nft_set_pipapo.c
++++ b/net/netfilter/nft_set_pipapo.c
+@@ -1217,6 +1217,17 @@ static int pipapo_realloc_scratch(struct
+ return 0;
+ }
+
++static bool nft_pipapo_transaction_mutex_held(const struct nft_set *set)
++{
++#ifdef CONFIG_PROVE_LOCKING
++ const struct net *net = read_pnet(&set->net);
++
++ return lockdep_is_held(&nft_pernet(net)->commit_mutex);
++#else
++ return true;
++#endif
++}
++
+ /**
+ * nft_pipapo_insert() - Validate and insert ranged elements
+ * @net: Network namespace
+@@ -1802,17 +1813,6 @@ static void nft_pipapo_commit(struct nft
+ pipapo_gc_queue(set);
+ }
+
+-static bool nft_pipapo_transaction_mutex_held(const struct nft_set *set)
+-{
+-#ifdef CONFIG_PROVE_LOCKING
+- const struct net *net = read_pnet(&set->net);
+-
+- return lockdep_is_held(&nft_pernet(net)->commit_mutex);
+-#else
+- return true;
+-#endif
+-}
+-
+ static void nft_pipapo_abort(const struct nft_set *set)
+ {
+ struct nft_pipapo *priv = nft_set_priv(set);
--- /dev/null
+From stable+bounces-288344-greg=kroah.com@vger.kernel.org Thu Jul 23 16:31:22 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 23 Jul 2026 10:18:53 -0400
+Subject: netfilter: nft_set_pipapo: prepare pipapo_get helper for on-demand clone
+To: stable@vger.kernel.org
+Cc: Florian Westphal <fw@strlen.de>, Stefano Brivio <sbrivio@redhat.com>, Pablo Neira Ayuso <pablo@netfilter.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260723141855.3217594-6-sashal@kernel.org>
+
+From: Florian Westphal <fw@strlen.de>
+
+[ Upstream commit a238106703ab4ae1090b86eba128815b8626d8f1 ]
+
+The helper uses priv->clone unconditionally which will fail once we do
+the clone conditionally on first insert or removal.
+
+'nft get element' from userspace needs to use priv->match since this
+runs from rcu read side lock section.
+
+Prepare for this by passing the match backend data as argument.
+
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Reviewed-by: Stefano Brivio <sbrivio@redhat.com>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Stable-dep-of: 47e65eff5069 ("netfilter: nft_set_pipapo: don't leak bad clone into future transaction")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/netfilter/nft_set_pipapo.c | 25 +++++++++++++++----------
+ 1 file changed, 15 insertions(+), 10 deletions(-)
+
+--- a/net/netfilter/nft_set_pipapo.c
++++ b/net/netfilter/nft_set_pipapo.c
+@@ -502,6 +502,7 @@ out:
+ * pipapo_get() - Get matching element reference given key data
+ * @net: Network namespace
+ * @set: nftables API set representation
++ * @m: storage containing active/existing elements
+ * @data: Key data to be matched against existing elements
+ * @genmask: If set, check that element is active in given genmask
+ * @tstamp: timestamp to check for expired elements
+@@ -515,17 +516,15 @@ out:
+ */
+ static struct nft_pipapo_elem *pipapo_get(const struct net *net,
+ const struct nft_set *set,
++ const struct nft_pipapo_match *m,
+ const u8 *data, u8 genmask,
+ u64 tstamp, gfp_t gfp)
+ {
+ struct nft_pipapo_elem *ret = ERR_PTR(-ENOENT);
+- struct nft_pipapo *priv = nft_set_priv(set);
+ unsigned long *res_map, *fill_map = NULL;
+- const struct nft_pipapo_match *m;
+ const struct nft_pipapo_field *f;
+ int i;
+
+- m = priv->clone;
+ if (m->bsize_max == 0)
+ return ret;
+
+@@ -609,7 +608,10 @@ out:
+ static void *nft_pipapo_get(const struct net *net, const struct nft_set *set,
+ const struct nft_set_elem *elem, unsigned int flags)
+ {
+- return pipapo_get(net, set, (const u8 *)elem->key.val.data,
++ struct nft_pipapo *priv = nft_set_priv(set);
++ struct nft_pipapo_match *m = rcu_dereference(priv->match);
++
++ return pipapo_get(net, set, m, (const u8 *)elem->key.val.data,
+ nft_genmask_cur(net), get_jiffies_64(), GFP_ATOMIC);
+ }
+
+@@ -1258,7 +1260,7 @@ static int nft_pipapo_insert(const struc
+ else
+ end = start;
+
+- dup = pipapo_get(net, set, start, genmask, tstamp, GFP_KERNEL);
++ dup = pipapo_get(net, set, m, start, genmask, tstamp, GFP_KERNEL);
+ if (!IS_ERR(dup)) {
+ /* Check if we already have the same exact entry */
+ const struct nft_data *dup_key, *dup_end;
+@@ -1280,7 +1282,7 @@ static int nft_pipapo_insert(const struc
+
+ if (PTR_ERR(dup) == -ENOENT) {
+ /* Look for partially overlapping entries */
+- dup = pipapo_get(net, set, end, nft_genmask_next(net), tstamp,
++ dup = pipapo_get(net, set, m, end, nft_genmask_next(net), tstamp,
+ GFP_KERNEL);
+ }
+
+@@ -1865,16 +1867,18 @@ static void *nft_pipapo_deactivate(const
+ const struct nft_set *set,
+ const struct nft_set_elem *elem)
+ {
++ const struct nft_pipapo *priv = nft_set_priv(set);
++ struct nft_pipapo_match *m = priv->clone;
+ struct nft_pipapo_elem *e;
+
+- e = pipapo_get(net, set, (const u8 *)elem->key.val.data,
++ e = pipapo_get(net, set, m, (const u8 *)elem->key.val.data,
+ nft_genmask_next(net), nft_net_tstamp(net), GFP_KERNEL);
+ if (IS_ERR(e))
+ return NULL;
+
+ nft_set_elem_change_active(net, set, &e->ext);
+
+- return &e->priv;
++ return e;
+ }
+
+ /**
+@@ -1900,8 +1904,9 @@ static bool nft_pipapo_flush(const struc
+ {
+ struct nft_pipapo_elem *e = elem;
+
+- return pipapo_deactivate(net, set, (const u8 *)nft_set_ext_key(&e->ext),
+- &e->ext);
++ nft_set_elem_change_active(net, set, &e->ext);
++
++ return true;
+ }
+
+ /**
--- /dev/null
+From stable+bounces-288341-greg=kroah.com@vger.kernel.org Thu Jul 23 16:31:07 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 23 Jul 2026 10:18:51 -0400
+Subject: netfilter: nft_set_pipapo: prepare walk function for on-demand clone
+To: stable@vger.kernel.org
+Cc: Florian Westphal <fw@strlen.de>, Stefano Brivio <sbrivio@redhat.com>, Pablo Neira Ayuso <pablo@netfilter.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260723141855.3217594-4-sashal@kernel.org>
+
+From: Florian Westphal <fw@strlen.de>
+
+[ Upstream commit 6c108d9bee448a850b03e682836bfe91fca645cb ]
+
+The existing code uses iter->type to figure out what data is needed, the
+live copy (READ) or clone (UPDATE).
+
+Without pending updates, priv->clone and priv->match will point to
+different memory locations, but they have identical content.
+
+Future patch will make priv->clone == NULL if there are no pending changes,
+in this case we must copy the live data for the UPDATE case.
+
+Currently this would require GFP_ATOMIC allocation. Split the walk
+function in two parts: one that does the walk and one that decides which
+data is needed.
+
+In the UPDATE case, callers hold the transaction mutex so we do not need
+the rcu read lock. This allows to use GFP_KERNEL allocation while
+cloning.
+
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Reviewed-by: Stefano Brivio <sbrivio@redhat.com>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Stable-dep-of: 47e65eff5069 ("netfilter: nft_set_pipapo: don't leak bad clone into future transaction")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/netfilter/nft_set_pipapo.c | 60 ++++++++++++++++++++++++++---------------
+ 1 file changed, 39 insertions(+), 21 deletions(-)
+
+--- a/net/netfilter/nft_set_pipapo.c
++++ b/net/netfilter/nft_set_pipapo.c
+@@ -2109,35 +2109,23 @@ static void nft_pipapo_remove(const stru
+ }
+
+ /**
+- * nft_pipapo_walk() - Walk over elements
++ * nft_pipapo_do_walk() - Walk over elements in m
+ * @ctx: nftables API context
+ * @set: nftables API set representation
++ * @m: matching data pointing to key mapping array
+ * @iter: Iterator
+ *
+ * As elements are referenced in the mapping array for the last field, directly
+ * scan that array: there's no need to follow rule mappings from the first
+- * field.
++ * field. @m is protected either by RCU read lock or by transaction mutex.
+ */
+-static void nft_pipapo_walk(const struct nft_ctx *ctx, struct nft_set *set,
+- struct nft_set_iter *iter)
++static void nft_pipapo_do_walk(const struct nft_ctx *ctx, struct nft_set *set,
++ const struct nft_pipapo_match *m,
++ struct nft_set_iter *iter)
+ {
+- struct nft_pipapo *priv = nft_set_priv(set);
+- const struct nft_pipapo_match *m;
+ const struct nft_pipapo_field *f;
+ int i, r;
+
+- WARN_ON_ONCE(iter->type != NFT_ITER_READ &&
+- iter->type != NFT_ITER_UPDATE);
+-
+- rcu_read_lock();
+- if (iter->type == NFT_ITER_READ)
+- m = rcu_dereference(priv->match);
+- else
+- m = priv->clone;
+-
+- if (unlikely(!m))
+- goto out;
+-
+ for (i = 0, f = m->f; i < m->field_count - 1; i++, f++)
+ ;
+
+@@ -2157,14 +2145,44 @@ static void nft_pipapo_walk(const struct
+
+ iter->err = iter->fn(ctx, set, iter, &elem);
+ if (iter->err < 0)
+- goto out;
++ return;
+
+ cont:
+ iter->count++;
+ }
++}
+
+-out:
+- rcu_read_unlock();
++/**
++ * nft_pipapo_walk() - Walk over elements
++ * @ctx: nftables API context
++ * @set: nftables API set representation
++ * @iter: Iterator
++ *
++ * Test if destructive action is needed or not, clone active backend if needed
++ * and call the real function to work on the data.
++ */
++static void nft_pipapo_walk(const struct nft_ctx *ctx, struct nft_set *set,
++ struct nft_set_iter *iter)
++{
++ struct nft_pipapo *priv = nft_set_priv(set);
++ const struct nft_pipapo_match *m;
++
++ switch (iter->type) {
++ case NFT_ITER_UPDATE:
++ m = priv->clone;
++ nft_pipapo_do_walk(ctx, set, m, iter);
++ break;
++ case NFT_ITER_READ:
++ rcu_read_lock();
++ m = rcu_dereference(priv->match);
++ nft_pipapo_do_walk(ctx, set, m, iter);
++ rcu_read_unlock();
++ break;
++ default:
++ iter->err = -EINVAL;
++ WARN_ON_ONCE(1);
++ break;
++ }
+ }
+
+ /**
--- /dev/null
+From stable+bounces-288339-greg=kroah.com@vger.kernel.org Thu Jul 23 16:31:05 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 23 Jul 2026 10:18:48 -0400
+Subject: netfilter: nft_set_pipapo: use GFP_KERNEL for insertions
+To: stable@vger.kernel.org
+Cc: Florian Westphal <fw@strlen.de>, Pablo Neira Ayuso <pablo@netfilter.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260723141855.3217594-1-sashal@kernel.org>
+
+From: Florian Westphal <fw@strlen.de>
+
+[ Upstream commit 5b651783d80b97167ecd27dc6a4408c694873902 ]
+
+An earlier attempt changed this to GFP_KERNEL, but the get helper is
+also called for get requests from userspace, which uses rcu.
+
+Let the caller pass in the kmalloc flags to allow insertions
+to schedule if needed.
+
+Suggested-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Stable-dep-of: 47e65eff5069 ("netfilter: nft_set_pipapo: don't leak bad clone into future transaction")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/netfilter/nft_set_pipapo.c | 17 ++++++++++-------
+ 1 file changed, 10 insertions(+), 7 deletions(-)
+
+--- a/net/netfilter/nft_set_pipapo.c
++++ b/net/netfilter/nft_set_pipapo.c
+@@ -505,6 +505,7 @@ out:
+ * @data: Key data to be matched against existing elements
+ * @genmask: If set, check that element is active in given genmask
+ * @tstamp: timestamp to check for expired elements
++ * @gfp: the type of memory to allocate (see kmalloc).
+ *
+ * This is essentially the same as the lookup function, except that it matches
+ * key data against the uncommitted copy and doesn't use preallocated maps for
+@@ -515,7 +516,7 @@ out:
+ static struct nft_pipapo_elem *pipapo_get(const struct net *net,
+ const struct nft_set *set,
+ const u8 *data, u8 genmask,
+- u64 tstamp)
++ u64 tstamp, gfp_t gfp)
+ {
+ struct nft_pipapo_elem *ret = ERR_PTR(-ENOENT);
+ struct nft_pipapo *priv = nft_set_priv(set);
+@@ -528,13 +529,13 @@ static struct nft_pipapo_elem *pipapo_ge
+ if (m->bsize_max == 0)
+ return ret;
+
+- res_map = kmalloc_array(m->bsize_max, sizeof(*res_map), GFP_ATOMIC);
++ res_map = kmalloc_array(m->bsize_max, sizeof(*res_map), gfp);
+ if (!res_map) {
+ ret = ERR_PTR(-ENOMEM);
+ goto out;
+ }
+
+- fill_map = kcalloc(m->bsize_max, sizeof(*res_map), GFP_ATOMIC);
++ fill_map = kcalloc(m->bsize_max, sizeof(*res_map), gfp);
+ if (!fill_map) {
+ ret = ERR_PTR(-ENOMEM);
+ goto out;
+@@ -609,7 +610,7 @@ static void *nft_pipapo_get(const struct
+ const struct nft_set_elem *elem, unsigned int flags)
+ {
+ return pipapo_get(net, set, (const u8 *)elem->key.val.data,
+- nft_genmask_cur(net), get_jiffies_64());
++ nft_genmask_cur(net), get_jiffies_64(), GFP_ATOMIC);
+ }
+
+
+@@ -1246,7 +1247,7 @@ static int nft_pipapo_insert(const struc
+ else
+ end = start;
+
+- dup = pipapo_get(net, set, start, genmask, tstamp);
++ dup = pipapo_get(net, set, start, genmask, tstamp, GFP_KERNEL);
+ if (!IS_ERR(dup)) {
+ /* Check if we already have the same exact entry */
+ const struct nft_data *dup_key, *dup_end;
+@@ -1268,7 +1269,8 @@ static int nft_pipapo_insert(const struc
+
+ if (PTR_ERR(dup) == -ENOENT) {
+ /* Look for partially overlapping entries */
+- dup = pipapo_get(net, set, end, nft_genmask_next(net), tstamp);
++ dup = pipapo_get(net, set, end, nft_genmask_next(net), tstamp,
++ GFP_KERNEL);
+ }
+
+ if (PTR_ERR(dup) != -ENOENT) {
+@@ -1869,7 +1871,8 @@ static void *pipapo_deactivate(const str
+ {
+ struct nft_pipapo_elem *e;
+
+- e = pipapo_get(net, set, data, nft_genmask_next(net), nft_net_tstamp(net));
++ e = pipapo_get(net, set, data, nft_genmask_next(net),
++ nft_net_tstamp(net), GFP_KERNEL);
+ if (IS_ERR(e))
+ return NULL;
+
--- /dev/null
+From stable+bounces-289593-greg=kroah.com@vger.kernel.org Mon Jul 27 17:49:19 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 11:40:54 -0400
+Subject: NFS: Charge unstable writes by request size, not folio size
+To: stable@vger.kernel.org
+Cc: Benjamin Coddington <ben.coddington@hammerspace.com>, Benjamin Coddington <bcodding@hammerspace.com>, Claude Sonnet 5 <noreply@anthropic.com>, Anna Schumaker <anna.schumaker@hammerspace.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260727154054.1513904-2-sashal@kernel.org>
+
+From: Benjamin Coddington <ben.coddington@hammerspace.com>
+
+[ Upstream commit 27934d02cbeb8a957dd11c985a579e58d30c5270 ]
+
+nfs_folio_mark_unstable() and nfs_folio_clear_commit() charge and
+uncharge NR_WRITEBACK/WB_WRITEBACK by folio_nr_pages(folio) once per
+*request* added to or removed from a commit list. This is correct only
+when a folio has a single associated request. When pg_test splits a
+folio into N sub-folio requests (e.g. pNFS flexfiles striping with a
+stripe unit smaller than the folio size, or plain wsize-limited
+splitting), each of the N requests independently charges the whole
+folio's page count, inflating the accounting by a factor of N per
+folio. With large folios and small stripe units this reaches multiple
+orders of magnitude: a 2 MiB folio split into 512 4 KiB requests can
+charge up to 512x its real size, pushing global dirty+writeback
+accounting past the system's dirty threshold and forcing every
+buffered writer on the host into the hard-throttle path, including
+unrelated in-kernel NFS server threads sharing the box.
+
+Charge each request only for the pages it actually covers.
+
+Fixes: 0c493b5cf16e ("NFS: Convert buffered writes to use folios")
+Cc: stable@vger.kernel.org
+Signed-off-by: Benjamin Coddington <bcodding@hammerspace.com>
+Assisted-By: Claude Sonnet 5 <noreply@anthropic.com>
+Signed-off-by: Anna Schumaker <anna.schumaker@hammerspace.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/nfs/internal.h | 12 +++++++-----
+ fs/nfs/pnfs_nfs.c | 2 +-
+ fs/nfs/write.c | 14 ++++++++------
+ 3 files changed, 16 insertions(+), 12 deletions(-)
+
+--- a/fs/nfs/internal.h
++++ b/fs/nfs/internal.h
+@@ -784,17 +784,19 @@ void nfs_super_set_maxbytes(struct super
+ }
+
+ /*
+- * Record the page as unstable (an extra writeback period) and mark its
+- * inode as dirty.
++ * Record the request's range as unstable (an extra writeback period) and
++ * mark its inode as dirty.
+ */
+-static inline void nfs_folio_mark_unstable(struct folio *folio,
++static inline void nfs_folio_mark_unstable(struct nfs_page *req,
+ struct nfs_commit_info *cinfo)
+ {
++ struct folio *folio = nfs_page_to_folio(req);
++
+ if (folio && !cinfo->dreq) {
+ struct inode *inode = folio->mapping->host;
+- long nr = folio_nr_pages(folio);
++ long nr = DIV_ROUND_UP(req->wb_bytes, PAGE_SIZE);
+
+- /* This page is really still in write-back - just that the
++ /* This range is really still in write-back - just that the
+ * writeback is happening on the server now.
+ */
+ node_stat_mod_folio(folio, NR_WRITEBACK, nr);
+--- a/fs/nfs/pnfs_nfs.c
++++ b/fs/nfs/pnfs_nfs.c
+@@ -1226,7 +1226,7 @@ pnfs_layout_mark_request_commit(struct n
+
+ nfs_request_add_commit_list_locked(req, list, cinfo);
+ mutex_unlock(&NFS_I(cinfo->inode)->commit_mutex);
+- nfs_folio_mark_unstable(nfs_page_to_folio(req), cinfo);
++ nfs_folio_mark_unstable(req, cinfo);
+ return;
+ out_resched:
+ mutex_unlock(&NFS_I(cinfo->inode)->commit_mutex);
+--- a/fs/nfs/write.c
++++ b/fs/nfs/write.c
+@@ -933,7 +933,7 @@ nfs_request_add_commit_list(struct nfs_p
+ mutex_lock(&NFS_I(cinfo->inode)->commit_mutex);
+ nfs_request_add_commit_list_locked(req, &cinfo->mds->list, cinfo);
+ mutex_unlock(&NFS_I(cinfo->inode)->commit_mutex);
+- nfs_folio_mark_unstable(nfs_page_to_folio(req), cinfo);
++ nfs_folio_mark_unstable(req, cinfo);
+ }
+ EXPORT_SYMBOL_GPL(nfs_request_add_commit_list);
+
+@@ -992,10 +992,12 @@ nfs_mark_request_commit(struct nfs_page
+ nfs_request_add_commit_list(req, cinfo);
+ }
+
+-static void nfs_folio_clear_commit(struct folio *folio)
++static void nfs_folio_clear_commit(struct nfs_page *req)
+ {
++ struct folio *folio = nfs_page_to_folio(req);
++
+ if (folio) {
+- long nr = folio_nr_pages(folio);
++ long nr = DIV_ROUND_UP(req->wb_bytes, PAGE_SIZE);
+
+ node_stat_mod_folio(folio, NR_WRITEBACK, -nr);
+ wb_stat_mod(&inode_to_bdi(folio_file_mapping(folio)->host)->wb,
+@@ -1016,7 +1018,7 @@ static void nfs_clear_request_commit(str
+ nfs_request_remove_commit_list(req, cinfo);
+ }
+ mutex_unlock(&NFS_I(inode)->commit_mutex);
+- nfs_folio_clear_commit(nfs_page_to_folio(req));
++ nfs_folio_clear_commit(req);
+ }
+ }
+
+@@ -1834,7 +1836,7 @@ void nfs_retry_commit(struct list_head *
+ req = nfs_list_entry(page_list->next);
+ nfs_list_remove_request(req);
+ nfs_mark_request_commit(req, lseg, cinfo, ds_commit_idx);
+- nfs_folio_clear_commit(nfs_page_to_folio(req));
++ nfs_folio_clear_commit(req);
+ nfs_unlock_and_release_request(req);
+ }
+ }
+@@ -1902,7 +1904,7 @@ static void nfs_commit_release_pages(str
+ req = nfs_list_entry(data->pages.next);
+ nfs_list_remove_request(req);
+ folio = nfs_page_to_folio(req);
+- nfs_folio_clear_commit(folio);
++ nfs_folio_clear_commit(req);
+
+ dprintk("NFS: commit (%s/%llu %d@%lld)",
+ nfs_req_openctx(req)->dentry->d_sb->s_id,
--- /dev/null
+From stable+bounces-289592-greg=kroah.com@vger.kernel.org Mon Jul 27 17:52:01 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 11:40:53 -0400
+Subject: nfs: remove dead code for the old swap over NFS implementation
+To: stable@vger.kernel.org
+Cc: Christoph Hellwig <hch@lst.de>, Sagi Grimberg <sagi@grimberg.me>, Anna Schumaker <Anna.Schumaker@Netapp.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260727154054.1513904-1-sashal@kernel.org>
+
+From: Christoph Hellwig <hch@lst.de>
+
+[ Upstream commit 7e8e78a0ba00c88f0ded86de64bdddc82e06b196 ]
+
+Remove the code testing folio_test_swapcache either explicitly or
+implicitly in pagemap.h headers, as is now handled using the direct I/O
+path and not the buffered I/O path that these helpers are located in.
+
+Signed-off-by: Christoph Hellwig <hch@lst.de>
+Reviewed-by: Sagi Grimberg <sagi@grimberg.me>
+Signed-off-by: Anna Schumaker <Anna.Schumaker@Netapp.com>
+Stable-dep-of: 27934d02cbeb ("NFS: Charge unstable writes by request size, not folio size")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/nfs/internal.h | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/fs/nfs/internal.h
++++ b/fs/nfs/internal.h
+@@ -791,7 +791,7 @@ static inline void nfs_folio_mark_unstab
+ struct nfs_commit_info *cinfo)
+ {
+ if (folio && !cinfo->dreq) {
+- struct inode *inode = folio_file_mapping(folio)->host;
++ struct inode *inode = folio->mapping->host;
+ long nr = folio_nr_pages(folio);
+
+ /* This page is really still in write-back - just that the
--- /dev/null
+From stable+bounces-288611-greg=kroah.com@vger.kernel.org Fri Jul 24 03:28:14 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 23 Jul 2026 21:25:11 -0400
+Subject: NFSD: pass nfsd_file to nfsd_iter_read()
+To: stable@vger.kernel.org
+Cc: Mike Snitzer <snitzer@kernel.org>, Jeff Layton <jlayton@kernel.org>, NeilBrown <neil@brown.name>, Christoph Hellwig <hch@lst.de>, Chuck Lever <chuck.lever@oracle.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260724012515.3833219-1-sashal@kernel.org>
+
+From: Mike Snitzer <snitzer@kernel.org>
+
+[ Upstream commit 803bc849f0039291f546ba0e2237faebeb5c073e ]
+
+Prepare for nfsd_iter_read() to use the DIO alignment stored in
+nfsd_file by passing the nfsd_file to nfsd_iter_read() rather than
+just the file which is associaed with the nfsd_file.
+
+This means nfsd4_encode_readv() now also needs the nfsd_file rather
+than the file. Instead of changing the file arg to be the nfsd_file,
+we discard the file arg as the nfsd_file (and indeed the file) is
+already available via the "read" argument.
+
+Signed-off-by: Mike Snitzer <snitzer@kernel.org>
+Reviewed-by: Jeff Layton <jlayton@kernel.org>
+Reviewed-by: NeilBrown <neil@brown.name>
+Reviewed-by: Christoph Hellwig <hch@lst.de>
+Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
+Stable-dep-of: 18c1cc698861 ("SUNRPC: Return an error from xdr_buf_to_bvec() on overflow")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/nfsd/nfs4xdr.c | 8 ++++----
+ fs/nfsd/vfs.c | 7 ++++---
+ fs/nfsd/vfs.h | 2 +-
+ 3 files changed, 9 insertions(+), 8 deletions(-)
+
+--- a/fs/nfsd/nfs4xdr.c
++++ b/fs/nfsd/nfs4xdr.c
+@@ -4096,7 +4096,7 @@ out_err:
+
+ static __be32 nfsd4_encode_readv(struct nfsd4_compoundres *resp,
+ struct nfsd4_read *read,
+- struct file *file, unsigned long maxcount)
++ unsigned long maxcount)
+ {
+ struct xdr_stream *xdr = resp->xdr;
+ unsigned int base = xdr->buf->page_len & ~PAGE_MASK;
+@@ -4107,7 +4107,7 @@ static __be32 nfsd4_encode_readv(struct
+ if (xdr_reserve_space_vec(xdr, maxcount) < 0)
+ return nfserr_resource;
+
+- nfserr = nfsd_iter_read(resp->rqstp, read->rd_fhp, file,
++ nfserr = nfsd_iter_read(resp->rqstp, read->rd_fhp, read->rd_nf,
+ read->rd_offset, &maxcount, base,
+ &read->rd_eof);
+ read->rd_length = maxcount;
+@@ -4155,7 +4155,7 @@ nfsd4_encode_read(struct nfsd4_compoundr
+ if (file->f_op->splice_read && splice_ok)
+ nfserr = nfsd4_encode_splice_read(resp, read, file, maxcount);
+ else
+- nfserr = nfsd4_encode_readv(resp, read, file, maxcount);
++ nfserr = nfsd4_encode_readv(resp, read, maxcount);
+ if (nfserr) {
+ xdr_truncate_encode(xdr, starting_len);
+ return nfserr;
+@@ -4906,7 +4906,7 @@ nfsd4_encode_read_plus_data(struct nfsd4
+ if (file->f_op->splice_read && splice_ok)
+ nfserr = nfsd4_encode_splice_read(resp, read, file, maxcount);
+ else
+- nfserr = nfsd4_encode_readv(resp, read, file, maxcount);
++ nfserr = nfsd4_encode_readv(resp, read, maxcount);
+ if (nfserr)
+ return nfserr;
+
+--- a/fs/nfsd/vfs.c
++++ b/fs/nfsd/vfs.c
+@@ -1061,7 +1061,7 @@ __be32 nfsd_splice_read(struct svc_rqst
+ * nfsd_iter_read - Perform a VFS read using an iterator
+ * @rqstp: RPC transaction context
+ * @fhp: file handle of file to be read
+- * @file: opened struct file of file to be read
++ * @nf: opened struct nfsd_file of file to be read
+ * @offset: starting byte offset
+ * @count: IN: requested number of bytes; OUT: number of bytes read
+ * @base: offset in first page of read buffer
+@@ -1074,9 +1074,10 @@ __be32 nfsd_splice_read(struct svc_rqst
+ * returned.
+ */
+ __be32 nfsd_iter_read(struct svc_rqst *rqstp, struct svc_fh *fhp,
+- struct file *file, loff_t offset, unsigned long *count,
++ struct nfsd_file *nf, loff_t offset, unsigned long *count,
+ unsigned int base, u32 *eof)
+ {
++ struct file *file = nf->nf_file;
+ unsigned long v, total;
+ struct iov_iter iter;
+ loff_t ppos = offset;
+@@ -1258,7 +1259,7 @@ __be32 nfsd_read(struct svc_rqst *rqstp,
+ if (file->f_op->splice_read && test_bit(RQ_SPLICE_OK, &rqstp->rq_flags))
+ err = nfsd_splice_read(rqstp, fhp, file, offset, count, eof);
+ else
+- err = nfsd_iter_read(rqstp, fhp, file, offset, count, 0, eof);
++ err = nfsd_iter_read(rqstp, fhp, nf, offset, count, 0, eof);
+
+ nfsd_file_put(nf);
+ trace_nfsd_read_done(rqstp, fhp, offset, *count);
+--- a/fs/nfsd/vfs.h
++++ b/fs/nfsd/vfs.h
+@@ -120,7 +120,7 @@ __be32 nfsd_splice_read(struct svc_rqst
+ unsigned long *count,
+ u32 *eof);
+ __be32 nfsd_iter_read(struct svc_rqst *rqstp, struct svc_fh *fhp,
+- struct file *file, loff_t offset,
++ struct nfsd_file *nf, loff_t offset,
+ unsigned long *count, unsigned int base,
+ u32 *eof);
+ __be32 nfsd_read(struct svc_rqst *rqstp, struct svc_fh *fhp,
--- /dev/null
+From stable+bounces-290076-greg=kroah.com@vger.kernel.org Tue Jul 28 19:24:15 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Jul 2026 12:10:46 -0400
+Subject: octeontx2-af: cn10k: restrict VF LMTLINE sharing to its own PF
+To: stable@vger.kernel.org
+Cc: Junrui Luo <moonafterrain@outlook.com>, Yuhao Jiang <danisjiang@gmail.com>, Jakub Kicinski <kuba@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260728161046.36627-1-sashal@kernel.org>
+
+From: Junrui Luo <moonafterrain@outlook.com>
+
+[ Upstream commit 8cdcf3d2caacdee7ddd363705fb4d93b0c1a0915 ]
+
+rvu_mbox_handler_lmtst_tbl_setup() uses req->base_pcifunc as a direct
+index into the LMT map table to read another function's LMTLINE
+physical base address and copy it into the caller's own LMT map table
+entry. The mailbox dispatcher authenticates req->hdr.pcifunc from the
+IRQ source, but req->base_pcifunc is a separate payload field and is
+not sanitized.
+
+Reject the request with -EPERM when a VF caller's base_pcifunc is not a
+valid function under its own PF. is_pf_func_valid() bounds the FUNC field
+to the PF's configured VF count, keeping the computed index inside the
+caller's own slot block.
+
+Fixes: 893ae97214c3 ("octeontx2-af: cn10k: Support configurable LMTST regions")
+Reported-by: Yuhao Jiang <danisjiang@gmail.com>
+Cc: stable@vger.kernel.org
+Signed-off-by: Junrui Luo <moonafterrain@outlook.com>
+Link: https://patch.msgid.link/SYBPR01MB78811656934E713B77DA6CEDAFE62@SYBPR01MB7881.ausprd01.prod.outlook.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+[ Dropped `rvu->pdev` argument from `rvu_get_pf()` calls and folded in the `is_pf_func_valid()` de-static plus its `rvu.h` declaration from commit 2156a29aecff. ]
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/net/ethernet/marvell/octeontx2/af/rvu.c | 2 +-
+ drivers/net/ethernet/marvell/octeontx2/af/rvu.h | 1 +
+ drivers/net/ethernet/marvell/octeontx2/af/rvu_cn10k.c | 9 +++++++++
+ 3 files changed, 11 insertions(+), 1 deletion(-)
+
+--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu.c
++++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu.c
+@@ -440,7 +440,7 @@ struct rvu_pfvf *rvu_get_pfvf(struct rvu
+ return &rvu->pf[rvu_get_pf(pcifunc)];
+ }
+
+-static bool is_pf_func_valid(struct rvu *rvu, u16 pcifunc)
++bool is_pf_func_valid(struct rvu *rvu, u16 pcifunc)
+ {
+ int pf, vf, nvfs;
+ u64 cfg;
+--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu.h
++++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu.h
+@@ -751,6 +751,7 @@ int rvu_get_pf(u16 pcifunc);
+ struct rvu_pfvf *rvu_get_pfvf(struct rvu *rvu, int pcifunc);
+ void rvu_get_pf_numvfs(struct rvu *rvu, int pf, int *numvfs, int *hwvf);
+ bool is_block_implemented(struct rvu_hwinfo *hw, int blkaddr);
++bool is_pf_func_valid(struct rvu *rvu, u16 pcifunc);
+ bool is_pffunc_map_valid(struct rvu *rvu, u16 pcifunc, int blktype);
+ int rvu_get_lf(struct rvu *rvu, struct rvu_block *block, u16 pcifunc, u16 slot);
+ int rvu_lf_reset(struct rvu *rvu, struct rvu_block *block, int lf);
+--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_cn10k.c
++++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_cn10k.c
+@@ -178,6 +178,15 @@ int rvu_mbox_handler_lmtst_tbl_setup(str
+ * pcifunc (will be the one who is calling this mailbox).
+ */
+ if (req->base_pcifunc) {
++ /* A VF is untrusted and must not redirect its LMTLINE to
++ * another PF's region, so confine VF callers to their own PF.
++ */
++ if (is_vf(req->hdr.pcifunc) &&
++ (!is_pf_func_valid(rvu, req->base_pcifunc) ||
++ rvu_get_pf(req->hdr.pcifunc) !=
++ rvu_get_pf(req->base_pcifunc)))
++ return -EPERM;
++
+ /* Calculating the LMT table index equivalent to primary
+ * pcifunc.
+ */
--- /dev/null
+From stable+bounces-288093-greg=kroah.com@vger.kernel.org Thu Jul 23 01:26:48 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 19:21:18 -0400
+Subject: octeontx2: Annotate mmio regions as __iomem
+To: stable@vger.kernel.org
+Cc: Subbaraya Sundeep <sbhatta@marvell.com>, Jakub Kicinski <kuba@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260722232119.2551122-1-sashal@kernel.org>
+
+From: Subbaraya Sundeep <sbhatta@marvell.com>
+
+[ Upstream commit d0976b43956ee8c8bd093223df9115bfcf63dfe5 ]
+
+This patch removes unnecessary typecasts by marking the
+mbox_regions array as __iomem since it is used to store
+pointers to memory-mapped I/O (MMIO) regions. Also simplified
+the call to readq() in PF driver by removing redundant type casts.
+
+Signed-off-by: Subbaraya Sundeep <sbhatta@marvell.com>
+Link: https://patch.msgid.link/1749484309-3434-1-git-send-email-sbhatta@marvell.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Stable-dep-of: f918554fb724 ("octeontx2-pf: clear stale mailbox IRQ state before request_irq()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/net/ethernet/marvell/octeontx2/af/rvu.c | 12 ++++++------
+ drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c | 3 +--
+ 2 files changed, 7 insertions(+), 8 deletions(-)
+
+--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu.c
++++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu.c
+@@ -2330,7 +2330,7 @@ static inline void rvu_afvf_mbox_up_hand
+ __rvu_mbox_up_handler(mwork, TYPE_AFVF);
+ }
+
+-static int rvu_get_mbox_regions(struct rvu *rvu, void **mbox_addr,
++static int rvu_get_mbox_regions(struct rvu *rvu, void __iomem **mbox_addr,
+ int num, int type, unsigned long *pf_bmap)
+ {
+ struct rvu_hwinfo *hw = rvu->hw;
+@@ -2355,7 +2355,7 @@ static int rvu_get_mbox_regions(struct r
+ bar4 = rvupf_read64(rvu, RVU_PF_VF_BAR4_ADDR);
+ bar4 += region * MBOX_SIZE;
+ }
+- mbox_addr[region] = (void *)ioremap_wc(bar4, MBOX_SIZE);
++ mbox_addr[region] = ioremap_wc(bar4, MBOX_SIZE);
+ if (!mbox_addr[region])
+ goto error;
+ }
+@@ -2378,7 +2378,7 @@ static int rvu_get_mbox_regions(struct r
+ RVU_AF_PF_BAR4_ADDR);
+ bar4 += region * MBOX_SIZE;
+ }
+- mbox_addr[region] = (void *)ioremap_wc(bar4, MBOX_SIZE);
++ mbox_addr[region] = ioremap_wc(bar4, MBOX_SIZE);
+ if (!mbox_addr[region])
+ goto error;
+ }
+@@ -2386,7 +2386,7 @@ static int rvu_get_mbox_regions(struct r
+
+ error:
+ while (region--)
+- iounmap((void __iomem *)mbox_addr[region]);
++ iounmap(mbox_addr[region]);
+ return -ENOMEM;
+ }
+
+@@ -2396,10 +2396,10 @@ static int rvu_mbox_init(struct rvu *rvu
+ void (mbox_up_handler)(struct work_struct *))
+ {
+ int err = -EINVAL, i, dir, dir_up;
++ void __iomem **mbox_regions;
+ void __iomem *reg_base;
+ struct rvu_work *mwork;
+ unsigned long *pf_bmap;
+- void **mbox_regions;
+ const char *name;
+ u64 cfg;
+
+@@ -2422,7 +2422,7 @@ static int rvu_mbox_init(struct rvu *rvu
+
+ mutex_init(&rvu->mbox_lock);
+
+- mbox_regions = kcalloc(num, sizeof(void *), GFP_KERNEL);
++ mbox_regions = kcalloc(num, sizeof(void __iomem *), GFP_KERNEL);
+ if (!mbox_regions) {
+ err = -ENOMEM;
+ goto free_bitmap;
+--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c
++++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c
+@@ -597,8 +597,7 @@ static int otx2_pfvf_mbox_init(struct ot
+ base = pci_resource_start(pf->pdev, PCI_MBOX_BAR_NUM) +
+ MBOX_SIZE;
+ else
+- base = readq((void __iomem *)((u64)pf->reg_base +
+- RVU_PF_VF_BAR4_ADDR));
++ base = readq(pf->reg_base + RVU_PF_VF_BAR4_ADDR);
+
+ hwbase = ioremap_wc(base, MBOX_SIZE * pf->total_vfs);
+ if (!hwbase) {
--- /dev/null
+From stable+bounces-288094-greg=kroah.com@vger.kernel.org Thu Jul 23 01:21:39 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 19:21:19 -0400
+Subject: octeontx2-pf: clear stale mailbox IRQ state before request_irq()
+To: stable@vger.kernel.org
+Cc: Runyu Xiao <runyu.xiao@seu.edu.cn>, Simon Horman <horms@kernel.org>, Ratheesh Kannoth <rkannoth@marvell.com>, Jakub Kicinski <kuba@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260722232119.2551122-2-sashal@kernel.org>
+
+From: Runyu Xiao <runyu.xiao@seu.edu.cn>
+
+[ Upstream commit f918554fb7246e89b98ef90abe80801f038258b3 ]
+
+otx2_register_mbox_intr() currently installs the PF mailbox IRQ handler
+before clearing stale mailbox interrupt state. The function itself then
+comments that the local interrupt bits must be cleared first to avoid
+spurious interrupts, but that clear happens only after request_irq() has
+already exposed the handler to irq delivery.
+
+A running system can reach this during PF mailbox interrupt registration
+while stale or latched RVU_PF_INT state is still present. If delivery
+happens in the request_irq()-to-clear window,
+otx2_pfaf_mbox_intr_handler() can run before local quiesce and touch
+the same pf->mbox and pf->mbox_wq carrier that probe and teardown later
+reuse or destroy.
+
+Move the stale mailbox interrupt clear ahead of request_irq(), but keep
+interrupt enabling after the handler is installed. This closes the
+pre-clear early-IRQ window without creating a new enable-before-handler
+window.
+
+Fixes: 5a6d7c9daef3 ("octeontx2-pf: Mailbox communication with AF")
+Cc: stable@vger.kernel.org
+Signed-off-by: Runyu Xiao <runyu.xiao@seu.edu.cn>
+Reviewed-by: Simon Horman <horms@kernel.org>
+Reviewed-by: Ratheesh Kannoth <rkannoth@marvell.com>
+Link: https://patch.msgid.link/20260611160014.3202224-2-runyu.xiao@seu.edu.cn
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c
++++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c
+@@ -1027,6 +1027,9 @@ static int otx2_register_mbox_intr(struc
+ char *irq_name;
+ int err;
+
++ /* Clear stale mailbox interrupt state before installing the handler. */
++ otx2_write64(pf, RVU_PF_INT, BIT_ULL(0));
++
+ /* Register mailbox interrupt handler */
+ irq_name = &hw->irq_name[RVU_PF_INT_VEC_AFPF_MBOX * NAME_SIZE];
+ snprintf(irq_name, NAME_SIZE, "RVUPFAF Mbox");
+@@ -1038,10 +1041,7 @@ static int otx2_register_mbox_intr(struc
+ return err;
+ }
+
+- /* Enable mailbox interrupt for msgs coming from AF.
+- * First clear to avoid spurious interrupts, if any.
+- */
+- otx2_write64(pf, RVU_PF_INT, BIT_ULL(0));
++ /* Enable mailbox interrupt for msgs coming from AF. */
+ otx2_write64(pf, RVU_PF_INT_ENA_W1S, BIT_ULL(0));
+
+ if (!probe_af)
--- /dev/null
+From stable+bounces-290231-greg=kroah.com@vger.kernel.org Wed Jul 29 04:39:02 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Jul 2026 22:30:28 -0400
+Subject: octeontx2-pf: fix SQB pointer leak on init failure
+To: stable@vger.kernel.org
+Cc: Dawei Feng <dawei.feng@seu.edu.cn>, Ratheesh Kannoth <rkannoth@marvell.com>, Paolo Abeni <pabeni@redhat.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260729023028.1114866-1-sashal@kernel.org>
+
+From: Dawei Feng <dawei.feng@seu.edu.cn>
+
+[ Upstream commit 62e7df6d042aeebd5efb581074e28865c04477be ]
+
+otx2_init_hw_resources() initializes SQ aura and pool resources before
+several later setup steps. On failure, err_free_sq_ptrs only frees SQB
+pages, leaving the per-SQ sqb_ptrs arrays behind.
+
+Use otx2_free_sq_res() for the SQ unwind path and let it free sqb_ptrs
+even when sq->sqe has not been allocated yet.
+
+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
+OcteonTX2 PF device and the corresponding AF mailbox setup to test with,
+no runtime testing was able to be performed.
+
+Fixes: caa2da34fd25 ("octeontx2-pf: Initialize and config queues")
+Cc: stable@vger.kernel.org
+Reviewed-by: Ratheesh Kannoth <rkannoth@marvell.com>
+Signed-off-by: Dawei Feng <dawei.feng@seu.edu.cn>
+Link: https://patch.msgid.link/20260630071625.349996-1-dawei.feng@seu.edu.cn
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c | 17 ++++++++---------
+ 1 file changed, 8 insertions(+), 9 deletions(-)
+
+--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c
++++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c
+@@ -1442,13 +1442,13 @@ static void otx2_free_sq_res(struct otx2
+ otx2_sq_free_sqbs(pf);
+ for (qidx = 0; qidx < otx2_get_total_tx_queues(pf); qidx++) {
+ sq = &qset->sq[qidx];
+- /* Skip freeing Qos queues if they are not initialized */
+- if (!sq->sqe)
+- continue;
+- qmem_free(pf->dev, sq->sqe);
+- qmem_free(pf->dev, sq->tso_hdrs);
+- qmem_free(pf->dev, sq->timestamps);
+- kfree(sq->sg);
++ /* sq->sqe is not initialized for unused QoS queues */
++ if (sq->sqe) {
++ qmem_free(pf->dev, sq->sqe);
++ qmem_free(pf->dev, sq->tso_hdrs);
++ qmem_free(pf->dev, sq->timestamps);
++ kfree(sq->sg);
++ }
+ kfree(sq->sqb_ptrs);
+ }
+ }
+@@ -1574,13 +1574,12 @@ static int otx2_init_hw_resources(struct
+ return err;
+
+ err_free_nix_queues:
+- otx2_free_sq_res(pf);
+ otx2_free_cq_res(pf);
+ otx2_ctx_disable(mbox, NIX_AQ_CTYPE_RQ, false);
+ err_free_txsch:
+ otx2_txschq_stop(pf);
+ err_free_sq_ptrs:
+- otx2_sq_free_sqbs(pf);
++ otx2_free_sq_res(pf);
+ err_free_rq_ptrs:
+ otx2_free_aura_ptr(pf, AURA_NIX_RQ);
+ otx2_ctx_disable(mbox, NPA_AQ_CTYPE_POOL, true);
--- /dev/null
+From stable+bounces-288096-greg=kroah.com@vger.kernel.org Thu Jul 23 01:27:11 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 19:21:22 -0400
+Subject: octeontx2-vf: clear stale mailbox IRQ state before request_irq()
+To: stable@vger.kernel.org
+Cc: Runyu Xiao <runyu.xiao@seu.edu.cn>, Simon Horman <horms@kernel.org>, Ratheesh Kannoth <rkannoth@marvell.com>, Jakub Kicinski <kuba@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260722232122.2551218-2-sashal@kernel.org>
+
+From: Runyu Xiao <runyu.xiao@seu.edu.cn>
+
+[ Upstream commit 0b352f04b9be2c83c0240aa6dae7257fefa90464 ]
+
+otx2vf_register_mbox_intr() currently installs the VF mailbox IRQ
+handler before clearing stale mailbox interrupt state. The code then says
+that local interrupt bits should be cleared first to avoid spurious
+interrupts, but that clear still happens only after request_irq() has
+already made the handler reachable.
+
+A running system can reach this during VF mailbox interrupt registration
+while stale or latched RVU_VF_INT state is still present. If delivery
+happens in the request_irq()-to-clear window,
+otx2vf_vfaf_mbox_intr_handler() can run before local quiesce and touch
+the same vf->mbox and vf->mbox_wq carrier that probe and teardown later
+reuse or destroy.
+
+Move the stale mailbox interrupt clear ahead of request_irq(), but keep
+interrupt enabling after the handler is installed. This closes the
+pre-clear early-IRQ window without creating a new enable-before-handler
+window.
+
+Fixes: 3184fb5ba96e ("octeontx2-vf: Virtual function driver support")
+Cc: stable@vger.kernel.org
+Signed-off-by: Runyu Xiao <runyu.xiao@seu.edu.cn>
+Reviewed-by: Simon Horman <horms@kernel.org>
+Reviewed-by: Ratheesh Kannoth <rkannoth@marvell.com>
+Link: https://patch.msgid.link/20260611160014.3202224-3-runyu.xiao@seu.edu.cn
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c | 13 ++++++++-----
+ 1 file changed, 8 insertions(+), 5 deletions(-)
+
+--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c
++++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c
+@@ -245,9 +245,15 @@ static int otx2vf_register_mbox_intr(str
+ {
+ struct otx2_hw *hw = &vf->hw;
+ struct msg_req *req;
++ u64 mbox_int_mask;
+ char *irq_name;
+ int err;
+
++ mbox_int_mask = BIT_ULL(0);
++
++ /* Clear stale mailbox interrupt state before installing the handler. */
++ otx2_write64(vf, RVU_VF_INT, mbox_int_mask);
++
+ /* Register mailbox interrupt handler */
+ irq_name = &hw->irq_name[RVU_VF_INT_VEC_MBOX * NAME_SIZE];
+ snprintf(irq_name, NAME_SIZE, "RVUVFAF Mbox");
+@@ -259,11 +265,8 @@ static int otx2vf_register_mbox_intr(str
+ return err;
+ }
+
+- /* Enable mailbox interrupt for msgs coming from PF.
+- * First clear to avoid spurious interrupts, if any.
+- */
+- otx2_write64(vf, RVU_VF_INT, BIT_ULL(0));
+- otx2_write64(vf, RVU_VF_INT_ENA_W1S, BIT_ULL(0));
++ /* Enable mailbox interrupt for msgs coming from PF. */
++ otx2_write64(vf, RVU_VF_INT_ENA_W1S, mbox_int_mask);
+
+ if (!probe_pf)
+ return 0;
--- /dev/null
+From stable+bounces-289602-greg=kroah.com@vger.kernel.org Mon Jul 27 17:52:15 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 11:41:17 -0400
+Subject: ovl: use linked upper dentry in copy-up tmpfile
+To: stable@vger.kernel.org
+Cc: Souvik Banerjee <souvik@amlalabs.com>, Amir Goldstein <amir73il@gmail.com>, Miklos Szeredi <mszeredi@redhat.com>, "Christian Brauner (Amutable)" <brauner@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260727154117.1516979-1-sashal@kernel.org>
+
+From: Souvik Banerjee <souvik@amlalabs.com>
+
+[ Upstream commit e348eecd4d8fa8d18a5157ff59f7be1dc59c5928 ]
+
+ovl_copy_up_tmpfile() stores the disconnected O_TMPFILE dentry as the
+overlay's upper dentry reference via ovl_inode_update(). vfs_tmpfile()
+allocated this dentry via d_alloc(parentpath->dentry, &slash_name), so
+d_name is "/" and d_parent is c->workdir. Local upper filesystems
+(ext4, btrfs, xfs, ...) immediately rename it to "#<inum>" via
+d_mark_tmpfile() inside their ->tmpfile() op; FUSE and virtiofs do
+not, so both fields stay that way. Neither identifies the destination
+directory and filename where ovl_do_link() actually linked the file.
+
+When the upper filesystem implements ->d_revalidate() (e.g. FUSE or
+virtiofs), ovl_revalidate_real() calls it with the dentry's parent
+inode and a snapshot of d_name. The server tries to look up "/" inside
+c->workdir, fails, and overlayfs reports -ESTALE.
+
+This causes persistent ESTALE errors for any file that was copied up via
+the tmpfile path, breaking dpkg, apt, and other tools that do
+rename-over-existing on overlayfs with a FUSE/virtiofs upper.
+
+Before commit 6b52243f633e ("ovl: fold copy-up helpers into callers"),
+the tmpfile copy-up path used a dedicated helper ovl_link_tmpfile()
+that captured the linked destination dentry returned by ovl_do_link():
+
+ err = ovl_do_link(temp, udir, upper);
+ ...
+ if (!err)
+ *newdentry = dget(upper);
+
+and published it via ovl_inode_update(d_inode(c->dentry), newdentry).
+The fold inlined ovl_do_link() into ovl_copy_up_tmpfile() but dropped
+the dget(upper) capture, and rewrote the publish line as
+ovl_inode_update(d_inode(c->dentry), dget(temp)) — where temp is the
+disconnected O_TMPFILE dentry.
+
+Fix by keeping a reference to the linked destination dentry after
+ovl_do_link() succeeds, and publishing that dentry at the existing
+ovl_inode_update() call site. The non-tmpfile/workdir path continues to
+publish the renamed temporary dentry.
+
+Reproducer:
+ - Mount overlayfs with virtiofs (or a FUSE fs whose server advertises
+ FUSE_TMPFILE) as upper
+ - Run: dpkg -i <any .deb>
+ - Observe: "error installing new file '...': Stale file handle"
+
+Fixes: 6b52243f633e ("ovl: fold copy-up helpers into callers")
+Cc: stable@vger.kernel.org # v4.20+
+Signed-off-by: Souvik Banerjee <souvik@amlalabs.com>
+Link: https://patch.msgid.link/20260501232735.2610824-1-souvik@amlalabs.com
+Reviewed-by: Amir Goldstein <amir73il@gmail.com>
+Reviewed-by: Miklos Szeredi <mszeredi@redhat.com>
+Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
+[ adapted scoped credential and creation helpers to explicit credential, locking, lookup, and cleanup handling ]
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/overlayfs/copy_up.c | 12 ++++++++++--
+ 1 file changed, 10 insertions(+), 2 deletions(-)
+
+--- a/fs/overlayfs/copy_up.c
++++ b/fs/overlayfs/copy_up.c
+@@ -793,7 +793,7 @@ static int ovl_copy_up_tmpfile(struct ov
+ {
+ struct ovl_fs *ofs = OVL_FS(c->dentry->d_sb);
+ struct inode *udir = d_inode(c->destdir);
+- struct dentry *temp, *upper;
++ struct dentry *temp, *upper, *newdentry = NULL;
+ struct file *tmpfile;
+ struct ovl_cu_creds cc;
+ int err;
+@@ -826,6 +826,14 @@ static int ovl_copy_up_tmpfile(struct ov
+ err = PTR_ERR(upper);
+ if (!IS_ERR(upper)) {
+ err = ovl_do_link(ofs, temp, udir, upper);
++ if (!err) {
++ /*
++ * Record the linked dentry -- not the disconnected
++ * O_TMPFILE dentry -- so that ->d_revalidate() on
++ * the upper fs sees the real parent/name.
++ */
++ newdentry = dget(upper);
++ }
+ dput(upper);
+ }
+ inode_unlock(udir);
+@@ -841,7 +849,7 @@ static int ovl_copy_up_tmpfile(struct ov
+
+ if (!c->metacopy)
+ ovl_set_upperdata(d_inode(c->dentry));
+- ovl_inode_update(d_inode(c->dentry), dget(temp));
++ ovl_inode_update(d_inode(c->dentry), newdentry);
+
+ out_fput:
+ fput(tmpfile);
--- /dev/null
+From stable+bounces-289948-greg=kroah.com@vger.kernel.org Tue Jul 28 14:18:24 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Jul 2026 08:11:24 -0400
+Subject: pinctrl: remove pinctrl_gpio_direction_output()
+To: stable@vger.kernel.org
+Cc: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>, Linus Walleij <linus.walleij@linaro.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260728121125.3703669-1-sashal@kernel.org>
+
+From: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
+
+[ Upstream commit 45d2055b0067739253883dc541f37c86aad45c92 ]
+
+There are no more users of pinctrl_gpio_direction_output() so remove it.
+
+Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
+Acked-by: Linus Walleij <linus.walleij@linaro.org>
+Stable-dep-of: d3e91a95b2b0 ("gpio: tegra: do not call pinctrl for GPIO direction")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/gpio/gpio-mvebu.c | 4 ++--
+ drivers/gpio/gpio-pxa.c | 4 ++--
+ drivers/gpio/gpio-tegra.c | 4 ++--
+ drivers/gpio/gpio-vf610.c | 4 ++--
+ drivers/pinctrl/cirrus/pinctrl-cs42l43.c | 4 ++--
+ drivers/pinctrl/cirrus/pinctrl-lochnagar.c | 2 +-
+ drivers/pinctrl/core.c | 14 ++++++++------
+ drivers/pinctrl/intel/pinctrl-cherryview.c | 4 ++--
+ drivers/pinctrl/intel/pinctrl-intel.c | 4 ++--
+ drivers/pinctrl/intel/pinctrl-lynxpoint.c | 4 ++--
+ drivers/pinctrl/mediatek/pinctrl-moore.c | 4 ++--
+ drivers/pinctrl/mediatek/pinctrl-mtk-common.c | 4 ++--
+ drivers/pinctrl/mediatek/pinctrl-paris.c | 4 ++--
+ drivers/pinctrl/nuvoton/pinctrl-npcm7xx.c | 4 ++--
+ drivers/pinctrl/pinctrl-as3722.c | 4 ++--
+ drivers/pinctrl/pinctrl-axp209.c | 2 +-
+ drivers/pinctrl/pinctrl-cy8c95x0.c | 4 ++--
+ drivers/pinctrl/pinctrl-ingenic.c | 11 ++++++++---
+ drivers/pinctrl/pinctrl-ocelot.c | 4 ++--
+ drivers/pinctrl/pinctrl-rk805.c | 4 ++--
+ drivers/pinctrl/pinctrl-st.c | 4 ++--
+ drivers/pinctrl/renesas/gpio.c | 4 ++--
+ drivers/pinctrl/stm32/pinctrl-stm32.c | 4 ++--
+ drivers/pinctrl/vt8500/pinctrl-wmt.c | 4 ++--
+ include/linux/pinctrl/consumer.h | 13 +++++++++----
+ 25 files changed, 67 insertions(+), 55 deletions(-)
+
+--- a/drivers/gpio/gpio-mvebu.c
++++ b/drivers/gpio/gpio-mvebu.c
+@@ -345,7 +345,7 @@ static int mvebu_gpio_direction_input(st
+ * Check with the pinctrl driver whether this pin is usable as
+ * an input GPIO
+ */
+- ret = pinctrl_gpio_direction_input(chip->base + pin);
++ ret = pinctrl_gpio_direction_input(chip, pin);
+ if (ret)
+ return ret;
+
+@@ -365,7 +365,7 @@ static int mvebu_gpio_direction_output(s
+ * Check with the pinctrl driver whether this pin is usable as
+ * an output GPIO
+ */
+- ret = pinctrl_gpio_direction_output(chip->base + pin);
++ ret = pinctrl_gpio_direction_output(chip, pin);
+ if (ret)
+ return ret;
+
+--- a/drivers/gpio/gpio-pxa.c
++++ b/drivers/gpio/gpio-pxa.c
+@@ -260,7 +260,7 @@ static int pxa_gpio_direction_input(stru
+ int ret;
+
+ if (pxa_gpio_has_pinctrl()) {
+- ret = pinctrl_gpio_direction_input(chip->base + offset);
++ ret = pinctrl_gpio_direction_input(chip, offset);
+ if (ret)
+ return ret;
+ }
+@@ -289,7 +289,7 @@ static int pxa_gpio_direction_output(str
+ writel_relaxed(mask, base + (value ? GPSR_OFFSET : GPCR_OFFSET));
+
+ if (pxa_gpio_has_pinctrl()) {
+- ret = pinctrl_gpio_direction_output(chip->base + offset);
++ ret = pinctrl_gpio_direction_output(chip, offset);
+ if (ret)
+ return ret;
+ }
+--- a/drivers/gpio/gpio-tegra.c
++++ b/drivers/gpio/gpio-tegra.c
+@@ -179,7 +179,7 @@ static int tegra_gpio_direction_input(st
+ tegra_gpio_mask_write(tgi, GPIO_MSK_OE(tgi, offset), offset, 0);
+ tegra_gpio_enable(tgi, offset);
+
+- ret = pinctrl_gpio_direction_input(chip->base + offset);
++ ret = pinctrl_gpio_direction_input(chip, offset);
+ if (ret < 0)
+ dev_err(tgi->dev,
+ "Failed to set pinctrl input direction of GPIO %d: %d",
+@@ -199,7 +199,7 @@ static int tegra_gpio_direction_output(s
+ tegra_gpio_mask_write(tgi, GPIO_MSK_OE(tgi, offset), offset, 1);
+ tegra_gpio_enable(tgi, offset);
+
+- ret = pinctrl_gpio_direction_output(chip->base + offset);
++ ret = pinctrl_gpio_direction_output(chip, offset);
+ if (ret < 0)
+ dev_err(tgi->dev,
+ "Failed to set pinctrl output direction of GPIO %d: %d",
+--- a/drivers/gpio/gpio-vf610.c
++++ b/drivers/gpio/gpio-vf610.c
+@@ -116,7 +116,7 @@ static int vf610_gpio_direction_input(st
+ vf610_gpio_writel(val, port->gpio_base + GPIO_PDDR);
+ }
+
+- return pinctrl_gpio_direction_input(chip->base + gpio);
++ return pinctrl_gpio_direction_input(chip, gpio);
+ }
+
+ static int vf610_gpio_direction_output(struct gpio_chip *chip, unsigned gpio,
+@@ -134,7 +134,7 @@ static int vf610_gpio_direction_output(s
+ vf610_gpio_writel(val, port->gpio_base + GPIO_PDDR);
+ }
+
+- return pinctrl_gpio_direction_output(chip->base + gpio);
++ return pinctrl_gpio_direction_output(chip, gpio);
+ }
+
+ static void vf610_gpio_irq_handler(struct irq_desc *desc)
+--- a/drivers/pinctrl/cirrus/pinctrl-cs42l43.c
++++ b/drivers/pinctrl/cirrus/pinctrl-cs42l43.c
+@@ -508,7 +508,7 @@ static void cs42l43_gpio_set(struct gpio
+
+ static int cs42l43_gpio_direction_in(struct gpio_chip *chip, unsigned int offset)
+ {
+- return pinctrl_gpio_direction_input(chip->base + offset);
++ return pinctrl_gpio_direction_input(chip, offset);
+ }
+
+ static int cs42l43_gpio_direction_out(struct gpio_chip *chip,
+@@ -516,7 +516,7 @@ static int cs42l43_gpio_direction_out(st
+ {
+ cs42l43_gpio_set(chip, offset, value);
+
+- return pinctrl_gpio_direction_output(chip->base + offset);
++ return pinctrl_gpio_direction_output(chip, offset);
+ }
+
+ static int cs42l43_gpio_add_pin_ranges(struct gpio_chip *chip)
+--- a/drivers/pinctrl/cirrus/pinctrl-lochnagar.c
++++ b/drivers/pinctrl/cirrus/pinctrl-lochnagar.c
+@@ -1098,7 +1098,7 @@ static int lochnagar_gpio_direction_out(
+ {
+ lochnagar_gpio_set(chip, offset, value);
+
+- return pinctrl_gpio_direction_output(chip->base + offset);
++ return pinctrl_gpio_direction_output(chip, offset);
+ }
+
+ static int lochnagar_fill_func_groups(struct lochnagar_pin_priv *priv)
+--- a/drivers/pinctrl/core.c
++++ b/drivers/pinctrl/core.c
+@@ -874,29 +874,31 @@ static int pinctrl_gpio_direction(unsign
+
+ /**
+ * pinctrl_gpio_direction_input() - request a GPIO pin to go into input mode
+- * @gpio: the GPIO pin number from the GPIO subsystem number space
++ * @gc: GPIO chip structure from the GPIO subsystem
++ * @offset: hardware offset of the GPIO relative to the controller
+ *
+ * This function should *ONLY* be used from gpiolib-based GPIO drivers,
+ * as part of their gpio_direction_input() semantics, platforms and individual
+ * drivers shall *NOT* touch pin control GPIO calls.
+ */
+-int pinctrl_gpio_direction_input(unsigned gpio)
++int pinctrl_gpio_direction_input(struct gpio_chip *gc, unsigned int offset)
+ {
+- return pinctrl_gpio_direction(gpio, true);
++ return pinctrl_gpio_direction(gc->base + offset, true);
+ }
+ EXPORT_SYMBOL_GPL(pinctrl_gpio_direction_input);
+
+ /**
+ * pinctrl_gpio_direction_output() - request a GPIO pin to go into output mode
+- * @gpio: the GPIO pin number from the GPIO subsystem number space
++ * @gc: GPIO chip structure from the GPIO subsystem
++ * @offset: hardware offset of the GPIO relative to the controller
+ *
+ * This function should *ONLY* be used from gpiolib-based GPIO drivers,
+ * as part of their gpio_direction_output() semantics, platforms and individual
+ * drivers shall *NOT* touch pin control GPIO calls.
+ */
+-int pinctrl_gpio_direction_output(unsigned gpio)
++int pinctrl_gpio_direction_output(struct gpio_chip *gc, unsigned int offset)
+ {
+- return pinctrl_gpio_direction(gpio, false);
++ return pinctrl_gpio_direction(gc->base + offset, false);
+ }
+ EXPORT_SYMBOL_GPL(pinctrl_gpio_direction_output);
+
+--- a/drivers/pinctrl/intel/pinctrl-cherryview.c
++++ b/drivers/pinctrl/intel/pinctrl-cherryview.c
+@@ -1172,14 +1172,14 @@ static int chv_gpio_get_direction(struct
+
+ static int chv_gpio_direction_input(struct gpio_chip *chip, unsigned int offset)
+ {
+- return pinctrl_gpio_direction_input(chip->base + offset);
++ return pinctrl_gpio_direction_input(chip, offset);
+ }
+
+ static int chv_gpio_direction_output(struct gpio_chip *chip, unsigned int offset,
+ int value)
+ {
+ chv_gpio_set(chip, offset, value);
+- return pinctrl_gpio_direction_output(chip->base + offset);
++ return pinctrl_gpio_direction_output(chip, offset);
+ }
+
+ static const struct gpio_chip chv_gpio_chip = {
+--- a/drivers/pinctrl/intel/pinctrl-intel.c
++++ b/drivers/pinctrl/intel/pinctrl-intel.c
+@@ -1026,14 +1026,14 @@ static int intel_gpio_get_direction(stru
+
+ static int intel_gpio_direction_input(struct gpio_chip *chip, unsigned int offset)
+ {
+- return pinctrl_gpio_direction_input(chip->base + offset);
++ return pinctrl_gpio_direction_input(chip, offset);
+ }
+
+ static int intel_gpio_direction_output(struct gpio_chip *chip, unsigned int offset,
+ int value)
+ {
+ intel_gpio_set(chip, offset, value);
+- return pinctrl_gpio_direction_output(chip->base + offset);
++ return pinctrl_gpio_direction_output(chip, offset);
+ }
+
+ static const struct gpio_chip intel_gpio_chip = {
+--- a/drivers/pinctrl/intel/pinctrl-lynxpoint.c
++++ b/drivers/pinctrl/intel/pinctrl-lynxpoint.c
+@@ -545,7 +545,7 @@ static void lp_gpio_set(struct gpio_chip
+
+ static int lp_gpio_direction_input(struct gpio_chip *chip, unsigned int offset)
+ {
+- return pinctrl_gpio_direction_input(chip->base + offset);
++ return pinctrl_gpio_direction_input(chip, offset);
+ }
+
+ static int lp_gpio_direction_output(struct gpio_chip *chip, unsigned int offset,
+@@ -553,7 +553,7 @@ static int lp_gpio_direction_output(stru
+ {
+ lp_gpio_set(chip, offset, value);
+
+- return pinctrl_gpio_direction_output(chip->base + offset);
++ return pinctrl_gpio_direction_output(chip, offset);
+ }
+
+ static int lp_gpio_get_direction(struct gpio_chip *chip, unsigned int offset)
+--- a/drivers/pinctrl/mediatek/pinctrl-moore.c
++++ b/drivers/pinctrl/mediatek/pinctrl-moore.c
+@@ -509,7 +509,7 @@ static void mtk_gpio_set(struct gpio_chi
+
+ static int mtk_gpio_direction_input(struct gpio_chip *chip, unsigned int gpio)
+ {
+- return pinctrl_gpio_direction_input(chip->base + gpio);
++ return pinctrl_gpio_direction_input(chip, gpio);
+ }
+
+ static int mtk_gpio_direction_output(struct gpio_chip *chip, unsigned int gpio,
+@@ -517,7 +517,7 @@ static int mtk_gpio_direction_output(str
+ {
+ mtk_gpio_set(chip, gpio, value);
+
+- return pinctrl_gpio_direction_output(chip->base + gpio);
++ return pinctrl_gpio_direction_output(chip, gpio);
+ }
+
+ static int mtk_gpio_to_irq(struct gpio_chip *chip, unsigned int offset)
+--- a/drivers/pinctrl/mediatek/pinctrl-mtk-common.c
++++ b/drivers/pinctrl/mediatek/pinctrl-mtk-common.c
+@@ -811,14 +811,14 @@ static const struct pinmux_ops mtk_pmx_o
+ static int mtk_gpio_direction_input(struct gpio_chip *chip,
+ unsigned offset)
+ {
+- return pinctrl_gpio_direction_input(chip->base + offset);
++ return pinctrl_gpio_direction_input(chip, offset);
+ }
+
+ static int mtk_gpio_direction_output(struct gpio_chip *chip,
+ unsigned offset, int value)
+ {
+ mtk_gpio_set(chip, offset, value);
+- return pinctrl_gpio_direction_output(chip->base + offset);
++ return pinctrl_gpio_direction_output(chip, offset);
+ }
+
+ static int mtk_gpio_get_direction(struct gpio_chip *chip, unsigned offset)
+--- a/drivers/pinctrl/mediatek/pinctrl-paris.c
++++ b/drivers/pinctrl/mediatek/pinctrl-paris.c
+@@ -904,7 +904,7 @@ static int mtk_gpio_direction_input(stru
+ if (gpio >= hw->soc->npins)
+ return -EINVAL;
+
+- return pinctrl_gpio_direction_input(chip->base + gpio);
++ return pinctrl_gpio_direction_input(chip, gpio);
+ }
+
+ static int mtk_gpio_direction_output(struct gpio_chip *chip, unsigned int gpio,
+@@ -917,7 +917,7 @@ static int mtk_gpio_direction_output(str
+
+ mtk_gpio_set(chip, gpio, value);
+
+- return pinctrl_gpio_direction_output(chip->base + gpio);
++ return pinctrl_gpio_direction_output(chip, gpio);
+ }
+
+ static int mtk_gpio_to_irq(struct gpio_chip *chip, unsigned int offset)
+--- a/drivers/pinctrl/nuvoton/pinctrl-npcm7xx.c
++++ b/drivers/pinctrl/nuvoton/pinctrl-npcm7xx.c
+@@ -171,7 +171,7 @@ static int npcmgpio_direction_input(stru
+ struct npcm7xx_gpio *bank = gpiochip_get_data(chip);
+ int ret;
+
+- ret = pinctrl_gpio_direction_input(offset + chip->base);
++ ret = pinctrl_gpio_direction_input(chip, offset);
+ if (ret)
+ return ret;
+
+@@ -188,7 +188,7 @@ static int npcmgpio_direction_output(str
+ dev_dbg(chip->parent, "gpio_direction_output: offset%d = %x\n", offset,
+ value);
+
+- ret = pinctrl_gpio_direction_output(offset + chip->base);
++ ret = pinctrl_gpio_direction_output(chip, offset);
+ if (ret)
+ return ret;
+
+--- a/drivers/pinctrl/pinctrl-as3722.c
++++ b/drivers/pinctrl/pinctrl-as3722.c
+@@ -502,14 +502,14 @@ static void as3722_gpio_set(struct gpio_
+
+ static int as3722_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
+ {
+- return pinctrl_gpio_direction_input(chip->base + offset);
++ return pinctrl_gpio_direction_input(chip, offset);
+ }
+
+ static int as3722_gpio_direction_output(struct gpio_chip *chip,
+ unsigned offset, int value)
+ {
+ as3722_gpio_set(chip, offset, value);
+- return pinctrl_gpio_direction_output(chip->base + offset);
++ return pinctrl_gpio_direction_output(chip, offset);
+ }
+
+ static int as3722_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
+--- a/drivers/pinctrl/pinctrl-axp209.c
++++ b/drivers/pinctrl/pinctrl-axp209.c
+@@ -126,7 +126,7 @@ static int axp20x_gpio_get_reg(unsigned
+
+ static int axp20x_gpio_input(struct gpio_chip *chip, unsigned int offset)
+ {
+- return pinctrl_gpio_direction_input(chip->base + offset);
++ return pinctrl_gpio_direction_input(chip, offset);
+ }
+
+ static int axp20x_gpio_get(struct gpio_chip *chip, unsigned int offset)
+--- a/drivers/pinctrl/pinctrl-cy8c95x0.c
++++ b/drivers/pinctrl/pinctrl-cy8c95x0.c
+@@ -557,7 +557,7 @@ out:
+
+ static int cy8c95x0_gpio_direction_input(struct gpio_chip *gc, unsigned int off)
+ {
+- return pinctrl_gpio_direction_input(gc->base + off);
++ return pinctrl_gpio_direction_input(gc, off);
+ }
+
+ static int cy8c95x0_gpio_direction_output(struct gpio_chip *gc,
+@@ -574,7 +574,7 @@ static int cy8c95x0_gpio_direction_outpu
+ if (ret)
+ return ret;
+
+- return pinctrl_gpio_direction_output(gc->base + off);
++ return pinctrl_gpio_direction_output(gc, off);
+ }
+
+ static int cy8c95x0_gpio_get_value(struct gpio_chip *gc, unsigned int off)
+--- a/drivers/pinctrl/pinctrl-ingenic.c
++++ b/drivers/pinctrl/pinctrl-ingenic.c
+@@ -133,6 +133,8 @@ struct ingenic_pinctrl {
+ struct pinctrl_pin_desc *pdesc;
+
+ const struct ingenic_chip_info *info;
++
++ struct gpio_chip *gc;
+ };
+
+ struct ingenic_gpio_chip {
+@@ -3561,14 +3563,14 @@ static int ingenic_gpio_get(struct gpio_
+ static int ingenic_gpio_direction_input(struct gpio_chip *gc,
+ unsigned int offset)
+ {
+- return pinctrl_gpio_direction_input(gc->base + offset);
++ return pinctrl_gpio_direction_input(gc, offset);
+ }
+
+ static int ingenic_gpio_direction_output(struct gpio_chip *gc,
+ unsigned int offset, int value)
+ {
+ ingenic_gpio_set(gc, offset, value);
+- return pinctrl_gpio_direction_output(gc->base + offset);
++ return pinctrl_gpio_direction_output(gc, offset);
+ }
+
+ static inline void ingenic_config_pin(struct ingenic_pinctrl *jzpc,
+@@ -4052,7 +4054,8 @@ static int ingenic_pinconf_set(struct pi
+ break;
+
+ case PIN_CONFIG_OUTPUT:
+- ret = pinctrl_gpio_direction_output(pin);
++ ret = pinctrl_gpio_direction_output(jzpc->gc,
++ pin - jzpc->gc->base);
+ if (ret)
+ return ret;
+
+@@ -4172,6 +4175,8 @@ static int __init ingenic_gpio_probe(str
+ if (!jzgc)
+ return -ENOMEM;
+
++ jzpc->gc = &jzgc->gc;
++
+ jzgc->jzpc = jzpc;
+ jzgc->reg_base = bank * jzpc->info->reg_offset;
+
+--- a/drivers/pinctrl/pinctrl-ocelot.c
++++ b/drivers/pinctrl/pinctrl-ocelot.c
+@@ -1779,7 +1779,7 @@ static int ocelot_gpio_get_direction(str
+ static int ocelot_gpio_direction_input(struct gpio_chip *chip,
+ unsigned int offset)
+ {
+- return pinctrl_gpio_direction_input(chip->base + offset);
++ return pinctrl_gpio_direction_input(chip, offset);
+ }
+
+ static int ocelot_gpio_direction_output(struct gpio_chip *chip,
+@@ -1795,7 +1795,7 @@ static int ocelot_gpio_direction_output(
+ regmap_write(info->map, REG(OCELOT_GPIO_OUT_CLR, info, offset),
+ pin);
+
+- return pinctrl_gpio_direction_output(chip->base + offset);
++ return pinctrl_gpio_direction_output(chip, offset);
+ }
+
+ static const struct gpio_chip ocelot_gpiolib_chip = {
+--- a/drivers/pinctrl/pinctrl-rk805.c
++++ b/drivers/pinctrl/pinctrl-rk805.c
+@@ -289,14 +289,14 @@ static void rk805_gpio_set(struct gpio_c
+ static int rk805_gpio_direction_input(struct gpio_chip *chip,
+ unsigned int offset)
+ {
+- return pinctrl_gpio_direction_input(chip->base + offset);
++ return pinctrl_gpio_direction_input(chip, offset);
+ }
+
+ static int rk805_gpio_direction_output(struct gpio_chip *chip,
+ unsigned int offset, int value)
+ {
+ rk805_gpio_set(chip, offset, value);
+- return pinctrl_gpio_direction_output(chip->base + offset);
++ return pinctrl_gpio_direction_output(chip, offset);
+ }
+
+ static int rk805_gpio_get_direction(struct gpio_chip *chip, unsigned int offset)
+--- a/drivers/pinctrl/pinctrl-st.c
++++ b/drivers/pinctrl/pinctrl-st.c
+@@ -719,7 +719,7 @@ static void st_gpio_set(struct gpio_chip
+
+ static int st_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
+ {
+- pinctrl_gpio_direction_input(chip->base + offset);
++ pinctrl_gpio_direction_input(chip, offset);
+
+ return 0;
+ }
+@@ -730,7 +730,7 @@ static int st_gpio_direction_output(stru
+ struct st_gpio_bank *bank = gpiochip_get_data(chip);
+
+ __st_gpio_set(bank, offset, value);
+- pinctrl_gpio_direction_output(chip->base + offset);
++ pinctrl_gpio_direction_output(chip, offset);
+
+ return 0;
+ }
+--- a/drivers/pinctrl/renesas/gpio.c
++++ b/drivers/pinctrl/renesas/gpio.c
+@@ -164,7 +164,7 @@ static void gpio_pin_set_value(struct sh
+
+ static int gpio_pin_direction_input(struct gpio_chip *gc, unsigned offset)
+ {
+- return pinctrl_gpio_direction_input(gc->base + offset);
++ return pinctrl_gpio_direction_input(gc, offset);
+ }
+
+ static int gpio_pin_direction_output(struct gpio_chip *gc, unsigned offset,
+@@ -172,7 +172,7 @@ static int gpio_pin_direction_output(str
+ {
+ gpio_pin_set_value(gpiochip_get_data(gc), offset, value);
+
+- return pinctrl_gpio_direction_output(gc->base + offset);
++ return pinctrl_gpio_direction_output(gc, offset);
+ }
+
+ static int gpio_pin_get(struct gpio_chip *gc, unsigned offset)
+--- a/drivers/pinctrl/stm32/pinctrl-stm32.c
++++ b/drivers/pinctrl/stm32/pinctrl-stm32.c
+@@ -241,7 +241,7 @@ static void stm32_gpio_set(struct gpio_c
+
+ static int stm32_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
+ {
+- return pinctrl_gpio_direction_input(chip->base + offset);
++ return pinctrl_gpio_direction_input(chip, offset);
+ }
+
+ static int stm32_gpio_direction_output(struct gpio_chip *chip,
+@@ -250,7 +250,7 @@ static int stm32_gpio_direction_output(s
+ struct stm32_gpio_bank *bank = gpiochip_get_data(chip);
+
+ __stm32_gpio_set(bank, offset, value);
+- pinctrl_gpio_direction_output(chip->base + offset);
++ pinctrl_gpio_direction_output(chip, offset);
+
+ return 0;
+ }
+--- a/drivers/pinctrl/vt8500/pinctrl-wmt.c
++++ b/drivers/pinctrl/vt8500/pinctrl-wmt.c
+@@ -528,14 +528,14 @@ static void wmt_gpio_set_value(struct gp
+
+ static int wmt_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
+ {
+- return pinctrl_gpio_direction_input(chip->base + offset);
++ return pinctrl_gpio_direction_input(chip, offset);
+ }
+
+ static int wmt_gpio_direction_output(struct gpio_chip *chip, unsigned offset,
+ int value)
+ {
+ wmt_gpio_set_value(chip, offset, value);
+- return pinctrl_gpio_direction_output(chip->base + offset);
++ return pinctrl_gpio_direction_output(chip, offset);
+ }
+
+ static const struct gpio_chip wmt_gpio_chip = {
+--- a/include/linux/pinctrl/consumer.h
++++ b/include/linux/pinctrl/consumer.h
+@@ -17,6 +17,7 @@
+ #include <linux/pinctrl/pinctrl-state.h>
+
+ struct device;
++struct gpio_chip;
+
+ /* This struct is private to the core and should be regarded as a cookie */
+ struct pinctrl;
+@@ -28,8 +29,10 @@ struct pinctrl_state;
+ extern bool pinctrl_gpio_can_use_line(unsigned gpio);
+ extern int pinctrl_gpio_request(unsigned gpio);
+ extern void pinctrl_gpio_free(unsigned gpio);
+-extern int pinctrl_gpio_direction_input(unsigned gpio);
+-extern int pinctrl_gpio_direction_output(unsigned gpio);
++extern int pinctrl_gpio_direction_input(struct gpio_chip *gc,
++ unsigned int offset);
++extern int pinctrl_gpio_direction_output(struct gpio_chip *gc,
++ unsigned int offset);
+ extern int pinctrl_gpio_set_config(unsigned gpio, unsigned long config);
+
+ extern struct pinctrl * __must_check pinctrl_get(struct device *dev);
+@@ -77,12 +80,14 @@ static inline void pinctrl_gpio_free(uns
+ {
+ }
+
+-static inline int pinctrl_gpio_direction_input(unsigned gpio)
++static inline int
++pinctrl_gpio_direction_input(struct gpio_chip *gc, unsigned int offset)
+ {
+ return 0;
+ }
+
+-static inline int pinctrl_gpio_direction_output(unsigned gpio)
++static inline int
++pinctrl_gpio_direction_output(struct gpio_chip *gc, unsigned int offset)
+ {
+ return 0;
+ }
--- /dev/null
+From stable+bounces-289363-greg=kroah.com@vger.kernel.org Sun Jul 26 23:28:44 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 26 Jul 2026 17:27:30 -0400
+Subject: platform/x86: dell-laptop: fix missing cleanups in init error path
+To: stable@vger.kernel.org
+Cc: "Haoxiang Li" <lihaoxiang@isrc.iscas.ac.cn>, "Ilpo Järvinen" <ilpo.jarvinen@linux.intel.com>, "Sasha Levin" <sashal@kernel.org>
+Message-ID: <20260726212730.4016280-2-sashal@kernel.org>
+
+From: Haoxiang Li <lihaoxiang@isrc.iscas.ac.cn>
+
+[ Upstream commit 6e9cab2247e5b243ae2d907ce7c948a8a9c8d61a ]
+
+dell_init() initializes several resources after dell_setup_rfkill(),
+including the optional touchpad LED, keyboard backlight LED, battery
+hook, debugfs directory and dell-laptop notifier.
+
+If a later LED or backlight registration fails, the error path only
+tears down the battery hook and rfkill resources. This leaves the
+notifier, debugfs directory, keyboard backlight LED and optional
+touchpad LED registered after dell_init() returns an error.
+
+Add the missing cleanup calls before tearing down rfkill.
+
+Fixes: 9c656b07997f ("platform/x86: dell-*: Call new led hw_changed API on kbd brightness change")
+Fixes: 037accfa14b2 ("dell-laptop: Add debugfs support")
+Fixes: 2d8b90be4f1c ("dell-laptop: support Synaptics/Alps touchpad led")
+Fixes: 6cff8d60aa0a ("platform: x86: dell-laptop: Add support for keyboard backlight")
+Cc: stable@vger.kernel.org
+Signed-off-by: Haoxiang Li <lihaoxiang@isrc.iscas.ac.cn>
+Link: https://patch.msgid.link/20260609081419.1995169-1-lihaoxiang@isrc.iscas.ac.cn
+Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/platform/x86/dell/dell-laptop.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+--- a/drivers/platform/x86/dell/dell-laptop.c
++++ b/drivers/platform/x86/dell/dell-laptop.c
+@@ -2295,6 +2295,11 @@ fail_backlight:
+ if (mute_led_registered)
+ led_classdev_unregister(&mute_led_cdev);
+ fail_led:
++ dell_laptop_unregister_notifier(&dell_laptop_notifier);
++ debugfs_remove_recursive(dell_laptop_dir);
++ kbd_led_exit();
++ if (quirks && quirks->touchpad_led)
++ touchpad_led_exit();
+ dell_cleanup_rfkill();
+ fail_rfkill:
+ platform_device_del(platform_device);
--- /dev/null
+From stable+bounces-289362-greg=kroah.com@vger.kernel.org Sun Jul 26 23:28:48 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 26 Jul 2026 17:27:29 -0400
+Subject: platform/x86: dell-smbios: Move request functions for reuse
+To: stable@vger.kernel.org
+Cc: "Lyndon Sanche" <lsanche@lyndeno.ca>, "Ilpo Järvinen" <ilpo.jarvinen@linux.intel.com>, "Sasha Levin" <sashal@kernel.org>
+Message-ID: <20260726212730.4016280-1-sashal@kernel.org>
+
+From: Lyndon Sanche <lsanche@lyndeno.ca>
+
+[ Upstream commit 33245680ae565aaa114d726bf407c8ff38e75e71 ]
+
+Move the dell_send_request and dell_fill_request symbols into smbios-base
+so that they can be used in multiple modules.
+
+Signed-off-by: Lyndon Sanche <lsanche@lyndeno.ca>
+Link: https://lore.kernel.org/r/20240529174843.13226-3-lsanche@lyndeno.ca
+Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Stable-dep-of: 6e9cab2247e5 ("platform/x86: dell-laptop: fix missing cleanups in init error path")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/platform/x86/dell/dell-laptop.c | 23 -----------------------
+ drivers/platform/x86/dell/dell-smbios-base.c | 25 +++++++++++++++++++++++++
+ drivers/platform/x86/dell/dell-smbios.h | 5 +++++
+ 3 files changed, 30 insertions(+), 23 deletions(-)
+
+--- a/drivers/platform/x86/dell/dell-laptop.c
++++ b/drivers/platform/x86/dell/dell-laptop.c
+@@ -353,29 +353,6 @@ static const struct dmi_system_id dell_q
+ { }
+ };
+
+-static void dell_fill_request(struct calling_interface_buffer *buffer,
+- u32 arg0, u32 arg1, u32 arg2, u32 arg3)
+-{
+- memset(buffer, 0, sizeof(struct calling_interface_buffer));
+- buffer->input[0] = arg0;
+- buffer->input[1] = arg1;
+- buffer->input[2] = arg2;
+- buffer->input[3] = arg3;
+-}
+-
+-static int dell_send_request(struct calling_interface_buffer *buffer,
+- u16 class, u16 select)
+-{
+- int ret;
+-
+- buffer->cmd_class = class;
+- buffer->cmd_select = select;
+- ret = dell_smbios_call(buffer);
+- if (ret != 0)
+- return ret;
+- return dell_smbios_error(buffer->output[0]);
+-}
+-
+ /*
+ * Derived from information in smbios-wireless-ctl:
+ *
+--- a/drivers/platform/x86/dell/dell-smbios-base.c
++++ b/drivers/platform/x86/dell/dell-smbios-base.c
+@@ -320,6 +320,31 @@ out_smbios_call:
+ }
+ EXPORT_SYMBOL_GPL(dell_smbios_call);
+
++void dell_fill_request(struct calling_interface_buffer *buffer,
++ u32 arg0, u32 arg1, u32 arg2, u32 arg3)
++{
++ memset(buffer, 0, sizeof(struct calling_interface_buffer));
++ buffer->input[0] = arg0;
++ buffer->input[1] = arg1;
++ buffer->input[2] = arg2;
++ buffer->input[3] = arg3;
++}
++EXPORT_SYMBOL_GPL(dell_fill_request);
++
++int dell_send_request(struct calling_interface_buffer *buffer,
++ u16 class, u16 select)
++{
++ int ret;
++
++ buffer->cmd_class = class;
++ buffer->cmd_select = select;
++ ret = dell_smbios_call(buffer);
++ if (ret != 0)
++ return ret;
++ return dell_smbios_error(buffer->output[0]);
++}
++EXPORT_SYMBOL_GPL(dell_send_request);
++
+ struct calling_interface_token *dell_smbios_find_token(int tokenid)
+ {
+ int i;
+--- a/drivers/platform/x86/dell/dell-smbios.h
++++ b/drivers/platform/x86/dell/dell-smbios.h
+@@ -64,6 +64,11 @@ int dell_smbios_call_filter(struct devic
+ struct calling_interface_buffer *buffer);
+ int dell_smbios_call(struct calling_interface_buffer *buffer);
+
++void dell_fill_request(struct calling_interface_buffer *buffer,
++ u32 arg0, u32 arg1, u32 arg2, u32 arg3);
++int dell_send_request(struct calling_interface_buffer *buffer,
++ u16 class, u16 select);
++
+ struct calling_interface_token *dell_smbios_find_token(int tokenid);
+
+ enum dell_laptop_notifier_actions {
--- /dev/null
+From stable+bounces-288015-greg=kroah.com@vger.kernel.org Wed Jul 22 19:36:30 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 13:30:03 -0400
+Subject: rxrpc: Don't need barrier for ->tx_bottom and ->acks_hard_ack
+To: stable@vger.kernel.org
+Cc: David Howells <dhowells@redhat.com>, Marc Dionne <marc.dionne@auristor.com>, linux-afs@lists.infradead.org, Jakub Kicinski <kuba@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260722173006.2069029-1-sashal@kernel.org>
+
+From: David Howells <dhowells@redhat.com>
+
+[ Upstream commit 6396b48ac0a77165f9c2c40ab03d6c8188c89739 ]
+
+We don't need a barrier for the ->tx_bottom value (which indicates the
+lowest sequence still in the transmission queue) and the ->acks_hard_ack
+value (which tracks the DATA packets hard-ack'd by the latest ACK packet
+received and thus indicates which DATA packets can now be discarded) as the
+app thread doesn't use either value as a reference to memory to access.
+Rather, the app thread merely uses these as a guide to how much space is
+available in the transmission queue
+
+Change the code to use READ/WRITE_ONCE() instead.
+
+Also, change rxrpc_check_tx_space() to use the same value for tx_bottom
+throughout.
+
+Signed-off-by: David Howells <dhowells@redhat.com>
+cc: Marc Dionne <marc.dionne@auristor.com>
+cc: linux-afs@lists.infradead.org
+Link: https://patch.msgid.link/20241204074710.990092-18-dhowells@redhat.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Stable-dep-of: e66f8f32f501 ("rxrpc: Fix socket notification race")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/rxrpc/sendmsg.c | 8 +++++---
+ net/rxrpc/txbuf.c | 4 ++--
+ 2 files changed, 7 insertions(+), 5 deletions(-)
+
+--- a/net/rxrpc/sendmsg.c
++++ b/net/rxrpc/sendmsg.c
+@@ -93,9 +93,11 @@ no_wait:
+ */
+ static bool rxrpc_check_tx_space(struct rxrpc_call *call, rxrpc_seq_t *_tx_win)
+ {
++ rxrpc_seq_t tx_bottom = READ_ONCE(call->tx_bottom);
++
+ if (_tx_win)
+- *_tx_win = call->tx_bottom;
+- return call->tx_prepared - call->tx_bottom < 256;
++ *_tx_win = tx_bottom;
++ return call->tx_prepared - tx_bottom < 256;
+ }
+
+ /*
+@@ -137,7 +139,7 @@ static int rxrpc_wait_for_tx_window_wait
+ rtt = 2;
+
+ timeout = rtt;
+- tx_start = smp_load_acquire(&call->acks_hard_ack);
++ tx_start = READ_ONCE(call->acks_hard_ack);
+
+ for (;;) {
+ set_current_state(TASK_UNINTERRUPTIBLE);
+--- a/net/rxrpc/txbuf.c
++++ b/net/rxrpc/txbuf.c
+@@ -112,14 +112,14 @@ void rxrpc_shrink_call_tx_buffer(struct
+
+ while ((txb = list_first_entry_or_null(&call->tx_buffer,
+ struct rxrpc_txbuf, call_link))) {
+- hard_ack = smp_load_acquire(&call->acks_hard_ack);
++ hard_ack = call->acks_hard_ack;
+ if (before(hard_ack, txb->seq))
+ break;
+
+ if (txb->seq != call->tx_bottom + 1)
+ rxrpc_see_txbuf(txb, rxrpc_txbuf_see_out_of_step);
+ ASSERTCMP(txb->seq, ==, call->tx_bottom + 1);
+- smp_store_release(&call->tx_bottom, call->tx_bottom + 1);
++ WRITE_ONCE(call->tx_bottom, call->tx_bottom + 1);
+ list_del_rcu(&txb->call_link);
+
+ trace_rxrpc_txqueue(call, rxrpc_txqueue_dequeue);
--- /dev/null
+From stable+bounces-288017-greg=kroah.com@vger.kernel.org Wed Jul 22 19:45:32 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 13:30:05 -0400
+Subject: rxrpc: Fix notification vs call-release vs recvmsg
+To: stable@vger.kernel.org
+Cc: David Howells <dhowells@redhat.com>, Jeffrey Altman <jaltman@auristor.com>, Marc Dionne <marc.dionne@auristor.com>, "Junvyyang, Tencent Zhuque Lab" <zhuque@tencent.com>, LePremierHomme <kwqcheii@proton.me>, Simon Horman <horms@kernel.org>, linux-afs@lists.infradead.org, Jakub Kicinski <kuba@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260722173006.2069029-3-sashal@kernel.org>
+
+From: David Howells <dhowells@redhat.com>
+
+[ Upstream commit 2fd895842d49c23137ae48252dd211e5d6d8a3ed ]
+
+When a call is released, rxrpc takes the spinlock and removes it from
+->recvmsg_q in an effort to prevent racing recvmsg() invocations from
+seeing the same call. Now, rxrpc_recvmsg() only takes the spinlock when
+actually removing a call from the queue; it doesn't, however, take it in
+the lead up to that when it checks to see if the queue is empty. It *does*
+hold the socket lock, which prevents a recvmsg/recvmsg race - but this
+doesn't prevent sendmsg from ending the call because sendmsg() drops the
+socket lock and relies on the call->user_mutex.
+
+Fix this by firstly removing the bit in rxrpc_release_call() that dequeues
+the released call and, instead, rely on recvmsg() to simply discard
+released calls (done in a preceding fix).
+
+Secondly, rxrpc_notify_socket() is abandoned if the call is already marked
+as released rather than trying to be clever by setting both pointers in
+call->recvmsg_link to NULL to trick list_empty(). This isn't perfect and
+can still race, resulting in a released call on the queue, but recvmsg()
+will now clean that up.
+
+Fixes: 17926a79320a ("[AF_RXRPC]: Provide secure RxRPC sockets for use by userspace and kernel both")
+Signed-off-by: David Howells <dhowells@redhat.com>
+Reviewed-by: Jeffrey Altman <jaltman@auristor.com>
+cc: Marc Dionne <marc.dionne@auristor.com>
+cc: Junvyyang, Tencent Zhuque Lab <zhuque@tencent.com>
+cc: LePremierHomme <kwqcheii@proton.me>
+cc: Simon Horman <horms@kernel.org>
+cc: linux-afs@lists.infradead.org
+Link: https://patch.msgid.link/20250717074350.3767366-4-dhowells@redhat.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Stable-dep-of: e66f8f32f501 ("rxrpc: Fix socket notification race")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ include/trace/events/rxrpc.h | 3 ++-
+ net/rxrpc/call_object.c | 28 ++++++++++++----------------
+ net/rxrpc/recvmsg.c | 4 ++++
+ 3 files changed, 18 insertions(+), 17 deletions(-)
+
+--- a/include/trace/events/rxrpc.h
++++ b/include/trace/events/rxrpc.h
+@@ -271,10 +271,10 @@
+ EM(rxrpc_call_put_poke, "PUT poke ") \
+ EM(rxrpc_call_put_recvmsg, "PUT recvmsg ") \
+ EM(rxrpc_call_put_recvmsg_peek_nowait, "PUT peek-nwt") \
++ EM(rxrpc_call_put_release_recvmsg_q, "PUT rls-rcmq") \
+ EM(rxrpc_call_put_release_sock, "PUT rls-sock") \
+ EM(rxrpc_call_put_release_sock_tba, "PUT rls-sk-a") \
+ EM(rxrpc_call_put_sendmsg, "PUT sendmsg ") \
+- EM(rxrpc_call_put_unnotify, "PUT unnotify") \
+ EM(rxrpc_call_put_userid_exists, "PUT u-exists") \
+ EM(rxrpc_call_put_userid, "PUT user-id ") \
+ EM(rxrpc_call_see_accept, "SEE accept ") \
+@@ -287,6 +287,7 @@
+ EM(rxrpc_call_see_disconnected, "SEE disconn ") \
+ EM(rxrpc_call_see_distribute_error, "SEE dist-err") \
+ EM(rxrpc_call_see_input, "SEE input ") \
++ EM(rxrpc_call_see_notify_released, "SEE nfy-rlsd") \
+ EM(rxrpc_call_see_recvmsg, "SEE recvmsg ") \
+ EM(rxrpc_call_see_recvmsg_requeue, "SEE recv-rqu") \
+ EM(rxrpc_call_see_recvmsg_requeue_first, "SEE recv-rqF") \
+--- a/net/rxrpc/call_object.c
++++ b/net/rxrpc/call_object.c
+@@ -550,7 +550,7 @@ static void rxrpc_cleanup_ring(struct rx
+ void rxrpc_release_call(struct rxrpc_sock *rx, struct rxrpc_call *call)
+ {
+ struct rxrpc_connection *conn = call->conn;
+- bool put = false, putu = false;
++ bool putu = false;
+
+ _enter("{%d,%d}", call->debug_id, refcount_read(&call->ref));
+
+@@ -562,23 +562,13 @@ void rxrpc_release_call(struct rxrpc_soc
+
+ rxrpc_put_call_slot(call);
+
+- /* Make sure we don't get any more notifications */
++ /* Note that at this point, the call may still be on or may have been
++ * added back on to the socket receive queue. recvmsg() must discard
++ * released calls. The CALL_RELEASED flag should prevent further
++ * notifications.
++ */
+ spin_lock_irq(&rx->recvmsg_lock);
+-
+- if (!list_empty(&call->recvmsg_link)) {
+- _debug("unlinking once-pending call %p { e=%lx f=%lx }",
+- call, call->events, call->flags);
+- list_del(&call->recvmsg_link);
+- put = true;
+- }
+-
+- /* list_empty() must return false in rxrpc_notify_socket() */
+- call->recvmsg_link.next = NULL;
+- call->recvmsg_link.prev = NULL;
+-
+ spin_unlock_irq(&rx->recvmsg_lock);
+- if (put)
+- rxrpc_put_call(call, rxrpc_call_put_unnotify);
+
+ write_lock(&rx->call_lock);
+
+@@ -627,6 +617,12 @@ void rxrpc_release_calls_on_socket(struc
+ rxrpc_put_call(call, rxrpc_call_put_release_sock);
+ }
+
++ while ((call = list_first_entry_or_null(&rx->recvmsg_q,
++ struct rxrpc_call, recvmsg_link))) {
++ list_del_init(&call->recvmsg_link);
++ rxrpc_put_call(call, rxrpc_call_put_release_recvmsg_q);
++ }
++
+ _leave("");
+ }
+
+--- a/net/rxrpc/recvmsg.c
++++ b/net/rxrpc/recvmsg.c
+@@ -29,6 +29,10 @@ void rxrpc_notify_socket(struct rxrpc_ca
+
+ if (!list_empty(&call->recvmsg_link))
+ return;
++ if (test_bit(RXRPC_CALL_RELEASED, &call->flags)) {
++ rxrpc_see_call(call, rxrpc_call_see_notify_released);
++ return;
++ }
+
+ rcu_read_lock();
+
--- /dev/null
+From stable+bounces-288018-greg=kroah.com@vger.kernel.org Wed Jul 22 19:36:38 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 13:30:06 -0400
+Subject: rxrpc: Fix socket notification race
+To: stable@vger.kernel.org
+Cc: David Howells <dhowells@redhat.com>, Marc Dionne <marc.dionne@auristor.com>, Jeffrey Altman <jaltman@auristor.com>, Simon Horman <horms@kernel.org>, linux-afs@lists.infradead.org, stable@kernel.org, Jakub Kicinski <kuba@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260722173006.2069029-4-sashal@kernel.org>
+
+From: David Howells <dhowells@redhat.com>
+
+[ Upstream commit e66f8f32f50116670dbbee5bc9e692cd2cd0c8f8 ]
+
+There's a race between rxrpc_recvmsg() and rxrpc_notify_socket(), whereby
+the latter's attempt to avoid disabling interrupts and taking the socket's
+recvmsg_lock if the call is already queued may happen simultaneously with
+the former's discarding of a call that has nothing queued.
+
+Fix this by removing the shortcut. Note that this only affects userspace's
+use of AF_RXRPC; the AFS filesystem driver doesn't use the socket queue.
+
+Fixes: 248f219cb8bc ("rxrpc: Rewrite the data and ack handling code")
+Link: https://sashiko.dev/#/patchset/20260616155749.2125907-1-dhowells%40redhat.com
+Signed-off-by: David Howells <dhowells@redhat.com>
+cc: Marc Dionne <marc.dionne@auristor.com>
+cc: Jeffrey Altman <jaltman@auristor.com>
+cc: Simon Horman <horms@kernel.org>
+cc: linux-afs@lists.infradead.org
+cc: stable@kernel.org
+Link: https://patch.msgid.link/20260624163819.3017002-10-dhowells@redhat.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/rxrpc/recvmsg.c | 2 --
+ 1 file changed, 2 deletions(-)
+
+--- a/net/rxrpc/recvmsg.c
++++ b/net/rxrpc/recvmsg.c
+@@ -27,8 +27,6 @@ void rxrpc_notify_socket(struct rxrpc_ca
+
+ _enter("%d", call->debug_id);
+
+- if (!list_empty(&call->recvmsg_link))
+- return;
+ if (test_bit(RXRPC_CALL_RELEASED, &call->flags)) {
+ rxrpc_see_call(call, rxrpc_call_see_notify_released);
+ return;
--- /dev/null
+From stable+bounces-287898-greg=kroah.com@vger.kernel.org Wed Jul 22 16:51:03 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 10:48:35 -0400
+Subject: rxrpc: Pull out certain app callback funcs into an ops table
+To: stable@vger.kernel.org
+Cc: David Howells <dhowells@redhat.com>, Marc Dionne <marc.dionne@auristor.com>, Simon Horman <horms@kernel.org>, linux-afs@lists.infradead.org, Jakub Kicinski <kuba@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260722144836.1601242-3-sashal@kernel.org>
+
+From: David Howells <dhowells@redhat.com>
+
+[ Upstream commit 23738cc8048322cf324f330cd697380fb3455da5 ]
+
+A number of functions separately furnish an AF_RXRPC socket with callback
+function pointers into a kernel app (such as the AFS filesystem) that is
+using it. Replace most of these with an ops table for the entire socket.
+This makes it easier to add more callback functions.
+
+Note that the call incoming data processing callback is retaind as that
+gets set to different things, depending on the type of op.
+
+Signed-off-by: David Howells <dhowells@redhat.com>
+cc: Marc Dionne <marc.dionne@auristor.com>
+cc: Simon Horman <horms@kernel.org>
+cc: linux-afs@lists.infradead.org
+Link: https://patch.msgid.link/20250411095303.2316168-3-dhowells@redhat.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Stable-dep-of: dc175389b18c ("rxrpc: serialize kernel accept preallocation with socket teardown")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/afs/rxrpc.c | 11 ++++++++---
+ include/net/af_rxrpc.h | 25 +++++++++++++++----------
+ net/rxrpc/af_rxrpc.c | 20 ++++++++------------
+ net/rxrpc/ar-internal.h | 3 +--
+ net/rxrpc/call_accept.c | 34 ++++++++++++++++------------------
+ net/rxrpc/rxperf.c | 10 +++++++---
+ 6 files changed, 55 insertions(+), 48 deletions(-)
+
+--- a/fs/afs/rxrpc.c
++++ b/fs/afs/rxrpc.c
+@@ -23,8 +23,15 @@ static void afs_wake_up_async_call(struc
+ static void afs_process_async_call(struct work_struct *);
+ static void afs_rx_new_call(struct sock *, struct rxrpc_call *, unsigned long);
+ static void afs_rx_discard_new_call(struct rxrpc_call *, unsigned long);
++static void afs_rx_attach(struct rxrpc_call *rxcall, unsigned long user_call_ID);
+ static int afs_deliver_cm_op_id(struct afs_call *);
+
++static const struct rxrpc_kernel_ops afs_rxrpc_callback_ops = {
++ .notify_new_call = afs_rx_new_call,
++ .discard_new_call = afs_rx_discard_new_call,
++ .user_attach_call = afs_rx_attach,
++};
++
+ /* asynchronous incoming call initial processing */
+ static const struct afs_call_type afs_RXCMxxxx = {
+ .name = "CB.xxxx",
+@@ -83,8 +90,7 @@ int afs_open_socket(struct afs_net *net)
+ * it sends back to us.
+ */
+
+- rxrpc_kernel_new_call_notification(socket, afs_rx_new_call,
+- afs_rx_discard_new_call);
++ rxrpc_kernel_set_notifications(socket, &afs_rxrpc_callback_ops);
+
+ ret = kernel_listen(socket, INT_MAX);
+ if (ret < 0)
+@@ -748,7 +754,6 @@ void afs_charge_preallocation(struct wor
+
+ if (rxrpc_kernel_charge_accept(net->socket,
+ afs_wake_up_async_call,
+- afs_rx_attach,
+ (unsigned long)call,
+ GFP_KERNEL,
+ call->debug_id) < 0)
+--- a/include/net/af_rxrpc.h
++++ b/include/net/af_rxrpc.h
+@@ -28,18 +28,23 @@ enum rxrpc_interruptibility {
+ */
+ extern atomic_t rxrpc_debug_id;
+
++/*
++ * Operations table for rxrpc to call out to a kernel application (e.g. kAFS).
++ */
++struct rxrpc_kernel_ops {
++ void (*notify_new_call)(struct sock *sk, struct rxrpc_call *call,
++ unsigned long user_call_ID);
++ void (*discard_new_call)(struct rxrpc_call *call, unsigned long user_call_ID);
++ void (*user_attach_call)(struct rxrpc_call *call, unsigned long user_call_ID);
++};
++
+ typedef void (*rxrpc_notify_rx_t)(struct sock *, struct rxrpc_call *,
+ unsigned long);
+ typedef void (*rxrpc_notify_end_tx_t)(struct sock *, struct rxrpc_call *,
+ unsigned long);
+-typedef void (*rxrpc_notify_new_call_t)(struct sock *, struct rxrpc_call *,
+- unsigned long);
+-typedef void (*rxrpc_discard_new_call_t)(struct rxrpc_call *, unsigned long);
+-typedef void (*rxrpc_user_attach_call_t)(struct rxrpc_call *, unsigned long);
+
+-void rxrpc_kernel_new_call_notification(struct socket *,
+- rxrpc_notify_new_call_t,
+- rxrpc_discard_new_call_t);
++void rxrpc_kernel_set_notifications(struct socket *sock,
++ const struct rxrpc_kernel_ops *app_ops);
+ struct rxrpc_call *rxrpc_kernel_begin_call(struct socket *sock,
+ struct sockaddr_rxrpc *srx,
+ struct key *key,
+@@ -63,9 +68,9 @@ void rxrpc_kernel_put_call(struct socket
+ void rxrpc_kernel_get_peer(struct socket *, struct rxrpc_call *,
+ struct sockaddr_rxrpc *);
+ bool rxrpc_kernel_get_srtt(struct socket *, struct rxrpc_call *, u32 *);
+-int rxrpc_kernel_charge_accept(struct socket *, rxrpc_notify_rx_t,
+- rxrpc_user_attach_call_t, unsigned long, gfp_t,
+- unsigned int);
++int rxrpc_kernel_charge_accept(struct socket *sock, rxrpc_notify_rx_t notify_rx,
++ unsigned long user_call_ID, gfp_t gfp,
++ unsigned int debug_id);
+ void rxrpc_kernel_set_tx_length(struct socket *, struct rxrpc_call *, s64);
+ bool rxrpc_kernel_check_life(const struct socket *, const struct rxrpc_call *);
+ u32 rxrpc_kernel_get_epoch(struct socket *, struct rxrpc_call *);
+--- a/net/rxrpc/af_rxrpc.c
++++ b/net/rxrpc/af_rxrpc.c
+@@ -418,24 +418,20 @@ u32 rxrpc_kernel_get_epoch(struct socket
+ EXPORT_SYMBOL(rxrpc_kernel_get_epoch);
+
+ /**
+- * rxrpc_kernel_new_call_notification - Get notifications of new calls
+- * @sock: The socket to intercept received messages on
+- * @notify_new_call: Function to be called when new calls appear
+- * @discard_new_call: Function to discard preallocated calls
++ * rxrpc_kernel_set_notifications - Set table of callback operations
++ * @sock: The socket to install table upon
++ * @app_ops: Callback operation table to set
+ *
+- * Allow a kernel service to be given notifications about new calls.
++ * Allow a kernel service to set a table of event notifications on a socket.
+ */
+-void rxrpc_kernel_new_call_notification(
+- struct socket *sock,
+- rxrpc_notify_new_call_t notify_new_call,
+- rxrpc_discard_new_call_t discard_new_call)
++void rxrpc_kernel_set_notifications(struct socket *sock,
++ const struct rxrpc_kernel_ops *app_ops)
+ {
+ struct rxrpc_sock *rx = rxrpc_sk(sock->sk);
+
+- rx->notify_new_call = notify_new_call;
+- rx->discard_new_call = discard_new_call;
++ rx->app_ops = app_ops;
+ }
+-EXPORT_SYMBOL(rxrpc_kernel_new_call_notification);
++EXPORT_SYMBOL(rxrpc_kernel_set_notifications);
+
+ /**
+ * rxrpc_kernel_set_max_life - Set maximum lifespan on a call
+--- a/net/rxrpc/ar-internal.h
++++ b/net/rxrpc/ar-internal.h
+@@ -141,8 +141,7 @@ struct rxrpc_backlog {
+ struct rxrpc_sock {
+ /* WARNING: sk has to be the first member */
+ struct sock sk;
+- rxrpc_notify_new_call_t notify_new_call; /* Func to notify of new call */
+- rxrpc_discard_new_call_t discard_new_call; /* Func to discard a new call */
++ const struct rxrpc_kernel_ops *app_ops; /* Table of kernel app notification funcs */
+ struct rxrpc_local *local; /* local endpoint */
+ struct rxrpc_backlog *backlog; /* Preallocation for services */
+ spinlock_t incoming_lock; /* Incoming call vs service shutdown lock */
+--- a/net/rxrpc/call_accept.c
++++ b/net/rxrpc/call_accept.c
+@@ -34,7 +34,6 @@ static void rxrpc_dummy_notify(struct so
+ static int rxrpc_service_prealloc_one(struct rxrpc_sock *rx,
+ struct rxrpc_backlog *b,
+ rxrpc_notify_rx_t notify_rx,
+- rxrpc_user_attach_call_t user_attach_call,
+ unsigned long user_call_ID, gfp_t gfp,
+ unsigned int debug_id)
+ {
+@@ -123,9 +122,10 @@ static int rxrpc_service_prealloc_one(st
+
+ call->user_call_ID = user_call_ID;
+ call->notify_rx = notify_rx;
+- if (user_attach_call) {
++ if (rx->app_ops &&
++ rx->app_ops->user_attach_call) {
+ rxrpc_get_call(call, rxrpc_call_get_kernel_service);
+- user_attach_call(call, user_call_ID);
++ rx->app_ops->user_attach_call(call, user_call_ID);
+ }
+
+ rxrpc_get_call(call, rxrpc_call_get_userid);
+@@ -221,9 +221,10 @@ void rxrpc_discard_prealloc(struct rxrpc
+ struct rxrpc_call *call = b->call_backlog[tail];
+ rxrpc_see_call(call, rxrpc_call_see_discard);
+ rcu_assign_pointer(call->socket, rx);
+- if (rx->discard_new_call) {
++ if (rx->app_ops &&
++ rx->app_ops->discard_new_call) {
+ _debug("discard %lx", call->user_call_ID);
+- rx->discard_new_call(call, call->user_call_ID);
++ rx->app_ops->discard_new_call(call, call->user_call_ID);
+ if (call->notify_rx)
+ call->notify_rx = rxrpc_dummy_notify;
+ rxrpc_put_call(call, rxrpc_call_put_kernel);
+@@ -392,8 +393,9 @@ bool rxrpc_new_incoming_call(struct rxrp
+ rxrpc_incoming_call(rx, call, skb);
+ conn = call->conn;
+
+- if (rx->notify_new_call)
+- rx->notify_new_call(&rx->sk, call, call->user_call_ID);
++ if (rx->app_ops &&
++ rx->app_ops->notify_new_call)
++ rx->app_ops->notify_new_call(&rx->sk, call, call->user_call_ID);
+
+ spin_lock(&conn->state_lock);
+ if (conn->state == RXRPC_CONN_SERVICE_UNSECURED) {
+@@ -445,8 +447,7 @@ int rxrpc_user_charge_accept(struct rxrp
+ if (rx->sk.sk_state == RXRPC_CLOSE)
+ return -ESHUTDOWN;
+
+- return rxrpc_service_prealloc_one(rx, b, NULL, NULL, user_call_ID,
+- GFP_KERNEL,
++ return rxrpc_service_prealloc_one(rx, b, NULL, user_call_ID, GFP_KERNEL,
+ atomic_inc_return(&rxrpc_debug_id));
+ }
+
+@@ -454,20 +455,18 @@ int rxrpc_user_charge_accept(struct rxrp
+ * rxrpc_kernel_charge_accept - Charge up socket with preallocated calls
+ * @sock: The socket on which to preallocate
+ * @notify_rx: Event notification function for the call
+- * @user_attach_call: Func to attach call to user_call_ID
+ * @user_call_ID: The tag to attach to the preallocated call
+ * @gfp: The allocation conditions.
+ * @debug_id: The tracing debug ID.
+ *
+- * Charge up the socket with preallocated calls, each with a user ID. A
+- * function should be provided to effect the attachment from the user's side.
+- * The user is given a ref to hold on the call.
++ * Charge up the socket with preallocated calls, each with a user ID. The
++ * ->user_attach_call() callback function should be provided to effect the
++ * attachment from the user's side. The user is given a ref to hold on the
++ * call.
+ *
+ * Note that the call may be come connected before this function returns.
+ */
+-int rxrpc_kernel_charge_accept(struct socket *sock,
+- rxrpc_notify_rx_t notify_rx,
+- rxrpc_user_attach_call_t user_attach_call,
++int rxrpc_kernel_charge_accept(struct socket *sock, rxrpc_notify_rx_t notify_rx,
+ unsigned long user_call_ID, gfp_t gfp,
+ unsigned int debug_id)
+ {
+@@ -477,8 +476,7 @@ int rxrpc_kernel_charge_accept(struct so
+ if (sock->sk->sk_state == RXRPC_CLOSE)
+ return -ESHUTDOWN;
+
+- return rxrpc_service_prealloc_one(rx, b, notify_rx,
+- user_attach_call, user_call_ID,
++ return rxrpc_service_prealloc_one(rx, b, notify_rx, user_call_ID,
+ gfp, debug_id);
+ }
+ EXPORT_SYMBOL(rxrpc_kernel_charge_accept);
+--- a/net/rxrpc/rxperf.c
++++ b/net/rxrpc/rxperf.c
+@@ -136,6 +136,12 @@ static void rxperf_notify_end_reply_tx(s
+ RXPERF_CALL_SV_AWAIT_ACK);
+ }
+
++static const struct rxrpc_kernel_ops rxperf_rxrpc_callback_ops = {
++ .notify_new_call = rxperf_rx_new_call,
++ .discard_new_call = rxperf_rx_discard_new_call,
++ .user_attach_call = rxperf_rx_attach,
++};
++
+ /*
+ * Charge the incoming call preallocation.
+ */
+@@ -161,7 +167,6 @@ static void rxperf_charge_preallocation(
+
+ if (rxrpc_kernel_charge_accept(rxperf_socket,
+ rxperf_notify_rx,
+- rxperf_rx_attach,
+ (unsigned long)call,
+ GFP_KERNEL,
+ call->debug_id) < 0)
+@@ -209,8 +214,7 @@ static int rxperf_open_socket(void)
+ if (ret < 0)
+ goto error_2;
+
+- rxrpc_kernel_new_call_notification(socket, rxperf_rx_new_call,
+- rxperf_rx_discard_new_call);
++ rxrpc_kernel_set_notifications(socket, &rxperf_rxrpc_callback_ops);
+
+ ret = kernel_listen(socket, INT_MAX);
+ if (ret < 0)
--- /dev/null
+From stable+bounces-287899-greg=kroah.com@vger.kernel.org Wed Jul 22 16:51:13 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 10:48:36 -0400
+Subject: rxrpc: serialize kernel accept preallocation with socket teardown
+To: stable@vger.kernel.org
+Cc: Li Daming <d4n.for.sec@gmail.com>, Yuan Tan <yuantan098@gmail.com>, Yifan Wu <yifanwucs@gmail.com>, Juefei Pu <tomapufckgml@gmail.com>, Xin Liu <bird@lzu.edu.cn>, Ren Wei <n05ec@lzu.edu.cn>, David Howells <dhowells@redhat.com>, Marc Dionne <marc.dionne@auristor.com>, Jeffrey Altman <jaltman@auristor.com>, Simon Horman <horms@kernel.org>, linux-afs@lists.infradead.org, stable@kernel.org, Jakub Kicinski <kuba@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260722144836.1601242-4-sashal@kernel.org>
+
+From: Li Daming <d4n.for.sec@gmail.com>
+
+[ Upstream commit dc175389b18c29a5303ee83169ec653adfae3e17 ]
+
+rxrpc_kernel_charge_accept() reads rx->backlog without any
+socket/backlog synchronization and passes that raw pointer into
+rxrpc_service_prealloc_one(). A concurrent rxrpc_discard_prealloc()
+sets rx->backlog = NULL and frees the backlog rings, so a kernel
+preallocation worker can keep using a freed struct rxrpc_backlog
+while updating *_backlog_head/tail and array slots.
+
+Serialize the state check and backlog lookup with the socket lock,
+and reject kernel preallocation once teardown has disabled
+listening or discarded the service backlog.
+
+Fixes: 00e907127e6f ("rxrpc: Preallocate peers, conns and calls for incoming service requests")
+Reported-by: Yuan Tan <yuantan098@gmail.com>
+Reported-by: Yifan Wu <yifanwucs@gmail.com>
+Reported-by: Juefei Pu <tomapufckgml@gmail.com>
+Reported-by: Xin Liu <bird@lzu.edu.cn>
+Signed-off-by: Li Daming <d4n.for.sec@gmail.com>
+Signed-off-by: Ren Wei <n05ec@lzu.edu.cn>
+Signed-off-by: David Howells <dhowells@redhat.com>
+cc: Marc Dionne <marc.dionne@auristor.com>
+cc: Jeffrey Altman <jaltman@auristor.com>
+cc: Simon Horman <horms@kernel.org>
+cc: linux-afs@lists.infradead.org
+cc: stable@kernel.org
+Link: https://patch.msgid.link/20260609140911.838677-6-dhowells@redhat.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/rxrpc/call_accept.c | 25 +++++++++++++++++++------
+ 1 file changed, 19 insertions(+), 6 deletions(-)
+
+--- a/net/rxrpc/call_accept.c
++++ b/net/rxrpc/call_accept.c
+@@ -470,13 +470,26 @@ int rxrpc_kernel_charge_accept(struct so
+ unsigned long user_call_ID, gfp_t gfp,
+ unsigned int debug_id)
+ {
+- struct rxrpc_sock *rx = rxrpc_sk(sock->sk);
+- struct rxrpc_backlog *b = rx->backlog;
++ struct rxrpc_backlog *b;
++ struct rxrpc_sock *rx;
++ struct sock *sk;
++ int ret;
+
+- if (sock->sk->sk_state == RXRPC_CLOSE)
+- return -ESHUTDOWN;
++ sk = sock->sk;
++ rx = rxrpc_sk(sk);
+
+- return rxrpc_service_prealloc_one(rx, b, notify_rx, user_call_ID,
+- gfp, debug_id);
++ lock_sock(sk);
++ if (sk->sk_state != RXRPC_SERVER_LISTENING || !rx->backlog) {
++ ret = -ESHUTDOWN;
++ goto out;
++ }
++
++ b = rx->backlog;
++ ret = rxrpc_service_prealloc_one(rx, b, notify_rx, user_call_ID,
++ gfp, debug_id);
++
++out:
++ release_sock(sk);
++ return ret;
+ }
+ EXPORT_SYMBOL(rxrpc_kernel_charge_accept);
--- /dev/null
+From stable+bounces-288016-greg=kroah.com@vger.kernel.org Wed Jul 22 19:36:35 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 13:30:04 -0400
+Subject: rxrpc: Use irq-disabling spinlocks between app and I/O thread
+To: stable@vger.kernel.org
+Cc: David Howells <dhowells@redhat.com>, Marc Dionne <marc.dionne@auristor.com>, linux-afs@lists.infradead.org, Jakub Kicinski <kuba@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260722173006.2069029-2-sashal@kernel.org>
+
+From: David Howells <dhowells@redhat.com>
+
+[ Upstream commit a2ea9a9072607c2fd6442bd1ffb4dbdbf882aed7 ]
+
+Where a spinlock is used by both the application thread and the I/O thread,
+use irq-disabling locking so that an interrupt taken on the app thread
+doesn't also slow down the I/O thread.
+
+Signed-off-by: David Howells <dhowells@redhat.com>
+cc: Marc Dionne <marc.dionne@auristor.com>
+cc: linux-afs@lists.infradead.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Stable-dep-of: e66f8f32f501 ("rxrpc: Fix socket notification race")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/rxrpc/af_rxrpc.c | 4 ++--
+ net/rxrpc/call_accept.c | 20 ++++++++++----------
+ net/rxrpc/call_object.c | 14 +++++++-------
+ net/rxrpc/conn_client.c | 12 ++++++------
+ net/rxrpc/conn_event.c | 14 +++++++-------
+ net/rxrpc/conn_object.c | 8 ++++----
+ net/rxrpc/input.c | 5 +----
+ net/rxrpc/peer_event.c | 8 ++++----
+ net/rxrpc/peer_object.c | 1 +
+ net/rxrpc/recvmsg.c | 22 +++++++++++-----------
+ net/rxrpc/security.c | 4 ++--
+ 11 files changed, 55 insertions(+), 57 deletions(-)
+
+--- a/net/rxrpc/af_rxrpc.c
++++ b/net/rxrpc/af_rxrpc.c
+@@ -362,9 +362,9 @@ void rxrpc_kernel_shutdown_call(struct s
+
+ /* Make sure we're not going to call back into a kernel service */
+ if (call->notify_rx) {
+- spin_lock(&call->notify_lock);
++ spin_lock_irq(&call->notify_lock);
+ call->notify_rx = rxrpc_dummy_notify_rx;
+- spin_unlock(&call->notify_lock);
++ spin_unlock_irq(&call->notify_lock);
+ }
+ }
+ mutex_unlock(&call->user_mutex);
+--- a/net/rxrpc/call_accept.c
++++ b/net/rxrpc/call_accept.c
+@@ -189,8 +189,8 @@ void rxrpc_discard_prealloc(struct rxrpc
+ /* Make sure that there aren't any incoming calls in progress before we
+ * clear the preallocation buffers.
+ */
+- spin_lock(&rx->incoming_lock);
+- spin_unlock(&rx->incoming_lock);
++ spin_lock_irq(&rx->incoming_lock);
++ spin_unlock_irq(&rx->incoming_lock);
+
+ head = b->peer_backlog_head;
+ tail = b->peer_backlog_tail;
+@@ -349,7 +349,7 @@ bool rxrpc_new_incoming_call(struct rxrp
+ if (sp->hdr.type != RXRPC_PACKET_TYPE_DATA)
+ return rxrpc_protocol_error(skb, rxrpc_eproto_no_service_call);
+
+- read_lock(&local->services_lock);
++ read_lock_irq(&local->services_lock);
+
+ /* Weed out packets to services we're not offering. Packets that would
+ * begin a call are explicitly rejected and the rest are just
+@@ -406,12 +406,12 @@ bool rxrpc_new_incoming_call(struct rxrp
+ spin_unlock(&conn->state_lock);
+
+ spin_unlock(&rx->incoming_lock);
+- read_unlock(&local->services_lock);
++ read_unlock_irq(&local->services_lock);
+
+ if (hlist_unhashed(&call->error_link)) {
+- spin_lock(&call->peer->lock);
++ spin_lock_irq(&call->peer->lock);
+ hlist_add_head(&call->error_link, &call->peer->error_targets);
+- spin_unlock(&call->peer->lock);
++ spin_unlock_irq(&call->peer->lock);
+ }
+
+ _leave(" = %p{%d}", call, call->debug_id);
+@@ -420,20 +420,20 @@ bool rxrpc_new_incoming_call(struct rxrp
+ return true;
+
+ unsupported_service:
+- read_unlock(&local->services_lock);
++ read_unlock_irq(&local->services_lock);
+ return rxrpc_direct_abort(skb, rxrpc_abort_service_not_offered,
+ RX_INVALID_OPERATION, -EOPNOTSUPP);
+ unsupported_security:
+- read_unlock(&local->services_lock);
++ read_unlock_irq(&local->services_lock);
+ return rxrpc_direct_abort(skb, rxrpc_abort_service_not_offered,
+ RX_INVALID_OPERATION, -EKEYREJECTED);
+ no_call:
+ spin_unlock(&rx->incoming_lock);
+- read_unlock(&local->services_lock);
++ read_unlock_irq(&local->services_lock);
+ _leave(" = f [%u]", skb->mark);
+ return false;
+ discard:
+- read_unlock(&local->services_lock);
++ read_unlock_irq(&local->services_lock);
+ return true;
+ }
+
+--- a/net/rxrpc/call_object.c
++++ b/net/rxrpc/call_object.c
+@@ -48,7 +48,7 @@ void rxrpc_poke_call(struct rxrpc_call *
+ bool busy;
+
+ if (!test_bit(RXRPC_CALL_DISCONNECTED, &call->flags)) {
+- spin_lock_bh(&local->lock);
++ spin_lock_irq(&local->lock);
+ busy = !list_empty(&call->attend_link);
+ trace_rxrpc_poke_call(call, busy, what);
+ if (!busy && !rxrpc_try_get_call(call, rxrpc_call_get_poke))
+@@ -56,7 +56,7 @@ void rxrpc_poke_call(struct rxrpc_call *
+ if (!busy) {
+ list_add_tail(&call->attend_link, &local->call_attend_q);
+ }
+- spin_unlock_bh(&local->lock);
++ spin_unlock_irq(&local->lock);
+ if (!busy)
+ rxrpc_wake_up_io_thread(local);
+ }
+@@ -311,9 +311,9 @@ static int rxrpc_connect_call(struct rxr
+
+ trace_rxrpc_client(NULL, -1, rxrpc_client_queue_new_call);
+ rxrpc_get_call(call, rxrpc_call_get_io_thread);
+- spin_lock(&local->client_call_lock);
++ spin_lock_irq(&local->client_call_lock);
+ list_add_tail(&call->wait_link, &local->new_client_calls);
+- spin_unlock(&local->client_call_lock);
++ spin_unlock_irq(&local->client_call_lock);
+ rxrpc_wake_up_io_thread(local);
+ return 0;
+
+@@ -439,7 +439,7 @@ error_attached_to_socket:
+
+ /*
+ * Set up an incoming call. call->conn points to the connection.
+- * This is called in BH context and isn't allowed to fail.
++ * This is called with interrupts disabled and isn't allowed to fail.
+ */
+ void rxrpc_incoming_call(struct rxrpc_sock *rx,
+ struct rxrpc_call *call,
+@@ -563,7 +563,7 @@ void rxrpc_release_call(struct rxrpc_soc
+ rxrpc_put_call_slot(call);
+
+ /* Make sure we don't get any more notifications */
+- spin_lock(&rx->recvmsg_lock);
++ spin_lock_irq(&rx->recvmsg_lock);
+
+ if (!list_empty(&call->recvmsg_link)) {
+ _debug("unlinking once-pending call %p { e=%lx f=%lx }",
+@@ -576,7 +576,7 @@ void rxrpc_release_call(struct rxrpc_soc
+ call->recvmsg_link.next = NULL;
+ call->recvmsg_link.prev = NULL;
+
+- spin_unlock(&rx->recvmsg_lock);
++ spin_unlock_irq(&rx->recvmsg_lock);
+ if (put)
+ rxrpc_put_call(call, rxrpc_call_put_unnotify);
+
+--- a/net/rxrpc/conn_client.c
++++ b/net/rxrpc/conn_client.c
+@@ -505,10 +505,10 @@ void rxrpc_connect_client_calls(struct r
+ ) {
+ struct rxrpc_bundle *bundle = call->bundle;
+
+- spin_lock(&local->client_call_lock);
++ spin_lock_irq(&local->client_call_lock);
+ list_move_tail(&call->wait_link, &bundle->waiting_calls);
+ rxrpc_see_call(call, rxrpc_call_see_waiting_call);
+- spin_unlock(&local->client_call_lock);
++ spin_unlock_irq(&local->client_call_lock);
+
+ if (rxrpc_bundle_has_space(bundle))
+ rxrpc_activate_channels(bundle);
+@@ -536,9 +536,9 @@ void rxrpc_expose_client_call(struct rxr
+ set_bit(RXRPC_CONN_DONT_REUSE, &conn->flags);
+ trace_rxrpc_client(conn, channel, rxrpc_client_exposed);
+
+- spin_lock(&call->peer->lock);
++ spin_lock_irq(&call->peer->lock);
+ hlist_add_head(&call->error_link, &call->peer->error_targets);
+- spin_unlock(&call->peer->lock);
++ spin_unlock_irq(&call->peer->lock);
+ }
+ }
+
+@@ -579,9 +579,9 @@ void rxrpc_disconnect_client_call(struct
+ ASSERTCMP(call->call_id, ==, 0);
+ ASSERT(!test_bit(RXRPC_CALL_EXPOSED, &call->flags));
+ /* May still be on ->new_client_calls. */
+- spin_lock(&local->client_call_lock);
++ spin_lock_irq(&local->client_call_lock);
+ list_del_init(&call->wait_link);
+- spin_unlock(&local->client_call_lock);
++ spin_unlock_irq(&local->client_call_lock);
+ return;
+ }
+
+--- a/net/rxrpc/conn_event.c
++++ b/net/rxrpc/conn_event.c
+@@ -26,7 +26,7 @@ static bool rxrpc_set_conn_aborted(struc
+ bool aborted = false;
+
+ if (conn->state != RXRPC_CONN_ABORTED) {
+- spin_lock(&conn->state_lock);
++ spin_lock_irq(&conn->state_lock);
+ if (conn->state != RXRPC_CONN_ABORTED) {
+ conn->abort_code = abort_code;
+ conn->error = err;
+@@ -37,7 +37,7 @@ static bool rxrpc_set_conn_aborted(struc
+ set_bit(RXRPC_CONN_EV_ABORT_CALLS, &conn->events);
+ aborted = true;
+ }
+- spin_unlock(&conn->state_lock);
++ spin_unlock_irq(&conn->state_lock);
+ }
+
+ return aborted;
+@@ -268,12 +268,12 @@ static int rxrpc_process_event(struct rx
+ return conn->security->respond_to_challenge(conn, skb);
+
+ case RXRPC_PACKET_TYPE_RESPONSE:
+- spin_lock(&conn->state_lock);
++ spin_lock_irq(&conn->state_lock);
+ if (conn->state != RXRPC_CONN_SERVICE_CHALLENGING) {
+- spin_unlock(&conn->state_lock);
++ spin_unlock_irq(&conn->state_lock);
+ return 0;
+ }
+- spin_unlock(&conn->state_lock);
++ spin_unlock_irq(&conn->state_lock);
+
+ ret = rxrpc_verify_response(conn, skb);
+ if (ret < 0)
+@@ -284,12 +284,12 @@ static int rxrpc_process_event(struct rx
+ if (ret < 0)
+ return ret;
+
+- spin_lock(&conn->state_lock);
++ spin_lock_irq(&conn->state_lock);
+ if (conn->state == RXRPC_CONN_SERVICE_CHALLENGING) {
+ conn->state = RXRPC_CONN_SERVICE;
+ secured = true;
+ }
+- spin_unlock(&conn->state_lock);
++ spin_unlock_irq(&conn->state_lock);
+
+ if (secured) {
+ /* Offload call state flipping to the I/O thread. As
+--- a/net/rxrpc/conn_object.c
++++ b/net/rxrpc/conn_object.c
+@@ -31,13 +31,13 @@ void rxrpc_poke_conn(struct rxrpc_connec
+ if (WARN_ON_ONCE(!local))
+ return;
+
+- spin_lock_bh(&local->lock);
++ spin_lock_irq(&local->lock);
+ busy = !list_empty(&conn->attend_link);
+ if (!busy) {
+ rxrpc_get_connection(conn, why);
+ list_add_tail(&conn->attend_link, &local->conn_attend_q);
+ }
+- spin_unlock_bh(&local->lock);
++ spin_unlock_irq(&local->lock);
+ rxrpc_wake_up_io_thread(local);
+ }
+
+@@ -196,9 +196,9 @@ void rxrpc_disconnect_call(struct rxrpc_
+ call->peer->cong_ssthresh = call->cong_ssthresh;
+
+ if (!hlist_unhashed(&call->error_link)) {
+- spin_lock(&call->peer->lock);
++ spin_lock_irq(&call->peer->lock);
+ hlist_del_init(&call->error_link);
+- spin_unlock(&call->peer->lock);
++ spin_unlock_irq(&call->peer->lock);
+ }
+
+ if (rxrpc_is_client_call(call)) {
+--- a/net/rxrpc/input.c
++++ b/net/rxrpc/input.c
+@@ -366,7 +366,7 @@ static void rxrpc_input_queue_data(struc
+ struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
+ bool last = sp->hdr.flags & RXRPC_LAST_PACKET;
+
+- __skb_queue_tail(&call->recvmsg_queue, skb);
++ skb_queue_tail(&call->recvmsg_queue, skb);
+ rxrpc_input_update_ack_window(call, window, wtop);
+ trace_rxrpc_receive(call, last ? why + 1 : why, sp->hdr.serial, sp->hdr.seq);
+ if (last)
+@@ -443,7 +443,6 @@ static void rxrpc_input_data_one(struct
+
+ rxrpc_get_skb(skb, rxrpc_skb_get_to_recvmsg);
+
+- spin_lock(&call->recvmsg_queue.lock);
+ rxrpc_input_queue_data(call, skb, window, wtop, rxrpc_receive_queue);
+ *_notify = true;
+
+@@ -465,8 +464,6 @@ static void rxrpc_input_data_one(struct
+ rxrpc_receive_queue_oos);
+ }
+
+- spin_unlock(&call->recvmsg_queue.lock);
+-
+ call->ackr_sack_base = sack;
+ } else {
+ unsigned int slot;
+--- a/net/rxrpc/peer_event.c
++++ b/net/rxrpc/peer_event.c
+@@ -205,23 +205,23 @@ static void rxrpc_distribute_error(struc
+ struct rxrpc_call *call;
+ HLIST_HEAD(error_targets);
+
+- spin_lock(&peer->lock);
++ spin_lock_irq(&peer->lock);
+ hlist_move_list(&peer->error_targets, &error_targets);
+
+ while (!hlist_empty(&error_targets)) {
+ call = hlist_entry(error_targets.first,
+ struct rxrpc_call, error_link);
+ hlist_del_init(&call->error_link);
+- spin_unlock(&peer->lock);
++ spin_unlock_irq(&peer->lock);
+
+ rxrpc_see_call(call, rxrpc_call_see_distribute_error);
+ rxrpc_set_call_completion(call, compl, 0, -err);
+ rxrpc_input_call_event(call, skb);
+
+- spin_lock(&peer->lock);
++ spin_lock_irq(&peer->lock);
+ }
+
+- spin_unlock(&peer->lock);
++ spin_unlock_irq(&peer->lock);
+ }
+
+ /*
+--- a/net/rxrpc/peer_object.c
++++ b/net/rxrpc/peer_object.c
+@@ -302,6 +302,7 @@ static void rxrpc_free_peer(struct rxrpc
+ * Set up a new incoming peer. There shouldn't be any other matching peers
+ * since we've already done a search in the list from the non-reentrant context
+ * (the data_ready handler) that is the only place we can add new peers.
++ * Called with interrupts disabled.
+ */
+ void rxrpc_new_incoming_peer(struct rxrpc_local *local, struct rxrpc_peer *peer)
+ {
+--- a/net/rxrpc/recvmsg.c
++++ b/net/rxrpc/recvmsg.c
+@@ -36,16 +36,16 @@ void rxrpc_notify_socket(struct rxrpc_ca
+ sk = &rx->sk;
+ if (rx && sk->sk_state < RXRPC_CLOSE) {
+ if (call->notify_rx) {
+- spin_lock(&call->notify_lock);
++ spin_lock_irq(&call->notify_lock);
+ call->notify_rx(sk, call, call->user_call_ID);
+- spin_unlock(&call->notify_lock);
++ spin_unlock_irq(&call->notify_lock);
+ } else {
+- spin_lock(&rx->recvmsg_lock);
++ spin_lock_irq(&rx->recvmsg_lock);
+ if (list_empty(&call->recvmsg_link)) {
+ rxrpc_get_call(call, rxrpc_call_get_notify_socket);
+ list_add_tail(&call->recvmsg_link, &rx->recvmsg_q);
+ }
+- spin_unlock(&rx->recvmsg_lock);
++ spin_unlock_irq(&rx->recvmsg_lock);
+
+ if (!sock_flag(sk, SOCK_DEAD)) {
+ _debug("call %ps", sk->sk_data_ready);
+@@ -379,14 +379,14 @@ try_again:
+ * We also want to weed out calls that got requeued whilst we were
+ * shovelling data out.
+ */
+- spin_lock(&rx->recvmsg_lock);
++ spin_lock_irq(&rx->recvmsg_lock);
+ l = rx->recvmsg_q.next;
+ call = list_entry(l, struct rxrpc_call, recvmsg_link);
+
+ if (!rxrpc_call_is_complete(call) &&
+ skb_queue_empty(&call->recvmsg_queue)) {
+ list_del_init(&call->recvmsg_link);
+- spin_unlock(&rx->recvmsg_lock);
++ spin_unlock_irq(&rx->recvmsg_lock);
+ release_sock(&rx->sk);
+ trace_rxrpc_recvmsg(call->debug_id, rxrpc_recvmsg_unqueue, 0);
+ rxrpc_put_call(call, rxrpc_call_put_recvmsg);
+@@ -407,7 +407,7 @@ try_again:
+ list_del_init(&call->recvmsg_link);
+ else
+ rxrpc_get_call(call, rxrpc_call_get_recvmsg);
+- spin_unlock(&rx->recvmsg_lock);
++ spin_unlock_irq(&rx->recvmsg_lock);
+
+ call_debug_id = call->debug_id;
+ trace_rxrpc_recvmsg(call_debug_id, rxrpc_recvmsg_dequeue, 0);
+@@ -502,17 +502,17 @@ error_unlock_call:
+
+ error_requeue_call:
+ if (!(flags & MSG_PEEK)) {
+- spin_lock(&rx->recvmsg_lock);
++ spin_lock_irq(&rx->recvmsg_lock);
+ if (list_empty(&call->recvmsg_link)) {
+ list_add(&call->recvmsg_link, &rx->recvmsg_q);
+ rxrpc_see_call(call, rxrpc_call_see_recvmsg_requeue);
+- spin_unlock(&rx->recvmsg_lock);
++ spin_unlock_irq(&rx->recvmsg_lock);
+ } else if (list_is_first(&call->recvmsg_link, &rx->recvmsg_q)) {
+- spin_unlock(&rx->recvmsg_lock);
++ spin_unlock_irq(&rx->recvmsg_lock);
+ rxrpc_put_call(call, rxrpc_call_see_recvmsg_requeue_first);
+ } else {
+ list_move(&call->recvmsg_link, &rx->recvmsg_q);
+- spin_unlock(&rx->recvmsg_lock);
++ spin_unlock_irq(&rx->recvmsg_lock);
+ rxrpc_put_call(call, rxrpc_call_see_recvmsg_requeue_move);
+ }
+ trace_rxrpc_recvmsg(call_debug_id, rxrpc_recvmsg_requeue, 0);
+--- a/net/rxrpc/security.c
++++ b/net/rxrpc/security.c
+@@ -114,10 +114,10 @@ found:
+ if (conn->state == RXRPC_CONN_CLIENT_UNSECURED) {
+ ret = conn->security->init_connection_security(conn, token);
+ if (ret == 0) {
+- spin_lock(&conn->state_lock);
++ spin_lock_irq(&conn->state_lock);
+ if (conn->state == RXRPC_CONN_CLIENT_UNSECURED)
+ conn->state = RXRPC_CONN_CLIENT;
+- spin_unlock(&conn->state_lock);
++ spin_unlock_irq(&conn->state_lock);
+ }
+ }
+ mutex_unlock(&conn->security_lock);
--- /dev/null
+From stable+bounces-288351-greg=kroah.com@vger.kernel.org Thu Jul 23 16:21:22 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 23 Jul 2026 10:19:20 -0400
+Subject: selinux: avoid sk_socket dereference in selinux_sctp_bind_connect()
+To: stable@vger.kernel.org
+Cc: Tristan Madani <tristan@talencesecurity.com>, Stephen Smalley <stephen.smalley.work@gmail.com>, Paul Moore <paul@paul-moore.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260723141920.3218519-2-sashal@kernel.org>
+
+From: Tristan Madani <tristan@talencesecurity.com>
+
+[ Upstream commit 56acfeb10019e200ab6787d01f8d7cbe0f01526f ]
+
+selinux_sctp_bind_connect() dereferences sk->sk_socket to pass a
+struct socket * to selinux_socket_bind() and
+selinux_socket_connect_helper(). However, when the hook is invoked
+from the ASCONF softirq path (sctp_process_asconf), there is no file
+reference guaranteeing that sk->sk_socket is non-NULL. The setsockopt
+callers (bindx, connectx, set_primary, sendmsg connect) hold a file
+reference and are not affected.
+
+Both selinux_socket_bind() and selinux_socket_connect_helper()
+immediately resolve sock->sk, never using the struct socket * for
+anything else. Refactor the inner logic into helpers that take a
+struct sock * directly so that selinux_sctp_bind_connect() never needs
+to touch sk->sk_socket at all.
+
+Cc: stable@vger.kernel.org
+Fixes: d452930fd3b9 ("selinux: Add SCTP support")
+Suggested-by: Stephen Smalley <stephen.smalley.work@gmail.com>
+Signed-off-by: Tristan Madani <tristan@talencesecurity.com>
+Reviewed-by: Stephen Smalley <stephen.smalley.work@gmail.com>
+Tested-by: Stephen Smalley <stephen.smalley.work@gmail.com>
+Signed-off-by: Paul Moore <paul@paul-moore.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ security/selinux/hooks.c | 19 ++++++++++---------
+ 1 file changed, 10 insertions(+), 9 deletions(-)
+
+--- a/security/selinux/hooks.c
++++ b/security/selinux/hooks.c
+@@ -4777,9 +4777,8 @@ static int selinux_socket_socketpair(str
+ Need to determine whether we should perform a name_bind
+ permission check between the socket and the port number. */
+
+-static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen)
++static int __selinux_socket_bind(struct sock *sk, struct sockaddr *address, int addrlen)
+ {
+- struct sock *sk = sock->sk;
+ struct sk_security_struct *sksec = selinux_sock(sk);
+ u16 family;
+ int err;
+@@ -4913,13 +4912,17 @@ err_af:
+ return -EAFNOSUPPORT;
+ }
+
++static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen)
++{
++ return __selinux_socket_bind(sock->sk, address, addrlen);
++}
++
+ /* This supports connect(2) and SCTP connect services such as sctp_connectx(3)
+ * and sctp_sendmsg(3) as described in Documentation/security/SCTP.rst
+ */
+-static int selinux_socket_connect_helper(struct socket *sock,
++static int selinux_socket_connect_helper(struct sock *sk,
+ struct sockaddr *address, int addrlen)
+ {
+- struct sock *sk = sock->sk;
+ struct sk_security_struct *sksec = selinux_sock(sk);
+ int err;
+
+@@ -5012,7 +5015,7 @@ static int selinux_socket_connect(struct
+ int err;
+ struct sock *sk = sock->sk;
+
+- err = selinux_socket_connect_helper(sock, address, addrlen);
++ err = selinux_socket_connect_helper(sk, address, addrlen);
+ if (err)
+ return err;
+
+@@ -5512,13 +5515,11 @@ static int selinux_sctp_bind_connect(str
+ int len, err = 0, walk_size = 0;
+ void *addr_buf;
+ struct sockaddr *addr;
+- struct socket *sock;
+
+ if (!selinux_policycap_extsockclass())
+ return 0;
+
+ /* Process one or more addresses that may be IPv4 or IPv6 */
+- sock = sk->sk_socket;
+ addr_buf = address;
+
+ while (walk_size < addrlen) {
+@@ -5547,14 +5548,14 @@ static int selinux_sctp_bind_connect(str
+ case SCTP_PRIMARY_ADDR:
+ case SCTP_SET_PEER_PRIMARY_ADDR:
+ case SCTP_SOCKOPT_BINDX_ADD:
+- err = selinux_socket_bind(sock, addr, len);
++ err = __selinux_socket_bind(sk, addr, len);
+ break;
+ /* Connect checks */
+ case SCTP_SOCKOPT_CONNECTX:
+ case SCTP_PARAM_SET_PRIMARY:
+ case SCTP_PARAM_ADD_IP:
+ case SCTP_SENDMSG_CONNECT:
+- err = selinux_socket_connect_helper(sock, addr, len);
++ err = selinux_socket_connect_helper(sk, addr, len);
+ if (err)
+ return err;
+
--- /dev/null
+From sashal@kernel.org Wed Jul 22 15:54:32 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 09:54:28 -0400
+Subject: serial: max310x: implement gpio_chip::get_direction()
+To: stable@vger.kernel.org
+Cc: Tapio Reijonen <tapio.reijonen@vaisala.com>, stable <stable@kernel.org>, Linus Walleij <linusw@kernel.org>, Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>, Hugo Villeneuve <hvilleneuve@dimonoff.com>, Greg Kroah-Hartman <gregkh@linuxfoundation.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260722135428.1365359-2-sashal@kernel.org>
+
+From: Tapio Reijonen <tapio.reijonen@vaisala.com>
+
+[ Upstream commit a483b1a91b33b7533280e7c3efd2bc1275caef18 ]
+
+It's strongly recommended for GPIO drivers to always implement the
+.get_direction() callback - even when the direction is tracked in
+software. The GPIO core emits a warning when the callback is missing
+and a user reads the direction of a line, e.g. via
+/sys/kernel/debug/gpio.
+
+The MAX310X keeps the GPIO direction in the GPIOCFG register (a set bit
+selects output), which the existing direction_input/output callbacks
+already program, so the current direction can be read back directly.
+
+Fixes: f65444187a66 ("serial: New serial driver MAX310X")
+Cc: stable <stable@kernel.org>
+Signed-off-by: Tapio Reijonen <tapio.reijonen@vaisala.com>
+Reviewed-by: Linus Walleij <linusw@kernel.org>
+Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
+Reviewed-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
+Link: https://patch.msgid.link/20260615-b4-serial-max310x-gpio-get-direction-v2-1-4704ba2b181a@vaisala.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/tty/serial/max310x.c | 12 ++++++++++++
+ 1 file changed, 12 insertions(+)
+
+--- a/drivers/tty/serial/max310x.c
++++ b/drivers/tty/serial/max310x.c
+@@ -1229,6 +1229,17 @@ static void max310x_gpio_set(struct gpio
+ value ? 1 << (offset % 4) : 0);
+ }
+
++static int max310x_gpio_get_direction(struct gpio_chip *chip, unsigned int offset)
++{
++ struct max310x_port *s = gpiochip_get_data(chip);
++ struct uart_port *port = &s->p[offset / 4].port;
++ unsigned int val;
++
++ val = max310x_port_read(port, MAX310X_GPIOCFG_REG);
++
++ return val & BIT(offset % 4) ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN;
++}
++
+ static int max310x_gpio_direction_input(struct gpio_chip *chip, unsigned int offset)
+ {
+ struct max310x_port *s = gpiochip_get_data(chip);
+@@ -1442,6 +1453,7 @@ static int max310x_probe(struct device *
+ s->gpio.owner = THIS_MODULE;
+ s->gpio.parent = dev;
+ s->gpio.label = devtype->name;
++ s->gpio.get_direction = max310x_gpio_get_direction;
+ s->gpio.direction_input = max310x_gpio_direction_input;
+ s->gpio.get = max310x_gpio_get;
+ s->gpio.direction_output= max310x_gpio_direction_output;
--- /dev/null
+From sashal@kernel.org Wed Jul 22 15:54:30 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 09:54:27 -0400
+Subject: serial: max310x: replace bare use of 'unsigned' with 'unsigned int' (checkpatch)
+To: stable@vger.kernel.org
+Cc: Hugo Villeneuve <hvilleneuve@dimonoff.com>, Andy Shevchenko <andy.shevchenko@gmail.com>, Greg Kroah-Hartman <gregkh@linuxfoundation.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260722135428.1365359-1-sashal@kernel.org>
+
+From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
+
+[ Upstream commit 79b69eb09cf5b6a77e621b2838b7e0d38113debb ]
+
+Fixes the following checkpatch warnings:
+
+ WARNING: Prefer 'unsigned int' to bare use of 'unsigned'
+
+With this change, the affected functions now match the prototypes in
+struct gpio_chip.
+
+Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
+Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
+Link: https://lore.kernel.org/r/20240118152213.2644269-16-hugo@hugovil.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Stable-dep-of: a483b1a91b33 ("serial: max310x: implement gpio_chip::get_direction()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/tty/serial/max310x.c | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+--- a/drivers/tty/serial/max310x.c
++++ b/drivers/tty/serial/max310x.c
+@@ -1209,7 +1209,7 @@ static int __maybe_unused max310x_resume
+ static SIMPLE_DEV_PM_OPS(max310x_pm_ops, max310x_suspend, max310x_resume);
+
+ #ifdef CONFIG_GPIOLIB
+-static int max310x_gpio_get(struct gpio_chip *chip, unsigned offset)
++static int max310x_gpio_get(struct gpio_chip *chip, unsigned int offset)
+ {
+ unsigned int val;
+ struct max310x_port *s = gpiochip_get_data(chip);
+@@ -1220,7 +1220,7 @@ static int max310x_gpio_get(struct gpio_
+ return !!((val >> 4) & (1 << (offset % 4)));
+ }
+
+-static void max310x_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
++static void max310x_gpio_set(struct gpio_chip *chip, unsigned int offset, int value)
+ {
+ struct max310x_port *s = gpiochip_get_data(chip);
+ struct uart_port *port = &s->p[offset / 4].port;
+@@ -1229,7 +1229,7 @@ static void max310x_gpio_set(struct gpio
+ value ? 1 << (offset % 4) : 0);
+ }
+
+-static int max310x_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
++static int max310x_gpio_direction_input(struct gpio_chip *chip, unsigned int offset)
+ {
+ struct max310x_port *s = gpiochip_get_data(chip);
+ struct uart_port *port = &s->p[offset / 4].port;
+@@ -1240,7 +1240,7 @@ static int max310x_gpio_direction_input(
+ }
+
+ static int max310x_gpio_direction_output(struct gpio_chip *chip,
+- unsigned offset, int value)
++ unsigned int offset, int value)
+ {
+ struct max310x_port *s = gpiochip_get_data(chip);
+ struct uart_port *port = &s->p[offset / 4].port;
io_uring-rw-fix-missing-erestartsys-conversion-in-re.patch
net-pcs-xpcs-fix-sgmii-state-reading.patch
bpf-drop-bpf_lsm_getselfattr-from-hook-list.patch
+bpf-reject-bpf_map_type_inode_storage-creation-if-bpf-lsm-is-uninitialized.patch
+iommu-amd-don-t-split-flush-for-amd_iommu_domain_flush_all.patch
+udmabuf-do-not-create-malformed-scatterlists.patch
+dma-buf-udmabuf-skip-redundant-cpu-sync-to-fix-cacheline-eexist-warning.patch
+fpga-dfl-afu-validate-dma-mapping-length-in-afu_dma_map_region.patch
+i2c-davinci-unregister-cpufreq-notifier-on-probe-failure.patch
+vfs-audit-introduce-kern_path_parent-for-audit.patch
+audit-widen-ino-fields-to-u64.patch
+audit-use-unsigned-int-instead-of-unsigned.patch
+audit-fix-recursive-locking-deadlock-in-audit_dupe_exe.patch
+i2c-i801-fix-hardware-state-machine-corruption-in-error-path.patch
+alsa-hda-conexant-remove-mic-bias-threshold-override.patch
+alsa-hda-fix-cached-processing-coefficient-verbs.patch
+serial-max310x-replace-bare-use-of-unsigned-with-unsigned-int-checkpatch.patch
+serial-max310x-implement-gpio_chip-get_direction.patch
+afs-annotate-struct-afs_addr_list-with-__counted_by.patch
+afs-turn-the-afs_addr_list-address-array-into-an-array-of-structs.patch
+rxrpc-pull-out-certain-app-callback-funcs-into-an-ops-table.patch
+rxrpc-serialize-kernel-accept-preallocation-with-socket-teardown.patch
+fbcon-rename-struct-fbcon_ops-to-struct-fbcon_par.patch
+fbcon-use-correct-type-for-vc_resize-return-value.patch
+tipc-restrict-socket-queue-dumps-in-enqueue-tracepoints.patch
+rxrpc-don-t-need-barrier-for-tx_bottom-and-acks_hard_ack.patch
+rxrpc-use-irq-disabling-spinlocks-between-app-and-i-o-thread.patch
+rxrpc-fix-notification-vs-call-release-vs-recvmsg.patch
+rxrpc-fix-socket-notification-race.patch
+vduse-use-fixed-4kb-bounce-pages-for-non-4kb-page-size.patch
+vduse-remove-unused-vaddr-parameter-of-vduse_domain_free_coherent.patch
+vduse-take-out-allocations-from-vduse_dev_alloc_coherent.patch
+vduse-avoid-leaking-information-to-userspace.patch
+octeontx2-annotate-mmio-regions-as-__iomem.patch
+octeontx2-pf-clear-stale-mailbox-irq-state-before-request_irq.patch
+octeontx2-vf-clear-stale-mailbox-irq-state-before-request_irq.patch
+fbdev-efifb-replace-references-to-global-screen_info-by-local-pointer.patch
+fbdev-efifb-fix-memory-leak-in-efifb_probe.patch
+asoc-mediatek-mt8195-remove-afe-dai-component-and-rework-codec-link.patch
+asoc-mediatek-mt8192-afe-pcm-convert-to-devm_pm_runtime_enable.patch
+asoc-mediatek-mt8192-afe-pcm-simplify-with-dev_err_probe.patch
+asoc-mediatek-use-common-mtk_afe_pcm_platform-with-common-probe-cb.patch
+asoc-mediatek-mt8192-afe-pcm-simplify-probe-with-local-dev-variable.patch
+asoc-mediatek-mt8192-check-runtime-resume-during-probe.patch
+asoc-mediatek-mt8183-check-runtime-resume-during-probe.patch
+netfilter-nft_set_pipapo-use-gfp_kernel-for-insertions.patch
+netfilter-nft_set_pipapo-move-prove_locking-helper-around.patch
+netfilter-nft_set_pipapo-make-pipapo_clone-helper-return-null.patch
+netfilter-nft_set_pipapo-prepare-walk-function-for-on-demand-clone.patch
+netfilter-nft_set_pipapo-merge-deactivate-helper-into-caller.patch
+netfilter-nft_set_pipapo-prepare-pipapo_get-helper-for-on-demand-clone.patch
+netfilter-nft_set_pipapo-move-cloning-of-match-info-to-insert-removal-path.patch
+netfilter-nft_set_pipapo-don-t-leak-bad-clone-into-future-transaction.patch
+lsm-infrastructure-management-of-the-sock-security.patch
+selinux-avoid-sk_socket-dereference-in-selinux_sctp_bind_connect.patch
+netfilter-nf_conntrack_sip-remove-net-variable-shadowing.patch
+netfilter-nf_conntrack_sip-validate-skb_dst-before-accessing-it.patch
+netfilter-nf_tables-remove-unused-nft_reduce_is_readonly.patch
+netfilter-nf_tables-remove-register-tracking-infrastructure.patch
+netfilter-nft_fib-reject-fib-expression-on-the-netdev-egress-hook.patch
+nfsd-pass-nfsd_file-to-nfsd_iter_read.patch
+sunrpc-allocate-a-separate-bvec-array-for-socket-sends.patch
+sunrpc-add-helpers-to-convert-xdr_buf-byte-ranges-to-scatterlists.patch
+sunrpc-return-an-error-from-xdr_buf_to_bvec-on-overflow.patch
+mm-mm_init-fix-pageblock-migratetype-for-zone_device-compound-pages.patch
+mtd-maps-vmu-flash-fix-fault-in-unaligned-fixup.patch
+mtd-rawnand-add-a-helper-for-calculating-a-page-index.patch
+mtd-rawnand-ensure-all-continuous-terms-are-always-in-sync.patch
+mtd-rawnand-pause-continuous-reads-at-block-boundaries.patch
+taskstats-fill_stats_for_tgid-use-for_each_thread.patch
+taskstats-retain-dead-thread-stats-in-tgid-queries.patch
+dma-dw-edma-fix-build-warning-in-dw_edma_pcie_probe.patch
+dmaengine-dw-edma-fix-confusing-cleanup.h-syntax.patch
+dmaengine-dw-edma-pcie-reject-devices-without-driver-data.patch
+thunderbolt-handle-lane-bonding-of-gen-4-xdomain-links-properly.patch
+thunderbolt-remove-usage-of-the-deprecated-ida_simple_xx-api.patch
+thunderbolt-update-property.c-function-documentation.patch
+thunderbolt-keep-xdomain-reference-during-the-lifetime-of-a-service.patch
+thunderbolt-remove-service-debugfs-entries-during-unregister.patch
+thunderbolt-remove-xdomain-from-the-bus-without-holding-tb-lock.patch
+thunderbolt-prevent-xdomain-delayed-work-use-after-free-on-disconnect.patch
+platform-x86-dell-smbios-move-request-functions-for-reuse.patch
+platform-x86-dell-laptop-fix-missing-cleanups-in-init-error-path.patch
+i2c-imx-separate-atomic-dma-and-non-dma-use-case.patch
+i2c-imx-fix-locked-bus-on-smbus-block-read-of-0-atomic.patch
+nfs-remove-dead-code-for-the-old-swap-over-nfs-implementation.patch
+nfs-charge-unstable-writes-by-request-size-not-folio-size.patch
+ovl-use-linked-upper-dentry-in-copy-up-tmpfile.patch
+dm-verity-avoid-double-increment-of-use_bh_wq_enabled.patch
+dm-verity-make-error-counter-atomic.patch
+firmware_loader-introduce-__free-cleanup-hanler.patch
+input-ims-pcu-fix-firmware-leak-in-async-update.patch
+workqueue-factor-out-init_cpu_worker_pool.patch
+workqueue-add-system_percpu_wq-and-system_dfl_wq.patch
+tracing-user_events-fix-use-after-free-in-user_event_mm_dup.patch
+wifi-libertas_tf-fix-use-after-free-in-lbtf_free_adapter.patch
+mmc-vub300-fix-use-after-free-on-disconnect.patch
+mmc-vub300-rename-probe-error-labels.patch
+mmc-vub300-fix-use-after-free-on-probe-failure.patch
+locking-rt-fix-the-incorrect-rcu-protection-in-rt_spin_unlock.patch
+net-mana-validate-the-packet-length-reported-by-the-nic.patch
+pinctrl-remove-pinctrl_gpio_direction_output.patch
+gpio-tegra-do-not-call-pinctrl-for-gpio-direction.patch
+net-ip6_tunnel-require-cap_net_admin-in-the-device-netns-for-changelink.patch
+gpio-mt7621-avoid-corruption-of-shared-interrupt-trigger-state.patch
+octeontx2-af-cn10k-restrict-vf-lmtline-sharing-to-its-own-pf.patch
+bootconfig-do-not-put-quotes-on-cmdline-items-unless-necessary.patch
+bootconfig-move-xbc_snprint_cmdline-to-lib-bootconfig.c.patch
+bootconfig-fix-null-pointer-arithmetic-in-xbc_snprint_cmdline.patch
+ipmi-fix-refcount-leak-in-i_ipmi_request.patch
+octeontx2-pf-fix-sqb-pointer-leak-on-init-failure.patch
+ata-libata-core-reject-an-invalid-concurrent-positioning-ranges-count.patch
+net-macb-drop-in-flight-tx-skbs-on-close.patch
+net-ipa-fix-smem-state-handle-leaks-in-smp2p-init.patch
+fs-resctrl-fix-double-add-of-pseudo-locked-region-s-rmid-to-free-list.patch
+kvm-introduce-vcpu-wants_to_run.patch
+kvm-x86-only-reset-tsc-deadline-timer-in-apic_timer_expired-on-kvm_run.patch
--- /dev/null
+From stable+bounces-288613-greg=kroah.com@vger.kernel.org Fri Jul 24 03:28:23 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 23 Jul 2026 21:25:13 -0400
+Subject: SUNRPC: Add helpers to convert xdr_buf byte ranges to scatterlists
+To: stable@vger.kernel.org
+Cc: Chuck Lever <chuck.lever@oracle.com>, Jeff Layton <jlayton@kernel.org>, Anna Schumaker <anna.schumaker@hammerspace.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260724012515.3833219-3-sashal@kernel.org>
+
+From: Chuck Lever <chuck.lever@oracle.com>
+
+[ Upstream commit e9be933959b581effd426f93b86654f5fbf0c574 ]
+
+The crypto/krb5 library accepts data in scatterlist form, but
+the GSS-API layer presents RPC payloads as struct xdr_buf.
+Bridge that gap with a pair of helper functions:
+
+ xdr_buf_to_sg() - populate a caller-supplied scatterlist
+ array from a byte range
+ xdr_buf_to_sg_alloc() - populate a caller-supplied inline
+ scatterlist, chaining to a heap-
+ allocated overflow for large payloads
+
+The inline array (typically stack-allocated at eight entries)
+covers the common case of small RPCs with no heap allocation
+on the encrypt/decrypt path. Only buffers spanning many pages
+incur a kmalloc for the chained extension.
+
+The segment-walking logic follows the same head, page array,
+tail traversal as xdr_process_buf(), but populates a
+scatterlist directly rather than invoking a per-segment
+callback. sg_next() traversal makes the walker safe for
+chained scatterlists. Once subsequent patches reroute all
+per-message crypto operations through crypto/krb5,
+xdr_process_buf() loses its last callers and is removed.
+
+Assisted-by: Claude:claude-opus-4-6
+Reviewed-by: Jeff Layton <jlayton@kernel.org>
+Acked-by: Anna Schumaker <anna.schumaker@hammerspace.com>
+Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
+Stable-dep-of: 18c1cc698861 ("SUNRPC: Return an error from xdr_buf_to_bvec() on overflow")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ include/linux/sunrpc/xdr.h | 15 +++
+ net/sunrpc/xdr.c | 199 +++++++++++++++++++++++++++++++++++++++++++++
+ 2 files changed, 214 insertions(+)
+
+--- a/include/linux/sunrpc/xdr.h
++++ b/include/linux/sunrpc/xdr.h
+@@ -141,6 +141,21 @@ int xdr_alloc_bvec(struct xdr_buf *buf,
+ void xdr_free_bvec(struct xdr_buf *buf);
+ unsigned int xdr_buf_to_bvec(struct bio_vec *bvec, unsigned int bvec_size,
+ const struct xdr_buf *xdr);
++int xdr_buf_to_sg(const struct xdr_buf *buf, unsigned int offset,
++ unsigned int len, struct scatterlist *sg, unsigned int nsg);
++int xdr_buf_to_sg_alloc(const struct xdr_buf *buf, unsigned int offset,
++ unsigned int len, struct scatterlist *sg_head,
++ unsigned int sg_head_nents,
++ struct scatterlist **sg_overflow, gfp_t gfp);
++
++/*
++ * Inline scatterlist entries for xdr_buf_to_sg_alloc(). Sized to cover the
++ * head kvec, tail kvec, and a few page fragments without any heap allocation.
++ */
++enum {
++ XDR_BUF_TO_SG_NENTS = 8,
++};
++
+
+ static inline __be32 *xdr_encode_array(__be32 *p, const void *s, unsigned int len)
+ {
+--- a/net/sunrpc/xdr.c
++++ b/net/sunrpc/xdr.c
+@@ -219,6 +219,205 @@ bvec_overflow:
+ }
+
+ /**
++ * xdr_buf_to_sg - Populate a scatterlist from an xdr_buf range
++ * @buf: xdr_buf to map
++ * @offset: starting byte offset within @buf
++ * @len: number of bytes to cover
++ * @sg: scatterlist array initialized with sg_init_table()
++ * @nsg: number of entries available in @sg
++ *
++ * @sg is traversed with sg_next(), so callers may pass a list
++ * assembled with sg_chain().
++ *
++ * Return: on success, the number of scatterlist entries used; the
++ * last used entry is marked with sg_mark_end(). On failure, a
++ * negative errno.
++ */
++int xdr_buf_to_sg(const struct xdr_buf *buf, unsigned int offset,
++ unsigned int len, struct scatterlist *sg, unsigned int nsg)
++{
++ unsigned int page_len, thislen, page_offset;
++ struct scatterlist *cur = sg, *prev = NULL;
++ int nents = 0;
++ int i;
++
++ if (len == 0)
++ return 0;
++
++ if (offset >= buf->head[0].iov_len) {
++ offset -= buf->head[0].iov_len;
++ } else {
++ thislen = min_t(unsigned int,
++ buf->head[0].iov_len - offset, len);
++ if (nents >= nsg)
++ return -ENOSPC;
++ sg_set_buf(cur, buf->head[0].iov_base + offset,
++ thislen);
++ prev = cur;
++ cur = sg_next(cur);
++ nents++;
++ len -= thislen;
++ offset = 0;
++ }
++ if (len == 0)
++ goto done;
++
++ if (offset >= buf->page_len) {
++ offset -= buf->page_len;
++ } else {
++ page_len = min(buf->page_len - offset, len);
++ len -= page_len;
++ page_offset = (offset + buf->page_base) & (PAGE_SIZE - 1);
++ i = (offset + buf->page_base) >> PAGE_SHIFT;
++ thislen = PAGE_SIZE - page_offset;
++ do {
++ if (thislen > page_len)
++ thislen = page_len;
++ if (nents >= nsg)
++ return -ENOSPC;
++ sg_set_page(cur, buf->pages[i],
++ thislen, page_offset);
++ prev = cur;
++ cur = sg_next(cur);
++ nents++;
++ page_len -= thislen;
++ i++;
++ page_offset = 0;
++ thislen = PAGE_SIZE;
++ } while (page_len != 0);
++ offset = 0;
++ }
++ if (len == 0)
++ goto done;
++
++ if (offset < buf->tail[0].iov_len) {
++ thislen = min_t(unsigned int,
++ buf->tail[0].iov_len - offset, len);
++ if (nents >= nsg)
++ return -ENOSPC;
++ sg_set_buf(cur, buf->tail[0].iov_base + offset,
++ thislen);
++ prev = cur;
++ nents++;
++ len -= thislen;
++ }
++ if (len != 0)
++ return -EINVAL;
++
++done:
++ if (prev)
++ sg_mark_end(prev);
++ return nents;
++}
++EXPORT_SYMBOL_GPL(xdr_buf_to_sg);
++
++/*
++ * Count the scatterlist entries needed to cover [offset, offset + len)
++ * within @buf. Mirrors the walk in xdr_buf_to_sg() so the caller can
++ * size an allocation that matches the requested sub-range rather than
++ * the full xdr_buf.
++ */
++static unsigned int xdr_buf_sg_nents(const struct xdr_buf *buf,
++ unsigned int offset, unsigned int len)
++{
++ unsigned int nsg = 0, thislen, page_offset;
++
++ if (len == 0)
++ return 0;
++
++ if (offset < buf->head[0].iov_len) {
++ thislen = min_t(unsigned int,
++ buf->head[0].iov_len - offset, len);
++ nsg++;
++ len -= thislen;
++ offset = 0;
++ } else {
++ offset -= buf->head[0].iov_len;
++ }
++ if (len == 0)
++ return nsg;
++
++ if (offset < buf->page_len) {
++ thislen = min(buf->page_len - offset, len);
++ page_offset = (offset + buf->page_base) & (PAGE_SIZE - 1);
++ nsg += DIV_ROUND_UP(page_offset + thislen, PAGE_SIZE);
++ len -= thislen;
++ offset = 0;
++ } else {
++ offset -= buf->page_len;
++ }
++ if (len == 0)
++ return nsg;
++
++ if (offset < buf->tail[0].iov_len)
++ nsg++;
++ return nsg;
++}
++
++/**
++ * xdr_buf_to_sg_alloc - Populate a scatterlist for an xdr_buf range
++ * @buf: xdr_buf to map
++ * @offset: starting byte offset within @buf
++ * @len: number of bytes to cover
++ * @sg_head: caller-provided scatterlist array (typically stack-allocated)
++ * @sg_head_nents: number of entries in @sg_head
++ * @sg_overflow: OUT: chained extension, or NULL when @sg_head sufficed
++ * @gfp: memory allocation flags for overflow
++ *
++ * Populates @sg_head directly when the xdr_buf fits. When more
++ * entries are needed, an overflow scatterlist is allocated and
++ * chained from @sg_head so that the result is traversable with
++ * sg_next().
++ *
++ * Return: on success, the number of populated scatterlist entries
++ * (counting only data entries, not chain entries). @sg_head is
++ * the head of the resulting list. Caller must kfree @sg_overflow
++ * when done. On failure, a negative errno.
++ */
++int xdr_buf_to_sg_alloc(const struct xdr_buf *buf, unsigned int offset,
++ unsigned int len, struct scatterlist *sg_head,
++ unsigned int sg_head_nents,
++ struct scatterlist **sg_overflow, gfp_t gfp)
++{
++ unsigned int nsg;
++ int ret;
++
++ *sg_overflow = NULL;
++ if (len == 0)
++ return 0;
++
++ nsg = xdr_buf_sg_nents(buf, offset, len);
++ if (nsg == 0)
++ return -EINVAL;
++
++ if (nsg <= sg_head_nents) {
++ sg_init_table(sg_head, nsg);
++ } else {
++ /* +1 replaces the slot sg_chain() consumes as the link. */
++ unsigned int overflow_nents = nsg - sg_head_nents + 1;
++ struct scatterlist *overflow;
++
++ overflow = kmalloc_array(overflow_nents, sizeof(*overflow),
++ gfp);
++ if (!overflow)
++ return -ENOMEM;
++
++ sg_init_table(sg_head, sg_head_nents);
++ sg_init_table(overflow, overflow_nents);
++ sg_chain(sg_head, sg_head_nents, overflow);
++ *sg_overflow = overflow;
++ }
++
++ ret = xdr_buf_to_sg(buf, offset, len, sg_head, nsg);
++ if (ret < 0) {
++ kfree(*sg_overflow);
++ *sg_overflow = NULL;
++ }
++ return ret;
++}
++EXPORT_SYMBOL_GPL(xdr_buf_to_sg_alloc);
++
++/**
+ * xdr_inline_pages - Prepare receive buffer for a large reply
+ * @xdr: xdr_buf into which reply will be placed
+ * @offset: expected offset where data payload will start, in bytes
--- /dev/null
+From stable+bounces-288612-greg=kroah.com@vger.kernel.org Fri Jul 24 03:28:18 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 23 Jul 2026 21:25:12 -0400
+Subject: sunrpc: allocate a separate bvec array for socket sends
+To: stable@vger.kernel.org
+Cc: Jeff Layton <jlayton@kernel.org>, NeilBrown <neil@brown.name>, Chuck Lever <chuck.lever@oracle.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260724012515.3833219-2-sashal@kernel.org>
+
+From: Jeff Layton <jlayton@kernel.org>
+
+[ Upstream commit 6b3b697d65d46a0f640216a3f6c72856c159c567 ]
+
+svc_tcp_sendmsg() calls xdr_buf_to_bvec() with the second slot of
+rq_bvec as the start, but doesn't reduce the array length by one, which
+could lead to an array overrun. Also, rq_bvec is always rq_maxpages in
+length, which can be too short in some cases, since the TCP record
+marker consumes a slot.
+
+Fix both problems by adding a separate bvec array to the svc_sock that
+is specifically for sending. For TCP, make this array one slot longer
+than rq_maxpages, to account for the record marker. For UDP, only
+allocate as large an array as we need since it's limited to 64k of
+payload.
+
+Signed-off-by: Jeff Layton <jlayton@kernel.org>
+Reviewed-by: NeilBrown <neil@brown.name>
+Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
+Stable-dep-of: 18c1cc698861 ("SUNRPC: Return an error from xdr_buf_to_bvec() on overflow")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ include/linux/sunrpc/svcsock.h | 3 +++
+ net/sunrpc/svcsock.c | 37 ++++++++++++++++++++++++++++---------
+ 2 files changed, 31 insertions(+), 9 deletions(-)
+
+--- a/include/linux/sunrpc/svcsock.h
++++ b/include/linux/sunrpc/svcsock.h
+@@ -26,6 +26,9 @@ struct svc_sock {
+ void (*sk_odata)(struct sock *);
+ void (*sk_owspace)(struct sock *);
+
++ /* For sends (protected by xpt_mutex) */
++ struct bio_vec *sk_bvec;
++
+ /* private TCP part */
+ /* On-the-wire fragment header: */
+ __be32 sk_marker;
+--- a/net/sunrpc/svcsock.c
++++ b/net/sunrpc/svcsock.c
+@@ -68,6 +68,17 @@
+
+ #define RPCDBG_FACILITY RPCDBG_SVCXPRT
+
++/*
++ * For UDP:
++ * 1 for header page
++ * enough pages for RPCSVC_MAXPAYLOAD_UDP
++ * 1 in case payload is not aligned
++ * 1 for tail page
++ */
++enum {
++ SUNRPC_MAX_UDP_SENDPAGES = 1 + RPCSVC_MAXPAYLOAD_UDP / PAGE_SIZE + 1 + 1
++};
++
+ /* To-do: to avoid tying up an nfsd thread while waiting for a
+ * handshake request, the request could instead be deferred.
+ */
+@@ -750,15 +761,14 @@ static int svc_udp_sendto(struct svc_rqs
+ if (svc_xprt_is_dead(xprt))
+ goto out_notconn;
+
+- count = xdr_buf_to_bvec(rqstp->rq_bvec,
+- ARRAY_SIZE(rqstp->rq_bvec), xdr);
++ count = xdr_buf_to_bvec(svsk->sk_bvec, SUNRPC_MAX_UDP_SENDPAGES, xdr);
+
+- iov_iter_bvec(&msg.msg_iter, ITER_SOURCE, rqstp->rq_bvec,
++ iov_iter_bvec(&msg.msg_iter, ITER_SOURCE, svsk->sk_bvec,
+ count, rqstp->rq_res.len);
+ err = sock_sendmsg(svsk->sk_sock, &msg);
+ if (err == -ECONNREFUSED) {
+ /* ICMP error on earlier request. */
+- iov_iter_bvec(&msg.msg_iter, ITER_SOURCE, rqstp->rq_bvec,
++ iov_iter_bvec(&msg.msg_iter, ITER_SOURCE, svsk->sk_bvec,
+ count, rqstp->rq_res.len);
+ err = sock_sendmsg(svsk->sk_sock, &msg);
+ }
+@@ -1257,19 +1267,19 @@ static int svc_tcp_sendmsg(struct svc_so
+ *sentp = 0;
+
+ /* The stream record marker is copied into a temporary page
+- * fragment buffer so that it can be included in rq_bvec.
++ * fragment buffer so that it can be included in sk_bvec.
+ */
+ buf = page_frag_alloc(&svsk->sk_frag_cache, sizeof(marker),
+ GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+ memcpy(buf, &marker, sizeof(marker));
+- bvec_set_virt(rqstp->rq_bvec, buf, sizeof(marker));
++ bvec_set_virt(svsk->sk_bvec, buf, sizeof(marker));
+
+- count = xdr_buf_to_bvec(rqstp->rq_bvec + 1,
+- ARRAY_SIZE(rqstp->rq_bvec) - 1, &rqstp->rq_res);
++ count = xdr_buf_to_bvec(svsk->sk_bvec + 1, RPCSVC_MAXPAGES,
++ &rqstp->rq_res);
+
+- iov_iter_bvec(&msg.msg_iter, ITER_SOURCE, rqstp->rq_bvec,
++ iov_iter_bvec(&msg.msg_iter, ITER_SOURCE, svsk->sk_bvec,
+ 1 + count, sizeof(marker) + rqstp->rq_res.len);
+ ret = sock_sendmsg(svsk->sk_sock, &msg);
+ page_frag_free(buf);
+@@ -1432,6 +1442,13 @@ static struct svc_sock *svc_setup_socket
+ if (!svsk)
+ return ERR_PTR(-ENOMEM);
+
++ svsk->sk_bvec = kcalloc(RPCSVC_MAXPAGES + 1, sizeof(*svsk->sk_bvec),
++ GFP_KERNEL);
++ if (!svsk->sk_bvec) {
++ kfree(svsk);
++ return ERR_PTR(-ENOMEM);
++ }
++
+ inet = sock->sk;
+
+ if (pmap_register) {
+@@ -1441,6 +1458,7 @@ static struct svc_sock *svc_setup_socket
+ inet->sk_protocol,
+ ntohs(inet_sk(inet)->inet_sport));
+ if (err < 0) {
++ kfree(svsk->sk_bvec);
+ kfree(svsk);
+ return ERR_PTR(err);
+ }
+@@ -1660,5 +1678,6 @@ static void svc_sock_free(struct svc_xpr
+ if (pfc->va)
+ __page_frag_cache_drain(virt_to_head_page(pfc->va),
+ pfc->pagecnt_bias);
++ kfree(svsk->sk_bvec);
+ kfree(svsk);
+ }
--- /dev/null
+From stable+bounces-288615-greg=kroah.com@vger.kernel.org Fri Jul 24 03:28:55 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 23 Jul 2026 21:25:15 -0400
+Subject: SUNRPC: Return an error from xdr_buf_to_bvec() on overflow
+To: stable@vger.kernel.org
+Cc: Chuck Lever <chuck.lever@oracle.com>, Chris Mason <clm@meta.com>, Jeff Layton <jlayton@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260724012515.3833219-5-sashal@kernel.org>
+
+From: Chuck Lever <chuck.lever@oracle.com>
+
+[ Upstream commit 18c1cc69886192e33536498289d26dba6894e3d5 ]
+
+xdr_buf_to_bvec() returns a slot count even when the caller's bvec
+budget is exhausted partway through the xdr_buf. Callers feed that
+count into iov_iter_bvec() and continue as if the conversion had
+succeeded, silently sending or writing fewer bytes than the data
+length declares. For an NFS WRITE the server reports the truncated
+transfer to the client as full success.
+
+The overflow represents an internal invariant violation: a higher
+layer reserved a bvec budget too small for the xdr_buf it then
+asked the encoder to convert. That is a server-side fault, not a
+media I/O failure and not a malformed client argument.
+
+Change xdr_buf_to_bvec() to return a signed int and have the
+overflow label return -ESERVERFAULT. Update the three callers to
+detect the negative return and fail the request: nfsd_vfs_write()
+folds the error into host_err, which nfserrno() translates to
+nfserr_serverfault for the WRITE reply; svc_udp_sendto() and
+svc_tcp_sendmsg() propagate the error out of the send path.
+
+Reported-by: Chris Mason <clm@meta.com>
+Fixes: 2eb2b9358181 ("SUNRPC: Convert svc_tcp_sendmsg to use bio_vecs directly")
+Cc: stable@vger.kernel.org
+Reviewed-by: Jeff Layton <jlayton@kernel.org>
+Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ include/linux/sunrpc/xdr.h | 4 ++--
+ net/sunrpc/svcsock.c | 14 ++++++++++++--
+ net/sunrpc/xdr.c | 11 ++++++-----
+ 3 files changed, 20 insertions(+), 9 deletions(-)
+
+--- a/include/linux/sunrpc/xdr.h
++++ b/include/linux/sunrpc/xdr.h
+@@ -139,8 +139,8 @@ void xdr_terminate_string(const struct x
+ size_t xdr_buf_pagecount(const struct xdr_buf *buf);
+ int xdr_alloc_bvec(struct xdr_buf *buf, gfp_t gfp);
+ void xdr_free_bvec(struct xdr_buf *buf);
+-unsigned int xdr_buf_to_bvec(struct bio_vec *bvec, unsigned int bvec_size,
+- const struct xdr_buf *xdr);
++int xdr_buf_to_bvec(struct bio_vec *bvec, unsigned int bvec_size,
++ const struct xdr_buf *xdr);
+ int xdr_buf_to_sg(const struct xdr_buf *buf, unsigned int offset,
+ unsigned int len, struct scatterlist *sg, unsigned int nsg);
+ int xdr_buf_to_sg_alloc(const struct xdr_buf *buf, unsigned int offset,
+--- a/net/sunrpc/svcsock.c
++++ b/net/sunrpc/svcsock.c
+@@ -748,7 +748,7 @@ static int svc_udp_sendto(struct svc_rqs
+ .msg_flags = MSG_SPLICE_PAGES,
+ .msg_controllen = sizeof(buffer),
+ };
+- unsigned int count;
++ int count;
+ int err;
+
+ svc_udp_release_ctxt(xprt, rqstp->rq_xprt_ctxt);
+@@ -762,6 +762,10 @@ static int svc_udp_sendto(struct svc_rqs
+ goto out_notconn;
+
+ count = xdr_buf_to_bvec(svsk->sk_bvec, SUNRPC_MAX_UDP_SENDPAGES, xdr);
++ if (count < 0) {
++ err = count;
++ goto out_trace;
++ }
+
+ iov_iter_bvec(&msg.msg_iter, ITER_SOURCE, svsk->sk_bvec,
+ count, rqstp->rq_res.len);
+@@ -773,6 +777,7 @@ static int svc_udp_sendto(struct svc_rqs
+ err = sock_sendmsg(svsk->sk_sock, &msg);
+ }
+
++out_trace:
+ trace_svcsock_udp_send(xprt, err);
+
+ mutex_unlock(&xprt->xpt_mutex);
+@@ -1260,7 +1265,7 @@ static int svc_tcp_sendmsg(struct svc_so
+ struct msghdr msg = {
+ .msg_flags = MSG_SPLICE_PAGES,
+ };
+- unsigned int count;
++ int count;
+ void *buf;
+ int ret;
+
+@@ -1278,10 +1283,15 @@ static int svc_tcp_sendmsg(struct svc_so
+
+ count = xdr_buf_to_bvec(svsk->sk_bvec + 1, RPCSVC_MAXPAGES,
+ &rqstp->rq_res);
++ if (count < 0) {
++ ret = count;
++ goto out;
++ }
+
+ iov_iter_bvec(&msg.msg_iter, ITER_SOURCE, svsk->sk_bvec,
+ 1 + count, sizeof(marker) + rqstp->rq_res.len);
+ ret = sock_sendmsg(svsk->sk_sock, &msg);
++out:
+ page_frag_free(buf);
+ if (ret < 0)
+ return ret;
+--- a/net/sunrpc/xdr.c
++++ b/net/sunrpc/xdr.c
+@@ -167,13 +167,14 @@ xdr_free_bvec(struct xdr_buf *buf)
+ /**
+ * xdr_buf_to_bvec - Copy components of an xdr_buf into a bio_vec array
+ * @bvec: bio_vec array to populate
+- * @bvec_size: element count of @bio_vec
++ * @bvec_size: element count of @bvec
+ * @xdr: xdr_buf to be copied
+ *
+- * Returns the number of entries consumed in @bvec.
++ * Returns the number of entries consumed in @bvec on success, or
++ * -ESERVERFAULT when @xdr does not fit within @bvec_size entries.
+ */
+-unsigned int xdr_buf_to_bvec(struct bio_vec *bvec, unsigned int bvec_size,
+- const struct xdr_buf *xdr)
++int xdr_buf_to_bvec(struct bio_vec *bvec, unsigned int bvec_size,
++ const struct xdr_buf *xdr)
+ {
+ const struct kvec *head = xdr->head;
+ const struct kvec *tail = xdr->tail;
+@@ -215,7 +216,7 @@ unsigned int xdr_buf_to_bvec(struct bio_
+
+ bvec_overflow:
+ pr_warn_once("%s: bio_vec array overflow\n", __func__);
+- return count;
++ return -ESERVERFAULT;
+ }
+
+ /**
--- /dev/null
+From stable+bounces-289267-greg=kroah.com@vger.kernel.org Sun Jul 26 14:42:20 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 26 Jul 2026 08:42:06 -0400
+Subject: taskstats: fill_stats_for_tgid: use for_each_thread()
+To: stable@vger.kernel.org
+Cc: Oleg Nesterov <oleg@redhat.com>, "Eric W. Biederman" <ebiederm@xmission.com>, Andrew Morton <akpm@linux-foundation.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260726124207.101107-1-sashal@kernel.org>
+
+From: Oleg Nesterov <oleg@redhat.com>
+
+[ Upstream commit ed5378a387fd7c382497f2abcf4605e030b64044 ]
+
+do/while_each_thread should be avoided when possible.
+
+Plus I _think_ this change allows to avoid lock_task_sighand() but I am
+not sure, I forgot everything about taskstats. In any case, this code
+does not look right in that the same thread can be accounted twice:
+taskstats_exit() can account the exiting thread in signal->stats and drop
+->siglock but this thread is still on the thread-group list, so
+lock_task_sighand() can't help.
+
+Link: https://lkml.kernel.org/r/20230909214951.GA24274@redhat.com
+Signed-off-by: Oleg Nesterov <oleg@redhat.com>
+Cc: Eric W. Biederman <ebiederm@xmission.com>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Stable-dep-of: b3e4fbb04220 ("taskstats: retain dead thread stats in TGID queries")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ kernel/taskstats.c | 5 ++---
+ 1 file changed, 2 insertions(+), 3 deletions(-)
+
+--- a/kernel/taskstats.c
++++ b/kernel/taskstats.c
+@@ -233,9 +233,8 @@ static int fill_stats_for_tgid(pid_t tgi
+ else
+ memset(stats, 0, sizeof(*stats));
+
+- tsk = first;
+ start_time = ktime_get_ns();
+- do {
++ for_each_thread(first, tsk) {
+ if (tsk->exit_state)
+ continue;
+ /*
+@@ -258,7 +257,7 @@ static int fill_stats_for_tgid(pid_t tgi
+
+ stats->nvcsw += tsk->nvcsw;
+ stats->nivcsw += tsk->nivcsw;
+- } while_each_thread(first, tsk);
++ }
+
+ unlock_task_sighand(first, &flags);
+ rc = 0;
--- /dev/null
+From stable+bounces-289268-greg=kroah.com@vger.kernel.org Sun Jul 26 14:42:14 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 26 Jul 2026 08:42:07 -0400
+Subject: taskstats: retain dead thread stats in TGID queries
+To: stable@vger.kernel.org
+Cc: Yiyang Chen <cyyzero16@gmail.com>, Balbir Singh <balbirs@nvidia.com>, "Dr. Thomas Orgis" <thomas.orgis@uni-hamburg.de>, Oleg Nesterov <oleg@redhat.com>, Wang Yaxin <wang.yaxin@zte.com.cn>, Yang Yang <yang.yang29@zte.com.cn>, Andrew Morton <akpm@linux-foundation.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260726124207.101107-2-sashal@kernel.org>
+
+From: Yiyang Chen <cyyzero16@gmail.com>
+
+[ Upstream commit b3e4fbb04220efc3bc022bcf31b5689d39c6b111 ]
+
+Patch series "taskstats: fix TGID dead-thread stat retention", v3.
+
+This series fixes a taskstats TGID aggregation bug where fields added in
+the TGID query path were not preserved after thread exit, and adds a
+kselftest covering the regression.
+
+The first patch keeps the cached TGID aggregate used for dead threads in
+step with the fields already accumulated for live threads, and also fixes
+the final TGID exit notification emitted when group_dead is true.
+
+The second patch adds a kselftest that verifies TGID CPU stats do not
+regress after a worker thread exits and has been reaped.
+
+This patch (of 2):
+
+fill_stats_for_tgid() builds TGID stats from two sources: the cached
+aggregate in signal->stats and a scan of the live threads in the group.
+
+However, fill_tgid_exit() only accumulates delay accounting into
+signal->stats. This means that once a thread exits, TGID queries lose the
+fields that fill_stats_for_tgid() adds for live threads.
+
+This gap was introduced incrementally by two earlier changes that extended
+fill_stats_for_tgid() but did not make the corresponding update to
+fill_tgid_exit():
+
+- commit 8c733420bdd5 ("taskstats: add e/u/stime for TGID command")
+ added ac_etime, ac_utime, and ac_stime to the TGID query path.
+- commit b663a79c1915 ("taskstats: add context-switch counters")
+ added nvcsw and nivcsw to the TGID query path.
+
+As a result, those fields were accounted for live threads in TGID queries,
+but were dropped from the cached TGID aggregate after thread exit. The
+final TGID exit notification emitted when group_dead is true also copies
+that cached aggregate, so it loses the same fields.
+
+Factor the per-task TGID accumulation into tgid_stats_add_task() and use
+it in both fill_stats_for_tgid() and fill_tgid_exit(). This keeps the
+cached aggregate used for dead threads aligned with the live-thread
+accumulation used by TGID queries.
+
+Link: https://lore.kernel.org/cover.1776094300.git.cyyzero16@gmail.com
+Link: https://lore.kernel.org/abd2a15d33343636ab5ba43d540bcfe508bd66c7.1776094300.git.cyyzero16@gmail.com
+Fixes: 8c733420bdd5 ("taskstats: add e/u/stime for TGID command")
+Fixes: b663a79c1915 ("taskstats: add context-switch counters")
+Signed-off-by: Yiyang Chen <cyyzero16@gmail.com>
+Acked-by: Balbir Singh <balbirs@nvidia.com>
+Cc: Dr. Thomas Orgis <thomas.orgis@uni-hamburg.de>
+Cc: Oleg Nesterov <oleg@redhat.com>
+Cc: Wang Yaxin <wang.yaxin@zte.com.cn>
+Cc: Yang Yang <yang.yang29@zte.com.cn>
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ kernel/taskstats.c | 62 ++++++++++++++++++++++++++++-------------------------
+ 1 file changed, 33 insertions(+), 29 deletions(-)
+
+--- a/kernel/taskstats.c
++++ b/kernel/taskstats.c
+@@ -210,13 +210,39 @@ static int fill_stats_for_pid(pid_t pid,
+ return 0;
+ }
+
++static void tgid_stats_add_task(struct taskstats *stats,
++ struct task_struct *tsk, u64 now_ns)
++{
++ u64 delta, utime, stime;
++
++ /*
++ * Each accounting subsystem calls its functions here to
++ * accumulate its per-task stats for tsk, into the per-tgid structure
++ *
++ * per-task-foo(stats, tsk);
++ */
++ delayacct_add_tsk(stats, tsk);
++
++ /* calculate task elapsed time in nsec */
++ delta = now_ns - tsk->start_time;
++ /* Convert to micro seconds */
++ do_div(delta, NSEC_PER_USEC);
++ stats->ac_etime += delta;
++
++ task_cputime(tsk, &utime, &stime);
++ stats->ac_utime += div_u64(utime, NSEC_PER_USEC);
++ stats->ac_stime += div_u64(stime, NSEC_PER_USEC);
++
++ stats->nvcsw += tsk->nvcsw;
++ stats->nivcsw += tsk->nivcsw;
++}
++
+ static int fill_stats_for_tgid(pid_t tgid, struct taskstats *stats)
+ {
+ struct task_struct *tsk, *first;
+ unsigned long flags;
+ int rc = -ESRCH;
+- u64 delta, utime, stime;
+- u64 start_time;
++ u64 now_ns;
+
+ /*
+ * Add additional stats from live tasks except zombie thread group
+@@ -233,30 +259,12 @@ static int fill_stats_for_tgid(pid_t tgi
+ else
+ memset(stats, 0, sizeof(*stats));
+
+- start_time = ktime_get_ns();
++ now_ns = ktime_get_ns();
+ for_each_thread(first, tsk) {
+ if (tsk->exit_state)
+ continue;
+- /*
+- * Accounting subsystem can call its functions here to
+- * fill in relevant parts of struct taskstsats as follows
+- *
+- * per-task-foo(stats, tsk);
+- */
+- delayacct_add_tsk(stats, tsk);
+-
+- /* calculate task elapsed time in nsec */
+- delta = start_time - tsk->start_time;
+- /* Convert to micro seconds */
+- do_div(delta, NSEC_PER_USEC);
+- stats->ac_etime += delta;
+-
+- task_cputime(tsk, &utime, &stime);
+- stats->ac_utime += div_u64(utime, NSEC_PER_USEC);
+- stats->ac_stime += div_u64(stime, NSEC_PER_USEC);
+
+- stats->nvcsw += tsk->nvcsw;
+- stats->nivcsw += tsk->nivcsw;
++ tgid_stats_add_task(stats, tsk, now_ns);
+ }
+
+ unlock_task_sighand(first, &flags);
+@@ -275,18 +283,14 @@ out:
+ static void fill_tgid_exit(struct task_struct *tsk)
+ {
+ unsigned long flags;
++ u64 now_ns;
+
+ spin_lock_irqsave(&tsk->sighand->siglock, flags);
+ if (!tsk->signal->stats)
+ goto ret;
+
+- /*
+- * Each accounting subsystem calls its functions here to
+- * accumalate its per-task stats for tsk, into the per-tgid structure
+- *
+- * per-task-foo(tsk->signal->stats, tsk);
+- */
+- delayacct_add_tsk(tsk->signal->stats, tsk);
++ now_ns = ktime_get_ns();
++ tgid_stats_add_task(tsk->signal->stats, tsk, now_ns);
+ ret:
+ spin_unlock_irqrestore(&tsk->sighand->siglock, flags);
+ return;
--- /dev/null
+From stable+bounces-289294-greg=kroah.com@vger.kernel.org Sun Jul 26 16:01:51 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 26 Jul 2026 09:59:49 -0400
+Subject: thunderbolt: Handle lane bonding of Gen 4 XDomain links properly
+To: stable@vger.kernel.org
+Cc: Gil Fine <gil.fine@linux.intel.com>, Mika Westerberg <mika.westerberg@linux.intel.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260726135955.668115-1-sashal@kernel.org>
+
+From: Gil Fine <gil.fine@linux.intel.com>
+
+[ Upstream commit 36b6ad6ad0350554e611a8cb754ccd40857416a8 ]
+
+Gen 4 links come up as bonded already so we are not supposed to initiate
+lane bonding on them. However, we should still update the port
+structures accordingly. Split these into their own functions to make it
+easier to follow.
+
+Signed-off-by: Gil Fine <gil.fine@linux.intel.com>
+Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
+Stable-dep-of: 2c5d2d3c3f70 ("thunderbolt: Prevent XDomain delayed work use-after-free on disconnect")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/thunderbolt/tb.c | 2 -
+ drivers/thunderbolt/xdomain.c | 49 +++++++++++++++++++++++++++++++++++++++++-
+ 2 files changed, 48 insertions(+), 3 deletions(-)
+
+--- a/drivers/thunderbolt/tb.c
++++ b/drivers/thunderbolt/tb.c
+@@ -539,8 +539,6 @@ static void tb_port_unconfigure_xdomain(
+ usb4_port_unconfigure_xdomain(port);
+ else
+ tb_lc_unconfigure_xdomain(port);
+-
+- tb_port_enable(port->dual_link_port);
+ }
+
+ static void tb_scan_xdomain(struct tb_port *port)
+--- a/drivers/thunderbolt/xdomain.c
++++ b/drivers/thunderbolt/xdomain.c
+@@ -1903,6 +1903,50 @@ struct device_type tb_xdomain_type = {
+ };
+ EXPORT_SYMBOL_GPL(tb_xdomain_type);
+
++static void tb_xdomain_link_init(struct tb_xdomain *xd, struct tb_port *down)
++{
++ if (!down->dual_link_port)
++ return;
++
++ /*
++ * Gen 4 links come up already as bonded so only update the port
++ * structures here.
++ */
++ if (tb_port_get_link_generation(down) >= 4) {
++ down->bonded = true;
++ down->dual_link_port->bonded = true;
++ } else {
++ xd->bonding_possible = true;
++ }
++}
++
++static void tb_xdomain_link_exit(struct tb_xdomain *xd)
++{
++ struct tb_port *down = tb_xdomain_downstream_port(xd);
++
++ if (!down->dual_link_port)
++ return;
++
++ if (tb_port_get_link_generation(down) >= 4) {
++ down->bonded = false;
++ down->dual_link_port->bonded = false;
++ } else if (xd->link_width > TB_LINK_WIDTH_SINGLE) {
++ /*
++ * Just return port structures back to way they were and
++ * update credits. No need to update userspace because
++ * the XDomain is removed soon anyway.
++ */
++ tb_port_lane_bonding_disable(down);
++ tb_port_update_credits(down);
++ } else if (down->dual_link_port) {
++ /*
++ * Re-enable the lane 1 adapter we disabled at the end
++ * of tb_xdomain_get_properties().
++ */
++ tb_port_enable(down->dual_link_port);
++ }
++}
++
+ /**
+ * tb_xdomain_alloc() - Allocate new XDomain object
+ * @tb: Domain where the XDomain belongs
+@@ -1953,7 +1997,8 @@ struct tb_xdomain *tb_xdomain_alloc(stru
+ goto err_free_local_uuid;
+ } else {
+ xd->needs_uuid = true;
+- xd->bonding_possible = !!down->dual_link_port;
++
++ tb_xdomain_link_init(xd, down);
+ }
+
+ device_initialize(&xd->dev);
+@@ -2022,6 +2067,8 @@ void tb_xdomain_remove(struct tb_xdomain
+
+ device_for_each_child_reverse(&xd->dev, xd, unregister_service);
+
++ tb_xdomain_link_exit(xd);
++
+ /*
+ * Undo runtime PM here explicitly because it is possible that
+ * the XDomain was never added to the bus and thus device_del()
--- /dev/null
+From stable+bounces-289297-greg=kroah.com@vger.kernel.org Sun Jul 26 16:00:04 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 26 Jul 2026 09:59:52 -0400
+Subject: thunderbolt: Keep XDomain reference during the lifetime of a service
+To: stable@vger.kernel.org
+Cc: Mika Westerberg <mika.westerberg@linux.intel.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260726135955.668115-4-sashal@kernel.org>
+
+From: Mika Westerberg <mika.westerberg@linux.intel.com>
+
+[ Upstream commit 8b4060998637f06975fceee9b73845d8672d411e ]
+
+This is needed because we release the service ID in tb_service_release()
+and the ID array is owned by the parent XDomain.
+
+Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
+Stable-dep-of: 2c5d2d3c3f70 ("thunderbolt: Prevent XDomain delayed work use-after-free on disconnect")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/thunderbolt/xdomain.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- a/drivers/thunderbolt/xdomain.c
++++ b/drivers/thunderbolt/xdomain.c
+@@ -1008,6 +1008,7 @@ static void tb_service_release(struct de
+ ida_free(&xd->service_ids, svc->id);
+ kfree(svc->key);
+ kfree(svc);
++ tb_xdomain_put(xd);
+ }
+
+ struct device_type tb_service_type = {
+@@ -1116,7 +1117,7 @@ static void enumerate_services(struct tb
+ svc->id = id;
+ svc->dev.bus = &tb_bus_type;
+ svc->dev.type = &tb_service_type;
+- svc->dev.parent = &xd->dev;
++ svc->dev.parent = get_device(&xd->dev);
+ dev_set_name(&svc->dev, "%s.%d", dev_name(&xd->dev), svc->id);
+
+ tb_service_debugfs_init(svc);
--- /dev/null
+From stable+bounces-289300-greg=kroah.com@vger.kernel.org Sun Jul 26 16:00:09 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 26 Jul 2026 09:59:55 -0400
+Subject: thunderbolt: Prevent XDomain delayed work use-after-free on disconnect
+To: stable@vger.kernel.org
+Cc: Michael Bommarito <michael.bommarito@gmail.com>, Mika Westerberg <mika.westerberg@linux.intel.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260726135955.668115-7-sashal@kernel.org>
+
+From: Michael Bommarito <michael.bommarito@gmail.com>
+
+[ Upstream commit 2c5d2d3c3f70cde2565d7b279b544893a2035842 ]
+
+tb_xdp_handle_request() runs on system_wq and queues
+xd->state_work via queue_delayed_work() in three request handlers:
+PROPERTIES_CHANGED_REQUEST, UUID_REQUEST (via start_handshake),
+and LINK_STATE_CHANGE_REQUEST. Similarly, update_xdomain() queues
+xd->properties_changed_work when local properties change.
+
+Concurrently, tb_xdomain_remove() calls stop_handshake() which does
+cancel_delayed_work_sync() on both delayed works. Later,
+tb_xdomain_unregister() calls device_unregister() which eventually
+frees the xdomain. Since commit 559c1e1e0134 ("thunderbolt: Run
+tb_xdp_handle_request() in system workqueue") moved the request
+handler off tb->wq, the handler and the remove path are no longer
+serialized. If queue_delayed_work() executes after
+cancel_delayed_work_sync() but before the xdomain is freed, the
+delayed work fires on a freed object.
+
+Add xd->removing that tb_xdomain_remove() sets under xd->lock
+before calling stop_handshake(). Each external queue site holds
+the same lock and checks removing before calling
+queue_delayed_work(). This provides the mutual exclusion needed:
+either the queue site acquires the lock first and queues work that
+the subsequent cancel will see, or the remove path acquires the
+lock first and the queue site observes removing == true and skips
+the queue.
+
+Fixes: 559c1e1e0134 ("thunderbolt: Run tb_xdp_handle_request() in system workqueue")
+Cc: stable@vger.kernel.org
+Assisted-by: Claude:claude-opus-4-7
+Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
+Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/thunderbolt/xdomain.c | 26 +++++++++++++-------------
+ 1 file changed, 13 insertions(+), 13 deletions(-)
+
+--- a/drivers/thunderbolt/xdomain.c
++++ b/drivers/thunderbolt/xdomain.c
+@@ -905,6 +905,19 @@ void tb_unregister_service_driver(struct
+ }
+ EXPORT_SYMBOL_GPL(tb_unregister_service_driver);
+
++static int update_xdomain(struct device *dev, void *data)
++{
++ struct tb_xdomain *xd;
++
++ xd = tb_to_xdomain(dev);
++ if (xd) {
++ queue_delayed_work(xd->tb->wq, &xd->properties_changed_work,
++ msecs_to_jiffies(50));
++ }
++
++ return 0;
++}
++
+ static ssize_t key_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+ {
+@@ -2475,19 +2488,6 @@ bool tb_xdomain_handle_request(struct tb
+ return ret > 0;
+ }
+
+-static int update_xdomain(struct device *dev, void *data)
+-{
+- struct tb_xdomain *xd;
+-
+- xd = tb_to_xdomain(dev);
+- if (xd) {
+- queue_delayed_work(xd->tb->wq, &xd->properties_changed_work,
+- msecs_to_jiffies(50));
+- }
+-
+- return 0;
+-}
+-
+ static void update_all_xdomains(void)
+ {
+ bus_for_each_dev(&tb_bus_type, NULL, NULL, update_xdomain);
--- /dev/null
+From stable+bounces-289298-greg=kroah.com@vger.kernel.org Sun Jul 26 16:00:05 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 26 Jul 2026 09:59:53 -0400
+Subject: thunderbolt: Remove service debugfs entries during unregister
+To: stable@vger.kernel.org
+Cc: Mika Westerberg <mika.westerberg@linux.intel.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260726135955.668115-5-sashal@kernel.org>
+
+From: Mika Westerberg <mika.westerberg@linux.intel.com>
+
+[ Upstream commit 4d5fc3f4068568dfcb8cbe2852b4adc56394aa26 ]
+
+We add them as part of the register path so to keep it symmetric remove
+them as part of the unregister path. This also removes them even if the
+service itself is not yet released (but is unregistered), thus allowing
+new register with the same service name to happen.
+
+Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
+Stable-dep-of: 2c5d2d3c3f70 ("thunderbolt: Prevent XDomain delayed work use-after-free on disconnect")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/thunderbolt/xdomain.c | 14 +++++++++++---
+ 1 file changed, 11 insertions(+), 3 deletions(-)
+
+--- a/drivers/thunderbolt/xdomain.c
++++ b/drivers/thunderbolt/xdomain.c
+@@ -1004,7 +1004,6 @@ static void tb_service_release(struct de
+ struct tb_service *svc = container_of(dev, struct tb_service, dev);
+ struct tb_xdomain *xd = tb_service_parent(svc);
+
+- tb_service_debugfs_remove(svc);
+ ida_free(&xd->service_ids, svc->id);
+ kfree(svc->key);
+ kfree(svc);
+@@ -1019,6 +1018,14 @@ struct device_type tb_service_type = {
+ };
+ EXPORT_SYMBOL_GPL(tb_service_type);
+
++static void __unregister_service(struct device *dev)
++{
++ struct tb_service *svc = tb_to_service(dev);
++
++ tb_service_debugfs_remove(svc);
++ device_unregister(&svc->dev);
++}
++
+ static int remove_missing_service(struct device *dev, void *data)
+ {
+ struct tb_xdomain *xd = data;
+@@ -1030,7 +1037,7 @@ static int remove_missing_service(struct
+
+ if (!tb_property_find(xd->remote_properties, svc->key,
+ TB_PROPERTY_TYPE_DIRECTORY))
+- device_unregister(dev);
++ __unregister_service(dev);
+
+ return 0;
+ }
+@@ -1123,6 +1130,7 @@ static void enumerate_services(struct tb
+ tb_service_debugfs_init(svc);
+
+ if (device_register(&svc->dev)) {
++ tb_service_debugfs_remove(svc);
+ put_device(&svc->dev);
+ break;
+ }
+@@ -2048,7 +2056,7 @@ void tb_xdomain_add(struct tb_xdomain *x
+
+ static int unregister_service(struct device *dev, void *data)
+ {
+- device_unregister(dev);
++ __unregister_service(dev);
+ return 0;
+ }
+
--- /dev/null
+From stable+bounces-289295-greg=kroah.com@vger.kernel.org Sun Jul 26 16:02:04 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 26 Jul 2026 09:59:50 -0400
+Subject: thunderbolt: Remove usage of the deprecated ida_simple_xx() API
+To: stable@vger.kernel.org
+Cc: Christophe JAILLET <christophe.jaillet@wanadoo.fr>, Mika Westerberg <mika.westerberg@linux.intel.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260726135955.668115-2-sashal@kernel.org>
+
+From: Christophe JAILLET <christophe.jaillet@wanadoo.fr>
+
+[ Upstream commit dec6a613574cd3dea799170b7aaa8fd76e22f176 ]
+
+ida_alloc() and ida_free() should be preferred to the deprecated
+ida_simple_get() and ida_simple_remove().
+
+Note that the upper limit of ida_simple_get() is exclusive, but the one of
+ida_alloc_range()/ida_alloc_max() is inclusive. So a -1 has been added
+when needed.
+
+Signed-off-by: Christophe JAILLET <christophe.jaillet@wanadoo.fr>
+Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
+Stable-dep-of: 2c5d2d3c3f70 ("thunderbolt: Prevent XDomain delayed work use-after-free on disconnect")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/thunderbolt/domain.c | 6 +++---
+ drivers/thunderbolt/nhi.c | 6 +++---
+ drivers/thunderbolt/nvm.c | 4 ++--
+ drivers/thunderbolt/switch.c | 6 +++---
+ drivers/thunderbolt/xdomain.c | 4 ++--
+ 5 files changed, 13 insertions(+), 13 deletions(-)
+
+--- a/drivers/thunderbolt/domain.c
++++ b/drivers/thunderbolt/domain.c
+@@ -321,7 +321,7 @@ static void tb_domain_release(struct dev
+
+ tb_ctl_free(tb->ctl);
+ destroy_workqueue(tb->wq);
+- ida_simple_remove(&tb_domain_ida, tb->index);
++ ida_free(&tb_domain_ida, tb->index);
+ mutex_destroy(&tb->lock);
+ kfree(tb);
+ }
+@@ -389,7 +389,7 @@ struct tb *tb_domain_alloc(struct tb_nhi
+ tb->nhi = nhi;
+ mutex_init(&tb->lock);
+
+- tb->index = ida_simple_get(&tb_domain_ida, 0, 0, GFP_KERNEL);
++ tb->index = ida_alloc(&tb_domain_ida, GFP_KERNEL);
+ if (tb->index < 0)
+ goto err_free;
+
+@@ -413,7 +413,7 @@ struct tb *tb_domain_alloc(struct tb_nhi
+ err_destroy_wq:
+ destroy_workqueue(tb->wq);
+ err_remove_ida:
+- ida_simple_remove(&tb_domain_ida, tb->index);
++ ida_free(&tb_domain_ida, tb->index);
+ err_free:
+ kfree(tb);
+
+--- a/drivers/thunderbolt/nhi.c
++++ b/drivers/thunderbolt/nhi.c
+@@ -465,7 +465,7 @@ static int ring_request_msix(struct tb_r
+ if (!nhi->pdev->msix_enabled)
+ return 0;
+
+- ret = ida_simple_get(&nhi->msix_ida, 0, MSIX_MAX_VECS, GFP_KERNEL);
++ ret = ida_alloc_max(&nhi->msix_ida, MSIX_MAX_VECS - 1, GFP_KERNEL);
+ if (ret < 0)
+ return ret;
+
+@@ -485,7 +485,7 @@ static int ring_request_msix(struct tb_r
+ return 0;
+
+ err_ida_remove:
+- ida_simple_remove(&nhi->msix_ida, ring->vector);
++ ida_free(&nhi->msix_ida, ring->vector);
+
+ return ret;
+ }
+@@ -496,7 +496,7 @@ static void ring_release_msix(struct tb_
+ return;
+
+ free_irq(ring->irq, ring);
+- ida_simple_remove(&ring->nhi->msix_ida, ring->vector);
++ ida_free(&ring->nhi->msix_ida, ring->vector);
+ ring->vector = 0;
+ ring->irq = 0;
+ }
+--- a/drivers/thunderbolt/nvm.c
++++ b/drivers/thunderbolt/nvm.c
+@@ -330,7 +330,7 @@ struct tb_nvm *tb_nvm_alloc(struct devic
+ if (!nvm)
+ return ERR_PTR(-ENOMEM);
+
+- ret = ida_simple_get(&nvm_ida, 0, 0, GFP_KERNEL);
++ ret = ida_alloc(&nvm_ida, GFP_KERNEL);
+ if (ret < 0) {
+ kfree(nvm);
+ return ERR_PTR(ret);
+@@ -528,7 +528,7 @@ void tb_nvm_free(struct tb_nvm *nvm)
+ nvmem_unregister(nvm->non_active);
+ nvmem_unregister(nvm->active);
+ vfree(nvm->buf);
+- ida_simple_remove(&nvm_ida, nvm->id);
++ ida_free(&nvm_ida, nvm->id);
+ }
+ kfree(nvm);
+ }
+--- a/drivers/thunderbolt/switch.c
++++ b/drivers/thunderbolt/switch.c
+@@ -777,7 +777,7 @@ static int tb_port_alloc_hopid(struct tb
+ if (max_hopid < 0 || max_hopid > port_max_hopid)
+ max_hopid = port_max_hopid;
+
+- return ida_simple_get(ida, min_hopid, max_hopid + 1, GFP_KERNEL);
++ return ida_alloc_range(ida, min_hopid, max_hopid, GFP_KERNEL);
+ }
+
+ /**
+@@ -815,7 +815,7 @@ int tb_port_alloc_out_hopid(struct tb_po
+ */
+ void tb_port_release_in_hopid(struct tb_port *port, int hopid)
+ {
+- ida_simple_remove(&port->in_hopids, hopid);
++ ida_free(&port->in_hopids, hopid);
+ }
+
+ /**
+@@ -825,7 +825,7 @@ void tb_port_release_in_hopid(struct tb_
+ */
+ void tb_port_release_out_hopid(struct tb_port *port, int hopid)
+ {
+- ida_simple_remove(&port->out_hopids, hopid);
++ ida_free(&port->out_hopids, hopid);
+ }
+
+ static inline bool tb_switch_is_reachable(const struct tb_switch *parent,
+--- a/drivers/thunderbolt/xdomain.c
++++ b/drivers/thunderbolt/xdomain.c
+@@ -1005,7 +1005,7 @@ static void tb_service_release(struct de
+ struct tb_xdomain *xd = tb_service_parent(svc);
+
+ tb_service_debugfs_remove(svc);
+- ida_simple_remove(&xd->service_ids, svc->id);
++ ida_free(&xd->service_ids, svc->id);
+ kfree(svc->key);
+ kfree(svc);
+ }
+@@ -1107,7 +1107,7 @@ static void enumerate_services(struct tb
+ break;
+ }
+
+- id = ida_simple_get(&xd->service_ids, 0, 0, GFP_KERNEL);
++ id = ida_alloc(&xd->service_ids, GFP_KERNEL);
+ if (id < 0) {
+ kfree(svc->key);
+ kfree(svc);
--- /dev/null
+From stable+bounces-289299-greg=kroah.com@vger.kernel.org Sun Jul 26 16:00:09 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 26 Jul 2026 09:59:54 -0400
+Subject: thunderbolt: Remove XDomain from the bus without holding tb->lock
+To: stable@vger.kernel.org
+Cc: Mika Westerberg <mika.westerberg@linux.intel.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260726135955.668115-6-sashal@kernel.org>
+
+From: Mika Westerberg <mika.westerberg@linux.intel.com>
+
+[ Upstream commit a8937f35cf39c39c64325aa84d0463d866850857 ]
+
+Currently we call device_unregister() for services and the XDomain
+itself with tb->lock held. This prevents the service drivers from
+calling any functions that may take it. For this reason separate
+removing the XDomain from the topology data structures (where we need
+the lock) from unregistering the device from the bus (where remove
+callbacks of the drivers are being called).
+
+Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
+Stable-dep-of: 2c5d2d3c3f70 ("thunderbolt: Prevent XDomain delayed work use-after-free on disconnect")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/thunderbolt/debugfs.c | 2 +
+ drivers/thunderbolt/domain.c | 30 +++++++++++++++++++++
+ drivers/thunderbolt/icm.c | 5 +++
+ drivers/thunderbolt/switch.c | 14 +++++++++
+ drivers/thunderbolt/tb.c | 59 ++++++++++++++++++++----------------------
+ drivers/thunderbolt/tb.h | 2 +
+ drivers/thunderbolt/xdomain.c | 53 +++++++++++++++++++++++--------------
+ 7 files changed, 115 insertions(+), 50 deletions(-)
+
+--- a/drivers/thunderbolt/debugfs.c
++++ b/drivers/thunderbolt/debugfs.c
+@@ -956,6 +956,8 @@ static void margining_port_remove(struct
+
+ if (!port->usb4)
+ return;
++ if (!port->usb4->margining)
++ return;
+
+ snprintf(dir_name, sizeof(dir_name), "port%d", port->port);
+ parent = debugfs_lookup(dir_name, port->sw->debugfs_dir);
+--- a/drivers/thunderbolt/domain.c
++++ b/drivers/thunderbolt/domain.c
+@@ -867,6 +867,36 @@ int tb_domain_disconnect_all_paths(struc
+ return bus_for_each_dev(&tb_bus_type, NULL, tb, disconnect_xdomain);
+ }
+
++struct unregister_context {
++ const struct tb *tb;
++ int n;
++};
++
++static int unregister_unplugged_xdomain(struct device *dev, void *data)
++{
++ struct unregister_context *ctx = data;
++ struct tb_xdomain *xd;
++
++ xd = tb_to_xdomain(dev);
++ if (xd && xd->tb == ctx->tb && xd->is_unplugged) {
++ tb_xdomain_unregister(xd);
++ ctx->n++;
++ }
++ return 0;
++}
++
++int tb_domain_unregister_unplugged_xdomains(struct tb *tb)
++{
++ struct unregister_context ctx;
++
++ ctx.tb = tb_domain_get(tb);
++ ctx.n = 0;
++ bus_for_each_dev(&tb_bus_type, NULL, &ctx, unregister_unplugged_xdomain);
++ tb_domain_put(tb);
++
++ return ctx.n;
++}
++
+ int tb_domain_init(void)
+ {
+ int ret;
+--- a/drivers/thunderbolt/icm.c
++++ b/drivers/thunderbolt/icm.c
+@@ -713,6 +713,7 @@ static void remove_xdomain(struct tb_xdo
+
+ sw = tb_to_switch(xd->dev.parent);
+ tb_port_at(xd->route, sw)->xdomain = NULL;
++ xd->is_unplugged = true;
+ tb_xdomain_remove(xd);
+ }
+
+@@ -1728,6 +1729,8 @@ static void icm_handle_notification(stru
+
+ kfree(n->pkg);
+ kfree(n);
++
++ tb_domain_unregister_unplugged_xdomains(tb);
+ }
+
+ static void icm_handle_event(struct tb *tb, enum tb_cfg_pkg_type type,
+@@ -2078,6 +2081,8 @@ static void icm_rescan_work(struct work_
+ if (tb->root_switch)
+ icm_free_unplugged_children(tb->root_switch);
+ mutex_unlock(&tb->lock);
++
++ tb_domain_unregister_unplugged_xdomains(tb);
+ }
+
+ static void icm_complete(struct tb *tb)
+--- a/drivers/thunderbolt/switch.c
++++ b/drivers/thunderbolt/switch.c
+@@ -3556,6 +3556,20 @@ int tb_switch_resume(struct tb_switch *s
+ tb_port_warn(port,
+ "lost during suspend, disconnecting\n");
+ tb_sw_set_unplugged(port->remote->sw);
++ } else if (port->xdomain) {
++ /*
++ * If the user replaced the XDomain with
++ * another router, this will succeed in
++ * which case we must remove the XDomain
++ * before adding the new router.
++ */
++ err = tb_cfg_get_upstream_port(sw->tb->ctl,
++ port->xdomain->route);
++ if (err > 0) {
++ tb_port_warn(port,
++ "XDomain was disconnected\n");
++ port->xdomain->is_unplugged = true;
++ }
+ }
+ }
+ }
+--- a/drivers/thunderbolt/tb.c
++++ b/drivers/thunderbolt/tb.c
+@@ -2319,6 +2319,8 @@ put_sw:
+ out:
+ mutex_unlock(&tb->lock);
+
++ tb_domain_unregister_unplugged_xdomains(tb);
++
+ pm_runtime_mark_last_busy(&tb->dev);
+ pm_runtime_put_autosuspend(&tb->dev);
+
+@@ -2787,6 +2789,24 @@ static void tb_restore_children(struct t
+ }
+ }
+
++static void tb_free_unplugged_xdomains(struct tb_switch *sw)
++{
++ struct tb_port *port;
++
++ tb_switch_for_each_port(sw, port) {
++ if (tb_is_upstream_port(port))
++ continue;
++ if (port->xdomain && port->xdomain->is_unplugged) {
++ tb_retimer_remove_all(port);
++ tb_xdomain_remove(port->xdomain);
++ tb_port_unconfigure_xdomain(port);
++ port->xdomain = NULL;
++ } else if (port->remote) {
++ tb_free_unplugged_xdomains(port->remote->sw);
++ }
++ }
++}
++
+ static int tb_resume_noirq(struct tb *tb)
+ {
+ struct tb_cm *tcm = tb_priv(tb);
+@@ -2806,6 +2826,7 @@ static int tb_resume_noirq(struct tb *tb
+ tb_switch_resume(tb->root_switch, false);
+ tb_free_invalid_tunnels(tb);
+ tb_free_unplugged_children(tb->root_switch);
++ tb_free_unplugged_xdomains(tb->root_switch);
+ tb_restore_children(tb->root_switch);
+
+ /*
+@@ -2848,28 +2869,6 @@ static int tb_resume_noirq(struct tb *tb
+ return 0;
+ }
+
+-static int tb_free_unplugged_xdomains(struct tb_switch *sw)
+-{
+- struct tb_port *port;
+- int ret = 0;
+-
+- tb_switch_for_each_port(sw, port) {
+- if (tb_is_upstream_port(port))
+- continue;
+- if (port->xdomain && port->xdomain->is_unplugged) {
+- tb_retimer_remove_all(port);
+- tb_xdomain_remove(port->xdomain);
+- tb_port_unconfigure_xdomain(port);
+- port->xdomain = NULL;
+- ret++;
+- } else if (port->remote) {
+- ret += tb_free_unplugged_xdomains(port->remote->sw);
+- }
+- }
+-
+- return ret;
+-}
+-
+ static int tb_freeze_noirq(struct tb *tb)
+ {
+ struct tb_cm *tcm = tb_priv(tb);
+@@ -2889,14 +2888,14 @@ static int tb_thaw_noirq(struct tb *tb)
+ static void tb_complete(struct tb *tb)
+ {
+ /*
+- * Release any unplugged XDomains and if there is a case where
++ * Unregister unplugged XDomains and if there is a case where
+ * another domain is swapped in place of unplugged XDomain we
+ * need to run another rescan.
+ */
+- mutex_lock(&tb->lock);
+- if (tb_free_unplugged_xdomains(tb->root_switch))
+- tb_scan_switch(tb->root_switch);
+- mutex_unlock(&tb->lock);
++ if (tb_domain_unregister_unplugged_xdomains(tb)) {
++ scoped_guard(mutex, &tb->lock)
++ tb_scan_switch(tb->root_switch);
++ }
+ }
+
+ static int tb_runtime_suspend(struct tb *tb)
+@@ -2923,11 +2922,11 @@ static void tb_remove_work(struct work_s
+ struct tb *tb = tcm_to_tb(tcm);
+
+ mutex_lock(&tb->lock);
+- if (tb->root_switch) {
++ if (tb->root_switch)
+ tb_free_unplugged_children(tb->root_switch);
+- tb_free_unplugged_xdomains(tb->root_switch);
+- }
+ mutex_unlock(&tb->lock);
++
++ tb_free_unplugged_xdomains(tb->root_switch);
+ }
+
+ static int tb_runtime_resume(struct tb *tb)
+--- a/drivers/thunderbolt/tb.h
++++ b/drivers/thunderbolt/tb.h
+@@ -752,6 +752,7 @@ int tb_domain_disconnect_xdomain_paths(s
+ int transmit_path, int transmit_ring,
+ int receive_path, int receive_ring);
+ int tb_domain_disconnect_all_paths(struct tb *tb);
++int tb_domain_unregister_unplugged_xdomains(struct tb *tb);
+
+ static inline struct tb *tb_domain_get(struct tb *tb)
+ {
+@@ -1200,6 +1201,7 @@ struct tb_xdomain *tb_xdomain_alloc(stru
+ const uuid_t *remote_uuid);
+ void tb_xdomain_add(struct tb_xdomain *xd);
+ void tb_xdomain_remove(struct tb_xdomain *xd);
++void tb_xdomain_unregister(struct tb_xdomain *xd);
+ struct tb_xdomain *tb_xdomain_find_by_link_depth(struct tb *tb, u8 link,
+ u8 depth);
+
+--- a/drivers/thunderbolt/xdomain.c
++++ b/drivers/thunderbolt/xdomain.c
+@@ -2061,41 +2061,54 @@ static int unregister_service(struct dev
+ }
+
+ /**
+- * tb_xdomain_remove() - Remove XDomain from the bus
++ * tb_xdomain_remove() - Remove XDomain
+ * @xd: XDomain to remove
+ *
+- * This will stop all ongoing configuration work and remove the XDomain
+- * along with any services from the bus. When the last reference to @xd
+- * is released the object will be released as well.
++ * This will stop all ongoing configuration work. XDomain is not removed
++ * from the bus if it was added. That needs to be done separately by
++ * calling tb_xdomain_unregister().
++ *
++ * Called with @tb->lock held.
+ */
+ void tb_xdomain_remove(struct tb_xdomain *xd)
+ {
+ tb_xdomain_debugfs_remove(xd);
+-
+ stop_handshake(xd);
+-
+- device_for_each_child_reverse(&xd->dev, xd, unregister_service);
+-
+ tb_xdomain_link_exit(xd);
+
+- /*
+- * Undo runtime PM here explicitly because it is possible that
+- * the XDomain was never added to the bus and thus device_del()
+- * is not called for it (device_del() would handle this otherwise).
+- */
+- pm_runtime_disable(&xd->dev);
+- pm_runtime_put_noidle(&xd->dev);
+- pm_runtime_set_suspended(&xd->dev);
+-
+ if (!device_is_registered(&xd->dev)) {
++ /*
++ * Undo runtime PM here explicitly because it is
++ * possible that the XDomain was never added to the bus
++ * and thus device_del() is not called for it
++ * (device_del() would handle this otherwise).
++ */
++ pm_runtime_disable(&xd->dev);
++ pm_runtime_put_noidle(&xd->dev);
++ pm_runtime_set_suspended(&xd->dev);
+ put_device(&xd->dev);
+- } else {
+- dev_info(&xd->dev, "host disconnected\n");
+- device_unregister(&xd->dev);
+ }
+ }
+
+ /**
++ * tb_xdomain_unregister() - Unregister XDomain
++ * @xd: XDomain to unregister
++ *
++ * This will unregister the XDomain along with any services from the
++ * bus. When the last reference to @xd is released the object will be
++ * released as well.
++ */
++void tb_xdomain_unregister(struct tb_xdomain *xd)
++{
++ lockdep_assert_not_held(&xd->tb->lock);
++
++ device_for_each_child_reverse(&xd->dev, xd, unregister_service);
++
++ dev_info(&xd->dev, "host disconnected\n");
++ device_unregister(&xd->dev);
++}
++
++/**
+ * tb_xdomain_lane_bonding_enable() - Enable lane bonding on XDomain
+ * @xd: XDomain connection
+ *
--- /dev/null
+From stable+bounces-289296-greg=kroah.com@vger.kernel.org Sun Jul 26 16:00:04 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 26 Jul 2026 09:59:51 -0400
+Subject: thunderbolt: Update property.c function documentation
+To: stable@vger.kernel.org
+Cc: Alan Borzeszkowski <alan.borzeszkowski@linux.intel.com>, Mika Westerberg <mika.westerberg@linux.intel.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260726135955.668115-3-sashal@kernel.org>
+
+From: Alan Borzeszkowski <alan.borzeszkowski@linux.intel.com>
+
+[ Upstream commit d015642ad36d78e6eba12d8ab96cea6fd4602b49 ]
+
+Make property.c function documentation compliant with current kernel-doc
+standards. No functional changes.
+
+Signed-off-by: Alan Borzeszkowski <alan.borzeszkowski@linux.intel.com>
+Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
+Stable-dep-of: 2c5d2d3c3f70 ("thunderbolt: Prevent XDomain delayed work use-after-free on disconnect")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/thunderbolt/property.c | 38 ++++++++++++++++++++++++++++----------
+ 1 file changed, 28 insertions(+), 10 deletions(-)
+
+--- a/drivers/thunderbolt/property.c
++++ b/drivers/thunderbolt/property.c
+@@ -231,11 +231,13 @@ static struct tb_property_dir *__tb_prop
+ *
+ * This function parses the XDomain properties data block into format that
+ * can be traversed using the helper functions provided by this module.
+- * Upon success returns the parsed directory. In case of error returns
+- * %NULL. The resulting &struct tb_property_dir needs to be released by
++ *
++ * The resulting &struct tb_property_dir needs to be released by
+ * calling tb_property_free_dir() when not needed anymore.
+ *
+ * The @block is expected to be root directory.
++ *
++ * Return: Pointer to &struct tb_property_dir, %NULL in case of failure.
+ */
+ struct tb_property_dir *tb_property_parse_dir(const u32 *block,
+ size_t block_len)
+@@ -258,6 +260,8 @@ struct tb_property_dir *tb_property_pars
+ *
+ * Creates new, empty property directory. If @uuid is %NULL then the
+ * directory is assumed to be root directory.
++ *
++ * Return: Pointer to &struct tb_property_dir, %NULL in case of failure.
+ */
+ struct tb_property_dir *tb_property_create_dir(const uuid_t *uuid)
+ {
+@@ -501,9 +505,11 @@ static ssize_t __tb_property_format_dir(
+ * @block_len: Length of the property block
+ *
+ * This function formats the directory to the packed format that can be
+- * then send over the thunderbolt fabric to receiving host. Returns %0 in
+- * case of success and negative errno on faulure. Passing %NULL in @block
+- * returns number of entries the block takes.
++ * then sent over the thunderbolt fabric to receiving host.
++ *
++ * Passing %NULL in @block returns number of entries the block takes.
++ *
++ * Return: %0 on success, negative errno otherwise.
+ */
+ ssize_t tb_property_format_dir(const struct tb_property_dir *dir, u32 *block,
+ size_t block_len)
+@@ -525,9 +531,9 @@ ssize_t tb_property_format_dir(const str
+ * tb_property_copy_dir() - Take a deep copy of directory
+ * @dir: Directory to copy
+ *
+- * This function takes a deep copy of @dir and returns back the copy. In
+- * case of error returns %NULL. The resulting directory needs to be
+- * released by calling tb_property_free_dir().
++ * The resulting directory needs to be released by calling tb_property_free_dir().
++ *
++ * Return: Pointer to &struct tb_property_dir, %NULL in case of failure.
+ */
+ struct tb_property_dir *tb_property_copy_dir(const struct tb_property_dir *dir)
+ {
+@@ -597,6 +603,8 @@ err_free:
+ * @parent: Directory to add the property
+ * @key: Key for the property
+ * @value: Immediate value to store with the property
++ *
++ * Return: %0 on success, negative errno otherwise.
+ */
+ int tb_property_add_immediate(struct tb_property_dir *parent, const char *key,
+ u32 value)
+@@ -626,6 +634,8 @@ EXPORT_SYMBOL_GPL(tb_property_add_immedi
+ * @buflen: Number of bytes in the data buffer
+ *
+ * Function takes a copy of @buf and adds it to the directory.
++ *
++ * Return: %0 on success, negative errno otherwise.
+ */
+ int tb_property_add_data(struct tb_property_dir *parent, const char *key,
+ const void *buf, size_t buflen)
+@@ -662,6 +672,8 @@ EXPORT_SYMBOL_GPL(tb_property_add_data);
+ * @text: String to add
+ *
+ * Function takes a copy of @text and adds it to the directory.
++ *
++ * Return: %0 on success, negative errno otherwise.
+ */
+ int tb_property_add_text(struct tb_property_dir *parent, const char *key,
+ const char *text)
+@@ -696,6 +708,8 @@ EXPORT_SYMBOL_GPL(tb_property_add_text);
+ * @parent: Directory to add the property
+ * @key: Key for the property
+ * @dir: Directory to add
++ *
++ * Return: %0 on success, negative errno otherwise.
+ */
+ int tb_property_add_dir(struct tb_property_dir *parent, const char *key,
+ struct tb_property_dir *dir)
+@@ -736,8 +750,10 @@ EXPORT_SYMBOL_GPL(tb_property_remove);
+ * @key: Key to look for
+ * @type: Type of the property
+ *
+- * Finds and returns property from the given directory. Does not recurse
+- * into sub-directories. Returns %NULL if the property was not found.
++ * Finds and returns property from the given directory. Does not
++ * recurse into sub-directories.
++ *
++ * Return: Pointer to &struct tb_property, %NULL if the property was not found.
+ */
+ struct tb_property *tb_property_find(struct tb_property_dir *dir,
+ const char *key, enum tb_property_type type)
+@@ -757,6 +773,8 @@ EXPORT_SYMBOL_GPL(tb_property_find);
+ * tb_property_get_next() - Get next property from directory
+ * @dir: Directory holding properties
+ * @prev: Previous property in the directory (%NULL returns the first)
++ *
++ * Return: Pointer to &struct tb_property, %NULL if property was not found.
+ */
+ struct tb_property *tb_property_get_next(struct tb_property_dir *dir,
+ struct tb_property *prev)
--- /dev/null
+From stable+bounces-288004-greg=kroah.com@vger.kernel.org Wed Jul 22 19:34:35 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 13:29:40 -0400
+Subject: tipc: restrict socket queue dumps in enqueue tracepoints
+To: stable@vger.kernel.org
+Cc: Li Xiasong <lixiasong1@huawei.com>, Tung Nguyen <tung.quang.nguyen@est.tech>, Jakub Kicinski <kuba@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260722172940.2067866-1-sashal@kernel.org>
+
+From: Li Xiasong <lixiasong1@huawei.com>
+
+[ Upstream commit acd7df8d955480a6f6e5bb809da67b1500cc3cf4 ]
+
+tipc_sk_enqueue() runs with sk->sk_lock.slock held while the socket is
+owned by user context. The spinlock protects the backlog queue in this
+path, but it does not serialize against the socket owner consuming or
+purging sk_receive_queue.
+
+KASAN reported:
+
+ CPU: 14 UID: 0 PID: 1050 Comm: tipc3 Not tainted 7.1.0-rc6+ #126 PREEMPT(lazy)
+ Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.15.0-1 04/01/2014
+ Call Trace:
+ <TASK>
+ dump_stack_lvl+0x76/0xa0 lib/dump_stack.c:123
+ print_report+0xce/0x5b0 mm/kasan/report.c:482
+ kasan_report+0xc6/0x100 mm/kasan/report.c:597
+ __asan_report_load4_noabort+0x14/0x30 mm/kasan/report_generic.c:380
+ tipc_skb_dump+0x1327/0x16f0 net/tipc/trace.c:73
+ tipc_list_dump+0x208/0x2e0 net/tipc/trace.c:187
+ tipc_sk_dump+0xaf6/0xd60 net/tipc/socket.c:3996
+ trace_event_raw_event_tipc_sk_class+0x312/0x5a0 net/tipc/trace.h:188
+ tipc_sk_rcv+0xb1d/0x1d50 net/tipc/socket.c:2497
+ tipc_node_xmit+0x1c3/0x1440 net/tipc/node.c:1689
+ __tipc_sendmsg+0x97a/0x1440 net/tipc/socket.c:1512
+ tipc_sendmsg+0x52/0x80 net/tipc/socket.c:1400
+ sock_sendmsg+0x2f6/0x3e0 net/socket.c:825
+ splice_to_socket+0x7f9/0x1010 fs/splice.c:884
+ do_splice+0xe21/0x2330 fs/splice.c:936
+ __do_splice+0x153/0x260 fs/splice.c:1431
+ __x64_sys_splice+0x150/0x230 fs/splice.c:1616
+ x64_sys_call+0xeb5/0x2790 arch/x86/entry/syscall_64.c:41
+ do_syscall_64+0xf3/0x620 arch/x86/entry/syscall_64.c:63
+ entry_SYSCALL_64_after_hwframe+0x76/0x7e arch/x86/entry/entry_64.S:130
+ RIP: 0033:0x71624e8aafe2
+ Code: 08 0f 85 71 3a ff ff 49 89 fb 48 89 f0 48 89 d7 48 89 ce 4c 89 c2 4d 89 ca 4c 8b 44 24 08 4c 8b 4c 24 10 4c 89 5c 24 08 0f 05 <c3> 66 2e 0f 1f 84 00 00 00 00 00 66 2e 0f 1f 84 00 00 00 00 00 66
+ RSP: 002b:0000716157ffed68 EFLAGS: 00000246 ORIG_RAX: 0000000000000113
+ RAX: ffffffffffffffda RBX: 0000716157fff6c0 RCX: 000071624e8aafe2
+ RDX: 000000000000005f RSI: 0000000000000000 RDI: 0000000000000066
+ RBP: 0000716157ffed90 R08: 0000000000008000 R09: 0000000000000001
+ R10: 0000000000000000 R11: 0000000000000246 R12: ffffffffffffff00
+ R13: 0000000000000021 R14: 0000000000000000 R15: 00007fff89799c40
+ </TASK>
+
+The TIPC_DUMP_ALL tracepoints in tipc_sk_enqueue() also dump
+sk_receive_queue and can therefore dereference skbs that the socket
+owner has already dequeued or freed. Restrict these dumps to
+TIPC_DUMP_SK_BKLGQ, which matches the queue protected by the held
+spinlock.
+
+Keep the change limited to the enqueue path, where the unsafe queue dump
+is reachable while the socket is owned by user context.
+
+Fixes: 01e661ebfbad ("tipc: add trace_events for tipc socket")
+Cc: stable@vger.kernel.org
+Signed-off-by: Li Xiasong <lixiasong1@huawei.com>
+Reviewed-by: Tung Nguyen <tung.quang.nguyen@est.tech>
+Link: https://patch.msgid.link/20260611135647.3666727-1-lixiasong1@huawei.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/tipc/socket.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+--- a/net/tipc/socket.c
++++ b/net/tipc/socket.c
+@@ -2461,17 +2461,17 @@ static void tipc_sk_enqueue(struct sk_bu
+ atomic_set(dcnt, 0);
+ lim = rcvbuf_limit(sk, skb) + atomic_read(dcnt);
+ if (likely(!sk_add_backlog(sk, skb, lim))) {
+- trace_tipc_sk_overlimit1(sk, skb, TIPC_DUMP_ALL,
++ trace_tipc_sk_overlimit1(sk, skb, TIPC_DUMP_SK_BKLGQ,
+ "bklg & rcvq >90% allocated!");
+ continue;
+ }
+
+- trace_tipc_sk_dump(sk, skb, TIPC_DUMP_ALL, "err_overload!");
++ trace_tipc_sk_dump(sk, skb, TIPC_DUMP_SK_BKLGQ, "err_overload!");
+ /* Overload => reject message back to sender */
+ onode = tipc_own_addr(sock_net(sk));
+ atomic_inc(&sk->sk_drops);
+ if (tipc_msg_reverse(onode, &skb, TIPC_ERR_OVERLOAD)) {
+- trace_tipc_sk_rej_msg(sk, skb, TIPC_DUMP_ALL,
++ trace_tipc_sk_rej_msg(sk, skb, TIPC_DUMP_SK_BKLGQ,
+ "@sk_enqueue!");
+ __skb_queue_tail(xmitq, skb);
+ }
--- /dev/null
+From stable+bounces-289751-greg=kroah.com@vger.kernel.org Tue Jul 28 01:40:14 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 19:37:50 -0400
+Subject: tracing/user_events: Fix use-after-free in user_event_mm_dup()
+To: stable@vger.kernel.org
+Cc: Michael Bommarito <michael.bommarito@gmail.com>, XIAO WU <xiaowu.417@qq.com>, Beau Belgrave <beaub@linux.microsoft.com>, Steven Rostedt <rostedt@goodmis.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260727233750.2439387-3-sashal@kernel.org>
+
+From: Michael Bommarito <michael.bommarito@gmail.com>
+
+[ Upstream commit 50fd6dd755c6e48a38af2fa4621167eea56829c2 ]
+
+user_event_mm_dup() walks the parent mm's enabler list locklessly under
+rcu_read_lock() during fork() (from copy_process()); it does not take
+event_mutex:
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(enabler, &old_mm->enablers, mm_enablers_link)
+ enabler->event = user_event_get(orig->event);
+
+user_event_enabler_destroy() removes an enabler from that list with
+list_del_rcu() and then, without waiting for a grace period, drops the
+enabler's user_event reference with user_event_put() and frees the enabler
+with kfree(). A reader that loaded the enabler before the list_del_rcu()
+can still be walking it, which leads to two use-after-frees:
+
+ - kfree(enabler) frees the enabler while that reader dereferences
+ enabler->event.
+
+ - user_event_put() may drop the last reference to the user_event, which
+ is then freed (via delayed_destroy_user_event() on a work queue), while
+ the same reader does user_event_get(orig->event) on it.
+
+Both are reachable by an unprivileged task that can open user_events_data:
+one multithreaded process that registers an enabler and then concurrently
+unregisters it and calls fork() triggers the race. KASAN reports a
+slab-use-after-free in user_event_mm_dup() during clone(), with a
+"refcount_t: addition on 0" warning when the user_event is freed.
+
+The enabler use-after-free was found first; the user_event one was reported
+by XIAO WU, and the earlier enabler-only fix did not address it.
+
+Defer both the user_event_put() and the kfree(enabler) to a work item
+queued with queue_rcu_work(), so they run only after an RCU grace period,
+once all readers walking the enabler list have finished. The put must run
+in process context because user_event_put() takes event_mutex on the last
+reference, so a work queue is used rather than call_rcu(). The now-unlocked
+put lets the locked argument of user_event_enabler_destroy() be removed;
+all callers are updated.
+
+Fixes: 7235759084a4 ("tracing/user_events: Use remote writes for event enablement")
+Cc: stable@vger.kernel.org
+Link: https://patch.msgid.link/20260707165912.2560537-2-michael.bommarito@gmail.com
+Reported-by: XIAO WU <xiaowu.417@qq.com>
+Closes: https://lore.kernel.org/all/tencent_89647CE40DC452B891C65C94D1B271DE8E07@qq.com/
+Suggested-by: Beau Belgrave <beaub@linux.microsoft.com>
+Assisted-by: Claude:claude-opus-4-8
+Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
+Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ kernel/trace/trace_events_user.c | 39 ++++++++++++++++++++++++++++++++-------
+ 1 file changed, 32 insertions(+), 7 deletions(-)
+
+--- a/kernel/trace/trace_events_user.c
++++ b/kernel/trace/trace_events_user.c
+@@ -104,6 +104,9 @@ struct user_event_enabler {
+
+ /* Track enable bit, flags, etc. Aligned for bitops. */
+ unsigned long values;
++
++ /* Defer the event put and enabler free past an RCU grace period. */
++ struct rcu_work put_rwork;
+ };
+
+ /* Bits 0-5 are for the bit to update upon enable/disable (0-63 allowed) */
+@@ -378,17 +381,39 @@ error:
+ return NULL;
+ };
+
+-static void user_event_enabler_destroy(struct user_event_enabler *enabler,
+- bool locked)
++static void delayed_user_event_enabler_put(struct work_struct *work)
+ {
+- list_del_rcu(&enabler->mm_enablers_link);
++ struct user_event_enabler *enabler = container_of(to_rcu_work(work),
++ struct user_event_enabler, put_rwork);
+
+ /* No longer tracking the event via the enabler */
+- user_event_put(enabler->event, locked);
++ user_event_put(enabler->event, false);
+
++ /* Run from queue_rcu_work(), the RCU grace period has elapsed */
+ kfree(enabler);
+ }
+
++static void user_event_enabler_destroy(struct user_event_enabler *enabler)
++{
++ list_del_rcu(&enabler->mm_enablers_link);
++
++ /*
++ * The enabler is removed from an RCU-traversed list
++ * (user_event_mm_dup() walks mm->enablers under rcu_read_lock() only),
++ * and readers there dereference enabler->event and take a new ref on
++ * it. Both the put of that event reference and the free of the enabler
++ * therefore have to wait for a grace period so no reader can be looking
++ * at the enabler or racing the last put of its event.
++ *
++ * The put itself must not run in RCU context: when it drops the last
++ * reference user_event_put() takes event_mutex, which cannot be taken
++ * from a softirq/RCU callback. Defer both to a work item scheduled
++ * after a grace period via queue_rcu_work().
++ */
++ INIT_RCU_WORK(&enabler->put_rwork, delayed_user_event_enabler_put);
++ queue_rcu_work(system_percpu_wq, &enabler->put_rwork);
++}
++
+ static int user_event_mm_fault_in(struct user_event_mm *mm, unsigned long uaddr,
+ int attempt)
+ {
+@@ -446,7 +471,7 @@ static void user_event_enabler_fault_fix
+
+ /* User asked for enabler to be removed during fault */
+ if (test_bit(ENABLE_VAL_FREEING_BIT, ENABLE_BITOPS(enabler))) {
+- user_event_enabler_destroy(enabler, true);
++ user_event_enabler_destroy(enabler);
+ goto out;
+ }
+
+@@ -746,7 +771,7 @@ static void user_event_mm_destroy(struct
+ struct user_event_enabler *enabler, *next;
+
+ list_for_each_entry_safe(enabler, next, &mm->enablers, mm_enablers_link)
+- user_event_enabler_destroy(enabler, false);
++ user_event_enabler_destroy(enabler);
+
+ mmdrop(mm->mm);
+ kfree(mm);
+@@ -2579,7 +2604,7 @@ static long user_events_ioctl_unreg(unsi
+ flags |= enabler->values & ENABLE_VAL_COMPAT_MASK;
+
+ if (!test_bit(ENABLE_VAL_FAULTING_BIT, ENABLE_BITOPS(enabler)))
+- user_event_enabler_destroy(enabler, true);
++ user_event_enabler_destroy(enabler);
+
+ /* Removed at least one */
+ ret = 0;
--- /dev/null
+From stable+bounces-281368-greg=kroah.com@vger.kernel.org Tue Jul 21 23:37:19 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 21 Jul 2026 14:28:02 -0400
+Subject: udmabuf: Do not create malformed scatterlists
+To: stable@vger.kernel.org
+Cc: Jason Gunthorpe <jgg@nvidia.com>, Julian Orth <ju.orth@gmail.com>, Vivek Kasireddy <vivek.kasireddy@intel.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260721182803.332607-1-sashal@kernel.org>
+
+From: Jason Gunthorpe <jgg@nvidia.com>
+
+[ Upstream commit 5bf888673e0dda5a53220fa0c4956271a46c353c ]
+
+Using a sg_set_folio() loop for every 4K results in a malformed scatterlist
+because sg_set_folio() has an issue with offsets > PAGE_SIZE and because
+scatterlist expects the creator to build a list which consolidates any
+physical contiguity.
+
+sg_alloc_table_from_pages() creates a valid scatterlist directly from a
+struct page array, so go back to that.
+
+Remove the offsets allocation and just store an array of tail pages as it
+did before the below commit. Everything wants that anyhow.
+
+Fixes: 0c8b91ef5100 ("udmabuf: add back support for mapping hugetlb pages")
+Reported-by: Julian Orth <ju.orth@gmail.com>
+Closes: https://lore.kernel.org/all/20260308-scatterlist-v1-1-39c4566b0bba@gmail.com/
+Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
+Reviewed-by: Vivek Kasireddy <vivek.kasireddy@intel.com>
+Signed-off-by: Vivek Kasireddy <vivek.kasireddy@intel.com>
+Link: https://patch.msgid.link/0-v1-42779f29381a+4b9-udmabuf_sg_jgg@nvidia.com
+Stable-dep-of: 504e2b4ab97a ("dma-buf/udmabuf: skip redundant cpu sync to fix cacheline EEXIST warning")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/dma-buf/udmabuf.c | 8 +++++---
+ 1 file changed, 5 insertions(+), 3 deletions(-)
+
+--- a/drivers/dma-buf/udmabuf.c
++++ b/drivers/dma-buf/udmabuf.c
+@@ -100,14 +100,16 @@ static struct sg_table *get_sg_table(str
+ 0, ubuf->pagecount << PAGE_SHIFT,
+ GFP_KERNEL);
+ if (ret < 0)
+- goto err;
++ goto err_alloc;
++
+ ret = dma_map_sgtable(dev, sg, direction, 0);
+ if (ret < 0)
+- goto err;
++ goto err_map;
+ return sg;
+
+-err:
++err_map:
+ sg_free_table(sg);
++err_alloc:
+ kfree(sg);
+ return ERR_PTR(ret);
+ }
--- /dev/null
+From stable+bounces-288035-greg=kroah.com@vger.kernel.org Wed Jul 22 20:08:36 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 14:00:52 -0400
+Subject: VDUSE: avoid leaking information to userspace
+To: stable@vger.kernel.org
+Cc: "Jason Wang" <jasowang@redhat.com>, "Xie Yongji" <xieyongji@bytedance.com>, "Eugenio Pérez" <eperezma@redhat.com>, "Michael S. Tsirkin" <mst@redhat.com>, "Sasha Levin" <sashal@kernel.org>
+Message-ID: <20260722180052.2184339-4-sashal@kernel.org>
+
+From: Jason Wang <jasowang@redhat.com>
+
+[ Upstream commit 9c1523803445ee0348f62b77793266dd981596e0 ]
+
+The bounceing is not necessarily page aligned, so current VDUSE can
+leak kernel information through mapping bounce pages to
+userspace. Allocate bounce pages with __GFP_ZERO to avoid leaking
+information to userspace.
+
+Fixes: 8c773d53fb7b ("vduse: Implement an MMU-based software IOTLB")
+Cc: stable@vger.kernel.org
+Signed-off-by: Jason Wang <jasowang@redhat.com>
+Reviewed-by: Xie Yongji <xieyongji@bytedance.com>
+Reviewed-by: Eugenio Pérez <eperezma@redhat.com>
+Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
+Message-ID: <20260130050750.4050-1-jasowang@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/vdpa/vdpa_user/iova_domain.c | 2 +-
+ drivers/vdpa/vdpa_user/vduse_dev.c | 2 +-
+ 2 files changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/vdpa/vdpa_user/iova_domain.c
++++ b/drivers/vdpa/vdpa_user/iova_domain.c
+@@ -124,7 +124,7 @@ static int vduse_domain_map_bounce_page(
+ if (!map->bounce_page) {
+ head_map = &domain->bounce_maps[(iova & PAGE_MASK) >> BOUNCE_MAP_SHIFT];
+ if (!head_map->bounce_page) {
+- tmp_page = alloc_page(GFP_ATOMIC);
++ tmp_page = alloc_page(GFP_ATOMIC | __GFP_ZERO);
+ if (!tmp_page)
+ return -ENOMEM;
+ if (cmpxchg(&head_map->bounce_page, NULL, tmp_page))
+--- a/drivers/vdpa/vdpa_user/vduse_dev.c
++++ b/drivers/vdpa/vdpa_user/vduse_dev.c
+@@ -855,7 +855,7 @@ static void *vduse_dev_alloc_coherent(st
+
+ *dma_addr = DMA_MAPPING_ERROR;
+
+- addr = alloc_pages_exact(size, flag);
++ addr = alloc_pages_exact(size, flag | __GFP_ZERO);
+ if (!addr)
+ return NULL;
+
--- /dev/null
+From stable+bounces-288032-greg=kroah.com@vger.kernel.org Wed Jul 22 20:08:17 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 14:00:50 -0400
+Subject: vduse: remove unused vaddr parameter of vduse_domain_free_coherent
+To: stable@vger.kernel.org
+Cc: "Eugenio Pérez" <eperezma@redhat.com>, "Michael S. Tsirkin" <mst@redhat.com>, "Sasha Levin" <sashal@kernel.org>
+Message-ID: <20260722180052.2184339-2-sashal@kernel.org>
+
+From: Eugenio Pérez <eperezma@redhat.com>
+
+[ Upstream commit 766e1749c0ef6a09651be9b8a8283d508c322b58 ]
+
+We will modify the function in next patches so let's clean it first.
+
+Signed-off-by: Eugenio Pérez <eperezma@redhat.com>
+Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
+Message-Id: <20260119143306.1818855-9-eperezma@redhat.com>
+Stable-dep-of: 9c1523803445 ("VDUSE: avoid leaking information to userspace")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/vdpa/vdpa_user/iova_domain.c | 3 +--
+ drivers/vdpa/vdpa_user/iova_domain.h | 3 +--
+ drivers/vdpa/vdpa_user/vduse_dev.c | 2 +-
+ 3 files changed, 3 insertions(+), 5 deletions(-)
+
+--- a/drivers/vdpa/vdpa_user/iova_domain.c
++++ b/drivers/vdpa/vdpa_user/iova_domain.c
+@@ -514,8 +514,7 @@ err:
+ }
+
+ void vduse_domain_free_coherent(struct vduse_iova_domain *domain, size_t size,
+- void *vaddr, dma_addr_t dma_addr,
+- unsigned long attrs)
++ dma_addr_t dma_addr, unsigned long attrs)
+ {
+ struct iova_domain *iovad = &domain->consistent_iovad;
+ struct vhost_iotlb_map *map;
+--- a/drivers/vdpa/vdpa_user/iova_domain.h
++++ b/drivers/vdpa/vdpa_user/iova_domain.h
+@@ -63,8 +63,7 @@ void *vduse_domain_alloc_coherent(struct
+ gfp_t flag, unsigned long attrs);
+
+ void vduse_domain_free_coherent(struct vduse_iova_domain *domain, size_t size,
+- void *vaddr, dma_addr_t dma_addr,
+- unsigned long attrs);
++ dma_addr_t dma_addr, unsigned long attrs);
+
+ void vduse_domain_reset_bounce_map(struct vduse_iova_domain *domain);
+
+--- a/drivers/vdpa/vdpa_user/vduse_dev.c
++++ b/drivers/vdpa/vdpa_user/vduse_dev.c
+@@ -872,7 +872,7 @@ static void vduse_dev_free_coherent(stru
+ struct vduse_dev *vdev = dev_to_vduse(dev);
+ struct vduse_iova_domain *domain = vdev->domain;
+
+- vduse_domain_free_coherent(domain, size, vaddr, dma_addr, attrs);
++ vduse_domain_free_coherent(domain, size, dma_addr, attrs);
+ }
+
+ static size_t vduse_dev_max_mapping_size(struct device *dev)
--- /dev/null
+From stable+bounces-288033-greg=kroah.com@vger.kernel.org Wed Jul 22 20:08:27 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 14:00:51 -0400
+Subject: vduse: take out allocations from vduse_dev_alloc_coherent
+To: stable@vger.kernel.org
+Cc: "Eugenio Pérez" <eperezma@redhat.com>, "Jason Wang" <jasowang@redhat.com>, "Michael S. Tsirkin" <mst@redhat.com>, "Sasha Levin" <sashal@kernel.org>
+Message-ID: <20260722180052.2184339-3-sashal@kernel.org>
+
+From: Eugenio Pérez <eperezma@redhat.com>
+
+[ Upstream commit 489d76520612abf9a4ede4344349105406c91a73 ]
+
+The function vduse_dev_alloc_coherent will be called under rwlock in
+next patches. Make it out of the lock to avoid increasing its fail
+rate.
+
+Acked-by: Jason Wang <jasowang@redhat.com>
+Signed-off-by: Eugenio Pérez <eperezma@redhat.com>
+Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
+Message-Id: <20260119143306.1818855-10-eperezma@redhat.com>
+Stable-dep-of: 9c1523803445 ("VDUSE: avoid leaking information to userspace")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/vdpa/vdpa_user/iova_domain.c | 24 +++++++-----------------
+ drivers/vdpa/vdpa_user/iova_domain.h | 5 ++---
+ drivers/vdpa/vdpa_user/vduse_dev.c | 14 ++++++++++----
+ 3 files changed, 19 insertions(+), 24 deletions(-)
+
+--- a/drivers/vdpa/vdpa_user/iova_domain.c
++++ b/drivers/vdpa/vdpa_user/iova_domain.c
+@@ -479,17 +479,15 @@ void vduse_domain_unmap_page(struct vdus
+ vduse_domain_free_iova(iovad, dma_addr, size);
+ }
+
+-void *vduse_domain_alloc_coherent(struct vduse_iova_domain *domain,
+- size_t size, dma_addr_t *dma_addr,
+- gfp_t flag, unsigned long attrs)
++dma_addr_t vduse_domain_alloc_coherent(struct vduse_iova_domain *domain,
++ size_t size, void *orig)
+ {
+ struct iova_domain *iovad = &domain->consistent_iovad;
+ unsigned long limit = domain->iova_limit;
+ dma_addr_t iova = vduse_domain_alloc_iova(iovad, size, limit);
+- void *orig = alloc_pages_exact(size, flag);
+
+- if (!iova || !orig)
+- goto err;
++ if (!iova)
++ return DMA_MAPPING_ERROR;
+
+ spin_lock(&domain->iotlb_lock);
+ if (vduse_iotlb_add_range(domain, (u64)iova, (u64)iova + size - 1,
+@@ -500,17 +498,12 @@ void *vduse_domain_alloc_coherent(struct
+ }
+ spin_unlock(&domain->iotlb_lock);
+
+- *dma_addr = iova;
++ return iova;
+
+- return orig;
+ err:
+- *dma_addr = DMA_MAPPING_ERROR;
+- if (orig)
+- free_pages_exact(orig, size);
+- if (iova)
+- vduse_domain_free_iova(iovad, iova, size);
++ vduse_domain_free_iova(iovad, iova, size);
+
+- return NULL;
++ return DMA_MAPPING_ERROR;
+ }
+
+ void vduse_domain_free_coherent(struct vduse_iova_domain *domain, size_t size,
+@@ -519,7 +512,6 @@ void vduse_domain_free_coherent(struct v
+ struct iova_domain *iovad = &domain->consistent_iovad;
+ struct vhost_iotlb_map *map;
+ struct vdpa_map_file *map_file;
+- phys_addr_t pa;
+
+ spin_lock(&domain->iotlb_lock);
+ map = vhost_iotlb_itree_first(domain->iotlb, (u64)dma_addr,
+@@ -531,12 +523,10 @@ void vduse_domain_free_coherent(struct v
+ map_file = (struct vdpa_map_file *)map->opaque;
+ fput(map_file->file);
+ kfree(map_file);
+- pa = map->addr;
+ vhost_iotlb_map_free(domain->iotlb, map);
+ spin_unlock(&domain->iotlb_lock);
+
+ vduse_domain_free_iova(iovad, dma_addr, size);
+- free_pages_exact(phys_to_virt(pa), size);
+ }
+
+ static vm_fault_t vduse_domain_mmap_fault(struct vm_fault *vmf)
+--- a/drivers/vdpa/vdpa_user/iova_domain.h
++++ b/drivers/vdpa/vdpa_user/iova_domain.h
+@@ -58,9 +58,8 @@ void vduse_domain_unmap_page(struct vdus
+ dma_addr_t dma_addr, size_t size,
+ enum dma_data_direction dir, unsigned long attrs);
+
+-void *vduse_domain_alloc_coherent(struct vduse_iova_domain *domain,
+- size_t size, dma_addr_t *dma_addr,
+- gfp_t flag, unsigned long attrs);
++dma_addr_t vduse_domain_alloc_coherent(struct vduse_iova_domain *domain,
++ size_t size, void *orig);
+
+ void vduse_domain_free_coherent(struct vduse_iova_domain *domain, size_t size,
+ dma_addr_t dma_addr, unsigned long attrs);
+--- a/drivers/vdpa/vdpa_user/vduse_dev.c
++++ b/drivers/vdpa/vdpa_user/vduse_dev.c
+@@ -851,18 +851,23 @@ static void *vduse_dev_alloc_coherent(st
+ {
+ struct vduse_dev *vdev = dev_to_vduse(dev);
+ struct vduse_iova_domain *domain = vdev->domain;
+- unsigned long iova;
+ void *addr;
+
+ *dma_addr = DMA_MAPPING_ERROR;
+- addr = vduse_domain_alloc_coherent(domain, size,
+- (dma_addr_t *)&iova, flag, attrs);
++
++ addr = alloc_pages_exact(size, flag);
+ if (!addr)
+ return NULL;
+
+- *dma_addr = (dma_addr_t)iova;
++ *dma_addr = vduse_domain_alloc_coherent(domain, size, addr);
++ if (*dma_addr == DMA_MAPPING_ERROR)
++ goto err;
+
+ return addr;
++
++err:
++ free_pages_exact(addr, size);
++ return NULL;
+ }
+
+ static void vduse_dev_free_coherent(struct device *dev, size_t size,
+@@ -873,6 +878,7 @@ static void vduse_dev_free_coherent(stru
+ struct vduse_iova_domain *domain = vdev->domain;
+
+ vduse_domain_free_coherent(domain, size, dma_addr, attrs);
++ free_pages_exact(vaddr, size);
+ }
+
+ static size_t vduse_dev_max_mapping_size(struct device *dev)
--- /dev/null
+From stable+bounces-288031-greg=kroah.com@vger.kernel.org Wed Jul 22 20:08:19 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jul 2026 14:00:49 -0400
+Subject: vduse: Use fixed 4KB bounce pages for non-4KB page size
+To: stable@vger.kernel.org
+Cc: Sheng Zhao <sheng.zhao@bytedance.com>, "Michael S. Tsirkin" <mst@redhat.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260722180052.2184339-1-sashal@kernel.org>
+
+From: Sheng Zhao <sheng.zhao@bytedance.com>
+
+[ Upstream commit 3fc3068e7247c94dec08e93fea422a1bb649bfe5 ]
+
+The allocation granularity of bounce pages is PAGE_SIZE. This may cause
+even small IO requests to occupy an entire bounce page exclusively. The
+kind of memory waste will be more significant when PAGE_SIZE is larger
+than 4KB (e.g. arm64 with 64KB pages).
+
+So, optimize it by using fixed 4KB bounce maps and iova allocation
+granularity. A single IO request occupies at least a 4KB bounce page
+instead of the entire memory page of PAGE_SIZE.
+
+Signed-off-by: Sheng Zhao <sheng.zhao@bytedance.com>
+Message-Id: <20250925113516.60305-1-sheng.zhao@bytedance.com>
+Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
+Stable-dep-of: 9c1523803445 ("VDUSE: avoid leaking information to userspace")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/vdpa/vdpa_user/iova_domain.c | 134 +++++++++++++++++++++++++----------
+ drivers/vdpa/vdpa_user/iova_domain.h | 5 +
+ 2 files changed, 101 insertions(+), 38 deletions(-)
+
+--- a/drivers/vdpa/vdpa_user/iova_domain.c
++++ b/drivers/vdpa/vdpa_user/iova_domain.c
+@@ -103,19 +103,38 @@ void vduse_domain_clear_map(struct vduse
+ static int vduse_domain_map_bounce_page(struct vduse_iova_domain *domain,
+ u64 iova, u64 size, u64 paddr)
+ {
+- struct vduse_bounce_map *map;
++ struct vduse_bounce_map *map, *head_map;
++ struct page *tmp_page;
+ u64 last = iova + size - 1;
+
+ while (iova <= last) {
+- map = &domain->bounce_maps[iova >> PAGE_SHIFT];
++ /*
++ * When PAGE_SIZE is larger than 4KB, multiple adjacent bounce_maps will
++ * point to the same memory page of PAGE_SIZE. Since bounce_maps originate
++ * from IO requests, we may not be able to guarantee that the orig_phys
++ * values of all IO requests within the same 64KB memory page are contiguous.
++ * Therefore, we need to store them separately.
++ *
++ * Bounce pages are allocated on demand. As a result, it may occur that
++ * multiple bounce pages corresponding to the same 64KB memory page attempt
++ * to allocate memory simultaneously, so we use cmpxchg to handle this
++ * concurrency.
++ */
++ map = &domain->bounce_maps[iova >> BOUNCE_MAP_SHIFT];
+ if (!map->bounce_page) {
+- map->bounce_page = alloc_page(GFP_ATOMIC);
+- if (!map->bounce_page)
+- return -ENOMEM;
++ head_map = &domain->bounce_maps[(iova & PAGE_MASK) >> BOUNCE_MAP_SHIFT];
++ if (!head_map->bounce_page) {
++ tmp_page = alloc_page(GFP_ATOMIC);
++ if (!tmp_page)
++ return -ENOMEM;
++ if (cmpxchg(&head_map->bounce_page, NULL, tmp_page))
++ __free_page(tmp_page);
++ }
++ map->bounce_page = head_map->bounce_page;
+ }
+ map->orig_phys = paddr;
+- paddr += PAGE_SIZE;
+- iova += PAGE_SIZE;
++ paddr += BOUNCE_MAP_SIZE;
++ iova += BOUNCE_MAP_SIZE;
+ }
+ return 0;
+ }
+@@ -127,12 +146,17 @@ static void vduse_domain_unmap_bounce_pa
+ u64 last = iova + size - 1;
+
+ while (iova <= last) {
+- map = &domain->bounce_maps[iova >> PAGE_SHIFT];
++ map = &domain->bounce_maps[iova >> BOUNCE_MAP_SHIFT];
+ map->orig_phys = INVALID_PHYS_ADDR;
+- iova += PAGE_SIZE;
++ iova += BOUNCE_MAP_SIZE;
+ }
+ }
+
++static unsigned int offset_in_bounce_page(dma_addr_t addr)
++{
++ return (addr & ~BOUNCE_MAP_MASK);
++}
++
+ static void do_bounce(phys_addr_t orig, void *addr, size_t size,
+ enum dma_data_direction dir)
+ {
+@@ -162,7 +186,7 @@ static void vduse_domain_bounce(struct v
+ enum dma_data_direction dir)
+ {
+ struct vduse_bounce_map *map;
+- unsigned int offset;
++ unsigned int offset, head_offset;
+ void *addr;
+ size_t sz;
+
+@@ -170,16 +194,17 @@ static void vduse_domain_bounce(struct v
+ return;
+
+ while (size) {
+- map = &domain->bounce_maps[iova >> PAGE_SHIFT];
+- offset = offset_in_page(iova);
+- sz = min_t(size_t, PAGE_SIZE - offset, size);
++ map = &domain->bounce_maps[iova >> BOUNCE_MAP_SHIFT];
++ head_offset = offset_in_page(iova);
++ offset = offset_in_bounce_page(iova);
++ sz = min_t(size_t, BOUNCE_MAP_SIZE - offset, size);
+
+ if (WARN_ON(!map->bounce_page ||
+ map->orig_phys == INVALID_PHYS_ADDR))
+ return;
+
+ addr = kmap_local_page(map->bounce_page);
+- do_bounce(map->orig_phys + offset, addr + offset, sz, dir);
++ do_bounce(map->orig_phys + offset, addr + head_offset, sz, dir);
+ kunmap_local(addr);
+ size -= sz;
+ iova += sz;
+@@ -214,7 +239,7 @@ vduse_domain_get_bounce_page(struct vdus
+ struct page *page = NULL;
+
+ read_lock(&domain->bounce_lock);
+- map = &domain->bounce_maps[iova >> PAGE_SHIFT];
++ map = &domain->bounce_maps[iova >> BOUNCE_MAP_SHIFT];
+ if (domain->user_bounce_pages || !map->bounce_page)
+ goto out;
+
+@@ -232,7 +257,7 @@ vduse_domain_free_kernel_bounce_pages(st
+ struct vduse_bounce_map *map;
+ unsigned long pfn, bounce_pfns;
+
+- bounce_pfns = domain->bounce_size >> PAGE_SHIFT;
++ bounce_pfns = domain->bounce_size >> BOUNCE_MAP_SHIFT;
+
+ for (pfn = 0; pfn < bounce_pfns; pfn++) {
+ map = &domain->bounce_maps[pfn];
+@@ -242,7 +267,8 @@ vduse_domain_free_kernel_bounce_pages(st
+ if (!map->bounce_page)
+ continue;
+
+- __free_page(map->bounce_page);
++ if (!((pfn << BOUNCE_MAP_SHIFT) & ~PAGE_MASK))
++ __free_page(map->bounce_page);
+ map->bounce_page = NULL;
+ }
+ }
+@@ -250,8 +276,12 @@ vduse_domain_free_kernel_bounce_pages(st
+ int vduse_domain_add_user_bounce_pages(struct vduse_iova_domain *domain,
+ struct page **pages, int count)
+ {
+- struct vduse_bounce_map *map;
+- int i, ret;
++ struct vduse_bounce_map *map, *head_map;
++ int i, j, ret;
++ int inner_pages = PAGE_SIZE / BOUNCE_MAP_SIZE;
++ int bounce_pfns = domain->bounce_size >> BOUNCE_MAP_SHIFT;
++ struct page *head_page = NULL;
++ bool need_copy;
+
+ /* Now we don't support partial mapping */
+ if (count != (domain->bounce_size >> PAGE_SHIFT))
+@@ -263,17 +293,25 @@ int vduse_domain_add_user_bounce_pages(s
+ goto out;
+
+ for (i = 0; i < count; i++) {
+- map = &domain->bounce_maps[i];
+- if (map->bounce_page) {
++ need_copy = false;
++ head_map = &domain->bounce_maps[(i * inner_pages)];
++ head_page = head_map->bounce_page;
++ for (j = 0; j < inner_pages; j++) {
++ if ((i * inner_pages + j) >= bounce_pfns)
++ break;
++ map = &domain->bounce_maps[(i * inner_pages + j)];
+ /* Copy kernel page to user page if it's in use */
+- if (map->orig_phys != INVALID_PHYS_ADDR)
+- memcpy_to_page(pages[i], 0,
+- page_address(map->bounce_page),
+- PAGE_SIZE);
+- __free_page(map->bounce_page);
++ if ((head_page) && (map->orig_phys != INVALID_PHYS_ADDR))
++ need_copy = true;
++ map->bounce_page = pages[i];
+ }
+- map->bounce_page = pages[i];
+ get_page(pages[i]);
++ if ((head_page) && (need_copy))
++ memcpy_to_page(pages[i], 0,
++ page_address(head_page),
++ PAGE_SIZE);
++ if (head_page)
++ __free_page(head_page);
+ }
+ domain->user_bounce_pages = true;
+ ret = 0;
+@@ -285,8 +323,12 @@ out:
+
+ void vduse_domain_remove_user_bounce_pages(struct vduse_iova_domain *domain)
+ {
+- struct vduse_bounce_map *map;
+- unsigned long i, count;
++ struct vduse_bounce_map *map, *head_map;
++ unsigned long i, j, count;
++ int inner_pages = PAGE_SIZE / BOUNCE_MAP_SIZE;
++ int bounce_pfns = domain->bounce_size >> BOUNCE_MAP_SHIFT;
++ struct page *head_page = NULL;
++ bool need_copy;
+
+ write_lock(&domain->bounce_lock);
+ if (!domain->user_bounce_pages)
+@@ -296,18 +338,34 @@ void vduse_domain_remove_user_bounce_pag
+ for (i = 0; i < count; i++) {
+ struct page *page = NULL;
+
+- map = &domain->bounce_maps[i];
+- if (WARN_ON(!map->bounce_page))
++ need_copy = false;
++ head_map = &domain->bounce_maps[(i * inner_pages)];
++ if (WARN_ON(!head_map->bounce_page))
+ continue;
++ head_page = head_map->bounce_page;
+
+- /* Copy user page to kernel page if it's in use */
+- if (map->orig_phys != INVALID_PHYS_ADDR) {
++ for (j = 0; j < inner_pages; j++) {
++ if ((i * inner_pages + j) >= bounce_pfns)
++ break;
++ map = &domain->bounce_maps[(i * inner_pages + j)];
++ if (WARN_ON(!map->bounce_page))
++ continue;
++ /* Copy user page to kernel page if it's in use */
++ if (map->orig_phys != INVALID_PHYS_ADDR)
++ need_copy = true;
++ }
++ if (need_copy) {
+ page = alloc_page(GFP_ATOMIC | __GFP_NOFAIL);
+ memcpy_from_page(page_address(page),
+- map->bounce_page, 0, PAGE_SIZE);
++ head_page, 0, PAGE_SIZE);
++ }
++ for (j = 0; j < inner_pages; j++) {
++ if ((i * inner_pages + j) >= bounce_pfns)
++ break;
++ map = &domain->bounce_maps[(i * inner_pages + j)];
++ map->bounce_page = page;
+ }
+- put_page(map->bounce_page);
+- map->bounce_page = page;
++ put_page(head_page);
+ }
+ domain->user_bounce_pages = false;
+ out:
+@@ -557,7 +615,7 @@ vduse_domain_create(unsigned long iova_l
+ unsigned long pfn, bounce_pfns;
+ int ret;
+
+- bounce_pfns = PAGE_ALIGN(bounce_size) >> PAGE_SHIFT;
++ bounce_pfns = PAGE_ALIGN(bounce_size) >> BOUNCE_MAP_SHIFT;
+ if (iova_limit <= bounce_size)
+ return NULL;
+
+@@ -589,7 +647,7 @@ vduse_domain_create(unsigned long iova_l
+ rwlock_init(&domain->bounce_lock);
+ spin_lock_init(&domain->iotlb_lock);
+ init_iova_domain(&domain->stream_iovad,
+- PAGE_SIZE, IOVA_START_PFN);
++ BOUNCE_MAP_SIZE, IOVA_START_PFN);
+ ret = iova_domain_init_rcaches(&domain->stream_iovad);
+ if (ret)
+ goto err_iovad_stream;
+--- a/drivers/vdpa/vdpa_user/iova_domain.h
++++ b/drivers/vdpa/vdpa_user/iova_domain.h
+@@ -19,6 +19,11 @@
+
+ #define INVALID_PHYS_ADDR (~(phys_addr_t)0)
+
++#define BOUNCE_MAP_SHIFT 12
++#define BOUNCE_MAP_SIZE (1 << BOUNCE_MAP_SHIFT)
++#define BOUNCE_MAP_MASK (~(BOUNCE_MAP_SIZE - 1))
++#define BOUNCE_MAP_ALIGN(addr) (((addr) + BOUNCE_MAP_SIZE - 1) & ~(BOUNCE_MAP_SIZE - 1))
++
+ struct vduse_bounce_map {
+ struct page *bounce_page;
+ u64 orig_phys;
--- /dev/null
+From stable+bounces-287676-greg=kroah.com@vger.kernel.org Wed Jul 22 04:46:08 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 21 Jul 2026 20:51:12 -0400
+Subject: VFS/audit: introduce kern_path_parent() for audit
+To: stable@vger.kernel.org
+Cc: NeilBrown <neil@brown.name>, Christian Brauner <brauner@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260722005116.605624-1-sashal@kernel.org>
+
+From: NeilBrown <neil@brown.name>
+
+[ Upstream commit 76a53de6f7ff0641570364234fb4489f4d4fc8e9 ]
+
+audit_alloc_mark() and audit_get_nd() both need to perform a path
+lookup getting the parent dentry (which must exist) and the final
+target (following a LAST_NORM name) which sometimes doesn't need to
+exist.
+
+They don't need the parent to be locked, but use kern_path_locked() or
+kern_path_locked_negative() anyway. This is somewhat misleading to the
+casual reader.
+
+This patch introduces a more targeted function, kern_path_parent(),
+which returns not holding locks. On success the "path" will
+be set to the parent, which must be found, and the return value is the
+dentry of the target, which might be negative.
+
+This will clear the way to rename kern_path_locked() which is
+otherwise only used to prepare for removing something.
+
+It also allows us to remove kern_path_locked_negative(), which is
+transformed into the new kern_path_parent().
+
+Signed-off-by: NeilBrown <neil@brown.name>
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Stable-dep-of: 81905b5acbe7 ("audit: fix recursive locking deadlock in audit_dupe_exe()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/namei.c | 43 +++++++++++++++++++++++++++++++++++++++++++
+ include/linux/namei.h | 1 +
+ kernel/audit.h | 4 ++--
+ kernel/audit_fsnotify.c | 9 +++------
+ kernel/audit_watch.c | 9 ++++++---
+ 5 files changed, 55 insertions(+), 11 deletions(-)
+
+--- a/fs/namei.c
++++ b/fs/namei.c
+@@ -2603,6 +2603,49 @@ static struct dentry *__kern_path_locked
+ return d;
+ }
+
++/**
++ * kern_path_parent: lookup path returning parent and target
++ * @name: path name
++ * @path: path to store parent in
++ *
++ * The path @name should end with a normal component, not "." or ".." or "/".
++ * A lookup is performed and if successful the parent information
++ * is store in @parent and the dentry is returned.
++ *
++ * The dentry maybe negative, the parent will be positive.
++ *
++ * Returns: dentry or error.
++ */
++struct dentry *kern_path_parent(const char *name, struct path *path)
++{
++ struct filename *filename = getname_kernel(name);
++ struct path parent_path;
++ struct dentry *d;
++ struct qstr last;
++ int type, error;
++
++ error = filename_parentat(AT_FDCWD, filename, 0, &parent_path, &last, &type);
++ if (error) {
++ d = ERR_PTR(error);
++ goto out;
++ }
++ if (unlikely(type != LAST_NORM)) {
++ path_put(&parent_path);
++ d = ERR_PTR(-EINVAL);
++ goto out;
++ }
++
++ d = lookup_one_len_unlocked(last.name, parent_path.dentry, last.len);
++ if (IS_ERR(d)) {
++ path_put(&parent_path);
++ goto out;
++ }
++ *path = parent_path;
++out:
++ putname(filename);
++ return d;
++}
++
+ struct dentry *kern_path_locked(const char *name, struct path *path)
+ {
+ struct filename *filename = getname_kernel(name);
+--- a/include/linux/namei.h
++++ b/include/linux/namei.h
+@@ -61,6 +61,7 @@ struct dentry *lookup_one_qstr_excl(cons
+ struct dentry *base,
+ unsigned int flags);
+ extern int kern_path(const char *, unsigned, struct path *);
++struct dentry *kern_path_parent(const char *name, struct path *parent);
+
+ extern struct dentry *kern_path_create(int, const char *, struct path *, unsigned int);
+ extern struct dentry *user_path_create(int, const char __user *, struct path *, unsigned int);
+--- a/kernel/audit.h
++++ b/kernel/audit.h
+@@ -279,8 +279,8 @@ extern struct audit_fsnotify_mark *audit
+ extern char *audit_mark_path(struct audit_fsnotify_mark *mark);
+ extern void audit_remove_mark(struct audit_fsnotify_mark *audit_mark);
+ extern void audit_remove_mark_rule(struct audit_krule *krule);
+-extern int audit_mark_compare(struct audit_fsnotify_mark *mark,
+- unsigned long ino, dev_t dev);
++extern int audit_mark_compare(struct audit_fsnotify_mark *mark, u64 ino,
++ dev_t dev);
+ extern int audit_dupe_exe(struct audit_krule *new, struct audit_krule *old);
+ extern int audit_exe_compare(struct task_struct *tsk,
+ struct audit_fsnotify_mark *mark);
+--- a/kernel/audit_fsnotify.c
++++ b/kernel/audit_fsnotify.c
+@@ -57,7 +57,7 @@ char *audit_mark_path(struct audit_fsnot
+ return mark->path;
+ }
+
+-int audit_mark_compare(struct audit_fsnotify_mark *mark, unsigned long ino, dev_t dev)
++int audit_mark_compare(struct audit_fsnotify_mark *mark, u64 ino, dev_t dev)
+ {
+ if (mark->ino == AUDIT_INO_UNSET)
+ return 0;
+@@ -76,17 +76,14 @@ struct audit_fsnotify_mark *audit_alloc_
+ struct audit_fsnotify_mark *audit_mark;
+ struct path path;
+ struct dentry *dentry;
+- struct inode *inode;
+ int ret;
+
+ if (pathname[0] != '/' || pathname[len-1] == '/')
+ return ERR_PTR(-EINVAL);
+
+- dentry = kern_path_locked(pathname, &path);
++ dentry = kern_path_parent(pathname, &path);
+ if (IS_ERR(dentry))
+ return ERR_CAST(dentry); /* returning an error */
+- inode = path.dentry->d_inode;
+- inode_unlock(inode);
+
+ audit_mark = kzalloc(sizeof(*audit_mark), GFP_KERNEL);
+ if (unlikely(!audit_mark)) {
+@@ -100,7 +97,7 @@ struct audit_fsnotify_mark *audit_alloc_
+ audit_update_mark(audit_mark, dentry->d_inode);
+ audit_mark->rule = krule;
+
+- ret = fsnotify_add_inode_mark(&audit_mark->mark, inode, 0);
++ ret = fsnotify_add_inode_mark(&audit_mark->mark, path.dentry->d_inode, 0);
+ if (ret < 0) {
+ audit_mark->path = NULL;
+ fsnotify_put_mark(&audit_mark->mark);
+--- a/kernel/audit_watch.c
++++ b/kernel/audit_watch.c
+@@ -244,7 +244,7 @@ static void audit_watch_log_rule_change(
+ /* Update inode info in audit rules based on filesystem event. */
+ static void audit_update_watch(struct audit_parent *parent,
+ const struct qstr *dname, dev_t dev,
+- unsigned long ino, unsigned invalidating)
++ u64 ino, unsigned int invalidating)
+ {
+ struct audit_watch *owatch, *nwatch, *nextw;
+ struct audit_krule *r, *nextr;
+@@ -347,15 +347,18 @@ static void audit_remove_parent_watches(
+ /* Get path information necessary for adding watches. */
+ static int audit_get_nd(struct audit_watch *watch, struct path *parent)
+ {
+- struct dentry *d = kern_path_locked(watch->path, parent);
++ struct dentry *d;
++
++ d = kern_path_parent(watch->path, parent);
+ if (IS_ERR(d))
+ return PTR_ERR(d);
++
+ if (d_is_positive(d)) {
+ /* update watch filter fields */
+ watch->dev = d->d_sb->s_dev;
+ watch->ino = d_backing_inode(d)->i_ino;
+ }
+- inode_unlock(d_backing_inode(parent->dentry));
++
+ dput(d);
+ return 0;
+ }
--- /dev/null
+From stable+bounces-289770-greg=kroah.com@vger.kernel.org Tue Jul 28 03:03:28 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 20:57:42 -0400
+Subject: wifi: libertas_tf: fix use-after-free in lbtf_free_adapter()
+To: stable@vger.kernel.org
+Cc: Maoyi Xie <maoyixie.tju@gmail.com>, Johannes Berg <johannes.berg@intel.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260728005742.2689126-1-sashal@kernel.org>
+
+From: Maoyi Xie <maoyixie.tju@gmail.com>
+
+[ Upstream commit aa6dcd5c8dd9ba1d7d0f60093bcda41c0d6d438d ]
+
+lbtf_free_adapter() calls timer_delete(&priv->command_timer), which does
+not wait for a running command_timer_fn() callback. lbtf_free_adapter()
+runs on the teardown path right before ieee80211_free_hw() frees priv,
+both in lbtf_remove_card() and in the probe error path. command_timer is
+armed by mod_timer() in lbtf_cmd() whenever a firmware command is sent.
+command_timer_fn() dereferences priv. If a command times out as the
+device is removed, command_timer_fn() runs concurrently with teardown and
+dereferences priv after it has been freed.
+
+This is the same use-after-free that commit 03cc8f90d053 ("wifi: libertas:
+fix use-after-free in lbs_free_adapter()") fixed in the sibling libertas
+driver. The libertas_tf variant has the identical pattern and was left
+unchanged. Use timer_delete_sync() so any in-flight callback completes
+before priv is freed.
+
+Fixes: 06b16ae53192 ("libertas_tf: main.c, data paths and mac80211 handlers")
+Cc: stable@vger.kernel.org
+Signed-off-by: Maoyi Xie <maoyixie.tju@gmail.com>
+Link: https://patch.msgid.link/178211481807.2212567.8773346114561900100@maoyixie.com
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/net/wireless/marvell/libertas_tf/main.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/net/wireless/marvell/libertas_tf/main.c
++++ b/drivers/net/wireless/marvell/libertas_tf/main.c
+@@ -174,7 +174,7 @@ static void lbtf_free_adapter(struct lbt
+ {
+ lbtf_deb_enter(LBTF_DEB_MAIN);
+ lbtf_free_cmd_buffer(priv);
+- del_timer(&priv->command_timer);
++ timer_delete_sync(&priv->command_timer);
+ lbtf_deb_leave(LBTF_DEB_MAIN);
+ }
+
--- /dev/null
+From stable+bounces-289750-greg=kroah.com@vger.kernel.org Tue Jul 28 01:37:58 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 19:37:49 -0400
+Subject: workqueue: Add system_percpu_wq and system_dfl_wq
+To: stable@vger.kernel.org
+Cc: Marco Crivellari <marco.crivellari@suse.com>, Tejun Heo <tj@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260727233750.2439387-2-sashal@kernel.org>
+
+From: Marco Crivellari <marco.crivellari@suse.com>
+
+[ Upstream commit 128ea9f6ccfb6960293ae4212f4f97165e42222d ]
+
+Currently, if a user enqueue a work item using schedule_delayed_work() the
+used wq is "system_wq" (per-cpu wq) while queue_delayed_work() use
+WORK_CPU_UNBOUND (used when a cpu is not specified). The same applies to
+schedule_work() that is using system_wq and queue_work(), that makes use
+again of WORK_CPU_UNBOUND.
+
+This lack of consistentcy cannot be addressed without refactoring the API.
+
+system_wq is a per-CPU worqueue, yet nothing in its name tells about that
+CPU affinity constraint, which is very often not required by users. Make it
+clear by adding a system_percpu_wq.
+
+system_unbound_wq should be the default workqueue so as not to enforce
+locality constraints for random work whenever it's not required.
+
+Adding system_dfl_wq to encourage its use when unbound work should be used.
+
+Suggested-by: Tejun Heo <tj@kernel.org>
+Signed-off-by: Marco Crivellari <marco.crivellari@suse.com>
+Signed-off-by: Tejun Heo <tj@kernel.org>
+Stable-dep-of: 50fd6dd755c6 ("tracing/user_events: Fix use-after-free in user_event_mm_dup()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ include/linux/workqueue.h | 8 +++++---
+ kernel/workqueue.c | 14 ++++++++++----
+ 2 files changed, 15 insertions(+), 7 deletions(-)
+
+--- a/include/linux/workqueue.h
++++ b/include/linux/workqueue.h
+@@ -410,7 +410,7 @@ enum {
+ /*
+ * System-wide workqueues which are always present.
+ *
+- * system_wq is the one used by schedule[_delayed]_work[_on]().
++ * system_percpu_wq is the one used by schedule[_delayed]_work[_on]().
+ * Multi-CPU multi-threaded. There are users which expect relatively
+ * short queue flush time. Don't queue works which can run for too
+ * long.
+@@ -421,7 +421,7 @@ enum {
+ * system_long_wq is similar to system_wq but may host long running
+ * works. Queue flushing might take relatively long.
+ *
+- * system_unbound_wq is unbound workqueue. Workers are not bound to
++ * system_dfl_wq is unbound workqueue. Workers are not bound to
+ * any specific CPU, not concurrency managed, and all queued works are
+ * executed immediately as long as max_active limit is not reached and
+ * resources are available.
+@@ -435,10 +435,12 @@ enum {
+ * system_power_efficient_wq is identical to system_wq if
+ * 'wq_power_efficient' is disabled. See WQ_POWER_EFFICIENT for more info.
+ */
+-extern struct workqueue_struct *system_wq;
++extern struct workqueue_struct *system_wq; /* use system_percpu_wq, this will be removed */
++extern struct workqueue_struct *system_percpu_wq;
+ extern struct workqueue_struct *system_highpri_wq;
+ extern struct workqueue_struct *system_long_wq;
+ extern struct workqueue_struct *system_unbound_wq;
++extern struct workqueue_struct *system_dfl_wq;
+ extern struct workqueue_struct *system_freezable_wq;
+ extern struct workqueue_struct *system_power_efficient_wq;
+ extern struct workqueue_struct *system_freezable_power_efficient_wq;
+--- a/kernel/workqueue.c
++++ b/kernel/workqueue.c
+@@ -425,12 +425,16 @@ static struct kthread_worker *pwq_releas
+
+ struct workqueue_struct *system_wq __read_mostly;
+ EXPORT_SYMBOL(system_wq);
++struct workqueue_struct *system_percpu_wq __read_mostly;
++EXPORT_SYMBOL(system_percpu_wq);
+ struct workqueue_struct *system_highpri_wq __read_mostly;
+ EXPORT_SYMBOL_GPL(system_highpri_wq);
+ struct workqueue_struct *system_long_wq __read_mostly;
+ EXPORT_SYMBOL_GPL(system_long_wq);
+ struct workqueue_struct *system_unbound_wq __read_mostly;
+ EXPORT_SYMBOL_GPL(system_unbound_wq);
++struct workqueue_struct *system_dfl_wq __read_mostly;
++EXPORT_SYMBOL_GPL(system_dfl_wq);
+ struct workqueue_struct *system_freezable_wq __read_mostly;
+ EXPORT_SYMBOL_GPL(system_freezable_wq);
+ struct workqueue_struct *system_power_efficient_wq __read_mostly;
+@@ -6696,10 +6700,11 @@ void __init workqueue_init_early(void)
+ }
+
+ system_wq = alloc_workqueue("events", 0, 0);
++ system_percpu_wq = alloc_workqueue("events", 0, 0);
+ system_highpri_wq = alloc_workqueue("events_highpri", WQ_HIGHPRI, 0);
+ system_long_wq = alloc_workqueue("events_long", 0, 0);
+- system_unbound_wq = alloc_workqueue("events_unbound", WQ_UNBOUND,
+- WQ_MAX_ACTIVE);
++ system_unbound_wq = alloc_workqueue("events_unbound", WQ_UNBOUND, WQ_MAX_ACTIVE);
++ system_dfl_wq = alloc_workqueue("events_unbound", WQ_UNBOUND, WQ_MAX_ACTIVE);
+ system_freezable_wq = alloc_workqueue("events_freezable",
+ WQ_FREEZABLE, 0);
+ system_power_efficient_wq = alloc_workqueue("events_power_efficient",
+@@ -6707,8 +6712,9 @@ void __init workqueue_init_early(void)
+ system_freezable_power_efficient_wq = alloc_workqueue("events_freezable_power_efficient",
+ WQ_FREEZABLE | WQ_POWER_EFFICIENT,
+ 0);
+- BUG_ON(!system_wq || !system_highpri_wq || !system_long_wq ||
+- !system_unbound_wq || !system_freezable_wq ||
++ BUG_ON(!system_wq || !system_percpu_wq || !system_highpri_wq ||
++ !system_long_wq || !system_unbound_wq || !system_dfl_wq ||
++ !system_freezable_wq ||
+ !system_power_efficient_wq ||
+ !system_freezable_power_efficient_wq);
+ }
--- /dev/null
+From stable+bounces-289749-greg=kroah.com@vger.kernel.org Tue Jul 28 01:40:07 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jul 2026 19:37:48 -0400
+Subject: workqueue: Factor out init_cpu_worker_pool()
+To: stable@vger.kernel.org
+Cc: Tejun Heo <tj@kernel.org>, Allen Pais <allen.lkml@gmail.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260727233750.2439387-1-sashal@kernel.org>
+
+From: Tejun Heo <tj@kernel.org>
+
+[ Upstream commit 2fcdb1b44491e08f5334a92c50e8f362e0d46f91 ]
+
+Factor out init_cpu_worker_pool() from workqueue_init_early(). This is pure
+reorganization in preparation of BH workqueue support.
+
+Signed-off-by: Tejun Heo <tj@kernel.org>
+Tested-by: Allen Pais <allen.lkml@gmail.com>
+Stable-dep-of: 50fd6dd755c6 ("tracing/user_events: Fix use-after-free in user_event_mm_dup()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ kernel/workqueue.c | 32 ++++++++++++++++++--------------
+ 1 file changed, 18 insertions(+), 14 deletions(-)
+
+--- a/kernel/workqueue.c
++++ b/kernel/workqueue.c
+@@ -6609,6 +6609,22 @@ static void __init restrict_unbound_cpum
+ cpumask_and(wq_unbound_cpumask, wq_unbound_cpumask, mask);
+ }
+
++static void __init init_cpu_worker_pool(struct worker_pool *pool, int cpu, int nice)
++{
++ BUG_ON(init_worker_pool(pool));
++ pool->cpu = cpu;
++ cpumask_copy(pool->attrs->cpumask, cpumask_of(cpu));
++ cpumask_copy(pool->attrs->__pod_cpumask, cpumask_of(cpu));
++ pool->attrs->nice = nice;
++ pool->attrs->affn_strict = true;
++ pool->node = cpu_to_node(cpu);
++
++ /* alloc pool ID */
++ mutex_lock(&wq_pool_mutex);
++ BUG_ON(worker_pool_assign_id(pool));
++ mutex_unlock(&wq_pool_mutex);
++}
++
+ /**
+ * workqueue_init_early - early init for workqueue subsystem
+ *
+@@ -6657,20 +6673,8 @@ void __init workqueue_init_early(void)
+ struct worker_pool *pool;
+
+ i = 0;
+- for_each_cpu_worker_pool(pool, cpu) {
+- BUG_ON(init_worker_pool(pool));
+- pool->cpu = cpu;
+- cpumask_copy(pool->attrs->cpumask, cpumask_of(cpu));
+- cpumask_copy(pool->attrs->__pod_cpumask, cpumask_of(cpu));
+- pool->attrs->nice = std_nice[i++];
+- pool->attrs->affn_strict = true;
+- pool->node = cpu_to_node(cpu);
+-
+- /* alloc pool ID */
+- mutex_lock(&wq_pool_mutex);
+- BUG_ON(worker_pool_assign_id(pool));
+- mutex_unlock(&wq_pool_mutex);
+- }
++ for_each_cpu_worker_pool(pool, cpu)
++ init_cpu_worker_pool(pool, cpu, std_nice[i++]);
+ }
+
+ /* create default unbound and ordered wq attrs */