]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
6.6-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 30 Jul 2026 13:14:40 +0000 (15:14 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 30 Jul 2026 13:14:40 +0000 (15:14 +0200)
added patches:
afs-annotate-struct-afs_addr_list-with-__counted_by.patch
afs-turn-the-afs_addr_list-address-array-into-an-array-of-structs.patch
alsa-hda-conexant-remove-mic-bias-threshold-override.patch
alsa-hda-fix-cached-processing-coefficient-verbs.patch
asoc-mediatek-mt8183-check-runtime-resume-during-probe.patch
asoc-mediatek-mt8192-afe-pcm-convert-to-devm_pm_runtime_enable.patch
asoc-mediatek-mt8192-afe-pcm-simplify-probe-with-local-dev-variable.patch
asoc-mediatek-mt8192-afe-pcm-simplify-with-dev_err_probe.patch
asoc-mediatek-mt8192-check-runtime-resume-during-probe.patch
asoc-mediatek-mt8195-remove-afe-dai-component-and-rework-codec-link.patch
asoc-mediatek-use-common-mtk_afe_pcm_platform-with-common-probe-cb.patch
ata-libata-core-reject-an-invalid-concurrent-positioning-ranges-count.patch
audit-fix-recursive-locking-deadlock-in-audit_dupe_exe.patch
audit-use-unsigned-int-instead-of-unsigned.patch
audit-widen-ino-fields-to-u64.patch
bootconfig-do-not-put-quotes-on-cmdline-items-unless-necessary.patch
bootconfig-fix-null-pointer-arithmetic-in-xbc_snprint_cmdline.patch
bootconfig-move-xbc_snprint_cmdline-to-lib-bootconfig.c.patch
bpf-reject-bpf_map_type_inode_storage-creation-if-bpf-lsm-is-uninitialized.patch
dm-verity-avoid-double-increment-of-use_bh_wq_enabled.patch
dm-verity-make-error-counter-atomic.patch
dma-buf-udmabuf-skip-redundant-cpu-sync-to-fix-cacheline-eexist-warning.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
fbcon-rename-struct-fbcon_ops-to-struct-fbcon_par.patch
fbcon-use-correct-type-for-vc_resize-return-value.patch
fbdev-efifb-fix-memory-leak-in-efifb_probe.patch
fbdev-efifb-replace-references-to-global-screen_info-by-local-pointer.patch
firmware_loader-introduce-__free-cleanup-hanler.patch
fpga-dfl-afu-validate-dma-mapping-length-in-afu_dma_map_region.patch
fs-resctrl-fix-double-add-of-pseudo-locked-region-s-rmid-to-free-list.patch
gpio-mt7621-avoid-corruption-of-shared-interrupt-trigger-state.patch
gpio-tegra-do-not-call-pinctrl-for-gpio-direction.patch
i2c-davinci-unregister-cpufreq-notifier-on-probe-failure.patch
i2c-i801-fix-hardware-state-machine-corruption-in-error-path.patch
i2c-imx-fix-locked-bus-on-smbus-block-read-of-0-atomic.patch
i2c-imx-separate-atomic-dma-and-non-dma-use-case.patch
input-ims-pcu-fix-firmware-leak-in-async-update.patch
iommu-amd-don-t-split-flush-for-amd_iommu_domain_flush_all.patch
ipmi-fix-refcount-leak-in-i_ipmi_request.patch
kvm-introduce-vcpu-wants_to_run.patch
kvm-x86-only-reset-tsc-deadline-timer-in-apic_timer_expired-on-kvm_run.patch
locking-rt-fix-the-incorrect-rcu-protection-in-rt_spin_unlock.patch
lsm-infrastructure-management-of-the-sock-security.patch
mm-mm_init-fix-pageblock-migratetype-for-zone_device-compound-pages.patch
mmc-vub300-fix-use-after-free-on-disconnect.patch
mmc-vub300-fix-use-after-free-on-probe-failure.patch
mmc-vub300-rename-probe-error-labels.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
net-ip6_tunnel-require-cap_net_admin-in-the-device-netns-for-changelink.patch
net-ipa-fix-smem-state-handle-leaks-in-smp2p-init.patch
net-macb-drop-in-flight-tx-skbs-on-close.patch
net-mana-validate-the-packet-length-reported-by-the-nic.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-register-tracking-infrastructure.patch
netfilter-nf_tables-remove-unused-nft_reduce_is_readonly.patch
netfilter-nft_fib-reject-fib-expression-on-the-netdev-egress-hook.patch
netfilter-nft_set_pipapo-don-t-leak-bad-clone-into-future-transaction.patch
netfilter-nft_set_pipapo-make-pipapo_clone-helper-return-null.patch
netfilter-nft_set_pipapo-merge-deactivate-helper-into-caller.patch
netfilter-nft_set_pipapo-move-cloning-of-match-info-to-insert-removal-path.patch
netfilter-nft_set_pipapo-move-prove_locking-helper-around.patch
netfilter-nft_set_pipapo-prepare-pipapo_get-helper-for-on-demand-clone.patch
netfilter-nft_set_pipapo-prepare-walk-function-for-on-demand-clone.patch
netfilter-nft_set_pipapo-use-gfp_kernel-for-insertions.patch
nfs-charge-unstable-writes-by-request-size-not-folio-size.patch
nfs-remove-dead-code-for-the-old-swap-over-nfs-implementation.patch
nfsd-pass-nfsd_file-to-nfsd_iter_read.patch
octeontx2-af-cn10k-restrict-vf-lmtline-sharing-to-its-own-pf.patch
octeontx2-annotate-mmio-regions-as-__iomem.patch
octeontx2-pf-clear-stale-mailbox-irq-state-before-request_irq.patch
octeontx2-pf-fix-sqb-pointer-leak-on-init-failure.patch
octeontx2-vf-clear-stale-mailbox-irq-state-before-request_irq.patch
ovl-use-linked-upper-dentry-in-copy-up-tmpfile.patch
pinctrl-remove-pinctrl_gpio_direction_output.patch
platform-x86-dell-laptop-fix-missing-cleanups-in-init-error-path.patch
platform-x86-dell-smbios-move-request-functions-for-reuse.patch
rxrpc-don-t-need-barrier-for-tx_bottom-and-acks_hard_ack.patch
rxrpc-fix-notification-vs-call-release-vs-recvmsg.patch
rxrpc-fix-socket-notification-race.patch
rxrpc-pull-out-certain-app-callback-funcs-into-an-ops-table.patch
rxrpc-serialize-kernel-accept-preallocation-with-socket-teardown.patch
rxrpc-use-irq-disabling-spinlocks-between-app-and-i-o-thread.patch
selinux-avoid-sk_socket-dereference-in-selinux_sctp_bind_connect.patch
serial-max310x-implement-gpio_chip-get_direction.patch
serial-max310x-replace-bare-use-of-unsigned-with-unsigned-int-checkpatch.patch
sunrpc-add-helpers-to-convert-xdr_buf-byte-ranges-to-scatterlists.patch
sunrpc-allocate-a-separate-bvec-array-for-socket-sends.patch
sunrpc-return-an-error-from-xdr_buf_to_bvec-on-overflow.patch
taskstats-fill_stats_for_tgid-use-for_each_thread.patch
taskstats-retain-dead-thread-stats-in-tgid-queries.patch
thunderbolt-handle-lane-bonding-of-gen-4-xdomain-links-properly.patch
thunderbolt-keep-xdomain-reference-during-the-lifetime-of-a-service.patch
thunderbolt-prevent-xdomain-delayed-work-use-after-free-on-disconnect.patch
thunderbolt-remove-service-debugfs-entries-during-unregister.patch
thunderbolt-remove-usage-of-the-deprecated-ida_simple_xx-api.patch
thunderbolt-remove-xdomain-from-the-bus-without-holding-tb-lock.patch
thunderbolt-update-property.c-function-documentation.patch
tipc-restrict-socket-queue-dumps-in-enqueue-tracepoints.patch
tracing-user_events-fix-use-after-free-in-user_event_mm_dup.patch
udmabuf-do-not-create-malformed-scatterlists.patch
vduse-avoid-leaking-information-to-userspace.patch
vduse-remove-unused-vaddr-parameter-of-vduse_domain_free_coherent.patch
vduse-take-out-allocations-from-vduse_dev_alloc_coherent.patch
vduse-use-fixed-4kb-bounce-pages-for-non-4kb-page-size.patch
vfs-audit-introduce-kern_path_parent-for-audit.patch
wifi-libertas_tf-fix-use-after-free-in-lbtf_free_adapter.patch
workqueue-add-system_percpu_wq-and-system_dfl_wq.patch
workqueue-factor-out-init_cpu_worker_pool.patch

115 files changed:
queue-6.6/afs-annotate-struct-afs_addr_list-with-__counted_by.patch [new file with mode: 0644]
queue-6.6/afs-turn-the-afs_addr_list-address-array-into-an-array-of-structs.patch [new file with mode: 0644]
queue-6.6/alsa-hda-conexant-remove-mic-bias-threshold-override.patch [new file with mode: 0644]
queue-6.6/alsa-hda-fix-cached-processing-coefficient-verbs.patch [new file with mode: 0644]
queue-6.6/asoc-mediatek-mt8183-check-runtime-resume-during-probe.patch [new file with mode: 0644]
queue-6.6/asoc-mediatek-mt8192-afe-pcm-convert-to-devm_pm_runtime_enable.patch [new file with mode: 0644]
queue-6.6/asoc-mediatek-mt8192-afe-pcm-simplify-probe-with-local-dev-variable.patch [new file with mode: 0644]
queue-6.6/asoc-mediatek-mt8192-afe-pcm-simplify-with-dev_err_probe.patch [new file with mode: 0644]
queue-6.6/asoc-mediatek-mt8192-check-runtime-resume-during-probe.patch [new file with mode: 0644]
queue-6.6/asoc-mediatek-mt8195-remove-afe-dai-component-and-rework-codec-link.patch [new file with mode: 0644]
queue-6.6/asoc-mediatek-use-common-mtk_afe_pcm_platform-with-common-probe-cb.patch [new file with mode: 0644]
queue-6.6/ata-libata-core-reject-an-invalid-concurrent-positioning-ranges-count.patch [new file with mode: 0644]
queue-6.6/audit-fix-recursive-locking-deadlock-in-audit_dupe_exe.patch [new file with mode: 0644]
queue-6.6/audit-use-unsigned-int-instead-of-unsigned.patch [new file with mode: 0644]
queue-6.6/audit-widen-ino-fields-to-u64.patch [new file with mode: 0644]
queue-6.6/bootconfig-do-not-put-quotes-on-cmdline-items-unless-necessary.patch [new file with mode: 0644]
queue-6.6/bootconfig-fix-null-pointer-arithmetic-in-xbc_snprint_cmdline.patch [new file with mode: 0644]
queue-6.6/bootconfig-move-xbc_snprint_cmdline-to-lib-bootconfig.c.patch [new file with mode: 0644]
queue-6.6/bpf-reject-bpf_map_type_inode_storage-creation-if-bpf-lsm-is-uninitialized.patch [new file with mode: 0644]
queue-6.6/dm-verity-avoid-double-increment-of-use_bh_wq_enabled.patch [new file with mode: 0644]
queue-6.6/dm-verity-make-error-counter-atomic.patch [new file with mode: 0644]
queue-6.6/dma-buf-udmabuf-skip-redundant-cpu-sync-to-fix-cacheline-eexist-warning.patch [new file with mode: 0644]
queue-6.6/dma-dw-edma-fix-build-warning-in-dw_edma_pcie_probe.patch [new file with mode: 0644]
queue-6.6/dmaengine-dw-edma-fix-confusing-cleanup.h-syntax.patch [new file with mode: 0644]
queue-6.6/dmaengine-dw-edma-pcie-reject-devices-without-driver-data.patch [new file with mode: 0644]
queue-6.6/fbcon-rename-struct-fbcon_ops-to-struct-fbcon_par.patch [new file with mode: 0644]
queue-6.6/fbcon-use-correct-type-for-vc_resize-return-value.patch [new file with mode: 0644]
queue-6.6/fbdev-efifb-fix-memory-leak-in-efifb_probe.patch [new file with mode: 0644]
queue-6.6/fbdev-efifb-replace-references-to-global-screen_info-by-local-pointer.patch [new file with mode: 0644]
queue-6.6/firmware_loader-introduce-__free-cleanup-hanler.patch [new file with mode: 0644]
queue-6.6/fpga-dfl-afu-validate-dma-mapping-length-in-afu_dma_map_region.patch [new file with mode: 0644]
queue-6.6/fs-resctrl-fix-double-add-of-pseudo-locked-region-s-rmid-to-free-list.patch [new file with mode: 0644]
queue-6.6/gpio-mt7621-avoid-corruption-of-shared-interrupt-trigger-state.patch [new file with mode: 0644]
queue-6.6/gpio-tegra-do-not-call-pinctrl-for-gpio-direction.patch [new file with mode: 0644]
queue-6.6/i2c-davinci-unregister-cpufreq-notifier-on-probe-failure.patch [new file with mode: 0644]
queue-6.6/i2c-i801-fix-hardware-state-machine-corruption-in-error-path.patch [new file with mode: 0644]
queue-6.6/i2c-imx-fix-locked-bus-on-smbus-block-read-of-0-atomic.patch [new file with mode: 0644]
queue-6.6/i2c-imx-separate-atomic-dma-and-non-dma-use-case.patch [new file with mode: 0644]
queue-6.6/input-ims-pcu-fix-firmware-leak-in-async-update.patch [new file with mode: 0644]
queue-6.6/iommu-amd-don-t-split-flush-for-amd_iommu_domain_flush_all.patch [new file with mode: 0644]
queue-6.6/ipmi-fix-refcount-leak-in-i_ipmi_request.patch [new file with mode: 0644]
queue-6.6/kvm-introduce-vcpu-wants_to_run.patch [new file with mode: 0644]
queue-6.6/kvm-x86-only-reset-tsc-deadline-timer-in-apic_timer_expired-on-kvm_run.patch [new file with mode: 0644]
queue-6.6/locking-rt-fix-the-incorrect-rcu-protection-in-rt_spin_unlock.patch [new file with mode: 0644]
queue-6.6/lsm-infrastructure-management-of-the-sock-security.patch [new file with mode: 0644]
queue-6.6/mm-mm_init-fix-pageblock-migratetype-for-zone_device-compound-pages.patch [new file with mode: 0644]
queue-6.6/mmc-vub300-fix-use-after-free-on-disconnect.patch [new file with mode: 0644]
queue-6.6/mmc-vub300-fix-use-after-free-on-probe-failure.patch [new file with mode: 0644]
queue-6.6/mmc-vub300-rename-probe-error-labels.patch [new file with mode: 0644]
queue-6.6/mtd-maps-vmu-flash-fix-fault-in-unaligned-fixup.patch [new file with mode: 0644]
queue-6.6/mtd-rawnand-add-a-helper-for-calculating-a-page-index.patch [new file with mode: 0644]
queue-6.6/mtd-rawnand-ensure-all-continuous-terms-are-always-in-sync.patch [new file with mode: 0644]
queue-6.6/mtd-rawnand-pause-continuous-reads-at-block-boundaries.patch [new file with mode: 0644]
queue-6.6/net-ip6_tunnel-require-cap_net_admin-in-the-device-netns-for-changelink.patch [new file with mode: 0644]
queue-6.6/net-ipa-fix-smem-state-handle-leaks-in-smp2p-init.patch [new file with mode: 0644]
queue-6.6/net-macb-drop-in-flight-tx-skbs-on-close.patch [new file with mode: 0644]
queue-6.6/net-mana-validate-the-packet-length-reported-by-the-nic.patch [new file with mode: 0644]
queue-6.6/netfilter-nf_conntrack_sip-remove-net-variable-shadowing.patch [new file with mode: 0644]
queue-6.6/netfilter-nf_conntrack_sip-validate-skb_dst-before-accessing-it.patch [new file with mode: 0644]
queue-6.6/netfilter-nf_tables-remove-register-tracking-infrastructure.patch [new file with mode: 0644]
queue-6.6/netfilter-nf_tables-remove-unused-nft_reduce_is_readonly.patch [new file with mode: 0644]
queue-6.6/netfilter-nft_fib-reject-fib-expression-on-the-netdev-egress-hook.patch [new file with mode: 0644]
queue-6.6/netfilter-nft_set_pipapo-don-t-leak-bad-clone-into-future-transaction.patch [new file with mode: 0644]
queue-6.6/netfilter-nft_set_pipapo-make-pipapo_clone-helper-return-null.patch [new file with mode: 0644]
queue-6.6/netfilter-nft_set_pipapo-merge-deactivate-helper-into-caller.patch [new file with mode: 0644]
queue-6.6/netfilter-nft_set_pipapo-move-cloning-of-match-info-to-insert-removal-path.patch [new file with mode: 0644]
queue-6.6/netfilter-nft_set_pipapo-move-prove_locking-helper-around.patch [new file with mode: 0644]
queue-6.6/netfilter-nft_set_pipapo-prepare-pipapo_get-helper-for-on-demand-clone.patch [new file with mode: 0644]
queue-6.6/netfilter-nft_set_pipapo-prepare-walk-function-for-on-demand-clone.patch [new file with mode: 0644]
queue-6.6/netfilter-nft_set_pipapo-use-gfp_kernel-for-insertions.patch [new file with mode: 0644]
queue-6.6/nfs-charge-unstable-writes-by-request-size-not-folio-size.patch [new file with mode: 0644]
queue-6.6/nfs-remove-dead-code-for-the-old-swap-over-nfs-implementation.patch [new file with mode: 0644]
queue-6.6/nfsd-pass-nfsd_file-to-nfsd_iter_read.patch [new file with mode: 0644]
queue-6.6/octeontx2-af-cn10k-restrict-vf-lmtline-sharing-to-its-own-pf.patch [new file with mode: 0644]
queue-6.6/octeontx2-annotate-mmio-regions-as-__iomem.patch [new file with mode: 0644]
queue-6.6/octeontx2-pf-clear-stale-mailbox-irq-state-before-request_irq.patch [new file with mode: 0644]
queue-6.6/octeontx2-pf-fix-sqb-pointer-leak-on-init-failure.patch [new file with mode: 0644]
queue-6.6/octeontx2-vf-clear-stale-mailbox-irq-state-before-request_irq.patch [new file with mode: 0644]
queue-6.6/ovl-use-linked-upper-dentry-in-copy-up-tmpfile.patch [new file with mode: 0644]
queue-6.6/pinctrl-remove-pinctrl_gpio_direction_output.patch [new file with mode: 0644]
queue-6.6/platform-x86-dell-laptop-fix-missing-cleanups-in-init-error-path.patch [new file with mode: 0644]
queue-6.6/platform-x86-dell-smbios-move-request-functions-for-reuse.patch [new file with mode: 0644]
queue-6.6/rxrpc-don-t-need-barrier-for-tx_bottom-and-acks_hard_ack.patch [new file with mode: 0644]
queue-6.6/rxrpc-fix-notification-vs-call-release-vs-recvmsg.patch [new file with mode: 0644]
queue-6.6/rxrpc-fix-socket-notification-race.patch [new file with mode: 0644]
queue-6.6/rxrpc-pull-out-certain-app-callback-funcs-into-an-ops-table.patch [new file with mode: 0644]
queue-6.6/rxrpc-serialize-kernel-accept-preallocation-with-socket-teardown.patch [new file with mode: 0644]
queue-6.6/rxrpc-use-irq-disabling-spinlocks-between-app-and-i-o-thread.patch [new file with mode: 0644]
queue-6.6/selinux-avoid-sk_socket-dereference-in-selinux_sctp_bind_connect.patch [new file with mode: 0644]
queue-6.6/serial-max310x-implement-gpio_chip-get_direction.patch [new file with mode: 0644]
queue-6.6/serial-max310x-replace-bare-use-of-unsigned-with-unsigned-int-checkpatch.patch [new file with mode: 0644]
queue-6.6/series
queue-6.6/sunrpc-add-helpers-to-convert-xdr_buf-byte-ranges-to-scatterlists.patch [new file with mode: 0644]
queue-6.6/sunrpc-allocate-a-separate-bvec-array-for-socket-sends.patch [new file with mode: 0644]
queue-6.6/sunrpc-return-an-error-from-xdr_buf_to_bvec-on-overflow.patch [new file with mode: 0644]
queue-6.6/taskstats-fill_stats_for_tgid-use-for_each_thread.patch [new file with mode: 0644]
queue-6.6/taskstats-retain-dead-thread-stats-in-tgid-queries.patch [new file with mode: 0644]
queue-6.6/thunderbolt-handle-lane-bonding-of-gen-4-xdomain-links-properly.patch [new file with mode: 0644]
queue-6.6/thunderbolt-keep-xdomain-reference-during-the-lifetime-of-a-service.patch [new file with mode: 0644]
queue-6.6/thunderbolt-prevent-xdomain-delayed-work-use-after-free-on-disconnect.patch [new file with mode: 0644]
queue-6.6/thunderbolt-remove-service-debugfs-entries-during-unregister.patch [new file with mode: 0644]
queue-6.6/thunderbolt-remove-usage-of-the-deprecated-ida_simple_xx-api.patch [new file with mode: 0644]
queue-6.6/thunderbolt-remove-xdomain-from-the-bus-without-holding-tb-lock.patch [new file with mode: 0644]
queue-6.6/thunderbolt-update-property.c-function-documentation.patch [new file with mode: 0644]
queue-6.6/tipc-restrict-socket-queue-dumps-in-enqueue-tracepoints.patch [new file with mode: 0644]
queue-6.6/tracing-user_events-fix-use-after-free-in-user_event_mm_dup.patch [new file with mode: 0644]
queue-6.6/udmabuf-do-not-create-malformed-scatterlists.patch [new file with mode: 0644]
queue-6.6/vduse-avoid-leaking-information-to-userspace.patch [new file with mode: 0644]
queue-6.6/vduse-remove-unused-vaddr-parameter-of-vduse_domain_free_coherent.patch [new file with mode: 0644]
queue-6.6/vduse-take-out-allocations-from-vduse_dev_alloc_coherent.patch [new file with mode: 0644]
queue-6.6/vduse-use-fixed-4kb-bounce-pages-for-non-4kb-page-size.patch [new file with mode: 0644]
queue-6.6/vfs-audit-introduce-kern_path_parent-for-audit.patch [new file with mode: 0644]
queue-6.6/wifi-libertas_tf-fix-use-after-free-in-lbtf_free_adapter.patch [new file with mode: 0644]
queue-6.6/workqueue-add-system_percpu_wq-and-system_dfl_wq.patch [new file with mode: 0644]
queue-6.6/workqueue-factor-out-init_cpu_worker_pool.patch [new file with mode: 0644]

diff --git a/queue-6.6/afs-annotate-struct-afs_addr_list-with-__counted_by.patch b/queue-6.6/afs-annotate-struct-afs_addr_list-with-__counted_by.patch
new file mode 100644 (file)
index 0000000..f7a4988
--- /dev/null
@@ -0,0 +1,46 @@
+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))
+ };
diff --git a/queue-6.6/afs-turn-the-afs_addr_list-address-array-into-an-array-of-structs.patch b/queue-6.6/afs-turn-the-afs_addr_list-address-array-into-an-array-of-structs.patch
new file mode 100644 (file)
index 0000000..692a53d
--- /dev/null
@@ -0,0 +1,250 @@
+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:
diff --git a/queue-6.6/alsa-hda-conexant-remove-mic-bias-threshold-override.patch b/queue-6.6/alsa-hda-conexant-remove-mic-bias-threshold-override.patch
new file mode 100644 (file)
index 0000000..c771488
--- /dev/null
@@ -0,0 +1,47 @@
+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.
+        */
diff --git a/queue-6.6/alsa-hda-fix-cached-processing-coefficient-verbs.patch b/queue-6.6/alsa-hda-fix-cached-processing-coefficient-verbs.patch
new file mode 100644 (file)
index 0000000..09938d6
--- /dev/null
@@ -0,0 +1,62 @@
+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);
+ }
diff --git a/queue-6.6/asoc-mediatek-mt8183-check-runtime-resume-during-probe.patch b/queue-6.6/asoc-mediatek-mt8183-check-runtime-resume-during-probe.patch
new file mode 100644 (file)
index 0000000..9c48edb
--- /dev/null
@@ -0,0 +1,60 @@
+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);
diff --git a/queue-6.6/asoc-mediatek-mt8192-afe-pcm-convert-to-devm_pm_runtime_enable.patch b/queue-6.6/asoc-mediatek-mt8192-afe-pcm-convert-to-devm_pm_runtime_enable.patch
new file mode 100644 (file)
index 0000000..2eabd33
--- /dev/null
@@ -0,0 +1,153 @@
+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)
diff --git a/queue-6.6/asoc-mediatek-mt8192-afe-pcm-simplify-probe-with-local-dev-variable.patch b/queue-6.6/asoc-mediatek-mt8192-afe-pcm-simplify-probe-with-local-dev-variable.patch
new file mode 100644 (file)
index 0000000..3a9354b
--- /dev/null
@@ -0,0 +1,94 @@
+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);
diff --git a/queue-6.6/asoc-mediatek-mt8192-afe-pcm-simplify-with-dev_err_probe.patch b/queue-6.6/asoc-mediatek-mt8192-afe-pcm-simplify-with-dev_err_probe.patch
new file mode 100644 (file)
index 0000000..b8cb42e
--- /dev/null
@@ -0,0 +1,147 @@
+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;
+ }
diff --git a/queue-6.6/asoc-mediatek-mt8192-check-runtime-resume-during-probe.patch b/queue-6.6/asoc-mediatek-mt8192-check-runtime-resume-during-probe.patch
new file mode 100644 (file)
index 0000000..ad4f7b6
--- /dev/null
@@ -0,0 +1,62 @@
+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);
diff --git a/queue-6.6/asoc-mediatek-mt8195-remove-afe-dai-component-and-rework-codec-link.patch b/queue-6.6/asoc-mediatek-mt8195-remove-afe-dai-component-and-rework-codec-link.patch
new file mode 100644 (file)
index 0000000..0a45d2d
--- /dev/null
@@ -0,0 +1,194 @@
+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) {
diff --git a/queue-6.6/asoc-mediatek-use-common-mtk_afe_pcm_platform-with-common-probe-cb.patch b/queue-6.6/asoc-mediatek-use-common-mtk_afe_pcm_platform-with-common-probe-cb.patch
new file mode 100644 (file)
index 0000000..fb311d2
--- /dev/null
@@ -0,0 +1,245 @@
+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");
diff --git a/queue-6.6/ata-libata-core-reject-an-invalid-concurrent-positioning-ranges-count.patch b/queue-6.6/ata-libata-core-reject-an-invalid-concurrent-positioning-ranges-count.patch
new file mode 100644 (file)
index 0000000..2ff83f6
--- /dev/null
@@ -0,0 +1,100 @@
+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,
diff --git a/queue-6.6/audit-fix-recursive-locking-deadlock-in-audit_dupe_exe.patch b/queue-6.6/audit-fix-recursive-locking-deadlock-in-audit_dupe_exe.patch
new file mode 100644 (file)
index 0000000..6294c46
--- /dev/null
@@ -0,0 +1,364 @@
+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)) {
diff --git a/queue-6.6/audit-use-unsigned-int-instead-of-unsigned.patch b/queue-6.6/audit-use-unsigned-int-instead-of-unsigned.patch
new file mode 100644 (file)
index 0000000..353e0b9
--- /dev/null
@@ -0,0 +1,190 @@
+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
diff --git a/queue-6.6/audit-widen-ino-fields-to-u64.patch b/queue-6.6/audit-widen-ino-fields-to-u64.patch
new file mode 100644 (file)
index 0000000..eb47ca2
--- /dev/null
@@ -0,0 +1,159 @@
+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),
diff --git a/queue-6.6/bootconfig-do-not-put-quotes-on-cmdline-items-unless-necessary.patch b/queue-6.6/bootconfig-do-not-put-quotes-on-cmdline-items-unless-necessary.patch
new file mode 100644 (file)
index 0000000..387a0e5
--- /dev/null
@@ -0,0 +1,68 @@
+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;
diff --git a/queue-6.6/bootconfig-fix-null-pointer-arithmetic-in-xbc_snprint_cmdline.patch b/queue-6.6/bootconfig-fix-null-pointer-arithmetic-in-xbc_snprint_cmdline.patch
new file mode 100644 (file)
index 0000000..2331ccd
--- /dev/null
@@ -0,0 +1,97 @@
+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
diff --git a/queue-6.6/bootconfig-move-xbc_snprint_cmdline-to-lib-bootconfig.c.patch b/queue-6.6/bootconfig-move-xbc_snprint_cmdline-to-lib-bootconfig.c.patch
new file mode 100644 (file)
index 0000000..64245e7
--- /dev/null
@@ -0,0 +1,165 @@
+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)
diff --git a/queue-6.6/bpf-reject-bpf_map_type_inode_storage-creation-if-bpf-lsm-is-uninitialized.patch b/queue-6.6/bpf-reject-bpf_map_type_inode_storage-creation-if-bpf-lsm-is-uninitialized.patch
new file mode 100644 (file)
index 0000000..f9f1afb
--- /dev/null
@@ -0,0 +1,115 @@
+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;
+ }
diff --git a/queue-6.6/dm-verity-avoid-double-increment-of-use_bh_wq_enabled.patch b/queue-6.6/dm-verity-avoid-double-increment-of-use_bh_wq_enabled.patch
new file mode 100644 (file)
index 0000000..e305fdf
--- /dev/null
@@ -0,0 +1,41 @@
+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;
diff --git a/queue-6.6/dm-verity-make-error-counter-atomic.patch b/queue-6.6/dm-verity-make-error-counter-atomic.patch
new file mode 100644 (file)
index 0000000..943fc03
--- /dev/null
@@ -0,0 +1,71 @@
+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;
diff --git a/queue-6.6/dma-buf-udmabuf-skip-redundant-cpu-sync-to-fix-cacheline-eexist-warning.patch b/queue-6.6/dma-buf-udmabuf-skip-redundant-cpu-sync-to-fix-cacheline-eexist-warning.patch
new file mode 100644 (file)
index 0000000..22f8a9f
--- /dev/null
@@ -0,0 +1,85 @@
+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);
+ }
diff --git a/queue-6.6/dma-dw-edma-fix-build-warning-in-dw_edma_pcie_probe.patch b/queue-6.6/dma-dw-edma-fix-build-warning-in-dw_edma_pcie_probe.patch
new file mode 100644 (file)
index 0000000..e0a3b40
--- /dev/null
@@ -0,0 +1,177 @@
+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);
+       }
diff --git a/queue-6.6/dmaengine-dw-edma-fix-confusing-cleanup.h-syntax.patch b/queue-6.6/dmaengine-dw-edma-fix-confusing-cleanup.h-syntax.patch
new file mode 100644 (file)
index 0000000..a39f6fb
--- /dev/null
@@ -0,0 +1,55 @@
+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;
diff --git a/queue-6.6/dmaengine-dw-edma-pcie-reject-devices-without-driver-data.patch b/queue-6.6/dmaengine-dw-edma-pcie-reject-devices-without-driver-data.patch
new file mode 100644 (file)
index 0000000..952466d
--- /dev/null
@@ -0,0 +1,43 @@
+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)
diff --git a/queue-6.6/fbcon-rename-struct-fbcon_ops-to-struct-fbcon_par.patch b/queue-6.6/fbcon-rename-struct-fbcon_ops-to-struct-fbcon_par.patch
new file mode 100644 (file)
index 0000000..4581b4b
--- /dev/null
@@ -0,0 +1,2490 @@
+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);
+       }
+ }
diff --git a/queue-6.6/fbcon-use-correct-type-for-vc_resize-return-value.patch b/queue-6.6/fbcon-use-correct-type-for-vc_resize-return-value.patch
new file mode 100644 (file)
index 0000000..e80c39f
--- /dev/null
@@ -0,0 +1,45 @@
+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];
diff --git a/queue-6.6/fbdev-efifb-fix-memory-leak-in-efifb_probe.patch b/queue-6.6/fbdev-efifb-fix-memory-leak-in-efifb_probe.patch
new file mode 100644 (file)
index 0000000..0085fed
--- /dev/null
@@ -0,0 +1,38 @@
+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
diff --git a/queue-6.6/fbdev-efifb-replace-references-to-global-screen_info-by-local-pointer.patch b/queue-6.6/fbdev-efifb-replace-references-to-global-screen_info-by-local-pointer.patch
new file mode 100644 (file)
index 0000000..a547f7e
--- /dev/null
@@ -0,0 +1,303 @@
+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;
diff --git a/queue-6.6/firmware_loader-introduce-__free-cleanup-hanler.patch b/queue-6.6/firmware_loader-introduce-__free-cleanup-hanler.patch
new file mode 100644 (file)
index 0000000..9c1293d
--- /dev/null
@@ -0,0 +1,70 @@
+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
diff --git a/queue-6.6/fpga-dfl-afu-validate-dma-mapping-length-in-afu_dma_map_region.patch b/queue-6.6/fpga-dfl-afu-validate-dma-mapping-length-in-afu_dma_map_region.patch
new file mode 100644 (file)
index 0000000..0d7361d
--- /dev/null
@@ -0,0 +1,46 @@
+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;
diff --git a/queue-6.6/fs-resctrl-fix-double-add-of-pseudo-locked-region-s-rmid-to-free-list.patch b/queue-6.6/fs-resctrl-fix-double-add-of-pseudo-locked-region-s-rmid-to-free-list.patch
new file mode 100644 (file)
index 0000000..a9c4b91
--- /dev/null
@@ -0,0 +1,60 @@
+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);
diff --git a/queue-6.6/gpio-mt7621-avoid-corruption-of-shared-interrupt-trigger-state.patch b/queue-6.6/gpio-mt7621-avoid-corruption-of-shared-interrupt-trigger-state.patch
new file mode 100644 (file)
index 0000000..a2ecd2e
--- /dev/null
@@ -0,0 +1,44 @@
+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)
diff --git a/queue-6.6/gpio-tegra-do-not-call-pinctrl-for-gpio-direction.patch b/queue-6.6/gpio-tegra-do-not-call-pinctrl-for-gpio-direction.patch
new file mode 100644 (file)
index 0000000..a7b1243
--- /dev/null
@@ -0,0 +1,100 @@
+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,
diff --git a/queue-6.6/i2c-davinci-unregister-cpufreq-notifier-on-probe-failure.patch b/queue-6.6/i2c-davinci-unregister-cpufreq-notifier-on-probe-failure.patch
new file mode 100644 (file)
index 0000000..b42f1cd
--- /dev/null
@@ -0,0 +1,50 @@
+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);
diff --git a/queue-6.6/i2c-i801-fix-hardware-state-machine-corruption-in-error-path.patch b/queue-6.6/i2c-i801-fix-hardware-state-machine-corruption-in-error-path.patch
new file mode 100644 (file)
index 0000000..c8b8c47
--- /dev/null
@@ -0,0 +1,68 @@
+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);
diff --git a/queue-6.6/i2c-imx-fix-locked-bus-on-smbus-block-read-of-0-atomic.patch b/queue-6.6/i2c-imx-fix-locked-bus-on-smbus-block-read-of-0-atomic.patch
new file mode 100644 (file)
index 0000000..3215906
--- /dev/null
@@ -0,0 +1,87 @@
+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,
diff --git a/queue-6.6/i2c-imx-separate-atomic-dma-and-non-dma-use-case.patch b/queue-6.6/i2c-imx-separate-atomic-dma-and-non-dma-use-case.patch
new file mode 100644 (file)
index 0000000..2de99c2
--- /dev/null
@@ -0,0 +1,194 @@
+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;
diff --git a/queue-6.6/input-ims-pcu-fix-firmware-leak-in-async-update.patch b/queue-6.6/input-ims-pcu-fix-firmware-leak-in-async-update.patch
new file mode 100644 (file)
index 0000000..5cdc529
--- /dev/null
@@ -0,0 +1,49 @@
+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);
+ }
diff --git a/queue-6.6/iommu-amd-don-t-split-flush-for-amd_iommu_domain_flush_all.patch b/queue-6.6/iommu-amd-don-t-split-flush-for-amd_iommu_domain_flush_all.patch
new file mode 100644 (file)
index 0000000..3e2f176
--- /dev/null
@@ -0,0 +1,65 @@
+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;
+       }
diff --git a/queue-6.6/ipmi-fix-refcount-leak-in-i_ipmi_request.patch b/queue-6.6/ipmi-fix-refcount-leak-in-i_ipmi_request.patch
new file mode 100644 (file)
index 0000000..2d06593
--- /dev/null
@@ -0,0 +1,60 @@
+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);
diff --git a/queue-6.6/kvm-introduce-vcpu-wants_to_run.patch b/queue-6.6/kvm-introduce-vcpu-wants_to_run.patch
new file mode 100644 (file)
index 0000000..c660d7b
--- /dev/null
@@ -0,0 +1,139 @@
+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;
+       }
diff --git a/queue-6.6/kvm-x86-only-reset-tsc-deadline-timer-in-apic_timer_expired-on-kvm_run.patch b/queue-6.6/kvm-x86-only-reset-tsc-deadline-timer-in-apic_timer_expired-on-kvm_run.patch
new file mode 100644 (file)
index 0000000..49bda30
--- /dev/null
@@ -0,0 +1,68 @@
+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;
diff --git a/queue-6.6/locking-rt-fix-the-incorrect-rcu-protection-in-rt_spin_unlock.patch b/queue-6.6/locking-rt-fix-the-incorrect-rcu-protection-in-rt_spin_unlock.patch
new file mode 100644 (file)
index 0000000..749be7c
--- /dev/null
@@ -0,0 +1,110 @@
+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);
diff --git a/queue-6.6/lsm-infrastructure-management-of-the-sock-security.patch b/queue-6.6/lsm-infrastructure-management-of-the-sock-security.patch
new file mode 100644 (file)
index 0000000..574754c
--- /dev/null
@@ -0,0 +1,985 @@
+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;
+       }
diff --git a/queue-6.6/mm-mm_init-fix-pageblock-migratetype-for-zone_device-compound-pages.patch b/queue-6.6/mm-mm_init-fix-pageblock-migratetype-for-zone_device-compound-pages.patch
new file mode 100644 (file)
index 0000000..9e4222e
--- /dev/null
@@ -0,0 +1,115 @@
+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));
+ }
diff --git a/queue-6.6/mmc-vub300-fix-use-after-free-on-disconnect.patch b/queue-6.6/mmc-vub300-fix-use-after-free-on-disconnect.patch
new file mode 100644 (file)
index 0000000..e963d13
--- /dev/null
@@ -0,0 +1,90 @@
+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
diff --git a/queue-6.6/mmc-vub300-fix-use-after-free-on-probe-failure.patch b/queue-6.6/mmc-vub300-fix-use-after-free-on-probe-failure.patch
new file mode 100644 (file)
index 0000000..01305de
--- /dev/null
@@ -0,0 +1,87 @@
+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);
+       /*
diff --git a/queue-6.6/mmc-vub300-rename-probe-error-labels.patch b/queue-6.6/mmc-vub300-rename-probe-error-labels.patch
new file mode 100644 (file)
index 0000000..d4c0d0c
--- /dev/null
@@ -0,0 +1,79 @@
+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;
+ }
diff --git a/queue-6.6/mtd-maps-vmu-flash-fix-fault-in-unaligned-fixup.patch b/queue-6.6/mtd-maps-vmu-flash-fix-fault-in-unaligned-fixup.patch
new file mode 100644 (file)
index 0000000..600b499
--- /dev/null
@@ -0,0 +1,58 @@
+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;
diff --git a/queue-6.6/mtd-rawnand-add-a-helper-for-calculating-a-page-index.patch b/queue-6.6/mtd-rawnand-add-a-helper-for-calculating-a-page-index.patch
new file mode 100644 (file)
index 0000000..291cea5
--- /dev/null
@@ -0,0 +1,60 @@
+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;
+ }
diff --git a/queue-6.6/mtd-rawnand-ensure-all-continuous-terms-are-always-in-sync.patch b/queue-6.6/mtd-rawnand-ensure-all-continuous-terms-are-always-in-sync.patch
new file mode 100644 (file)
index 0000000..cf989d6
--- /dev/null
@@ -0,0 +1,77 @@
+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);
+ }
+ /**
diff --git a/queue-6.6/mtd-rawnand-pause-continuous-reads-at-block-boundaries.patch b/queue-6.6/mtd-rawnand-pause-continuous-reads-at-block-boundaries.patch
new file mode 100644 (file)
index 0000000..5861cea
--- /dev/null
@@ -0,0 +1,72 @@
+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)
diff --git a/queue-6.6/net-ip6_tunnel-require-cap_net_admin-in-the-device-netns-for-changelink.patch b/queue-6.6/net-ip6_tunnel-require-cap_net_admin-in-the-device-netns-for-changelink.patch
new file mode 100644 (file)
index 0000000..cd0c44d
--- /dev/null
@@ -0,0 +1,48 @@
+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;
diff --git a/queue-6.6/net-ipa-fix-smem-state-handle-leaks-in-smp2p-init.patch b/queue-6.6/net-ipa-fix-smem-state-handle-leaks-in-smp2p-init.patch
new file mode 100644 (file)
index 0000000..9c66f36
--- /dev/null
@@ -0,0 +1,91 @@
+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);
+ }
diff --git a/queue-6.6/net-macb-drop-in-flight-tx-skbs-on-close.patch b/queue-6.6/net-macb-drop-in-flight-tx-skbs-on-close.patch
new file mode 100644 (file)
index 0000000..674535c
--- /dev/null
@@ -0,0 +1,64 @@
+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,
diff --git a/queue-6.6/net-mana-validate-the-packet-length-reported-by-the-nic.patch b/queue-6.6/net-mana-validate-the-packet-length-reported-by-the-nic.patch
new file mode 100644 (file)
index 0000000..4673515
--- /dev/null
@@ -0,0 +1,50 @@
+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.
diff --git a/queue-6.6/netfilter-nf_conntrack_sip-remove-net-variable-shadowing.patch b/queue-6.6/netfilter-nf_conntrack_sip-remove-net-variable-shadowing.patch
new file mode 100644 (file)
index 0000000..3db1227
--- /dev/null
@@ -0,0 +1,39 @@
+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));
diff --git a/queue-6.6/netfilter-nf_conntrack_sip-validate-skb_dst-before-accessing-it.patch b/queue-6.6/netfilter-nf_conntrack_sip-validate-skb_dst-before-accessing-it.patch
new file mode 100644 (file)
index 0000000..2afd150
--- /dev/null
@@ -0,0 +1,56 @@
+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)
diff --git a/queue-6.6/netfilter-nf_tables-remove-register-tracking-infrastructure.patch b/queue-6.6/netfilter-nf_tables-remove-register-tracking-infrastructure.patch
new file mode 100644 (file)
index 0000000..ed61e52
--- /dev/null
@@ -0,0 +1,1645 @@
+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 = {
diff --git a/queue-6.6/netfilter-nf_tables-remove-unused-nft_reduce_is_readonly.patch b/queue-6.6/netfilter-nf_tables-remove-unused-nft_reduce_is_readonly.patch
new file mode 100644 (file)
index 0000000..fa69670
--- /dev/null
@@ -0,0 +1,39 @@
+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);
diff --git a/queue-6.6/netfilter-nft_fib-reject-fib-expression-on-the-netdev-egress-hook.patch b/queue-6.6/netfilter-nft_fib-reject-fib-expression-on-the-netdev-egress-hook.patch
new file mode 100644 (file)
index 0000000..bf2d34d
--- /dev/null
@@ -0,0 +1,100 @@
+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 = {
diff --git a/queue-6.6/netfilter-nft_set_pipapo-don-t-leak-bad-clone-into-future-transaction.patch b/queue-6.6/netfilter-nft_set_pipapo-don-t-leak-bad-clone-into-future-transaction.patch
new file mode 100644 (file)
index 0000000..8c2ae98
--- /dev/null
@@ -0,0 +1,164 @@
+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;
diff --git a/queue-6.6/netfilter-nft_set_pipapo-make-pipapo_clone-helper-return-null.patch b/queue-6.6/netfilter-nft_set_pipapo-make-pipapo_clone-helper-return-null.patch
new file mode 100644 (file)
index 0000000..fa963dc
--- /dev/null
@@ -0,0 +1,94 @@
+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;
+       }
diff --git a/queue-6.6/netfilter-nft_set_pipapo-merge-deactivate-helper-into-caller.patch b/queue-6.6/netfilter-nft_set_pipapo-merge-deactivate-helper-into-caller.patch
new file mode 100644 (file)
index 0000000..01e266a
--- /dev/null
@@ -0,0 +1,89 @@
+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
diff --git a/queue-6.6/netfilter-nft_set_pipapo-move-cloning-of-match-info-to-insert-removal-path.patch b/queue-6.6/netfilter-nft_set_pipapo-move-cloning-of-match-info-to-insert-removal-path.patch
new file mode 100644 (file)
index 0000000..d53b0ae
--- /dev/null
@@ -0,0 +1,184 @@
+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:
diff --git a/queue-6.6/netfilter-nft_set_pipapo-move-prove_locking-helper-around.patch b/queue-6.6/netfilter-nft_set_pipapo-move-prove_locking-helper-around.patch
new file mode 100644 (file)
index 0000000..30808c2
--- /dev/null
@@ -0,0 +1,63 @@
+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);
diff --git a/queue-6.6/netfilter-nft_set_pipapo-prepare-pipapo_get-helper-for-on-demand-clone.patch b/queue-6.6/netfilter-nft_set_pipapo-prepare-pipapo_get-helper-for-on-demand-clone.patch
new file mode 100644 (file)
index 0000000..64d0af3
--- /dev/null
@@ -0,0 +1,122 @@
+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;
+ }
+ /**
diff --git a/queue-6.6/netfilter-nft_set_pipapo-prepare-walk-function-for-on-demand-clone.patch b/queue-6.6/netfilter-nft_set_pipapo-prepare-walk-function-for-on-demand-clone.patch
new file mode 100644 (file)
index 0000000..4eb8afa
--- /dev/null
@@ -0,0 +1,131 @@
+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;
++      }
+ }
+ /**
diff --git a/queue-6.6/netfilter-nft_set_pipapo-use-gfp_kernel-for-insertions.patch b/queue-6.6/netfilter-nft_set_pipapo-use-gfp_kernel-for-insertions.patch
new file mode 100644 (file)
index 0000000..2050ed2
--- /dev/null
@@ -0,0 +1,100 @@
+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;
diff --git a/queue-6.6/nfs-charge-unstable-writes-by-request-size-not-folio-size.patch b/queue-6.6/nfs-charge-unstable-writes-by-request-size-not-folio-size.patch
new file mode 100644 (file)
index 0000000..e4bb6d1
--- /dev/null
@@ -0,0 +1,133 @@
+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,
diff --git a/queue-6.6/nfs-remove-dead-code-for-the-old-swap-over-nfs-implementation.patch b/queue-6.6/nfs-remove-dead-code-for-the-old-swap-over-nfs-implementation.patch
new file mode 100644 (file)
index 0000000..170a618
--- /dev/null
@@ -0,0 +1,37 @@
+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
diff --git a/queue-6.6/nfsd-pass-nfsd_file-to-nfsd_iter_read.patch b/queue-6.6/nfsd-pass-nfsd_file-to-nfsd_iter_read.patch
new file mode 100644 (file)
index 0000000..c98eec3
--- /dev/null
@@ -0,0 +1,116 @@
+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,
diff --git a/queue-6.6/octeontx2-af-cn10k-restrict-vf-lmtline-sharing-to-its-own-pf.patch b/queue-6.6/octeontx2-af-cn10k-restrict-vf-lmtline-sharing-to-its-own-pf.patch
new file mode 100644 (file)
index 0000000..e26aad8
--- /dev/null
@@ -0,0 +1,78 @@
+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.
+                */
diff --git a/queue-6.6/octeontx2-annotate-mmio-regions-as-__iomem.patch b/queue-6.6/octeontx2-annotate-mmio-regions-as-__iomem.patch
new file mode 100644 (file)
index 0000000..339ff67
--- /dev/null
@@ -0,0 +1,99 @@
+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) {
diff --git a/queue-6.6/octeontx2-pf-clear-stale-mailbox-irq-state-before-request_irq.patch b/queue-6.6/octeontx2-pf-clear-stale-mailbox-irq-state-before-request_irq.patch
new file mode 100644 (file)
index 0000000..a9c71de
--- /dev/null
@@ -0,0 +1,67 @@
+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)
diff --git a/queue-6.6/octeontx2-pf-fix-sqb-pointer-leak-on-init-failure.patch b/queue-6.6/octeontx2-pf-fix-sqb-pointer-leak-on-init-failure.patch
new file mode 100644 (file)
index 0000000..b825a97
--- /dev/null
@@ -0,0 +1,79 @@
+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);
diff --git a/queue-6.6/octeontx2-vf-clear-stale-mailbox-irq-state-before-request_irq.patch b/queue-6.6/octeontx2-vf-clear-stale-mailbox-irq-state-before-request_irq.patch
new file mode 100644 (file)
index 0000000..17084b0
--- /dev/null
@@ -0,0 +1,75 @@
+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;
diff --git a/queue-6.6/ovl-use-linked-upper-dentry-in-copy-up-tmpfile.patch b/queue-6.6/ovl-use-linked-upper-dentry-in-copy-up-tmpfile.patch
new file mode 100644 (file)
index 0000000..de76ee7
--- /dev/null
@@ -0,0 +1,105 @@
+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);
diff --git a/queue-6.6/pinctrl-remove-pinctrl_gpio_direction_output.patch b/queue-6.6/pinctrl-remove-pinctrl_gpio_direction_output.patch
new file mode 100644 (file)
index 0000000..0801db9
--- /dev/null
@@ -0,0 +1,590 @@
+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;
+ }
diff --git a/queue-6.6/platform-x86-dell-laptop-fix-missing-cleanups-in-init-error-path.patch b/queue-6.6/platform-x86-dell-laptop-fix-missing-cleanups-in-init-error-path.patch
new file mode 100644 (file)
index 0000000..039f5ac
--- /dev/null
@@ -0,0 +1,52 @@
+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);
diff --git a/queue-6.6/platform-x86-dell-smbios-move-request-functions-for-reuse.patch b/queue-6.6/platform-x86-dell-smbios-move-request-functions-for-reuse.patch
new file mode 100644 (file)
index 0000000..d1d1c67
--- /dev/null
@@ -0,0 +1,108 @@
+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 {
diff --git a/queue-6.6/rxrpc-don-t-need-barrier-for-tx_bottom-and-acks_hard_ack.patch b/queue-6.6/rxrpc-don-t-need-barrier-for-tx_bottom-and-acks_hard_ack.patch
new file mode 100644 (file)
index 0000000..f799feb
--- /dev/null
@@ -0,0 +1,82 @@
+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);
diff --git a/queue-6.6/rxrpc-fix-notification-vs-call-release-vs-recvmsg.patch b/queue-6.6/rxrpc-fix-notification-vs-call-release-vs-recvmsg.patch
new file mode 100644 (file)
index 0000000..6c5b24e
--- /dev/null
@@ -0,0 +1,138 @@
+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();
diff --git a/queue-6.6/rxrpc-fix-socket-notification-race.patch b/queue-6.6/rxrpc-fix-socket-notification-race.patch
new file mode 100644 (file)
index 0000000..39ddfe2
--- /dev/null
@@ -0,0 +1,47 @@
+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;
diff --git a/queue-6.6/rxrpc-pull-out-certain-app-callback-funcs-into-an-ops-table.patch b/queue-6.6/rxrpc-pull-out-certain-app-callback-funcs-into-an-ops-table.patch
new file mode 100644 (file)
index 0000000..9695f33
--- /dev/null
@@ -0,0 +1,294 @@
+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)
diff --git a/queue-6.6/rxrpc-serialize-kernel-accept-preallocation-with-socket-teardown.patch b/queue-6.6/rxrpc-serialize-kernel-accept-preallocation-with-socket-teardown.patch
new file mode 100644 (file)
index 0000000..d1f5522
--- /dev/null
@@ -0,0 +1,79 @@
+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);
diff --git a/queue-6.6/rxrpc-use-irq-disabling-spinlocks-between-app-and-i-o-thread.patch b/queue-6.6/rxrpc-use-irq-disabling-spinlocks-between-app-and-i-o-thread.patch
new file mode 100644 (file)
index 0000000..f1271f6
--- /dev/null
@@ -0,0 +1,447 @@
+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);
diff --git a/queue-6.6/selinux-avoid-sk_socket-dereference-in-selinux_sctp_bind_connect.patch b/queue-6.6/selinux-avoid-sk_socket-dereference-in-selinux_sctp_bind_connect.patch
new file mode 100644 (file)
index 0000000..41426e1
--- /dev/null
@@ -0,0 +1,112 @@
+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;
diff --git a/queue-6.6/serial-max310x-implement-gpio_chip-get_direction.patch b/queue-6.6/serial-max310x-implement-gpio_chip-get_direction.patch
new file mode 100644 (file)
index 0000000..e5e8313
--- /dev/null
@@ -0,0 +1,64 @@
+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;
diff --git a/queue-6.6/serial-max310x-replace-bare-use-of-unsigned-with-unsigned-int-checkpatch.patch b/queue-6.6/serial-max310x-replace-bare-use-of-unsigned-with-unsigned-int-checkpatch.patch
new file mode 100644 (file)
index 0000000..8cdbd26
--- /dev/null
@@ -0,0 +1,68 @@
+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;
index a5afa24d6f694f0ba1fd76a3a690e2d3bb7a0684..9d855aaa4babf0a1b082d6e43f42a1422167cf55 100644 (file)
@@ -360,3 +360,117 @@ drm-amd-display-fix-dcn32-dtb-dto-update-breaking-li.patch
 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
