]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
5.15-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 21 Jul 2026 13:51:58 +0000 (15:51 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 21 Jul 2026 13:51:58 +0000 (15:51 +0200)
added patches:
acpi-cppc-suppress-ubsan-warning-caused-by-field-misuse.patch
acpi-nfit-core-fix-acpi_nfit_init-error-cleanup.patch
alsa-aoa-check-snd_ctl_new1-return-value.patch
audit-add-audit_log_nf_skb-helper-function.patch
audit-fix-potential-integer-overflow-in-audit_log_n_hex.patch
bitops-make-bytes_to_bits-treewide-available.patch
bluetooth-l2cap-cancel-pending_rx_work-before-taking-conn-lock.patch
bluetooth-l2cap-fix-deadlock-in-l2cap_conn_del.patch
bluetooth-l2cap-fix-not-tracking-outstanding-tx-ident.patch
bpf-allow-lpm-map-access-from-sleepable-bpf-programs.patch
bpf-consistently-use-bpf_rcu_lock_held-everywhere.patch
bpf-convert-lpm_trie.c-to-rqspinlock.patch
bpf-reject-fragmented-frames-in-devmap.patch
btrfs-do-not-trim-a-device-which-is-not-writeable.patch
btrfs-fix-false-io-failure-after-falling-back-to-buffered-write.patch
btrfs-fix-incorrect-buffered-io-fallback-for-append-direct-writes.patch
cifs-add-tracing-for-the-cifs_tcon-struct-refcounting.patch
cifs-create-a-new-shared-file-holding-smb2-pdu-definitions.patch
cifs-remove-check-of-list-iterator-against-head-past-the-loop-body.patch
cifs-remove-unused-server-parameter-from-calc_smb_size.patch
coresight-etb10-restore-atomic_t-for-shared-reading-state.patch
crypto-atmel-drop-explicit-initialization-of-struct-i2c_device_id-driver_data-to-0.patch
crypto-atmel-sha204a-drop-hwrng-quality-reduction-for-atsha204a.patch
crypto-atmel-sha204a-mark-of-related-data-as-maybe-unused.patch
crypto-qat-fix-restarting-state-leak-on-allocation-failure.patch
crypto-qat-fix-vf2pf-work-teardown-race-in-adf_disable_sriov.patch
crypto-qat-validate-rsa-crt-component-lengths.patch
drm-i2c-sil164-drop-no-op-remove-function.patch
gpio-pca953x-make-platform-teardown-callback-return-void.patch
gpio-sch-use-new-gpio-line-value-setter-callbacks.patch
gpio-sch-use-raw_spinlock_t-in-the-irq-startup-path.patch
hfs-hfsplus-fix-u32-overflow-in-check_and_correct_requested_length.patch
hfs-hfsplus-prevent-getting-negative-values-of-offset-length.patch
hid-add-haptics-page-defines.patch
hid-appleir-fix-uaf-on-pending-key_up_timer-in-remove.patch
hid-multitouch-fix-out-of-bounds-bit-access-on-mt_io_flags.patch
iio-common-st_sensors-honour-channel-endianness-in-read_axis_data.patch
iio-hid-sensor-rotation-fix-stale-or-zero-output-when-reading-raw-values.patch
iio-imu-adis-add-irqf_no_thread-to-non-fifo-trigger-irq.patch
iio-imu-inv_icm42600-fix-timestamping-by-limiting-fifo-reading.patch
iio-imu-inv_icm42600-make-timestamp-module-chip-independent.patch
iio-imu-inv_icm42600-stabilized-timestamp-in-interrupt.patch
iio-imu-inv_mpu6050-use-the-common-inv_sensors-timestamp-module.patch
iio-invensense-fix-timestamp-glitches-when-switching-frequency.patch
iio-invensense-remove-redundant-initialization-of-variable-period.patch
iio-make-invensense-timestamp-module-generic.patch
iio-move-inv_icm42600-timestamp-module-in-common.patch
ksmbd-centralize-ksmbd_conn-final-release-to-plug-transport-leak.patch
ksmbd-destroy-async_ida-in-ksmbd_conn_free.patch
ksmbd-fix-mechtoken-leak-when-spnego-decode-fails-after-token-alloc.patch
ksmbd-fix-use-after-free-in-__ksmbd_close_fd-via-durable-scavenger.patch
ksmbd-track-the-connection-owning-a-byte-range-lock.patch
leds-lm3601x-improve-error-reporting-for-problems-during-.remove.patch
leds-lm3697-remove-duplicated-error-reporting-in-.remove.patch
netfilter-ebtables-use-vmalloc_array-to-improve-code.patch
netfilter-ebtables-zero-chainstack-array.patch
pci-add-kerneldoc-for-pci_resize_resource.patch
pci-altera-fix-resource-leaks-on-probe-failure.patch
pci-controller-use-dev_fwnode-instead-of-of_fwnode_handle.patch
pci-fix-restoring-bars-on-bar-resize-rollback-path.patch
pci-free-saved-list-without-holding-pci_bus_sem.patch
pci-imx6-fix-imx6sx_gpr12_pcie_test_powerdown-handling.patch
pci-mediatek-convert-bool-to-single-quirks-entry-and-bitmap.patch
pci-mediatek-fix-irq-domain-leak-when-port-fails-to-enable.patch
pci-move-resizable-bar-code-to-rebar.c.patch
pci-prevent-resource-tree-corruption-when-bar-resize-fails.patch
pci-skip-resizable-bar-restore-on-read-error.patch
perf-x86-intel-uncore-defer-adl-global-pmon-enable-to-enable_box.patch
proc-move-fdinfo-ptrace_mode_read-check-into-the-inode-.permission-operation.patch
proc-protect-ptrace_may_access-with-exec_update_lock-fd-links.patch
proc-protect-ptrace_may_access-with-exec_update_lock-part-1.patch
proc-rename-proc_setattr-to-proc_nochmod_setattr.patch
proc-use-generic-setattr-for-proc-pid-net.patch
regulator-scmi-fix-of_node-refcount-leak-in-scmi_regulator_probe.patch
serial-8250_mid-disable-dma-for-selected-platforms.patch
serial-8250_mid-remove-8250_pci-usage.patch
smb-client-fix-next-buffer-leak-in-receive_encrypted_standard.patch
smb-client-harden-posix-sid-length-parsing.patch
smb-client-mask-server-provided-mode-to-07777-in-modefromsid.patch
smb-client-resolve-swn-tcon-from-live-registrations.patch
smb-client-restrict-implied-bcc-exemption-to-responses-without-data-area.patch
smb-client-use-unaligned-reads-in-parse_posix_ctxt.patch
smb2-small-refactor-in-smb2_check_message.patch
staging-rtl8723bs-core-move-constants-to-right-side-in-comparison.patch
staging-rtl8723bs-fix-heap-buffer-overflow-in-rtw_cfg80211_set_wpa_ie.patch
staging-rtl8723bs-fix-indentation-issues.patch
staging-rtl8723bs-fix-oob-reads-in-rtw_get_sec_ie-rtw_get_wapi_ie-and-rtw_get_wps_attr.patch
staging-rtl8723bs-fix-space-issues.patch
staging-rtl8723bs-fix-spaces-around-binary-operators.patch
staging-rtl8723bs-remove-redundant-braces-in-if-statements.patch
staging-rtl8723bs-remove-redundant-else-branches.patch
treewide-switch-rename-to-timer_delete.patch
usb-atm-ueagle-atm-wait-for-pre-firmware-load-in-.disconnect.patch
usb-gadget-f_fs-initialize-reset_work-at-allocation-time.patch
usb-gadget-f_fs-tie-read_buffer-lifetime-to-ffs_epfile.patch
usb-iowarrior-remove-inherent-race-with-minor-number.patch
usb-typec-tcpm-fix-vdm-type-for-enter-mode-commands.patch
usb-typec-ucsi-ccg-fix-use-after-free-of-ucsi-on-remove.patch
vfio-pci-release-the-vga-arbiter-client-on-register_device-failure.patch
x.509-fix-validation-of-asn.1-certificate-header.patch
xdp-introduce-flags-field-in-xdp_buff-xdp_frame.patch
xfs-remove-dead-code.patch
xfs-remove-redundant-assignment-of-mp.patch
xfs-use-null-daddr-for-unset-first-bad-log-block.patch

105 files changed:
queue-5.15/acpi-cppc-suppress-ubsan-warning-caused-by-field-misuse.patch [new file with mode: 0644]
queue-5.15/acpi-nfit-core-fix-acpi_nfit_init-error-cleanup.patch [new file with mode: 0644]
queue-5.15/alsa-aoa-check-snd_ctl_new1-return-value.patch [new file with mode: 0644]
queue-5.15/audit-add-audit_log_nf_skb-helper-function.patch [new file with mode: 0644]
queue-5.15/audit-fix-potential-integer-overflow-in-audit_log_n_hex.patch [new file with mode: 0644]
queue-5.15/bitops-make-bytes_to_bits-treewide-available.patch [new file with mode: 0644]
queue-5.15/bluetooth-l2cap-cancel-pending_rx_work-before-taking-conn-lock.patch [new file with mode: 0644]
queue-5.15/bluetooth-l2cap-fix-deadlock-in-l2cap_conn_del.patch [new file with mode: 0644]
queue-5.15/bluetooth-l2cap-fix-not-tracking-outstanding-tx-ident.patch [new file with mode: 0644]
queue-5.15/bpf-allow-lpm-map-access-from-sleepable-bpf-programs.patch [new file with mode: 0644]
queue-5.15/bpf-consistently-use-bpf_rcu_lock_held-everywhere.patch [new file with mode: 0644]
queue-5.15/bpf-convert-lpm_trie.c-to-rqspinlock.patch [new file with mode: 0644]
queue-5.15/bpf-reject-fragmented-frames-in-devmap.patch [new file with mode: 0644]
queue-5.15/btrfs-do-not-trim-a-device-which-is-not-writeable.patch [new file with mode: 0644]
queue-5.15/btrfs-fix-false-io-failure-after-falling-back-to-buffered-write.patch [new file with mode: 0644]
queue-5.15/btrfs-fix-incorrect-buffered-io-fallback-for-append-direct-writes.patch [new file with mode: 0644]
queue-5.15/cifs-add-tracing-for-the-cifs_tcon-struct-refcounting.patch [new file with mode: 0644]
queue-5.15/cifs-create-a-new-shared-file-holding-smb2-pdu-definitions.patch [new file with mode: 0644]
queue-5.15/cifs-remove-check-of-list-iterator-against-head-past-the-loop-body.patch [new file with mode: 0644]
queue-5.15/cifs-remove-unused-server-parameter-from-calc_smb_size.patch [new file with mode: 0644]
queue-5.15/coresight-etb10-restore-atomic_t-for-shared-reading-state.patch [new file with mode: 0644]
queue-5.15/crypto-atmel-drop-explicit-initialization-of-struct-i2c_device_id-driver_data-to-0.patch [new file with mode: 0644]
queue-5.15/crypto-atmel-sha204a-drop-hwrng-quality-reduction-for-atsha204a.patch [new file with mode: 0644]
queue-5.15/crypto-atmel-sha204a-mark-of-related-data-as-maybe-unused.patch [new file with mode: 0644]
queue-5.15/crypto-qat-fix-restarting-state-leak-on-allocation-failure.patch [new file with mode: 0644]
queue-5.15/crypto-qat-fix-vf2pf-work-teardown-race-in-adf_disable_sriov.patch [new file with mode: 0644]
queue-5.15/crypto-qat-validate-rsa-crt-component-lengths.patch [new file with mode: 0644]
queue-5.15/drm-i2c-sil164-drop-no-op-remove-function.patch [new file with mode: 0644]
queue-5.15/gpio-pca953x-make-platform-teardown-callback-return-void.patch [new file with mode: 0644]
queue-5.15/gpio-sch-use-new-gpio-line-value-setter-callbacks.patch [new file with mode: 0644]
queue-5.15/gpio-sch-use-raw_spinlock_t-in-the-irq-startup-path.patch [new file with mode: 0644]
queue-5.15/hfs-hfsplus-fix-u32-overflow-in-check_and_correct_requested_length.patch [new file with mode: 0644]
queue-5.15/hfs-hfsplus-prevent-getting-negative-values-of-offset-length.patch [new file with mode: 0644]
queue-5.15/hid-add-haptics-page-defines.patch [new file with mode: 0644]
queue-5.15/hid-appleir-fix-uaf-on-pending-key_up_timer-in-remove.patch [new file with mode: 0644]
queue-5.15/hid-multitouch-fix-out-of-bounds-bit-access-on-mt_io_flags.patch [new file with mode: 0644]
queue-5.15/iio-common-st_sensors-honour-channel-endianness-in-read_axis_data.patch [new file with mode: 0644]
queue-5.15/iio-hid-sensor-rotation-fix-stale-or-zero-output-when-reading-raw-values.patch [new file with mode: 0644]
queue-5.15/iio-imu-adis-add-irqf_no_thread-to-non-fifo-trigger-irq.patch [new file with mode: 0644]
queue-5.15/iio-imu-inv_icm42600-fix-timestamping-by-limiting-fifo-reading.patch [new file with mode: 0644]
queue-5.15/iio-imu-inv_icm42600-make-timestamp-module-chip-independent.patch [new file with mode: 0644]
queue-5.15/iio-imu-inv_icm42600-stabilized-timestamp-in-interrupt.patch [new file with mode: 0644]
queue-5.15/iio-imu-inv_mpu6050-use-the-common-inv_sensors-timestamp-module.patch [new file with mode: 0644]
queue-5.15/iio-invensense-fix-timestamp-glitches-when-switching-frequency.patch [new file with mode: 0644]
queue-5.15/iio-invensense-remove-redundant-initialization-of-variable-period.patch [new file with mode: 0644]
queue-5.15/iio-make-invensense-timestamp-module-generic.patch [new file with mode: 0644]
queue-5.15/iio-move-inv_icm42600-timestamp-module-in-common.patch [new file with mode: 0644]
queue-5.15/ksmbd-centralize-ksmbd_conn-final-release-to-plug-transport-leak.patch [new file with mode: 0644]
queue-5.15/ksmbd-destroy-async_ida-in-ksmbd_conn_free.patch [new file with mode: 0644]
queue-5.15/ksmbd-fix-mechtoken-leak-when-spnego-decode-fails-after-token-alloc.patch [new file with mode: 0644]
queue-5.15/ksmbd-fix-use-after-free-in-__ksmbd_close_fd-via-durable-scavenger.patch [new file with mode: 0644]
queue-5.15/ksmbd-track-the-connection-owning-a-byte-range-lock.patch [new file with mode: 0644]
queue-5.15/leds-lm3601x-improve-error-reporting-for-problems-during-.remove.patch [new file with mode: 0644]
queue-5.15/leds-lm3697-remove-duplicated-error-reporting-in-.remove.patch [new file with mode: 0644]
queue-5.15/netfilter-ebtables-use-vmalloc_array-to-improve-code.patch [new file with mode: 0644]
queue-5.15/netfilter-ebtables-zero-chainstack-array.patch [new file with mode: 0644]
queue-5.15/pci-add-kerneldoc-for-pci_resize_resource.patch [new file with mode: 0644]
queue-5.15/pci-altera-fix-resource-leaks-on-probe-failure.patch [new file with mode: 0644]
queue-5.15/pci-controller-use-dev_fwnode-instead-of-of_fwnode_handle.patch [new file with mode: 0644]
queue-5.15/pci-fix-restoring-bars-on-bar-resize-rollback-path.patch [new file with mode: 0644]
queue-5.15/pci-free-saved-list-without-holding-pci_bus_sem.patch [new file with mode: 0644]
queue-5.15/pci-imx6-fix-imx6sx_gpr12_pcie_test_powerdown-handling.patch [new file with mode: 0644]
queue-5.15/pci-mediatek-convert-bool-to-single-quirks-entry-and-bitmap.patch [new file with mode: 0644]
queue-5.15/pci-mediatek-fix-irq-domain-leak-when-port-fails-to-enable.patch [new file with mode: 0644]
queue-5.15/pci-move-resizable-bar-code-to-rebar.c.patch [new file with mode: 0644]
queue-5.15/pci-prevent-resource-tree-corruption-when-bar-resize-fails.patch [new file with mode: 0644]
queue-5.15/pci-skip-resizable-bar-restore-on-read-error.patch [new file with mode: 0644]
queue-5.15/perf-x86-intel-uncore-defer-adl-global-pmon-enable-to-enable_box.patch [new file with mode: 0644]
queue-5.15/proc-move-fdinfo-ptrace_mode_read-check-into-the-inode-.permission-operation.patch [new file with mode: 0644]
queue-5.15/proc-protect-ptrace_may_access-with-exec_update_lock-fd-links.patch [new file with mode: 0644]
queue-5.15/proc-protect-ptrace_may_access-with-exec_update_lock-part-1.patch [new file with mode: 0644]
queue-5.15/proc-rename-proc_setattr-to-proc_nochmod_setattr.patch [new file with mode: 0644]
queue-5.15/proc-use-generic-setattr-for-proc-pid-net.patch [new file with mode: 0644]
queue-5.15/regulator-scmi-fix-of_node-refcount-leak-in-scmi_regulator_probe.patch [new file with mode: 0644]
queue-5.15/serial-8250_mid-disable-dma-for-selected-platforms.patch [new file with mode: 0644]
queue-5.15/serial-8250_mid-remove-8250_pci-usage.patch [new file with mode: 0644]
queue-5.15/series
queue-5.15/smb-client-fix-next-buffer-leak-in-receive_encrypted_standard.patch [new file with mode: 0644]
queue-5.15/smb-client-harden-posix-sid-length-parsing.patch [new file with mode: 0644]
queue-5.15/smb-client-mask-server-provided-mode-to-07777-in-modefromsid.patch [new file with mode: 0644]
queue-5.15/smb-client-resolve-swn-tcon-from-live-registrations.patch [new file with mode: 0644]
queue-5.15/smb-client-restrict-implied-bcc-exemption-to-responses-without-data-area.patch [new file with mode: 0644]
queue-5.15/smb-client-use-unaligned-reads-in-parse_posix_ctxt.patch [new file with mode: 0644]
queue-5.15/smb2-small-refactor-in-smb2_check_message.patch [new file with mode: 0644]
queue-5.15/staging-rtl8723bs-core-move-constants-to-right-side-in-comparison.patch [new file with mode: 0644]
queue-5.15/staging-rtl8723bs-fix-heap-buffer-overflow-in-rtw_cfg80211_set_wpa_ie.patch [new file with mode: 0644]
queue-5.15/staging-rtl8723bs-fix-indentation-issues.patch [new file with mode: 0644]
queue-5.15/staging-rtl8723bs-fix-oob-reads-in-rtw_get_sec_ie-rtw_get_wapi_ie-and-rtw_get_wps_attr.patch [new file with mode: 0644]
queue-5.15/staging-rtl8723bs-fix-space-issues.patch [new file with mode: 0644]
queue-5.15/staging-rtl8723bs-fix-spaces-around-binary-operators.patch [new file with mode: 0644]
queue-5.15/staging-rtl8723bs-remove-redundant-braces-in-if-statements.patch [new file with mode: 0644]
queue-5.15/staging-rtl8723bs-remove-redundant-else-branches.patch [new file with mode: 0644]
queue-5.15/treewide-switch-rename-to-timer_delete.patch [new file with mode: 0644]
queue-5.15/usb-atm-ueagle-atm-wait-for-pre-firmware-load-in-.disconnect.patch [new file with mode: 0644]
queue-5.15/usb-gadget-f_fs-initialize-reset_work-at-allocation-time.patch [new file with mode: 0644]
queue-5.15/usb-gadget-f_fs-tie-read_buffer-lifetime-to-ffs_epfile.patch [new file with mode: 0644]
queue-5.15/usb-iowarrior-remove-inherent-race-with-minor-number.patch [new file with mode: 0644]
queue-5.15/usb-typec-tcpm-fix-vdm-type-for-enter-mode-commands.patch [new file with mode: 0644]
queue-5.15/usb-typec-ucsi-ccg-fix-use-after-free-of-ucsi-on-remove.patch [new file with mode: 0644]
queue-5.15/vfio-pci-release-the-vga-arbiter-client-on-register_device-failure.patch [new file with mode: 0644]
queue-5.15/x.509-fix-validation-of-asn.1-certificate-header.patch [new file with mode: 0644]
queue-5.15/xdp-introduce-flags-field-in-xdp_buff-xdp_frame.patch [new file with mode: 0644]
queue-5.15/xfs-remove-dead-code.patch [new file with mode: 0644]
queue-5.15/xfs-remove-redundant-assignment-of-mp.patch [new file with mode: 0644]
queue-5.15/xfs-use-null-daddr-for-unset-first-bad-log-block.patch [new file with mode: 0644]

diff --git a/queue-5.15/acpi-cppc-suppress-ubsan-warning-caused-by-field-misuse.patch b/queue-5.15/acpi-cppc-suppress-ubsan-warning-caused-by-field-misuse.patch
new file mode 100644 (file)
index 0000000..7881594
--- /dev/null
@@ -0,0 +1,85 @@
+From stable+bounces-273234-greg=kroah.com@vger.kernel.org Fri Jul 10 15:31:18 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 10 Jul 2026 09:30:16 -0400
+Subject: ACPI: CPPC: Suppress UBSAN warning caused by field misuse
+To: stable@vger.kernel.org
+Cc: Jeremy Linton <jeremy.linton@arm.com>, Jarred White <jarredwhite@linux.microsoft.com>, Easwar Hariharan <easwar.hariharan@linux.microsoft.com>, "Rafael J. Wysocki" <rafael.j.wysocki@intel.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260710133016.79831-1-sashal@kernel.org>
+
+From: Jeremy Linton <jeremy.linton@arm.com>
+
+[ Upstream commit 1b1acf2dada0cc3931bb2cb9ff8832edfbee46a1 ]
+
+The definition of reg->access_width changes depending on the
+reg->space_id type.  Type ACPI_ADR_SPACE_PLATFORM_COMM uses
+access_width to indicate the PCC region, which can result in a UBSAN
+if the value is greater than 4.
+
+For example:
+
+ UBSAN: shift-out-of-bounds in drivers/acpi/cppc_acpi.c:1090:9
+ shift exponent 32 is too large for 32-bit type 'int'
+ CPU: 61 UID: 0 PID: 1220 Comm: (udev-worker) Not tainted 7.0.10-201.fc44.aarch64 #1 PREEMPT(lazy)
+ Hardware name: To be filled by O.E.M.
+ Call trace:
+  ...(trimming)
+  ubsan_epilogue+0x10/0x48
+  __ubsan_handle_shift_out_of_bounds+0xdc/0x1e0
+  cpc_write+0x4d0/0x670
+  cppc_set_perf+0x18c/0x490
+  cppc_cpufreq_cpu_init+0x1c8/0x380 [cppc_cpufreq]
+  ... (trimming)
+
+Lets fix this by validating the region type, as well as whether
+access_width has a value. Then since we are returning bit_width
+directly for ACPI_ADR_SPACE_PLATFORM_COMM, drop the code correcting
+the size.
+
+Fixes: 2f4a4d63a193 ("ACPI: CPPC: Use access_width over bit_width for system memory accesses")
+Signed-off-by: Jeremy Linton <jeremy.linton@arm.com>
+Tested-by: Jarred White <jarredwhite@linux.microsoft.com>
+Reviewed-by: Jarred White <jarredwhite@linux.microsoft.com>
+Reviewed-by: Easwar Hariharan <easwar.hariharan@linux.microsoft.com>
+Cc: All applicable <stable@vger.kernel.org>
+Link: https://patch.msgid.link/20260601235808.1113137-1-jeremy.linton@arm.com
+Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/acpi/cppc_acpi.c |   11 +++++++----
+ 1 file changed, 7 insertions(+), 4 deletions(-)
+
+--- a/drivers/acpi/cppc_acpi.c
++++ b/drivers/acpi/cppc_acpi.c
+@@ -161,8 +161,13 @@ show_cppc_data(cppc_get_perf_caps, cppc_
+ show_cppc_data(cppc_get_perf_ctrs, cppc_perf_fb_ctrs, reference_perf);
+ show_cppc_data(cppc_get_perf_ctrs, cppc_perf_fb_ctrs, wraparound_time);
+-/* Check for valid access_width, otherwise, fallback to using bit_width */
+-#define GET_BIT_WIDTH(reg) ((reg)->access_width ? (8 << ((reg)->access_width - 1)) : (reg)->bit_width)
++/*
++ * PCC reuses the access_width field as the subspace id, so only decode access
++ * size for non-PCC registers. Otherwise, use the bit_width.
++ */
++#define GET_BIT_WIDTH(reg) (((reg)->access_width &&                           \
++                           (reg)->space_id != ACPI_ADR_SPACE_PLATFORM_COMM) ? \
++                          (8 << ((reg)->access_width - 1)) : (reg)->bit_width)
+ /* Shift and apply the mask for CPC reads/writes */
+ #define MASK_VAL_READ(reg, val) (((val) >> (reg)->bit_offset) &                               \
+@@ -970,7 +975,6 @@ static int cpc_read(int cpu, struct cpc_
+                * by the bit width field; the access size is used to indicate
+                * the PCC subspace id.
+                */
+-              size = reg->bit_width;
+               vaddr = GET_PCC_VADDR(reg->address, pcc_ss_id);
+       }
+       else if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY)
+@@ -1030,7 +1034,6 @@ static int cpc_write(int cpu, struct cpc
+                * by the bit width field; the access size is used to indicate
+                * the PCC subspace id.
+                */
+-              size = reg->bit_width;
+               vaddr = GET_PCC_VADDR(reg->address, pcc_ss_id);
+       }
+       else if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY)
diff --git a/queue-5.15/acpi-nfit-core-fix-acpi_nfit_init-error-cleanup.patch b/queue-5.15/acpi-nfit-core-fix-acpi_nfit_init-error-cleanup.patch
new file mode 100644 (file)
index 0000000..8eebaa8
--- /dev/null
@@ -0,0 +1,103 @@
+From stable+bounces-273253-greg=kroah.com@vger.kernel.org Fri Jul 10 16:55:10 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 10 Jul 2026 10:38:56 -0400
+Subject: ACPI: NFIT: core: Fix acpi_nfit_init() error cleanup
+To: stable@vger.kernel.org
+Cc: "Rafael J. Wysocki" <rafael.j.wysocki@intel.com>, Dave Jiang <dave.jiang@intel.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260710143856.184042-1-sashal@kernel.org>
+
+From: "Rafael J. Wysocki" <rafael.j.wysocki@intel.com>
+
+[ Upstream commit 38bf27511ef41bffebd157ec3eba41fc89ba59cd ]
+
+If acpi_nfit_init() fails after adding the acpi_desc object to the
+acpi_descs list, that object is never removed from that list because
+the acpi_nfit_shutdown() devm action is not added for the NFIT device
+in that case.  Next, the acpi_nfit_init() failure causes
+acpi_nfit_probe() to fail, the acpi_desc object is freed, and a
+dangling pointer is left behind in the acpi_descs.  Any subsequent
+ACPI Machine Check Exception will trigger nfit_handle_mce() which
+iterates over acpi_descs and so a use-after-free will occur.
+
+Moreover, if acpi_nfit_probe() returns 0 after installing a notify
+handler for the NFIT device and without allocating the acpi_desc
+object and setting the NFIT device's driver data pointer, the
+acpi_desc object will be allocated by acpi_nfit_update_notify()
+and acpi_nfit_init() will be called to initialize it.  Regardless
+of whether or not acpi_nfit_init() fails in that case, the
+acpi_nfit_shutdown() devm action is not added for the NFIT device
+and acpi_desc is never removed from the acpi_descs list.  If the
+acpi_desc object is freed subsequently on driver removal, any
+subsequent ACPI MCE will lead to a use-after-free like in the
+previous case.
+
+To address the first issue mentioned above, make acpi_nfit_probe()
+call acpi_nfit_shutdown() directly on acpi_nfit_init() failures and
+to address the other one, add a remove callback to the driver and
+make it call acpi_nfit_shutdown().  Also, since it is now possible to
+pass NULL to acpi_nfit_shutdown() or the acpi_desc object passed to it
+may not have been initialized, add checks against NULL for acpi_desc and
+its nvdimm_bus field to that function and make acpi_nfit_unregister()
+clear the latter after unregistering the NVDIMM bus.
+
+Fixes: a61fe6f7902e ("nfit, tools/testing/nvdimm: unify common init for acpi_nfit_desc")
+Fixes: fbabd829fe76 ("acpi, nfit: fix module unload vs workqueue shutdown race")
+Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+Cc: All applicable <stable@vger.kernel.org>
+Reviewed-by: Dave Jiang <dave.jiang@intel.com>
+Link: https://patch.msgid.link/1963615.tdWV9SEqCh@rafael.j.wysocki
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/acpi/nfit/core.c |   15 +++++++++++----
+ 1 file changed, 11 insertions(+), 4 deletions(-)
+
+--- a/drivers/acpi/nfit/core.c
++++ b/drivers/acpi/nfit/core.c
+@@ -3445,6 +3445,8 @@ static void acpi_nfit_unregister(void *d
+       struct acpi_nfit_desc *acpi_desc = data;
+       nvdimm_bus_unregister(acpi_desc->nvdimm_bus);
++      /* The nvdimm_bus object may have been freed, so clear the pointer. */
++      acpi_desc->nvdimm_bus = NULL;
+ }
+ int acpi_nfit_init(struct acpi_nfit_desc *acpi_desc, void *data, acpi_size sz)
+@@ -3669,7 +3671,10 @@ static void acpi_nfit_put_table(void *ta
+ void acpi_nfit_shutdown(void *data)
+ {
+       struct acpi_nfit_desc *acpi_desc = data;
+-      struct device *bus_dev = to_nvdimm_bus_dev(acpi_desc->nvdimm_bus);
++      struct device *bus_dev;
++
++      if (!acpi_desc || !acpi_desc->nvdimm_bus)
++              return;
+       /*
+        * Destruct under acpi_desc_lock so that nfit_handle_mce does not
+@@ -3684,6 +3689,7 @@ void acpi_nfit_shutdown(void *data)
+       mutex_unlock(&acpi_desc->init_mutex);
+       cancel_delayed_work_sync(&acpi_desc->dwork);
++      bus_dev = to_nvdimm_bus_dev(acpi_desc->nvdimm_bus);
+       /*
+        * Bounce the nvdimm bus lock to make sure any in-flight
+        * acpi_nfit_ars_rescan() submissions have had a chance to
+@@ -3751,13 +3757,14 @@ static int acpi_nfit_add(struct acpi_dev
+                               sz - sizeof(struct acpi_table_nfit));
+       if (rc)
+-              return rc;
+-      return devm_add_action_or_reset(dev, acpi_nfit_shutdown, acpi_desc);
++              acpi_nfit_shutdown(acpi_desc);
++
++      return rc;
+ }
+ static int acpi_nfit_remove(struct acpi_device *adev)
+ {
+-      /* see acpi_nfit_unregister */
++      acpi_nfit_shutdown(dev_get_drvdata(&adev->dev));
+       return 0;
+ }
diff --git a/queue-5.15/alsa-aoa-check-snd_ctl_new1-return-value.patch b/queue-5.15/alsa-aoa-check-snd_ctl_new1-return-value.patch
new file mode 100644 (file)
index 0000000..c6b093d
--- /dev/null
@@ -0,0 +1,61 @@
+From stable+bounces-274081-greg=kroah.com@vger.kernel.org Tue Jul 14 04:10:08 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 13 Jul 2026 22:09:11 -0400
+Subject: ALSA: aoa: check snd_ctl_new1() return value
+To: stable@vger.kernel.org
+Cc: Zhao Dongdong <zhaodongdong@kylinos.cn>, Takashi Iwai <tiwai@suse.de>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260714020911.2355950-1-sashal@kernel.org>
+
+From: Zhao Dongdong <zhaodongdong@kylinos.cn>
+
+[ Upstream commit 8df560fefe6fed6a20b7e06720eeaeccec349ac0 ]
+
+snd_ctl_new1() can return NULL when memory allocation fails. In
+layout.c, the function does not check the return value before
+dereferencing ctl->id.name or passing to aoa_snd_ctl_add(), which can
+lead to a NULL pointer dereference.
+
+Add NULL checks after snd_ctl_new1() calls and return early if any
+fails.
+
+Assisted-by: Opencode:DeepSeek-V4-Flash
+Cc: stable@vger.kernel.org
+Fixes: f3d9478b2ce4 ("[ALSA] snd-aoa: add snd-aoa")
+Signed-off-by: Zhao Dongdong <zhaodongdong@kylinos.cn>
+Link: https://patch.msgid.link/tencent_35F3A25FEEBF190A2E15ED787754C57E3708@qq.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/aoa/fabrics/layout.c |    6 ++++++
+ 1 file changed, 6 insertions(+)
+
+--- a/sound/aoa/fabrics/layout.c
++++ b/sound/aoa/fabrics/layout.c
+@@ -947,6 +947,8 @@ static void layout_attached_codec(struct
+                       if (lineout == 1)
+                               ldev->gpio.methods->set_lineout(codec->gpio, 1);
+                       ctl = snd_ctl_new1(&lineout_ctl, codec->gpio);
++                      if (!ctl)
++                              return;
+                       if (cc->connected & CC_LINEOUT_LABELLED_HEADPHONE)
+                               strscpy(ctl->id.name,
+                                       "Headphone Switch", sizeof(ctl->id.name));
+@@ -961,6 +963,8 @@ static void layout_attached_codec(struct
+                       if (ldev->have_lineout_detect) {
+                               ctl = snd_ctl_new1(&lineout_detect_choice,
+                                                  ldev);
++                              if (!ctl)
++                                      return;
+                               if (cc->connected & CC_LINEOUT_LABELLED_HEADPHONE)
+                                       strscpy(ctl->id.name,
+                                               "Headphone Detect Autoswitch",
+@@ -968,6 +972,8 @@ static void layout_attached_codec(struct
+                               aoa_snd_ctl_add(ctl);
+                               ctl = snd_ctl_new1(&lineout_detected,
+                                                  ldev);
++                              if (!ctl)
++                                      return;
+                               if (cc->connected & CC_LINEOUT_LABELLED_HEADPHONE)
+                                       strscpy(ctl->id.name,
+                                               "Headphone Detected",
diff --git a/queue-5.15/audit-add-audit_log_nf_skb-helper-function.patch b/queue-5.15/audit-add-audit_log_nf_skb-helper-function.patch
new file mode 100644 (file)
index 0000000..c6fecd6
--- /dev/null
@@ -0,0 +1,296 @@
+From stable+bounces-278563-greg=kroah.com@vger.kernel.org Tue Jul 21 13:50:42 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 21 Jul 2026 07:47:02 -0400
+Subject: audit: add audit_log_nf_skb helper function
+To: stable@vger.kernel.org
+Cc: Ricardo Robaina <rrobaina@redhat.com>, Florian Westphal <fw@strlen.de>, Paul Moore <paul@paul-moore.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260721114703.3688208-1-sashal@kernel.org>
+
+From: Ricardo Robaina <rrobaina@redhat.com>
+
+[ Upstream commit f19590b07cb620be1fcd5474c49515e21a05d406 ]
+
+Netfilter code (net/netfilter/nft_log.c and net/netfilter/xt_AUDIT.c)
+have to be kept in sync. Both source files had duplicated versions of
+audit_ip4() and audit_ip6() functions, which can result in lack of
+consistency and/or duplicated work.
+
+This patch adds a helper function in audit.c that can be called by
+netfilter code commonly, aiming to improve maintainability and
+consistency.
+
+Suggested-by: Florian Westphal <fw@strlen.de>
+Suggested-by: Paul Moore <paul@paul-moore.com>
+Signed-off-by: Ricardo Robaina <rrobaina@redhat.com>
+Acked-by: Florian Westphal <fw@strlen.de>
+Signed-off-by: Paul Moore <paul@paul-moore.com>
+Stable-dep-of: 65dfde57d1e2 ("audit: fix potential integer overflow in audit_log_n_hex()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ include/linux/audit.h    |    8 +++++
+ kernel/audit.c           |   64 +++++++++++++++++++++++++++++++++++++++++++++++
+ net/netfilter/nft_log.c  |   58 ------------------------------------------
+ net/netfilter/xt_AUDIT.c |   58 ------------------------------------------
+ 4 files changed, 74 insertions(+), 114 deletions(-)
+
+--- a/include/linux/audit.h
++++ b/include/linux/audit.h
+@@ -186,6 +186,8 @@ extern void                    audit_log_lost(const ch
+ extern int audit_log_task_context(struct audit_buffer *ab);
+ extern void audit_log_task_info(struct audit_buffer *ab);
++extern int audit_log_nf_skb(struct audit_buffer *ab,
++                          const struct sk_buff *skb, u8 nfproto);
+ extern int                audit_update_lsm_rules(void);
+@@ -251,6 +253,12 @@ static inline int audit_log_task_context
+ static inline void audit_log_task_info(struct audit_buffer *ab)
+ { }
++static inline int audit_log_nf_skb(struct audit_buffer *ab,
++                                 const struct sk_buff *skb, u8 nfproto)
++{
++      return 0;
++}
++
+ static inline kuid_t audit_get_loginuid(struct task_struct *tsk)
+ {
+       return INVALID_UID;
+--- a/kernel/audit.c
++++ b/kernel/audit.c
+@@ -59,6 +59,8 @@
+ #include <linux/freezer.h>
+ #include <linux/pid_namespace.h>
+ #include <net/netns/generic.h>
++#include <net/ip.h>
++#include <net/ipv6.h>
+ #include "audit.h"
+@@ -2307,6 +2309,68 @@ void audit_log_path_denied(int type, con
+       audit_log_end(ab);
+ }
++int audit_log_nf_skb(struct audit_buffer *ab,
++                   const struct sk_buff *skb, u8 nfproto)
++{
++      /* find the IP protocol in the case of NFPROTO_BRIDGE */
++      if (nfproto == NFPROTO_BRIDGE) {
++              switch (eth_hdr(skb)->h_proto) {
++              case htons(ETH_P_IP):
++                      nfproto = NFPROTO_IPV4;
++                      break;
++              case htons(ETH_P_IPV6):
++                      nfproto = NFPROTO_IPV6;
++                      break;
++              default:
++                      goto unknown_proto;
++              }
++      }
++
++      switch (nfproto) {
++      case NFPROTO_IPV4: {
++              struct iphdr iph;
++              const struct iphdr *ih;
++
++              ih = skb_header_pointer(skb, skb_network_offset(skb),
++                                      sizeof(iph), &iph);
++              if (!ih)
++                      return -ENOMEM;
++
++              audit_log_format(ab, " saddr=%pI4 daddr=%pI4 proto=%hhu",
++                               &ih->saddr, &ih->daddr, ih->protocol);
++              break;
++      }
++      case NFPROTO_IPV6: {
++              struct ipv6hdr iph;
++              const struct ipv6hdr *ih;
++              u8 nexthdr;
++              __be16 frag_off;
++
++              ih = skb_header_pointer(skb, skb_network_offset(skb),
++                                      sizeof(iph), &iph);
++              if (!ih)
++                      return -ENOMEM;
++
++              nexthdr = ih->nexthdr;
++              ipv6_skip_exthdr(skb, skb_network_offset(skb) + sizeof(iph),
++                               &nexthdr, &frag_off);
++
++              audit_log_format(ab, " saddr=%pI6c daddr=%pI6c proto=%hhu",
++                               &ih->saddr, &ih->daddr, nexthdr);
++              break;
++      }
++      default:
++              goto unknown_proto;
++      }
++
++      return 0;
++
++unknown_proto:
++      audit_log_format(ab, " saddr=? daddr=? proto=?");
++      return -EPFNOSUPPORT;
++}
++EXPORT_SYMBOL(audit_log_nf_skb);
++
+ /* global counter which is incremented every time something logs in */
+ static atomic_t session_id = ATOMIC_INIT(0);
+--- a/net/netfilter/nft_log.c
++++ b/net/netfilter/nft_log.c
+@@ -26,46 +26,10 @@ struct nft_log {
+       char                    *prefix;
+ };
+-static bool audit_ip4(struct audit_buffer *ab, struct sk_buff *skb)
+-{
+-      struct iphdr _iph;
+-      const struct iphdr *ih;
+-
+-      ih = skb_header_pointer(skb, skb_network_offset(skb), sizeof(_iph), &_iph);
+-      if (!ih)
+-              return false;
+-
+-      audit_log_format(ab, " saddr=%pI4 daddr=%pI4 proto=%hhu",
+-                       &ih->saddr, &ih->daddr, ih->protocol);
+-
+-      return true;
+-}
+-
+-static bool audit_ip6(struct audit_buffer *ab, struct sk_buff *skb)
+-{
+-      struct ipv6hdr _ip6h;
+-      const struct ipv6hdr *ih;
+-      u8 nexthdr;
+-      __be16 frag_off;
+-
+-      ih = skb_header_pointer(skb, skb_network_offset(skb), sizeof(_ip6h), &_ip6h);
+-      if (!ih)
+-              return false;
+-
+-      nexthdr = ih->nexthdr;
+-      ipv6_skip_exthdr(skb, skb_network_offset(skb) + sizeof(_ip6h), &nexthdr, &frag_off);
+-
+-      audit_log_format(ab, " saddr=%pI6c daddr=%pI6c proto=%hhu",
+-                       &ih->saddr, &ih->daddr, nexthdr);
+-
+-      return true;
+-}
+-
+ static void nft_log_eval_audit(const struct nft_pktinfo *pkt)
+ {
+       struct sk_buff *skb = pkt->skb;
+       struct audit_buffer *ab;
+-      int fam = -1;
+       if (!audit_enabled)
+               return;
+@@ -76,27 +40,7 @@ static void nft_log_eval_audit(const str
+       audit_log_format(ab, "mark=%#x", skb->mark);
+-      switch (nft_pf(pkt)) {
+-      case NFPROTO_BRIDGE:
+-              switch (eth_hdr(skb)->h_proto) {
+-              case htons(ETH_P_IP):
+-                      fam = audit_ip4(ab, skb) ? NFPROTO_IPV4 : -1;
+-                      break;
+-              case htons(ETH_P_IPV6):
+-                      fam = audit_ip6(ab, skb) ? NFPROTO_IPV6 : -1;
+-                      break;
+-              }
+-              break;
+-      case NFPROTO_IPV4:
+-              fam = audit_ip4(ab, skb) ? NFPROTO_IPV4 : -1;
+-              break;
+-      case NFPROTO_IPV6:
+-              fam = audit_ip6(ab, skb) ? NFPROTO_IPV6 : -1;
+-              break;
+-      }
+-
+-      if (fam == -1)
+-              audit_log_format(ab, " saddr=? daddr=? proto=-1");
++      audit_log_nf_skb(ab, skb, nft_pf(pkt));
+       audit_log_end(ab);
+ }
+--- a/net/netfilter/xt_AUDIT.c
++++ b/net/netfilter/xt_AUDIT.c
+@@ -28,46 +28,10 @@ MODULE_ALIAS("ip6t_AUDIT");
+ MODULE_ALIAS("ebt_AUDIT");
+ MODULE_ALIAS("arpt_AUDIT");
+-static bool audit_ip4(struct audit_buffer *ab, struct sk_buff *skb)
+-{
+-      struct iphdr _iph;
+-      const struct iphdr *ih;
+-
+-      ih = skb_header_pointer(skb, skb_network_offset(skb), sizeof(_iph), &_iph);
+-      if (!ih)
+-              return false;
+-
+-      audit_log_format(ab, " saddr=%pI4 daddr=%pI4 proto=%hhu",
+-                       &ih->saddr, &ih->daddr, ih->protocol);
+-
+-      return true;
+-}
+-
+-static bool audit_ip6(struct audit_buffer *ab, struct sk_buff *skb)
+-{
+-      struct ipv6hdr _ip6h;
+-      const struct ipv6hdr *ih;
+-      u8 nexthdr;
+-      __be16 frag_off;
+-
+-      ih = skb_header_pointer(skb, skb_network_offset(skb), sizeof(_ip6h), &_ip6h);
+-      if (!ih)
+-              return false;
+-
+-      nexthdr = ih->nexthdr;
+-      ipv6_skip_exthdr(skb, skb_network_offset(skb) + sizeof(_ip6h), &nexthdr, &frag_off);
+-
+-      audit_log_format(ab, " saddr=%pI6c daddr=%pI6c proto=%hhu",
+-                       &ih->saddr, &ih->daddr, nexthdr);
+-
+-      return true;
+-}
+-
+ static unsigned int
+ audit_tg(struct sk_buff *skb, const struct xt_action_param *par)
+ {
+       struct audit_buffer *ab;
+-      int fam = -1;
+       if (audit_enabled == AUDIT_OFF)
+               goto errout;
+@@ -77,27 +41,7 @@ audit_tg(struct sk_buff *skb, const stru
+       audit_log_format(ab, "mark=%#x", skb->mark);
+-      switch (xt_family(par)) {
+-      case NFPROTO_BRIDGE:
+-              switch (eth_hdr(skb)->h_proto) {
+-              case htons(ETH_P_IP):
+-                      fam = audit_ip4(ab, skb) ? NFPROTO_IPV4 : -1;
+-                      break;
+-              case htons(ETH_P_IPV6):
+-                      fam = audit_ip6(ab, skb) ? NFPROTO_IPV6 : -1;
+-                      break;
+-              }
+-              break;
+-      case NFPROTO_IPV4:
+-              fam = audit_ip4(ab, skb) ? NFPROTO_IPV4 : -1;
+-              break;
+-      case NFPROTO_IPV6:
+-              fam = audit_ip6(ab, skb) ? NFPROTO_IPV6 : -1;
+-              break;
+-      }
+-
+-      if (fam == -1)
+-              audit_log_format(ab, " saddr=? daddr=? proto=-1");
++      audit_log_nf_skb(ab, skb, xt_family(par));
+       audit_log_end(ab);
diff --git a/queue-5.15/audit-fix-potential-integer-overflow-in-audit_log_n_hex.patch b/queue-5.15/audit-fix-potential-integer-overflow-in-audit_log_n_hex.patch
new file mode 100644 (file)
index 0000000..b5b6487
--- /dev/null
@@ -0,0 +1,66 @@
+From stable+bounces-278564-greg=kroah.com@vger.kernel.org Tue Jul 21 13:50:47 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 21 Jul 2026 07:47:03 -0400
+Subject: audit: fix potential integer overflow in audit_log_n_hex()
+To: stable@vger.kernel.org
+Cc: Ricardo Robaina <rrobaina@redhat.com>, Richard Guy Briggs <rgb@redhat.com>, Paul Moore <paul@paul-moore.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260721114703.3688208-2-sashal@kernel.org>
+
+From: Ricardo Robaina <rrobaina@redhat.com>
+
+[ Upstream commit 65dfde57d1e29ce2b76fc23dd565eccd5c0bc0f0 ]
+
+The function calculates new_len as len << 1 for hex encoding. This
+has two overflow risks: the shift itself can overflow when len is
+large, and the result can be truncated when assigned to new_len
+(declared as int) from the size_t calculation.
+
+Fix by using check_shl_overflow() to catch shift overflow and
+changing new_len and loop counter i to size_t to prevent truncation.
+
+Cc: stable@vger.kernel.org
+Fixes: 168b7173959f ("AUDIT: Clean up logging of untrusted strings")
+Reviewed-by: Richard Guy Briggs <rgb@redhat.com>
+Signed-off-by: Ricardo Robaina <rrobaina@redhat.com>
+[PM: remove vertical whitspace noise]
+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.c |   11 +++++++++--
+ 1 file changed, 9 insertions(+), 2 deletions(-)
+
+--- a/kernel/audit.c
++++ b/kernel/audit.c
+@@ -61,6 +61,7 @@
+ #include <net/netns/generic.h>
+ #include <net/ip.h>
+ #include <net/ipv6.h>
++#include <linux/overflow.h>
+ #include "audit.h"
+@@ -2037,7 +2038,8 @@ void audit_log_format(struct audit_buffe
+ void audit_log_n_hex(struct audit_buffer *ab, const unsigned char *buf,
+               size_t len)
+ {
+-      int i, avail, new_len;
++      int avail;
++      size_t i, new_len;
+       unsigned char *ptr;
+       struct sk_buff *skb;
+@@ -2047,7 +2049,12 @@ void audit_log_n_hex(struct audit_buffer
+       BUG_ON(!ab->skb);
+       skb = ab->skb;
+       avail = skb_tailroom(skb);
+-      new_len = len<<1;
++
++      if (check_shl_overflow(len, 1, &new_len)) {
++              audit_log_format(ab, "?");
++              return;
++      }
++
+       if (new_len >= avail) {
+               /* Round the buffer request up to the next multiple */
+               new_len = AUDIT_BUFSIZ*(((new_len-avail)/AUDIT_BUFSIZ) + 1);
diff --git a/queue-5.15/bitops-make-bytes_to_bits-treewide-available.patch b/queue-5.15/bitops-make-bytes_to_bits-treewide-available.patch
new file mode 100644 (file)
index 0000000..2f1d217
--- /dev/null
@@ -0,0 +1,90 @@
+From stable+bounces-274079-greg=kroah.com@vger.kernel.org Tue Jul 14 04:10:00 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 13 Jul 2026 22:09:07 -0400
+Subject: bitops: make BYTES_TO_BITS() treewide-available
+To: stable@vger.kernel.org
+Cc: Alexander Lobakin <aleksander.lobakin@intel.com>, Andy Shevchenko <andriy.shevchenko@linux.intel.com>, Przemek Kitszel <przemyslaw.kitszel@intel.com>, Yury Norov <yury.norov@gmail.com>, "David S. Miller" <davem@davemloft.net>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260714020908.2355876-1-sashal@kernel.org>
+
+From: Alexander Lobakin <aleksander.lobakin@intel.com>
+
+[ Upstream commit 7d8296b250f2eed73f1758607926d4d258dea5d4 ]
+
+Avoid open-coding that simple expression each time by moving
+BYTES_TO_BITS() from the probes code to <linux/bitops.h> to export
+it to the rest of the kernel.
+Simplify the macro while at it. `BITS_PER_LONG / sizeof(long)` always
+equals to %BITS_PER_BYTE, regardless of the target architecture.
+Do the same for the tools ecosystem as well (incl. its version of
+bitops.h). The previous implementation had its implicit type of long,
+while the new one is int, so adjust the format literal accordingly in
+the perf code.
+
+Suggested-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
+Reviewed-by: Przemek Kitszel <przemyslaw.kitszel@intel.com>
+Acked-by: Yury Norov <yury.norov@gmail.com>
+Signed-off-by: Alexander Lobakin <aleksander.lobakin@intel.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Stable-dep-of: 55052184ac90 ("iio: common: st_sensors: honour channel endianness in read_axis_data")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ include/linux/bitops.h         |    2 ++
+ kernel/trace/trace_probe.c     |    2 --
+ tools/include/linux/bitops.h   |    2 ++
+ tools/perf/util/probe-finder.c |    4 +---
+ 4 files changed, 5 insertions(+), 5 deletions(-)
+
+--- a/include/linux/bitops.h
++++ b/include/linux/bitops.h
+@@ -21,6 +21,8 @@
+ #define BITS_TO_U32(nr)               __KERNEL_DIV_ROUND_UP(nr, BITS_PER_TYPE(u32))
+ #define BITS_TO_BYTES(nr)     __KERNEL_DIV_ROUND_UP(nr, BITS_PER_TYPE(char))
++#define BYTES_TO_BITS(nb)     ((nb) * BITS_PER_BYTE)
++
+ extern unsigned int __sw_hweight8(unsigned int w);
+ extern unsigned int __sw_hweight16(unsigned int w);
+ extern unsigned int __sw_hweight32(unsigned int w);
+--- a/kernel/trace/trace_probe.c
++++ b/kernel/trace/trace_probe.c
+@@ -531,8 +531,6 @@ parse_probe_arg(char *arg, const struct
+       return ret;
+ }
+-#define BYTES_TO_BITS(nb)     ((BITS_PER_LONG * (nb)) / sizeof(long))
+-
+ /* Bitfield type needs to be parsed into a fetch function */
+ static int __parse_bitfield_probe_arg(const char *bf,
+                                     const struct fetch_type *t,
+--- a/tools/include/linux/bitops.h
++++ b/tools/include/linux/bitops.h
+@@ -20,6 +20,8 @@
+ #define BITS_TO_U32(nr)               DIV_ROUND_UP(nr, BITS_PER_TYPE(u32))
+ #define BITS_TO_BYTES(nr)     DIV_ROUND_UP(nr, BITS_PER_TYPE(char))
++#define BYTES_TO_BITS(nb)     ((nb) * BITS_PER_BYTE)
++
+ extern unsigned int __sw_hweight8(unsigned int w);
+ extern unsigned int __sw_hweight16(unsigned int w);
+ extern unsigned int __sw_hweight32(unsigned int w);
+--- a/tools/perf/util/probe-finder.c
++++ b/tools/perf/util/probe-finder.c
+@@ -304,8 +304,6 @@ static_var:
+       return ret2;
+ }
+-#define BYTES_TO_BITS(nb)     ((nb) * BITS_PER_LONG / sizeof(long))
+-
+ static int convert_variable_type(Dwarf_Die *vr_die,
+                                struct probe_trace_arg *tvar,
+                                const char *cast, bool user_access)
+@@ -335,7 +333,7 @@ static int convert_variable_type(Dwarf_D
+               total = dwarf_bytesize(vr_die);
+               if (boffs < 0 || total < 0)
+                       return -ENOENT;
+-              ret = snprintf(buf, 16, "b%d@%d/%zd", bsize, boffs,
++              ret = snprintf(buf, 16, "b%d@%d/%d", bsize, boffs,
+                               BYTES_TO_BITS(total));
+               goto formatted;
+       }
diff --git a/queue-5.15/bluetooth-l2cap-cancel-pending_rx_work-before-taking-conn-lock.patch b/queue-5.15/bluetooth-l2cap-cancel-pending_rx_work-before-taking-conn-lock.patch
new file mode 100644 (file)
index 0000000..58b7818
--- /dev/null
@@ -0,0 +1,67 @@
+From stable+bounces-275103-greg=kroah.com@vger.kernel.org Thu Jul 16 02:04:20 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 15 Jul 2026 20:04:13 -0400
+Subject: Bluetooth: L2CAP: cancel pending_rx_work before taking conn->lock
+To: stable@vger.kernel.org
+Cc: Runyu Xiao <runyu.xiao@seu.edu.cn>, Luiz Augusto von Dentz <luiz.von.dentz@intel.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260716000413.561625-3-sashal@kernel.org>
+
+From: Runyu Xiao <runyu.xiao@seu.edu.cn>
+
+[ Upstream commit 2641a9e0a1dd4af2e21995470a21d55dd35e5203 ]
+
+l2cap_conn_del() takes conn->lock and then calls cancel_work_sync() for
+pending_rx_work.  process_pending_rx() takes the same mutex, so teardown
+can deadlock against the worker it is flushing.
+
+This issue was found by our static analysis tool and then manually
+reviewed against the current tree.
+
+The grounded PoC kept the l2cap_conn_ready() -> queue_work(...,
+&conn->pending_rx_work) submit path, the l2cap_conn_del() ->
+cancel_work_sync(&conn->pending_rx_work) teardown path, and the
+process_pending_rx() -> mutex_lock(&conn->lock) worker edge.  Lockdep
+reported:
+
+  WARNING: possible circular locking dependency detected
+  process_pending_rx+0x21/0x2a [vuln_msv]
+  l2cap_conn_del.constprop.0+0x3f/0x4e [vuln_msv]
+  *** DEADLOCK ***
+
+Cancel pending_rx_work before taking conn->lock, matching the existing
+lock-before-drain ordering used for the two delayed works in the same
+teardown path.  The pending_rx queue is still purged after the work has
+been cancelled and conn->lock has been acquired.
+
+Fixes: 7ab56c3a6ecc ("Bluetooth: Fix deadlock in l2cap_conn_del()")
+Cc: stable@vger.kernel.org
+Signed-off-by: Runyu Xiao <runyu.xiao@seu.edu.cn>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/bluetooth/l2cap_core.c |   10 ++--------
+ 1 file changed, 2 insertions(+), 8 deletions(-)
+
+--- a/net/bluetooth/l2cap_core.c
++++ b/net/bluetooth/l2cap_core.c
+@@ -1899,17 +1899,11 @@ static void l2cap_conn_del(struct hci_co
+       cancel_delayed_work_sync(&conn->info_timer);
+       cancel_work_sync(&conn->id_addr_update_work);
++      cancel_work_sync(&conn->pending_rx_work);
++
+       kfree_skb(conn->rx_skb);
+       skb_queue_purge(&conn->pending_rx);
+-
+-      /* We can not call flush_work(&conn->pending_rx_work) here since we
+-       * might block if we are running on a worker from the same workqueue
+-       * pending_rx_work is waiting on.
+-       */
+-      if (work_pending(&conn->pending_rx_work))
+-              cancel_work_sync(&conn->pending_rx_work);
+-
+       ida_destroy(&conn->tx_ida);
+       l2cap_unregister_all_users(conn);
diff --git a/queue-5.15/bluetooth-l2cap-fix-deadlock-in-l2cap_conn_del.patch b/queue-5.15/bluetooth-l2cap-fix-deadlock-in-l2cap_conn_del.patch
new file mode 100644 (file)
index 0000000..57c317e
--- /dev/null
@@ -0,0 +1,65 @@
+From stable+bounces-275102-greg=kroah.com@vger.kernel.org Thu Jul 16 02:04:22 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 15 Jul 2026 20:04:12 -0400
+Subject: Bluetooth: L2CAP: Fix deadlock in l2cap_conn_del()
+To: stable@vger.kernel.org
+Cc: Hyunwoo Kim <imv4bel@gmail.com>, Luiz Augusto von Dentz <luiz.von.dentz@intel.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260716000413.561625-2-sashal@kernel.org>
+
+From: Hyunwoo Kim <imv4bel@gmail.com>
+
+[ Upstream commit 00fdebbbc557a2fc21321ff2eaa22fd70c078608 ]
+
+l2cap_conn_del() calls cancel_delayed_work_sync() for both info_timer
+and id_addr_timer while holding conn->lock. However, the work functions
+l2cap_info_timeout() and l2cap_conn_update_id_addr() both acquire
+conn->lock, creating a potential AB-BA deadlock if the work is already
+executing when l2cap_conn_del() takes the lock.
+
+Move the work cancellations before acquiring conn->lock and use
+disable_delayed_work_sync() to additionally prevent the works from
+being rearmed after cancellation, consistent with the pattern used in
+hci_conn_del().
+
+Fixes: ab4eedb790ca ("Bluetooth: L2CAP: Fix corrupted list in hci_chan_del")
+Signed-off-by: Hyunwoo Kim <imv4bel@gmail.com>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Stable-dep-of: 2641a9e0a1dd ("Bluetooth: L2CAP: cancel pending_rx_work before taking conn->lock")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/bluetooth/l2cap_core.c |    9 +++------
+ 1 file changed, 3 insertions(+), 6 deletions(-)
+
+--- a/net/bluetooth/l2cap_core.c
++++ b/net/bluetooth/l2cap_core.c
+@@ -1896,6 +1896,9 @@ static void l2cap_conn_del(struct hci_co
+       BT_DBG("hcon %p conn %p, err %d", hcon, conn, err);
++      cancel_delayed_work_sync(&conn->info_timer);
++      cancel_work_sync(&conn->id_addr_update_work);
++
+       kfree_skb(conn->rx_skb);
+       skb_queue_purge(&conn->pending_rx);
+@@ -1909,9 +1912,6 @@ static void l2cap_conn_del(struct hci_co
+       ida_destroy(&conn->tx_ida);
+-      if (work_pending(&conn->id_addr_update_work))
+-              cancel_work_sync(&conn->id_addr_update_work);
+-
+       l2cap_unregister_all_users(conn);
+       /* Force the connection to be immediately dropped */
+@@ -1936,9 +1936,6 @@ static void l2cap_conn_del(struct hci_co
+       hci_chan_del(conn->hchan);
+-      if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT)
+-              cancel_delayed_work_sync(&conn->info_timer);
+-
+       hcon->l2cap_data = NULL;
+       conn->hchan = NULL;
+       l2cap_conn_put(conn);
diff --git a/queue-5.15/bluetooth-l2cap-fix-not-tracking-outstanding-tx-ident.patch b/queue-5.15/bluetooth-l2cap-fix-not-tracking-outstanding-tx-ident.patch
new file mode 100644 (file)
index 0000000..b764245
--- /dev/null
@@ -0,0 +1,154 @@
+From stable+bounces-275101-greg=kroah.com@vger.kernel.org Thu Jul 16 02:04:19 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 15 Jul 2026 20:04:11 -0400
+Subject: Bluetooth: L2CAP: Fix not tracking outstanding TX ident
+To: stable@vger.kernel.org
+Cc: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>, Paul Menzel <pmenzel@molgen.mpg.de>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260716000413.561625-1-sashal@kernel.org>
+
+From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+
+[ Upstream commit 6c3ea155e5ee3e56606233acde8309afda66d483 ]
+
+This attempts to proper track outstanding request by using struct ida
+and allocating from it in l2cap_get_ident using ida_alloc_range which
+would reuse ids as they are free, then upon completion release
+the id using ida_free.
+
+This fixes the qualification test case L2CAP/COS/CED/BI-29-C which
+attempts to check if the host stack is able to work after 256 attempts
+to connect which requires Ident field to use the full range of possible
+values in order to pass the test.
+
+Link: https://github.com/bluez/bluez/issues/1829
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Reviewed-by: Paul Menzel <pmenzel@molgen.mpg.de>
+Stable-dep-of: 2641a9e0a1dd ("Bluetooth: L2CAP: cancel pending_rx_work before taking conn->lock")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ include/net/bluetooth/l2cap.h |    3 --
+ net/bluetooth/l2cap_core.c    |   53 ++++++++++++++++++++++++++++++------------
+ 2 files changed, 40 insertions(+), 16 deletions(-)
+
+--- a/include/net/bluetooth/l2cap.h
++++ b/include/net/bluetooth/l2cap.h
+@@ -691,8 +691,7 @@ struct l2cap_conn {
+       struct sk_buff          *rx_skb;
+       __u32                   rx_len;
+-      __u8                    tx_ident;
+-      struct mutex            ident_lock;
++      struct ida              tx_ida;
+       struct sk_buff_head     pending_rx;
+       struct work_struct      pending_rx_work;
+--- a/net/bluetooth/l2cap_core.c
++++ b/net/bluetooth/l2cap_core.c
+@@ -937,26 +937,18 @@ int l2cap_chan_check_security(struct l2c
+                                initiator);
+ }
+-static u8 l2cap_get_ident(struct l2cap_conn *conn)
++static int l2cap_get_ident(struct l2cap_conn *conn)
+ {
+-      u8 id;
++      /* LE link does not support tools like l2ping so use the full range */
++      if (conn->hcon->type == LE_LINK)
++              return ida_alloc_range(&conn->tx_ida, 1, 255, GFP_ATOMIC);
+       /* Get next available identificator.
+        *    1 - 128 are used by kernel.
+        *  129 - 199 are reserved.
+        *  200 - 254 are used by utilities like l2ping, etc.
+        */
+-
+-      mutex_lock(&conn->ident_lock);
+-
+-      if (++conn->tx_ident > 128)
+-              conn->tx_ident = 1;
+-
+-      id = conn->tx_ident;
+-
+-      mutex_unlock(&conn->ident_lock);
+-
+-      return id;
++      return ida_alloc_range(&conn->tx_ida, 1, 128, GFP_ATOMIC);
+ }
+ static void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len,
+@@ -1915,6 +1907,8 @@ static void l2cap_conn_del(struct hci_co
+       if (work_pending(&conn->pending_rx_work))
+               cancel_work_sync(&conn->pending_rx_work);
++      ida_destroy(&conn->tx_ida);
++
+       if (work_pending(&conn->id_addr_update_work))
+               cancel_work_sync(&conn->id_addr_update_work);
+@@ -5778,12 +5772,41 @@ unlock:
+       return err;
+ }
++static void l2cap_put_ident(struct l2cap_conn *conn, u8 code, u8 id)
++{
++      int ret;
++
++      switch (code) {
++      case L2CAP_COMMAND_REJ:
++      case L2CAP_CONN_RSP:
++      case L2CAP_CONF_RSP:
++      case L2CAP_DISCONN_RSP:
++      case L2CAP_ECHO_RSP:
++      case L2CAP_INFO_RSP:
++      case L2CAP_CONN_PARAM_UPDATE_RSP:
++      case L2CAP_ECRED_CONN_RSP:
++      case L2CAP_ECRED_RECONF_RSP:
++              /* The remote may send bogus ids that would make ida_free
++               * generate warnings, so only free ids that are actually
++               * allocated: probing the exact id returns -ENOSPC when it
++               * is in use, otherwise the probe allocated it and freeing
++               * is safe either way.  Only on -ENOMEM is the id known to
++               * be unallocated and the free must be skipped.
++               */
++              ret = ida_alloc_range(&conn->tx_ida, id, id, GFP_ATOMIC);
++              if (ret >= 0 || ret == -ENOSPC)
++                      ida_free(&conn->tx_ida, id);
++      }
++}
++
+ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn,
+                                     struct l2cap_cmd_hdr *cmd, u16 cmd_len,
+                                     u8 *data)
+ {
+       int err = 0;
++      l2cap_put_ident(conn, cmd->code, cmd->ident);
++
+       switch (cmd->code) {
+       case L2CAP_COMMAND_REJ:
+               l2cap_command_rej(conn, cmd, cmd_len, data);
+@@ -6522,6 +6545,8 @@ static inline int l2cap_le_sig_cmd(struc
+ {
+       int err = 0;
++      l2cap_put_ident(conn, cmd->code, cmd->ident);
++
+       switch (cmd->code) {
+       case L2CAP_COMMAND_REJ:
+               l2cap_le_command_rej(conn, cmd, cmd_len, data);
+@@ -8075,13 +8100,13 @@ static struct l2cap_conn *l2cap_conn_add
+            hci_dev_test_flag(hcon->hdev, HCI_FORCE_BREDR_SMP)))
+               conn->local_fixed_chan |= L2CAP_FC_SMP_BREDR;
+-      mutex_init(&conn->ident_lock);
+       mutex_init(&conn->chan_lock);
+       INIT_LIST_HEAD(&conn->chan_l);
+       INIT_LIST_HEAD(&conn->users);
+       INIT_DELAYED_WORK(&conn->info_timer, l2cap_info_timeout);
++      ida_init(&conn->tx_ida);
+       skb_queue_head_init(&conn->pending_rx);
+       INIT_WORK(&conn->pending_rx_work, process_pending_rx);
diff --git a/queue-5.15/bpf-allow-lpm-map-access-from-sleepable-bpf-programs.patch b/queue-5.15/bpf-allow-lpm-map-access-from-sleepable-bpf-programs.patch
new file mode 100644 (file)
index 0000000..d15be05
--- /dev/null
@@ -0,0 +1,118 @@
+From stable+bounces-277389-greg=kroah.com@vger.kernel.org Sat Jul 18 23:51:57 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 18 Jul 2026 17:50:21 -0400
+Subject: bpf: Allow LPM map access from sleepable BPF programs
+To: stable@vger.kernel.org
+Cc: Vlad Poenaru <vlad.wing@gmail.com>, Emil Tsalapatis <emil@etsalapatis.com>, Alexei Starovoitov <ast@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260718215021.3447372-3-sashal@kernel.org>
+
+From: Vlad Poenaru <vlad.wing@gmail.com>
+
+[ Upstream commit 2f884d371fafea137afea504d49ee4a7c8d7985b ]
+
+trie_lookup_elem() annotates its rcu_dereference_check() walks with
+only rcu_read_lock_bh_held().  Because rcu_dereference_check(p, c)
+resolves to "c || rcu_read_lock_held()", this passes for XDP/NAPI and
+classic RCU readers but fails for sleepable BPF programs, which enter
+via __bpf_prog_enter_sleepable() and hold only rcu_read_lock_trace().
+
+trie_update_elem() and trie_delete_elem() have the same problem in a
+different form: they walk the trie with plain rcu_dereference(), which
+asserts rcu_read_lock_held() unconditionally.  Both are reachable from
+sleepable BPF programs via the bpf_map_update_elem / bpf_map_delete_elem
+helpers, and from the syscall path under classic rcu_read_lock().  In
+the writer paths the trie is actually protected by trie->lock (an
+rqspinlock taken across the walk); we never relied on the RCU read-side
+lock to keep nodes alive there.
+
+A sleepable LSM hook that ends up touching an LPM trie therefore
+triggers lockdep on debug kernels:
+
+  =============================
+  WARNING: suspicious RCU usage
+  7.1.0-... Tainted: G            E
+  -----------------------------
+  kernel/bpf/lpm_trie.c:249 suspicious rcu_dereference_check() usage!
+  1 lock held by net_tests/540:
+   #0: (rcu_tasks_trace_srcu_struct){....}-{0:0},
+       at: __bpf_prog_enter_sleepable+0x26/0x280
+  Call Trace:
+   dump_stack_lvl
+   lockdep_rcu_suspicious
+   trie_lookup_elem
+   bpf_prog_..._enforce_security_socket_connect
+   bpf_trampoline_...
+   security_socket_connect
+   __sys_connect
+   do_syscall_64
+
+This is lockdep-only -- no UAF, since Tasks Trace RCU does serialize
+against the trie's reclaim path -- but it spams the console once per
+distinct callsite on every debug kernel running a sleepable BPF LSM
+that touches an LPM trie, which is increasingly common.
+
+For the lookup path, switch the rcu_dereference_check() annotation
+from rcu_read_lock_bh_held() to bpf_rcu_lock_held(), which accepts all
+three contexts (classic, BH, Tasks Trace).  Other map types already
+follow this convention.
+
+For trie_update_elem() and trie_delete_elem(), annotate the walks as
+rcu_dereference_protected(*p, 1) -- matching trie_free() in the same
+file -- since trie->lock is held across the walk.  rqspinlock has no
+lockdep_map, so the predicate degenerates to '1' rather than
+lockdep_is_held(&trie->lock); the protection is real but not
+machine-verifiable.  trie_get_next_key() also uses bare
+rcu_dereference() but is reachable only from the BPF syscall, which
+holds classic rcu_read_lock() before dispatching, so it is left
+untouched.
+
+Fixes: 694cea395fde ("bpf: Allow RCU-protected lookups to happen from bh context")
+Cc: stable@vger.kernel.org
+Signed-off-by: Vlad Poenaru <vlad.wing@gmail.com>
+Reviewed-by: Emil Tsalapatis <emil@etsalapatis.com>
+Link: https://lore.kernel.org/r/20260609135558.193287-2-vlad.wing@gmail.com
+Signed-off-by: Alexei Starovoitov <ast@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ kernel/bpf/lpm_trie.c |    8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+--- a/kernel/bpf/lpm_trie.c
++++ b/kernel/bpf/lpm_trie.c
+@@ -235,7 +235,7 @@ static void *trie_lookup_elem(struct bpf
+       /* Start walking the trie from the root node ... */
+-      for (node = rcu_dereference_check(trie->root, rcu_read_lock_bh_held());
++      for (node = rcu_dereference_check(trie->root, bpf_rcu_lock_held());
+            node;) {
+               unsigned int next_bit;
+               size_t matchlen;
+@@ -269,7 +269,7 @@ static void *trie_lookup_elem(struct bpf
+                */
+               next_bit = extract_bit(key->data, node->prefixlen);
+               node = rcu_dereference_check(node->child[next_bit],
+-                                           rcu_read_lock_bh_held());
++                                           bpf_rcu_lock_held());
+       }
+       if (!found)
+@@ -349,7 +349,7 @@ static int trie_update_elem(struct bpf_m
+        */
+       slot = &trie->root;
+-      while ((node = rcu_dereference(*slot))) {
++      while ((node = rcu_dereference_protected(*slot, 1))) {
+               matchlen = longest_prefix_match(trie, node, key);
+               if (node->prefixlen != matchlen ||
+@@ -474,7 +474,7 @@ static int trie_delete_elem(struct bpf_m
+       trim = &trie->root;
+       trim2 = trim;
+       parent = NULL;
+-      while ((node = rcu_dereference(*trim))) {
++      while ((node = rcu_dereference_protected(*trim, 1))) {
+               matchlen = longest_prefix_match(trie, node, key);
+               if (node->prefixlen != matchlen ||
diff --git a/queue-5.15/bpf-consistently-use-bpf_rcu_lock_held-everywhere.patch b/queue-5.15/bpf-consistently-use-bpf_rcu_lock_held-everywhere.patch
new file mode 100644 (file)
index 0000000..6b16544
--- /dev/null
@@ -0,0 +1,153 @@
+From stable+bounces-277388-greg=kroah.com@vger.kernel.org Sat Jul 18 23:51:52 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 18 Jul 2026 17:50:20 -0400
+Subject: bpf: Consistently use bpf_rcu_lock_held() everywhere
+To: stable@vger.kernel.org
+Cc: Andrii Nakryiko <andrii@kernel.org>, Daniel Borkmann <daniel@iogearbox.net>, Jiri Olsa <jolsa@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260718215021.3447372-2-sashal@kernel.org>
+
+From: Andrii Nakryiko <andrii@kernel.org>
+
+[ Upstream commit 48a97ffc6c826640907d13b199e29008f4fe2c15 ]
+
+We have many places which open-code what's now is bpf_rcu_lock_held()
+macro, so replace all those places with a clean and short macro invocation.
+For that, move bpf_rcu_lock_held() macro into include/linux/bpf.h.
+
+Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
+Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
+Acked-by: Jiri Olsa <jolsa@kernel.org>
+Link: https://lore.kernel.org/bpf/20251014201403.4104511-1-andrii@kernel.org
+Stable-dep-of: 2f884d371faf ("bpf: Allow LPM map access from sleepable BPF programs")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ include/linux/bpf.h  |    4 ++++
+ kernel/bpf/hashtab.c |   21 +++++++--------------
+ kernel/bpf/helpers.c |    9 +++------
+ 3 files changed, 14 insertions(+), 20 deletions(-)
+
+--- a/include/linux/bpf.h
++++ b/include/linux/bpf.h
+@@ -23,6 +23,7 @@
+ #include <linux/slab.h>
+ #include <linux/percpu-refcount.h>
+ #include <linux/bpfptr.h>
++#include <linux/rcupdate_trace.h>
+ struct bpf_verifier_env;
+ struct bpf_verifier_log;
+@@ -1482,6 +1483,9 @@ out:
+               _ret;                                   \
+       })
++#define bpf_rcu_lock_held() \
++      (rcu_read_lock_held() || rcu_read_lock_trace_held() || rcu_read_lock_bh_held())
++
+ #ifdef CONFIG_BPF_SYSCALL
+ DECLARE_PER_CPU(int, bpf_prog_active);
+ extern struct mutex bpf_stats_enabled_mutex;
+--- a/kernel/bpf/hashtab.c
++++ b/kernel/bpf/hashtab.c
+@@ -642,8 +642,7 @@ static void *__htab_map_lookup_elem(stru
+       struct htab_elem *l;
+       u32 hash, key_size;
+-      WARN_ON_ONCE(!rcu_read_lock_held() && !rcu_read_lock_trace_held() &&
+-                   !rcu_read_lock_bh_held());
++      WARN_ON_ONCE(!bpf_rcu_lock_held());
+       key_size = map->key_size;
+@@ -1047,8 +1046,7 @@ static int htab_map_update_elem(struct b
+               /* unknown flags */
+               return -EINVAL;
+-      WARN_ON_ONCE(!rcu_read_lock_held() && !rcu_read_lock_trace_held() &&
+-                   !rcu_read_lock_bh_held());
++      WARN_ON_ONCE(!bpf_rcu_lock_held());
+       key_size = map->key_size;
+@@ -1149,8 +1147,7 @@ static int htab_lru_map_update_elem(stru
+               /* unknown flags */
+               return -EINVAL;
+-      WARN_ON_ONCE(!rcu_read_lock_held() && !rcu_read_lock_trace_held() &&
+-                   !rcu_read_lock_bh_held());
++      WARN_ON_ONCE(!bpf_rcu_lock_held());
+       key_size = map->key_size;
+@@ -1218,8 +1215,7 @@ static int __htab_percpu_map_update_elem
+               /* unknown flags */
+               return -EINVAL;
+-      WARN_ON_ONCE(!rcu_read_lock_held() && !rcu_read_lock_trace_held() &&
+-                   !rcu_read_lock_bh_held());
++      WARN_ON_ONCE(!bpf_rcu_lock_held());
+       key_size = map->key_size;
+@@ -1273,8 +1269,7 @@ static int __htab_lru_percpu_map_update_
+               /* unknown flags */
+               return -EINVAL;
+-      WARN_ON_ONCE(!rcu_read_lock_held() && !rcu_read_lock_trace_held() &&
+-                   !rcu_read_lock_bh_held());
++      WARN_ON_ONCE(!bpf_rcu_lock_held());
+       key_size = map->key_size;
+@@ -1349,8 +1344,7 @@ static int htab_map_delete_elem(struct b
+       u32 hash, key_size;
+       int ret;
+-      WARN_ON_ONCE(!rcu_read_lock_held() && !rcu_read_lock_trace_held() &&
+-                   !rcu_read_lock_bh_held());
++      WARN_ON_ONCE(!bpf_rcu_lock_held());
+       key_size = map->key_size;
+@@ -1385,8 +1379,7 @@ static int htab_lru_map_delete_elem(stru
+       u32 hash, key_size;
+       int ret;
+-      WARN_ON_ONCE(!rcu_read_lock_held() && !rcu_read_lock_trace_held() &&
+-                   !rcu_read_lock_bh_held());
++      WARN_ON_ONCE(!bpf_rcu_lock_held());
+       key_size = map->key_size;
+--- a/kernel/bpf/helpers.c
++++ b/kernel/bpf/helpers.c
+@@ -30,8 +30,7 @@
+  */
+ BPF_CALL_2(bpf_map_lookup_elem, struct bpf_map *, map, void *, key)
+ {
+-      WARN_ON_ONCE(!rcu_read_lock_held() && !rcu_read_lock_trace_held() &&
+-                   !rcu_read_lock_bh_held());
++      WARN_ON_ONCE(!bpf_rcu_lock_held());
+       return (unsigned long) map->ops->map_lookup_elem(map, key);
+ }
+@@ -47,8 +46,7 @@ const struct bpf_func_proto bpf_map_look
+ BPF_CALL_4(bpf_map_update_elem, struct bpf_map *, map, void *, key,
+          void *, value, u64, flags)
+ {
+-      WARN_ON_ONCE(!rcu_read_lock_held() && !rcu_read_lock_trace_held() &&
+-                   !rcu_read_lock_bh_held());
++      WARN_ON_ONCE(!bpf_rcu_lock_held());
+       return map->ops->map_update_elem(map, key, value, flags);
+ }
+@@ -65,8 +63,7 @@ const struct bpf_func_proto bpf_map_upda
+ BPF_CALL_2(bpf_map_delete_elem, struct bpf_map *, map, void *, key)
+ {
+-      WARN_ON_ONCE(!rcu_read_lock_held() && !rcu_read_lock_trace_held() &&
+-                   !rcu_read_lock_bh_held());
++      WARN_ON_ONCE(!bpf_rcu_lock_held());
+       return map->ops->map_delete_elem(map, key);
+ }
diff --git a/queue-5.15/bpf-convert-lpm_trie.c-to-rqspinlock.patch b/queue-5.15/bpf-convert-lpm_trie.c-to-rqspinlock.patch
new file mode 100644 (file)
index 0000000..2f54e94
--- /dev/null
@@ -0,0 +1,54 @@
+From stable+bounces-277387-greg=kroah.com@vger.kernel.org Sat Jul 18 23:51:13 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 18 Jul 2026 17:50:19 -0400
+Subject: bpf: Convert lpm_trie.c to rqspinlock
+To: stable@vger.kernel.org
+Cc: Kumar Kartikeya Dwivedi <memxor@gmail.com>, Alexei Starovoitov <ast@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260718215021.3447372-1-sashal@kernel.org>
+
+From: Kumar Kartikeya Dwivedi <memxor@gmail.com>
+
+[ Upstream commit 47979314c0fe245ed54306e2f91b3f819c7c0f9f ]
+
+Convert all LPM trie usage of raw_spinlock to rqspinlock.
+
+Note that rcu_dereference_protected in trie_delete_elem is switched over
+to plain rcu_dereference, the RCU read lock should be held from BPF
+program side or eBPF syscall path, and the trie->lock is just acquired
+before the dereference. It is not clear the reason the protected variant
+was used from the commit history, but the above reasoning makes sense so
+switch over.
+
+Closes: https://lore.kernel.org/lkml/000000000000adb08b061413919e@google.com
+Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
+Link: https://lore.kernel.org/r/20250316040541.108729-22-memxor@gmail.com
+Signed-off-by: Alexei Starovoitov <ast@kernel.org>
+Stable-dep-of: 2f884d371faf ("bpf: Allow LPM map access from sleepable BPF programs")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ kernel/bpf/lpm_trie.c |    6 ++----
+ 1 file changed, 2 insertions(+), 4 deletions(-)
+
+--- a/kernel/bpf/lpm_trie.c
++++ b/kernel/bpf/lpm_trie.c
+@@ -349,8 +349,7 @@ static int trie_update_elem(struct bpf_m
+        */
+       slot = &trie->root;
+-      while ((node = rcu_dereference_protected(*slot,
+-                                      lockdep_is_held(&trie->lock)))) {
++      while ((node = rcu_dereference(*slot))) {
+               matchlen = longest_prefix_match(trie, node, key);
+               if (node->prefixlen != matchlen ||
+@@ -475,8 +474,7 @@ static int trie_delete_elem(struct bpf_m
+       trim = &trie->root;
+       trim2 = trim;
+       parent = NULL;
+-      while ((node = rcu_dereference_protected(
+-                     *trim, lockdep_is_held(&trie->lock)))) {
++      while ((node = rcu_dereference(*trim))) {
+               matchlen = longest_prefix_match(trie, node, key);
+               if (node->prefixlen != matchlen ||
diff --git a/queue-5.15/bpf-reject-fragmented-frames-in-devmap.patch b/queue-5.15/bpf-reject-fragmented-frames-in-devmap.patch
new file mode 100644 (file)
index 0000000..41efad9
--- /dev/null
@@ -0,0 +1,70 @@
+From stable+bounces-277354-greg=kroah.com@vger.kernel.org Sat Jul 18 18:31:20 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 18 Jul 2026 12:31:12 -0400
+Subject: bpf: Reject fragmented frames in devmap
+To: stable@vger.kernel.org
+Cc: "Zhao Zhang" <zzhan461@ucr.edu>, stable@kernel.org, "Yuan Tan" <yuantan098@gmail.com>, "Zhengchuan Liang" <zcliangcn@gmail.com>, "Xin Liu" <bird@lzu.edu.cn>, "Ren Wei" <n05ec@lzu.edu.cn>, "Emil Tsalapatis" <emil@etsalapatis.com>, "Toke Høiland-Jørgensen" <toke@redhat.com>, "Alexei Starovoitov" <ast@kernel.org>, "Sasha Levin" <sashal@kernel.org>
+Message-ID: <20260718163112.3339166-2-sashal@kernel.org>
+
+From: Zhao Zhang <zzhan461@ucr.edu>
+
+[ Upstream commit aa496720618f1a6054f1c870bf10b4f6c99bf656 ]
+
+Devmap broadcast redirects clone the packet for all but the last
+destination.
+
+For native XDP, that clone path copies only the linear xdp_frame data,
+while fragmented frames keep skb_shared_info in tailroom outside the
+linear area. Cloning such a frame leaves XDP_FLAGS_HAS_FRAGS set but
+without valid frag metadata, and the later free path can interpret
+uninitialized tail data as skb_shared_info, leading to an out-of-bounds
+access during frame return.
+
+Reject fragmented native XDP frames in dev_map_enqueue_clone().
+
+Add the same restriction to the generic XDP clone path in
+dev_map_redirect_clone(). Generic XDP represents fragmented packets as
+nonlinear skbs, and rejecting them here keeps clone-based broadcast
+support aligned between native and generic XDP.
+
+Fixes: e624d4ed4aa8 ("xdp: Extend xdp_redirect_map with broadcast support")
+Cc: stable@kernel.org
+Reported-by: Yuan Tan <yuantan098@gmail.com>
+Reported-by: Zhengchuan Liang <zcliangcn@gmail.com>
+Reported-by: Xin Liu <bird@lzu.edu.cn>
+Assisted-by: Codex:GPT-5.4
+Signed-off-by: Zhao Zhang <zzhan461@ucr.edu>
+Signed-off-by: Ren Wei <n05ec@lzu.edu.cn>
+Reviewed-by: Emil Tsalapatis <emil@etsalapatis.com>
+Reviewed-by: Toke Høiland-Jørgensen <toke@redhat.com>
+Link: https://lore.kernel.org/r/21c2d153dd25603d359069a02bf06779b51f6423.1780385378.git.zzhan461@ucr.edu
+Signed-off-by: Alexei Starovoitov <ast@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ kernel/bpf/devmap.c |    7 +++++++
+ 1 file changed, 7 insertions(+)
+
+--- a/kernel/bpf/devmap.c
++++ b/kernel/bpf/devmap.c
+@@ -551,6 +551,10 @@ static int dev_map_enqueue_clone(struct
+ {
+       struct xdp_frame *nxdpf;
++      /* Frags live outside the linear frame and cannot be cloned safely. */
++      if (unlikely(xdp_frame_has_frags(xdpf)))
++              return -EOPNOTSUPP;
++
+       nxdpf = xdpf_clone(xdpf);
+       if (!nxdpf)
+               return -ENOMEM;
+@@ -708,6 +712,9 @@ static int dev_map_redirect_clone(struct
+       struct sk_buff *nskb;
+       int err;
++      if (unlikely(skb_is_nonlinear(skb)))
++              return -EOPNOTSUPP;
++
+       nskb = skb_clone(skb, GFP_ATOMIC);
+       if (!nskb)
+               return -ENOMEM;
diff --git a/queue-5.15/btrfs-do-not-trim-a-device-which-is-not-writeable.patch b/queue-5.15/btrfs-do-not-trim-a-device-which-is-not-writeable.patch
new file mode 100644 (file)
index 0000000..b86fa7c
--- /dev/null
@@ -0,0 +1,144 @@
+From stable+bounces-278188-greg=kroah.com@vger.kernel.org Mon Jul 20 20:30:15 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Jul 2026 14:30:07 -0400
+Subject: btrfs: do not trim a device which is not writeable
+To: stable@vger.kernel.org
+Cc: Qu Wenruo <wqu@suse.com>, Su Yue <glass.su@suse.com>, Filipe Manana <fdmanana@suse.com>, David Sterba <dsterba@suse.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260720183007.3057972-1-sashal@kernel.org>
+
+From: Qu Wenruo <wqu@suse.com>
+
+[ Upstream commit 1b1937eb08f51319bf71575484cde2b8c517aedc ]
+
+[BUG]
+There is a bug report that btrfs/242 can randomly fail with the
+following NULL pointer dereference:
+
+  run fstests btrfs/242 at 2026-06-01 10:25:08
+  BTRFS: device fsid d4d7f234-487c-4787-88e4-47a8b68c9874 devid 1 transid 9 /dev/sdc (8:32) scanned by mount (122609)
+  BTRFS info (device sdc): first mount of filesystem d4d7f234-487c-4787-88e4-47a8b68c9874
+  BTRFS info (device sdc): using crc32c checksum algorithm
+  BTRFS warning (device sdc): devid 2 uuid fbe72d72-3272-482d-80fb-ab88ed398192 is missing
+  BTRFS warning (device sdc): devid 2 uuid fbe72d72-3272-482d-80fb-ab88ed398192 is missing
+  BTRFS info (device sdc): allowing degraded mounts
+  BTRFS info (device sdc): turning on async discard
+  BTRFS info (device sdc): enabling free space tree
+  Unable to handle kernel NULL pointer dereference at virtual address 0000000000000018
+  user pgtable: 4k pages, 48-bit VAs, pgdp=000000013fd6b000
+  CPU: 4 UID: 0 PID: 122625 Comm: fstrim Not tainted 7.0.10-2-default #1 PREEMPT(full) openSUSE Tumbleweed e9a5f6b24978fba3bf015a992f865837fdfff3dd
+  Hardware name: QEMU KVM Virtual Machine, BIOS edk2-20250812-19.fc42 08/12/2025
+  pstate: 01400005 (nzcv daif +PAN -UAO -TCO +DIT -SSBS BTYPE=--)
+  pc : btrfs_trim_fs+0x34c/0xa00 [btrfs]
+  lr : btrfs_trim_fs+0x1f0/0xa00 [btrfs]
+  Call trace:
+   btrfs_trim_fs+0x34c/0xa00 [btrfs f02c1d570ceea621c69d302ba75dd61868083840] (P)
+   btrfs_ioctl_fitrim+0xe8/0x178 [btrfs f02c1d570ceea621c69d302ba75dd61868083840]
+   btrfs_ioctl+0xdd4/0x2bd8 [btrfs f02c1d570ceea621c69d302ba75dd61868083840]
+   __arm64_sys_ioctl+0xac/0x108
+   invoke_syscall.constprop.0+0x5c/0xd0
+   el0_svc_common.constprop.0+0x40/0xf0
+   do_el0_svc+0x24/0x40
+   el0_svc+0x40/0x1d0
+   el0t_64_sync_handler+0xa0/0xe8
+   el0t_64_sync+0x1b0/0x1b8
+  Code: 17ffff83 f94017e0 f9002be0 f9402ea0 (f9400c00)
+  ---[ end trace 0000000000000000  ]---
+
+Also the reporter is very kind to test the following ASSERT() added to
+btrfs_trim_free_extents_throttle():
+
+       ASSERT(device->bdev,
+              "devid=%llu path=%s dev_state=0x%lx\n",
+              device->devid, btrfs_dev_name(device), device->dev_state);
+
+And it shows the following output:
+
+  assertion failed: device->bdev, in extent-tree.c:6630 (devid=2 path=/dev/sdd dev_state=0x82)
+
+Which means the device->bdev is NULL, and the dev_state is
+BTRFS_DEV_STATE_IN_FS_METADATA | BTRFS_DEV_STATE_ITEM_FOUND, without
+BTRFS_DEV_STATE_WRITEABLE flag set.
+
+[CAUSE]
+The pc points to the following call chain:
+
+  btrfs_trim_fs()
+  |- btrfs_trim_free_extents()
+     |- btrfs_trim_free_extents_throttle()
+        |- bdev_max_discard_sectors(device->bdev)
+
+So the NULL pointer dereference is caused by device->bdev being NULL.
+
+This looks impossible by a quick glance, as just before calling
+btrfs_trim_free_extents_throttle(), we have skipped any device that has
+BTRFS_DEV_STATE_MISSING flag set.
+
+However in this particular case, there is a window where the missing
+device is later re-scanned, causing btrfs to remove the
+BTRFS_DEV_STATE_MISSING flag:
+
+  btrfs_control_ioctl()
+  |- btrfs_scan_one_device()
+     |- device_list_add()
+        |- rcu_assign_pointer(device->name, name);
+        |  This updates the missing device's path to the new good path.
+        |
+        |- clear_bit(BTRFS_DEV_STATE_MISSING, &device->dev_state)
+           This removes the BTRFS_DEV_STATE_MISSING flag.
+
+This allows the missing device to re-appear and clear the
+BTRFS_DEV_STATE_MISSING flag.  However the device still does not have
+the BTRFS_DEV_STATE_WRITEABLE flag set, nor is its bdev pointer updated.
+
+The bdev pointer remains NULL, triggering the crash later.
+
+[FIX]
+This is a big de-synchronization between BTRFS_DEV_STATE_MISSING and
+device->bdev pointer, and shows a gap in btrfs's re-appearing-device
+handling.
+
+The proper handling of re-appearing device will need quite some extra
+work, which is out of the context of this small fix.
+
+Thankfully the regular bbio submission path has already handled it well
+by checking if the device->bdev is NULL before submitting.
+
+So here we just fix the crash by checking if the device is writeable and
+has a bdev pointer before calling bdev_max_discard_sectors().
+
+Reported-by: Su Yue <glass.su@suse.com>
+Link: https://lore.kernel.org/linux-btrfs/wlwir19t.fsf@damenly.org/
+Fixes: 499f377f49f0 ("btrfs: iterate over unused chunk space in FITRIM")
+CC: stable@vger.kernel.org # 5.10+
+Reviewed-by: Filipe Manana <fdmanana@suse.com>
+Signed-off-by: Qu Wenruo <wqu@suse.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/btrfs/extent-tree.c |   12 ++++++++----
+ 1 file changed, 8 insertions(+), 4 deletions(-)
+
+--- a/fs/btrfs/extent-tree.c
++++ b/fs/btrfs/extent-tree.c
+@@ -5994,12 +5994,16 @@ static int btrfs_trim_free_extents(struc
+       *trimmed = 0;
+-      /* Discard not supported = nothing to do. */
+-      if (!blk_queue_discard(bdev_get_queue(device->bdev)))
++      /*
++       * The caller only filters out MISSING devices, but a device that was
++       * missing at mount and later rescanned has MISSING cleared while bdev
++       * is still NULL and WRITEABLE is still unset. Skip those here.
++       */
++      if (!test_bit(BTRFS_DEV_STATE_WRITEABLE, &device->dev_state) || !device->bdev)
+               return 0;
+-      /* Not writable = nothing to do. */
+-      if (!test_bit(BTRFS_DEV_STATE_WRITEABLE, &device->dev_state))
++      /* Discard not supported = nothing to do. */
++      if (!blk_queue_discard(bdev_get_queue(device->bdev)))
+               return 0;
+       /* No free space = nothing to do. */
diff --git a/queue-5.15/btrfs-fix-false-io-failure-after-falling-back-to-buffered-write.patch b/queue-5.15/btrfs-fix-false-io-failure-after-falling-back-to-buffered-write.patch
new file mode 100644 (file)
index 0000000..cd50e7f
--- /dev/null
@@ -0,0 +1,198 @@
+From stable+bounces-278222-greg=kroah.com@vger.kernel.org Mon Jul 20 22:19:49 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Jul 2026 16:19:28 -0400
+Subject: btrfs: fix false IO failure after falling back to buffered write
+To: stable@vger.kernel.org
+Cc: Qu Wenruo <wqu@suse.com>, Boris Burkov <boris@bur.io>, David Sterba <dsterba@suse.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260720201928.3242739-1-sashal@kernel.org>
+
+From: Qu Wenruo <wqu@suse.com>
+
+[ Upstream commit 66ff4d366e7eb4d31813d2acabf3af512ce03aa5 ]
+
+[BUG]
+The test case generic/362 will fail with "nodatasum" mount option (*):
+
+ MOUNT_OPTIONS -- -o nodatasum /dev/mapper/test-scratch1 /mnt/scratch
+
+# generic/362  0s ... - output mismatch (see /home/adam/xfstests/results//generic/362.out.bad)
+#    --- tests/generic/362.out 2024-08-24 15:31:37.200000000 +0930
+#    +++ /home/adam/xfstests/results//generic/362.out.bad      2026-05-27 10:21:17.574771567 +0930
+#    @@ -1,2 +1,3 @@
+#     QA output created by 362
+#    +First write failed: Input/output error
+#     Silence is golden
+#    ...
+
+*: If the test case has been executed before with default data checksum,
+the failure will not reproduce. Need the following fix to make it
+reliably reproducible:
+https://lore.kernel.org/linux-btrfs/20260528111659.87113-1-wqu@suse.com/
+
+[CAUSE]
+Inside __iomap_dio_rw(), the -EFAULT/-ENOTBLK error is not directly returned.
+Thus we never got an error pointer from __iomap_dio_rw().
+
+The call chain looks like this:
+
+ btrfs_direct_write()
+ |- btrfs_dio_write()
+ |-  __iomap_dio_rw()
+ |  |- iomap_iter()
+ |  |  |- btrfs_dio_iomap_begin()
+ |  |     Now an ordered extent is allocated for the 4K write.
+ |  |
+ |  |- iomi.status = iomap_dio_iter()
+ |  |  Where iomap_dio_iter() returned -EFAULT.
+ |  |
+ |  |- ret = iomap_iter()
+ |  |  |- btrfs_dio_iomap_end()
+ |  |  |  |- btrfs_finish_ordered_extent(uptodate = false)
+ |  |  |  |  |- can_finish_ordered_extent()
+ |  |  |  |     |- btrfs_mark_ordered_extent_error()
+ |  |  |  |        |- mapping_set_error()
+ |  |  |  |           Now the address space is marked error.
+ |  |  |  | return -ENOTBLK
+ |  |  |- return -ENOTBLK
+ |  |- if (ret == -ENOTBLK) { ret = 0; }
+ |     Now the return value is reset to 0.
+ |     Thus no error pointer will be returned.
+ |
+ |- ret = iomap_dio_complete()
+ |  Since no byte is submitted, @ret is 0.
+ |
+ |- Fallback to buffered IO
+ |  And the buffered write finished without error
+ |
+ |- filemap_fdatawait_range()
+    |- filemap_check_errors()
+       The previous error is recorded, thus an error is returned
+
+However the buffered write is properly submitted and finished, the error
+is from the btrfs_finish_ordered_extent() call with @uptodate = false.
+
+[FIX]
+When a short dio write happened, any range that is submitted will have
+btrfs_extract_ordered_extent() to be called, thus the submitted range
+will always have an OE just covering the submitted range.
+
+The remaining OE range is never submitted, thus they should be treated
+as truncated, not an error. So that we can properly reclaim and not
+insert an unnecessary file extent item, without marking the mapping as
+error.
+
+Extract a helper, btrfs_mark_ordered_extent_truncated(), and utilize
+that helper to mark the direct IO ordered extent as truncated, so it
+won't cause failure for the later buffered fallback.
+
+[REASON FOR NO FIXES TAG]
+The bug itself is pretty old, at commit f85781fb505e ("btrfs: switch to
+iomap for direct IO") we're already passing @uptodate=false finishing
+the OE.
+But at that time OE with IOERR won't call mapping_set_error(), so it's
+not exposed.
+Later commit d61bec08b904 ("btrfs: mark ordered extent and inode with
+error if we fail to finish") finally exposed the bug, but that commit
+is doing a correct job, not the root cause.
+
+Anyway the bug is very old, dating back to 5.1x days, thus only CC to
+stable.
+
+CC: stable@vger.kernel.org # 5.15+
+Reviewed-by: Boris Burkov <boris@bur.io>
+Signed-off-by: Qu Wenruo <wqu@suse.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/btrfs/inode.c        |   34 ++++++++++++++++++++++++++--------
+ fs/btrfs/ordered-data.c |   13 +++++++++++++
+ fs/btrfs/ordered-data.h |    2 ++
+ 3 files changed, 41 insertions(+), 8 deletions(-)
+
+--- a/fs/btrfs/inode.c
++++ b/fs/btrfs/inode.c
+@@ -8207,12 +8207,34 @@ static int btrfs_dio_iomap_end(struct in
+       if (submitted < length) {
+               pos += submitted;
+               length -= submitted;
+-              if (write)
++              if (write) {
++                      struct btrfs_ordered_extent *ordered;
++
++                      /*
++                       * We have a short write. Any range that was properly
++                       * submitted will be finished through the bio
++                       * completion path with an uptodate status matching
++                       * the bio status.
++                       *
++                       * The remaining range was never submitted, so mark
++                       * it as truncated and finish it without an error, so
++                       * the extent is reclaimed without inserting a file
++                       * extent item for it and without marking the mapping
++                       * as failed, which would make a later buffered write
++                       * fallback fail with a false -EIO.
++                       */
++                      ordered = btrfs_lookup_ordered_extent(BTRFS_I(inode),
++                                                            pos);
++                      ASSERT(ordered);
++                      btrfs_mark_ordered_extent_truncated(ordered,
++                                      pos - ordered->file_offset);
++                      btrfs_put_ordered_extent(ordered);
+                       __endio_write_update_ordered(BTRFS_I(inode), pos,
+-                                      length, false);
+-              else
++                                      length, true);
++              } else {
+                       unlock_extent(&BTRFS_I(inode)->io_tree, pos,
+                                     pos + length - 1);
++              }
+               ret = -ENOTBLK;
+       }
+@@ -8839,11 +8861,7 @@ static void btrfs_invalidatepage(struct
+                                        EXTENT_LOCKED | EXTENT_DO_ACCOUNTING |
+                                        EXTENT_DEFRAG, 1, 0, &cached_state);
+-              spin_lock_irq(&inode->ordered_tree.lock);
+-              set_bit(BTRFS_ORDERED_TRUNCATED, &ordered->flags);
+-              ordered->truncated_len = min(ordered->truncated_len,
+-                                           cur - ordered->file_offset);
+-              spin_unlock_irq(&inode->ordered_tree.lock);
++              btrfs_mark_ordered_extent_truncated(ordered, cur - ordered->file_offset);
+               if (btrfs_dec_test_ordered_pending(inode, &ordered,
+                                                  cur, range_end + 1 - cur)) {
+--- a/fs/btrfs/ordered-data.c
++++ b/fs/btrfs/ordered-data.c
+@@ -298,6 +298,19 @@ void btrfs_add_ordered_sum(struct btrfs_
+       spin_unlock_irq(&tree->lock);
+ }
++void btrfs_mark_ordered_extent_truncated(struct btrfs_ordered_extent *ordered,
++                                       u64 truncate_len)
++{
++      struct btrfs_ordered_inode_tree *tree;
++
++      tree = &BTRFS_I(ordered->inode)->ordered_tree;
++      ASSERT(truncate_len <= ordered->num_bytes);
++      spin_lock_irq(&tree->lock);
++      set_bit(BTRFS_ORDERED_TRUNCATED, &ordered->flags);
++      ordered->truncated_len = min(ordered->truncated_len, truncate_len);
++      spin_unlock_irq(&tree->lock);
++}
++
+ /*
+  * Mark all ordered extents io inside the specified range finished.
+  *
+--- a/fs/btrfs/ordered-data.h
++++ b/fs/btrfs/ordered-data.h
+@@ -212,6 +212,8 @@ void btrfs_lock_and_flush_ordered_range(
+                                       struct extent_state **cached_state);
+ int btrfs_split_ordered_extent(struct btrfs_ordered_extent *ordered, u64 pre,
+                              u64 post);
++void btrfs_mark_ordered_extent_truncated(struct btrfs_ordered_extent *ordered,
++                                       u64 truncate_len);
+ int __init ordered_data_init(void);
+ void __cold ordered_data_exit(void);
diff --git a/queue-5.15/btrfs-fix-incorrect-buffered-io-fallback-for-append-direct-writes.patch b/queue-5.15/btrfs-fix-incorrect-buffered-io-fallback-for-append-direct-writes.patch
new file mode 100644 (file)
index 0000000..213860d
--- /dev/null
@@ -0,0 +1,197 @@
+From stable+bounces-278288-greg=kroah.com@vger.kernel.org Tue Jul 21 02:46:48 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Jul 2026 20:45:24 -0400
+Subject: btrfs: fix incorrect buffered IO fallback for append direct writes
+To: stable@vger.kernel.org
+Cc: Qu Wenruo <wqu@suse.com>, Boris Burkov <boris@bur.io>, David Sterba <dsterba@suse.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260721004524.3422340-2-sashal@kernel.org>
+
+From: Qu Wenruo <wqu@suse.com>
+
+[ Upstream commit ff66fe6662330226b3f486014c375538d91c44aa ]
+
+[BUG]
+With the previous bug of short direct writes fixed, test case
+generic/362 (*) still fails with the following error with nodatasum
+mount option:
+
+# generic/362  0s ... - output mismatch (see /home/adam/xfstests/results//generic/362.out.bad)
+# - output mismatch (see /home/adam/xfstests/results//generic/362.out.bad)
+#    --- tests/generic/362.out 2024-08-24 15:31:37.200000000 +0930
+#    +++ /home/adam/xfstests/results//generic/362.out.bad      2026-05-27 10:13:09.072485767 +0930
+#    @@ -1,2 +1,3 @@
+#     QA output created by 362
+#    +Wrong file size after first write, got 8192 expected 4096
+#     Silence is golden
+#    ...
+
+*: If the test case has been executed before with default data checksum,
+the failure will not reproduce. Need the following fix to make it
+reliably reproducible:
+https://lore.kernel.org/linux-btrfs/20260528111659.87113-1-wqu@suse.com/
+
+[CAUSE]
+Inside btrfs_dio_iomap_begin() for a direct write, we increase the isize
+if it's beyond the current isize.
+
+But if the direct io finished short, we do not revert the isize to the
+previous value nor to the short write end.
+
+Then if we need to fall back to buffered writes, and the write has
+IOCB_APPEND flag, then the buffered write will be positioned at the
+incorrect isize.
+
+The call chain looks like this:
+
+ btrfs_direct_write(pos=0, length=4K)
+ |- __iomap_dio_rw()
+ |  |- iomap_iter()
+ |  |  |- btrfs_dio_iomap_begin()
+ |  |     |- btrfs_get_blocks_direct_write()
+ |  |        |- i_size_write()
+ |  |           Which updates the isize to the write end (4K).
+ |  |
+ |  |- iomap_dio_iter()
+ |  |  Failed with -EFAULT on the first page.
+ |  |
+ |  |- iomap_iter()
+ |  |  |- btrfs_dio_iomap_end()
+ |  |     Detects a short write, return -ENOTBLK
+ |  |- if (ret == -ENOTBLK) { ret = 0;}
+ |     Which resets the return value.
+ |
+ |- ret = iomap_dio_complet()
+ |  Which returns 0.
+ |
+ |- btrfs_buffered_write(iocb, from);
+    |- generic_write_checks()
+       |- iocb->ki_pos = i_size_read()
+          Which is still the new size (4K), other than the original
+         isize 0.
+
+[FIX]
+Introduce the following btrfs_dio_data members:
+
+- old_isize
+
+- updated_isize
+  If the direct write has enlarged the isize.
+
+Then if we got a short write, and btrfs_dio_data::updated_isize is set,
+revert to the correct isize based on old_isize and current file
+position.
+
+And here we call i_size_write() without holding an extent lock, which is
+a very special case that we're safe to do:
+
+ - Only a single writer can be enlarging isize
+   Enlarging isize will take the exclusive inode lock.
+
+ - Buffered readers need to wait for the OE we're holding
+   Buffered readers will lock extent and wait for OE of the folio range.
+   Sometimes we can skip the OE wait, but since all page cache is
+   invalidated, the OE wait can not be skipped.
+
+But I do not think this is the most elegant solution, nor covers all
+cases. E.g. if the bio is submitted but IO failed, we are unable to do
+the revert.
+
+I believe the more elegant one would be extend the EXTENT_DIO_LOCKED
+lifespan for direct writes, so that we can update the isize when a
+write beyond EOF finished successfully.
+
+However that change is too huge for a small bug fix.
+So only implement the minimal partial fix for now.
+
+[REASON FOR NO FIXES TAG]
+The bug is again very old, before commit f85781fb505e ("btrfs: switch to
+iomap for direct IO") we are already increasing isize without a
+proper rollback for short writes.
+
+Thus only a CC to stable.
+
+CC: stable@vger.kernel.org # 5.15+
+Reviewed-by: Boris Burkov <boris@bur.io>
+Signed-off-by: Qu Wenruo <wqu@suse.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/btrfs/inode.c |   43 ++++++++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 42 insertions(+), 1 deletion(-)
+
+--- a/fs/btrfs/inode.c
++++ b/fs/btrfs/inode.c
+@@ -61,7 +61,9 @@ struct btrfs_iget_args {
+ struct btrfs_dio_data {
+       ssize_t submitted;
++      loff_t old_isize;
+       struct extent_changeset *data_reserved;
++      bool updated_isize;
+ };
+ static const struct inode_operations btrfs_dir_inode_operations;
+@@ -7904,6 +7906,7 @@ static int btrfs_get_blocks_direct_write
+       bool can_nocow = false;
+       bool space_reserved = false;
+       u64 prev_len;
++      loff_t old_isize;
+       int ret = 0;
+       /*
+@@ -7997,8 +8000,14 @@ static int btrfs_get_blocks_direct_write
+        * Need to update the i_size under the extent lock so buffered
+        * readers will get the updated i_size when we unlock.
+        */
+-      if (start + len > i_size_read(inode))
++      old_isize = i_size_read(inode);
++      if (start + len > old_isize) {
++              if (!dio_data->updated_isize) {
++                      dio_data->old_isize = old_isize;
++                      dio_data->updated_isize = true;
++              }
+               i_size_write(inode, start + len);
++      }
+ out:
+       if (ret && space_reserved) {
+               btrfs_delalloc_release_extents(BTRFS_I(inode), len);
+@@ -8211,6 +8220,38 @@ static int btrfs_dio_iomap_end(struct in
+                       struct btrfs_ordered_extent *ordered;
+                       /*
++                       * Got a short write and have updated the isize, need to
++                       * revert the isize change.
++                       *
++                       * Normally we need to update isize with extent lock hold,
++                       * but we're safe due to the following factors:
++                       *
++                       * - Only a single writer can be enlarging isize
++                       *   Enlarging isize will take the exclusive inode lock.
++                       *
++                       * - Buffered readers need to wait for the OE we're holding
++                       *   Buffered readers will lock extent and wait for OE
++                       *   of the folio range, and since page cache is invalidated
++                       *   the OE wait can not be skipped.
++                       *
++                       * So here we are safe to revert the isize before
++                       * finishing the OE, and no reader of the remaining range
++                       * can see the enlarged size.
++                       *
++                       * TODO: Extend the DIO_LOCKED lifespan for direct writes,
++                       * and only enlarge isize after a successful write.
++                       */
++                      if (dio_data->updated_isize) {
++                              u64 new_isize;
++
++                              if (submitted == 0)
++                                      new_isize = dio_data->old_isize;
++                              else
++                                      new_isize = max(dio_data->old_isize, pos);
++                              i_size_write(inode, new_isize);
++                              dio_data->updated_isize = false;
++                      }
++                      /*
+                        * We have a short write. Any range that was properly
+                        * submitted will be finished through the bio
+                        * completion path with an uptodate status matching
diff --git a/queue-5.15/cifs-add-tracing-for-the-cifs_tcon-struct-refcounting.patch b/queue-5.15/cifs-add-tracing-for-the-cifs_tcon-struct-refcounting.patch
new file mode 100644 (file)
index 0000000..2459277
--- /dev/null
@@ -0,0 +1,510 @@
+From stable+bounces-276751-greg=kroah.com@vger.kernel.org Thu Jul 16 18:52:01 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 16 Jul 2026 11:36:43 -0400
+Subject: cifs: Add tracing for the cifs_tcon struct refcounting
+To: stable@vger.kernel.org
+Cc: David Howells <dhowells@redhat.com>, "Paulo Alcantara (Red Hat)" <pc@manguebit.com>, Shyam Prasad N <nspmangalore@gmail.com>, linux-cifs@vger.kernel.org, linux-fsdevel@vger.kernel.org, Steve French <stfrench@microsoft.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260716153644.582614-1-sashal@kernel.org>
+
+From: David Howells <dhowells@redhat.com>
+
+[ Upstream commit afc23febd51c7e24361e3a9c09f3e892eb0a41ea ]
+
+Add tracing for the refcounting/lifecycle of the cifs_tcon struct, marking
+different events with different labels and giving each tcon its own debug
+ID so that the tracelines corresponding to individual tcons can be
+distinguished.  This can be enabled with:
+
+       echo 1 >/sys/kernel/debug/tracing/events/cifs/smb3_tcon_ref/enable
+
+Signed-off-by: David Howells <dhowells@redhat.com>
+Acked-by: Paulo Alcantara (Red Hat) <pc@manguebit.com>
+cc: Shyam Prasad N <nspmangalore@gmail.com>
+cc: linux-cifs@vger.kernel.org
+cc: linux-fsdevel@vger.kernel.org
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Stable-dep-of: ec457f9afe5a ("smb: client: resolve SWN tcon from live registrations")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/cifs/cifs_swn.c      |   13 +++---
+ fs/cifs/cifsfs.c        |    2 +
+ fs/cifs/cifsglob.h      |    1 
+ fs/cifs/cifsproto.h     |    9 +---
+ fs/cifs/connect.c       |   21 ++++++----
+ fs/cifs/dfs_cache.c     |    2 -
+ fs/cifs/misc.c          |   10 +++--
+ fs/cifs/smb2misc.c      |   10 +++--
+ fs/cifs/smb2ops.c       |    7 +++
+ fs/cifs/smb2pdu.c       |    4 +-
+ fs/cifs/smb2transport.c |    2 +
+ fs/cifs/trace.h         |   93 ++++++++++++++++++++++++++++++++++++++++++++++--
+ 12 files changed, 141 insertions(+), 33 deletions(-)
+
+--- a/fs/cifs/cifs_swn.c
++++ b/fs/cifs/cifs_swn.c
+@@ -313,17 +313,15 @@ static struct cifs_swn_reg *cifs_get_swn
+       reg = cifs_find_swn_reg(tcon);
+       if (!IS_ERR(reg)) {
+               kref_get(&reg->ref_count);
+-              mutex_unlock(&cifs_swnreg_idr_mutex);
+-              return reg;
++              goto unlock;
+       } else if (PTR_ERR(reg) != -EEXIST) {
+-              mutex_unlock(&cifs_swnreg_idr_mutex);
+-              return reg;
++              goto unlock;
+       }
+       reg = kmalloc(sizeof(struct cifs_swn_reg), GFP_ATOMIC);
+       if (reg == NULL) {
+-              mutex_unlock(&cifs_swnreg_idr_mutex);
+-              return ERR_PTR(-ENOMEM);
++              ret = -ENOMEM;
++              goto fail_unlock;
+       }
+       kref_init(&reg->ref_count);
+@@ -354,7 +352,7 @@ static struct cifs_swn_reg *cifs_get_swn
+       reg->ip_notify = (tcon->capabilities & SMB2_SHARE_CAP_SCALEOUT);
+       reg->tcon = tcon;
+-
++unlock:
+       mutex_unlock(&cifs_swnreg_idr_mutex);
+       return reg;
+@@ -365,6 +363,7 @@ fail_idr:
+       idr_remove(&cifs_swnreg_idr, reg->id);
+ fail:
+       kfree(reg);
++fail_unlock:
+       mutex_unlock(&cifs_swnreg_idr_mutex);
+       return ERR_PTR(ret);
+ }
+--- a/fs/cifs/cifsfs.c
++++ b/fs/cifs/cifsfs.c
+@@ -705,6 +705,8 @@ static void cifs_umount_begin(struct sup
+       tcon = cifs_sb_master_tcon(cifs_sb);
+       spin_lock(&cifs_tcp_ses_lock);
++      trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count,
++                          netfs_trace_tcon_ref_see_umount);
+       if ((tcon->tc_count > 1) || (tcon->tidStatus == CifsExiting)) {
+               /* we have other mounts to same share or we have
+                  already tried to force umount this and woken up
+--- a/fs/cifs/cifsglob.h
++++ b/fs/cifs/cifsglob.h
+@@ -1025,6 +1025,7 @@ struct cached_fid {
+  */
+ struct cifs_tcon {
+       struct list_head tcon_list;
++      int debug_id;           /* Debugging for tracing */
+       int tc_count;
+       struct list_head rlist; /* reconnect list */
+       atomic_t num_local_opens;  /* num of all opens including disconnected */
+--- a/fs/cifs/cifsproto.h
++++ b/fs/cifs/cifsproto.h
+@@ -273,7 +273,7 @@ extern void cifs_close_deferred_file_und
+ extern struct TCP_Server_Info *cifs_get_tcp_session(struct smb3_fs_context *ctx);
+ extern void cifs_put_tcp_session(struct TCP_Server_Info *server,
+                                int from_reconnect);
+-extern void cifs_put_tcon(struct cifs_tcon *tcon);
++extern void cifs_put_tcon(struct cifs_tcon *tcon, enum smb3_tcon_ref_trace trace);
+ #if IS_ENABLED(CONFIG_CIFS_DFS_UPCALL)
+ extern void cifs_dfs_release_automount_timer(void);
+@@ -489,8 +489,8 @@ extern int CIFSSMBLogoff(const unsigned
+ extern struct cifs_ses *sesInfoAlloc(void);
+ extern void sesInfoFree(struct cifs_ses *);
+-extern struct cifs_tcon *tconInfoAlloc(void);
+-extern void tconInfoFree(struct cifs_tcon *);
++extern struct cifs_tcon *tconInfoAlloc(enum smb3_tcon_ref_trace trace);
++extern void tconInfoFree(struct cifs_tcon *tcon, enum smb3_tcon_ref_trace trace);
+ extern int cifs_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server,
+                  __u32 *pexpected_response_sequence_number);
+@@ -634,9 +634,6 @@ static inline int cifs_create_options(st
+               return options;
+ }
+-struct super_block *cifs_get_tcon_super(struct cifs_tcon *tcon);
+-void cifs_put_tcon_super(struct super_block *sb);
+-
+ static inline void cifs_mid_q_entry_release(struct mid_q_entry *midEntry)
+ {
+       kref_put(&midEntry->refcount, _cifs_mid_q_entry_release);
+--- a/fs/cifs/connect.c
++++ b/fs/cifs/connect.c
+@@ -1663,7 +1663,7 @@ cifs_setup_ipc(struct cifs_ses *ses, str
+               }
+       }
+-      tcon = tconInfoAlloc();
++      tcon = tconInfoAlloc(netfs_trace_tcon_ref_new_ipc);
+       if (tcon == NULL)
+               return -ENOMEM;
+@@ -1678,7 +1678,7 @@ cifs_setup_ipc(struct cifs_ses *ses, str
+       if (rc) {
+               cifs_server_dbg(VFS, "failed to connect to IPC (rc=%d)\n", rc);
+-              tconInfoFree(tcon);
++              tconInfoFree(tcon, netfs_trace_tcon_ref_free_ipc_fail);
+               goto out;
+       }
+@@ -1709,7 +1709,7 @@ cifs_free_ipc(struct cifs_ses *ses)
+       if (tcon == NULL)
+               return 0;
+-      tconInfoFree(tcon);
++      tconInfoFree(tcon, netfs_trace_tcon_ref_free_ipc);
+       ses->tcon_ipc = NULL;
+       return 0;
+ }
+@@ -2114,6 +2114,8 @@ cifs_find_tcon(struct cifs_ses *ses, str
+               if (!match_tcon(tcon, ctx))
+                       continue;
+               ++tcon->tc_count;
++              trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count,
++                                  netfs_trace_tcon_ref_get_find);
+               spin_unlock(&cifs_tcp_ses_lock);
+               return tcon;
+       }
+@@ -2122,7 +2124,7 @@ cifs_find_tcon(struct cifs_ses *ses, str
+ }
+ void
+-cifs_put_tcon(struct cifs_tcon *tcon)
++cifs_put_tcon(struct cifs_tcon *tcon, enum smb3_tcon_ref_trace trace)
+ {
+       unsigned int xid;
+       struct cifs_ses *ses;
+@@ -2137,6 +2139,7 @@ cifs_put_tcon(struct cifs_tcon *tcon)
+       ses = tcon->ses;
+       cifs_dbg(FYI, "%s: tc_count=%d\n", __func__, tcon->tc_count);
+       spin_lock(&cifs_tcp_ses_lock);
++      trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count - 1, trace);
+       if (--tcon->tc_count > 0) {
+               spin_unlock(&cifs_tcp_ses_lock);
+               return;
+@@ -2164,7 +2167,7 @@ cifs_put_tcon(struct cifs_tcon *tcon)
+       _free_xid(xid);
+       cifs_fscache_release_super_cookie(tcon);
+-      tconInfoFree(tcon);
++      tconInfoFree(tcon, netfs_trace_tcon_ref_free);
+       cifs_put_smb_ses(ses);
+ }
+@@ -2212,7 +2215,7 @@ cifs_get_tcon(struct cifs_ses *ses, stru
+               goto out_fail;
+       }
+-      tcon = tconInfoAlloc();
++      tcon = tconInfoAlloc(netfs_trace_tcon_ref_new);
+       if (tcon == NULL) {
+               rc = -ENOMEM;
+               goto out_fail;
+@@ -2388,7 +2391,7 @@ cifs_get_tcon(struct cifs_ses *ses, stru
+       return tcon;
+ out_fail:
+-      tconInfoFree(tcon);
++      tconInfoFree(tcon, netfs_trace_tcon_ref_free_fail);
+       return ERR_PTR(rc);
+ }
+@@ -2405,7 +2408,7 @@ cifs_put_tlink(struct tcon_link *tlink)
+       }
+       if (!IS_ERR(tlink_tcon(tlink)))
+-              cifs_put_tcon(tlink_tcon(tlink));
++              cifs_put_tcon(tlink_tcon(tlink), netfs_trace_tcon_ref_put_tlink);
+       kfree(tlink);
+       return;
+ }
+@@ -2959,7 +2962,7 @@ static inline void mount_put_conns(struc
+       int rc = 0;
+       if (mnt_ctx->tcon)
+-              cifs_put_tcon(mnt_ctx->tcon);
++              cifs_put_tcon(mnt_ctx->tcon, netfs_trace_tcon_ref_put_mnt_ctx);
+       else if (mnt_ctx->ses)
+               cifs_put_smb_ses(mnt_ctx->ses);
+       else if (mnt_ctx->server)
+--- a/fs/cifs/dfs_cache.c
++++ b/fs/cifs/dfs_cache.c
+@@ -1544,7 +1544,7 @@ static void refresh_mounts(struct cifs_s
+               mutex_unlock(&server->refpath_lock);
+               __refresh_tcon(server->origin_fullpath + 1, sessions, tcon, false);
+-              cifs_put_tcon(tcon);
++              cifs_put_tcon(tcon, netfs_trace_tcon_ref_put_dfs_refer);
+       }
+ }
+--- a/fs/cifs/misc.c
++++ b/fs/cifs/misc.c
+@@ -101,9 +101,10 @@ sesInfoFree(struct cifs_ses *buf_to_free
+ }
+ struct cifs_tcon *
+-tconInfoAlloc(void)
++tconInfoAlloc(enum smb3_tcon_ref_trace trace)
+ {
+       struct cifs_tcon *ret_buf;
++      static atomic_t tcon_debug_id;
+       ret_buf = kzalloc(sizeof(*ret_buf), GFP_KERNEL);
+       if (!ret_buf)
+@@ -116,7 +117,8 @@ tconInfoAlloc(void)
+       atomic_inc(&tconInfoAllocCount);
+       ret_buf->tidStatus = CifsNew;
+-      ++ret_buf->tc_count;
++      ret_buf->debug_id = atomic_inc_return(&tcon_debug_id);
++      ret_buf->tc_count = 1;
+       INIT_LIST_HEAD(&ret_buf->openFileList);
+       INIT_LIST_HEAD(&ret_buf->tcon_list);
+       spin_lock_init(&ret_buf->open_file_lock);
+@@ -124,17 +126,19 @@ tconInfoAlloc(void)
+       spin_lock_init(&ret_buf->stat_lock);
+       atomic_set(&ret_buf->num_local_opens, 0);
+       atomic_set(&ret_buf->num_remote_opens, 0);
++      trace_smb3_tcon_ref(ret_buf->debug_id, ret_buf->tc_count, trace);
+       return ret_buf;
+ }
+ void
+-tconInfoFree(struct cifs_tcon *buf_to_free)
++tconInfoFree(struct cifs_tcon *buf_to_free, enum smb3_tcon_ref_trace trace)
+ {
+       if (buf_to_free == NULL) {
+               cifs_dbg(FYI, "Null buffer passed to tconInfoFree\n");
+               return;
+       }
++      trace_smb3_tcon_ref(buf_to_free->debug_id, buf_to_free->tc_count, trace);
+       atomic_dec(&tconInfoAllocCount);
+       kfree(buf_to_free->nativeFileSystem);
+       kfree_sensitive(buf_to_free->password);
+--- a/fs/cifs/smb2misc.c
++++ b/fs/cifs/smb2misc.c
+@@ -773,7 +773,7 @@ smb2_cancelled_close_fid(struct work_str
+       if (rc)
+               cifs_tcon_dbg(VFS, "Close cancelled mid failed rc:%d\n", rc);
+-      cifs_put_tcon(tcon);
++      cifs_put_tcon(tcon, netfs_trace_tcon_ref_put_cancelled_close_fid);
+       kfree(cancelled);
+ }
+@@ -817,6 +817,8 @@ smb2_handle_cancelled_close(struct cifs_
+       if (tcon->tc_count <= 0) {
+               struct TCP_Server_Info *server = NULL;
++              trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count,
++                                  netfs_trace_tcon_ref_see_cancelled_close);
+               WARN_ONCE(tcon->tc_count < 0, "tcon refcount is negative");
+               spin_unlock(&cifs_tcp_ses_lock);
+@@ -830,12 +832,14 @@ smb2_handle_cancelled_close(struct cifs_
+               return 0;
+       }
+       tcon->tc_count++;
++      trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count,
++                          netfs_trace_tcon_ref_get_cancelled_close);
+       spin_unlock(&cifs_tcp_ses_lock);
+       rc = __smb2_handle_cancelled_cmd(tcon, SMB2_CLOSE_HE, 0,
+                                        persistent_fid, volatile_fid);
+       if (rc)
+-              cifs_put_tcon(tcon);
++              cifs_put_tcon(tcon, netfs_trace_tcon_ref_put_cancelled_close);
+       return rc;
+ }
+@@ -863,7 +867,7 @@ smb2_handle_cancelled_mid(struct mid_q_e
+                                        rsp->PersistentFileId,
+                                        rsp->VolatileFileId);
+       if (rc)
+-              cifs_put_tcon(tcon);
++              cifs_put_tcon(tcon, netfs_trace_tcon_ref_put_cancelled_mid);
+       return rc;
+ }
+--- a/fs/cifs/smb2ops.c
++++ b/fs/cifs/smb2ops.c
+@@ -2895,8 +2895,11 @@ smb2_get_dfs_refer(const unsigned int xi
+               tcon = list_first_entry_or_null(&ses->tcon_list,
+                                               struct cifs_tcon,
+                                               tcon_list);
+-              if (tcon)
++              if (tcon) {
+                       tcon->tc_count++;
++                      trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count,
++                                          netfs_trace_tcon_ref_get_dfs_refer);
++              }
+               spin_unlock(&cifs_tcp_ses_lock);
+       }
+@@ -2960,6 +2963,8 @@ smb2_get_dfs_refer(const unsigned int xi
+               /* ipc tcons are not refcounted */
+               spin_lock(&cifs_tcp_ses_lock);
+               tcon->tc_count--;
++              trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count,
++                                  netfs_trace_tcon_ref_dec_dfs_refer);
+               /* tc_count can never go negative */
+               WARN_ON(tcon->tc_count < 0);
+               spin_unlock(&cifs_tcp_ses_lock);
+--- a/fs/cifs/smb2pdu.c
++++ b/fs/cifs/smb2pdu.c
+@@ -3769,6 +3769,8 @@ void smb2_reconnect_server(struct work_s
+               list_for_each_entry(tcon, &ses->tcon_list, tcon_list) {
+                       if (tcon->need_reconnect || tcon->need_reopen_files) {
+                               tcon->tc_count++;
++                              trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count,
++                                                  netfs_trace_tcon_ref_get_reconnect_server);
+                               list_add_tail(&tcon->rlist, &tmp_list);
+                               tcon_exist = true;
+                       }
+@@ -3802,7 +3804,7 @@ void smb2_reconnect_server(struct work_s
+               if (tcon->ipc)
+                       cifs_put_smb_ses(tcon->ses);
+               else
+-                      cifs_put_tcon(tcon);
++                      cifs_put_tcon(tcon, netfs_trace_tcon_ref_put_reconnect_server);
+       }
+       cifs_dbg(FYI, "Reconnecting tcons finished\n");
+--- a/fs/cifs/smb2transport.c
++++ b/fs/cifs/smb2transport.c
+@@ -168,6 +168,8 @@ smb2_find_smb_sess_tcon_unlocked(struct
+               if (tcon->tid != tid)
+                       continue;
+               ++tcon->tc_count;
++              trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count,
++                                  netfs_trace_tcon_ref_get_find_sess_tcon);
+               return tcon;
+       }
+--- a/fs/cifs/trace.h
++++ b/fs/cifs/trace.h
+@@ -3,6 +3,9 @@
+  *   Copyright (C) 2018, Microsoft Corporation.
+  *
+  *   Author(s): Steve French <stfrench@microsoft.com>
++ *
++ * Please use this 3-part article as a reference for writing new tracepoints:
++ * https://lwn.net/Articles/379903/
+  */
+ #undef TRACE_SYSTEM
+ #define TRACE_SYSTEM cifs
+@@ -13,9 +16,71 @@
+ #include <linux/tracepoint.h>
+ /*
+- * Please use this 3-part article as a reference for writing new tracepoints:
+- * https://lwn.net/Articles/379903/
++ * Specify enums for tracing information.
++ */
++#define smb3_tcon_ref_traces                                        \
++      EM(netfs_trace_tcon_ref_dec_dfs_refer,          "DEC DfsRef") \
++      EM(netfs_trace_tcon_ref_free,                   "FRE       ") \
++      EM(netfs_trace_tcon_ref_free_fail,              "FRE Fail  ") \
++      EM(netfs_trace_tcon_ref_free_ipc,               "FRE Ipc   ") \
++      EM(netfs_trace_tcon_ref_free_ipc_fail,          "FRE Ipc-F ") \
++      EM(netfs_trace_tcon_ref_free_reconnect_server,  "FRE Reconn") \
++      EM(netfs_trace_tcon_ref_get_cancelled_close,    "GET Cn-Cls") \
++      EM(netfs_trace_tcon_ref_get_dfs_refer,          "GET DfsRef") \
++      EM(netfs_trace_tcon_ref_get_find,               "GET Find  ") \
++      EM(netfs_trace_tcon_ref_get_find_sess_tcon,     "GET FndSes") \
++      EM(netfs_trace_tcon_ref_get_reconnect_server,   "GET Reconn") \
++      EM(netfs_trace_tcon_ref_new,                    "NEW       ") \
++      EM(netfs_trace_tcon_ref_new_ipc,                "NEW Ipc   ") \
++      EM(netfs_trace_tcon_ref_new_reconnect_server,   "NEW Reconn") \
++      EM(netfs_trace_tcon_ref_put_cancelled_close,    "PUT Cn-Cls") \
++      EM(netfs_trace_tcon_ref_put_cancelled_close_fid, "PUT Cn-Fid") \
++      EM(netfs_trace_tcon_ref_put_cancelled_mid,      "PUT Cn-Mid") \
++      EM(netfs_trace_tcon_ref_put_mnt_ctx,            "PUT MntCtx") \
++      EM(netfs_trace_tcon_ref_put_dfs_refer,          "PUT DfsRfr") \
++      EM(netfs_trace_tcon_ref_put_reconnect_server,   "PUT Reconn") \
++      EM(netfs_trace_tcon_ref_put_tlink,              "PUT Tlink ") \
++      EM(netfs_trace_tcon_ref_see_cancelled_close,    "SEE Cn-Cls") \
++      EM(netfs_trace_tcon_ref_see_fscache_collision,  "SEE FV-CO!") \
++      EM(netfs_trace_tcon_ref_see_fscache_okay,       "SEE FV-Ok ") \
++      EM(netfs_trace_tcon_ref_see_fscache_relinq,     "SEE FV-Rlq") \
++      E_(netfs_trace_tcon_ref_see_umount,             "SEE Umount")
++
++#undef EM
++#undef E_
++
++/*
++ * Define those tracing enums.
+  */
++#ifndef __SMB3_DECLARE_TRACE_ENUMS_ONCE_ONLY
++#define __SMB3_DECLARE_TRACE_ENUMS_ONCE_ONLY
++
++#define EM(a, b) a,
++#define E_(a, b) a
++
++enum smb3_tcon_ref_trace { smb3_tcon_ref_traces } __mode(byte);
++
++#undef EM
++#undef E_
++#endif
++
++/*
++ * Export enum symbols via userspace.
++ */
++#define EM(a, b) TRACE_DEFINE_ENUM(a);
++#define E_(a, b) TRACE_DEFINE_ENUM(a);
++
++smb3_tcon_ref_traces;
++
++#undef EM
++#undef E_
++
++/*
++ * Now redefine the EM() and E_() macros to map the enums to the strings that
++ * will be printed in the output.
++ */
++#define EM(a, b)      { a, b },
++#define E_(a, b)      { a, b }
+ /* For logging errors in read or write */
+ DECLARE_EVENT_CLASS(smb3_rw_err_class,
+@@ -937,6 +1002,30 @@ DEFINE_SMB3_CREDIT_EVENT(too_many_credit
+ DEFINE_SMB3_CREDIT_EVENT(add_credits);
+ DEFINE_SMB3_CREDIT_EVENT(set_credits);
++
++TRACE_EVENT(smb3_tcon_ref,
++          TP_PROTO(unsigned int tcon_debug_id, int ref,
++                   enum smb3_tcon_ref_trace trace),
++          TP_ARGS(tcon_debug_id, ref, trace),
++          TP_STRUCT__entry(
++                  __field(unsigned int,               tcon)
++                  __field(int,                        ref)
++                  __field(enum smb3_tcon_ref_trace,   trace)
++                           ),
++          TP_fast_assign(
++                  __entry->tcon       = tcon_debug_id;
++                  __entry->ref        = ref;
++                  __entry->trace      = trace;
++                         ),
++          TP_printk("TC=%08x %s r=%u",
++                    __entry->tcon,
++                    __print_symbolic(__entry->trace, smb3_tcon_ref_traces),
++                    __entry->ref)
++          );
++
++
++#undef EM
++#undef E_
+ #endif /* _CIFS_TRACE_H */
+ #undef TRACE_INCLUDE_PATH
diff --git a/queue-5.15/cifs-create-a-new-shared-file-holding-smb2-pdu-definitions.patch b/queue-5.15/cifs-create-a-new-shared-file-holding-smb2-pdu-definitions.patch
new file mode 100644 (file)
index 0000000..8cf6c8e
--- /dev/null
@@ -0,0 +1,2056 @@
+From stable+bounces-274536-greg=kroah.com@vger.kernel.org Tue Jul 14 22:03:12 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 14 Jul 2026 16:02:11 -0400
+Subject: cifs: Create a new shared file holding smb2 pdu definitions
+To: stable@vger.kernel.org
+Cc: Ronnie Sahlberg <lsahlber@redhat.com>, Namjae Jeon <linkinjeon@kernel.org>, Steve French <stfrench@microsoft.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260714200215.3152449-1-sashal@kernel.org>
+
+From: Ronnie Sahlberg <lsahlber@redhat.com>
+
+[ Upstream commit 0d35e382e4e96a4fd97a1438bc1b11a91d2d85a6 ]
+
+This file will contain all the definitions we need for SMB2 packets
+and will follow the naming convention of MS-SMB2.PDF as closely
+as possible to make it easier to cross-reference beween the definitions
+and the standard.
+
+The content of this file will mostly consist of migration of existing
+definitions in the cifs/smb2.pdu.h and ksmbd/smb2pdu.h files
+with some additional tweaks as the two files have diverged.
+
+This patch introduces the new smbfs_common/smb2pdu.h file
+and migrates the SMB2 header as well as TREE_CONNECT and TREE_DISCONNECT
+to the shared file.
+
+Signed-off-by: Ronnie Sahlberg <lsahlber@redhat.com>
+Reviewed-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Stable-dep-of: 53b7c271f06b ("smb: client: restrict implied bcc[0] exemption to responses without data area")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/cifs/cifsfs.c          |    1 
+ fs/cifs/cifsglob.h        |    3 
+ fs/cifs/connect.c         |    4 
+ fs/cifs/misc.c            |    2 
+ fs/cifs/smb2maperror.c    |   16 +
+ fs/cifs/smb2misc.c        |   43 ++---
+ fs/cifs/smb2ops.c         |   65 ++++----
+ fs/cifs/smb2pdu.c         |  106 ++++++-------
+ fs/cifs/smb2pdu.h         |  373 ++++------------------------------------------
+ fs/cifs/smb2proto.h       |    2 
+ fs/cifs/smb2transport.c   |   36 ++--
+ fs/smbfs_common/smb2pdu.h |  318 +++++++++++++++++++++++++++++++++++++++
+ 12 files changed, 493 insertions(+), 476 deletions(-)
+ create mode 100644 fs/smbfs_common/smb2pdu.h
+
+--- a/fs/cifs/cifsfs.c
++++ b/fs/cifs/cifsfs.c
+@@ -38,7 +38,6 @@
+ #include <linux/key-type.h>
+ #include "cifs_spnego.h"
+ #include "fscache.h"
+-#include "smb2pdu.h"
+ #ifdef CONFIG_CIFS_DFS_UPCALL
+ #include "dfs_cache.h"
+ #endif
+--- a/fs/cifs/cifsglob.h
++++ b/fs/cifs/cifsglob.h
+@@ -22,6 +22,7 @@
+ #include <crypto/internal/hash.h>
+ #include <linux/scatterlist.h>
+ #include <uapi/linux/cifs/cifs_mount.h>
++#include "../smbfs_common/smb2pdu.h"
+ #include "smb2pdu.h"
+ #include "smb2glob.h"
+@@ -794,7 +795,7 @@ revert_current_mid(struct TCP_Server_Inf
+ static inline void
+ revert_current_mid_from_hdr(struct TCP_Server_Info *server,
+-                          const struct smb2_sync_hdr *shdr)
++                          const struct smb2_hdr *shdr)
+ {
+       unsigned int num = le16_to_cpu(shdr->CreditCharge);
+--- a/fs/cifs/connect.c
++++ b/fs/cifs/connect.c
+@@ -722,7 +722,7 @@ dequeue_mid(struct mid_q_entry *mid, boo
+ static unsigned int
+ smb2_get_credits_from_hdr(char *buffer, struct TCP_Server_Info *server)
+ {
+-      struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)buffer;
++      struct smb2_hdr *shdr = (struct smb2_hdr *)buffer;
+       /*
+        * SMB1 does not use credits.
+@@ -926,7 +926,7 @@ cifs_handle_standard(struct TCP_Server_I
+ static void
+ smb2_add_credits_from_hdr(char *buffer, struct TCP_Server_Info *server)
+ {
+-      struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)buffer;
++      struct smb2_hdr *shdr = (struct smb2_hdr *)buffer;
+       int scredits, in_flight;
+       /*
+--- a/fs/cifs/misc.c
++++ b/fs/cifs/misc.c
+@@ -150,7 +150,7 @@ cifs_buf_get(void)
+        * SMB2 header is bigger than CIFS one - no problems to clean some
+        * more bytes for CIFS.
+        */
+-      size_t buf_size = sizeof(struct smb2_sync_hdr);
++      size_t buf_size = sizeof(struct smb2_hdr);
+       /*
+        * We could use negotiated size instead of max_msgsize -
+--- a/fs/cifs/smb2maperror.c
++++ b/fs/cifs/smb2maperror.c
+@@ -2439,14 +2439,16 @@ smb2_print_status(__le32 status)
+ int
+ map_smb2_to_linux_error(char *buf, bool log_err)
+ {
+-      struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)buf;
++      struct smb2_hdr *shdr = (struct smb2_hdr *)buf;
+       unsigned int i;
+       int rc = -EIO;
+       __le32 smb2err = shdr->Status;
+       if (smb2err == 0) {
+-              trace_smb3_cmd_done(shdr->TreeId, shdr->SessionId,
+-                      le16_to_cpu(shdr->Command), le64_to_cpu(shdr->MessageId));
++              trace_smb3_cmd_done(le32_to_cpu(shdr->Id.SyncId.TreeId),
++                            le64_to_cpu(shdr->SessionId),
++                            le16_to_cpu(shdr->Command),
++                            le64_to_cpu(shdr->MessageId));
+               return 0;
+       }
+@@ -2470,8 +2472,10 @@ map_smb2_to_linux_error(char *buf, bool
+       cifs_dbg(FYI, "Mapping SMB2 status code 0x%08x to POSIX err %d\n",
+                __le32_to_cpu(smb2err), rc);
+-      trace_smb3_cmd_err(shdr->TreeId, shdr->SessionId,
+-                      le16_to_cpu(shdr->Command),
+-                      le64_to_cpu(shdr->MessageId), le32_to_cpu(smb2err), rc);
++      trace_smb3_cmd_err(le32_to_cpu(shdr->Id.SyncId.TreeId),
++                         le64_to_cpu(shdr->SessionId),
++                         le16_to_cpu(shdr->Command),
++                         le64_to_cpu(shdr->MessageId),
++                         le32_to_cpu(smb2err), rc);
+       return rc;
+ }
+--- a/fs/cifs/smb2misc.c
++++ b/fs/cifs/smb2misc.c
+@@ -8,7 +8,6 @@
+  *
+  */
+ #include <linux/ctype.h>
+-#include "smb2pdu.h"
+ #include "cifsglob.h"
+ #include "cifsproto.h"
+ #include "smb2proto.h"
+@@ -19,7 +18,7 @@
+ #include "nterr.h"
+ static int
+-check_smb2_hdr(struct smb2_sync_hdr *shdr, __u64 mid)
++check_smb2_hdr(struct smb2_hdr *shdr, __u64 mid)
+ {
+       __u64 wire_mid = le64_to_cpu(shdr->MessageId);
+@@ -81,9 +80,9 @@ static const __le16 smb2_rsp_struct_size
+       /* SMB2_OPLOCK_BREAK */ cpu_to_le16(24)
+ };
+-#define SMB311_NEGPROT_BASE_SIZE (sizeof(struct smb2_sync_hdr) + sizeof(struct smb2_negotiate_rsp))
++#define SMB311_NEGPROT_BASE_SIZE (sizeof(struct smb2_hdr) + sizeof(struct smb2_negotiate_rsp))
+-static __u32 get_neg_ctxt_len(struct smb2_sync_hdr *hdr, __u32 len,
++static __u32 get_neg_ctxt_len(struct smb2_hdr *hdr, __u32 len,
+                             __u32 non_ctxlen)
+ {
+       __u16 neg_count;
+@@ -135,13 +134,13 @@ static __u32 get_neg_ctxt_len(struct smb
+ int
+ smb2_check_message(char *buf, unsigned int len, struct TCP_Server_Info *srvr)
+ {
+-      struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)buf;
+-      struct smb2_sync_pdu *pdu = (struct smb2_sync_pdu *)shdr;
++      struct smb2_hdr *shdr = (struct smb2_hdr *)buf;
++      struct smb2_pdu *pdu = (struct smb2_pdu *)shdr;
+       __u64 mid;
+       __u32 clc_len;  /* calculated length */
+       int command;
+-      int pdu_size = sizeof(struct smb2_sync_pdu);
+-      int hdr_size = sizeof(struct smb2_sync_hdr);
++      int pdu_size = sizeof(struct smb2_pdu);
++      int hdr_size = sizeof(struct smb2_hdr);
+       /*
+        * Add function to do table lookup of StructureSize by command
+@@ -155,7 +154,7 @@ smb2_check_message(char *buf, unsigned i
+               /* decrypt frame now that it is completely read in */
+               spin_lock(&cifs_tcp_ses_lock);
+               list_for_each_entry(ses, &srvr->smb_ses_list, smb_ses_list) {
+-                      if (ses->Suid == thdr->SessionId)
++                      if (ses->Suid == le64_to_cpu(thdr->SessionId))
+                               break;
+               }
+               spin_unlock(&cifs_tcp_ses_lock);
+@@ -296,7 +295,7 @@ static const bool has_smb2_data_area[NUM
+  * area and the offset to it (from the beginning of the smb are also returned.
+  */
+ char *
+-smb2_get_data_area_len(int *off, int *len, struct smb2_sync_hdr *shdr)
++smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *shdr)
+ {
+       const int max_off = 4096;
+       const int max_len = 128 * 1024;
+@@ -395,8 +394,8 @@ smb2_get_data_area_len(int *off, int *le
+ unsigned int
+ smb2_calc_size(void *buf, struct TCP_Server_Info *srvr)
+ {
+-      struct smb2_sync_pdu *pdu = (struct smb2_sync_pdu *)buf;
+-      struct smb2_sync_hdr *shdr = &pdu->sync_hdr;
++      struct smb2_pdu *pdu = (struct smb2_pdu *)buf;
++      struct smb2_hdr *shdr = &pdu->hdr;
+       int offset; /* the offset from the beginning of SMB to data area */
+       int data_length; /* the length of the variable length data area */
+       /* Structure Size has already been checked to make sure it is 64 */
+@@ -663,7 +662,7 @@ smb2_is_valid_oplock_break(char *buffer,
+       cifs_dbg(FYI, "Checking for oplock break\n");
+-      if (rsp->sync_hdr.Command != SMB2_OPLOCK_BREAK)
++      if (rsp->hdr.Command != SMB2_OPLOCK_BREAK)
+               return false;
+       if (rsp->StructureSize !=
+@@ -813,23 +812,23 @@ smb2_handle_cancelled_close(struct cifs_
+ int
+ smb2_handle_cancelled_mid(struct mid_q_entry *mid, struct TCP_Server_Info *server)
+ {
+-      struct smb2_sync_hdr *sync_hdr = mid->resp_buf;
++      struct smb2_hdr *hdr = mid->resp_buf;
+       struct smb2_create_rsp *rsp = mid->resp_buf;
+       struct cifs_tcon *tcon;
+       int rc;
+-      if ((mid->optype & CIFS_CP_CREATE_CLOSE_OP) || sync_hdr->Command != SMB2_CREATE ||
+-          sync_hdr->Status != STATUS_SUCCESS)
++      if ((mid->optype & CIFS_CP_CREATE_CLOSE_OP) || hdr->Command != SMB2_CREATE ||
++          hdr->Status != STATUS_SUCCESS)
+               return 0;
+-      tcon = smb2_find_smb_tcon(server, sync_hdr->SessionId,
+-                                sync_hdr->TreeId);
++      tcon = smb2_find_smb_tcon(server, le64_to_cpu(hdr->SessionId),
++                                le32_to_cpu(hdr->Id.SyncId.TreeId));
+       if (!tcon)
+               return -ENOENT;
+       rc = __smb2_handle_cancelled_cmd(tcon,
+-                                       le16_to_cpu(sync_hdr->Command),
+-                                       le64_to_cpu(sync_hdr->MessageId),
++                                       le16_to_cpu(hdr->Command),
++                                       le64_to_cpu(hdr->MessageId),
+                                        rsp->PersistentFileId,
+                                        rsp->VolatileFileId);
+       if (rc)
+@@ -853,10 +852,10 @@ smb311_update_preauth_hash(struct cifs_s
+ {
+       int i, rc;
+       struct sdesc *d;
+-      struct smb2_sync_hdr *hdr;
++      struct smb2_hdr *hdr;
+       struct TCP_Server_Info *server = cifs_ses_server(ses);
+-      hdr = (struct smb2_sync_hdr *)iov[0].iov_base;
++      hdr = (struct smb2_hdr *)iov[0].iov_base;
+       /* neg prot are always taken */
+       if (hdr->Command == SMB2_NEGOTIATE)
+               goto ok;
+--- a/fs/cifs/smb2ops.c
++++ b/fs/cifs/smb2ops.c
+@@ -326,7 +326,7 @@ static struct mid_q_entry *
+ __smb2_find_mid(struct TCP_Server_Info *server, char *buf, bool dequeue)
+ {
+       struct mid_q_entry *mid;
+-      struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)buf;
++      struct smb2_hdr *shdr = (struct smb2_hdr *)buf;
+       __u64 wire_mid = le64_to_cpu(shdr->MessageId);
+       if (shdr->ProtocolId == SMB2_TRANSFORM_PROTO_NUM) {
+@@ -368,11 +368,11 @@ static void
+ smb2_dump_detail(void *buf, struct TCP_Server_Info *server)
+ {
+ #ifdef CONFIG_CIFS_DEBUG2
+-      struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)buf;
++      struct smb2_hdr *shdr = (struct smb2_hdr *)buf;
+       cifs_server_dbg(VFS, "Cmd: %d Err: 0x%x Flags: 0x%x Mid: %llu Pid: %d\n",
+                shdr->Command, shdr->Status, shdr->Flags, shdr->MessageId,
+-               shdr->ProcessId);
++               shdr->Id.SyncId.ProcessId);
+       cifs_server_dbg(VFS, "smb buf %p len %u\n", buf,
+                server->ops->calc_smb_size(buf, server));
+ #endif
+@@ -890,7 +890,7 @@ int open_cached_dir(unsigned int xid, st
+       oparms.fid->persistent_fid = o_rsp->PersistentFileId;
+       oparms.fid->volatile_fid = o_rsp->VolatileFileId;
+ #ifdef CONFIG_CIFS_DEBUG2
+-      oparms.fid->mid = le64_to_cpu(o_rsp->sync_hdr.MessageId);
++      oparms.fid->mid = le64_to_cpu(o_rsp->hdr.MessageId);
+ #endif /* CIFS_DEBUG2 */
+       tcon->crfid.tcon = tcon;
+@@ -2430,7 +2430,7 @@ again:
+       /* If the open failed there is nothing to do */
+       op_rsp = (struct smb2_create_rsp *)rsp_iov[0].iov_base;
+-      if (op_rsp == NULL || op_rsp->sync_hdr.Status != STATUS_SUCCESS) {
++      if (op_rsp == NULL || op_rsp->hdr.Status != STATUS_SUCCESS) {
+               cifs_dbg(FYI, "query_dir_first: open failed rc=%d\n", rc);
+               goto qdf_free;
+       }
+@@ -2449,7 +2449,7 @@ again:
+       atomic_inc(&tcon->num_remote_opens);
+       qd_rsp = (struct smb2_query_directory_rsp *)rsp_iov[1].iov_base;
+-      if (qd_rsp->sync_hdr.Status == STATUS_NO_MORE_FILES) {
++      if (qd_rsp->hdr.Status == STATUS_NO_MORE_FILES) {
+               trace_smb3_query_dir_done(xid, fid->persistent_fid,
+                                         tcon->tid, tcon->ses->Suid, 0, 0);
+               srch_inf->endOfSearch = true;
+@@ -2501,7 +2501,7 @@ smb2_close_dir(const unsigned int xid, s
+ static bool
+ smb2_is_status_pending(char *buf, struct TCP_Server_Info *server)
+ {
+-      struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)buf;
++      struct smb2_hdr *shdr = (struct smb2_hdr *)buf;
+       int scredits, in_flight;
+       if (shdr->Status != STATUS_PENDING)
+@@ -2528,13 +2528,14 @@ smb2_is_status_pending(char *buf, struct
+ static bool
+ smb2_is_session_expired(char *buf)
+ {
+-      struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)buf;
++      struct smb2_hdr *shdr = (struct smb2_hdr *)buf;
+       if (shdr->Status != STATUS_NETWORK_SESSION_EXPIRED &&
+           shdr->Status != STATUS_USER_SESSION_DELETED)
+               return false;
+-      trace_smb3_ses_expired(shdr->TreeId, shdr->SessionId,
++      trace_smb3_ses_expired(le32_to_cpu(shdr->Id.SyncId.TreeId),
++                             le64_to_cpu(shdr->SessionId),
+                              le16_to_cpu(shdr->Command),
+                              le64_to_cpu(shdr->MessageId));
+       cifs_dbg(FYI, "Session expired or deleted\n");
+@@ -2545,7 +2546,7 @@ smb2_is_session_expired(char *buf)
+ static bool
+ smb2_is_status_io_timeout(char *buf)
+ {
+-      struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)buf;
++      struct smb2_hdr *shdr = (struct smb2_hdr *)buf;
+       if (shdr->Status == STATUS_IO_TIMEOUT)
+               return true;
+@@ -2556,7 +2557,7 @@ smb2_is_status_io_timeout(char *buf)
+ static void
+ smb2_is_network_name_deleted(char *buf, struct TCP_Server_Info *server)
+ {
+-      struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)buf;
++      struct smb2_hdr *shdr = (struct smb2_hdr *)buf;
+       struct list_head *tmp, *tmp1;
+       struct cifs_ses *ses;
+       struct cifs_tcon *tcon;
+@@ -2569,7 +2570,7 @@ smb2_is_network_name_deleted(char *buf,
+               ses = list_entry(tmp, struct cifs_ses, smb_ses_list);
+               list_for_each(tmp1, &ses->tcon_list) {
+                       tcon = list_entry(tmp1, struct cifs_tcon, tcon_list);
+-                      if (tcon->tid == shdr->TreeId) {
++                      if (tcon->tid == le32_to_cpu(shdr->Id.SyncId.TreeId)) {
+                               tcon->need_reconnect = true;
+                               spin_unlock(&cifs_tcp_ses_lock);
+                               pr_warn_once("Server share %s deleted.\n",
+@@ -2596,9 +2597,9 @@ smb2_oplock_response(struct cifs_tcon *t
+ void
+ smb2_set_related(struct smb_rqst *rqst)
+ {
+-      struct smb2_sync_hdr *shdr;
++      struct smb2_hdr *shdr;
+-      shdr = (struct smb2_sync_hdr *)(rqst->rq_iov[0].iov_base);
++      shdr = (struct smb2_hdr *)(rqst->rq_iov[0].iov_base);
+       if (shdr == NULL) {
+               cifs_dbg(FYI, "shdr NULL in smb2_set_related\n");
+               return;
+@@ -2611,13 +2612,13 @@ char smb2_padding[7] = {0, 0, 0, 0, 0, 0
+ void
+ smb2_set_next_command(struct cifs_tcon *tcon, struct smb_rqst *rqst)
+ {
+-      struct smb2_sync_hdr *shdr;
++      struct smb2_hdr *shdr;
+       struct cifs_ses *ses = tcon->ses;
+       struct TCP_Server_Info *server = ses->server;
+       unsigned long len = smb_rqst_len(server, rqst);
+       int i, num_padding;
+-      shdr = (struct smb2_sync_hdr *)(rqst->rq_iov[0].iov_base);
++      shdr = (struct smb2_hdr *)(rqst->rq_iov[0].iov_base);
+       if (shdr == NULL) {
+               cifs_dbg(FYI, "shdr NULL in smb2_set_next_command\n");
+               return;
+@@ -3172,7 +3173,7 @@ smb2_query_symlink(const unsigned int xi
+                               resp_buftype, rsp_iov);
+       create_rsp = rsp_iov[0].iov_base;
+-      if (create_rsp && create_rsp->sync_hdr.Status)
++      if (create_rsp && create_rsp->hdr.Status)
+               err_iov = rsp_iov[0];
+       ioctl_rsp = rsp_iov[1].iov_base;
+@@ -4423,8 +4424,8 @@ static void
+ fill_transform_hdr(struct smb2_transform_hdr *tr_hdr, unsigned int orig_len,
+                  struct smb_rqst *old_rq, __le16 cipher_type)
+ {
+-      struct smb2_sync_hdr *shdr =
+-                      (struct smb2_sync_hdr *)old_rq->rq_iov[0].iov_base;
++      struct smb2_hdr *shdr =
++                      (struct smb2_hdr *)old_rq->rq_iov[0].iov_base;
+       memset(tr_hdr, 0, sizeof(struct smb2_transform_hdr));
+       tr_hdr->ProtocolId = SMB2_TRANSFORM_PROTO_NUM;
+@@ -4562,7 +4563,7 @@ crypt_message(struct TCP_Server_Info *se
+       unsigned int crypt_len = le32_to_cpu(tr_hdr->OriginalMessageSize);
+       void *creq;
+-      rc = smb2_get_enc_key(server, tr_hdr->SessionId, enc, key);
++      rc = smb2_get_enc_key(server, le64_to_cpu(tr_hdr->SessionId), enc, key);
+       if (rc) {
+               cifs_server_dbg(VFS, "%s: Could not get %scryption key\n", __func__,
+                        enc ? "en" : "de");
+@@ -4848,7 +4849,7 @@ handle_read_data(struct TCP_Server_Info
+       unsigned int cur_page_idx;
+       unsigned int pad_len;
+       struct cifs_readdata *rdata = mid->callback_data;
+-      struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)buf;
++      struct smb2_hdr *shdr = (struct smb2_hdr *)buf;
+       struct bio_vec *bvec = NULL;
+       struct iov_iter iter;
+       struct kvec iov;
+@@ -5177,7 +5178,7 @@ receive_encrypted_standard(struct TCP_Se
+ {
+       int ret, length;
+       char *buf = server->smallbuf;
+-      struct smb2_sync_hdr *shdr;
++      struct smb2_hdr *shdr;
+       unsigned int pdu_length = server->pdu_size;
+       unsigned int buf_size;
+       unsigned int next_cmd;
+@@ -5208,7 +5209,7 @@ receive_encrypted_standard(struct TCP_Se
+       next_is_large = server->large_buf;
+ one_more:
+-      shdr = (struct smb2_sync_hdr *)buf;
++      shdr = (struct smb2_hdr *)buf;
+       next_cmd = le32_to_cpu(shdr->NextCommand);
+       if (next_cmd) {
+               if (WARN_ON_ONCE(next_cmd > pdu_length))
+@@ -5279,7 +5280,7 @@ smb3_receive_transform(struct TCP_Server
+       unsigned int orig_len = le32_to_cpu(tr_hdr->OriginalMessageSize);
+       if (pdu_length < sizeof(struct smb2_transform_hdr) +
+-                                              sizeof(struct smb2_sync_hdr)) {
++                                              sizeof(struct smb2_hdr)) {
+               cifs_server_dbg(VFS, "Transform message is too small (%u)\n",
+                        pdu_length);
+               cifs_reconnect(server);
+@@ -5312,7 +5313,7 @@ smb3_handle_read_data(struct TCP_Server_
+ static int
+ smb2_next_header(char *buf)
+ {
+-      struct smb2_sync_hdr *hdr = (struct smb2_sync_hdr *)buf;
++      struct smb2_hdr *hdr = (struct smb2_hdr *)buf;
+       struct smb2_transform_hdr *t_hdr = (struct smb2_transform_hdr *)buf;
+       if (hdr->ProtocolId == SMB2_TRANSFORM_PROTO_NUM)
+@@ -5856,7 +5857,7 @@ struct smb_version_values smb20_values =
+       .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE_LOCK,
+       .shared_lock_type = SMB2_LOCKFLAG_SHARED_LOCK,
+       .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK,
+-      .header_size = sizeof(struct smb2_sync_hdr),
++      .header_size = sizeof(struct smb2_hdr),
+       .header_preamble_size = 0,
+       .max_header_size = MAX_SMB2_HDR_SIZE,
+       .read_rsp_size = sizeof(struct smb2_read_rsp),
+@@ -5878,7 +5879,7 @@ struct smb_version_values smb21_values =
+       .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE_LOCK,
+       .shared_lock_type = SMB2_LOCKFLAG_SHARED_LOCK,
+       .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK,
+-      .header_size = sizeof(struct smb2_sync_hdr),
++      .header_size = sizeof(struct smb2_hdr),
+       .header_preamble_size = 0,
+       .max_header_size = MAX_SMB2_HDR_SIZE,
+       .read_rsp_size = sizeof(struct smb2_read_rsp),
+@@ -5899,7 +5900,7 @@ struct smb_version_values smb3any_values
+       .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE_LOCK,
+       .shared_lock_type = SMB2_LOCKFLAG_SHARED_LOCK,
+       .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK,
+-      .header_size = sizeof(struct smb2_sync_hdr),
++      .header_size = sizeof(struct smb2_hdr),
+       .header_preamble_size = 0,
+       .max_header_size = MAX_SMB2_HDR_SIZE,
+       .read_rsp_size = sizeof(struct smb2_read_rsp),
+@@ -5920,7 +5921,7 @@ struct smb_version_values smbdefault_val
+       .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE_LOCK,
+       .shared_lock_type = SMB2_LOCKFLAG_SHARED_LOCK,
+       .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK,
+-      .header_size = sizeof(struct smb2_sync_hdr),
++      .header_size = sizeof(struct smb2_hdr),
+       .header_preamble_size = 0,
+       .max_header_size = MAX_SMB2_HDR_SIZE,
+       .read_rsp_size = sizeof(struct smb2_read_rsp),
+@@ -5941,7 +5942,7 @@ struct smb_version_values smb30_values =
+       .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE_LOCK,
+       .shared_lock_type = SMB2_LOCKFLAG_SHARED_LOCK,
+       .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK,
+-      .header_size = sizeof(struct smb2_sync_hdr),
++      .header_size = sizeof(struct smb2_hdr),
+       .header_preamble_size = 0,
+       .max_header_size = MAX_SMB2_HDR_SIZE,
+       .read_rsp_size = sizeof(struct smb2_read_rsp),
+@@ -5962,7 +5963,7 @@ struct smb_version_values smb302_values
+       .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE_LOCK,
+       .shared_lock_type = SMB2_LOCKFLAG_SHARED_LOCK,
+       .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK,
+-      .header_size = sizeof(struct smb2_sync_hdr),
++      .header_size = sizeof(struct smb2_hdr),
+       .header_preamble_size = 0,
+       .max_header_size = MAX_SMB2_HDR_SIZE,
+       .read_rsp_size = sizeof(struct smb2_read_rsp),
+@@ -5983,7 +5984,7 @@ struct smb_version_values smb311_values
+       .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE_LOCK,
+       .shared_lock_type = SMB2_LOCKFLAG_SHARED_LOCK,
+       .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK,
+-      .header_size = sizeof(struct smb2_sync_hdr),
++      .header_size = sizeof(struct smb2_hdr),
+       .header_preamble_size = 0,
+       .max_header_size = MAX_SMB2_HDR_SIZE,
+       .read_rsp_size = sizeof(struct smb2_read_rsp),
+--- a/fs/cifs/smb2pdu.c
++++ b/fs/cifs/smb2pdu.c
+@@ -23,7 +23,6 @@
+ #include <linux/uuid.h>
+ #include <linux/pagemap.h>
+ #include <linux/xattr.h>
+-#include "smb2pdu.h"
+ #include "cifsglob.h"
+ #include "cifsacl.h"
+ #include "cifsproto.h"
+@@ -84,7 +83,7 @@ int smb3_encryption_required(const struc
+ }
+ static void
+-smb2_hdr_assemble(struct smb2_sync_hdr *shdr, __le16 smb2_cmd,
++smb2_hdr_assemble(struct smb2_hdr *shdr, __le16 smb2_cmd,
+                 const struct cifs_tcon *tcon,
+                 struct TCP_Server_Info *server)
+ {
+@@ -104,7 +103,7 @@ smb2_hdr_assemble(struct smb2_sync_hdr *
+       } else {
+               shdr->CreditRequest = cpu_to_le16(2);
+       }
+-      shdr->ProcessId = cpu_to_le32((__u16)current->tgid);
++      shdr->Id.SyncId.ProcessId = cpu_to_le32((__u16)current->tgid);
+       if (!tcon)
+               goto out;
+@@ -115,10 +114,10 @@ smb2_hdr_assemble(struct smb2_sync_hdr *
+               shdr->CreditCharge = cpu_to_le16(1);
+       /* else CreditCharge MBZ */
+-      shdr->TreeId = tcon->tid;
++      shdr->Id.SyncId.TreeId = cpu_to_le32(tcon->tid);
+       /* Uid is not converted */
+       if (tcon->ses)
+-              shdr->SessionId = tcon->ses->Suid;
++              shdr->SessionId = cpu_to_le64(tcon->ses->Suid);
+       /*
+        * If we would set SMB2_FLAGS_DFS_OPERATIONS on open we also would have
+@@ -338,7 +337,7 @@ fill_small_buf(__le16 smb2_command, stru
+              void *buf,
+              unsigned int *total_len)
+ {
+-      struct smb2_sync_pdu *spdu = (struct smb2_sync_pdu *)buf;
++      struct smb2_pdu *spdu = (struct smb2_pdu *)buf;
+       /* lookup word count ie StructureSize from table */
+       __u16 parmsize = smb2_req_struct_sizes[le16_to_cpu(smb2_command)];
+@@ -348,10 +347,10 @@ fill_small_buf(__le16 smb2_command, stru
+        */
+       memset(buf, 0, 256);
+-      smb2_hdr_assemble(&spdu->sync_hdr, smb2_command, tcon, server);
++      smb2_hdr_assemble(&spdu->hdr, smb2_command, tcon, server);
+       spdu->StructureSize2 = cpu_to_le16(parmsize);
+-      *total_len = parmsize + sizeof(struct smb2_sync_hdr);
++      *total_len = parmsize + sizeof(struct smb2_hdr);
+ }
+ /*
+@@ -379,7 +378,7 @@ static int __smb2_plain_req_init(__le16
+       }
+       fill_small_buf(smb2_command, tcon, server,
+-                     (struct smb2_sync_hdr *)(*request_buf),
++                     (struct smb2_hdr *)(*request_buf),
+                      total_len);
+       if (tcon != NULL) {
+@@ -869,7 +868,7 @@ SMB2_negotiate(const unsigned int xid, s
+       if (rc)
+               return rc;
+-      req->sync_hdr.SessionId = 0;
++      req->hdr.SessionId = 0;
+       memset(server->preauth_sha_hash, 0, SMB2_PREAUTH_HASH_SIZE);
+       memset(ses->preauth_sha_hash, 0, SMB2_PREAUTH_HASH_SIZE);
+@@ -1034,7 +1033,7 @@ SMB2_negotiate(const unsigned int xid, s
+               server->cipher_type = SMB2_ENCRYPTION_AES128_CCM;
+       security_blob = smb2_get_data_area_len(&blob_offset, &blob_length,
+-                                             (struct smb2_sync_hdr *)rsp);
++                                             (struct smb2_hdr *)rsp);
+       /*
+        * See MS-SMB2 section 2.2.4: if no blob, client picks default which
+        * for us will be
+@@ -1272,13 +1271,13 @@ SMB2_sess_alloc_buffer(struct SMB2_sess_
+               return rc;
+       if (sess_data->ses->binding) {
+-              req->sync_hdr.SessionId = sess_data->ses->Suid;
+-              req->sync_hdr.Flags |= SMB2_FLAGS_SIGNED;
++              req->hdr.SessionId = cpu_to_le64(sess_data->ses->Suid);
++              req->hdr.Flags |= SMB2_FLAGS_SIGNED;
+               req->PreviousSessionId = 0;
+               req->Flags = SMB2_SESSION_REQ_FLAG_BINDING;
+       } else {
+               /* First session, not a reauthenticate */
+-              req->sync_hdr.SessionId = 0;
++              req->hdr.SessionId = 0;
+               /*
+                * if reconnect, we need to send previous sess id
+                * otherwise it is 0
+@@ -1288,7 +1287,7 @@ SMB2_sess_alloc_buffer(struct SMB2_sess_
+       }
+       /* enough to enable echos and oplocks and one max size write */
+-      req->sync_hdr.CreditRequest = cpu_to_le16(130);
++      req->hdr.CreditRequest = cpu_to_le16(130);
+       /* only one of SMB2 signing flags may be set in SMB2 request */
+       if (server->sign)
+@@ -1447,7 +1446,7 @@ SMB2_auth_kerberos(struct SMB2_sess_data
+       rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base;
+       /* keep session id and flags if binding */
+       if (!ses->binding) {
+-              ses->Suid = rsp->sync_hdr.SessionId;
++              ses->Suid = le64_to_cpu(rsp->hdr.SessionId);
+               ses->session_flags = le16_to_cpu(rsp->SessionFlags);
+       }
+@@ -1523,7 +1522,7 @@ SMB2_sess_auth_rawntlmssp_negotiate(stru
+       /* If true, rc here is expected and not an error */
+       if (sess_data->buf0_type != CIFS_NO_BUFFER &&
+-              rsp->sync_hdr.Status == STATUS_MORE_PROCESSING_REQUIRED)
++              rsp->hdr.Status == STATUS_MORE_PROCESSING_REQUIRED)
+               rc = 0;
+       if (rc)
+@@ -1545,7 +1544,7 @@ SMB2_sess_auth_rawntlmssp_negotiate(stru
+       /* keep existing ses id and flags if binding */
+       if (!ses->binding) {
+-              ses->Suid = rsp->sync_hdr.SessionId;
++              ses->Suid = le64_to_cpu(rsp->hdr.SessionId);
+               ses->session_flags = le16_to_cpu(rsp->SessionFlags);
+       }
+@@ -1580,7 +1579,7 @@ SMB2_sess_auth_rawntlmssp_authenticate(s
+               goto out;
+       req = (struct smb2_sess_setup_req *) sess_data->iov[0].iov_base;
+-      req->sync_hdr.SessionId = ses->Suid;
++      req->hdr.SessionId = cpu_to_le64(ses->Suid);
+       rc = build_ntlmssp_auth_blob(&ntlmssp_blob, &blob_length, ses,
+                                       sess_data->nls_cp);
+@@ -1606,7 +1605,7 @@ SMB2_sess_auth_rawntlmssp_authenticate(s
+       /* keep existing ses id and flags if binding */
+       if (!ses->binding) {
+-              ses->Suid = rsp->sync_hdr.SessionId;
++              ses->Suid = le64_to_cpu(rsp->hdr.SessionId);
+               ses->session_flags = le16_to_cpu(rsp->SessionFlags);
+       }
+@@ -1737,12 +1736,12 @@ SMB2_logoff(const unsigned int xid, stru
+               return rc;
+        /* since no tcon, smb2_init can not do this, so do here */
+-      req->sync_hdr.SessionId = ses->Suid;
++      req->hdr.SessionId = cpu_to_le64(ses->Suid);
+       if (ses->session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA)
+               flags |= CIFS_TRANSFORM_REQ;
+       else if (server->sign)
+-              req->sync_hdr.Flags |= SMB2_FLAGS_SIGNED;
++              req->hdr.Flags |= SMB2_FLAGS_SIGNED;
+       flags |= CIFS_NO_RSP_BUF;
+@@ -1849,14 +1848,14 @@ SMB2_tcon(const unsigned int xid, struct
+           !(ses->session_flags &
+                   (SMB2_SESSION_FLAG_IS_GUEST|SMB2_SESSION_FLAG_IS_NULL)) &&
+           ((ses->user_name != NULL) || (ses->sectype == Kerberos)))
+-              req->sync_hdr.Flags |= SMB2_FLAGS_SIGNED;
++              req->hdr.Flags |= SMB2_FLAGS_SIGNED;
+       memset(&rqst, 0, sizeof(struct smb_rqst));
+       rqst.rq_iov = iov;
+       rqst.rq_nvec = 2;
+       /* Need 64 for max size write so ask for more in case not there yet */
+-      req->sync_hdr.CreditRequest = cpu_to_le16(64);
++      req->hdr.CreditRequest = cpu_to_le16(64);
+       rc = cifs_send_recv(xid, ses, server,
+                           &rqst, &resp_buftype, flags, &rsp_iov);
+@@ -1892,7 +1891,7 @@ SMB2_tcon(const unsigned int xid, struct
+       tcon->maximal_access = le32_to_cpu(rsp->MaximalAccess);
+       tcon->tidStatus = CifsGood;
+       tcon->need_reconnect = false;
+-      tcon->tid = rsp->sync_hdr.TreeId;
++      tcon->tid = le32_to_cpu(rsp->hdr.Id.SyncId.TreeId);
+       strlcpy(tcon->treeName, tree, sizeof(tcon->treeName));
+       if ((rsp->Capabilities & SMB2_SHARE_CAP_DFS) &&
+@@ -1913,9 +1912,8 @@ tcon_exit:
+       return rc;
+ tcon_error_exit:
+-      if (rsp && rsp->sync_hdr.Status == STATUS_BAD_NETWORK_NAME) {
++      if (rsp && rsp->hdr.Status == STATUS_BAD_NETWORK_NAME)
+               cifs_tcon_dbg(VFS, "BAD_NETWORK_NAME: %s\n", tree);
+-      }
+       goto tcon_exit;
+ }
+@@ -2649,7 +2647,7 @@ int smb311_posix_mkdir(const unsigned in
+       if (tcon->share_flags & SHI1005_FLAGS_DFS) {
+               int name_len;
+-              req->sync_hdr.Flags |= SMB2_FLAGS_DFS_OPERATIONS;
++              req->hdr.Flags |= SMB2_FLAGS_DFS_OPERATIONS;
+               rc = alloc_path_with_tree_prefix(&copy_path, &copy_size,
+                                                &name_len,
+                                                tcon->treeName, utf16_path);
+@@ -2781,7 +2779,7 @@ SMB2_open_init(struct cifs_tcon *tcon, s
+       if (tcon->share_flags & SHI1005_FLAGS_DFS) {
+               int name_len;
+-              req->sync_hdr.Flags |= SMB2_FLAGS_DFS_OPERATIONS;
++              req->hdr.Flags |= SMB2_FLAGS_DFS_OPERATIONS;
+               rc = alloc_path_with_tree_prefix(&copy_path, &copy_size,
+                                                &name_len,
+                                                tcon->treeName, path);
+@@ -2993,7 +2991,7 @@ SMB2_open(const unsigned int xid, struct
+       oparms->fid->volatile_fid = rsp->VolatileFileId;
+       oparms->fid->access = oparms->desired_access;
+ #ifdef CONFIG_CIFS_DEBUG2
+-      oparms->fid->mid = le64_to_cpu(rsp->sync_hdr.MessageId);
++      oparms->fid->mid = le64_to_cpu(rsp->hdr.MessageId);
+ #endif /* CIFS_DEBUG2 */
+       if (buf) {
+@@ -3102,7 +3100,7 @@ SMB2_ioctl_init(struct cifs_tcon *tcon,
+        * response size smaller.
+        */
+       req->MaxOutputResponse = cpu_to_le32(max_response_size);
+-      req->sync_hdr.CreditCharge =
++      req->hdr.CreditCharge =
+               cpu_to_le16(DIV_ROUND_UP(max(indatalen, max_response_size),
+                                        SMB2_MAX_BUFFER_SIZE));
+       /* always an FSCTL (for now) */
+@@ -3110,7 +3108,7 @@ SMB2_ioctl_init(struct cifs_tcon *tcon,
+       /* validate negotiate request must be signed - see MS-SMB2 3.2.5.5 */
+       if (opcode == FSCTL_VALIDATE_NEGOTIATE_INFO)
+-              req->sync_hdr.Flags |= SMB2_FLAGS_SIGNED;
++              req->hdr.Flags |= SMB2_FLAGS_SIGNED;
+       return 0;
+ }
+@@ -3740,7 +3738,7 @@ smb2_echo_callback(struct mid_q_entry *m
+       if (mid->mid_state == MID_RESPONSE_RECEIVED
+           || mid->mid_state == MID_RESPONSE_MALFORMED) {
+-              credits.value = le16_to_cpu(rsp->sync_hdr.CreditRequest);
++              credits.value = le16_to_cpu(rsp->hdr.CreditRequest);
+               credits.instance = server->reconnect_instance;
+       }
+@@ -3840,7 +3838,7 @@ SMB2_echo(struct TCP_Server_Info *server
+       if (rc)
+               return rc;
+-      req->sync_hdr.CreditRequest = cpu_to_le16(1);
++      req->hdr.CreditRequest = cpu_to_le16(1);
+       iov[0].iov_len = total_len;
+       iov[0].iov_base = (char *)req;
+@@ -3944,7 +3942,7 @@ smb2_new_read_req(void **buf, unsigned i
+ {
+       int rc = -EACCES;
+       struct smb2_read_plain_req *req = NULL;
+-      struct smb2_sync_hdr *shdr;
++      struct smb2_hdr *shdr;
+       struct TCP_Server_Info *server = io_parms->server;
+       rc = smb2_plain_req_init(SMB2_READ, io_parms->tcon, server,
+@@ -3955,8 +3953,8 @@ smb2_new_read_req(void **buf, unsigned i
+       if (server == NULL)
+               return -ECONNABORTED;
+-      shdr = &req->sync_hdr;
+-      shdr->ProcessId = cpu_to_le32(io_parms->pid);
++      shdr = &req->hdr;
++      shdr->Id.SyncId.ProcessId = cpu_to_le32(io_parms->pid);
+       req->PersistentFileId = io_parms->persistent_fid;
+       req->VolatileFileId = io_parms->volatile_fid;
+@@ -4017,8 +4015,8 @@ smb2_new_read_req(void **buf, unsigned i
+                        * Related requests use info from previous read request
+                        * in chain.
+                        */
+-                      shdr->SessionId = 0xFFFFFFFFFFFFFFFF;
+-                      shdr->TreeId = 0xFFFFFFFF;
++                      shdr->SessionId = cpu_to_le64(0xFFFFFFFFFFFFFFFF);
++                      shdr->Id.SyncId.TreeId = cpu_to_le32(0xFFFFFFFF);
+                       req->PersistentFileId = 0xFFFFFFFFFFFFFFFF;
+                       req->VolatileFileId = 0xFFFFFFFFFFFFFFFF;
+               }
+@@ -4038,8 +4036,8 @@ smb2_readv_callback(struct mid_q_entry *
+       struct cifs_readdata *rdata = mid->callback_data;
+       struct cifs_tcon *tcon = tlink_tcon(rdata->cfile->tlink);
+       struct TCP_Server_Info *server = rdata->server;
+-      struct smb2_sync_hdr *shdr =
+-                              (struct smb2_sync_hdr *)rdata->iov[0].iov_base;
++      struct smb2_hdr *shdr =
++                              (struct smb2_hdr *)rdata->iov[0].iov_base;
+       struct cifs_credits credits = { .value = 0, .instance = 0 };
+       struct smb_rqst rqst = { .rq_iov = &rdata->iov[1],
+                                .rq_nvec = 1, };
+@@ -4128,7 +4126,7 @@ smb2_async_readv(struct cifs_readdata *r
+ {
+       int rc, flags = 0;
+       char *buf;
+-      struct smb2_sync_hdr *shdr;
++      struct smb2_hdr *shdr;
+       struct cifs_io_parms io_parms;
+       struct smb_rqst rqst = { .rq_iov = rdata->iov,
+                                .rq_nvec = 1 };
+@@ -4161,7 +4159,7 @@ smb2_async_readv(struct cifs_readdata *r
+       rdata->iov[0].iov_base = buf;
+       rdata->iov[0].iov_len = total_len;
+-      shdr = (struct smb2_sync_hdr *)buf;
++      shdr = (struct smb2_hdr *)buf;
+       if (rdata->credits.value > 0) {
+               shdr->CreditCharge = cpu_to_le16(DIV_ROUND_UP(rdata->bytes,
+@@ -4294,7 +4292,7 @@ smb2_writev_callback(struct mid_q_entry
+       switch (mid->mid_state) {
+       case MID_RESPONSE_RECEIVED:
+-              credits.value = le16_to_cpu(rsp->sync_hdr.CreditRequest);
++              credits.value = le16_to_cpu(rsp->hdr.CreditRequest);
+               credits.instance = server->reconnect_instance;
+               wdata->result = smb2_check_receive(mid, server, 0);
+               if (wdata->result != 0)
+@@ -4320,7 +4318,7 @@ smb2_writev_callback(struct mid_q_entry
+               wdata->result = -EAGAIN;
+               break;
+       case MID_RESPONSE_MALFORMED:
+-              credits.value = le16_to_cpu(rsp->sync_hdr.CreditRequest);
++              credits.value = le16_to_cpu(rsp->hdr.CreditRequest);
+               credits.instance = server->reconnect_instance;
+               fallthrough;
+       default:
+@@ -4367,7 +4365,7 @@ smb2_async_writev(struct cifs_writedata
+ {
+       int rc = -EACCES, flags = 0;
+       struct smb2_write_req *req = NULL;
+-      struct smb2_sync_hdr *shdr;
++      struct smb2_hdr *shdr;
+       struct cifs_tcon *tcon = tlink_tcon(wdata->cfile->tlink);
+       struct TCP_Server_Info *server = wdata->server;
+       struct kvec iov[1];
+@@ -4385,8 +4383,8 @@ smb2_async_writev(struct cifs_writedata
+       if (smb3_encryption_required(tcon))
+               flags |= CIFS_TRANSFORM_REQ;
+-      shdr = (struct smb2_sync_hdr *)req;
+-      shdr->ProcessId = cpu_to_le32(wdata->cfile->pid);
++      shdr = (struct smb2_hdr *)req;
++      shdr->Id.SyncId.ProcessId = cpu_to_le32(wdata->cfile->pid);
+       req->PersistentFileId = wdata->cfile->fid.persistent_fid;
+       req->VolatileFileId = wdata->cfile->fid.volatile_fid;
+@@ -4537,7 +4535,7 @@ SMB2_write(const unsigned int xid, struc
+       if (smb3_encryption_required(io_parms->tcon))
+               flags |= CIFS_TRANSFORM_REQ;
+-      req->sync_hdr.ProcessId = cpu_to_le32(io_parms->pid);
++      req->hdr.Id.SyncId.ProcessId = cpu_to_le32(io_parms->pid);
+       req->PersistentFileId = io_parms->persistent_fid;
+       req->VolatileFileId = io_parms->volatile_fid;
+@@ -4922,7 +4920,7 @@ SMB2_query_directory(const unsigned int
+       if (rc) {
+               if (rc == -ENODATA &&
+-                  rsp->sync_hdr.Status == STATUS_NO_MORE_FILES) {
++                  rsp->hdr.Status == STATUS_NO_MORE_FILES) {
+                       trace_smb3_query_dir_done(xid, persistent_fid,
+                               tcon->tid, tcon->ses->Suid, index, 0);
+                       srch_inf->endOfSearch = true;
+@@ -4970,7 +4968,7 @@ SMB2_set_info_init(struct cifs_tcon *tco
+       if (rc)
+               return rc;
+-      req->sync_hdr.ProcessId = cpu_to_le32(pid);
++      req->hdr.Id.SyncId.ProcessId = cpu_to_le32(pid);
+       req->InfoType = info_type;
+       req->FileInfoClass = info_class;
+       req->PersistentFileId = persistent_fid;
+@@ -5130,7 +5128,7 @@ SMB2_oplock_break(const unsigned int xid
+       req->VolatileFid = volatile_fid;
+       req->PersistentFid = persistent_fid;
+       req->OplockLevel = oplock_level;
+-      req->sync_hdr.CreditRequest = cpu_to_le16(1);
++      req->hdr.CreditRequest = cpu_to_le16(1);
+       flags |= CIFS_NO_RSP_BUF;
+@@ -5437,7 +5435,7 @@ smb2_lockv(const unsigned int xid, struc
+       if (smb3_encryption_required(tcon))
+               flags |= CIFS_TRANSFORM_REQ;
+-      req->sync_hdr.ProcessId = cpu_to_le32(pid);
++      req->hdr.Id.SyncId.ProcessId = cpu_to_le32(pid);
+       req->LockCount = cpu_to_le16(num_lock);
+       req->PersistentFileId = persist_fid;
+@@ -5513,7 +5511,7 @@ SMB2_lease_break(const unsigned int xid,
+       if (smb3_encryption_required(tcon))
+               flags |= CIFS_TRANSFORM_REQ;
+-      req->sync_hdr.CreditRequest = cpu_to_le16(1);
++      req->hdr.CreditRequest = cpu_to_le16(1);
+       req->StructureSize = cpu_to_le16(36);
+       total_len += 12;
+--- a/fs/cifs/smb2pdu.h
++++ b/fs/cifs/smb2pdu.h
+@@ -14,156 +14,12 @@
+ #include <net/sock.h>
+ #include "cifsacl.h"
+-/*
+- * Note that, due to trying to use names similar to the protocol specifications,
+- * there are many mixed case field names in the structures below.  Although
+- * this does not match typical Linux kernel style, it is necessary to be
+- * able to match against the protocol specfication.
+- *
+- * SMB2 commands
+- * Some commands have minimal (wct=0,bcc=0), or uninteresting, responses
+- * (ie no useful data other than the SMB error code itself) and are marked such.
+- * Knowing this helps avoid response buffer allocations and copy in some cases.
+- */
+-
+-/* List of commands in host endian */
+-#define SMB2_NEGOTIATE_HE     0x0000
+-#define SMB2_SESSION_SETUP_HE 0x0001
+-#define SMB2_LOGOFF_HE                0x0002 /* trivial request/resp */
+-#define SMB2_TREE_CONNECT_HE  0x0003
+-#define SMB2_TREE_DISCONNECT_HE       0x0004 /* trivial req/resp */
+-#define SMB2_CREATE_HE                0x0005
+-#define SMB2_CLOSE_HE         0x0006
+-#define SMB2_FLUSH_HE         0x0007 /* trivial resp */
+-#define SMB2_READ_HE          0x0008
+-#define SMB2_WRITE_HE         0x0009
+-#define SMB2_LOCK_HE          0x000A
+-#define SMB2_IOCTL_HE         0x000B
+-#define SMB2_CANCEL_HE                0x000C
+-#define SMB2_ECHO_HE          0x000D
+-#define SMB2_QUERY_DIRECTORY_HE       0x000E
+-#define SMB2_CHANGE_NOTIFY_HE 0x000F
+-#define SMB2_QUERY_INFO_HE    0x0010
+-#define SMB2_SET_INFO_HE      0x0011
+-#define SMB2_OPLOCK_BREAK_HE  0x0012
+-
+-/* The same list in little endian */
+-#define SMB2_NEGOTIATE                cpu_to_le16(SMB2_NEGOTIATE_HE)
+-#define SMB2_SESSION_SETUP    cpu_to_le16(SMB2_SESSION_SETUP_HE)
+-#define SMB2_LOGOFF           cpu_to_le16(SMB2_LOGOFF_HE)
+-#define SMB2_TREE_CONNECT     cpu_to_le16(SMB2_TREE_CONNECT_HE)
+-#define SMB2_TREE_DISCONNECT  cpu_to_le16(SMB2_TREE_DISCONNECT_HE)
+-#define SMB2_CREATE           cpu_to_le16(SMB2_CREATE_HE)
+-#define SMB2_CLOSE            cpu_to_le16(SMB2_CLOSE_HE)
+-#define SMB2_FLUSH            cpu_to_le16(SMB2_FLUSH_HE)
+-#define SMB2_READ             cpu_to_le16(SMB2_READ_HE)
+-#define SMB2_WRITE            cpu_to_le16(SMB2_WRITE_HE)
+-#define SMB2_LOCK             cpu_to_le16(SMB2_LOCK_HE)
+-#define SMB2_IOCTL            cpu_to_le16(SMB2_IOCTL_HE)
+-#define SMB2_CANCEL           cpu_to_le16(SMB2_CANCEL_HE)
+-#define SMB2_ECHO             cpu_to_le16(SMB2_ECHO_HE)
+-#define SMB2_QUERY_DIRECTORY  cpu_to_le16(SMB2_QUERY_DIRECTORY_HE)
+-#define SMB2_CHANGE_NOTIFY    cpu_to_le16(SMB2_CHANGE_NOTIFY_HE)
+-#define SMB2_QUERY_INFO               cpu_to_le16(SMB2_QUERY_INFO_HE)
+-#define SMB2_SET_INFO         cpu_to_le16(SMB2_SET_INFO_HE)
+-#define SMB2_OPLOCK_BREAK     cpu_to_le16(SMB2_OPLOCK_BREAK_HE)
+-
+-#define SMB2_INTERNAL_CMD     cpu_to_le16(0xFFFF)
+-
+-#define NUMBER_OF_SMB2_COMMANDS       0x0013
+-
+ /* 52 transform hdr + 64 hdr + 88 create rsp */
+ #define SMB2_TRANSFORM_HEADER_SIZE 52
+ #define MAX_SMB2_HDR_SIZE 204
+-#define SMB2_PROTO_NUMBER cpu_to_le32(0x424d53fe)
+-#define SMB2_TRANSFORM_PROTO_NUM cpu_to_le32(0x424d53fd)
+-#define SMB2_COMPRESSION_TRANSFORM_ID cpu_to_le32(0x424d53fc)
+-
+-/*
+- * SMB2 Header Definition
+- *
+- * "MBZ" :  Must be Zero
+- * "BB"  :  BugBug, Something to check/review/analyze later
+- * "PDU" :  "Protocol Data Unit" (ie a network "frame")
+- *
+- */
+-
+-#define SMB2_HEADER_STRUCTURE_SIZE cpu_to_le16(64)
+-
+-struct smb2_sync_hdr {
+-      __le32 ProtocolId;      /* 0xFE 'S' 'M' 'B' */
+-      __le16 StructureSize;   /* 64 */
+-      __le16 CreditCharge;    /* MBZ */
+-      __le32 Status;          /* Error from server */
+-      __le16 Command;
+-      __le16 CreditRequest;  /* CreditResponse */
+-      __le32 Flags;
+-      __le32 NextCommand;
+-      __le64 MessageId;
+-      __le32 ProcessId;
+-      __u32  TreeId;          /* opaque - so do not make little endian */
+-      __u64  SessionId;       /* opaque - so do not make little endian */
+-      __u8   Signature[16];
+-} __packed;
+-
+ /* The total header size for SMB2 read and write */
+-#define SMB2_READWRITE_PDU_HEADER_SIZE (48 + sizeof(struct smb2_sync_hdr))
+-
+-struct smb2_sync_pdu {
+-      struct smb2_sync_hdr sync_hdr;
+-      __le16 StructureSize2; /* size of wct area (varies, request specific) */
+-} __packed;
+-
+-#define SMB3_AES_CCM_NONCE 11
+-#define SMB3_AES_GCM_NONCE 12
+-
+-/* Transform flags (for 3.0 dialect this flag indicates CCM */
+-#define TRANSFORM_FLAG_ENCRYPTED      0x0001
+-struct smb2_transform_hdr {
+-      __le32 ProtocolId;      /* 0xFD 'S' 'M' 'B' */
+-      __u8   Signature[16];
+-      __u8   Nonce[16];
+-      __le32 OriginalMessageSize;
+-      __u16  Reserved1;
+-      __le16 Flags; /* EncryptionAlgorithm for 3.0, enc enabled for 3.1.1 */
+-      __u64  SessionId;
+-} __packed;
+-
+-/* See MS-SMB2 2.2.42 */
+-struct smb2_compression_transform_hdr_unchained {
+-      __le32 ProtocolId;      /* 0xFC 'S' 'M' 'B' */
+-      __le32 OriginalCompressedSegmentSize;
+-      __le16 CompressionAlgorithm;
+-      __le16 Flags;
+-      __le16 Length; /* if chained it is length, else offset */
+-} __packed;
+-
+-/* See MS-SMB2 2.2.42.1 */
+-#define SMB2_COMPRESSION_FLAG_NONE    0x0000
+-#define SMB2_COMPRESSION_FLAG_CHAINED 0x0001
+-
+-struct compression_payload_header {
+-      __le16  CompressionAlgorithm;
+-      __le16  Flags;
+-      __le32  Length; /* length of compressed playload including field below if present */
+-      /* __le32 OriginalPayloadSize; */ /* optional, present when LZNT1, LZ77, LZ77+Huffman */
+-} __packed;
+-
+-/* See MS-SMB2 2.2.42.2 */
+-struct smb2_compression_transform_hdr_chained {
+-      __le32 ProtocolId;      /* 0xFC 'S' 'M' 'B' */
+-      __le32 OriginalCompressedSegmentSize;
+-      /* struct compression_payload_header[] */
+-} __packed;
+-
+-/* See MS-SMB2 2.2.42.2.2 */
+-struct compression_pattern_payload_v1 {
+-      __le16  Pattern;
+-      __le16  Reserved1;
+-      __le16  Reserved2;
+-      __le32  Repetitions;
+-} __packed;
++#define SMB2_READWRITE_PDU_HEADER_SIZE (48 + sizeof(struct smb2_hdr))
+ /* See MS-SMB2 2.2.43 */
+ struct smb2_rdma_transform {
+@@ -190,17 +46,6 @@ struct smb2_rdma_crypto_transform {
+ } __packed;
+ /*
+- *    SMB2 flag definitions
+- */
+-#define SMB2_FLAGS_SERVER_TO_REDIR    cpu_to_le32(0x00000001)
+-#define SMB2_FLAGS_ASYNC_COMMAND      cpu_to_le32(0x00000002)
+-#define SMB2_FLAGS_RELATED_OPERATIONS cpu_to_le32(0x00000004)
+-#define SMB2_FLAGS_SIGNED             cpu_to_le32(0x00000008)
+-#define SMB2_FLAGS_PRIORITY_MASK      cpu_to_le32(0x00000070) /* SMB3.1.1 */
+-#define SMB2_FLAGS_DFS_OPERATIONS     cpu_to_le32(0x10000000)
+-#define SMB2_FLAGS_REPLAY_OPERATION   cpu_to_le32(0x20000000) /* SMB3 & up */
+-
+-/*
+  *    Definitions for SMB2 Protocol Data Units (network frames)
+  *
+  *  See MS-SMB2.PDF specification for protocol details.
+@@ -214,7 +59,7 @@ struct smb2_rdma_crypto_transform {
+ #define SMB2_ERROR_STRUCTURE_SIZE2 cpu_to_le16(9)
+ struct smb2_err_rsp {
+-      struct smb2_sync_hdr sync_hdr;
++      struct smb2_hdr hdr;
+       __le16 StructureSize;
+       __le16 Reserved; /* MBZ */
+       __le32 ByteCount;  /* even if zero, at least one byte follows */
+@@ -273,7 +118,7 @@ struct share_redirect_error_context_rsp
+ #define SMB2_CLIENT_GUID_SIZE 16
+ struct smb2_negotiate_req {
+-      struct smb2_sync_hdr sync_hdr;
++      struct smb2_hdr hdr;
+       __le16 StructureSize; /* Must be 36 */
+       __le16 DialectCount;
+       __le16 SecurityMode;
+@@ -472,7 +317,7 @@ struct smb2_posix_neg_context {
+ } __packed;
+ struct smb2_negotiate_rsp {
+-      struct smb2_sync_hdr sync_hdr;
++      struct smb2_hdr hdr;
+       __le16 StructureSize;   /* Must be 65 */
+       __le16 SecurityMode;
+       __le16 DialectRevision;
+@@ -495,7 +340,7 @@ struct smb2_negotiate_rsp {
+ #define SMB2_SESSION_REQ_FLAG_ENCRYPT_DATA    0x04
+ struct smb2_sess_setup_req {
+-      struct smb2_sync_hdr sync_hdr;
++      struct smb2_hdr hdr;
+       __le16 StructureSize; /* Must be 25 */
+       __u8   Flags;
+       __u8   SecurityMode;
+@@ -512,7 +357,7 @@ struct smb2_sess_setup_req {
+ #define SMB2_SESSION_FLAG_IS_NULL     0x0002
+ #define SMB2_SESSION_FLAG_ENCRYPT_DATA        0x0004
+ struct smb2_sess_setup_rsp {
+-      struct smb2_sync_hdr sync_hdr;
++      struct smb2_hdr hdr;
+       __le16 StructureSize; /* Must be 9 */
+       __le16 SessionFlags;
+       __le16 SecurityBufferOffset;
+@@ -521,161 +366,13 @@ struct smb2_sess_setup_rsp {
+ } __packed;
+ struct smb2_logoff_req {
+-      struct smb2_sync_hdr sync_hdr;
++      struct smb2_hdr hdr;
+       __le16 StructureSize;   /* Must be 4 */
+       __le16 Reserved;
+ } __packed;
+ struct smb2_logoff_rsp {
+-      struct smb2_sync_hdr sync_hdr;
+-      __le16 StructureSize;   /* Must be 4 */
+-      __le16 Reserved;
+-} __packed;
+-
+-/* Flags/Reserved for SMB3.1.1 */
+-#define SMB2_TREE_CONNECT_FLAG_CLUSTER_RECONNECT cpu_to_le16(0x0001)
+-#define SMB2_TREE_CONNECT_FLAG_REDIRECT_TO_OWNER cpu_to_le16(0x0002)
+-#define SMB2_TREE_CONNECT_FLAG_EXTENSION_PRESENT cpu_to_le16(0x0004)
+-
+-struct smb2_tree_connect_req {
+-      struct smb2_sync_hdr sync_hdr;
+-      __le16 StructureSize;   /* Must be 9 */
+-      __le16 Flags; /* Reserved MBZ for dialects prior to SMB3.1.1 */
+-      __le16 PathOffset;
+-      __le16 PathLength;
+-      __u8   Buffer[];        /* variable length */
+-} __packed;
+-
+-/* See MS-SMB2 section 2.2.9.2 */
+-/* Context Types */
+-#define SMB2_RESERVED_TREE_CONNECT_CONTEXT_ID 0x0000
+-#define SMB2_REMOTED_IDENTITY_TREE_CONNECT_CONTEXT_ID cpu_to_le16(0x0001)
+-
+-struct tree_connect_contexts {
+-      __le16 ContextType;
+-      __le16 DataLength;
+-      __le32 Reserved;
+-      __u8   Data[];
+-} __packed;
+-
+-/* Remoted identity tree connect context structures - see MS-SMB2 2.2.9.2.1 */
+-struct smb3_blob_data {
+-      __le16 BlobSize;
+-      __u8   BlobData[];
+-} __packed;
+-
+-/* Valid values for Attr */
+-#define SE_GROUP_MANDATORY            0x00000001
+-#define SE_GROUP_ENABLED_BY_DEFAULT   0x00000002
+-#define SE_GROUP_ENABLED              0x00000004
+-#define SE_GROUP_OWNER                        0x00000008
+-#define SE_GROUP_USE_FOR_DENY_ONLY    0x00000010
+-#define SE_GROUP_INTEGRITY            0x00000020
+-#define SE_GROUP_INTEGRITY_ENABLED    0x00000040
+-#define SE_GROUP_RESOURCE             0x20000000
+-#define SE_GROUP_LOGON_ID             0xC0000000
+-
+-/* struct sid_attr_data is SidData array in BlobData format then le32 Attr */
+-
+-struct sid_array_data {
+-      __le16 SidAttrCount;
+-      /* SidAttrList - array of sid_attr_data structs */
+-} __packed;
+-
+-struct luid_attr_data {
+-
+-} __packed;
+-
+-/*
+- * struct privilege_data is the same as BLOB_DATA - see MS-SMB2 2.2.9.2.1.5
+- * but with size of LUID_ATTR_DATA struct and BlobData set to LUID_ATTR DATA
+- */
+-
+-struct privilege_array_data {
+-      __le16 PrivilegeCount;
+-      /* array of privilege_data structs */
+-} __packed;
+-
+-struct remoted_identity_tcon_context {
+-      __le16 TicketType; /* must be 0x0001 */
+-      __le16 TicketSize; /* total size of this struct */
+-      __le16 User; /* offset to SID_ATTR_DATA struct with user info */
+-      __le16 UserName; /* offset to null terminated Unicode username string */
+-      __le16 Domain; /* offset to null terminated Unicode domain name */
+-      __le16 Groups; /* offset to SID_ARRAY_DATA struct with group info */
+-      __le16 RestrictedGroups; /* similar to above */
+-      __le16 Privileges; /* offset to PRIVILEGE_ARRAY_DATA struct */
+-      __le16 PrimaryGroup; /* offset to SID_ARRAY_DATA struct */
+-      __le16 Owner; /* offset to BLOB_DATA struct */
+-      __le16 DefaultDacl; /* offset to BLOB_DATA struct */
+-      __le16 DeviceGroups; /* offset to SID_ARRAY_DATA struct */
+-      __le16 UserClaims; /* offset to BLOB_DATA struct */
+-      __le16 DeviceClaims; /* offset to BLOB_DATA struct */
+-      __u8   TicketInfo[]; /* variable length buf - remoted identity data */
+-} __packed;
+-
+-struct smb2_tree_connect_req_extension {
+-      __le32 TreeConnectContextOffset;
+-      __le16 TreeConnectContextCount;
+-      __u8  Reserved[10];
+-      __u8  PathName[]; /* variable sized array */
+-      /* followed by array of TreeConnectContexts */
+-} __packed;
+-
+-struct smb2_tree_connect_rsp {
+-      struct smb2_sync_hdr sync_hdr;
+-      __le16 StructureSize;   /* Must be 16 */
+-      __u8   ShareType;  /* see below */
+-      __u8   Reserved;
+-      __le32 ShareFlags; /* see below */
+-      __le32 Capabilities; /* see below */
+-      __le32 MaximalAccess;
+-} __packed;
+-
+-/* Possible ShareType values */
+-#define SMB2_SHARE_TYPE_DISK  0x01
+-#define SMB2_SHARE_TYPE_PIPE  0x02
+-#define       SMB2_SHARE_TYPE_PRINT   0x03
+-
+-/*
+- * Possible ShareFlags - exactly one and only one of the first 4 caching flags
+- * must be set (any of the remaining, SHI1005, flags may be set individually
+- * or in combination.
+- */
+-#define SMB2_SHAREFLAG_MANUAL_CACHING                 0x00000000
+-#define SMB2_SHAREFLAG_AUTO_CACHING                   0x00000010
+-#define SMB2_SHAREFLAG_VDO_CACHING                    0x00000020
+-#define SMB2_SHAREFLAG_NO_CACHING                     0x00000030
+-#define SHI1005_FLAGS_DFS                             0x00000001
+-#define SHI1005_FLAGS_DFS_ROOT                                0x00000002
+-#define SHI1005_FLAGS_RESTRICT_EXCLUSIVE_OPENS                0x00000100
+-#define SHI1005_FLAGS_FORCE_SHARED_DELETE             0x00000200
+-#define SHI1005_FLAGS_ALLOW_NAMESPACE_CACHING         0x00000400
+-#define SHI1005_FLAGS_ACCESS_BASED_DIRECTORY_ENUM     0x00000800
+-#define SHI1005_FLAGS_FORCE_LEVELII_OPLOCK            0x00001000
+-#define SHI1005_FLAGS_ENABLE_HASH_V1                  0x00002000
+-#define SHI1005_FLAGS_ENABLE_HASH_V2                  0x00004000
+-#define SHI1005_FLAGS_ENCRYPT_DATA                    0x00008000
+-#define SMB2_SHAREFLAG_IDENTITY_REMOTING              0x00040000 /* 3.1.1 */
+-#define SMB2_SHAREFLAG_COMPRESS_DATA                  0x00100000 /* 3.1.1 */
+-#define SHI1005_FLAGS_ALL                             0x0014FF33
+-
+-/* Possible share capabilities */
+-#define SMB2_SHARE_CAP_DFS    cpu_to_le32(0x00000008) /* all dialects */
+-#define SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY cpu_to_le32(0x00000010) /* 3.0 */
+-#define SMB2_SHARE_CAP_SCALEOUT       cpu_to_le32(0x00000020) /* 3.0 */
+-#define SMB2_SHARE_CAP_CLUSTER        cpu_to_le32(0x00000040) /* 3.0 */
+-#define SMB2_SHARE_CAP_ASYMMETRIC cpu_to_le32(0x00000080) /* 3.02 */
+-#define SMB2_SHARE_CAP_REDIRECT_TO_OWNER cpu_to_le32(0x00000100) /* 3.1.1 */
+-
+-struct smb2_tree_disconnect_req {
+-      struct smb2_sync_hdr sync_hdr;
+-      __le16 StructureSize;   /* Must be 4 */
+-      __le16 Reserved;
+-} __packed;
+-
+-struct smb2_tree_disconnect_rsp {
+-      struct smb2_sync_hdr sync_hdr;
++      struct smb2_hdr hdr;
+       __le16 StructureSize;   /* Must be 4 */
+       __le16 Reserved;
+ } __packed;
+@@ -808,7 +505,7 @@ struct smb2_tree_disconnect_rsp {
+ #define SMB2_CREATE_IOV_SIZE 8
+ struct smb2_create_req {
+-      struct smb2_sync_hdr sync_hdr;
++      struct smb2_hdr hdr;
+       __le16 StructureSize;   /* Must be 57 */
+       __u8   SecurityFlags;
+       __u8   RequestedOplockLevel;
+@@ -835,7 +532,7 @@ struct smb2_create_req {
+ #define MAX_SMB2_CREATE_RESPONSE_SIZE 880
+ struct smb2_create_rsp {
+-      struct smb2_sync_hdr sync_hdr;
++      struct smb2_hdr hdr;
+       __le16 StructureSize;   /* Must be 89 */
+       __u8   OplockLevel;
+       __u8   Flag;  /* 0x01 if reparse point */
+@@ -1210,7 +907,7 @@ struct duplicate_extents_to_file {
+ #define SMB2_IOCTL_IOV_SIZE 2
+ struct smb2_ioctl_req {
+-      struct smb2_sync_hdr sync_hdr;
++      struct smb2_hdr hdr;
+       __le16 StructureSize;   /* Must be 57 */
+       __u16 Reserved;
+       __le32 CtlCode;
+@@ -1228,7 +925,7 @@ struct smb2_ioctl_req {
+ } __packed;
+ struct smb2_ioctl_rsp {
+-      struct smb2_sync_hdr sync_hdr;
++      struct smb2_hdr hdr;
+       __le16 StructureSize;   /* Must be 57 */
+       __u16 Reserved;
+       __le32 CtlCode;
+@@ -1246,7 +943,7 @@ struct smb2_ioctl_rsp {
+ /* Currently defined values for close flags */
+ #define SMB2_CLOSE_FLAG_POSTQUERY_ATTRIB      cpu_to_le16(0x0001)
+ struct smb2_close_req {
+-      struct smb2_sync_hdr sync_hdr;
++      struct smb2_hdr hdr;
+       __le16 StructureSize;   /* Must be 24 */
+       __le16 Flags;
+       __le32 Reserved;
+@@ -1260,7 +957,7 @@ struct smb2_close_req {
+ #define MAX_SMB2_CLOSE_RESPONSE_SIZE 124
+ struct smb2_close_rsp {
+-      struct smb2_sync_hdr sync_hdr;
++      struct smb2_hdr hdr;
+       __le16 StructureSize; /* 60 */
+       __le16 Flags;
+       __le32 Reserved;
+@@ -1274,7 +971,7 @@ struct smb2_close_rsp {
+ } __packed;
+ struct smb2_flush_req {
+-      struct smb2_sync_hdr sync_hdr;
++      struct smb2_hdr hdr;
+       __le16 StructureSize;   /* Must be 24 */
+       __le16 Reserved1;
+       __le32 Reserved2;
+@@ -1283,7 +980,7 @@ struct smb2_flush_req {
+ } __packed;
+ struct smb2_flush_rsp {
+-      struct smb2_sync_hdr sync_hdr;
++      struct smb2_hdr hdr;
+       __le16 StructureSize;
+       __le16 Reserved;
+ } __packed;
+@@ -1300,7 +997,7 @@ struct smb2_flush_rsp {
+ /* SMB2 read request without RFC1001 length at the beginning */
+ struct smb2_read_plain_req {
+-      struct smb2_sync_hdr sync_hdr;
++      struct smb2_hdr hdr;
+       __le16 StructureSize; /* Must be 49 */
+       __u8   Padding; /* offset from start of SMB2 header to place read */
+       __u8   Flags; /* MBZ unless SMB3.02 or later */
+@@ -1321,7 +1018,7 @@ struct smb2_read_plain_req {
+ #define SMB2_READFLAG_RESPONSE_RDMA_TRANSFORM 0x00000001
+ struct smb2_read_rsp {
+-      struct smb2_sync_hdr sync_hdr;
++      struct smb2_hdr hdr;
+       __le16 StructureSize; /* Must be 17 */
+       __u8   DataOffset;
+       __u8   Reserved;
+@@ -1336,7 +1033,7 @@ struct smb2_read_rsp {
+ #define SMB2_WRITEFLAG_WRITE_UNBUFFERED       0x00000002      /* SMB3.02 or later */
+ struct smb2_write_req {
+-      struct smb2_sync_hdr sync_hdr;
++      struct smb2_hdr hdr;
+       __le16 StructureSize; /* Must be 49 */
+       __le16 DataOffset; /* offset from start of SMB2 header to write data */
+       __le32 Length;
+@@ -1352,7 +1049,7 @@ struct smb2_write_req {
+ } __packed;
+ struct smb2_write_rsp {
+-      struct smb2_sync_hdr sync_hdr;
++      struct smb2_hdr hdr;
+       __le16 StructureSize; /* Must be 17 */
+       __u8   DataOffset;
+       __u8   Reserved;
+@@ -1380,7 +1077,7 @@ struct smb2_write_rsp {
+ #define FILE_NOTIFY_CHANGE_STREAM_WRITE               0x00000800
+ struct smb2_change_notify_req {
+-      struct smb2_sync_hdr sync_hdr;
++      struct smb2_hdr hdr;
+       __le16  StructureSize;
+       __le16  Flags;
+       __le32  OutputBufferLength;
+@@ -1391,7 +1088,7 @@ struct smb2_change_notify_req {
+ } __packed;
+ struct smb2_change_notify_rsp {
+-      struct smb2_sync_hdr sync_hdr;
++      struct smb2_hdr hdr;
+       __le16  StructureSize;  /* Must be 9 */
+       __le16  OutputBufferOffset;
+       __le32  OutputBufferLength;
+@@ -1411,7 +1108,7 @@ struct smb2_lock_element {
+ } __packed;
+ struct smb2_lock_req {
+-      struct smb2_sync_hdr sync_hdr;
++      struct smb2_hdr hdr;
+       __le16 StructureSize; /* Must be 48 */
+       __le16 LockCount;
+       /*
+@@ -1429,19 +1126,19 @@ struct smb2_lock_req {
+ } __packed;
+ struct smb2_lock_rsp {
+-      struct smb2_sync_hdr sync_hdr;
++      struct smb2_hdr hdr;
+       __le16 StructureSize; /* Must be 4 */
+       __le16 Reserved;
+ } __packed;
+ struct smb2_echo_req {
+-      struct smb2_sync_hdr sync_hdr;
++      struct smb2_hdr hdr;
+       __le16 StructureSize;   /* Must be 4 */
+       __u16  Reserved;
+ } __packed;
+ struct smb2_echo_rsp {
+-      struct smb2_sync_hdr sync_hdr;
++      struct smb2_hdr hdr;
+       __le16 StructureSize;   /* Must be 4 */
+       __u16  Reserved;
+ } __packed;
+@@ -1471,7 +1168,7 @@ struct smb2_echo_rsp {
+  */
+ struct smb2_query_directory_req {
+-      struct smb2_sync_hdr sync_hdr;
++      struct smb2_hdr hdr;
+       __le16 StructureSize; /* Must be 33 */
+       __u8   FileInformationClass;
+       __u8   Flags;
+@@ -1485,7 +1182,7 @@ struct smb2_query_directory_req {
+ } __packed;
+ struct smb2_query_directory_rsp {
+-      struct smb2_sync_hdr sync_hdr;
++      struct smb2_hdr hdr;
+       __le16 StructureSize; /* Must be 9 */
+       __le16 OutputBufferOffset;
+       __le32 OutputBufferLength;
+@@ -1518,7 +1215,7 @@ struct smb2_query_directory_rsp {
+ #define SL_INDEX_SPECIFIED    0x00000004
+ struct smb2_query_info_req {
+-      struct smb2_sync_hdr sync_hdr;
++      struct smb2_hdr hdr;
+       __le16 StructureSize; /* Must be 41 */
+       __u8   InfoType;
+       __u8   FileInfoClass;
+@@ -1534,7 +1231,7 @@ struct smb2_query_info_req {
+ } __packed;
+ struct smb2_query_info_rsp {
+-      struct smb2_sync_hdr sync_hdr;
++      struct smb2_hdr hdr;
+       __le16 StructureSize; /* Must be 9 */
+       __le16 OutputBufferOffset;
+       __le32 OutputBufferLength;
+@@ -1551,7 +1248,7 @@ struct smb2_query_info_rsp {
+ #define SMB2_SET_INFO_IOV_SIZE 3
+ struct smb2_set_info_req {
+-      struct smb2_sync_hdr sync_hdr;
++      struct smb2_hdr hdr;
+       __le16 StructureSize; /* Must be 33 */
+       __u8   InfoType;
+       __u8   FileInfoClass;
+@@ -1565,12 +1262,12 @@ struct smb2_set_info_req {
+ } __packed;
+ struct smb2_set_info_rsp {
+-      struct smb2_sync_hdr sync_hdr;
++      struct smb2_hdr hdr;
+       __le16 StructureSize; /* Must be 2 */
+ } __packed;
+ struct smb2_oplock_break {
+-      struct smb2_sync_hdr sync_hdr;
++      struct smb2_hdr hdr;
+       __le16 StructureSize; /* Must be 24 */
+       __u8   OplockLevel;
+       __u8   Reserved;
+@@ -1582,7 +1279,7 @@ struct smb2_oplock_break {
+ #define SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED cpu_to_le32(0x01)
+ struct smb2_lease_break {
+-      struct smb2_sync_hdr sync_hdr;
++      struct smb2_hdr hdr;
+       __le16 StructureSize; /* Must be 44 */
+       __le16 Epoch;
+       __le32 Flags;
+@@ -1595,7 +1292,7 @@ struct smb2_lease_break {
+ } __packed;
+ struct smb2_lease_ack {
+-      struct smb2_sync_hdr sync_hdr;
++      struct smb2_hdr hdr;
+       __le16 StructureSize; /* Must be 36 */
+       __le16 Reserved;
+       __le32 Flags;
+--- a/fs/cifs/smb2proto.h
++++ b/fs/cifs/smb2proto.h
+@@ -25,7 +25,7 @@ extern int smb2_check_message(char *buf,
+                             struct TCP_Server_Info *server);
+ extern unsigned int smb2_calc_size(void *buf, struct TCP_Server_Info *server);
+ extern char *smb2_get_data_area_len(int *off, int *len,
+-                                  struct smb2_sync_hdr *shdr);
++                                  struct smb2_hdr *shdr);
+ extern __le16 *cifs_convert_path_to_utf16(const char *from,
+                                         struct cifs_sb_info *cifs_sb);
+--- a/fs/cifs/smb2transport.c
++++ b/fs/cifs/smb2transport.c
+@@ -19,7 +19,6 @@
+ #include <linux/mempool.h>
+ #include <linux/highmem.h>
+ #include <crypto/aead.h>
+-#include "smb2pdu.h"
+ #include "cifsglob.h"
+ #include "cifsproto.h"
+ #include "smb2proto.h"
+@@ -213,14 +212,14 @@ smb2_calc_signature(struct smb_rqst *rqs
+       unsigned char smb2_signature[SMB2_HMACSHA256_SIZE];
+       unsigned char *sigptr = smb2_signature;
+       struct kvec *iov = rqst->rq_iov;
+-      struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)iov[0].iov_base;
++      struct smb2_hdr *shdr = (struct smb2_hdr *)iov[0].iov_base;
+       struct cifs_ses *ses;
+       struct shash_desc *shash;
+       struct crypto_shash *hash;
+       struct sdesc *sdesc = NULL;
+       struct smb_rqst drqst;
+-      ses = smb2_find_smb_ses(server, shdr->SessionId);
++      ses = smb2_find_smb_ses(server, le64_to_cpu(shdr->SessionId));
+       if (unlikely(!ses)) {
+               cifs_server_dbg(VFS, "%s: Could not find session\n", __func__);
+               return -ENOENT;
+@@ -549,14 +548,14 @@ smb3_calc_signature(struct smb_rqst *rqs
+       unsigned char smb3_signature[SMB2_CMACAES_SIZE];
+       unsigned char *sigptr = smb3_signature;
+       struct kvec *iov = rqst->rq_iov;
+-      struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)iov[0].iov_base;
++      struct smb2_hdr *shdr = (struct smb2_hdr *)iov[0].iov_base;
+       struct shash_desc *shash;
+       struct crypto_shash *hash;
+       struct sdesc *sdesc = NULL;
+       struct smb_rqst drqst;
+       u8 key[SMB3_SIGN_KEY_SIZE];
+-      rc = smb2_get_sign_key(shdr->SessionId, server, key);
++      rc = smb2_get_sign_key(le64_to_cpu(shdr->SessionId), server, key);
+       if (unlikely(rc)) {
+               cifs_server_dbg(VFS, "%s: Could not get signing key\n", __func__);
+               return rc;
+@@ -628,12 +627,12 @@ static int
+ smb2_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server)
+ {
+       int rc = 0;
+-      struct smb2_sync_hdr *shdr;
++      struct smb2_hdr *shdr;
+       struct smb2_sess_setup_req *ssr;
+       bool is_binding;
+       bool is_signed;
+-      shdr = (struct smb2_sync_hdr *)rqst->rq_iov[0].iov_base;
++      shdr = (struct smb2_hdr *)rqst->rq_iov[0].iov_base;
+       ssr = (struct smb2_sess_setup_req *)shdr;
+       is_binding = shdr->Command == SMB2_SESSION_SETUP &&
+@@ -659,8 +658,8 @@ smb2_verify_signature(struct smb_rqst *r
+ {
+       unsigned int rc;
+       char server_response_sig[SMB2_SIGNATURE_SIZE];
+-      struct smb2_sync_hdr *shdr =
+-                      (struct smb2_sync_hdr *)rqst->rq_iov[0].iov_base;
++      struct smb2_hdr *shdr =
++                      (struct smb2_hdr *)rqst->rq_iov[0].iov_base;
+       if ((shdr->Command == SMB2_NEGOTIATE) ||
+           (shdr->Command == SMB2_SESSION_SETUP) ||
+@@ -706,7 +705,7 @@ smb2_verify_signature(struct smb_rqst *r
+  */
+ static inline void
+ smb2_seq_num_into_buf(struct TCP_Server_Info *server,
+-                    struct smb2_sync_hdr *shdr)
++                    struct smb2_hdr *shdr)
+ {
+       unsigned int i, num = le16_to_cpu(shdr->CreditCharge);
+@@ -717,7 +716,7 @@ smb2_seq_num_into_buf(struct TCP_Server_
+ }
+ static struct mid_q_entry *
+-smb2_mid_entry_alloc(const struct smb2_sync_hdr *shdr,
++smb2_mid_entry_alloc(const struct smb2_hdr *shdr,
+                    struct TCP_Server_Info *server)
+ {
+       struct mid_q_entry *temp;
+@@ -749,14 +748,15 @@ smb2_mid_entry_alloc(const struct smb2_s
+       atomic_inc(&midCount);
+       temp->mid_state = MID_REQUEST_ALLOCATED;
+-      trace_smb3_cmd_enter(shdr->TreeId, shdr->SessionId,
+-              le16_to_cpu(shdr->Command), temp->mid);
++      trace_smb3_cmd_enter(le32_to_cpu(shdr->Id.SyncId.TreeId),
++                           le64_to_cpu(shdr->SessionId),
++                           le16_to_cpu(shdr->Command), temp->mid);
+       return temp;
+ }
+ static int
+ smb2_get_mid_entry(struct cifs_ses *ses, struct TCP_Server_Info *server,
+-                 struct smb2_sync_hdr *shdr, struct mid_q_entry **mid)
++                 struct smb2_hdr *shdr, struct mid_q_entry **mid)
+ {
+       if (server->tcpStatus == CifsExiting)
+               return -ENOENT;
+@@ -824,8 +824,8 @@ smb2_setup_request(struct cifs_ses *ses,
+                  struct smb_rqst *rqst)
+ {
+       int rc;
+-      struct smb2_sync_hdr *shdr =
+-                      (struct smb2_sync_hdr *)rqst->rq_iov[0].iov_base;
++      struct smb2_hdr *shdr =
++                      (struct smb2_hdr *)rqst->rq_iov[0].iov_base;
+       struct mid_q_entry *mid;
+       smb2_seq_num_into_buf(server, shdr);
+@@ -850,8 +850,8 @@ struct mid_q_entry *
+ smb2_setup_async_request(struct TCP_Server_Info *server, struct smb_rqst *rqst)
+ {
+       int rc;
+-      struct smb2_sync_hdr *shdr =
+-                      (struct smb2_sync_hdr *)rqst->rq_iov[0].iov_base;
++      struct smb2_hdr *shdr =
++                      (struct smb2_hdr *)rqst->rq_iov[0].iov_base;
+       struct mid_q_entry *mid;
+       if (server->tcpStatus == CifsNeedNegotiate &&
+--- /dev/null
++++ b/fs/smbfs_common/smb2pdu.h
+@@ -0,0 +1,318 @@
++/* SPDX-License-Identifier: LGPL-2.1 */
++#ifndef _COMMON_SMB2PDU_H
++#define _COMMON_SMB2PDU_H
++
++/*
++ * Note that, due to trying to use names similar to the protocol specifications,
++ * there are many mixed case field names in the structures below.  Although
++ * this does not match typical Linux kernel style, it is necessary to be
++ * able to match against the protocol specfication.
++ *
++ * SMB2 commands
++ * Some commands have minimal (wct=0,bcc=0), or uninteresting, responses
++ * (ie no useful data other than the SMB error code itself) and are marked such.
++ * Knowing this helps avoid response buffer allocations and copy in some cases.
++ */
++
++/* List of commands in host endian */
++#define SMB2_NEGOTIATE_HE     0x0000
++#define SMB2_SESSION_SETUP_HE 0x0001
++#define SMB2_LOGOFF_HE                0x0002 /* trivial request/resp */
++#define SMB2_TREE_CONNECT_HE  0x0003
++#define SMB2_TREE_DISCONNECT_HE       0x0004 /* trivial req/resp */
++#define SMB2_CREATE_HE                0x0005
++#define SMB2_CLOSE_HE         0x0006
++#define SMB2_FLUSH_HE         0x0007 /* trivial resp */
++#define SMB2_READ_HE          0x0008
++#define SMB2_WRITE_HE         0x0009
++#define SMB2_LOCK_HE          0x000A
++#define SMB2_IOCTL_HE         0x000B
++#define SMB2_CANCEL_HE                0x000C
++#define SMB2_ECHO_HE          0x000D
++#define SMB2_QUERY_DIRECTORY_HE       0x000E
++#define SMB2_CHANGE_NOTIFY_HE 0x000F
++#define SMB2_QUERY_INFO_HE    0x0010
++#define SMB2_SET_INFO_HE      0x0011
++#define SMB2_OPLOCK_BREAK_HE  0x0012
++
++/* The same list in little endian */
++#define SMB2_NEGOTIATE                cpu_to_le16(SMB2_NEGOTIATE_HE)
++#define SMB2_SESSION_SETUP    cpu_to_le16(SMB2_SESSION_SETUP_HE)
++#define SMB2_LOGOFF           cpu_to_le16(SMB2_LOGOFF_HE)
++#define SMB2_TREE_CONNECT     cpu_to_le16(SMB2_TREE_CONNECT_HE)
++#define SMB2_TREE_DISCONNECT  cpu_to_le16(SMB2_TREE_DISCONNECT_HE)
++#define SMB2_CREATE           cpu_to_le16(SMB2_CREATE_HE)
++#define SMB2_CLOSE            cpu_to_le16(SMB2_CLOSE_HE)
++#define SMB2_FLUSH            cpu_to_le16(SMB2_FLUSH_HE)
++#define SMB2_READ             cpu_to_le16(SMB2_READ_HE)
++#define SMB2_WRITE            cpu_to_le16(SMB2_WRITE_HE)
++#define SMB2_LOCK             cpu_to_le16(SMB2_LOCK_HE)
++#define SMB2_IOCTL            cpu_to_le16(SMB2_IOCTL_HE)
++#define SMB2_CANCEL           cpu_to_le16(SMB2_CANCEL_HE)
++#define SMB2_ECHO             cpu_to_le16(SMB2_ECHO_HE)
++#define SMB2_QUERY_DIRECTORY  cpu_to_le16(SMB2_QUERY_DIRECTORY_HE)
++#define SMB2_CHANGE_NOTIFY    cpu_to_le16(SMB2_CHANGE_NOTIFY_HE)
++#define SMB2_QUERY_INFO               cpu_to_le16(SMB2_QUERY_INFO_HE)
++#define SMB2_SET_INFO         cpu_to_le16(SMB2_SET_INFO_HE)
++#define SMB2_OPLOCK_BREAK     cpu_to_le16(SMB2_OPLOCK_BREAK_HE)
++
++#define SMB2_INTERNAL_CMD     cpu_to_le16(0xFFFF)
++
++#define NUMBER_OF_SMB2_COMMANDS       0x0013
++
++/*
++ * SMB2 Header Definition
++ *
++ * "MBZ" :  Must be Zero
++ * "BB"  :  BugBug, Something to check/review/analyze later
++ * "PDU" :  "Protocol Data Unit" (ie a network "frame")
++ *
++ */
++
++#define __SMB2_HEADER_STRUCTURE_SIZE  64
++#define SMB2_HEADER_STRUCTURE_SIZE                            \
++      cpu_to_le16(__SMB2_HEADER_STRUCTURE_SIZE)
++
++#define SMB2_PROTO_NUMBER cpu_to_le32(0x424d53fe)
++#define SMB2_TRANSFORM_PROTO_NUM cpu_to_le32(0x424d53fd)
++#define SMB2_COMPRESSION_TRANSFORM_ID cpu_to_le32(0x424d53fc)
++
++/*
++ *    SMB2 flag definitions
++ */
++#define SMB2_FLAGS_SERVER_TO_REDIR    cpu_to_le32(0x00000001)
++#define SMB2_FLAGS_ASYNC_COMMAND      cpu_to_le32(0x00000002)
++#define SMB2_FLAGS_RELATED_OPERATIONS cpu_to_le32(0x00000004)
++#define SMB2_FLAGS_SIGNED             cpu_to_le32(0x00000008)
++#define SMB2_FLAGS_PRIORITY_MASK      cpu_to_le32(0x00000070) /* SMB3.1.1 */
++#define SMB2_FLAGS_DFS_OPERATIONS     cpu_to_le32(0x10000000)
++#define SMB2_FLAGS_REPLAY_OPERATION   cpu_to_le32(0x20000000) /* SMB3 & up */
++
++/* See MS-SMB2 section 2.2.1 */
++struct smb2_hdr {
++      __le32 ProtocolId;      /* 0xFE 'S' 'M' 'B' */
++      __le16 StructureSize;   /* 64 */
++      __le16 CreditCharge;    /* MBZ */
++      __le32 Status;          /* Error from server */
++      __le16 Command;
++      __le16 CreditRequest;   /* CreditResponse */
++      __le32 Flags;
++      __le32 NextCommand;
++      __le64 MessageId;
++      union {
++              struct {
++                      __le32 ProcessId;
++                      __le32  TreeId;
++              } __packed SyncId;
++              __le64  AsyncId;
++      } __packed Id;
++      __le64  SessionId;
++      __u8   Signature[16];
++} __packed;
++
++struct smb2_pdu {
++      struct smb2_hdr hdr;
++      __le16 StructureSize2; /* size of wct area (varies, request specific) */
++} __packed;
++
++#define SMB3_AES_CCM_NONCE 11
++#define SMB3_AES_GCM_NONCE 12
++
++/* Transform flags (for 3.0 dialect this flag indicates CCM */
++#define TRANSFORM_FLAG_ENCRYPTED      0x0001
++struct smb2_transform_hdr {
++      __le32 ProtocolId;      /* 0xFD 'S' 'M' 'B' */
++      __u8   Signature[16];
++      __u8   Nonce[16];
++      __le32 OriginalMessageSize;
++      __u16  Reserved1;
++      __le16 Flags; /* EncryptionAlgorithm for 3.0, enc enabled for 3.1.1 */
++      __le64  SessionId;
++} __packed;
++
++
++/* See MS-SMB2 2.2.42 */
++struct smb2_compression_transform_hdr_unchained {
++      __le32 ProtocolId;      /* 0xFC 'S' 'M' 'B' */
++      __le32 OriginalCompressedSegmentSize;
++      __le16 CompressionAlgorithm;
++      __le16 Flags;
++      __le16 Length; /* if chained it is length, else offset */
++} __packed;
++
++/* See MS-SMB2 2.2.42.1 */
++#define SMB2_COMPRESSION_FLAG_NONE    0x0000
++#define SMB2_COMPRESSION_FLAG_CHAINED 0x0001
++
++struct compression_payload_header {
++      __le16  CompressionAlgorithm;
++      __le16  Flags;
++      __le32  Length; /* length of compressed playload including field below if present */
++      /* __le32 OriginalPayloadSize; */ /* optional, present when LZNT1, LZ77, LZ77+Huffman */
++} __packed;
++
++/* See MS-SMB2 2.2.42.2 */
++struct smb2_compression_transform_hdr_chained {
++      __le32 ProtocolId;      /* 0xFC 'S' 'M' 'B' */
++      __le32 OriginalCompressedSegmentSize;
++      /* struct compression_payload_header[] */
++} __packed;
++
++/* See MS-SMB2 2.2.42.2.2 */
++struct compression_pattern_payload_v1 {
++      __le16  Pattern;
++      __le16  Reserved1;
++      __le16  Reserved2;
++      __le32  Repetitions;
++} __packed;
++
++/* See MS-SMB2 section 2.2.9.2 */
++/* Context Types */
++#define SMB2_RESERVED_TREE_CONNECT_CONTEXT_ID 0x0000
++#define SMB2_REMOTED_IDENTITY_TREE_CONNECT_CONTEXT_ID cpu_to_le16(0x0001)
++
++struct tree_connect_contexts {
++      __le16 ContextType;
++      __le16 DataLength;
++      __le32 Reserved;
++      __u8   Data[];
++} __packed;
++
++/* Remoted identity tree connect context structures - see MS-SMB2 2.2.9.2.1 */
++struct smb3_blob_data {
++      __le16 BlobSize;
++      __u8   BlobData[];
++} __packed;
++
++/* Valid values for Attr */
++#define SE_GROUP_MANDATORY            0x00000001
++#define SE_GROUP_ENABLED_BY_DEFAULT   0x00000002
++#define SE_GROUP_ENABLED              0x00000004
++#define SE_GROUP_OWNER                        0x00000008
++#define SE_GROUP_USE_FOR_DENY_ONLY    0x00000010
++#define SE_GROUP_INTEGRITY            0x00000020
++#define SE_GROUP_INTEGRITY_ENABLED    0x00000040
++#define SE_GROUP_RESOURCE             0x20000000
++#define SE_GROUP_LOGON_ID             0xC0000000
++
++/* struct sid_attr_data is SidData array in BlobData format then le32 Attr */
++
++struct sid_array_data {
++      __le16 SidAttrCount;
++      /* SidAttrList - array of sid_attr_data structs */
++} __packed;
++
++struct luid_attr_data {
++
++} __packed;
++
++/*
++ * struct privilege_data is the same as BLOB_DATA - see MS-SMB2 2.2.9.2.1.5
++ * but with size of LUID_ATTR_DATA struct and BlobData set to LUID_ATTR DATA
++ */
++
++struct privilege_array_data {
++      __le16 PrivilegeCount;
++      /* array of privilege_data structs */
++} __packed;
++
++struct remoted_identity_tcon_context {
++      __le16 TicketType; /* must be 0x0001 */
++      __le16 TicketSize; /* total size of this struct */
++      __le16 User; /* offset to SID_ATTR_DATA struct with user info */
++      __le16 UserName; /* offset to null terminated Unicode username string */
++      __le16 Domain; /* offset to null terminated Unicode domain name */
++      __le16 Groups; /* offset to SID_ARRAY_DATA struct with group info */
++      __le16 RestrictedGroups; /* similar to above */
++      __le16 Privileges; /* offset to PRIVILEGE_ARRAY_DATA struct */
++      __le16 PrimaryGroup; /* offset to SID_ARRAY_DATA struct */
++      __le16 Owner; /* offset to BLOB_DATA struct */
++      __le16 DefaultDacl; /* offset to BLOB_DATA struct */
++      __le16 DeviceGroups; /* offset to SID_ARRAY_DATA struct */
++      __le16 UserClaims; /* offset to BLOB_DATA struct */
++      __le16 DeviceClaims; /* offset to BLOB_DATA struct */
++      __u8   TicketInfo[]; /* variable length buf - remoted identity data */
++} __packed;
++
++struct smb2_tree_connect_req_extension {
++      __le32 TreeConnectContextOffset;
++      __le16 TreeConnectContextCount;
++      __u8  Reserved[10];
++      __u8  PathName[]; /* variable sized array */
++      /* followed by array of TreeConnectContexts */
++} __packed;
++
++/* Flags/Reserved for SMB3.1.1 */
++#define SMB2_TREE_CONNECT_FLAG_CLUSTER_RECONNECT cpu_to_le16(0x0001)
++#define SMB2_TREE_CONNECT_FLAG_REDIRECT_TO_OWNER cpu_to_le16(0x0002)
++#define SMB2_TREE_CONNECT_FLAG_EXTENSION_PRESENT cpu_to_le16(0x0004)
++
++struct smb2_tree_connect_req {
++      struct smb2_hdr hdr;
++      __le16 StructureSize;   /* Must be 9 */
++      __le16 Flags;           /* Flags in SMB3.1.1 */
++      __le16 PathOffset;
++      __le16 PathLength;
++      __u8   Buffer[];        /* variable length */
++} __packed;
++
++/* Possible ShareType values */
++#define SMB2_SHARE_TYPE_DISK  0x01
++#define SMB2_SHARE_TYPE_PIPE  0x02
++#define       SMB2_SHARE_TYPE_PRINT   0x03
++
++/*
++ * Possible ShareFlags - exactly one and only one of the first 4 caching flags
++ * must be set (any of the remaining, SHI1005, flags may be set individually
++ * or in combination.
++ */
++#define SMB2_SHAREFLAG_MANUAL_CACHING                 0x00000000
++#define SMB2_SHAREFLAG_AUTO_CACHING                   0x00000010
++#define SMB2_SHAREFLAG_VDO_CACHING                    0x00000020
++#define SMB2_SHAREFLAG_NO_CACHING                     0x00000030
++#define SHI1005_FLAGS_DFS                             0x00000001
++#define SHI1005_FLAGS_DFS_ROOT                                0x00000002
++#define SHI1005_FLAGS_RESTRICT_EXCLUSIVE_OPENS                0x00000100
++#define SHI1005_FLAGS_FORCE_SHARED_DELETE             0x00000200
++#define SHI1005_FLAGS_ALLOW_NAMESPACE_CACHING         0x00000400
++#define SHI1005_FLAGS_ACCESS_BASED_DIRECTORY_ENUM     0x00000800
++#define SHI1005_FLAGS_FORCE_LEVELII_OPLOCK            0x00001000
++#define SHI1005_FLAGS_ENABLE_HASH_V1                  0x00002000
++#define SHI1005_FLAGS_ENABLE_HASH_V2                  0x00004000
++#define SHI1005_FLAGS_ENCRYPT_DATA                    0x00008000
++#define SMB2_SHAREFLAG_IDENTITY_REMOTING              0x00040000 /* 3.1.1 */
++#define SMB2_SHAREFLAG_COMPRESS_DATA                  0x00100000 /* 3.1.1 */
++#define SHI1005_FLAGS_ALL                             0x0014FF33
++
++/* Possible share capabilities */
++#define SMB2_SHARE_CAP_DFS    cpu_to_le32(0x00000008) /* all dialects */
++#define SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY cpu_to_le32(0x00000010) /* 3.0 */
++#define SMB2_SHARE_CAP_SCALEOUT       cpu_to_le32(0x00000020) /* 3.0 */
++#define SMB2_SHARE_CAP_CLUSTER        cpu_to_le32(0x00000040) /* 3.0 */
++#define SMB2_SHARE_CAP_ASYMMETRIC cpu_to_le32(0x00000080) /* 3.02 */
++#define SMB2_SHARE_CAP_REDIRECT_TO_OWNER cpu_to_le32(0x00000100) /* 3.1.1 */
++
++struct smb2_tree_connect_rsp {
++      struct smb2_hdr hdr;
++      __le16 StructureSize;   /* Must be 16 */
++      __u8   ShareType;       /* see below */
++      __u8   Reserved;
++      __le32 ShareFlags;      /* see below */
++      __le32 Capabilities;    /* see below */
++      __le32 MaximalAccess;
++} __packed;
++
++struct smb2_tree_disconnect_req {
++      struct smb2_hdr hdr;
++      __le16 StructureSize;   /* Must be 4 */
++      __le16 Reserved;
++} __packed;
++
++struct smb2_tree_disconnect_rsp {
++      struct smb2_hdr hdr;
++      __le16 StructureSize;   /* Must be 4 */
++      __le16 Reserved;
++} __packed;
++
++
++#endif                                /* _COMMON_SMB2PDU_H */
diff --git a/queue-5.15/cifs-remove-check-of-list-iterator-against-head-past-the-loop-body.patch b/queue-5.15/cifs-remove-check-of-list-iterator-against-head-past-the-loop-body.patch
new file mode 100644 (file)
index 0000000..239ec25
--- /dev/null
@@ -0,0 +1,60 @@
+From stable+bounces-274533-greg=kroah.com@vger.kernel.org Tue Jul 14 22:03:04 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 14 Jul 2026 16:02:12 -0400
+Subject: cifs: remove check of list iterator against head past the loop body
+To: stable@vger.kernel.org
+Cc: Jakob Koschel <jakobkoschel@gmail.com>, "Paulo Alcantara (SUSE)" <pc@cjr.nz>, Steve French <stfrench@microsoft.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260714200215.3152449-2-sashal@kernel.org>
+
+From: Jakob Koschel <jakobkoschel@gmail.com>
+
+[ Upstream commit 00c796eecba4898194ea549679797ee28f89a92f ]
+
+When list_for_each_entry() completes the iteration over the whole list
+without breaking the loop, the iterator value will be a bogus pointer
+computed based on the head element.
+
+While it is safe to use the pointer to determine if it was computed
+based on the head element, either with list_entry_is_head() or
+&pos->member == head, using the iterator variable after the loop should
+be avoided.
+
+In preparation to limit the scope of a list iterator to the list
+traversal loop, use a dedicated pointer to point to the found element [1].
+
+Link: https://lore.kernel.org/all/CAHk-=wgRr_D8CB-D9Kg-c=EHreAsk5SqXPwr9Y7k9sA6cWXJ6w@mail.gmail.com/ [1]
+Reviewed-by: Paulo Alcantara (SUSE) <pc@cjr.nz>
+Signed-off-by: Jakob Koschel <jakobkoschel@gmail.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Stable-dep-of: 53b7c271f06b ("smb: client: restrict implied bcc[0] exemption to responses without data area")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/cifs/smb2misc.c |   10 ++++++----
+ 1 file changed, 6 insertions(+), 4 deletions(-)
+
+--- a/fs/cifs/smb2misc.c
++++ b/fs/cifs/smb2misc.c
+@@ -150,16 +150,18 @@ smb2_check_message(char *buf, unsigned i
+               struct smb2_transform_hdr *thdr =
+                       (struct smb2_transform_hdr *)buf;
+               struct cifs_ses *ses = NULL;
++              struct cifs_ses *iter;
+               /* decrypt frame now that it is completely read in */
+               spin_lock(&cifs_tcp_ses_lock);
+-              list_for_each_entry(ses, &srvr->smb_ses_list, smb_ses_list) {
+-                      if (ses->Suid == le64_to_cpu(thdr->SessionId))
++              list_for_each_entry(iter, &srvr->smb_ses_list, smb_ses_list) {
++                      if (iter->Suid == le64_to_cpu(thdr->SessionId)) {
++                              ses = iter;
+                               break;
++                      }
+               }
+               spin_unlock(&cifs_tcp_ses_lock);
+-              if (list_entry_is_head(ses, &srvr->smb_ses_list,
+-                                     smb_ses_list)) {
++              if (!ses) {
+                       cifs_dbg(VFS, "no decryption - session id not found\n");
+                       return 1;
+               }
diff --git a/queue-5.15/cifs-remove-unused-server-parameter-from-calc_smb_size.patch b/queue-5.15/cifs-remove-unused-server-parameter-from-calc_smb_size.patch
new file mode 100644 (file)
index 0000000..80b5aea
--- /dev/null
@@ -0,0 +1,151 @@
+From stable+bounces-274535-greg=kroah.com@vger.kernel.org Tue Jul 14 22:03:03 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 14 Jul 2026 16:02:14 -0400
+Subject: cifs: remove unused server parameter from calc_smb_size()
+To: stable@vger.kernel.org
+Cc: Enzo Matsumiya <ematsumiya@suse.de>, "Paulo Alcantara (SUSE)" <pc@cjr.nz>, Steve French <stfrench@microsoft.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260714200215.3152449-4-sashal@kernel.org>
+
+From: Enzo Matsumiya <ematsumiya@suse.de>
+
+[ Upstream commit 68ed14496b032b0c9ef21b38ee45c6c8f3a18ff1 ]
+
+This parameter is unused by the called function
+
+Reviewed-by: Paulo Alcantara (SUSE) <pc@cjr.nz>
+Signed-off-by: Enzo Matsumiya <ematsumiya@suse.de>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Stable-dep-of: 53b7c271f06b ("smb: client: restrict implied bcc[0] exemption to responses without data area")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/cifs/cifs_debug.c |    2 +-
+ fs/cifs/cifsglob.h   |    2 +-
+ fs/cifs/cifsproto.h  |    2 +-
+ fs/cifs/misc.c       |    2 +-
+ fs/cifs/netmisc.c    |    2 +-
+ fs/cifs/readdir.c    |    6 ++----
+ fs/cifs/smb2misc.c   |    4 ++--
+ fs/cifs/smb2ops.c    |    2 +-
+ fs/cifs/smb2proto.h  |    2 +-
+ 9 files changed, 11 insertions(+), 13 deletions(-)
+
+--- a/fs/cifs/cifs_debug.c
++++ b/fs/cifs/cifs_debug.c
+@@ -42,7 +42,7 @@ void cifs_dump_detail(void *buf, struct
+                smb->Command, smb->Status.CifsError,
+                smb->Flags, smb->Flags2, smb->Mid, smb->Pid);
+       cifs_dbg(VFS, "smb buf %p len %u\n", smb,
+-               server->ops->calc_smb_size(smb, server));
++               server->ops->calc_smb_size(smb));
+ #endif /* CONFIG_CIFS_DEBUG2 */
+ }
+--- a/fs/cifs/cifsglob.h
++++ b/fs/cifs/cifsglob.h
+@@ -389,7 +389,7 @@ struct smb_version_operations {
+       int (*close_dir)(const unsigned int, struct cifs_tcon *,
+                        struct cifs_fid *);
+       /* calculate a size of SMB message */
+-      unsigned int (*calc_smb_size)(void *buf, struct TCP_Server_Info *ptcpi);
++      unsigned int (*calc_smb_size)(void *buf);
+       /* check for STATUS_PENDING and process the response if yes */
+       bool (*is_status_pending)(char *buf, struct TCP_Server_Info *server);
+       /* check for STATUS_NETWORK_SESSION_EXPIRED */
+--- a/fs/cifs/cifsproto.h
++++ b/fs/cifs/cifsproto.h
+@@ -148,7 +148,7 @@ extern int cifs_get_writable_path(struct
+ extern struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *, bool);
+ extern int cifs_get_readable_path(struct cifs_tcon *tcon, const char *name,
+                                 struct cifsFileInfo **ret_file);
+-extern unsigned int smbCalcSize(void *buf, struct TCP_Server_Info *server);
++extern unsigned int smbCalcSize(void *buf);
+ extern int decode_negTokenInit(unsigned char *security_blob, int length,
+                       struct TCP_Server_Info *server);
+ extern int cifs_convert_address(struct sockaddr *dst, const char *src, int len);
+--- a/fs/cifs/misc.c
++++ b/fs/cifs/misc.c
+@@ -357,7 +357,7 @@ checkSMB(char *buf, unsigned int total_r
+       /* otherwise, there is enough to get to the BCC */
+       if (check_smb_hdr(smb))
+               return -EIO;
+-      clc_len = smbCalcSize(smb, server);
++      clc_len = smbCalcSize(smb);
+       if (4 + rfclen != total_read) {
+               cifs_dbg(VFS, "Length read does not match RFC1001 length %d\n",
+--- a/fs/cifs/netmisc.c
++++ b/fs/cifs/netmisc.c
+@@ -912,7 +912,7 @@ map_and_check_smb_error(struct mid_q_ent
+  * portion, the number of word parameters and the data portion of the message
+  */
+ unsigned int
+-smbCalcSize(void *buf, struct TCP_Server_Info *server)
++smbCalcSize(void *buf)
+ {
+       struct smb_hdr *ptr = (struct smb_hdr *)buf;
+       return (sizeof(struct smb_hdr) + (2 * ptr->WordCount) +
+--- a/fs/cifs/readdir.c
++++ b/fs/cifs/readdir.c
+@@ -808,8 +808,7 @@ find_cifs_entry(const unsigned int xid,
+               end_of_smb = cfile->srch_inf.ntwrk_buf_start +
+                       server->ops->calc_smb_size(
+-                                      cfile->srch_inf.ntwrk_buf_start,
+-                                      server);
++                                      cfile->srch_inf.ntwrk_buf_start);
+               cur_ent = cfile->srch_inf.srch_entries_start;
+               first_entry_in_buffer = cfile->srch_inf.index_of_last_entry
+@@ -1008,8 +1007,7 @@ int cifs_readdir(struct file *file, stru
+       cifs_dbg(FYI, "loop through %d times filling dir for net buf %p\n",
+                num_to_fill, cifsFile->srch_inf.ntwrk_buf_start);
+       max_len = tcon->ses->server->ops->calc_smb_size(
+-                      cifsFile->srch_inf.ntwrk_buf_start,
+-                      tcon->ses->server);
++                      cifsFile->srch_inf.ntwrk_buf_start);
+       end_of_smb = cifsFile->srch_inf.ntwrk_buf_start + max_len;
+       tmp_buf = kmalloc(UNICODE_NAME_MAX, GFP_KERNEL);
+--- a/fs/cifs/smb2misc.c
++++ b/fs/cifs/smb2misc.c
+@@ -221,7 +221,7 @@ smb2_check_message(char *buf, unsigned i
+               }
+       }
+-      calc_len = smb2_calc_size(buf, server);
++      calc_len = smb2_calc_size(buf);
+       /* For SMB2_IOCTL, OutputOffset and OutputLength are optional, so might
+        * be 0, and not a real miscalculation */
+@@ -403,7 +403,7 @@ smb2_get_data_area_len(int *off, int *le
+  * portion, the number of word parameters and the data portion of the message.
+  */
+ unsigned int
+-smb2_calc_size(void *buf, struct TCP_Server_Info *srvr)
++smb2_calc_size(void *buf)
+ {
+       struct smb2_pdu *pdu = (struct smb2_pdu *)buf;
+       struct smb2_hdr *shdr = &pdu->hdr;
+--- a/fs/cifs/smb2ops.c
++++ b/fs/cifs/smb2ops.c
+@@ -374,7 +374,7 @@ smb2_dump_detail(void *buf, struct TCP_S
+                shdr->Command, shdr->Status, shdr->Flags, shdr->MessageId,
+                shdr->Id.SyncId.ProcessId);
+       cifs_server_dbg(VFS, "smb buf %p len %u\n", buf,
+-               server->ops->calc_smb_size(buf, server));
++               server->ops->calc_smb_size(buf));
+ #endif
+ }
+--- a/fs/cifs/smb2proto.h
++++ b/fs/cifs/smb2proto.h
+@@ -23,7 +23,7 @@ struct smb_rqst;
+ extern int map_smb2_to_linux_error(char *buf, bool log_err);
+ extern int smb2_check_message(char *buf, unsigned int length,
+                             struct TCP_Server_Info *server);
+-extern unsigned int smb2_calc_size(void *buf, struct TCP_Server_Info *server);
++extern unsigned int smb2_calc_size(void *buf);
+ extern char *smb2_get_data_area_len(int *off, int *len,
+                                   struct smb2_hdr *shdr);
+ extern __le16 *cifs_convert_path_to_utf16(const char *from,
diff --git a/queue-5.15/coresight-etb10-restore-atomic_t-for-shared-reading-state.patch b/queue-5.15/coresight-etb10-restore-atomic_t-for-shared-reading-state.patch
new file mode 100644 (file)
index 0000000..80b63a1
--- /dev/null
@@ -0,0 +1,123 @@
+From stable+bounces-274998-greg=kroah.com@vger.kernel.org Wed Jul 15 18:57:30 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 15 Jul 2026 12:44:18 -0400
+Subject: coresight: etb10: restore atomic_t for shared reading state
+To: stable@vger.kernel.org
+Cc: Runyu Xiao <runyu.xiao@seu.edu.cn>, James Clark <james.clark@linaro.org>, Suzuki K Poulose <suzuki.poulose@arm.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260715164418.958997-1-sashal@kernel.org>
+
+From: Runyu Xiao <runyu.xiao@seu.edu.cn>
+
+[ Upstream commit fa09f08ede3db3050ae16ae1ed92c902d0cada23 ]
+
+The etb10 miscdevice uses drvdata->reading as a shared exclusivity gate
+for userspace buffer access. etb_open() claims that gate with
+local_cmpxchg(), and etb_release() clears it with local_set().
+
+That gate is shared per-device state rather than CPU-local state. A
+running system can reach it whenever /dev/<etb> is opened, closed, and
+reopened by different tasks while the device remains registered, so the
+same drvdata->reading variable may be claimed on one CPU and later
+cleared on another.
+
+This code used to use atomic_t for the same gate, but commit
+27b10da8fff2 ("coresight: etb10: moving to local atomic operations")
+changed it to local_t even though the access pattern remained cross-task
+and cross-CPU. Restore atomic_t together with atomic_cmpxchg() and
+atomic_set() so the exclusivity gate again uses a primitive intended
+for shared state.
+
+The issue was found on Linux v6.18.21 by our static analysis tool while
+scanning surviving local_t-on-shared-state sites, and then manually
+reviewed against the live etb10 file-op path.
+
+It was runtime-validated with a reproducible QEMU no-device KCSAN PoC
+that kept the same report-local contract:
+
+  1. use one shared struct etb_drvdata carrier and its
+     drvdata->reading gate;
+  2. call etb_open() and etb_release() sequentially on that gate to
+     confirm the original claim/clear path;
+  3. bind the open side to CPU0 and the release side to CPU1 for the
+     same gate to show cross-CPU ownership;
+  4. run bound workers that repeatedly race etb_open() and
+     etb_release() on the same gate until KCSAN reports a target hit.
+
+The harness recorded:
+
+  L1 passed open=1 release=1
+  reading_after_open=1 reading_after_release=0
+  L2 passed open_cpu=0 release_cpu=1
+  cross_cpu_release=1 reading_after=0 open_ret=0
+
+Representative KCSAN excerpt from the no-device validation run:
+
+  BUG: KCSAN: data-race in etb_open.constprop.0.isra.0 [vuln_msv]
+
+  write to 0xffffffffc0003810 of 4 bytes by task 216 on cpu 1:
+   etb_open.constprop.0.isra.0+0x38/0x80 [vuln_msv]
+   l3_worker_thread_fn+0x4f/0xf0 [vuln_msv]
+   kthread+0x17e/0x1c0
+   ret_from_fork+0x22/0x30
+
+  read to 0xffffffffc0003810 of 4 bytes by task 215 on cpu 0:
+   etb_open.constprop.0.isra.0+0x18/0x80 [vuln_msv]
+   l3_worker_thread_fn+0x4f/0xf0 [vuln_msv]
+   kthread+0x17e/0x1c0
+   ret_from_fork+0x22/0x30
+
+  value changed: 0x00000000 -> 0x00000001
+
+  Reported by Kernel Concurrency Sanitizer on:
+  CPU: 0 PID: 215 Comm: etb10_l3_a Tainted: G           O       6.1.66 #2
+
+This no-device harness is not a real ETB10 hardware end-to-end run, but
+it preserves the same shared drvdata->reading gate and the same
+etb_open()/etb_release() claim/clear contract. No real ETB10 hardware
+was available for runtime testing.
+
+Build-tested with:
+  make olddefconfig
+  make -j"$(nproc)" drivers/hwtracing/coresight/coresight-etb10.o
+
+Fixes: 27b10da8fff2 ("coresight: etb10: moving to local atomic operations")
+Cc: stable@vger.kernel.org
+Signed-off-by: Runyu Xiao <runyu.xiao@seu.edu.cn>
+Reviewed-by: James Clark <james.clark@linaro.org>
+Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
+Link: https://lore.kernel.org/r/20260528165201.319452-1-runyu.xiao@seu.edu.cn
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/hwtracing/coresight/coresight-etb10.c |    6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+--- a/drivers/hwtracing/coresight/coresight-etb10.c
++++ b/drivers/hwtracing/coresight/coresight-etb10.c
+@@ -86,7 +86,7 @@ struct etb_drvdata {
+       struct coresight_device *csdev;
+       struct miscdevice       miscdev;
+       spinlock_t              spinlock;
+-      local_t                 reading;
++      atomic_t                reading;
+       pid_t                   pid;
+       u8                      *buf;
+       u32                     mode;
+@@ -604,7 +604,7 @@ static int etb_open(struct inode *inode,
+       struct etb_drvdata *drvdata = container_of(file->private_data,
+                                                  struct etb_drvdata, miscdev);
+-      if (local_cmpxchg(&drvdata->reading, 0, 1))
++      if (atomic_cmpxchg(&drvdata->reading, 0, 1))
+               return -EBUSY;
+       dev_dbg(&drvdata->csdev->dev, "%s: successfully opened\n", __func__);
+@@ -642,7 +642,7 @@ static int etb_release(struct inode *ino
+ {
+       struct etb_drvdata *drvdata = container_of(file->private_data,
+                                                  struct etb_drvdata, miscdev);
+-      local_set(&drvdata->reading, 0);
++      atomic_set(&drvdata->reading, 0);
+       dev_dbg(&drvdata->csdev->dev, "%s: released\n", __func__);
+       return 0;
diff --git a/queue-5.15/crypto-atmel-drop-explicit-initialization-of-struct-i2c_device_id-driver_data-to-0.patch b/queue-5.15/crypto-atmel-drop-explicit-initialization-of-struct-i2c_device_id-driver_data-to-0.patch
new file mode 100644 (file)
index 0000000..7c92a93
--- /dev/null
@@ -0,0 +1,39 @@
+From stable+bounces-277905-greg=kroah.com@vger.kernel.org Mon Jul 20 17:49:04 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Jul 2026 11:00:50 -0400
+Subject: crypto: atmel - Drop explicit initialization of struct i2c_device_id::driver_data to 0
+To: stable@vger.kernel.org
+Cc: "Uwe Kleine-König" <u.kleine-koenig@baylibre.com>, "Herbert Xu" <herbert@gondor.apana.org.au>, "Sasha Levin" <sashal@kernel.org>
+Message-ID: <20260720150051.1918796-2-sashal@kernel.org>
+
+From: Uwe Kleine-König <u.kleine-koenig@baylibre.com>
+
+[ Upstream commit d86ad3911a5d4549297ed810ee450e5772fd665f ]
+
+These drivers don't use the driver_data member of struct i2c_device_id,
+so don't explicitly initialize this member.
+
+This prepares putting driver_data in an anonymous union which requires
+either no initialization or named designators. But it's also a nice
+cleanup on its own.
+
+Signed-off-by: Uwe Kleine-König <u.kleine-koenig@baylibre.com>
+Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
+Stable-dep-of: ea5e57cc9718 ("crypto: atmel-sha204a - drop hwrng quality reduction for ATSHA204A")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/crypto/atmel-ecc.c |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/crypto/atmel-ecc.c
++++ b/drivers/crypto/atmel-ecc.c
+@@ -375,7 +375,7 @@ MODULE_DEVICE_TABLE(of, atmel_ecc_dt_ids
+ #endif
+ static const struct i2c_device_id atmel_ecc_id[] = {
+-      { "atecc508a", 0 },
++      { "atecc508a" },
+       { }
+ };
+ MODULE_DEVICE_TABLE(i2c, atmel_ecc_id);
diff --git a/queue-5.15/crypto-atmel-sha204a-drop-hwrng-quality-reduction-for-atsha204a.patch b/queue-5.15/crypto-atmel-sha204a-drop-hwrng-quality-reduction-for-atsha204a.patch
new file mode 100644 (file)
index 0000000..77bedb2
--- /dev/null
@@ -0,0 +1,101 @@
+From stable+bounces-277907-greg=kroah.com@vger.kernel.org Mon Jul 20 17:49:06 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Jul 2026 11:00:51 -0400
+Subject: crypto: atmel-sha204a - drop hwrng quality reduction for ATSHA204A
+To: stable@vger.kernel.org
+Cc: Thorsten Blum <thorsten.blum@linux.dev>, Ard Biesheuvel <ardb@kernel.org>, Herbert Xu <herbert@gondor.apana.org.au>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260720150051.1918796-3-sashal@kernel.org>
+
+From: Thorsten Blum <thorsten.blum@linux.dev>
+
+[ Upstream commit ea5e57cc97185329dcc5ebdcaae7e1500bf0ad0b ]
+
+Commit 8006aff15516 ("crypto: atmel-sha204a - Set hwrng quality to
+lowest possible") reduced the hwrng quality to 1 based on a review by
+Bill Cox [1]. However, despite its title, the review only tested the
+ATSHA204, not the ATSHA204A.
+
+In the same thread, Atmel engineer Landon Cox wrote "this behavior has
+been eliminated entirely"[2] in the ATSHA204A and "this problem does not
+affect the ATECC108 or the ATECC108A (or the ATSHA204A)"[3].
+
+According to the official ATSHA204A datasheet [4], the device contains a
+high-quality hardware RNG that combines its output with an internal seed
+value stored in EEPROM or SRAM to generate random numbers. The device
+also implements all security functions using SHA-256, and the driver
+uses the chip's Random command in seed-update mode.
+
+Keep 'quality = 1' for ATSHA204, but drop the explicit hwrng quality
+reduction for ATSHA204A and fall back to the hwrng core default.
+
+[1] https://www.metzdowd.com/pipermail/cryptography/2014-December/023858.html
+[2] https://www.metzdowd.com/pipermail/cryptography/2014-December/023852.html
+[3] https://www.metzdowd.com/pipermail/cryptography/2014-December/023886.html
+[4] https://ww1.microchip.com/downloads/en/DeviceDoc/ATSHA204A-Data-Sheet-40002025A.pdf
+
+Fixes: 8006aff15516 ("crypto: atmel-sha204a - Set hwrng quality to lowest possible")
+Cc: stable@vger.kernel.org
+Signed-off-by: Thorsten Blum <thorsten.blum@linux.dev>
+Reviewed-by: Ard Biesheuvel <ardb@kernel.org>
+Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/crypto/atmel-sha204a.c |   19 ++++++++++++-------
+ 1 file changed, 12 insertions(+), 7 deletions(-)
+
+--- a/drivers/crypto/atmel-sha204a.c
++++ b/drivers/crypto/atmel-sha204a.c
+@@ -18,6 +18,12 @@
+ #include <linux/workqueue.h>
+ #include "atmel-i2c.h"
++/*
++ * According to review by Bill Cox [1], the ATSHA204 has very low entropy.
++ * [1] https://www.metzdowd.com/pipermail/cryptography/2014-December/023858.html
++ */
++static const unsigned short atsha204_quality = 1;
++
+ static void atmel_sha204a_rng_done(struct atmel_i2c_work_data *work_data,
+                                  void *areq, int status)
+ {
+@@ -95,6 +101,7 @@ static int atmel_sha204a_probe(struct i2
+                              const struct i2c_device_id *id)
+ {
+       struct atmel_i2c_client_priv *i2c_priv;
++      const unsigned short *quality;
+       int ret;
+       ret = atmel_i2c_probe(client, id);
+@@ -108,11 +115,9 @@ static int atmel_sha204a_probe(struct i2
+       i2c_priv->hwrng.name = dev_name(&client->dev);
+       i2c_priv->hwrng.read = atmel_sha204a_rng_read;
+-      /*
+-       * According to review by Bill Cox [1], this HWRNG has very low entropy.
+-       * [1] https://www.metzdowd.com/pipermail/cryptography/2014-December/023858.html
+-       */
+-      i2c_priv->hwrng.quality = 1;
++      quality = i2c_get_match_data(client);
++      if (quality)
++              i2c_priv->hwrng.quality = *quality;
+       ret = devm_hwrng_register(&client->dev, &i2c_priv->hwrng);
+       if (ret)
+@@ -137,14 +142,14 @@ static int atmel_sha204a_remove(struct i
+ }
+ static const struct of_device_id atmel_sha204a_dt_ids[] __maybe_unused = {
+-      { .compatible = "atmel,atsha204", },
++      { .compatible = "atmel,atsha204", .data = &atsha204_quality },
+       { .compatible = "atmel,atsha204a", },
+       { /* sentinel */ }
+ };
+ MODULE_DEVICE_TABLE(of, atmel_sha204a_dt_ids);
+ static const struct i2c_device_id atmel_sha204a_id[] = {
+-      { "atsha204" },
++      { "atsha204", (kernel_ulong_t)&atsha204_quality },
+       { "atsha204a" },
+       { /* sentinel */ }
+ };
diff --git a/queue-5.15/crypto-atmel-sha204a-mark-of-related-data-as-maybe-unused.patch b/queue-5.15/crypto-atmel-sha204a-mark-of-related-data-as-maybe-unused.patch
new file mode 100644 (file)
index 0000000..65e8fc2
--- /dev/null
@@ -0,0 +1,47 @@
+From stable+bounces-277904-greg=kroah.com@vger.kernel.org Mon Jul 20 17:25:13 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Jul 2026 11:00:49 -0400
+Subject: crypto: atmel-sha204a - Mark OF related data as maybe unused
+To: stable@vger.kernel.org
+Cc: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>, Herbert Xu <herbert@gondor.apana.org.au>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260720150051.1918796-1-sashal@kernel.org>
+
+From: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+
+[ Upstream commit 75f3d950054389e2556277e42170e37dd97cd872 ]
+
+The driver can be compile tested with !CONFIG_OF making certain data
+unused:
+
+  drivers/crypto/atmel-sha204a.c:129:34: error: â€˜atmel_sha204a_dt_ids’ defined but not used [-Werror=unused-const-variable=]
+
+Signed-off-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
+Stable-dep-of: ea5e57cc9718 ("crypto: atmel-sha204a - drop hwrng quality reduction for ATSHA204A")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/crypto/atmel-sha204a.c |    6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+--- a/drivers/crypto/atmel-sha204a.c
++++ b/drivers/crypto/atmel-sha204a.c
+@@ -136,14 +136,16 @@ static int atmel_sha204a_remove(struct i
+       return 0;
+ }
+-static const struct of_device_id atmel_sha204a_dt_ids[] = {
++static const struct of_device_id atmel_sha204a_dt_ids[] __maybe_unused = {
++      { .compatible = "atmel,atsha204", },
+       { .compatible = "atmel,atsha204a", },
+       { /* sentinel */ }
+ };
+ MODULE_DEVICE_TABLE(of, atmel_sha204a_dt_ids);
+ static const struct i2c_device_id atmel_sha204a_id[] = {
+-      { "atsha204a", 0 },
++      { "atsha204" },
++      { "atsha204a" },
+       { /* sentinel */ }
+ };
+ MODULE_DEVICE_TABLE(i2c, atmel_sha204a_id);
diff --git a/queue-5.15/crypto-qat-fix-restarting-state-leak-on-allocation-failure.patch b/queue-5.15/crypto-qat-fix-restarting-state-leak-on-allocation-failure.patch
new file mode 100644 (file)
index 0000000..0621f7c
--- /dev/null
@@ -0,0 +1,57 @@
+From stable+bounces-278219-greg=kroah.com@vger.kernel.org Mon Jul 20 22:19:01 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Jul 2026 16:18:53 -0400
+Subject: crypto: qat - fix restarting state leak on allocation failure
+To: stable@vger.kernel.org
+Cc: Ahsan Atta <ahsan.atta@intel.com>, Maksim Lukoshkov <maksim.lukoshkov@intel.com>, Giovanni Cabiddu <giovanni.cabiddu@intel.com>, Herbert Xu <herbert@gondor.apana.org.au>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260720201853.3239508-1-sashal@kernel.org>
+
+From: Ahsan Atta <ahsan.atta@intel.com>
+
+[ Upstream commit 7d3ed20f7e46b3e991936fedd7a28f3ff4aec8d2 ]
+
+In adf_dev_aer_schedule_reset(), ADF_STATUS_RESTARTING is set before
+allocating reset_data. If the allocation fails, the function returns
+-ENOMEM without queuing reset work, so nothing ever clears the bit.
+This leaves the device permanently stuck in the restarting state,
+causing all subsequent reset attempts to be silently skipped.
+
+Fix this by using test_and_set_bit() to atomically claim the
+RESTARTING state, preventing duplicate reset scheduling races under
+concurrent fatal error reporting. If the subsequent allocation fails,
+clear the bit to restore clean state so future reset attempts can
+proceed.
+
+Cc: stable@vger.kernel.org
+Fixes: d8cba25d2c68 ("crypto: qat - Intel(R) QAT driver framework")
+Signed-off-by: Ahsan Atta <ahsan.atta@intel.com>
+Co-developed-by: Maksim Lukoshkov <maksim.lukoshkov@intel.com>
+Signed-off-by: Maksim Lukoshkov <maksim.lukoshkov@intel.com>
+Reviewed-by: Giovanni Cabiddu <giovanni.cabiddu@intel.com>
+Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/crypto/qat/qat_common/adf_aer.c |    7 ++++---
+ 1 file changed, 4 insertions(+), 3 deletions(-)
+
+--- a/drivers/crypto/qat/qat_common/adf_aer.c
++++ b/drivers/crypto/qat/qat_common/adf_aer.c
+@@ -116,13 +116,14 @@ static int adf_dev_aer_schedule_reset(st
+       struct adf_reset_dev_data *reset_data;
+       if (!adf_dev_started(accel_dev) ||
+-          test_bit(ADF_STATUS_RESTARTING, &accel_dev->status))
++          test_and_set_bit(ADF_STATUS_RESTARTING, &accel_dev->status))
+               return 0;
+-      set_bit(ADF_STATUS_RESTARTING, &accel_dev->status);
+       reset_data = kzalloc(sizeof(*reset_data), GFP_KERNEL);
+-      if (!reset_data)
++      if (!reset_data) {
++              clear_bit(ADF_STATUS_RESTARTING, &accel_dev->status);
+               return -ENOMEM;
++      }
+       reset_data->accel_dev = accel_dev;
+       init_completion(&reset_data->compl);
+       reset_data->mode = mode;
diff --git a/queue-5.15/crypto-qat-fix-vf2pf-work-teardown-race-in-adf_disable_sriov.patch b/queue-5.15/crypto-qat-fix-vf2pf-work-teardown-race-in-adf_disable_sriov.patch
new file mode 100644 (file)
index 0000000..bd46f9b
--- /dev/null
@@ -0,0 +1,230 @@
+From stable+bounces-274714-greg=kroah.com@vger.kernel.org Wed Jul 15 05:16:11 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 14 Jul 2026 23:16:01 -0400
+Subject: crypto: qat - fix VF2PF work teardown race in adf_disable_sriov()
+To: stable@vger.kernel.org
+Cc: Giovanni Cabiddu <giovanni.cabiddu@intel.com>, Ahsan Atta <ahsan.atta@intel.com>, Herbert Xu <herbert@gondor.apana.org.au>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260715031602.247772-1-sashal@kernel.org>
+
+From: Giovanni Cabiddu <giovanni.cabiddu@intel.com>
+
+[ Upstream commit 277281c10c63791067d24d421f7c43a15faa9096 ]
+
+The VF2PF interrupt handler queues PF-side response work that stores a
+raw pointer to per-VF state (struct adf_accel_vf_info). Currently,
+adf_disable_sriov() destroys per-VF mutexes and frees vf_info without
+stopping new VF2PF work or waiting for in-flight workers to complete. A
+concurrently scheduled or already queued worker can then dereference
+freed memory.
+
+This manifests as a use-after-free when KASAN is enabled:
+
+  BUG: KASAN: null-ptr-deref in mutex_lock+0x76/0xe0
+  Write of size 8 at addr 0000000000000260 by task kworker/24:2/...
+  Workqueue: qat_pf2vf_resp_wq adf_iov_send_resp [intel_qat]
+  Call Trace:
+    kasan_report+0x119/0x140
+    mutex_lock+0x76/0xe0
+    adf_gen4_pfvf_send+0xd4/0x1f0 [intel_qat]
+    adf_recv_and_handle_vf2pf_msg+0x290/0x360 [intel_qat]
+    adf_iov_send_resp+0x8c/0xe0 [intel_qat]
+    process_one_work+0x6ac/0xfd0
+    worker_thread+0x4dd/0xd30
+    kthread+0x326/0x410
+    ret_from_fork+0x33b/0x670
+
+Add a PF-local flag, vf2pf_disabled, that gates work queueing, worker
+processing, and interrupt re-enabling during teardown. Set this flag
+atomically with the hardware interrupt mask inside
+adf_disable_all_vf2pf_interrupts(). After masking, synchronize the AE
+cluster MSI-X interrupt and flush the PF response workqueue before
+tearing down per-VF locks and state so all in-flight work completes
+before vf_info is destroyed.
+
+Introduce adf_enable_all_vf2pf_interrupts() to clear the flag and
+unmask all VF2PF interrupts under the same lock when SR-IOV is
+re-enabled. This ensures the software flag and hardware state transition
+atomically on both the enable and disable paths.
+
+Cc: stable@vger.kernel.org
+Fixes: ed8ccaef52fa ("crypto: qat - Add support for SRIOV")
+Signed-off-by: Giovanni Cabiddu <giovanni.cabiddu@intel.com>
+Reviewed-by: Ahsan Atta <ahsan.atta@intel.com>
+Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/crypto/qat/qat_common/adf_accel_devices.h |    2 +
+ drivers/crypto/qat/qat_common/adf_common_drv.h    |    4 +++
+ drivers/crypto/qat/qat_common/adf_isr.c           |   18 ++++++++++++++
+ drivers/crypto/qat/qat_common/adf_pf2vf_msg.c     |   27 ++++++++++++++++++++++
+ drivers/crypto/qat/qat_common/adf_sriov.c         |   27 ++++++++++++++++++----
+ 5 files changed, 74 insertions(+), 4 deletions(-)
+
+--- a/drivers/crypto/qat/qat_common/adf_accel_devices.h
++++ b/drivers/crypto/qat/qat_common/adf_accel_devices.h
+@@ -248,6 +248,8 @@ struct adf_accel_dev {
+               struct {
+                       /* protects VF2PF interrupts access */
+                       spinlock_t vf2pf_ints_lock;
++                      /* prevents VF2PF handling from racing with VF state teardown */
++                      bool vf2pf_disabled;
+                       /* vf_info is non-zero when SR-IOV is init'ed */
+                       struct adf_accel_vf_info *vf_info;
+               } pf;
+--- a/drivers/crypto/qat/qat_common/adf_common_drv.h
++++ b/drivers/crypto/qat/qat_common/adf_common_drv.h
+@@ -120,6 +120,7 @@ void qat_asym_algs_unregister(void);
+ int adf_isr_resource_alloc(struct adf_accel_dev *accel_dev);
+ void adf_isr_resource_free(struct adf_accel_dev *accel_dev);
++void adf_isr_sync_ae_cluster(struct adf_accel_dev *accel_dev);
+ int adf_vf_isr_resource_alloc(struct adf_accel_dev *accel_dev);
+ void adf_vf_isr_resource_free(struct adf_accel_dev *accel_dev);
+@@ -189,6 +190,9 @@ void adf_disable_vf2pf_interrupts_irq(st
+                                     u32 vf_mask);
+ void adf_enable_vf2pf_interrupts(struct adf_accel_dev *accel_dev,
+                                u32 vf_mask);
++void adf_enable_all_vf2pf_interrupts(struct adf_accel_dev *accel_dev,
++                                   u32 num_vfs);
++void adf_disable_all_vf2pf_interrupts(struct adf_accel_dev *accel_dev);
+ void adf_enable_pf2vf_interrupts(struct adf_accel_dev *accel_dev);
+ void adf_disable_pf2vf_interrupts(struct adf_accel_dev *accel_dev);
+ void adf_schedule_vf2pf_handler(struct adf_accel_vf_info *vf_info);
+--- a/drivers/crypto/qat/qat_common/adf_isr.c
++++ b/drivers/crypto/qat/qat_common/adf_isr.c
+@@ -135,6 +135,24 @@ static irqreturn_t adf_msix_isr_ae(int i
+       return IRQ_NONE;
+ }
++void adf_isr_sync_ae_cluster(struct adf_accel_dev *accel_dev)
++{
++      struct adf_accel_pci *pci_dev_info = &accel_dev->accel_pci_dev;
++      struct adf_hw_device_data *hw_data = accel_dev->hw_device;
++      struct msix_entry *msixe = pci_dev_info->msix_entries.entries;
++      u32 num_entries = pci_dev_info->msix_entries.num_entries;
++      u32 irq_idx;
++
++      if (!test_bit(ADF_STATUS_IRQ_ALLOCATED, &accel_dev->status) || !msixe)
++              return;
++
++      irq_idx = num_entries > 1 ? hw_data->num_banks : 0;
++      if (irq_idx >= num_entries)
++              return;
++
++      synchronize_irq(msixe[irq_idx].vector);
++}
++
+ static int adf_request_irqs(struct adf_accel_dev *accel_dev)
+ {
+       struct adf_accel_pci *pci_dev_info = &accel_dev->accel_pci_dev;
+--- a/drivers/crypto/qat/qat_common/adf_pf2vf_msg.c
++++ b/drivers/crypto/qat/qat_common/adf_pf2vf_msg.c
+@@ -40,6 +40,23 @@ void adf_enable_vf2pf_interrupts(struct
+       unsigned long flags;
+       spin_lock_irqsave(&accel_dev->pf.vf2pf_ints_lock, flags);
++      if (!READ_ONCE(accel_dev->pf.vf2pf_disabled))
++              __adf_enable_vf2pf_interrupts(accel_dev, vf_mask);
++      spin_unlock_irqrestore(&accel_dev->pf.vf2pf_ints_lock, flags);
++}
++
++void adf_enable_all_vf2pf_interrupts(struct adf_accel_dev *accel_dev,
++                                   u32 num_vfs)
++{
++      unsigned long flags;
++      u32 vf_mask;
++
++      vf_mask = BIT_ULL(num_vfs) - 1;
++      if (!vf_mask)
++              return;
++
++      spin_lock_irqsave(&accel_dev->pf.vf2pf_ints_lock, flags);
++      WRITE_ONCE(accel_dev->pf.vf2pf_disabled, false);
+       __adf_enable_vf2pf_interrupts(accel_dev, vf_mask);
+       spin_unlock_irqrestore(&accel_dev->pf.vf2pf_ints_lock, flags);
+ }
+@@ -84,6 +101,16 @@ void adf_disable_vf2pf_interrupts_irq(st
+       spin_unlock(&accel_dev->pf.vf2pf_ints_lock);
+ }
++void adf_disable_all_vf2pf_interrupts(struct adf_accel_dev *accel_dev)
++{
++      unsigned long flags;
++
++      spin_lock_irqsave(&accel_dev->pf.vf2pf_ints_lock, flags);
++      WRITE_ONCE(accel_dev->pf.vf2pf_disabled, true);
++      __adf_disable_vf2pf_interrupts(accel_dev, GENMASK(31, 0));
++      spin_unlock_irqrestore(&accel_dev->pf.vf2pf_ints_lock, flags);
++}
++
+ static int __adf_iov_putmsg(struct adf_accel_dev *accel_dev, u32 msg, u8 vf_nr)
+ {
+       struct adf_accel_pci *pci_info = &accel_dev->accel_pci_dev;
+--- a/drivers/crypto/qat/qat_common/adf_sriov.c
++++ b/drivers/crypto/qat/qat_common/adf_sriov.c
+@@ -19,15 +19,26 @@ static void adf_iov_send_resp(struct wor
+ {
+       struct adf_pf2vf_resp *pf2vf_resp =
+               container_of(work, struct adf_pf2vf_resp, pf2vf_resp_work);
++      struct adf_accel_vf_info *vf_info = pf2vf_resp->vf_info;
++      struct adf_accel_dev *accel_dev = vf_info->accel_dev;
+-      adf_vf2pf_req_hndl(pf2vf_resp->vf_info);
++      if (READ_ONCE(accel_dev->pf.vf2pf_disabled))
++              goto out;
++
++      adf_vf2pf_req_hndl(vf_info);
++
++out:
+       kfree(pf2vf_resp);
+ }
+ void adf_schedule_vf2pf_handler(struct adf_accel_vf_info *vf_info)
+ {
++      struct adf_accel_dev *accel_dev = vf_info->accel_dev;
+       struct adf_pf2vf_resp *pf2vf_resp;
++      if (READ_ONCE(accel_dev->pf.vf2pf_disabled))
++              return;
++
+       pf2vf_resp = kzalloc(sizeof(*pf2vf_resp), GFP_ATOMIC);
+       if (!pf2vf_resp)
+               return;
+@@ -37,6 +48,12 @@ void adf_schedule_vf2pf_handler(struct a
+       queue_work(pf2vf_resp_wq, &pf2vf_resp->pf2vf_resp_work);
+ }
++static void adf_flush_pf2vf_resp_wq(void)
++{
++      if (pf2vf_resp_wq)
++              flush_workqueue(pf2vf_resp_wq);
++}
++
+ static int adf_enable_sriov(struct adf_accel_dev *accel_dev)
+ {
+       struct pci_dev *pdev = accel_to_pci_dev(accel_dev);
+@@ -63,7 +80,7 @@ static int adf_enable_sriov(struct adf_a
+       /* Enable VF to PF interrupts for all VFs */
+       if (hw_data->get_pf2vf_offset)
+-              adf_enable_vf2pf_interrupts(accel_dev, BIT_ULL(totalvfs) - 1);
++              adf_enable_all_vf2pf_interrupts(accel_dev, totalvfs);
+       /*
+        * Due to the hardware design, when SR-IOV and the ring arbiter
+@@ -97,9 +114,11 @@ void adf_disable_sriov(struct adf_accel_
+       pci_disable_sriov(accel_to_pci_dev(accel_dev));
+-      /* Disable VF to PF interrupts */
++      /* Block VF2PF work and disable VF to PF interrupts */
+       if (hw_data->get_pf2vf_offset)
+-              adf_disable_vf2pf_interrupts(accel_dev, GENMASK(31, 0));
++              adf_disable_all_vf2pf_interrupts(accel_dev);
++      adf_isr_sync_ae_cluster(accel_dev);
++      adf_flush_pf2vf_resp_wq();
+       /* Clear Valid bits in AE Thread to PCIe Function Mapping */
+       if (hw_data->configure_iov_threads)
diff --git a/queue-5.15/crypto-qat-validate-rsa-crt-component-lengths.patch b/queue-5.15/crypto-qat-validate-rsa-crt-component-lengths.patch
new file mode 100644 (file)
index 0000000..6c03753
--- /dev/null
@@ -0,0 +1,87 @@
+From stable+bounces-278283-greg=kroah.com@vger.kernel.org Tue Jul 21 02:45:14 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Jul 2026 20:45:08 -0400
+Subject: crypto: qat - validate RSA CRT component lengths
+To: stable@vger.kernel.org
+Cc: Giovanni Cabiddu <giovanni.cabiddu@intel.com>, Ahsan Atta <ahsan.atta@intel.com>, Laurent M Coquerel <laurent.m.coquerel@intel.com>, Herbert Xu <herbert@gondor.apana.org.au>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260721004508.3421467-1-sashal@kernel.org>
+
+From: Giovanni Cabiddu <giovanni.cabiddu@intel.com>
+
+[ Upstream commit b3ac78756588059729b9195fcc9f4b37d54057a5 ]
+
+The generic RSA key parser (rsa_helper.c) bounds each CRT component (p,
+q, dp, dq, qinv) by the modulus size n_sz, but qat_rsa_setkey_crt()
+allocates half-size DMA buffers (key_sz / 2) and right-aligns each
+component with:
+
+    memcpy(dst + half_key_sz - len, src, len)
+
+When a CRT component is larger than half_key_sz the subtraction
+underflows and memcpy writes past the DMA buffer, causing memory
+corruption.
+
+Add a len > half_key_sz check next to the existing !len check for each
+of the five CRT components so the driver falls back to the non-CRT path
+instead of writing out of bounds.
+
+Fixes: 879f77e9071f ("crypto: qat - Add RSA CRT mode")
+Cc: stable@vger.kernel.org
+Signed-off-by: Giovanni Cabiddu <giovanni.cabiddu@intel.com>
+Reviewed-by: Ahsan Atta <ahsan.atta@intel.com>
+Reviewed-by: Laurent M Coquerel <laurent.m.coquerel@intel.com>
+Tested-by: Laurent M Coquerel <laurent.m.coquerel@intel.com>
+Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/crypto/qat/qat_common/qat_asym_algs.c |   10 +++++-----
+ 1 file changed, 5 insertions(+), 5 deletions(-)
+
+--- a/drivers/crypto/qat/qat_common/qat_asym_algs.c
++++ b/drivers/crypto/qat/qat_common/qat_asym_algs.c
+@@ -1032,7 +1032,7 @@ static void qat_rsa_setkey_crt(struct qa
+       ptr = rsa_key->p;
+       len = rsa_key->p_sz;
+       qat_rsa_drop_leading_zeros(&ptr, &len);
+-      if (!len)
++      if (!len || len > half_key_sz)
+               goto err;
+       ctx->p = dma_alloc_coherent(dev, half_key_sz, &ctx->dma_p, GFP_KERNEL);
+       if (!ctx->p)
+@@ -1043,7 +1043,7 @@ static void qat_rsa_setkey_crt(struct qa
+       ptr = rsa_key->q;
+       len = rsa_key->q_sz;
+       qat_rsa_drop_leading_zeros(&ptr, &len);
+-      if (!len)
++      if (!len || len > half_key_sz)
+               goto free_p;
+       ctx->q = dma_alloc_coherent(dev, half_key_sz, &ctx->dma_q, GFP_KERNEL);
+       if (!ctx->q)
+@@ -1054,7 +1054,7 @@ static void qat_rsa_setkey_crt(struct qa
+       ptr = rsa_key->dp;
+       len = rsa_key->dp_sz;
+       qat_rsa_drop_leading_zeros(&ptr, &len);
+-      if (!len)
++      if (!len || len > half_key_sz)
+               goto free_q;
+       ctx->dp = dma_alloc_coherent(dev, half_key_sz, &ctx->dma_dp,
+                                    GFP_KERNEL);
+@@ -1066,7 +1066,7 @@ static void qat_rsa_setkey_crt(struct qa
+       ptr = rsa_key->dq;
+       len = rsa_key->dq_sz;
+       qat_rsa_drop_leading_zeros(&ptr, &len);
+-      if (!len)
++      if (!len || len > half_key_sz)
+               goto free_dp;
+       ctx->dq = dma_alloc_coherent(dev, half_key_sz, &ctx->dma_dq,
+                                    GFP_KERNEL);
+@@ -1078,7 +1078,7 @@ static void qat_rsa_setkey_crt(struct qa
+       ptr = rsa_key->qinv;
+       len = rsa_key->qinv_sz;
+       qat_rsa_drop_leading_zeros(&ptr, &len);
+-      if (!len)
++      if (!len || len > half_key_sz)
+               goto free_dq;
+       ctx->qinv = dma_alloc_coherent(dev, half_key_sz, &ctx->dma_qinv,
+                                      GFP_KERNEL);
diff --git a/queue-5.15/drm-i2c-sil164-drop-no-op-remove-function.patch b/queue-5.15/drm-i2c-sil164-drop-no-op-remove-function.patch
new file mode 100644 (file)
index 0000000..a33fd67
--- /dev/null
@@ -0,0 +1,47 @@
+From stable+bounces-277528-greg=kroah.com@vger.kernel.org Mon Jul 20 04:25:52 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 19 Jul 2026 22:25:33 -0400
+Subject: drm/i2c/sil164: Drop no-op remove function
+To: stable@vger.kernel.org
+Cc: "Uwe Kleine-König" <u.kleine-koenig@pengutronix.de>, "Wolfram Sang" <wsa@kernel.org>, "Sasha Levin" <sashal@kernel.org>
+Message-ID: <20260720022537.2927284-1-sashal@kernel.org>
+
+From: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
+
+[ Upstream commit eb09882d64807c24228ed1d005dbbae70860ca3f ]
+
+A remove callback that just returns 0 is equivalent to no callback at all
+as can be seen in i2c_device_remove(). So simplify accordingly.
+
+Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
+Signed-off-by: Wolfram Sang <wsa@kernel.org>
+Stable-dep-of: 1f0bdc2884b6 ("usb: typec: ucsi: ccg: Fix use-after-free of ucsi on remove")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/gpu/drm/i2c/sil164_drv.c |    7 -------
+ 1 file changed, 7 deletions(-)
+
+--- a/drivers/gpu/drm/i2c/sil164_drv.c
++++ b/drivers/gpu/drm/i2c/sil164_drv.c
+@@ -370,12 +370,6 @@ sil164_probe(struct i2c_client *client,
+       return 0;
+ }
+-static int
+-sil164_remove(struct i2c_client *client)
+-{
+-      return 0;
+-}
+-
+ static struct i2c_client *
+ sil164_detect_slave(struct i2c_client *client)
+ {
+@@ -427,7 +421,6 @@ MODULE_DEVICE_TABLE(i2c, sil164_ids);
+ static struct drm_i2c_encoder_driver sil164_driver = {
+       .i2c_driver = {
+               .probe = sil164_probe,
+-              .remove = sil164_remove,
+               .driver = {
+                       .name = "sil164",
+               },
diff --git a/queue-5.15/gpio-pca953x-make-platform-teardown-callback-return-void.patch b/queue-5.15/gpio-pca953x-make-platform-teardown-callback-return-void.patch
new file mode 100644 (file)
index 0000000..341b1a3
--- /dev/null
@@ -0,0 +1,106 @@
+From stable+bounces-277531-greg=kroah.com@vger.kernel.org Mon Jul 20 04:25:48 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 19 Jul 2026 22:25:36 -0400
+Subject: gpio: pca953x: Make platform teardown callback return void
+To: stable@vger.kernel.org
+Cc: "Uwe Kleine-König" <u.kleine-koenig@pengutronix.de>, "Andy Shevchenko" <andy.shevchenko@gmail.com>, "Bartosz Golaszewski" <brgl@bgdev.pl>, "Wolfram Sang" <wsa@kernel.org>, "Sasha Levin" <sashal@kernel.org>
+Message-ID: <20260720022537.2927284-4-sashal@kernel.org>
+
+From: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
+
+[ Upstream commit 6a8f359c3132e4f51bdb263ad74ec632c65e55fd ]
+
+All platforms that provide a teardown callback return 0. New users are
+supposed to not make use of platform support, so there is no
+functionality lost.
+
+This patch is a preparation for making i2c remove callbacks return void.
+
+Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
+Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
+Acked-by: Bartosz Golaszewski <brgl@bgdev.pl>
+Signed-off-by: Wolfram Sang <wsa@kernel.org>
+Stable-dep-of: 1f0bdc2884b6 ("usb: typec: ucsi: ccg: Fix use-after-free of ucsi on remove")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ arch/arm/mach-davinci/board-da850-evm.c |   12 ++++--------
+ drivers/gpio/gpio-pca953x.c             |   11 +++--------
+ include/linux/platform_data/pca953x.h   |    2 +-
+ 3 files changed, 8 insertions(+), 17 deletions(-)
+
+--- a/arch/arm/mach-davinci/board-da850-evm.c
++++ b/arch/arm/mach-davinci/board-da850-evm.c
+@@ -517,8 +517,8 @@ exp_setup_sela_fail:
+       return ret;
+ }
+-static int da850_evm_ui_expander_teardown(struct i2c_client *client,
+-                                      unsigned gpio, unsigned ngpio, void *c)
++static void da850_evm_ui_expander_teardown(struct i2c_client *client,
++                                         unsigned gpio, unsigned ngpio, void *c)
+ {
+       platform_device_unregister(&da850_evm_ui_keys_device);
+@@ -530,8 +530,6 @@ static int da850_evm_ui_expander_teardow
+       gpio_free(gpio + DA850_EVM_UI_EXP_SEL_C);
+       gpio_free(gpio + DA850_EVM_UI_EXP_SEL_B);
+       gpio_free(gpio + DA850_EVM_UI_EXP_SEL_A);
+-
+-      return 0;
+ }
+ /* assign the baseboard expander's GPIOs after the UI board's */
+@@ -698,13 +696,11 @@ io_exp_setup_sw_fail:
+       return ret;
+ }
+-static int da850_evm_bb_expander_teardown(struct i2c_client *client,
+-                                      unsigned gpio, unsigned ngpio, void *c)
++static void da850_evm_bb_expander_teardown(struct i2c_client *client,
++                                         unsigned gpio, unsigned ngpio, void *c)
+ {
+       platform_device_unregister(&da850_evm_bb_leds_device);
+       platform_device_unregister(&da850_evm_bb_keys_device);
+-
+-      return 0;
+ }
+ static struct pca953x_platform_data da850_evm_ui_expander_info = {
+--- a/drivers/gpio/gpio-pca953x.c
++++ b/drivers/gpio/gpio-pca953x.c
+@@ -1074,20 +1074,15 @@ static int pca953x_remove(struct i2c_cli
+ {
+       struct pca953x_platform_data *pdata = dev_get_platdata(&client->dev);
+       struct pca953x_chip *chip = i2c_get_clientdata(client);
+-      int ret;
+       if (pdata && pdata->teardown) {
+-              ret = pdata->teardown(client, chip->gpio_chip.base,
+-                                    chip->gpio_chip.ngpio, pdata->context);
+-              if (ret < 0)
+-                      dev_err(&client->dev, "teardown failed, %d\n", ret);
+-      } else {
+-              ret = 0;
++              pdata->teardown(client, chip->gpio_chip.base,
++                              chip->gpio_chip.ngpio, pdata->context);
+       }
+       regulator_disable(chip->regulator);
+-      return ret;
++      return 0;
+ }
+ #ifdef CONFIG_PM_SLEEP
+--- a/include/linux/platform_data/pca953x.h
++++ b/include/linux/platform_data/pca953x.h
+@@ -22,7 +22,7 @@ struct pca953x_platform_data {
+       int             (*setup)(struct i2c_client *client,
+                               unsigned gpio, unsigned ngpio,
+                               void *context);
+-      int             (*teardown)(struct i2c_client *client,
++      void            (*teardown)(struct i2c_client *client,
+                               unsigned gpio, unsigned ngpio,
+                               void *context);
+       const char      *const *names;
diff --git a/queue-5.15/gpio-sch-use-new-gpio-line-value-setter-callbacks.patch b/queue-5.15/gpio-sch-use-new-gpio-line-value-setter-callbacks.patch
new file mode 100644 (file)
index 0000000..8779097
--- /dev/null
@@ -0,0 +1,65 @@
+From stable+bounces-275010-greg=kroah.com@vger.kernel.org Wed Jul 15 19:58:03 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 15 Jul 2026 13:55:23 -0400
+Subject: gpio: sch: use new GPIO line value setter callbacks
+To: stable@vger.kernel.org
+Cc: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>, Andy Shevchenko <andriy.shevchenko@linux.intel.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260715175524.1011791-1-sashal@kernel.org>
+
+From: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
+
+[ Upstream commit 883c7eb2c4a9e143b2662ba754f9c16fb31adced ]
+
+struct gpio_chip now has callbacks for setting line values that return
+an integer, allowing to indicate failures. Convert the driver to using
+them.
+
+Acked-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
+Link: https://lore.kernel.org/r/20250625-gpiochip-set-rv-gpio-round2-v1-3-bc110a3b52ff@linaro.org
+Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
+Stable-dep-of: 286533cb14a3 ("gpio: sch: use raw_spinlock_t in the irq startup path")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/gpio/gpio-sch.c |    9 +++++----
+ 1 file changed, 5 insertions(+), 4 deletions(-)
+
+--- a/drivers/gpio/gpio-sch.c
++++ b/drivers/gpio/gpio-sch.c
+@@ -116,7 +116,7 @@ static int sch_gpio_get(struct gpio_chip
+       return sch_gpio_reg_get(sch, gpio_num, GLV);
+ }
+-static void sch_gpio_set(struct gpio_chip *gc, unsigned int gpio_num, int val)
++static int sch_gpio_set(struct gpio_chip *gc, unsigned int gpio_num, int val)
+ {
+       struct sch_gpio *sch = gpiochip_get_data(gc);
+       unsigned long flags;
+@@ -124,6 +124,8 @@ static void sch_gpio_set(struct gpio_chi
+       spin_lock_irqsave(&sch->lock, flags);
+       sch_gpio_reg_set(sch, gpio_num, GLV, val);
+       spin_unlock_irqrestore(&sch->lock, flags);
++
++      return 0;
+ }
+ static int sch_gpio_direction_out(struct gpio_chip *gc, unsigned int gpio_num,
+@@ -145,8 +147,7 @@ static int sch_gpio_direction_out(struct
+        * But we cannot prevent a short low pulse if direction is set to high
+        * and an external pull-up is connected.
+        */
+-      sch_gpio_set(gc, gpio_num, val);
+-      return 0;
++      return sch_gpio_set(gc, gpio_num, val);
+ }
+ static int sch_gpio_get_direction(struct gpio_chip *gc, unsigned int gpio_num)
+@@ -165,7 +166,7 @@ static const struct gpio_chip sch_gpio_c
+       .direction_input        = sch_gpio_direction_in,
+       .get                    = sch_gpio_get,
+       .direction_output       = sch_gpio_direction_out,
+-      .set                    = sch_gpio_set,
++      .set_rv                 = sch_gpio_set,
+       .get_direction          = sch_gpio_get_direction,
+ };
diff --git a/queue-5.15/gpio-sch-use-raw_spinlock_t-in-the-irq-startup-path.patch b/queue-5.15/gpio-sch-use-raw_spinlock_t-in-the-irq-startup-path.patch
new file mode 100644 (file)
index 0000000..336c213
--- /dev/null
@@ -0,0 +1,190 @@
+From stable+bounces-275011-greg=kroah.com@vger.kernel.org Wed Jul 15 19:58:40 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 15 Jul 2026 13:55:24 -0400
+Subject: gpio: sch: use raw_spinlock_t in the irq startup path
+To: stable@vger.kernel.org
+Cc: Runyu Xiao <runyu.xiao@seu.edu.cn>, Sebastian Andrzej Siewior <bigeasy@linutronix.de>, Andy Shevchenko <andriy.shevchenko@intel.com>, Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260715175524.1011791-2-sashal@kernel.org>
+
+From: Runyu Xiao <runyu.xiao@seu.edu.cn>
+
+[ Upstream commit 286533cb14a3c8a8bd39ff64ea2fc8e1aa0f638b ]
+
+sch_irq_unmask() enables the GPIO IRQ and then updates the controller
+state through sch_irq_mask_unmask(), which takes sch->lock with
+spin_lock_irqsave().  The callback can be reached from irq_startup()
+while setting up a requested IRQ.  That path is not sleepable, but on
+PREEMPT_RT a regular spinlock_t becomes a sleeping lock.
+
+This issue was found by our static analysis tool and then manually
+reviewed against the current tree.
+
+The grounded PoC kept the request_threaded_irq() -> __setup_irq() ->
+irq_startup() -> sch_irq_unmask() -> sch_irq_mask_unmask() carrier and
+used the original spin_lock_irqsave(&sch->lock) edge.  Lockdep reported:
+
+  BUG: sleeping function called from invalid context
+  hardirqs last disabled at ... __setup_irq.constprop.0 ... [vuln_msv]
+  sch_rt_spin_lock_irqsave+0x1c/0x30 [vuln_msv]
+  sch_irq_mask_unmask.constprop.0+0x31/0x70 [vuln_msv]
+  __setup_irq.constprop.0+0xd/0x30 [vuln_msv]
+
+Convert the SCH controller lock to raw_spinlock_t.  The same lock is
+also used by the GPIO direction and value callbacks, but those critical
+sections only update MMIO-backed GPIO registers and do not contain
+sleepable operations.  Keeping this register lock non-sleeping is
+therefore appropriate for the irqchip callbacks and does not change the
+GPIO-side locking contract.
+
+Fixes: 7a81638485c1 ("gpio: sch: Add edge event support")
+Cc: stable@vger.kernel.org
+Signed-off-by: Runyu Xiao <runyu.xiao@seu.edu.cn>
+Reviewed-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+Reviewed-by: Andy Shevchenko <andriy.shevchenko@intel.com>
+Link: https://patch.msgid.link/20260617154035.1199948-2-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-sch.c |   41 ++++++++++++++++++++---------------------
+ 1 file changed, 20 insertions(+), 21 deletions(-)
+
+--- a/drivers/gpio/gpio-sch.c
++++ b/drivers/gpio/gpio-sch.c
+@@ -39,7 +39,7 @@
+ struct sch_gpio {
+       struct gpio_chip chip;
+       struct irq_chip irqchip;
+-      spinlock_t lock;
++      raw_spinlock_t lock;
+       unsigned short iobase;
+       unsigned short resume_base;
+@@ -103,9 +103,9 @@ static int sch_gpio_direction_in(struct
+       struct sch_gpio *sch = gpiochip_get_data(gc);
+       unsigned long flags;
+-      spin_lock_irqsave(&sch->lock, flags);
++      raw_spin_lock_irqsave(&sch->lock, flags);
+       sch_gpio_reg_set(sch, gpio_num, GIO, 1);
+-      spin_unlock_irqrestore(&sch->lock, flags);
++      raw_spin_unlock_irqrestore(&sch->lock, flags);
+       return 0;
+ }
+@@ -116,16 +116,14 @@ static int sch_gpio_get(struct gpio_chip
+       return sch_gpio_reg_get(sch, gpio_num, GLV);
+ }
+-static int sch_gpio_set(struct gpio_chip *gc, unsigned int gpio_num, int val)
++static void sch_gpio_set(struct gpio_chip *gc, unsigned int gpio_num, int val)
+ {
+       struct sch_gpio *sch = gpiochip_get_data(gc);
+       unsigned long flags;
+-      spin_lock_irqsave(&sch->lock, flags);
++      raw_spin_lock_irqsave(&sch->lock, flags);
+       sch_gpio_reg_set(sch, gpio_num, GLV, val);
+-      spin_unlock_irqrestore(&sch->lock, flags);
+-
+-      return 0;
++      raw_spin_unlock_irqrestore(&sch->lock, flags);
+ }
+ static int sch_gpio_direction_out(struct gpio_chip *gc, unsigned int gpio_num,
+@@ -134,9 +132,9 @@ static int sch_gpio_direction_out(struct
+       struct sch_gpio *sch = gpiochip_get_data(gc);
+       unsigned long flags;
+-      spin_lock_irqsave(&sch->lock, flags);
++      raw_spin_lock_irqsave(&sch->lock, flags);
+       sch_gpio_reg_set(sch, gpio_num, GIO, 0);
+-      spin_unlock_irqrestore(&sch->lock, flags);
++      raw_spin_unlock_irqrestore(&sch->lock, flags);
+       /*
+        * according to the datasheet, writing to the level register has no
+@@ -147,7 +145,8 @@ static int sch_gpio_direction_out(struct
+        * But we cannot prevent a short low pulse if direction is set to high
+        * and an external pull-up is connected.
+        */
+-      return sch_gpio_set(gc, gpio_num, val);
++      sch_gpio_set(gc, gpio_num, val);
++      return 0;
+ }
+ static int sch_gpio_get_direction(struct gpio_chip *gc, unsigned int gpio_num)
+@@ -166,7 +165,7 @@ static const struct gpio_chip sch_gpio_c
+       .direction_input        = sch_gpio_direction_in,
+       .get                    = sch_gpio_get,
+       .direction_output       = sch_gpio_direction_out,
+-      .set_rv                 = sch_gpio_set,
++      .set                    = sch_gpio_set,
+       .get_direction          = sch_gpio_get_direction,
+ };
+@@ -195,14 +194,14 @@ static int sch_irq_type(struct irq_data
+               return -EINVAL;
+       }
+-      spin_lock_irqsave(&sch->lock, flags);
++      raw_spin_lock_irqsave(&sch->lock, flags);
+       sch_gpio_reg_set(sch, gpio_num, GTPE, rising);
+       sch_gpio_reg_set(sch, gpio_num, GTNE, falling);
+       irq_set_handler_locked(d, handle_edge_irq);
+-      spin_unlock_irqrestore(&sch->lock, flags);
++      raw_spin_unlock_irqrestore(&sch->lock, flags);
+       return 0;
+ }
+@@ -214,9 +213,9 @@ static void sch_irq_ack(struct irq_data
+       irq_hw_number_t gpio_num = irqd_to_hwirq(d);
+       unsigned long flags;
+-      spin_lock_irqsave(&sch->lock, flags);
++      raw_spin_lock_irqsave(&sch->lock, flags);
+       sch_gpio_reg_set(sch, gpio_num, GTS, 1);
+-      spin_unlock_irqrestore(&sch->lock, flags);
++      raw_spin_unlock_irqrestore(&sch->lock, flags);
+ }
+ static void sch_irq_mask_unmask(struct irq_data *d, int val)
+@@ -226,9 +225,9 @@ static void sch_irq_mask_unmask(struct i
+       irq_hw_number_t gpio_num = irqd_to_hwirq(d);
+       unsigned long flags;
+-      spin_lock_irqsave(&sch->lock, flags);
++      raw_spin_lock_irqsave(&sch->lock, flags);
+       sch_gpio_reg_set(sch, gpio_num, GGPE, val);
+-      spin_unlock_irqrestore(&sch->lock, flags);
++      raw_spin_unlock_irqrestore(&sch->lock, flags);
+ }
+ static void sch_irq_mask(struct irq_data *d)
+@@ -251,12 +250,12 @@ static u32 sch_gpio_gpe_handler(acpi_han
+       int offset;
+       u32 ret;
+-      spin_lock_irqsave(&sch->lock, flags);
++      raw_spin_lock_irqsave(&sch->lock, flags);
+       core_status = inl(sch->iobase + CORE_BANK_OFFSET + GTS);
+       resume_status = inl(sch->iobase + RESUME_BANK_OFFSET + GTS);
+-      spin_unlock_irqrestore(&sch->lock, flags);
++      raw_spin_unlock_irqrestore(&sch->lock, flags);
+       pending = (resume_status << sch->resume_base) | core_status;
+       for_each_set_bit(offset, &pending, sch->chip.ngpio)
+@@ -322,7 +321,7 @@ static int sch_gpio_probe(struct platfor
+                                pdev->name))
+               return -EBUSY;
+-      spin_lock_init(&sch->lock);
++      raw_spin_lock_init(&sch->lock);
+       sch->iobase = res->start;
+       sch->chip = sch_gpio_chip;
+       sch->chip.label = dev_name(&pdev->dev);
diff --git a/queue-5.15/hfs-hfsplus-fix-u32-overflow-in-check_and_correct_requested_length.patch b/queue-5.15/hfs-hfsplus-fix-u32-overflow-in-check_and_correct_requested_length.patch
new file mode 100644 (file)
index 0000000..e04f8b0
--- /dev/null
@@ -0,0 +1,69 @@
+From stable+bounces-277296-greg=kroah.com@vger.kernel.org Sat Jul 18 13:44:37 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 18 Jul 2026 07:44:27 -0400
+Subject: hfs/hfsplus: fix u32 overflow in check_and_correct_requested_length
+To: stable@vger.kernel.org
+Cc: Tristan Madani <tristan@talencesecurity.com>, syzbot+6df204b70bf3261691c5@syzkaller.appspotmail.com, syzbot+e76bf3d19b85350571ac@syzkaller.appspotmail.com, Viacheslav Dubeyko <slava@dubeyko.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260718114427.3045393-2-sashal@kernel.org>
+
+From: Tristan Madani <tristan@talencesecurity.com>
+
+[ Upstream commit 966cb76fb2857a4242cab6ea2ea17acf818a3da7 ]
+
+check_and_correct_requested_length() compares (off + len) against
+node_size using u32 arithmetic.  When the caller passes a large len
+value (e.g. from an underflowed subtraction in hfs_brec_remove()),
+off + len can wrap past 2^32 and produce a small result, causing the
+bounds check to pass when it should fail.
+
+For example, with off=14 and len=0xFFFFFFF2 (underflowed from
+data_off - keyoffset - size in hfs_brec_remove), off + len wraps to 6,
+which is less than a typical node_size of 512, so the check passes and
+the subsequent memmove reads ~4GB past the node buffer.
+
+Fix this by widening the addition to u64 before comparing against
+node_size.  This prevents the u32 wrap while keeping the logic
+straightforward.
+
+Reported-by: syzbot+6df204b70bf3261691c5@syzkaller.appspotmail.com
+Closes: https://syzkaller.appspot.com/bug?extid=6df204b70bf3261691c5
+Tested-by: syzbot+6df204b70bf3261691c5@syzkaller.appspotmail.com
+Reported-by: syzbot+e76bf3d19b85350571ac@syzkaller.appspotmail.com
+Closes: https://syzkaller.appspot.com/bug?extid=e76bf3d19b85350571ac
+Tested-by: syzbot+e76bf3d19b85350571ac@syzkaller.appspotmail.com
+Fixes: a431930c9bac ("hfs: fix slab-out-of-bounds in hfs_bnode_read()")
+Cc: stable@vger.kernel.org
+Signed-off-by: Tristan Madani <tristan@talencesecurity.com>
+Reviewed-by: Viacheslav Dubeyko <slava@dubeyko.com>
+Signed-off-by: Viacheslav Dubeyko <slava@dubeyko.com>
+Link: https://lore.kernel.org/r/20260505111300.3592757-2-tristmd@gmail.com
+Signed-off-by: Viacheslav Dubeyko <slava@dubeyko.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/hfs/bnode.c          |    2 +-
+ fs/hfsplus/hfsplus_fs.h |    2 +-
+ 2 files changed, 2 insertions(+), 2 deletions(-)
+
+--- a/fs/hfs/bnode.c
++++ b/fs/hfs/bnode.c
+@@ -41,7 +41,7 @@ u32 check_and_correct_requested_length(s
+       node_size = node->tree->node_size;
+-      if ((off + len) > node_size) {
++      if ((u64)off + len > node_size) {
+               u32 new_len = node_size - off;
+               pr_err("requested length has been corrected: "
+--- a/fs/hfsplus/hfsplus_fs.h
++++ b/fs/hfsplus/hfsplus_fs.h
+@@ -611,7 +611,7 @@ u32 check_and_correct_requested_length(s
+       node_size = node->tree->node_size;
+-      if ((off + len) > node_size) {
++      if ((u64)off + len > node_size) {
+               u32 new_len = node_size - off;
+               pr_err("requested length has been corrected: "
diff --git a/queue-5.15/hfs-hfsplus-prevent-getting-negative-values-of-offset-length.patch b/queue-5.15/hfs-hfsplus-prevent-getting-negative-values-of-offset-length.patch
new file mode 100644 (file)
index 0000000..53b7fec
--- /dev/null
@@ -0,0 +1,958 @@
+From stable+bounces-277297-greg=kroah.com@vger.kernel.org Sat Jul 18 13:44:43 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 18 Jul 2026 07:44:26 -0400
+Subject: hfs/hfsplus: prevent getting negative values of offset/length
+To: stable@vger.kernel.org
+Cc: Viacheslav Dubeyko <slava@dubeyko.com>, John Paul Adrian Glaubitz <glaubitz@physik.fu-berlin.de>, Yangtao Li <frank.li@vivo.com>, linux-fsdevel@vger.kernel.org, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260718114427.3045393-1-sashal@kernel.org>
+
+From: Viacheslav Dubeyko <slava@dubeyko.com>
+
+[ Upstream commit 00c14a09a70e10ae18eb3707d0059291425c04bd ]
+
+The syzbot reported KASAN out-of-bounds issue in
+hfs_bnode_move():
+
+[   45.588165][ T9821] hfs: dst 14, src 65536, len -65536
+[   45.588895][ T9821] ==================================================================
+[   45.590114][ T9821] BUG: KASAN: out-of-bounds in hfs_bnode_move+0xfd/0x140
+[   45.591127][ T9821] Read of size 18446744073709486080 at addr ffff888035935400 by task repro/9821
+[   45.592207][ T9821]
+[   45.592420][ T9821] CPU: 0 UID: 0 PID: 9821 Comm: repro Not tainted 6.16.0-rc7-dirty #42 PREEMPT(full)
+[   45.592428][ T9821] Hardware name: QEMU Ubuntu 24.04 PC (i440FX + PIIX, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
+[   45.592431][ T9821] Call Trace:
+[   45.592434][ T9821]  <TASK>
+[   45.592437][ T9821]  dump_stack_lvl+0x1c1/0x2a0
+[   45.592446][ T9821]  ? __virt_addr_valid+0x1c8/0x5c0
+[   45.592454][ T9821]  ? __pfx_dump_stack_lvl+0x10/0x10
+[   45.592461][ T9821]  ? rcu_is_watching+0x15/0xb0
+[   45.592469][ T9821]  ? lock_release+0x4b/0x3e0
+[   45.592476][ T9821]  ? __virt_addr_valid+0x1c8/0x5c0
+[   45.592483][ T9821]  ? __virt_addr_valid+0x4a5/0x5c0
+[   45.592491][ T9821]  print_report+0x17e/0x7c0
+[   45.592497][ T9821]  ? __virt_addr_valid+0x1c8/0x5c0
+[   45.592504][ T9821]  ? __virt_addr_valid+0x4a5/0x5c0
+[   45.592511][ T9821]  ? __phys_addr+0xd3/0x180
+[   45.592519][ T9821]  ? hfs_bnode_move+0xfd/0x140
+[   45.592526][ T9821]  kasan_report+0x147/0x180
+[   45.592531][ T9821]  ? _printk+0xcf/0x120
+[   45.592537][ T9821]  ? hfs_bnode_move+0xfd/0x140
+[   45.592544][ T9821]  ? hfs_bnode_move+0xfd/0x140
+[   45.592552][ T9821]  kasan_check_range+0x2b0/0x2c0
+[   45.592557][ T9821]  ? hfs_bnode_move+0xfd/0x140
+[   45.592565][ T9821]  __asan_memmove+0x29/0x70
+[   45.592572][ T9821]  hfs_bnode_move+0xfd/0x140
+[   45.592580][ T9821]  hfs_brec_remove+0x473/0x560
+[   45.592589][ T9821]  hfs_cat_move+0x6fb/0x960
+[   45.592598][ T9821]  ? __pfx_hfs_cat_move+0x10/0x10
+[   45.592607][ T9821]  ? seqcount_lockdep_reader_access+0x122/0x1c0
+[   45.592614][ T9821]  ? lockdep_hardirqs_on+0x9c/0x150
+[   45.592631][ T9821]  ? __lock_acquire+0xaec/0xd80
+[   45.592641][ T9821]  hfs_rename+0x1dc/0x2d0
+[   45.592649][ T9821]  ? __pfx_hfs_rename+0x10/0x10
+[   45.592657][ T9821]  vfs_rename+0xac6/0xed0
+[   45.592664][ T9821]  ? __pfx_vfs_rename+0x10/0x10
+[   45.592670][ T9821]  ? d_alloc+0x144/0x190
+[   45.592677][ T9821]  ? bpf_lsm_path_rename+0x9/0x20
+[   45.592683][ T9821]  ? security_path_rename+0x17d/0x490
+[   45.592691][ T9821]  do_renameat2+0x890/0xc50
+[   45.592699][ T9821]  ? __pfx_do_renameat2+0x10/0x10
+[   45.592707][ T9821]  ? getname_flags+0x1e5/0x540
+[   45.592714][ T9821]  __x64_sys_rename+0x82/0x90
+[   45.592720][ T9821]  ? entry_SYSCALL_64_after_hwframe+0x77/0x7f
+[   45.592725][ T9821]  do_syscall_64+0xf3/0x3a0
+[   45.592741][ T9821]  ? exc_page_fault+0x9f/0xf0
+[   45.592748][ T9821]  entry_SYSCALL_64_after_hwframe+0x77/0x7f
+[   45.592754][ T9821] RIP: 0033:0x7f7f73fe3fc9
+[   45.592760][ T9821] Code: 00 c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 48
+[   45.592765][ T9821] RSP: 002b:00007ffc7e116cf8 EFLAGS: 00000283 ORIG_RAX: 0000000000000052
+[   45.592772][ T9821] RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 00007f7f73fe3fc9
+[   45.592776][ T9821] RDX: 0000200000000871 RSI: 0000200000000780 RDI: 00002000000003c0
+[   45.592781][ T9821] RBP: 00007ffc7e116d00 R08: 0000000000000000 R09: 00007ffc7e116d30
+[   45.592784][ T9821] R10: fffffffffffffff0 R11: 0000000000000283 R12: 00005557e81f8250
+[   45.592788][ T9821] R13: 0000000000000000 R14: 0000000000000000 R15: 0000000000000000
+[   45.592795][ T9821]  </TASK>
+[   45.592797][ T9821]
+[   45.619721][ T9821] The buggy address belongs to the physical page:
+[   45.620300][ T9821] page: refcount:1 mapcount:1 mapping:0000000000000000 index:0x559a88174 pfn:0x35935
+[   45.621150][ T9821] memcg:ffff88810a1d5b00
+[   45.621531][ T9821] anon flags: 0xfff60000020838(uptodate|dirty|lru|owner_2|swapbacked|node=0|zone=1|lastcpupid=0x7ff)
+[   45.622496][ T9821] raw: 00fff60000020838 ffffea0000d64d88 ffff888021753e10 ffff888029da0771
+[   45.623260][ T9821] raw: 0000000559a88174 0000000000000000 0000000100000000 ffff88810a1d5b00
+[   45.624030][ T9821] page dumped because: kasan: bad access detected
+[   45.624602][ T9821] page_owner tracks the page as allocated
+[   45.625115][ T9821] page last allocated via order 0, migratetype Movable, gfp_mask 0x140dca(GFP_HIGHUSER_MOVABLE|__GFP_ZERO0
+[   45.626685][ T9821]  post_alloc_hook+0x240/0x2a0
+[   45.627127][ T9821]  get_page_from_freelist+0x2101/0x21e0
+[   45.627628][ T9821]  __alloc_frozen_pages_noprof+0x274/0x380
+[   45.628154][ T9821]  alloc_pages_mpol+0x241/0x4b0
+[   45.628593][ T9821]  vma_alloc_folio_noprof+0xe4/0x210
+[   45.629066][ T9821]  folio_prealloc+0x30/0x180
+[   45.629487][ T9821]  __handle_mm_fault+0x34bd/0x5640
+[   45.629957][ T9821]  handle_mm_fault+0x40e/0x8e0
+[   45.630392][ T9821]  do_user_addr_fault+0xa81/0x1390
+[   45.630862][ T9821]  exc_page_fault+0x76/0xf0
+[   45.631273][ T9821]  asm_exc_page_fault+0x26/0x30
+[   45.631712][ T9821] page last free pid 5269 tgid 5269 stack trace:
+[   45.632281][ T9821]  free_unref_folios+0xc73/0x14c0
+[   45.632740][ T9821]  folios_put_refs+0x55b/0x640
+[   45.633177][ T9821]  free_pages_and_swap_cache+0x26d/0x510
+[   45.633685][ T9821]  tlb_flush_mmu+0x3a0/0x680
+[   45.634105][ T9821]  tlb_finish_mmu+0xd4/0x200
+[   45.634525][ T9821]  exit_mmap+0x44c/0xb70
+[   45.634914][ T9821]  __mmput+0x118/0x420
+[   45.635286][ T9821]  exit_mm+0x1da/0x2c0
+[   45.635659][ T9821]  do_exit+0x652/0x2330
+[   45.636039][ T9821]  do_group_exit+0x21c/0x2d0
+[   45.636457][ T9821]  __x64_sys_exit_group+0x3f/0x40
+[   45.636915][ T9821]  x64_sys_call+0x21ba/0x21c0
+[   45.637342][ T9821]  do_syscall_64+0xf3/0x3a0
+[   45.637756][ T9821]  entry_SYSCALL_64_after_hwframe+0x77/0x7f
+[   45.638290][ T9821] page has been migrated, last migrate reason: numa_misplaced
+[   45.638956][ T9821]
+[   45.639173][ T9821] Memory state around the buggy address:
+[   45.639677][ T9821]  ffff888035935300: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+[   45.640397][ T9821]  ffff888035935380: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+[   45.641117][ T9821] >ffff888035935400: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+[   45.641837][ T9821]                    ^
+[   45.642207][ T9821]  ffff888035935480: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+[   45.642929][ T9821]  ffff888035935500: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+[   45.643650][ T9821] ==================================================================
+
+This commit [1] fixes the issue if an offset inside of b-tree node
+or length of the request is bigger than b-tree node. However,
+this fix is still not ready for negative values
+of the offset or length. Moreover, negative values of
+the offset or length doesn't make sense for b-tree's
+operations. Because we could try to access the memory address
+outside of the beginning of memory page's addresses range.
+Also, using of negative values make logic very complicated,
+unpredictable, and we could access the wrong item(s)
+in the b-tree node.
+
+This patch changes b-tree interface by means of converting
+signed integer arguments of offset and length on u32 type.
+Such conversion has goal to prevent of using negative values
+unintentionally or by mistake in b-tree operations.
+
+[1] 'commit a431930c9bac ("hfs: fix slab-out-of-bounds in hfs_bnode_read()")'
+
+Signed-off-by: Viacheslav Dubeyko <slava@dubeyko.com>
+cc: John Paul Adrian Glaubitz <glaubitz@physik.fu-berlin.de>
+cc: Yangtao Li <frank.li@vivo.com>
+cc: linux-fsdevel@vger.kernel.org
+Link: https://lore.kernel.org/r/20251002200020.2578311-1-slava@dubeyko.com
+Signed-off-by: Viacheslav Dubeyko <slava@dubeyko.com>
+Stable-dep-of: 966cb76fb285 ("hfs/hfsplus: fix u32 overflow in check_and_correct_requested_length")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/hfs/bfind.c          |    2 -
+ fs/hfs/bnode.c          |   52 ++++++++++++++---------------
+ fs/hfs/brec.c           |    2 -
+ fs/hfs/btree.c          |    2 -
+ fs/hfs/btree.h          |   71 ++++++++++++++++++++--------------------
+ fs/hfs/hfs_fs.h         |   85 ++++++++++++++++++++++++++++--------------------
+ fs/hfs/inode.c          |    2 -
+ fs/hfsplus/bfind.c      |    2 -
+ fs/hfsplus/bnode.c      |   60 ++++++++++++++++-----------------
+ fs/hfsplus/brec.c       |    2 -
+ fs/hfsplus/btree.c      |    2 -
+ fs/hfsplus/hfsplus_fs.h |   38 ++++++++++-----------
+ 12 files changed, 168 insertions(+), 152 deletions(-)
+
+--- a/fs/hfs/bfind.c
++++ b/fs/hfs/bfind.c
+@@ -164,7 +164,7 @@ release:
+       return res;
+ }
+-int hfs_brec_read(struct hfs_find_data *fd, void *rec, int rec_len)
++int hfs_brec_read(struct hfs_find_data *fd, void *rec, u32 rec_len)
+ {
+       int res;
+--- a/fs/hfs/bnode.c
++++ b/fs/hfs/bnode.c
+@@ -16,14 +16,14 @@
+ #include "btree.h"
+ static inline
+-bool is_bnode_offset_valid(struct hfs_bnode *node, int off)
++bool is_bnode_offset_valid(struct hfs_bnode *node, u32 off)
+ {
+       bool is_valid = off < node->tree->node_size;
+       if (!is_valid) {
+               pr_err("requested invalid offset: "
+                      "NODE: id %u, type %#x, height %u, "
+-                     "node_size %u, offset %d\n",
++                     "node_size %u, offset %u\n",
+                      node->this, node->type, node->height,
+                      node->tree->node_size, off);
+       }
+@@ -32,7 +32,7 @@ bool is_bnode_offset_valid(struct hfs_bn
+ }
+ static inline
+-int check_and_correct_requested_length(struct hfs_bnode *node, int off, int len)
++u32 check_and_correct_requested_length(struct hfs_bnode *node, u32 off, u32 len)
+ {
+       unsigned int node_size;
+@@ -42,12 +42,12 @@ int check_and_correct_requested_length(s
+       node_size = node->tree->node_size;
+       if ((off + len) > node_size) {
+-              int new_len = (int)node_size - off;
++              u32 new_len = node_size - off;
+               pr_err("requested length has been corrected: "
+                      "NODE: id %u, type %#x, height %u, "
+-                     "node_size %u, offset %d, "
+-                     "requested_len %d, corrected_len %d\n",
++                     "node_size %u, offset %u, "
++                     "requested_len %u, corrected_len %u\n",
+                      node->this, node->type, node->height,
+                      node->tree->node_size, off, len, new_len);
+@@ -57,12 +57,12 @@ int check_and_correct_requested_length(s
+       return len;
+ }
+-void hfs_bnode_read(struct hfs_bnode *node, void *buf, int off, int len)
++void hfs_bnode_read(struct hfs_bnode *node, void *buf, u32 off, u32 len)
+ {
+       struct page *page;
+-      int pagenum;
+-      int bytes_read;
+-      int bytes_to_read;
++      u32 pagenum;
++      u32 bytes_read;
++      u32 bytes_to_read;
+       void *vaddr;
+       memset(buf, 0, len);
+@@ -73,7 +73,7 @@ void hfs_bnode_read(struct hfs_bnode *no
+       if (len == 0) {
+               pr_err("requested zero length: "
+                      "NODE: id %u, type %#x, height %u, "
+-                     "node_size %u, offset %d, len %d\n",
++                     "node_size %u, offset %u, len %u\n",
+                      node->this, node->type, node->height,
+                      node->tree->node_size, off, len);
+               return;
+@@ -89,7 +89,7 @@ void hfs_bnode_read(struct hfs_bnode *no
+               if (pagenum >= node->tree->pages_per_bnode)
+                       break;
+               page = node->page[pagenum];
+-              bytes_to_read = min_t(int, len - bytes_read, PAGE_SIZE - off);
++              bytes_to_read = min_t(u32, len - bytes_read, PAGE_SIZE - off);
+               vaddr = kmap_atomic(page);
+               memcpy(buf + bytes_read, vaddr + off, bytes_to_read);
+@@ -100,7 +100,7 @@ void hfs_bnode_read(struct hfs_bnode *no
+       }
+ }
+-u16 hfs_bnode_read_u16(struct hfs_bnode *node, int off)
++u16 hfs_bnode_read_u16(struct hfs_bnode *node, u32 off)
+ {
+       __be16 data;
+       // optimize later...
+@@ -108,7 +108,7 @@ u16 hfs_bnode_read_u16(struct hfs_bnode
+       return be16_to_cpu(data);
+ }
+-u8 hfs_bnode_read_u8(struct hfs_bnode *node, int off)
++u8 hfs_bnode_read_u8(struct hfs_bnode *node, u32 off)
+ {
+       u8 data;
+       // optimize later...
+@@ -116,10 +116,10 @@ u8 hfs_bnode_read_u8(struct hfs_bnode *n
+       return data;
+ }
+-void hfs_bnode_read_key(struct hfs_bnode *node, void *key, int off)
++void hfs_bnode_read_key(struct hfs_bnode *node, void *key, u32 off)
+ {
+       struct hfs_btree *tree;
+-      int key_len;
++      u32 key_len;
+       tree = node->tree;
+       if (node->type == HFS_NODE_LEAF ||
+@@ -130,14 +130,14 @@ void hfs_bnode_read_key(struct hfs_bnode
+       if (key_len > sizeof(hfs_btree_key) || key_len < 1) {
+               memset(key, 0, sizeof(hfs_btree_key));
+-              pr_err("hfs: Invalid key length: %d\n", key_len);
++              pr_err("hfs: Invalid key length: %u\n", key_len);
+               return;
+       }
+       hfs_bnode_read(node, key, off, key_len);
+ }
+-void hfs_bnode_write(struct hfs_bnode *node, void *buf, int off, int len)
++void hfs_bnode_write(struct hfs_bnode *node, void *buf, u32 off, u32 len)
+ {
+       struct page *page;
+@@ -147,7 +147,7 @@ void hfs_bnode_write(struct hfs_bnode *n
+       if (len == 0) {
+               pr_err("requested zero length: "
+                      "NODE: id %u, type %#x, height %u, "
+-                     "node_size %u, offset %d, len %d\n",
++                     "node_size %u, offset %u, len %u\n",
+                      node->this, node->type, node->height,
+                      node->tree->node_size, off, len);
+               return;
+@@ -163,20 +163,20 @@ void hfs_bnode_write(struct hfs_bnode *n
+       set_page_dirty(page);
+ }
+-void hfs_bnode_write_u16(struct hfs_bnode *node, int off, u16 data)
++void hfs_bnode_write_u16(struct hfs_bnode *node, u32 off, u16 data)
+ {
+       __be16 v = cpu_to_be16(data);
+       // optimize later...
+       hfs_bnode_write(node, &v, off, 2);
+ }
+-void hfs_bnode_write_u8(struct hfs_bnode *node, int off, u8 data)
++void hfs_bnode_write_u8(struct hfs_bnode *node, u32 off, u8 data)
+ {
+       // optimize later...
+       hfs_bnode_write(node, &data, off, 1);
+ }
+-void hfs_bnode_clear(struct hfs_bnode *node, int off, int len)
++void hfs_bnode_clear(struct hfs_bnode *node, u32 off, u32 len)
+ {
+       struct page *page;
+@@ -186,7 +186,7 @@ void hfs_bnode_clear(struct hfs_bnode *n
+       if (len == 0) {
+               pr_err("requested zero length: "
+                      "NODE: id %u, type %#x, height %u, "
+-                     "node_size %u, offset %d, len %d\n",
++                     "node_size %u, offset %u, len %u\n",
+                      node->this, node->type, node->height,
+                      node->tree->node_size, off, len);
+               return;
+@@ -202,8 +202,8 @@ void hfs_bnode_clear(struct hfs_bnode *n
+       set_page_dirty(page);
+ }
+-void hfs_bnode_copy(struct hfs_bnode *dst_node, int dst,
+-              struct hfs_bnode *src_node, int src, int len)
++void hfs_bnode_copy(struct hfs_bnode *dst_node, u32 dst,
++                  struct hfs_bnode *src_node, u32 src, u32 len)
+ {
+       struct page *src_page, *dst_page;
+@@ -225,7 +225,7 @@ void hfs_bnode_copy(struct hfs_bnode *ds
+       set_page_dirty(dst_page);
+ }
+-void hfs_bnode_move(struct hfs_bnode *node, int dst, int src, int len)
++void hfs_bnode_move(struct hfs_bnode *node, u32 dst, u32 src, u32 len)
+ {
+       struct page *page;
+       void *ptr;
+--- a/fs/hfs/brec.c
++++ b/fs/hfs/brec.c
+@@ -62,7 +62,7 @@ u16 hfs_brec_keylen(struct hfs_bnode *no
+       return retval;
+ }
+-int hfs_brec_insert(struct hfs_find_data *fd, void *entry, int entry_len)
++int hfs_brec_insert(struct hfs_find_data *fd, void *entry, u32 entry_len)
+ {
+       struct hfs_btree *tree;
+       struct hfs_bnode *node, *new_node;
+--- a/fs/hfs/btree.c
++++ b/fs/hfs/btree.c
+@@ -221,7 +221,7 @@ static struct hfs_bnode *hfs_bmap_new_bm
+ }
+ /* Make sure @tree has enough space for the @rsvd_nodes */
+-int hfs_bmap_reserve(struct hfs_btree *tree, int rsvd_nodes)
++int hfs_bmap_reserve(struct hfs_btree *tree, u32 rsvd_nodes)
+ {
+       struct inode *inode = tree->inode;
+       u32 count;
+--- a/fs/hfs/btree.h
++++ b/fs/hfs/btree.h
+@@ -86,48 +86,49 @@ struct hfs_find_data {
+ /* btree.c */
+-extern struct hfs_btree *hfs_btree_open(struct super_block *, u32, btree_keycmp);
+-extern void hfs_btree_close(struct hfs_btree *);
+-extern void hfs_btree_write(struct hfs_btree *);
+-extern int hfs_bmap_reserve(struct hfs_btree *, int);
+-extern struct hfs_bnode * hfs_bmap_alloc(struct hfs_btree *);
++extern struct hfs_btree *hfs_btree_open(struct super_block *sb, u32 id,
++                                      btree_keycmp keycmp);
++extern void hfs_btree_close(struct hfs_btree *tree);
++extern void hfs_btree_write(struct hfs_btree *tree);
++extern int hfs_bmap_reserve(struct hfs_btree *tree, u32 rsvd_nodes);
++extern struct hfs_bnode *hfs_bmap_alloc(struct hfs_btree *tree);
+ extern void hfs_bmap_free(struct hfs_bnode *node);
+ /* bnode.c */
+-extern void hfs_bnode_read(struct hfs_bnode *, void *, int, int);
+-extern u16 hfs_bnode_read_u16(struct hfs_bnode *, int);
+-extern u8 hfs_bnode_read_u8(struct hfs_bnode *, int);
+-extern void hfs_bnode_read_key(struct hfs_bnode *, void *, int);
+-extern void hfs_bnode_write(struct hfs_bnode *, void *, int, int);
+-extern void hfs_bnode_write_u16(struct hfs_bnode *, int, u16);
+-extern void hfs_bnode_write_u8(struct hfs_bnode *, int, u8);
+-extern void hfs_bnode_clear(struct hfs_bnode *, int, int);
+-extern void hfs_bnode_copy(struct hfs_bnode *, int,
+-                         struct hfs_bnode *, int, int);
+-extern void hfs_bnode_move(struct hfs_bnode *, int, int, int);
+-extern void hfs_bnode_dump(struct hfs_bnode *);
+-extern void hfs_bnode_unlink(struct hfs_bnode *);
+-extern struct hfs_bnode *hfs_bnode_findhash(struct hfs_btree *, u32);
+-extern struct hfs_bnode *hfs_bnode_find(struct hfs_btree *, u32);
+-extern void hfs_bnode_unhash(struct hfs_bnode *);
+-extern void hfs_bnode_free(struct hfs_bnode *);
+-extern struct hfs_bnode *hfs_bnode_create(struct hfs_btree *, u32);
+-extern void hfs_bnode_get(struct hfs_bnode *);
+-extern void hfs_bnode_put(struct hfs_bnode *);
++extern void hfs_bnode_read(struct hfs_bnode *node, void *buf, u32 off, u32 len);
++extern u16 hfs_bnode_read_u16(struct hfs_bnode *node, u32 off);
++extern u8 hfs_bnode_read_u8(struct hfs_bnode *node, u32 off);
++extern void hfs_bnode_read_key(struct hfs_bnode *node, void *key, u32 off);
++extern void hfs_bnode_write(struct hfs_bnode *node, void *buf, u32 off, u32 len);
++extern void hfs_bnode_write_u16(struct hfs_bnode *node, u32 off, u16 data);
++extern void hfs_bnode_write_u8(struct hfs_bnode *node, u32 off, u8 data);
++extern void hfs_bnode_clear(struct hfs_bnode *node, u32 off, u32 len);
++extern void hfs_bnode_copy(struct hfs_bnode *dst_node, u32 dst,
++                         struct hfs_bnode *src_node, u32 src, u32 len);
++extern void hfs_bnode_move(struct hfs_bnode *node, u32 dst, u32 src, u32 len);
++extern void hfs_bnode_dump(struct hfs_bnode *node);
++extern void hfs_bnode_unlink(struct hfs_bnode *node);
++extern struct hfs_bnode *hfs_bnode_findhash(struct hfs_btree *tree, u32 cnid);
++extern struct hfs_bnode *hfs_bnode_find(struct hfs_btree *tree, u32 num);
++extern void hfs_bnode_unhash(struct hfs_bnode *node);
++extern void hfs_bnode_free(struct hfs_bnode *node);
++extern struct hfs_bnode *hfs_bnode_create(struct hfs_btree *tree, u32 num);
++extern void hfs_bnode_get(struct hfs_bnode *node);
++extern void hfs_bnode_put(struct hfs_bnode *node);
+ /* brec.c */
+-extern u16 hfs_brec_lenoff(struct hfs_bnode *, u16, u16 *);
+-extern u16 hfs_brec_keylen(struct hfs_bnode *, u16);
+-extern int hfs_brec_insert(struct hfs_find_data *, void *, int);
+-extern int hfs_brec_remove(struct hfs_find_data *);
++extern u16 hfs_brec_lenoff(struct hfs_bnode *node, u16 rec, u16 *off);
++extern u16 hfs_brec_keylen(struct hfs_bnode *node, u16 rec);
++extern int hfs_brec_insert(struct hfs_find_data *fd, void *entry, u32 entry_len);
++extern int hfs_brec_remove(struct hfs_find_data *fd);
+ /* bfind.c */
+-extern int hfs_find_init(struct hfs_btree *, struct hfs_find_data *);
+-extern void hfs_find_exit(struct hfs_find_data *);
+-extern int __hfs_brec_find(struct hfs_bnode *, struct hfs_find_data *);
+-extern int hfs_brec_find(struct hfs_find_data *);
+-extern int hfs_brec_read(struct hfs_find_data *, void *, int);
+-extern int hfs_brec_goto(struct hfs_find_data *, int);
++extern int hfs_find_init(struct hfs_btree *tree, struct hfs_find_data *fd);
++extern void hfs_find_exit(struct hfs_find_data *fd);
++extern int __hfs_brec_find(struct hfs_bnode *bnode, struct hfs_find_data *fd);
++extern int hfs_brec_find(struct hfs_find_data *fd);
++extern int hfs_brec_read(struct hfs_find_data *fd, void *rec, u32 rec_len);
++extern int hfs_brec_goto(struct hfs_find_data *fd, int cnt);
+ struct hfs_bnode_desc {
+--- a/fs/hfs/hfs_fs.h
++++ b/fs/hfs/hfs_fs.h
+@@ -171,71 +171,86 @@ struct hfs_sb_info {
+ #define HFS_FLG_ALT_MDB_DIRTY 2
+ /* bitmap.c */
+-extern u32 hfs_vbm_search_free(struct super_block *, u32, u32 *);
+-extern int hfs_clear_vbm_bits(struct super_block *, u16, u16);
++extern u32 hfs_vbm_search_free(struct super_block *sb, u32 goal, u32 *num_bits);
++extern int hfs_clear_vbm_bits(struct super_block *sb, u16 start, u16 count);
+ /* catalog.c */
+-extern int hfs_cat_keycmp(const btree_key *, const btree_key *);
++extern int hfs_cat_keycmp(const btree_key *key1, const btree_key *key2);
+ struct hfs_find_data;
+-extern int hfs_cat_find_brec(struct super_block *, u32, struct hfs_find_data *);
+-extern int hfs_cat_create(u32, struct inode *, const struct qstr *, struct inode *);
+-extern int hfs_cat_delete(u32, struct inode *, const struct qstr *);
+-extern int hfs_cat_move(u32, struct inode *, const struct qstr *,
+-                      struct inode *, const struct qstr *);
+-extern void hfs_cat_build_key(struct super_block *, btree_key *, u32, const struct qstr *);
++extern int hfs_cat_find_brec(struct super_block *sb, u32 cnid,
++                           struct hfs_find_data *fd);
++extern int hfs_cat_create(u32 cnid, struct inode *dir,
++                        const struct qstr *str, struct inode *inode);
++extern int hfs_cat_delete(u32 cnid, struct inode *dir, const struct qstr *str);
++extern int hfs_cat_move(u32 cnid, struct inode *src_dir,
++                      const struct qstr *src_name,
++                      struct inode *dst_dir,
++                      const struct qstr *dst_name);
++extern void hfs_cat_build_key(struct super_block *sb, btree_key *key,
++                            u32 parent, const struct qstr *name);
+ /* dir.c */
+ extern const struct file_operations hfs_dir_operations;
+ extern const struct inode_operations hfs_dir_inode_operations;
+ /* extent.c */
+-extern int hfs_ext_keycmp(const btree_key *, const btree_key *);
+-extern int hfs_free_fork(struct super_block *, struct hfs_cat_file *, int);
+-extern int hfs_ext_write_extent(struct inode *);
+-extern int hfs_extend_file(struct inode *);
+-extern void hfs_file_truncate(struct inode *);
++extern int hfs_ext_keycmp(const btree_key *key1, const btree_key *key2);
++extern int hfs_free_fork(struct super_block *sb,
++                       struct hfs_cat_file *file, int type);
++extern int hfs_ext_write_extent(struct inode *inode);
++extern int hfs_extend_file(struct inode *inode);
++extern void hfs_file_truncate(struct inode *inode);
+-extern int hfs_get_block(struct inode *, sector_t, struct buffer_head *, int);
++extern int hfs_get_block(struct inode *inode, sector_t block,
++                       struct buffer_head *bh_result, int create);
+ /* inode.c */
+ extern const struct address_space_operations hfs_aops;
+ extern const struct address_space_operations hfs_btree_aops;
+-extern struct inode *hfs_new_inode(struct inode *, const struct qstr *, umode_t);
+-extern void hfs_inode_write_fork(struct inode *, struct hfs_extent *, __be32 *, __be32 *);
+-extern int hfs_write_inode(struct inode *, struct writeback_control *);
+-extern int hfs_inode_setattr(struct user_namespace *, struct dentry *,
+-                           struct iattr *);
++extern struct inode *hfs_new_inode(struct inode *dir, const struct qstr *name,
++                                 umode_t mode);
++extern void hfs_inode_write_fork(struct inode *inode, struct hfs_extent *ext,
++                               __be32 *log_size, __be32 *phys_size);
++extern int hfs_write_inode(struct inode *inode, struct writeback_control *wbc);
++extern int hfs_inode_setattr(struct user_namespace *mnt_userns,
++                           struct dentry *dentry, struct iattr *attr);
+ extern void hfs_inode_read_fork(struct inode *inode, struct hfs_extent *ext,
+-                      __be32 log_size, __be32 phys_size, u32 clump_size);
+-extern struct inode *hfs_iget(struct super_block *, struct hfs_cat_key *, hfs_cat_rec *);
+-extern void hfs_evict_inode(struct inode *);
+-extern void hfs_delete_inode(struct inode *);
++                              __be32 __log_size, __be32 phys_size,
++                              u32 clump_size);
++extern struct inode *hfs_iget(struct super_block *sb, struct hfs_cat_key *key,
++                              hfs_cat_rec *rec);
++extern void hfs_evict_inode(struct inode *inode);
++extern void hfs_delete_inode(struct inode *inode);
+ /* attr.c */
+ extern const struct xattr_handler *hfs_xattr_handlers[];
+ /* mdb.c */
+-extern int hfs_mdb_get(struct super_block *);
+-extern void hfs_mdb_commit(struct super_block *);
+-extern void hfs_mdb_close(struct super_block *);
+-extern void hfs_mdb_put(struct super_block *);
++extern int hfs_mdb_get(struct super_block *sb);
++extern void hfs_mdb_commit(struct super_block *sb);
++extern void hfs_mdb_close(struct super_block *sb);
++extern void hfs_mdb_put(struct super_block *sb);
+ /* part_tbl.c */
+-extern int hfs_part_find(struct super_block *, sector_t *, sector_t *);
++extern int hfs_part_find(struct super_block *sb,
++                       sector_t *part_start, sector_t *part_size);
+ /* string.c */
+ extern const struct dentry_operations hfs_dentry_operations;
+-extern int hfs_hash_dentry(const struct dentry *, struct qstr *);
+-extern int hfs_strcmp(const unsigned char *, unsigned int,
+-                    const unsigned char *, unsigned int);
++extern int hfs_hash_dentry(const struct dentry *dentry, struct qstr *this);
++extern int hfs_strcmp(const unsigned char *s1, unsigned int len1,
++                    const unsigned char *s2, unsigned int len2);
+ extern int hfs_compare_dentry(const struct dentry *dentry,
+-              unsigned int len, const char *str, const struct qstr *name);
++                              unsigned int len, const char *str,
++                              const struct qstr *name);
+ /* trans.c */
+-extern void hfs_asc2mac(struct super_block *, struct hfs_name *, const struct qstr *);
+-extern int hfs_mac2asc(struct super_block *, char *, const struct hfs_name *);
++extern void hfs_asc2mac(struct super_block *sb,
++                      struct hfs_name *out, const struct qstr *in);
++extern int hfs_mac2asc(struct super_block *sb,
++                      char *out, const struct hfs_name *in);
+ /* super.c */
+ extern void hfs_mark_mdb_dirty(struct super_block *sb);
+--- a/fs/hfs/inode.c
++++ b/fs/hfs/inode.c
+@@ -50,7 +50,7 @@ static void hfs_write_failed(struct addr
+ }
+ static int hfs_write_begin(struct file *file, struct address_space *mapping,
+-                      loff_t pos, unsigned len, unsigned flags,
++                      loff_t pos, unsigned int len, unsigned flags,
+                       struct page **pagep, void **fsdata)
+ {
+       int ret;
+--- a/fs/hfsplus/bfind.c
++++ b/fs/hfsplus/bfind.c
+@@ -210,7 +210,7 @@ release:
+       return res;
+ }
+-int hfs_brec_read(struct hfs_find_data *fd, void *rec, int rec_len)
++int hfs_brec_read(struct hfs_find_data *fd, void *rec, u32 rec_len)
+ {
+       int res;
+--- a/fs/hfsplus/bnode.c
++++ b/fs/hfsplus/bnode.c
+@@ -20,10 +20,10 @@
+ /* Copy a specified range of bytes from the raw data of a node */
+-void hfs_bnode_read(struct hfs_bnode *node, void *buf, int off, int len)
++void hfs_bnode_read(struct hfs_bnode *node, void *buf, u32 off, u32 len)
+ {
+       struct page **pagep;
+-      int l;
++      u32 l;
+       memset(buf, 0, len);
+@@ -33,7 +33,7 @@ void hfs_bnode_read(struct hfs_bnode *no
+       if (len == 0) {
+               pr_err("requested zero length: "
+                      "NODE: id %u, type %#x, height %u, "
+-                     "node_size %u, offset %d, len %d\n",
++                     "node_size %u, offset %u, len %u\n",
+                      node->this, node->type, node->height,
+                      node->tree->node_size, off, len);
+               return;
+@@ -45,19 +45,19 @@ void hfs_bnode_read(struct hfs_bnode *no
+       pagep = node->page + (off >> PAGE_SHIFT);
+       off &= ~PAGE_MASK;
+-      l = min_t(int, len, PAGE_SIZE - off);
++      l = min_t(u32, len, PAGE_SIZE - off);
+       memcpy(buf, kmap(*pagep) + off, l);
+       kunmap(*pagep);
+       while ((len -= l) != 0) {
+               buf += l;
+-              l = min_t(int, len, PAGE_SIZE);
++              l = min_t(u32, len, PAGE_SIZE);
+               memcpy(buf, kmap(*++pagep), l);
+               kunmap(*pagep);
+       }
+ }
+-u16 hfs_bnode_read_u16(struct hfs_bnode *node, int off)
++u16 hfs_bnode_read_u16(struct hfs_bnode *node, u32 off)
+ {
+       __be16 data;
+       /* TODO: optimize later... */
+@@ -65,7 +65,7 @@ u16 hfs_bnode_read_u16(struct hfs_bnode
+       return be16_to_cpu(data);
+ }
+-u8 hfs_bnode_read_u8(struct hfs_bnode *node, int off)
++u8 hfs_bnode_read_u8(struct hfs_bnode *node, u32 off)
+ {
+       u8 data;
+       /* TODO: optimize later... */
+@@ -73,10 +73,10 @@ u8 hfs_bnode_read_u8(struct hfs_bnode *n
+       return data;
+ }
+-void hfs_bnode_read_key(struct hfs_bnode *node, void *key, int off)
++void hfs_bnode_read_key(struct hfs_bnode *node, void *key, u32 off)
+ {
+       struct hfs_btree *tree;
+-      int key_len;
++      u32 key_len;
+       tree = node->tree;
+       if (node->type == HFS_NODE_LEAF ||
+@@ -88,17 +88,17 @@ void hfs_bnode_read_key(struct hfs_bnode
+       if (key_len > sizeof(hfsplus_btree_key) || key_len < 1) {
+               memset(key, 0, sizeof(hfsplus_btree_key));
+-              pr_err("hfsplus: Invalid key length: %d\n", key_len);
++              pr_err("hfsplus: Invalid key length: %u\n", key_len);
+               return;
+       }
+       hfs_bnode_read(node, key, off, key_len);
+ }
+-void hfs_bnode_write(struct hfs_bnode *node, void *buf, int off, int len)
++void hfs_bnode_write(struct hfs_bnode *node, void *buf, u32 off, u32 len)
+ {
+       struct page **pagep;
+-      int l;
++      u32 l;
+       if (!is_bnode_offset_valid(node, off))
+               return;
+@@ -106,7 +106,7 @@ void hfs_bnode_write(struct hfs_bnode *n
+       if (len == 0) {
+               pr_err("requested zero length: "
+                      "NODE: id %u, type %#x, height %u, "
+-                     "node_size %u, offset %d, len %d\n",
++                     "node_size %u, offset %u, len %u\n",
+                      node->this, node->type, node->height,
+                      node->tree->node_size, off, len);
+               return;
+@@ -118,31 +118,31 @@ void hfs_bnode_write(struct hfs_bnode *n
+       pagep = node->page + (off >> PAGE_SHIFT);
+       off &= ~PAGE_MASK;
+-      l = min_t(int, len, PAGE_SIZE - off);
++      l = min_t(u32, len, PAGE_SIZE - off);
+       memcpy(kmap(*pagep) + off, buf, l);
+       set_page_dirty(*pagep);
+       kunmap(*pagep);
+       while ((len -= l) != 0) {
+               buf += l;
+-              l = min_t(int, len, PAGE_SIZE);
++              l = min_t(u32, len, PAGE_SIZE);
+               memcpy(kmap(*++pagep), buf, l);
+               set_page_dirty(*pagep);
+               kunmap(*pagep);
+       }
+ }
+-void hfs_bnode_write_u16(struct hfs_bnode *node, int off, u16 data)
++void hfs_bnode_write_u16(struct hfs_bnode *node, u32 off, u16 data)
+ {
+       __be16 v = cpu_to_be16(data);
+       /* TODO: optimize later... */
+       hfs_bnode_write(node, &v, off, 2);
+ }
+-void hfs_bnode_clear(struct hfs_bnode *node, int off, int len)
++void hfs_bnode_clear(struct hfs_bnode *node, u32 off, u32 len)
+ {
+       struct page **pagep;
+-      int l;
++      u32 l;
+       if (!is_bnode_offset_valid(node, off))
+               return;
+@@ -150,7 +150,7 @@ void hfs_bnode_clear(struct hfs_bnode *n
+       if (len == 0) {
+               pr_err("requested zero length: "
+                      "NODE: id %u, type %#x, height %u, "
+-                     "node_size %u, offset %d, len %d\n",
++                     "node_size %u, offset %u, len %u\n",
+                      node->this, node->type, node->height,
+                      node->tree->node_size, off, len);
+               return;
+@@ -162,24 +162,24 @@ void hfs_bnode_clear(struct hfs_bnode *n
+       pagep = node->page + (off >> PAGE_SHIFT);
+       off &= ~PAGE_MASK;
+-      l = min_t(int, len, PAGE_SIZE - off);
++      l = min_t(u32, len, PAGE_SIZE - off);
+       memset(kmap(*pagep) + off, 0, l);
+       set_page_dirty(*pagep);
+       kunmap(*pagep);
+       while ((len -= l) != 0) {
+-              l = min_t(int, len, PAGE_SIZE);
++              l = min_t(u32, len, PAGE_SIZE);
+               memset(kmap(*++pagep), 0, l);
+               set_page_dirty(*pagep);
+               kunmap(*pagep);
+       }
+ }
+-void hfs_bnode_copy(struct hfs_bnode *dst_node, int dst,
+-                  struct hfs_bnode *src_node, int src, int len)
++void hfs_bnode_copy(struct hfs_bnode *dst_node, u32 dst,
++                  struct hfs_bnode *src_node, u32 src, u32 len)
+ {
+       struct page **src_page, **dst_page;
+-      int l;
++      u32 l;
+       hfs_dbg(BNODE_MOD, "copybytes: %u,%u,%u\n", dst, src, len);
+       if (!len)
+@@ -196,14 +196,14 @@ void hfs_bnode_copy(struct hfs_bnode *ds
+       dst &= ~PAGE_MASK;
+       if (src == dst) {
+-              l = min_t(int, len, PAGE_SIZE - src);
++              l = min_t(u32, len, PAGE_SIZE - src);
+               memcpy(kmap(*dst_page) + src, kmap(*src_page) + src, l);
+               kunmap(*src_page);
+               set_page_dirty(*dst_page);
+               kunmap(*dst_page);
+               while ((len -= l) != 0) {
+-                      l = min_t(int, len, PAGE_SIZE);
++                      l = min_t(u32, len, PAGE_SIZE);
+                       memcpy(kmap(*++dst_page), kmap(*++src_page), l);
+                       kunmap(*src_page);
+                       set_page_dirty(*dst_page);
+@@ -237,10 +237,10 @@ void hfs_bnode_copy(struct hfs_bnode *ds
+       }
+ }
+-void hfs_bnode_move(struct hfs_bnode *node, int dst, int src, int len)
++void hfs_bnode_move(struct hfs_bnode *node, u32 dst, u32 src, u32 len)
+ {
+       struct page **src_page, **dst_page;
+-      int l;
++      u32 l;
+       hfs_dbg(BNODE_MOD, "movebytes: %u,%u,%u\n", dst, src, len);
+       if (!len)
+@@ -309,7 +309,7 @@ void hfs_bnode_move(struct hfs_bnode *no
+               dst &= ~PAGE_MASK;
+               if (src == dst) {
+-                      l = min_t(int, len, PAGE_SIZE - src);
++                      l = min_t(u32, len, PAGE_SIZE - src);
+                       memmove(kmap(*dst_page) + src,
+                               kmap(*src_page) + src, l);
+                       kunmap(*src_page);
+@@ -317,7 +317,7 @@ void hfs_bnode_move(struct hfs_bnode *no
+                       kunmap(*dst_page);
+                       while ((len -= l) != 0) {
+-                              l = min_t(int, len, PAGE_SIZE);
++                              l = min_t(u32, len, PAGE_SIZE);
+                               memmove(kmap(*++dst_page),
+                                       kmap(*++src_page), l);
+                               kunmap(*src_page);
+--- a/fs/hfsplus/brec.c
++++ b/fs/hfsplus/brec.c
+@@ -60,7 +60,7 @@ u16 hfs_brec_keylen(struct hfs_bnode *no
+       return retval;
+ }
+-int hfs_brec_insert(struct hfs_find_data *fd, void *entry, int entry_len)
++int hfs_brec_insert(struct hfs_find_data *fd, void *entry, u32 entry_len)
+ {
+       struct hfs_btree *tree;
+       struct hfs_bnode *node, *new_node;
+--- a/fs/hfsplus/btree.c
++++ b/fs/hfsplus/btree.c
+@@ -343,7 +343,7 @@ static struct hfs_bnode *hfs_bmap_new_bm
+ }
+ /* Make sure @tree has enough space for the @rsvd_nodes */
+-int hfs_bmap_reserve(struct hfs_btree *tree, int rsvd_nodes)
++int hfs_bmap_reserve(struct hfs_btree *tree, u32 rsvd_nodes)
+ {
+       struct inode *inode = tree->inode;
+       struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
+--- a/fs/hfsplus/hfsplus_fs.h
++++ b/fs/hfsplus/hfsplus_fs.h
+@@ -388,21 +388,21 @@ u32 hfsplus_calc_btree_clump_size(u32 bl
+ struct hfs_btree *hfs_btree_open(struct super_block *sb, u32 id);
+ void hfs_btree_close(struct hfs_btree *tree);
+ int hfs_btree_write(struct hfs_btree *tree);
+-int hfs_bmap_reserve(struct hfs_btree *tree, int rsvd_nodes);
++int hfs_bmap_reserve(struct hfs_btree *tree, u32 rsvd_nodes);
+ struct hfs_bnode *hfs_bmap_alloc(struct hfs_btree *tree);
+ void hfs_bmap_free(struct hfs_bnode *node);
+ /* bnode.c */
+-void hfs_bnode_read(struct hfs_bnode *node, void *buf, int off, int len);
+-u16 hfs_bnode_read_u16(struct hfs_bnode *node, int off);
+-u8 hfs_bnode_read_u8(struct hfs_bnode *node, int off);
+-void hfs_bnode_read_key(struct hfs_bnode *node, void *key, int off);
+-void hfs_bnode_write(struct hfs_bnode *node, void *buf, int off, int len);
+-void hfs_bnode_write_u16(struct hfs_bnode *node, int off, u16 data);
+-void hfs_bnode_clear(struct hfs_bnode *node, int off, int len);
+-void hfs_bnode_copy(struct hfs_bnode *dst_node, int dst,
+-                  struct hfs_bnode *src_node, int src, int len);
+-void hfs_bnode_move(struct hfs_bnode *node, int dst, int src, int len);
++void hfs_bnode_read(struct hfs_bnode *node, void *buf, u32 off, u32 len);
++u16 hfs_bnode_read_u16(struct hfs_bnode *node, u32 off);
++u8 hfs_bnode_read_u8(struct hfs_bnode *node, u32 off);
++void hfs_bnode_read_key(struct hfs_bnode *node, void *key, u32 off);
++void hfs_bnode_write(struct hfs_bnode *node, void *buf, u32 off, u32 len);
++void hfs_bnode_write_u16(struct hfs_bnode *node, u32 off, u16 data);
++void hfs_bnode_clear(struct hfs_bnode *node, u32 off, u32 len);
++void hfs_bnode_copy(struct hfs_bnode *dst_node, u32 dst,
++                  struct hfs_bnode *src_node, u32 src, u32 len);
++void hfs_bnode_move(struct hfs_bnode *node, u32 dst, u32 src, u32 len);
+ void hfs_bnode_dump(struct hfs_bnode *node);
+ void hfs_bnode_unlink(struct hfs_bnode *node);
+ struct hfs_bnode *hfs_bnode_findhash(struct hfs_btree *tree, u32 cnid);
+@@ -417,7 +417,7 @@ bool hfs_bnode_need_zeroout(struct hfs_b
+ /* brec.c */
+ u16 hfs_brec_lenoff(struct hfs_bnode *node, u16 rec, u16 *off);
+ u16 hfs_brec_keylen(struct hfs_bnode *node, u16 rec);
+-int hfs_brec_insert(struct hfs_find_data *fd, void *entry, int entry_len);
++int hfs_brec_insert(struct hfs_find_data *fd, void *entry, u32 entry_len);
+ int hfs_brec_remove(struct hfs_find_data *fd);
+ /* bfind.c */
+@@ -430,7 +430,7 @@ int hfs_find_rec_by_key(struct hfs_bnode
+ int __hfs_brec_find(struct hfs_bnode *bnode, struct hfs_find_data *fd,
+                   search_strategy_t rec_found);
+ int hfs_brec_find(struct hfs_find_data *fd, search_strategy_t do_key_compare);
+-int hfs_brec_read(struct hfs_find_data *fd, void *rec, int rec_len);
++int hfs_brec_read(struct hfs_find_data *fd, void *rec, u32 rec_len);
+ int hfs_brec_goto(struct hfs_find_data *fd, int cnt);
+ /* catalog.c */
+@@ -586,14 +586,14 @@ hfsplus_btree_lock_class(struct hfs_btre
+ }
+ static inline
+-bool is_bnode_offset_valid(struct hfs_bnode *node, int off)
++bool is_bnode_offset_valid(struct hfs_bnode *node, u32 off)
+ {
+       bool is_valid = off < node->tree->node_size;
+       if (!is_valid) {
+               pr_err("requested invalid offset: "
+                      "NODE: id %u, type %#x, height %u, "
+-                     "node_size %u, offset %d\n",
++                     "node_size %u, offset %u\n",
+                      node->this, node->type, node->height,
+                      node->tree->node_size, off);
+       }
+@@ -602,7 +602,7 @@ bool is_bnode_offset_valid(struct hfs_bn
+ }
+ static inline
+-int check_and_correct_requested_length(struct hfs_bnode *node, int off, int len)
++u32 check_and_correct_requested_length(struct hfs_bnode *node, u32 off, u32 len)
+ {
+       unsigned int node_size;
+@@ -612,12 +612,12 @@ int check_and_correct_requested_length(s
+       node_size = node->tree->node_size;
+       if ((off + len) > node_size) {
+-              int new_len = (int)node_size - off;
++              u32 new_len = node_size - off;
+               pr_err("requested length has been corrected: "
+                      "NODE: id %u, type %#x, height %u, "
+-                     "node_size %u, offset %d, "
+-                     "requested_len %d, corrected_len %d\n",
++                     "node_size %u, offset %u, "
++                     "requested_len %u, corrected_len %u\n",
+                      node->this, node->type, node->height,
+                      node->tree->node_size, off, len, new_len);
diff --git a/queue-5.15/hid-add-haptics-page-defines.patch b/queue-5.15/hid-add-haptics-page-defines.patch
new file mode 100644 (file)
index 0000000..33e051d
--- /dev/null
@@ -0,0 +1,78 @@
+From stable+bounces-277074-greg=kroah.com@vger.kernel.org Fri Jul 17 16:34:56 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 17 Jul 2026 10:34:46 -0400
+Subject: HID: add haptics page defines
+To: stable@vger.kernel.org
+Cc: Angela Czubak <aczubak@google.com>, Jonathan Denose <jdenose@google.com>, Benjamin Tissoires <bentiss@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260717143447.1877787-1-sashal@kernel.org>
+
+From: Angela Czubak <aczubak@google.com>
+
+[ Upstream commit 5e0ae59159e3a07391a35865bb79ff335473fa79 ]
+
+Introduce haptic usages as defined in HID Usage Tables specification.
+Add HID units for newton and gram.
+
+Signed-off-by: Angela Czubak <aczubak@google.com>
+Co-developed-by: Jonathan Denose <jdenose@google.com>
+Signed-off-by: Jonathan Denose <jdenose@google.com>
+Signed-off-by: Benjamin Tissoires <bentiss@kernel.org>
+Stable-dep-of: 8813b0612275 ("HID: multitouch: fix out-of-bounds bit access on mt_io_flags")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ include/linux/hid.h |   29 +++++++++++++++++++++++++++++
+ 1 file changed, 29 insertions(+)
+
+--- a/include/linux/hid.h
++++ b/include/linux/hid.h
+@@ -153,6 +153,7 @@ struct hid_item {
+ #define HID_UP_TELEPHONY      0x000b0000
+ #define HID_UP_CONSUMER               0x000c0000
+ #define HID_UP_DIGITIZER      0x000d0000
++#define HID_UP_HAPTIC         0x000e0000
+ #define HID_UP_PID            0x000f0000
+ #define HID_UP_BATTERY                0x00850000
+ #define HID_UP_HPVENDOR         0xff7f0000
+@@ -301,6 +302,28 @@ struct hid_item {
+ #define HID_DG_TOOLSERIALNUMBER       0x000d005b
+ #define HID_DG_LATENCYMODE    0x000d0060
++#define HID_HP_SIMPLECONTROLLER       0x000e0001
++#define HID_HP_WAVEFORMLIST   0x000e0010
++#define HID_HP_DURATIONLIST   0x000e0011
++#define HID_HP_AUTOTRIGGER    0x000e0020
++#define HID_HP_MANUALTRIGGER  0x000e0021
++#define HID_HP_AUTOTRIGGERASSOCIATEDCONTROL 0x000e0022
++#define HID_HP_INTENSITY      0x000e0023
++#define HID_HP_REPEATCOUNT    0x000e0024
++#define HID_HP_RETRIGGERPERIOD        0x000e0025
++#define HID_HP_WAVEFORMVENDORPAGE     0x000e0026
++#define HID_HP_WAVEFORMVENDORID       0x000e0027
++#define HID_HP_WAVEFORMCUTOFFTIME     0x000e0028
++#define HID_HP_WAVEFORMNONE   0x000e1001
++#define HID_HP_WAVEFORMSTOP   0x000e1002
++#define HID_HP_WAVEFORMCLICK  0x000e1003
++#define HID_HP_WAVEFORMBUZZCONTINUOUS 0x000e1004
++#define HID_HP_WAVEFORMRUMBLECONTINUOUS       0x000e1005
++#define HID_HP_WAVEFORMPRESS  0x000e1006
++#define HID_HP_WAVEFORMRELEASE        0x000e1007
++#define HID_HP_VENDORWAVEFORMMIN      0x000e2001
++#define HID_HP_VENDORWAVEFORMMAX      0x000e2fff
++
+ #define HID_BAT_ABSOLUTESTATEOFCHARGE 0x00850065
+ #define HID_VD_ASUS_CUSTOM_MEDIA_KEYS 0xff310076
+@@ -389,6 +412,12 @@ struct hid_item {
+ #define HID_BOOT_PROTOCOL     0
+ /*
++ * HID units
++ */
++#define HID_UNIT_GRAM         0x0101
++#define HID_UNIT_NEWTON               0xe111
++
++/*
+  * This is the global environment of the parser. This information is
+  * persistent for main-items. The global environment can be saved and
+  * restored with PUSH/POP statements.
diff --git a/queue-5.15/hid-appleir-fix-uaf-on-pending-key_up_timer-in-remove.patch b/queue-5.15/hid-appleir-fix-uaf-on-pending-key_up_timer-in-remove.patch
new file mode 100644 (file)
index 0000000..9bc9e83
--- /dev/null
@@ -0,0 +1,150 @@
+From stable+bounces-277176-greg=kroah.com@vger.kernel.org Fri Jul 17 20:16:04 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 17 Jul 2026 14:11:59 -0400
+Subject: HID: appleir: fix UAF on pending key_up_timer in remove()
+To: stable@vger.kernel.org
+Cc: Manish Khadka <maskmemanish@gmail.com>, Jiri Kosina <jkosina@suse.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260717181159.2086971-2-sashal@kernel.org>
+
+From: Manish Khadka <maskmemanish@gmail.com>
+
+[ Upstream commit 75fe87e19d8aff81eb2c64d15d244ab8da4de945 ]
+
+appleir_remove() runs hid_hw_stop() before timer_delete_sync().
+hid_hw_stop() synchronously unregisters the HID input device via
+hid_disconnect() -> hidinput_disconnect() -> input_unregister_device(),
+which drops the last reference and frees the underlying input_dev when
+no userspace handle holds it open.
+
+key_up_tick() reads appleir->input_dev and calls input_report_key() /
+input_sync() on it.  The timer is armed from appleir_raw_event() with
+a HZ/8 (~125 ms) timeout on every keydown and key-repeat report.  If a
+key was pressed shortly before the device is disconnected, the timer
+can fire after hid_hw_stop() has freed input_dev but before the
+teardown drains it.
+
+A simple reorder is not sufficient.  Putting the timer drain first
+still leaves a window where a USB URB completion (raw_event) running
+during hid_hw_stop() can call mod_timer() and re-arm the timer, which
+then fires after hidinput_disconnect() has freed input_dev.  The same
+URB-completion window also lets raw_event() reach key_up(), key_down()
+and battery_flat() directly, all of which dereference
+appleir->input_dev.
+
+Introduce a 'removing' flag on struct appleir, gated by the existing
+spinlock.  appleir_remove() sets the flag under the lock and then
+shuts down the timer with timer_shutdown_sync(), which both drains any
+in-flight callback and permanently disables further mod_timer() calls.
+appleir_raw_event() and key_up_tick() bail out early if the flag is
+set, so no path can arm or run the timer, or dereference
+appleir->input_dev, after remove() has started tearing down.
+
+The keyrepeat and flatbattery branches of appleir_raw_event()
+previously called into the input layer without holding the spinlock;
+take it now so the flag check is well-defined.  This incidentally
+closes a pre-existing read-side race on appleir->current_key in the
+keyrepeat branch.
+
+This bug is structurally a sibling of commit 4db2af929279 ("HID:
+appletb-kbd: fix UAF in inactivity-timer cleanup path") and has been
+present since the driver was introduced.
+
+Fixes: 9a4a5574ce42 ("HID: appleir: add support for Apple ir devices")
+Cc: stable@vger.kernel.org
+Signed-off-by: Manish Khadka <maskmemanish@gmail.com>
+Signed-off-by: Jiri Kosina <jkosina@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/hid/hid-appleir.c |   45 +++++++++++++++++++++++++++++++++++----------
+ 1 file changed, 35 insertions(+), 10 deletions(-)
+
+--- a/drivers/hid/hid-appleir.c
++++ b/drivers/hid/hid-appleir.c
+@@ -109,9 +109,10 @@ struct appleir {
+       struct hid_device *hid;
+       unsigned short keymap[ARRAY_SIZE(appleir_key_table)];
+       struct timer_list key_up_timer; /* timer for key up */
+-      spinlock_t lock;                /* protects .current_key */
++      spinlock_t lock;                /* protects .current_key, .removing */
+       int current_key;                /* the currently pressed key */
+       int prev_key_idx;               /* key index in a 2 packets message */
++      bool removing;                  /* set during teardown; gates input_dev access */
+ };
+ static int get_key(int data)
+@@ -172,7 +173,7 @@ static void key_up_tick(struct timer_lis
+       unsigned long flags;
+       spin_lock_irqsave(&appleir->lock, flags);
+-      if (appleir->current_key) {
++      if (!appleir->removing && appleir->current_key) {
+               key_up(hid, appleir, appleir->current_key);
+               appleir->current_key = 0;
+       }
+@@ -195,6 +196,10 @@ static int appleir_raw_event(struct hid_
+               int index;
+               spin_lock_irqsave(&appleir->lock, flags);
++              if (appleir->removing) {
++                      spin_unlock_irqrestore(&appleir->lock, flags);
++                      goto out;
++              }
+               /*
+                * If we already have a key down, take it up before marking
+                * this one down
+@@ -229,17 +234,25 @@ static int appleir_raw_event(struct hid_
+       appleir->prev_key_idx = 0;
+       if (!memcmp(data, keyrepeat, sizeof(keyrepeat))) {
+-              key_down(hid, appleir, appleir->current_key);
+-              /*
+-               * Remote doesn't do key up, either pull them up, in the test
+-               * above, or here set a timer which pulls them up after 1/8 s
+-               */
+-              mod_timer(&appleir->key_up_timer, jiffies + HZ / 8);
++              spin_lock_irqsave(&appleir->lock, flags);
++              if (!appleir->removing) {
++                      key_down(hid, appleir, appleir->current_key);
++                      /*
++                       * Remote doesn't do key up, either pull them up, in
++                       * the test above, or here set a timer which pulls them
++                       * up after 1/8 s
++                       */
++                      mod_timer(&appleir->key_up_timer, jiffies + HZ / 8);
++              }
++              spin_unlock_irqrestore(&appleir->lock, flags);
+               goto out;
+       }
+       if (!memcmp(data, flatbattery, sizeof(flatbattery))) {
+-              battery_flat(appleir);
++              spin_lock_irqsave(&appleir->lock, flags);
++              if (!appleir->removing)
++                      battery_flat(appleir);
++              spin_unlock_irqrestore(&appleir->lock, flags);
+               /* Fall through */
+       }
+@@ -318,8 +331,20 @@ fail:
+ static void appleir_remove(struct hid_device *hid)
+ {
+       struct appleir *appleir = hid_get_drvdata(hid);
++      unsigned long flags;
++
++      /*
++       * Mark the driver as tearing down so that any concurrent raw_event
++       * (e.g. from a USB URB completion that hid_hw_stop() has not yet
++       * killed) and the key_up_timer softirq stop touching input_dev
++       * before hid_hw_stop() frees it via hidinput_disconnect().
++       */
++      spin_lock_irqsave(&appleir->lock, flags);
++      appleir->removing = true;
++      spin_unlock_irqrestore(&appleir->lock, flags);
++
++      timer_shutdown_sync(&appleir->key_up_timer);
+       hid_hw_stop(hid);
+-      timer_delete_sync(&appleir->key_up_timer);
+ }
+ static const struct hid_device_id appleir_devices[] = {
diff --git a/queue-5.15/hid-multitouch-fix-out-of-bounds-bit-access-on-mt_io_flags.patch b/queue-5.15/hid-multitouch-fix-out-of-bounds-bit-access-on-mt_io_flags.patch
new file mode 100644 (file)
index 0000000..8805344
--- /dev/null
@@ -0,0 +1,158 @@
+From stable+bounces-277075-greg=kroah.com@vger.kernel.org Fri Jul 17 16:34:57 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 17 Jul 2026 10:34:47 -0400
+Subject: HID: multitouch: fix out-of-bounds bit access on mt_io_flags
+To: stable@vger.kernel.org
+Cc: Trung Nguyen <trungnh@cystack.net>, Benjamin Tissoires <bentiss@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260717143447.1877787-2-sashal@kernel.org>
+
+From: Trung Nguyen <trungnh@cystack.net>
+
+[ Upstream commit 8813b0612275cc61fe9e6603d0ee019247ade6be ]
+
+mt_io_flags is a single unsigned long, but mt_process_slot(),
+mt_release_pending_palms() and mt_release_contacts() use it as a
+per-slot bitmap indexed by the slot number. That slot number is only
+bounded by td->maxcontacts, which is taken from the device's
+ContactCountMaximum feature report and can be up to 255, not by
+BITS_PER_LONG.
+
+As a result, a multitouch device that advertises a large contact count
+makes set_bit()/clear_bit() operate past the mt_io_flags word and
+corrupt the adjacent members of struct mt_device. The sticky-fingers
+release timer is the easiest way to reach this. mt_release_contacts()
+runs
+
+       for (i = 0; i < mt->num_slots; i++)
+               clear_bit(i, &td->mt_io_flags);
+
+with num_slots == maxcontacts. For maxcontacts around 250 the loop
+clears the bits that overlap td->applications.next, zeroing that list
+head, and the list_for_each_entry() that immediately follows then
+dereferences NULL. The kernel panics from timer (softirq) context. On a
+KASAN build this shows up as a general protection fault in
+mt_release_contacts() with a null-ptr-deref at offset 0x58, which is
+offsetof(struct mt_application, num_received).
+
+The state is reachable from an untrusted USB or Bluetooth HID
+multitouch device; no local privileges are required.
+
+Store the per-slot active state in a separately allocated bitmap sized
+for maxcontacts, the same pattern already used for pending_palm_slots,
+and keep only MT_IO_FLAGS_RUNNING in mt_io_flags. The two
+"mt_io_flags & MT_IO_SLOTS_MASK" arming checks become
+bitmap_empty(td->active_slots, td->maxcontacts).
+
+Move MT_IO_FLAGS_RUNNING back to bit 0. It was bumped to bit 32 by the
+same commit to leave the low byte for the slot bits; with the slot bits
+gone it fits in bit 0 again, which also keeps it within the unsigned
+long on 32-bit.
+
+Fixes: 46f781e0d151 ("HID: multitouch: fix sticky fingers")
+Cc: stable@vger.kernel.org
+Signed-off-by: Trung Nguyen <trungnh@cystack.net>
+Signed-off-by: Benjamin Tissoires <bentiss@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/hid/hid-multitouch.c |   32 ++++++++++++++++++++------------
+ 1 file changed, 20 insertions(+), 12 deletions(-)
+
+--- a/drivers/hid/hid-multitouch.c
++++ b/drivers/hid/hid-multitouch.c
+@@ -31,6 +31,7 @@
+  * [1] https://gitlab.freedesktop.org/libevdev/hid-tools
+  */
++#include <linux/bitmap.h>
+ #include <linux/device.h>
+ #include <linux/hid.h>
+ #include <linux/module.h>
+@@ -83,8 +84,7 @@ enum latency_mode {
+       HID_LATENCY_HIGH = 1,
+ };
+-#define MT_IO_SLOTS_MASK              GENMASK(7, 0) /* reserve first 8 bits for slot tracking */
+-#define MT_IO_FLAGS_RUNNING           32
++#define MT_IO_FLAGS_RUNNING           0
+ static const bool mtrue = true;               /* default for true */
+ static const bool mfalse;             /* default for false */
+@@ -160,10 +160,9 @@ struct mt_device {
+       struct mt_class mtclass;        /* our mt device class */
+       struct timer_list release_timer;        /* to release sticky fingers */
+       struct hid_device *hdev;        /* hid_device we're attached to */
+-      unsigned long mt_io_flags;      /* mt flags (MT_IO_FLAGS_RUNNING)
+-                                       * first 8 bits are reserved for keeping the slot
+-                                       * states, this is fine because we only support up
+-                                       * to 250 slots (MT_MAX_MAXCONTACT)
++      unsigned long mt_io_flags;      /* mt flags (MT_IO_FLAGS_RUNNING) */
++      unsigned long *active_slots;    /* bitmap of slots with an active
++                                       * contact, sized for maxcontacts
+                                        */
+       __u8 inputmode_value;   /* InputMode HID feature value */
+       __u8 maxcontacts;
+@@ -947,7 +946,7 @@ static void mt_release_pending_palms(str
+       for_each_set_bit(slotnum, app->pending_palm_slots, td->maxcontacts) {
+               clear_bit(slotnum, app->pending_palm_slots);
+-              clear_bit(slotnum, &td->mt_io_flags);
++              clear_bit(slotnum, td->active_slots);
+               input_mt_slot(input, slotnum);
+               input_mt_report_slot_inactive(input);
+@@ -1153,9 +1152,9 @@ static int mt_process_slot(struct mt_dev
+               input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, major);
+               input_event(input, EV_ABS, ABS_MT_TOUCH_MINOR, minor);
+-              set_bit(slotnum, &td->mt_io_flags);
++              set_bit(slotnum, td->active_slots);
+       } else {
+-              clear_bit(slotnum, &td->mt_io_flags);
++              clear_bit(slotnum, td->active_slots);
+       }
+       return 0;
+@@ -1290,7 +1289,7 @@ static void mt_touch_report(struct hid_d
+        * defect.
+        */
+       if (app->quirks & MT_QUIRK_STICKY_FINGERS) {
+-              if (td->mt_io_flags & MT_IO_SLOTS_MASK)
++              if (!bitmap_empty(td->active_slots, td->maxcontacts))
+                       mod_timer(&td->release_timer,
+                                 jiffies + msecs_to_jiffies(100));
+               else
+@@ -1330,6 +1329,15 @@ static int mt_touch_input_configured(str
+       if (td->is_buttonpad)
+               __set_bit(INPUT_PROP_BUTTONPAD, input->propbit);
++      if (!td->active_slots) {
++              td->active_slots = devm_kcalloc(&td->hdev->dev,
++                                              BITS_TO_LONGS(td->maxcontacts),
++                                              sizeof(long),
++                                              GFP_KERNEL);
++              if (!td->active_slots)
++                      return -ENOMEM;
++      }
++
+       app->pending_palm_slots = devm_kcalloc(&hi->input->dev,
+                                              BITS_TO_LONGS(td->maxcontacts),
+                                              sizeof(long),
+@@ -1740,7 +1748,7 @@ static void mt_release_contacts(struct h
+                       for (i = 0; i < mt->num_slots; i++) {
+                               input_mt_slot(input_dev, i);
+                               input_mt_report_slot_inactive(input_dev);
+-                              clear_bit(i, &td->mt_io_flags);
++                              clear_bit(i, td->active_slots);
+                       }
+                       input_mt_sync_frame(input_dev);
+                       input_sync(input_dev);
+@@ -1763,7 +1771,7 @@ static void mt_expired_timeout(struct ti
+        */
+       if (test_and_set_bit_lock(MT_IO_FLAGS_RUNNING, &td->mt_io_flags))
+               return;
+-      if (td->mt_io_flags & MT_IO_SLOTS_MASK)
++      if (!bitmap_empty(td->active_slots, td->maxcontacts))
+               mt_release_contacts(hdev);
+       clear_bit_unlock(MT_IO_FLAGS_RUNNING, &td->mt_io_flags);
+ }
diff --git a/queue-5.15/iio-common-st_sensors-honour-channel-endianness-in-read_axis_data.patch b/queue-5.15/iio-common-st_sensors-honour-channel-endianness-in-read_axis_data.patch
new file mode 100644 (file)
index 0000000..a2b5a52
--- /dev/null
@@ -0,0 +1,109 @@
+From stable+bounces-274080-greg=kroah.com@vger.kernel.org Tue Jul 14 04:10:06 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 13 Jul 2026 22:09:08 -0400
+Subject: iio: common: st_sensors: honour channel endianness in read_axis_data
+To: stable@vger.kernel.org
+Cc: Herman van Hazendonk <github.com@herrie.org>, Andy Shevchenko <andriy.shevchenko@intel.com>, Jonathan Cameron <jic23@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260714020908.2355876-2-sashal@kernel.org>
+
+From: Herman van Hazendonk <github.com@herrie.org>
+
+[ Upstream commit 55052184ac9011db2ea983e54d6c21f0b1079a12 ]
+
+st_sensors_read_axis_data() unconditionally decoded multi-byte
+results with get_unaligned_le16() / get_unaligned_le24() regardless
+of the channel's declared scan_type.endianness.
+
+For every ST sensor that has used this helper since it was introduced
+this happened to be fine because the ST IMU/accel/gyro/pressure
+families publish their data registers as little-endian and the
+channel specs in those drivers declare IIO_LE accordingly.
+
+The LSM303DLH magnetometer however publishes its X/Y/Z output as a
+pair of big-endian bytes (the H register sits at the lower address,
+0x03/0x05/0x07, and the L register immediately after), and its
+channel specs in st_magn_core.c correctly declare IIO_BE -- but
+read_axis_data() ignored that and decoded as little-endian, swapping
+the high and low bytes of every magnetometer sample. The LSM303DLHC
+and LSM303DLM share the same st_magn_16bit_channels (IIO_BE) and
+were therefore byte-swapped by the same bug; users of those parts
+will see different in_magn_*_raw values after this fix lands.
+
+The bug is most visible on a stationary chip: in earth's field the
+true X reading is small and the high byte sits at 0x00, so swapping
+the bytes pins sysfs X at exactly the low byte's pattern (e.g. 0x00F0
+= 240). Y and Z still appear "to vary" because their magnitudes are
+larger and the noise in the low byte produces big swings in the
+swapped high byte:
+
+  before (LSM303DLH flat, sysfs in_magn_*_raw):
+      X=240 (stuck), Y= 12032..23296, Z=-16128..-9728
+
+  after (direct i2c-dev big-endian decode, same chip same orientation):
+      X≈-4096, Y≈210, Z≈80     (sensible values reflecting earth's
+                                ambient field at low gauss range)
+
+Fix read_axis_data() to dispatch on ch->scan_type.endianness and
+call get_unaligned_be16() / get_unaligned_be24() when the channel
+declares IIO_BE. Existing IIO_LE consumers (st_accel, st_gyro,
+st_pressure, st_lsm6dsx and others) are unaffected because their
+channel specs already declare IIO_LE and the LE path is unchanged.
+
+While restructuring the branches, replace the previously implicit
+silent-success-with-uninitialised-*data fall-through for
+byte_for_channel outside 1..3 with an explicit return -EINVAL. No
+in-tree ST sensor publishes such a channel, but the new behaviour
+is strictly safer than handing userspace garbage.
+
+Fixes: 23491b513bcd ("iio:common: Add STMicroelectronics common library")
+Cc: stable@vger.kernel.org
+Assisted-by: Claude:claude-opus-4-7 sparse smatch clang-analyzer coccinelle checkpatch
+Assisted-by: Sashiko:claude-opus-4-7
+Signed-off-by: Herman van Hazendonk <github.com@herrie.org>
+Reviewed-by: Andy Shevchenko <andriy.shevchenko@intel.com>
+Signed-off-by: Jonathan Cameron <jic23@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/iio/common/st_sensors/st_sensors_core.c |   23 +++++++++++++++++------
+ 1 file changed, 17 insertions(+), 6 deletions(-)
+
+--- a/drivers/iio/common/st_sensors/st_sensors_core.c
++++ b/drivers/iio/common/st_sensors/st_sensors_core.c
+@@ -528,6 +528,7 @@ static int st_sensors_read_axis_data(str
+       u8 *outdata;
+       struct st_sensor_data *sdata = iio_priv(indio_dev);
+       unsigned int byte_for_channel;
++      u32 tmp;
+       byte_for_channel = DIV_ROUND_UP(ch->scan_type.realbits +
+                                       ch->scan_type.shift, 8);
+@@ -540,12 +541,22 @@ static int st_sensors_read_axis_data(str
+       if (err < 0)
+               goto st_sensors_free_memory;
+-      if (byte_for_channel == 1)
+-              *data = (s8)*outdata;
+-      else if (byte_for_channel == 2)
+-              *data = (s16)get_unaligned_le16(outdata);
+-      else if (byte_for_channel == 3)
+-              *data = (s32)sign_extend32(get_unaligned_le24(outdata), 23);
++      if (byte_for_channel == 1) {
++              tmp = *outdata;
++      } else if (byte_for_channel == 2) {
++              if (ch->scan_type.endianness == IIO_BE)
++                      tmp = get_unaligned_be16(outdata);
++              else
++                      tmp = get_unaligned_le16(outdata);
++      } else if (byte_for_channel == 3) {
++              if (ch->scan_type.endianness == IIO_BE)
++                      tmp = get_unaligned_be24(outdata);
++              else
++                      tmp = get_unaligned_le24(outdata);
++      } else {
++              return -EINVAL;
++      }
++      *data = sign_extend32(tmp, BYTES_TO_BITS(byte_for_channel) - 1);
+ st_sensors_free_memory:
+       kfree(outdata);
diff --git a/queue-5.15/iio-hid-sensor-rotation-fix-stale-or-zero-output-when-reading-raw-values.patch b/queue-5.15/iio-hid-sensor-rotation-fix-stale-or-zero-output-when-reading-raw-values.patch
new file mode 100644 (file)
index 0000000..527e162
--- /dev/null
@@ -0,0 +1,89 @@
+From stable+bounces-273966-greg=kroah.com@vger.kernel.org Mon Jul 13 20:44:16 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 13 Jul 2026 14:44:09 -0400
+Subject: iio: hid-sensor-rotation: Fix stale or zero output when reading raw values
+To: stable@vger.kernel.org
+Cc: Zhang Lixu <lixu.zhang@intel.com>, Andy Shevchenko <andriy.shevchenko@intel.com>, Stable@vger.kernel.org, Jonathan Cameron <jic23@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260713184409.1954470-2-sashal@kernel.org>
+
+From: Zhang Lixu <lixu.zhang@intel.com>
+
+[ Upstream commit 3ce8d099e0afc5a7da75a2007a67f67c4f5a4af1 ]
+
+When reading the raw quaternion attribute (in_rot_quaternion_raw), the
+driver currently returns either all zeros (if the sensor was never enabled)
+or stale data (if the sensor was previously enabled) because it reads from
+the internal buffer without explicitly requesting a new sample from the
+sensor.
+
+To fix this, power up the sensor, call sensor_hub_input_attr_read_values()
+to issue a synchronous GET_REPORT and receive the full quaternion data
+directly into a local buffer, then decode the four components.
+
+Fixes: fc18dddc0625 ("iio: hid-sensors: Added device rotation support")
+Signed-off-by: Zhang Lixu <lixu.zhang@intel.com>
+Reviewed-by: Andy Shevchenko <andriy.shevchenko@intel.com>
+Cc: <Stable@vger.kernel.org>
+Signed-off-by: Jonathan Cameron <jic23@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/iio/orientation/hid-sensor-rotation.c |   40 ++++++++++++++++++++++++--
+ 1 file changed, 38 insertions(+), 2 deletions(-)
+
+--- a/drivers/iio/orientation/hid-sensor-rotation.c
++++ b/drivers/iio/orientation/hid-sensor-rotation.c
+@@ -69,6 +69,13 @@ static int dev_rot_read_raw(struct iio_d
+                               long mask)
+ {
+       struct dev_rot_state *rot_state = iio_priv(indio_dev);
++      struct hid_sensor_hub_device *hsdev = rot_state->common_attributes.hsdev;
++      struct hid_sensor_hub_attribute_info *info = &rot_state->quaternion;
++      u32 usage_id = HID_USAGE_SENSOR_ORIENT_QUATERNION;
++      union {
++              s16 val16[4];
++              s32 val32[4];
++      } raw_buf;
+       int ret_type;
+       int i;
+@@ -78,8 +85,37 @@ static int dev_rot_read_raw(struct iio_d
+       switch (mask) {
+       case IIO_CHAN_INFO_RAW:
+               if (size >= 4) {
+-                      for (i = 0; i < 4; ++i)
+-                              vals[i] = rot_state->scan.sampled_vals[i];
++                      if (info->size <= 0 || info->size > sizeof(raw_buf))
++                              return -EINVAL;
++
++                      hid_sensor_power_state(&rot_state->common_attributes, true);
++
++                      ret_type = sensor_hub_input_attr_read_values(hsdev,
++                                                                   hsdev->usage,
++                                                                   usage_id,
++                                                                   info->report_id,
++                                                                   SENSOR_HUB_SYNC,
++                                                                   info->size,
++                                                                   (u8 *)&raw_buf);
++
++                      hid_sensor_power_state(&rot_state->common_attributes, false);
++
++                      if (ret_type < 0)
++                              return ret_type;
++
++                      switch (info->size) {
++                      case sizeof(raw_buf.val16):
++                              for (i = 0; i < ARRAY_SIZE(raw_buf.val16); i++)
++                                      vals[i] = raw_buf.val16[i];
++                              break;
++                      case sizeof(raw_buf.val32):
++                              for (i = 0; i < ARRAY_SIZE(raw_buf.val32); i++)
++                                      vals[i] = raw_buf.val32[i];
++                              break;
++                      default:
++                              return -EINVAL;
++                      }
++
+                       ret_type = IIO_VAL_INT_MULTIPLE;
+                       *val_len =  4;
+               } else
diff --git a/queue-5.15/iio-imu-adis-add-irqf_no_thread-to-non-fifo-trigger-irq.patch b/queue-5.15/iio-imu-adis-add-irqf_no_thread-to-non-fifo-trigger-irq.patch
new file mode 100644 (file)
index 0000000..4c0c618
--- /dev/null
@@ -0,0 +1,49 @@
+From stable+bounces-273947-greg=kroah.com@vger.kernel.org Mon Jul 13 20:16:41 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 13 Jul 2026 14:13:17 -0400
+Subject: iio: imu: adis: add IRQF_NO_THREAD to non-FIFO trigger IRQ
+To: stable@vger.kernel.org
+Cc: Runyu Xiao <runyu.xiao@seu.edu.cn>, Jonathan Cameron <jic23@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260713181317.1932155-1-sashal@kernel.org>
+
+From: Runyu Xiao <runyu.xiao@seu.edu.cn>
+
+[ Upstream commit 6e1b9bff1202da55c464e36bd34a2b6863d7fe30 ]
+
+devm_adis_probe_trigger() registers iio_trigger_generic_data_rdy_poll()
+through devm_request_irq() on the non-FIFO path, but it does not add
+IRQF_NO_THREAD to the IRQ flags.
+
+When the kernel is booted with forced IRQ threading, the parent IRQ can
+otherwise be threaded by the IRQ core and the subsequent IIO trigger
+child IRQ is then dispatched from irq/... thread context instead of
+hardirq context. Because iio_trigger_generic_data_rdy_poll()
+immediately drives iio_trigger_poll(), this violates the hardirq-only
+IIO trigger helper contract and can push downstream trigger consumers
+through the wrong execution context.
+
+Add IRQF_NO_THREAD on top of the existing adis->irq_flag value for the
+non-FIFO request_irq() path, while preserving the current trigger
+polarity and IRQF_NO_AUTOEN behavior.
+
+Fixes: fec86c6b8369 ("iio: imu: adis: Add Managed device functions")
+Cc: stable@vger.kernel.org
+Signed-off-by: Runyu Xiao <runyu.xiao@seu.edu.cn>
+Signed-off-by: Jonathan Cameron <jic23@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/iio/imu/adis_trigger.c |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/iio/imu/adis_trigger.c
++++ b/drivers/iio/imu/adis_trigger.c
+@@ -79,7 +79,7 @@ int devm_adis_probe_trigger(struct adis
+       ret = devm_request_irq(&adis->spi->dev, adis->spi->irq,
+                              &iio_trigger_generic_data_rdy_poll,
+-                             adis->irq_flag,
++                             adis->irq_flag | IRQF_NO_THREAD,
+                              indio_dev->name,
+                              adis->trig);
+       if (ret)
diff --git a/queue-5.15/iio-imu-inv_icm42600-fix-timestamping-by-limiting-fifo-reading.patch b/queue-5.15/iio-imu-inv_icm42600-fix-timestamping-by-limiting-fifo-reading.patch
new file mode 100644 (file)
index 0000000..82604ec
--- /dev/null
@@ -0,0 +1,72 @@
+From stable+bounces-274066-greg=kroah.com@vger.kernel.org Tue Jul 14 03:16:53 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 13 Jul 2026 21:16:27 -0400
+Subject: iio: imu: inv_icm42600: fix timestamping by limiting FIFO reading
+To: stable@vger.kernel.org
+Cc: Jean-Baptiste Maneyrol <jean-baptiste.maneyrol@tdk.com>, Jonathan Cameron <jic23@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260714011627.2188779-8-sashal@kernel.org>
+
+From: Jean-Baptiste Maneyrol <jean-baptiste.maneyrol@tdk.com>
+
+[ Upstream commit affe3f077d7a4eeb25937f5323ff059a54b4712c ]
+
+Timestamps are made by measuring the chip clock using the watermark
+interrupts. If we read more than watermark samples as done today, we
+are reducing the period between interrupts and distort the time
+measurement. Fix that by reading only watermark samples in the
+interrupt case.
+
+Fixes: 7f85e42a6c54 ("iio: imu: inv_icm42600: add buffer support in iio devices")
+Cc: stable@vger.kernel.org
+Signed-off-by: Jean-Baptiste Maneyrol <jean-baptiste.maneyrol@tdk.com>
+Signed-off-by: Jonathan Cameron <jic23@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c |    9 +++++----
+ drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h |    1 +
+ 2 files changed, 6 insertions(+), 4 deletions(-)
+
+--- a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c
++++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c
+@@ -257,6 +257,7 @@ int inv_icm42600_buffer_update_watermark
+       /* compute watermark value in bytes */
+       wm_size = watermark * packet_size;
++      st->fifo.watermark.value = watermark;
+       /* changing FIFO watermark requires to turn off watermark interrupt */
+       ret = regmap_update_bits_check(st->map, INV_ICM42600_REG_INT_SOURCE0,
+@@ -476,11 +477,10 @@ int inv_icm42600_buffer_fifo_read(struct
+       st->fifo.nb.accel = 0;
+       st->fifo.nb.total = 0;
+-      /* compute maximum FIFO read size */
++      /* compute maximum FIFO read size (watermark for max = 0 interrupt case) */
+       if (max == 0)
+-              max_count = sizeof(st->fifo.data);
+-      else
+-              max_count = max * inv_icm42600_get_packet_size(st->fifo.en);
++              max = st->fifo.watermark.value;
++      max_count = max * inv_icm42600_get_packet_size(st->fifo.en);
+       /* read FIFO count value */
+       raw_fifo_count = (__be16 *)st->buffer;
+@@ -592,6 +592,7 @@ int inv_icm42600_buffer_init(struct inv_
+       st->fifo.watermark.eff_gyro = 1;
+       st->fifo.watermark.eff_accel = 1;
++      st->fifo.watermark.value = 1;
+       /*
+        * Default FIFO configuration (bits 7 to 5)
+--- a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h
++++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h
+@@ -34,6 +34,7 @@ struct inv_icm42600_fifo {
+               unsigned int accel;
+               unsigned int eff_gyro;
+               unsigned int eff_accel;
++              unsigned int value;
+       } watermark;
+       size_t count;
+       struct {
diff --git a/queue-5.15/iio-imu-inv_icm42600-make-timestamp-module-chip-independent.patch b/queue-5.15/iio-imu-inv_icm42600-make-timestamp-module-chip-independent.patch
new file mode 100644 (file)
index 0000000..41c28ae
--- /dev/null
@@ -0,0 +1,103 @@
+From stable+bounces-274057-greg=kroah.com@vger.kernel.org Tue Jul 14 03:16:36 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 13 Jul 2026 21:16:20 -0400
+Subject: iio: imu: inv_icm42600: make timestamp module chip independent
+To: stable@vger.kernel.org
+Cc: Jean-Baptiste Maneyrol <jean-baptiste.maneyrol@tdk.com>, Andy Shevchenko <andy.shevchenko@gmail.com>, Jonathan Cameron <Jonathan.Cameron@huawei.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260714011627.2188779-1-sashal@kernel.org>
+
+From: Jean-Baptiste Maneyrol <jean-baptiste.maneyrol@tdk.com>
+
+[ Upstream commit 6e9f2d8375cb24ba75e02e0272e9164d06a1522e ]
+
+Move icm42600 dependent function inside the core module.
+Do some headers cleanup at the same time.
+
+Signed-off-by: Jean-Baptiste Maneyrol <jean-baptiste.maneyrol@tdk.com>
+Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
+Link: https://lore.kernel.org/r/20230606162147.79667-2-inv.git-commit@tdk.com
+Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
+Stable-dep-of: affe3f077d7a ("iio: imu: inv_icm42600: fix timestamping by limiting FIFO reading")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/iio/imu/inv_icm42600/inv_icm42600_core.c      |   11 +++++++++++
+ drivers/iio/imu/inv_icm42600/inv_icm42600_timestamp.c |   14 +-------------
+ drivers/iio/imu/inv_icm42600/inv_icm42600_timestamp.h |    6 ------
+ 3 files changed, 12 insertions(+), 19 deletions(-)
+
+--- a/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c
++++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c
+@@ -522,6 +522,17 @@ static int inv_icm42600_irq_init(struct
+                                        "inv_icm42600", st);
+ }
++static int inv_icm42600_timestamp_setup(struct inv_icm42600_state *st)
++{
++      unsigned int val;
++
++      /* enable timestamp register */
++      val = INV_ICM42600_TMST_CONFIG_TMST_TO_REGS_EN |
++            INV_ICM42600_TMST_CONFIG_TMST_EN;
++      return regmap_update_bits(st->map, INV_ICM42600_REG_TMST_CONFIG,
++                                INV_ICM42600_TMST_CONFIG_MASK, val);
++}
++
+ static int inv_icm42600_enable_regulator_vddio(struct inv_icm42600_state *st)
+ {
+       int ret;
+--- a/drivers/iio/imu/inv_icm42600/inv_icm42600_timestamp.c
++++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_timestamp.c
+@@ -3,11 +3,10 @@
+  * Copyright (C) 2020 Invensense, Inc.
+  */
++#include <linux/errno.h>
+ #include <linux/kernel.h>
+-#include <linux/regmap.h>
+ #include <linux/math64.h>
+-#include "inv_icm42600.h"
+ #include "inv_icm42600_timestamp.h"
+ /* internal chip period is 32kHz, 31250ns */
+@@ -56,17 +55,6 @@ void inv_icm42600_timestamp_init(struct
+       inv_update_acc(&ts->chip_period, INV_ICM42600_TIMESTAMP_PERIOD);
+ }
+-int inv_icm42600_timestamp_setup(struct inv_icm42600_state *st)
+-{
+-      unsigned int val;
+-
+-      /* enable timestamp register */
+-      val = INV_ICM42600_TMST_CONFIG_TMST_TO_REGS_EN |
+-            INV_ICM42600_TMST_CONFIG_TMST_EN;
+-      return regmap_update_bits(st->map, INV_ICM42600_REG_TMST_CONFIG,
+-                                INV_ICM42600_TMST_CONFIG_MASK, val);
+-}
+-
+ int inv_icm42600_timestamp_update_odr(struct inv_icm42600_timestamp *ts,
+                                     uint32_t period, bool fifo)
+ {
+--- a/drivers/iio/imu/inv_icm42600/inv_icm42600_timestamp.h
++++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_timestamp.h
+@@ -6,10 +6,6 @@
+ #ifndef INV_ICM42600_TIMESTAMP_H_
+ #define INV_ICM42600_TIMESTAMP_H_
+-#include <linux/kernel.h>
+-
+-struct inv_icm42600_state;
+-
+ /**
+  * struct inv_icm42600_timestamp_interval - timestamps interval
+  * @lo:       interval lower bound
+@@ -53,8 +49,6 @@ struct inv_icm42600_timestamp {
+ void inv_icm42600_timestamp_init(struct inv_icm42600_timestamp *ts,
+                                uint32_t period);
+-int inv_icm42600_timestamp_setup(struct inv_icm42600_state *st);
+-
+ int inv_icm42600_timestamp_update_odr(struct inv_icm42600_timestamp *ts,
+                                     uint32_t period, bool fifo);
diff --git a/queue-5.15/iio-imu-inv_icm42600-stabilized-timestamp-in-interrupt.patch b/queue-5.15/iio-imu-inv_icm42600-stabilized-timestamp-in-interrupt.patch
new file mode 100644 (file)
index 0000000..c06a222
--- /dev/null
@@ -0,0 +1,116 @@
+From stable+bounces-274064-greg=kroah.com@vger.kernel.org Tue Jul 14 03:16:49 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 13 Jul 2026 21:16:26 -0400
+Subject: iio: imu: inv_icm42600: stabilized timestamp in interrupt
+To: stable@vger.kernel.org
+Cc: Jean-Baptiste Maneyrol <jean-baptiste.maneyrol@tdk.com>, Jonathan Cameron <Jonathan.Cameron@huawei.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260714011627.2188779-7-sashal@kernel.org>
+
+From: Jean-Baptiste Maneyrol <jean-baptiste.maneyrol@tdk.com>
+
+[ Upstream commit d7bd473632d07f8a54655c270c0940cc3671c548 ]
+
+Use IRQF_ONESHOT flag to ensure the timestamp is not updated in the
+hard handler during the thread handler. And compute and use the
+effective watermark value that correspond to this first timestamp.
+
+This way we can ensure the timestamp is always corresponding to the
+value used by the timestamping mechanism. Otherwise, it is possible
+that between FIFO count read and FIFO processing the timestamp is
+overwritten in the hard handler.
+
+Fixes: ec74ae9fd37c ("iio: imu: inv_icm42600: add accurate timestamping")
+Cc: stable@vger.kernel.org
+Signed-off-by: Jean-Baptiste Maneyrol <jean-baptiste.maneyrol@tdk.com>
+Link: https://lore.kernel.org/r/20240529154717.651863-1-inv.git-commit@tdk.com
+Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
+Stable-dep-of: affe3f077d7a ("iio: imu: inv_icm42600: fix timestamping by limiting FIFO reading")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c |   19 +++++++++++++++++--
+ drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h |    2 ++
+ drivers/iio/imu/inv_icm42600/inv_icm42600_core.c   |    1 +
+ 3 files changed, 20 insertions(+), 2 deletions(-)
+
+--- a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c
++++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c
+@@ -222,10 +222,15 @@ int inv_icm42600_buffer_update_watermark
+       latency_accel = period_accel * wm_accel;
+       /* 0 value for watermark means that the sensor is turned off */
++      if (wm_gyro == 0 && wm_accel == 0)
++              return 0;
++
+       if (latency_gyro == 0) {
+               watermark = wm_accel;
++              st->fifo.watermark.eff_accel = wm_accel;
+       } else if (latency_accel == 0) {
+               watermark = wm_gyro;
++              st->fifo.watermark.eff_gyro = wm_gyro;
+       } else {
+               /* compute the smallest latency that is a multiple of both */
+               if (latency_gyro <= latency_accel)
+@@ -241,6 +246,13 @@ int inv_icm42600_buffer_update_watermark
+               watermark = latency / period;
+               if (watermark < 1)
+                       watermark = 1;
++              /* update effective watermark */
++              st->fifo.watermark.eff_gyro = latency / period_gyro;
++              if (st->fifo.watermark.eff_gyro < 1)
++                      st->fifo.watermark.eff_gyro = 1;
++              st->fifo.watermark.eff_accel = latency / period_accel;
++              if (st->fifo.watermark.eff_accel < 1)
++                      st->fifo.watermark.eff_accel = 1;
+       }
+       /* compute watermark value in bytes */
+@@ -517,7 +529,7 @@ int inv_icm42600_buffer_fifo_parse(struc
+       /* handle gyroscope timestamp and FIFO data parsing */
+       if (st->fifo.nb.gyro > 0) {
+               ts = iio_priv(st->indio_gyro);
+-              inv_sensors_timestamp_interrupt(ts, st->fifo.nb.gyro,
++              inv_sensors_timestamp_interrupt(ts, st->fifo.watermark.eff_gyro,
+                                               st->timestamp.gyro);
+               ret = inv_icm42600_gyro_parse_fifo(st->indio_gyro);
+               if (ret)
+@@ -527,7 +539,7 @@ int inv_icm42600_buffer_fifo_parse(struc
+       /* handle accelerometer timestamp and FIFO data parsing */
+       if (st->fifo.nb.accel > 0) {
+               ts = iio_priv(st->indio_accel);
+-              inv_sensors_timestamp_interrupt(ts, st->fifo.nb.accel,
++              inv_sensors_timestamp_interrupt(ts, st->fifo.watermark.eff_accel,
+                                               st->timestamp.accel);
+               ret = inv_icm42600_accel_parse_fifo(st->indio_accel);
+               if (ret)
+@@ -578,6 +590,9 @@ int inv_icm42600_buffer_init(struct inv_
+       unsigned int val;
+       int ret;
++      st->fifo.watermark.eff_gyro = 1;
++      st->fifo.watermark.eff_accel = 1;
++
+       /*
+        * Default FIFO configuration (bits 7 to 5)
+        * - use invalid value
+--- a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h
++++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h
+@@ -32,6 +32,8 @@ struct inv_icm42600_fifo {
+       struct {
+               unsigned int gyro;
+               unsigned int accel;
++              unsigned int eff_gyro;
++              unsigned int eff_accel;
+       } watermark;
+       size_t count;
+       struct {
+--- a/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c
++++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c
+@@ -518,6 +518,7 @@ static int inv_icm42600_irq_init(struct
+       if (ret)
+               return ret;
++      irq_type |= IRQF_ONESHOT;
+       return devm_request_threaded_irq(dev, irq, inv_icm42600_irq_timestamp,
+                                        inv_icm42600_irq_handler, irq_type,
+                                        "inv_icm42600", st);
diff --git a/queue-5.15/iio-imu-inv_mpu6050-use-the-common-inv_sensors-timestamp-module.patch b/queue-5.15/iio-imu-inv_mpu6050-use-the-common-inv_sensors-timestamp-module.patch
new file mode 100644 (file)
index 0000000..b5ac482
--- /dev/null
@@ -0,0 +1,307 @@
+From stable+bounces-274059-greg=kroah.com@vger.kernel.org Tue Jul 14 03:16:39 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 13 Jul 2026 21:16:23 -0400
+Subject: iio: imu: inv_mpu6050: use the common inv_sensors timestamp module
+To: stable@vger.kernel.org
+Cc: Jean-Baptiste Maneyrol <jean-baptiste.maneyrol@tdk.com>, Andy Shevchenko <andy.shevchenko@gmail.com>, Jonathan Cameron <Jonathan.Cameron@huawei.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260714011627.2188779-4-sashal@kernel.org>
+
+From: Jean-Baptiste Maneyrol <jean-baptiste.maneyrol@tdk.com>
+
+[ Upstream commit 111e1abd00455971f6a568900f033cbddec9d4e5 ]
+
+Replace timestamping by the new common inv_sensors timestamp
+module. The principle behind is the same but the implementation in
+the new module is far better providing less jitter and a better
+estimation.
+
+Signed-off-by: Jean-Baptiste Maneyrol <jean-baptiste.maneyrol@tdk.com>
+Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
+Link: https://lore.kernel.org/r/20230606162147.79667-5-inv.git-commit@tdk.com
+Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
+Stable-dep-of: affe3f077d7a ("iio: imu: inv_icm42600: fix timestamping by limiting FIFO reading")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/iio/imu/inv_mpu6050/Kconfig           |    1 
+ drivers/iio/imu/inv_mpu6050/inv_mpu_core.c    |   30 +++++++--
+ drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h     |   18 ++---
+ drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c    |   83 ++------------------------
+ drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c |    6 +
+ 5 files changed, 45 insertions(+), 93 deletions(-)
+
+--- a/drivers/iio/imu/inv_mpu6050/Kconfig
++++ b/drivers/iio/imu/inv_mpu6050/Kconfig
+@@ -7,6 +7,7 @@ config INV_MPU6050_IIO
+       tristate
+       select IIO_BUFFER
+       select IIO_TRIGGERED_BUFFER
++      select IIO_INV_SENSORS_TIMESTAMP
+ config INV_MPU6050_I2C
+       tristate "Invensense MPU6050 devices (I2C)"
+--- a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c
++++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c
+@@ -12,12 +12,15 @@
+ #include <linux/jiffies.h>
+ #include <linux/irq.h>
+ #include <linux/interrupt.h>
+-#include <linux/iio/iio.h>
+ #include <linux/acpi.h>
+ #include <linux/platform_device.h>
+ #include <linux/regulator/consumer.h>
+ #include <linux/pm.h>
+ #include <linux/pm_runtime.h>
++
++#include <linux/iio/common/inv_sensors_timestamp.h>
++#include <linux/iio/iio.h>
++
+ #include "inv_mpu_iio.h"
+ #include "inv_mpu_magn.h"
+@@ -503,6 +506,7 @@ static int inv_mpu6050_init_config(struc
+       int result;
+       u8 d;
+       struct inv_mpu6050_state *st = iio_priv(indio_dev);
++      struct inv_sensors_timestamp_chip timestamp;
+       result = inv_mpu6050_set_gyro_fsr(st, st->chip_config.fsr);
+       if (result)
+@@ -526,12 +530,12 @@ static int inv_mpu6050_init_config(struc
+       if (result)
+               return result;
+-      /*
+-       * Internal chip period is 1ms (1kHz).
+-       * Let's use at the beginning the theorical value before measuring
+-       * with interrupt timestamps.
+-       */
+-      st->chip_period = NSEC_PER_MSEC;
++      /* clock jitter is +/- 2% */
++      timestamp.clock_period = NSEC_PER_SEC / INV_MPU6050_INTERNAL_FREQ_HZ;
++      timestamp.jitter = 20;
++      timestamp.init_period =
++                      NSEC_PER_SEC / INV_MPU6050_DIVIDER_TO_FIFO_RATE(st->chip_config.divider);
++      inv_sensors_timestamp_init(&st->timestamp, &timestamp);
+       /* magn chip init, noop if not present in the chip */
+       result = inv_mpu_magn_probe(st);
+@@ -918,6 +922,8 @@ inv_mpu6050_fifo_rate_store(struct devic
+                           const char *buf, size_t count)
+ {
+       int fifo_rate;
++      u32 fifo_period;
++      bool fifo_on;
+       u8 d;
+       int result;
+       struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+@@ -934,12 +940,21 @@ inv_mpu6050_fifo_rate_store(struct devic
+       d = INV_MPU6050_FIFO_RATE_TO_DIVIDER(fifo_rate);
+       /* compute back the fifo rate to handle truncation cases */
+       fifo_rate = INV_MPU6050_DIVIDER_TO_FIFO_RATE(d);
++      fifo_period = NSEC_PER_SEC / fifo_rate;
+       mutex_lock(&st->lock);
+       if (d == st->chip_config.divider) {
+               result = 0;
+               goto fifo_rate_fail_unlock;
+       }
++
++      fifo_on = st->chip_config.accl_fifo_enable ||
++                st->chip_config.gyro_fifo_enable ||
++                st->chip_config.magn_fifo_enable;
++      result = inv_sensors_timestamp_update_odr(&st->timestamp, fifo_period, fifo_on);
++      if (result)
++              goto fifo_rate_fail_unlock;
++
+       result = pm_runtime_resume_and_get(pdev);
+       if (result)
+               goto fifo_rate_fail_unlock;
+@@ -1767,3 +1782,4 @@ EXPORT_SYMBOL_GPL(inv_mpu_pmops);
+ MODULE_AUTHOR("Invensense Corporation");
+ MODULE_DESCRIPTION("Invensense device MPU6050 driver");
+ MODULE_LICENSE("GPL");
++MODULE_IMPORT_NS(IIO_INV_SENSORS_TIMESTAMP);
+--- a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h
++++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h
+@@ -9,15 +9,17 @@
+ #include <linux/i2c.h>
+ #include <linux/i2c-mux.h>
+ #include <linux/mutex.h>
+-#include <linux/iio/iio.h>
+-#include <linux/iio/buffer.h>
++#include <linux/platform_data/invensense_mpu6050.h>
+ #include <linux/regmap.h>
+-#include <linux/iio/sysfs.h>
++
++#include <linux/iio/buffer.h>
++#include <linux/iio/common/inv_sensors_timestamp.h>
++#include <linux/iio/iio.h>
+ #include <linux/iio/kfifo_buf.h>
+ #include <linux/iio/trigger.h>
+ #include <linux/iio/triggered_buffer.h>
+ #include <linux/iio/trigger_consumer.h>
+-#include <linux/platform_data/invensense_mpu6050.h>
++#include <linux/iio/sysfs.h>
+ /**
+  *  struct inv_mpu6050_reg_map - Notable registers.
+@@ -168,9 +170,7 @@ struct inv_mpu6050_hw {
+  *  @map              regmap pointer.
+  *  @irq              interrupt number.
+  *  @irq_mask         the int_pin_cfg mask to configure interrupt type.
+- *  @chip_period:     chip internal period estimation (~1kHz).
+- *  @it_timestamp:    timestamp from previous interrupt.
+- *  @data_timestamp:  timestamp for next data sample.
++ *  @timestamp:               timestamping module
+  *  @vdd_supply:      VDD voltage regulator for the chip.
+  *  @vddio_supply     I/O voltage regulator for the chip.
+  *  @magn_disabled:     magnetometer disabled for backward compatibility reason.
+@@ -194,9 +194,7 @@ struct inv_mpu6050_state {
+       int irq;
+       u8 irq_mask;
+       unsigned skip_samples;
+-      s64 chip_period;
+-      s64 it_timestamp;
+-      s64 data_timestamp;
++      struct inv_sensors_timestamp timestamp;
+       struct regulator *vdd_supply;
+       struct regulator *vddio_supply;
+       bool magn_disabled;
+--- a/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c
++++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c
+@@ -13,81 +13,10 @@
+ #include <linux/interrupt.h>
+ #include <linux/poll.h>
+ #include <linux/math64.h>
+-#include "inv_mpu_iio.h"
+-
+-/**
+- *  inv_mpu6050_update_period() - Update chip internal period estimation
+- *
+- *  @st:              driver state
+- *  @timestamp:               the interrupt timestamp
+- *  @nb:              number of data set in the fifo
+- *
+- *  This function uses interrupt timestamps to estimate the chip period and
+- *  to choose the data timestamp to come.
+- */
+-static void inv_mpu6050_update_period(struct inv_mpu6050_state *st,
+-                                    s64 timestamp, size_t nb)
+-{
+-      /* Period boundaries for accepting timestamp */
+-      const s64 period_min =
+-              (NSEC_PER_MSEC * (100 - INV_MPU6050_TS_PERIOD_JITTER)) / 100;
+-      const s64 period_max =
+-              (NSEC_PER_MSEC * (100 + INV_MPU6050_TS_PERIOD_JITTER)) / 100;
+-      const s32 divider = INV_MPU6050_FREQ_DIVIDER(st);
+-      s64 delta, interval;
+-      bool use_it_timestamp = false;
+-
+-      if (st->it_timestamp == 0) {
+-              /* not initialized, forced to use it_timestamp */
+-              use_it_timestamp = true;
+-      } else if (nb == 1) {
+-              /*
+-               * Validate the use of it timestamp by checking if interrupt
+-               * has been delayed.
+-               * nb > 1 means interrupt was delayed for more than 1 sample,
+-               * so it's obviously not good.
+-               * Compute the chip period between 2 interrupts for validating.
+-               */
+-              delta = div_s64(timestamp - st->it_timestamp, divider);
+-              if (delta > period_min && delta < period_max) {
+-                      /* update chip period and use it timestamp */
+-                      st->chip_period = (st->chip_period + delta) / 2;
+-                      use_it_timestamp = true;
+-              }
+-      }
+-      if (use_it_timestamp) {
+-              /*
+-               * Manage case of multiple samples in the fifo (nb > 1):
+-               * compute timestamp corresponding to the first sample using
+-               * estimated chip period.
+-               */
+-              interval = (nb - 1) * st->chip_period * divider;
+-              st->data_timestamp = timestamp - interval;
+-      }
++#include <linux/iio/common/inv_sensors_timestamp.h>
+-      /* save it timestamp */
+-      st->it_timestamp = timestamp;
+-}
+-
+-/**
+- *  inv_mpu6050_get_timestamp() - Return the current data timestamp
+- *
+- *  @st:              driver state
+- *  @return:          current data timestamp
+- *
+- *  This function returns the current data timestamp and prepares for next one.
+- */
+-static s64 inv_mpu6050_get_timestamp(struct inv_mpu6050_state *st)
+-{
+-      s64 ts;
+-
+-      /* return current data timestamp and increment */
+-      ts = st->data_timestamp;
+-      st->data_timestamp += st->chip_period * INV_MPU6050_FREQ_DIVIDER(st);
+-
+-      return ts;
+-}
++#include "inv_mpu_iio.h"
+ static int inv_reset_fifo(struct iio_dev *indio_dev)
+ {
+@@ -121,6 +50,7 @@ irqreturn_t inv_mpu6050_read_fifo(int ir
+       size_t bytes_per_datum;
+       int result;
+       u16 fifo_count;
++      u32 fifo_period;
+       s64 timestamp;
+       int int_status;
+       size_t i, nb;
+@@ -177,7 +107,10 @@ irqreturn_t inv_mpu6050_read_fifo(int ir
+       /* compute and process all complete datum */
+       nb = fifo_count / bytes_per_datum;
+-      inv_mpu6050_update_period(st, pf->timestamp, nb);
++      /* Each FIFO data contains all sensors, so same number for FIFO and sensor data */
++      fifo_period = NSEC_PER_SEC / INV_MPU6050_DIVIDER_TO_FIFO_RATE(st->chip_config.divider);
++      inv_sensors_timestamp_interrupt(&st->timestamp, fifo_period, nb, nb, pf->timestamp);
++      inv_sensors_timestamp_apply_odr(&st->timestamp, fifo_period, nb, 0);
+       for (i = 0; i < nb; ++i) {
+               result = regmap_noinc_read(st->map, st->reg->fifo_r_w,
+                                          st->data, bytes_per_datum);
+@@ -188,7 +121,7 @@ irqreturn_t inv_mpu6050_read_fifo(int ir
+                       st->skip_samples--;
+                       continue;
+               }
+-              timestamp = inv_mpu6050_get_timestamp(st);
++              timestamp = inv_sensors_timestamp_pop(&st->timestamp);
+               iio_push_to_buffers_with_timestamp(indio_dev, st->data, timestamp);
+       }
+--- a/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c
++++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c
+@@ -4,6 +4,9 @@
+ */
+ #include <linux/pm_runtime.h>
++
++#include <linux/iio/common/inv_sensors_timestamp.h>
++
+ #include "inv_mpu_iio.h"
+ static unsigned int inv_scan_query_mpu6050(struct iio_dev *indio_dev)
+@@ -106,7 +109,8 @@ int inv_mpu6050_prepare_fifo(struct inv_
+       int ret;
+       if (enable) {
+-              st->it_timestamp = 0;
++              /* reset timestamping */
++              inv_sensors_timestamp_reset(&st->timestamp);
+               /* reset FIFO */
+               d = st->chip_config.user_ctrl | INV_MPU6050_BIT_FIFO_RST;
+               ret = regmap_write(st->map, st->reg->user_ctrl, d);
diff --git a/queue-5.15/iio-invensense-fix-timestamp-glitches-when-switching-frequency.patch b/queue-5.15/iio-invensense-fix-timestamp-glitches-when-switching-frequency.patch
new file mode 100644 (file)
index 0000000..ad435b0
--- /dev/null
@@ -0,0 +1,202 @@
+From stable+bounces-274065-greg=kroah.com@vger.kernel.org Tue Jul 14 03:16:52 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 13 Jul 2026 21:16:25 -0400
+Subject: iio: invensense: fix timestamp glitches when switching frequency
+To: stable@vger.kernel.org
+Cc: Jean-Baptiste Maneyrol <jean-baptiste.maneyrol@tdk.com>, Stable@vger.kernel.org, Jonathan Cameron <Jonathan.Cameron@huawei.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260714011627.2188779-6-sashal@kernel.org>
+
+From: Jean-Baptiste Maneyrol <jean-baptiste.maneyrol@tdk.com>
+
+[ Upstream commit bf8367b00c33c64a9391c262bb2e11d274c9f2a4 ]
+
+When a sensor is running and there is a FIFO frequency change due to
+another sensor turned on/off, there are glitches on timestamp. Fix that
+by using only interrupt timestamp when there is the corresponding sensor
+data in the FIFO.
+
+Delete FIFO period handling and simplify internal functions.
+
+Update integration inside inv_mpu6050 and inv_icm42600 drivers.
+
+Fixes: 0ecc363ccea7 ("iio: make invensense timestamp module generic")
+Cc: Stable@vger.kernel.org
+Signed-off-by: Jean-Baptiste Maneyrol <jean-baptiste.maneyrol@tdk.com>
+Link: https://lore.kernel.org/r/20240426094835.138389-1-inv.git-commit@tdk.com
+Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
+Stable-dep-of: affe3f077d7a ("iio: imu: inv_icm42600: fix timestamping by limiting FIFO reading")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/iio/common/inv_sensors/inv_sensors_timestamp.c |   30 +++++++----------
+ drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c     |   20 ++++-------
+ drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c             |    2 -
+ include/linux/iio/common/inv_sensors_timestamp.h       |    3 -
+ 4 files changed, 23 insertions(+), 32 deletions(-)
+
+--- a/drivers/iio/common/inv_sensors/inv_sensors_timestamp.c
++++ b/drivers/iio/common/inv_sensors/inv_sensors_timestamp.c
+@@ -70,13 +70,13 @@ int inv_sensors_timestamp_update_odr(str
+ }
+ EXPORT_SYMBOL_NS_GPL(inv_sensors_timestamp_update_odr, IIO_INV_SENSORS_TIMESTAMP);
+-static bool inv_validate_period(struct inv_sensors_timestamp *ts, uint32_t period, uint32_t mult)
++static bool inv_validate_period(struct inv_sensors_timestamp *ts, uint32_t period)
+ {
+       uint32_t period_min, period_max;
+       /* check that period is acceptable */
+-      period_min = ts->min_period * mult;
+-      period_max = ts->max_period * mult;
++      period_min = ts->min_period * ts->mult;
++      period_max = ts->max_period * ts->mult;
+       if (period > period_min && period < period_max)
+               return true;
+       else
+@@ -84,32 +84,30 @@ static bool inv_validate_period(struct i
+ }
+ static bool inv_compute_chip_period(struct inv_sensors_timestamp *ts,
+-                                  uint32_t mult, uint32_t period)
++                                  uint32_t period)
+ {
+       uint32_t new_chip_period;
+-      if (!inv_validate_period(ts, period, mult))
++      if (!inv_validate_period(ts, period))
+               return false;
+       /* update chip internal period estimation */
+-      new_chip_period = period / mult;
++      new_chip_period = period / ts->mult;
+       inv_update_acc(&ts->chip_period, new_chip_period);
+       return true;
+ }
+ void inv_sensors_timestamp_interrupt(struct inv_sensors_timestamp *ts,
+-                                    uint32_t fifo_period, size_t fifo_nb,
+-                                    size_t sensor_nb, int64_t timestamp)
++                                   size_t sample_nb, int64_t timestamp)
+ {
+       struct inv_sensors_timestamp_interval *it;
+       int64_t delta, interval;
+-      const uint32_t fifo_mult = fifo_period / ts->chip.clock_period;
+       uint32_t period;
+       int32_t m;
+       bool valid = false;
+-      if (fifo_nb == 0)
++      if (sample_nb == 0)
+               return;
+       /* update interrupt timestamp and compute chip and sensor periods */
+@@ -119,8 +117,8 @@ void inv_sensors_timestamp_interrupt(str
+       delta = it->up - it->lo;
+       if (it->lo != 0) {
+               /* compute period: delta time divided by number of samples */
+-              period = div_s64(delta, fifo_nb);
+-              valid = inv_compute_chip_period(ts, fifo_mult, period);
++              period = div_s64(delta, sample_nb);
++              valid = inv_compute_chip_period(ts, period);
+               /* update sensor period if chip internal period is updated */
+               if (valid)
+                       ts->period = ts->mult * ts->chip_period.val;
+@@ -129,20 +127,18 @@ void inv_sensors_timestamp_interrupt(str
+       /* no previous data, compute theoritical value from interrupt */
+       if (ts->timestamp == 0) {
+               /* elapsed time: sensor period * sensor samples number */
+-              interval = (int64_t)ts->period * (int64_t)sensor_nb;
++              interval = (int64_t)ts->period * (int64_t)sample_nb;
+               ts->timestamp = it->up - interval;
+               return;
+       }
+       /* if interrupt interval is valid, sync with interrupt timestamp */
+       if (valid) {
+-              /* compute measured fifo_period */
+-              fifo_period = fifo_mult * ts->chip_period.val;
+               /* delta time between last sample and last interrupt */
+               delta = it->lo - ts->timestamp;
+               /* if there are multiple samples, go back to first one */
+-              while (delta >= (fifo_period * 3 / 2))
+-                      delta -= fifo_period;
++              while (delta >= (ts->period * 3 / 2))
++                      delta -= ts->period;
+               /* compute maximal adjustment value */
+               m = INV_SENSORS_TIMESTAMP_JITTER((int64_t)ts->period, ts->chip.jitter);
+               if (delta > m)
+--- a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c
++++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c
+@@ -515,20 +515,20 @@ int inv_icm42600_buffer_fifo_parse(struc
+               return 0;
+       /* handle gyroscope timestamp and FIFO data parsing */
+-      ts = iio_priv(st->indio_gyro);
+-      inv_sensors_timestamp_interrupt(ts, st->fifo.period, st->fifo.nb.total,
+-                                      st->fifo.nb.gyro, st->timestamp.gyro);
+       if (st->fifo.nb.gyro > 0) {
++              ts = iio_priv(st->indio_gyro);
++              inv_sensors_timestamp_interrupt(ts, st->fifo.nb.gyro,
++                                              st->timestamp.gyro);
+               ret = inv_icm42600_gyro_parse_fifo(st->indio_gyro);
+               if (ret)
+                       return ret;
+       }
+       /* handle accelerometer timestamp and FIFO data parsing */
+-      ts = iio_priv(st->indio_accel);
+-      inv_sensors_timestamp_interrupt(ts, st->fifo.period, st->fifo.nb.total,
+-                                      st->fifo.nb.accel, st->timestamp.accel);
+       if (st->fifo.nb.accel > 0) {
++              ts = iio_priv(st->indio_accel);
++              inv_sensors_timestamp_interrupt(ts, st->fifo.nb.accel,
++                                              st->timestamp.accel);
+               ret = inv_icm42600_accel_parse_fifo(st->indio_accel);
+               if (ret)
+                       return ret;
+@@ -556,9 +556,7 @@ int inv_icm42600_buffer_hwfifo_flush(str
+       if (st->fifo.nb.gyro > 0) {
+               ts = iio_priv(st->indio_gyro);
+-              inv_sensors_timestamp_interrupt(ts, st->fifo.period,
+-                                              st->fifo.nb.total, st->fifo.nb.gyro,
+-                                              gyro_ts);
++              inv_sensors_timestamp_interrupt(ts, st->fifo.nb.gyro, gyro_ts);
+               ret = inv_icm42600_gyro_parse_fifo(st->indio_gyro);
+               if (ret)
+                       return ret;
+@@ -566,9 +564,7 @@ int inv_icm42600_buffer_hwfifo_flush(str
+       if (st->fifo.nb.accel > 0) {
+               ts = iio_priv(st->indio_accel);
+-              inv_sensors_timestamp_interrupt(ts, st->fifo.period,
+-                                              st->fifo.nb.total, st->fifo.nb.accel,
+-                                              accel_ts);
++              inv_sensors_timestamp_interrupt(ts, st->fifo.nb.accel, accel_ts);
+               ret = inv_icm42600_accel_parse_fifo(st->indio_accel);
+               if (ret)
+                       return ret;
+--- a/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c
++++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c
+@@ -109,7 +109,7 @@ irqreturn_t inv_mpu6050_read_fifo(int ir
+       nb = fifo_count / bytes_per_datum;
+       /* Each FIFO data contains all sensors, so same number for FIFO and sensor data */
+       fifo_period = NSEC_PER_SEC / INV_MPU6050_DIVIDER_TO_FIFO_RATE(st->chip_config.divider);
+-      inv_sensors_timestamp_interrupt(&st->timestamp, fifo_period, nb, nb, pf->timestamp);
++      inv_sensors_timestamp_interrupt(&st->timestamp, nb, pf->timestamp);
+       inv_sensors_timestamp_apply_odr(&st->timestamp, fifo_period, nb, 0);
+       for (i = 0; i < nb; ++i) {
+               result = regmap_noinc_read(st->map, st->reg->fifo_r_w,
+--- a/include/linux/iio/common/inv_sensors_timestamp.h
++++ b/include/linux/iio/common/inv_sensors_timestamp.h
+@@ -71,8 +71,7 @@ int inv_sensors_timestamp_update_odr(str
+                                    uint32_t period, bool fifo);
+ void inv_sensors_timestamp_interrupt(struct inv_sensors_timestamp *ts,
+-                                   uint32_t fifo_period, size_t fifo_nb,
+-                                   size_t sensor_nb, int64_t timestamp);
++                                   size_t sample_nb, int64_t timestamp);
+ static inline int64_t inv_sensors_timestamp_pop(struct inv_sensors_timestamp *ts)
+ {
diff --git a/queue-5.15/iio-invensense-remove-redundant-initialization-of-variable-period.patch b/queue-5.15/iio-invensense-remove-redundant-initialization-of-variable-period.patch
new file mode 100644 (file)
index 0000000..272e8eb
--- /dev/null
@@ -0,0 +1,42 @@
+From stable+bounces-274060-greg=kroah.com@vger.kernel.org Tue Jul 14 03:16:39 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 13 Jul 2026 21:16:24 -0400
+Subject: iio: invensense: remove redundant initialization of variable period
+To: stable@vger.kernel.org
+Cc: Colin Ian King <colin.i.king@gmail.com>, Jean-Baptiste Maneyrol <jean-baptiste.maneyrol@tdk.com>, Jonathan Cameron <Jonathan.Cameron@huawei.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260714011627.2188779-5-sashal@kernel.org>
+
+From: Colin Ian King <colin.i.king@gmail.com>
+
+[ Upstream commit b58b13f156c00c2457035b7071eaaac105fe6836 ]
+
+The variable period is being initialized with a value that is never
+read, it is being re-assigned a new value later on before it is read.
+The initialization is redundant and can be removed.
+
+Cleans up clang scan build warning:
+Value stored to 'period' during its initialization is never
+read [deadcode.DeadStores]
+
+Signed-off-by: Colin Ian King <colin.i.king@gmail.com>
+Acked-by: Jean-Baptiste Maneyrol <jean-baptiste.maneyrol@tdk.com>
+Link: https://lore.kernel.org/r/20240106153202.54861-1-colin.i.king@gmail.com
+Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
+Stable-dep-of: affe3f077d7a ("iio: imu: inv_icm42600: fix timestamping by limiting FIFO reading")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/iio/common/inv_sensors/inv_sensors_timestamp.c |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/iio/common/inv_sensors/inv_sensors_timestamp.c
++++ b/drivers/iio/common/inv_sensors/inv_sensors_timestamp.c
+@@ -105,7 +105,7 @@ void inv_sensors_timestamp_interrupt(str
+       struct inv_sensors_timestamp_interval *it;
+       int64_t delta, interval;
+       const uint32_t fifo_mult = fifo_period / ts->chip.clock_period;
+-      uint32_t period = ts->period;
++      uint32_t period;
+       int32_t m;
+       bool valid = false;
diff --git a/queue-5.15/iio-make-invensense-timestamp-module-generic.patch b/queue-5.15/iio-make-invensense-timestamp-module-generic.patch
new file mode 100644 (file)
index 0000000..31d6369
--- /dev/null
@@ -0,0 +1,962 @@
+From stable+bounces-274062-greg=kroah.com@vger.kernel.org Tue Jul 14 03:16:45 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 13 Jul 2026 21:16:22 -0400
+Subject: iio: make invensense timestamp module generic
+To: stable@vger.kernel.org
+Cc: Jean-Baptiste Maneyrol <jean-baptiste.maneyrol@tdk.com>, Andy Shevchenko <andy.shevchenko@gmail.com>, Jonathan Cameron <Jonathan.Cameron@huawei.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260714011627.2188779-3-sashal@kernel.org>
+
+From: Jean-Baptiste Maneyrol <jean-baptiste.maneyrol@tdk.com>
+
+[ Upstream commit 0ecc363ccea71eda6a2cceade120489259bcdb33 ]
+
+Rename common module to inv_sensors_timestamp, add configuration
+at init (chip internal clock, acceptable jitter, ...) and update
+inv_icm42600 driver integration.
+
+Signed-off-by: Jean-Baptiste Maneyrol <jean-baptiste.maneyrol@tdk.com>
+Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
+Link: https://lore.kernel.org/r/20230606162147.79667-4-inv.git-commit@tdk.com
+Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
+Stable-dep-of: affe3f077d7a ("iio: imu: inv_icm42600: fix timestamping by limiting FIFO reading")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/iio/common/inv_sensors/Makefile                 |    2 
+ drivers/iio/common/inv_sensors/inv_icm42600_timestamp.c |  192 ----------------
+ drivers/iio/common/inv_sensors/inv_sensors_timestamp.c  |  191 +++++++++++++++
+ drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c       |   32 +-
+ drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c      |   34 +-
+ drivers/iio/imu/inv_icm42600/inv_icm42600_core.c        |   10 
+ drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c        |   32 +-
+ include/linux/iio/common/inv_icm42600_timestamp.h       |   79 ------
+ include/linux/iio/common/inv_sensors_timestamp.h        |   95 +++++++
+ 9 files changed, 349 insertions(+), 318 deletions(-)
+ rename drivers/iio/common/inv_sensors/{inv_icm42600_timestamp.c => inv_sensors_timestamp.c} (61%)
+ delete mode 100644 include/linux/iio/common/inv_icm42600_timestamp.h
+ create mode 100644 include/linux/iio/common/inv_sensors_timestamp.h
+
+--- a/drivers/iio/common/inv_sensors/Makefile
++++ b/drivers/iio/common/inv_sensors/Makefile
+@@ -3,4 +3,4 @@
+ # Makefile for TDK-InvenSense sensors module.
+ #
+-obj-$(CONFIG_IIO_INV_SENSORS_TIMESTAMP) += inv_icm42600_timestamp.o
++obj-$(CONFIG_IIO_INV_SENSORS_TIMESTAMP) += inv_sensors_timestamp.o
+--- a/drivers/iio/common/inv_sensors/inv_icm42600_timestamp.c
++++ /dev/null
+@@ -1,192 +0,0 @@
+-// SPDX-License-Identifier: GPL-2.0-or-later
+-/*
+- * Copyright (C) 2020 Invensense, Inc.
+- */
+-
+-#include <linux/errno.h>
+-#include <linux/kernel.h>
+-#include <linux/math64.h>
+-#include <linux/module.h>
+-
+-#include <linux/iio/common/inv_icm42600_timestamp.h>
+-
+-/* internal chip period is 32kHz, 31250ns */
+-#define INV_ICM42600_TIMESTAMP_PERIOD         31250
+-/* allow a jitter of +/- 2% */
+-#define INV_ICM42600_TIMESTAMP_JITTER         2
+-/* compute min and max periods accepted */
+-#define INV_ICM42600_TIMESTAMP_MIN_PERIOD(_p)         \
+-      (((_p) * (100 - INV_ICM42600_TIMESTAMP_JITTER)) / 100)
+-#define INV_ICM42600_TIMESTAMP_MAX_PERIOD(_p)         \
+-      (((_p) * (100 + INV_ICM42600_TIMESTAMP_JITTER)) / 100)
+-
+-/* Add a new value inside an accumulator and update the estimate value */
+-static void inv_update_acc(struct inv_icm42600_timestamp_acc *acc, uint32_t val)
+-{
+-      uint64_t sum = 0;
+-      size_t i;
+-
+-      acc->values[acc->idx++] = val;
+-      if (acc->idx >= ARRAY_SIZE(acc->values))
+-              acc->idx = 0;
+-
+-      /* compute the mean of all stored values, use 0 as empty slot */
+-      for (i = 0; i < ARRAY_SIZE(acc->values); ++i) {
+-              if (acc->values[i] == 0)
+-                      break;
+-              sum += acc->values[i];
+-      }
+-
+-      acc->val = div_u64(sum, i);
+-}
+-
+-void inv_icm42600_timestamp_init(struct inv_icm42600_timestamp *ts,
+-                               uint32_t period)
+-{
+-      /* initial odr for sensor after reset is 1kHz */
+-      const uint32_t default_period = 1000000;
+-
+-      /* current multiplier and period values after reset */
+-      ts->mult = default_period / INV_ICM42600_TIMESTAMP_PERIOD;
+-      ts->period = default_period;
+-      /* new set multiplier is the one from chip initialization */
+-      ts->new_mult = period / INV_ICM42600_TIMESTAMP_PERIOD;
+-
+-      /* use theoretical value for chip period */
+-      inv_update_acc(&ts->chip_period, INV_ICM42600_TIMESTAMP_PERIOD);
+-}
+-EXPORT_SYMBOL_NS_GPL(inv_icm42600_timestamp_init, IIO_INV_SENSORS_TIMESTAMP);
+-
+-int inv_icm42600_timestamp_update_odr(struct inv_icm42600_timestamp *ts,
+-                                    uint32_t period, bool fifo)
+-{
+-      /* when FIFO is on, prevent odr change if one is already pending */
+-      if (fifo && ts->new_mult != 0)
+-              return -EAGAIN;
+-
+-      ts->new_mult = period / INV_ICM42600_TIMESTAMP_PERIOD;
+-
+-      return 0;
+-}
+-EXPORT_SYMBOL_NS_GPL(inv_icm42600_timestamp_update_odr, IIO_INV_SENSORS_TIMESTAMP);
+-
+-static bool inv_validate_period(uint32_t period, uint32_t mult)
+-{
+-      const uint32_t chip_period = INV_ICM42600_TIMESTAMP_PERIOD;
+-      uint32_t period_min, period_max;
+-
+-      /* check that period is acceptable */
+-      period_min = INV_ICM42600_TIMESTAMP_MIN_PERIOD(chip_period) * mult;
+-      period_max = INV_ICM42600_TIMESTAMP_MAX_PERIOD(chip_period) * mult;
+-      if (period > period_min && period < period_max)
+-              return true;
+-      else
+-              return false;
+-}
+-
+-static bool inv_compute_chip_period(struct inv_icm42600_timestamp *ts,
+-                                  uint32_t mult, uint32_t period)
+-{
+-      uint32_t new_chip_period;
+-
+-      if (!inv_validate_period(period, mult))
+-              return false;
+-
+-      /* update chip internal period estimation */
+-      new_chip_period = period / mult;
+-      inv_update_acc(&ts->chip_period, new_chip_period);
+-
+-      return true;
+-}
+-
+-void inv_icm42600_timestamp_interrupt(struct inv_icm42600_timestamp *ts,
+-                                    uint32_t fifo_period, size_t fifo_nb,
+-                                    size_t sensor_nb, int64_t timestamp)
+-{
+-      struct inv_icm42600_timestamp_interval *it;
+-      int64_t delta, interval;
+-      const uint32_t fifo_mult = fifo_period / INV_ICM42600_TIMESTAMP_PERIOD;
+-      uint32_t period = ts->period;
+-      int32_t m;
+-      bool valid = false;
+-
+-      if (fifo_nb == 0)
+-              return;
+-
+-      /* update interrupt timestamp and compute chip and sensor periods */
+-      it = &ts->it;
+-      it->lo = it->up;
+-      it->up = timestamp;
+-      delta = it->up - it->lo;
+-      if (it->lo != 0) {
+-              /* compute period: delta time divided by number of samples */
+-              period = div_s64(delta, fifo_nb);
+-              valid = inv_compute_chip_period(ts, fifo_mult, period);
+-              /* update sensor period if chip internal period is updated */
+-              if (valid)
+-                      ts->period = ts->mult * ts->chip_period.val;
+-      }
+-
+-      /* no previous data, compute theoritical value from interrupt */
+-      if (ts->timestamp == 0) {
+-              /* elapsed time: sensor period * sensor samples number */
+-              interval = (int64_t)ts->period * (int64_t)sensor_nb;
+-              ts->timestamp = it->up - interval;
+-              return;
+-      }
+-
+-      /* if interrupt interval is valid, sync with interrupt timestamp */
+-      if (valid) {
+-              /* compute measured fifo_period */
+-              fifo_period = fifo_mult * ts->chip_period.val;
+-              /* delta time between last sample and last interrupt */
+-              delta = it->lo - ts->timestamp;
+-              /* if there are multiple samples, go back to first one */
+-              while (delta >= (fifo_period * 3 / 2))
+-                      delta -= fifo_period;
+-              /* compute maximal adjustment value */
+-              m = INV_ICM42600_TIMESTAMP_MAX_PERIOD(ts->period) - ts->period;
+-              if (delta > m)
+-                      delta = m;
+-              else if (delta < -m)
+-                      delta = -m;
+-              ts->timestamp += delta;
+-      }
+-}
+-EXPORT_SYMBOL_NS_GPL(inv_icm42600_timestamp_interrupt, IIO_INV_SENSORS_TIMESTAMP);
+-
+-void inv_icm42600_timestamp_apply_odr(struct inv_icm42600_timestamp *ts,
+-                                    uint32_t fifo_period, size_t fifo_nb,
+-                                    unsigned int fifo_no)
+-{
+-      int64_t interval;
+-      uint32_t fifo_mult;
+-
+-      if (ts->new_mult == 0)
+-              return;
+-
+-      /* update to new multiplier and update period */
+-      ts->mult = ts->new_mult;
+-      ts->new_mult = 0;
+-      ts->period = ts->mult * ts->chip_period.val;
+-
+-      /*
+-       * After ODR change the time interval with the previous sample is
+-       * undertermined (depends when the change occures). So we compute the
+-       * timestamp from the current interrupt using the new FIFO period, the
+-       * total number of samples and the current sample numero.
+-       */
+-      if (ts->timestamp != 0) {
+-              /* compute measured fifo period */
+-              fifo_mult = fifo_period / INV_ICM42600_TIMESTAMP_PERIOD;
+-              fifo_period = fifo_mult * ts->chip_period.val;
+-              /* computes time interval between interrupt and this sample */
+-              interval = (int64_t)(fifo_nb - fifo_no) * (int64_t)fifo_period;
+-              ts->timestamp = ts->it.up - interval;
+-      }
+-}
+-EXPORT_SYMBOL_NS_GPL(inv_icm42600_timestamp_apply_odr, IIO_INV_SENSORS_TIMESTAMP);
+-
+-MODULE_AUTHOR("InvenSense, Inc.");
+-MODULE_DESCRIPTION("InvenSense sensors timestamp module");
+-MODULE_LICENSE("GPL");
+--- /dev/null
++++ b/drivers/iio/common/inv_sensors/inv_sensors_timestamp.c
+@@ -0,0 +1,191 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * Copyright (C) 2020 Invensense, Inc.
++ */
++
++#include <linux/errno.h>
++#include <linux/kernel.h>
++#include <linux/math64.h>
++#include <linux/module.h>
++
++#include <linux/iio/common/inv_sensors_timestamp.h>
++
++/* compute jitter, min and max following jitter in per mille */
++#define INV_SENSORS_TIMESTAMP_JITTER(_val, _jitter)           \
++      (div_s64((_val) * (_jitter), 1000))
++#define INV_SENSORS_TIMESTAMP_MIN(_val, _jitter)              \
++      (((_val) * (1000 - (_jitter))) / 1000)
++#define INV_SENSORS_TIMESTAMP_MAX(_val, _jitter)              \
++      (((_val) * (1000 + (_jitter))) / 1000)
++
++/* Add a new value inside an accumulator and update the estimate value */
++static void inv_update_acc(struct inv_sensors_timestamp_acc *acc, uint32_t val)
++{
++      uint64_t sum = 0;
++      size_t i;
++
++      acc->values[acc->idx++] = val;
++      if (acc->idx >= ARRAY_SIZE(acc->values))
++              acc->idx = 0;
++
++      /* compute the mean of all stored values, use 0 as empty slot */
++      for (i = 0; i < ARRAY_SIZE(acc->values); ++i) {
++              if (acc->values[i] == 0)
++                      break;
++              sum += acc->values[i];
++      }
++
++      acc->val = div_u64(sum, i);
++}
++
++void inv_sensors_timestamp_init(struct inv_sensors_timestamp *ts,
++                              const struct inv_sensors_timestamp_chip *chip)
++{
++      memset(ts, 0, sizeof(*ts));
++
++      /* save chip parameters and compute min and max clock period */
++      ts->chip = *chip;
++      ts->min_period = INV_SENSORS_TIMESTAMP_MIN(chip->clock_period, chip->jitter);
++      ts->max_period = INV_SENSORS_TIMESTAMP_MAX(chip->clock_period, chip->jitter);
++
++      /* current multiplier and period values after reset */
++      ts->mult = chip->init_period / chip->clock_period;
++      ts->period = chip->init_period;
++
++      /* use theoretical value for chip period */
++      inv_update_acc(&ts->chip_period, chip->clock_period);
++}
++EXPORT_SYMBOL_NS_GPL(inv_sensors_timestamp_init, IIO_INV_SENSORS_TIMESTAMP);
++
++int inv_sensors_timestamp_update_odr(struct inv_sensors_timestamp *ts,
++                                   uint32_t period, bool fifo)
++{
++      /* when FIFO is on, prevent odr change if one is already pending */
++      if (fifo && ts->new_mult != 0)
++              return -EAGAIN;
++
++      ts->new_mult = period / ts->chip.clock_period;
++
++      return 0;
++}
++EXPORT_SYMBOL_NS_GPL(inv_sensors_timestamp_update_odr, IIO_INV_SENSORS_TIMESTAMP);
++
++static bool inv_validate_period(struct inv_sensors_timestamp *ts, uint32_t period, uint32_t mult)
++{
++      uint32_t period_min, period_max;
++
++      /* check that period is acceptable */
++      period_min = ts->min_period * mult;
++      period_max = ts->max_period * mult;
++      if (period > period_min && period < period_max)
++              return true;
++      else
++              return false;
++}
++
++static bool inv_compute_chip_period(struct inv_sensors_timestamp *ts,
++                                  uint32_t mult, uint32_t period)
++{
++      uint32_t new_chip_period;
++
++      if (!inv_validate_period(ts, period, mult))
++              return false;
++
++      /* update chip internal period estimation */
++      new_chip_period = period / mult;
++      inv_update_acc(&ts->chip_period, new_chip_period);
++
++      return true;
++}
++
++void inv_sensors_timestamp_interrupt(struct inv_sensors_timestamp *ts,
++                                    uint32_t fifo_period, size_t fifo_nb,
++                                    size_t sensor_nb, int64_t timestamp)
++{
++      struct inv_sensors_timestamp_interval *it;
++      int64_t delta, interval;
++      const uint32_t fifo_mult = fifo_period / ts->chip.clock_period;
++      uint32_t period = ts->period;
++      int32_t m;
++      bool valid = false;
++
++      if (fifo_nb == 0)
++              return;
++
++      /* update interrupt timestamp and compute chip and sensor periods */
++      it = &ts->it;
++      it->lo = it->up;
++      it->up = timestamp;
++      delta = it->up - it->lo;
++      if (it->lo != 0) {
++              /* compute period: delta time divided by number of samples */
++              period = div_s64(delta, fifo_nb);
++              valid = inv_compute_chip_period(ts, fifo_mult, period);
++              /* update sensor period if chip internal period is updated */
++              if (valid)
++                      ts->period = ts->mult * ts->chip_period.val;
++      }
++
++      /* no previous data, compute theoritical value from interrupt */
++      if (ts->timestamp == 0) {
++              /* elapsed time: sensor period * sensor samples number */
++              interval = (int64_t)ts->period * (int64_t)sensor_nb;
++              ts->timestamp = it->up - interval;
++              return;
++      }
++
++      /* if interrupt interval is valid, sync with interrupt timestamp */
++      if (valid) {
++              /* compute measured fifo_period */
++              fifo_period = fifo_mult * ts->chip_period.val;
++              /* delta time between last sample and last interrupt */
++              delta = it->lo - ts->timestamp;
++              /* if there are multiple samples, go back to first one */
++              while (delta >= (fifo_period * 3 / 2))
++                      delta -= fifo_period;
++              /* compute maximal adjustment value */
++              m = INV_SENSORS_TIMESTAMP_JITTER((int64_t)ts->period, ts->chip.jitter);
++              if (delta > m)
++                      delta = m;
++              else if (delta < -m)
++                      delta = -m;
++              ts->timestamp += delta;
++      }
++}
++EXPORT_SYMBOL_NS_GPL(inv_sensors_timestamp_interrupt, IIO_INV_SENSORS_TIMESTAMP);
++
++void inv_sensors_timestamp_apply_odr(struct inv_sensors_timestamp *ts,
++                                   uint32_t fifo_period, size_t fifo_nb,
++                                   unsigned int fifo_no)
++{
++      int64_t interval;
++      uint32_t fifo_mult;
++
++      if (ts->new_mult == 0)
++              return;
++
++      /* update to new multiplier and update period */
++      ts->mult = ts->new_mult;
++      ts->new_mult = 0;
++      ts->period = ts->mult * ts->chip_period.val;
++
++      /*
++       * After ODR change the time interval with the previous sample is
++       * undertermined (depends when the change occures). So we compute the
++       * timestamp from the current interrupt using the new FIFO period, the
++       * total number of samples and the current sample numero.
++       */
++      if (ts->timestamp != 0) {
++              /* compute measured fifo period */
++              fifo_mult = fifo_period / ts->chip.clock_period;
++              fifo_period = fifo_mult * ts->chip_period.val;
++              /* computes time interval between interrupt and this sample */
++              interval = (int64_t)(fifo_nb - fifo_no) * (int64_t)fifo_period;
++              ts->timestamp = ts->it.up - interval;
++      }
++}
++EXPORT_SYMBOL_NS_GPL(inv_sensors_timestamp_apply_odr, IIO_INV_SENSORS_TIMESTAMP);
++
++MODULE_AUTHOR("InvenSense, Inc.");
++MODULE_DESCRIPTION("InvenSense sensors timestamp module");
++MODULE_LICENSE("GPL");
+--- a/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c
++++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c
+@@ -12,7 +12,7 @@
+ #include <linux/math64.h>
+ #include <linux/iio/buffer.h>
+-#include <linux/iio/common/inv_icm42600_timestamp.h>
++#include <linux/iio/common/inv_sensors_timestamp.h>
+ #include <linux/iio/iio.h>
+ #include <linux/iio/kfifo_buf.h>
+@@ -99,7 +99,7 @@ static int inv_icm42600_accel_update_sca
+                                              const unsigned long *scan_mask)
+ {
+       struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev);
+-      struct inv_icm42600_timestamp *ts = iio_priv(indio_dev);
++      struct inv_sensors_timestamp *ts = iio_priv(indio_dev);
+       struct inv_icm42600_sensor_conf conf = INV_ICM42600_SENSOR_CONF_INIT;
+       unsigned int fifo_en = 0;
+       unsigned int sleep_temp = 0;
+@@ -127,7 +127,7 @@ static int inv_icm42600_accel_update_sca
+       }
+       /* update data FIFO write */
+-      inv_icm42600_timestamp_apply_odr(ts, 0, 0, 0);
++      inv_sensors_timestamp_apply_odr(ts, 0, 0, 0);
+       ret = inv_icm42600_buffer_set_fifo_en(st, fifo_en | st->fifo.en);
+ out_unlock:
+@@ -308,7 +308,7 @@ static int inv_icm42600_accel_write_odr(
+                                       int val, int val2)
+ {
+       struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev);
+-      struct inv_icm42600_timestamp *ts = iio_priv(indio_dev);
++      struct inv_sensors_timestamp *ts = iio_priv(indio_dev);
+       struct device *dev = regmap_get_device(st->map);
+       unsigned int idx;
+       struct inv_icm42600_sensor_conf conf = INV_ICM42600_SENSOR_CONF_INIT;
+@@ -329,8 +329,8 @@ static int inv_icm42600_accel_write_odr(
+       pm_runtime_get_sync(dev);
+       mutex_lock(&st->lock);
+-      ret = inv_icm42600_timestamp_update_odr(ts, inv_icm42600_odr_to_period(conf.odr),
+-                                              iio_buffer_enabled(indio_dev));
++      ret = inv_sensors_timestamp_update_odr(ts, inv_icm42600_odr_to_period(conf.odr),
++                                             iio_buffer_enabled(indio_dev));
+       if (ret)
+               goto out_unlock;
+@@ -706,7 +706,8 @@ struct iio_dev *inv_icm42600_accel_init(
+ {
+       struct device *dev = regmap_get_device(st->map);
+       const char *name;
+-      struct inv_icm42600_timestamp *ts;
++      struct inv_sensors_timestamp_chip ts_chip;
++      struct inv_sensors_timestamp *ts;
+       struct iio_dev *indio_dev;
+       int ret;
+@@ -718,8 +719,15 @@ struct iio_dev *inv_icm42600_accel_init(
+       if (!indio_dev)
+               return ERR_PTR(-ENOMEM);
++      /*
++       * clock period is 32kHz (31250ns)
++       * jitter is +/- 2% (20 per mille)
++       */
++      ts_chip.clock_period = 31250;
++      ts_chip.jitter = 20;
++      ts_chip.init_period = inv_icm42600_odr_to_period(st->conf.accel.odr);
+       ts = iio_priv(indio_dev);
+-      inv_icm42600_timestamp_init(ts, inv_icm42600_odr_to_period(st->conf.accel.odr));
++      inv_sensors_timestamp_init(ts, &ts_chip);
+       iio_device_set_drvdata(indio_dev, st);
+       indio_dev->name = name;
+@@ -745,7 +753,7 @@ struct iio_dev *inv_icm42600_accel_init(
+ int inv_icm42600_accel_parse_fifo(struct iio_dev *indio_dev)
+ {
+       struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev);
+-      struct inv_icm42600_timestamp *ts = iio_priv(indio_dev);
++      struct inv_sensors_timestamp *ts = iio_priv(indio_dev);
+       ssize_t i, size;
+       unsigned int no;
+       const void *accel, *gyro, *timestamp;
+@@ -769,13 +777,13 @@ int inv_icm42600_accel_parse_fifo(struct
+               /* update odr */
+               if (odr & INV_ICM42600_SENSOR_ACCEL)
+-                      inv_icm42600_timestamp_apply_odr(ts, st->fifo.period,
+-                                                       st->fifo.nb.total, no);
++                      inv_sensors_timestamp_apply_odr(ts, st->fifo.period,
++                                                      st->fifo.nb.total, no);
+               memcpy(&buffer.accel, accel, sizeof(buffer.accel));
+               /* convert 8 bits FIFO temperature in high resolution format */
+               buffer.temp = temp ? (*temp * 64) : 0;
+-              ts_val = inv_icm42600_timestamp_pop(ts);
++              ts_val = inv_sensors_timestamp_pop(ts);
+               iio_push_to_buffers_with_timestamp(indio_dev, &buffer, ts_val);
+       }
+--- a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c
++++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c
+@@ -11,7 +11,7 @@
+ #include <linux/delay.h>
+ #include <linux/iio/buffer.h>
+-#include <linux/iio/common/inv_icm42600_timestamp.h>
++#include <linux/iio/common/inv_sensors_timestamp.h>
+ #include <linux/iio/iio.h>
+ #include "inv_icm42600.h"
+@@ -276,12 +276,12 @@ static int inv_icm42600_buffer_preenable
+ {
+       struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev);
+       struct device *dev = regmap_get_device(st->map);
+-      struct inv_icm42600_timestamp *ts = iio_priv(indio_dev);
++      struct inv_sensors_timestamp *ts = iio_priv(indio_dev);
+       pm_runtime_get_sync(dev);
+       mutex_lock(&st->lock);
+-      inv_icm42600_timestamp_reset(ts);
++      inv_sensors_timestamp_reset(ts);
+       mutex_unlock(&st->lock);
+       return 0;
+@@ -378,7 +378,7 @@ out_unlock:
+ static int inv_icm42600_buffer_postdisable(struct iio_dev *indio_dev)
+ {
+       struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev);
+-      struct inv_icm42600_timestamp *ts = iio_priv(indio_dev);
++      struct inv_sensors_timestamp *ts = iio_priv(indio_dev);
+       struct device *dev = regmap_get_device(st->map);
+       unsigned int sensor;
+       unsigned int *watermark;
+@@ -400,7 +400,7 @@ static int inv_icm42600_buffer_postdisab
+       mutex_lock(&st->lock);
+-      inv_icm42600_timestamp_apply_odr(ts, 0, 0, 0);
++      inv_sensors_timestamp_apply_odr(ts, 0, 0, 0);
+       ret = inv_icm42600_buffer_set_fifo_en(st, st->fifo.en & ~sensor);
+       if (ret)
+@@ -508,7 +508,7 @@ int inv_icm42600_buffer_fifo_read(struct
+ int inv_icm42600_buffer_fifo_parse(struct inv_icm42600_state *st)
+ {
+-      struct inv_icm42600_timestamp *ts;
++      struct inv_sensors_timestamp *ts;
+       int ret;
+       if (st->fifo.nb.total == 0)
+@@ -516,8 +516,8 @@ int inv_icm42600_buffer_fifo_parse(struc
+       /* handle gyroscope timestamp and FIFO data parsing */
+       ts = iio_priv(st->indio_gyro);
+-      inv_icm42600_timestamp_interrupt(ts, st->fifo.period, st->fifo.nb.total,
+-                                       st->fifo.nb.gyro, st->timestamp.gyro);
++      inv_sensors_timestamp_interrupt(ts, st->fifo.period, st->fifo.nb.total,
++                                      st->fifo.nb.gyro, st->timestamp.gyro);
+       if (st->fifo.nb.gyro > 0) {
+               ret = inv_icm42600_gyro_parse_fifo(st->indio_gyro);
+               if (ret)
+@@ -526,8 +526,8 @@ int inv_icm42600_buffer_fifo_parse(struc
+       /* handle accelerometer timestamp and FIFO data parsing */
+       ts = iio_priv(st->indio_accel);
+-      inv_icm42600_timestamp_interrupt(ts, st->fifo.period, st->fifo.nb.total,
+-                                       st->fifo.nb.accel, st->timestamp.accel);
++      inv_sensors_timestamp_interrupt(ts, st->fifo.period, st->fifo.nb.total,
++                                      st->fifo.nb.accel, st->timestamp.accel);
+       if (st->fifo.nb.accel > 0) {
+               ret = inv_icm42600_accel_parse_fifo(st->indio_accel);
+               if (ret)
+@@ -540,7 +540,7 @@ int inv_icm42600_buffer_fifo_parse(struc
+ int inv_icm42600_buffer_hwfifo_flush(struct inv_icm42600_state *st,
+                                    unsigned int count)
+ {
+-      struct inv_icm42600_timestamp *ts;
++      struct inv_sensors_timestamp *ts;
+       int64_t gyro_ts, accel_ts;
+       int ret;
+@@ -556,9 +556,9 @@ int inv_icm42600_buffer_hwfifo_flush(str
+       if (st->fifo.nb.gyro > 0) {
+               ts = iio_priv(st->indio_gyro);
+-              inv_icm42600_timestamp_interrupt(ts, st->fifo.period,
+-                                               st->fifo.nb.total, st->fifo.nb.gyro,
+-                                               gyro_ts);
++              inv_sensors_timestamp_interrupt(ts, st->fifo.period,
++                                              st->fifo.nb.total, st->fifo.nb.gyro,
++                                              gyro_ts);
+               ret = inv_icm42600_gyro_parse_fifo(st->indio_gyro);
+               if (ret)
+                       return ret;
+@@ -566,9 +566,9 @@ int inv_icm42600_buffer_hwfifo_flush(str
+       if (st->fifo.nb.accel > 0) {
+               ts = iio_priv(st->indio_accel);
+-              inv_icm42600_timestamp_interrupt(ts, st->fifo.period,
+-                                               st->fifo.nb.total, st->fifo.nb.accel,
+-                                               accel_ts);
++              inv_sensors_timestamp_interrupt(ts, st->fifo.period,
++                                              st->fifo.nb.total, st->fifo.nb.accel,
++                                              accel_ts);
+               ret = inv_icm42600_accel_parse_fifo(st->indio_accel);
+               if (ret)
+                       return ret;
+--- a/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c
++++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c
+@@ -16,7 +16,7 @@
+ #include <linux/property.h>
+ #include <linux/regmap.h>
+-#include <linux/iio/common/inv_icm42600_timestamp.h>
++#include <linux/iio/common/inv_sensors_timestamp.h>
+ #include <linux/iio/iio.h>
+ #include "inv_icm42600.h"
+@@ -720,8 +720,8 @@ out_unlock:
+ static int __maybe_unused inv_icm42600_resume(struct device *dev)
+ {
+       struct inv_icm42600_state *st = dev_get_drvdata(dev);
+-      struct inv_icm42600_timestamp *gyro_ts = iio_priv(st->indio_gyro);
+-      struct inv_icm42600_timestamp *accel_ts = iio_priv(st->indio_accel);
++      struct inv_sensors_timestamp *gyro_ts = iio_priv(st->indio_gyro);
++      struct inv_sensors_timestamp *accel_ts = iio_priv(st->indio_accel);
+       int ret = 0;
+       mutex_lock(&st->lock);
+@@ -742,8 +742,8 @@ static int __maybe_unused inv_icm42600_r
+       /* restore FIFO data streaming */
+       if (st->fifo.on) {
+-              inv_icm42600_timestamp_reset(gyro_ts);
+-              inv_icm42600_timestamp_reset(accel_ts);
++              inv_sensors_timestamp_reset(gyro_ts);
++              inv_sensors_timestamp_reset(accel_ts);
+               ret = regmap_write(st->map, INV_ICM42600_REG_FIFO_CONFIG,
+                                  INV_ICM42600_FIFO_CONFIG_STREAM);
+       }
+--- a/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c
++++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c
+@@ -12,7 +12,7 @@
+ #include <linux/math64.h>
+ #include <linux/iio/buffer.h>
+-#include <linux/iio/common/inv_icm42600_timestamp.h>
++#include <linux/iio/common/inv_sensors_timestamp.h>
+ #include <linux/iio/iio.h>
+ #include <linux/iio/kfifo_buf.h>
+@@ -99,7 +99,7 @@ static int inv_icm42600_gyro_update_scan
+                                             const unsigned long *scan_mask)
+ {
+       struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev);
+-      struct inv_icm42600_timestamp *ts = iio_priv(indio_dev);
++      struct inv_sensors_timestamp *ts = iio_priv(indio_dev);
+       struct inv_icm42600_sensor_conf conf = INV_ICM42600_SENSOR_CONF_INIT;
+       unsigned int fifo_en = 0;
+       unsigned int sleep_gyro = 0;
+@@ -127,7 +127,7 @@ static int inv_icm42600_gyro_update_scan
+       }
+       /* update data FIFO write */
+-      inv_icm42600_timestamp_apply_odr(ts, 0, 0, 0);
++      inv_sensors_timestamp_apply_odr(ts, 0, 0, 0);
+       ret = inv_icm42600_buffer_set_fifo_en(st, fifo_en | st->fifo.en);
+ out_unlock:
+@@ -320,7 +320,7 @@ static int inv_icm42600_gyro_write_odr(s
+                                      int val, int val2)
+ {
+       struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev);
+-      struct inv_icm42600_timestamp *ts = iio_priv(indio_dev);
++      struct inv_sensors_timestamp *ts = iio_priv(indio_dev);
+       struct device *dev = regmap_get_device(st->map);
+       unsigned int idx;
+       struct inv_icm42600_sensor_conf conf = INV_ICM42600_SENSOR_CONF_INIT;
+@@ -341,8 +341,8 @@ static int inv_icm42600_gyro_write_odr(s
+       pm_runtime_get_sync(dev);
+       mutex_lock(&st->lock);
+-      ret = inv_icm42600_timestamp_update_odr(ts, inv_icm42600_odr_to_period(conf.odr),
+-                                              iio_buffer_enabled(indio_dev));
++      ret = inv_sensors_timestamp_update_odr(ts, inv_icm42600_odr_to_period(conf.odr),
++                                             iio_buffer_enabled(indio_dev));
+       if (ret)
+               goto out_unlock;
+@@ -717,7 +717,8 @@ struct iio_dev *inv_icm42600_gyro_init(s
+ {
+       struct device *dev = regmap_get_device(st->map);
+       const char *name;
+-      struct inv_icm42600_timestamp *ts;
++      struct inv_sensors_timestamp_chip ts_chip;
++      struct inv_sensors_timestamp *ts;
+       struct iio_dev *indio_dev;
+       int ret;
+@@ -729,8 +730,15 @@ struct iio_dev *inv_icm42600_gyro_init(s
+       if (!indio_dev)
+               return ERR_PTR(-ENOMEM);
++      /*
++       * clock period is 32kHz (31250ns)
++       * jitter is +/- 2% (20 per mille)
++       */
++      ts_chip.clock_period = 31250;
++      ts_chip.jitter = 20;
++      ts_chip.init_period = inv_icm42600_odr_to_period(st->conf.accel.odr);
+       ts = iio_priv(indio_dev);
+-      inv_icm42600_timestamp_init(ts, inv_icm42600_odr_to_period(st->conf.gyro.odr));
++      inv_sensors_timestamp_init(ts, &ts_chip);
+       iio_device_set_drvdata(indio_dev, st);
+       indio_dev->name = name;
+@@ -757,7 +765,7 @@ struct iio_dev *inv_icm42600_gyro_init(s
+ int inv_icm42600_gyro_parse_fifo(struct iio_dev *indio_dev)
+ {
+       struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev);
+-      struct inv_icm42600_timestamp *ts = iio_priv(indio_dev);
++      struct inv_sensors_timestamp *ts = iio_priv(indio_dev);
+       ssize_t i, size;
+       unsigned int no;
+       const void *accel, *gyro, *timestamp;
+@@ -781,13 +789,13 @@ int inv_icm42600_gyro_parse_fifo(struct
+               /* update odr */
+               if (odr & INV_ICM42600_SENSOR_GYRO)
+-                      inv_icm42600_timestamp_apply_odr(ts, st->fifo.period,
+-                                                       st->fifo.nb.total, no);
++                      inv_sensors_timestamp_apply_odr(ts, st->fifo.period,
++                                                      st->fifo.nb.total, no);
+               memcpy(&buffer.gyro, gyro, sizeof(buffer.gyro));
+               /* convert 8 bits FIFO temperature in high resolution format */
+               buffer.temp = temp ? (*temp * 64) : 0;
+-              ts_val = inv_icm42600_timestamp_pop(ts);
++              ts_val = inv_sensors_timestamp_pop(ts);
+               iio_push_to_buffers_with_timestamp(indio_dev, &buffer, ts_val);
+       }
+--- a/include/linux/iio/common/inv_icm42600_timestamp.h
++++ /dev/null
+@@ -1,79 +0,0 @@
+-/* SPDX-License-Identifier: GPL-2.0-or-later */
+-/*
+- * Copyright (C) 2020 Invensense, Inc.
+- */
+-
+-#ifndef INV_ICM42600_TIMESTAMP_H_
+-#define INV_ICM42600_TIMESTAMP_H_
+-
+-/**
+- * struct inv_icm42600_timestamp_interval - timestamps interval
+- * @lo:       interval lower bound
+- * @up:       interval upper bound
+- */
+-struct inv_icm42600_timestamp_interval {
+-      int64_t lo;
+-      int64_t up;
+-};
+-
+-/**
+- * struct inv_icm42600_timestamp_acc - accumulator for computing an estimation
+- * @val:      current estimation of the value, the mean of all values
+- * @idx:      current index of the next free place in values table
+- * @values:   table of all measured values, use for computing the mean
+- */
+-struct inv_icm42600_timestamp_acc {
+-      uint32_t val;
+-      size_t idx;
+-      uint32_t values[32];
+-};
+-
+-/**
+- * struct inv_icm42600_timestamp - timestamp management states
+- * @it:                       interrupts interval timestamps
+- * @timestamp:                store last timestamp for computing next data timestamp
+- * @mult:             current internal period multiplier
+- * @new_mult:         new set internal period multiplier (not yet effective)
+- * @period:           measured current period of the sensor
+- * @chip_period:      accumulator for computing internal chip period
+- */
+-struct inv_icm42600_timestamp {
+-      struct inv_icm42600_timestamp_interval it;
+-      int64_t timestamp;
+-      uint32_t mult;
+-      uint32_t new_mult;
+-      uint32_t period;
+-      struct inv_icm42600_timestamp_acc chip_period;
+-};
+-
+-void inv_icm42600_timestamp_init(struct inv_icm42600_timestamp *ts,
+-                               uint32_t period);
+-
+-int inv_icm42600_timestamp_update_odr(struct inv_icm42600_timestamp *ts,
+-                                    uint32_t period, bool fifo);
+-
+-void inv_icm42600_timestamp_interrupt(struct inv_icm42600_timestamp *ts,
+-                                    uint32_t fifo_period, size_t fifo_nb,
+-                                    size_t sensor_nb, int64_t timestamp);
+-
+-static inline int64_t
+-inv_icm42600_timestamp_pop(struct inv_icm42600_timestamp *ts)
+-{
+-      ts->timestamp += ts->period;
+-      return ts->timestamp;
+-}
+-
+-void inv_icm42600_timestamp_apply_odr(struct inv_icm42600_timestamp *ts,
+-                                    uint32_t fifo_period, size_t fifo_nb,
+-                                    unsigned int fifo_no);
+-
+-static inline void
+-inv_icm42600_timestamp_reset(struct inv_icm42600_timestamp *ts)
+-{
+-      const struct inv_icm42600_timestamp_interval interval_init = {0LL, 0LL};
+-
+-      ts->it = interval_init;
+-      ts->timestamp = 0;
+-}
+-
+-#endif
+--- /dev/null
++++ b/include/linux/iio/common/inv_sensors_timestamp.h
+@@ -0,0 +1,95 @@
++/* SPDX-License-Identifier: GPL-2.0-or-later */
++/*
++ * Copyright (C) 2020 Invensense, Inc.
++ */
++
++#ifndef INV_SENSORS_TIMESTAMP_H_
++#define INV_SENSORS_TIMESTAMP_H_
++
++/**
++ * struct inv_sensors_timestamp_chip - chip internal properties
++ * @clock_period:     internal clock period in ns
++ * @jitter:           acceptable jitter in per-mille
++ * @init_period:      chip initial period at reset in ns
++ */
++struct inv_sensors_timestamp_chip {
++      uint32_t clock_period;
++      uint32_t jitter;
++      uint32_t init_period;
++};
++
++/**
++ * struct inv_sensors_timestamp_interval - timestamps interval
++ * @lo:       interval lower bound
++ * @up:       interval upper bound
++ */
++struct inv_sensors_timestamp_interval {
++      int64_t lo;
++      int64_t up;
++};
++
++/**
++ * struct inv_sensors_timestamp_acc - accumulator for computing an estimation
++ * @val:      current estimation of the value, the mean of all values
++ * @idx:      current index of the next free place in values table
++ * @values:   table of all measured values, use for computing the mean
++ */
++struct inv_sensors_timestamp_acc {
++      uint32_t val;
++      size_t idx;
++      uint32_t values[32];
++};
++
++/**
++ * struct inv_sensors_timestamp - timestamp management states
++ * @chip:             chip internal characteristics
++ * @min_period:               minimal acceptable clock period
++ * @max_period:               maximal acceptable clock period
++ * @it:                       interrupts interval timestamps
++ * @timestamp:                store last timestamp for computing next data timestamp
++ * @mult:             current internal period multiplier
++ * @new_mult:         new set internal period multiplier (not yet effective)
++ * @period:           measured current period of the sensor
++ * @chip_period:      accumulator for computing internal chip period
++ */
++struct inv_sensors_timestamp {
++      struct inv_sensors_timestamp_chip chip;
++      uint32_t min_period;
++      uint32_t max_period;
++      struct inv_sensors_timestamp_interval it;
++      int64_t timestamp;
++      uint32_t mult;
++      uint32_t new_mult;
++      uint32_t period;
++      struct inv_sensors_timestamp_acc chip_period;
++};
++
++void inv_sensors_timestamp_init(struct inv_sensors_timestamp *ts,
++                              const struct inv_sensors_timestamp_chip *chip);
++
++int inv_sensors_timestamp_update_odr(struct inv_sensors_timestamp *ts,
++                                   uint32_t period, bool fifo);
++
++void inv_sensors_timestamp_interrupt(struct inv_sensors_timestamp *ts,
++                                   uint32_t fifo_period, size_t fifo_nb,
++                                   size_t sensor_nb, int64_t timestamp);
++
++static inline int64_t inv_sensors_timestamp_pop(struct inv_sensors_timestamp *ts)
++{
++      ts->timestamp += ts->period;
++      return ts->timestamp;
++}
++
++void inv_sensors_timestamp_apply_odr(struct inv_sensors_timestamp *ts,
++                                   uint32_t fifo_period, size_t fifo_nb,
++                                   unsigned int fifo_no);
++
++static inline void inv_sensors_timestamp_reset(struct inv_sensors_timestamp *ts)
++{
++      const struct inv_sensors_timestamp_interval interval_init = {0LL, 0LL};
++
++      ts->it = interval_init;
++      ts->timestamp = 0;
++}
++
++#endif
diff --git a/queue-5.15/iio-move-inv_icm42600-timestamp-module-in-common.patch b/queue-5.15/iio-move-inv_icm42600-timestamp-module-in-common.patch
new file mode 100644 (file)
index 0000000..3bad550
--- /dev/null
@@ -0,0 +1,741 @@
+From stable+bounces-274058-greg=kroah.com@vger.kernel.org Tue Jul 14 03:16:34 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 13 Jul 2026 21:16:21 -0400
+Subject: iio: move inv_icm42600 timestamp module in common
+To: stable@vger.kernel.org
+Cc: Jean-Baptiste Maneyrol <jean-baptiste.maneyrol@tdk.com>, Andy Shevchenko <andy.shevchenko@gmail.com>, Jonathan Cameron <Jonathan.Cameron@huawei.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260714011627.2188779-2-sashal@kernel.org>
+
+From: Jean-Baptiste Maneyrol <jean-baptiste.maneyrol@tdk.com>
+
+[ Upstream commit d99ff463ecf651437e9e4abe68f331dfb6b5bd9d ]
+
+Create new inv_sensors common modules and move inv_icm42600
+timestamp module inside. This module will be used by IMUs and
+also in the future by other chips.
+
+Modify inv_icm42600 driver to use timestamp module and do some
+headers cleanup.
+
+Signed-off-by: Jean-Baptiste Maneyrol <jean-baptiste.maneyrol@tdk.com>
+Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
+Link: https://lore.kernel.org/r/20230606162147.79667-3-inv.git-commit@tdk.com
+Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
+Stable-dep-of: affe3f077d7a ("iio: imu: inv_icm42600: fix timestamping by limiting FIFO reading")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/iio/common/Kconfig                            |  1 +
+ drivers/iio/common/Makefile                           |  1 +
+ drivers/iio/common/inv_sensors/Kconfig                |  7 +++++++
+ drivers/iio/common/inv_sensors/Makefile               |  6 ++++++
+ .../inv_sensors}/inv_icm42600_timestamp.c             | 11 ++++++++++-
+ drivers/iio/imu/inv_icm42600/Kconfig                  |  1 +
+ drivers/iio/imu/inv_icm42600/Makefile                 |  1 -
+ drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c     |  5 +++--
+ drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c    |  5 +++--
+ drivers/iio/imu/inv_icm42600/inv_icm42600_core.c      |  4 +++-
+ drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c      |  5 +++--
+ .../linux/iio/common}/inv_icm42600_timestamp.h        |  0
+ drivers/iio/common/Kconfig                              |    1 
+ drivers/iio/common/Makefile                             |    1 
+ drivers/iio/common/inv_sensors/Kconfig                  |    7 
+ drivers/iio/common/inv_sensors/Makefile                 |    6 
+ drivers/iio/common/inv_sensors/inv_icm42600_timestamp.c |  192 ++++++++++++++++
+ drivers/iio/imu/inv_icm42600/Kconfig                    |    1 
+ drivers/iio/imu/inv_icm42600/Makefile                   |    1 
+ drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c       |    5 
+ drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c      |    5 
+ drivers/iio/imu/inv_icm42600/inv_icm42600_core.c        |    4 
+ drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c        |    5 
+ drivers/iio/imu/inv_icm42600/inv_icm42600_timestamp.c   |  183 ---------------
+ drivers/iio/imu/inv_icm42600/inv_icm42600_timestamp.h   |   79 ------
+ include/linux/iio/common/inv_icm42600_timestamp.h       |   79 ++++++
+ 14 files changed, 299 insertions(+), 270 deletions(-)
+ create mode 100644 drivers/iio/common/inv_sensors/Kconfig
+ create mode 100644 drivers/iio/common/inv_sensors/Makefile
+ rename drivers/iio/{imu/inv_icm42600 => common/inv_sensors}/inv_icm42600_timestamp.c (91%)
+ rename {drivers/iio/imu/inv_icm42600 => include/linux/iio/common}/inv_icm42600_timestamp.h (100%)
+
+--- a/drivers/iio/common/Kconfig
++++ b/drivers/iio/common/Kconfig
+@@ -5,6 +5,7 @@
+ source "drivers/iio/common/cros_ec_sensors/Kconfig"
+ source "drivers/iio/common/hid-sensors/Kconfig"
++source "drivers/iio/common/inv_sensors/Kconfig"
+ source "drivers/iio/common/ms_sensors/Kconfig"
+ source "drivers/iio/common/scmi_sensors/Kconfig"
+ source "drivers/iio/common/ssp_sensors/Kconfig"
+--- a/drivers/iio/common/Makefile
++++ b/drivers/iio/common/Makefile
+@@ -10,6 +10,7 @@
+ # When adding new entries keep the list in alphabetical order
+ obj-y += cros_ec_sensors/
+ obj-y += hid-sensors/
++obj-y += inv_sensors/
+ obj-y += ms_sensors/
+ obj-y += scmi_sensors/
+ obj-y += ssp_sensors/
+--- /dev/null
++++ b/drivers/iio/common/inv_sensors/Kconfig
+@@ -0,0 +1,7 @@
++# SPDX-License-Identifier: GPL-2.0-only
++#
++# TDK-InvenSense sensors common library
++#
++
++config IIO_INV_SENSORS_TIMESTAMP
++      tristate
+--- /dev/null
++++ b/drivers/iio/common/inv_sensors/Makefile
+@@ -0,0 +1,6 @@
++# SPDX-License-Identifier: GPL-2.0
++#
++# Makefile for TDK-InvenSense sensors module.
++#
++
++obj-$(CONFIG_IIO_INV_SENSORS_TIMESTAMP) += inv_icm42600_timestamp.o
+--- /dev/null
++++ b/drivers/iio/common/inv_sensors/inv_icm42600_timestamp.c
+@@ -0,0 +1,192 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * Copyright (C) 2020 Invensense, Inc.
++ */
++
++#include <linux/errno.h>
++#include <linux/kernel.h>
++#include <linux/math64.h>
++#include <linux/module.h>
++
++#include <linux/iio/common/inv_icm42600_timestamp.h>
++
++/* internal chip period is 32kHz, 31250ns */
++#define INV_ICM42600_TIMESTAMP_PERIOD         31250
++/* allow a jitter of +/- 2% */
++#define INV_ICM42600_TIMESTAMP_JITTER         2
++/* compute min and max periods accepted */
++#define INV_ICM42600_TIMESTAMP_MIN_PERIOD(_p)         \
++      (((_p) * (100 - INV_ICM42600_TIMESTAMP_JITTER)) / 100)
++#define INV_ICM42600_TIMESTAMP_MAX_PERIOD(_p)         \
++      (((_p) * (100 + INV_ICM42600_TIMESTAMP_JITTER)) / 100)
++
++/* Add a new value inside an accumulator and update the estimate value */
++static void inv_update_acc(struct inv_icm42600_timestamp_acc *acc, uint32_t val)
++{
++      uint64_t sum = 0;
++      size_t i;
++
++      acc->values[acc->idx++] = val;
++      if (acc->idx >= ARRAY_SIZE(acc->values))
++              acc->idx = 0;
++
++      /* compute the mean of all stored values, use 0 as empty slot */
++      for (i = 0; i < ARRAY_SIZE(acc->values); ++i) {
++              if (acc->values[i] == 0)
++                      break;
++              sum += acc->values[i];
++      }
++
++      acc->val = div_u64(sum, i);
++}
++
++void inv_icm42600_timestamp_init(struct inv_icm42600_timestamp *ts,
++                               uint32_t period)
++{
++      /* initial odr for sensor after reset is 1kHz */
++      const uint32_t default_period = 1000000;
++
++      /* current multiplier and period values after reset */
++      ts->mult = default_period / INV_ICM42600_TIMESTAMP_PERIOD;
++      ts->period = default_period;
++      /* new set multiplier is the one from chip initialization */
++      ts->new_mult = period / INV_ICM42600_TIMESTAMP_PERIOD;
++
++      /* use theoretical value for chip period */
++      inv_update_acc(&ts->chip_period, INV_ICM42600_TIMESTAMP_PERIOD);
++}
++EXPORT_SYMBOL_NS_GPL(inv_icm42600_timestamp_init, IIO_INV_SENSORS_TIMESTAMP);
++
++int inv_icm42600_timestamp_update_odr(struct inv_icm42600_timestamp *ts,
++                                    uint32_t period, bool fifo)
++{
++      /* when FIFO is on, prevent odr change if one is already pending */
++      if (fifo && ts->new_mult != 0)
++              return -EAGAIN;
++
++      ts->new_mult = period / INV_ICM42600_TIMESTAMP_PERIOD;
++
++      return 0;
++}
++EXPORT_SYMBOL_NS_GPL(inv_icm42600_timestamp_update_odr, IIO_INV_SENSORS_TIMESTAMP);
++
++static bool inv_validate_period(uint32_t period, uint32_t mult)
++{
++      const uint32_t chip_period = INV_ICM42600_TIMESTAMP_PERIOD;
++      uint32_t period_min, period_max;
++
++      /* check that period is acceptable */
++      period_min = INV_ICM42600_TIMESTAMP_MIN_PERIOD(chip_period) * mult;
++      period_max = INV_ICM42600_TIMESTAMP_MAX_PERIOD(chip_period) * mult;
++      if (period > period_min && period < period_max)
++              return true;
++      else
++              return false;
++}
++
++static bool inv_compute_chip_period(struct inv_icm42600_timestamp *ts,
++                                  uint32_t mult, uint32_t period)
++{
++      uint32_t new_chip_period;
++
++      if (!inv_validate_period(period, mult))
++              return false;
++
++      /* update chip internal period estimation */
++      new_chip_period = period / mult;
++      inv_update_acc(&ts->chip_period, new_chip_period);
++
++      return true;
++}
++
++void inv_icm42600_timestamp_interrupt(struct inv_icm42600_timestamp *ts,
++                                    uint32_t fifo_period, size_t fifo_nb,
++                                    size_t sensor_nb, int64_t timestamp)
++{
++      struct inv_icm42600_timestamp_interval *it;
++      int64_t delta, interval;
++      const uint32_t fifo_mult = fifo_period / INV_ICM42600_TIMESTAMP_PERIOD;
++      uint32_t period = ts->period;
++      int32_t m;
++      bool valid = false;
++
++      if (fifo_nb == 0)
++              return;
++
++      /* update interrupt timestamp and compute chip and sensor periods */
++      it = &ts->it;
++      it->lo = it->up;
++      it->up = timestamp;
++      delta = it->up - it->lo;
++      if (it->lo != 0) {
++              /* compute period: delta time divided by number of samples */
++              period = div_s64(delta, fifo_nb);
++              valid = inv_compute_chip_period(ts, fifo_mult, period);
++              /* update sensor period if chip internal period is updated */
++              if (valid)
++                      ts->period = ts->mult * ts->chip_period.val;
++      }
++
++      /* no previous data, compute theoritical value from interrupt */
++      if (ts->timestamp == 0) {
++              /* elapsed time: sensor period * sensor samples number */
++              interval = (int64_t)ts->period * (int64_t)sensor_nb;
++              ts->timestamp = it->up - interval;
++              return;
++      }
++
++      /* if interrupt interval is valid, sync with interrupt timestamp */
++      if (valid) {
++              /* compute measured fifo_period */
++              fifo_period = fifo_mult * ts->chip_period.val;
++              /* delta time between last sample and last interrupt */
++              delta = it->lo - ts->timestamp;
++              /* if there are multiple samples, go back to first one */
++              while (delta >= (fifo_period * 3 / 2))
++                      delta -= fifo_period;
++              /* compute maximal adjustment value */
++              m = INV_ICM42600_TIMESTAMP_MAX_PERIOD(ts->period) - ts->period;
++              if (delta > m)
++                      delta = m;
++              else if (delta < -m)
++                      delta = -m;
++              ts->timestamp += delta;
++      }
++}
++EXPORT_SYMBOL_NS_GPL(inv_icm42600_timestamp_interrupt, IIO_INV_SENSORS_TIMESTAMP);
++
++void inv_icm42600_timestamp_apply_odr(struct inv_icm42600_timestamp *ts,
++                                    uint32_t fifo_period, size_t fifo_nb,
++                                    unsigned int fifo_no)
++{
++      int64_t interval;
++      uint32_t fifo_mult;
++
++      if (ts->new_mult == 0)
++              return;
++
++      /* update to new multiplier and update period */
++      ts->mult = ts->new_mult;
++      ts->new_mult = 0;
++      ts->period = ts->mult * ts->chip_period.val;
++
++      /*
++       * After ODR change the time interval with the previous sample is
++       * undertermined (depends when the change occures). So we compute the
++       * timestamp from the current interrupt using the new FIFO period, the
++       * total number of samples and the current sample numero.
++       */
++      if (ts->timestamp != 0) {
++              /* compute measured fifo period */
++              fifo_mult = fifo_period / INV_ICM42600_TIMESTAMP_PERIOD;
++              fifo_period = fifo_mult * ts->chip_period.val;
++              /* computes time interval between interrupt and this sample */
++              interval = (int64_t)(fifo_nb - fifo_no) * (int64_t)fifo_period;
++              ts->timestamp = ts->it.up - interval;
++      }
++}
++EXPORT_SYMBOL_NS_GPL(inv_icm42600_timestamp_apply_odr, IIO_INV_SENSORS_TIMESTAMP);
++
++MODULE_AUTHOR("InvenSense, Inc.");
++MODULE_DESCRIPTION("InvenSense sensors timestamp module");
++MODULE_LICENSE("GPL");
+--- a/drivers/iio/imu/inv_icm42600/Kconfig
++++ b/drivers/iio/imu/inv_icm42600/Kconfig
+@@ -3,6 +3,7 @@
+ config INV_ICM42600
+       tristate
+       select IIO_BUFFER
++      select IIO_INV_SENSORS_TIMESTAMP
+ config INV_ICM42600_I2C
+       tristate "InvenSense ICM-426xx I2C driver"
+--- a/drivers/iio/imu/inv_icm42600/Makefile
++++ b/drivers/iio/imu/inv_icm42600/Makefile
+@@ -6,7 +6,6 @@ inv-icm42600-y += inv_icm42600_gyro.o
+ inv-icm42600-y += inv_icm42600_accel.o
+ inv-icm42600-y += inv_icm42600_temp.o
+ inv-icm42600-y += inv_icm42600_buffer.o
+-inv-icm42600-y += inv_icm42600_timestamp.o
+ obj-$(CONFIG_INV_ICM42600_I2C) += inv-icm42600-i2c.o
+ inv-icm42600-i2c-y += inv_icm42600_i2c.o
+--- a/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c
++++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c
+@@ -10,14 +10,15 @@
+ #include <linux/regmap.h>
+ #include <linux/delay.h>
+ #include <linux/math64.h>
+-#include <linux/iio/iio.h>
++
+ #include <linux/iio/buffer.h>
++#include <linux/iio/common/inv_icm42600_timestamp.h>
++#include <linux/iio/iio.h>
+ #include <linux/iio/kfifo_buf.h>
+ #include "inv_icm42600.h"
+ #include "inv_icm42600_temp.h"
+ #include "inv_icm42600_buffer.h"
+-#include "inv_icm42600_timestamp.h"
+ #define INV_ICM42600_ACCEL_CHAN(_modifier, _index, _ext_info)         \
+       {                                                               \
+--- a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c
++++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c
+@@ -9,11 +9,12 @@
+ #include <linux/pm_runtime.h>
+ #include <linux/regmap.h>
+ #include <linux/delay.h>
+-#include <linux/iio/iio.h>
++
+ #include <linux/iio/buffer.h>
++#include <linux/iio/common/inv_icm42600_timestamp.h>
++#include <linux/iio/iio.h>
+ #include "inv_icm42600.h"
+-#include "inv_icm42600_timestamp.h"
+ #include "inv_icm42600_buffer.h"
+ /* FIFO header: 1 byte */
+--- a/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c
++++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c
+@@ -15,11 +15,12 @@
+ #include <linux/pm_runtime.h>
+ #include <linux/property.h>
+ #include <linux/regmap.h>
++
++#include <linux/iio/common/inv_icm42600_timestamp.h>
+ #include <linux/iio/iio.h>
+ #include "inv_icm42600.h"
+ #include "inv_icm42600_buffer.h"
+-#include "inv_icm42600_timestamp.h"
+ static const struct regmap_range_cfg inv_icm42600_regmap_ranges[] = {
+       {
+@@ -798,3 +799,4 @@ EXPORT_SYMBOL_GPL(inv_icm42600_pm_ops);
+ MODULE_AUTHOR("InvenSense, Inc.");
+ MODULE_DESCRIPTION("InvenSense ICM-426xx device driver");
+ MODULE_LICENSE("GPL");
++MODULE_IMPORT_NS(IIO_INV_SENSORS_TIMESTAMP);
+--- a/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c
++++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c
+@@ -10,14 +10,15 @@
+ #include <linux/regmap.h>
+ #include <linux/delay.h>
+ #include <linux/math64.h>
+-#include <linux/iio/iio.h>
++
+ #include <linux/iio/buffer.h>
++#include <linux/iio/common/inv_icm42600_timestamp.h>
++#include <linux/iio/iio.h>
+ #include <linux/iio/kfifo_buf.h>
+ #include "inv_icm42600.h"
+ #include "inv_icm42600_temp.h"
+ #include "inv_icm42600_buffer.h"
+-#include "inv_icm42600_timestamp.h"
+ #define INV_ICM42600_GYRO_CHAN(_modifier, _index, _ext_info)          \
+       {                                                               \
+--- a/drivers/iio/imu/inv_icm42600/inv_icm42600_timestamp.c
++++ /dev/null
+@@ -1,183 +0,0 @@
+-// SPDX-License-Identifier: GPL-2.0-or-later
+-/*
+- * Copyright (C) 2020 Invensense, Inc.
+- */
+-
+-#include <linux/errno.h>
+-#include <linux/kernel.h>
+-#include <linux/math64.h>
+-
+-#include "inv_icm42600_timestamp.h"
+-
+-/* internal chip period is 32kHz, 31250ns */
+-#define INV_ICM42600_TIMESTAMP_PERIOD         31250
+-/* allow a jitter of +/- 2% */
+-#define INV_ICM42600_TIMESTAMP_JITTER         2
+-/* compute min and max periods accepted */
+-#define INV_ICM42600_TIMESTAMP_MIN_PERIOD(_p)         \
+-      (((_p) * (100 - INV_ICM42600_TIMESTAMP_JITTER)) / 100)
+-#define INV_ICM42600_TIMESTAMP_MAX_PERIOD(_p)         \
+-      (((_p) * (100 + INV_ICM42600_TIMESTAMP_JITTER)) / 100)
+-
+-/* Add a new value inside an accumulator and update the estimate value */
+-static void inv_update_acc(struct inv_icm42600_timestamp_acc *acc, uint32_t val)
+-{
+-      uint64_t sum = 0;
+-      size_t i;
+-
+-      acc->values[acc->idx++] = val;
+-      if (acc->idx >= ARRAY_SIZE(acc->values))
+-              acc->idx = 0;
+-
+-      /* compute the mean of all stored values, use 0 as empty slot */
+-      for (i = 0; i < ARRAY_SIZE(acc->values); ++i) {
+-              if (acc->values[i] == 0)
+-                      break;
+-              sum += acc->values[i];
+-      }
+-
+-      acc->val = div_u64(sum, i);
+-}
+-
+-void inv_icm42600_timestamp_init(struct inv_icm42600_timestamp *ts,
+-                               uint32_t period)
+-{
+-      /* initial odr for sensor after reset is 1kHz */
+-      const uint32_t default_period = 1000000;
+-
+-      /* current multiplier and period values after reset */
+-      ts->mult = default_period / INV_ICM42600_TIMESTAMP_PERIOD;
+-      ts->period = default_period;
+-      /* new set multiplier is the one from chip initialization */
+-      ts->new_mult = period / INV_ICM42600_TIMESTAMP_PERIOD;
+-
+-      /* use theoretical value for chip period */
+-      inv_update_acc(&ts->chip_period, INV_ICM42600_TIMESTAMP_PERIOD);
+-}
+-
+-int inv_icm42600_timestamp_update_odr(struct inv_icm42600_timestamp *ts,
+-                                    uint32_t period, bool fifo)
+-{
+-      /* when FIFO is on, prevent odr change if one is already pending */
+-      if (fifo && ts->new_mult != 0)
+-              return -EAGAIN;
+-
+-      ts->new_mult = period / INV_ICM42600_TIMESTAMP_PERIOD;
+-
+-      return 0;
+-}
+-
+-static bool inv_validate_period(uint32_t period, uint32_t mult)
+-{
+-      const uint32_t chip_period = INV_ICM42600_TIMESTAMP_PERIOD;
+-      uint32_t period_min, period_max;
+-
+-      /* check that period is acceptable */
+-      period_min = INV_ICM42600_TIMESTAMP_MIN_PERIOD(chip_period) * mult;
+-      period_max = INV_ICM42600_TIMESTAMP_MAX_PERIOD(chip_period) * mult;
+-      if (period > period_min && period < period_max)
+-              return true;
+-      else
+-              return false;
+-}
+-
+-static bool inv_compute_chip_period(struct inv_icm42600_timestamp *ts,
+-                                  uint32_t mult, uint32_t period)
+-{
+-      uint32_t new_chip_period;
+-
+-      if (!inv_validate_period(period, mult))
+-              return false;
+-
+-      /* update chip internal period estimation */
+-      new_chip_period = period / mult;
+-      inv_update_acc(&ts->chip_period, new_chip_period);
+-
+-      return true;
+-}
+-
+-void inv_icm42600_timestamp_interrupt(struct inv_icm42600_timestamp *ts,
+-                                    uint32_t fifo_period, size_t fifo_nb,
+-                                    size_t sensor_nb, int64_t timestamp)
+-{
+-      struct inv_icm42600_timestamp_interval *it;
+-      int64_t delta, interval;
+-      const uint32_t fifo_mult = fifo_period / INV_ICM42600_TIMESTAMP_PERIOD;
+-      uint32_t period = ts->period;
+-      int32_t m;
+-      bool valid = false;
+-
+-      if (fifo_nb == 0)
+-              return;
+-
+-      /* update interrupt timestamp and compute chip and sensor periods */
+-      it = &ts->it;
+-      it->lo = it->up;
+-      it->up = timestamp;
+-      delta = it->up - it->lo;
+-      if (it->lo != 0) {
+-              /* compute period: delta time divided by number of samples */
+-              period = div_s64(delta, fifo_nb);
+-              valid = inv_compute_chip_period(ts, fifo_mult, period);
+-              /* update sensor period if chip internal period is updated */
+-              if (valid)
+-                      ts->period = ts->mult * ts->chip_period.val;
+-      }
+-
+-      /* no previous data, compute theoritical value from interrupt */
+-      if (ts->timestamp == 0) {
+-              /* elapsed time: sensor period * sensor samples number */
+-              interval = (int64_t)ts->period * (int64_t)sensor_nb;
+-              ts->timestamp = it->up - interval;
+-              return;
+-      }
+-
+-      /* if interrupt interval is valid, sync with interrupt timestamp */
+-      if (valid) {
+-              /* compute measured fifo_period */
+-              fifo_period = fifo_mult * ts->chip_period.val;
+-              /* delta time between last sample and last interrupt */
+-              delta = it->lo - ts->timestamp;
+-              /* if there are multiple samples, go back to first one */
+-              while (delta >= (fifo_period * 3 / 2))
+-                      delta -= fifo_period;
+-              /* compute maximal adjustment value */
+-              m = INV_ICM42600_TIMESTAMP_MAX_PERIOD(ts->period) - ts->period;
+-              if (delta > m)
+-                      delta = m;
+-              else if (delta < -m)
+-                      delta = -m;
+-              ts->timestamp += delta;
+-      }
+-}
+-
+-void inv_icm42600_timestamp_apply_odr(struct inv_icm42600_timestamp *ts,
+-                                    uint32_t fifo_period, size_t fifo_nb,
+-                                    unsigned int fifo_no)
+-{
+-      int64_t interval;
+-      uint32_t fifo_mult;
+-
+-      if (ts->new_mult == 0)
+-              return;
+-
+-      /* update to new multiplier and update period */
+-      ts->mult = ts->new_mult;
+-      ts->new_mult = 0;
+-      ts->period = ts->mult * ts->chip_period.val;
+-
+-      /*
+-       * After ODR change the time interval with the previous sample is
+-       * undertermined (depends when the change occures). So we compute the
+-       * timestamp from the current interrupt using the new FIFO period, the
+-       * total number of samples and the current sample numero.
+-       */
+-      if (ts->timestamp != 0) {
+-              /* compute measured fifo period */
+-              fifo_mult = fifo_period / INV_ICM42600_TIMESTAMP_PERIOD;
+-              fifo_period = fifo_mult * ts->chip_period.val;
+-              /* computes time interval between interrupt and this sample */
+-              interval = (int64_t)(fifo_nb - fifo_no) * (int64_t)fifo_period;
+-              ts->timestamp = ts->it.up - interval;
+-      }
+-}
+--- a/drivers/iio/imu/inv_icm42600/inv_icm42600_timestamp.h
++++ /dev/null
+@@ -1,79 +0,0 @@
+-/* SPDX-License-Identifier: GPL-2.0-or-later */
+-/*
+- * Copyright (C) 2020 Invensense, Inc.
+- */
+-
+-#ifndef INV_ICM42600_TIMESTAMP_H_
+-#define INV_ICM42600_TIMESTAMP_H_
+-
+-/**
+- * struct inv_icm42600_timestamp_interval - timestamps interval
+- * @lo:       interval lower bound
+- * @up:       interval upper bound
+- */
+-struct inv_icm42600_timestamp_interval {
+-      int64_t lo;
+-      int64_t up;
+-};
+-
+-/**
+- * struct inv_icm42600_timestamp_acc - accumulator for computing an estimation
+- * @val:      current estimation of the value, the mean of all values
+- * @idx:      current index of the next free place in values table
+- * @values:   table of all measured values, use for computing the mean
+- */
+-struct inv_icm42600_timestamp_acc {
+-      uint32_t val;
+-      size_t idx;
+-      uint32_t values[32];
+-};
+-
+-/**
+- * struct inv_icm42600_timestamp - timestamp management states
+- * @it:                       interrupts interval timestamps
+- * @timestamp:                store last timestamp for computing next data timestamp
+- * @mult:             current internal period multiplier
+- * @new_mult:         new set internal period multiplier (not yet effective)
+- * @period:           measured current period of the sensor
+- * @chip_period:      accumulator for computing internal chip period
+- */
+-struct inv_icm42600_timestamp {
+-      struct inv_icm42600_timestamp_interval it;
+-      int64_t timestamp;
+-      uint32_t mult;
+-      uint32_t new_mult;
+-      uint32_t period;
+-      struct inv_icm42600_timestamp_acc chip_period;
+-};
+-
+-void inv_icm42600_timestamp_init(struct inv_icm42600_timestamp *ts,
+-                               uint32_t period);
+-
+-int inv_icm42600_timestamp_update_odr(struct inv_icm42600_timestamp *ts,
+-                                    uint32_t period, bool fifo);
+-
+-void inv_icm42600_timestamp_interrupt(struct inv_icm42600_timestamp *ts,
+-                                    uint32_t fifo_period, size_t fifo_nb,
+-                                    size_t sensor_nb, int64_t timestamp);
+-
+-static inline int64_t
+-inv_icm42600_timestamp_pop(struct inv_icm42600_timestamp *ts)
+-{
+-      ts->timestamp += ts->period;
+-      return ts->timestamp;
+-}
+-
+-void inv_icm42600_timestamp_apply_odr(struct inv_icm42600_timestamp *ts,
+-                                    uint32_t fifo_period, size_t fifo_nb,
+-                                    unsigned int fifo_no);
+-
+-static inline void
+-inv_icm42600_timestamp_reset(struct inv_icm42600_timestamp *ts)
+-{
+-      const struct inv_icm42600_timestamp_interval interval_init = {0LL, 0LL};
+-
+-      ts->it = interval_init;
+-      ts->timestamp = 0;
+-}
+-
+-#endif
+--- /dev/null
++++ b/include/linux/iio/common/inv_icm42600_timestamp.h
+@@ -0,0 +1,79 @@
++/* SPDX-License-Identifier: GPL-2.0-or-later */
++/*
++ * Copyright (C) 2020 Invensense, Inc.
++ */
++
++#ifndef INV_ICM42600_TIMESTAMP_H_
++#define INV_ICM42600_TIMESTAMP_H_
++
++/**
++ * struct inv_icm42600_timestamp_interval - timestamps interval
++ * @lo:       interval lower bound
++ * @up:       interval upper bound
++ */
++struct inv_icm42600_timestamp_interval {
++      int64_t lo;
++      int64_t up;
++};
++
++/**
++ * struct inv_icm42600_timestamp_acc - accumulator for computing an estimation
++ * @val:      current estimation of the value, the mean of all values
++ * @idx:      current index of the next free place in values table
++ * @values:   table of all measured values, use for computing the mean
++ */
++struct inv_icm42600_timestamp_acc {
++      uint32_t val;
++      size_t idx;
++      uint32_t values[32];
++};
++
++/**
++ * struct inv_icm42600_timestamp - timestamp management states
++ * @it:                       interrupts interval timestamps
++ * @timestamp:                store last timestamp for computing next data timestamp
++ * @mult:             current internal period multiplier
++ * @new_mult:         new set internal period multiplier (not yet effective)
++ * @period:           measured current period of the sensor
++ * @chip_period:      accumulator for computing internal chip period
++ */
++struct inv_icm42600_timestamp {
++      struct inv_icm42600_timestamp_interval it;
++      int64_t timestamp;
++      uint32_t mult;
++      uint32_t new_mult;
++      uint32_t period;
++      struct inv_icm42600_timestamp_acc chip_period;
++};
++
++void inv_icm42600_timestamp_init(struct inv_icm42600_timestamp *ts,
++                               uint32_t period);
++
++int inv_icm42600_timestamp_update_odr(struct inv_icm42600_timestamp *ts,
++                                    uint32_t period, bool fifo);
++
++void inv_icm42600_timestamp_interrupt(struct inv_icm42600_timestamp *ts,
++                                    uint32_t fifo_period, size_t fifo_nb,
++                                    size_t sensor_nb, int64_t timestamp);
++
++static inline int64_t
++inv_icm42600_timestamp_pop(struct inv_icm42600_timestamp *ts)
++{
++      ts->timestamp += ts->period;
++      return ts->timestamp;
++}
++
++void inv_icm42600_timestamp_apply_odr(struct inv_icm42600_timestamp *ts,
++                                    uint32_t fifo_period, size_t fifo_nb,
++                                    unsigned int fifo_no);
++
++static inline void
++inv_icm42600_timestamp_reset(struct inv_icm42600_timestamp *ts)
++{
++      const struct inv_icm42600_timestamp_interval interval_init = {0LL, 0LL};
++
++      ts->it = interval_init;
++      ts->timestamp = 0;
++}
++
++#endif
diff --git a/queue-5.15/ksmbd-centralize-ksmbd_conn-final-release-to-plug-transport-leak.patch b/queue-5.15/ksmbd-centralize-ksmbd_conn-final-release-to-plug-transport-leak.patch
new file mode 100644 (file)
index 0000000..a56dad0
--- /dev/null
@@ -0,0 +1,256 @@
+From stable+bounces-276810-greg=kroah.com@vger.kernel.org Thu Jul 16 20:31:32 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 16 Jul 2026 14:22:04 -0400
+Subject: ksmbd: centralize ksmbd_conn final release to plug transport leak
+To: stable@vger.kernel.org
+Cc: DaeMyung Kang <charsyam@gmail.com>, Namjae Jeon <linkinjeon@kernel.org>, Steve French <stfrench@microsoft.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260716182205.921233-6-sashal@kernel.org>
+
+From: DaeMyung Kang <charsyam@gmail.com>
+
+[ Upstream commit b1f1e80620deb49daf63c2e677046599b693dc1f ]
+
+ksmbd_conn_free() is one of four sites that can observe the last
+refcount drop of a struct ksmbd_conn.  The other three
+
+    fs/smb/server/connection.c    ksmbd_conn_r_count_dec()
+    fs/smb/server/oplock.c        __free_opinfo()
+    fs/smb/server/vfs_cache.c     session_fd_check()
+
+end the conn with a bare kfree(), skipping
+ida_destroy(&conn->async_ida) and
+conn->transport->ops->free_transport(conn->transport).  Whenever one
+of them is the last putter, the embedded async_ida and the entire
+transport struct leak -- for TCP, that is also the struct socket and
+the kvec iov.
+
+__free_opinfo() being a final putter is not theoretical.  opinfo_put()
+queues the callback via call_rcu(&opinfo->rcu, free_opinfo_rcu), so
+ksmbd_server_terminate_conn() can deposit N opinfo releases in RCU and
+have ksmbd_conn_free() run in the handler thread before any of them
+fire.  ksmbd_conn_free() then observes refcnt > 0 and short-circuits;
+the last RCU-delivered __free_opinfo() falls onto its bare kfree(conn)
+branch and the transport is lost.
+
+A/B validation in a QEMU/virtme guest, mounting //127.0.0.1/testshare:
+each iteration holds 8 files open via sleep processes, force-closes
+TCP with "ss -K sport = :445", kills the holders, lazy-umounts;
+repeated 10 times, then ksmbd shutdown and kmemleak scan.
+
+    state         conn_alloc  conn_free  tcp_free  opi_rcu  kmemleak
+    ----------    ----------  ---------  --------  -------  --------
+    pre-patch         20          20        10       160        7
+    with patch        20          20        20       160        0
+
+Pre-patch conn_free=20 with tcp_free=10 directly demonstrates the
+bare-kfree paths skipping transport cleanup; kmemleak backtraces point
+into struct tcp_transport / iov.  With this patch tcp_free matches
+conn_free at 20/20 and kmemleak is clean.
+
+Move the per-struct final release into __ksmbd_conn_release_work() and
+route the three bare-kfree final-put sites through a new
+ksmbd_conn_put().  Those sites now pair ida_destroy() and
+free_transport() with kfree(conn) regardless of which holder happens
+to release the last reference.  stop_sessions() only triggers the
+transport shutdown and does not itself drop the last conn reference,
+so it is unaffected.
+
+The centralized release reaches sock_release() -> tcp_close() ->
+lock_sock_nested() (might_sleep) from every final putter, including
+__free_opinfo() invoked from an RCU softirq callback, which trips
+CONFIG_DEBUG_ATOMIC_SLEEP.  Defer the release to a dedicated
+ksmbd_conn_wq workqueue so ksmbd_conn_put() is safe from any
+non-sleeping context.
+
+Make ksmbd_file own a strong connection reference while fp->conn is
+non-NULL so durable-preserve and final-close paths cannot dereference
+a stale connection.  ksmbd_open_fd() and ksmbd_reopen_durable_fd()
+take the reference via ksmbd_conn_get() (the latter also reorders the
+fp->conn / fp->tcon assignments before __open_id() so the published fp
+is never observed with fp->conn == NULL); session_fd_check() and
+__ksmbd_close_fd() drop it via ksmbd_conn_put().  With that invariant,
+session_fd_check() can take a local conn pointer once and use it
+across the m_op_list and lock_list iterations even though op->conn
+puts may otherwise drop the last reference.
+
+At module exit the workqueue is flushed and destroyed after
+rcu_barrier(), so any release queued by a trailing RCU callback is
+drained before the inode hash and module text go away.
+
+Fixes: ee426bfb9d09 ("ksmbd: add refcnt to ksmbd_conn struct")
+Signed-off-by: DaeMyung Kang <charsyam@gmail.com>
+Acked-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Stable-dep-of: c1016dd1d8b2 ("ksmbd: track the connection owning a byte-range lock")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/ksmbd/connection.c |   49 +++++++++++++++++++++++++++++++++++++++++++++++--
+ fs/ksmbd/connection.h |    3 +++
+ fs/ksmbd/oplock.c     |    4 ++--
+ fs/ksmbd/vfs_cache.c  |   18 +++++++++++++++++-
+ 4 files changed, 69 insertions(+), 5 deletions(-)
+
+--- a/fs/ksmbd/connection.c
++++ b/fs/ksmbd/connection.c
+@@ -23,6 +23,43 @@ LIST_HEAD(conn_list);
+ DECLARE_RWSEM(conn_list_lock);
+ /**
++ * ksmbd_conn_get() - take a reference on @conn and return it.
++ *
++ * Returns @conn unchanged so callers can write
++ * "fp->conn = ksmbd_conn_get(work->conn);" in one expression.  Returns NULL
++ * if @conn is NULL.
++ */
++struct ksmbd_conn *ksmbd_conn_get(struct ksmbd_conn *conn)
++{
++      if (!conn)
++              return NULL;
++
++      atomic_inc(&conn->refcnt);
++      return conn;
++}
++
++/**
++ * ksmbd_conn_put() - drop a reference and, if it was the last, perform the
++ * final, once-per-struct release of a ksmbd_conn.
++ *
++ * The transport is not released here: free_transport() releases it after
++ * calling ksmbd_conn_free().  The final release therefore only pairs
++ * ida_destroy() with kfree(), reaches no sleeping teardown and is safe
++ * from any context, including the RCU callback that frees an opinfo
++ * holding a connection reference.
++ */
++void ksmbd_conn_put(struct ksmbd_conn *conn)
++{
++      if (!conn)
++              return;
++
++      if (atomic_dec_and_test(&conn->refcnt)) {
++              ida_destroy(&conn->async_ida);
++              kfree(conn);
++      }
++}
++
++/**
+  * ksmbd_conn_free() - free resources of the connection instance
+  *
+  * @conn:     connection instance to be cleand up
+@@ -36,12 +73,19 @@ void ksmbd_conn_free(struct ksmbd_conn *
+       list_del(&conn->conns_list);
+       up_write(&conn_list_lock);
++      /*
++       * request_buf / preauth_info / mechToken are only ever accessed by the
++       * connection handler thread that owns @conn.  ksmbd_conn_free() is
++       * called from the transport free_transport() path when that thread is
++       * exiting, so it is safe to release them unconditionally even when
++       * ksmbd_conn_put() below is not the final putter (oplock / ksmbd_file
++       * holders only retain the conn pointer, not these per-thread buffers).
++       */
+       xa_destroy(&conn->sessions);
+       kvfree(conn->request_buf);
+       kfree(conn->preauth_info);
+       kfree(conn->mechToken);
+-      ida_destroy(&conn->async_ida);
+-      kfree(conn);
++      ksmbd_conn_put(conn);
+ }
+ /**
+@@ -70,6 +114,7 @@ struct ksmbd_conn *ksmbd_conn_alloc(void
+               conn->um = NULL;
+       atomic_set(&conn->req_running, 0);
+       atomic_set(&conn->r_count, 0);
++      atomic_set(&conn->refcnt, 1);
+       conn->total_credits = 1;
+       conn->outstanding_credits = 0;
+--- a/fs/ksmbd/connection.h
++++ b/fs/ksmbd/connection.h
+@@ -106,6 +106,7 @@ struct ksmbd_conn {
+       bool                            signing_negotiated;
+       __le16                          signing_algorithm;
+       bool                            binding;
++      atomic_t                        refcnt;
+ };
+ struct ksmbd_conn_ops {
+@@ -148,6 +149,8 @@ bool ksmbd_conn_alive(struct ksmbd_conn
+ void ksmbd_conn_wait_idle(struct ksmbd_conn *conn, u64 sess_id);
+ struct ksmbd_conn *ksmbd_conn_alloc(void);
+ void ksmbd_conn_free(struct ksmbd_conn *conn);
++struct ksmbd_conn *ksmbd_conn_get(struct ksmbd_conn *conn);
++void ksmbd_conn_put(struct ksmbd_conn *conn);
+ bool ksmbd_conn_lookup_dialect(struct ksmbd_conn *c);
+ int ksmbd_conn_write(struct ksmbd_work *work);
+ int ksmbd_conn_rdma_read(struct ksmbd_conn *conn,
+--- a/fs/ksmbd/oplock.c
++++ b/fs/ksmbd/oplock.c
+@@ -30,7 +30,6 @@ static DEFINE_RWLOCK(lease_list_lock);
+ static struct oplock_info *alloc_opinfo(struct ksmbd_work *work,
+                                       u64 id, __u16 Tid)
+ {
+-      struct ksmbd_conn *conn = work->conn;
+       struct ksmbd_session *sess = work->sess;
+       struct oplock_info *opinfo;
+@@ -39,7 +38,7 @@ static struct oplock_info *alloc_opinfo(
+               return NULL;
+       opinfo->sess = sess;
+-      opinfo->conn = conn;
++      opinfo->conn = ksmbd_conn_get(work->conn);
+       opinfo->level = SMB2_OPLOCK_LEVEL_NONE;
+       opinfo->op_state = OPLOCK_STATE_NONE;
+       opinfo->pending_break = 0;
+@@ -124,6 +123,7 @@ static void free_opinfo(struct oplock_in
+ {
+       if (opinfo->is_lease)
+               free_lease(opinfo);
++      ksmbd_conn_put(opinfo->conn);
+       kfree(opinfo);
+ }
+--- a/fs/ksmbd/vfs_cache.c
++++ b/fs/ksmbd/vfs_cache.c
+@@ -328,6 +328,14 @@ static void __ksmbd_close_fd(struct ksmb
+               kfree(smb_lock);
+       }
++      /*
++       * Drop fp's strong reference on conn (taken in ksmbd_open_fd()).
++       */
++      if (fp->conn) {
++              ksmbd_conn_put(fp->conn);
++              fp->conn = NULL;
++      }
++
+       if (ksmbd_stream_fd(fp))
+               kfree(fp->stream.name);
+       kmem_cache_free(filp_cache, fp);
+@@ -579,7 +587,13 @@ struct ksmbd_file *ksmbd_open_fd(struct
+       atomic_set(&fp->refcount, 1);
+       fp->filp                = filp;
+-      fp->conn                = work->conn;
++      /*
++       * fp owns a strong reference on fp->conn for as long as fp->conn is
++       * non-NULL, so __ksmbd_close_fd() never dereferences a dangling
++       * pointer.  Paired with ksmbd_conn_put() in __ksmbd_close_fd()
++       * (final close) and on the error paths below.
++       */
++      fp->conn                = ksmbd_conn_get(work->conn);
+       fp->tcon                = work->tcon;
+       fp->volatile_id         = KSMBD_NO_FID;
+       fp->persistent_id       = KSMBD_NO_FID;
+@@ -601,6 +615,8 @@ struct ksmbd_file *ksmbd_open_fd(struct
+       return fp;
+ err_out:
++      /* fp->conn was set and refcounted before every branch here. */
++      ksmbd_conn_put(fp->conn);
+       kmem_cache_free(filp_cache, fp);
+       return ERR_PTR(ret);
+ }
diff --git a/queue-5.15/ksmbd-destroy-async_ida-in-ksmbd_conn_free.patch b/queue-5.15/ksmbd-destroy-async_ida-in-ksmbd_conn_free.patch
new file mode 100644 (file)
index 0000000..1483ac8
--- /dev/null
@@ -0,0 +1,46 @@
+From stable+bounces-276808-greg=kroah.com@vger.kernel.org Thu Jul 16 20:29:06 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 16 Jul 2026 14:22:03 -0400
+Subject: ksmbd: destroy async_ida in ksmbd_conn_free()
+To: stable@vger.kernel.org
+Cc: DaeMyung Kang <charsyam@gmail.com>, Namjae Jeon <linkinjeon@kernel.org>, Steve French <stfrench@microsoft.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260716182205.921233-5-sashal@kernel.org>
+
+From: DaeMyung Kang <charsyam@gmail.com>
+
+[ Upstream commit b32c8db48212a34998c36d0bbc05b29d5c407ef5 ]
+
+When per-connection async_ida was converted from a dynamically
+allocated ksmbd_ida to an embedded struct ida, ksmbd_ida_free() was
+removed from the connection teardown path but no matching
+ida_destroy() was added.  The connection is therefore freed with the
+IDA's backing xarray still intact.
+
+The kernel IDA API expects ida_init() and ida_destroy() to be paired
+over an object's lifetime, so add the missing cleanup before the
+connection is freed.
+
+No leak has been observed in testing; this is a pairing fix to match
+the IDA lifetime rules, not a response to a reproduced regression.
+
+Fixes: d40012a83f87 ("cifsd: declare ida statically")
+Signed-off-by: DaeMyung Kang <charsyam@gmail.com>
+Acked-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Stable-dep-of: c1016dd1d8b2 ("ksmbd: track the connection owning a byte-range lock")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/ksmbd/connection.c |    1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/fs/ksmbd/connection.c
++++ b/fs/ksmbd/connection.c
+@@ -40,6 +40,7 @@ void ksmbd_conn_free(struct ksmbd_conn *
+       kvfree(conn->request_buf);
+       kfree(conn->preauth_info);
+       kfree(conn->mechToken);
++      ida_destroy(&conn->async_ida);
+       kfree(conn);
+ }
diff --git a/queue-5.15/ksmbd-fix-mechtoken-leak-when-spnego-decode-fails-after-token-alloc.patch b/queue-5.15/ksmbd-fix-mechtoken-leak-when-spnego-decode-fails-after-token-alloc.patch
new file mode 100644 (file)
index 0000000..cd205ca
--- /dev/null
@@ -0,0 +1,79 @@
+From sashal@kernel.org Thu Jul 16 20:22:11 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 16 Jul 2026 14:22:02 -0400
+Subject: ksmbd: fix mechToken leak when SPNEGO decode fails after token alloc
+To: stable@vger.kernel.org
+Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>, Namjae Jeon <linkinjeon@kernel.org>, Steve French <smfrench@gmail.com>, Sergey Senozhatsky <senozhatsky@chromium.org>, Tom Talpey <tom@talpey.com>, linux-cifs@vger.kernel.org, stable@kernel.org, Steve French <stfrench@microsoft.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260716182205.921233-4-sashal@kernel.org>
+
+From: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+[ Upstream commit ad0057fb91218914d6c98268718ceb9d59b388e1 ]
+
+The kernel ASN.1 BER decoder calls action callbacks incrementally as it
+walks the input.  When ksmbd_decode_negTokenInit() reaches the mechToken
+[2] OCTET STRING element, ksmbd_neg_token_alloc() allocates
+conn->mechToken immediately via kmemdup_nul().  If a later element in
+the same blob is malformed, then the decoder will return nonzero after
+the allocation is already live.  This could happen if mechListMIC [3]
+overrunse the enclosing SEQUENCE.
+
+decode_negotiation_token() then sets conn->use_spnego = false because
+both the negTokenInit and negTokenTarg grammars failed.  The cleanup at
+the bottom of smb2_sess_setup() is gated on use_spnego:
+
+       if (conn->use_spnego && conn->mechToken) {
+               kfree(conn->mechToken);
+               conn->mechToken = NULL;
+       }
+
+so the kfree is skipped, causing the mechToken to never be freed.
+
+This codepath is reachable pre-authentication, so untrusted clients can
+cause slow memory leaks on a server without even being properly
+authenticated.
+
+Fix this up by not checking check for use_spnego, as it's not required,
+so the memory will always be properly freed.  At the same time, always
+free the memory in ksmbd_conn_free() incase some other failure path
+forgot to free it.
+
+Cc: Namjae Jeon <linkinjeon@kernel.org>
+Cc: Steve French <smfrench@gmail.com>
+Cc: Sergey Senozhatsky <senozhatsky@chromium.org>
+Cc: Tom Talpey <tom@talpey.com>
+Cc: linux-cifs@vger.kernel.org
+Cc: <stable@kernel.org>
+Assisted-by: gregkh_clanker_t1000
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Acked-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Stable-dep-of: c1016dd1d8b2 ("ksmbd: track the connection owning a byte-range lock")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/ksmbd/connection.c |    1 +
+ fs/ksmbd/smb2pdu.c    |    2 +-
+ 2 files changed, 2 insertions(+), 1 deletion(-)
+
+--- a/fs/ksmbd/connection.c
++++ b/fs/ksmbd/connection.c
+@@ -39,6 +39,7 @@ void ksmbd_conn_free(struct ksmbd_conn *
+       xa_destroy(&conn->sessions);
+       kvfree(conn->request_buf);
+       kfree(conn->preauth_info);
++      kfree(conn->mechToken);
+       kfree(conn);
+ }
+--- a/fs/ksmbd/smb2pdu.c
++++ b/fs/ksmbd/smb2pdu.c
+@@ -1920,7 +1920,7 @@ out_err:
+       else if (rc)
+               rsp->hdr.Status = STATUS_LOGON_FAILURE;
+-      if (conn->use_spnego && conn->mechToken) {
++      if (conn->mechToken) {
+               kfree(conn->mechToken);
+               conn->mechToken = NULL;
+       }
diff --git a/queue-5.15/ksmbd-fix-use-after-free-in-__ksmbd_close_fd-via-durable-scavenger.patch b/queue-5.15/ksmbd-fix-use-after-free-in-__ksmbd_close_fd-via-durable-scavenger.patch
new file mode 100644 (file)
index 0000000..07bd7f9
--- /dev/null
@@ -0,0 +1,67 @@
+From stable+bounces-276806-greg=kroah.com@vger.kernel.org Thu Jul 16 20:29:01 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 16 Jul 2026 14:22:01 -0400
+Subject: ksmbd: fix use-after-free in __ksmbd_close_fd() via durable scavenger
+To: stable@vger.kernel.org
+Cc: Namjae Jeon <linkinjeon@kernel.org>, munan Huang <munanevil@gmail.com>, ChenXiaoSong <chenxiaosong@kylinos.cn>, Steve French <stfrench@microsoft.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260716182205.921233-3-sashal@kernel.org>
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+[ Upstream commit 235e32320a470fcd3998fb3774f2290a0eb302a1 ]
+
+When a durable file handle survives session disconnect (TCP close without
+SMB2_LOGOFF), session_fd_check() sets fp->conn = NULL to preserve the
+handle for later reconnection. However, it did not clean up the byte-range
+locks on fp->lock_list.
+
+Later, when the durable scavenger thread times out and calls
+__ksmbd_close_fd(NULL, fp), the lock cleanup loop did:
+
+    spin_lock(&fp->conn->llist_lock);
+
+This caused a slab use-after-free because fp->conn was NULL and the
+original connection object had already been freed by
+ksmbd_tcp_disconnect().
+
+The root cause is asymmetric cleanup: lock entries (smb_lock->clist) were
+left dangling on the freed conn->lock_list while fp->conn was nulled out.
+
+To fix this issue properly, we need to handle the lifetime of
+smb_lock->clist across three paths:
+ - Safely skip clist deletion when list is empty and fp->conn is NULL.
+ - Remove the lock from the old connection's lock_list in
+   session_fd_check()
+ - Re-add the lock to the new connection's lock_list in
+   ksmbd_reopen_durable_fd().
+
+Fixes: c8efcc786146 ("ksmbd: add support for durable handles v1/v2")
+Co-developed-by: munan Huang <munanevil@gmail.com>
+Signed-off-by: munan Huang <munanevil@gmail.com>
+Reviewed-by: ChenXiaoSong <chenxiaosong@kylinos.cn>
+Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Stable-dep-of: c1016dd1d8b2 ("ksmbd: track the connection owning a byte-range lock")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/ksmbd/vfs_cache.c |    8 +++++---
+ 1 file changed, 5 insertions(+), 3 deletions(-)
+
+--- a/fs/ksmbd/vfs_cache.c
++++ b/fs/ksmbd/vfs_cache.c
+@@ -317,9 +317,11 @@ static void __ksmbd_close_fd(struct ksmb
+        * there are not accesses to fp->lock_list.
+        */
+       list_for_each_entry_safe(smb_lock, tmp_lock, &fp->lock_list, flist) {
+-              spin_lock(&fp->conn->llist_lock);
+-              list_del(&smb_lock->clist);
+-              spin_unlock(&fp->conn->llist_lock);
++              if (!list_empty(&smb_lock->clist) && fp->conn) {
++                      spin_lock(&fp->conn->llist_lock);
++                      list_del(&smb_lock->clist);
++                      spin_unlock(&fp->conn->llist_lock);
++              }
+               list_del(&smb_lock->flist);
+               locks_free_lock(smb_lock->fl);
diff --git a/queue-5.15/ksmbd-track-the-connection-owning-a-byte-range-lock.patch b/queue-5.15/ksmbd-track-the-connection-owning-a-byte-range-lock.patch
new file mode 100644 (file)
index 0000000..2026d56
--- /dev/null
@@ -0,0 +1,112 @@
+From stable+bounces-276811-greg=kroah.com@vger.kernel.org Thu Jul 16 20:39:31 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 16 Jul 2026 14:22:05 -0400
+Subject: ksmbd: track the connection owning a byte-range lock
+To: stable@vger.kernel.org
+Cc: Namjae Jeon <linkinjeon@kernel.org>, Musaab Khan <musaab.khan@protonmail.com>, Steve French <stfrench@microsoft.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260716182205.921233-7-sashal@kernel.org>
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+[ Upstream commit c1016dd1d8b2bcd1158bbaabe94a31bb7e7431fb ]
+
+SMB2_LOCK adds each granted byte-range lock to both the file lock list
+and the lock list of the connection which handled the request.  The
+final close and durable handle paths, however, remove the connection
+list entry while holding fp->conn->llist_lock.
+
+With SMB3 multichannel, the connection handling the LOCK request can be
+different from the connection which opened the file.  The entry can
+therefore be removed under a different spinlock from the one protecting
+the list it belongs to.  A concurrent traversal can then access freed
+struct ksmbd_lock and struct file_lock objects.
+
+Record the connection owning each lock's clist entry and hold a
+reference to it while the entry is linked.  Use that connection and its
+llist_lock for unlock, rollback, close, and durable preserve.  Durable
+reconnect assigns the new connection as the owner when publishing the
+locks again.
+
+Fixes: f5a544e3bab7 ("ksmbd: add support for SMB3 multichannel")
+Cc: stable@vger.kernel.org
+Reported-by: Musaab Khan <musaab.khan@protonmail.com>
+Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/ksmbd/smb2pdu.c   |   10 ++++++++--
+ fs/ksmbd/vfs_cache.c |   12 ++++++++----
+ fs/ksmbd/vfs_cache.h |    1 +
+ 3 files changed, 17 insertions(+), 6 deletions(-)
+
+--- a/fs/ksmbd/smb2pdu.c
++++ b/fs/ksmbd/smb2pdu.c
+@@ -7074,9 +7074,11 @@ int smb2_lock(struct ksmbd_work *work)
+                                               nolock = 0;
+                                               list_del(&cmp_lock->flist);
+                                               list_del(&cmp_lock->clist);
++                                              cmp_lock->conn = NULL;
+                                               spin_unlock(&conn->llist_lock);
+                                               up_read(&conn_list_lock);
++                                              ksmbd_conn_put(conn);
+                                               locks_free_lock(cmp_lock->fl);
+                                               kfree(cmp_lock);
+                                               goto out_check_cl;
+@@ -7212,6 +7214,7 @@ skip:
+                               goto retry;
+                       } else if (!rc) {
+                               list_add(&smb_lock->llist, &rollback_list);
++                              smb_lock->conn = ksmbd_conn_get(work->conn);
+                               spin_lock(&work->conn->llist_lock);
+                               list_add_tail(&smb_lock->clist,
+                                             &work->conn->lock_list);
+@@ -7259,11 +7262,14 @@ out:
+                       pr_err("rollback unlock fail : %d\n", rc);
+               list_del(&smb_lock->llist);
+-              spin_lock(&work->conn->llist_lock);
++              conn = smb_lock->conn;
++              spin_lock(&conn->llist_lock);
+               if (!list_empty(&smb_lock->flist))
+                       list_del(&smb_lock->flist);
+               list_del(&smb_lock->clist);
+-              spin_unlock(&work->conn->llist_lock);
++              smb_lock->conn = NULL;
++              spin_unlock(&conn->llist_lock);
++              ksmbd_conn_put(conn);
+               locks_free_lock(smb_lock->fl);
+               locks_free_lock(rlock);
+--- a/fs/ksmbd/vfs_cache.c
++++ b/fs/ksmbd/vfs_cache.c
+@@ -317,10 +317,14 @@ static void __ksmbd_close_fd(struct ksmb
+        * there are not accesses to fp->lock_list.
+        */
+       list_for_each_entry_safe(smb_lock, tmp_lock, &fp->lock_list, flist) {
+-              if (!list_empty(&smb_lock->clist) && fp->conn) {
+-                      spin_lock(&fp->conn->llist_lock);
+-                      list_del(&smb_lock->clist);
+-                      spin_unlock(&fp->conn->llist_lock);
++              struct ksmbd_conn *conn = smb_lock->conn;
++
++              if (conn) {
++                      spin_lock(&conn->llist_lock);
++                      list_del_init(&smb_lock->clist);
++                      smb_lock->conn = NULL;
++                      spin_unlock(&conn->llist_lock);
++                      ksmbd_conn_put(conn);
+               }
+               list_del(&smb_lock->flist);
+--- a/fs/ksmbd/vfs_cache.h
++++ b/fs/ksmbd/vfs_cache.h
+@@ -30,6 +30,7 @@ struct ksmbd_session;
+ struct ksmbd_lock {
+       struct file_lock *fl;
++      struct ksmbd_conn *conn;
+       struct list_head clist;
+       struct list_head flist;
+       struct list_head llist;
diff --git a/queue-5.15/leds-lm3601x-improve-error-reporting-for-problems-during-.remove.patch b/queue-5.15/leds-lm3601x-improve-error-reporting-for-problems-during-.remove.patch
new file mode 100644 (file)
index 0000000..4347cad
--- /dev/null
@@ -0,0 +1,54 @@
+From stable+bounces-277530-greg=kroah.com@vger.kernel.org Mon Jul 20 04:25:48 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 19 Jul 2026 22:25:35 -0400
+Subject: leds: lm3601x: Improve error reporting for problems during .remove()
+To: stable@vger.kernel.org
+Cc: "Uwe Kleine-König" <u.kleine-koenig@pengutronix.de>, "Pavel Machek" <pavel@ucw.cz>, "Wolfram Sang" <wsa@kernel.org>, "Sasha Levin" <sashal@kernel.org>
+Message-ID: <20260720022537.2927284-3-sashal@kernel.org>
+
+From: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
+
+[ Upstream commit 22a23436891886a66d21af3619f4a4e8809f0e0a ]
+
+Returning an error value in an i2c remove callback results in a generic
+error message being emitted by the i2c core, but otherwise it doesn't
+make a difference. The device goes away anyhow and the devm cleanups are
+called.
+
+So instead of triggering the generic i2c error message, emit a more
+helpful message if a problem occurs and return 0 to suppress the generic
+message.
+
+This patch is a preparation for making i2c remove callbacks return void.
+
+Acked-by: Pavel Machek <pavel@ucw.cz>
+Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
+Signed-off-by: Wolfram Sang <wsa@kernel.org>
+Stable-dep-of: 1f0bdc2884b6 ("usb: typec: ucsi: ccg: Fix use-after-free of ucsi on remove")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/leds/flash/leds-lm3601x.c |   11 ++++++++---
+ 1 file changed, 8 insertions(+), 3 deletions(-)
+
+--- a/drivers/leds/flash/leds-lm3601x.c
++++ b/drivers/leds/flash/leds-lm3601x.c
+@@ -443,10 +443,15 @@ static int lm3601x_probe(struct i2c_clie
+ static int lm3601x_remove(struct i2c_client *client)
+ {
+       struct lm3601x_led *led = i2c_get_clientdata(client);
++      int ret;
+-      return regmap_update_bits(led->regmap, LM3601X_ENABLE_REG,
+-                         LM3601X_ENABLE_MASK,
+-                         LM3601X_MODE_STANDBY);
++      ret = regmap_update_bits(led->regmap, LM3601X_ENABLE_REG,
++                               LM3601X_ENABLE_MASK, LM3601X_MODE_STANDBY);
++      if (ret)
++              dev_warn(&client->dev,
++                       "Failed to put into standby (%pe)\n", ERR_PTR(ret));
++
++      return 0;
+ }
+ static const struct i2c_device_id lm3601x_id[] = {
diff --git a/queue-5.15/leds-lm3697-remove-duplicated-error-reporting-in-.remove.patch b/queue-5.15/leds-lm3697-remove-duplicated-error-reporting-in-.remove.patch
new file mode 100644 (file)
index 0000000..33d0f76
--- /dev/null
@@ -0,0 +1,48 @@
+From stable+bounces-277529-greg=kroah.com@vger.kernel.org Mon Jul 20 04:25:47 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 19 Jul 2026 22:25:34 -0400
+Subject: leds: lm3697: Remove duplicated error reporting in .remove()
+To: stable@vger.kernel.org
+Cc: "Uwe Kleine-König" <u.kleine-koenig@pengutronix.de>, "Pavel Machek" <pavel@ucw.cz>, "Wolfram Sang" <wsa@kernel.org>, "Sasha Levin" <sashal@kernel.org>
+Message-ID: <20260720022537.2927284-2-sashal@kernel.org>
+
+From: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
+
+[ Upstream commit af89fa11fae1cee4e07453f780e6e5573ef0f477 ]
+
+Returning an error value from an i2c remove callback results in an error
+message being emitted by the i2c core, but otherwise it doesn't make a
+difference. The device goes away anyhow and the devm cleanups are
+called.
+
+As lm3697_remove() already emits an error message on failure and the
+additional error message by the i2c core doesn't add any useful
+information, don't pass the error value up the stack. Instead continue
+to clean up and return 0.
+
+This patch is a preparation for making i2c remove callbacks return void.
+
+Acked-by: Pavel Machek <pavel@ucw.cz>
+Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
+Signed-off-by: Wolfram Sang <wsa@kernel.org>
+Stable-dep-of: 1f0bdc2884b6 ("usb: typec: ucsi: ccg: Fix use-after-free of ucsi on remove")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/leds/leds-lm3697.c |    4 +---
+ 1 file changed, 1 insertion(+), 3 deletions(-)
+
+--- a/drivers/leds/leds-lm3697.c
++++ b/drivers/leds/leds-lm3697.c
+@@ -345,10 +345,8 @@ static int lm3697_remove(struct i2c_clie
+       ret = regmap_update_bits(led->regmap, LM3697_CTRL_ENABLE,
+                                LM3697_CTRL_A_B_EN, 0);
+-      if (ret) {
++      if (ret)
+               dev_err(dev, "Failed to disable the device\n");
+-              return ret;
+-      }
+       if (led->enable_gpio)
+               gpiod_direction_output(led->enable_gpio, 0);
diff --git a/queue-5.15/netfilter-ebtables-use-vmalloc_array-to-improve-code.patch b/queue-5.15/netfilter-ebtables-use-vmalloc_array-to-improve-code.patch
new file mode 100644 (file)
index 0000000..2c64bee
--- /dev/null
@@ -0,0 +1,77 @@
+From stable+bounces-275053-greg=kroah.com@vger.kernel.org Wed Jul 15 23:26:53 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 15 Jul 2026 17:26:45 -0400
+Subject: netfilter: ebtables: Use vmalloc_array() to improve code
+To: stable@vger.kernel.org
+Cc: Qianfeng Rong <rongqianfeng@vivo.com>, Florian Westphal <fw@strlen.de>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260715212646.278719-1-sashal@kernel.org>
+
+From: Qianfeng Rong <rongqianfeng@vivo.com>
+
+[ Upstream commit 46015e6b3ea75297b28d4806564f3f692cf11861 ]
+
+Remove array_size() calls and replace vmalloc() with vmalloc_array() to
+simplify the code.  vmalloc_array() is also optimized better, uses fewer
+instructions, and handles overflow more concisely[1].
+
+[1]: https://lore.kernel.org/lkml/abc66ec5-85a4-47e1-9759-2f60ab111971@vivo.com/
+Signed-off-by: Qianfeng Rong <rongqianfeng@vivo.com>
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Stable-dep-of: cbfe53599eeb ("netfilter: ebtables: zero chainstack array")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/bridge/netfilter/ebtables.c |   14 +++++++-------
+ 1 file changed, 7 insertions(+), 7 deletions(-)
+
+--- a/net/bridge/netfilter/ebtables.c
++++ b/net/bridge/netfilter/ebtables.c
+@@ -923,8 +923,8 @@ static int translate_table(struct net *n
+                * if an error occurs
+                */
+               newinfo->chainstack =
+-                      vmalloc(array_size(nr_cpu_ids,
+-                                         sizeof(*(newinfo->chainstack))));
++                      vmalloc_array(nr_cpu_ids,
++                                    sizeof(*(newinfo->chainstack)));
+               if (!newinfo->chainstack)
+                       return -ENOMEM;
+               for_each_possible_cpu(i) {
+@@ -941,7 +941,7 @@ static int translate_table(struct net *n
+                       }
+               }
+-              cl_s = vmalloc(array_size(udc_cnt, sizeof(*cl_s)));
++              cl_s = vmalloc_array(udc_cnt, sizeof(*cl_s));
+               if (!cl_s)
+                       return -ENOMEM;
+               i = 0; /* the i'th udc */
+@@ -1021,8 +1021,8 @@ static int do_replace_finish(struct net
+        * the check on the size is done later, when we have the lock
+        */
+       if (repl->num_counters) {
+-              unsigned long size = repl->num_counters * sizeof(*counterstmp);
+-              counterstmp = vmalloc(size);
++              counterstmp = vmalloc_array(repl->num_counters,
++                                          sizeof(*counterstmp));
+               if (!counterstmp)
+                       return -ENOMEM;
+       }
+@@ -1389,7 +1389,7 @@ static int do_update_counters(struct net
+       if (num_counters == 0)
+               return -EINVAL;
+-      tmp = vmalloc(array_size(num_counters, sizeof(*tmp)));
++      tmp = vmalloc_array(num_counters, sizeof(*tmp));
+       if (!tmp)
+               return -ENOMEM;
+@@ -1530,7 +1530,7 @@ static int copy_counters_to_user(struct
+       if (num_counters != nentries)
+               return -EINVAL;
+-      counterstmp = vmalloc(array_size(nentries, sizeof(*counterstmp)));
++      counterstmp = vmalloc_array(nentries, sizeof(*counterstmp));
+       if (!counterstmp)
+               return -ENOMEM;
diff --git a/queue-5.15/netfilter-ebtables-zero-chainstack-array.patch b/queue-5.15/netfilter-ebtables-zero-chainstack-array.patch
new file mode 100644 (file)
index 0000000..05eb469
--- /dev/null
@@ -0,0 +1,46 @@
+From stable+bounces-275054-greg=kroah.com@vger.kernel.org Wed Jul 15 23:26:54 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 15 Jul 2026 17:26:46 -0400
+Subject: netfilter: ebtables: zero chainstack array
+To: stable@vger.kernel.org
+Cc: Florian Westphal <fw@strlen.de>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260715212646.278719-2-sashal@kernel.org>
+
+From: Florian Westphal <fw@strlen.de>
+
+[ Upstream commit cbfe53599eebffd188938ab6774cc41794f6f9d5 ]
+
+sashiko reports:
+ looking at ebtables table
+ translation, could a sparse cpu_possible_mask lead to an uninitialized pointer
+ free?
+
+ If cpu_possible_mask is sparse (for example, CPU 0 and CPU 2 are possible,
+ but CPU 1 is not), the allocation loop skips CPU 1. If vmalloc_node() fails at
+ CPU 2, the cleanup loop will blindly decrement and call vfree() on
+ newinfo->chainstack[1].
+
+Not a real-world bug, such allocation isn't expected to fail
+in the first place.
+
+Cc: stable@vger.kernel.org
+Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
+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/bridge/netfilter/ebtables.c |    3 +--
+ 1 file changed, 1 insertion(+), 2 deletions(-)
+
+--- a/net/bridge/netfilter/ebtables.c
++++ b/net/bridge/netfilter/ebtables.c
+@@ -923,8 +923,7 @@ static int translate_table(struct net *n
+                * if an error occurs
+                */
+               newinfo->chainstack =
+-                      vmalloc_array(nr_cpu_ids,
+-                                    sizeof(*(newinfo->chainstack)));
++                      vcalloc(nr_cpu_ids, sizeof(*(newinfo->chainstack)));
+               if (!newinfo->chainstack)
+                       return -ENOMEM;
+               for_each_possible_cpu(i) {
diff --git a/queue-5.15/pci-add-kerneldoc-for-pci_resize_resource.patch b/queue-5.15/pci-add-kerneldoc-for-pci_resize_resource.patch
new file mode 100644 (file)
index 0000000..0bbdc10
--- /dev/null
@@ -0,0 +1,55 @@
+From stable+bounces-274894-greg=kroah.com@vger.kernel.org Wed Jul 15 13:16:31 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 15 Jul 2026 07:16:16 -0400
+Subject: PCI: Add kerneldoc for pci_resize_resource()
+To: stable@vger.kernel.org
+Cc: "Ilpo Järvinen" <ilpo.jarvinen@linux.intel.com>, "Bjorn Helgaas" <bhelgaas@google.com>, "Alex Bennée" <alex.bennee@linaro.org>, "Sasha Levin" <sashal@kernel.org>
+Message-ID: <20260715111618.647249-4-sashal@kernel.org>
+
+From: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+
+[ Upstream commit d787018e2dfdc4c1331538e7a8717690d1b7c9b3 ]
+
+As pci_resize_resource() is meant to be used also outside of PCI core,
+document the interface with kerneldoc.
+
+Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
+Tested-by: Alex Bennée <alex.bennee@linaro.org> # AVA, AMD GPU
+Link: https://patch.msgid.link/20251113162628.5946-8-ilpo.jarvinen@linux.intel.com
+Stable-dep-of: ee7471fe968d ("PCI: Skip Resizable BAR restore on read error")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/pci/setup-res.c |   20 ++++++++++++++++++++
+ 1 file changed, 20 insertions(+)
+
+--- a/drivers/pci/setup-res.c
++++ b/drivers/pci/setup-res.c
+@@ -422,6 +422,26 @@ void pci_release_resource(struct pci_dev
+ }
+ EXPORT_SYMBOL(pci_release_resource);
++/**
++ * pci_resize_resource - reconfigure a Resizable BAR and resources
++ * @dev: the PCI device
++ * @resno: index of the BAR to be resized
++ * @size: new size as defined in the spec (0=1MB, 31=128TB)
++ * @exclude_bars: a mask of BARs that should not be released
++ *
++ * Reconfigure @resno to @size and re-run resource assignment algorithm
++ * with the new size.
++ *
++ * Prior to resize, release @dev resources that share a bridge window with
++ * @resno.  This unpins the bridge window resource to allow changing it.
++ *
++ * The caller may prevent releasing a particular BAR by providing
++ * @exclude_bars mask, but this may result in the resize operation failing
++ * due to insufficient space.
++ *
++ * Return: 0 on success, or negative on error. In case of an error, the
++ *         resources are restored to their original places.
++ */
+ int pci_resize_resource(struct pci_dev *dev, int resno, int size,
+                       int exclude_bars)
+ {
diff --git a/queue-5.15/pci-altera-fix-resource-leaks-on-probe-failure.patch b/queue-5.15/pci-altera-fix-resource-leaks-on-probe-failure.patch
new file mode 100644 (file)
index 0000000..e394f25
--- /dev/null
@@ -0,0 +1,99 @@
+From stable+bounces-274467-greg=kroah.com@vger.kernel.org Tue Jul 14 18:55:56 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 14 Jul 2026 12:55:48 -0400
+Subject: PCI: altera: Fix resource leaks on probe failure
+To: stable@vger.kernel.org
+Cc: Mahesh Vaidya <mahesh.vaidya@altera.com>, Manivannan Sadhasivam <mani@kernel.org>, "Subhransu S. Prusty" <subhransu.sekhar.prusty@altera.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260714165548.2885119-1-sashal@kernel.org>
+
+From: Mahesh Vaidya <mahesh.vaidya@altera.com>
+
+[ Upstream commit 7a94138caeb27f3c49c1dbd93bf422098925bb28 ]
+
+The chained IRQ handler is set during probe, but is only removed during the
+driver remove(). If pci_host_probe() fails, the handler and INTx IRQ
+domain remain set even though the devm-managed host bridge storage
+containing struct altera_pcie will be released, leaving the handler with
+a stale data pointer.
+
+Interrupts are also enabled before pci_host_probe() is called. If probe
+fails after that point, the controller interrupt source should be disabled
+before the chained handler and INTx domain are removed.
+
+So set the chained handler only after the INTx domain has been created.
+Disable controller interrupts during IRQ teardown, and tear the IRQ setup
+down if pci_host_probe() fails.
+
+Fixes: c63aed7334c2 ("PCI: altera: Use pci_host_probe() to register host")
+Signed-off-by: Mahesh Vaidya <mahesh.vaidya@altera.com>
+[mani: commit log]
+Signed-off-by: Manivannan Sadhasivam <mani@kernel.org>
+Reviewed-by: Subhransu S. Prusty <subhransu.sekhar.prusty@altera.com>
+Cc: stable@vger.kernel.org
+Link: https://patch.msgid.link/20260430204330.3121003-3-mahesh.vaidya@altera.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/pci/controller/pcie-altera.c |   28 ++++++++++++++++++++++++++--
+ 1 file changed, 26 insertions(+), 2 deletions(-)
+
+--- a/drivers/pci/controller/pcie-altera.c
++++ b/drivers/pci/controller/pcie-altera.c
+@@ -683,8 +683,18 @@ static int altera_pcie_init_irq_domain(s
+       return 0;
+ }
++static void altera_pcie_disable_irq(struct altera_pcie *pcie)
++{
++      if (pcie->pcie_data->version == ALTERA_PCIE_V1 ||
++          pcie->pcie_data->version == ALTERA_PCIE_V2) {
++              /* Disable all P2A interrupts */
++              cra_writel(pcie, 0, P2A_INT_ENABLE);
++      }
++}
++
+ static void altera_pcie_irq_teardown(struct altera_pcie *pcie)
+ {
++      altera_pcie_disable_irq(pcie);
+       irq_set_chained_handler_and_data(pcie->irq, NULL, NULL);
+       irq_domain_remove(pcie->irq_domain);
+ }
+@@ -709,7 +719,6 @@ static int altera_pcie_parse_dt(struct a
+       if (pcie->irq < 0)
+               return pcie->irq;
+-      irq_set_chained_handler_and_data(pcie->irq, altera_pcie_isr, pcie);
+       return 0;
+ }
+@@ -794,6 +803,12 @@ static int altera_pcie_probe(struct plat
+               return ret;
+       }
++      /*
++       * The chained handler uses pcie->irq_domain, so set it only after the
++       * INTx domain has been created.
++       */
++      irq_set_chained_handler_and_data(pcie->irq, altera_pcie_isr, pcie);
++
+       /* clear all interrupts */
+       cra_writel(pcie, P2A_INT_STS_ALL, P2A_INT_STATUS);
+       /* enable all interrupts */
+@@ -804,7 +819,16 @@ static int altera_pcie_probe(struct plat
+       bridge->busnr = pcie->root_bus_nr;
+       bridge->ops = &altera_pcie_ops;
+-      return pci_host_probe(bridge);
++      ret = pci_host_probe(bridge);
++      if (ret)
++              goto err_teardown_irq;
++
++      return 0;
++
++err_teardown_irq:
++      altera_pcie_irq_teardown(pcie);
++
++      return ret;
+ }
+ static int altera_pcie_remove(struct platform_device *pdev)
diff --git a/queue-5.15/pci-controller-use-dev_fwnode-instead-of-of_fwnode_handle.patch b/queue-5.15/pci-controller-use-dev_fwnode-instead-of-of_fwnode_handle.patch
new file mode 100644 (file)
index 0000000..08aa9ac
--- /dev/null
@@ -0,0 +1,142 @@
+From stable+bounces-274634-greg=kroah.com@vger.kernel.org Wed Jul 15 02:17:59 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 14 Jul 2026 20:17:47 -0400
+Subject: PCI: controller: Use dev_fwnode() instead of of_fwnode_handle()
+To: stable@vger.kernel.org
+Cc: "Jiri Slaby (SUSE)" <jirislaby@kernel.org>, Arnd Bergmann <arnd@arndb.de>, Bjorn Helgaas <bhelgaas@google.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260715001749.3783652-1-sashal@kernel.org>
+
+From: "Jiri Slaby (SUSE)" <jirislaby@kernel.org>
+
+[ Upstream commit a103d2dede5683dabbac2c3374bc24b6a9434478 ]
+
+All irq_domain functions now accept fwnode instead of of_node. But many
+PCI controllers still extract dev to of_node and then of_node to fwnode.
+
+Instead, clean this up and simply use the dev_fwnode() helper to extract
+fwnode directly from dev. Internally, it still does dev => of_node =>
+fwnode steps, but it's now hidden from the users.
+
+In the case of altera, this also removes an unused 'node' variable that is
+only used when CONFIG_OF is enabled:
+
+  drivers/pci/controller/pcie-altera.c: In function 'altera_pcie_init_irq_domain':
+  drivers/pci/controller/pcie-altera.c:855:29: error: unused variable 'node' [-Werror=unused-variable]
+    855 |         struct device_node *node = dev->of_node;
+
+Signed-off-by: Jiri Slaby (SUSE) <jirislaby@kernel.org>
+Signed-off-by: Arnd Bergmann <arnd@arndb.de>   # altera
+[bhelgaas: squash together, rebase to precede msi-parent]
+Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
+Link: https://patch.msgid.link/20250521163329.2137973-1-arnd@kernel.org
+Link: https://patch.msgid.link/20250611104348.192092-16-jirislaby@kernel.org
+Link: https://patch.msgid.link/20250723065907.1841758-1-jirislaby@kernel.org
+Stable-dep-of: f865a57896bd ("PCI: mediatek: Fix IRQ domain leak when port fails to enable")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/pci/controller/dwc/pcie-designware-host.c    |    2 +-
+ drivers/pci/controller/mobiveil/pcie-mobiveil-host.c |    8 +++-----
+ drivers/pci/controller/pcie-altera-msi.c             |    2 +-
+ drivers/pci/controller/pcie-altera.c                 |    3 +--
+ drivers/pci/controller/pcie-mediatek-gen3.c          |    2 +-
+ drivers/pci/controller/pcie-mediatek.c               |    2 +-
+ drivers/pci/controller/pcie-xilinx-nwl.c             |    2 +-
+ 7 files changed, 9 insertions(+), 12 deletions(-)
+
+--- a/drivers/pci/controller/dwc/pcie-designware-host.c
++++ b/drivers/pci/controller/dwc/pcie-designware-host.c
+@@ -232,7 +232,7 @@ static const struct irq_domain_ops dw_pc
+ int dw_pcie_allocate_domains(struct pcie_port *pp)
+ {
+       struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
+-      struct fwnode_handle *fwnode = of_node_to_fwnode(pci->dev->of_node);
++      struct fwnode_handle *fwnode = dev_fwnode(pci->dev);
+       pp->irq_domain = irq_domain_create_linear(fwnode, pp->num_vectors,
+                                              &dw_pcie_msi_domain_ops, pp);
+--- a/drivers/pci/controller/mobiveil/pcie-mobiveil-host.c
++++ b/drivers/pci/controller/mobiveil/pcie-mobiveil-host.c
+@@ -445,7 +445,7 @@ static const struct irq_domain_ops msi_d
+ static int mobiveil_allocate_msi_domains(struct mobiveil_pcie *pcie)
+ {
+       struct device *dev = &pcie->pdev->dev;
+-      struct fwnode_handle *fwnode = of_node_to_fwnode(dev->of_node);
++      struct fwnode_handle *fwnode = dev_fwnode(dev);
+       struct mobiveil_msi *msi = &pcie->rp.msi;
+       mutex_init(&msi->lock);
+@@ -471,13 +471,11 @@ static int mobiveil_allocate_msi_domains
+ static int mobiveil_pcie_init_irq_domain(struct mobiveil_pcie *pcie)
+ {
+       struct device *dev = &pcie->pdev->dev;
+-      struct device_node *node = dev->of_node;
+       struct mobiveil_root_port *rp = &pcie->rp;
+       /* setup INTx */
+-      rp->intx_domain = irq_domain_add_linear(node, PCI_NUM_INTX,
+-                                              &intx_domain_ops, pcie);
+-
++      rp->intx_domain = irq_domain_create_linear(dev_fwnode(dev), PCI_NUM_INTX, &intx_domain_ops,
++                                                 pcie);
+       if (!rp->intx_domain) {
+               dev_err(dev, "Failed to get a INTx IRQ domain\n");
+               return -ENOMEM;
+--- a/drivers/pci/controller/pcie-altera-msi.c
++++ b/drivers/pci/controller/pcie-altera-msi.c
+@@ -171,7 +171,7 @@ static const struct irq_domain_ops msi_d
+ static int altera_allocate_domains(struct altera_msi *msi)
+ {
+-      struct fwnode_handle *fwnode = of_node_to_fwnode(msi->pdev->dev.of_node);
++      struct fwnode_handle *fwnode = dev_fwnode(&msi->pdev->dev);
+       msi->inner_domain = irq_domain_add_linear(NULL, msi->num_of_vectors,
+                                            &msi_domain_ops, msi);
+--- a/drivers/pci/controller/pcie-altera.c
++++ b/drivers/pci/controller/pcie-altera.c
+@@ -670,10 +670,9 @@ static void altera_pcie_isr(struct irq_d
+ static int altera_pcie_init_irq_domain(struct altera_pcie *pcie)
+ {
+       struct device *dev = &pcie->pdev->dev;
+-      struct device_node *node = dev->of_node;
+       /* Setup INTx */
+-      pcie->irq_domain = irq_domain_add_linear(node, PCI_NUM_INTX,
++      pcie->irq_domain = irq_domain_create_linear(dev_fwnode(dev), PCI_NUM_INTX,
+                                       &intx_domain_ops, pcie);
+       if (!pcie->irq_domain) {
+               dev_err(dev, "Failed to get a INTx IRQ domain\n");
+--- a/drivers/pci/controller/pcie-mediatek-gen3.c
++++ b/drivers/pci/controller/pcie-mediatek-gen3.c
+@@ -607,7 +607,7 @@ static int mtk_pcie_init_irq_domains(str
+       /* Setup MSI */
+       mutex_init(&port->lock);
+-      port->msi_bottom_domain = irq_domain_add_linear(node, PCIE_MSI_IRQS_NUM,
++      port->msi_bottom_domain = irq_domain_create_linear(dev_fwnode(dev), PCIE_MSI_IRQS_NUM,
+                                 &mtk_msi_bottom_domain_ops, port);
+       if (!port->msi_bottom_domain) {
+               dev_err(dev, "failed to create MSI bottom domain\n");
+--- a/drivers/pci/controller/pcie-mediatek.c
++++ b/drivers/pci/controller/pcie-mediatek.c
+@@ -503,7 +503,7 @@ static struct msi_domain_info mtk_msi_do
+ static int mtk_pcie_allocate_msi_domains(struct mtk_pcie_port *port)
+ {
+-      struct fwnode_handle *fwnode = of_node_to_fwnode(port->pcie->dev->of_node);
++      struct fwnode_handle *fwnode = dev_fwnode(port->pcie->dev);
+       mutex_init(&port->lock);
+--- a/drivers/pci/controller/pcie-xilinx-nwl.c
++++ b/drivers/pci/controller/pcie-xilinx-nwl.c
+@@ -505,7 +505,7 @@ static int nwl_pcie_init_msi_irq_domain(
+ {
+ #ifdef CONFIG_PCI_MSI
+       struct device *dev = pcie->dev;
+-      struct fwnode_handle *fwnode = of_node_to_fwnode(dev->of_node);
++      struct fwnode_handle *fwnode = dev_fwnode(dev);
+       struct nwl_msi *msi = &pcie->msi;
+       msi->dev_domain = irq_domain_add_linear(NULL, INT_PCI_MSI_NR,
diff --git a/queue-5.15/pci-fix-restoring-bars-on-bar-resize-rollback-path.patch b/queue-5.15/pci-fix-restoring-bars-on-bar-resize-rollback-path.patch
new file mode 100644 (file)
index 0000000..9fa2c06
--- /dev/null
@@ -0,0 +1,336 @@
+From stable+bounces-274893-greg=kroah.com@vger.kernel.org Wed Jul 15 13:16:28 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 15 Jul 2026 07:16:15 -0400
+Subject: PCI: Fix restoring BARs on BAR resize rollback path
+To: stable@vger.kernel.org
+Cc: "Ilpo Järvinen" <ilpo.jarvinen@linux.intel.com>, "Simon Richter" <Simon.Richter@hogyros.de>, "Alex Bennée" <alex.bennee@linaro.org>, "Bjorn Helgaas" <bhelgaas@google.com>, "Christian König" <christian.koenig@amd.com>, "Sasha Levin" <sashal@kernel.org>
+Message-ID: <20260715111618.647249-3-sashal@kernel.org>
+
+From: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+
+[ Upstream commit 337b1b566db087347194e4543ddfdfa5645275cc ]
+
+BAR resize operation is implemented in the pci_resize_resource() and
+pbus_reassign_bridge_resources() functions. pci_resize_resource() can be
+called either from __resource_resize_store() from sysfs or directly by the
+driver for the Endpoint Device.
+
+The pci_resize_resource() requires that caller has released the device
+resources that share the bridge window with the BAR to be resized as
+otherwise the bridge window is pinned in place and cannot be changed.
+
+pbus_reassign_bridge_resources() rolls back resources if the resize
+operation fails, but rollback is performed only for the bridge windows.
+Because releasing the device resources are done by the caller of the BAR
+resize interface, these functions performing the BAR resize do not have
+access to the device resources as they were before the resize.
+
+pbus_reassign_bridge_resources() could try __pci_bridge_assign_resources()
+after rolling back the bridge windows as they were, however, it will not
+guarantee the resource are assigned due to differences in how FW and the
+kernel assign the resources (alignment of the start address and tail).
+
+To perform rollback robustly, the BAR resize interface has to be altered to
+also release the device resources that share the bridge window with the BAR
+to be resized.
+
+Also, remove restoring from the entries failed list as saved list should
+now contain both the bridge windows and device resources so the extra
+restore is duplicated work.
+
+Some drivers (currently only amdgpu) want to prevent releasing some
+resources. Add exclude_bars param to pci_resize_resource() and make amdgpu
+pass its register BAR (BAR 2 or 5), which should never be released during
+resize operation. Normally 64-bit prefetchable resources do not share a
+bridge window with the 32-bit only register BAR, but there are various
+fallbacks in the resource assignment logic which may make the resources
+share the bridge window in rare cases.
+
+This change (together with the driver side changes) is to counter the
+resource releases that had to be done to prevent resource tree corruption
+in the ("PCI: Release assigned resource before restoring them") change. As
+such, it likely restores functionality in cases where device resources were
+released to avoid resource tree conflicts which appeared to be "working"
+when such conflicts were not correctly detected by the kernel.
+
+Reported-by: Simon Richter <Simon.Richter@hogyros.de>
+Link: https://lore.kernel.org/linux-pci/f9a8c975-f5d3-4dd2-988e-4371a1433a60@hogyros.de/
+Reported-by: Alex Bennée <alex.bennee@linaro.org>
+Link: https://lore.kernel.org/linux-pci/874irqop6b.fsf@draig.linaro.org/
+Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+[bhelgaas: squash amdgpu BAR selection from
+https: //lore.kernel.org/r/20251114103053.13778-1-ilpo.jarvinen@linux.intel.com]
+Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
+Tested-by: Alex Bennée <alex.bennee@linaro.org> # AVA, AMD GPU
+Reviewed-by: Christian König <christian.koenig@amd.com>
+Link: https://patch.msgid.link/20251113162628.5946-7-ilpo.jarvinen@linux.intel.com
+Stable-dep-of: ee7471fe968d ("PCI: Skip Resizable BAR restore on read error")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/gpu/drm/amd/amdgpu/amdgpu_device.c |    4 -
+ drivers/pci/pci.h                          |    3 
+ drivers/pci/setup-bus.c                    |   98 +++++++++++++++++++----------
+ drivers/pci/setup-res.c                    |   19 +----
+ include/linux/pci.h                        |    4 -
+ 5 files changed, 80 insertions(+), 48 deletions(-)
+
+--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
++++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
+@@ -1249,7 +1249,9 @@ int amdgpu_device_resize_fb_bar(struct a
+       pci_release_resource(adev->pdev, 0);
+-      r = pci_resize_resource(adev->pdev, 0, rbar_size);
++      r = pci_resize_resource(adev->pdev, 0, rbar_size,
++                              (adev->asic_type >= CHIP_BONAIRE) ? 1 << 5
++                                                                : 1 << 2);
+       if (r == -ENOSPC)
+               DRM_INFO("Not enough PCI address space for a large BAR.");
+       else if (r && r != -ENOTSUPP)
+--- a/drivers/pci/pci.h
++++ b/drivers/pci/pci.h
+@@ -287,6 +287,9 @@ enum pci_bar_type {
+ struct device *pci_get_host_bridge_device(struct pci_dev *dev);
+ void pci_put_host_bridge_device(struct device *dev);
++int pci_do_resource_release_and_resize(struct pci_dev *dev, int resno, int size,
++                                     int exclude_bars);
++
+ int pci_configure_extended_tags(struct pci_dev *dev, void *ign);
+ bool pci_bus_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *pl,
+                               int crs_timeout);
+--- a/drivers/pci/setup-bus.c
++++ b/drivers/pci/setup-bus.c
+@@ -2166,18 +2166,16 @@ enable_all:
+ }
+ EXPORT_SYMBOL_GPL(pci_assign_unassigned_bridge_resources);
+-int pci_reassign_bridge_resources(struct pci_dev *bridge, unsigned long type)
++static int pci_reassign_bridge_resources(struct pci_dev *bridge, unsigned long type,
++                                       struct list_head *saved)
+ {
+       struct pci_dev_resource *dev_res;
+       struct pci_dev *next;
+-      LIST_HEAD(saved);
+       LIST_HEAD(added);
+       LIST_HEAD(failed);
+       unsigned int i;
+       int ret;
+-      down_read(&pci_bus_sem);
+-
+       /* Walk to the root hub, releasing bridge BARs when possible */
+       next = bridge;
+       do {
+@@ -2193,9 +2191,9 @@ int pci_reassign_bridge_resources(struct
+                       if (res->child)
+                               continue;
+-                      ret = add_to_list(&saved, bridge, res, 0, 0);
++                      ret = add_to_list(saved, bridge, res, 0, 0);
+                       if (ret)
+-                              goto cleanup;
++                              return ret;
+                       pci_info(bridge, "BAR %d: releasing %pR\n",
+                                i, res);
+@@ -2212,67 +2210,105 @@ int pci_reassign_bridge_resources(struct
+               next = bridge->bus ? bridge->bus->self : NULL;
+       } while (next);
+-      if (list_empty(&saved)) {
+-              up_read(&pci_bus_sem);
++      if (list_empty(saved))
+               return -ENOENT;
+-      }
+       __pci_bus_size_bridges(bridge->subordinate, &added);
+       __pci_bridge_assign_resources(bridge, &added, &failed);
+       BUG_ON(!list_empty(&added));
+       if (!list_empty(&failed)) {
+-              ret = -ENOSPC;
+-              goto cleanup;
++              free_list(&failed);
++              return -ENOSPC;
+       }
+-      list_for_each_entry(dev_res, &saved, list) {
++      list_for_each_entry(dev_res, saved, list) {
+               /* Skip the bridge we just assigned resources for */
+               if (bridge == dev_res->dev)
+                       continue;
++              if (!dev_res->dev->subordinate)
++                      continue;
++
+               bridge = dev_res->dev;
+               pci_setup_bridge(bridge->subordinate);
+       }
+-      free_list(&saved);
+-      up_read(&pci_bus_sem);
+       return 0;
++}
+-cleanup:
+-      /* Restore size and flags */
+-      list_for_each_entry(dev_res, &failed, list) {
+-              struct resource *res = dev_res->res;
++int pci_do_resource_release_and_resize(struct pci_dev *pdev, int resno, int size,
++                                     int exclude_bars)
++{
++      struct resource *res = pdev->resource + resno;
++      unsigned long flags = res->flags;
++      struct pci_dev_resource *dev_res;
++      struct pci_bus *bus = pdev->bus;
++      struct resource *r;
++      LIST_HEAD(saved);
++      unsigned int i;
++      int ret = 0;
+-              res->start = dev_res->start;
+-              res->end = dev_res->end;
+-              res->flags = dev_res->flags;
++      down_read(&pci_bus_sem);
++
++      for (i = 0; i < PCI_BRIDGE_RESOURCES; i++) {
++              r = pdev->resource + i;
++
++              if (exclude_bars & BIT(i))
++                      continue;
++
++              if (!pci_resource_len(pdev, i) || r->flags != flags)
++                      continue;
++
++              ret = add_to_list(&saved, pdev, r, 0, 0);
++              if (ret)
++                      goto restore;
++              pci_release_resource(pdev, i);
+       }
+-      free_list(&failed);
++      res->end = res->start + pci_rebar_size_to_bytes(size) - 1;
++
++      if (!bus->self)
++              goto out;
++
++      ret = pci_reassign_bridge_resources(bus->self, res->flags, &saved);
++      if (ret)
++              goto restore;
++
++out:
++      up_read(&pci_bus_sem);
++      free_list(&saved);
++      return ret;
++
++restore:
+       /* Revert to the old configuration */
+       list_for_each_entry(dev_res, &saved, list) {
+               struct resource *res = dev_res->res;
++              struct pci_dev *dev = dev_res->dev;
+-              bridge = dev_res->dev;
+-              i = res - bridge->resource;
++              i = res - dev->resource;
+               if (res->parent) {
+                       release_child_resources(res);
+-                      pci_release_resource(bridge, i);
++                      pci_release_resource(dev, i);
+               }
+               res->start = dev_res->start;
+               res->end = dev_res->end;
+               res->flags = dev_res->flags;
+-              pci_claim_resource(bridge, i);
+-              pci_setup_bridge(bridge->subordinate);
+-      }
+-      up_read(&pci_bus_sem);
+-      free_list(&saved);
++              if (pci_claim_resource(dev, i))
++                      continue;
+-      return ret;
++              if (i < PCI_BRIDGE_RESOURCES) {
++                      pci_update_resource(dev, i);
++                      pci_info(dev, "BAR %d %pR: old value restored\n",
++                               i, res);
++              }
++              if (dev->subordinate)
++                      pci_setup_bridge(dev->subordinate);
++      }
++      goto out;
+ }
+ void pci_assign_unassigned_bus_resources(struct pci_bus *bus)
+--- a/drivers/pci/setup-res.c
++++ b/drivers/pci/setup-res.c
+@@ -422,9 +422,9 @@ void pci_release_resource(struct pci_dev
+ }
+ EXPORT_SYMBOL(pci_release_resource);
+-int pci_resize_resource(struct pci_dev *dev, int resno, int size)
++int pci_resize_resource(struct pci_dev *dev, int resno, int size,
++                      int exclude_bars)
+ {
+-      struct resource *res = dev->resource + resno;
+       struct pci_host_bridge *host;
+       int old, ret;
+       u32 sizes;
+@@ -435,10 +435,6 @@ int pci_resize_resource(struct pci_dev *
+       if (host->preserve_config)
+               return -ENOTSUPP;
+-      /* Make sure the resource isn't assigned before resizing it. */
+-      if (!(res->flags & IORESOURCE_UNSET))
+-              return -EBUSY;
+-
+       pci_read_config_word(dev, PCI_COMMAND, &cmd);
+       if (cmd & PCI_COMMAND_MEMORY)
+               return -EBUSY;
+@@ -458,19 +454,14 @@ int pci_resize_resource(struct pci_dev *
+       if (ret)
+               return ret;
+-      res->end = res->start + pci_rebar_size_to_bytes(size) - 1;
++      ret = pci_do_resource_release_and_resize(dev, resno, size, exclude_bars);
++      if (ret)
++              goto error_resize;
+-      /* Check if the new config works by trying to assign everything. */
+-      if (dev->bus->self) {
+-              ret = pci_reassign_bridge_resources(dev->bus->self, res->flags);
+-              if (ret)
+-                      goto error_resize;
+-      }
+       return 0;
+ error_resize:
+       pci_rebar_set_size(dev, resno, old);
+-      res->end = res->start + pci_rebar_size_to_bytes(old) - 1;
+       return ret;
+ }
+ EXPORT_SYMBOL(pci_resize_resource);
+--- a/include/linux/pci.h
++++ b/include/linux/pci.h
+@@ -1317,7 +1317,8 @@ static inline int pci_rebar_bytes_to_siz
+ }
+ u32 pci_rebar_get_possible_sizes(struct pci_dev *pdev, int bar);
+-int __must_check pci_resize_resource(struct pci_dev *dev, int i, int size);
++int __must_check pci_resize_resource(struct pci_dev *dev, int i, int size,
++                                   int exclude_bars);
+ int pci_select_bars(struct pci_dev *dev, unsigned long flags);
+ bool pci_device_is_present(struct pci_dev *pdev);
+ void pci_ignore_hotplug(struct pci_dev *dev);
+@@ -1384,7 +1385,6 @@ void pci_assign_unassigned_resources(voi
+ void pci_assign_unassigned_bridge_resources(struct pci_dev *bridge);
+ void pci_assign_unassigned_bus_resources(struct pci_bus *bus);
+ void pci_assign_unassigned_root_bus_resources(struct pci_bus *bus);
+-int pci_reassign_bridge_resources(struct pci_dev *bridge, unsigned long type);
+ void pdev_enable_device(struct pci_dev *);
+ int pci_enable_resources(struct pci_dev *, int mask);
+ void pci_assign_irq(struct pci_dev *dev);
diff --git a/queue-5.15/pci-free-saved-list-without-holding-pci_bus_sem.patch b/queue-5.15/pci-free-saved-list-without-holding-pci_bus_sem.patch
new file mode 100644 (file)
index 0000000..700a5fb
--- /dev/null
@@ -0,0 +1,38 @@
+From stable+bounces-274892-greg=kroah.com@vger.kernel.org Wed Jul 15 13:16:27 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 15 Jul 2026 07:16:14 -0400
+Subject: PCI: Free saved list without holding pci_bus_sem
+To: stable@vger.kernel.org
+Cc: "Ilpo Järvinen" <ilpo.jarvinen@linux.intel.com>, "Bjorn Helgaas" <bhelgaas@google.com>, "Alex Bennée" <alex.bennee@linaro.org>, "Sasha Levin" <sashal@kernel.org>
+Message-ID: <20260715111618.647249-2-sashal@kernel.org>
+
+From: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+
+[ Upstream commit 1d8a0506f69895b7cfd9d5c4546761c508231a8a ]
+
+Freeing the saved list does not require holding pci_bus_sem, so the
+critical section can be made shorter.
+
+Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
+Tested-by: Alex Bennée <alex.bennee@linaro.org> # AVA, AMD GPU
+Link: https://patch.msgid.link/20251113162628.5946-6-ilpo.jarvinen@linux.intel.com
+Stable-dep-of: ee7471fe968d ("PCI: Skip Resizable BAR restore on read error")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/pci/setup-bus.c |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/pci/setup-bus.c
++++ b/drivers/pci/setup-bus.c
+@@ -2269,8 +2269,8 @@ cleanup:
+               pci_claim_resource(bridge, i);
+               pci_setup_bridge(bridge->subordinate);
+       }
+-      free_list(&saved);
+       up_read(&pci_bus_sem);
++      free_list(&saved);
+       return ret;
+ }
diff --git a/queue-5.15/pci-imx6-fix-imx6sx_gpr12_pcie_test_powerdown-handling.patch b/queue-5.15/pci-imx6-fix-imx6sx_gpr12_pcie_test_powerdown-handling.patch
new file mode 100644 (file)
index 0000000..74e6202
--- /dev/null
@@ -0,0 +1,55 @@
+From stable+bounces-274537-greg=kroah.com@vger.kernel.org Tue Jul 14 22:03:14 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 14 Jul 2026 16:02:20 -0400
+Subject: PCI: imx6: Fix IMX6SX_GPR12_PCIE_TEST_POWERDOWN handling
+To: stable@vger.kernel.org
+Cc: Richard Zhu <hongxing.zhu@nxp.com>, Manivannan Sadhasivam <mani@kernel.org>, Bjorn Helgaas <bhelgaas@google.com>, Frank Li <Frank.Li@nxp.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260714200220.3152632-1-sashal@kernel.org>
+
+From: Richard Zhu <hongxing.zhu@nxp.com>
+
+[ Upstream commit aad953fb4eed0df5486cd54ccad80ac197678e01 ]
+
+The IMX6SX_GPR12_PCIE_TEST_POWERDOWN bit does not control the PCIe
+reference clock on i.MX6SX. Instead, it is part of i.MX6SX PCIe core
+reset sequence.
+
+Move the IMX6SX_GPR12_PCIE_TEST_POWERDOWN assertion/deassertion into
+the core reset functions to properly reflect its purpose. Remove the
+.enable_ref_clk() callback for i.MX6SX since it was incorrectly
+manipulating this bit.
+
+Fixes: e3c06cd063d6 ("PCI: imx6: Add initial imx6sx support")
+Signed-off-by: Richard Zhu <hongxing.zhu@nxp.com>
+Signed-off-by: Manivannan Sadhasivam <mani@kernel.org>
+Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
+Reviewed-by: Frank Li <Frank.Li@nxp.com>
+Cc: stable@vger.kernel.org
+Link: https://patch.msgid.link/20260319090844.444987-1-hongxing.zhu@nxp.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/pci/controller/dwc/pci-imx6.c |    5 ++---
+ 1 file changed, 2 insertions(+), 3 deletions(-)
+
+--- a/drivers/pci/controller/dwc/pci-imx6.c
++++ b/drivers/pci/controller/dwc/pci-imx6.c
+@@ -430,9 +430,6 @@ static int imx6_pcie_enable_ref_clk(stru
+                       dev_err(dev, "unable to enable pcie_axi clock\n");
+                       break;
+               }
+-
+-              regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
+-                                 IMX6SX_GPR12_PCIE_TEST_POWERDOWN, 0);
+               break;
+       case IMX6QP:
+       case IMX6Q:
+@@ -559,6 +556,8 @@ static void imx6_pcie_deassert_core_rese
+               imx7d_pcie_wait_for_phy_pll_lock(imx6_pcie);
+               break;
+       case IMX6SX:
++              regmap_clear_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
++                                IMX6SX_GPR12_PCIE_TEST_POWERDOWN);
+               regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR5,
+                                  IMX6SX_GPR5_PCIE_BTNRST_RESET, 0);
+               break;
diff --git a/queue-5.15/pci-mediatek-convert-bool-to-single-quirks-entry-and-bitmap.patch b/queue-5.15/pci-mediatek-convert-bool-to-single-quirks-entry-and-bitmap.patch
new file mode 100644 (file)
index 0000000..a33a531
--- /dev/null
@@ -0,0 +1,127 @@
+From stable+bounces-274635-greg=kroah.com@vger.kernel.org Wed Jul 15 02:18:21 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 14 Jul 2026 20:17:48 -0400
+Subject: PCI: mediatek: Convert bool to single quirks entry and bitmap
+To: stable@vger.kernel.org
+Cc: Christian Marangi <ansuelsmth@gmail.com>, Manivannan Sadhasivam <mani@kernel.org>, AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260715001749.3783652-2-sashal@kernel.org>
+
+From: Christian Marangi <ansuelsmth@gmail.com>
+
+[ Upstream commit 04305367fab7ec9c98eeba315ad09c8b20abce93 ]
+
+To clean Mediatek SoC PCIe struct, convert all the bool to a bitmap and
+use a single quirks to reference all the values. This permits cleaner
+addition of new quirk without having to define a new bool in the struct.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Signed-off-by: Manivannan Sadhasivam <mani@kernel.org>
+Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+Link: https://patch.msgid.link/20251020111121.31779-4-ansuelsmth@gmail.com
+Stable-dep-of: f865a57896bd ("PCI: mediatek: Fix IRQ domain leak when port fails to enable")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/pci/controller/pcie-mediatek.c |   33 ++++++++++++++++++++-------------
+ 1 file changed, 20 insertions(+), 13 deletions(-)
+
+--- a/drivers/pci/controller/pcie-mediatek.c
++++ b/drivers/pci/controller/pcie-mediatek.c
+@@ -143,23 +143,31 @@
+ struct mtk_pcie_port;
+ /**
++ * enum mtk_pcie_quirks - MTK PCIe quirks
++ * @MTK_PCIE_FIX_CLASS_ID: host's class ID needed to be fixed
++ * @MTK_PCIE_FIX_DEVICE_ID: host's device ID needed to be fixed
++ * @MTK_PCIE_NO_MSI: Bridge has no MSI support, and relies on an external block
++ */
++enum mtk_pcie_quirks {
++      MTK_PCIE_FIX_CLASS_ID = BIT(0),
++      MTK_PCIE_FIX_DEVICE_ID = BIT(1),
++      MTK_PCIE_NO_MSI = BIT(2),
++};
++
++/**
+  * struct mtk_pcie_soc - differentiate between host generations
+- * @need_fix_class_id: whether this host's class ID needed to be fixed or not
+- * @need_fix_device_id: whether this host's device ID needed to be fixed or not
+- * @no_msi: Bridge has no MSI support, and relies on an external block
+  * @device_id: device ID which this host need to be fixed
+  * @ops: pointer to configuration access functions
+  * @startup: pointer to controller setting functions
+  * @setup_irq: pointer to initialize IRQ functions
++ * @quirks: PCIe device quirks.
+  */
+ struct mtk_pcie_soc {
+-      bool need_fix_class_id;
+-      bool need_fix_device_id;
+-      bool no_msi;
+       unsigned int device_id;
+       struct pci_ops *ops;
+       int (*startup)(struct mtk_pcie_port *port);
+       int (*setup_irq)(struct mtk_pcie_port *port, struct device_node *node);
++      enum mtk_pcie_quirks quirks;
+ };
+ /**
+@@ -720,7 +728,7 @@ static int mtk_pcie_startup_port_v2(stru
+       writel(val, port->base + PCIE_RST_CTRL);
+       /* Set up vendor ID and class code */
+-      if (soc->need_fix_class_id) {
++      if (soc->quirks & MTK_PCIE_FIX_CLASS_ID) {
+               val = PCI_VENDOR_ID_MEDIATEK;
+               writew(val, port->base + PCIE_CONF_VEND_ID);
+@@ -728,7 +736,7 @@ static int mtk_pcie_startup_port_v2(stru
+               writew(val, port->base + PCIE_CONF_CLASS_ID);
+       }
+-      if (soc->need_fix_device_id)
++      if (soc->quirks & MTK_PCIE_FIX_DEVICE_ID)
+               writew(soc->device_id, port->base + PCIE_CONF_DEVICE_ID);
+       /* 100ms timeout value should be enough for Gen1/2 training */
+@@ -1129,7 +1137,7 @@ static int mtk_pcie_probe(struct platfor
+       host->ops = pcie->soc->ops;
+       host->sysdata = pcie;
+-      host->msi_domain = pcie->soc->no_msi;
++      host->msi_domain = !!(pcie->soc->quirks & MTK_PCIE_NO_MSI);
+       err = pci_host_probe(host);
+       if (err)
+@@ -1219,9 +1227,9 @@ static const struct dev_pm_ops mtk_pcie_
+ };
+ static const struct mtk_pcie_soc mtk_pcie_soc_v1 = {
+-      .no_msi = true,
+       .ops = &mtk_pcie_ops,
+       .startup = mtk_pcie_startup_port,
++      .quirks = MTK_PCIE_NO_MSI,
+ };
+ static const struct mtk_pcie_soc mtk_pcie_soc_mt2712 = {
+@@ -1231,19 +1239,18 @@ static const struct mtk_pcie_soc mtk_pci
+ };
+ static const struct mtk_pcie_soc mtk_pcie_soc_mt7622 = {
+-      .need_fix_class_id = true,
+       .ops = &mtk_pcie_ops_v2,
+       .startup = mtk_pcie_startup_port_v2,
+       .setup_irq = mtk_pcie_setup_irq,
++      .quirks = MTK_PCIE_FIX_CLASS_ID,
+ };
+ static const struct mtk_pcie_soc mtk_pcie_soc_mt7629 = {
+-      .need_fix_class_id = true,
+-      .need_fix_device_id = true,
+       .device_id = PCI_DEVICE_ID_MEDIATEK_7629,
+       .ops = &mtk_pcie_ops_v2,
+       .startup = mtk_pcie_startup_port_v2,
+       .setup_irq = mtk_pcie_setup_irq,
++      .quirks = MTK_PCIE_FIX_CLASS_ID | MTK_PCIE_FIX_DEVICE_ID,
+ };
+ static const struct of_device_id mtk_pcie_ids[] = {
diff --git a/queue-5.15/pci-mediatek-fix-irq-domain-leak-when-port-fails-to-enable.patch b/queue-5.15/pci-mediatek-fix-irq-domain-leak-when-port-fails-to-enable.patch
new file mode 100644 (file)
index 0000000..11c3d74
--- /dev/null
@@ -0,0 +1,170 @@
+From stable+bounces-274636-greg=kroah.com@vger.kernel.org Wed Jul 15 02:18:25 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 14 Jul 2026 20:17:49 -0400
+Subject: PCI: mediatek: Fix IRQ domain leak when port fails to enable
+To: stable@vger.kernel.org
+Cc: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>, Manivannan Sadhasivam <mani@kernel.org>, Caleb James DeLisle <cjd@cjdns.fr>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260715001749.3783652-3-sashal@kernel.org>
+
+From: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>
+
+[ Upstream commit f865a57896bd92d7662eb2818d8f48872e2cbbc7 ]
+
+When mtk_pcie_enable_port() fails, mtk_pcie_port_free() removes the port
+from pcie->ports and frees the port structure. However, the IRQ domains set
+up earlier by mtk_pcie_init_irq_domain() are never freed.
+
+Fix this by refactoring mtk_pcie_irq_teardown() into a per-port helper,
+mtk_pcie_irq_teardown_port(), and calling it from mtk_pcie_setup() when
+mtk_pcie_enable_port() fails. Since the IRQ teardown must only happen in
+the probe error path (during resume, child devices may have active MSI
+mappings and the NOIRQ context prohibits sleeping locks),
+mtk_pcie_enable_port() is changed to return an error code so callers can
+distinguish the two paths and act accordingly.
+
+This issue was reported by Sashiko while reviewing the EcoNet EN7528 SoC
+support series.
+
+Fixes: b099631df160 ("PCI: mediatek: Add controller support for MT2712 and MT7622")
+Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>
+Signed-off-by: Manivannan Sadhasivam <mani@kernel.org>
+Cc: stable@vger.kernel.org # 5.10
+Cc: Caleb James DeLisle <cjd@cjdns.fr>
+Link: https://patch.msgid.link/20260521174617.17692-1-mani@kernel.org
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/pci/controller/pcie-mediatek.c |   67 ++++++++++++++++++++-------------
+ 1 file changed, 42 insertions(+), 25 deletions(-)
+
+--- a/drivers/pci/controller/pcie-mediatek.c
++++ b/drivers/pci/controller/pcie-mediatek.c
+@@ -547,25 +547,29 @@ static void mtk_pcie_enable_msi(struct m
+       writel(val, port->base + PCIE_INT_MASK);
+ }
+-static void mtk_pcie_irq_teardown(struct mtk_pcie *pcie)
++static void mtk_pcie_irq_teardown_port(struct mtk_pcie_port *port)
+ {
+-      struct mtk_pcie_port *port, *tmp;
++      irq_set_chained_handler_and_data(port->irq, NULL, NULL);
+-      list_for_each_entry_safe(port, tmp, &pcie->ports, list) {
+-              irq_set_chained_handler_and_data(port->irq, NULL, NULL);
++      if (port->irq_domain)
++              irq_domain_remove(port->irq_domain);
+-              if (port->irq_domain)
+-                      irq_domain_remove(port->irq_domain);
++      if (IS_ENABLED(CONFIG_PCI_MSI)) {
++              if (port->msi_domain)
++                      irq_domain_remove(port->msi_domain);
++              if (port->inner_domain)
++                      irq_domain_remove(port->inner_domain);
++      }
+-              if (IS_ENABLED(CONFIG_PCI_MSI)) {
+-                      if (port->msi_domain)
+-                              irq_domain_remove(port->msi_domain);
+-                      if (port->inner_domain)
+-                              irq_domain_remove(port->inner_domain);
+-              }
++      irq_dispose_mapping(port->irq);
++}
+-              irq_dispose_mapping(port->irq);
+-      }
++static void mtk_pcie_irq_teardown(struct mtk_pcie *pcie)
++{
++      struct mtk_pcie_port *port, *tmp;
++
++      list_for_each_entry_safe(port, tmp, &pcie->ports, list)
++              mtk_pcie_irq_teardown_port(port);
+ }
+ static int mtk_pcie_intx_map(struct irq_domain *domain, unsigned int irq,
+@@ -846,7 +850,7 @@ static int mtk_pcie_startup_port(struct
+       return 0;
+ }
+-static void mtk_pcie_enable_port(struct mtk_pcie_port *port)
++static int mtk_pcie_enable_port(struct mtk_pcie_port *port)
+ {
+       struct mtk_pcie *pcie = port->pcie;
+       struct device *dev = pcie->dev;
+@@ -855,7 +859,7 @@ static void mtk_pcie_enable_port(struct
+       err = clk_prepare_enable(port->sys_ck);
+       if (err) {
+               dev_err(dev, "failed to enable sys_ck%d clock\n", port->slot);
+-              goto err_sys_clk;
++              return err;
+       }
+       err = clk_prepare_enable(port->ahb_ck);
+@@ -903,11 +907,15 @@ static void mtk_pcie_enable_port(struct
+               goto err_phy_on;
+       }
+-      if (!pcie->soc->startup(port))
+-              return;
++      err = pcie->soc->startup(port);
++      if (err) {
++              dev_info(dev, "Port%d link down\n", port->slot);
++              goto err_soc_startup;
++      }
+-      dev_info(dev, "Port%d link down\n", port->slot);
++      return 0;
++err_soc_startup:
+       phy_power_off(port->phy);
+ err_phy_on:
+       phy_exit(port->phy);
+@@ -923,8 +931,8 @@ err_aux_clk:
+       clk_disable_unprepare(port->ahb_ck);
+ err_ahb_clk:
+       clk_disable_unprepare(port->sys_ck);
+-err_sys_clk:
+-      mtk_pcie_port_free(port);
++
++      return err;
+ }
+ static int mtk_pcie_parse_port(struct mtk_pcie *pcie,
+@@ -1100,8 +1108,13 @@ static int mtk_pcie_setup(struct mtk_pci
+               return err;
+       /* enable each port, and then check link status */
+-      list_for_each_entry_safe(port, tmp, &pcie->ports, list)
+-              mtk_pcie_enable_port(port);
++      list_for_each_entry_safe(port, tmp, &pcie->ports, list) {
++              err = mtk_pcie_enable_port(port);
++              if (err) {
++                      mtk_pcie_irq_teardown_port(port);
++                      mtk_pcie_port_free(port);
++              }
++      }
+       /* power down PCIe subsys if slots are all empty (link down) */
+       if (list_empty(&pcie->ports))
+@@ -1205,14 +1218,18 @@ static int __maybe_unused mtk_pcie_resum
+ {
+       struct mtk_pcie *pcie = dev_get_drvdata(dev);
+       struct mtk_pcie_port *port, *tmp;
++      int err;
+       if (list_empty(&pcie->ports))
+               return 0;
+       clk_prepare_enable(pcie->free_ck);
+-      list_for_each_entry_safe(port, tmp, &pcie->ports, list)
+-              mtk_pcie_enable_port(port);
++      list_for_each_entry_safe(port, tmp, &pcie->ports, list) {
++              err = mtk_pcie_enable_port(port);
++              if (err)
++                      mtk_pcie_port_free(port);
++      }
+       /* In case of EP was removed while system suspend. */
+       if (list_empty(&pcie->ports))
diff --git a/queue-5.15/pci-move-resizable-bar-code-to-rebar.c.patch b/queue-5.15/pci-move-resizable-bar-code-to-rebar.c.patch
new file mode 100644 (file)
index 0000000..e47ffba
--- /dev/null
@@ -0,0 +1,510 @@
+From stable+bounces-274896-greg=kroah.com@vger.kernel.org Wed Jul 15 13:16:44 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 15 Jul 2026 07:16:17 -0400
+Subject: PCI: Move Resizable BAR code to rebar.c
+To: stable@vger.kernel.org
+Cc: "Ilpo Järvinen" <ilpo.jarvinen@linux.intel.com>, "Bjorn Helgaas" <bhelgaas@google.com>, "Christian König" <christian.koenig@amd.com>, "Sasha Levin" <sashal@kernel.org>
+Message-ID: <20260715111618.647249-5-sashal@kernel.org>
+
+From: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+
+[ Upstream commit 9f71938cd77f32a448f40a288e409eca60e55486 ]
+
+For lack of a better place to put it, Resizable BAR code has been placed
+inside pci.c and setup-res.c that do not use it for anything.  Upcoming
+changes are going to add more Resizable BAR related functions, increasing
+the code size.
+
+As pci.c is huge as is, move the Resizable BAR related code and the BAR
+resize code from setup-res.c to rebar.c.
+
+Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
+Reviewed-by: Christian König <christian.koenig@amd.com>
+Link: https://patch.msgid.link/20251113180053.27944-2-ilpo.jarvinen@linux.intel.com
+Stable-dep-of: ee7471fe968d ("PCI: Skip Resizable BAR restore on read error")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ Documentation/driver-api/pci/pci.rst |    3 
+ drivers/pci/Makefile                 |    2 
+ drivers/pci/pci.c                    |  136 ----------------------
+ drivers/pci/pci.h                    |    1 
+ drivers/pci/rebar.c                  |  212 +++++++++++++++++++++++++++++++++++
+ drivers/pci/setup-res.c              |   64 ----------
+ 6 files changed, 217 insertions(+), 201 deletions(-)
+ create mode 100644 drivers/pci/rebar.c
+
+--- a/Documentation/driver-api/pci/pci.rst
++++ b/Documentation/driver-api/pci/pci.rst
+@@ -31,6 +31,9 @@ PCI Support Library
+ .. kernel-doc:: drivers/pci/slot.c
+    :export:
++.. kernel-doc:: drivers/pci/rebar.c
++   :export:
++
+ .. kernel-doc:: drivers/pci/rom.c
+    :export:
+--- a/drivers/pci/Makefile
++++ b/drivers/pci/Makefile
+@@ -4,7 +4,7 @@
+ obj-$(CONFIG_PCI)             += access.o bus.o probe.o host-bridge.o \
+                                  remove.o pci.o pci-driver.o search.o \
+-                                 pci-sysfs.o rom.o setup-res.o irq.o vpd.o \
++                                 pci-sysfs.o rebar.o rom.o setup-res.o irq.o vpd.o \
+                                  setup-bus.o vc.o mmap.o setup-irq.o msi.o
+ obj-$(CONFIG_PCI)             += pcie/
+--- a/drivers/pci/pci.c
++++ b/drivers/pci/pci.c
+@@ -1663,33 +1663,6 @@ static void pci_restore_config_space(str
+       }
+ }
+-static void pci_restore_rebar_state(struct pci_dev *pdev)
+-{
+-      unsigned int pos, nbars, i;
+-      u32 ctrl;
+-
+-      pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_REBAR);
+-      if (!pos)
+-              return;
+-
+-      pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
+-      nbars = (ctrl & PCI_REBAR_CTRL_NBAR_MASK) >>
+-                  PCI_REBAR_CTRL_NBAR_SHIFT;
+-
+-      for (i = 0; i < nbars; i++, pos += 8) {
+-              struct resource *res;
+-              int bar_idx, size;
+-
+-              pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
+-              bar_idx = ctrl & PCI_REBAR_CTRL_BAR_IDX;
+-              res = pdev->resource + bar_idx;
+-              size = pci_rebar_bytes_to_size(resource_size(res));
+-              ctrl &= ~PCI_REBAR_CTRL_BAR_SIZE;
+-              ctrl |= size << PCI_REBAR_CTRL_BAR_SHIFT;
+-              pci_write_config_dword(pdev, pos + PCI_REBAR_CTRL, ctrl);
+-      }
+-}
+-
+ /**
+  * pci_restore_state - Restore the saved state of a PCI device
+  * @dev: PCI device that we're dealing with
+@@ -3619,115 +3592,6 @@ void pci_acs_init(struct pci_dev *dev)
+ }
+ /**
+- * pci_rebar_find_pos - find position of resize ctrl reg for BAR
+- * @pdev: PCI device
+- * @bar: BAR to find
+- *
+- * Helper to find the position of the ctrl register for a BAR.
+- * Returns -ENOTSUPP if resizable BARs are not supported at all.
+- * Returns -ENOENT if no ctrl register for the BAR could be found.
+- */
+-static int pci_rebar_find_pos(struct pci_dev *pdev, int bar)
+-{
+-      unsigned int pos, nbars, i;
+-      u32 ctrl;
+-
+-      pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_REBAR);
+-      if (!pos)
+-              return -ENOTSUPP;
+-
+-      pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
+-      nbars = (ctrl & PCI_REBAR_CTRL_NBAR_MASK) >>
+-                  PCI_REBAR_CTRL_NBAR_SHIFT;
+-
+-      for (i = 0; i < nbars; i++, pos += 8) {
+-              int bar_idx;
+-
+-              pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
+-              bar_idx = ctrl & PCI_REBAR_CTRL_BAR_IDX;
+-              if (bar_idx == bar)
+-                      return pos;
+-      }
+-
+-      return -ENOENT;
+-}
+-
+-/**
+- * pci_rebar_get_possible_sizes - get possible sizes for BAR
+- * @pdev: PCI device
+- * @bar: BAR to query
+- *
+- * Get the possible sizes of a resizable BAR as bitmask defined in the spec
+- * (bit 0=1MB, bit 19=512GB). Returns 0 if BAR isn't resizable.
+- */
+-u32 pci_rebar_get_possible_sizes(struct pci_dev *pdev, int bar)
+-{
+-      int pos;
+-      u32 cap;
+-
+-      pos = pci_rebar_find_pos(pdev, bar);
+-      if (pos < 0)
+-              return 0;
+-
+-      pci_read_config_dword(pdev, pos + PCI_REBAR_CAP, &cap);
+-      cap = FIELD_GET(PCI_REBAR_CAP_SIZES, cap);
+-
+-      /* Sapphire RX 5600 XT Pulse has an invalid cap dword for BAR 0 */
+-      if (pdev->vendor == PCI_VENDOR_ID_ATI && pdev->device == 0x731f &&
+-          bar == 0 && cap == 0x700)
+-              return 0x3f00;
+-
+-      return cap;
+-}
+-EXPORT_SYMBOL(pci_rebar_get_possible_sizes);
+-
+-/**
+- * pci_rebar_get_current_size - get the current size of a BAR
+- * @pdev: PCI device
+- * @bar: BAR to set size to
+- *
+- * Read the size of a BAR from the resizable BAR config.
+- * Returns size if found or negative error code.
+- */
+-int pci_rebar_get_current_size(struct pci_dev *pdev, int bar)
+-{
+-      int pos;
+-      u32 ctrl;
+-
+-      pos = pci_rebar_find_pos(pdev, bar);
+-      if (pos < 0)
+-              return pos;
+-
+-      pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
+-      return (ctrl & PCI_REBAR_CTRL_BAR_SIZE) >> PCI_REBAR_CTRL_BAR_SHIFT;
+-}
+-
+-/**
+- * pci_rebar_set_size - set a new size for a BAR
+- * @pdev: PCI device
+- * @bar: BAR to set size to
+- * @size: new size as defined in the spec (0=1MB, 19=512GB)
+- *
+- * Set the new size of a BAR as defined in the spec.
+- * Returns zero if resizing was successful, error code otherwise.
+- */
+-int pci_rebar_set_size(struct pci_dev *pdev, int bar, int size)
+-{
+-      int pos;
+-      u32 ctrl;
+-
+-      pos = pci_rebar_find_pos(pdev, bar);
+-      if (pos < 0)
+-              return pos;
+-
+-      pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
+-      ctrl &= ~PCI_REBAR_CTRL_BAR_SIZE;
+-      ctrl |= size << PCI_REBAR_CTRL_BAR_SHIFT;
+-      pci_write_config_dword(pdev, pos + PCI_REBAR_CTRL, ctrl);
+-      return 0;
+-}
+-
+-/**
+  * pci_enable_atomic_ops_to_root - enable AtomicOp requests to root port
+  * @dev: the PCI device
+  * @cap_mask: mask of desired AtomicOp sizes, including one or more of:
+--- a/drivers/pci/pci.h
++++ b/drivers/pci/pci.h
+@@ -657,6 +657,7 @@ static inline int acpi_get_rc_resources(
+ }
+ #endif
++void pci_restore_rebar_state(struct pci_dev *pdev);
+ int pci_rebar_get_current_size(struct pci_dev *pdev, int bar);
+ int pci_rebar_set_size(struct pci_dev *pdev, int bar, int size);
+ static inline u64 pci_rebar_size_to_bytes(int size)
+--- /dev/null
++++ b/drivers/pci/rebar.c
+@@ -0,0 +1,212 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * PCI Resizable BAR Extended Capability handling.
++ */
++
++#include <linux/bitfield.h>
++#include <linux/errno.h>
++#include <linux/export.h>
++#include <linux/ioport.h>
++#include <linux/pci.h>
++#include <linux/types.h>
++
++#include "pci.h"
++
++/**
++ * pci_rebar_find_pos - find position of resize ctrl reg for BAR
++ * @pdev: PCI device
++ * @bar: BAR to find
++ *
++ * Helper to find the position of the ctrl register for a BAR.
++ * Returns -ENOTSUPP if resizable BARs are not supported at all.
++ * Returns -ENOENT if no ctrl register for the BAR could be found.
++ */
++static int pci_rebar_find_pos(struct pci_dev *pdev, int bar)
++{
++      unsigned int pos, nbars, i;
++      u32 ctrl;
++
++      pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_REBAR);
++      if (!pos)
++              return -ENOTSUPP;
++
++      pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
++      nbars = (ctrl & PCI_REBAR_CTRL_NBAR_MASK) >>
++                  PCI_REBAR_CTRL_NBAR_SHIFT;
++
++      for (i = 0; i < nbars; i++, pos += 8) {
++              int bar_idx;
++
++              pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
++              bar_idx = ctrl & PCI_REBAR_CTRL_BAR_IDX;
++              if (bar_idx == bar)
++                      return pos;
++      }
++
++      return -ENOENT;
++}
++
++/**
++ * pci_rebar_get_possible_sizes - get possible sizes for BAR
++ * @pdev: PCI device
++ * @bar: BAR to query
++ *
++ * Get the possible sizes of a resizable BAR as bitmask defined in the spec
++ * (bit 0=1MB, bit 19=512GB). Returns 0 if BAR isn't resizable.
++ */
++u32 pci_rebar_get_possible_sizes(struct pci_dev *pdev, int bar)
++{
++      int pos;
++      u32 cap;
++
++      pos = pci_rebar_find_pos(pdev, bar);
++      if (pos < 0)
++              return 0;
++
++      pci_read_config_dword(pdev, pos + PCI_REBAR_CAP, &cap);
++      cap = FIELD_GET(PCI_REBAR_CAP_SIZES, cap);
++
++      /* Sapphire RX 5600 XT Pulse has an invalid cap dword for BAR 0 */
++      if (pdev->vendor == PCI_VENDOR_ID_ATI && pdev->device == 0x731f &&
++          bar == 0 && cap == 0x700)
++              return 0x3f00;
++
++      return cap;
++}
++EXPORT_SYMBOL(pci_rebar_get_possible_sizes);
++
++/**
++ * pci_rebar_get_current_size - get the current size of a BAR
++ * @pdev: PCI device
++ * @bar: BAR to set size to
++ *
++ * Read the size of a BAR from the resizable BAR config.
++ * Returns size if found or negative error code.
++ */
++int pci_rebar_get_current_size(struct pci_dev *pdev, int bar)
++{
++      int pos;
++      u32 ctrl;
++
++      pos = pci_rebar_find_pos(pdev, bar);
++      if (pos < 0)
++              return pos;
++
++      pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
++      return (ctrl & PCI_REBAR_CTRL_BAR_SIZE) >> PCI_REBAR_CTRL_BAR_SHIFT;
++}
++
++/**
++ * pci_rebar_set_size - set a new size for a BAR
++ * @pdev: PCI device
++ * @bar: BAR to set size to
++ * @size: new size as defined in the spec (0=1MB, 19=512GB)
++ *
++ * Set the new size of a BAR as defined in the spec.
++ * Returns zero if resizing was successful, error code otherwise.
++ */
++int pci_rebar_set_size(struct pci_dev *pdev, int bar, int size)
++{
++      int pos;
++      u32 ctrl;
++
++      pos = pci_rebar_find_pos(pdev, bar);
++      if (pos < 0)
++              return pos;
++
++      pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
++      ctrl &= ~PCI_REBAR_CTRL_BAR_SIZE;
++      ctrl |= size << PCI_REBAR_CTRL_BAR_SHIFT;
++      pci_write_config_dword(pdev, pos + PCI_REBAR_CTRL, ctrl);
++      return 0;
++}
++
++void pci_restore_rebar_state(struct pci_dev *pdev)
++{
++      unsigned int pos, nbars, i;
++      u32 ctrl;
++
++      pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_REBAR);
++      if (!pos)
++              return;
++
++      pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
++      nbars = FIELD_GET(PCI_REBAR_CTRL_NBAR_MASK, ctrl);
++
++      for (i = 0; i < nbars; i++, pos += 8) {
++              struct resource *res;
++              int bar_idx, size;
++
++              pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
++              bar_idx = ctrl & PCI_REBAR_CTRL_BAR_IDX;
++              res = pdev->resource + bar_idx;
++              size = pci_rebar_bytes_to_size(resource_size(res));
++              ctrl &= ~PCI_REBAR_CTRL_BAR_SIZE;
++              ctrl |= size << PCI_REBAR_CTRL_BAR_SHIFT;
++              pci_write_config_dword(pdev, pos + PCI_REBAR_CTRL, ctrl);
++      }
++}
++
++/**
++ * pci_resize_resource - reconfigure a Resizable BAR and resources
++ * @dev: the PCI device
++ * @resno: index of the BAR to be resized
++ * @size: new size as defined in the spec (0=1MB, 31=128TB)
++ * @exclude_bars: a mask of BARs that should not be released
++ *
++ * Reconfigure @resno to @size and re-run resource assignment algorithm
++ * with the new size.
++ *
++ * Prior to resize, release @dev resources that share a bridge window with
++ * @resno.  This unpins the bridge window resource to allow changing it.
++ *
++ * The caller may prevent releasing a particular BAR by providing
++ * @exclude_bars mask, but this may result in the resize operation failing
++ * due to insufficient space.
++ *
++ * Return: 0 on success, or negative on error. In case of an error, the
++ *         resources are restored to their original places.
++ */
++int pci_resize_resource(struct pci_dev *dev, int resno, int size,
++                      int exclude_bars)
++{
++      struct pci_host_bridge *host;
++      int old, ret;
++      u32 sizes;
++      u16 cmd;
++
++      /* Check if we must preserve the firmware's resource assignment */
++      host = pci_find_host_bridge(dev->bus);
++      if (host->preserve_config)
++              return -ENOTSUPP;
++
++      pci_read_config_word(dev, PCI_COMMAND, &cmd);
++      if (cmd & PCI_COMMAND_MEMORY)
++              return -EBUSY;
++
++      sizes = pci_rebar_get_possible_sizes(dev, resno);
++      if (!sizes)
++              return -ENOTSUPP;
++
++      if (!(sizes & BIT(size)))
++              return -EINVAL;
++
++      old = pci_rebar_get_current_size(dev, resno);
++      if (old < 0)
++              return old;
++
++      ret = pci_rebar_set_size(dev, resno, size);
++      if (ret)
++              return ret;
++
++      ret = pci_do_resource_release_and_resize(dev, resno, size, exclude_bars);
++      if (ret)
++              goto error_resize;
++
++      return 0;
++
++error_resize:
++      pci_rebar_set_size(dev, resno, old);
++      return ret;
++}
++EXPORT_SYMBOL(pci_resize_resource);
+--- a/drivers/pci/setup-res.c
++++ b/drivers/pci/setup-res.c
+@@ -422,70 +422,6 @@ void pci_release_resource(struct pci_dev
+ }
+ EXPORT_SYMBOL(pci_release_resource);
+-/**
+- * pci_resize_resource - reconfigure a Resizable BAR and resources
+- * @dev: the PCI device
+- * @resno: index of the BAR to be resized
+- * @size: new size as defined in the spec (0=1MB, 31=128TB)
+- * @exclude_bars: a mask of BARs that should not be released
+- *
+- * Reconfigure @resno to @size and re-run resource assignment algorithm
+- * with the new size.
+- *
+- * Prior to resize, release @dev resources that share a bridge window with
+- * @resno.  This unpins the bridge window resource to allow changing it.
+- *
+- * The caller may prevent releasing a particular BAR by providing
+- * @exclude_bars mask, but this may result in the resize operation failing
+- * due to insufficient space.
+- *
+- * Return: 0 on success, or negative on error. In case of an error, the
+- *         resources are restored to their original places.
+- */
+-int pci_resize_resource(struct pci_dev *dev, int resno, int size,
+-                      int exclude_bars)
+-{
+-      struct pci_host_bridge *host;
+-      int old, ret;
+-      u32 sizes;
+-      u16 cmd;
+-
+-      /* Check if we must preserve the firmware's resource assignment */
+-      host = pci_find_host_bridge(dev->bus);
+-      if (host->preserve_config)
+-              return -ENOTSUPP;
+-
+-      pci_read_config_word(dev, PCI_COMMAND, &cmd);
+-      if (cmd & PCI_COMMAND_MEMORY)
+-              return -EBUSY;
+-
+-      sizes = pci_rebar_get_possible_sizes(dev, resno);
+-      if (!sizes)
+-              return -ENOTSUPP;
+-
+-      if (!(sizes & BIT(size)))
+-              return -EINVAL;
+-
+-      old = pci_rebar_get_current_size(dev, resno);
+-      if (old < 0)
+-              return old;
+-
+-      ret = pci_rebar_set_size(dev, resno, size);
+-      if (ret)
+-              return ret;
+-
+-      ret = pci_do_resource_release_and_resize(dev, resno, size, exclude_bars);
+-      if (ret)
+-              goto error_resize;
+-
+-      return 0;
+-
+-error_resize:
+-      pci_rebar_set_size(dev, resno, old);
+-      return ret;
+-}
+-EXPORT_SYMBOL(pci_resize_resource);
+-
+ int pci_enable_resources(struct pci_dev *dev, int mask)
+ {
+       u16 cmd, old_cmd;
diff --git a/queue-5.15/pci-prevent-resource-tree-corruption-when-bar-resize-fails.patch b/queue-5.15/pci-prevent-resource-tree-corruption-when-bar-resize-fails.patch
new file mode 100644 (file)
index 0000000..f1f7593
--- /dev/null
@@ -0,0 +1,71 @@
+From stable+bounces-274891-greg=kroah.com@vger.kernel.org Wed Jul 15 13:16:26 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 15 Jul 2026 07:16:13 -0400
+Subject: PCI: Prevent resource tree corruption when BAR resize fails
+To: stable@vger.kernel.org
+Cc: "Ilpo Järvinen" <ilpo.jarvinen@linux.intel.com>, "Simon Richter" <Simon.Richter@hogyros.de>, "Alex Bennée" <alex.bennee@linaro.org>, "Bjorn Helgaas" <bhelgaas@google.com>, "Sasha Levin" <sashal@kernel.org>
+Message-ID: <20260715111618.647249-1-sashal@kernel.org>
+
+From: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+
+[ Upstream commit 91c4c89db41499eea1b29c56655f79c3bae66e93 ]
+
+pbus_reassign_bridge_resources() saves bridge windows into the saved
+list before attempting to adjust resource assignments to perform a BAR
+resize operation. If resource adjustments cannot be completed fully,
+rollback is attempted by restoring the resource from the saved list.
+
+The rollback, however, does not check whether the resources it restores were
+assigned by the partial resize attempt. If restore changes addresses of the
+resource, it can result in corrupting the resource tree.
+
+An example of a corrupted resource tree with overlapping addresses:
+
+  6200000000000-6203fbfffffff : pciex@620c3c0000000
+    6200000000000-6203fbff0ffff : PCI Bus 0030:01
+      6200020000000-62000207fffff : 0030:01:00.0
+      6200000000000-6203fbff0ffff : PCI Bus 0030:02
+
+A resource that are assigned into the resource tree must remain
+unchanged. Thus, release such a resource before attempting to restore
+and claim it back.
+
+For simplicity, always do the release and claim back for the resource
+even in the cases where it is restored to the same address range.
+
+Note: this fix may "break" some cases where devices "worked" because
+the resource tree corruption allowed address space double counting to
+fit more resource than what can now be assigned without double
+counting. The upcoming changes to BAR resizing should address those
+scenarios (to the extent possible).
+
+Fixes: 8bb705e3e79d ("PCI: Add pci_resize_resource() for resizing BARs")
+Reported-by: Simon Richter <Simon.Richter@hogyros.de>
+Link: https://lore.kernel.org/linux-pci/67840a16-99b4-4d8c-9b5c-4721ab0970a2@hogyros.de/
+Reported-by: Alex Bennée <alex.bennee@linaro.org>
+Link: https://lore.kernel.org/linux-pci/874irqop6b.fsf@draig.linaro.org/
+Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
+Tested-by: Alex Bennée <alex.bennee@linaro.org> # AVA, AMD GPU
+Link: https://patch.msgid.link/20251113162628.5946-2-ilpo.jarvinen@linux.intel.com
+Stable-dep-of: ee7471fe968d ("PCI: Skip Resizable BAR restore on read error")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/pci/setup-bus.c |    5 +++++
+ 1 file changed, 5 insertions(+)
+
+--- a/drivers/pci/setup-bus.c
++++ b/drivers/pci/setup-bus.c
+@@ -2257,6 +2257,11 @@ cleanup:
+               bridge = dev_res->dev;
+               i = res - bridge->resource;
++              if (res->parent) {
++                      release_child_resources(res);
++                      pci_release_resource(bridge, i);
++              }
++
+               res->start = dev_res->start;
+               res->end = dev_res->end;
+               res->flags = dev_res->flags;
diff --git a/queue-5.15/pci-skip-resizable-bar-restore-on-read-error.patch b/queue-5.15/pci-skip-resizable-bar-restore-on-read-error.patch
new file mode 100644 (file)
index 0000000..1093706
--- /dev/null
@@ -0,0 +1,61 @@
+From stable+bounces-274895-greg=kroah.com@vger.kernel.org Wed Jul 15 13:16:40 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 15 Jul 2026 07:16:18 -0400
+Subject: PCI: Skip Resizable BAR restore on read error
+To: stable@vger.kernel.org
+Cc: Marco Nenciarini <mnencia@kcore.it>, Bjorn Helgaas <bhelgaas@google.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260715111618.647249-6-sashal@kernel.org>
+
+From: Marco Nenciarini <mnencia@kcore.it>
+
+[ Upstream commit ee7471fe968d210939be9046089a924cd23c8c3b ]
+
+pci_restore_rebar_state() uses the Resizable BAR Control register to decide
+how many BARs to restore (nbars) and which BAR each iteration addresses
+(bar_idx).
+
+When a device does not respond, config reads typically return
+PCI_ERROR_RESPONSE (~0).  Both fields are 3 bits wide, so nbars and bar_idx
+both evaluate to 7, past the spec's valid ranges for both fields.
+pci_resource_n() then returns an unrelated resource slot, whose size is
+used to derive a nonsensical value written back to the Resizable BAR
+Control register.
+
+Bail out if any Resizable BAR Control read returns PCI_ERROR_RESPONSE. No
+further BARs are touched, which is safe because a config read that returns
+PCI_ERROR_RESPONSE indicates the device is unreachable and restoration is
+pointless.
+
+Fixes: d3252ace0bc6 ("PCI: Restore resized BAR state on resume")
+Signed-off-by: Marco Nenciarini <mnencia@kcore.it>
+Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
+Cc: stable@vger.kernel.org
+Link: https://patch.msgid.link/666cac19b5daa0ab0e0ab64454e76b4d24465dbd.1776429882.git.mnencia@kcore.it
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/pci/rebar.c |    6 ++++++
+ 1 file changed, 6 insertions(+)
+
+--- a/drivers/pci/rebar.c
++++ b/drivers/pci/rebar.c
+@@ -131,6 +131,9 @@ void pci_restore_rebar_state(struct pci_
+               return;
+       pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
++      if (PCI_POSSIBLE_ERROR(ctrl))
++              return;
++
+       nbars = FIELD_GET(PCI_REBAR_CTRL_NBAR_MASK, ctrl);
+       for (i = 0; i < nbars; i++, pos += 8) {
+@@ -138,6 +141,9 @@ void pci_restore_rebar_state(struct pci_
+               int bar_idx, size;
+               pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
++              if (PCI_POSSIBLE_ERROR(ctrl))
++                      return;
++
+               bar_idx = ctrl & PCI_REBAR_CTRL_BAR_IDX;
+               res = pdev->resource + bar_idx;
+               size = pci_rebar_bytes_to_size(resource_size(res));
diff --git a/queue-5.15/perf-x86-intel-uncore-defer-adl-global-pmon-enable-to-enable_box.patch b/queue-5.15/perf-x86-intel-uncore-defer-adl-global-pmon-enable-to-enable_box.patch
new file mode 100644 (file)
index 0000000..0323173
--- /dev/null
@@ -0,0 +1,57 @@
+From stable+bounces-276819-greg=kroah.com@vger.kernel.org Thu Jul 16 21:14:38 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 16 Jul 2026 15:14:28 -0400
+Subject: perf/x86/intel/uncore: Defer ADL global PMON enable to enable_box()
+To: stable@vger.kernel.org
+Cc: Zide Chen <zide.chen@intel.com>, "Peter Zijlstra (Intel)" <peterz@infradead.org>, Dapeng Mi <dapeng1.mi@linux.intel.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260716191428.1077510-1-sashal@kernel.org>
+
+From: Zide Chen <zide.chen@intel.com>
+
+[ Upstream commit 9a0bb848a37150aeccc10088e141339917d995dc ]
+
+On some Raptor Cove CPUs, enabling uncore PMON globally at driver init
+may increase power consumption even when no perf events are in use.
+
+Drop adl_uncore_msr_init_box() and defer programming the global control
+register to enable_box(), so it is only set when a box is actually used.
+
+IMC and IMC freerunning counters use a separate control path and are
+unaffected.
+
+Fixes: 772ed05f3c5c ("perf/x86/intel/uncore: Add Alder Lake support")
+Signed-off-by: Zide Chen <zide.chen@intel.com>
+Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
+Reviewed-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
+Cc: stable@vger.kernel.org
+Link: https://patch.msgid.link/20260602144908.263680-5-zide.chen@intel.com
+[ deleted adl_uncore_msr_init_box() in its 6.12 wrmsrl() spelling since the tree predates the wrmsrq() rename ]
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ arch/x86/events/intel/uncore_snb.c |    7 -------
+ 1 file changed, 7 deletions(-)
+
+--- a/arch/x86/events/intel/uncore_snb.c
++++ b/arch/x86/events/intel/uncore_snb.c
+@@ -445,12 +445,6 @@ void tgl_uncore_cpu_init(void)
+       skl_uncore_msr_ops.init_box = rkl_uncore_msr_init_box;
+ }
+-static void adl_uncore_msr_init_box(struct intel_uncore_box *box)
+-{
+-      if (box->pmu->pmu_idx == 0)
+-              wrmsrl(ADL_UNC_PERF_GLOBAL_CTL, SNB_UNC_GLOBAL_CTL_EN);
+-}
+-
+ static void adl_uncore_msr_enable_box(struct intel_uncore_box *box)
+ {
+       wrmsrl(ADL_UNC_PERF_GLOBAL_CTL, SNB_UNC_GLOBAL_CTL_EN);
+@@ -469,7 +463,6 @@ static void adl_uncore_msr_exit_box(stru
+ }
+ static struct intel_uncore_ops adl_uncore_msr_ops = {
+-      .init_box       = adl_uncore_msr_init_box,
+       .enable_box     = adl_uncore_msr_enable_box,
+       .disable_box    = adl_uncore_msr_disable_box,
+       .exit_box       = adl_uncore_msr_exit_box,
diff --git a/queue-5.15/proc-move-fdinfo-ptrace_mode_read-check-into-the-inode-.permission-operation.patch b/queue-5.15/proc-move-fdinfo-ptrace_mode_read-check-into-the-inode-.permission-operation.patch
new file mode 100644 (file)
index 0000000..756330f
--- /dev/null
@@ -0,0 +1,163 @@
+From stable+bounces-277055-greg=kroah.com@vger.kernel.org Fri Jul 17 16:03:50 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 17 Jul 2026 10:00:14 -0400
+Subject: proc: Move fdinfo PTRACE_MODE_READ check into the inode .permission operation
+To: stable@vger.kernel.org
+Cc: "Tyler Hicks (Microsoft)" <code@tyhicks.com>, "Kevin Parsons (Microsoft)" <parsonskev@gmail.com>, "Christian Brauner" <brauner@kernel.org>, "Christian König" <christian.koenig@amd.com>, "Jann Horn" <jannh@google.com>, "Kalesh Singh" <kaleshsingh@google.com>, "Hardik Garg" <hargar@linux.microsoft.com>, "Allen Pais" <apais@linux.microsoft.com>, "Sasha Levin" <sashal@kernel.org>
+Message-ID: <20260717140016.1717138-2-sashal@kernel.org>
+
+From: "Tyler Hicks (Microsoft)" <code@tyhicks.com>
+
+[ Upstream commit 0a960ba49869ebe8ff859d000351504dd6b93b68 ]
+
+The following commits loosened the permissions of /proc/<PID>/fdinfo/
+directory, as well as the files within it, from 0500 to 0555 while also
+introducing a PTRACE_MODE_READ check between the current task and
+<PID>'s task:
+
+ - commit 7bc3fa0172a4 ("procfs: allow reading fdinfo with PTRACE_MODE_READ")
+ - commit 1927e498aee1 ("procfs: prevent unprivileged processes accessing fdinfo dir")
+
+Before those changes, inode based system calls like inotify_add_watch(2)
+would fail when the current task didn't have sufficient read permissions:
+
+ [...]
+ lstat("/proc/1/task/1/fdinfo", {st_mode=S_IFDIR|0500, st_size=0, ...}) = 0
+ inotify_add_watch(64, "/proc/1/task/1/fdinfo",
+                  IN_MODIFY|IN_ATTRIB|IN_MOVED_FROM|IN_MOVED_TO|IN_CREATE|IN_DELETE|
+                  IN_ONLYDIR|IN_DONT_FOLLOW|IN_EXCL_UNLINK) = -1 EACCES (Permission denied)
+ [...]
+
+This matches the documented behavior in the inotify_add_watch(2) man
+page:
+
+ ERRORS
+       EACCES Read access to the given file is not permitted.
+
+After those changes, inotify_add_watch(2) started succeeding despite the
+current task not having PTRACE_MODE_READ privileges on the target task:
+
+ [...]
+ lstat("/proc/1/task/1/fdinfo", {st_mode=S_IFDIR|0555, st_size=0, ...}) = 0
+ inotify_add_watch(64, "/proc/1/task/1/fdinfo",
+                  IN_MODIFY|IN_ATTRIB|IN_MOVED_FROM|IN_MOVED_TO|IN_CREATE|IN_DELETE|
+                  IN_ONLYDIR|IN_DONT_FOLLOW|IN_EXCL_UNLINK) = 1757
+ openat(AT_FDCWD, "/proc/1/task/1/fdinfo",
+       O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_DIRECTORY) = -1 EACCES (Permission denied)
+ [...]
+
+This change in behavior broke .NET prior to v7. See the github link
+below for the v7 commit that inadvertently/quietly (?) fixed .NET after
+the kernel changes mentioned above.
+
+Return to the old behavior by moving the PTRACE_MODE_READ check out of
+the file .open operation and into the inode .permission operation:
+
+ [...]
+ lstat("/proc/1/task/1/fdinfo", {st_mode=S_IFDIR|0555, st_size=0, ...}) = 0
+ inotify_add_watch(64, "/proc/1/task/1/fdinfo",
+                  IN_MODIFY|IN_ATTRIB|IN_MOVED_FROM|IN_MOVED_TO|IN_CREATE|IN_DELETE|
+                  IN_ONLYDIR|IN_DONT_FOLLOW|IN_EXCL_UNLINK) = -1 EACCES (Permission denied)
+ [...]
+
+Reported-by: Kevin Parsons (Microsoft) <parsonskev@gmail.com>
+Link: https://github.com/dotnet/runtime/commit/89e5469ac591b82d38510fe7de98346cce74ad4f
+Link: https://stackoverflow.com/questions/75379065/start-self-contained-net6-build-exe-as-service-on-raspbian-system-unauthorizeda
+Fixes: 7bc3fa0172a4 ("procfs: allow reading fdinfo with PTRACE_MODE_READ")
+Cc: stable@vger.kernel.org
+Cc: Christian Brauner <brauner@kernel.org>
+Cc: Christian König <christian.koenig@amd.com>
+Cc: Jann Horn <jannh@google.com>
+Cc: Kalesh Singh <kaleshsingh@google.com>
+Cc: Hardik Garg <hargar@linux.microsoft.com>
+Cc: Allen Pais <apais@linux.microsoft.com>
+Signed-off-by: Tyler Hicks (Microsoft) <code@tyhicks.com>
+Link: https://lore.kernel.org/r/20240501005646.745089-1-code@tyhicks.com
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Stable-dep-of: 6255da28d4bb ("proc: protect ptrace_may_access() with exec_update_lock (FD links)")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/proc/fd.c |   42 ++++++++++++++++++++----------------------
+ 1 file changed, 20 insertions(+), 22 deletions(-)
+
+--- a/fs/proc/fd.c
++++ b/fs/proc/fd.c
+@@ -72,7 +72,18 @@ out:
+       return 0;
+ }
+-static int proc_fdinfo_access_allowed(struct inode *inode)
++static int seq_fdinfo_open(struct inode *inode, struct file *file)
++{
++      return single_open(file, seq_show, inode);
++}
++
++/**
++ * Shared /proc/pid/fdinfo and /proc/pid/fdinfo/fd permission helper to ensure
++ * that the current task has PTRACE_MODE_READ in addition to the normal
++ * POSIX-like checks.
++ */
++static int proc_fdinfo_permission(struct mnt_idmap *idmap, struct inode *inode,
++                                int mask)
+ {
+       bool allowed = false;
+       struct task_struct *task = get_proc_task(inode);
+@@ -86,18 +97,13 @@ static int proc_fdinfo_access_allowed(st
+       if (!allowed)
+               return -EACCES;
+-      return 0;
++      return generic_permission(idmap, inode, mask);
+ }
+-static int seq_fdinfo_open(struct inode *inode, struct file *file)
+-{
+-      int ret = proc_fdinfo_access_allowed(inode);
+-
+-      if (ret)
+-              return ret;
+-
+-      return single_open(file, seq_show, inode);
+-}
++static const struct inode_operations proc_fdinfo_file_inode_operations = {
++      .permission     = proc_fdinfo_permission,
++      .setattr        = proc_setattr,
++};
+ static const struct file_operations proc_fdinfo_file_operations = {
+       .open           = seq_fdinfo_open,
+@@ -339,6 +345,8 @@ static struct dentry *proc_fdinfo_instan
+       ei = PROC_I(inode);
+       ei->fd = data->fd;
++      inode->i_op = &proc_fdinfo_file_inode_operations;
++
+       inode->i_fop = &proc_fdinfo_file_operations;
+       tid_fd_update_inode(task, inode, 0);
+@@ -358,23 +366,13 @@ static int proc_readfdinfo(struct file *
+                                 proc_fdinfo_instantiate);
+ }
+-static int proc_open_fdinfo(struct inode *inode, struct file *file)
+-{
+-      int ret = proc_fdinfo_access_allowed(inode);
+-
+-      if (ret)
+-              return ret;
+-
+-      return 0;
+-}
+-
+ const struct inode_operations proc_fdinfo_inode_operations = {
+       .lookup         = proc_lookupfdinfo,
++      .permission     = proc_fdinfo_permission,
+       .setattr        = proc_setattr,
+ };
+ const struct file_operations proc_fdinfo_operations = {
+-      .open           = proc_open_fdinfo,
+       .read           = generic_read_dir,
+       .iterate_shared = proc_readfdinfo,
+       .llseek         = generic_file_llseek,
diff --git a/queue-5.15/proc-protect-ptrace_may_access-with-exec_update_lock-fd-links.patch b/queue-5.15/proc-protect-ptrace_may_access-with-exec_update_lock-fd-links.patch
new file mode 100644 (file)
index 0000000..50fb51f
--- /dev/null
@@ -0,0 +1,274 @@
+From stable+bounces-277057-greg=kroah.com@vger.kernel.org Fri Jul 17 16:00:26 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 17 Jul 2026 10:00:16 -0400
+Subject: proc: protect ptrace_may_access() with exec_update_lock (FD links)
+To: stable@vger.kernel.org
+Cc: Jann Horn <jannh@google.com>, "Christian Brauner (Amutable)" <brauner@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260717140016.1717138-4-sashal@kernel.org>
+
+From: Jann Horn <jannh@google.com>
+
+[ Upstream commit 6255da28d4bb5349fe18e84cb043ccd394eba75d ]
+
+proc_pid_get_link() and proc_pid_readlink() currently look up the task from
+the pid once, then do the ptrace access check on that task, then look up
+the task from the pid a second time to do the actual access.
+That's racy in several ways.
+
+To fix it, pass the task to the ->proc_get_link() handler, and instead of
+proc_fd_access_allowed(), introduce a new helper call_proc_get_link() that
+looks up and locks the task, does the access check, and calls
+->proc_get_link().
+
+Fixes: 778c1144771f ("[PATCH] proc: Use sane permission checks on the /proc/<pid>/fd/ symlinks")
+Cc: stable@vger.kernel.org
+Signed-off-by: Jann Horn <jannh@google.com>
+Link: https://patch.msgid.link/20260518-procfs-lockfix-part1-v1-2-5c3d20e0ac33@google.com
+Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/proc/base.c     |  119 ++++++++++++++++++++---------------------------------
+ fs/proc/fd.c       |   25 ++++-------
+ fs/proc/internal.h |    2 
+ 3 files changed, 58 insertions(+), 88 deletions(-)
+
+--- a/fs/proc/base.c
++++ b/fs/proc/base.c
+@@ -218,33 +218,24 @@ static int get_task_root(struct task_str
+       return result;
+ }
+-static int proc_cwd_link(struct dentry *dentry, struct path *path)
++static int proc_cwd_link(struct dentry *dentry, struct path *path,
++                       struct task_struct *task)
+ {
+-      struct task_struct *task = get_proc_task(d_inode(dentry));
+       int result = -ENOENT;
+-      if (task) {
+-              task_lock(task);
+-              if (task->fs) {
+-                      get_fs_pwd(task->fs, path);
+-                      result = 0;
+-              }
+-              task_unlock(task);
+-              put_task_struct(task);
++      task_lock(task);
++      if (task->fs) {
++              get_fs_pwd(task->fs, path);
++              result = 0;
+       }
++      task_unlock(task);
+       return result;
+ }
+-static int proc_root_link(struct dentry *dentry, struct path *path)
++static int proc_root_link(struct dentry *dentry, struct path *path,
++                        struct task_struct *task)
+ {
+-      struct task_struct *task = get_proc_task(d_inode(dentry));
+-      int result = -ENOENT;
+-
+-      if (task) {
+-              result = get_task_root(task, path);
+-              put_task_struct(task);
+-      }
+-      return result;
++      return get_task_root(task, path);
+ }
+ /*
+@@ -704,23 +695,6 @@ static int proc_pid_syscall(struct seq_f
+ /*                       Here the fs part begins                        */
+ /************************************************************************/
+-/* permission checks */
+-static int proc_fd_access_allowed(struct inode *inode)
+-{
+-      struct task_struct *task;
+-      int allowed = 0;
+-      /* Allow access to a task's file descriptors if it is us or we
+-       * may use ptrace attach to the process and find out that
+-       * information.
+-       */
+-      task = get_proc_task(inode);
+-      if (task) {
+-              allowed = ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS);
+-              put_task_struct(task);
+-      }
+-      return allowed;
+-}
+-
+ int proc_nochmod_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
+                struct iattr *attr)
+ {
+@@ -1779,16 +1753,12 @@ static const struct file_operations proc
+       .release        = single_release,
+ };
+-static int proc_exe_link(struct dentry *dentry, struct path *exe_path)
++static int proc_exe_link(struct dentry *dentry, struct path *exe_path,
++                       struct task_struct *task)
+ {
+-      struct task_struct *task;
+       struct file *exe_file;
+-      task = get_proc_task(d_inode(dentry));
+-      if (!task)
+-              return -ENOENT;
+       exe_file = get_task_exe_file(task);
+-      put_task_struct(task);
+       if (exe_file) {
+               *exe_path = exe_file->f_path;
+               path_get(&exe_file->f_path);
+@@ -1798,26 +1768,42 @@ static int proc_exe_link(struct dentry *
+               return -ENOENT;
+ }
++static int call_proc_get_link(struct dentry *dentry, struct inode *inode, struct path *path_out)
++{
++      struct task_struct *task;
++      int ret;
++
++      task = get_proc_task(inode);
++      if (!task)
++              return -ENOENT;
++      ret = down_read_killable(&task->signal->exec_update_lock);
++      if (ret)
++              goto out_put_task;
++      if (!ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS)) {
++              ret = -EACCES;
++              goto out;
++      }
++      ret = PROC_I(inode)->op.proc_get_link(dentry, path_out, task);
++
++out:
++      up_read(&task->signal->exec_update_lock);
++out_put_task:
++      put_task_struct(task);
++      return ret;
++}
++
+ static const char *proc_pid_get_link(struct dentry *dentry,
+                                    struct inode *inode,
+                                    struct delayed_call *done)
+ {
+       struct path path;
+-      int error = -EACCES;
++      int error;
+       if (!dentry)
+               return ERR_PTR(-ECHILD);
+-
+-      /* Are we allowed to snoop on the tasks file descriptors? */
+-      if (!proc_fd_access_allowed(inode))
+-              goto out;
+-
+-      error = PROC_I(inode)->op.proc_get_link(dentry, &path);
+-      if (error)
+-              goto out;
+-
+-      error = nd_jump_link(&path);
+-out:
++      error = call_proc_get_link(dentry, inode, &path);
++      if (!error)
++              error = nd_jump_link(&path);
+       return ERR_PTR(error);
+ }
+@@ -1851,17 +1837,11 @@ static int proc_pid_readlink(struct dent
+       struct inode *inode = d_inode(dentry);
+       struct path path;
+-      /* Are we allowed to snoop on the tasks file descriptors? */
+-      if (!proc_fd_access_allowed(inode))
+-              goto out;
+-
+-      error = PROC_I(inode)->op.proc_get_link(dentry, &path);
+-      if (error)
+-              goto out;
+-
+-      error = do_proc_readlink(&path, buffer, buflen);
+-      path_put(&path);
+-out:
++      error = call_proc_get_link(dentry, inode, &path);
++      if (!error) {
++              error = do_proc_readlink(&path, buffer, buflen);
++              path_put(&path);
++      }
+       return error;
+ }
+@@ -2247,21 +2227,16 @@ static const struct dentry_operations ti
+       .d_delete       = pid_delete_dentry,
+ };
+-static int map_files_get_link(struct dentry *dentry, struct path *path)
++static int map_files_get_link(struct dentry *dentry, struct path *path,
++                            struct task_struct *task)
+ {
+       unsigned long vm_start, vm_end;
+       struct vm_area_struct *vma;
+-      struct task_struct *task;
+       struct mm_struct *mm;
+       int rc;
+       rc = -ENOENT;
+-      task = get_proc_task(d_inode(dentry));
+-      if (!task)
+-              goto out;
+-
+       mm = get_task_mm(task);
+-      put_task_struct(task);
+       if (!mm)
+               goto out;
+--- a/fs/proc/fd.c
++++ b/fs/proc/fd.c
+@@ -170,24 +170,19 @@ static const struct dentry_operations ti
+       .d_delete       = pid_delete_dentry,
+ };
+-static int proc_fd_link(struct dentry *dentry, struct path *path)
++static int proc_fd_link(struct dentry *dentry, struct path *path,
++                      struct task_struct *task)
+ {
+-      struct task_struct *task;
+       int ret = -ENOENT;
++      unsigned int fd = proc_fd(d_inode(dentry));
++      struct file *fd_file;
+-      task = get_proc_task(d_inode(dentry));
+-      if (task) {
+-              unsigned int fd = proc_fd(d_inode(dentry));
+-              struct file *fd_file;
+-
+-              fd_file = fget_task(task, fd);
+-              if (fd_file) {
+-                      *path = fd_file->f_path;
+-                      path_get(&fd_file->f_path);
+-                      ret = 0;
+-                      fput(fd_file);
+-              }
+-              put_task_struct(task);
++      fd_file = fget_task(task, fd);
++      if (fd_file) {
++              *path = fd_file->f_path;
++              path_get(&fd_file->f_path);
++              ret = 0;
++              fput(fd_file);
+       }
+       return ret;
+--- a/fs/proc/internal.h
++++ b/fs/proc/internal.h
+@@ -97,7 +97,7 @@ extern struct kmem_cache *proc_dir_entry
+ void pde_free(struct proc_dir_entry *pde);
+ union proc_op {
+-      int (*proc_get_link)(struct dentry *, struct path *);
++      int (*proc_get_link)(struct dentry *, struct path *, struct task_struct *);
+       int (*proc_show)(struct seq_file *m,
+               struct pid_namespace *ns, struct pid *pid,
+               struct task_struct *task);
diff --git a/queue-5.15/proc-protect-ptrace_may_access-with-exec_update_lock-part-1.patch b/queue-5.15/proc-protect-ptrace_may_access-with-exec_update_lock-part-1.patch
new file mode 100644 (file)
index 0000000..507716e
--- /dev/null
@@ -0,0 +1,210 @@
+From stable+bounces-277174-greg=kroah.com@vger.kernel.org Fri Jul 17 20:15:51 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 17 Jul 2026 14:11:49 -0400
+Subject: proc: protect ptrace_may_access() with exec_update_lock (part 1)
+To: stable@vger.kernel.org
+Cc: Jann Horn <jannh@google.com>, "Christian Brauner (Amutable)" <brauner@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260717181149.2086640-1-sashal@kernel.org>
+
+From: Jann Horn <jannh@google.com>
+
+[ Upstream commit 6650527444dadc63d84aa939d14ecba4fadb2f69 ]
+
+Fix the easy cases where procfs currently calls ptrace_may_access() without
+exec_update_lock protection, where the fix is to simply add the extra lock
+or use mm_access():
+
+ - do_task_stat(): grab exec_update_lock
+ - proc_pid_wchan(): grab exec_update_lock
+ - proc_map_files_lookup(): use mm_access() instead of get_task_mm()
+ - proc_map_files_readdir(): use mm_access() instead of get_task_mm()
+ - proc_ns_get_link(): grab exec_update_lock
+ - proc_ns_readlink(): grab exec_update_lock
+
+Fixes: f83ce3e6b02d ("proc: avoid information leaks to non-privileged processes")
+Cc: stable@vger.kernel.org
+Signed-off-by: Jann Horn <jannh@google.com>
+Link: https://patch.msgid.link/20260518-procfs-lockfix-part1-v1-1-5c3d20e0ac33@google.com
+Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
+[ converted NULL return from mm_access() to ERR_PTR(-ESRCH) at call sites since 5.15's mm_access() returns NULL when the task has no mm ]
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/proc/array.c      |    6 ++++++
+ fs/proc/base.c       |   41 ++++++++++++++++++++++++-----------------
+ fs/proc/namespaces.c |   12 ++++++++++++
+ 3 files changed, 42 insertions(+), 17 deletions(-)
+
+--- a/fs/proc/array.c
++++ b/fs/proc/array.c
+@@ -468,6 +468,11 @@ static int do_task_stat(struct seq_file
+       unsigned long flags;
+       struct signal_struct *sig = task->signal;
+       unsigned int seq = 1;
++      int ret;
++
++      ret = down_read_killable(&task->signal->exec_update_lock);
++      if (ret)
++              return ret;
+       state = *get_task_state(task);
+       vsize = eip = esp = 0;
+@@ -642,6 +647,7 @@ static int do_task_stat(struct seq_file
+               seq_puts(m, " 0");
+       seq_putc(m, '\n');
++      up_read(&task->signal->exec_update_lock);
+       if (mm)
+               mmput(mm);
+       return 0;
+--- a/fs/proc/base.c
++++ b/fs/proc/base.c
+@@ -414,18 +414,24 @@ static int proc_pid_wchan(struct seq_fil
+ {
+       unsigned long wchan;
+       char symname[KSYM_NAME_LEN];
++      int err;
++      err = down_read_killable(&task->signal->exec_update_lock);
++      if (err)
++              return err;
+       if (!ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS))
+               goto print0;
+       wchan = get_wchan(task);
+       if (wchan && !lookup_symbol_name(wchan, symname)) {
+               seq_puts(m, symname);
++              up_read(&task->signal->exec_update_lock);
+               return 0;
+       }
+ print0:
+       seq_putc(m, '0');
++      up_read(&task->signal->exec_update_lock);
+       return 0;
+ }
+ #endif /* CONFIG_KALLSYMS */
+@@ -2332,17 +2338,17 @@ static struct dentry *proc_map_files_loo
+       if (!task)
+               goto out;
+-      result = ERR_PTR(-EACCES);
+-      if (!ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS))
+-              goto out_put_task;
+-
+       result = ERR_PTR(-ENOENT);
+       if (dname_to_vma_addr(dentry, &vm_start, &vm_end))
+               goto out_put_task;
+-      mm = get_task_mm(task);
++      mm = mm_access(task, PTRACE_MODE_READ_FSCREDS);
+       if (!mm)
++              mm = ERR_PTR(-ESRCH);
++      if (IS_ERR(mm)) {
++              result = ERR_CAST(mm);
+               goto out_put_task;
++      }
+       result = ERR_PTR(-EINTR);
+       if (mmap_read_lock_killable(mm))
+@@ -2391,23 +2397,24 @@ proc_map_files_readdir(struct file *file
+       if (!task)
+               goto out;
+-      ret = -EACCES;
+-      if (!ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS))
+-              goto out_put_task;
+-
+       ret = 0;
+       if (!dir_emit_dots(file, ctx))
+               goto out_put_task;
+-      mm = get_task_mm(task);
++      mm = mm_access(task, PTRACE_MODE_READ_FSCREDS);
+       if (!mm)
++              mm = ERR_PTR(-ESRCH);
++      if (IS_ERR(mm)) {
++              ret = PTR_ERR(mm);
++              /* if the task has no mm, the directory should just be empty */
++              if (ret == -ESRCH)
++                      ret = 0;
+               goto out_put_task;
++      }
+       ret = mmap_read_lock_killable(mm);
+-      if (ret) {
+-              mmput(mm);
+-              goto out_put_task;
+-      }
++      if (ret)
++              goto out_put_mm;
+       nr_files = 0;
+@@ -2431,8 +2438,7 @@ proc_map_files_readdir(struct file *file
+               if (!p) {
+                       ret = -ENOMEM;
+                       mmap_read_unlock(mm);
+-                      mmput(mm);
+-                      goto out_put_task;
++                      goto out_put_mm;
+               }
+               p->start = vma->vm_start;
+@@ -2440,7 +2446,6 @@ proc_map_files_readdir(struct file *file
+               p->mode = vma->vm_file->f_mode;
+       }
+       mmap_read_unlock(mm);
+-      mmput(mm);
+       for (i = 0; i < nr_files; i++) {
+               char buf[4 * sizeof(long) + 2]; /* max: %lx-%lx\0 */
+@@ -2457,6 +2462,8 @@ proc_map_files_readdir(struct file *file
+               ctx->pos++;
+       }
++out_put_mm:
++      mmput(mm);
+ out_put_task:
+       put_task_struct(task);
+ out:
+--- a/fs/proc/namespaces.c
++++ b/fs/proc/namespaces.c
+@@ -55,6 +55,10 @@ static const char *proc_ns_get_link(stru
+       if (!task)
+               return ERR_PTR(-EACCES);
++      error = down_read_killable(&task->signal->exec_update_lock);
++      if (error)
++              goto out_put_task;
++
+       if (!ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS))
+               goto out;
+@@ -64,6 +68,8 @@ static const char *proc_ns_get_link(stru
+       error = nd_jump_link(&ns_path);
+ out:
++      up_read(&task->signal->exec_update_lock);
++out_put_task:
+       put_task_struct(task);
+       return ERR_PTR(error);
+ }
+@@ -80,11 +86,17 @@ static int proc_ns_readlink(struct dentr
+       if (!task)
+               return res;
++      res = down_read_killable(&task->signal->exec_update_lock);
++      if (res)
++              goto out_put_task;
++
+       if (ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS)) {
+               res = ns_get_name(name, sizeof(name), task, ns_ops);
+               if (res >= 0)
+                       res = readlink_copy(buffer, buflen, name);
+       }
++      up_read(&task->signal->exec_update_lock);
++out_put_task:
+       put_task_struct(task);
+       return res;
+ }
diff --git a/queue-5.15/proc-rename-proc_setattr-to-proc_nochmod_setattr.patch b/queue-5.15/proc-rename-proc_setattr-to-proc_nochmod_setattr.patch
new file mode 100644 (file)
index 0000000..9a664c4
--- /dev/null
@@ -0,0 +1,221 @@
+From stable+bounces-277056-greg=kroah.com@vger.kernel.org Fri Jul 17 16:01:36 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 17 Jul 2026 10:00:15 -0400
+Subject: proc: rename proc_setattr to proc_nochmod_setattr
+To: stable@vger.kernel.org
+Cc: Christoph Hellwig <hch@lst.de>, Jan Kara <jack@suse.cz>, Christian Brauner <brauner@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260717140016.1717138-3-sashal@kernel.org>
+
+From: Christoph Hellwig <hch@lst.de>
+
+[ Upstream commit 690005b0b1e6b567c88b7790e6d90d4d6c9e09cc ]
+
+What is currently proc_setattr is a special version added after the more
+general procfs ->seattr in commit 6d76fa58b050 ("Don't allow chmod() on
+the /proc/<pid>/ files").  Give it a name that reflects that to free the
+proc_setattr name and better describe what is doing.
+
+Signed-off-by: Christoph Hellwig <hch@lst.de>
+Link: https://patch.msgid.link/20260325063711.3298685-5-hch@lst.de
+Reviewed-by: Jan Kara <jack@suse.cz>
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Stable-dep-of: 6255da28d4bb ("proc: protect ptrace_may_access() with exec_update_lock (FD links)")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/proc/base.c       |   22 +++++++++++-----------
+ fs/proc/fd.c         |   12 ++++++------
+ fs/proc/internal.h   |    4 ++--
+ fs/proc/namespaces.c |    4 ++--
+ fs/proc/proc_net.c   |    2 +-
+ 5 files changed, 22 insertions(+), 22 deletions(-)
+
+--- a/fs/proc/base.c
++++ b/fs/proc/base.c
+@@ -721,7 +721,7 @@ static int proc_fd_access_allowed(struct
+       return allowed;
+ }
+-int proc_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
++int proc_nochmod_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
+                struct iattr *attr)
+ {
+       int error;
+@@ -795,7 +795,7 @@ static int proc_pid_permission(struct us
+ static const struct inode_operations proc_def_inode_operations = {
+-      .setattr        = proc_setattr,
++      .setattr        = proc_nochmod_setattr,
+ };
+ static int proc_single_show(struct seq_file *m, void *v)
+@@ -1868,7 +1868,7 @@ out:
+ const struct inode_operations proc_pid_link_inode_operations = {
+       .readlink       = proc_pid_readlink,
+       .get_link       = proc_pid_get_link,
+-      .setattr        = proc_setattr,
++      .setattr        = proc_nochmod_setattr,
+ };
+@@ -2316,7 +2316,7 @@ proc_map_files_get_link(struct dentry *d
+ static const struct inode_operations proc_map_files_link_inode_operations = {
+       .readlink       = proc_pid_readlink,
+       .get_link       = proc_map_files_get_link,
+-      .setattr        = proc_setattr,
++      .setattr        = proc_nochmod_setattr,
+ };
+ static struct dentry *
+@@ -2395,7 +2395,7 @@ out:
+ static const struct inode_operations proc_map_files_inode_operations = {
+       .lookup         = proc_map_files_lookup,
+       .permission     = proc_fd_permission,
+-      .setattr        = proc_setattr,
++      .setattr        = proc_nochmod_setattr,
+ };
+ static int
+@@ -2887,7 +2887,7 @@ static struct dentry *proc_##LSM##_attr_
+ static const struct inode_operations proc_##LSM##_attr_dir_inode_ops = { \
+       .lookup         = proc_##LSM##_attr_dir_lookup, \
+       .getattr        = pid_getattr, \
+-      .setattr        = proc_setattr, \
++      .setattr        = proc_nochmod_setattr, \
+ }
+ #ifdef CONFIG_SECURITY_SMACK
+@@ -2946,7 +2946,7 @@ static struct dentry *proc_attr_dir_look
+ static const struct inode_operations proc_attr_dir_inode_operations = {
+       .lookup         = proc_attr_dir_lookup,
+       .getattr        = pid_getattr,
+-      .setattr        = proc_setattr,
++      .setattr        = proc_nochmod_setattr,
+ };
+ #endif
+@@ -3403,7 +3403,7 @@ static struct dentry *proc_tgid_base_loo
+ static const struct inode_operations proc_tgid_base_inode_operations = {
+       .lookup         = proc_tgid_base_lookup,
+       .getattr        = pid_getattr,
+-      .setattr        = proc_setattr,
++      .setattr        = proc_nochmod_setattr,
+       .permission     = proc_pid_permission,
+ };
+@@ -3603,7 +3603,7 @@ static int proc_tid_comm_permission(stru
+ }
+ static const struct inode_operations proc_tid_comm_inode_operations = {
+-              .setattr        = proc_setattr,
++              .setattr        = proc_nochmod_setattr,
+               .permission     = proc_tid_comm_permission,
+ };
+@@ -3730,7 +3730,7 @@ static const struct file_operations proc
+ static const struct inode_operations proc_tid_base_inode_operations = {
+       .lookup         = proc_tid_base_lookup,
+       .getattr        = pid_getattr,
+-      .setattr        = proc_setattr,
++      .setattr        = proc_nochmod_setattr,
+ };
+ static struct dentry *proc_task_instantiate(struct dentry *dentry,
+@@ -3926,7 +3926,7 @@ static int proc_task_getattr(struct user
+ static const struct inode_operations proc_task_inode_operations = {
+       .lookup         = proc_task_lookup,
+       .getattr        = proc_task_getattr,
+-      .setattr        = proc_setattr,
++      .setattr        = proc_nochmod_setattr,
+       .permission     = proc_pid_permission,
+ };
+--- a/fs/proc/fd.c
++++ b/fs/proc/fd.c
+@@ -82,8 +82,8 @@ static int seq_fdinfo_open(struct inode
+  * that the current task has PTRACE_MODE_READ in addition to the normal
+  * POSIX-like checks.
+  */
+-static int proc_fdinfo_permission(struct mnt_idmap *idmap, struct inode *inode,
+-                                int mask)
++static int proc_fdinfo_permission(struct user_namespace *mnt_userns,
++                                struct inode *inode, int mask)
+ {
+       bool allowed = false;
+       struct task_struct *task = get_proc_task(inode);
+@@ -97,12 +97,12 @@ static int proc_fdinfo_permission(struct
+       if (!allowed)
+               return -EACCES;
+-      return generic_permission(idmap, inode, mask);
++      return generic_permission(mnt_userns, inode, mask);
+ }
+ static const struct inode_operations proc_fdinfo_file_inode_operations = {
+       .permission     = proc_fdinfo_permission,
+-      .setattr        = proc_setattr,
++      .setattr        = proc_nochmod_setattr,
+ };
+ static const struct file_operations proc_fdinfo_file_operations = {
+@@ -328,7 +328,7 @@ int proc_fd_permission(struct user_names
+ const struct inode_operations proc_fd_inode_operations = {
+       .lookup         = proc_lookupfd,
+       .permission     = proc_fd_permission,
+-      .setattr        = proc_setattr,
++      .setattr        = proc_nochmod_setattr,
+ };
+ static struct dentry *proc_fdinfo_instantiate(struct dentry *dentry,
+@@ -369,7 +369,7 @@ static int proc_readfdinfo(struct file *
+ const struct inode_operations proc_fdinfo_inode_operations = {
+       .lookup         = proc_lookupfdinfo,
+       .permission     = proc_fdinfo_permission,
+-      .setattr        = proc_setattr,
++      .setattr        = proc_nochmod_setattr,
+ };
+ const struct file_operations proc_fdinfo_operations = {
+--- a/fs/proc/internal.h
++++ b/fs/proc/internal.h
+@@ -178,8 +178,8 @@ extern int proc_pid_statm(struct seq_fil
+ extern const struct dentry_operations pid_dentry_operations;
+ extern int pid_getattr(struct user_namespace *, const struct path *,
+                      struct kstat *, u32, unsigned int);
+-extern int proc_setattr(struct user_namespace *, struct dentry *,
+-                      struct iattr *);
++int proc_nochmod_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
++                      struct iattr *attr);
+ extern void proc_pid_evict_inode(struct proc_inode *);
+ extern struct inode *proc_pid_make_inode(struct super_block *, struct task_struct *, umode_t);
+ extern void pid_update_inode(struct task_struct *, struct inode *);
+--- a/fs/proc/namespaces.c
++++ b/fs/proc/namespaces.c
+@@ -92,7 +92,7 @@ static int proc_ns_readlink(struct dentr
+ static const struct inode_operations proc_ns_link_inode_operations = {
+       .readlink       = proc_ns_readlink,
+       .get_link       = proc_ns_get_link,
+-      .setattr        = proc_setattr,
++      .setattr        = proc_nochmod_setattr,
+ };
+ static struct dentry *proc_ns_instantiate(struct dentry *dentry,
+@@ -179,5 +179,5 @@ out_no_task:
+ const struct inode_operations proc_ns_dir_inode_operations = {
+       .lookup         = proc_ns_dir_lookup,
+       .getattr        = pid_getattr,
+-      .setattr        = proc_setattr,
++      .setattr        = proc_nochmod_setattr,
+ };
+--- a/fs/proc/proc_net.c
++++ b/fs/proc/proc_net.c
+@@ -311,7 +311,7 @@ static int proc_tgid_net_getattr(struct
+ const struct inode_operations proc_net_inode_operations = {
+       .lookup         = proc_tgid_net_lookup,
+       .getattr        = proc_tgid_net_getattr,
+-      .setattr        = proc_setattr,
++      .setattr        = proc_nochmod_setattr,
+ };
+ static int proc_tgid_net_readdir(struct file *file, struct dir_context *ctx)
diff --git a/queue-5.15/proc-use-generic-setattr-for-proc-pid-net.patch b/queue-5.15/proc-use-generic-setattr-for-proc-pid-net.patch
new file mode 100644 (file)
index 0000000..0e03dda
--- /dev/null
@@ -0,0 +1,46 @@
+From stable+bounces-277054-greg=kroah.com@vger.kernel.org Fri Jul 17 16:00:23 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 17 Jul 2026 10:00:13 -0400
+Subject: proc: use generic setattr() for /proc/$PID/net
+To: stable@vger.kernel.org
+Cc: "Thomas Weißschuh" <linux@weissschuh.net>, "Zhangjin Wu" <falcon@tinylab.org>, "Willy Tarreau" <w@1wt.eu>, "Christian Brauner" <brauner@kernel.org>, "Sasha Levin" <sashal@kernel.org>
+Message-ID: <20260717140016.1717138-1-sashal@kernel.org>
+
+From: Thomas Weißschuh <linux@weissschuh.net>
+
+[ Upstream commit 18e66ae67673f2c8df6f02428798b1355691f2a9 ]
+
+All other files in /proc/$PID/ use proc_setattr().
+
+Not using it allows the usage of chmod() on /proc/$PID/net, even on
+other processes owned by the same user.
+The same would probably also be true for other attributes to be changed.
+
+As this technically represents an ABI change it is not marked for
+stable so any unlikely regressions are caught during a full release cycle.
+
+Fixes: e9720acd728a ("[NET]: Make /proc/net a symlink on /proc/self/net (v3)")
+Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
+Link: https://lore.kernel.org/lkml/d0d111ef-edae-4760-83fb-36db84278da1@t-8ch.de/
+Fixes: b4844fa0bdb4 ("selftests/nolibc: implement a few tests for various syscalls")
+Tested-by: Zhangjin Wu <falcon@tinylab.org>
+Signed-off-by: Willy Tarreau <w@1wt.eu>
+Message-Id: <20230624-proc-net-setattr-v1-2-73176812adee@weissschuh.net>
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Stable-dep-of: 6255da28d4bb ("proc: protect ptrace_may_access() with exec_update_lock (FD links)")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/proc/proc_net.c |    1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/fs/proc/proc_net.c
++++ b/fs/proc/proc_net.c
+@@ -311,6 +311,7 @@ static int proc_tgid_net_getattr(struct
+ const struct inode_operations proc_net_inode_operations = {
+       .lookup         = proc_tgid_net_lookup,
+       .getattr        = proc_tgid_net_getattr,
++      .setattr        = proc_setattr,
+ };
+ static int proc_tgid_net_readdir(struct file *file, struct dir_context *ctx)
diff --git a/queue-5.15/regulator-scmi-fix-of_node-refcount-leak-in-scmi_regulator_probe.patch b/queue-5.15/regulator-scmi-fix-of_node-refcount-leak-in-scmi_regulator_probe.patch
new file mode 100644 (file)
index 0000000..7013f8d
--- /dev/null
@@ -0,0 +1,42 @@
+From stable+bounces-278566-greg=kroah.com@vger.kernel.org Tue Jul 21 13:51:56 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 21 Jul 2026 07:47:38 -0400
+Subject: regulator: scmi: fix of_node refcount leak in scmi_regulator_probe()
+To: stable@vger.kernel.org
+Cc: Wentao Liang <vulab@iscas.ac.cn>, Mark Brown <broonie@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260721114738.3689493-1-sashal@kernel.org>
+
+From: Wentao Liang <vulab@iscas.ac.cn>
+
+[ Upstream commit fa11039d6cdff84584a3ef8cc1f5e1b56e045da2 ]
+
+scmi_regulator_probe() calls of_find_node_by_name() which takes a
+reference on the returned device node. On the error path where
+process_scmi_regulator_of_node() fails, the function returns without
+calling of_node_put() on the child node, leaking the reference.
+
+Add of_node_put(np) on the error path to properly release the
+reference.
+
+Cc: stable@vger.kernel.org
+Fixes: 0fbeae70ee7c ("regulator: add SCMI driver")
+Signed-off-by: Wentao Liang <vulab@iscas.ac.cn>
+Link: https://patch.msgid.link/20260527104850.872415-1-vulab@iscas.ac.cn
+Signed-off-by: Mark Brown <broonie@kernel.org>
+[ kept the existing of_node_put(child) since 5.15 still uses the non-scoped for_each_child_of_node() loop, only adding the new of_node_put(np) line ]
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/regulator/scmi-regulator.c |    1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/regulator/scmi-regulator.c
++++ b/drivers/regulator/scmi-regulator.c
+@@ -350,6 +350,7 @@ static int scmi_regulator_probe(struct s
+               /* abort on any mem issue */
+               if (ret == -ENOMEM) {
+                       of_node_put(child);
++                      of_node_put(np);
+                       return ret;
+               }
+       }
diff --git a/queue-5.15/serial-8250_mid-disable-dma-for-selected-platforms.patch b/queue-5.15/serial-8250_mid-disable-dma-for-selected-platforms.patch
new file mode 100644 (file)
index 0000000..9326ca1
--- /dev/null
@@ -0,0 +1,64 @@
+From stable+bounces-277231-greg=kroah.com@vger.kernel.org Sat Jul 18 03:00:13 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 17 Jul 2026 20:59:58 -0400
+Subject: serial: 8250_mid: Disable DMA for selected platforms
+To: stable@vger.kernel.org
+Cc: Andy Shevchenko <andriy.shevchenko@linux.intel.com>, micas-opensource <zjianan156@gmail.com>, stable <stable@kernel.org>, Greg Kroah-Hartman <gregkh@linuxfoundation.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260718005958.2505769-2-sashal@kernel.org>
+
+From: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
+
+[ Upstream commit b1b4efea05a56c0995e4702a86d6624b4fdff32f ]
+
+In accordance with Errata (specification updates)
+HSUART May Stop Functioning when DMA is Active.
+
+- Denverton document #572409, rev 3.4, DNV60
+- Ice Lake Xeon D document #714070, ICXD65
+- Snowridge document #731931, SNR44
+
+For a quick fix just disable the respective callbacks during the device probe.
+Depending on the future development we might remove them completely.
+
+Reported-by: micas-opensource <zjianan156@gmail.com>
+Closes: https://lore.kernel.org/linux-serial/20250625031409.2404219-1-opensource@ruijie.com.cn/
+Fixes: 6ede6dcd87aa ("serial: 8250_mid: add support for DMA engine handling from UART MMIO")
+Cc: stable <stable@kernel.org>
+Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
+Link: https://patch.msgid.link/20260626094937.561776-1-andriy.shevchenko@linux.intel.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/8250/8250_mid.c |   13 +++++++++++--
+ 1 file changed, 11 insertions(+), 2 deletions(-)
+
+--- a/drivers/tty/serial/8250/8250_mid.c
++++ b/drivers/tty/serial/8250/8250_mid.c
+@@ -10,6 +10,7 @@
+ #include <linux/module.h>
+ #include <linux/pci.h>
+ #include <linux/rational.h>
++#include <linux/util_macros.h>
+ #include <linux/dma/hsu.h>
+@@ -371,8 +372,16 @@ static const struct mid8250_board dnv_bo
+       .freq = 133333333,
+       .base_baud = 115200,
+       .bar = 1,
+-      .setup = dnv_setup,
+-      .exit = dnv_exit,
++      /*
++       * Errata:
++       * HSUART May Stop Functioning when DMA is Active.
++       *
++       * - Denverton document #572409, rev 3.4, DNV60
++       * - Ice Lake Xeon D document #714070, ICXD65
++       * - Snowridge document #731931, SNR44
++       */
++      .setup = PTR_IF(false, dnv_setup),
++      .exit = PTR_IF(false, dnv_exit),
+ };
+ #define MID_DEVICE(id, board) { PCI_VDEVICE(INTEL, id), (kernel_ulong_t)&board }
diff --git a/queue-5.15/serial-8250_mid-remove-8250_pci-usage.patch b/queue-5.15/serial-8250_mid-remove-8250_pci-usage.patch
new file mode 100644 (file)
index 0000000..b433c5c
--- /dev/null
@@ -0,0 +1,120 @@
+From stable+bounces-277230-greg=kroah.com@vger.kernel.org Sat Jul 18 03:00:15 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 17 Jul 2026 20:59:57 -0400
+Subject: serial: 8250_mid: Remove 8250_pci usage
+To: stable@vger.kernel.org
+Cc: "Ilpo Järvinen" <ilpo.jarvinen@linux.intel.com>, "Greg Kroah-Hartman" <gregkh@linuxfoundation.org>, "Sasha Levin" <sashal@kernel.org>
+Message-ID: <20260718005958.2505769-1-sashal@kernel.org>
+
+From: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+
+[ Upstream commit a136abd7e7abe0f1247f8ffde6cc7c8ab09f985b ]
+
+8250_mid uses FL_*BASE* from linux/8250_pci.h and nothing else. The
+code can be simplified by directly defining BARs within the driver
+instead.
+
+Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Link: https://lore.kernel.org/r/20230915094336.13278-1-ilpo.jarvinen@linux.intel.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Stable-dep-of: b1b4efea05a5 ("serial: 8250_mid: Disable DMA for selected platforms")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/tty/serial/8250/8250_mid.c |   18 +++++++-----------
+ 1 file changed, 7 insertions(+), 11 deletions(-)
+
+--- a/drivers/tty/serial/8250/8250_mid.c
++++ b/drivers/tty/serial/8250/8250_mid.c
+@@ -12,7 +12,6 @@
+ #include <linux/rational.h>
+ #include <linux/dma/hsu.h>
+-#include <linux/8250_pci.h>
+ #include "8250.h"
+@@ -32,9 +31,9 @@
+ struct mid8250;
+ struct mid8250_board {
+-      unsigned int flags;
+       unsigned long freq;
+       unsigned int base_baud;
++      unsigned int bar;
+       int (*setup)(struct mid8250 *, struct uart_port *p);
+       void (*exit)(struct mid8250 *);
+ };
+@@ -169,7 +168,6 @@ static int dnv_setup(struct mid8250 *mid
+ {
+       struct hsu_dma_chip *chip = &mid->dma_chip;
+       struct pci_dev *pdev = to_pci_dev(p->dev);
+-      unsigned int bar = FL_GET_BASE(mid->board->flags);
+       int ret;
+       pci_set_master(pdev);
+@@ -183,7 +181,7 @@ static int dnv_setup(struct mid8250 *mid
+       chip->dev = &pdev->dev;
+       chip->irq = pci_irq_vector(pdev, 0);
+       chip->regs = p->membase;
+-      chip->length = pci_resource_len(pdev, bar);
++      chip->length = pci_resource_len(pdev, mid->board->bar);
+       chip->offset = DNV_DMA_CHAN_OFFSET;
+       /* Falling back to PIO mode if DMA probing fails */
+@@ -292,7 +290,6 @@ static int mid8250_probe(struct pci_dev
+ {
+       struct uart_8250_port uart;
+       struct mid8250 *mid;
+-      unsigned int bar;
+       int ret;
+       ret = pcim_enable_device(pdev);
+@@ -304,7 +301,6 @@ static int mid8250_probe(struct pci_dev
+               return -ENOMEM;
+       mid->board = (struct mid8250_board *)id->driver_data;
+-      bar = FL_GET_BASE(mid->board->flags);
+       memset(&uart, 0, sizeof(struct uart_8250_port));
+@@ -317,8 +313,8 @@ static int mid8250_probe(struct pci_dev
+       uart.port.flags = UPF_SHARE_IRQ | UPF_FIXED_PORT | UPF_FIXED_TYPE;
+       uart.port.set_termios = mid8250_set_termios;
+-      uart.port.mapbase = pci_resource_start(pdev, bar);
+-      uart.port.membase = pcim_iomap(pdev, bar, 0);
++      uart.port.mapbase = pci_resource_start(pdev, mid->board->bar);
++      uart.port.membase = pcim_iomap(pdev, mid->board->bar, 0);
+       if (!uart.port.membase)
+               return -ENOMEM;
+@@ -356,25 +352,25 @@ static void mid8250_remove(struct pci_de
+ }
+ static const struct mid8250_board pnw_board = {
+-      .flags = FL_BASE0,
+       .freq = 50000000,
+       .base_baud = 115200,
++      .bar = 0,
+       .setup = pnw_setup,
+       .exit = pnw_exit,
+ };
+ static const struct mid8250_board tng_board = {
+-      .flags = FL_BASE0,
+       .freq = 38400000,
+       .base_baud = 1843200,
++      .bar = 0,
+       .setup = tng_setup,
+       .exit = tng_exit,
+ };
+ static const struct mid8250_board dnv_board = {
+-      .flags = FL_BASE1,
+       .freq = 133333333,
+       .base_baud = 115200,
++      .bar = 1,
+       .setup = dnv_setup,
+       .exit = dnv_exit,
+ };
index 6aea9df204f971567c6cfc35027ca9f4a31bfaef..7fc3cdb0ce821ed58ab032089fe4f4db59b1a430 100644 (file)
@@ -727,3 +727,107 @@ mmc-vub300-defer-reset-until-cmd_mutex-is-unlocked.patch
 mtd-rawnand-fsl_ifc-return-errors-for-failed-page-reads.patch
 mtd-rawnand-lpc32xx_mlc-fail-dma-transfers-on-timeout.patch
 mtd-rawnand-lpc32xx_slc-fail-dma-transfer-on-completion-timeout.patch
+acpi-cppc-suppress-ubsan-warning-caused-by-field-misuse.patch
+acpi-nfit-core-fix-acpi_nfit_init-error-cleanup.patch
+iio-imu-adis-add-irqf_no_thread-to-non-fifo-trigger-irq.patch
+iio-hid-sensor-rotation-fix-stale-or-zero-output-when-reading-raw-values.patch
+iio-imu-inv_icm42600-make-timestamp-module-chip-independent.patch
+iio-move-inv_icm42600-timestamp-module-in-common.patch
+iio-make-invensense-timestamp-module-generic.patch
+iio-imu-inv_mpu6050-use-the-common-inv_sensors-timestamp-module.patch
+iio-invensense-remove-redundant-initialization-of-variable-period.patch
+iio-invensense-fix-timestamp-glitches-when-switching-frequency.patch
+iio-imu-inv_icm42600-stabilized-timestamp-in-interrupt.patch
+iio-imu-inv_icm42600-fix-timestamping-by-limiting-fifo-reading.patch
+bitops-make-bytes_to_bits-treewide-available.patch
+iio-common-st_sensors-honour-channel-endianness-in-read_axis_data.patch
+alsa-aoa-check-snd_ctl_new1-return-value.patch
+vfio-pci-release-the-vga-arbiter-client-on-register_device-failure.patch
+pci-altera-fix-resource-leaks-on-probe-failure.patch
+cifs-create-a-new-shared-file-holding-smb2-pdu-definitions.patch
+cifs-remove-check-of-list-iterator-against-head-past-the-loop-body.patch
+smb2-small-refactor-in-smb2_check_message.patch
+cifs-remove-unused-server-parameter-from-calc_smb_size.patch
+smb-client-restrict-implied-bcc-exemption-to-responses-without-data-area.patch
+pci-imx6-fix-imx6sx_gpr12_pcie_test_powerdown-handling.patch
+staging-rtl8723bs-fix-indentation-issues.patch
+staging-rtl8723bs-fix-space-issues.patch
+staging-rtl8723bs-fix-heap-buffer-overflow-in-rtw_cfg80211_set_wpa_ie.patch
+pci-controller-use-dev_fwnode-instead-of-of_fwnode_handle.patch
+pci-mediatek-convert-bool-to-single-quirks-entry-and-bitmap.patch
+pci-mediatek-fix-irq-domain-leak-when-port-fails-to-enable.patch
+staging-rtl8723bs-remove-redundant-else-branches.patch
+staging-rtl8723bs-remove-redundant-braces-in-if-statements.patch
+staging-rtl8723bs-core-move-constants-to-right-side-in-comparison.patch
+staging-rtl8723bs-fix-spaces-around-binary-operators.patch
+staging-rtl8723bs-fix-oob-reads-in-rtw_get_sec_ie-rtw_get_wapi_ie-and-rtw_get_wps_attr.patch
+crypto-qat-fix-vf2pf-work-teardown-race-in-adf_disable_sriov.patch
+pci-prevent-resource-tree-corruption-when-bar-resize-fails.patch
+pci-free-saved-list-without-holding-pci_bus_sem.patch
+pci-fix-restoring-bars-on-bar-resize-rollback-path.patch
+pci-add-kerneldoc-for-pci_resize_resource.patch
+pci-move-resizable-bar-code-to-rebar.c.patch
+pci-skip-resizable-bar-restore-on-read-error.patch
+coresight-etb10-restore-atomic_t-for-shared-reading-state.patch
+gpio-sch-use-new-gpio-line-value-setter-callbacks.patch
+gpio-sch-use-raw_spinlock_t-in-the-irq-startup-path.patch
+netfilter-ebtables-use-vmalloc_array-to-improve-code.patch
+netfilter-ebtables-zero-chainstack-array.patch
+bluetooth-l2cap-fix-not-tracking-outstanding-tx-ident.patch
+bluetooth-l2cap-fix-deadlock-in-l2cap_conn_del.patch
+bluetooth-l2cap-cancel-pending_rx_work-before-taking-conn-lock.patch
+smb-client-fix-next-buffer-leak-in-receive_encrypted_standard.patch
+cifs-add-tracing-for-the-cifs_tcon-struct-refcounting.patch
+smb-client-resolve-swn-tcon-from-live-registrations.patch
+smb-client-mask-server-provided-mode-to-07777-in-modefromsid.patch
+smb-client-use-unaligned-reads-in-parse_posix_ctxt.patch
+smb-client-harden-posix-sid-length-parsing.patch
+ksmbd-fix-use-after-free-in-__ksmbd_close_fd-via-durable-scavenger.patch
+ksmbd-fix-mechtoken-leak-when-spnego-decode-fails-after-token-alloc.patch
+ksmbd-destroy-async_ida-in-ksmbd_conn_free.patch
+ksmbd-centralize-ksmbd_conn-final-release-to-plug-transport-leak.patch
+ksmbd-track-the-connection-owning-a-byte-range-lock.patch
+perf-x86-intel-uncore-defer-adl-global-pmon-enable-to-enable_box.patch
+x.509-fix-validation-of-asn.1-certificate-header.patch
+proc-use-generic-setattr-for-proc-pid-net.patch
+proc-move-fdinfo-ptrace_mode_read-check-into-the-inode-.permission-operation.patch
+proc-rename-proc_setattr-to-proc_nochmod_setattr.patch
+proc-protect-ptrace_may_access-with-exec_update_lock-fd-links.patch
+hid-add-haptics-page-defines.patch
+hid-multitouch-fix-out-of-bounds-bit-access-on-mt_io_flags.patch
+proc-protect-ptrace_may_access-with-exec_update_lock-part-1.patch
+treewide-switch-rename-to-timer_delete.patch
+hid-appleir-fix-uaf-on-pending-key_up_timer-in-remove.patch
+serial-8250_mid-remove-8250_pci-usage.patch
+serial-8250_mid-disable-dma-for-selected-platforms.patch
+xfs-remove-redundant-assignment-of-mp.patch
+xfs-remove-dead-code.patch
+xfs-use-null-daddr-for-unset-first-bad-log-block.patch
+hfs-hfsplus-prevent-getting-negative-values-of-offset-length.patch
+hfs-hfsplus-fix-u32-overflow-in-check_and_correct_requested_length.patch
+xdp-introduce-flags-field-in-xdp_buff-xdp_frame.patch
+bpf-reject-fragmented-frames-in-devmap.patch
+bpf-convert-lpm_trie.c-to-rqspinlock.patch
+bpf-consistently-use-bpf_rcu_lock_held-everywhere.patch
+bpf-allow-lpm-map-access-from-sleepable-bpf-programs.patch
+usb-iowarrior-remove-inherent-race-with-minor-number.patch
+drm-i2c-sil164-drop-no-op-remove-function.patch
+leds-lm3697-remove-duplicated-error-reporting-in-.remove.patch
+leds-lm3601x-improve-error-reporting-for-problems-during-.remove.patch
+gpio-pca953x-make-platform-teardown-callback-return-void.patch
+usb-typec-ucsi-ccg-fix-use-after-free-of-ucsi-on-remove.patch
+usb-atm-ueagle-atm-wait-for-pre-firmware-load-in-.disconnect.patch
+usb-typec-tcpm-fix-vdm-type-for-enter-mode-commands.patch
+usb-gadget-f_fs-initialize-reset_work-at-allocation-time.patch
+crypto-atmel-sha204a-mark-of-related-data-as-maybe-unused.patch
+crypto-atmel-drop-explicit-initialization-of-struct-i2c_device_id-driver_data-to-0.patch
+crypto-atmel-sha204a-drop-hwrng-quality-reduction-for-atsha204a.patch
+usb-gadget-f_fs-tie-read_buffer-lifetime-to-ffs_epfile.patch
+btrfs-do-not-trim-a-device-which-is-not-writeable.patch
+crypto-qat-fix-restarting-state-leak-on-allocation-failure.patch
+btrfs-fix-false-io-failure-after-falling-back-to-buffered-write.patch
+crypto-qat-validate-rsa-crt-component-lengths.patch
+btrfs-fix-incorrect-buffered-io-fallback-for-append-direct-writes.patch
+audit-add-audit_log_nf_skb-helper-function.patch
+audit-fix-potential-integer-overflow-in-audit_log_n_hex.patch
+regulator-scmi-fix-of_node-refcount-leak-in-scmi_regulator_probe.patch
diff --git a/queue-5.15/smb-client-fix-next-buffer-leak-in-receive_encrypted_standard.patch b/queue-5.15/smb-client-fix-next-buffer-leak-in-receive_encrypted_standard.patch
new file mode 100644 (file)
index 0000000..91e71af
--- /dev/null
@@ -0,0 +1,56 @@
+From stable+bounces-275245-greg=kroah.com@vger.kernel.org Thu Jul 16 13:21:12 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 16 Jul 2026 07:20:48 -0400
+Subject: smb: client: Fix next buffer leak in receive_encrypted_standard()
+To: stable@vger.kernel.org
+Cc: Haoxiang Li <haoxiang_li2024@163.com>, Steve French <stfrench@microsoft.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260716112048.1732964-2-sashal@kernel.org>
+
+From: Haoxiang Li <haoxiang_li2024@163.com>
+
+[ Upstream commit 1c6267a1d5cf4c73b656f8181b310cbbb3e4767b ]
+
+receive_encrypted_standard() allocates next_buffer before checking
+whether the number of compound PDUs already reached MAX_COMPOUND. If
+the limit check fails, the function returns immediately and the newly
+allocated next_buffer is not assigned to server->smallbuf/server->bigbuf,
+making it leaked.
+
+Move the MAX_COMPOUND check before allocating next_buffer.
+
+Fixes: b24df3e30cbf ("cifs: update receive_encrypted_standard to handle compounded responses")
+Cc: stable@vger.kernel.org
+Signed-off-by: Haoxiang Li <haoxiang_li2024@163.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/cifs/smb2ops.c |   10 ++++++----
+ 1 file changed, 6 insertions(+), 4 deletions(-)
+
+--- a/fs/cifs/smb2ops.c
++++ b/fs/cifs/smb2ops.c
+@@ -5211,6 +5211,12 @@ receive_encrypted_standard(struct TCP_Se
+ one_more:
+       shdr = (struct smb2_hdr *)buf;
+       next_cmd = le32_to_cpu(shdr->NextCommand);
++
++      if (*num_mids >= MAX_COMPOUND) {
++              cifs_server_dbg(VFS, "too many PDUs in compound\n");
++              return -1;
++      }
++
+       if (next_cmd) {
+               if (WARN_ON_ONCE(next_cmd > pdu_length))
+                       return -1;
+@@ -5234,10 +5240,6 @@ one_more:
+               mid_entry->resp_buf_size = server->pdu_size;
+       }
+-      if (*num_mids >= MAX_COMPOUND) {
+-              cifs_server_dbg(VFS, "too many PDUs in compound\n");
+-              return -1;
+-      }
+       bufs[*num_mids] = buf;
+       mids[(*num_mids)++] = mid_entry;
diff --git a/queue-5.15/smb-client-harden-posix-sid-length-parsing.patch b/queue-5.15/smb-client-harden-posix-sid-length-parsing.patch
new file mode 100644 (file)
index 0000000..964925c
--- /dev/null
@@ -0,0 +1,44 @@
+From stable+bounces-276769-greg=kroah.com@vger.kernel.org Thu Jul 16 18:43:42 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 16 Jul 2026 12:04:23 -0400
+Subject: smb: client: harden POSIX SID length parsing
+To: stable@vger.kernel.org
+Cc: Zihan Xi <xizh2024@lzu.edu.cn>, 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>, Steve French <stfrench@microsoft.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260716160423.620093-1-sashal@kernel.org>
+
+From: Zihan Xi <xizh2024@lzu.edu.cn>
+
+[ Upstream commit 7ad2bcf2441430bb2e918fb3ef9a90d775a6e422 ]
+
+posix_info_sid_size() reads sid[1] to obtain the subauthority count,
+but its existing boundary check still accepts buffers with only one
+remaining byte. Require two bytes before reading sid[1] so all client
+paths that reuse the helper reject truncated POSIX SIDs safely.
+
+Fixes: 349e13ad30b4 ("cifs: add smb2 POSIX info level")
+Cc: stable@vger.kernel.org
+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>
+Assisted-by: Codex:gpt-5.4
+Signed-off-by: Zihan Xi <xizh2024@lzu.edu.cn>
+Signed-off-by: Ren Wei <n05ec@lzu.edu.cn>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/cifs/smb2pdu.c |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/fs/cifs/smb2pdu.c
++++ b/fs/cifs/smb2pdu.c
+@@ -4592,7 +4592,7 @@ int posix_info_sid_size(const void *beg,
+       size_t subauth;
+       int total;
+-      if (beg + 1 > end)
++      if (beg + 2 > end)
+               return -1;
+       subauth = *(u8 *)(beg+1);
diff --git a/queue-5.15/smb-client-mask-server-provided-mode-to-07777-in-modefromsid.patch b/queue-5.15/smb-client-mask-server-provided-mode-to-07777-in-modefromsid.patch
new file mode 100644 (file)
index 0000000..7356ea8
--- /dev/null
@@ -0,0 +1,38 @@
+From stable+bounces-276767-greg=kroah.com@vger.kernel.org Thu Jul 16 18:04:21 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 16 Jul 2026 12:04:14 -0400
+Subject: smb: client: mask server-provided mode to 07777 in modefromsid
+To: stable@vger.kernel.org
+Cc: Norbert Manthey <nmanthey@amazon.de>, Steve French <stfrench@microsoft.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260716160414.619549-1-sashal@kernel.org>
+
+From: Norbert Manthey <nmanthey@amazon.de>
+
+[ Upstream commit e3d9c7160d483fc8f9e225aafad8ecbbc43f3151 ]
+
+When modefromsid is active, parse_dacl() applies the server-provided
+sub_auth[2] value from the NFS mode SID to cf_mode without masking to
+07777. Apply the correct masking, same as in the read path.
+
+Fixes: e2f8fbfb8d09c ("cifs: get mode bits from special sid on stat")
+Signed-off-by: Norbert Manthey <nmanthey@amazon.de>
+Assisted-by: Kiro:claude-opus-4.6
+Cc: stable@vger.kernel.org
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/cifs/cifsacl.c |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/fs/cifs/cifsacl.c
++++ b/fs/cifs/cifsacl.c
+@@ -887,7 +887,7 @@ static void parse_dacl(struct cifs_acl *
+                                */
+                               fattr->cf_mode &= ~07777;
+                               fattr->cf_mode |=
+-                                      le32_to_cpu(ppace[i]->sid.sub_auth[2]);
++                                      le32_to_cpu(ppace[i]->sid.sub_auth[2]) & 07777;
+                               break;
+                       } else {
+                               if (compare_sids(&(ppace[i]->sid), pownersid) == 0) {
diff --git a/queue-5.15/smb-client-resolve-swn-tcon-from-live-registrations.patch b/queue-5.15/smb-client-resolve-swn-tcon-from-live-registrations.patch
new file mode 100644 (file)
index 0000000..843a486
--- /dev/null
@@ -0,0 +1,623 @@
+From stable+bounces-276752-greg=kroah.com@vger.kernel.org Thu Jul 16 18:30:56 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 16 Jul 2026 11:36:44 -0400
+Subject: smb: client: resolve SWN tcon from live registrations
+To: stable@vger.kernel.org
+Cc: Michael Bommarito <michael.bommarito@gmail.com>, Steve French <stfrench@microsoft.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260716153644.582614-2-sashal@kernel.org>
+
+From: Michael Bommarito <michael.bommarito@gmail.com>
+
+cifs_swn_notify() looks up a witness registration by id under
+cifs_swnreg_idr_mutex, drops the mutex, and then uses the registration's
+cached tcon pointer.  That pointer is not a lifetime reference, and it is
+not a stable representative once cifs_get_swn_reg() lets multiple tcons
+for the same net/share name share one registration id.
+
+A same-share second mount can keep the cifs_swn_reg alive after the first
+tcon unregisters and is freed.  The registration then still points at the
+freed first tcon, so taking tc_lock or incrementing tc_count through
+swnreg->tcon only moves the use-after-free earlier.  Taking tc_lock while
+holding cifs_swnreg_idr_mutex also violates the documented CIFS lock
+order.
+
+Fix this by making the registration store only the stable witness
+identity: id, net name, share name, and notify flags.  When a notify
+arrives, copy that identity under cifs_swnreg_idr_mutex, drop the mutex,
+then find and pin a live witness tcon that currently matches the net/share
+pair under the normal cifs_tcp_ses_lock -> tc_lock order.  The notification
+path uses that pinned tcon directly and drops the reference when done.
+
+Registration and unregister messages now use the live tcon passed by the
+caller instead of a cached tcon in the registration.  The final unregister
+send is folded into cifs_swn_unregister() while the registration is still
+protected by cifs_swnreg_idr_mutex.  This removes the previous
+find/drop/reacquire raw-pointer window.  The release path only removes the
+idr entry and frees the stable identity strings.
+
+This preserves the intended one-registration/many-tcon behavior: a
+registration id represents a net/share pair, and notify handling acts on a
+live representative selected at use time.  It also preserves CLIENT_MOVE
+ordering for the representative tcon because the old-IP unregister is sent
+before cifs_swn_register() sends the new-IP register.
+
+Fixes: fed979a7e082 ("cifs: Set witness notification handler for messages from userspace daemon")
+Cc: stable@vger.kernel.org
+Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
+Assisted-by: Claude:claude-opus-4-7
+Signed-off-by: Steve French <stfrench@microsoft.com>
+(cherry picked from commit ec457f9afe5ae9538bdcd58fd4cb442b9787e183)
+
+[ sashal: 5.15 adaptations: cifs_swn_resource_state_changed() keeps the
+  per-channel GlobalMid_Lock tcpStatus reconnect loop (5.15 lacks
+  cifs_signal_cifsd_for_reconnect(), v5.17), switched from swnreg->tcon
+  to the tcon argument; cifs_swn_get_tcon() drops the tc_lock pair and
+  checks tcon->tidStatus == CifsExiting instead of
+  tcon->status == TID_EXITING -- 5.15 has no tc_lock, tc_count and
+  tidStatus are protected by the already-held cifs_tcp_ses_lock;
+  tcon->tree_name is tcon->treeName in 5.15; trace.h hunks applied to
+  fs/cifs/trace.h (pre fs/smb/client move) ]
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/cifs/cifs_swn.c |  318 +++++++++++++++++++++++++++++++++++++++++++----------
+ fs/cifs/trace.h    |    2 
+ 2 files changed, 262 insertions(+), 58 deletions(-)
+
+--- a/fs/cifs/cifs_swn.c
++++ b/fs/cifs/cifs_swn.c
+@@ -28,10 +28,54 @@ struct cifs_swn_reg {
+       bool net_name_notify;
+       bool share_name_notify;
+       bool ip_notify;
++};
+-      struct cifs_tcon *tcon;
++struct cifs_swn_reg_info {
++      int id;
++      unsigned int ref_count;
++      const char *net_name;
++      const char *share_name;
++      bool net_name_notify;
++      bool share_name_notify;
++      bool ip_notify;
+ };
++static void cifs_swn_snapshot_reg(struct cifs_swn_reg *swnreg,
++                                struct cifs_swn_reg_info *info)
++{
++      info->id = swnreg->id;
++      info->ref_count = kref_read(&swnreg->ref_count);
++      info->net_name = swnreg->net_name;
++      info->share_name = swnreg->share_name;
++      info->net_name_notify = swnreg->net_name_notify;
++      info->share_name_notify = swnreg->share_name_notify;
++      info->ip_notify = swnreg->ip_notify;
++}
++
++static int cifs_swn_dup_reg(struct cifs_swn_reg *swnreg,
++                          struct cifs_swn_reg_info *info)
++{
++      cifs_swn_snapshot_reg(swnreg, info);
++
++      info->net_name = kstrdup(swnreg->net_name, GFP_KERNEL);
++      if (!info->net_name)
++              return -ENOMEM;
++
++      info->share_name = kstrdup(swnreg->share_name, GFP_KERNEL);
++      if (!info->share_name) {
++              kfree(info->net_name);
++              return -ENOMEM;
++      }
++
++      return 0;
++}
++
++static void cifs_swn_free_reg_info(struct cifs_swn_reg_info *info)
++{
++      kfree(info->net_name);
++      kfree(info->share_name);
++}
++
+ static int cifs_swn_auth_info_krb(struct cifs_tcon *tcon, struct sk_buff *skb)
+ {
+       int ret;
+@@ -73,7 +117,8 @@ static int cifs_swn_auth_info_ntlm(struc
+  * The authentication information to connect to the witness service is bundled
+  * into the message.
+  */
+-static int cifs_swn_send_register_message(struct cifs_swn_reg *swnreg)
++static int cifs_swn_send_register_message(struct cifs_swn_reg_info *swnreg,
++                                        struct cifs_tcon *tcon)
+ {
+       struct sk_buff *skb;
+       struct genlmsghdr *hdr;
+@@ -111,10 +156,10 @@ static int cifs_swn_send_register_messag
+        * told to switch to it (client move message). In these cases we unregister from the
+        * server address and register to the new address when we receive the notification.
+        */
+-      if (swnreg->tcon->ses->server->use_swn_dstaddr)
+-              addr = &swnreg->tcon->ses->server->swn_dstaddr;
++      if (tcon->ses->server->use_swn_dstaddr)
++              addr = &tcon->ses->server->swn_dstaddr;
+       else
+-              addr = &swnreg->tcon->ses->server->dstaddr;
++              addr = &tcon->ses->server->dstaddr;
+       ret = nla_put(skb, CIFS_GENL_ATTR_SWN_IP, sizeof(struct sockaddr_storage), addr);
+       if (ret < 0)
+@@ -138,10 +183,10 @@ static int cifs_swn_send_register_messag
+                       goto nlmsg_fail;
+       }
+-      authtype = cifs_select_sectype(swnreg->tcon->ses->server, swnreg->tcon->ses->sectype);
++      authtype = cifs_select_sectype(tcon->ses->server, tcon->ses->sectype);
+       switch (authtype) {
+       case Kerberos:
+-              ret = cifs_swn_auth_info_krb(swnreg->tcon, skb);
++              ret = cifs_swn_auth_info_krb(tcon, skb);
+               if (ret < 0) {
+                       cifs_dbg(VFS, "%s: Failed to get kerberos auth info: %d\n", __func__, ret);
+                       goto nlmsg_fail;
+@@ -149,7 +194,7 @@ static int cifs_swn_send_register_messag
+               break;
+       case NTLMv2:
+       case RawNTLMSSP:
+-              ret = cifs_swn_auth_info_ntlm(swnreg->tcon, skb);
++              ret = cifs_swn_auth_info_ntlm(tcon, skb);
+               if (ret < 0) {
+                       cifs_dbg(VFS, "%s: Failed to get NTLM auth info: %d\n", __func__, ret);
+                       goto nlmsg_fail;
+@@ -179,7 +224,8 @@ fail:
+ /*
+  * Sends an uregister message to the userspace daemon based on the registration
+  */
+-static int cifs_swn_send_unregister_message(struct cifs_swn_reg *swnreg)
++static int cifs_swn_send_unregister_message(struct cifs_swn_reg_info *swnreg,
++                                          struct cifs_tcon *tcon)
+ {
+       struct sk_buff *skb;
+       struct genlmsghdr *hdr;
+@@ -208,7 +254,7 @@ static int cifs_swn_send_unregister_mess
+               goto nlmsg_fail;
+       ret = nla_put(skb, CIFS_GENL_ATTR_SWN_IP, sizeof(struct sockaddr_storage),
+-                      &swnreg->tcon->ses->server->dstaddr);
++                      &tcon->ses->server->dstaddr);
+       if (ret < 0)
+               goto nlmsg_fail;
+@@ -245,6 +291,84 @@ nlmsg_fail:
+ }
+ /*
++ * Allocation-free mirror of extract_hostname() + extract_sharename() from
++ * fs/cifs/unc.c.  Those helpers kmalloc(GFP_KERNEL); this runs under the
++ * cifs_tcp_ses_lock spinlock, so we mirror their parsing in place against
++ * the caller's stable net_name/share_name strings.
++ * Keep in sync with unc.c.
++ */
++static bool cifs_swn_tcon_matches(struct cifs_tcon *tcon,
++                                const char *net_name,
++                                const char *share_name)
++{
++      const char *unc = tcon->treeName;
++      const char *host, *share, *delim;
++      size_t host_len, share_len;
++
++      if (!tcon->use_witness)
++              return false;
++
++      /* extract_hostname: require strlen(unc) >= 3 */
++      if (strnlen(unc, 3) < 3)
++              return false;
++      /* extract_hostname: skip all leading '\' characters */
++      for (host = unc; *host == '\\'; host++)
++              ;
++      if (!*host)
++              return false;
++      delim = strchr(host, '\\');
++      if (!delim)
++              return false;
++      host_len = delim - host;
++      if (strlen(net_name) != host_len ||
++          strncasecmp(host, net_name, host_len))
++              return false;
++
++      /* extract_sharename: start at unc + 2, then first '\' onward */
++      share = unc + 2;
++      delim = strchr(share, '\\');
++      if (!delim)
++              return false;
++      share = delim + 1;
++      share_len = strlen(share);
++
++      return strlen(share_name) == share_len &&
++             !strncasecmp(share, share_name, share_len);
++}
++
++/*
++ * One SWN registration id represents one net/share name pair.  Multiple
++ * mounted tcons can therefore share the id.  Pick a live representative at
++ * use time instead of caching the first tcon pointer in the registration.
++ */
++static struct cifs_tcon *cifs_swn_get_tcon(struct cifs_swn_reg_info *swnreg)
++{
++      struct TCP_Server_Info *server;
++      struct cifs_ses *ses;
++      struct cifs_tcon *tcon;
++
++      spin_lock(&cifs_tcp_ses_lock);
++      list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) {
++              list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
++                      list_for_each_entry(tcon, &ses->tcon_list, tcon_list) {
++                              if (tcon->tidStatus == CifsExiting ||
++                                  !cifs_swn_tcon_matches(tcon, swnreg->net_name,
++                                                         swnreg->share_name))
++                                      continue;
++                              ++tcon->tc_count;
++                              trace_smb3_tcon_ref(tcon->debug_id,
++                                                  tcon->tc_count,
++                                                  netfs_trace_tcon_ref_get_swn_notify);
++                              spin_unlock(&cifs_tcp_ses_lock);
++                              return tcon;
++                      }
++              }
++      }
++      spin_unlock(&cifs_tcp_ses_lock);
++      return NULL;
++}
++
++/*
+  * Try to find a matching registration for the tcon's server name and share name.
+  * Calls to this function must be protected by cifs_swnreg_idr_mutex.
+  * TODO Try to avoid memory allocations
+@@ -350,8 +474,6 @@ static struct cifs_swn_reg *cifs_get_swn
+       reg->net_name_notify = true;
+       reg->share_name_notify = true;
+       reg->ip_notify = (tcon->capabilities & SMB2_SHARE_CAP_SCALEOUT);
+-
+-      reg->tcon = tcon;
+ unlock:
+       mutex_unlock(&cifs_swnreg_idr_mutex);
+@@ -371,11 +493,6 @@ fail_unlock:
+ static void cifs_swn_reg_release(struct kref *ref)
+ {
+       struct cifs_swn_reg *swnreg = container_of(ref, struct cifs_swn_reg, ref_count);
+-      int ret;
+-
+-      ret = cifs_swn_send_unregister_message(swnreg);
+-      if (ret < 0)
+-              cifs_dbg(VFS, "%s: Failed to send unregister message: %d\n", __func__, ret);
+       idr_remove(&cifs_swnreg_idr, swnreg->id);
+       kfree(swnreg->net_name);
+@@ -383,33 +500,43 @@ static void cifs_swn_reg_release(struct
+       kfree(swnreg);
+ }
+-static void cifs_put_swn_reg(struct cifs_swn_reg *swnreg)
++static void cifs_put_swn_reg_locked(struct cifs_swn_reg *swnreg,
++                                  struct cifs_tcon *tcon)
+ {
+-      mutex_lock(&cifs_swnreg_idr_mutex);
++      if (kref_read(&swnreg->ref_count) == 1) {
++              struct cifs_swn_reg_info swnreg_info;
++              int ret;
++
++              cifs_swn_snapshot_reg(swnreg, &swnreg_info);
++              ret = cifs_swn_send_unregister_message(&swnreg_info, tcon);
++              if (ret < 0)
++                      cifs_dbg(VFS, "%s: Failed to send unregister message: %d\n",
++                               __func__, ret);
++      }
++
+       kref_put(&swnreg->ref_count, cifs_swn_reg_release);
+-      mutex_unlock(&cifs_swnreg_idr_mutex);
+ }
+-static int cifs_swn_resource_state_changed(struct cifs_swn_reg *swnreg, const char *name, int state)
++static int cifs_swn_resource_state_changed(struct cifs_tcon *tcon, const char *name, int state)
+ {
+       int i;
+       switch (state) {
+       case CIFS_SWN_RESOURCE_STATE_UNAVAILABLE:
+               cifs_dbg(FYI, "%s: resource name '%s' become unavailable\n", __func__, name);
+-              for (i = 0; i < swnreg->tcon->ses->chan_count; i++) {
++              for (i = 0; i < tcon->ses->chan_count; i++) {
+                       spin_lock(&GlobalMid_Lock);
+-                      if (swnreg->tcon->ses->chans[i].server->tcpStatus != CifsExiting)
+-                              swnreg->tcon->ses->chans[i].server->tcpStatus = CifsNeedReconnect;
++                      if (tcon->ses->chans[i].server->tcpStatus != CifsExiting)
++                              tcon->ses->chans[i].server->tcpStatus = CifsNeedReconnect;
+                       spin_unlock(&GlobalMid_Lock);
+               }
+               break;
+       case CIFS_SWN_RESOURCE_STATE_AVAILABLE:
+               cifs_dbg(FYI, "%s: resource name '%s' become available\n", __func__, name);
+-              for (i = 0; i < swnreg->tcon->ses->chan_count; i++) {
++              for (i = 0; i < tcon->ses->chan_count; i++) {
+                       spin_lock(&GlobalMid_Lock);
+-                      if (swnreg->tcon->ses->chans[i].server->tcpStatus != CifsExiting)
+-                              swnreg->tcon->ses->chans[i].server->tcpStatus = CifsNeedReconnect;
++                      if (tcon->ses->chans[i].server->tcpStatus != CifsExiting)
++                              tcon->ses->chans[i].server->tcpStatus = CifsNeedReconnect;
+                       spin_unlock(&GlobalMid_Lock);
+               }
+               break;
+@@ -520,7 +647,7 @@ unlock:
+       return ret;
+ }
+-static int cifs_swn_client_move(struct cifs_swn_reg *swnreg, struct sockaddr_storage *addr)
++static int cifs_swn_client_move(struct cifs_tcon *tcon, struct sockaddr_storage *addr)
+ {
+       struct sockaddr_in *ipv4 = (struct sockaddr_in *)addr;
+       struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)addr;
+@@ -530,14 +657,17 @@ static int cifs_swn_client_move(struct c
+       else if (addr->ss_family == AF_INET6)
+               cifs_dbg(FYI, "%s: move to %pI6\n", __func__, &ipv6->sin6_addr);
+-      return cifs_swn_reconnect(swnreg->tcon, addr);
++      return cifs_swn_reconnect(tcon, addr);
+ }
+ int cifs_swn_notify(struct sk_buff *skb, struct genl_info *info)
+ {
+       struct cifs_swn_reg *swnreg;
++      struct cifs_swn_reg_info swnreg_info;
++      struct cifs_tcon *tcon;
+       char name[256];
+       int type;
++      int ret = 0;
+       if (info->attrs[CIFS_GENL_ATTR_SWN_REGISTRATION_ID]) {
+               int swnreg_id;
+@@ -545,21 +675,34 @@ int cifs_swn_notify(struct sk_buff *skb,
+               swnreg_id = nla_get_u32(info->attrs[CIFS_GENL_ATTR_SWN_REGISTRATION_ID]);
+               mutex_lock(&cifs_swnreg_idr_mutex);
+               swnreg = idr_find(&cifs_swnreg_idr, swnreg_id);
+-              mutex_unlock(&cifs_swnreg_idr_mutex);
+               if (swnreg == NULL) {
++                      mutex_unlock(&cifs_swnreg_idr_mutex);
+                       cifs_dbg(FYI, "%s: registration id %d not found\n", __func__, swnreg_id);
+                       return -EINVAL;
+               }
++              ret = cifs_swn_dup_reg(swnreg, &swnreg_info);
++              mutex_unlock(&cifs_swnreg_idr_mutex);
++              if (ret)
++                      return ret;
+       } else {
+               cifs_dbg(FYI, "%s: missing registration id attribute\n", __func__);
+               return -EINVAL;
+       }
++      tcon = cifs_swn_get_tcon(&swnreg_info);
++      if (!tcon) {
++              cifs_dbg(FYI, "%s: registration id %d has no live tcon\n",
++                       __func__, swnreg_info.id);
++              ret = -ENODEV;
++              goto free_info;
++      }
++
+       if (info->attrs[CIFS_GENL_ATTR_SWN_NOTIFICATION_TYPE]) {
+               type = nla_get_u32(info->attrs[CIFS_GENL_ATTR_SWN_NOTIFICATION_TYPE]);
+       } else {
+               cifs_dbg(FYI, "%s: missing notification type attribute\n", __func__);
+-              return -EINVAL;
++              ret = -EINVAL;
++              goto out;
+       }
+       switch (type) {
+@@ -571,15 +714,18 @@ int cifs_swn_notify(struct sk_buff *skb,
+                                       sizeof(name));
+               } else {
+                       cifs_dbg(FYI, "%s: missing resource name attribute\n", __func__);
+-                      return -EINVAL;
++                      ret = -EINVAL;
++                      goto out;
+               }
+               if (info->attrs[CIFS_GENL_ATTR_SWN_RESOURCE_STATE]) {
+                       state = nla_get_u32(info->attrs[CIFS_GENL_ATTR_SWN_RESOURCE_STATE]);
+               } else {
+                       cifs_dbg(FYI, "%s: missing resource state attribute\n", __func__);
+-                      return -EINVAL;
++                      ret = -EINVAL;
++                      goto out;
+               }
+-              return cifs_swn_resource_state_changed(swnreg, name, state);
++              ret = cifs_swn_resource_state_changed(tcon, name, state);
++              break;
+       }
+       case CIFS_SWN_NOTIFICATION_CLIENT_MOVE: {
+               struct sockaddr_storage addr;
+@@ -588,28 +734,36 @@ int cifs_swn_notify(struct sk_buff *skb,
+                       nla_memcpy(&addr, info->attrs[CIFS_GENL_ATTR_SWN_IP], sizeof(addr));
+               } else {
+                       cifs_dbg(FYI, "%s: missing IP address attribute\n", __func__);
+-                      return -EINVAL;
++                      ret = -EINVAL;
++                      goto out;
+               }
+-              return cifs_swn_client_move(swnreg, &addr);
++              ret = cifs_swn_client_move(tcon, &addr);
++              break;
+       }
+       default:
+               cifs_dbg(FYI, "%s: unknown notification type %d\n", __func__, type);
+               break;
+       }
+-      return 0;
++out:
++      cifs_put_tcon(tcon, netfs_trace_tcon_ref_put_swn_notify);
++free_info:
++      cifs_swn_free_reg_info(&swnreg_info);
++      return ret;
+ }
+ int cifs_swn_register(struct cifs_tcon *tcon)
+ {
+       struct cifs_swn_reg *swnreg;
++      struct cifs_swn_reg_info swnreg_info;
+       int ret;
+       swnreg = cifs_get_swn_reg(tcon);
+       if (IS_ERR(swnreg))
+               return PTR_ERR(swnreg);
+-      ret = cifs_swn_send_register_message(swnreg);
++      cifs_swn_snapshot_reg(swnreg, &swnreg_info);
++      ret = cifs_swn_send_register_message(&swnreg_info, tcon);
+       if (ret < 0) {
+               cifs_dbg(VFS, "%s: Failed to send swn register message: %d\n", __func__, ret);
+               /* Do not put the swnreg or return error, the echo task will retry */
+@@ -630,35 +784,68 @@ int cifs_swn_unregister(struct cifs_tcon
+               return PTR_ERR(swnreg);
+       }
++      cifs_put_swn_reg_locked(swnreg, tcon);
+       mutex_unlock(&cifs_swnreg_idr_mutex);
+-      cifs_put_swn_reg(swnreg);
+-
+       return 0;
+ }
+-void cifs_swn_dump(struct seq_file *m)
++/*
++ * Snapshot one registration under cifs_swnreg_idr_mutex and return.  Callers
++ * intentionally do the per-registration network/genlmsg work without the
++ * mutex held, both to keep the critical section short and to avoid nesting
++ * cifs_swnreg_idr_mutex inside the higher cifs_tcp_ses_lock when a live
++ * tcon is then pinned for the send.
++ */
++static int cifs_swn_get_next_reg_info(int *id, struct cifs_swn_reg_info *info)
+ {
+       struct cifs_swn_reg *swnreg;
++      int ret = 0;
++
++      mutex_lock(&cifs_swnreg_idr_mutex);
++      swnreg = idr_get_next(&cifs_swnreg_idr, id);
++      if (swnreg) {
++              ret = cifs_swn_dup_reg(swnreg, info);
++              if (!ret) {
++                      *id = swnreg->id + 1;
++                      ret = 1;
++              }
++      }
++      mutex_unlock(&cifs_swnreg_idr_mutex);
++
++      return ret;
++}
++
++void cifs_swn_dump(struct seq_file *m)
++{
++      struct cifs_swn_reg_info swnreg_info;
++      struct cifs_tcon *tcon;
+       struct sockaddr_in *sa;
+       struct sockaddr_in6 *sa6;
+-      int id;
++      int id = 0;
++      int ret;
+       seq_puts(m, "Witness registrations:");
+-      mutex_lock(&cifs_swnreg_idr_mutex);
+-      idr_for_each_entry(&cifs_swnreg_idr, swnreg, id) {
++      while ((ret = cifs_swn_get_next_reg_info(&id, &swnreg_info)) > 0) {
+               seq_printf(m, "\nId: %u Refs: %u Network name: '%s'%s Share name: '%s'%s Ip address: ",
+-                              id, kref_read(&swnreg->ref_count),
+-                              swnreg->net_name, swnreg->net_name_notify ? "(y)" : "(n)",
+-                              swnreg->share_name, swnreg->share_name_notify ? "(y)" : "(n)");
+-              switch (swnreg->tcon->ses->server->dstaddr.ss_family) {
++                         swnreg_info.id, swnreg_info.ref_count,
++                         swnreg_info.net_name, swnreg_info.net_name_notify ? "(y)" : "(n)",
++                         swnreg_info.share_name, swnreg_info.share_name_notify ? "(y)" : "(n)");
++
++              tcon = cifs_swn_get_tcon(&swnreg_info);
++              if (!tcon) {
++                      seq_puts(m, "(no live tcon)");
++                      goto next;
++              }
++
++              switch (tcon->ses->server->dstaddr.ss_family) {
+               case AF_INET:
+-                      sa = (struct sockaddr_in *) &swnreg->tcon->ses->server->dstaddr;
++                      sa = (struct sockaddr_in *)&tcon->ses->server->dstaddr;
+                       seq_printf(m, "%pI4", &sa->sin_addr.s_addr);
+                       break;
+               case AF_INET6:
+-                      sa6 = (struct sockaddr_in6 *) &swnreg->tcon->ses->server->dstaddr;
++                      sa6 = (struct sockaddr_in6 *)&tcon->ses->server->dstaddr;
+                       seq_printf(m, "%pI6", &sa6->sin6_addr.s6_addr);
+                       if (sa6->sin6_scope_id)
+                               seq_printf(m, "%%%u", sa6->sin6_scope_id);
+@@ -666,23 +853,38 @@ void cifs_swn_dump(struct seq_file *m)
+               default:
+                       seq_puts(m, "(unknown)");
+               }
+-              seq_printf(m, "%s", swnreg->ip_notify ? "(y)" : "(n)");
++              cifs_put_tcon(tcon, netfs_trace_tcon_ref_put_swn_notify);
++next:
++              seq_printf(m, "%s", swnreg_info.ip_notify ? "(y)" : "(n)");
++              cifs_swn_free_reg_info(&swnreg_info);
+       }
+-      mutex_unlock(&cifs_swnreg_idr_mutex);
++      if (ret < 0)
++              seq_printf(m, "\nFailed to snapshot witness registration: %d", ret);
+       seq_puts(m, "\n");
+ }
+ void cifs_swn_check(void)
+ {
+-      struct cifs_swn_reg *swnreg;
+-      int id;
++      struct cifs_swn_reg_info swnreg_info;
++      struct cifs_tcon *tcon;
++      int id = 0;
+       int ret;
+-      mutex_lock(&cifs_swnreg_idr_mutex);
+-      idr_for_each_entry(&cifs_swnreg_idr, swnreg, id) {
+-              ret = cifs_swn_send_register_message(swnreg);
++      while ((ret = cifs_swn_get_next_reg_info(&id, &swnreg_info)) > 0) {
++              tcon = cifs_swn_get_tcon(&swnreg_info);
++              if (!tcon) {
++                      cifs_dbg(FYI, "%s: registration id %d has no live tcon\n",
++                               __func__, swnreg_info.id);
++                      goto free_info;
++              }
++
++              ret = cifs_swn_send_register_message(&swnreg_info, tcon);
+               if (ret < 0)
+                       cifs_dbg(FYI, "%s: Failed to send register message: %d\n", __func__, ret);
++              cifs_put_tcon(tcon, netfs_trace_tcon_ref_put_swn_notify);
++free_info:
++              cifs_swn_free_reg_info(&swnreg_info);
+       }
+-      mutex_unlock(&cifs_swnreg_idr_mutex);
++      if (ret < 0)
++              cifs_dbg(FYI, "%s: Failed to snapshot registration: %d\n", __func__, ret);
+ }
+--- a/fs/cifs/trace.h
++++ b/fs/cifs/trace.h
+@@ -30,6 +30,7 @@
+       EM(netfs_trace_tcon_ref_get_find,               "GET Find  ") \
+       EM(netfs_trace_tcon_ref_get_find_sess_tcon,     "GET FndSes") \
+       EM(netfs_trace_tcon_ref_get_reconnect_server,   "GET Reconn") \
++      EM(netfs_trace_tcon_ref_get_swn_notify,         "GET SwnNot") \
+       EM(netfs_trace_tcon_ref_new,                    "NEW       ") \
+       EM(netfs_trace_tcon_ref_new_ipc,                "NEW Ipc   ") \
+       EM(netfs_trace_tcon_ref_new_reconnect_server,   "NEW Reconn") \
+@@ -39,6 +40,7 @@
+       EM(netfs_trace_tcon_ref_put_mnt_ctx,            "PUT MntCtx") \
+       EM(netfs_trace_tcon_ref_put_dfs_refer,          "PUT DfsRfr") \
+       EM(netfs_trace_tcon_ref_put_reconnect_server,   "PUT Reconn") \
++      EM(netfs_trace_tcon_ref_put_swn_notify,         "PUT SwnNot") \
+       EM(netfs_trace_tcon_ref_put_tlink,              "PUT Tlink ") \
+       EM(netfs_trace_tcon_ref_see_cancelled_close,    "SEE Cn-Cls") \
+       EM(netfs_trace_tcon_ref_see_fscache_collision,  "SEE FV-CO!") \
diff --git a/queue-5.15/smb-client-restrict-implied-bcc-exemption-to-responses-without-data-area.patch b/queue-5.15/smb-client-restrict-implied-bcc-exemption-to-responses-without-data-area.patch
new file mode 100644 (file)
index 0000000..e772565
--- /dev/null
@@ -0,0 +1,206 @@
+From stable+bounces-274538-greg=kroah.com@vger.kernel.org Tue Jul 14 22:03:20 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 14 Jul 2026 16:02:15 -0400
+Subject: smb: client: restrict implied bcc[0] exemption to responses without data area
+To: stable@vger.kernel.org
+Cc: Shoichiro Miyamoto <shoichiro.miyamoto@gmail.com>, Steve French <stfrench@microsoft.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260714200215.3152449-5-sashal@kernel.org>
+
+From: Shoichiro Miyamoto <shoichiro.miyamoto@gmail.com>
+
+[ Upstream commit 53b7c271f06be4dd5cfc8c6ef552a8355c891a7f ]
+
+smb2_check_message() has a long-standing quirk that accepts a response
+whose calculated length is one byte larger than the bytes actually
+received ("server can return one byte more due to implied bcc[0]").
+This was introduced to accommodate servers that omit the trailing bcc[0]
+overlap byte when no data area is present.
+
+However, the exemption is applied unconditionally, regardless of whether
+the command actually carries a data area (has_smb2_data_area[]).  When a
+response with a data area is subject to the +1 exemption, the reported
+data can extend one byte beyond the bytes actually received, yet
+smb2_check_message() still accepts it.  The subsequent decoder then reads
+past the end of the receive buffer.  This is reachable during NEGOTIATE
+and SESSION_SETUP, before the session is established.
+
+The resulting out-of-bounds reads are visible under KASAN when mounting
+against a non-conforming server; both the SPNEGO/negTokenInit and the
+NTLMSSP challenge decoders are affected:
+
+  BUG: KASAN: slab-out-of-bounds in asn1_ber_decoder+0x16a7/0x1b00
+  Read of size 1 at addr ffff8880084d67c0 by task mount.cifs/81
+  CPU: 1 UID: 0 PID: 81 Comm: mount.cifs Not tainted 7.1.0-rc6 #1
+  Call Trace:
+   <TASK>
+   dump_stack_lvl+0x4e/0x70
+   print_report+0x157/0x4c9
+   kasan_report+0xce/0x100
+   asn1_ber_decoder+0x16a7/0x1b00
+   decode_negTokenInit+0x19/0x30
+   SMB2_negotiate+0x31d9/0x4c90
+   cifs_negotiate_protocol+0x1f2/0x3f0
+   cifs_get_smb_ses+0x93f/0x17e0
+   cifs_mount_get_session+0x7f/0x3a0
+   cifs_mount+0xb4/0xcf0
+   cifs_smb3_do_mount+0x23a/0x1500
+   smb3_get_tree+0x3b0/0x630
+   vfs_get_tree+0x82/0x2d0
+   fc_mount+0x10/0x1b0
+   path_mount+0x50d/0x1de0
+   __x64_sys_mount+0x20b/0x270
+   do_syscall_64+0xee/0x590
+   entry_SYSCALL_64_after_hwframe+0x77/0x7f
+   </TASK>
+  Allocated by task 85:
+   kmem_cache_alloc_noprof+0x106/0x380
+   mempool_alloc_noprof+0x116/0x1e0
+   cifs_small_buf_get+0x31/0x80
+   allocate_buffers+0x10d/0x2b0
+   cifs_demultiplex_thread+0x1d5/0x1d50
+   kthread+0x2c6/0x390
+   ret_from_fork+0x36e/0x5a0
+   ret_from_fork_asm+0x1a/0x30
+  The buggy address is located 0 bytes to the right of
+   allocated 448-byte region [ffff8880084d6600, ffff8880084d67c0)
+   which belongs to the cache cifs_small_rq of size 448
+
+  BUG: KASAN: slab-out-of-bounds in kmemdup_noprof+0x36/0x50
+  Read of size 329 at addr ffff88800726c678 by task mount.cifs/89
+  CPU: 0 UID: 0 PID: 89 Comm: mount.cifs Tainted: G    B      7.1.0-rc6 #1
+  Call Trace:
+   <TASK>
+   dump_stack_lvl+0x4e/0x70
+   print_report+0x157/0x4c9
+   kasan_report+0xce/0x100
+   kasan_check_range+0x10f/0x1e0
+   __asan_memcpy+0x23/0x60
+   kmemdup_noprof+0x36/0x50
+   decode_ntlmssp_challenge+0x457/0x680
+   SMB2_sess_auth_rawntlmssp_negotiate+0x6f0/0xcb0
+   SMB2_sess_setup+0x219/0x4f0
+   cifs_setup_session+0x248/0xaf0
+   cifs_get_smb_ses+0xf79/0x17e0
+   cifs_mount_get_session+0x7f/0x3a0
+   cifs_mount+0xb4/0xcf0
+   cifs_smb3_do_mount+0x23a/0x1500
+   smb3_get_tree+0x3b0/0x630
+   vfs_get_tree+0x82/0x2d0
+   fc_mount+0x10/0x1b0
+   path_mount+0x50d/0x1de0
+   __x64_sys_mount+0x20b/0x270
+   do_syscall_64+0xee/0x590
+   entry_SYSCALL_64_after_hwframe+0x77/0x7f
+   </TASK>
+  Allocated by task 93:
+   kmem_cache_alloc_noprof+0x106/0x380
+   mempool_alloc_noprof+0x116/0x1e0
+   cifs_small_buf_get+0x31/0x80
+   allocate_buffers+0x10d/0x2b0
+   cifs_demultiplex_thread+0x1d5/0x1d50
+   kthread+0x2c6/0x390
+   ret_from_fork+0x36e/0x5a0
+   ret_from_fork_asm+0x1a/0x30
+  The buggy address is located 120 bytes inside of
+   allocated 448-byte region [ffff88800726c600, ffff88800726c7c0)
+   which belongs to the cache cifs_small_rq of size 448
+
+Restrict the +1 exemption to responses that have no data area, so that
+it still covers the bcc[0] omission it was meant for.  When a data area
+is present, the +1 discrepancy instead means the reported data length
+overruns the received buffer, so the response must be rejected.
+
+Fixes: 093b2bdad322 ("CIFS: Make demultiplex_thread work with SMB2 code")
+Cc: stable@vger.kernel.org
+Signed-off-by: Shoichiro Miyamoto <shoichiro.miyamoto@gmail.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/cifs/smb2misc.c |   32 ++++++++++++++++++++++++++------
+ 1 file changed, 26 insertions(+), 6 deletions(-)
+
+--- a/fs/cifs/smb2misc.c
++++ b/fs/cifs/smb2misc.c
+@@ -17,6 +17,8 @@
+ #include "smb2glob.h"
+ #include "nterr.h"
++static unsigned int __smb2_calc_size(void *buf, bool *have_data);
++
+ static int
+ check_smb2_hdr(struct smb2_hdr *shdr, __u64 mid)
+ {
+@@ -141,6 +143,7 @@ smb2_check_message(char *buf, unsigned i
+       int command;
+       __u32 calc_len; /* calculated length */
+       __u64 mid;
++      bool have_data;
+       /*
+        * Add function to do table lookup of StructureSize by command
+@@ -221,7 +224,8 @@ smb2_check_message(char *buf, unsigned i
+               }
+       }
+-      calc_len = smb2_calc_size(buf);
++      have_data = false;
++      calc_len = __smb2_calc_size(buf, &have_data);
+       /* For SMB2_IOCTL, OutputOffset and OutputLength are optional, so might
+        * be 0, and not a real miscalculation */
+@@ -239,8 +243,13 @@ smb2_check_message(char *buf, unsigned i
+               /* Windows 7 server returns 24 bytes more */
+               if (calc_len + 24 == len && command == SMB2_OPLOCK_BREAK_HE)
+                       return 0;
+-              /* server can return one byte more due to implied bcc[0] */
+-              if (calc_len == len + 1)
++              /*
++               * Server can return one byte more due to implied bcc[0].
++               * Allow it only when there is no data area; if data_length > 0
++               * the +1 gap indicates an overreported data length rather than
++               * the bcc[0] omission.
++               */
++              if (calc_len == len + 1 && !have_data)
+                       return 0;
+               /*
+@@ -401,14 +410,17 @@ smb2_get_data_area_len(int *off, int *le
+ /*
+  * Calculate the size of the SMB message based on the fixed header
+  * portion, the number of word parameters and the data portion of the message.
++ * If have_data is non-NULL, it is set to true when a non-empty data area was
++ * found (data_length > 0), allowing callers to distinguish the implied bcc[0]
++ * case (no data area) from an overreported data length.
+  */
+-unsigned int
+-smb2_calc_size(void *buf)
++static unsigned int
++__smb2_calc_size(void *buf, bool *have_data)
+ {
+       struct smb2_pdu *pdu = (struct smb2_pdu *)buf;
+       struct smb2_hdr *shdr = &pdu->hdr;
+       int offset; /* the offset from the beginning of SMB to data area */
+-      int data_length; /* the length of the variable length data area */
++      int data_length = 0; /* the length of the variable length data area */
+       /* Structure Size has already been checked to make sure it is 64 */
+       int len = le16_to_cpu(shdr->StructureSize);
+@@ -441,9 +453,17 @@ smb2_calc_size(void *buf)
+       }
+ calc_size_exit:
+       cifs_dbg(FYI, "SMB2 len %d\n", len);
++      if (have_data)
++              *have_data = (data_length > 0);
+       return len;
+ }
++unsigned int
++smb2_calc_size(void *buf)
++{
++      return __smb2_calc_size(buf, NULL);
++}
++
+ /* Note: caller must free return buffer */
+ __le16 *
+ cifs_convert_path_to_utf16(const char *from, struct cifs_sb_info *cifs_sb)
diff --git a/queue-5.15/smb-client-use-unaligned-reads-in-parse_posix_ctxt.patch b/queue-5.15/smb-client-use-unaligned-reads-in-parse_posix_ctxt.patch
new file mode 100644 (file)
index 0000000..bddaa7c
--- /dev/null
@@ -0,0 +1,42 @@
+From stable+bounces-276768-greg=kroah.com@vger.kernel.org Thu Jul 16 18:43:50 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 16 Jul 2026 12:04:19 -0400
+Subject: smb: client: use unaligned reads in parse_posix_ctxt()
+To: stable@vger.kernel.org
+Cc: Zihan Xi <xizh2024@lzu.edu.cn>, Ren Wei <n05ec@lzu.edu.cn>, Steve French <stfrench@microsoft.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260716160419.619801-1-sashal@kernel.org>
+
+From: Zihan Xi <xizh2024@lzu.edu.cn>
+
+[ Upstream commit b86467cd2691192ad4809a5a6e922fc24b8e9839 ]
+
+The server controls create-context DataOffset, so the POSIX context data
+pointer may be misaligned on strict-alignment architectures. Use
+get_unaligned_le32() when reading nlink, reparse_tag, and mode.
+
+Fixes: 69dda3059e7a ("cifs: add SMB2_open() arg to return POSIX data")
+Cc: stable@vger.kernel.org
+Signed-off-by: Zihan Xi <xizh2024@lzu.edu.cn>
+Signed-off-by: Ren Wei <n05ec@lzu.edu.cn>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/cifs/smb2pdu.c |    6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+--- a/fs/cifs/smb2pdu.c
++++ b/fs/cifs/smb2pdu.c
+@@ -2037,9 +2037,9 @@ parse_posix_ctxt(struct create_context *
+       memset(posix, 0, sizeof(*posix));
+-      posix->nlink = le32_to_cpu(*(__le32 *)(beg + 0));
+-      posix->reparse_tag = le32_to_cpu(*(__le32 *)(beg + 4));
+-      posix->mode = le32_to_cpu(*(__le32 *)(beg + 8));
++      posix->nlink = get_unaligned_le32(beg);
++      posix->reparse_tag = get_unaligned_le32(beg + 4);
++      posix->mode = get_unaligned_le32(beg + 8);
+       sid = beg + 12;
+       sid_len = posix_info_sid_size(sid, end);
diff --git a/queue-5.15/smb2-small-refactor-in-smb2_check_message.patch b/queue-5.15/smb2-small-refactor-in-smb2_check_message.patch
new file mode 100644 (file)
index 0000000..9d6de47
--- /dev/null
@@ -0,0 +1,168 @@
+From stable+bounces-274534-greg=kroah.com@vger.kernel.org Tue Jul 14 22:03:09 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 14 Jul 2026 16:02:13 -0400
+Subject: smb2: small refactor in smb2_check_message()
+To: stable@vger.kernel.org
+Cc: Enzo Matsumiya <ematsumiya@suse.de>, Steve French <stfrench@microsoft.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260714200215.3152449-3-sashal@kernel.org>
+
+From: Enzo Matsumiya <ematsumiya@suse.de>
+
+[ Upstream commit da3847894fddc27ca95d5ac0012f444a77a5e0c3 ]
+
+If the command is SMB2_IOCTL, OutputLength and OutputContext are
+optional and can be zero, so return early and skip calculated length
+check.
+
+Move the mismatched length message to the end of the check, to avoid
+unnecessary logs when the check was not a real miscalculation.
+
+Also change the pr_warn_once() to a pr_warn() so we're sure to get a
+log for the real mismatches.
+
+Signed-off-by: Enzo Matsumiya <ematsumiya@suse.de>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Stable-dep-of: 53b7c271f06b ("smb: client: restrict implied bcc[0] exemption to responses without data area")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/cifs/connect.c  |   13 ++++++-------
+ fs/cifs/smb2misc.c |   47 ++++++++++++++++++++++++++++-------------------
+ 2 files changed, 34 insertions(+), 26 deletions(-)
+
+--- a/fs/cifs/connect.c
++++ b/fs/cifs/connect.c
+@@ -890,19 +890,18 @@ int
+ cifs_handle_standard(struct TCP_Server_Info *server, struct mid_q_entry *mid)
+ {
+       char *buf = server->large_buf ? server->bigbuf : server->smallbuf;
+-      int length;
++      int rc;
+       /*
+        * We know that we received enough to get to the MID as we
+        * checked the pdu_length earlier. Now check to see
+-       * if the rest of the header is OK. We borrow the length
+-       * var for the rest of the loop to avoid a new stack var.
++       * if the rest of the header is OK.
+        *
+        * 48 bytes is enough to display the header and a little bit
+        * into the payload for debugging purposes.
+        */
+-      length = server->ops->check_message(buf, server->total_read, server);
+-      if (length != 0)
++      rc = server->ops->check_message(buf, server->total_read, server);
++      if (rc)
+               cifs_dump_mem("Bad SMB: ", buf,
+                       min_t(unsigned int, server->total_read, 48));
+@@ -917,9 +916,9 @@ cifs_handle_standard(struct TCP_Server_I
+               return -1;
+       if (!mid)
+-              return length;
++              return rc;
+-      handle_mid(mid, server, buf, length);
++      handle_mid(mid, server, buf, rc);
+       return 0;
+ }
+--- a/fs/cifs/smb2misc.c
++++ b/fs/cifs/smb2misc.c
+@@ -132,15 +132,15 @@ static __u32 get_neg_ctxt_len(struct smb
+ }
+ int
+-smb2_check_message(char *buf, unsigned int len, struct TCP_Server_Info *srvr)
++smb2_check_message(char *buf, unsigned int len, struct TCP_Server_Info *server)
+ {
+       struct smb2_hdr *shdr = (struct smb2_hdr *)buf;
+       struct smb2_pdu *pdu = (struct smb2_pdu *)shdr;
+-      __u64 mid;
+-      __u32 clc_len;  /* calculated length */
+-      int command;
+-      int pdu_size = sizeof(struct smb2_pdu);
+       int hdr_size = sizeof(struct smb2_hdr);
++      int pdu_size = sizeof(struct smb2_pdu);
++      int command;
++      __u32 calc_len; /* calculated length */
++      __u64 mid;
+       /*
+        * Add function to do table lookup of StructureSize by command
+@@ -154,7 +154,7 @@ smb2_check_message(char *buf, unsigned i
+               /* decrypt frame now that it is completely read in */
+               spin_lock(&cifs_tcp_ses_lock);
+-              list_for_each_entry(iter, &srvr->smb_ses_list, smb_ses_list) {
++              list_for_each_entry(iter, &server->smb_ses_list, smb_ses_list) {
+                       if (iter->Suid == le64_to_cpu(thdr->SessionId)) {
+                               ses = iter;
+                               break;
+@@ -221,30 +221,33 @@ smb2_check_message(char *buf, unsigned i
+               }
+       }
+-      clc_len = smb2_calc_size(buf, srvr);
++      calc_len = smb2_calc_size(buf, server);
++
++      /* For SMB2_IOCTL, OutputOffset and OutputLength are optional, so might
++       * be 0, and not a real miscalculation */
++      if (command == SMB2_IOCTL_HE && calc_len == 0)
++              return 0;
+-      if (shdr->Command == SMB2_NEGOTIATE)
+-              clc_len += get_neg_ctxt_len(shdr, len, clc_len);
++      if (command == SMB2_NEGOTIATE_HE)
++              calc_len += get_neg_ctxt_len(shdr, len, calc_len);
+-      if (len != clc_len) {
+-              cifs_dbg(FYI, "Calculated size %u length %u mismatch mid %llu\n",
+-                       clc_len, len, mid);
++      if (len != calc_len) {
+               /* create failed on symlink */
+               if (command == SMB2_CREATE_HE &&
+                   shdr->Status == STATUS_STOPPED_ON_SYMLINK)
+                       return 0;
+               /* Windows 7 server returns 24 bytes more */
+-              if (clc_len + 24 == len && command == SMB2_OPLOCK_BREAK_HE)
++              if (calc_len + 24 == len && command == SMB2_OPLOCK_BREAK_HE)
+                       return 0;
+               /* server can return one byte more due to implied bcc[0] */
+-              if (clc_len == len + 1)
++              if (calc_len == len + 1)
+                       return 0;
+               /*
+                * Some windows servers (win2016) will pad also the final
+                * PDU in a compound to 8 bytes.
+                */
+-              if (((clc_len + 7) & ~7) == len)
++              if (((calc_len + 7) & ~7) == len)
+                       return 0;
+               /*
+@@ -253,12 +256,18 @@ smb2_check_message(char *buf, unsigned i
+                * SMB2/SMB3 frame length (header + smb2 response specific data)
+                * Some windows servers also pad up to 8 bytes when compounding.
+                */
+-              if (clc_len < len)
++              if (calc_len < len)
+                       return 0;
+-              pr_warn_once(
+-                      "srv rsp too short, len %d not %d. cmd:%d mid:%llu\n",
+-                      len, clc_len, command, mid);
++              /* Only log a message if len was really miscalculated */
++              if (unlikely(cifsFYI))
++                      cifs_dbg(FYI, "Server response too short: calculated "
++                               "length %u doesn't match read length %u (cmd=%d, mid=%llu)\n",
++                               calc_len, len, command, mid);
++              else
++                      pr_warn("Server response too short: calculated length "
++                              "%u doesn't match read length %u (cmd=%d, mid=%llu)\n",
++                              calc_len, len, command, mid);
+               return 1;
+       }
diff --git a/queue-5.15/staging-rtl8723bs-core-move-constants-to-right-side-in-comparison.patch b/queue-5.15/staging-rtl8723bs-core-move-constants-to-right-side-in-comparison.patch
new file mode 100644 (file)
index 0000000..783d186
--- /dev/null
@@ -0,0 +1,56 @@
+From stable+bounces-274664-greg=kroah.com@vger.kernel.org Wed Jul 15 03:34:19 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 14 Jul 2026 21:34:06 -0400
+Subject: staging: rtl8723bs: core: move constants to right side in comparison
+To: stable@vger.kernel.org
+Cc: William Hansen-Baird <william.hansen.baird@gmail.com>, Greg Kroah-Hartman <gregkh@linuxfoundation.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260715013408.4110029-3-sashal@kernel.org>
+
+From: William Hansen-Baird <william.hansen.baird@gmail.com>
+
+[ Upstream commit cf0f2680c30d4b438a5e84b73dc70be405936b54 ]
+
+Move constants to right side in if-statement conditions.
+
+Signed-off-by: William Hansen-Baird <william.hansen.baird@gmail.com>
+Link: https://patch.msgid.link/20251224100329.762141-3-william.hansen.baird@gmail.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Stable-dep-of: 1463ca3ec660 ("staging: rtl8723bs: fix OOB reads in rtw_get_sec_ie(), rtw_get_wapi_ie(), and rtw_get_wps_attr()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/staging/rtl8723bs/core/rtw_ieee80211.c |    4 ++--
+ drivers/staging/rtl8723bs/core/rtw_security.c  |    2 +-
+ 2 files changed, 3 insertions(+), 3 deletions(-)
+
+--- a/drivers/staging/rtl8723bs/core/rtw_ieee80211.c
++++ b/drivers/staging/rtl8723bs/core/rtw_ieee80211.c
+@@ -1028,7 +1028,7 @@ static int rtw_get_cipher_info(struct wl
+       pbuf = rtw_get_wpa_ie(&pnetwork->network.ies[12], &wpa_ielen, pnetwork->network.ie_length-12);
+       if (pbuf && (wpa_ielen > 0)) {
+-              if (_SUCCESS == rtw_parse_wpa_ie(pbuf, wpa_ielen+2, &group_cipher, &pairwise_cipher, &is8021x)) {
++              if (rtw_parse_wpa_ie(pbuf, wpa_ielen+2, &group_cipher, &pairwise_cipher, &is8021x) == _SUCCESS) {
+                       pnetwork->bcn_info.pairwise_cipher = pairwise_cipher;
+                       pnetwork->bcn_info.group_cipher = group_cipher;
+                       pnetwork->bcn_info.is_8021x = is8021x;
+@@ -1038,7 +1038,7 @@ static int rtw_get_cipher_info(struct wl
+               pbuf = rtw_get_wpa2_ie(&pnetwork->network.ies[12], &wpa_ielen, pnetwork->network.ie_length-12);
+               if (pbuf && (wpa_ielen > 0)) {
+-                      if (_SUCCESS == rtw_parse_wpa2_ie(pbuf, wpa_ielen+2, &group_cipher, &pairwise_cipher, &is8021x)) {
++                      if (rtw_parse_wpa2_ie(pbuf, wpa_ielen+2, &group_cipher, &pairwise_cipher, &is8021x) == _SUCCESS) {
+                               pnetwork->bcn_info.pairwise_cipher = pairwise_cipher;
+                               pnetwork->bcn_info.group_cipher = group_cipher;
+                               pnetwork->bcn_info.is_8021x = is8021x;
+--- a/drivers/staging/rtl8723bs/core/rtw_security.c
++++ b/drivers/staging/rtl8723bs/core/rtw_security.c
+@@ -1538,7 +1538,7 @@ void rtw_sec_restore_wep_key(struct adap
+       struct security_priv *securitypriv = &(adapter->securitypriv);
+       signed int keyid;
+-      if ((_WEP40_ == securitypriv->dot11PrivacyAlgrthm) || (_WEP104_ == securitypriv->dot11PrivacyAlgrthm)) {
++      if ((securitypriv->dot11PrivacyAlgrthm == _WEP40_) || (securitypriv->dot11PrivacyAlgrthm == _WEP104_)) {
+               for (keyid = 0; keyid < 4; keyid++) {
+                       if (securitypriv->key_mask & BIT(keyid)) {
+                               if (keyid == securitypriv->dot11PrivacyKeyIndex)
diff --git a/queue-5.15/staging-rtl8723bs-fix-heap-buffer-overflow-in-rtw_cfg80211_set_wpa_ie.patch b/queue-5.15/staging-rtl8723bs-fix-heap-buffer-overflow-in-rtw_cfg80211_set_wpa_ie.patch
new file mode 100644 (file)
index 0000000..4de9528
--- /dev/null
@@ -0,0 +1,66 @@
+From stable+bounces-274586-greg=kroah.com@vger.kernel.org Wed Jul 15 00:05:51 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 14 Jul 2026 18:04:26 -0400
+Subject: staging: rtl8723bs: fix heap buffer overflow in rtw_cfg80211_set_wpa_ie()
+To: stable@vger.kernel.org
+Cc: Alexandru Hossu <hossu.alexandru@gmail.com>, stable <stable@kernel.org>, Luka Gejak <luka.gejak@linux.dev>, Greg Kroah-Hartman <gregkh@linuxfoundation.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260714220426.3334125-3-sashal@kernel.org>
+
+From: Alexandru Hossu <hossu.alexandru@gmail.com>
+
+[ Upstream commit 5a752a616e756844388a1a45404db9fc29fec655 ]
+
+supplicant_ie is a 256-byte array in struct security_priv. The WPA and
+WPA2 IE copy paths use:
+
+    memcpy(padapter->securitypriv.supplicant_ie, &pwpa[0], wpa_ielen + 2);
+
+where wpa_ielen is the raw IE length field (u8, 0-255). When a local user
+supplies a connect request via nl80211 with a crafted WPA IE of length 255,
+wpa_ielen + 2 equals 257, overflowing the 256-byte buffer by one byte into
+the adjacent last_mic_err_time field.
+
+rtw_parse_wpa_ie() does not prevent this: its length consistency check
+compares *(wpa_ie+1) against (u8)(wpa_ie_len-2), which is (u8)(255) == 255
+when wpa_ie_len = 257, so the check passes silently.
+
+Add explicit bounds checks for both the WPA and WPA2 paths before the
+memcpy, rejecting any IE whose total size (wpa_ielen + 2) exceeds the
+supplicant_ie buffer.
+
+Fixes: 554c0a3abf21 ("staging: Add rtl8723bs sdio wifi driver")
+Cc: stable <stable@kernel.org>
+Reviewed-by: Luka Gejak <luka.gejak@linux.dev>
+Signed-off-by: Alexandru Hossu <hossu.alexandru@gmail.com>
+Link: https://patch.msgid.link/20260522004531.1038924-4-hossu.alexandru@gmail.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/staging/rtl8723bs/os_dep/ioctl_cfg80211.c |    8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+--- a/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c
++++ b/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c
+@@ -1481,6 +1481,10 @@ static int rtw_cfg80211_set_wpa_ie(struc
+       pwpa = rtw_get_wpa_ie(buf, &wpa_ielen, ielen);
+       if (pwpa && wpa_ielen > 0) {
++              if (wpa_ielen + 2 > sizeof(padapter->securitypriv.supplicant_ie)) {
++                      ret = -EINVAL;
++                      goto exit;
++              }
+               if (rtw_parse_wpa_ie(pwpa, wpa_ielen + 2, &group_cipher, &pairwise_cipher, NULL) == _SUCCESS) {
+                       padapter->securitypriv.dot11AuthAlgrthm = dot11AuthAlgrthm_8021X;
+                       padapter->securitypriv.ndisauthtype = Ndis802_11AuthModeWPAPSK;
+@@ -1490,6 +1494,10 @@ static int rtw_cfg80211_set_wpa_ie(struc
+       pwpa2 = rtw_get_wpa2_ie(buf, &wpa2_ielen, ielen);
+       if (pwpa2 && wpa2_ielen > 0) {
++              if (wpa2_ielen + 2 > sizeof(padapter->securitypriv.supplicant_ie)) {
++                      ret = -EINVAL;
++                      goto exit;
++              }
+               if (rtw_parse_wpa2_ie(pwpa2, wpa2_ielen + 2, &group_cipher, &pairwise_cipher, NULL) == _SUCCESS) {
+                       padapter->securitypriv.dot11AuthAlgrthm = dot11AuthAlgrthm_8021X;
+                       padapter->securitypriv.ndisauthtype = Ndis802_11AuthModeWPA2PSK;
diff --git a/queue-5.15/staging-rtl8723bs-fix-indentation-issues.patch b/queue-5.15/staging-rtl8723bs-fix-indentation-issues.patch
new file mode 100644 (file)
index 0000000..461e107
--- /dev/null
@@ -0,0 +1,56 @@
+From stable+bounces-274584-greg=kroah.com@vger.kernel.org Wed Jul 15 00:05:18 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 14 Jul 2026 18:04:24 -0400
+Subject: staging: rtl8723bs: Fix indentation issues
+To: stable@vger.kernel.org
+Cc: Franziska Naepelt <franziska.naepelt@googlemail.com>, Franziska Naepelt <franziska.naepelt@gmail.com>, Greg Kroah-Hartman <gregkh@linuxfoundation.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260714220426.3334125-1-sashal@kernel.org>
+
+From: Franziska Naepelt <franziska.naepelt@googlemail.com>
+
+[ Upstream commit b59cba2309b16fa364df03e3693b8d45c3fadbd6 ]
+
+Fix the following checkpatch indentation issues:
+- WARNING: suspect code indent for conditional statements (32, 48)
+- WARNING: suspect code indent for conditional statements (24, 24)
+- ERROR: code indent should use tabs where possible
+
+Signed-off-by: Franziska Naepelt <franziska.naepelt@gmail.com>
+Link: https://lore.kernel.org/r/20230619180351.18925-1-franziska.naepelt@gmail.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Stable-dep-of: 5a752a616e75 ("staging: rtl8723bs: fix heap buffer overflow in rtw_cfg80211_set_wpa_ie()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c |    6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+--- a/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c
++++ b/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c
+@@ -582,7 +582,7 @@ static int rtw_cfg80211_ap_set_encryptio
+                               psecuritypriv->dot118021XGrpPrivacy = _WEP40_;
+                               if (param->u.crypt.key_len == 13)
+-                                              psecuritypriv->dot118021XGrpPrivacy = _WEP104_;
++                                      psecuritypriv->dot118021XGrpPrivacy = _WEP104_;
+                       } else if (strcmp(param->u.crypt.alg, "TKIP") == 0) {
+                               psecuritypriv->dot118021XGrpPrivacy = _TKIP_;
+@@ -1305,7 +1305,7 @@ static int cfg80211_rtw_scan(struct wiph
+       } else if (request->n_channels <= 4) {
+               for (j = request->n_channels - 1; j >= 0; j--)
+                       for (i = 0; i < survey_times; i++)
+-                      memcpy(&ch[j*survey_times+i], &ch[j], sizeof(struct rtw_ieee80211_channel));
++                              memcpy(&ch[j*survey_times+i], &ch[j], sizeof(struct rtw_ieee80211_channel));
+               _status = rtw_sitesurvey_cmd(padapter, ssid, RTW_SSID_SCAN_AMOUNT, ch, survey_times * request->n_channels);
+       } else {
+               _status = rtw_sitesurvey_cmd(padapter, ssid, RTW_SSID_SCAN_AMOUNT, NULL, 0);
+@@ -2805,7 +2805,7 @@ int rtw_wdev_alloc(struct adapter *padap
+       wdev->netdev = pnetdev;
+       wdev->iftype = NL80211_IFTYPE_STATION; /*  will be init in rtw_hal_init() */
+-                                             /*  Must sync with _rtw_init_mlme_priv() */
++                                         /*  Must sync with _rtw_init_mlme_priv() */
+                                          /*  pmlmepriv->fw_state = WIFI_STATION_STATE */
+       padapter->rtw_wdev = wdev;
+       pnetdev->ieee80211_ptr = wdev;
diff --git a/queue-5.15/staging-rtl8723bs-fix-oob-reads-in-rtw_get_sec_ie-rtw_get_wapi_ie-and-rtw_get_wps_attr.patch b/queue-5.15/staging-rtl8723bs-fix-oob-reads-in-rtw_get_sec_ie-rtw_get_wapi_ie-and-rtw_get_wps_attr.patch
new file mode 100644 (file)
index 0000000..f779f55
--- /dev/null
@@ -0,0 +1,94 @@
+From stable+bounces-274666-greg=kroah.com@vger.kernel.org Wed Jul 15 03:34:20 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 14 Jul 2026 21:34:08 -0400
+Subject: staging: rtl8723bs: fix OOB reads in rtw_get_sec_ie(), rtw_get_wapi_ie(), and rtw_get_wps_attr()
+To: stable@vger.kernel.org
+Cc: Alexandru Hossu <hossu.alexandru@gmail.com>, stable <stable@kernel.org>, Greg Kroah-Hartman <gregkh@linuxfoundation.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260715013408.4110029-5-sashal@kernel.org>
+
+From: Alexandru Hossu <hossu.alexandru@gmail.com>
+
+[ Upstream commit 1463ca3ec6601cbb097d8d87dbf5dcf1cb86a344 ]
+
+Three IE/attribute parsing functions have missing bounds checks.
+
+rtw_get_sec_ie() and rtw_get_wapi_ie() iterate over a raw IE buffer
+without verifying that the header bytes (tag + length) are within the
+remaining buffer before reading them.  Additionally, rtw_get_sec_ie()
+compares the 4-byte WPA OUI at cnt+2 without checking that at least
+6 bytes remain, and rtw_get_wapi_ie() compares a 4-byte WAPI OUI at
+cnt+6 without checking that at least 10 bytes remain.
+
+rtw_get_wps_attr() reads wps_ie[0] and wps_ie+2 unconditionally at
+entry, before verifying that wps_ielen is large enough to contain
+the 6-byte WPS IE header (element_id + length + 4-byte OUI).  Inside
+the attribute loop, get_unaligned_be16() is called on attr_ptr and
+attr_ptr+2 without checking that 4 bytes remain in the buffer.
+
+Add a cnt+2 bounds check before each loop body in rtw_get_sec_ie()
+and rtw_get_wapi_ie(), guard each multi-byte comparison with a minimum
+IE length requirement, add a wps_ielen < 6 early return in
+rtw_get_wps_attr(), and add a 4-byte bounds check in its inner loop.
+
+Fixes: 554c0a3abf21 ("staging: Add rtl8723bs sdio wifi driver")
+Cc: stable <stable@kernel.org>
+Signed-off-by: Alexandru Hossu <hossu.alexandru@gmail.com>
+Link: https://patch.msgid.link/20260522004531.1038924-8-hossu.alexandru@gmail.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/staging/rtl8723bs/core/rtw_ieee80211.c |   15 +++++++++++++++
+ 1 file changed, 15 insertions(+)
+
+--- a/drivers/staging/rtl8723bs/core/rtw_ieee80211.c
++++ b/drivers/staging/rtl8723bs/core/rtw_ieee80211.c
+@@ -583,9 +583,14 @@ int rtw_get_wapi_ie(u8 *in_ie, uint in_l
+       cnt = (_TIMESTAMP_ + _BEACON_ITERVAL_ + _CAPABILITY_);
+       while (cnt < in_len) {
++              if (cnt + 2 > in_len)
++                      break;
++              if (cnt + 2 + in_ie[cnt + 1] > in_len)
++                      break;
+               authmode = in_ie[cnt];
+               if (authmode == WLAN_EID_BSS_AC_ACCESS_DELAY &&
++                  in_ie[cnt + 1] >= 8 &&
+                   (!memcmp(&in_ie[cnt + 6], wapi_oui1, 4) ||
+                    !memcmp(&in_ie[cnt + 6], wapi_oui2, 4))) {
+                       if (wapi_ie)
+@@ -618,9 +623,14 @@ void rtw_get_sec_ie(u8 *in_ie, uint in_l
+       cnt = (_TIMESTAMP_ + _BEACON_ITERVAL_ + _CAPABILITY_);
+       while (cnt < in_len) {
++              if (cnt + 2 > in_len)
++                      break;
++              if (cnt + 2 + in_ie[cnt + 1] > in_len)
++                      break;
+               authmode = in_ie[cnt];
+               if ((authmode == WLAN_EID_VENDOR_SPECIFIC) &&
++                  in_ie[cnt + 1] >= 4 &&
+                   (!memcmp(&in_ie[cnt + 2], &wpa_oui[0], 4))) {
+                       if (wpa_ie)
+                               memcpy(wpa_ie, &in_ie[cnt], in_ie[cnt + 1] + 2);
+@@ -722,6 +732,9 @@ u8 *rtw_get_wps_attr(u8 *wps_ie, uint wp
+       if (len_attr)
+               *len_attr = 0;
++      if (wps_ielen < 6)
++              return attr_ptr;
++
+       if ((wps_ie[0] != WLAN_EID_VENDOR_SPECIFIC) ||
+               (memcmp(wps_ie + 2, wps_oui, 4))) {
+               return attr_ptr;
+@@ -732,6 +745,8 @@ u8 *rtw_get_wps_attr(u8 *wps_ie, uint wp
+       while (attr_ptr - wps_ie < wps_ielen) {
+               /*  4 = 2(Attribute ID) + 2(Length) */
++              if (attr_ptr + 4 > wps_ie + wps_ielen)
++                      break;
+               u16 attr_id = get_unaligned_be16(attr_ptr);
+               u16 attr_data_len = get_unaligned_be16(attr_ptr + 2);
+               u16 attr_len = attr_data_len + 4;
diff --git a/queue-5.15/staging-rtl8723bs-fix-space-issues.patch b/queue-5.15/staging-rtl8723bs-fix-space-issues.patch
new file mode 100644 (file)
index 0000000..b3e61cc
--- /dev/null
@@ -0,0 +1,255 @@
+From stable+bounces-274585-greg=kroah.com@vger.kernel.org Wed Jul 15 00:04:53 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 14 Jul 2026 18:04:25 -0400
+Subject: staging: rtl8723bs: Fix space issues
+To: stable@vger.kernel.org
+Cc: Franziska Naepelt <franziska.naepelt@googlemail.com>, Greg Kroah-Hartman <gregkh@linuxfoundation.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260714220426.3334125-2-sashal@kernel.org>
+
+From: Franziska Naepelt <franziska.naepelt@googlemail.com>
+
+[ Upstream commit c4b811b9361b0d7ea167a79587f5ec0a13fd8863 ]
+
+Fix the following checkpatch space issues:
+- CHECK: spaces preferred around that '*' (ctx:VxV)
+- CHECK: spaces preferred around that '+' (ctx:VxV)
+- CHECK: spaces preferred around that '-' (ctx:VxV)
+- CHECK: spaces preferred around that '|' (ctx:VxV)
+- CHECK: No space is necessary after a cast
+- WARNING: please, no spaces at the start of a line
+
+Signed-off-by: Franziska Naepelt <franziska.naepelt@googlemail.com>
+Link: https://lore.kernel.org/r/20230701102538.5359-1-franziska.naepelt@googlemail.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Stable-dep-of: 5a752a616e75 ("staging: rtl8723bs: fix heap buffer overflow in rtw_cfg80211_set_wpa_ie()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c |   76 +++++++++++-----------
+ 1 file changed, 38 insertions(+), 38 deletions(-)
+
+--- a/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c
++++ b/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c
+@@ -95,14 +95,14 @@ static struct ieee80211_channel rtw_2ghz
+ static void rtw_2g_channels_init(struct ieee80211_channel *channels)
+ {
+       memcpy((void *)channels, (void *)rtw_2ghz_channels,
+-              sizeof(struct ieee80211_channel)*RTW_2G_CHANNELS_NUM
++              sizeof(struct ieee80211_channel) * RTW_2G_CHANNELS_NUM
+       );
+ }
+ static void rtw_2g_rates_init(struct ieee80211_rate *rates)
+ {
+       memcpy(rates, rtw_g_rates,
+-              sizeof(struct ieee80211_rate)*RTW_G_RATES_NUM
++              sizeof(struct ieee80211_rate) * RTW_G_RATES_NUM
+       );
+ }
+@@ -126,8 +126,8 @@ static struct ieee80211_supported_band *
+       if (!spt_band)
+               goto exit;
+-      spt_band->channels = (struct ieee80211_channel *)(((u8 *)spt_band)+sizeof(struct ieee80211_supported_band));
+-      spt_band->bitrates = (struct ieee80211_rate *)(((u8 *)spt_band->channels)+sizeof(struct ieee80211_channel)*n_channels);
++      spt_band->channels = (struct ieee80211_channel *)(((u8 *)spt_band) + sizeof(struct ieee80211_supported_band));
++      spt_band->bitrates = (struct ieee80211_rate *)(((u8 *)spt_band->channels) + sizeof(struct ieee80211_channel) * n_channels);
+       spt_band->band = band;
+       spt_band->n_channels = n_channels;
+       spt_band->n_bitrates = n_bitrates;
+@@ -247,10 +247,10 @@ struct cfg80211_bss *rtw_cfg80211_inform
+               u32 wpsielen = 0;
+               u8 *wpsie = NULL;
+-              wpsie = rtw_get_wps_ie(pnetwork->network.ies+_FIXED_IE_LENGTH_, pnetwork->network.ie_length-_FIXED_IE_LENGTH_, NULL, &wpsielen);
++              wpsie = rtw_get_wps_ie(pnetwork->network.ies + _FIXED_IE_LENGTH_, pnetwork->network.ie_length - _FIXED_IE_LENGTH_, NULL, &wpsielen);
+               if (wpsie && wpsielen > 0)
+-                      psr = rtw_get_wps_attr_content(wpsie,  wpsielen, WPS_ATTR_SELECTED_REGISTRAR, (u8 *)(&sr), NULL);
++                      psr = rtw_get_wps_attr_content(wpsie, wpsielen, WPS_ATTR_SELECTED_REGISTRAR, (u8 *)(&sr), NULL);
+               if (sr != 0) {
+                       /* it means under processing WPS */
+@@ -277,9 +277,9 @@ struct cfg80211_bss *rtw_cfg80211_inform
+       /* We've set wiphy's signal_type as CFG80211_SIGNAL_TYPE_MBM: signal strength in mBm (100*dBm) */
+       if (check_fwstate(pmlmepriv, _FW_LINKED) == true &&
+               is_same_network(&pmlmepriv->cur_network.network, &pnetwork->network, 0)) {
+-              notify_signal = 100*translate_percentage_to_dbm(padapter->recvpriv.signal_strength);/* dbm */
++              notify_signal = 100 * translate_percentage_to_dbm(padapter->recvpriv.signal_strength);/* dbm */
+       } else {
+-              notify_signal = 100*translate_percentage_to_dbm(pnetwork->network.phy_info.signal_strength);/* dbm */
++              notify_signal = 100 * translate_percentage_to_dbm(pnetwork->network.phy_info.signal_strength);/* dbm */
+       }
+       buf = kzalloc(MAX_BSSINFO_LEN, GFP_ATOMIC);
+@@ -453,20 +453,20 @@ check_bss:
+               roam_info.channel = notify_channel;
+               roam_info.bssid = cur_network->network.mac_address;
+               roam_info.req_ie =
+-                      pmlmepriv->assoc_req+sizeof(struct ieee80211_hdr_3addr)+2;
++                      pmlmepriv->assoc_req + sizeof(struct ieee80211_hdr_3addr) + 2;
+               roam_info.req_ie_len =
+-                      pmlmepriv->assoc_req_len-sizeof(struct ieee80211_hdr_3addr)-2;
++                      pmlmepriv->assoc_req_len - sizeof(struct ieee80211_hdr_3addr) - 2;
+               roam_info.resp_ie =
+-                      pmlmepriv->assoc_rsp+sizeof(struct ieee80211_hdr_3addr)+6;
++                      pmlmepriv->assoc_rsp + sizeof(struct ieee80211_hdr_3addr) + 6;
+               roam_info.resp_ie_len =
+-                      pmlmepriv->assoc_rsp_len-sizeof(struct ieee80211_hdr_3addr)-6;
++                      pmlmepriv->assoc_rsp_len - sizeof(struct ieee80211_hdr_3addr) - 6;
+               cfg80211_roamed(padapter->pnetdev, &roam_info, GFP_ATOMIC);
+       } else {
+               cfg80211_connect_result(padapter->pnetdev, cur_network->network.mac_address
+-                      , pmlmepriv->assoc_req+sizeof(struct ieee80211_hdr_3addr)+2
+-                      , pmlmepriv->assoc_req_len-sizeof(struct ieee80211_hdr_3addr)-2
+-                      , pmlmepriv->assoc_rsp+sizeof(struct ieee80211_hdr_3addr)+6
+-                      , pmlmepriv->assoc_rsp_len-sizeof(struct ieee80211_hdr_3addr)-6
++                      , pmlmepriv->assoc_req + sizeof(struct ieee80211_hdr_3addr) + 2
++                      , pmlmepriv->assoc_req_len - sizeof(struct ieee80211_hdr_3addr) - 2
++                      , pmlmepriv->assoc_rsp + sizeof(struct ieee80211_hdr_3addr) + 6
++                      , pmlmepriv->assoc_rsp_len - sizeof(struct ieee80211_hdr_3addr) - 6
+                       , WLAN_STATUS_SUCCESS, GFP_ATOMIC);
+       }
+ }
+@@ -721,7 +721,7 @@ static int rtw_cfg80211_set_encryption(s
+       param->u.crypt.err = 0;
+       param->u.crypt.alg[IEEE_CRYPT_ALG_NAME_LEN - 1] = '\0';
+-      if (param_len < (u32) ((u8 *) param->u.crypt.key - (u8 *) param) + param->u.crypt.key_len) {
++      if (param_len < (u32)((u8 *)param->u.crypt.key - (u8 *)param) + param->u.crypt.key_len) {
+               ret =  -EINVAL;
+               goto exit;
+       }
+@@ -1035,10 +1035,10 @@ static int cfg80211_rtw_get_station(stru
+       }
+       /* for Ad-Hoc/AP mode */
+-      if ((check_fwstate(pmlmepriv, WIFI_ADHOC_STATE)
+- || check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE)
+- || check_fwstate(pmlmepriv, WIFI_AP_STATE))
+-              && check_fwstate(pmlmepriv, _FW_LINKED)) {
++      if ((check_fwstate(pmlmepriv, WIFI_ADHOC_STATE) ||
++           check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE) ||
++           check_fwstate(pmlmepriv, WIFI_AP_STATE)) &&
++          check_fwstate(pmlmepriv, _FW_LINKED)) {
+               /* TODO: should acquire station info... */
+       }
+@@ -1237,7 +1237,7 @@ static int cfg80211_rtw_scan(struct wiph
+       spin_unlock_bh(&pwdev_priv->scan_req_lock);
+       if (check_fwstate(pmlmepriv, WIFI_AP_STATE) == true) {
+-              if (check_fwstate(pmlmepriv, WIFI_UNDER_WPS|_FW_UNDER_SURVEY|_FW_UNDER_LINKING) == true) {
++              if (check_fwstate(pmlmepriv, WIFI_UNDER_WPS | _FW_UNDER_SURVEY | _FW_UNDER_LINKING) == true) {
+                       need_indicate_scan_done = true;
+                       goto check_need_indicate_scan_done;
+               }
+@@ -1291,7 +1291,7 @@ static int cfg80211_rtw_scan(struct wiph
+       }
+       /* parsing channels, n_channels */
+-      memset(ch, 0, sizeof(struct rtw_ieee80211_channel)*RTW_CHANNEL_SCAN_AMOUNT);
++      memset(ch, 0, sizeof(struct rtw_ieee80211_channel) * RTW_CHANNEL_SCAN_AMOUNT);
+       for (i = 0; i < request->n_channels && i < RTW_CHANNEL_SCAN_AMOUNT; i++) {
+               ch[i].hw_value = request->channels[i]->hw_value;
+               ch[i].flags = request->channels[i]->flags;
+@@ -1305,7 +1305,7 @@ static int cfg80211_rtw_scan(struct wiph
+       } else if (request->n_channels <= 4) {
+               for (j = request->n_channels - 1; j >= 0; j--)
+                       for (i = 0; i < survey_times; i++)
+-                              memcpy(&ch[j*survey_times+i], &ch[j], sizeof(struct rtw_ieee80211_channel));
++                              memcpy(&ch[j * survey_times + i], &ch[j], sizeof(struct rtw_ieee80211_channel));
+               _status = rtw_sitesurvey_cmd(padapter, ssid, RTW_SSID_SCAN_AMOUNT, ch, survey_times * request->n_channels);
+       } else {
+               _status = rtw_sitesurvey_cmd(padapter, ssid, RTW_SSID_SCAN_AMOUNT, NULL, 0);
+@@ -1461,7 +1461,7 @@ static int rtw_cfg80211_set_wpa_ie(struc
+               goto exit;
+       }
+-      if (ielen > MAX_WPA_IE_LEN+MAX_WPS_IE_LEN+MAX_P2P_IE_LEN) {
++      if (ielen > MAX_WPA_IE_LEN + MAX_WPS_IE_LEN + MAX_P2P_IE_LEN) {
+               ret = -EINVAL;
+               goto exit;
+       }
+@@ -1481,19 +1481,19 @@ static int rtw_cfg80211_set_wpa_ie(struc
+       pwpa = rtw_get_wpa_ie(buf, &wpa_ielen, ielen);
+       if (pwpa && wpa_ielen > 0) {
+-              if (rtw_parse_wpa_ie(pwpa, wpa_ielen+2, &group_cipher, &pairwise_cipher, NULL) == _SUCCESS) {
++              if (rtw_parse_wpa_ie(pwpa, wpa_ielen + 2, &group_cipher, &pairwise_cipher, NULL) == _SUCCESS) {
+                       padapter->securitypriv.dot11AuthAlgrthm = dot11AuthAlgrthm_8021X;
+                       padapter->securitypriv.ndisauthtype = Ndis802_11AuthModeWPAPSK;
+-                      memcpy(padapter->securitypriv.supplicant_ie, &pwpa[0], wpa_ielen+2);
++                      memcpy(padapter->securitypriv.supplicant_ie, &pwpa[0], wpa_ielen + 2);
+               }
+       }
+       pwpa2 = rtw_get_wpa2_ie(buf, &wpa2_ielen, ielen);
+       if (pwpa2 && wpa2_ielen > 0) {
+-              if (rtw_parse_wpa2_ie(pwpa2, wpa2_ielen+2, &group_cipher, &pairwise_cipher, NULL) == _SUCCESS) {
++              if (rtw_parse_wpa2_ie(pwpa2, wpa2_ielen + 2, &group_cipher, &pairwise_cipher, NULL) == _SUCCESS) {
+                       padapter->securitypriv.dot11AuthAlgrthm = dot11AuthAlgrthm_8021X;
+                       padapter->securitypriv.ndisauthtype = Ndis802_11AuthModeWPA2PSK;
+-                      memcpy(padapter->securitypriv.supplicant_ie, &pwpa2[0], wpa2_ielen+2);
++                      memcpy(padapter->securitypriv.supplicant_ie, &pwpa2[0], wpa2_ielen + 2);
+               }
+       }
+@@ -1891,7 +1891,7 @@ static int cfg80211_rtw_set_pmksa(struct
+                       memcpy(psecuritypriv->PMKIDList[index].PMKID, (u8 *)pmksa->pmkid, WLAN_PMKID_LEN);
+                       psecuritypriv->PMKIDList[index].bUsed = true;
+-                      psecuritypriv->PMKIDIndex = index+1;
++                      psecuritypriv->PMKIDIndex = index + 1;
+                       blInserted = true;
+                       break;
+               }
+@@ -2090,8 +2090,8 @@ static netdev_tx_t rtw_cfg80211_monitor_
+               /* Use the real net device to transmit the packet */
+               return _rtw_xmit_entry(skb, padapter->pnetdev);
+-      } else if ((frame_control & (IEEE80211_FCTL_FTYPE|IEEE80211_FCTL_STYPE)) ==
+-                 (IEEE80211_FTYPE_MGMT|IEEE80211_STYPE_ACTION)) {
++      } else if ((frame_control & (IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) ==
++                 (IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION)) {
+               /* only for action frames */
+               struct xmit_frame               *pmgntframe;
+               struct pkt_attrib       *pattrib;
+@@ -2205,7 +2205,7 @@ static int rtw_cfg80211_add_monitor_if(s
+               goto out;
+       *ndev = pwdev_priv->pmon_ndev = mon_ndev;
+-      memcpy(pwdev_priv->ifname_mon, name, IFNAMSIZ+1);
++      memcpy(pwdev_priv->ifname_mon, name, IFNAMSIZ + 1);
+ out:
+       if (ret && mon_wdev) {
+@@ -2299,14 +2299,14 @@ static int rtw_add_beacon(struct adapter
+       if (head_len < 24)
+               return -EINVAL;
+-      pbuf = rtw_zmalloc(head_len+tail_len);
++      pbuf = rtw_zmalloc(head_len + tail_len);
+       if (!pbuf)
+               return -ENOMEM;
+-      memcpy(pbuf, (void *)head+24, head_len-24);/*  24 =beacon header len. */
+-      memcpy(pbuf+head_len-24, (void *)tail, tail_len);
++      memcpy(pbuf, (void *)head + 24, head_len - 24);/*  24 =beacon header len. */
++      memcpy(pbuf + head_len - 24, (void *)tail, tail_len);
+-      len = head_len+tail_len-24;
++      len = head_len + tail_len - 24;
+       /* check wps ie if inclued */
+       rtw_get_wps_ie(pbuf + _FIXED_IE_LENGTH_, len - _FIXED_IE_LENGTH_, NULL, &wps_ielen);
+@@ -2595,7 +2595,7 @@ static int cfg80211_rtw_mgmt_tx(struct w
+       pwdev_priv = adapter_wdev_data(padapter);
+       /* cookie generation */
+-      *cookie = (unsigned long) buf;
++      *cookie = (unsigned long)buf;
+       /* indicate ack before issue frame to avoid racing with rsp frame */
+       rtw_cfg80211_mgmt_tx_status(padapter, *cookie, buf, len, ack, GFP_KERNEL);
diff --git a/queue-5.15/staging-rtl8723bs-fix-spaces-around-binary-operators.patch b/queue-5.15/staging-rtl8723bs-fix-spaces-around-binary-operators.patch
new file mode 100644 (file)
index 0000000..2b48fda
--- /dev/null
@@ -0,0 +1,206 @@
+From stable+bounces-274665-greg=kroah.com@vger.kernel.org Wed Jul 15 03:34:22 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 14 Jul 2026 21:34:07 -0400
+Subject: staging: rtl8723bs: fix spaces around binary operators
+To: stable@vger.kernel.org
+Cc: Nikolay Kulikov <nikolayof23@gmail.com>, Ethan Tidmore <ethantidmore06@gmail.com>, Greg Kroah-Hartman <gregkh@linuxfoundation.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260715013408.4110029-4-sashal@kernel.org>
+
+From: Nikolay Kulikov <nikolayof23@gmail.com>
+
+[ Upstream commit 0c9d1b56f9af0762a5be5118e1bb962f23784dc4 ]
+
+Add missing spaces and fix line length to comply with kernel coding
+style.
+
+Signed-off-by: Nikolay Kulikov <nikolayof23@gmail.com>
+Reviewed-by: Ethan Tidmore <ethantidmore06@gmail.com>
+Link: https://patch.msgid.link/20260221172751.52329-1-nikolayof23@gmail.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Stable-dep-of: 1463ca3ec660 ("staging: rtl8723bs: fix OOB reads in rtw_get_sec_ie(), rtw_get_wapi_ie(), and rtw_get_wps_attr()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/staging/rtl8723bs/core/rtw_ieee80211.c |   78 +++++++++++++------------
+ 1 file changed, 42 insertions(+), 36 deletions(-)
+
+--- a/drivers/staging/rtl8723bs/core/rtw_ieee80211.c
++++ b/drivers/staging/rtl8723bs/core/rtw_ieee80211.c
+@@ -454,10 +454,10 @@ int rtw_parse_wpa_ie(u8 *wpa_ie, int wpa
+               return _FAIL;
+       }
+-      if ((*wpa_ie != WLAN_EID_VENDOR_SPECIFIC) || (*(wpa_ie+1) != (u8)(wpa_ie_len - 2)) ||
+-         (memcmp(wpa_ie+2, RTW_WPA_OUI_TYPE, WPA_SELECTOR_LEN))) {
++      if ((*wpa_ie != WLAN_EID_VENDOR_SPECIFIC) ||
++          (*(wpa_ie + 1) != (u8)(wpa_ie_len - 2)) ||
++          (memcmp(wpa_ie + 2, RTW_WPA_OUI_TYPE, WPA_SELECTOR_LEN)))
+               return _FAIL;
+-      }
+       pos = wpa_ie;
+@@ -517,7 +517,7 @@ int rtw_parse_wpa2_ie(u8 *rsn_ie, int rs
+               return _FAIL;
+       }
+-      if ((*rsn_ie != WLAN_EID_RSN) || (*(rsn_ie+1) != (u8)(rsn_ie_len - 2)))
++      if ((*rsn_ie != WLAN_EID_RSN) || (*(rsn_ie + 1) != (u8)(rsn_ie_len - 2)))
+               return _FAIL;
+       pos = rsn_ie;
+@@ -585,18 +585,18 @@ int rtw_get_wapi_ie(u8 *in_ie, uint in_l
+       while (cnt < in_len) {
+               authmode = in_ie[cnt];
+-              /* if (authmode == WLAN_EID_BSS_AC_ACCESS_DELAY) */
+-              if (authmode == WLAN_EID_BSS_AC_ACCESS_DELAY && (!memcmp(&in_ie[cnt+6], wapi_oui1, 4) ||
+-                                      !memcmp(&in_ie[cnt+6], wapi_oui2, 4))) {
++              if (authmode == WLAN_EID_BSS_AC_ACCESS_DELAY &&
++                  (!memcmp(&in_ie[cnt + 6], wapi_oui1, 4) ||
++                   !memcmp(&in_ie[cnt + 6], wapi_oui2, 4))) {
+                       if (wapi_ie)
+-                              memcpy(wapi_ie, &in_ie[cnt], in_ie[cnt+1]+2);
++                              memcpy(wapi_ie, &in_ie[cnt], in_ie[cnt + 1] + 2);
+                       if (wapi_len)
+-                              *wapi_len = in_ie[cnt+1]+2;
++                              *wapi_len = in_ie[cnt + 1] + 2;
+-                      cnt += in_ie[cnt+1]+2;  /* get next */
++                      cnt += in_ie[cnt + 1] + 2;  /* get next */
+               } else {
+-                      cnt += in_ie[cnt+1]+2;   /* get next */
++                      cnt += in_ie[cnt + 1] + 2;   /* get next */
+               }
+       }
+@@ -620,9 +620,10 @@ void rtw_get_sec_ie(u8 *in_ie, uint in_l
+       while (cnt < in_len) {
+               authmode = in_ie[cnt];
+-              if ((authmode == WLAN_EID_VENDOR_SPECIFIC) && (!memcmp(&in_ie[cnt+2], &wpa_oui[0], 4))) {
++              if ((authmode == WLAN_EID_VENDOR_SPECIFIC) &&
++                  (!memcmp(&in_ie[cnt + 2], &wpa_oui[0], 4))) {
+                       if (wpa_ie)
+-                              memcpy(wpa_ie, &in_ie[cnt], in_ie[cnt+1]+2);
++                              memcpy(wpa_ie, &in_ie[cnt], in_ie[cnt + 1] + 2);
+                       *wpa_len = in_ie[cnt + 1] + 2;
+                       cnt += in_ie[cnt + 1] + 2;  /* get next */
+@@ -631,10 +632,10 @@ void rtw_get_sec_ie(u8 *in_ie, uint in_l
+                               if (rsn_ie)
+                                       memcpy(rsn_ie, &in_ie[cnt], in_ie[cnt + 1] + 2);
+-                              *rsn_len = in_ie[cnt+1]+2;
+-                              cnt += in_ie[cnt+1]+2;  /* get next */
++                              *rsn_len = in_ie[cnt + 1] + 2;
++                              cnt += in_ie[cnt + 1] + 2;  /* get next */
+                       } else {
+-                              cnt += in_ie[cnt+1]+2;   /* get next */
++                              cnt += in_ie[cnt + 1] + 2;   /* get next */
+                       }
+               }
+       }
+@@ -683,20 +684,20 @@ u8 *rtw_get_wps_ie(u8 *in_ie, uint in_le
+       while (cnt < in_len) {
+               eid = in_ie[cnt];
+-              if ((eid == WLAN_EID_VENDOR_SPECIFIC) && (!memcmp(&in_ie[cnt+2], wps_oui, 4))) {
++              if ((eid == WLAN_EID_VENDOR_SPECIFIC) && (!memcmp(&in_ie[cnt + 2], wps_oui, 4))) {
+                       wpsie_ptr = &in_ie[cnt];
+                       if (wps_ie)
+-                              memcpy(wps_ie, &in_ie[cnt], in_ie[cnt+1]+2);
++                              memcpy(wps_ie, &in_ie[cnt], in_ie[cnt + 1] + 2);
+                       if (wps_ielen)
+-                              *wps_ielen = in_ie[cnt+1]+2;
++                              *wps_ielen = in_ie[cnt + 1] + 2;
+-                      cnt += in_ie[cnt+1]+2;
++                      cnt += in_ie[cnt + 1] + 2;
+                       break;
+               }
+-              cnt += in_ie[cnt+1]+2; /* goto next */
++              cnt += in_ie[cnt + 1] + 2; /* goto next */
+       }
+       return wpsie_ptr;
+@@ -774,12 +775,12 @@ u8 *rtw_get_wps_attr_content(u8 *wps_ie,
+       if (attr_ptr && attr_len) {
+               if (buf_content)
+-                      memcpy(buf_content, attr_ptr+4, attr_len-4);
++                      memcpy(buf_content, attr_ptr + 4, attr_len - 4);
+               if (len_content)
+-                      *len_content = attr_len-4;
++                      *len_content = attr_len - 4;
+-              return attr_ptr+4;
++              return attr_ptr + 4;
+       }
+       return NULL;
+@@ -1025,20 +1026,25 @@ static int rtw_get_cipher_info(struct wl
+       int group_cipher = 0, pairwise_cipher = 0, is8021x = 0;
+       int ret = _FAIL;
+-      pbuf = rtw_get_wpa_ie(&pnetwork->network.ies[12], &wpa_ielen, pnetwork->network.ie_length-12);
++      pbuf = rtw_get_wpa_ie(&pnetwork->network.ies[12],
++                            &wpa_ielen,
++                            pnetwork->network.ie_length - 12);
+       if (pbuf && (wpa_ielen > 0)) {
+-              if (rtw_parse_wpa_ie(pbuf, wpa_ielen+2, &group_cipher, &pairwise_cipher, &is8021x) == _SUCCESS) {
++              if (rtw_parse_wpa_ie(pbuf, wpa_ielen + 2, &group_cipher,
++                                   &pairwise_cipher, &is8021x) == _SUCCESS) {
+                       pnetwork->bcn_info.pairwise_cipher = pairwise_cipher;
+                       pnetwork->bcn_info.group_cipher = group_cipher;
+                       pnetwork->bcn_info.is_8021x = is8021x;
+                       ret = _SUCCESS;
+               }
+       } else {
+-              pbuf = rtw_get_wpa2_ie(&pnetwork->network.ies[12], &wpa_ielen, pnetwork->network.ie_length-12);
++              pbuf = rtw_get_wpa2_ie(&pnetwork->network.ies[12], &wpa_ielen,
++                                     pnetwork->network.ie_length - 12);
+               if (pbuf && (wpa_ielen > 0)) {
+-                      if (rtw_parse_wpa2_ie(pbuf, wpa_ielen+2, &group_cipher, &pairwise_cipher, &is8021x) == _SUCCESS) {
++                      if (rtw_parse_wpa2_ie(pbuf, wpa_ielen + 2, &group_cipher,
++                                            &pairwise_cipher, &is8021x) == _SUCCESS) {
+                               pnetwork->bcn_info.pairwise_cipher = pairwise_cipher;
+                               pnetwork->bcn_info.group_cipher = group_cipher;
+                               pnetwork->bcn_info.is_8021x = is8021x;
+@@ -1107,21 +1113,21 @@ u16 rtw_mcs_rate(u8 bw_40MHz, u8 short_G
+       u16 max_rate = 0;
+       if (MCS_rate[0] & BIT(7))
+-              max_rate = (bw_40MHz) ? ((short_GI)?1500:1350):((short_GI)?722:650);
++              max_rate = (bw_40MHz) ? ((short_GI) ? 1500 : 1350) : ((short_GI) ? 722 : 650);
+       else if (MCS_rate[0] & BIT(6))
+-              max_rate = (bw_40MHz) ? ((short_GI)?1350:1215):((short_GI)?650:585);
++              max_rate = (bw_40MHz) ? ((short_GI) ? 1350 : 1215) : ((short_GI) ? 650 : 585);
+       else if (MCS_rate[0] & BIT(5))
+-              max_rate = (bw_40MHz) ? ((short_GI)?1200:1080):((short_GI)?578:520);
++              max_rate = (bw_40MHz) ? ((short_GI) ? 1200 : 1080) : ((short_GI) ? 578 : 520);
+       else if (MCS_rate[0] & BIT(4))
+-              max_rate = (bw_40MHz) ? ((short_GI)?900:810):((short_GI)?433:390);
++              max_rate = (bw_40MHz) ? ((short_GI) ? 900 : 810) : ((short_GI) ? 433 : 390);
+       else if (MCS_rate[0] & BIT(3))
+-              max_rate = (bw_40MHz) ? ((short_GI)?600:540):((short_GI)?289:260);
++              max_rate = (bw_40MHz) ? ((short_GI) ? 600 : 540) : ((short_GI) ? 289 : 260);
+       else if (MCS_rate[0] & BIT(2))
+-              max_rate = (bw_40MHz) ? ((short_GI)?450:405):((short_GI)?217:195);
++              max_rate = (bw_40MHz) ? ((short_GI) ? 450 : 405) : ((short_GI) ? 217 : 195);
+       else if (MCS_rate[0] & BIT(1))
+-              max_rate = (bw_40MHz) ? ((short_GI)?300:270):((short_GI)?144:130);
++              max_rate = (bw_40MHz) ? ((short_GI) ? 300 : 270) : ((short_GI) ? 144 : 130);
+       else if (MCS_rate[0] & BIT(0))
+-              max_rate = (bw_40MHz) ? ((short_GI)?150:135):((short_GI)?72:65);
++              max_rate = (bw_40MHz) ? ((short_GI) ? 150 : 135) : ((short_GI) ? 72 : 65);
+       return max_rate;
+ }
diff --git a/queue-5.15/staging-rtl8723bs-remove-redundant-braces-in-if-statements.patch b/queue-5.15/staging-rtl8723bs-remove-redundant-braces-in-if-statements.patch
new file mode 100644 (file)
index 0000000..46e245a
--- /dev/null
@@ -0,0 +1,74 @@
+From stable+bounces-274663-greg=kroah.com@vger.kernel.org Wed Jul 15 03:34:17 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 14 Jul 2026 21:34:05 -0400
+Subject: staging: rtl8723bs: remove redundant braces in if statements
+To: stable@vger.kernel.org
+Cc: Sevinj Aghayeva <sevinj.aghayeva@gmail.com>, Greg Kroah-Hartman <gregkh@linuxfoundation.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260715013408.4110029-2-sashal@kernel.org>
+
+From: Sevinj Aghayeva <sevinj.aghayeva@gmail.com>
+
+[ Upstream commit 94579b02720bb63096312c4bc7694fd2f6df7cbb ]
+
+Adhere to Linux kernel coding style.
+
+Reported by checkpatch:
+
+WARNING: braces {} are not necessary for single statement blocks
+
+Signed-off-by: Sevinj Aghayeva <sevinj.aghayeva@gmail.com>
+Link: https://lore.kernel.org/r/20220331113245.GA425141@euclid
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Stable-dep-of: 1463ca3ec660 ("staging: rtl8723bs: fix OOB reads in rtw_get_sec_ie(), rtw_get_wapi_ie(), and rtw_get_wps_attr()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/staging/rtl8723bs/core/rtw_ieee80211.c |   12 ++++--------
+ 1 file changed, 4 insertions(+), 8 deletions(-)
+
+--- a/drivers/staging/rtl8723bs/core/rtw_ieee80211.c
++++ b/drivers/staging/rtl8723bs/core/rtw_ieee80211.c
+@@ -341,9 +341,8 @@ int rtw_generate_ie(struct registry_priv
+       ie = rtw_set_ie(ie, WLAN_EID_IBSS_PARAMS, 2, (u8 *)&(pdev_network->configuration.atim_window), &sz);
+-      if (rateLen > 8) {
++      if (rateLen > 8)
+               ie = rtw_set_ie(ie, WLAN_EID_EXT_SUPP_RATES, (rateLen - 8), (pdev_network->supported_rates + 8), &sz);
+-      }
+       /* HT Cap. */
+       if ((pregistrypriv->wireless_mode & WIRELESS_11_24N) &&
+@@ -372,9 +371,8 @@ unsigned char *rtw_get_wpa_ie(unsigned c
+               if (pbuf) {
+                       /* check if oui matches... */
+-                      if (memcmp((pbuf + 2), wpa_oui_type, sizeof(wpa_oui_type))) {
++                      if (memcmp((pbuf + 2), wpa_oui_type, sizeof(wpa_oui_type)))
+                               goto check_next_ie;
+-                      }
+                       /* check version... */
+                       memcpy((u8 *)&le_tmp, (pbuf + 6), sizeof(val16));
+@@ -499,9 +497,8 @@ int rtw_parse_wpa_ie(u8 *wpa_ie, int wpa
+       if (is_8021x) {
+               if (left >= 6) {
+                       pos += 2;
+-                      if (!memcmp(pos, SUITE_1X, 4)) {
++                      if (!memcmp(pos, SUITE_1X, 4))
+                               *is_8021x = 1;
+-                      }
+               }
+       }
+@@ -520,9 +517,8 @@ int rtw_parse_wpa2_ie(u8 *rsn_ie, int rs
+               return _FAIL;
+       }
+-      if ((*rsn_ie != WLAN_EID_RSN) || (*(rsn_ie+1) != (u8)(rsn_ie_len - 2))) {
++      if ((*rsn_ie != WLAN_EID_RSN) || (*(rsn_ie+1) != (u8)(rsn_ie_len - 2)))
+               return _FAIL;
+-      }
+       pos = rsn_ie;
+       pos += 4;
diff --git a/queue-5.15/staging-rtl8723bs-remove-redundant-else-branches.patch b/queue-5.15/staging-rtl8723bs-remove-redundant-else-branches.patch
new file mode 100644 (file)
index 0000000..2ebdb9c
--- /dev/null
@@ -0,0 +1,87 @@
+From stable+bounces-274662-greg=kroah.com@vger.kernel.org Wed Jul 15 03:34:17 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 14 Jul 2026 21:34:04 -0400
+Subject: staging: rtl8723bs: Remove redundant else branches.
+To: stable@vger.kernel.org
+Cc: Sevinj Aghayeva <sevinj.aghayeva@gmail.com>, Greg Kroah-Hartman <gregkh@linuxfoundation.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260715013408.4110029-1-sashal@kernel.org>
+
+From: Sevinj Aghayeva <sevinj.aghayeva@gmail.com>
+
+[ Upstream commit 4610e57a7d2edeef14aa852cd945f9920e1759da ]
+
+This patch fixes the following checkpatch.pl warning:
+
+WARNING: else is not generally useful after a break or return
+
+Signed-off-by: Sevinj Aghayeva <sevinj.aghayeva@gmail.com>
+Link: https://lore.kernel.org/r/20220329140904.GA3566@ubuntu
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Stable-dep-of: 1463ca3ec660 ("staging: rtl8723bs: fix OOB reads in rtw_get_sec_ie(), rtw_get_wapi_ie(), and rtw_get_wps_attr()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/staging/rtl8723bs/core/rtw_ieee80211.c |   27 ++++++++++---------------
+ 1 file changed, 11 insertions(+), 16 deletions(-)
+
+--- a/drivers/staging/rtl8723bs/core/rtw_ieee80211.c
++++ b/drivers/staging/rtl8723bs/core/rtw_ieee80211.c
+@@ -94,16 +94,14 @@ bool rtw_is_cckratesonly_included(u8 *ra
+ int rtw_check_network_type(unsigned char *rate, int ratelen, int channel)
+ {
+-      if (channel > 14) {
++      if (channel > 14)
+               return WIRELESS_INVALID;
+-      } else { /*  could be pure B, pure G, or B/G */
+-              if (rtw_is_cckratesonly_included(rate))
+-                      return WIRELESS_11B;
+-              else if (rtw_is_cckrates_included(rate))
+-                      return  WIRELESS_11BG;
+-              else
+-                      return WIRELESS_11G;
+-      }
++      /* could be pure B, pure G, or B/G */
++      if (rtw_is_cckratesonly_included(rate))
++              return WIRELESS_11B;
++      if (rtw_is_cckrates_included(rate))
++              return WIRELESS_11BG;
++      return WIRELESS_11G;
+ }
+ u8 *rtw_set_fixed_ie(unsigned char *pbuf, unsigned int len, unsigned char *source,
+@@ -155,10 +153,9 @@ u8 *rtw_get_ie(u8 *pbuf, signed int inde
+               if (*p == index) {
+                       *len = tmp;
+                       return p;
+-              } else {
+-                      p += (tmp + 2);
+-                      i += (tmp + 2);
+               }
++              p += (tmp + 2);
++              i += (tmp + 2);
+       }
+       return NULL;
+ }
+@@ -702,9 +699,8 @@ u8 *rtw_get_wps_ie(u8 *in_ie, uint in_le
+                       cnt += in_ie[cnt+1]+2;
+                       break;
+-              } else {
+-                      cnt += in_ie[cnt+1]+2; /* goto next */
+               }
++              cnt += in_ie[cnt+1]+2; /* goto next */
+       }
+       return wpsie_ptr;
+@@ -753,9 +749,8 @@ u8 *rtw_get_wps_attr(u8 *wps_ie, uint wp
+                               *len_attr = attr_len;
+                       break;
+-              } else {
+-                      attr_ptr += attr_len; /* goto next */
+               }
++              attr_ptr += attr_len; /* goto next */
+       }
+       return target_attr_ptr;
diff --git a/queue-5.15/treewide-switch-rename-to-timer_delete.patch b/queue-5.15/treewide-switch-rename-to-timer_delete.patch
new file mode 100644 (file)
index 0000000..68709b7
--- /dev/null
@@ -0,0 +1,37 @@
+From stable+bounces-277175-greg=kroah.com@vger.kernel.org Fri Jul 17 20:13:55 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 17 Jul 2026 14:11:58 -0400
+Subject: treewide: Switch/rename to timer_delete[_sync]()
+To: stable@vger.kernel.org
+Cc: Thomas Gleixner <tglx@linutronix.de>, Ingo Molnar <mingo@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260717181159.2086971-1-sashal@kernel.org>
+
+From: Thomas Gleixner <tglx@linutronix.de>
+
+[ Upstream commit 8fa7292fee5c5240402371ea89ab285ec856c916 ]
+
+timer_delete[_sync]() replaces del_timer[_sync](). Convert the whole tree
+over and remove the historical wrapper inlines.
+
+Conversion was done with coccinelle plus manual fixups where necessary.
+
+Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
+Signed-off-by: Ingo Molnar <mingo@kernel.org>
+Stable-dep-of: 75fe87e19d8a ("HID: appleir: fix UAF on pending key_up_timer in remove()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/hid/hid-appleir.c |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/hid/hid-appleir.c
++++ b/drivers/hid/hid-appleir.c
+@@ -319,7 +319,7 @@ static void appleir_remove(struct hid_de
+ {
+       struct appleir *appleir = hid_get_drvdata(hid);
+       hid_hw_stop(hid);
+-      del_timer_sync(&appleir->key_up_timer);
++      timer_delete_sync(&appleir->key_up_timer);
+ }
+ static const struct hid_device_id appleir_devices[] = {
diff --git a/queue-5.15/usb-atm-ueagle-atm-wait-for-pre-firmware-load-in-.disconnect.patch b/queue-5.15/usb-atm-ueagle-atm-wait-for-pre-firmware-load-in-.disconnect.patch
new file mode 100644 (file)
index 0000000..d6c73b5
--- /dev/null
@@ -0,0 +1,175 @@
+From stable+bounces-277533-greg=kroah.com@vger.kernel.org Mon Jul 20 04:26:04 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 19 Jul 2026 22:25:41 -0400
+Subject: usb: atm: ueagle-atm: wait for pre-firmware load in .disconnect()
+To: stable@vger.kernel.org
+Cc: Mauricio Faria de Oliveira <mfo@igalia.com>, stable <stable@kernel.org>, syzbot+ce1e5a1b4e086b43e56d@syzkaller.appspotmail.com, syzbot+306212936b13e520679d@syzkaller.appspotmail.com, syzbot+457452d30bcdda75ead2@syzkaller.appspotmail.com, Andrey Tsygunka <aitsygunka@yandex.ru>, Stanislaw Gruszka <stf_xl@wp.pl>, Greg Kroah-Hartman <gregkh@linuxfoundation.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260720022541.2927433-1-sashal@kernel.org>
+
+From: Mauricio Faria de Oliveira <mfo@igalia.com>
+
+[ Upstream commit e2674dfbed8a30d57e2bc872c4bfa6c3eec918bf ]
+
+ueagle-atm uses the asynchronous request_firmware_nowait() in .probe(),
+but does not wait for its completion, not even in .disconnect(); so, if the
+device is unplugged meanwhile, its teardown runs concurrently with that.
+
+Even though this inconsistency is worth addressing on its own, it has also
+triggered several bug reports in syzbot over the years (some auto-closed)
+where the firmware sysfs fallback mechanism (CONFIG_FW_LOADER_USER_HELPER)
+creates a firmware subdirectory in the device directory during its removal,
+which might hit unexpected conditions in kernfs, apparently, depending at
+which point the add and remove operations raced. (See links.)
+
+The pattern is:
+
+usb ?-?: Direct firmware load for ueagle-atm/eagle?.fw failed with error -2
+usb ?-?: Falling back to sysfs fallback for: ueagle-atm/eagle?.fw
+<ERROR>
+Call trace:
+ ...
+ kernfs_create_dir_ns
+ sysfs_create_dir_ns
+ create_dir
+ kobject_add_internal
+ kobject_add_varg
+ kobject_add
+ class_dir_create_and_add
+ get_device_parent
+ device_add
+ fw_load_sysfs_fallback
+ fw_load_from_user_helper
+ firmware_fallback_sysfs
+ _request_firmware
+ request_firmware_work_func
+ ...
+
+(Some variations are observed, after fw_load_sysfs_fallback(), e.g., [1].)
+
+While the kernfs side is being looked at, the ueagle-atm side can be fixed
+by waiting for the pre-firmware load in the .disconnect() handler.
+
+This change has a similar approach to previous work by Andrey Tsygunka [2]
+(wait_for_completion() in .disconnect()), but it is relatively different in
+design/implementation; using the Originally-by tag for credit assignment.
+
+This has been tested with:
+- synthetic reproducer to check the error path;
+- USB gadget (virtual device) to check the firmware upload path;
+- QEMU device emulator to check the device ID re-enumeration path;
+(The latter two were written by Claude; no other code/text in this commit.)
+
+Links (year first reported):
+ 2025 https://syzbot.org/bug?extid=ce1e5a1b4e086b43e56d
+ 2025 https://syzbot.org/bug?extid=9af8471255ac36e34fd4
+ 2024 https://syzbot.org/bug?extid=306212936b13e520679d
+ 2023 https://syzkaller.appspot.com/bug?extid=457452d30bcdda75ead2
+ 2022 https://syzbot.org/bug?extid=782984d6f1701b526edb
+ 2021 https://syzbot.org/bug?id=f3f221579f4ef7e9691281f3c6f56c05f83e8490
+ 2021 https://syzbot.org/bug?id=84d86f0d71394829df6fc53daf6642c045983881
+ 2021 https://syzbot.org/bug?id=3302dc1c0e2b9c94f2e8edb404eabc9267bc6f90
+
+[1] https://syzkaller.appspot.com/bug?extid=457452d30bcdda75ead2
+[2] https://lore.kernel.org/lkml/20250410093146.3776801-2-aitsygunka@yandex.ru/
+
+Cc: stable <stable@kernel.org>
+Reported-by: syzbot+ce1e5a1b4e086b43e56d@syzkaller.appspotmail.com
+Closes: https://syzbot.org/bug?extid=ce1e5a1b4e086b43e56d
+Reported-by: syzbot+306212936b13e520679d@syzkaller.appspotmail.com
+Closes: https://syzbot.org/bug?extid=306212936b13e520679d
+Reported-by: syzbot+457452d30bcdda75ead2@syzkaller.appspotmail.com
+Closes: https://syzkaller.appspot.com/bug?extid=457452d30bcdda75ead2
+Originally-by: Andrey Tsygunka <aitsygunka@yandex.ru>
+Fixes: b72458a80c75 ("[PATCH] USB: Eagle and ADI 930 usb adsl modem driver")
+Assisted-by: Claude:claude-opus-4.7 # usb gadget & qemu device for testing
+Signed-off-by: Mauricio Faria de Oliveira <mfo@igalia.com>
+Acked-by: Stanislaw Gruszka <stf_xl@wp.pl>
+Link: https://patch.msgid.link/20260526-ueagle-atm_req-fw-sync-v3-1-93c01961daaf@igalia.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/usb/atm/ueagle-atm.c |   36 +++++++++++++++++++++++++++++++-----
+ 1 file changed, 31 insertions(+), 5 deletions(-)
+
+--- a/drivers/usb/atm/ueagle-atm.c
++++ b/drivers/usb/atm/ueagle-atm.c
+@@ -599,7 +599,9 @@ static int uea_send_modem_cmd(struct usb
+ static void uea_upload_pre_firmware(const struct firmware *fw_entry,
+                                                               void *context)
+ {
+-      struct usb_device *usb = context;
++      struct usb_interface *intf = context;
++      struct usb_device *usb = interface_to_usbdev(intf);
++      struct completion *fw_done = usb_get_intfdata(intf);
+       const u8 *pfw;
+       u8 value;
+       u32 crc = 0;
+@@ -670,15 +672,17 @@ err_fw_corrupted:
+ err:
+       release_firmware(fw_entry);
+       uea_leaves(usb);
++      complete(fw_done);
+ }
+ /*
+  * uea_load_firmware - Load usb firmware for pre-firmware devices.
+  */
+-static int uea_load_firmware(struct usb_device *usb, unsigned int ver)
++static int uea_load_firmware(struct usb_interface *intf, unsigned int ver)
+ {
+       int ret;
+       char *fw_name = EAGLE_FIRMWARE;
++      struct usb_device *usb = interface_to_usbdev(intf);
+       uea_enters(usb);
+       uea_info(usb, "pre-firmware device, uploading firmware\n");
+@@ -702,7 +706,7 @@ static int uea_load_firmware(struct usb_
+       }
+       ret = request_firmware_nowait(THIS_MODULE, 1, fw_name, &usb->dev,
+-                                      GFP_KERNEL, usb,
++                                      GFP_KERNEL, intf,
+                                       uea_upload_pre_firmware);
+       if (ret)
+               uea_err(usb, "firmware %s is not available\n", fw_name);
+@@ -2598,8 +2602,23 @@ static int uea_probe(struct usb_interfac
+       usb_reset_device(usb);
+-      if (UEA_IS_PREFIRM(id))
+-              return uea_load_firmware(usb, UEA_CHIP_VERSION(id));
++      if (UEA_IS_PREFIRM(id)) {
++              struct completion *fw_done;
++
++              /* Wait for the firmware load to be done, in .disconnect() */
++              fw_done = kzalloc(sizeof(*fw_done), GFP_KERNEL);
++              if (!fw_done)
++                      return -ENOMEM;
++
++              init_completion(fw_done);
++              usb_set_intfdata(intf, fw_done);
++
++              ret = uea_load_firmware(intf, UEA_CHIP_VERSION(id));
++              if (ret)
++                      kfree(fw_done);
++
++              return ret;
++      }
+       ret = usbatm_usb_probe(intf, id, &uea_usbatm_driver);
+       if (ret == 0) {
+@@ -2630,6 +2649,13 @@ static void uea_disconnect(struct usb_in
+               usbatm_usb_disconnect(intf);
+               mutex_unlock(&uea_mutex);
+               uea_info(usb, "ADSL device removed\n");
++      } else if (usb->config->desc.bNumInterfaces == 1) {
++              struct completion *fw_done = usb_get_intfdata(intf);
++
++              uea_dbg(usb, "pre-firmware device, waiting firmware upload\n");
++              wait_for_completion(fw_done);
++              uea_dbg(usb, "pre-firmware device, finished waiting\n");
++              kfree(fw_done);
+       }
+       uea_leaves(usb);
diff --git a/queue-5.15/usb-gadget-f_fs-initialize-reset_work-at-allocation-time.patch b/queue-5.15/usb-gadget-f_fs-initialize-reset_work-at-allocation-time.patch
new file mode 100644 (file)
index 0000000..ffa4903
--- /dev/null
@@ -0,0 +1,94 @@
+From sashal@kernel.org Mon Jul 20 15:55:10 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Jul 2026 09:55:05 -0400
+Subject: usb: gadget: f_fs: initialize reset_work at allocation time
+To: stable@vger.kernel.org
+Cc: "Tyler Baker" <tyler.baker@oss.qualcomm.com>, stable <stable@kernel.org>, "Loic Poulain" <loic.poulain@oss.qualcomm.com>, "Dmitry Baryshkov" <dmitry.baryshkov@oss.qualcomm.com>, "Srinivas Kandagatla" <srinivas.kandagatla@oss.qualcomm.com>, "Peter Chen" <peter.chen@kernel.org>, "MichaÅ‚ Nazarewicz" <mina86@mina86.com>, "Greg Kroah-Hartman" <gregkh@linuxfoundation.org>, "Sasha Levin" <sashal@kernel.org>
+Message-ID: <20260720135505.1350249-1-sashal@kernel.org>
+
+From: Tyler Baker <tyler.baker@oss.qualcomm.com>
+
+[ Upstream commit 3137b243c93982fe3460335e12f9247739766e10 ]
+
+ffs_fs_kill_sb() unconditionally calls cancel_work_sync() on
+ffs->reset_work when a functionfs instance is unmounted:
+
+       ffs_data_reset(ffs);
+       cancel_work_sync(&ffs->reset_work);
+
+However ffs->reset_work is only ever initialized via INIT_WORK() in
+ffs_func_set_alt() and ffs_func_disable(), and only on the
+FFS_DEACTIVATED path. That state is reached solely by ffs_data_closed()
+when the instance is mounted with the "no_disconnect" option, so for the
+common case (no "no_disconnect", or mounted and unmounted without ever
+being deactivated) reset_work is never initialized.
+
+ffs_data_new() allocates the ffs_data with kzalloc_obj() and does not
+initialize reset_work, and ffs_data_reset()/ffs_data_clear() do not touch
+it either, so reset_work.func is left NULL. cancel_work_sync() on such a
+work then trips the WARN_ON(!work->func) guard in __flush_work():
+
+  WARNING: kernel/workqueue.c:4301 at __flush_work+0x330/0x360, CPU#3: umount
+  Call trace:
+   __flush_work
+   cancel_work_sync
+   ffs_fs_kill_sb [usb_f_fs]
+   deactivate_locked_super
+   deactivate_super
+   cleanup_mnt
+   __cleanup_mnt
+   task_work_run
+   exit_to_user_mode_loop
+   el0_svc
+
+On older kernels cancel_work_sync() on a zero-initialized work struct was
+a silent no-op, which hid the missing initialization.
+
+Initialize reset_work once in ffs_data_new() so it is always valid for
+the lifetime of the ffs_data, and drop the now-redundant INIT_WORK()
+calls from the two deactivation paths.
+
+Fixes: 18d6b32fca38 ("usb: gadget: f_fs: add "no_disconnect" mode")
+Cc: stable <stable@kernel.org>
+Signed-off-by: Tyler Baker <tyler.baker@oss.qualcomm.com>
+Cc: Loic Poulain <loic.poulain@oss.qualcomm.com>
+Cc: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
+Cc: Srinivas Kandagatla <srinivas.kandagatla@oss.qualcomm.com>
+Tested-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
+Reviewed-by: Peter Chen <peter.chen@kernel.org>
+Acked-by: MichaÅ‚ Nazarewicz <mina86@mina86.com>
+Link: https://patch.msgid.link/20260609193635.2284430-1-tyler.baker@oss.qualcomm.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+[ dropped the ffs_func_disable hunk since 6.6 predates the disable-body split and routes disable through ffs_func_set_alt, so removing the single lazy INIT_WORK there covers both paths ]
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/gadget/function/f_fs.c |    3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- a/drivers/usb/gadget/function/f_fs.c
++++ b/drivers/usb/gadget/function/f_fs.c
+@@ -255,6 +255,7 @@ static int ffs_acquire_dev(const char *d
+ static void ffs_release_dev(struct ffs_dev *ffs_dev);
+ static int ffs_ready(struct ffs_data *ffs);
+ static void ffs_closed(struct ffs_data *ffs);
++static void ffs_reset_work(struct work_struct *work);
+ /* Misc helper functions ****************************************************/
+@@ -1786,6 +1787,7 @@ static struct ffs_data *ffs_data_new(con
+       init_waitqueue_head(&ffs->ev.waitq);
+       init_waitqueue_head(&ffs->wait);
+       init_completion(&ffs->ep0req_completion);
++      INIT_WORK(&ffs->reset_work, ffs_reset_work);
+       /* XXX REVISIT need to update it in some places, or do we? */
+       ffs->ev.can_stall = 1;
+@@ -3342,7 +3344,6 @@ static int ffs_func_set_alt(struct usb_f
+       if (ffs->state == FFS_DEACTIVATED) {
+               ffs->state = FFS_CLOSING;
+-              INIT_WORK(&ffs->reset_work, ffs_reset_work);
+               schedule_work(&ffs->reset_work);
+               return -ENODEV;
+       }
diff --git a/queue-5.15/usb-gadget-f_fs-tie-read_buffer-lifetime-to-ffs_epfile.patch b/queue-5.15/usb-gadget-f_fs-tie-read_buffer-lifetime-to-ffs_epfile.patch
new file mode 100644 (file)
index 0000000..63b9e5d
--- /dev/null
@@ -0,0 +1,55 @@
+From stable+bounces-278071-greg=kroah.com@vger.kernel.org Mon Jul 20 17:49:03 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Jul 2026 11:48:25 -0400
+Subject: usb: gadget: f_fs: Tie read_buffer lifetime to ffs_epfile
+To: stable@vger.kernel.org
+Cc: Neill Kapron <nkapron@google.com>, stable <stable@kernel.org>, Greg Kroah-Hartman <gregkh@linuxfoundation.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260720154825.2322611-1-sashal@kernel.org>
+
+From: Neill Kapron <nkapron@google.com>
+
+[ Upstream commit 8bdcf96eb135aebacac319667f87db034fb38406 ]
+
+Currently, ffs_epfile_release unconditionally frees the endpoint's
+read_buffer when a file descriptor is closed. If userspace explicitly
+opens the endpoint multiple times and closes one, the read_buffer is
+destroyed. This can lead to silent data loss if other file descriptors
+are still actively reading from the endpoint.
+
+By tying the lifetime of the read_buffer to the ffs_epfile structure itself
+(which is destroyed when the functionfs instance is torn down in
+ffs_epfiles_destroy), we eliminate the brittle dependency on open/release
+calls while correctly matching the conceptual lifetime of unread data on
+the hardware endpoint.
+
+Fixes: 9353afbbfa7b ("usb: gadget: f_fs: buffer data from â€˜oversized’ OUT requests")
+Cc: stable <stable@kernel.org>
+Assisted-by: Antigravity:gemini-3.1-pro
+Signed-off-by: Neill Kapron <nkapron@google.com>
+Link: https://patch.msgid.link/20260619040609.4010746-3-nkapron@google.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+[ adjusted context in ffs_epfiles_destroy() since 6.12 lacks the simple_remove_by_name() rework and still uses dentry-based cleanup ]
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/gadget/function/f_fs.c |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/usb/gadget/function/f_fs.c
++++ b/drivers/usb/gadget/function/f_fs.c
+@@ -1292,7 +1292,6 @@ ffs_epfile_release(struct inode *inode,
+       ENTER();
+-      __ffs_epfile_read_buffer_free(epfile);
+       ffs_data_closed(epfile->ffs);
+       return 0;
+@@ -1957,6 +1956,7 @@ static void ffs_epfiles_destroy(struct f
+       for (; count; --count, ++epfile) {
+               BUG_ON(mutex_is_locked(&epfile->mutex));
++              __ffs_epfile_read_buffer_free(epfile);
+               if (epfile->dentry) {
+                       d_delete(epfile->dentry);
+                       dput(epfile->dentry);
diff --git a/queue-5.15/usb-iowarrior-remove-inherent-race-with-minor-number.patch b/queue-5.15/usb-iowarrior-remove-inherent-race-with-minor-number.patch
new file mode 100644 (file)
index 0000000..fd31bdd
--- /dev/null
@@ -0,0 +1,109 @@
+From sashal@kernel.org Sun Jul 19 18:31:17 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 19 Jul 2026 12:31:12 -0400
+Subject: usb: iowarrior: remove inherent race with minor number
+To: stable@vger.kernel.org
+Cc: Oliver Neukum <oneukum@suse.com>, Greg Kroah-Hartman <gregkh@linuxfoundation.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260719163113.566274-1-sashal@kernel.org>
+
+From: Oliver Neukum <oneukum@suse.com>
+
+[ Upstream commit 56dd29088c9d9510c48a8ebad2465248fde36551 ]
+
+The driver saves the minor number it gets upon registration
+in its descriptor for debugging purposes. However, there is
+inevitably a window between registration and saving the correct
+minor in a descriptor. During this window the debugging output
+will be wrong.
+As wrong debug output is worse than no debug output, just
+remove it.
+
+Signed-off-by: Oliver Neukum <oneukum@suse.com>
+Link: https://patch.msgid.link/20260312094619.1590556-1-oneukum@suse.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Stable-dep-of: c602254ba4c1 ("USB: iowarrior: fix use-after-free on disconnect race")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/misc/iowarrior.c |   17 +++--------------
+ 1 file changed, 3 insertions(+), 14 deletions(-)
+
+--- a/drivers/usb/misc/iowarrior.c
++++ b/drivers/usb/misc/iowarrior.c
+@@ -74,7 +74,6 @@ struct iowarrior {
+       struct mutex mutex;                     /* locks this structure */
+       struct usb_device *udev;                /* save off the usb device pointer */
+       struct usb_interface *interface;        /* the interface for this device */
+-      unsigned char minor;                    /* the starting minor number for this device */
+       struct usb_endpoint_descriptor *int_out_endpoint;       /* endpoint for reading (needed for IOW56 only) */
+       struct usb_endpoint_descriptor *int_in_endpoint;        /* endpoint for reading */
+       struct urb *int_in_urb;         /* the urb for reading data */
+@@ -246,7 +245,6 @@ static void iowarrior_write_callback(str
+  */
+ static inline void iowarrior_delete(struct iowarrior *dev)
+ {
+-      dev_dbg(&dev->interface->dev, "minor %d\n", dev->minor);
+       kfree(dev->int_in_buffer);
+       usb_free_urb(dev->int_in_urb);
+       kfree(dev->read_queue);
+@@ -297,9 +295,6 @@ static ssize_t iowarrior_read(struct fil
+               goto exit;
+       }
+-      dev_dbg(&dev->interface->dev, "minor %d, count = %zd\n",
+-              dev->minor, count);
+-
+       /* read count must be packet size (+ time stamp) */
+       if ((count != dev->report_size)
+           && (count != (dev->report_size + 1))) {
+@@ -379,8 +374,6 @@ static ssize_t iowarrior_write(struct fi
+               retval = -ENODEV;
+               goto exit;
+       }
+-      dev_dbg(&dev->interface->dev, "minor %d, count = %zd\n",
+-              dev->minor, count);
+       /* if count is 0 we're already done */
+       if (count == 0) {
+               retval = 0;
+@@ -523,9 +516,6 @@ static long iowarrior_ioctl(struct file
+               goto error_out;
+       }
+-      dev_dbg(&dev->interface->dev, "minor %d, cmd 0x%.4x, arg %ld\n",
+-              dev->minor, cmd, arg);
+-
+       retval = 0;
+       io_res = 0;
+       switch (cmd) {
+@@ -672,8 +662,6 @@ static int iowarrior_release(struct inod
+       if (!dev)
+               return -ENODEV;
+-      dev_dbg(&dev->interface->dev, "minor %d\n", dev->minor);
+-
+       /* lock our device */
+       mutex_lock(&dev->mutex);
+@@ -776,6 +764,7 @@ static int iowarrior_probe(struct usb_in
+       struct usb_host_interface *iface_desc;
+       int retval = -ENOMEM;
+       int res;
++      int minor;
+       /* allocate memory for our device state and initialize it */
+       dev = kzalloc(sizeof(struct iowarrior), GFP_KERNEL);
+@@ -891,12 +880,12 @@ static int iowarrior_probe(struct usb_in
+               goto error;
+       }
+-      dev->minor = interface->minor;
++      minor = interface->minor;
+       /* let the user know what node this device is now attached to */
+       dev_info(&interface->dev, "IOWarrior product=0x%x, serial=%s interface=%d "
+                "now attached to iowarrior%d\n", dev->product_id, dev->chip_serial,
+-               iface_desc->desc.bInterfaceNumber, dev->minor - IOWARRIOR_MINOR_BASE);
++               iface_desc->desc.bInterfaceNumber, minor - IOWARRIOR_MINOR_BASE);
+       return retval;
+ error:
diff --git a/queue-5.15/usb-typec-tcpm-fix-vdm-type-for-enter-mode-commands.patch b/queue-5.15/usb-typec-tcpm-fix-vdm-type-for-enter-mode-commands.patch
new file mode 100644 (file)
index 0000000..fdc31d1
--- /dev/null
@@ -0,0 +1,39 @@
+From stable+bounces-277534-greg=kroah.com@vger.kernel.org Mon Jul 20 04:25:52 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 19 Jul 2026 22:25:44 -0400
+Subject: usb: typec: tcpm: Fix VDM type for Enter Mode commands
+To: stable@vger.kernel.org
+Cc: Andy Yan <andyshrk@163.com>, stable <stable@kernel.org>, Heikki Krogerus <heikki.krogerus@linux.intel.com>, Greg Kroah-Hartman <gregkh@linuxfoundation.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260720022544.2927635-1-sashal@kernel.org>
+
+From: Andy Yan <andyshrk@163.com>
+
+[ Upstream commit 9cff680e47632b7723cb19f9c5e63669063c3417 ]
+
+VDO() second parameter is VDM type (bit 15): 1 for SVDM, 0 for UVDM.
+Using 'vdo ? 2 : 1' corrupts SVID low bit when vdo is non-NULL
+(2 << 15 = BIT(16)). Enter Mode is always SVDM, hardcode to 1.
+
+Fixes: 8face9aa57c8 ("usb: typec: Add parameter for the VDO to typec_altmode_enter()")
+Cc: stable <stable@kernel.org>
+Signed-off-by: Andy Yan <andyshrk@163.com>
+Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+Link: https://patch.msgid.link/20260604105059.18750-1-andyshrk@163.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/usb/typec/tcpm/tcpm.c |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/usb/typec/tcpm/tcpm.c
++++ b/drivers/usb/typec/tcpm/tcpm.c
+@@ -2223,7 +2223,7 @@ static int tcpm_altmode_enter(struct typ
+       if (svdm_version < 0)
+               return svdm_version;
+-      header = VDO(altmode->svid, vdo ? 2 : 1, svdm_version, CMD_ENTER_MODE);
++      header = VDO(altmode->svid, 1, svdm_version, CMD_ENTER_MODE);
+       header |= VDO_OPOS(altmode->mode);
+       tcpm_queue_vdm_unlocked(port, header, vdo, vdo ? 1 : 0);
diff --git a/queue-5.15/usb-typec-ucsi-ccg-fix-use-after-free-of-ucsi-on-remove.patch b/queue-5.15/usb-typec-ucsi-ccg-fix-use-after-free-of-ucsi-on-remove.patch
new file mode 100644 (file)
index 0000000..09fb762
--- /dev/null
@@ -0,0 +1,56 @@
+From stable+bounces-277532-greg=kroah.com@vger.kernel.org Mon Jul 20 04:26:01 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 19 Jul 2026 22:25:37 -0400
+Subject: usb: typec: ucsi: ccg: Fix use-after-free of ucsi on remove
+To: stable@vger.kernel.org
+Cc: Fan Wu <fanwu01@zju.edu.cn>, stable <stable@kernel.org>, Heikki Krogerus <heikki.krogerus@linux.intel.com>, Greg Kroah-Hartman <gregkh@linuxfoundation.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260720022537.2927284-5-sashal@kernel.org>
+
+From: Fan Wu <fanwu01@zju.edu.cn>
+
+[ Upstream commit 1f0bdc2884b67de337215079bba166df0cdf4ac5 ]
+
+The threaded IRQ handler ccg_irq_handler() calls ucsi_notify_common(),
+which on a connector-change event calls ucsi_connector_change() and
+schedules connector work.  In ucsi_ccg_remove(), ucsi_destroy() frees
+uc->ucsi (kfree) before free_irq() is called, so a handler invocation
+already in flight may access the freed object after ucsi_destroy().
+
+  CPU 0 (remove)            | CPU 1 (threaded IRQ)
+    ucsi_destroy(uc->ucsi)  |   ccg_irq_handler()
+      kfree(ucsi) // FREE   |     ucsi_notify_common(uc->ucsi) // USE
+
+Move free_irq() before ucsi_destroy() in the remove path.  It is kept
+after ucsi_unregister(): ucsi_unregister() cancels connector work whose
+handler issues GET_CONNECTOR_STATUS through ucsi_send_command_common(),
+which waits for a completion that is signalled from the IRQ handler, so
+the IRQ must stay active until that work has been cancelled.
+
+The probe error path already orders free_irq() before ucsi_destroy().
+
+This bug was found by static analysis.
+
+Fixes: e32fd989ac1c ("usb: typec: ucsi: ccg: Move to the new API")
+Cc: stable <stable@kernel.org>
+Signed-off-by: Fan Wu <fanwu01@zju.edu.cn>
+Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+Link: https://patch.msgid.link/20260616132011.103279-1-fanwu01@zju.edu.cn
+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/usb/typec/ucsi/ucsi_ccg.c |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/usb/typec/ucsi/ucsi_ccg.c
++++ b/drivers/usb/typec/ucsi/ucsi_ccg.c
+@@ -1418,8 +1418,8 @@ static int ucsi_ccg_remove(struct i2c_cl
+       cancel_work_sync(&uc->work);
+       pm_runtime_disable(uc->dev);
+       ucsi_unregister(uc->ucsi);
+-      ucsi_destroy(uc->ucsi);
+       free_irq(uc->irq, uc);
++      ucsi_destroy(uc->ucsi);
+       return 0;
+ }
diff --git a/queue-5.15/vfio-pci-release-the-vga-arbiter-client-on-register_device-failure.patch b/queue-5.15/vfio-pci-release-the-vga-arbiter-client-on-register_device-failure.patch
new file mode 100644 (file)
index 0000000..9f8eaf7
--- /dev/null
@@ -0,0 +1,46 @@
+From stable+bounces-274248-greg=kroah.com@vger.kernel.org Tue Jul 14 15:41:49 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 14 Jul 2026 09:36:03 -0400
+Subject: vfio/pci: Release the VGA arbiter client on register_device() failure
+To: stable@vger.kernel.org
+Cc: Alex Williamson <alex.williamson@nvidia.com>, Kevin Tian <kevin.tian@intel.com>, Alex Williamson <alex@shazbot.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260714133603.2673230-1-sashal@kernel.org>
+
+From: Alex Williamson <alex.williamson@nvidia.com>
+
+[ Upstream commit daedde7f024ecf88bc8e832ed40cf2c795f0796a ]
+
+The re-order in the Fixes commit below displaced vfio_pci_vga_init() as
+the last failure point of what is now vfio_pci_core_register_device()
+without introducing an unwind for the VGA arbiter registration.
+
+In current kernels this is mostly benign because vfio_pci_set_decode()
+only uses pci_dev state, but the original failure path could leave a
+callback with a freed vdev cookie.  The stale registration also becomes
+unsafe again once the callback follows drvdata to the vfio device.
+
+Add the required VGA unwind callout.
+
+Fixes: 4aeec3984ddc ("vfio/pci: Re-order vfio_pci_probe()")
+Cc: stable@vger.kernel.org
+Assisted-by: Claude:claude-opus-4-8
+Signed-off-by: Alex Williamson <alex.williamson@nvidia.com>
+Reviewed-by: Kevin Tian <kevin.tian@intel.com>
+Link: https://lore.kernel.org/r/20260615191241.688297-3-alex.williamson@nvidia.com
+Signed-off-by: Alex Williamson <alex@shazbot.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/vfio/pci/vfio_pci_core.c |    1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/vfio/pci/vfio_pci_core.c
++++ b/drivers/vfio/pci/vfio_pci_core.c
+@@ -1899,6 +1899,7 @@ int vfio_pci_core_register_device(struct
+ out_power:
+       if (!disable_idle_d3)
+               vfio_pci_set_power_state(vdev, PCI_D0);
++      vfio_pci_vga_uninit(vdev);
+ out_vf:
+       vfio_pci_vf_uninit(vdev);
+ out_group_put:
diff --git a/queue-5.15/x.509-fix-validation-of-asn.1-certificate-header.patch b/queue-5.15/x.509-fix-validation-of-asn.1-certificate-header.patch
new file mode 100644 (file)
index 0000000..21c3d47
--- /dev/null
@@ -0,0 +1,44 @@
+From stable+bounces-276824-greg=kroah.com@vger.kernel.org Thu Jul 16 21:49:45 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 16 Jul 2026 15:49:38 -0400
+Subject: X.509: Fix validation of ASN.1 certificate header
+To: stable@vger.kernel.org
+Cc: Lukas Wunner <lukas@wunner.de>, Sashiko <sashiko-bot@kernel.org>, Ignat Korchagin <ignat@linux.win>, Alistair Francis <alistair.francis@wdc.com>, Herbert Xu <herbert@gondor.apana.org.au>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260716194938.1111666-1-sashal@kernel.org>
+
+From: Lukas Wunner <lukas@wunner.de>
+
+[ Upstream commit 3b626ba431c4501512ad07549310685e07fe4706 ]
+
+x509_load_certificate_list() seeks to enforce that a certificate starts
+with 0x30 0x82 (ASN.1 SEQUENCE tag followed by a length of more than 256
+and less than 65535 bytes).
+
+But it only enforces that *either* of those two byte values are present,
+instead of checking for the *conjunction* of the two values.  Fix it.
+
+Fixes: 631cc66eb9ea ("MODSIGN: Provide module signing public keys to the kernel")
+Reported-by: Sashiko <sashiko-bot@kernel.org>
+Closes: https://lore.kernel.org/r/20260508033917.B5873C2BCB0@smtp.kernel.org/
+Signed-off-by: Lukas Wunner <lukas@wunner.de>
+Cc: stable@vger.kernel.org # v3.7+
+Reviewed-by: Ignat Korchagin <ignat@linux.win>
+Reviewed-by: Alistair Francis <alistair.francis@wdc.com>
+Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ certs/common.c |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/certs/common.c
++++ b/certs/common.c
+@@ -20,7 +20,7 @@ int load_certificate_list(const u8 cert_
+                */
+               if (end - p < 4)
+                       goto dodgy_cert;
+-              if (p[0] != 0x30 &&
++              if (p[0] != 0x30 ||
+                   p[1] != 0x82)
+                       goto dodgy_cert;
+               plen = (p[2] << 8) | p[3];
diff --git a/queue-5.15/xdp-introduce-flags-field-in-xdp_buff-xdp_frame.patch b/queue-5.15/xdp-introduce-flags-field-in-xdp_buff-xdp_frame.patch
new file mode 100644 (file)
index 0000000..6214f4c
--- /dev/null
@@ -0,0 +1,109 @@
+From stable+bounces-277353-greg=kroah.com@vger.kernel.org Sat Jul 18 18:33:24 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 18 Jul 2026 12:31:11 -0400
+Subject: xdp: introduce flags field in xdp_buff/xdp_frame
+To: stable@vger.kernel.org
+Cc: Lorenzo Bianconi <lorenzo@kernel.org>, Toke Hoiland-Jorgensen <toke@redhat.com>, John Fastabend <john.fastabend@gmail.com>, Jesper Dangaard Brouer <brouer@redhat.com>, Alexei Starovoitov <ast@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260718163112.3339166-1-sashal@kernel.org>
+
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+
+[ Upstream commit 2e88d4ff03013937028f5397268b21e10cf68713 ]
+
+Introduce flags field in xdp_frame and xdp_buffer data structures
+to define additional buffer features. At the moment the only
+supported buffer feature is frags bit (XDP_FLAGS_HAS_FRAGS).
+frags bit is used to specify if this is a linear buffer
+(XDP_FLAGS_HAS_FRAGS not set) or a frags frame (XDP_FLAGS_HAS_FRAGS
+set). In the latter case the driver is expected to initialize the
+skb_shared_info structure at the end of the first buffer to link together
+subsequent buffers belonging to the same frame.
+
+Acked-by: Toke Hoiland-Jorgensen <toke@redhat.com>
+Acked-by: John Fastabend <john.fastabend@gmail.com>
+Acked-by: Jesper Dangaard Brouer <brouer@redhat.com>
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Link: https://lore.kernel.org/r/e389f14f3a162c0a5bc6a2e1aa8dd01a90be117d.1642758637.git.lorenzo@kernel.org
+Signed-off-by: Alexei Starovoitov <ast@kernel.org>
+Stable-dep-of: aa496720618f ("bpf: Reject fragmented frames in devmap")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ include/net/xdp.h |   29 +++++++++++++++++++++++++++++
+ 1 file changed, 29 insertions(+)
+
+--- a/include/net/xdp.h
++++ b/include/net/xdp.h
+@@ -66,6 +66,10 @@ struct xdp_txq_info {
+       struct net_device *dev;
+ };
++enum xdp_buff_flags {
++      XDP_FLAGS_HAS_FRAGS     = BIT(0), /* non-linear xdp buff */
++};
++
+ struct xdp_buff {
+       void *data;
+       void *data_end;
+@@ -74,13 +78,30 @@ struct xdp_buff {
+       struct xdp_rxq_info *rxq;
+       struct xdp_txq_info *txq;
+       u32 frame_sz; /* frame size to deduce data_hard_end/reserved tailroom*/
++      u32 flags; /* supported values defined in xdp_buff_flags */
+ };
++static __always_inline bool xdp_buff_has_frags(struct xdp_buff *xdp)
++{
++      return !!(xdp->flags & XDP_FLAGS_HAS_FRAGS);
++}
++
++static __always_inline void xdp_buff_set_frags_flag(struct xdp_buff *xdp)
++{
++      xdp->flags |= XDP_FLAGS_HAS_FRAGS;
++}
++
++static __always_inline void xdp_buff_clear_frags_flag(struct xdp_buff *xdp)
++{
++      xdp->flags &= ~XDP_FLAGS_HAS_FRAGS;
++}
++
+ static __always_inline void
+ xdp_init_buff(struct xdp_buff *xdp, u32 frame_sz, struct xdp_rxq_info *rxq)
+ {
+       xdp->frame_sz = frame_sz;
+       xdp->rxq = rxq;
++      xdp->flags = 0;
+ }
+ static __always_inline void
+@@ -122,8 +143,14 @@ struct xdp_frame {
+        */
+       struct xdp_mem_info mem;
+       struct net_device *dev_rx; /* used by cpumap */
++      u32 flags; /* supported values defined in xdp_buff_flags */
+ };
++static __always_inline bool xdp_frame_has_frags(struct xdp_frame *frame)
++{
++      return !!(frame->flags & XDP_FLAGS_HAS_FRAGS);
++}
++
+ #define XDP_BULK_QUEUE_SIZE   16
+ struct xdp_frame_bulk {
+       int count;
+@@ -180,6 +207,7 @@ void xdp_convert_frame_to_buff(struct xd
+       xdp->data_end = frame->data + frame->len;
+       xdp->data_meta = frame->data - frame->metasize;
+       xdp->frame_sz = frame->frame_sz;
++      xdp->flags = frame->flags;
+ }
+ static inline
+@@ -206,6 +234,7 @@ int xdp_update_frame_from_buff(struct xd
+       xdp_frame->headroom = headroom - sizeof(*xdp_frame);
+       xdp_frame->metasize = metasize;
+       xdp_frame->frame_sz = xdp->frame_sz;
++      xdp_frame->flags = xdp->flags;
+       return 0;
+ }
diff --git a/queue-5.15/xfs-remove-dead-code.patch b/queue-5.15/xfs-remove-dead-code.patch
new file mode 100644 (file)
index 0000000..53b1691
--- /dev/null
@@ -0,0 +1,113 @@
+From stable+bounces-277236-greg=kroah.com@vger.kernel.org Sat Jul 18 03:33:48 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 17 Jul 2026 21:33:34 -0400
+Subject: xfs: Remove dead code
+To: stable@vger.kernel.org
+Cc: Jiapeng Chong <jiapeng.chong@linux.alibaba.com>, Abaci Robot <abaci@linux.alibaba.com>, "Darrick J. Wong" <djwong@kernel.org>, Dave Chinner <dchinner@redhat.com>, Dave Chinner <david@fromorbit.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260718013335.2621394-2-sashal@kernel.org>
+
+From: Jiapeng Chong <jiapeng.chong@linux.alibaba.com>
+
+[ Upstream commit e62c720817597f259b81f1ff004eb042293bf046 ]
+
+Remove tht entire xlog_recover_check_summary() function, this entire
+function is dead code and has been for 12 years.
+
+Reported-by: Abaci Robot <abaci@linux.alibaba.com>
+Signed-off-by: Jiapeng Chong <jiapeng.chong@linux.alibaba.com>
+Reviewed-by: Darrick J. Wong <djwong@kernel.org>
+Reviewed-by: Dave Chinner <dchinner@redhat.com>
+Signed-off-by: Dave Chinner <david@fromorbit.com>
+Stable-dep-of: cc9af5e461ea ("xfs: use null daddr for unset first bad log block")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/xfs/xfs_log_recover.c |   59 -----------------------------------------------
+ 1 file changed, 59 deletions(-)
+
+--- a/fs/xfs/xfs_log_recover.c
++++ b/fs/xfs/xfs_log_recover.c
+@@ -39,13 +39,6 @@ STATIC int
+ xlog_clear_stale_blocks(
+       struct xlog     *,
+       xfs_lsn_t);
+-#if defined(DEBUG)
+-STATIC void
+-xlog_recover_check_summary(
+-      struct xlog *);
+-#else
+-#define       xlog_recover_check_summary(log)
+-#endif
+ STATIC int
+ xlog_do_recovery_pass(
+         struct xlog *, xfs_daddr_t, xfs_daddr_t, int, xfs_daddr_t *);
+@@ -3382,8 +3375,6 @@ xlog_do_recover(
+       }
+       mp->m_alloc_set_aside = xfs_alloc_set_aside(mp);
+-      xlog_recover_check_summary(log);
+-
+       /* Normal transactions can now occur */
+       clear_bit(XLOG_ACTIVE_RECOVERY, &log->l_opstate);
+       return 0;
+@@ -3526,7 +3517,6 @@ xlog_recover_finish(
+       }
+       xlog_recover_process_iunlinks(log);
+-      xlog_recover_check_summary(log);
+       /*
+        * Recover any CoW staging blocks that are still referenced by the
+@@ -3560,52 +3550,3 @@ xlog_recover_cancel(
+               xlog_recover_cancel_intents(log);
+ }
+-#if defined(DEBUG)
+-/*
+- * Read all of the agf and agi counters and check that they
+- * are consistent with the superblock counters.
+- */
+-STATIC void
+-xlog_recover_check_summary(
+-      struct xlog             *log)
+-{
+-      struct xfs_mount        *mp = log->l_mp;
+-      struct xfs_perag        *pag;
+-      struct xfs_buf          *agfbp;
+-      struct xfs_buf          *agibp;
+-      xfs_agnumber_t          agno;
+-      uint64_t                freeblks;
+-      uint64_t                itotal;
+-      uint64_t                ifree;
+-      int                     error;
+-
+-      freeblks = 0LL;
+-      itotal = 0LL;
+-      ifree = 0LL;
+-      for_each_perag(mp, agno, pag) {
+-              error = xfs_read_agf(mp, NULL, pag->pag_agno, 0, &agfbp);
+-              if (error) {
+-                      xfs_alert(mp, "%s agf read failed agno %d error %d",
+-                                              __func__, pag->pag_agno, error);
+-              } else {
+-                      struct xfs_agf  *agfp = agfbp->b_addr;
+-
+-                      freeblks += be32_to_cpu(agfp->agf_freeblks) +
+-                                  be32_to_cpu(agfp->agf_flcount);
+-                      xfs_buf_relse(agfbp);
+-              }
+-
+-              error = xfs_read_agi(mp, NULL, pag->pag_agno, &agibp);
+-              if (error) {
+-                      xfs_alert(mp, "%s agi read failed agno %d error %d",
+-                                              __func__, pag->pag_agno, error);
+-              } else {
+-                      struct xfs_agi  *agi = agibp->b_addr;
+-
+-                      itotal += be32_to_cpu(agi->agi_count);
+-                      ifree += be32_to_cpu(agi->agi_freecount);
+-                      xfs_buf_relse(agibp);
+-              }
+-      }
+-}
+-#endif /* DEBUG */
diff --git a/queue-5.15/xfs-remove-redundant-assignment-of-mp.patch b/queue-5.15/xfs-remove-redundant-assignment-of-mp.patch
new file mode 100644 (file)
index 0000000..f4e37bf
--- /dev/null
@@ -0,0 +1,43 @@
+From stable+bounces-277235-greg=kroah.com@vger.kernel.org Sat Jul 18 03:33:43 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 17 Jul 2026 21:33:33 -0400
+Subject: xfs: Remove redundant assignment of mp
+To: stable@vger.kernel.org
+Cc: Jiapeng Chong <jiapeng.chong@linux.alibaba.com>, Abaci Robot <abaci@linux.alibaba.com>, "Darrick J. Wong" <djwong@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260718013335.2621394-1-sashal@kernel.org>
+
+From: Jiapeng Chong <jiapeng.chong@linux.alibaba.com>
+
+[ Upstream commit f4901a182d33d05a3b7020e2af97c635f6c47959 ]
+
+mp is being initialized to log->l_mp but this is never read
+as record is overwritten later on. Remove the redundant
+assignment.
+
+Cleans up the following clang-analyzer warning:
+
+fs/xfs/xfs_log_recover.c:3543:20: warning: Value stored to 'mp' during
+its initialization is never read [clang-analyzer-deadcode.DeadStores].
+
+Reported-by: Abaci Robot <abaci@linux.alibaba.com>
+Signed-off-by: Jiapeng Chong <jiapeng.chong@linux.alibaba.com>
+Reviewed-by: Darrick J. Wong <djwong@kernel.org>
+Signed-off-by: Darrick J. Wong <djwong@kernel.org>
+Stable-dep-of: cc9af5e461ea ("xfs: use null daddr for unset first bad log block")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/xfs/xfs_log_recover.c |    2 --
+ 1 file changed, 2 deletions(-)
+
+--- a/fs/xfs/xfs_log_recover.c
++++ b/fs/xfs/xfs_log_recover.c
+@@ -3579,8 +3579,6 @@ xlog_recover_check_summary(
+       uint64_t                ifree;
+       int                     error;
+-      mp = log->l_mp;
+-
+       freeblks = 0LL;
+       itotal = 0LL;
+       ifree = 0LL;
diff --git a/queue-5.15/xfs-use-null-daddr-for-unset-first-bad-log-block.patch b/queue-5.15/xfs-use-null-daddr-for-unset-first-bad-log-block.patch
new file mode 100644 (file)
index 0000000..be2920c
--- /dev/null
@@ -0,0 +1,59 @@
+From stable+bounces-277237-greg=kroah.com@vger.kernel.org Sat Jul 18 03:33:49 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 17 Jul 2026 21:33:35 -0400
+Subject: xfs: use null daddr for unset first bad log block
+To: stable@vger.kernel.org
+Cc: Yousef Alhouseen <alhouseenyousef@gmail.com>, "Darrick J. Wong" <djwong@kernel.org>, syzbot+b7dfbed0c6c2b5e9fd34@syzkaller.appspotmail.com, Carlos Maiolino <cem@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260718013335.2621394-3-sashal@kernel.org>
+
+From: Yousef Alhouseen <alhouseenyousef@gmail.com>
+
+[ Upstream commit cc9af5e461ea5f6e37738f3f1e41c45a9b7f45d6 ]
+
+xlog_do_recovery_pass() may return before setting first_bad.  The caller
+must distinguish that case from an error at a valid log block, including
+block zero after the log wraps.
+
+Initialize first_bad to XFS_BUF_DADDR_NULL and test it explicitly before
+treating the error as a torn write.
+
+Fixes: 7088c4136fa1 ("xfs: detect and trim torn writes during log recovery")
+Suggested-by: Darrick J. Wong <djwong@kernel.org>
+Reported-by: syzbot+b7dfbed0c6c2b5e9fd34@syzkaller.appspotmail.com
+Closes: https://syzkaller.appspot.com/bug?extid=b7dfbed0c6c2b5e9fd34
+Cc: stable@vger.kernel.org # v4.5
+Signed-off-by: Yousef Alhouseen <alhouseenyousef@gmail.com>
+Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>
+Signed-off-by: Carlos Maiolino <cem@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/xfs/xfs_log_recover.c |    6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+--- a/fs/xfs/xfs_log_recover.c
++++ b/fs/xfs/xfs_log_recover.c
+@@ -1028,7 +1028,7 @@ xlog_verify_head(
+ {
+       struct xlog_rec_header  *tmp_rhead;
+       char                    *tmp_buffer;
+-      xfs_daddr_t             first_bad;
++      xfs_daddr_t             first_bad = XFS_BUF_DADDR_NULL;
+       xfs_daddr_t             tmp_rhead_blk;
+       int                     found;
+       int                     error;
+@@ -1057,7 +1057,8 @@ xlog_verify_head(
+        */
+       error = xlog_do_recovery_pass(log, *head_blk, tmp_rhead_blk,
+                                     XLOG_RECOVER_CRCPASS, &first_bad);
+-      if ((error == -EFSBADCRC || error == -EFSCORRUPTED) && first_bad) {
++      if ((error == -EFSBADCRC || error == -EFSCORRUPTED) &&
++          first_bad != XFS_BUF_DADDR_NULL) {
+               /*
+                * We've hit a potential torn write. Reset the error and warn
+                * about it.
+@@ -3549,4 +3550,3 @@ xlog_recover_cancel(
+       if (xlog_recovery_needed(log))
+               xlog_recover_cancel_intents(log);
+ }
+-