diff --git a/queue-6.6/sunrpc-add-helpers-to-convert-xdr_buf-byte-ranges-to-scatterlists.patch b/queue-6.6/sunrpc-add-helpers-to-convert-xdr_buf-byte-ranges-to-scatterlists.patch
new file mode 100644 (file)
index 0000000..e3d9094
--- /dev/null
@@ -0,0 +1,279 @@
+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
diff --git a/queue-6.6/sunrpc-allocate-a-separate-bvec-array-for-socket-sends.patch b/queue-6.6/sunrpc-allocate-a-separate-bvec-array-for-socket-sends.patch
new file mode 100644 (file)
index 0000000..4b50588
--- /dev/null
@@ -0,0 +1,140 @@
+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);
+ }
diff --git a/queue-6.6/sunrpc-return-an-error-from-xdr_buf_to_bvec-on-overflow.patch b/queue-6.6/sunrpc-return-an-error-from-xdr_buf_to_bvec-on-overflow.patch
new file mode 100644 (file)
index 0000000..c08ef60
--- /dev/null
@@ -0,0 +1,142 @@
+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;
+ }
+ /**
diff --git a/queue-6.6/taskstats-fill_stats_for_tgid-use-for_each_thread.patch b/queue-6.6/taskstats-fill_stats_for_tgid-use-for_each_thread.patch
new file mode 100644 (file)
index 0000000..b617ed7
--- /dev/null
@@ -0,0 +1,54 @@
+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;
diff --git a/queue-6.6/taskstats-retain-dead-thread-stats-in-tgid-queries.patch b/queue-6.6/taskstats-retain-dead-thread-stats-in-tgid-queries.patch
new file mode 100644 (file)
index 0000000..29ed75f
--- /dev/null
@@ -0,0 +1,170 @@
+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;
diff --git a/queue-6.6/thunderbolt-handle-lane-bonding-of-gen-4-xdomain-links-properly.patch b/queue-6.6/thunderbolt-handle-lane-bonding-of-gen-4-xdomain-links-properly.patch
new file mode 100644 (file)
index 0000000..6f77eae
--- /dev/null
@@ -0,0 +1,110 @@
+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()
diff --git a/queue-6.6/thunderbolt-keep-xdomain-reference-during-the-lifetime-of-a-service.patch b/queue-6.6/thunderbolt-keep-xdomain-reference-during-the-lifetime-of-a-service.patch
new file mode 100644 (file)
index 0000000..9171c6b
--- /dev/null
@@ -0,0 +1,42 @@
+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);
diff --git a/queue-6.6/thunderbolt-prevent-xdomain-delayed-work-use-after-free-on-disconnect.patch b/queue-6.6/thunderbolt-prevent-xdomain-delayed-work-use-after-free-on-disconnect.patch
new file mode 100644 (file)
index 0000000..21e9b75
--- /dev/null
@@ -0,0 +1,90 @@
+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);
diff --git a/queue-6.6/thunderbolt-remove-service-debugfs-entries-during-unregister.patch b/queue-6.6/thunderbolt-remove-service-debugfs-entries-during-unregister.patch
new file mode 100644 (file)
index 0000000..ecf7872
--- /dev/null
@@ -0,0 +1,76 @@
+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;
+ }
diff --git a/queue-6.6/thunderbolt-remove-usage-of-the-deprecated-ida_simple_xx-api.patch b/queue-6.6/thunderbolt-remove-usage-of-the-deprecated-ida_simple_xx-api.patch
new file mode 100644 (file)
index 0000000..bbe5639
--- /dev/null
@@ -0,0 +1,159 @@
+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);
diff --git a/queue-6.6/thunderbolt-remove-xdomain-from-the-bus-without-holding-tb-lock.patch b/queue-6.6/thunderbolt-remove-xdomain-from-the-bus-without-holding-tb-lock.patch
new file mode 100644 (file)
index 0000000..f41c1de
--- /dev/null
@@ -0,0 +1,337 @@
+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
+  *
diff --git a/queue-6.6/thunderbolt-update-property.c-function-documentation.patch b/queue-6.6/thunderbolt-update-property.c-function-documentation.patch
new file mode 100644 (file)
index 0000000..f01daf2
--- /dev/null
@@ -0,0 +1,137 @@
+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)
diff --git a/queue-6.6/tipc-restrict-socket-queue-dumps-in-enqueue-tracepoints.patch b/queue-6.6/tipc-restrict-socket-queue-dumps-in-enqueue-tracepoints.patch
new file mode 100644 (file)
index 0000000..9f61b5a
--- /dev/null
@@ -0,0 +1,97 @@
+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);
+               }
diff --git a/queue-6.6/tracing-user_events-fix-use-after-free-in-user_event_mm_dup.patch b/queue-6.6/tracing-user_events-fix-use-after-free-in-user_event_mm_dup.patch
new file mode 100644 (file)
index 0000000..eab5649
--- /dev/null
@@ -0,0 +1,148 @@
+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;
diff --git a/queue-6.6/udmabuf-do-not-create-malformed-scatterlists.patch b/queue-6.6/udmabuf-do-not-create-malformed-scatterlists.patch
new file mode 100644 (file)
index 0000000..55aa4ee
--- /dev/null
@@ -0,0 +1,59 @@
+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);
+ }
diff --git a/queue-6.6/vduse-avoid-leaking-information-to-userspace.patch b/queue-6.6/vduse-avoid-leaking-information-to-userspace.patch
new file mode 100644 (file)
index 0000000..a872007
--- /dev/null
@@ -0,0 +1,53 @@
+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;
diff --git a/queue-6.6/vduse-remove-unused-vaddr-parameter-of-vduse_domain_free_coherent.patch b/queue-6.6/vduse-remove-unused-vaddr-parameter-of-vduse_domain_free_coherent.patch
new file mode 100644 (file)
index 0000000..d7178f0
--- /dev/null
@@ -0,0 +1,61 @@
+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)
diff --git a/queue-6.6/vduse-take-out-allocations-from-vduse_dev_alloc_coherent.patch b/queue-6.6/vduse-take-out-allocations-from-vduse_dev_alloc_coherent.patch
new file mode 100644 (file)
index 0000000..887642c
--- /dev/null
@@ -0,0 +1,147 @@
+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)
diff --git a/queue-6.6/vduse-use-fixed-4kb-bounce-pages-for-non-4kb-page-size.patch b/queue-6.6/vduse-use-fixed-4kb-bounce-pages-for-non-4kb-page-size.patch
new file mode 100644 (file)
index 0000000..1e1e140
--- /dev/null
@@ -0,0 +1,297 @@
+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;
diff --git a/queue-6.6/vfs-audit-introduce-kern_path_parent-for-audit.patch b/queue-6.6/vfs-audit-introduce-kern_path_parent-for-audit.patch
new file mode 100644 (file)
index 0000000..ee698e9
--- /dev/null
@@ -0,0 +1,191 @@
+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;
+ }
diff --git a/queue-6.6/wifi-libertas_tf-fix-use-after-free-in-lbtf_free_adapter.patch b/queue-6.6/wifi-libertas_tf-fix-use-after-free-in-lbtf_free_adapter.patch
new file mode 100644 (file)
index 0000000..acd8556
--- /dev/null
@@ -0,0 +1,49 @@
+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);
+ }
diff --git a/queue-6.6/workqueue-add-system_percpu_wq-and-system_dfl_wq.patch b/queue-6.6/workqueue-add-system_percpu_wq-and-system_dfl_wq.patch
new file mode 100644 (file)
index 0000000..a741b34
--- /dev/null
@@ -0,0 +1,119 @@
+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);
+ }
diff --git a/queue-6.6/workqueue-factor-out-init_cpu_worker_pool.patch b/queue-6.6/workqueue-factor-out-init_cpu_worker_pool.patch
new file mode 100644 (file)
index 0000000..aa7d86d
--- /dev/null
@@ -0,0 +1,72 @@
+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 */